Skip to content

refactor(iam): split compileIamRole into Strategy pattern with per-service modules #707

@VirtueMe

Description

@VirtueMe

Summary

compileIamRole.js has grown to ~1000 lines with 30+ functions, and its test file compileIamRole.test.js is now ~4700 lines. Both files are difficult to navigate and maintain as new AWS service integrations are added over time.

This is part of a broader refactoring roadmap tracked in #708.

We'd like to propose a refactor that splits this into a clean Strategy pattern, where each AWS service integration lives in its own focused module.

Proposed Structure

lib/deploy/stepFunctions/
  iamRole/
    index.js          ← compileIamRole() entry point, consolidation, getIamStatements
    dispatcher.js     ← iterates strategies, calls matches() + getPermissions()
    utils.js          ← shared helpers (isJsonPathParameter, isJsonataArgument, getTaskStates, etc.)
    strategies/
      sns.js
      sqs.js
      dynamodb.js
      redshift.js
      lambda.js
      stepFunctions.js
      s3.js
      batchGlueEcs.js
      services.js     ← CodeBuild, SageMaker, Bedrock, EventBridge, HTTP, Scheduler

Each strategy exports two functions:

  • matches(resource) — returns true if this strategy handles the given resource ARN pattern
  • getPermissions(state) — returns the IAM permission objects for this service

The dispatcher replaces the current large if/else if chain in getIamPermissions():

const strategies = [
  require('./strategies/sqs'),
  require('./strategies/sns'),
  // ...
];

function getIamPermissions(state) {
  const strategy = strategies.find(s => s.matches(state.Resource));
  return strategy ? strategy.getPermissions(state) : [];
}

Tests mirror the source structure exactly, so strategies/dynamodb.js is covered by strategies/dynamodb.test.js etc.

Benefits

  • Easier to navigate — finding the code for a specific service integration is obvious
  • Easier to maintain — changes to one service can't accidentally break another
  • Easier to extend — adding a new AWS service integration means dropping in a new strategy file with no changes to existing code
  • Easier to review — PRs for new service integrations are focused and small
  • Better test isolation — test failures point directly to the affected service module

Public interface

No changes to the plugin's public API or configuration. index.js continues to export compileIamRole() and is mixed in the same way as today.

Dependency on pending PRs

We have several open PRs that touch compileIamRole.js and compileIamRole.test.js. We'd like those to be merged before starting this refactor so the restructured code includes all current fixes:

Question for maintainers

Are you open to this refactor? We're happy to implement it incrementally (e.g. migrate one strategy at a time) or in a single PR — whichever you prefer.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions