Skip to content

Commit 05363e0

Browse files
committed
Merge branch 'develop'
2 parents dd800bc + c24dbf0 commit 05363e0

7 files changed

Lines changed: 96 additions & 48 deletions

File tree

API.Test/AuthentificationTests.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,4 +45,13 @@ public void Authentification()
4545
}
4646
Assert.Pass();
4747
}
48+
49+
[Test]
50+
public void GetSessionExpiresDateTime()
51+
{
52+
using WebUntisClient client = new("WebUntisAPI_TEST", TimeSpan.FromSeconds(5));
53+
client.LoginAsync(s_Server, s_LoginName, s_UserName, s_Password).Wait();
54+
_ = client.SessionExpires;
55+
_ = client.SessionBegin;
56+
}
4857
}

API.Test/TeachingTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public void GetClass()
3030
{
3131
Client.LoginAsync(s_Server, s_LoginName, s_UserName, s_Password).Wait();
3232

33-
Task<Class[]> classes = Client.GetClassesAsync(new SchoolYear() { Id = 5});
33+
Task<Class[]> classes = Client.GetClassesAsync(new SchoolYear() { Id = 5 });
3434
classes.Wait();
3535
if (classes.Result.Length > 0)
3636
Assert.Pass();

WebUntisAPI.Client/Models/Messages/Attachment.cs

Lines changed: 24 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@
66
using System.Linq;
77
using System.Net;
88
using System.Net.Http;
9-
using System.Text;
10-
using System.Text.RegularExpressions;
119
using System.Threading;
1210
using System.Threading.Tasks;
1311

@@ -34,15 +32,16 @@ public struct Attachment
3432
/// <summary>
3533
/// Get the content of the attachment as stream with a progress report
3634
/// </summary>
37-
/// <param name="client">The client with them you got this instane (It doesn't have to be the same client but the same account)</param>
38-
/// <param name="timeout">The time after that the download of the content will cancelled (In miliseconds)</param>
35+
/// <param name="client">The client with them you got this instance (It doesn't have to be the same client but the same account)</param>
36+
/// <param name="targetStream">The stream to write the download to</param>
37+
/// <param name="timeout">The time after that the download of the content will cancelled (In milliseconds)</param>
3938
/// <param name="progress">The progress of the download in percent</param>
4039
/// <param name="ct">Cancellation token</param>
4140
/// <returns>The attachment as stream</returns>
4241
/// <exception cref="ObjectDisposedException">Thrown when the instance was disposed</exception>
4342
/// <exception cref="UnauthorizedAccessException">Thrown when you're logged in</exception>
4443
/// <exception cref="HttpRequestException">Thrown when an error happened while the http request</exception>
45-
public async Task<Stream> DownloadContentAsStreamAsync(WebUntisClient client, TimeSpan timeout, IProgress<double> progress = null, CancellationToken ct = default)
44+
public async Task DownloadContentAsStreamAsync(WebUntisClient client, Stream targetStream, TimeSpan timeout, IProgress<double> progress = null, CancellationToken ct = default)
4645
{
4746
string storageResponseString = await client.MakeAPIGetRequestAsync($"/WebUntis/api/rest/view/v1/messages/{Id}/attachmentstorageurl", ct);
4847

@@ -69,41 +68,33 @@ public async Task<Stream> DownloadContentAsStreamAsync(WebUntisClient client, Ti
6968
downloadClient.Timeout = timeout;
7069
HttpResponseMessage response = await downloadClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, ct);
7170

72-
using (MemoryStream content = new MemoryStream())
71+
using (Stream contentStream = await response.Content.ReadAsStreamAsync())
7372
{
74-
using (Stream contentStream = await response.Content.ReadAsStreamAsync())
75-
{
76-
int totalBytes = (int?)response.Content.Headers.ContentLength ?? -1;
77-
int totalRecievedBytes = 0;
73+
int totalBytes = (int?)response.Content.Headers.ContentLength ?? -1;
74+
int totalReceivedBytes = 0;
7875

79-
int bytesRead = 0;
80-
byte[] buffer = new byte[8192];
81-
while ((bytesRead = await contentStream.ReadAsync(buffer, 0, buffer.Length, ct)) > 0)
82-
{
83-
if (ct.IsCancellationRequested)
84-
return default;
76+
int bytesRead = 0;
77+
byte[] buffer = new byte[4096];
78+
while ((bytesRead = await contentStream.ReadAsync(buffer, 0, buffer.Length, ct)) > 0)
79+
{
80+
if (ct.IsCancellationRequested)
81+
return;
8582

86-
content.Write(buffer, 0, bytesRead);
87-
totalRecievedBytes += bytesRead;
88-
progress?.Report((double)totalRecievedBytes / totalBytes * 100d);
89-
}
83+
targetStream.Write(buffer, 0, bytesRead);
84+
totalReceivedBytes += bytesRead;
85+
progress?.Report((double)totalReceivedBytes / totalBytes * 100d);
9086
}
87+
}
9188

92-
if (ct.IsCancellationRequested)
93-
return default;
89+
if (ct.IsCancellationRequested)
90+
return;
9491

95-
// Verify response
96-
if (response.StatusCode == HttpStatusCode.Unauthorized || response.StatusCode == HttpStatusCode.Forbidden)
97-
{
98-
string detail = Regex.Match(Encoding.UTF8.GetString(content.ToArray()), @"<Message>([a-zA-z0-9\s]+)</Message>").Groups[1].Value; // Get the error message
99-
throw new UnauthorizedAccessException($"Invalid authentication. Detail: {detail}");
100-
}
101-
102-
if (response.StatusCode != HttpStatusCode.OK)
103-
throw new HttpRequestException($"There was an error while the http request (Code: {response.StatusCode}).");
92+
// Verify response
93+
if (response.StatusCode == HttpStatusCode.Unauthorized || response.StatusCode == HttpStatusCode.Forbidden)
94+
throw new UnauthorizedAccessException($"Invalid authentication.");
10495

105-
return content;
106-
}
96+
if (response.StatusCode != HttpStatusCode.OK)
97+
throw new HttpRequestException($"There was an error while the http request (Code: {response.StatusCode}).");
10798
}
10899
}
109100
}

