openrewrite / openrewrite/rewrite

ChangePropertyKey + UnfoldProperties produce duplicate-key (invalid) YAML when relocating multiple properties into the same new nested prefix (regression from #8291)

Open
#8,838 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Java
Stars
3.7k
Forks
570
Avg merge
13h 12m
Merged PRs (30d)
261

Description

What version of OpenRewrite are you using?
rewrite-yaml 8.89.0 (via org.openrewrite.recipe:rewrite-recipe-bom 3.37.0). Confirmed working correctly on 8.87.0 (rewrite-recipe-bom 3.35.0).

How are you running OpenRewrite?
Gradle plugin, declarative YAML recipe.

What is the smallest, simplest way to reproduce the problem?

A declarative recipe with three ChangePropertyKey steps renaming distinct old properties into a shared new nested prefix, run against a YAML file that already has a top-level spring: mapping (but not yet the target nested prefix), followed by org.openrewrite.yaml.UnfoldProperties to fold the resulting flattened keys back into a nested mapping:

recipeList:
  - org.openrewrite.yaml.ChangePropertyKey:
      oldPropertyKey: legacy.jwks.url
      newPropertyKey: spring.security.oauth2.resourceserver.jwt.jwk-set-uri
      relaxedBinding: true
  - org.openrewrite.yaml.ChangePropertyKey:
      oldPropertyKey: legacy.jwt.issuer
      newPropertyKey: spring.security.oauth2.resourceserver.jwt.issuer-uri
      relaxedBinding: true
  - org.openrewrite.yaml.ChangePropertyKey:
      oldPropertyKey: legacy.jwt.audience
      newPropertyKey: spring.security.oauth2.resourceserver.jwt.audiences
      relaxedBinding: true
  - org.openrewrite.yaml.UnfoldProperties:
      applyTo:
        - $..[security.oauth2.resourceserver.jwt]

Input:

spring:
  application:
    name: my-service
legacy:
  jwks:
    url: https://auth.example.com/jwks
  jwt:
    issuer: https://auth.example.com
    audience: my-audience

What did you expect to see?

A single merged nested block:

spring:
  application:
    name: my-service
  security:
    oauth2:
      resourceserver:
        jwt:
          jwk-set-uri: https://auth.example.com/jwks
          issuer-uri: https://auth.example.com
          audiences: my-audience

(This is what 8.87.0 produced.)

What did you see instead?

Three separate, sibling security: mapping entries under spring: — duplicate keys, which is invalid/undefined per the YAML spec, and silently drops all but the last when read by SnakeYAML/Jackson:

spring:
  application:
    name: my-service
  security:
    oauth2:
      resourceserver:
        jwt:
          jwk-set-uri: https://auth.example.com/jwks
  security:
    oauth2:
      resourceserver:
        jwt:
          issuer-uri: https://auth.example.com
  security:
    oauth2:
      resourceserver:
        jwt:
          audiences: my-audience

Is there something in particular you'd like feedback on?

  • This looks like a regression from #8291: since spring: already exists as a mapping, each ChangePropertyKey now nests its relocated key one level under spring: rather than writing it as a fully-dotted top-level flat key. That's correct in isolation for a single rename, but when three separate renames target the same new nested prefix, each one independently inserts a sibling security: entry rather than recognizing and merging into the one a prior step in the same recipe run just inserted.

I also tried adding org.openrewrite.yaml.CoalesceProperties after UnfoldProperties to squash the duplicates — it does merge the content correctly, but the recipe then fails to stabilize in a single cycle (RewriteTest's "recipe should complete in one cycle" check), and also destabilizes an unrelated, previously-passing test case whose input has no pre-existing spring: tree at all — suggesting the combination is non-idempotent here, not just slower.

Any additional context?

This surfaced while chasing a version bump for rewrite-recipe-bom in a downstream project's own OpenRewrite migration recipes — happy to share the full failing recipe/test if useful.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the ChangePropertyKey and UnfoldProperties entry points and reproduce the three-step recipe against the YAML input shown. Trace how each relocation is inserted beneath the existing spring mapping, then verify that the resulting YAML has one merged security mapping, remains valid, and completes stably in one recipe cycle.

Written by the indexing model from the issue text.

Assessment

Tech stack
java
Domain
tooling
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.