Merge branch 'main' into main

This commit is contained in:
Ashita Prasad
2023-08-29 05:22:55 +05:30
committed by GitHub
50 changed files with 1746 additions and 686 deletions

2
.gitignore vendored
View File

@ -49,8 +49,10 @@ linux/
macos/
windows/
web/
ios/
.vscode/*
icons/
coverage/*
installers/*
.metadata
.fvm/

View File

@ -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:
@ -151,6 +111,20 @@ To view the coverage report in the browser for further analysis, execute:
open coverage/html/index.html
```
#### Testing a single file
To run tests specified in a single file, execute the following command:
```
flutter test <file_path>.dart
```
Example:
```
flutter test test/widgets/codegen_previewer_test.dart
```
### How to add a new package to pubspec.yaml?
Instead of copy pasting from pub.dev, it is recommended that you use `flutter pub add package_name` to add a new package to `pubspec.yaml`. You can read more [here](https://docs.flutter.dev/packages-and-plugins/using-packages#adding-a-package-dependency-to-an-app-using-flutter-pub-add).

View File

@ -1,13 +1,13 @@
# Installation Instructions
## Windows
Download the latest Windows Installer (64 bit) from [here](https://github.com/foss42/api-dash/releases/latest)
Download the latest Windows Installer (64 bit) from [here](https://github.com/foss42/apidash/releases/latest)
To install it, simply double click on the installer and follow the step by step installation wizard.
## MacOS
Download the latest MacOS Installer (Universal - Intel and Apple Silicon) from [here](https://github.com/foss42/api-dash/releases/latest)
Download the latest MacOS Installer (Universal - Intel and Apple Silicon) from [here](https://github.com/foss42/apidash/releases/latest)
**As this app is distributed outside the App Store you have to follow the following instructions to setup and run it only for the first time.**
@ -46,7 +46,7 @@ Note: The next step has to be performed twice so that macOS adds the app to whit
### Debian-based Linux Distributions (Debian, Ubuntu, Linux Mint, etc.)
Download the `.deb` file from the [latest release](https://github.com/foss42/api-dash/releases/latest) corresponding to you CPU architecture (x64/amd64 or arm64).
Download the `.deb` file from the [latest release](https://github.com/foss42/apidash/releases/latest) corresponding to you CPU architecture (x64/amd64 or arm64).
`cd` to the Downloads folder and execute the following command to install API Dash.
@ -64,7 +64,7 @@ Launch API Dash via `apidash` command or by clicking on the API Dash app icon.
### Red Hat-based Linux Distributions (Fedora, Rocky, AlmaLinux, CentOS, RHEL, etc.)
Download the `.rpm` file from the [latest release](https://github.com/foss42/api-dash/releases/latest) corresponding to you CPU architecture (x86_64 or aarch64/arm64).
Download the `.rpm` file from the [latest release](https://github.com/foss42/apidash/releases/latest) corresponding to you CPU architecture (x86_64 or aarch64/arm64).
`cd` to the Downloads folder and execute the following command to install API Dash.

View File

@ -23,21 +23,21 @@ API Dash can be downloaded from the links below:
<tr>
<td>macOS</td>
<td><code>.dmg</code></td>
<td><a href="https://github.com/foss42/api-dash/blob/main/INSTALLATION.md#macos">Link</a></td>
<td><a href="https://github.com/foss42/apidash/blob/main/INSTALLATION.md#macos">Link</a></td>
<td>Apple Silicon & Intel</td>
<td><a href="https://bit.ly/44wmazf">Link</a></td>
</tr>
<tr>
<td>Windows</td>
<td><code>.exe</code></td>
<td><a href="https://github.com/foss42/api-dash/blob/main/INSTALLATION.md#windows">Link</a></td>
<td><a href="https://github.com/foss42/apidash/blob/main/INSTALLATION.md#windows">Link</a></td>
<td>64-bit</td>
<td><a href="https://bit.ly/424ExKb">Link</a></td>
</tr>
<tr>
<td rowspan=4>Linux</td>
<td rowspan=2><code>.deb</code></td>
<td rowspan=2><a href="https://github.com/foss42/api-dash/blob/main/INSTALLATION.md#debian-based-linux-distributions-debian-ubuntu-linux-mint-etc">Link</a></td>
<td rowspan=2><a href="https://github.com/foss42/apidash/blob/main/INSTALLATION.md#debian-based-linux-distributions-debian-ubuntu-linux-mint-etc">Link</a></td>
<td>amd64</td>
<td><a href="https://bit.ly/44sWPq2">Link</a></td>
</tr>
@ -47,7 +47,7 @@ API Dash can be downloaded from the links below:
</tr>
<tr>
<td rowspan=2><code>.rpm</code></td>
<td rowspan=2><a href="https://github.com/foss42/api-dash/blob/main/INSTALLATION.md#red-hat-based-linux-distributions-fedora-rocky-almalinux-centos-rhel-etc">Link</a></td>
<td rowspan=2><a href="https://github.com/foss42/apidash/blob/main/INSTALLATION.md#red-hat-based-linux-distributions-fedora-rocky-almalinux-centos-rhel-etc">Link</a></td>
<td>x86_64</td>
<td><a href="https://bit.ly/417gWHe">Link</a></td>
</tr>
@ -62,7 +62,7 @@ API Dash can be downloaded from the links below:
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
https://github.com/foss42/apidash/assets/615622/fccc569e-3152-47be-9f94-ceb851ee85a0
## List of Features
@ -101,16 +101,16 @@ Visit [CHANGELOG.md](CHANGELOG.md)
## Provide Feedback, Report Bugs & Request New Features
Just click on the [Issue tab](https://github.com/foss42/api-dash/issues) to raise a new issue in this repo.
Just click on the [Issue tab](https://github.com/foss42/apidash/issues) to raise a new issue in this repo.
## Contribute to API Dash
You can contribute to API Dash in any or all of the following ways:
- [Ask a question](https://github.com/foss42/api-dash/discussions)
- [Submit a bug report](https://github.com/foss42/api-dash/issues/new/choose)
- [Request a new feature](https://github.com/foss42/api-dash/issues/new/choose)
- [Suggest ways to improve the developer experience of an existing feature](https://github.com/foss42/api-dash/issues/new/choose)
- [Ask a question](https://github.com/foss42/apidash/discussions)
- [Submit a bug report](https://github.com/foss42/apidash/issues/new/choose)
- [Request a new feature](https://github.com/foss42/apidash/issues/new/choose)
- [Suggest ways to improve the developer experience of an existing feature](https://github.com/foss42/apidash/issues/new/choose)
- Add documentation
- To add a new feature, resolve an existing issue or add a new test to the project, check out our [Contribution Guidelines](CONTRIBUTING.md).

View File

@ -40,7 +40,7 @@ Using API Dash, you can easily create & customize your API requests, visually in
7. Notification on save, download and any other user action (UX improvement).
8. Linux builds are now available for API Dash (.deb & .rpm)
.. and various bug fixes & performance improvements. View full changelog [here](https://github.com/foss42/api-dash/blob/main/CHANGELOG.md).
.. and various bug fixes & performance improvements. View full changelog [here](https://github.com/foss42/apidash/blob/main/CHANGELOG.md).
#br
#br

View File

@ -47,7 +47,7 @@ val body = "${requestModel.requestBody}".toRequestBody(mediaType)\n""";
}
for (final queryParam in requestModel.requestParams!) {
result =
"""$result .addQueryParameter("${queryParam.k}", "${queryParam.v}")\n""";
"""$result .addQueryParameter("${queryParam.name}", "${queryParam.value}")\n""";
}
return result;
}
@ -58,7 +58,7 @@ val body = "${requestModel.requestBody}".toRequestBody(mediaType)\n""";
return result;
}
for (final header in requestModel.requestHeaders!) {
result = """$result .addHeader("${header.k}", "${header.v}")\n""";
result = """$result .addHeader("${header.name}", "${header.value}")\n""";
}
return result;
}

View File

@ -6,7 +6,7 @@ import 'package:google_fonts/google_fonts.dart';
import 'package:davi/davi.dart';
const kDiscordUrl = "https://bit.ly/heyfoss";
const kGitUrl = "https://github.com/foss42/api-dash";
const kGitUrl = "https://github.com/foss42/apidash";
const kIssueUrl = "$kGitUrl/issues";
final kIsMacOS = !kIsWeb && Platform.isMacOS;
@ -28,6 +28,7 @@ const kWindowTitle = "API Dash";
const kMinWindowSize = Size(900, 600);
const kMinInitialWindowWidth = 1200.0;
const kMinInitialWindowHeight = 800.0;
const kMinRequestEditorDetailsCardPaneSize = 300.0;
const kColorSchemeSeed = Colors.blue;
final kFontFamily = GoogleFonts.openSans().fontFamily;
@ -225,16 +226,19 @@ const kMethodsWithBody = [
HTTPVerb.patch,
HTTPVerb.delete,
];
const kDefaultHttpMethod = HTTPVerb.get;
const kDefaultContentType = ContentType.json;
enum CodegenLanguage {
dartHttp("Dart (http)"),
kotlinOkHttp("Kotlin (OkHttp)"),
dartHttp("Dart (http)", "dart", "dart"),
kotlinOkHttp("Kotlin (okhttp3)", "java", "kt");
pythonHttpClient("Python (http.client)");
const CodegenLanguage(this.label);
const CodegenLanguage(this.label, this.codeHighlightLang, this.ext);
final String label;
final String codeHighlightLang;
final String ext;
}
const JsonEncoder kEncoder = JsonEncoder.withIndent(' ');

View File

@ -13,7 +13,7 @@ void main() async {
await setupInitialWindow();
} else {
var win = getInitialSize();
await setupWindow(sz: win.$0, off: win.$1);
await setupWindow(sz: win.$1, off: win.$2);
}
runApp(
ProviderScope(

View File

@ -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,
);
}
}

View File

@ -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';

View File

@ -0,0 +1,17 @@
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:flutter/foundation.dart';
part 'name_value_model.freezed.dart';
part 'name_value_model.g.dart';
@freezed
class NameValueModel with _$NameValueModel {
const factory NameValueModel({
required String name,
required dynamic value,
}) = _NameValueModel;
factory NameValueModel.fromJson(Map<String, Object?> json) =>
_$NameValueModelFromJson(json);
}

View 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;
}

View 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,
};

View File

@ -1,21 +1,21 @@
import 'package:flutter/foundation.dart';
import 'package:apidash/consts.dart';
import 'package:apidash/utils/utils.dart' show mapToRows, rowsToMap;
import 'kvrow_model.dart';
import 'name_value_model.dart';
import 'response_model.dart';
@immutable
class RequestModel {
const RequestModel({
required this.id,
this.method = kDefaultHttpMethod,
this.method = HTTPVerb.get,
this.url = "",
this.name = "",
this.description = "",
this.requestTabIndex = 0,
this.requestHeaders,
this.requestParams,
this.requestBodyContentType = kDefaultContentType,
this.requestBodyContentType = ContentType.json,
this.requestBody,
this.responseStatus,
this.message,
@ -28,8 +28,8 @@ class RequestModel {
final String name;
final String description;
final int requestTabIndex;
final List<KVRow>? requestHeaders;
final List<KVRow>? requestParams;
final List<NameValueModel>? requestHeaders;
final List<NameValueModel>? requestParams;
final ContentType requestBodyContentType;
final String? requestBody;
final int? responseStatus;
@ -59,8 +59,8 @@ class RequestModel {
String? name,
String? description,
int? requestTabIndex,
List<KVRow>? requestHeaders,
List<KVRow>? requestParams,
List<NameValueModel>? requestHeaders,
List<NameValueModel>? requestParams,
ContentType? requestBodyContentType,
String? requestBody,
int? responseStatus,

View File

@ -96,8 +96,8 @@ class CollectionStateNotifier extends StateNotifier<List<RequestModel>?> {
String? name,
String? description,
int? requestTabIndex,
List<KVRow>? requestHeaders,
List<KVRow>? requestParams,
List<NameValueModel>? requestHeaders,
List<NameValueModel>? requestParams,
ContentType? requestBodyContentType,
String? requestBody,
int? responseStatus,
@ -132,17 +132,17 @@ class CollectionStateNotifier extends StateNotifier<List<RequestModel>?> {
var responseRec =
await request(requestModel, defaultUriScheme: defaultUriScheme);
late final RequestModel newRequestModel;
if (responseRec.$0 == null) {
if (responseRec.$1 == null) {
newRequestModel = requestModel.copyWith(
responseStatus: -1,
message: responseRec.$2,
message: responseRec.$3,
);
} else {
final responseModel = baseResponseModel.fromResponse(
response: responseRec.$0!,
time: responseRec.$1!,
response: responseRec.$1!,
time: responseRec.$2!,
);
int statusCode = responseRec.$0!.statusCode;
int statusCode = responseRec.$1!.statusCode;
newRequestModel = requestModel.copyWith(
responseStatus: statusCode,
message: kResponseCodeReasons[statusCode],
@ -203,4 +203,10 @@ class CollectionStateNotifier extends StateNotifier<List<RequestModel>?> {
await hiveHandler.removeUnused();
ref.read(saveDataStateProvider.notifier).update((state) => false);
}
Map<String, dynamic> exportData() {
return {
"data": state!.map((e) => e.toJson(includeResponse: false)).toList()
};
}
}

View File

@ -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:",
),

View File

@ -15,7 +15,7 @@ class EditRequestHeaders extends ConsumerStatefulWidget {
}
class EditRequestHeadersState extends ConsumerState<EditRequestHeaders> {
late List<KVRow> rows;
late List<NameValueModel> rows;
final random = Random.secure();
late int seed;
@ -37,9 +37,11 @@ class EditRequestHeadersState extends ConsumerState<EditRequestHeaders> {
final length = ref.watch(activeRequestModelProvider
.select((value) => value?.requestHeaders?.length));
var rH = ref.read(activeRequestModelProvider)?.requestHeaders;
rows = (rH == null || rH.isEmpty) ? [const KVRow("", "")] : rH;
rows = (rH == null || rH.isEmpty)
? [const NameValueModel(name: "", value: "")]
: rH;
DaviModel<KVRow> model = DaviModel<KVRow>(
DaviModel<NameValueModel> model = DaviModel<NameValueModel>(
rows: rows,
columns: [
DaviColumn(
@ -49,10 +51,10 @@ class EditRequestHeadersState extends ConsumerState<EditRequestHeaders> {
int idx = row.index;
return CellField(
keyId: "$activeId-$idx-headers-k-$seed",
initialValue: rows[idx].k,
initialValue: rows[idx].name,
hintText: "Add Header Name",
onChanged: (value) {
rows[idx] = rows[idx].copyWith(k: value);
rows[idx] = rows[idx].copyWith(name: value);
_onFieldChange(activeId!);
},
colorScheme: Theme.of(context).colorScheme,
@ -76,10 +78,10 @@ class EditRequestHeadersState extends ConsumerState<EditRequestHeaders> {
int idx = row.index;
return CellField(
keyId: "$activeId-$idx-headers-v-$seed",
initialValue: rows[idx].v,
initialValue: rows[idx].value,
hintText: " Add Header Value",
onChanged: (value) {
rows[idx] = rows[idx].copyWith(v: value);
rows[idx] = rows[idx].copyWith(value: value);
_onFieldChange(activeId!);
},
colorScheme: Theme.of(context).colorScheme,
@ -96,6 +98,9 @@ class EditRequestHeadersState extends ConsumerState<EditRequestHeaders> {
? kIconRemoveDark
: kIconRemoveLight,
onTap: () {
if (rows.length == 1) {
return;
}
rows.removeAt(row.index);
seed = random.nextInt(kRandMax);
_onFieldChange(activeId!);
@ -118,7 +123,7 @@ class EditRequestHeadersState extends ConsumerState<EditRequestHeaders> {
Expanded(
child: DaviTheme(
data: kTableThemeData,
child: Davi<KVRow>(model),
child: Davi<NameValueModel>(model),
),
),
],
@ -130,7 +135,7 @@ class EditRequestHeadersState extends ConsumerState<EditRequestHeaders> {
padding: const EdgeInsets.only(bottom: 30),
child: ElevatedButton.icon(
onPressed: () {
rows.add(const KVRow("", ""));
rows.add(const NameValueModel(name: "", value: ""));
_onFieldChange(activeId!);
},
icon: const Icon(Icons.add),

View File

@ -16,7 +16,7 @@ class EditRequestURLParams extends ConsumerStatefulWidget {
}
class EditRequestURLParamsState extends ConsumerState<EditRequestURLParams> {
late List<KVRow> rows;
late List<NameValueModel> rows;
final random = Random.secure();
late int seed;
@ -38,9 +38,11 @@ class EditRequestURLParamsState extends ConsumerState<EditRequestURLParams> {
final length = ref.watch(activeRequestModelProvider
.select((value) => value?.requestParams?.length));
var rP = ref.read(activeRequestModelProvider)?.requestParams;
rows = (rP == null || rP.isEmpty) ? [const KVRow("", "")] : rP;
rows = (rP == null || rP.isEmpty)
? [const NameValueModel(name: "", value: "")]
: rP;
DaviModel<KVRow> model = DaviModel<KVRow>(
DaviModel<NameValueModel> model = DaviModel<NameValueModel>(
rows: rows,
columns: [
DaviColumn(
@ -50,10 +52,10 @@ class EditRequestURLParamsState extends ConsumerState<EditRequestURLParams> {
int idx = row.index;
return CellField(
keyId: "$activeId-$idx-params-k-$seed",
initialValue: rows[idx].k,
initialValue: rows[idx].name,
hintText: "Add URL Parameter",
onChanged: (value) {
rows[idx] = rows[idx].copyWith(k: value);
rows[idx] = rows[idx].copyWith(name: value);
_onFieldChange(activeId!);
},
colorScheme: Theme.of(context).colorScheme,
@ -77,10 +79,10 @@ class EditRequestURLParamsState extends ConsumerState<EditRequestURLParams> {
int idx = row.index;
return CellField(
keyId: "$activeId-$idx-params-v-$seed",
initialValue: rows[idx].v,
initialValue: rows[idx].value,
hintText: "Add Value",
onChanged: (value) {
rows[idx] = rows[idx].copyWith(v: value);
rows[idx] = rows[idx].copyWith(value: value);
_onFieldChange(activeId!);
},
colorScheme: Theme.of(context).colorScheme,
@ -97,6 +99,9 @@ class EditRequestURLParamsState extends ConsumerState<EditRequestURLParams> {
? kIconRemoveDark
: kIconRemoveLight,
onTap: () {
if (rows.length == 1) {
return;
}
rows.removeAt(row.index);
seed = random.nextInt(kRandMax);
_onFieldChange(activeId!);
@ -119,7 +124,7 @@ class EditRequestURLParamsState extends ConsumerState<EditRequestURLParams> {
Expanded(
child: DaviTheme(
data: kTableThemeData,
child: Davi<KVRow>(model),
child: Davi<NameValueModel>(model),
),
),
],
@ -131,7 +136,7 @@ class EditRequestURLParamsState extends ConsumerState<EditRequestURLParams> {
padding: const EdgeInsets.only(bottom: 30),
child: ElevatedButton.icon(
onPressed: () {
rows.add(const KVRow("", ""));
rows.add(const NameValueModel(name: "", value: ""));
_onFieldChange(activeId!);
},
icon: const Icon(Icons.add),

View File

@ -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(

View File

@ -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(

View File

@ -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(),

View File

@ -1,7 +1,8 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:apidash/providers/providers.dart';
import 'package:apidash/widgets/widgets.dart';
import '../providers/providers.dart';
import '../widgets/widgets.dart';
import '../utils/utils.dart';
import 'package:apidash/consts.dart';
class SettingsPage extends ConsumerStatefulWidget {
@ -73,6 +74,24 @@ class _SettingsPageState extends ConsumerState<SettingsPage> {
.update(saveResponses: value);
},
),
ListTile(
contentPadding: EdgeInsets.zero,
hoverColor: kColorTransparent,
title: const Text('Export Collection'),
subtitle: const Text('Export your collection to a JSON file'),
trailing: FilledButton(
onPressed: () async {
var data = ref
.read(collectionStateNotifierProvider.notifier)
.exportData();
var pth = await getFileDownloadpath(null, "json");
if (pth != null) {
await saveFile(pth, jsonMapToBytes(data));
}
},
child: const Text("Export Data"),
),
),
ListTile(
contentPadding: EdgeInsets.zero,
hoverColor: kColorTransparent,

View File

@ -7,66 +7,59 @@ import 'package:apidash/models/models.dart';
import 'package:apidash/consts.dart';
Future<(http.Response?, Duration?, String?)> request(
RequestModel requestModel,
{String defaultUriScheme = kDefaultUriScheme}
) async {
(Uri?, String?) uriRec = getValidRequestUri(requestModel.url,
requestModel.requestParams,
defaultUriScheme: defaultUriScheme);
if(uriRec.$0 != null){
Uri requestUrl = uriRec.$0!;
RequestModel requestModel, {
String defaultUriScheme = kDefaultUriScheme,
}) async {
(Uri?, String?) uriRec = getValidRequestUri(
requestModel.url,
requestModel.requestParams,
defaultUriScheme: defaultUriScheme,
);
if (uriRec.$1 != null) {
Uri requestUrl = uriRec.$1!;
Map<String, String> headers = rowsToMap(requestModel.requestHeaders) ?? {};
http.Response response;
String? body;
try {
try {
var requestBody = requestModel.requestBody;
if(kMethodsWithBody.contains(requestModel.method) && requestBody != null){
if (kMethodsWithBody.contains(requestModel.method) &&
requestBody != null) {
var contentLength = utf8.encode(requestBody).length;
if (contentLength > 0){
if (contentLength > 0) {
body = requestBody;
headers[HttpHeaders.contentLengthHeader] = contentLength.toString();
headers[HttpHeaders.contentTypeHeader] = kContentTypeMap[requestModel.requestBodyContentType] ?? "";
headers[HttpHeaders.contentTypeHeader] =
kContentTypeMap[requestModel.requestBodyContentType] ?? "";
}
}
Stopwatch stopwatch = Stopwatch()..start();
switch(requestModel.method){
switch (requestModel.method) {
case HTTPVerb.get:
response = await http.get(requestUrl,
headers: headers);
response = await http.get(requestUrl, headers: headers);
break;
case HTTPVerb.head:
response = await http.head(requestUrl,
headers: headers);
response = await http.head(requestUrl, headers: headers);
break;
case HTTPVerb.post:
response = await http.post(requestUrl,
headers: headers,
body: body);
response = await http.post(requestUrl, headers: headers, body: body);
break;
case HTTPVerb.put:
response = await http.put(requestUrl,
headers: headers,
body: body);
response = await http.put(requestUrl, headers: headers, body: body);
break;
case HTTPVerb.patch:
response = await http.patch(requestUrl,
headers: headers,
body: body);
response = await http.patch(requestUrl, headers: headers, body: body);
break;
case HTTPVerb.delete:
response = await http.delete(requestUrl,
headers: headers,
body: body);
response =
await http.delete(requestUrl, headers: headers, body: body);
break;
}
stopwatch.stop();
stopwatch.stop();
return (response, stopwatch.elapsed, null);
}
catch (e) {
} catch (e) {
return (null, null, e.toString());
}
}
else {
return (null, null, uriRec.$1);
} else {
return (null, null, uriRec.$2);
}
}

View File

@ -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;
}
}

View File

@ -3,7 +3,7 @@ import 'dart:io';
import 'package:collection/collection.dart' show mergeMaps;
import 'package:http_parser/http_parser.dart';
import 'package:xml/xml.dart';
import 'package:apidash/models/models.dart' show KVRow;
import '../models/models.dart';
import 'convert_utils.dart' show rowsToMap;
import '../consts.dart';
@ -44,8 +44,8 @@ MediaType? getMediaTypeFromHeaders(Map? headers) {
}
(String?, bool) getUriScheme(Uri uri) {
if(uri.hasScheme){
if(kSupportedUriSchemes.contains(uri.scheme)){
if (uri.hasScheme) {
if (kSupportedUriSchemes.contains(uri.scheme)) {
return (uri.scheme, true);
}
return (uri.scheme, false);
@ -54,37 +54,34 @@ MediaType? getMediaTypeFromHeaders(Map? headers) {
}
(Uri?, String?) getValidRequestUri(
String? url,
List<KVRow>? requestParams,
{String defaultUriScheme = kDefaultUriScheme}
) {
String? url, List<NameValueModel>? requestParams,
{String defaultUriScheme = kDefaultUriScheme}) {
url = url?.trim();
if(url == null || url == ""){
if (url == null || url == "") {
return (null, "URL is missing!");
}
Uri? uri = Uri.tryParse(url);
if(uri == null){
Uri? uri = Uri.tryParse(url);
if (uri == null) {
return (null, "Check URL (malformed)");
}
(String?, bool) urlScheme = getUriScheme(uri);
if(urlScheme.$0 != null){
if (!urlScheme.$1){
return (null, "Unsupported URL Scheme (${urlScheme.$0})");
if (urlScheme.$1 != null) {
if (!urlScheme.$2) {
return (null, "Unsupported URL Scheme (${urlScheme.$1})");
}
}
else {
} else {
url = "$defaultUriScheme://$url";
}
uri = Uri.parse(url);
if (uri.hasFragment){
uri = Uri.parse(url);
if (uri.hasFragment) {
uri = uri.removeFragment();
}
Map<String, String>? queryParams = rowsToMap(requestParams);
if(queryParams != null){
if(uri.hasQuery){
if (queryParams != null) {
if (uri.hasQuery) {
Map<String, String> urlQueryParams = uri.queryParameters;
queryParams = mergeMaps(urlQueryParams, queryParams);
}
@ -93,48 +90,58 @@ MediaType? getMediaTypeFromHeaders(Map? headers) {
return (uri, null);
}
(List<ResponseBodyView>, String?) getResponseBodyViewOptions(MediaType? mediaType){
if(mediaType != null){
(List<ResponseBodyView>, String?) getResponseBodyViewOptions(
MediaType? mediaType) {
if (mediaType != null) {
var type = mediaType.type;
var subtype = mediaType.subtype;
if(kResponseBodyViewOptions.containsKey(type)){
if (kResponseBodyViewOptions[type]!.containsKey(subtype)){
return (kResponseBodyViewOptions[type]![subtype]!, kCodeHighlighterMap[subtype] ?? subtype);
if (kResponseBodyViewOptions.containsKey(type)) {
if (kResponseBodyViewOptions[type]!.containsKey(subtype)) {
return (
kResponseBodyViewOptions[type]![subtype]!,
kCodeHighlighterMap[subtype] ?? subtype
);
}
if(subtype.contains(kSubTypeJson)){
if (subtype.contains(kSubTypeJson)) {
subtype = kSubTypeJson;
}
if(subtype.contains(kSubTypeXml)){
if (subtype.contains(kSubTypeXml)) {
subtype = kSubTypeXml;
}
if (kResponseBodyViewOptions[type]!.containsKey(subtype)){
return (kResponseBodyViewOptions[type]![subtype]!, kCodeHighlighterMap[subtype] ?? subtype);
if (kResponseBodyViewOptions[type]!.containsKey(subtype)) {
return (
kResponseBodyViewOptions[type]![subtype]!,
kCodeHighlighterMap[subtype] ?? subtype
);
}
return (kResponseBodyViewOptions[type]![kSubTypeDefaultViewOptions]!, subtype);
return (
kResponseBodyViewOptions[type]![kSubTypeDefaultViewOptions]!,
subtype
);
}
}
return (kNoBodyViewOptions, null);
}
String? formatBody(String? body, MediaType? mediaType){
if(mediaType != null && body != null){
String? formatBody(String? body, MediaType? mediaType) {
if (mediaType != null && body != null) {
var subtype = mediaType.subtype;
try {
if(subtype.contains(kSubTypeJson)){
if (subtype.contains(kSubTypeJson)) {
final tmp = jsonDecode(body);
String result = kEncoder.convert(tmp);
return result;
}
if(subtype.contains(kSubTypeXml)){
if (subtype.contains(kSubTypeXml)) {
final document = XmlDocument.parse(body);
String result = document.toXmlString(pretty: true, indent: ' ');
return result;
}
if(subtype == kSubTypeHtml){
if (subtype == kSubTypeHtml) {
var len = body.length;
var lines = kSplitter.convert(body);
var numOfLines = lines.length;
if(numOfLines !=0 && len/numOfLines <= kCodeCharsPerLineLimit){
if (numOfLines != 0 && len / numOfLines <= kCodeCharsPerLineLimit) {
return body;
}
}
@ -143,4 +150,4 @@ String? formatBody(String? body, MediaType? mediaType){
}
}
return null;
}
}

View File

@ -2,3 +2,4 @@ export 'ui_utils.dart';
export 'convert_utils.dart';
export 'http_utils.dart';
export 'file_utils.dart';
export 'window_utils.dart';

View File

@ -0,0 +1,18 @@
bool showButtonLabelsInBodySuccess(int options, double maxWidth) {
switch (options) {
case 0:
return true;
case 1:
return (maxWidth < 300) ? false : true;
case 2:
return (maxWidth < 400) ? false : true;
case 3:
return (maxWidth < 500) ? false : true;
default:
return false;
}
}
bool showButtonLabelsInViewCodePane(double maxWidth) {
return (maxWidth < 400) ? false : true;
}

View File

@ -104,12 +104,14 @@ class SaveInDownloadsButton extends StatefulWidget {
super.key,
this.content,
this.mimeType,
this.ext,
this.name,
this.showLabel = true,
});
final Uint8List? content;
final String? mimeType;
final String? ext;
final String? name;
final bool showLabel;
@ -129,10 +131,9 @@ class _SaveInDownloadsButtonState extends State<SaveInDownloadsButton> {
onPressed: (widget.content != null)
? () async {
var message = "";
var ext = getFileExtension(widget.mimeType);
var path = await getFileDownloadpath(
widget.name,
ext,
widget.ext ?? getFileExtension(widget.mimeType),
);
if (path != null) {
try {

View File

@ -6,7 +6,7 @@ import 'error_message.dart';
(String, bool) sanitize(String input) {
bool limitedLines = false;
int tabSize = 4;
var lines = kSplitter.convert(input);
var lines = kSplitter.convert(input);
if (lines.length > kCodePreviewLinesLimit) {
lines = lines.sublist(0, kCodePreviewLinesLimit);
limitedLines = true;
@ -67,7 +67,12 @@ class _CodePreviewerState extends State<CodePreviewer> {
textStyle = textStyle.merge(widget.textStyle);
}
processed = sanitize(widget.code);
spans = asyncGenerateSpans(processed.$0, widget.language, widget.theme, processed.$1);
spans = asyncGenerateSpans(
processed.$1,
widget.language,
widget.theme,
processed.$2,
);
}
@override
@ -131,12 +136,14 @@ class _CodePreviewerState extends State<CodePreviewer> {
}
}
Future<List<TextSpan>> asyncGenerateSpans(
String code, String? language, Map<String, TextStyle> theme, bool limitedLines) async {
Future<List<TextSpan>> asyncGenerateSpans(String code, String? language,
Map<String, TextStyle> theme, bool limitedLines) async {
var parsed = highlight.parse(code, language: language);
var spans = convert(parsed.nodes!, theme);
if(limitedLines) {
spans.add(const TextSpan(text: "\n... more.\nPreview ends here ($kCodePreviewLinesLimit lines).\nYou can check Raw for full result."));
if (limitedLines) {
spans.add(const TextSpan(
text:
"\n... more.\nPreview ends here ($kCodePreviewLinesLimit lines).\nYou can check Raw for full result."));
}
return spans;
}

View File

@ -135,48 +135,55 @@ class _ViewCodePaneState extends State<ViewCodePane> {
borderRadius: kBorderRadius8,
);
return Padding(
padding: kP10,
child: Column(
children: [
SizedBox(
height: kHeaderHeight,
child: Row(
children: [
Expanded(
child: Text(
"Code",
style: Theme.of(context).textTheme.titleMedium,
return LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
var showLabel = showButtonLabelsInViewCodePane(
constraints.maxWidth,
);
return Padding(
padding: kP10,
child: Column(
children: [
SizedBox(
height: kHeaderHeight,
child: Row(
children: [
Expanded(
child: DropdownButtonCodegenLanguage(
codegenLanguage: widget.codegenLanguage,
onChanged: widget.onChangedCodegenLanguage,
),
),
CopyButton(
toCopy: widget.code,
showLabel: showLabel,
),
SaveInDownloadsButton(
content: stringToBytes(widget.code),
ext: widget.codegenLanguage.ext,
showLabel: showLabel,
)
],
),
),
kVSpacer10,
Expanded(
child: Container(
width: double.maxFinite,
padding: kP8,
decoration: textContainerdecoration,
child: CodeGenPreviewer(
code: widget.code,
theme: codeTheme,
language: widget.codegenLanguage.codeHighlightLang,
textStyle: kCodeStyle,
),
),
DropdownButtonCodegenLanguage(
codegenLanguage: widget.codegenLanguage,
onChanged: widget.onChangedCodegenLanguage,
),
CopyButton(toCopy: widget.code),
SaveInDownloadsButton(
content: stringToBytes(widget.code),
mimeType: "application/vnd.dart",
)
],
),
),
kVSpacer10,
Expanded(
child: Container(
width: double.maxFinite,
padding: kP8,
decoration: textContainerdecoration,
child: CodeGenPreviewer(
code: widget.code,
theme: codeTheme,
language: 'dart',
textStyle: kCodeStyle,
),
),
],
),
],
),
);
},
);
}
}

View File

@ -339,8 +339,8 @@ class _ResponseBodyState extends State<ResponseBody> {
}
var responseBodyView = getResponseBodyViewOptions(mediaType);
var options = responseBodyView.$0;
var highlightLanguage = responseBodyView.$1;
var options = responseBodyView.$1;
var highlightLanguage = responseBodyView.$2;
if (formattedBody == null) {
options = [...options];
@ -400,18 +400,10 @@ class _BodySuccessState extends State<BodySuccess> {
return LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
var showLabel = false;
switch (widget.options.length) {
case 1:
showLabel = (constraints.maxWidth < 300) ? false : true;
break;
case 2:
showLabel = (constraints.maxWidth < 400) ? false : true;
break;
case 3:
showLabel = (constraints.maxWidth < 500) ? false : true;
break;
}
var showLabel = showButtonLabelsInBodySuccess(
widget.options.length,
constraints.maxWidth,
);
return Padding(
padding: kP10,
child: Column(

View File

@ -76,8 +76,8 @@ class EqualSplitView extends StatefulWidget {
class _EqualSplitViewState extends State<EqualSplitView> {
final MultiSplitViewController _controller = MultiSplitViewController(
areas: [
Area(minimalSize: 300),
Area(minimalSize: 300),
Area(minimalSize: kMinRequestEditorDetailsCardPaneSize),
Area(minimalSize: kMinRequestEditorDetailsCardPaneSize),
],
);

File diff suppressed because it is too large Load Diff

View File

@ -4,46 +4,51 @@ publish_to: 'none'
version: 0.2.0+2
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
url_launcher: ^6.1.12
flutter_riverpod: ^2.3.7
uuid: ^3.0.7
davi: ^3.2.0
http: ^0.13.5
davi: ^3.4.1
http: ^1.1.0
http_parser: ^4.0.2
collection: ^1.17.0
google_fonts: ^4.0.3
collection: ^1.17.2
google_fonts: ^5.1.0
highlighter: ^0.1.1
xml: ^6.2.2
jinja: ^0.4.2
xml: ^6.3.0
jinja: ^0.5.0
window_size:
git:
url: https://github.com/google/flutter-desktop-embedding.git
path: plugins/window_size
hive_flutter: ^1.1.0
lottie: ^2.3.2
lottie: ^2.6.0
mime_dart: ^3.0.0
path_provider: ^2.0.14
window_manager: ^0.3.2
path: ^1.8.2
flutter_markdown: ^0.6.14
markdown: ^7.1.0
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
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^2.0.0
flutter_launcher_icons: ^0.12.0
test: ^1.22.0
flutter_lints: ^2.0.2
flutter_launcher_icons: ^0.13.1
test: ^1.24.3
build_runner: ^2.4.6
freezed: ^2.4.1
json_serializable: ^6.7.1
flutter:
uses-material-design: true

View File

@ -0,0 +1,505 @@
import 'package:apidash/codegen/dart/pkg_http.dart';
import 'package:test/test.dart';
import '../request_models.dart';
void main() {
final dartHttpCodeGen = DartHttpCodeGen();
group('GET Request', () {
test('GET 1', () {
const expectedCode = r"""import 'package:http/http.dart' as http;
void main() async {
var uri = Uri.parse('https://api.foss42.com');
final response = await http.get(uri);
int statusCode = response.statusCode;
if (statusCode >= 200 && statusCode < 300) {
print('Status Code: $statusCode');
print('Response Body: ${response.body}');
}
else{
print('Error Status Code: $statusCode');
print('Error Response Body: ${response.body}');
}
}
""";
expect(dartHttpCodeGen.getCode(requestModelGet1, "https"), expectedCode);
});
test('GET 2', () {
const expectedCode = r"""import 'package:http/http.dart' as http;
void main() async {
var uri = Uri.parse('https://api.foss42.com/country/data');
var queryParams = {
"code": "US"
};
uri = uri.replace(queryParameters: queryParams);
final response = await http.get(uri);
int statusCode = response.statusCode;
if (statusCode >= 200 && statusCode < 300) {
print('Status Code: $statusCode');
print('Response Body: ${response.body}');
}
else{
print('Error Status Code: $statusCode');
print('Error Response Body: ${response.body}');
}
}
""";
expect(dartHttpCodeGen.getCode(requestModelGet2, "https"), expectedCode);
});
test('GET 3', () {
const expectedCode = r"""import 'package:http/http.dart' as http;
void main() async {
var uri = Uri.parse('https://api.foss42.com/country/data?code=US');
var queryParams = {
"code": "IND"
};
var urlQueryParams = Map<String,String>.from(uri.queryParameters);
urlQueryParams.addAll(queryParams);
uri = uri.replace(queryParameters: urlQueryParams);
final response = await http.get(uri);
int statusCode = response.statusCode;
if (statusCode >= 200 && statusCode < 300) {
print('Status Code: $statusCode');
print('Response Body: ${response.body}');
}
else{
print('Error Status Code: $statusCode');
print('Error Response Body: ${response.body}');
}
}
""";
expect(dartHttpCodeGen.getCode(requestModelGet3, "https"), expectedCode);
});
test('GET 4', () {
const expectedCode = r"""import 'package:http/http.dart' as http;
void main() async {
var uri = Uri.parse('https://api.foss42.com/humanize/social');
var queryParams = {
"num": "8700000",
"digits": "3",
"system": "SS",
"add_space": "true",
"trailing_zeros": "true"
};
uri = uri.replace(queryParameters: queryParams);
final response = await http.get(uri);
int statusCode = response.statusCode;
if (statusCode >= 200 && statusCode < 300) {
print('Status Code: $statusCode');
print('Response Body: ${response.body}');
}
else{
print('Error Status Code: $statusCode');
print('Error Response Body: ${response.body}');
}
}
""";
expect(dartHttpCodeGen.getCode(requestModelGet4, "https"), expectedCode);
});
test('GET 5', () {
const expectedCode = r"""import 'package:http/http.dart' as http;
void main() async {
var uri = Uri.parse('https://api.github.com/repos/foss42/apidash');
var headers = {
"Authorization": "Bearer XYZ"
};
final response = await http.get(uri,
headers: headers);
int statusCode = response.statusCode;
if (statusCode >= 200 && statusCode < 300) {
print('Status Code: $statusCode');
print('Response Body: ${response.body}');
}
else{
print('Error Status Code: $statusCode');
print('Error Response Body: ${response.body}');
}
}
""";
expect(dartHttpCodeGen.getCode(requestModelGet5, "https"), expectedCode);
});
test('GET 6', () {
const expectedCode = r"""import 'package:http/http.dart' as http;
void main() async {
var uri = Uri.parse('https://api.github.com/repos/foss42/apidash');
var queryParams = {
"raw": "true"
};
uri = uri.replace(queryParameters: queryParams);
var headers = {
"Authorization": "Bearer XYZ"
};
final response = await http.get(uri,
headers: headers);
int statusCode = response.statusCode;
if (statusCode >= 200 && statusCode < 300) {
print('Status Code: $statusCode');
print('Response Body: ${response.body}');
}
else{
print('Error Status Code: $statusCode');
print('Error Response Body: ${response.body}');
}
}
""";
expect(dartHttpCodeGen.getCode(requestModelGet6, "https"), expectedCode);
});
test('GET 7', () {
const expectedCode = r"""import 'package:http/http.dart' as http;
void main() async {
var uri = Uri.parse('https://api.foss42.com');
final response = await http.get(uri);
int statusCode = response.statusCode;
if (statusCode >= 200 && statusCode < 300) {
print('Status Code: $statusCode');
print('Response Body: ${response.body}');
}
else{
print('Error Status Code: $statusCode');
print('Error Response Body: ${response.body}');
}
}
""";
expect(dartHttpCodeGen.getCode(requestModelGet7, "https"), expectedCode);
});
test('GET 8', () {
const expectedCode = r"""import 'package:http/http.dart' as http;
void main() async {
var uri = Uri.parse('https://api.github.com/repos/foss42/apidash');
var queryParams = {
"raw": "true"
};
uri = uri.replace(queryParameters: queryParams);
var headers = {
"Authorization": "Bearer XYZ"
};
final response = await http.get(uri,
headers: headers);
int statusCode = response.statusCode;
if (statusCode >= 200 && statusCode < 300) {
print('Status Code: $statusCode');
print('Response Body: ${response.body}');
}
else{
print('Error Status Code: $statusCode');
print('Error Response Body: ${response.body}');
}
}
""";
expect(dartHttpCodeGen.getCode(requestModelGet8, "https"), expectedCode);
});
});
group('HEAD Request', () {
test('HEAD 1', () {
const expectedCode = r"""import 'package:http/http.dart' as http;
void main() async {
var uri = Uri.parse('https://api.foss42.com');
final response = await http.head(uri);
int statusCode = response.statusCode;
if (statusCode >= 200 && statusCode < 300) {
print('Status Code: $statusCode');
print('Response Body: ${response.body}');
}
else{
print('Error Status Code: $statusCode');
print('Error Response Body: ${response.body}');
}
}
""";
expect(dartHttpCodeGen.getCode(requestModelHead1, "https"), expectedCode);
});
test('HEAD 2', () {
const expectedCode = r"""import 'package:http/http.dart' as http;
void main() async {
var uri = Uri.parse('http://api.foss42.com');
final response = await http.head(uri);
int statusCode = response.statusCode;
if (statusCode >= 200 && statusCode < 300) {
print('Status Code: $statusCode');
print('Response Body: ${response.body}');
}
else{
print('Error Status Code: $statusCode');
print('Error Response Body: ${response.body}');
}
}
""";
expect(dartHttpCodeGen.getCode(requestModelHead2, "http"), expectedCode);
});
});
group('POST Request', () {
test('POST 1', () {
const expectedCode = r"""import 'package:http/http.dart' as http;
void main() async {
var uri = Uri.parse('https://api.foss42.com/case/lower');
String body = r'''{
"text": "I LOVE Flutter"
}''';
var headers = {
"content-type": "text/plain"
};
final response = await http.post(uri,
headers: headers,
body: body);
int statusCode = response.statusCode;
if (statusCode >= 200 && statusCode < 300) {
print('Status Code: $statusCode');
print('Response Body: ${response.body}');
}
else{
print('Error Status Code: $statusCode');
print('Error Response Body: ${response.body}');
}
}
""";
expect(dartHttpCodeGen.getCode(requestModelPost1, "https"), expectedCode);
});
test('POST 2', () {
const expectedCode = r"""import 'package:http/http.dart' as http;
void main() async {
var uri = Uri.parse('https://api.foss42.com/case/lower');
String body = r'''{
"text": "I LOVE Flutter"
}''';
var headers = {
"content-type": "application/json"
};
final response = await http.post(uri,
headers: headers,
body: body);
int statusCode = response.statusCode;
if (statusCode >= 200 && statusCode < 300) {
print('Status Code: $statusCode');
print('Response Body: ${response.body}');
}
else{
print('Error Status Code: $statusCode');
print('Error Response Body: ${response.body}');
}
}
""";
expect(dartHttpCodeGen.getCode(requestModelPost2, "https"), expectedCode);
});
test('POST 3', () {
const expectedCode = r"""import 'package:http/http.dart' as http;
void main() async {
var uri = Uri.parse('https://api.foss42.com/case/lower');
String body = r'''{
"text": "I LOVE Flutter"
}''';
var headers = {
"Authorization": "Bearer XYZ",
"content-type": "application/json"
};
final response = await http.post(uri,
headers: headers,
body: body);
int statusCode = response.statusCode;
if (statusCode >= 200 && statusCode < 300) {
print('Status Code: $statusCode');
print('Response Body: ${response.body}');
}
else{
print('Error Status Code: $statusCode');
print('Error Response Body: ${response.body}');
}
}
""";
expect(dartHttpCodeGen.getCode(requestModelPost3, "https"), expectedCode);
});
});
group('PUT Request', () {
test('PUT 1', () {
const expectedCode = r"""import 'package:http/http.dart' as http;
void main() async {
var uri = Uri.parse('https://reqres.in/api/users/2');
String body = r'''{
"name": "morpheus",
"job": "zion resident"
}''';
var headers = {
"content-type": "application/json"
};
final response = await http.put(uri,
headers: headers,
body: body);
int statusCode = response.statusCode;
if (statusCode >= 200 && statusCode < 300) {
print('Status Code: $statusCode');
print('Response Body: ${response.body}');
}
else{
print('Error Status Code: $statusCode');
print('Error Response Body: ${response.body}');
}
}
""";
expect(dartHttpCodeGen.getCode(requestModelPut1, "https"), expectedCode);
});
});
group('PATCH Request', () {
test('PATCH 1', () {
const expectedCode = r"""import 'package:http/http.dart' as http;
void main() async {
var uri = Uri.parse('https://reqres.in/api/users/2');
String body = r'''{
"name": "marfeus",
"job": "accountant"
}''';
var headers = {
"content-type": "application/json"
};
final response = await http.patch(uri,
headers: headers,
body: body);
int statusCode = response.statusCode;
if (statusCode >= 200 && statusCode < 300) {
print('Status Code: $statusCode');
print('Response Body: ${response.body}');
}
else{
print('Error Status Code: $statusCode');
print('Error Response Body: ${response.body}');
}
}
""";
expect(
dartHttpCodeGen.getCode(requestModelPatch1, "https"), expectedCode);
});
});
group('DELETE Request', () {
test('DELETE 1', () {
const expectedCode = r"""import 'package:http/http.dart' as http;
void main() async {
var uri = Uri.parse('https://reqres.in/api/users/2');
final response = await http.delete(uri);
int statusCode = response.statusCode;
if (statusCode >= 200 && statusCode < 300) {
print('Status Code: $statusCode');
print('Response Body: ${response.body}');
}
else{
print('Error Status Code: $statusCode');
print('Error Response Body: ${response.body}');
}
}
""";
expect(
dartHttpCodeGen.getCode(requestModelDelete1, "https"), expectedCode);
});
test('DELETE 2', () {
const expectedCode = r"""import 'package:http/http.dart' as http;
void main() async {
var uri = Uri.parse('https://reqres.in/api/users/2');
String body = r'''{
"name": "marfeus",
"job": "accountant"
}''';
var headers = {
"content-type": "application/json"
};
final response = await http.delete(uri,
headers: headers,
body: body);
int statusCode = response.statusCode;
if (statusCode >= 200 && statusCode < 300) {
print('Status Code: $statusCode');
print('Response Body: ${response.body}');
}
else{
print('Error Status Code: $statusCode');
print('Error Response Body: ${response.body}');
}
}
""";
expect(
dartHttpCodeGen.getCode(requestModelDelete2, "https"), expectedCode);
});
});
}

View File

@ -1,19 +1,13 @@
import 'package:apidash/codegen/kotlin/pkg_okhttp.dart';
import 'package:apidash/models/models.dart' show KVRow, RequestModel;
import 'package:test/test.dart';
import 'package:apidash/consts.dart';
import '../request_models.dart';
void main() {
group('KotlinOkHttpCodeGen', () {
final kotlinOkHttpCodeGen = KotlinOkHttpCodeGen();
test('getCode returns valid code for GET request', () {
const requestModel = RequestModel(
url: 'https://jsonplaceholder.typicode.com/todos/1',
method: HTTPVerb.get,
id: '',
);
const expectedCode = """import okhttp3.MediaType.Companion.toMediaType
const expectedCode = r"""import okhttp3.MediaType.Companion.toMediaType
import okhttp3.MultipartBody
import okhttp3.OkHttpClient
import okhttp3.Request
@ -24,24 +18,17 @@ import java.util.concurrent.TimeUnit
val client = OkHttpClient()
val request = Request.Builder()
.url("https://jsonplaceholder.typicode.com/todos/1")
.url("https://api.foss42.com")
.build()
val response = client.newCall(request).execute()
println(response.body!!.string())
""";
expect(kotlinOkHttpCodeGen.getCode(requestModel), expectedCode);
expect(kotlinOkHttpCodeGen.getCode(requestModelGet1), expectedCode);
});
test('getCode returns valid code for POST request', () {
const requestModel = RequestModel(
url: 'https://jsonplaceholder.typicode.com/posts',
method: HTTPVerb.post,
requestBody: '{"title": "foo","body": "bar","userId": 1}',
requestBodyContentType: ContentType.json,
id: '1',
);
const expectedCode = """import okhttp3.MediaType.Companion.toMediaType
const expectedCode = r"""import okhttp3.MediaType.Companion.toMediaType
import okhttp3.MultipartBody
import okhttp3.OkHttpClient
import okhttp3.Request
@ -52,27 +39,20 @@ import java.util.concurrent.TimeUnit
val client = OkHttpClient()
val mediaType = "application/json".toMediaType()
val body = "{\"title\": \"foo\",\"body\": \"bar\",\"userId\": 1}".toRequestBody(mediaType)
val body = "{"text": "IS Upper"}".toRequestBody(mediaType)
val request = Request.Builder()
.url("https://jsonplaceholder.typicode.com/posts")
.url("https://api.foss42.com/case/lower")
.post(body)
.build()
val response = client.newCall(request).execute()
println(response.body!!.string())
""";
expect(kotlinOkHttpCodeGen.getCode(requestModel), expectedCode);
expect(kotlinOkHttpCodeGen.getCode(requestModelPost1), expectedCode);
});
test('getCode returns valid code for DELETE request', () {
const requestModel = RequestModel(
url: 'https://jsonplaceholder.typicode.com/posts/1',
method: HTTPVerb.delete,
requestBody: '{"title": "foo","body": "bar","userId": 1}',
requestBodyContentType: ContentType.json,
id: '1',
);
const expectedCode = """import okhttp3.MediaType.Companion.toMediaType
const expectedCode = r"""import okhttp3.MediaType.Companion.toMediaType
import okhttp3.MultipartBody
import okhttp3.OkHttpClient
import okhttp3.Request
@ -83,7 +63,7 @@ import java.util.concurrent.TimeUnit
val client = OkHttpClient()
val mediaType = "application/json".toMediaType()
val body = "{\"title\": \"foo\",\"body\": \"bar\",\"userId\": 1}".toRequestBody(mediaType)
val body = "{"title": "foo","body": "bar","userId": 1}".toRequestBody(mediaType)
val request = Request.Builder()
.url("https://jsonplaceholder.typicode.com/posts/1")
.method("DELETE", body)
@ -92,15 +72,10 @@ val response = client.newCall(request).execute()
println(response.body!!.string())
""";
expect(kotlinOkHttpCodeGen.getCode(requestModel), expectedCode);
expect(kotlinOkHttpCodeGen.getCode(requestModelDelete1), expectedCode);
});
test('getCode returns valid code for HEAD request', () {
const requestModel = RequestModel(
url: 'https://jsonplaceholder.typicode.com/posts/1',
method: HTTPVerb.head,
id: '1',
);
const expectedCode = """import okhttp3.MediaType.Companion.toMediaType
import okhttp3.MultipartBody
import okhttp3.OkHttpClient
@ -119,24 +94,12 @@ val response = client.newCall(request).execute()
println(response.body!!.string())
""";
expect(kotlinOkHttpCodeGen.getCode(requestModel), expectedCode);
expect(kotlinOkHttpCodeGen.getCode(requestModelHead1), expectedCode);
});
test(
'getCode returns valid code for requests with headers and query parameters',
() {
const requestModel = RequestModel(
url: 'https://jsonplaceholder.typicode.com/posts',
method: HTTPVerb.get,
requestParams: [
KVRow('userId', 1),
],
requestHeaders: [
KVRow('Custom-Header-1', 'Value-1'),
KVRow('Custom-Header-2', 'Value-2')
],
id: '1',
);
const expectedCode = """import okhttp3.MediaType.Companion.toMediaType
import okhttp3.MultipartBody
import okhttp3.OkHttpClient
@ -157,7 +120,7 @@ val response = client.newCall(request).execute()
println(response.body!!.string())
""";
expect(kotlinOkHttpCodeGen.getCode(requestModel), expectedCode);
expect(kotlinOkHttpCodeGen.getCode(requestModelGet2), expectedCode);
});
});
}

View File

@ -1,17 +1,17 @@
import 'package:test/test.dart';
import 'package:apidash/models/kvrow_model.dart';
import 'package:apidash/models/name_value_model.dart';
void main() {
KVRow kvRow1 = const KVRow("harry", 23);
const kvRow1 = NameValueModel(name: "harry", value: 23);
String kvRow1Expected = "{harry: 23}";
test('Testing toString()', () {
expect(kvRow1.toString(), kvRow1Expected);
});
KVRow kvRow2Expected = const KVRow("winter", "26");
const kvRow2Expected = NameValueModel(name: "winter", value: "26");
test('Testing copyWith()', () {
expect(kvRow1.copyWith(k: "winter", v: "26"), kvRow2Expected);
expect(kvRow1.copyWith(name: "winter", value: "26"), kvRow2Expected);
});
test('Testing hashcode', () {

View File

@ -55,8 +55,9 @@ void main() {
url: 'api.foss42.com/case/lower',
name: 'foss42 api',
requestHeaders: const [
KVRow('content-length', '18'),
KVRow('content-type', 'application/json; charset=utf-8')
NameValueModel(name: 'content-length', value: '18'),
NameValueModel(
name: 'content-type', value: 'application/json; charset=utf-8')
],
requestBodyContentType: ContentType.json,
requestBody: '''{
@ -71,8 +72,9 @@ void main() {
url: 'api.foss42.com/case/lower',
name: 'foss42 api',
requestHeaders: [
KVRow('content-length', '18'),
KVRow('content-type', 'application/json; charset=utf-8')
NameValueModel(name: 'content-length', value: '18'),
NameValueModel(
name: 'content-type', value: 'application/json; charset=utf-8')
],
requestBodyContentType: ContentType.json,
requestBody: '''{

182
test/request_models.dart Normal file
View File

@ -0,0 +1,182 @@
import 'package:apidash/models/models.dart' show NameValueModel, RequestModel;
import 'package:apidash/consts.dart';
/// Basic GET request model
const requestModelGet1 = RequestModel(
id: 'get1',
url: 'https://api.foss42.com',
method: HTTPVerb.get,
);
/// GET request model with query params
const requestModelGet2 = RequestModel(
id: 'get2',
url: 'https://api.foss42.com/country/data',
method: HTTPVerb.get,
requestParams: [
NameValueModel(name: 'code', value: 'US'),
],
);
/// GET request model with override query params
const requestModelGet3 = RequestModel(
id: 'get3',
url: 'https://api.foss42.com/country/data?code=US',
method: HTTPVerb.get,
requestParams: [
NameValueModel(name: 'code', value: 'IND'),
],
);
/// GET request model with different types of query params
const requestModelGet4 = RequestModel(
id: 'get4',
url: 'https://api.foss42.com/humanize/social',
method: HTTPVerb.get,
requestParams: [
NameValueModel(name: 'num', value: '8700000'),
NameValueModel(name: 'digits', value: '3'),
NameValueModel(name: 'system', value: 'SS'),
NameValueModel(name: 'add_space', value: 'true'),
NameValueModel(name: 'trailing_zeros', value: 'true'),
],
);
/// GET request model with headers
const requestModelGet5 = RequestModel(
id: 'get5',
url: 'https://api.github.com/repos/foss42/apidash',
method: HTTPVerb.get,
requestHeaders: [
NameValueModel(name: 'Authorization', value: 'Bearer XYZ'),
],
);
/// GET request model with headers & query params
const requestModelGet6 = RequestModel(
id: 'get6',
url: 'https://api.github.com/repos/foss42/apidash',
method: HTTPVerb.get,
requestHeaders: [
NameValueModel(name: 'Authorization', value: 'Bearer XYZ'),
],
requestParams: [
NameValueModel(name: 'raw', value: 'true'),
],
);
/// GET request model with body
const requestModelGet7 = RequestModel(
id: 'get7',
url: 'https://api.foss42.com',
method: HTTPVerb.get,
requestBodyContentType: ContentType.text,
requestBody:
'This is a random text which should not be attached with a GET request',
);
/// GET request model with empty header & query param name
const requestModelGet8 = RequestModel(
id: 'get8',
url: 'https://api.github.com/repos/foss42/apidash',
method: HTTPVerb.get,
requestHeaders: [
NameValueModel(name: 'Authorization', value: 'Bearer XYZ'),
NameValueModel(name: '', value: 'Bearer XYZ'),
],
requestParams: [
NameValueModel(name: 'raw', value: 'true'),
NameValueModel(name: '', value: 'true'),
],
);
/// Basic HEAD request model
const requestModelHead1 = RequestModel(
id: 'head1',
url: 'https://api.foss42.com',
method: HTTPVerb.head,
);
/// Without URI Scheme (pass default as http)
const requestModelHead2 = RequestModel(
id: 'head2',
url: 'api.foss42.com',
method: HTTPVerb.head,
);
/// Basic POST request model (txt body)
const requestModelPost1 = RequestModel(
id: 'post1',
url: 'https://api.foss42.com/case/lower',
method: HTTPVerb.post,
requestBody: r"""{
"text": "I LOVE Flutter"
}""",
requestBodyContentType: ContentType.text);
/// POST request model with JSON body
const requestModelPost2 = RequestModel(
id: 'post2',
url: 'https://api.foss42.com/case/lower',
method: HTTPVerb.post,
requestBody: r"""{
"text": "I LOVE Flutter"
}""",
);
/// POST request model with headers
const requestModelPost3 = RequestModel(
id: 'post3',
url: 'https://api.foss42.com/case/lower',
method: HTTPVerb.post,
requestBody: r"""{
"text": "I LOVE Flutter"
}""",
requestBodyContentType: ContentType.json,
requestHeaders: [
NameValueModel(name: 'Authorization', value: 'Bearer XYZ'),
],
);
/// PUT request model
const requestModelPut1 = RequestModel(
id: 'put1',
url: 'https://reqres.in/api/users/2',
method: HTTPVerb.put,
requestBody: r"""{
"name": "morpheus",
"job": "zion resident"
}""",
requestBodyContentType: ContentType.json,
);
/// PATCH request model
const requestModelPatch1 = RequestModel(
id: 'patch1',
url: 'https://reqres.in/api/users/2',
method: HTTPVerb.patch,
requestBody: r"""{
"name": "marfeus",
"job": "accountant"
}""",
requestBodyContentType: ContentType.json,
);
/// Basic DELETE request model
const requestModelDelete1 = RequestModel(
id: 'delete1',
url: 'https://reqres.in/api/users/2',
method: HTTPVerb.delete,
);
/// Basic DELETE with body
const requestModelDelete2 = RequestModel(
id: 'delete1',
url: 'https://reqres.in/api/users/2',
method: HTTPVerb.delete,
requestBody: r"""{
"name": "marfeus",
"job": "accountant"
}""",
requestBodyContentType: ContentType.json,
);

View File

@ -1,15 +1,19 @@
import 'dart:math';
const _chars = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz1234567890';
Random _rnd = Random();
class RandomStringGenerator {
static const _chars =
'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz1234567890';
static Random rnd = Random();
String getRandomString(int length) => String.fromCharCodes(Iterable.generate(
length, (_) => _chars.codeUnitAt(_rnd.nextInt(_chars.length))));
static String getRandomString(int length) =>
String.fromCharCodes(Iterable.generate(
length, (_) => _chars.codeUnitAt(rnd.nextInt(_chars.length))));
String getRandomStringLines(int lines, int length) {
List<String> result = [];
for (var i = 0; i < lines; i++) {
result.add(getRandomString(length));
static String getRandomStringLines(int lines, int length) {
List<String> result = [];
for (var i = 0; i < lines; i++) {
result.add(getRandomString(length));
}
return result.join('\n');
}
return result.join('\n');
}

View File

@ -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);
});

View File

@ -0,0 +1,17 @@
import 'package:test/test.dart';
import 'package:apidash/utils/file_utils.dart';
void main() {
group(
"Testing x function",
() {
/*test('Test case 2', () {
expect(showButtonLabelsInViewCodePane(350), false);
});
test('Test case 3', () {
expect(showButtonLabelsInViewCodePane(450), true);
});*/
},
);
}

