feat: export to HAR

This commit is contained in:
Ashita Prasad
2023-10-02 06:31:44 +05:30
parent d1e27598bc
commit 461f1aedd7
7 changed files with 211 additions and 72 deletions

View File

@ -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<String, dynamic> convertRequestModelToHARJson(RequestModel requestModel) {
Map<String, dynamic> 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;

View File

@ -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<RequestModel?>((ref) {
@ -204,9 +204,11 @@ class CollectionStateNotifier extends StateNotifier<List<RequestModel>?> {
ref.read(saveDataStateProvider.notifier).update((state) => false);
}
Map<String, dynamic> exportData() {
return {
"data": state!.map((e) => e.toJson(includeResponse: false)).toList()
};
Future<Map<String, dynamic>> exportDataToHAR() async {
var result = await collectionToHAR(state);
return result;
// return {
// "data": state!.map((e) => e.toJson(includeResponse: false)).toList()
// };
}
}

View File

@ -77,17 +77,27 @@ class _SettingsPageState extends ConsumerState<SettingsPage> {
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
var message = "";
try {
var data = await ref
.read(collectionStateNotifierProvider.notifier)
.exportData();
var pth = await getFileDownloadpath(null, "json");
.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"),
),

162
lib/utils/har_utils.dart Normal file
View File

@ -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<Map<String, dynamic>> collectionToHAR(
List<RequestModel>? collection) async {
Map<String, dynamic> harJson = {
"log": {
"creator": {
"comment": "For support, check out API Dash repo - $kGitUrl",
"version": (await PackageInfo.fromPlatform()).version,
"name": "API Dash"
},
"entries": <Map<String, dynamic>>[],
"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<String, dynamic> entryToHAR(RequestModel requestModel) {
Map<String, dynamic> 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<String, dynamic> requestModelToHARJsonRequest(
RequestModel requestModel, {
bool exportMode = false,
}) {
Map<String, dynamic> 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;
}

View File

@ -3,3 +3,4 @@ export 'convert_utils.dart';
export 'http_utils.dart';
export 'file_utils.dart';
export 'window_utils.dart';
export 'har_utils.dart';

View File

@ -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:

View File

@ -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: