@@ -19,17 +19,14 @@ public static IApiClient Create(UriString repositoryUrl, IKeychain keychain)
19
19
new GitHubClient ( AppConfiguration . ProductHeader , credentialStore , hostAddress . ApiUri ) ) ;
20
20
}
21
21
22
- private static readonly Unity . ILogging logger = Unity . Logging . GetLogger < ApiClient > ( ) ;
22
+ private static readonly ILogging logger = Logging . GetLogger < ApiClient > ( ) ;
23
23
public HostAddress HostAddress { get ; }
24
24
public UriString OriginalUrl { get ; }
25
25
26
26
private readonly IKeychain keychain ;
27
27
private readonly IGitHubClient githubClient ;
28
28
private readonly ILoginManager loginManager ;
29
29
30
- IList < Organization > organizationsCache ;
31
- Octokit . User userCache ;
32
-
33
30
public ApiClient ( UriString hostUrl , IKeychain keychain , IGitHubClient githubClient )
34
31
{
35
32
Guard . ArgumentNotNull ( hostUrl , nameof ( hostUrl ) ) ;
@@ -53,7 +50,7 @@ private async Task LogoutInternal(UriString host)
53
50
await loginManager . Logout ( host ) ;
54
51
}
55
52
56
- public async Task CreateRepository ( NewRepository newRepository , Action < Octokit . Repository , Exception > callback , string organization = null )
53
+ public async Task CreateRepository ( NewRepository newRepository , Action < GitHubRepository , Exception > callback , string organization = null )
57
54
{
58
55
Guard . ArgumentNotNull ( callback , "callback" ) ;
59
56
try
@@ -67,21 +64,27 @@ public async Task CreateRepository(NewRepository newRepository, Action<Octokit.R
67
64
}
68
65
}
69
66
70
- public async Task GetOrganizations ( Action < IList < Organization > > callback )
67
+ public async Task GetOrganizations ( Action < Organization [ ] > onSuccess , Action < Exception > onError = null )
71
68
{
72
- Guard . ArgumentNotNull ( callback , "callback" ) ;
73
- var organizations = await GetOrganizationInternal ( ) ;
74
- callback ( organizations ) ;
69
+ Guard . ArgumentNotNull ( onSuccess , nameof ( onSuccess ) ) ;
70
+ await GetOrganizationInternal ( onSuccess , onError ) ;
75
71
}
76
72
77
- public async Task LoadKeychain ( Action < bool > callback )
73
+ public async Task ValidateCurrentUser ( Action onSuccess , Action < Exception > onError = null )
78
74
{
79
- Guard . ArgumentNotNull ( callback , "callback" ) ;
80
- var hasLoadedKeys = await LoadKeychainInternal ( ) ;
81
- callback ( hasLoadedKeys ) ;
75
+ Guard . ArgumentNotNull ( onSuccess , nameof ( onSuccess ) ) ;
76
+ try
77
+ {
78
+ await ValidateCurrentUserInternal ( ) ;
79
+ onSuccess ( ) ;
80
+ }
81
+ catch ( Exception e )
82
+ {
83
+ onError ? . Invoke ( e ) ;
84
+ }
82
85
}
83
86
84
- public async Task GetCurrentUser ( Action < Octokit . User > callback )
87
+ public async Task GetCurrentUser ( Action < GitHubUser > callback )
85
88
{
86
89
Guard . ArgumentNotNull ( callback , "callback" ) ;
87
90
var user = await GetCurrentUserInternal ( ) ;
@@ -184,29 +187,27 @@ public async Task<bool> ContinueLoginAsync(LoginResult loginResult, Func<LoginRe
184
187
return result . Code == LoginResultCodes . Success ;
185
188
}
186
189
187
- private async Task < Octokit . Repository > CreateRepositoryInternal ( NewRepository newRepository , string organization )
190
+ private async Task < GitHubRepository > CreateRepositoryInternal ( NewRepository newRepository , string organization )
188
191
{
189
192
try
190
193
{
191
194
logger . Trace ( "Creating repository" ) ;
192
195
193
- if ( ! await LoadKeychainInternal ( ) )
194
- {
195
- throw new InvalidOperationException ( "The keychain did not load" ) ;
196
- }
196
+ await ValidateKeychain ( ) ;
197
+ await ValidateCurrentUserInternal ( ) ;
197
198
198
- Octokit . Repository repository ;
199
+ GitHubRepository repository ;
199
200
if ( ! string . IsNullOrEmpty ( organization ) )
200
201
{
201
202
logger . Trace ( "Creating repository for organization" ) ;
202
203
203
- repository = await githubClient . Repository . Create ( organization , newRepository ) ;
204
+ repository = ( await githubClient . Repository . Create ( organization , newRepository ) ) . ToGitHubRepository ( ) ;
204
205
}
205
206
else
206
207
{
207
208
logger . Trace ( "Creating repository for user" ) ;
208
209
209
- repository = await githubClient . Repository . Create ( newRepository ) ;
210
+ repository = ( await githubClient . Repository . Create ( newRepository ) ) . ToGitHubRepository ( ) ;
210
211
}
211
212
212
213
logger . Trace ( "Created Repository" ) ;
@@ -219,66 +220,78 @@ public async Task<bool> ContinueLoginAsync(LoginResult loginResult, Func<LoginRe
219
220
}
220
221
}
221
222
222
- private async Task < IList < Organization > > GetOrganizationInternal ( )
223
+ private async Task GetOrganizationInternal ( Action < Organization [ ] > onSuccess , Action < Exception > onError = null )
223
224
{
224
225
try
225
226
{
226
227
logger . Trace ( "Getting Organizations" ) ;
227
228
228
- if ( ! await LoadKeychainInternal ( ) )
229
- {
230
- return new List < Organization > ( ) ;
231
- }
229
+ await ValidateKeychain ( ) ;
230
+ await ValidateCurrentUserInternal ( ) ;
232
231
233
232
var organizations = await githubClient . Organization . GetAllForCurrent ( ) ;
234
233
235
234
logger . Trace ( "Obtained {0} Organizations" , organizations ? . Count . ToString ( ) ?? "NULL" ) ;
236
235
237
236
if ( organizations != null )
238
237
{
239
- organizationsCache = organizations . ToArray ( ) ;
238
+ var array = organizations . Select ( organization => new Organization ( ) {
239
+ Name = organization . Name ,
240
+ Login = organization . Login
241
+ } ) . ToArray ( ) ;
242
+ onSuccess ( array ) ;
240
243
}
241
244
}
242
245
catch ( Exception ex )
243
246
{
244
247
logger . Error ( ex , "Error Getting Organizations" ) ;
245
- throw ;
248
+ onError ? . Invoke ( ex ) ;
246
249
}
247
-
248
- return organizationsCache ;
249
250
}
250
251
251
- private async Task < Octokit . User > GetCurrentUserInternal ( )
252
+ private async Task < GitHubUser > GetCurrentUserInternal ( )
252
253
{
253
254
try
254
255
{
255
- logger . Trace ( "Getting Organizations" ) ;
256
-
257
- if ( ! await LoadKeychainInternal ( ) )
258
- {
259
- return null ;
260
- }
256
+ logger . Trace ( "Getting Current User" ) ;
257
+ await ValidateKeychain ( ) ;
261
258
262
- userCache = await githubClient . User . Current ( ) ;
259
+ return ( await githubClient . User . Current ( ) ) . ToGitHubUser ( ) ;
263
260
}
264
- catch ( Exception ex )
261
+ catch ( KeychainEmptyException )
262
+ {
263
+ logger . Warning ( "Keychain is empty" ) ;
264
+ throw ;
265
+ }
266
+ catch ( Exception ex )
265
267
{
266
268
logger . Error ( ex , "Error Getting Current User" ) ;
267
269
throw ;
268
270
}
271
+ }
272
+
273
+ private async Task ValidateCurrentUserInternal ( )
274
+ {
275
+ logger . Trace ( "Validating User" ) ;
269
276
270
- return userCache ;
277
+ var apiUser = await GetCurrentUserInternal ( ) ;
278
+ var apiUsername = apiUser . Login ;
279
+
280
+ var cachedUsername = keychain . Connections . First ( ) . Username ;
281
+
282
+ if ( apiUsername != cachedUsername )
283
+ {
284
+ throw new TokenUsernameMismatchException ( cachedUsername , apiUsername ) ;
285
+ }
271
286
}
272
287
273
288
private async Task < bool > LoadKeychainInternal ( )
274
289
{
275
- logger . Trace ( "LoadKeychainInternal" ) ;
276
-
277
290
if ( keychain . HasKeys )
278
291
{
279
292
if ( ! keychain . NeedsLoad )
280
293
{
281
- logger . Trace ( "LoadKeychainInternal: Has keys does not need load " ) ;
294
+ logger . Trace ( "LoadKeychainInternal: Previously Loaded " ) ;
282
295
return true ;
283
296
}
284
297
@@ -288,6 +301,8 @@ private async Task<bool> LoadKeychainInternal()
288
301
var uriString = keychain . Connections . First ( ) . Host ;
289
302
var keychainAdapter = await keychain . Load ( uriString ) ;
290
303
304
+ logger . Trace ( "LoadKeychainInternal: Loaded" ) ;
305
+
291
306
return keychainAdapter . OctokitCredentials != Credentials . Anonymous ;
292
307
}
293
308
@@ -296,23 +311,54 @@ private async Task<bool> LoadKeychainInternal()
296
311
return false ;
297
312
}
298
313
299
- public async Task < bool > ValidateCredentials ( )
314
+ private async Task ValidateKeychain ( )
300
315
{
301
- try
316
+ if ( ! await LoadKeychainInternal ( ) )
302
317
{
303
- var store = keychain . Connect ( OriginalUrl ) ;
304
-
305
- if ( store . OctokitCredentials != Credentials . Anonymous )
306
- {
307
- var credential = store . Credential ;
308
- await githubClient . Authorization . CheckApplicationAuthentication ( ApplicationInfo . ClientId , credential . Token ) ;
309
- }
318
+ throw new KeychainEmptyException ( ) ;
310
319
}
311
- catch
312
- {
313
- return false ;
314
- }
315
- return true ;
316
320
}
317
321
}
322
+
323
+ class GitHubUser
324
+ {
325
+ public string Name { get ; set ; }
326
+ public string Login { get ; set ; }
327
+ }
328
+
329
+ class GitHubRepository
330
+ {
331
+ public string Name { get ; set ; }
332
+ public string CloneUrl { get ; set ; }
333
+ }
334
+
335
+ class ApiClientException : Exception
336
+ {
337
+ public ApiClientException ( )
338
+ { }
339
+
340
+ public ApiClientException ( string message ) : base ( message )
341
+ { }
342
+
343
+ public ApiClientException ( string message , Exception innerException ) : base ( message , innerException )
344
+ { }
345
+ }
346
+
347
+ class TokenUsernameMismatchException : ApiClientException
348
+ {
349
+ public string CachedUsername { get ; }
350
+ public string CurrentUsername { get ; }
351
+
352
+ public TokenUsernameMismatchException ( string cachedUsername , string currentUsername )
353
+ {
354
+ CachedUsername = cachedUsername ;
355
+ CurrentUsername = currentUsername ;
356
+ }
357
+ }
358
+
359
+ class KeychainEmptyException : ApiClientException
360
+ {
361
+ public KeychainEmptyException ( )
362
+ { }
363
+ }
318
364
}
0 commit comments