Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
cb3da14
feat(explorer): initialize API Explorer layout with header and search…
BalaSubramaniam12007 Apr 19, 2025
4c9c9a0
feat: add API template model, card UI, and template loading service
BalaSubramaniam12007 Apr 19, 2025
2cf06df
feat: Add DescriptionPage with navigable UI from ExplorerPage
BalaSubramaniam12007 Apr 20, 2025
005ff58
(Feat)(Fix): Reconstruct explorer_model to use RequestModel, pass Api…
BalaSubramaniam12007 Apr 21, 2025
1ecaa58
Add RequestCard, rename MethodPane to RequestsPane, update Descriptio…
BalaSubramaniam12007 Apr 21, 2025
b47f213
feat: add UrlCard widget displaying method, non-editable URL, and Sen…
BalaSubramaniam12007 Apr 22, 2025
38a90fb
feat: add ExplorerSplitView for resizable request and description panes
BalaSubramaniam12007 Apr 22, 2025
bb4ceb7
feat: create reusable MethodChip widget and integrate with UrlCard
BalaSubramaniam12007 Apr 22, 2025
369121b
fix:reslove the assets bug
BalaSubramaniam12007 Apr 22, 2025
5db9435
Enhance mock json data
BalaSubramaniam12007 Apr 27, 2025
9ec3e55
fix bugs and refactor the files
BalaSubramaniam12007 Apr 27, 2025
8d3f118
Refactor chip components into reusable CustomChip
BalaSubramaniam12007 Apr 27, 2025
7833472
feat: add CustomChip.tag factory method and update TemplateCard tag d…
BalaSubramaniam12007 Apr 27, 2025
e862c13
feat: add support to import requests from templates and optimize perf…
BalaSubramaniam12007 Apr 27, 2025
0bfdc19
Add navigation to HomePage after importing request from ExplorerPage
BalaSubramaniam12007 Apr 28, 2025
f1e7446
Add SnackBar notification for successful request import in UrlCard
BalaSubramaniam12007 Apr 28, 2025
713826b
resuse the getSnackBar widget
BalaSubramaniam12007 Apr 28, 2025
10dc7a6
(fix)Responsive TemplateCard
BalaSubramaniam12007 Apr 28, 2025
fcaf3ad
Add method parameter to RequestPane to display HTTP method chip on th…
BalaSubramaniam12007 Apr 28, 2025
51e6c71
Merge branch 'foss42:main' into api_explorer
BalaSubramaniam12007 Apr 28, 2025
70064ed
(doc) : add helper readme file
BalaSubramaniam12007 Apr 28, 2025
36c0c8c
Create create-release.yml
BalaSubramaniam12007 Apr 29, 2025
c12484c
(fix) dir bugs
BalaSubramaniam12007 Apr 29, 2025
a21325f
(feat:explorer) Add GitHub template fetching and Riverpod state manag…
BalaSubramaniam12007 Apr 29, 2025
0574834
(feat:explorer) Integrate Hive for template caching with append strategy
BalaSubramaniam12007 Apr 29, 2025
961cc9a
release test
BalaSubramaniam12007 Apr 30, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions .github/workflows/create-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: Release API Templates

on:
push:
paths:
- 'lib/screens/explorer/api_templates/**'

permissions:
contents: write

jobs:
release:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2

- name: Zip API Templates
run: zip -r api_templates.zip lib/screens/explorer/api_templates

- name: Create Release
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: templates-${{ github.sha }}
release_name: API Templates Release ${{ github.sha }}
draft: false
prerelease: false

- name: Upload Release Asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./api_templates.zip
asset_name: api_templates.zip
asset_content_type: application/zip
59 changes: 59 additions & 0 deletions lib/models/explorer_model.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import 'models.dart';

class ApiTemplate {
final Info info;
final List requests;

ApiTemplate({required this.info, required this.requests});

/// Parses JSON data into an ApiTemplate object.
factory ApiTemplate.fromJson(Map<String, dynamic> json) {
return ApiTemplate(
info: Info.fromJson(json['info'] ?? {}),
requests: (json['requests'] as List?)
?.map((request) => RequestModel.fromJson(request))
.toList() ??
[],
);
}

/// Converts the ApiTemplate back to JSON.
Map<String, dynamic> toJson() {
return {
'info': info.toJson(),
'requests': requests.map((request) => request.toJson()).toList(),
};
}
}

/// Represents metadata (e.g., title, description, tags).
class Info {
final String title;
final String description;
final List<String> tags;

Info({
required this.title,
required this.description,
required this.tags,
});

/// Parses JSON data into an Info object.
/// Future extensions: Add fields like category, version, or lastUpdated.
factory Info.fromJson(Map<String, dynamic> json) {
return Info(
title: json['title'] ?? 'Untitled',
description: json['description'] ?? 'No description',
tags: List<String>.from(json['tags'] ?? []),
);
}

/// Converts the Info object back to JSON.
Map<String, dynamic> toJson() {
return {
'title': title,
'description': description,
'tags': tags,
};
}
}
1 change: 1 addition & 0 deletions lib/models/models.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export 'history_meta_model.dart';
export 'history_request_model.dart';
export 'request_model.dart';
export 'settings_model.dart';
export 'explorer_model.dart';
77 changes: 77 additions & 0 deletions lib/providers/templates_provider.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:apidash/models/models.dart';
import 'package:apidash/services/templates_service.dart';

class TemplatesState {
final List<ApiTemplate> templates;
final bool isLoading;
final String? error;
final bool isCached;

TemplatesState({
this.templates = const [],
this.isLoading = false,
this.error,
this.isCached = false,
});

TemplatesState copyWith({
List<ApiTemplate>? templates,
bool? isLoading,
String? error,
bool? isCached,
}) {
return TemplatesState(
templates: templates ?? this.templates,
isLoading: isLoading ?? this.isLoading,
error: error ?? this.error,
isCached: isCached ?? this.isCached,
);
}
}

class TemplatesNotifier extends StateNotifier<TemplatesState> {
TemplatesNotifier() : super(TemplatesState()) {
loadInitialTemplates();
}

Future<void> loadInitialTemplates() async {
state = state.copyWith(isLoading: true, error: null);
try {
final templates = await TemplatesService.loadTemplates();
final isCached = await TemplatesService.hasCachedTemplates();
state = state.copyWith(
templates: templates,
isLoading: false,
isCached: isCached,
);
} catch (e) {
state = state.copyWith(
templates: [],
isLoading: false,
error: 'Failed to load templates: $e',
);
}
}

Future<void> fetchTemplatesFromGitHub() async {
state = state.copyWith(isLoading: true, error: null);
try {
final templates = await TemplatesService.fetchTemplatesFromGitHub();
state = state.copyWith(
templates: templates,
isLoading: false,
isCached: true,
);
} catch (e) {
state = state.copyWith(
isLoading: false,
error: 'Failed to fetch templates: $e',
);
}
}
}

final templatesProvider = StateNotifierProvider<TemplatesNotifier, TemplatesState>(
(ref) => TemplatesNotifier(),
);
19 changes: 17 additions & 2 deletions lib/screens/dashboard.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import 'common_widgets/common_widgets.dart';
import 'envvar/environment_page.dart';
import 'home_page/home_page.dart';
import 'history/history_page.dart';
import 'explorer/explorer_page.dart';
import 'settings_page.dart';

