1
0
mirror of https://github.com/foss42/apidash.git synced 2025-06-14 17:45:04 +08:00

Merge branch 'main' into add-rust-ureq-codegen

This commit is contained in:
Ashita Prasad
2024-03-12 20:29:08 +05:30
committed by GitHub
7 changed files with 2543 additions and 1 deletions

@ -1,12 +1,14 @@
import 'package:apidash/codegen/rust/ureq.dart';
import 'package:apidash/models/models.dart' show RequestModel;
import 'package:apidash/consts.dart';
import 'package:apidash/utils/utils.dart' show getNewUuid;
import 'dart/http.dart';
import 'dart/dio.dart';
import 'go/http.dart';
import 'kotlin/okhttp.dart';
import 'python/http_client.dart';
import 'python/requests.dart';
import 'rust/actix.dart';
import 'rust/ureq.dart';
import 'js/axios.dart';
import 'js/fetch.dart';
import 'others/har.dart';
@ -55,6 +57,10 @@ class Codegen {
return PythonRequestsCodeGen().getCode(rM, boundary: boundary);
case CodegenLanguage.rustUreq:
return RustUreqCodeGen().getCode(rM, boundary: boundary);
case CodegenLanguage.rustActix:
return RustActixCodeGen().getCode(rM, boundary: boundary);
case CodegenLanguage.goHttp:
return GoHttpCodeGen().getCode(rM);
}
}
}

172
lib/codegen/go/http.dart Normal file

@ -0,0 +1,172 @@
import 'package:apidash/consts.dart';
import 'package:jinja/jinja.dart' as jj;
import 'package:apidash/utils/utils.dart' show getValidRequestUri;
import 'package:apidash/models/models.dart' show RequestModel;
class GoHttpCodeGen {
final String kTemplateStart = """package main
import (
"fmt"
"io"
"net/http"
"net/url"{% if hasBody %}
"bytes"{% if hasFormData %}
"mime/multipart"{% if hasFileInFormData %}
"os"{% endif %}{% endif %}{% endif %}
)
func main() {
client := &http.Client{}
""";
String kTemplateUrl = """
url, _ := url.Parse("{{url}}")
""";
String kTemplateBody = """
{% if body %}payload := bytes.NewBuffer([]byte(`{{body}}`)){% endif %}
""";
String kTemplateFormData = """
payload := &bytes.Buffer{}
writer := multipart.NewWriter(payload){% if hasFileInFormData %}
var (
file *os.File
part io.Writer
){% endif %}
{% for field in fields %}
{% if field.type == "file" %}file, _ = os.Open("{{field.value}}")
defer file.Close()
part, _ = writer.CreateFormFile("{{field.name}}", "{{field.value}}")
io.Copy(part, file)
{% else %}writer.WriteField("{{field.name}}", "{{field.value}}"){% endif %}{% endfor %}
writer.Close()
""";
String kTemplateHeader = """
{% if headers %}{% for header, value in headers %}
req.Header.Set("{{header}}", "{{value}}"){% endfor %}
{% endif %}
""";
String kStringFormDataHeader = """
req.Header.Set("Content-Type", writer.FormDataContentType())
""";
String kTemplateQueryParam = """
query := url.Query()
{% for key, value in params %}
query.Set("{{key}}", "{{value}}"){% endfor %}
url.RawQuery = query.Encode()
""";
String kTemplateRequest = """
req, _ := http.NewRequest("{{method}}", url.String(), {% if hasBody %}payload{% else %}nil{% endif %})
""";
final String kTemplateEnd = """
response, err := client.Do(req)
if err != nil {
fmt.Println(err)
return
}
defer response.Body.Close()
fmt.Println("Status Code:", response.StatusCode)
body, _ := io.ReadAll(response.Body)
fmt.Println("Response body:", string(body))
}""";
String? getCode(
RequestModel requestModel,
) {
try {
String result = "";
var hasBody = false;
var requestBody = requestModel.requestBody;
String url = requestModel.url;
var templateStart = jj.Template(kTemplateStart);
result += templateStart.render({
"hasBody": requestModel.hasData,
"hasFormData": requestModel.hasFormData,
"hasFileInFormData": requestModel.hasFileInFormData,
});
var templateUrl = jj.Template(kTemplateUrl);
result += templateUrl.render({"url": url});
var rec = getValidRequestUri(
url,
requestModel.enabledRequestParams,
);
Uri? uri = rec.$1;
if (uri != null) {
if (requestModel.hasTextData || requestModel.hasJsonData) {
hasBody = true;
var templateRawBody = jj.Template(kTemplateBody);
result += templateRawBody.render({"body": requestBody});
} else if (requestModel.hasFormData) {
hasBody = true;
var templateFormData = jj.Template(kTemplateFormData);
result += templateFormData.render({
"hasFileInFormData": requestModel.hasFileInFormData,
"fields": requestModel.formDataMapList,
});
}
if (uri.hasQuery) {
var params = uri.queryParameters;
if (params.isNotEmpty) {
var templateQueryParam = jj.Template(kTemplateQueryParam);
result += templateQueryParam.render({"params": params});
}
}
var method = requestModel.method.name.toUpperCase();
var templateRequest = jj.Template(kTemplateRequest);
result += templateRequest.render({
"method": method,
"hasBody": hasBody,
});
var headersList = requestModel.enabledRequestHeaders;
if (headersList != null || requestModel.hasData) {
var headers = requestModel.enabledHeadersMap;
if (requestModel.hasJsonData || requestModel.hasTextData) {
headers.putIfAbsent(kHeaderContentType,
() => requestModel.requestBodyContentType.header);
}
if (headers.isNotEmpty) {
var templateHeader = jj.Template(kTemplateHeader);
result += templateHeader.render({
"headers": headers,
});
}
}
if (requestModel.hasFormData) {
result += kStringFormDataHeader;
}
result += kTemplateEnd;
}
return result;
} catch (e) {
return null;
}
}
}

