Skip to content

Commit 3307883

Browse files
committed
Completed ResourceApi Infrastructure & Sample
1 parent ff4e03c commit 3307883

23 files changed

Lines changed: 496 additions & 129 deletions

File tree

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
@using CodeBeam.UltimateAuth.Sample.BlazorStandaloneWasm.ResourceApi
22
@inject ProductApiService Api
3+
@inject ISnackbar Snackbar
34

45
<MudDialog Class="mud-width-full" ContentClass="uauth-dialog">
56

@@ -9,8 +10,45 @@
910
</TitleContent>
1011

1112
<DialogContent>
12-
Products: @(string.Join(", ", _products.Select(x => x.Name)))
13-
<MudButton OnClick="GetProducts">Get Products</MudButton>
13+
<MudButton OnClick="GetProducts">Reload</MudButton>
14+
15+
<MudDataGrid Items="@_products" Dense="true" Striped="true" Hover="true"
16+
EditMode="DataGridEditMode.Form" ReadOnly="false" CommittedItemChanges="@CommittedItemChanges">
17+
<Columns>
18+
<PropertyColumn Property="x => x.Id" Title="Id" Editable="false" />
19+
<PropertyColumn Property="x => x.Name" Title="Name" />
20+
<TemplateColumn Title="Actions" Editable="false">
21+
<CellTemplate>
22+
<MudStack Style="min-width: 120px" Row="true" Spacing="2" Wrap="Wrap.Wrap">
23+
<MudTooltip Text="Edit" Color="Color.Primary" Delay="300" ShowOnFocus="false">
24+
<MudIconButton Color="Color.Primary" Variant="Variant.Filled" Icon="@Icons.Material.Filled.Edit" Size="Size.Small" OnClick="@context.Actions.StartEditingItemAsync" />
25+
</MudTooltip>
26+
27+
<MudTooltip Text="Delete" Color="Color.Error" Delay="300" ShowOnFocus="false">
28+
<MudIconButton Color="Color.Error" Variant="Variant.Filled" Icon="@Icons.Material.Filled.Delete" Size="Size.Small" OnClick="@(() => DeleteProduct(context.Item.Id))" />
29+
</MudTooltip>
30+
</MudStack>
31+
</CellTemplate>
32+
</TemplateColumn>
33+
</Columns>
34+
<PagerContent>
35+
<MudDataGridPager T="SampleProduct" PageSizeOptions="new int[] { 5, 10, 20 }" />
36+
</PagerContent>
37+
</MudDataGrid>
38+
39+
<MudExpansionPanels Class="mt-4" Elevation="0">
40+
<MudExpansionPanel Style="background: var(--mud-palette-background-gray)" Text="Add New Product" Expanded="false">
41+
<MudGrid>
42+
<MudItem xs="12" sm="6">
43+
<MudTextField @bind-Value="_newName" Variant="Variant.Outlined" Label="Name" />
44+
</MudItem>
45+
46+
<MudItem xs="12">
47+
<MudButton Style="width: fit-content" Color="Color.Primary" Variant="Variant.Outlined" OnClick="CreateProduct">Add</MudButton>
48+
</MudItem>
49+
</MudGrid>
50+
</MudExpansionPanel>
51+
</MudExpansionPanels>
1452
</DialogContent>
1553

1654
</MudDialog>

‎samples/blazor-standalone-wasm/CodeBeam.UltimateAuth.Sample.BlazorStandaloneWasm/Components/Dialogs/ResourceApiDialog.razor.cs‎

Lines changed: 75 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
using CodeBeam.UltimateAuth.Client;
2+
using CodeBeam.UltimateAuth.Core.Contracts;
3+
using CodeBeam.UltimateAuth.Core.Domain;
24
using CodeBeam.UltimateAuth.Sample.BlazorStandaloneWasm.ResourceApi;
5+
using CodeBeam.UltimateAuth.Users.Contracts;
36
using Microsoft.AspNetCore.Components;
47
using MudBlazor;
58

@@ -13,10 +16,80 @@ public partial class ResourceApiDialog
1316
[Parameter]
1417
public UAuthState AuthState { get; set; } = default!;
1518

16-
List<Product> _products = new List<Product>();
19+
private List<SampleProduct> _products = new List<SampleProduct>();
20+
private string? _newName = null;
21+
22+
protected override async Task OnAfterRenderAsync(bool firstRender)
23+
{
24+
if (firstRender)
25+
{
26+
_products = (await Api.GetAllAsync()).Value ?? new();
27+
StateHasChanged();
28+
}
29+
}
30+
31+
private async Task<DataGridEditFormAction> CommittedItemChanges(SampleProduct item)
32+
{
33+
var result = await Api.UpdateAsync(item.Id, item);
34+
35+
if (result.IsSuccess)
36+
{
37+
Snackbar.Add("Product updated successfully", Severity.Success);
38+
}
39+
else
40+
{
41+
Snackbar.Add(result?.ErrorText ?? "Failed to update product.", Severity.Error);
42+
}
43+
44+
return DataGridEditFormAction.Close;
45+
}
1746

