Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add GIT integration #528

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
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);
}
}
}