Skip to content

Commit

Permalink
Merge pull request #53 from electronicarts/master
Browse files Browse the repository at this point in the history
[JENKINS-53053] Implement pipeline steps for read / writing CSV files
  • Loading branch information
rsandell authored Feb 1, 2019
2 parents 0bab806 + 11be06f commit 113be3b
Show file tree
Hide file tree
Showing 16 changed files with 1,028 additions and 0 deletions.
2 changes: 2 additions & 0 deletions docs/STEPS.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
* `writeYaml` - Write a [YAML](http://yaml.org) file from an object. ([help](../src/main/resources/org/jenkinsci/plugins/pipeline/utility/steps/conf/WriteYamlStep/help.html))
* `readJSON` - Read [JSON](http://www.json.org/json-it.html) from files in the workspace or text. ([help](../src/main/resources/org/jenkinsci/plugins/pipeline/utility/steps/json/ReadJSONStep/help.html))
* `writeJSON` - Write a [JSON](http://www.json.org/json-it.html) object to a files in the workspace. ([help](../src/main/resources/org/jenkinsci/plugins/pipeline/utility/steps/json/WriteJSONStep/help.html))
* `readCSV` - Read [CSV](https://commons.apache.org/proper/commons-csv/) from files in the workspace or text. ([help](../src/main/resources/org/jenkinsci/plugins/pipeline/utility/steps/csv/ReadCSVStep/help.html))
* `writeCSV` - Write a [CSV](https://commons.apache.org/proper/commons-csv/) file from an object. ([help](../src/main/resources/org/jenkinsci/plugins/pipeline/utility/steps/csv/WriteCSVStep/help.html))

#### Maven Projects
* `readMavenPom` - Read a [Maven Project](https://maven.apache.org/pom.html) into a [Model](http://maven.apache.org/components/ref/3.3.9/maven-model/apidocs/org/apache/maven/model/Model.html) data structure. ([help](../src/main/resources/org/jenkinsci/plugins/pipeline/utility/steps/maven/ReadMavenPomStep/help.html))
Expand Down
5 changes: 5 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,11 @@
<classifier>tests</classifier>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-csv</artifactId>
<version>1.5</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
/*
* The MIT License
*
* Copyright (C) 2018 Electronic Arts Inc. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.jenkinsci.plugins.pipeline.utility.steps.csv;

import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVRecord;
import hudson.Extension;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
import javax.annotation.Nonnull;
import org.codehaus.groovy.control.customizers.ImportCustomizer;
import org.jenkinsci.plugins.pipeline.utility.steps.AbstractFileOrTextStep;
import org.jenkinsci.plugins.pipeline.utility.steps.AbstractFileOrTextStepDescriptorImpl;
import org.jenkinsci.plugins.scriptsecurity.sandbox.Whitelist;
import org.jenkinsci.plugins.workflow.cps.CpsFlowExecution;
import org.jenkinsci.plugins.workflow.cps.GroovyShellDecorator;
import org.jenkinsci.plugins.workflow.steps.StepContext;
import org.jenkinsci.plugins.workflow.steps.StepExecution;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.DataBoundSetter;

/**
*
* @author Stuart Rowe
*/
public class ReadCSVStep extends AbstractFileOrTextStep {

private static final String ORG_APACHE_COMMONS_CSV = "org.apache.commons.csv";
private CSVFormat format;

@DataBoundConstructor
public ReadCSVStep() {
this.format = CSVFormat.DEFAULT;
}

public CSVFormat getFormat() {
return this.format;
}

@DataBoundSetter
public void setFormat(CSVFormat format) {
this.format = format;
}

@Override
public StepExecution start(StepContext context) throws Exception {
return new ReadCSVStepExecution(this, context);
}

@Extension
public static class DescriptorImpl extends AbstractFileOrTextStepDescriptorImpl {

public DescriptorImpl() {

}

@Override
public String getFunctionName() {
return "readCSV";
}

@Override
@Nonnull
public String getDisplayName() {
return Messages.ReadCSVStep_DescriptorImpl_displayName();
}
}

/**
* Auto imports org.apache.commons.csv.* .
*/
@Extension(optional = true)
public static class PackageAutoImporter extends GroovyShellDecorator {
@Override
public void customizeImports(CpsFlowExecution context, ImportCustomizer ic) {
ic.addStarImports(ORG_APACHE_COMMONS_CSV);
}
}

/**
* Whitelists various non static setters, getters, constructors and static fields
* for the CSVFormat and CSVRecord classes in the org.apache.commons.csv package.
* Specifically:
* - CSVFormat: all static fields; static valueOf and newFormat methods; with* methods;
* - CSVRecord: all static fields; all methods;
*/
@Extension
public static class WhiteLister extends Whitelist {
public WhiteLister() {
super();
}

@Override
public boolean permitsMethod(Method method, Object receiver, Object[] args) {
if (receiver == null) {
return false;
}

final Class<?> aClass = receiver.getClass();
final Package aPackage = aClass.getPackage();

if(aPackage == null) {
return false;
}

if (!aPackage.getName().equals(ORG_APACHE_COMMONS_CSV)) {
return false;
}

if (aClass == CSVFormat.class) {
return method.getName().startsWith("with");
} else if (aClass == CSVRecord.class) {
return true;
}

return false;
}

@Override
public boolean permitsConstructor(@Nonnull Constructor<?> constructor, @Nonnull Object[] args) {
return false;
}

@Override
public boolean permitsStaticMethod(@Nonnull Method method, @Nonnull Object[] args) {
final Class<?> aClass = method.getDeclaringClass();
final Package aPackage = aClass.getPackage();

if (aPackage == null) {
return false;
}

if (!aPackage.getName().equals(ORG_APACHE_COMMONS_CSV)) {
return false;
}

if (aClass == CSVFormat.class) {
return (method.getName().equals("newFormat") || method.getName().equals("valueOf"));
}

return false;
}

@Override
public boolean permitsFieldGet(@Nonnull Field field, @Nonnull Object receiver) {
return false;
}

@Override
public boolean permitsFieldSet(@Nonnull Field field, @Nonnull Object receiver, Object value) {
return false;
}

@Override
public boolean permitsStaticFieldGet(@Nonnull Field field) {
final Class<?> aClass = field.getDeclaringClass();
final Package aPackage = aClass.getPackage();

if (aPackage == null) {
return false;
}

if (!aPackage.getName().equals(ORG_APACHE_COMMONS_CSV)) {
return false;
}

if (aClass == CSVFormat.class) {
return true;
}

return false;
}

@Override
public boolean permitsStaticFieldSet(@Nonnull Field field, Object value) {
return false;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/*
* The MIT License
*
* Copyright (C) 2018 Electronic Arts Inc. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.jenkinsci.plugins.pipeline.utility.steps.csv;

import hudson.FilePath;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Nonnull;
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVParser;
import org.apache.commons.csv.CSVRecord;
import org.jenkinsci.plugins.pipeline.utility.steps.AbstractFileOrTextStepExecution;
import org.jenkinsci.plugins.workflow.steps.StepContext;

import static org.apache.commons.lang.StringUtils.isNotBlank;


/**
*
* @author Stuart Rowe
*/
public class ReadCSVStepExecution extends AbstractFileOrTextStepExecution<List<CSVRecord>> {
private static final long serialVersionUID = 1L;

private final transient ReadCSVStep step;

protected ReadCSVStepExecution(@Nonnull ReadCSVStep step, @Nonnull StepContext context) {
super(step, context);
this.step = step;
}

@Override
protected List<CSVRecord> doRun() throws Exception {
String fName = step.getDescriptor().getFunctionName();
if (isNotBlank(step.getFile()) && isNotBlank(step.getText())) {
throw new IllegalArgumentException(Messages.ReadCSVStepExecution_tooManyArguments(fName));
}
Reader reader = null;
if (isNotBlank(step.getFile())) {
FilePath f = ws.child(step.getFile());
if (f.exists() && !f.isDirectory()) {
reader = new InputStreamReader(f.read(), StandardCharsets.UTF_8);
}
}

if (isNotBlank(step.getText())) {
reader = new StringReader(step.getText());
}

List<CSVRecord> records = new ArrayList<>();
if (reader != null) {
CSVFormat format = step.getFormat();
if (format == null) {
format = CSVFormat.DEFAULT;
}

CSVParser parser = format.parse(reader);
records.addAll(parser.getRecords());
}
return records;
}
}
Loading

0 comments on commit 113be3b

Please sign in to comment.