mirror of
https://github.com/foss42/apidash.git
synced 2025-06-05 10:20:44 +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.
|
about: Something wrong with API Dash. Report the problem/bug here.
|
||||||
title: ''
|
title: ''
|
||||||
labels: bug
|
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.
|
about: Help us make API Dash better by suggesting new features.
|
||||||
title: ''
|
title: ''
|
||||||
labels: enhancement
|
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.
|
Don't worry, we got you covered.
|
||||||
title: ''
|
title: ''
|
||||||
labels: ''
|
labels: ''
|
||||||
assignees: animator
|
assignees: ''
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -49,8 +49,10 @@ linux/
|
|||||||
macos/
|
macos/
|
||||||
windows/
|
windows/
|
||||||
web/
|
web/
|
||||||
|
ios/
|
||||||
.vscode/*
|
.vscode/*
|
||||||
icons/
|
icons/
|
||||||
coverage/*
|
coverage/*
|
||||||
installers/*
|
installers/*
|
||||||
.metadata
|
.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:
|
You can contribute to the project in any or all of the following ways:
|
||||||
|
|
||||||
- [Ask a question](https://github.com/foss42/api-dash/discussions)
|
- [Ask a question](https://github.com/foss42/apidash/discussions)
|
||||||
- [Submit a bug report](https://github.com/foss42/api-dash/issues/new/choose)
|
- [Submit a bug report](https://github.com/foss42/apidash/issues/new/choose)
|
||||||
- [Request a new feature](https://github.com/foss42/api-dash/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/api-dash/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 documentation
|
||||||
- Add a new feature, resolve an existing issue or add a new test to the project. (Goto [Code Contribution Guidelines](#code-contribution-guidelines)).
|
- 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
|
### 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 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 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 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 5** - Run API Dash locally (More details [here](#how-to-run-api-dash-locally)).
|
||||||
**Step 6** - Make code changes in the branch.
|
**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
|
### 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 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/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-feature-xyz`.
|
**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 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.
|
**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/`).
|
- Services (`lib/services/`).
|
||||||
|
|
||||||
**Step 1** - Identify the test you want to add or improve.
|
**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 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 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)).
|
**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
|
## 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?
|
### How to run API Dash locally?
|
||||||
|
|
||||||
1. Fork the project.
|
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:
|
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?
|
### How to run tests?
|
||||||
@ -86,7 +94,7 @@ flutter run --enable-experiment=records
|
|||||||
To run tests execute the following command:
|
To run tests execute the following command:
|
||||||
|
|
||||||
```
|
```
|
||||||
flutter test --enable-experiment=records --coverage
|
flutter test --coverage
|
||||||
```
|
```
|
||||||
|
|
||||||
To generate coverage report as html execute:
|
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
|
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:
|
To view the coverage report in the browser for further analysis, execute:
|
||||||
|
|
||||||
```
|
```
|
||||||
open coverage/html/index.html
|
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
|
# Installation Instructions
|
||||||
|
|
||||||
## Windows
|
## 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.
|
To install it, simply double click on the installer and follow the step by step installation wizard.
|
||||||
|
|
||||||
## MacOS
|
## 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.**
|
**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.)
|
### 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.
|
`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.)
|
### 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.
|
`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>
|
<tr>
|
||||||
<td>macOS</td>
|
<td>macOS</td>
|
||||||
<td><code>.dmg</code></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>Apple Silicon & Intel</td>
|
||||||
<td><a href="https://bit.ly/44wmazf">Link</a></td>
|
<td><a href="https://bit.ly/44wmazf">Link</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Windows</td>
|
<td>Windows</td>
|
||||||
<td><code>.exe</code></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>64-bit</td>
|
||||||
<td><a href="https://bit.ly/424ExKb">Link</a></td>
|
<td><a href="https://bit.ly/424ExKb">Link</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td rowspan=4>Linux</td>
|
<td rowspan=4>Linux</td>
|
||||||
<td rowspan=2><code>.deb</code></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>amd64</td>
|
||||||
<td><a href="https://bit.ly/44sWPq2">Link</a></td>
|
<td><a href="https://bit.ly/44sWPq2">Link</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
@ -47,7 +47,7 @@ API Dash can be downloaded from the links below:
|
|||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td rowspan=2><code>.rpm</code></td>
|
<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>x86_64</td>
|
||||||
<td><a href="https://bit.ly/417gWHe">Link</a></td>
|
<td><a href="https://bit.ly/417gWHe">Link</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
@ -60,7 +60,9 @@ API Dash can be downloaded from the links below:
|
|||||||
|
|
||||||
## A Quick Glimpse of API Dash ⚡️ (Demo Video)
|
## 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
|
## List of Features
|
||||||
|
|
||||||
@ -99,16 +101,16 @@ Visit [CHANGELOG.md](CHANGELOG.md)
|
|||||||
|
|
||||||
## Provide Feedback, Report Bugs & Request New Features
|
## 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
|
## Contribute to API Dash
|
||||||
|
|
||||||
You can contribute to API Dash in any or all of the following ways:
|
You can contribute to API Dash in any or all of the following ways:
|
||||||
|
|
||||||
- [Ask a question](https://github.com/foss42/api-dash/discussions)
|
- [Ask a question](https://github.com/foss42/apidash/discussions)
|
||||||
- [Submit a bug report](https://github.com/foss42/api-dash/issues/new/choose)
|
- [Submit a bug report](https://github.com/foss42/apidash/issues/new/choose)
|
||||||
- [Request a new feature](https://github.com/foss42/api-dash/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/api-dash/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 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).
|
- 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).
|
7. Notification on save, download and any other user action (UX improvement).
|
||||||
8. Linux builds are now available for API Dash (.deb & .rpm)
|
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
|
||||||
#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';
|
import 'package:davi/davi.dart';
|
||||||
|
|
||||||
const kDiscordUrl = "https://bit.ly/heyfoss";
|
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";
|
const kIssueUrl = "$kGitUrl/issues";
|
||||||
|
|
||||||
final kIsMacOS = !kIsWeb && Platform.isMacOS;
|
final kIsMacOS = !kIsWeb && Platform.isMacOS;
|
||||||
@ -28,6 +28,7 @@ const kWindowTitle = "API Dash";
|
|||||||
const kMinWindowSize = Size(900, 600);
|
const kMinWindowSize = Size(900, 600);
|
||||||
const kMinInitialWindowWidth = 1200.0;
|
const kMinInitialWindowWidth = 1200.0;
|
||||||
const kMinInitialWindowHeight = 800.0;
|
const kMinInitialWindowHeight = 800.0;
|
||||||
|
const kMinRequestEditorDetailsCardPaneSize = 300.0;
|
||||||
|
|
||||||
const kColorSchemeSeed = Colors.blue;
|
const kColorSchemeSeed = Colors.blue;
|
||||||
final kFontFamily = GoogleFonts.openSans().fontFamily;
|
final kFontFamily = GoogleFonts.openSans().fontFamily;
|
||||||
@ -225,9 +226,22 @@ const kMethodsWithBody = [
|
|||||||
HTTPVerb.patch,
|
HTTPVerb.patch,
|
||||||
HTTPVerb.delete,
|
HTTPVerb.delete,
|
||||||
];
|
];
|
||||||
|
|
||||||
const kDefaultHttpMethod = HTTPVerb.get;
|
const kDefaultHttpMethod = HTTPVerb.get;
|
||||||
const kDefaultContentType = ContentType.json;
|
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 JsonEncoder kEncoder = JsonEncoder.withIndent(' ');
|
||||||
const LineSplitter kSplitter = LineSplitter();
|
const LineSplitter kSplitter = LineSplitter();
|
||||||
|
|
||||||
@ -311,7 +325,7 @@ const Map<String, Map<String, List<ResponseBodyView>>>
|
|||||||
kSubTypeSvg: kCodeRawBodyViewOptions,
|
kSubTypeSvg: kCodeRawBodyViewOptions,
|
||||||
},
|
},
|
||||||
kTypeAudio: {
|
kTypeAudio: {
|
||||||
kSubTypeDefaultViewOptions: kNoBodyViewOptions,
|
kSubTypeDefaultViewOptions: kPreviewBodyViewOptions,
|
||||||
},
|
},
|
||||||
kTypeVideo: {
|
kTypeVideo: {
|
||||||
kSubTypeDefaultViewOptions: kNoBodyViewOptions,
|
kSubTypeDefaultViewOptions: kNoBodyViewOptions,
|
||||||
@ -427,6 +441,9 @@ const kImageError =
|
|||||||
const kPdfError =
|
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.";
|
"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 =
|
const kRaiseIssue =
|
||||||
"\nPlease raise an issue in API Dash GitHub repo so that we can resolve it.";
|
"\nPlease raise an issue in API Dash GitHub repo so that we can resolve it.";
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ void main() async {
|
|||||||
await setupInitialWindow();
|
await setupInitialWindow();
|
||||||
} else {
|
} else {
|
||||||
var win = getInitialSize();
|
var win = getInitialSize();
|
||||||
await setupWindow(sz: win.$0, off: win.$1);
|
await setupWindow(sz: win.$1, off: win.$2);
|
||||||
}
|
}
|
||||||
runApp(
|
runApp(
|
||||||
ProviderScope(
|
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 'request_model.dart';
|
||||||
export 'response_model.dart';
|
export 'response_model.dart';
|
||||||
export 'settings_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:flutter/foundation.dart';
|
||||||
import 'package:apidash/consts.dart';
|
import 'package:apidash/consts.dart';
|
||||||
import 'package:apidash/utils/utils.dart' show mapToRows, rowsToMap;
|
import 'package:apidash/utils/utils.dart' show mapToRows, rowsToMap;
|
||||||
import 'kvrow_model.dart';
|
import 'name_value_model.dart';
|
||||||
import 'response_model.dart';
|
import 'response_model.dart';
|
||||||
|
|
||||||
@immutable
|
@immutable
|
||||||
class RequestModel {
|
class RequestModel {
|
||||||
const RequestModel({
|
const RequestModel({
|
||||||
required this.id,
|
required this.id,
|
||||||
this.method = kDefaultHttpMethod,
|
this.method = HTTPVerb.get,
|
||||||
this.url = "",
|
this.url = "",
|
||||||
this.name = "",
|
this.name = "",
|
||||||
this.description = "",
|
this.description = "",
|
||||||
this.requestTabIndex = 0,
|
this.requestTabIndex = 0,
|
||||||
this.requestHeaders,
|
this.requestHeaders,
|
||||||
this.requestParams,
|
this.requestParams,
|
||||||
this.requestBodyContentType = kDefaultContentType,
|
this.requestBodyContentType = ContentType.json,
|
||||||
this.requestBody,
|
this.requestBody,
|
||||||
this.responseStatus,
|
this.responseStatus,
|
||||||
this.message,
|
this.message,
|
||||||
@ -28,8 +28,8 @@ class RequestModel {
|
|||||||
final String name;
|
final String name;
|
||||||
final String description;
|
final String description;
|
||||||
final int requestTabIndex;
|
final int requestTabIndex;
|
||||||
final List<KVRow>? requestHeaders;
|
final List<NameValueModel>? requestHeaders;
|
||||||
final List<KVRow>? requestParams;
|
final List<NameValueModel>? requestParams;
|
||||||
final ContentType requestBodyContentType;
|
final ContentType requestBodyContentType;
|
||||||
final String? requestBody;
|
final String? requestBody;
|
||||||
final int? responseStatus;
|
final int? responseStatus;
|
||||||
@ -59,8 +59,8 @@ class RequestModel {
|
|||||||
String? name,
|
String? name,
|
||||||
String? description,
|
String? description,
|
||||||
int? requestTabIndex,
|
int? requestTabIndex,
|
||||||
List<KVRow>? requestHeaders,
|
List<NameValueModel>? requestHeaders,
|
||||||
List<KVRow>? requestParams,
|
List<NameValueModel>? requestParams,
|
||||||
ContentType? requestBodyContentType,
|
ContentType? requestBodyContentType,
|
||||||
String? requestBody,
|
String? requestBody,
|
||||||
int? responseStatus,
|
int? responseStatus,
|
||||||
|
@ -96,8 +96,8 @@ class CollectionStateNotifier extends StateNotifier<List<RequestModel>?> {
|
|||||||
String? name,
|
String? name,
|
||||||
String? description,
|
String? description,
|
||||||
int? requestTabIndex,
|
int? requestTabIndex,
|
||||||
List<KVRow>? requestHeaders,
|
List<NameValueModel>? requestHeaders,
|
||||||
List<KVRow>? requestParams,
|
List<NameValueModel>? requestParams,
|
||||||
ContentType? requestBodyContentType,
|
ContentType? requestBodyContentType,
|
||||||
String? requestBody,
|
String? requestBody,
|
||||||
int? responseStatus,
|
int? responseStatus,
|
||||||
@ -132,17 +132,17 @@ class CollectionStateNotifier extends StateNotifier<List<RequestModel>?> {
|
|||||||
var responseRec =
|
var responseRec =
|
||||||
await request(requestModel, defaultUriScheme: defaultUriScheme);
|
await request(requestModel, defaultUriScheme: defaultUriScheme);
|
||||||
late final RequestModel newRequestModel;
|
late final RequestModel newRequestModel;
|
||||||
if (responseRec.$0 == null) {
|
if (responseRec.$1 == null) {
|
||||||
newRequestModel = requestModel.copyWith(
|
newRequestModel = requestModel.copyWith(
|
||||||
responseStatus: -1,
|
responseStatus: -1,
|
||||||
message: responseRec.$2,
|
message: responseRec.$3,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
final responseModel = baseResponseModel.fromResponse(
|
final responseModel = baseResponseModel.fromResponse(
|
||||||
response: responseRec.$0!,
|
response: responseRec.$1!,
|
||||||
time: responseRec.$1!,
|
time: responseRec.$2!,
|
||||||
);
|
);
|
||||||
int statusCode = responseRec.$0!.statusCode;
|
int statusCode = responseRec.$1!.statusCode;
|
||||||
newRequestModel = requestModel.copyWith(
|
newRequestModel = requestModel.copyWith(
|
||||||
responseStatus: statusCode,
|
responseStatus: statusCode,
|
||||||
message: kResponseCodeReasons[statusCode],
|
message: kResponseCodeReasons[statusCode],
|
||||||
@ -203,4 +203,10 @@ class CollectionStateNotifier extends StateNotifier<List<RequestModel>?> {
|
|||||||
await hiveHandler.removeUnused();
|
await hiveHandler.removeUnused();
|
||||||
ref.read(saveDataStateProvider.notifier).update((state) => false);
|
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';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
|
||||||
final navRailIndexStateProvider = StateProvider<int?>((ref) => 0);
|
final navRailIndexStateProvider = StateProvider<int?>((ref) => 0);
|
||||||
@ -7,3 +8,4 @@ final sentRequestIdStateProvider = StateProvider<String?>((ref) => null);
|
|||||||
final codePaneVisibleStateProvider = StateProvider<bool>((ref) => false);
|
final codePaneVisibleStateProvider = StateProvider<bool>((ref) => false);
|
||||||
final saveDataStateProvider = StateProvider<bool>((ref) => false);
|
final saveDataStateProvider = StateProvider<bool>((ref) => false);
|
||||||
final clearDataStateProvider = 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,
|
color: Theme.of(context).colorScheme.surfaceVariant,
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: <int?, Widget>{
|
child: IndexedStack(
|
||||||
null: const SettingsPage(),
|
alignment: AlignmentDirectional.topCenter,
|
||||||
0: const IntroPage(),
|
index: railIdx == null ? 0 : railIdx + 1,
|
||||||
1: const HomePage()
|
children: const [
|
||||||
}[railIdx]!,
|
SettingsPage(),
|
||||||
|
IntroPage(),
|
||||||
|
HomePage(),
|
||||||
|
],
|
||||||
|
),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -13,7 +13,7 @@ class CodePane extends ConsumerStatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _CodePaneState extends ConsumerState<CodePane> {
|
class _CodePaneState extends ConsumerState<CodePane> {
|
||||||
final DartHttpCodeGen dartHttpCodeGen = DartHttpCodeGen();
|
final Codegen codegen = Codegen();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@ -22,10 +22,15 @@ class _CodePaneState extends ConsumerState<CodePane> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final CodegenLanguage codegenLanguage =
|
||||||
|
ref.watch(codegenLanguageStateProvider);
|
||||||
|
|
||||||
final activeRequestModel = ref.watch(activeRequestModelProvider);
|
final activeRequestModel = ref.watch(activeRequestModelProvider);
|
||||||
final defaultUriScheme =
|
final defaultUriScheme =
|
||||||
ref.watch(settingsProvider.select((value) => value.defaultUriScheme));
|
ref.watch(settingsProvider.select((value) => value.defaultUriScheme));
|
||||||
final code = dartHttpCodeGen.getCode(activeRequestModel!, defaultUriScheme);
|
|
||||||
|
final code =
|
||||||
|
codegen.getCode(codegenLanguage, activeRequestModel!, defaultUriScheme);
|
||||||
if (code == null) {
|
if (code == null) {
|
||||||
return const ErrorMessage(
|
return const ErrorMessage(
|
||||||
message: "An error was encountered while generating code. $kRaiseIssue",
|
message: "An error was encountered while generating code. $kRaiseIssue",
|
||||||
@ -33,6 +38,12 @@ class _CodePaneState extends ConsumerState<CodePane> {
|
|||||||
}
|
}
|
||||||
return ViewCodePane(
|
return ViewCodePane(
|
||||||
code: code,
|
code: code,
|
||||||
|
codegenLanguage: codegenLanguage,
|
||||||
|
onChangedCodegenLanguage: (CodegenLanguage? value) {
|
||||||
|
ref
|
||||||
|
.read(codegenLanguageStateProvider.notifier)
|
||||||
|
.update((state) => value!);
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ class _EditRequestBodyState extends ConsumerState<EditRequestBody> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final activeId = ref.watch(activeIdStateProvider);
|
final activeId = ref.watch(activeIdStateProvider);
|
||||||
final reqestModel = ref
|
final requestModel = ref
|
||||||
.read(collectionStateNotifierProvider.notifier)
|
.read(collectionStateNotifierProvider.notifier)
|
||||||
.getRequestModel(activeId!);
|
.getRequestModel(activeId!);
|
||||||
return Container(
|
return Container(
|
||||||
@ -25,11 +25,11 @@ class _EditRequestBodyState extends ConsumerState<EditRequestBody> {
|
|||||||
margin: kPt5o10,
|
margin: kPt5o10,
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
SizedBox(
|
const SizedBox(
|
||||||
height: kHeaderHeight,
|
height: kHeaderHeight,
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: const [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
"Select Content Type:",
|
"Select Content Type:",
|
||||||
),
|
),
|
||||||
@ -41,7 +41,7 @@ class _EditRequestBodyState extends ConsumerState<EditRequestBody> {
|
|||||||
child: TextFieldEditor(
|
child: TextFieldEditor(
|
||||||
key: Key("$activeId-body"),
|
key: Key("$activeId-body"),
|
||||||
fieldKey: "$activeId-body-editor",
|
fieldKey: "$activeId-body-editor",
|
||||||
initialValue: reqestModel.requestBody,
|
initialValue: requestModel.requestBody,
|
||||||
onChanged: (String value) {
|
onChanged: (String value) {
|
||||||
ref
|
ref
|
||||||
.read(collectionStateNotifierProvider.notifier)
|
.read(collectionStateNotifierProvider.notifier)
|
||||||
|
@ -15,7 +15,7 @@ class EditRequestHeaders extends ConsumerStatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class EditRequestHeadersState extends ConsumerState<EditRequestHeaders> {
|
class EditRequestHeadersState extends ConsumerState<EditRequestHeaders> {
|
||||||
late List<KVRow> rows;
|
late List<NameValueModel> rows;
|
||||||
final random = Random.secure();
|
final random = Random.secure();
|
||||||
late int seed;
|
late int seed;
|
||||||
|
|
||||||
@ -37,9 +37,11 @@ class EditRequestHeadersState extends ConsumerState<EditRequestHeaders> {
|
|||||||
final length = ref.watch(activeRequestModelProvider
|
final length = ref.watch(activeRequestModelProvider
|
||||||
.select((value) => value?.requestHeaders?.length));
|
.select((value) => value?.requestHeaders?.length));
|
||||||
var rH = ref.read(activeRequestModelProvider)?.requestHeaders;
|
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,
|
rows: rows,
|
||||||
columns: [
|
columns: [
|
||||||
DaviColumn(
|
DaviColumn(
|
||||||
@ -49,10 +51,10 @@ class EditRequestHeadersState extends ConsumerState<EditRequestHeaders> {
|
|||||||
int idx = row.index;
|
int idx = row.index;
|
||||||
return CellField(
|
return CellField(
|
||||||
keyId: "$activeId-$idx-headers-k-$seed",
|
keyId: "$activeId-$idx-headers-k-$seed",
|
||||||
initialValue: rows[idx].k,
|
initialValue: rows[idx].name,
|
||||||
hintText: "Add Header Name",
|
hintText: "Add Header Name",
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
rows[idx] = rows[idx].copyWith(k: value);
|
rows[idx] = rows[idx].copyWith(name: value);
|
||||||
_onFieldChange(activeId!);
|
_onFieldChange(activeId!);
|
||||||
},
|
},
|
||||||
colorScheme: Theme.of(context).colorScheme,
|
colorScheme: Theme.of(context).colorScheme,
|
||||||
@ -76,10 +78,10 @@ class EditRequestHeadersState extends ConsumerState<EditRequestHeaders> {
|
|||||||
int idx = row.index;
|
int idx = row.index;
|
||||||
return CellField(
|
return CellField(
|
||||||
keyId: "$activeId-$idx-headers-v-$seed",
|
keyId: "$activeId-$idx-headers-v-$seed",
|
||||||
initialValue: rows[idx].v,
|
initialValue: rows[idx].value,
|
||||||
hintText: " Add Header Value",
|
hintText: " Add Header Value",
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
rows[idx] = rows[idx].copyWith(v: value);
|
rows[idx] = rows[idx].copyWith(value: value);
|
||||||
_onFieldChange(activeId!);
|
_onFieldChange(activeId!);
|
||||||
},
|
},
|
||||||
colorScheme: Theme.of(context).colorScheme,
|
colorScheme: Theme.of(context).colorScheme,
|
||||||
@ -96,6 +98,9 @@ class EditRequestHeadersState extends ConsumerState<EditRequestHeaders> {
|
|||||||
? kIconRemoveDark
|
? kIconRemoveDark
|
||||||
: kIconRemoveLight,
|
: kIconRemoveLight,
|
||||||
onTap: () {
|
onTap: () {
|
||||||
|
if (rows.length == 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
rows.removeAt(row.index);
|
rows.removeAt(row.index);
|
||||||
seed = random.nextInt(kRandMax);
|
seed = random.nextInt(kRandMax);
|
||||||
_onFieldChange(activeId!);
|
_onFieldChange(activeId!);
|
||||||
@ -118,7 +123,7 @@ class EditRequestHeadersState extends ConsumerState<EditRequestHeaders> {
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: DaviTheme(
|
child: DaviTheme(
|
||||||
data: kTableThemeData,
|
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),
|
padding: const EdgeInsets.only(bottom: 30),
|
||||||
child: ElevatedButton.icon(
|
child: ElevatedButton.icon(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
rows.add(const KVRow("", ""));
|
rows.add(const NameValueModel(name: "", value: ""));
|
||||||
_onFieldChange(activeId!);
|
_onFieldChange(activeId!);
|
||||||
},
|
},
|
||||||
icon: const Icon(Icons.add),
|
icon: const Icon(Icons.add),
|
||||||
|
@ -16,7 +16,7 @@ class EditRequestURLParams extends ConsumerStatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class EditRequestURLParamsState extends ConsumerState<EditRequestURLParams> {
|
class EditRequestURLParamsState extends ConsumerState<EditRequestURLParams> {
|
||||||
late List<KVRow> rows;
|
late List<NameValueModel> rows;
|
||||||
final random = Random.secure();
|
final random = Random.secure();
|
||||||
late int seed;
|
late int seed;
|
||||||
|
|
||||||
@ -38,9 +38,11 @@ class EditRequestURLParamsState extends ConsumerState<EditRequestURLParams> {
|
|||||||
final length = ref.watch(activeRequestModelProvider
|
final length = ref.watch(activeRequestModelProvider
|
||||||
.select((value) => value?.requestParams?.length));
|
.select((value) => value?.requestParams?.length));
|
||||||
var rP = ref.read(activeRequestModelProvider)?.requestParams;
|
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,
|
rows: rows,
|
||||||
columns: [
|
columns: [
|
||||||
DaviColumn(
|
DaviColumn(
|
||||||
@ -50,10 +52,10 @@ class EditRequestURLParamsState extends ConsumerState<EditRequestURLParams> {
|
|||||||
int idx = row.index;
|
int idx = row.index;
|
||||||
return CellField(
|
return CellField(
|
||||||
keyId: "$activeId-$idx-params-k-$seed",
|
keyId: "$activeId-$idx-params-k-$seed",
|
||||||
initialValue: rows[idx].k,
|
initialValue: rows[idx].name,
|
||||||
hintText: "Add URL Parameter",
|
hintText: "Add URL Parameter",
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
rows[idx] = rows[idx].copyWith(k: value);
|
rows[idx] = rows[idx].copyWith(name: value);
|
||||||
_onFieldChange(activeId!);
|
_onFieldChange(activeId!);
|
||||||
},
|
},
|
||||||
colorScheme: Theme.of(context).colorScheme,
|
colorScheme: Theme.of(context).colorScheme,
|
||||||
@ -77,10 +79,10 @@ class EditRequestURLParamsState extends ConsumerState<EditRequestURLParams> {
|
|||||||
int idx = row.index;
|
int idx = row.index;
|
||||||
return CellField(
|
return CellField(
|
||||||
keyId: "$activeId-$idx-params-v-$seed",
|
keyId: "$activeId-$idx-params-v-$seed",
|
||||||
initialValue: rows[idx].v,
|
initialValue: rows[idx].value,
|
||||||
hintText: "Add Value",
|
hintText: "Add Value",
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
rows[idx] = rows[idx].copyWith(v: value);
|
rows[idx] = rows[idx].copyWith(value: value);
|
||||||
_onFieldChange(activeId!);
|
_onFieldChange(activeId!);
|
||||||
},
|
},
|
||||||
colorScheme: Theme.of(context).colorScheme,
|
colorScheme: Theme.of(context).colorScheme,
|
||||||
@ -97,6 +99,9 @@ class EditRequestURLParamsState extends ConsumerState<EditRequestURLParams> {
|
|||||||
? kIconRemoveDark
|
? kIconRemoveDark
|
||||||
: kIconRemoveLight,
|
: kIconRemoveLight,
|
||||||
onTap: () {
|
onTap: () {
|
||||||
|
if (rows.length == 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
rows.removeAt(row.index);
|
rows.removeAt(row.index);
|
||||||
seed = random.nextInt(kRandMax);
|
seed = random.nextInt(kRandMax);
|
||||||
_onFieldChange(activeId!);
|
_onFieldChange(activeId!);
|
||||||
@ -119,7 +124,7 @@ class EditRequestURLParamsState extends ConsumerState<EditRequestURLParams> {
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: DaviTheme(
|
child: DaviTheme(
|
||||||
data: kTableThemeData,
|
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),
|
padding: const EdgeInsets.only(bottom: 30),
|
||||||
child: ElevatedButton.icon(
|
child: ElevatedButton.icon(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
rows.add(const KVRow("", ""));
|
rows.add(const NameValueModel(name: "", value: ""));
|
||||||
_onFieldChange(activeId!);
|
_onFieldChange(activeId!);
|
||||||
},
|
},
|
||||||
icon: const Icon(Icons.add),
|
icon: const Icon(Icons.add),
|
||||||
|
@ -28,8 +28,8 @@ class _RequestEditorPaneState extends ConsumerState<RequestEditorPane> {
|
|||||||
} else {
|
} else {
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: kIsMacOS ? kPt24o8 : kP8,
|
padding: kIsMacOS ? kPt24o8 : kP8,
|
||||||
child: Column(
|
child: const Column(
|
||||||
children: const [
|
children: [
|
||||||
EditorPaneRequestURLCard(),
|
EditorPaneRequestURLCard(),
|
||||||
kVSpacer10,
|
kVSpacer10,
|
||||||
Expanded(
|
Expanded(
|
||||||
|
@ -28,13 +28,13 @@ class _EditorPaneRequestURLCardState extends State<EditorPaneRequestURLCard> {
|
|||||||
),
|
),
|
||||||
borderRadius: kBorderRadius12,
|
borderRadius: kBorderRadius12,
|
||||||
),
|
),
|
||||||
child: Padding(
|
child: const Padding(
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: EdgeInsets.symmetric(
|
||||||
vertical: 5,
|
vertical: 5,
|
||||||
horizontal: 20,
|
horizontal: 20,
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: const [
|
children: [
|
||||||
DropdownButtonHTTPMethod(),
|
DropdownButtonHTTPMethod(),
|
||||||
kHSpacer20,
|
kHSpacer20,
|
||||||
Expanded(
|
Expanded(
|
||||||
|
@ -13,8 +13,8 @@ class HomePage extends StatefulWidget {
|
|||||||
class HomePageState extends State<HomePage> {
|
class HomePageState extends State<HomePage> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Column(
|
return const Column(
|
||||||
children: const [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: DashboardSplitView(
|
child: DashboardSplitView(
|
||||||
sidebarWidget: CollectionPane(),
|
sidebarWidget: CollectionPane(),
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:apidash/widgets/widgets.dart';
|
import 'package:apidash/widgets/widgets.dart';
|
||||||
import 'package:apidash/consts.dart';
|
|
||||||
|
|
||||||
class IntroPage extends StatefulWidget {
|
class IntroPage extends StatefulWidget {
|
||||||
const IntroPage({super.key});
|
const IntroPage({super.key});
|
||||||
@ -12,9 +11,6 @@ class IntroPage extends StatefulWidget {
|
|||||||
class _IntroPageState extends State<IntroPage> {
|
class _IntroPageState extends State<IntroPage> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return const Padding(
|
return const IntroMessage();
|
||||||
padding: kPh60,
|
|
||||||
child: IntroMessage(),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:apidash/providers/providers.dart';
|
import '../providers/providers.dart';
|
||||||
import 'package:apidash/widgets/widgets.dart';
|
import '../widgets/widgets.dart';
|
||||||
|
import '../utils/utils.dart';
|
||||||
import 'package:apidash/consts.dart';
|
import 'package:apidash/consts.dart';
|
||||||
|
|
||||||
class SettingsPage extends ConsumerStatefulWidget {
|
class SettingsPage extends ConsumerStatefulWidget {
|
||||||
@ -73,6 +74,24 @@ class _SettingsPageState extends ConsumerState<SettingsPage> {
|
|||||||
.update(saveResponses: value);
|
.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(
|
ListTile(
|
||||||
contentPadding: EdgeInsets.zero,
|
contentPadding: EdgeInsets.zero,
|
||||||
hoverColor: kColorTransparent,
|
hoverColor: kColorTransparent,
|
||||||
|
@ -7,66 +7,59 @@ import 'package:apidash/models/models.dart';
|
|||||||
import 'package:apidash/consts.dart';
|
import 'package:apidash/consts.dart';
|
||||||
|
|
||||||
Future<(http.Response?, Duration?, String?)> request(
|
Future<(http.Response?, Duration?, String?)> request(
|
||||||
RequestModel requestModel,
|
RequestModel requestModel, {
|
||||||
{String defaultUriScheme = kDefaultUriScheme}
|
String defaultUriScheme = kDefaultUriScheme,
|
||||||
) async {
|
}) async {
|
||||||
(Uri?, String?) uriRec = getValidRequestUri(requestModel.url,
|
(Uri?, String?) uriRec = getValidRequestUri(
|
||||||
requestModel.requestParams,
|
requestModel.url,
|
||||||
defaultUriScheme: defaultUriScheme);
|
requestModel.requestParams,
|
||||||
if(uriRec.$0 != null){
|
defaultUriScheme: defaultUriScheme,
|
||||||
Uri requestUrl = uriRec.$0!;
|
);
|
||||||
|
if (uriRec.$1 != null) {
|
||||||
|
Uri requestUrl = uriRec.$1!;
|
||||||
Map<String, String> headers = rowsToMap(requestModel.requestHeaders) ?? {};
|
Map<String, String> headers = rowsToMap(requestModel.requestHeaders) ?? {};
|
||||||
http.Response response;
|
http.Response response;
|
||||||
String? body;
|
String? body;
|
||||||
try {
|
try {
|
||||||
var requestBody = requestModel.requestBody;
|
var requestBody = requestModel.requestBody;
|
||||||
if(kMethodsWithBody.contains(requestModel.method) && requestBody != null){
|
if (kMethodsWithBody.contains(requestModel.method) &&
|
||||||
|
requestBody != null) {
|
||||||
var contentLength = utf8.encode(requestBody).length;
|
var contentLength = utf8.encode(requestBody).length;
|
||||||
if (contentLength > 0){
|
if (contentLength > 0) {
|
||||||
body = requestBody;
|
body = requestBody;
|
||||||
headers[HttpHeaders.contentLengthHeader] = contentLength.toString();
|
headers[HttpHeaders.contentLengthHeader] = contentLength.toString();
|
||||||
headers[HttpHeaders.contentTypeHeader] = kContentTypeMap[requestModel.requestBodyContentType] ?? "";
|
headers[HttpHeaders.contentTypeHeader] =
|
||||||
|
kContentTypeMap[requestModel.requestBodyContentType] ?? "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Stopwatch stopwatch = Stopwatch()..start();
|
Stopwatch stopwatch = Stopwatch()..start();
|
||||||
switch(requestModel.method){
|
switch (requestModel.method) {
|
||||||
case HTTPVerb.get:
|
case HTTPVerb.get:
|
||||||
response = await http.get(requestUrl,
|
response = await http.get(requestUrl, headers: headers);
|
||||||
headers: headers);
|
|
||||||
break;
|
break;
|
||||||
case HTTPVerb.head:
|
case HTTPVerb.head:
|
||||||
response = await http.head(requestUrl,
|
response = await http.head(requestUrl, headers: headers);
|
||||||
headers: headers);
|
|
||||||
break;
|
break;
|
||||||
case HTTPVerb.post:
|
case HTTPVerb.post:
|
||||||
response = await http.post(requestUrl,
|
response = await http.post(requestUrl, headers: headers, body: body);
|
||||||
headers: headers,
|
|
||||||
body: body);
|
|
||||||
break;
|
break;
|
||||||
case HTTPVerb.put:
|
case HTTPVerb.put:
|
||||||
response = await http.put(requestUrl,
|
response = await http.put(requestUrl, headers: headers, body: body);
|
||||||
headers: headers,
|
|
||||||
body: body);
|
|
||||||
break;
|
break;
|
||||||
case HTTPVerb.patch:
|
case HTTPVerb.patch:
|
||||||
response = await http.patch(requestUrl,
|
response = await http.patch(requestUrl, headers: headers, body: body);
|
||||||
headers: headers,
|
|
||||||
body: body);
|
|
||||||
break;
|
break;
|
||||||
case HTTPVerb.delete:
|
case HTTPVerb.delete:
|
||||||
response = await http.delete(requestUrl,
|
response =
|
||||||
headers: headers,
|
await http.delete(requestUrl, headers: headers, body: body);
|
||||||
body: body);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
stopwatch.stop();
|
stopwatch.stop();
|
||||||
return (response, stopwatch.elapsed, null);
|
return (response, stopwatch.elapsed, null);
|
||||||
}
|
} catch (e) {
|
||||||
catch (e) {
|
|
||||||
return (null, null, e.toString());
|
return (null, null, e.toString());
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
return (null, null, uriRec.$2);
|
||||||
return (null, null, uriRec.$1);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
import '../models/models.dart';
|
||||||
import '../consts.dart';
|
import '../consts.dart';
|
||||||
import 'package:apidash/models/models.dart' show KVRow;
|
|
||||||
|
|
||||||
String humanizeDuration(Duration? duration) {
|
String humanizeDuration(Duration? duration) {
|
||||||
if (duration == null) {
|
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) {
|
String capitalizeFirstLetter(String? text) {
|
||||||
if (text == null || text == "") {
|
if (text == null || text == "") {
|
||||||
return "";
|
return "";
|
||||||
@ -52,30 +60,31 @@ String padMultilineString(String text, int padding,
|
|||||||
return lines.join("\n");
|
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) {
|
if (kvRows == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
Map<String, String> finalMap = {};
|
Map<String, String> finalMap = {};
|
||||||
for (var row in kvRows) {
|
for (var row in kvRows) {
|
||||||
if (row.k.trim() != "") {
|
if (row.name.trim() != "") {
|
||||||
String key = row.k;
|
String key = row.name;
|
||||||
if (isHeader) {
|
if (isHeader) {
|
||||||
key = key.toLowerCase();
|
key = key.toLowerCase();
|
||||||
}
|
}
|
||||||
finalMap[key] = row.v.toString();
|
finalMap[key] = row.value.toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return finalMap;
|
return finalMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<KVRow>? mapToRows(Map<String, String>? kvMap) {
|
List<NameValueModel>? mapToRows(Map<String, String>? kvMap) {
|
||||||
if (kvMap == null) {
|
if (kvMap == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
List<KVRow> finalRows = [];
|
List<NameValueModel> finalRows = [];
|
||||||
for (var k in kvMap.keys) {
|
for (var k in kvMap.keys) {
|
||||||
finalRows.add(KVRow(k, kvMap[k]));
|
finalRows.add(NameValueModel(name: k, value: kvMap[k]));
|
||||||
}
|
}
|
||||||
return finalRows;
|
return finalRows;
|
||||||
}
|
}
|
||||||
@ -89,3 +98,14 @@ Uint8List? stringToBytes(String? text) {
|
|||||||
return bytes;
|
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:collection/collection.dart' show mergeMaps;
|
||||||
import 'package:http_parser/http_parser.dart';
|
import 'package:http_parser/http_parser.dart';
|
||||||
import 'package:xml/xml.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 'convert_utils.dart' show rowsToMap;
|
||||||
import '../consts.dart';
|
import '../consts.dart';
|
||||||
|
|
||||||
@ -44,8 +44,8 @@ MediaType? getMediaTypeFromHeaders(Map? headers) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
(String?, bool) getUriScheme(Uri uri) {
|
(String?, bool) getUriScheme(Uri uri) {
|
||||||
if(uri.hasScheme){
|
if (uri.hasScheme) {
|
||||||
if(kSupportedUriSchemes.contains(uri.scheme)){
|
if (kSupportedUriSchemes.contains(uri.scheme)) {
|
||||||
return (uri.scheme, true);
|
return (uri.scheme, true);
|
||||||
}
|
}
|
||||||
return (uri.scheme, false);
|
return (uri.scheme, false);
|
||||||
@ -54,37 +54,34 @@ MediaType? getMediaTypeFromHeaders(Map? headers) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
(Uri?, String?) getValidRequestUri(
|
(Uri?, String?) getValidRequestUri(
|
||||||
String? url,
|
String? url, List<NameValueModel>? requestParams,
|
||||||
List<KVRow>? requestParams,
|
{String defaultUriScheme = kDefaultUriScheme}) {
|
||||||
{String defaultUriScheme = kDefaultUriScheme}
|
|
||||||
) {
|
|
||||||
url = url?.trim();
|
url = url?.trim();
|
||||||
if(url == null || url == ""){
|
if (url == null || url == "") {
|
||||||
return (null, "URL is missing!");
|
return (null, "URL is missing!");
|
||||||
}
|
}
|
||||||
Uri? uri = Uri.tryParse(url);
|
Uri? uri = Uri.tryParse(url);
|
||||||
if(uri == null){
|
if (uri == null) {
|
||||||
return (null, "Check URL (malformed)");
|
return (null, "Check URL (malformed)");
|
||||||
}
|
}
|
||||||
(String?, bool) urlScheme = getUriScheme(uri);
|
(String?, bool) urlScheme = getUriScheme(uri);
|
||||||
|
|
||||||
if(urlScheme.$0 != null){
|
if (urlScheme.$1 != null) {
|
||||||
if (!urlScheme.$1){
|
if (!urlScheme.$2) {
|
||||||
return (null, "Unsupported URL Scheme (${urlScheme.$0})");
|
return (null, "Unsupported URL Scheme (${urlScheme.$1})");
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
url = "$defaultUriScheme://$url";
|
url = "$defaultUriScheme://$url";
|
||||||
}
|
}
|
||||||
|
|
||||||
uri = Uri.parse(url);
|
uri = Uri.parse(url);
|
||||||
if (uri.hasFragment){
|
if (uri.hasFragment) {
|
||||||
uri = uri.removeFragment();
|
uri = uri.removeFragment();
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, String>? queryParams = rowsToMap(requestParams);
|
Map<String, String>? queryParams = rowsToMap(requestParams);
|
||||||
if(queryParams != null){
|
if (queryParams != null) {
|
||||||
if(uri.hasQuery){
|
if (uri.hasQuery) {
|
||||||
Map<String, String> urlQueryParams = uri.queryParameters;
|
Map<String, String> urlQueryParams = uri.queryParameters;
|
||||||
queryParams = mergeMaps(urlQueryParams, queryParams);
|
queryParams = mergeMaps(urlQueryParams, queryParams);
|
||||||
}
|
}
|
||||||
@ -93,48 +90,58 @@ MediaType? getMediaTypeFromHeaders(Map? headers) {
|
|||||||
return (uri, null);
|
return (uri, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
(List<ResponseBodyView>, String?) getResponseBodyViewOptions(MediaType? mediaType){
|
(List<ResponseBodyView>, String?) getResponseBodyViewOptions(
|
||||||
if(mediaType != null){
|
MediaType? mediaType) {
|
||||||
|
if (mediaType != null) {
|
||||||
var type = mediaType.type;
|
var type = mediaType.type;
|
||||||
var subtype = mediaType.subtype;
|
var subtype = mediaType.subtype;
|
||||||
if(kResponseBodyViewOptions.containsKey(type)){
|
if (kResponseBodyViewOptions.containsKey(type)) {
|
||||||
if (kResponseBodyViewOptions[type]!.containsKey(subtype)){
|
if (kResponseBodyViewOptions[type]!.containsKey(subtype)) {
|
||||||
return (kResponseBodyViewOptions[type]![subtype]!, kCodeHighlighterMap[subtype] ?? subtype);
|
return (
|
||||||
|
kResponseBodyViewOptions[type]![subtype]!,
|
||||||
|
kCodeHighlighterMap[subtype] ?? subtype
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if(subtype.contains(kSubTypeJson)){
|
if (subtype.contains(kSubTypeJson)) {
|
||||||
subtype = kSubTypeJson;
|
subtype = kSubTypeJson;
|
||||||
}
|
}
|
||||||
if(subtype.contains(kSubTypeXml)){
|
if (subtype.contains(kSubTypeXml)) {
|
||||||
subtype = kSubTypeXml;
|
subtype = kSubTypeXml;
|
||||||
}
|
}
|
||||||
if (kResponseBodyViewOptions[type]!.containsKey(subtype)){
|
if (kResponseBodyViewOptions[type]!.containsKey(subtype)) {
|
||||||
return (kResponseBodyViewOptions[type]![subtype]!, kCodeHighlighterMap[subtype] ?? subtype);
|
return (
|
||||||
|
kResponseBodyViewOptions[type]![subtype]!,
|
||||||
|
kCodeHighlighterMap[subtype] ?? subtype
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return (kResponseBodyViewOptions[type]![kSubTypeDefaultViewOptions]!, subtype);
|
return (
|
||||||
|
kResponseBodyViewOptions[type]![kSubTypeDefaultViewOptions]!,
|
||||||
|
subtype
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return (kNoBodyViewOptions, null);
|
return (kNoBodyViewOptions, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
String? formatBody(String? body, MediaType? mediaType){
|
String? formatBody(String? body, MediaType? mediaType) {
|
||||||
if(mediaType != null && body != null){
|
if (mediaType != null && body != null) {
|
||||||
var subtype = mediaType.subtype;
|
var subtype = mediaType.subtype;
|
||||||
try {
|
try {
|
||||||
if(subtype.contains(kSubTypeJson)){
|
if (subtype.contains(kSubTypeJson)) {
|
||||||
final tmp = jsonDecode(body);
|
final tmp = jsonDecode(body);
|
||||||
String result = kEncoder.convert(tmp);
|
String result = kEncoder.convert(tmp);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
if(subtype.contains(kSubTypeXml)){
|
if (subtype.contains(kSubTypeXml)) {
|
||||||
final document = XmlDocument.parse(body);
|
final document = XmlDocument.parse(body);
|
||||||
String result = document.toXmlString(pretty: true, indent: ' ');
|
String result = document.toXmlString(pretty: true, indent: ' ');
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
if(subtype == kSubTypeHtml){
|
if (subtype == kSubTypeHtml) {
|
||||||
var len = body.length;
|
var len = body.length;
|
||||||
var lines = kSplitter.convert(body);
|
var lines = kSplitter.convert(body);
|
||||||
var numOfLines = lines.length;
|
var numOfLines = lines.length;
|
||||||
if(numOfLines !=0 && len/numOfLines <= kCodeCharsPerLineLimit){
|
if (numOfLines != 0 && len / numOfLines <= kCodeCharsPerLineLimit) {
|
||||||
return body;
|
return body;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,3 +2,4 @@ export 'ui_utils.dart';
|
|||||||
export 'convert_utils.dart';
|
export 'convert_utils.dart';
|
||||||
export 'http_utils.dart';
|
export 'http_utils.dart';
|
||||||
export 'file_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,
|
super.key,
|
||||||
this.content,
|
this.content,
|
||||||
this.mimeType,
|
this.mimeType,
|
||||||
|
this.ext,
|
||||||
this.name,
|
this.name,
|
||||||
this.showLabel = true,
|
this.showLabel = true,
|
||||||
});
|
});
|
||||||
|
|
||||||
final Uint8List? content;
|
final Uint8List? content;
|
||||||
final String? mimeType;
|
final String? mimeType;
|
||||||
|
final String? ext;
|
||||||
final String? name;
|
final String? name;
|
||||||
final bool showLabel;
|
final bool showLabel;
|
||||||
|
|
||||||
@ -129,10 +131,9 @@ class _SaveInDownloadsButtonState extends State<SaveInDownloadsButton> {
|
|||||||
onPressed: (widget.content != null)
|
onPressed: (widget.content != null)
|
||||||
? () async {
|
? () async {
|
||||||
var message = "";
|
var message = "";
|
||||||
var ext = getFileExtension(widget.mimeType);
|
|
||||||
var path = await getFileDownloadpath(
|
var path = await getFileDownloadpath(
|
||||||
widget.name,
|
widget.name,
|
||||||
ext,
|
widget.ext ?? getFileExtension(widget.mimeType),
|
||||||
);
|
);
|
||||||
if (path != null) {
|
if (path != null) {
|
||||||
try {
|
try {
|
||||||
|
@ -6,7 +6,7 @@ import 'error_message.dart';
|
|||||||
(String, bool) sanitize(String input) {
|
(String, bool) sanitize(String input) {
|
||||||
bool limitedLines = false;
|
bool limitedLines = false;
|
||||||
int tabSize = 4;
|
int tabSize = 4;
|
||||||
var lines = kSplitter.convert(input);
|
var lines = kSplitter.convert(input);
|
||||||
if (lines.length > kCodePreviewLinesLimit) {
|
if (lines.length > kCodePreviewLinesLimit) {
|
||||||
lines = lines.sublist(0, kCodePreviewLinesLimit);
|
lines = lines.sublist(0, kCodePreviewLinesLimit);
|
||||||
limitedLines = true;
|
limitedLines = true;
|
||||||
@ -67,7 +67,12 @@ class _CodePreviewerState extends State<CodePreviewer> {
|
|||||||
textStyle = textStyle.merge(widget.textStyle);
|
textStyle = textStyle.merge(widget.textStyle);
|
||||||
}
|
}
|
||||||
processed = sanitize(widget.code);
|
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
|
@override
|
||||||
@ -131,12 +136,14 @@ class _CodePreviewerState extends State<CodePreviewer> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<TextSpan>> asyncGenerateSpans(
|
Future<List<TextSpan>> asyncGenerateSpans(String code, String? language,
|
||||||
String code, String? language, Map<String, TextStyle> theme, bool limitedLines) async {
|
Map<String, TextStyle> theme, bool limitedLines) async {
|
||||||
var parsed = highlight.parse(code, language: language);
|
var parsed = highlight.parse(code, language: language);
|
||||||
var spans = convert(parsed.nodes!, theme);
|
var spans = convert(parsed.nodes!, theme);
|
||||||
if(limitedLines) {
|
if (limitedLines) {
|
||||||
spans.add(const TextSpan(text: "\n... more.\nPreview ends here ($kCodePreviewLinesLimit lines).\nYou can check Raw for full result."));
|
spans.add(const TextSpan(
|
||||||
|
text:
|
||||||
|
"\n... more.\nPreview ends here ($kCodePreviewLinesLimit lines).\nYou can check Raw for full result."));
|
||||||
}
|
}
|
||||||
return spans;
|
return spans;
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,9 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:highlighter/highlighter.dart' show highlight;
|
import 'package:highlighter/highlighter.dart' show highlight;
|
||||||
import 'package:apidash/consts.dart';
|
import 'package:apidash/consts.dart';
|
||||||
import 'package:apidash/utils/utils.dart';
|
import 'package:apidash/utils/utils.dart';
|
||||||
import 'code_previewer.dart' show convert;
|
import 'code_previewer.dart';
|
||||||
import 'buttons.dart';
|
import 'widgets.dart'
|
||||||
|
show CopyButton, DropdownButtonCodegenLanguage, SaveInDownloadsButton;
|
||||||
|
|
||||||
class CodeGenPreviewer extends StatefulWidget {
|
class CodeGenPreviewer extends StatefulWidget {
|
||||||
const CodeGenPreviewer({
|
const CodeGenPreviewer({
|
||||||
@ -105,9 +106,13 @@ class ViewCodePane extends StatefulWidget {
|
|||||||
const ViewCodePane({
|
const ViewCodePane({
|
||||||
super.key,
|
super.key,
|
||||||
required this.code,
|
required this.code,
|
||||||
|
required this.codegenLanguage,
|
||||||
|
required this.onChangedCodegenLanguage,
|
||||||
});
|
});
|
||||||
|
|
||||||
final String code;
|
final String code;
|
||||||
|
final CodegenLanguage codegenLanguage;
|
||||||
|
final Function(CodegenLanguage?) onChangedCodegenLanguage;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<ViewCodePane> createState() => _ViewCodePaneState();
|
State<ViewCodePane> createState() => _ViewCodePaneState();
|
||||||
@ -130,44 +135,55 @@ class _ViewCodePaneState extends State<ViewCodePane> {
|
|||||||
borderRadius: kBorderRadius8,
|
borderRadius: kBorderRadius8,
|
||||||
);
|
);
|
||||||
|
|
||||||
return Padding(
|
return LayoutBuilder(
|
||||||
padding: kP10,
|
builder: (BuildContext context, BoxConstraints constraints) {
|
||||||
child: Column(
|
var showLabel = showButtonLabelsInViewCodePane(
|
||||||
children: [
|
constraints.maxWidth,
|
||||||
SizedBox(
|
);
|
||||||
height: kHeaderHeight,
|
return Padding(
|
||||||
child: Row(
|
padding: kP10,
|
||||||
children: [
|
child: Column(
|
||||||
Expanded(
|
children: [
|
||||||
child: Text(
|
SizedBox(
|
||||||
"Code",
|
height: kHeaderHeight,
|
||||||
style: Theme.of(context).textTheme.titleMedium,
|
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/material.dart';
|
||||||
import 'package:flutter/services.dart' show rootBundle;
|
import 'package:flutter/services.dart' show rootBundle;
|
||||||
|
import 'package:apidash/consts.dart';
|
||||||
import 'markdown.dart';
|
import 'markdown.dart';
|
||||||
import 'error_message.dart';
|
import 'error_message.dart';
|
||||||
|
|
||||||
@ -28,7 +29,10 @@ class _IntroMessageState extends State<IntroMessage> {
|
|||||||
} else {
|
} else {
|
||||||
text = text.replaceAll("{{mode}}", "light");
|
text = text.replaceAll("{{mode}}", "light");
|
||||||
}
|
}
|
||||||
return CustomMarkdown(data: text);
|
return CustomMarkdown(
|
||||||
|
data: text,
|
||||||
|
padding: kPh60,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (snapshot.hasError) {
|
if (snapshot.hasError) {
|
||||||
return const ErrorMessage(message: "An error occured");
|
return const ErrorMessage(message: "An error occured");
|
||||||
|
@ -8,8 +8,11 @@ class CustomMarkdown extends StatefulWidget {
|
|||||||
const CustomMarkdown({
|
const CustomMarkdown({
|
||||||
super.key,
|
super.key,
|
||||||
required this.data,
|
required this.data,
|
||||||
|
this.padding = const EdgeInsets.all(16.0),
|
||||||
});
|
});
|
||||||
final String data;
|
final String data;
|
||||||
|
final EdgeInsets padding;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<CustomMarkdown> createState() => _CustomMarkdownState();
|
State<CustomMarkdown> createState() => _CustomMarkdownState();
|
||||||
}
|
}
|
||||||
@ -22,6 +25,7 @@ class _CustomMarkdownState extends State<CustomMarkdown> {
|
|||||||
p: Theme.of(context).textTheme.titleMedium,
|
p: Theme.of(context).textTheme.titleMedium,
|
||||||
);
|
);
|
||||||
return Markdown(
|
return Markdown(
|
||||||
|
padding: widget.padding,
|
||||||
styleSheet: mdStyleSheet,
|
styleSheet: mdStyleSheet,
|
||||||
data: widget.data,
|
data: widget.data,
|
||||||
selectable: true,
|
selectable: true,
|
||||||
|
@ -3,6 +3,8 @@ import 'package:flutter/material.dart';
|
|||||||
import 'error_message.dart';
|
import 'error_message.dart';
|
||||||
import 'package:apidash/consts.dart';
|
import 'package:apidash/consts.dart';
|
||||||
import 'package:printing/printing.dart';
|
import 'package:printing/printing.dart';
|
||||||
|
import 'uint8_audio_player.dart';
|
||||||
|
|
||||||
|
|
||||||
class Previewer extends StatefulWidget {
|
class Previewer extends StatefulWidget {
|
||||||
const Previewer({
|
const Previewer({
|
||||||
@ -43,7 +45,14 @@ class _PreviewerState extends State<Previewer> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (widget.type == kTypeAudio) {
|
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) {
|
if (widget.type == kTypeVideo) {
|
||||||
// TODO: Video Player
|
// TODO: Video Player
|
||||||
|
@ -339,8 +339,8 @@ class _ResponseBodyState extends State<ResponseBody> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var responseBodyView = getResponseBodyViewOptions(mediaType);
|
var responseBodyView = getResponseBodyViewOptions(mediaType);
|
||||||
var options = responseBodyView.$0;
|
var options = responseBodyView.$1;
|
||||||
var highlightLanguage = responseBodyView.$1;
|
var highlightLanguage = responseBodyView.$2;
|
||||||
|
|
||||||
if (formattedBody == null) {
|
if (formattedBody == null) {
|
||||||
options = [...options];
|
options = [...options];
|
||||||
@ -400,18 +400,10 @@ class _BodySuccessState extends State<BodySuccess> {
|
|||||||
|
|
||||||
return LayoutBuilder(
|
return LayoutBuilder(
|
||||||
builder: (BuildContext context, BoxConstraints constraints) {
|
builder: (BuildContext context, BoxConstraints constraints) {
|
||||||
var showLabel = false;
|
var showLabel = showButtonLabelsInBodySuccess(
|
||||||
switch (widget.options.length) {
|
widget.options.length,
|
||||||
case 1:
|
constraints.maxWidth,
|
||||||
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;
|
|
||||||
}
|
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: kP10,
|
padding: kP10,
|
||||||
child: Column(
|
child: Column(
|
||||||
|
@ -76,8 +76,8 @@ class EqualSplitView extends StatefulWidget {
|
|||||||
class _EqualSplitViewState extends State<EqualSplitView> {
|
class _EqualSplitViewState extends State<EqualSplitView> {
|
||||||
final MultiSplitViewController _controller = MultiSplitViewController(
|
final MultiSplitViewController _controller = MultiSplitViewController(
|
||||||
areas: [
|
areas: [
|
||||||
Area(minimalSize: 300),
|
Area(minimalSize: kMinRequestEditorDetailsCardPaneSize),
|
||||||
Area(minimalSize: 300),
|
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 'response_widgets.dart';
|
||||||
export 'snackbars.dart';
|
export 'snackbars.dart';
|
||||||
export 'markdown.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
|
version: 0.2.0+2
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: '>=2.19.2 <3.0.0'
|
sdk: '>=3.0.0 <4.0.0'
|
||||||
|
flutter: '>=3.7.2'
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
multi_split_view: ^2.4.0
|
multi_split_view: ^2.4.0
|
||||||
url_launcher: ^6.1.10
|
url_launcher: ^6.1.12
|
||||||
flutter_riverpod: ^2.1.3
|
flutter_riverpod: ^2.3.7
|
||||||
uuid: ^3.0.7
|
uuid: ^3.0.7
|
||||||
davi: ^3.2.0
|
davi: ^3.4.1
|
||||||
http: ^0.13.5
|
http: ^1.1.0
|
||||||
http_parser: ^4.0.2
|
http_parser: ^4.0.2
|
||||||
collection: ^1.17.0
|
collection: ^1.17.2
|
||||||
google_fonts: ^4.0.3
|
google_fonts: ^5.1.0
|
||||||
highlighter: ^0.1.1
|
highlighter: ^0.1.1
|
||||||
xml: ^6.2.2
|
xml: ^6.3.0
|
||||||
jinja: ^0.4.2
|
jinja: ^0.5.0
|
||||||
window_size:
|
window_size:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/google/flutter-desktop-embedding.git
|
url: https://github.com/google/flutter-desktop-embedding.git
|
||||||
path: plugins/window_size
|
path: plugins/window_size
|
||||||
hive_flutter: ^1.1.0
|
hive_flutter: ^1.1.0
|
||||||
lottie: ^2.3.2
|
lottie: ^2.6.0
|
||||||
mime_dart: ^3.0.0
|
mime_dart: ^3.0.0
|
||||||
path_provider: ^2.0.14
|
path_provider: ^2.1.0
|
||||||
window_manager: ^0.3.2
|
window_manager: ^0.3.5
|
||||||
path: ^1.8.2
|
path: ^1.8.3
|
||||||
flutter_markdown: ^0.6.14
|
flutter_markdown: ^0.6.17+1
|
||||||
markdown: ^7.1.0
|
markdown: ^7.1.1
|
||||||
printing: ^5.10.4
|
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:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
flutter_lints: ^2.0.0
|
flutter_lints: ^2.0.2
|
||||||
flutter_launcher_icons: ^0.12.0
|
flutter_launcher_icons: ^0.13.1
|
||||||
test: ^1.22.0
|
test: ^1.24.3
|
||||||
|
build_runner: ^2.4.6
|
||||||
|
freezed: ^2.4.1
|
||||||
|
json_serializable: ^6.7.1
|
||||||
|
|
||||||
flutter:
|
flutter:
|
||||||
uses-material-design: true
|
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:test/test.dart';
|
||||||
import 'package:apidash/models/kvrow_model.dart';
|
import 'package:apidash/models/name_value_model.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
KVRow kvRow1 = const KVRow("harry", 23);
|
const kvRow1 = NameValueModel(name: "harry", value: 23);
|
||||||
String kvRow1Expected = "{harry: 23}";
|
String kvRow1Expected = "{harry: 23}";
|
||||||
|
|
||||||
test('Testing toString()', () {
|
test('Testing toString()', () {
|
||||||
expect(kvRow1.toString(), kvRow1Expected);
|
expect(kvRow1.toString(), kvRow1Expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
KVRow kvRow2Expected = const KVRow("winter", "26");
|
const kvRow2Expected = NameValueModel(name: "winter", value: "26");
|
||||||
test('Testing copyWith()', () {
|
test('Testing copyWith()', () {
|
||||||
expect(kvRow1.copyWith(k: "winter", v: "26"), kvRow2Expected);
|
expect(kvRow1.copyWith(name: "winter", value: "26"), kvRow2Expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Testing hashcode', () {
|
test('Testing hashcode', () {
|
||||||
|
@ -55,8 +55,9 @@ void main() {
|
|||||||
url: 'api.foss42.com/case/lower',
|
url: 'api.foss42.com/case/lower',
|
||||||
name: 'foss42 api',
|
name: 'foss42 api',
|
||||||
requestHeaders: const [
|
requestHeaders: const [
|
||||||
KVRow('content-length', '18'),
|
NameValueModel(name: 'content-length', value: '18'),
|
||||||
KVRow('content-type', 'application/json; charset=utf-8')
|
NameValueModel(
|
||||||
|
name: 'content-type', value: 'application/json; charset=utf-8')
|
||||||
],
|
],
|
||||||
requestBodyContentType: ContentType.json,
|
requestBodyContentType: ContentType.json,
|
||||||
requestBody: '''{
|
requestBody: '''{
|
||||||
@ -71,8 +72,9 @@ void main() {
|
|||||||
url: 'api.foss42.com/case/lower',
|
url: 'api.foss42.com/case/lower',
|
||||||
name: 'foss42 api',
|
name: 'foss42 api',
|
||||||
requestHeaders: [
|
requestHeaders: [
|
||||||
KVRow('content-length', '18'),
|
NameValueModel(name: 'content-length', value: '18'),
|
||||||
KVRow('content-type', 'application/json; charset=utf-8')
|
NameValueModel(
|
||||||
|
name: 'content-type', value: 'application/json; charset=utf-8')
|
||||||
],
|
],
|
||||||
requestBodyContentType: ContentType.json,
|
requestBodyContentType: ContentType.json,
|
||||||
requestBody: '''{
|
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';
|
import 'dart:math';
|
||||||
|
|
||||||
const _chars = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz1234567890';
|
class RandomStringGenerator {
|
||||||
Random _rnd = Random();
|
static const _chars =
|
||||||
|
'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz1234567890';
|
||||||
|
static Random rnd = Random();
|
||||||
|
|
||||||
String getRandomString(int length) => String.fromCharCodes(Iterable.generate(
|
static String getRandomString(int length) =>
|
||||||
length, (_) => _chars.codeUnitAt(_rnd.nextInt(_chars.length))));
|
String.fromCharCodes(Iterable.generate(
|
||||||
|
length, (_) => _chars.codeUnitAt(rnd.nextInt(_chars.length))));
|
||||||
|
|
||||||
String getRandomStringLines(int lines, int length) {
|
static String getRandomStringLines(int lines, int length) {
|
||||||
List<String> result = [];
|
List<String> result = [];
|
||||||
for (var i = 0; i < lines; i++) {
|
for (var i = 0; i < lines; i++) {
|
||||||
result.add(getRandomString(length));
|
result.add(getRandomString(length));
|
||||||
|
}
|
||||||
|
return result.join('\n');
|
||||||
}
|
}
|
||||||
return result.join('\n');
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import 'package:test/test.dart';
|
import 'package:test/test.dart';
|
||||||
import 'package:apidash/utils/convert_utils.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() {
|
void main() {
|
||||||
group("Testing humanizeDuration function", () {
|
group("Testing humanizeDuration function", () {
|
||||||
@ -73,18 +73,18 @@ void main() {
|
|||||||
expect(rowsToMap(null), null);
|
expect(rowsToMap(null), null);
|
||||||
});
|
});
|
||||||
test('Testing for string KVRow values', () {
|
test('Testing for string KVRow values', () {
|
||||||
KVRow kvRow1 = const KVRow("code", "IN");
|
const kvRow1 = NameValueModel(name: "code", value: "IN");
|
||||||
expect(rowsToMap([kvRow1]), {"code": "IN"});
|
expect(rowsToMap([kvRow1]), {"code": "IN"});
|
||||||
});
|
});
|
||||||
test('Testing when header is True', () {
|
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"});
|
expect(rowsToMap([kvRow2], isHeader: true), {"text": "ABC"});
|
||||||
});
|
});
|
||||||
test('Testing when header is false and key is in upper case', () {
|
test('Testing when header is false and key is in upper case', () {
|
||||||
List<KVRow> kvRow3 = const [
|
const kvRow3 = <NameValueModel>[
|
||||||
KVRow("TEXT", "ABC"),
|
NameValueModel(name: "TEXT", value: "ABC"),
|
||||||
KVRow("version", 0.1),
|
NameValueModel(name: "version", value: 0.1),
|
||||||
KVRow("month", 4)
|
NameValueModel(name: "month", value: 4),
|
||||||
];
|
];
|
||||||
expect(
|
expect(
|
||||||
rowsToMap(kvRow3), {"TEXT": "ABC", "version": "0.1", "month": "4"});
|
rowsToMap(kvRow3), {"TEXT": "ABC", "version": "0.1", "month": "4"});
|
||||||
@ -97,10 +97,10 @@ void main() {
|
|||||||
});
|
});
|
||||||
test('Testing with a map value', () {
|
test('Testing with a map value', () {
|
||||||
Map<String, String> value1 = {"text": "abc", "lang": "eng", "code": "1"};
|
Map<String, String> value1 = {"text": "abc", "lang": "eng", "code": "1"};
|
||||||
List<KVRow> result1Expected = const [
|
const result1Expected = <NameValueModel>[
|
||||||
KVRow("text", "abc"),
|
NameValueModel(name: "text", value: "abc"),
|
||||||
KVRow("lang", "eng"),
|
NameValueModel(name: "lang", value: "eng"),
|
||||||
KVRow("code", "1")
|
NameValueModel(name: "code", value: "1")
|
||||||
];
|
];
|
||||||
expect(mapToRows(value1), result1Expected);
|
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:test/test.dart';
|
||||||
import 'package:http_parser/http_parser.dart';
|
import 'package:http_parser/http_parser.dart';
|
||||||
import 'package:apidash/utils/http_utils.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 'package:apidash/consts.dart';
|
||||||
import '../test_utilities.dart';
|
import '../test_utilities.dart';
|
||||||
|
|
||||||
@ -156,74 +156,74 @@ void main() {
|
|||||||
path: 'guides/libraries/library-tour',
|
path: 'guides/libraries/library-tour',
|
||||||
fragment: 'numbers');
|
fragment: 'numbers');
|
||||||
String uriScheme1Expected = 'https';
|
String uriScheme1Expected = 'https';
|
||||||
expect(getUriScheme(uri1), (uriScheme1Expected,true));
|
expect(getUriScheme(uri1), (uriScheme1Expected, true));
|
||||||
});
|
});
|
||||||
test('Testing getUriScheme for mailto scheme value', () {
|
test('Testing getUriScheme for mailto scheme value', () {
|
||||||
Uri uri2 = Uri(scheme: 'mailto');
|
Uri uri2 = Uri(scheme: 'mailto');
|
||||||
String uriScheme2Expected = 'mailto';
|
String uriScheme2Expected = 'mailto';
|
||||||
expect(getUriScheme(uri2), (uriScheme2Expected,false));
|
expect(getUriScheme(uri2), (uriScheme2Expected, false));
|
||||||
});
|
});
|
||||||
test('Testing getUriScheme for empty scheme value', () {
|
test('Testing getUriScheme for empty scheme value', () {
|
||||||
Uri uri3 = Uri(
|
Uri uri3 = Uri(scheme: '');
|
||||||
scheme: '');
|
expect(getUriScheme(uri3), (null, false));
|
||||||
expect(getUriScheme(uri3), (null,false));
|
|
||||||
});
|
});
|
||||||
test('Testing getUriScheme for null scheme value', () {
|
test('Testing getUriScheme for null scheme value', () {
|
||||||
Uri uri4 = Uri(
|
Uri uri4 = Uri(scheme: null);
|
||||||
scheme: null);
|
expect(getUriScheme(uri4), (null, false));
|
||||||
expect(getUriScheme(uri4), (null,false));
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
group("Testing getValidRequestUri", () {
|
group("Testing getValidRequestUri", () {
|
||||||
test('Testing getValidRequestUri for normal values', () {
|
test('Testing getValidRequestUri for normal values', () {
|
||||||
String url1 = "https://api.foss42.com/country/data";
|
String url1 = "https://api.foss42.com/country/data";
|
||||||
KVRow kvRow1 = const KVRow("code", "US");
|
const kvRow1 = NameValueModel(name: "code", value: "US");
|
||||||
Uri uri1Expected = Uri(
|
Uri uri1Expected = Uri(
|
||||||
scheme: 'https',
|
scheme: 'https',
|
||||||
host: 'api.foss42.com',
|
host: 'api.foss42.com',
|
||||||
path: 'country/data',
|
path: 'country/data',
|
||||||
queryParameters: {'code':'US'});
|
queryParameters: {'code': 'US'});
|
||||||
expect(getValidRequestUri(url1, [kvRow1]), (uri1Expected, null));
|
expect(getValidRequestUri(url1, [kvRow1]), (uri1Expected, null));
|
||||||
});
|
});
|
||||||
test('Testing getValidRequestUri for null url value', () {
|
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!"));
|
expect(getValidRequestUri(null, [kvRow2]), (null, "URL is missing!"));
|
||||||
});
|
});
|
||||||
test('Testing getValidRequestUri for empty url value', () {
|
test('Testing getValidRequestUri for empty url value', () {
|
||||||
KVRow kvRow3 = const KVRow("", "");
|
const kvRow3 = NameValueModel(name: "", value: "");
|
||||||
expect(getValidRequestUri("", [kvRow3]), (null, "URL is missing!"));
|
expect(getValidRequestUri("", [kvRow3]), (null, "URL is missing!"));
|
||||||
});
|
});
|
||||||
test('Testing getValidRequestUri when https is not provided in url', () {
|
test('Testing getValidRequestUri when https is not provided in url', () {
|
||||||
String url4 = "api.foss42.com/country/data";
|
String url4 = "api.foss42.com/country/data";
|
||||||
KVRow kvRow4 = const KVRow("code", "US");
|
const kvRow4 = NameValueModel(name: "code", value: "US");
|
||||||
Uri uri4Expected = Uri(
|
Uri uri4Expected = Uri(
|
||||||
scheme: 'https',
|
scheme: 'https',
|
||||||
host: 'api.foss42.com',
|
host: 'api.foss42.com',
|
||||||
path: 'country/data',
|
path: 'country/data',
|
||||||
queryParameters: {'code':'US'});
|
queryParameters: {'code': 'US'});
|
||||||
expect(getValidRequestUri(url4, [kvRow4]), (uri4Expected, null));
|
expect(getValidRequestUri(url4, [kvRow4]), (uri4Expected, null));
|
||||||
});
|
});
|
||||||
test('Testing getValidRequestUri when url has fragment', () {
|
test('Testing getValidRequestUri when url has fragment', () {
|
||||||
String url5 = "https://dart.dev/guides/libraries/library-tour#numbers";
|
String url5 = "https://dart.dev/guides/libraries/library-tour#numbers";
|
||||||
Uri uri5Expected = Uri(
|
Uri uri5Expected = Uri(
|
||||||
scheme: 'https',
|
scheme: 'https',
|
||||||
host: 'dart.dev',
|
host: 'dart.dev',
|
||||||
path: '/guides/libraries/library-tour');
|
path: '/guides/libraries/library-tour');
|
||||||
expect(getValidRequestUri(url5, null), (uri5Expected, null));
|
expect(getValidRequestUri(url5, null), (uri5Expected, null));
|
||||||
});
|
});
|
||||||
test('Testing getValidRequestUri when uri scheme is not supported', () {
|
test('Testing getValidRequestUri when uri scheme is not supported', () {
|
||||||
String url5 = "mailto:someone@example.com";
|
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";
|
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(
|
Uri uri6Expected = Uri(
|
||||||
scheme: 'https',
|
scheme: 'https',
|
||||||
host: 'api.foss42.com',
|
host: 'api.foss42.com',
|
||||||
path: 'country/data',
|
path: 'country/data',
|
||||||
queryParameters: {'code':'US'});
|
queryParameters: {'code': 'US'});
|
||||||
expect(getValidRequestUri(url6, [kvRow6]), (uri6Expected, null));
|
expect(getValidRequestUri(url6, [kvRow6]), (uri6Expected, null));
|
||||||
});
|
});
|
||||||
test('Testing getValidRequestUri when kvrow is null', () {
|
test('Testing getValidRequestUri when kvrow is null', () {
|
||||||
@ -232,7 +232,7 @@ void main() {
|
|||||||
scheme: 'https',
|
scheme: 'https',
|
||||||
host: 'api.foss42.com',
|
host: 'api.foss42.com',
|
||||||
path: 'country/data',
|
path: 'country/data',
|
||||||
queryParameters: {'code':'US'});
|
queryParameters: {'code': 'US'});
|
||||||
expect(getValidRequestUri(url7, null), (uri7Expected, null));
|
expect(getValidRequestUri(url7, null), (uri7Expected, null));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -241,72 +241,78 @@ void main() {
|
|||||||
test('Testing getResponseBodyViewOptions for application/json', () {
|
test('Testing getResponseBodyViewOptions for application/json', () {
|
||||||
MediaType mediaType1 = MediaType("application", "json");
|
MediaType mediaType1 = MediaType("application", "json");
|
||||||
var result1 = getResponseBodyViewOptions(mediaType1);
|
var result1 = getResponseBodyViewOptions(mediaType1);
|
||||||
expect(result1.$0,kCodeRawBodyViewOptions);
|
expect(result1.$1, kCodeRawBodyViewOptions);
|
||||||
expect(result1.$1, "json");
|
expect(result1.$2, "json");
|
||||||
});
|
});
|
||||||
test('Testing getResponseBodyViewOptions for application/xml', () {
|
test('Testing getResponseBodyViewOptions for application/xml', () {
|
||||||
MediaType mediaType2 = MediaType("application", "xml");
|
MediaType mediaType2 = MediaType("application", "xml");
|
||||||
var result2 = getResponseBodyViewOptions(mediaType2);
|
var result2 = getResponseBodyViewOptions(mediaType2);
|
||||||
expect(result2.$0, kCodeRawBodyViewOptions);
|
expect(result2.$1, kCodeRawBodyViewOptions);
|
||||||
expect(result2.$1,"xml");
|
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");
|
MediaType mediaType3 = MediaType("message", "news");
|
||||||
var result3 = getResponseBodyViewOptions(mediaType3);
|
var result3 = getResponseBodyViewOptions(mediaType3);
|
||||||
expect(result3.$0,kNoBodyViewOptions);
|
expect(result3.$1, kNoBodyViewOptions);
|
||||||
expect(result3.$1,null);
|
expect(result3.$2, null);
|
||||||
});
|
});
|
||||||
test('Testing getResponseBodyViewOptions for application/calendar+json', () {
|
test('Testing getResponseBodyViewOptions for application/calendar+json',
|
||||||
|
() {
|
||||||
MediaType mediaType4 = MediaType("application", "calendar+json");
|
MediaType mediaType4 = MediaType("application", "calendar+json");
|
||||||
var result4 = getResponseBodyViewOptions(mediaType4);
|
var result4 = getResponseBodyViewOptions(mediaType4);
|
||||||
expect(result4.$0,kCodeRawBodyViewOptions);
|
expect(result4.$1, kCodeRawBodyViewOptions);
|
||||||
expect(result4.$1, "json");
|
expect(result4.$2, "json");
|
||||||
});
|
});
|
||||||
test('Testing getResponseBodyViewOptions for image/svg+xml', () {
|
test('Testing getResponseBodyViewOptions for image/svg+xml', () {
|
||||||
MediaType mediaType5 = MediaType("image", "svg+xml");
|
MediaType mediaType5 = MediaType("image", "svg+xml");
|
||||||
var result5 = getResponseBodyViewOptions(mediaType5);
|
var result5 = getResponseBodyViewOptions(mediaType5);
|
||||||
expect(result5.$0,kCodeRawBodyViewOptions);
|
expect(result5.$1, kCodeRawBodyViewOptions);
|
||||||
expect(result5.$1, "xml");
|
expect(result5.$2, "xml");
|
||||||
});
|
});
|
||||||
test('Testing getResponseBodyViewOptions for application/xhtml+xml', () {
|
test('Testing getResponseBodyViewOptions for application/xhtml+xml', () {
|
||||||
MediaType mediaType6 = MediaType("application", "xhtml+xml");
|
MediaType mediaType6 = MediaType("application", "xhtml+xml");
|
||||||
var result6 = getResponseBodyViewOptions(mediaType6);
|
var result6 = getResponseBodyViewOptions(mediaType6);
|
||||||
expect(result6.$0,kCodeRawBodyViewOptions);
|
expect(result6.$1, kCodeRawBodyViewOptions);
|
||||||
expect(result6.$1, "xml");
|
expect(result6.$2, "xml");
|
||||||
});
|
});
|
||||||
test('Testing getResponseBodyViewOptions for application/xml-external-parsed-entity', () {
|
test(
|
||||||
MediaType mediaType7 = MediaType("application", "xml-external-parsed-entity");
|
'Testing getResponseBodyViewOptions for application/xml-external-parsed-entity',
|
||||||
|
() {
|
||||||
|
MediaType mediaType7 =
|
||||||
|
MediaType("application", "xml-external-parsed-entity");
|
||||||
var result7 = getResponseBodyViewOptions(mediaType7);
|
var result7 = getResponseBodyViewOptions(mediaType7);
|
||||||
expect(result7.$0,kCodeRawBodyViewOptions);
|
expect(result7.$1, kCodeRawBodyViewOptions);
|
||||||
expect(result7.$1, "xml");
|
expect(result7.$2, "xml");
|
||||||
});
|
});
|
||||||
test('Testing getResponseBodyViewOptions for text/html', () {
|
test('Testing getResponseBodyViewOptions for text/html', () {
|
||||||
MediaType mediaType8 = MediaType("text", "html");
|
MediaType mediaType8 = MediaType("text", "html");
|
||||||
var result8 = getResponseBodyViewOptions(mediaType8);
|
var result8 = getResponseBodyViewOptions(mediaType8);
|
||||||
expect(result8.$0,kCodeRawBodyViewOptions);
|
expect(result8.$1, kCodeRawBodyViewOptions);
|
||||||
expect(result8.$1, "xml");
|
expect(result8.$2, "xml");
|
||||||
});
|
});
|
||||||
test('Testing getResponseBodyViewOptions for application/pdf', () {
|
test('Testing getResponseBodyViewOptions for application/pdf', () {
|
||||||
MediaType mediaType9 = MediaType("application", "pdf");
|
MediaType mediaType9 = MediaType("application", "pdf");
|
||||||
var result9 = getResponseBodyViewOptions(mediaType9);
|
var result9 = getResponseBodyViewOptions(mediaType9);
|
||||||
expect(result9.$0,kNoBodyViewOptions);
|
expect(result9.$1, kNoBodyViewOptions);
|
||||||
expect(result9.$1, "pdf");
|
expect(result9.$2, "pdf");
|
||||||
});
|
});
|
||||||
test('Testing getResponseBodyViewOptions for text/calendar', () {
|
test('Testing getResponseBodyViewOptions for text/calendar', () {
|
||||||
MediaType mediaType10 = MediaType("text", "calendar");
|
MediaType mediaType10 = MediaType("text", "calendar");
|
||||||
var result10 = getResponseBodyViewOptions(mediaType10);
|
var result10 = getResponseBodyViewOptions(mediaType10);
|
||||||
expect(result10.$0,kRawBodyViewOptions);
|
expect(result10.$1, kRawBodyViewOptions);
|
||||||
expect(result10.$1, "calendar");
|
expect(result10.$2, "calendar");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
group("Testing formatBody", () {
|
group("Testing formatBody", () {
|
||||||
test('Testing formatBody for null values', () {
|
test('Testing formatBody for null values', () {
|
||||||
expect(formatBody(null, null),null);
|
expect(formatBody(null, null), null);
|
||||||
});
|
});
|
||||||
test('Testing formatBody for null body values', () {
|
test('Testing formatBody for null body values', () {
|
||||||
MediaType mediaType1 = MediaType("application", "xml");
|
MediaType mediaType1 = MediaType("application", "xml");
|
||||||
expect(formatBody(null, mediaType1),null);
|
expect(formatBody(null, mediaType1), null);
|
||||||
});
|
});
|
||||||
test('Testing formatBody for null MediaType values', () {
|
test('Testing formatBody for null MediaType values', () {
|
||||||
String body1 = '''
|
String body1 = '''
|
||||||
@ -314,7 +320,7 @@ void main() {
|
|||||||
"text":"The Chosen One";
|
"text":"The Chosen One";
|
||||||
}
|
}
|
||||||
''';
|
''';
|
||||||
expect(formatBody(body1, null),null);
|
expect(formatBody(body1, null), null);
|
||||||
});
|
});
|
||||||
test('Testing formatBody for json subtype values', () {
|
test('Testing formatBody for json subtype values', () {
|
||||||
String body2 = '''{"data":{"area":9831510.0,"population":331893745}}''';
|
String body2 = '''{"data":{"area":9831510.0,"population":331893745}}''';
|
||||||
@ -325,7 +331,7 @@ void main() {
|
|||||||
"population": 331893745
|
"population": 331893745
|
||||||
}
|
}
|
||||||
}''';
|
}''';
|
||||||
expect(formatBody(body2, mediaType2),result2Expected);
|
expect(formatBody(body2, mediaType2), result2Expected);
|
||||||
});
|
});
|
||||||
test('Testing formatBody for xml subtype values', () {
|
test('Testing formatBody for xml subtype values', () {
|
||||||
String body3 = '''
|
String body3 = '''
|
||||||
@ -347,28 +353,32 @@ void main() {
|
|||||||
<calories>650</calories>
|
<calories>650</calories>
|
||||||
</food>
|
</food>
|
||||||
</breakfast_menu>''';
|
</breakfast_menu>''';
|
||||||
expect(formatBody(body3, mediaType3),result3Expected);
|
expect(formatBody(body3, mediaType3), result3Expected);
|
||||||
});
|
});
|
||||||
group("Testing formatBody for html", () {
|
group("Testing formatBody for html", () {
|
||||||
MediaType mediaTypeHtml = MediaType("text", "html");
|
MediaType mediaTypeHtml = MediaType("text", "html");
|
||||||
test('Testing formatBody for html subtype values', () {
|
test('Testing formatBody for html subtype values', () {
|
||||||
String body4 = '''<html>
|
String body4 = '''<html>
|
||||||
<body>
|
<body>
|
||||||
<h1>My First Heading</h1>
|
<h1>My First Heading</h1>
|
||||||
<p>My first paragraph.</p>
|
<p>My first paragraph.</p>
|
||||||
</body>
|
</body>
|
||||||
</html>''';
|
</html>''';
|
||||||
expect(formatBody(body4, mediaTypeHtml),body4);
|
expect(formatBody(body4, mediaTypeHtml), body4);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Testing formatBody for html subtype values with random values', () {
|
test('Testing formatBody for html subtype values with random values', () {
|
||||||
String body5 = '''<html>${getRandomStringLines(100, 10000)}</html>''';
|
String body5 =
|
||||||
expect(formatBody(body5, mediaTypeHtml),null);
|
'''<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>${getRandomStringLines(100, 190)}</html>''';
|
test(
|
||||||
expect(formatBody(body6, mediaTypeHtml),body6);
|
'Testing formatBody for html subtype values with random values within limit',
|
||||||
});
|
() {
|
||||||
|
String body6 =
|
||||||
|
'''<html>${RandomStringGenerator.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',
|
testWidgets('Testing for code previewer when code is of 1000 lines',
|
||||||
(tester) async {
|
(tester) async {
|
||||||
String codeLines = getRandomStringLines(1000, 20);
|
String codeLines = RandomStringGenerator.getRandomStringLines(1000, 20);
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
MaterialApp(
|
MaterialApp(
|
||||||
title: 'Code Previewer',
|
title: 'Code Previewer',
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
|
import 'package:apidash/widgets/widgets.dart'
|
||||||
|
show ViewCodePane, CodeGenPreviewer;
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:apidash/widgets/codegen_previewer.dart';
|
|
||||||
import 'package:apidash/consts.dart';
|
import 'package:apidash/consts.dart';
|
||||||
import '../test_consts.dart';
|
import '../test_consts.dart';
|
||||||
|
|
||||||
@ -58,6 +59,8 @@ void main() async {
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: ViewCodePane(
|
child: ViewCodePane(
|
||||||
code: code,
|
code: code,
|
||||||
|
codegenLanguage: CodegenLanguage.dartHttp,
|
||||||
|
onChangedCodegenLanguage: (p0) {},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -66,7 +69,18 @@ void main() async {
|
|||||||
);
|
);
|
||||||
|
|
||||||
await tester.pumpAndSettle();
|
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),
|
expect(find.textContaining('Error Status Code', findRichText: true),
|
||||||
findsOneWidget);
|
findsOneWidget);
|
||||||
@ -84,6 +98,8 @@ void main() async {
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: ViewCodePane(
|
child: ViewCodePane(
|
||||||
code: code,
|
code: code,
|
||||||
|
codegenLanguage: CodegenLanguage.dartHttp,
|
||||||
|
onChangedCodegenLanguage: (p0) {},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -92,7 +108,7 @@ void main() async {
|
|||||||
);
|
);
|
||||||
|
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
expect(find.text('Code'), findsOneWidget);
|
expect(find.text('Dart (http)'), findsOneWidget);
|
||||||
|
|
||||||
expect(find.textContaining('Error Status Code', findRichText: true),
|
expect(find.textContaining('Error Status Code', findRichText: true),
|
||||||
findsOneWidget);
|
findsOneWidget);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_test/flutter_test.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:apidash/consts.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import '../test_consts.dart';
|
import '../test_consts.dart';
|
||||||
@ -33,9 +33,7 @@ void main() {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(
|
expect(find.byType(Uint8AudioPlayer), findsOneWidget);
|
||||||
find.text("${kMimeTypeRaiseIssueStart}audio/mpeg$kMimeTypeRaiseIssue"),
|
|
||||||
findsOneWidget);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Testing when type/subtype is video/H264', (tester) async {
|
testWidgets('Testing when type/subtype is video/H264', (tester) async {
|
||||||
@ -120,4 +118,21 @@ void main() {
|
|||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
expect(find.text(kImageError), findsOneWidget);
|
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',
|
url: 'api.foss42.com/case/lower',
|
||||||
name: 'foss42 api',
|
name: 'foss42 api',
|
||||||
requestHeaders: [
|
requestHeaders: [
|
||||||
KVRow('content-length', '18'),
|
NameValueModel(name: 'content-length', value: '18'),
|
||||||
KVRow('content-type', 'application/json; charset=utf-8')
|
NameValueModel(
|
||||||
|
name: 'content-type', value: 'application/json; charset=utf-8')
|
||||||
],
|
],
|
||||||
requestBodyContentType: ContentType.json,
|
requestBodyContentType: ContentType.json,
|
||||||
requestBody: '''{
|
requestBody: '''{
|
||||||
|
@ -6,12 +6,12 @@ import 'package:multi_split_view/multi_split_view.dart';
|
|||||||
void main() {
|
void main() {
|
||||||
testWidgets('Testing for Dashboard Splitview', (tester) async {
|
testWidgets('Testing for Dashboard Splitview', (tester) async {
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
MaterialApp(
|
const MaterialApp(
|
||||||
title: 'Dashboard Splitview',
|
title: 'Dashboard Splitview',
|
||||||
home: Scaffold(
|
home: Scaffold(
|
||||||
body: DashboardSplitView(
|
body: DashboardSplitView(
|
||||||
sidebarWidget: Column(children: const [Text("Hello")]),
|
sidebarWidget: Column(children: [Text("Hello")]),
|
||||||
mainWidget: Column(children: const [Text("World")]),
|
mainWidget: Column(children: [Text("World")]),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -23,12 +23,12 @@ void main() {
|
|||||||
});
|
});
|
||||||
testWidgets('Testing for Equal SplitView', (tester) async {
|
testWidgets('Testing for Equal SplitView', (tester) async {
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
MaterialApp(
|
const MaterialApp(
|
||||||
title: 'Equal SplitView',
|
title: 'Equal SplitView',
|
||||||
home: Scaffold(
|
home: Scaffold(
|
||||||
body: EqualSplitView(
|
body: EqualSplitView(
|
||||||
leftWidget: Column(children: const [Text("Hello equal")]),
|
leftWidget: Column(children: [Text("Hello equal")]),
|
||||||
rightWidget: Column(children: const [Text("World equal")]),
|
rightWidget: Column(children: [Text("World equal")]),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -10,8 +10,8 @@ void main() {
|
|||||||
MaterialApp(
|
MaterialApp(
|
||||||
title: 'URL Field',
|
title: 'URL Field',
|
||||||
theme: kThemeDataDark,
|
theme: kThemeDataDark,
|
||||||
home: Scaffold(
|
home: const Scaffold(
|
||||||
body: Column(children: const [URLField(activeId: '2')]),
|
body: Column(children: [URLField(activeId: '2')]),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -30,9 +30,9 @@ void main() {
|
|||||||
MaterialApp(
|
MaterialApp(
|
||||||
title: 'CellField',
|
title: 'CellField',
|
||||||
theme: kThemeDataLight,
|
theme: kThemeDataLight,
|
||||||
home: Scaffold(
|
home: const Scaffold(
|
||||||
body: Column(
|
body: Column(
|
||||||
children: const [
|
children: [
|
||||||
CellField(
|
CellField(
|
||||||
keyId: "4",
|
keyId: "4",
|
||||||
hintText: "Passing some hint text",
|
hintText: "Passing some hint text",
|
||||||
|
Reference in New Issue
Block a user