Skip to content

Commit 229abbc

Browse files
CopilotPhantomDave
andauthored
Add authorization, input validation, and fix N+1 queries in Dashboard APIs (#93)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: PhantomDave <34485699+PhantomDave@users.noreply.github.com>
1 parent 4e1fed8 commit 229abbc

6 files changed

Lines changed: 30 additions & 35 deletions

File tree

PhantomDave.BankTracking.Api/Services/DashboardService.cs

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,20 +20,11 @@ public async Task<IEnumerable<Dashboard>> GetAllDashboardsAsync()
2020
{
2121
var dashboards = await _unitOfWork.Dashboards
2222
.Query()
23+
.Include(d => d.Widgets)
2324
.AsNoTracking()
2425
.OrderBy(d => d.Id)
2526
.ToListAsync();
2627

27-
foreach (var dashboard in dashboards)
28-
{
29-
var widgets = await _unitOfWork.DashboardWidgets
30-
.Query()
31-
.Where(w => w.DashboardId == dashboard.Id)
32-
.OrderBy(w => w.Id)
33-
.ToListAsync();
34-
dashboard.Widgets = widgets.ToList();
35-
}
36-
3728
return dashboards;
3829
}
3930

PhantomDave.BankTracking.Api/Types/Mutations/DashboardMutations.cs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using HotChocolate;
2+
using HotChocolate.Authorization;
23
using HotChocolate.Types;
34
using PhantomDave.BankTracking.Api.Types.Inputs;
45
using PhantomDave.BankTracking.Api.Types.ObjectTypes;
@@ -18,17 +19,28 @@ private static string TruncateName(string? name)
1819
return trimmed.Length > MaxDashboardNameLength ? trimmed[..MaxDashboardNameLength] : trimmed;
1920
}
2021

