Merge branch 'main' into feat_ruby_net_http

This commit is contained in:
Ankit Mahato
2024-04-06 13:50:54 +05:30
committed by GitHub
48 changed files with 5844 additions and 1151 deletions

View File

@ -127,6 +127,7 @@ API Dash currently supports API integration code generation for the following la
| ---------------------- | ------------- | ------- |
| cURL | | |
| HAR | | |
| C# | `RestSharp` | |
| Dart | `http` | |
| Dart | `dio` | |
| Go | `net/http` | |
@ -141,9 +142,10 @@ API Dash currently supports API integration code generation for the following la
| Rust | `ureq` | |
| Rust | `Actix Client` | |
| Java | `asynchttpclient` | https://github.com/foss42/apidash/issues/136 |
| Java | `HttpClient` | https://github.com/foss42/apidash/issues/137 |
| Java | `HttpClient` | |
| Java | `okhttp3` | |
| Julia | `HTTP` | https://github.com/foss42/apidash/issues/154 |
| Julia | `HTTP` | |
| PHP | `curl` | https://github.com/foss42/apidash/issues/375 |
| PHP | `guzzle` | https://github.com/foss42/apidash/issues/143 |
We welcome contributions to support other programming languages/libraries/frameworks. Please check out more details [here](https://github.com/foss42/apidash/discussions/80).
@ -157,7 +159,16 @@ Here is the complete list of mimetypes that can be directly previewed in API Das
| File Type | Mimetype | Extension | Comment |
| --------- | -------------------------- | ----------------- | -------- |
| PDF | `application/pdf` | `.pdf` | |
| CSV | `text/csv` | `.csv` | Can be improved |
| Video | `video/mp4` | `.mp4` | |
| Video | `video/webm` | `.webm` | |
| Video | `video/x-ms-wmv` | `.wmv` | |
| Video | `video/x-ms-asf` | `.wmv` | |
| Video | `video/avi` | `.avi` | |
| Video | `video/msvideo` | `.avi` | |
| Video | `video/x-msvideo` | `.avi` | |
| Video | `video/quicktime` | `.mov` | |
| Video | `video/x-quicktime` | `.mov` | |
| Video | `video/x-matroska` | `.mkv` | |
| Image | `image/apng` | `.apng` | Animated |
| Image | `image/avif` | `.avif` | |
| Image | `image/bmp` | `.bmp` | |
@ -188,6 +199,7 @@ Here is the complete list of mimetypes that can be directly previewed in API Das
| Audio | `audio/x-m4a` | `.m4a` | |
| Audio | `audio/wav` | `.wav` | |
| Audio | `audio/wave` | `.wav` | |
| CSV | `text/csv` | `.csv` | Can be improved |
We welcome PRs to add support for previewing other multimedia mimetypes. Please go ahead and raise an issue so that we can discuss the approach.
We are adding support for other mimetypes with each release. But, if you are looking for any particular mimetype support, please go ahead and open an issue. We will prioritize it's addition.

View File

@ -57,7 +57,7 @@ class _AppState extends ConsumerState<App> with WindowListener {
bool isPreventClose = await windowManager.isPreventClose();
if (isPreventClose) {
if (ref.watch(
settingsProvider.select((value) => value.promptBeforeClosing))) {
settingsProvider.select((value) => value.promptBeforeClosing)) && ref.watch(hasUnsavedChangesProvider)) {
showDialog(
context: context,
builder: (_) => AlertDialog(

View File

@ -1,13 +1,17 @@
import 'package:apidash/models/models.dart' show RequestModel;
import 'package:apidash/consts.dart';
import 'package:apidash/utils/utils.dart' show getNewUuid;
import 'csharp/rest_sharp.dart';
import 'dart/http.dart';
import 'dart/dio.dart';
import 'go/http.dart';
import 'kotlin/okhttp.dart';
import 'php/guzzle.dart';
import 'php/curl.dart';
import 'python/http_client.dart';
import 'python/requests.dart';
import 'ruby/faraday.dart';
import 'ruby/net_http.dart';
import 'rust/actix.dart';
import 'rust/curl_rust.dart';
import 'rust/reqwest.dart';
@ -20,7 +24,6 @@ import 'julia/http.dart';
import 'java/okhttp.dart';
import 'java/async_http_client.dart';
import 'java/httpclient.dart';
import 'ruby/net_http.dart';
class Codegen {
String? getCode(
@ -61,7 +64,7 @@ class Codegen {
case CodegenLanguage.javaAsyncHttpClient:
return JavaAsyncHttpClientGen().getCode(rM);
case CodegenLanguage.javaHttpClient:
return JavaHttpClientCodeGen().getCode(rM);
return JavaHttpClientCodeGen().getCode(rM, boundary: boundary);
case CodegenLanguage.javaOkHttp:
return JavaOkHttpCodeGen().getCode(rM);
case CodegenLanguage.juliaHttp:
@ -73,6 +76,10 @@ class Codegen {
.getCode(rM, boundary: boundary ?? getNewUuid());
case CodegenLanguage.pythonRequests:
return PythonRequestsCodeGen().getCode(rM, boundary: boundary);
case CodegenLanguage.rubyFaraday:
return RubyFaradayCodeGen().getCode(rM);
case CodegenLanguage.rubyNetHttp:
return rubyCodeGen().getCode(rM);
case CodegenLanguage.rustActix:
return RustActixCodeGen().getCode(rM, boundary: boundary);
case CodegenLanguage.rustCurl:
@ -83,8 +90,10 @@ class Codegen {
return RustUreqCodeGen().getCode(rM, boundary: boundary);
case CodegenLanguage.phpGuzzle:
return PhpGuzzleCodeGen().getCode(rM);
case CodegenLanguage.rubyNetHttp:
return rubyCodeGen().getCode(rM);
case CodegenLanguage.phpCurl:
return PHPcURLCodeGen().getCode(rM);
case CodegenLanguage.cSharpRestSharp:
return CSharpRestSharp().getCode(rM);
}
}
}

View File

@ -0,0 +1,197 @@
import 'dart:convert';
import 'package:apidash/consts.dart';
import 'package:jinja/jinja.dart' as jj;
import '../../models/request_model.dart';
import '../../extensions/extensions.dart';
import '../../utils/http_utils.dart';
class CSharpRestSharp {
String kStringImports = """
using System;
using RestSharp;
using System.Threading.Tasks;
""";
String kStringInit = """
class Program
{
static async Task Main(){
try{
""";
String kInitClientTemplate = """
const String _baseUrl = "{{baseUrl}}";
var client = new RestClient(_baseUrl);
""";
String kMethodTypeTemplate = """
var request = new RestRequest("{{path}}", Method.{{method}});
""";
String kTemplateParams = """
request.AddQueryParameter("{{param}}", "{{value}}");
""";
String kTemplateHeaders = """
request.AddHeader("{{header}}", "{{value}}");
""";
String kTemplateFormData = """
{% if type == "text" -%}
request.AddParameter("{{name}}", "{{value}}", ParameterType.GetOrPost);
{% else -%}
request.AddFile("{{name}}", "{{value}}", options: options);
{% endif -%}
""";
String kStringFormDataOption = """
request.AlwaysMultipartFormData = true;
""";
String kStringFormdataFileOption = """
var options = new FileParameterOptions
{
DisableFilenameEncoding = true
};
""";
String kTemplateJsonData = """
var jsonBody = new {{jsonData}};
request.AddJsonBody(jsonBody);
""";
String kTemplateTextData = """
var textBody = {{textData}};
request.AddStringBody(textBody, ContentType.Plain);
""";
String kStringEnd = """
var response = await client.ExecuteAsync(request);
Console.WriteLine("Status Code: " + (int)response.StatusCode);
Console.WriteLine("Response Content: " + response.Content);
}
catch(Exception ex){
Console.WriteLine("Error: " + ex);
}
}
}
""";
String? getCode(RequestModel requestModel) {
try {
String result = "";
var rec = getValidRequestUri(
requestModel.url,
requestModel.enabledRequestParams,
);
Uri? uri = rec.$1;
if (uri != null) {
jj.Template kNodejsImportTemplate = jj.Template(kStringImports);
String importsData = kNodejsImportTemplate.render();
result += importsData;
result += kStringInit;
jj.Template templateInitClient = jj.Template(kInitClientTemplate);
String initClient = templateInitClient
.render({"baseUrl": "${uri.scheme}://${uri.authority}"});
result += initClient;
jj.Template templateMethodType = jj.Template(kMethodTypeTemplate);
String methodType = templateMethodType.render({
"path": uri.path,
"method": requestModel.method.name.capitalize(),
});
result += methodType;
if (uri.hasQuery) {
var params = uri.queryParameters;
if (params.isNotEmpty) {
jj.Template templateParams = jj.Template(kTemplateParams);
String paramsResult = "";
for (var item in params.entries) {
paramsResult += templateParams
.render({"param": item.key, "value": item.value});
}
result += "$paramsResult\n";
}
}
var headersList = requestModel.enabledRequestHeaders;
if (headersList != null ||
requestModel.hasJsonData ||
requestModel.hasTextData) {
var headers = requestModel.enabledHeadersMap;
if (requestModel.hasJsonData || requestModel.hasTextData) {
headers[kHeaderContentType] =
requestModel.requestBodyContentType.header;
}
if (headers.isNotEmpty) {
jj.Template templateHeaders = jj.Template(kTemplateHeaders);
String headersResult = "";
for (var item in headers.entries) {
headersResult += templateHeaders
.render({"header": item.key, "value": item.value});
}
result += "$headersResult\n";
}
}
if (requestModel.hasFormData) {
jj.Template templateFormData = jj.Template(kTemplateFormData);
String formDataResult = "";
for (var data in requestModel.formDataMapList) {
formDataResult += templateFormData.render({
"name": data["name"],
"value": data["value"],
"type": data["type"]
});
}
result += kStringFormDataOption;
if (requestModel.hasFileInFormData) {
result += kStringFormdataFileOption;
}
result += "$formDataResult\n";
}
if (requestModel.hasJsonData) {
var templateJsonData = jj.Template(kTemplateJsonData);
Map<String, dynamic> bodyData =
json.decode(requestModel.requestBody!);
List<String> jsonArr = [];
bodyData.forEach((key, value) {
jsonArr += ["$key = \"$value\""];
});
String jsonDataResult = "{\n${jsonArr.join(",\n")}\n}";
result += templateJsonData.render({"jsonData": jsonDataResult});
}
if (requestModel.hasTextData) {
jj.Template templateTextData = jj.Template(kTemplateTextData);
result += templateTextData
.render({"textData": jsonEncode(requestModel.requestBody)});
}
result += kStringEnd;
}
return result;
} catch (e) {
return null;
}
}
}

View File

@ -148,9 +148,9 @@ public class Main {
// especially sets up Content-Type header if the request has a body
// and Content-Type is not explicitely set by the developer
if (hasBody &&
!requestModel.enabledHeadersMap.containsKey('Content-Type')) {
!requestModel.enabledHeadersMap.containsKey(kHeaderContentType)) {
result += templateRequestHeader
.render({"name": 'Content-Type', "value": contentType});
.render({"name": kHeaderContentType, "value": contentType});
}
// setting up rest of the request headers

View File

@ -1,183 +1,197 @@
import 'dart:convert';
import 'package:jinja/jinja.dart' as jj;
import 'package:apidash/utils/utils.dart'
show getValidRequestUri, requestModelToHARJsonRequest, stripUriParams;
import '../../models/request_model.dart';
show getValidRequestUri, requestModelToHARJsonRequest;
import 'package:apidash/models/models.dart' show RequestModel;
import 'package:apidash/consts.dart';
class JavaHttpClientCodeGen {
final String kTemplateStart = """
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
final String kTemplateStart = """import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.http.HttpHeaders;
import java.net.http.HttpRequest.BodyPublishers;
import java.net.http.HttpResponse.BodyHandlers;
import java.io.IOException;
{% if hasFormData %}import java.util.HashMap;
import java.util.Map;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;{% endif %}
public class JavaHttpClientExample {
public static void main(String[] args) throws IOException, InterruptedException {
HttpClient client = HttpClient.newHttpClient();
""";
final String kTemplateUrl = '''
String url = "{{url}}";
''';
final String kTemplateUrlQuery = '''
String url = "{{url}}";
public class Main {
public static void main(String[] args) {
try {
URI uri = new URI(url);
url = uri.resolve("{{params}}").toString();
} catch (URISyntaxException e) {
e.printStackTrace();
}
HttpClient client = HttpClient.newHttpClient();
''';
String kTemplateRequestBody = '''
String body = "{{body}}";
''';
final String kStringRequestStart = """
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
""";
final String kTemplateRequestEnd = """
.{{method}}({{body}})
.build();
String kTemplateUrl = """
URI uri = URI.create("{{url}}");
HttpResponse<String> response = client.send(request, BodyHandlers.ofString());
System.out.println(response.statusCode());
System.out.println(response.body());
""";
String kTemplateFormHeaderContentType = '''
multipart/form-data; boundary={{boundary}}''';
String kTemplateMethod = """
{% if method == 'get' %}
HttpRequest.Builder requestBuilder = HttpRequest.newBuilder(uri).GET();
{% elif method == 'post' %}
HttpRequest.Builder requestBuilder = HttpRequest.newBuilder(uri).POST({% if hasBody %}bodyPublisher{% else %}HttpRequest.BodyPublishers.noBody(){% endif %});
{% elif method == 'put' %}
HttpRequest.Builder requestBuilder = HttpRequest.newBuilder(uri).PUT({% if hasBody %}bodyPublisher{% else %}HttpRequest.BodyPublishers.noBody(){% endif %});
{% elif method == 'delete' %}
HttpRequest.Builder requestBuilder = HttpRequest.newBuilder(uri).method("DELETE", {% if hasBody %}bodyPublisher{% else %}HttpRequest.BodyPublishers.noBody(){% endif %});
{% elif method == 'patch' %}
HttpRequest.Builder requestBuilder = HttpRequest.newBuilder(uri).method("PATCH", {% if hasBody %}bodyPublisher{% else %}HttpRequest.BodyPublishers.noBody(){% endif %});
{% elif method == 'head' %}
HttpRequest.Builder requestBuilder = HttpRequest.newBuilder(uri).method("HEAD", HttpRequest.BodyPublishers.noBody());
{% endif %}
""";
String kTemplateRawBody = """
HttpRequest.BodyPublisher bodyPublisher = HttpRequest.BodyPublishers.ofString(\"\"\"
{{body}}\"\"\");
""";
String kTemplateJsonBody = """
HttpRequest.BodyPublisher bodyPublisher = HttpRequest.BodyPublishers.ofString(\"\"\"
{{body}}\"\"\");
""";
String kTemplateFormData = """
String boundary = "{{boundary}}";
Map<Object, Object> data = new HashMap<>();
{% for field in fields %}
{% if field.type == "file" %}data.put("{{field.name}}", Paths.get("{{field.value}}"));{% else %}data.put("{{field.name}}", "{{field.value}}");{% endif %}{% endfor %}
HttpRequest.BodyPublisher bodyPublisher = buildMultipartFormData(data, boundary);
""";
String kTemplateHeader = """
requestBuilder = requestBuilder.headers({% for header, value in headers %}
"{{header}}", "{{value}}"{% if not loop.last %},{% endif %}{% endfor %}
);
""";
final String kTemplateEnd = """
HttpResponse<String> response = client.send(requestBuilder.build(), HttpResponse.BodyHandlers.ofString());
System.out.println("Response body: " + response.body());
System.out.println("Response code: " + response.statusCode());
} catch (IOException | InterruptedException e) {
System.out.println("An error occurred: " + e.getMessage());
}
}
\n
{% if hasFormData %}
private static HttpRequest.BodyPublisher buildMultipartFormData(Map<Object, Object> data, String boundary) throws IOException {
var byteArrays = new ArrayList<byte[]>();
var CRLF = "\\r\\n".getBytes(StandardCharsets.UTF_8);
for (Map.Entry<Object, Object> entry : data.entrySet()) {
byteArrays.add(("--" + boundary + "\\r\\n").getBytes(StandardCharsets.UTF_8));
if (entry.getValue() instanceof Path) {
var file = (Path) entry.getValue();
var fileName = file.getFileName().toString();
byteArrays.add(("Content-Disposition: form-data; name=\\"" + entry.getKey() + "\\"; filename=\\"" + fileName + "\\"\\r\\n").getBytes(StandardCharsets.UTF_8));
byteArrays.add(("Content-Type: " + Files.probeContentType(file) + "\\r\\n\\r\\n").getBytes(StandardCharsets.UTF_8));
byteArrays.add(Files.readAllBytes(file));
byteArrays.add(CRLF);
} else {
byteArrays.add(("Content-Disposition: form-data; name=\\"" + entry.getKey() + "\\"\\r\\n\\r\\n").getBytes(StandardCharsets.UTF_8));
byteArrays.add(entry.getValue().toString().getBytes(StandardCharsets.UTF_8));
byteArrays.add(CRLF);
}
}
byteArrays.add(("--" + boundary + "--\\r\\n").getBytes(StandardCharsets.UTF_8));
return HttpRequest.BodyPublishers.ofByteArrays(byteArrays);
}{% endif %}
}
""";
String? getCode(
RequestModel requestModel,
) {
RequestModel requestModel, {
String? boundary,
}) {
try {
String result = "";
bool hasQuery = false;
bool hasBody = false;
bool hasJsonBody = false;
var requestBody = requestModel.requestBody;
String url = requestModel.url;
result += jj.Template(kTemplateStart).render({
"hasFormData": requestModel.hasFormData,
});
var rec = getValidRequestUri(
requestModel.url,
url,
requestModel.enabledRequestParams,
);
Uri? uri = rec.$1;
var harJson =
requestModelToHARJsonRequest(requestModel, useEnabled: true);
if (uri != null) {
String url = stripUriParams(uri);
if (uri.hasQuery) {
var params = uri.queryParameters;
if (params.isNotEmpty) {
hasQuery = true;
var templateParams = jj.Template(kTemplateUrlQuery);
result += templateParams.render({"url": url, "params": uri.query});
}
}
if (!hasQuery) {
var templateUrl = jj.Template(kTemplateUrl);
result += templateUrl.render({"url": url});
}
var rM = requestModel.copyWith(url: url);
result += templateUrl.render({"url": harJson["url"]});
var harJson = requestModelToHARJsonRequest(rM, useEnabled: true);
var method = requestModel.method;
var requestBody = requestModel.requestBody;
if (requestModel.hasFormData &&
requestModel.formDataMapList.isNotEmpty &&
kMethodsWithBody.contains(method)) {
var formDataList = requestModel.formDataMapList;
result += """
StringBuilder formData = new StringBuilder();
formData.append(""";
for (var formDataMap in formDataList) {
result += '"""${formDataMap['name']}=${formDataMap['value']}&""",';
}
result = result.substring(0, result.length - 1);
result += ");\n";
hasBody = true;
} else if (kMethodsWithBody.contains(method) && requestBody != null) {
var contentLength = utf8.encode(requestBody).length;
if (contentLength > 0) {
var templateBody = jj.Template(kTemplateRequestBody);
hasBody = true;
hasJsonBody =
requestBody.startsWith("{") && requestBody.endsWith("}");
if (harJson["postData"]?["text"] != null) {
result += templateBody.render({
"body": kEncoder.convert(harJson["postData"]["text"]).substring(
1, kEncoder.convert(harJson["postData"]["text"]).length - 1)
String? bodyPublisher = "";
if (requestModel.hasTextData) {
var templateBody = jj.Template(kTemplateRawBody);
bodyPublisher = templateBody.render({"body": requestBody});
} else if (requestModel.hasJsonData) {
var templateBody = jj.Template(kTemplateJsonBody);
bodyPublisher = templateBody.render({"body": requestBody});
} else if (requestModel.hasFormData) {
var templateFormData = jj.Template(kTemplateFormData);
bodyPublisher = templateFormData.render({
"fields": requestModel.formDataMapList,
"boundary": boundary,
});
}
}
}
result = kTemplateStart + result;
result += kStringRequestStart;
result += bodyPublisher;
var methodTemplate = jj.Template(kTemplateMethod);
result += methodTemplate.render({
"method": requestModel.method.name,
"hasBody": requestModel.hasBody,
});
var headersList = requestModel.enabledRequestHeaders;
var contentType = requestModel.requestBodyContentType.header;
if (hasBody &&
!requestModel.enabledHeadersMap.containsKey('Content-Type')) {
result =
"""$result .header("Content-Type", "$contentType")\n""";
}
if (headersList != null) {
if (headersList != null || requestModel.hasBody) {
var headers = requestModel.enabledHeadersMap;
if (requestModel.hasJsonData || requestModel.hasTextData) {
headers.putIfAbsent(kHeaderContentType,
() => requestModel.requestBodyContentType.header);
}
if (requestModel.hasFormData) {
var formDataHeader = jj.Template(kTemplateFormHeaderContentType);
headers.putIfAbsent(
kHeaderContentType,
() => formDataHeader.render({
"boundary": boundary,
}));
}
if (headers.isNotEmpty) {
result += getHeaders(headers, hasJsonBody);
}
}
var templateRequestEnd = jj.Template(kTemplateRequestEnd);
if (kMethodsWithBody.contains(method)) {
result += templateRequestEnd.render({
"method": method.name.toUpperCase(),
"body": hasBody
? "BodyPublishers.ofString(body)"
: "BodyPublishers.noBody()"
var templateHeader = jj.Template(kTemplateHeader);
result += templateHeader.render({
"headers": headers,
});
} else {
result += templateRequestEnd
.render({"method": method.name.toUpperCase(), "body": ""});
}
}
return result;
var templateEnd = jj.Template(kTemplateEnd);
result += templateEnd.render({
"hasFormData": requestModel.hasFormData,
"boundary": boundary,
});
}
return result.replaceAll(RegExp('\\n\\n+'), '\n\n');
} catch (e) {
return null;
}
}
String getHeaders(Map<String, String> headers, hasJsonBody) {
String result = "";
for (final k in headers.keys) {
if (k.toLowerCase() == 'authorization') {
result = """$result .header("$k", "${headers[k]}")\n""";
} else {
result = """$result .header("$k", "${headers[k]}")\n""";
}
}
return result;
}
}

View File

@ -90,7 +90,7 @@ axios(config)
m[i["name"]] = i["value"];
}
if (requestModel.hasFormData) {
m[kHeaderContentType] = 'multipart/form-data';
m[kHeaderContentType] = ContentType.formdata.header;
}
result += templateHeader
.render({"headers": padMultilineString(kEncoder.convert(m), 2)});

View File

@ -99,7 +99,7 @@ fetch(url, options)
var m = {};
for (var i in headers) {
// fetch can automatically add the Content-Type header when FormData is passed as body
if (i["name"] == "Content-Type" && requestModel.hasFormData) {
if (i["name"] == kHeaderContentType && requestModel.hasFormData) {
continue;
}
m[i["name"]] = i["value"];

View File

@ -1,98 +1,73 @@
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 getNewUuid, getValidRequestUri, padMultilineString, stripUriParams;
show getValidRequestUri, stripUriParams;
import 'package:apidash/models/models.dart' show RequestModel;
class JuliaHttpClientCodeGen {
final String kTemplateStart = """using HTTP,JSON
final String kTemplateStart = """
using HTTP{% if hasJson %}, JSON{% endif %}
\n
""";
final String kTemplateUrl = """
url = "{{url}}"
\n
""";
String kTemplateParams = """
{% set new_params = params | replace(":", "=>") | replace("{", "(") | replace("}", ")") %}
params = Dict{{new_params}}
""";
int kParamsPadding = 9;
String kTemplateBody = '''
{% set new_params = body | replace(":", "=>") | replace("{", "(") | replace("}", ")") %}
payload = Dict{{new_params}}
''';
String kTemplateJson = """
{% set new_params = body | replace(":", "=>") | replace("{", "(") | replace("}", ")") %}
payload = Dict{{new_params}}
params = Dict(
{%- for name, value in params %}
"{{ name }}" => "{{ value }}",
{%- endfor %}
)
\n
""";
String kTemplateHeaders = """
{% set new_params = headers | replace(":", "=>") | replace("{", "(") | replace("}", ")") %}
headers = Dict{{new_params}}
headers = Dict(
{%- for name, value in headers %}
"{{ name }}" => "{{ value }}",
{%- endfor %}
)
\n
""";
String kTemplateFormHeaderContentType = '''
multipart/form-data; boundary={{boundary}}''';
final String kTemplateFormDataBody = '''
data = Dict(
{%- for data in formdata %}
{%- if data.type == "text" %}
"{{ data.name }}" => "{{ data.value }}",
{%- else %}
"{{ data.name }}" => open("{{ data.value }}"),
{%- endif %}
{%- endfor %}
)
int kHeadersPadding = 10;
payload = HTTP.Form(data)
\n
''';
String kTemplateBody = '''
payload = """{{ body }}"""
\n
''';
String kTemplateRequest = """
response = HTTP.{{method}}(url
response = HTTP.request("{{ method | upper }}", url
""";
final String kStringFormDataBody = r'''
function build_data_list(fields)
dataList = []
for field in fields
name = field["name"]
value = field["value"]
type_ = get(field, "type", "text")
push!(dataList, b"--{{boundary}}")
if type_ == "text"
push!(dataList, b"Content-Disposition: form-data; name=\"$name\"")
push!(dataList, b"Content-Type: text/plain")
push!(dataList, b"")
push!(dataList, codeunits(value))
elseif type_ == "file"
push!(dataList, b"Content-Disposition: form-data; name=\"$name\"; filename=\"$value\"")
push!(dataList, b"Content-Type: $value")
push!(dataList, b"")
push!(dataList, String(read(value)))
end
end
push!(dataList, "--{{boundary}}--")
push!(dataList, b"")
return dataList
end
dataList = build_data_list({{fields_list}})
payload = join(dataList, b"\r\n")
''';
String kStringRequestParams = """, query=params""";
String kStringRequestBody = """, payload=payload""";
String kStringRequestJson = """, JSON.json(payload)""";
String kStringRequestBody = """, body=payload""";
String kStringRequestHeaders = """, headers=headers""";
final String kStringRequestEnd = """
)
final String kStringRequestEnd = r"""
, status_exception=false)
println("Status Code:", response.status)
println("Response Body:", String(response.body))
println("Status Code: $(response.status) $(HTTP.StatusCodes.statustext(response.status))")
println("Response Body: \n$(String(response.body))")
""";
String? getCode(RequestModel requestModel) {
@ -100,9 +75,7 @@ println("Response Body:", String(response.body))
String result = "";
bool hasQuery = false;
bool hasHeaders = false;
bool hasBody = false;
bool hasJsonBody = false;
String uuid = getNewUuid();
bool addHeaderForBody = false;
var rec = getValidRequestUri(
requestModel.url,
@ -110,86 +83,80 @@ println("Response Body:", String(response.body))
);
Uri? uri = rec.$1;
if (uri != null) {
var templateStartUrl = jj.Template(kTemplateStart);
result += templateStartUrl.render({
"url": stripUriParams(uri),
final templateStart = jj.Template(kTemplateStart);
result += templateStart.render({
// "hasJson": requestModel.hasBody && requestModel.hasJsonContentType && requestModel.hasJsonData,
"hasJson":
false, // we manually send false because we do not require JSON package
});
final templateUrl = jj.Template(kTemplateUrl);
result += templateUrl.render({"url": stripUriParams(uri)});
if (uri.hasQuery) {
var params = uri.queryParameters;
if (params.isNotEmpty) {
hasQuery = true;
var templateParams = jj.Template(kTemplateParams);
var paramsString = kEncoder.convert(params);
paramsString = padMultilineString(paramsString, kParamsPadding);
result += templateParams.render({"params": paramsString});
final templateParams = jj.Template(kTemplateParams);
result += templateParams.render({"params": params});
}
}
if (requestModel.hasJsonData) {
hasJsonBody = true;
var templateBody = jj.Template(kTemplateJson);
result += templateBody.render({"body": requestModel.requestBody});
} else if (requestModel.hasTextData) {
hasBody = true;
var templateBody = jj.Template(kTemplateBody);
result += templateBody.render({"body": requestModel.requestBody});
if (requestModel.hasJsonData || requestModel.hasTextData) {
addHeaderForBody = true;
final templateBody = jj.Template(kTemplateBody);
var bodyStr = requestModel.requestBody;
result += templateBody.render({"body": bodyStr});
}
var headersList = requestModel.enabledRequestHeaders;
if (headersList != null || hasBody) {
var headers = requestModel.enabledHeadersMap;
if (requestModel.hasFormData) {
var formHeaderTemplate =
jj.Template(kTemplateFormHeaderContentType);
headers[HttpHeaders.contentTypeHeader] = formHeaderTemplate.render({
"boundary": uuid,
});
}
if (headers.isNotEmpty || hasBody) {
hasHeaders = true;
if (hasBody) {
headers[HttpHeaders.contentTypeHeader] =
requestModel.requestBodyContentType.header;
}
var headersString = kEncoder.convert(headers);
headersString = padMultilineString(headersString, kHeadersPadding);
var templateHeaders = jj.Template(kTemplateHeaders);
result += templateHeaders.render({"headers": headersString});
}
}
if (requestModel.hasFormData) {
var formDataBodyData = jj.Template(kStringFormDataBody);
final formDataBodyData = jj.Template(kTemplateFormDataBody);
result += formDataBodyData.render(
{
"fields_list": json.encode(requestModel.formDataMapList),
"boundary": uuid,
"hasFile": requestModel.hasFileInFormData,
"formdata": requestModel.formDataMapList,
},
);
}
var headersList = requestModel.enabledRequestHeaders;
if (headersList != null || addHeaderForBody) {
var headers = requestModel.enabledHeadersMap;
if (!requestModel.hasContentTypeHeader) {
if (addHeaderForBody) {
headers[HttpHeaders.contentTypeHeader] =
requestModel.requestBodyContentType.header;
}
}
if (headers.isNotEmpty) {
hasHeaders = true;
var templateHeaders = jj.Template(kTemplateHeaders);
result += templateHeaders.render({"headers": headers});
}
}
var templateRequest = jj.Template(kTemplateRequest);
result += templateRequest.render({
"method": requestModel.method.name.toLowerCase(),
"method": requestModel.method.name,
});
if (hasHeaders) {
result += kStringRequestHeaders;
}
if (requestModel.hasBody) {
result += kStringRequestBody;
}
if (hasQuery) {
result += kStringRequestParams;
}
if (hasBody || requestModel.hasFormData) {
result += kStringRequestBody;
}
if (hasJsonBody || requestModel.hasFormData) {
result += kStringRequestJson;
}
if (hasHeaders || requestModel.hasFormData) {
result += kStringRequestHeaders;
}
result += kStringRequestEnd;
}
return result;
} catch (e) {
return null;

342
lib/codegen/php/curl.dart Normal file
View File

@ -0,0 +1,342 @@
import 'dart:convert';
import 'package:jinja/jinja.dart' as jj;
import 'package:apidash/utils/utils.dart'
show getNewUuid, getValidRequestUri, requestModelToHARJsonRequest;
import 'package:apidash/models/models.dart' show RequestModel;
import 'package:apidash/consts.dart';
class PHPcURLCodeGen {
final String kTemplateStart = """
<?php
""";
final String kTemplateUri = """
\$uri = "{{uri}}";
""";
//defining query parameters
String kTemplateParams = """
\$queryParams = [{{params}}];
\$queryString = "?" . http_build_query(\$queryParams);
if (count(\$queryParams) > 0) {
\$uri .= \$queryString;
}
""";
//initialising the request
String kTemplateRequestInit = """
\$request = curl_init(\$uri);
""";
String kTemplateBody = """
\$request_body = <<<EOF
{{body}}
EOF;
""";
//specifying headers
String kTemplateHeaders = """
\$headers = [{{headers}}];
curl_setopt(\$request, CURLOPT_HTTPHEADER, \$headers);
""";
String kTemplateFormHeaderContentType = '''
multipart/form-data; boundary={{boundary}}''';
String kTemplateRequest = """
curl_setopt(\$request, CURLOPT_RETURNTRANSFER, 1);
curl_setopt(\$request, {{method}}, 1);
""";
//passing the request body
String kStringRequestBody = """
curl_setopt(\$request, CURLOPT_POSTFIELDS, \$request_body);
""";
//ending template
final String kStringRequestEnd = """
\$response = curl_exec(\$request);
curl_close(\$request);
var_dump(\$response);
""";
//template for generating unique boundary
String kBoundaryUniqueIdTemplate = """
\$boundary = "{{uuid}}";
""";
//
String kFileClassString = """
class File
{
public string \$name;
public string \$filename;
public string \$content;
function __construct(\$name, \$filename)
{
\$this->name = \$name;
\$this->filename = \$filename;
\$available_content = file_get_contents(\$this->filename);
\$this->content = \$available_content ? \$available_content : "";
}
}
""";
//function to build formdata without 'file' type
String kBuildFormDataFunctionWithoutFilesString = """
function build_data(\$boundary, \$fields)
{
\$data = '';
\$eol = "\\r\\n";
\$delimiter = '-------------' . \$boundary;
foreach (\$fields as \$name => \$content) {
\$data .= "--" . \$delimiter . \$eol
. 'Content-Disposition: form-data; name="' . \$name . "\\"" . \$eol . \$eol
. \$content . \$eol;
}
\$data .= "--" . \$delimiter . "--" . \$eol;
return \$data;
}
""";
//function to build formdata with 'file' type
String kBuildFormDataFunctionWithFilesString = """
function build_data_files(\$boundary, \$fields, \$files)
{
\$data = '';
\$eol = "\\r\\n";
\$delimiter = '-------------' . \$boundary;
foreach (\$fields as \$name => \$content) {
\$data .= "--" . \$delimiter . \$eol
. 'Content-Disposition: form-data; name="' . \$name . "\\"" . \$eol . \$eol
. \$content . \$eol;
}
foreach (\$files as \$uploaded_file) {
if (\$uploaded_file instanceof File) {
\$data .= "--" . \$delimiter . \$eol
. 'Content-Disposition: form-data; name="' . \$uploaded_file->name . '"; filename="' . \$uploaded_file->filename . '"' . \$eol
. 'Content-Transfer-Encoding: binary' . \$eol;
\$data .= \$eol;
\$data .= \$uploaded_file->content . \$eol;
}
}
\$data .= "--" . \$delimiter . "--" . \$eol;
return \$data;
}
""";
//
String kMultiPartBodyWithFiles = """
\$request_body = build_data_files(\$boundary, \$fields, \$files);
""";
//
String kMultiPartBodyWithoutFiles = """
\$request_body = build_data(\$boundary, \$fields);
""";
String? getCode(RequestModel requestModel) {
String uuid = getNewUuid();
uuid = uuid.replaceAll(RegExp(r'-'), "");
try {
String result = "";
bool hasQuery = false;
bool hasBody = false;
var rec = getValidRequestUri(
requestModel.url,
requestModel.enabledRequestParams,
);
Uri? uri = rec.$1;
//renders starting template
if (uri != null) {
var templateStart = jj.Template(kTemplateStart);
result += templateStart.render();
// if the request does not contain any file uploads, we do not need
// to add the class for File in the request
if (requestModel.hasFileInFormData) {
result += kFileClassString;
}
//adds the function to build formdata with or without 'file' type
if (requestModel.hasFormData) {
result += requestModel.hasFileInFormData
? kBuildFormDataFunctionWithFilesString
: kBuildFormDataFunctionWithoutFilesString;
}
var templateUri = jj.Template(kTemplateUri);
result += templateUri.render({"uri": requestModel.url});
//checking and adding query params
if (uri.hasQuery) {
var params = uri.queryParameters;
if (params.isNotEmpty) {
hasQuery = true;
var templateParams = jj.Template(kTemplateParams);
// generating the map of key and value for the query parameters
List<String> queryList = [];
for (MapEntry<String, String> entry in params.entries) {
String entryStr = "\"${entry.key}\" => \"${entry.value}\"";
queryList.add(entryStr);
}
String paramsString = "\n ${queryList.join(",\n ")}\n";
result += templateParams.render({"params": paramsString});
}
}
// renders the initial request init function call
var templateRequestInit = jj.Template(kTemplateRequestInit);
result += templateRequestInit.render();
var harJson =
requestModelToHARJsonRequest(requestModel, useEnabled: true);
var headers = harJson["headers"];
//parses and adds the headers
if (headers.isNotEmpty || requestModel.hasFormData) {
var templateHeader = jj.Template(kTemplateHeaders);
var m = {};
for (var i in headers) {
m[i["name"]] = i["value"];
}
if (requestModel.hasFormData) {
// we will override any existing boundary and use our own boundary
m[kHeaderContentType] =
"${ContentType.formdata.header}; boundary=-------------$uuid";
var boundaryUniqueIdTemplate =
jj.Template(kBoundaryUniqueIdTemplate);
result += boundaryUniqueIdTemplate.render({"uuid": uuid});
var fieldsString = '\$fields = [\n';
var filesString = '\$files = [\n';
for (var formData in requestModel.formDataMapList) {
if (formData['type'] == 'text') {
// the four spaces on the left hand side are for indentation, hence do not remove
fieldsString +=
' "${formData['name']}" => "${formData['value']}",\n';
} else if (formData['type'] == 'file') {
filesString +=
' new File("${formData['name']}", "${formData['value']}"),\n';
}
}
fieldsString += '];\n';
filesString += '];\n';
result += fieldsString;
if (requestModel.hasFileInFormData) {
result += filesString;
result += kMultiPartBodyWithFiles;
} else {
result += kMultiPartBodyWithoutFiles;
}
}
var headersString = '\n';
m.forEach((key, value) {
headersString += "\t\t\t\t'$key: $value', \n";
});
result += templateHeader.render({
"headers": headersString,
});
}
// contains the HTTP method associated with the request
var method = requestModel.method;
// contains the entire request body as a string if body is present
var requestBody = requestModel.requestBody;
//renders the request body
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});
}
}
//renders the request temlate
var templateRequest = jj.Template(kTemplateRequest);
result += templateRequest.render({
"authority": uri.authority,
"method": httpMethod(method.name.toUpperCase()),
"path": uri.path,
"queryParamsStr": hasQuery ? "queryParamsStr" : "",
});
if (hasBody || requestModel.hasFormData) {
result += kStringRequestBody;
}
//and of the request
result += kStringRequestEnd;
}
return result;
} catch (e) {
return null;
}
}
//function for http verb to curl mapping
String httpMethod(String methodName) {
switch (methodName) {
case "POST":
return "CURLOPT_POST";
case "GET":
return "CURLOPT_HTTPGET";
case "PUT":
return "CURLOPT_PUT";
case "DELETE":
return "CURLOPT_CUSTOMREQUEST";
case "PATCH":
return "CURLOPT_CUSTOMREQUEST";
case "HEAD":
return "CURLOPT_NOBODY";
default:
return "";
}
}
}

View File

@ -107,7 +107,7 @@ echo \$res->getBody();
headersString += "\t\t\t\t'$key' => '$value', \n";
});
if (requestModel.hasFormData) {
m['Content-Type'] = 'multipart/form-data';
m[kHeaderContentType] = ContentType.formdata.header;
}
headersString = headersString.substring(
0, headersString.length - 2); // Removing trailing comma and space

View File

@ -0,0 +1,178 @@
import 'package:apidash/consts.dart';
import 'package:jinja/jinja.dart' as jj;
import 'package:apidash/utils/utils.dart' show getValidRequestUri;
import 'package:apidash/utils/http_utils.dart' show stripUriParams;
import 'package:apidash/models/models.dart' show RequestModel;
// Note that delete is a special case in Faraday as API Dash supports request
// body inside delete reqest, but Faraday does not. Hence we need to manually
// setup request body for delete request and add that to request.
//
// Refer https://lostisland.github.io/faraday/#/getting-started/quick-start?id=get-head-delete-trace
class RubyFaradayCodeGen {
final String kStringFaradayRequireStatement = """
require 'uri'
require 'faraday'
""";
final String kStringFaradayMultipartRequireStatement = '''
require 'faraday/multipart'
''';
final String kTemplateRequestUrl = """
REQUEST_URL = URI("{{ url }}")
""";
final String kTemplateBody = """
PAYLOAD = <<HEREDOC
{{ body }}
HEREDOC
""";
final String kTemplateFormParamsWithFile = """
PAYLOAD = {
{% for param in params %}{% if param.type == "text" %} "{{ param.name }}" => Faraday::Multipart::ParamPart.new("{{ param.value }}", "text/plain"),
{% elif param.type == "file" %} "{{ param.name }}" => Faraday::Multipart::FilePart.new("{{ param.value }}", "application/octet-stream"),{% endif %}{% endfor %}
}
""";
final String kTemplateFormParamsWithoutFile = """
PAYLOAD = URI.encode_www_form({\n{% for param in params %} "{{ param.name }}" => "{{ param.value }}",\n{% endfor %}})\n\n
""";
final String kTemplateConnection = """
conn = Faraday.new do |faraday|
faraday.adapter Faraday.default_adapter{% if hasFile %}\n faraday.request :multipart{% endif %}
end
""";
final String kTemplateRequestStart = """
response = conn.{{ method|lower }}(REQUEST_URL{% if doesMethodAcceptBody and containsBody %}, PAYLOAD{% endif %}) do |req|
""";
final String kTemplateRequestParams = """
req.params = {
{% for key, val in params %} "{{ key }}" => "{{ val }}",\n{% endfor %} }
""";
final String kTemplateRequestHeaders = """
req.headers = {
{% for key, val in headers %} "{{ key }}" => "{{ val }}",\n{% endfor %} }
""";
final String kStringDeleteRequestBody = """
req.body = PAYLOAD
""";
final String kStringRequestEnd = """
end
""";
final String kStringResponse = """
puts "Status Code: #{response.status}"
puts "Response Body: #{response.body}"
""";
String? getCode(
RequestModel requestModel,
) {
try {
String result = "";
var rec = getValidRequestUri(
requestModel.url,
requestModel.enabledRequestParams,
);
Uri? uri = rec.$1;
if (uri == null) {
return "";
}
var url = stripUriParams(uri);
result += kStringFaradayRequireStatement;
if (requestModel.hasFormDataContentType && requestModel.hasFileInFormData) {
result += kStringFaradayMultipartRequireStatement;
}
var templateRequestUrl = jj.Template(kTemplateRequestUrl);
result += templateRequestUrl.render({"url": url});
if (requestModel.hasFormData) {
jj.Template payload;
if (requestModel.hasFileInFormData) {
payload = jj.Template(kTemplateFormParamsWithFile);
} else {
payload = jj.Template(kTemplateFormParamsWithoutFile);
}
result += payload.render({"params": requestModel.formDataMapList});
} else if (requestModel.hasJsonData || requestModel.hasTextData) {
var templateBody = jj.Template(kTemplateBody);
result += templateBody.render({
"body": requestModel.requestBody,
});
}
// crreating faraday connection for request
var templateConnection = jj.Template(kTemplateConnection);
result += templateConnection.render({
"hasFile": requestModel.hasFormDataContentType && requestModel.hasFileInFormData //
});
// start of the request sending
var templateRequestStart = jj.Template(kTemplateRequestStart);
result += templateRequestStart.render({
"method": requestModel.method.name,
"doesMethodAcceptBody":
kMethodsWithBody.contains(requestModel.method) && requestModel.method != HTTPVerb.delete, //
"containsBody": requestModel.hasBody,
});
var headers = requestModel.enabledHeadersMap;
if (requestModel.hasBody && !requestModel.hasContentTypeHeader) {
if (requestModel.hasJsonData || requestModel.hasTextData) {
headers[kHeaderContentType] = requestModel.requestBodyContentType.header;
}
}
if (headers.isNotEmpty) {
var templateRequestHeaders = jj.Template(kTemplateRequestHeaders);
result += templateRequestHeaders.render({"headers": headers});
}
if (uri.hasQuery) {
var params = uri.queryParameters;
if (params.isNotEmpty) {
var templateRequestParams = jj.Template(kTemplateRequestParams);
result += templateRequestParams.render({"params": params});
}
}
if (requestModel.hasBody && requestModel.method == HTTPVerb.delete) {
result += kStringDeleteRequestBody;
}
result += kStringRequestEnd;
result += kStringResponse;
return result;
} catch (e) {
return null;
}
}
}

View File

@ -3,7 +3,6 @@ import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:davi/davi.dart';
const kDiscordUrl = "https://bit.ly/heyfoss";
const kGitUrl = "https://github.com/foss42/apidash";
@ -21,7 +20,7 @@ final kIsLinux = !kIsWeb && Platform.isLinux;
final kIsApple = !kIsWeb && (Platform.isIOS || Platform.isMacOS);
final kIsDesktop =
!kIsWeb && (Platform.isMacOS || Platform.isWindows || Platform.isLinux);
final kIsRunningTests = Platform.environment.containsKey('FLUTTER_TEST');
final kIsIOS = !kIsWeb && Platform.isIOS;
final kIsAndroid = !kIsWeb && Platform.isAndroid;
final kIsMobile = !kIsWeb && (Platform.isIOS || Platform.isAndroid);
@ -56,6 +55,7 @@ const kForegroundOpacity = 0.05;
const kOverlayBackgroundOpacity = 0.5;
const kTextStyleButton = TextStyle(fontWeight: FontWeight.bold);
const kTextStyleTab = TextStyle(fontSize: 14);
const kTextStyleButtonSmall = TextStyle(fontSize: 12);
const kFormDataButtonLabelTextStyle = TextStyle(
fontSize: 12,
@ -71,12 +71,18 @@ const kP5 = EdgeInsets.all(5);
const kP8 = EdgeInsets.all(8);
const kPs8 = EdgeInsets.only(left: 8);
const kPs2 = EdgeInsets.only(left: 2);
const kPe8 = EdgeInsets.only(right: 8.0);
const kPh20v5 = EdgeInsets.symmetric(horizontal: 20, vertical: 5);
const kPh20v10 = EdgeInsets.symmetric(horizontal: 20, vertical: 10);
const kP10 = EdgeInsets.all(10);
const kPv8 = EdgeInsets.symmetric(vertical: 8);
const kPv2 = EdgeInsets.symmetric(vertical: 2);
const kPh2 = EdgeInsets.symmetric(horizontal: 2);
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 kPh4 = EdgeInsets.symmetric(horizontal: 4);
const kPh8 = EdgeInsets.symmetric(horizontal: 8);
const kPh20 = EdgeInsets.symmetric(
horizontal: 20,
);
@ -105,8 +111,6 @@ const kPb10 = EdgeInsets.only(
const kPb15 = EdgeInsets.only(
bottom: 15,
);
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);
@ -118,26 +122,18 @@ const kVSpacer20 = SizedBox(height: 20);
const kVSpacer40 = SizedBox(height: 40);
const kTabAnimationDuration = Duration(milliseconds: 200);
const kTabHeight = 45.0;
const kTabHeight = 32.0;
const kHeaderHeight = 32.0;
const kSegmentHeight = 24.0;
const kTextButtonMinWidth = 44.0;
const kRandMax = 100000;
const kTableThemeData = DaviThemeData(
columnDividerThickness: 1,
columnDividerColor: kColorTransparent,
row: RowThemeData(
dividerColor: kColorTransparent,
),
decoration: BoxDecoration(
border: Border(),
),
header: HeaderThemeData(
visible: false,
),
const kDataTableScrollbarTheme = ScrollbarThemeData(
crossAxisMargin: -4,
);
const kDataTableBottomPadding = 12.0;
const kDataTableRowHeight = 36.0;
const kIconRemoveDark = Icon(
Icons.remove_circle,
@ -276,6 +272,7 @@ const kDefaultContentType = ContentType.json;
enum CodegenLanguage {
curl("cURL", "bash", "curl"),
har("HAR", "json", "har"),
cSharpRestSharp("C# (Rest Sharp)", "cs", "cs"),
dartHttp("Dart (http)", "dart", "dart"),
dartDio("Dart (dio)", "dart", "dart"),
goHttp("Go (http)", "go", "go"),
@ -286,6 +283,8 @@ enum CodegenLanguage {
kotlinOkHttp("Kotlin (okhttp3)", "java", "kt"),
pythonRequests("Python (requests)", "python", "py"),
pythonHttpClient("Python (http.client)", "python", "py"),
rubyFaraday("Ruby (Faraday)", "ruby", "rb"),
rubyNetHttp("Ruby (Net::Http)", "Ruby", "rb"),
rustActix("Rust (Actix Client)", "rust", "rs"),
rustReqwest("Rust (reqwest)", "rust", "rs"),
rustCurl("Rust (curl-rust)", "rust", "rs"),
@ -295,7 +294,7 @@ enum CodegenLanguage {
javaHttpClient("Java (HttpClient)", "java", "java"),
juliaHttp("Julia (HTTP)", "julia", "jl"),
phpGuzzle("PHP (guzzle)", "php", "php"),
rubyNetHttp("Ruby (Net::Http)", "Ruby", "rb");
phpCurl("PHP (curl)", "php", "php");
const CodegenLanguage(this.label, this.codeHighlightLang, this.ext);
final String label;
@ -401,7 +400,7 @@ const Map<String, Map<String, List<ResponseBodyView>>>
kSubTypeDefaultViewOptions: kPreviewBodyViewOptions,
},
kTypeVideo: {
kSubTypeDefaultViewOptions: kNoBodyViewOptions,
kSubTypeDefaultViewOptions: kPreviewBodyViewOptions,
},
kTypeText: {
kSubTypeDefaultViewOptions: kRawBodyViewOptions,
@ -510,6 +509,9 @@ const kMimeTypeRaiseIssue =
const kUnexpectedRaiseIssue =
"\nIf the behaviour is unexpected, please raise an issue in API Dash GitHub repo so that we can resolve it.";
const kVideoError =
"There seems to be an issue playing this video. Please raise an issue in API Dash GitHub repo so that we can resolve it.";
const kImageError =
"There seems to be an issue rendering this image. Please raise an issue in API Dash GitHub repo so that we can resolve it.";
@ -538,3 +540,36 @@ const kLabelSave = "Save";
const kLabelDownload = "Download";
const kLabelSaving = "Saving";
const kLabelSaved = "Saved";
// Request Pane
const kLabelRequest = "Request";
const kLabelHideCode = "Hide Code";
const kLabelViewCode = "View Code";
const kLabelURLParams = "URL Params";
const kLabelHeaders = "Headers";
const kLabelBody = "Body";
const kNameCheckbox = "Checkbox";
const kNameURLParam = "URL Parameter";
const kNameHeader = "Header Name";
const kNameValue = "Value";
const kNameField = "Field";
const kHintAddURLParam = "Add URL Parameter";
const kHintAddValue = "Add Value";
const kHintAddName = "Add Name";
const kHintAddFieldName = "Add Field Name";
const kLabelAddParam = "Add Param";
const kLabelAddHeader = "Add Header";
const kLabelSelectFile = "Select File";
const kLabelAddFormField = "Add Form Field";
// Response Pane
const kLabelNotSent = "Not Sent";
const kLabelResponse = "Response";
const kLabelResponseBody = "Response Body";
const kTooltipClearResponse = "Clear Response";
const kHeaderRow = ["Header Name", "Header Value"];
const kLabelRequestHeaders = "Request Headers";
const kLabelResponseHeaders = "Response Headers";
const kLabelItems = "items";
const kMsgError = "Error: Response data does not exist.";
const kMsgNullBody = "Response body is missing (null).";
const kMsgNoContent = "No content";
const kMsgUnknowContentType = "Unknown Response Content-Type";

View File

@ -0,0 +1 @@
export 'string_extensions.dart';

View File

@ -0,0 +1,5 @@
extension StringExtension on String {
String capitalize() {
return "${this[0].toUpperCase()}${substring(1).toLowerCase()}";
}
}

View File

@ -31,6 +31,7 @@ class RequestModel {
this.message,
this.responseModel,
this.isWorking = false,
this.sendingTime,
});
final String id;
@ -50,6 +51,7 @@ class RequestModel {
final String? message;
final ResponseModel? responseModel;
final bool isWorking;
final DateTime? sendingTime;
List<NameValueModel>? get enabledRequestHeaders =>
getEnabledRows(requestHeaders, isHeaderEnabledList);
@ -95,6 +97,7 @@ class RequestModel {
RequestModel duplicate({
required String id,
String? name,
int? requestTabIndex,
}) {
return RequestModel(
id: id,
@ -102,6 +105,7 @@ class RequestModel {
url: url,
name: name ?? "${this.name} (copy)",
description: description,
requestTabIndex: requestTabIndex ?? 0,
requestHeaders: requestHeaders != null ? [...requestHeaders!] : null,
requestParams: requestParams != null ? [...requestParams!] : null,
isHeaderEnabledList:
@ -133,6 +137,7 @@ class RequestModel {
String? message,
ResponseModel? responseModel,
bool? isWorking,
DateTime? sendingTime,
}) {
var headers = requestHeaders ?? this.requestHeaders;
var params = requestParams ?? this.requestParams;
@ -158,6 +163,7 @@ class RequestModel {
message: message ?? this.message,
responseModel: responseModel ?? this.responseModel,
isWorking: isWorking ?? this.isWorking,
sendingTime: sendingTime ?? this.sendingTime,
);
}

View File

@ -1,11 +1,12 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'settings_providers.dart';
import 'ui_providers.dart';
import 'package:http/http.dart' as http;
import '../consts.dart';
import '../models/models.dart';
import '../services/services.dart' show hiveHandler, HiveHandler, request;
import '../utils/utils.dart' show getNewUuid, collectionToHAR;
import '../consts.dart';
import 'package:http/http.dart' as http;
import 'settings_providers.dart';
import 'ui_providers.dart';
final selectedIdStateProvider = StateProvider<String?>((ref) => null);
@ -65,6 +66,7 @@ class CollectionStateNotifier
.read(requestSequenceProvider.notifier)
.update((state) => [id, ...state]);
ref.read(selectedIdStateProvider.notifier).state = newRequestModel.id;
ref.read(hasUnsavedChangesProvider.notifier).state = true;
}
void reorder(int oldIdx, int newIdx) {
@ -72,6 +74,7 @@ class CollectionStateNotifier
final itemId = itemIds.removeAt(oldIdx);
itemIds.insert(newIdx, itemId);
ref.read(requestSequenceProvider.notifier).state = [...itemIds];
ref.read(hasUnsavedChangesProvider.notifier).state = true;
}
void remove(String id) {
@ -94,6 +97,7 @@ class CollectionStateNotifier
var map = {...state!};
map.remove(id);
state = map;
ref.read(hasUnsavedChangesProvider.notifier).state = true;
}
void clearResponse(String? id) {
@ -102,10 +106,12 @@ class CollectionStateNotifier
final newModel = currentModel.duplicate(
id: id,
name: currentModel.name,
requestTabIndex: currentModel.requestTabIndex,
);
var map = {...state!};
map[id] = newModel;
state = map;
ref.read(hasUnsavedChangesProvider.notifier).state = true;
}
void duplicate(String id) {
@ -125,6 +131,7 @@ class CollectionStateNotifier
ref.read(requestSequenceProvider.notifier).state = [...itemIds];
ref.read(selectedIdStateProvider.notifier).state = newId;
ref.read(hasUnsavedChangesProvider.notifier).state = true;
}
void update(
@ -166,6 +173,7 @@ class CollectionStateNotifier
var map = {...state!};
map[id] = newModel;
state = map;
ref.read(hasUnsavedChangesProvider.notifier).state = true;
}
Future<void> sendRequest(String id) async {
@ -180,7 +188,10 @@ class CollectionStateNotifier
// set current model's isWorking to true and update state
var map = {...state!};
map[id] = requestModel.copyWith(isWorking: true);
map[id] = requestModel.copyWith(
isWorking: true,
sendingTime: DateTime.now(),
);
state = map;
(http.Response?, Duration?, String?)? responseRec = await request(
@ -212,6 +223,7 @@ class CollectionStateNotifier
map = {...state!};
map[id] = newRequestModel;
state = map;
ref.read(hasUnsavedChangesProvider.notifier).state = true;
}
Future<void> clearData() async {
@ -221,6 +233,7 @@ class CollectionStateNotifier
ref.read(clearDataStateProvider.notifier).state = false;
ref.read(requestSequenceProvider.notifier).state = [];
state = {};
ref.read(hasUnsavedChangesProvider.notifier).state = true;
}
bool loadData() {
@ -261,6 +274,7 @@ class CollectionStateNotifier
}
await hiveHandler.removeUnused();
ref.read(saveDataStateProvider.notifier).state = false;
ref.read(hasUnsavedChangesProvider.notifier).state = false;
}
Future<Map<String, dynamic>> exportDataToHAR() async {

View File

@ -6,6 +6,8 @@ final selectedIdEditStateProvider = StateProvider<String?>((ref) => null);
final codePaneVisibleStateProvider = StateProvider<bool>((ref) => false);
final saveDataStateProvider = StateProvider<bool>((ref) => false);
final clearDataStateProvider = StateProvider<bool>((ref) => false);
final hasUnsavedChangesProvider = StateProvider<bool>((ref) => false);
// final nameTextFieldControllerProvider =
// StateProvider.autoDispose<TextEditingController>((ref) {
// TextEditingController controller = TextEditingController(text: "");
@ -23,3 +25,5 @@ final nameTextFieldFocusNodeProvider =
});
return focusNode;
});
final searchQueryProvider = StateProvider<String>((ref) => '');

View File

@ -15,6 +15,7 @@ class CollectionPane extends ConsumerWidget {
final overlayWidget = OverlayWidgetTemplate(context: context);
final collection = ref.watch(collectionStateNotifierProvider);
final savingData = ref.watch(saveDataStateProvider);
final hasUnsavedChanges = ref.watch(hasUnsavedChangesProvider);
if (collection == null) {
return const Center(
child: CircularProgressIndicator(),
@ -26,12 +27,12 @@ class CollectionPane extends ConsumerWidget {
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Padding(
padding: kPr8CollectionPane,
padding: kPe8,
child: Wrap(
alignment: WrapAlignment.spaceBetween,
children: [
TextButton.icon(
onPressed: savingData
onPressed: (savingData || !hasUnsavedChanges)
? null
: () async {
overlayWidget.show(
@ -69,7 +70,39 @@ class CollectionPane extends ConsumerWidget {
],
),
),
kVSpacer8,
kVSpacer10,
Container(
height: 30,
margin: const EdgeInsets.only(right: 8),
decoration: BoxDecoration(
borderRadius: kBorderRadius8,
border: Border.all(
color: Theme.of(context).colorScheme.surfaceVariant,
),
),
child: Row(
children: [
kHSpacer5,
Icon(
Icons.filter_alt,
size: 18,
color: Theme.of(context).colorScheme.secondary,
),
kHSpacer5,
Expanded(
child: RawTextField(
style: Theme.of(context).textTheme.bodyMedium,
hintText: "Filter by name or URL",
onChanged: (value) {
ref.read(searchQueryProvider.notifier).state =
value.toLowerCase();
},
),
),
],
),
),
kVSpacer10,
const Expanded(
child: RequestList(),
),
@ -109,13 +142,15 @@ class _RequestListState extends ConsumerState<RequestList> {
final requestItems = ref.watch(collectionStateNotifierProvider)!;
final alwaysShowCollectionPaneScrollbar = ref.watch(settingsProvider
.select((value) => value.alwaysShowCollectionPaneScrollbar));
final filterQuery = ref.watch(searchQueryProvider).trim();
return Scrollbar(
controller: controller,
thumbVisibility: alwaysShowCollectionPaneScrollbar ? true : null,
radius: const Radius.circular(12),
child: ReorderableListView.builder(
padding: kPr8CollectionPane,
child: filterQuery.isEmpty
? ReorderableListView.builder(
padding: kPe8,
scrollController: controller,
buildDefaultDragHandles: false,
itemCount: requestSequence.length,
@ -143,6 +178,24 @@ class _RequestListState extends ConsumerState<RequestList> {
),
);
},
)
: ListView(
padding: kPe8,
controller: controller,
children: requestSequence.map((id) {
var item = requestItems[id]!;
if (item.url.toLowerCase().contains(filterQuery) ||
item.name.toLowerCase().contains(filterQuery)) {
return Padding(
padding: kP1,
child: RequestItem(
id: id,
requestModel: item,
),
);
}
return const SizedBox();
}).toList(),
),
);
}

View File

@ -21,7 +21,6 @@ class EditRequestBody extends ConsumerWidget {
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.background,
),
margin: kPt5o10,
child: Column(
children: [
const SizedBox(
@ -38,9 +37,12 @@ class EditRequestBody extends ConsumerWidget {
),
Expanded(
child: switch (contentType) {
ContentType.formdata => const FormDataWidget(),
ContentType.formdata =>
const Padding(padding: kPh4, child: FormDataWidget()),
// TODO: Fix JsonTextFieldEditor & plug it here
ContentType.json => TextFieldEditor(
ContentType.json => Padding(
padding: kPt5o10,
child: TextFieldEditor(
key: Key("$selectedId-json-body"),
fieldKey: "$selectedId-json-body-editor",
initialValue: requestModel?.requestBody,
@ -50,7 +52,10 @@ class EditRequestBody extends ConsumerWidget {
.update(selectedId, requestBody: value);
},
),
_ => TextFieldEditor(
),
_ => Padding(
padding: kPt5o10,
child: TextFieldEditor(
key: Key("$selectedId-body"),
fieldKey: "$selectedId-body-editor",
initialValue: requestModel?.requestBody,
@ -60,6 +65,7 @@ class EditRequestBody extends ConsumerWidget {
.update(selectedId, requestBody: value);
},
),
),
},
)
],

View File

@ -1,7 +1,7 @@
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:davi/davi.dart';
import 'package:data_table_2/data_table_2.dart';
import 'package:apidash/providers/providers.dart';
import 'package:apidash/widgets/widgets.dart';
import 'package:apidash/models/models.dart';
@ -18,6 +18,7 @@ class _FormDataBodyState extends ConsumerState<FormDataWidget> {
late int seed;
final random = Random.secure();
late List<FormDataModel> formRows;
bool isAddingRow = false;
@override
void initState() {
@ -39,159 +40,163 @@ class _FormDataBodyState extends ConsumerState<FormDataWidget> {
.select((value) => value?.requestFormDataList?.length));
var rF = ref.read(selectedRequestModelProvider)?.requestFormDataList;
bool isFormDataEmpty = rF == null || rF.isEmpty;
List<FormDataModel> rows = (isFormDataEmpty)
formRows = isFormDataEmpty
? [
kFormDataEmptyModel,
]
: rF;
formRows = isFormDataEmpty ? rows : rows + [kFormDataEmptyModel];
: rF +
[
kFormDataEmptyModel,
];
isAddingRow = false;
DaviModel<FormDataModel> daviModelRows = DaviModel<FormDataModel>(
rows: formRows,
columns: [
DaviColumn(
cellPadding: kpsV5,
name: 'Key',
grow: 4,
cellBuilder: (_, row) {
int idx = row.index;
bool isLast = idx + 1 == formRows.length;
return Theme(
data: Theme.of(context),
child: FormDataField(
keyId: "$selectedId-$idx-form-v-$seed",
initialValue: formRows[idx].name,
hintText: " Add Key",
List<DataColumn> columns = const [
DataColumn2(
label: Text(kNameField),
size: ColumnSize.M,
),
DataColumn2(
label: Text('='),
fixedWidth: 20,
),
DataColumn2(
label: Text(''),
fixedWidth: 70,
),
DataColumn2(
label: Text(kNameValue),
size: ColumnSize.L,
),
DataColumn2(
label: Text(''),
fixedWidth: 32,
),
];
List<DataRow> dataRows = List<DataRow>.generate(
formRows.length,
(index) {
bool isLast = index + 1 == formRows.length;
return DataRow(
key: ValueKey("$selectedId-$index-form-row-$seed"),
cells: <DataCell>[
DataCell(
CellField(
keyId: "$selectedId-$index-form-k-$seed",
initialValue: formRows[index].name,
hintText: kHintAddFieldName,
onChanged: (value) {
formRows[idx] = formRows[idx].copyWith(name: value);
if (isLast) formRows.add(kFormDataEmptyModel);
formRows[index] = formRows[index].copyWith(name: value);
if (isLast && !isAddingRow) {
isAddingRow = true;
formRows.add(kFormDataEmptyModel);
}
_onFieldChange(selectedId!);
},
colorScheme: Theme.of(context).colorScheme,
formDataType: formRows[idx].type,
onFormDataTypeChanged: (value) {
bool hasChanged = formRows[idx].type != value;
formRows[idx] = formRows[idx].copyWith(
),
),
DataCell(
Center(
child: Text(
"=",
style: kCodeStyle,
),
),
),
DataCell(
DropdownButtonFormData(
formDataType: formRows[index].type,
onChanged: (value) {
bool hasChanged = formRows[index].type != value;
formRows[index] = formRows[index].copyWith(
type: value ?? FormDataType.text,
);
formRows[idx] = formRows[idx].copyWith(value: "");
if (idx == formRows.length - 1 && hasChanged) {
formRows[index] = formRows[index].copyWith(value: "");
if (isLast && hasChanged) {
formRows.add(kFormDataEmptyModel);
}
setState(() {});
_onFieldChange(selectedId!);
},
),
);
},
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;
bool isLast = idx + 1 == formRows.length;
return formRows[idx].type == FormDataType.file
? Align(
alignment: Alignment.centerLeft,
child: Row(
children: [
Expanded(
child: Theme(
data: Theme.of(context),
child: ElevatedButton.icon(
DataCell(
formRows[index].type == FormDataType.file
? ElevatedButton.icon(
icon: const Icon(
Icons.snippet_folder_rounded,
size: 20,
),
style: ButtonStyle(
shape: MaterialStatePropertyAll(
RoundedRectangleBorder(
style: ElevatedButton.styleFrom(
minimumSize: const Size.fromHeight(kDataTableRowHeight),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(6),
),
),
),
onPressed: () async {
var pickedResult = await pickFile();
if (pickedResult != null &&
pickedResult.files.isNotEmpty &&
pickedResult.files.first.path != null) {
formRows[idx] = formRows[idx].copyWith(
value: pickedResult.files.first.path!,
pickedResult.path.isNotEmpty) {
formRows[index] = formRows[index].copyWith(
value: pickedResult.path,
);
setState(() {});
_onFieldChange(selectedId!);
}
},
label: Text(
(formRows[idx].type == FormDataType.file &&
formRows[idx].value.isNotEmpty)
? formRows[idx].value.toString()
: "Select File",
textAlign: TextAlign.center,
(formRows[index].type == FormDataType.file &&
formRows[index].value.isNotEmpty)
? formRows[index].value.toString()
: kLabelSelectFile,
overflow: TextOverflow.ellipsis,
style: kFormDataButtonLabelTextStyle,
),
),
),
),
],
),
)
: CellField(
keyId: "$selectedId-$idx-form-v-$seed",
initialValue: formRows[idx].value,
hintText: " Add Value",
keyId: "$selectedId-$index-form-v-$seed",
initialValue: formRows[index].value,
hintText: kHintAddValue,
onChanged: (value) {
formRows[idx] = formRows[idx].copyWith(value: value);
if (isLast) formRows.add(kFormDataEmptyModel);
formRows[index] =
formRows[index].copyWith(value: value);
if (isLast && !isAddingRow) {
isAddingRow = true;
formRows.add(kFormDataEmptyModel);
}
_onFieldChange(selectedId!);
},
colorScheme: Theme.of(context).colorScheme,
);
},
sortable: false,
),
DaviColumn(
pinStatus: PinStatus.none,
width: 30,
cellBuilder: (_, row) {
bool isLast = row.index + 1 == formRows.length;
return InkWell(
),
DataCell(
InkWell(
onTap: isLast
? null
: () {
seed = random.nextInt(kRandMax);
if (formRows.length == 2) {
setState(() {
formRows = [kFormDataEmptyModel];
formRows = [
kFormDataEmptyModel,
];
});
} else {
formRows.removeAt(row.index);
formRows.removeAt(index);
}
_onFieldChange(selectedId!);
},
child: Theme.of(context).brightness == Brightness.dark
? kIconRemoveDark
: kIconRemoveLight,
);
},
),
),
],
);
},
);
return Stack(
children: [
Container(
@ -203,19 +208,30 @@ class _FormDataBodyState extends ConsumerState<FormDataWidget> {
child: Column(
children: [
Expanded(
child: DaviTheme(
data: kTableThemeData,
child: Davi<FormDataModel>(daviModelRows),
child: Theme(
data: Theme.of(context)
.copyWith(scrollbarTheme: kDataTableScrollbarTheme),
child: DataTable2(
columnSpacing: 12,
dividerThickness: 0,
horizontalMargin: 0,
headingRowHeight: 0,
dataRowHeight: kDataTableRowHeight,
bottomMargin: kDataTableBottomPadding,
isVerticalScrollBarVisible: true,
columns: columns,
rows: dataRows,
),
),
kVSpacer20,
),
kVSpacer40,
],
),
),
Align(
alignment: Alignment.bottomCenter,
child: Padding(
padding: const EdgeInsets.only(bottom: 5),
padding: kPb15,
child: ElevatedButton.icon(
onPressed: () {
formRows.add(kFormDataEmptyModel);
@ -223,7 +239,7 @@ class _FormDataBodyState extends ConsumerState<FormDataWidget> {
},
icon: const Icon(Icons.add),
label: const Text(
"Add Form Data",
kLabelAddFormField,
style: kTextStyleButton,
),
),

View File

@ -1,7 +1,7 @@
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:davi/davi.dart';
import 'package:data_table_2/data_table_2.dart';
import 'package:apidash/providers/providers.dart';
import 'package:apidash/widgets/widgets.dart';
import 'package:apidash/models/models.dart';
@ -19,6 +19,7 @@ class EditRequestHeadersState extends ConsumerState<EditRequestHeaders> {
final random = Random.secure();
late List<NameValueModel> headerRows;
late List<bool> isRowEnabledList;
bool isAddingRow = false;
@override
void initState() {
@ -42,105 +43,106 @@ class EditRequestHeadersState extends ConsumerState<EditRequestHeaders> {
.select((value) => value?.requestHeaders?.length));
var rH = ref.read(selectedRequestModelProvider)?.requestHeaders;
bool isHeadersEmpty = rH == null || rH.isEmpty;
List<NameValueModel> rows = (isHeadersEmpty)
headerRows = isHeadersEmpty
? [
kNameValueEmptyModel,
]
: rH;
headerRows = isHeadersEmpty ? rows : rows + [kNameValueEmptyModel];
: rH + [kNameValueEmptyModel];
isRowEnabledList =
ref.read(selectedRequestModelProvider)?.isHeaderEnabledList ??
List.filled(rH?.length ?? 0, true, growable: true);
isRowEnabledList.add(false);
isAddingRow = false;
DaviModel<NameValueModel> model = DaviModel<NameValueModel>(
rows: headerRows,
columns: [
DaviColumn(
name: 'Checkbox',
width: 30,
cellBuilder: (_, row) {
int idx = row.index;
bool isLast = idx + 1 == headerRows.length;
return CheckBox(
keyId: "$selectedId-$idx-headers-c-$seed",
value: isRowEnabledList[idx],
List<DataColumn> columns = const [
DataColumn2(
label: Text(kNameCheckbox),
fixedWidth: 30,
),
DataColumn2(
label: Text(kNameHeader),
),
DataColumn2(
label: Text('='),
fixedWidth: 30,
),
DataColumn2(
label: Text(kNameValue),
),
DataColumn2(
label: Text(''),
fixedWidth: 32,
),
];
List<DataRow> dataRows = List<DataRow>.generate(
headerRows.length,
(index) {
bool isLast = index + 1 == headerRows.length;
return DataRow(
key: ValueKey("$selectedId-$index-headers-row-$seed"),
cells: <DataCell>[
DataCell(
CheckBox(
keyId: "$selectedId-$index-headers-c-$seed",
value: isRowEnabledList[index],
onChanged: isLast
? null
: (value) {
setState(() {
isRowEnabledList[idx] = value!;
isRowEnabledList[index] = value!;
});
_onFieldChange(selectedId!);
},
colorScheme: Theme.of(context).colorScheme,
);
},
),
DaviColumn(
name: 'Header Name',
width: 70,
grow: 1,
cellBuilder: (_, row) {
int idx = row.index;
bool isLast = idx + 1 == headerRows.length;
return HeaderField(
keyId: "$selectedId-$idx-headers-k-$seed",
initialValue: headerRows[idx].name,
hintText: "Add Header Name",
),
DataCell(
HeaderField(
keyId: "$selectedId-$index-headers-k-$seed",
initialValue: headerRows[index].name,
hintText: kHintAddName,
onChanged: (value) {
headerRows[idx] = headerRows[idx].copyWith(name: value);
if (isLast) {
isRowEnabledList[idx] = true;
headerRows[index] = headerRows[index].copyWith(name: value);
if (isLast && !isAddingRow) {
isAddingRow = true;
isRowEnabledList[index] = true;
headerRows.add(kNameValueEmptyModel);
isRowEnabledList.add(false);
}
_onFieldChange(selectedId!);
},
colorScheme: Theme.of(context).colorScheme,
);
},
sortable: false,
),
DaviColumn(
width: 30,
cellBuilder: (_, row) {
return Text(
),
DataCell(
Center(
child: Text(
"=",
style: kCodeStyle,
);
},
),
DaviColumn(
name: 'Header Value',
grow: 1,
cellBuilder: (_, row) {
int idx = row.index;
bool isLast = idx + 1 == headerRows.length;
return CellField(
keyId: "$selectedId-$idx-headers-v-$seed",
initialValue: headerRows[idx].value,
hintText: " Add Header Value",
),
),
DataCell(
CellField(
keyId: "$selectedId-$index-headers-v-$seed",
initialValue: headerRows[index].value,
hintText: kHintAddValue,
onChanged: (value) {
headerRows[idx] = headerRows[idx].copyWith(value: value);
if (isLast) {
isRowEnabledList[idx] = true;
headerRows[index] = headerRows[index].copyWith(value: value);
if (isLast && !isAddingRow) {
isAddingRow = true;
isRowEnabledList[index] = true;
headerRows.add(kNameValueEmptyModel);
isRowEnabledList.add(false);
}
_onFieldChange(selectedId!);
},
colorScheme: Theme.of(context).colorScheme,
);
},
sortable: false,
),
DaviColumn(
pinStatus: PinStatus.none,
width: 30,
cellBuilder: (_, row) {
bool isLast = row.index + 1 == headerRows.length;
return InkWell(
),
DataCell(
InkWell(
onTap: isLast
? null
: () {
@ -153,19 +155,21 @@ class EditRequestHeadersState extends ConsumerState<EditRequestHeaders> {
isRowEnabledList = [false];
});
} else {
headerRows.removeAt(row.index);
isRowEnabledList.removeAt(row.index);
headerRows.removeAt(index);
isRowEnabledList.removeAt(index);
}
_onFieldChange(selectedId!);
},
child: Theme.of(context).brightness == Brightness.dark
? kIconRemoveDark
: kIconRemoveLight,
);
},
),
),
],
);
},
);
return Stack(
children: [
Container(
@ -177,9 +181,20 @@ class EditRequestHeadersState extends ConsumerState<EditRequestHeaders> {
child: Column(
children: [
Expanded(
child: DaviTheme(
data: kTableThemeData,
child: Davi<NameValueModel>(model),
child: Theme(
data: Theme.of(context)
.copyWith(scrollbarTheme: kDataTableScrollbarTheme),
child: DataTable2(
columnSpacing: 12,
dividerThickness: 0,
horizontalMargin: 0,
headingRowHeight: 0,
dataRowHeight: kDataTableRowHeight,
bottomMargin: kDataTableBottomPadding,
isVerticalScrollBarVisible: true,
columns: columns,
rows: dataRows,
),
),
),
kVSpacer40,
@ -198,7 +213,7 @@ class EditRequestHeadersState extends ConsumerState<EditRequestHeaders> {
},
icon: const Icon(Icons.add),
label: const Text(
"Add Header",
kLabelAddHeader,
style: kTextStyleButton,
),
),

View File

@ -1,11 +1,11 @@
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:davi/davi.dart';
import 'package:apidash/providers/providers.dart';
import 'package:apidash/widgets/widgets.dart';
import 'package:apidash/models/models.dart';
import 'package:apidash/consts.dart';
import 'package:data_table_2/data_table_2.dart';
class EditRequestURLParams extends ConsumerStatefulWidget {
const EditRequestURLParams({super.key});
@ -20,6 +20,7 @@ class EditRequestURLParamsState extends ConsumerState<EditRequestURLParams> {
final random = Random.secure();
late List<NameValueModel> paramRows;
late List<bool> isRowEnabledList;
bool isAddingRow = false;
@override
void initState() {
@ -42,105 +43,106 @@ class EditRequestURLParamsState extends ConsumerState<EditRequestURLParams> {
.select((value) => value?.requestParams?.length));
var rP = ref.read(selectedRequestModelProvider)?.requestParams;
bool isParamsEmpty = rP == null || rP.isEmpty;
List<NameValueModel> rows = (isParamsEmpty)
paramRows = isParamsEmpty
? [
kNameValueEmptyModel,
]
: rP;
paramRows = isParamsEmpty ? rows : rows + [kNameValueEmptyModel];
: rP + [kNameValueEmptyModel];
isRowEnabledList =
ref.read(selectedRequestModelProvider)?.isParamEnabledList ??
List.filled(rP?.length ?? 0, true, growable: true);
isRowEnabledList.add(false);
isAddingRow = false;
DaviModel<NameValueModel> model = DaviModel<NameValueModel>(
rows: paramRows,
columns: [
DaviColumn(
name: 'Checkbox',
width: 30,
cellBuilder: (_, row) {
int idx = row.index;
bool isLast = idx + 1 == paramRows.length;
return CheckBox(
keyId: "$selectedId-$idx-params-c-$seed",
value: isRowEnabledList[idx],
List<DataColumn> columns = const [
DataColumn2(
label: Text(kNameCheckbox),
fixedWidth: 30,
),
DataColumn2(
label: Text(kNameURLParam),
),
DataColumn2(
label: Text('='),
fixedWidth: 30,
),
DataColumn2(
label: Text(kNameValue),
),
DataColumn2(
label: Text(''),
fixedWidth: 32,
),
];
List<DataRow> dataRows = List<DataRow>.generate(
paramRows.length,
(index) {
bool isLast = index + 1 == paramRows.length;
return DataRow(
key: ValueKey("$selectedId-$index-params-row-$seed"),
cells: <DataCell>[
DataCell(
CheckBox(
keyId: "$selectedId-$index-params-c-$seed",
value: isRowEnabledList[index],
onChanged: isLast
? null
: (value) {
setState(() {
isRowEnabledList[idx] = value!;
isRowEnabledList[index] = value!;
});
_onFieldChange(selectedId!);
},
colorScheme: Theme.of(context).colorScheme,
);
},
),
DaviColumn(
name: 'URL Parameter',
width: 70,
grow: 1,
cellBuilder: (_, row) {
int idx = row.index;
bool isLast = idx + 1 == paramRows.length;
return CellField(
keyId: "$selectedId-$idx-params-k-$seed",
initialValue: paramRows[idx].name,
hintText: "Add URL Parameter",
),
DataCell(
CellField(
keyId: "$selectedId-$index-params-k-$seed",
initialValue: paramRows[index].name,
hintText: kHintAddURLParam,
onChanged: (value) {
paramRows[idx] = paramRows[idx].copyWith(name: value);
if (isLast) {
isRowEnabledList[idx] = true;
paramRows[index] = paramRows[index].copyWith(name: value);
if (isLast && !isAddingRow) {
isAddingRow = true;
isRowEnabledList[index] = true;
paramRows.add(kNameValueEmptyModel);
isRowEnabledList.add(false);
}
_onFieldChange(selectedId!);
},
colorScheme: Theme.of(context).colorScheme,
);
},
sortable: false,
),
DaviColumn(
width: 30,
cellBuilder: (_, row) {
return Text(
),
DataCell(
Center(
child: Text(
"=",
style: kCodeStyle,
);
},
),
DaviColumn(
name: 'Value',
grow: 1,
cellBuilder: (_, row) {
int idx = row.index;
bool isLast = idx + 1 == paramRows.length;
return CellField(
keyId: "$selectedId-$idx-params-v-$seed",
initialValue: paramRows[idx].value,
hintText: "Add Value",
),
),
DataCell(
CellField(
keyId: "$selectedId-$index-params-v-$seed",
initialValue: paramRows[index].value,
hintText: kHintAddValue,
onChanged: (value) {
paramRows[idx] = paramRows[idx].copyWith(value: value);
if (isLast) {
isRowEnabledList[idx] = true;
paramRows[index] = paramRows[index].copyWith(value: value);
if (isLast && !isAddingRow) {
isAddingRow = true;
isRowEnabledList[index] = true;
paramRows.add(kNameValueEmptyModel);
isRowEnabledList.add(false);
}
_onFieldChange(selectedId!);
},
colorScheme: Theme.of(context).colorScheme,
);
},
sortable: false,
),
DaviColumn(
pinStatus: PinStatus.none,
width: 30,
cellBuilder: (_, row) {
bool isLast = row.index + 1 == paramRows.length;
return InkWell(
),
DataCell(
InkWell(
onTap: isLast
? null
: () {
@ -153,19 +155,21 @@ class EditRequestURLParamsState extends ConsumerState<EditRequestURLParams> {
isRowEnabledList = [false];
});
} else {
paramRows.removeAt(row.index);
isRowEnabledList.removeAt(row.index);
paramRows.removeAt(index);
isRowEnabledList.removeAt(index);
}
_onFieldChange(selectedId!);
},
child: Theme.of(context).brightness == Brightness.dark
? kIconRemoveDark
: kIconRemoveLight,
);
},
),
),
],
);
},
);
return Stack(
children: [
Container(
@ -175,11 +179,23 @@ class EditRequestURLParamsState extends ConsumerState<EditRequestURLParams> {
),
margin: kP10,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Expanded(
child: DaviTheme(
data: kTableThemeData,
child: Davi<NameValueModel>(model),
child: Theme(
data: Theme.of(context)
.copyWith(scrollbarTheme: kDataTableScrollbarTheme),
child: DataTable2(
columnSpacing: 12,
dividerThickness: 0,
horizontalMargin: 0,
headingRowHeight: 0,
dataRowHeight: kDataTableRowHeight,
bottomMargin: kDataTableBottomPadding,
isVerticalScrollBarVisible: true,
columns: columns,
rows: dataRows,
),
),
),
kVSpacer40,
@ -198,7 +214,7 @@ class EditRequestURLParamsState extends ConsumerState<EditRequestURLParams> {
},
icon: const Icon(Icons.add),
label: const Text(
"Add Param",
kLabelAddParam,
style: kTextStyleButton,
),
),

View File

@ -12,12 +12,17 @@ class ResponsePane extends ConsumerWidget {
final isWorking = ref.watch(
selectedRequestModelProvider.select((value) => value?.isWorking)) ??
false;
final startSendingTime = ref.watch(
selectedRequestModelProvider.select((value) => value?.sendingTime));
final responseStatus = ref.watch(
selectedRequestModelProvider.select((value) => value?.responseStatus));
final message = ref
.watch(selectedRequestModelProvider.select((value) => value?.message));
if (isWorking) {
return const SendingWidget();
return SendingWidget(
startSendingTime: startSendingTime,
);
}
if (responseStatus == null) {
return const NotSentWidget();
@ -34,7 +39,6 @@ class ResponseDetails extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
var sm = ScaffoldMessenger.of(context);
final responseStatus = ref.watch(
selectedRequestModelProvider.select((value) => value?.responseStatus));
final message = ref
@ -52,8 +56,6 @@ class ResponseDetails extends ConsumerWidget {
ref
.read(collectionStateNotifierProvider.notifier)
.clearResponse(selectedRequest?.id);
sm.hideCurrentSnackBar();
sm.showSnackBar(getSnackBar('Response cleared'));
},
),
const Expanded(

View File

@ -1,6 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:apidash/providers/collection_providers.dart';
import 'package:apidash/providers/providers.dart';
import 'package:apidash/consts.dart';
import 'details_card/details_card.dart';
import 'url_card.dart';

View File

@ -1,10 +1,10 @@
import 'dart:io';
import 'dart:typed_data';
import 'package:file_selector/file_selector.dart';
import 'package:path/path.dart' as p;
import 'package:mime_dart/mime_dart.dart';
import 'package:uuid/uuid.dart';
import 'package:path_provider/path_provider.dart';
import 'package:file_picker/file_picker.dart';
const uuid = Uuid();
@ -57,7 +57,7 @@ String getTempFileName() {
return getNewUuid();
}
Future<FilePickerResult?> pickFile() async {
FilePickerResult? pickedResult = await FilePicker.platform.pickFiles();
Future<XFile?> pickFile() async {
XFile? pickedResult = await openFile();
return pickedResult;
}

View File

@ -246,15 +246,12 @@ class ClearResponseButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Tooltip(
message: 'Clear response',
child: TextButton(
style: TextButton.styleFrom(minimumSize: const Size(40, 40)),
return IconButton(
tooltip: kTooltipClearResponse,
onPressed: onPressed,
child: const Icon(
icon: const Icon(
Icons.delete,
size: 20,
),
size: 16,
),
);
}

View File

@ -1,83 +0,0 @@
import 'package:apidash/consts.dart';
import 'package:flutter/material.dart';
import 'dropdowns.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,
contentPadding: const EdgeInsets.only(bottom: 16),
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,
),
),
],
);
}
}

View File

@ -76,11 +76,14 @@ class _HeaderFieldState extends State<HeaderField> {
onChanged: widget.onChanged,
controller: controller,
focusNode: focusNode,
style: kCodeStyle.copyWith(color: colorScheme.onSurface),
style: kCodeStyle.copyWith(
color: colorScheme.onSurface,
),
decoration: InputDecoration(
hintStyle: kCodeStyle.copyWith(
color: colorScheme.outline.withOpacity(kHintOpacity)),
hintText: widget.hintText,
contentPadding: const EdgeInsets.only(bottom: 12),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: colorScheme.primary.withOpacity(

View File

@ -8,6 +8,7 @@ import 'error_message.dart';
import 'uint8_audio_player.dart';
import 'json_previewer.dart';
import 'csv_previewer.dart';
import 'video_previewer.dart';
import '../consts.dart';
class Previewer extends StatefulWidget {
@ -86,7 +87,12 @@ class _PreviewerState extends State<Previewer> {
return CsvPreviewer(body: widget.body);
}
if (widget.type == kTypeVideo) {
// TODO: Video Player
try {
var preview = VideoPreviewer(videoBytes: widget.bytes);
return preview;
} catch (e) {
return const ErrorMessage(message: kVideoError);
}
}
String message = widget.hasRaw
? "$kMimeTypeRawRaiseIssueStart${widget.type}/${widget.subtype}$kMimeTypeRaiseIssue"

View File

@ -48,16 +48,12 @@ class _RequestPaneState extends State<RequestPane>
return Column(
children: [
Padding(
padding: kPh20v10,
padding: kP8,
child: SizedBox(
height: kHeaderHeight,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
mainAxisAlignment: MainAxisAlignment.end,
children: [
Text(
"Request",
style: Theme.of(context).textTheme.titleMedium,
),
FilledButton.tonalIcon(
onPressed: widget.onPressedCodeButton,
icon: Icon(
@ -68,7 +64,8 @@ class _RequestPaneState extends State<RequestPane>
label: SizedBox(
width: 75,
child: Text(
widget.codePaneVisible ? "Hide Code" : "View Code"),
widget.codePaneVisible ? kLabelHideCode : kLabelViewCode,
),
),
),
],
@ -79,22 +76,24 @@ class _RequestPaneState extends State<RequestPane>
key: Key(widget.selectedId!),
controller: _controller,
overlayColor: kColorTransparentState,
labelPadding: kPh2,
onTap: widget.onTapTabBar,
tabs: [
TabLabel(
text: 'URL Params',
text: kLabelURLParams,
showIndicator: widget.showIndicators[0],
),
TabLabel(
text: 'Headers',
text: kLabelHeaders,
showIndicator: widget.showIndicators[1],
),
TabLabel(
text: 'Body',
text: kLabelBody,
showIndicator: widget.showIndicators[2],
),
],
),
kVSpacer5,
Expanded(
child: TabBarView(
controller: _controller,

View File

@ -1,3 +1,4 @@
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:http_parser/http_parser.dart';
@ -23,7 +24,7 @@ class NotSentWidget extends StatelessWidget {
color: color,
),
Text(
'Not Sent',
kLabelNotSent,
style:
Theme.of(context).textTheme.titleMedium?.copyWith(color: color),
),
@ -33,18 +34,80 @@ class NotSentWidget extends StatelessWidget {
}
}
class SendingWidget extends StatelessWidget {
const SendingWidget({super.key});
class SendingWidget extends StatefulWidget {
final DateTime? startSendingTime;
const SendingWidget({
super.key,
required this.startSendingTime,
});
@override
State<SendingWidget> createState() => _SendingWidgetState();
}
class _SendingWidgetState extends State<SendingWidget> {
int _millisecondsElapsed = 0;
Timer? _timer;
@override
void initState() {
super.initState();
if (widget.startSendingTime != null) {
_millisecondsElapsed =
(DateTime.now().difference(widget.startSendingTime!).inMilliseconds ~/
100) *
100;
_timer = Timer.periodic(const Duration(milliseconds: 100), _updateTimer);
}
}
void _updateTimer(Timer timer) {
setState(() {
_millisecondsElapsed += 100;
});
}
@override
void dispose() {
if (_timer != null && _timer!.isActive) _timer?.cancel();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Center(
child: Column(
return Stack(
children: [
Center(
child: Lottie.asset(kAssetSendingLottie),
),
Padding(
padding: kPh20t40,
child: Visibility(
visible: _millisecondsElapsed >= 0,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Lottie.asset(kAssetSendingLottie),
Icon(
Icons.alarm,
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
const SizedBox(
width: 10,
),
Text(
'Time elapsed: ${humanizeDuration(Duration(milliseconds: _millisecondsElapsed))}',
textAlign: TextAlign.center,
overflow: TextOverflow.fade,
softWrap: false,
style: kTextStyleButton.copyWith(
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
),
],
),
),
),
],
);
}
}
@ -66,42 +129,19 @@ class ResponsePaneHeader extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Padding(
padding: kPh20v10,
padding: kPv8,
child: SizedBox(
height: kHeaderHeight,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text.rich(
TextSpan(
children: [
const TextSpan(
text: "Response (",
),
TextSpan(
text: "$responseStatus",
style: TextStyle(
color: getResponseStatusCodeColor(
responseStatus,
brightness: Theme.of(context).brightness,
),
fontFamily: kCodeStyle.fontFamily,
),
),
const TextSpan(
text: ")",
),
],
style: Theme.of(context).textTheme.titleMedium,
),
),
kHSpacer20,
kHSpacer10,
Expanded(
child: Text(
message ?? "",
"$responseStatus: ${message ?? '-'}",
softWrap: false,
overflow: TextOverflow.ellipsis,
style: Theme.of(context).textTheme.titleMedium!.copyWith(
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
fontFamily: kCodeStyle.fontFamily,
color: getResponseStatusCodeColor(
responseStatus,
@ -110,10 +150,10 @@ class ResponsePaneHeader extends StatelessWidget {
),
),
),
kHSpacer20,
kHSpacer10,
Text(
humanizeDuration(time),
style: Theme.of(context).textTheme.titleMedium!.copyWith(
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
fontFamily: kCodeStyle.fontFamily,
color: Theme.of(context).colorScheme.secondary,
),
@ -163,31 +203,15 @@ class _ResponseTabViewState extends State<ResponseTabView>
TabBar(
key: Key(widget.selectedId!),
controller: _controller,
labelPadding: kPh2,
overlayColor: kColorTransparentState,
onTap: (index) {},
tabs: const [
SizedBox(
height: kTabHeight,
child: Center(
child: Text(
'Body',
textAlign: TextAlign.center,
overflow: TextOverflow.fade,
softWrap: false,
style: kTextStyleButton,
),
),
),
SizedBox(
height: kTabHeight,
child: Center(
child: Text(
'Headers',
textAlign: TextAlign.center,
overflow: TextOverflow.fade,
style: kTextStyleButton,
),
TabLabel(
text: kLabelResponseBody,
),
TabLabel(
text: kLabelHeaders,
),
],
),
@ -227,8 +251,8 @@ class ResponseHeadersHeader extends StatelessWidget {
children: [
Expanded(
child: Text(
"$name (${map.length} items)",
style: Theme.of(context).textTheme.labelLarge!.copyWith(
"$name (${map.length} $kLabelItems)",
style: Theme.of(context).textTheme.labelMedium?.copyWith(
fontWeight: FontWeight.bold,
),
),
@ -243,8 +267,6 @@ class ResponseHeadersHeader extends StatelessWidget {
}
}
const kHeaderRow = ["Header Name", "Header Value"];
class ResponseHeaders extends StatelessWidget {
const ResponseHeaders({
super.key,
@ -263,7 +285,7 @@ class ResponseHeaders extends StatelessWidget {
children: [
ResponseHeadersHeader(
map: responseHeaders,
name: "Response Headers",
name: kLabelResponseHeaders,
),
if (responseHeaders.isNotEmpty) kVSpacer5,
if (responseHeaders.isNotEmpty)
@ -275,7 +297,7 @@ class ResponseHeaders extends StatelessWidget {
kVSpacer10,
ResponseHeadersHeader(
map: requestHeaders,
name: "Request Headers",
name: kLabelRequestHeaders,
),
if (requestHeaders.isNotEmpty) kVSpacer5,
if (requestHeaders.isNotEmpty)
@ -302,20 +324,18 @@ class ResponseBody extends StatelessWidget {
Widget build(BuildContext context) {
final responseModel = selectedRequestModel?.responseModel;
if (responseModel == null) {
return const ErrorMessage(
message:
'Error: Response data does not exist. $kUnexpectedRaiseIssue');
return const ErrorMessage(message: '$kMsgError $kUnexpectedRaiseIssue');
}
var body = responseModel.body;
var formattedBody = responseModel.formattedBody;
if (body == null) {
return const ErrorMessage(
message: 'Response body is missing (null). $kUnexpectedRaiseIssue');
message: '$kMsgNullBody $kUnexpectedRaiseIssue');
}
if (body.isEmpty) {
return const ErrorMessage(
message: 'No content',
message: kMsgNoContent,
showIcon: false,
showIssueButton: false,
);
@ -325,7 +345,7 @@ class ResponseBody extends StatelessWidget {
if (mediaType == null) {
return ErrorMessage(
message:
'Unknown Response Content-Type - ${responseModel.contentType}. $kUnexpectedRaiseIssue');
'$kMsgUnknowContentType - ${responseModel.contentType}. $kUnexpectedRaiseIssue');
}
var responseBodyView = getResponseBodyViewOptions(mediaType);

View File

@ -2,7 +2,11 @@ import 'package:flutter/material.dart';
import 'package:apidash/consts.dart';
class TabLabel extends StatelessWidget {
const TabLabel({super.key, required this.text, this.showIndicator = false});
const TabLabel({
super.key,
required this.text,
this.showIndicator = false,
});
final String text;
final bool showIndicator;
@ -18,14 +22,14 @@ class TabLabel extends StatelessWidget {
textAlign: TextAlign.center,
overflow: TextOverflow.fade,
softWrap: false,
style: kTextStyleButton,
style: kTextStyleTab,
),
),
if (showIndicator)
const Align(
alignment: Alignment.topCenter,
child: Padding(
padding: EdgeInsets.only(top: 6),
padding: EdgeInsets.only(top: 1),
child: Icon(
Icons.circle,
size: 6,

View File

@ -68,6 +68,7 @@ class CellField extends StatelessWidget {
),
),
hintText: hintText,
contentPadding: const EdgeInsets.only(bottom: 12),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: clrScheme.primary.withOpacity(
@ -94,14 +95,39 @@ class JsonSearchField extends StatelessWidget {
@override
Widget build(BuildContext context) {
return TextField(
return RawTextField(
controller: controller,
onChanged: onChanged,
style: kCodeStyle,
decoration: const InputDecoration(
hintText: 'Search..',
);
}
}
class RawTextField extends StatelessWidget {
const RawTextField({
super.key,
this.onChanged,
this.controller,
this.hintText,
this.style,
});
final void Function(String)? onChanged;
final TextEditingController? controller;
final String? hintText;
final TextStyle? style;
@override
Widget build(BuildContext context) {
return TextField(
controller: controller,
onChanged: onChanged,
style: style,
decoration: InputDecoration(
isDense: true,
border: InputBorder.none,
hintText: 'Search..',
hintText: hintText,
),
);
}

View File

@ -0,0 +1,147 @@
import 'dart:io';
import 'dart:typed_data';
import 'package:apidash/consts.dart';
import 'package:fvp/fvp.dart' as fvp;
import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart';
import 'package:video_player/video_player.dart';
import 'package:path_provider/path_provider.dart';
class VideoPreviewer extends StatefulWidget {
const VideoPreviewer({
super.key,
required this.videoBytes,
});
final Uint8List videoBytes;
@override
State<VideoPreviewer> createState() => _VideoPreviewerState();
}
class _VideoPreviewerState extends State<VideoPreviewer> {
VideoPlayerController? _videoController;
bool _isPlaying = false;
File? _tempVideoFile;
bool _showControls = false;
@override
void initState() {
super.initState();
registerWithAllPlatforms();
_initializeVideoPlayer();
}
void registerWithAllPlatforms() {
try {
fvp.registerWith();
} catch (e) {
// pass
}
}
void _initializeVideoPlayer() async {
final tempDir = await getTemporaryDirectory();
_tempVideoFile = File(
'${tempDir.path}/temp_video_${DateTime.now().millisecondsSinceEpoch}');
try {
await _tempVideoFile?.writeAsBytes(widget.videoBytes);
_videoController = VideoPlayerController.file(_tempVideoFile!)
..initialize().then((_) {
if (mounted) {
setState(() {
_videoController!.play();
_videoController!.setLooping(true);
});
}
});
} catch (e) {
return;
}
}
@override
Widget build(BuildContext context) {
final iconColor = Theme.of(context).iconTheme.color;
final progressBarColors = VideoProgressColors(
playedColor: iconColor!,
bufferedColor: iconColor.withOpacity(0.5),
backgroundColor: iconColor.withOpacity(0.3),
);
return Scaffold(
body: MouseRegion(
onEnter: (_) => setState(() => _showControls = true),
onExit: (_) => setState(() => _showControls = false),
child: Stack(
children: [
Center(
child: _videoController?.value.isInitialized == true
? AspectRatio(
aspectRatio: _videoController!.value.aspectRatio,
child: VideoPlayer(_videoController!),
)
: const CircularProgressIndicator(),
),
Positioned(
left: 0,
right: 0,
bottom: 0,
child: _videoController?.value.isInitialized == true
? SizedBox(
height: 50.0,
child: VideoProgressIndicator(
_videoController!,
allowScrubbing: true,
padding: const EdgeInsets.all(20),
colors: progressBarColors,
),
)
: Container(height: 0),
),
if (_showControls)
Center(
child: GestureDetector(
onTap: () {
if (_videoController!.value.isPlaying) {
_videoController!.pause();
} else {
_videoController!.play();
}
setState(() {
_isPlaying = !_isPlaying;
});
},
child: Container(
color: Colors.transparent,
child: Icon(
_isPlaying ? Icons.play_arrow : Icons.pause,
size: 64,
color: iconColor,
),
),
),
),
],
),
),
);
}
@override
void dispose() {
_videoController?.pause();
_videoController?.dispose();
if (!kIsRunningTests) {
Future.delayed(const Duration(seconds: 1), () async {
try {
if (_tempVideoFile != null) {
await _tempVideoFile!.delete();
}
} catch (e) {
return;
}
});
}
super.dispose();
}
}

View File

@ -7,7 +7,6 @@ export 'dropdowns.dart';
export 'editor_json.dart';
export 'editor.dart';
export 'error_message.dart';
export 'form_data_field.dart';
export 'headerfield.dart';
export 'intro_message.dart';
export 'json_previewer.dart';

View File

@ -49,14 +49,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.1.18"
axis_layout:
dependency: transitive
description:
name: axis_layout
sha256: "9ba44f279f39121065d811e72da892de86f5613d68eb0b295f60d021ea8f2a59"
url: "https://pub.dev"
source: hosted
version: "1.0.1"
barcode:
dependency: transitive
description:
@ -209,6 +201,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.7.2"
cross_file:
dependency: transitive
description:
name: cross_file
sha256: "55d7b444feb71301ef6b8838dbc1ae02e63dd48c8773f3810ff53bb1e2945b32"
url: "https://pub.dev"
source: hosted
version: "0.3.4+1"
crypto:
dependency: transitive
description:
@ -217,6 +217,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.0.3"
csslib:
dependency: transitive
description:
name: csslib
sha256: "706b5707578e0c1b4b7550f64078f0a0f19dec3f50a178ffae7006b0a9ca58fb"
url: "https://pub.dev"
source: hosted
version: "1.0.0"
csv:
dependency: "direct main"
description:
@ -233,14 +241,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.3.6"
davi:
data_table_2:
dependency: "direct main"
description:
name: davi
sha256: "4105870281c4c33e8e017e21e212b96fd2637b4c1a35b2a56f14aaa4acdf6f0d"
name: data_table_2
sha256: fdb0551f103f1daf837bddfde14619fd9e683408833a618c9afabeb533fce88c
url: "https://pub.dev"
source: hosted
version: "3.4.1"
version: "2.5.11"
eventify:
dependency: transitive
description:
@ -289,14 +297,70 @@ packages:
url: "https://pub.dev"
source: hosted
version: "7.0.0"
file_picker:
file_selector:
dependency: "direct main"
description:
name: file_picker
sha256: caa6bc229eab3e32eb2f37b53a5f9d22a6981474afd210c512a7546c1e1a04f6
name: file_selector
sha256: "5019692b593455127794d5718304ff1ae15447dea286cdda9f0db2a796a1b828"
url: "https://pub.dev"
source: hosted
version: "6.2.0"
version: "1.0.3"
file_selector_android:
dependency: transitive
description:
name: file_selector_android
sha256: "1cd66575f063b689e041aec836905ba7be18d76c9f0634d0d75daec825f67095"
url: "https://pub.dev"
source: hosted
version: "0.5.0+7"
file_selector_ios:
dependency: transitive
description:
name: file_selector_ios
sha256: b015154e6d9fddbc4d08916794df170b44531798c8dd709a026df162d07ad81d
url: "https://pub.dev"
source: hosted
version: "0.5.1+8"
file_selector_linux:
dependency: transitive
description:
name: file_selector_linux
sha256: "045d372bf19b02aeb69cacf8b4009555fb5f6f0b7ad8016e5f46dd1387ddd492"
url: "https://pub.dev"
source: hosted
version: "0.9.2+1"
file_selector_macos:
dependency: transitive
description:
name: file_selector_macos
sha256: b15c3da8bd4908b9918111fa486903f5808e388b8d1c559949f584725a6594d6
url: "https://pub.dev"
source: hosted
version: "0.9.3+3"
file_selector_platform_interface:
dependency: transitive
description:
name: file_selector_platform_interface
sha256: a3994c26f10378a039faa11de174d7b78eb8f79e4dd0af2a451410c1a5c3f66b
url: "https://pub.dev"
source: hosted
version: "2.6.2"
file_selector_web:
dependency: transitive
description:
name: file_selector_web
sha256: "619e431b224711a3869e30dbd7d516f5f5a4f04b265013a50912f39e1abc88c8"
url: "https://pub.dev"
source: hosted
version: "0.9.4+1"
file_selector_windows:
dependency: transitive
description:
name: file_selector_windows
sha256: d3547240c20cabf205c7c7f01a50ecdbc413755814d6677f3cb366f04abcead0
url: "https://pub.dev"
source: hosted
version: "0.9.3+1"
fixnum:
dependency: transitive
description:
@ -382,14 +446,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.6.21"
flutter_plugin_android_lifecycle:
dependency: transitive
description:
name: flutter_plugin_android_lifecycle
sha256: b068ffc46f82a55844acfa4fdbb61fad72fa2aef0905548419d97f0f95c456da
url: "https://pub.dev"
source: hosted
version: "2.0.17"
flutter_riverpod:
dependency: "direct main"
description:
@ -448,6 +504,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.2.0"
fvp:
dependency: "direct main"
description:
name: fvp
sha256: "995328479ba4641da6760ddc84a168db157a3b9db4f0417fa68713d99344a146"
url: "https://pub.dev"
source: hosted
version: "0.14.0"
glob:
dependency: transitive
description:
@ -496,6 +560,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.1.0"
html:
dependency: transitive
description:
name: html
sha256: "3a7812d5bcd2894edf53dfaf8cd640876cf6cef50a8f238745c8b8120ea74d3a"
url: "https://pub.dev"
source: hosted
version: "0.15.4"
html_unescape:
dependency: transitive
description:
@ -1294,6 +1366,46 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.4"
video_player:
dependency: "direct main"
description:
name: video_player
sha256: afc65f4b8bcb2c188f64a591f84fb471f4f2e19fc607c65fd8d2f8fedb3dec23
url: "https://pub.dev"
source: hosted
version: "2.8.3"
video_player_android:
dependency: transitive
description:
name: video_player_android
sha256: "4dd9b8b86d70d65eecf3dcabfcdfbb9c9115d244d022654aba49a00336d540c2"
url: "https://pub.dev"
source: hosted
version: "2.4.12"
video_player_avfoundation:
dependency: transitive
description:
name: video_player_avfoundation
sha256: "309e3962795e761be010869bae65c0b0e45b5230c5cee1bec72197ca7db040ed"
url: "https://pub.dev"
source: hosted
version: "2.5.6"
video_player_platform_interface:
dependency: "direct main"
description:
name: video_player_platform_interface
sha256: "236454725fafcacf98f0f39af0d7c7ab2ce84762e3b63f2cbb3ef9a7e0550bc6"
url: "https://pub.dev"
source: hosted
version: "6.2.2"
video_player_web:
dependency: transitive
description:
name: video_player_web
sha256: "41245cef5ef29c4585dbabcbcbe9b209e34376642c7576cabf11b4ad9289d6e4"
url: "https://pub.dev"
source: hosted
version: "2.3.0"
vm_service:
dependency: transitive
description:

View File

@ -15,7 +15,6 @@ dependencies:
flutter_riverpod: ^2.5.1
riverpod: ^2.5.1
uuid: ^4.3.3
davi: ^3.4.1
http: ^1.2.1
http_parser: ^4.0.2
collection: ^1.17.2
@ -44,18 +43,22 @@ dependencies:
package_info_plus: ^5.0.1
flutter_typeahead: ^5.2.0
provider: ^6.1.2
fvp: ^0.14.0
video_player: ^2.3.2
video_player_platform_interface: ^6.2.2
json_data_explorer:
git:
url: https://github.com/foss42/json_data_explorer.git
ref: b7dde2f85dff4f482eed7eda4ef2a71344ef8b3a
scrollable_positioned_list: ^0.3.8
file_picker: ^6.2.0
flutter_svg: ^2.0.10+1
vector_graphics_compiler: ^1.1.9+1
code_builder: ^4.10.0
dart_style: ^2.3.6
json_text_field: ^1.1.0
csv: ^6.0.0
data_table_2: ^2.5.11
file_selector: ^1.0.3
dependency_overrides:
web: ^0.5.0

View File

@ -0,0 +1,982 @@
import 'package:apidash/consts.dart';
import 'package:apidash/screens/home_page/editor_pane/details_card/code_pane.dart';
import 'package:flutter_test/flutter_test.dart';
import '../request_models.dart';
void main() {
group("Get Request Test", () {
test("Get 1", () {
const expectedCode = r"""
using System;
using RestSharp;
using System.Threading.Tasks;
class Program
{
static async Task Main(){
try{
const String _baseUrl = "https://api.apidash.dev";
var client = new RestClient(_baseUrl);
var request = new RestRequest("", Method.Get);
var response = await client.ExecuteAsync(request);
Console.WriteLine("Status Code: " + (int)response.StatusCode);
Console.WriteLine("Response Content: " + response.Content);
}
catch(Exception ex){
Console.WriteLine("Error: " + ex);
}
}
}
""";
expect(
codegen.getCode(
CodegenLanguage.cSharpRestSharp, requestModelGet1, "https"),
expectedCode);
});
test("Get 2", () {
const expectedCode = r"""
using System;
using RestSharp;
using System.Threading.Tasks;
class Program
{
static async Task Main(){
try{
const String _baseUrl = "https://api.apidash.dev";
var client = new RestClient(_baseUrl);
var request = new RestRequest("/country/data", Method.Get);
request.AddQueryParameter("code", "US");
var response = await client.ExecuteAsync(request);
Console.WriteLine("Status Code: " + (int)response.StatusCode);
Console.WriteLine("Response Content: " + response.Content);
}
catch(Exception ex){
Console.WriteLine("Error: " + ex);
}
}
}
""";
expect(
codegen.getCode(
CodegenLanguage.cSharpRestSharp, requestModelGet2, "https"),
expectedCode);
});
test("Get 3", () {
const expectedCode = r"""
using System;
using RestSharp;
using System.Threading.Tasks;
class Program
{
static async Task Main(){
try{
const String _baseUrl = "https://api.apidash.dev";
var client = new RestClient(_baseUrl);
var request = new RestRequest("/country/data", Method.Get);
request.AddQueryParameter("code", "IND");
var response = await client.ExecuteAsync(request);
Console.WriteLine("Status Code: " + (int)response.StatusCode);
Console.WriteLine("Response Content: " + response.Content);
}
catch(Exception ex){
Console.WriteLine("Error: " + ex);
}
}
}
""";
expect(
codegen.getCode(
CodegenLanguage.cSharpRestSharp, requestModelGet3, "https"),
expectedCode);
});
test("Get 4", () {
const expectedCode = r"""
using System;
using RestSharp;
using System.Threading.Tasks;
class Program
{
static async Task Main(){
try{
const String _baseUrl = "https://api.apidash.dev";
var client = new RestClient(_baseUrl);
var request = new RestRequest("/humanize/social", Method.Get);
request.AddQueryParameter("num", "8700000");
request.AddQueryParameter("digits", "3");
request.AddQueryParameter("system", "SS");
request.AddQueryParameter("add_space", "true");
request.AddQueryParameter("trailing_zeros", "true");
var response = await client.ExecuteAsync(request);
Console.WriteLine("Status Code: " + (int)response.StatusCode);
Console.WriteLine("Response Content: " + response.Content);
}
catch(Exception ex){
Console.WriteLine("Error: " + ex);
}
}
}
""";
expect(
codegen.getCode(
CodegenLanguage.cSharpRestSharp, requestModelGet4, "https"),
expectedCode);
});
test("Get 5", () {
const expectedCode = r"""
using System;
using RestSharp;
using System.Threading.Tasks;
class Program
{
static async Task Main(){
try{
const String _baseUrl = "https://api.github.com";
var client = new RestClient(_baseUrl);
var request = new RestRequest("/repos/foss42/apidash", Method.Get);
request.AddHeader("User-Agent", "Test Agent");
var response = await client.ExecuteAsync(request);
Console.WriteLine("Status Code: " + (int)response.StatusCode);
Console.WriteLine("Response Content: " + response.Content);
}
catch(Exception ex){
Console.WriteLine("Error: " + ex);
}
}
}
""";
expect(
codegen.getCode(
CodegenLanguage.cSharpRestSharp, requestModelGet5, "https"),
expectedCode);
});
test("Get 6", () {
const expectedCode = r"""
using System;
using RestSharp;
using System.Threading.Tasks;
class Program
{
static async Task Main(){
try{
const String _baseUrl = "https://api.github.com";
var client = new RestClient(_baseUrl);
var request = new RestRequest("/repos/foss42/apidash", Method.Get);
request.AddQueryParameter("raw", "true");
request.AddHeader("User-Agent", "Test Agent");
var response = await client.ExecuteAsync(request);
Console.WriteLine("Status Code: " + (int)response.StatusCode);
Console.WriteLine("Response Content: " + response.Content);
}
catch(Exception ex){
Console.WriteLine("Error: " + ex);
}
}
}
""";
expect(
codegen.getCode(
CodegenLanguage.cSharpRestSharp, requestModelGet6, "https"),
expectedCode);
});
test("Get 7", () {
const expectedCode = r"""
using System;
using RestSharp;
using System.Threading.Tasks;
class Program
{
static async Task Main(){
try{
const String _baseUrl = "https://api.apidash.dev";
var client = new RestClient(_baseUrl);
var request = new RestRequest("", Method.Get);
var response = await client.ExecuteAsync(request);
Console.WriteLine("Status Code: " + (int)response.StatusCode);
Console.WriteLine("Response Content: " + response.Content);
}
catch(Exception ex){
Console.WriteLine("Error: " + ex);
}
}
}
""";
expect(
codegen.getCode(
CodegenLanguage.cSharpRestSharp, requestModelGet7, "https"),
expectedCode);
});
test("Get 8", () {
const expectedCode = r"""
using System;
using RestSharp;
using System.Threading.Tasks;
class Program
{
static async Task Main(){
try{
const String _baseUrl = "https://api.github.com";
var client = new RestClient(_baseUrl);
var request = new RestRequest("/repos/foss42/apidash", Method.Get);
request.AddQueryParameter("raw", "true");
request.AddHeader("User-Agent", "Test Agent");
var response = await client.ExecuteAsync(request);
Console.WriteLine("Status Code: " + (int)response.StatusCode);
Console.WriteLine("Response Content: " + response.Content);
}
catch(Exception ex){
Console.WriteLine("Error: " + ex);
}
}
}
""";
expect(
codegen.getCode(
CodegenLanguage.cSharpRestSharp, requestModelGet8, "https"),
expectedCode);
});
test("Get 9", () {
const expectedCode = r"""
using System;
using RestSharp;
using System.Threading.Tasks;
class Program
{
static async Task Main(){
try{
const String _baseUrl = "https://api.apidash.dev";
var client = new RestClient(_baseUrl);
var request = new RestRequest("/humanize/social", Method.Get);
request.AddQueryParameter("num", "8700000");
request.AddQueryParameter("add_space", "true");
var response = await client.ExecuteAsync(request);
Console.WriteLine("Status Code: " + (int)response.StatusCode);
Console.WriteLine("Response Content: " + response.Content);
}
catch(Exception ex){
Console.WriteLine("Error: " + ex);
}
}
}
""";
expect(
codegen.getCode(
CodegenLanguage.cSharpRestSharp, requestModelGet9, "https"),
expectedCode);
});
test("Get 10", () {
const expectedCode = r"""
using System;
using RestSharp;
using System.Threading.Tasks;
class Program
{
static async Task Main(){
try{
const String _baseUrl = "https://api.apidash.dev";
var client = new RestClient(_baseUrl);
var request = new RestRequest("/humanize/social", Method.Get);
request.AddHeader("User-Agent", "Test Agent");
var response = await client.ExecuteAsync(request);
Console.WriteLine("Status Code: " + (int)response.StatusCode);
Console.WriteLine("Response Content: " + response.Content);
}
catch(Exception ex){
Console.WriteLine("Error: " + ex);
}
}
}
""";
expect(
codegen.getCode(
CodegenLanguage.cSharpRestSharp, requestModelGet10, "https"),
expectedCode);
});
test("Get 11", () {
const expectedCode = r"""
using System;
using RestSharp;
using System.Threading.Tasks;
class Program
{
static async Task Main(){
try{
const String _baseUrl = "https://api.apidash.dev";
var client = new RestClient(_baseUrl);
var request = new RestRequest("/humanize/social", Method.Get);
request.AddQueryParameter("num", "8700000");
request.AddQueryParameter("digits", "3");
request.AddHeader("User-Agent", "Test Agent");
var response = await client.ExecuteAsync(request);
Console.WriteLine("Status Code: " + (int)response.StatusCode);
Console.WriteLine("Response Content: " + response.Content);
}
catch(Exception ex){
Console.WriteLine("Error: " + ex);
}
}
}
""";
expect(
codegen.getCode(
CodegenLanguage.cSharpRestSharp, requestModelGet11, "https"),
expectedCode);
});
test("Get 12", () {
const expectedCode = r"""
using System;
using RestSharp;
using System.Threading.Tasks;
class Program
{
static async Task Main(){
try{
const String _baseUrl = "https://api.apidash.dev";
var client = new RestClient(_baseUrl);
var request = new RestRequest("/humanize/social", Method.Get);
var response = await client.ExecuteAsync(request);
Console.WriteLine("Status Code: " + (int)response.StatusCode);
Console.WriteLine("Response Content: " + response.Content);
}
catch(Exception ex){
Console.WriteLine("Error: " + ex);
}
}
}
""";
expect(
codegen.getCode(
CodegenLanguage.cSharpRestSharp, requestModelGet12, "https"),
expectedCode);
});
});
group("Head Request Test", () {
test("Head 1", () {
const expectedCode = r"""
using System;
using RestSharp;
using System.Threading.Tasks;
class Program
{
static async Task Main(){
try{
const String _baseUrl = "https://api.apidash.dev";
var client = new RestClient(_baseUrl);
var request = new RestRequest("", Method.Head);
var response = await client.ExecuteAsync(request);
Console.WriteLine("Status Code: " + (int)response.StatusCode);
Console.WriteLine("Response Content: " + response.Content);
}
catch(Exception ex){
Console.WriteLine("Error: " + ex);
}
}
}
""";
expect(
codegen.getCode(
CodegenLanguage.cSharpRestSharp, requestModelHead1, "https"),
expectedCode);
});
test("Head 2", () {
const expectedCode = r"""
using System;
using RestSharp;
using System.Threading.Tasks;
class Program
{
static async Task Main(){
try{
const String _baseUrl = "http://api.apidash.dev";
var client = new RestClient(_baseUrl);
var request = new RestRequest("", Method.Head);
var response = await client.ExecuteAsync(request);
Console.WriteLine("Status Code: " + (int)response.StatusCode);
Console.WriteLine("Response Content: " + response.Content);
}
catch(Exception ex){
Console.WriteLine("Error: " + ex);
}
}
}
""";
expect(
codegen.getCode(
CodegenLanguage.cSharpRestSharp, requestModelHead2, "http"),
expectedCode);
});
});
group("Post Request Test", () {
test("Post 1", () {
const expectedCode = r"""
using System;
using RestSharp;
using System.Threading.Tasks;
class Program
{
static async Task Main(){
try{
const String _baseUrl = "https://api.apidash.dev";
var client = new RestClient(_baseUrl);
var request = new RestRequest("/case/lower", Method.Post);
request.AddHeader("Content-Type", "text/plain");
var textBody = "{\n\"text\": \"I LOVE Flutter\"\n}";
request.AddStringBody(textBody, ContentType.Plain);
var response = await client.ExecuteAsync(request);
Console.WriteLine("Status Code: " + (int)response.StatusCode);
Console.WriteLine("Response Content: " + response.Content);
}
catch(Exception ex){
Console.WriteLine("Error: " + ex);
}
}
}
""";
expect(
codegen.getCode(
CodegenLanguage.cSharpRestSharp, requestModelPost1, "https"),
expectedCode);
});
test("Post 2", () {
const expectedCode = """
using System;
using RestSharp;
using System.Threading.Tasks;
class Program
{
static async Task Main(){
try{
const String _baseUrl = "https://api.apidash.dev";
var client = new RestClient(_baseUrl);
var request = new RestRequest("/case/lower", Method.Post);
request.AddHeader("Content-Type", "application/json");
var jsonBody = new {
text = "I LOVE Flutter",
flag = "null",
male = "true",
female = "false",
no = "1.2",
arr = "[null, true, false, null]"
};
request.AddJsonBody(jsonBody);
var response = await client.ExecuteAsync(request);
Console.WriteLine("Status Code: " + (int)response.StatusCode);
Console.WriteLine("Response Content: " + response.Content);
}
catch(Exception ex){
Console.WriteLine("Error: " + ex);
}
}
}
""";
expect(
codegen.getCode(
CodegenLanguage.cSharpRestSharp, requestModelPost2, "https"),
expectedCode);
});
test("Post 3", () {
const expectedCode = """
using System;
using RestSharp;
using System.Threading.Tasks;
class Program
{
static async Task Main(){
try{
const String _baseUrl = "https://api.apidash.dev";
var client = new RestClient(_baseUrl);
var request = new RestRequest("/case/lower", Method.Post);
request.AddHeader("User-Agent", "Test Agent");
request.AddHeader("Content-Type", "application/json");
var jsonBody = new {
text = "I LOVE Flutter"
};
request.AddJsonBody(jsonBody);
var response = await client.ExecuteAsync(request);
Console.WriteLine("Status Code: " + (int)response.StatusCode);
Console.WriteLine("Response Content: " + response.Content);
}
catch(Exception ex){
Console.WriteLine("Error: " + ex);
}
}
}
""";
expect(
codegen.getCode(
CodegenLanguage.cSharpRestSharp, requestModelPost3, "https"),
expectedCode);
});
test("Post 4", () {
const expectedCode = r"""
using System;
using RestSharp;
using System.Threading.Tasks;
class Program
{
static async Task Main(){
try{
const String _baseUrl = "https://api.apidash.dev";
var client = new RestClient(_baseUrl);
var request = new RestRequest("/io/form", Method.Post);
request.AlwaysMultipartFormData = true;
request.AddParameter("text", "API", ParameterType.GetOrPost);
request.AddParameter("sep", "|", ParameterType.GetOrPost);
request.AddParameter("times", "3", ParameterType.GetOrPost);
var response = await client.ExecuteAsync(request);
Console.WriteLine("Status Code: " + (int)response.StatusCode);
Console.WriteLine("Response Content: " + response.Content);
}
catch(Exception ex){
Console.WriteLine("Error: " + ex);
}
}
}
""";
expect(
codegen.getCode(
CodegenLanguage.cSharpRestSharp, requestModelPost4, "https"),
expectedCode);
});
test("Post 5", () {
const expectedCode = r"""
using System;
using RestSharp;
using System.Threading.Tasks;
class Program
{
static async Task Main(){
try{
const String _baseUrl = "https://api.apidash.dev";
var client = new RestClient(_baseUrl);
var request = new RestRequest("/io/form", Method.Post);
request.AddHeader("User-Agent", "Test Agent");
request.AlwaysMultipartFormData = true;
request.AddParameter("text", "API", ParameterType.GetOrPost);
request.AddParameter("sep", "|", ParameterType.GetOrPost);
request.AddParameter("times", "3", ParameterType.GetOrPost);
var response = await client.ExecuteAsync(request);
Console.WriteLine("Status Code: " + (int)response.StatusCode);
Console.WriteLine("Response Content: " + response.Content);
}
catch(Exception ex){
Console.WriteLine("Error: " + ex);
}
}
}
""";
expect(
codegen.getCode(
CodegenLanguage.cSharpRestSharp, requestModelPost5, "https"),
expectedCode);
});
test("Post 6", () {
const expectedCode = r"""
using System;
using RestSharp;
using System.Threading.Tasks;
class Program
{
static async Task Main(){
try{
const String _baseUrl = "https://api.apidash.dev";
var client = new RestClient(_baseUrl);
var request = new RestRequest("/io/img", Method.Post);
request.AlwaysMultipartFormData = true;
var options = new FileParameterOptions
{
DisableFilenameEncoding = true
};
request.AddParameter("token", "xyz", ParameterType.GetOrPost);
request.AddFile("imfile", "/Documents/up/1.png", options: options);
var response = await client.ExecuteAsync(request);
Console.WriteLine("Status Code: " + (int)response.StatusCode);
Console.WriteLine("Response Content: " + response.Content);
}
catch(Exception ex){
Console.WriteLine("Error: " + ex);
}
}
}
""";
expect(
codegen.getCode(
CodegenLanguage.cSharpRestSharp, requestModelPost6, "https"),
expectedCode);
});
test("Post 7", () {
const expectedCode = r"""
using System;
using RestSharp;
using System.Threading.Tasks;
class Program
{
static async Task Main(){
try{
const String _baseUrl = "https://api.apidash.dev";
var client = new RestClient(_baseUrl);
var request = new RestRequest("/io/img", Method.Post);
request.AlwaysMultipartFormData = true;
var options = new FileParameterOptions
{
DisableFilenameEncoding = true
};
request.AddParameter("token", "xyz", ParameterType.GetOrPost);
request.AddFile("imfile", "/Documents/up/1.png", options: options);
var response = await client.ExecuteAsync(request);
Console.WriteLine("Status Code: " + (int)response.StatusCode);
Console.WriteLine("Response Content: " + response.Content);
}
catch(Exception ex){
Console.WriteLine("Error: " + ex);
}
}
}
""";
expect(
codegen.getCode(
CodegenLanguage.cSharpRestSharp, requestModelPost7, "https"),
expectedCode);
});
test("Post 8", () {
const expectedCode = r"""
using System;
using RestSharp;
using System.Threading.Tasks;
class Program
{
static async Task Main(){
try{
const String _baseUrl = "https://api.apidash.dev";
var client = new RestClient(_baseUrl);
var request = new RestRequest("/io/form", Method.Post);
request.AddQueryParameter("size", "2");
request.AddQueryParameter("len", "3");
request.AlwaysMultipartFormData = true;
request.AddParameter("text", "API", ParameterType.GetOrPost);
request.AddParameter("sep", "|", ParameterType.GetOrPost);
request.AddParameter("times", "3", ParameterType.GetOrPost);
var response = await client.ExecuteAsync(request);
Console.WriteLine("Status Code: " + (int)response.StatusCode);
Console.WriteLine("Response Content: " + response.Content);
}
catch(Exception ex){
Console.WriteLine("Error: " + ex);
}
}
}
""";
expect(
codegen.getCode(
CodegenLanguage.cSharpRestSharp, requestModelPost8, "https"),
expectedCode);
});
test("Post 9", () {
const expectedCode = r"""
using System;
using RestSharp;
using System.Threading.Tasks;
class Program
{
static async Task Main(){
try{
const String _baseUrl = "https://api.apidash.dev";
var client = new RestClient(_baseUrl);
var request = new RestRequest("/io/img", Method.Post);
request.AddQueryParameter("size", "2");
request.AddQueryParameter("len", "3");
request.AddHeader("User-Agent", "Test Agent");
request.AddHeader("Keep-Alive", "true");
request.AlwaysMultipartFormData = true;
var options = new FileParameterOptions
{
DisableFilenameEncoding = true
};
request.AddParameter("token", "xyz", ParameterType.GetOrPost);
request.AddFile("imfile", "/Documents/up/1.png", options: options);
var response = await client.ExecuteAsync(request);
Console.WriteLine("Status Code: " + (int)response.StatusCode);
Console.WriteLine("Response Content: " + response.Content);
}
catch(Exception ex){
Console.WriteLine("Error: " + ex);
}
}
}
""";
expect(
codegen.getCode(
CodegenLanguage.cSharpRestSharp, requestModelPost9, "https"),
expectedCode);
});
});
group("Put Request Test", () {
test("Put 1", () {
const expectedCode = """
using System;
using RestSharp;
using System.Threading.Tasks;
class Program
{
static async Task Main(){
try{
const String _baseUrl = "https://reqres.in";
var client = new RestClient(_baseUrl);
var request = new RestRequest("/api/users/2", Method.Put);
request.AddHeader("Content-Type", "application/json");
var jsonBody = new {
name = "morpheus",
job = "zion resident"
};
request.AddJsonBody(jsonBody);
var response = await client.ExecuteAsync(request);
Console.WriteLine("Status Code: " + (int)response.StatusCode);
Console.WriteLine("Response Content: " + response.Content);
}
catch(Exception ex){
Console.WriteLine("Error: " + ex);
}
}
}
""";
expect(
codegen.getCode(
CodegenLanguage.cSharpRestSharp, requestModelPut1, "https"),
expectedCode);
});
});
group("Patch Request Test", () {
test("Patch 1", () {
const expectedCode = """
using System;
using RestSharp;
using System.Threading.Tasks;
class Program
{
static async Task Main(){
try{
const String _baseUrl = "https://reqres.in";
var client = new RestClient(_baseUrl);
var request = new RestRequest("/api/users/2", Method.Patch);
request.AddHeader("Content-Type", "application/json");
var jsonBody = new {
name = "marfeus",
job = "accountant"
};
request.AddJsonBody(jsonBody);
var response = await client.ExecuteAsync(request);
Console.WriteLine("Status Code: " + (int)response.StatusCode);
Console.WriteLine("Response Content: " + response.Content);
}
catch(Exception ex){
Console.WriteLine("Error: " + ex);
}
}
}
""";
expect(
codegen.getCode(
CodegenLanguage.cSharpRestSharp, requestModelPatch1, "https"),
expectedCode);
});
});
group("Delete Request Test", () {
test("Delete 1", () {
const expectedCode = """
using System;
using RestSharp;
using System.Threading.Tasks;
class Program
{
static async Task Main(){
try{
const String _baseUrl = "https://reqres.in";
var client = new RestClient(_baseUrl);
var request = new RestRequest("/api/users/2", Method.Delete);
var response = await client.ExecuteAsync(request);
Console.WriteLine("Status Code: " + (int)response.StatusCode);
Console.WriteLine("Response Content: " + response.Content);
}
catch(Exception ex){
Console.WriteLine("Error: " + ex);
}
}
}
""";
expect(
codegen.getCode(
CodegenLanguage.cSharpRestSharp, requestModelDelete1, "https"),
expectedCode);
});
test("Delete 2", () {
const expectedCode = """
using System;
using RestSharp;
using System.Threading.Tasks;
class Program
{
static async Task Main(){
try{
const String _baseUrl = "https://reqres.in";
var client = new RestClient(_baseUrl);
var request = new RestRequest("/api/users/2", Method.Delete);
request.AddHeader("Content-Type", "application/json");
var jsonBody = new {
name = "marfeus",
job = "accountant"
};
request.AddJsonBody(jsonBody);
var response = await client.ExecuteAsync(request);
Console.WriteLine("Status Code: " + (int)response.StatusCode);
Console.WriteLine("Response Content: " + response.Content);
}
catch(Exception ex){
Console.WriteLine("Error: " + ex);
}
}
}
""";
expect(
codegen.getCode(
CodegenLanguage.cSharpRestSharp, requestModelDelete2, "https"),
expectedCode);
});
});
}

File diff suppressed because it is too large Load Diff

View File

@ -8,76 +8,72 @@ void main() {
group('GET Request', () {
test('GET 1', () {
const expectedCode = r"""using HTTP,JSON
const expectedCode = r"""using HTTP
url = "https://api.apidash.dev"
response = HTTP.request("GET", url, status_exception=false)
response = HTTP.get(url)
println("Status Code:", response.status)
println("Response Body:", String(response.body))
println("Status Code: $(response.status) $(HTTP.StatusCodes.statustext(response.status))")
println("Response Body: \n$(String(response.body))")
""";
expect(
codeGen.getCode(CodegenLanguage.juliaHttp, requestModelGet1, "https"),
expectedCode);
});
test('GET 2', () {
const expectedCode = r"""using HTTP,JSON
const expectedCode = r"""using HTTP
url = "https://api.apidash.dev/country/data"
params = Dict(
"code"=> "US"
"code" => "US",
)
response = HTTP.get(url, query=params)
response = HTTP.request("GET", url, query=params, status_exception=false)
println("Status Code:", response.status)
println("Response Body:", String(response.body))
println("Status Code: $(response.status) $(HTTP.StatusCodes.statustext(response.status))")
println("Response Body: \n$(String(response.body))")
""";
expect(
codeGen.getCode(CodegenLanguage.juliaHttp, requestModelGet2, "https"),
expectedCode);
});
test('GET 3', () {
const expectedCode = r"""using HTTP,JSON
const expectedCode = r"""using HTTP
url = "https://api.apidash.dev/country/data"
params = Dict(
"code"=> "IND"
"code" => "IND",
)
response = HTTP.get(url, query=params)
response = HTTP.request("GET", url, query=params, status_exception=false)
println("Status Code:", response.status)
println("Response Body:", String(response.body))
println("Status Code: $(response.status) $(HTTP.StatusCodes.statustext(response.status))")
println("Response Body: \n$(String(response.body))")
""";
expect(
codeGen.getCode(CodegenLanguage.juliaHttp, requestModelGet3, "https"),
expectedCode);
});
test('GET 4', () {
const expectedCode = r"""using HTTP,JSON
const expectedCode = r"""using HTTP
url = "https://api.apidash.dev/humanize/social"
params = Dict(
"num" => "8700000",
"digits" => "3",
"system" => "SS",
"add_space" => "true",
"trailing_zeros"=> "true"
"trailing_zeros" => "true",
)
response = HTTP.get(url, query=params)
response = HTTP.request("GET", url, query=params, status_exception=false)
println("Status Code:", response.status)
println("Response Body:", String(response.body))
println("Status Code: $(response.status) $(HTTP.StatusCodes.statustext(response.status))")
println("Response Body: \n$(String(response.body))")
""";
expect(
codeGen.getCode(CodegenLanguage.juliaHttp, requestModelGet4, "https"),
@ -85,19 +81,18 @@ println("Response Body:", String(response.body))
});
test('GET 5', () {
const expectedCode = r"""using HTTP,JSON
const expectedCode = r"""using HTTP
url = "https://api.github.com/repos/foss42/apidash"
headers = Dict(
"User-Agent"=> "Test Agent"
"User-Agent" => "Test Agent",
)
response = HTTP.get(url, headers=headers)
response = HTTP.request("GET", url, headers=headers, status_exception=false)
println("Status Code:", response.status)
println("Response Body:", String(response.body))
println("Status Code: $(response.status) $(HTTP.StatusCodes.statustext(response.status))")
println("Response Body: \n$(String(response.body))")
""";
expect(
codeGen.getCode(CodegenLanguage.juliaHttp, requestModelGet5, "https"),
@ -105,23 +100,22 @@ println("Response Body:", String(response.body))
});
test('GET 6', () {
const expectedCode = r"""using HTTP,JSON
const expectedCode = r"""using HTTP
url = "https://api.github.com/repos/foss42/apidash"
params = Dict(
"raw"=> "true"
"raw" => "true",
)
headers = Dict(
"User-Agent"=> "Test Agent"
"User-Agent" => "Test Agent",
)
response = HTTP.get(url, query=params, headers=headers)
response = HTTP.request("GET", url, headers=headers, query=params, status_exception=false)
println("Status Code:", response.status)
println("Response Body:", String(response.body))
println("Status Code: $(response.status) $(HTTP.StatusCodes.statustext(response.status))")
println("Response Body: \n$(String(response.body))")
""";
expect(
codeGen.getCode(CodegenLanguage.juliaHttp, requestModelGet6, "https"),
@ -129,15 +123,14 @@ println("Response Body:", String(response.body))
});
test('GET 7', () {
const expectedCode = r"""using HTTP,JSON
const expectedCode = r"""using HTTP
url = "https://api.apidash.dev"
response = HTTP.request("GET", url, status_exception=false)
response = HTTP.get(url)
println("Status Code:", response.status)
println("Response Body:", String(response.body))
println("Status Code: $(response.status) $(HTTP.StatusCodes.statustext(response.status))")
println("Response Body: \n$(String(response.body))")
""";
expect(
codeGen.getCode(CodegenLanguage.juliaHttp, requestModelGet7, "https"),
@ -145,23 +138,22 @@ println("Response Body:", String(response.body))
});
test('GET 8', () {
const expectedCode = r"""using HTTP,JSON
const expectedCode = r"""using HTTP
url = "https://api.github.com/repos/foss42/apidash"
params = Dict(
"raw"=> "true"
"raw" => "true",
)
headers = Dict(
"User-Agent"=> "Test Agent"
"User-Agent" => "Test Agent",
)
response = HTTP.get(url, query=params, headers=headers)
response = HTTP.request("GET", url, headers=headers, query=params, status_exception=false)
println("Status Code:", response.status)
println("Response Body:", String(response.body))
println("Status Code: $(response.status) $(HTTP.StatusCodes.statustext(response.status))")
println("Response Body: \n$(String(response.body))")
""";
expect(
codeGen.getCode(CodegenLanguage.juliaHttp, requestModelGet8, "https"),
@ -169,20 +161,19 @@ println("Response Body:", String(response.body))
});
test('GET 9', () {
const expectedCode = r"""using HTTP,JSON
const expectedCode = r"""using HTTP
url = "https://api.apidash.dev/humanize/social"
params = Dict(
"num" => "8700000",
"add_space"=> "true"
"add_space" => "true",
)
response = HTTP.get(url, query=params)
response = HTTP.request("GET", url, query=params, status_exception=false)
println("Status Code:", response.status)
println("Response Body:", String(response.body))
println("Status Code: $(response.status) $(HTTP.StatusCodes.statustext(response.status))")
println("Response Body: \n$(String(response.body))")
""";
expect(
codeGen.getCode(CodegenLanguage.juliaHttp, requestModelGet9, "https"),
@ -190,19 +181,18 @@ println("Response Body:", String(response.body))
});
test('GET 10', () {
const expectedCode = r"""using HTTP,JSON
const expectedCode = r"""using HTTP
url = "https://api.apidash.dev/humanize/social"
headers = Dict(
"User-Agent"=> "Test Agent"
"User-Agent" => "Test Agent",
)
response = HTTP.get(url, headers=headers)
response = HTTP.request("GET", url, headers=headers, status_exception=false)
println("Status Code:", response.status)
println("Response Body:", String(response.body))
println("Status Code: $(response.status) $(HTTP.StatusCodes.statustext(response.status))")
println("Response Body: \n$(String(response.body))")
""";
expect(
codeGen.getCode(
@ -211,24 +201,23 @@ println("Response Body:", String(response.body))
});
test('GET 11', () {
const expectedCode = r"""using HTTP,JSON
const expectedCode = r"""using HTTP
url = "https://api.apidash.dev/humanize/social"
params = Dict(
"num" => "8700000",
"digits"=> "3"
"digits" => "3",
)
headers = Dict(
"User-Agent"=> "Test Agent"
"User-Agent" => "Test Agent",
)
response = HTTP.get(url, query=params, headers=headers)
response = HTTP.request("GET", url, headers=headers, query=params, status_exception=false)
println("Status Code:", response.status)
println("Response Body:", String(response.body))
println("Status Code: $(response.status) $(HTTP.StatusCodes.statustext(response.status))")
println("Response Body: \n$(String(response.body))")
""";
expect(
codeGen.getCode(
@ -237,15 +226,14 @@ println("Response Body:", String(response.body))
});
test('GET 12', () {
const expectedCode = r"""using HTTP,JSON
const expectedCode = r"""using HTTP
url = "https://api.apidash.dev/humanize/social"
response = HTTP.request("GET", url, status_exception=false)
response = HTTP.get(url)
println("Status Code:", response.status)
println("Response Body:", String(response.body))
println("Status Code: $(response.status) $(HTTP.StatusCodes.statustext(response.status))")
println("Response Body: \n$(String(response.body))")
""";
expect(
codeGen.getCode(
@ -256,15 +244,14 @@ println("Response Body:", String(response.body))
group('HEAD Request', () {
test('HEAD 1', () {
const expectedCode = r"""using HTTP,JSON
const expectedCode = r"""using HTTP
url = "https://api.apidash.dev"
response = HTTP.request("HEAD", url, status_exception=false)
response = HTTP.head(url)
println("Status Code:", response.status)
println("Response Body:", String(response.body))
println("Status Code: $(response.status) $(HTTP.StatusCodes.statustext(response.status))")
println("Response Body: \n$(String(response.body))")
""";
expect(
codeGen.getCode(
@ -273,43 +260,40 @@ println("Response Body:", String(response.body))
});
test('HEAD 2', () {
const expectedCode = r"""using HTTP,JSON
const expectedCode = r"""using HTTP
url = "https://api.apidash.dev"
url = "http://api.apidash.dev"
response = HTTP.request("HEAD", url, status_exception=false)
response = HTTP.head(url)
println("Status Code:", response.status)
println("Response Body:", String(response.body))
println("Status Code: $(response.status) $(HTTP.StatusCodes.statustext(response.status))")
println("Response Body: \n$(String(response.body))")
""";
expect(
codeGen.getCode(
CodegenLanguage.juliaHttp, requestModelHead2, "https"),
codeGen.getCode(CodegenLanguage.juliaHttp, requestModelHead2, "http"),
expectedCode);
});
});
group('POST Request', () {
test('POST 1', () {
const expectedCode = r"""using HTTP,JSON
const expectedCode = r'''using HTTP
url = "https://api.apidash.dev/case/lower"
payload = Dict(
"text"=> "I LOVE Flutter"
)
payload = """{
"text": "I LOVE Flutter"
}"""
headers = Dict(
"content-type"=> "text/plain"
"content-type" => "text/plain",
)
response = HTTP.post(url, payload=payload, headers=headers)
response = HTTP.request("POST", url, headers=headers, body=payload, status_exception=false)
println("Status Code:", response.status)
println("Response Body:", String(response.body))
""";
println("Status Code: $(response.status) $(HTTP.StatusCodes.statustext(response.status))")
println("Response Body: \n$(String(response.body))")
''';
expect(
codeGen.getCode(
CodegenLanguage.juliaHttp, requestModelPost1, "https"),
@ -317,72 +301,234 @@ println("Response Body:", String(response.body))
});
test('POST 2', () {
const expectedCode = r"""using HTTP,JSON
const expectedCode = r'''using HTTP
url = "https://api.apidash.dev/case/lower"
payload = """{
"text": "I LOVE Flutter",
"flag": null,
"male": true,
"female": false,
"no": 1.2,
"arr": ["null", "true", "false", null]
}"""
payload = Dict(
"text"=> "I LOVE Flutter",
"flag"=> null,
"male"=> true,
"female"=> false,
"no"=> 1.2,
"arr"=> ["null", "true", "false", null]
headers = Dict(
"content-type" => "application/json",
)
response = HTTP.post(url, JSON.json(payload))
response = HTTP.request("POST", url, headers=headers, body=payload, status_exception=false)
println("Status Code:", response.status)
println("Response Body:", String(response.body))
""";
println("Status Code: $(response.status) $(HTTP.StatusCodes.statustext(response.status))")
println("Response Body: \n$(String(response.body))")
''';
expect(
codeGen.getCode(
CodegenLanguage.juliaHttp, requestModelPost2, "https"),
expectedCode);
});
test('POST 3', () {
const expectedCode = r"""using HTTP,JSON
const expectedCode = r'''using HTTP
url = "https://api.apidash.dev/case/lower"
payload = Dict(
"text"=> "I LOVE Flutter"
)
payload = """{
"text": "I LOVE Flutter"
}"""
headers = Dict(
"User-Agent"=> "Test Agent"
"User-Agent" => "Test Agent",
"content-type" => "application/json",
)
response = HTTP.post(url, JSON.json(payload), headers=headers)
response = HTTP.request("POST", url, headers=headers, body=payload, status_exception=false)
println("Status Code:", response.status)
println("Response Body:", String(response.body))
""";
println("Status Code: $(response.status) $(HTTP.StatusCodes.statustext(response.status))")
println("Response Body: \n$(String(response.body))")
''';
expect(
codeGen.getCode(
CodegenLanguage.juliaHttp, requestModelPost3, "https"),
expectedCode);
});
test('POST 4', () {
const expectedCode = r"""using HTTP
url = "https://api.apidash.dev/io/form"
data = Dict(
"text" => "API",
"sep" => "|",
"times" => "3",
)
payload = HTTP.Form(data)
response = HTTP.request("POST", url, body=payload, status_exception=false)
println("Status Code: $(response.status) $(HTTP.StatusCodes.statustext(response.status))")
println("Response Body: \n$(String(response.body))")
""";
expect(
codeGen.getCode(
CodegenLanguage.juliaHttp, requestModelPost4, "https"),
expectedCode);
});
test('POST 5', () {
const expectedCode = r"""using HTTP
url = "https://api.apidash.dev/io/form"
data = Dict(
"text" => "API",
"sep" => "|",
"times" => "3",
)
payload = HTTP.Form(data)
headers = Dict(
"User-Agent" => "Test Agent",
)
response = HTTP.request("POST", url, headers=headers, body=payload, status_exception=false)
println("Status Code: $(response.status) $(HTTP.StatusCodes.statustext(response.status))")
println("Response Body: \n$(String(response.body))")
""";
expect(
codeGen.getCode(
CodegenLanguage.juliaHttp, requestModelPost5, "https"),
expectedCode);
});
test('POST 6', () {
const expectedCode = r"""using HTTP
url = "https://api.apidash.dev/io/img"
data = Dict(
"token" => "xyz",
"imfile" => open("/Documents/up/1.png"),
)
payload = HTTP.Form(data)
response = HTTP.request("POST", url, body=payload, status_exception=false)
println("Status Code: $(response.status) $(HTTP.StatusCodes.statustext(response.status))")
println("Response Body: \n$(String(response.body))")
""";
expect(
codeGen.getCode(
CodegenLanguage.juliaHttp, requestModelPost6, "https"),
expectedCode);
});
test('POST 7', () {
const expectedCode = r"""using HTTP
url = "https://api.apidash.dev/io/img"
data = Dict(
"token" => "xyz",
"imfile" => open("/Documents/up/1.png"),
)
payload = HTTP.Form(data)
response = HTTP.request("POST", url, body=payload, status_exception=false)
println("Status Code: $(response.status) $(HTTP.StatusCodes.statustext(response.status))")
println("Response Body: \n$(String(response.body))")
""";
expect(
codeGen.getCode(
CodegenLanguage.juliaHttp, requestModelPost7, "https"),
expectedCode);
});
test('POST 8', () {
const expectedCode = r"""using HTTP
url = "https://api.apidash.dev/io/form"
params = Dict(
"size" => "2",
"len" => "3",
)
data = Dict(
"text" => "API",
"sep" => "|",
"times" => "3",
)
payload = HTTP.Form(data)
response = HTTP.request("POST", url, body=payload, query=params, status_exception=false)
println("Status Code: $(response.status) $(HTTP.StatusCodes.statustext(response.status))")
println("Response Body: \n$(String(response.body))")
""";
expect(
codeGen.getCode(
CodegenLanguage.juliaHttp, requestModelPost8, "https"),
expectedCode);
});
test('POST 9', () {
const expectedCode = r"""using HTTP
url = "https://api.apidash.dev/io/img"
params = Dict(
"size" => "2",
"len" => "3",
)
data = Dict(
"token" => "xyz",
"imfile" => open("/Documents/up/1.png"),
)
payload = HTTP.Form(data)
headers = Dict(
"User-Agent" => "Test Agent",
"Keep-Alive" => "true",
)
response = HTTP.request("POST", url, headers=headers, body=payload, query=params, status_exception=false)
println("Status Code: $(response.status) $(HTTP.StatusCodes.statustext(response.status))")
println("Response Body: \n$(String(response.body))")
""";
expect(
codeGen.getCode(
CodegenLanguage.juliaHttp, requestModelPost9, "https"),
expectedCode);
});
});
group('PUT Request', () {
test('PUT 1', () {
const expectedCode = r"""using HTTP,JSON
const expectedCode = r'''using HTTP
url = "https://reqres.in/api/users/2"
payload = """{
"name": "morpheus",
"job": "zion resident"
}"""
payload = Dict(
"name"=> "morpheus",
"job"=> "zion resident"
headers = Dict(
"content-type" => "application/json",
)
response = HTTP.put(url, JSON.json(payload))
response = HTTP.request("PUT", url, headers=headers, body=payload, status_exception=false)
println("Status Code:", response.status)
println("Response Body:", String(response.body))
""";
println("Status Code: $(response.status) $(HTTP.StatusCodes.statustext(response.status))")
println("Response Body: \n$(String(response.body))")
''';
expect(
codeGen.getCode(CodegenLanguage.juliaHttp, requestModelPut1, "https"),
expectedCode);
@ -390,21 +536,24 @@ println("Response Body:", String(response.body))
});
group('PATCH Request', () {
test('PATCH 1', () {
const expectedCode = r"""using HTTP,JSON
const expectedCode = r'''using HTTP
url = "https://reqres.in/api/users/2"
payload = """{
"name": "marfeus",
"job": "accountant"
}"""
payload = Dict(
"name"=> "marfeus",
"job"=> "accountant"
headers = Dict(
"content-type" => "application/json",
)
response = HTTP.patch(url, JSON.json(payload))
response = HTTP.request("PATCH", url, headers=headers, body=payload, status_exception=false)
println("Status Code:", response.status)
println("Response Body:", String(response.body))
""";
println("Status Code: $(response.status) $(HTTP.StatusCodes.statustext(response.status))")
println("Response Body: \n$(String(response.body))")
''';
expect(
codeGen.getCode(
CodegenLanguage.juliaHttp, requestModelPatch1, "https"),
@ -413,15 +562,14 @@ println("Response Body:", String(response.body))
});
group('DELETE Request', () {
test('DELETE 1', () {
const expectedCode = r"""using HTTP,JSON
const expectedCode = r"""using HTTP
url = "https://reqres.in/api/users/2"
response = HTTP.request("DELETE", url, status_exception=false)
response = HTTP.delete(url)
println("Status Code:", response.status)
println("Response Body:", String(response.body))
println("Status Code: $(response.status) $(HTTP.StatusCodes.statustext(response.status))")
println("Response Body: \n$(String(response.body))")
""";
expect(
codeGen.getCode(
@ -429,21 +577,24 @@ println("Response Body:", String(response.body))
expectedCode);
});
test('DELETE 2', () {
const expectedCode = r"""using HTTP,JSON
const expectedCode = r'''using HTTP
url = "https://reqres.in/api/users/2"
payload = """{
"name": "marfeus",
"job": "accountant"
}"""
payload = Dict(
"name"=> "marfeus",
"job"=> "accountant"
headers = Dict(
"content-type" => "application/json",
)
response = HTTP.delete(url, JSON.json(payload))
response = HTTP.request("DELETE", url, headers=headers, body=payload, status_exception=false)
println("Status Code:", response.status)
println("Response Body:", String(response.body))
""";
println("Status Code: $(response.status) $(HTTP.StatusCodes.statustext(response.status))")
println("Response Body: \n$(String(response.body))")
''';
expect(
codeGen.getCode(
CodegenLanguage.juliaHttp, requestModelDelete2, "https"),

View File

@ -0,0 +1,718 @@
import 'package:apidash/codegen/codegen.dart';
import 'package:apidash/consts.dart';
import 'package:test/test.dart';
import '../request_models.dart';
void main() {
final codeGen = Codegen();
group('GET Request', () {
test('GET 1', () {
const expectedCode = r"""require 'uri'
require 'faraday'
REQUEST_URL = URI("https://api.apidash.dev")
conn = Faraday.new do |faraday|
faraday.adapter Faraday.default_adapter
end
response = conn.get(REQUEST_URL) do |req|
end
puts "Status Code: #{response.status}"
puts "Response Body: #{response.body}"
""";
expect(codeGen.getCode(CodegenLanguage.rubyFaraday, requestModelGet1, "https"), expectedCode);
});
test('GET 2', () {
const expectedCode = r"""require 'uri'
require 'faraday'
REQUEST_URL = URI("https://api.apidash.dev/country/data")
conn = Faraday.new do |faraday|
faraday.adapter Faraday.default_adapter
end
response = conn.get(REQUEST_URL) do |req|
req.params = {
"code" => "US",
}
end
puts "Status Code: #{response.status}"
puts "Response Body: #{response.body}"
""";
expect(codeGen.getCode(CodegenLanguage.rubyFaraday, requestModelGet2, "https"), expectedCode);
});
test('GET 3', () {
const expectedCode = r"""require 'uri'
require 'faraday'
REQUEST_URL = URI("https://api.apidash.dev/country/data")
conn = Faraday.new do |faraday|
faraday.adapter Faraday.default_adapter
end
response = conn.get(REQUEST_URL) do |req|
req.params = {
"code" => "IND",
}
end
puts "Status Code: #{response.status}"
puts "Response Body: #{response.body}"
""";
expect(codeGen.getCode(CodegenLanguage.rubyFaraday, requestModelGet3, "https"), expectedCode);
});
test('GET 4', () {
const expectedCode = r"""require 'uri'
require 'faraday'
REQUEST_URL = URI("https://api.apidash.dev/humanize/social")
conn = Faraday.new do |faraday|
faraday.adapter Faraday.default_adapter
end
response = conn.get(REQUEST_URL) do |req|
req.params = {
"num" => "8700000",
"digits" => "3",
"system" => "SS",
"add_space" => "true",
"trailing_zeros" => "true",
}
end
puts "Status Code: #{response.status}"
puts "Response Body: #{response.body}"
""";
expect(codeGen.getCode(CodegenLanguage.rubyFaraday, requestModelGet4, "https"), expectedCode);
});
test('GET 5', () {
const expectedCode = r"""require 'uri'
require 'faraday'
REQUEST_URL = URI("https://api.github.com/repos/foss42/apidash")
conn = Faraday.new do |faraday|
faraday.adapter Faraday.default_adapter
end
response = conn.get(REQUEST_URL) do |req|
req.headers = {
"User-Agent" => "Test Agent",
}
end
puts "Status Code: #{response.status}"
puts "Response Body: #{response.body}"
""";
expect(codeGen.getCode(CodegenLanguage.rubyFaraday, requestModelGet5, "https"), expectedCode);
});
test('GET 6', () {
const expectedCode = r"""require 'uri'
require 'faraday'
REQUEST_URL = URI("https://api.github.com/repos/foss42/apidash")
conn = Faraday.new do |faraday|
faraday.adapter Faraday.default_adapter
end
response = conn.get(REQUEST_URL) do |req|
req.headers = {
"User-Agent" => "Test Agent",
}
req.params = {
"raw" => "true",
}
end
puts "Status Code: #{response.status}"
puts "Response Body: #{response.body}"
""";
expect(codeGen.getCode(CodegenLanguage.rubyFaraday, requestModelGet6, "https"), expectedCode);
});
test('GET 7', () {
const expectedCode = r"""require 'uri'
require 'faraday'
REQUEST_URL = URI("https://api.apidash.dev")
conn = Faraday.new do |faraday|
faraday.adapter Faraday.default_adapter
end
response = conn.get(REQUEST_URL) do |req|
end
puts "Status Code: #{response.status}"
puts "Response Body: #{response.body}"
""";
expect(codeGen.getCode(CodegenLanguage.rubyFaraday, requestModelGet7, "https"), expectedCode);
});
test('GET 8', () {
const expectedCode = r"""require 'uri'
require 'faraday'
REQUEST_URL = URI("https://api.github.com/repos/foss42/apidash")
conn = Faraday.new do |faraday|
faraday.adapter Faraday.default_adapter
end
response = conn.get(REQUEST_URL) do |req|
req.headers = {
"User-Agent" => "Test Agent",
}
req.params = {
"raw" => "true",
}
end
puts "Status Code: #{response.status}"
puts "Response Body: #{response.body}"
""";
expect(codeGen.getCode(CodegenLanguage.rubyFaraday, requestModelGet8, "https"), expectedCode);
});
test('GET 9', () {
const expectedCode = r"""require 'uri'
require 'faraday'
REQUEST_URL = URI("https://api.apidash.dev/humanize/social")
conn = Faraday.new do |faraday|
faraday.adapter Faraday.default_adapter
end
response = conn.get(REQUEST_URL) do |req|
req.params = {
"num" => "8700000",
"add_space" => "true",
}
end
puts "Status Code: #{response.status}"
puts "Response Body: #{response.body}"
""";
expect(codeGen.getCode(CodegenLanguage.rubyFaraday, requestModelGet9, "https"), expectedCode);
});
test('GET 10', () {
const expectedCode = r"""require 'uri'
require 'faraday'
REQUEST_URL = URI("https://api.apidash.dev/humanize/social")
conn = Faraday.new do |faraday|
faraday.adapter Faraday.default_adapter
end
response = conn.get(REQUEST_URL) do |req|
req.headers = {
"User-Agent" => "Test Agent",
}
end
puts "Status Code: #{response.status}"
puts "Response Body: #{response.body}"
""";
expect(
codeGen.getCode(
CodegenLanguage.rubyFaraday,
requestModelGet10,
"https",
),
expectedCode);
});
test('GET 11', () {
const expectedCode = r"""require 'uri'
require 'faraday'
REQUEST_URL = URI("https://api.apidash.dev/humanize/social")
conn = Faraday.new do |faraday|
faraday.adapter Faraday.default_adapter
end
response = conn.get(REQUEST_URL) do |req|
req.headers = {
"User-Agent" => "Test Agent",
}
req.params = {
"num" => "8700000",
"digits" => "3",
}
end
puts "Status Code: #{response.status}"
puts "Response Body: #{response.body}"
""";
expect(codeGen.getCode(CodegenLanguage.rubyFaraday, requestModelGet11, "https"), expectedCode);
});
test('GET 12', () {
const expectedCode = r"""require 'uri'
require 'faraday'
REQUEST_URL = URI("https://api.apidash.dev/humanize/social")
conn = Faraday.new do |faraday|
faraday.adapter Faraday.default_adapter
end
response = conn.get(REQUEST_URL) do |req|
end
puts "Status Code: #{response.status}"
puts "Response Body: #{response.body}"
""";
expect(codeGen.getCode(CodegenLanguage.rubyFaraday, requestModelGet12, "https"), expectedCode);
});
});
group('HEAD Request', () {
test('HEAD 1', () {
const expectedCode = r"""require 'uri'
require 'faraday'
REQUEST_URL = URI("https://api.apidash.dev")
conn = Faraday.new do |faraday|
faraday.adapter Faraday.default_adapter
end
response = conn.head(REQUEST_URL) do |req|
end
puts "Status Code: #{response.status}"
puts "Response Body: #{response.body}"
""";
expect(codeGen.getCode(CodegenLanguage.rubyFaraday, requestModelHead1, "https"), expectedCode);
});
test('HEAD 2', () {
const expectedCode = r"""require 'uri'
require 'faraday'
REQUEST_URL = URI("http://api.apidash.dev")
conn = Faraday.new do |faraday|
faraday.adapter Faraday.default_adapter
end
response = conn.head(REQUEST_URL) do |req|
end
puts "Status Code: #{response.status}"
puts "Response Body: #{response.body}"
""";
expect(codeGen.getCode(CodegenLanguage.rubyFaraday, requestModelHead2, "http"), expectedCode);
});
});
group('POST Request', () {
test('POST 1', () {
const expectedCode = r"""require 'uri'
require 'faraday'
REQUEST_URL = URI("https://api.apidash.dev/case/lower")
PAYLOAD = <<HEREDOC
{
"text": "I LOVE Flutter"
}
HEREDOC
conn = Faraday.new do |faraday|
faraday.adapter Faraday.default_adapter
end
response = conn.post(REQUEST_URL, PAYLOAD) do |req|
req.headers = {
"Content-Type" => "text/plain",
}
end
puts "Status Code: #{response.status}"
puts "Response Body: #{response.body}"
""";
expect(
codeGen.getCode(CodegenLanguage.rubyFaraday, requestModelPost1, "https"),
expectedCode);
});
test('POST 2', () {
const expectedCode = r"""require 'uri'
require 'faraday'
REQUEST_URL = URI("https://api.apidash.dev/case/lower")
PAYLOAD = <<HEREDOC
{
"text": "I LOVE Flutter",
"flag": null,
"male": true,
"female": false,
"no": 1.2,
"arr": ["null", "true", "false", null]
}
HEREDOC
conn = Faraday.new do |faraday|
faraday.adapter Faraday.default_adapter
end
response = conn.post(REQUEST_URL, PAYLOAD) do |req|
req.headers = {
"Content-Type" => "application/json",
}
end
puts "Status Code: #{response.status}"
puts "Response Body: #{response.body}"
""";
expect(
codeGen.getCode(CodegenLanguage.rubyFaraday, requestModelPost2, "https"),
expectedCode);
});
test('POST 3', () {
const expectedCode = r"""require 'uri'
require 'faraday'
REQUEST_URL = URI("https://api.apidash.dev/case/lower")
PAYLOAD = <<HEREDOC
{
"text": "I LOVE Flutter"
}
HEREDOC
conn = Faraday.new do |faraday|
faraday.adapter Faraday.default_adapter
end
response = conn.post(REQUEST_URL, PAYLOAD) do |req|
req.headers = {
"User-Agent" => "Test Agent",
"Content-Type" => "application/json",
}
end
puts "Status Code: #{response.status}"
puts "Response Body: #{response.body}"
""";
expect(
codeGen.getCode(CodegenLanguage.rubyFaraday, requestModelPost3, "https"),
expectedCode);
});
test('POST 4', () {
const expectedCode = r"""require 'uri'
require 'faraday'
REQUEST_URL = URI("https://api.apidash.dev/io/form")
PAYLOAD = URI.encode_www_form({
"text" => "API",
"sep" => "|",
"times" => "3",
})
conn = Faraday.new do |faraday|
faraday.adapter Faraday.default_adapter
end
response = conn.post(REQUEST_URL, PAYLOAD) do |req|
end
puts "Status Code: #{response.status}"
puts "Response Body: #{response.body}"
""";
expect(
codeGen.getCode(
CodegenLanguage.rubyFaraday,
requestModelPost4,
"https",
),
expectedCode);
});
test('POST 5', () {
const expectedCode = r"""require 'uri'
require 'faraday'
REQUEST_URL = URI("https://api.apidash.dev/io/form")
PAYLOAD = URI.encode_www_form({
"text" => "API",
"sep" => "|",
"times" => "3",
})
conn = Faraday.new do |faraday|
faraday.adapter Faraday.default_adapter
end
response = conn.post(REQUEST_URL, PAYLOAD) do |req|
req.headers = {
"User-Agent" => "Test Agent",
}
end
puts "Status Code: #{response.status}"
puts "Response Body: #{response.body}"
""";
expect(codeGen.getCode(CodegenLanguage.rubyFaraday, requestModelPost5, "https"), expectedCode);
});
test('POST 6', () {
const expectedCode = r"""require 'uri'
require 'faraday'
require 'faraday/multipart'
REQUEST_URL = URI("https://api.apidash.dev/io/img")
PAYLOAD = {
"token" => Faraday::Multipart::ParamPart.new("xyz", "text/plain"),
"imfile" => Faraday::Multipart::FilePart.new("/Documents/up/1.png", "application/octet-stream"),
}
conn = Faraday.new do |faraday|
faraday.adapter Faraday.default_adapter
faraday.request :multipart
end
response = conn.post(REQUEST_URL, PAYLOAD) do |req|
end
puts "Status Code: #{response.status}"
puts "Response Body: #{response.body}"
""";
expect(
codeGen.getCode(CodegenLanguage.rubyFaraday, requestModelPost6, "https"),
expectedCode);
});
test('POST 7', () {
const expectedCode = r"""require 'uri'
require 'faraday'
require 'faraday/multipart'
REQUEST_URL = URI("https://api.apidash.dev/io/img")
PAYLOAD = {
"token" => Faraday::Multipart::ParamPart.new("xyz", "text/plain"),
"imfile" => Faraday::Multipart::FilePart.new("/Documents/up/1.png", "application/octet-stream"),
}
conn = Faraday.new do |faraday|
faraday.adapter Faraday.default_adapter
faraday.request :multipart
end
response = conn.post(REQUEST_URL, PAYLOAD) do |req|
end
puts "Status Code: #{response.status}"
puts "Response Body: #{response.body}"
""";
expect(
codeGen.getCode(CodegenLanguage.rubyFaraday, requestModelPost7, "https"),
expectedCode);
});
test('POST 8', () {
const expectedCode = r"""require 'uri'
require 'faraday'
REQUEST_URL = URI("https://api.apidash.dev/io/form")
PAYLOAD = URI.encode_www_form({
"text" => "API",
"sep" => "|",
"times" => "3",
})
conn = Faraday.new do |faraday|
faraday.adapter Faraday.default_adapter
end
response = conn.post(REQUEST_URL, PAYLOAD) do |req|
req.params = {
"size" => "2",
"len" => "3",
}
end
puts "Status Code: #{response.status}"
puts "Response Body: #{response.body}"
""";
expect(codeGen.getCode(CodegenLanguage.rubyFaraday, requestModelPost8, "https"), expectedCode);
});
test('POST 9', () {
const expectedCode = r"""require 'uri'
require 'faraday'
require 'faraday/multipart'
REQUEST_URL = URI("https://api.apidash.dev/io/img")
PAYLOAD = {
"token" => Faraday::Multipart::ParamPart.new("xyz", "text/plain"),
"imfile" => Faraday::Multipart::FilePart.new("/Documents/up/1.png", "application/octet-stream"),
}
conn = Faraday.new do |faraday|
faraday.adapter Faraday.default_adapter
faraday.request :multipart
end
response = conn.post(REQUEST_URL, PAYLOAD) do |req|
req.headers = {
"User-Agent" => "Test Agent",
"Keep-Alive" => "true",
}
req.params = {
"size" => "2",
"len" => "3",
}
end
puts "Status Code: #{response.status}"
puts "Response Body: #{response.body}"
""";
expect(
codeGen.getCode(CodegenLanguage.rubyFaraday, requestModelPost9, "https"),
expectedCode);
});
});
group('PUT Request', () {
test('PUT 1', () {
const expectedCode = r"""require 'uri'
require 'faraday'
REQUEST_URL = URI("https://reqres.in/api/users/2")
PAYLOAD = <<HEREDOC
{
"name": "morpheus",
"job": "zion resident"
}
HEREDOC
conn = Faraday.new do |faraday|
faraday.adapter Faraday.default_adapter
end
response = conn.put(REQUEST_URL, PAYLOAD) do |req|
req.headers = {
"Content-Type" => "application/json",
}
end
puts "Status Code: #{response.status}"
puts "Response Body: #{response.body}"
""";
expect(
codeGen.getCode(CodegenLanguage.rubyFaraday, requestModelPut1, "https"),
expectedCode);
});
});
group('PATCH Request', () {
test('PATCH 1', () {
const expectedCode = r"""require 'uri'
require 'faraday'
REQUEST_URL = URI("https://reqres.in/api/users/2")
PAYLOAD = <<HEREDOC
{
"name": "marfeus",
"job": "accountant"
}
HEREDOC
conn = Faraday.new do |faraday|
faraday.adapter Faraday.default_adapter
end
response = conn.patch(REQUEST_URL, PAYLOAD) do |req|
req.headers = {
"Content-Type" => "application/json",
}
end
puts "Status Code: #{response.status}"
puts "Response Body: #{response.body}"
""";
expect(
codeGen.getCode(CodegenLanguage.rubyFaraday, requestModelPatch1, "https"),
expectedCode);
});
});
group('DELETE Request', () {
test('DELETE 1', () {
const expectedCode = r"""require 'uri'
require 'faraday'
REQUEST_URL = URI("https://reqres.in/api/users/2")
conn = Faraday.new do |faraday|
faraday.adapter Faraday.default_adapter
end
response = conn.delete(REQUEST_URL) do |req|
end
puts "Status Code: #{response.status}"
puts "Response Body: #{response.body}"
""";
expect(codeGen.getCode(CodegenLanguage.rubyFaraday, requestModelDelete1, "https"), expectedCode);
});
test('DELETE 2', () {
const expectedCode = r"""require 'uri'
require 'faraday'
REQUEST_URL = URI("https://reqres.in/api/users/2")
PAYLOAD = <<HEREDOC
{
"name": "marfeus",
"job": "accountant"
}
HEREDOC
conn = Faraday.new do |faraday|
faraday.adapter Faraday.default_adapter
end
response = conn.delete(REQUEST_URL) do |req|
req.headers = {
"Content-Type" => "application/json",
}
req.body = PAYLOAD
end
puts "Status Code: #{response.status}"
puts "Response Body: #{response.body}"
""";
expect(
codeGen.getCode(CodegenLanguage.rubyFaraday, requestModelDelete2, "https"),
expectedCode);
});
});
}

View File

@ -0,0 +1,614 @@
import 'dart:io';
import 'package:apidash/providers/providers.dart';
import 'package:apidash/screens/dashboard.dart';
import 'package:apidash/screens/home_page/collection_pane.dart';
import 'package:apidash/screens/home_page/editor_pane/details_card/code_pane.dart';
import 'package:apidash/screens/home_page/editor_pane/details_card/response_pane.dart';
import 'package:apidash/screens/home_page/editor_pane/editor_default.dart';
import 'package:apidash/screens/home_page/editor_pane/editor_pane.dart';
import 'package:apidash/screens/home_page/editor_pane/url_card.dart';
import 'package:apidash/screens/home_page/home_page.dart';
import 'package:apidash/screens/intro_page.dart';
import 'package:apidash/screens/settings_page.dart';
import 'package:apidash/services/hive_services.dart';
import 'package:apidash/widgets/response_widgets.dart';
import 'package:apidash/widgets/textfields.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_test/flutter_test.dart';
import '../test_consts.dart';
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
setUp(() async {
const MethodChannel channel =
MethodChannel('plugins.flutter.io/path_provider');
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
.setMockMethodCallHandler(channel, (MethodCall methodCall) async {
if (methodCall.method == 'getApplicationDocumentsDirectory') {
// Create a mock app doc directory for testing
Directory tempDir =
await Directory.systemTemp.createTemp('mock_app_doc_dir');
return tempDir.path; // Return the path to the mock directory
}
return null;
});
await openBoxes();
});
group('Testing navRailIndexStateProvider', () {
testWidgets('Dashboard should display correct initial page',
(tester) async {
await tester.pumpWidget(
const ProviderScope(
child: MaterialApp(
home: Dashboard(),
),
),
);
// Verify that the HomePage is displayed initially
expect(find.byType(HomePage), findsOneWidget);
expect(find.byType(IntroPage), findsNothing);
expect(find.byType(SettingsPage), findsNothing);
});
testWidgets(
"Dashboard should display IntroPage when navRailIndexStateProvider is 1",
(WidgetTester tester) async {
await tester.pumpWidget(
ProviderScope(
overrides: [
navRailIndexStateProvider.overrideWith((ref) => 1),
],
child: const MaterialApp(
home: Dashboard(),
),
),
);
// Verify that the IntroPage is displayed
expect(find.byType(IntroPage), findsOneWidget);
expect(find.byType(HomePage), findsNothing);
expect(find.byType(SettingsPage), findsNothing);
});
testWidgets(
"Dashboard should display SettingsPage when navRailIndexStateProvider is 2",
(WidgetTester tester) async {
await tester.pumpWidget(
ProviderScope(
overrides: [
navRailIndexStateProvider.overrideWith((ref) => 2),
],
child: const MaterialApp(
home: Dashboard(),
),
),
);
// Verify that the SettingsPage is displayed
expect(find.byType(SettingsPage), findsOneWidget);
expect(find.byType(IntroPage), findsNothing);
expect(find.byType(HomePage), findsNothing);
});
testWidgets(
'navRailIndexStateProvider should update when icon button is pressed',
(tester) async {
await tester.pumpWidget(
const ProviderScope(
child: MaterialApp(
home: Dashboard(),
),
),
);
// Tap on the Intro icon
await tester.tap(find.byIcon(Icons.help_outline));
await tester.pump();
// Verify that the navRailIndexStateProvider is updated
final dashboard = tester.element(find.byType(Dashboard));
final container = ProviderScope.containerOf(dashboard);
expect(container.read(navRailIndexStateProvider), 1);
});
testWidgets(
'navRailIndexStateProvider should persist across widget rebuilds',
(tester) async {
// Pump the initial widget tree
await tester.pumpWidget(
const ProviderScope(
child: MaterialApp(
home: Dashboard(),
),
),
);
// Tap on the Settings icon to change the index to 2
await tester.tap(find.byIcon(Icons.settings_outlined));
await tester.pump();
// Rebuild the widget tree with the same ProviderScope
await tester.pumpWidget(
const ProviderScope(
child: MaterialApp(
home: Dashboard(),
),
),
);
// Verify that the navRailIndexStateProvider still has the updated value
final dashboard = tester.element(find.byType(Dashboard));
final container = ProviderScope.containerOf(dashboard);
expect(container.read(navRailIndexStateProvider), 2);
// Verify that the SettingsPage is still displayed after the rebuild
expect(find.byType(SettingsPage), findsOneWidget);
expect(find.byType(IntroPage), findsNothing);
expect(find.byType(HomePage), findsNothing);
});
testWidgets(
'UI should update correctly when navRailIndexStateProvider changes',
(tester) async {
await tester.pumpWidget(
const ProviderScope(
child: MaterialApp(
home: Dashboard(),
),
),
);
// Grab the Dashboard widget and its ProviderContainer
final dashboard = tester.element(find.byType(Dashboard));
final container = ProviderScope.containerOf(dashboard);
// Go to IntroPage
container.read(navRailIndexStateProvider.notifier).state = 1;
await tester.pump();
// Verify that the IntroPage is displayed
expect(find.byType(IntroPage), findsOneWidget);
// Verify that the selected icon is the filled version (selectedIcon)
expect(find.byIcon(Icons.help), findsOneWidget);
// Go to SettingsPage
container.read(navRailIndexStateProvider.notifier).state = 2;
await tester.pump();
// Verify that the SettingsPage is displayed
expect(find.byType(SettingsPage), findsOneWidget);
// Verify that the selected icon is the filled version (selectedIcon)
expect(find.byIcon(Icons.settings), findsOneWidget);
});
testWidgets(
'navRailIndexStateProvider should be disposed when Dashboard is removed',
(tester) async {
await tester.pumpWidget(
const ProviderScope(
child: MaterialApp(
home: Dashboard(),
),
),
);
// Grab the Dashboard widget and its ProviderContainer
final dashboard = tester.element(find.byType(Dashboard));
final container = ProviderScope.containerOf(dashboard);
// Pumping a different widget to remove the Dashboard from the widget tree
await tester.pumpWidget(
const MaterialApp(
home: Scaffold(body: Text('Different Widget')),
),
);
// Verify that the ProviderContainer has been disposed
// by trying to read from disposed container
bool isDisposed = false;
try {
container.read(navRailIndexStateProvider);
} catch (e) {
isDisposed = true;
}
expect(isDisposed, true);
});
});
group("Testing selectedIdEditStateProvider", () {
testWidgets('It should have an initial value of null', (tester) async {
await tester.pumpWidget(
const ProviderScope(
child: MaterialApp(
home: Scaffold(
body: CollectionPane(),
),
),
),
);
// Verify that the initial value is null
final collectionPane = tester.element(find.byType(CollectionPane));
final container = ProviderScope.containerOf(collectionPane);
expect(container.read(selectedIdEditStateProvider), null);
});
testWidgets(
'selectedIdEditStateProvider should not be null after rename button has been tapped',
(tester) async {
await tester.pumpWidget(
const ProviderScope(
child: MaterialApp(
home: Scaffold(
body: CollectionPane(),
),
),
),
);
// Tap on the three dots to open the request card menu
await tester.tap(find.byType(RequestList));
await tester.pump();
await tester.tap(find.byType(RequestItem));
await tester.pump();
await tester.tap(find.byIcon(Icons.more_vert).first);
await tester.pumpAndSettle();
// Tap on the "Rename" option in the menu
await tester.tap(find.text('Rename'));
await tester.pumpAndSettle();
// Verify that the selectedIdEditStateProvider is not null
final collectionPane = tester.element(find.byType(CollectionPane));
final container = ProviderScope.containerOf(collectionPane);
expect(container.read(selectedIdEditStateProvider), isNotNull);
expect((container.read(selectedIdEditStateProvider)).runtimeType, String);
});
testWidgets(
'It should be set back to null when user taps outside name editor',
(tester) async {
await tester.pumpWidget(
const ProviderScope(
child: MaterialApp(
home: Scaffold(
body: CollectionPane(),
),
),
),
);
// Grab the CollectionPane widget and its ProviderContainer
final collectionPane = tester.element(find.byType(CollectionPane));
final container = ProviderScope.containerOf(collectionPane);
// Tap on the three dots to open the request card menu
await tester.tap(find.byType(RequestList));
await tester.pump();
await tester.tap(find.byType(RequestItem));
await tester.pump();
await tester.tap(find.byIcon(Icons.more_vert).first);
await tester.pumpAndSettle();
// Tap on the "Rename" option in the menu
await tester.tap(find.text('Rename'));
await tester.pumpAndSettle();
// Verify that the selectedIdEditStateProvider is not null
expect(container.read(selectedIdEditStateProvider), isNotNull);
expect((container.read(selectedIdEditStateProvider)).runtimeType, String);
// Tap on the screen to simulate tapping outside the name editor
await tester.tap(find.byType(CollectionPane));
await tester.pumpAndSettle();
// Verify that the selectedIdEditStateProvider is null
expect(container.read(selectedIdEditStateProvider), null);
});
testWidgets("It should be properly disposed", (tester) async {
await tester.pumpWidget(
const ProviderScope(
child: MaterialApp(
home: Scaffold(
body: CollectionPane(),
),
),
),
);
// Grab the Dashboard widget and its ProviderContainer
final collectionPane = tester.element(find.byType(CollectionPane));
final container = ProviderScope.containerOf(collectionPane);
// Pumping a different widget to remove the CollectionPane from the widget tree
await tester.pumpWidget(
const MaterialApp(
home: Scaffold(body: Text('Foo')),
),
);
// Verify that the ProviderContainer has been disposed
// by trying to read from disposed container
bool isDisposed = false;
try {
container.read(selectedIdEditStateProvider);
} catch (e) {
isDisposed = true;
}
expect(isDisposed, true);
});
});
group('Testing codePaneVisibleStateProvider', () {
testWidgets("It should have have an initial value of false",
(tester) async {
await tester.pumpWidget(
const ProviderScope(
child: MaterialApp(
home: RequestEditorPane(),
),
),
);
// Verify that the initial value is false
final editorPane = tester.element(find.byType(RequestEditorPane));
final container = ProviderScope.containerOf(editorPane);
expect(container.read(codePaneVisibleStateProvider), false);
});
testWidgets("When state is false ResponsePane should be visible",
(tester) async {
await tester.pumpWidget(
const ProviderScope(
child: MaterialApp(
home: RequestEditorPane(),
),
),
);
expect(find.byType(RequestEditorDefault), findsOneWidget);
// Tap on the "Plus New" button
Finder plusNewButton = find.descendant(
of: find.byType(RequestEditorDefault),
matching: find.byType(ElevatedButton),
);
await tester.tap(plusNewButton);
await tester.pump();
// Verify that NotSentWidget is visible
expect(find.byType(NotSentWidget), findsOneWidget);
// Add some data in URLTextField
Finder field = find.descendant(
of: find.byType(URLField),
matching: find.byType(TextFormField),
);
await tester.enterText(field, kTestUrl);
await tester.pump();
// Tap on the "Send" button
Finder sendButton = find.byType(SendButton);
await tester.tap(sendButton);
await tester.pump();
final editorPane = tester.element(find.byType(RequestEditorPane));
final container = ProviderScope.containerOf(editorPane);
expect(container.read(codePaneVisibleStateProvider), false);
expect(find.byType(ResponsePane), findsOneWidget);
});
testWidgets("When state is true CodePane should be visible",
(tester) async {
await tester.pumpWidget(
const ProviderScope(
child: MaterialApp(
home: Scaffold(
body: RequestEditorPane(),
),
),
),
);
expect(find.byType(RequestEditorDefault), findsOneWidget);
// Tap on the "Plus New" button
Finder plusNewButton = find.descendant(
of: find.byType(RequestEditorDefault),
matching: find.byType(ElevatedButton),
);
await tester.tap(plusNewButton);
await tester.pump();
// Verify that NotSentWidget is visible
expect(find.byType(NotSentWidget), findsOneWidget);
// Add some data in URLTextField
Finder field = find.descendant(
of: find.byType(URLField),
matching: find.byType(TextFormField),
);
await tester.enterText(field, kTestUrl);
await tester.pump();
// Tap on the "Send" button
Finder sendButton = find.byType(SendButton);
await tester.tap(sendButton);
await tester.pump();
final editorPane = tester.element(find.byType(RequestEditorPane));
final container = ProviderScope.containerOf(editorPane);
// Change codePaneVisibleStateProvider state to true
container.read(codePaneVisibleStateProvider.notifier).state = true;
await tester.pump();
// Verify that the CodePane is visible
expect(container.read(codePaneVisibleStateProvider), true);
expect(find.byType(CodePane), findsOneWidget);
});
testWidgets("Hide/View Code button toggles the state", (tester) async {
await tester.pumpWidget(
const ProviderScope(
child: MaterialApp(
home: Scaffold(
body: RequestEditorPane(),
),
),
),
);
expect(find.byType(RequestEditorDefault), findsOneWidget);
// Tap on the "Plus New" button
Finder plusNewButton = find.descendant(
of: find.byType(RequestEditorDefault),
matching: find.byType(ElevatedButton),
);
await tester.tap(plusNewButton);
await tester.pump();
// Verify that NotSentWidget is visible
expect(find.byType(NotSentWidget), findsOneWidget);
// Add some data in URLTextField
Finder field = find.descendant(
of: find.byType(URLField),
matching: find.byType(TextFormField),
);
await tester.enterText(field, kTestUrl);
await tester.pump();
// Tap on the "Send" button
Finder sendButton = find.byType(SendButton);
await tester.tap(sendButton);
await tester.pump();
final editorPane = tester.element(find.byType(RequestEditorPane));
final container = ProviderScope.containerOf(editorPane);
final bool currentValue = container.read(codePaneVisibleStateProvider);
// Click on View Code button
await tester.tap(find.byIcon(Icons.code_rounded));
await tester.pump();
// Verify that the state value has changed
expect(container.read(codePaneVisibleStateProvider), !currentValue);
final bool newValue = container.read(codePaneVisibleStateProvider);
// Click on Hide Code button
await tester.tap(find.byIcon(Icons.code_off_rounded));
await tester.pump();
// Verify that the state value has changed
expect(container.read(codePaneVisibleStateProvider), !newValue);
});
testWidgets("That state persists across widget rebuilds", (tester) async {
await tester.pumpWidget(
const ProviderScope(
child: MaterialApp(
home: Scaffold(
body: RequestEditorPane(),
),
),
),
);
expect(find.byType(RequestEditorDefault), findsOneWidget);
// Tap on the "Plus New" button
Finder plusNewButton = find.descendant(
of: find.byType(RequestEditorDefault),
matching: find.byType(ElevatedButton),
);
await tester.tap(plusNewButton);
await tester.pump();
// Verify that NotSentWidget is visible
expect(find.byType(NotSentWidget), findsOneWidget);
// Add some data in URLTextField
Finder field = find.descendant(
of: find.byType(URLField),
matching: find.byType(TextFormField),
);
await tester.enterText(field, kTestUrl);
await tester.pump();
// Tap on the "Send" button
Finder sendButton = find.byType(SendButton);
await tester.tap(sendButton);
await tester.pump();
final editorPane = tester.element(find.byType(RequestEditorPane));
final container = ProviderScope.containerOf(editorPane);
final bool currentValue = container.read(codePaneVisibleStateProvider);
// Click on View Code button
await tester.tap(find.byIcon(Icons.code_rounded));
await tester.pump();
// Verify that the state value has changed
expect(container.read(codePaneVisibleStateProvider), !currentValue);
bool matcher = !currentValue;
// Rebuild the widget tree
await tester.pumpWidget(
const ProviderScope(
child: MaterialApp(
home: Scaffold(
body: RequestEditorPane(),
),
),
),
);
// Verify that the value of codePaneVisibleStateProvider is still true
final containerAfterRebuild = ProviderScope.containerOf(editorPane);
bool actual = containerAfterRebuild.read(codePaneVisibleStateProvider);
expect(actual, matcher);
});
testWidgets("That it is properly disposed", (tester) async {
await tester.pumpWidget(
const ProviderScope(
child: MaterialApp(
home: Scaffold(
body: RequestEditorPane(),
),
),
),
);
// Verify that codePaneVisibleStateProvider is present
final editorPane = tester.element(find.byType(RequestEditorPane));
final container = ProviderScope.containerOf(editorPane);
expect(container.read(codePaneVisibleStateProvider).runtimeType, bool);
// Update the widget tree to dispose the provider
await tester.pumpWidget(const MaterialApp());
// Verify that the provider was disposed
expect(() => container.read(codePaneVisibleStateProvider),
throwsA(isA<StateError>()));
expect(
() => container.read(codePaneVisibleStateProvider),
throwsA(
isA<StateError>().having(
(e) => e.message,
'message',
contains('was already disposed'),
),
),
);
});
});
}

View File

@ -1,5 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
final kThemeDataDark = ThemeData(
useMaterial3: true,
@ -13,6 +13,8 @@ final kThemeDataLight = ThemeData(
brightness: Brightness.light,
);
const kTestUrl = 'https://api.apidash.dev';
Uint8List kBodyBytesJpeg = Uint8List.fromList([
255,
216,

View File

@ -1,21 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:apidash/widgets/form_data_field.dart';
void main() {
testWidgets('Testing for Form Data Widget', (tester) async {
await tester.pumpWidget(
const MaterialApp(
title: 'Form Data Field Widget',
home: Scaffold(
body: FormDataField(
keyId: "1",
initialValue: "Test Field",
),
),
),
);
expect(find.text("Test Field"), findsOneWidget);
});
}

View File

@ -5,6 +5,7 @@ import 'package:apidash/consts.dart';
import 'package:flutter/foundation.dart';
import 'package:printing/printing.dart' show PdfPreview;
import 'package:flutter_svg/flutter_svg.dart' show SvgPicture;
import 'package:apidash/widgets/video_previewer.dart';
import '../test_consts.dart';
void main() {
@ -63,10 +64,7 @@ void main() {
),
),
);
expect(
find.text("${kMimeTypeRaiseIssueStart}video/H264$kMimeTypeRaiseIssue"),
findsOneWidget);
expect(find.byType(VideoPreviewer), findsOneWidget);
});
testWidgets('Testing when type/subtype is model/step+xml', (tester) async {

View File

@ -21,7 +21,6 @@ void main() {
);
expect(find.byType(Center), findsAtLeastNWidgets(1));
expect(find.text('Request'), findsOneWidget);
expect(find.text('Hide Code'), findsOneWidget);
expect(find.text('View Code'), findsNothing);
expect(find.text('URL Params'), findsOneWidget);
@ -52,7 +51,6 @@ void main() {
);
expect(find.byType(Center), findsAtLeastNWidgets(1));
expect(find.text('Request'), findsOneWidget);
expect(find.text('Hide Code'), findsOneWidget);
expect(find.text('View Code'), findsNothing);
expect(find.text('URL Params'), findsOneWidget);
@ -83,7 +81,6 @@ void main() {
);
expect(find.byType(Center), findsAtLeastNWidgets(1));
expect(find.text('Request'), findsOneWidget);
expect(find.text('Hide Code'), findsNothing);
expect(find.text('View Code'), findsOneWidget);
expect(find.text('URL Params'), findsOneWidget);
@ -117,7 +114,6 @@ void main() {
);
expect(find.byType(Center), findsAtLeastNWidgets(1));
expect(find.text('Request'), findsOneWidget);
expect(find.text('URL Params'), findsOneWidget);
expect(find.text('Headers'), findsOneWidget);
expect(find.text('Body'), findsOneWidget);

View File

@ -10,13 +10,15 @@ import 'package:apidash/models/models.dart';
import '../test_consts.dart';
void main() {
testWidgets('Testing Sending Widget', (tester) async {
testWidgets('Testing Sending Widget Without Timer', (tester) async {
await tester.pumpWidget(
MaterialApp(
title: 'Send',
theme: kThemeDataDark,
home: const Scaffold(
body: SendingWidget(),
body: SendingWidget(
startSendingTime: null,
),
),
),
);
@ -24,6 +26,26 @@ void main() {
expect(find.byType(Lottie), findsOneWidget);
});
testWidgets('Testing Sending Widget With Timer', (tester) async {
await tester.pumpWidget(
MaterialApp(
title: 'Send',
theme: kThemeDataDark,
home: Scaffold(
body: SendingWidget(
startSendingTime: DateTime.now(),
),
),
),
);
expect(find.text('Time elapsed: 0 ms'), findsOneWidget);
expect(find.byType(Lottie), findsOneWidget);
await tester.pump(const Duration(seconds: 1));
expect(find.text('Time elapsed: 1.00 s'), findsOneWidget);
});
testWidgets('Testing Not Sent Widget', (tester) async {
await tester.pumpWidget(
const MaterialApp(
@ -52,11 +74,8 @@ void main() {
),
);
expect(find.byType(RichText), findsAtLeastNWidgets(1));
expect(
find.textContaining("Response (", findRichText: true), findsOneWidget);
expect(find.text('Hi'), findsOneWidget);
expect(find.textContaining("200", findRichText: true), findsOneWidget);
expect(find.textContaining("Hi", findRichText: true), findsOneWidget);
expect(find.text(humanizeDuration(const Duration(microseconds: 23))),
findsOneWidget);
});
@ -73,7 +92,7 @@ void main() {
),
);
expect(find.text('Body'), findsOneWidget);
expect(find.text('Response Body'), findsOneWidget);
expect(find.text('Headers'), findsOneWidget);
await tester.tap(find.text('Headers'));
@ -81,7 +100,7 @@ void main() {
expect(find.text('first'), findsNothing);
expect(find.text('second'), findsOneWidget);
await tester.tap(find.text('Body'));
await tester.tap(find.text('Response Body'));
await tester.pumpAndSettle();
expect(find.text('first'), findsOneWidget);