Skip to content
This repository was archived by the owner on Dec 5, 2024. It is now read-only.

Commit 0043802

Browse files
Merge pull request #817 from github-for-unity/fixes/synccontext
Don't trample an existing SynchronizationContext
2 parents a3b896a + 5f32a30 commit 0043802

File tree

14 files changed

+202
-315
lines changed

14 files changed

+202
-315
lines changed

src/GitHub.Api/Application/ApiClient.cs

Lines changed: 120 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Linq;
4-
using System.Threading.Tasks;
54
using GitHub.Logging;
65
using System.Runtime.Serialization;
76
using System.Text;
@@ -33,127 +32,25 @@ public ApiClient(UriString hostUrl, IKeychain keychain, IProcessManager processM
3332
this.taskManager = taskManager;
3433
this.nodeJsExecutablePath = nodeJsExecutablePath;
3534
this.octorunScriptPath = octorunScriptPath;
36-
loginManager = new LoginManager(keychain,
37-
processManager: processManager,
38-
taskManager: taskManager,
39-
nodeJsExecutablePath: nodeJsExecutablePath,
40-
octorunScript: octorunScriptPath);
35+
loginManager = new LoginManager(keychain, processManager, taskManager, nodeJsExecutablePath, octorunScriptPath);
4136
}
4237

4338
public ITask Logout(UriString host)
4439
{
4540
return loginManager.Logout(host);
4641
}
4742

48-
public async Task CreateRepository(string name, string description, bool isPrivate, Action<GitHubRepository, Exception> callback, string organization = null)
43+
public void CreateRepository(string name, string description, bool isPrivate, Action<GitHubRepository, Exception> callback, string organization = null)
4944
{
5045
Guard.ArgumentNotNull(callback, "callback");
51-
try
52-
{
53-
var repository = await CreateRepositoryInternal(name, organization, description, isPrivate);
54-
callback(repository, null);
55-
}
56-
catch (Exception e)
57-
{
58-
callback(null, e);
59-
}
60-
}
61-
62-
public async Task GetOrganizations(Action<Organization[]> onSuccess, Action<Exception> onError = null)
63-
{
64-
Guard.ArgumentNotNull(onSuccess, nameof(onSuccess));
65-
await GetOrganizationInternal(onSuccess, onError);
66-
}
6746

