Skip to content

Commit 7da8ce5

Browse files
authored
Merge pull request #5 from RestApia/feature/playground-added
Feature / Simple playground project added
2 parents d6065f2 + c8c62d4 commit 7da8ce5

25 files changed

+597
-17
lines changed

RestApia.Shared.sln

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RestApia.Extensions.Auth.Ba
88
EndProject
99
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RestApia.Extensions.Auth.OAuth2", "src\Extensions\RestApia.Extensions.Auth.OAuth2\RestApia.Extensions.Auth.OAuth2.csproj", "{AE97EEF8-CE4E-0976-B0FB-8C4FC7616E01}"
1010
EndProject
11-
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Builder", "Builder", "{07A0437C-C5B5-4FC9-AA80-71C96A11FF54}"
12-
EndProject
13-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Builder.Shared", ".build\Builder.Shared.csproj", "{35A67EB2-CF2D-40B2-B598-685D70F9D94D}"
14-
EndProject
1511
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RestApia.Extensions.Import.Postman", "src\Extensions\RestApia.Extensions.Import.Postman\RestApia.Extensions.Import.Postman.csproj", "{00CAA372-8954-4A50-94C5-6574EBACC467}"
1612
EndProject
1713
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Playground", "Playground", "{B9D6F201-FDF7-42BB-A688-C8CBC51B4718}"
@@ -22,6 +18,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RestApia.Extensions.ValuesP
2218
EndProject
2319
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RestApia.Extensions.ValuesProvider.CollectionValuesProvider", "src\Extensions\RestApia.Extensions.ValuesProvider.CollectionValuesProvider\RestApia.Extensions.ValuesProvider.CollectionValuesProvider.csproj", "{47CBF40F-4929-4FA4-A119-054E04AFF40C}"
2420
EndProject
21+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RestApia.Experiments.Desktop", "src\Playground\RestApia.Experiments.Desktop\RestApia.Experiments.Desktop.csproj", "{D34CE22C-21F9-4464-BE88-93D6E0DC41ED}"
22+
EndProject
2523
Global
2624
GlobalSection(SolutionConfigurationPlatforms) = preSolution
2725
Debug|Any CPU = Debug|Any CPU
@@ -40,10 +38,6 @@ Global
4038
{AE97EEF8-CE4E-0976-B0FB-8C4FC7616E01}.Debug|Any CPU.Build.0 = Debug|Any CPU
4139
{AE97EEF8-CE4E-0976-B0FB-8C4FC7616E01}.Release|Any CPU.ActiveCfg = Release|Any CPU
4240
{AE97EEF8-CE4E-0976-B0FB-8C4FC7616E01}.Release|Any CPU.Build.0 = Release|Any CPU
43-
{35A67EB2-CF2D-40B2-B598-685D70F9D94D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
44-
{35A67EB2-CF2D-40B2-B598-685D70F9D94D}.Debug|Any CPU.Build.0 = Debug|Any CPU
45-
{35A67EB2-CF2D-40B2-B598-685D70F9D94D}.Release|Any CPU.ActiveCfg = Release|Any CPU
46-
{35A67EB2-CF2D-40B2-B598-685D70F9D94D}.Release|Any CPU.Build.0 = Release|Any CPU
4741
{00CAA372-8954-4A50-94C5-6574EBACC467}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
4842
{00CAA372-8954-4A50-94C5-6574EBACC467}.Debug|Any CPU.Build.0 = Debug|Any CPU
4943
{00CAA372-8954-4A50-94C5-6574EBACC467}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -60,14 +54,18 @@ Global
6054
{47CBF40F-4929-4FA4-A119-054E04AFF40C}.Debug|Any CPU.Build.0 = Debug|Any CPU
6155
{47CBF40F-4929-4FA4-A119-054E04AFF40C}.Release|Any CPU.ActiveCfg = Release|Any CPU
6256
{47CBF40F-4929-4FA4-A119-054E04AFF40C}.Release|Any CPU.Build.0 = Release|Any CPU
57+
{D34CE22C-21F9-4464-BE88-93D6E0DC41ED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
58+
{D34CE22C-21F9-4464-BE88-93D6E0DC41ED}.Debug|Any CPU.Build.0 = Debug|Any CPU
59+
{D34CE22C-21F9-4464-BE88-93D6E0DC41ED}.Release|Any CPU.ActiveCfg = Release|Any CPU
60+
{D34CE22C-21F9-4464-BE88-93D6E0DC41ED}.Release|Any CPU.Build.0 = Release|Any CPU
6361
EndGlobalSection
6462
GlobalSection(NestedProjects) = preSolution
6563
{9B2C67A1-83B9-157D-5EAF-ADD07B0E8D3F} = {E2CF6B65-E284-39DA-C77D-4FDAB55B16AD}
6664
{AE97EEF8-CE4E-0976-B0FB-8C4FC7616E01} = {E2CF6B65-E284-39DA-C77D-4FDAB55B16AD}
67-
{35A67EB2-CF2D-40B2-B598-685D70F9D94D} = {07A0437C-C5B5-4FC9-AA80-71C96A11FF54}
6865
{00CAA372-8954-4A50-94C5-6574EBACC467} = {E2CF6B65-E284-39DA-C77D-4FDAB55B16AD}
6966
{200B00D8-9D09-464F-B4A8-B7DEC68EE9E8} = {B9D6F201-FDF7-42BB-A688-C8CBC51B4718}
7067
{63845E5A-2440-4322-B06B-4AB9EA415DAE} = {E2CF6B65-E284-39DA-C77D-4FDAB55B16AD}
7168
{47CBF40F-4929-4FA4-A119-054E04AFF40C} = {E2CF6B65-E284-39DA-C77D-4FDAB55B16AD}
69+
{D34CE22C-21F9-4464-BE88-93D6E0DC41ED} = {B9D6F201-FDF7-42BB-A688-C8CBC51B4718}
7270
EndGlobalSection
7371
EndGlobal

src/Extensions/RestApia.Extensions.ValuesProvider.AzureKeyVault/KeyVaultValuesProvider.cs

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -60,22 +60,30 @@ public async Task<ReloadValuesResults> ReloadValuesAsync(IReadOnlyCollection<Val
6060
if (!VariablesConverter.TryDeserialize<KeyVaultSettings>(inputValues, out var settings))
6161
return ReloadValuesResults.Failed;
6262

63+
var values = await GetRemoteValuesAsync(settings);
64+
if (values == null) return ReloadValuesResults.Failed;
65+
6366
return new ReloadValuesResults
64-
{ Values = await GetRemoteValuesAsync(settings), Status = ValueReloadResultType.Success };
67+
{
68+
Values = values,
69+
Status = ValueReloadResultType.Success,
70+
};
6571
}
6672

67-
private async Task<IReadOnlyCollection<ValueModel>> GetRemoteValuesAsync(KeyVaultSettings settings)
73+
private async Task<IReadOnlyCollection<ValueModel>?> GetRemoteValuesAsync(KeyVaultSettings settings)
6874
{
6975
if (!Uri.TryCreate(settings.KeyVaultUrl, UriKind.Absolute, out var keyVaultUrl))
7076
{
7177
_dialogs.ShowError("Cannot read KeyVault secret values. KeyVault URL is not valid.");
72-
return [];
78+
return null;
7379
}
7480

7581
var credentials = BuildCredentials(settings);
7682
var client = BuildClient(keyVaultUrl, credentials);
7783
var result = await ReadValuesAsync(client, settings);
7884

85+
if (result == null) return null;
86+
7987
return
8088
[
8189
new ()
@@ -88,14 +96,13 @@ private async Task<IReadOnlyCollection<ValueModel>> GetRemoteValuesAsync(KeyVaul
8896
];
8997
}
9098

91-
private async Task<IReadOnlyCollection<ValueModel>> ReadValuesAsync(SecretClient client, KeyVaultSettings settings)
99+
private async Task<IReadOnlyCollection<ValueModel>?> ReadValuesAsync(SecretClient client, KeyVaultSettings settings)
92100
{
93-
var result = new List<ValueModel>();
94-
95101
try
96102
{
97103
// get list of secrets
98104
var secretProperties = client.GetPropertiesOfSecrets();
105+
var result = new List<ValueModel>();
99106

100107
foreach (var secretProperty in secretProperties)
101108
{
@@ -107,14 +114,16 @@ private async Task<IReadOnlyCollection<ValueModel>> ReadValuesAsync(SecretClient
107114
Type = ValueTypeEnum.Variable,
108115
});
109116
}
117+
118+
return result;
110119
}
111120
catch (Exception ex)
112121
{
113122
_logger.Fail(ex, $"Cannot read KeyVault secret values from remote. {ex.Message}");
114123
_dialogs.ShowError("Cannot read KeyVault secret values from remote.");
115-
}
116124

117-
return result;
125+
return null;
126+
}
118127
}
119128

120129
private static SecretClient BuildClient(Uri url, TokenCredential credentials)
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<Application xmlns="https://github.com/avaloniaui"
2+
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
3+
x:Class="RestApia.Experiments.Desktop.App"
4+
xmlns:local="using:RestApia.Experiments.Desktop"
5+
RequestedThemeVariant="Dark">
6+
<!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. -->
7+
8+
<Application.DataTemplates>
9+
<local:ViewLocator/>
10+
</Application.DataTemplates>
11+
12+
<Application.Styles>
13+
<FluentTheme />
14+
<StyleInclude Source="avares://Avalonia.Controls.DataGrid/Themes/Fluent.xaml"/>
15+
</Application.Styles>
16+
</Application>
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
using Avalonia;
2+
using Avalonia.Controls;
3+
using Avalonia.Controls.ApplicationLifetimes;
4+
using Avalonia.Markup.Xaml;
5+
using AvaloniaWebView;
6+
using RestApia.Experiments.Desktop.Views;
7+
8+
namespace RestApia.Experiments.Desktop;
9+
10+
public class App : Application
11+
{
12+
public override void Initialize()
13+
{
14+
AvaloniaXamlLoader.Load(this);
15+
}
16+
17+
public override void OnFrameworkInitializationCompleted()
18+
{
19+
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
20+
{
21+
desktop.MainWindow = new MainWindow();
22+
AppWindow = desktop.MainWindow;
23+
AvaloniaWebViewBuilder.Initialize(config =>
24+
{
25+
// some settings could be here
26+
});
27+
}
28+
29+
base.OnFrameworkInitializationCompleted();
30+
}
31+
32+
public static Window AppWindow { get; private set; } = null!;
33+
}
Binary file not shown.
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
using Avalonia.Controls;
2+
using CustomMessageBox.Avalonia;
3+
using RestApia.Extensions.Auth.Basic;
4+
using RestApia.Shared.Common.Enums;
5+
using RestApia.Shared.Extensions.ValuesProviderService.Enums;
6+
namespace RestApia.Experiments.Desktop.Experiments.Auth;
7+
8+
public class BasicAuthExperiments
9+
{
10+
public static async Task RunBasicAsync()
11+
{
12+
var service = new BasicAuthService();
13+
var result = await service.ReloadValuesAsync([
14+
new () { Name = nameof(BasicAuthSettings.Name), Value = "User", Type = ValueTypeEnum.Variable },
15+
new () { Name = nameof(BasicAuthSettings.Password), Value = "Pa$$word", Type = ValueTypeEnum.Variable },
16+
], ValuesReloadMode.Interactive);
17+
18+
if (!string.IsNullOrWhiteSpace(result.ErrorMessage))
19+
{
20+
await MessageBox.Show(result.ErrorMessage, "Authorization Failed", MessageBoxButtons.OK, MessageBoxIcon.Error);
21+
return;
22+
}
23+
24+
if (result.Status != ValueReloadResultType.Success)
25+
{
26+
await MessageBox.Show($"Authorization status: {result.Status}", "Authorization", MessageBoxButtons.OK, MessageBoxIcon.Warning);
27+
return;
28+
}
29+
30+
var message = result.Values.Select(x => $"{x.Name}: {x.Value}");
31+
message = [$"Expired: {result.ExpiredAt}", ..message];
32+
33+
await MessageBox.Show(new SelectableTextBlock
34+
{
35+
Text = string.Join("\n", message),
36+
},
37+
"Authorization",
38+
MessageBoxButtons.OK,
39+
MessageBoxIcon.Information);
40+
}
41+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
using CustomMessageBox.Avalonia;
2+
using RestApia.Experiments.Desktop.Modules.Dialogs;
3+
using RestApia.Experiments.Desktop.Modules.Logger;
4+
using RestApia.Extensions.Auth.OAuth2.AuthCode;
5+
using RestApia.Extensions.Auth.OAuth2.Implicit;
6+
using RestApia.Shared.Common.Enums;
7+
using RestApia.Shared.Common.Models;
8+
using RestApia.Shared.Common.Services;
9+
using RestApia.Shared.Extensions.ValuesProviderService.Enums;
10+
namespace RestApia.Experiments.Desktop.Experiments.Auth;
11+
12+
public static class OAuth2Experiments
13+
{
14+
public static async Task RunAuthCodeAsync()
15+
{
16+
var service = new OAuth2AuthCodeService(ConsoleExtensionLogger.Instance, new ExtensionDialogs());
17+
var settings = LocalSettings.Get<OAuth2AuthCodeSettings>();
18+
var result = await service.ReloadValuesAsync(VariablesConverter.Serialize(settings), ValuesReloadMode.Interactive);
19+
20+
if (!string.IsNullOrWhiteSpace(result.ErrorMessage))
21+
{
22+
await MessageBox.Show(result.ErrorMessage, "Authorization Failed", MessageBoxButtons.OK, MessageBoxIcon.Error);
23+
return;
24+
}
25+
26+
if (result.Status != ValueReloadResultType.Success)
27+
{
28+
await MessageBox.Show($"Authorization status: {result.Status}", "Authorization", MessageBoxButtons.OK, MessageBoxIcon.Warning);
29+
return;
30+
}
31+
32+
await MessageGrid.ShowAsync(result
33+
.Values
34+
.Prepend(new ValueModel { Name = "Expired At", Value = result.ExpiredAt?.ToString() ?? "Never expired", Type = ValueTypeEnum.Other })
35+
.Select(x => new { x.Name, Value = x.Value.ToString() }));
36+
}
37+
38+
public static async Task RunImplicitAsync()
39+
{
40+
var service = new OAuth2ImplicitService(ConsoleExtensionLogger.Instance, new ExtensionDialogs());
41+
var settings = LocalSettings.Get<OAuth2ImplicitSettings>();
42+
var result = await service.ReloadValuesAsync(VariablesConverter.Serialize(settings), ValuesReloadMode.Interactive);
43+
44+
if (!string.IsNullOrWhiteSpace(result.ErrorMessage))
45+
{
46+
await MessageBox.Show(result.ErrorMessage, "Authorization Failed", MessageBoxButtons.OK, MessageBoxIcon.Error);
47+
return;
48+
}
49+
50+
if (result.Status != ValueReloadResultType.Success)
51+
{
52+
await MessageBox.Show($"Authorization status: {result.Status}", "Authorization", MessageBoxButtons.OK, MessageBoxIcon.Warning);
53+
return;
54+
}
55+
56+
await MessageGrid.ShowAsync(result
57+
.Values
58+
.Prepend(new ValueModel { Name = "Expired At", Value = result.ExpiredAt?.ToString() ?? "Never expired", Type = ValueTypeEnum.Other })
59+
.Select(x => new { x.Name, Value = x.Value.ToString() }));
60+
}
61+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
using CustomMessageBox.Avalonia;
2+
using RestApia.Experiments.Desktop.Modules.Dialogs;
3+
using RestApia.Experiments.Desktop.Modules.Logger;
4+
using RestApia.Extensions.ValuesProvider.AzureKeyVault;
5+
using RestApia.Shared.Common.Services;
6+
using RestApia.Shared.Extensions.ValuesProviderService.Enums;
7+
namespace RestApia.Experiments.Desktop.Experiments.Secrets;
8+
9+
public static class AzureKeyVaultExperiments
10+
{
11+
public static async Task RunAsync()
12+
{
13+
var settings = LocalSettings.Get<KeyVaultSettings>();
14+
var service = new KeyVaultValuesProvider(ConsoleExtensionLogger.Instance, new ExtensionDialogs());
15+
var result = await service.ReloadValuesAsync(VariablesConverter.Serialize(settings), ValuesReloadMode.Interactive);
16+
17+
if (!string.IsNullOrWhiteSpace(result.ErrorMessage))
18+
{
19+
await MessageBox.Show(result.ErrorMessage, "Secrets reading Failed", MessageBoxButtons.OK, MessageBoxIcon.Error);
20+
return;
21+
}
22+
23+
if (result.Status != ValueReloadResultType.Success)
24+
{
25+
await MessageBox.Show($"Secrets status: {result.Status}", "Authorization", MessageBoxButtons.OK, MessageBoxIcon.Warning);
26+
return;
27+
}
28+
29+
await MessageGrid.ShowAsync(result
30+
.Values
31+
.Select(x => new { x.Name, Value = x.Value.ToString() }));
32+
}
33+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<Window xmlns="https://github.com/avaloniaui"
2+
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
3+
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
4+
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
5+
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
6+
x:Class="RestApia.Experiments.Desktop.Modules.Auth.AuthWindow"
7+
Title="AuthWindow">
8+
<Panel>
9+
<WebView x:Name="WebViewControl" Focusable="True" />
10+
</Panel>
11+
</Window>
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
using System.Diagnostics.CodeAnalysis;
2+
using Avalonia.Controls;
3+
using Avalonia.Interactivity;
4+
using Avalonia.Threading;
5+
using Microsoft.Web.WebView2.Core;
6+
using RestApia.Shared.Common.Models;
7+
using WebViewCore.Events;
8+
9+
namespace RestApia.Experiments.Desktop.Modules.Auth;
10+
11+
public partial class AuthWindow : Window
12+
{
13+
private readonly string _startUrl;
14+
private readonly string _stopUrl;
15+
16+
[SuppressMessage("ReSharper", "UnusedMember.Global")]
17+
public AuthWindow()
18+
{
19+
if (!Design.IsDesignMode)
20+
throw new Exception("This constructor is only for design mode");
21+
22+
_stopUrl = string.Empty;
23+
_startUrl = string.Empty;
24+
25+
InitializeComponent();
26+
}
27+
28+
public AuthWindow(string startUrl, string stopUrl)
29+
{
30+
_startUrl = startUrl;
31+
_stopUrl = stopUrl;
32+
33+
InitializeComponent();
34+
}
35+
36+
protected override void OnLoaded(RoutedEventArgs e)
37+
{
38+
base.OnLoaded(e);
39+
40+
if (Design.IsDesignMode) return;
41+
WebViewControl.NavigationStarting += NavigateStarting;
42+
WebViewControl.Url = new Uri(_startUrl);
43+
}
44+
45+
protected override void OnUnloaded(RoutedEventArgs e)
46+
{
47+
WebViewControl.NavigationStarting -= NavigateStarting;
48+
base.OnUnloaded(e);
49+
}
50+
51+
private void NavigateStarting(object? sender, WebViewUrlLoadingEventArg e)
52+
{
53+
var url = e.Url?.ToString();
54+
if (string.IsNullOrWhiteSpace(url))
55+
throw new ("Browser navigation url is empty");
56+
57+
if (!url.StartsWith(_stopUrl, StringComparison.OrdinalIgnoreCase)) return;
58+
59+
// stop URL detected
60+
e.Cancel = true;
61+
62+
var headers = new Dictionary<string, string>();
63+
if (e.RawArgs is CoreWebView2NavigationStartingEventArgs args)
64+
headers = args.RequestHeaders.ToDictionary(header => header.Key, header => header.Value);
65+
66+
Dispatcher.UIThread.Post(() => Close(new BrowserDialogResult
67+
{
68+
Url = url,
69+
Headers = headers,
70+
}));
71+
}
72+
}

0 commit comments

Comments
 (0)