Skip to content

✨ Update JSInterop details to use direct code evaluation #232

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

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
@@ -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
@@ -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;
@@ -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);
}
@@ -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
@@ -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);
}

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
@@ -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
@@ -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}",
@@ -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",
@@ -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>(
@@ -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));
}

@@ -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;

@@ -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));
}

@@ -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);
@@ -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