217
lib/codegen/rust/actix.dart Normal file

@ -0,0 +1,217 @@
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, stripUriParams;
import 'package:apidash/models/models.dart' show RequestModel;
class RustActixCodeGen {
final String kTemplateStart = """
{%- if isFormDataRequest -%}
use std::io::Read;
{% endif -%}
#[actix_rt::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let url = "{{url}}";
let client = awc::Client::default();
""";
String kTemplateParams =
"""\n .query(&{{ params }})\n .unwrap()""";
String kTemplateBody = """
let payload = r#"{{body}}"#;
""";
String kTemplateJson = """
let payload = serde_json::json!({{body}});
""";
String kTemplateHeaders =
"""\n {% for key, val in headers -%}.insert_header(("{{key}}", "{{val}}")){% if not loop.last %}{{ '\n ' }}{% endif %}{%- endfor -%}""";
String kTemplateFormHeaderContentType = '''
multipart/form-data; boundary={{boundary}}''';
String kTemplateRequest = """
let mut response = client\n .{{method}}(url)
""";
final String kStringFormDataBody = r"""
struct FormDataItem {
name: String,
value: String,
field_type: String,
}
let form_data_items: Vec<FormDataItem> = vec![
{%- for formitem in fields_list %}
FormDataItem {
{%- for key, val in formitem %}
{% if key == "type" %}field_type: "{{ val }}".to_string(),{% else %}{{ key }}: "{{ val }}".to_string(),{% endif %}
{%- endfor %}
},
{%- endfor %}
];
fn build_data_list(fields: Vec<FormDataItem>) -> Vec<u8> {
let mut data_list = Vec::new();
for field in fields {
data_list.extend_from_slice(b"--{{boundary}}\r\n");
if field.field_type == "text" {
data_list.extend_from_slice(format!("Content-Disposition: form-data; name=\"{}\"\r\n", field.name).as_bytes());
data_list.extend_from_slice(b"Content-Type: text/plain\r\n\r\n");
data_list.extend_from_slice(field.value.as_bytes());
data_list.extend_from_slice(b"\r\n");
} else if field.field_type == "file" {
data_list.extend_from_slice(format!("Content-Disposition: form-data; name=\"{}\"; filename=\"{}\"\r\n", field.name, field.value).as_bytes());
let mime_type = mime_guess::from_path(&field.value).first_or(mime_guess::mime::APPLICATION_OCTET_STREAM);
data_list.extend_from_slice(format!("Content-Type: {}\r\n\r\n", mime_type).as_bytes());
let mut file = std::fs::File::open(&field.value).unwrap();
let mut file_contents = Vec::new();
file.read_to_end(&mut file_contents).unwrap();
data_list.extend_from_slice(&file_contents);
data_list.extend_from_slice(b"\r\n");
}
}
data_list.extend_from_slice(b"--{{boundary}}--\r\n");
data_list
}
let payload = build_data_list(form_data_items);
""";
String kStringRequestBody = """\n .send_body(payload)""";
String kStringRequestJson = """\n .send_json(&payload)""";
String kStringRequestNormal = """\n .send()""";
final String kStringRequestEnd = """\n .await\n .unwrap();
let body_bytes = response.body().await.unwrap();
let body = std::str::from_utf8(&body_bytes).unwrap();
println!("Response Status: {}", response.status());
println!("Response: {:?}", body);
Ok(())
}
""";
String? getCode(
RequestModel requestModel, {
String? boundary,
}) {
try {
String uuid = getNewUuid();
String result = "";
bool hasBody = false;
bool hasJsonBody = false;
String url = requestModel.url;
var rec = getValidRequestUri(
url,
requestModel.enabledRequestParams,
);
Uri? uri = rec.$1;
if (uri != null) {
var templateStartUrl = jj.Template(kTemplateStart);
result += templateStartUrl.render({
"url": stripUriParams(uri),
'isFormDataRequest': requestModel.hasFormData,
"method": requestModel.method.name.toLowerCase()
});
var method = requestModel.method;
var requestBody = requestModel.requestBody;
if (kMethodsWithBody.contains(method) && requestBody != null) {
var contentLength = utf8.encode(requestBody).length;
if (contentLength > 0) {
if (requestModel.requestBodyContentType == ContentType.json) {
hasJsonBody = true;
var templateBody = jj.Template(kTemplateJson);
result += templateBody.render({"body": requestBody});
} else if (!requestModel.hasFormData) {
hasBody = true;
var templateBody = jj.Template(kTemplateBody);
result += templateBody.render({"body": requestBody});
}
}
}
if (requestModel.hasFormData) {
var formDataBodyData = jj.Template(kStringFormDataBody);
result += formDataBodyData.render(
{
"fields_list": requestModel.formDataMapList,
"boundary": boundary ?? uuid,
},
);
}
var templateRequest = jj.Template(kTemplateRequest);
result += templateRequest.render({
"method": method.name.toLowerCase(),
});
if (uri.hasQuery) {
var params = uri.queryParameters;
if (params.isNotEmpty) {
var tupleStrings = params.entries
.map((entry) => '("${entry.key}", "${entry.value}")')
.toList();
var paramsString = "[${tupleStrings.join(', ')}]";
var templateParms = jj.Template(kTemplateParams);
result += templateParms.render({"params": paramsString});
}
}
var headersList = requestModel.enabledRequestHeaders;
if (headersList != null || hasBody || requestModel.hasFormData) {
var headers = requestModel.enabledHeadersMap;
if (requestModel.hasFormData) {
var formHeaderTemplate =
jj.Template(kTemplateFormHeaderContentType);
headers[HttpHeaders.contentTypeHeader] = formHeaderTemplate.render({
"boundary": boundary ?? uuid,
});
} else if (hasBody) {
headers[HttpHeaders.contentTypeHeader] =
requestModel.requestBodyContentType.header;
}
if (headers.isNotEmpty) {
var templateHeaders = jj.Template(kTemplateHeaders);
result += templateHeaders.render({"headers": headers});
}
}
if (hasBody || requestModel.hasFormData) {
result += kStringRequestBody;
} else if (hasJsonBody) {
result += kStringRequestJson;
} else {
result += kStringRequestNormal;
}
result += kStringRequestEnd;
}
return result;
} catch (e) {
return null;
}
}
}

