Skip to content

Commit b858ed2

Browse files
authored
Merge branch 'main' into resolve-issue-history-clear
2 parents 1deacc6 + b54074a commit b858ed2

File tree

10 files changed

+209
-29
lines changed

10 files changed

+209
-29
lines changed
56.9 KB
Loading
99.7 KB
Loading
86.9 KB
Loading
86.8 KB
Loading

doc/dev_guide/packaging.md

Lines changed: 172 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
# Packaging API Dash
22

3+
- [Windows](#windows)
4+
- [macOS](#macos)
5+
- [Linux Debian (.deb) & RPM (.rpm)](#linux-debian-deb--rpm-rpm)
6+
- [Arch Linux (PKGBUILD)](#arch-linux-pkgbuild)
7+
- [FlatHub (Flatpak)](#flathub-flatpak)
8+
- [Homebrew](#homebrew)
9+
- [Chocolatey](#chocolatey)
10+
- [WinGet](#winget)
11+
312
## Windows
413

514
[Packaging and Distributing Flutter Desktop Apps : Creating Windows .exe installer](https://medium.com/@fluttergems/packaging-and-distributing-flutter-desktop-apps-the-missing-guide-for-open-source-indie-0b468d5e9e70)
@@ -22,11 +31,172 @@ TODO Instructions
2231

2332
## Homebrew
2433

25-
TODO Instructions
34+
Homebrew Formula Submission
35+
36+
### 1. Prepare Tap Repository
37+
38+
```
39+
# Create Homebrew tap
40+
gh repo create homebrew-tap --public --clone
41+
mkdir -p homebrew-tap/Formula
42+
cd homebrew-tap
43+
```
44+
45+
### 2. Package apidash
46+
47+
```
48+
# Build macOS bundle
49+
flutter build macos
50+
51+
# Create versioned tarball
52+
tar -czvf apidash-v1.0.0.tar.gz \
53+
-C build/macos/Build/Products/Release/ \
54+
Apidash.app
55+
56+
# Generate SHA256 checksum
57+
shasum -a 256 apidash-v1.0.0.tar.gz
58+
```
59+
60+
### 3. Create Formula File
61+
62+
`Formula/apidash.rb`:
63+
64+
```
65+
class Apidash < Formula
66+
desc "Modern API dashboard for developers"
67+
homepage "https://apidash.dev"
68+
url "https://github.com/<user>/<repo>/releases/download/v1.0.0/apidash-v1.0.0.tar.gz"
69+
sha256 "PASTE_YOUR_SHA256_HERE"
70+
71+
def install
72+
prefix.install "Apidash.app"
73+
bin.write_exec_script prefix/"Apidash.app/Contents/MacOS/Apidash"
74+
end
75+
76+
test do
77+
system "#{bin}/Apidash", "--version"
78+
end
79+
end
80+
```
81+
82+
### 4. Local Validation
83+
84+
```
85+
# Check formula syntax
86+
brew audit --strict Formula/apidash.rb
87+
88+
# Test installation
89+
brew install --build-from-source Formula/apidash.rb
90+
91+
# Verify execution
92+
brew test apidash
93+
```
94+
95+
### 5. Custom Tap Submission
96+
97+
```
98+
# Commit formula to your tap repo
99+
git add Formula/Apidash.rb
100+
git commit -m "added apidash formula"
101+
git push
102+
103+
# Create release for tarball
104+
gh release create v1.0.0 apidash-v1.0.0.tar.gz
105+
```
106+
107+
### 6. Installation
108+
109+
```
110+
brew tap homebrew-tap/Formula
111+
brew install apidash
112+
```
26113

27114
## Chocolatey
28115

29-
TODO Instructions
116+
### Step 1: Setup Skeleton
117+
118+
First step towards making a choco package is initializing a base.
119+
120+
The command `choco new -h` can teach you more about the `new` command, its usage, options, switches, and exit codes.
121+
122+
Run the following command to setup the base
123+
124+
```powershell
125+
choco new --name="apidash" --version="0.3.0" maintainername="foss42" maintainerrepo="https://github.com/foss42/apidash" --built-in-template
126+
```
127+
128+
![choco folder structure](images/choco_create_structure.png)
129+
130+
This creates the following folder structure
131+
132+
```
133+
apidash
134+
├── ReadMe.md
135+
├── _TODO.txt
136+
├── apidash.nuspec
137+
└── tools
138+
├── chocolateybeforemodify.ps1
139+
├── chocolateyinstall.ps1
140+
├── chocolateyuninstall.ps1
141+
├── LICENSE.txt
142+
└── VERIFICATION.txt
143+
```
144+
145+
The files `ReadMe.md` and `_TODO.md` can be deleted before pushing.
146+
147+
The files of our main interest are `chocolateyinstall.ps1` and `apidash.nuspec`.
148+
149+
### Step 2: Editing `chocolateyinstall.ps1`
150+
151+
Take a look at `chocolateyinstall.ps1` file. There are many comments stating the use case of each line itself.
152+
![chocolatelyinstall.ps1](images/choco_chocolateyinstall_ps1.png)
153+
154+
Comments can bre remoed using the following command.
155+
```powershell
156+
$f='apidash\tools\chocolateyinstall.ps1'
157+
gc $f | ? {$_ -notmatch "^\s*#"} | % {$_ -replace '(^.*?)\s*? [^``]#.*','$1'} | Out-File $f+".~" -en utf8; mv -fo $f+".~" $f
158+
```
159+
160+
Now our `chocolateyinstall.ps1` file is ready.
161+
162+
### Step 3: Editing `apidash.nuspec`
163+
164+
![final apidash.nuspec](images/choco_nuspec.png)
165+
166+
### Step 4: Build the package
167+
168+
All our files are ready, we just need to pack out files in a choco package with the extension `.nupkg`.
169+
170+
Run the following command from the root of your directory:
171+
```powershell
172+
choco pack
173+
```
174+
This command generates the `apidash.0.3.0.nupkg` file.
175+
176+
### Step 5: Test the Package Locally
177+
178+
Install the package locally using Chocolatey:
179+
```powershell
180+
choco install apidash -s .
181+
```
182+
Ensure the application installs correctly.
183+
184+
![Shell output](images/choco_shell_output.png)
185+
186+
### Step 6: Pre-Publishing - Update `LICENSE.txt` & `VERIFICATION.txt`
187+
188+
Update `LICENSE.txt` with the actual **LICENSE **and `VERIFICATION.txt` accordingly.
189+
190+
### Step 7: Publish the Package (Optional)
191+
192+
To share the package, you can push it to a Chocolatey repository. For the official Chocolatey Community Repository, follow these steps:
193+
194+
1. Create an account on the Chocolatey Community.
195+
2. Get an API key by navigating to your profile.
196+
3. Use the following command to push your package:
197+
```powershell
198+
choco push apidash.0.3.0.nupkg --source="https://push.chocolatey.org/" --api-key="YOUR_API_KEY"
199+
```
30200

31201
## WinGet
32202

lib/providers/collection_providers.dart

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,22 +25,18 @@ final requestSequenceProvider = StateProvider<List<String>>((ref) {
2525
return ids ?? [];
2626
});
2727

28-
final httpClientManager = HttpClientManager();
29-
3028
final StateNotifierProvider<CollectionStateNotifier, Map<String, RequestModel>?>
3129
collectionStateNotifierProvider =
3230
StateNotifierProvider((ref) => CollectionStateNotifier(
3331
ref,
3432
hiveHandler,
35-
httpClientManager,
3633
));
3734

3835
class CollectionStateNotifier
3936
extends StateNotifier<Map<String, RequestModel>?> {
4037
CollectionStateNotifier(
4138
this.ref,
4239
this.hiveHandler,
43-
this.httpClientManager,
4440
) : super(null) {
4541
var status = loadData();
4642
Future.microtask(() {
@@ -57,7 +53,6 @@ class CollectionStateNotifier
5753
final Ref ref;
5854
final HiveHandler hiveHandler;
5955
final baseHttpResponseModel = const HttpResponseModel();
60-
final HttpClientManager httpClientManager;
6156

6257
bool hasId(String id) => state?.keys.contains(id) ?? false;
6358

@@ -117,6 +112,7 @@ class CollectionStateNotifier
117112
final rId = id ?? ref.read(selectedIdStateProvider);
118113
var itemIds = ref.read(requestSequenceProvider);
119114
int idx = itemIds.indexOf(rId!);
115+
cancelHttpRequest(rId);
120116
itemIds.remove(rId);
121117
ref.read(requestSequenceProvider.notifier).state = [...itemIds];
122118

@@ -293,7 +289,7 @@ class CollectionStateNotifier
293289
state = map;
294290

295291
bool noSSL = ref.read(settingsProvider).isSSLDisabled;
296-
(HttpResponse?, Duration?, String?)? responseRec = await request(
292+
var responseRec = await sendHttpRequest(
297293
requestId,
298294
apiType,
299295
substitutedHttpRequestModel,
@@ -349,7 +345,7 @@ class CollectionStateNotifier
349345

350346
void cancelRequest() {
351347
final id = ref.read(selectedIdStateProvider);
352-
httpClientManager.cancelRequest(id);
348+
cancelHttpRequest(id);
353349
unsave();
354350
}
355351

lib/providers/history_providers.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,4 +89,11 @@ class HistoryMetaStateNotifier
8989
await hiveHandler.setHistoryRequest(id, model.toJson());
9090
await loadHistoryRequest(id);
9191
}
92+
93+
Future<void> clearAllHistory() async {
94+
await hiveHandler.clearAllHistory();
95+
ref.read(selectedHistoryIdStateProvider.notifier).state = null;
96+
ref.read(selectedHistoryRequestModelProvider.notifier).state = null;
97+
loadHistoryMetas();
98+
}
9299
}

lib/screens/history/history_widgets/his_sidebar_header.dart

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
import 'package:apidash/services/services.dart';
2-
import 'package:apidash_design_system/apidash_design_system.dart';
31
import 'package:flutter/material.dart';
42
import 'package:hooks_riverpod/hooks_riverpod.dart';
3+
import 'package:apidash_design_system/apidash_design_system.dart';
54
import 'package:apidash/providers/providers.dart';
65
import 'package:apidash/widgets/widgets.dart';
7-
import 'package:apidash/consts.dart';
6+
import '../../../consts.dart';
87

98
class HistorySidebarHeader extends ConsumerWidget {
109
const HistorySidebarHeader({super.key});
@@ -23,12 +22,12 @@ class HistorySidebarHeader extends ConsumerWidget {
2322
),
2423
const Spacer(),
2524
ADIconButton(
26-
icon: Icons.delete_forever,
27-
iconSize: kButtonIconSizeLarge,
28-
tooltip: "Clear History",
29-
color: Theme.of(context).brightness == Brightness.dark
30-
? kColorDarkDanger
31-
: kColorLightDanger,
25+
icon: Icons.delete_forever,
26+
iconSize: kButtonIconSizeLarge,
27+
tooltip: "Clear History",
28+
color: Theme.of(context).brightness == Brightness.dark
29+
? kColorDarkDanger
30+
: kColorLightDanger,
3231
onPressed: () {
3332
showDialog(
3433
context: context,
@@ -43,7 +42,9 @@ class HistorySidebarHeader extends ConsumerWidget {
4342
TextButton(
4443
onPressed: () async {
4544
try {
46-
await hiveHandler.clearAllHistory(ref); // ✅ Pass `ref` here
45+
await ref
46+
.read(historyMetaStateNotifier.notifier)
47+
.clearAllHistory();
4748

4849
if (context.mounted) {
4950
Navigator.pop(context);

packages/apidash_core/lib/services/http_service.dart

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,16 @@ import 'http_client_manager.dart';
1010

1111
typedef HttpResponse = http.Response;
1212

13-
Future<(HttpResponse?, Duration?, String?)> request(
13+
final httpClientManager = HttpClientManager();
14+
15+
Future<(HttpResponse?, Duration?, String?)> sendHttpRequest(
1416
String requestId,
1517
APIType apiType,
1618
HttpRequestModel requestModel, {
1719
SupportedUriSchemes defaultUriScheme = kDefaultUriScheme,
1820
bool noSSL = false,
1921
}) async {
20-
final clientManager = HttpClientManager();
21-
final client = clientManager.createClient(requestId, noSSL: noSSL);
22+
final client = httpClientManager.createClient(requestId, noSSL: noSSL);
2223

2324
(Uri?, String?) uriRec = getValidRequestUri(
2425
requestModel.url,
@@ -123,14 +124,19 @@ Future<(HttpResponse?, Duration?, String?)> request(
123124
stopwatch.stop();
124125
return (response, stopwatch.elapsed, null);
125126
} catch (e) {
126-
if (clientManager.wasRequestCancelled(requestId)) {
127+
if (httpClientManager.wasRequestCancelled(requestId)) {
127128
return (null, null, kMsgRequestCancelled);
128129
}
129130
return (null, null, e.toString());
130131
} finally {
131-
clientManager.closeClient(requestId);
132+
httpClientManager.closeClient(requestId);
132133
}
133134
} else {
134135
return (null, null, uriRec.$2);
135136
}
136-
}
137+
}
138+
139+
void cancelHttpRequest(String? requestId) {
140+
httpClientManager.cancelRequest(requestId);
141+
}
142+

test/models/response_model_test.dart

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ void main() {
1414
});
1515

1616
test('Testing fromResponse', () async {
17-
(HttpResponse?, Duration?, String?)? responseRec = await request(
17+
var responseRec = await sendHttpRequest(
1818
requestModelGet1.id,
1919
requestModelGet1.apiType,
2020
requestModelGet1.httpRequestModel!,
@@ -32,7 +32,7 @@ void main() {
3232
});
3333

3434
test('Testing fromResponse for contentType not Json', () async {
35-
(HttpResponse?, Duration?, String?)? responseRec = await request(
35+
var responseRec = await sendHttpRequest(
3636
requestModelGet13.id,
3737
requestModelGet1.apiType,
3838
requestModelGet13.httpRequestModel!,
@@ -48,7 +48,7 @@ void main() {
4848
});
4949

5050
test('Testing fromResponse for Bad SSL with certificate check', () async {
51-
(HttpResponse?, Duration?, String?)? responseRec = await request(
51+
var responseRec = await sendHttpRequest(
5252
requestModelGetBadSSL.id,
5353
requestModelGet1.apiType,
5454
requestModelGetBadSSL.httpRequestModel!,
@@ -60,7 +60,7 @@ void main() {
6060
});
6161

6262
test('Testing fromResponse for Bad SSL with no certificate check', () async {
63-
(HttpResponse?, Duration?, String?)? responseRec = await request(
63+
var responseRec = await sendHttpRequest(
6464
requestModelGetBadSSL.id,
6565
requestModelGet1.apiType,
6666
requestModelGetBadSSL.httpRequestModel!,

0 commit comments

Comments
 (0)