diff --git a/lib/apis/gitlab.dart b/lib/apis/gitlab.dart new file mode 100644 index 00000000..1b7ec716 --- /dev/null +++ b/lib/apis/gitlab.dart @@ -0,0 +1,179 @@ +import 'dart:async'; +import 'dart:convert'; +import 'dart:io'; + +import 'package:http/http.dart' as http; +import 'package:flutter/services.dart'; + +import 'package:url_launcher/url_launcher.dart'; + +import 'dart:math'; + +String _randomString(int length) { + var rand = new Random(); + var codeUnits = new List.generate(length, (index) { + return rand.nextInt(33) + 89; + }); + + return new String.fromCharCodes(codeUnits); +} + +class Gitlab { + static const _clientID = + "faf33c3716faf05bfb701b1b31e36c83a23c3ec2d7161f4ff00fba2275524d09"; + + var _platform = const MethodChannel('gitjournal.io/git'); + var _accessCode = ""; + var _stateOAuth = ""; + + void init(Function callback) { + Future _handleMessages(MethodCall call) async { + if (call.method != "onURL") { + print("GitLab Unknown Call: " + call.method); + return; + } + + print("GitLab: Called onUrl with " + call.arguments.toString()); + + var url = call.arguments["URL"]; + var uri = Uri.parse(url); + + var state = uri.queryParameters['state']; + if (state != _stateOAuth) { + print("GitLab: OAuth State incorrect"); + callback(); + } + + _accessCode = uri.queryParameters['access_token']; + if (_accessCode == null) { + print("GitLab: Missing access code. Now what?"); + callback(); + } + + callback(); + } + + _platform.setMethodCallHandler(_handleMessages); + print("GitLab: Installed Handler"); + } + + Future launchOAuthScreen() async { + _stateOAuth = _randomString(10); + + var url = + "https://gitlab.com/oauth/authorize?client_id=$_clientID&response_type=token&state=$_stateOAuth&redirect_uri=gitjournal://login.oauth2"; + return launch(url); + } + + Future> listRepos() async { + if (_accessCode.isEmpty) { + throw "GitHub Access Code Missing"; + } + + // FIXME: pagination! + var url = + "https://gitlab.com/api/v4/projects?simple=true&membership=true&order_by=last_activity_at&access_token=$_accessCode"; + + var response = await http.get(url); + if (response.statusCode != 200) { + print("GitLab listRepos: Invalid response " + + response.statusCode.toString() + + ": " + + response.body); + return null; + } + + List list = jsonDecode(response.body); + List repos = new List(); + list.forEach((dynamic d) { + var map = Map.from(d); + var repo = GitLabRepo.fromJson(map); + repos.add(repo); + }); + + // FIXME: Sort these based on some criteria + return repos; + } + +// FIXME: Proper error when the repo exists! + Future createRepo(String name) async { + if (_accessCode.isEmpty) { + throw "GitLab Access Code Missing"; + } + + var url = "https://gitlab.com/api/v4/projects?access_token=$_accessCode"; + Map data = { + 'name': name, + 'visibility': 'private', + }; + + var headers = { + HttpHeaders.contentTypeHeader: "application/json", + }; + + var response = + await http.post(url, headers: headers, body: json.encode(data)); + if (response.statusCode != 201) { + print("GitLab createRepo: Invalid response " + + response.statusCode.toString() + + ": " + + response.body); + return null; + } + + print("GitLab createRepo: " + response.body); + var map = json.decode(response.body); + return GitLabRepo.fromJson(map); + } + + Future addDeployKey(String sshPublicKey, String repo) async { + if (_accessCode.isEmpty) { + throw "GitLab Access Code Missing"; + } + + repo = repo.replaceAll('/', '%2F'); + var url = + "https://gitlab.com/api/v4/projects/$repo/deploy_keys?access_token=$_accessCode"; + + Map data = { + 'title': "GitJournal", + 'key': sshPublicKey, + 'can_push': true, + }; + + var headers = { + HttpHeaders.contentTypeHeader: "application/json", + }; + + var response = + await http.post(url, headers: headers, body: json.encode(data)); + if (response.statusCode != 201) { + print("GitLab addDeployKey: Invalid response " + + response.statusCode.toString() + + ": " + + response.body); + return null; + } + + print("GitLab addDeployKey: " + response.body); + return json.decode(response.body); + } +} + +class GitLabRepo { + String fullName; + String cloneUrl; + + GitLabRepo({this.fullName, this.cloneUrl}); + factory GitLabRepo.fromJson(Map parsedJson) { + return new GitLabRepo( + fullName: parsedJson['path_with_namespace'], + cloneUrl: parsedJson['ssh_url_to_repo'], + ); + } + + @override + String toString() { + return 'GitLabRepo{fulleName: $fullName, cloneUrl: $cloneUrl}'; + } +} diff --git a/lib/oauthapp.dart b/lib/oauthapp.dart index 784a51d7..d3163638 100644 --- a/lib/oauthapp.dart +++ b/lib/oauthapp.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:journal/apis/github.dart'; +import 'package:journal/apis/gitlab.dart'; class OAuthApp extends StatefulWidget { @override @@ -13,8 +14,7 @@ var key = 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC+VAh8r+vn0c+M+DacOo/szXcdMpxO1kIO3USkzgE5XdO83kQdDwh4Xc4P3dcc+FFSfVcEl3mSXGKbYC3G0ZoVcWd4ed40Gt3sLHSfNRQlRv+obnqKbzDLuOGfq65EkaJ90vrWBo/k7K8tBC2j1FZ/PUYy3DxeQkPEZXCMZDSG5P/+XoHn5IPcaxDpvlZjtOrx4H3pQ/YVI+XmyFAsZe+/Shy5sg4ilsdo4BQN2nODuBLwmgYu/hHmCcd8t4OxgBANVN8TMqHnZfRLixRSuXn0DbV4YOa/b2lBFQNvjkoBF6KhXOxZ+awyjyTpNp4AgF5c+3xptkNwUlwiQDCzcUmH your_email@example.com'; class OAuthAppState extends State { - var github = new GitHub(); - String githubAccessCode = ""; + var github = new Gitlab(); void initState() { super.initState(); @@ -41,9 +41,13 @@ class OAuthAppState extends State { RaisedButton( child: Text("List Repos"), onPressed: () async { - var repos = await github.listRepos(); - for (var repo in repos) { - print(repo.fullName); + try { + var repos = await github.listRepos(); + for (var repo in repos) { + print(repo); + } + } catch (err) { + print("ListRepos: " + err.toString()); } }, ), @@ -51,7 +55,8 @@ class OAuthAppState extends State { child: Text("Create Repo"), onPressed: () async { try { - await github.createRepo("journal_test2"); + var repo = await github.createRepo("journal_test2"); + print(repo); } catch (err) { print("Create Repo: " + err.toString()); }