Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[#442] account for lists in YamlUtils#loadYaml #443

Merged
merged 1 commit into from
Oct 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import com.boozallen.aissemble.upgrade.migration.AbstractAissembleMigration;
import org.apache.commons.lang3.StringUtils;
import org.apache.maven.model.Dependency;
import org.apache.maven.model.DependencyManagement;
import org.apache.maven.model.InputLocation;
import org.apache.maven.model.InputLocationTracker;
import org.apache.maven.model.Model;
Expand Down Expand Up @@ -86,10 +87,13 @@ private List<Dependency> getDeltaCoreDependenciesForProject(Model model) {

private List<Dependency> getDeltaCoreDependencies(ModelBase model) {
List<Dependency> deltaCoreDependencies = new ArrayList<>();
model.getDependencyManagement().getDependencies()
.stream()
.filter(DeltaSparkPomMigration::isDeltaCore)
.forEach(deltaCoreDependencies::add);
DependencyManagement dependencyManagement = model.getDependencyManagement();
if (dependencyManagement != null) {
dependencyManagement.getDependencies()
.stream()
.filter(DeltaSparkPomMigration::isDeltaCore)
.forEach(deltaCoreDependencies::add);
}
model.getDependencies()
.stream()
.filter(DeltaSparkPomMigration::isDeltaCore)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.LoaderOptions;
import org.yaml.snakeyaml.constructor.Constructor;
import org.yaml.snakeyaml.error.YAMLException;
import org.yaml.snakeyaml.introspector.Property;
import org.yaml.snakeyaml.introspector.PropertyUtils;
import org.yaml.snakeyaml.representer.Representer;
Expand All @@ -40,13 +41,31 @@ public class YamlUtils {
private static final String SPACE = " ";
private static final int TAB = 2;

/**
* Reads the YAML file into a {@link YamlObject}. If the file contains a list at the root level, then the returned
* YamlObject will have one entry under the key "items" that is the resulting list. Multiple objects separated by
* dashes is not supported.
*
* @param file a YAML file to parse
* @return the {@link YamlObject} represented by the file
* @throws IOException if the file cannot be read
* @throws YAMLException if the file fails to parse to a valid YamlObject
*/
public static YamlObject loadYaml(File file) throws IOException {
Yaml yaml = new Yaml();
try (InputStream fileStream = Files.asByteSource(file).openStream()) {
Map<String, Object> contents = yaml.load(fileStream);
//If the file is empty or only contains comments, contents will be null
if (contents == null) {
Object data = yaml.load(fileStream);
Map<String, Object> contents;
//If the file is empty or only contains comments, data will be null
if (data == null) {
contents = Map.of();
} else if (data instanceof Map) {
//noinspection unchecked
contents = (Map<String, Object>) data;
} else if (data instanceof List) {
contents = Map.of("items", data);
} else {
throw new YAMLException("Unrecognized root data type [" + data.getClass().getName() + "] in file: " + file.getAbsolutePath());
}
return new YamlObject(contents);
}
Expand Down Expand Up @@ -78,23 +97,23 @@ public String getString(String... path) {
}

public boolean hasInt(String... path) {
return hasValue(int.class, path);
return hasValue(Integer.class, path);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I: these were returning false when they shouldn't have as the map has the boxed version of the value so isInstance was failing.

}

public int getInt(String... path) {
return getValue(path);
}

public boolean hasDouble(String... path) {
return hasValue(double.class, path);
return hasValue(Double.class, path);
}

public double getDouble(String... path) {
return getValue(path);
}

public boolean hasBoolean(String... path) {
return hasValue(boolean.class, path);
return hasValue(Boolean.class, path);
}

public boolean getBoolean(String... path) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,8 @@
"implementation": "com.boozallen.aissemble.upgrade.migration.v1_10_0.DeltaSparkYamlMigration",
"fileSets": [
{
"includes": ["**/*.yaml"]
"includes": ["**/*.yaml"],
"excludes": ["**/target/**", "**/templates/**"]
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I: Not strictly necessary but this cuts down significantly on the number of output lines talking about un-parseable helm templates.

}
]
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package com.boozallen.aissemble.upgrade.migration.utils;

/*-
* #%L
* aiSSEMBLE::Foundation::Upgrade
* %%
* Copyright (C) 2021 Booz Allen
* %%
* This software package is licensed under the Booz Allen Public License. All Rights Reserved.
* #L%
*/

import com.boozallen.aissemble.upgrade.util.YamlUtils;
import io.cucumber.java.en.Then;
import io.cucumber.java.en.Given;
import io.cucumber.java.en.When;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

public class YamlUtilsSteps {

private Path testFile;
private YamlUtils.YamlObject yaml;

@Given("a YAML file:")
public void aYamlFile(String contents) throws IOException {
testFile = Files.createTempFile(Path.of("target"), "yaml-util-test", "");
Files.writeString(testFile, contents);
}

@When("the file is loaded")
public void theFileIsLoaded() throws IOException {
yaml = YamlUtils.loadYaml(testFile.toFile());
}

@Then("the string value of the property {string} is {string}")
public void theStringValueOfThePropertyIs(String name, String value) {
String type = yaml.get(name).getClass().getSimpleName();
assertTrue("Property was not loaded as string: " + name + " (was " + type + ")", yaml.hasString(name));
assertEquals("Property value does not match for: " + name, value, yaml.getString(name));
}

@Then("the decimal value of the property {string} is {double}")
public void theDecimalValueOfThePropertyIs(String name, double value) {
String type = yaml.get(name).getClass().getSimpleName();
assertTrue("Property was not loaded as double: " + name + " (was " + type + ")", yaml.hasDouble(name));
assertEquals("Property value does not match for: " + name, value, yaml.getDouble(name), 0.01);
Copy link
Contributor

Choose a reason for hiding this comment

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

Q: 0.01?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's a delta param that they added to floating point assertions. Avoids the tricky floating point rounding issues where you get 0.000000000001 or some other minuscule difference just from how FP is represented. So this says treat the numbers as equal if they are within 0.01 of each other.

}

@Then("the integer value of the property {string} is {int}")
public void theIntegerValueOfThePropertyIs(String name, int value) {
String type = yaml.get(name).getClass().getSimpleName();
assertTrue("Property was not loaded as int: " + name + " (was " + type + ")", yaml.hasInt(name));
assertEquals("Property value does not match for: " + name, value, yaml.getInt(name));
}

@Then("the boolean value of the property {string} is {string}")
public void theBooleanValueOfThePropertyIs(String name, String value) {
String type = yaml.get(name).getClass().getSimpleName();
assertTrue("Property was not loaded as boolean: " + name + " (was " + type + ")", yaml.hasBoolean(name));
assertEquals("Property value does not match for: " + name, Boolean.valueOf(value), yaml.getBoolean(name));
}

@Then("the property {string} is an object")
public void thePropertyIsAnObject(String name) {
String type = yaml.get(name).getClass().getSimpleName();
assertTrue("Property was not loaded as object: " + name + " (was " + type + ")", yaml.hasObject(name));
}

@Then("the decimal value of the property {string} of the Object {string} is {double}")
public void theDecimalValueOfThePropertyOfTheObjectIs(String prop, String obj, double value) {
assertTrue("Property was not loaded as double: " + obj + "." + prop, yaml.hasDouble(obj, prop));
assertEquals("Property value does not match for: " + obj + "." + prop, value, yaml.getDouble(obj, prop), 0.01);
}

@Then("the size of the list {string} is {int}")
public void theSizeOfTheListIs(String name, int size) {
String type = yaml.get(name).getClass().getSimpleName();
assertTrue("Property was not loaded as list: " + name + " (was " + type + ")", yaml.hasList(name));
assertEquals("Property value does not match for: " + name, size, yaml.getList(name).size());
}

@Then("the {string} of item {int} of the list {string} is {int}")
public void theIntegerValueOfItemOfTheListIs(String prop, int index, String list, int value) {
List<YamlUtils.YamlObject> objects = yaml.getListOfObjects(list);
YamlUtils.YamlObject object = objects.get(index);
String key = list + "[" + index + "]." + prop;
assertEquals("Property value does not match for: " + key, value, object.getInt(prop));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ public void aPOMThatReferencesTheDeltaCorePackage() {
testFile = getTestFile("v1_10_0/DeltaSparkPomMigration/migration/pom.xml");
}

@Given("a POM that references the delta-core package that does not have dependencyManagement")
public void aPOMThatReferencesTheDeltaCorePackageThatDoesNotHaveDependencyManagement() {
testFile = getTestFile("v1_10_0/DeltaSparkPomMigration/migration/no-mgmt.xml");
}

@When("the 1.10.0 DeltaSpark yaml migration executes")
public void theDeltaSparkYamlMigrationExecutes() {
DeltaSparkYamlMigration migration = new DeltaSparkYamlMigration();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,8 @@ Feature: Migrate Delta Lake Spark dependencies
Given a POM that references the delta-core package
When the 1.10.0 DeltaSpark POM migration executes
Then delta-core is updated to delta-spark and the version is set to the version.delta property

Scenario: POM dependencies are migrated without dependency management
Given a POM that references the delta-core package that does not have dependencyManagement
When the 1.10.0 DeltaSpark POM migration executes
Then delta-core is updated to delta-spark and the version is set to the version.delta property
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
Feature: Utilities -> YAML

Scenario: I can load a YAML file with an object
Given a YAML file:
"""yaml
name: test
size: 7.5
count: 12
complete: True
nested: {
size: 2.0
}
multiple:
- intvalue: 5
- intvalue: 7
"""
When the file is loaded
Then the string value of the property "name" is "test"
And the decimal value of the property "size" is 7.5
And the integer value of the property "count" is 12
And the boolean value of the property "complete" is "true"
And the property "nested" is an object
And the decimal value of the property "size" of the Object "nested" is 2.0
And the size of the list "multiple" is 2
And the "intvalue" of item 1 of the list "multiple" is 7

Scenario: I can load a YAML file with a list
Given a YAML file:
"""yaml
- red
- blue
- yellow
"""
When the file is loaded
Then the size of the list "items" is 3
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<!--
#%L
aiSSEMBLE::Foundation::Upgrade
%%
Copyright (C) 2021 Booz Allen
%%
This software package is licensed under the Booz Allen Public License. All Rights Reserved.
#L%
-->
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<groupId>org.test</groupId>
<artifactId>test-pom</artifactId>
<version>1.0.0-SNAPSHOT</version>

<dependencies>
<dependency>
<groupId>io.delta</groupId>
<artifactId>delta-core_2.13</artifactId>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<!--
#%L
aiSSEMBLE::Foundation::Upgrade
%%
Copyright (C) 2021 Booz Allen
%%
This software package is licensed under the Booz Allen Public License. All Rights Reserved.
#L%
-->
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<groupId>org.test</groupId>
<artifactId>test-pom</artifactId>
<version>1.0.0-SNAPSHOT</version>

<dependencies>
<dependency>
<groupId>io.delta</groupId>
<artifactId>delta-spark_2.13</artifactId>
<version>${version.delta}</version>
</dependency>
</dependencies>
</project>