Skip to content

Fix case-insensitive stack status changes - #2474

Open
niemyjski wants to merge 7 commits into
mainfrom
issue/stack-status-case-insensitive
Open

Fix case-insensitive stack status changes#2474
niemyjski wants to merge 7 commits into
mainfrom
issue/stack-status-case-insensitive

Conversation

@niemyjski

@niemyjski niemyjski commented Aug 13, 2026

Copy link
Copy Markdown
Member

Summary

  • Accept stack status query values case-insensitively at the Minimal API boundary.
  • Bind and validate status through a small ChangeStackStatusRequest model, returning 422 for empty, unknown, numeric, regressed, or snoozed values.
  • Preserve the historical lowercase JSON and storage contract with canonical status constants.
  • Preserve the existing six-value StackStatus OpenAPI schema for generated clients through an endpoint-local operation transformer.
  • Surface mutation failures directly from the initiating UI action or dialog while keeping the stack API module UI-free.
  • Add API, serializer, OpenAPI snapshot, and Playwright regression coverage.

RCA

The status enum has used lowercase serialized values since the original stack-status implementation. The regression was introduced by the Minimal API migration: MVC enum binding accepted case-insensitive names, while the new Minimal API enum binding rejected lowercase values such as ignored before the handler ran.

Verification

  • Backend build: clean
  • Stack endpoint tests: 51 passed
  • Stack serializer tests: 12 passed
  • OpenAPI snapshot tests: 4 passed
  • Svelte lint/check: clean
  • CI API tests with coverage: passed
  • CI full Playwright E2E: passed
  • CI Docker builds: passed

Breaking changes

None. The route, query name, omitted-status default, lowercase serialization/storage, and generated StackStatus client contract are unchanged.

@niemyjski
niemyjski requested a review from ejsmith August 13, 2026 13:36
@niemyjski niemyjski self-assigned this Aug 13, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ba195a9944

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +370 to +371
status = default;
return false;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Preserve numeric status values accepted by enum binding

BLOCKER: When an existing API caller sends a numeric enum value such as status=1, status=4, or status=5, the former StackStatus? binding accepted it and the handler applied Fixed, Ignored, or Discarded respectively, whereas this hard-coded parser now returns 400. Preserve those aliases or explicitly obtain approval for this public API break.

AGENTS.md reference: AGENTS.md:L67-L67

Useful? React with 👍 / 👎.

})
.RequireAuthorization(AuthorizationRoles.StacksWritePolicy)
.Produces(StatusCodes.Status200OK)
.ProducesProblem(StatusCodes.Status400BadRequest)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Add the new 400 response to the HTTP contract samples

The endpoint now explicitly documents and returns a 400 response for invalid status input, but tests/http/stacks.http remains unchanged and only contains successful lowercase requests at lines 55 and 96. Add an invalid-status request that exercises the new response contract as required for endpoint response changes.

AGENTS.md reference: AGENTS.md:L68-L68

Useful? React with 👍 / 👎.

Comment on lines +111 to +113
} catch (error: unknown) {
toast.error(getProblemMessage(error, `Unable to mark this stack as ${label}.`));
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Keep discard dialogs open when status updates fail

When a discard request fails, this catch reports the error but resolves updateStatus, so MarkStackDiscardedDialog.onSubmit() treats discard() as successful and immediately closes the dialog. The bulk component has the same behavior because its failed markDiscarded() also returns normally; propagate the failure so either dialog closes only after a successful mutation.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 70e93924bd

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

=> (await mediator.InvokeAsync<Result>(new ChangeStacksStatus(ids, Enum.Parse<StackStatus>(status ?? Stack.KnownStatuses.Open, ignoreCase: true), httpContext))).ToHttpResult(resultMapper))
.RequireAuthorization(AuthorizationRoles.StacksWritePolicy)
.Produces(StatusCodes.Status200OK)
.ProducesProblem(StatusCodes.Status422UnprocessableEntity)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Preserve the 400 response for invalid status values

BLOCKER: When a client sends an unsupported named value such as status=unknown, Regressed, or Snoozed, the parent implementation returned 400 through enum binding or the handler, but the new validation path returns 422. HTTP status codes are part of the observable public API contract, so preserve the existing 400 response or obtain explicit approval for the breaking change.

AGENTS.md reference: AGENTS.md:L67-L67

Useful? React with 👍 / 👎.

Comment thread src/Exceptionless.Core/Models/Stack.cs Outdated
public const string Snoozed = "snoozed";
public const string Ignored = "ignored";
public const string Discarded = "discarded";
public const string ChangeablePattern = "(?i)^(" + Open + "|" + Fixed + "|" + Ignored + "|" + Discarded + ")$";

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Publish an ECMA-compatible status pattern

Because this constant is used by RegularExpressionAttribute, OpenAPI publishes it verbatim as the query parameter's schema pattern. OpenAPI 3.1 regex consumers such as JavaScript RegExp reject the .NET-only inline (?i) option with an invalid-group syntax error, which can break schema validation and SDK generation; expose an ECMA-compatible pattern or model the allowed values separately from the server-side case-insensitive check.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e1d9ba6b1a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +17 to +21
try {
await discard();
open = false;
} catch (error) {
toast.error(getProblemMessage(error, 'Unable to discard the selected stacks.'));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Keep the discard dialog open until the mutation succeeds

When the change-status request rejects, this catch cannot retain the dialog because the submit control is still an AlertDialog.Action, whose click closes the alert-dialog root independently of the awaited callback; the toast therefore appears only after the dialog has disappeared, preventing an immediate retry. Fresh evidence after the earlier report is that the new catch now propagates and handles the rejection correctly, but it still runs behind that auto-closing action; use a regular button for submission or otherwise prevent the primitive from closing until discard() succeeds.

Useful? React with 👍 / 👎.

@github-actions

Copy link
Copy Markdown

Code Coverage

Package Line Rate Branch Rate Complexity Health
Exceptionless.Core 75% 67% 9992
Exceptionless.AppHost 38% 40% 139
Exceptionless.Insulation 37% 35% 286
Exceptionless.Web 85% 69% 7033
Summary 78% (23490 / 30296) 67% (11022 / 16526) 17450

@niemyjski

Copy link
Copy Markdown
Member Author

/preview

@github-actions github-actions Bot added the dev-preview Deploy this pull request to the shared dev environment. label Aug 14, 2026
@github-actions

github-actions Bot commented Aug 14, 2026

Copy link
Copy Markdown

Preview deployed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dev-preview Deploy this pull request to the shared dev environment.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant