diff --git a/.gitignore b/.gitignore index 2c782154..2dc22a05 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,7 @@ build **/build-info.properties # Location for unshared files -.user/ \ No newline at end of file +.user/ + + +*.iml \ No newline at end of file diff --git a/build.gradle b/build.gradle index 6fe362bd..11c74bd2 100644 --- a/build.gradle +++ b/build.gradle @@ -27,7 +27,7 @@ dependencies { implementation 'org.slf4j:slf4j-api:1.7.36' implementation 'ch.qos.logback:logback-core:1.2.11' implementation 'ch.qos.logback:logback-classic:1.2.11' - implementation 'io.seqera.tower:tower-java-sdk:1.9.7' + implementation 'io.seqera.tower:tower-java-sdk:1.9.9' implementation 'info.picocli:picocli:4.6.3' implementation 'org.apache.commons:commons-compress:1.22' implementation 'org.tukaani:xz:1.9' diff --git a/conf/reflect-config.json b/conf/reflect-config.json index 8c5c4731..809cda4f 100644 --- a/conf/reflect-config.json +++ b/conf/reflect-config.json @@ -178,6 +178,12 @@ "queryAllDeclaredMethods":true, "methods":[{"name":"","parameterTypes":[] }] }, +{ + "name":"io.seqera.tower.cli.commands.DataStudiosCmd", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"io.seqera.tower.cli.commands.DatasetsCmd", "allDeclaredFields":true, @@ -1025,6 +1031,29 @@ "queryAllDeclaredMethods":true, "methods":[{"name":"","parameterTypes":[] }] }, +{ + "name":"io.seqera.tower.cli.commands.data.studios.AbstractStudiosCmd", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true +}, +{ + "name":"io.seqera.tower.cli.commands.data.studios.DataStudioRefOptions", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"io.seqera.tower.cli.commands.data.studios.DataStudioRefOptions$DataStudioRef", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"io.seqera.tower.cli.commands.data.studios.ViewCmd", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"io.seqera.tower.cli.commands.datasets.AbstractDatasetsCmd", "allDeclaredFields":true, @@ -1090,6 +1119,35 @@ "queryAllDeclaredMethods":true, "methods":[{"name":"","parameterTypes":[] }] }, +{ + "name":"io.seqera.tower.cli.commands.datastudios.AbstractStudiosCmd", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true +}, +{ + "name":"io.seqera.tower.cli.commands.datastudios.DataStudioRefOptions", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"io.seqera.tower.cli.commands.datastudios.DataStudioRefOptions$DataStudioRef", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"io.seqera.tower.cli.commands.datastudios.ListCmd", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"io.seqera.tower.cli.commands.datastudios.ViewCmd", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"io.seqera.tower.cli.commands.global.PaginationOptions", "allDeclaredFields":true, @@ -1777,6 +1835,18 @@ "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, +{ + "name":"io.seqera.tower.cli.responses.datastudios.DataStudiosList", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, +{ + "name":"io.seqera.tower.cli.responses.datastudios.DataStudiosView", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, { "name":"io.seqera.tower.cli.responses.labels.DeleteLabelsResponse", "allDeclaredFields":true, @@ -2113,6 +2183,13 @@ "allDeclaredMethods":true, "allDeclaredConstructors":true }, +{ + "name":"io.seqera.tower.model.ActiveConnection", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"","parameterTypes":[] }, {"name":"setLastActive","parameterTypes":["java.time.OffsetDateTime"] }] +}, { "name":"io.seqera.tower.model.AddMemberRequest", "allDeclaredFields":true, @@ -2161,7 +2238,7 @@ "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, - "methods":[{"name":"","parameterTypes":[] }, {"name":"getSiteId","parameterTypes":[] }, {"name":"getUrl","parameterTypes":[] }, {"name":"setSiteId","parameterTypes":["java.lang.Integer"] }, {"name":"setUrl","parameterTypes":["java.lang.String"] }] + "methods":[{"name":"","parameterTypes":[] }, {"name":"getHubspotId","parameterTypes":[] }, {"name":"getPosthogApiHost","parameterTypes":[] }, {"name":"getPosthogApiKey","parameterTypes":[] }, {"name":"setHubspotId","parameterTypes":["java.lang.String"] }, {"name":"setPosthogApiHost","parameterTypes":["java.lang.String"] }, {"name":"setPosthogApiKey","parameterTypes":["java.lang.String"] }] }, { "name":"io.seqera.tower.model.AssociateActionLabelsRequest", @@ -2266,14 +2343,15 @@ "name":"io.seqera.tower.model.ComputeEnvDbDto", "allDeclaredFields":true, "queryAllDeclaredMethods":true, - "queryAllDeclaredConstructors":true + "queryAllDeclaredConstructors":true, + "methods":[{"name":"getId","parameterTypes":[] }, {"name":"getName","parameterTypes":[] }, {"name":"getPlatform","parameterTypes":[] }, {"name":"getRegion","parameterTypes":[] }, {"name":"setId","parameterTypes":["java.lang.String"] }, {"name":"setName","parameterTypes":["java.lang.String"] }, {"name":"setPlatform","parameterTypes":["java.lang.String"] }, {"name":"setRegion","parameterTypes":["java.lang.String"] }] }, { "name":"io.seqera.tower.model.ComputeEnvResponseDto", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, - "methods":[{"name":"","parameterTypes":[] }, {"name":"getConfig","parameterTypes":[] }, {"name":"getCredentialsId","parameterTypes":[] }, {"name":"getDateCreated","parameterTypes":[] }, {"name":"getDeleted","parameterTypes":[] }, {"name":"getDescription","parameterTypes":[] }, {"name":"getId","parameterTypes":[] }, {"name":"getLabels","parameterTypes":[] }, {"name":"getLastUpdated","parameterTypes":[] }, {"name":"getLastUsed","parameterTypes":[] }, {"name":"getMessage","parameterTypes":[] }, {"name":"getName","parameterTypes":[] }, {"name":"getOrgId","parameterTypes":[] }, {"name":"getPlatform","parameterTypes":[] }, {"name":"getPrimary","parameterTypes":[] }, {"name":"getStatus","parameterTypes":[] }, {"name":"getWorkspaceId","parameterTypes":[] }, {"name":"setConfig","parameterTypes":["io.seqera.tower.model.ComputeConfig"] }, {"name":"setCredentialsId","parameterTypes":["java.lang.String"] }, {"name":"setDateCreated","parameterTypes":["java.time.OffsetDateTime"] }, {"name":"setDeleted","parameterTypes":["java.lang.Boolean"] }, {"name":"setDescription","parameterTypes":["java.lang.String"] }, {"name":"setId","parameterTypes":["java.lang.String"] }, {"name":"setLabels","parameterTypes":["java.util.List"] }, {"name":"setLastUpdated","parameterTypes":["java.time.OffsetDateTime"] }, {"name":"setLastUsed","parameterTypes":["java.time.OffsetDateTime"] }, {"name":"setMessage","parameterTypes":["java.lang.String"] }, {"name":"setName","parameterTypes":["java.lang.String"] }, {"name":"setOrgId","parameterTypes":["java.lang.Long"] }, {"name":"setPlatform","parameterTypes":["io.seqera.tower.model.ComputeEnvResponseDto$PlatformEnum"] }, {"name":"setPrimary","parameterTypes":["java.lang.Boolean"] }, {"name":"setStatus","parameterTypes":["io.seqera.tower.model.ComputeEnvStatus"] }, {"name":"setWorkspaceId","parameterTypes":["java.lang.Long"] }] + "methods":[{"name":"","parameterTypes":[] }, {"name":"getConfig","parameterTypes":[] }, {"name":"getCredentialsId","parameterTypes":[] }, {"name":"getDateCreated","parameterTypes":[] }, {"name":"getDeleted","parameterTypes":[] }, {"name":"getDescription","parameterTypes":[] }, {"name":"getId","parameterTypes":[] }, {"name":"getLabels","parameterTypes":[] }, {"name":"getLastUpdated","parameterTypes":[] }, {"name":"getLastUsed","parameterTypes":[] }, {"name":"getManagedIdentityId","parameterTypes":[] }, {"name":"getMessage","parameterTypes":[] }, {"name":"getName","parameterTypes":[] }, {"name":"getOrgId","parameterTypes":[] }, {"name":"getPlatform","parameterTypes":[] }, {"name":"getPrimary","parameterTypes":[] }, {"name":"getStatus","parameterTypes":[] }, {"name":"getWorkspaceId","parameterTypes":[] }, {"name":"setConfig","parameterTypes":["io.seqera.tower.model.ComputeConfig"] }, {"name":"setCredentialsId","parameterTypes":["java.lang.String"] }, {"name":"setDateCreated","parameterTypes":["java.time.OffsetDateTime"] }, {"name":"setDeleted","parameterTypes":["java.lang.Boolean"] }, {"name":"setDescription","parameterTypes":["java.lang.String"] }, {"name":"setId","parameterTypes":["java.lang.String"] }, {"name":"setLabels","parameterTypes":["java.util.List"] }, {"name":"setLastUpdated","parameterTypes":["java.time.OffsetDateTime"] }, {"name":"setLastUsed","parameterTypes":["java.time.OffsetDateTime"] }, {"name":"setManagedIdentityId","parameterTypes":["java.lang.String"] }, {"name":"setMessage","parameterTypes":["java.lang.String"] }, {"name":"setName","parameterTypes":["java.lang.String"] }, {"name":"setOrgId","parameterTypes":["java.lang.Long"] }, {"name":"setPlatform","parameterTypes":["io.seqera.tower.model.ComputeEnvResponseDto$PlatformEnum"] }, {"name":"setPrimary","parameterTypes":["java.lang.Boolean"] }, {"name":"setStatus","parameterTypes":["io.seqera.tower.model.ComputeEnvStatus"] }, {"name":"setWorkspaceId","parameterTypes":["java.lang.Long"] }] }, { "name":"io.seqera.tower.model.ComputeEnvResponseDto$PlatformEnum", @@ -2536,6 +2614,73 @@ "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"fromValue","parameterTypes":["java.lang.String"] }, {"name":"setDataLinks","parameterTypes":["java.util.List"] }, {"name":"setTotalSize","parameterTypes":["java.lang.Long"] }] }, +{ + "name":"io.seqera.tower.model.DataStudioComputeEnvDto", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"","parameterTypes":[] }, {"name":"getCredentialsId","parameterTypes":[] }, {"name":"getRegion","parameterTypes":[] }, {"name":"setCredentialsId","parameterTypes":["java.lang.String"] }, {"name":"setRegion","parameterTypes":["java.lang.String"] }] +}, +{ + "name":"io.seqera.tower.model.DataStudioConfiguration", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"","parameterTypes":[] }, {"name":"getCondaEnvironment","parameterTypes":[] }, {"name":"getCpu","parameterTypes":[] }, {"name":"getGpu","parameterTypes":[] }, {"name":"getMemory","parameterTypes":[] }, {"name":"getMountData","parameterTypes":[] }, {"name":"setCondaEnvironment","parameterTypes":["java.lang.String"] }, {"name":"setCpu","parameterTypes":["java.lang.Integer"] }, {"name":"setGpu","parameterTypes":["java.lang.Integer"] }, {"name":"setMemory","parameterTypes":["java.lang.Integer"] }, {"name":"setMountData","parameterTypes":["java.util.List"] }] +}, +{ + "name":"io.seqera.tower.model.DataStudioDto", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"","parameterTypes":[] }, {"name":"getActiveConnections","parameterTypes":[] }, {"name":"getBaseImage","parameterTypes":[] }, {"name":"getComputeEnv","parameterTypes":[] }, {"name":"getConfiguration","parameterTypes":[] }, {"name":"getCustomImage","parameterTypes":[] }, {"name":"getDateCreated","parameterTypes":[] }, {"name":"getDescription","parameterTypes":[] }, {"name":"getLastUpdated","parameterTypes":[] }, {"name":"getMountedDataLinks","parameterTypes":[] }, {"name":"getName","parameterTypes":[] }, {"name":"getParentCheckpoint","parameterTypes":[] }, {"name":"getProgress","parameterTypes":[] }, {"name":"getSessionId","parameterTypes":[] }, {"name":"getStatusInfo","parameterTypes":[] }, {"name":"getStudioUrl","parameterTypes":[] }, {"name":"getTemplate","parameterTypes":[] }, {"name":"getUser","parameterTypes":[] }, {"name":"getWaveBuildUrl","parameterTypes":[] }, {"name":"getWorkspaceId","parameterTypes":[] }, {"name":"setActiveConnections","parameterTypes":["java.util.List"] }, {"name":"setBaseImage","parameterTypes":["java.lang.String"] }, {"name":"setComputeEnv","parameterTypes":["io.seqera.tower.model.DataStudioComputeEnvDto"] }, {"name":"setConfiguration","parameterTypes":["io.seqera.tower.model.DataStudioConfiguration"] }, {"name":"setCustomImage","parameterTypes":["java.lang.Boolean"] }, {"name":"setDateCreated","parameterTypes":["java.time.OffsetDateTime"] }, {"name":"setDescription","parameterTypes":["java.lang.String"] }, {"name":"setLastUpdated","parameterTypes":["java.time.OffsetDateTime"] }, {"name":"setMountedDataLinks","parameterTypes":["java.util.List"] }, {"name":"setName","parameterTypes":["java.lang.String"] }, {"name":"setParentCheckpoint","parameterTypes":["io.seqera.tower.model.DataStudioDtoParentCheckpoint"] }, {"name":"setProgress","parameterTypes":["java.util.List"] }, {"name":"setSessionId","parameterTypes":["java.lang.String"] }, {"name":"setStatusInfo","parameterTypes":["io.seqera.tower.model.DataStudioStatusInfo"] }, {"name":"setStudioUrl","parameterTypes":["java.lang.String"] }, {"name":"setTemplate","parameterTypes":["io.seqera.tower.model.DataStudioTemplate"] }, {"name":"setUser","parameterTypes":["io.seqera.tower.model.StudioUser"] }, {"name":"setWaveBuildUrl","parameterTypes":["java.lang.String"] }, {"name":"setWorkspaceId","parameterTypes":["java.lang.Long"] }] +}, +{ + "name":"io.seqera.tower.model.DataStudioDtoParentCheckpoint", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, +{ + "name":"io.seqera.tower.model.DataStudioListResponse", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"","parameterTypes":[] }, {"name":"setStudios","parameterTypes":["java.util.List"] }, {"name":"setTotalSize","parameterTypes":["java.lang.Long"] }] +}, +{ + "name":"io.seqera.tower.model.DataStudioProgressStep", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"","parameterTypes":[] }, {"name":"getMessage","parameterTypes":[] }, {"name":"getStatus","parameterTypes":[] }, {"name":"getWarnings","parameterTypes":[] }, {"name":"setMessage","parameterTypes":["java.lang.String"] }, {"name":"setStatus","parameterTypes":["io.seqera.tower.model.DataStudioProgressStepStatus"] }, {"name":"setWarnings","parameterTypes":["java.util.List"] }] +}, +{ + "name":"io.seqera.tower.model.DataStudioProgressStepStatus", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "methods":[{"name":"fromValue","parameterTypes":["java.lang.String"] }, {"name":"getValue","parameterTypes":[] }, {"name":"setValue","parameterTypes":["java.lang.String"] }] +}, +{ + "name":"io.seqera.tower.model.DataStudioStatus", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "methods":[{"name":"fromValue","parameterTypes":["java.lang.String"] }, {"name":"getValue","parameterTypes":[] }] +}, +{ + "name":"io.seqera.tower.model.DataStudioStatusInfo", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"","parameterTypes":[] }, {"name":"getLastUpdate","parameterTypes":[] }, {"name":"getMessage","parameterTypes":[] }, {"name":"getStatus","parameterTypes":[] }, {"name":"setLastUpdate","parameterTypes":["java.time.OffsetDateTime"] }, {"name":"setMessage","parameterTypes":["java.lang.String"] }, {"name":"setStatus","parameterTypes":["io.seqera.tower.model.DataStudioStatus"] }] +}, +{ + "name":"io.seqera.tower.model.DataStudioTemplate", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"","parameterTypes":[] }, {"name":"getIcon","parameterTypes":[] }, {"name":"getRepository","parameterTypes":[] }, {"name":"setIcon","parameterTypes":["java.lang.String"] }, {"name":"setRepository","parameterTypes":["java.lang.String"] }] +}, { "name":"io.seqera.tower.model.Dataset", "allDeclaredFields":true, @@ -2628,7 +2773,8 @@ "name":"io.seqera.tower.model.DescribeWorkspaceResponse", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true + "allDeclaredConstructors":true, + "methods":[{"name":"","parameterTypes":[] }, {"name":"setWorkspace","parameterTypes":["io.seqera.tower.model.Workspace"] }] }, { "name":"io.seqera.tower.model.EksComputeConfig", @@ -3042,6 +3188,13 @@ "allDeclaredMethods":true, "allDeclaredConstructors":true }, +{ + "name":"io.seqera.tower.model.StudioUser", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[{"name":"","parameterTypes":[] }, {"name":"getAvatar","parameterTypes":[] }, {"name":"getEmail","parameterTypes":[] }, {"name":"getId","parameterTypes":[] }, {"name":"getUserName","parameterTypes":[] }, {"name":"setAvatar","parameterTypes":["java.lang.String"] }, {"name":"setEmail","parameterTypes":["java.lang.String"] }, {"name":"setId","parameterTypes":["java.lang.Long"] }, {"name":"setUserName","parameterTypes":["java.lang.String"] }] +}, { "name":"io.seqera.tower.model.SubmitWorkflowLaunchRequest", "allDeclaredFields":true, @@ -3098,7 +3251,7 @@ "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, - "methods":[{"name":"getName","parameterTypes":[] }] + "methods":[{"name":"getCredentialsId","parameterTypes":[] }, {"name":"getName","parameterTypes":[] }] }, { "name":"io.seqera.tower.model.UpdateCredentialsRequest", @@ -3263,7 +3416,8 @@ "name":"io.seqera.tower.model.Workspace", "allDeclaredFields":true, "allDeclaredMethods":true, - "allDeclaredConstructors":true + "allDeclaredConstructors":true, + "methods":[{"name":"","parameterTypes":[] }, {"name":"setDateCreated","parameterTypes":["java.time.OffsetDateTime"] }, {"name":"setDescription","parameterTypes":["java.lang.String"] }, {"name":"setFullName","parameterTypes":["java.lang.String"] }, {"name":"setId_JsonNullable","parameterTypes":["org.openapitools.jackson.nullable.JsonNullable"] }, {"name":"setLastUpdated","parameterTypes":["java.time.OffsetDateTime"] }, {"name":"setName","parameterTypes":["java.lang.String"] }, {"name":"setVisibility","parameterTypes":["io.seqera.tower.model.Visibility"] }] }, { "name":"io.seqera.tower.model.WspRole", diff --git a/src/main/java/io/seqera/tower/cli/Tower.java b/src/main/java/io/seqera/tower/cli/Tower.java index 46be6253..6974b5af 100644 --- a/src/main/java/io/seqera/tower/cli/Tower.java +++ b/src/main/java/io/seqera/tower/cli/Tower.java @@ -27,6 +27,7 @@ import io.seqera.tower.cli.commands.ComputeEnvsCmd; import io.seqera.tower.cli.commands.CredentialsCmd; import io.seqera.tower.cli.commands.DataLinksCmd; +import io.seqera.tower.cli.commands.DataStudiosCmd; import io.seqera.tower.cli.commands.DatasetsCmd; import io.seqera.tower.cli.commands.InfoCmd; import io.seqera.tower.cli.commands.LaunchCmd; @@ -60,6 +61,7 @@ ComputeEnvsCmd.class, CredentialsCmd.class, DataLinksCmd.class, + DataStudiosCmd.class, DatasetsCmd.class, GenerateCompletion.class, InfoCmd.class, diff --git a/src/main/java/io/seqera/tower/cli/commands/DataStudiosCmd.java b/src/main/java/io/seqera/tower/cli/commands/DataStudiosCmd.java new file mode 100644 index 00000000..948aa4ad --- /dev/null +++ b/src/main/java/io/seqera/tower/cli/commands/DataStudiosCmd.java @@ -0,0 +1,33 @@ +/* + * 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; + +import io.seqera.tower.cli.commands.datastudios.ListCmd; +import io.seqera.tower.cli.commands.datastudios.ViewCmd; +import picocli.CommandLine; + +@CommandLine.Command( + name = "studios", + description = "Manage data studios.", + subcommands = { + ViewCmd.class, + ListCmd.class, + } +) +public class DataStudiosCmd extends AbstractRootCmd { +} diff --git a/src/main/java/io/seqera/tower/cli/commands/datastudios/AbstractStudiosCmd.java b/src/main/java/io/seqera/tower/cli/commands/datastudios/AbstractStudiosCmd.java new file mode 100644 index 00000000..69b6f2ff --- /dev/null +++ b/src/main/java/io/seqera/tower/cli/commands/datastudios/AbstractStudiosCmd.java @@ -0,0 +1,29 @@ +/* + * 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 io.seqera.tower.ApiException; +import io.seqera.tower.cli.commands.AbstractApiCmd; +import io.seqera.tower.model.DataStudioDto; + +public class AbstractStudiosCmd extends AbstractApiCmd { + + protected DataStudioDto fetchDataStudio(DataStudioRefOptions dataStudioRefOptions, Long wspId) throws ApiException { + return api().describeDataStudio(dataStudioRefOptions.dataStudio.sessionId, wspId); + } +} diff --git a/src/main/java/io/seqera/tower/cli/commands/datastudios/DataStudioRefOptions.java b/src/main/java/io/seqera/tower/cli/commands/datastudios/DataStudioRefOptions.java new file mode 100644 index 00000000..e44da5bc --- /dev/null +++ b/src/main/java/io/seqera/tower/cli/commands/datastudios/DataStudioRefOptions.java @@ -0,0 +1,31 @@ +/* + * 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 DataStudioRefOptions { + + @CommandLine.ArgGroup(multiplicity = "1") + public DataStudioRef dataStudio; + + public static class DataStudioRef { + @CommandLine.Option(names = {"-i", "--id"}, description = "DataStudio session ID.") + public String sessionId; + } +} diff --git a/src/main/java/io/seqera/tower/cli/commands/datastudios/ListCmd.java b/src/main/java/io/seqera/tower/cli/commands/datastudios/ListCmd.java new file mode 100644 index 00000000..475a48d8 --- /dev/null +++ b/src/main/java/io/seqera/tower/cli/commands/datastudios/ListCmd.java @@ -0,0 +1,72 @@ +/* + * 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 io.seqera.tower.ApiException; +import io.seqera.tower.cli.commands.global.PaginationOptions; +import io.seqera.tower.cli.commands.global.WorkspaceOptionalOptions; +import io.seqera.tower.cli.exceptions.TowerException; +import io.seqera.tower.cli.exceptions.WorkspaceNotFoundException; +import io.seqera.tower.cli.responses.Response; +import io.seqera.tower.cli.responses.datastudios.DataStudiosList; +import io.seqera.tower.cli.utils.PaginationInfo; +import io.seqera.tower.model.DataStudioListResponse; +import picocli.CommandLine; +import picocli.CommandLine.Command; + +import java.io.IOException; + +@Command( + name = "list", + description = "List workspace data studios." +) +public class ListCmd extends AbstractStudiosCmd { + + @CommandLine.Mixin + public WorkspaceOptionalOptions workspace; + + @CommandLine.Option(names = {"-f", "--filter"}, description = "Optional filter criteria, allowing free text search on name and templateUrl " + + "and keywords: `userName`, `computeEnvName` and `status`. Example keyword usage: -f status:RUNNING") + public String filter; + + @CommandLine.Mixin + PaginationOptions paginationOptions; + + @Override + protected Response exec() throws ApiException, IOException { + Long wspId = workspaceId(workspace.workspace); + Integer max = PaginationOptions.getMax(paginationOptions); + Integer offset = PaginationOptions.getOffset(paginationOptions, max); + + DataStudioListResponse response = new DataStudioListResponse(); + + try { + response = api().listDataStudios(wspId, filter, max, offset); + } catch (ApiException e) { + if (e.getCode() == 404){ + throw new WorkspaceNotFoundException(wspId); + } + if (e.getCode() == 403) { + throw new TowerException(String.format("User not entitled to %s workspace", wspId)); + } + throw e; + } + + return new DataStudiosList(workspaceRef(wspId), response.getStudios(), PaginationInfo.from(paginationOptions, response.getTotalSize())); + } +} diff --git a/src/main/java/io/seqera/tower/cli/commands/datastudios/ViewCmd.java b/src/main/java/io/seqera/tower/cli/commands/datastudios/ViewCmd.java new file mode 100644 index 00000000..5ce30fb3 --- /dev/null +++ b/src/main/java/io/seqera/tower/cli/commands/datastudios/ViewCmd.java @@ -0,0 +1,60 @@ +/* + * 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 io.seqera.tower.ApiException; +import io.seqera.tower.cli.commands.global.WorkspaceOptionalOptions; +import io.seqera.tower.cli.exceptions.DataStudioNotFoundException; +import io.seqera.tower.cli.exceptions.TowerException; +import io.seqera.tower.cli.responses.Response; +import io.seqera.tower.cli.responses.datastudios.DataStudiosView; +import io.seqera.tower.model.DataStudioDto; +import picocli.CommandLine; + +@CommandLine.Command( + name = "view", + description = "View data studio." +) +public class ViewCmd extends AbstractStudiosCmd { + + @CommandLine.Mixin + public WorkspaceOptionalOptions workspace; + + @CommandLine.Mixin + public DataStudioRefOptions dataStudioRefOptions; + + @Override + protected Response exec() throws ApiException { + Long wspId = workspaceId(workspace.workspace); + + try { + DataStudioDto dataStudioDto = fetchDataStudio(dataStudioRefOptions, wspId); + + return new DataStudiosView(dataStudioDto, workspaceRef(wspId)); + } catch (ApiException e) { + if (e.getCode() == 404) { + throw new DataStudioNotFoundException(dataStudioRefOptions.dataStudio.sessionId, wspId); + } + if (e.getCode() == 403) { + throw new TowerException(String.format("User not entitled to view studio '%s' at %s workspace", dataStudioRefOptions.dataStudio.sessionId, wspId)); + } + throw e; + } + } + +} \ No newline at end of file diff --git a/src/main/java/io/seqera/tower/cli/exceptions/DataStudioNotFoundException.java b/src/main/java/io/seqera/tower/cli/exceptions/DataStudioNotFoundException.java new file mode 100644 index 00000000..4a035649 --- /dev/null +++ b/src/main/java/io/seqera/tower/cli/exceptions/DataStudioNotFoundException.java @@ -0,0 +1,26 @@ +/* + * 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 DataStudioNotFoundException extends TowerException { + + public DataStudioNotFoundException( String sessionId, Long workspaceId) { + super(String.format("DataStudio '%s' not found at workspace '%s'", sessionId, workspaceId)); + } + +} diff --git a/src/main/java/io/seqera/tower/cli/responses/datastudios/DataStudiosList.java b/src/main/java/io/seqera/tower/cli/responses/datastudios/DataStudiosList.java new file mode 100644 index 00000000..07c7d29e --- /dev/null +++ b/src/main/java/io/seqera/tower/cli/responses/datastudios/DataStudiosList.java @@ -0,0 +1,87 @@ +/* + * 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.responses.datastudios; + +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.List; +import javax.annotation.Nullable; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.seqera.tower.cli.responses.Response; +import io.seqera.tower.cli.utils.PaginationInfo; +import io.seqera.tower.cli.utils.TableList; +import io.seqera.tower.model.DataStudioDto; +import io.seqera.tower.model.DataStudioStatusInfo; +import io.seqera.tower.model.StudioUser; + +import static io.seqera.tower.cli.utils.FormatHelper.formatDataStudioStatus; + +public class DataStudiosList extends Response { + + public final String workspaceRef; + public final List studios; + + @JsonIgnore + @Nullable + private final PaginationInfo paginationInfo; + + public DataStudiosList(String workspaceRef, List studios, @Nullable PaginationInfo paginationInfo) { + this.workspaceRef = workspaceRef; + this.studios = studios; + this.paginationInfo = paginationInfo; + } + + @Override + public void toString(PrintWriter out) { + + out.println(ansi(String.format("%n @|bold Data Studios at %s workspace:|@%n", workspaceRef))); + + if (studios.isEmpty()) { + out.println(ansi(" @|yellow No data studios found|@")); + return; + } + + List descriptions = new ArrayList<>(List.of("ID", "Name", "Description", "User", "Status")); + + TableList table = new TableList(out, descriptions.size(), descriptions.toArray(new String[descriptions.size()])).sortBy(0); + table.setPrefix(" "); + + studios.forEach(studio -> { + + DataStudioStatusInfo statusInfo = studio.getStatusInfo(); + StudioUser user = studio.getUser(); + List rows = new ArrayList<>(List.of( + studio.getSessionId() == null ? "" : studio.getSessionId(), + studio.getName() == null ? "" : studio.getName(), + studio.getDescription() == null ? "" : studio.getDescription(), + user == null ? "" : user.getUserName(), + formatDataStudioStatus(statusInfo == null ? null : statusInfo.getStatus()) + )); + + table.addRow(rows.toArray(new String[rows.size()])); + }); + + table.print(); + + PaginationInfo.addFooter(out, paginationInfo); + + out.println(""); + } + +} diff --git a/src/main/java/io/seqera/tower/cli/responses/datastudios/DataStudiosView.java b/src/main/java/io/seqera/tower/cli/responses/datastudios/DataStudiosView.java new file mode 100644 index 00000000..12596eff --- /dev/null +++ b/src/main/java/io/seqera/tower/cli/responses/datastudios/DataStudiosView.java @@ -0,0 +1,76 @@ +/* + * 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.responses.datastudios; + +import io.seqera.tower.cli.responses.Response; +import io.seqera.tower.cli.utils.TableList; +import io.seqera.tower.model.DataLinkDto; +import io.seqera.tower.model.DataStudioConfiguration; +import io.seqera.tower.model.DataStudioDto; +import io.seqera.tower.model.DataStudioStatusInfo; +import io.seqera.tower.model.StudioUser; + +import java.io.PrintWriter; + +import static io.seqera.tower.cli.utils.FormatHelper.formatDataStudioStatus; +import static io.seqera.tower.cli.utils.FormatHelper.formatTime; + +public class DataStudiosView extends Response { + + public final DataStudioDto dataStudio; + public final String workspaceRef; + + public DataStudiosView(DataStudioDto dataStudioDto, String workspaceRef) { + this.dataStudio = dataStudioDto; + this.workspaceRef = workspaceRef; + } + + @Override + public void toString(PrintWriter out){ + out.println(ansi(String.format("%n @|bold DataStudio at workspace '%s'|@%n", workspaceRef))); + + DataStudioStatusInfo statusInfo = dataStudio.getStatusInfo(); + StudioUser studioUser = dataStudio.getUser(); + DataStudioConfiguration config = dataStudio.getConfiguration(); + TableList table = new TableList(out, 2); + table.setPrefix(" "); + table.addRow("SessionID", dataStudio.getSessionId()); + table.addRow("Name", dataStudio.getName()); + table.addRow("Status", formatDataStudioStatus(statusInfo == null ? null : statusInfo.getStatus())); + table.addRow("Status Last Update", statusInfo == null ? "NA" : formatTime(statusInfo.getLastUpdate())); + table.addRow("Studio URL", dataStudio.getStudioUrl()); + table.addRow("Description", dataStudio.getDescription()); + table.addRow("Created on", formatTime(dataStudio.getDateCreated())); + table.addRow("Created by", studioUser == null ? "NA" : String.format("%s | %s",studioUser.getUserName(), studioUser.getEmail())); + table.addRow("Template", dataStudio.getTemplate() == null ? "NA" : dataStudio.getTemplate().getRepository()); + table.addRow("Mounted Data", dataStudio.getMountedDataLinks() == null ? "NA" : dataStudio.getMountedDataLinks() + .stream().map(DataLinkDto::getResourceRef).collect(java.util.stream.Collectors.joining(", "))); + table.addRow("Compute environment", dataStudio.getComputeEnv() == null ? "NA" : dataStudio.getComputeEnv().getName()); + table.addRow("Region", dataStudio.getComputeEnv() == null ? "NA" : dataStudio.getComputeEnv().getRegion()); + table.addRow("GPU allocated", config == null ? "-" : String.valueOf(config.getGpu())); + table.addRow("CPU allocated", config == null ? "-" : String.valueOf(config.getCpu())); + table.addRow("Memory allocated", config == null ? "-" : String.valueOf(config.getMemory())); + table.addRow("Build reports", dataStudio.getWaveBuildUrl() == null ? "NA" : dataStudio.getWaveBuildUrl()); + + table.print(); + if (config != null && config.getCondaEnvironment() != null && !config.getCondaEnvironment().isEmpty()) { + out.println(String.format("%n Conda Environment:%n%n%s%n", config.getCondaEnvironment().replaceAll("(?m)^", " "))); + } + + } +} diff --git a/src/main/java/io/seqera/tower/cli/utils/FormatHelper.java b/src/main/java/io/seqera/tower/cli/utils/FormatHelper.java index cb8a78e1..912c41e2 100644 --- a/src/main/java/io/seqera/tower/cli/utils/FormatHelper.java +++ b/src/main/java/io/seqera/tower/cli/utils/FormatHelper.java @@ -254,6 +254,32 @@ public static String formatComputeEnvStatus(ComputeEnvStatus status) { } } + public static String formatDataStudioStatus(DataStudioStatus status) { + if (status == null) { + return "NA"; + } + + switch (status) { + case STARTING: + return ansi("@|fg(214) STARTING|@"); + case RUNNING: + return ansi("@|fg(green) RUNNING|@"); + case STOPPING: + return ansi("@|fg(214) STOPPING|@"); + case STOPPED: + return ansi("@|fg(214) STOPPED|@"); + case ERRORED: + return ansi("@|fg(red) ERRORED|@"); + case BUILDING: + return ansi("@|fg(214) BUILDING|@"); + case BUILDFAILED: + return ansi("@|fg(red) BUILDFAILED|@"); + default: + return status.toString(); + } + } + + public static String formatParticipantType(ParticipantType participantType) { if (participantType == null) { return "NA"; diff --git a/src/test/java/io/seqera/tower/cli/datastudios/DataStudiosCmdTest.java b/src/test/java/io/seqera/tower/cli/datastudios/DataStudiosCmdTest.java new file mode 100644 index 00000000..fff4b3ec --- /dev/null +++ b/src/test/java/io/seqera/tower/cli/datastudios/DataStudiosCmdTest.java @@ -0,0 +1,489 @@ +/* + * 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.datastudios; + +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.responses.datastudios.DataStudiosList; +import io.seqera.tower.cli.responses.datastudios.DataStudiosView; +import io.seqera.tower.cli.utils.PaginationInfo; +import io.seqera.tower.model.DataStudioDto; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; + +import java.util.Arrays; +import java.util.Collections; + +import org.mockserver.client.MockServerClient; +import org.mockserver.model.MediaType; + +import static io.seqera.tower.cli.utils.JsonHelper.parseJson; +import static org.mockserver.matchers.Times.exactly; +import static org.mockserver.model.HttpRequest.request; +import static org.mockserver.model.HttpResponse.response; + +public class DataStudiosCmdTest extends BaseCmdTest { + + @ParameterizedTest + @EnumSource(OutputType.class) + void testView(OutputType format, MockServerClient mock) throws JsonProcessingException { + 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/3e8370e7").withQueryStringParameter("workspaceId", "75887156211589"), exactly(1) + ).respond( + response().withStatusCode(200).withBody(loadResource("datastudios/datastudios_view_response")).withContentType(MediaType.APPLICATION_JSON) + ); + + ExecOut out = exec(format, mock, "studios", "view", "-w", "75887156211589", "-i" ,"3e8370e7"); + + assertOutput(format, out, new DataStudiosView(parseJson("{\n" + + " \"sessionId\": \"3e8370e7\",\n" + + " \"workspaceId\": 75887156211589,\n" + + " \"user\": {\n" + + " \"id\": 2345,\n" + + " \"userName\": \"John Doe\",\n" + + " \"email\": \"john@seqera.io\",\n" + + " \"avatar\": null\n" + + " },\n" + + " \"name\": \"studio-a66d\",\n" + + " \"description\": \"my first studio\",\n" + + " \"studioUrl\": \"https://a3e8370e7.dev-tower.com\",\n" + + " \"computeEnv\": {\n" + + " \"id\": \"3xkkzYH2nbD3nZjrzKm0oR\",\n" + + " \"name\": \"ce1\",\n" + + " \"platform\": \"aws-batch\",\n" + + " \"region\": \"us-east-2\"\n" + + " },\n" + + " \"template\": {\n" + + " \"repository\": \"cr.seqera.io/public/data-studio-vscode:1.93.1-snapshot\",\n" + + " \"icon\": \"vscode\"\n" + + " },\n" + + " \"configuration\": {\n" + + " \"gpu\": 0,\n" + + " \"cpu\": 2,\n" + + " \"memory\": 8192,\n" + + " \"mountData\": [\n" + + " \"v1-user-1ccf131810375d303bf0402dd8423433\"\n" + + " ],\n" + + " \"condaEnvironment\":null\n" + + " },\n" + + " \"dateCreated\": \"2024-12-19T06:49:24.893122+01:00\",\n" + + " \"lastUpdated\": \"2024-12-19T06:52:50.686822+01:00\",\n" + + " \"statusInfo\": {\n" + + " \"status\": \"running\",\n" + + " \"message\": \"\",\n" + + " \"lastUpdate\": \"2024-12-19T05:52:41.823Z\"\n" + + " },\n" + + " \"waveBuildUrl\": null,\n" + + " \"baseImage\": \"cr.seqera.io/public/data-studio-jupyter:4.2.5-snapshot\",\n" + + " \"mountedDataLinks\": [\n" + + " {\n" + + " \"id\": \"v1-user-1ccf131810375d303bf0402dd8423433\",\n" + + " \"name\": \"aaa-my-bucket\",\n" + + " \"resourceRef\": \"s3://aaa-my-bucket\",\n" + + " \"type\": \"bucket\",\n" + + " \"provider\": \"aws\",\n" + + " \"region\": \"us-east-2\"\n" + + " }\n" + + " ]\n" + + "}", DataStudioDto.class), "[organization1 / workspace1]" )); + } + + @ParameterizedTest + @EnumSource(OutputType.class) + void testList(OutputType format, MockServerClient mock) throws JsonProcessingException { + 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"), exactly(1) + ).respond( + response().withStatusCode(200).withBody(loadResource("datastudios/datastudios_list_response")).withContentType(MediaType.APPLICATION_JSON) + ); + + ExecOut out = exec(format, mock, "studios", "list", "-w", "75887156211589"); + + assertOutput(format, out, new DataStudiosList("[organization1 / workspace1]", Arrays.asList(parseJson(" {\n" + + " \"sessionId\": \"ddfd5e14\",\n" + + " \"workspaceId\": 75887156211589,\n" + + " \"parentCheckpoint\": null,\n" + + " \"user\": {\n" + + " \"id\": 1,\n" + + " \"userName\": \"samurai-jack\",\n" + + " \"email\": \"jack@seqera.io\",\n" + + " \"avatar\": null\n" + + " },\n" + + " \"name\": \"studio-7728\",\n" + + " \"description\": \"Local studio\",\n" + + " \"studioUrl\": \"http://addfd5e14.studio.localhost:9191\",\n" + + " \"computeEnv\": {\n" + + " \"id\": \"16esMgELkyQ3QPcHGNTiXQ\",\n" + + " \"name\": \"my-other-local-ce\",\n" + + " \"platform\": \"local-platform\",\n" + + " \"region\": null\n" + + " },\n" + + " \"template\": {\n" + + " \"repository\": \"cr.seqera.io/public/data-studio-jupyter:4.2.5-snapshot\",\n" + + " \"icon\": \"jupyter\"\n" + + " },\n" + + " \"configuration\": {\n" + + " \"gpu\": 0,\n" + + " \"cpu\": 2,\n" + + " \"memory\": 8192,\n" + + " \"mountData\": [],\n" + + " \"condaEnvironment\": null\n" + + " },\n" + + " \"dateCreated\": \"2025-01-14T11:51:05.393498Z\",\n" + + " \"lastUpdated\": \"2025-01-15T09:10:30.016752Z\",\n" + + " \"activeConnections\": [],\n" + + " \"statusInfo\": {\n" + + " \"status\": \"running\",\n" + + " \"message\": \"\",\n" + + " \"lastUpdate\": \"2025-01-15T09:10:30.016588Z\"\n" + + " },\n" + + " \"waveBuildUrl\": null,\n" + + " \"baseImage\": null,\n" + + " \"customImage\": false,\n" + + " \"progress\": null\n" + + " }", DataStudioDto.class), + parseJson("{\n" + + " \"sessionId\": \"c779bf09\",\n" + + " \"workspaceId\": 75887156211589,\n" + + " \"parentCheckpoint\": null,\n" + + " \"user\": {\n" + + " \"id\": 1,\n" + + " \"userName\": \"johnny-bravo\",\n" + + " \"email\": \"johnny@seqera.io\",\n" + + " \"avatar\": null\n" + + " },\n" + + " \"name\": \"studio-d456\",\n" + + " \"description\": null,\n" + + " \"studioUrl\": \"http://ac779bf09.studio.localhost:9191\",\n" + + " \"computeEnv\": {\n" + + " \"id\": \"61DYXYj3XQAYbJIHrI1XSg\",\n" + + " \"name\": \"my-local-ce\",\n" + + " \"platform\": \"local-platform\",\n" + + " \"region\": null\n" + + " },\n" + + " \"template\": {\n" + + " \"repository\": \"cr.seqera.io/public/data-studio-vscode:1.93.1-snapshot\",\n" + + " \"icon\": \"vscode\"\n" + + " },\n" + + " \"configuration\": {\n" + + " \"gpu\": 0,\n" + + " \"cpu\": 2,\n" + + " \"memory\": 8192,\n" + + " \"mountData\": [],\n" + + " \"condaEnvironment\": null\n" + + " },\n" + + " \"dateCreated\": \"2025-01-10T17:26:36.83703Z\",\n" + + " \"lastUpdated\": \"2025-01-12T03:00:30.014415Z\",\n" + + " \"activeConnections\": [],\n" + + " \"statusInfo\": {\n" + + " \"status\": \"errored\",\n" + + " \"message\": \"\",\n" + + " \"lastUpdate\": \"2025-01-12T03:00:30.010738Z\"\n" + + " },\n" + + " \"waveBuildUrl\": null,\n" + + " \"baseImage\": null,\n" + + " \"customImage\": false,\n" + + " \"progress\": null\n" + + " }", DataStudioDto.class) + ), null)); + } + + @ParameterizedTest + @EnumSource(OutputType.class) + void testListWithOffset(OutputType format, MockServerClient mock) throws JsonProcessingException { + 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("offset", "1") + .withQueryStringParameter("max", "2"), exactly(1) + ).respond( + response().withStatusCode(200).withBody(loadResource("datastudios/datastudios_list_response")).withContentType(MediaType.APPLICATION_JSON) + ); + + ExecOut out = exec(format, mock, "studios", "list", "-w", "75887156211589", "--offset", "1", "--max", "2"); + + assertOutput(format, out, new DataStudiosList("[organization1 / workspace1]", Arrays.asList(parseJson(" {\n" + + " \"sessionId\": \"ddfd5e14\",\n" + + " \"workspaceId\": 75887156211589,\n" + + " \"parentCheckpoint\": null,\n" + + " \"user\": {\n" + + " \"id\": 1,\n" + + " \"userName\": \"samurai-jack\",\n" + + " \"email\": \"jack@seqera.io\",\n" + + " \"avatar\": null\n" + + " },\n" + + " \"name\": \"studio-7728\",\n" + + " \"description\": \"Local studio\",\n" + + " \"studioUrl\": \"http://addfd5e14.studio.localhost:9191\",\n" + + " \"computeEnv\": {\n" + + " \"id\": \"16esMgELkyQ3QPcHGNTiXQ\",\n" + + " \"name\": \"my-other-local-ce\",\n" + + " \"platform\": \"local-platform\",\n" + + " \"region\": null\n" + + " },\n" + + " \"template\": {\n" + + " \"repository\": \"cr.seqera.io/public/data-studio-jupyter:4.2.5-snapshot\",\n" + + " \"icon\": \"jupyter\"\n" + + " },\n" + + " \"configuration\": {\n" + + " \"gpu\": 0,\n" + + " \"cpu\": 2,\n" + + " \"memory\": 8192,\n" + + " \"mountData\": [],\n" + + " \"condaEnvironment\": null\n" + + " },\n" + + " \"dateCreated\": \"2025-01-14T11:51:05.393498Z\",\n" + + " \"lastUpdated\": \"2025-01-15T09:10:30.016752Z\",\n" + + " \"activeConnections\": [],\n" + + " \"statusInfo\": {\n" + + " \"status\": \"running\",\n" + + " \"message\": \"\",\n" + + " \"lastUpdate\": \"2025-01-15T09:10:30.016588Z\"\n" + + " },\n" + + " \"waveBuildUrl\": null,\n" + + " \"baseImage\": null,\n" + + " \"customImage\": false,\n" + + " \"progress\": null\n" + + " }", DataStudioDto.class), + parseJson("{\n" + + " \"sessionId\": \"c779bf09\",\n" + + " \"workspaceId\": 75887156211589,\n" + + " \"parentCheckpoint\": null,\n" + + " \"user\": {\n" + + " \"id\": 1,\n" + + " \"userName\": \"johnny-bravo\",\n" + + " \"email\": \"johnny@seqera.io\",\n" + + " \"avatar\": null\n" + + " },\n" + + " \"name\": \"studio-d456\",\n" + + " \"description\": null,\n" + + " \"studioUrl\": \"http://ac779bf09.studio.localhost:9191\",\n" + + " \"computeEnv\": {\n" + + " \"id\": \"61DYXYj3XQAYbJIHrI1XSg\",\n" + + " \"name\": \"my-local-ce\",\n" + + " \"platform\": \"local-platform\",\n" + + " \"region\": null\n" + + " },\n" + + " \"template\": {\n" + + " \"repository\": \"cr.seqera.io/public/data-studio-vscode:1.93.1-snapshot\",\n" + + " \"icon\": \"vscode\"\n" + + " },\n" + + " \"configuration\": {\n" + + " \"gpu\": 0,\n" + + " \"cpu\": 2,\n" + + " \"memory\": 8192,\n" + + " \"mountData\": [],\n" + + " \"condaEnvironment\": null\n" + + " },\n" + + " \"dateCreated\": \"2025-01-10T17:26:36.83703Z\",\n" + + " \"lastUpdated\": \"2025-01-12T03:00:30.014415Z\",\n" + + " \"activeConnections\": [],\n" + + " \"statusInfo\": {\n" + + " \"status\": \"errored\",\n" + + " \"message\": \"\",\n" + + " \"lastUpdate\": \"2025-01-12T03:00:30.010738Z\"\n" + + " },\n" + + " \"waveBuildUrl\": null,\n" + + " \"baseImage\": null,\n" + + " \"customImage\": false,\n" + + " \"progress\": null\n" + + " }", DataStudioDto.class) + ), PaginationInfo.from(1, 2, null, 2L))); + } + + + @ParameterizedTest + @EnumSource(OutputType.class) + void testListWithPage(OutputType format, MockServerClient mock) throws JsonProcessingException { + 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("offset", "0") + .withQueryStringParameter("max", "1"), exactly(1) + ).respond( + response().withStatusCode(200).withBody(loadResource("datastudios/datastudios_list_filtered_response")).withContentType(MediaType.APPLICATION_JSON) + ); + + ExecOut out = exec(format, mock, "studios", "list", "-w", "75887156211589", "--page", "1", "--max", "1"); + + assertOutput(format, out, new DataStudiosList("[organization1 / workspace1]", Arrays.asList(parseJson(" {\n" + + " \"sessionId\": \"ddfd5e14\",\n" + + " \"workspaceId\": 75887156211589,\n" + + " \"parentCheckpoint\": null,\n" + + " \"user\": {\n" + + " \"id\": 1,\n" + + " \"userName\": \"samurai-jack\",\n" + + " \"email\": \"jack@seqera.io\",\n" + + " \"avatar\": null\n" + + " },\n" + + " \"name\": \"studio-7728\",\n" + + " \"description\": \"Local studio\",\n" + + " \"studioUrl\": \"http://addfd5e14.studio.localhost:9191\",\n" + + " \"computeEnv\": {\n" + + " \"id\": \"16esMgELkyQ3QPcHGNTiXQ\",\n" + + " \"name\": \"my-other-local-ce\",\n" + + " \"platform\": \"local-platform\",\n" + + " \"region\": null\n" + + " },\n" + + " \"template\": {\n" + + " \"repository\": \"cr.seqera.io/public/data-studio-jupyter:4.2.5-snapshot\",\n" + + " \"icon\": \"jupyter\"\n" + + " },\n" + + " \"configuration\": {\n" + + " \"gpu\": 0,\n" + + " \"cpu\": 2,\n" + + " \"memory\": 8192,\n" + + " \"mountData\": [],\n" + + " \"condaEnvironment\": null\n" + + " },\n" + + " \"dateCreated\": \"2025-01-14T11:51:05.393498Z\",\n" + + " \"lastUpdated\": \"2025-01-15T09:10:30.016752Z\",\n" + + " \"activeConnections\": [],\n" + + " \"statusInfo\": {\n" + + " \"status\": \"running\",\n" + + " \"message\": \"\",\n" + + " \"lastUpdate\": \"2025-01-15T09:10:30.016588Z\"\n" + + " },\n" + + " \"waveBuildUrl\": null,\n" + + " \"baseImage\": null,\n" + + " \"customImage\": false,\n" + + " \"progress\": null\n" + + " }", DataStudioDto.class) + ), PaginationInfo.from(null, 1, 1, 2L))); + } + + @ParameterizedTest + @EnumSource(OutputType.class) + void testListWithFilter(OutputType format, MockServerClient mock) throws JsonProcessingException { + 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("search", "status:running"), exactly(1) + ).respond( + response().withStatusCode(200).withBody(loadResource("datastudios/datastudios_list_filtered_response")).withContentType(MediaType.APPLICATION_JSON) + ); + + ExecOut out = exec(format, mock, "studios", "list", "-w", "75887156211589", "--filter", "status:running"); + + assertOutput(format, out, new DataStudiosList("[organization1 / workspace1]", Collections.singletonList(parseJson(" {\n" + + " \"sessionId\": \"ddfd5e14\",\n" + + " \"workspaceId\": 75887156211589,\n" + + " \"parentCheckpoint\": null,\n" + + " \"user\": {\n" + + " \"id\": 1,\n" + + " \"userName\": \"samurai-jack\",\n" + + " \"email\": \"jack@seqera.io\",\n" + + " \"avatar\": null\n" + + " },\n" + + " \"name\": \"studio-7728\",\n" + + " \"description\": \"Local studio\",\n" + + " \"studioUrl\": \"http://addfd5e14.studio.localhost:9191\",\n" + + " \"computeEnv\": {\n" + + " \"id\": \"16esMgELkyQ3QPcHGNTiXQ\",\n" + + " \"name\": \"my-other-local-ce\",\n" + + " \"platform\": \"local-platform\",\n" + + " \"region\": null\n" + + " },\n" + + " \"template\": {\n" + + " \"repository\": \"cr.seqera.io/public/data-studio-jupyter:4.2.5-snapshot\",\n" + + " \"icon\": \"jupyter\"\n" + + " },\n" + + " \"configuration\": {\n" + + " \"gpu\": 0,\n" + + " \"cpu\": 2,\n" + + " \"memory\": 8192,\n" + + " \"mountData\": [],\n" + + " \"condaEnvironment\": null\n" + + " },\n" + + " \"dateCreated\": \"2025-01-14T11:51:05.393498Z\",\n" + + " \"lastUpdated\": \"2025-01-15T09:10:30.016752Z\",\n" + + " \"activeConnections\": [],\n" + + " \"statusInfo\": {\n" + + " \"status\": \"running\",\n" + + " \"message\": \"\",\n" + + " \"lastUpdate\": \"2025-01-15T09:10:30.016588Z\"\n" + + " },\n" + + " \"waveBuildUrl\": null,\n" + + " \"baseImage\": null,\n" + + " \"customImage\": false,\n" + + " \"progress\": null\n" + + " }", DataStudioDto.class) + ), null)); + } +} diff --git a/src/test/resources/runcmd/datastudios/datastudios_list_filtered_response.json b/src/test/resources/runcmd/datastudios/datastudios_list_filtered_response.json new file mode 100644 index 00000000..8edb8d50 --- /dev/null +++ b/src/test/resources/runcmd/datastudios/datastudios_list_filtered_response.json @@ -0,0 +1,48 @@ +{ + "studios": [ + { + "sessionId": "ddfd5e14", + "workspaceId": 75887156211589, + "parentCheckpoint": null, + "user": { + "id": 1, + "userName": "samurai-jack", + "email": "jack@seqera.io", + "avatar": null + }, + "name": "studio-7728", + "description": "Local studio", + "studioUrl": "http://addfd5e14.studio.localhost:9191", + "computeEnv": { + "id": "16esMgELkyQ3QPcHGNTiXQ", + "name": "my-other-local-ce", + "platform": "local-platform", + "region": null + }, + "template": { + "repository": "cr.seqera.io/public/data-studio-jupyter:4.2.5-snapshot", + "icon": "jupyter" + }, + "configuration": { + "gpu": 0, + "cpu": 2, + "memory": 8192, + "mountData": [], + "condaEnvironment": null + }, + "dateCreated": "2025-01-14T11:51:05.393498Z", + "lastUpdated": "2025-01-15T09:10:30.016752Z", + "activeConnections": [], + "statusInfo": { + "status": "running", + "message": "", + "lastUpdate": "2025-01-15T09:10:30.016588Z" + }, + "waveBuildUrl": null, + "baseImage": null, + "customImage": false, + "progress": null + } + ], + "totalSize": 2 +} \ No newline at end of file diff --git a/src/test/resources/runcmd/datastudios/datastudios_list_response.json b/src/test/resources/runcmd/datastudios/datastudios_list_response.json new file mode 100644 index 00000000..6a9e35b2 --- /dev/null +++ b/src/test/resources/runcmd/datastudios/datastudios_list_response.json @@ -0,0 +1,91 @@ +{ + "studios": [ + { + "sessionId": "ddfd5e14", + "workspaceId": 75887156211589, + "parentCheckpoint": null, + "user": { + "id": 1, + "userName": "samurai-jack", + "email": "jack@seqera.io", + "avatar": null + }, + "name": "studio-7728", + "description": "Local studio", + "studioUrl": "http://addfd5e14.studio.localhost:9191", + "computeEnv": { + "id": "16esMgELkyQ3QPcHGNTiXQ", + "name": "my-other-local-ce", + "platform": "local-platform", + "region": null + }, + "template": { + "repository": "cr.seqera.io/public/data-studio-jupyter:4.2.5-snapshot", + "icon": "jupyter" + }, + "configuration": { + "gpu": 0, + "cpu": 2, + "memory": 8192, + "mountData": [], + "condaEnvironment": null + }, + "dateCreated": "2025-01-14T11:51:05.393498Z", + "lastUpdated": "2025-01-15T09:10:30.016752Z", + "activeConnections": [], + "statusInfo": { + "status": "running", + "message": "", + "lastUpdate": "2025-01-15T09:10:30.016588Z" + }, + "waveBuildUrl": null, + "baseImage": null, + "customImage": false, + "progress": null + }, + { + "sessionId": "c779bf09", + "workspaceId": 75887156211589, + "parentCheckpoint": null, + "user": { + "id": 1, + "userName": "johnny-bravo", + "email": "johnny@seqera.io", + "avatar": null + }, + "name": "studio-d456", + "description": null, + "studioUrl": "http://ac779bf09.studio.localhost:9191", + "computeEnv": { + "id": "61DYXYj3XQAYbJIHrI1XSg", + "name": "my-local-ce", + "platform": "local-platform", + "region": null + }, + "template": { + "repository": "cr.seqera.io/public/data-studio-vscode:1.93.1-snapshot", + "icon": "vscode" + }, + "configuration": { + "gpu": 0, + "cpu": 2, + "memory": 8192, + "mountData": [], + "condaEnvironment": null + }, + "dateCreated": "2025-01-10T17:26:36.83703Z", + "lastUpdated": "2025-01-12T03:00:30.014415Z", + "activeConnections": [], + "statusInfo": { + "status": "errored", + "message": "", + "lastUpdate": "2025-01-12T03:00:30.010738Z" + }, + "waveBuildUrl": null, + "baseImage": null, + "customImage": false, + "progress": null + } + ], + "totalSize": 2 +} \ No newline at end of file diff --git a/src/test/resources/runcmd/datastudios/datastudios_view_response.json b/src/test/resources/runcmd/datastudios/datastudios_view_response.json new file mode 100644 index 00000000..419023eb --- /dev/null +++ b/src/test/resources/runcmd/datastudios/datastudios_view_response.json @@ -0,0 +1,51 @@ +{ + "sessionId": "3e8370e7", + "workspaceId": 75887156211589, + "user": { + "id": 2345, + "userName": "John Doe", + "email": "john@seqera.io", + "avatar": null + }, + "name": "studio-a66d", + "description": "my first studio", + "studioUrl": "https://a3e8370e7.dev-tower.com", + "computeEnv": { + "id": "3xkkzYH2nbD3nZjrzKm0oR", + "name": "ce1", + "platform": "aws-batch", + "region": "us-east-2" + }, + "template": { + "repository": "cr.seqera.io/public/data-studio-vscode:1.93.1-snapshot", + "icon": "vscode" + }, + "configuration": { + "gpu": 0, + "cpu": 2, + "memory": 8192, + "mountData": [ + "v1-user-1ccf131810375d303bf0402dd8423433" + ], + "condaEnvironment":null + }, + "dateCreated": "2024-12-19T06:49:24.893122+01:00", + "lastUpdated": "2024-12-19T06:52:50.686822+01:00", + "statusInfo": { + "status": "running", + "message": "", + "lastUpdate": "2024-12-19T05:52:41.823Z" + }, + "waveBuildUrl": null, + "baseImage": "cr.seqera.io/public/data-studio-jupyter:4.2.5-snapshot", + "mountedDataLinks": [ + { + "id": "v1-user-1ccf131810375d303bf0402dd8423433", + "name": "aaa-my-bucket", + "resourceRef": "s3://aaa-my-bucket", + "type": "bucket", + "provider": "aws", + "region": "us-east-2" + } + ] +} \ No newline at end of file