mirror of
https://github.com/foss42/apidash.git
synced 2025-05-31 14:23:45 +08:00
Merge branch 'main' into add-feature-pdf-preview
This commit is contained in:
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@ -3,7 +3,7 @@ name: Bug report
|
||||
about: Something wrong with API Dash. Report the problem/bug here.
|
||||
title: ''
|
||||
labels: bug
|
||||
assignees: animator
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
|
2
.github/ISSUE_TEMPLATE/feature_request.md
vendored
2
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@ -3,7 +3,7 @@ name: Feature request
|
||||
about: Help us make API Dash better by suggesting new features.
|
||||
title: ''
|
||||
labels: enhancement
|
||||
assignees: ashitaprasad
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
|
2
.github/ISSUE_TEMPLATE/feedback.md
vendored
2
.github/ISSUE_TEMPLATE/feedback.md
vendored
@ -4,7 +4,7 @@ about: Wanted to share something regarding API Dash that is neither a bug nor a
|
||||
Don't worry, we got you covered.
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: animator
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -49,8 +49,10 @@ linux/
|
||||
macos/
|
||||
windows/
|
||||
web/
|
||||
ios/
|
||||
.vscode/*
|
||||
icons/
|
||||
coverage/*
|
||||
installers/*
|
||||
.metadata
|
||||
.fvm/
|
||||
|
128
CODE_OF_CONDUCT.md
Normal file
128
CODE_OF_CONDUCT.md
Normal file
@ -0,0 +1,128 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
We as members, contributors, and leaders pledge to make participation in our
|
||||
community a harassment-free experience for everyone, regardless of age, body
|
||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||
identity and expression, level of experience, education, socio-economic status,
|
||||
nationality, personal appearance, race, religion, or sexual identity
|
||||
and orientation.
|
||||
|
||||
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||
diverse, inclusive, and healthy community.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to a positive environment for our
|
||||
community include:
|
||||
|
||||
* Demonstrating empathy and kindness toward other people
|
||||
* Being respectful of differing opinions, viewpoints, and experiences
|
||||
* Giving and gracefully accepting constructive feedback
|
||||
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
and learning from the experience
|
||||
* Focusing on what is best not just for us as individuals, but for the
|
||||
overall community
|
||||
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
* The use of sexualized language or imagery, and sexual attention or
|
||||
advances of any kind
|
||||
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or email
|
||||
address, without their explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Enforcement Responsibilities
|
||||
|
||||
Community leaders are responsible for clarifying and enforcing our standards of
|
||||
acceptable behavior and will take appropriate and fair corrective action in
|
||||
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||
or harmful.
|
||||
|
||||
Community leaders have the right and responsibility to remove, edit, or reject
|
||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||
decisions when appropriate.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies within all community spaces, and also applies when
|
||||
an individual is officially representing the community in public spaces.
|
||||
Examples of representing our community include using an official e-mail address,
|
||||
posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported to the community leaders responsible for enforcement at
|
||||
foss42.org@gmail.com.
|
||||
All complaints will be reviewed and investigated promptly and fairly.
|
||||
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
reporter of any incident.
|
||||
|
||||
## Enforcement Guidelines
|
||||
|
||||
Community leaders will follow these Community Impact Guidelines in determining
|
||||
the consequences for any action they deem in violation of this Code of Conduct:
|
||||
|
||||
### 1. Correction
|
||||
|
||||
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||
unprofessional or unwelcome in the community.
|
||||
|
||||
**Consequence**: A private, written warning from community leaders, providing
|
||||
clarity around the nature of the violation and an explanation of why the
|
||||
behavior was inappropriate. A public apology may be requested.
|
||||
|
||||
### 2. Warning
|
||||
|
||||
**Community Impact**: A violation through a single incident or series
|
||||
of actions.
|
||||
|
||||
**Consequence**: A warning with consequences for continued behavior. No
|
||||
interaction with the people involved, including unsolicited interaction with
|
||||
those enforcing the Code of Conduct, for a specified period of time. This
|
||||
includes avoiding interactions in community spaces as well as external channels
|
||||
like social media. Violating these terms may lead to a temporary or
|
||||
permanent ban.
|
||||
|
||||
### 3. Temporary Ban
|
||||
|
||||
**Community Impact**: A serious violation of community standards, including
|
||||
sustained inappropriate behavior.
|
||||
|
||||
**Consequence**: A temporary ban from any sort of interaction or public
|
||||
communication with the community for a specified period of time. No public or
|
||||
private interaction with the people involved, including unsolicited interaction
|
||||
with those enforcing the Code of Conduct, is allowed during this period.
|
||||
Violating these terms may lead to a permanent ban.
|
||||
|
||||
### 4. Permanent Ban
|
||||
|
||||
**Community Impact**: Demonstrating a pattern of violation of community
|
||||
standards, including sustained inappropriate behavior, harassment of an
|
||||
individual, or aggression toward or disparagement of classes of individuals.
|
||||
|
||||
**Consequence**: A permanent ban from any sort of public interaction within
|
||||
the community.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||
version 2.0, available at
|
||||
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
||||
|
||||
Community Impact Guidelines were inspired by [Mozilla's code of conduct
|
||||
enforcement ladder](https://github.com/mozilla/diversity).
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
https://www.contributor-covenant.org/faq. Translations are available at
|
||||
https://www.contributor-covenant.org/translations.
|
@ -4,10 +4,10 @@ We value your participation in this open source project. This page will give you
|
||||
|
||||
You can contribute to the project in any or all of the following ways:
|
||||
|
||||
- [Ask a question](https://github.com/foss42/api-dash/discussions)
|
||||
- [Submit a bug report](https://github.com/foss42/api-dash/issues/new/choose)
|
||||
- [Request a new feature](https://github.com/foss42/api-dash/issues/new/choose)
|
||||
- [Suggest ways to improve the developer experience of an existing feature](https://github.com/foss42/api-dash/issues/new/choose)
|
||||
- [Ask a question](https://github.com/foss42/apidash/discussions)
|
||||
- [Submit a bug report](https://github.com/foss42/apidash/issues/new/choose)
|
||||
- [Request a new feature](https://github.com/foss42/apidash/issues/new/choose)
|
||||
- [Suggest ways to improve the developer experience of an existing feature](https://github.com/foss42/apidash/issues/new/choose)
|
||||
- Add documentation
|
||||
- Add a new feature, resolve an existing issue or add a new test to the project. (Goto [Code Contribution Guidelines](#code-contribution-guidelines)).
|
||||
|
||||
@ -27,11 +27,11 @@ We currently do not accept PRs that involve:
|
||||
|
||||
### Resolving an existing issue / Adding a requested feature
|
||||
|
||||
You can find all existing issues [here](https://github.com/foss42/api-dash/issues). A good place to start is to take a look at ["good first issues"](https://github.com/foss42/api-dash/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22).
|
||||
You can find all existing issues [here](https://github.com/foss42/apidash/issues). A good place to start is to take a look at ["good first issues"](https://github.com/foss42/apidash/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22).
|
||||
|
||||
**Step 1** - Identify the issue you want to work on.
|
||||
**Step 2** - Comment on the issue so that we can discuss how to approach and solve the problem.
|
||||
**Step 3** - Fork the [`foss42/api-dash`](https://github.com/foss42/api-dash) repo to your account.
|
||||
**Step 3** - Fork the [`foss42/apidash`](https://github.com/foss42/apidash) repo to your account.
|
||||
**Step 4** - Create a new branch in your fork and name it `add-feature-xyz` or `resolve-issue-xyz`.
|
||||
**Step 5** - Run API Dash locally (More details [here](#how-to-run-api-dash-locally)).
|
||||
**Step 6** - Make code changes in the branch.
|
||||
@ -41,8 +41,8 @@ You can find all existing issues [here](https://github.com/foss42/api-dash/issue
|
||||
|
||||
### Adding a new feature
|
||||
|
||||
**Step 1** - Open an [issue](https://github.com/foss42/api-dash/issues/new/choose) so that we can discuss on the new feature.
|
||||
**Step 2** - Fork the [`foss42/api-dash`](https://github.com/foss42/api-dash) repo to your account.
|
||||
**Step 1** - Open an [issue](https://github.com/foss42/apidash/issues/new/choose) so that we can discuss on the new feature.
|
||||
**Step 2** - Fork the [`foss42/apidash`](https://github.com/foss42/apidash) repo to your account.
|
||||
**Step 3** - Create a new branch in your fork and name it `add-feature-xyz`.
|
||||
**Step 4** - Run API Dash locally (More details [here](#how-to-run-api-dash-locally)).
|
||||
**Step 5** - Make the necessary code changes required to implement the feature in the branch.
|
||||
@ -61,7 +61,7 @@ You can contribute by adding missing/new tests for:
|
||||
- Services (`lib/services/`).
|
||||
|
||||
**Step 1** - Identify the test you want to add or improve.
|
||||
**Step 2** - Fork the [`foss42/api-dash`](https://github.com/foss42/api-dash) repo to your account.
|
||||
**Step 2** - Fork the [`foss42/apidash`](https://github.com/foss42/apidash) repo to your account.
|
||||
**Step 3** - Create a new branch in your fork and name it `add-test-xyz`.
|
||||
**Step 4** - Add the test to an existing test file or create a new test file in the `test` folder.
|
||||
**Step 5** - Run the tests locally (More details [here](#how-to-run-tests)).
|
||||
@ -70,6 +70,14 @@ You can contribute by adding missing/new tests for:
|
||||
|
||||
## General Instructions
|
||||
|
||||
### What is the supported Flutter/Dart version?
|
||||
|
||||
This project supports the latest Dart 3 & Flutter version. If you are using older Flutter version that does not support Dart 3, you might get errors.
|
||||
|
||||
In case you are setting up Flutter for the first time, just go ahead and download the latest (Stable) SDK from the [Flutter SDK Archive](https://docs.flutter.dev/release/archive). Then proceed with the Flutter installation.
|
||||
|
||||
In case you have already setup Flutter, make sure to switch to `stable` branch and upgrade it.
|
||||
|
||||
### How to run API Dash locally?
|
||||
|
||||
1. Fork the project.
|
||||
@ -78,7 +86,7 @@ You can contribute by adding missing/new tests for:
|
||||
4. This project uses [Records feature in Dart](https://github.com/dart-lang/language/blob/main/accepted/future-releases/records/records-feature-specification.md), so to run the project execute the following command:
|
||||
|
||||
```
|
||||
flutter run --enable-experiment=records
|
||||
flutter run
|
||||
```
|
||||
|
||||
### How to run tests?
|
||||
@ -86,7 +94,7 @@ flutter run --enable-experiment=records
|
||||
To run tests execute the following command:
|
||||
|
||||
```
|
||||
flutter test --enable-experiment=records --coverage
|
||||
flutter test --coverage
|
||||
```
|
||||
|
||||
To generate coverage report as html execute:
|
||||
@ -95,10 +103,28 @@ To generate coverage report as html execute:
|
||||
genhtml coverage/lcov.info -o coverage/html
|
||||
```
|
||||
|
||||
*Note*: On macOS you need to have `lcov` installed on your system (`brew install lcov`) to run the above command.
|
||||
**Note**: On macOS you need to have `lcov` installed on your system (`brew install lcov`) to run the above command.
|
||||
|
||||
To view the coverage report in the browser for further analysis, execute:
|
||||
|
||||
```
|
||||
open coverage/html/index.html
|
||||
```
|
||||
|
||||
#### Testing a single file
|
||||
|
||||
To run tests specified in a single file, execute the following command:
|
||||
|
||||
```
|
||||
flutter test <file_path>.dart
|
||||
```
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
flutter test test/widgets/codegen_previewer_test.dart
|
||||
```
|
||||
|
||||
### How to add a new package to pubspec.yaml?
|
||||
|
||||
Instead of copy pasting from pub.dev, it is recommended that you use `flutter pub add package_name` to add a new package to `pubspec.yaml`. You can read more [here](https://docs.flutter.dev/packages-and-plugins/using-packages#adding-a-package-dependency-to-an-app-using-flutter-pub-add).
|
||||
|
@ -1,13 +1,13 @@
|
||||
# Installation Instructions
|
||||
|
||||
## Windows
|
||||
Download the latest Windows Installer (64 bit) from [here](https://github.com/foss42/api-dash/releases/latest)
|
||||
Download the latest Windows Installer (64 bit) from [here](https://github.com/foss42/apidash/releases/latest)
|
||||
|
||||
To install it, simply double click on the installer and follow the step by step installation wizard.
|
||||
|
||||
## MacOS
|
||||
|
||||
Download the latest MacOS Installer (Universal - Intel and Apple Silicon) from [here](https://github.com/foss42/api-dash/releases/latest)
|
||||
Download the latest MacOS Installer (Universal - Intel and Apple Silicon) from [here](https://github.com/foss42/apidash/releases/latest)
|
||||
|
||||
**As this app is distributed outside the App Store you have to follow the following instructions to setup and run it only for the first time.**
|
||||
|
||||
@ -46,7 +46,7 @@ Note: The next step has to be performed twice so that macOS adds the app to whit
|
||||
|
||||
### Debian-based Linux Distributions (Debian, Ubuntu, Linux Mint, etc.)
|
||||
|
||||
Download the `.deb` file from the [latest release](https://github.com/foss42/api-dash/releases/latest) corresponding to you CPU architecture (x64/amd64 or arm64).
|
||||
Download the `.deb` file from the [latest release](https://github.com/foss42/apidash/releases/latest) corresponding to you CPU architecture (x64/amd64 or arm64).
|
||||
|
||||
`cd` to the Downloads folder and execute the following command to install API Dash.
|
||||
|
||||
@ -64,7 +64,7 @@ Launch API Dash via `apidash` command or by clicking on the API Dash app icon.
|
||||
|
||||
### Red Hat-based Linux Distributions (Fedora, Rocky, AlmaLinux, CentOS, RHEL, etc.)
|
||||
|
||||
Download the `.rpm` file from the [latest release](https://github.com/foss42/api-dash/releases/latest) corresponding to you CPU architecture (x86_64 or aarch64/arm64).
|
||||
Download the `.rpm` file from the [latest release](https://github.com/foss42/apidash/releases/latest) corresponding to you CPU architecture (x86_64 or aarch64/arm64).
|
||||
|
||||
`cd` to the Downloads folder and execute the following command to install API Dash.
|
||||
|
||||
|
22
README.md
22
README.md
@ -23,21 +23,21 @@ API Dash can be downloaded from the links below:
|
||||
<tr>
|
||||
<td>macOS</td>
|
||||
<td><code>.dmg</code></td>
|
||||
<td><a href="https://github.com/foss42/api-dash/blob/main/INSTALLATION.md#macos">Link</a></td>
|
||||
<td><a href="https://github.com/foss42/apidash/blob/main/INSTALLATION.md#macos">Link</a></td>
|
||||
<td>Apple Silicon & Intel</td>
|
||||
<td><a href="https://bit.ly/44wmazf">Link</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Windows</td>
|
||||
<td><code>.exe</code></td>
|
||||
<td><a href="https://github.com/foss42/api-dash/blob/main/INSTALLATION.md#windows">Link</a></td>
|
||||
<td><a href="https://github.com/foss42/apidash/blob/main/INSTALLATION.md#windows">Link</a></td>
|
||||
<td>64-bit</td>
|
||||
<td><a href="https://bit.ly/424ExKb">Link</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td rowspan=4>Linux</td>
|
||||
<td rowspan=2><code>.deb</code></td>
|
||||
<td rowspan=2><a href="https://github.com/foss42/api-dash/blob/main/INSTALLATION.md#debian-based-linux-distributions-debian-ubuntu-linux-mint-etc">Link</a></td>
|
||||
<td rowspan=2><a href="https://github.com/foss42/apidash/blob/main/INSTALLATION.md#debian-based-linux-distributions-debian-ubuntu-linux-mint-etc">Link</a></td>
|
||||
<td>amd64</td>
|
||||
<td><a href="https://bit.ly/44sWPq2">Link</a></td>
|
||||
</tr>
|
||||
@ -47,7 +47,7 @@ API Dash can be downloaded from the links below:
|
||||
</tr>
|
||||
<tr>
|
||||
<td rowspan=2><code>.rpm</code></td>
|
||||
<td rowspan=2><a href="https://github.com/foss42/api-dash/blob/main/INSTALLATION.md#red-hat-based-linux-distributions-fedora-rocky-almalinux-centos-rhel-etc">Link</a></td>
|
||||
<td rowspan=2><a href="https://github.com/foss42/apidash/blob/main/INSTALLATION.md#red-hat-based-linux-distributions-fedora-rocky-almalinux-centos-rhel-etc">Link</a></td>
|
||||
<td>x86_64</td>
|
||||
<td><a href="https://bit.ly/417gWHe">Link</a></td>
|
||||
</tr>
|
||||
@ -60,7 +60,9 @@ API Dash can be downloaded from the links below:
|
||||
|
||||
## A Quick Glimpse of API Dash ⚡️ (Demo Video)
|
||||
|
||||
https://github.com/foss42/api-dash/assets/615622/fccc569e-3152-47be-9f94-ceb851ee85a0
|
||||
Demo Video on Youtube - [Link](https://youtu.be/IQlrgpNpS2s) (In case there is an error loading the embedded video below 👇)
|
||||
|
||||
https://github.com/foss42/apidash/assets/615622/fccc569e-3152-47be-9f94-ceb851ee85a0
|
||||
|
||||
## List of Features
|
||||
|
||||
@ -99,16 +101,16 @@ Visit [CHANGELOG.md](CHANGELOG.md)
|
||||
|
||||
## Provide Feedback, Report Bugs & Request New Features
|
||||
|
||||
Just click on the [Issue tab](https://github.com/foss42/api-dash/issues) to raise a new issue in this repo.
|
||||
Just click on the [Issue tab](https://github.com/foss42/apidash/issues) to raise a new issue in this repo.
|
||||
|
||||
## Contribute to API Dash
|
||||
|
||||
You can contribute to API Dash in any or all of the following ways:
|
||||
|
||||
- [Ask a question](https://github.com/foss42/api-dash/discussions)
|
||||
- [Submit a bug report](https://github.com/foss42/api-dash/issues/new/choose)
|
||||
- [Request a new feature](https://github.com/foss42/api-dash/issues/new/choose)
|
||||
- [Suggest ways to improve the developer experience of an existing feature](https://github.com/foss42/api-dash/issues/new/choose)
|
||||
- [Ask a question](https://github.com/foss42/apidash/discussions)
|
||||
- [Submit a bug report](https://github.com/foss42/apidash/issues/new/choose)
|
||||
- [Request a new feature](https://github.com/foss42/apidash/issues/new/choose)
|
||||
- [Suggest ways to improve the developer experience of an existing feature](https://github.com/foss42/apidash/issues/new/choose)
|
||||
- Add documentation
|
||||
- To add a new feature, resolve an existing issue or add a new test to the project, check out our [Contribution Guidelines](CONTRIBUTING.md).
|
||||
|
||||
|
@ -40,7 +40,7 @@ Using API Dash, you can easily create & customize your API requests, visually in
|
||||
7. Notification on save, download and any other user action (UX improvement).
|
||||
8. Linux builds are now available for API Dash (.deb & .rpm)
|
||||
|
||||
.. and various bug fixes & performance improvements. View full changelog [here](https://github.com/foss42/api-dash/blob/main/CHANGELOG.md).
|
||||
.. and various bug fixes & performance improvements. View full changelog [here](https://github.com/foss42/apidash/blob/main/CHANGELOG.md).
|
||||
|
||||
#br
|
||||
#br
|
||||
|
@ -1 +1,28 @@
|
||||
export 'dart/pkg_http.dart';
|
||||
import 'package:apidash/codegen/kotlin/pkg_okhttp.dart';
|
||||
import 'python/pkg_http_client.dart';
|
||||
import 'package:apidash/codegen/python/pkg_request.dart';
|
||||
import 'package:apidash/consts.dart';
|
||||
|
||||
import 'package:apidash/models/models.dart' show RequestModel;
|
||||
import 'dart/pkg_http.dart';
|
||||
|
||||
class Codegen {
|
||||
String? getCode(
|
||||
CodegenLanguage codegenLanguage,
|
||||
RequestModel requestModel,
|
||||
String defaultUriScheme,
|
||||
) {
|
||||
switch (codegenLanguage) {
|
||||
case CodegenLanguage.dartHttp:
|
||||
return DartHttpCodeGen().getCode(requestModel, defaultUriScheme);
|
||||
case CodegenLanguage.kotlinOkHttp:
|
||||
return KotlinOkHttpCodeGen().getCode(requestModel);
|
||||
case CodegenLanguage.pythonHttpClient:
|
||||
return PythonHttpClient().getCode(requestModel);
|
||||
case CodegenLanguage.pythonRequests:
|
||||
return PythonRequestCodeGen().getCode(requestModel, defaultUriScheme);
|
||||
default:
|
||||
throw ArgumentError('Invalid codegenLanguage');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
80
lib/codegen/kotlin/pkg_okhttp.dart
Normal file
80
lib/codegen/kotlin/pkg_okhttp.dart
Normal file
@ -0,0 +1,80 @@
|
||||
import 'package:apidash/consts.dart';
|
||||
|
||||
import '../../models/request_model.dart';
|
||||
|
||||
class KotlinOkHttpCodeGen {
|
||||
final String headerSnippet = """import okhttp3.MediaType.Companion.toMediaType
|
||||
import okhttp3.MultipartBody
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.RequestBody.Companion.toRequestBody
|
||||
import okhttp3.RequestBody.Companion.asRequestBody
|
||||
import java.io.File
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
val client = OkHttpClient()
|
||||
""";
|
||||
|
||||
final String footerSnippet = """ .build()
|
||||
val response = client.newCall(request).execute()
|
||||
|
||||
println(response.body!!.string())
|
||||
""";
|
||||
String getCode(RequestModel requestModel) {
|
||||
String result = "";
|
||||
result = result + headerSnippet;
|
||||
if (requestModel.method != HTTPVerb.get &&
|
||||
requestModel.method != HTTPVerb.head) {
|
||||
result =
|
||||
"""${result}val mediaType = "${requestModel.requestBodyContentType == ContentType.json ? "application/json" : "text/plain"}".toMediaType()
|
||||
val body = "${requestModel.requestBody}".toRequestBody(mediaType)\n""";
|
||||
}
|
||||
result = "${result}val request = Request.Builder()\n";
|
||||
|
||||
result = "$result .url(\"${requestModel.url}\")\n";
|
||||
result = result + addQueryParams(requestModel);
|
||||
result = result + addRequestMethod(requestModel);
|
||||
result = result + addHeaders(requestModel);
|
||||
result = result + footerSnippet;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
String addQueryParams(RequestModel requestModel) {
|
||||
String result = "";
|
||||
if (requestModel.requestParams == null) {
|
||||
return result;
|
||||
}
|
||||
for (final queryParam in requestModel.requestParams!) {
|
||||
result =
|
||||
"""$result .addQueryParameter("${queryParam.name}", "${queryParam.value}")\n""";
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
String addHeaders(RequestModel requestModel) {
|
||||
String result = "";
|
||||
if (requestModel.requestHeaders == null) {
|
||||
return result;
|
||||
}
|
||||
for (final header in requestModel.requestHeaders!) {
|
||||
result = """$result .addHeader("${header.name}", "${header.value}")\n""";
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
String addRequestMethod(RequestModel requestModel) {
|
||||
String result = "";
|
||||
if (requestModel.method != HTTPVerb.get &&
|
||||
requestModel.method != HTTPVerb.head &&
|
||||
requestModel.method != HTTPVerb.delete) {
|
||||
result = """$result .${requestModel.method.name}(body)\n""";
|
||||
} else if (requestModel.method == HTTPVerb.head) {
|
||||
result = """$result .${requestModel.method.name}()\n""";
|
||||
}
|
||||
if (requestModel.method == HTTPVerb.delete) {
|
||||
result = """$result .method("DELETE", body)\n""";
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
82
lib/codegen/python/pkg_http_client.dart
Normal file
82
lib/codegen/python/pkg_http_client.dart
Normal file
@ -0,0 +1,82 @@
|
||||
import 'package:apidash/consts.dart';
|
||||
|
||||
import '../../models/request_model.dart';
|
||||
|
||||
class PythonHttpClient {
|
||||
final String headerSnippet = """
|
||||
import http.client
|
||||
import json
|
||||
|
||||
""";
|
||||
|
||||
final String footerSnippet = """
|
||||
res = conn.getresponse()
|
||||
data = res.read()
|
||||
print(data.decode("utf-8"))
|
||||
""";
|
||||
|
||||
String getCode(RequestModel requestModel) {
|
||||
String result = "";
|
||||
result += headerSnippet;
|
||||
result +=
|
||||
"conn = http.client.HTTPSConnection('${getUrl(requestModel)["host"]}'${getUrl(requestModel)["port"]})\n";
|
||||
result += "payload = json.dumps(${requestModel.requestBody ?? ""})\n";
|
||||
result +=
|
||||
"""headers = {\n'Content-Type':'${requestModel.requestBodyContentType == ContentType.json ? 'application/json' : 'text/plain'}'\n${addHeaders(requestModel)}},\n""";
|
||||
result +=
|
||||
"conn.request(\"${requestModel.method.name.toUpperCase()}\", \"${getUrl(requestModel)["endpoint"]}${addQueryParams(requestModel)}\", payload, headers)\n";
|
||||
result += footerSnippet;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
String addHeaders(RequestModel requestModel) {
|
||||
String result = "";
|
||||
if (requestModel.requestHeaders == null) {
|
||||
return result;
|
||||
}
|
||||
for (final header in requestModel.requestHeaders!) {
|
||||
result += """'${header.name}':'${header.value}',\n""";
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
String addQueryParams(RequestModel requestModel) {
|
||||
String result = "";
|
||||
if (requestModel.requestParams == null) {
|
||||
return result;
|
||||
}
|
||||
result += "?";
|
||||
for (final queryParam in requestModel.requestParams!) {
|
||||
result +=
|
||||
"${queryParam.name.toString().replaceAll(" ", "%20")}=${queryParam.value.toString().replaceAll(" ", "%20")}&";
|
||||
}
|
||||
return result.substring(0, result.length - 1);
|
||||
}
|
||||
|
||||
Map<String, String> getUrl(RequestModel requestModel) {
|
||||
String result = "";
|
||||
if (requestModel.url.startsWith('http://') ||
|
||||
requestModel.url.startsWith('https://')) {
|
||||
result = requestModel.url.substring(requestModel.url.indexOf('://') + 3);
|
||||
} else {
|
||||
result = requestModel.url;
|
||||
}
|
||||
Map<String, String> resultMap = {};
|
||||
if (result.contains(":")) {
|
||||
resultMap["host"] = result.substring(0, result.indexOf(':'));
|
||||
resultMap["port"] =
|
||||
",${result.substring(result.indexOf(':') + 1, result.contains('/') ? result.indexOf('/') : result.length)}";
|
||||
resultMap["endpoint"] =
|
||||
"/${result.substring(result.contains('/') ? result.indexOf('/') + 1 : result.length)}";
|
||||
} else {
|
||||
resultMap["host"] = result.contains("/")
|
||||
? result.substring(0, result.indexOf('/'))
|
||||
: result;
|
||||
resultMap["port"] = "";
|
||||
resultMap["endpoint"] =
|
||||
"/${result.substring(result.contains('/') ? result.indexOf('/') + 1 : result.length)}";
|
||||
}
|
||||
return resultMap;
|
||||
}
|
||||
}
|
104
lib/codegen/python/pkg_request.dart
Normal file
104
lib/codegen/python/pkg_request.dart
Normal file
@ -0,0 +1,104 @@
|
||||
import 'package:jinja/jinja.dart' as jj;
|
||||
import 'package:apidash/models/models.dart' show RequestModel;
|
||||
import 'package:apidash/utils/utils.dart' show padMultilineString, rowsToMap;
|
||||
import 'package:apidash/consts.dart';
|
||||
|
||||
class PythonRequestCodeGen {
|
||||
int kHeadersPadding = 16;
|
||||
String kPythonTemplate = '''
|
||||
import requests
|
||||
|
||||
def main():
|
||||
url = '{{url}}'{{params}}{{body}}{{headers}}
|
||||
|
||||
response = requests.{{method}}(
|
||||
url{{request_params}}{{request_headers}}{{request_body}}
|
||||
)
|
||||
|
||||
status_code = response.status_code
|
||||
if 200 <= status_code < 300:
|
||||
print('Status Code:', status_code)
|
||||
print('Response Body:', response.text)
|
||||
else:
|
||||
print('Error Status Code:', status_code)
|
||||
print('Error Response Body:', response.reason)
|
||||
|
||||
main()
|
||||
''';
|
||||
|
||||
String? getCode(RequestModel requestModel, String defaultUriScheme) {
|
||||
try {
|
||||
bool hasHeaders = false;
|
||||
bool hasBody = false;
|
||||
bool hasParams = false;
|
||||
|
||||
String url = requestModel.url;
|
||||
if (!url.contains('://') && url.isNotEmpty) {
|
||||
url = '$defaultUriScheme://$url';
|
||||
}
|
||||
|
||||
var paramsList = requestModel.requestParams;
|
||||
String params = '';
|
||||
if (paramsList != null) {
|
||||
for (var param in paramsList) {
|
||||
if (param.name.isNotEmpty) {
|
||||
hasParams = true;
|
||||
params += '\n "${param.name}": "${param.value}",';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var method = requestModel.method.name.toLowerCase();
|
||||
|
||||
var requestBody = requestModel.requestBody;
|
||||
String requestBodyString = '';
|
||||
if (requestBody != null && requestBody.isNotEmpty) {
|
||||
hasBody = true;
|
||||
var bodyType = requestModel.requestBodyContentType;
|
||||
if (bodyType == ContentType.json) {
|
||||
int index = requestBody.lastIndexOf("}");
|
||||
index--;
|
||||
while (requestBody[index] == " " || requestBody[index] == "\n") {
|
||||
index--;
|
||||
}
|
||||
if (requestBody[index] == ",") {
|
||||
requestBody = requestBody.substring(0, index) +
|
||||
requestBody.substring(index + 1);
|
||||
}
|
||||
}
|
||||
requestBodyString = "data = '''$requestBody'''";
|
||||
}
|
||||
|
||||
var headersList = requestModel.requestHeaders;
|
||||
String headers = '';
|
||||
if (headersList != null || hasBody) {
|
||||
var head = rowsToMap(requestModel.requestHeaders) ?? {};
|
||||
if (head.isNotEmpty || hasBody) {
|
||||
if (hasBody) {
|
||||
head["content-type"] =
|
||||
kContentTypeMap[requestModel.requestBodyContentType] ?? "";
|
||||
}
|
||||
headers = kEncoder.convert(head);
|
||||
headers = padMultilineString(headers, kHeadersPadding);
|
||||
}
|
||||
hasHeaders = headers.isNotEmpty;
|
||||
}
|
||||
|
||||
var template = jj.Template(kPythonTemplate);
|
||||
var pythonCode = template.render({
|
||||
'url': url,
|
||||
'params': hasParams ? '\n\n params = {$params \n }' : '',
|
||||
'body': hasBody ? '\n\n $requestBodyString' : '',
|
||||
'headers': hasHeaders ? '\n\n headers = $headers' : '',
|
||||
'method': method,
|
||||
'request_params': hasParams ? ', params=params' : '',
|
||||
'request_headers': hasHeaders ? ', headers=headers' : '',
|
||||
'request_body': hasBody ? ', data=data' : '',
|
||||
});
|
||||
|
||||
return pythonCode;
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@ -6,7 +6,7 @@ import 'package:google_fonts/google_fonts.dart';
|
||||
import 'package:davi/davi.dart';
|
||||
|
||||
const kDiscordUrl = "https://bit.ly/heyfoss";
|
||||
const kGitUrl = "https://github.com/foss42/api-dash";
|
||||
const kGitUrl = "https://github.com/foss42/apidash";
|
||||
const kIssueUrl = "$kGitUrl/issues";
|
||||
|
||||
final kIsMacOS = !kIsWeb && Platform.isMacOS;
|
||||
@ -28,6 +28,7 @@ const kWindowTitle = "API Dash";
|
||||
const kMinWindowSize = Size(900, 600);
|
||||
const kMinInitialWindowWidth = 1200.0;
|
||||
const kMinInitialWindowHeight = 800.0;
|
||||
const kMinRequestEditorDetailsCardPaneSize = 300.0;
|
||||
|
||||
const kColorSchemeSeed = Colors.blue;
|
||||
final kFontFamily = GoogleFonts.openSans().fontFamily;
|
||||
@ -225,9 +226,22 @@ const kMethodsWithBody = [
|
||||
HTTPVerb.patch,
|
||||
HTTPVerb.delete,
|
||||
];
|
||||
|
||||
const kDefaultHttpMethod = HTTPVerb.get;
|
||||
const kDefaultContentType = ContentType.json;
|
||||
|
||||
enum CodegenLanguage {
|
||||
dartHttp("Dart (http)", "dart", "dart"),
|
||||
kotlinOkHttp("Kotlin (okhttp3)", "java", "kt"),
|
||||
pythonHttpClient("Python (http.client)", "python", "py"),
|
||||
pythonRequests("Python (requests)", "python", "py");
|
||||
|
||||
const CodegenLanguage(this.label, this.codeHighlightLang, this.ext);
|
||||
final String label;
|
||||
final String codeHighlightLang;
|
||||
final String ext;
|
||||
}
|
||||
|
||||
const JsonEncoder kEncoder = JsonEncoder.withIndent(' ');
|
||||
const LineSplitter kSplitter = LineSplitter();
|
||||
|
||||
@ -311,7 +325,7 @@ const Map<String, Map<String, List<ResponseBodyView>>>
|
||||
kSubTypeSvg: kCodeRawBodyViewOptions,
|
||||
},
|
||||
kTypeAudio: {
|
||||
kSubTypeDefaultViewOptions: kNoBodyViewOptions,
|
||||
kSubTypeDefaultViewOptions: kPreviewBodyViewOptions,
|
||||
},
|
||||
kTypeVideo: {
|
||||
kSubTypeDefaultViewOptions: kNoBodyViewOptions,
|
||||
@ -427,6 +441,9 @@ const kImageError =
|
||||
const kPdfError =
|
||||
"There seems to be an issue rendering this pdf. Please raise an issue in API Dash GitHub repo so that we can resolve it.";
|
||||
|
||||
const kAudioError =
|
||||
"There seems to be an issue playing this audio. Please raise an issue in API Dash GitHub repo so that we can resolve it.";
|
||||
|
||||
const kRaiseIssue =
|
||||
"\nPlease raise an issue in API Dash GitHub repo so that we can resolve it.";
|
||||
|
||||
|
@ -13,7 +13,7 @@ void main() async {
|
||||
await setupInitialWindow();
|
||||
} else {
|
||||
var win = getInitialSize();
|
||||
await setupWindow(sz: win.$0, off: win.$1);
|
||||
await setupWindow(sz: win.$1, off: win.$2);
|
||||
}
|
||||
runApp(
|
||||
ProviderScope(
|
||||
|
@ -1,38 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
@immutable
|
||||
class KVRow {
|
||||
const KVRow(this.k, this.v);
|
||||
|
||||
final String k;
|
||||
final dynamic v;
|
||||
|
||||
KVRow copyWith({
|
||||
String? k,
|
||||
dynamic v,
|
||||
}) {
|
||||
return KVRow(k ?? this.k, v ?? this.v);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return {k: v}.toString();
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is KVRow &&
|
||||
other.runtimeType == runtimeType &&
|
||||
other.k == k &&
|
||||
other.v == v;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
return Object.hash(
|
||||
runtimeType,
|
||||
k,
|
||||
v,
|
||||
);
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
export 'kvrow_model.dart';
|
||||
export 'name_value_model.dart';
|
||||
export 'request_model.dart';
|
||||
export 'response_model.dart';
|
||||
export 'settings_model.dart';
|
||||
|
17
lib/models/name_value_model.dart
Normal file
17
lib/models/name_value_model.dart
Normal file
@ -0,0 +1,17 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
part 'name_value_model.freezed.dart';
|
||||
|
||||
part 'name_value_model.g.dart';
|
||||
|
||||
@freezed
|
||||
class NameValueModel with _$NameValueModel {
|
||||
const factory NameValueModel({
|
||||
required String name,
|
||||
required dynamic value,
|
||||
}) = _NameValueModel;
|
||||
|
||||
factory NameValueModel.fromJson(Map<String, Object?> json) =>
|
||||
_$NameValueModelFromJson(json);
|
||||
}
|
181
lib/models/name_value_model.freezed.dart
Normal file
181
lib/models/name_value_model.freezed.dart
Normal file
@ -0,0 +1,181 @@
|
||||
// coverage:ignore-file
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||
|
||||
part of 'name_value_model.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// FreezedGenerator
|
||||
// **************************************************************************
|
||||
|
||||
T _$identity<T>(T value) => value;
|
||||
|
||||
final _privateConstructorUsedError = UnsupportedError(
|
||||
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods');
|
||||
|
||||
NameValueModel _$NameValueModelFromJson(Map<String, dynamic> json) {
|
||||
return _NameValueModel.fromJson(json);
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
mixin _$NameValueModel {
|
||||
String get name => throw _privateConstructorUsedError;
|
||||
dynamic get value => throw _privateConstructorUsedError;
|
||||
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
@JsonKey(ignore: true)
|
||||
$NameValueModelCopyWith<NameValueModel> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $NameValueModelCopyWith<$Res> {
|
||||
factory $NameValueModelCopyWith(
|
||||
NameValueModel value, $Res Function(NameValueModel) then) =
|
||||
_$NameValueModelCopyWithImpl<$Res, NameValueModel>;
|
||||
@useResult
|
||||
$Res call({String name, dynamic value});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$NameValueModelCopyWithImpl<$Res, $Val extends NameValueModel>
|
||||
implements $NameValueModelCopyWith<$Res> {
|
||||
_$NameValueModelCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? name = null,
|
||||
Object? value = freezed,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
name: null == name
|
||||
? _value.name
|
||||
: name // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
value: freezed == value
|
||||
? _value.value
|
||||
: value // ignore: cast_nullable_to_non_nullable
|
||||
as dynamic,
|
||||
) as $Val);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$_NameValueModelCopyWith<$Res>
|
||||
implements $NameValueModelCopyWith<$Res> {
|
||||
factory _$$_NameValueModelCopyWith(
|
||||
_$_NameValueModel value, $Res Function(_$_NameValueModel) then) =
|
||||
__$$_NameValueModelCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call({String name, dynamic value});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$_NameValueModelCopyWithImpl<$Res>
|
||||
extends _$NameValueModelCopyWithImpl<$Res, _$_NameValueModel>
|
||||
implements _$$_NameValueModelCopyWith<$Res> {
|
||||
__$$_NameValueModelCopyWithImpl(
|
||||
_$_NameValueModel _value, $Res Function(_$_NameValueModel) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? name = null,
|
||||
Object? value = freezed,
|
||||
}) {
|
||||
return _then(_$_NameValueModel(
|
||||
name: null == name
|
||||
? _value.name
|
||||
: name // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
value: freezed == value
|
||||
? _value.value
|
||||
: value // ignore: cast_nullable_to_non_nullable
|
||||
as dynamic,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
class _$_NameValueModel
|
||||
with DiagnosticableTreeMixin
|
||||
implements _NameValueModel {
|
||||
const _$_NameValueModel({required this.name, required this.value});
|
||||
|
||||
factory _$_NameValueModel.fromJson(Map<String, dynamic> json) =>
|
||||
_$$_NameValueModelFromJson(json);
|
||||
|
||||
@override
|
||||
final String name;
|
||||
@override
|
||||
final dynamic value;
|
||||
|
||||
@override
|
||||
String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) {
|
||||
return 'NameValueModel(name: $name, value: $value)';
|
||||
}
|
||||
|
||||
@override
|
||||
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||
super.debugFillProperties(properties);
|
||||
properties
|
||||
..add(DiagnosticsProperty('type', 'NameValueModel'))
|
||||
..add(DiagnosticsProperty('name', name))
|
||||
..add(DiagnosticsProperty('value', value));
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(dynamic other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$_NameValueModel &&
|
||||
(identical(other.name, name) || other.name == name) &&
|
||||
const DeepCollectionEquality().equals(other.value, value));
|
||||
}
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType, name, const DeepCollectionEquality().hash(value));
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$_NameValueModelCopyWith<_$_NameValueModel> get copyWith =>
|
||||
__$$_NameValueModelCopyWithImpl<_$_NameValueModel>(this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$$_NameValueModelToJson(
|
||||
this,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _NameValueModel implements NameValueModel {
|
||||
const factory _NameValueModel(
|
||||
{required final String name,
|
||||
required final dynamic value}) = _$_NameValueModel;
|
||||
|
||||
factory _NameValueModel.fromJson(Map<String, dynamic> json) =
|
||||
_$_NameValueModel.fromJson;
|
||||
|
||||
@override
|
||||
String get name;
|
||||
@override
|
||||
dynamic get value;
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
_$$_NameValueModelCopyWith<_$_NameValueModel> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
19
lib/models/name_value_model.g.dart
Normal file
19
lib/models/name_value_model.g.dart
Normal file
@ -0,0 +1,19 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'name_value_model.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
_$_NameValueModel _$$_NameValueModelFromJson(Map<String, dynamic> json) =>
|
||||
_$_NameValueModel(
|
||||
name: json['name'] as String,
|
||||
value: json['value'],
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$_NameValueModelToJson(_$_NameValueModel instance) =>
|
||||
<String, dynamic>{
|
||||
'name': instance.name,
|
||||
'value': instance.value,
|
||||
};
|
@ -1,21 +1,21 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:apidash/consts.dart';
|
||||
import 'package:apidash/utils/utils.dart' show mapToRows, rowsToMap;
|
||||
import 'kvrow_model.dart';
|
||||
import 'name_value_model.dart';
|
||||
import 'response_model.dart';
|
||||
|
||||
@immutable
|
||||
class RequestModel {
|
||||
const RequestModel({
|
||||
required this.id,
|
||||
this.method = kDefaultHttpMethod,
|
||||
this.method = HTTPVerb.get,
|
||||
this.url = "",
|
||||
this.name = "",
|
||||
this.description = "",
|
||||
this.requestTabIndex = 0,
|
||||
this.requestHeaders,
|
||||
this.requestParams,
|
||||
this.requestBodyContentType = kDefaultContentType,
|
||||
this.requestBodyContentType = ContentType.json,
|
||||
this.requestBody,
|
||||
this.responseStatus,
|
||||
this.message,
|
||||
@ -28,8 +28,8 @@ class RequestModel {
|
||||
final String name;
|
||||
final String description;
|
||||
final int requestTabIndex;
|
||||
final List<KVRow>? requestHeaders;
|
||||
final List<KVRow>? requestParams;
|
||||
final List<NameValueModel>? requestHeaders;
|
||||
final List<NameValueModel>? requestParams;
|
||||
final ContentType requestBodyContentType;
|
||||
final String? requestBody;
|
||||
final int? responseStatus;
|
||||
@ -59,8 +59,8 @@ class RequestModel {
|
||||
String? name,
|
||||
String? description,
|
||||
int? requestTabIndex,
|
||||
List<KVRow>? requestHeaders,
|
||||
List<KVRow>? requestParams,
|
||||
List<NameValueModel>? requestHeaders,
|
||||
List<NameValueModel>? requestParams,
|
||||
ContentType? requestBodyContentType,
|
||||
String? requestBody,
|
||||
int? responseStatus,
|
||||
|
@ -96,8 +96,8 @@ class CollectionStateNotifier extends StateNotifier<List<RequestModel>?> {
|
||||
String? name,
|
||||
String? description,
|
||||
int? requestTabIndex,
|
||||
List<KVRow>? requestHeaders,
|
||||
List<KVRow>? requestParams,
|
||||
List<NameValueModel>? requestHeaders,
|
||||
List<NameValueModel>? requestParams,
|
||||
ContentType? requestBodyContentType,
|
||||
String? requestBody,
|
||||
int? responseStatus,
|
||||
@ -132,17 +132,17 @@ class CollectionStateNotifier extends StateNotifier<List<RequestModel>?> {
|
||||
var responseRec =
|
||||
await request(requestModel, defaultUriScheme: defaultUriScheme);
|
||||
late final RequestModel newRequestModel;
|
||||
if (responseRec.$0 == null) {
|
||||
if (responseRec.$1 == null) {
|
||||
newRequestModel = requestModel.copyWith(
|
||||
responseStatus: -1,
|
||||
message: responseRec.$2,
|
||||
message: responseRec.$3,
|
||||
);
|
||||
} else {
|
||||
final responseModel = baseResponseModel.fromResponse(
|
||||
response: responseRec.$0!,
|
||||
time: responseRec.$1!,
|
||||
response: responseRec.$1!,
|
||||
time: responseRec.$2!,
|
||||
);
|
||||
int statusCode = responseRec.$0!.statusCode;
|
||||
int statusCode = responseRec.$1!.statusCode;
|
||||
newRequestModel = requestModel.copyWith(
|
||||
responseStatus: statusCode,
|
||||
message: kResponseCodeReasons[statusCode],
|
||||
@ -203,4 +203,10 @@ class CollectionStateNotifier extends StateNotifier<List<RequestModel>?> {
|
||||
await hiveHandler.removeUnused();
|
||||
ref.read(saveDataStateProvider.notifier).update((state) => false);
|
||||
}
|
||||
|
||||
Map<String, dynamic> exportData() {
|
||||
return {
|
||||
"data": state!.map((e) => e.toJson(includeResponse: false)).toList()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
import 'package:apidash/consts.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
final navRailIndexStateProvider = StateProvider<int?>((ref) => 0);
|
||||
@ -7,3 +8,4 @@ final sentRequestIdStateProvider = StateProvider<String?>((ref) => null);
|
||||
final codePaneVisibleStateProvider = StateProvider<bool>((ref) => false);
|
||||
final saveDataStateProvider = StateProvider<bool>((ref) => false);
|
||||
final clearDataStateProvider = StateProvider<bool>((ref) => false);
|
||||
final codegenLanguageStateProvider = StateProvider<CodegenLanguage>((ref) => CodegenLanguage.dartHttp);
|
@ -82,11 +82,15 @@ class _DashboardState extends ConsumerState<Dashboard> {
|
||||
color: Theme.of(context).colorScheme.surfaceVariant,
|
||||
),
|
||||
Expanded(
|
||||
child: <int?, Widget>{
|
||||
null: const SettingsPage(),
|
||||
0: const IntroPage(),
|
||||
1: const HomePage()
|
||||
}[railIdx]!,
|
||||
child: IndexedStack(
|
||||
alignment: AlignmentDirectional.topCenter,
|
||||
index: railIdx == null ? 0 : railIdx + 1,
|
||||
children: const [
|
||||
SettingsPage(),
|
||||
IntroPage(),
|
||||
HomePage(),
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
|
@ -13,7 +13,7 @@ class CodePane extends ConsumerStatefulWidget {
|
||||
}
|
||||
|
||||
class _CodePaneState extends ConsumerState<CodePane> {
|
||||
final DartHttpCodeGen dartHttpCodeGen = DartHttpCodeGen();
|
||||
final Codegen codegen = Codegen();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@ -22,10 +22,15 @@ class _CodePaneState extends ConsumerState<CodePane> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final CodegenLanguage codegenLanguage =
|
||||
ref.watch(codegenLanguageStateProvider);
|
||||
|
||||
final activeRequestModel = ref.watch(activeRequestModelProvider);
|
||||
final defaultUriScheme =
|
||||
ref.watch(settingsProvider.select((value) => value.defaultUriScheme));
|
||||
final code = dartHttpCodeGen.getCode(activeRequestModel!, defaultUriScheme);
|
||||
|
||||
final code =
|
||||
codegen.getCode(codegenLanguage, activeRequestModel!, defaultUriScheme);
|
||||
if (code == null) {
|
||||
return const ErrorMessage(
|
||||
message: "An error was encountered while generating code. $kRaiseIssue",
|
||||
@ -33,6 +38,12 @@ class _CodePaneState extends ConsumerState<CodePane> {
|
||||
}
|
||||
return ViewCodePane(
|
||||
code: code,
|
||||
codegenLanguage: codegenLanguage,
|
||||
onChangedCodegenLanguage: (CodegenLanguage? value) {
|
||||
ref
|
||||
.read(codegenLanguageStateProvider.notifier)
|
||||
.update((state) => value!);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ class _EditRequestBodyState extends ConsumerState<EditRequestBody> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final activeId = ref.watch(activeIdStateProvider);
|
||||
final reqestModel = ref
|
||||
final requestModel = ref
|
||||
.read(collectionStateNotifierProvider.notifier)
|
||||
.getRequestModel(activeId!);
|
||||
return Container(
|
||||
@ -25,11 +25,11 @@ class _EditRequestBodyState extends ConsumerState<EditRequestBody> {
|
||||
margin: kPt5o10,
|
||||
child: Column(
|
||||
children: [
|
||||
SizedBox(
|
||||
const SizedBox(
|
||||
height: kHeaderHeight,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: const [
|
||||
children: [
|
||||
Text(
|
||||
"Select Content Type:",
|
||||
),
|
||||
@ -41,7 +41,7 @@ class _EditRequestBodyState extends ConsumerState<EditRequestBody> {
|
||||
child: TextFieldEditor(
|
||||
key: Key("$activeId-body"),
|
||||
fieldKey: "$activeId-body-editor",
|
||||
initialValue: reqestModel.requestBody,
|
||||
initialValue: requestModel.requestBody,
|
||||
onChanged: (String value) {
|
||||
ref
|
||||
.read(collectionStateNotifierProvider.notifier)
|
||||
|
@ -15,7 +15,7 @@ class EditRequestHeaders extends ConsumerStatefulWidget {
|
||||
}
|
||||
|
||||
class EditRequestHeadersState extends ConsumerState<EditRequestHeaders> {
|
||||
late List<KVRow> rows;
|
||||
late List<NameValueModel> rows;
|
||||
final random = Random.secure();
|
||||
late int seed;
|
||||
|
||||
@ -37,9 +37,11 @@ class EditRequestHeadersState extends ConsumerState<EditRequestHeaders> {
|
||||
final length = ref.watch(activeRequestModelProvider
|
||||
.select((value) => value?.requestHeaders?.length));
|
||||
var rH = ref.read(activeRequestModelProvider)?.requestHeaders;
|
||||
rows = (rH == null || rH.isEmpty) ? [const KVRow("", "")] : rH;
|
||||
rows = (rH == null || rH.isEmpty)
|
||||
? [const NameValueModel(name: "", value: "")]
|
||||
: rH;
|
||||
|
||||
DaviModel<KVRow> model = DaviModel<KVRow>(
|
||||
DaviModel<NameValueModel> model = DaviModel<NameValueModel>(
|
||||
rows: rows,
|
||||
columns: [
|
||||
DaviColumn(
|
||||
@ -49,10 +51,10 @@ class EditRequestHeadersState extends ConsumerState<EditRequestHeaders> {
|
||||
int idx = row.index;
|
||||
return CellField(
|
||||
keyId: "$activeId-$idx-headers-k-$seed",
|
||||
initialValue: rows[idx].k,
|
||||
initialValue: rows[idx].name,
|
||||
hintText: "Add Header Name",
|
||||
onChanged: (value) {
|
||||
rows[idx] = rows[idx].copyWith(k: value);
|
||||
rows[idx] = rows[idx].copyWith(name: value);
|
||||
_onFieldChange(activeId!);
|
||||
},
|
||||
colorScheme: Theme.of(context).colorScheme,
|
||||
@ -76,10 +78,10 @@ class EditRequestHeadersState extends ConsumerState<EditRequestHeaders> {
|
||||
int idx = row.index;
|
||||
return CellField(
|
||||
keyId: "$activeId-$idx-headers-v-$seed",
|
||||
initialValue: rows[idx].v,
|
||||
initialValue: rows[idx].value,
|
||||
hintText: " Add Header Value",
|
||||
onChanged: (value) {
|
||||
rows[idx] = rows[idx].copyWith(v: value);
|
||||
rows[idx] = rows[idx].copyWith(value: value);
|
||||
_onFieldChange(activeId!);
|
||||
},
|
||||
colorScheme: Theme.of(context).colorScheme,
|
||||
@ -96,6 +98,9 @@ class EditRequestHeadersState extends ConsumerState<EditRequestHeaders> {
|
||||
? kIconRemoveDark
|
||||
: kIconRemoveLight,
|
||||
onTap: () {
|
||||
if (rows.length == 1) {
|
||||
return;
|
||||
}
|
||||
rows.removeAt(row.index);
|
||||
seed = random.nextInt(kRandMax);
|
||||
_onFieldChange(activeId!);
|
||||
@ -118,7 +123,7 @@ class EditRequestHeadersState extends ConsumerState<EditRequestHeaders> {
|
||||
Expanded(
|
||||
child: DaviTheme(
|
||||
data: kTableThemeData,
|
||||
child: Davi<KVRow>(model),
|
||||
child: Davi<NameValueModel>(model),
|
||||
),
|
||||
),
|
||||
],
|
||||
@ -130,7 +135,7 @@ class EditRequestHeadersState extends ConsumerState<EditRequestHeaders> {
|
||||
padding: const EdgeInsets.only(bottom: 30),
|
||||
child: ElevatedButton.icon(
|
||||
onPressed: () {
|
||||
rows.add(const KVRow("", ""));
|
||||
rows.add(const NameValueModel(name: "", value: ""));
|
||||
_onFieldChange(activeId!);
|
||||
},
|
||||
icon: const Icon(Icons.add),
|
||||
|
@ -16,7 +16,7 @@ class EditRequestURLParams extends ConsumerStatefulWidget {
|
||||
}
|
||||
|
||||
class EditRequestURLParamsState extends ConsumerState<EditRequestURLParams> {
|
||||
late List<KVRow> rows;
|
||||
late List<NameValueModel> rows;
|
||||
final random = Random.secure();
|
||||
late int seed;
|
||||
|
||||
@ -38,9 +38,11 @@ class EditRequestURLParamsState extends ConsumerState<EditRequestURLParams> {
|
||||
final length = ref.watch(activeRequestModelProvider
|
||||
.select((value) => value?.requestParams?.length));
|
||||
var rP = ref.read(activeRequestModelProvider)?.requestParams;
|
||||
rows = (rP == null || rP.isEmpty) ? [const KVRow("", "")] : rP;
|
||||
rows = (rP == null || rP.isEmpty)
|
||||
? [const NameValueModel(name: "", value: "")]
|
||||
: rP;
|
||||
|
||||
DaviModel<KVRow> model = DaviModel<KVRow>(
|
||||
DaviModel<NameValueModel> model = DaviModel<NameValueModel>(
|
||||
rows: rows,
|
||||
columns: [
|
||||
DaviColumn(
|
||||
@ -50,10 +52,10 @@ class EditRequestURLParamsState extends ConsumerState<EditRequestURLParams> {
|
||||
int idx = row.index;
|
||||
return CellField(
|
||||
keyId: "$activeId-$idx-params-k-$seed",
|
||||
initialValue: rows[idx].k,
|
||||
initialValue: rows[idx].name,
|
||||
hintText: "Add URL Parameter",
|
||||
onChanged: (value) {
|
||||
rows[idx] = rows[idx].copyWith(k: value);
|
||||
rows[idx] = rows[idx].copyWith(name: value);
|
||||
_onFieldChange(activeId!);
|
||||
},
|
||||
colorScheme: Theme.of(context).colorScheme,
|
||||
@ -77,10 +79,10 @@ class EditRequestURLParamsState extends ConsumerState<EditRequestURLParams> {
|
||||
int idx = row.index;
|
||||
return CellField(
|
||||
keyId: "$activeId-$idx-params-v-$seed",
|
||||
initialValue: rows[idx].v,
|
||||
initialValue: rows[idx].value,
|
||||
hintText: "Add Value",
|
||||
onChanged: (value) {
|
||||
rows[idx] = rows[idx].copyWith(v: value);
|
||||
rows[idx] = rows[idx].copyWith(value: value);
|
||||
_onFieldChange(activeId!);
|
||||
},
|
||||
colorScheme: Theme.of(context).colorScheme,
|
||||
@ -97,6 +99,9 @@ class EditRequestURLParamsState extends ConsumerState<EditRequestURLParams> {
|
||||
? kIconRemoveDark
|
||||
: kIconRemoveLight,
|
||||
onTap: () {
|
||||
if (rows.length == 1) {
|
||||
return;
|
||||
}
|
||||
rows.removeAt(row.index);
|
||||
seed = random.nextInt(kRandMax);
|
||||
_onFieldChange(activeId!);
|
||||
@ -119,7 +124,7 @@ class EditRequestURLParamsState extends ConsumerState<EditRequestURLParams> {
|
||||
Expanded(
|
||||
child: DaviTheme(
|
||||
data: kTableThemeData,
|
||||
child: Davi<KVRow>(model),
|
||||
child: Davi<NameValueModel>(model),
|
||||
),
|
||||
),
|
||||
],
|
||||
@ -131,7 +136,7 @@ class EditRequestURLParamsState extends ConsumerState<EditRequestURLParams> {
|
||||
padding: const EdgeInsets.only(bottom: 30),
|
||||
child: ElevatedButton.icon(
|
||||
onPressed: () {
|
||||
rows.add(const KVRow("", ""));
|
||||
rows.add(const NameValueModel(name: "", value: ""));
|
||||
_onFieldChange(activeId!);
|
||||
},
|
||||
icon: const Icon(Icons.add),
|
||||
|
@ -28,8 +28,8 @@ class _RequestEditorPaneState extends ConsumerState<RequestEditorPane> {
|
||||
} else {
|
||||
return Padding(
|
||||
padding: kIsMacOS ? kPt24o8 : kP8,
|
||||
child: Column(
|
||||
children: const [
|
||||
child: const Column(
|
||||
children: [
|
||||
EditorPaneRequestURLCard(),
|
||||
kVSpacer10,
|
||||
Expanded(
|
||||
|
@ -28,13 +28,13 @@ class _EditorPaneRequestURLCardState extends State<EditorPaneRequestURLCard> {
|
||||
),
|
||||
borderRadius: kBorderRadius12,
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
child: const Padding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
vertical: 5,
|
||||
horizontal: 20,
|
||||
),
|
||||
child: Row(
|
||||
children: const [
|
||||
children: [
|
||||
DropdownButtonHTTPMethod(),
|
||||
kHSpacer20,
|
||||
Expanded(
|
||||
|
@ -13,8 +13,8 @@ class HomePage extends StatefulWidget {
|
||||
class HomePageState extends State<HomePage> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
children: const [
|
||||
return const Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: DashboardSplitView(
|
||||
sidebarWidget: CollectionPane(),
|
||||
|
@ -1,6 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:apidash/widgets/widgets.dart';
|
||||
import 'package:apidash/consts.dart';
|
||||
|
||||
class IntroPage extends StatefulWidget {
|
||||
const IntroPage({super.key});
|
||||
@ -12,9 +11,6 @@ class IntroPage extends StatefulWidget {
|
||||
class _IntroPageState extends State<IntroPage> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return const Padding(
|
||||
padding: kPh60,
|
||||
child: IntroMessage(),
|
||||
);
|
||||
return const IntroMessage();
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,8 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:apidash/providers/providers.dart';
|
||||
import 'package:apidash/widgets/widgets.dart';
|
||||
import '../providers/providers.dart';
|
||||
import '../widgets/widgets.dart';
|
||||
import '../utils/utils.dart';
|
||||
import 'package:apidash/consts.dart';
|
||||
|
||||
class SettingsPage extends ConsumerStatefulWidget {
|
||||
@ -73,6 +74,24 @@ class _SettingsPageState extends ConsumerState<SettingsPage> {
|
||||
.update(saveResponses: value);
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
hoverColor: kColorTransparent,
|
||||
title: const Text('Export Collection'),
|
||||
subtitle: const Text('Export your collection to a JSON file'),
|
||||
trailing: FilledButton(
|
||||
onPressed: () async {
|
||||
var data = ref
|
||||
.read(collectionStateNotifierProvider.notifier)
|
||||
.exportData();
|
||||
var pth = await getFileDownloadpath(null, "json");
|
||||
if (pth != null) {
|
||||
await saveFile(pth, jsonMapToBytes(data));
|
||||
}
|
||||
},
|
||||
child: const Text("Export Data"),
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
hoverColor: kColorTransparent,
|
||||
|
@ -7,66 +7,59 @@ import 'package:apidash/models/models.dart';
|
||||
import 'package:apidash/consts.dart';
|
||||
|
||||
Future<(http.Response?, Duration?, String?)> request(
|
||||
RequestModel requestModel,
|
||||
{String defaultUriScheme = kDefaultUriScheme}
|
||||
) async {
|
||||
(Uri?, String?) uriRec = getValidRequestUri(requestModel.url,
|
||||
requestModel.requestParams,
|
||||
defaultUriScheme: defaultUriScheme);
|
||||
if(uriRec.$0 != null){
|
||||
Uri requestUrl = uriRec.$0!;
|
||||
RequestModel requestModel, {
|
||||
String defaultUriScheme = kDefaultUriScheme,
|
||||
}) async {
|
||||
(Uri?, String?) uriRec = getValidRequestUri(
|
||||
requestModel.url,
|
||||
requestModel.requestParams,
|
||||
defaultUriScheme: defaultUriScheme,
|
||||
);
|
||||
if (uriRec.$1 != null) {
|
||||
Uri requestUrl = uriRec.$1!;
|
||||
Map<String, String> headers = rowsToMap(requestModel.requestHeaders) ?? {};
|
||||
http.Response response;
|
||||
String? body;
|
||||
try {
|
||||
try {
|
||||
var requestBody = requestModel.requestBody;
|
||||
if(kMethodsWithBody.contains(requestModel.method) && requestBody != null){
|
||||
if (kMethodsWithBody.contains(requestModel.method) &&
|
||||
requestBody != null) {
|
||||
var contentLength = utf8.encode(requestBody).length;
|
||||
if (contentLength > 0){
|
||||
if (contentLength > 0) {
|
||||
body = requestBody;
|
||||
headers[HttpHeaders.contentLengthHeader] = contentLength.toString();
|
||||
headers[HttpHeaders.contentTypeHeader] = kContentTypeMap[requestModel.requestBodyContentType] ?? "";
|
||||
headers[HttpHeaders.contentTypeHeader] =
|
||||
kContentTypeMap[requestModel.requestBodyContentType] ?? "";
|
||||
}
|
||||
}
|
||||
Stopwatch stopwatch = Stopwatch()..start();
|
||||
switch(requestModel.method){
|
||||
switch (requestModel.method) {
|
||||
case HTTPVerb.get:
|
||||
response = await http.get(requestUrl,
|
||||
headers: headers);
|
||||
response = await http.get(requestUrl, headers: headers);
|
||||
break;
|
||||
case HTTPVerb.head:
|
||||
response = await http.head(requestUrl,
|
||||
headers: headers);
|
||||
response = await http.head(requestUrl, headers: headers);
|
||||
break;
|
||||
case HTTPVerb.post:
|
||||
response = await http.post(requestUrl,
|
||||
headers: headers,
|
||||
body: body);
|
||||
response = await http.post(requestUrl, headers: headers, body: body);
|
||||
break;
|
||||
case HTTPVerb.put:
|
||||
response = await http.put(requestUrl,
|
||||
headers: headers,
|
||||
body: body);
|
||||
response = await http.put(requestUrl, headers: headers, body: body);
|
||||
break;
|
||||
case HTTPVerb.patch:
|
||||
response = await http.patch(requestUrl,
|
||||
headers: headers,
|
||||
body: body);
|
||||
response = await http.patch(requestUrl, headers: headers, body: body);
|
||||
break;
|
||||
case HTTPVerb.delete:
|
||||
response = await http.delete(requestUrl,
|
||||
headers: headers,
|
||||
body: body);
|
||||
response =
|
||||
await http.delete(requestUrl, headers: headers, body: body);
|
||||
break;
|
||||
}
|
||||
stopwatch.stop();
|
||||
stopwatch.stop();
|
||||
return (response, stopwatch.elapsed, null);
|
||||
}
|
||||
catch (e) {
|
||||
} catch (e) {
|
||||
return (null, null, e.toString());
|
||||
}
|
||||
}
|
||||
else {
|
||||
return (null, null, uriRec.$1);
|
||||
} else {
|
||||
return (null, null, uriRec.$2);
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import 'dart:typed_data';
|
||||
import 'dart:convert';
|
||||
import '../models/models.dart';
|
||||
import '../consts.dart';
|
||||
import 'package:apidash/models/models.dart' show KVRow;
|
||||
|
||||
String humanizeDuration(Duration? duration) {
|
||||
if (duration == null) {
|
||||
@ -24,6 +24,14 @@ String humanizeDuration(Duration? duration) {
|
||||
}
|
||||
}
|
||||
|
||||
String audioPosition(Duration? duration) {
|
||||
if (duration == null) return "";
|
||||
var min = duration.inMinutes;
|
||||
var secs = duration.inSeconds.remainder(60);
|
||||
var secondsPadding = secs < 10 ? "0" : "";
|
||||
return "$min:$secondsPadding$secs";
|
||||
}
|
||||
|
||||
String capitalizeFirstLetter(String? text) {
|
||||
if (text == null || text == "") {
|
||||
return "";
|
||||
@ -52,30 +60,31 @@ String padMultilineString(String text, int padding,
|
||||
return lines.join("\n");
|
||||
}
|
||||
|
||||
Map<String, String>? rowsToMap(List<KVRow>? kvRows, {bool isHeader = false}) {
|
||||
Map<String, String>? rowsToMap(List<NameValueModel>? kvRows,
|
||||
{bool isHeader = false}) {
|
||||
if (kvRows == null) {
|
||||
return null;
|
||||
}
|
||||
Map<String, String> finalMap = {};
|
||||
for (var row in kvRows) {
|
||||
if (row.k.trim() != "") {
|
||||
String key = row.k;
|
||||
if (row.name.trim() != "") {
|
||||
String key = row.name;
|
||||
if (isHeader) {
|
||||
key = key.toLowerCase();
|
||||
}
|
||||
finalMap[key] = row.v.toString();
|
||||
finalMap[key] = row.value.toString();
|
||||
}
|
||||
}
|
||||
return finalMap;
|
||||
}
|
||||
|
||||
List<KVRow>? mapToRows(Map<String, String>? kvMap) {
|
||||
List<NameValueModel>? mapToRows(Map<String, String>? kvMap) {
|
||||
if (kvMap == null) {
|
||||
return null;
|
||||
}
|
||||
List<KVRow> finalRows = [];
|
||||
List<NameValueModel> finalRows = [];
|
||||
for (var k in kvMap.keys) {
|
||||
finalRows.add(KVRow(k, kvMap[k]));
|
||||
finalRows.add(NameValueModel(name: k, value: kvMap[k]));
|
||||
}
|
||||
return finalRows;
|
||||
}
|
||||
@ -89,3 +98,14 @@ Uint8List? stringToBytes(String? text) {
|
||||
return bytes;
|
||||
}
|
||||
}
|
||||
|
||||
Uint8List jsonMapToBytes(Map<String, dynamic>? map) {
|
||||
if (map == null) {
|
||||
return Uint8List.fromList([]);
|
||||
} else {
|
||||
String text = kEncoder.convert(map);
|
||||
var l = utf8.encode(text);
|
||||
var bytes = Uint8List.fromList(l);
|
||||
return bytes;
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ import 'dart:io';
|
||||
import 'package:collection/collection.dart' show mergeMaps;
|
||||
import 'package:http_parser/http_parser.dart';
|
||||
import 'package:xml/xml.dart';
|
||||
import 'package:apidash/models/models.dart' show KVRow;
|
||||
import '../models/models.dart';
|
||||
import 'convert_utils.dart' show rowsToMap;
|
||||
import '../consts.dart';
|
||||
|
||||
@ -44,8 +44,8 @@ MediaType? getMediaTypeFromHeaders(Map? headers) {
|
||||
}
|
||||
|
||||
(String?, bool) getUriScheme(Uri uri) {
|
||||
if(uri.hasScheme){
|
||||
if(kSupportedUriSchemes.contains(uri.scheme)){
|
||||
if (uri.hasScheme) {
|
||||
if (kSupportedUriSchemes.contains(uri.scheme)) {
|
||||
return (uri.scheme, true);
|
||||
}
|
||||
return (uri.scheme, false);
|
||||
@ -54,37 +54,34 @@ MediaType? getMediaTypeFromHeaders(Map? headers) {
|
||||
}
|
||||
|
||||
(Uri?, String?) getValidRequestUri(
|
||||
String? url,
|
||||
List<KVRow>? requestParams,
|
||||
{String defaultUriScheme = kDefaultUriScheme}
|
||||
) {
|
||||
String? url, List<NameValueModel>? requestParams,
|
||||
{String defaultUriScheme = kDefaultUriScheme}) {
|
||||
url = url?.trim();
|
||||
if(url == null || url == ""){
|
||||
if (url == null || url == "") {
|
||||
return (null, "URL is missing!");
|
||||
}
|
||||
Uri? uri = Uri.tryParse(url);
|
||||
if(uri == null){
|
||||
Uri? uri = Uri.tryParse(url);
|
||||
if (uri == null) {
|
||||
return (null, "Check URL (malformed)");
|
||||
}
|
||||
(String?, bool) urlScheme = getUriScheme(uri);
|
||||
|
||||
if(urlScheme.$0 != null){
|
||||
if (!urlScheme.$1){
|
||||
return (null, "Unsupported URL Scheme (${urlScheme.$0})");
|
||||
if (urlScheme.$1 != null) {
|
||||
if (!urlScheme.$2) {
|
||||
return (null, "Unsupported URL Scheme (${urlScheme.$1})");
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
url = "$defaultUriScheme://$url";
|
||||
}
|
||||
|
||||
uri = Uri.parse(url);
|
||||
if (uri.hasFragment){
|
||||
uri = Uri.parse(url);
|
||||
if (uri.hasFragment) {
|
||||
uri = uri.removeFragment();
|
||||
}
|
||||
|
||||
Map<String, String>? queryParams = rowsToMap(requestParams);
|
||||
if(queryParams != null){
|
||||
if(uri.hasQuery){
|
||||
if (queryParams != null) {
|
||||
if (uri.hasQuery) {
|
||||
Map<String, String> urlQueryParams = uri.queryParameters;
|
||||
queryParams = mergeMaps(urlQueryParams, queryParams);
|
||||
}
|
||||
@ -93,48 +90,58 @@ MediaType? getMediaTypeFromHeaders(Map? headers) {
|
||||
return (uri, null);
|
||||
}
|
||||
|
||||
(List<ResponseBodyView>, String?) getResponseBodyViewOptions(MediaType? mediaType){
|
||||
if(mediaType != null){
|
||||
(List<ResponseBodyView>, String?) getResponseBodyViewOptions(
|
||||
MediaType? mediaType) {
|
||||
if (mediaType != null) {
|
||||
var type = mediaType.type;
|
||||
var subtype = mediaType.subtype;
|
||||
if(kResponseBodyViewOptions.containsKey(type)){
|
||||
if (kResponseBodyViewOptions[type]!.containsKey(subtype)){
|
||||
return (kResponseBodyViewOptions[type]![subtype]!, kCodeHighlighterMap[subtype] ?? subtype);
|
||||
if (kResponseBodyViewOptions.containsKey(type)) {
|
||||
if (kResponseBodyViewOptions[type]!.containsKey(subtype)) {
|
||||
return (
|
||||
kResponseBodyViewOptions[type]![subtype]!,
|
||||
kCodeHighlighterMap[subtype] ?? subtype
|
||||
);
|
||||
}
|
||||
if(subtype.contains(kSubTypeJson)){
|
||||
if (subtype.contains(kSubTypeJson)) {
|
||||
subtype = kSubTypeJson;
|
||||
}
|
||||
if(subtype.contains(kSubTypeXml)){
|
||||
if (subtype.contains(kSubTypeXml)) {
|
||||
subtype = kSubTypeXml;
|
||||
}
|
||||
if (kResponseBodyViewOptions[type]!.containsKey(subtype)){
|
||||
return (kResponseBodyViewOptions[type]![subtype]!, kCodeHighlighterMap[subtype] ?? subtype);
|
||||
if (kResponseBodyViewOptions[type]!.containsKey(subtype)) {
|
||||
return (
|
||||
kResponseBodyViewOptions[type]![subtype]!,
|
||||
kCodeHighlighterMap[subtype] ?? subtype
|
||||
);
|
||||
}
|
||||
return (kResponseBodyViewOptions[type]![kSubTypeDefaultViewOptions]!, subtype);
|
||||
return (
|
||||
kResponseBodyViewOptions[type]![kSubTypeDefaultViewOptions]!,
|
||||
subtype
|
||||
);
|
||||
}
|
||||
}
|
||||
return (kNoBodyViewOptions, null);
|
||||
}
|
||||
|
||||
String? formatBody(String? body, MediaType? mediaType){
|
||||
if(mediaType != null && body != null){
|
||||
String? formatBody(String? body, MediaType? mediaType) {
|
||||
if (mediaType != null && body != null) {
|
||||
var subtype = mediaType.subtype;
|
||||
try {
|
||||
if(subtype.contains(kSubTypeJson)){
|
||||
if (subtype.contains(kSubTypeJson)) {
|
||||
final tmp = jsonDecode(body);
|
||||
String result = kEncoder.convert(tmp);
|
||||
return result;
|
||||
}
|
||||
if(subtype.contains(kSubTypeXml)){
|
||||
if (subtype.contains(kSubTypeXml)) {
|
||||
final document = XmlDocument.parse(body);
|
||||
String result = document.toXmlString(pretty: true, indent: ' ');
|
||||
return result;
|
||||
}
|
||||
if(subtype == kSubTypeHtml){
|
||||
if (subtype == kSubTypeHtml) {
|
||||
var len = body.length;
|
||||
var lines = kSplitter.convert(body);
|
||||
var numOfLines = lines.length;
|
||||
if(numOfLines !=0 && len/numOfLines <= kCodeCharsPerLineLimit){
|
||||
if (numOfLines != 0 && len / numOfLines <= kCodeCharsPerLineLimit) {
|
||||
return body;
|
||||
}
|
||||
}
|
||||
@ -143,4 +150,4 @@ String? formatBody(String? body, MediaType? mediaType){
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -2,3 +2,4 @@ export 'ui_utils.dart';
|
||||
export 'convert_utils.dart';
|
||||
export 'http_utils.dart';
|
||||
export 'file_utils.dart';
|
||||
export 'window_utils.dart';
|
||||
|
18
lib/utils/window_utils.dart
Normal file
18
lib/utils/window_utils.dart
Normal file
@ -0,0 +1,18 @@
|
||||
bool showButtonLabelsInBodySuccess(int options, double maxWidth) {
|
||||
switch (options) {
|
||||
case 0:
|
||||
return true;
|
||||
case 1:
|
||||
return (maxWidth < 300) ? false : true;
|
||||
case 2:
|
||||
return (maxWidth < 400) ? false : true;
|
||||
case 3:
|
||||
return (maxWidth < 500) ? false : true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool showButtonLabelsInViewCodePane(double maxWidth) {
|
||||
return (maxWidth < 400) ? false : true;
|
||||
}
|
@ -104,12 +104,14 @@ class SaveInDownloadsButton extends StatefulWidget {
|
||||
super.key,
|
||||
this.content,
|
||||
this.mimeType,
|
||||
this.ext,
|
||||
this.name,
|
||||
this.showLabel = true,
|
||||
});
|
||||
|
||||
final Uint8List? content;
|
||||
final String? mimeType;
|
||||
final String? ext;
|
||||
final String? name;
|
||||
final bool showLabel;
|
||||
|
||||
@ -129,10 +131,9 @@ class _SaveInDownloadsButtonState extends State<SaveInDownloadsButton> {
|
||||
onPressed: (widget.content != null)
|
||||
? () async {
|
||||
var message = "";
|
||||
var ext = getFileExtension(widget.mimeType);
|
||||
var path = await getFileDownloadpath(
|
||||
widget.name,
|
||||
ext,
|
||||
widget.ext ?? getFileExtension(widget.mimeType),
|
||||
);
|
||||
if (path != null) {
|
||||
try {
|
||||
|
@ -6,7 +6,7 @@ import 'error_message.dart';
|
||||
(String, bool) sanitize(String input) {
|
||||
bool limitedLines = false;
|
||||
int tabSize = 4;
|
||||
var lines = kSplitter.convert(input);
|
||||
var lines = kSplitter.convert(input);
|
||||
if (lines.length > kCodePreviewLinesLimit) {
|
||||
lines = lines.sublist(0, kCodePreviewLinesLimit);
|
||||
limitedLines = true;
|
||||
@ -67,7 +67,12 @@ class _CodePreviewerState extends State<CodePreviewer> {
|
||||
textStyle = textStyle.merge(widget.textStyle);
|
||||
}
|
||||
processed = sanitize(widget.code);
|
||||
spans = asyncGenerateSpans(processed.$0, widget.language, widget.theme, processed.$1);
|
||||
spans = asyncGenerateSpans(
|
||||
processed.$1,
|
||||
widget.language,
|
||||
widget.theme,
|
||||
processed.$2,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
@ -131,12 +136,14 @@ class _CodePreviewerState extends State<CodePreviewer> {
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<TextSpan>> asyncGenerateSpans(
|
||||
String code, String? language, Map<String, TextStyle> theme, bool limitedLines) async {
|
||||
Future<List<TextSpan>> asyncGenerateSpans(String code, String? language,
|
||||
Map<String, TextStyle> theme, bool limitedLines) async {
|
||||
var parsed = highlight.parse(code, language: language);
|
||||
var spans = convert(parsed.nodes!, theme);
|
||||
if(limitedLines) {
|
||||
spans.add(const TextSpan(text: "\n... more.\nPreview ends here ($kCodePreviewLinesLimit lines).\nYou can check Raw for full result."));
|
||||
if (limitedLines) {
|
||||
spans.add(const TextSpan(
|
||||
text:
|
||||
"\n... more.\nPreview ends here ($kCodePreviewLinesLimit lines).\nYou can check Raw for full result."));
|
||||
}
|
||||
return spans;
|
||||
}
|
||||
|
@ -2,8 +2,9 @@ import 'package:flutter/material.dart';
|
||||
import 'package:highlighter/highlighter.dart' show highlight;
|
||||
import 'package:apidash/consts.dart';
|
||||
import 'package:apidash/utils/utils.dart';
|
||||
import 'code_previewer.dart' show convert;
|
||||
import 'buttons.dart';
|
||||
import 'code_previewer.dart';
|
||||
import 'widgets.dart'
|
||||
show CopyButton, DropdownButtonCodegenLanguage, SaveInDownloadsButton;
|
||||
|
||||
class CodeGenPreviewer extends StatefulWidget {
|
||||
const CodeGenPreviewer({
|
||||
@ -105,9 +106,13 @@ class ViewCodePane extends StatefulWidget {
|
||||
const ViewCodePane({
|
||||
super.key,
|
||||
required this.code,
|
||||
required this.codegenLanguage,
|
||||
required this.onChangedCodegenLanguage,
|
||||
});
|
||||
|
||||
final String code;
|
||||
final CodegenLanguage codegenLanguage;
|
||||
final Function(CodegenLanguage?) onChangedCodegenLanguage;
|
||||
|
||||
@override
|
||||
State<ViewCodePane> createState() => _ViewCodePaneState();
|
||||
@ -130,44 +135,55 @@ class _ViewCodePaneState extends State<ViewCodePane> {
|
||||
borderRadius: kBorderRadius8,
|
||||
);
|
||||
|
||||
return Padding(
|
||||
padding: kP10,
|
||||
child: Column(
|
||||
children: [
|
||||
SizedBox(
|
||||
height: kHeaderHeight,
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
"Code",
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
return LayoutBuilder(
|
||||
builder: (BuildContext context, BoxConstraints constraints) {
|
||||
var showLabel = showButtonLabelsInViewCodePane(
|
||||
constraints.maxWidth,
|
||||
);
|
||||
return Padding(
|
||||
padding: kP10,
|
||||
child: Column(
|
||||
children: [
|
||||
SizedBox(
|
||||
height: kHeaderHeight,
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: DropdownButtonCodegenLanguage(
|
||||
codegenLanguage: widget.codegenLanguage,
|
||||
onChanged: widget.onChangedCodegenLanguage,
|
||||
),
|
||||
),
|
||||
CopyButton(
|
||||
toCopy: widget.code,
|
||||
showLabel: showLabel,
|
||||
),
|
||||
SaveInDownloadsButton(
|
||||
content: stringToBytes(widget.code),
|
||||
ext: widget.codegenLanguage.ext,
|
||||
showLabel: showLabel,
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
kVSpacer10,
|
||||
Expanded(
|
||||
child: Container(
|
||||
width: double.maxFinite,
|
||||
padding: kP8,
|
||||
decoration: textContainerdecoration,
|
||||
child: CodeGenPreviewer(
|
||||
code: widget.code,
|
||||
theme: codeTheme,
|
||||
language: widget.codegenLanguage.codeHighlightLang,
|
||||
textStyle: kCodeStyle,
|
||||
),
|
||||
),
|
||||
CopyButton(toCopy: widget.code),
|
||||
SaveInDownloadsButton(
|
||||
content: stringToBytes(widget.code),
|
||||
mimeType: "application/vnd.dart",
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
kVSpacer10,
|
||||
Expanded(
|
||||
child: Container(
|
||||
width: double.maxFinite,
|
||||
padding: kP8,
|
||||
decoration: textContainerdecoration,
|
||||
child: CodeGenPreviewer(
|
||||
code: widget.code,
|
||||
theme: codeTheme,
|
||||
language: 'dart',
|
||||
textStyle: kCodeStyle,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -109,3 +109,55 @@ class _DropdownButtonContentTypeState extends State<DropdownButtonContentType> {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DropdownButtonCodegenLanguage extends StatefulWidget {
|
||||
const DropdownButtonCodegenLanguage({
|
||||
Key? key,
|
||||
this.codegenLanguage,
|
||||
this.onChanged,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<DropdownButtonCodegenLanguage> createState() =>
|
||||
_DropdownButtonCodegenLanguageState();
|
||||
final CodegenLanguage? codegenLanguage;
|
||||
final void Function(CodegenLanguage?)? onChanged;
|
||||
}
|
||||
|
||||
class _DropdownButtonCodegenLanguageState
|
||||
extends State<DropdownButtonCodegenLanguage> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final surfaceColor = Theme.of(context).colorScheme.surface;
|
||||
return DropdownButton<CodegenLanguage>(
|
||||
focusColor: surfaceColor,
|
||||
value: widget.codegenLanguage,
|
||||
icon: const Icon(
|
||||
Icons.unfold_more_rounded,
|
||||
size: 16,
|
||||
),
|
||||
elevation: 4,
|
||||
style: kCodeStyle.copyWith(
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
underline: Container(
|
||||
height: 0,
|
||||
),
|
||||
onChanged: widget.onChanged,
|
||||
borderRadius: kBorderRadius12,
|
||||
items: CodegenLanguage.values
|
||||
.map<DropdownMenuItem<CodegenLanguage>>((CodegenLanguage value) {
|
||||
return DropdownMenuItem<CodegenLanguage>(
|
||||
value: value,
|
||||
child: Padding(
|
||||
padding: kPs8,
|
||||
child: Text(
|
||||
value.label,
|
||||
style: kTextStyleButton,
|
||||
),
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
);
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart' show rootBundle;
|
||||
import 'package:apidash/consts.dart';
|
||||
import 'markdown.dart';
|
||||
import 'error_message.dart';
|
||||
|
||||
@ -28,7 +29,10 @@ class _IntroMessageState extends State<IntroMessage> {
|
||||
} else {
|
||||
text = text.replaceAll("{{mode}}", "light");
|
||||
}
|
||||
return CustomMarkdown(data: text);
|
||||
return CustomMarkdown(
|
||||
data: text,
|
||||
padding: kPh60,
|
||||
);
|
||||
}
|
||||
if (snapshot.hasError) {
|
||||
return const ErrorMessage(message: "An error occured");
|
||||
|
@ -8,8 +8,11 @@ class CustomMarkdown extends StatefulWidget {
|
||||
const CustomMarkdown({
|
||||
super.key,
|
||||
required this.data,
|
||||
this.padding = const EdgeInsets.all(16.0),
|
||||
});
|
||||
final String data;
|
||||
final EdgeInsets padding;
|
||||
|
||||
@override
|
||||
State<CustomMarkdown> createState() => _CustomMarkdownState();
|
||||
}
|
||||
@ -22,6 +25,7 @@ class _CustomMarkdownState extends State<CustomMarkdown> {
|
||||
p: Theme.of(context).textTheme.titleMedium,
|
||||
);
|
||||
return Markdown(
|
||||
padding: widget.padding,
|
||||
styleSheet: mdStyleSheet,
|
||||
data: widget.data,
|
||||
selectable: true,
|
||||
|
@ -3,6 +3,8 @@ import 'package:flutter/material.dart';
|
||||
import 'error_message.dart';
|
||||
import 'package:apidash/consts.dart';
|
||||
import 'package:printing/printing.dart';
|
||||
import 'uint8_audio_player.dart';
|
||||
|
||||
|
||||
class Previewer extends StatefulWidget {
|
||||
const Previewer({
|
||||
@ -43,7 +45,14 @@ class _PreviewerState extends State<Previewer> {
|
||||
);
|
||||
}
|
||||
if (widget.type == kTypeAudio) {
|
||||
// TODO: Audio Player
|
||||
return Uint8AudioPlayer(
|
||||
bytes: widget.bytes,
|
||||
type: widget.type!,
|
||||
subtype: widget.subtype!,
|
||||
errorBuilder: (context, error, stacktrace) {
|
||||
return const ErrorMessage(message: kAudioError);
|
||||
},
|
||||
);
|
||||
}
|
||||
if (widget.type == kTypeVideo) {
|
||||
// TODO: Video Player
|
||||
|
@ -339,8 +339,8 @@ class _ResponseBodyState extends State<ResponseBody> {
|
||||
}
|
||||
|
||||
var responseBodyView = getResponseBodyViewOptions(mediaType);
|
||||
var options = responseBodyView.$0;
|
||||
var highlightLanguage = responseBodyView.$1;
|
||||
var options = responseBodyView.$1;
|
||||
var highlightLanguage = responseBodyView.$2;
|
||||
|
||||
if (formattedBody == null) {
|
||||
options = [...options];
|
||||
@ -400,18 +400,10 @@ class _BodySuccessState extends State<BodySuccess> {
|
||||
|
||||
return LayoutBuilder(
|
||||
builder: (BuildContext context, BoxConstraints constraints) {
|
||||
var showLabel = false;
|
||||
switch (widget.options.length) {
|
||||
case 1:
|
||||
showLabel = (constraints.maxWidth < 300) ? false : true;
|
||||
break;
|
||||
case 2:
|
||||
showLabel = (constraints.maxWidth < 400) ? false : true;
|
||||
break;
|
||||
case 3:
|
||||
showLabel = (constraints.maxWidth < 500) ? false : true;
|
||||
break;
|
||||
}
|
||||
var showLabel = showButtonLabelsInBodySuccess(
|
||||
widget.options.length,
|
||||
constraints.maxWidth,
|
||||
);
|
||||
return Padding(
|
||||
padding: kP10,
|
||||
child: Column(
|
||||
|
@ -76,8 +76,8 @@ class EqualSplitView extends StatefulWidget {
|
||||
class _EqualSplitViewState extends State<EqualSplitView> {
|
||||
final MultiSplitViewController _controller = MultiSplitViewController(
|
||||
areas: [
|
||||
Area(minimalSize: 300),
|
||||
Area(minimalSize: 300),
|
||||
Area(minimalSize: kMinRequestEditorDetailsCardPaneSize),
|
||||
Area(minimalSize: kMinRequestEditorDetailsCardPaneSize),
|
||||
],
|
||||
);
|
||||
|
||||
|
240
lib/widgets/uint8_audio_player.dart
Normal file
240
lib/widgets/uint8_audio_player.dart
Normal file
@ -0,0 +1,240 @@
|
||||
import 'dart:typed_data';
|
||||
import 'package:apidash/consts.dart';
|
||||
import 'package:apidash/utils/utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:just_audio/just_audio.dart';
|
||||
|
||||
typedef AudioErrorWidgetBuilder = Widget Function(
|
||||
BuildContext context,
|
||||
Object error,
|
||||
StackTrace? stackTrace,
|
||||
);
|
||||
|
||||
// Uint8List AudioSource for just_audio
|
||||
class Uint8AudioSource extends StreamAudioSource {
|
||||
Uint8AudioSource(this.bytes, {required this.contentType});
|
||||
|
||||
final List<int> bytes;
|
||||
final String contentType;
|
||||
|
||||
@override
|
||||
Future<StreamAudioResponse> request([int? start, int? end]) async {
|
||||
start ??= 0;
|
||||
end ??= bytes.length;
|
||||
return StreamAudioResponse(
|
||||
sourceLength: bytes.length,
|
||||
contentLength: end - start,
|
||||
offset: start,
|
||||
stream: Stream.value(bytes.sublist(start, end)),
|
||||
contentType: contentType,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class Uint8AudioPlayer extends StatefulWidget {
|
||||
/// Creates a widget for playing audio obtained from a [Uint8List].
|
||||
const Uint8AudioPlayer({
|
||||
super.key,
|
||||
required this.bytes,
|
||||
required this.type,
|
||||
required this.subtype,
|
||||
required this.errorBuilder,
|
||||
});
|
||||
|
||||
final Uint8List bytes;
|
||||
final String type;
|
||||
final String subtype;
|
||||
final AudioErrorWidgetBuilder errorBuilder;
|
||||
|
||||
@override
|
||||
State<Uint8AudioPlayer> createState() => _Uint8AudioPlayerState();
|
||||
}
|
||||
|
||||
class _Uint8AudioPlayerState extends State<Uint8AudioPlayer> {
|
||||
final player = AudioPlayer();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
player.setAudioSource(Uint8AudioSource(
|
||||
widget.bytes,
|
||||
contentType: '${widget.type}/${widget.subtype}',
|
||||
));
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
player.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return StreamBuilder<PlayerState>(
|
||||
stream: player.playerStateStream,
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.hasError) {
|
||||
return widget.errorBuilder(
|
||||
context, snapshot.error!, snapshot.stackTrace);
|
||||
} else {
|
||||
final playerState = snapshot.data;
|
||||
final processingState = playerState?.processingState;
|
||||
if (processingState == ProcessingState.ready ||
|
||||
processingState == ProcessingState.completed ||
|
||||
processingState == ProcessingState.buffering) {
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
// Audio Player
|
||||
Padding(
|
||||
padding: kPh20v10,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
// Duration Position Builder (time elapsed)
|
||||
_buildDuration(
|
||||
player.positionStream,
|
||||
maxDuration: player.duration,
|
||||
),
|
||||
|
||||
// Slider to view & change Duration Position
|
||||
_buildPositionBar(
|
||||
player.positionStream,
|
||||
maxDuration: player.duration,
|
||||
onChanged: (value) =>
|
||||
player.seek(Duration(seconds: value.toInt())),
|
||||
),
|
||||
|
||||
// Total Duration
|
||||
Text(
|
||||
audioPosition(player.duration),
|
||||
style: TextStyle(fontFamily: kCodeStyle.fontFamily),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
// Audio Player Controls
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
// Play/Pause Button
|
||||
_buildPlayButton(
|
||||
player.playingStream,
|
||||
play: player.play,
|
||||
pause: player.pause,
|
||||
restart: () => player.seek(Duration.zero),
|
||||
completed: processingState == ProcessingState.completed,
|
||||
),
|
||||
|
||||
// Mute/UnMute button
|
||||
_buildVolumeButton(
|
||||
player.volumeStream,
|
||||
mute: () => player.setVolume(0),
|
||||
unmute: () => player.setVolume(1),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
} else if (processingState == ProcessingState.idle) {
|
||||
// Error in Loading AudioSource
|
||||
return widget.errorBuilder(
|
||||
context,
|
||||
ErrorDescription('${player.audioSource} Loading Error'),
|
||||
snapshot.stackTrace,
|
||||
);
|
||||
} else {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
StreamBuilder<bool> _buildPlayButton(
|
||||
Stream<bool> stream, {
|
||||
VoidCallback? play,
|
||||
VoidCallback? pause,
|
||||
VoidCallback? restart,
|
||||
required bool completed,
|
||||
}) {
|
||||
return StreamBuilder<bool>(
|
||||
stream: stream,
|
||||
builder: (context, snapshot) {
|
||||
final playing = snapshot.data;
|
||||
if (playing != true) {
|
||||
return IconButton(
|
||||
icon: const Icon(Icons.play_arrow),
|
||||
onPressed: play,
|
||||
);
|
||||
} else if (completed) {
|
||||
return IconButton(
|
||||
icon: const Icon(Icons.replay),
|
||||
onPressed: restart,
|
||||
);
|
||||
} else {
|
||||
return IconButton(
|
||||
icon: const Icon(Icons.pause),
|
||||
onPressed: pause,
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
StreamBuilder<Duration> _buildDuration(
|
||||
Stream<Duration> stream, {
|
||||
Duration? maxDuration,
|
||||
}) {
|
||||
return StreamBuilder<Duration>(
|
||||
stream: stream,
|
||||
builder: (context, snapshot) {
|
||||
final position = snapshot.data;
|
||||
return Text(
|
||||
audioPosition(position),
|
||||
style: TextStyle(fontFamily: kCodeStyle.fontFamily),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
StreamBuilder<Duration> _buildPositionBar(
|
||||
Stream<Duration> stream, {
|
||||
Duration? maxDuration,
|
||||
ValueChanged<double>? onChanged,
|
||||
}) {
|
||||
return StreamBuilder<Duration>(
|
||||
stream: stream,
|
||||
builder: (context, snapshot) {
|
||||
return Flexible(
|
||||
child: SliderTheme(
|
||||
data: SliderTheme.of(context).copyWith(
|
||||
trackShape: const RectangularSliderTrackShape(),
|
||||
thumbShape: const RoundSliderThumbShape(enabledThumbRadius: 8.0),
|
||||
overlayShape: const RoundSliderOverlayShape(overlayRadius: 16.0),
|
||||
),
|
||||
child: Slider(
|
||||
value: snapshot.data?.inSeconds.toDouble() ?? 0,
|
||||
max: maxDuration?.inSeconds.toDouble() ?? 0,
|
||||
onChanged: onChanged,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
StreamBuilder<double> _buildVolumeButton(Stream<double> stream,
|
||||
{VoidCallback? mute, VoidCallback? unmute}) {
|
||||
return StreamBuilder<double>(
|
||||
stream: stream,
|
||||
builder: (context, snapshot) {
|
||||
return snapshot.data == 0
|
||||
? IconButton(icon: const Icon(Icons.volume_off), onPressed: unmute)
|
||||
: IconButton(icon: const Icon(Icons.volume_up), onPressed: mute);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
@ -16,3 +16,4 @@ export 'request_widgets.dart';
|
||||
export 'response_widgets.dart';
|
||||
export 'snackbars.dart';
|
||||
export 'markdown.dart';
|
||||
export 'uint8_audio_player.dart';
|
||||
|
570
pubspec.lock
570
pubspec.lock
File diff suppressed because it is too large
Load Diff
47
pubspec.yaml
47
pubspec.yaml
@ -4,43 +4,52 @@ publish_to: 'none'
|
||||
version: 0.2.0+2
|
||||
|
||||
environment:
|
||||
sdk: '>=2.19.2 <3.0.0'
|
||||
sdk: '>=3.0.0 <4.0.0'
|
||||
flutter: '>=3.7.2'
|
||||
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
multi_split_view: ^2.4.0
|
||||
url_launcher: ^6.1.10
|
||||
flutter_riverpod: ^2.1.3
|
||||
url_launcher: ^6.1.12
|
||||
flutter_riverpod: ^2.3.7
|
||||
uuid: ^3.0.7
|
||||
davi: ^3.2.0
|
||||
http: ^0.13.5
|
||||
davi: ^3.4.1
|
||||
http: ^1.1.0
|
||||
http_parser: ^4.0.2
|
||||
collection: ^1.17.0
|
||||
google_fonts: ^4.0.3
|
||||
collection: ^1.17.2
|
||||
google_fonts: ^5.1.0
|
||||
highlighter: ^0.1.1
|
||||
xml: ^6.2.2
|
||||
jinja: ^0.4.2
|
||||
xml: ^6.3.0
|
||||
jinja: ^0.5.0
|
||||
window_size:
|
||||
git:
|
||||
url: https://github.com/google/flutter-desktop-embedding.git
|
||||
path: plugins/window_size
|
||||
hive_flutter: ^1.1.0
|
||||
lottie: ^2.3.2
|
||||
lottie: ^2.6.0
|
||||
mime_dart: ^3.0.0
|
||||
path_provider: ^2.0.14
|
||||
window_manager: ^0.3.2
|
||||
path: ^1.8.2
|
||||
flutter_markdown: ^0.6.14
|
||||
markdown: ^7.1.0
|
||||
printing: ^5.10.4
|
||||
path_provider: ^2.1.0
|
||||
window_manager: ^0.3.5
|
||||
path: ^1.8.3
|
||||
flutter_markdown: ^0.6.17+1
|
||||
markdown: ^7.1.1
|
||||
just_audio: ^0.9.34
|
||||
just_audio_mpv: ^0.1.7
|
||||
just_audio_windows: ^0.2.0
|
||||
freezed_annotation: ^2.4.1
|
||||
json_annotation: ^4.8.1
|
||||
printing: ^5.11.0
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
flutter_lints: ^2.0.0
|
||||
flutter_launcher_icons: ^0.12.0
|
||||
test: ^1.22.0
|
||||
flutter_lints: ^2.0.2
|
||||
flutter_launcher_icons: ^0.13.1
|
||||
test: ^1.24.3
|
||||
build_runner: ^2.4.6
|
||||
freezed: ^2.4.1
|
||||
json_serializable: ^6.7.1
|
||||
|
||||
flutter:
|
||||
uses-material-design: true
|
||||
|
505
test/codegen/dart_http_codegen_test.dart
Normal file
505
test/codegen/dart_http_codegen_test.dart
Normal file
@ -0,0 +1,505 @@
|
||||
import 'package:apidash/codegen/dart/pkg_http.dart';
|
||||
import 'package:test/test.dart';
|
||||
import '../request_models.dart';
|
||||
|
||||
void main() {
|
||||
final dartHttpCodeGen = DartHttpCodeGen();
|
||||
|
||||
group('GET Request', () {
|
||||
test('GET 1', () {
|
||||
const expectedCode = r"""import 'package:http/http.dart' as http;
|
||||
|
||||
void main() async {
|
||||
var uri = Uri.parse('https://api.foss42.com');
|
||||
|
||||
final response = await http.get(uri);
|
||||
|
||||
int statusCode = response.statusCode;
|
||||
if (statusCode >= 200 && statusCode < 300) {
|
||||
print('Status Code: $statusCode');
|
||||
print('Response Body: ${response.body}');
|
||||
}
|
||||
else{
|
||||
print('Error Status Code: $statusCode');
|
||||
print('Error Response Body: ${response.body}');
|
||||
}
|
||||
}
|
||||
""";
|
||||
expect(dartHttpCodeGen.getCode(requestModelGet1, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('GET 2', () {
|
||||
const expectedCode = r"""import 'package:http/http.dart' as http;
|
||||
|
||||
void main() async {
|
||||
var uri = Uri.parse('https://api.foss42.com/country/data');
|
||||
|
||||
var queryParams = {
|
||||
"code": "US"
|
||||
};
|
||||
uri = uri.replace(queryParameters: queryParams);
|
||||
|
||||
final response = await http.get(uri);
|
||||
|
||||
int statusCode = response.statusCode;
|
||||
if (statusCode >= 200 && statusCode < 300) {
|
||||
print('Status Code: $statusCode');
|
||||
print('Response Body: ${response.body}');
|
||||
}
|
||||
else{
|
||||
print('Error Status Code: $statusCode');
|
||||
print('Error Response Body: ${response.body}');
|
||||
}
|
||||
}
|
||||
""";
|
||||
expect(dartHttpCodeGen.getCode(requestModelGet2, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('GET 3', () {
|
||||
const expectedCode = r"""import 'package:http/http.dart' as http;
|
||||
|
||||
void main() async {
|
||||
var uri = Uri.parse('https://api.foss42.com/country/data?code=US');
|
||||
|
||||
var queryParams = {
|
||||
"code": "IND"
|
||||
};
|
||||
var urlQueryParams = Map<String,String>.from(uri.queryParameters);
|
||||
urlQueryParams.addAll(queryParams);
|
||||
uri = uri.replace(queryParameters: urlQueryParams);
|
||||
|
||||
final response = await http.get(uri);
|
||||
|
||||
int statusCode = response.statusCode;
|
||||
if (statusCode >= 200 && statusCode < 300) {
|
||||
print('Status Code: $statusCode');
|
||||
print('Response Body: ${response.body}');
|
||||
}
|
||||
else{
|
||||
print('Error Status Code: $statusCode');
|
||||
print('Error Response Body: ${response.body}');
|
||||
}
|
||||
}
|
||||
""";
|
||||
expect(dartHttpCodeGen.getCode(requestModelGet3, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('GET 4', () {
|
||||
const expectedCode = r"""import 'package:http/http.dart' as http;
|
||||
|
||||
void main() async {
|
||||
var uri = Uri.parse('https://api.foss42.com/humanize/social');
|
||||
|
||||
var queryParams = {
|
||||
"num": "8700000",
|
||||
"digits": "3",
|
||||
"system": "SS",
|
||||
"add_space": "true",
|
||||
"trailing_zeros": "true"
|
||||
};
|
||||
uri = uri.replace(queryParameters: queryParams);
|
||||
|
||||
final response = await http.get(uri);
|
||||
|
||||
int statusCode = response.statusCode;
|
||||
if (statusCode >= 200 && statusCode < 300) {
|
||||
print('Status Code: $statusCode');
|
||||
print('Response Body: ${response.body}');
|
||||
}
|
||||
else{
|
||||
print('Error Status Code: $statusCode');
|
||||
print('Error Response Body: ${response.body}');
|
||||
}
|
||||
}
|
||||
""";
|
||||
expect(dartHttpCodeGen.getCode(requestModelGet4, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('GET 5', () {
|
||||
const expectedCode = r"""import 'package:http/http.dart' as http;
|
||||
|
||||
void main() async {
|
||||
var uri = Uri.parse('https://api.github.com/repos/foss42/apidash');
|
||||
|
||||
var headers = {
|
||||
"Authorization": "Bearer XYZ"
|
||||
};
|
||||
|
||||
final response = await http.get(uri,
|
||||
headers: headers);
|
||||
|
||||
int statusCode = response.statusCode;
|
||||
if (statusCode >= 200 && statusCode < 300) {
|
||||
print('Status Code: $statusCode');
|
||||
print('Response Body: ${response.body}');
|
||||
}
|
||||
else{
|
||||
print('Error Status Code: $statusCode');
|
||||
print('Error Response Body: ${response.body}');
|
||||
}
|
||||
}
|
||||
""";
|
||||
expect(dartHttpCodeGen.getCode(requestModelGet5, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('GET 6', () {
|
||||
const expectedCode = r"""import 'package:http/http.dart' as http;
|
||||
|
||||
void main() async {
|
||||
var uri = Uri.parse('https://api.github.com/repos/foss42/apidash');
|
||||
|
||||
var queryParams = {
|
||||
"raw": "true"
|
||||
};
|
||||
uri = uri.replace(queryParameters: queryParams);
|
||||
|
||||
var headers = {
|
||||
"Authorization": "Bearer XYZ"
|
||||
};
|
||||
|
||||
final response = await http.get(uri,
|
||||
headers: headers);
|
||||
|
||||
int statusCode = response.statusCode;
|
||||
if (statusCode >= 200 && statusCode < 300) {
|
||||
print('Status Code: $statusCode');
|
||||
print('Response Body: ${response.body}');
|
||||
}
|
||||
else{
|
||||
print('Error Status Code: $statusCode');
|
||||
print('Error Response Body: ${response.body}');
|
||||
}
|
||||
}
|
||||
""";
|
||||
expect(dartHttpCodeGen.getCode(requestModelGet6, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('GET 7', () {
|
||||
const expectedCode = r"""import 'package:http/http.dart' as http;
|
||||
|
||||
void main() async {
|
||||
var uri = Uri.parse('https://api.foss42.com');
|
||||
|
||||
final response = await http.get(uri);
|
||||
|
||||
int statusCode = response.statusCode;
|
||||
if (statusCode >= 200 && statusCode < 300) {
|
||||
print('Status Code: $statusCode');
|
||||
print('Response Body: ${response.body}');
|
||||
}
|
||||
else{
|
||||
print('Error Status Code: $statusCode');
|
||||
print('Error Response Body: ${response.body}');
|
||||
}
|
||||
}
|
||||
""";
|
||||
expect(dartHttpCodeGen.getCode(requestModelGet7, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('GET 8', () {
|
||||
const expectedCode = r"""import 'package:http/http.dart' as http;
|
||||
|
||||
void main() async {
|
||||
var uri = Uri.parse('https://api.github.com/repos/foss42/apidash');
|
||||
|
||||
var queryParams = {
|
||||
"raw": "true"
|
||||
};
|
||||
uri = uri.replace(queryParameters: queryParams);
|
||||
|
||||
var headers = {
|
||||
"Authorization": "Bearer XYZ"
|
||||
};
|
||||
|
||||
final response = await http.get(uri,
|
||||
headers: headers);
|
||||
|
||||
int statusCode = response.statusCode;
|
||||
if (statusCode >= 200 && statusCode < 300) {
|
||||
print('Status Code: $statusCode');
|
||||
print('Response Body: ${response.body}');
|
||||
}
|
||||
else{
|
||||
print('Error Status Code: $statusCode');
|
||||
print('Error Response Body: ${response.body}');
|
||||
}
|
||||
}
|
||||
""";
|
||||
expect(dartHttpCodeGen.getCode(requestModelGet8, "https"), expectedCode);
|
||||
});
|
||||
});
|
||||
|
||||
group('HEAD Request', () {
|
||||
test('HEAD 1', () {
|
||||
const expectedCode = r"""import 'package:http/http.dart' as http;
|
||||
|
||||
void main() async {
|
||||
var uri = Uri.parse('https://api.foss42.com');
|
||||
|
||||
final response = await http.head(uri);
|
||||
|
||||
int statusCode = response.statusCode;
|
||||
if (statusCode >= 200 && statusCode < 300) {
|
||||
print('Status Code: $statusCode');
|
||||
print('Response Body: ${response.body}');
|
||||
}
|
||||
else{
|
||||
print('Error Status Code: $statusCode');
|
||||
print('Error Response Body: ${response.body}');
|
||||
}
|
||||
}
|
||||
""";
|
||||
expect(dartHttpCodeGen.getCode(requestModelHead1, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('HEAD 2', () {
|
||||
const expectedCode = r"""import 'package:http/http.dart' as http;
|
||||
|
||||
void main() async {
|
||||
var uri = Uri.parse('http://api.foss42.com');
|
||||
|
||||
final response = await http.head(uri);
|
||||
|
||||
int statusCode = response.statusCode;
|
||||
if (statusCode >= 200 && statusCode < 300) {
|
||||
print('Status Code: $statusCode');
|
||||
print('Response Body: ${response.body}');
|
||||
}
|
||||
else{
|
||||
print('Error Status Code: $statusCode');
|
||||
print('Error Response Body: ${response.body}');
|
||||
}
|
||||
}
|
||||
""";
|
||||
expect(dartHttpCodeGen.getCode(requestModelHead2, "http"), expectedCode);
|
||||
});
|
||||
});
|
||||
|
||||
group('POST Request', () {
|
||||
test('POST 1', () {
|
||||
const expectedCode = r"""import 'package:http/http.dart' as http;
|
||||
|
||||
void main() async {
|
||||
var uri = Uri.parse('https://api.foss42.com/case/lower');
|
||||
|
||||
String body = r'''{
|
||||
"text": "I LOVE Flutter"
|
||||
}''';
|
||||
|
||||
var headers = {
|
||||
"content-type": "text/plain"
|
||||
};
|
||||
|
||||
final response = await http.post(uri,
|
||||
headers: headers,
|
||||
body: body);
|
||||
|
||||
int statusCode = response.statusCode;
|
||||
if (statusCode >= 200 && statusCode < 300) {
|
||||
print('Status Code: $statusCode');
|
||||
print('Response Body: ${response.body}');
|
||||
}
|
||||
else{
|
||||
print('Error Status Code: $statusCode');
|
||||
print('Error Response Body: ${response.body}');
|
||||
}
|
||||
}
|
||||
""";
|
||||
expect(dartHttpCodeGen.getCode(requestModelPost1, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('POST 2', () {
|
||||
const expectedCode = r"""import 'package:http/http.dart' as http;
|
||||
|
||||
void main() async {
|
||||
var uri = Uri.parse('https://api.foss42.com/case/lower');
|
||||
|
||||
String body = r'''{
|
||||
"text": "I LOVE Flutter"
|
||||
}''';
|
||||
|
||||
var headers = {
|
||||
"content-type": "application/json"
|
||||
};
|
||||
|
||||
final response = await http.post(uri,
|
||||
headers: headers,
|
||||
body: body);
|
||||
|
||||
int statusCode = response.statusCode;
|
||||
if (statusCode >= 200 && statusCode < 300) {
|
||||
print('Status Code: $statusCode');
|
||||
print('Response Body: ${response.body}');
|
||||
}
|
||||
else{
|
||||
print('Error Status Code: $statusCode');
|
||||
print('Error Response Body: ${response.body}');
|
||||
}
|
||||
}
|
||||
""";
|
||||
expect(dartHttpCodeGen.getCode(requestModelPost2, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('POST 3', () {
|
||||
const expectedCode = r"""import 'package:http/http.dart' as http;
|
||||
|
||||
void main() async {
|
||||
var uri = Uri.parse('https://api.foss42.com/case/lower');
|
||||
|
||||
String body = r'''{
|
||||
"text": "I LOVE Flutter"
|
||||
}''';
|
||||
|
||||
var headers = {
|
||||
"Authorization": "Bearer XYZ",
|
||||
"content-type": "application/json"
|
||||
};
|
||||
|
||||
final response = await http.post(uri,
|
||||
headers: headers,
|
||||
body: body);
|
||||
|
||||
int statusCode = response.statusCode;
|
||||
if (statusCode >= 200 && statusCode < 300) {
|
||||
print('Status Code: $statusCode');
|
||||
print('Response Body: ${response.body}');
|
||||
}
|
||||
else{
|
||||
print('Error Status Code: $statusCode');
|
||||
print('Error Response Body: ${response.body}');
|
||||
}
|
||||
}
|
||||
""";
|
||||
expect(dartHttpCodeGen.getCode(requestModelPost3, "https"), expectedCode);
|
||||
});
|
||||
});
|
||||
group('PUT Request', () {
|
||||
test('PUT 1', () {
|
||||
const expectedCode = r"""import 'package:http/http.dart' as http;
|
||||
|
||||
void main() async {
|
||||
var uri = Uri.parse('https://reqres.in/api/users/2');
|
||||
|
||||
String body = r'''{
|
||||
"name": "morpheus",
|
||||
"job": "zion resident"
|
||||
}''';
|
||||
|
||||
var headers = {
|
||||
"content-type": "application/json"
|
||||
};
|
||||
|
||||
final response = await http.put(uri,
|
||||
headers: headers,
|
||||
body: body);
|
||||
|
||||
int statusCode = response.statusCode;
|
||||
if (statusCode >= 200 && statusCode < 300) {
|
||||
print('Status Code: $statusCode');
|
||||
print('Response Body: ${response.body}');
|
||||
}
|
||||
else{
|
||||
print('Error Status Code: $statusCode');
|
||||
print('Error Response Body: ${response.body}');
|
||||
}
|
||||
}
|
||||
""";
|
||||
expect(dartHttpCodeGen.getCode(requestModelPut1, "https"), expectedCode);
|
||||
});
|
||||
});
|
||||
|
||||
group('PATCH Request', () {
|
||||
test('PATCH 1', () {
|
||||
const expectedCode = r"""import 'package:http/http.dart' as http;
|
||||
|
||||
void main() async {
|
||||
var uri = Uri.parse('https://reqres.in/api/users/2');
|
||||
|
||||
String body = r'''{
|
||||
"name": "marfeus",
|
||||
"job": "accountant"
|
||||
}''';
|
||||
|
||||
var headers = {
|
||||
"content-type": "application/json"
|
||||
};
|
||||
|
||||
final response = await http.patch(uri,
|
||||
headers: headers,
|
||||
body: body);
|
||||
|
||||
int statusCode = response.statusCode;
|
||||
if (statusCode >= 200 && statusCode < 300) {
|
||||
print('Status Code: $statusCode');
|
||||
print('Response Body: ${response.body}');
|
||||
}
|
||||
else{
|
||||
print('Error Status Code: $statusCode');
|
||||
print('Error Response Body: ${response.body}');
|
||||
}
|
||||
}
|
||||
""";
|
||||
expect(
|
||||
dartHttpCodeGen.getCode(requestModelPatch1, "https"), expectedCode);
|
||||
});
|
||||
});
|
||||
|
||||
group('DELETE Request', () {
|
||||
test('DELETE 1', () {
|
||||
const expectedCode = r"""import 'package:http/http.dart' as http;
|
||||
|
||||
void main() async {
|
||||
var uri = Uri.parse('https://reqres.in/api/users/2');
|
||||
|
||||
final response = await http.delete(uri);
|
||||
|
||||
int statusCode = response.statusCode;
|
||||
if (statusCode >= 200 && statusCode < 300) {
|
||||
print('Status Code: $statusCode');
|
||||
print('Response Body: ${response.body}');
|
||||
}
|
||||
else{
|
||||
print('Error Status Code: $statusCode');
|
||||
print('Error Response Body: ${response.body}');
|
||||
}
|
||||
}
|
||||
""";
|
||||
expect(
|
||||
dartHttpCodeGen.getCode(requestModelDelete1, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('DELETE 2', () {
|
||||
const expectedCode = r"""import 'package:http/http.dart' as http;
|
||||
|
||||
void main() async {
|
||||
var uri = Uri.parse('https://reqres.in/api/users/2');
|
||||
|
||||
String body = r'''{
|
||||
"name": "marfeus",
|
||||
"job": "accountant"
|
||||
}''';
|
||||
|
||||
var headers = {
|
||||
"content-type": "application/json"
|
||||
};
|
||||
|
||||
final response = await http.delete(uri,
|
||||
headers: headers,
|
||||
body: body);
|
||||
|
||||
int statusCode = response.statusCode;
|
||||
if (statusCode >= 200 && statusCode < 300) {
|
||||
print('Status Code: $statusCode');
|
||||
print('Response Body: ${response.body}');
|
||||
}
|
||||
else{
|
||||
print('Error Status Code: $statusCode');
|
||||
print('Error Response Body: ${response.body}');
|
||||
}
|
||||
}
|
||||
""";
|
||||
expect(
|
||||
dartHttpCodeGen.getCode(requestModelDelete2, "https"), expectedCode);
|
||||
});
|
||||
});
|
||||
}
|
126
test/codegen/kotlin_okhttp_codegen_test.dart
Normal file
126
test/codegen/kotlin_okhttp_codegen_test.dart
Normal file
@ -0,0 +1,126 @@
|
||||
import 'package:apidash/codegen/kotlin/pkg_okhttp.dart';
|
||||
import 'package:test/test.dart';
|
||||
import '../request_models.dart';
|
||||
|
||||
void main() {
|
||||
group('KotlinOkHttpCodeGen', () {
|
||||
final kotlinOkHttpCodeGen = KotlinOkHttpCodeGen();
|
||||
|
||||
test('getCode returns valid code for GET request', () {
|
||||
const expectedCode = r"""import okhttp3.MediaType.Companion.toMediaType
|
||||
import okhttp3.MultipartBody
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.RequestBody.Companion.toRequestBody
|
||||
import okhttp3.RequestBody.Companion.asRequestBody
|
||||
import java.io.File
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
val client = OkHttpClient()
|
||||
val request = Request.Builder()
|
||||
.url("https://api.foss42.com")
|
||||
.build()
|
||||
val response = client.newCall(request).execute()
|
||||
|
||||
println(response.body!!.string())
|
||||
""";
|
||||
expect(kotlinOkHttpCodeGen.getCode(requestModelGet1), expectedCode);
|
||||
});
|
||||
|
||||
test('getCode returns valid code for POST request', () {
|
||||
const expectedCode = r"""import okhttp3.MediaType.Companion.toMediaType
|
||||
import okhttp3.MultipartBody
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.RequestBody.Companion.toRequestBody
|
||||
import okhttp3.RequestBody.Companion.asRequestBody
|
||||
import java.io.File
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
val client = OkHttpClient()
|
||||
val mediaType = "application/json".toMediaType()
|
||||
val body = "{"text": "IS Upper"}".toRequestBody(mediaType)
|
||||
val request = Request.Builder()
|
||||
.url("https://api.foss42.com/case/lower")
|
||||
.post(body)
|
||||
.build()
|
||||
val response = client.newCall(request).execute()
|
||||
|
||||
println(response.body!!.string())
|
||||
""";
|
||||
expect(kotlinOkHttpCodeGen.getCode(requestModelPost1), expectedCode);
|
||||
});
|
||||
|
||||
test('getCode returns valid code for DELETE request', () {
|
||||
const expectedCode = r"""import okhttp3.MediaType.Companion.toMediaType
|
||||
import okhttp3.MultipartBody
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.RequestBody.Companion.toRequestBody
|
||||
import okhttp3.RequestBody.Companion.asRequestBody
|
||||
import java.io.File
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
val client = OkHttpClient()
|
||||
val mediaType = "application/json".toMediaType()
|
||||
val body = "{"title": "foo","body": "bar","userId": 1}".toRequestBody(mediaType)
|
||||
val request = Request.Builder()
|
||||
.url("https://jsonplaceholder.typicode.com/posts/1")
|
||||
.method("DELETE", body)
|
||||
.build()
|
||||
val response = client.newCall(request).execute()
|
||||
|
||||
println(response.body!!.string())
|
||||
""";
|
||||
expect(kotlinOkHttpCodeGen.getCode(requestModelDelete1), expectedCode);
|
||||
});
|
||||
|
||||
test('getCode returns valid code for HEAD request', () {
|
||||
const expectedCode = """import okhttp3.MediaType.Companion.toMediaType
|
||||
import okhttp3.MultipartBody
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.RequestBody.Companion.toRequestBody
|
||||
import okhttp3.RequestBody.Companion.asRequestBody
|
||||
import java.io.File
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
val client = OkHttpClient()
|
||||
val request = Request.Builder()
|
||||
.url("https://jsonplaceholder.typicode.com/posts/1")
|
||||
.head()
|
||||
.build()
|
||||
val response = client.newCall(request).execute()
|
||||
|
||||
println(response.body!!.string())
|
||||
""";
|
||||
expect(kotlinOkHttpCodeGen.getCode(requestModelHead1), expectedCode);
|
||||
});
|
||||
|
||||
test(
|
||||
'getCode returns valid code for requests with headers and query parameters',
|
||||
() {
|
||||
const expectedCode = """import okhttp3.MediaType.Companion.toMediaType
|
||||
import okhttp3.MultipartBody
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.RequestBody.Companion.toRequestBody
|
||||
import okhttp3.RequestBody.Companion.asRequestBody
|
||||
import java.io.File
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
val client = OkHttpClient()
|
||||
val request = Request.Builder()
|
||||
.url("https://jsonplaceholder.typicode.com/posts")
|
||||
.addQueryParameter("userId", "1")
|
||||
.addHeader("Custom-Header-1", "Value-1")
|
||||
.addHeader("Custom-Header-2", "Value-2")
|
||||
.build()
|
||||
val response = client.newCall(request).execute()
|
||||
|
||||
println(response.body!!.string())
|
||||
""";
|
||||
expect(kotlinOkHttpCodeGen.getCode(requestModelGet2), expectedCode);
|
||||
});
|
||||
});
|
||||
}
|
135
test/codegen/python_http_client_codegen_test.dart
Normal file
135
test/codegen/python_http_client_codegen_test.dart
Normal file
@ -0,0 +1,135 @@
|
||||
import 'package:apidash/codegen/python/pkg_http_client.dart';
|
||||
import 'package:apidash/models/models.dart';
|
||||
import 'package:test/test.dart';
|
||||
import 'package:apidash/consts.dart';
|
||||
|
||||
void main() {
|
||||
group('PythonHttpClient', () {
|
||||
final PythonHttpClient pythonHttpClient = PythonHttpClient();
|
||||
|
||||
test('getCode returns valid code for GET request', () {
|
||||
const requestModel = RequestModel(
|
||||
url: 'https://jsonplaceholder.typicode.com/todos/1',
|
||||
method: HTTPVerb.get,
|
||||
id: '',
|
||||
);
|
||||
const expectedCode = """import http.client
|
||||
import json
|
||||
|
||||
conn = http.client.HTTPSConnection('jsonplaceholder.typicode.com')
|
||||
payload = json.dumps()
|
||||
headers = {
|
||||
'Content-Type':'application/json'
|
||||
},
|
||||
conn.request("GET", "/todos/1", payload, headers)
|
||||
res = conn.getresponse()
|
||||
data = res.read()
|
||||
print(data.decode("utf-8"))
|
||||
""";
|
||||
expect(pythonHttpClient.getCode(requestModel), expectedCode);
|
||||
});
|
||||
|
||||
test('getCode returns valid code for POST request', () {
|
||||
const requestModel = RequestModel(
|
||||
url: 'https://jsonplaceholder.typicode.com/todos',
|
||||
method: HTTPVerb.post,
|
||||
requestBody: '{"title": "foo","body": "bar","userId": 1}',
|
||||
requestBodyContentType: ContentType.json,
|
||||
id: '1',
|
||||
);
|
||||
const expectedCode = """import http.client
|
||||
import json
|
||||
|
||||
conn = http.client.HTTPSConnection('jsonplaceholder.typicode.com')
|
||||
payload = json.dumps({"title": "foo","body": "bar","userId": 1})
|
||||
headers = {
|
||||
'Content-Type':'application/json'
|
||||
},
|
||||
conn.request("POST", "/todos", payload, headers)
|
||||
res = conn.getresponse()
|
||||
data = res.read()
|
||||
print(data.decode("utf-8"))
|
||||
""";
|
||||
expect(pythonHttpClient.getCode(requestModel), expectedCode);
|
||||
});
|
||||
|
||||
test('getCode returns valid code for DELETE request', () {
|
||||
const requestModel = RequestModel(
|
||||
url: 'https://jsonplaceholder.typicode.com/todos/1',
|
||||
method: HTTPVerb.delete,
|
||||
requestBody: '{"title": "foo","body": "bar","userId": 1}',
|
||||
requestBodyContentType: ContentType.json,
|
||||
id: '1',
|
||||
);
|
||||
const expectedCode = """import http.client
|
||||
import json
|
||||
|
||||
conn = http.client.HTTPSConnection('jsonplaceholder.typicode.com')
|
||||
payload = json.dumps({"title": "foo","body": "bar","userId": 1})
|
||||
headers = {
|
||||
'Content-Type':'application/json'
|
||||
},
|
||||
conn.request("DELETE", "/todos/1", payload, headers)
|
||||
res = conn.getresponse()
|
||||
data = res.read()
|
||||
print(data.decode("utf-8"))
|
||||
""";
|
||||
expect(pythonHttpClient.getCode(requestModel), expectedCode);
|
||||
});
|
||||
|
||||
test('getCode returns valid code for HEAD request', () {
|
||||
const requestModel = RequestModel(
|
||||
url: 'https://jsonplaceholder.typicode.com/todos/1',
|
||||
method: HTTPVerb.head,
|
||||
id: '1',
|
||||
);
|
||||
const expectedCode = """import http.client
|
||||
import json
|
||||
|
||||
conn = http.client.HTTPSConnection('jsonplaceholder.typicode.com')
|
||||
payload = json.dumps()
|
||||
headers = {
|
||||
'Content-Type':'application/json'
|
||||
},
|
||||
conn.request("HEAD", "/todos/1", payload, headers)
|
||||
res = conn.getresponse()
|
||||
data = res.read()
|
||||
print(data.decode("utf-8"))
|
||||
""";
|
||||
expect(pythonHttpClient.getCode(requestModel), expectedCode);
|
||||
});
|
||||
|
||||
test(
|
||||
'getCode returns valid code for requests with headers and query parameters',
|
||||
() {
|
||||
const requestModel = RequestModel(
|
||||
url: 'https://jsonplaceholder.typicode.com/posts',
|
||||
method: HTTPVerb.get,
|
||||
requestParams: [
|
||||
NameValueModel(name: 'userId', value: 1),
|
||||
],
|
||||
requestHeaders: [
|
||||
NameValueModel(name: 'Custom-Header-1', value: 'Value-1'),
|
||||
NameValueModel(name: 'Custom-Header-2', value: 'Value-2')
|
||||
],
|
||||
id: '1',
|
||||
);
|
||||
const expectedCode = """import http.client
|
||||
import json
|
||||
|
||||
conn = http.client.HTTPSConnection('jsonplaceholder.typicode.com')
|
||||
payload = json.dumps()
|
||||
headers = {
|
||||
'Content-Type':'application/json'
|
||||
'Custom-Header-1':'Value-1',
|
||||
'Custom-Header-2':'Value-2',
|
||||
},
|
||||
conn.request("GET", "/posts?userId=1", payload, headers)
|
||||
res = conn.getresponse()
|
||||
data = res.read()
|
||||
print(data.decode("utf-8"))
|
||||
""";
|
||||
expect(pythonHttpClient.getCode(requestModel), expectedCode);
|
||||
});
|
||||
});
|
||||
}
|
179
test/codegen/python_request_codegen_test.dart
Normal file
179
test/codegen/python_request_codegen_test.dart
Normal file
@ -0,0 +1,179 @@
|
||||
import 'package:apidash/codegen/python/pkg_request.dart';
|
||||
import 'package:apidash/models/models.dart';
|
||||
import 'package:test/test.dart';
|
||||
import 'package:apidash/consts.dart';
|
||||
|
||||
void main() {
|
||||
group('PythonRequestCodeGen', () {
|
||||
final pythonRequestCodeGen = PythonRequestCodeGen();
|
||||
|
||||
test('getCode returns valid code for GET request', () {
|
||||
const requestModel = RequestModel(
|
||||
url: 'https://jsonplaceholder.typicode.com/todos/1',
|
||||
method: HTTPVerb.get,
|
||||
id: '',
|
||||
);
|
||||
const expectedCode = """import requests
|
||||
|
||||
def main():
|
||||
url = 'https://jsonplaceholder.typicode.com/todos/1'
|
||||
|
||||
response = requests.get(
|
||||
url
|
||||
)
|
||||
|
||||
status_code = response.status_code
|
||||
if 200 <= status_code < 300:
|
||||
print('Status Code:', status_code)
|
||||
print('Response Body:', response.text)
|
||||
else:
|
||||
print('Error Status Code:', status_code)
|
||||
print('Error Response Body:', response.reason)
|
||||
|
||||
main()""";
|
||||
expect(pythonRequestCodeGen.getCode(requestModel, 'https'), expectedCode);
|
||||
});
|
||||
|
||||
test('getCode returns valid code for POST request', () {
|
||||
const requestModel = RequestModel(
|
||||
url: 'https://jsonplaceholder.typicode.com/posts',
|
||||
method: HTTPVerb.post,
|
||||
requestBody: '{"title": "foo","body": "bar","userId": 1}',
|
||||
requestBodyContentType: ContentType.json,
|
||||
id: '1',
|
||||
);
|
||||
const expectedCode = """import requests
|
||||
|
||||
def main():
|
||||
url = 'https://jsonplaceholder.typicode.com/posts'
|
||||
|
||||
data = '''{"title": "foo","body": "bar","userId": 1}'''
|
||||
|
||||
headers = {
|
||||
"content-type": "application/json"
|
||||
}
|
||||
|
||||
response = requests.post(
|
||||
url, headers=headers, data=data
|
||||
)
|
||||
|
||||
status_code = response.status_code
|
||||
if 200 <= status_code < 300:
|
||||
print('Status Code:', status_code)
|
||||
print('Response Body:', response.text)
|
||||
else:
|
||||
print('Error Status Code:', status_code)
|
||||
print('Error Response Body:', response.reason)
|
||||
|
||||
main()""";
|
||||
expect(pythonRequestCodeGen.getCode(requestModel, 'https'), expectedCode);
|
||||
});
|
||||
|
||||
test('getCode returns valid code for DELETE request', () {
|
||||
const requestModel = RequestModel(
|
||||
url: 'https://jsonplaceholder.typicode.com/posts/1',
|
||||
method: HTTPVerb.delete,
|
||||
requestBody: '{"title": "foo","body": "bar","userId": 1}',
|
||||
requestBodyContentType: ContentType.json,
|
||||
id: '1',
|
||||
);
|
||||
const expectedCode = """import requests
|
||||
|
||||
def main():
|
||||
url = 'https://jsonplaceholder.typicode.com/posts/1'
|
||||
|
||||
data = '''{"title": "foo","body": "bar","userId": 1}'''
|
||||
|
||||
headers = {
|
||||
"content-type": "application/json"
|
||||
}
|
||||
|
||||
response = requests.delete(
|
||||
url, headers=headers, data=data
|
||||
)
|
||||
|
||||
status_code = response.status_code
|
||||
if 200 <= status_code < 300:
|
||||
print('Status Code:', status_code)
|
||||
print('Response Body:', response.text)
|
||||
else:
|
||||
print('Error Status Code:', status_code)
|
||||
print('Error Response Body:', response.reason)
|
||||
|
||||
main()""";
|
||||
expect(pythonRequestCodeGen.getCode(requestModel, 'https'), expectedCode);
|
||||
});
|
||||
|
||||
test('getCode returns valid code for HEAD request', () {
|
||||
const requestModel = RequestModel(
|
||||
url: 'https://jsonplaceholder.typicode.com/posts/1',
|
||||
method: HTTPVerb.head,
|
||||
id: '1',
|
||||
);
|
||||
const expectedCode = """import requests
|
||||
|
||||
def main():
|
||||
url = 'https://jsonplaceholder.typicode.com/posts/1'
|
||||
|
||||
response = requests.head(
|
||||
url
|
||||
)
|
||||
|
||||
status_code = response.status_code
|
||||
if 200 <= status_code < 300:
|
||||
print('Status Code:', status_code)
|
||||
print('Response Body:', response.text)
|
||||
else:
|
||||
print('Error Status Code:', status_code)
|
||||
print('Error Response Body:', response.reason)
|
||||
|
||||
main()""";
|
||||
expect(pythonRequestCodeGen.getCode(requestModel, 'https'), expectedCode);
|
||||
});
|
||||
|
||||
test(
|
||||
'getCode returns valid code for requests with headers and query parameters',
|
||||
() {
|
||||
const requestModel = RequestModel(
|
||||
url: 'https://jsonplaceholder.typicode.com/posts',
|
||||
method: HTTPVerb.get,
|
||||
requestParams: [
|
||||
NameValueModel(name: 'userId', value: 1),
|
||||
],
|
||||
requestHeaders: [
|
||||
NameValueModel(name: 'Custom-Header-1', value: 'Value-1'),
|
||||
NameValueModel(name: 'Custom-Header-2', value: 'Value-2')
|
||||
],
|
||||
id: '1',
|
||||
);
|
||||
const expectedCode = """import requests
|
||||
|
||||
def main():
|
||||
url = 'https://jsonplaceholder.typicode.com/posts'
|
||||
|
||||
params = {
|
||||
"userId": "1",
|
||||
}
|
||||
|
||||
headers = {
|
||||
"Custom-Header-1": "Value-1",
|
||||
"Custom-Header-2": "Value-2"
|
||||
}
|
||||
|
||||
response = requests.get(
|
||||
url, params=params, headers=headers
|
||||
)
|
||||
|
||||
status_code = response.status_code
|
||||
if 200 <= status_code < 300:
|
||||
print('Status Code:', status_code)
|
||||
print('Response Body:', response.text)
|
||||
else:
|
||||
print('Error Status Code:', status_code)
|
||||
print('Error Response Body:', response.reason)
|
||||
|
||||
main()""";
|
||||
expect(pythonRequestCodeGen.getCode(requestModel, 'https'), expectedCode);
|
||||
});
|
||||
});
|
||||
}
|
@ -1,17 +1,17 @@
|
||||
import 'package:test/test.dart';
|
||||
import 'package:apidash/models/kvrow_model.dart';
|
||||
import 'package:apidash/models/name_value_model.dart';
|
||||
|
||||
void main() {
|
||||
KVRow kvRow1 = const KVRow("harry", 23);
|
||||
const kvRow1 = NameValueModel(name: "harry", value: 23);
|
||||
String kvRow1Expected = "{harry: 23}";
|
||||
|
||||
test('Testing toString()', () {
|
||||
expect(kvRow1.toString(), kvRow1Expected);
|
||||
});
|
||||
|
||||
KVRow kvRow2Expected = const KVRow("winter", "26");
|
||||
const kvRow2Expected = NameValueModel(name: "winter", value: "26");
|
||||
test('Testing copyWith()', () {
|
||||
expect(kvRow1.copyWith(k: "winter", v: "26"), kvRow2Expected);
|
||||
expect(kvRow1.copyWith(name: "winter", value: "26"), kvRow2Expected);
|
||||
});
|
||||
|
||||
test('Testing hashcode', () {
|
||||
|
@ -55,8 +55,9 @@ void main() {
|
||||
url: 'api.foss42.com/case/lower',
|
||||
name: 'foss42 api',
|
||||
requestHeaders: const [
|
||||
KVRow('content-length', '18'),
|
||||
KVRow('content-type', 'application/json; charset=utf-8')
|
||||
NameValueModel(name: 'content-length', value: '18'),
|
||||
NameValueModel(
|
||||
name: 'content-type', value: 'application/json; charset=utf-8')
|
||||
],
|
||||
requestBodyContentType: ContentType.json,
|
||||
requestBody: '''{
|
||||
@ -71,8 +72,9 @@ void main() {
|
||||
url: 'api.foss42.com/case/lower',
|
||||
name: 'foss42 api',
|
||||
requestHeaders: [
|
||||
KVRow('content-length', '18'),
|
||||
KVRow('content-type', 'application/json; charset=utf-8')
|
||||
NameValueModel(name: 'content-length', value: '18'),
|
||||
NameValueModel(
|
||||
name: 'content-type', value: 'application/json; charset=utf-8')
|
||||
],
|
||||
requestBodyContentType: ContentType.json,
|
||||
requestBody: '''{
|
||||
|
182
test/request_models.dart
Normal file
182
test/request_models.dart
Normal file
@ -0,0 +1,182 @@
|
||||
import 'package:apidash/models/models.dart' show NameValueModel, RequestModel;
|
||||
import 'package:apidash/consts.dart';
|
||||
|
||||
/// Basic GET request model
|
||||
const requestModelGet1 = RequestModel(
|
||||
id: 'get1',
|
||||
url: 'https://api.foss42.com',
|
||||
method: HTTPVerb.get,
|
||||
);
|
||||
|
||||
/// GET request model with query params
|
||||
const requestModelGet2 = RequestModel(
|
||||
id: 'get2',
|
||||
url: 'https://api.foss42.com/country/data',
|
||||
method: HTTPVerb.get,
|
||||
requestParams: [
|
||||
NameValueModel(name: 'code', value: 'US'),
|
||||
],
|
||||
);
|
||||
|
||||
/// GET request model with override query params
|
||||
const requestModelGet3 = RequestModel(
|
||||
id: 'get3',
|
||||
url: 'https://api.foss42.com/country/data?code=US',
|
||||
method: HTTPVerb.get,
|
||||
requestParams: [
|
||||
NameValueModel(name: 'code', value: 'IND'),
|
||||
],
|
||||
);
|
||||
|
||||
/// GET request model with different types of query params
|
||||
const requestModelGet4 = RequestModel(
|
||||
id: 'get4',
|
||||
url: 'https://api.foss42.com/humanize/social',
|
||||
method: HTTPVerb.get,
|
||||
requestParams: [
|
||||
NameValueModel(name: 'num', value: '8700000'),
|
||||
NameValueModel(name: 'digits', value: '3'),
|
||||
NameValueModel(name: 'system', value: 'SS'),
|
||||
NameValueModel(name: 'add_space', value: 'true'),
|
||||
NameValueModel(name: 'trailing_zeros', value: 'true'),
|
||||
],
|
||||
);
|
||||
|
||||
/// GET request model with headers
|
||||
const requestModelGet5 = RequestModel(
|
||||
id: 'get5',
|
||||
url: 'https://api.github.com/repos/foss42/apidash',
|
||||
method: HTTPVerb.get,
|
||||
requestHeaders: [
|
||||
NameValueModel(name: 'Authorization', value: 'Bearer XYZ'),
|
||||
],
|
||||
);
|
||||
|
||||
/// GET request model with headers & query params
|
||||
const requestModelGet6 = RequestModel(
|
||||
id: 'get6',
|
||||
url: 'https://api.github.com/repos/foss42/apidash',
|
||||
method: HTTPVerb.get,
|
||||
requestHeaders: [
|
||||
NameValueModel(name: 'Authorization', value: 'Bearer XYZ'),
|
||||
],
|
||||
requestParams: [
|
||||
NameValueModel(name: 'raw', value: 'true'),
|
||||
],
|
||||
);
|
||||
|
||||
/// GET request model with body
|
||||
const requestModelGet7 = RequestModel(
|
||||
id: 'get7',
|
||||
url: 'https://api.foss42.com',
|
||||
method: HTTPVerb.get,
|
||||
requestBodyContentType: ContentType.text,
|
||||
requestBody:
|
||||
'This is a random text which should not be attached with a GET request',
|
||||
);
|
||||
|
||||
/// GET request model with empty header & query param name
|
||||
const requestModelGet8 = RequestModel(
|
||||
id: 'get8',
|
||||
url: 'https://api.github.com/repos/foss42/apidash',
|
||||
method: HTTPVerb.get,
|
||||
requestHeaders: [
|
||||
NameValueModel(name: 'Authorization', value: 'Bearer XYZ'),
|
||||
NameValueModel(name: '', value: 'Bearer XYZ'),
|
||||
],
|
||||
requestParams: [
|
||||
NameValueModel(name: 'raw', value: 'true'),
|
||||
NameValueModel(name: '', value: 'true'),
|
||||
],
|
||||
);
|
||||
|
||||
/// Basic HEAD request model
|
||||
const requestModelHead1 = RequestModel(
|
||||
id: 'head1',
|
||||
url: 'https://api.foss42.com',
|
||||
method: HTTPVerb.head,
|
||||
);
|
||||
|
||||
/// Without URI Scheme (pass default as http)
|
||||
const requestModelHead2 = RequestModel(
|
||||
id: 'head2',
|
||||
url: 'api.foss42.com',
|
||||
method: HTTPVerb.head,
|
||||
);
|
||||
|
||||
/// Basic POST request model (txt body)
|
||||
const requestModelPost1 = RequestModel(
|
||||
id: 'post1',
|
||||
url: 'https://api.foss42.com/case/lower',
|
||||
method: HTTPVerb.post,
|
||||
requestBody: r"""{
|
||||
"text": "I LOVE Flutter"
|
||||
}""",
|
||||
requestBodyContentType: ContentType.text);
|
||||
|
||||
/// POST request model with JSON body
|
||||
const requestModelPost2 = RequestModel(
|
||||
id: 'post2',
|
||||
url: 'https://api.foss42.com/case/lower',
|
||||
method: HTTPVerb.post,
|
||||
requestBody: r"""{
|
||||
"text": "I LOVE Flutter"
|
||||
}""",
|
||||
);
|
||||
|
||||
/// POST request model with headers
|
||||
const requestModelPost3 = RequestModel(
|
||||
id: 'post3',
|
||||
url: 'https://api.foss42.com/case/lower',
|
||||
method: HTTPVerb.post,
|
||||
requestBody: r"""{
|
||||
"text": "I LOVE Flutter"
|
||||
}""",
|
||||
requestBodyContentType: ContentType.json,
|
||||
requestHeaders: [
|
||||
NameValueModel(name: 'Authorization', value: 'Bearer XYZ'),
|
||||
],
|
||||
);
|
||||
|
||||
/// PUT request model
|
||||
const requestModelPut1 = RequestModel(
|
||||
id: 'put1',
|
||||
url: 'https://reqres.in/api/users/2',
|
||||
method: HTTPVerb.put,
|
||||
requestBody: r"""{
|
||||
"name": "morpheus",
|
||||
"job": "zion resident"
|
||||
}""",
|
||||
requestBodyContentType: ContentType.json,
|
||||
);
|
||||
|
||||
/// PATCH request model
|
||||
const requestModelPatch1 = RequestModel(
|
||||
id: 'patch1',
|
||||
url: 'https://reqres.in/api/users/2',
|
||||
method: HTTPVerb.patch,
|
||||
requestBody: r"""{
|
||||
"name": "marfeus",
|
||||
"job": "accountant"
|
||||
}""",
|
||||
requestBodyContentType: ContentType.json,
|
||||
);
|
||||
|
||||
/// Basic DELETE request model
|
||||
const requestModelDelete1 = RequestModel(
|
||||
id: 'delete1',
|
||||
url: 'https://reqres.in/api/users/2',
|
||||
method: HTTPVerb.delete,
|
||||
);
|
||||
|
||||
/// Basic DELETE with body
|
||||
const requestModelDelete2 = RequestModel(
|
||||
id: 'delete1',
|
||||
url: 'https://reqres.in/api/users/2',
|
||||
method: HTTPVerb.delete,
|
||||
requestBody: r"""{
|
||||
"name": "marfeus",
|
||||
"job": "accountant"
|
||||
}""",
|
||||
requestBodyContentType: ContentType.json,
|
||||
);
|
@ -1,15 +1,19 @@
|
||||
import 'dart:math';
|
||||
|
||||
const _chars = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz1234567890';
|
||||
Random _rnd = Random();
|
||||
class RandomStringGenerator {
|
||||
static const _chars =
|
||||
'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz1234567890';
|
||||
static Random rnd = Random();
|
||||
|
||||
String getRandomString(int length) => String.fromCharCodes(Iterable.generate(
|
||||
length, (_) => _chars.codeUnitAt(_rnd.nextInt(_chars.length))));
|
||||
static String getRandomString(int length) =>
|
||||
String.fromCharCodes(Iterable.generate(
|
||||
length, (_) => _chars.codeUnitAt(rnd.nextInt(_chars.length))));
|
||||
|
||||
String getRandomStringLines(int lines, int length) {
|
||||
List<String> result = [];
|
||||
for (var i = 0; i < lines; i++) {
|
||||
result.add(getRandomString(length));
|
||||
static String getRandomStringLines(int lines, int length) {
|
||||
List<String> result = [];
|
||||
for (var i = 0; i < lines; i++) {
|
||||
result.add(getRandomString(length));
|
||||
}
|
||||
return result.join('\n');
|
||||
}
|
||||
return result.join('\n');
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import 'package:test/test.dart';
|
||||
import 'package:apidash/utils/convert_utils.dart';
|
||||
import 'package:apidash/models/kvrow_model.dart';
|
||||
import 'package:apidash/models/name_value_model.dart';
|
||||
|
||||
void main() {
|
||||
group("Testing humanizeDuration function", () {
|
||||
@ -73,18 +73,18 @@ void main() {
|
||||
expect(rowsToMap(null), null);
|
||||
});
|
||||
test('Testing for string KVRow values', () {
|
||||
KVRow kvRow1 = const KVRow("code", "IN");
|
||||
const kvRow1 = NameValueModel(name: "code", value: "IN");
|
||||
expect(rowsToMap([kvRow1]), {"code": "IN"});
|
||||
});
|
||||
test('Testing when header is True', () {
|
||||
KVRow kvRow2 = const KVRow("Text", "ABC");
|
||||
const kvRow2 = NameValueModel(name: "Text", value: "ABC");
|
||||
expect(rowsToMap([kvRow2], isHeader: true), {"text": "ABC"});
|
||||
});
|
||||
test('Testing when header is false and key is in upper case', () {
|
||||
List<KVRow> kvRow3 = const [
|
||||
KVRow("TEXT", "ABC"),
|
||||
KVRow("version", 0.1),
|
||||
KVRow("month", 4)
|
||||
const kvRow3 = <NameValueModel>[
|
||||
NameValueModel(name: "TEXT", value: "ABC"),
|
||||
NameValueModel(name: "version", value: 0.1),
|
||||
NameValueModel(name: "month", value: 4),
|
||||
];
|
||||
expect(
|
||||
rowsToMap(kvRow3), {"TEXT": "ABC", "version": "0.1", "month": "4"});
|
||||
@ -97,10 +97,10 @@ void main() {
|
||||
});
|
||||
test('Testing with a map value', () {
|
||||
Map<String, String> value1 = {"text": "abc", "lang": "eng", "code": "1"};
|
||||
List<KVRow> result1Expected = const [
|
||||
KVRow("text", "abc"),
|
||||
KVRow("lang", "eng"),
|
||||
KVRow("code", "1")
|
||||
const result1Expected = <NameValueModel>[
|
||||
NameValueModel(name: "text", value: "abc"),
|
||||
NameValueModel(name: "lang", value: "eng"),
|
||||
NameValueModel(name: "code", value: "1")
|
||||
];
|
||||
expect(mapToRows(value1), result1Expected);
|
||||
});
|
||||
|
17
test/utils/file_utils_test.dart
Normal file
17
test/utils/file_utils_test.dart
Normal file
@ -0,0 +1,17 @@
|
||||
import 'package:test/test.dart';
|
||||
import 'package:apidash/utils/file_utils.dart';
|
||||
|
||||
void main() {
|
||||
group(
|
||||
"Testing x function",
|
||||
() {
|
||||
/*test('Test case 2', () {
|
||||
expect(showButtonLabelsInViewCodePane(350), false);
|
||||
});
|
||||
|
||||
test('Test case 3', () {
|
||||
expect(showButtonLabelsInViewCodePane(450), true);
|
||||
});*/
|
||||
},
|
||||
);
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
import 'package:test/test.dart';
|
||||
import 'package:http_parser/http_parser.dart';
|
||||
import 'package:apidash/utils/http_utils.dart';
|
||||
import 'package:apidash/models/kvrow_model.dart';
|
||||
import 'package:apidash/models/name_value_model.dart';
|
||||
import 'package:apidash/consts.dart';
|
||||
import '../test_utilities.dart';
|
||||
|
||||
@ -156,74 +156,74 @@ void main() {
|
||||
path: 'guides/libraries/library-tour',
|
||||
fragment: 'numbers');
|
||||
String uriScheme1Expected = 'https';
|
||||
expect(getUriScheme(uri1), (uriScheme1Expected,true));
|
||||
expect(getUriScheme(uri1), (uriScheme1Expected, true));
|
||||
});
|
||||
test('Testing getUriScheme for mailto scheme value', () {
|
||||
Uri uri2 = Uri(scheme: 'mailto');
|
||||
String uriScheme2Expected = 'mailto';
|
||||
expect(getUriScheme(uri2), (uriScheme2Expected,false));
|
||||
expect(getUriScheme(uri2), (uriScheme2Expected, false));
|
||||
});
|
||||
test('Testing getUriScheme for empty scheme value', () {
|
||||
Uri uri3 = Uri(
|
||||
scheme: '');
|
||||
expect(getUriScheme(uri3), (null,false));
|
||||
Uri uri3 = Uri(scheme: '');
|
||||
expect(getUriScheme(uri3), (null, false));
|
||||
});
|
||||
test('Testing getUriScheme for null scheme value', () {
|
||||
Uri uri4 = Uri(
|
||||
scheme: null);
|
||||
expect(getUriScheme(uri4), (null,false));
|
||||
Uri uri4 = Uri(scheme: null);
|
||||
expect(getUriScheme(uri4), (null, false));
|
||||
});
|
||||
});
|
||||
|
||||
group("Testing getValidRequestUri", () {
|
||||
test('Testing getValidRequestUri for normal values', () {
|
||||
String url1 = "https://api.foss42.com/country/data";
|
||||
KVRow kvRow1 = const KVRow("code", "US");
|
||||
const kvRow1 = NameValueModel(name: "code", value: "US");
|
||||
Uri uri1Expected = Uri(
|
||||
scheme: 'https',
|
||||
host: 'api.foss42.com',
|
||||
path: 'country/data',
|
||||
queryParameters: {'code':'US'});
|
||||
queryParameters: {'code': 'US'});
|
||||
expect(getValidRequestUri(url1, [kvRow1]), (uri1Expected, null));
|
||||
});
|
||||
test('Testing getValidRequestUri for null url value', () {
|
||||
KVRow kvRow2 = const KVRow("code", "US");
|
||||
const kvRow2 = NameValueModel(name: "code", value: "US");
|
||||
expect(getValidRequestUri(null, [kvRow2]), (null, "URL is missing!"));
|
||||
});
|
||||
test('Testing getValidRequestUri for empty url value', () {
|
||||
KVRow kvRow3 = const KVRow("", "");
|
||||
const kvRow3 = NameValueModel(name: "", value: "");
|
||||
expect(getValidRequestUri("", [kvRow3]), (null, "URL is missing!"));
|
||||
});
|
||||
test('Testing getValidRequestUri when https is not provided in url', () {
|
||||
String url4 = "api.foss42.com/country/data";
|
||||
KVRow kvRow4 = const KVRow("code", "US");
|
||||
const kvRow4 = NameValueModel(name: "code", value: "US");
|
||||
Uri uri4Expected = Uri(
|
||||
scheme: 'https',
|
||||
host: 'api.foss42.com',
|
||||
path: 'country/data',
|
||||
queryParameters: {'code':'US'});
|
||||
queryParameters: {'code': 'US'});
|
||||
expect(getValidRequestUri(url4, [kvRow4]), (uri4Expected, null));
|
||||
});
|
||||
test('Testing getValidRequestUri when url has fragment', () {
|
||||
String url5 = "https://dart.dev/guides/libraries/library-tour#numbers";
|
||||
Uri uri5Expected = Uri(
|
||||
scheme: 'https',
|
||||
host: 'dart.dev',
|
||||
path: '/guides/libraries/library-tour');
|
||||
scheme: 'https',
|
||||
host: 'dart.dev',
|
||||
path: '/guides/libraries/library-tour');
|
||||
expect(getValidRequestUri(url5, null), (uri5Expected, null));
|
||||
});
|
||||
test('Testing getValidRequestUri when uri scheme is not supported', () {
|
||||
String url5 = "mailto:someone@example.com";
|
||||
expect(getValidRequestUri(url5, null), (null, "Unsupported URL Scheme (mailto)"));
|
||||
expect(getValidRequestUri(url5, null),
|
||||
(null, "Unsupported URL Scheme (mailto)"));
|
||||
});
|
||||
test('Testing getValidRequestUri when query params in both url and kvrow', () {
|
||||
test('Testing getValidRequestUri when query params in both url and kvrow',
|
||||
() {
|
||||
String url6 = "api.foss42.com/country/data?code=IND";
|
||||
KVRow kvRow6 = const KVRow("code", "US");
|
||||
const kvRow6 = NameValueModel(name: "code", value: "US");
|
||||
Uri uri6Expected = Uri(
|
||||
scheme: 'https',
|
||||
host: 'api.foss42.com',
|
||||
path: 'country/data',
|
||||
queryParameters: {'code':'US'});
|
||||
queryParameters: {'code': 'US'});
|
||||
expect(getValidRequestUri(url6, [kvRow6]), (uri6Expected, null));
|
||||
});
|
||||
test('Testing getValidRequestUri when kvrow is null', () {
|
||||
@ -232,7 +232,7 @@ void main() {
|
||||
scheme: 'https',
|
||||
host: 'api.foss42.com',
|
||||
path: 'country/data',
|
||||
queryParameters: {'code':'US'});
|
||||
queryParameters: {'code': 'US'});
|
||||
expect(getValidRequestUri(url7, null), (uri7Expected, null));
|
||||
});
|
||||
});
|
||||
@ -241,72 +241,78 @@ void main() {
|
||||
test('Testing getResponseBodyViewOptions for application/json', () {
|
||||
MediaType mediaType1 = MediaType("application", "json");
|
||||
var result1 = getResponseBodyViewOptions(mediaType1);
|
||||
expect(result1.$0,kCodeRawBodyViewOptions);
|
||||
expect(result1.$1, "json");
|
||||
expect(result1.$1, kCodeRawBodyViewOptions);
|
||||
expect(result1.$2, "json");
|
||||
});
|
||||
test('Testing getResponseBodyViewOptions for application/xml', () {
|
||||
MediaType mediaType2 = MediaType("application", "xml");
|
||||
var result2 = getResponseBodyViewOptions(mediaType2);
|
||||
expect(result2.$0, kCodeRawBodyViewOptions);
|
||||
expect(result2.$1,"xml");
|
||||
expect(result2.$1, kCodeRawBodyViewOptions);
|
||||
expect(result2.$2, "xml");
|
||||
});
|
||||
test('Testing getResponseBodyViewOptions for message/news a format currently not supported', () {
|
||||
test(
|
||||
'Testing getResponseBodyViewOptions for message/news a format currently not supported',
|
||||
() {
|
||||
MediaType mediaType3 = MediaType("message", "news");
|
||||
var result3 = getResponseBodyViewOptions(mediaType3);
|
||||
expect(result3.$0,kNoBodyViewOptions);
|
||||
expect(result3.$1,null);
|
||||
expect(result3.$1, kNoBodyViewOptions);
|
||||
expect(result3.$2, null);
|
||||
});
|
||||
test('Testing getResponseBodyViewOptions for application/calendar+json', () {
|
||||
test('Testing getResponseBodyViewOptions for application/calendar+json',
|
||||
() {
|
||||
MediaType mediaType4 = MediaType("application", "calendar+json");
|
||||
var result4 = getResponseBodyViewOptions(mediaType4);
|
||||
expect(result4.$0,kCodeRawBodyViewOptions);
|
||||
expect(result4.$1, "json");
|
||||
expect(result4.$1, kCodeRawBodyViewOptions);
|
||||
expect(result4.$2, "json");
|
||||
});
|
||||
test('Testing getResponseBodyViewOptions for image/svg+xml', () {
|
||||
MediaType mediaType5 = MediaType("image", "svg+xml");
|
||||
var result5 = getResponseBodyViewOptions(mediaType5);
|
||||
expect(result5.$0,kCodeRawBodyViewOptions);
|
||||
expect(result5.$1, "xml");
|
||||
expect(result5.$1, kCodeRawBodyViewOptions);
|
||||
expect(result5.$2, "xml");
|
||||
});
|
||||
test('Testing getResponseBodyViewOptions for application/xhtml+xml', () {
|
||||
MediaType mediaType6 = MediaType("application", "xhtml+xml");
|
||||
var result6 = getResponseBodyViewOptions(mediaType6);
|
||||
expect(result6.$0,kCodeRawBodyViewOptions);
|
||||
expect(result6.$1, "xml");
|
||||
expect(result6.$1, kCodeRawBodyViewOptions);
|
||||
expect(result6.$2, "xml");
|
||||
});
|
||||
test('Testing getResponseBodyViewOptions for application/xml-external-parsed-entity', () {
|
||||
MediaType mediaType7 = MediaType("application", "xml-external-parsed-entity");
|
||||
test(
|
||||
'Testing getResponseBodyViewOptions for application/xml-external-parsed-entity',
|
||||
() {
|
||||
MediaType mediaType7 =
|
||||
MediaType("application", "xml-external-parsed-entity");
|
||||
var result7 = getResponseBodyViewOptions(mediaType7);
|
||||
expect(result7.$0,kCodeRawBodyViewOptions);
|
||||
expect(result7.$1, "xml");
|
||||
expect(result7.$1, kCodeRawBodyViewOptions);
|
||||
expect(result7.$2, "xml");
|
||||
});
|
||||
test('Testing getResponseBodyViewOptions for text/html', () {
|
||||
MediaType mediaType8 = MediaType("text", "html");
|
||||
var result8 = getResponseBodyViewOptions(mediaType8);
|
||||
expect(result8.$0,kCodeRawBodyViewOptions);
|
||||
expect(result8.$1, "xml");
|
||||
expect(result8.$1, kCodeRawBodyViewOptions);
|
||||
expect(result8.$2, "xml");
|
||||
});
|
||||
test('Testing getResponseBodyViewOptions for application/pdf', () {
|
||||
MediaType mediaType9 = MediaType("application", "pdf");
|
||||
var result9 = getResponseBodyViewOptions(mediaType9);
|
||||
expect(result9.$0,kNoBodyViewOptions);
|
||||
expect(result9.$1, "pdf");
|
||||
expect(result9.$1, kNoBodyViewOptions);
|
||||
expect(result9.$2, "pdf");
|
||||
});
|
||||
test('Testing getResponseBodyViewOptions for text/calendar', () {
|
||||
test('Testing getResponseBodyViewOptions for text/calendar', () {
|
||||
MediaType mediaType10 = MediaType("text", "calendar");
|
||||
var result10 = getResponseBodyViewOptions(mediaType10);
|
||||
expect(result10.$0,kRawBodyViewOptions);
|
||||
expect(result10.$1, "calendar");
|
||||
expect(result10.$1, kRawBodyViewOptions);
|
||||
expect(result10.$2, "calendar");
|
||||
});
|
||||
});
|
||||
|
||||
group("Testing formatBody", () {
|
||||
test('Testing formatBody for null values', () {
|
||||
expect(formatBody(null, null),null);
|
||||
expect(formatBody(null, null), null);
|
||||
});
|
||||
test('Testing formatBody for null body values', () {
|
||||
MediaType mediaType1 = MediaType("application", "xml");
|
||||
expect(formatBody(null, mediaType1),null);
|
||||
expect(formatBody(null, mediaType1), null);
|
||||
});
|
||||
test('Testing formatBody for null MediaType values', () {
|
||||
String body1 = '''
|
||||
@ -314,7 +320,7 @@ void main() {
|
||||
"text":"The Chosen One";
|
||||
}
|
||||
''';
|
||||
expect(formatBody(body1, null),null);
|
||||
expect(formatBody(body1, null), null);
|
||||
});
|
||||
test('Testing formatBody for json subtype values', () {
|
||||
String body2 = '''{"data":{"area":9831510.0,"population":331893745}}''';
|
||||
@ -325,7 +331,7 @@ void main() {
|
||||
"population": 331893745
|
||||
}
|
||||
}''';
|
||||
expect(formatBody(body2, mediaType2),result2Expected);
|
||||
expect(formatBody(body2, mediaType2), result2Expected);
|
||||
});
|
||||
test('Testing formatBody for xml subtype values', () {
|
||||
String body3 = '''
|
||||
@ -347,28 +353,32 @@ void main() {
|
||||
<calories>650</calories>
|
||||
</food>
|
||||
</breakfast_menu>''';
|
||||
expect(formatBody(body3, mediaType3),result3Expected);
|
||||
expect(formatBody(body3, mediaType3), result3Expected);
|
||||
});
|
||||
group("Testing formatBody for html", () {
|
||||
MediaType mediaTypeHtml = MediaType("text", "html");
|
||||
test('Testing formatBody for html subtype values', () {
|
||||
String body4 = '''<html>
|
||||
String body4 = '''<html>
|
||||
<body>
|
||||
<h1>My First Heading</h1>
|
||||
<p>My first paragraph.</p>
|
||||
</body>
|
||||
</html>''';
|
||||
expect(formatBody(body4, mediaTypeHtml),body4);
|
||||
</html>''';
|
||||
expect(formatBody(body4, mediaTypeHtml), body4);
|
||||
});
|
||||
|
||||
test('Testing formatBody for html subtype values with random values', () {
|
||||
String body5 =
|
||||
'''<html>${RandomStringGenerator.getRandomStringLines(100, 10000)}</html>''';
|
||||
expect(formatBody(body5, mediaTypeHtml), null);
|
||||
});
|
||||
test(
|
||||
'Testing formatBody for html subtype values with random values within limit',
|
||||
() {
|
||||
String body6 =
|
||||
'''<html>${RandomStringGenerator.getRandomStringLines(100, 190)}</html>''';
|
||||
expect(formatBody(body6, mediaTypeHtml), body6);
|
||||
});
|
||||
});
|
||||
|
||||
test('Testing formatBody for html subtype values with random values', () {
|
||||
String body5 = '''<html>${getRandomStringLines(100, 10000)}</html>''';
|
||||
expect(formatBody(body5, mediaTypeHtml),null);
|
||||
});
|
||||
test('Testing formatBody for html subtype values with random values within limit', () {
|
||||
String body6 = '''<html>${getRandomStringLines(100, 190)}</html>''';
|
||||
expect(formatBody(body6, mediaTypeHtml),body6);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
62
test/utils/window_utils_test.dart
Normal file
62
test/utils/window_utils_test.dart
Normal file
@ -0,0 +1,62 @@
|
||||
import 'package:test/test.dart';
|
||||
import 'package:apidash/utils/window_utils.dart';
|
||||
|
||||
void main() {
|
||||
group(
|
||||
"Testing showButtonLabelsInBodySuccess function",
|
||||
() {
|
||||
test('Test case 1 options 0', () {
|
||||
expect(showButtonLabelsInBodySuccess(0, 300), true);
|
||||
});
|
||||
|
||||
test('Test case 2 options 0', () {
|
||||
expect(showButtonLabelsInBodySuccess(0, 500), true);
|
||||
});
|
||||
|
||||
test('Test case 1 options 1', () {
|
||||
expect(showButtonLabelsInBodySuccess(1, 250), false);
|
||||
});
|
||||
|
||||
test('Test case 2 options 1', () {
|
||||
expect(showButtonLabelsInBodySuccess(1, 350), true);
|
||||
});
|
||||
|
||||
test('Test case 1 options 2', () {
|
||||
expect(showButtonLabelsInBodySuccess(2, 250), false);
|
||||
});
|
||||
|
||||
test('Test case 2 options 2', () {
|
||||
expect(showButtonLabelsInBodySuccess(2, 350), false);
|
||||
});
|
||||
|
||||
test('Test case 3 options 2', () {
|
||||
expect(showButtonLabelsInBodySuccess(2, 450), true);
|
||||
});
|
||||
|
||||
test('Test case 1 options 3', () {
|
||||
expect(showButtonLabelsInBodySuccess(3, 350), false);
|
||||
});
|
||||
|
||||
test('Test case 2 options 3', () {
|
||||
expect(showButtonLabelsInBodySuccess(3, 450), false);
|
||||
});
|
||||
|
||||
test('Test case 3 options 3', () {
|
||||
expect(showButtonLabelsInBodySuccess(3, 550), true);
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
group(
|
||||
"Testing showButtonLabelsInViewCodePane function",
|
||||
() {
|
||||
test('Test case 2', () {
|
||||
expect(showButtonLabelsInViewCodePane(350), false);
|
||||
});
|
||||
|
||||
test('Test case 3', () {
|
||||
expect(showButtonLabelsInViewCodePane(450), true);
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
1
test/widget_test.dart
Normal file
1
test/widget_test.dart
Normal file
@ -0,0 +1 @@
|
||||
void main() {}
|
@ -50,7 +50,7 @@ void main() async {
|
||||
});
|
||||
testWidgets('Testing for code previewer when code is of 1000 lines',
|
||||
(tester) async {
|
||||
String codeLines = getRandomStringLines(1000, 20);
|
||||
String codeLines = RandomStringGenerator.getRandomStringLines(1000, 20);
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
title: 'Code Previewer',
|
||||
|
@ -1,6 +1,7 @@
|
||||
import 'package:apidash/widgets/widgets.dart'
|
||||
show ViewCodePane, CodeGenPreviewer;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:apidash/widgets/codegen_previewer.dart';
|
||||
import 'package:apidash/consts.dart';
|
||||
import '../test_consts.dart';
|
||||
|
||||
@ -58,6 +59,8 @@ void main() async {
|
||||
Expanded(
|
||||
child: ViewCodePane(
|
||||
code: code,
|
||||
codegenLanguage: CodegenLanguage.dartHttp,
|
||||
onChangedCodegenLanguage: (p0) {},
|
||||
),
|
||||
),
|
||||
],
|
||||
@ -66,7 +69,18 @@ void main() async {
|
||||
);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('Code'), findsOneWidget);
|
||||
expect(find.byType(DropdownButton<CodegenLanguage>), findsOneWidget);
|
||||
expect(
|
||||
(tester.widget(find.byType(DropdownButton<CodegenLanguage>))
|
||||
as DropdownButton)
|
||||
.value,
|
||||
equals(CodegenLanguage.dartHttp));
|
||||
|
||||
await tester.tap(find.text('Dart (http)'));
|
||||
await tester.pump();
|
||||
await tester.pump(const Duration(seconds: 1));
|
||||
|
||||
expect(find.text('Kotlin (OkHttp)'), findsWidgets);
|
||||
|
||||
expect(find.textContaining('Error Status Code', findRichText: true),
|
||||
findsOneWidget);
|
||||
@ -84,6 +98,8 @@ void main() async {
|
||||
Expanded(
|
||||
child: ViewCodePane(
|
||||
code: code,
|
||||
codegenLanguage: CodegenLanguage.dartHttp,
|
||||
onChangedCodegenLanguage: (p0) {},
|
||||
),
|
||||
),
|
||||
],
|
||||
@ -92,7 +108,7 @@ void main() async {
|
||||
);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('Code'), findsOneWidget);
|
||||
expect(find.text('Dart (http)'), findsOneWidget);
|
||||
|
||||
expect(find.textContaining('Error Status Code', findRichText: true),
|
||||
findsOneWidget);
|
||||
|
@ -1,6 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:apidash/widgets/previewer.dart';
|
||||
import 'package:apidash/widgets/widgets.dart';
|
||||
import 'package:apidash/consts.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import '../test_consts.dart';
|
||||
@ -33,9 +33,7 @@ void main() {
|
||||
),
|
||||
);
|
||||
|
||||
expect(
|
||||
find.text("${kMimeTypeRaiseIssueStart}audio/mpeg$kMimeTypeRaiseIssue"),
|
||||
findsOneWidget);
|
||||
expect(find.byType(Uint8AudioPlayer), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('Testing when type/subtype is video/H264', (tester) async {
|
||||
@ -120,4 +118,21 @@ void main() {
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text(kImageError), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('Testing when type/subtype is audio/mpeg corrupted',
|
||||
(tester) async {
|
||||
Uint8List bytesAudioCorrupt =
|
||||
Uint8List.fromList(List.generate(100, (index) => index));
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
title: 'Previewer',
|
||||
home: Scaffold(
|
||||
body: Previewer(
|
||||
type: 'audio', subtype: 'mpeg', bytes: bytesAudioCorrupt),
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text(kAudioError), findsOneWidget);
|
||||
});
|
||||
}
|
||||
|
@ -204,8 +204,9 @@ void main() {
|
||||
url: 'api.foss42.com/case/lower',
|
||||
name: 'foss42 api',
|
||||
requestHeaders: [
|
||||
KVRow('content-length', '18'),
|
||||
KVRow('content-type', 'application/json; charset=utf-8')
|
||||
NameValueModel(name: 'content-length', value: '18'),
|
||||
NameValueModel(
|
||||
name: 'content-type', value: 'application/json; charset=utf-8')
|
||||
],
|
||||
requestBodyContentType: ContentType.json,
|
||||
requestBody: '''{
|
||||
|
@ -6,12 +6,12 @@ import 'package:multi_split_view/multi_split_view.dart';
|
||||
void main() {
|
||||
testWidgets('Testing for Dashboard Splitview', (tester) async {
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
const MaterialApp(
|
||||
title: 'Dashboard Splitview',
|
||||
home: Scaffold(
|
||||
body: DashboardSplitView(
|
||||
sidebarWidget: Column(children: const [Text("Hello")]),
|
||||
mainWidget: Column(children: const [Text("World")]),
|
||||
sidebarWidget: Column(children: [Text("Hello")]),
|
||||
mainWidget: Column(children: [Text("World")]),
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -23,12 +23,12 @@ void main() {
|
||||
});
|
||||
testWidgets('Testing for Equal SplitView', (tester) async {
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
const MaterialApp(
|
||||
title: 'Equal SplitView',
|
||||
home: Scaffold(
|
||||
body: EqualSplitView(
|
||||
leftWidget: Column(children: const [Text("Hello equal")]),
|
||||
rightWidget: Column(children: const [Text("World equal")]),
|
||||
leftWidget: Column(children: [Text("Hello equal")]),
|
||||
rightWidget: Column(children: [Text("World equal")]),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -10,8 +10,8 @@ void main() {
|
||||
MaterialApp(
|
||||
title: 'URL Field',
|
||||
theme: kThemeDataDark,
|
||||
home: Scaffold(
|
||||
body: Column(children: const [URLField(activeId: '2')]),
|
||||
home: const Scaffold(
|
||||
body: Column(children: [URLField(activeId: '2')]),
|
||||
),
|
||||
),
|
||||
);
|
||||
@ -30,9 +30,9 @@ void main() {
|
||||
MaterialApp(
|
||||
title: 'CellField',
|
||||
theme: kThemeDataLight,
|
||||
home: Scaffold(
|
||||
home: const Scaffold(
|
||||
body: Column(
|
||||
children: const [
|
||||
children: [
|
||||
CellField(
|
||||
keyId: "4",
|
||||
hintText: "Passing some hint text",
|
||||
|
Reference in New Issue
Block a user