Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ Update JSInterop details to use direct code evaluation #232

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,5 @@
</app>

<script src="_framework/blazor.server.js"></script>
<script src="_content/Microsoft.AspNetCore.ProtectedBrowserStorage/protectedBrowserStorage.js"></script>
</body>
</html>
11 changes: 3 additions & 8 deletions src/ProtectedBrowserStorage/src/ProtectedBrowserStorage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@ namespace Microsoft.AspNetCore.ProtectedBrowserStorage
/// </summary>
public abstract class ProtectedBrowserStorage
{
private const string JsFunctionsPrefix = "protectedBrowserStorage";

private readonly string _storeName;
private readonly IJSRuntime _jsRuntime;
private readonly IDataProtectionProvider _dataProtectionProvider;
Expand Down Expand Up @@ -95,8 +93,7 @@ public ValueTask SetAsync(string purpose, string key, object value)
var protector = GetOrCreateCachedProtector(purpose);
var protectedJson = protector.Protect(json);
return _jsRuntime.InvokeVoidAsync(
$"{JsFunctionsPrefix}.set",
_storeName,
$"{_storeName}.setItem",
key,
protectedJson);
}
Expand All @@ -123,8 +120,7 @@ public ValueTask<T> GetAsync<T>(string key)
public async ValueTask<T> GetAsync<T>(string purpose, string key)
{
var protectedJson = await _jsRuntime.InvokeAsync<string>(
$"{JsFunctionsPrefix}.get",
_storeName,
$"{_storeName}.getItem",
key);

// We should consider having both TryGetAsync and GetValueOrDefaultAsync
Expand All @@ -150,8 +146,7 @@ public async ValueTask<T> GetAsync<T>(string purpose, string key)
public ValueTask DeleteAsync(string key)
{
return _jsRuntime.InvokeVoidAsync(
$"{JsFunctionsPrefix}.delete",
_storeName,
$"{_storeName}.removeItem",
key);
}

Expand Down
10 changes: 0 additions & 10 deletions src/ProtectedBrowserStorage/src/wwwroot/protectedBrowserStorage.js

This file was deleted.

47 changes: 24 additions & 23 deletions src/ProtectedBrowserStorage/test/ProtectedBrowserStorageTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,14 @@ public void SetAsync_ProtectsAndInvokesJS(string customPurpose)
{
// Arrange
var jsRuntime = new TestJSRuntime();
var storeName = "test store";
var dataProtectionProvider = new TestDataProtectionProvider();
var protectedBrowserStorage = new TestProtectedBrowserStorage("test store", jsRuntime, dataProtectionProvider);
var protectedBrowserStorage = new TestProtectedBrowserStorage(storeName, jsRuntime, dataProtectionProvider);
var jsResultTask = new ValueTask<object>((object)null);
var data = new TestModel { StringProperty = "Hello", IntProperty = 123 };
var keyName = "test key";
var expectedPurpose = customPurpose == null
? $"{typeof(TestProtectedBrowserStorage).FullName}:test store:{keyName}"
? $"{typeof(TestProtectedBrowserStorage).FullName}:{storeName}:{keyName}"
: customPurpose;

// Act
Expand All @@ -65,9 +66,8 @@ public void SetAsync_ProtectsAndInvokesJS(string customPurpose)

// Assert
var invocation = jsRuntime.Invocations.Single();
Assert.Equal("protectedBrowserStorage.set", invocation.Identifier);
Assert.Equal($"{storeName}.setItem", invocation.Identifier);
Assert.Collection(invocation.Args,
arg => Assert.Equal("test store", arg),
arg => Assert.Equal(keyName, arg),
arg => Assert.Equal(
"{\"StringProperty\":\"Hello\",\"IntProperty\":123}",
Expand All @@ -79,20 +79,20 @@ public void SetAsync_ProtectsAndInvokesJS_NullValue()
{
// Arrange
var jsRuntime = new TestJSRuntime();
var storeName = "test store";
var dataProtectionProvider = new TestDataProtectionProvider();
var protectedBrowserStorage = new TestProtectedBrowserStorage("test store", jsRuntime, dataProtectionProvider);
var protectedBrowserStorage = new TestProtectedBrowserStorage(storeName, jsRuntime, dataProtectionProvider);
var jsResultTask = new ValueTask<object>((object)null);
var expectedPurpose = $"{typeof(TestProtectedBrowserStorage).FullName}:test store:test key";
var expectedPurpose = $"{typeof(TestProtectedBrowserStorage).FullName}:{storeName}:test key";

// Act
jsRuntime.NextInvocationResult = jsResultTask;
var result = protectedBrowserStorage.SetAsync("test key", null);

// Assert
var invocation = jsRuntime.Invocations.Single();
Assert.Equal("protectedBrowserStorage.set", invocation.Identifier);
Assert.Equal($"{storeName}.setItem", invocation.Identifier);
Assert.Collection(invocation.Args,
arg => Assert.Equal("test store", arg),
arg => Assert.Equal("test key", arg),
arg => Assert.Equal(
"null",
Expand All @@ -107,11 +107,12 @@ public async Task GetAsync_InvokesJSAndUnprotects_ValidData(string customPurpose
// Arrange
var jsRuntime = new TestJSRuntime();
var dataProtectionProvider = new TestDataProtectionProvider();
var protectedBrowserStorage = new TestProtectedBrowserStorage("test store", jsRuntime, dataProtectionProvider);
var storeName = "test store";
var protectedBrowserStorage = new TestProtectedBrowserStorage(storeName, jsRuntime, dataProtectionProvider);
var data = new TestModel { StringProperty = "Hello", IntProperty = 123 };
var keyName = "test key";
var expectedPurpose = customPurpose == null
? $"{typeof(TestProtectedBrowserStorage).FullName}:test store:{keyName}"
? $"{typeof(TestProtectedBrowserStorage).FullName}:{storeName}:{keyName}"
: customPurpose;
var storedJson = "{\"StringProperty\":\"Hello\",\"IntProperty\":123}";
jsRuntime.NextInvocationResult = new ValueTask<string>(
Expand All @@ -127,9 +128,8 @@ public async Task GetAsync_InvokesJSAndUnprotects_ValidData(string customPurpose
Assert.Equal(123, result.IntProperty);

var invocation = jsRuntime.Invocations.Single();
Assert.Equal("protectedBrowserStorage.get", invocation.Identifier);
Assert.Equal($"{storeName}.getItem", invocation.Identifier);
Assert.Collection(invocation.Args,
arg => Assert.Equal("test store", arg),
arg => Assert.Equal(keyName, arg));
}

Expand Down Expand Up @@ -217,8 +217,9 @@ public void DeleteAsync_InvokesJS()
{
// Arrange
var jsRuntime = new TestJSRuntime();
var storeName = "test store";
var dataProtectionProvider = new TestDataProtectionProvider();
var protectedBrowserStorage = new TestProtectedBrowserStorage("test store", jsRuntime, dataProtectionProvider);
var protectedBrowserStorage = new TestProtectedBrowserStorage(storeName, jsRuntime, dataProtectionProvider);
var nextTask = new ValueTask<object>((object)null);
jsRuntime.NextInvocationResult = nextTask;

Expand All @@ -227,9 +228,8 @@ public void DeleteAsync_InvokesJS()

// Assert
var invocation = jsRuntime.Invocations.Single();
Assert.Equal("protectedBrowserStorage.delete", invocation.Identifier);
Assert.Equal($"{storeName}.removeItem", invocation.Identifier);
Assert.Collection(invocation.Args,
arg => Assert.Equal("test store", arg),
arg => Assert.Equal("test key", arg));
}

Expand All @@ -239,8 +239,9 @@ public async Task ReusesCachedProtectorsByPurpose()
// Arrange
var jsRuntime = new TestJSRuntime();
jsRuntime.NextInvocationResult = new ValueTask<object>((object)null);
var storeName = "test store";
var dataProtectionProvider = new TestDataProtectionProvider();
var protectedBrowserStorage = new TestProtectedBrowserStorage("test store", jsRuntime, dataProtectionProvider);
var protectedBrowserStorage = new TestProtectedBrowserStorage(storeName, jsRuntime, dataProtectionProvider);

// Act
await protectedBrowserStorage.SetAsync("key 1", null);
Expand All @@ -252,17 +253,17 @@ public async Task ReusesCachedProtectorsByPurpose()
var typeName = typeof(TestProtectedBrowserStorage).FullName;
var expectedPurposes = new[]
{
$"{typeName}:test store:key 1",
$"{typeName}:test store:key 2",
$"{typeName}:test store:key 3"
$"{typeName}:{storeName}:key 1",
$"{typeName}:{storeName}:key 2",
$"{typeName}:{storeName}:key 3"
};
Assert.Equal(expectedPurposes, dataProtectionProvider.ProtectorsCreated.ToArray());

Assert.Collection(jsRuntime.Invocations,
invocation => Assert.Equal(TestDataProtectionProvider.Protect(expectedPurposes[0], "null"), invocation.Args[2]),
invocation => Assert.Equal(TestDataProtectionProvider.Protect(expectedPurposes[1], "null"), invocation.Args[2]),
invocation => Assert.Equal(TestDataProtectionProvider.Protect(expectedPurposes[0], "null"), invocation.Args[2]),
invocation => Assert.Equal(TestDataProtectionProvider.Protect(expectedPurposes[2], "null"), invocation.Args[2]));
invocation => Assert.Equal(TestDataProtectionProvider.Protect(expectedPurposes[0], "null"), invocation.Args[1]),
invocation => Assert.Equal(TestDataProtectionProvider.Protect(expectedPurposes[1], "null"), invocation.Args[1]),
invocation => Assert.Equal(TestDataProtectionProvider.Protect(expectedPurposes[0], "null"), invocation.Args[1]),
invocation => Assert.Equal(TestDataProtectionProvider.Protect(expectedPurposes[2], "null"), invocation.Args[1]));
}

class TestModel
Expand Down