Skip to content

SED-4539 automation package collection and accessor#615

Open
cmisev wants to merge 28 commits intomasterfrom
SED-4539-automation-package-collection-and-accessor
Open

SED-4539 automation package collection and accessor#615
cmisev wants to merge 28 commits intomasterfrom
SED-4539-automation-package-collection-and-accessor

Conversation

@cmisev
Copy link
Copy Markdown

@cmisev cmisev commented Mar 19, 2026

No description provided.

@gemini-code-assist
Copy link
Copy Markdown

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a robust framework for managing automation package YAML files programmatically, particularly focusing on plans. By providing collection-like access and precise YAML patching capabilities, it lays the groundwork for more sophisticated tooling, such as IDE integrations, that can manipulate automation package definitions while preserving their integrity and structure.

Highlights

  • New Module Introduction: A new Maven module, step-automation-packages-ide, has been introduced to house functionalities related to IDE integration for automation packages.
  • YAML Fragment Management: A new AutomationPackageYamlFragmentManager class has been implemented to centralize the management of YAML fragments within automation packages, enabling programmatic saving, modification, and removal of entities like plans while preserving YAML structure.
  • Collection-like Access for Plans: New AutomationPackageCollectionFactory and AutomationPackagePlanCollection classes provide a collection-like interface for interacting with plans defined in automation package YAML files, facilitating CRUD operations.
  • Enhanced YAML Deserialization for Patching: The YAML deserialization process has been significantly enhanced with PatchableYamlArtefact, PatchingParserDelegate, and PatchableYamlArtefactDeserializer to capture precise source location information, which is crucial for making targeted modifications to YAML content.
  • Refactoring of YAML Model and Reader: Core YAML model classes (AbstractAutomationPackageFragmentYaml, AutomationPackageDescriptorYamlImpl, AutomationPackageFragmentYaml) and the AutomationPackageDescriptorReader have been refactored to support the new fragment management and deserialization capabilities, including Jackson injection for dependencies and dynamic handling of additional fields.
  • Comprehensive Testing: Extensive unit tests have been added for the new collection and fragment management features, covering various scenarios such as reading, modifying, renaming, adding, and removing plans within YAML files.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a new step-automation-packages-ide module, which provides a collection-based accessor for automation packages, particularly for plans stored in YAML. This is a significant feature that allows treating file-based plans as if they were in a database collection, with support for reading, creating, updating, and deleting. The implementation cleverly uses YAML patching to preserve formatting and comments, which is a great approach. The changes are extensive and well-tested.

My review has identified a few areas for improvement. The primary concern is the exception handling strategy in several new classes, where checked exceptions are wrapped in generic RuntimeException. This can obscure error details and should be addressed. I also found a bug in YamlPlainTextPlan where an incorrect collection name is returned. Additionally, there are a couple of minor code hygiene issues that I've pointed out.

@cmisev cmisev marked this pull request as draft March 19, 2026 09:57
@cmisev cmisev marked this pull request as ready for review March 20, 2026 10:55
@cmisev cmisev requested a review from cl-exense March 20, 2026 10:56
@cmisev cmisev changed the title Sed 4539 automation package collection and accessor SED-4539 automation package collection and accessor Mar 20, 2026
Copy link
Copy Markdown
Contributor

@cl-exense cl-exense left a comment

Choose a reason for hiding this comment

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

Looks mostly fine!

Don't be scared by the number of comments, this is actually a pretty good PR, and the requested changes are mostly simple.

Note that I have not understood all of the logic in deep detail, but the overall concept, and I've focused mostly on code understandability rather than implementation details.

private final AutomationPackageYamlFragmentManager fragmentManager;

