Skip to content

feat: add print service#139

Merged
jeastham1993 merged 40 commits into
mainfrom
feat/print-service
Feb 11, 2026
Merged

feat: add print service#139
jeastham1993 merged 40 commits into
mainfrom
feat/print-service

Conversation

@jeastham1993

Copy link
Copy Markdown
Collaborator

No description provided.

@jeastham1993 jeastham1993 requested a review from Copilot January 15, 2026 21:46

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a comprehensive print service implementation for Stickerlandia, introducing a complete print job management system with printer registration, job queuing, polling, and acknowledgment capabilities.

Changes:

  • Implements core print service functionality including printer registration, print job submission, polling, and acknowledgment
  • Adds comprehensive unit and integration test coverage for all major components
  • Introduces a Blazor-based printer client application for job processing

Reviewed changes

Copilot reviewed 242 out of 268 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
print-service/tests/Stickerlandia.PrintService.UnitTest/PrintJobTests/*.cs Unit tests for print job query and command handlers
print-service/tests/Stickerlandia.PrintService.IntegrationTest/*.cs Integration tests covering printer and print job workflows
print-service/tests/Stickerlandia.PrintService.Client.Tests/*.cs Unit tests for the printer client application
print-service/src/Stickerlandia.PrintService.Core/**/*.cs Core domain models, handlers, and interfaces for print service
print-service/src/Stickerlandia.PrintService.Client/**/* Blazor-based printer client application
print-service/src/utils/Stickerlandia.PrintService.JwtGenerator/*.cs JWT token generation utility
print-service/src/Stickerlandia.PrintService.Worker/*.cs Background worker services for outbox processing
Files not reviewed (3)
  • print-service/infra/azure/.terraform.lock.hcl: Language not supported
  • print-service/infra/gcp/.terraform.lock.hcl: Language not supported
  • print-service/src/Stickerlandia.PrintService.Agnostic/Migrations/20260115122951_InitialCreate.Designer.cs: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +18 to +24
[JsonPropertyName("EventName")]
public string EventName { get; set; } = string.Empty;

[JsonPropertyName("printerName")]
public string PrinterName { get; set; } = string.Empty;

[JsonPropertyName("Key")]

Copilot AI Jan 15, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The JsonPropertyName attributes use PascalCase ("EventName", "Key") which is inconsistent with the camelCase naming convention used in other response models in the same directory (e.g., "printerId", "printerName"). Consider changing to "eventName" and "key" for consistency.

Suggested change
[JsonPropertyName("EventName")]
public string EventName { get; set; } = string.Empty;
[JsonPropertyName("printerName")]
public string PrinterName { get; set; } = string.Empty;
[JsonPropertyName("Key")]
[JsonPropertyName("eventName")]
public string EventName { get; set; } = string.Empty;
[JsonPropertyName("printerName")]
public string PrinterName { get; set; } = string.Empty;
[JsonPropertyName("key")]

Copilot uses AI. Check for mistakes.
[JsonPropertyName("printerId")]
public string PrinterId { get; set; } = "";

[JsonPropertyName("EventName")]

Copilot AI Jan 15, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The JsonPropertyName attribute uses PascalCase ("EventName") which is inconsistent with the camelCase naming convention used in other DTO models (e.g., "printerId", "printerName"). Consider changing to "eventName" for consistency.

Suggested change
[JsonPropertyName("EventName")]
[JsonPropertyName("eventName")]

Copilot uses AI. Check for mistakes.
[JsonPropertyName("printerId")]
public string PrinterId { get; set; }

[JsonPropertyName("EventName")]

Copilot AI Jan 15, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The JsonPropertyName attribute uses PascalCase ("EventName") which is inconsistent with the camelCase naming convention used for other properties in this class ("printerId", "printerName"). Consider changing to "eventName" for consistency.

Suggested change
[JsonPropertyName("EventName")]
[JsonPropertyName("eventName")]

Copilot uses AI. Check for mistakes.
Key = printer.Key;
}

[JsonPropertyName("Key")] public string Key { get; set; } = string.Empty;

Copilot AI Jan 15, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The JsonPropertyName attribute uses PascalCase ("Key") which is inconsistent with the camelCase naming convention. Consider changing to "key" for consistency with standard JSON naming conventions.

Suggested change
[JsonPropertyName("Key")] public string Key { get; set; } = string.Empty;
[JsonPropertyName("key")] public string Key { get; set; } = string.Empty;

Copilot uses AI. Check for mistakes.
PrinterId = printer?.Id?.Value ?? "";
}

[JsonPropertyName("EventName")]

Copilot AI Jan 15, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The JsonPropertyName attribute uses PascalCase ("EventName") which is inconsistent with the camelCase naming convention used for other properties ("eventVersion", "printerId"). Consider changing to "eventName" for consistency.

Suggested change
[JsonPropertyName("EventName")]
[JsonPropertyName("eventName")]

Copilot uses AI. Check for mistakes.
StickerId = printJob.StickerId;
}

[JsonPropertyName("EventName")]

Copilot AI Jan 15, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The JsonPropertyName attribute uses PascalCase ("EventName") which is inconsistent with the camelCase naming convention used for other properties ("eventVersion", "printJobId", "printerId", "userId", "stickerId"). Consider changing to "eventName" for consistency.

Suggested change
[JsonPropertyName("EventName")]
[JsonPropertyName("eventName")]

Copilot uses AI. Check for mistakes.
FailureReason = printJob.FailureReason ?? string.Empty;
}

[JsonPropertyName("EventName")]

Copilot AI Jan 15, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The JsonPropertyName attribute uses PascalCase ("EventName") which is inconsistent with the camelCase naming convention used for other properties ("eventVersion", "printJobId", "printerId", "failureReason"). Consider changing to "eventName" for consistency.

Suggested change
[JsonPropertyName("EventName")]
[JsonPropertyName("eventName")]

Copilot uses AI. Check for mistakes.
CompletedAt = printJob.CompletedAt ?? DateTimeOffset.UtcNow;
}

[JsonPropertyName("EventName")]

Copilot AI Jan 15, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The JsonPropertyName attribute uses PascalCase ("EventName") which is inconsistent with the camelCase naming convention used for other properties ("eventVersion", "printJobId", "printerId", "completedAt"). Consider changing to "eventName" for consistency.

Suggested change
[JsonPropertyName("EventName")]
[JsonPropertyName("eventName")]

Copilot uses AI. Check for mistakes.

public abstract record DomainEvent
{
[JsonPropertyName("EventName")]

Copilot AI Jan 15, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The JsonPropertyName attribute uses PascalCase ("EventName") which is inconsistent with the camelCase naming convention used for "eventVersion". Consider changing to "eventName" for consistency.

Suggested change
[JsonPropertyName("EventName")]
[JsonPropertyName("eventName")]

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

Comment thread .github/workflows/print-service.yml Fixed
Comment thread .github/workflows/print-service.yml Fixed
Comment thread .github/workflows/print-service.yml Fixed
Comment thread .github/workflows/print-service.yml Fixed
scottgerring

This comment was marked as outdated.

@scottgerring scottgerring left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: SHA tagging inconsistency identified

Comment thread .github/workflows/print-service.yml

@scottgerring scottgerring left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Memory limit

Comment thread print-service/infra/aws/lib/api.ts

@scottgerring scottgerring left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mise task

Comment thread print-service/mise.toml Outdated

@scottgerring scottgerring left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code quality checks

Comment thread .github/workflows/print-service.yml
}

services
.AddStickerlandiaUserManagement();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy-paste: calls AddStickerlandiaUserManagement() - needs renaming

Comment thread print-service/infra/aws/lib/background-workers.ts Outdated
Comment thread print-service/infra/azure/application.tf Outdated
Comment thread print-service/infra/gcp/main.tf Outdated
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"profiles": {
"Stickerlandia.UserManagement.MigrationService": {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy-paste: profile name is Stickerlandia.UserManagement.MigrationService

…/ViewModels/RegisterPrinterResponse.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Comment thread docker-compose.yml

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@scottgerring could you review this file in particular please to check I've not broken anything propertly

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@scottgerring could you have a look at these auth file changes in particular, just to see if you can spot anything obvious I've broken.

jeastham1993 and others added 4 commits January 20, 2026 11:07
Implement print station UI for the sticker printing microservice:

- Add PrintStation page with printer list and status polling
- Add PrintDialog for selecting stickers and submitting print jobs
- Add RegisterPrinterDialog for admin printer registration with API key display
- Add print button to StickerDetail page with navigation state
- Fix Sidebar link to Print Station
- Add print.js service with proper auth and error handling

Security & stability improvements from code review:
- Add AbortController for all async fetch operations
- Add mounted ref checks to prevent state updates after unmount
- Add double-submit protection with ref guards
- Add userId validation before API calls
- Add URL parameter encoding (encodeURIComponent)
- Add token validation in getHeaders() to prevent "Bearer undefined"
- Fix stale closure in escape handler with useCallback

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@claude

claude Bot commented Feb 10, 2026

Copy link
Copy Markdown

Code Review

I've found 9 issues in this PR that need attention:

Critical Bugs 🐛

  1. HttpClient disposed before use (AuthenticationExtensions.cs:85)

    • HttpClientHandler and HttpClient are declared with using var, so they are disposed when the if block exits at line 102
    • However, the IssuerSigningKeyResolver lambda captures httpClient and uses it later during token validation
    • This will throw ObjectDisposedException on every token validation attempt when MetadataAddress is configured
    • Fix: Remove the using keyword from both declarations
  2. Wrong repository method called (AcknowledgePrintJobCommandHandler.cs:57)

    • GetPrinterByKeyAsync is called with a printer ID (format: EVENTNAME-PRINTERNAME) instead of an API key
    • This method searches by the Key field (base64-encoded GUID), so it always returns null
    • Results in eventName always being "unknown" in metrics and printer.RecordJobProcessed() never executing
    • Fix: Use a method that looks up by printer ID, not API key
  3. OutboxProcessor missing event handlers (OutboxProcessor.cs:60)

    • Switch statement only handles printers.registered.v1 events
    • Print job events (printJobs.queued.v1, printJobs.completed.v1, printJobs.failed.v1) fall through to default case
    • All print job events are marked as Failed with "Unknown event type"
    • In agnostic (Postgres/Kafka) deployment, print job lifecycle events are never published
    • Fix: Add cases for print job event types
  4. Duplicate EventId compilation error (LogMessages.cs:96)

    • Both StartingMessageProcessor (line 56) and StoppingMessageProcessor (line 96) use EventId = 5
    • Source generator for [LoggerMessage] will produce error: SYSLIB1002
    • Fix: Change line 96 to use EventId = 11
  5. Route parameter ignored (RegisterPrinterEndpoint.cs:22)

    • Route has /event/{eventName} parameter but uses request.EventName from body instead
    • Caller can POST to /event/A with body {"eventName": "B"} and printer registers under "B"
    • Violates REST semantics and could cause authorization issues
    • Fix: Override request.EventName = eventName or validate they match

Security Issue 🔒

  1. Hardcoded JWT signing key (AuthenticationExtensions.cs:127)
    • Fallback key DRjd/GnduI3Efzen9V9BvbNUfc/VKgXltV7Kbk9sMkY= is publicly visible in source code
    • Same key appears in appsettings.Development.json and TestConstants.cs
    • Anyone can forge valid JWT tokens if config is missing or Authentication:Mode is unconfigured
    • Fix: Throw exception if no signing key configured instead of using hardcoded fallback

Quality Issues

  1. Wrong log message (Program.cs:216)

    • Log states "UserManagement API started" but this is the Print Service
    • Per CLAUDE.md, this is a Print Management Service
    • Fix: Change to "Print Service API started"
  2. RFC 7807 not implemented (GlobalExceptionHandler.cs:102)

    • CLAUDE.md requires RFC 7807 Problem Details format
    • Current response uses {Status: "Error", Message: "..."} instead of RFC 7807 fields
    • Fix: Use ProblemDetails class or implement RFC 7807 structure (type, title, status, detail, instance)
  3. Hardcoded log value (OutboxProcessor.cs:43)

    • Log call passes literal 5 instead of outboxItems.Count
    • Log message always reports 5 items regardless of actual count
    • Fix: Change to LogMessages.LogUnprocessedOutboxItems(logger, outboxItems.Count, null)

Priority: Issues 1-6 are critical and should be fixed before merging. Issues 7-9 are quality improvements but less urgent.

@jeastham1993 jeastham1993 merged commit 5d615cf into main Feb 11, 2026
15 of 17 checks passed
@jeastham1993 jeastham1993 deleted the feat/print-service branch February 11, 2026 21:14
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