mirror of
https://github.com/foss42/apidash.git
synced 2025-05-31 14:23:45 +08:00
Merge branch 'main' into add-feat-insomia
This commit is contained in:
19
CHANGELOG.md
19
CHANGELOG.md
@ -1,6 +1,21 @@
|
||||
# API Dash ⚡️ Changelog
|
||||
|
||||
## v0.4.0 [WIP]
|
||||
## v0.5.0 [WIP]
|
||||
|
||||
In this release, we have added the following features:
|
||||
|
||||
1. Create workspace (directory) to persist your entire data locally in the provided file-system location
|
||||
2. Environment varibles
|
||||
3. GraphQL support
|
||||
4. Import requests from cURL & Postman Collection (v2.1)
|
||||
5. History of your requests & response
|
||||
6. Support for Request cancellation
|
||||
7. Disable SSL verification
|
||||
8. Codegen for Swift, hyper, etc.
|
||||
|
||||
A big shout-out to all the contributors 🎉
|
||||
|
||||
## v0.4.0 [iOS Release]
|
||||
|
||||
In this release, we have added the following features:
|
||||
|
||||
@ -15,7 +30,7 @@ In this release, we have added the following features:
|
||||
9. Dart http codegen has been rewritten using dart code builder
|
||||
10. Ability to override request contenttype
|
||||
11. More header suggestions
|
||||
|
||||
12. Mobile platform support
|
||||
|
||||
## v0.3.0 [29-11-2023]
|
||||
|
||||
|
22
README.md
22
README.md
@ -76,11 +76,31 @@ API Dash can be downloaded from the links below:
|
||||
|
||||
## List of Features
|
||||
|
||||
| API Type | Supported |
|
||||
| --- | --- |
|
||||
| HTTP | ✅ |
|
||||
| GraphQL | ✅ |
|
||||
| SSE | https://github.com/foss42/apidash/issues/116 |
|
||||
| WebSocket | https://github.com/foss42/apidash/issues/15 |
|
||||
| MQTT | https://github.com/foss42/apidash/issues/115 |
|
||||
| gRPC | https://github.com/foss42/apidash/issues/14 |
|
||||
|
||||
| Import Collection From | Supported |
|
||||
| --- | --- |
|
||||
| Postman | ✅ |
|
||||
| cURL | ✅ |
|
||||
| Insomnia | https://github.com/foss42/apidash/issues/125 |
|
||||
| OpenAPI | https://github.com/foss42/apidash/issues/121 |
|
||||
| hurl | https://github.com/foss42/apidash/issues/123 |
|
||||
| HAR | https://github.com/foss42/apidash/issues/122 |
|
||||
|
||||
|
||||
**↗️ Create & Customize API Requests**
|
||||
|
||||
- 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.
|
||||
- Create GraphQL requests with `headers` and `query`.
|
||||
|
||||
**💼 Organize Requests in Collections & Folders**
|
||||
|
||||
@ -222,7 +242,7 @@ Here is the complete list of MIME types that are syntax highlighted in API Dash:
|
||||
| `text/javascript` | `.js` | |
|
||||
| `text/markdown` | `.md` | |
|
||||
|
||||
## What's new in v0.3.0?
|
||||
## What's new in v0.5.0?
|
||||
|
||||
Visit [CHANGELOG.md](CHANGELOG.md)
|
||||
|
||||
|
@ -5,14 +5,14 @@
|
||||
- [x] Remaining Code Generators (https://github.com/foss42/apidash/discussions/80)
|
||||
- [x] Environment Variables (https://github.com/foss42/apidash/issues/25)
|
||||
- [x] Integration Testing (https://github.com/foss42/apidash/issues/119)
|
||||
- [ ] Paste not working on iOS and desktop (Right Click -> Paste) (https://github.com/foss42/apidash/issues/490)
|
||||
- [x] Paste not working on iOS and desktop (Right Click -> Paste) (https://github.com/foss42/apidash/issues/490)
|
||||
- [ ] WebSocket support (https://github.com/foss42/apidash/issues/15)
|
||||
- [ ] SSE support (https://github.com/foss42/apidash/issues/116)
|
||||
- [ ] MQTT support (https://github.com/foss42/apidash/issues/115)
|
||||
- [ ] GraphQL support (https://github.com/foss42/apidash/issues/117)
|
||||
- [x] GraphQL support (https://github.com/foss42/apidash/issues/117)
|
||||
- [ ] gRPC support (https://github.com/foss42/apidash/issues/14)
|
||||
- [ ] Figuring out how to build for various Linux packaging formats (https://github.com/foss42/apidash/discussions/240) [Docs]
|
||||
- [ ] Save items in Response headers/body in an Environment variable to be used by other requests (https://github.com/foss42/apidash/issues/465)
|
||||
- [ ] Save items in Response headers/body in an Environment variable to be used by other requests. Something like a post-processing script. (https://github.com/foss42/apidash/issues/465)
|
||||
- [ ] Importers
|
||||
- [ ] OpenAPI (https://github.com/foss42/apidash/issues/121)
|
||||
- [ ] Insomnia (https://github.com/foss42/apidash/issues/125)
|
||||
|
@ -1,8 +1,8 @@
|
||||
## API Dash User Guide
|
||||
# API Dash User Guide
|
||||
|
||||
- [History of Requests](https://github.com/foss42/apidash/blob/main/doc/user_guide/his_user_guide.md)
|
||||
- [Environment Variables Manager](https://github.com/foss42/apidash/blob/main/doc/user_guide/env_user_guide.md)
|
||||
- [How to Disable SSL for Requests](https://github.com/foss42/apidash/blob/main/doc/user_guide/disable_ssl.md)
|
||||
- Import Request for cURL (Doc TODO)
|
||||
- How to use Code Generator (Doc TODO)
|
||||
- Import Request for cURL (Contributions Welcome!)
|
||||
- [How to Run Generated Code for a Programming Language](https://github.com/foss42/apidash/blob/main/doc/user_guide/instructions_to_run_generated_code.md) (Contributions Welcome!)
|
||||
- [API Dash on Mobile](https://github.com/foss42/apidash/blob/main/doc/user_guide/req_user_guide.md)
|
||||
|
150
doc/user_guide/instructions_to_run_generated_code.md
Normal file
150
doc/user_guide/instructions_to_run_generated_code.md
Normal file
@ -0,0 +1,150 @@
|
||||
# How to Run Generated Code for a Programming Language
|
||||
|
||||
Choose your programming language/library from the list provided below to learn more how you can execute them:
|
||||
- [cURL](#curl)
|
||||
- [C (libcurl)](#c-libcurl)
|
||||
- [C# (HttpClient)](#c-httpclient)
|
||||
- [C# (RestSharp)](#c-restsharp)
|
||||
- [Dart (http)](#dart-http)
|
||||
- [Dart (dio)](#dart-dio)
|
||||
- [Go (net/http)](#go-nethttp)
|
||||
- [JavaScript (axios)](#javascript-axios)
|
||||
- [JavaScript (fetch)](#javascript-fetch)
|
||||
- [node.js (JavaScript, axios)](#nodejs-javascript-axios)
|
||||
- [node.js (JavaScript, fetch)](#nodejs-javascript-fetch)
|
||||
- [Java (asynchttpclient)](#java-asynchttpclient)
|
||||
- [Java (HttpClient)](#java-httpclient)
|
||||
- [Java (okhttp3)](#java-okhttp3)
|
||||
- [Java (Unirest)](#java-unirest)
|
||||
- [Julia (HTTP)](#julia-http)
|
||||
- [Kotlin (okhttp3)](#kotlin-okhttp3)
|
||||
- [PHP (curl)](#php-curl)
|
||||
- [PHP (guzzle)](#php-guzzle)
|
||||
- [PHP (HTTPlug)](#php-httplug)
|
||||
- [Python (requests)](#python-requests)
|
||||
- [Python (http.client)](#python-httpclient)
|
||||
- [Ruby (faraday)](#ruby-faraday)
|
||||
- [Ruby (net/http)](#ruby-nethttp)
|
||||
- [Rust (hyper)](#rust-hyper)
|
||||
- [Rust (reqwest)](#rust-reqwest)
|
||||
- [Rust (ureq)](#rust-ureq)
|
||||
- [Rust (Actix Client)](#rust-actix-client)
|
||||
- [Swift](#swift)
|
||||
|
||||
**Please raise a GitHub issue in case any instruction is not clear or if it is not working.**
|
||||
|
||||
## cURL
|
||||
|
||||
TODO
|
||||
|
||||
## C (libcurl)
|
||||
|
||||
TODO
|
||||
|
||||
## C# (HttpClient)
|
||||
|
||||
TODO
|
||||
|
||||
## C# (RestSharp)
|
||||
|
||||
TODO
|
||||
|
||||
## Dart (http)
|
||||
|
||||
TODO
|
||||
|
||||
## Dart (dio)
|
||||
|
||||
TODO
|
||||
|
||||
## Go (net/http)
|
||||
|
||||
TODO
|
||||
|
||||
## JavaScript (axios)
|
||||
|
||||
TODO
|
||||
|
||||
## JavaScript (fetch)
|
||||
|
||||
TODO
|
||||
|
||||
## node.js (JavaScript, axios)
|
||||
|
||||
TODO
|
||||
|
||||
## node.js (JavaScript, fetch)
|
||||
|
||||
TODO
|
||||
|
||||
## Java (asynchttpclient)
|
||||
|
||||
TODO
|
||||
|
||||
## Java (HttpClient)
|
||||
|
||||
TODO
|
||||
|
||||
## Java (okhttp3)
|
||||
|
||||
TODO
|
||||
|
||||
## Java (Unirest)
|
||||
|
||||
TODO
|
||||
|
||||
## Julia (HTTP)
|
||||
|
||||
TODO
|
||||
|
||||
## Kotlin (okhttp3)
|
||||
|
||||
TODO
|
||||
|
||||
## PHP (curl)
|
||||
|
||||
TODO
|
||||
|
||||
## PHP (guzzle)
|
||||
|
||||
TODO
|
||||
|
||||
## PHP (HTTPlug)
|
||||
|
||||
TODO
|
||||
|
||||
## Python (requests)
|
||||
|
||||
TODO
|
||||
|
||||
## Python (http.client)
|
||||
|
||||
TODO
|
||||
|
||||
## Ruby (faraday)
|
||||
|
||||
TODO
|
||||
|
||||
## Ruby (net/http)
|
||||
|
||||
TODO
|
||||
|
||||
## Rust (hyper)
|
||||
|
||||
TODO
|
||||
|
||||
## Rust (reqwest)
|
||||
|
||||
TODO
|
||||
|
||||
## Rust (ureq)
|
||||
|
||||
TODO
|
||||
|
||||
## Rust (Actix Client)
|
||||
|
||||
TODO
|
||||
|
||||
## Swift
|
||||
|
||||
TODO
|
@ -26,7 +26,7 @@ class ApidashTestEnvHelper {
|
||||
Future<void> renameNewEnvironment(String newEnvName) async {
|
||||
Finder envItems = find.byType(EnvironmentItem);
|
||||
Finder newEnvItem = envItems.at(1);
|
||||
expect(find.descendant(of: newEnvItem, matching: find.text("untitled")),
|
||||
expect(find.descendant(of: newEnvItem, matching: find.text(kUntitled)),
|
||||
findsOneWidget);
|
||||
Finder itemCardMenu =
|
||||
find.descendant(of: newEnvItem, matching: find.byType(ItemCardMenu));
|
||||
|
@ -29,7 +29,7 @@ class ApidashTestRequestHelper {
|
||||
Future<void> renameNewRequest(String newReqName) async {
|
||||
Finder reqItems = find.byType(RequestItem);
|
||||
Finder newReqItem = reqItems.at(0);
|
||||
expect(find.descendant(of: newReqItem, matching: find.text("untitled")),
|
||||
expect(find.descendant(of: newReqItem, matching: find.text(kUntitled)),
|
||||
findsOneWidget);
|
||||
Finder itemCardMenu =
|
||||
find.descendant(of: newReqItem, matching: find.byType(ItemCardMenu));
|
||||
|
@ -432,6 +432,7 @@ const kLabelSelect = "Select";
|
||||
const kLabelContinue = "Continue";
|
||||
const kLabelCancel = "Cancel";
|
||||
const kLabelOk = "Ok";
|
||||
const kUntitled = "untitled";
|
||||
// Request Pane
|
||||
const kLabelRequest = "Request";
|
||||
const kLabelHideCode = "Hide Code";
|
||||
@ -439,6 +440,7 @@ const kLabelViewCode = "View Code";
|
||||
const kLabelURLParams = "URL Params";
|
||||
const kLabelHeaders = "Headers";
|
||||
const kLabelBody = "Body";
|
||||
const kLabelQuery = "Query";
|
||||
const kNameCheckbox = "Checkbox";
|
||||
const kNameURLParam = "URL Parameter";
|
||||
const kNameHeader = "Header Name";
|
||||
@ -453,6 +455,10 @@ const kLabelAddHeader = "Add Header";
|
||||
const kLabelAddVariable = "Add Variable";
|
||||
const kLabelSelectFile = "Select File";
|
||||
const kLabelAddFormField = "Add Form Field";
|
||||
const kHintContent = "Enter content";
|
||||
const kHintText = "Enter text";
|
||||
const kHintJson = "Enter JSON";
|
||||
const kHintQuery = "Enter Query";
|
||||
// Response Pane
|
||||
const kLabelNotSent = "Not Sent";
|
||||
const kLabelResponse = "Response";
|
||||
|
@ -9,6 +9,7 @@ class HistoryMetaModel with _$HistoryMetaModel {
|
||||
const factory HistoryMetaModel({
|
||||
required String historyId,
|
||||
required String requestId,
|
||||
required APIType apiType,
|
||||
@Default("") String name,
|
||||
required String url,
|
||||
required HTTPVerb method,
|
||||
|
@ -22,6 +22,7 @@ HistoryMetaModel _$HistoryMetaModelFromJson(Map<String, dynamic> json) {
|
||||
mixin _$HistoryMetaModel {
|
||||
String get historyId => throw _privateConstructorUsedError;
|
||||
String get requestId => throw _privateConstructorUsedError;
|
||||
APIType get apiType => throw _privateConstructorUsedError;
|
||||
String get name => throw _privateConstructorUsedError;
|
||||
String get url => throw _privateConstructorUsedError;
|
||||
HTTPVerb get method => throw _privateConstructorUsedError;
|
||||
@ -47,6 +48,7 @@ abstract class $HistoryMetaModelCopyWith<$Res> {
|
||||
$Res call(
|
||||
{String historyId,
|
||||
String requestId,
|
||||
APIType apiType,
|
||||
String name,
|
||||
String url,
|
||||
HTTPVerb method,
|
||||
@ -71,6 +73,7 @@ class _$HistoryMetaModelCopyWithImpl<$Res, $Val extends HistoryMetaModel>
|
||||
$Res call({
|
||||
Object? historyId = null,
|
||||
Object? requestId = null,
|
||||
Object? apiType = null,
|
||||
Object? name = null,
|
||||
Object? url = null,
|
||||
Object? method = null,
|
||||
@ -86,6 +89,10 @@ class _$HistoryMetaModelCopyWithImpl<$Res, $Val extends HistoryMetaModel>
|
||||
? _value.requestId
|
||||
: requestId // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
apiType: null == apiType
|
||||
? _value.apiType
|
||||
: apiType // ignore: cast_nullable_to_non_nullable
|
||||
as APIType,
|
||||
name: null == name
|
||||
? _value.name
|
||||
: name // ignore: cast_nullable_to_non_nullable
|
||||
@ -121,6 +128,7 @@ abstract class _$$HistoryMetaModelImplCopyWith<$Res>
|
||||
$Res call(
|
||||
{String historyId,
|
||||
String requestId,
|
||||
APIType apiType,
|
||||
String name,
|
||||
String url,
|
||||
HTTPVerb method,
|
||||
@ -143,6 +151,7 @@ class __$$HistoryMetaModelImplCopyWithImpl<$Res>
|
||||
$Res call({
|
||||
Object? historyId = null,
|
||||
Object? requestId = null,
|
||||
Object? apiType = null,
|
||||
Object? name = null,
|
||||
Object? url = null,
|
||||
Object? method = null,
|
||||
@ -158,6 +167,10 @@ class __$$HistoryMetaModelImplCopyWithImpl<$Res>
|
||||
? _value.requestId
|
||||
: requestId // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
apiType: null == apiType
|
||||
? _value.apiType
|
||||
: apiType // ignore: cast_nullable_to_non_nullable
|
||||
as APIType,
|
||||
name: null == name
|
||||
? _value.name
|
||||
: name // ignore: cast_nullable_to_non_nullable
|
||||
@ -188,6 +201,7 @@ class _$HistoryMetaModelImpl implements _HistoryMetaModel {
|
||||
const _$HistoryMetaModelImpl(
|
||||
{required this.historyId,
|
||||
required this.requestId,
|
||||
required this.apiType,
|
||||
this.name = "",
|
||||
required this.url,
|
||||
required this.method,
|
||||
@ -202,6 +216,8 @@ class _$HistoryMetaModelImpl implements _HistoryMetaModel {
|
||||
@override
|
||||
final String requestId;
|
||||
@override
|
||||
final APIType apiType;
|
||||
@override
|
||||
@JsonKey()
|
||||
final String name;
|
||||
@override
|
||||
@ -215,7 +231,7 @@ class _$HistoryMetaModelImpl implements _HistoryMetaModel {
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'HistoryMetaModel(historyId: $historyId, requestId: $requestId, name: $name, url: $url, method: $method, responseStatus: $responseStatus, timeStamp: $timeStamp)';
|
||||
return 'HistoryMetaModel(historyId: $historyId, requestId: $requestId, apiType: $apiType, name: $name, url: $url, method: $method, responseStatus: $responseStatus, timeStamp: $timeStamp)';
|
||||
}
|
||||
|
||||
@override
|
||||
@ -227,6 +243,7 @@ class _$HistoryMetaModelImpl implements _HistoryMetaModel {
|
||||
other.historyId == historyId) &&
|
||||
(identical(other.requestId, requestId) ||
|
||||
other.requestId == requestId) &&
|
||||
(identical(other.apiType, apiType) || other.apiType == apiType) &&
|
||||
(identical(other.name, name) || other.name == name) &&
|
||||
(identical(other.url, url) || other.url == url) &&
|
||||
(identical(other.method, method) || other.method == method) &&
|
||||
@ -238,8 +255,8 @@ class _$HistoryMetaModelImpl implements _HistoryMetaModel {
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, historyId, requestId, name, url,
|
||||
method, responseStatus, timeStamp);
|
||||
int get hashCode => Object.hash(runtimeType, historyId, requestId, apiType,
|
||||
name, url, method, responseStatus, timeStamp);
|
||||
|
||||
/// Create a copy of HistoryMetaModel
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@ -262,6 +279,7 @@ abstract class _HistoryMetaModel implements HistoryMetaModel {
|
||||
const factory _HistoryMetaModel(
|
||||
{required final String historyId,
|
||||
required final String requestId,
|
||||
required final APIType apiType,
|
||||
final String name,
|
||||
required final String url,
|
||||
required final HTTPVerb method,
|
||||
@ -276,6 +294,8 @@ abstract class _HistoryMetaModel implements HistoryMetaModel {
|
||||
@override
|
||||
String get requestId;
|
||||
@override
|
||||
APIType get apiType;
|
||||
@override
|
||||
String get name;
|
||||
@override
|
||||
String get url;
|
||||
|
@ -11,6 +11,7 @@ _$HistoryMetaModelImpl _$$HistoryMetaModelImplFromJson(
|
||||
_$HistoryMetaModelImpl(
|
||||
historyId: json['historyId'] as String,
|
||||
requestId: json['requestId'] as String,
|
||||
apiType: $enumDecode(_$APITypeEnumMap, json['apiType']),
|
||||
name: json['name'] as String? ?? "",
|
||||
url: json['url'] as String,
|
||||
method: $enumDecode(_$HTTPVerbEnumMap, json['method']),
|
||||
@ -23,6 +24,7 @@ Map<String, dynamic> _$$HistoryMetaModelImplToJson(
|
||||
<String, dynamic>{
|
||||
'historyId': instance.historyId,
|
||||
'requestId': instance.requestId,
|
||||
'apiType': _$APITypeEnumMap[instance.apiType]!,
|
||||
'name': instance.name,
|
||||
'url': instance.url,
|
||||
'method': _$HTTPVerbEnumMap[instance.method]!,
|
||||
@ -30,6 +32,11 @@ Map<String, dynamic> _$$HistoryMetaModelImplToJson(
|
||||
'timeStamp': instance.timeStamp.toIso8601String(),
|
||||
};
|
||||
|
||||
const _$APITypeEnumMap = {
|
||||
APIType.rest: 'rest',
|
||||
APIType.graphql: 'graphql',
|
||||
};
|
||||
|
||||
const _$HTTPVerbEnumMap = {
|
||||
HTTPVerb.get: 'get',
|
||||
HTTPVerb.head: 'head',
|
||||
|
@ -43,4 +43,5 @@ Map<String, dynamic> _$$RequestModelImplToJson(_$RequestModelImpl instance) =>
|
||||
|
||||
const _$APITypeEnumMap = {
|
||||
APIType.rest: 'rest',
|
||||
APIType.graphql: 'graphql',
|
||||
};
|
||||
|
@ -1,4 +1,5 @@
|
||||
import 'package:apidash_core/apidash_core.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:apidash/consts.dart';
|
||||
import 'providers.dart';
|
||||
@ -112,10 +113,11 @@ class CollectionStateNotifier
|
||||
unsave();
|
||||
}
|
||||
|
||||
void remove(String id) {
|
||||
void remove({String? id}) {
|
||||
final rId = id ?? ref.read(selectedIdStateProvider);
|
||||
var itemIds = ref.read(requestSequenceProvider);
|
||||
int idx = itemIds.indexOf(id);
|
||||
itemIds.remove(id);
|
||||
int idx = itemIds.indexOf(rId!);
|
||||
itemIds.remove(rId);
|
||||
ref.read(requestSequenceProvider.notifier).state = [...itemIds];
|
||||
|
||||
String? newId;
|
||||
@ -130,14 +132,15 @@ class CollectionStateNotifier
|
||||
ref.read(selectedIdStateProvider.notifier).state = newId;
|
||||
|
||||
var map = {...state!};
|
||||
map.remove(id);
|
||||
map.remove(rId);
|
||||
state = map;
|
||||
unsave();
|
||||
}
|
||||
|
||||
void clearResponse(String? id) {
|
||||
if (id == null || state?[id] == null) return;
|
||||
var currentModel = state![id]!;
|
||||
void clearResponse({String? id}) {
|
||||
final rId = id ?? ref.read(selectedIdStateProvider);
|
||||
if (rId == null || state?[rId] == null) return;
|
||||
var currentModel = state![rId]!;
|
||||
final newModel = currentModel.copyWith(
|
||||
responseStatus: null,
|
||||
message: null,
|
||||
@ -146,17 +149,18 @@ class CollectionStateNotifier
|
||||
sendingTime: null,
|
||||
);
|
||||
var map = {...state!};
|
||||
map[id] = newModel;
|
||||
map[rId] = newModel;
|
||||
state = map;
|
||||
unsave();
|
||||
}
|
||||
|
||||
void duplicate(String id) {
|
||||
void duplicate({String? id}) {
|
||||
final rId = id ?? ref.read(selectedIdStateProvider);
|
||||
final newId = getNewUuid();
|
||||
|
||||
var itemIds = ref.read(requestSequenceProvider);
|
||||
int idx = itemIds.indexOf(id);
|
||||
var currentModel = state![id]!;
|
||||
int idx = itemIds.indexOf(rId!);
|
||||
var currentModel = state![rId]!;
|
||||
final newModel = currentModel.copyWith(
|
||||
id: newId,
|
||||
name: "${currentModel.name} (copy)",
|
||||
@ -204,9 +208,10 @@ class CollectionStateNotifier
|
||||
unsave();
|
||||
}
|
||||
|
||||
void update(
|
||||
String id, {
|
||||
void update({
|
||||
String? id,
|
||||
HTTPVerb? method,
|
||||
APIType? apiType,
|
||||
String? url,
|
||||
String? name,
|
||||
String? description,
|
||||
@ -217,14 +222,21 @@ class CollectionStateNotifier
|
||||
List<bool>? isParamEnabledList,
|
||||
ContentType? bodyContentType,
|
||||
String? body,
|
||||
String? query,
|
||||
List<FormDataModel>? formData,
|
||||
int? responseStatus,
|
||||
String? message,
|
||||
HttpResponseModel? httpResponseModel,
|
||||
}) {
|
||||
var currentModel = state![id]!;
|
||||
final rId = id ?? ref.read(selectedIdStateProvider);
|
||||
if (rId == null) {
|
||||
debugPrint("Unable to update as Request Id is null");
|
||||
return;
|
||||
}
|
||||
var currentModel = state![rId]!;
|
||||
var currentHttpRequestModel = currentModel.httpRequestModel;
|
||||
final newModel = currentModel.copyWith(
|
||||
apiType: apiType ?? currentModel.apiType,
|
||||
name: name ?? currentModel.name,
|
||||
description: description ?? currentModel.description,
|
||||
requestTabIndex: requestTabIndex ?? currentModel.requestTabIndex,
|
||||
@ -240,6 +252,7 @@ class CollectionStateNotifier
|
||||
bodyContentType:
|
||||
bodyContentType ?? currentHttpRequestModel.bodyContentType,
|
||||
body: body ?? currentHttpRequestModel.body,
|
||||
query: query ?? currentHttpRequestModel.query,
|
||||
formData: formData ?? currentHttpRequestModel.formData,
|
||||
),
|
||||
responseStatus: responseStatus ?? currentModel.responseStatus,
|
||||
@ -248,7 +261,7 @@ class CollectionStateNotifier
|
||||
);
|
||||
|
||||
var map = {...state!};
|
||||
map[id] = newModel;
|
||||
map[rId] = newModel;
|
||||
state = map;
|
||||
unsave();
|
||||
}
|
||||
@ -267,8 +280,9 @@ class CollectionStateNotifier
|
||||
return;
|
||||
}
|
||||
|
||||
APIType apiType = requestModel!.apiType;
|
||||
HttpRequestModel substitutedHttpRequestModel =
|
||||
getSubstitutedHttpRequestModel(requestModel!.httpRequestModel!);
|
||||
getSubstitutedHttpRequestModel(requestModel.httpRequestModel!);
|
||||
|
||||
// set current model's isWorking to true and update state
|
||||
var map = {...state!};
|
||||
@ -281,6 +295,7 @@ class CollectionStateNotifier
|
||||
bool noSSL = ref.read(settingsProvider).isSSLDisabled;
|
||||
(HttpResponse?, Duration?, String?)? responseRec = await request(
|
||||
requestId,
|
||||
apiType,
|
||||
substitutedHttpRequestModel,
|
||||
defaultUriScheme: defaultUriScheme,
|
||||
noSSL: noSSL,
|
||||
@ -311,6 +326,7 @@ class CollectionStateNotifier
|
||||
metaData: HistoryMetaModel(
|
||||
historyId: newHistoryId,
|
||||
requestId: requestId,
|
||||
apiType: requestModel.apiType,
|
||||
name: requestModel.name,
|
||||
url: substitutedHttpRequestModel.url,
|
||||
method: substitutedHttpRequestModel.method,
|
||||
|
23
lib/screens/common_widgets/api_type_dropdown.dart
Normal file
23
lib/screens/common_widgets/api_type_dropdown.dart
Normal file
@ -0,0 +1,23 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:apidash/providers/providers.dart';
|
||||
import 'package:apidash/widgets/widgets.dart';
|
||||
|
||||
class APITypeDropdown extends ConsumerWidget {
|
||||
const APITypeDropdown({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
ref.watch(selectedIdStateProvider);
|
||||
final apiType = ref
|
||||
.watch(selectedRequestModelProvider.select((value) => value?.apiType));
|
||||
return APITypePopupMenu(
|
||||
apiType: apiType,
|
||||
onChanged: (type) {
|
||||
ref
|
||||
.read(collectionStateNotifierProvider.notifier)
|
||||
.update(apiType: type);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
import 'package:apidash_core/apidash_core.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:apidash/providers/providers.dart';
|
||||
@ -39,6 +40,13 @@ class CodePane extends ConsumerWidget {
|
||||
|
||||
final code = codegen.getCode(
|
||||
codegenLanguage, substitutedRequestModel!, defaultUriScheme);
|
||||
|
||||
// TODO: Add GraphQL Codegen
|
||||
if (substitutedRequestModel.apiType == APIType.graphql) {
|
||||
return const ErrorMessage(
|
||||
message: "Code generation for GraphQL is currently not available.",
|
||||
);
|
||||
}
|
||||
if (code == null) {
|
||||
return const ErrorMessage(
|
||||
message: "An error was encountered while generating code. $kRaiseIssue",
|
||||
|
@ -1,3 +1,4 @@
|
||||
export 'api_type_dropdown.dart';
|
||||
export 'button_navbar.dart';
|
||||
export 'code_pane.dart';
|
||||
export 'editor_title.dart';
|
||||
|
@ -5,7 +5,7 @@ import 'package:apidash/widgets/widgets.dart';
|
||||
import 'package:apidash/providers/providers.dart';
|
||||
import 'package:apidash/utils/utils.dart';
|
||||
import 'package:apidash/consts.dart';
|
||||
import 'history_pane.dart';
|
||||
import 'history_sidebar.dart';
|
||||
import 'history_viewer.dart';
|
||||
|
||||
class HistoryPage extends ConsumerWidget {
|
||||
|
@ -142,6 +142,7 @@ class _HistoryExpansionTileState extends ConsumerState<HistoryExpansionTile>
|
||||
padding: kPv2 + kPh4,
|
||||
child: SidebarHistoryCard(
|
||||
id: item.first.historyId,
|
||||
apiType: item.first.apiType,
|
||||
models: item,
|
||||
method: item.first.method,
|
||||
isSelected: selectedGroupId == getHistoryRequestKey(item.first),
|
@ -18,7 +18,8 @@ class HistoryRequestPane extends ConsumerWidget {
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final selectedId = ref.watch(selectedHistoryIdStateProvider);
|
||||
final codePaneVisible = ref.watch(historyCodePaneVisibleStateProvider);
|
||||
|
||||
final apiType = ref.watch(selectedHistoryRequestModelProvider
|
||||
.select((value) => value?.metaData.apiType));
|
||||
final headersMap = ref.watch(selectedHistoryRequestModelProvider
|
||||
.select((value) => value?.httpRequestModel.headersMap)) ??
|
||||
{};
|
||||
@ -33,7 +34,13 @@ class HistoryRequestPane extends ConsumerWidget {
|
||||
.select((value) => value?.httpRequestModel.hasBody)) ??
|
||||
false;
|
||||
|
||||
return RequestPane(
|
||||
final hasQuery = ref.watch(selectedHistoryRequestModelProvider
|
||||
.select((value) => value?.httpRequestModel.hasQuery)) ??
|
||||
false;
|
||||
|
||||
return switch (apiType) {
|
||||
APIType.rest => RequestPane(
|
||||
key: const Key("history-request-pane-rest"),
|
||||
selectedId: selectedId,
|
||||
codePaneVisible: codePaneVisible,
|
||||
onPressedCodeButton: () {
|
||||
@ -46,6 +53,11 @@ class HistoryRequestPane extends ConsumerWidget {
|
||||
headerLength > 0,
|
||||
hasBody,
|
||||
],
|
||||
tabLabels: const [
|
||||
kLabelURLParams,
|
||||
kLabelHeaders,
|
||||
kLabelBody,
|
||||
],
|
||||
children: [
|
||||
RequestDataTable(
|
||||
rows: paramsMap,
|
||||
@ -57,7 +69,34 @@ class HistoryRequestPane extends ConsumerWidget {
|
||||
),
|
||||
const HisRequestBody(),
|
||||
],
|
||||
);
|
||||
),
|
||||
APIType.graphql => RequestPane(
|
||||
key: const Key("history-request-pane-graphql"),
|
||||
selectedId: selectedId,
|
||||
codePaneVisible: codePaneVisible,
|
||||
onPressedCodeButton: () {
|
||||
ref.read(historyCodePaneVisibleStateProvider.notifier).state =
|
||||
!codePaneVisible;
|
||||
},
|
||||
showViewCodeButton: !isCompact,
|
||||
showIndicators: [
|
||||
headerLength > 0,
|
||||
hasQuery,
|
||||
],
|
||||
tabLabels: const [
|
||||
kLabelHeaders,
|
||||
kLabelQuery,
|
||||
],
|
||||
children: [
|
||||
RequestDataTable(
|
||||
rows: headersMap,
|
||||
keyName: kNameHeader,
|
||||
),
|
||||
const HisRequestBody(),
|
||||
],
|
||||
),
|
||||
_ => kSizedBoxEmpty,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -67,10 +106,12 @@ class HisRequestBody extends ConsumerWidget {
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final selectedHistoryModel = ref.watch(selectedHistoryRequestModelProvider);
|
||||
final apiType = selectedHistoryModel?.metaData.apiType;
|
||||
final requestModel = selectedHistoryModel?.httpRequestModel;
|
||||
final contentType = requestModel?.bodyContentType;
|
||||
|
||||
return Column(
|
||||
return switch (apiType) {
|
||||
APIType.rest => Column(
|
||||
children: [
|
||||
kVSpacer5,
|
||||
RichText(
|
||||
@ -94,8 +135,8 @@ class HisRequestBody extends ConsumerWidget {
|
||||
child: switch (contentType) {
|
||||
ContentType.formdata => Padding(
|
||||
padding: kPh4,
|
||||
child:
|
||||
RequestFormDataTable(rows: requestModel?.formData ?? [])),
|
||||
child: RequestFormDataTable(
|
||||
rows: requestModel?.formData ?? [])),
|
||||
// TODO: Fix JsonTextFieldEditor & plug it here
|
||||
ContentType.json => Padding(
|
||||
padding: kPt5o10,
|
||||
@ -111,7 +152,8 @@ class HisRequestBody extends ConsumerWidget {
|
||||
padding: kPt5o10,
|
||||
child: TextFieldEditor(
|
||||
key: Key("${selectedHistoryModel?.historyId}-body"),
|
||||
fieldKey: "${selectedHistoryModel?.historyId}-body-viewer",
|
||||
fieldKey:
|
||||
"${selectedHistoryModel?.historyId}-body-viewer",
|
||||
initialValue: requestModel?.body,
|
||||
readOnly: true,
|
||||
),
|
||||
@ -119,6 +161,17 @@ class HisRequestBody extends ConsumerWidget {
|
||||
},
|
||||
)
|
||||
],
|
||||
);
|
||||
),
|
||||
APIType.graphql => Padding(
|
||||
padding: kPt5o10,
|
||||
child: TextFieldEditor(
|
||||
key: Key("${selectedHistoryModel?.historyId}-query"),
|
||||
fieldKey: "${selectedHistoryModel?.historyId}-query-viewer",
|
||||
initialValue: requestModel?.query,
|
||||
readOnly: true,
|
||||
),
|
||||
),
|
||||
_ => kSizedBoxEmpty,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
import 'package:apidash/services/services.dart';
|
||||
import 'package:apidash_design_system/apidash_design_system.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
@ -21,11 +22,19 @@ class HistorySidebarHeader extends ConsumerWidget {
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
),
|
||||
const Spacer(),
|
||||
IconButton(
|
||||
tooltip: "Manage History",
|
||||
style: IconButton.styleFrom(
|
||||
foregroundColor: Theme.of(context).colorScheme.primary,
|
||||
ADIconButton(
|
||||
icon: Icons.delete_forever,
|
||||
iconSize: kButtonIconSizeLarge,
|
||||
tooltip: "Clear History",
|
||||
color: Theme.of(context).brightness == Brightness.dark
|
||||
? kColorDarkDanger
|
||||
: kColorLightDanger,
|
||||
onPressed: () => hiveHandler.clearAllHistory(),
|
||||
),
|
||||
ADIconButton(
|
||||
icon: Icons.manage_history_rounded,
|
||||
iconSize: kButtonIconSizeLarge,
|
||||
tooltip: "Manage History",
|
||||
onPressed: () {
|
||||
showHistoryRetentionDialog(
|
||||
context,
|
||||
@ -36,10 +45,6 @@ class HistorySidebarHeader extends ConsumerWidget {
|
||||
);
|
||||
});
|
||||
},
|
||||
icon: const Icon(
|
||||
Icons.manage_history_rounded,
|
||||
size: 20,
|
||||
),
|
||||
),
|
||||
context.width <= kMinWindowSize.width
|
||||
? IconButton(
|
||||
|
@ -1,3 +1,4 @@
|
||||
import 'package:apidash_core/apidash_core.dart';
|
||||
import 'package:apidash_design_system/apidash_design_system.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:apidash/widgets/widgets.dart';
|
||||
@ -16,6 +17,7 @@ class HistoryURLCard extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final apiType = historyRequestModel?.metaData.apiType;
|
||||
final method = historyRequestModel?.metaData.method;
|
||||
final url = historyRequestModel?.metaData.url;
|
||||
final fontSize = Theme.of(context).textTheme.titleMedium?.fontSize;
|
||||
@ -41,6 +43,7 @@ class HistoryURLCard extends StatelessWidget {
|
||||
child: Row(
|
||||
children: [
|
||||
isCompact ? const SizedBox.shrink() : kHSpacer10,
|
||||
if (apiType == APIType.rest) ...[
|
||||
Text(
|
||||
method!.name.toUpperCase(),
|
||||
style: kCodeStyle.copyWith(
|
||||
@ -53,6 +56,7 @@ class HistoryURLCard extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
isCompact ? kHSpacer10 : kHSpacer20,
|
||||
],
|
||||
Expanded(
|
||||
child: ReadOnlyTextField(
|
||||
initialValue: url,
|
||||
|
@ -186,6 +186,7 @@ class RequestItem extends ConsumerWidget {
|
||||
|
||||
return SidebarRequestCard(
|
||||
id: id,
|
||||
apiType: requestModel.apiType,
|
||||
method: requestModel.httpRequestModel!.method,
|
||||
name: requestModel.name,
|
||||
url: requestModel.httpRequestModel?.url,
|
||||
@ -208,7 +209,7 @@ class RequestItem extends ConsumerWidget {
|
||||
value = value.trim();
|
||||
ref
|
||||
.read(collectionStateNotifierProvider.notifier)
|
||||
.update(editRequestId!, name: value);
|
||||
.update(id: editRequestId!, name: value);
|
||||
},
|
||||
onTapOutsideNameEditor: () {
|
||||
ref.read(selectedIdEditStateProvider.notifier).state = null;
|
||||
@ -231,10 +232,10 @@ class RequestItem extends ConsumerWidget {
|
||||
);
|
||||
}
|
||||
if (item == ItemMenuOption.delete) {
|
||||
ref.read(collectionStateNotifierProvider.notifier).remove(id);
|
||||
ref.read(collectionStateNotifierProvider.notifier).remove(id: id);
|
||||
}
|
||||
if (item == ItemMenuOption.duplicate) {
|
||||
ref.read(collectionStateNotifierProvider.notifier).duplicate(id);
|
||||
ref.read(collectionStateNotifierProvider.notifier).duplicate(id: id);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
@ -18,6 +18,8 @@ class EditRequestBody extends ConsumerWidget {
|
||||
.getRequestModel(selectedId!);
|
||||
final contentType = ref.watch(selectedRequestModelProvider
|
||||
.select((value) => value?.httpRequestModel?.bodyContentType));
|
||||
final apiType = ref
|
||||
.watch(selectedRequestModelProvider.select((value) => value?.apiType));
|
||||
|
||||
// TODO: #178 GET->POST Currently switches to POST everytime user edits body even if the user intentionally chooses GET
|
||||
// final sm = ScaffoldMessenger.of(context);
|
||||
@ -36,7 +38,8 @@ class EditRequestBody extends ConsumerWidget {
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
const SizedBox(
|
||||
(apiType == APIType.rest)
|
||||
? const SizedBox(
|
||||
height: kHeaderHeight,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
@ -47,8 +50,10 @@ class EditRequestBody extends ConsumerWidget {
|
||||
DropdownButtonBodyContentType(),
|
||||
],
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
)
|
||||
: kSizedBoxEmpty,
|
||||
switch (apiType) {
|
||||
APIType.rest => Expanded(
|
||||
child: switch (contentType) {
|
||||
ContentType.formdata => const Padding(
|
||||
padding: kPh4,
|
||||
@ -67,8 +72,9 @@ class EditRequestBody extends ConsumerWidget {
|
||||
// changeToPostMethod();
|
||||
ref
|
||||
.read(collectionStateNotifierProvider.notifier)
|
||||
.update(selectedId, body: value);
|
||||
.update(body: value);
|
||||
},
|
||||
hintText: kHintJson,
|
||||
),
|
||||
),
|
||||
_ => Padding(
|
||||
@ -81,12 +87,31 @@ class EditRequestBody extends ConsumerWidget {
|
||||
// changeToPostMethod();
|
||||
ref
|
||||
.read(collectionStateNotifierProvider.notifier)
|
||||
.update(selectedId, body: value);
|
||||
.update(body: value);
|
||||
},
|
||||
hintText: kHintText,
|
||||
),
|
||||
),
|
||||
},
|
||||
)
|
||||
),
|
||||
APIType.graphql => Expanded(
|
||||
child: Padding(
|
||||
padding: kPt5o10,
|
||||
child: TextFieldEditor(
|
||||
key: Key("$selectedId-query"),
|
||||
fieldKey: "$selectedId-query-editor",
|
||||
initialValue: requestModel?.httpRequestModel?.query,
|
||||
onChanged: (String value) {
|
||||
ref
|
||||
.read(collectionStateNotifierProvider.notifier)
|
||||
.update(query: value);
|
||||
},
|
||||
hintText: kHintQuery,
|
||||
),
|
||||
),
|
||||
),
|
||||
_ => kSizedBoxEmpty,
|
||||
}
|
||||
],
|
||||
);
|
||||
}
|
||||
@ -99,7 +124,7 @@ class DropdownButtonBodyContentType extends ConsumerWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final selectedId = ref.watch(selectedIdStateProvider);
|
||||
ref.watch(selectedIdStateProvider);
|
||||
final requestBodyContentType = ref.watch(selectedRequestModelProvider
|
||||
.select((value) => value?.httpRequestModel?.bodyContentType));
|
||||
return DropdownButtonContentType(
|
||||
@ -107,7 +132,7 @@ class DropdownButtonBodyContentType extends ConsumerWidget {
|
||||
onChanged: (ContentType? value) {
|
||||
ref
|
||||
.read(collectionStateNotifierProvider.notifier)
|
||||
.update(selectedId!, bodyContentType: value);
|
||||
.update(bodyContentType: value);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
@ -27,9 +27,8 @@ class _FormDataBodyState extends ConsumerState<FormDataWidget> {
|
||||
seed = random.nextInt(kRandMax);
|
||||
}
|
||||
|
||||
void _onFieldChange(String selectedId) {
|
||||
void _onFieldChange() {
|
||||
ref.read(collectionStateNotifierProvider.notifier).update(
|
||||
selectedId,
|
||||
formData: formRows.sublist(0, formRows.length - 1),
|
||||
);
|
||||
}
|
||||
@ -93,7 +92,7 @@ class _FormDataBodyState extends ConsumerState<FormDataWidget> {
|
||||
isAddingRow = true;
|
||||
formRows.add(kFormDataEmptyModel);
|
||||
}
|
||||
_onFieldChange(selectedId!);
|
||||
_onFieldChange();
|
||||
},
|
||||
colorScheme: Theme.of(context).colorScheme,
|
||||
),
|
||||
@ -119,7 +118,7 @@ class _FormDataBodyState extends ConsumerState<FormDataWidget> {
|
||||
formRows.add(kFormDataEmptyModel);
|
||||
}
|
||||
setState(() {});
|
||||
_onFieldChange(selectedId!);
|
||||
_onFieldChange();
|
||||
},
|
||||
),
|
||||
),
|
||||
@ -134,7 +133,7 @@ class _FormDataBodyState extends ConsumerState<FormDataWidget> {
|
||||
value: pickedResult.path,
|
||||
);
|
||||
setState(() {});
|
||||
_onFieldChange(selectedId!);
|
||||
_onFieldChange();
|
||||
}
|
||||
},
|
||||
initialValue: formRows[index].value,
|
||||
@ -150,7 +149,7 @@ class _FormDataBodyState extends ConsumerState<FormDataWidget> {
|
||||
isAddingRow = true;
|
||||
formRows.add(kFormDataEmptyModel);
|
||||
}
|
||||
_onFieldChange(selectedId!);
|
||||
_onFieldChange();
|
||||
},
|
||||
colorScheme: Theme.of(context).colorScheme,
|
||||
),
|
||||
@ -170,7 +169,7 @@ class _FormDataBodyState extends ConsumerState<FormDataWidget> {
|
||||
} else {
|
||||
formRows.removeAt(index);
|
||||
}
|
||||
_onFieldChange(selectedId!);
|
||||
_onFieldChange();
|
||||
},
|
||||
child: Theme.of(context).brightness == Brightness.dark
|
||||
? kIconRemoveDark
|
||||
@ -216,7 +215,7 @@ class _FormDataBodyState extends ConsumerState<FormDataWidget> {
|
||||
child: ElevatedButton.icon(
|
||||
onPressed: () {
|
||||
formRows.add(kFormDataEmptyModel);
|
||||
_onFieldChange(selectedId!);
|
||||
_onFieldChange();
|
||||
},
|
||||
icon: const Icon(Icons.add),
|
||||
label: const Text(
|
||||
|
@ -29,9 +29,8 @@ class EditRequestHeadersState extends ConsumerState<EditRequestHeaders> {
|
||||
seed = random.nextInt(kRandMax);
|
||||
}
|
||||
|
||||
void _onFieldChange(String selectedId) {
|
||||
void _onFieldChange() {
|
||||
ref.read(collectionStateNotifierProvider.notifier).update(
|
||||
selectedId,
|
||||
headers: headerRows.sublist(0, headerRows.length - 1),
|
||||
isHeaderEnabledList:
|
||||
isRowEnabledList.sublist(0, headerRows.length - 1),
|
||||
@ -99,7 +98,7 @@ class EditRequestHeadersState extends ConsumerState<EditRequestHeaders> {
|
||||
setState(() {
|
||||
isRowEnabledList[index] = value!;
|
||||
});
|
||||
_onFieldChange(selectedId!);
|
||||
_onFieldChange();
|
||||
},
|
||||
colorScheme: Theme.of(context).colorScheme,
|
||||
),
|
||||
@ -117,7 +116,7 @@ class EditRequestHeadersState extends ConsumerState<EditRequestHeaders> {
|
||||
headerRows.add(kNameValueEmptyModel);
|
||||
isRowEnabledList.add(false);
|
||||
}
|
||||
_onFieldChange(selectedId!);
|
||||
_onFieldChange();
|
||||
},
|
||||
colorScheme: Theme.of(context).colorScheme,
|
||||
),
|
||||
@ -143,7 +142,7 @@ class EditRequestHeadersState extends ConsumerState<EditRequestHeaders> {
|
||||
headerRows.add(kNameValueEmptyModel);
|
||||
isRowEnabledList.add(false);
|
||||
}
|
||||
_onFieldChange(selectedId!);
|
||||
_onFieldChange();
|
||||
},
|
||||
colorScheme: Theme.of(context).colorScheme,
|
||||
),
|
||||
@ -165,7 +164,7 @@ class EditRequestHeadersState extends ConsumerState<EditRequestHeaders> {
|
||||
headerRows.removeAt(index);
|
||||
isRowEnabledList.removeAt(index);
|
||||
}
|
||||
_onFieldChange(selectedId!);
|
||||
_onFieldChange();
|
||||
},
|
||||
child: Theme.of(context).brightness == Brightness.dark
|
||||
? kIconRemoveDark
|
||||
@ -212,7 +211,7 @@ class EditRequestHeadersState extends ConsumerState<EditRequestHeaders> {
|
||||
onPressed: () {
|
||||
headerRows.add(kNameValueEmptyModel);
|
||||
isRowEnabledList.add(false);
|
||||
_onFieldChange(selectedId!);
|
||||
_onFieldChange();
|
||||
},
|
||||
icon: const Icon(Icons.add),
|
||||
label: const Text(
|
||||
|
@ -1,54 +1,23 @@
|
||||
import 'package:apidash_core/apidash_core.dart';
|
||||
import 'package:apidash_design_system/apidash_design_system.dart';
|
||||
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 'request_headers.dart';
|
||||
import 'request_params.dart';
|
||||
import 'request_body.dart';
|
||||
import 'request_pane_graphql.dart';
|
||||
import 'request_pane_rest.dart';
|
||||
|
||||
class EditRequestPane extends ConsumerWidget {
|
||||
const EditRequestPane({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final selectedId = ref.watch(selectedIdStateProvider);
|
||||
final codePaneVisible = ref.watch(codePaneVisibleStateProvider);
|
||||
final tabIndex = ref.watch(
|
||||
selectedRequestModelProvider.select((value) => value?.requestTabIndex));
|
||||
|
||||
final headerLength = ref.watch(selectedRequestModelProvider
|
||||
.select((value) => value?.httpRequestModel?.headersMap.length)) ??
|
||||
0;
|
||||
final paramLength = ref.watch(selectedRequestModelProvider
|
||||
.select((value) => value?.httpRequestModel?.paramsMap.length)) ??
|
||||
0;
|
||||
final hasBody = ref.watch(selectedRequestModelProvider
|
||||
.select((value) => value?.httpRequestModel?.hasBody)) ??
|
||||
false;
|
||||
|
||||
return RequestPane(
|
||||
selectedId: selectedId,
|
||||
codePaneVisible: codePaneVisible,
|
||||
tabIndex: tabIndex,
|
||||
onPressedCodeButton: () {
|
||||
ref.read(codePaneVisibleStateProvider.notifier).state =
|
||||
!codePaneVisible;
|
||||
},
|
||||
onTapTabBar: (index) {
|
||||
ref
|
||||
.read(collectionStateNotifierProvider.notifier)
|
||||
.update(selectedId!, requestTabIndex: index);
|
||||
},
|
||||
showIndicators: [
|
||||
paramLength > 0,
|
||||
headerLength > 0,
|
||||
hasBody,
|
||||
],
|
||||
children: const [
|
||||
EditRequestURLParams(),
|
||||
EditRequestHeaders(),
|
||||
EditRequestBody(),
|
||||
],
|
||||
);
|
||||
ref.watch(selectedIdStateProvider);
|
||||
final apiType = ref
|
||||
.watch(selectedRequestModelProvider.select((value) => value?.apiType));
|
||||
return switch (apiType) {
|
||||
APIType.rest => const EditRestRequestPane(),
|
||||
APIType.graphql => const EditGraphQLRequestPane(),
|
||||
_ => kSizedBoxEmpty,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,54 @@
|
||||
import 'package:apidash/consts.dart';
|
||||
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 'request_headers.dart';
|
||||
import 'request_body.dart';
|
||||
|
||||
class EditGraphQLRequestPane extends ConsumerWidget {
|
||||
const EditGraphQLRequestPane({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final selectedId = ref.watch(selectedIdStateProvider);
|
||||
var tabIndex = ref.watch(
|
||||
selectedRequestModelProvider.select((value) => value?.requestTabIndex));
|
||||
final codePaneVisible = ref.watch(codePaneVisibleStateProvider);
|
||||
final headerLength = ref.watch(selectedRequestModelProvider
|
||||
.select((value) => value?.httpRequestModel?.headersMap.length)) ??
|
||||
0;
|
||||
final hasQuery = ref.watch(selectedRequestModelProvider
|
||||
.select((value) => value?.httpRequestModel?.hasQuery)) ??
|
||||
false;
|
||||
if (tabIndex >= 2) {
|
||||
tabIndex = 0;
|
||||
}
|
||||
return RequestPane(
|
||||
selectedId: selectedId,
|
||||
codePaneVisible: codePaneVisible,
|
||||
tabIndex: tabIndex,
|
||||
onPressedCodeButton: () {
|
||||
ref.read(codePaneVisibleStateProvider.notifier).state =
|
||||
!codePaneVisible;
|
||||
},
|
||||
onTapTabBar: (index) {
|
||||
ref
|
||||
.read(collectionStateNotifierProvider.notifier)
|
||||
.update(requestTabIndex: index);
|
||||
},
|
||||
showIndicators: [
|
||||
headerLength > 0,
|
||||
hasQuery,
|
||||
],
|
||||
tabLabels: const [
|
||||
kLabelHeaders,
|
||||
kLabelQuery,
|
||||
],
|
||||
children: const [
|
||||
EditRequestHeaders(),
|
||||
EditRequestBody(),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
import 'package:apidash/consts.dart';
|
||||
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 'request_headers.dart';
|
||||
import 'request_params.dart';
|
||||
import 'request_body.dart';
|
||||
|
||||
class EditRestRequestPane extends ConsumerWidget {
|
||||
const EditRestRequestPane({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final selectedId = ref.watch(selectedIdStateProvider);
|
||||
final codePaneVisible = ref.watch(codePaneVisibleStateProvider);
|
||||
final tabIndex = ref.watch(
|
||||
selectedRequestModelProvider.select((value) => value?.requestTabIndex));
|
||||
|
||||
final headerLength = ref.watch(selectedRequestModelProvider
|
||||
.select((value) => value?.httpRequestModel?.headersMap.length)) ??
|
||||
0;
|
||||
final paramLength = ref.watch(selectedRequestModelProvider
|
||||
.select((value) => value?.httpRequestModel?.paramsMap.length)) ??
|
||||
0;
|
||||
final hasBody = ref.watch(selectedRequestModelProvider
|
||||
.select((value) => value?.httpRequestModel?.hasBody)) ??
|
||||
false;
|
||||
|
||||
return RequestPane(
|
||||
selectedId: selectedId,
|
||||
codePaneVisible: codePaneVisible,
|
||||
tabIndex: tabIndex,
|
||||
onPressedCodeButton: () {
|
||||
ref.read(codePaneVisibleStateProvider.notifier).state =
|
||||
!codePaneVisible;
|
||||
},
|
||||
onTapTabBar: (index) {
|
||||
ref
|
||||
.read(collectionStateNotifierProvider.notifier)
|
||||
.update(requestTabIndex: index);
|
||||
},
|
||||
showIndicators: [
|
||||
paramLength > 0,
|
||||
headerLength > 0,
|
||||
hasBody,
|
||||
],
|
||||
tabLabels: const [
|
||||
kLabelURLParams,
|
||||
kLabelHeaders,
|
||||
kLabelBody,
|
||||
],
|
||||
children: const [
|
||||
EditRequestURLParams(),
|
||||
EditRequestHeaders(),
|
||||
EditRequestBody(),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
@ -29,9 +29,8 @@ class EditRequestURLParamsState extends ConsumerState<EditRequestURLParams> {
|
||||
seed = random.nextInt(kRandMax);
|
||||
}
|
||||
|
||||
void _onFieldChange(String selectedId) {
|
||||
void _onFieldChange() {
|
||||
ref.read(collectionStateNotifierProvider.notifier).update(
|
||||
selectedId,
|
||||
params: paramRows.sublist(0, paramRows.length - 1),
|
||||
isParamEnabledList: isRowEnabledList.sublist(0, paramRows.length - 1),
|
||||
);
|
||||
@ -98,7 +97,7 @@ class EditRequestURLParamsState extends ConsumerState<EditRequestURLParams> {
|
||||
setState(() {
|
||||
isRowEnabledList[index] = value!;
|
||||
});
|
||||
_onFieldChange(selectedId!);
|
||||
_onFieldChange();
|
||||
},
|
||||
colorScheme: Theme.of(context).colorScheme,
|
||||
),
|
||||
@ -116,7 +115,7 @@ class EditRequestURLParamsState extends ConsumerState<EditRequestURLParams> {
|
||||
paramRows.add(kNameValueEmptyModel);
|
||||
isRowEnabledList.add(false);
|
||||
}
|
||||
_onFieldChange(selectedId!);
|
||||
_onFieldChange();
|
||||
},
|
||||
colorScheme: Theme.of(context).colorScheme,
|
||||
),
|
||||
@ -142,7 +141,7 @@ class EditRequestURLParamsState extends ConsumerState<EditRequestURLParams> {
|
||||
paramRows.add(kNameValueEmptyModel);
|
||||
isRowEnabledList.add(false);
|
||||
}
|
||||
_onFieldChange(selectedId!);
|
||||
_onFieldChange();
|
||||
},
|
||||
colorScheme: Theme.of(context).colorScheme,
|
||||
),
|
||||
@ -164,7 +163,7 @@ class EditRequestURLParamsState extends ConsumerState<EditRequestURLParams> {
|
||||
paramRows.removeAt(index);
|
||||
isRowEnabledList.removeAt(index);
|
||||
}
|
||||
_onFieldChange(selectedId!);
|
||||
_onFieldChange();
|
||||
},
|
||||
child: Theme.of(context).brightness == Brightness.dark
|
||||
? kIconRemoveDark
|
||||
@ -212,7 +211,7 @@ class EditRequestURLParamsState extends ConsumerState<EditRequestURLParams> {
|
||||
onPressed: () {
|
||||
paramRows.add(kNameValueEmptyModel);
|
||||
isRowEnabledList.add(false);
|
||||
_onFieldChange(selectedId!);
|
||||
_onFieldChange();
|
||||
},
|
||||
icon: const Icon(Icons.add),
|
||||
label: const Text(
|
||||
|
@ -61,10 +61,7 @@ class ResponseDetails extends ConsumerWidget {
|
||||
message: message,
|
||||
time: responseModel?.time,
|
||||
onClearResponse: () {
|
||||
final selectedRequest = ref.read(selectedRequestModelProvider);
|
||||
ref
|
||||
.read(collectionStateNotifierProvider.notifier)
|
||||
.clearResponse(selectedRequest?.id);
|
||||
ref.read(collectionStateNotifierProvider.notifier).clearResponse();
|
||||
},
|
||||
),
|
||||
const Expanded(
|
||||
|
@ -4,6 +4,7 @@ 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 '../../../consts.dart';
|
||||
import '../../common_widgets/common_widgets.dart';
|
||||
|
||||
class RequestEditorTopBar extends ConsumerWidget {
|
||||
@ -11,26 +12,18 @@ class RequestEditorTopBar extends ConsumerWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final id = ref.watch(selectedIdStateProvider);
|
||||
ref.watch(selectedIdStateProvider);
|
||||
final name =
|
||||
ref.watch(selectedRequestModelProvider.select((value) => value?.name));
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 12.0,
|
||||
top: 4.0,
|
||||
right: 4.0,
|
||||
bottom: 4.0,
|
||||
),
|
||||
padding: kP4,
|
||||
child: Row(
|
||||
children: [
|
||||
DropdownButtonAPIType(
|
||||
apiType: APIType.rest,
|
||||
onChanged: (apiType) {},
|
||||
),
|
||||
const APITypeDropdown(),
|
||||
kHSpacer10,
|
||||
Expanded(
|
||||
child: Text(
|
||||
name ?? "",
|
||||
name.isNullOrEmpty() ? kUntitled : name!,
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 1,
|
||||
@ -42,14 +35,13 @@ class RequestEditorTopBar extends ConsumerWidget {
|
||||
showRenameDialog(context, "Rename Request", name, (val) {
|
||||
ref
|
||||
.read(collectionStateNotifierProvider.notifier)
|
||||
.update(id!, name: val);
|
||||
.update(name: val);
|
||||
});
|
||||
},
|
||||
onDuplicatePressed: () => ref
|
||||
.read(collectionStateNotifierProvider.notifier)
|
||||
.duplicate(id!),
|
||||
onDuplicatePressed: () =>
|
||||
ref.read(collectionStateNotifierProvider.notifier).duplicate(),
|
||||
onDeletePressed: () =>
|
||||
ref.read(collectionStateNotifierProvider.notifier).remove(id!),
|
||||
ref.read(collectionStateNotifierProvider.notifier).remove(),
|
||||
),
|
||||
kHSpacer10,
|
||||
const EnvironmentDropdown(),
|
||||
|
@ -6,11 +6,14 @@ import 'package:apidash/providers/providers.dart';
|
||||
import 'package:apidash/widgets/widgets.dart';
|
||||
import '../../common_widgets/common_widgets.dart';
|
||||
|
||||
class EditorPaneRequestURLCard extends StatelessWidget {
|
||||
class EditorPaneRequestURLCard extends ConsumerWidget {
|
||||
const EditorPaneRequestURLCard({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
ref.watch(selectedIdStateProvider);
|
||||
final apiType = ref
|
||||
.watch(selectedRequestModelProvider.select((value) => value?.apiType));
|
||||
return Card(
|
||||
color: kColorTransparent,
|
||||
surfaceTintColor: kColorTransparent,
|
||||
@ -27,24 +30,38 @@ class EditorPaneRequestURLCard extends StatelessWidget {
|
||||
horizontal: !context.isMediumWindow ? 20 : 6,
|
||||
),
|
||||
child: context.isMediumWindow
|
||||
? const Row(
|
||||
? Row(
|
||||
children: [
|
||||
DropdownButtonHTTPMethod(),
|
||||
kHSpacer5,
|
||||
Expanded(
|
||||
switch (apiType) {
|
||||
APIType.rest => const DropdownButtonHTTPMethod(),
|
||||
APIType.graphql => kSizedBoxEmpty,
|
||||
null => kSizedBoxEmpty,
|
||||
},
|
||||
switch (apiType) {
|
||||
APIType.rest => kHSpacer5,
|
||||
_ => kHSpacer8,
|
||||
},
|
||||
const Expanded(
|
||||
child: URLTextField(),
|
||||
),
|
||||
],
|
||||
)
|
||||
: const Row(
|
||||
: Row(
|
||||
children: [
|
||||
DropdownButtonHTTPMethod(),
|
||||
kHSpacer20,
|
||||
Expanded(
|
||||
switch (apiType) {
|
||||
APIType.rest => const DropdownButtonHTTPMethod(),
|
||||
APIType.graphql => kSizedBoxEmpty,
|
||||
null => kSizedBoxEmpty,
|
||||
},
|
||||
switch (apiType) {
|
||||
APIType.rest => kHSpacer20,
|
||||
_ => kHSpacer8,
|
||||
},
|
||||
const Expanded(
|
||||
child: URLTextField(),
|
||||
),
|
||||
kHSpacer20,
|
||||
SizedBox(
|
||||
const SizedBox(
|
||||
height: 36,
|
||||
child: SendRequestButton(),
|
||||
)
|
||||
@ -67,10 +84,9 @@ class DropdownButtonHTTPMethod extends ConsumerWidget {
|
||||
return DropdownButtonHttpMethod(
|
||||
method: method,
|
||||
onChanged: (HTTPVerb? value) {
|
||||
final selectedId = ref.read(selectedRequestModelProvider)!.id;
|
||||
ref
|
||||
.read(collectionStateNotifierProvider.notifier)
|
||||
.update(selectedId, method: value);
|
||||
.update(method: value);
|
||||
},
|
||||
);
|
||||
}
|
||||
@ -92,9 +108,7 @@ class URLTextField extends ConsumerWidget {
|
||||
?.httpRequestModel
|
||||
?.url,
|
||||
onChanged: (value) {
|
||||
ref
|
||||
.read(collectionStateNotifierProvider.notifier)
|
||||
.update(selectedId, url: value);
|
||||
ref.read(collectionStateNotifierProvider.notifier).update(url: value);
|
||||
},
|
||||
onFieldSubmitted: (value) {
|
||||
ref.read(collectionStateNotifierProvider.notifier).sendRequest();
|
||||
|
@ -1,21 +1,34 @@
|
||||
import 'package:apidash/screens/common_widgets/common_widgets.dart';
|
||||
import 'package:apidash_design_system/apidash_design_system.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:apidash/widgets/widgets.dart';
|
||||
import 'package:apidash/consts.dart';
|
||||
import '../../../consts.dart';
|
||||
import '../../common_widgets/common_widgets.dart';
|
||||
import '../../home_page/editor_pane/details_card/response_pane.dart';
|
||||
import '../../home_page/editor_pane/editor_request.dart';
|
||||
import '../../home_page/editor_pane/url_card.dart';
|
||||
|
||||
class RequestTabs extends StatelessWidget {
|
||||
const RequestTabs({super.key, required this.controller});
|
||||
const RequestTabs({
|
||||
super.key,
|
||||
required this.controller,
|
||||
});
|
||||
final TabController controller;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
children: [
|
||||
kVSpacer5,
|
||||
const Padding(
|
||||
padding: kPh8,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
APITypeDropdown(),
|
||||
EnvironmentDropdown(),
|
||||
],
|
||||
),
|
||||
),
|
||||
kVSpacer3,
|
||||
const Padding(
|
||||
padding: kPh4,
|
||||
child: EditorPaneRequestURLCard(),
|
||||
|
@ -40,19 +40,19 @@ class _RequestResponsePageState extends ConsumerState<RequestResponsePage>
|
||||
showRenameDialog(context, "Rename Request", name, (val) {
|
||||
ref
|
||||
.read(collectionStateNotifierProvider.notifier)
|
||||
.update(id!, name: val);
|
||||
.update(name: val);
|
||||
});
|
||||
}
|
||||
if (item == ItemMenuOption.delete) {
|
||||
ref.read(collectionStateNotifierProvider.notifier).remove(id!);
|
||||
ref.read(collectionStateNotifierProvider.notifier).remove();
|
||||
}
|
||||
if (item == ItemMenuOption.duplicate) {
|
||||
ref.read(collectionStateNotifierProvider.notifier).duplicate(id!);
|
||||
ref.read(collectionStateNotifierProvider.notifier).duplicate();
|
||||
}
|
||||
},
|
||||
),
|
||||
leftDrawerContent: const CollectionPane(),
|
||||
actions: const [Padding(padding: kPh8, child: EnvironmentDropdown())],
|
||||
actions: const [kVSpacer16],
|
||||
mainContent: id == null
|
||||
? const RequestEditorDefault()
|
||||
: RequestTabs(
|
||||
|
@ -140,6 +140,11 @@ class HiveHandler {
|
||||
|
||||
Future<void> deleteHistoryRequest(String id) => historyLazyBox.delete(id);
|
||||
|
||||
Future clearAllHistory() async {
|
||||
await historyMetaBox.clear();
|
||||
await historyLazyBox.clear();
|
||||
}
|
||||
|
||||
Future clear() async {
|
||||
await dataBox.clear();
|
||||
await environmentBox.clear();
|
||||
|
@ -4,7 +4,7 @@ import 'package:apidash/models/models.dart';
|
||||
|
||||
String getEnvironmentTitle(String? name) {
|
||||
if (name == null || name.trim() == "") {
|
||||
return "untitled";
|
||||
return kUntitled;
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ DateTime stripTime(DateTime dateTime) {
|
||||
RequestModel getRequestModelFromHistoryModel(HistoryRequestModel model) {
|
||||
return RequestModel(
|
||||
id: model.historyId,
|
||||
apiType: model.metaData.apiType,
|
||||
name: model.metaData.name,
|
||||
responseStatus: model.httpResponseModel.statusCode,
|
||||
message: kResponseCodeReasons[model.httpResponseModel.statusCode],
|
||||
|
@ -3,12 +3,12 @@ import '../consts.dart';
|
||||
|
||||
String getRequestTitleFromUrl(String? url) {
|
||||
if (url == null || url.trim() == "") {
|
||||
return "untitled";
|
||||
return kUntitled;
|
||||
}
|
||||
if (url.contains("://")) {
|
||||
String rem = url.split("://")[1];
|
||||
if (rem.trim() == "") {
|
||||
return "untitled";
|
||||
return kUntitled;
|
||||
}
|
||||
return rem;
|
||||
}
|
||||
|
@ -3,12 +3,13 @@ import 'package:apidash_design_system/apidash_design_system.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:apidash/models/models.dart';
|
||||
import 'package:apidash/utils/utils.dart';
|
||||
import 'texts.dart' show MethodBox;
|
||||
import 'texts.dart';
|
||||
|
||||
class SidebarHistoryCard extends StatelessWidget {
|
||||
const SidebarHistoryCard({
|
||||
super.key,
|
||||
required this.id,
|
||||
required this.apiType,
|
||||
required this.models,
|
||||
required this.method,
|
||||
this.isSelected = false,
|
||||
@ -17,6 +18,7 @@ class SidebarHistoryCard extends StatelessWidget {
|
||||
});
|
||||
|
||||
final String id;
|
||||
final APIType apiType;
|
||||
final List<HistoryMetaModel> models;
|
||||
final HTTPVerb method;
|
||||
final bool isSelected;
|
||||
@ -63,7 +65,10 @@ class SidebarHistoryCard extends StatelessWidget {
|
||||
height: 20,
|
||||
child: Row(
|
||||
children: [
|
||||
MethodBox(method: method),
|
||||
SidebarRequestCardTextBox(
|
||||
apiType: apiType,
|
||||
method: method,
|
||||
),
|
||||
kHSpacer4,
|
||||
Expanded(
|
||||
child: Text(
|
||||
|
@ -4,12 +4,13 @@ import 'package:flutter/material.dart';
|
||||
import 'package:apidash/consts.dart';
|
||||
import 'package:apidash/utils/utils.dart';
|
||||
import 'menu_item_card.dart';
|
||||
import 'texts.dart' show MethodBox;
|
||||
import 'texts.dart';
|
||||
|
||||
class SidebarRequestCard extends StatelessWidget {
|
||||
const SidebarRequestCard({
|
||||
super.key,
|
||||
required this.id,
|
||||
required this.apiType,
|
||||
required this.method,
|
||||
this.name,
|
||||
this.url,
|
||||
@ -26,6 +27,7 @@ class SidebarRequestCard extends StatelessWidget {
|
||||
});
|
||||
|
||||
final String id;
|
||||
final APIType apiType;
|
||||
final String? name;
|
||||
final String? url;
|
||||
final HTTPVerb method;
|
||||
@ -88,7 +90,10 @@ class SidebarRequestCard extends StatelessWidget {
|
||||
height: 20,
|
||||
child: Row(
|
||||
children: [
|
||||
MethodBox(method: method),
|
||||
SidebarRequestCardTextBox(
|
||||
apiType: apiType,
|
||||
method: method,
|
||||
),
|
||||
kHSpacer4,
|
||||
Expanded(
|
||||
child: inEditMode
|
||||
|
@ -1,4 +1,5 @@
|
||||
import 'dart:math' as math;
|
||||
import 'package:apidash/consts.dart';
|
||||
import 'package:apidash_design_system/apidash_design_system.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
@ -10,12 +11,14 @@ class TextFieldEditor extends StatefulWidget {
|
||||
this.onChanged,
|
||||
this.initialValue,
|
||||
this.readOnly = false,
|
||||
this.hintText,
|
||||
});
|
||||
|
||||
final String fieldKey;
|
||||
final Function(String)? onChanged;
|
||||
final String? initialValue;
|
||||
final bool readOnly;
|
||||
final String? hintText;
|
||||
@override
|
||||
State<TextFieldEditor> createState() => _TextFieldEditorState();
|
||||
}
|
||||
@ -72,14 +75,16 @@ class _TextFieldEditorState extends State<TextFieldEditor> {
|
||||
expands: true,
|
||||
maxLines: null,
|
||||
readOnly: widget.readOnly,
|
||||
style: kCodeStyle,
|
||||
style: kCodeStyle.copyWith(
|
||||
fontSize: Theme.of(context).textTheme.bodyMedium?.fontSize,
|
||||
),
|
||||
textAlignVertical: TextAlignVertical.top,
|
||||
onChanged: widget.onChanged,
|
||||
onTapOutside: (PointerDownEvent event) {
|
||||
editorFocusNode.unfocus();
|
||||
},
|
||||
decoration: InputDecoration(
|
||||
hintText: "Enter content (body)",
|
||||
hintText: widget.hintText ?? kHintContent,
|
||||
hintStyle: TextStyle(
|
||||
color: Theme.of(context).colorScheme.outline.withOpacity(
|
||||
kHintOpacity,
|
||||
|
@ -1,4 +1,5 @@
|
||||
import 'dart:math' as math;
|
||||
import 'package:apidash/consts.dart';
|
||||
import 'package:apidash_design_system/apidash_design_system.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
@ -93,7 +94,7 @@ class _JsonTextFieldEditorState extends State<JsonTextFieldEditor> {
|
||||
widget.onChanged?.call(value);
|
||||
},
|
||||
decoration: InputDecoration(
|
||||
hintText: "Enter content (body)",
|
||||
hintText: kHintJson,
|
||||
hintStyle: TextStyle(
|
||||
color: Theme.of(context).colorScheme.outline.withOpacity(
|
||||
kHintOpacity,
|
||||
|
@ -2,10 +2,10 @@ import 'package:apidash_core/apidash_core.dart';
|
||||
import 'package:apidash_design_system/apidash_design_system.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class DropdownButtonAPIType extends StatelessWidget {
|
||||
const DropdownButtonAPIType({
|
||||
class APITypePopupMenu extends StatelessWidget {
|
||||
const APITypePopupMenu({
|
||||
super.key,
|
||||
this.apiType,
|
||||
required this.apiType,
|
||||
this.onChanged,
|
||||
});
|
||||
|
||||
@ -14,11 +14,13 @@ class DropdownButtonAPIType extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ADDropdownButton<APIType>(
|
||||
value: apiType,
|
||||
return ADPopupMenu<APIType>(
|
||||
tooltip: "Select API Type",
|
||||
width: 100,
|
||||
value: apiType?.label,
|
||||
values: APIType.values.map((e) => (e, e.label)),
|
||||
onChanged: onChanged,
|
||||
isDense: true,
|
||||
isOutlined: true,
|
||||
);
|
||||
}
|
||||
}
|
@ -12,6 +12,7 @@ class RequestPane extends StatefulHookWidget {
|
||||
this.tabIndex,
|
||||
this.onPressedCodeButton,
|
||||
this.onTapTabBar,
|
||||
required this.tabLabels,
|
||||
required this.children,
|
||||
this.showIndicators = const [false, false, false],
|
||||
this.showViewCodeButton,
|
||||
@ -22,6 +23,7 @@ class RequestPane extends StatefulHookWidget {
|
||||
final int? tabIndex;
|
||||
final void Function()? onPressedCodeButton;
|
||||
final void Function(int)? onTapTabBar;
|
||||
final List<String> tabLabels;
|
||||
final List<Widget> children;
|
||||
final List<bool> showIndicators;
|
||||
final bool? showViewCodeButton;
|
||||
@ -35,7 +37,7 @@ class _RequestPaneState extends State<RequestPane>
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final TabController controller = useTabController(
|
||||
initialLength: 3,
|
||||
initialLength: widget.children.length,
|
||||
vsync: this,
|
||||
);
|
||||
if (widget.tabIndex != null) {
|
||||
@ -75,27 +77,21 @@ class _RequestPaneState extends State<RequestPane>
|
||||
),
|
||||
),
|
||||
)
|
||||
: const SizedBox.shrink(),
|
||||
: kVSpacer10,
|
||||
TabBar(
|
||||
key: Key(widget.selectedId!),
|
||||
controller: controller,
|
||||
overlayColor: kColorTransparentState,
|
||||
labelPadding: kPh2,
|
||||
onTap: widget.onTapTabBar,
|
||||
tabs: [
|
||||
TabLabel(
|
||||
text: kLabelURLParams,
|
||||
showIndicator: widget.showIndicators[0],
|
||||
tabs: widget.tabLabels.indexed
|
||||
.map<Widget>(
|
||||
(e) => TabLabel(
|
||||
text: e.$2,
|
||||
showIndicator: widget.showIndicators[e.$1],
|
||||
),
|
||||
TabLabel(
|
||||
text: kLabelHeaders,
|
||||
showIndicator: widget.showIndicators[1],
|
||||
),
|
||||
TabLabel(
|
||||
text: kLabelBody,
|
||||
showIndicator: widget.showIndicators[2],
|
||||
),
|
||||
],
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
kVSpacer5,
|
||||
Expanded(
|
@ -3,11 +3,13 @@ import 'package:apidash_design_system/apidash_design_system.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:apidash/utils/utils.dart';
|
||||
|
||||
class MethodBox extends StatelessWidget {
|
||||
const MethodBox({
|
||||
class SidebarRequestCardTextBox extends StatelessWidget {
|
||||
const SidebarRequestCardTextBox({
|
||||
super.key,
|
||||
required this.apiType,
|
||||
required this.method,
|
||||
});
|
||||
final APIType apiType;
|
||||
final HTTPVerb method;
|
||||
|
||||
@override
|
||||
@ -15,15 +17,21 @@ class MethodBox extends StatelessWidget {
|
||||
return SizedBox(
|
||||
width: 24,
|
||||
child: Text(
|
||||
method.abbr,
|
||||
switch (apiType) {
|
||||
APIType.rest => method.abbr,
|
||||
APIType.graphql => apiType.abbr,
|
||||
},
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 8,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: getHTTPMethodColor(
|
||||
color: switch (apiType) {
|
||||
APIType.rest => getHTTPMethodColor(
|
||||
method,
|
||||
brightness: Theme.of(context).brightness,
|
||||
),
|
||||
APIType.graphql => kColorGQL,
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
|
@ -24,7 +24,6 @@ export 'dropdown_content_type.dart';
|
||||
export 'dropdown_formdata.dart';
|
||||
export 'dropdown_http_method.dart';
|
||||
export 'dropdown_import_format.dart';
|
||||
export 'dropdown_api_type.dart';
|
||||
export 'editor_json.dart';
|
||||
export 'editor.dart';
|
||||
export 'error_message.dart';
|
||||
@ -40,12 +39,13 @@ export 'markdown.dart';
|
||||
export 'menu_item_card.dart';
|
||||
export 'menu_sidebar_top.dart';
|
||||
export 'overlay_widget.dart';
|
||||
export 'popup_menu_api_type.dart';
|
||||
export 'popup_menu_codegen.dart';
|
||||
export 'popup_menu_env.dart';
|
||||
export 'popup_menu_history.dart';
|
||||
export 'popup_menu_uri.dart';
|
||||
export 'previewer.dart';
|
||||
export 'request_widgets.dart';
|
||||
export 'request_pane.dart';
|
||||
export 'response_widgets.dart';
|
||||
export 'splitview_drawer.dart';
|
||||
export 'splitview_dashboard.dart';
|
||||
|
@ -8,6 +8,14 @@ scripts:
|
||||
run: melos exec -- "flutter analyze"
|
||||
description: Analyze all packages
|
||||
|
||||
clean:
|
||||
run: melos exec -- "flutter clean"
|
||||
description: Clean all packages
|
||||
|
||||
build-gen:
|
||||
run: melos exec -- "dart run build_runner build --delete-conflicting-outputs"
|
||||
description: Run build generator for all packages
|
||||
|
||||
test:
|
||||
run: melos exec --dir-exists=test -- "flutter test --coverage"
|
||||
description: Run tests for all packages
|
||||
|
@ -1,10 +1,12 @@
|
||||
import 'dart:convert';
|
||||
|
||||
enum APIType {
|
||||
rest("HTTP");
|
||||
rest("HTTP", "HTTP"),
|
||||
graphql("GraphQL", "GQL");
|
||||
|
||||
const APIType(this.label);
|
||||
const APIType(this.label, this.abbr);
|
||||
final String label;
|
||||
final String abbr;
|
||||
}
|
||||
|
||||
enum HTTPVerb {
|
||||
|
@ -19,3 +19,15 @@ extension StringExtension on String {
|
||||
return "${substring(0, limit)}...";
|
||||
}
|
||||
}
|
||||
|
||||
extension StringOrNullExtension on String? {
|
||||
bool isNullOrEmpty() {
|
||||
if (this == null) {
|
||||
return true;
|
||||
}
|
||||
if (this!.isEmpty) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,6 @@ import '../utils/utils.dart'
|
||||
import '../consts.dart';
|
||||
|
||||
part 'http_request_model.freezed.dart';
|
||||
|
||||
part 'http_request_model.g.dart';
|
||||
|
||||
@freezed
|
||||
@ -27,6 +26,7 @@ class HttpRequestModel with _$HttpRequestModel {
|
||||
List<bool>? isParamEnabledList,
|
||||
@Default(ContentType.json) ContentType bodyContentType,
|
||||
String? body,
|
||||
String? query,
|
||||
List<FormDataModel>? formData,
|
||||
}) = _HttpRequestModel;
|
||||
|
||||
@ -61,6 +61,7 @@ class HttpRequestModel with _$HttpRequestModel {
|
||||
kMethodsWithBody.contains(method) &&
|
||||
hasFormDataContentType &&
|
||||
formDataMapList.isNotEmpty;
|
||||
bool get hasQuery => query?.isNotEmpty ?? false;
|
||||
List<FormDataModel> get formDataList => formData ?? <FormDataModel>[];
|
||||
List<Map<String, String>> get formDataMapList =>
|
||||
rowsToFormDataMapList(formDataList) ?? [];
|
||||
|
@ -28,6 +28,7 @@ mixin _$HttpRequestModel {
|
||||
List<bool>? get isParamEnabledList => throw _privateConstructorUsedError;
|
||||
ContentType get bodyContentType => throw _privateConstructorUsedError;
|
||||
String? get body => throw _privateConstructorUsedError;
|
||||
String? get query => throw _privateConstructorUsedError;
|
||||
List<FormDataModel>? get formData => throw _privateConstructorUsedError;
|
||||
|
||||
/// Serializes this HttpRequestModel to a JSON map.
|
||||
@ -55,6 +56,7 @@ abstract class $HttpRequestModelCopyWith<$Res> {
|
||||
List<bool>? isParamEnabledList,
|
||||
ContentType bodyContentType,
|
||||
String? body,
|
||||
String? query,
|
||||
List<FormDataModel>? formData});
|
||||
}
|
||||
|
||||
@ -81,6 +83,7 @@ class _$HttpRequestModelCopyWithImpl<$Res, $Val extends HttpRequestModel>
|
||||
Object? isParamEnabledList = freezed,
|
||||
Object? bodyContentType = null,
|
||||
Object? body = freezed,
|
||||
Object? query = freezed,
|
||||
Object? formData = freezed,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
@ -116,6 +119,10 @@ class _$HttpRequestModelCopyWithImpl<$Res, $Val extends HttpRequestModel>
|
||||
? _value.body
|
||||
: body // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
query: freezed == query
|
||||
? _value.query
|
||||
: query // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
formData: freezed == formData
|
||||
? _value.formData
|
||||
: formData // ignore: cast_nullable_to_non_nullable
|
||||
@ -141,6 +148,7 @@ abstract class _$$HttpRequestModelImplCopyWith<$Res>
|
||||
List<bool>? isParamEnabledList,
|
||||
ContentType bodyContentType,
|
||||
String? body,
|
||||
String? query,
|
||||
List<FormDataModel>? formData});
|
||||
}
|
||||
|
||||
@ -165,6 +173,7 @@ class __$$HttpRequestModelImplCopyWithImpl<$Res>
|
||||
Object? isParamEnabledList = freezed,
|
||||
Object? bodyContentType = null,
|
||||
Object? body = freezed,
|
||||
Object? query = freezed,
|
||||
Object? formData = freezed,
|
||||
}) {
|
||||
return _then(_$HttpRequestModelImpl(
|
||||
@ -200,6 +209,10 @@ class __$$HttpRequestModelImplCopyWithImpl<$Res>
|
||||
? _value.body
|
||||
: body // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
query: freezed == query
|
||||
? _value.query
|
||||
: query // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
formData: freezed == formData
|
||||
? _value._formData
|
||||
: formData // ignore: cast_nullable_to_non_nullable
|
||||
@ -221,6 +234,7 @@ class _$HttpRequestModelImpl extends _HttpRequestModel {
|
||||
final List<bool>? isParamEnabledList,
|
||||
this.bodyContentType = ContentType.json,
|
||||
this.body,
|
||||
this.query,
|
||||
final List<FormDataModel>? formData})
|
||||
: _headers = headers,
|
||||
_params = params,
|
||||
@ -285,6 +299,8 @@ class _$HttpRequestModelImpl extends _HttpRequestModel {
|
||||
final ContentType bodyContentType;
|
||||
@override
|
||||
final String? body;
|
||||
@override
|
||||
final String? query;
|
||||
final List<FormDataModel>? _formData;
|
||||
@override
|
||||
List<FormDataModel>? get formData {
|
||||
@ -297,7 +313,7 @@ class _$HttpRequestModelImpl extends _HttpRequestModel {
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'HttpRequestModel(method: $method, url: $url, headers: $headers, params: $params, isHeaderEnabledList: $isHeaderEnabledList, isParamEnabledList: $isParamEnabledList, bodyContentType: $bodyContentType, body: $body, formData: $formData)';
|
||||
return 'HttpRequestModel(method: $method, url: $url, headers: $headers, params: $params, isHeaderEnabledList: $isHeaderEnabledList, isParamEnabledList: $isParamEnabledList, bodyContentType: $bodyContentType, body: $body, query: $query, formData: $formData)';
|
||||
}
|
||||
|
||||
@override
|
||||
@ -316,6 +332,7 @@ class _$HttpRequestModelImpl extends _HttpRequestModel {
|
||||
(identical(other.bodyContentType, bodyContentType) ||
|
||||
other.bodyContentType == bodyContentType) &&
|
||||
(identical(other.body, body) || other.body == body) &&
|
||||
(identical(other.query, query) || other.query == query) &&
|
||||
const DeepCollectionEquality().equals(other._formData, _formData));
|
||||
}
|
||||
|
||||
@ -331,6 +348,7 @@ class _$HttpRequestModelImpl extends _HttpRequestModel {
|
||||
const DeepCollectionEquality().hash(_isParamEnabledList),
|
||||
bodyContentType,
|
||||
body,
|
||||
query,
|
||||
const DeepCollectionEquality().hash(_formData));
|
||||
|
||||
/// Create a copy of HttpRequestModel
|
||||
@ -360,6 +378,7 @@ abstract class _HttpRequestModel extends HttpRequestModel {
|
||||
final List<bool>? isParamEnabledList,
|
||||
final ContentType bodyContentType,
|
||||
final String? body,
|
||||
final String? query,
|
||||
final List<FormDataModel>? formData}) = _$HttpRequestModelImpl;
|
||||
const _HttpRequestModel._() : super._();
|
||||
|
||||
@ -383,6 +402,8 @@ abstract class _HttpRequestModel extends HttpRequestModel {
|
||||
@override
|
||||
String? get body;
|
||||
@override
|
||||
String? get query;
|
||||
@override
|
||||
List<FormDataModel>? get formData;
|
||||
|
||||
/// Create a copy of HttpRequestModel
|
||||
|
@ -29,6 +29,7 @@ _$HttpRequestModelImpl _$$HttpRequestModelImplFromJson(Map json) =>
|
||||
$enumDecodeNullable(_$ContentTypeEnumMap, json['bodyContentType']) ??
|
||||
ContentType.json,
|
||||
body: json['body'] as String?,
|
||||
query: json['query'] as String?,
|
||||
formData: (json['formData'] as List<dynamic>?)
|
||||
?.map((e) =>
|
||||
FormDataModel.fromJson(Map<String, Object?>.from(e as Map)))
|
||||
@ -46,6 +47,7 @@ Map<String, dynamic> _$$HttpRequestModelImplToJson(
|
||||
'isParamEnabledList': instance.isParamEnabledList,
|
||||
'bodyContentType': _$ContentTypeEnumMap[instance.bodyContentType]!,
|
||||
'body': instance.body,
|
||||
'query': instance.query,
|
||||
'formData': instance.formData?.map((e) => e.toJson()).toList(),
|
||||
};
|
||||
|
||||
|
@ -10,7 +10,6 @@ import '../utils/utils.dart';
|
||||
import '../consts.dart';
|
||||
|
||||
part 'http_response_model.freezed.dart';
|
||||
|
||||
part 'http_response_model.g.dart';
|
||||
|
||||
class Uint8ListConverter implements JsonConverter<Uint8List?, List<int>?> {
|
||||
|
@ -12,6 +12,7 @@ typedef HttpResponse = http.Response;
|
||||
|
||||
Future<(HttpResponse?, Duration?, String?)> request(
|
||||
String requestId,
|
||||
APIType apiType,
|
||||
HttpRequestModel requestModel, {
|
||||
SupportedUriSchemes defaultUriScheme = kDefaultUriScheme,
|
||||
bool noSSL = false,
|
||||
@ -28,10 +29,11 @@ Future<(HttpResponse?, Duration?, String?)> request(
|
||||
if (uriRec.$1 != null) {
|
||||
Uri requestUrl = uriRec.$1!;
|
||||
Map<String, String> headers = requestModel.enabledHeadersMap;
|
||||
HttpResponse response;
|
||||
HttpResponse? response;
|
||||
String? body;
|
||||
try {
|
||||
Stopwatch stopwatch = Stopwatch()..start();
|
||||
if (apiType == APIType.rest) {
|
||||
var isMultiPartRequest =
|
||||
requestModel.bodyContentType == ContentType.formdata;
|
||||
|
||||
@ -41,7 +43,8 @@ Future<(HttpResponse?, Duration?, String?)> request(
|
||||
var contentLength = utf8.encode(requestBody).length;
|
||||
if (contentLength > 0) {
|
||||
body = requestBody;
|
||||
headers[HttpHeaders.contentLengthHeader] = contentLength.toString();
|
||||
headers[HttpHeaders.contentLengthHeader] =
|
||||
contentLength.toString();
|
||||
if (!requestModel.hasContentTypeHeader) {
|
||||
headers[HttpHeaders.contentTypeHeader] =
|
||||
requestModel.bodyContentType.header;
|
||||
@ -86,7 +89,8 @@ Future<(HttpResponse?, Duration?, String?)> request(
|
||||
await client.post(requestUrl, headers: headers, body: body);
|
||||
break;
|
||||
case HTTPVerb.put:
|
||||
response = await client.put(requestUrl, headers: headers, body: body);
|
||||
response =
|
||||
await client.put(requestUrl, headers: headers, body: body);
|
||||
break;
|
||||
case HTTPVerb.patch:
|
||||
response =
|
||||
@ -97,6 +101,25 @@ Future<(HttpResponse?, Duration?, String?)> request(
|
||||
await client.delete(requestUrl, headers: headers, body: body);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (apiType == APIType.graphql) {
|
||||
var requestBody = getGraphQLBody(requestModel);
|
||||
if (requestBody != null) {
|
||||
var contentLength = utf8.encode(requestBody).length;
|
||||
if (contentLength > 0) {
|
||||
body = requestBody;
|
||||
headers[HttpHeaders.contentLengthHeader] = contentLength.toString();
|
||||
if (!requestModel.hasContentTypeHeader) {
|
||||
headers[HttpHeaders.contentTypeHeader] = ContentType.json.header;
|
||||
}
|
||||
}
|
||||
}
|
||||
response = await client.post(
|
||||
requestUrl,
|
||||
headers: headers,
|
||||
body: body,
|
||||
);
|
||||
}
|
||||
stopwatch.stop();
|
||||
return (response, stopwatch.elapsed, null);
|
||||
} catch (e) {
|
||||
|
11
packages/apidash_core/lib/utils/graphql_utils.dart
Normal file
11
packages/apidash_core/lib/utils/graphql_utils.dart
Normal file
@ -0,0 +1,11 @@
|
||||
import '../consts.dart';
|
||||
import '../models/models.dart';
|
||||
|
||||
String? getGraphQLBody(HttpRequestModel httpRequestModel) {
|
||||
if (httpRequestModel.hasQuery) {
|
||||
return kJsonEncoder.convert({
|
||||
"query": httpRequestModel.query,
|
||||
});
|
||||
}
|
||||
return null;
|
||||
}
|
@ -1,5 +1,8 @@
|
||||
import 'package:apidash_core/consts.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:seed/seed.dart';
|
||||
import '../models/models.dart';
|
||||
import 'graphql_utils.dart';
|
||||
|
||||
Map<String, String>? rowsToMap(
|
||||
List<NameValueModel>? kvRows, {
|
||||
@ -88,3 +91,13 @@ List<NameValueModel>? getEnabledRows(
|
||||
rows.where((element) => isRowEnabledList[rows.indexOf(element)]).toList();
|
||||
return finalRows == [] ? null : finalRows;
|
||||
}
|
||||
|
||||
String? getRequestBody(APIType type, HttpRequestModel httpRequestModel) {
|
||||
return switch (type) {
|
||||
APIType.rest =>
|
||||
(httpRequestModel.hasJsonData || httpRequestModel.hasTextData)
|
||||
? httpRequestModel.body
|
||||
: null,
|
||||
APIType.graphql => getGraphQLBody(httpRequestModel),
|
||||
};
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
export 'content_type_utils.dart';
|
||||
export 'graphql_utils.dart';
|
||||
export 'http_request_utils.dart';
|
||||
export 'http_response_utils.dart';
|
||||
export 'string_utils.dart';
|
||||
|
@ -21,7 +21,7 @@ dependencies:
|
||||
path: ../postman
|
||||
insomnia_collection:
|
||||
path: ../insomnia_collection
|
||||
seed: ^0.0.2
|
||||
seed: ^0.0.3
|
||||
xml: ^6.3.0
|
||||
|
||||
dev_dependencies:
|
||||
@ -30,4 +30,5 @@ dev_dependencies:
|
||||
build_runner: ^2.4.12
|
||||
flutter_lints: ^4.0.0
|
||||
freezed: ^2.5.7
|
||||
json_serializable: ^6.7.1
|
||||
test: ^1.25.2
|
||||
|
@ -1,6 +1,8 @@
|
||||
# melos_managed_dependency_overrides: seed,curl_parser
|
||||
# melos_managed_dependency_overrides: curl_parser,postman,seed
|
||||
dependency_overrides:
|
||||
curl_parser:
|
||||
path: ../curl_parser
|
||||
postman:
|
||||
path: ../postman
|
||||
seed:
|
||||
path: ../seed
|
||||
|
@ -24,6 +24,8 @@ final kColorHttpMethodPut = Colors.amber.shade900;
|
||||
final kColorHttpMethodPatch = kColorHttpMethodPut;
|
||||
final kColorHttpMethodDelete = Colors.red.shade800;
|
||||
|
||||
final kColorGQL = Colors.pink.shade600;
|
||||
|
||||
const kHintOpacity = 0.6;
|
||||
const kForegroundOpacity = 0.05;
|
||||
const kOverlayBackgroundOpacity = 0.5;
|
||||
|
@ -90,6 +90,7 @@ const kHSpacer10 = SizedBox(width: 10);
|
||||
const kHSpacer12 = SizedBox(width: 12);
|
||||
const kHSpacer20 = SizedBox(width: 20);
|
||||
const kHSpacer40 = SizedBox(width: 40);
|
||||
const kVSpacer3 = SizedBox(height: 3);
|
||||
const kVSpacer5 = SizedBox(height: 5);
|
||||
const kVSpacer8 = SizedBox(height: 8);
|
||||
const kVSpacer10 = SizedBox(height: 10);
|
||||
|
@ -10,6 +10,7 @@ class ADPopupMenu<T> extends StatelessWidget {
|
||||
this.tooltip,
|
||||
this.width,
|
||||
this.isOutlined = false,
|
||||
this.borderColor,
|
||||
});
|
||||
|
||||
final String? value;
|
||||
@ -18,6 +19,7 @@ class ADPopupMenu<T> extends StatelessWidget {
|
||||
final String? tooltip;
|
||||
final double? width;
|
||||
final bool isOutlined;
|
||||
final Color? borderColor;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -62,7 +64,8 @@ class ADPopupMenu<T> extends StatelessWidget {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
color: borderColor ??
|
||||
Theme.of(context).colorScheme.surfaceContainerHighest,
|
||||
),
|
||||
borderRadius: kBorderRadius8,
|
||||
),
|
||||
|
1
packages/curl_parser/.gitignore
vendored
1
packages/curl_parser/.gitignore
vendored
@ -30,3 +30,4 @@ build/
|
||||
|
||||
.vscode/
|
||||
coverage/
|
||||
pubspec_overrides.yaml
|
||||
|
@ -4,3 +4,4 @@ melos_curl_parser.iml
|
||||
build/
|
||||
coverage/
|
||||
test/
|
||||
pubspec_overrides.yaml
|
||||
|
@ -1,3 +1,7 @@
|
||||
## 0.1.2
|
||||
|
||||
- Bump dependencies.
|
||||
|
||||
## 0.1.1
|
||||
|
||||
- Add formdata support and new test cases.
|
||||
|
@ -1,6 +1,6 @@
|
||||
name: curl_parser
|
||||
description: Parse cURL command to Dart object and convert Dart object to cURL command.
|
||||
version: 0.1.1
|
||||
version: 0.1.2
|
||||
homepage: https://github.com/foss42/apidash/tree/main/packages/curl_parser
|
||||
repository: https://github.com/foss42/apidash/tree/main/packages/curl_parser
|
||||
issue_tracker: https://github.com/foss42/apidash/issues
|
||||
@ -19,7 +19,7 @@ environment:
|
||||
dependencies:
|
||||
args: ^2.5.0
|
||||
equatable: ^2.0.5
|
||||
seed: ^0.0.1
|
||||
seed: ^0.0.3
|
||||
shlex: ^2.0.2
|
||||
|
||||
dev_dependencies:
|
||||
|
@ -1,4 +0,0 @@
|
||||
# melos_managed_dependency_overrides: seed
|
||||
dependency_overrides:
|
||||
seed:
|
||||
path: ../seed
|
@ -1,3 +1,7 @@
|
||||
## 0.0.3
|
||||
|
||||
- Fix: Add `json_serializable` under dev_dependencies.
|
||||
|
||||
## 0.0.2
|
||||
|
||||
- Fix pubspec dependency for freezed_annotation.
|
||||
|
@ -1,6 +1,6 @@
|
||||
name: seed
|
||||
description: Seed is a foundational package designed to provide reusable building blocks for API Dash projects.
|
||||
version: 0.0.2
|
||||
version: 0.0.3
|
||||
homepage: https://github.com/foss42/apidash/tree/main/packages/seed
|
||||
repository: https://github.com/foss42/apidash/tree/main/packages/seed
|
||||
issue_tracker: https://github.com/foss42/apidash/issues
|
||||
@ -15,5 +15,6 @@ dependencies:
|
||||
dev_dependencies:
|
||||
build_runner: ^2.4.12
|
||||
freezed: ^2.5.7
|
||||
json_serializable: ^6.7.1
|
||||
lints: ^4.0.0
|
||||
test: ^1.24.0
|
||||
|
@ -306,7 +306,7 @@ packages:
|
||||
path: "packages/curl_parser"
|
||||
relative: true
|
||||
source: path
|
||||
version: "0.1.1"
|
||||
version: "0.1.2"
|
||||
dart_style:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@ -1379,10 +1379,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: seed
|
||||
sha256: "5c5ac5d73bf94e4b207d8c283903fc2b62ca8015e519b99949a8389605ee0eef"
|
||||
sha256: "0d74a46abd169c96a73d9dec4739e6623021915661beadf265e885bb1eafd214"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.0.2"
|
||||
version: "0.0.3"
|
||||
shared_preferences:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -1,7 +1,7 @@
|
||||
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.4.0+4
|
||||
version: 0.5.0+5
|
||||
|
||||
environment:
|
||||
sdk: ">=3.0.0 <4.0.0"
|
||||
|
@ -9,6 +9,7 @@ import 'http_response_models.dart';
|
||||
final historyMetaModel1 = HistoryMetaModel(
|
||||
historyId: 'historyId1',
|
||||
requestId: 'requestId1',
|
||||
apiType: APIType.rest,
|
||||
url: 'https://api.apidash.dev/humanize/social',
|
||||
method: HTTPVerb.get,
|
||||
timeStamp: DateTime(2024, 1, 1),
|
||||
@ -26,6 +27,7 @@ final historyRequestModel1 = HistoryRequestModel(
|
||||
final historyMetaModel2 = HistoryMetaModel(
|
||||
historyId: 'historyId2',
|
||||
requestId: 'requestId2',
|
||||
apiType: APIType.rest,
|
||||
url: 'https://api.apidash.dev/case/lower',
|
||||
method: HTTPVerb.post,
|
||||
timeStamp: DateTime(2024, 1, 1),
|
||||
@ -43,6 +45,7 @@ final historyRequestModel2 = HistoryRequestModel(
|
||||
final Map<String, dynamic> historyMetaModelJson1 = {
|
||||
"historyId": "historyId1",
|
||||
"requestId": "requestId1",
|
||||
"apiType": "rest",
|
||||
"name": "",
|
||||
"url": "https://api.apidash.dev/humanize/social",
|
||||
"method": "get",
|
||||
@ -60,6 +63,7 @@ final Map<String, dynamic> historyRequestModelJson1 = {
|
||||
final Map<String, dynamic> historyMetaModelJson2 = {
|
||||
"historyId": "historyId2",
|
||||
"requestId": "requestId2",
|
||||
"apiType": "rest",
|
||||
"name": "",
|
||||
"url": "https://api.apidash.dev/case/lower",
|
||||
"method": "post",
|
||||
|
@ -388,6 +388,7 @@ const httpRequestModelGet4Json = <String, dynamic>{
|
||||
"isParamEnabledList": null,
|
||||
"bodyContentType": "json",
|
||||
"body": null,
|
||||
"query": null,
|
||||
"formData": null
|
||||
};
|
||||
|
||||
@ -408,6 +409,7 @@ const httpRequestModelPost10Json = <String, dynamic>{
|
||||
"body": '''{
|
||||
"text": "I LOVE Flutter"
|
||||
}''',
|
||||
"query": null,
|
||||
'formData': [
|
||||
{'name': 'token', 'value': 'xyz', 'type': 'text'},
|
||||
{'name': 'imfile', 'value': '/Documents/up/1.png', 'type': 'file'}
|
||||
|
@ -16,6 +16,7 @@ void main() {
|
||||
test('Testing fromResponse', () async {
|
||||
(HttpResponse?, Duration?, String?)? responseRec = await request(
|
||||
requestModelGet1.id,
|
||||
requestModelGet1.apiType,
|
||||
requestModelGet1.httpRequestModel!,
|
||||
defaultUriScheme: kDefaultUriScheme,
|
||||
noSSL: false,
|
||||
@ -33,6 +34,7 @@ void main() {
|
||||
test('Testing fromResponse for contentType not Json', () async {
|
||||
(HttpResponse?, Duration?, String?)? responseRec = await request(
|
||||
requestModelGet13.id,
|
||||
requestModelGet1.apiType,
|
||||
requestModelGet13.httpRequestModel!,
|
||||
defaultUriScheme: kDefaultUriScheme,
|
||||
noSSL: false,
|
||||
@ -48,6 +50,7 @@ void main() {
|
||||
test('Testing fromResponse for Bad SSL with certificate check', () async {
|
||||
(HttpResponse?, Duration?, String?)? responseRec = await request(
|
||||
requestModelGetBadSSL.id,
|
||||
requestModelGet1.apiType,
|
||||
requestModelGetBadSSL.httpRequestModel!,
|
||||
defaultUriScheme: kDefaultUriScheme,
|
||||
noSSL: false,
|
||||
@ -59,6 +62,7 @@ void main() {
|
||||
test('Testing fromResponse for Bad SSL with no certificate check', () async {
|
||||
(HttpResponse?, Duration?, String?)? responseRec = await request(
|
||||
requestModelGetBadSSL.id,
|
||||
requestModelGet1.apiType,
|
||||
requestModelGetBadSSL.httpRequestModel!,
|
||||
defaultUriScheme: kDefaultUriScheme,
|
||||
noSSL: true,
|
||||
|
@ -56,20 +56,19 @@ const activeEnvVars = [
|
||||
|
||||
void main() {
|
||||
group("Testing getEnvironmentTitle function", () {
|
||||
String titleUntitled = "untitled";
|
||||
test("Testing getEnvironmentTitle with null", () {
|
||||
String? envName1;
|
||||
expect(getEnvironmentTitle(envName1), titleUntitled);
|
||||
expect(getEnvironmentTitle(envName1), kUntitled);
|
||||
});
|
||||
|
||||
test("Testing getEnvironmentTitle with empty string", () {
|
||||
String envName2 = "";
|
||||
expect(getEnvironmentTitle(envName2), titleUntitled);
|
||||
expect(getEnvironmentTitle(envName2), kUntitled);
|
||||
});
|
||||
|
||||
test("Testing getEnvironmentTitle with trimmable string", () {
|
||||
String envName3 = " ";
|
||||
expect(getEnvironmentTitle(envName3), titleUntitled);
|
||||
expect(getEnvironmentTitle(envName3), kUntitled);
|
||||
});
|
||||
|
||||
test("Testing getEnvironmentTitle with non-empty string", () {
|
||||
|
@ -5,15 +5,14 @@ import 'package:apidash/consts.dart';
|
||||
|
||||
void main() {
|
||||
group("Testing getRequestTitleFromUrl function", () {
|
||||
String titleUntitled = "untitled";
|
||||
test('Testing getRequestTitleFromUrl using url1', () {
|
||||
String url1 = "";
|
||||
expect(getRequestTitleFromUrl(url1), titleUntitled);
|
||||
expect(getRequestTitleFromUrl(url1), kUntitled);
|
||||
});
|
||||
|
||||
test('Testing getRequestTitleFromUrl using url2', () {
|
||||
String url2 = " ";
|
||||
expect(getRequestTitleFromUrl(url2), titleUntitled);
|
||||
expect(getRequestTitleFromUrl(url2), kUntitled);
|
||||
});
|
||||
|
||||
test('Testing getRequestTitleFromUrl using url3', () {
|
||||
@ -30,11 +29,11 @@ void main() {
|
||||
|
||||
test('Testing getRequestTitleFromUrl using url5', () {
|
||||
String url5 = "http://";
|
||||
expect(getRequestTitleFromUrl(url5), titleUntitled);
|
||||
expect(getRequestTitleFromUrl(url5), kUntitled);
|
||||
});
|
||||
|
||||
test('Testing getRequestTitleFromUrl for null value', () {
|
||||
expect(getRequestTitleFromUrl(null), titleUntitled);
|
||||
expect(getRequestTitleFromUrl(null), kUntitled);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -13,6 +13,7 @@ void main() {
|
||||
final mockModel = HistoryMetaModel(
|
||||
historyId: 'historyId',
|
||||
requestId: 'requestId',
|
||||
apiType: APIType.rest,
|
||||
url: 'https://api.apidash.dev',
|
||||
method: HTTPVerb.get,
|
||||
timeStamp: DateTime.now(),
|
||||
|
@ -11,6 +11,7 @@ void main() {
|
||||
HistoryMetaModel(
|
||||
historyId: 'historyId',
|
||||
requestId: 'requestId',
|
||||
apiType: APIType.rest,
|
||||
url: 'https://api.apidash.dev',
|
||||
method: HTTPVerb.get,
|
||||
timeStamp: DateTime.now(),
|
||||
@ -30,6 +31,7 @@ void main() {
|
||||
SidebarHistoryCard(
|
||||
id: '1',
|
||||
models: sampleModels,
|
||||
apiType: APIType.rest,
|
||||
method: HTTPVerb.get,
|
||||
onTap: () {
|
||||
changedValue = 'Tapped';
|
||||
@ -68,6 +70,7 @@ void main() {
|
||||
children: [
|
||||
SidebarHistoryCard(
|
||||
id: '1',
|
||||
apiType: APIType.rest,
|
||||
models: sampleModels,
|
||||
method: HTTPVerb.get,
|
||||
onTap: () {
|
||||
|
@ -16,6 +16,7 @@ void main() {
|
||||
children: [
|
||||
SidebarRequestCard(
|
||||
id: '23',
|
||||
apiType: APIType.rest,
|
||||
selectedId: '2',
|
||||
url: 'https://api.apidash.dev',
|
||||
method: HTTPVerb.get,
|
||||
@ -61,6 +62,7 @@ void main() {
|
||||
children: [
|
||||
SidebarRequestCard(
|
||||
id: '2',
|
||||
apiType: APIType.rest,
|
||||
selectedId: '2',
|
||||
editRequestId: '2',
|
||||
url: 'https://api.apidash.dev',
|
||||
|
@ -1,3 +1,4 @@
|
||||
import 'package:apidash/consts.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
@ -28,7 +29,7 @@ void main() {
|
||||
|
||||
expect(find.byType(TextFormField), findsOneWidget);
|
||||
expect(find.byKey(const Key("2")), findsOneWidget);
|
||||
expect(find.text('Enter content (body)'), findsOneWidget);
|
||||
expect(find.text(kHintContent), findsOneWidget);
|
||||
var txtForm = find.byKey(const Key("2"));
|
||||
await tester.enterText(txtForm, 'entering 123 for testing content body');
|
||||
await tester.pump();
|
||||
@ -66,7 +67,7 @@ void main() {
|
||||
expect(find.text('initial'), findsOneWidget);
|
||||
expect(find.byType(TextFormField), findsOneWidget);
|
||||
expect(find.byKey(const Key("2")), findsOneWidget);
|
||||
expect(find.text('Enter content (body)'), findsOneWidget);
|
||||
expect(find.text(kHintContent), findsOneWidget);
|
||||
var txtForm = find.byKey(const Key("2"));
|
||||
await tester.enterText(txtForm, 'entering 123 for testing content body');
|
||||
await tester.pump();
|
||||
|
@ -1,6 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:apidash/widgets/request_widgets.dart';
|
||||
import 'package:apidash/widgets/request_pane.dart';
|
||||
import '../extensions/widget_tester_extensions.dart';
|
||||
import '../test_consts.dart';
|
||||
|
||||
@ -15,8 +15,9 @@ void main() {
|
||||
body: RequestPane(
|
||||
selectedId: '1',
|
||||
codePaneVisible: true,
|
||||
children: const [Text('abc'), Text('xyz'), Text('mno')],
|
||||
tabLabels: const ['URL Params', 'Headers', 'Body'],
|
||||
onPressedCodeButton: () {},
|
||||
children: const [Text('abc'), Text('xyz'), Text('mno')],
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -47,6 +48,7 @@ void main() {
|
||||
codePaneVisible: true,
|
||||
onPressedCodeButton: () {},
|
||||
tabIndex: 1,
|
||||
tabLabels: const ['URL Params', 'Headers', 'Body'],
|
||||
children: const [Text('abc'), Text('xyz'), Text('mno')],
|
||||
),
|
||||
),
|
||||
@ -78,6 +80,7 @@ void main() {
|
||||
codePaneVisible: false,
|
||||
onPressedCodeButton: () {},
|
||||
tabIndex: 2,
|
||||
tabLabels: const ['URL Params', 'Headers', 'Body'],
|
||||
children: const [Text('abc'), Text('xyz'), Text('mno')],
|
||||
),
|
||||
),
|
||||
@ -111,6 +114,7 @@ void main() {
|
||||
onTapTabBar: (value) {
|
||||
computedTabIndex = value;
|
||||
},
|
||||
tabLabels: const ['URL Params', 'Headers', 'Body'],
|
||||
children: const [Text('abc'), Text('xyz'), Text('mno')],
|
||||
),
|
||||
),
|
@ -8,12 +8,16 @@ import 'package:apidash/widgets/texts.dart';
|
||||
void main() {
|
||||
testWidgets('Testing when method is GET', (tester) async {
|
||||
var methodGet = HTTPVerb.get;
|
||||
var apiType = APIType.rest;
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
title: 'Texts',
|
||||
theme: ThemeData(brightness: Brightness.light),
|
||||
home: Scaffold(
|
||||
body: MethodBox(method: methodGet),
|
||||
body: SidebarRequestCardTextBox(
|
||||
apiType: apiType,
|
||||
method: methodGet,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
@ -28,12 +32,16 @@ void main() {
|
||||
|
||||
testWidgets('Testing when method is DELETE', (tester) async {
|
||||
var methodDel = HTTPVerb.delete;
|
||||
var apiType = APIType.rest;
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
title: 'Texts',
|
||||
theme: ThemeData(brightness: Brightness.dark),
|
||||
home: Scaffold(
|
||||
body: MethodBox(method: methodDel),
|
||||
body: SidebarRequestCardTextBox(
|
||||
apiType: apiType,
|
||||
method: methodDel,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
Reference in New Issue
Block a user