1847
private async Task GetProducts()
1948
{
20-
_products = await Api.GetAllAsync();
49+
var result = await Api.GetAllAsync();
50+
51+
if (result.IsSuccess)
52+
{
53+
_products = result.Value ?? new();
54+
}
55+
else
56+
{
57+
Snackbar.Add(result.ErrorText ?? "Process failed.", Severity.Error);
58+
}
59+
}
60+
61+
private async Task CreateProduct()
62+
{
63+
var product = new SampleProduct
64+
{
65+
Name = _newName
66+
};
67+
68+
var result = await Api.CreateAsync(product);
69+
70+
if (result.IsSuccess)
71+
{
72+
Snackbar.Add("New product created.");
73+
_products = (await Api.GetAllAsync()).Value ?? new();
74+
}
75+
else
76+
{
77+
Snackbar.Add(result.ErrorText ?? "Process failed.", Severity.Error);
78+
}
79+
}
80+
81+
private async Task DeleteProduct(int id)
82+
{
83+
var result = await Api.DeleteAsync(id);
84+
85+
if (result.IsSuccess)
86+
{
87+
Snackbar.Add("Product deleted succesfully.", Severity.Success);
88+
_products = (await Api.GetAllAsync()).Value ?? new();
89+
}
90+
else
91+
{
92+
Snackbar.Add(result.ErrorText ?? "Process failed.", Severity.Error);
93+
}
2194
}
2295
}
Lines changed: 37 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using Microsoft.AspNetCore.Components.WebAssembly.Http;
1+
using CodeBeam.UltimateAuth.Core.Contracts;
2+
using Microsoft.AspNetCore.Components.WebAssembly.Http;
23
using System.Net.Http.Json;
34

45
namespace CodeBeam.UltimateAuth.Sample.BlazorStandaloneWasm.ResourceApi;
@@ -25,56 +26,53 @@ private HttpRequestMessage CreateRequest(HttpMethod method, string url, object?
2526
return request;
2627
}
2728

28-
public async Task<List<Product>> GetAllAsync()
29-
{
30-
var request = CreateRequest(HttpMethod.Get, "/api/products");
31-
var response = await _http.SendAsync(request);
29+
public Task<UAuthResult<List<SampleProduct>>> GetAllAsync()
30+
=> SendAsync<List<SampleProduct>>(CreateRequest(HttpMethod.Get, "/api/products"));
3231

33-
if (!response.IsSuccessStatusCode)
34-
throw new Exception($"Failed to fetch products: {response.StatusCode}");
32+
public Task<UAuthResult<SampleProduct?>> GetAsync(int id)
33+
=> SendAsync<SampleProduct?>(CreateRequest(HttpMethod.Get, $"/api/products/{id}"));
3534

36-
return await response.Content.ReadFromJsonAsync<List<Product>>() ?? new();
37-
}
35+
public Task<UAuthResult<SampleProduct?>> CreateAsync(SampleProduct product)
36+
=> SendAsync<SampleProduct?>(CreateRequest(HttpMethod.Post, $"/api/products", product));
3837

39-
public async Task<Product?> GetAsync(int id)
40-
{
41-
var request = CreateRequest(HttpMethod.Get, $"/api/products/{id}");
42-
var response = await _http.SendAsync(request);
38+
public Task<UAuthResult<SampleProduct?>> UpdateAsync(int id, SampleProduct product)
39+
=> SendAsync<SampleProduct?>(CreateRequest(HttpMethod.Put, $"/api/products/{id}", product));
4340

44-
if (!response.IsSuccessStatusCode)
45-
return null;
41+
public Task<UAuthResult<SampleProduct?>> DeleteAsync(int id)
42+
=> SendAsync<SampleProduct?>(CreateRequest(HttpMethod.Delete, $"/api/products/{id}"));
4643

47-
return await response.Content.ReadFromJsonAsync<Product>();
48-
}
49-
50-
public async Task<Product?> CreateAsync(Product product)
44+
private async Task<UAuthResult<T>> SendAsync<T>(HttpRequestMessage request)
5145
{
52-
var request = CreateRequest(HttpMethod.Post, "/api/products", product);
5346
var response = await _http.SendAsync(request);
5447

55-
if (!response.IsSuccessStatusCode)
56-
throw new Exception($"Create failed: {response.StatusCode}");
57-
58-
return await response.Content.ReadFromJsonAsync<Product>();
59-
}
60-
61-
public async Task<Product?> UpdateAsync(int id, Product product)
62-
{
63-
var request = CreateRequest(HttpMethod.Put, $"/api/products/{id}", product);
64-
var response = await _http.SendAsync(request);
48+
var result = new UAuthResult<T>
49+
{
50+
Status = (int)response.StatusCode,
51+
IsSuccess = response.IsSuccessStatusCode
52+
};
6553

66-
if (!response.IsSuccessStatusCode)
67-
throw new Exception($"Update failed: {response.StatusCode}");
54+
if (response.IsSuccessStatusCode)
55+
{
56+
result.Value = await response.Content.ReadFromJsonAsync<T>();
57+
return result;
58+
}
6859

69-
return await response.Content.ReadFromJsonAsync<Product>();
60+
result.Problem = await TryReadProblem(response);
61+
return result;
7062
}
7163

