Compare commits

...
1 Commits
Author SHA1 Message Date
amacocianandGitHub c8ba125abc Allow individual uploads/downloads for backed up builds (#77) 2022-08-25 20:44:20 +00:00
10 changed files with 279 additions and 126 deletions
+1 -1
View File
@@ -10,7 +10,7 @@
<LangVersion>preview</LangVersion>
<ApplicationIcon>Daybreak.ico</ApplicationIcon>
<IncludePackageReferencesDuringMarkupCompilation>true</IncludePackageReferencesDuringMarkupCompilation>
<Version>0.9.6.2</Version>
<Version>0.9.6.3</Version>
<EnableWindowsTargeting>true</EnableWindowsTargeting>
<UserSecretsId>cfb2a489-db80-448d-a969-80270f314c46</UserSecretsId>
</PropertyGroup>
+9
View File
@@ -0,0 +1,9 @@
using Daybreak.Models.Builds;
namespace Daybreak.Models;
public sealed class BuildWithTemplateCode
{
public BuildEntry Build { get; set; }
public string TemplateCode { get; set; }
}
@@ -80,6 +80,22 @@ namespace Daybreak.Services.BuildTemplates
}
}
public async Task<Result<BuildEntry, Exception>> GetBuild(string name)
{
if (File.Exists($"{BuildsPath}/{name}.txt") is false)
{
return new InvalidOperationException("Unable to find build file");
}
var content = await File.ReadAllTextAsync($"{BuildsPath}/{name}.txt");
if (this.TryDecodeTemplate(content, out var build) is false)
{
return new InvalidOperationException("Unable to parse build file");
}
return new BuildEntry { Build = build, Name = name, PreviousName = name };
}
public void ClearBuilds()
{
foreach(var file in Directory.GetFiles(BuildsPath))
@@ -260,7 +276,7 @@ namespace Daybreak.Services.BuildTemplates
buildMetadata.NewTemplate = false;
}
buildMetadata.ProfessionIdLength = stream.Read(2) * 2 + 4;
buildMetadata.ProfessionIdLength = (stream.Read(2) * 2) + 4;
buildMetadata.PrimaryProfessionId = stream.Read(buildMetadata.ProfessionIdLength);
buildMetadata.SecondaryProfessionId = stream.Read(buildMetadata.ProfessionIdLength);
buildMetadata.AttributeCount = stream.Read(4);
@@ -297,7 +313,7 @@ namespace Daybreak.Services.BuildTemplates
var desiredProfessionIdLength = GetBitLength(new List<int> { buildMetadata.PrimaryProfessionId, buildMetadata.SecondaryProfessionId }.Max());
var professionIdLength = Math.Max((desiredProfessionIdLength - 4) / 2, 0);
var finalProfessionIdLength = professionIdLength * 2 + 4;
var finalProfessionIdLength = (professionIdLength * 2) + 4;
stream.Write(professionIdLength, 2);
stream.Write(buildMetadata.PrimaryProfessionId, finalProfessionIdLength);
stream.Write(buildMetadata.SecondaryProfessionId, finalProfessionIdLength);
@@ -1,5 +1,8 @@
using Daybreak.Models.Builds;
using System;
using System.Collections.Generic;
using System.Extensions;
using System.Threading.Tasks;
namespace Daybreak.Services.BuildTemplates
{
@@ -12,6 +15,7 @@ namespace Daybreak.Services.BuildTemplates
void SaveBuild(BuildEntry buildEntry);
void RemoveBuild(BuildEntry buildEntry);
IAsyncEnumerable<BuildEntry> GetBuilds();
Task<Result<BuildEntry, Exception>> GetBuild(string name);
Build DecodeTemplate(string template);
bool TryDecodeTemplate(string template, out Build build);
string EncodeTemplate(Build build);
+76 -60
View File
@@ -15,7 +15,6 @@ using System.Extensions;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
@@ -40,13 +39,15 @@ public sealed class GraphClient : IGraphClient
private const string GraphBaseUrl = "https://graph.microsoft.com/v1.0/";
private const string TokenUrlPlaceholder = $"https://login.microsoftonline.com/consumers/oauth2/v2.0/token";
private const string AuthorizationUrlPlaceholder = $"https://login.microsoftonline.com/consumers/oauth2/v2.0/authorize?client_id={ClientIdPlaceholder}&response_type=code&redirect_uri={RedirectUriPlaceholder}&response_mode=query&scope={ScopesPlaceholder}&state={StatePlaceholder}";
private const string SyncFileUri = $"me/drive/root:/Daybreak/{BackupFileName}";
private const string SyncFolderUri = $"me/drive/root:/Daybreak/Builds{SuffixPlaceholder}";
private const string SyncFileUri = $"me/drive/root:/Daybreak/Builds/{FilenamePlaceholder}{ContentSuffix}";
private const string ContentSuffix = ":/content";
private const string BackupFileName = "daybreak_backup.json";
private const string ChildrenSuffix = ":/children";
private const string SuffixPlaceholder = "[Suffix]";
private const string FilenamePlaceholder = "[File]";
private static readonly byte[] Entropy = Convert.FromBase64String("R3VpbGR3YXJz");
private static readonly string ApplicationId = SecretManager.GetSecret(SecretKeys.AadApplicationId);
private static readonly string TenantId = SecretManager.GetSecret(SecretKeys.AadTenantId);
private readonly IBuildTemplateManager buildTemplateManager;
private readonly IViewManager viewManager;
@@ -75,7 +76,7 @@ public sealed class GraphClient : IGraphClient
ChromiumBrowserWrapper chromiumBrowserWrapper,
CancellationToken cancellationToken = default)
{
var maybeAuthCode = await this.RetrieveAuthorizationCode(chromiumBrowserWrapper, cancellationToken);
var maybeAuthCode = await RetrieveAuthorizationCode(chromiumBrowserWrapper, cancellationToken);
if (maybeAuthCode.TryExtractSuccess(out var authCode) is false)
{
return maybeAuthCode.SwitchAny(onFailure: exception => exception);
@@ -130,22 +131,17 @@ public sealed class GraphClient : IGraphClient
public async Task<Result<bool, Exception>> UploadBuilds()
{
var compiledBuilds = await this.buildTemplateManager.GetBuilds().Select(b => new BuildFile { FileName = b.Name, TemplateCode = this.buildTemplateManager.EncodeTemplate(b.Build) }).ToListAsync();
var serializedBuilds = JsonConvert.SerializeObject(compiledBuilds);
await this.UploadBackupItem(serializedBuilds);
await this.buildTemplateManager.GetBuilds()
.ForEachAsync(async buildEntry => await this.PutFileItem(buildEntry));
return true;
}
public async Task<Result<bool, Exception>> DownloadBuilds()
{
var maybeBuilds = await this.RetrieveBuildsList();
if (maybeBuilds.TryExtractSuccess(out var builds) is false)
{
return maybeBuilds.SwitchAny(onFailure: exception => exception);
}
var builds = this.RetrieveBuildsList();
this.buildTemplateManager.ClearBuilds();
_ = builds.Select(buildFile =>
var compiledBuilds = await builds.Select(buildFile =>
{
if (this.buildTemplateManager.TryDecodeTemplate(buildFile.TemplateCode, out var build) is false)
{
@@ -158,33 +154,75 @@ public sealed class GraphClient : IGraphClient
Name = buildFile.FileName,
PreviousName = buildFile.FileName
};
}).Where(entry => entry is not null)
.Do(this.buildTemplateManager.SaveBuild).ToList();
}).Where(entry => entry is not null).ToListAsync();
_ = compiledBuilds.Do(this.buildTemplateManager.SaveBuild).ToList();
return true;
}
public async Task<Result<List<BuildFile>, Exception>> RetrieveBuildsList()
public async Task<Result<bool, Exception>> DownloadBuild(string buildName)
{
var maybeCompiledBuilds = await this.GetBackupItemContent();
if (maybeCompiledBuilds.ExtractValue() is not string compiledBuilds)
{
return new InvalidOperationException("No backup found");
}
var builds = this.RetrieveBuildsList();
var builds = JsonConvert.DeserializeObject<List<BuildFile>>(compiledBuilds);
return builds;
var compiledBuilds = await builds
.Where(build => build.FileName == buildName)
.Select(buildFile =>
{
if (this.buildTemplateManager.TryDecodeTemplate(buildFile.TemplateCode, out var build) is false)
{
return null;
}
return new BuildEntry
{
Build = build,
Name = buildFile.FileName,
PreviousName = buildFile.FileName
};
}).Where(entry => entry is not null).ToListAsync();
_ = compiledBuilds.Do(this.buildTemplateManager.SaveBuild).ToList();
return true;
}
public async Task<Result<DateTime, Exception>> GetLastUpdateTime()
public async Task<Result<bool, Exception>> UploadBuild(string buildName)
{
var maybeDriveItem = await this.GetDriveItem();
if (maybeDriveItem.ExtractValue() is not DriveItem driveItem)
var getBuildResult = await this.buildTemplateManager.GetBuild(buildName);
if (getBuildResult.TryExtractSuccess(out var buildEntry) is false)
{
return new InvalidOperationException("Unable to retrieve the drive file");
return getBuildResult.SwitchAny(
onFailure: exception => exception);
}
return driveItem.LastModifiedDateTime;
return await this.PutFileItem(buildEntry);
}
public async IAsyncEnumerable<BuildFile> RetrieveBuildsList()
{
var folderResult = await this.GetFolderItem();
if (folderResult.ExtractValue() is not FolderItem folder)
{
folder = new FolderItem { Files = new List<FileItem>() };
}
var retrieveContentTasks = folder.Files
.Where(file => file.Name.EndsWith(".txt"))
.Select(async file =>
{
var response = await this.httpClient.GetAsync(file.DownloadUrl);
if (response.IsSuccessStatusCode is false)
{
return null;
}
return new BuildFile { FileName = file.Name.Replace(".txt", string.Empty), TemplateCode = await response.Content.ReadAsStringAsync() };
})
.ToList();
foreach(var task in retrieveContentTasks)
{
yield return await task;
}
}
public void ResetAuthorization()
@@ -192,48 +230,26 @@ public sealed class GraphClient : IGraphClient
this.ResetAccessToken();
}
private async Task<Optional<string>> GetBackupItemContent()
private async Task<bool> PutFileItem(BuildEntry buildEntry)
{
var maybeDriveItem = await this.GetDriveItem();
if (maybeDriveItem.ExtractValue() is not DriveItem driveItem)
{
return Optional.None<string>();
}
if (driveItem.Name != BackupFileName)
{
return Optional.None<string>();
}
var response = await this.httpClient.GetAsync(driveItem.DownloadUrl);
if (response.IsSuccessStatusCode is false)
{
return Optional.None<string>();
}
return await response.Content.ReadAsStringAsync();
}
private async Task<bool> UploadBackupItem(string serializedBuilds)
{
using var stringContent = new StringContent(serializedBuilds);
var response = await this.httpClient.PutAsync(SyncFileUri + ContentSuffix, stringContent);
using var stringContent = new StringContent(this.buildTemplateManager.EncodeTemplate(buildEntry.Build));
var response = await this.httpClient.PutAsync(SyncFileUri.Replace(FilenamePlaceholder, $"{buildEntry.Name}.txt"), stringContent);
return response.IsSuccessStatusCode;
}
private async Task<Optional<DriveItem>> GetDriveItem()
private async Task<Optional<FolderItem>> GetFolderItem()
{
var response = await this.httpClient.GetAsync(SyncFileUri);
var response = await this.httpClient.GetAsync(SyncFolderUri.Replace(SuffixPlaceholder, ChildrenSuffix));
if (response.IsSuccessStatusCode is false)
{
return Optional.None<DriveItem>();
return Optional.None<FolderItem>();
}
var driveItemContent = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<DriveItem>(driveItemContent);
return JsonConvert.DeserializeObject<FolderItem>(driveItemContent);
}
private async Task<Result<string, Exception>> RetrieveAuthorizationCode(
private static async Task<Result<string, Exception>> RetrieveAuthorizationCode(
ChromiumBrowserWrapper chromiumBrowserWrapper,
CancellationToken cancellationToken = default)
{
@@ -258,7 +274,7 @@ public sealed class GraphClient : IGraphClient
return new TaskCanceledException();
}
await Task.Delay(1000).ConfigureAwait(true);
await Task.Delay(1000, cancellationToken).ConfigureAwait(true);
}
var query = HttpUtility.ParseQueryString(chromiumBrowserWrapper.Address.Split('?').Skip(1).FirstOrDefault());
+3 -2
View File
@@ -16,7 +16,8 @@ public interface IGraphClient
Task<Result<bool, Exception>> PerformAuthorizationFlow(ChromiumBrowserWrapper chromiumBrowserWrapper, CancellationToken cancellationToken = default);
Task<Result<bool, Exception>> UploadBuilds();
Task<Result<bool, Exception>> DownloadBuilds();
Task<Result<List<BuildFile>, Exception>> RetrieveBuildsList();
Task<Result<DateTime, Exception>> GetLastUpdateTime();
Task<Result<bool, Exception>> UploadBuild(string buildName);
Task<Result<bool, Exception>> DownloadBuild(string buildName);
IAsyncEnumerable<BuildFile> RetrieveBuildsList();
void ResetAuthorization();
}
@@ -3,7 +3,7 @@ using System;
namespace Daybreak.Services.Graph.Models;
public sealed class DriveItem
public sealed class FileItem
{
[JsonProperty("@microsoft.graph.downloadUrl")]
public string DownloadUrl { get; set; }
@@ -0,0 +1,10 @@
using Newtonsoft.Json;
using System.Collections.Generic;
namespace Daybreak.Services.Graph.Models;
public sealed class FolderItem
{
[JsonProperty("value")]
public List<FileItem> Files { get; set; }
}
+95 -30
View File
@@ -5,14 +5,17 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Daybreak.Views"
xmlns:controls="clr-namespace:Daybreak.Controls"
xmlns:converters="clr-namespace:Daybreak.Converters"
Loaded="UserControl_Loaded"
mc:Ignorable="d"
x:Name="_this"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"></BooleanToVisibilityConverter>
</UserControl.Resources>
<Grid
Background="#A0202020">
<Grid.RowDefinitions>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
@@ -20,20 +23,6 @@
<controls:BackButton Foreground="White" Height="30" Width="30" Grid.Column="0" HorizontalAlignment="Left" Margin="5"
Clicked="BackButton_Clicked" VerticalAlignment="Top"
IsEnabled="{Binding ElementName=_this, Path=ButtonsEnabled, Mode=OneWay}"></controls:BackButton>
<controls:BackButton Foreground="White" Height="30" Width="30" HorizontalAlignment="Right" Margin="0, 5, 45, 5"
Clicked="DownloadButton_Clicked"
IsEnabled="{Binding ElementName=_this, Path=ButtonsEnabled, Mode=OneWay}">
<controls:BackButton.RenderTransform>
<RotateTransform Angle="270" CenterX="15" CenterY="15"></RotateTransform>
</controls:BackButton.RenderTransform>
</controls:BackButton>
<controls:BackButton Foreground="White" Height="30" Width="30" HorizontalAlignment="Right" Margin="0, 5, 5, 5"
Clicked="UploadButton_Clicked"
IsEnabled="{Binding ElementName=_this, Path=ButtonsEnabled, Mode=OneWay}">
<controls:BackButton.RenderTransform>
<RotateTransform Angle="90" CenterX="15" CenterY="15"></RotateTransform>
</controls:BackButton.RenderTransform>
</controls:BackButton>
<TextBlock HorizontalAlignment="Center"
Text="Build templates synchronization"
FontSize="24"
@@ -47,32 +36,45 @@
FontSize="20"
Foreground="White"></TextBlock>
</WrapPanel>
<WrapPanel HorizontalAlignment="Center"
Grid.Row="2">
<TextBlock Text="Last uploaded: "
Foreground="White"
FontSize="18"/>
<TextBlock Text="{Binding ElementName=_this, Path=LastUploadDate, Mode=OneWay}"
Foreground="White"
FontSize="20"/>
</WrapPanel>
<Grid Grid.Row="3">
<Grid Grid.Row="2">
<Grid.RowDefinitions>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<TextBlock Text="Preview uploaded templates"
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0"
Text="Local"
FontSize="14"
Foreground="White"
HorizontalAlignment="Center"/>
<ListView Grid.Row="1" Background="Transparent" ItemsSource="{Binding ElementName=_this, Path=BuildEntries, Mode=OneWay}"
HorizontalContentAlignment="Stretch">
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
<controls:OpaqueButton Grid.Column="0"
Text="Upload all"
Foreground="White"
Background="DarkGray"
HorizontalAlignment="Right"
Padding="5"
Margin="5"
Clicked="UploadAllButton_Clicked"
Width="80"
IsEnabled="{Binding ElementName=_this, Path=ButtonsEnabled, Mode=OneWay}"/>
<ListView
Grid.Column="0"
Grid.Row="1"
Background="Transparent"
ItemsSource="{Binding ElementName=_this, Path=LocalBuildEntries, Mode=OneWay}"
SelectedItem="{Binding ElementName=_this, Path=SelectedLocalBuild, Mode=TwoWay}"
HorizontalContentAlignment="Stretch">
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<TextBlock FontSize="14"
Foreground="White"
Text="{Binding FileName}"
Text="{Binding Build.Name}"
HorizontalAlignment="Left"></TextBlock>
<TextBlock FontSize="14"
Foreground="White"
@@ -82,6 +84,69 @@
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<TextBlock Grid.Column="2"
Text="Remote"
FontSize="14"
Foreground="White"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
<controls:OpaqueButton Grid.Column="2"
Text="Download all"
Foreground="White"
Background="DarkGray"
HorizontalAlignment="Left"
Padding="5"
Margin="5"
Clicked="DownloadAllButton_Clicked"
Width="80"
IsEnabled="{Binding ElementName=_this, Path=ButtonsEnabled, Mode=OneWay}"/>
<ListView
Grid.Column="2"
Grid.Row="1"
Background="Transparent"
ItemsSource="{Binding ElementName=_this, Path=RemoteBuildEntries, Mode=OneWay}"
SelectedItem="{Binding ElementName=_this, Path=SelectedRemoteBuild, Mode=TwoWay}"
HorizontalContentAlignment="Stretch">
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<TextBlock FontSize="14"
Foreground="White"
Text="{Binding TemplateCode}"
HorizontalAlignment="Left"></TextBlock>
<TextBlock FontSize="14"
Foreground="White"
Text="{Binding FileName}"
HorizontalAlignment="Right"></TextBlock>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<StackPanel Grid.Row="1"
Grid.Column="1"
VerticalAlignment="Center"
HorizontalAlignment="Center">
<controls:OpaqueButton Text="Download Build"
Foreground="White"
Background="DarkGray"
Margin="5"
Padding="5"
Clicked="DownloadButton_Clicked"
IsEnabled="{Binding ElementName=_this, Path=ButtonsEnabled, Mode=OneWay}"></controls:OpaqueButton>
<controls:OpaqueButton Text="Upload Build"
Foreground="White"
Background="DarkGray"
Margin="5"
Padding="5"
Clicked="UploadButton_Clicked"
IsEnabled="{Binding ElementName=_this, Path=ButtonsEnabled, Mode=OneWay}"></controls:OpaqueButton>
</StackPanel>
</Grid>
<Grid Grid.RowSpan="3"
Background="#A0202020"
Visibility="{Binding ElementName=_this, Path=ShowLoading, Mode=OneWay, Converter={StaticResource BooleanToVisibilityConverter}}">
<controls:CircularLoadingWidget MaxWidth="200"
MaxHeight="200"/>
</Grid>
</Grid>
</UserControl>
@@ -10,6 +10,9 @@ using Daybreak.Services.Graph.Models;
using System.Extensions;
using System.Threading.Tasks;
using System;
using Daybreak.Services.BuildTemplates;
using System.Linq;
using Daybreak.Models;
namespace Daybreak.Views;
@@ -18,11 +21,13 @@ namespace Daybreak.Views;
/// </summary>
public partial class BuildsSynchronizationView : UserControl
{
private readonly IBuildTemplateManager buildTemplateManager;
private readonly IGraphClient graphClient;
private readonly IViewManager viewManager;
private readonly ILogger<BuildsSynchronizationView> logger;
public ObservableCollection<BuildFile> BuildEntries { get; } = new();
public ObservableCollection<BuildFile> RemoteBuildEntries { get; } = new();
public ObservableCollection<BuildWithTemplateCode> LocalBuildEntries { get; } = new();
[GenerateDependencyProperty(InitialValue = true)]
private bool buttonsEnabled;
@@ -30,12 +35,20 @@ public partial class BuildsSynchronizationView : UserControl
private string displayName;
[GenerateDependencyProperty]
private string lastUploadDate;
[GenerateDependencyProperty]
private BuildFile selectedRemoteBuild;
[GenerateDependencyProperty]
private BuildWithTemplateCode selectedLocalBuild;
[GenerateDependencyProperty]
private bool showLoading;
public BuildsSynchronizationView(
IBuildTemplateManager buildTemplateManager,
IGraphClient graphClient,
IViewManager viewManager,
ILogger<BuildsSynchronizationView> logger)
{
this.buildTemplateManager = buildTemplateManager.ThrowIfNull();
this.graphClient = graphClient.ThrowIfNull();
this.viewManager = viewManager.ThrowIfNull();
this.logger = logger.ThrowIfNull();
@@ -45,6 +58,7 @@ public partial class BuildsSynchronizationView : UserControl
private async void UserControl_Loaded(object sender, RoutedEventArgs e)
{
this.ButtonsEnabled = false;
this.ShowLoading = true;
var profile = await this.graphClient.GetUserProfile<BuildsSynchronizationView>();
if (profile.TryExtractSuccess(out var user) is false)
{
@@ -53,31 +67,16 @@ public partial class BuildsSynchronizationView : UserControl
}
this.DisplayName = user.DisplayName;
await this.PopulateLastUpdateTime();
await this.PopulateBuilds();
this.ButtonsEnabled = true;
}
private async Task PopulateLastUpdateTime()
{
var maybeDateTime = await this.graphClient.GetLastUpdateTime();
if (maybeDateTime.TryExtractSuccess(out var dateTime))
{
this.LastUploadDate = dateTime.ToString("G");
}
else
{
this.LastUploadDate = "Never";
}
this.ShowLoading = false;
}
private async Task PopulateBuilds()
{
var maybeBuilds = await this.graphClient.RetrieveBuildsList();
if (maybeBuilds.TryExtractSuccess(out var builds))
{
this.BuildEntries.ClearAnd().AddRange(builds);
}
this.RemoteBuildEntries.ClearAnd().AddRange(await this.graphClient.RetrieveBuildsList().ToListAsync());
var localBuilds = await this.buildTemplateManager.GetBuilds().ToListAsync();
this.LocalBuildEntries.ClearAnd().AddRange(localBuilds.Select(build => new BuildWithTemplateCode { Build = build, TemplateCode = this.buildTemplateManager.EncodeTemplate(build.Build) }));
}
private void BackButton_Clicked(object sender, EventArgs e)
@@ -87,31 +86,64 @@ public partial class BuildsSynchronizationView : UserControl
private async void UploadButton_Clicked(object sender, EventArgs e)
{
this.ButtonsEnabled = false;
var result = await this.graphClient.UploadBuilds();
result.DoAny(
onFailure: (failure) =>
{
this.logger.LogError(failure, $"Failed to upload builds");
});
// TODO: Handle failures to upload
if (this.SelectedLocalBuild is not BuildWithTemplateCode buildWithTemplateCode)
{
return;
}
await this.PopulateLastUpdateTime();
this.ButtonsEnabled = false;
this.ShowLoading = true;
await this.graphClient.UploadBuild(buildWithTemplateCode.Build.Name);
await this.PopulateBuilds();
this.ButtonsEnabled = true;
this.ShowLoading = false;
}
private async void DownloadButton_Clicked(object sender, EventArgs e)
{
if (this.SelectedRemoteBuild is not BuildFile buildFile)
{
return;
}
this.ButtonsEnabled = false;
var result = await this.graphClient.DownloadBuilds();
this.ShowLoading = true;
await this.graphClient.DownloadBuild(buildFile.FileName);
await this.PopulateBuilds();
this.ButtonsEnabled = true;
this.ShowLoading = false;
}
private async void DownloadAllButton_Clicked(object sender, EventArgs e)
{
this.ButtonsEnabled = false;
this.ShowLoading = true;
var result = await this.graphClient.DownloadBuilds().ConfigureAwait(true);
result.DoAny(
onFailure: (failure) =>
{
this.logger.LogError(failure, $"Failed to download builds");
});
await this.PopulateLastUpdateTime();
await this.PopulateBuilds();
this.ButtonsEnabled = true;
this.ShowLoading = false;
}
private async void UploadAllButton_Clicked(object sender, EventArgs e)
{
this.ButtonsEnabled = false;
this.ShowLoading = true;
var result = await this.graphClient.UploadBuilds().ConfigureAwait(true);
result.DoAny(
onFailure: (failure) =>
{
this.logger.LogError(failure, $"Failed to upload builds");
});
await this.PopulateBuilds();
this.ButtonsEnabled = true;
this.ShowLoading = false;
}
}