diff --git a/conf/reflect-config.json b/conf/reflect-config.json index ece486f6..80463446 100644 --- a/conf/reflect-config.json +++ b/conf/reflect-config.json @@ -1160,6 +1160,18 @@ "queryAllDeclaredMethods":true, "methods":[{"name":"","parameterTypes":[] }] }, +{ + "name":"io.seqera.tower.cli.commands.datastudios.DataStudioTemplateOptions", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"io.seqera.tower.cli.commands.datastudios.DataStudioTemplateOptions$DataStudioTemplate", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"io.seqera.tower.cli.commands.datastudios.DeleteCmd", "allDeclaredFields":true, diff --git a/src/main/java/io/seqera/tower/cli/commands/datastudios/AddCmd.java b/src/main/java/io/seqera/tower/cli/commands/datastudios/AddCmd.java index 8f04165d..a2a7594d 100644 --- a/src/main/java/io/seqera/tower/cli/commands/datastudios/AddCmd.java +++ b/src/main/java/io/seqera/tower/cli/commands/datastudios/AddCmd.java @@ -19,6 +19,7 @@ import io.seqera.tower.ApiException; import io.seqera.tower.cli.commands.global.WorkspaceOptionalOptions; +import io.seqera.tower.cli.exceptions.DataStudiosCustomTemplateWithCondaException; import io.seqera.tower.cli.exceptions.DataStudiosTemplateNotFoundException; import io.seqera.tower.cli.exceptions.TowerException; import io.seqera.tower.cli.responses.Response; @@ -52,8 +53,11 @@ public class AddCmd extends AbstractStudiosCmd{ @CommandLine.Mixin public WorkspaceOptionalOptions workspace; - @CommandLine.Option(names = {"-t", "--template"}, description = "Data studio template container image. Available templates can be listed with 'studios templates' command", required = true) - public String template; + @CommandLine.Mixin + public DataStudioTemplateOptions templateOptions; + + @CommandLine.Option(names = {"--conda-env-yml", "--conda-env-yaml"}, description = "Path to a YAML env file with Conda packages to be installed in the Data Studio environment.") + public Path condaEnv; @CommandLine.Option(names = {"-c", "--compute-env"}, description = "Compute environment name.", required = true) public String computeEnv; @@ -61,9 +65,6 @@ public class AddCmd extends AbstractStudiosCmd{ @CommandLine.Mixin public DataStudioConfigurationOptions dataStudioConfigOptions; - @CommandLine.Option(names = {"--conda-env-yml", "--conda-env-yaml"}, description = "Path to a YAML env file with Conda packages to be installed in the Data Studio environment.") - public Path condaEnv; - @CommandLine.Option(names = {"-a", "--autoStart"}, description = "Create Data Studio and start it immediately, defaults to false", defaultValue = "false") public Boolean autoStart; @@ -75,7 +76,7 @@ protected Response exec() throws ApiException { Long wspId = workspaceId(workspace.workspace); try { - checkIfTemplateIsAvailable(template, wspId); + templateValidation(templateOptions, condaEnv, wspId); DataStudioCreateRequest request = prepareRequest(); DataStudioCreateResponse response = api().createDataStudio(request, wspId, autoStart); DataStudioDto dataStudioDto = response.getStudio(); @@ -89,6 +90,14 @@ protected Response exec() throws ApiException { } } + private void templateValidation(DataStudioTemplateOptions templateOptions, Path condaEnv, Long wspId) throws ApiException { + if (templateOptions.template.standardTemplate != null) { + checkIfTemplateIsAvailable(templateOptions.template.standardTemplate, wspId); + } else if (condaEnv != null) { + throw new DataStudiosCustomTemplateWithCondaException(); + } + } + void checkIfTemplateIsAvailable(String template, Long workspaceId) throws ApiException { List availableTemplates = fetchDataStudioTemplates(workspaceId); boolean validTemplate = availableTemplates.stream() @@ -103,7 +112,7 @@ DataStudioCreateRequest prepareRequest() throws ApiException { DataStudioCreateRequest request = new DataStudioCreateRequest(); request.setName(name); if (description != null && !description.isEmpty()) {request.description(description);} - request.setDataStudioToolUrl(template); + request.setDataStudioToolUrl(templateOptions.getTemplate()); request.setComputeEnvId(computeEnv); String condaEnvString = null; diff --git a/src/main/java/io/seqera/tower/cli/commands/datastudios/DataStudioTemplateOptions.java b/src/main/java/io/seqera/tower/cli/commands/datastudios/DataStudioTemplateOptions.java new file mode 100644 index 00000000..2e738153 --- /dev/null +++ b/src/main/java/io/seqera/tower/cli/commands/datastudios/DataStudioTemplateOptions.java @@ -0,0 +1,38 @@ +/* + * Copyright 2021-2023, Seqera. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package io.seqera.tower.cli.commands.datastudios; + +import picocli.CommandLine; + +public class DataStudioTemplateOptions { + + @CommandLine.ArgGroup(multiplicity = "1") + public DataStudioTemplate template; + + public static class DataStudioTemplate { + @CommandLine.Option(names = {"-t", "--template"}, description = "Container image template to be used for Data Studio. Available templates can be listed with 'studios templates' command") + public String standardTemplate; + + @CommandLine.Option(names = {"-ct", "--custom-template"}, description = "Custom container image template to be used for Data Studio") + public String customTemplate; + } + + public String getTemplate() { + return template.standardTemplate != null ? template.standardTemplate : template.customTemplate; + } +} diff --git a/src/main/java/io/seqera/tower/cli/exceptions/DataStudiosCustomTemplateWithCondaException.java b/src/main/java/io/seqera/tower/cli/exceptions/DataStudiosCustomTemplateWithCondaException.java new file mode 100644 index 00000000..3a23c847 --- /dev/null +++ b/src/main/java/io/seqera/tower/cli/exceptions/DataStudiosCustomTemplateWithCondaException.java @@ -0,0 +1,25 @@ +/* + * Copyright 2021-2023, Seqera. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package io.seqera.tower.cli.exceptions; + +public class DataStudiosCustomTemplateWithCondaException extends TowerException { + + public DataStudiosCustomTemplateWithCondaException() { + super(String.format("DataStudio template provided is a custom template and cannot be used together with conda environment option.")); + } +} diff --git a/src/main/java/io/seqera/tower/cli/exceptions/DataStudiosTemplateNotFoundException.java b/src/main/java/io/seqera/tower/cli/exceptions/DataStudiosTemplateNotFoundException.java index 971209dc..58d81b30 100644 --- a/src/main/java/io/seqera/tower/cli/exceptions/DataStudiosTemplateNotFoundException.java +++ b/src/main/java/io/seqera/tower/cli/exceptions/DataStudiosTemplateNotFoundException.java @@ -22,6 +22,6 @@ public class DataStudiosTemplateNotFoundException extends TowerException { public DataStudiosTemplateNotFoundException(String template, List templatesAvailable) { - super(String.format("DataStudio template '%s' is not one of available templates '%s'", template, templatesAvailable)); + super(String.format("DataStudio template provided '%s', is not one of the Platform's available templates '%s'.", template, templatesAvailable)); } } diff --git a/src/test/java/io/seqera/tower/cli/datastudios/DataStudiosCmdTest.java b/src/test/java/io/seqera/tower/cli/datastudios/DataStudiosCmdTest.java index a96da041..a8fff5ba 100644 --- a/src/test/java/io/seqera/tower/cli/datastudios/DataStudiosCmdTest.java +++ b/src/test/java/io/seqera/tower/cli/datastudios/DataStudiosCmdTest.java @@ -20,6 +20,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import io.seqera.tower.cli.BaseCmdTest; import io.seqera.tower.cli.commands.enums.OutputType; +import io.seqera.tower.cli.exceptions.DataStudiosCustomTemplateWithCondaException; import io.seqera.tower.cli.exceptions.DataStudiosTemplateNotFoundException; import io.seqera.tower.cli.exceptions.MultipleDataLinksFoundException; import io.seqera.tower.cli.responses.datastudios.DataStudioDeleted; @@ -1016,6 +1017,37 @@ void testAddThrowsDataStudiosTemplateNotFoundException(OutputType format, MockSe assertEquals(1, out.exitCode); } + @ParameterizedTest + @EnumSource(OutputType.class) + void testAddThrowsDataStudiosCustomTemplateWithCondaException(OutputType format, MockServerClient mock){ + + mock.when( + request().withMethod("GET").withPath("/user-info"), exactly(1) + ).respond( + response().withStatusCode(200).withBody(loadResource("user")).withContentType(MediaType.APPLICATION_JSON) + ); + + mock.when( + request().withMethod("GET").withPath("/user/1264/workspaces"), exactly(1) + ).respond( + response().withStatusCode(200).withBody(loadResource("workspaces/workspaces_list")).withContentType(MediaType.APPLICATION_JSON) + ); + + mock.when( + request().withMethod("GET").withPath("/studios") + .withQueryStringParameter("workspaceId", "75887156211589") + .withQueryStringParameter("autostart", "false"), exactly(1) + ).respond( + response().withStatusCode(200).withBody(loadResource("datastudios/datastudios_created_response")).withContentType(MediaType.APPLICATION_JSON) + ); + + ExecOut out = exec(format, mock, "studios", "add", "-n", "studio-a66d", "-w", "75887156211589", "-ct" ,"custom-template", "--conda-env-yml", "path/to/any/yaml/file","-c", "7WgvfmcjAwp3Or75UphCJl"); + + assertEquals(errorMessage(out.app, new DataStudiosCustomTemplateWithCondaException()), out.stdErr); + assertEquals("", out.stdOut); + assertEquals(1, out.exitCode); + } + // Only run this test in json output format, since extra stdout output is printed out to console with --wait flag @ParameterizedTest @EnumSource(value = OutputType.class, names = {"json"})