class Dashboard extends ConsumerWidget {
Expand Down Expand Up @@ -68,6 +69,19 @@ class Dashboard extends ConsumerWidget {
'History',
style: Theme.of(context).textTheme.labelSmall,
),
kVSpacer10,
IconButton(
isSelected: railIdx == 3,
onPressed: () {
ref.read(navRailIndexStateProvider.notifier).state = 3;
},
icon: const Icon(Icons.explore_outlined),
selectedIcon: const Icon(Icons.explore),
),
Text(
'Explorer',
style: Theme.of(context).textTheme.labelSmall,
),
],
),
Expanded(
Expand All @@ -92,7 +106,7 @@ class Dashboard extends ConsumerWidget {
padding: const EdgeInsets.only(bottom: 16.0),
child: NavbarButton(
railIdx: railIdx,
buttonIdx: 3,
buttonIdx: 4,
selectedIcon: Icons.settings,
icon: Icons.settings_outlined,
label: 'Settings',
Expand All @@ -118,7 +132,8 @@ class Dashboard extends ConsumerWidget {
HomePage(),
EnvironmentPage(),
HistoryPage(),
SettingsPage(),
ExplorerPage(), // Added ExplorerPage at index 3
SettingsPage(), // Shifted to index 4
],
),
)
Expand Down
113 changes: 113 additions & 0 deletions lib/screens/explorer/api_templates/mock/blog_post.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
{
"info": {
"title": "Blog Post API",
"description": "API for managing blog posts",
"tags": ["blog", "posts"]
},
"requests": [
{
"id": "post_create",
"apiType": "rest",
"name": "Create post",
"description": "Create a new blog post",
"httpRequestModel": {
"method": "post",
"url": "https://api.example.com/v1/posts",
"headers": [
{"name": "Content-Type", "value": "application/json"}
],
"params": [],
"isHeaderEnabledList": [true],
"isParamEnabledList": [],
"bodyContentType": "json",
"body": "{\"title\":\"New Post\",\"content\":\"Content here\"}",
"query": null,
"formData": null
},
"responseStatus": 201,
"message": "Post created",
"httpResponseModel": {
"statusCode": 201,
"headers": {
"Content-Type": "application/json",
"Content-Length": "89"
},
"requestHeaders": {
"Content-Type": "application/json"
},
"body": "{\"id\":1,\"title\":\"New Post\",\"content\":\"Content here\",\"created_at\":\"2025-04-25T10:30:00Z\"}",
"formattedBody": "{\n \"id\": 1,\n \"title\": \"New Post\",\n \"content\": \"Content here\",\n \"created_at\": \"2025-04-25T10:30:00Z\"\n}",
"time": 240000
},
"isWorking": false,
"sendingTime": null
},
{
"id": "post_get_all",
"apiType": "rest",
"name": "List posts",
"description": "Get all blog posts",
"httpRequestModel": {
"method": "get",
"url": "https://api.example.com/v1/posts",
"headers": [],
"params": [
{"name": "limit", "value": "10"}
],
"isHeaderEnabledList": [],
"isParamEnabledList": [true],
"bodyContentType": "json",
"body": null,
"query": "limit=10",
"formData": null
},
"responseStatus": 200,
"message": "Success",
"httpResponseModel": {
"statusCode": 200,
"headers": {
"Content-Type": "application/json",
"Content-Length": "126"
},
"requestHeaders": {},
"body": "{\"data\":[{\"id\":1,\"title\":\"New Post\"},{\"id\":2,\"title\":\"Another Post\"}],\"meta\":{\"total\":2}}",
"formattedBody": "{\n \"data\": [\n {\n \"id\": 1,\n \"title\": \"New Post\"\n },\n {\n \"id\": 2,\n \"title\": \"Another Post\"\n }\n ],\n \"meta\": {\n \"total\": 2\n }\n}",
"time": 150000
},
"isWorking": false,
"sendingTime": null
},
{
"id": "post_delete",
"apiType": "rest",
"name": "Delete post",
"description": "Delete a post by ID",
"httpRequestModel": {
"method": "delete",
"url": "https://api.example.com/v1/posts/1",
"headers": [],
"params": [],
"isHeaderEnabledList": [],
"isParamEnabledList": [],
"bodyContentType": "json",
"body": null,
"query": null,
"formData": null
},
"responseStatus": 204,
"message": "Deleted",
"httpResponseModel": {
"statusCode": 204,
"headers": {
"Content-Length": "0"
},
"requestHeaders": {},
"body": "",
"formattedBody": "",
"time": 110000
},
"isWorking": false,
"sendingTime": null
}
]
}
Loading