68-
public async Task GetCurrentUser(Action<GitHubUser> onSuccess, Action<Exception> onError = null)
69-
{
70-
Guard.ArgumentNotNull(onSuccess, nameof(onSuccess));
71-
try
72-
{
73-
var user = await GetCurrentUser();
74-
onSuccess(user);
75-
}
76-
catch (Exception e)
47+
new FuncTask<GitHubRepository>(taskManager.Token, () =>
7748
{
78-
onError?.Invoke(e);
79-
}
80-
}
81-
82-
public async Task Login(string username, string password, Action<LoginResult> need2faCode, Action<bool, string> result)
83-
{
84-
Guard.ArgumentNotNull(need2faCode, "need2faCode");
85-
Guard.ArgumentNotNull(result, "result");
86-
87-
LoginResultData res = null;
88-
try
89-
{
90-
res = await loginManager.Login(OriginalUrl, username, password);
91-
}
92-
catch (Exception ex)
93-
{
94-
logger.Warning(ex);
95-
result(false, ex.Message);
96-
return;
97-
}
98-
99-
if (res.Code == LoginResultCodes.CodeRequired)
100-
{
101-
var resultCache = new LoginResult(res, result, need2faCode);
102-
need2faCode(resultCache);
103-
}
104-
else
105-
{
106-
result(res.Code == LoginResultCodes.Success, res.Message);
107-
}
108-
}
109-
110-
public async Task ContinueLogin(LoginResult loginResult, string code)
111-
{
112-
LoginResultData result = null;
113-
try
114-
{
115-
result = await loginManager.ContinueLogin(loginResult.Data, code);
116-
}
117-
catch (Exception ex)
118-
{
119-
loginResult.Callback(false, ex.Message);
120-
return;
121-
}
122-
if (result.Code == LoginResultCodes.CodeFailed)
123-
{
124-
loginResult.TwoFACallback(new LoginResult(result, loginResult.Callback, loginResult.TwoFACallback));
125-
}
126-
loginResult.Callback(result.Code == LoginResultCodes.Success, result.Message);
127-
}
128-
129-
private async Task<GitHubUser> GetCurrentUser()
130-
{
131-
//TODO: ONE_USER_LOGIN This assumes we only support one login
132-
var keychainConnection = keychain.Connections.FirstOrDefault();
133-
if (keychainConnection == null)
134-
throw new KeychainEmptyException();
135-
136-
var keychainAdapter = await GetValidatedKeychainAdapter(keychainConnection);
137-
138-
// we can't trust that the system keychain has the username filled out correctly.
139-
// if it doesn't, we need to grab the username from the server and check it
140-
// unfortunately this means that things will be slower when the keychain doesn't have all the info
141-
if (keychainConnection.User == null || keychainAdapter.Credential.Username != keychainConnection.Username)
142-
{
143-
keychainConnection.User = await GetValidatedGitHubUser(keychainConnection, keychainAdapter);
144-
}
145-
return keychainConnection.User;
146-
}
147-
148-
private async Task<GitHubRepository> CreateRepositoryInternal(string repositoryName, string organization, string description, bool isPrivate)
149-
{
150-
try
151-
{
152-
var user = await GetCurrentUser();
49+
var user = GetCurrentUser();
15350
var keychainAdapter = keychain.Connect(OriginalUrl);
15451

15552
var command = new StringBuilder("publish -r \"");
156-
command.Append(repositoryName);
53+
command.Append(name);
15754
command.Append("\"");
15855

15956
if (!string.IsNullOrEmpty(description))
@@ -176,10 +73,10 @@ private async Task<GitHubRepository> CreateRepositoryInternal(string repositoryN
17673
}
17774

17875
var octorunTask = new OctorunTask(taskManager.Token, nodeJsExecutablePath, octorunScriptPath, command.ToString(),
179-
user: user.Login, userToken: keychainAdapter.Credential.Token)
76+
user: user.Login, userToken: keychainAdapter.Credential.Token)
18077
.Configure(processManager);
18178

182-
var ret = await octorunTask.StartAwait();
79+
var ret = octorunTask.RunSynchronously();
18380
if (ret.IsSuccess && ret.Output.Length == 2)
18481
{
18582
return new GitHubRepository
@@ -190,26 +87,33 @@ private async Task<GitHubRepository> CreateRepositoryInternal(string repositoryN
19087
}
19188

19289
throw new ApiClientException(ret.GetApiErrorMessage() ?? "Publish failed");
193-
}
194-
catch (Exception ex)
90+
})
91+
.FinallyInUI((success, ex, repository) =>
19592
{
196-
logger.Error(ex, "Error Creating Repository");
197-
throw;
198-
}
93+
if (success)
94+
callback(repository, null);
95+
else
96+
{
97+
logger.Error(ex, "Error creating repository");
98+
callback(null, ex);
99+
}
100+
})
101+
.Start();
199102
}
200103

