Skip to content

Commit 75489bf

Browse files
authored
Merge pull request #459 from classtranscribe/Update-DownloadMedia-Logging
BoxAccessTokenTrial
2 parents 9b6a89f + ffd9095 commit 75489bf

File tree

2 files changed

+134
-40
lines changed

2 files changed

+134
-40
lines changed

ClassTranscribeDatabase/Services/BoxAPI.cs

Lines changed: 131 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,26 @@
77
using System;
88
using System.Linq;
99
using System.Threading.Tasks;
10+
using System.Threading; // Interlocked
11+
12+
// https://support.box.com/hc/en-us/community/posts/360049144934-Refresh-Token-Expiring-in-1-hour
1013

1114
namespace ClassTranscribeDatabase.Services
1215
{
1316
public class BoxAPI
1417
{
18+
private static int refreshing = 0;
1519
private readonly SlackLogger _slack;
1620
private readonly ILogger _logger;
21+
private readonly BoxConfig _config;
1722
public BoxAPI(ILogger<BoxAPI> logger, SlackLogger slack)
1823
{
1924
_logger = logger;
2025
_slack = slack;
26+
_config = new BoxConfig(Globals.appSettings.BOX_CLIENT_ID, Globals.appSettings.BOX_CLIENT_SECRET, new Uri("http://locahost"));
2127
}
2228

29+
// Used by Controller/BoxController.cs
2330
// To generate authCode on a browser open,
2431
// https://account.box.com/api/oauth2/authorize?client_id=[CLIENT_ID]&response_type=code
2532
/// <summary>Updates Box accessToken and refreshToken values in the Dictionary table.
@@ -30,30 +37,11 @@ public async Task CreateAccessTokenAsync(string authCode)
3037
// This implementation is overly chatty with the database, but we rarely create access tokens so it is not a problem
3138
using (var _context = CTDbContext.CreateDbContext())
3239
{
33-
if (!await _context.Dictionaries.Where(d => d.Key == CommonUtils.BOX_ACCESS_TOKEN).AnyAsync())
34-
{
35-
_context.Dictionaries.Add(new Dictionary
36-
{
37-
Key = CommonUtils.BOX_ACCESS_TOKEN
38-
});
39-
await _context.SaveChangesAsync();
40-
}
41-
if (!await _context.Dictionaries.Where(d => d.Key == CommonUtils.BOX_REFRESH_TOKEN).AnyAsync())
42-
{
43-
_context.Dictionaries.Add(new Dictionary
44-
{
45-
Key = CommonUtils.BOX_REFRESH_TOKEN
46-
});
47-
await _context.SaveChangesAsync();
48-
}
49-
50-
51-
var accessToken = _context.Dictionaries.Where(d => d.Key == CommonUtils.BOX_ACCESS_TOKEN).First();
52-
var refreshToken = _context.Dictionaries.Where(d => d.Key == CommonUtils.BOX_REFRESH_TOKEN).First();
53-
var config = new BoxConfig(Globals.appSettings.BOX_CLIENT_ID, Globals.appSettings.BOX_CLIENT_SECRET, new Uri("http://locahost"));
54-
var client = new Box.V2.BoxClient(config);
40+
var client = new Box.V2.BoxClient(_config);
5541
var auth = await client.Auth.AuthenticateAsync(authCode);
5642
_logger.LogInformation("Created Box Tokens");
43+
//Dictionary accessToken,refreshToken;
44+
var ( accessToken, refreshToken) = await getOrCreateDatabaseEntries(_context);
5745
accessToken.Value = auth.AccessToken;
5846
refreshToken.Value = auth.RefreshToken;
5947
await _context.SaveChangesAsync();
@@ -64,23 +52,42 @@ public async Task CreateAccessTokenAsync(string authCode)
6452
/// </summary>
6553
public async Task RefreshAccessTokenAsync()
6654
{
55+
_logger.LogInformation("RefreshAccessTokenAsync: Starting");
6756
try
6857
{
6958
using (var _context = CTDbContext.CreateDbContext())
7059
{
71-
var accessToken = await _context.Dictionaries.Where(d => d.Key == CommonUtils.BOX_ACCESS_TOKEN).FirstAsync();
72-
var refreshToken = await _context.Dictionaries.Where(d => d.Key == CommonUtils.BOX_REFRESH_TOKEN).FirstAsync();
73-
var config = new BoxConfig(Globals.appSettings.BOX_CLIENT_ID, Globals.appSettings.BOX_CLIENT_SECRET, new Uri("http://locahost"));
60+
// Dictionary accessToken,refreshToken;
61+
var( accessToken, refreshToken) = await getOrCreateDatabaseEntries(_context);
62+
7463
var auth = new OAuthSession(accessToken.Value, refreshToken.Value, 3600, "bearer");
75-
var client = new BoxClient(config, auth);
64+
var client = new BoxClient(_config, auth);
7665
/// Try to refresh the access token
7766
auth = await client.Auth.RefreshAccessTokenAsync(auth.AccessToken);
67+
_logger.LogInformation("RefreshAccessTokenAsync: Complete (RefreshAccessTokenAsync returned)");
7868
/// Create the client again
79-
client = new BoxClient(config, auth);
80-
_logger.LogInformation("Refreshed Tokens");
69+
client = new BoxClient(_config, auth);
70+
if (accessToken.Value != auth.AccessToken)
71+
{
72+
_logger.LogInformation($"Access Token Changed to ({auth.AccessToken.Substring(4)}...)");
73+
}
74+
else
75+
{
76+
_logger.LogInformation($"Access Token Unchanged ({auth.AccessToken.Substring(4)}...)");
77+
}
78+
if (refreshToken.Value != auth.RefreshToken)
79+
{
80+
_logger.LogInformation($"Refresh Token Changed to ({auth.RefreshToken.Substring(4)}...");
81+
}
82+
else
83+
{
84+
_logger.LogInformation($"Refresh Token Unchanged ({auth.RefreshToken.Substring(4)}...");
85+
}
86+
8187
accessToken.Value = auth.AccessToken;
8288
refreshToken.Value = auth.RefreshToken;
8389
await _context.SaveChangesAsync();
90+
_logger.LogInformation("RefreshAccessTokenAsync: Complete (database updated)");
8491
}
8592
}
8693
catch (Box.V2.Exceptions.BoxSessionInvalidatedException e)
@@ -89,24 +96,111 @@ public async Task RefreshAccessTokenAsync()
8996
await _slack.PostErrorAsync(e, "Box Token Failure.");
9097
throw;
9198
}
99+
_logger.LogInformation("RefreshAccessTokenAsync: returning");
100+
101+
}
102+
public async Task<(Dictionary,Dictionary)> getOrCreateDatabaseEntries(CTDbContext context)
103+
{
104+
// sanity check- expect 0 or 1 entries for key
105+
if(await context.Dictionaries.Where(d=>d.Key == CommonUtils.BOX_ACCESS_TOKEN).CountAsync() >1
106+
|| await context.Dictionaries.Where(d=>d.Key == CommonUtils.BOX_REFRESH_TOKEN).CountAsync() >1 )
107+
{ // should never happen
108+
var badEntries = context.Dictionaries.Where(d=>d.Key == CommonUtils.BOX_ACCESS_TOKEN || d.Key == CommonUtils.BOX_REFRESH_TOKEN);
109+
context.Dictionaries.RemoveRange(badEntries);
110+
await context.SaveChangesAsync();
111+
}
112+
var changed = false;
113+
114+
var accessToken = await context.Dictionaries.Where(d => d.Key == CommonUtils.BOX_ACCESS_TOKEN).FirstOrDefaultAsync();
115+
var refreshToken = await context.Dictionaries.Where(d => d.Key == CommonUtils.BOX_REFRESH_TOKEN).FirstOrDefaultAsync();
116+
117+
if (accessToken == null)
118+
{
119+
accessToken = new Dictionary
120+
{
121+
Key = CommonUtils.BOX_ACCESS_TOKEN
122+
};
123+
context.Dictionaries.Add(accessToken);
124+
changed = true;
125+
}
126+
if (refreshToken == null)
127+
{
128+
refreshToken = new Dictionary
129+
{
130+
Key = CommonUtils.BOX_REFRESH_TOKEN
131+
};
132+
context.Dictionaries.Add(refreshToken);
133+
changed = true;
134+
}
135+
if (changed)
136+
{
137+
await context.SaveChangesAsync();
138+
}
139+
return (accessToken, refreshToken);
92140
}
141+
93142
/// <summary>
94143
/// Creates a new box client, after first refreshing the access and refresh token.
95144
/// </summary>
96145
public async Task<BoxClient> GetBoxClientAsync()
97146
{
98147
// Todo RefreshAccessTokenAsync could return this information for us; and avoid another trip to the database
99-
await RefreshAccessTokenAsync();
100-
BoxClient boxClient;
101-
using (var _context = CTDbContext.CreateDbContext())
148+
int attempt = 1;
149+
int maxAttempt = 10;
150+
while (attempt < maxAttempt)
102151
{
103-
var accessToken = await _context.Dictionaries.Where(d => d.Key == CommonUtils.BOX_ACCESS_TOKEN).FirstAsync();
104-
var refreshToken = await _context.Dictionaries.Where(d => d.Key == CommonUtils.BOX_REFRESH_TOKEN).FirstAsync();
105-
var config = new BoxConfig(Globals.appSettings.BOX_CLIENT_ID, Globals.appSettings.BOX_CLIENT_SECRET, new Uri("http://locahost"));
106-
var auth = new OAuthSession(accessToken.Value, refreshToken.Value, 3600, "bearer");
107-
boxClient = new Box.V2.BoxClient(config, auth);
152+
using (var _context = CTDbContext.CreateDbContext())
153+
{
154+
_logger.LogInformation($"GetBoxClientAsync: Attempt {attempt} of {maxAttempt} to get valid client");
155+
// var accessToken = await _context.Dictionaries.Where(d => d.Key == CommonUtils.BOX_ACCESS_TOKEN).FirstOrDefaultAsync();
156+
//Dictionary accessToken,refreshToken;
157+
158+
var (accessToken, refreshToken) = await getOrCreateDatabaseEntries(_context);
159+
160+
if (string.IsNullOrEmpty(accessToken.Value))
161+
{
162+
_logger.LogInformation($"GetBoxClientAsync: Attempting box client using access token ({accessToken.Value.Substring(4)}...");
163+
try
164+
{
165+
var auth = new OAuthSession(accessToken.Value, "", 3600, "bearer");
166+
167+
var client = new BoxClient(_config, auth);
168+
_logger.LogInformation($"GetBoxClientAsync: Attempt {attempt} returning client using existing access token");
169+
return client; // Normal return here
170+
}
171+
catch (Exception e)
172+
{
173+
_logger.LogInformation(e, "GetBoxClientAsync: Existing access token is invalid");
174+
}
175+
}
176+
}
177+
if (refreshing > 0)
178+
{
179+
var sleep = 5 + 5 * attempt;
180+
_logger.LogInformation($"GetBoxClientAsync: refresh in progress - Sleeping {sleep} seconds - Give time for another thread to refresh the token before retrying");
181+
await Task.Delay(sleep * 1000);
182+
}
183+
else
184+
{
185+
Interlocked.Increment(ref refreshing); // threadsafe refreshing ++;
186+
_logger.LogInformation($"GetBoxClientAsync: Calling RefreshAccessTokenAsync");
187+
await RefreshAccessTokenAsync();
188+
Interlocked.Decrement(ref refreshing);
189+
}
108190
}
109-
return boxClient;
191+
_logger.LogError("Failed to authenticate with Box");
192+
throw new Exception("Failed to authenticate with Box");
193+
// BoxClient boxClient;
194+
// using (var _context = CTDbContext.CreateDbContext())
195+
// {
196+
// var accessToken = await _context.Dictionaries.Where(d => d.Key == CommonUtils.BOX_ACCESS_TOKEN).FirstAsync();
197+
// var refreshToken = await _context.Dictionaries.Where(d => d.Key == CommonUtils.BOX_REFRESH_TOKEN).FirstAsync();
198+
// var config = new BoxConfig(Globals.appSettings.BOX_CLIENT_ID, Globals.appSettings.BOX_CLIENT_SECRET, new Uri("http://locahost"));
199+
// var auth = new OAuthSession(accessToken.Value, refreshToken.Value, 3600, "bearer");
200+
// boxClient = new Box.V2.BoxClient(config, auth);
201+
// }
202+
// return boxClient;
110203
}
111204
}
112205
}
206+

