Skip to content
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
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,9 @@ AWS_PROFILE=myprofile ./mvnw spring-boot:run
This project suffers from well-known problems with Intellij's terminal and jline. Most notably, normal
input controls don't work, and tab completion is broken: https://youtrack.jetbrains.com/issue/IDEA-183619

`rm` supports -f / --force, but Spring Shell doesn't seem to be able to handle a boolean flag without args combined with
standalone args, so one has to specify `rm -f true <param>`.
`rm` supports -f / --force, but Spring Shell doesn't seem to be able to handle a boolean flag without an arg except at
the end of the command line. Example: `rm foo --force` works, but `rm --force foo` doesn't.
See: https://github.com/spring-projects/spring-shell/issues/1262

## TODO

Expand Down
29 changes: 23 additions & 6 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,14 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.5.7</version>
<version>4.0.2</version>
</parent>

<properties>
<maven.compiler.release>25</maven.compiler.release>
<lombok.version>1.18.42</lombok.version>
<aws.sdk.version>2.41.0</aws.sdk.version>
<aws.sdk.version>2.41.19</aws.sdk.version>
<spring-shell.version>4.0.1</spring-shell.version>
</properties>

<dependencyManagement>
Expand Down Expand Up @@ -61,21 +62,37 @@
</dependency>
<dependency>
<groupId>org.springframework.shell</groupId>
<artifactId>spring-shell-starter</artifactId>
<version>3.4.1</version>
<artifactId>spring-shell-starter-ffm</artifactId>
<version>${spring-shell.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.21.0</version>
</dependency>
<dependency>
<groupId>org.springframework.shell</groupId>
<artifactId>spring-shell-test-autoconfigure</artifactId>
<version>${spring-shell.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.awaitility</groupId>
<artifactId>awaitility</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId>
<artifactId>testcontainers-junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>localstack</artifactId>
<artifactId>testcontainers-localstack</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,9 @@
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.shell.command.annotation.CommandScan;
import software.amazon.awssdk.services.ssm.SsmClient;

@SpringBootApplication
@CommandScan
class ParameterStoreApp {

public static void main(String[] args) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,18 @@

import com.internetstaff.parameterstore.application.port.out.ParameterStore;
import lombok.RequiredArgsConstructor;
import org.springframework.shell.command.annotation.Command;
import org.springframework.shell.command.annotation.Option;
import org.springframework.shell.core.command.annotation.Argument;
import org.springframework.shell.core.command.annotation.Command;
import org.springframework.stereotype.Component;

@Command
@Component
@RequiredArgsConstructor
class Cat {
private final ParameterStore parameterStore;

@Command(description = "Cat Parameter", group = "Parameter Store")
public String cat(
@Option(description = "Full path of parameter") String name
@Argument(index = 0, description = "Full path of parameter") String name
) {

var result = parameterStore.getParameter(name);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,19 @@

import com.internetstaff.parameterstore.application.port.in.SetCurrentDirectoryUseCase;
import lombok.RequiredArgsConstructor;
import org.springframework.shell.command.annotation.Command;
import org.springframework.shell.command.annotation.Option;
import org.springframework.shell.core.command.annotation.Argument;
import org.springframework.shell.core.command.annotation.Command;
import org.springframework.stereotype.Component;

@Command

@Component
@RequiredArgsConstructor
public class Cd {
private final SetCurrentDirectoryUseCase setCurrentDirectoryUseCase;

@Command(description = "Change directory", group = "Parameter Store")
public void cd(
@Option(description = "Path") String path
@Argument(index = 0, description = "Path") String path
) {
setCurrentDirectoryUseCase.setCurrentDirectory(path);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,19 @@

import com.internetstaff.parameterstore.application.port.out.ParameterStore;
import lombok.RequiredArgsConstructor;
import org.springframework.shell.command.annotation.Command;
import org.springframework.shell.command.annotation.Option;
import org.springframework.shell.core.command.annotation.Argument;
import org.springframework.shell.core.command.annotation.Command;
import org.springframework.stereotype.Component;

@Command
@Component
@RequiredArgsConstructor
class Cp {
private final ParameterStore parameterStore;

@Command(description = "Copy Parameter", group = "Parameter Store")
public String cp(
@Option(description = "Full path of source parameter") String source,
@Option(description = "Full path of destination parameter") String destination
@Argument(index = 0, description = "Full path of source parameter") String source,
@Argument(index = 1, description = "Full path of destination parameter") String destination
) {

if (parameterStore.copyParameter(source, destination)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,40 @@

import com.internetstaff.parameterstore.application.port.out.ParameterStore;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.jline.terminal.Terminal;
import org.springframework.shell.command.annotation.Command;
import org.springframework.shell.command.annotation.Option;
import org.springframework.shell.standard.AbstractShellComponent;
import org.springframework.shell.core.command.annotation.Argument;
import org.springframework.shell.core.command.annotation.Command;
import org.springframework.shell.core.command.annotation.Option;
import org.springframework.stereotype.Component;
import software.amazon.awssdk.services.ssm.model.ParameterType;

import java.util.Optional;
import java.util.stream.Collectors;

@Component
@RequiredArgsConstructor
@Command
class Create extends AbstractShellComponent {
class Create {
private final ParameterStore parameterStore;
private final Terminal terminal;
private final OptionPrompter promptingService;

@Command(description = "Create Parameter", group = "Parameter Store")
public void create(
@Option String name,
@Option String value,
@Option String type
@Option(description = "Parameter Type") String type,
@Argument(index = 0, description = "Parameter name") String name,
@Argument(index = 1, description = "Parameter value") String value
) {
var paramName = Optional.ofNullable(name)
.map(StringUtils::trimToNull)
.orElseGet(() -> promptingService.prompt("Parameter name: "));

var paramValue = Optional.ofNullable(value)
.map(StringUtils::trimToNull)
.orElseGet(() -> promptingService.prompt("Parameter value: "));

var paramType = Optional.ofNullable(type)
.map(StringUtils::trimToNull)
.orElseGet(() -> promptingService.select("Parameter type: ",
ParameterType.knownValues().stream()
.collect(Collectors.toMap(ParameterType::toString, ParameterType::toString))));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,23 @@
package com.internetstaff.parameterstore.adapter.in.shell;

import com.internetstaff.parameterstore.application.port.in.GetCurrentDirectoryUseCase;
import com.internetstaff.parameterstore.application.port.out.ParameterStore;
import com.internetstaff.parameterstore.application.port.out.ParameterStore.Metadata;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.shell.command.annotation.Command;
import org.springframework.shell.command.annotation.Option;
import org.springframework.shell.core.command.annotation.Argument;
import org.springframework.shell.core.command.annotation.Command;
import org.springframework.stereotype.Component;

import java.util.Comparator;

@Command
@Component
@RequiredArgsConstructor
class Ls {
private final ParameterStore parameterStore;
private final GetCurrentDirectoryUseCase getCurrentDirectoryUseCase;

// Method to do this in Spring Shell?
private int maxLength(java.util.List<Metadata> parameters) {
return parameters.stream()
.map(Metadata::name)
Expand All @@ -24,7 +28,7 @@ private int maxLength(java.util.List<Metadata> parameters) {

@Command(description = "List parameters", group = "Parameter Store")
public String ls(
@Option(defaultValue = "*", description = "Glob pattern") String path
@Argument(index = 0, defaultValue = "*", description = "Glob pattern") String path
) {

var parameters = parameterStore.getParameters(path);
Expand All @@ -33,7 +37,7 @@ public String ls(
var result = new StringBuilder();

for (var parameter : parameters) {
result.append(StringUtils.rightPad(parameter.name(), len))
result.append(StringUtils.rightPad(getCurrentDirectoryUseCase.baseName(parameter.name()), len))
.append(" ")
.append(parameter.lastModifiedDate())
.append("\n");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,19 @@

import com.internetstaff.parameterstore.application.port.out.ParameterStore;
import lombok.RequiredArgsConstructor;
import org.springframework.shell.command.annotation.Command;
import org.springframework.shell.command.annotation.Option;
import org.springframework.shell.core.command.annotation.Argument;
import org.springframework.shell.core.command.annotation.Command;
import org.springframework.stereotype.Component;

@Command
@Component
@RequiredArgsConstructor
class Mv {
private final ParameterStore parameterStore;

@Command(description = "Move Parameter", group = "Parameter Store")
public String mv(
@Option(description = "Full path of source parameter") String source,
@Option(description = "Full path of destination parameter") String destination
@Argument(index = 0, description = "Full path of source parameter") String source,
@Argument(index = 1, description = "Full path of destination parameter") String destination
) {

if (parameterStore.copyParameter(source, destination)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@
import lombok.RequiredArgsConstructor;
import org.jline.terminal.Terminal;
import org.springframework.core.io.ResourceLoader;
import org.springframework.shell.component.SingleItemSelector;
import org.springframework.shell.component.StringInput;
import org.springframework.shell.component.support.SelectorItem;
import org.springframework.shell.style.TemplateExecutor;
import org.springframework.shell.jline.tui.component.SingleItemSelector;
import org.springframework.shell.jline.tui.component.StringInput;
import org.springframework.shell.jline.tui.component.support.SelectorItem;
import org.springframework.shell.jline.tui.style.TemplateExecutor;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.Map;

@Component
Expand Down Expand Up @@ -43,4 +44,18 @@ public <T> T select(String name, Map<String, T> options) {
.orElse(null);
}

public boolean confirm(String message) {
terminal.writer().print("%s [y/N] ".formatted(message));
terminal.writer().flush();

try {
int ch = terminal.reader().read();
terminal.writer().println();
terminal.writer().flush();
return ch == 'y' || ch == 'Y';
} catch (IOException e) {
return false;
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,32 @@

import com.internetstaff.parameterstore.application.port.out.ParameterStore;
import lombok.RequiredArgsConstructor;
import org.springframework.shell.command.annotation.Command;
import org.springframework.shell.command.annotation.Option;
import org.springframework.shell.core.command.annotation.Argument;
import org.springframework.shell.core.command.annotation.Command;
import org.springframework.shell.core.command.annotation.Option;
import org.springframework.stereotype.Component;

@Command
@Component
@RequiredArgsConstructor
class Rm {
private final ParameterStore parameterStore;
private final OptionPrompter promptingService;

@Command(description = "Remove parameter", group = "Parameter Store")
public String rm(
@Option(description = "Full path of parameter to remove", required = true) String name
@Option(description = "Force without prompting", shortName = 'f', defaultValue = "false") boolean force,
@Argument(index = 0, description = "Full path of parameter to remove") String name
) {

if (parameterStore.deleteParameter(name)) {
return "%s removed.".formatted(name);
if (force || promptingService.confirm("Are you sure you want to remove parameter '%s'?".formatted(name))) {

if (parameterStore.deleteParameter(name)) {
return "%s removed.".formatted(name);
} else {
return "Parameter not found";
}
} else {
return "Parameter not found";
return "Operation cancelled";
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,17 @@ public String getCurrentDirectory() {
return currentDirectory;
}

@Override
public void setCurrentDirectory(String newDirectory) {
if (StringUtils.isBlank(newDirectory)) {
currentDirectory = DIRECTORY_SEPARATOR;
} else if (StringUtils.startsWith(newDirectory, DIRECTORY_SEPARATOR)) {
currentDirectory = normalize(newDirectory);
} else {
currentDirectory = normalize(this.currentDirectory + DIRECTORY_SEPARATOR + newDirectory);
}
}

private String normalize(String path) {
return Path.of(path).normalize().toString();
}
Expand All @@ -36,13 +47,14 @@ public String qualifyName(String name) {
}

@Override
public void setCurrentDirectory(String newDirectory) {
if (StringUtils.isBlank(newDirectory)) {
currentDirectory = DIRECTORY_SEPARATOR;
} else if (StringUtils.startsWith(newDirectory, DIRECTORY_SEPARATOR)) {
currentDirectory = normalize(newDirectory);
} else {
currentDirectory = normalize(this.currentDirectory + DIRECTORY_SEPARATOR + newDirectory);
public String baseName(String name) {
if (StringUtils.startsWith(name, currentDirectory)) {
String stripped = StringUtils.removeStart(name, currentDirectory);
if (StringUtils.startsWith(stripped, DIRECTORY_SEPARATOR)) {
return StringUtils.removeStart(stripped, DIRECTORY_SEPARATOR);
}
return stripped;
}
return name;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,6 @@ public interface GetCurrentDirectoryUseCase {
String getCurrentDirectory();

String qualifyName(String name);

String baseName(String name);
}
Loading