View File

@ -1,7 +1,7 @@
import 'package:test/test.dart';
import 'package:http_parser/http_parser.dart';
import 'package:apidash/utils/http_utils.dart';
import 'package:apidash/models/kvrow_model.dart';
import 'package:apidash/models/name_value_model.dart';
import 'package:apidash/consts.dart';
import '../test_utilities.dart';
@ -156,74 +156,74 @@ void main() {
path: 'guides/libraries/library-tour',
fragment: 'numbers');
String uriScheme1Expected = 'https';
expect(getUriScheme(uri1), (uriScheme1Expected,true));
expect(getUriScheme(uri1), (uriScheme1Expected, true));
});
test('Testing getUriScheme for mailto scheme value', () {
Uri uri2 = Uri(scheme: 'mailto');
String uriScheme2Expected = 'mailto';
expect(getUriScheme(uri2), (uriScheme2Expected,false));
expect(getUriScheme(uri2), (uriScheme2Expected, false));
});
test('Testing getUriScheme for empty scheme value', () {
Uri uri3 = Uri(
scheme: '');
expect(getUriScheme(uri3), (null,false));
Uri uri3 = Uri(scheme: '');
expect(getUriScheme(uri3), (null, false));
});
test('Testing getUriScheme for null scheme value', () {
Uri uri4 = Uri(
scheme: null);
expect(getUriScheme(uri4), (null,false));
Uri uri4 = Uri(scheme: null);
expect(getUriScheme(uri4), (null, false));
});
});
group("Testing getValidRequestUri", () {
test('Testing getValidRequestUri for normal values', () {
String url1 = "https://api.foss42.com/country/data";
KVRow kvRow1 = const KVRow("code", "US");
const kvRow1 = NameValueModel(name: "code", value: "US");
Uri uri1Expected = Uri(
scheme: 'https',
host: 'api.foss42.com',
path: 'country/data',
queryParameters: {'code':'US'});
queryParameters: {'code': 'US'});
expect(getValidRequestUri(url1, [kvRow1]), (uri1Expected, null));
});
test('Testing getValidRequestUri for null url value', () {
KVRow kvRow2 = const KVRow("code", "US");
const kvRow2 = NameValueModel(name: "code", value: "US");
expect(getValidRequestUri(null, [kvRow2]), (null, "URL is missing!"));
});
test('Testing getValidRequestUri for empty url value', () {
KVRow kvRow3 = const KVRow("", "");
const kvRow3 = NameValueModel(name: "", value: "");
expect(getValidRequestUri("", [kvRow3]), (null, "URL is missing!"));
});
test('Testing getValidRequestUri when https is not provided in url', () {
String url4 = "api.foss42.com/country/data";
KVRow kvRow4 = const KVRow("code", "US");
const kvRow4 = NameValueModel(name: "code", value: "US");
Uri uri4Expected = Uri(
scheme: 'https',
host: 'api.foss42.com',
path: 'country/data',
queryParameters: {'code':'US'});
queryParameters: {'code': 'US'});
expect(getValidRequestUri(url4, [kvRow4]), (uri4Expected, null));
});
test('Testing getValidRequestUri when url has fragment', () {
String url5 = "https://dart.dev/guides/libraries/library-tour#numbers";
Uri uri5Expected = Uri(
scheme: 'https',
host: 'dart.dev',
path: '/guides/libraries/library-tour');
scheme: 'https',
host: 'dart.dev',
path: '/guides/libraries/library-tour');
expect(getValidRequestUri(url5, null), (uri5Expected, null));
});
test('Testing getValidRequestUri when uri scheme is not supported', () {
String url5 = "mailto:someone@example.com";
expect(getValidRequestUri(url5, null), (null, "Unsupported URL Scheme (mailto)"));
expect(getValidRequestUri(url5, null),
(null, "Unsupported URL Scheme (mailto)"));
});
test('Testing getValidRequestUri when query params in both url and kvrow', () {
test('Testing getValidRequestUri when query params in both url and kvrow',
() {
String url6 = "api.foss42.com/country/data?code=IND";
KVRow kvRow6 = const KVRow("code", "US");
const kvRow6 = NameValueModel(name: "code", value: "US");
Uri uri6Expected = Uri(
scheme: 'https',
host: 'api.foss42.com',
path: 'country/data',
queryParameters: {'code':'US'});
queryParameters: {'code': 'US'});
expect(getValidRequestUri(url6, [kvRow6]), (uri6Expected, null));
});
test('Testing getValidRequestUri when kvrow is null', () {
@ -232,7 +232,7 @@ void main() {
scheme: 'https',
host: 'api.foss42.com',
path: 'country/data',
queryParameters: {'code':'US'});
queryParameters: {'code': 'US'});
expect(getValidRequestUri(url7, null), (uri7Expected, null));
});
});
@ -241,72 +241,78 @@ void main() {
test('Testing getResponseBodyViewOptions for application/json', () {
MediaType mediaType1 = MediaType("application", "json");
var result1 = getResponseBodyViewOptions(mediaType1);
expect(result1.$0,kCodeRawBodyViewOptions);
expect(result1.$1, "json");
expect(result1.$1, kCodeRawBodyViewOptions);
expect(result1.$2, "json");
});
test('Testing getResponseBodyViewOptions for application/xml', () {
MediaType mediaType2 = MediaType("application", "xml");
var result2 = getResponseBodyViewOptions(mediaType2);
expect(result2.$0, kCodeRawBodyViewOptions);
expect(result2.$1,"xml");
expect(result2.$1, kCodeRawBodyViewOptions);
expect(result2.$2, "xml");
});
test('Testing getResponseBodyViewOptions for message/news a format currently not supported', () {
test(
'Testing getResponseBodyViewOptions for message/news a format currently not supported',
() {
MediaType mediaType3 = MediaType("message", "news");
var result3 = getResponseBodyViewOptions(mediaType3);
expect(result3.$0,kNoBodyViewOptions);
expect(result3.$1,null);
expect(result3.$1, kNoBodyViewOptions);
expect(result3.$2, null);
});
test('Testing getResponseBodyViewOptions for application/calendar+json', () {
test('Testing getResponseBodyViewOptions for application/calendar+json',
() {
MediaType mediaType4 = MediaType("application", "calendar+json");
var result4 = getResponseBodyViewOptions(mediaType4);
expect(result4.$0,kCodeRawBodyViewOptions);
expect(result4.$1, "json");
expect(result4.$1, kCodeRawBodyViewOptions);
expect(result4.$2, "json");
});
test('Testing getResponseBodyViewOptions for image/svg+xml', () {
MediaType mediaType5 = MediaType("image", "svg+xml");
var result5 = getResponseBodyViewOptions(mediaType5);
expect(result5.$0,kCodeRawBodyViewOptions);
expect(result5.$1, "xml");
expect(result5.$1, kCodeRawBodyViewOptions);
expect(result5.$2, "xml");
});
test('Testing getResponseBodyViewOptions for application/xhtml+xml', () {
MediaType mediaType6 = MediaType("application", "xhtml+xml");
var result6 = getResponseBodyViewOptions(mediaType6);
expect(result6.$0,kCodeRawBodyViewOptions);
expect(result6.$1, "xml");
expect(result6.$1, kCodeRawBodyViewOptions);
expect(result6.$2, "xml");
});
test('Testing getResponseBodyViewOptions for application/xml-external-parsed-entity', () {
MediaType mediaType7 = MediaType("application", "xml-external-parsed-entity");
test(
'Testing getResponseBodyViewOptions for application/xml-external-parsed-entity',
() {
MediaType mediaType7 =
MediaType("application", "xml-external-parsed-entity");
var result7 = getResponseBodyViewOptions(mediaType7);
expect(result7.$0,kCodeRawBodyViewOptions);
expect(result7.$1, "xml");
expect(result7.$1, kCodeRawBodyViewOptions);
expect(result7.$2, "xml");
});
test('Testing getResponseBodyViewOptions for text/html', () {
MediaType mediaType8 = MediaType("text", "html");
var result8 = getResponseBodyViewOptions(mediaType8);
expect(result8.$0,kCodeRawBodyViewOptions);
expect(result8.$1, "xml");
expect(result8.$1, kCodeRawBodyViewOptions);
expect(result8.$2, "xml");
});
test('Testing getResponseBodyViewOptions for application/pdf', () {
MediaType mediaType9 = MediaType("application", "pdf");
var result9 = getResponseBodyViewOptions(mediaType9);
expect(result9.$0,kNoBodyViewOptions);
expect(result9.$1, "pdf");
expect(result9.$1, kNoBodyViewOptions);
expect(result9.$2, "pdf");
});
test('Testing getResponseBodyViewOptions for text/calendar', () {
test('Testing getResponseBodyViewOptions for text/calendar', () {
MediaType mediaType10 = MediaType("text", "calendar");
var result10 = getResponseBodyViewOptions(mediaType10);
expect(result10.$0,kRawBodyViewOptions);
expect(result10.$1, "calendar");
expect(result10.$1, kRawBodyViewOptions);
expect(result10.$2, "calendar");
});
});
group("Testing formatBody", () {
test('Testing formatBody for null values', () {
expect(formatBody(null, null),null);
expect(formatBody(null, null), null);
});
test('Testing formatBody for null body values', () {
MediaType mediaType1 = MediaType("application", "xml");
expect(formatBody(null, mediaType1),null);
expect(formatBody(null, mediaType1), null);
});
test('Testing formatBody for null MediaType values', () {
String body1 = '''
@ -314,7 +320,7 @@ void main() {
"text":"The Chosen One";
}
''';
expect(formatBody(body1, null),null);
expect(formatBody(body1, null), null);
});
test('Testing formatBody for json subtype values', () {
String body2 = '''{"data":{"area":9831510.0,"population":331893745}}''';
@ -325,7 +331,7 @@ void main() {
"population": 331893745
}
}''';
expect(formatBody(body2, mediaType2),result2Expected);
expect(formatBody(body2, mediaType2), result2Expected);
});
test('Testing formatBody for xml subtype values', () {
String body3 = '''
@ -347,28 +353,32 @@ void main() {
<calories>650</calories>
</food>
</breakfast_menu>''';
expect(formatBody(body3, mediaType3),result3Expected);
expect(formatBody(body3, mediaType3), result3Expected);
});
group("Testing formatBody for html", () {
MediaType mediaTypeHtml = MediaType("text", "html");
test('Testing formatBody for html subtype values', () {
String body4 = '''<html>
String body4 = '''<html>
<body>
<h1>My First Heading</h1>
<p>My first paragraph.</p>
</body>
</html>''';
expect(formatBody(body4, mediaTypeHtml),body4);
</html>''';
expect(formatBody(body4, mediaTypeHtml), body4);
});
test('Testing formatBody for html subtype values with random values', () {
String body5 =
'''<html>${RandomStringGenerator.getRandomStringLines(100, 10000)}</html>''';
expect(formatBody(body5, mediaTypeHtml), null);
});
test(
'Testing formatBody for html subtype values with random values within limit',
() {
String body6 =
'''<html>${RandomStringGenerator.getRandomStringLines(100, 190)}</html>''';
expect(formatBody(body6, mediaTypeHtml), body6);
});
});
test('Testing formatBody for html subtype values with random values', () {
String body5 = '''<html>${getRandomStringLines(100, 10000)}</html>''';
expect(formatBody(body5, mediaTypeHtml),null);
});
test('Testing formatBody for html subtype values with random values within limit', () {
String body6 = '''<html>${getRandomStringLines(100, 190)}</html>''';
expect(formatBody(body6, mediaTypeHtml),body6);
});
});
});
}

View File

@ -0,0 +1,62 @@
import 'package:test/test.dart';
import 'package:apidash/utils/window_utils.dart';
void main() {
group(
"Testing showButtonLabelsInBodySuccess function",
() {
test('Test case 1 options 0', () {
expect(showButtonLabelsInBodySuccess(0, 300), true);
});
test('Test case 2 options 0', () {
expect(showButtonLabelsInBodySuccess(0, 500), true);
});
test('Test case 1 options 1', () {
expect(showButtonLabelsInBodySuccess(1, 250), false);
});
test('Test case 2 options 1', () {
expect(showButtonLabelsInBodySuccess(1, 350), true);
});
test('Test case 1 options 2', () {
expect(showButtonLabelsInBodySuccess(2, 250), false);
});
test('Test case 2 options 2', () {
expect(showButtonLabelsInBodySuccess(2, 350), false);
});
test('Test case 3 options 2', () {
expect(showButtonLabelsInBodySuccess(2, 450), true);
});
test('Test case 1 options 3', () {
expect(showButtonLabelsInBodySuccess(3, 350), false);
});
test('Test case 2 options 3', () {
expect(showButtonLabelsInBodySuccess(3, 450), false);
});
test('Test case 3 options 3', () {
expect(showButtonLabelsInBodySuccess(3, 550), true);
});
},
);
group(
"Testing showButtonLabelsInViewCodePane function",
() {
test('Test case 2', () {
expect(showButtonLabelsInViewCodePane(350), false);
});
test('Test case 3', () {
expect(showButtonLabelsInViewCodePane(450), true);
});
},
);
}

View File

@ -1,31 +1 @@
// This is a basic Flutter widget test.
//
// To perform an interaction with a widget in your test, use the WidgetTester
// utility in the flutter_test package. For example, you can send tap and scroll
// gestures. You can also use WidgetTester to find child widgets in the widget
// tree, read text, and verify that the values of widget properties are correct.
import 'package:apidash/app.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:apidash/main.dart';
void main() {
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
// Build our app and trigger a frame.
await tester.pumpWidget(const DashApp());
// Verify that our counter starts at 0.
expect(find.text('0'), findsOneWidget);
expect(find.text('1'), findsNothing);
// Tap the '+' icon and trigger a frame.
await tester.tap(find.byIcon(Icons.add));
await tester.pump();
// Verify that our counter has incremented.
expect(find.text('0'), findsNothing);
expect(find.text('1'), findsOneWidget);
});
}
void main() {}

View File

@ -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',

View File

@ -69,7 +69,18 @@ void main() async {
);
await tester.pumpAndSettle();
expect(find.text('Code'), findsOneWidget);
expect(find.byType(DropdownButton<CodegenLanguage>), findsOneWidget);
expect(
(tester.widget(find.byType(DropdownButton<CodegenLanguage>))
as DropdownButton)
.value,
equals(CodegenLanguage.dartHttp));
await tester.tap(find.text('Dart (http)'));
await tester.pump();
await tester.pump(const Duration(seconds: 1));
expect(find.text('Kotlin (OkHttp)'), findsWidgets);
expect(find.textContaining('Error Status Code', findRichText: true),
findsOneWidget);
@ -97,7 +108,7 @@ void main() async {
);
await tester.pumpAndSettle();
expect(find.text('Code'), findsOneWidget);
expect(find.text('Dart (http)'), findsOneWidget);
expect(find.textContaining('Error Status Code', findRichText: true),
findsOneWidget);

View File

@ -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: '''{

View File

@ -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")]),
),
),
),

View File

@ -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",