Skip to content

Commit

Permalink
Ported Json Web Token Encoder Decoder to DevToys 2.0 (#1078)
Browse files Browse the repository at this point in the history
* added jwt decode helper and started working on the decode ui

* added support for claims

* added JsonWebTokenEncodeHelper

* work in progress on encode

* added missing resx

* code cleanup

* added ui fixes and small changes

* updated following comments

---------

Co-authored-by: Etienne Baudoux <[email protected]>
  • Loading branch information
btiteux and veler authored Mar 23, 2024
1 parent 7d38968 commit ea3dc87
Show file tree
Hide file tree
Showing 92 changed files with 6,498 additions and 66 deletions.
6 changes: 4 additions & 2 deletions src/Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
<PackageVersion Include="GirCore.Adw-1" Version="$(GirCore)" />
<PackageVersion Include="GirCore.Gtk-4.0" Version="$(GirCore)" />
<PackageVersion Include="GirCore.WebKit-6.0" Version="$(GirCore)" />
<PackageVersion Include="Markdig" Version="0.33.0" />
<PackageVersion Include="Markdig" Version="0.34.0" />
<PackageVersion Include="Markdown.ColorCode" Version="2.1.0" />
<PackageVersion Include="Microsoft.AspNetCore.Components.Web" Version="$(DotNetVersion)" />
<PackageVersion Include="Microsoft.AspNetCore.Components.WebView" Version="$(DotNetVersion)" />
Expand All @@ -28,6 +28,7 @@
<PackageVersion Include="Microsoft.Extensions.Logging" Version="$(DotNetVersion)" Condition="'$(UseMaui)' != 'true'" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="$(DotNetVersion)" Condition="'$(UseMaui)' != 'true'" />
<PackageVersion Include="Microsoft.Extensions.Logging.Debug" Version="$(DotNetVersion)" Condition="'$(UseMaui)' != 'true'" />
<PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="7.2.0" />
<PackageVersion Include="Microsoft.IO.RecyclableMemoryStream" Version="3.0.0" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.1.0" />
<PackageVersion Include="Microsoft.TypeScript.MSBuild" Version="5.3.2" />
Expand All @@ -38,13 +39,14 @@
<PackageVersion Include="Nuke.Common" Version="8.0.0" />
<PackageVersion Include="OneOf" Version="3.0.263" />
<PackageVersion Include="SixLabors.ImageSharp" Version="3.1.3" />
<PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="7.2.0" />
<PackageVersion Include="System.Text.Json" Version="$(DotNetVersion)" />
<PackageVersion Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
<PackageVersion Include="System.ComponentModel.Composition" Version="$(DotNetVersion)" />
<PackageVersion Include="System.Threading.Tasks.Extensions" Version="4.5.4" />
<PackageVersion Include="xunit" Version="$(XUnit)" />
<PackageVersion Include="xunit.runner.visualstudio" Version="$(XUnit)" />
<PackageVersion Include="YamlDotNet" Version="13.0.0" />
<PackageVersion Include="YamlDotNet" Version="13.7.1" />
<PackageVersion Include="ZXing.Net" Version="0.16.9" />
<PackageVersion Include="ZXing.Net.Bindings.ImageSharp.V3" Version="0.16.15" />
</ItemGroup>
Expand Down
105 changes: 97 additions & 8 deletions src/app/dev/DevToys.Api/Core/ResultInfo.cs
Original file line number Diff line number Diff line change
@@ -1,18 +1,107 @@
namespace DevToys.Api;
using static System.Runtime.InteropServices.JavaScript.JSType;

namespace DevToys.Api;

/// <summary>
/// Record to contain both whether the task was a success and the resulting data
/// </summary>
/// <typeparam name="T">Type of the result</typeparam>
/// <param name="Data">The resulting data or the task</param>
/// <param name="HasSucceeded">Whether the task succeeded</param>
public record ResultInfo<T>(T Data, bool HasSucceeded = true);
public record ResultInfo<T>
{
/// <summary>
/// The resulting data or the task
/// </summary>
public T? Data { get; }

/// <summary>
/// Whether the task succeeded
/// </summary>
public bool HasSucceeded { get; }

/// <summary>
/// Error message to display
/// </summary>
public string? ErrorMessage { get; }

/// <summary>
/// Record to contain both whether the task was a success and the resulting data
/// </summary>
/// <param name="data">The resulting data or the task</param>
/// <param name="hasSucceeded">Whether the task succeeded</param>
public ResultInfo(T data, bool hasSucceeded = true)
{
Data = data;
HasSucceeded = hasSucceeded;
}

/// <summary>
/// Record to contain both whether the task was a success and the resulting data
/// </summary>
/// <param name="data">The resulting data or the task</param>
/// <param name="errorMessage">The error message</param>
/// <param name="hasSucceeded">Whether the task succeeded</param>
public ResultInfo(T data, string errorMessage, bool hasSucceeded = false)
{
Data = data;
HasSucceeded = hasSucceeded;
ErrorMessage = errorMessage;
}
}

/// <summary>
/// Record to contain both whether the task was a success and the resulting data
/// </summary>
/// <typeparam name="T">Type of the result</typeparam>
/// <typeparam name="U">Type of the severity</typeparam>
/// <param name="Data">The resulting data or the task</param>
/// <param name="Severity">The severity of the result</param>
public record ResultInfo<T, U>(T Data, U Severity);
/// <typeparam name="ResultInfoSeverity">The severity of the result</typeparam>
public record ResultInfo<T, ResultInfoSeverity>
{
/// <summary>
/// The resulting data or the task
/// </summary>
public T? Data { get; }

/// <summary>
/// Severity of the result
/// </summary>
public ResultInfoSeverity Severity { get; }

/// <summary>
/// Error message to display
/// </summary>
public string? ErrorMessage { get; }

/// <summary>
/// Record to contain both whether the task was a success and the resulting data
/// </summary>
/// <param name="data">The resulting data or the task</param>
/// <param name="severity">The severity of the result</param>
public ResultInfo(T data, ResultInfoSeverity severity)
{
Data = data;
Severity = severity;
}

/// <summary>
/// Record to contain both whether the task was a success and the resulting data
/// </summary>
/// <param name="errorMessage">The error message</param>
/// <param name="severity">The severity of the result</param>
public ResultInfo(string errorMessage, ResultInfoSeverity severity)
{
Severity = severity;
ErrorMessage = errorMessage;
}

/// <summary>
/// Record to contain both whether the task was a success and the resulting data
/// </summary>
/// <param name="data">The resulting data or the task</param>
/// <param name="errorMessage">The error message</param>
/// <param name="severity">The severity of the result</param>
public ResultInfo(T data, string errorMessage, ResultInfoSeverity severity)
{
Data = data;
Severity = severity;
ErrorMessage = errorMessage;
}
}
8 changes: 8 additions & 0 deletions src/app/dev/DevToys.Api/Core/ResultInfoSeverity.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace DevToys.Api;

public enum ResultInfoSeverity
{
Success,
Warning,
Error
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ public interface IUIMultiLineTextInput : IUISingleLineTextInput
/// </summary>
IReadOnlyList<UIHighlightedTextSpan> HighlightedSpans { get; }

/// <summary>
/// Gets the list of tooltip to display on word hover.
/// </summary>
IReadOnlyList<UIHoverTooltip> HoverTooltips { get; }

/// <summary>
/// Gets the programming language name to use when colorizing the text in the control.
/// </summary>
Expand All @@ -33,6 +38,11 @@ public interface IUIMultiLineTextInput : IUISingleLineTextInput
/// </summary>
TextSpan Selection { get; }

/// <summary>
/// Raised when <see cref="HoverTooltips"/> is changed.
/// </summary>
event EventHandler? HoverTooltipChanged;

/// <summary>
/// Raised when <see cref="HighlightedSpans"/> is changed.
/// </summary>
Expand Down Expand Up @@ -62,6 +72,7 @@ public interface IUIMultiLineTextInput : IUISingleLineTextInput
[DebuggerDisplay($"Id = {{{nameof(Id)}}}, Text = {{{nameof(Text)}}}, SyntaxColorizationLanguageName = {{{nameof(SyntaxColorizationLanguageName)}}}")]
internal class UIMultilineTextInput : UISingleLineTextInput, IUIMultiLineTextInput
{
private IReadOnlyList<UIHoverTooltip>? _hoverTooltip;
private IReadOnlyList<UIHighlightedTextSpan>? _highlightedSpans;
private string? _syntaxColorizationLanguageName;
private bool _isExtendableToFullScreen;
Expand All @@ -79,6 +90,12 @@ public IReadOnlyList<UIHighlightedTextSpan> HighlightedSpans
internal set => SetPropertyValue(ref _highlightedSpans, value, HighlightedSpansChanged);
}

public IReadOnlyList<UIHoverTooltip> HoverTooltips
{
get => _hoverTooltip ?? Array.Empty<UIHoverTooltip>();
internal set => SetPropertyValue(ref _hoverTooltip, value, HoverTooltipChanged);
}

public string SyntaxColorizationLanguageName
{
get => _syntaxColorizationLanguageName ?? string.Empty;
Expand Down Expand Up @@ -122,6 +139,7 @@ internal set
}
}

public event EventHandler? HoverTooltipChanged;
public event EventHandler? HighlightedSpansChanged;
public event EventHandler? SyntaxColorizationLanguageNameChanged;
public event EventHandler? IsExtendableToFullScreenChanged;
Expand Down Expand Up @@ -176,6 +194,15 @@ public static IUIMultiLineTextInput Highlight(this IUIMultiLineTextInput element
return element;
}

/// <summary>
/// Sets the list of tooltips to display on Word hover in the text document.
/// </summary>
public static IUIMultiLineTextInput HoverTooltip(this IUIMultiLineTextInput element, params UIHoverTooltip[] tooltips)
{
((UIMultilineTextInput)element).HoverTooltips = tooltips;
return element;
}

/// <summary>
/// Sets the programming language name to use to colorize the text in the control.
/// </summary>
Expand Down
26 changes: 26 additions & 0 deletions src/app/dev/DevToys.Api/Tool/GUI/Components/UIHoverTooltip.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
namespace DevToys.Api;

/// <summary>
/// Represents the Tooltip to display on hover
/// </summary>
public record UIHoverTooltip
{
/// <summary>
/// Contain the position of the span to search
/// </summary>
public TextSpan Span { get; }

/// <summary>
/// Contain the information we want to display on hover
/// </summary>
public string Description { get; }

/// <summary>
/// Create a new isntance of the <see cref="UIHoverTooltip"/> class.
/// </summary>
public UIHoverTooltip(TextSpan span, string description)
{
Span = span;
Description = description;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@
background-color: var(--card-background-color-secondary);
@include transition(transform 250ms cubic-bezier(1, 1, 0, 1));
transform: translateY(-100%);

.expander-card {
background-color: transparent;
}
}

&[aria-expanded='true'] {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Text.Json;
using System.Reflection;
using System.Text.Json;
using System.Text.Json.Serialization;
using DevToys.Blazor.Components.Monaco;
using DevToys.Blazor.Components.Monaco.Editor;
Expand Down Expand Up @@ -519,6 +520,13 @@ internal async Task<ComputedEditorOptions> GetOptionsAsync()
internal ValueTask<EditorOptions> GetRawOptionsAsync()
=> JSRuntime.InvokeAsync<EditorOptions>("devtoys.MonacoEditor.getRawOptions", Id);

/// <summary>
/// Get value of the current model attached to this editor.
/// See <see cref="TextModel.GetWordAtPosition(Position)"/>
/// </summary>
internal ValueTask<WordAtPosition> GetWordAtPositionAsync(string position)
=> JSRuntime.InvokeAsync<WordAtPosition>("devtoys.MonacoEditor.getWordAtPosition", position);

/// <summary>
/// Get value of the current model attached to this editor.
/// See <see cref="TextModel.GetValue(EndOfLinePreference?, bool?)"/>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,26 @@
namespace DevToys.Blazor.Components.UIElements;
using DevToys.Api;

namespace DevToys.Blazor.Components.UIElements;

public partial class UIGridPresenter : ComponentBase
{
[Parameter]
public IUIGrid UIGrid { get; set; } = default!;

protected override void OnInitialized()
{
base.OnInitialized();

UIGrid.CellsChanged += UIGrid_CellsChanged;
}

public void Dispose()
{
UIGrid.CellsChanged += UIGrid_CellsChanged;
}

private void UIGrid_CellsChanged(object? sender, EventArgs e)
{
StateHasChanged();
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using DevToys.Blazor.Components.Monaco;
using DevToys.Blazor.Components.Monaco.Editor;
using Microsoft.VisualBasic;
using Range = DevToys.Blazor.Components.Monaco.Range;

namespace DevToys.Blazor.Components.UIElements;

Expand All @@ -26,6 +28,7 @@ protected override void OnInitialized()
UIMultiLineTextInput.IsReadOnlyChanged += UIMultiLineTextInput_IsReadOnlyChanged;
UIMultiLineTextInput.SelectionChanged += UIMultiLineTextInput_SelectionChanged;
UIMultiLineTextInput.HighlightedSpansChanged += UIMultiLineTextInput_HighlightedSpansChanged;
UIMultiLineTextInput.HoverTooltipChanged += UIMultiLineTextInput_HoverTooltipChanged;
}

public override ValueTask DisposeAsync()
Expand All @@ -35,6 +38,7 @@ public override ValueTask DisposeAsync()
UIMultiLineTextInput.IsReadOnlyChanged -= UIMultiLineTextInput_IsReadOnlyChanged;
UIMultiLineTextInput.SelectionChanged -= UIMultiLineTextInput_SelectionChanged;
UIMultiLineTextInput.HighlightedSpansChanged -= UIMultiLineTextInput_HighlightedSpansChanged;
UIMultiLineTextInput.HoverTooltipChanged -= UIMultiLineTextInput_HoverTooltipChanged;

return base.DisposeAsync();
}
Expand Down Expand Up @@ -133,6 +137,52 @@ private async void UIMultiLineTextInput_HighlightedSpansChanged(object? sender,
}
}

private async void UIMultiLineTextInput_HoverTooltipChanged(object? sender, EventArgs e)
{
await _monacoInitializationAwaiter.Task;

using (await _semaphore.WaitAsync(CancellationToken.None))
{
TextModel model = await _monacoEditor.GetModelAsync();

if (model is not null)
{
var decorations = new ModelDeltaDecoration[UIMultiLineTextInput.HoverTooltips.Count];

for (int i = 0; i < decorations.Length; i++)
{
UIHoverTooltip hoverTooltip = UIMultiLineTextInput.HoverTooltips[i];
Position selectionStartPosition = await model.GetPositionAtAsync(JSRuntime, hoverTooltip.Span.StartPosition);
Position selectionEndPosition = await model.GetPositionAtAsync(JSRuntime, hoverTooltip.Span.EndPosition);

decorations[i]
= new ModelDeltaDecoration
{
Range = new Monaco.Range
{
StartLineNumber = selectionStartPosition.LineNumber,
StartColumn = selectionStartPosition.Column,
EndLineNumber = selectionEndPosition.LineNumber,
EndColumn = selectionEndPosition.Column
},
Options = new ModelDecorationOptions
{
HoverMessage = new[]
{
new MarkdownString()
{
Value = hoverTooltip.Description
}
}
}
};
}

await _monacoEditor.ReplaceAllDecorationsByAsync(decorations);
}
}
}

private async Task OnMonacoEditorTextChangedAsync(ModelContentChangedEvent ev)
{
if (ev.Changes is not null)
Expand Down
Loading

0 comments on commit ea3dc87

Please sign in to comment.