aws / aws/aws-cdk

(core): detail-scoped warning suppression via structured context on Validations.acknowledge

Open
#38,812 4 comments 0 reactions 0 assignees View on GitHub
cloudformation-validation effort/medium feature-request p2
Dominant language
TypeScript
Stars
12.9k
Forks
4.6k
Avg merge
2d 3h
Merged PRs (30d)
83

Description

### Describe the feature

Allow `Validations.acknowledge()` to suppress a *subset* of a warning id's occurrences by matching structured context attached to the warning, instead of the current all-or-nothing-per-id behavior. There is no wildcarding or globbing on the id or the message — matching is on explicit, structured key/value context that the construct chooses to attach.

### Use case

Some constructs emit a single warning id for a class of situations that is intentionally heterogeneous — for example a generic "unsupported input" or "unsupported action type" warning that can fire for several different services or resource types, all under one id.

Today the only lever is `Validations.of(x).acknowledge({ id, reason })` (or `Annotations.of(x).acknowledgeWarning(id)`), which suppresses **every** occurrence of that id. A user who intentionally uses one of those inputs cannot acknowledge just that one case while still being warned about the others — they must silence the whole class and lose signal for the cases they still care about.

A concrete instance surfaced while reviewing #38724 (the LogAlarm L2), which warns under a single id when an alarm action's service is not in its supported set. A user may legitimately attach one such action on purpose and accept that it will not be dispatched, while still wanting to be warned about the other unsupported cases — which the current single-id acknowledgement cannot express.

### Proposed solution

Add an optional, generic structured context to the warning, and an optional matching filter to the acknowledgement. Scope the **public API change to the `Validations` subsystem only** (where `reason` is already required, so every selective suppression is documented), leaving the `Annotations.acknowledgeWarning` public signature untouched.

Emit side — `Validations.addWarning` gains optional context (keys are the construct's choice; illustrative below):

```ts
Validations.of(scope).addWarning(
'aws-cloudwatch:someUnsupportedActionType',
'This action type is not supported and will be ignored',
{ context: { service: 'aiops', resource: 'investigation-group' } }, // NEW
);
```

Suppress all — unchanged, backwards compatible:

```ts
Validations.of(scope).acknowledge({ id: 'aws-cloudwatch:someUnsupportedActionType', reason: '...' });
```

Suppress selectively — `reason` still required:

```ts
Validations.of(scope).acknowledge({
id: 'aws-cloudwatch:someUnsupportedActionType',
reason: 'This action is attached intentionally and its non-dispatch is acceptable',
where: [ /* predicate(s) — see below */ ],
});
```

Core stays generic: the context is a plain `{ [key: string]: string }` map, and the construct decides the keys.

#### Filter shape — `where` as an array of predicates (for future operator extensibility)

`where` should be an **array of predicates**, not an object map. A map keyed by field name can only ever express equality and cannot grow comparison operators without a breaking change; an array of predicate objects can gain operators (`contains`, `startsWith`, `regex`, …) later, non-breaking. **Only equality is needed in the initial version** — the array shape is purely to keep that door open.

Two candidate element shapes:

**Option A — data struct with a future optional operator:**

```ts
export interface WarningContextFilter {
readonly key: string; // matches a key in the warning's context
readonly value: string;
// readonly operator?: FilterOperator; // FUTURE — defaults to EQUALS; non-breaking to add
}

where: [{ key: 'service', value: 'aiops' }]
```

**Option B — factory-method opaque type (preferred):**

```ts
where: [ WarningContextFilter.equals('service', 'aiops') ]
// future: WarningContextFilter.contains(...), WarningContextFilter.matches(...), etc.
```

Option B is the more CDK-idiomatic way to model a growing operator set (mirrors `Match`, `elbv2.ListenerCondition`, `events.Schedule`), avoids jsii-incompatible union types, and lets each future operator carry its own parameter shape rather than forcing everything into a single `value: string`. Option A is the lighter alternative if operators are expected to remain purely string-valued.

#### Matching semantics

- `where` absent → suppress all occurrences of the id (**current behavior, backwards compatible**).
- `where` present → suppress an occurrence iff **all** predicates match its context (AND). Equality-only in v1.
- A warning with no context is only ever matched by a filter-less acknowledgement — a specific predicate can never accidentally silence an undetailed warning.

### Why this is backwards compatible and respects the original design

- Existing callers pass no context and no `where` → behavior is unchanged.
- A filtered acknowledgement is a refinement **within a single id** — it never crosses id boundaries and always suppresses a strict subset of what a bare `acknowledge({ id })` suppresses today. It therefore cannot weaken `--strict` mode (the reason id/message wildcards are undesirable); if anything it improves strict-mode usability, since teams currently forced to silence a whole id could instead scope the suppression and regain warnings for the non-matching cases.
- Acknowledgements stay enumerable and auditable: a concrete id + explicit predicates + a required reason.

### Implementation notes

- **Do not encode context into the message string.** The `[ack: ]` tag appended to warning messages is effectively a CLI display contract, and `:` / `::` are already reserved (the latter is the `Validations` prefix separator). Context should be carried as structured metadata and matched in-framework at synth; the CLI does not need to parse it.
- **Public API confined to `Validations`, but the private suppression engine is shared.** `Validations.acknowledge` delegates into the `Annotations`/`Acknowledgements` internals, which own emit-time suppression and tree purge (what actually stops the warning from printing). Those internals must learn about context — storing it structurally on the warning's metadata entry so selective purge works regardless of whether the acknowledgement is declared before or after the warning is emitted. No public `Annotations` signature changes are required.
- Interacts with in-flight fixes #37764 (Annotations/Validations interoperability) and #38495 (acknowledge scope-correctness); this should build on top of them.
- No new dependency required.

### Other information

This is a public-API addition to `core` that touches `--strict` semantics, so it likely warrants an RFC before implementation. Related prior art: #26144 (introduced `acknowledgeWarning`) and #23403 (original request for suppressible annotations).

### Acknowledgements

- [ ] I may be able to implement this feature request
- [ ] This feature might incur a breaking change

Contributor guide

Open the contributing guide

Research direction

Start with the Validations.addWarning and Validations.acknowledge entry points, then trace their delegation into the Annotations/Acknowledgements internals that handle emit-time suppression and tree purge. Resolve the filter-shape choice through the proposed RFC, and consider the work done when structured context and selective equality matching work for acknowledgements declared before or after warnings without changing the public Annotations signature.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
api
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.