Skip to content

Commit

Permalink
Add GIT integration
Browse files Browse the repository at this point in the history
Fixes foss42#502

Implement GIT integration to push/pull collections with version history.

* **Add GIT Services**: Create `lib/services/git_services.dart` to handle push/export and pull/import functionalities.
* **Settings Page**: Modify `lib/screens/settings_page.dart` to include GIT configuration settings (Token, Repository) and add a dialog for GIT settings.
* **Collection Pane**: Update `lib/screens/home_page/collection_pane.dart` to add sync buttons for GIT push and pull actions.
* **Collection Providers**: Modify `lib/providers/collection_providers.dart` to add methods for handling GIT push and pull actions.
* **Constants**: Update `lib/consts.dart` to add constants for GIT settings.
* **Settings Model**: Modify `lib/models/settings_model.dart` to include fields for GIT settings.
* **Hive Services**: Update `lib/services/hive_services.dart` to add methods for handling GIT data storage and retrieval.

---

For more details, open the [Copilot Workspace session](https://copilot-workspace.githubnext.com/foss42/apidash/issues/502?shareId=XXXX-XXXX-XXXX-XXXX).
  • Loading branch information
iamprathosh committed Jan 11, 2025
1 parent 1aad26f commit 07f6357
Show file tree
Hide file tree
Showing 7 changed files with 231 additions and 2 deletions.
4 changes: 4 additions & 0 deletions lib/consts.dart
Original file line number Diff line number Diff line change
Expand Up @@ -467,3 +467,7 @@ const kMsgNoContent = "No content";
const kMsgUnknowContentType = "Unknown Response Content-Type";
// Workspace Selector
const kMsgSelectWorkspace = "Create your workspace";

// GIT settings constants
const kGitToken = "gitToken";
const kGitRepository = "gitRepository";
22 changes: 21 additions & 1 deletion lib/models/settings_model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ class SettingsModel {
this.historyRetentionPeriod = HistoryRetentionPeriod.oneWeek,
this.workspaceFolderPath,
this.isSSLDisabled = false,
this.gitToken,
this.gitRepository,
});

final bool isDark;
Expand All @@ -31,6 +33,8 @@ class SettingsModel {
final HistoryRetentionPeriod historyRetentionPeriod;
final String? workspaceFolderPath;
final bool isSSLDisabled;
final String? gitToken;
final String? gitRepository;

SettingsModel copyWith({
bool? isDark,
Expand All @@ -45,6 +49,8 @@ class SettingsModel {
HistoryRetentionPeriod? historyRetentionPeriod,
String? workspaceFolderPath,
bool? isSSLDisabled,
String? gitToken,
String? gitRepository,
}) {
return SettingsModel(
isDark: isDark ?? this.isDark,
Expand All @@ -61,6 +67,8 @@ class SettingsModel {
historyRetentionPeriod ?? this.historyRetentionPeriod,
workspaceFolderPath: workspaceFolderPath ?? this.workspaceFolderPath,
isSSLDisabled: isSSLDisabled ?? this.isSSLDisabled,
gitToken: gitToken ?? this.gitToken,
gitRepository: gitRepository ?? this.gitRepository,
);
}

Expand All @@ -80,6 +88,8 @@ class SettingsModel {
historyRetentionPeriod: historyRetentionPeriod,
workspaceFolderPath: workspaceFolderPath,
isSSLDisabled: isSSLDisabled,
gitToken: gitToken,
gitRepository: gitRepository,
);
}

Expand Down Expand Up @@ -134,6 +144,8 @@ class SettingsModel {
}
final workspaceFolderPath = data["workspaceFolderPath"] as String?;
final isSSLDisabled = data["isSSLDisabled"] as bool?;
final gitToken = data["gitToken"] as String?;
final gitRepository = data["gitRepository"] as String?;

const sm = SettingsModel();

Expand All @@ -151,6 +163,8 @@ class SettingsModel {
historyRetentionPeriod ?? HistoryRetentionPeriod.oneWeek,
workspaceFolderPath: workspaceFolderPath,
isSSLDisabled: isSSLDisabled,
gitToken: gitToken,
gitRepository: gitRepository,
);
}

Expand All @@ -170,6 +184,8 @@ class SettingsModel {
"historyRetentionPeriod": historyRetentionPeriod.name,
"workspaceFolderPath": workspaceFolderPath,
"isSSLDisabled": isSSLDisabled,
"gitToken": gitToken,
"gitRepository": gitRepository,
};
}