22+
[Authorize]
2123
public async Task<DashboardType> CreateDashboard(
2224
[Service] IUnitOfWork unitOfWork,
2325
[Service] IHttpContextAccessor httpContextAccessor,
2426
CreateDashboardInput input)
2527
{
2628
var accountId = httpContextAccessor.GetAccountIdFromContext();
2729

30+
var trimmedName = TruncateName(input.Name);
31+
if (string.IsNullOrEmpty(trimmedName))
32+
{
33+
throw new GraphQLException(
34+
ErrorBuilder.New()
35+
.SetMessage("Dashboard name cannot be empty.")
36+
.SetCode("BAD_USER_INPUT")
37+
.Build());
38+
}
39+
2840
var dashboard = new Dashboard
2941
{
3042
AccountId = accountId,
31-
Name = TruncateName(input.Name)
43+
Name = trimmedName
3244
};
3345

3446
await unitOfWork.Dashboards.AddAsync(dashboard);
@@ -37,6 +49,7 @@ public async Task<DashboardType> CreateDashboard(
3749
return DashboardType.FromDashboard(dashboard);
3850
}
3951

52+
[Authorize]
4053
public async Task<DashboardType> UpdateDashboard(
4154
[Service] IUnitOfWork unitOfWork,
4255
[Service] IHttpContextAccessor httpContextAccessor,
@@ -59,11 +72,13 @@ public async Task<DashboardType> UpdateDashboard(
5972
dashboard.Name = TruncateName(input.Name);
6073
}
6174

75+
await unitOfWork.Dashboards.UpdateAsync(dashboard);
6276
await unitOfWork.SaveChangesAsync();
6377

6478
return DashboardType.FromDashboard(dashboard);
6579
}
6680

81+
[Authorize]
6782
public async Task<bool> DeleteDashboard(
6883
[Service] IUnitOfWork unitOfWork,
6984
[Service] IHttpContextAccessor httpContextAccessor,

PhantomDave.BankTracking.Api/Types/Mutations/DashboardWidgetMutations.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using HotChocolate;
2+
using HotChocolate.Authorization;
23
using HotChocolate.Types;
34
using PhantomDave.BankTracking.Api.Types.Inputs;
45
using PhantomDave.BankTracking.Api.Types.ObjectTypes;
@@ -10,6 +11,7 @@ namespace PhantomDave.BankTracking.Api.Types.Mutations;
1011
[ExtendObjectType(OperationTypeNames.Mutation)]
1112
public class DashboardWidgetMutations
1213
{
14+
[Authorize]
1315
public async Task<DashboardWidgetType> AddWidget(
1416
[Service] IUnitOfWork unitOfWork,
1517
[Service] IHttpContextAccessor httpContextAccessor,
@@ -52,6 +54,7 @@ public async Task<DashboardWidgetType> AddWidget(
5254
return DashboardWidgetType.FromDashboardWidget(widget);
5355
}
5456

57+
[Authorize]
5558
public async Task<DashboardWidgetType> UpdateWidget(
5659
[Service] IUnitOfWork unitOfWork,
5760
[Service] IHttpContextAccessor httpContextAccessor,
@@ -128,6 +131,7 @@ public async Task<DashboardWidgetType> UpdateWidget(
128131
return DashboardWidgetType.FromDashboardWidget(widget);
129132
}
130133

134+
[Authorize]
131135
public async Task<bool> RemoveWidget(
132136
[Service] IUnitOfWork unitOfWork,
133137
[Service] IHttpContextAccessor httpContextAccessor,

PhantomDave.BankTracking.Api/Types/Queries/DashboardQueries.cs

Lines changed: 6 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -19,20 +19,11 @@ public async Task<IEnumerable<DashboardType>> GetDashboards(
1919

2020
var dashboards = await unitOfWork.Dashboards
2121
.Query()
22+
.Include(d => d.Widgets)
2223
.Where(d => d.AccountId == accountId)
2324
.OrderBy(d => d.Id)
2425
.ToListAsync();
2526

26-
foreach (var dashboard in dashboards)
27-
{
28-
var widgets = await unitOfWork.DashboardWidgets
29-
.Query()
30-
.Where(w => w.DashboardId == dashboard.Id)
31-
.OrderBy(w => w.Id)
32-
.ToListAsync();
33-
dashboard.Widgets = widgets.ToList();
34-
}
35-
3627
return dashboards.Select(DashboardType.FromDashboard);
3728
}
3829

@@ -44,19 +35,16 @@ public async Task<IEnumerable<DashboardType>> GetDashboards(
4435
{
4536
var accountId = httpContextAccessor.GetAccountIdFromContext();
4637

47-
var dashboard = await unitOfWork.Dashboards.GetByIdAsync(id);
38+
var dashboard = await unitOfWork.Dashboards
39+
.Query()
40+
.Include(d => d.Widgets)
41+
.FirstOrDefaultAsync(d => d.Id == id);
42+
4843
if (dashboard is null || dashboard.AccountId != accountId)
4944
{
5045
return null;
5146
}
5247

53-
var widgets = await unitOfWork.DashboardWidgets
54-
.Query()
55-
.Where(w => w.DashboardId == dashboard.Id)
56-
.OrderBy(w => w.Id)
57-
.ToListAsync();
58-
dashboard.Widgets = widgets.ToList();
59-
6048
return DashboardType.FromDashboard(dashboard);
6149
}
6250
}

frontend/src/app/models/dashboards/gridster-item.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,4 @@ export interface Widget extends GridsterItem {
1414
export enum WidgetType {
1515
CurrentBalance,
1616
NetGraph,
17-
Placeholder,
1817
}

frontend/src/app/widgets/widget-net-graph/widget-net-graph.component.spec.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,14 @@
1-
/* tslint:disable:no-unused-variable */
2-
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
3-
import { DebugElement } from '@angular/core';
1+
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
42

53
import { WidgetNetGraphComponent } from './widget-net-graph.component';
64

75
describe('WidgetNetGraphComponent', () => {
86
let component: WidgetNetGraphComponent;
97
let fixture: ComponentFixture<WidgetNetGraphComponent>;
108

11-
beforeEach(async(() => {
9+
beforeEach(waitForAsync(() => {
1210
TestBed.configureTestingModule({
13-
declarations: [ WidgetNetGraphComponent ]
11+
imports: [ WidgetNetGraphComponent ]
1412
})
1513
.compileComponents();
1614
}));

0 commit comments

Comments
 (0)