Skip to content

[ADR-161] Working PR for Layout customization backend service - #138

Draft
jodavis wants to merge 11 commits into
mainfrom
feature/ADR-161-cusrtomization-service
Draft

[ADR-161] Working PR for Layout customization backend service#138
jodavis wants to merge 11 commits into
mainfrom
feature/ADR-161-cusrtomization-service

Conversation

@jodavis

Copy link
Copy Markdown
Owner

Working PR for a cloud backend that enables administrators to create, edit, compile, and publish remote control layouts for the AdaptiveRemote client application. Administrators edit layouts via a web editor; the backend stores, compiles, validates, and distributes them; client applications download and cache layouts and update automatically when new versions are published.

This epic covers the backend services only. The web editor UI, client-side layout integration, CI/CD deployment, and load testing are covered by related epics ADR-162 through ADR-165.

@github-actions

github-actionsBot commented Apr 7, 2026

Copy link
Copy Markdown

Test Results

434 tests +33 434 ✅ +33 2m 27s ⏱️ +4s
8 suites + 3 0 💤 ± 0 
8 files + 3 0 ❌ ± 0 

Results for commit 799c155. ± Comparison against base commit aee2d9f.

This pull request removes 3 and adds 35 tests. Note that renamed tests count towards both.
,False)
AdaptiveRemote.Services.ProgrammaticSettings.PersistSettingsTests ‑ PersistSettings_Set_ValidatesKeyNameAsync (Hello
AdaptiveRemote.Services.ProgrammaticSettings.PersistSettingsTests ‑ PersistSettings_Set_ValidatesValueAsync (Invalid
AdaptiveRemote.Backend.ApiTests.Features.CompiledLayoutServiceAuthenticationFeature ‑ Health endpoint is accessible without authentication
AdaptiveRemote.Backend.ApiTests.Features.CompiledLayoutServiceAuthenticationFeature ‑ Request with expired JWT is rejected
AdaptiveRemote.Backend.ApiTests.Features.CompiledLayoutServiceAuthenticationFeature ‑ Request with valid JWT is accepted
AdaptiveRemote.Backend.ApiTests.Features.CompiledLayoutServiceAuthenticationFeature ‑ Unauthenticated request is rejected
AdaptiveRemote.Backend.ApiTests.Features.CompiledLayoutServiceEndpointsFeature ‑ Get active compiled layout
AdaptiveRemote.Backend.ApiTests.Features.HealthEndpointsFeature ‑ Get service health status
AdaptiveRemote.Backend.RawLayoutService.Tests.Repositories.DynamoDbRawLayoutRepositoryTests ‑ DeleteAsync_WhenCancelledDuringScan_TaskIsCancelled
AdaptiveRemote.Backend.RawLayoutService.Tests.Repositories.DynamoDbRawLayoutRepositoryTests ‑ DeleteAsync_WhenDeleteItemThrowsException_TaskIsFaulted
AdaptiveRemote.Backend.RawLayoutService.Tests.Repositories.DynamoDbRawLayoutRepositoryTests ‑ DeleteAsync_WhenScanAsyncIsPending_TaskIsNotComplete
AdaptiveRemote.Backend.RawLayoutService.Tests.Repositories.DynamoDbRawLayoutRepositoryTests ‑ DeleteAsync_WhenScanThrowsException_TaskIsFaulted
…

♻️ This comment has been updated with latest results.

@jodavis
jodavis marked this pull request as ready for review April 8, 2026 16:57
@jodavis
jodavis marked this pull request as draft April 8, 2026 16:57
@jodavisjodavis changed the title [ADR-161] Layout customization backend service[ADR-161] Working PR for Layout customization backend serviceApr 19, 2026
@jodavis
jodavisforce-pushed the feature/ADR-161-cusrtomization-service branch 4 times, most recently from 4eb619e to 106f115CompareApril 23, 2026 20:49
ElwoodMovesand others added 8 commits April 28, 2026 07:16
* ADR-166: Add AdaptiveRemote.Contracts shared library and solution filters
- Add AdaptiveRemote.Contracts project (net10.0, no platform-specific dependencies)
containing all layout definition DTOs, enums, and LayoutContractsJsonContext
from the spec's Shared Contracts section:
CommandType enum, ICommandProperties interface, LayoutElementDto hierarchy
(compiled), RawLayoutElementDto hierarchy (raw), RawLayout/CompiledLayout/
PreviewLayout top-level records, ValidationIssue/ValidationResult records,
and source-generated LayoutContractsJsonContext for consistent serialization
across all consumers including Native AOT Lambda functions.
- Add AdaptiveRemote.Contracts to AdaptiveRemote.sln with full build configurations.
- Reference AdaptiveRemote.Contracts from AdaptiveRemote.App.
- Add client.slnf and backend.slnf solution filters.
- Update _doc_Projects.md to document the new shared contracts project.
https://claude.ai/code/session_01T3tonn7C9F6TYbqH23KmG1
* Address PR review comments
- Remove Action from CommandType enum (it's a WPF adapter, not a command type)
- Change type discriminator from "type" to "\$type" on both LayoutElementDto and
RawLayoutElementDto to avoid conflict with the behavioral Type property on
CommandDefinitionDto and RawCommandDefinitionDto
- Move RawLayout into RawLayoutElementDto.cs, CompiledLayout into LayoutElementDto.cs,
PreviewLayout into its own PreviewLayout.cs; delete Layouts.cs
https://claude.ai/code/session_01T3tonn7C9F6TYbqH23KmG1
---------
Co-authored-by: Claude <noreply@anthropic.com>
* ADR-167: Create CompiledLayoutService with health and layout endpoints
* ADR-167: Add docker-compose and API integration tests infrastructure
* ADR-167: Fix API test path resolution and assertions - all API tests passing
* ADR-167: Address code review comments - use repository pattern, nameof, error logging, proper test assertions
Co-authored-by: anthropic-code-agent[bot] <242468646+Claude@users.noreply.github.com>
Co-authored-by: jodavis <6740581+jodavis@users.noreply.github.com>
…o fixed the timeout so it doesn't take 2.5 minutes to wait 30 seconds.
* [ADR-168] Wire JWT Bearer auth via Cognito; add CognitoTokenService and auth API tests
- CompiledLayoutService: add JWT Bearer authentication backed by AWS Cognito.
GET /layouts/compiled/active now requires a valid bearer token; GET /health
remains unauthenticated. The sub claim is extracted as userId.
CognitoSettings reads Authority and Audience from appsettings/env vars.
- Client app: add BackendSettings, ICognitoTokenService, CognitoTokenService.
CognitoTokenService acquires and caches tokens via the OAuth2 Client Credentials
flow (lazy acquire, 60-second expiry buffer). Log messages in range 1600-1699.
- API tests: add TestJwtAuthority (local OIDC/JWKS server) so auth can be tested
end-to-end without a real Cognito user pool. ServiceFixture now starts the
authority first and configures Cognito__Authority on the service process.
CommonSteps updated to use Reqnroll context injection (ServiceContext) for
shared fixture state. AuthenticationEndpoints.feature covers 401 for no-auth
and expired tokens, 200 for valid JWT, and unauthenticated /health access.
- Docs: _doc_Auth.md documents Cognito setup for local dev, client credentials
config, editor Authorization Code flow, and the test JWT authority pattern.
https://claude.ai/code/session_01LLWQBraEp7n7uLVy7M4PFp
* Fix build failures
* [ADR-168] Wire JWT Bearer auth via Cognito; add CognitoTokenService and auth API tests
- CompiledLayoutService: add JWT Bearer authentication backed by AWS Cognito.
GET /layouts/compiled/active now requires a valid bearer token; GET /health
remains unauthenticated. The sub claim is extracted as userId.
CognitoSettings reads Authority and Audience from appsettings/env vars.
- Client app: add BackendSettings, ICognitoTokenService, CognitoTokenService.
CognitoTokenService acquires and caches tokens via the OAuth2 Client Credentials
flow (lazy acquire, 60-second expiry buffer). Log messages in range 1600-1699.
- API tests: add TestJwtAuthority (local OIDC/JWKS server) so auth can be tested
end-to-end without a real Cognito user pool. ServiceFixture now starts the
authority first and configures Cognito__Authority on the service process.
CommonSteps updated to use Reqnroll context injection (ServiceContext) for
shared fixture state. AuthenticationEndpoints.feature covers 401 for no-auth
and expired tokens, 200 for valid JWT, and unauthenticated /health access.
- Docs: _doc_Auth.md documents Cognito setup for local dev, client credentials
config, editor Authorization Code flow, and the test JWT authority pattern.
https://claude.ai/code/session_01LLWQBraEp7n7uLVy7M4PFp
* Fix API test hanging, build errors, and missing package
Three categories of fixes:
1. API tests hanging: ServiceFixture.StartService() was blocking the async
test thread with Thread.Sleep() and HttpClient.GetAsync().Result, risking
a deadlock when called from Reqnroll's async step dispatcher. Renamed to
StartServiceAsync(), switched to await/Task.Delay, added a 5-second per-
request timeout on the health-check client, and made the [Given] step
binding async. The service URL is now allocated on a dynamic free port
instead of hardcoded :5000, eliminating port-conflict failures between
consecutive scenarios.
2. Build errors:
- Removed 'using System.Net.Http;' from CognitoTokenService.cs — the
namespace is already a global implicit using for Microsoft.NET.Sdk.Razor
and the redundant directive triggers a warning/error with /warnaserror.
- Changed <Content Include="appsettings*.json"> to <Content Update=...> in
AdaptiveRemote.csproj so we only set CopyToOutputDirectory metadata on
items the SDK already includes automatically, instead of adding duplicate
Content items.
3. Missing package: Added Microsoft.NET.Test.Sdk to AdaptiveRemote.Backend.
ApiTests.csproj — required by Reqnroll.MSTest for proper test-host
infrastructure (consistent with all other test projects in the solution).
https://claude.ai/code/session_01LLWQBraEp7n7uLVy7M4PFp
* Fix build errors and workflow: package mismatch, CA1816, and E2E test gating
Three build fixes:
1. Package mismatch (NU1608): Replace MSTest meta-package with
MSTest.TestAdapter + MSTest.TestFramework in ApiTests project.
MSTest 3.1.1 meta-package requires Microsoft.NET.Test.Sdk = 17.6.0
exactly, which conflicts with the 18.0.1 version in Directory.Packages.props.
Other test projects already use the individual packages to avoid this.
2. Code analysis error (CA1816): Add GC.SuppressFinalize(this) to
ServiceContext.Dispose() as required by CA1816.
3. Workflow: Add id: build to the Build step and change E2E Tests from
if: always() to if: always() && steps.build.outcome == 'success'.
This ensures E2E tests skip when the build fails, but still run when
only unit tests fail (as was the intent of always()).
Agent-Logs-Url: https://github.com/jodavis/AdaptiveRemote/sessions/d7976913-3a8a-4c5f-b6ce-5f024fe07d01
Co-authored-by: jodavis <6740581+jodavis@users.noreply.github.com>
* Fix API test URL mismatch: add --no-launch-profile to prevent launchSettings from overriding ASPNETCORE_URLS
Root cause: dotnet run reads launchSettings.json and overwrites ASPNETCORE_URLS
with applicationUrl (https://localhost:54433;http://localhost:54434), ignoring
the dynamic port set in ProcessStartInfo.Environment. The health check polls the
dynamic port but the service listens on 54434, so it always times out.
Changes:
1. ServiceFixture.cs: Added --no-launch-profile to dotnet run so ASPNETCORE_URLS
from the test fixture's environment is respected.
2. ServiceFixture.cs: Added per-attempt diagnostic logging in the health check
loop showing the URL polled and the status/exception per attempt.
3. Program.cs: Fixed misleading ServiceStarted log — app.Urls is always empty
before Run(), so read ASPNETCORE_URLS from IConfiguration instead.
Agent-Logs-Url: https://github.com/jodavis/AdaptiveRemote/sessions/ba6750ee-d018-4f28-b4d0-7d4e309d6f02
Co-authored-by: jodavis <6740581+jodavis@users.noreply.github.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Joe Davis <ElwoodMoves@hotmail.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: jodavis <6740581+jodavis@users.noreply.github.com>
… experience for backend services. Also added guidance for adding a task to a spec, for future additions.
… checks, and docs) and unblock Linux full-solution builds (#145)
* Update global.json to allow installed .NET SDKs in different environments
* Implement ADR-187 backend dev environment foundations
Agent-Logs-Url: https://github.com/jodavis/AdaptiveRemote/sessions/b1ffa40a-c569-45a6-ad54-bf0f1a3681b0
Co-authored-by: jodavis <6740581+jodavis@users.noreply.github.com>
* Enable windows targeting for non-headless E2E host projects
Agent-Logs-Url: https://github.com/jodavis/AdaptiveRemote/sessions/b1ffa40a-c569-45a6-ad54-bf0f1a3681b0
Co-authored-by: jodavis <6740581+jodavis@users.noreply.github.com>
* Refine LocalStack health check diagnostics in backend startup
Agent-Logs-Url: https://github.com/jodavis/AdaptiveRemote/sessions/b1ffa40a-c569-45a6-ad54-bf0f1a3681b0
Co-authored-by: jodavis <6740581+jodavis@users.noreply.github.com>
* Fix LocalStack health parsing for dotnet run startup verification
Agent-Logs-Url: https://github.com/jodavis/AdaptiveRemote/sessions/d15fa3e9-7842-4324-aade-379b3c18dde0
Co-authored-by: jodavis <6740581+jodavis@users.noreply.github.com>
* Clarify LocalStack health response docs
Agent-Logs-Url: https://github.com/jodavis/AdaptiveRemote/sessions/d15fa3e9-7842-4324-aade-379b3c18dde0
Co-authored-by: jodavis <6740581+jodavis@users.noreply.github.com>
* Add LocalStack startup retries and exception-aware dependency logging
Agent-Logs-Url: https://github.com/jodavis/AdaptiveRemote/sessions/af738fc3-eb45-4437-8bb9-b96b3afb41f0
Co-authored-by: jodavis <6740581+jodavis@users.noreply.github.com>
* Address reviewer nits in LocalStack startup checks
Agent-Logs-Url: https://github.com/jodavis/AdaptiveRemote/sessions/af738fc3-eb45-4437-8bb9-b96b3afb41f0
Co-authored-by: jodavis <6740581+jodavis@users.noreply.github.com>
* Refine LocalStack timeout constants and naming consistency
Agent-Logs-Url: https://github.com/jodavis/AdaptiveRemote/sessions/af738fc3-eb45-4437-8bb9-b96b3afb41f0
Co-authored-by: jodavis <6740581+jodavis@users.noreply.github.com>
* Update _doc_Auth.md with details on how to get an access token for scalar testing.
---------
Co-authored-by: Joe Davis <ElwoodMoves@hotmail.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: jodavis <6740581+jodavis@users.noreply.github.com>
@jodavis
jodavisforce-pushed the feature/ADR-161-cusrtomization-service branch from 106f115 to 5ff955cCompareApril 29, 2026 03:26
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@jodavis@ElwoodMoves@Claude