Expand All @@ -194,7 +210,9 @@ class SettingsModel {
other.activeEnvironmentId == activeEnvironmentId &&
other.historyRetentionPeriod == historyRetentionPeriod &&
other.workspaceFolderPath == workspaceFolderPath &&
other.isSSLDisabled == isSSLDisabled;
other.isSSLDisabled == isSSLDisabled &&
other.gitToken == gitToken &&
other.gitRepository == gitRepository;
}

@override
Expand All @@ -213,6 +231,8 @@ class SettingsModel {
historyRetentionPeriod,
workspaceFolderPath,
isSSLDisabled,
gitToken,
gitRepository,
);
}
}
21 changes: 20 additions & 1 deletion lib/providers/collection_providers.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:apidash/consts.dart';
import 'providers.dart';
import '../models/models.dart';
import '../services/services.dart' show hiveHandler, HiveHandler;
import '../services/services.dart' show hiveHandler, HiveHandler, GitService;
import '../utils/utils.dart'
show getNewUuid, collectionToHAR, substituteHttpRequestModel;

Expand Down Expand Up @@ -415,4 +415,23 @@ class CollectionStateNotifier
activeEnvId,
);
}

Future<void> pushToGit() async {
final settings = ref.read(settingsProvider);
final gitService = GitService(
repositoryUrl: settings.gitRepository!,
token: settings.gitToken!,
);
await gitService.pushData();
}

Future<void> pullFromGit() async {
final settings = ref.read(settingsProvider);
final gitService = GitService(
repositoryUrl: settings.gitRepository!,
token: settings.gitToken!,
);
await gitService.pullData();
loadData();
}
}
23 changes: 23 additions & 0 deletions lib/screens/home_page/collection_pane.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import 'package:apidash/widgets/widgets.dart';
import 'package:apidash/models/models.dart';
import 'package:apidash/consts.dart';
import '../common_widgets/common_widgets.dart';
import 'package:apidash/services/git_services.dart';

class CollectionPane extends ConsumerWidget {
const CollectionPane({
Expand Down Expand Up @@ -45,6 +46,28 @@ class CollectionPane extends ConsumerWidget {
},
),
kVSpacer10,
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
IconButton(
icon: const Icon(Icons.cloud_upload),
onPressed: () async {
await ref
.read(collectionStateNotifierProvider.notifier)
.pushToGit();
},
),
IconButton(
icon: const Icon(Icons.cloud_download),
onPressed: () async {
await ref
.read(collectionStateNotifierProvider.notifier)
.pullFromGit();
},
),
],
),
kVSpacer10,
const Expanded(
child: RequestList(),
),
Expand Down
73 changes: 73 additions & 0 deletions lib/screens/settings_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,23 @@ class SettingsPage extends ConsumerWidget {
showAboutAppDialog(context);
},
),
ListTile(
hoverColor: kColorTransparent,
title: const Text('GIT Configuration'),
subtitle: const Text(
'Configure GIT settings to enable push/pull functionality.'),
trailing: IconButton(
icon: const Icon(Icons.settings),
onPressed: () {
showDialog(
context: context,
builder: (BuildContext context) {
return GitSettingsDialog();
},
);
},
),
),
kVSpacer20,
],
),
Expand All @@ -240,3 +257,59 @@ class SettingsPage extends ConsumerWidget {
);
}
}

class GitSettingsDialog extends ConsumerStatefulWidget {
@override
_GitSettingsDialogState createState() => _GitSettingsDialogState();
}

