Skip to content

Commit bcb3d9d

Browse files
feat: PLAT-1152 / add data studios 'list' command (#475)
* feat: adding data studios view command * fix: adding some missing reflection metadata * fix: adding some more reflection metadata * fix: yet more reflection metadata * fix: missing reflection metadata * fix: update sdk and extend studios view command * fix: missing reflection metadata * fix: missing reflection metadata again * fix: newline formatting * fix: removing conda env from test * remove duplicated reflection metadata * PLAT-1152 - Add Data studios CLI list command * PLAT-1152 - Update reflect-config.json for new data studio List command * PLAT-1152 - Update reflect-config.json for new data studio List command * PLAT-1152 - Add more fine grain API error handling for data studios * PLAT-1152 - minor cleanup tweaks --------- Co-authored-by: endre-seqera <[email protected]>
1 parent e9fa350 commit bcb3d9d

17 files changed

+1286
-8
lines changed

.gitignore

+4-1
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,7 @@ build
77
**/build-info.properties
88

99
# Location for unshared files
10-
.user/
10+
.user/
11+
12+
13+
*.iml

build.gradle

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ dependencies {
2727
implementation 'org.slf4j:slf4j-api:1.7.36'
2828
implementation 'ch.qos.logback:logback-core:1.2.11'
2929
implementation 'ch.qos.logback:logback-classic:1.2.11'
30-
implementation 'io.seqera.tower:tower-java-sdk:1.9.7'
30+
implementation 'io.seqera.tower:tower-java-sdk:1.9.9'
3131
implementation 'info.picocli:picocli:4.6.3'
3232
implementation 'org.apache.commons:commons-compress:1.22'
3333
implementation 'org.tukaani:xz:1.9'

conf/reflect-config.json

+160-6
Large diffs are not rendered by default.

src/main/java/io/seqera/tower/cli/Tower.java

+2
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import io.seqera.tower.cli.commands.ComputeEnvsCmd;
2828
import io.seqera.tower.cli.commands.CredentialsCmd;
2929
import io.seqera.tower.cli.commands.DataLinksCmd;
30+
import io.seqera.tower.cli.commands.DataStudiosCmd;
3031
import io.seqera.tower.cli.commands.DatasetsCmd;
3132
import io.seqera.tower.cli.commands.InfoCmd;
3233
import io.seqera.tower.cli.commands.LaunchCmd;
@@ -60,6 +61,7 @@
6061
ComputeEnvsCmd.class,
6162
CredentialsCmd.class,
6263
DataLinksCmd.class,
64+
DataStudiosCmd.class,
6365
DatasetsCmd.class,
6466
GenerateCompletion.class,
6567
InfoCmd.class,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
* Copyright 2021-2023, Seqera.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
*/
17+
18+
package io.seqera.tower.cli.commands;
19+
20+
import io.seqera.tower.cli.commands.datastudios.ListCmd;
21+
import io.seqera.tower.cli.commands.datastudios.ViewCmd;
22+
import picocli.CommandLine;
23+
24+
@CommandLine.Command(
25+
name = "studios",
26+
description = "Manage data studios.",
27+
subcommands = {
28+
ViewCmd.class,
29+
ListCmd.class,
30+
}
31+
)
32+
public class DataStudiosCmd extends AbstractRootCmd {
33+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* Copyright 2021-2023, Seqera.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
*/
17+
18+
package io.seqera.tower.cli.commands.datastudios;
19+
20+
import io.seqera.tower.ApiException;
21+
import io.seqera.tower.cli.commands.AbstractApiCmd;
22+
import io.seqera.tower.model.DataStudioDto;
23+
24+
public class AbstractStudiosCmd extends AbstractApiCmd {
25+
26+
protected DataStudioDto fetchDataStudio(DataStudioRefOptions dataStudioRefOptions, Long wspId) throws ApiException {
27+
return api().describeDataStudio(dataStudioRefOptions.dataStudio.sessionId, wspId);
28+
}
29+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
* Copyright 2021-2023, Seqera.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
*/
17+
18+
package io.seqera.tower.cli.commands.datastudios;
19+
20+
import picocli.CommandLine;
21+
22+
public class DataStudioRefOptions {
23+
24+
@CommandLine.ArgGroup(multiplicity = "1")
25+
public DataStudioRef dataStudio;
26+
27+
public static class DataStudioRef {
28+
@CommandLine.Option(names = {"-i", "--id"}, description = "DataStudio session ID.")
29+
public String sessionId;
30+
}
31+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/*
2+
* Copyright 2021-2023, Seqera.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
*/
17+
18+
package io.seqera.tower.cli.commands.datastudios;
19+
20+
import io.seqera.tower.ApiException;
21+
import io.seqera.tower.cli.commands.global.PaginationOptions;
22+
import io.seqera.tower.cli.commands.global.WorkspaceOptionalOptions;
23+
import io.seqera.tower.cli.exceptions.TowerException;
24+
import io.seqera.tower.cli.exceptions.WorkspaceNotFoundException;
25+
import io.seqera.tower.cli.responses.Response;
26+
import io.seqera.tower.cli.responses.datastudios.DataStudiosList;
27+
import io.seqera.tower.cli.utils.PaginationInfo;
28+
import io.seqera.tower.model.DataStudioListResponse;
29+
import picocli.CommandLine;
30+
import picocli.CommandLine.Command;
31+
32+
import java.io.IOException;
33+
34+
@Command(
35+
name = "list",
36+
description = "List workspace data studios."
37+
)
38+
public class ListCmd extends AbstractStudiosCmd {
39+
40+
@CommandLine.Mixin
41+
public WorkspaceOptionalOptions workspace;
42+
43+
@CommandLine.Option(names = {"-f", "--filter"}, description = "Optional filter criteria, allowing free text search on name and templateUrl " +
44+
"and keywords: `userName`, `computeEnvName` and `status`. Example keyword usage: -f status:RUNNING")
45+
public String filter;
46+
47+
@CommandLine.Mixin
48+
PaginationOptions paginationOptions;
49+
50+
@Override
51+
protected Response exec() throws ApiException, IOException {
52+
Long wspId = workspaceId(workspace.workspace);
53+
Integer max = PaginationOptions.getMax(paginationOptions);
54+
Integer offset = PaginationOptions.getOffset(paginationOptions, max);
55+
56+
DataStudioListResponse response = new DataStudioListResponse();
57+
58+
try {
59+
response = api().listDataStudios(wspId, filter, max, offset);
60+
} catch (ApiException e) {
61+
if (e.getCode() == 404){
62+
throw new WorkspaceNotFoundException(wspId);
63+
}
64+
if (e.getCode() == 403) {
65+
throw new TowerException(String.format("User not entitled to %s workspace", wspId));
66+
}
67+
throw e;
68+
}
69+
70+
return new DataStudiosList(workspaceRef(wspId), response.getStudios(), PaginationInfo.from(paginationOptions, response.getTotalSize()));
71+
}
72+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*
2+
* Copyright 2021-2023, Seqera.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
*/
17+
18+
package io.seqera.tower.cli.commands.datastudios;
19+
20+
import io.seqera.tower.ApiException;
21+
import io.seqera.tower.cli.commands.global.WorkspaceOptionalOptions;
22+
import io.seqera.tower.cli.exceptions.DataStudioNotFoundException;
23+
import io.seqera.tower.cli.exceptions.TowerException;
24+
import io.seqera.tower.cli.responses.Response;
25+
import io.seqera.tower.cli.responses.datastudios.DataStudiosView;
26+
import io.seqera.tower.model.DataStudioDto;
27+
import picocli.CommandLine;
28+
29+
@CommandLine.Command(
30+
name = "view",
31+
description = "View data studio."
32+
)
33+
public class ViewCmd extends AbstractStudiosCmd {
34+
35+
@CommandLine.Mixin
36+
public WorkspaceOptionalOptions workspace;
37+
38+
@CommandLine.Mixin
39+
public DataStudioRefOptions dataStudioRefOptions;
40+
41+
@Override
42+
protected Response exec() throws ApiException {
43+
Long wspId = workspaceId(workspace.workspace);
44+
45+
try {
46+
DataStudioDto dataStudioDto = fetchDataStudio(dataStudioRefOptions, wspId);
47+
48+
return new DataStudiosView(dataStudioDto, workspaceRef(wspId));
49+
} catch (ApiException e) {
50+
if (e.getCode() == 404) {
51+
throw new DataStudioNotFoundException(dataStudioRefOptions.dataStudio.sessionId, wspId);
52+
}
53+
if (e.getCode() == 403) {
54+
throw new TowerException(String.format("User not entitled to view studio '%s' at %s workspace", dataStudioRefOptions.dataStudio.sessionId, wspId));
55+
}
56+
throw e;
57+
}
58+
}
59+
60+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* Copyright 2021-2023, Seqera.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
*/
17+
18+
package io.seqera.tower.cli.exceptions;
19+
20+
public class DataStudioNotFoundException extends TowerException {
21+
22+
public DataStudioNotFoundException( String sessionId, Long workspaceId) {
23+
super(String.format("DataStudio '%s' not found at workspace '%s'", sessionId, workspaceId));
24+
}
25+
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/*
2+
* Copyright 2021-2023, Seqera.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
*/
17+
18+
package io.seqera.tower.cli.responses.datastudios;
19+
20+
import java.io.PrintWriter;
21+
import java.util.ArrayList;
22+
import java.util.List;
23+
import javax.annotation.Nullable;
24+
25+
import com.fasterxml.jackson.annotation.JsonIgnore;
26+
import io.seqera.tower.cli.responses.Response;
27+
import io.seqera.tower.cli.utils.PaginationInfo;
28+
import io.seqera.tower.cli.utils.TableList;
29+
import io.seqera.tower.model.DataStudioDto;
30+
import io.seqera.tower.model.DataStudioStatusInfo;
31+
import io.seqera.tower.model.StudioUser;
32+
33+
import static io.seqera.tower.cli.utils.FormatHelper.formatDataStudioStatus;
34+
35+
public class DataStudiosList extends Response {
36+
37+
public final String workspaceRef;
38+
public final List<DataStudioDto> studios;
39+
40+
@JsonIgnore
41+
@Nullable
42+
private final PaginationInfo paginationInfo;
43+
44+
public DataStudiosList(String workspaceRef, List<DataStudioDto> studios, @Nullable PaginationInfo paginationInfo) {
45+
this.workspaceRef = workspaceRef;
46+
this.studios = studios;
47+
this.paginationInfo = paginationInfo;
48+
}
49+
50+
@Override
51+
public void toString(PrintWriter out) {
52+
53+
out.println(ansi(String.format("%n @|bold Data Studios at %s workspace:|@%n", workspaceRef)));
54+
55+
if (studios.isEmpty()) {
56+
out.println(ansi(" @|yellow No data studios found|@"));
57+
return;
58+
}
59+
60+
List<String> descriptions = new ArrayList<>(List.of("ID", "Name", "Description", "User", "Status"));
61+
62+
TableList table = new TableList(out, descriptions.size(), descriptions.toArray(new String[descriptions.size()])).sortBy(0);
63+
table.setPrefix(" ");
64+
65+
studios.forEach(studio -> {
66+
67+
DataStudioStatusInfo statusInfo = studio.getStatusInfo();
68+
StudioUser user = studio.getUser();
69+
List<String> rows = new ArrayList<>(List.of(
70+
studio.getSessionId() == null ? "" : studio.getSessionId(),
71+
studio.getName() == null ? "" : studio.getName(),
72+
studio.getDescription() == null ? "" : studio.getDescription(),
73+
user == null ? "" : user.getUserName(),
74+
formatDataStudioStatus(statusInfo == null ? null : statusInfo.getStatus())
75+
));
76+
77+
table.addRow(rows.toArray(new String[rows.size()]));
78+
});
79+
80+
table.print();
81+
82+
PaginationInfo.addFooter(out, paginationInfo);
83+
84+
out.println("");
85+
}
86+
87+
}

0 commit comments

Comments
 (0)