TaskEngine/Tasks/DownloadMediaTask.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -135,10 +135,10 @@ protected async Task<bool> updateMediaWithVideo(string mediaId, Video newVideo)
135135
return false;
136136
}
137137
GetLogger().LogInformation($"Media ({mediaId}): existing media.Video {media.Video != null}");
138-
GetLogger().LogInformation($"Media ({mediaId}): media.Video?.Video1.Id={media.Video?.Video1.Id} ...Video2.Id={media.Video?.Video2.Id} ");
138+
GetLogger().LogInformation($"Media ({mediaId}): media.Video?.Video1.Id={media.Video?.Video1?.Id ?? "none"} ...Video2.Id={media.Video?.Video2?.Id ?? "none"}");
139139

140-
GetLogger().LogInformation($"Media ({mediaId}): downloaded: newVideo.Video1={newVideo.Video1} ...Video2={newVideo.Video2} ");
141-
GetLogger().LogInformation($"Media ({mediaId}): downloaded: newVideo.Video1.Hash={newVideo.Video1?.Hash} ...Hash2={newVideo.Video2?.Hash} ");
140+
GetLogger().LogInformation($"Media ({mediaId}): downloaded: newVideo.Video1={newVideo?.Video1.PrivatePath ?? "none"} ...Video2={newVideo?.Video2?.PrivatePath ?? "none"}");
141+
GetLogger().LogInformation($"Media ({mediaId}): downloaded: newVideo.Video1.Hash={newVideo.Video1?.Hash ?? "none"} ...Hash2={newVideo.Video2?.Hash ?? "none"} ");
142142

143143
//
144144
if(newVideo.Id != null) {

0 commit comments

Comments
 (0)