public AutomationPackagePlanCollection(AutomationPackageYamlFragmentManager fragmentManager) {
super(true, "plans");
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.

These kinds of names should always be declared as public static final String constants, instead of using magic literal Strings.

public AutomationPackagePlanCollection(AutomationPackageYamlFragmentManager fragmentManager) {
super(true, "plans");
this.fragmentManager = fragmentManager;
fragmentManager.getPlans().forEach(super::save);
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.

This is some unexpected logic here. Add a comment on why it is needed.


@Override
public void remove(Filter filter) {
find(filter, null, null, null, Integer.MAX_VALUE).forEach(fragmentManager::removePlan);
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.

Even if the maxTime isn't actually used in the InMemoryCollection implementation - use 0 to indicate no limit as in all other invocations across the codebase.

List<Plan> plans = planCollection.find(Filters.empty(), null, null, null, 100).collect(Collectors.toList());

assertEquals(2, count);
Set<String> names = plans.stream().map(p -> p.getAttributes().get("name")).collect(Collectors.toUnmodifiableSet());
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.

These are just test classes, so it's less critical, but: prefer to use constants instead of repeatedly using magic strings. "name" is AbstractOrganizableObject.NAME. This also applies to all other such occurrences.


planCollection.save(plan);

Sequence sequence = new Sequence();
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.

This block is repeated many times in the test class (Sequence, plan, plan name). Consider refactoring it into a helper method. This will make the individual tests more focused and more readable.

private int startOffset = -1;

@JsonIgnore
private int startColumn = -1;
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.

idem


abstract public boolean hasAutomationPackageDescriptor();

abstract public URL getDescriptorYamlUrl();
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.

Heads up: this method is not implemented in the DotNetAutomationPackageArchive, it's therefore currently not possible to build an enterprise distribution for this branch.

Judging from the other methods, I guess the correct implementation there is simply

    @Override
    public URL getDescriptorYamlUrl() {
        throw new UnsupportedOperationException(NOT_SUPPORTED_WITH_DOT_NET_DLL_AUTOMATION_PACKAGE);
    }

:-)

}

@Override
public InputStream getDescriptorYaml() {
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.

Since this method and the new getDescriptorYamlUrl are practivally identical, it should actually be possible to implement the getDescriptorYaml() in terms of getDescriptorYamlUrl(), for instance:

@Override
public InputStream getDescriptorYaml() {
    URL url = getDescriptorYamlUrl();
    if (url == null) {
        return null;
    }
    try {
        return url.openStream();
    } catch (IOException e) {
        return null;
    }
}

This could be directly done in the base class, eliminating the need for separate implementations in derived classes.

* Scans and returns all {@link StepYamlDeserializer} classes annotated with {@link StepYamlDeserializerAddOn}
*/
public static List<DeserializerBind<?>> scanDeserializerAddons(ObjectMapper yamlObjectMapper, List<Consumer<StepYamlDeserializer<?>>> configurators) {
public static List<DeserializerBind<?>> scanDeserializerAddons(ObjectMapper yamlObjectMapper) {
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.

I'm all for simplification, yay! But why is this not needed anymore?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

This was only used for one modification of a deserializer, to set a serializer registry to the YamlAutomationPackageFragmentDeserializer.java.

On multiple levels, Jackson provides neat design patterns for the use case in question:

  • any singleton can be made accessible in a deserializer by using injectables and ctxt.findInjectableValue(...)
  • should any different modification of a deserializer be.necessary during initialization, there is already an example of that in AutomationPackageDescriptorReader.java, context.addBeanDeserializerModifier(...)
  • In the particular example, the serializer awareness in the (now removed YamlAutomationPackageFragmentDeserializer.java) was only needed to do the custom handling of the additonalFields map. For such cases, Jackson equally has a design pattern mean for this, it is the custom @JsonAnySetter in AbstractAutomationPackageFragmentYaml.java

public void setCategories(List<String> categories) {
this.categories = categories;
}
public void setCategories(List<String> categories) {
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.

Please make sure that your IntelliJ has the "use editorconfig" setting enabled and obeys it (and that you're reformatting files if necessary before commit).

For some reason, you have reformatted these lines with tabs instead of 4 spaces, which is against the coding guidelines and, more importantly, messes up the commit history. (Correctly reformatting again now will fix it as the squash-merge at the end of the PR should be fine)

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.

2 participants