201-
private async Task GetOrganizationInternal(Action<Organization[]> onSuccess, Action<Exception> onError = null)
104+
public void GetOrganizations(Action<Organization[]> onSuccess, Action<Exception> onError = null)
202105
{
203-
try
106+
Guard.ArgumentNotNull(onSuccess, nameof(onSuccess));
107+
new FuncTask<Organization[]>(taskManager.Token, () =>
204108
{
205-
var user = await GetCurrentUser();
109+
var user = GetCurrentUser();
206110
var keychainAdapter = keychain.Connect(OriginalUrl);
207111

208112
var octorunTask = new OctorunTask(taskManager.Token, nodeJsExecutablePath, octorunScriptPath, "organizations",
209113
user: user.Login, userToken: keychainAdapter.Credential.Token)
210114
.Configure(processManager);
211115

212-
var ret = await octorunTask.StartAsAsync();
116+
var ret = octorunTask.RunSynchronously();
213117
if (ret.IsSuccess)
214118
{
215119
var organizations = new List<Organization>();
@@ -221,23 +125,109 @@ private async Task GetOrganizationInternal(Action<Organization[]> onSuccess, Act
221125
Login = ret.Output[i + 1]
222126
});
223127
}
224-
225-
onSuccess(organizations.ToArray());
226-
return;
128+
return organizations.ToArray();
227129
}
228130

229131
throw new ApiClientException(ret.GetApiErrorMessage() ?? "Error getting organizations");
230-
}
231-
catch (Exception ex)
132+
})
133+
.FinallyInUI((success, ex, orgs) =>
134+
{
135+
if (success)
136+
onSuccess(orgs);
137+
else
138+
{
139+
logger.Error(ex, "Error Getting Organizations");
140+
onError?.Invoke(ex);
141+
}
142+
})
143+
.Start();
144+
}
145+
146+
public void GetCurrentUser(Action<GitHubUser> onSuccess, Action<Exception> onError = null)
147+
{
148+
Guard.ArgumentNotNull(onSuccess, nameof(onSuccess));
149+
new FuncTask<GitHubUser>(taskManager.Token, GetCurrentUser)
150+
.FinallyInUI((success, ex, user) =>
151+
{
152+
if (success)
153+
onSuccess(user);
154+
else
155+
onError?.Invoke(ex);
156+
})
157+
.Start();
158+
}
159+
160+
public void Login(string username, string password, Action<LoginResult> need2faCode, Action<bool, string> result)
161+
{
162+
Guard.ArgumentNotNull(need2faCode, "need2faCode");
163+
Guard.ArgumentNotNull(result, "result");
164+
165+
new FuncTask<LoginResultData>(taskManager.Token,
166+
() => loginManager.Login(OriginalUrl, username, password))
167+
.FinallyInUI((success, ex, res) =>
168+
{
169+
if (!success)
170+
{
171+
logger.Warning(ex);
172+
result(false, ex.Message);
173+
return;
174+
}
175+
176+
if (res.Code == LoginResultCodes.CodeRequired)
177+
{
178+
var resultCache = new LoginResult(res, result, need2faCode);
179+
need2faCode(resultCache);
180+
}
181+
else
182+
{
183+
result(res.Code == LoginResultCodes.Success, res.Message);
184+
}
185+
})
186+
.Start();
187+
}
188+
189+
public void ContinueLogin(LoginResult loginResult, string code)
190+
{
191+
new FuncTask<LoginResultData>(taskManager.Token,
192+
() => loginManager.ContinueLogin(loginResult.Data, code))
193+
.FinallyInUI((success, ex, result) =>
194+
{
195+
if (!success)
196+
{
197+
loginResult.Callback(false, ex.Message);
198+
return;
199+
}
200+
if (result.Code == LoginResultCodes.CodeFailed)
201+
{
202+
loginResult.TwoFACallback(new LoginResult(result, loginResult.Callback, loginResult.TwoFACallback));
203+
}
204+
loginResult.Callback(result.Code == LoginResultCodes.Success, result.Message);
205+
})
206+
.Start();
207+
}
208+
209+
private GitHubUser GetCurrentUser()
210+
{
211+
//TODO: ONE_USER_LOGIN This assumes we only support one login
212+
var keychainConnection = keychain.Connections.FirstOrDefault();
213+
if (keychainConnection == null)
214+
throw new KeychainEmptyException();
215+
216+
var keychainAdapter = GetValidatedKeychainAdapter(keychainConnection);
217+
218+
// we can't trust that the system keychain has the username filled out correctly.
219+
// if it doesn't, we need to grab the username from the server and check it
220+
// unfortunately this means that things will be slower when the keychain doesn't have all the info
221+
if (keychainConnection.User == null || keychainAdapter.Credential.Username != keychainConnection.Username)
232222
{
233-
logger.Error(ex, "Error Getting Organizations");
234-
onError?.Invoke(ex);
223+
keychainConnection.User = GetValidatedGitHubUser(keychainConnection, keychainAdapter);
235224
}
225+
return keychainConnection.User;
236226
}
237227

