Reformat and tweaking claude.md
This commit is contained in:
@@ -33,7 +33,7 @@ MicCheck: open source. asp.net, c#, typescript, VueJS. Manage feature flags, pro
|
|||||||
## Testing
|
## Testing
|
||||||
- Min 70% code coverage, target 90%. Unit tests focus end-user scenarios first.
|
- Min 70% code coverage, target 90%. Unit tests focus end-user scenarios first.
|
||||||
- Don't write tests just for coverage. Call out missing coverage rather than cover stuff not valuable to end user.
|
- Don't write tests just for coverage. Call out missing coverage rather than cover stuff not valuable to end user.
|
||||||
- Code not cleanly unit-testable → mark `[ExcludeFromCodeCoverage]` or exclude namespace from coverage.
|
- Code not cleanly unit-testable → mark `[ExcludeFromCodeCoverage]` or exclude namespace from coverage in .runsettings file
|
||||||
- BDD-style unit tests, end-to-end as possible, no external resources (DB, filesystem). e.g. `WhenAUserDoesSomething_ThenAThingAppears`
|
- BDD-style unit tests, end-to-end as possible, no external resources (DB, filesystem). e.g. `WhenAUserDoesSomething_ThenAThingAppears`
|
||||||
- Mock external deps w/ Moq
|
- Mock external deps w/ Moq
|
||||||
- Mock EntityFramework DBContexts via extracted interface + Moq. Don't rely on InMemory provider.
|
- Mock EntityFramework DBContexts via extracted interface + Moq. Don't rely on InMemory provider.
|
||||||
@@ -50,4 +50,4 @@ MicCheck: open source. asp.net, c#, typescript, VueJS. Manage feature flags, pro
|
|||||||
- Plan implemented from `docs/plans/<name>.md` → save summary as `docs/plans/<name>_output.md`
|
- Plan implemented from `docs/plans/<name>.md` → save summary as `docs/plans/<name>_output.md`
|
||||||
|
|
||||||
## Stack
|
## Stack
|
||||||
`.gitignore` set for .NET/Visual Studio (C#, NuGet, MSBuild). Update if stack change.
|
`.gitignore` set for .NET/Visual Studio (C#, NuGet, MSBuild). Update if stack change.
|
||||||
|
|||||||
@@ -12,17 +12,15 @@ namespace MicCheck.Api.Audit;
|
|||||||
[ApiController]
|
[ApiController]
|
||||||
[Authorize(Policy = AuthorizationPolicies.AdminApiAccess)]
|
[Authorize(Policy = AuthorizationPolicies.AdminApiAccess)]
|
||||||
[EnableRateLimiting("AdminApi")]
|
[EnableRateLimiting("AdminApi")]
|
||||||
|
[Route("api/v1")]
|
||||||
public class AuditLogsController(
|
public class AuditLogsController(
|
||||||
AuditLogQueryService auditLogQueryService,
|
AuditLogQueryService auditLogQueryService,
|
||||||
OrganizationService organizationService,
|
OrganizationService organizationService,
|
||||||
ProjectService projectService,
|
ProjectService projectService,
|
||||||
EnvironmentService environmentService) : ControllerBase
|
EnvironmentService environmentService) : ControllerBase
|
||||||
{
|
{
|
||||||
[HttpGet("api/v1/organisation/{id}/audit-logs")]
|
[HttpGet("organisation/{id}/audit-logs")]
|
||||||
public async Task<ActionResult<PaginatedResponse<AuditLogResponse>>> ListByOrganization(
|
public async Task<ActionResult<PaginatedResponse<AuditLogResponse>>> ListByOrganization(int id, [FromQuery] AuditLogFilter filter, CancellationToken ct)
|
||||||
int id,
|
|
||||||
[FromQuery] AuditLogFilter filter,
|
|
||||||
CancellationToken ct)
|
|
||||||
{
|
{
|
||||||
var org = await organizationService.FindByIdAsync(id, ct);
|
var org = await organizationService.FindByIdAsync(id, ct);
|
||||||
if (org is null) return NotFound();
|
if (org is null) return NotFound();
|
||||||
@@ -31,11 +29,8 @@ public class AuditLogsController(
|
|||||||
return Ok(new PaginatedResponse<AuditLogResponse>(result.Total, null, null, result.Items.ToList()));
|
return Ok(new PaginatedResponse<AuditLogResponse>(result.Total, null, null, result.Items.ToList()));
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("api/v1/project/{projectId}/audit-logs")]
|
[HttpGet("project/{projectId}/audit-logs")]
|
||||||
public async Task<ActionResult<PaginatedResponse<AuditLogResponse>>> ListByProject(
|
public async Task<ActionResult<PaginatedResponse<AuditLogResponse>>> ListByProject(int projectId, [FromQuery] AuditLogFilter filter, CancellationToken ct)
|
||||||
int projectId,
|
|
||||||
[FromQuery] AuditLogFilter filter,
|
|
||||||
CancellationToken ct)
|
|
||||||
{
|
{
|
||||||
var project = await projectService.FindByIdAsync(projectId, ct);
|
var project = await projectService.FindByIdAsync(projectId, ct);
|
||||||
if (project is null) return NotFound();
|
if (project is null) return NotFound();
|
||||||
@@ -44,11 +39,8 @@ public class AuditLogsController(
|
|||||||
return Ok(new PaginatedResponse<AuditLogResponse>(result.Total, null, null, result.Items.ToList()));
|
return Ok(new PaginatedResponse<AuditLogResponse>(result.Total, null, null, result.Items.ToList()));
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("api/v1/environment/{apiKey}/audit-logs")]
|
[HttpGet("environment/{apiKey}/audit-logs")]
|
||||||
public async Task<ActionResult<PaginatedResponse<AuditLogResponse>>> ListByEnvironment(
|
public async Task<ActionResult<PaginatedResponse<AuditLogResponse>>> ListByEnvironment(string apiKey, [FromQuery] AuditLogFilter filter, CancellationToken ct)
|
||||||
string apiKey,
|
|
||||||
[FromQuery] AuditLogFilter filter,
|
|
||||||
CancellationToken ct)
|
|
||||||
{
|
{
|
||||||
var environment = await environmentService.FindByApiKeyAsync(apiKey, ct);
|
var environment = await environmentService.FindByApiKeyAsync(apiKey, ct);
|
||||||
if (environment is null) return NotFound();
|
if (environment is null) return NotFound();
|
||||||
|
|||||||
@@ -1,9 +1,3 @@
|
|||||||
namespace MicCheck.Api.Common.Security.ApiKeys;
|
namespace MicCheck.Api.Common.Security.ApiKeys;
|
||||||
|
|
||||||
public record ApiKeyResponse(
|
public record ApiKeyResponse(int Id, string Name, string Prefix, bool IsActive, DateTimeOffset? ExpiresAt, DateTimeOffset CreatedAt);
|
||||||
int Id,
|
|
||||||
string Name,
|
|
||||||
string Prefix,
|
|
||||||
bool IsActive,
|
|
||||||
DateTimeOffset? ExpiresAt,
|
|
||||||
DateTimeOffset CreatedAt);
|
|
||||||
|
|||||||
@@ -13,10 +13,7 @@ namespace MicCheck.Api.Features;
|
|||||||
public class FeatureUsageController(FeatureUsageQueryService queryService, IMemoryCache cache) : ControllerBase
|
public class FeatureUsageController(FeatureUsageQueryService queryService, IMemoryCache cache) : ControllerBase
|
||||||
{
|
{
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
public async Task<ActionResult<DashboardUsageResponse>> GetDashboardUsage(
|
public async Task<ActionResult<DashboardUsageResponse>> GetDashboardUsage(int environmentId, [FromQuery] int days = 14, CancellationToken ct = default)
|
||||||
int environmentId,
|
|
||||||
[FromQuery] int days = 14,
|
|
||||||
CancellationToken ct = default)
|
|
||||||
{
|
{
|
||||||
days = Math.Clamp(days, 1, 90);
|
days = Math.Clamp(days, 1, 90);
|
||||||
var cacheKey = $"usage-dashboard:{environmentId}:{days}";
|
var cacheKey = $"usage-dashboard:{environmentId}:{days}";
|
||||||
|
|||||||
Reference in New Issue
Block a user