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

Archetype V1 prompt for resolved inputs #900

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
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
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, 2021 Oracle and/or its affiliates.
* Copyright (c) 2020, 2023 Oracle and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -37,14 +37,16 @@ private FlowNodeControllers() {
*
* @param flowNode The flow node.
* @param properties Properties used to resolve and expressions.
* @param userInputs userInputs
* @return A flow controller.
*/
public static FlowNodeController create(ArchetypeDescriptor.FlowNode flowNode, Map<String, String> properties) {
public static FlowNodeController create(ArchetypeDescriptor.FlowNode flowNode, Map<String, String> properties,
Map<String, String> userInputs) {
if (flowNode instanceof ArchetypeDescriptor.Input) {
return new InputController((ArchetypeDescriptor.Input) flowNode, properties);
return new InputController((ArchetypeDescriptor.Input) flowNode, properties, userInputs);
}
if (flowNode instanceof ArchetypeDescriptor.Select) {
return new SelectController((ArchetypeDescriptor.Select) flowNode, properties);
return new SelectController((ArchetypeDescriptor.Select) flowNode, properties, userInputs);
}
throw new UnsupportedOperationException("No support for " + flowNode);
}
Expand All @@ -54,15 +56,21 @@ public static FlowNodeController create(ArchetypeDescriptor.FlowNode flowNode, M
*/
public abstract static class FlowNodeController {
private final Map<String, String> properties;
private final Map<String, String> userInputs;

FlowNodeController(Map<String, String> properties) {
FlowNodeController(Map<String, String> properties, Map<String, String> userInputs) {
this.properties = properties;
this.userInputs = userInputs;
}

Map<String, String> properties() {
return properties;
}

Map<String, String> userInputs() {
return userInputs;
}

/**
* Execute the controller.
*/
Expand All @@ -75,8 +83,8 @@ Map<String, String> properties() {
static class InputController extends FlowNodeController {
private final ArchetypeDescriptor.Input input;

InputController(ArchetypeDescriptor.Input input, Map<String, String> properties) {
super(properties);
InputController(ArchetypeDescriptor.Input input, Map<String, String> properties, Map<String, String> userInputs) {
super(properties, userInputs);
this.input = input;
}

Expand All @@ -89,8 +97,10 @@ public void execute() {
String defaultValue = input.defaultValue()
.map(v -> hasExpression(v) ? PropertyEvaluator.evaluate(v, properties()) : v)
.orElse(null);
String v = prompt(input.text(), defaultValue);
properties().put(property, v);
if (!userInputs().containsKey(property)) {
String v = prompt(input.text(), defaultValue);
properties().put(property, v);
}
}
}

Expand All @@ -100,8 +110,8 @@ public void execute() {
static class SelectController extends FlowNodeController {
private final ArchetypeDescriptor.Select select;

SelectController(ArchetypeDescriptor.Select select, Map<String, String> properties) {
super(properties);
SelectController(ArchetypeDescriptor.Select select, Map<String, String> properties, Map<String, String> userInputs) {
super(properties, userInputs);
this.select = select;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import java.util.Map;
import java.util.Properties;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;

import io.helidon.build.archetype.engine.v1.ArchetypeCatalog;
Expand Down Expand Up @@ -64,6 +65,13 @@
*/
abstract class ArchetypeInvoker {

protected static final String FLAVOR_PROPERTY = "flavor";
protected static final String GROUP_ID_PROPERTY = "groupId";
protected static final String ARTIFACT_ID_PROPERTY = "artifactId";
protected static final String PACKAGE_NAME_PROPERTY = "package";
protected static final String BUILD_SYSTEM_PROPERTY = "build-system";
protected static final String ARCHETYPE_BASE_PROPERTY = "app-type";

/**
* Archetype engine versions.
*/
Expand Down Expand Up @@ -308,11 +316,18 @@ Path invoke() throws IOException {
String helidonVersion = initOptions.helidonVersion();

if (isInteractive()) {
// Select flavor interactively
String[] flavorOptions = new String[]{"SE", "MP"};
int flavorIndex = initOptions.flavor() == Flavor.SE ? 0 : 1;
flavorIndex = prompt("Helidon flavor", flavorOptions, flavorIndex);
initOptions.flavor(Flavor.valueOf(flavorOptions[flavorIndex]));
String userInputFlavor = userInput(
initOptions::flavorOption,
() -> this.initProperties().get(FLAVOR_PROPERTY),
String::toUpperCase);
if (userInputFlavor == null) {
// Select flavor interactively
String[] flavorOptions = new String[]{"SE", "MP"};
int flavorIndex = initOptions.flavor() == Flavor.SE ? 0 : 1;
flavorIndex = prompt("Helidon flavor", flavorOptions, flavorIndex);
userInputFlavor = flavorOptions[flavorIndex];
}
initOptions.flavor(Flavor.valueOf(userInputFlavor));
}

Flavor flavor = initOptions.flavor();
Expand All @@ -324,12 +339,24 @@ Path invoke() throws IOException {

ArchetypeEntry archetype;
if (isInteractive()) {
// Select archetype interactively
List<String> descriptions = archetypes.stream()
.map(a -> a.name() + " | " + a.description().orElse(a.summary()))
.collect(Collectors.toList());
int archetypeIndex = prompt("Select archetype", descriptions, 0);
archetype = archetypes.get(archetypeIndex);
String userInputArchetypeName = userInput(
initOptions::archetypeNameOption,
()->this.initProperties().get(ARCHETYPE_BASE_PROPERTY),
Function.identity());
archetype = archetypes.stream()
.filter(arch -> arch.name().equalsIgnoreCase(userInputArchetypeName))
.findFirst().orElse(null);
if (userInputArchetypeName != null && archetype == null) {
throw new IllegalArgumentException("Unable to find an archetype for the input - " + userInputArchetypeName);
}
if (archetype == null) {
// Select archetype interactively
List<String> descriptions = archetypes.stream()
.map(a -> a.name() + " | " + a.description().orElse(a.summary()))
.collect(Collectors.toList());
int archetypeIndex = prompt("Select archetype", descriptions, 0);
archetype = archetypes.get(archetypeIndex);
}
initOptions.archetypeName(archetype.name());
} else {
// find the archetype that matches archetypeName
Expand All @@ -350,6 +377,10 @@ Path invoke() throws IOException {
initProperties.putAll(Maps.fromProperties(System.getProperties()));
initProperties.putAll(initOptions().initProperties());

Map<String, String> userInputs = new HashMap<>();
userInputs.putAll(initOptions().userInputs());
userInputs.putAll(this.initProperties());

ArchetypeEngine engine = new ArchetypeEngine(loader, initProperties);

// Run input flow if not in batch mode
Expand All @@ -359,7 +390,7 @@ Path invoke() throws IOException {

// Process input flow from template and updates properties
inputFlow.nodes().stream()
.map(n -> FlowNodeControllers.create(n, initProperties))
.map(n -> FlowNodeControllers.create(n, initProperties, userInputs))
.forEach(FlowNodeController::execute);
}

Expand All @@ -374,20 +405,24 @@ Path invoke() throws IOException {

return projectDir;
}

private <T> String userInput(
Supplier<T> optionSupplier,
Supplier<String> propertySupplier,
Function<String, String> inputMapper) {
String input = optionSupplier.get() != null ? optionSupplier.get().toString() : propertySupplier.get();
if (input != null) {
return inputMapper.apply(input);
}
return input;
}
}

/**
* Invoker for the archetype V2 engine.
*/
static class V2Invoker extends ArchetypeInvoker {

private static final String FLAVOR_PROPERTY = "flavor";
private static final String GROUP_ID_PROPERTY = "groupId";
private static final String ARTIFACT_ID_PROPERTY = "artifactId";
private static final String PACKAGE_NAME_PROPERTY = "package";
private static final String BUILD_SYSTEM_PROPERTY = "build-system";
private static final String ARCHETYPE_BASE_PROPERTY = "app-type";

private V2Invoker(Builder builder) {
super(builder);
}
Expand Down
21 changes: 21 additions & 0 deletions cli/impl/src/main/java/io/helidon/build/cli/impl/InitOptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import io.helidon.build.cli.harness.CommandFragment;
import io.helidon.build.cli.harness.Creator;
Expand All @@ -41,6 +42,9 @@ public final class InitOptions {
private static final String ARTIFACT_ID_PROPERTY = "artifactId";
private static final String PACKAGE_NAME_PROPERTY = "package";
private static final String HELIDON_VERSION_PROPERTY = "helidonVersion";
private static final String HELIDON_VERSION_OPTION = "version";
private static final String FLAVOR_OPTION = "flavor";
private static final String ARTIFACT_OPTION = "app-type";
private static final String MAVEN_PROPERTY = "maven";
private static final List<String> OVERRIDES = List.of(GROUP_ID_PROPERTY, ARTIFACT_ID_PROPERTY,
PACKAGE_NAME_PROPERTY);
Expand Down Expand Up @@ -399,4 +403,21 @@ Map<String, String> initProperties() {
result.putIfAbsent(MAVEN_PROPERTY, "true"); // No gradle support yet
return result;
}

/**
* Get user inputs of init options.
*
* @return Map of init options
*/
Map<String, String> userInputs() {
Map<String, String> result = new HashMap<>();
Optional.ofNullable(projectNameOption).ifPresent(value -> result.put(PROJECT_NAME_PROPERTY, value));
Optional.ofNullable(groupIdOption).ifPresent(value -> result.put(GROUP_ID_PROPERTY, value));
Optional.ofNullable(artifactIdOption).ifPresent(value -> result.put(ARTIFACT_ID_PROPERTY, value));
Optional.ofNullable(packageNameOption).ifPresent(value -> result.put(PACKAGE_NAME_PROPERTY, value));
Optional.ofNullable(helidonVersion).ifPresent(value -> result.put(HELIDON_VERSION_OPTION, value));
Optional.ofNullable(flavorOption).ifPresent(value -> result.put(FLAVOR_OPTION, value.name()));
Optional.ofNullable(artifactIdOption).ifPresent(value -> result.put(ARTIFACT_OPTION, value));
return result;
}
}