238-
private async Task<IKeychainAdapter> GetValidatedKeychainAdapter(Connection keychainConnection)
228+
private IKeychainAdapter GetValidatedKeychainAdapter(Connection keychainConnection)
239229
{
240-
var keychainAdapter = await keychain.Load(keychainConnection.Host);
230+
var keychainAdapter = keychain.Load(keychainConnection.Host);
241231
if (keychainAdapter == null)
242232
throw new KeychainEmptyException();
243233

@@ -255,15 +245,15 @@ private async Task<IKeychainAdapter> GetValidatedKeychainAdapter(Connection keyc
255245
return keychainAdapter;
256246
}
257247

258-
private async Task<GitHubUser> GetValidatedGitHubUser(Connection keychainConnection, IKeychainAdapter keychainAdapter)
248+
private GitHubUser GetValidatedGitHubUser(Connection keychainConnection, IKeychainAdapter keychainAdapter)
259249
{
260250
try
261251
{
262252
var octorunTask = new OctorunTask(taskManager.Token, nodeJsExecutablePath, octorunScriptPath, "validate",
263253
user: keychainConnection.Username, userToken: keychainAdapter.Credential.Token)
264254
.Configure(processManager);
265255

266-
var ret = await octorunTask.StartAsAsync();
256+
var ret = octorunTask.RunSynchronously();
267257
if (ret.IsSuccess)
268258
{
269259
var login = ret.Output[1];

src/GitHub.Api/Application/ApplicationManagerBase.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,10 @@ public event Action<IProgress> OnProgress
2828

2929
public ApplicationManagerBase(SynchronizationContext synchronizationContext, IEnvironment environment)
3030
{
31+
UIScheduler = ThreadingHelper.GetUIScheduler(synchronizationContext);
32+
3133
SynchronizationContext = synchronizationContext;
32-
SynchronizationContext.SetSynchronizationContext(SynchronizationContext);
3334
ThreadingHelper.SetUIThread();
34-
UIScheduler = TaskScheduler.FromCurrentSynchronizationContext();
3535
ThreadingHelper.MainThreadScheduler = UIScheduler;
3636

3737
Environment = environment;
Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,17 @@
1-
using System.Threading.Tasks;
2-
using System;
1+
using System;
32

43
namespace GitHub.Unity
54
{
65
interface IApiClient
76
{
87
HostAddress HostAddress { get; }
98
UriString OriginalUrl { get; }
10-
Task CreateRepository(string name, string description, bool isPrivate,
9+
void CreateRepository(string name, string description, bool isPrivate,
1110
Action<GitHubRepository, Exception> callback, string organization = null);
12-
Task GetOrganizations(Action<Organization[]> onSuccess, Action<Exception> onError = null);
13-
Task Login(string username, string password, Action<LoginResult> need2faCode, Action<bool, string> result);
14-
Task ContinueLogin(LoginResult loginResult, string code);
11+
void GetOrganizations(Action<Organization[]> onSuccess, Action<Exception> onError = null);
12+
void Login(string username, string password, Action<LoginResult> need2faCode, Action<bool, string> result);
13+
void ContinueLogin(LoginResult loginResult, string code);
1514
ITask Logout(UriString host);
16-
Task GetCurrentUser(Action<GitHubUser> onSuccess, Action<Exception> onError = null);
15+
void GetCurrentUser(Action<GitHubUser> onSuccess, Action<Exception> onError = null);
1716
}
1817
}

src/GitHub.Api/Authentication/ICredentialManager.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@ public interface ICredential : IDisposable
1313

1414
public interface ICredentialManager
1515
{
16-
Task<ICredential> Load(UriString host);
17-
Task Save(ICredential cred);
18-
Task Delete(UriString host);
16+
ICredential Load(UriString host);
17+
void Save(ICredential cred);
18+
void Delete(UriString host);
1919
bool HasCredentials();
2020
ICredential CachedCredentials { get; }
2121
}
22-
}
22+
}

0 commit comments

Comments
 (0)