diff --git a/conf/reflect-config.json b/conf/reflect-config.json index e2887db9..96122234 100644 --- a/conf/reflect-config.json +++ b/conf/reflect-config.json @@ -1200,6 +1200,18 @@ "queryAllDeclaredMethods":true, "methods":[{"name":"","parameterTypes":[] }] }, +{ + "name":"io.seqera.tower.cli.commands.datasets.DatasetMultiRefOptions", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"io.seqera.tower.cli.commands.datasets.DatasetMultiRefOptions$DatasetMultiRef", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"io.seqera.tower.cli.commands.datasets.DatasetRefOptions", "allDeclaredFields":true, @@ -1224,12 +1236,30 @@ "queryAllDeclaredMethods":true, "methods":[{"name":"","parameterTypes":[] }] }, +{ + "name":"io.seqera.tower.cli.commands.datasets.HideCmd", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"io.seqera.tower.cli.commands.datasets.LabelsCmd", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"io.seqera.tower.cli.commands.datasets.ListCmd", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"","parameterTypes":[] }] }, +{ + "name":"io.seqera.tower.cli.commands.datasets.ShowCmd", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"io.seqera.tower.cli.commands.datasets.UpdateCmd", "allDeclaredFields":true, @@ -2153,6 +2183,12 @@ "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, +{ + "name":"io.seqera.tower.cli.responses.datasets.DatasetsVisibility", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true +}, { "name":"io.seqera.tower.cli.responses.labels.DeleteLabelsResponse", "allDeclaredFields":true, diff --git a/src/main/java/io/seqera/tower/cli/commands/DatasetsCmd.java b/src/main/java/io/seqera/tower/cli/commands/DatasetsCmd.java index f9bb73d0..fbb568f4 100644 --- a/src/main/java/io/seqera/tower/cli/commands/DatasetsCmd.java +++ b/src/main/java/io/seqera/tower/cli/commands/DatasetsCmd.java @@ -20,7 +20,10 @@ import io.seqera.tower.cli.commands.datasets.AddCmd; import io.seqera.tower.cli.commands.datasets.DeleteCmd; import io.seqera.tower.cli.commands.datasets.DownloadCmd; +import io.seqera.tower.cli.commands.datasets.HideCmd; +import io.seqera.tower.cli.commands.datasets.LabelsCmd; import io.seqera.tower.cli.commands.datasets.ListCmd; +import io.seqera.tower.cli.commands.datasets.ShowCmd; import io.seqera.tower.cli.commands.datasets.UpdateCmd; import io.seqera.tower.cli.commands.datasets.UrlCmd; import io.seqera.tower.cli.commands.datasets.ViewCmd; @@ -33,7 +36,10 @@ AddCmd.class, DeleteCmd.class, DownloadCmd.class, + HideCmd.class, + LabelsCmd.class, ListCmd.class, + ShowCmd.class, ViewCmd.class, UpdateCmd.class, UrlCmd.class, diff --git a/src/main/java/io/seqera/tower/cli/commands/datasets/AbstractDatasetsCmd.java b/src/main/java/io/seqera/tower/cli/commands/datasets/AbstractDatasetsCmd.java index a1b752dd..30127c1f 100644 --- a/src/main/java/io/seqera/tower/cli/commands/datasets/AbstractDatasetsCmd.java +++ b/src/main/java/io/seqera/tower/cli/commands/datasets/AbstractDatasetsCmd.java @@ -26,6 +26,7 @@ import io.seqera.tower.model.ListDatasetsResponse; import picocli.CommandLine; +import java.util.ArrayList; import java.util.Comparator; import java.util.List; import java.util.Objects; @@ -35,7 +36,7 @@ public abstract class AbstractDatasetsCmd extends AbstractApiCmd { protected DatasetDto datasetByName(Long workspaceId, String datasetName) throws ApiException { - ListDatasetsResponse listDatasetsResponse = datasetsApi().listDatasets(workspaceId); + ListDatasetsResponse listDatasetsResponse = datasetsApi().listDatasetsV2(workspaceId, null, null, datasetName, null, null, "all", List.of()); if (listDatasetsResponse == null || listDatasetsResponse.getDatasets() == null) { throw new DatasetNotFoundException(workspaceRef(workspaceId)); @@ -49,36 +50,14 @@ protected DatasetDto datasetByName(Long workspaceId, String datasetName) throws throw new DatasetNotFoundException(datasetName, workspaceRef(workspaceId)); } - return datasetList.stream().findFirst().orElse(null); - } - - protected List searchByName(Long workspaceId, String datasetName) throws ApiException { - ListDatasetsResponse listDatasetsResponse = datasetsApi().listDatasets(workspaceId); - - if (datasetName == null) { - return listDatasetsResponse.getDatasets(); - } - - if (listDatasetsResponse == null || listDatasetsResponse.getDatasets() == null) { - throw new DatasetNotFoundException(workspaceRef(workspaceId)); - } - - List datasetList = listDatasetsResponse.getDatasets().stream() - .filter(it -> it.getName().startsWith(datasetName)) - .collect(Collectors.toList()); - - if (datasetList.isEmpty()) { - throw new DatasetNotFoundException(datasetName, workspaceRef(workspaceId)); - } - - return datasetList; + return datasetList.get(0); } protected DatasetDto fetchDescribeDatasetResponse(DatasetRefOptions datasetRefOptions, Long wspId) throws ApiException { DatasetDto response; if (datasetRefOptions.dataset.datasetId != null) { - response = datasetsApi().describeDataset(wspId, datasetRefOptions.dataset.datasetId, List.of()).getDataset(); + response = datasetsApi().describeDatasetV2(datasetRefOptions.dataset.datasetId, wspId, List.of()).getDataset(); } else { response = datasetByName(wspId, datasetRefOptions.dataset.datasetName); } @@ -89,7 +68,7 @@ protected DatasetDto fetchDescribeDatasetResponse(DatasetRefOptions datasetRefOp protected DatasetVersionDto fetchDatasetVersion(Long wspId, String datasetId, String datasetMediaType, Long version) throws ApiException { DatasetVersionDto datasetVersion; - ListDatasetVersionsResponse listDatasetVersionsResponse = datasetsApi().listDatasetVersions(wspId, datasetId, datasetMediaType); + ListDatasetVersionsResponse listDatasetVersionsResponse = datasetsApi().listDatasetVersionsV2(datasetId, wspId, datasetMediaType); if (listDatasetVersionsResponse == null || listDatasetVersionsResponse.getVersions() == null) { throw new TowerException(String.format("No versions were found for dataset %s", datasetId)); @@ -129,13 +108,26 @@ protected String getDatasetRef(DatasetRefOptions datasetRefOptions) { return datasetRefOptions.dataset.datasetName != null ? datasetRefOptions.dataset.datasetName : datasetRefOptions.dataset.datasetId; } + protected List resolveDatasetIds(DatasetMultiRefOptions options, Long wspId) throws ApiException { + List ids = new ArrayList<>(); + if (options.dataset.datasetIds != null) { + ids.addAll(options.dataset.datasetIds); + } + if (options.dataset.datasetNames != null) { + for (String name : options.dataset.datasetNames) { + ids.add(datasetByName(wspId, name).getId()); + } + } + return ids; + } + protected void deleteDatasetByName(String datasetName, Long wspId) throws DatasetNotFoundException, ApiException { DatasetDto response = datasetByName(wspId, datasetName); deleteDatasetById(response.getId(), wspId); } protected void deleteDatasetById(String datasetId, Long wspId) throws DatasetNotFoundException, ApiException { - datasetsApi().deleteDataset(wspId, datasetId); + datasetsApi().deleteDatasetV2(datasetId, wspId); } diff --git a/src/main/java/io/seqera/tower/cli/commands/datasets/AddCmd.java b/src/main/java/io/seqera/tower/cli/commands/datasets/AddCmd.java index 3070c12d..fa172980 100644 --- a/src/main/java/io/seqera/tower/cli/commands/datasets/AddCmd.java +++ b/src/main/java/io/seqera/tower/cli/commands/datasets/AddCmd.java @@ -72,9 +72,9 @@ protected Response exec() throws ApiException, IOException { if (overwrite) tryDeleteDataset(name, wspId); - CreateDatasetResponse response = datasetsApi().createDataset(wspId, request); + CreateDatasetResponse response = datasetsApi().createDatasetV2(request, wspId); - datasetsApi().uploadDataset(wspId, response.getDataset().getId(), header, fileName.toFile()); + datasetsApi().uploadDatasetV2(response.getDataset().getId(), wspId, header, fileName.toFile()); return new DatasetCreate(response.getDataset().getName(), workspace.workspace, response.getDataset().getId()); } diff --git a/src/main/java/io/seqera/tower/cli/commands/datasets/DatasetMultiRefOptions.java b/src/main/java/io/seqera/tower/cli/commands/datasets/DatasetMultiRefOptions.java new file mode 100644 index 00000000..e2a17ec8 --- /dev/null +++ b/src/main/java/io/seqera/tower/cli/commands/datasets/DatasetMultiRefOptions.java @@ -0,0 +1,36 @@ +/* + * Copyright 2021-2026, 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.datasets; + +import picocli.CommandLine; + +import java.util.List; + +public class DatasetMultiRefOptions { + + @CommandLine.ArgGroup(multiplicity = "1") + public DatasetMultiRef dataset; + + public static class DatasetMultiRef { + + @CommandLine.Option(names = {"-i", "--id"}, arity = "1..*", description = "Dataset unique identifier(s). May be combined with --name.") + public List datasetIds; + + @CommandLine.Option(names = {"-n", "--name"}, arity = "1..*", description = "Dataset name(s). May be combined with --id.") + public List datasetNames; + } +} diff --git a/src/main/java/io/seqera/tower/cli/commands/datasets/DatasetsLabelsManager.java b/src/main/java/io/seqera/tower/cli/commands/datasets/DatasetsLabelsManager.java new file mode 100644 index 00000000..fc6616ff --- /dev/null +++ b/src/main/java/io/seqera/tower/cli/commands/datasets/DatasetsLabelsManager.java @@ -0,0 +1,51 @@ +/* + * Copyright 2021-2026, 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.datasets; + +import io.seqera.tower.ApiException; +import io.seqera.tower.api.LabelsApi; +import io.seqera.tower.cli.commands.labels.BaseLabelsManager; +import io.seqera.tower.model.AssociateDatasetsLabelsRequest; + +import java.util.List; + +public class DatasetsLabelsManager extends BaseLabelsManager { + + public DatasetsLabelsManager(LabelsApi api) { + super(api, "dataset"); + } + + @Override + protected AssociateDatasetsLabelsRequest getRequest(List labelsIds, String entityId) { + return new AssociateDatasetsLabelsRequest().labelIds(labelsIds).datasetIds(List.of(entityId)); + } + + @Override + protected void apply(AssociateDatasetsLabelsRequest request, Long wspId) throws ApiException { + api.applyLabelsToDatasets(request, wspId); + } + + @Override + protected void remove(AssociateDatasetsLabelsRequest request, Long wspId) throws ApiException { + api.removeLabelsFromDatasets(request, wspId); + } + + @Override + protected void append(AssociateDatasetsLabelsRequest request, Long wspId) throws ApiException { + api.addLabelsToDatasets(request, wspId); + } +} diff --git a/src/main/java/io/seqera/tower/cli/commands/datasets/HideCmd.java b/src/main/java/io/seqera/tower/cli/commands/datasets/HideCmd.java new file mode 100644 index 00000000..afdda04b --- /dev/null +++ b/src/main/java/io/seqera/tower/cli/commands/datasets/HideCmd.java @@ -0,0 +1,51 @@ +/* + * Copyright 2021-2026, 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.datasets; + +import io.seqera.tower.ApiException; +import io.seqera.tower.cli.commands.global.WorkspaceRequiredOptions; +import io.seqera.tower.cli.responses.Response; +import io.seqera.tower.cli.responses.datasets.DatasetsVisibility; +import io.seqera.tower.model.ChangeDatasetVisibilityRequest; +import picocli.CommandLine; + +import java.io.IOException; +import java.util.List; + +@CommandLine.Command( + name = "hide", + description = "Hide one or more datasets" +) +public class HideCmd extends AbstractDatasetsCmd { + + @CommandLine.Mixin + public DatasetMultiRefOptions datasetMultiRefOptions; + + @CommandLine.Mixin + public WorkspaceRequiredOptions workspace; + + @Override + protected Response exec() throws ApiException, IOException { + Long wspId = workspaceId(workspace.workspace); + List ids = resolveDatasetIds(datasetMultiRefOptions, wspId); + + ChangeDatasetVisibilityRequest request = new ChangeDatasetVisibilityRequest().datasetIds(ids); + datasetsApi().hideDatasets(request, wspId); + + return new DatasetsVisibility(ids, workspace.workspace, true); + } +} diff --git a/src/main/java/io/seqera/tower/cli/commands/datasets/LabelsCmd.java b/src/main/java/io/seqera/tower/cli/commands/datasets/LabelsCmd.java new file mode 100644 index 00000000..2a803135 --- /dev/null +++ b/src/main/java/io/seqera/tower/cli/commands/datasets/LabelsCmd.java @@ -0,0 +1,43 @@ +/* + * Copyright 2021-2026, 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.datasets; + +import io.seqera.tower.ApiException; +import io.seqera.tower.cli.commands.labels.LabelsSubcmdOptions; +import io.seqera.tower.cli.responses.Response; +import picocli.CommandLine; + +import java.io.IOException; + +@CommandLine.Command(name = "labels", description = "Manage dataset labels") +public class LabelsCmd extends AbstractDatasetsCmd { + + @CommandLine.Mixin + public DatasetRefOptions datasetRefOptions; + + @CommandLine.Mixin + public LabelsSubcmdOptions labelsSubcmdOptions; + + @Override + protected Response exec() throws ApiException, IOException { + Long wspId = workspaceId(labelsSubcmdOptions.workspace.workspace); + String datasetId = fetchDescribeDatasetResponse(datasetRefOptions, wspId).getId(); + + DatasetsLabelsManager manager = new DatasetsLabelsManager(labelsApi()); + return manager.execute(wspId, datasetId, labelsSubcmdOptions); + } +} diff --git a/src/main/java/io/seqera/tower/cli/commands/datasets/ListCmd.java b/src/main/java/io/seqera/tower/cli/commands/datasets/ListCmd.java index fb390f78..7d7cbb58 100644 --- a/src/main/java/io/seqera/tower/cli/commands/datasets/ListCmd.java +++ b/src/main/java/io/seqera/tower/cli/commands/datasets/ListCmd.java @@ -17,13 +17,18 @@ package io.seqera.tower.cli.commands.datasets; import io.seqera.tower.ApiException; +import io.seqera.tower.cli.commands.global.PaginationOptions; +import io.seqera.tower.cli.commands.global.ShowLabelsOption; import io.seqera.tower.cli.commands.global.WorkspaceRequiredOptions; import io.seqera.tower.cli.responses.Response; import io.seqera.tower.cli.responses.datasets.DatasetList; -import io.seqera.tower.model.DatasetDto; +import io.seqera.tower.cli.utils.PaginationInfo; +import io.seqera.tower.model.DatasetQueryAttribute; +import io.seqera.tower.model.ListDatasetsResponse; import picocli.CommandLine; import java.io.IOException; +import java.util.Collections; import java.util.List; @CommandLine.Command( @@ -35,14 +40,42 @@ public class ListCmd extends AbstractDatasetsCmd { @CommandLine.Mixin public WorkspaceRequiredOptions workspace; - @CommandLine.Option(names = {"-f", "--filter"}, description = "Filter datasets by name substring") + @CommandLine.Option(names = {"-f", "--filter"}, description = "Optional filter criteria, allowing free text search on name or ID " + + "and keywords: `username`, `label`, `visibility`, `createdAfter`, `createdBefore`, `usedAfter`, `usedBefore`. Example keyword usage: -f label:custom-label.") public String filter; + @CommandLine.Option(names = {"--show-hidden"}, description = "Include datasets marked as hidden in the results.", defaultValue = "false") + public boolean showHidden; + + @CommandLine.Mixin + ShowLabelsOption showLabelsOption; + + @CommandLine.Mixin + PaginationOptions paginationOptions; + @Override protected Response exec() throws ApiException, IOException { Long wspId = workspaceId(workspace.workspace); - List response = searchByName(wspId, filter); - return new DatasetList(response, workspace.workspace); + Integer max = PaginationOptions.getMax(paginationOptions); + Integer offset = PaginationOptions.getOffset(paginationOptions, max); + + List attributes = Boolean.TRUE.equals(showLabelsOption.showLabels) + ? List.of(DatasetQueryAttribute.labels) + : Collections.emptyList(); + + String visibility = showHidden ? "all" : null; + + ListDatasetsResponse response = datasetsApi().listDatasetsV2( + wspId, max, offset, filter, null, null, visibility, attributes + ); + + return new DatasetList( + response.getDatasets(), + workspace.workspace, + Boolean.TRUE.equals(showLabelsOption.showLabels), + showHidden, + PaginationInfo.from(paginationOptions, response.getTotalSize()) + ); } } diff --git a/src/main/java/io/seqera/tower/cli/commands/datasets/ShowCmd.java b/src/main/java/io/seqera/tower/cli/commands/datasets/ShowCmd.java new file mode 100644 index 00000000..124c267a --- /dev/null +++ b/src/main/java/io/seqera/tower/cli/commands/datasets/ShowCmd.java @@ -0,0 +1,51 @@ +/* + * Copyright 2021-2026, 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.datasets; + +import io.seqera.tower.ApiException; +import io.seqera.tower.cli.commands.global.WorkspaceRequiredOptions; +import io.seqera.tower.cli.responses.Response; +import io.seqera.tower.cli.responses.datasets.DatasetsVisibility; +import io.seqera.tower.model.ChangeDatasetVisibilityRequest; +import picocli.CommandLine; + +import java.io.IOException; +import java.util.List; + +@CommandLine.Command( + name = "show", + description = "Make one or more hidden datasets visible" +) +public class ShowCmd extends AbstractDatasetsCmd { + + @CommandLine.Mixin + public DatasetMultiRefOptions datasetMultiRefOptions; + + @CommandLine.Mixin + public WorkspaceRequiredOptions workspace; + + @Override + protected Response exec() throws ApiException, IOException { + Long wspId = workspaceId(workspace.workspace); + List ids = resolveDatasetIds(datasetMultiRefOptions, wspId); + + ChangeDatasetVisibilityRequest request = new ChangeDatasetVisibilityRequest().datasetIds(ids); + datasetsApi().showDatasets(request, wspId); + + return new DatasetsVisibility(ids, workspace.workspace, false); + } +} diff --git a/src/main/java/io/seqera/tower/cli/commands/datasets/UpdateCmd.java b/src/main/java/io/seqera/tower/cli/commands/datasets/UpdateCmd.java index ab6b4dbf..60a3bef8 100644 --- a/src/main/java/io/seqera/tower/cli/commands/datasets/UpdateCmd.java +++ b/src/main/java/io/seqera/tower/cli/commands/datasets/UpdateCmd.java @@ -64,10 +64,10 @@ protected Response exec() throws ApiException, IOException { request.setName(newName != null ? newName : dataset.getName()); request.setDescription(description != null ? description : dataset.getDescription()); - datasetsApi().updateDataset(wspId, dataset.getId(), request); + datasetsApi().updateDatasetV2(dataset.getId(), request, wspId); if (fileName != null) { - datasetsApi().uploadDataset(wspId, dataset.getId(), header, fileName.toFile()); + datasetsApi().uploadDatasetV2(dataset.getId(), wspId, header, fileName.toFile()); } return new DatasetUpdate(dataset.getName(), workspace.workspace, dataset.getId()); diff --git a/src/main/java/io/seqera/tower/cli/commands/datasets/versions/VersionsCmd.java b/src/main/java/io/seqera/tower/cli/commands/datasets/versions/VersionsCmd.java index 6530e64d..b59d8583 100644 --- a/src/main/java/io/seqera/tower/cli/commands/datasets/versions/VersionsCmd.java +++ b/src/main/java/io/seqera/tower/cli/commands/datasets/versions/VersionsCmd.java @@ -42,7 +42,7 @@ protected Response exec() throws ApiException, IOException { DatasetDto dataset = fetchDescribeDatasetResponse(parentCommand.datasetRefOptions, wspId); String datasetRef = parentCommand.datasetRefOptions.dataset.datasetName != null ? parentCommand.datasetRefOptions.dataset.datasetName : parentCommand.datasetRefOptions.dataset.datasetId; - ListDatasetVersionsResponse response = datasetsApi().listDatasetVersions(wspId, dataset.getId(), dataset.getMediaType()); + ListDatasetVersionsResponse response = datasetsApi().listDatasetVersionsV2(dataset.getId(), wspId, dataset.getMediaType()); return new DatasetVersionsList(response.getVersions(), datasetRef, parentCommand.workspace.workspace); } diff --git a/src/main/java/io/seqera/tower/cli/commands/runs/ListCmd.java b/src/main/java/io/seqera/tower/cli/commands/runs/ListCmd.java index f864f490..ec05f615 100644 --- a/src/main/java/io/seqera/tower/cli/commands/runs/ListCmd.java +++ b/src/main/java/io/seqera/tower/cli/commands/runs/ListCmd.java @@ -40,7 +40,7 @@ public class ListCmd extends AbstractRunsCmd { @CommandLine.Mixin public WorkspaceOptionalOptions workspace; - @CommandLine.Option(names = {"-f", "--filter"}, description = "Filter pipeline runs by run name. Performs case-insensitive substring matching on the runName field.") + @CommandLine.Option(names = {"-f", "--filter"}, description = "Filter pipeline runs using the server search syntax. A bare value matches the run name substring; key:value pairs filter on supported keys (e.g. datasetId:, runName:, status:, after:, before:).") public String filter; @CommandLine.Mixin diff --git a/src/main/java/io/seqera/tower/cli/responses/datasets/DatasetList.java b/src/main/java/io/seqera/tower/cli/responses/datasets/DatasetList.java index 8702bb91..59ae1b7b 100644 --- a/src/main/java/io/seqera/tower/cli/responses/datasets/DatasetList.java +++ b/src/main/java/io/seqera/tower/cli/responses/datasets/DatasetList.java @@ -16,38 +16,70 @@ package io.seqera.tower.cli.responses.datasets; +import com.fasterxml.jackson.annotation.JsonIgnore; import io.seqera.tower.cli.responses.Response; import io.seqera.tower.cli.utils.FormatHelper; +import io.seqera.tower.cli.utils.PaginationInfo; import io.seqera.tower.cli.utils.TableList; import io.seqera.tower.model.DatasetDto; +import jakarta.annotation.Nullable; import java.io.PrintWriter; +import java.util.ArrayList; import java.util.List; public class DatasetList extends Response { public final List datasetList; public final String workspace; + public final boolean showLabels; + public final boolean showHidden; + + @JsonIgnore + @Nullable + private final PaginationInfo paginationInfo; public DatasetList(List datasetList, String workspace) { + this(datasetList, workspace, false, false, null); + } + + public DatasetList(List datasetList, String workspace, boolean showLabels, boolean showHidden, @Nullable PaginationInfo paginationInfo) { this.datasetList = datasetList; this.workspace = workspace; + this.showLabels = showLabels; + this.showHidden = showHidden; + this.paginationInfo = paginationInfo; } @Override public void toString(PrintWriter out) { out.println(ansi(String.format("%n @|bold Datasets at %s workspace:|@%n", workspace))); - if (datasetList.isEmpty()) { + if (datasetList == null || datasetList.isEmpty()) { out.println(ansi(" @|yellow No datasets found|@")); return; } - TableList table = new TableList(out, 3, "ID", "Name", "Created").sortBy(0); + List desc = new ArrayList<>(List.of("ID", "Name", "Created")); + if (showHidden) desc.add("Hidden"); + if (showLabels) desc.add("Labels"); + + TableList table = new TableList(out, desc.size(), desc.toArray(new String[0])).sortBy(0); table.setPrefix(" "); - datasetList.forEach(ds -> table.addRow(ds.getId(), ds.getName(), FormatHelper.formatDate(ds.getDateCreated()))); + datasetList.forEach(ds -> { + List row = new ArrayList<>(List.of( + ds.getId(), + ds.getName(), + FormatHelper.formatDate(ds.getDateCreated()) + )); + if (showHidden) row.add(Boolean.TRUE.equals(ds.getHidden()) ? "yes" : "no"); + if (showLabels) row.add(FormatHelper.formatLabels(ds.getLabels())); + table.addRow(row.toArray(new String[0])); + }); table.print(); + PaginationInfo.addFooter(out, paginationInfo); + out.println(""); } } diff --git a/src/main/java/io/seqera/tower/cli/responses/datasets/DatasetsVisibility.java b/src/main/java/io/seqera/tower/cli/responses/datasets/DatasetsVisibility.java new file mode 100644 index 00000000..2d0965d8 --- /dev/null +++ b/src/main/java/io/seqera/tower/cli/responses/datasets/DatasetsVisibility.java @@ -0,0 +1,43 @@ +/* + * Copyright 2021-2026, 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.datasets; + +import io.seqera.tower.cli.responses.Response; + +import java.io.PrintWriter; +import java.util.List; + +public class DatasetsVisibility extends Response { + + public final List datasetIds; + public final String workspace; + public final boolean hidden; + + public DatasetsVisibility(List datasetIds, String workspace, boolean hidden) { + this.datasetIds = datasetIds; + this.workspace = workspace; + this.hidden = hidden; + } + + @Override + public void toString(PrintWriter out) { + String action = hidden ? "hidden" : "shown"; + out.println(ansi(String.format("%n @|yellow Datasets %s at %s workspace:|@", action, workspace))); + datasetIds.forEach(id -> out.println(ansi(String.format(" - %s", id)))); + out.println(""); + } +} diff --git a/src/test/java/io/seqera/tower/cli/datasets/DatasetsCmdTest.java b/src/test/java/io/seqera/tower/cli/datasets/DatasetsCmdTest.java index 46415904..3a4f364c 100644 --- a/src/test/java/io/seqera/tower/cli/datasets/DatasetsCmdTest.java +++ b/src/test/java/io/seqera/tower/cli/datasets/DatasetsCmdTest.java @@ -28,6 +28,8 @@ import io.seqera.tower.cli.responses.datasets.DatasetUrl; import io.seqera.tower.cli.responses.datasets.DatasetVersionsList; import io.seqera.tower.cli.responses.datasets.DatasetView; +import io.seqera.tower.cli.responses.datasets.DatasetsVisibility; +import io.seqera.tower.cli.responses.labels.ManageLabels; import io.seqera.tower.model.DatasetDto; import io.seqera.tower.model.DatasetVersionDto; import org.junit.jupiter.params.ParameterizedTest; @@ -41,12 +43,14 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Path; import java.util.Arrays; +import java.util.List; import static io.seqera.tower.cli.utils.JsonHelper.parseJson; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockserver.matchers.Times.exactly; import static org.mockserver.model.HttpRequest.request; import static org.mockserver.model.HttpResponse.response; +import static org.mockserver.model.JsonBody.json; public class DatasetsCmdTest extends BaseCmdTest { @@ -55,7 +59,8 @@ public class DatasetsCmdTest extends BaseCmdTest { void testList(OutputType format, MockServerClient mock) throws JsonProcessingException { mock.when( - request().withMethod("GET").withPath("/workspaces/249664655368293/datasets"), exactly(1) + request().withMethod("GET").withPath("/datasets") + .withQueryStringParameter("workspaceId", "249664655368293"), exactly(1) ).respond( response().withStatusCode(200).withBody(loadResource("datasets/datasets_list")).withContentType(MediaType.APPLICATION_JSON) ); @@ -90,7 +95,8 @@ void testList(OutputType format, MockServerClient mock) throws JsonProcessingExc @EnumSource(OutputType.class) void testView(OutputType format, MockServerClient mock) throws JsonProcessingException { mock.when( - request().withMethod("GET").withPath("/workspaces/249664655368293/datasets/4D9TP0w2pM0qmwqVHgrgBK/metadata"), exactly(1) + request().withMethod("GET").withPath("/datasets/4D9TP0w2pM0qmwqVHgrgBK/metadata") + .withQueryStringParameter("workspaceId", "249664655368293"), exactly(1) ).respond( response().withStatusCode(200).withBody(loadResource("datasets/dataset_metadata")).withContentType(MediaType.APPLICATION_JSON) ); @@ -114,13 +120,15 @@ void testView(OutputType format, MockServerClient mock) throws JsonProcessingExc @EnumSource(OutputType.class) void testVersions(OutputType format, MockServerClient mock) throws JsonProcessingException { mock.when( - request().withMethod("GET").withPath("/workspaces/249664655368293/datasets/4D9TP0w2pM0qmwqVHgrgBK/metadata"), exactly(1) + request().withMethod("GET").withPath("/datasets/4D9TP0w2pM0qmwqVHgrgBK/metadata") + .withQueryStringParameter("workspaceId", "249664655368293"), exactly(1) ).respond( response().withStatusCode(200).withBody(loadResource("datasets/dataset_metadata")).withContentType(MediaType.APPLICATION_JSON) ); mock.when( - request().withMethod("GET").withPath("/workspaces/249664655368293/datasets/4D9TP0w2pM0qmwqVHgrgBK/versions"), exactly(1) + request().withMethod("GET").withPath("/datasets/4D9TP0w2pM0qmwqVHgrgBK/versions") + .withQueryStringParameter("workspaceId", "249664655368293"), exactly(1) ).respond( response().withStatusCode(200).withBody(loadResource("datasets/dataset_versions")).withContentType(MediaType.APPLICATION_JSON) ); @@ -157,13 +165,15 @@ void testVersions(OutputType format, MockServerClient mock) throws JsonProcessin @EnumSource(OutputType.class) void testDelete(OutputType format, MockServerClient mock) { mock.when( - request().withMethod("GET").withPath("/workspaces/249664655368293/datasets/4D9TP0w2pM0qmwqVHgrgBK/metadata"), exactly(1) + request().withMethod("GET").withPath("/datasets/4D9TP0w2pM0qmwqVHgrgBK/metadata") + .withQueryStringParameter("workspaceId", "249664655368293"), exactly(1) ).respond( response().withStatusCode(200).withBody(loadResource("datasets/dataset_metadata")).withContentType(MediaType.APPLICATION_JSON) ); mock.when( - request().withMethod("DELETE").withPath("/workspaces/249664655368293/datasets/4D9TP0w2pM0qmwqVHgrgBK"), exactly(1) + request().withMethod("DELETE").withPath("/datasets/4D9TP0w2pM0qmwqVHgrgBK") + .withQueryStringParameter("workspaceId", "249664655368293"), exactly(1) ).respond( response().withStatusCode(204) ); @@ -180,13 +190,15 @@ void testDelete(OutputType format, MockServerClient mock) { void testUrl(OutputType format, MockServerClient mock) throws JsonProcessingException { mock.when( - request().withMethod("GET").withPath("/workspaces/249664655368293/datasets/4D9TP0w2pM0qmwqVHgrgBK/metadata"), exactly(1) + request().withMethod("GET").withPath("/datasets/4D9TP0w2pM0qmwqVHgrgBK/metadata") + .withQueryStringParameter("workspaceId", "249664655368293"), exactly(1) ).respond( response().withStatusCode(200).withBody(loadResource("datasets/dataset_metadata")).withContentType(MediaType.APPLICATION_JSON) ); mock.when( - request().withMethod("GET").withPath("/workspaces/249664655368293/datasets/4D9TP0w2pM0qmwqVHgrgBK/versions"), exactly(1) + request().withMethod("GET").withPath("/datasets/4D9TP0w2pM0qmwqVHgrgBK/versions") + .withQueryStringParameter("workspaceId", "249664655368293"), exactly(1) ).respond( response().withStatusCode(200).withBody(loadResource("datasets/dataset_versions")).withContentType(MediaType.APPLICATION_JSON) ); @@ -203,13 +215,15 @@ void testUrl(OutputType format, MockServerClient mock) throws JsonProcessingExce @EnumSource(OutputType.class) void testDownload(OutputType format, MockServerClient mock) throws IOException { mock.when( - request().withMethod("GET").withPath("/workspaces/249664655368293/datasets/4D9TP0w2pM0qmwqVHgrgBK/metadata"), exactly(1) + request().withMethod("GET").withPath("/datasets/4D9TP0w2pM0qmwqVHgrgBK/metadata") + .withQueryStringParameter("workspaceId", "249664655368293"), exactly(1) ).respond( response().withStatusCode(200).withBody(loadResource("datasets/dataset_metadata")).withContentType(MediaType.APPLICATION_JSON) ); mock.when( - request().withMethod("GET").withPath("/workspaces/249664655368293/datasets/4D9TP0w2pM0qmwqVHgrgBK/versions"), exactly(1) + request().withMethod("GET").withPath("/datasets/4D9TP0w2pM0qmwqVHgrgBK/versions") + .withQueryStringParameter("workspaceId", "249664655368293"), exactly(1) ).respond( response().withStatusCode(200).withBody(loadResource("datasets/dataset_versions")).withContentType(MediaType.APPLICATION_JSON) ); @@ -235,14 +249,16 @@ void testDownload(OutputType format, MockServerClient mock) throws IOException { @EnumSource(OutputType.class) void testAdd(OutputType format, MockServerClient mock) throws IOException { mock.when( - request().withMethod("POST").withPath("/workspaces/249664655368293/datasets"), exactly(1) + request().withMethod("POST").withPath("/datasets") + .withQueryStringParameter("workspaceId", "249664655368293"), exactly(1) ).respond( response().withStatusCode(200).withBody(loadResource("datasets/dataset_created_response")).withContentType(MediaType.APPLICATION_JSON) ); mock.when( request().withMethod("POST") - .withPath("/workspaces/249664655368293/datasets/1W3BTHWgRH71OJmOPMdG7S/upload") + .withPath("/datasets/1W3BTHWgRH71OJmOPMdG7S/upload") + .withQueryStringParameter("workspaceId", "249664655368293") .withQueryStringParameter("header", "false") , exactly(1) ).respond( @@ -261,19 +277,17 @@ void testAdd(OutputType format, MockServerClient mock) throws IOException { void testAddWithOverwrite(OutputType format, MockServerClient mock) throws IOException { mock.when( - request().withMethod("GET").withPath("/workspaces/249664655368293/datasets/4D9TP0w2pM0qmwqVHgrgBK/metadata"), exactly(1) - ).respond( - response().withStatusCode(200).withBody(loadResource("datasets/dataset_metadata")).withContentType(MediaType.APPLICATION_JSON) - ); - - mock.when( - request().withMethod("GET").withPath("/workspaces/249664655368293/datasets"), exactly(1) + request().withMethod("GET").withPath("/datasets") + .withQueryStringParameter("workspaceId", "249664655368293") + .withQueryStringParameter("search", "dataset2") + .withQueryStringParameter("visibility", "all"), exactly(1) ).respond( response().withStatusCode(200).withBody(loadResource("datasets/datasets_list")).withContentType(MediaType.APPLICATION_JSON) ); mock.when( - request().withMethod("POST").withPath("/workspaces/249664655368293/datasets"), exactly(1) + request().withMethod("POST").withPath("/datasets") + .withQueryStringParameter("workspaceId", "249664655368293"), exactly(1) ).respond( response().withStatusCode(200).withBody(JsonBody.json("{\n" + " \"dataset\": {\n" + @@ -290,7 +304,8 @@ void testAddWithOverwrite(OutputType format, MockServerClient mock) throws IOExc mock.when( request().withMethod("POST") - .withPath("/workspaces/249664655368293/datasets/1W3BTHWgRH71OJmOPMdG7S/upload") + .withPath("/datasets/1W3BTHWgRH71OJmOPMdG7S/upload") + .withQueryStringParameter("workspaceId", "249664655368293") .withQueryStringParameter("header", "false") , exactly(1) ).respond( @@ -298,7 +313,8 @@ void testAddWithOverwrite(OutputType format, MockServerClient mock) throws IOExc ); mock.when( - request().withMethod("DELETE").withPath("/workspaces/249664655368293/datasets/1W2FqBiI6WoNokQTkPkEzo"), exactly(1) + request().withMethod("DELETE").withPath("/datasets/1W2FqBiI6WoNokQTkPkEzo") + .withQueryStringParameter("workspaceId", "249664655368293"), exactly(1) ).respond( response().withStatusCode(204) ); @@ -314,13 +330,15 @@ void testAddWithOverwrite(OutputType format, MockServerClient mock) throws IOExc @EnumSource(OutputType.class) void testUpdate(OutputType format, MockServerClient mock) { mock.when( - request().withMethod("GET").withPath("/workspaces/249664655368293/datasets/4D9TP0w2pM0qmwqVHgrgBK/metadata"), exactly(1) + request().withMethod("GET").withPath("/datasets/4D9TP0w2pM0qmwqVHgrgBK/metadata") + .withQueryStringParameter("workspaceId", "249664655368293"), exactly(1) ).respond( response().withStatusCode(200).withBody(loadResource("datasets/dataset_metadata")).withContentType(MediaType.APPLICATION_JSON) ); mock.when( - request().withMethod("PUT").withPath("/workspaces/249664655368293/datasets/4D9TP0w2pM0qmwqVHgrgBK"), exactly(1) + request().withMethod("PUT").withPath("/datasets/4D9TP0w2pM0qmwqVHgrgBK") + .withQueryStringParameter("workspaceId", "249664655368293"), exactly(1) ).respond( response().withStatusCode(204) ); @@ -336,20 +354,23 @@ void testUpdate(OutputType format, MockServerClient mock) { @EnumSource(OutputType.class) void testUpdateWithFile(OutputType format, MockServerClient mock) throws IOException { mock.when( - request().withMethod("GET").withPath("/workspaces/249664655368293/datasets/4D9TP0w2pM0qmwqVHgrgBK/metadata"), exactly(1) + request().withMethod("GET").withPath("/datasets/4D9TP0w2pM0qmwqVHgrgBK/metadata") + .withQueryStringParameter("workspaceId", "249664655368293"), exactly(1) ).respond( response().withStatusCode(200).withBody(loadResource("datasets/dataset_metadata")).withContentType(MediaType.APPLICATION_JSON) ); mock.when( - request().withMethod("PUT").withPath("/workspaces/249664655368293/datasets/4D9TP0w2pM0qmwqVHgrgBK"), exactly(1) + request().withMethod("PUT").withPath("/datasets/4D9TP0w2pM0qmwqVHgrgBK") + .withQueryStringParameter("workspaceId", "249664655368293"), exactly(1) ).respond( response().withStatusCode(204) ); mock.when( request().withMethod("POST") - .withPath("/workspaces/249664655368293/datasets/4D9TP0w2pM0qmwqVHgrgBK/upload") + .withPath("/datasets/4D9TP0w2pM0qmwqVHgrgBK/upload") + .withQueryStringParameter("workspaceId", "249664655368293") .withQueryStringParameter("header", "false") , exactly(1) ).respond( @@ -373,4 +394,189 @@ void testFileNotExistsError(OutputType format, MockServerClient mock) throws IOE assertEquals("", out.stdOut); assertEquals(1, out.exitCode); } + + @ParameterizedTest + @EnumSource(OutputType.class) + void testListWithFilter(OutputType format, MockServerClient mock) throws JsonProcessingException { + + mock.when( + request().withMethod("GET").withPath("/datasets") + .withQueryStringParameter("workspaceId", "249664655368293") + .withQueryStringParameter("search", "data"), exactly(1) + ).respond( + response().withStatusCode(200).withBody(loadResource("datasets/datasets_list")).withContentType(MediaType.APPLICATION_JSON) + ); + + ExecOut out = exec(format, mock, "datasets", "list", "-w", "249664655368293", "--filter", "data"); + + assertOutput(format, out, new DatasetList(Arrays.asList( + parseJson("{\"id\":\"4D9TP0w2pM0qmwqVHgrgBK\",\"name\":\"dataset1\",\"description\":null,\"mediaType\":null,\"deleted\":false,\"dateCreated\":\"2021-11-26T14:51:20+01:00\",\"lastUpdated\":\"2021-11-26T14:51:20+01:00\"}", DatasetDto.class), + parseJson("{\"id\":\"1W2FqBiI6WoNokQTkPkEzo\",\"name\":\"dataset2\",\"description\":null,\"mediaType\":null,\"deleted\":false,\"dateCreated\":\"2021-11-29T08:05:44+01:00\",\"lastUpdated\":\"2021-11-29T08:05:44+01:00\"}", DatasetDto.class) + ), "249664655368293")); + assertEquals("", out.stdErr); + assertEquals(0, out.exitCode); + } + + @ParameterizedTest + @EnumSource(OutputType.class) + void testListShowHidden(OutputType format, MockServerClient mock) throws JsonProcessingException { + + mock.when( + request().withMethod("GET").withPath("/datasets") + .withQueryStringParameter("workspaceId", "249664655368293") + .withQueryStringParameter("visibility", "all"), exactly(1) + ).respond( + response().withStatusCode(200).withBody(loadResource("datasets/datasets_list")).withContentType(MediaType.APPLICATION_JSON) + ); + + ExecOut out = exec(format, mock, "datasets", "list", "-w", "249664655368293", "--show-hidden"); + + assertOutput(format, out, new DatasetList(Arrays.asList( + parseJson("{\"id\":\"4D9TP0w2pM0qmwqVHgrgBK\",\"name\":\"dataset1\",\"description\":null,\"mediaType\":null,\"deleted\":false,\"dateCreated\":\"2021-11-26T14:51:20+01:00\",\"lastUpdated\":\"2021-11-26T14:51:20+01:00\"}", DatasetDto.class), + parseJson("{\"id\":\"1W2FqBiI6WoNokQTkPkEzo\",\"name\":\"dataset2\",\"description\":null,\"mediaType\":null,\"deleted\":false,\"dateCreated\":\"2021-11-29T08:05:44+01:00\",\"lastUpdated\":\"2021-11-29T08:05:44+01:00\"}", DatasetDto.class) + ), "249664655368293", false, true, null)); + assertEquals("", out.stdErr); + assertEquals(0, out.exitCode); + } + + @ParameterizedTest + @EnumSource(OutputType.class) + void testHideById(OutputType format, MockServerClient mock) { + mock.when( + request().withMethod("POST").withPath("/datasets/hide") + .withQueryStringParameter("workspaceId", "249664655368293") + .withBody(json("{\"datasetIds\":[\"4D9TP0w2pM0qmwqVHgrgBK\",\"1W2FqBiI6WoNokQTkPkEzo\"]}")), + exactly(1) + ).respond( + response().withStatusCode(204) + ); + + ExecOut out = exec(format, mock, "datasets", "hide", "-w", "249664655368293", + "-i", "4D9TP0w2pM0qmwqVHgrgBK", "-i", "1W2FqBiI6WoNokQTkPkEzo"); + + assertOutput(format, out, new DatasetsVisibility( + List.of("4D9TP0w2pM0qmwqVHgrgBK", "1W2FqBiI6WoNokQTkPkEzo"), + "249664655368293", true)); + assertEquals("", out.stdErr); + assertEquals(0, out.exitCode); + } + + @ParameterizedTest + @EnumSource(OutputType.class) + void testShowById(OutputType format, MockServerClient mock) { + mock.when( + request().withMethod("POST").withPath("/datasets/show") + .withQueryStringParameter("workspaceId", "249664655368293") + .withBody(json("{\"datasetIds\":[\"4D9TP0w2pM0qmwqVHgrgBK\"]}")), + exactly(1) + ).respond( + response().withStatusCode(204) + ); + + ExecOut out = exec(format, mock, "datasets", "show", "-w", "249664655368293", + "-i", "4D9TP0w2pM0qmwqVHgrgBK"); + + assertOutput(format, out, new DatasetsVisibility( + List.of("4D9TP0w2pM0qmwqVHgrgBK"), + "249664655368293", false)); + assertEquals("", out.stdErr); + assertEquals(0, out.exitCode); + } + + @ParameterizedTest + @EnumSource(OutputType.class) + void testHideByName(OutputType format, MockServerClient mock) { + mock.when( + request().withMethod("GET").withPath("/datasets") + .withQueryStringParameter("workspaceId", "249664655368293") + .withQueryStringParameter("search", "dataset1") + .withQueryStringParameter("visibility", "all"), exactly(1) + ).respond( + response().withStatusCode(200).withBody(loadResource("datasets/datasets_list")).withContentType(MediaType.APPLICATION_JSON) + ); + + mock.when( + request().withMethod("POST").withPath("/datasets/hide") + .withQueryStringParameter("workspaceId", "249664655368293") + .withBody(json("{\"datasetIds\":[\"4D9TP0w2pM0qmwqVHgrgBK\"]}")), + exactly(1) + ).respond( + response().withStatusCode(204) + ); + + ExecOut out = exec(format, mock, "datasets", "hide", "-w", "249664655368293", + "-n", "dataset1"); + + assertOutput(format, out, new DatasetsVisibility( + List.of("4D9TP0w2pM0qmwqVHgrgBK"), + "249664655368293", true)); + assertEquals("", out.stdErr); + assertEquals(0, out.exitCode); + } + + @ParameterizedTest + @EnumSource(OutputType.class) + void testShowByName(OutputType format, MockServerClient mock) { + mock.when( + request().withMethod("GET").withPath("/datasets") + .withQueryStringParameter("workspaceId", "249664655368293") + .withQueryStringParameter("search", "dataset1") + .withQueryStringParameter("visibility", "all"), exactly(1) + ).respond( + response().withStatusCode(200).withBody(loadResource("datasets/datasets_list")).withContentType(MediaType.APPLICATION_JSON) + ); + + mock.when( + request().withMethod("POST").withPath("/datasets/show") + .withQueryStringParameter("workspaceId", "249664655368293") + .withBody(json("{\"datasetIds\":[\"4D9TP0w2pM0qmwqVHgrgBK\"]}")), + exactly(1) + ).respond( + response().withStatusCode(204) + ); + + ExecOut out = exec(format, mock, "datasets", "show", "-w", "249664655368293", + "-n", "dataset1"); + + assertOutput(format, out, new DatasetsVisibility( + List.of("4D9TP0w2pM0qmwqVHgrgBK"), + "249664655368293", false)); + assertEquals("", out.stdErr); + assertEquals(0, out.exitCode); + } + + @ParameterizedTest + @EnumSource(OutputType.class) + void testLabelsApply(OutputType format, MockServerClient mock) { + mock.when( + request().withMethod("GET").withPath("/datasets/4D9TP0w2pM0qmwqVHgrgBK/metadata") + .withQueryStringParameter("workspaceId", "249664655368293"), exactly(1) + ).respond( + response().withStatusCode(200).withBody(loadResource("datasets/dataset_metadata")).withContentType(MediaType.APPLICATION_JSON) + ); + + mock.when( + request().withMethod("GET").withPath("/labels") + .withQueryStringParameter("search", "foo"), exactly(1) + ).respond( + response().withStatusCode(200).withContentType(MediaType.APPLICATION_JSON) + .withBody("{\"labels\":[{\"id\":111,\"name\":\"foo\",\"value\":null,\"resource\":false,\"isDefault\":false}],\"totalSize\":1}") + ); + + mock.when( + request().withMethod("POST").withPath("/datasets/labels/apply") + .withQueryStringParameter("workspaceId", "249664655368293") + .withBody(json("{\"datasetIds\":[\"4D9TP0w2pM0qmwqVHgrgBK\"],\"labelIds\":[111]}")), + exactly(1) + ).respond( + response().withStatusCode(204) + ); + + ExecOut out = exec(format, mock, "datasets", "labels", "-w", "249664655368293", + "-i", "4D9TP0w2pM0qmwqVHgrgBK", "foo"); + + assertOutput(format, out, new ManageLabels("set", "dataset", "4D9TP0w2pM0qmwqVHgrgBK", 249664655368293L)); + assertEquals("", out.stdErr); + assertEquals(0, out.exitCode); + } }