72-
public async Task DeleteAsync(int id)
64+
private async Task<UAuthProblem?> TryReadProblem(HttpResponseMessage response)
7365
{
74-
var request = CreateRequest(HttpMethod.Delete, $"/api/products/{id}");
75-
var response = await _http.SendAsync(request);
76-
77-
if (!response.IsSuccessStatusCode)
78-
throw new Exception($"Delete failed: {response.StatusCode}");
66+
try
67+
{
68+
return await response.Content.ReadFromJsonAsync<UAuthProblem>();
69+
}
70+
catch
71+
{
72+
return new UAuthProblem
73+
{
74+
Title = response.ReasonPhrase
75+
};
76+
}
7977
}
8078
}

samples/blazor-standalone-wasm/CodeBeam.UltimateAuth.Sample.BlazorStandaloneWasm/ResourceApi/Product.cs renamed to samples/blazor-standalone-wasm/CodeBeam.UltimateAuth.Sample.BlazorStandaloneWasm/ResourceApi/SampleProduct.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
namespace CodeBeam.UltimateAuth.Sample.BlazorStandaloneWasm.ResourceApi;
22

3-
public class Product
3+
public class SampleProduct
44
{
55
public int Id { get; set; }
6-
public string Name { get; set; } = "";
6+
public string? Name { get; set; }
77
}
Lines changed: 3 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,15 @@
1-
using System.Security.Claims;
2-
using CodeBeam.UltimateAuth.Core.Extensions;
31
using CodeBeam.UltimateAuth.Server.Extensions;
42

53
var builder = WebApplication.CreateBuilder(args);
64

7-
// Add services to the container.
8-
95
builder.Services.AddControllers();
10-
// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi
116
builder.Services.AddOpenApi();
127

13-
builder.Services.AddUltimateAuthResourceApi(o => o.UAuthHubBaseUrl = "https://localhost:6110");
14-
15-
builder.Services.AddCors(options =>
16-
{
17-
options.AddPolicy("WasmSample", policy =>
8+
builder.Services.AddUltimateAuthResourceApi(o =>
189
{
19-
policy
20-
.WithOrigins("https://localhost:6130")
21-
.AllowAnyHeader()
22-
.AllowAnyMethod()
23-
.AllowCredentials();
10+
o.UAuthHubBaseUrl = "https://localhost:6110";
11+
o.AllowedClientOrigins.Add("https://localhost:6130");
2412
});
25-
});
2613

2714
var app = builder.Build();
2815

@@ -32,44 +19,11 @@
3219
}
3320

3421
app.UseHttpsRedirection();
35-
app.UseCors("WasmSample");
3622

3723
app.UseUltimateAuthResourceApi();
3824
app.UseAuthentication();
3925
app.UseAuthorization();
4026

4127
app.MapControllers();
42-
app.MapGet("/health", () =>
43-
{
44-
return Results.Ok(new
45-
{
46-
service = "ResourceApi",
47-
status = "ok"
48-
});
49-
});
50-
51-
app.MapGet("/me", (ClaimsPrincipal user) =>
52-
{
53-
return Results.Ok(new
54-
{
55-
IsAuthenticated = user.Identity?.IsAuthenticated,
56-
Name = user.Identity?.Name,
57-
Claims = user.Claims.Select(c => new
58-
{
59-
c.Type,
60-
c.Value
61-
})
62-
});
63-
})
64-
.RequireAuthorization();
65-
66-
app.MapGet("/data", () =>
67-
{
68-
return Results.Ok(new
69-
{
70-
Message = "You are authorized to access protected data."
71-
});
72-
})
73-
.RequireAuthorization("ApiUser");
7428

7529
app.Run();
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
using CodeBeam.UltimateAuth.Core.Contracts;
2+
using CodeBeam.UltimateAuth.Core.Defaults;
3+
4+
namespace CodeBeam.UltimateAuth.Sample.ResourceApi;
5+
6+
public static class AppActions
7+
{
8+
public static class Products
9+
{
10+
public static readonly string Read = UAuthActions.Create("products", "read", ActionScope.Self);
11+
12+
public static readonly string Create = UAuthActions.Create("products", "create", ActionScope.Admin);
13+
14+
public static readonly string Update = UAuthActions.Create("products", "update", ActionScope.Admin);
15+
16+
public static readonly string Delete = UAuthActions.Create("products", "delete", ActionScope.Admin);
17+
}
18+
}

‎samples/resource-api/CodeBeam.UltimateAuth.Sample.ResourceApi/SampleData/ProductStore.cs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@
22

33
public static class ProductStore
44
{
5-
public static List<Product> Items = new() { new Product() { Id = 0, Name = "Test"} };
5+
public static List<SampleProduct> Items = new() { new SampleProduct() { Id = 0, Name = "Test"} };
66
}

0 commit comments

Comments
 (0)