From 461f1aedd7d45f91c2a5afa403337e00b9f64c1c Mon Sep 17 00:00:00 2001 From: Ashita Prasad Date: Mon, 2 Oct 2023 06:31:44 +0530 Subject: [PATCH] feat: export to HAR --- lib/codegen/others/har.dart | 57 +-------- lib/providers/collection_providers.dart | 12 +- lib/screens/settings_page.dart | 26 ++-- lib/utils/har_utils.dart | 162 ++++++++++++++++++++++++ lib/utils/utils.dart | 1 + pubspec.lock | 16 +++ pubspec.yaml | 9 +- 7 files changed, 211 insertions(+), 72 deletions(-) create mode 100644 lib/utils/har_utils.dart diff --git a/lib/codegen/others/har.dart b/lib/codegen/others/har.dart index 7487c110..3f50485d 100644 --- a/lib/codegen/others/har.dart +++ b/lib/codegen/others/har.dart @@ -1,65 +1,12 @@ -import 'dart:convert'; import 'package:apidash/consts.dart'; -import 'package:apidash/utils/utils.dart' show rowsToMap; +import 'package:apidash/utils/utils.dart' show requestModelToHARJsonRequest; import 'package:apidash/models/models.dart' show RequestModel; -Map convertRequestModelToHARJson(RequestModel requestModel) { - Map json = {}; - bool hasBody = false; - - json["method"] = requestModel.method.name.toUpperCase(); - json["url"] = requestModel.url; - json["httpVersion"] = "HTTP/1.1"; - json["queryString"] = []; - json["headers"] = []; - - var paramsList = requestModel.requestParams; - if (paramsList != null) { - var params = rowsToMap(requestModel.requestParams) ?? {}; - if (params.isNotEmpty) { - for (final k in params.keys) { - json["queryString"].add({"name": k, "value": params[k]}); - } - } - } - - 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; - json["postData"] = {}; - json["postData"]["mimeType"] = - kContentTypeMap[requestModel.requestBodyContentType] ?? ""; - json["postData"]["text"] = requestBody; - } - } - - var headersList = requestModel.requestHeaders; - if (headersList != null || hasBody) { - var headers = rowsToMap(requestModel.requestHeaders) ?? {}; - if (headers.isNotEmpty || hasBody) { - if (hasBody) { - json["headers"].add({ - "name": "Content-Type", - "value": kContentTypeMap[requestModel.requestBodyContentType] ?? "" - }); - } - for (final k in headers.keys) { - json["headers"].add({"name": k, "value": headers[k]}); - } - } - } - - return json; -} - class HARCodeGen { String? getCode(RequestModel requestModel) { try { var harString = - kEncoder.convert(convertRequestModelToHARJson(requestModel)); + kEncoder.convert(requestModelToHARJsonRequest(requestModel)); return harString; } catch (e) { return null; diff --git a/lib/providers/collection_providers.dart b/lib/providers/collection_providers.dart index baf1242f..4c876d21 100644 --- a/lib/providers/collection_providers.dart +++ b/lib/providers/collection_providers.dart @@ -3,7 +3,7 @@ import 'package:apidash/providers/providers.dart'; import 'package:apidash/models/models.dart'; import 'package:apidash/services/services.dart' show hiveHandler, HiveHandler, request; -import 'package:apidash/utils/utils.dart' show uuid; +import 'package:apidash/utils/utils.dart' show uuid, collectionToHAR; import 'package:apidash/consts.dart'; final activeRequestModelProvider = StateProvider((ref) { @@ -204,9 +204,11 @@ class CollectionStateNotifier extends StateNotifier?> { ref.read(saveDataStateProvider.notifier).update((state) => false); } - Map exportData() { - return { - "data": state!.map((e) => e.toJson(includeResponse: false)).toList() - }; + Future> exportDataToHAR() async { + var result = await collectionToHAR(state); + return result; + // return { + // "data": state!.map((e) => e.toJson(includeResponse: false)).toList() + // }; } } diff --git a/lib/screens/settings_page.dart b/lib/screens/settings_page.dart index c71de31f..e91c3342 100644 --- a/lib/screens/settings_page.dart +++ b/lib/screens/settings_page.dart @@ -77,17 +77,27 @@ class _SettingsPageState extends ConsumerState { ListTile( contentPadding: EdgeInsets.zero, hoverColor: kColorTransparent, - title: const Text('Export Collection'), - subtitle: const Text('Export your collection to a JSON file'), + 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( onPressed: () async { - var data = ref - .read(collectionStateNotifierProvider.notifier) - .exportData(); - var pth = await getFileDownloadpath(null, "json"); - if (pth != null) { - await saveFile(pth, jsonMapToBytes(data)); + 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)); }, child: const Text("Export Data"), ), diff --git a/lib/utils/har_utils.dart b/lib/utils/har_utils.dart new file mode 100644 index 00000000..d30b8a66 --- /dev/null +++ b/lib/utils/har_utils.dart @@ -0,0 +1,162 @@ +import 'dart:convert'; +import 'package:apidash/consts.dart'; +import 'package:apidash/utils/utils.dart' show rowsToMap, getValidRequestUri; +import 'package:apidash/models/models.dart' show RequestModel; +import 'package:package_info_plus/package_info_plus.dart'; + +Future> collectionToHAR( + List? collection) async { + Map harJson = { + "log": { + "creator": { + "comment": "For support, check out API Dash repo - $kGitUrl", + "version": (await PackageInfo.fromPlatform()).version, + "name": "API Dash" + }, + "entries": >[], + "comment": "", + "browser": { + "version": (await PackageInfo.fromPlatform()).version, + "comment": "", + "name": "API Dash" + }, + "version": "1.2" + } + }; + + if (collection != null) { + for (final req in collection) { + harJson["log"]["entries"].add(entryToHAR(req)); + } + } + return harJson; +} + +Map entryToHAR(RequestModel requestModel) { + Map entryJson = { + "startedDateTime": DateTime.now().toUtc().toIso8601String(), + "comment": + "${requestModel.name.isNotEmpty ? '${requestModel.name} | ' : ''}id:${requestModel.id}", + "serverIPAddress": "", + "time": 0, + "timings": { + "connect": -1, + "comment": "", + "blocked": -1, + "dns": -1, + "receive": 0, + "send": 0, + "wait": 0, + "ssl": -1 + }, + "response": { + "status": 200, + "statusText": "OK", + "httpVersion": "HTTP/1.1", + "cookies": [], + "headers": [], + "content": {"size": 0, "mimeType": "", "comment": "", "text": ""}, + "redirectURL": "", + "headersSize": 0, + "bodySize": 0, + "comment": "" + }, + "request": requestModelToHARJsonRequest( + requestModel, + exportMode: true, + ), + "cache": {} + }; + return entryJson; +} + +Map requestModelToHARJsonRequest( + RequestModel requestModel, { + bool exportMode = false, +}) { + Map json = {}; + bool hasBody = false; + + var rec = getValidRequestUri( + requestModel.url, + requestModel.requestParams, + ); + + Uri? uri = rec.$1; + var u = ""; + if (uri != null) { + u = uri.toString(); + if (u[u.length - 1] == "?") { + u = u.substring(0, u.length - 1); + } + } + + json["method"] = requestModel.method.name.toUpperCase(); + json["url"] = u; + json["httpVersion"] = "HTTP/1.1"; + json["queryString"] = []; + json["headers"] = []; + + var paramsList = requestModel.requestParams; + if (paramsList != null) { + var params = rowsToMap(requestModel.requestParams) ?? {}; + if (params.isNotEmpty) { + for (final k in params.keys) { + var m = {"name": k, "value": params[k]}; + if (exportMode) { + m["comment"] = ""; + } + json["queryString"].add(m); + } + } + } + + 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; + json["postData"] = {}; + json["postData"]["mimeType"] = + kContentTypeMap[requestModel.requestBodyContentType] ?? ""; + json["postData"]["text"] = requestBody; + if (exportMode) { + json["postData"]["comment"] = ""; + } + } + } + + var headersList = requestModel.requestHeaders; + if (headersList != null || hasBody) { + var headers = rowsToMap(requestModel.requestHeaders) ?? {}; + if (headers.isNotEmpty || hasBody) { + if (hasBody) { + var m = { + "name": "Content-Type", + "value": kContentTypeMap[requestModel.requestBodyContentType] ?? "" + }; + if (exportMode) { + m["comment"] = ""; + } + json["headers"].add(m); + } + for (final k in headers.keys) { + var m = {"name": k, "value": headers[k]}; + if (exportMode) { + m["comment"] = ""; + } + json["headers"].add(m); + } + } + } + + if (exportMode) { + json["comment"] = ""; + json["cookies"] = []; + json["headersSize"] = -1; + json["bodySize"] = hasBody ? utf8.encode(requestBody!).length : 0; + } + + return json; +} diff --git a/lib/utils/utils.dart b/lib/utils/utils.dart index 04eae938..ef1edd5b 100644 --- a/lib/utils/utils.dart +++ b/lib/utils/utils.dart @@ -3,3 +3,4 @@ export 'convert_utils.dart'; export 'http_utils.dart'; export 'file_utils.dart'; export 'window_utils.dart'; +export 'har_utils.dart'; diff --git a/pubspec.lock b/pubspec.lock index c1fe9c13..c78b1211 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -616,6 +616,22 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.0" + package_info_plus: + dependency: "direct main" + description: + name: package_info_plus + sha256: "6ff267fcd9d48cb61c8df74a82680e8b82e940231bb5f68356672fde0397334a" + url: "https://pub.dev" + source: hosted + version: "4.1.0" + package_info_plus_platform_interface: + dependency: transitive + description: + name: package_info_plus_platform_interface + sha256: "9bc8ba46813a4cc42c66ab781470711781940780fd8beddd0c3da62506d3a6c6" + url: "https://pub.dev" + source: hosted + version: "2.0.1" path: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index b0ccb17d..b52424c4 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,11 +1,11 @@ name: apidash description: API Dash is a beautiful open-source cross-platform API Client built using Flutter which can help you easily create & customize your API requests, visually inspect responses and generate Dart code on the go. -publish_to: 'none' +publish_to: "none" version: 0.2.0+2 environment: - sdk: '>=3.0.0 <4.0.0' - flutter: '>=3.7.2' + sdk: ">=3.0.0 <4.0.0" + flutter: ">=3.7.2" dependencies: flutter: @@ -40,6 +40,7 @@ dependencies: freezed_annotation: ^2.4.1 json_annotation: ^4.8.1 printing: ^5.11.0 + package_info_plus: ^4.1.0 dev_dependencies: flutter_test: @@ -50,7 +51,7 @@ dev_dependencies: build_runner: ^2.4.6 freezed: ^2.4.1 json_serializable: ^6.7.1 - + flutter: uses-material-design: true assets: