mirror of
https://github.com/foss42/apidash.git
synced 2025-06-30 21:06:43 +08:00
Merge pull request #101 from vidya-hub/form_data_imp
Form-Data and multi-part request feature added
This commit is contained in:
@ -1,8 +1,8 @@
|
||||
import 'package:apidash/consts.dart';
|
||||
import 'package:apidash/models/request_model.dart' show RequestModel;
|
||||
import 'dart:convert';
|
||||
import 'package:code_builder/code_builder.dart';
|
||||
import 'package:dart_style/dart_style.dart';
|
||||
|
||||
import 'package:apidash/models/request_model.dart' show RequestModel;
|
||||
import 'package:apidash/consts.dart';
|
||||
import 'shared.dart';
|
||||
|
||||
class DartDioCodeGen {
|
||||
@ -22,6 +22,7 @@ class DartDioCodeGen {
|
||||
headers: requestModel.enabledHeadersMap,
|
||||
body: requestModel.requestBody,
|
||||
contentType: requestModel.requestBodyContentType,
|
||||
formData: requestModel.formDataMapList,
|
||||
);
|
||||
return next;
|
||||
} catch (e) {
|
||||
@ -36,6 +37,7 @@ class DartDioCodeGen {
|
||||
required Map<String, String> headers,
|
||||
required String? body,
|
||||
required ContentType contentType,
|
||||
required List<Map<String, dynamic>> formData,
|
||||
}) {
|
||||
final sbf = StringBuffer();
|
||||
final emitter = DartEmitter();
|
||||
@ -54,9 +56,22 @@ class DartDioCodeGen {
|
||||
literalMap(headers.map((key, value) => MapEntry(key, value))),
|
||||
);
|
||||
}
|
||||
|
||||
final multiPartList = Code('''
|
||||
final List<Map<String,String>> formDataList = ${json.encode(formData)};
|
||||
for (var formField in formDataList) {
|
||||
if (formField['type'] == 'file') {
|
||||
formData.files.add(MapEntry(
|
||||
formField['name'],
|
||||
await MultipartFile.fromFile(formField['value'], filename: formField['value']),
|
||||
));
|
||||
} else {
|
||||
formData.fields.add(MapEntry(formField['name'], formField['value']));
|
||||
}
|
||||
}
|
||||
''');
|
||||
Expression? dataExp;
|
||||
if (kMethodsWithBody.contains(method) && (body?.isNotEmpty ?? false)) {
|
||||
if ((kMethodsWithBody.contains(method) && (body?.isNotEmpty ?? false) ||
|
||||
contentType == ContentType.formdata)) {
|
||||
final strContent = CodeExpression(Code('r\'\'\'$body\'\'\''));
|
||||
switch (contentType) {
|
||||
// dio dosen't need pass `content-type` header when body is json or plain text
|
||||
@ -68,6 +83,8 @@ class DartDioCodeGen {
|
||||
case ContentType.text:
|
||||
dataExp = declareFinal('data').assign(strContent);
|
||||
// when add new type of [ContentType], need update [dataExp].
|
||||
case ContentType.formdata:
|
||||
dataExp = declareFinal('data').assign(refer('FormData()'));
|
||||
}
|
||||
}
|
||||
final responseExp = declareFinal('response').assign(InvokeExpression.newOf(
|
||||
@ -93,6 +110,8 @@ class DartDioCodeGen {
|
||||
if (queryParamExp != null) queryParamExp,
|
||||
if (headerExp != null) headerExp,
|
||||
if (dataExp != null) dataExp,
|
||||
if ((contentType == ContentType.formdata && formData.isNotEmpty))
|
||||
multiPartList,
|
||||
responseExp,
|
||||
refer('print').call([refer('response').property('statusCode')]),
|
||||
refer('print').call([refer('response').property('data')]),
|
||||
@ -122,6 +141,8 @@ class DartDioCodeGen {
|
||||
|
||||
sbf.writeln(mainFunction.accept(emitter));
|
||||
|
||||
return DartFormatter(pageWidth: 160).format(sbf.toString());
|
||||
return DartFormatter(
|
||||
pageWidth: contentType == ContentType.formdata ? 70 : 160)
|
||||
.format(sbf.toString());
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,9 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:apidash/consts.dart';
|
||||
import 'package:apidash/models/models.dart' show RequestModel;
|
||||
import 'package:code_builder/code_builder.dart';
|
||||
import 'package:dart_style/dart_style.dart';
|
||||
|
||||
import 'package:apidash/models/models.dart' show RequestModel;
|
||||
import 'package:apidash/consts.dart';
|
||||
import 'shared.dart';
|
||||
|
||||
class DartHttpCodeGen {
|
||||
@ -22,9 +21,10 @@ class DartHttpCodeGen {
|
||||
method: requestModel.method,
|
||||
queryParams: requestModel.enabledParamsMap,
|
||||
headers: {...requestModel.enabledHeadersMap},
|
||||
body: requestModel.requestBody,
|
||||
contentType: requestModel.requestBodyContentType,
|
||||
hasContentTypeHeader: requestModel.hasContentTypeHeader,
|
||||
body: requestModel.requestBody,
|
||||
formData: requestModel.formDataMapList,
|
||||
);
|
||||
return next;
|
||||
} catch (e) {
|
||||
@ -37,9 +37,10 @@ class DartHttpCodeGen {
|
||||
required HTTPVerb method,
|
||||
required Map<String, String> queryParams,
|
||||
required Map<String, String> headers,
|
||||
required String? body,
|
||||
required ContentType contentType,
|
||||
required String? body,
|
||||
required bool hasContentTypeHeader,
|
||||
required List<Map<String, dynamic>> formData,
|
||||
}) {
|
||||
final uri = Uri.parse(url);
|
||||
|
||||
@ -55,7 +56,6 @@ class DartHttpCodeGen {
|
||||
if (kMethodsWithBody.contains(method) && (body?.isNotEmpty ?? false)) {
|
||||
final strContent = CodeExpression(Code('r\'\'\'$body\'\'\''));
|
||||
dataExp = declareVar('body', type: refer('String')).assign(strContent);
|
||||
|
||||
if (!hasContentTypeHeader) {
|
||||
headers.putIfAbsent(HttpHeaders.contentTypeHeader,
|
||||
() => kContentTypeMap[contentType] ?? '');
|
||||
@ -102,7 +102,9 @@ class DartHttpCodeGen {
|
||||
);
|
||||
}
|
||||
final responseExp = declareFinal('response').assign(InvokeExpression.newOf(
|
||||
refer('http.${method.name}'),
|
||||
refer(
|
||||
'http.${method.name}',
|
||||
),
|
||||
[refer('uri')],
|
||||
{
|
||||
if (headerExp != null) 'headers': refer('headers'),
|
||||
@ -110,7 +112,36 @@ class DartHttpCodeGen {
|
||||
},
|
||||
[],
|
||||
).awaited);
|
||||
final multiPartRequest =
|
||||
declareFinal('request').assign(InvokeExpression.newOf(
|
||||
refer(
|
||||
'http.MultipartRequest',
|
||||
),
|
||||
[refer(jsonEncode(method.name.toUpperCase())), refer('uri')],
|
||||
));
|
||||
final multiPartFiles = declareFinal('formDataList').assign(refer(
|
||||
jsonEncode(formData),
|
||||
));
|
||||
|
||||
final addHeaders = refer('request.headers.addAll').call([refer('headers')]);
|
||||
const multiPartList = Code('''
|
||||
for (Map<String, String> formData in formDataList){
|
||||
if (formData['type'] == 'text') {
|
||||
request.fields.addAll({formData['name']: formData['value']});
|
||||
} else {
|
||||
request.files.add(
|
||||
await http.MultipartFile.fromPath(
|
||||
formData['name'],
|
||||
formData['value'],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
''');
|
||||
var multiPartRequestSend =
|
||||
declareFinal('response').assign(refer('request.send()').awaited);
|
||||
var multiPartResponseBody = declareFinal('responseBody')
|
||||
.assign(refer('response.stream.bytesToString()').awaited);
|
||||
final mainFunction = Method((m) {
|
||||
final statusRef = refer('statusCode');
|
||||
m
|
||||
@ -135,25 +166,49 @@ class DartHttpCodeGen {
|
||||
b.statements.add(headerExp.statement);
|
||||
}
|
||||
b.statements.add(const Code('\n'));
|
||||
if (contentType == ContentType.formdata) {
|
||||
if (formData.isNotEmpty) {
|
||||
b.statements.add(multiPartFiles.statement);
|
||||
}
|
||||
b.statements.add(multiPartRequest.statement);
|
||||
if (formData.isNotEmpty) {
|
||||
b.statements.add(multiPartList);
|
||||
}
|
||||
if (headerExp != null) {
|
||||
b.statements.add(addHeaders.statement);
|
||||
}
|
||||
b.statements.add(multiPartRequestSend.statement);
|
||||
b.statements.add(multiPartResponseBody.statement);
|
||||
b.statements.add(declareVar('statusCode', type: refer('int'))
|
||||
.assign(refer('response').property('statusCode'))
|
||||
.statement);
|
||||
b.statements.add(const Code('\n'));
|
||||
} else {
|
||||
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}')]),
|
||||
refer('print').call([
|
||||
literalString(
|
||||
'Response Body: ${contentType == ContentType.formdata ? ':\$responseBody' : '\${response.body}'}')
|
||||
]),
|
||||
],
|
||||
elseBody: [
|
||||
refer('print')
|
||||
.call([literalString(r'Error Status Code: $statusCode')]),
|
||||
refer('print').call(
|
||||
[literalString(r'Error Response Body: ${response.body}')]),
|
||||
refer('print').call([
|
||||
literalString(
|
||||
'Error Response Body: ${contentType == ContentType.formdata ? ':\$responseBody' : '\${response.body}'}')
|
||||
]),
|
||||
],
|
||||
));
|
||||
});
|
||||
@ -161,6 +216,8 @@ class DartHttpCodeGen {
|
||||
|
||||
sbf.writeln(mainFunction.accept(emitter));
|
||||
|
||||
return DartFormatter(pageWidth: 160).format(sbf.toString());
|
||||
return DartFormatter(
|
||||
pageWidth: contentType == ContentType.formdata ? 70 : 160)
|
||||
.format(sbf.toString());
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +1,18 @@
|
||||
import 'package:apidash/consts.dart';
|
||||
import 'dart:convert';
|
||||
import 'package:jinja/jinja.dart' as jj;
|
||||
import 'package:apidash/utils/utils.dart'
|
||||
show requestModelToHARJsonRequest, padMultilineString, stripUrlParams;
|
||||
show padMultilineString, requestModelToHARJsonRequest, stripUrlParams;
|
||||
import 'package:apidash/models/models.dart' show RequestModel;
|
||||
import 'package:apidash/consts.dart';
|
||||
|
||||
class AxiosCodeGen {
|
||||
AxiosCodeGen({this.isNodeJs = false});
|
||||
|
||||
final bool isNodeJs;
|
||||
|
||||
String kStringImportNode = """import axios from 'axios';
|
||||
String kStringImportNode = """{% if isNodeJs %}import axios from 'axios';
|
||||
|
||||
{% endif %}{% if isFormDataRequest and isNodeJs %}const fs = require('fs');{% endif %}
|
||||
""";
|
||||
|
||||
String kTemplateStart = """let config = {
|
||||
@ -46,13 +48,49 @@ axios(config)
|
||||
console.log(error);
|
||||
});
|
||||
""";
|
||||
String kMultiPartBodyTemplate = r'''
|
||||
|
||||
|
||||
async function buildFormData(fields) {
|
||||
var formdata = new FormData();
|
||||
for (const field of fields) {
|
||||
const name = field.name || '';
|
||||
const value = field.value || '';
|
||||
const type = field.type || 'text';
|
||||
|
||||
if (type === 'text') {
|
||||
formdata.append(name, value);
|
||||
} else if (type === 'file') {
|
||||
formdata.append(name,{% if isNodeJs %} fs.createReadStream(value){% else %} fileInput.files[0],value{% endif %});
|
||||
}
|
||||
}
|
||||
return formdata;
|
||||
}
|
||||
|
||||
|
||||
''';
|
||||
var kGetFormDataTemplate = '''buildFormData({{fields_list}});
|
||||
''';
|
||||
String? getCode(
|
||||
RequestModel requestModel,
|
||||
String defaultUriScheme,
|
||||
) {
|
||||
try {
|
||||
String result = isNodeJs ? kStringImportNode : "";
|
||||
jj.Template kNodejsImportTemplate = jj.Template(kStringImportNode);
|
||||
String importsData = kNodejsImportTemplate.render({
|
||||
"isFormDataRequest": requestModel.isFormDataRequest,
|
||||
"isNodeJs": isNodeJs,
|
||||
});
|
||||
|
||||
String result = importsData;
|
||||
if (requestModel.isFormDataRequest &&
|
||||
requestModel.formDataMapList.isNotEmpty) {
|
||||
var templateMultiPartBody = jj.Template(kMultiPartBodyTemplate);
|
||||
var renderedMultiPartBody = templateMultiPartBody.render({
|
||||
"isNodeJs": isNodeJs,
|
||||
});
|
||||
result += renderedMultiPartBody;
|
||||
}
|
||||
|
||||
String url = requestModel.url;
|
||||
if (!url.contains("://") && url.isNotEmpty) {
|
||||
@ -80,18 +118,31 @@ axios(config)
|
||||
}
|
||||
|
||||
var headers = harJson["headers"];
|
||||
if (headers.isNotEmpty) {
|
||||
if (headers.isNotEmpty || requestModel.isFormDataRequest) {
|
||||
var templateHeader = jj.Template(kTemplateHeader);
|
||||
var m = {};
|
||||
for (var i in headers) {
|
||||
m[i["name"]] = i["value"];
|
||||
}
|
||||
if (requestModel.isFormDataRequest) {
|
||||
m['Content-Type'] = 'multipart/form-data';
|
||||
}
|
||||
result += templateHeader
|
||||
.render({"headers": padMultilineString(kEncoder.convert(m), 2)});
|
||||
}
|
||||
|
||||
if (harJson["postData"]?["text"] != null) {
|
||||
var templateBody = jj.Template(kTemplateBody);
|
||||
|
||||
if (requestModel.isFormDataRequest &&
|
||||
requestModel.formDataMapList.isNotEmpty) {
|
||||
var getFieldDataTemplate = jj.Template(kGetFormDataTemplate);
|
||||
|
||||
result += templateBody.render({
|
||||
"body": getFieldDataTemplate.render({
|
||||
"fields_list": json.encode(requestModel.formDataMapList),
|
||||
})
|
||||
});
|
||||
}
|
||||
if (harJson["postData"]?["text"] != null) {
|
||||
result += templateBody
|
||||
.render({"body": kEncoder.convert(harJson["postData"]["text"])});
|
||||
}
|
||||
|
@ -1,15 +1,18 @@
|
||||
import 'package:apidash/consts.dart';
|
||||
import 'dart:convert';
|
||||
import 'package:jinja/jinja.dart' as jj;
|
||||
import 'package:apidash/utils/utils.dart'
|
||||
show requestModelToHARJsonRequest, padMultilineString;
|
||||
show padMultilineString, requestModelToHARJsonRequest;
|
||||
import 'package:apidash/models/models.dart' show RequestModel;
|
||||
import 'package:apidash/consts.dart';
|
||||
|
||||
class FetchCodeGen {
|
||||
FetchCodeGen({this.isNodeJs = false});
|
||||
|
||||
final bool isNodeJs;
|
||||
|
||||
String kStringImportNode = """import fetch from 'node-fetch';
|
||||
String kStringImportNode = """
|
||||
import fetch from 'node-fetch';
|
||||
{% if isFormDataRequest %}const fs = require('fs');{% endif %}
|
||||
|
||||
""";
|
||||
|
||||
@ -28,6 +31,26 @@ let options = {
|
||||
{{body}}
|
||||
""";
|
||||
|
||||
String kMultiPartBodyTemplate = r'''
|
||||
async function buildDataList(fields) {
|
||||
var formdata = new FormData();
|
||||
for (const field of fields) {
|
||||
const name = field.name || '';
|
||||
const value = field.value || '';
|
||||
const type = field.type || 'text';
|
||||
|
||||
if (type === 'text') {
|
||||
formdata.append(name, value);
|
||||
} else if (type === 'file') {
|
||||
formdata.append(name,{% if isNodeJs %} fs.createReadStream(value){% else %} fileInput.files[0],value{% endif %});
|
||||
}
|
||||
}
|
||||
return formdata;
|
||||
}
|
||||
|
||||
const payload = buildDataList({{fields_list}});
|
||||
|
||||
''';
|
||||
String kStringRequest = """
|
||||
|
||||
};
|
||||
@ -53,8 +76,19 @@ fetch(url, options)
|
||||
String defaultUriScheme,
|
||||
) {
|
||||
try {
|
||||
String result = isNodeJs ? kStringImportNode : "";
|
||||
jj.Template kNodejsImportTemplate = jj.Template(kStringImportNode);
|
||||
String importsData = kNodejsImportTemplate.render({
|
||||
"isFormDataRequest": requestModel.isFormDataRequest,
|
||||
});
|
||||
|
||||
String result = isNodeJs ? importsData : "";
|
||||
if (requestModel.isFormDataRequest) {
|
||||
var templateMultiPartBody = jj.Template(kMultiPartBodyTemplate);
|
||||
result += templateMultiPartBody.render({
|
||||
"isNodeJs": isNodeJs,
|
||||
"fields_list": json.encode(requestModel.formDataMapList),
|
||||
});
|
||||
}
|
||||
String url = requestModel.url;
|
||||
if (!url.contains("://") && url.isNotEmpty) {
|
||||
url = "$defaultUriScheme://$url";
|
||||
@ -70,21 +104,33 @@ fetch(url, options)
|
||||
});
|
||||
|
||||
var headers = harJson["headers"];
|
||||
|
||||
if (headers.isNotEmpty) {
|
||||
var templateHeader = jj.Template(kTemplateHeader);
|
||||
var m = {};
|
||||
if (requestModel.isFormDataRequest) {
|
||||
m["Content-Type"] = "multipart/form-data";
|
||||
}
|
||||
for (var i in headers) {
|
||||
m[i["name"]] = i["value"];
|
||||
}
|
||||
result += templateHeader
|
||||
.render({"headers": padMultilineString(kEncoder.convert(m), 2)});
|
||||
result += templateHeader.render({
|
||||
"headers": padMultilineString(kEncoder.convert(m), 2),
|
||||
});
|
||||
}
|
||||
|
||||
if (harJson["postData"]?["text"] != null) {
|
||||
var templateBody = jj.Template(kTemplateBody);
|
||||
result += templateBody
|
||||
.render({"body": kEncoder.convert(harJson["postData"]["text"])});
|
||||
result += templateBody.render({
|
||||
"body": kEncoder.convert(harJson["postData"]["text"]),
|
||||
});
|
||||
} else if (requestModel.isFormDataRequest) {
|
||||
var templateBody = jj.Template(kTemplateBody);
|
||||
result += templateBody.render({
|
||||
"body": 'payload',
|
||||
});
|
||||
}
|
||||
|
||||
result += kStringRequest;
|
||||
return result;
|
||||
} catch (e) {
|
||||
|
@ -1,9 +1,9 @@
|
||||
import 'dart:convert';
|
||||
import 'package:apidash/consts.dart';
|
||||
import 'package:jinja/jinja.dart' as jj;
|
||||
import 'package:apidash/utils/utils.dart'
|
||||
show getValidRequestUri, stripUriParams;
|
||||
import '../../models/request_model.dart';
|
||||
import 'package:apidash/consts.dart';
|
||||
|
||||
class KotlinOkHttpCodeGen {
|
||||
final String kTemplateStart = """import okhttp3.OkHttpClient
|
||||
@ -61,6 +61,13 @@ import okhttp3.MediaType.Companion.toMediaType""";
|
||||
}
|
||||
|
||||
""";
|
||||
// Converting list of form data objects to kolin multi part data
|
||||
String kFormDataBody = '''
|
||||
val body = MultipartBody.Builder().setType(MultipartBody.FORM){% for item in formDataList %}{% if item.type == 'file' %}
|
||||
.addFormDataPart("{{item.name}}",null,File("{{item.value}}").asRequestBody("application/octet-stream".toMediaType()))
|
||||
{% else %}.addFormDataPart("{{item.name}}","{{item.value}}")
|
||||
{% endif %}{% endfor %}.build()
|
||||
''';
|
||||
|
||||
String? getCode(
|
||||
RequestModel requestModel,
|
||||
@ -68,7 +75,6 @@ import okhttp3.MediaType.Companion.toMediaType""";
|
||||
) {
|
||||
try {
|
||||
String result = "";
|
||||
bool hasHeaders = false;
|
||||
bool hasQuery = false;
|
||||
bool hasBody = false;
|
||||
|
||||
@ -102,7 +108,13 @@ import okhttp3.MediaType.Companion.toMediaType""";
|
||||
|
||||
var method = requestModel.method;
|
||||
var requestBody = requestModel.requestBody;
|
||||
if (kMethodsWithBody.contains(method) && requestBody != null) {
|
||||
if (requestModel.isFormDataRequest) {
|
||||
var formDataTemplate = jj.Template(kFormDataBody);
|
||||
|
||||
result += formDataTemplate.render({
|
||||
"formDataList": requestModel.formDataMapList,
|
||||
});
|
||||
} else if (kMethodsWithBody.contains(method) && requestBody != null) {
|
||||
var contentLength = utf8.encode(requestBody).length;
|
||||
if (contentLength > 0) {
|
||||
hasBody = true;
|
||||
@ -127,7 +139,6 @@ import okhttp3.MediaType.Companion.toMediaType""";
|
||||
if (headersList != null) {
|
||||
var headers = requestModel.enabledHeadersMap;
|
||||
if (headers.isNotEmpty) {
|
||||
hasHeaders = true;
|
||||
result += getHeaders(headers);
|
||||
}
|
||||
}
|
||||
@ -135,7 +146,7 @@ import okhttp3.MediaType.Companion.toMediaType""";
|
||||
var templateRequestEnd = jj.Template(kTemplateRequestEnd);
|
||||
result += templateRequestEnd.render({
|
||||
"method": method.name.toLowerCase(),
|
||||
"hasBody": hasBody ? "body" : "",
|
||||
"hasBody": (hasBody || requestModel.isFormDataRequest) ? "body" : "",
|
||||
});
|
||||
}
|
||||
return result;
|
||||
|
@ -10,6 +10,9 @@ class cURLCodeGen {
|
||||
String kTemplateHeader = """ \\
|
||||
--header '{{name}}: {{value}}'
|
||||
""";
|
||||
String kTemplateFormData = """ \\
|
||||
--form '{{name}}: {{value}}'
|
||||
""";
|
||||
|
||||
String kTemplateBody = """ \\
|
||||
--data '{{body}}'
|
||||
@ -48,6 +51,23 @@ class cURLCodeGen {
|
||||
.render({"name": item["name"], "value": item["value"]});
|
||||
}
|
||||
}
|
||||
if (harJson['formData'] != null) {
|
||||
var formDataList = harJson['formData'] as List<Map<String, dynamic>>;
|
||||
for (var formData in formDataList) {
|
||||
var templateFormData = jj.Template(kTemplateFormData);
|
||||
if (formData['type'] != null &&
|
||||
formData['name'] != null &&
|
||||
formData['value'] != null &&
|
||||
formData['name']!.isNotEmpty &&
|
||||
formData['value']!.isNotEmpty) {
|
||||
result += templateFormData.render({
|
||||
"name": formData["name"],
|
||||
"value":
|
||||
"${formData['type'] == 'file' ? '@' : ''}${formData["value"]}",
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (harJson["postData"]?["text"] != null) {
|
||||
var templateBody = jj.Template(kTemplateBody);
|
||||
|
@ -1,13 +1,16 @@
|
||||
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 getValidRequestUri, padMultilineString;
|
||||
show getNewUuid, getValidRequestUri, padMultilineString;
|
||||
import 'package:apidash/models/models.dart' show RequestModel;
|
||||
import 'package:apidash/consts.dart';
|
||||
|
||||
class PythonHttpClientCodeGen {
|
||||
final String kTemplateStart = """import http.client
|
||||
{% if isFormDataRequest %}import mimetypes
|
||||
from codecs import encode
|
||||
{% endif %}
|
||||
""";
|
||||
|
||||
String kTemplateParams = """
|
||||
@ -30,6 +33,8 @@ body = r'''{{body}}'''
|
||||
headers = {{headers}}
|
||||
|
||||
""";
|
||||
String kTemplateFormHeaderContentType = '''
|
||||
multipart/form-data; boundary={{boundary}}''';
|
||||
|
||||
int kHeadersPadding = 10;
|
||||
|
||||
@ -55,10 +60,38 @@ data = res.read()
|
||||
print(data.decode("utf-8"))
|
||||
""";
|
||||
|
||||
final String kStringFormDataBody = r'''
|
||||
|
||||
def build_data_list(fields):
|
||||
dataList = []
|
||||
for field in fields:
|
||||
name = field.get('name', '')
|
||||
value = field.get('value', '')
|
||||
type_ = field.get('type', 'text')
|
||||
dataList.append(encode('--{{boundary}}'))
|
||||
if type_ == 'text':
|
||||
dataList.append(encode(f'Content-Disposition: form-data; name="{name}"'))
|
||||
dataList.append(encode('Content-Type: text/plain'))
|
||||
dataList.append(encode(''))
|
||||
dataList.append(encode(value))
|
||||
elif type_ == 'file':
|
||||
dataList.append(encode(f'Content-Disposition: form-data; name="{name}"; filename="{value}"'))
|
||||
dataList.append(encode(f'Content-Type: {mimetypes.guess_type(value)[0] or "application/octet-stream"}'))
|
||||
dataList.append(encode(''))
|
||||
dataList.append(open(value, 'rb').read())
|
||||
dataList.append(encode(f'--{{boundary}}--'))
|
||||
dataList.append(encode(''))
|
||||
return dataList
|
||||
|
||||
dataList = build_data_list({{fields_list}})
|
||||
body = b'\r\n'.join(dataList)
|
||||
''';
|
||||
String? getCode(
|
||||
RequestModel requestModel,
|
||||
String defaultUriScheme,
|
||||
) {
|
||||
String uuid = getNewUuid();
|
||||
|
||||
try {
|
||||
String result = "";
|
||||
bool hasHeaders = false;
|
||||
@ -70,11 +103,17 @@ print(data.decode("utf-8"))
|
||||
url = "$defaultUriScheme://$url";
|
||||
}
|
||||
|
||||
result += kTemplateStart;
|
||||
var templateStartUrl = jj.Template(kTemplateStart);
|
||||
result += templateStartUrl.render(
|
||||
{
|
||||
"isFormDataRequest": requestModel.isFormDataRequest,
|
||||
},
|
||||
);
|
||||
var rec = getValidRequestUri(
|
||||
url,
|
||||
requestModel.enabledRequestParams,
|
||||
);
|
||||
|
||||
Uri? uri = rec.$1;
|
||||
|
||||
if (uri != null) {
|
||||
@ -103,6 +142,14 @@ print(data.decode("utf-8"))
|
||||
var headersList = requestModel.enabledRequestHeaders;
|
||||
if (headersList != null || hasBody) {
|
||||
var headers = requestModel.enabledHeadersMap;
|
||||
if (requestModel.isFormDataRequest) {
|
||||
var formHeaderTemplate =
|
||||
jj.Template(kTemplateFormHeaderContentType);
|
||||
headers[HttpHeaders.contentTypeHeader] = formHeaderTemplate.render({
|
||||
"boundary": uuid,
|
||||
});
|
||||
}
|
||||
|
||||
if (headers.isNotEmpty || hasBody) {
|
||||
hasHeaders = true;
|
||||
if (hasBody && !requestModel.hasContentTypeHeader) {
|
||||
@ -115,7 +162,15 @@ print(data.decode("utf-8"))
|
||||
result += templateHeaders.render({"headers": headersString});
|
||||
}
|
||||
}
|
||||
|
||||
if (requestModel.isFormDataRequest) {
|
||||
var formDataBodyData = jj.Template(kStringFormDataBody);
|
||||
result += formDataBodyData.render(
|
||||
{
|
||||
"fields_list": json.encode(requestModel.formDataMapList),
|
||||
"boundary": uuid,
|
||||
},
|
||||
);
|
||||
}
|
||||
var templateConnection = jj.Template(kTemplateConnection);
|
||||
result += templateConnection.render({
|
||||
"isHttps": uri.scheme == "https" ? "S" : "",
|
||||
@ -129,11 +184,11 @@ print(data.decode("utf-8"))
|
||||
"queryParamsStr": hasQuery ? " + queryParamsStr" : "",
|
||||
});
|
||||
|
||||
if (hasBody) {
|
||||
if (hasBody || requestModel.isFormDataRequest) {
|
||||
result += kStringRequestBody;
|
||||
}
|
||||
|
||||
if (hasHeaders) {
|
||||
if (hasHeaders || requestModel.isFormDataRequest) {
|
||||
result += kStringRequestHeaders;
|
||||
}
|
||||
|
||||
|
@ -3,12 +3,14 @@ import 'dart:convert';
|
||||
import 'package:jinja/jinja.dart' as jj;
|
||||
import 'package:apidash/consts.dart';
|
||||
import 'package:apidash/utils/utils.dart'
|
||||
show getValidRequestUri, padMultilineString, stripUriParams;
|
||||
show getNewUuid, getValidRequestUri, padMultilineString, stripUriParams;
|
||||
import 'package:apidash/models/models.dart' show RequestModel;
|
||||
|
||||
class PythonRequestsCodeGen {
|
||||
final String kTemplateStart = """import requests
|
||||
|
||||
{% if isFormDataRequest %}import mimetypes
|
||||
from codecs import encode
|
||||
{% endif %}
|
||||
url = '{{url}}'
|
||||
|
||||
""";
|
||||
@ -37,6 +39,8 @@ payload = {{body}}
|
||||
headers = {{headers}}
|
||||
|
||||
""";
|
||||
String kTemplateFormHeaderContentType = '''
|
||||
multipart/form-data; boundary={{boundary}}''';
|
||||
|
||||
int kHeadersPadding = 10;
|
||||
|
||||
@ -45,6 +49,34 @@ headers = {{headers}}
|
||||
response = requests.{{method}}(url
|
||||
""";
|
||||
|
||||
final String kStringFormDataBody = r'''
|
||||
|
||||
def build_data_list(fields):
|
||||
dataList = []
|
||||
for field in fields:
|
||||
name = field.get('name', '')
|
||||
value = field.get('value', '')
|
||||
type_ = field.get('type', 'text')
|
||||
|
||||
dataList.append(encode('--{{boundary}}'))
|
||||
if type_ == 'text':
|
||||
dataList.append(encode(f'Content-Disposition: form-data; name="{name}"'))
|
||||
dataList.append(encode('Content-Type: text/plain'))
|
||||
dataList.append(encode(''))
|
||||
dataList.append(encode(value))
|
||||
elif type_ == 'file':
|
||||
dataList.append(encode(f'Content-Disposition: form-data; name="{name}"; filename="{value}"'))
|
||||
dataList.append(encode(f'Content-Type: {mimetypes.guess_type(value)[0] or "application/octet-stream"}'))
|
||||
dataList.append(encode(''))
|
||||
dataList.append(open(value, 'rb').read())
|
||||
dataList.append(encode('--{{boundary}}--'))
|
||||
dataList.append(encode(''))
|
||||
return dataList
|
||||
|
||||
dataList = build_data_list({{fields_list}})
|
||||
payload = b'\r\n'.join(dataList)
|
||||
''';
|
||||
|
||||
String kStringRequestParams = """, params=params""";
|
||||
|
||||
String kStringRequestBody = """, data=payload""";
|
||||
@ -69,6 +101,7 @@ print('Response Body:', response.text)
|
||||
bool hasHeaders = false;
|
||||
bool hasBody = false;
|
||||
bool hasJsonBody = false;
|
||||
String uuid = getNewUuid();
|
||||
|
||||
String url = requestModel.url;
|
||||
if (!url.contains("://") && url.isNotEmpty) {
|
||||
@ -82,7 +115,10 @@ print('Response Body:', response.text)
|
||||
Uri? uri = rec.$1;
|
||||
if (uri != null) {
|
||||
var templateStartUrl = jj.Template(kTemplateStart);
|
||||
result += templateStartUrl.render({"url": stripUriParams(uri)});
|
||||
result += templateStartUrl.render({
|
||||
"url": stripUriParams(uri),
|
||||
'isFormDataRequest': requestModel.isFormDataRequest
|
||||
});
|
||||
|
||||
if (uri.hasQuery) {
|
||||
var params = uri.queryParameters;
|
||||
@ -115,6 +151,13 @@ print('Response Body:', response.text)
|
||||
var headersList = requestModel.enabledRequestHeaders;
|
||||
if (headersList != null || hasBody) {
|
||||
var headers = requestModel.enabledHeadersMap;
|
||||
if (requestModel.isFormDataRequest) {
|
||||
var formHeaderTemplate =
|
||||
jj.Template(kTemplateFormHeaderContentType);
|
||||
headers[HttpHeaders.contentTypeHeader] = formHeaderTemplate.render({
|
||||
"boundary": uuid,
|
||||
});
|
||||
}
|
||||
if (headers.isNotEmpty || hasBody) {
|
||||
hasHeaders = true;
|
||||
if (hasBody) {
|
||||
@ -127,7 +170,15 @@ print('Response Body:', response.text)
|
||||
result += templateHeaders.render({"headers": headersString});
|
||||
}
|
||||
}
|
||||
|
||||
if (requestModel.isFormDataRequest) {
|
||||
var formDataBodyData = jj.Template(kStringFormDataBody);
|
||||
result += formDataBodyData.render(
|
||||
{
|
||||
"fields_list": json.encode(requestModel.formDataMapList),
|
||||
"boundary": uuid,
|
||||
},
|
||||
);
|
||||
}
|
||||
var templateRequest = jj.Template(kTemplateRequest);
|
||||
result += templateRequest.render({
|
||||
"method": method.name.toLowerCase(),
|
||||
@ -137,15 +188,15 @@ print('Response Body:', response.text)
|
||||
result += kStringRequestParams;
|
||||
}
|
||||
|
||||
if (hasBody) {
|
||||
if (hasBody || requestModel.isFormDataRequest) {
|
||||
result += kStringRequestBody;
|
||||
}
|
||||
|
||||
if (hasJsonBody) {
|
||||
if (hasJsonBody || requestModel.isFormDataRequest) {
|
||||
result += kStringRequestJson;
|
||||
}
|
||||
|
||||
if (hasHeaders) {
|
||||
if (hasHeaders || requestModel.isFormDataRequest) {
|
||||
result += kStringRequestHeaders;
|
||||
}
|
||||
|
||||
|
@ -48,6 +48,10 @@ const kHintOpacity = 0.6;
|
||||
const kForegroundOpacity = 0.05;
|
||||
|
||||
const kTextStyleButton = TextStyle(fontWeight: FontWeight.bold);
|
||||
const kFormDataButtonLabelTextStyle = TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
);
|
||||
|
||||
const kBorderRadius8 = BorderRadius.all(Radius.circular(8));
|
||||
final kBorderRadius10 = BorderRadius.circular(10);
|
||||
@ -57,6 +61,7 @@ const kP1 = EdgeInsets.all(1);
|
||||
const kP5 = EdgeInsets.all(5);
|
||||
const kP8 = EdgeInsets.all(8);
|
||||
const kPs8 = EdgeInsets.only(left: 8);
|
||||
const kPs2 = EdgeInsets.only(left: 2);
|
||||
const kPh20v5 = EdgeInsets.symmetric(horizontal: 20, vertical: 5);
|
||||
const kPh20v10 = EdgeInsets.symmetric(horizontal: 20, vertical: 10);
|
||||
const kP10 = EdgeInsets.all(10);
|
||||
@ -85,7 +90,7 @@ const kP8CollectionPane = EdgeInsets.only(
|
||||
// bottom: 8.0,
|
||||
);
|
||||
const kPr8CollectionPane = EdgeInsets.only(right: 8.0);
|
||||
|
||||
const kpsV5 = EdgeInsets.symmetric(vertical: 2);
|
||||
const kHSpacer4 = SizedBox(width: 4);
|
||||
const kHSpacer5 = SizedBox(width: 5);
|
||||
const kHSpacer10 = SizedBox(width: 10);
|
||||
@ -237,7 +242,9 @@ enum RequestItemMenuOption { edit, delete, duplicate }
|
||||
|
||||
enum HTTPVerb { get, head, post, put, patch, delete }
|
||||
|
||||
enum ContentType { json, text }
|
||||
enum ContentType { json, text, formdata }
|
||||
|
||||
enum FormDataType { text, file }
|
||||
|
||||
const kSupportedUriSchemes = ["https", "http"];
|
||||
const kDefaultUriScheme = "https";
|
||||
@ -308,6 +315,7 @@ const kSubTypeDefaultViewOptions = 'all';
|
||||
const kContentTypeMap = {
|
||||
ContentType.json: "$kTypeApplication/$kSubTypeJson",
|
||||
ContentType.text: "$kTypeText/$kSubTypePlain",
|
||||
ContentType.formdata: "multipart/form-data",
|
||||
};
|
||||
|
||||
enum ResponseBodyView { preview, code, raw, none }
|
||||
|
23
lib/models/form_data_model.dart
Normal file
23
lib/models/form_data_model.dart
Normal file
@ -0,0 +1,23 @@
|
||||
import 'package:apidash/consts.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
part 'form_data_model.freezed.dart';
|
||||
part 'form_data_model.g.dart';
|
||||
|
||||
@freezed
|
||||
class FormDataModel with _$FormDataModel {
|
||||
const factory FormDataModel({
|
||||
required String name,
|
||||
required String value,
|
||||
required FormDataType type,
|
||||
}) = _FormDataModel;
|
||||
|
||||
factory FormDataModel.fromJson(Map<String, Object?> json) =>
|
||||
_$FormDataModelFromJson(json);
|
||||
}
|
||||
|
||||
const kFormDataEmptyModel = FormDataModel(
|
||||
name: "",
|
||||
value: "",
|
||||
type: FormDataType.text,
|
||||
);
|
187
lib/models/form_data_model.freezed.dart
Normal file
187
lib/models/form_data_model.freezed.dart
Normal file
@ -0,0 +1,187 @@
|
||||
// coverage:ignore-file
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||
|
||||
part of 'form_data_model.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// FreezedGenerator
|
||||
// **************************************************************************
|
||||
|
||||
T _$identity<T>(T value) => value;
|
||||
|
||||
final _privateConstructorUsedError = UnsupportedError(
|
||||
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods');
|
||||
|
||||
FormDataModel _$FormDataModelFromJson(Map<String, dynamic> json) {
|
||||
return _FormDataModel.fromJson(json);
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
mixin _$FormDataModel {
|
||||
String get name => throw _privateConstructorUsedError;
|
||||
String get value => throw _privateConstructorUsedError;
|
||||
FormDataType get type => throw _privateConstructorUsedError;
|
||||
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
@JsonKey(ignore: true)
|
||||
$FormDataModelCopyWith<FormDataModel> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $FormDataModelCopyWith<$Res> {
|
||||
factory $FormDataModelCopyWith(
|
||||
FormDataModel value, $Res Function(FormDataModel) then) =
|
||||
_$FormDataModelCopyWithImpl<$Res, FormDataModel>;
|
||||
@useResult
|
||||
$Res call({String name, String value, FormDataType type});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$FormDataModelCopyWithImpl<$Res, $Val extends FormDataModel>
|
||||
implements $FormDataModelCopyWith<$Res> {
|
||||
_$FormDataModelCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? name = null,
|
||||
Object? value = null,
|
||||
Object? type = null,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
name: null == name
|
||||
? _value.name
|
||||
: name // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
value: null == value
|
||||
? _value.value
|
||||
: value // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
type: null == type
|
||||
? _value.type
|
||||
: type // ignore: cast_nullable_to_non_nullable
|
||||
as FormDataType,
|
||||
) as $Val);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$FormDataModelImplCopyWith<$Res>
|
||||
implements $FormDataModelCopyWith<$Res> {
|
||||
factory _$$FormDataModelImplCopyWith(
|
||||
_$FormDataModelImpl value, $Res Function(_$FormDataModelImpl) then) =
|
||||
__$$FormDataModelImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call({String name, String value, FormDataType type});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$FormDataModelImplCopyWithImpl<$Res>
|
||||
extends _$FormDataModelCopyWithImpl<$Res, _$FormDataModelImpl>
|
||||
implements _$$FormDataModelImplCopyWith<$Res> {
|
||||
__$$FormDataModelImplCopyWithImpl(
|
||||
_$FormDataModelImpl _value, $Res Function(_$FormDataModelImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? name = null,
|
||||
Object? value = null,
|
||||
Object? type = null,
|
||||
}) {
|
||||
return _then(_$FormDataModelImpl(
|
||||
name: null == name
|
||||
? _value.name
|
||||
: name // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
value: null == value
|
||||
? _value.value
|
||||
: value // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
type: null == type
|
||||
? _value.type
|
||||
: type // ignore: cast_nullable_to_non_nullable
|
||||
as FormDataType,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
class _$FormDataModelImpl implements _FormDataModel {
|
||||
const _$FormDataModelImpl(
|
||||
{required this.name, required this.value, required this.type});
|
||||
|
||||
factory _$FormDataModelImpl.fromJson(Map<String, dynamic> json) =>
|
||||
_$$FormDataModelImplFromJson(json);
|
||||
|
||||
@override
|
||||
final String name;
|
||||
@override
|
||||
final String value;
|
||||
@override
|
||||
final FormDataType type;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'FormDataModel(name: $name, value: $value, type: $type)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(dynamic other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$FormDataModelImpl &&
|
||||
(identical(other.name, name) || other.name == name) &&
|
||||
(identical(other.value, value) || other.value == value) &&
|
||||
(identical(other.type, type) || other.type == type));
|
||||
}
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, name, value, type);
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$FormDataModelImplCopyWith<_$FormDataModelImpl> get copyWith =>
|
||||
__$$FormDataModelImplCopyWithImpl<_$FormDataModelImpl>(this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$$FormDataModelImplToJson(
|
||||
this,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _FormDataModel implements FormDataModel {
|
||||
const factory _FormDataModel(
|
||||
{required final String name,
|
||||
required final String value,
|
||||
required final FormDataType type}) = _$FormDataModelImpl;
|
||||
|
||||
factory _FormDataModel.fromJson(Map<String, dynamic> json) =
|
||||
_$FormDataModelImpl.fromJson;
|
||||
|
||||
@override
|
||||
String get name;
|
||||
@override
|
||||
String get value;
|
||||
@override
|
||||
FormDataType get type;
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
_$$FormDataModelImplCopyWith<_$FormDataModelImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
26
lib/models/form_data_model.g.dart
Normal file
26
lib/models/form_data_model.g.dart
Normal file
@ -0,0 +1,26 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'form_data_model.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
_$FormDataModelImpl _$$FormDataModelImplFromJson(Map<String, dynamic> json) =>
|
||||
_$FormDataModelImpl(
|
||||
name: json['name'] as String,
|
||||
value: json['value'] as String,
|
||||
type: $enumDecode(_$FormDataTypeEnumMap, json['type']),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$FormDataModelImplToJson(_$FormDataModelImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'name': instance.name,
|
||||
'value': instance.value,
|
||||
'type': _$FormDataTypeEnumMap[instance.type]!,
|
||||
};
|
||||
|
||||
const _$FormDataTypeEnumMap = {
|
||||
FormDataType.text: 'text',
|
||||
FormDataType.file: 'file',
|
||||
};
|
@ -2,3 +2,4 @@ export 'name_value_model.dart';
|
||||
export 'request_model.dart';
|
||||
export 'response_model.dart';
|
||||
export 'settings_model.dart';
|
||||
export 'form_data_model.dart';
|
||||
|
@ -1,10 +1,14 @@
|
||||
import 'dart:io';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:apidash/consts.dart';
|
||||
import 'package:apidash/utils/utils.dart'
|
||||
show mapToRows, rowsToMap, getEnabledRows;
|
||||
import 'name_value_model.dart';
|
||||
import 'response_model.dart';
|
||||
import '../utils/utils.dart'
|
||||
show
|
||||
mapListToFormDataModelRows,
|
||||
rowsToFormDataMapList,
|
||||
mapToRows,
|
||||
rowsToMap,
|
||||
getEnabledRows;
|
||||
import '../consts.dart';
|
||||
import 'models.dart';
|
||||
|
||||
@immutable
|
||||
class RequestModel {
|
||||
@ -21,6 +25,7 @@ class RequestModel {
|
||||
this.isParamEnabledList,
|
||||
this.requestBodyContentType = ContentType.json,
|
||||
this.requestBody,
|
||||
this.requestFormDataList,
|
||||
this.responseStatus,
|
||||
this.message,
|
||||
this.responseModel,
|
||||
@ -38,6 +43,7 @@ class RequestModel {
|
||||
final List<bool>? isParamEnabledList;
|
||||
final ContentType requestBodyContentType;
|
||||
final String? requestBody;
|
||||
final List<FormDataModel>? requestFormDataList;
|
||||
final int? responseStatus;
|
||||
final String? message;
|
||||
final ResponseModel? responseModel;
|
||||
@ -54,6 +60,10 @@ class RequestModel {
|
||||
Map<String, String> get headersMap => rowsToMap(requestHeaders) ?? {};
|
||||
Map<String, String> get paramsMap => rowsToMap(requestParams) ?? {};
|
||||
|
||||
List<Map<String, dynamic>> get formDataMapList =>
|
||||
rowsToFormDataMapList(requestFormDataList) ?? [];
|
||||
bool get isFormDataRequest => requestBodyContentType == ContentType.formdata;
|
||||
|
||||
bool get hasContentTypeHeader => enabledHeadersMap.keys
|
||||
.any((k) => k.toLowerCase() == HttpHeaders.contentTypeHeader);
|
||||
|
||||
@ -74,6 +84,8 @@ class RequestModel {
|
||||
isParamEnabledList != null ? [...isParamEnabledList!] : null,
|
||||
requestBodyContentType: requestBodyContentType,
|
||||
requestBody: requestBody,
|
||||
requestFormDataList:
|
||||
requestFormDataList != null ? [...requestFormDataList!] : null,
|
||||
);
|
||||
}
|
||||
|
||||
@ -90,6 +102,7 @@ class RequestModel {
|
||||
List<bool>? isParamEnabledList,
|
||||
ContentType? requestBodyContentType,
|
||||
String? requestBody,
|
||||
List<FormDataModel>? requestFormDataList,
|
||||
int? responseStatus,
|
||||
String? message,
|
||||
ResponseModel? responseModel,
|
||||
@ -112,6 +125,7 @@ class RequestModel {
|
||||
requestBodyContentType:
|
||||
requestBodyContentType ?? this.requestBodyContentType,
|
||||
requestBody: requestBody ?? this.requestBody,
|
||||
requestFormDataList: requestFormDataList ?? this.requestFormDataList,
|
||||
responseStatus: responseStatus ?? this.responseStatus,
|
||||
message: message ?? this.message,
|
||||
responseModel: responseModel ?? this.responseModel,
|
||||
@ -143,9 +157,11 @@ class RequestModel {
|
||||
requestBodyContentType = kDefaultContentType;
|
||||
}
|
||||
final requestBody = data["requestBody"] as String?;
|
||||
final requestFormDataList = data["requestFormDataList"];
|
||||
final responseStatus = data["responseStatus"] as int?;
|
||||
final message = data["message"] as String?;
|
||||
final responseModelJson = data["responseModel"];
|
||||
|
||||
if (responseModelJson != null) {
|
||||
responseModel =
|
||||
ResponseModel.fromJson(Map<String, dynamic>.from(responseModelJson));
|
||||
@ -170,6 +186,9 @@ class RequestModel {
|
||||
isParamEnabledList: isParamEnabledList,
|
||||
requestBodyContentType: requestBodyContentType,
|
||||
requestBody: requestBody,
|
||||
requestFormDataList: requestFormDataList != null
|
||||
? mapListToFormDataModelRows(List<Map>.from(requestFormDataList))
|
||||
: null,
|
||||
responseStatus: responseStatus,
|
||||
message: message,
|
||||
responseModel: responseModel,
|
||||
@ -189,6 +208,7 @@ class RequestModel {
|
||||
"isParamEnabledList": isParamEnabledList,
|
||||
"requestBodyContentType": requestBodyContentType.name,
|
||||
"requestBody": requestBody,
|
||||
"requestFormDataList": rowsToFormDataMapList(requestFormDataList),
|
||||
"responseStatus": includeResponse ? responseStatus : null,
|
||||
"message": includeResponse ? message : null,
|
||||
"responseModel": includeResponse ? responseModel?.toJson() : null,
|
||||
@ -210,6 +230,7 @@ class RequestModel {
|
||||
"Enabled Params: ${isParamEnabledList.toString()}",
|
||||
"Request Body Content Type: ${requestBodyContentType.toString()}",
|
||||
"Request Body: ${requestBody.toString()}",
|
||||
"Request FormData: ${requestFormDataList.toString()}",
|
||||
"Response Status: $responseStatus",
|
||||
"Response Message: $message",
|
||||
"Response: ${responseModel.toString()}"
|
||||
@ -232,6 +253,7 @@ class RequestModel {
|
||||
listEquals(other.isParamEnabledList, isParamEnabledList) &&
|
||||
other.requestBodyContentType == requestBodyContentType &&
|
||||
other.requestBody == requestBody &&
|
||||
other.requestFormDataList == requestFormDataList &&
|
||||
other.responseStatus == responseStatus &&
|
||||
other.message == message &&
|
||||
other.responseModel == responseModel;
|
||||
@ -253,6 +275,7 @@ class RequestModel {
|
||||
isParamEnabledList,
|
||||
requestBodyContentType,
|
||||
requestBody,
|
||||
requestFormDataList,
|
||||
responseStatus,
|
||||
message,
|
||||
responseModel,
|
||||
|
@ -5,6 +5,7 @@ import '../models/models.dart';
|
||||
import '../services/services.dart' show hiveHandler, HiveHandler, request;
|
||||
import '../utils/utils.dart' show uuid, collectionToHAR;
|
||||
import '../consts.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
|
||||
final activeIdStateProvider = StateProvider<String?>((ref) => null);
|
||||
|
||||
@ -127,6 +128,7 @@ class CollectionStateNotifier
|
||||
List<bool>? isParamEnabledList,
|
||||
ContentType? requestBodyContentType,
|
||||
String? requestBody,
|
||||
List<FormDataModel>? requestFormDataList,
|
||||
int? responseStatus,
|
||||
String? message,
|
||||
ResponseModel? responseModel,
|
||||
@ -143,9 +145,11 @@ class CollectionStateNotifier
|
||||
isParamEnabledList: isParamEnabledList,
|
||||
requestBodyContentType: requestBodyContentType,
|
||||
requestBody: requestBody,
|
||||
requestFormDataList: requestFormDataList,
|
||||
responseStatus: responseStatus,
|
||||
message: message,
|
||||
responseModel: responseModel);
|
||||
//print(newModel);
|
||||
var map = {...state!};
|
||||
map[id] = newModel;
|
||||
state = map;
|
||||
@ -156,10 +160,13 @@ class CollectionStateNotifier
|
||||
ref.read(codePaneVisibleStateProvider.notifier).state = false;
|
||||
final defaultUriScheme =
|
||||
ref.read(settingsProvider.select((value) => value.defaultUriScheme));
|
||||
|
||||
RequestModel requestModel = state![id]!;
|
||||
var responseRec =
|
||||
await request(requestModel, defaultUriScheme: defaultUriScheme);
|
||||
(http.Response?, Duration?, String?)? responseRec = await request(
|
||||
requestModel,
|
||||
defaultUriScheme: defaultUriScheme,
|
||||
isMultiPartRequest:
|
||||
requestModel.requestBodyContentType == ContentType.formdata,
|
||||
);
|
||||
late final RequestModel newRequestModel;
|
||||
if (responseRec.$1 == null) {
|
||||
newRequestModel = requestModel.copyWith(
|
||||
|
@ -18,6 +18,10 @@ class _EditRequestBodyState extends ConsumerState<EditRequestBody> {
|
||||
final requestModel = ref
|
||||
.read(collectionStateNotifierProvider.notifier)
|
||||
.getRequestModel(activeId!);
|
||||
ContentType? requestBodyStateWatcher = (ref
|
||||
.watch(collectionStateNotifierProvider)![activeId]
|
||||
?.requestBodyContentType) ??
|
||||
ContentType.values.first;
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
@ -38,7 +42,9 @@ class _EditRequestBodyState extends ConsumerState<EditRequestBody> {
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: TextFieldEditor(
|
||||
child: requestBodyStateWatcher == ContentType.formdata
|
||||
? const FormDataWidget()
|
||||
: TextFieldEditor(
|
||||
key: Key("$activeId-body"),
|
||||
fieldKey: "$activeId-body-editor",
|
||||
initialValue: requestModel?.requestBody,
|
||||
|
@ -9,6 +9,7 @@ import 'package:apidash/consts.dart';
|
||||
Future<(http.Response?, Duration?, String?)> request(
|
||||
RequestModel requestModel, {
|
||||
String defaultUriScheme = kDefaultUriScheme,
|
||||
bool isMultiPartRequest = false,
|
||||
}) async {
|
||||
(Uri?, String?) uriRec = getValidRequestUri(
|
||||
requestModel.url,
|
||||
@ -35,6 +36,31 @@ Future<(http.Response?, Duration?, String?)> request(
|
||||
}
|
||||
}
|
||||
Stopwatch stopwatch = Stopwatch()..start();
|
||||
if (isMultiPartRequest) {
|
||||
var multiPartRequest = http.MultipartRequest(
|
||||
requestModel.method.name.toUpperCase(),
|
||||
requestUrl,
|
||||
);
|
||||
multiPartRequest.headers.addAll(headers);
|
||||
for (FormDataModel formData
|
||||
in (requestModel.requestFormDataList ?? [])) {
|
||||
if (formData.type == FormDataType.text) {
|
||||
multiPartRequest.fields.addAll({formData.name: formData.value});
|
||||
} else {
|
||||
multiPartRequest.files.add(
|
||||
await http.MultipartFile.fromPath(
|
||||
formData.name,
|
||||
formData.value,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
http.StreamedResponse multiPartResponse = await multiPartRequest.send();
|
||||
stopwatch.stop();
|
||||
http.Response convertedMultiPartResponse =
|
||||
await convertStreamedResponse(multiPartResponse);
|
||||
return (convertedMultiPartResponse, stopwatch.elapsed, null);
|
||||
}
|
||||
switch (requestModel.method) {
|
||||
case HTTPVerb.get:
|
||||
response = await http.get(requestUrl, headers: headers);
|
||||
|
@ -2,6 +2,7 @@ import 'dart:typed_data';
|
||||
import 'dart:convert';
|
||||
import '../models/models.dart';
|
||||
import '../consts.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
|
||||
String humanizeDuration(Duration? duration) {
|
||||
if (duration == null) {
|
||||
@ -89,6 +90,43 @@ List<NameValueModel>? mapToRows(Map<String, String>? kvMap) {
|
||||
return finalRows;
|
||||
}
|
||||
|
||||
List<Map<String, dynamic>>? rowsToFormDataMapList(
|
||||
List<FormDataModel>? kvRows,
|
||||
) {
|
||||
if (kvRows == null) {
|
||||
return null;
|
||||
}
|
||||
List<Map<String, dynamic>> finalMap = kvRows
|
||||
.map((FormDataModel formData) => {
|
||||
"name": formData.name,
|
||||
"value": formData.value,
|
||||
"type": formData.type.name,
|
||||
})
|
||||
.toList();
|
||||
return finalMap;
|
||||
}
|
||||
|
||||
List<FormDataModel>? mapListToFormDataModelRows(List<Map>? kvMap) {
|
||||
if (kvMap == null) {
|
||||
return null;
|
||||
}
|
||||
List<FormDataModel> finalRows = kvMap.map(
|
||||
(formData) {
|
||||
return FormDataModel(
|
||||
name: formData["name"],
|
||||
value: formData["value"],
|
||||
type: getFormDataType(formData["type"]),
|
||||
);
|
||||
},
|
||||
).toList();
|
||||
return finalRows;
|
||||
}
|
||||
|
||||
FormDataType getFormDataType(String? type) {
|
||||
return FormDataType.values.firstWhere((element) => element.name == type,
|
||||
orElse: () => FormDataType.text);
|
||||
}
|
||||
|
||||
Uint8List? stringToBytes(String? text) {
|
||||
if (text == null) {
|
||||
return null;
|
||||
@ -110,6 +148,23 @@ Uint8List jsonMapToBytes(Map<String, dynamic>? map) {
|
||||
}
|
||||
}
|
||||
|
||||
Future<http.Response> convertStreamedResponse(
|
||||
http.StreamedResponse streamedResponse,
|
||||
) async {
|
||||
Uint8List bodyBytes = await streamedResponse.stream.toBytes();
|
||||
|
||||
http.Response response = http.Response.bytes(
|
||||
bodyBytes,
|
||||
streamedResponse.statusCode,
|
||||
headers: streamedResponse.headers,
|
||||
persistentConnection: streamedResponse.persistentConnection,
|
||||
reasonPhrase: streamedResponse.reasonPhrase,
|
||||
request: streamedResponse.request,
|
||||
);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
List<NameValueModel>? getEnabledRows(
|
||||
List<NameValueModel>? rows, List<bool>? isRowEnabledList) {
|
||||
if (rows == null || isRowEnabledList == null) {
|
||||
|
@ -1,6 +1,8 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'package:apidash/consts.dart';
|
||||
import 'package:apidash/models/form_data_model.dart';
|
||||
import 'package:apidash/utils/convert_utils.dart';
|
||||
import 'package:apidash/utils/utils.dart' show getValidRequestUri;
|
||||
import 'package:apidash/models/models.dart' show RequestModel;
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
@ -153,7 +155,9 @@ Map<String, dynamic> requestModelToHARJsonRequest(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (requestModel.isFormDataRequest) {
|
||||
json["formData"] = requestModel.formDataMapList;
|
||||
}
|
||||
if (exportMode) {
|
||||
json["comment"] = "";
|
||||
json["cookies"] = [];
|
||||
|
@ -93,6 +93,56 @@ class DropdownButtonContentType extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class DropdownButtonFormData extends StatefulWidget {
|
||||
const DropdownButtonFormData({
|
||||
super.key,
|
||||
this.formDataType,
|
||||
this.onChanged,
|
||||
});
|
||||
|
||||
final FormDataType? formDataType;
|
||||
final void Function(FormDataType?)? onChanged;
|
||||
|
||||
@override
|
||||
State<DropdownButtonFormData> createState() => _DropdownButtonFormData();
|
||||
}
|
||||
|
||||
class _DropdownButtonFormData extends State<DropdownButtonFormData> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final surfaceColor = Theme.of(context).colorScheme.surface;
|
||||
return DropdownButton<FormDataType>(
|
||||
dropdownColor: surfaceColor,
|
||||
focusColor: surfaceColor,
|
||||
value: widget.formDataType,
|
||||
icon: const Icon(
|
||||
Icons.unfold_more_rounded,
|
||||
size: 16,
|
||||
),
|
||||
elevation: 4,
|
||||
style: kCodeStyle.copyWith(
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
underline: const IgnorePointer(),
|
||||
onChanged: widget.onChanged,
|
||||
borderRadius: kBorderRadius12,
|
||||
items: FormDataType.values
|
||||
.map<DropdownMenuItem<FormDataType>>((FormDataType value) {
|
||||
return DropdownMenuItem<FormDataType>(
|
||||
value: value,
|
||||
child: Padding(
|
||||
padding: kPs8,
|
||||
child: Text(
|
||||
value.name,
|
||||
style: kTextStyleButton,
|
||||
),
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DropdownButtonCodegenLanguage extends StatelessWidget {
|
||||
const DropdownButtonCodegenLanguage({
|
||||
super.key,
|
||||
|
81
lib/widgets/form_data_field.dart
Normal file
81
lib/widgets/form_data_field.dart
Normal file
@ -0,0 +1,81 @@
|
||||
import 'package:apidash/consts.dart';
|
||||
import 'package:apidash/widgets/widgets.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class FormDataField extends StatefulWidget {
|
||||
const FormDataField({
|
||||
super.key,
|
||||
required this.keyId,
|
||||
this.initialValue,
|
||||
this.hintText,
|
||||
this.onChanged,
|
||||
this.colorScheme,
|
||||
this.formDataType,
|
||||
this.onFormDataTypeChanged,
|
||||
});
|
||||
|
||||
final String keyId;
|
||||
final String? initialValue;
|
||||
final String? hintText;
|
||||
final void Function(String)? onChanged;
|
||||
final ColorScheme? colorScheme;
|
||||
final FormDataType? formDataType;
|
||||
final void Function(FormDataType?)? onFormDataTypeChanged;
|
||||
|
||||
@override
|
||||
State<FormDataField> createState() => _FormDataFieldState();
|
||||
}
|
||||
|
||||
class _FormDataFieldState extends State<FormDataField> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var colorScheme = widget.colorScheme ?? Theme.of(context).colorScheme;
|
||||
return Row(
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: TextFormField(
|
||||
initialValue: widget.initialValue,
|
||||
key: Key(widget.keyId),
|
||||
style: kCodeStyle.copyWith(
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
decoration: InputDecoration(
|
||||
hintStyle: kCodeStyle.copyWith(
|
||||
color: colorScheme.outline.withOpacity(
|
||||
kHintOpacity,
|
||||
),
|
||||
),
|
||||
hintText: widget.hintText,
|
||||
focusedBorder: UnderlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: colorScheme.primary.withOpacity(
|
||||
kHintOpacity,
|
||||
),
|
||||
),
|
||||
),
|
||||
enabledBorder: UnderlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: colorScheme.surfaceVariant,
|
||||
),
|
||||
),
|
||||
suffixIcon: DropdownButtonFormData(
|
||||
formDataType: widget.formDataType,
|
||||
onChanged: (p0) {
|
||||
if (widget.onFormDataTypeChanged != null) {
|
||||
widget.onFormDataTypeChanged!(p0);
|
||||
}
|
||||
},
|
||||
)),
|
||||
onChanged: widget.onChanged,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
223
lib/widgets/form_data_widget.dart
Normal file
223
lib/widgets/form_data_widget.dart
Normal file
@ -0,0 +1,223 @@
|
||||
import 'dart:math';
|
||||
import 'package:apidash/consts.dart';
|
||||
import 'package:apidash/models/form_data_model.dart';
|
||||
import 'package:apidash/models/models.dart';
|
||||
import 'package:apidash/providers/collection_providers.dart';
|
||||
import 'package:apidash/widgets/form_data_field.dart';
|
||||
import 'package:apidash/widgets/textfields.dart';
|
||||
import 'package:davi/davi.dart';
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
class FormDataWidget extends ConsumerStatefulWidget {
|
||||
const FormDataWidget({super.key});
|
||||
@override
|
||||
ConsumerState<FormDataWidget> createState() => _FormDataBodyState();
|
||||
}
|
||||
|
||||
class _FormDataBodyState extends ConsumerState<FormDataWidget> {
|
||||
late int seed;
|
||||
final random = Random.secure();
|
||||
late List<FormDataModel> rows;
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
seed = random.nextInt(kRandMax);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final activeId = ref.watch(activeIdStateProvider);
|
||||
var formRows = ref.read(activeRequestModelProvider)?.requestFormDataList;
|
||||
rows =
|
||||
formRows == null || formRows.isEmpty ? [kFormDataEmptyModel] : formRows;
|
||||
|
||||
DaviModel<FormDataModel> daviModelRows = DaviModel<FormDataModel>(
|
||||
rows: rows,
|
||||
columns: [
|
||||
DaviColumn(
|
||||
cellPadding: kpsV5,
|
||||
name: 'Key',
|
||||
grow: 4,
|
||||
cellBuilder: (_, row) {
|
||||
int idx = row.index;
|
||||
return Theme(
|
||||
data: Theme.of(context),
|
||||
child: FormDataField(
|
||||
keyId: "$activeId-$idx-form-v-$seed",
|
||||
initialValue: rows[idx].name,
|
||||
hintText: " Add Key",
|
||||
onChanged: (value) {
|
||||
rows[idx] = rows[idx].copyWith(
|
||||
name: value,
|
||||
);
|
||||
_onFieldChange(activeId!);
|
||||
},
|
||||
colorScheme: Theme.of(context).colorScheme,
|
||||
formDataType: rows[idx].type,
|
||||
onFormDataTypeChanged: (value) {
|
||||
rows[idx] = rows[idx].copyWith(
|
||||
type: value ?? FormDataType.text,
|
||||
);
|
||||
rows[idx] = rows[idx].copyWith(value: "");
|
||||
setState(() {});
|
||||
_onFieldChange(activeId!);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
sortable: false,
|
||||
),
|
||||
DaviColumn(
|
||||
width: 40,
|
||||
cellPadding: kpsV5,
|
||||
cellAlignment: Alignment.center,
|
||||
cellBuilder: (_, row) {
|
||||
return Text(
|
||||
"=",
|
||||
style: kCodeStyle,
|
||||
);
|
||||
},
|
||||
),
|
||||
DaviColumn(
|
||||
name: 'Value',
|
||||
grow: 4,
|
||||
cellPadding: kpsV5,
|
||||
cellBuilder: (_, row) {
|
||||
int idx = row.index;
|
||||
return rows[idx].type == FormDataType.file
|
||||
? Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Theme(
|
||||
data: Theme.of(context),
|
||||
child: ElevatedButton.icon(
|
||||
icon: const Icon(
|
||||
Icons.snippet_folder_rounded,
|
||||
size: 20,
|
||||
),
|
||||
style: ButtonStyle(
|
||||
shape: MaterialStatePropertyAll(
|
||||
RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
),
|
||||
),
|
||||
),
|
||||
onPressed: () async {
|
||||
FilePickerResult? pickedResult =
|
||||
await FilePicker.platform.pickFiles();
|
||||
if (pickedResult != null &&
|
||||
pickedResult.files.isNotEmpty &&
|
||||
pickedResult.files.first.path != null) {
|
||||
rows[idx] = rows[idx].copyWith(
|
||||
value: pickedResult.files.first.path!,
|
||||
);
|
||||
setState(() {});
|
||||
_onFieldChange(activeId!);
|
||||
}
|
||||
},
|
||||
label: Text(
|
||||
(rows[idx].type == FormDataType.file &&
|
||||
rows[idx].value.isNotEmpty)
|
||||
? rows[idx].value.toString()
|
||||
: "Select File",
|
||||
textAlign: TextAlign.center,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: kFormDataButtonLabelTextStyle,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
: CellField(
|
||||
keyId: "$activeId-$idx-form-v-$seed",
|
||||
initialValue: rows[idx].value,
|
||||
hintText: " Add Value",
|
||||
onChanged: (value) {
|
||||
rows[idx] = rows[idx].copyWith(value: value);
|
||||
_onFieldChange(activeId!);
|
||||
},
|
||||
colorScheme: Theme.of(context).colorScheme,
|
||||
);
|
||||
},
|
||||
sortable: false,
|
||||
),
|
||||
DaviColumn(
|
||||
pinStatus: PinStatus.none,
|
||||
width: 30,
|
||||
cellBuilder: (_, row) {
|
||||
return InkWell(
|
||||
child: Theme.of(context).brightness == Brightness.dark
|
||||
? kIconRemoveDark
|
||||
: kIconRemoveLight,
|
||||
onTap: () {
|
||||
seed = random.nextInt(kRandMax);
|
||||
if (rows.length == 1) {
|
||||
setState(() {
|
||||
rows = [kFormDataEmptyModel];
|
||||
});
|
||||
} else {
|
||||
rows.removeAt(row.index);
|
||||
}
|
||||
_onFieldChange(activeId!);
|
||||
setState(() {});
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
return Stack(
|
||||
children: [
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
borderRadius: kBorderRadius12,
|
||||
),
|
||||
margin: kP10,
|
||||
child: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: DaviTheme(
|
||||
data: kTableThemeData,
|
||||
child: Davi<FormDataModel>(daviModelRows),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Align(
|
||||
alignment: Alignment.bottomCenter,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(bottom: 30),
|
||||
child: ElevatedButton.icon(
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
rows.add(kFormDataEmptyModel);
|
||||
});
|
||||
_onFieldChange(activeId!);
|
||||
},
|
||||
icon: const Icon(Icons.add),
|
||||
label: const Text(
|
||||
"Add Form Data",
|
||||
style: kTextStyleButton,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
void _onFieldChange(String activeId) {
|
||||
ref.read(collectionStateNotifierProvider.notifier).update(
|
||||
activeId,
|
||||
requestFormDataList: rows,
|
||||
);
|
||||
}
|
||||
}
|
@ -22,3 +22,4 @@ export 'textfields.dart';
|
||||
export 'texts.dart';
|
||||
export 'uint8_audio_player.dart';
|
||||
export 'window_caption.dart';
|
||||
export 'form_data_widget.dart';
|
||||
|
@ -48,6 +48,7 @@ dependencies:
|
||||
url: https://github.com/foss42/json_data_explorer.git
|
||||
version: ^0.1.1
|
||||
scrollable_positioned_list: ^0.2.3
|
||||
file_picker: ^6.1.1
|
||||
flutter_svg: ^2.0.9
|
||||
vector_graphics_compiler: ^1.1.9+1
|
||||
code_builder: ^4.9.0
|
||||
|
@ -113,6 +113,7 @@ void main() {
|
||||
"requestBody": '''{
|
||||
"text":"WORLD"
|
||||
}''',
|
||||
'requestFormDataList': null,
|
||||
'responseStatus': null,
|
||||
'message': null,
|
||||
'responseModel': null
|
||||
@ -147,6 +148,7 @@ void main() {
|
||||
"Enabled Params: null",
|
||||
"Request Body Content Type: ContentType.json",
|
||||
'Request Body: {\n"text":"WORLD"\n}',
|
||||
'Request FormData: null',
|
||||
"Response Status: null",
|
||||
"Response Message: null",
|
||||
"Response: null"
|
||||
|
Reference in New Issue
Block a user