SED-4539 automation package collection and accessor#615
SED-4539 automation package collection and accessor#615
Conversation
Summary of ChangesHello, 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
Using Gemini Code AssistThe 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
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 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
|
There was a problem hiding this comment.
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.
...omation-packages-manager/src/main/java/step/automation/packages/AutomationPackageReader.java
Show resolved
Hide resolved
...s-yaml/src/main/java/step/automation/packages/yaml/AutomationPackageYamlFragmentManager.java
Outdated
Show resolved
Hide resolved
step-plans/step-plans-parser/src/main/java/step/plans/automation/YamlPlainTextPlan.java
Outdated
Show resolved
Hide resolved
...ion-packages-manager/src/main/java/step/automation/packages/JavaAutomationPackageReader.java
Outdated
Show resolved
Hide resolved
...yaml/src/main/java/step/automation/packages/yaml/deserialization/PatchingParserDelegate.java
Outdated
Show resolved
Hide resolved
cl-exense
left a comment
There was a problem hiding this comment.
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"); |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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()); |
There was a problem hiding this comment.
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(); |
There was a problem hiding this comment.
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; |
|
|
||
| abstract public boolean hasAutomationPackageDescriptor(); | ||
|
|
||
| abstract public URL getDescriptorYamlUrl(); |
There was a problem hiding this comment.
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() { |
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
I'm all for simplification, yay! But why is this not needed anymore?
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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)
No description provided.