@ -266,11 +266,13 @@ enum CodegenLanguage {
har("HAR", "json", "har"),
dartHttp("Dart (http)", "dart", "dart"),
dartDio("Dart (dio)", "dart", "dart"),
goHttp("Go (http)", "go", "go"),
jsAxios("JavaScript (axios)", "javascript", "js"),
jsFetch("JavaScript (fetch)", "javascript", "js"),
nodejsAxios("node.js (axios)", "javascript", "js"),
nodejsFetch("node.js (fetch)", "javascript", "js"),
kotlinOkHttp("Kotlin (okhttp3)", "java", "kt"),
rustActix("Rust (Actix Client)", "rust", "rs"),
pythonHttpClient("Python (http.client)", "python", "py"),
pythonRequests("Python (requests)", "python", "py"),
rustUreq("Rust (ureq)", "rust", "rs");

@ -68,6 +68,7 @@ class RequestModel {
bool get hasJsonContentType => requestBodyContentType == ContentType.json;
bool get hasTextContentType => requestBodyContentType == ContentType.text;
int get contentLength => utf8.encode(requestBody ?? "").length;
bool get hasData => hasJsonData || hasTextData || hasFormData;
bool get hasJsonData =>
kMethodsWithBody.contains(method) &&
hasJsonContentType &&
@ -84,6 +85,9 @@ class RequestModel {
requestFormDataList ?? <FormDataModel>[];
List<Map<String, dynamic>> get formDataMapList =>
rowsToFormDataMapList(requestFormDataList) ?? [];
bool get hasFileInFormData => formDataList
.map((e) => e.type == FormDataType.file)
.any((element) => element);
bool get hasContentTypeHeader => enabledHeadersMap.keys
.any((k) => k.toLowerCase() == HttpHeaders.contentTypeHeader);

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff