From 16dd9e95452b00a91278b81e723301de9868cf7f Mon Sep 17 00:00:00 2001 From: Greg Schueler Date: Thu, 2 Feb 2017 10:26:52 -0800 Subject: [PATCH] Fix #68 improve projects list output --- .../rundeck/client/api/model/ProjectItem.java | 13 +++++ .../client/tool/commands/Projects.java | 43 ++++++++++++-- .../options/ProjectListFormatOptions.java | 19 ++++++ .../client/tool/commands/ProjectsSpec.groovy | 58 +++++++++++++++++++ 4 files changed, 127 insertions(+), 6 deletions(-) create mode 100644 src/main/java/org/rundeck/client/tool/options/ProjectListFormatOptions.java create mode 100644 src/test/groovy/org/rundeck/client/tool/commands/ProjectsSpec.groovy diff --git a/src/main/java/org/rundeck/client/api/model/ProjectItem.java b/src/main/java/org/rundeck/client/api/model/ProjectItem.java index c71e1466..8cb33425 100644 --- a/src/main/java/org/rundeck/client/api/model/ProjectItem.java +++ b/src/main/java/org/rundeck/client/api/model/ProjectItem.java @@ -3,6 +3,8 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; +import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.Map; /** @@ -49,6 +51,17 @@ public void setConfig(Map config) { this.config = config; } + public Map toMap() { + + HashMap detail = new LinkedHashMap<>(); + detail.put("name", getName()); + detail.put("description", description != null && !"".equals(description.trim()) ? description : ""); + detail.put("url", getUrl()); + if (null != getConfig()) { + detail.put("config", getConfig()); + } + return detail; + } public String toBasicString() { return String.format( "%s%s", diff --git a/src/main/java/org/rundeck/client/tool/commands/Projects.java b/src/main/java/org/rundeck/client/tool/commands/Projects.java index 4227e1e6..f19fe6e5 100644 --- a/src/main/java/org/rundeck/client/tool/commands/Projects.java +++ b/src/main/java/org/rundeck/client/tool/commands/Projects.java @@ -12,14 +12,14 @@ import org.rundeck.client.tool.commands.projects.ACLs; import org.rundeck.client.tool.commands.projects.Readme; import org.rundeck.client.tool.commands.projects.SCM; -import org.rundeck.client.tool.options.OptionUtil; -import org.rundeck.client.tool.options.ProjectCreateOptions; -import org.rundeck.client.tool.options.ProjectNameOptions; +import org.rundeck.client.tool.options.*; +import org.rundeck.client.util.Format; import java.io.IOException; import java.util.Arrays; import java.util.List; import java.util.Map; +import java.util.function.Function; import java.util.stream.Collectors; @@ -41,11 +41,42 @@ public List getSubCommands() { ); } + interface ProjectResultOptions extends ProjectListFormatOptions, VerboseOption { + + } + + @CommandLineInterface(application = "list") interface ProjectListOpts extends ProjectResultOptions { + + } + @Command(isDefault = true, description = "List all projects. (no options.)") - public void list(CommandOutput output) throws IOException, InputError { + public void list(ProjectListOpts opts, CommandOutput output) throws IOException, InputError { List body = apiCall(RundeckApi::listProjects); - output.info(String.format("%d Projects:%n", body.size())); - output.output(body.stream().map(ProjectItem::toBasicString).collect(Collectors.toList())); + if (!opts.isOutputFormat()) { + output.info(String.format("%d Projects:%n", body.size())); + } + + outputProjectList(opts, output, body); + } + + private void outputProjectList( + final ProjectResultOptions options, + final CommandOutput output, + final List body + ) + { + final Function outformat; + if (options.isVerbose()) { + output.output(body.stream().map(ProjectItem::toMap).collect(Collectors.toList())); + return; + } + if (options.isOutputFormat()) { + outformat = Format.formatter(options.getOutputFormat(), ProjectItem::toMap, "%", ""); + } else { + outformat = ProjectItem::getName; + } + + output.output(body.stream().map(outformat).collect(Collectors.toList())); } @CommandLineInterface(application = "delete") interface ProjectDelete extends ProjectNameOptions { diff --git a/src/main/java/org/rundeck/client/tool/options/ProjectListFormatOptions.java b/src/main/java/org/rundeck/client/tool/options/ProjectListFormatOptions.java new file mode 100644 index 00000000..50031ced --- /dev/null +++ b/src/main/java/org/rundeck/client/tool/options/ProjectListFormatOptions.java @@ -0,0 +1,19 @@ +package org.rundeck.client.tool.options; + +import com.lexicalscope.jewel.cli.Option; + +/** + * @author greg + * @since 2/2/17 + */ +public interface ProjectListFormatOptions { + + @Option(shortName = "%", + longName = "outformat", + description = "Output format specifier for project info. You can use \"%key\" where key is one of:" + + "name, description, url, config, config.KEY. E.g. \"%name: " + + "%description\".") + String getOutputFormat(); + + boolean isOutputFormat(); +} diff --git a/src/test/groovy/org/rundeck/client/tool/commands/ProjectsSpec.groovy b/src/test/groovy/org/rundeck/client/tool/commands/ProjectsSpec.groovy new file mode 100644 index 00000000..17757fc6 --- /dev/null +++ b/src/test/groovy/org/rundeck/client/tool/commands/ProjectsSpec.groovy @@ -0,0 +1,58 @@ +package org.rundeck.client.tool.commands + +import com.simplifyops.toolbelt.CommandOutput +import org.rundeck.client.api.RundeckApi +import org.rundeck.client.api.model.ProjectItem +import org.rundeck.client.api.model.ScheduledJobItem +import org.rundeck.client.tool.RdApp +import org.rundeck.client.util.Client +import retrofit2.Retrofit +import retrofit2.mock.Calls +import spock.lang.Specification + +/** + * @author greg + * @since 2/2/17 + */ +class ProjectsSpec extends Specification { + + def "projects list outformat"() { + given: + + def api = Mock(RundeckApi) + def opts = Mock(Projects.ProjectListOpts) { + getOutputFormat() >> outFormat + isOutputFormat() >> (outFormat != null) + } + + def retrofit = new Retrofit.Builder().baseUrl('http://example.com/fake/').build() + def client = new Client(api, retrofit, 18) + def hasclient = Mock(RdApp) { + getClient() >> client + } + Projects projects = new Projects(hasclient) + def out = Mock(CommandOutput) + + when: + projects.list(opts, out) + + then: + 1 * api.listProjects() >> + Calls.response( + [new ProjectItem(name: 'abc', description: '123', config: [xyz: '993']), new ProjectItem( + name: 'def', + description: '' + )] + ) + 1 * out.output(result) + + + where: + outFormat | result + null | ['abc', 'def'] + '%name' | ['abc', 'def'] + '%name/%description' | ['abc/123', 'def/'] + '%name/%config' | ['abc/{xyz=993}', 'def/'] + '%name/%config.xyz' | ['abc/993', 'def/'] + } +}