mirror of
https://github.com/foss42/apidash.git
synced 2025-06-10 23:27:54 +08:00
Merge branch 'main' into mobileDashboardFeature
This commit is contained in:
.gitignoreCHANGELOG.mdCONTRIBUTING.mdINSTALLATION.mdREADME.md
assets
distribute_options.yamllib
app.dart
pubspec.lockpubspec.yamlcodegen
consts.dartmain.dartmodels
kvrow_model.dartmodels.dartname_value_model.dartname_value_model.freezed.dartname_value_model.g.dartrequest_model.dartsettings_model.dart
providers
screens
dashboard.dart
home_page
settings_page.dartservices
utils
widgets
test
codegen
curl_codegen_test.dartdart_http_codegen_test.darthar_codegen_test.dartjs_axios_codegen_test.dartjs_fetch_codegen_test.dartkotlin_okhttp_codegen_test.dartnodejs_axios_codegen_test.dartnodejs_fetch_codegen_test.dartpython_http_client_codegen_test.dartpython_requests_codegen_test.dartrequest_models.dart
models
request_models.darttest_utilities.dartutils
widget_test.dartwidgets
1
.gitignore
vendored
1
.gitignore
vendored
@ -49,6 +49,7 @@ linux/
|
||||
macos/
|
||||
windows/
|
||||
web/
|
||||
ios/
|
||||
.vscode/*
|
||||
icons/
|
||||
coverage/*
|
||||
|
57
CHANGELOG.md
57
CHANGELOG.md
@ -1,5 +1,58 @@
|
||||
# API Dash ⚡️ Changelog
|
||||
|
||||
## v0.3.0 [29-11-2023]
|
||||
|
||||
In this major release, we have migrated the project to Dart 3 & added tons of amazing new features mentioned below:
|
||||
|
||||
1. Create collections and folders to easily organize your APIs.
|
||||
|
||||

|
||||
|
||||
2. Well tested code generators for `cURL`, `HAR`, Python (`requests`, `http.client`), JavaScript (`axios`, `fetch`), node.js (`axios`, `fetch`) & Kotlin (`okhttp3`).
|
||||
|
||||

|
||||
|
||||
3. Interactive JSON Response viewer with a Find Box to easily explore the results.
|
||||
|
||||

|
||||
|
||||
4. Auto-suggestions for the most popularly used headers.
|
||||
|
||||

|
||||
|
||||
5. Export collections & folders into a `HAR` (HTTP Archive) file that can be version controlled & can be directly imported in other API Clients like Postman, Paw, etc. You can also export your entire data via `Settings > Export Data`.
|
||||
|
||||

|
||||

|
||||
|
||||
6. Tab indicators for Request URL Parameters, Headers and Body tabs to quickly identify if they are populated.
|
||||
|
||||

|
||||
|
||||
7. Support for PDF response
|
||||
|
||||

|
||||
|
||||
8. Support for Audio (wav , mp3) response
|
||||
|
||||

|
||||
|
||||
9. Support for APNG response
|
||||
|
||||

|
||||
|
||||
10. Updated Help & Support page.
|
||||
|
||||

|
||||
|
||||
11. Scrollbar added to collection pane which can be switched between being permanently visible or being visible only while scrolling.
|
||||
|
||||

|
||||
|
||||
along with other bug fixes & performance updates.
|
||||
|
||||
A big thank you to these wonderful developers for their contributions in this release: [@aqsasayyed](https://github.com/aqsasayyed), [@mmjsmohit](https://github.com/mmjsmohit), [@Dushant-Bansal](https://github.com/Dushant-Bansal), [@Mixel2004](https://github.com/Mixel2004), [@morpheus-30](https://github.com/morpheus-30) & [@madhupashish](https://github.com/madhupashish)
|
||||
|
||||
## v0.2.0 [05-05-2023]
|
||||
|
||||
The following features were added in this release:
|
||||
@ -39,7 +92,9 @@ Initial release
|
||||
### Features included in v0.1.0
|
||||
|
||||
#### 1. Create & Customize API Requests
|
||||
|
||||
Draft API requests via an easy to use GUI which allows you to:
|
||||
|
||||
- Create different types of HTTP requests (GET, HEAD, POST, PATCH, PUT and DELETE)
|
||||
- Easily manipulate and play around with request inputs like headers, query parameters and body.
|
||||
|
||||
@ -60,7 +115,7 @@ https://user-images.githubusercontent.com/1382619/227082005-7b374f5a-c406-4963-8
|
||||
#### 3. Generate Dart Code Automatically
|
||||
|
||||
API Dash is the **only** open source API client that supports Dart code generation so that you can easily integrate APIs in your Dart/Flutter project.
|
||||
For each request, you can click on **View Code** to directly view the corresponding Dart code which you can then *Copy* and directly run it on DartPad.
|
||||
For each request, you can click on **View Code** to directly view the corresponding Dart code which you can then _Copy_ and directly run it on DartPad.
|
||||
|
||||
**Feature Preview (Video)👇**
|
||||
|
||||
|
@ -4,10 +4,10 @@ We value your participation in this open source project. This page will give you
|
||||
|
||||
You can contribute to the project in any or all of the following ways:
|
||||
|
||||
- [Ask a question](https://github.com/foss42/api-dash/discussions)
|
||||
- [Submit a bug report](https://github.com/foss42/api-dash/issues/new/choose)
|
||||
- [Request a new feature](https://github.com/foss42/api-dash/issues/new/choose)
|
||||
- [Suggest ways to improve the developer experience of an existing feature](https://github.com/foss42/api-dash/issues/new/choose)
|
||||
- [Ask a question](https://github.com/foss42/apidash/discussions)
|
||||
- [Submit a bug report](https://github.com/foss42/apidash/issues/new/choose)
|
||||
- [Request a new feature](https://github.com/foss42/apidash/issues/new/choose)
|
||||
- [Suggest ways to improve the developer experience of an existing feature](https://github.com/foss42/apidash/issues/new/choose)
|
||||
- Add documentation
|
||||
- Add a new feature, resolve an existing issue or add a new test to the project. (Goto [Code Contribution Guidelines](#code-contribution-guidelines)).
|
||||
|
||||
@ -27,11 +27,11 @@ We currently do not accept PRs that involve:
|
||||
|
||||
### Resolving an existing issue / Adding a requested feature
|
||||
|
||||
You can find all existing issues [here](https://github.com/foss42/api-dash/issues). A good place to start is to take a look at ["good first issues"](https://github.com/foss42/api-dash/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22).
|
||||
You can find all existing issues [here](https://github.com/foss42/apidash/issues). A good place to start is to take a look at ["good first issues"](https://github.com/foss42/apidash/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22).
|
||||
|
||||
**Step 1** - Identify the issue you want to work on.
|
||||
**Step 2** - Comment on the issue so that we can discuss how to approach and solve the problem.
|
||||
**Step 3** - Fork the [`foss42/api-dash`](https://github.com/foss42/api-dash) repo to your account.
|
||||
**Step 3** - Fork the [`foss42/apidash`](https://github.com/foss42/apidash) repo to your account.
|
||||
**Step 4** - Create a new branch in your fork and name it `add-feature-xyz` or `resolve-issue-xyz`.
|
||||
**Step 5** - Run API Dash locally (More details [here](#how-to-run-api-dash-locally)).
|
||||
**Step 6** - Make code changes in the branch.
|
||||
@ -41,8 +41,8 @@ You can find all existing issues [here](https://github.com/foss42/api-dash/issue
|
||||
|
||||
### Adding a new feature
|
||||
|
||||
**Step 1** - Open an [issue](https://github.com/foss42/api-dash/issues/new/choose) so that we can discuss on the new feature.
|
||||
**Step 2** - Fork the [`foss42/api-dash`](https://github.com/foss42/api-dash) repo to your account.
|
||||
**Step 1** - Open an [issue](https://github.com/foss42/apidash/issues/new/choose) so that we can discuss on the new feature.
|
||||
**Step 2** - Fork the [`foss42/apidash`](https://github.com/foss42/apidash) repo to your account.
|
||||
**Step 3** - Create a new branch in your fork and name it `add-feature-xyz`.
|
||||
**Step 4** - Run API Dash locally (More details [here](#how-to-run-api-dash-locally)).
|
||||
**Step 5** - Make the necessary code changes required to implement the feature in the branch.
|
||||
@ -61,7 +61,7 @@ You can contribute by adding missing/new tests for:
|
||||
- Services (`lib/services/`).
|
||||
|
||||
**Step 1** - Identify the test you want to add or improve.
|
||||
**Step 2** - Fork the [`foss42/api-dash`](https://github.com/foss42/api-dash) repo to your account.
|
||||
**Step 2** - Fork the [`foss42/apidash`](https://github.com/foss42/apidash) repo to your account.
|
||||
**Step 3** - Create a new branch in your fork and name it `add-test-xyz`.
|
||||
**Step 4** - Add the test to an existing test file or create a new test file in the `test` folder.
|
||||
**Step 5** - Run the tests locally (More details [here](#how-to-run-tests)).
|
||||
@ -72,50 +72,11 @@ You can contribute by adding missing/new tests for:
|
||||
|
||||
### What is the supported Flutter/Dart version?
|
||||
|
||||
As the project has not migrated to Dart 3, the latest Flutter version we support is `3.7.12` (Dart `2.19.6`). If you are using newer flutter version, you will get errors.
|
||||
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 version `3.7.12` (Stable) SDK from the [Flutter SDK Archive](https://docs.flutter.dev/release/archive). Then proceed with the Flutter installation.
|
||||
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 use the instructions below to downgrade/upgrade if you are on any version other than the one mentioned above.
|
||||
|
||||
1. Locate the directory where you have installed Flutter SDK and navigate to it. The contents of the directory should resemble the following:
|
||||
```
|
||||
$ ls
|
||||
analysis_options.yaml CONTRIBUTING.md flutter_root.iml TESTOWNERS
|
||||
AUTHORS dartdoc_options.yaml LICENSE version
|
||||
bin dev packages
|
||||
CODE_OF_CONDUCT.md examples PATENT_GRANT
|
||||
CODEOWNERS flutter_console.bat README.md
|
||||
```
|
||||
|
||||
2. In the same directory, execute the following command to change the head of the local Flutter SDK to version `3.7.12`.
|
||||
```
|
||||
git checkout 4d9e56e
|
||||
```
|
||||
|
||||
3. Run the Flutter Doctor command to verify:
|
||||
```
|
||||
$ flutter doctor -v
|
||||
|
||||
[!] Flutter (Channel unknown, 3.7.12, on Ubuntu 22.04.2 LTS 5.19.0-42-generic,
|
||||
locale en_IN)
|
||||
! Flutter version 3.7.12 on channel unknown at
|
||||
/home/<user>/snap/flutter/common/flutter
|
||||
Currently on an unknown channel. Run `flutter channel` to switch to an
|
||||
official channel.
|
||||
If that doesn't fix the issue, reinstall Flutter by following instructions
|
||||
at https://flutter.dev/docs/get-started/install.
|
||||
! Unknown upstream repository.
|
||||
Reinstall Flutter by following instructions at
|
||||
https://flutter.dev/docs/get-started/install.
|
||||
• Framework revision 4d9e56e694 (5 weeks ago), 2023-04-17 21:47:46 -0400
|
||||
• Engine revision 1a65d409c7
|
||||
• Dart version 2.19.6
|
||||
• DevTools version 2.20.1
|
||||
• If those were intentional, you can disregard the above warnings; however
|
||||
it is recommended to use "git" directly to perform update checks and
|
||||
upgrades.
|
||||
```
|
||||
In case you have already setup Flutter, make sure to switch to `stable` branch and upgrade it.
|
||||
|
||||
### How to run API Dash locally?
|
||||
|
||||
@ -125,16 +86,15 @@ $ flutter doctor -v
|
||||
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
|
||||
```
|
||||
**Note**: In case you encounter an invalid Dart Package name error on your first run, rename the project's folder name from "api-dash" to "apidash" and re-run.
|
||||
|
||||
### How to run tests?
|
||||
|
||||
To run tests execute the following command:
|
||||
|
||||
```
|
||||
flutter test --enable-experiment=records --coverage
|
||||
flutter test --coverage
|
||||
```
|
||||
|
||||
To generate coverage report as html execute:
|
||||
@ -156,13 +116,13 @@ open coverage/html/index.html
|
||||
To run tests specified in a single file, execute the following command:
|
||||
|
||||
```
|
||||
flutter test --enable-experiment=records <file_path>.dart
|
||||
flutter test <file_path>.dart
|
||||
```
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
flutter test --enable-experiment=records test/widgets/codegen_previewer_test.dart
|
||||
flutter test test/widgets/codegen_previewer_test.dart
|
||||
```
|
||||
|
||||
### How to add a new package to pubspec.yaml?
|
||||
|
@ -1,52 +1,52 @@
|
||||
# Installation Instructions
|
||||
|
||||
## Windows
|
||||
Download the latest Windows Installer (64 bit) from [here](https://github.com/foss42/api-dash/releases/latest)
|
||||
Download the latest Windows Installer (64 bit) from [here](https://github.com/foss42/apidash/releases/latest)
|
||||
|
||||
To install it, simply double click on the installer and follow the step by step installation wizard.
|
||||
To install it, simply double click on the installer.
|
||||
|
||||
If prompted by Windows that **Windows prevented an unrecognized app from running**, click on **Run anyway**.
|
||||
|
||||
Now, follow the step by step installation wizard.
|
||||
|
||||
## MacOS
|
||||
|
||||
Download the latest MacOS Installer (Universal - Intel and Apple Silicon) from [here](https://github.com/foss42/api-dash/releases/latest)
|
||||
Download the latest MacOS Installer (Universal - Intel and Apple Silicon) from [here](https://github.com/foss42/apidash/releases/latest)
|
||||
|
||||
**As this app is distributed outside the App Store you have to follow the following instructions to setup and run it only for the first time.**
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
|
||||
This process has to be followed only once and from the next time you can directly launch the API Dash App from the Launchpad.
|
||||
|
||||
##
|
||||
|
||||
You can refer to the video given below which shows the steps to install and run API Dash on macOS.
|
||||
|
||||
https://user-images.githubusercontent.com/1382619/227956871-87376f18-d80f-4a53-9456-cb724f8149c7.mp4
|
||||
|
||||
|
||||
##
|
||||
|
||||
|
||||
**Step 1** - After downloading `api_dash_macos.dmg` file, right click & open it.
|
||||
Note: If you directly open it by double clicking, macOS will warn you and not allow you to open it. So, right click and open it again.
|
||||
|
||||
**Step 2** - Click Open in the dialog box
|
||||
|
||||
**Step 3** - Drag and drop API Dash App in the Applications folder as shown in the video
|
||||
|
||||
**Step 4** - Now go to the Applications folder
|
||||
|
||||
Note: The next step has to be performed twice so that macOS adds the app to whitelist
|
||||
|
||||
**Step 5** - Right click on API Dash App & click Open
|
||||
|
||||
**Step 6** - Click OK
|
||||
|
||||
**Step 7** - Once again right click on API Dash App & then click on Open
|
||||
|
||||
**Step 8** - Now in this new dialog box click on Open to launch the API Dash App
|
||||
|
||||
**Step 9** - The application is now ready for use
|
||||
|
||||
**Step 10** - This process has to be followed only once and from the next time you can directly launch the API Dash App from the Launchpad.
|
||||
|
||||
## Linux
|
||||
|
||||
### Debian-based Linux Distributions (Debian, Ubuntu, Linux Mint, etc.)
|
||||
|
||||
Download the `.deb` file from the [latest release](https://github.com/foss42/api-dash/releases/latest) corresponding to you CPU architecture (x64/amd64 or arm64).
|
||||
Download the `.deb` file from the [latest release](https://github.com/foss42/apidash/releases/latest) corresponding to you CPU architecture (x64/amd64 or arm64).
|
||||
|
||||
`cd` to the Downloads folder and execute the following command to install API Dash.
|
||||
|
||||
@ -64,7 +64,7 @@ Launch API Dash via `apidash` command or by clicking on the API Dash app icon.
|
||||
|
||||
### Red Hat-based Linux Distributions (Fedora, Rocky, AlmaLinux, CentOS, RHEL, etc.)
|
||||
|
||||
Download the `.rpm` file from the [latest release](https://github.com/foss42/api-dash/releases/latest) corresponding to you CPU architecture (x86_64 or aarch64/arm64).
|
||||
Download the `.rpm` file from the [latest release](https://github.com/foss42/apidash/releases/latest) corresponding to you CPU architecture (x86_64 or aarch64/arm64).
|
||||
|
||||
`cd` to the Downloads folder and execute the following command to install API Dash.
|
||||
|
||||
|
147
README.md
147
README.md
@ -6,6 +6,8 @@
|
||||
|
||||
API Dash is a beautiful open-source cross-platform API Client that can help you easily create & customize your API requests, visually inspect responses and generate Dart code on the go.
|
||||
|
||||

|
||||
|
||||
## Download
|
||||
API Dash can be downloaded from the links below:
|
||||
|
||||
@ -23,94 +25,173 @@ API Dash can be downloaded from the links below:
|
||||
<tr>
|
||||
<td>macOS</td>
|
||||
<td><code>.dmg</code></td>
|
||||
<td><a href="https://github.com/foss42/api-dash/blob/main/INSTALLATION.md#macos">Link</a></td>
|
||||
<td><a href="https://github.com/foss42/apidash/blob/main/INSTALLATION.md#macos">Link</a></td>
|
||||
<td>Apple Silicon & Intel</td>
|
||||
<td><a href="https://bit.ly/44wmazf">Link</a></td>
|
||||
<td><a href="https://github.com/foss42/apidash/releases/latest/download/apidash-macos.dmg">Link</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Windows</td>
|
||||
<td><code>.exe</code></td>
|
||||
<td><a href="https://github.com/foss42/api-dash/blob/main/INSTALLATION.md#windows">Link</a></td>
|
||||
<td><a href="https://github.com/foss42/apidash/blob/main/INSTALLATION.md#windows">Link</a></td>
|
||||
<td>64-bit</td>
|
||||
<td><a href="https://bit.ly/424ExKb">Link</a></td>
|
||||
<td><a href="https://github.com/foss42/apidash/releases/latest/download/apidash-windows-x86_64.exe">Link</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td rowspan=4>Linux</td>
|
||||
<td rowspan=5>Linux</td>
|
||||
<td rowspan=2><code>.deb</code></td>
|
||||
<td rowspan=2><a href="https://github.com/foss42/api-dash/blob/main/INSTALLATION.md#debian-based-linux-distributions-debian-ubuntu-linux-mint-etc">Link</a></td>
|
||||
<td rowspan=2><a href="https://github.com/foss42/apidash/blob/main/INSTALLATION.md#debian-based-linux-distributions-debian-ubuntu-linux-mint-etc">Link</a></td>
|
||||
<td>amd64</td>
|
||||
<td><a href="https://bit.ly/44sWPq2">Link</a></td>
|
||||
<td><a href="https://github.com/foss42/apidash/releases/latest/download/apidash-linux-amd64.deb">Link</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>arm64</td>
|
||||
<td><a href="https://bit.ly/3pdVgvP">Link</a></td>
|
||||
<td><a href="https://github.com/foss42/apidash/releases/latest/download/apidash-linux-arm64.deb">Link</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td rowspan=2><code>.rpm</code></td>
|
||||
<td rowspan=2><a href="https://github.com/foss42/api-dash/blob/main/INSTALLATION.md#red-hat-based-linux-distributions-fedora-rocky-almalinux-centos-rhel-etc">Link</a></td>
|
||||
<td rowspan=2><a href="https://github.com/foss42/apidash/blob/main/INSTALLATION.md#red-hat-based-linux-distributions-fedora-rocky-almalinux-centos-rhel-etc">Link</a></td>
|
||||
<td>x86_64</td>
|
||||
<td><a href="https://bit.ly/417gWHe">Link</a></td>
|
||||
<td><a href="https://github.com/foss42/apidash/releases/latest/download/apidash-linux-x86_64.rpm">Link</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>aarch64</td>
|
||||
<td><a href="https://bit.ly/3LWdJWV">Link</a></td>
|
||||
<td><a href="https://github.com/foss42/apidash/releases/latest/download/apidash-linux-aarch64.rpm">Link</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>PKGBUILD</code> (Arch Linux)</td>
|
||||
<td><a href="https://aur.archlinux.org/packages/apidash-bin">Link</a></td>
|
||||
<td>x86_64</td>
|
||||
<td><a href="https://aur.archlinux.org/packages/apidash-bin">Link</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
## A Quick Glimpse of API Dash ⚡️ (Demo Video)
|
||||
|
||||
Demo Video on Youtube - [Link](https://youtu.be/IQlrgpNpS2s) (In case there is an error loading the embedded video below 👇)
|
||||
|
||||
https://github.com/foss42/api-dash/assets/615622/fccc569e-3152-47be-9f94-ceb851ee85a0
|
||||
|
||||
## List of Features
|
||||
|
||||
**↗️ Create & Customize API Requests**
|
||||
- Create different types of HTTP requests (`GET`, `HEAD`, `POST`, `PATCH`, `PUT` and `DELETE`)
|
||||
- Create different types of HTTP requests (`GET`, `HEAD`, `POST`, `PATCH`, `PUT` and `DELETE`).
|
||||
- Easily manipulate and play around with request inputs like `headers`, `query parameters` and `body`.
|
||||
- Full support to send text content with 🥳 Unicode/Emoji and preview any API response containing Unicode/Emoji.
|
||||
|
||||
**🔎 Visually Inspect and Download API Responses**
|
||||
- Inspect the API Response (HTTP status code, error message, headers, body, time taken)
|
||||
**💼 Organize Requests in Collections & Folders**
|
||||
- Create collections and folders to organize your requests.
|
||||
- Press and Drag to Re-arrange requests.
|
||||
- Click and open popup menu to rename, duplicate and delete a request.
|
||||
|
||||
**🔎 Visually Preview and Download Data & Multimedia API Responses**
|
||||
- Inspect the API Response (HTTP status code, error message, headers, body, time taken).
|
||||
- View formatted code previews for responses of various content types like `JSON`, `XML`, `YAML`, `HTML`, `SQL`, etc.
|
||||
- Supports directly previewing these a wide variety of 🖼 image file formats such as `jpeg`, `png`, `gif`, etc.
|
||||
- API Dash helps explore, test & preview Multimedia API responses which is **not supported by any other API client**. You can directly test APIs that return images, PDF, audio & more. Check out the [full list of supported mimetypes/formats here](https://github.com/foss42/apidash#mime-types-supported-by-api-dash-response-previewer).
|
||||
- Save 💾 response body of any mimetype (`image`, `text`, etc.) directly in the `Downloads` folder of your system by clicking on the `Download` button.
|
||||
|
||||
**👩🏻💻 Code Generation**
|
||||
- The **only** open source API client that supports advanced Dart code generation so that you can easily integrate APIs in your Dart/Flutter project or directly run it on DartPad.
|
||||
- We started out as the **only** open source API client that supports advanced Dart code generation so that you can easily integrate APIs in your Dart/Flutter project or directly run it on DartPad. But, now API Dash supports generation of well-tested integration codes for **JavaScript**, **Python**, **Kotlin** & various other languages. You can check out the [full list of supported languages/libraries](https://github.com/foss42/apidash#code-generators).
|
||||
|
||||
**🌙 Full Dark Mode Support**
|
||||
- Easily switch between light mode and dark mode.
|
||||
|
||||
**💼 Organize Requests in Collection Pane**
|
||||
- Press and Drag to Re-arrange requests.
|
||||
- Double-click to rename requests.
|
||||
- Click and open popup menu to duplicate and delete a request.
|
||||
|
||||
**💾 Data**
|
||||
- Data is persisted locally on the disk. To save the current snapshot, just press the **Save** button in the collection pane.
|
||||
- Click and open the collection/folder popup menu to export it as HAR. This can be version controlled & can be directly imported in other API Clients like Postman, Paw, etc.
|
||||
- Export your entire data into a HAR (HTTP Archive) file. To access this option goto `Settings > Export Data`.
|
||||
|
||||
**⚙️ Settings & Other Options**
|
||||
- Customize various options using a dedicated Settings screen.
|
||||
- Window Configuration (Size & Position) is persisted and restored on app start. (Only macOS & Windows)
|
||||
|
||||
## What's new in v0.2.0?
|
||||
## Code Generators
|
||||
|
||||
API Dash currently supports API integration code generation for the following languages/libraries.
|
||||
|
||||
|Language|Library|
|
||||
|--|--|
|
||||
| cURL | |
|
||||
| HAR | |
|
||||
| Dart | `http` |
|
||||
| JavaScript | `axios` |
|
||||
| JavaScript | `fetch` |
|
||||
| JavaScript (`node.js`) | `axios` |
|
||||
| JavaScript (`node.js`) | `fetch` |
|
||||
| Python | `http.client` |
|
||||
| Python | `requests` |
|
||||
| Kotlin | `okhttp3` |
|
||||
|
||||
We welcome contributions to support other programming languages/libraries/frameworks. Please check out more details [here](https://github.com/foss42/apidash/discussions/80).
|
||||
|
||||
## MIME Types supported by API Dash Response Previewer
|
||||
|
||||
API Dash is a next-gen API client that supports exploring, testing & previewing various data & multimedia API responses which is limited/not supported by other API clients. You can directly test APIs that return images, PDF, audio & more.
|
||||
|
||||
Here is the complete list of mimetypes that can be directly previewed in API Dash:
|
||||
|
||||
|File Type|Mimetype|Extension|Comment|
|
||||
|--|--|--|--|
|
||||
| PDF | `application/pdf` | `.pdf` | |
|
||||
| Image | `image/apng` | `.apng` | Animated |
|
||||
| Image | `image/avif` | `.avif` | |
|
||||
| Image | `image/bmp` | `.bmp` | |
|
||||
| Image | `image/gif` | `.gif` | Animated |
|
||||
| Image | `image/jpeg` | `.jpeg` or `.jpg`| |
|
||||
| Image | `image/jp2` | `.jp2` | |
|
||||
| Image | `image/jpx` | `.jpf` or `.jpx` | |
|
||||
| Image | `image/pict` | `.pct` | |
|
||||
| Image | `image/portable-anymap` | `.pnm` | |
|
||||
| Image | `image/png` | `.png` | |
|
||||
| Image | `image/sgi` | `.sgi` | |
|
||||
| Image | `image/svg+xml` | `.svg` | Partial support. See issue https://github.com/foss42/apidash/issues/20 |
|
||||
| Image | `image/tiff` | `.tiff` | |
|
||||
| Image | `image/targa` | `.tga` | |
|
||||
| Image | `image/vnd.wap.wbmp` | `.wbmp` | |
|
||||
| Image | `image/webp` | `.webp` | |
|
||||
| Image | `image/xwindowdump` | `.xwd` | |
|
||||
| Image | `image/x-icon` | `.ico` | |
|
||||
| Image | `image/x-portable-anymap` | `.pnm` | |
|
||||
| Image | `image/x-portable-bitmap` | `.pbm` | |
|
||||
| Image | `image/x-portable-graymap` | `.pgm` | |
|
||||
| Image | `image/x-portable-pixmap` | `.ppm` | |
|
||||
| Image | `image/x-tga` | `.tga` | |
|
||||
| Image | `image/x-xwindowdump` | `.xwd` | |
|
||||
| Audio | `audio/flac` | `.flac` | |
|
||||
| Audio | `audio/mpeg` | `.mp3` | |
|
||||
| Audio | `audio/mp4` | `.m4a` or `.mp4a` | |
|
||||
| Audio | `audio/x-m4a` | `.m4a` | |
|
||||
| Audio | `audio/wav` | `.wav` | |
|
||||
| Audio | `audio/wave` | `.wav` | |
|
||||
|
||||
We welcome PRs to add support for previewing other multimedia mimetypes. Please go ahead and raise an issue so that we can discuss the approach.
|
||||
We are adding support for other mimetypes with each release. But, if you are looking for any particular mimetype support, please go ahead and open an issue. We will prioritize it's addition.
|
||||
|
||||
Here is the complete list of mimetypes that are syntax highlighted in API Dash:
|
||||
|
||||
|Mimetype|Extension|Comment|
|
||||
|--|--|--|
|
||||
| `application/json` | `.json` |Other mimetypes like `application/geo+json`, `application/vcard+json` that are based on `json` are also supported.|
|
||||
| `application/xml` | `.xml` |Other mimetypes like `application/xhtml+xml`, `application/vcard+xml` that are based on `xml` are also supported.|
|
||||
| `text/xml` | `.xml` ||
|
||||
| `application/yaml` | `.yaml` | Others - `application/x-yaml` or `application/x-yml` |
|
||||
| `text/yaml` | `.yaml` | Others - `text/yml`|
|
||||
| `application/sql` | `.sql` | |
|
||||
| `text/css` | `.css` | |
|
||||
| `text/html` | `.html` | Only syntax highlighting, no web preview. |
|
||||
| `text/javascript` | `.js` | |
|
||||
| `text/markdown` | `.md` | |
|
||||
|
||||
## What's new in v0.3.0?
|
||||
|
||||
Visit [CHANGELOG.md](CHANGELOG.md)
|
||||
|
||||
## Provide Feedback, Report Bugs & Request New Features
|
||||
|
||||
Just click on the [Issue tab](https://github.com/foss42/api-dash/issues) to raise a new issue in this repo.
|
||||
Just click on the [Issue tab](https://github.com/foss42/apidash/issues) to raise a new issue in this repo.
|
||||
|
||||
## Contribute to API Dash
|
||||
|
||||
You can contribute to API Dash in any or all of the following ways:
|
||||
|
||||
- [Ask a question](https://github.com/foss42/api-dash/discussions)
|
||||
- [Submit a bug report](https://github.com/foss42/api-dash/issues/new/choose)
|
||||
- [Request a new feature](https://github.com/foss42/api-dash/issues/new/choose)
|
||||
- [Suggest ways to improve the developer experience of an existing feature](https://github.com/foss42/api-dash/issues/new/choose)
|
||||
- [Ask a question](https://github.com/foss42/apidash/discussions)
|
||||
- [Submit a bug report](https://github.com/foss42/apidash/issues/new/choose)
|
||||
- [Request a new feature](https://github.com/foss42/apidash/issues/new/choose)
|
||||
- [Suggest ways to improve the developer experience of an existing feature](https://github.com/foss42/apidash/issues/new/choose)
|
||||
- Add documentation
|
||||
- To add a new feature, resolve an existing issue or add a new test to the project, check out our [Contribution Guidelines](CONTRIBUTING.md).
|
||||
|
||||
|
@ -6,41 +6,18 @@
|
||||
|
||||
#br
|
||||
|
||||
Using API Dash, you can easily create & customize your API requests, visually inspect responses and generate Dart code on the go.
|
||||
Using API Dash, you can easily create & customize your API requests, visually inspect responses and generate API integration code on the go.
|
||||
|
||||
**Please support this project by giving us a** ~`Star on GitHub`~
|
||||
|
||||
#br
|
||||
|
||||
## What's new in this release (v0.2.0)?
|
||||
## Release Details
|
||||
|
||||
#br
|
||||
|
||||
1. A brand new UI with Settings.
|
||||
|
||||

|
||||
|
||||
2. Dark Mode Support
|
||||
|
||||

|
||||
|
||||
3. You can now rename any request. Just double-click on it and enter the name.
|
||||
|
||||

|
||||
|
||||
4. Emoji support across the app. You can now easily send text content with emojis and preview any API response containing emojis.
|
||||
|
||||

|
||||
|
||||
5. You can now save response body of any mimetype (image, text, etc.) directly in the Downloads folder by clicking on the Download button.
|
||||
|
||||

|
||||
|
||||
6. Window size and position is persisted and the configuration is restored on app start.
|
||||
7. Notification on save, download and any other user action (UX improvement).
|
||||
8. Linux builds are now available for API Dash (.deb & .rpm)
|
||||
|
||||
.. and various bug fixes & performance improvements. View full changelog [here](https://github.com/foss42/api-dash/blob/main/CHANGELOG.md).
|
||||
Version: {{version}}
|
||||
Read the complete change-log [here](https://github.com/foss42/apidash/blob/main/CHANGELOG.md).
|
||||
|
||||
#br
|
||||
#br
|
||||
|
@ -6,12 +6,7 @@ releases:
|
||||
package:
|
||||
platform: linux
|
||||
target: deb
|
||||
build_args:
|
||||
enable-experiment: records
|
||||
- name: release-dev-linux-rpm
|
||||
package:
|
||||
platform: linux
|
||||
target: rpm
|
||||
build_args:
|
||||
enable-experiment: records
|
||||
|
||||
|
@ -4,7 +4,7 @@ import 'package:window_manager/window_manager.dart';
|
||||
import 'providers/providers.dart';
|
||||
import 'screens/screens.dart';
|
||||
import 'consts.dart'
|
||||
show kFontFamily, kFontFamilyFallback, kColorSchemeSeed, kIsMobile;
|
||||
show kIsLinux, kIsMobile, kFontFamily, kFontFamilyFallback, kColorSchemeSeed;
|
||||
|
||||
class App extends ConsumerStatefulWidget {
|
||||
const App({super.key});
|
||||
@ -39,7 +39,7 @@ class _AppState extends ConsumerState<App> with WindowListener {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return const DashApp();
|
||||
return const Dashboard();
|
||||
}
|
||||
|
||||
@override
|
||||
@ -84,7 +84,7 @@ class _DashAppState extends ConsumerState<DashApp> {
|
||||
title: 'Requests',
|
||||
scaffoldBody: CollectionPane(),
|
||||
)
|
||||
: const Dashboard(),
|
||||
: kIsLinux ? const Dashboard() : const App(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,13 @@
|
||||
import 'package:apidash/codegen/kotlin/pkg_okhttp.dart';
|
||||
import 'package:apidash/consts.dart';
|
||||
|
||||
import 'package:apidash/models/models.dart' show RequestModel;
|
||||
import 'dart/pkg_http.dart';
|
||||
import 'package:apidash/consts.dart';
|
||||
import 'dart/http.dart';
|
||||
import 'kotlin/okhttp.dart';
|
||||
import 'python/http_client.dart';
|
||||
import 'python/requests.dart';
|
||||
import 'js/axios.dart';
|
||||
import 'js/fetch.dart';
|
||||
import 'others/har.dart';
|
||||
import 'others/curl.dart';
|
||||
|
||||
class Codegen {
|
||||
String? getCode(
|
||||
@ -11,10 +16,29 @@ class Codegen {
|
||||
String defaultUriScheme,
|
||||
) {
|
||||
switch (codegenLanguage) {
|
||||
case CodegenLanguage.curl:
|
||||
return cURLCodeGen().getCode(requestModel, defaultUriScheme);
|
||||
case CodegenLanguage.har:
|
||||
return HARCodeGen().getCode(requestModel, defaultUriScheme);
|
||||
case CodegenLanguage.dartHttp:
|
||||
return DartHttpCodeGen().getCode(requestModel, defaultUriScheme);
|
||||
case CodegenLanguage.jsAxios:
|
||||
return AxiosCodeGen().getCode(requestModel, defaultUriScheme);
|
||||
case CodegenLanguage.jsFetch:
|
||||
return FetchCodeGen().getCode(requestModel, defaultUriScheme);
|
||||
case CodegenLanguage.nodejsAxios:
|
||||
return AxiosCodeGen(isNodeJs: true)
|
||||
.getCode(requestModel, defaultUriScheme);
|
||||
case CodegenLanguage.nodejsFetch:
|
||||
return FetchCodeGen(isNodeJs: true)
|
||||
.getCode(requestModel, defaultUriScheme);
|
||||
case CodegenLanguage.kotlinOkHttp:
|
||||
return KotlinOkHttpCodeGen().getCode(requestModel);
|
||||
return KotlinOkHttpCodeGen().getCode(requestModel, defaultUriScheme);
|
||||
case CodegenLanguage.pythonHttpClient:
|
||||
return PythonHttpClientCodeGen()
|
||||
.getCode(requestModel, defaultUriScheme);
|
||||
case CodegenLanguage.pythonRequests:
|
||||
return PythonRequestsCodeGen().getCode(requestModel, defaultUriScheme);
|
||||
default:
|
||||
throw ArgumentError('Invalid codegenLanguage');
|
||||
}
|
||||
|
@ -2,11 +2,11 @@ import 'dart:io';
|
||||
import 'dart:convert';
|
||||
import 'package:jinja/jinja.dart' as jj;
|
||||
import 'package:apidash/consts.dart';
|
||||
import 'package:apidash/utils/utils.dart' show padMultilineString, rowsToMap;
|
||||
import 'package:apidash/utils/utils.dart' show padMultilineString;
|
||||
import 'package:apidash/models/models.dart' show RequestModel;
|
||||
|
||||
class DartHttpCodeGen {
|
||||
String kTemplateUrl = """import 'package:http/http.dart' as http;
|
||||
String kTemplateStart = """import 'package:http/http.dart' as http;
|
||||
|
||||
void main() async {
|
||||
var uri = Uri.parse('{{url}}');
|
||||
@ -80,12 +80,12 @@ void main() async {
|
||||
if (!url.contains("://") && url.isNotEmpty) {
|
||||
url = "$defaultUriScheme://$url";
|
||||
}
|
||||
var templateUrl = jj.Template(kTemplateUrl);
|
||||
result += templateUrl.render({"url": url});
|
||||
var templateStartUrl = jj.Template(kTemplateStart);
|
||||
result += templateStartUrl.render({"url": url});
|
||||
|
||||
var paramsList = requestModel.requestParams;
|
||||
if (paramsList != null) {
|
||||
var params = rowsToMap(requestModel.requestParams) ?? {};
|
||||
var params = requestModel.paramsMap;
|
||||
if (params.isNotEmpty) {
|
||||
var templateParams = jj.Template(kTemplateParams);
|
||||
var paramsString = kEncoder.convert(params);
|
||||
@ -113,7 +113,7 @@ void main() async {
|
||||
|
||||
var headersList = requestModel.requestHeaders;
|
||||
if (headersList != null || hasBody) {
|
||||
var headers = rowsToMap(requestModel.requestHeaders) ?? {};
|
||||
var headers = requestModel.headersMap;
|
||||
if (headers.isNotEmpty || hasBody) {
|
||||
hasHeaders = true;
|
||||
if (hasBody) {
|
104
lib/codegen/js/axios.dart
Normal file
104
lib/codegen/js/axios.dart
Normal file
@ -0,0 +1,104 @@
|
||||
import 'package:apidash/consts.dart';
|
||||
import 'package:jinja/jinja.dart' as jj;
|
||||
import 'package:apidash/utils/utils.dart'
|
||||
show requestModelToHARJsonRequest, padMultilineString, stripUrlParams;
|
||||
import 'package:apidash/models/models.dart' show RequestModel;
|
||||
|
||||
class AxiosCodeGen {
|
||||
AxiosCodeGen({this.isNodeJs = false});
|
||||
|
||||
final bool isNodeJs;
|
||||
|
||||
String kStringImportNode = """import axios from 'axios';
|
||||
|
||||
""";
|
||||
|
||||
String kTemplateStart = """let config = {
|
||||
url: '{{url}}',
|
||||
method: '{{method}}'
|
||||
""";
|
||||
|
||||
String kTemplateParams = """,
|
||||
params: {{params}}
|
||||
""";
|
||||
|
||||
String kTemplateHeader = """,
|
||||
headers: {{headers}}
|
||||
""";
|
||||
|
||||
String kTemplateBody = """,
|
||||
data: {{body}}
|
||||
""";
|
||||
|
||||
String kStringRequest = """
|
||||
|
||||
};
|
||||
|
||||
axios(config)
|
||||
.then(function (response) {
|
||||
// handle success
|
||||
console.log(response.status);
|
||||
console.log(response.data);
|
||||
})
|
||||
.catch(function (error) {
|
||||
// handle error
|
||||
console.log(error.response.status);
|
||||
console.log(error);
|
||||
});
|
||||
""";
|
||||
|
||||
String? getCode(
|
||||
RequestModel requestModel,
|
||||
String defaultUriScheme,
|
||||
) {
|
||||
try {
|
||||
String result = isNodeJs ? kStringImportNode : "";
|
||||
|
||||
String url = requestModel.url;
|
||||
if (!url.contains("://") && url.isNotEmpty) {
|
||||
url = "$defaultUriScheme://$url";
|
||||
}
|
||||
var rM = requestModel.copyWith(url: url);
|
||||
|
||||
var harJson = requestModelToHARJsonRequest(rM);
|
||||
|
||||
var templateStart = jj.Template(kTemplateStart);
|
||||
result += templateStart.render({
|
||||
"url": stripUrlParams(url),
|
||||
"method": harJson["method"].toLowerCase(),
|
||||
});
|
||||
|
||||
var params = harJson["queryString"];
|
||||
if (params.isNotEmpty) {
|
||||
var templateParams = jj.Template(kTemplateParams);
|
||||
var m = {};
|
||||
for (var i in params) {
|
||||
m[i["name"]] = i["value"];
|
||||
}
|
||||
result += templateParams
|
||||
.render({"params": padMultilineString(kEncoder.convert(m), 2)});
|
||||
}
|
||||
|
||||
var headers = harJson["headers"];
|
||||
if (headers.isNotEmpty) {
|
||||
var templateHeader = jj.Template(kTemplateHeader);
|
||||
var m = {};
|
||||
for (var i in headers) {
|
||||
m[i["name"]] = i["value"];
|
||||
}
|
||||
result += templateHeader
|
||||
.render({"headers": padMultilineString(kEncoder.convert(m), 2)});
|
||||
}
|
||||
|
||||
if (harJson["postData"]?["text"] != null) {
|
||||
var templateBody = jj.Template(kTemplateBody);
|
||||
result += templateBody
|
||||
.render({"body": kEncoder.convert(harJson["postData"]["text"])});
|
||||
}
|
||||
result += kStringRequest;
|
||||
return result;
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
94
lib/codegen/js/fetch.dart
Normal file
94
lib/codegen/js/fetch.dart
Normal file
@ -0,0 +1,94 @@
|
||||
import 'package:apidash/consts.dart';
|
||||
import 'package:jinja/jinja.dart' as jj;
|
||||
import 'package:apidash/utils/utils.dart'
|
||||
show requestModelToHARJsonRequest, padMultilineString;
|
||||
import 'package:apidash/models/models.dart' show RequestModel;
|
||||
|
||||
class FetchCodeGen {
|
||||
FetchCodeGen({this.isNodeJs = false});
|
||||
|
||||
final bool isNodeJs;
|
||||
|
||||
String kStringImportNode = """import fetch from 'node-fetch';
|
||||
|
||||
""";
|
||||
|
||||
String kTemplateStart = """let url = '{{url}}';
|
||||
|
||||
let options = {
|
||||
method: '{{method}}'
|
||||
""";
|
||||
|
||||
String kTemplateHeader = """,
|
||||
headers: {{headers}}
|
||||
""";
|
||||
|
||||
String kTemplateBody = """,
|
||||
body:
|
||||
{{body}}
|
||||
""";
|
||||
|
||||
String kStringRequest = """
|
||||
|
||||
};
|
||||
|
||||
let status;
|
||||
fetch(url, options)
|
||||
.then(res => {
|
||||
status = res.status;
|
||||
return res.json()
|
||||
})
|
||||
.then(body => {
|
||||
console.log(status);
|
||||
console.log(body);
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(status);
|
||||
console.error('error:' + err);
|
||||
});
|
||||
""";
|
||||
|
||||
String? getCode(
|
||||
RequestModel requestModel,
|
||||
String defaultUriScheme,
|
||||
) {
|
||||
try {
|
||||
String result = isNodeJs ? kStringImportNode : "";
|
||||
|
||||
String url = requestModel.url;
|
||||
if (!url.contains("://") && url.isNotEmpty) {
|
||||
url = "$defaultUriScheme://$url";
|
||||
}
|
||||
var rM = requestModel.copyWith(url: url);
|
||||
|
||||
var harJson = requestModelToHARJsonRequest(rM);
|
||||
|
||||
var templateStart = jj.Template(kTemplateStart);
|
||||
result += templateStart.render({
|
||||
"url": harJson["url"],
|
||||
"method": harJson["method"],
|
||||
});
|
||||
|
||||
var headers = harJson["headers"];
|
||||
if (headers.isNotEmpty) {
|
||||
var templateHeader = jj.Template(kTemplateHeader);
|
||||
var m = {};
|
||||
for (var i in headers) {
|
||||
m[i["name"]] = i["value"];
|
||||
}
|
||||
result += templateHeader
|
||||
.render({"headers": padMultilineString(kEncoder.convert(m), 2)});
|
||||
}
|
||||
|
||||
if (harJson["postData"]?["text"] != null) {
|
||||
var templateBody = jj.Template(kTemplateBody);
|
||||
result += templateBody
|
||||
.render({"body": kEncoder.convert(harJson["postData"]["text"])});
|
||||
}
|
||||
result += kStringRequest;
|
||||
return result;
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
159
lib/codegen/kotlin/okhttp.dart
Normal file
159
lib/codegen/kotlin/okhttp.dart
Normal file
@ -0,0 +1,159 @@
|
||||
import 'dart:convert';
|
||||
import 'package:apidash/consts.dart';
|
||||
import 'package:jinja/jinja.dart' as jj;
|
||||
import 'package:apidash/utils/utils.dart'
|
||||
show getValidRequestUri, stripUriParams;
|
||||
import '../../models/request_model.dart';
|
||||
|
||||
class KotlinOkHttpCodeGen {
|
||||
final String kTemplateStart = """import okhttp3.OkHttpClient
|
||||
import okhttp3.Request{{importForQuery}}{{importForBody}}
|
||||
|
||||
fun main() {
|
||||
val client = OkHttpClient()
|
||||
|
||||
""";
|
||||
|
||||
final String kStringImportForQuery = """
|
||||
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl""";
|
||||
|
||||
final String kStringImportForBody = """
|
||||
|
||||
import okhttp3.RequestBody.Companion.toRequestBody
|
||||
import okhttp3.MediaType.Companion.toMediaType""";
|
||||
|
||||
final String kTemplateUrl = '''
|
||||
|
||||
val url = "{{url}}"
|
||||
|
||||
''';
|
||||
|
||||
final String kTemplateUrlQuery = '''
|
||||
|
||||
val url = "{{url}}".toHttpUrl().newBuilder()
|
||||
{{params}} .build()
|
||||
|
||||
''';
|
||||
|
||||
String kTemplateRequestBody = '''
|
||||
|
||||
val mediaType = "{{contentType}}".toMediaType()
|
||||
|
||||
val body = """{{body}}""".toRequestBody(mediaType)
|
||||
|
||||
''';
|
||||
|
||||
final String kStringRequestStart = """
|
||||
|
||||
val request = Request.Builder()
|
||||
.url(url)
|
||||
""";
|
||||
|
||||
final String kTemplateRequestEnd = """
|
||||
.{{method}}({{hasBody}})
|
||||
.build()
|
||||
|
||||
val response = client.newCall(request).execute()
|
||||
|
||||
println(response.code)
|
||||
println(response.body?.string())
|
||||
}
|
||||
|
||||
""";
|
||||
|
||||
String? getCode(
|
||||
RequestModel requestModel,
|
||||
String defaultUriScheme,
|
||||
) {
|
||||
try {
|
||||
String result = "";
|
||||
bool hasHeaders = false;
|
||||
bool hasQuery = false;
|
||||
bool hasBody = false;
|
||||
|
||||
String url = requestModel.url;
|
||||
if (!url.contains("://") && url.isNotEmpty) {
|
||||
url = "$defaultUriScheme://$url";
|
||||
}
|
||||
|
||||
var rec = getValidRequestUri(url, requestModel.requestParams);
|
||||
Uri? uri = rec.$1;
|
||||
|
||||
if (uri != null) {
|
||||
String url = stripUriParams(uri);
|
||||
|
||||
if (uri.hasQuery) {
|
||||
var params = uri.queryParameters;
|
||||
if (params.isNotEmpty) {
|
||||
hasQuery = true;
|
||||
var templateParams = jj.Template(kTemplateUrlQuery);
|
||||
result += templateParams
|
||||
.render({"url": url, "params": getQueryParams(params)});
|
||||
}
|
||||
}
|
||||
if (!hasQuery) {
|
||||
var templateUrl = jj.Template(kTemplateUrl);
|
||||
result += templateUrl.render({"url": url});
|
||||
}
|
||||
|
||||
var method = requestModel.method;
|
||||
var requestBody = requestModel.requestBody;
|
||||
if (kMethodsWithBody.contains(method) && requestBody != null) {
|
||||
var contentLength = utf8.encode(requestBody).length;
|
||||
if (contentLength > 0) {
|
||||
hasBody = true;
|
||||
String contentType =
|
||||
kContentTypeMap[requestModel.requestBodyContentType] ?? "";
|
||||
var templateBody = jj.Template(kTemplateRequestBody);
|
||||
result += templateBody
|
||||
.render({"contentType": contentType, "body": requestBody});
|
||||
}
|
||||
}
|
||||
|
||||
var templateStart = jj.Template(kTemplateStart);
|
||||
var stringStart = templateStart.render({
|
||||
"importForQuery": hasQuery ? kStringImportForQuery : "",
|
||||
"importForBody": hasBody ? kStringImportForBody : ""
|
||||
});
|
||||
|
||||
result = stringStart + result;
|
||||
result += kStringRequestStart;
|
||||
|
||||
var headersList = requestModel.requestHeaders;
|
||||
if (headersList != null) {
|
||||
var headers = requestModel.headersMap;
|
||||
if (headers.isNotEmpty) {
|
||||
hasHeaders = true;
|
||||
result += getHeaders(headers);
|
||||
}
|
||||
}
|
||||
|
||||
var templateRequestEnd = jj.Template(kTemplateRequestEnd);
|
||||
result += templateRequestEnd.render({
|
||||
"method": method.name.toLowerCase(),
|
||||
"hasBody": hasBody ? "body" : "",
|
||||
});
|
||||
}
|
||||
return result;
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
String getQueryParams(Map<String, String> params) {
|
||||
String result = "";
|
||||
for (final k in params.keys) {
|
||||
result = """$result .addQueryParameter("$k", "${params[k]}")\n""";
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
String getHeaders(Map<String, String> headers) {
|
||||
String result = "";
|
||||
for (final k in headers.keys) {
|
||||
result = """$result .addHeader("$k", "${headers[k]}")\n""";
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
@ -1,80 +0,0 @@
|
||||
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.k}", "${queryParam.v}")\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.k}", "${header.v}")\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;
|
||||
}
|
||||
}
|
61
lib/codegen/others/curl.dart
Normal file
61
lib/codegen/others/curl.dart
Normal file
@ -0,0 +1,61 @@
|
||||
import 'package:jinja/jinja.dart' as jj;
|
||||
import 'package:apidash/utils/utils.dart' show requestModelToHARJsonRequest;
|
||||
import 'package:apidash/models/models.dart' show RequestModel;
|
||||
|
||||
// ignore: camel_case_types
|
||||
class cURLCodeGen {
|
||||
String kTemplateStart = """curl{{method}} --url '{{url}}'
|
||||
""";
|
||||
|
||||
String kTemplateHeader = """ \\
|
||||
--header '{{name}}: {{value}}'
|
||||
""";
|
||||
|
||||
String kTemplateBody = """ \\
|
||||
--data '{{body}}'
|
||||
""";
|
||||
|
||||
String? getCode(
|
||||
RequestModel requestModel,
|
||||
String defaultUriScheme,
|
||||
) {
|
||||
try {
|
||||
String result = "";
|
||||
|
||||
String url = requestModel.url;
|
||||
if (!url.contains("://") && url.isNotEmpty) {
|
||||
url = "$defaultUriScheme://$url";
|
||||
}
|
||||
var rM = requestModel.copyWith(url: url);
|
||||
|
||||
var harJson = requestModelToHARJsonRequest(rM);
|
||||
|
||||
var templateStart = jj.Template(kTemplateStart);
|
||||
result += templateStart.render({
|
||||
"method": switch (harJson["method"]) {
|
||||
"GET" => "",
|
||||
"HEAD" => " --head",
|
||||
_ => " --request ${harJson["method"]} \\\n"
|
||||
},
|
||||
"url": harJson["url"],
|
||||
});
|
||||
|
||||
var headers = harJson["headers"];
|
||||
if (headers.isNotEmpty) {
|
||||
for (var item in headers) {
|
||||
var templateHeader = jj.Template(kTemplateHeader);
|
||||
result += templateHeader
|
||||
.render({"name": item["name"], "value": item["value"]});
|
||||
}
|
||||
}
|
||||
|
||||
if (harJson["postData"]?["text"] != null) {
|
||||
var templateBody = jj.Template(kTemplateBody);
|
||||
result += templateBody.render({"body": harJson["postData"]["text"]});
|
||||
}
|
||||
return result;
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
19
lib/codegen/others/har.dart
Normal file
19
lib/codegen/others/har.dart
Normal file
@ -0,0 +1,19 @@
|
||||
import 'package:apidash/consts.dart';
|
||||
import 'package:apidash/utils/utils.dart' show requestModelToHARJsonRequest;
|
||||
import 'package:apidash/models/models.dart' show RequestModel;
|
||||
|
||||
class HARCodeGen {
|
||||
String? getCode(
|
||||
RequestModel requestModel,
|
||||
String defaultUriScheme,
|
||||
) {
|
||||
try {
|
||||
var harString = kEncoder.convert(requestModelToHARJsonRequest(
|
||||
requestModel,
|
||||
defaultUriScheme: defaultUriScheme));
|
||||
return harString;
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
144
lib/codegen/python/http_client.dart
Normal file
144
lib/codegen/python/http_client.dart
Normal file
@ -0,0 +1,144 @@
|
||||
import 'dart:io';
|
||||
import 'dart:convert';
|
||||
import 'package:jinja/jinja.dart' as jj;
|
||||
import 'package:apidash/consts.dart';
|
||||
import 'package:apidash/utils/utils.dart'
|
||||
show getValidRequestUri, padMultilineString;
|
||||
import 'package:apidash/models/models.dart' show RequestModel;
|
||||
|
||||
class PythonHttpClientCodeGen {
|
||||
final String kTemplateStart = """import http.client
|
||||
""";
|
||||
|
||||
String kTemplateParams = """
|
||||
from urllib.parse import urlencode
|
||||
|
||||
queryParams = {{params}}
|
||||
queryParamsStr = '?' + urlencode(queryParams)
|
||||
|
||||
""";
|
||||
int kParamsPadding = 14;
|
||||
|
||||
String kTemplateBody = """
|
||||
|
||||
body = r'''{{body}}'''
|
||||
|
||||
""";
|
||||
|
||||
String kTemplateHeaders = """
|
||||
|
||||
headers = {{headers}}
|
||||
|
||||
""";
|
||||
|
||||
int kHeadersPadding = 10;
|
||||
|
||||
String kTemplateConnection = """
|
||||
|
||||
conn = http.client.HTTP{{isHttps}}Connection("{{authority}}")""";
|
||||
|
||||
String kTemplateRequest = """
|
||||
|
||||
conn.request("{{method}}", "{{path}}"{{queryParamsStr}}""";
|
||||
|
||||
String kStringRequestBody = """,
|
||||
body= body""";
|
||||
|
||||
String kStringRequestHeaders = """,
|
||||
headers= headers""";
|
||||
|
||||
final String kStringRequestEnd = """)
|
||||
|
||||
res = conn.getresponse()
|
||||
data = res.read()
|
||||
|
||||
print(data.decode("utf-8"))
|
||||
""";
|
||||
|
||||
String? getCode(
|
||||
RequestModel requestModel,
|
||||
String defaultUriScheme,
|
||||
) {
|
||||
try {
|
||||
String result = "";
|
||||
bool hasHeaders = false;
|
||||
bool hasQuery = false;
|
||||
bool hasBody = false;
|
||||
|
||||
String url = requestModel.url;
|
||||
if (!url.contains("://") && url.isNotEmpty) {
|
||||
url = "$defaultUriScheme://$url";
|
||||
}
|
||||
|
||||
result += kTemplateStart;
|
||||
var rec = getValidRequestUri(url, requestModel.requestParams);
|
||||
Uri? uri = rec.$1;
|
||||
|
||||
if (uri != null) {
|
||||
if (uri.hasQuery) {
|
||||
var params = uri.queryParameters;
|
||||
if (params.isNotEmpty) {
|
||||
hasQuery = true;
|
||||
var templateParams = jj.Template(kTemplateParams);
|
||||
var paramsString = kEncoder.convert(params);
|
||||
paramsString = padMultilineString(paramsString, kParamsPadding);
|
||||
result += templateParams.render({"params": paramsString});
|
||||
}
|
||||
}
|
||||
|
||||
var method = requestModel.method;
|
||||
var requestBody = requestModel.requestBody;
|
||||
if (kMethodsWithBody.contains(method) && requestBody != null) {
|
||||
var contentLength = utf8.encode(requestBody).length;
|
||||
if (contentLength > 0) {
|
||||
hasBody = true;
|
||||
var templateBody = jj.Template(kTemplateBody);
|
||||
result += templateBody.render({"body": requestBody});
|
||||
}
|
||||
}
|
||||
|
||||
var headersList = requestModel.requestHeaders;
|
||||
if (headersList != null || hasBody) {
|
||||
var headers = requestModel.headersMap;
|
||||
if (headers.isNotEmpty || hasBody) {
|
||||
hasHeaders = true;
|
||||
if (hasBody) {
|
||||
headers[HttpHeaders.contentTypeHeader] =
|
||||
kContentTypeMap[requestModel.requestBodyContentType] ?? "";
|
||||
}
|
||||
var headersString = kEncoder.convert(headers);
|
||||
headersString = padMultilineString(headersString, kHeadersPadding);
|
||||
var templateHeaders = jj.Template(kTemplateHeaders);
|
||||
result += templateHeaders.render({"headers": headersString});
|
||||
}
|
||||
}
|
||||
|
||||
var templateConnection = jj.Template(kTemplateConnection);
|
||||
result += templateConnection.render({
|
||||
"isHttps": uri.scheme == "https" ? "S" : "",
|
||||
"authority": uri.authority
|
||||
});
|
||||
|
||||
var templateRequest = jj.Template(kTemplateRequest);
|
||||
result += templateRequest.render({
|
||||
"method": method.name.toUpperCase(),
|
||||
"path": uri.path,
|
||||
"queryParamsStr": hasQuery ? " + queryParamsStr" : "",
|
||||
});
|
||||
|
||||
if (hasBody) {
|
||||
result += kStringRequestBody;
|
||||
}
|
||||
|
||||
if (hasHeaders) {
|
||||
result += kStringRequestHeaders;
|
||||
}
|
||||
|
||||
result += kStringRequestEnd;
|
||||
}
|
||||
return result;
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
156
lib/codegen/python/requests.dart
Normal file
156
lib/codegen/python/requests.dart
Normal file
@ -0,0 +1,156 @@
|
||||
import 'dart:io';
|
||||
import 'dart:convert';
|
||||
import 'package:jinja/jinja.dart' as jj;
|
||||
import 'package:apidash/consts.dart';
|
||||
import 'package:apidash/utils/utils.dart'
|
||||
show getValidRequestUri, padMultilineString, stripUriParams;
|
||||
import 'package:apidash/models/models.dart' show RequestModel;
|
||||
|
||||
class PythonRequestsCodeGen {
|
||||
final String kTemplateStart = """import requests
|
||||
|
||||
url = '{{url}}'
|
||||
|
||||
""";
|
||||
|
||||
String kTemplateParams = """
|
||||
|
||||
params = {{params}}
|
||||
|
||||
""";
|
||||
int kParamsPadding = 9;
|
||||
|
||||
String kTemplateBody = """
|
||||
|
||||
payload = r'''{{body}}'''
|
||||
|
||||
""";
|
||||
|
||||
String kTemplateJson = """
|
||||
|
||||
payload = {{body}}
|
||||
|
||||
""";
|
||||
|
||||
String kTemplateHeaders = """
|
||||
|
||||
headers = {{headers}}
|
||||
|
||||
""";
|
||||
|
||||
int kHeadersPadding = 10;
|
||||
|
||||
String kTemplateRequest = """
|
||||
|
||||
response = requests.{{method}}(url
|
||||
""";
|
||||
|
||||
String kStringRequestParams = """, params=params""";
|
||||
|
||||
String kStringRequestBody = """, data=payload""";
|
||||
|
||||
String kStringRequestJson = """, json=payload""";
|
||||
|
||||
String kStringRequestHeaders = """, headers=headers""";
|
||||
|
||||
final String kStringRequestEnd = """)
|
||||
|
||||
print('Status Code:', response.status_code)
|
||||
print('Response Body:', response.text)
|
||||
""";
|
||||
|
||||
String? getCode(
|
||||
RequestModel requestModel,
|
||||
String defaultUriScheme,
|
||||
) {
|
||||
try {
|
||||
String result = "";
|
||||
bool hasQuery = false;
|
||||
bool hasHeaders = false;
|
||||
bool hasBody = false;
|
||||
bool hasJsonBody = false;
|
||||
|
||||
String url = requestModel.url;
|
||||
if (!url.contains("://") && url.isNotEmpty) {
|
||||
url = "$defaultUriScheme://$url";
|
||||
}
|
||||
|
||||
var rec = getValidRequestUri(url, requestModel.requestParams);
|
||||
Uri? uri = rec.$1;
|
||||
if (uri != null) {
|
||||
var templateStartUrl = jj.Template(kTemplateStart);
|
||||
result += templateStartUrl.render({"url": stripUriParams(uri)});
|
||||
|
||||
if (uri.hasQuery) {
|
||||
var params = uri.queryParameters;
|
||||
if (params.isNotEmpty) {
|
||||
hasQuery = true;
|
||||
var templateParams = jj.Template(kTemplateParams);
|
||||
var paramsString = kEncoder.convert(params);
|
||||
paramsString = padMultilineString(paramsString, kParamsPadding);
|
||||
result += templateParams.render({"params": paramsString});
|
||||
}
|
||||
}
|
||||
|
||||
var method = requestModel.method;
|
||||
var requestBody = requestModel.requestBody;
|
||||
if (kMethodsWithBody.contains(method) && requestBody != null) {
|
||||
var contentLength = utf8.encode(requestBody).length;
|
||||
if (contentLength > 0) {
|
||||
if (requestModel.requestBodyContentType == ContentType.json) {
|
||||
hasJsonBody = true;
|
||||
var templateBody = jj.Template(kTemplateJson);
|
||||
result += templateBody.render({"body": requestBody});
|
||||
} else {
|
||||
hasBody = true;
|
||||
var templateBody = jj.Template(kTemplateBody);
|
||||
result += templateBody.render({"body": requestBody});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var headersList = requestModel.requestHeaders;
|
||||
if (headersList != null || hasBody) {
|
||||
var headers = requestModel.headersMap;
|
||||
if (headers.isNotEmpty || hasBody) {
|
||||
hasHeaders = true;
|
||||
if (hasBody) {
|
||||
headers[HttpHeaders.contentTypeHeader] =
|
||||
kContentTypeMap[requestModel.requestBodyContentType] ?? "";
|
||||
}
|
||||
var headersString = kEncoder.convert(headers);
|
||||
headersString = padMultilineString(headersString, kHeadersPadding);
|
||||
var templateHeaders = jj.Template(kTemplateHeaders);
|
||||
result += templateHeaders.render({"headers": headersString});
|
||||
}
|
||||
}
|
||||
|
||||
var templateRequest = jj.Template(kTemplateRequest);
|
||||
result += templateRequest.render({
|
||||
"method": method.name.toLowerCase(),
|
||||
});
|
||||
|
||||
if (hasQuery) {
|
||||
result += kStringRequestParams;
|
||||
}
|
||||
|
||||
if (hasBody) {
|
||||
result += kStringRequestBody;
|
||||
}
|
||||
|
||||
if (hasJsonBody) {
|
||||
result += kStringRequestJson;
|
||||
}
|
||||
|
||||
if (hasHeaders) {
|
||||
result += kStringRequestHeaders;
|
||||
}
|
||||
|
||||
result += kStringRequestEnd;
|
||||
}
|
||||
return result;
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@ -6,7 +6,7 @@ import 'package:google_fonts/google_fonts.dart';
|
||||
import 'package:davi/davi.dart';
|
||||
|
||||
const kDiscordUrl = "https://bit.ly/heyfoss";
|
||||
const kGitUrl = "https://github.com/foss42/api-dash";
|
||||
const kGitUrl = "https://github.com/foss42/apidash";
|
||||
const kIssueUrl = "$kGitUrl/issues";
|
||||
|
||||
final kIsMacOS = !kIsWeb && Platform.isMacOS;
|
||||
@ -69,7 +69,11 @@ const kPh20t40 = EdgeInsets.only(
|
||||
top: 40,
|
||||
);
|
||||
const kPh60 = EdgeInsets.symmetric(horizontal: 60);
|
||||
const kP24CollectionPane = EdgeInsets.only(top: 24, left: 8.0, bottom: 8.0);
|
||||
const kP8CollectionPane = EdgeInsets.only(top: 8.0, left: 8.0, bottom: 8.0);
|
||||
const kPr8CollectionPane = EdgeInsets.only(right: 8.0);
|
||||
|
||||
const kHSpacer4 = SizedBox(width: 4);
|
||||
const kHSpacer5 = SizedBox(width: 5);
|
||||
const kHSpacer10 = SizedBox(width: 10);
|
||||
const kHSpacer20 = SizedBox(width: 20);
|
||||
@ -82,7 +86,7 @@ const kTabAnimationDuration = Duration(milliseconds: 200);
|
||||
const kTabHeight = 45.0;
|
||||
const kHeaderHeight = 32.0;
|
||||
const kSegmentHeight = 24.0;
|
||||
const kTextButtonMinWidth = 36.0;
|
||||
const kTextButtonMinWidth = 44.0;
|
||||
|
||||
const kRandMax = 100000;
|
||||
|
||||
@ -216,7 +220,7 @@ final kColorHttpMethodPut = Colors.amber.shade900;
|
||||
final kColorHttpMethodPatch = kColorHttpMethodPut;
|
||||
final kColorHttpMethodDelete = Colors.red.shade800;
|
||||
|
||||
enum RequestItemMenuOption { delete, duplicate }
|
||||
enum RequestItemMenuOption { edit, delete, duplicate }
|
||||
|
||||
enum HTTPVerb { get, head, post, put, patch, delete }
|
||||
|
||||
@ -230,12 +234,21 @@ const kMethodsWithBody = [
|
||||
HTTPVerb.patch,
|
||||
HTTPVerb.delete,
|
||||
];
|
||||
|
||||
const kDefaultHttpMethod = HTTPVerb.get;
|
||||
const kDefaultContentType = ContentType.json;
|
||||
|
||||
enum CodegenLanguage {
|
||||
curl("cURL", "bash", "curl"),
|
||||
har("HAR", "json", "har"),
|
||||
dartHttp("Dart (http)", "dart", "dart"),
|
||||
kotlinOkHttp("Kotlin (OkHttp)", "java", "kt");
|
||||
jsAxios("JavaScript (axios)", "javascript", "js"),
|
||||
jsFetch("JavaScript (fetch)", "javascript", "js"),
|
||||
nodejsAxios("node.js (axios)", "javascript", "js"),
|
||||
nodejsFetch("node.js (fetch)", "javascript", "js"),
|
||||
kotlinOkHttp("Kotlin (okhttp3)", "java", "kt"),
|
||||
pythonHttpClient("Python (http.client)", "python", "py"),
|
||||
pythonRequests("Python (requests)", "python", "py");
|
||||
|
||||
const CodegenLanguage(this.label, this.codeHighlightLang, this.ext);
|
||||
final String label;
|
||||
@ -253,7 +266,8 @@ const kSubTypeOctetStream = 'octet-stream';
|
||||
const kSubTypePdf = 'pdf';
|
||||
const kSubTypeSql = 'sql';
|
||||
const kSubTypeXml = 'xml';
|
||||
const kSubTypeYaml = 'x-yaml';
|
||||
const kSubTypeYaml = 'yaml';
|
||||
const kSubTypeXYaml = 'x-yaml';
|
||||
const kSubTypeYml = 'x-yml';
|
||||
|
||||
const kTypeText = 'text';
|
||||
@ -303,6 +317,10 @@ const kCodeRawBodyViewOptions = [ResponseBodyView.code, ResponseBodyView.raw];
|
||||
const kPreviewBodyViewOptions = [
|
||||
ResponseBodyView.preview,
|
||||
];
|
||||
const kPreviewRawBodyViewOptions = [
|
||||
ResponseBodyView.preview,
|
||||
ResponseBodyView.raw
|
||||
];
|
||||
const kPreviewCodeRawBodyViewOptions = [
|
||||
ResponseBodyView.preview,
|
||||
ResponseBodyView.code,
|
||||
@ -313,12 +331,13 @@ const Map<String, Map<String, List<ResponseBodyView>>>
|
||||
kResponseBodyViewOptions = {
|
||||
kTypeApplication: {
|
||||
kSubTypeDefaultViewOptions: kNoRawBodyViewOptions,
|
||||
kSubTypeJson: kCodeRawBodyViewOptions,
|
||||
kSubTypeJson: kPreviewRawBodyViewOptions,
|
||||
kSubTypeOctetStream: kNoBodyViewOptions,
|
||||
kSubTypePdf: kNoBodyViewOptions,
|
||||
kSubTypePdf: kPreviewBodyViewOptions,
|
||||
kSubTypeSql: kCodeRawBodyViewOptions,
|
||||
kSubTypeXml: kCodeRawBodyViewOptions,
|
||||
kSubTypeYaml: kCodeRawBodyViewOptions,
|
||||
kSubTypeXYaml: kCodeRawBodyViewOptions,
|
||||
kSubTypeYml: kCodeRawBodyViewOptions,
|
||||
},
|
||||
kTypeImage: {
|
||||
@ -347,8 +366,9 @@ const Map<String, String> kCodeHighlighterMap = {
|
||||
kSubTypeHtml: "xml",
|
||||
kSubTypeSvg: "xml",
|
||||
kSubTypeYaml: "yaml",
|
||||
kSubTypeXYaml: "yaml",
|
||||
kSubTypeYml: "yaml",
|
||||
kSubTypeTextYaml: "yaml",
|
||||
//kSubTypeTextYaml: "yaml",
|
||||
kSubTypeTextYml: "yaml",
|
||||
};
|
||||
|
||||
@ -439,6 +459,9 @@ const kUnexpectedRaiseIssue =
|
||||
const kImageError =
|
||||
"There seems to be an issue rendering this image. Please raise an issue in API Dash GitHub repo so that we can resolve it.";
|
||||
|
||||
const kPdfError =
|
||||
"There seems to be an issue rendering this pdf. Please raise an issue in API Dash GitHub repo so that we can resolve it.";
|
||||
|
||||
const kAudioError =
|
||||
"There seems to be an issue playing this audio. Please raise an issue in API Dash GitHub repo so that we can resolve it.";
|
||||
|
||||
|
@ -13,11 +13,11 @@ void main() async {
|
||||
await setupInitialWindow();
|
||||
} else {
|
||||
var win = getInitialSize();
|
||||
await setupWindow(sz: win.$0, off: win.$1);
|
||||
await setupWindow(sz: win.$1, off: win.$2);
|
||||
}
|
||||
runApp(
|
||||
ProviderScope(
|
||||
child: kIsLinux ? const DashApp() : const App(),
|
||||
const ProviderScope(
|
||||
child: DashApp(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -1,38 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
@immutable
|
||||
class KVRow {
|
||||
const KVRow(this.k, this.v);
|
||||
|
||||
final String k;
|
||||
final dynamic v;
|
||||
|
||||
KVRow copyWith({
|
||||
String? k,
|
||||
dynamic v,
|
||||
}) {
|
||||
return KVRow(k ?? this.k, v ?? this.v);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return {k: v}.toString();
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is KVRow &&
|
||||
other.runtimeType == runtimeType &&
|
||||
other.k == k &&
|
||||
other.v == v;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
return Object.hash(
|
||||
runtimeType,
|
||||
k,
|
||||
v,
|
||||
);
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
export 'kvrow_model.dart';
|
||||
export 'name_value_model.dart';
|
||||
export 'request_model.dart';
|
||||
export 'response_model.dart';
|
||||
export 'settings_model.dart';
|
||||
|
19
lib/models/name_value_model.dart
Normal file
19
lib/models/name_value_model.dart
Normal file
@ -0,0 +1,19 @@
|
||||
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);
|
||||
}
|
||||
|
||||
const kNameValueEmptyModel = NameValueModel(name: "", value: "");
|
181
lib/models/name_value_model.freezed.dart
Normal file
181
lib/models/name_value_model.freezed.dart
Normal file
@ -0,0 +1,181 @@
|
||||
// coverage:ignore-file
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||
|
||||
part of 'name_value_model.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// FreezedGenerator
|
||||
// **************************************************************************
|
||||
|
||||
T _$identity<T>(T value) => value;
|
||||
|
||||
final _privateConstructorUsedError = UnsupportedError(
|
||||
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods');
|
||||
|
||||
NameValueModel _$NameValueModelFromJson(Map<String, dynamic> json) {
|
||||
return _NameValueModel.fromJson(json);
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
mixin _$NameValueModel {
|
||||
String get name => throw _privateConstructorUsedError;
|
||||
dynamic get value => throw _privateConstructorUsedError;
|
||||
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
@JsonKey(ignore: true)
|
||||
$NameValueModelCopyWith<NameValueModel> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $NameValueModelCopyWith<$Res> {
|
||||
factory $NameValueModelCopyWith(
|
||||
NameValueModel value, $Res Function(NameValueModel) then) =
|
||||
_$NameValueModelCopyWithImpl<$Res, NameValueModel>;
|
||||
@useResult
|
||||
$Res call({String name, dynamic value});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$NameValueModelCopyWithImpl<$Res, $Val extends NameValueModel>
|
||||
implements $NameValueModelCopyWith<$Res> {
|
||||
_$NameValueModelCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? name = null,
|
||||
Object? value = freezed,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
name: null == name
|
||||
? _value.name
|
||||
: name // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
value: freezed == value
|
||||
? _value.value
|
||||
: value // ignore: cast_nullable_to_non_nullable
|
||||
as dynamic,
|
||||
) as $Val);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$_NameValueModelCopyWith<$Res>
|
||||
implements $NameValueModelCopyWith<$Res> {
|
||||
factory _$$_NameValueModelCopyWith(
|
||||
_$_NameValueModel value, $Res Function(_$_NameValueModel) then) =
|
||||
__$$_NameValueModelCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call({String name, dynamic value});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$_NameValueModelCopyWithImpl<$Res>
|
||||
extends _$NameValueModelCopyWithImpl<$Res, _$_NameValueModel>
|
||||
implements _$$_NameValueModelCopyWith<$Res> {
|
||||
__$$_NameValueModelCopyWithImpl(
|
||||
_$_NameValueModel _value, $Res Function(_$_NameValueModel) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? name = null,
|
||||
Object? value = freezed,
|
||||
}) {
|
||||
return _then(_$_NameValueModel(
|
||||
name: null == name
|
||||
? _value.name
|
||||
: name // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
value: freezed == value
|
||||
? _value.value
|
||||
: value // ignore: cast_nullable_to_non_nullable
|
||||
as dynamic,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
class _$_NameValueModel
|
||||
with DiagnosticableTreeMixin
|
||||
implements _NameValueModel {
|
||||
const _$_NameValueModel({required this.name, required this.value});
|
||||
|
||||
factory _$_NameValueModel.fromJson(Map<String, dynamic> json) =>
|
||||
_$$_NameValueModelFromJson(json);
|
||||
|
||||
@override
|
||||
final String name;
|
||||
@override
|
||||
final dynamic value;
|
||||
|
||||
@override
|
||||
String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) {
|
||||
return 'NameValueModel(name: $name, value: $value)';
|
||||
}
|
||||
|
||||
@override
|
||||
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||
super.debugFillProperties(properties);
|
||||
properties
|
||||
..add(DiagnosticsProperty('type', 'NameValueModel'))
|
||||
..add(DiagnosticsProperty('name', name))
|
||||
..add(DiagnosticsProperty('value', value));
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(dynamic other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$_NameValueModel &&
|
||||
(identical(other.name, name) || other.name == name) &&
|
||||
const DeepCollectionEquality().equals(other.value, value));
|
||||
}
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType, name, const DeepCollectionEquality().hash(value));
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$_NameValueModelCopyWith<_$_NameValueModel> get copyWith =>
|
||||
__$$_NameValueModelCopyWithImpl<_$_NameValueModel>(this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$$_NameValueModelToJson(
|
||||
this,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _NameValueModel implements NameValueModel {
|
||||
const factory _NameValueModel(
|
||||
{required final String name,
|
||||
required final dynamic value}) = _$_NameValueModel;
|
||||
|
||||
factory _NameValueModel.fromJson(Map<String, dynamic> json) =
|
||||
_$_NameValueModel.fromJson;
|
||||
|
||||
@override
|
||||
String get name;
|
||||
@override
|
||||
dynamic get value;
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
_$$_NameValueModelCopyWith<_$_NameValueModel> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
19
lib/models/name_value_model.g.dart
Normal file
19
lib/models/name_value_model.g.dart
Normal file
@ -0,0 +1,19 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'name_value_model.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
_$_NameValueModel _$$_NameValueModelFromJson(Map<String, dynamic> json) =>
|
||||
_$_NameValueModel(
|
||||
name: json['name'] as String,
|
||||
value: json['value'],
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$_NameValueModelToJson(_$_NameValueModel instance) =>
|
||||
<String, dynamic>{
|
||||
'name': instance.name,
|
||||
'value': instance.value,
|
||||
};
|
@ -1,21 +1,21 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:apidash/consts.dart';
|
||||
import 'package:apidash/utils/utils.dart' show mapToRows, rowsToMap;
|
||||
import 'kvrow_model.dart';
|
||||
import 'name_value_model.dart';
|
||||
import 'response_model.dart';
|
||||
|
||||
@immutable
|
||||
class RequestModel {
|
||||
const RequestModel({
|
||||
required this.id,
|
||||
this.method = kDefaultHttpMethod,
|
||||
this.method = HTTPVerb.get,
|
||||
this.url = "",
|
||||
this.name = "",
|
||||
this.description = "",
|
||||
this.requestTabIndex = 0,
|
||||
this.requestHeaders,
|
||||
this.requestParams,
|
||||
this.requestBodyContentType = kDefaultContentType,
|
||||
this.requestBodyContentType = ContentType.json,
|
||||
this.requestBody,
|
||||
this.responseStatus,
|
||||
this.message,
|
||||
@ -28,14 +28,17 @@ class RequestModel {
|
||||
final String name;
|
||||
final String description;
|
||||
final int requestTabIndex;
|
||||
final List<KVRow>? requestHeaders;
|
||||
final List<KVRow>? requestParams;
|
||||
final List<NameValueModel>? requestHeaders;
|
||||
final List<NameValueModel>? requestParams;
|
||||
final ContentType requestBodyContentType;
|
||||
final String? requestBody;
|
||||
final int? responseStatus;
|
||||
final String? message;
|
||||
final ResponseModel? responseModel;
|
||||
|
||||
Map<String, String> get headersMap => rowsToMap(requestHeaders) ?? {};
|
||||
Map<String, String> get paramsMap => rowsToMap(requestParams) ?? {};
|
||||
|
||||
RequestModel duplicate({
|
||||
required String id,
|
||||
}) {
|
||||
@ -43,10 +46,10 @@ class RequestModel {
|
||||
id: id,
|
||||
method: method,
|
||||
url: url,
|
||||
name: name,
|
||||
name: "$name (copy)",
|
||||
description: description,
|
||||
requestHeaders: requestHeaders,
|
||||
requestParams: requestParams,
|
||||
requestHeaders: requestHeaders != null ? [...requestHeaders!] : null,
|
||||
requestParams: requestParams != null ? [...requestParams!] : null,
|
||||
requestBodyContentType: requestBodyContentType,
|
||||
requestBody: requestBody,
|
||||
);
|
||||
@ -59,14 +62,16 @@ class RequestModel {
|
||||
String? name,
|
||||
String? description,
|
||||
int? requestTabIndex,
|
||||
List<KVRow>? requestHeaders,
|
||||
List<KVRow>? requestParams,
|
||||
List<NameValueModel>? requestHeaders,
|
||||
List<NameValueModel>? requestParams,
|
||||
ContentType? requestBodyContentType,
|
||||
String? requestBody,
|
||||
int? responseStatus,
|
||||
String? message,
|
||||
ResponseModel? responseModel,
|
||||
}) {
|
||||
var headers = requestHeaders ?? this.requestHeaders;
|
||||
var params = requestParams ?? this.requestParams;
|
||||
return RequestModel(
|
||||
id: id ?? this.id,
|
||||
method: method ?? this.method,
|
||||
@ -74,8 +79,8 @@ class RequestModel {
|
||||
name: name ?? this.name,
|
||||
description: description ?? this.description,
|
||||
requestTabIndex: requestTabIndex ?? this.requestTabIndex,
|
||||
requestHeaders: requestHeaders ?? this.requestHeaders,
|
||||
requestParams: requestParams ?? this.requestParams,
|
||||
requestHeaders: headers != null ? [...headers] : null,
|
||||
requestParams: params != null ? [...params] : null,
|
||||
requestBodyContentType:
|
||||
requestBodyContentType ?? this.requestBodyContentType,
|
||||
requestBody: requestBody ?? this.requestBody,
|
||||
|
@ -5,28 +5,37 @@ import 'package:apidash/consts.dart';
|
||||
class SettingsModel {
|
||||
const SettingsModel(
|
||||
{this.isDark = false,
|
||||
this.alwaysShowCollectionPaneScrollbar = true,
|
||||
this.size,
|
||||
this.offset,
|
||||
this.defaultUriScheme = kDefaultUriScheme,
|
||||
this.defaultCodeGenLang = CodegenLanguage.curl,
|
||||
this.saveResponses = true});
|
||||
|
||||
final bool isDark;
|
||||
final bool alwaysShowCollectionPaneScrollbar;
|
||||
final Size? size;
|
||||
final Offset? offset;
|
||||
final String defaultUriScheme;
|
||||
final CodegenLanguage defaultCodeGenLang;
|
||||
final bool saveResponses;
|
||||
|
||||
SettingsModel copyWith({
|
||||
bool? isDark,
|
||||
bool? alwaysShowCollectionPaneScrollbar,
|
||||
Size? size,
|
||||
Offset? offset,
|
||||
String? defaultUriScheme,
|
||||
CodegenLanguage? defaultCodeGenLang,
|
||||
bool? saveResponses,
|
||||
}) {
|
||||
return SettingsModel(
|
||||
isDark: isDark ?? this.isDark,
|
||||
alwaysShowCollectionPaneScrollbar: alwaysShowCollectionPaneScrollbar ??
|
||||
this.alwaysShowCollectionPaneScrollbar,
|
||||
size: size ?? this.size,
|
||||
defaultUriScheme: defaultUriScheme ?? this.defaultUriScheme,
|
||||
defaultCodeGenLang: defaultCodeGenLang ?? this.defaultCodeGenLang,
|
||||
offset: offset ?? this.offset,
|
||||
saveResponses: saveResponses ?? this.saveResponses,
|
||||
);
|
||||
@ -34,6 +43,8 @@ class SettingsModel {
|
||||
|
||||
factory SettingsModel.fromJson(Map<dynamic, dynamic> data) {
|
||||
final isDark = data["isDark"] as bool?;
|
||||
final alwaysShowCollectionPaneScrollbar =
|
||||
data["alwaysShowCollectionPaneScrollbar"] as bool?;
|
||||
final width = data["width"] as double?;
|
||||
final height = data["height"] as double?;
|
||||
final dx = data["dx"] as double?;
|
||||
@ -47,15 +58,27 @@ class SettingsModel {
|
||||
offset = Offset(dx, dy);
|
||||
}
|
||||
final defaultUriScheme = data["defaultUriScheme"] as String?;
|
||||
final defaultCodeGenLangStr = data["defaultCodeGenLang"] as String?;
|
||||
CodegenLanguage? defaultCodeGenLang;
|
||||
if (defaultCodeGenLangStr != null) {
|
||||
try {
|
||||
defaultCodeGenLang =
|
||||
CodegenLanguage.values.byName(defaultCodeGenLangStr);
|
||||
} catch (e) {
|
||||
// pass
|
||||
}
|
||||
}
|
||||
final saveResponses = data["saveResponses"] as bool?;
|
||||
|
||||
const sm = SettingsModel();
|
||||
|
||||
return sm.copyWith(
|
||||
isDark: isDark,
|
||||
alwaysShowCollectionPaneScrollbar: alwaysShowCollectionPaneScrollbar,
|
||||
size: size,
|
||||
offset: offset,
|
||||
defaultUriScheme: defaultUriScheme,
|
||||
defaultCodeGenLang: defaultCodeGenLang,
|
||||
saveResponses: saveResponses,
|
||||
);
|
||||
}
|
||||
@ -63,11 +86,13 @@ class SettingsModel {
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
"isDark": isDark,
|
||||
"alwaysShowCollectionPaneScrollbar": alwaysShowCollectionPaneScrollbar,
|
||||
"width": size?.width,
|
||||
"height": size?.height,
|
||||
"dx": offset?.dx,
|
||||
"dy": offset?.dy,
|
||||
"defaultUriScheme": defaultUriScheme,
|
||||
"defaultCodeGenLang": defaultCodeGenLang.name,
|
||||
"saveResponses": saveResponses,
|
||||
};
|
||||
}
|
||||
|
@ -1,10 +1,12 @@
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:apidash/providers/providers.dart';
|
||||
import 'package:apidash/models/models.dart';
|
||||
import 'package:apidash/services/services.dart'
|
||||
show hiveHandler, HiveHandler, request;
|
||||
import 'package:apidash/utils/utils.dart' show uuid;
|
||||
import 'package:apidash/consts.dart';
|
||||
import 'settings_providers.dart';
|
||||
import 'ui_providers.dart';
|
||||
import '../models/models.dart';
|
||||
import '../services/services.dart' show hiveHandler, HiveHandler, request;
|
||||
import '../utils/utils.dart' show uuid, collectionToHAR;
|
||||
import '../consts.dart';
|
||||
|
||||
final activeIdStateProvider = StateProvider<String?>((ref) => null);
|
||||
|
||||
final activeRequestModelProvider = StateProvider<RequestModel?>((ref) {
|
||||
final activeId = ref.watch(activeIdStateProvider);
|
||||
@ -12,81 +14,104 @@ final activeRequestModelProvider = StateProvider<RequestModel?>((ref) {
|
||||
if (activeId == null || collection == null) {
|
||||
return null;
|
||||
} else {
|
||||
final idIdx = collection.indexWhere((m) => m.id == activeId);
|
||||
if (idIdx.isNegative) {
|
||||
return null;
|
||||
} else {
|
||||
return collection[idIdx];
|
||||
}
|
||||
return collection[activeId];
|
||||
}
|
||||
});
|
||||
|
||||
final StateNotifierProvider<CollectionStateNotifier, List<RequestModel>?>
|
||||
final requestSequenceProvider = StateProvider<List<String>>((ref) {
|
||||
var ids = hiveHandler.getIds();
|
||||
return ids ?? [];
|
||||
});
|
||||
|
||||
final StateNotifierProvider<CollectionStateNotifier, Map<String, RequestModel>?>
|
||||
collectionStateNotifierProvider =
|
||||
StateNotifierProvider((ref) => CollectionStateNotifier(ref, hiveHandler));
|
||||
|
||||
class CollectionStateNotifier extends StateNotifier<List<RequestModel>?> {
|
||||
class CollectionStateNotifier
|
||||
extends StateNotifier<Map<String, RequestModel>?> {
|
||||
CollectionStateNotifier(this.ref, this.hiveHandler) : super(null) {
|
||||
loadData();
|
||||
Future.microtask(() =>
|
||||
ref.read(activeIdStateProvider.notifier).update((s) => state?[0].id));
|
||||
var status = loadData();
|
||||
Future.microtask(() {
|
||||
if (status) {
|
||||
ref.read(requestSequenceProvider.notifier).state = [
|
||||
state!.keys.first,
|
||||
];
|
||||
}
|
||||
ref.read(activeIdStateProvider.notifier).state =
|
||||
ref.read(requestSequenceProvider)[0];
|
||||
});
|
||||
}
|
||||
|
||||
final Ref ref;
|
||||
final HiveHandler hiveHandler;
|
||||
final baseResponseModel = const ResponseModel();
|
||||
|
||||
List<String> getIds() => state!.map((e) => e.id).toList();
|
||||
int idxOfId(String id) => state!.indexWhere((element) => element.id == id);
|
||||
bool hasId(String id) => state?.keys.contains(id) ?? false;
|
||||
|
||||
RequestModel getRequestModel(String id) {
|
||||
final idx = idxOfId(id);
|
||||
return state![idx];
|
||||
RequestModel? getRequestModel(String id) {
|
||||
return state?[id];
|
||||
}
|
||||
|
||||
void add() {
|
||||
final id = uuid.v1();
|
||||
final newRequestModel = RequestModel(
|
||||
id: uuid.v1(),
|
||||
id: id,
|
||||
);
|
||||
state = [newRequestModel, ...state!];
|
||||
var map = {...state!};
|
||||
map[id] = newRequestModel;
|
||||
state = map;
|
||||
ref
|
||||
.read(activeIdStateProvider.notifier)
|
||||
.update((state) => newRequestModel.id);
|
||||
.read(requestSequenceProvider.notifier)
|
||||
.update((state) => [id, ...state]);
|
||||
ref.read(activeIdStateProvider.notifier).state = newRequestModel.id;
|
||||
}
|
||||
|
||||
void reorder(int oldIdx, int newIdx) {
|
||||
final item = state!.removeAt(oldIdx);
|
||||
state!.insert(newIdx, item);
|
||||
var itemIds = ref.read(requestSequenceProvider);
|
||||
final itemId = itemIds.removeAt(oldIdx);
|
||||
itemIds.insert(newIdx, itemId);
|
||||
ref.read(requestSequenceProvider.notifier).state = [...itemIds];
|
||||
}
|
||||
|
||||
void remove(String id) {
|
||||
int idx = idxOfId(id);
|
||||
var itemIds = ref.read(requestSequenceProvider);
|
||||
int idx = itemIds.indexOf(id);
|
||||
itemIds.remove(id);
|
||||
ref.read(requestSequenceProvider.notifier).state = [...itemIds];
|
||||
|
||||
String? newId;
|
||||
if (idx == 0 && state!.length > 1) {
|
||||
newId = state![1].id;
|
||||
} else if (state!.length > 2) {
|
||||
newId = state![idx - 1].id;
|
||||
if (idx == 0 && itemIds.isNotEmpty) {
|
||||
newId = itemIds[0];
|
||||
} else if (itemIds.length > 1) {
|
||||
newId = itemIds[idx - 1];
|
||||
} else {
|
||||
newId = null;
|
||||
}
|
||||
|
||||
state = [
|
||||
for (final model in state!)
|
||||
if (model.id != id) model,
|
||||
];
|
||||
ref.read(activeIdStateProvider.notifier).update((state) => newId);
|
||||
ref.read(activeIdStateProvider.notifier).state = newId;
|
||||
|
||||
var map = {...state!};
|
||||
map.remove(id);
|
||||
state = map;
|
||||
}
|
||||
|
||||
void duplicate(String id) {
|
||||
final idx = idxOfId(id);
|
||||
final newModel = state![idx].duplicate(
|
||||
id: uuid.v1(),
|
||||
final newId = uuid.v1();
|
||||
|
||||
var itemIds = ref.read(requestSequenceProvider);
|
||||
int idx = itemIds.indexOf(id);
|
||||
|
||||
final newModel = state![id]!.duplicate(
|
||||
id: newId,
|
||||
);
|
||||
state = [
|
||||
...state!.sublist(0, idx + 1),
|
||||
newModel,
|
||||
...state!.sublist(idx + 1)
|
||||
];
|
||||
|
||||
itemIds.insert(idx + 1, newId);
|
||||
var map = {...state!};
|
||||
map[newId] = newModel;
|
||||
state = map;
|
||||
|
||||
ref.read(requestSequenceProvider.notifier).state = [...itemIds];
|
||||
ref.read(activeIdStateProvider.notifier).state = newId;
|
||||
}
|
||||
|
||||
void update(
|
||||
@ -96,16 +121,15 @@ class CollectionStateNotifier extends StateNotifier<List<RequestModel>?> {
|
||||
String? name,
|
||||
String? description,
|
||||
int? requestTabIndex,
|
||||
List<KVRow>? requestHeaders,
|
||||
List<KVRow>? requestParams,
|
||||
List<NameValueModel>? requestHeaders,
|
||||
List<NameValueModel>? requestParams,
|
||||
ContentType? requestBodyContentType,
|
||||
String? requestBody,
|
||||
int? responseStatus,
|
||||
String? message,
|
||||
ResponseModel? responseModel,
|
||||
}) {
|
||||
final idx = idxOfId(id);
|
||||
final newModel = state![idx].copyWith(
|
||||
final newModel = state![id]!.copyWith(
|
||||
method: method,
|
||||
url: url,
|
||||
name: name,
|
||||
@ -119,30 +143,32 @@ class CollectionStateNotifier extends StateNotifier<List<RequestModel>?> {
|
||||
message: message,
|
||||
responseModel: responseModel);
|
||||
//print(newModel);
|
||||
state = [...state!.sublist(0, idx), newModel, ...state!.sublist(idx + 1)];
|
||||
var map = {...state!};
|
||||
map[id] = newModel;
|
||||
state = map;
|
||||
}
|
||||
|
||||
Future<void> sendRequest(String id) async {
|
||||
ref.read(sentRequestIdStateProvider.notifier).update((state) => id);
|
||||
ref.read(codePaneVisibleStateProvider.notifier).update((state) => false);
|
||||
ref.read(sentRequestIdStateProvider.notifier).state = id;
|
||||
ref.read(codePaneVisibleStateProvider.notifier).state = false;
|
||||
final defaultUriScheme =
|
||||
ref.read(settingsProvider.select((value) => value.defaultUriScheme));
|
||||
final idx = idxOfId(id);
|
||||
RequestModel requestModel = getRequestModel(id);
|
||||
|
||||
RequestModel requestModel = state![id]!;
|
||||
var responseRec =
|
||||
await request(requestModel, defaultUriScheme: defaultUriScheme);
|
||||
late final RequestModel newRequestModel;
|
||||
if (responseRec.$0 == null) {
|
||||
if (responseRec.$1 == null) {
|
||||
newRequestModel = requestModel.copyWith(
|
||||
responseStatus: -1,
|
||||
message: responseRec.$2,
|
||||
message: responseRec.$3,
|
||||
);
|
||||
} else {
|
||||
final responseModel = baseResponseModel.fromResponse(
|
||||
response: responseRec.$0!,
|
||||
time: responseRec.$1!,
|
||||
response: responseRec.$1!,
|
||||
time: responseRec.$2!,
|
||||
);
|
||||
int statusCode = responseRec.$0!.statusCode;
|
||||
int statusCode = responseRec.$1!.statusCode;
|
||||
newRequestModel = requestModel.copyWith(
|
||||
responseStatus: statusCode,
|
||||
message: kResponseCodeReasons[statusCode],
|
||||
@ -150,57 +176,66 @@ class CollectionStateNotifier extends StateNotifier<List<RequestModel>?> {
|
||||
);
|
||||
}
|
||||
//print(newRequestModel);
|
||||
ref.read(sentRequestIdStateProvider.notifier).update((state) => null);
|
||||
state = [
|
||||
...state!.sublist(0, idx),
|
||||
newRequestModel,
|
||||
...state!.sublist(idx + 1)
|
||||
];
|
||||
ref.read(sentRequestIdStateProvider.notifier).state = null;
|
||||
var map = {...state!};
|
||||
map[id] = newRequestModel;
|
||||
state = map;
|
||||
}
|
||||
|
||||
Future<void> clearData() async {
|
||||
ref.read(clearDataStateProvider.notifier).update((state) => true);
|
||||
ref.read(activeIdStateProvider.notifier).update((state) => null);
|
||||
ref.read(clearDataStateProvider.notifier).state = true;
|
||||
ref.read(activeIdStateProvider.notifier).state = null;
|
||||
await hiveHandler.clear();
|
||||
ref.read(clearDataStateProvider.notifier).update((state) => false);
|
||||
state = [];
|
||||
ref.read(clearDataStateProvider.notifier).state = false;
|
||||
ref.read(requestSequenceProvider.notifier).state = [];
|
||||
state = {};
|
||||
}
|
||||
|
||||
void loadData() {
|
||||
bool loadData() {
|
||||
var ids = hiveHandler.getIds();
|
||||
if (ids == null || ids.length == 0) {
|
||||
state = [
|
||||
RequestModel(
|
||||
id: uuid.v1(),
|
||||
String newId = uuid.v1();
|
||||
state = {
|
||||
newId: RequestModel(
|
||||
id: newId,
|
||||
),
|
||||
];
|
||||
};
|
||||
return true;
|
||||
} else {
|
||||
List<RequestModel> data = [];
|
||||
Map<String, RequestModel> data = {};
|
||||
for (var id in ids) {
|
||||
var jsonModel = hiveHandler.getRequestModel(id);
|
||||
if (jsonModel != null) {
|
||||
var requestModel =
|
||||
RequestModel.fromJson(Map<String, dynamic>.from(jsonModel));
|
||||
data.add(requestModel);
|
||||
data[id] = requestModel;
|
||||
}
|
||||
}
|
||||
state = data;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> saveData() async {
|
||||
ref.read(saveDataStateProvider.notifier).update((state) => true);
|
||||
final saveResponse =
|
||||
ref.read(settingsProvider.select((value) => value.saveResponses));
|
||||
final ids = getIds();
|
||||
ref.read(saveDataStateProvider.notifier).state = true;
|
||||
final saveResponse = ref.read(settingsProvider).saveResponses;
|
||||
final ids = ref.read(requestSequenceProvider);
|
||||
await hiveHandler.setIds(ids);
|
||||
for (var e in state!) {
|
||||
for (var id in ids) {
|
||||
await hiveHandler.setRequestModel(
|
||||
e.id,
|
||||
e.toJson(includeResponse: saveResponse),
|
||||
id,
|
||||
state?[id]?.toJson(includeResponse: saveResponse),
|
||||
);
|
||||
}
|
||||
await hiveHandler.removeUnused();
|
||||
ref.read(saveDataStateProvider.notifier).update((state) => false);
|
||||
ref.read(saveDataStateProvider.notifier).state = false;
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> exportDataToHAR() async {
|
||||
var result = await collectionToHAR(state?.values.toList());
|
||||
return result;
|
||||
// return {
|
||||
// "data": state!.map((e) => e.toJson(includeResponse: false)).toList()
|
||||
// };
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,11 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:apidash/models/models.dart' show SettingsModel;
|
||||
import 'package:apidash/services/services.dart' show hiveHandler, HiveHandler;
|
||||
import '../models/models.dart' show SettingsModel;
|
||||
import '../services/services.dart' show hiveHandler, HiveHandler;
|
||||
import '../consts.dart';
|
||||
|
||||
final codegenLanguageStateProvider = StateProvider<CodegenLanguage>((ref) =>
|
||||
ref.watch(settingsProvider.select((value) => value.defaultCodeGenLang)));
|
||||
|
||||
final StateNotifierProvider<ThemeStateNotifier, SettingsModel>
|
||||
settingsProvider =
|
||||
@ -15,16 +19,20 @@ class ThemeStateNotifier extends StateNotifier<SettingsModel> {
|
||||
|
||||
Future<void> update({
|
||||
bool? isDark,
|
||||
bool? alwaysShowCollectionPaneScrollbar,
|
||||
Size? size,
|
||||
Offset? offset,
|
||||
String? defaultUriScheme,
|
||||
CodegenLanguage? defaultCodeGenLang,
|
||||
bool? saveResponses,
|
||||
}) async {
|
||||
state = state.copyWith(
|
||||
isDark: isDark,
|
||||
alwaysShowCollectionPaneScrollbar: alwaysShowCollectionPaneScrollbar,
|
||||
size: size,
|
||||
offset: offset,
|
||||
defaultUriScheme: defaultUriScheme,
|
||||
defaultCodeGenLang: defaultCodeGenLang,
|
||||
saveResponses: saveResponses,
|
||||
);
|
||||
await hiveHandler.saveSettings(state.toJson());
|
||||
|
@ -1,11 +1,26 @@
|
||||
import 'package:apidash/consts.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
final navRailIndexStateProvider = StateProvider<int?>((ref) => 0);
|
||||
final activeIdStateProvider = StateProvider<String?>((ref) => null);
|
||||
final navRailIndexStateProvider = StateProvider<int>((ref) => 0);
|
||||
final activeIdEditStateProvider = StateProvider<String?>((ref) => null);
|
||||
final sentRequestIdStateProvider = StateProvider<String?>((ref) => null);
|
||||
final codePaneVisibleStateProvider = StateProvider<bool>((ref) => false);
|
||||
final saveDataStateProvider = StateProvider<bool>((ref) => false);
|
||||
final clearDataStateProvider = StateProvider<bool>((ref) => false);
|
||||
final codegenLanguageStateProvider = StateProvider<CodegenLanguage>((ref) => CodegenLanguage.dartHttp);
|
||||
// final nameTextFieldControllerProvider =
|
||||
// StateProvider.autoDispose<TextEditingController>((ref) {
|
||||
// TextEditingController controller = TextEditingController(text: "");
|
||||
// ref.onDispose(() {
|
||||
// controller.dispose();
|
||||
// });
|
||||
// return controller;
|
||||
// });
|
||||
|
||||
final nameTextFieldFocusNodeProvider =
|
||||
StateProvider.autoDispose<FocusNode>((ref) {
|
||||
FocusNode focusNode = FocusNode();
|
||||
ref.onDispose(() {
|
||||
focusNode.dispose();
|
||||
});
|
||||
return focusNode;
|
||||
});
|
||||
|
@ -21,61 +21,60 @@ class _DashboardState extends ConsumerState<Dashboard> {
|
||||
body: SafeArea(
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
NavigationRail(
|
||||
selectedIndex: railIdx,
|
||||
groupAlignment: -1.0,
|
||||
onDestinationSelected: (int index) {
|
||||
setState(() {
|
||||
ref
|
||||
.read(navRailIndexStateProvider.notifier)
|
||||
.update((state) => index);
|
||||
});
|
||||
Column(
|
||||
children: [
|
||||
SizedBox(
|
||||
height: kIsMacOS ? 32.0 : 16.0,
|
||||
width: 64,
|
||||
),
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
IconButton(
|
||||
isSelected: railIdx == 0,
|
||||
onPressed: () {
|
||||
ref.read(navRailIndexStateProvider.notifier).state = 0;
|
||||
},
|
||||
labelType: NavigationRailLabelType.all,
|
||||
leading: SizedBox(height: kIsMacOS ? 24.0 : 8.0),
|
||||
trailing: Expanded(
|
||||
child: Align(
|
||||
alignment: Alignment.bottomCenter,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(bottom: 16.0),
|
||||
child: TextButton(
|
||||
style: (railIdx == null)
|
||||
? TextButton.styleFrom(
|
||||
backgroundColor: Theme.of(context)
|
||||
.colorScheme
|
||||
.secondaryContainer,
|
||||
)
|
||||
: null,
|
||||
onPressed: (railIdx == null)
|
||||
? null
|
||||
: () {
|
||||
ref
|
||||
.read(navRailIndexStateProvider.notifier)
|
||||
.update((state) => null);
|
||||
},
|
||||
child: Icon(
|
||||
(railIdx == null)
|
||||
? Icons.settings
|
||||
: Icons.settings_outlined,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
icon: const Icon(Icons.auto_awesome_mosaic_outlined),
|
||||
selectedIcon: const Icon(Icons.auto_awesome_mosaic),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
destinations: const <NavigationRailDestination>[
|
||||
NavigationRailDestination(
|
||||
icon: Icon(Icons.home_outlined),
|
||||
selectedIcon: Icon(Icons.home),
|
||||
label: Text('Home'),
|
||||
),
|
||||
NavigationRailDestination(
|
||||
icon: Icon(Icons.auto_awesome_mosaic_outlined),
|
||||
selectedIcon: Icon(Icons.auto_awesome_mosaic),
|
||||
label: Text('Requests'),
|
||||
Text(
|
||||
'Requests',
|
||||
style: Theme.of(context).textTheme.labelSmall,
|
||||
),
|
||||
],
|
||||
),
|
||||
Expanded(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 16.0),
|
||||
child: bottomButton(context, railIdx, 1, Icons.help,
|
||||
Icons.help_outline),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 16.0),
|
||||
child: bottomButton(context, railIdx, 2, Icons.settings,
|
||||
Icons.settings_outlined),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
// destinations: const <NavigationRailDestination>[
|
||||
// // NavigationRailDestination(
|
||||
// // icon: Icon(Icons.home_outlined),
|
||||
// // selectedIcon: Icon(Icons.home),
|
||||
// // label: Text('Home'),
|
||||
// // ),
|
||||
// NavigationRailDestination(
|
||||
// icon: Icon(Icons.auto_awesome_mosaic_outlined),
|
||||
// selectedIcon: Icon(Icons.auto_awesome_mosaic),
|
||||
// label: Text('Requests'),
|
||||
// ),
|
||||
// ],
|
||||
),
|
||||
VerticalDivider(
|
||||
thickness: 1,
|
||||
width: 1,
|
||||
@ -84,11 +83,11 @@ class _DashboardState extends ConsumerState<Dashboard> {
|
||||
Expanded(
|
||||
child: IndexedStack(
|
||||
alignment: AlignmentDirectional.topCenter,
|
||||
index: railIdx == null ? 0 : railIdx + 1,
|
||||
index: railIdx,
|
||||
children: const [
|
||||
SettingsPage(),
|
||||
IntroPage(),
|
||||
HomePage(),
|
||||
IntroPage(),
|
||||
SettingsPage(),
|
||||
],
|
||||
),
|
||||
)
|
||||
@ -97,4 +96,30 @@ class _DashboardState extends ConsumerState<Dashboard> {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
TextButton bottomButton(
|
||||
BuildContext context,
|
||||
int railIdx,
|
||||
int buttonIdx,
|
||||
IconData selectedIcon,
|
||||
IconData icon,
|
||||
) {
|
||||
bool isSelected = railIdx == buttonIdx;
|
||||
return TextButton(
|
||||
style: isSelected
|
||||
? TextButton.styleFrom(
|
||||
backgroundColor: Theme.of(context).colorScheme.secondaryContainer,
|
||||
)
|
||||
: null,
|
||||
onPressed: isSelected
|
||||
? null
|
||||
: () {
|
||||
ref.read(navRailIndexStateProvider.notifier).state = buttonIdx;
|
||||
},
|
||||
child: Icon(
|
||||
isSelected ? selectedIcon : icon,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -5,23 +5,13 @@ import 'package:apidash/widgets/widgets.dart';
|
||||
import 'package:apidash/models/models.dart';
|
||||
import 'package:apidash/consts.dart';
|
||||
|
||||
class CollectionPane extends ConsumerStatefulWidget {
|
||||
class CollectionPane extends ConsumerWidget {
|
||||
const CollectionPane({
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
ConsumerState<CollectionPane> createState() => _CollectionPaneState();
|
||||
}
|
||||
|
||||
class _CollectionPaneState extends ConsumerState<CollectionPane> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
var sm = ScaffoldMessenger.of(context);
|
||||
final collection = ref.watch(collectionStateNotifierProvider);
|
||||
final savingData = ref.watch(saveDataStateProvider);
|
||||
@ -31,11 +21,13 @@ class _CollectionPaneState extends ConsumerState<CollectionPane> {
|
||||
);
|
||||
}
|
||||
return Padding(
|
||||
padding: kIsMacOS ? kPt24o8 : kP8,
|
||||
padding: kIsMacOS ? kP24CollectionPane : kP8CollectionPane,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
Wrap(
|
||||
Padding(
|
||||
padding: kPr8CollectionPane,
|
||||
child: Wrap(
|
||||
alignment: WrapAlignment.spaceBetween,
|
||||
children: [
|
||||
TextButton.icon(
|
||||
@ -70,6 +62,7 @@ class _CollectionPaneState extends ConsumerState<CollectionPane> {
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
kVSpacer8,
|
||||
const Expanded(
|
||||
child: RequestList(),
|
||||
@ -82,25 +75,44 @@ class _CollectionPaneState extends ConsumerState<CollectionPane> {
|
||||
|
||||
class RequestList extends ConsumerStatefulWidget {
|
||||
const RequestList({
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
ConsumerState<RequestList> createState() => _RequestListState();
|
||||
}
|
||||
|
||||
class _RequestListState extends ConsumerState<RequestList> {
|
||||
late final ScrollController controller;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
controller = ScrollController();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
controller.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final requestSequence = ref.watch(requestSequenceProvider);
|
||||
final requestItems = ref.watch(collectionStateNotifierProvider)!;
|
||||
return ReorderableListView.builder(
|
||||
final alwaysShowCollectionPaneScrollbar = ref.watch(settingsProvider
|
||||
.select((value) => value.alwaysShowCollectionPaneScrollbar));
|
||||
|
||||
return Scrollbar(
|
||||
controller: controller,
|
||||
thumbVisibility: alwaysShowCollectionPaneScrollbar ? true : null,
|
||||
radius: const Radius.circular(12),
|
||||
child: ReorderableListView.builder(
|
||||
padding: kPr8CollectionPane,
|
||||
scrollController: controller,
|
||||
buildDefaultDragHandles: false,
|
||||
itemCount: requestItems.length,
|
||||
itemCount: requestSequence.length,
|
||||
onReorder: (int oldIndex, int newIndex) {
|
||||
if (oldIndex < newIndex) {
|
||||
newIndex -= 1;
|
||||
@ -112,63 +124,55 @@ class _RequestListState extends ConsumerState<RequestList> {
|
||||
}
|
||||
},
|
||||
itemBuilder: (context, index) {
|
||||
var id = requestSequence[index];
|
||||
return ReorderableDragStartListener(
|
||||
key: Key(requestItems[index].id),
|
||||
key: ValueKey(id),
|
||||
index: index,
|
||||
child: Padding(
|
||||
padding: kP1,
|
||||
child: RequestItem(
|
||||
id: requestItems[index].id,
|
||||
requestModel: requestItems[index],
|
||||
id: id,
|
||||
requestModel: requestItems[id]!,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class RequestItem extends ConsumerStatefulWidget {
|
||||
class RequestItem extends ConsumerWidget {
|
||||
const RequestItem({
|
||||
super.key,
|
||||
required this.id,
|
||||
required this.requestModel,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
final String id;
|
||||
final RequestModel requestModel;
|
||||
|
||||
@override
|
||||
ConsumerState<RequestItem> createState() => _RequestItemState();
|
||||
}
|
||||
|
||||
class _RequestItemState extends ConsumerState<RequestItem> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final activeRequestId = ref.watch(activeIdStateProvider);
|
||||
final editRequestId = ref.watch(activeIdEditStateProvider);
|
||||
|
||||
return SidebarRequestCard(
|
||||
id: widget.id,
|
||||
method: widget.requestModel.method,
|
||||
name: widget.requestModel.name,
|
||||
url: widget.requestModel.url,
|
||||
id: id,
|
||||
method: requestModel.method,
|
||||
name: requestModel.name,
|
||||
url: requestModel.url,
|
||||
activeRequestId: activeRequestId,
|
||||
editRequestId: editRequestId,
|
||||
onTap: () {
|
||||
ref.read(activeIdStateProvider.notifier).update((state) => widget.id);
|
||||
},
|
||||
onDoubleTap: () {
|
||||
ref.read(activeIdStateProvider.notifier).update((state) => widget.id);
|
||||
ref
|
||||
.read(activeIdEditStateProvider.notifier)
|
||||
.update((state) => widget.id);
|
||||
ref.read(activeIdStateProvider.notifier).state = id;
|
||||
},
|
||||
// onDoubleTap: () {
|
||||
// ref.read(activeIdStateProvider.notifier).state = id;
|
||||
// ref.read(activeIdEditStateProvider.notifier).state = id;
|
||||
// },
|
||||
// controller: ref.watch(nameTextFieldControllerProvider),
|
||||
focusNode: ref.watch(nameTextFieldFocusNodeProvider),
|
||||
onChangedNameEditor: (value) {
|
||||
value = value.trim();
|
||||
ref
|
||||
@ -176,16 +180,30 @@ class _RequestItemState extends ConsumerState<RequestItem> {
|
||||
.update(editRequestId!, name: value);
|
||||
},
|
||||
onTapOutsideNameEditor: () {
|
||||
ref.read(activeIdEditStateProvider.notifier).update((state) => null);
|
||||
ref.read(activeIdEditStateProvider.notifier).state = null;
|
||||
},
|
||||
onMenuSelected: (RequestItemMenuOption item) {
|
||||
if (item == RequestItemMenuOption.edit) {
|
||||
// var controller =
|
||||
// ref.read(nameTextFieldControllerProvider.notifier).state;
|
||||
// controller.text = requestModel.name;
|
||||
// controller.selection = TextSelection.fromPosition(
|
||||
// TextPosition(offset: controller.text.length),
|
||||
// );
|
||||
ref.read(activeIdEditStateProvider.notifier).state = id;
|
||||
Future.delayed(
|
||||
const Duration(milliseconds: 150),
|
||||
() => ref
|
||||
.read(nameTextFieldFocusNodeProvider.notifier)
|
||||
.state
|
||||
.requestFocus(),
|
||||
);
|
||||
}
|
||||
if (item == RequestItemMenuOption.delete) {
|
||||
ref.read(collectionStateNotifierProvider.notifier).remove(widget.id);
|
||||
ref.read(collectionStateNotifierProvider.notifier).remove(id);
|
||||
}
|
||||
if (item == RequestItemMenuOption.duplicate) {
|
||||
ref
|
||||
.read(collectionStateNotifierProvider.notifier)
|
||||
.duplicate(widget.id);
|
||||
ref.read(collectionStateNotifierProvider.notifier).duplicate(id);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
@ -40,9 +40,7 @@ class _CodePaneState extends ConsumerState<CodePane> {
|
||||
code: code,
|
||||
codegenLanguage: codegenLanguage,
|
||||
onChangedCodegenLanguage: (CodegenLanguage? value) {
|
||||
ref
|
||||
.read(codegenLanguageStateProvider.notifier)
|
||||
.update((state) => value!);
|
||||
ref.read(codegenLanguageStateProvider.notifier).state = value!;
|
||||
},
|
||||
);
|
||||
}
|
||||
|
@ -25,11 +25,11 @@ class _EditRequestBodyState extends ConsumerState<EditRequestBody> {
|
||||
margin: kPt5o10,
|
||||
child: Column(
|
||||
children: [
|
||||
SizedBox(
|
||||
const SizedBox(
|
||||
height: kHeaderHeight,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: const [
|
||||
children: [
|
||||
Text(
|
||||
"Select Content Type:",
|
||||
),
|
||||
@ -41,7 +41,7 @@ class _EditRequestBodyState extends ConsumerState<EditRequestBody> {
|
||||
child: TextFieldEditor(
|
||||
key: Key("$activeId-body"),
|
||||
fieldKey: "$activeId-body-editor",
|
||||
initialValue: requestModel.requestBody,
|
||||
initialValue: requestModel?.requestBody,
|
||||
onChanged: (String value) {
|
||||
ref
|
||||
.read(collectionStateNotifierProvider.notifier)
|
||||
|
@ -8,14 +8,14 @@ import 'package:apidash/models/models.dart';
|
||||
import 'package:apidash/consts.dart';
|
||||
|
||||
class EditRequestHeaders extends ConsumerStatefulWidget {
|
||||
const EditRequestHeaders({Key? key}) : super(key: key);
|
||||
const EditRequestHeaders({super.key});
|
||||
|
||||
@override
|
||||
ConsumerState<EditRequestHeaders> createState() => EditRequestHeadersState();
|
||||
}
|
||||
|
||||
class EditRequestHeadersState extends ConsumerState<EditRequestHeaders> {
|
||||
late List<KVRow> rows;
|
||||
late List<NameValueModel> rows;
|
||||
final random = Random.secure();
|
||||
late int seed;
|
||||
|
||||
@ -37,9 +37,13 @@ class EditRequestHeadersState extends ConsumerState<EditRequestHeaders> {
|
||||
final length = ref.watch(activeRequestModelProvider
|
||||
.select((value) => value?.requestHeaders?.length));
|
||||
var rH = ref.read(activeRequestModelProvider)?.requestHeaders;
|
||||
rows = (rH == null || rH.isEmpty) ? [const KVRow("", "")] : rH;
|
||||
rows = (rH == null || rH.isEmpty)
|
||||
? [
|
||||
kNameValueEmptyModel,
|
||||
]
|
||||
: rH;
|
||||
|
||||
DaviModel<KVRow> model = DaviModel<KVRow>(
|
||||
DaviModel<NameValueModel> model = DaviModel<NameValueModel>(
|
||||
rows: rows,
|
||||
columns: [
|
||||
DaviColumn(
|
||||
@ -47,12 +51,12 @@ class EditRequestHeadersState extends ConsumerState<EditRequestHeaders> {
|
||||
grow: 1,
|
||||
cellBuilder: (_, row) {
|
||||
int idx = row.index;
|
||||
return CellField(
|
||||
return HeaderField(
|
||||
keyId: "$activeId-$idx-headers-k-$seed",
|
||||
initialValue: rows[idx].k,
|
||||
initialValue: rows[idx].name,
|
||||
hintText: "Add Header Name",
|
||||
onChanged: (value) {
|
||||
rows[idx] = rows[idx].copyWith(k: value);
|
||||
rows[idx] = rows[idx].copyWith(name: value);
|
||||
_onFieldChange(activeId!);
|
||||
},
|
||||
colorScheme: Theme.of(context).colorScheme,
|
||||
@ -76,10 +80,10 @@ class EditRequestHeadersState extends ConsumerState<EditRequestHeaders> {
|
||||
int idx = row.index;
|
||||
return CellField(
|
||||
keyId: "$activeId-$idx-headers-v-$seed",
|
||||
initialValue: rows[idx].v,
|
||||
initialValue: rows[idx].value,
|
||||
hintText: " Add Header Value",
|
||||
onChanged: (value) {
|
||||
rows[idx] = rows[idx].copyWith(v: value);
|
||||
rows[idx] = rows[idx].copyWith(value: value);
|
||||
_onFieldChange(activeId!);
|
||||
},
|
||||
colorScheme: Theme.of(context).colorScheme,
|
||||
@ -96,11 +100,16 @@ class EditRequestHeadersState extends ConsumerState<EditRequestHeaders> {
|
||||
? kIconRemoveDark
|
||||
: kIconRemoveLight,
|
||||
onTap: () {
|
||||
if (rows.length == 1) {
|
||||
return;
|
||||
}
|
||||
rows.removeAt(row.index);
|
||||
seed = random.nextInt(kRandMax);
|
||||
if (rows.length == 1) {
|
||||
setState(() {
|
||||
rows = [
|
||||
kNameValueEmptyModel,
|
||||
];
|
||||
});
|
||||
} else {
|
||||
rows.removeAt(row.index);
|
||||
}
|
||||
_onFieldChange(activeId!);
|
||||
},
|
||||
);
|
||||
@ -121,7 +130,7 @@ class EditRequestHeadersState extends ConsumerState<EditRequestHeaders> {
|
||||
Expanded(
|
||||
child: DaviTheme(
|
||||
data: kTableThemeData,
|
||||
child: Davi<KVRow>(model),
|
||||
child: Davi<NameValueModel>(model),
|
||||
),
|
||||
),
|
||||
],
|
||||
@ -133,7 +142,7 @@ class EditRequestHeadersState extends ConsumerState<EditRequestHeaders> {
|
||||
padding: const EdgeInsets.only(bottom: 30),
|
||||
child: ElevatedButton.icon(
|
||||
onPressed: () {
|
||||
rows.add(const KVRow("", ""));
|
||||
rows.add(kNameValueEmptyModel);
|
||||
_onFieldChange(activeId!);
|
||||
},
|
||||
icon: const Icon(Icons.add),
|
||||
|
@ -18,25 +18,34 @@ class _EditRequestPaneState extends ConsumerState<EditRequestPane> {
|
||||
Widget build(BuildContext context) {
|
||||
final activeId = ref.watch(activeIdStateProvider);
|
||||
final codePaneVisible = ref.watch(codePaneVisibleStateProvider);
|
||||
var index = ref
|
||||
.read(collectionStateNotifierProvider.notifier)
|
||||
.getRequestModel(activeId!)
|
||||
.requestTabIndex;
|
||||
final tabIndex = ref.watch(
|
||||
activeRequestModelProvider.select((value) => value?.requestTabIndex));
|
||||
|
||||
final headerLength = ref.watch(
|
||||
activeRequestModelProvider.select((value) => value?.headersMap.length));
|
||||
final paramLength = ref.watch(
|
||||
activeRequestModelProvider.select((value) => value?.paramsMap.length));
|
||||
final bodyLength = ref.watch(activeRequestModelProvider
|
||||
.select((value) => value?.requestBody?.length));
|
||||
|
||||
return RequestPane(
|
||||
activeId: activeId,
|
||||
codePaneVisible: codePaneVisible,
|
||||
tabIndex: index,
|
||||
tabIndex: tabIndex,
|
||||
onPressedCodeButton: () {
|
||||
ref
|
||||
.read(codePaneVisibleStateProvider.notifier)
|
||||
.update((state) => !codePaneVisible);
|
||||
ref.read(codePaneVisibleStateProvider.notifier).state =
|
||||
!codePaneVisible;
|
||||
},
|
||||
onTapTabBar: (index) {
|
||||
ref
|
||||
.read(collectionStateNotifierProvider.notifier)
|
||||
.update(activeId, requestTabIndex: index);
|
||||
.update(activeId!, requestTabIndex: index);
|
||||
},
|
||||
showIndicators: [
|
||||
paramLength != null && paramLength > 0,
|
||||
headerLength != null && headerLength > 0,
|
||||
bodyLength != null && bodyLength > 0,
|
||||
],
|
||||
children: const [
|
||||
EditRequestURLParams(),
|
||||
EditRequestHeaders(),
|
||||
|
@ -8,7 +8,7 @@ import 'package:apidash/models/models.dart';
|
||||
import 'package:apidash/consts.dart';
|
||||
|
||||
class EditRequestURLParams extends ConsumerStatefulWidget {
|
||||
const EditRequestURLParams({Key? key}) : super(key: key);
|
||||
const EditRequestURLParams({super.key});
|
||||
|
||||
@override
|
||||
ConsumerState<EditRequestURLParams> createState() =>
|
||||
@ -16,7 +16,7 @@ class EditRequestURLParams extends ConsumerStatefulWidget {
|
||||
}
|
||||
|
||||
class EditRequestURLParamsState extends ConsumerState<EditRequestURLParams> {
|
||||
late List<KVRow> rows;
|
||||
late List<NameValueModel> rows;
|
||||
final random = Random.secure();
|
||||
late int seed;
|
||||
|
||||
@ -38,9 +38,13 @@ class EditRequestURLParamsState extends ConsumerState<EditRequestURLParams> {
|
||||
final length = ref.watch(activeRequestModelProvider
|
||||
.select((value) => value?.requestParams?.length));
|
||||
var rP = ref.read(activeRequestModelProvider)?.requestParams;
|
||||
rows = (rP == null || rP.isEmpty) ? [const KVRow("", "")] : rP;
|
||||
rows = (rP == null || rP.isEmpty)
|
||||
? [
|
||||
kNameValueEmptyModel,
|
||||
]
|
||||
: rP;
|
||||
|
||||
DaviModel<KVRow> model = DaviModel<KVRow>(
|
||||
DaviModel<NameValueModel> model = DaviModel<NameValueModel>(
|
||||
rows: rows,
|
||||
columns: [
|
||||
DaviColumn(
|
||||
@ -50,10 +54,10 @@ class EditRequestURLParamsState extends ConsumerState<EditRequestURLParams> {
|
||||
int idx = row.index;
|
||||
return CellField(
|
||||
keyId: "$activeId-$idx-params-k-$seed",
|
||||
initialValue: rows[idx].k,
|
||||
initialValue: rows[idx].name,
|
||||
hintText: "Add URL Parameter",
|
||||
onChanged: (value) {
|
||||
rows[idx] = rows[idx].copyWith(k: value);
|
||||
rows[idx] = rows[idx].copyWith(name: value);
|
||||
_onFieldChange(activeId!);
|
||||
},
|
||||
colorScheme: Theme.of(context).colorScheme,
|
||||
@ -77,10 +81,10 @@ class EditRequestURLParamsState extends ConsumerState<EditRequestURLParams> {
|
||||
int idx = row.index;
|
||||
return CellField(
|
||||
keyId: "$activeId-$idx-params-v-$seed",
|
||||
initialValue: rows[idx].v,
|
||||
initialValue: rows[idx].value,
|
||||
hintText: "Add Value",
|
||||
onChanged: (value) {
|
||||
rows[idx] = rows[idx].copyWith(v: value);
|
||||
rows[idx] = rows[idx].copyWith(value: value);
|
||||
_onFieldChange(activeId!);
|
||||
},
|
||||
colorScheme: Theme.of(context).colorScheme,
|
||||
@ -97,11 +101,16 @@ class EditRequestURLParamsState extends ConsumerState<EditRequestURLParams> {
|
||||
? kIconRemoveDark
|
||||
: kIconRemoveLight,
|
||||
onTap: () {
|
||||
if (rows.length == 1) {
|
||||
return;
|
||||
}
|
||||
rows.removeAt(row.index);
|
||||
seed = random.nextInt(kRandMax);
|
||||
if (rows.length == 1) {
|
||||
setState(() {
|
||||
rows = [
|
||||
kNameValueEmptyModel,
|
||||
];
|
||||
});
|
||||
} else {
|
||||
rows.removeAt(row.index);
|
||||
}
|
||||
_onFieldChange(activeId!);
|
||||
},
|
||||
);
|
||||
@ -122,7 +131,7 @@ class EditRequestURLParamsState extends ConsumerState<EditRequestURLParams> {
|
||||
Expanded(
|
||||
child: DaviTheme(
|
||||
data: kTableThemeData,
|
||||
child: Davi<KVRow>(model),
|
||||
child: Davi<NameValueModel>(model),
|
||||
),
|
||||
),
|
||||
],
|
||||
@ -134,7 +143,7 @@ class EditRequestURLParamsState extends ConsumerState<EditRequestURLParams> {
|
||||
padding: const EdgeInsets.only(bottom: 30),
|
||||
child: ElevatedButton.icon(
|
||||
onPressed: () {
|
||||
rows.add(const KVRow("", ""));
|
||||
rows.add(kNameValueEmptyModel);
|
||||
_onFieldChange(activeId!);
|
||||
},
|
||||
icon: const Icon(Icons.add),
|
||||
|
@ -7,8 +7,8 @@ import 'url_card.dart';
|
||||
|
||||
class RequestEditorPane extends ConsumerStatefulWidget {
|
||||
const RequestEditorPane({
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
ConsumerState<RequestEditorPane> createState() => _RequestEditorPaneState();
|
||||
@ -28,8 +28,8 @@ class _RequestEditorPaneState extends ConsumerState<RequestEditorPane> {
|
||||
} else {
|
||||
return Padding(
|
||||
padding: kIsMacOS ? kPt24o8 : kP8,
|
||||
child: Column(
|
||||
children: const [
|
||||
child: const Column(
|
||||
children: [
|
||||
EditorPaneRequestURLCard(),
|
||||
kVSpacer10,
|
||||
Expanded(
|
||||
|
@ -28,13 +28,13 @@ class _EditorPaneRequestURLCardState extends State<EditorPaneRequestURLCard> {
|
||||
),
|
||||
borderRadius: kBorderRadius12,
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
child: const Padding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
vertical: 5,
|
||||
horizontal: 20,
|
||||
),
|
||||
child: Row(
|
||||
children: const [
|
||||
children: [
|
||||
DropdownButtonHTTPMethod(),
|
||||
kHSpacer20,
|
||||
Expanded(
|
||||
@ -108,7 +108,7 @@ class _URLTextFieldState extends ConsumerState<URLTextField> {
|
||||
initialValue: ref
|
||||
.read(collectionStateNotifierProvider.notifier)
|
||||
.getRequestModel(activeId)
|
||||
.url,
|
||||
?.url,
|
||||
onChanged: (value) {
|
||||
ref
|
||||
.read(collectionStateNotifierProvider.notifier)
|
||||
|
@ -4,7 +4,7 @@ import 'editor_pane/editor_pane.dart';
|
||||
import 'collection_pane.dart';
|
||||
|
||||
class HomePage extends StatefulWidget {
|
||||
const HomePage({Key? key}) : super(key: key);
|
||||
const HomePage({super.key});
|
||||
|
||||
@override
|
||||
HomePageState createState() => HomePageState();
|
||||
@ -13,8 +13,8 @@ class HomePage extends StatefulWidget {
|
||||
class HomePageState extends State<HomePage> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
children: const [
|
||||
return const Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: DashboardSplitView(
|
||||
sidebarWidget: CollectionPane(),
|
||||
|
@ -1,7 +1,8 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:apidash/providers/providers.dart';
|
||||
import 'package:apidash/widgets/widgets.dart';
|
||||
import '../providers/providers.dart';
|
||||
import '../widgets/widgets.dart';
|
||||
import '../utils/utils.dart';
|
||||
import 'package:apidash/consts.dart';
|
||||
|
||||
class SettingsPage extends ConsumerStatefulWidget {
|
||||
@ -42,6 +43,19 @@ class _SettingsPageState extends ConsumerState<SettingsPage> {
|
||||
ref.read(settingsProvider.notifier).update(isDark: value);
|
||||
},
|
||||
),
|
||||
SwitchListTile(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
hoverColor: kColorTransparent,
|
||||
title: const Text('Collection Pane Scrollbar Visiblity'),
|
||||
subtitle: Text(
|
||||
'Current selection: ${settings.alwaysShowCollectionPaneScrollbar ? "Always show" : "Show only when scrolling"}'),
|
||||
value: settings.alwaysShowCollectionPaneScrollbar,
|
||||
onChanged: (bool? value) {
|
||||
ref
|
||||
.read(settingsProvider.notifier)
|
||||
.update(alwaysShowCollectionPaneScrollbar: value);
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
hoverColor: kColorTransparent,
|
||||
@ -63,6 +77,25 @@ class _SettingsPageState extends ConsumerState<SettingsPage> {
|
||||
);
|
||||
}).toList()),
|
||||
),
|
||||
ListTile(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
hoverColor: kColorTransparent,
|
||||
title: const Text('Default Code Generator'),
|
||||
trailing: DropdownMenu(
|
||||
onSelected: (value) {
|
||||
ref
|
||||
.read(settingsProvider.notifier)
|
||||
.update(defaultCodeGenLang: value);
|
||||
},
|
||||
initialSelection: settings.defaultCodeGenLang,
|
||||
dropdownMenuEntries: CodegenLanguage.values
|
||||
.map<DropdownMenuEntry<CodegenLanguage>>((value) {
|
||||
return DropdownMenuEntry<CodegenLanguage>(
|
||||
value: value,
|
||||
label: value.label,
|
||||
);
|
||||
}).toList()),
|
||||
),
|
||||
CheckboxListTile(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
title: const Text("Save Responses"),
|
||||
@ -75,6 +108,34 @@ class _SettingsPageState extends ConsumerState<SettingsPage> {
|
||||
.update(saveResponses: value);
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
hoverColor: kColorTransparent,
|
||||
title: const Text('Export Data'),
|
||||
subtitle: const Text(
|
||||
'Export your collection to HAR (HTTP Archive format).\nVersion control this file or import in other API clients.'),
|
||||
trailing: FilledButton(
|
||||
onPressed: () async {
|
||||
var message = "";
|
||||
try {
|
||||
var data = await ref
|
||||
.read(collectionStateNotifierProvider.notifier)
|
||||
.exportDataToHAR();
|
||||
var pth = await getFileDownloadpath(null, "har");
|
||||
if (pth != null) {
|
||||
await saveFile(pth, jsonMapToBytes(data));
|
||||
var sp = getShortPath(pth);
|
||||
message = 'Saved to $sp';
|
||||
}
|
||||
} catch (e) {
|
||||
message = "An error occurred while exporting.";
|
||||
}
|
||||
sm.hideCurrentSnackBar();
|
||||
sm.showSnackBar(getSnackBar(message, small: false));
|
||||
},
|
||||
child: const Text("Export Data"),
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
hoverColor: kColorTransparent,
|
||||
|
@ -7,66 +7,59 @@ import 'package:apidash/models/models.dart';
|
||||
import 'package:apidash/consts.dart';
|
||||
|
||||
Future<(http.Response?, Duration?, String?)> request(
|
||||
RequestModel requestModel,
|
||||
{String defaultUriScheme = kDefaultUriScheme}
|
||||
) async {
|
||||
(Uri?, String?) uriRec = getValidRequestUri(requestModel.url,
|
||||
RequestModel requestModel, {
|
||||
String defaultUriScheme = kDefaultUriScheme,
|
||||
}) async {
|
||||
(Uri?, String?) uriRec = getValidRequestUri(
|
||||
requestModel.url,
|
||||
requestModel.requestParams,
|
||||
defaultUriScheme: defaultUriScheme);
|
||||
if(uriRec.$0 != null){
|
||||
Uri requestUrl = uriRec.$0!;
|
||||
Map<String, String> headers = rowsToMap(requestModel.requestHeaders) ?? {};
|
||||
defaultUriScheme: defaultUriScheme,
|
||||
);
|
||||
if (uriRec.$1 != null) {
|
||||
Uri requestUrl = uriRec.$1!;
|
||||
Map<String, String> headers = requestModel.headersMap;
|
||||
http.Response response;
|
||||
String? body;
|
||||
try {
|
||||
var requestBody = requestModel.requestBody;
|
||||
if(kMethodsWithBody.contains(requestModel.method) && requestBody != null){
|
||||
if (kMethodsWithBody.contains(requestModel.method) &&
|
||||
requestBody != null) {
|
||||
var contentLength = utf8.encode(requestBody).length;
|
||||
if (contentLength > 0){
|
||||
if (contentLength > 0) {
|
||||
body = requestBody;
|
||||
headers[HttpHeaders.contentLengthHeader] = contentLength.toString();
|
||||
headers[HttpHeaders.contentTypeHeader] = kContentTypeMap[requestModel.requestBodyContentType] ?? "";
|
||||
headers[HttpHeaders.contentTypeHeader] =
|
||||
kContentTypeMap[requestModel.requestBodyContentType] ?? "";
|
||||
}
|
||||
}
|
||||
Stopwatch stopwatch = Stopwatch()..start();
|
||||
switch(requestModel.method){
|
||||
switch (requestModel.method) {
|
||||
case HTTPVerb.get:
|
||||
response = await http.get(requestUrl,
|
||||
headers: headers);
|
||||
response = await http.get(requestUrl, headers: headers);
|
||||
break;
|
||||
case HTTPVerb.head:
|
||||
response = await http.head(requestUrl,
|
||||
headers: headers);
|
||||
response = await http.head(requestUrl, headers: headers);
|
||||
break;
|
||||
case HTTPVerb.post:
|
||||
response = await http.post(requestUrl,
|
||||
headers: headers,
|
||||
body: body);
|
||||
response = await http.post(requestUrl, headers: headers, body: body);
|
||||
break;
|
||||
case HTTPVerb.put:
|
||||
response = await http.put(requestUrl,
|
||||
headers: headers,
|
||||
body: body);
|
||||
response = await http.put(requestUrl, headers: headers, body: body);
|
||||
break;
|
||||
case HTTPVerb.patch:
|
||||
response = await http.patch(requestUrl,
|
||||
headers: headers,
|
||||
body: body);
|
||||
response = await http.patch(requestUrl, headers: headers, body: body);
|
||||
break;
|
||||
case HTTPVerb.delete:
|
||||
response = await http.delete(requestUrl,
|
||||
headers: headers,
|
||||
body: body);
|
||||
response =
|
||||
await http.delete(requestUrl, headers: headers, body: body);
|
||||
break;
|
||||
}
|
||||
stopwatch.stop();
|
||||
return (response, stopwatch.elapsed, null);
|
||||
}
|
||||
catch (e) {
|
||||
} catch (e) {
|
||||
return (null, null, e.toString());
|
||||
}
|
||||
}
|
||||
else {
|
||||
return (null, null, uriRec.$1);
|
||||
} else {
|
||||
return (null, null, uriRec.$2);
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import 'dart:typed_data';
|
||||
import 'dart:convert';
|
||||
import '../models/models.dart';
|
||||
import '../consts.dart';
|
||||
import 'package:apidash/models/models.dart' show KVRow;
|
||||
|
||||
String humanizeDuration(Duration? duration) {
|
||||
if (duration == null) {
|
||||
@ -60,30 +60,31 @@ String padMultilineString(String text, int padding,
|
||||
return lines.join("\n");
|
||||
}
|
||||
|
||||
Map<String, String>? rowsToMap(List<KVRow>? kvRows, {bool isHeader = false}) {
|
||||
Map<String, String>? rowsToMap(List<NameValueModel>? kvRows,
|
||||
{bool isHeader = false}) {
|
||||
if (kvRows == null) {
|
||||
return null;
|
||||
}
|
||||
Map<String, String> finalMap = {};
|
||||
for (var row in kvRows) {
|
||||
if (row.k.trim() != "") {
|
||||
String key = row.k;
|
||||
if (row.name.trim() != "") {
|
||||
String key = row.name;
|
||||
if (isHeader) {
|
||||
key = key.toLowerCase();
|
||||
}
|
||||
finalMap[key] = row.v.toString();
|
||||
finalMap[key] = row.value.toString();
|
||||
}
|
||||
}
|
||||
return finalMap;
|
||||
}
|
||||
|
||||
List<KVRow>? mapToRows(Map<String, String>? kvMap) {
|
||||
List<NameValueModel>? mapToRows(Map<String, String>? kvMap) {
|
||||
if (kvMap == null) {
|
||||
return null;
|
||||
}
|
||||
List<KVRow> finalRows = [];
|
||||
List<NameValueModel> finalRows = [];
|
||||
for (var k in kvMap.keys) {
|
||||
finalRows.add(KVRow(k, kvMap[k]));
|
||||
finalRows.add(NameValueModel(name: k, value: kvMap[k]));
|
||||
}
|
||||
return finalRows;
|
||||
}
|
||||
@ -97,3 +98,14 @@ Uint8List? stringToBytes(String? text) {
|
||||
return bytes;
|
||||
}
|
||||
}
|
||||
|
||||
Uint8List jsonMapToBytes(Map<String, dynamic>? map) {
|
||||
if (map == null) {
|
||||
return Uint8List.fromList([]);
|
||||
} else {
|
||||
String text = kEncoder.convert(map);
|
||||
var l = utf8.encode(text);
|
||||
var bytes = Uint8List.fromList(l);
|
||||
return bytes;
|
||||
}
|
||||
}
|
||||
|
160
lib/utils/har_utils.dart
Normal file
160
lib/utils/har_utils.dart
Normal file
@ -0,0 +1,160 @@
|
||||
import 'dart:convert';
|
||||
import 'package:apidash/consts.dart';
|
||||
import 'package:apidash/utils/utils.dart' show getValidRequestUri;
|
||||
import 'package:apidash/models/models.dart' show RequestModel;
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
|
||||
Future<Map<String, dynamic>> collectionToHAR(
|
||||
List<RequestModel>? collection) async {
|
||||
Map<String, dynamic> harJson = {
|
||||
"log": {
|
||||
"creator": {
|
||||
"comment": "For support, check out API Dash repo - $kGitUrl",
|
||||
"version": (await PackageInfo.fromPlatform()).version,
|
||||
"name": "API Dash"
|
||||
},
|
||||
"entries": <Map<String, dynamic>>[],
|
||||
"comment": "",
|
||||
"browser": {
|
||||
"version": (await PackageInfo.fromPlatform()).version,
|
||||
"comment": "",
|
||||
"name": "API Dash"
|
||||
},
|
||||
"version": "1.2"
|
||||
}
|
||||
};
|
||||
|
||||
if (collection != null) {
|
||||
for (final req in collection) {
|
||||
harJson["log"]["entries"].add(entryToHAR(req));
|
||||
}
|
||||
}
|
||||
return harJson;
|
||||
}
|
||||
|
||||
Map<String, dynamic> entryToHAR(RequestModel requestModel) {
|
||||
Map<String, dynamic> entryJson = {
|
||||
"startedDateTime": DateTime.now().toUtc().toIso8601String(),
|
||||
"comment":
|
||||
"${requestModel.name.isNotEmpty ? '${requestModel.name} | ' : ''}id:${requestModel.id}",
|
||||
"serverIPAddress": "",
|
||||
"time": 0,
|
||||
"timings": {
|
||||
"connect": -1,
|
||||
"comment": "",
|
||||
"blocked": -1,
|
||||
"dns": -1,
|
||||
"receive": 0,
|
||||
"send": 0,
|
||||
"wait": 0,
|
||||
"ssl": -1
|
||||
},
|
||||
"response": {
|
||||
"status": 200,
|
||||
"statusText": "OK",
|
||||
"httpVersion": "HTTP/1.1",
|
||||
"cookies": [],
|
||||
"headers": [],
|
||||
"content": {"size": 0, "mimeType": "", "comment": "", "text": ""},
|
||||
"redirectURL": "",
|
||||
"headersSize": 0,
|
||||
"bodySize": 0,
|
||||
"comment": ""
|
||||
},
|
||||
"request": requestModelToHARJsonRequest(
|
||||
requestModel,
|
||||
exportMode: true,
|
||||
),
|
||||
"cache": {}
|
||||
};
|
||||
return entryJson;
|
||||
}
|
||||
|
||||
Map<String, dynamic> requestModelToHARJsonRequest(
|
||||
RequestModel requestModel, {
|
||||
defaultUriScheme = kDefaultUriScheme,
|
||||
bool exportMode = false,
|
||||
}) {
|
||||
Map<String, dynamic> json = {};
|
||||
bool hasBody = false;
|
||||
|
||||
var rec = getValidRequestUri(
|
||||
requestModel.url,
|
||||
requestModel.requestParams,
|
||||
defaultUriScheme: defaultUriScheme,
|
||||
);
|
||||
|
||||
Uri? uri = rec.$1;
|
||||
var u = "";
|
||||
if (uri != null) {
|
||||
u = uri.toString();
|
||||
if (u[u.length - 1] == "?") {
|
||||
u = u.substring(0, u.length - 1);
|
||||
}
|
||||
|
||||
json["method"] = requestModel.method.name.toUpperCase();
|
||||
json["url"] = u;
|
||||
json["httpVersion"] = "HTTP/1.1";
|
||||
json["queryString"] = [];
|
||||
json["headers"] = [];
|
||||
|
||||
var params = uri.queryParameters;
|
||||
if (params.isNotEmpty) {
|
||||
for (final k in params.keys) {
|
||||
var m = {"name": k, "value": params[k]};
|
||||
if (exportMode) {
|
||||
m["comment"] = "";
|
||||
}
|
||||
json["queryString"].add(m);
|
||||
}
|
||||
}
|
||||
|
||||
var method = requestModel.method;
|
||||
var requestBody = requestModel.requestBody;
|
||||
if (kMethodsWithBody.contains(method) && requestBody != null) {
|
||||
var contentLength = utf8.encode(requestBody).length;
|
||||
if (contentLength > 0) {
|
||||
hasBody = true;
|
||||
json["postData"] = {};
|
||||
json["postData"]["mimeType"] =
|
||||
kContentTypeMap[requestModel.requestBodyContentType] ?? "";
|
||||
json["postData"]["text"] = requestBody;
|
||||
if (exportMode) {
|
||||
json["postData"]["comment"] = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var headersList = requestModel.requestHeaders;
|
||||
if (headersList != null || hasBody) {
|
||||
var headers = requestModel.headersMap;
|
||||
if (headers.isNotEmpty || hasBody) {
|
||||
if (hasBody) {
|
||||
var m = {
|
||||
"name": "Content-Type",
|
||||
"value": kContentTypeMap[requestModel.requestBodyContentType] ?? ""
|
||||
};
|
||||
if (exportMode) {
|
||||
m["comment"] = "";
|
||||
}
|
||||
json["headers"].add(m);
|
||||
}
|
||||
for (final k in headers.keys) {
|
||||
var m = {"name": k, "value": headers[k]};
|
||||
if (exportMode) {
|
||||
m["comment"] = "";
|
||||
}
|
||||
json["headers"].add(m);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (exportMode) {
|
||||
json["comment"] = "";
|
||||
json["cookies"] = [];
|
||||
json["headersSize"] = -1;
|
||||
json["bodySize"] = hasBody ? utf8.encode(requestBody!).length : 0;
|
||||
}
|
||||
}
|
||||
return json;
|
||||
}
|
72
lib/utils/header_utils.dart
Normal file
72
lib/utils/header_utils.dart
Normal file
@ -0,0 +1,72 @@
|
||||
Map<String, String> headers = {
|
||||
"Accept": "Specifies the media types that are acceptable for the response.",
|
||||
"Accept-Encoding":
|
||||
"Indicates the encoding methods the client can understand.",
|
||||
"Access-Control-Request-Headers":
|
||||
"Used in preflight requests during CORS to specify the headers that will be included in the actual request.",
|
||||
"Authorization":
|
||||
"Contains credentials for authenticating the client with the server.",
|
||||
"Authorization Bearer Token": "Often used for token-based authentication.",
|
||||
"Cache-Control":
|
||||
"Provides directives for caching mechanisms in both requests and responses.",
|
||||
"Content-Disposition":
|
||||
"Specifies the presentation style (inline or attachment) of the response.",
|
||||
"Content-Encoding":
|
||||
"Indicates the encoding transformations that have been applied to the entity body of the response.",
|
||||
"Content-Security-Policy":
|
||||
"Controls the sources from which content can be loaded on a web page to mitigate various types of attacks.",
|
||||
"Cookie": "Used to send previously stored cookies back to the server.",
|
||||
"Cross-Origin-Embedder-Policy":
|
||||
"Controls whether a document is allowed to be embedded in another document.",
|
||||
"Cross-Origin-Opener-Policy":
|
||||
"Controls which documents are allowed to open a new window or access the current window.",
|
||||
"Cross-Origin-Resource-Policy":
|
||||
"Controls how cross-origin requests for resources are handled.",
|
||||
"DNT":
|
||||
"Informs websites whether the user's preference is to opt out of online tracking.",
|
||||
"Expect": "Indicates certain expectations that need to be met by the server.",
|
||||
"Host": "Specifies the domain name of the server and the port number.",
|
||||
"If-Match":
|
||||
"Used for conditional requests, allows the server to respond based on certain conditions.",
|
||||
"If-Modified-Since":
|
||||
"Used for conditional requests, allows the server to respond based on certain conditions.",
|
||||
"If-None-Match":
|
||||
"Used for conditional requests, allows the server to respond based on certain conditions.",
|
||||
"If-Range":
|
||||
"Used in conjunction with the Range header to conditionally request a partial resource.",
|
||||
"If-Unmodified-Since":
|
||||
"Used for conditional requests, allows the server to respond based on certain conditions.",
|
||||
"Origin": "Specifies the origin of a cross-origin request.",
|
||||
"Range":
|
||||
"Used to request only part of a resource, typically in the context of downloading large files.",
|
||||
"Referer":
|
||||
"Indicates the URL of the page that referred the client to the current URL.",
|
||||
"Referrer-Policy":
|
||||
"Specifies how much information the browser should include in the Referer header when navigating to other pages.",
|
||||
"Retry-After":
|
||||
"Informs the client how long it should wait before making another request after a server has responded with a rate-limiting status code.",
|
||||
"Strict-Transport-Security":
|
||||
"Instructs the browser to always use HTTPS for the given domain.",
|
||||
"TE": "Specifies the transfer encodings that are acceptable to the client.",
|
||||
"User-Agent":
|
||||
"Identifies the client software and version making the request.",
|
||||
"Via":
|
||||
"Indicates intermediate proxies or gateways through which the request or response has passed.",
|
||||
"X-Api-Key": "Used to authenticate requests to an API with an API key.",
|
||||
"X-CSRF-Token":
|
||||
"Used for protection against Cross-Site Request Forgery (CSRF) attacks.",
|
||||
"X-Forwarded-For":
|
||||
"Identifies the client's original IP address when behind a proxy or load balancer.",
|
||||
"X-Requested-With":
|
||||
"Indicates whether the request was made with JavaScript using XMLHttpRequest.",
|
||||
"X-XSS-Protection":
|
||||
"Enables or disables the browser's built-in cross-site scripting (XSS) filter.",
|
||||
};
|
||||
|
||||
List<String> getHeaderSuggestions(String pattern) {
|
||||
return headers.keys
|
||||
.where(
|
||||
(element) => element.toLowerCase().contains(pattern.toLowerCase()),
|
||||
)
|
||||
.toList();
|
||||
}
|
@ -3,7 +3,7 @@ import 'dart:io';
|
||||
import 'package:collection/collection.dart' show mergeMaps;
|
||||
import 'package:http_parser/http_parser.dart';
|
||||
import 'package:xml/xml.dart';
|
||||
import 'package:apidash/models/models.dart' show KVRow;
|
||||
import '../models/models.dart';
|
||||
import 'convert_utils.dart' show rowsToMap;
|
||||
import '../consts.dart';
|
||||
|
||||
@ -44,8 +44,8 @@ MediaType? getMediaTypeFromHeaders(Map? headers) {
|
||||
}
|
||||
|
||||
(String?, bool) getUriScheme(Uri uri) {
|
||||
if(uri.hasScheme){
|
||||
if(kSupportedUriSchemes.contains(uri.scheme)){
|
||||
if (uri.hasScheme) {
|
||||
if (kSupportedUriSchemes.contains(uri.scheme)) {
|
||||
return (uri.scheme, true);
|
||||
}
|
||||
return (uri.scheme, false);
|
||||
@ -53,38 +53,44 @@ MediaType? getMediaTypeFromHeaders(Map? headers) {
|
||||
return (null, false);
|
||||
}
|
||||
|
||||
String stripUriParams(Uri uri) {
|
||||
return "${uri.scheme}://${uri.authority}${uri.path}";
|
||||
}
|
||||
|
||||
String stripUrlParams(String url) {
|
||||
var idx = url.indexOf("?");
|
||||
return idx > 0 ? url.substring(0, idx) : url;
|
||||
}
|
||||
|
||||
(Uri?, String?) getValidRequestUri(
|
||||
String? url,
|
||||
List<KVRow>? requestParams,
|
||||
{String defaultUriScheme = kDefaultUriScheme}
|
||||
) {
|
||||
String? url, List<NameValueModel>? requestParams,
|
||||
{String defaultUriScheme = kDefaultUriScheme}) {
|
||||
url = url?.trim();
|
||||
if(url == null || url == ""){
|
||||
if (url == null || url == "") {
|
||||
return (null, "URL is missing!");
|
||||
}
|
||||
Uri? uri = Uri.tryParse(url);
|
||||
if(uri == null){
|
||||
if (uri == null) {
|
||||
return (null, "Check URL (malformed)");
|
||||
}
|
||||
(String?, bool) urlScheme = getUriScheme(uri);
|
||||
|
||||
if(urlScheme.$0 != null){
|
||||
if (!urlScheme.$1){
|
||||
return (null, "Unsupported URL Scheme (${urlScheme.$0})");
|
||||
if (urlScheme.$1 != null) {
|
||||
if (!urlScheme.$2) {
|
||||
return (null, "Unsupported URL Scheme (${urlScheme.$1})");
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
url = "$defaultUriScheme://$url";
|
||||
}
|
||||
|
||||
uri = Uri.parse(url);
|
||||
if (uri.hasFragment){
|
||||
if (uri.hasFragment) {
|
||||
uri = uri.removeFragment();
|
||||
}
|
||||
|
||||
Map<String, String>? queryParams = rowsToMap(requestParams);
|
||||
if(queryParams != null){
|
||||
if(uri.hasQuery){
|
||||
if (queryParams != null) {
|
||||
if (uri.hasQuery) {
|
||||
Map<String, String> urlQueryParams = uri.queryParameters;
|
||||
queryParams = mergeMaps(urlQueryParams, queryParams);
|
||||
}
|
||||
@ -93,48 +99,58 @@ MediaType? getMediaTypeFromHeaders(Map? headers) {
|
||||
return (uri, null);
|
||||
}
|
||||
|
||||
(List<ResponseBodyView>, String?) getResponseBodyViewOptions(MediaType? mediaType){
|
||||
if(mediaType != null){
|
||||
(List<ResponseBodyView>, String?) getResponseBodyViewOptions(
|
||||
MediaType? mediaType) {
|
||||
if (mediaType != null) {
|
||||
var type = mediaType.type;
|
||||
var subtype = mediaType.subtype;
|
||||
if(kResponseBodyViewOptions.containsKey(type)){
|
||||
if (kResponseBodyViewOptions[type]!.containsKey(subtype)){
|
||||
return (kResponseBodyViewOptions[type]![subtype]!, kCodeHighlighterMap[subtype] ?? subtype);
|
||||
if (kResponseBodyViewOptions.containsKey(type)) {
|
||||
if (kResponseBodyViewOptions[type]!.containsKey(subtype)) {
|
||||
return (
|
||||
kResponseBodyViewOptions[type]![subtype]!,
|
||||
kCodeHighlighterMap[subtype] ?? subtype
|
||||
);
|
||||
}
|
||||
if(subtype.contains(kSubTypeJson)){
|
||||
if (subtype.contains(kSubTypeJson)) {
|
||||
subtype = kSubTypeJson;
|
||||
}
|
||||
if(subtype.contains(kSubTypeXml)){
|
||||
if (subtype.contains(kSubTypeXml)) {
|
||||
subtype = kSubTypeXml;
|
||||
}
|
||||
if (kResponseBodyViewOptions[type]!.containsKey(subtype)){
|
||||
return (kResponseBodyViewOptions[type]![subtype]!, kCodeHighlighterMap[subtype] ?? subtype);
|
||||
if (kResponseBodyViewOptions[type]!.containsKey(subtype)) {
|
||||
return (
|
||||
kResponseBodyViewOptions[type]![subtype]!,
|
||||
kCodeHighlighterMap[subtype] ?? subtype
|
||||
);
|
||||
}
|
||||
return (kResponseBodyViewOptions[type]![kSubTypeDefaultViewOptions]!, subtype);
|
||||
return (
|
||||
kResponseBodyViewOptions[type]![kSubTypeDefaultViewOptions]!,
|
||||
subtype
|
||||
);
|
||||
}
|
||||
}
|
||||
return (kNoBodyViewOptions, null);
|
||||
}
|
||||
|
||||
String? formatBody(String? body, MediaType? mediaType){
|
||||
if(mediaType != null && body != null){
|
||||
String? formatBody(String? body, MediaType? mediaType) {
|
||||
if (mediaType != null && body != null) {
|
||||
var subtype = mediaType.subtype;
|
||||
try {
|
||||
if(subtype.contains(kSubTypeJson)){
|
||||
if (subtype.contains(kSubTypeJson)) {
|
||||
final tmp = jsonDecode(body);
|
||||
String result = kEncoder.convert(tmp);
|
||||
return result;
|
||||
}
|
||||
if(subtype.contains(kSubTypeXml)){
|
||||
if (subtype.contains(kSubTypeXml)) {
|
||||
final document = XmlDocument.parse(body);
|
||||
String result = document.toXmlString(pretty: true, indent: ' ');
|
||||
return result;
|
||||
}
|
||||
if(subtype == kSubTypeHtml){
|
||||
if (subtype == kSubTypeHtml) {
|
||||
var len = body.length;
|
||||
var lines = kSplitter.convert(body);
|
||||
var numOfLines = lines.length;
|
||||
if(numOfLines !=0 && len/numOfLines <= kCodeCharsPerLineLimit){
|
||||
if (numOfLines != 0 && len / numOfLines <= kCodeCharsPerLineLimit) {
|
||||
return body;
|
||||
}
|
||||
}
|
||||
|
@ -3,3 +3,4 @@ export 'convert_utils.dart';
|
||||
export 'http_utils.dart';
|
||||
export 'file_utils.dart';
|
||||
export 'window_utils.dart';
|
||||
export 'har_utils.dart';
|
||||
|
@ -5,7 +5,7 @@ bool showButtonLabelsInBodySuccess(int options, double maxWidth) {
|
||||
case 1:
|
||||
return (maxWidth < 300) ? false : true;
|
||||
case 2:
|
||||
return (maxWidth < 400) ? false : true;
|
||||
return (maxWidth < 430) ? false : true;
|
||||
case 3:
|
||||
return (maxWidth < 500) ? false : true;
|
||||
default:
|
||||
@ -14,5 +14,5 @@ bool showButtonLabelsInBodySuccess(int options, double maxWidth) {
|
||||
}
|
||||
|
||||
bool showButtonLabelsInViewCodePane(double maxWidth) {
|
||||
return (maxWidth < 400) ? false : true;
|
||||
return (maxWidth < 450) ? false : true;
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ import 'package:apidash/utils/utils.dart';
|
||||
import 'menus.dart' show RequestCardMenu;
|
||||
import 'texts.dart' show MethodBox;
|
||||
|
||||
class SidebarRequestCard extends StatefulWidget {
|
||||
class SidebarRequestCard extends StatelessWidget {
|
||||
const SidebarRequestCard({
|
||||
super.key,
|
||||
required this.id,
|
||||
@ -15,7 +15,10 @@ class SidebarRequestCard extends StatefulWidget {
|
||||
this.editRequestId,
|
||||
this.onTap,
|
||||
this.onDoubleTap,
|
||||
this.onSecondaryTap,
|
||||
this.onChangedNameEditor,
|
||||
// this.controller,
|
||||
this.focusNode,
|
||||
this.onTapOutsideNameEditor,
|
||||
this.onMenuSelected,
|
||||
});
|
||||
@ -28,26 +31,30 @@ class SidebarRequestCard extends StatefulWidget {
|
||||
final String? editRequestId;
|
||||
final void Function()? onTap;
|
||||
final void Function()? onDoubleTap;
|
||||
final void Function()? onSecondaryTap;
|
||||
final Function(String)? onChangedNameEditor;
|
||||
// final TextEditingController? controller;
|
||||
final FocusNode? focusNode;
|
||||
final Function()? onTapOutsideNameEditor;
|
||||
final Function(RequestItemMenuOption)? onMenuSelected;
|
||||
|
||||
@override
|
||||
State<SidebarRequestCard> createState() => _SidebarRequestCardState();
|
||||
}
|
||||
|
||||
class _SidebarRequestCardState extends State<SidebarRequestCard> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final Color color = Theme.of(context).colorScheme.surface;
|
||||
final Color colorVariant =
|
||||
Theme.of(context).colorScheme.surfaceVariant.withOpacity(0.5);
|
||||
final Color surfaceTint = Theme.of(context).colorScheme.primary;
|
||||
bool isActiveId = widget.activeRequestId == widget.id;
|
||||
bool inEditMode = widget.editRequestId == widget.id;
|
||||
return Card(
|
||||
bool isActiveId = activeRequestId == id;
|
||||
bool inEditMode = editRequestId == id;
|
||||
String nm = (name != null && name!.trim().isNotEmpty)
|
||||
? name!
|
||||
: getRequestTitleFromUrl(url);
|
||||
return Tooltip(
|
||||
message: nm,
|
||||
waitDuration: const Duration(seconds: 1),
|
||||
child: Card(
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: kBorderRadius12,
|
||||
borderRadius: kBorderRadius8,
|
||||
),
|
||||
elevation: isActiveId ? 1 : 0,
|
||||
surfaceTintColor: isActiveId ? surfaceTint : null,
|
||||
@ -58,15 +65,16 @@ class _SidebarRequestCardState extends State<SidebarRequestCard> {
|
||||
: color,
|
||||
margin: EdgeInsets.zero,
|
||||
child: InkWell(
|
||||
borderRadius: kBorderRadius12,
|
||||
borderRadius: kBorderRadius8,
|
||||
hoverColor: colorVariant,
|
||||
focusColor: colorVariant.withOpacity(0.5),
|
||||
onTap: inEditMode ? null : widget.onTap,
|
||||
onDoubleTap: inEditMode ? null : widget.onDoubleTap,
|
||||
onTap: inEditMode ? null : onTap,
|
||||
// onDoubleTap: inEditMode ? null : onDoubleTap,
|
||||
onSecondaryTap: onSecondaryTap,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(
|
||||
left: 10,
|
||||
right: isActiveId ? 0 : 20,
|
||||
left: 6,
|
||||
right: isActiveId ? 6 : 10,
|
||||
top: 5,
|
||||
bottom: 5,
|
||||
),
|
||||
@ -74,20 +82,22 @@ class _SidebarRequestCardState extends State<SidebarRequestCard> {
|
||||
height: 20,
|
||||
child: Row(
|
||||
children: [
|
||||
MethodBox(method: widget.method),
|
||||
kHSpacer5,
|
||||
MethodBox(method: method),
|
||||
kHSpacer4,
|
||||
Expanded(
|
||||
child: inEditMode
|
||||
? TextFormField(
|
||||
key: Key("${widget.id}-name"),
|
||||
initialValue: widget.name,
|
||||
autofocus: true,
|
||||
key: ValueKey("$id-name"),
|
||||
initialValue: name,
|
||||
// controller: controller,
|
||||
focusNode: focusNode,
|
||||
//autofocus: true,
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
onTapOutside: (_) {
|
||||
widget.onTapOutsideNameEditor?.call();
|
||||
FocusScope.of(context).unfocus();
|
||||
onTapOutsideNameEditor?.call();
|
||||
//FocusScope.of(context).unfocus();
|
||||
},
|
||||
onChanged: widget.onChangedNameEditor,
|
||||
onChanged: onChangedNameEditor,
|
||||
decoration: const InputDecoration(
|
||||
isCollapsed: true,
|
||||
contentPadding: EdgeInsets.zero,
|
||||
@ -95,18 +105,18 @@ class _SidebarRequestCardState extends State<SidebarRequestCard> {
|
||||
),
|
||||
)
|
||||
: Text(
|
||||
(widget.name != null &&
|
||||
widget.name!.trim().isNotEmpty)
|
||||
? widget.name!
|
||||
: getRequestTitleFromUrl(widget.url),
|
||||
nm,
|
||||
softWrap: false,
|
||||
overflow: TextOverflow.fade,
|
||||
),
|
||||
),
|
||||
Visibility(
|
||||
visible: isActiveId && !inEditMode,
|
||||
child: SizedBox(
|
||||
width: 28,
|
||||
child: RequestCardMenu(
|
||||
onSelected: widget.onMenuSelected,
|
||||
onSelected: onMenuSelected,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
@ -114,6 +124,7 @@ class _SidebarRequestCardState extends State<SidebarRequestCard> {
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -67,7 +67,12 @@ class _CodePreviewerState extends State<CodePreviewer> {
|
||||
textStyle = textStyle.merge(widget.textStyle);
|
||||
}
|
||||
processed = sanitize(widget.code);
|
||||
spans = asyncGenerateSpans(processed.$0, widget.language, widget.theme, processed.$1);
|
||||
spans = asyncGenerateSpans(
|
||||
processed.$1,
|
||||
widget.language,
|
||||
widget.theme,
|
||||
processed.$2,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
@ -131,12 +136,14 @@ class _CodePreviewerState extends State<CodePreviewer> {
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<TextSpan>> asyncGenerateSpans(
|
||||
String code, String? language, Map<String, TextStyle> theme, bool limitedLines) async {
|
||||
Future<List<TextSpan>> asyncGenerateSpans(String code, String? language,
|
||||
Map<String, TextStyle> theme, bool limitedLines) async {
|
||||
var parsed = highlight.parse(code, language: language);
|
||||
var spans = convert(parsed.nodes!, theme);
|
||||
if(limitedLines) {
|
||||
spans.add(const TextSpan(text: "\n... more.\nPreview ends here ($kCodePreviewLinesLimit lines).\nYou can check Raw for full result."));
|
||||
if (limitedLines) {
|
||||
spans.add(const TextSpan(
|
||||
text:
|
||||
"\n... more.\nPreview ends here ($kCodePreviewLinesLimit lines).\nYou can check Raw for full result."));
|
||||
}
|
||||
return spans;
|
||||
}
|
||||
|
@ -41,19 +41,14 @@ class _CodeGenPreviewerState extends State<CodeGenPreviewer> {
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
Widget build(BuildContext context) {
|
||||
final spans = generateSpans(widget.code, widget.language, widget.theme);
|
||||
textStyle = TextStyle(
|
||||
color: widget.theme[_rootKey]?.color ?? _defaultFontColor,
|
||||
);
|
||||
if (widget.textStyle != null) {
|
||||
textStyle = textStyle.merge(widget.textStyle);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final spans = generateSpans(widget.code, widget.language, widget.theme);
|
||||
return Padding(
|
||||
padding: widget.padding,
|
||||
child: Scrollbar(
|
||||
|
@ -112,10 +112,10 @@ class _DropdownButtonContentTypeState extends State<DropdownButtonContentType> {
|
||||
|
||||
class DropdownButtonCodegenLanguage extends StatefulWidget {
|
||||
const DropdownButtonCodegenLanguage({
|
||||
Key? key,
|
||||
super.key,
|
||||
this.codegenLanguage,
|
||||
this.onChanged,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
@override
|
||||
State<DropdownButtonCodegenLanguage> createState() =>
|
||||
|
@ -4,9 +4,12 @@ import 'package:flutter/services.dart';
|
||||
import 'package:apidash/consts.dart';
|
||||
|
||||
class TextFieldEditor extends StatefulWidget {
|
||||
const TextFieldEditor(
|
||||
{Key? key, required this.fieldKey, this.onChanged, this.initialValue})
|
||||
: super(key: key);
|
||||
const TextFieldEditor({
|
||||
super.key,
|
||||
required this.fieldKey,
|
||||
this.onChanged,
|
||||
this.initialValue,
|
||||
});
|
||||
|
||||
final String fieldKey;
|
||||
final Function(String)? onChanged;
|
||||
|
120
lib/widgets/headerfield.dart
Normal file
120
lib/widgets/headerfield.dart
Normal file
@ -0,0 +1,120 @@
|
||||
import 'package:apidash/utils/header_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:apidash/consts.dart';
|
||||
import 'package:flutter_typeahead/flutter_typeahead.dart';
|
||||
|
||||
class HeaderField extends StatefulWidget {
|
||||
const HeaderField({
|
||||
super.key,
|
||||
required this.keyId,
|
||||
this.hintText,
|
||||
this.initialValue,
|
||||
this.onChanged,
|
||||
this.colorScheme,
|
||||
});
|
||||
final String keyId;
|
||||
final String? hintText;
|
||||
final String? initialValue;
|
||||
final void Function(String)? onChanged;
|
||||
final ColorScheme? colorScheme;
|
||||
|
||||
@override
|
||||
State<HeaderField> createState() => _HeaderFieldState();
|
||||
}
|
||||
|
||||
class _HeaderFieldState extends State<HeaderField> {
|
||||
final TextEditingController controller = TextEditingController();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
controller.text = widget.initialValue ?? "";
|
||||
controller.selection =
|
||||
TextSelection.collapsed(offset: controller.text.length);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
controller.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(HeaderField oldWidget) {
|
||||
if (oldWidget.initialValue != widget.initialValue) {
|
||||
controller.text = widget.initialValue ?? "";
|
||||
controller.selection =
|
||||
TextSelection.collapsed(offset: controller.text.length);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var colorScheme = widget.colorScheme ?? Theme.of(context).colorScheme;
|
||||
return TypeAheadField(
|
||||
key: Key(widget.keyId),
|
||||
hideOnEmpty: true,
|
||||
minCharsForSuggestions: 1,
|
||||
onSuggestionSelected: (value) {
|
||||
setState(() {
|
||||
controller.text = value;
|
||||
});
|
||||
widget.onChanged!.call(value);
|
||||
},
|
||||
itemBuilder: (context, String suggestion) {
|
||||
return ListTile(
|
||||
dense: true,
|
||||
title: Text(suggestion),
|
||||
);
|
||||
},
|
||||
suggestionsCallback: headerSuggestionCallback,
|
||||
suggestionsBoxDecoration: suggestionBoxDecorations(context),
|
||||
textFieldConfiguration: TextFieldConfiguration(
|
||||
onChanged: widget.onChanged,
|
||||
controller: controller,
|
||||
style: kCodeStyle.copyWith(
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
decoration: InputDecoration(
|
||||
hintStyle: kCodeStyle.copyWith(
|
||||
color: colorScheme.outline.withOpacity(
|
||||
kHintOpacity,
|
||||
),
|
||||
),
|
||||
hintText: widget.hintText,
|
||||
focusedBorder: UnderlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: colorScheme.primary.withOpacity(
|
||||
kHintOpacity,
|
||||
),
|
||||
),
|
||||
),
|
||||
enabledBorder: UnderlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: colorScheme.surfaceVariant,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
SuggestionsBoxDecoration suggestionBoxDecorations(BuildContext context) {
|
||||
return SuggestionsBoxDecoration(
|
||||
elevation: 4,
|
||||
constraints: const BoxConstraints(maxHeight: 400),
|
||||
shape: RoundedRectangleBorder(
|
||||
side: BorderSide(
|
||||
color: Theme.of(context).dividerColor,
|
||||
width: 1.2,
|
||||
),
|
||||
borderRadius: const BorderRadius.vertical(bottom: Radius.circular(8)),
|
||||
),
|
||||
clipBehavior: Clip.hardEdge,
|
||||
);
|
||||
}
|
||||
|
||||
Future<List<String>> headerSuggestionCallback(String pattern) async {
|
||||
return getHeaderSuggestions(pattern);
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart' show rootBundle;
|
||||
import 'package:apidash/consts.dart';
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
import '../consts.dart';
|
||||
import 'markdown.dart';
|
||||
import 'error_message.dart';
|
||||
|
||||
@ -16,19 +17,26 @@ class IntroMessage extends StatefulWidget {
|
||||
class _IntroMessageState extends State<IntroMessage> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final Future<String> intro = rootBundle.loadString('assets/intro.md');
|
||||
late String text;
|
||||
late final String version;
|
||||
|
||||
Future<void> introData() async {
|
||||
text = await rootBundle.loadString('assets/intro.md');
|
||||
version = (await PackageInfo.fromPlatform()).version;
|
||||
}
|
||||
|
||||
return FutureBuilder(
|
||||
future: intro,
|
||||
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
|
||||
if ((snapshot.connectionState == ConnectionState.done) &&
|
||||
snapshot.hasData) {
|
||||
String text = snapshot.data!;
|
||||
future: introData(),
|
||||
builder: (BuildContext context, AsyncSnapshot<void> snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.done) {
|
||||
if (Theme.of(context).brightness == Brightness.dark) {
|
||||
text = text.replaceAll("{{mode}}", "dark");
|
||||
} else {
|
||||
text = text.replaceAll("{{mode}}", "light");
|
||||
}
|
||||
|
||||
text = text.replaceAll("{{version}}", version);
|
||||
|
||||
return CustomMarkdown(
|
||||
data: text,
|
||||
padding: kPh60,
|
||||
@ -37,7 +45,7 @@ class _IntroMessageState extends State<IntroMessage> {
|
||||
if (snapshot.hasError) {
|
||||
return const ErrorMessage(message: "An error occured");
|
||||
}
|
||||
return const CircularProgressIndicator();
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
},
|
||||
);
|
||||
}
|
||||
|
402
lib/widgets/json_previewer.dart
Normal file
402
lib/widgets/json_previewer.dart
Normal file
@ -0,0 +1,402 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:json_data_explorer/json_data_explorer.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
|
||||
import 'package:url_launcher/url_launcher_string.dart';
|
||||
import '../consts.dart';
|
||||
import "snackbars.dart";
|
||||
import 'textfields.dart';
|
||||
|
||||
class JsonPreviewerColor {
|
||||
const JsonPreviewerColor._();
|
||||
|
||||
static const Color lightRootInfoBox = Color(0x80E1E1E1);
|
||||
static const Color lightRootKeyText = Colors.black;
|
||||
static const Color lightPropertyKeyText = Colors.black;
|
||||
static const Color lightKeySearchHighlightText = Colors.black;
|
||||
static const Color lightKeySearchHighlightBackground = Color(0xFFFFEDAD);
|
||||
static const Color lightFocusedKeySearchHighlightText = Colors.black;
|
||||
static const Color lightFocusedKeySearchHighlightBackground =
|
||||
Color(0xFFF29D0B);
|
||||
static const Color lightValueText = Color(0xffc41a16);
|
||||
static const Color lightValueSearchHighlightText = Color(0xffc41a16);
|
||||
static const Color lightValueNum = Color(0xff3F6E74);
|
||||
static const Color lightValueBool = Color(0xff1c00cf);
|
||||
static const Color lightValueSearchHighlightBackground = Color(0xFFFFEDAD);
|
||||
static const Color lightFocusedValueSearchHighlightText = Colors.black;
|
||||
static const Color lightFocusedValueSearchHighlightBackground =
|
||||
Color(0xFFF29D0B);
|
||||
static const Color lightIndentationLineColor =
|
||||
Color.fromARGB(255, 213, 213, 213);
|
||||
static const Color lightHighlightColor = Color(0xFFF1F1F1);
|
||||
|
||||
// Dark colors
|
||||
static const Color darkRootInfoBox = Color.fromARGB(255, 83, 13, 19);
|
||||
static const Color darkRootKeyText = Color(0xffd6deeb);
|
||||
static const Color darkPropertyKeyText = Color(0xffd6deeb);
|
||||
static const Color darkKeySearchHighlightText = Color(0xffd6deeb);
|
||||
static const Color darkKeySearchHighlightBackground = Color(0xff9b703f);
|
||||
static const Color darkFocusedKeySearchHighlightText = Color(0xffd6deeb);
|
||||
static const Color darkFocusedKeySearchHighlightBackground =
|
||||
Color(0xffc41a16);
|
||||
static const Color darkValueText = Color(0xffecc48d);
|
||||
static const Color darkValueSearchHighlightText = Color(0xffecc48d);
|
||||
static const Color darkValueNum = Color(0xffaddb67);
|
||||
static const Color darkValueBool = Color(0xff82aaff);
|
||||
static const Color darkValueSearchHighlightBackground = Color(0xff9b703f);
|
||||
static const Color darkFocusedValueSearchHighlightText = Color(0xffd6deeb);
|
||||
static const Color darkFocusedValueSearchHighlightBackground =
|
||||
Color(0xffc41a16);
|
||||
static const Color darkIndentationLineColor =
|
||||
Color.fromARGB(255, 119, 119, 119);
|
||||
static const Color darkHighlightColor = Color.fromARGB(255, 55, 55, 55);
|
||||
}
|
||||
|
||||
final dataExplorerThemeLight = DataExplorerTheme(
|
||||
rootKeyTextStyle: kCodeStyle.copyWith(
|
||||
color: JsonPreviewerColor.lightRootKeyText,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
propertyKeyTextStyle: kCodeStyle.copyWith(
|
||||
color: JsonPreviewerColor.lightPropertyKeyText,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
keySearchHighlightTextStyle: kCodeStyle.copyWith(
|
||||
color: JsonPreviewerColor.lightKeySearchHighlightText,
|
||||
backgroundColor: JsonPreviewerColor.lightKeySearchHighlightBackground,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
focusedKeySearchHighlightTextStyle: kCodeStyle.copyWith(
|
||||
color: JsonPreviewerColor.lightFocusedKeySearchHighlightText,
|
||||
backgroundColor:
|
||||
JsonPreviewerColor.lightFocusedKeySearchHighlightBackground,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
valueTextStyle: kCodeStyle.copyWith(
|
||||
color: JsonPreviewerColor.lightValueText,
|
||||
),
|
||||
valueSearchHighlightTextStyle: kCodeStyle.copyWith(
|
||||
color: JsonPreviewerColor.lightValueSearchHighlightText,
|
||||
backgroundColor: JsonPreviewerColor.lightValueSearchHighlightBackground,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
focusedValueSearchHighlightTextStyle: kCodeStyle.copyWith(
|
||||
color: JsonPreviewerColor.lightFocusedValueSearchHighlightText,
|
||||
backgroundColor:
|
||||
JsonPreviewerColor.lightFocusedValueSearchHighlightBackground,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
indentationLineColor: JsonPreviewerColor.lightIndentationLineColor,
|
||||
highlightColor: JsonPreviewerColor.lightHighlightColor,
|
||||
);
|
||||
|
||||
final dataExplorerThemeDark = DataExplorerTheme(
|
||||
rootKeyTextStyle: kCodeStyle.copyWith(
|
||||
color: JsonPreviewerColor.darkRootKeyText,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
propertyKeyTextStyle: kCodeStyle.copyWith(
|
||||
color: JsonPreviewerColor.darkPropertyKeyText,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
keySearchHighlightTextStyle: kCodeStyle.copyWith(
|
||||
color: JsonPreviewerColor.darkKeySearchHighlightText,
|
||||
backgroundColor: JsonPreviewerColor.darkKeySearchHighlightBackground,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
focusedKeySearchHighlightTextStyle: kCodeStyle.copyWith(
|
||||
color: JsonPreviewerColor.darkFocusedKeySearchHighlightText,
|
||||
backgroundColor: JsonPreviewerColor.darkFocusedKeySearchHighlightBackground,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
valueTextStyle: kCodeStyle.copyWith(
|
||||
color: JsonPreviewerColor.darkValueText,
|
||||
),
|
||||
valueSearchHighlightTextStyle: kCodeStyle.copyWith(
|
||||
color: JsonPreviewerColor.darkValueSearchHighlightText,
|
||||
backgroundColor: JsonPreviewerColor.darkValueSearchHighlightBackground,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
focusedValueSearchHighlightTextStyle: kCodeStyle.copyWith(
|
||||
color: JsonPreviewerColor.darkFocusedValueSearchHighlightText,
|
||||
backgroundColor:
|
||||
JsonPreviewerColor.darkFocusedValueSearchHighlightBackground,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
indentationLineColor: JsonPreviewerColor.darkIndentationLineColor,
|
||||
highlightColor: JsonPreviewerColor.darkHighlightColor,
|
||||
);
|
||||
|
||||
class JsonPreviewer extends StatefulWidget {
|
||||
const JsonPreviewer({
|
||||
super.key,
|
||||
required this.code,
|
||||
});
|
||||
final dynamic code;
|
||||
|
||||
@override
|
||||
State<JsonPreviewer> createState() => _JsonPreviewerState();
|
||||
}
|
||||
|
||||
class _JsonPreviewerState extends State<JsonPreviewer> {
|
||||
final searchController = TextEditingController();
|
||||
final itemScrollController = ItemScrollController();
|
||||
final DataExplorerStore store = DataExplorerStore();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
store.buildNodes(widget.code, areAllCollapsed: true);
|
||||
store.expandAll();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var sm = ScaffoldMessenger.of(context);
|
||||
return ChangeNotifierProvider.value(
|
||||
value: store,
|
||||
child: Consumer<DataExplorerStore>(
|
||||
builder: (context, state, child) => Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
await _copy(kEncoder.convert(widget.code), sm);
|
||||
},
|
||||
child: const Text('Copy'),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: state.areAllExpanded() ? null : state.expandAll,
|
||||
child: const Text('Expand All'),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: state.areAllCollapsed() ? null : state.collapseAll,
|
||||
child: const Text('Collapse All'),
|
||||
),
|
||||
],
|
||||
),
|
||||
Expanded(
|
||||
child: JsonDataExplorer(
|
||||
nodes: state.displayNodes,
|
||||
itemScrollController: itemScrollController,
|
||||
itemSpacing: 4,
|
||||
rootInformationBuilder: (context, node) =>
|
||||
rootInfoBox(context, node),
|
||||
collapsableToggleBuilder: (context, node) => AnimatedRotation(
|
||||
turns: node.isCollapsed ? -0.25 : 0,
|
||||
duration: const Duration(milliseconds: 300),
|
||||
child: const Icon(Icons.arrow_drop_down),
|
||||
),
|
||||
trailingBuilder: (context, node) => node.isFocused
|
||||
? Padding(
|
||||
padding: const EdgeInsets.only(right: 12),
|
||||
child: IconButton(
|
||||
padding: EdgeInsets.zero,
|
||||
constraints: const BoxConstraints(maxHeight: 18),
|
||||
icon: const Icon(
|
||||
Icons.copy,
|
||||
size: 18,
|
||||
),
|
||||
onPressed: () async {
|
||||
await _copy(kEncoder.convert(toJson(node)), sm);
|
||||
},
|
||||
),
|
||||
)
|
||||
: const SizedBox(),
|
||||
valueStyleBuilder: (value, style) =>
|
||||
valueStyleOverride(context, value, style),
|
||||
theme: (Theme.of(context).brightness == Brightness.light)
|
||||
? dataExplorerThemeLight
|
||||
: dataExplorerThemeDark,
|
||||
),
|
||||
),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
border: Border.all(
|
||||
color: Theme.of(context).colorScheme.surfaceVariant),
|
||||
borderRadius: kBorderRadius8,
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
const Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 8.0),
|
||||
child: Icon(
|
||||
Icons.search,
|
||||
size: 18,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: JsonSearchField(
|
||||
controller: searchController,
|
||||
onChanged: (term) => state.search(term),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 8,
|
||||
),
|
||||
if (state.searchResults.isNotEmpty)
|
||||
Text(_searchFocusText(),
|
||||
style: Theme.of(context).textTheme.bodySmall),
|
||||
if (state.searchResults.isNotEmpty)
|
||||
IconButton(
|
||||
visualDensity: VisualDensity.compact,
|
||||
onPressed: () {
|
||||
store.focusPreviousSearchResult();
|
||||
_scrollToSearchMatch();
|
||||
},
|
||||
icon: const Icon(Icons.arrow_drop_up),
|
||||
),
|
||||
if (state.searchResults.isNotEmpty)
|
||||
IconButton(
|
||||
visualDensity: VisualDensity.compact,
|
||||
onPressed: () {
|
||||
store.focusNextSearchResult();
|
||||
_scrollToSearchMatch();
|
||||
},
|
||||
icon: const Icon(Icons.arrow_drop_down),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _copy(String text, ScaffoldMessengerState sm) async {
|
||||
String msg;
|
||||
try {
|
||||
await Clipboard.setData(ClipboardData(text: text));
|
||||
msg = "Copied";
|
||||
} catch (e) {
|
||||
msg = "An error occurred";
|
||||
}
|
||||
sm.hideCurrentSnackBar();
|
||||
sm.showSnackBar(getSnackBar(msg));
|
||||
}
|
||||
|
||||
PropertyOverrides valueStyleOverride(
|
||||
BuildContext context,
|
||||
dynamic value,
|
||||
TextStyle style,
|
||||
) {
|
||||
TextStyle newStyle = style;
|
||||
bool isUrl = false;
|
||||
if (value.runtimeType.toString() == "num" ||
|
||||
value.runtimeType.toString() == "double" ||
|
||||
value.runtimeType.toString() == "int") {
|
||||
newStyle = style.copyWith(
|
||||
color: (Theme.of(context).brightness == Brightness.light)
|
||||
? JsonPreviewerColor.lightValueNum
|
||||
: JsonPreviewerColor.darkValueNum,
|
||||
);
|
||||
} else if (value.runtimeType.toString() == "bool") {
|
||||
newStyle = style.copyWith(
|
||||
color: (Theme.of(context).brightness == Brightness.light)
|
||||
? JsonPreviewerColor.lightValueBool
|
||||
: JsonPreviewerColor.darkValueBool,
|
||||
);
|
||||
} else {
|
||||
isUrl = _valueIsUrl(value);
|
||||
if (isUrl) {
|
||||
newStyle = style.copyWith(
|
||||
decoration: TextDecoration.underline,
|
||||
decorationColor: (Theme.of(context).brightness == Brightness.light)
|
||||
? JsonPreviewerColor.lightValueText
|
||||
: JsonPreviewerColor.darkValueText,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return PropertyOverrides(
|
||||
style: newStyle,
|
||||
onTap: isUrl ? () => _launchUrl(value as String) : null,
|
||||
);
|
||||
}
|
||||
|
||||
DecoratedBox rootInfoBox(BuildContext context, NodeViewModelState node) {
|
||||
return DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
color: (Theme.of(context).brightness == Brightness.light)
|
||||
? JsonPreviewerColor.lightRootInfoBox
|
||||
: JsonPreviewerColor.darkRootInfoBox,
|
||||
borderRadius: const BorderRadius.all(Radius.circular(2)),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 4,
|
||||
vertical: 2,
|
||||
),
|
||||
child: SelectableText(
|
||||
node.isClass ? '{${node.childrenCount}}' : '[${node.childrenCount}]',
|
||||
style: kCodeStyle,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
String _searchFocusText() =>
|
||||
'${store.focusedSearchResultIndex + 1} of ${store.searchResults.length}';
|
||||
|
||||
void _scrollToSearchMatch() {
|
||||
final index = store.displayNodes.indexOf(store.focusedSearchResult.node);
|
||||
if (index != -1) {
|
||||
itemScrollController.scrollTo(
|
||||
index: index,
|
||||
duration: const Duration(milliseconds: 300),
|
||||
curve: Curves.easeInOutCubic,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
bool _valueIsUrl(dynamic value) {
|
||||
if (value is String) {
|
||||
return Uri.tryParse(value)?.hasAbsolutePath ?? false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Future _launchUrl(String url) {
|
||||
return launchUrlString(url);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
searchController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
dynamic toJson(
|
||||
NodeViewModelState node,
|
||||
) {
|
||||
dynamic res;
|
||||
if (node.isRoot) {
|
||||
if (node.isClass) {
|
||||
res = {};
|
||||
for (var i in node.children) {
|
||||
res.addAll(toJson(i));
|
||||
}
|
||||
}
|
||||
if (node.isArray) {
|
||||
res = [];
|
||||
for (var i in node.children) {
|
||||
res.add(toJson(i));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
res = node.value;
|
||||
}
|
||||
|
||||
if (node.parent != null && node.parent!.isArray) {
|
||||
return res;
|
||||
} else {
|
||||
return {node.key: res};
|
||||
}
|
||||
}
|
@ -23,6 +23,10 @@ class _RequestCardMenuState extends State<RequestCardMenu> {
|
||||
onSelected: widget.onSelected,
|
||||
itemBuilder: (BuildContext context) =>
|
||||
<PopupMenuEntry<RequestItemMenuOption>>[
|
||||
const PopupMenuItem<RequestItemMenuOption>(
|
||||
value: RequestItemMenuOption.edit,
|
||||
child: Text('Rename'),
|
||||
),
|
||||
const PopupMenuItem<RequestItemMenuOption>(
|
||||
value: RequestItemMenuOption.delete,
|
||||
child: Text('Delete'),
|
||||
|
@ -1,19 +1,24 @@
|
||||
import 'dart:convert';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'error_message.dart';
|
||||
import 'package:apidash/consts.dart';
|
||||
import 'package:printing/printing.dart';
|
||||
import 'uint8_audio_player.dart';
|
||||
import 'json_previewer.dart';
|
||||
|
||||
class Previewer extends StatefulWidget {
|
||||
const Previewer({
|
||||
super.key,
|
||||
required this.bytes,
|
||||
required this.body,
|
||||
this.type,
|
||||
this.subtype,
|
||||
this.hasRaw = false,
|
||||
});
|
||||
|
||||
final Uint8List bytes;
|
||||
final String body;
|
||||
final String? type;
|
||||
final String? subtype;
|
||||
final bool hasRaw;
|
||||
@ -25,6 +30,16 @@ class Previewer extends StatefulWidget {
|
||||
class _PreviewerState extends State<Previewer> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (widget.type == kTypeApplication && widget.subtype == kSubTypeJson) {
|
||||
try {
|
||||
var preview = JsonPreviewer(
|
||||
code: jsonDecode(widget.body),
|
||||
);
|
||||
return preview;
|
||||
} catch (e) {
|
||||
// pass
|
||||
}
|
||||
}
|
||||
if (widget.type == kTypeImage) {
|
||||
return Image.memory(
|
||||
widget.bytes,
|
||||
@ -34,7 +49,13 @@ class _PreviewerState extends State<Previewer> {
|
||||
);
|
||||
}
|
||||
if (widget.type == kTypeApplication && widget.subtype == kSubTypePdf) {
|
||||
// TODO: PDF Viewer
|
||||
return PdfPreview(
|
||||
build: (_) => widget.bytes,
|
||||
useActions: false,
|
||||
onError: (context, error) {
|
||||
return const ErrorMessage(message: kPdfError);
|
||||
},
|
||||
);
|
||||
}
|
||||
if (widget.type == kTypeAudio) {
|
||||
return Uint8AudioPlayer(
|
||||
|
@ -1,5 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:apidash/consts.dart';
|
||||
import 'tabs.dart';
|
||||
|
||||
class RequestPane extends StatefulWidget {
|
||||
const RequestPane({
|
||||
@ -10,6 +11,7 @@ class RequestPane extends StatefulWidget {
|
||||
this.onPressedCodeButton,
|
||||
this.onTapTabBar,
|
||||
required this.children,
|
||||
this.showIndicators = const [false, false, false],
|
||||
});
|
||||
|
||||
final String? activeId;
|
||||
@ -18,6 +20,7 @@ class RequestPane extends StatefulWidget {
|
||||
final void Function()? onPressedCodeButton;
|
||||
final void Function(int)? onTapTabBar;
|
||||
final List<Widget> children;
|
||||
final List<bool> showIndicators;
|
||||
|
||||
@override
|
||||
State<RequestPane> createState() => _RequestPaneState();
|
||||
@ -77,40 +80,18 @@ class _RequestPaneState extends State<RequestPane>
|
||||
controller: _controller,
|
||||
overlayColor: kColorTransparentState,
|
||||
onTap: widget.onTapTabBar,
|
||||
tabs: const [
|
||||
SizedBox(
|
||||
height: kTabHeight,
|
||||
child: Center(
|
||||
child: Text(
|
||||
'URL Params',
|
||||
textAlign: TextAlign.center,
|
||||
overflow: TextOverflow.fade,
|
||||
softWrap: false,
|
||||
style: kTextStyleButton,
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: kTabHeight,
|
||||
child: Center(
|
||||
child: Text(
|
||||
'Headers',
|
||||
textAlign: TextAlign.center,
|
||||
overflow: TextOverflow.fade,
|
||||
style: kTextStyleButton,
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: kTabHeight,
|
||||
child: Center(
|
||||
child: Text(
|
||||
'Body',
|
||||
textAlign: TextAlign.center,
|
||||
overflow: TextOverflow.fade,
|
||||
style: kTextStyleButton,
|
||||
tabs: [
|
||||
TabLabel(
|
||||
text: 'URL Params',
|
||||
showIndicator: widget.showIndicators[0],
|
||||
),
|
||||
TabLabel(
|
||||
text: 'Headers',
|
||||
showIndicator: widget.showIndicators[1],
|
||||
),
|
||||
TabLabel(
|
||||
text: 'Body',
|
||||
showIndicator: widget.showIndicators[2],
|
||||
),
|
||||
],
|
||||
),
|
||||
|
@ -339,8 +339,8 @@ class _ResponseBodyState extends State<ResponseBody> {
|
||||
}
|
||||
|
||||
var responseBodyView = getResponseBodyViewOptions(mediaType);
|
||||
var options = responseBodyView.$0;
|
||||
var highlightLanguage = responseBodyView.$1;
|
||||
var options = responseBodyView.$1;
|
||||
var highlightLanguage = responseBodyView.$2;
|
||||
|
||||
if (formattedBody == null) {
|
||||
options = [...options];
|
||||
@ -453,14 +453,20 @@ class _BodySuccessState extends State<BodySuccess> {
|
||||
visible: currentSeg == ResponseBodyView.preview ||
|
||||
currentSeg == ResponseBodyView.none,
|
||||
child: Expanded(
|
||||
child: Container(
|
||||
width: double.maxFinite,
|
||||
padding: kP8,
|
||||
decoration: textContainerdecoration,
|
||||
child: Previewer(
|
||||
bytes: widget.bytes,
|
||||
body: widget.body,
|
||||
type: widget.mediaType.type,
|
||||
subtype: widget.mediaType.subtype,
|
||||
hasRaw: widget.options.contains(ResponseBodyView.raw),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (widget.formattedBody != null)
|
||||
Visibility(
|
||||
visible: currentSeg == ResponseBodyView.code,
|
||||
|
@ -4,10 +4,10 @@ import 'package:apidash/consts.dart';
|
||||
|
||||
class DashboardSplitView extends StatefulWidget {
|
||||
const DashboardSplitView({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.sidebarWidget,
|
||||
required this.mainWidget,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
final Widget sidebarWidget;
|
||||
final Widget mainWidget;
|
||||
|
@ -51,8 +51,7 @@ class _MapTableState extends State<MapTable> {
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
...widget.map.entries
|
||||
.map<TableRow>(
|
||||
...widget.map.entries.map<TableRow>(
|
||||
(entry) => TableRow(
|
||||
children: [
|
||||
TableCell(
|
||||
@ -81,8 +80,7 @@ class _MapTableState extends State<MapTable> {
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
39
lib/widgets/tabs.dart
Normal file
39
lib/widgets/tabs.dart
Normal file
@ -0,0 +1,39 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:apidash/consts.dart';
|
||||
|
||||
class TabLabel extends StatelessWidget {
|
||||
const TabLabel({super.key, required this.text, this.showIndicator = false});
|
||||
final String text;
|
||||
final bool showIndicator;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
height: kTabHeight,
|
||||
child: Stack(
|
||||
children: [
|
||||
Center(
|
||||
child: Text(
|
||||
text,
|
||||
textAlign: TextAlign.center,
|
||||
overflow: TextOverflow.fade,
|
||||
softWrap: false,
|
||||
style: kTextStyleButton,
|
||||
),
|
||||
),
|
||||
if (showIndicator)
|
||||
const Align(
|
||||
alignment: Alignment.topCenter,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(top: 6),
|
||||
child: Icon(
|
||||
Icons.circle,
|
||||
size: 6,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -97,3 +97,25 @@ class _CellFieldState extends State<CellField> {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class JsonSearchField extends StatelessWidget {
|
||||
const JsonSearchField({super.key, this.onChanged, this.controller});
|
||||
|
||||
final void Function(String)? onChanged;
|
||||
final TextEditingController? controller;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return TextField(
|
||||
controller: controller,
|
||||
onChanged: onChanged,
|
||||
style: kCodeStyle,
|
||||
cursorHeight: 18,
|
||||
decoration: const InputDecoration(
|
||||
isDense: true,
|
||||
border: InputBorder.none,
|
||||
hintText: 'Search..',
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ export 'dropdowns.dart';
|
||||
export 'splitviews.dart';
|
||||
export 'texts.dart';
|
||||
export 'textfields.dart';
|
||||
export 'headerfield.dart';
|
||||
export 'menus.dart';
|
||||
export 'cards.dart';
|
||||
export 'intro_message.dart';
|
||||
@ -17,3 +18,5 @@ export 'response_widgets.dart';
|
||||
export 'snackbars.dart';
|
||||
export 'markdown.dart';
|
||||
export 'uint8_audio_player.dart';
|
||||
export 'tabs.dart';
|
||||
export 'json_previewer.dart';
|
||||
|
637
pubspec.lock
637
pubspec.lock
File diff suppressed because it is too large
Load Diff
60
pubspec.yaml
60
pubspec.yaml
@ -1,49 +1,63 @@
|
||||
name: apidash
|
||||
description: API Dash is a beautiful open-source cross-platform API Client built using Flutter which can help you easily create & customize your API requests, visually inspect responses and generate Dart code on the go.
|
||||
publish_to: 'none'
|
||||
version: 0.2.0+2
|
||||
publish_to: "none"
|
||||
version: 0.3.0+3
|
||||
|
||||
environment:
|
||||
sdk: '>=2.19.2 <3.0.0'
|
||||
flutter: '>=3.7.2 <3.10.0'
|
||||
sdk: ">=3.0.0 <4.0.0"
|
||||
flutter: ">=3.7.2"
|
||||
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
multi_split_view: ^2.4.0
|
||||
url_launcher: ^6.1.10
|
||||
flutter_riverpod: ^2.1.3
|
||||
uuid: ^3.0.7
|
||||
davi: ^3.2.0
|
||||
http: ^0.13.5
|
||||
url_launcher: ^6.1.12
|
||||
flutter_riverpod: ^2.3.7
|
||||
uuid: ^4.1.0
|
||||
davi: ^3.4.1
|
||||
http: ^1.1.0
|
||||
http_parser: ^4.0.2
|
||||
collection: ^1.17.0
|
||||
google_fonts: ^4.0.3
|
||||
collection: ^1.17.2
|
||||
google_fonts: ^6.1.0
|
||||
highlighter: ^0.1.1
|
||||
xml: ^6.2.2
|
||||
jinja: ^0.4.2
|
||||
xml: ^6.3.0
|
||||
jinja: ^0.5.0
|
||||
window_size:
|
||||
git:
|
||||
url: https://github.com/google/flutter-desktop-embedding.git
|
||||
path: plugins/window_size
|
||||
hive_flutter: ^1.1.0
|
||||
lottie: ^2.3.2
|
||||
lottie: ^2.6.0
|
||||
mime_dart: ^3.0.0
|
||||
path_provider: ^2.0.14
|
||||
window_manager: ^0.3.2
|
||||
path: ^1.8.2
|
||||
flutter_markdown: ^0.6.14
|
||||
markdown: ^7.1.0
|
||||
path_provider: ^2.1.0
|
||||
window_manager: ^0.3.5
|
||||
path: ^1.8.3
|
||||
flutter_markdown: ^0.6.17+1
|
||||
markdown: ^7.1.1
|
||||
just_audio: ^0.9.34
|
||||
just_audio_mpv: ^0.1.6
|
||||
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.1
|
||||
package_info_plus: ^4.1.0
|
||||
flutter_typeahead: ^4.8.0
|
||||
provider: ^6.0.5
|
||||
json_data_explorer:
|
||||
git:
|
||||
url: https://github.com/foss42/json_data_explorer.git
|
||||
version: ^0.1.1
|
||||
scrollable_positioned_list: ^0.2.3
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
flutter_lints: ^2.0.0
|
||||
flutter_launcher_icons: ^0.12.0
|
||||
test: ^1.22.0
|
||||
flutter_lints: ^3.0.0
|
||||
flutter_launcher_icons: ^0.13.1
|
||||
test: ^1.24.3
|
||||
build_runner: ^2.4.6
|
||||
freezed: ^2.4.1
|
||||
json_serializable: ^6.7.1
|
||||
|
||||
flutter:
|
||||
uses-material-design: true
|
||||
|
147
test/codegen/curl_codegen_test.dart
Normal file
147
test/codegen/curl_codegen_test.dart
Normal file
@ -0,0 +1,147 @@
|
||||
import 'package:apidash/codegen/others/curl.dart';
|
||||
import '../request_models.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
final curlCodeGen = cURLCodeGen();
|
||||
|
||||
group('GET Request', () {
|
||||
test('GET 1', () {
|
||||
const expectedCode = r"""curl --url 'https://api.foss42.com'""";
|
||||
expect(curlCodeGen.getCode(requestModelGet1, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('GET 2', () {
|
||||
const expectedCode =
|
||||
r"""curl --url 'https://api.foss42.com/country/data?code=US'""";
|
||||
expect(curlCodeGen.getCode(requestModelGet2, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('GET 3', () {
|
||||
const expectedCode =
|
||||
r"""curl --url 'https://api.foss42.com/country/data?code=IND'""";
|
||||
expect(curlCodeGen.getCode(requestModelGet3, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('GET 4', () {
|
||||
const expectedCode =
|
||||
r"""curl --url 'https://api.foss42.com/humanize/social?num=8700000&digits=3&system=SS&add_space=true&trailing_zeros=true'""";
|
||||
expect(curlCodeGen.getCode(requestModelGet4, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('GET 5', () {
|
||||
const expectedCode =
|
||||
r"""curl --url 'https://api.github.com/repos/foss42/apidash' \
|
||||
--header 'User-Agent: Test Agent'""";
|
||||
expect(curlCodeGen.getCode(requestModelGet5, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('GET 6', () {
|
||||
const expectedCode =
|
||||
r"""curl --url 'https://api.github.com/repos/foss42/apidash?raw=true' \
|
||||
--header 'User-Agent: Test Agent'""";
|
||||
expect(curlCodeGen.getCode(requestModelGet6, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('GET 7', () {
|
||||
const expectedCode = r"""curl --url 'https://api.foss42.com'""";
|
||||
expect(curlCodeGen.getCode(requestModelGet7, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('GET 8', () {
|
||||
const expectedCode =
|
||||
r"""curl --url 'https://api.github.com/repos/foss42/apidash?raw=true' \
|
||||
--header 'User-Agent: Test Agent'""";
|
||||
expect(curlCodeGen.getCode(requestModelGet8, "https"), expectedCode);
|
||||
});
|
||||
});
|
||||
|
||||
group('HEAD Request', () {
|
||||
test('HEAD 1', () {
|
||||
const expectedCode = r"""curl --head --url 'https://api.foss42.com'""";
|
||||
expect(curlCodeGen.getCode(requestModelHead1, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('HEAD 2', () {
|
||||
const expectedCode = r"""curl --head --url 'http://api.foss42.com'""";
|
||||
expect(curlCodeGen.getCode(requestModelHead2, "http"), expectedCode);
|
||||
});
|
||||
});
|
||||
|
||||
group('POST Request', () {
|
||||
test('POST 1', () {
|
||||
const expectedCode = r"""curl --request POST \
|
||||
--url 'https://api.foss42.com/case/lower' \
|
||||
--header 'Content-Type: text/plain' \
|
||||
--data '{
|
||||
"text": "I LOVE Flutter"
|
||||
}'""";
|
||||
expect(curlCodeGen.getCode(requestModelPost1, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('POST 2', () {
|
||||
const expectedCode = r"""curl --request POST \
|
||||
--url 'https://api.foss42.com/case/lower' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"text": "I LOVE Flutter"
|
||||
}'""";
|
||||
expect(curlCodeGen.getCode(requestModelPost2, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('POST 3', () {
|
||||
const expectedCode = r"""curl --request POST \
|
||||
--url 'https://api.foss42.com/case/lower' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--header 'User-Agent: Test Agent' \
|
||||
--data '{
|
||||
"text": "I LOVE Flutter"
|
||||
}'""";
|
||||
expect(curlCodeGen.getCode(requestModelPost3, "https"), expectedCode);
|
||||
});
|
||||
});
|
||||
group('PUT Request', () {
|
||||
test('PUT 1', () {
|
||||
const expectedCode = r"""curl --request PUT \
|
||||
--url 'https://reqres.in/api/users/2' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"name": "morpheus",
|
||||
"job": "zion resident"
|
||||
}'""";
|
||||
expect(curlCodeGen.getCode(requestModelPut1, "https"), expectedCode);
|
||||
});
|
||||
});
|
||||
|
||||
group('PATCH Request', () {
|
||||
test('PATCH 1', () {
|
||||
const expectedCode = r"""curl --request PATCH \
|
||||
--url 'https://reqres.in/api/users/2' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"name": "marfeus",
|
||||
"job": "accountant"
|
||||
}'""";
|
||||
expect(curlCodeGen.getCode(requestModelPatch1, "https"), expectedCode);
|
||||
});
|
||||
});
|
||||
|
||||
group('DELETE Request', () {
|
||||
test('DELETE 1', () {
|
||||
const expectedCode = r"""curl --request DELETE \
|
||||
--url 'https://reqres.in/api/users/2'""";
|
||||
expect(curlCodeGen.getCode(requestModelDelete1, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('DELETE 2', () {
|
||||
const expectedCode = r"""curl --request DELETE \
|
||||
--url 'https://reqres.in/api/users/2' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"name": "marfeus",
|
||||
"job": "accountant"
|
||||
}'""";
|
||||
expect(curlCodeGen.getCode(requestModelDelete2, "https"), expectedCode);
|
||||
});
|
||||
});
|
||||
}
|
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/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 = {
|
||||
"User-Agent": "Test Agent"
|
||||
};
|
||||
|
||||
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 = {
|
||||
"User-Agent": "Test Agent"
|
||||
};
|
||||
|
||||
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 = {
|
||||
"User-Agent": "Test Agent"
|
||||
};
|
||||
|
||||
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 = {
|
||||
"User-Agent": "Test Agent",
|
||||
"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);
|
||||
});
|
||||
});
|
||||
}
|
319
test/codegen/har_codegen_test.dart
Normal file
319
test/codegen/har_codegen_test.dart
Normal file
@ -0,0 +1,319 @@
|
||||
import 'package:apidash/codegen/others/har.dart';
|
||||
import '../request_models.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
final harCodeGen = HARCodeGen();
|
||||
|
||||
group('GET Request', () {
|
||||
test('GET 1', () {
|
||||
const expectedCode = r"""{
|
||||
"method": "GET",
|
||||
"url": "https://api.foss42.com",
|
||||
"httpVersion": "HTTP/1.1",
|
||||
"queryString": [],
|
||||
"headers": []
|
||||
}""";
|
||||
expect(harCodeGen.getCode(requestModelGet1, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('GET 2', () {
|
||||
const expectedCode = r"""{
|
||||
"method": "GET",
|
||||
"url": "https://api.foss42.com/country/data?code=US",
|
||||
"httpVersion": "HTTP/1.1",
|
||||
"queryString": [
|
||||
{
|
||||
"name": "code",
|
||||
"value": "US"
|
||||
}
|
||||
],
|
||||
"headers": []
|
||||
}""";
|
||||
expect(harCodeGen.getCode(requestModelGet2, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('GET 3', () {
|
||||
const expectedCode = r"""{
|
||||
"method": "GET",
|
||||
"url": "https://api.foss42.com/country/data?code=IND",
|
||||
"httpVersion": "HTTP/1.1",
|
||||
"queryString": [
|
||||
{
|
||||
"name": "code",
|
||||
"value": "IND"
|
||||
}
|
||||
],
|
||||
"headers": []
|
||||
}""";
|
||||
expect(harCodeGen.getCode(requestModelGet3, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('GET 4', () {
|
||||
const expectedCode = r"""{
|
||||
"method": "GET",
|
||||
"url": "https://api.foss42.com/humanize/social?num=8700000&digits=3&system=SS&add_space=true&trailing_zeros=true",
|
||||
"httpVersion": "HTTP/1.1",
|
||||
"queryString": [
|
||||
{
|
||||
"name": "num",
|
||||
"value": "8700000"
|
||||
},
|
||||
{
|
||||
"name": "digits",
|
||||
"value": "3"
|
||||
},
|
||||
{
|
||||
"name": "system",
|
||||
"value": "SS"
|
||||
},
|
||||
{
|
||||
"name": "add_space",
|
||||
"value": "true"
|
||||
},
|
||||
{
|
||||
"name": "trailing_zeros",
|
||||
"value": "true"
|
||||
}
|
||||
],
|
||||
"headers": []
|
||||
}""";
|
||||
expect(harCodeGen.getCode(requestModelGet4, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('GET 5', () {
|
||||
const expectedCode = r"""{
|
||||
"method": "GET",
|
||||
"url": "https://api.github.com/repos/foss42/apidash",
|
||||
"httpVersion": "HTTP/1.1",
|
||||
"queryString": [],
|
||||
"headers": [
|
||||
{
|
||||
"name": "User-Agent",
|
||||
"value": "Test Agent"
|
||||
}
|
||||
]
|
||||
}""";
|
||||
expect(harCodeGen.getCode(requestModelGet5, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('GET 6', () {
|
||||
const expectedCode = r"""{
|
||||
"method": "GET",
|
||||
"url": "https://api.github.com/repos/foss42/apidash?raw=true",
|
||||
"httpVersion": "HTTP/1.1",
|
||||
"queryString": [
|
||||
{
|
||||
"name": "raw",
|
||||
"value": "true"
|
||||
}
|
||||
],
|
||||
"headers": [
|
||||
{
|
||||
"name": "User-Agent",
|
||||
"value": "Test Agent"
|
||||
}
|
||||
]
|
||||
}""";
|
||||
expect(harCodeGen.getCode(requestModelGet6, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('GET 7', () {
|
||||
const expectedCode = r"""{
|
||||
"method": "GET",
|
||||
"url": "https://api.foss42.com",
|
||||
"httpVersion": "HTTP/1.1",
|
||||
"queryString": [],
|
||||
"headers": []
|
||||
}""";
|
||||
expect(harCodeGen.getCode(requestModelGet7, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('GET 8', () {
|
||||
const expectedCode = r"""{
|
||||
"method": "GET",
|
||||
"url": "https://api.github.com/repos/foss42/apidash?raw=true",
|
||||
"httpVersion": "HTTP/1.1",
|
||||
"queryString": [
|
||||
{
|
||||
"name": "raw",
|
||||
"value": "true"
|
||||
}
|
||||
],
|
||||
"headers": [
|
||||
{
|
||||
"name": "User-Agent",
|
||||
"value": "Test Agent"
|
||||
}
|
||||
]
|
||||
}""";
|
||||
expect(harCodeGen.getCode(requestModelGet8, "https"), expectedCode);
|
||||
});
|
||||
});
|
||||
|
||||
group('HEAD Request', () {
|
||||
test('HEAD 1', () {
|
||||
const expectedCode = r"""{
|
||||
"method": "HEAD",
|
||||
"url": "https://api.foss42.com",
|
||||
"httpVersion": "HTTP/1.1",
|
||||
"queryString": [],
|
||||
"headers": []
|
||||
}""";
|
||||
expect(harCodeGen.getCode(requestModelHead1, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('HEAD 2', () {
|
||||
const expectedCode = r"""{
|
||||
"method": "HEAD",
|
||||
"url": "http://api.foss42.com",
|
||||
"httpVersion": "HTTP/1.1",
|
||||
"queryString": [],
|
||||
"headers": []
|
||||
}""";
|
||||
expect(harCodeGen.getCode(requestModelHead2, "http"), expectedCode);
|
||||
});
|
||||
});
|
||||
|
||||
group('POST Request', () {
|
||||
test('POST 1', () {
|
||||
const expectedCode = r"""{
|
||||
"method": "POST",
|
||||
"url": "https://api.foss42.com/case/lower",
|
||||
"httpVersion": "HTTP/1.1",
|
||||
"queryString": [],
|
||||
"headers": [
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "text/plain"
|
||||
}
|
||||
],
|
||||
"postData": {
|
||||
"mimeType": "text/plain",
|
||||
"text": "{\n\"text\": \"I LOVE Flutter\"\n}"
|
||||
}
|
||||
}""";
|
||||
expect(harCodeGen.getCode(requestModelPost1, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('POST 2', () {
|
||||
const expectedCode = r"""{
|
||||
"method": "POST",
|
||||
"url": "https://api.foss42.com/case/lower",
|
||||
"httpVersion": "HTTP/1.1",
|
||||
"queryString": [],
|
||||
"headers": [
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
}
|
||||
],
|
||||
"postData": {
|
||||
"mimeType": "application/json",
|
||||
"text": "{\n\"text\": \"I LOVE Flutter\"\n}"
|
||||
}
|
||||
}""";
|
||||
expect(harCodeGen.getCode(requestModelPost2, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('POST 3', () {
|
||||
const expectedCode = r"""{
|
||||
"method": "POST",
|
||||
"url": "https://api.foss42.com/case/lower",
|
||||
"httpVersion": "HTTP/1.1",
|
||||
"queryString": [],
|
||||
"headers": [
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "User-Agent",
|
||||
"value": "Test Agent"
|
||||
}
|
||||
],
|
||||
"postData": {
|
||||
"mimeType": "application/json",
|
||||
"text": "{\n\"text\": \"I LOVE Flutter\"\n}"
|
||||
}
|
||||
}""";
|
||||
expect(harCodeGen.getCode(requestModelPost3, "https"), expectedCode);
|
||||
});
|
||||
});
|
||||
group('PUT Request', () {
|
||||
test('PUT 1', () {
|
||||
const expectedCode = r"""{
|
||||
"method": "PUT",
|
||||
"url": "https://reqres.in/api/users/2",
|
||||
"httpVersion": "HTTP/1.1",
|
||||
"queryString": [],
|
||||
"headers": [
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
}
|
||||
],
|
||||
"postData": {
|
||||
"mimeType": "application/json",
|
||||
"text": "{\n\"name\": \"morpheus\",\n\"job\": \"zion resident\"\n}"
|
||||
}
|
||||
}""";
|
||||
expect(harCodeGen.getCode(requestModelPut1, "https"), expectedCode);
|
||||
});
|
||||
});
|
||||
|
||||
group('PATCH Request', () {
|
||||
test('PATCH 1', () {
|
||||
const expectedCode = r"""{
|
||||
"method": "PATCH",
|
||||
"url": "https://reqres.in/api/users/2",
|
||||
"httpVersion": "HTTP/1.1",
|
||||
"queryString": [],
|
||||
"headers": [
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
}
|
||||
],
|
||||
"postData": {
|
||||
"mimeType": "application/json",
|
||||
"text": "{\n\"name\": \"marfeus\",\n\"job\": \"accountant\"\n}"
|
||||
}
|
||||
}""";
|
||||
expect(harCodeGen.getCode(requestModelPatch1, "https"), expectedCode);
|
||||
});
|
||||
});
|
||||
|
||||
group('DELETE Request', () {
|
||||
test('DELETE 1', () {
|
||||
const expectedCode = r"""{
|
||||
"method": "DELETE",
|
||||
"url": "https://reqres.in/api/users/2",
|
||||
"httpVersion": "HTTP/1.1",
|
||||
"queryString": [],
|
||||
"headers": []
|
||||
}""";
|
||||
expect(harCodeGen.getCode(requestModelDelete1, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('DELETE 2', () {
|
||||
const expectedCode = r"""{
|
||||
"method": "DELETE",
|
||||
"url": "https://reqres.in/api/users/2",
|
||||
"httpVersion": "HTTP/1.1",
|
||||
"queryString": [],
|
||||
"headers": [
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
}
|
||||
],
|
||||
"postData": {
|
||||
"mimeType": "application/json",
|
||||
"text": "{\n\"name\": \"marfeus\",\n\"job\": \"accountant\"\n}"
|
||||
}
|
||||
}""";
|
||||
expect(harCodeGen.getCode(requestModelDelete2, "https"), expectedCode);
|
||||
});
|
||||
});
|
||||
}
|
428
test/codegen/js_axios_codegen_test.dart
Normal file
428
test/codegen/js_axios_codegen_test.dart
Normal file
@ -0,0 +1,428 @@
|
||||
import 'package:apidash/codegen/js/axios.dart';
|
||||
import '../request_models.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
final axiosCodeGen = AxiosCodeGen();
|
||||
|
||||
group('GET Request', () {
|
||||
test('GET 1', () {
|
||||
const expectedCode = r"""let config = {
|
||||
url: 'https://api.foss42.com',
|
||||
method: 'get'
|
||||
};
|
||||
|
||||
axios(config)
|
||||
.then(function (response) {
|
||||
// handle success
|
||||
console.log(response.status);
|
||||
console.log(response.data);
|
||||
})
|
||||
.catch(function (error) {
|
||||
// handle error
|
||||
console.log(error.response.status);
|
||||
console.log(error);
|
||||
});
|
||||
""";
|
||||
expect(axiosCodeGen.getCode(requestModelGet1, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('GET 2', () {
|
||||
const expectedCode = r"""let config = {
|
||||
url: 'https://api.foss42.com/country/data',
|
||||
method: 'get',
|
||||
params: {
|
||||
"code": "US"
|
||||
}
|
||||
};
|
||||
|
||||
axios(config)
|
||||
.then(function (response) {
|
||||
// handle success
|
||||
console.log(response.status);
|
||||
console.log(response.data);
|
||||
})
|
||||
.catch(function (error) {
|
||||
// handle error
|
||||
console.log(error.response.status);
|
||||
console.log(error);
|
||||
});
|
||||
""";
|
||||
expect(axiosCodeGen.getCode(requestModelGet2, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('GET 3', () {
|
||||
const expectedCode = r"""let config = {
|
||||
url: 'https://api.foss42.com/country/data',
|
||||
method: 'get',
|
||||
params: {
|
||||
"code": "IND"
|
||||
}
|
||||
};
|
||||
|
||||
axios(config)
|
||||
.then(function (response) {
|
||||
// handle success
|
||||
console.log(response.status);
|
||||
console.log(response.data);
|
||||
})
|
||||
.catch(function (error) {
|
||||
// handle error
|
||||
console.log(error.response.status);
|
||||
console.log(error);
|
||||
});
|
||||
""";
|
||||
expect(axiosCodeGen.getCode(requestModelGet3, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('GET 4', () {
|
||||
const expectedCode = r"""let config = {
|
||||
url: 'https://api.foss42.com/humanize/social',
|
||||
method: 'get',
|
||||
params: {
|
||||
"num": "8700000",
|
||||
"digits": "3",
|
||||
"system": "SS",
|
||||
"add_space": "true",
|
||||
"trailing_zeros": "true"
|
||||
}
|
||||
};
|
||||
|
||||
axios(config)
|
||||
.then(function (response) {
|
||||
// handle success
|
||||
console.log(response.status);
|
||||
console.log(response.data);
|
||||
})
|
||||
.catch(function (error) {
|
||||
// handle error
|
||||
console.log(error.response.status);
|
||||
console.log(error);
|
||||
});
|
||||
""";
|
||||
expect(axiosCodeGen.getCode(requestModelGet4, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('GET 5', () {
|
||||
const expectedCode = r"""let config = {
|
||||
url: 'https://api.github.com/repos/foss42/apidash',
|
||||
method: 'get',
|
||||
headers: {
|
||||
"User-Agent": "Test Agent"
|
||||
}
|
||||
};
|
||||
|
||||
axios(config)
|
||||
.then(function (response) {
|
||||
// handle success
|
||||
console.log(response.status);
|
||||
console.log(response.data);
|
||||
})
|
||||
.catch(function (error) {
|
||||
// handle error
|
||||
console.log(error.response.status);
|
||||
console.log(error);
|
||||
});
|
||||
""";
|
||||
expect(axiosCodeGen.getCode(requestModelGet5, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('GET 6', () {
|
||||
const expectedCode = r"""let config = {
|
||||
url: 'https://api.github.com/repos/foss42/apidash',
|
||||
method: 'get',
|
||||
params: {
|
||||
"raw": "true"
|
||||
},
|
||||
headers: {
|
||||
"User-Agent": "Test Agent"
|
||||
}
|
||||
};
|
||||
|
||||
axios(config)
|
||||
.then(function (response) {
|
||||
// handle success
|
||||
console.log(response.status);
|
||||
console.log(response.data);
|
||||
})
|
||||
.catch(function (error) {
|
||||
// handle error
|
||||
console.log(error.response.status);
|
||||
console.log(error);
|
||||
});
|
||||
""";
|
||||
expect(axiosCodeGen.getCode(requestModelGet6, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('GET 7', () {
|
||||
const expectedCode = r"""let config = {
|
||||
url: 'https://api.foss42.com',
|
||||
method: 'get'
|
||||
};
|
||||
|
||||
axios(config)
|
||||
.then(function (response) {
|
||||
// handle success
|
||||
console.log(response.status);
|
||||
console.log(response.data);
|
||||
})
|
||||
.catch(function (error) {
|
||||
// handle error
|
||||
console.log(error.response.status);
|
||||
console.log(error);
|
||||
});
|
||||
""";
|
||||
expect(axiosCodeGen.getCode(requestModelGet7, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('GET 8', () {
|
||||
const expectedCode = r"""let config = {
|
||||
url: 'https://api.github.com/repos/foss42/apidash',
|
||||
method: 'get',
|
||||
params: {
|
||||
"raw": "true"
|
||||
},
|
||||
headers: {
|
||||
"User-Agent": "Test Agent"
|
||||
}
|
||||
};
|
||||
|
||||
axios(config)
|
||||
.then(function (response) {
|
||||
// handle success
|
||||
console.log(response.status);
|
||||
console.log(response.data);
|
||||
})
|
||||
.catch(function (error) {
|
||||
// handle error
|
||||
console.log(error.response.status);
|
||||
console.log(error);
|
||||
});
|
||||
""";
|
||||
expect(axiosCodeGen.getCode(requestModelGet8, "https"), expectedCode);
|
||||
});
|
||||
});
|
||||
|
||||
group('HEAD Request', () {
|
||||
test('HEAD 1', () {
|
||||
const expectedCode = r"""let config = {
|
||||
url: 'https://api.foss42.com',
|
||||
method: 'head'
|
||||
};
|
||||
|
||||
axios(config)
|
||||
.then(function (response) {
|
||||
// handle success
|
||||
console.log(response.status);
|
||||
console.log(response.data);
|
||||
})
|
||||
.catch(function (error) {
|
||||
// handle error
|
||||
console.log(error.response.status);
|
||||
console.log(error);
|
||||
});
|
||||
""";
|
||||
expect(axiosCodeGen.getCode(requestModelHead1, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('HEAD 2', () {
|
||||
const expectedCode = r"""let config = {
|
||||
url: 'http://api.foss42.com',
|
||||
method: 'head'
|
||||
};
|
||||
|
||||
axios(config)
|
||||
.then(function (response) {
|
||||
// handle success
|
||||
console.log(response.status);
|
||||
console.log(response.data);
|
||||
})
|
||||
.catch(function (error) {
|
||||
// handle error
|
||||
console.log(error.response.status);
|
||||
console.log(error);
|
||||
});
|
||||
""";
|
||||
expect(axiosCodeGen.getCode(requestModelHead2, "http"), expectedCode);
|
||||
});
|
||||
});
|
||||
|
||||
group('POST Request', () {
|
||||
test('POST 1', () {
|
||||
const expectedCode = r"""let config = {
|
||||
url: 'https://api.foss42.com/case/lower',
|
||||
method: 'post',
|
||||
headers: {
|
||||
"Content-Type": "text/plain"
|
||||
},
|
||||
data: "{\n\"text\": \"I LOVE Flutter\"\n}"
|
||||
};
|
||||
|
||||
axios(config)
|
||||
.then(function (response) {
|
||||
// handle success
|
||||
console.log(response.status);
|
||||
console.log(response.data);
|
||||
})
|
||||
.catch(function (error) {
|
||||
// handle error
|
||||
console.log(error.response.status);
|
||||
console.log(error);
|
||||
});
|
||||
""";
|
||||
expect(axiosCodeGen.getCode(requestModelPost1, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('POST 2', () {
|
||||
const expectedCode = r"""let config = {
|
||||
url: 'https://api.foss42.com/case/lower',
|
||||
method: 'post',
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
data: "{\n\"text\": \"I LOVE Flutter\"\n}"
|
||||
};
|
||||
|
||||
axios(config)
|
||||
.then(function (response) {
|
||||
// handle success
|
||||
console.log(response.status);
|
||||
console.log(response.data);
|
||||
})
|
||||
.catch(function (error) {
|
||||
// handle error
|
||||
console.log(error.response.status);
|
||||
console.log(error);
|
||||
});
|
||||
""";
|
||||
expect(axiosCodeGen.getCode(requestModelPost2, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('POST 3', () {
|
||||
const expectedCode = r"""let config = {
|
||||
url: 'https://api.foss42.com/case/lower',
|
||||
method: 'post',
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"User-Agent": "Test Agent"
|
||||
},
|
||||
data: "{\n\"text\": \"I LOVE Flutter\"\n}"
|
||||
};
|
||||
|
||||
axios(config)
|
||||
.then(function (response) {
|
||||
// handle success
|
||||
console.log(response.status);
|
||||
console.log(response.data);
|
||||
})
|
||||
.catch(function (error) {
|
||||
// handle error
|
||||
console.log(error.response.status);
|
||||
console.log(error);
|
||||
});
|
||||
""";
|
||||
expect(axiosCodeGen.getCode(requestModelPost3, "https"), expectedCode);
|
||||
});
|
||||
});
|
||||
group('PUT Request', () {
|
||||
test('PUT 1', () {
|
||||
const expectedCode = r"""let config = {
|
||||
url: 'https://reqres.in/api/users/2',
|
||||
method: 'put',
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
data: "{\n\"name\": \"morpheus\",\n\"job\": \"zion resident\"\n}"
|
||||
};
|
||||
|
||||
axios(config)
|
||||
.then(function (response) {
|
||||
// handle success
|
||||
console.log(response.status);
|
||||
console.log(response.data);
|
||||
})
|
||||
.catch(function (error) {
|
||||
// handle error
|
||||
console.log(error.response.status);
|
||||
console.log(error);
|
||||
});
|
||||
""";
|
||||
expect(axiosCodeGen.getCode(requestModelPut1, "https"), expectedCode);
|
||||
});
|
||||
});
|
||||
|
||||
group('PATCH Request', () {
|
||||
test('PATCH 1', () {
|
||||
const expectedCode = r"""let config = {
|
||||
url: 'https://reqres.in/api/users/2',
|
||||
method: 'patch',
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
data: "{\n\"name\": \"marfeus\",\n\"job\": \"accountant\"\n}"
|
||||
};
|
||||
|
||||
axios(config)
|
||||
.then(function (response) {
|
||||
// handle success
|
||||
console.log(response.status);
|
||||
console.log(response.data);
|
||||
})
|
||||
.catch(function (error) {
|
||||
// handle error
|
||||
console.log(error.response.status);
|
||||
console.log(error);
|
||||
});
|
||||
""";
|
||||
expect(axiosCodeGen.getCode(requestModelPatch1, "https"), expectedCode);
|
||||
});
|
||||
});
|
||||
|
||||
group('DELETE Request', () {
|
||||
test('DELETE 1', () {
|
||||
const expectedCode = r"""let config = {
|
||||
url: 'https://reqres.in/api/users/2',
|
||||
method: 'delete'
|
||||
};
|
||||
|
||||
axios(config)
|
||||
.then(function (response) {
|
||||
// handle success
|
||||
console.log(response.status);
|
||||
console.log(response.data);
|
||||
})
|
||||
.catch(function (error) {
|
||||
// handle error
|
||||
console.log(error.response.status);
|
||||
console.log(error);
|
||||
});
|
||||
""";
|
||||
expect(axiosCodeGen.getCode(requestModelDelete1, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('DELETE 2', () {
|
||||
const expectedCode = r"""let config = {
|
||||
url: 'https://reqres.in/api/users/2',
|
||||
method: 'delete',
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
data: "{\n\"name\": \"marfeus\",\n\"job\": \"accountant\"\n}"
|
||||
};
|
||||
|
||||
axios(config)
|
||||
.then(function (response) {
|
||||
// handle success
|
||||
console.log(response.status);
|
||||
console.log(response.data);
|
||||
})
|
||||
.catch(function (error) {
|
||||
// handle error
|
||||
console.log(error.response.status);
|
||||
console.log(error);
|
||||
});
|
||||
""";
|
||||
expect(axiosCodeGen.getCode(requestModelDelete2, "https"), expectedCode);
|
||||
});
|
||||
});
|
||||
}
|
489
test/codegen/js_fetch_codegen_test.dart
Normal file
489
test/codegen/js_fetch_codegen_test.dart
Normal file
@ -0,0 +1,489 @@
|
||||
import 'package:apidash/codegen/js/fetch.dart';
|
||||
import '../request_models.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
final fetchCodeGen = FetchCodeGen();
|
||||
|
||||
group('GET Request', () {
|
||||
test('GET 1', () {
|
||||
const expectedCode = r"""let url = 'https://api.foss42.com';
|
||||
|
||||
let options = {
|
||||
method: 'GET'
|
||||
};
|
||||
|
||||
let status;
|
||||
fetch(url, options)
|
||||
.then(res => {
|
||||
status = res.status;
|
||||
return res.json()
|
||||
})
|
||||
.then(body => {
|
||||
console.log(status);
|
||||
console.log(body);
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(status);
|
||||
console.error('error:' + err);
|
||||
});
|
||||
""";
|
||||
expect(fetchCodeGen.getCode(requestModelGet1, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('GET 2', () {
|
||||
const expectedCode =
|
||||
r"""let url = 'https://api.foss42.com/country/data?code=US';
|
||||
|
||||
let options = {
|
||||
method: 'GET'
|
||||
};
|
||||
|
||||
let status;
|
||||
fetch(url, options)
|
||||
.then(res => {
|
||||
status = res.status;
|
||||
return res.json()
|
||||
})
|
||||
.then(body => {
|
||||
console.log(status);
|
||||
console.log(body);
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(status);
|
||||
console.error('error:' + err);
|
||||
});
|
||||
""";
|
||||
expect(fetchCodeGen.getCode(requestModelGet2, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('GET 3', () {
|
||||
const expectedCode =
|
||||
r"""let url = 'https://api.foss42.com/country/data?code=IND';
|
||||
|
||||
let options = {
|
||||
method: 'GET'
|
||||
};
|
||||
|
||||
let status;
|
||||
fetch(url, options)
|
||||
.then(res => {
|
||||
status = res.status;
|
||||
return res.json()
|
||||
})
|
||||
.then(body => {
|
||||
console.log(status);
|
||||
console.log(body);
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(status);
|
||||
console.error('error:' + err);
|
||||
});
|
||||
""";
|
||||
expect(fetchCodeGen.getCode(requestModelGet3, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('GET 4', () {
|
||||
const expectedCode =
|
||||
r"""let url = 'https://api.foss42.com/humanize/social?num=8700000&digits=3&system=SS&add_space=true&trailing_zeros=true';
|
||||
|
||||
let options = {
|
||||
method: 'GET'
|
||||
};
|
||||
|
||||
let status;
|
||||
fetch(url, options)
|
||||
.then(res => {
|
||||
status = res.status;
|
||||
return res.json()
|
||||
})
|
||||
.then(body => {
|
||||
console.log(status);
|
||||
console.log(body);
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(status);
|
||||
console.error('error:' + err);
|
||||
});
|
||||
""";
|
||||
expect(fetchCodeGen.getCode(requestModelGet4, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('GET 5', () {
|
||||
const expectedCode =
|
||||
r"""let url = 'https://api.github.com/repos/foss42/apidash';
|
||||
|
||||
let options = {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
"User-Agent": "Test Agent"
|
||||
}
|
||||
};
|
||||
|
||||
let status;
|
||||
fetch(url, options)
|
||||
.then(res => {
|
||||
status = res.status;
|
||||
return res.json()
|
||||
})
|
||||
.then(body => {
|
||||
console.log(status);
|
||||
console.log(body);
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(status);
|
||||
console.error('error:' + err);
|
||||
});
|
||||
""";
|
||||
expect(fetchCodeGen.getCode(requestModelGet5, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('GET 6', () {
|
||||
const expectedCode =
|
||||
r"""let url = 'https://api.github.com/repos/foss42/apidash?raw=true';
|
||||
|
||||
let options = {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
"User-Agent": "Test Agent"
|
||||
}
|
||||
};
|
||||
|
||||
let status;
|
||||
fetch(url, options)
|
||||
.then(res => {
|
||||
status = res.status;
|
||||
return res.json()
|
||||
})
|
||||
.then(body => {
|
||||
console.log(status);
|
||||
console.log(body);
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(status);
|
||||
console.error('error:' + err);
|
||||
});
|
||||
""";
|
||||
expect(fetchCodeGen.getCode(requestModelGet6, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('GET 7', () {
|
||||
const expectedCode = r"""let url = 'https://api.foss42.com';
|
||||
|
||||
let options = {
|
||||
method: 'GET'
|
||||
};
|
||||
|
||||
let status;
|
||||
fetch(url, options)
|
||||
.then(res => {
|
||||
status = res.status;
|
||||
return res.json()
|
||||
})
|
||||
.then(body => {
|
||||
console.log(status);
|
||||
console.log(body);
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(status);
|
||||
console.error('error:' + err);
|
||||
});
|
||||
""";
|
||||
expect(fetchCodeGen.getCode(requestModelGet7, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('GET 8', () {
|
||||
const expectedCode =
|
||||
r"""let url = 'https://api.github.com/repos/foss42/apidash?raw=true';
|
||||
|
||||
let options = {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
"User-Agent": "Test Agent"
|
||||
}
|
||||
};
|
||||
|
||||
let status;
|
||||
fetch(url, options)
|
||||
.then(res => {
|
||||
status = res.status;
|
||||
return res.json()
|
||||
})
|
||||
.then(body => {
|
||||
console.log(status);
|
||||
console.log(body);
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(status);
|
||||
console.error('error:' + err);
|
||||
});
|
||||
""";
|
||||
expect(fetchCodeGen.getCode(requestModelGet8, "https"), expectedCode);
|
||||
});
|
||||
});
|
||||
|
||||
group('HEAD Request', () {
|
||||
test('HEAD 1', () {
|
||||
const expectedCode = r"""let url = 'https://api.foss42.com';
|
||||
|
||||
let options = {
|
||||
method: 'HEAD'
|
||||
};
|
||||
|
||||
let status;
|
||||
fetch(url, options)
|
||||
.then(res => {
|
||||
status = res.status;
|
||||
return res.json()
|
||||
})
|
||||
.then(body => {
|
||||
console.log(status);
|
||||
console.log(body);
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(status);
|
||||
console.error('error:' + err);
|
||||
});
|
||||
""";
|
||||
expect(fetchCodeGen.getCode(requestModelHead1, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('HEAD 2', () {
|
||||
const expectedCode = r"""let url = 'http://api.foss42.com';
|
||||
|
||||
let options = {
|
||||
method: 'HEAD'
|
||||
};
|
||||
|
||||
let status;
|
||||
fetch(url, options)
|
||||
.then(res => {
|
||||
status = res.status;
|
||||
return res.json()
|
||||
})
|
||||
.then(body => {
|
||||
console.log(status);
|
||||
console.log(body);
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(status);
|
||||
console.error('error:' + err);
|
||||
});
|
||||
""";
|
||||
expect(fetchCodeGen.getCode(requestModelHead2, "http"), expectedCode);
|
||||
});
|
||||
});
|
||||
|
||||
group('POST Request', () {
|
||||
test('POST 1', () {
|
||||
const expectedCode = r"""let url = 'https://api.foss42.com/case/lower';
|
||||
|
||||
let options = {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
"Content-Type": "text/plain"
|
||||
},
|
||||
body:
|
||||
"{\n\"text\": \"I LOVE Flutter\"\n}"
|
||||
};
|
||||
|
||||
let status;
|
||||
fetch(url, options)
|
||||
.then(res => {
|
||||
status = res.status;
|
||||
return res.json()
|
||||
})
|
||||
.then(body => {
|
||||
console.log(status);
|
||||
console.log(body);
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(status);
|
||||
console.error('error:' + err);
|
||||
});
|
||||
""";
|
||||
expect(fetchCodeGen.getCode(requestModelPost1, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('POST 2', () {
|
||||
const expectedCode = r"""let url = 'https://api.foss42.com/case/lower';
|
||||
|
||||
let options = {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
body:
|
||||
"{\n\"text\": \"I LOVE Flutter\"\n}"
|
||||
};
|
||||
|
||||
let status;
|
||||
fetch(url, options)
|
||||
.then(res => {
|
||||
status = res.status;
|
||||
return res.json()
|
||||
})
|
||||
.then(body => {
|
||||
console.log(status);
|
||||
console.log(body);
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(status);
|
||||
console.error('error:' + err);
|
||||
});
|
||||
""";
|
||||
expect(fetchCodeGen.getCode(requestModelPost2, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('POST 3', () {
|
||||
const expectedCode = r"""let url = 'https://api.foss42.com/case/lower';
|
||||
|
||||
let options = {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"User-Agent": "Test Agent"
|
||||
},
|
||||
body:
|
||||
"{\n\"text\": \"I LOVE Flutter\"\n}"
|
||||
};
|
||||
|
||||
let status;
|
||||
fetch(url, options)
|
||||
.then(res => {
|
||||
status = res.status;
|
||||
return res.json()
|
||||
})
|
||||
.then(body => {
|
||||
console.log(status);
|
||||
console.log(body);
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(status);
|
||||
console.error('error:' + err);
|
||||
});
|
||||
""";
|
||||
expect(fetchCodeGen.getCode(requestModelPost3, "https"), expectedCode);
|
||||
});
|
||||
});
|
||||
group('PUT Request', () {
|
||||
test('PUT 1', () {
|
||||
const expectedCode = r"""let url = 'https://reqres.in/api/users/2';
|
||||
|
||||
let options = {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
body:
|
||||
"{\n\"name\": \"morpheus\",\n\"job\": \"zion resident\"\n}"
|
||||
};
|
||||
|
||||
let status;
|
||||
fetch(url, options)
|
||||
.then(res => {
|
||||
status = res.status;
|
||||
return res.json()
|
||||
})
|
||||
.then(body => {
|
||||
console.log(status);
|
||||
console.log(body);
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(status);
|
||||
console.error('error:' + err);
|
||||
});
|
||||
""";
|
||||
expect(fetchCodeGen.getCode(requestModelPut1, "https"), expectedCode);
|
||||
});
|
||||
});
|
||||
|
||||
group('PATCH Request', () {
|
||||
test('PATCH 1', () {
|
||||
const expectedCode = r"""let url = 'https://reqres.in/api/users/2';
|
||||
|
||||
let options = {
|
||||
method: 'PATCH',
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
body:
|
||||
"{\n\"name\": \"marfeus\",\n\"job\": \"accountant\"\n}"
|
||||
};
|
||||
|
||||
let status;
|
||||
fetch(url, options)
|
||||
.then(res => {
|
||||
status = res.status;
|
||||
return res.json()
|
||||
})
|
||||
.then(body => {
|
||||
console.log(status);
|
||||
console.log(body);
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(status);
|
||||
console.error('error:' + err);
|
||||
});
|
||||
""";
|
||||
expect(fetchCodeGen.getCode(requestModelPatch1, "https"), expectedCode);
|
||||
});
|
||||
});
|
||||
|
||||
group('DELETE Request', () {
|
||||
test('DELETE 1', () {
|
||||
const expectedCode = r"""let url = 'https://reqres.in/api/users/2';
|
||||
|
||||
let options = {
|
||||
method: 'DELETE'
|
||||
};
|
||||
|
||||
let status;
|
||||
fetch(url, options)
|
||||
.then(res => {
|
||||
status = res.status;
|
||||
return res.json()
|
||||
})
|
||||
.then(body => {
|
||||
console.log(status);
|
||||
console.log(body);
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(status);
|
||||
console.error('error:' + err);
|
||||
});
|
||||
""";
|
||||
expect(fetchCodeGen.getCode(requestModelDelete1, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('DELETE 2', () {
|
||||
const expectedCode = r"""let url = 'https://reqres.in/api/users/2';
|
||||
|
||||
let options = {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
body:
|
||||
"{\n\"name\": \"marfeus\",\n\"job\": \"accountant\"\n}"
|
||||
};
|
||||
|
||||
let status;
|
||||
fetch(url, options)
|
||||
.then(res => {
|
||||
status = res.status;
|
||||
return res.json()
|
||||
})
|
||||
.then(body => {
|
||||
console.log(status);
|
||||
console.log(body);
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(status);
|
||||
console.error('error:' + err);
|
||||
});
|
||||
""";
|
||||
expect(fetchCodeGen.getCode(requestModelDelete2, "https"), expectedCode);
|
||||
});
|
||||
});
|
||||
}
|
@ -1,126 +1,500 @@
|
||||
import 'package:apidash/codegen/kotlin/pkg_okhttp.dart';
|
||||
import 'package:apidash/codegen/kotlin/okhttp.dart';
|
||||
import 'package:test/test.dart';
|
||||
import 'request_models.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
|
||||
group('GET Request', () {
|
||||
test('GET 1', () {
|
||||
const expectedCode = r"""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")
|
||||
fun main() {
|
||||
val client = OkHttpClient()
|
||||
|
||||
val url = "https://api.foss42.com"
|
||||
|
||||
val request = Request.Builder()
|
||||
.url(url)
|
||||
.get()
|
||||
.build()
|
||||
val response = client.newCall(request).execute()
|
||||
|
||||
println(response.body!!.string())
|
||||
val response = client.newCall(request).execute()
|
||||
|
||||
println(response.code)
|
||||
println(response.body?.string())
|
||||
}
|
||||
""";
|
||||
expect(kotlinOkHttpCodeGen.getCode(requestModelGet1), expectedCode);
|
||||
expect(
|
||||
kotlinOkHttpCodeGen.getCode(requestModelGet1, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('getCode returns valid code for POST request', () {
|
||||
const expectedCode = r"""import okhttp3.MediaType.Companion.toMediaType
|
||||
import okhttp3.MultipartBody
|
||||
import okhttp3.OkHttpClient
|
||||
test('GET 2', () {
|
||||
const expectedCode = r"""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
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
|
||||
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)
|
||||
fun main() {
|
||||
val client = OkHttpClient()
|
||||
|
||||
val url = "https://api.foss42.com/country/data".toHttpUrl().newBuilder()
|
||||
.addQueryParameter("code", "US")
|
||||
.build()
|
||||
val response = client.newCall(request).execute()
|
||||
|
||||
println(response.body!!.string())
|
||||
val request = Request.Builder()
|
||||
.url(url)
|
||||
.get()
|
||||
.build()
|
||||
|
||||
val response = client.newCall(request).execute()
|
||||
|
||||
println(response.code)
|
||||
println(response.body?.string())
|
||||
}
|
||||
""";
|
||||
expect(kotlinOkHttpCodeGen.getCode(requestModelPost1), expectedCode);
|
||||
expect(
|
||||
kotlinOkHttpCodeGen.getCode(requestModelGet2, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('getCode returns valid code for DELETE request', () {
|
||||
const expectedCode = r"""import okhttp3.MediaType.Companion.toMediaType
|
||||
import okhttp3.MultipartBody
|
||||
import okhttp3.OkHttpClient
|
||||
test('GET 3', () {
|
||||
const expectedCode = r"""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
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
|
||||
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)
|
||||
fun main() {
|
||||
val client = OkHttpClient()
|
||||
|
||||
val url = "https://api.foss42.com/country/data".toHttpUrl().newBuilder()
|
||||
.addQueryParameter("code", "IND")
|
||||
.build()
|
||||
val response = client.newCall(request).execute()
|
||||
|
||||
println(response.body!!.string())
|
||||
val request = Request.Builder()
|
||||
.url(url)
|
||||
.get()
|
||||
.build()
|
||||
|
||||
val response = client.newCall(request).execute()
|
||||
|
||||
println(response.code)
|
||||
println(response.body?.string())
|
||||
}
|
||||
""";
|
||||
expect(kotlinOkHttpCodeGen.getCode(requestModelDelete1), expectedCode);
|
||||
expect(
|
||||
kotlinOkHttpCodeGen.getCode(requestModelGet3, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('getCode returns valid code for HEAD request', () {
|
||||
const expectedCode = """import okhttp3.MediaType.Companion.toMediaType
|
||||
import okhttp3.MultipartBody
|
||||
import okhttp3.OkHttpClient
|
||||
test('GET 4', () {
|
||||
const expectedCode = r"""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
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
|
||||
val client = OkHttpClient()
|
||||
val request = Request.Builder()
|
||||
.url("https://jsonplaceholder.typicode.com/posts/1")
|
||||
fun main() {
|
||||
val client = OkHttpClient()
|
||||
|
||||
val url = "https://api.foss42.com/humanize/social".toHttpUrl().newBuilder()
|
||||
.addQueryParameter("num", "8700000")
|
||||
.addQueryParameter("digits", "3")
|
||||
.addQueryParameter("system", "SS")
|
||||
.addQueryParameter("add_space", "true")
|
||||
.addQueryParameter("trailing_zeros", "true")
|
||||
.build()
|
||||
|
||||
val request = Request.Builder()
|
||||
.url(url)
|
||||
.get()
|
||||
.build()
|
||||
|
||||
val response = client.newCall(request).execute()
|
||||
|
||||
println(response.code)
|
||||
println(response.body?.string())
|
||||
}
|
||||
""";
|
||||
expect(
|
||||
kotlinOkHttpCodeGen.getCode(requestModelGet4, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('GET 5', () {
|
||||
const expectedCode = r"""import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
|
||||
fun main() {
|
||||
val client = OkHttpClient()
|
||||
|
||||
val url = "https://api.github.com/repos/foss42/apidash"
|
||||
|
||||
val request = Request.Builder()
|
||||
.url(url)
|
||||
.addHeader("User-Agent", "Test Agent")
|
||||
.get()
|
||||
.build()
|
||||
|
||||
val response = client.newCall(request).execute()
|
||||
|
||||
println(response.code)
|
||||
println(response.body?.string())
|
||||
}
|
||||
""";
|
||||
expect(
|
||||
kotlinOkHttpCodeGen.getCode(requestModelGet5, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('GET 6', () {
|
||||
const expectedCode = r"""import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
|
||||
fun main() {
|
||||
val client = OkHttpClient()
|
||||
|
||||
val url = "https://api.github.com/repos/foss42/apidash".toHttpUrl().newBuilder()
|
||||
.addQueryParameter("raw", "true")
|
||||
.build()
|
||||
|
||||
val request = Request.Builder()
|
||||
.url(url)
|
||||
.addHeader("User-Agent", "Test Agent")
|
||||
.get()
|
||||
.build()
|
||||
|
||||
val response = client.newCall(request).execute()
|
||||
|
||||
println(response.code)
|
||||
println(response.body?.string())
|
||||
}
|
||||
""";
|
||||
expect(
|
||||
kotlinOkHttpCodeGen.getCode(requestModelGet6, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('GET 7', () {
|
||||
const expectedCode = r"""import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
|
||||
fun main() {
|
||||
val client = OkHttpClient()
|
||||
|
||||
val url = "https://api.foss42.com"
|
||||
|
||||
val request = Request.Builder()
|
||||
.url(url)
|
||||
.get()
|
||||
.build()
|
||||
|
||||
val response = client.newCall(request).execute()
|
||||
|
||||
println(response.code)
|
||||
println(response.body?.string())
|
||||
}
|
||||
""";
|
||||
expect(
|
||||
kotlinOkHttpCodeGen.getCode(requestModelGet7, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('GET 8', () {
|
||||
const expectedCode = r"""import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
|
||||
fun main() {
|
||||
val client = OkHttpClient()
|
||||
|
||||
val url = "https://api.github.com/repos/foss42/apidash".toHttpUrl().newBuilder()
|
||||
.addQueryParameter("raw", "true")
|
||||
.build()
|
||||
|
||||
val request = Request.Builder()
|
||||
.url(url)
|
||||
.addHeader("User-Agent", "Test Agent")
|
||||
.get()
|
||||
.build()
|
||||
|
||||
val response = client.newCall(request).execute()
|
||||
|
||||
println(response.code)
|
||||
println(response.body?.string())
|
||||
}
|
||||
""";
|
||||
expect(
|
||||
kotlinOkHttpCodeGen.getCode(requestModelGet8, "https"), expectedCode);
|
||||
});
|
||||
});
|
||||
|
||||
group('HEAD Request', () {
|
||||
test('HEAD 1', () {
|
||||
const expectedCode = r"""import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
|
||||
fun main() {
|
||||
val client = OkHttpClient()
|
||||
|
||||
val url = "https://api.foss42.com"
|
||||
|
||||
val request = Request.Builder()
|
||||
.url(url)
|
||||
.head()
|
||||
.build()
|
||||
val response = client.newCall(request).execute()
|
||||
|
||||
println(response.body!!.string())
|
||||
val response = client.newCall(request).execute()
|
||||
|
||||
println(response.code)
|
||||
println(response.body?.string())
|
||||
}
|
||||
""";
|
||||
expect(kotlinOkHttpCodeGen.getCode(requestModelHead1), expectedCode);
|
||||
expect(kotlinOkHttpCodeGen.getCode(requestModelHead1, "https"),
|
||||
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
|
||||
test('HEAD 2', () {
|
||||
const expectedCode = r"""import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
|
||||
fun main() {
|
||||
val client = OkHttpClient()
|
||||
|
||||
val url = "http://api.foss42.com"
|
||||
|
||||
val request = Request.Builder()
|
||||
.url(url)
|
||||
.head()
|
||||
.build()
|
||||
|
||||
val response = client.newCall(request).execute()
|
||||
|
||||
println(response.code)
|
||||
println(response.body?.string())
|
||||
}
|
||||
""";
|
||||
expect(
|
||||
kotlinOkHttpCodeGen.getCode(requestModelHead2, "http"), expectedCode);
|
||||
});
|
||||
});
|
||||
|
||||
group('POST Request', () {
|
||||
test('POST 1', () {
|
||||
const expectedCode = r'''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
|
||||
import okhttp3.MediaType.Companion.toMediaType
|
||||
|
||||
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")
|
||||
fun main() {
|
||||
val client = OkHttpClient()
|
||||
|
||||
val url = "https://api.foss42.com/case/lower"
|
||||
|
||||
val mediaType = "text/plain".toMediaType()
|
||||
|
||||
val body = """{
|
||||
"text": "I LOVE Flutter"
|
||||
}""".toRequestBody(mediaType)
|
||||
|
||||
val request = Request.Builder()
|
||||
.url(url)
|
||||
.post(body)
|
||||
.build()
|
||||
val response = client.newCall(request).execute()
|
||||
|
||||
println(response.body!!.string())
|
||||
val response = client.newCall(request).execute()
|
||||
|
||||
println(response.code)
|
||||
println(response.body?.string())
|
||||
}
|
||||
''';
|
||||
expect(kotlinOkHttpCodeGen.getCode(requestModelPost1, "https"),
|
||||
expectedCode);
|
||||
});
|
||||
|
||||
test('POST 2', () {
|
||||
const expectedCode = r'''import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.RequestBody.Companion.toRequestBody
|
||||
import okhttp3.MediaType.Companion.toMediaType
|
||||
|
||||
fun main() {
|
||||
val client = OkHttpClient()
|
||||
|
||||
val url = "https://api.foss42.com/case/lower"
|
||||
|
||||
val mediaType = "application/json".toMediaType()
|
||||
|
||||
val body = """{
|
||||
"text": "I LOVE Flutter"
|
||||
}""".toRequestBody(mediaType)
|
||||
|
||||
val request = Request.Builder()
|
||||
.url(url)
|
||||
.post(body)
|
||||
.build()
|
||||
|
||||
val response = client.newCall(request).execute()
|
||||
|
||||
println(response.code)
|
||||
println(response.body?.string())
|
||||
}
|
||||
''';
|
||||
expect(kotlinOkHttpCodeGen.getCode(requestModelPost2, "https"),
|
||||
expectedCode);
|
||||
});
|
||||
|
||||
test('POST 3', () {
|
||||
const expectedCode = r'''import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.RequestBody.Companion.toRequestBody
|
||||
import okhttp3.MediaType.Companion.toMediaType
|
||||
|
||||
fun main() {
|
||||
val client = OkHttpClient()
|
||||
|
||||
val url = "https://api.foss42.com/case/lower"
|
||||
|
||||
val mediaType = "application/json".toMediaType()
|
||||
|
||||
val body = """{
|
||||
"text": "I LOVE Flutter"
|
||||
}""".toRequestBody(mediaType)
|
||||
|
||||
val request = Request.Builder()
|
||||
.url(url)
|
||||
.addHeader("User-Agent", "Test Agent")
|
||||
.post(body)
|
||||
.build()
|
||||
|
||||
val response = client.newCall(request).execute()
|
||||
|
||||
println(response.code)
|
||||
println(response.body?.string())
|
||||
}
|
||||
''';
|
||||
expect(kotlinOkHttpCodeGen.getCode(requestModelPost3, "https"),
|
||||
expectedCode);
|
||||
});
|
||||
});
|
||||
group('PUT Request', () {
|
||||
test('PUT 1', () {
|
||||
const expectedCode = r'''import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.RequestBody.Companion.toRequestBody
|
||||
import okhttp3.MediaType.Companion.toMediaType
|
||||
|
||||
fun main() {
|
||||
val client = OkHttpClient()
|
||||
|
||||
val url = "https://reqres.in/api/users/2"
|
||||
|
||||
val mediaType = "application/json".toMediaType()
|
||||
|
||||
val body = """{
|
||||
"name": "morpheus",
|
||||
"job": "zion resident"
|
||||
}""".toRequestBody(mediaType)
|
||||
|
||||
val request = Request.Builder()
|
||||
.url(url)
|
||||
.put(body)
|
||||
.build()
|
||||
|
||||
val response = client.newCall(request).execute()
|
||||
|
||||
println(response.code)
|
||||
println(response.body?.string())
|
||||
}
|
||||
''';
|
||||
expect(
|
||||
kotlinOkHttpCodeGen.getCode(requestModelPut1, "https"), expectedCode);
|
||||
});
|
||||
});
|
||||
|
||||
group('PATCH Request', () {
|
||||
test('PATCH 1', () {
|
||||
const expectedCode = r'''import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.RequestBody.Companion.toRequestBody
|
||||
import okhttp3.MediaType.Companion.toMediaType
|
||||
|
||||
fun main() {
|
||||
val client = OkHttpClient()
|
||||
|
||||
val url = "https://reqres.in/api/users/2"
|
||||
|
||||
val mediaType = "application/json".toMediaType()
|
||||
|
||||
val body = """{
|
||||
"name": "marfeus",
|
||||
"job": "accountant"
|
||||
}""".toRequestBody(mediaType)
|
||||
|
||||
val request = Request.Builder()
|
||||
.url(url)
|
||||
.patch(body)
|
||||
.build()
|
||||
|
||||
val response = client.newCall(request).execute()
|
||||
|
||||
println(response.code)
|
||||
println(response.body?.string())
|
||||
}
|
||||
''';
|
||||
expect(kotlinOkHttpCodeGen.getCode(requestModelPatch1, "https"),
|
||||
expectedCode);
|
||||
});
|
||||
});
|
||||
|
||||
group('DELETE Request', () {
|
||||
test('DELETE 1', () {
|
||||
const expectedCode = r"""import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
|
||||
fun main() {
|
||||
val client = OkHttpClient()
|
||||
|
||||
val url = "https://reqres.in/api/users/2"
|
||||
|
||||
val request = Request.Builder()
|
||||
.url(url)
|
||||
.delete()
|
||||
.build()
|
||||
|
||||
val response = client.newCall(request).execute()
|
||||
|
||||
println(response.code)
|
||||
println(response.body?.string())
|
||||
}
|
||||
""";
|
||||
expect(kotlinOkHttpCodeGen.getCode(requestModelGet2), expectedCode);
|
||||
expect(kotlinOkHttpCodeGen.getCode(requestModelDelete1, "https"),
|
||||
expectedCode);
|
||||
});
|
||||
|
||||
test('DELETE 2', () {
|
||||
const expectedCode = r'''import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.RequestBody.Companion.toRequestBody
|
||||
import okhttp3.MediaType.Companion.toMediaType
|
||||
|
||||
fun main() {
|
||||
val client = OkHttpClient()
|
||||
|
||||
val url = "https://reqres.in/api/users/2"
|
||||
|
||||
val mediaType = "application/json".toMediaType()
|
||||
|
||||
val body = """{
|
||||
"name": "marfeus",
|
||||
"job": "accountant"
|
||||
}""".toRequestBody(mediaType)
|
||||
|
||||
val request = Request.Builder()
|
||||
.url(url)
|
||||
.delete(body)
|
||||
.build()
|
||||
|
||||
val response = client.newCall(request).execute()
|
||||
|
||||
println(response.code)
|
||||
println(response.body?.string())
|
||||
}
|
||||
''';
|
||||
expect(kotlinOkHttpCodeGen.getCode(requestModelDelete2, "https"),
|
||||
expectedCode);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
462
test/codegen/nodejs_axios_codegen_test.dart
Normal file
462
test/codegen/nodejs_axios_codegen_test.dart
Normal file
@ -0,0 +1,462 @@
|
||||
import 'package:apidash/codegen/js/axios.dart';
|
||||
import '../request_models.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
final axiosCodeGen = AxiosCodeGen(isNodeJs: true);
|
||||
|
||||
group('GET Request', () {
|
||||
test('GET 1', () {
|
||||
const expectedCode = r"""import axios from 'axios';
|
||||
|
||||
let config = {
|
||||
url: 'https://api.foss42.com',
|
||||
method: 'get'
|
||||
};
|
||||
|
||||
axios(config)
|
||||
.then(function (response) {
|
||||
// handle success
|
||||
console.log(response.status);
|
||||
console.log(response.data);
|
||||
})
|
||||
.catch(function (error) {
|
||||
// handle error
|
||||
console.log(error.response.status);
|
||||
console.log(error);
|
||||
});
|
||||
""";
|
||||
expect(axiosCodeGen.getCode(requestModelGet1, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('GET 2', () {
|
||||
const expectedCode = r"""import axios from 'axios';
|
||||
|
||||
let config = {
|
||||
url: 'https://api.foss42.com/country/data',
|
||||
method: 'get',
|
||||
params: {
|
||||
"code": "US"
|
||||
}
|
||||
};
|
||||
|
||||
axios(config)
|
||||
.then(function (response) {
|
||||
// handle success
|
||||
console.log(response.status);
|
||||
console.log(response.data);
|
||||
})
|
||||
.catch(function (error) {
|
||||
// handle error
|
||||
console.log(error.response.status);
|
||||
console.log(error);
|
||||
});
|
||||
""";
|
||||
expect(axiosCodeGen.getCode(requestModelGet2, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('GET 3', () {
|
||||
const expectedCode = r"""import axios from 'axios';
|
||||
|
||||
let config = {
|
||||
url: 'https://api.foss42.com/country/data',
|
||||
method: 'get',
|
||||
params: {
|
||||
"code": "IND"
|
||||
}
|
||||
};
|
||||
|
||||
axios(config)
|
||||
.then(function (response) {
|
||||
// handle success
|
||||
console.log(response.status);
|
||||
console.log(response.data);
|
||||
})
|
||||
.catch(function (error) {
|
||||
// handle error
|
||||
console.log(error.response.status);
|
||||
console.log(error);
|
||||
});
|
||||
""";
|
||||
expect(axiosCodeGen.getCode(requestModelGet3, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('GET 4', () {
|
||||
const expectedCode = r"""import axios from 'axios';
|
||||
|
||||
let config = {
|
||||
url: 'https://api.foss42.com/humanize/social',
|
||||
method: 'get',
|
||||
params: {
|
||||
"num": "8700000",
|
||||
"digits": "3",
|
||||
"system": "SS",
|
||||
"add_space": "true",
|
||||
"trailing_zeros": "true"
|
||||
}
|
||||
};
|
||||
|
||||
axios(config)
|
||||
.then(function (response) {
|
||||
// handle success
|
||||
console.log(response.status);
|
||||
console.log(response.data);
|
||||
})
|
||||
.catch(function (error) {
|
||||
// handle error
|
||||
console.log(error.response.status);
|
||||
console.log(error);
|
||||
});
|
||||
""";
|
||||
expect(axiosCodeGen.getCode(requestModelGet4, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('GET 5', () {
|
||||
const expectedCode = r"""import axios from 'axios';
|
||||
|
||||
let config = {
|
||||
url: 'https://api.github.com/repos/foss42/apidash',
|
||||
method: 'get',
|
||||
headers: {
|
||||
"User-Agent": "Test Agent"
|
||||
}
|
||||
};
|
||||
|
||||
axios(config)
|
||||
.then(function (response) {
|
||||
// handle success
|
||||
console.log(response.status);
|
||||
console.log(response.data);
|
||||
})
|
||||
.catch(function (error) {
|
||||
// handle error
|
||||
console.log(error.response.status);
|
||||
console.log(error);
|
||||
});
|
||||
""";
|
||||
expect(axiosCodeGen.getCode(requestModelGet5, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('GET 6', () {
|
||||
const expectedCode = r"""import axios from 'axios';
|
||||
|
||||
let config = {
|
||||
url: 'https://api.github.com/repos/foss42/apidash',
|
||||
method: 'get',
|
||||
params: {
|
||||
"raw": "true"
|
||||
},
|
||||
headers: {
|
||||
"User-Agent": "Test Agent"
|
||||
}
|
||||
};
|
||||
|
||||
axios(config)
|
||||
.then(function (response) {
|
||||
// handle success
|
||||
console.log(response.status);
|
||||
console.log(response.data);
|
||||
})
|
||||
.catch(function (error) {
|
||||
// handle error
|
||||
console.log(error.response.status);
|
||||
console.log(error);
|
||||
});
|
||||
""";
|
||||
expect(axiosCodeGen.getCode(requestModelGet6, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('GET 7', () {
|
||||
const expectedCode = r"""import axios from 'axios';
|
||||
|
||||
let config = {
|
||||
url: 'https://api.foss42.com',
|
||||
method: 'get'
|
||||
};
|
||||
|
||||
axios(config)
|
||||
.then(function (response) {
|
||||
// handle success
|
||||
console.log(response.status);
|
||||
console.log(response.data);
|
||||
})
|
||||
.catch(function (error) {
|
||||
// handle error
|
||||
console.log(error.response.status);
|
||||
console.log(error);
|
||||
});
|
||||
""";
|
||||
expect(axiosCodeGen.getCode(requestModelGet7, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('GET 8', () {
|
||||
const expectedCode = r"""import axios from 'axios';
|
||||
|
||||
let config = {
|
||||
url: 'https://api.github.com/repos/foss42/apidash',
|
||||
method: 'get',
|
||||
params: {
|
||||
"raw": "true"
|
||||
},
|
||||
headers: {
|
||||
"User-Agent": "Test Agent"
|
||||
}
|
||||
};
|
||||
|
||||
axios(config)
|
||||
.then(function (response) {
|
||||
// handle success
|
||||
console.log(response.status);
|
||||
console.log(response.data);
|
||||
})
|
||||
.catch(function (error) {
|
||||
// handle error
|
||||
console.log(error.response.status);
|
||||
console.log(error);
|
||||
});
|
||||
""";
|
||||
expect(axiosCodeGen.getCode(requestModelGet8, "https"), expectedCode);
|
||||
});
|
||||
});
|
||||
|
||||
group('HEAD Request', () {
|
||||
test('HEAD 1', () {
|
||||
const expectedCode = r"""import axios from 'axios';
|
||||
|
||||
let config = {
|
||||
url: 'https://api.foss42.com',
|
||||
method: 'head'
|
||||
};
|
||||
|
||||
axios(config)
|
||||
.then(function (response) {
|
||||
// handle success
|
||||
console.log(response.status);
|
||||
console.log(response.data);
|
||||
})
|
||||
.catch(function (error) {
|
||||
// handle error
|
||||
console.log(error.response.status);
|
||||
console.log(error);
|
||||
});
|
||||
""";
|
||||
expect(axiosCodeGen.getCode(requestModelHead1, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('HEAD 2', () {
|
||||
const expectedCode = r"""import axios from 'axios';
|
||||
|
||||
let config = {
|
||||
url: 'http://api.foss42.com',
|
||||
method: 'head'
|
||||
};
|
||||
|
||||
axios(config)
|
||||
.then(function (response) {
|
||||
// handle success
|
||||
console.log(response.status);
|
||||
console.log(response.data);
|
||||
})
|
||||
.catch(function (error) {
|
||||
// handle error
|
||||
console.log(error.response.status);
|
||||
console.log(error);
|
||||
});
|
||||
""";
|
||||
expect(axiosCodeGen.getCode(requestModelHead2, "http"), expectedCode);
|
||||
});
|
||||
});
|
||||
|
||||
group('POST Request', () {
|
||||
test('POST 1', () {
|
||||
const expectedCode = r"""import axios from 'axios';
|
||||
|
||||
let config = {
|
||||
url: 'https://api.foss42.com/case/lower',
|
||||
method: 'post',
|
||||
headers: {
|
||||
"Content-Type": "text/plain"
|
||||
},
|
||||
data: "{\n\"text\": \"I LOVE Flutter\"\n}"
|
||||
};
|
||||
|
||||
axios(config)
|
||||
.then(function (response) {
|
||||
// handle success
|
||||
console.log(response.status);
|
||||
console.log(response.data);
|
||||
})
|
||||
.catch(function (error) {
|
||||
// handle error
|
||||
console.log(error.response.status);
|
||||
console.log(error);
|
||||
});
|
||||
""";
|
||||
expect(axiosCodeGen.getCode(requestModelPost1, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('POST 2', () {
|
||||
const expectedCode = r"""import axios from 'axios';
|
||||
|
||||
let config = {
|
||||
url: 'https://api.foss42.com/case/lower',
|
||||
method: 'post',
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
data: "{\n\"text\": \"I LOVE Flutter\"\n}"
|
||||
};
|
||||
|
||||
axios(config)
|
||||
.then(function (response) {
|
||||
// handle success
|
||||
console.log(response.status);
|
||||
console.log(response.data);
|
||||
})
|
||||
.catch(function (error) {
|
||||
// handle error
|
||||
console.log(error.response.status);
|
||||
console.log(error);
|
||||
});
|
||||
""";
|
||||
expect(axiosCodeGen.getCode(requestModelPost2, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('POST 3', () {
|
||||
const expectedCode = r"""import axios from 'axios';
|
||||
|
||||
let config = {
|
||||
url: 'https://api.foss42.com/case/lower',
|
||||
method: 'post',
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"User-Agent": "Test Agent"
|
||||
},
|
||||
data: "{\n\"text\": \"I LOVE Flutter\"\n}"
|
||||
};
|
||||
|
||||
axios(config)
|
||||
.then(function (response) {
|
||||
// handle success
|
||||
console.log(response.status);
|
||||
console.log(response.data);
|
||||
})
|
||||
.catch(function (error) {
|
||||
// handle error
|
||||
console.log(error.response.status);
|
||||
console.log(error);
|
||||
});
|
||||
""";
|
||||
expect(axiosCodeGen.getCode(requestModelPost3, "https"), expectedCode);
|
||||
});
|
||||
});
|
||||
group('PUT Request', () {
|
||||
test('PUT 1', () {
|
||||
const expectedCode = r"""import axios from 'axios';
|
||||
|
||||
let config = {
|
||||
url: 'https://reqres.in/api/users/2',
|
||||
method: 'put',
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
data: "{\n\"name\": \"morpheus\",\n\"job\": \"zion resident\"\n}"
|
||||
};
|
||||
|
||||
axios(config)
|
||||
.then(function (response) {
|
||||
// handle success
|
||||
console.log(response.status);
|
||||
console.log(response.data);
|
||||
})
|
||||
.catch(function (error) {
|
||||
// handle error
|
||||
console.log(error.response.status);
|
||||
console.log(error);
|
||||
});
|
||||
""";
|
||||
expect(axiosCodeGen.getCode(requestModelPut1, "https"), expectedCode);
|
||||
});
|
||||
});
|
||||
|
||||
group('PATCH Request', () {
|
||||
test('PATCH 1', () {
|
||||
const expectedCode = r"""import axios from 'axios';
|
||||
|
||||
let config = {
|
||||
url: 'https://reqres.in/api/users/2',
|
||||
method: 'patch',
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
data: "{\n\"name\": \"marfeus\",\n\"job\": \"accountant\"\n}"
|
||||
};
|
||||
|
||||
axios(config)
|
||||
.then(function (response) {
|
||||
// handle success
|
||||
console.log(response.status);
|
||||
console.log(response.data);
|
||||
})
|
||||
.catch(function (error) {
|
||||
// handle error
|
||||
console.log(error.response.status);
|
||||
console.log(error);
|
||||
});
|
||||
""";
|
||||
expect(axiosCodeGen.getCode(requestModelPatch1, "https"), expectedCode);
|
||||
});
|
||||
});
|
||||
|
||||
group('DELETE Request', () {
|
||||
test('DELETE 1', () {
|
||||
const expectedCode = r"""import axios from 'axios';
|
||||
|
||||
let config = {
|
||||
url: 'https://reqres.in/api/users/2',
|
||||
method: 'delete'
|
||||
};
|
||||
|
||||
axios(config)
|
||||
.then(function (response) {
|
||||
// handle success
|
||||
console.log(response.status);
|
||||
console.log(response.data);
|
||||
})
|
||||
.catch(function (error) {
|
||||
// handle error
|
||||
console.log(error.response.status);
|
||||
console.log(error);
|
||||
});
|
||||
""";
|
||||
expect(axiosCodeGen.getCode(requestModelDelete1, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('DELETE 2', () {
|
||||
const expectedCode = r"""import axios from 'axios';
|
||||
|
||||
let config = {
|
||||
url: 'https://reqres.in/api/users/2',
|
||||
method: 'delete',
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
data: "{\n\"name\": \"marfeus\",\n\"job\": \"accountant\"\n}"
|
||||
};
|
||||
|
||||
axios(config)
|
||||
.then(function (response) {
|
||||
// handle success
|
||||
console.log(response.status);
|
||||
console.log(response.data);
|
||||
})
|
||||
.catch(function (error) {
|
||||
// handle error
|
||||
console.log(error.response.status);
|
||||
console.log(error);
|
||||
});
|
||||
""";
|
||||
expect(axiosCodeGen.getCode(requestModelDelete2, "https"), expectedCode);
|
||||
});
|
||||
});
|
||||
}
|
517
test/codegen/nodejs_fetch_codegen_test.dart
Normal file
517
test/codegen/nodejs_fetch_codegen_test.dart
Normal file
@ -0,0 +1,517 @@
|
||||
import 'package:apidash/codegen/js/fetch.dart';
|
||||
import '../request_models.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
final fetchCodeGen = FetchCodeGen(isNodeJs: true);
|
||||
|
||||
group('GET Request', () {
|
||||
test('GET 1', () {
|
||||
const expectedCode = r"""import fetch from 'node-fetch';
|
||||
|
||||
let url = 'https://api.foss42.com';
|
||||
|
||||
let options = {
|
||||
method: 'GET'
|
||||
};
|
||||
|
||||
let status;
|
||||
fetch(url, options)
|
||||
.then(res => {
|
||||
status = res.status;
|
||||
return res.json()
|
||||
})
|
||||
.then(body => {
|
||||
console.log(status);
|
||||
console.log(body);
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(status);
|
||||
console.error('error:' + err);
|
||||
});
|
||||
""";
|
||||
expect(fetchCodeGen.getCode(requestModelGet1, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('GET 2', () {
|
||||
const expectedCode = r"""import fetch from 'node-fetch';
|
||||
|
||||
let url = 'https://api.foss42.com/country/data?code=US';
|
||||
|
||||
let options = {
|
||||
method: 'GET'
|
||||
};
|
||||
|
||||
let status;
|
||||
fetch(url, options)
|
||||
.then(res => {
|
||||
status = res.status;
|
||||
return res.json()
|
||||
})
|
||||
.then(body => {
|
||||
console.log(status);
|
||||
console.log(body);
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(status);
|
||||
console.error('error:' + err);
|
||||
});
|
||||
""";
|
||||
expect(fetchCodeGen.getCode(requestModelGet2, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('GET 3', () {
|
||||
const expectedCode = r"""import fetch from 'node-fetch';
|
||||
|
||||
let url = 'https://api.foss42.com/country/data?code=IND';
|
||||
|
||||
let options = {
|
||||
method: 'GET'
|
||||
};
|
||||
|
||||
let status;
|
||||
fetch(url, options)
|
||||
.then(res => {
|
||||
status = res.status;
|
||||
return res.json()
|
||||
})
|
||||
.then(body => {
|
||||
console.log(status);
|
||||
console.log(body);
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(status);
|
||||
console.error('error:' + err);
|
||||
});
|
||||
""";
|
||||
expect(fetchCodeGen.getCode(requestModelGet3, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('GET 4', () {
|
||||
const expectedCode = r"""import fetch from 'node-fetch';
|
||||
|
||||
let url = 'https://api.foss42.com/humanize/social?num=8700000&digits=3&system=SS&add_space=true&trailing_zeros=true';
|
||||
|
||||
let options = {
|
||||
method: 'GET'
|
||||
};
|
||||
|
||||
let status;
|
||||
fetch(url, options)
|
||||
.then(res => {
|
||||
status = res.status;
|
||||
return res.json()
|
||||
})
|
||||
.then(body => {
|
||||
console.log(status);
|
||||
console.log(body);
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(status);
|
||||
console.error('error:' + err);
|
||||
});
|
||||
""";
|
||||
expect(fetchCodeGen.getCode(requestModelGet4, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('GET 5', () {
|
||||
const expectedCode = r"""import fetch from 'node-fetch';
|
||||
|
||||
let url = 'https://api.github.com/repos/foss42/apidash';
|
||||
|
||||
let options = {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
"User-Agent": "Test Agent"
|
||||
}
|
||||
};
|
||||
|
||||
let status;
|
||||
fetch(url, options)
|
||||
.then(res => {
|
||||
status = res.status;
|
||||
return res.json()
|
||||
})
|
||||
.then(body => {
|
||||
console.log(status);
|
||||
console.log(body);
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(status);
|
||||
console.error('error:' + err);
|
||||
});
|
||||
""";
|
||||
expect(fetchCodeGen.getCode(requestModelGet5, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('GET 6', () {
|
||||
const expectedCode = r"""import fetch from 'node-fetch';
|
||||
|
||||
let url = 'https://api.github.com/repos/foss42/apidash?raw=true';
|
||||
|
||||
let options = {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
"User-Agent": "Test Agent"
|
||||
}
|
||||
};
|
||||
|
||||
let status;
|
||||
fetch(url, options)
|
||||
.then(res => {
|
||||
status = res.status;
|
||||
return res.json()
|
||||
})
|
||||
.then(body => {
|
||||
console.log(status);
|
||||
console.log(body);
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(status);
|
||||
console.error('error:' + err);
|
||||
});
|
||||
""";
|
||||
expect(fetchCodeGen.getCode(requestModelGet6, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('GET 7', () {
|
||||
const expectedCode = r"""import fetch from 'node-fetch';
|
||||
|
||||
let url = 'https://api.foss42.com';
|
||||
|
||||
let options = {
|
||||
method: 'GET'
|
||||
};
|
||||
|
||||
let status;
|
||||
fetch(url, options)
|
||||
.then(res => {
|
||||
status = res.status;
|
||||
return res.json()
|
||||
})
|
||||
.then(body => {
|
||||
console.log(status);
|
||||
console.log(body);
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(status);
|
||||
console.error('error:' + err);
|
||||
});
|
||||
""";
|
||||
expect(fetchCodeGen.getCode(requestModelGet7, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('GET 8', () {
|
||||
const expectedCode = r"""import fetch from 'node-fetch';
|
||||
|
||||
let url = 'https://api.github.com/repos/foss42/apidash?raw=true';
|
||||
|
||||
let options = {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
"User-Agent": "Test Agent"
|
||||
}
|
||||
};
|
||||
|
||||
let status;
|
||||
fetch(url, options)
|
||||
.then(res => {
|
||||
status = res.status;
|
||||
return res.json()
|
||||
})
|
||||
.then(body => {
|
||||
console.log(status);
|
||||
console.log(body);
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(status);
|
||||
console.error('error:' + err);
|
||||
});
|
||||
""";
|
||||
expect(fetchCodeGen.getCode(requestModelGet8, "https"), expectedCode);
|
||||
});
|
||||
});
|
||||
|
||||
group('HEAD Request', () {
|
||||
test('HEAD 1', () {
|
||||
const expectedCode = r"""import fetch from 'node-fetch';
|
||||
|
||||
let url = 'https://api.foss42.com';
|
||||
|
||||
let options = {
|
||||
method: 'HEAD'
|
||||
};
|
||||
|
||||
let status;
|
||||
fetch(url, options)
|
||||
.then(res => {
|
||||
status = res.status;
|
||||
return res.json()
|
||||
})
|
||||
.then(body => {
|
||||
console.log(status);
|
||||
console.log(body);
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(status);
|
||||
console.error('error:' + err);
|
||||
});
|
||||
""";
|
||||
expect(fetchCodeGen.getCode(requestModelHead1, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('HEAD 2', () {
|
||||
const expectedCode = r"""import fetch from 'node-fetch';
|
||||
|
||||
let url = 'http://api.foss42.com';
|
||||
|
||||
let options = {
|
||||
method: 'HEAD'
|
||||
};
|
||||
|
||||
let status;
|
||||
fetch(url, options)
|
||||
.then(res => {
|
||||
status = res.status;
|
||||
return res.json()
|
||||
})
|
||||
.then(body => {
|
||||
console.log(status);
|
||||
console.log(body);
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(status);
|
||||
console.error('error:' + err);
|
||||
});
|
||||
""";
|
||||
expect(fetchCodeGen.getCode(requestModelHead2, "http"), expectedCode);
|
||||
});
|
||||
});
|
||||
|
||||
group('POST Request', () {
|
||||
test('POST 1', () {
|
||||
const expectedCode = r"""import fetch from 'node-fetch';
|
||||
|
||||
let url = 'https://api.foss42.com/case/lower';
|
||||
|
||||
let options = {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
"Content-Type": "text/plain"
|
||||
},
|
||||
body:
|
||||
"{\n\"text\": \"I LOVE Flutter\"\n}"
|
||||
};
|
||||
|
||||
let status;
|
||||
fetch(url, options)
|
||||
.then(res => {
|
||||
status = res.status;
|
||||
return res.json()
|
||||
})
|
||||
.then(body => {
|
||||
console.log(status);
|
||||
console.log(body);
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(status);
|
||||
console.error('error:' + err);
|
||||
});
|
||||
""";
|
||||
expect(fetchCodeGen.getCode(requestModelPost1, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('POST 2', () {
|
||||
const expectedCode = r"""import fetch from 'node-fetch';
|
||||
|
||||
let url = 'https://api.foss42.com/case/lower';
|
||||
|
||||
let options = {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
body:
|
||||
"{\n\"text\": \"I LOVE Flutter\"\n}"
|
||||
};
|
||||
|
||||
let status;
|
||||
fetch(url, options)
|
||||
.then(res => {
|
||||
status = res.status;
|
||||
return res.json()
|
||||
})
|
||||
.then(body => {
|
||||
console.log(status);
|
||||
console.log(body);
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(status);
|
||||
console.error('error:' + err);
|
||||
});
|
||||
""";
|
||||
expect(fetchCodeGen.getCode(requestModelPost2, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('POST 3', () {
|
||||
const expectedCode = r"""import fetch from 'node-fetch';
|
||||
|
||||
let url = 'https://api.foss42.com/case/lower';
|
||||
|
||||
let options = {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"User-Agent": "Test Agent"
|
||||
},
|
||||
body:
|
||||
"{\n\"text\": \"I LOVE Flutter\"\n}"
|
||||
};
|
||||
|
||||
let status;
|
||||
fetch(url, options)
|
||||
.then(res => {
|
||||
status = res.status;
|
||||
return res.json()
|
||||
})
|
||||
.then(body => {
|
||||
console.log(status);
|
||||
console.log(body);
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(status);
|
||||
console.error('error:' + err);
|
||||
});
|
||||
""";
|
||||
expect(fetchCodeGen.getCode(requestModelPost3, "https"), expectedCode);
|
||||
});
|
||||
});
|
||||
group('PUT Request', () {
|
||||
test('PUT 1', () {
|
||||
const expectedCode = r"""import fetch from 'node-fetch';
|
||||
|
||||
let url = 'https://reqres.in/api/users/2';
|
||||
|
||||
let options = {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
body:
|
||||
"{\n\"name\": \"morpheus\",\n\"job\": \"zion resident\"\n}"
|
||||
};
|
||||
|
||||
let status;
|
||||
fetch(url, options)
|
||||
.then(res => {
|
||||
status = res.status;
|
||||
return res.json()
|
||||
})
|
||||
.then(body => {
|
||||
console.log(status);
|
||||
console.log(body);
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(status);
|
||||
console.error('error:' + err);
|
||||
});
|
||||
""";
|
||||
expect(fetchCodeGen.getCode(requestModelPut1, "https"), expectedCode);
|
||||
});
|
||||
});
|
||||
|
||||
group('PATCH Request', () {
|
||||
test('PATCH 1', () {
|
||||
const expectedCode = r"""import fetch from 'node-fetch';
|
||||
|
||||
let url = 'https://reqres.in/api/users/2';
|
||||
|
||||
let options = {
|
||||
method: 'PATCH',
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
body:
|
||||
"{\n\"name\": \"marfeus\",\n\"job\": \"accountant\"\n}"
|
||||
};
|
||||
|
||||
let status;
|
||||
fetch(url, options)
|
||||
.then(res => {
|
||||
status = res.status;
|
||||
return res.json()
|
||||
})
|
||||
.then(body => {
|
||||
console.log(status);
|
||||
console.log(body);
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(status);
|
||||
console.error('error:' + err);
|
||||
});
|
||||
""";
|
||||
expect(fetchCodeGen.getCode(requestModelPatch1, "https"), expectedCode);
|
||||
});
|
||||
});
|
||||
|
||||
group('DELETE Request', () {
|
||||
test('DELETE 1', () {
|
||||
const expectedCode = r"""import fetch from 'node-fetch';
|
||||
|
||||
let url = 'https://reqres.in/api/users/2';
|
||||
|
||||
let options = {
|
||||
method: 'DELETE'
|
||||
};
|
||||
|
||||
let status;
|
||||
fetch(url, options)
|
||||
.then(res => {
|
||||
status = res.status;
|
||||
return res.json()
|
||||
})
|
||||
.then(body => {
|
||||
console.log(status);
|
||||
console.log(body);
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(status);
|
||||
console.error('error:' + err);
|
||||
});
|
||||
""";
|
||||
expect(fetchCodeGen.getCode(requestModelDelete1, "https"), expectedCode);
|
||||
});
|
||||
|
||||
test('DELETE 2', () {
|
||||
const expectedCode = r"""import fetch from 'node-fetch';
|
||||
|
||||
let url = 'https://reqres.in/api/users/2';
|
||||
|
||||
let options = {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
body:
|
||||
"{\n\"name\": \"marfeus\",\n\"job\": \"accountant\"\n}"
|
||||
};
|
||||
|
||||
let status;
|
||||
fetch(url, options)
|
||||
.then(res => {
|
||||
status = res.status;
|
||||
return res.json()
|
||||
})
|
||||
.then(body => {
|
||||
console.log(status);
|
||||
console.log(body);
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(status);
|
||||
console.error('error:' + err);
|
||||
});
|
||||
""";
|
||||
expect(fetchCodeGen.getCode(requestModelDelete2, "https"), expectedCode);
|
||||
});
|
||||
});
|
||||
}
|
386
test/codegen/python_http_client_codegen_test.dart
Normal file
386
test/codegen/python_http_client_codegen_test.dart
Normal file
@ -0,0 +1,386 @@
|
||||
import 'package:apidash/codegen/python/http_client.dart';
|
||||
import '../request_models.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
final pythonHttpClientCodeGen = PythonHttpClientCodeGen();
|
||||
|
||||
group('GET Request', () {
|
||||
test('GET 1', () {
|
||||
const expectedCode = r"""import http.client
|
||||
|
||||
conn = http.client.HTTPSConnection("api.foss42.com")
|
||||
conn.request("GET", "")
|
||||
|
||||
res = conn.getresponse()
|
||||
data = res.read()
|
||||
|
||||
print(data.decode("utf-8"))
|
||||
""";
|
||||
expect(pythonHttpClientCodeGen.getCode(requestModelGet1, "https"),
|
||||
expectedCode);
|
||||
});
|
||||
|
||||
test('GET 2', () {
|
||||
const expectedCode = r"""import http.client
|
||||
from urllib.parse import urlencode
|
||||
|
||||
queryParams = {
|
||||
"code": "US"
|
||||
}
|
||||
queryParamsStr = '?' + urlencode(queryParams)
|
||||
|
||||
conn = http.client.HTTPSConnection("api.foss42.com")
|
||||
conn.request("GET", "/country/data" + queryParamsStr)
|
||||
|
||||
res = conn.getresponse()
|
||||
data = res.read()
|
||||
|
||||
print(data.decode("utf-8"))
|
||||
""";
|
||||
expect(pythonHttpClientCodeGen.getCode(requestModelGet2, "https"),
|
||||
expectedCode);
|
||||
});
|
||||
|
||||
test('GET 3', () {
|
||||
const expectedCode = r"""import http.client
|
||||
from urllib.parse import urlencode
|
||||
|
||||
queryParams = {
|
||||
"code": "IND"
|
||||
}
|
||||
queryParamsStr = '?' + urlencode(queryParams)
|
||||
|
||||
conn = http.client.HTTPSConnection("api.foss42.com")
|
||||
conn.request("GET", "/country/data" + queryParamsStr)
|
||||
|
||||
res = conn.getresponse()
|
||||
data = res.read()
|
||||
|
||||
print(data.decode("utf-8"))
|
||||
""";
|
||||
expect(pythonHttpClientCodeGen.getCode(requestModelGet3, "https"),
|
||||
expectedCode);
|
||||
});
|
||||
|
||||
test('GET 4', () {
|
||||
const expectedCode = r"""import http.client
|
||||
from urllib.parse import urlencode
|
||||
|
||||
queryParams = {
|
||||
"num": "8700000",
|
||||
"digits": "3",
|
||||
"system": "SS",
|
||||
"add_space": "true",
|
||||
"trailing_zeros": "true"
|
||||
}
|
||||
queryParamsStr = '?' + urlencode(queryParams)
|
||||
|
||||
conn = http.client.HTTPSConnection("api.foss42.com")
|
||||
conn.request("GET", "/humanize/social" + queryParamsStr)
|
||||
|
||||
res = conn.getresponse()
|
||||
data = res.read()
|
||||
|
||||
print(data.decode("utf-8"))
|
||||
""";
|
||||
expect(pythonHttpClientCodeGen.getCode(requestModelGet4, "https"),
|
||||
expectedCode);
|
||||
});
|
||||
|
||||
test('GET 5', () {
|
||||
const expectedCode = r"""import http.client
|
||||
|
||||
headers = {
|
||||
"User-Agent": "Test Agent"
|
||||
}
|
||||
|
||||
conn = http.client.HTTPSConnection("api.github.com")
|
||||
conn.request("GET", "/repos/foss42/apidash",
|
||||
headers= headers)
|
||||
|
||||
res = conn.getresponse()
|
||||
data = res.read()
|
||||
|
||||
print(data.decode("utf-8"))
|
||||
""";
|
||||
expect(pythonHttpClientCodeGen.getCode(requestModelGet5, "https"),
|
||||
expectedCode);
|
||||
});
|
||||
|
||||
test('GET 6', () {
|
||||
const expectedCode = r"""import http.client
|
||||
from urllib.parse import urlencode
|
||||
|
||||
queryParams = {
|
||||
"raw": "true"
|
||||
}
|
||||
queryParamsStr = '?' + urlencode(queryParams)
|
||||
|
||||
headers = {
|
||||
"User-Agent": "Test Agent"
|
||||
}
|
||||
|
||||
conn = http.client.HTTPSConnection("api.github.com")
|
||||
conn.request("GET", "/repos/foss42/apidash" + queryParamsStr,
|
||||
headers= headers)
|
||||
|
||||
res = conn.getresponse()
|
||||
data = res.read()
|
||||
|
||||
print(data.decode("utf-8"))
|
||||
""";
|
||||
expect(pythonHttpClientCodeGen.getCode(requestModelGet6, "https"),
|
||||
expectedCode);
|
||||
});
|
||||
|
||||
test('GET 7', () {
|
||||
const expectedCode = r"""import http.client
|
||||
|
||||
conn = http.client.HTTPSConnection("api.foss42.com")
|
||||
conn.request("GET", "")
|
||||
|
||||
res = conn.getresponse()
|
||||
data = res.read()
|
||||
|
||||
print(data.decode("utf-8"))
|
||||
""";
|
||||
expect(pythonHttpClientCodeGen.getCode(requestModelGet7, "https"),
|
||||
expectedCode);
|
||||
});
|
||||
|
||||
test('GET 8', () {
|
||||
const expectedCode = r"""import http.client
|
||||
from urllib.parse import urlencode
|
||||
|
||||
queryParams = {
|
||||
"raw": "true"
|
||||
}
|
||||
queryParamsStr = '?' + urlencode(queryParams)
|
||||
|
||||
headers = {
|
||||
"User-Agent": "Test Agent"
|
||||
}
|
||||
|
||||
conn = http.client.HTTPSConnection("api.github.com")
|
||||
conn.request("GET", "/repos/foss42/apidash" + queryParamsStr,
|
||||
headers= headers)
|
||||
|
||||
res = conn.getresponse()
|
||||
data = res.read()
|
||||
|
||||
print(data.decode("utf-8"))
|
||||
""";
|
||||
expect(pythonHttpClientCodeGen.getCode(requestModelGet8, "https"),
|
||||
expectedCode);
|
||||
});
|
||||
});
|
||||
|
||||
group('HEAD Request', () {
|
||||
test('HEAD 1', () {
|
||||
const expectedCode = r"""import http.client
|
||||
|
||||
conn = http.client.HTTPSConnection("api.foss42.com")
|
||||
conn.request("HEAD", "")
|
||||
|
||||
res = conn.getresponse()
|
||||
data = res.read()
|
||||
|
||||
print(data.decode("utf-8"))
|
||||
""";
|
||||
expect(pythonHttpClientCodeGen.getCode(requestModelHead1, "https"),
|
||||
expectedCode);
|
||||
});
|
||||
|
||||
test('HEAD 2', () {
|
||||
const expectedCode = r"""import http.client
|
||||
|
||||
conn = http.client.HTTPConnection("api.foss42.com")
|
||||
conn.request("HEAD", "")
|
||||
|
||||
res = conn.getresponse()
|
||||
data = res.read()
|
||||
|
||||
print(data.decode("utf-8"))
|
||||
""";
|
||||
expect(pythonHttpClientCodeGen.getCode(requestModelHead2, "http"),
|
||||
expectedCode);
|
||||
});
|
||||
});
|
||||
|
||||
group('POST Request', () {
|
||||
test('POST 1', () {
|
||||
const expectedCode = r"""import http.client
|
||||
|
||||
body = r'''{
|
||||
"text": "I LOVE Flutter"
|
||||
}'''
|
||||
|
||||
headers = {
|
||||
"content-type": "text/plain"
|
||||
}
|
||||
|
||||
conn = http.client.HTTPSConnection("api.foss42.com")
|
||||
conn.request("POST", "/case/lower",
|
||||
body= body,
|
||||
headers= headers)
|
||||
|
||||
res = conn.getresponse()
|
||||
data = res.read()
|
||||
|
||||
print(data.decode("utf-8"))
|
||||
""";
|
||||
expect(pythonHttpClientCodeGen.getCode(requestModelPost1, "https"),
|
||||
expectedCode);
|
||||
});
|
||||
|
||||
test('POST 2', () {
|
||||
const expectedCode = r"""import http.client
|
||||
|
||||
body = r'''{
|
||||
"text": "I LOVE Flutter"
|
||||
}'''
|
||||
|
||||
headers = {
|
||||
"content-type": "application/json"
|
||||
}
|
||||
|
||||
conn = http.client.HTTPSConnection("api.foss42.com")
|
||||
conn.request("POST", "/case/lower",
|
||||
body= body,
|
||||
headers= headers)
|
||||
|
||||
res = conn.getresponse()
|
||||
data = res.read()
|
||||
|
||||
print(data.decode("utf-8"))
|
||||
""";
|
||||
expect(pythonHttpClientCodeGen.getCode(requestModelPost2, "https"),
|
||||
expectedCode);
|
||||
});
|
||||
|
||||
test('POST 3', () {
|
||||
const expectedCode = r"""import http.client
|
||||
|
||||
body = r'''{
|
||||
"text": "I LOVE Flutter"
|
||||
}'''
|
||||
|
||||
headers = {
|
||||
"User-Agent": "Test Agent",
|
||||
"content-type": "application/json"
|
||||
}
|
||||
|
||||
conn = http.client.HTTPSConnection("api.foss42.com")
|
||||
conn.request("POST", "/case/lower",
|
||||
body= body,
|
||||
headers= headers)
|
||||
|
||||
res = conn.getresponse()
|
||||
data = res.read()
|
||||
|
||||
print(data.decode("utf-8"))
|
||||
""";
|
||||
expect(pythonHttpClientCodeGen.getCode(requestModelPost3, "https"),
|
||||
expectedCode);
|
||||
});
|
||||
});
|
||||
group('PUT Request', () {
|
||||
test('PUT 1', () {
|
||||
const expectedCode = r"""import http.client
|
||||
|
||||
body = r'''{
|
||||
"name": "morpheus",
|
||||
"job": "zion resident"
|
||||
}'''
|
||||
|
||||
headers = {
|
||||
"content-type": "application/json"
|
||||
}
|
||||
|
||||
conn = http.client.HTTPSConnection("reqres.in")
|
||||
conn.request("PUT", "/api/users/2",
|
||||
body= body,
|
||||
headers= headers)
|
||||
|
||||
res = conn.getresponse()
|
||||
data = res.read()
|
||||
|
||||
print(data.decode("utf-8"))
|
||||
""";
|
||||
expect(pythonHttpClientCodeGen.getCode(requestModelPut1, "https"),
|
||||
expectedCode);
|
||||
});
|
||||
});
|
||||
|
||||
group('PATCH Request', () {
|
||||
test('PATCH 1', () {
|
||||
const expectedCode = r"""import http.client
|
||||
|
||||
body = r'''{
|
||||
"name": "marfeus",
|
||||
"job": "accountant"
|
||||
}'''
|
||||
|
||||
headers = {
|
||||
"content-type": "application/json"
|
||||
}
|
||||
|
||||
conn = http.client.HTTPSConnection("reqres.in")
|
||||
conn.request("PATCH", "/api/users/2",
|
||||
body= body,
|
||||
headers= headers)
|
||||
|
||||
res = conn.getresponse()
|
||||
data = res.read()
|
||||
|
||||
print(data.decode("utf-8"))
|
||||
""";
|
||||
expect(pythonHttpClientCodeGen.getCode(requestModelPatch1, "https"),
|
||||
expectedCode);
|
||||
});
|
||||
});
|
||||
|
||||
group('DELETE Request', () {
|
||||
test('DELETE 1', () {
|
||||
const expectedCode = r"""import http.client
|
||||
|
||||
conn = http.client.HTTPSConnection("reqres.in")
|
||||
conn.request("DELETE", "/api/users/2")
|
||||
|
||||
res = conn.getresponse()
|
||||
data = res.read()
|
||||
|
||||
print(data.decode("utf-8"))
|
||||
""";
|
||||
expect(pythonHttpClientCodeGen.getCode(requestModelDelete1, "https"),
|
||||
expectedCode);
|
||||
});
|
||||
|
||||
test('DELETE 2', () {
|
||||
const expectedCode = r"""import http.client
|
||||
|
||||
body = r'''{
|
||||
"name": "marfeus",
|
||||
"job": "accountant"
|
||||
}'''
|
||||
|
||||
headers = {
|
||||
"content-type": "application/json"
|
||||
}
|
||||
|
||||
conn = http.client.HTTPSConnection("reqres.in")
|
||||
conn.request("DELETE", "/api/users/2",
|
||||
body= body,
|
||||
headers= headers)
|
||||
|
||||
res = conn.getresponse()
|
||||
data = res.read()
|
||||
|
||||
print(data.decode("utf-8"))
|
||||
""";
|
||||
expect(pythonHttpClientCodeGen.getCode(requestModelDelete2, "https"),
|
||||
expectedCode);
|
||||
});
|
||||
});
|
||||
}
|
327
test/codegen/python_requests_codegen_test.dart
Normal file
327
test/codegen/python_requests_codegen_test.dart
Normal file
@ -0,0 +1,327 @@
|
||||
import 'package:apidash/codegen/python/requests.dart';
|
||||
import '../request_models.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
final pythonRequestsCodeGen = PythonRequestsCodeGen();
|
||||
|
||||
group('GET Request', () {
|
||||
test('GET 1', () {
|
||||
const expectedCode = r"""import requests
|
||||
|
||||
url = 'https://api.foss42.com'
|
||||
|
||||
response = requests.get(url)
|
||||
|
||||
print('Status Code:', response.status_code)
|
||||
print('Response Body:', response.text)
|
||||
""";
|
||||
expect(pythonRequestsCodeGen.getCode(requestModelGet1, "https"),
|
||||
expectedCode);
|
||||
});
|
||||
|
||||
test('GET 2', () {
|
||||
const expectedCode = r"""import requests
|
||||
|
||||
url = 'https://api.foss42.com/country/data'
|
||||
|
||||
params = {
|
||||
"code": "US"
|
||||
}
|
||||
|
||||
response = requests.get(url, params=params)
|
||||
|
||||
print('Status Code:', response.status_code)
|
||||
print('Response Body:', response.text)
|
||||
""";
|
||||
expect(pythonRequestsCodeGen.getCode(requestModelGet2, "https"),
|
||||
expectedCode);
|
||||
});
|
||||
|
||||
test('GET 3', () {
|
||||
const expectedCode = r"""import requests
|
||||
|
||||
url = 'https://api.foss42.com/country/data'
|
||||
|
||||
params = {
|
||||
"code": "IND"
|
||||
}
|
||||
|
||||
response = requests.get(url, params=params)
|
||||
|
||||
print('Status Code:', response.status_code)
|
||||
print('Response Body:', response.text)
|
||||
""";
|
||||
expect(pythonRequestsCodeGen.getCode(requestModelGet3, "https"),
|
||||
expectedCode);
|
||||
});
|
||||
|
||||
test('GET 4', () {
|
||||
const expectedCode = r"""import requests
|
||||
|
||||
url = 'https://api.foss42.com/humanize/social'
|
||||
|
||||
params = {
|
||||
"num": "8700000",
|
||||
"digits": "3",
|
||||
"system": "SS",
|
||||
"add_space": "true",
|
||||
"trailing_zeros": "true"
|
||||
}
|
||||
|
||||
response = requests.get(url, params=params)
|
||||
|
||||
print('Status Code:', response.status_code)
|
||||
print('Response Body:', response.text)
|
||||
""";
|
||||
expect(pythonRequestsCodeGen.getCode(requestModelGet4, "https"),
|
||||
expectedCode);
|
||||
});
|
||||
|
||||
test('GET 5', () {
|
||||
const expectedCode = r"""import requests
|
||||
|
||||
url = 'https://api.github.com/repos/foss42/apidash'
|
||||
|
||||
headers = {
|
||||
"User-Agent": "Test Agent"
|
||||
}
|
||||
|
||||
response = requests.get(url, headers=headers)
|
||||
|
||||
print('Status Code:', response.status_code)
|
||||
print('Response Body:', response.text)
|
||||
""";
|
||||
expect(pythonRequestsCodeGen.getCode(requestModelGet5, "https"),
|
||||
expectedCode);
|
||||
});
|
||||
|
||||
test('GET 6', () {
|
||||
const expectedCode = r"""import requests
|
||||
|
||||
url = 'https://api.github.com/repos/foss42/apidash'
|
||||
|
||||
params = {
|
||||
"raw": "true"
|
||||
}
|
||||
|
||||
headers = {
|
||||
"User-Agent": "Test Agent"
|
||||
}
|
||||
|
||||
response = requests.get(url, params=params, headers=headers)
|
||||
|
||||
print('Status Code:', response.status_code)
|
||||
print('Response Body:', response.text)
|
||||
""";
|
||||
expect(pythonRequestsCodeGen.getCode(requestModelGet6, "https"),
|
||||
expectedCode);
|
||||
});
|
||||
|
||||
test('GET 7', () {
|
||||
const expectedCode = r"""import requests
|
||||
|
||||
url = 'https://api.foss42.com'
|
||||
|
||||
response = requests.get(url)
|
||||
|
||||
print('Status Code:', response.status_code)
|
||||
print('Response Body:', response.text)
|
||||
""";
|
||||
expect(pythonRequestsCodeGen.getCode(requestModelGet7, "https"),
|
||||
expectedCode);
|
||||
});
|
||||
|
||||
test('GET 8', () {
|
||||
const expectedCode = r"""import requests
|
||||
|
||||
url = 'https://api.github.com/repos/foss42/apidash'
|
||||
|
||||
params = {
|
||||
"raw": "true"
|
||||
}
|
||||
|
||||
headers = {
|
||||
"User-Agent": "Test Agent"
|
||||
}
|
||||
|
||||
response = requests.get(url, params=params, headers=headers)
|
||||
|
||||
print('Status Code:', response.status_code)
|
||||
print('Response Body:', response.text)
|
||||
""";
|
||||
expect(pythonRequestsCodeGen.getCode(requestModelGet8, "https"),
|
||||
expectedCode);
|
||||
});
|
||||
});
|
||||
|
||||
group('HEAD Request', () {
|
||||
test('HEAD 1', () {
|
||||
const expectedCode = r"""import requests
|
||||
|
||||
url = 'https://api.foss42.com'
|
||||
|
||||
response = requests.head(url)
|
||||
|
||||
print('Status Code:', response.status_code)
|
||||
print('Response Body:', response.text)
|
||||
""";
|
||||
expect(pythonRequestsCodeGen.getCode(requestModelHead1, "https"),
|
||||
expectedCode);
|
||||
});
|
||||
|
||||
test('HEAD 2', () {
|
||||
const expectedCode = r"""import requests
|
||||
|
||||
url = 'http://api.foss42.com'
|
||||
|
||||
response = requests.head(url)
|
||||
|
||||
print('Status Code:', response.status_code)
|
||||
print('Response Body:', response.text)
|
||||
""";
|
||||
expect(pythonRequestsCodeGen.getCode(requestModelHead2, "http"),
|
||||
expectedCode);
|
||||
});
|
||||
});
|
||||
|
||||
group('POST Request', () {
|
||||
test('POST 1', () {
|
||||
const expectedCode = r"""import requests
|
||||
|
||||
url = 'https://api.foss42.com/case/lower'
|
||||
|
||||
payload = r'''{
|
||||
"text": "I LOVE Flutter"
|
||||
}'''
|
||||
|
||||
headers = {
|
||||
"content-type": "text/plain"
|
||||
}
|
||||
|
||||
response = requests.post(url, data=payload, headers=headers)
|
||||
|
||||
print('Status Code:', response.status_code)
|
||||
print('Response Body:', response.text)
|
||||
""";
|
||||
expect(pythonRequestsCodeGen.getCode(requestModelPost1, "https"),
|
||||
expectedCode);
|
||||
});
|
||||
|
||||
test('POST 2', () {
|
||||
const expectedCode = r"""import requests
|
||||
|
||||
url = 'https://api.foss42.com/case/lower'
|
||||
|
||||
payload = {
|
||||
"text": "I LOVE Flutter"
|
||||
}
|
||||
|
||||
response = requests.post(url, json=payload)
|
||||
|
||||
print('Status Code:', response.status_code)
|
||||
print('Response Body:', response.text)
|
||||
""";
|
||||
expect(pythonRequestsCodeGen.getCode(requestModelPost2, "https"),
|
||||
expectedCode);
|
||||
});
|
||||
|
||||
test('POST 3', () {
|
||||
const expectedCode = r"""import requests
|
||||
|
||||
url = 'https://api.foss42.com/case/lower'
|
||||
|
||||
payload = {
|
||||
"text": "I LOVE Flutter"
|
||||
}
|
||||
|
||||
headers = {
|
||||
"User-Agent": "Test Agent"
|
||||
}
|
||||
|
||||
response = requests.post(url, json=payload, headers=headers)
|
||||
|
||||
print('Status Code:', response.status_code)
|
||||
print('Response Body:', response.text)
|
||||
""";
|
||||
expect(pythonRequestsCodeGen.getCode(requestModelPost3, "https"),
|
||||
expectedCode);
|
||||
});
|
||||
});
|
||||
group('PUT Request', () {
|
||||
test('PUT 1', () {
|
||||
const expectedCode = r"""import requests
|
||||
|
||||
url = 'https://reqres.in/api/users/2'
|
||||
|
||||
payload = {
|
||||
"name": "morpheus",
|
||||
"job": "zion resident"
|
||||
}
|
||||
|
||||
response = requests.put(url, json=payload)
|
||||
|
||||
print('Status Code:', response.status_code)
|
||||
print('Response Body:', response.text)
|
||||
""";
|
||||
expect(pythonRequestsCodeGen.getCode(requestModelPut1, "https"),
|
||||
expectedCode);
|
||||
});
|
||||
});
|
||||
|
||||
group('PATCH Request', () {
|
||||
test('PATCH 1', () {
|
||||
const expectedCode = r"""import requests
|
||||
|
||||
url = 'https://reqres.in/api/users/2'
|
||||
|
||||
payload = {
|
||||
"name": "marfeus",
|
||||
"job": "accountant"
|
||||
}
|
||||
|
||||
response = requests.patch(url, json=payload)
|
||||
|
||||
print('Status Code:', response.status_code)
|
||||
print('Response Body:', response.text)
|
||||
""";
|
||||
expect(pythonRequestsCodeGen.getCode(requestModelPatch1, "https"),
|
||||
expectedCode);
|
||||
});
|
||||
});
|
||||
|
||||
group('DELETE Request', () {
|
||||
test('DELETE 1', () {
|
||||
const expectedCode = r"""import requests
|
||||
|
||||
url = 'https://reqres.in/api/users/2'
|
||||
|
||||
response = requests.delete(url)
|
||||
|
||||
print('Status Code:', response.status_code)
|
||||
print('Response Body:', response.text)
|
||||
""";
|
||||
expect(pythonRequestsCodeGen.getCode(requestModelDelete1, "https"),
|
||||
expectedCode);
|
||||
});
|
||||
|
||||
test('DELETE 2', () {
|
||||
const expectedCode = r"""import requests
|
||||
|
||||
url = 'https://reqres.in/api/users/2'
|
||||
|
||||
payload = {
|
||||
"name": "marfeus",
|
||||
"job": "accountant"
|
||||
}
|
||||
|
||||
response = requests.delete(url, json=payload)
|
||||
|
||||
print('Status Code:', response.status_code)
|
||||
print('Response Body:', response.text)
|
||||
""";
|
||||
expect(pythonRequestsCodeGen.getCode(requestModelDelete2, "https"),
|
||||
expectedCode);
|
||||
});
|
||||
});
|
||||
}
|
@ -1,48 +0,0 @@
|
||||
import 'package:apidash/models/models.dart' show KVRow, RequestModel;
|
||||
import 'package:apidash/consts.dart';
|
||||
|
||||
/// Basic GET request model
|
||||
const requestModelGet1 = RequestModel(
|
||||
url: 'https://api.foss42.com',
|
||||
method: HTTPVerb.get,
|
||||
id: '',
|
||||
);
|
||||
|
||||
/// GET request model with headers and query params
|
||||
const requestModelGet2 = RequestModel(
|
||||
url: 'https://jsonplaceholder.typicode.com/posts',
|
||||
method: HTTPVerb.get,
|
||||
requestParams: [
|
||||
KVRow('userId', 1),
|
||||
],
|
||||
requestHeaders: [
|
||||
KVRow('Custom-Header-1', 'Value-1'),
|
||||
KVRow('Custom-Header-2', 'Value-2')
|
||||
],
|
||||
id: '1',
|
||||
);
|
||||
|
||||
/// Basic HEAD request model
|
||||
const requestModelHead1 = RequestModel(
|
||||
url: 'https://jsonplaceholder.typicode.com/posts/1',
|
||||
method: HTTPVerb.head,
|
||||
id: '1',
|
||||
);
|
||||
|
||||
/// Basic POST request model
|
||||
const requestModelPost1 = RequestModel(
|
||||
url: 'https://api.foss42.com/case/lower',
|
||||
method: HTTPVerb.post,
|
||||
requestBody: '{"text": "IS Upper"}',
|
||||
requestBodyContentType: ContentType.json,
|
||||
id: '1',
|
||||
);
|
||||
|
||||
/// Basic DELETE request model
|
||||
const requestModelDelete1 = RequestModel(
|
||||
url: 'https://jsonplaceholder.typicode.com/posts/1',
|
||||
method: HTTPVerb.delete,
|
||||
requestBody: '{"title": "foo","body": "bar","userId": 1}',
|
||||
requestBodyContentType: ContentType.json,
|
||||
id: '1',
|
||||
);
|
@ -1,20 +0,0 @@
|
||||
import 'package:test/test.dart';
|
||||
import 'package:apidash/models/kvrow_model.dart';
|
||||
|
||||
void main() {
|
||||
KVRow kvRow1 = const KVRow("harry", 23);
|
||||
String kvRow1Expected = "{harry: 23}";
|
||||
|
||||
test('Testing toString()', () {
|
||||
expect(kvRow1.toString(), kvRow1Expected);
|
||||
});
|
||||
|
||||
KVRow kvRow2Expected = const KVRow("winter", "26");
|
||||
test('Testing copyWith()', () {
|
||||
expect(kvRow1.copyWith(k: "winter", v: "26"), kvRow2Expected);
|
||||
});
|
||||
|
||||
test('Testing hashcode', () {
|
||||
expect(kvRow1.hashCode, greaterThan(0));
|
||||
});
|
||||
}
|
25
test/models/name_value_model_test.dart
Normal file
25
test/models/name_value_model_test.dart
Normal file
@ -0,0 +1,25 @@
|
||||
import 'package:test/test.dart';
|
||||
import 'package:apidash/models/name_value_model.dart';
|
||||
|
||||
void main() {
|
||||
const nmRow1 = NameValueModel(name: "harry", value: 23);
|
||||
|
||||
test('Testing toString()', () {
|
||||
const resultExpected = 'NameValueModel(name: harry, value: 23)';
|
||||
expect(nmRow1.toString(), resultExpected);
|
||||
});
|
||||
|
||||
test('Testing toJson()', () {
|
||||
const resultExpected = {"name": "harry", "value": 23};
|
||||
expect(nmRow1.toJson(), resultExpected);
|
||||
});
|
||||
|
||||
test('Testing copyWith()', () {
|
||||
const resultExpected = NameValueModel(name: "winter", value: "26");
|
||||
expect(nmRow1.copyWith(name: "winter", value: "26"), resultExpected);
|
||||
});
|
||||
|
||||
test('Testing hashcode', () {
|
||||
expect(nmRow1.hashCode, greaterThan(0));
|
||||
});
|
||||
}
|
@ -55,8 +55,9 @@ void main() {
|
||||
url: 'api.foss42.com/case/lower',
|
||||
name: 'foss42 api',
|
||||
requestHeaders: const [
|
||||
KVRow('content-length', '18'),
|
||||
KVRow('content-type', 'application/json; charset=utf-8')
|
||||
NameValueModel(name: 'content-length', value: '18'),
|
||||
NameValueModel(
|
||||
name: 'content-type', value: 'application/json; charset=utf-8')
|
||||
],
|
||||
requestBodyContentType: ContentType.json,
|
||||
requestBody: '''{
|
||||
@ -71,8 +72,24 @@ void main() {
|
||||
url: 'api.foss42.com/case/lower',
|
||||
name: 'foss42 api',
|
||||
requestHeaders: [
|
||||
KVRow('content-length', '18'),
|
||||
KVRow('content-type', 'application/json; charset=utf-8')
|
||||
NameValueModel(name: 'content-length', value: '18'),
|
||||
NameValueModel(
|
||||
name: 'content-type', value: 'application/json; charset=utf-8')
|
||||
],
|
||||
requestBodyContentType: ContentType.json,
|
||||
requestBody: '''{
|
||||
"text":"WORLD"
|
||||
}''');
|
||||
|
||||
RequestModel requestModelCopy = const RequestModel(
|
||||
id: '1',
|
||||
method: HTTPVerb.post,
|
||||
url: 'api.foss42.com/case/lower',
|
||||
name: 'foss42 api (copy)',
|
||||
requestHeaders: [
|
||||
NameValueModel(name: 'content-length', value: '18'),
|
||||
NameValueModel(
|
||||
name: 'content-type', value: 'application/json; charset=utf-8')
|
||||
],
|
||||
requestBodyContentType: ContentType.json,
|
||||
requestBody: '''{
|
||||
@ -103,7 +120,7 @@ void main() {
|
||||
expect(requestModelcopyWith.name, 'API foss42');
|
||||
});
|
||||
test('Testing duplicate', () {
|
||||
expect(requestModel.duplicate(id: '1'), requestModelDup);
|
||||
expect(requestModel.duplicate(id: '1'), requestModelCopy);
|
||||
});
|
||||
|
||||
test('Testing toJson', () {
|
||||
@ -122,7 +139,7 @@ void main() {
|
||||
"Request Name: foss42 api",
|
||||
"Request Description: ",
|
||||
"Request Tab Index: 0",
|
||||
"Request Headers: [{content-length: 18}, {content-type: application/json; charset=utf-8}]",
|
||||
"Request Headers: [NameValueModel(name: content-length, value: 18), NameValueModel(name: content-type, value: application/json; charset=utf-8)]",
|
||||
"Request Params: null",
|
||||
"Request Body Content Type: ContentType.json",
|
||||
'Request Body: {\n"text":"WORLD"\n}',
|
||||
|
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: 'User-Agent', value: 'Test Agent'),
|
||||
],
|
||||
);
|
||||
|
||||
/// 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: 'User-Agent', value: 'Test Agent'),
|
||||
],
|
||||
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: 'User-Agent', value: 'Test Agent'),
|
||||
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: 'User-Agent', value: 'Test Agent'),
|
||||
],
|
||||
);
|
||||
|
||||
/// 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: 'delete2',
|
||||
url: 'https://reqres.in/api/users/2',
|
||||
method: HTTPVerb.delete,
|
||||
requestBody: r"""{
|
||||
"name": "marfeus",
|
||||
"job": "accountant"
|
||||
}""",
|
||||
requestBodyContentType: ContentType.json,
|
||||
);
|
@ -1,15 +1,19 @@
|
||||
import 'dart:math';
|
||||
|
||||
const _chars = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz1234567890';
|
||||
Random _rnd = Random();
|
||||
class RandomStringGenerator {
|
||||
static const _chars =
|
||||
'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz1234567890';
|
||||
static Random rnd = Random();
|
||||
|
||||
String getRandomString(int length) => String.fromCharCodes(Iterable.generate(
|
||||
length, (_) => _chars.codeUnitAt(_rnd.nextInt(_chars.length))));
|
||||
static String getRandomString(int length) =>
|
||||
String.fromCharCodes(Iterable.generate(
|
||||
length, (_) => _chars.codeUnitAt(rnd.nextInt(_chars.length))));
|
||||
|
||||
String getRandomStringLines(int lines, int length) {
|
||||
static String getRandomStringLines(int lines, int length) {
|
||||
List<String> result = [];
|
||||
for (var i = 0; i < lines; i++) {
|
||||
result.add(getRandomString(length));
|
||||
}
|
||||
return result.join('\n');
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import 'package:test/test.dart';
|
||||
import 'package:apidash/utils/convert_utils.dart';
|
||||
import 'package:apidash/models/kvrow_model.dart';
|
||||
import 'package:apidash/models/name_value_model.dart';
|
||||
|
||||
void main() {
|
||||
group("Testing humanizeDuration function", () {
|
||||
@ -73,18 +73,18 @@ void main() {
|
||||
expect(rowsToMap(null), null);
|
||||
});
|
||||
test('Testing for string KVRow values', () {
|
||||
KVRow kvRow1 = const KVRow("code", "IN");
|
||||
const kvRow1 = NameValueModel(name: "code", value: "IN");
|
||||
expect(rowsToMap([kvRow1]), {"code": "IN"});
|
||||
});
|
||||
test('Testing when header is True', () {
|
||||
KVRow kvRow2 = const KVRow("Text", "ABC");
|
||||
const kvRow2 = NameValueModel(name: "Text", value: "ABC");
|
||||
expect(rowsToMap([kvRow2], isHeader: true), {"text": "ABC"});
|
||||
});
|
||||
test('Testing when header is false and key is in upper case', () {
|
||||
List<KVRow> kvRow3 = const [
|
||||
KVRow("TEXT", "ABC"),
|
||||
KVRow("version", 0.1),
|
||||
KVRow("month", 4)
|
||||
const kvRow3 = <NameValueModel>[
|
||||
NameValueModel(name: "TEXT", value: "ABC"),
|
||||
NameValueModel(name: "version", value: 0.1),
|
||||
NameValueModel(name: "month", value: 4),
|
||||
];
|
||||
expect(
|
||||
rowsToMap(kvRow3), {"TEXT": "ABC", "version": "0.1", "month": "4"});
|
||||
@ -97,10 +97,10 @@ void main() {
|
||||
});
|
||||
test('Testing with a map value', () {
|
||||
Map<String, String> value1 = {"text": "abc", "lang": "eng", "code": "1"};
|
||||
List<KVRow> result1Expected = const [
|
||||
KVRow("text", "abc"),
|
||||
KVRow("lang", "eng"),
|
||||
KVRow("code", "1")
|
||||
const result1Expected = <NameValueModel>[
|
||||
NameValueModel(name: "text", value: "abc"),
|
||||
NameValueModel(name: "lang", value: "eng"),
|
||||
NameValueModel(name: "code", value: "1")
|
||||
];
|
||||
expect(mapToRows(value1), result1Expected);
|
||||
});
|
||||
|
@ -1,7 +1,7 @@
|
||||
import 'package:test/test.dart';
|
||||
import 'package:http_parser/http_parser.dart';
|
||||
import 'package:apidash/utils/http_utils.dart';
|
||||
import 'package:apidash/models/kvrow_model.dart';
|
||||
import 'package:apidash/models/name_value_model.dart';
|
||||
import 'package:apidash/consts.dart';
|
||||
import '../test_utilities.dart';
|
||||
|
||||
@ -156,52 +156,50 @@ void main() {
|
||||
path: 'guides/libraries/library-tour',
|
||||
fragment: 'numbers');
|
||||
String uriScheme1Expected = 'https';
|
||||
expect(getUriScheme(uri1), (uriScheme1Expected,true));
|
||||
expect(getUriScheme(uri1), (uriScheme1Expected, true));
|
||||
});
|
||||
test('Testing getUriScheme for mailto scheme value', () {
|
||||
Uri uri2 = Uri(scheme: 'mailto');
|
||||
String uriScheme2Expected = 'mailto';
|
||||
expect(getUriScheme(uri2), (uriScheme2Expected,false));
|
||||
expect(getUriScheme(uri2), (uriScheme2Expected, false));
|
||||
});
|
||||
test('Testing getUriScheme for empty scheme value', () {
|
||||
Uri uri3 = Uri(
|
||||
scheme: '');
|
||||
expect(getUriScheme(uri3), (null,false));
|
||||
Uri uri3 = Uri(scheme: '');
|
||||
expect(getUriScheme(uri3), (null, false));
|
||||
});
|
||||
test('Testing getUriScheme for null scheme value', () {
|
||||
Uri uri4 = Uri(
|
||||
scheme: null);
|
||||
expect(getUriScheme(uri4), (null,false));
|
||||
Uri uri4 = Uri(scheme: null);
|
||||
expect(getUriScheme(uri4), (null, false));
|
||||
});
|
||||
});
|
||||
|
||||
group("Testing getValidRequestUri", () {
|
||||
test('Testing getValidRequestUri for normal values', () {
|
||||
String url1 = "https://api.foss42.com/country/data";
|
||||
KVRow kvRow1 = const KVRow("code", "US");
|
||||
const kvRow1 = NameValueModel(name: "code", value: "US");
|
||||
Uri uri1Expected = Uri(
|
||||
scheme: 'https',
|
||||
host: 'api.foss42.com',
|
||||
path: 'country/data',
|
||||
queryParameters: {'code':'US'});
|
||||
queryParameters: {'code': 'US'});
|
||||
expect(getValidRequestUri(url1, [kvRow1]), (uri1Expected, null));
|
||||
});
|
||||
test('Testing getValidRequestUri for null url value', () {
|
||||
KVRow kvRow2 = const KVRow("code", "US");
|
||||
const kvRow2 = NameValueModel(name: "code", value: "US");
|
||||
expect(getValidRequestUri(null, [kvRow2]), (null, "URL is missing!"));
|
||||
});
|
||||
test('Testing getValidRequestUri for empty url value', () {
|
||||
KVRow kvRow3 = const KVRow("", "");
|
||||
const kvRow3 = NameValueModel(name: "", value: "");
|
||||
expect(getValidRequestUri("", [kvRow3]), (null, "URL is missing!"));
|
||||
});
|
||||
test('Testing getValidRequestUri when https is not provided in url', () {
|
||||
String url4 = "api.foss42.com/country/data";
|
||||
KVRow kvRow4 = const KVRow("code", "US");
|
||||
const kvRow4 = NameValueModel(name: "code", value: "US");
|
||||
Uri uri4Expected = Uri(
|
||||
scheme: 'https',
|
||||
host: 'api.foss42.com',
|
||||
path: 'country/data',
|
||||
queryParameters: {'code':'US'});
|
||||
queryParameters: {'code': 'US'});
|
||||
expect(getValidRequestUri(url4, [kvRow4]), (uri4Expected, null));
|
||||
});
|
||||
test('Testing getValidRequestUri when url has fragment', () {
|
||||
@ -214,16 +212,18 @@ void main() {
|
||||
});
|
||||
test('Testing getValidRequestUri when uri scheme is not supported', () {
|
||||
String url5 = "mailto:someone@example.com";
|
||||
expect(getValidRequestUri(url5, null), (null, "Unsupported URL Scheme (mailto)"));
|
||||
expect(getValidRequestUri(url5, null),
|
||||
(null, "Unsupported URL Scheme (mailto)"));
|
||||
});
|
||||
test('Testing getValidRequestUri when query params in both url and kvrow', () {
|
||||
test('Testing getValidRequestUri when query params in both url and kvrow',
|
||||
() {
|
||||
String url6 = "api.foss42.com/country/data?code=IND";
|
||||
KVRow kvRow6 = const KVRow("code", "US");
|
||||
const kvRow6 = NameValueModel(name: "code", value: "US");
|
||||
Uri uri6Expected = Uri(
|
||||
scheme: 'https',
|
||||
host: 'api.foss42.com',
|
||||
path: 'country/data',
|
||||
queryParameters: {'code':'US'});
|
||||
queryParameters: {'code': 'US'});
|
||||
expect(getValidRequestUri(url6, [kvRow6]), (uri6Expected, null));
|
||||
});
|
||||
test('Testing getValidRequestUri when kvrow is null', () {
|
||||
@ -232,7 +232,7 @@ void main() {
|
||||
scheme: 'https',
|
||||
host: 'api.foss42.com',
|
||||
path: 'country/data',
|
||||
queryParameters: {'code':'US'});
|
||||
queryParameters: {'code': 'US'});
|
||||
expect(getValidRequestUri(url7, null), (uri7Expected, null));
|
||||
});
|
||||
});
|
||||
@ -241,72 +241,78 @@ void main() {
|
||||
test('Testing getResponseBodyViewOptions for application/json', () {
|
||||
MediaType mediaType1 = MediaType("application", "json");
|
||||
var result1 = getResponseBodyViewOptions(mediaType1);
|
||||
expect(result1.$0,kCodeRawBodyViewOptions);
|
||||
expect(result1.$1, "json");
|
||||
expect(result1.$1, kPreviewRawBodyViewOptions);
|
||||
expect(result1.$2, "json");
|
||||
});
|
||||
test('Testing getResponseBodyViewOptions for application/xml', () {
|
||||
MediaType mediaType2 = MediaType("application", "xml");
|
||||
var result2 = getResponseBodyViewOptions(mediaType2);
|
||||
expect(result2.$0, kCodeRawBodyViewOptions);
|
||||
expect(result2.$1,"xml");
|
||||
expect(result2.$1, kCodeRawBodyViewOptions);
|
||||
expect(result2.$2, "xml");
|
||||
});
|
||||
test('Testing getResponseBodyViewOptions for message/news a format currently not supported', () {
|
||||
test(
|
||||
'Testing getResponseBodyViewOptions for message/news a format currently not supported',
|
||||
() {
|
||||
MediaType mediaType3 = MediaType("message", "news");
|
||||
var result3 = getResponseBodyViewOptions(mediaType3);
|
||||
expect(result3.$0,kNoBodyViewOptions);
|
||||
expect(result3.$1,null);
|
||||
expect(result3.$1, kNoBodyViewOptions);
|
||||
expect(result3.$2, null);
|
||||
});
|
||||
test('Testing getResponseBodyViewOptions for application/calendar+json', () {
|
||||
test('Testing getResponseBodyViewOptions for application/calendar+json',
|
||||
() {
|
||||
MediaType mediaType4 = MediaType("application", "calendar+json");
|
||||
var result4 = getResponseBodyViewOptions(mediaType4);
|
||||
expect(result4.$0,kCodeRawBodyViewOptions);
|
||||
expect(result4.$1, "json");
|
||||
expect(result4.$1, kPreviewRawBodyViewOptions);
|
||||
expect(result4.$2, "json");
|
||||
});
|
||||
test('Testing getResponseBodyViewOptions for image/svg+xml', () {
|
||||
MediaType mediaType5 = MediaType("image", "svg+xml");
|
||||
var result5 = getResponseBodyViewOptions(mediaType5);
|
||||
expect(result5.$0,kCodeRawBodyViewOptions);
|
||||
expect(result5.$1, "xml");
|
||||
expect(result5.$1, kCodeRawBodyViewOptions);
|
||||
expect(result5.$2, "xml");
|
||||
});
|
||||
test('Testing getResponseBodyViewOptions for application/xhtml+xml', () {
|
||||
MediaType mediaType6 = MediaType("application", "xhtml+xml");
|
||||
var result6 = getResponseBodyViewOptions(mediaType6);
|
||||
expect(result6.$0,kCodeRawBodyViewOptions);
|
||||
expect(result6.$1, "xml");
|
||||
expect(result6.$1, kCodeRawBodyViewOptions);
|
||||
expect(result6.$2, "xml");
|
||||
});
|
||||
test('Testing getResponseBodyViewOptions for application/xml-external-parsed-entity', () {
|
||||
MediaType mediaType7 = MediaType("application", "xml-external-parsed-entity");
|
||||
test(
|
||||
'Testing getResponseBodyViewOptions for application/xml-external-parsed-entity',
|
||||
() {
|
||||
MediaType mediaType7 =
|
||||
MediaType("application", "xml-external-parsed-entity");
|
||||
var result7 = getResponseBodyViewOptions(mediaType7);
|
||||
expect(result7.$0,kCodeRawBodyViewOptions);
|
||||
expect(result7.$1, "xml");
|
||||
expect(result7.$1, kCodeRawBodyViewOptions);
|
||||
expect(result7.$2, "xml");
|
||||
});
|
||||
test('Testing getResponseBodyViewOptions for text/html', () {
|
||||
MediaType mediaType8 = MediaType("text", "html");
|
||||
var result8 = getResponseBodyViewOptions(mediaType8);
|
||||
expect(result8.$0,kCodeRawBodyViewOptions);
|
||||
expect(result8.$1, "xml");
|
||||
expect(result8.$1, kCodeRawBodyViewOptions);
|
||||
expect(result8.$2, "xml");
|
||||
});
|
||||
test('Testing getResponseBodyViewOptions for application/pdf', () {
|
||||
MediaType mediaType9 = MediaType("application", "pdf");
|
||||
var result9 = getResponseBodyViewOptions(mediaType9);
|
||||
expect(result9.$0,kNoBodyViewOptions);
|
||||
expect(result9.$1, "pdf");
|
||||
expect(result9.$1, kPreviewBodyViewOptions);
|
||||
expect(result9.$2, "pdf");
|
||||
});
|
||||
test('Testing getResponseBodyViewOptions for text/calendar', () {
|
||||
MediaType mediaType10 = MediaType("text", "calendar");
|
||||
var result10 = getResponseBodyViewOptions(mediaType10);
|
||||
expect(result10.$0,kRawBodyViewOptions);
|
||||
expect(result10.$1, "calendar");
|
||||
expect(result10.$1, kRawBodyViewOptions);
|
||||
expect(result10.$2, "calendar");
|
||||
});
|
||||
});
|
||||
|
||||
group("Testing formatBody", () {
|
||||
test('Testing formatBody for null values', () {
|
||||
expect(formatBody(null, null),null);
|
||||
expect(formatBody(null, null), null);
|
||||
});
|
||||
test('Testing formatBody for null body values', () {
|
||||
MediaType mediaType1 = MediaType("application", "xml");
|
||||
expect(formatBody(null, mediaType1),null);
|
||||
expect(formatBody(null, mediaType1), null);
|
||||
});
|
||||
test('Testing formatBody for null MediaType values', () {
|
||||
String body1 = '''
|
||||
@ -314,7 +320,7 @@ void main() {
|
||||
"text":"The Chosen One";
|
||||
}
|
||||
''';
|
||||
expect(formatBody(body1, null),null);
|
||||
expect(formatBody(body1, null), null);
|
||||
});
|
||||
test('Testing formatBody for json subtype values', () {
|
||||
String body2 = '''{"data":{"area":9831510.0,"population":331893745}}''';
|
||||
@ -325,7 +331,7 @@ void main() {
|
||||
"population": 331893745
|
||||
}
|
||||
}''';
|
||||
expect(formatBody(body2, mediaType2),result2Expected);
|
||||
expect(formatBody(body2, mediaType2), result2Expected);
|
||||
});
|
||||
test('Testing formatBody for xml subtype values', () {
|
||||
String body3 = '''
|
||||
@ -347,7 +353,7 @@ void main() {
|
||||
<calories>650</calories>
|
||||
</food>
|
||||
</breakfast_menu>''';
|
||||
expect(formatBody(body3, mediaType3),result3Expected);
|
||||
expect(formatBody(body3, mediaType3), result3Expected);
|
||||
});
|
||||
group("Testing formatBody for html", () {
|
||||
MediaType mediaTypeHtml = MediaType("text", "html");
|
||||
@ -358,16 +364,20 @@ void main() {
|
||||
<p>My first paragraph.</p>
|
||||
</body>
|
||||
</html>''';
|
||||
expect(formatBody(body4, mediaTypeHtml),body4);
|
||||
expect(formatBody(body4, mediaTypeHtml), body4);
|
||||
});
|
||||
|
||||
test('Testing formatBody for html subtype values with random values', () {
|
||||
String body5 = '''<html>${getRandomStringLines(100, 10000)}</html>''';
|
||||
expect(formatBody(body5, mediaTypeHtml),null);
|
||||
String body5 =
|
||||
'''<html>${RandomStringGenerator.getRandomStringLines(100, 10000)}</html>''';
|
||||
expect(formatBody(body5, mediaTypeHtml), null);
|
||||
});
|
||||
test('Testing formatBody for html subtype values with random values within limit', () {
|
||||
String body6 = '''<html>${getRandomStringLines(100, 190)}</html>''';
|
||||
expect(formatBody(body6, mediaTypeHtml),body6);
|
||||
test(
|
||||
'Testing formatBody for html subtype values with random values within limit',
|
||||
() {
|
||||
String body6 =
|
||||
'''<html>${RandomStringGenerator.getRandomStringLines(100, 190)}</html>''';
|
||||
expect(formatBody(body6, mediaTypeHtml), body6);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1,4 +1 @@
|
||||
import 'package:apidash/main.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
void main() {}
|
||||
|
@ -44,11 +44,11 @@ void main() {
|
||||
expect(changedValue, 'Single Tapped');
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.tap(tappable);
|
||||
await tester.pump(const Duration(milliseconds: 100));
|
||||
await tester.tap(tappable);
|
||||
await tester.pumpAndSettle(const Duration(seconds: 2));
|
||||
expect(changedValue, 'Double Tapped');
|
||||
// await tester.tap(tappable);
|
||||
// await tester.pump(const Duration(milliseconds: 100));
|
||||
// await tester.tap(tappable);
|
||||
// await tester.pumpAndSettle(const Duration(seconds: 2));
|
||||
// expect(changedValue, 'Double Tapped');
|
||||
});
|
||||
testWidgets('Testing Sidebar Request Card dark mode', (tester) async {
|
||||
dynamic changedValue;
|
||||
|
@ -50,7 +50,7 @@ void main() async {
|
||||
});
|
||||
testWidgets('Testing for code previewer when code is of 1000 lines',
|
||||
(tester) async {
|
||||
String codeLines = getRandomStringLines(1000, 20);
|
||||
String codeLines = RandomStringGenerator.getRandomStringLines(1000, 20);
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
title: 'Code Previewer',
|
||||
|
@ -80,7 +80,9 @@ void main() async {
|
||||
await tester.pump();
|
||||
await tester.pump(const Duration(seconds: 1));
|
||||
|
||||
expect(find.text('Kotlin (OkHttp)'), findsWidgets);
|
||||
expect(find.text('Kotlin (okhttp3)'), findsWidgets);
|
||||
expect(find.text('Python (http.client)'), findsWidgets);
|
||||
expect(find.text('Python (requests)'), findsWidgets);
|
||||
|
||||
expect(find.textContaining('Error Status Code', findRichText: true),
|
||||
findsOneWidget);
|
||||
|
@ -25,5 +25,5 @@ void main() {
|
||||
expect(find.byIcon(Icons.star), findsOneWidget);
|
||||
expect(find.text('Star on GitHub'), findsOneWidget);
|
||||
await tester.tap(find.byIcon(Icons.star));
|
||||
});
|
||||
}, skip: true);
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:apidash/widgets/widgets.dart';
|
||||
import 'package:apidash/consts.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:printing/printing.dart' show PdfPreview;
|
||||
import '../test_consts.dart';
|
||||
|
||||
void main() {
|
||||
@ -12,7 +13,12 @@ void main() {
|
||||
MaterialApp(
|
||||
title: 'Previewer',
|
||||
home: Scaffold(
|
||||
body: Previewer(type: 'application', subtype: 'pdf', bytes: bytes1),
|
||||
body: Previewer(
|
||||
type: 'application',
|
||||
subtype: 'pdf',
|
||||
bytes: bytes1,
|
||||
body: "",
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
@ -20,7 +26,8 @@ void main() {
|
||||
expect(
|
||||
find.text(
|
||||
"${kMimeTypeRaiseIssueStart}application/pdf$kMimeTypeRaiseIssue"),
|
||||
findsOneWidget);
|
||||
findsNothing);
|
||||
expect(find.byType(PdfPreview), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('Testing when type/subtype is audio/mpeg', (tester) async {
|
||||
@ -28,7 +35,12 @@ void main() {
|
||||
MaterialApp(
|
||||
title: 'Previewer',
|
||||
home: Scaffold(
|
||||
body: Previewer(type: 'audio', subtype: 'mpeg', bytes: bytes1),
|
||||
body: Previewer(
|
||||
type: 'audio',
|
||||
subtype: 'mpeg',
|
||||
bytes: bytes1,
|
||||
body: "",
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
@ -41,7 +53,12 @@ void main() {
|
||||
MaterialApp(
|
||||
title: 'Previewer',
|
||||
home: Scaffold(
|
||||
body: Previewer(type: 'video', subtype: 'H264', bytes: bytes1),
|
||||
body: Previewer(
|
||||
type: 'video',
|
||||
subtype: 'H264',
|
||||
bytes: bytes1,
|
||||
body: "",
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
@ -56,7 +73,12 @@ void main() {
|
||||
MaterialApp(
|
||||
title: 'Previewer',
|
||||
home: Scaffold(
|
||||
body: Previewer(type: 'model', subtype: 'step+xml', bytes: bytes1),
|
||||
body: Previewer(
|
||||
type: 'model',
|
||||
subtype: 'step+xml',
|
||||
bytes: bytes1,
|
||||
body: "",
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
@ -72,8 +94,12 @@ void main() {
|
||||
MaterialApp(
|
||||
title: 'Previewer',
|
||||
home: Scaffold(
|
||||
body:
|
||||
Previewer(type: 'image', subtype: 'jpeg', bytes: kBodyBytesJpeg),
|
||||
body: Previewer(
|
||||
type: 'image',
|
||||
subtype: 'jpeg',
|
||||
bytes: kBodyBytesJpeg,
|
||||
body: "",
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
@ -111,7 +137,11 @@ void main() {
|
||||
title: 'Previewer',
|
||||
home: Scaffold(
|
||||
body: Previewer(
|
||||
type: 'image', subtype: 'jpeg', bytes: bytesJpegCorrupt),
|
||||
type: 'image',
|
||||
subtype: 'jpeg',
|
||||
bytes: bytesJpegCorrupt,
|
||||
body: "",
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
@ -128,7 +158,11 @@ void main() {
|
||||
title: 'Previewer',
|
||||
home: Scaffold(
|
||||
body: Previewer(
|
||||
type: 'audio', subtype: 'mpeg', bytes: bytesAudioCorrupt),
|
||||
type: 'audio',
|
||||
subtype: 'mpeg',
|
||||
bytes: bytesAudioCorrupt,
|
||||
body: "",
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
@ -204,8 +204,9 @@ void main() {
|
||||
url: 'api.foss42.com/case/lower',
|
||||
name: 'foss42 api',
|
||||
requestHeaders: [
|
||||
KVRow('content-length', '18'),
|
||||
KVRow('content-type', 'application/json; charset=utf-8')
|
||||
NameValueModel(name: 'content-length', value: '18'),
|
||||
NameValueModel(
|
||||
name: 'content-type', value: 'application/json; charset=utf-8')
|
||||
],
|
||||
requestBodyContentType: ContentType.json,
|
||||
requestBody: '''{
|
||||
|
@ -6,12 +6,12 @@ import 'package:multi_split_view/multi_split_view.dart';
|
||||
void main() {
|
||||
testWidgets('Testing for Dashboard Splitview', (tester) async {
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
const MaterialApp(
|
||||
title: 'Dashboard Splitview',
|
||||
home: Scaffold(
|
||||
body: DashboardSplitView(
|
||||
sidebarWidget: Column(children: const [Text("Hello")]),
|
||||
mainWidget: Column(children: const [Text("World")]),
|
||||
sidebarWidget: Column(children: [Text("Hello")]),
|
||||
mainWidget: Column(children: [Text("World")]),
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -23,12 +23,12 @@ void main() {
|
||||
});
|
||||
testWidgets('Testing for Equal SplitView', (tester) async {
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
const MaterialApp(
|
||||
title: 'Equal SplitView',
|
||||
home: Scaffold(
|
||||
body: EqualSplitView(
|
||||
leftWidget: Column(children: const [Text("Hello equal")]),
|
||||
rightWidget: Column(children: const [Text("World equal")]),
|
||||
leftWidget: Column(children: [Text("Hello equal")]),
|
||||
rightWidget: Column(children: [Text("World equal")]),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -10,8 +10,8 @@ void main() {
|
||||
MaterialApp(
|
||||
title: 'URL Field',
|
||||
theme: kThemeDataDark,
|
||||
home: Scaffold(
|
||||
body: Column(children: const [URLField(activeId: '2')]),
|
||||
home: const Scaffold(
|
||||
body: Column(children: [URLField(activeId: '2')]),
|
||||
),
|
||||
),
|
||||
);
|
||||
@ -30,9 +30,9 @@ void main() {
|
||||
MaterialApp(
|
||||
title: 'CellField',
|
||||
theme: kThemeDataLight,
|
||||
home: Scaffold(
|
||||
home: const Scaffold(
|
||||
body: Column(
|
||||
children: const [
|
||||
children: [
|
||||
CellField(
|
||||
keyId: "4",
|
||||
hintText: "Passing some hint text",
|
||||
|
Reference in New Issue
Block a user