WebUntisAPI.Client/Models/Messages/MessagePreview.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ public class MessagePreview
5353
/// </summary>
5454
[JsonProperty("sentDateTime")]
5555
[JsonConverter(typeof(APIDateTimeJsonConverter))]
56-
DateTime SentTime { get; set; }
56+
public DateTime SentTime { get; set; }
5757

5858
/// <summary>
5959
/// Is allowed to delete the message

WebUntisAPI.Client/WebUntisAPI.Client.csproj

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@
1616
<Copyright>Copyright © Suiram1 2023</Copyright>
1717
<Description>A library in .NET to interact with the WebUntis API.</Description>
1818
<Company>Suiram1</Company>
19-
<AssemblyVersion>1.1.0</AssemblyVersion>
20-
<FileVersion>1.1.0</FileVersion>
21-
<Version>1.1.0</Version>
19+
<AssemblyVersion>1.1.1</AssemblyVersion>
20+
<FileVersion>1.1.1</FileVersion>
21+
<Version>1.1.1</Version>
2222
</PropertyGroup>
2323

2424
<ItemGroup>

WebUntisAPI.Client/WebUntisClient.Messages.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,14 @@ public async Task<MessagePreview> SendMessageAsync(Draft draft, MessagePerson[]
167167
Dictionary<string, Task<Stream>> attachmentTasks = new Dictionary<string, Task<Stream>>();
168168

169169
foreach (Attachment attachment in draft.Attachments)
170-
attachmentTasks.Add(attachment.Name, attachment.DownloadContentAsStreamAsync(this, timeout, ct: ct));
170+
{
171+
attachmentTasks.Add(attachment.Name, Task.Run(async () =>
172+
{
173+
Stream stream = new MemoryStream();
174+
await attachment.DownloadContentAsStreamAsync(this, stream, timeout, ct: ct);
175+
return stream;
176+
}));
177+
}
171178

172179
await Task.WhenAll(attachmentTasks.Values);
173180
attachments = attachmentTasks.Select(attachment => new Tuple<string, Stream>(attachment.Key, attachment.Value.Result)).ToArray();

WebUntisAPI.Client/WebUntisClient.cs

Lines changed: 50 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,49 @@ public partial class WebUntisClient : IDisposable
2828
/// </summary>
2929
public string ClientName { get; }
3030

31+
/// <summary>
32+
/// The dateTime where the current session expires
33+
/// </summary>
34+
/// <remarks>
35+
/// You can refresh the session with <see cref="ReloadSessionAsync(CancellationToken)"/>
36+
/// </remarks>
37+
public DateTime SessionExpires
38+
{
39+
get
40+
{
41+
if (_disposedValue)
42+
throw new ObjectDisposedException(GetType().FullName);
43+
44+
if (!LoggedIn)
45+
throw new UnauthorizedAccessException("You're not logged in!");
46+
47+
string tokenString = _bearerToken.Split('.')[1]; // Get the JWT part
48+
49+
JObject token = JObject.Parse(Encoding.UTF8.GetString(Convert.FromBase64String(tokenString)));
50+
return new DateTime(1970, 01, 01, 0, 0, 0, DateTimeKind.Utc).AddSeconds(token["exp"].Value<long>());
51+
}
52+
}
53+
54+
/// <summary>
55+
/// The date Time off beginning of the current session
56+
/// </summary>
57+
public DateTime SessionBegin
58+
{
59+
get
60+
{
61+
if (_disposedValue)
62+
throw new ObjectDisposedException(GetType().FullName);
63+
64+
if (!LoggedIn)
65+
throw new UnauthorizedAccessException("You're not logged in!");
66+
67+
string tokenString = _bearerToken.Split('.')[1]; // Get the JWT part
68+
69+
JObject token = JObject.Parse(Encoding.UTF8.GetString(Convert.FromBase64String(tokenString)));
70+
return new DateTime(1970, 01, 01, 0, 0, 0, DateTimeKind.Utc).AddSeconds(token["iat"].Value<long>());
71+
}
72+
}
73+
3174
/// <summary>
3275
/// <see langword="true"/> when the client is currently logged in
3376
/// </summary>
@@ -195,7 +238,7 @@ public async Task<bool> LoginAsync(string server, string loginName, string usern
195238
_loggedIn = true;
196239

197240
// Get the api auth token and the logged in user
198-
Task<string> bearerTokenTask = GetBearerTokenAsync(ct);
241+
Task bearerTokenTask = ReloadSessionAsync(ct);
199242
IUser[] users;
200243
if ((UserType)responseObject["result"]["personType"].ToObject<int>() == Client.UserType.Student) // For student and teacher separate tasks
201244
{
@@ -216,8 +259,6 @@ public async Task<bool> LoginAsync(string server, string loginName, string usern
216259
return false;
217260
}
218261

219-
_bearerToken = bearerTokenTask.Result;
220-
221262
_userType = (UserType)responseObject["result"]["personType"].ToObject<int>();
222263
_user = users.FirstOrDefault(user => user.Id == responseObject["result"]["personId"].ToObject<int>());
223264

@@ -373,7 +414,7 @@ private async Task<JToken> MakeJSONRPCRequestAsync(string id, string methodName,
373414
/// Make a GET request to the API
374415
/// </summary>
375416
/// <param name="requestUrl">Url to request</param>
376-
/// <param name="ct">Cnacellation token</param>
417+
/// <param name="ct">Cancelation token</param>
377418
/// <returns>The returned content</returns>
378419
/// <exception cref="ObjectDisposedException">Thrown when the instance was disposed</exception>
379420
/// <exception cref="UnauthorizedAccessException">Thrown when the client isn't logged in</exception>
@@ -417,12 +458,11 @@ internal async Task<string> MakeAPIGetRequestAsync(string requestUrl, Cancellati
417458
}
418459

419460
/// <summary>
420-
/// Get the bearer auth token for the api authentication
461+
/// Refresh the session
421462
/// </summary>
422463
/// <param name="ct">Cancellation token</param>
423-
/// <returns>The bearer token</returns>
424464
/// <exception cref="HttpRequestException">Thrown when an error happend while the http request</exception>
425-
private async Task<string> GetBearerTokenAsync(CancellationToken ct)
465+
public async Task ReloadSessionAsync(CancellationToken ct = default)
426466
{
427467
HttpRequestMessage request = new HttpRequestMessage()
428468
{
@@ -436,13 +476,14 @@ private async Task<string> GetBearerTokenAsync(CancellationToken ct)
436476

437477
// Check cancellation token
438478
if (ct.IsCancellationRequested)
439-
return default;
479+
return;
440480

441481
// Verify response
442482
if (response.StatusCode != HttpStatusCode.OK)
443483
throw new HttpRequestException($"There was an error while the http request (Code: {response.StatusCode}).");
444484

445-
return await response.Content.ReadAsStringAsync();
485+
486+
_bearerToken = await response.Content.ReadAsStringAsync();
446487
}
447488

448489
#region IDisposable

0 commit comments

Comments
 (0)