Skip to content

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

Draft
jodavis wants to merge 9 commits intomainfrom
feature/ADR-161-cusrtomization-service
Draft

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

Conversation

@jodavis
Copy link
Copy Markdown
Owner

@jodavis jodavis commented Apr 7, 2026

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
Copy link
Copy Markdown

github-actions Bot commented Apr 7, 2026

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
@jodavis jodavis changed the title [ADR-161] Layout customization backend service [ADR-161] Working PR for Layout customization backend service Apr 19, 2026
@jodavis jodavis force-pushed the feature/ADR-161-cusrtomization-service branch 4 times, most recently from 4eb619e to 106f115 Compare April 23, 2026 20:49
ElwoodMoves and 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 jodavis force-pushed the feature/ADR-161-cusrtomization-service branch from 106f115 to 5ff955c Compare April 29, 2026 03:26
Sign up for free to 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