class _GitSettingsDialogState extends ConsumerState<GitSettingsDialog> {
final TextEditingController _tokenController = TextEditingController();
final TextEditingController _repositoryController = TextEditingController();

@override
void initState() {
super.initState();
final settings = ref.read(settingsProvider);
_tokenController.text = settings.gitToken ?? '';
_repositoryController.text = settings.gitRepository ?? '';
}

@override
Widget build(BuildContext context) {
return AlertDialog(
title: const Text('GIT Settings'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextField(
controller: _tokenController,
decoration: const InputDecoration(labelText: 'GIT Token'),
),
TextField(
controller: _repositoryController,
decoration: const InputDecoration(labelText: 'GIT Repository'),
),
],
),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: const Text('Cancel'),
),
TextButton(
onPressed: () {
ref.read(settingsProvider.notifier).update(
gitToken: _tokenController.text,
gitRepository: _repositoryController.text,
);
Navigator.of(context).pop();
},
child: const Text('Save'),
),
],
);
}
}
53 changes: 53 additions & 0 deletions lib/services/git_services.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import 'dart:convert';
import 'package:git/git.dart';
import 'package:hive/hive.dart';
import 'package:path_provider/path_provider.dart';
import 'package:apidash/consts.dart';

class GitService {
final String repositoryUrl;
final String token;
final String branch;

GitService({
required this.repositoryUrl,
required this.token,
this.branch = 'main',
});

Future<void> pushData() async {
final directory = await getApplicationDocumentsDirectory();
final repoDir = '${directory.path}/apidash_repo';

final gitDir = await GitDir.fromExisting(repoDir, allowSubdirectory: true);
await gitDir.runCommand(['pull', 'origin', branch]);

final box = await Hive.openBox(kDataBox);
final data = box.toMap();
final jsonData = jsonEncode(data);

final file = File('$repoDir/collections.json');
await file.writeAsString(jsonData);

await gitDir.runCommand(['add', 'collections.json']);
await gitDir.runCommand(['commit', '-m', 'Update collections']);
await gitDir.runCommand(['push', 'origin', branch]);
}

Future<void> pullData() async {
final directory = await getApplicationDocumentsDirectory();
final repoDir = '${directory.path}/apidash_repo';

final gitDir = await GitDir.fromExisting(repoDir, allowSubdirectory: true);
await gitDir.runCommand(['pull', 'origin', branch]);

final file = File('$repoDir/collections.json');
if (await file.exists()) {
final jsonData = await file.readAsString();
final data = jsonDecode(jsonData);

final box = await Hive.openBox(kDataBox);
await box.putAll(data);
}
}
}
37 changes: 37 additions & 0 deletions lib/services/hive_services.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import 'package:flutter/foundation.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'dart:convert';
import 'package:git/git.dart';
import 'package:path_provider/path_provider.dart';

const String kDataBox = "apidash-data";
const String kKeyDataBoxIds = "ids";
Expand Down Expand Up @@ -167,4 +170,38 @@ class HiveHandler {
}
}
}

Future<void> pushDataToGit(String repositoryUrl, String token) async {
final directory = await getApplicationDocumentsDirectory();
final repoDir = '${directory.path}/apidash_repo';

final gitDir = await GitDir.fromExisting(repoDir, allowSubdirectory: true);
await gitDir.runCommand(['pull', 'origin', 'main']);

final data = dataBox.toMap();
final jsonData = jsonEncode(data);

final file = File('$repoDir/collections.json');
await file.writeAsString(jsonData);

await gitDir.runCommand(['add', 'collections.json']);
await gitDir.runCommand(['commit', '-m', 'Update collections']);
await gitDir.runCommand(['push', 'origin', 'main']);
}

Future<void> pullDataFromGit(String repositoryUrl, String token) async {
final directory = await getApplicationDocumentsDirectory();
final repoDir = '${directory.path}/apidash_repo';

final gitDir = await GitDir.fromExisting(repoDir, allowSubdirectory: true);
await gitDir.runCommand(['pull', 'origin', 'main']);

final file = File('$repoDir/collections.json');
if (await file.exists()) {
final jsonData = await file.readAsString();
final data = jsonDecode(jsonData);

await dataBox.putAll(data);
}
}
}

0 comments on commit 07f6357

Please sign in to comment.