Skip to content

Commit

Permalink
PLAT-1243 - Add ability to query data studio by name
Browse files Browse the repository at this point in the history
  • Loading branch information
georgi-seqera committed Jan 27, 2025
1 parent cb7d054 commit 7d31056
Show file tree
Hide file tree
Showing 10 changed files with 139 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,13 @@

package io.seqera.tower.cli.commands.datastudios;

import java.util.List;
import java.util.Optional;
import java.util.function.Supplier;

import io.seqera.tower.ApiException;
import io.seqera.tower.cli.commands.AbstractApiCmd;
import io.seqera.tower.cli.exceptions.DataStudioNotFoundException;
import io.seqera.tower.model.DataStudioDto;
import io.seqera.tower.model.DataStudioProgressStep;

Expand All @@ -31,7 +33,28 @@
public class AbstractStudiosCmd extends AbstractApiCmd {

protected DataStudioDto fetchDataStudio(DataStudioRefOptions dataStudioRefOptions, Long wspId) throws ApiException {
return api().describeDataStudio(dataStudioRefOptions.dataStudio.sessionId, wspId);
DataStudioDto dataStudio;

if (dataStudioRefOptions.dataStudio.sessionId != null) {
dataStudio = getDataStudioById(wspId, dataStudioRefOptions.dataStudio.sessionId);
} else {
dataStudio = getDataStudioByName(wspId, dataStudioRefOptions.dataStudio.dataStudioName );
}

return dataStudio;
}

private DataStudioDto getDataStudioByName(Long wspId, String dataStudioName) throws ApiException {
List<DataStudioDto> studios = api().listDataStudios(wspId, null, null, null).getStudios();

return studios.stream()
.filter(s -> dataStudioName.equals(s.getName()))
.findFirst()
.orElseThrow(() -> new DataStudioNotFoundException(dataStudioName, wspId));
}

private DataStudioDto getDataStudioById(Long wspId, String sessionId) throws ApiException {
return api().describeDataStudio(sessionId, wspId);
}

public class ProgressStepMessageSupplier implements Supplier<String> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,12 @@ public class DataStudioRefOptions {
public static class DataStudioRef {
@CommandLine.Option(names = {"-i", "--id"}, description = "DataStudio session ID.")
public String sessionId;

@CommandLine.Option(names = {"-n", "--name"}, description = "DataStudio name.")
public String dataStudioName;
}

public String getDataStudioIdentifier() {
return dataStudio.sessionId != null ? dataStudio.sessionId : dataStudio.dataStudioName;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,15 +64,15 @@ protected Response exec() throws ApiException {

DataStudioStartRequest request = getStartRequestWithOverridesApplied(dataStudioDto);

DataStudioStartResponse response = api().startDataStudio(dataStudioRefOptions.dataStudio.sessionId, request, wspId);
DataStudioStartResponse response = api().startDataStudio(dataStudioDto.getSessionId(), request, wspId);

return new DataStudioStartSubmitted(dataStudioRefOptions.dataStudio.sessionId, wspId, workspaceRef(wspId), baseWorkspaceUrl(wspId), response.getJobSubmitted());
return new DataStudioStartSubmitted(dataStudioRefOptions.getDataStudioIdentifier(), wspId, workspaceRef(wspId), baseWorkspaceUrl(wspId), response.getJobSubmitted());
} catch (ApiException e) {
if (e.getCode() == 404) {
throw new DataStudioNotFoundException(dataStudioRefOptions.dataStudio.sessionId, wspId);
throw new DataStudioNotFoundException(dataStudioRefOptions.getDataStudioIdentifier(), workspace.workspace);
}
if (e.getCode() == 403) {
throw new TowerException(String.format("User not entitled to view studio '%s' at %s workspace", dataStudioRefOptions.dataStudio.sessionId, wspId));
throw new TowerException(String.format("User not entitled to view studio '%s' at %s workspace", dataStudioRefOptions.getDataStudioIdentifier(), workspace.workspace));
}
throw e;
}
Expand All @@ -98,10 +98,10 @@ protected Integer onBeforeExit(int exitCode, Response response) {
return waitStatus(
app().getOut(),
showProgress,
new ProgressStepMessageSupplier(submitted.sessionId, submitted.workspaceId),
new ProgressStepMessageSupplier(submitted.studioIdentifier, submitted.workspaceId),
wait,
DataStudioStatus.values(),
() -> checkDataStudioStatus(submitted.sessionId, submitted.workspaceId),
() -> checkDataStudioStatus(submitted.studioIdentifier, submitted.workspaceId),
DataStudioStatus.STOPPED, DataStudioStatus.ERRORED, DataStudioStatus.RUNNING
);
} catch (InterruptedException e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,15 +43,19 @@ protected Response exec() throws ApiException {
Long wspId = workspaceId(workspace.workspace);

try {
DataStudioStopResponse response = api().stopDataStudio(dataStudioRefOptions.dataStudio.sessionId, wspId);
String sessionId = dataStudioRefOptions.dataStudio.sessionId != null
? dataStudioRefOptions.dataStudio.sessionId
: fetchDataStudio(dataStudioRefOptions, wspId).getSessionId();

return new DataStudioStopSubmitted(dataStudioRefOptions.dataStudio.sessionId, wspId, workspaceRef(wspId), response.getJobSubmitted());
DataStudioStopResponse response = api().stopDataStudio(sessionId, wspId);

return new DataStudioStopSubmitted(dataStudioRefOptions.getDataStudioIdentifier(), wspId, workspaceRef(wspId), response.getJobSubmitted());
} catch (ApiException e) {
if (e.getCode() == 404) {
throw new DataStudioNotFoundException(dataStudioRefOptions.dataStudio.sessionId, wspId);
throw new DataStudioNotFoundException(dataStudioRefOptions.getDataStudioIdentifier(), workspace.workspace);
}
if (e.getCode() == 403) {
throw new TowerException(String.format("User not entitled to view studio '%s' at %s workspace", dataStudioRefOptions.dataStudio.sessionId, wspId));
throw new TowerException(String.format("User not entitled to view studio '%s' at %s workspace", dataStudioRefOptions.getDataStudioIdentifier(), workspace.workspace));
}
throw e;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,10 @@ protected Response exec() throws ApiException {
return new DataStudiosView(dataStudioDto, workspaceRef(wspId));
} catch (ApiException e) {
if (e.getCode() == 404) {
throw new DataStudioNotFoundException(dataStudioRefOptions.dataStudio.sessionId, wspId);
throw new DataStudioNotFoundException(dataStudioRefOptions.getDataStudioIdentifier(), workspace.workspace);
}
if (e.getCode() == 403) {
throw new TowerException(String.format("User not entitled to view studio '%s' at %s workspace", dataStudioRefOptions.dataStudio.sessionId, wspId));
throw new TowerException(String.format("User not entitled to view studio '%s' at %s workspace", dataStudioRefOptions.getDataStudioIdentifier(), workspace.workspace));
}
throw e;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,12 @@

public class DataStudioNotFoundException extends TowerException {

public DataStudioNotFoundException( String sessionId, Long workspaceId) {
super(String.format("DataStudio '%s' not found at workspace '%s'", sessionId, workspaceId));
public DataStudioNotFoundException(String studioIdentifier, Long workspaceId) {
super(String.format("DataStudio '%s' not found at workspace '%s'", studioIdentifier, workspaceId));
}

public DataStudioNotFoundException(String studioIdentifier, String workspace) {
super(String.format("DataStudio '%s' not found at workspace '%s'", studioIdentifier, workspace));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -21,26 +21,26 @@

public class DataStudioStartSubmitted extends Response {

public final String sessionId;
public final String studioIdentifier;

public final String studioUrl;

public final Long workspaceId;
public final String workspaceRef;
public final Boolean jobSubmitted;

public DataStudioStartSubmitted(String sessionId, Long workspaceId, String workspaceRef, String baseWorkspaceUrl, Boolean jobSubmitted) {
this.sessionId = sessionId;
public DataStudioStartSubmitted(String studioIdentifier, Long workspaceId, String workspaceRef, String baseWorkspaceUrl, Boolean jobSubmitted) {
this.studioIdentifier = studioIdentifier;
this.workspaceId = workspaceId;
this.workspaceRef = workspaceRef;
this.studioUrl = String.format("%s/studios/%s/connect", baseWorkspaceUrl, sessionId);
this.studioUrl = String.format("%s/studios/%s/connect", baseWorkspaceUrl, studioIdentifier);
this.jobSubmitted = jobSubmitted;
}

@Override
public String toString() {
String isSuccess = jobSubmitted ? "successfully submitted" : "failed to submit";
return ansi(String.format("%n @|yellow Data Studio %s START %s at %s workspace.|@%n%n @|bold %s|@%n", sessionId, isSuccess, workspaceRef, studioUrl));
return ansi(String.format("%n @|yellow Data Studio %s START %s at %s workspace.|@%n%n @|bold %s|@%n", studioIdentifier, isSuccess, workspaceRef, studioUrl));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,14 @@

public class DataStudioStopSubmitted extends Response {

public final String sessionId;
public final String studioIdentifier;

public final Long workspaceId;
public final String workspaceRef;
public final Boolean jobSubmitted;

public DataStudioStopSubmitted(String sessionId, Long workspaceId, String workspaceRef, Boolean jobSubmitted) {
this.sessionId = sessionId;
public DataStudioStopSubmitted(String studioIdentifier, Long workspaceId, String workspaceRef, Boolean jobSubmitted) {
this.studioIdentifier = studioIdentifier;
this.workspaceId = workspaceId;
this.workspaceRef = workspaceRef;
this.jobSubmitted = jobSubmitted;
Expand All @@ -37,7 +37,7 @@ public DataStudioStopSubmitted(String sessionId, Long workspaceId, String worksp
@Override
public String toString() {
String isSuccess = jobSubmitted ? "successfully submitted" : "failed to submit";
return ansi(String.format("%n @|yellow Data Studio %s STOP %s at %s workspace.|@%n%n ", sessionId, isSuccess, workspaceRef));
return ansi(String.format("%n @|yellow Data Studio %s STOP %s at %s workspace.|@%n%n ", studioIdentifier, isSuccess, workspaceRef));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ void testList(OutputType format, MockServerClient mock) throws JsonProcessingExc
""", DataStudioDto.class),
parseJson("""
{
"sessionId": "c779bf09",
"sessionId": "3e8370e7",
"workspaceId": 75887156211589,
"parentCheckpoint": null,
"user": {
Expand All @@ -218,9 +218,9 @@ void testList(OutputType format, MockServerClient mock) throws JsonProcessingExc
"email": "[email protected]",
"avatar": null
},
"name": "studio-d456",
"description": null,
"studioUrl": "http://ac779bf09.studio.localhost:9191",
"name": "studio-a66d",
"description": "my first studio",
"studioUrl": "http://a3e8370e7.studio.localhost:9191",
"computeEnv": {
"id": "61DYXYj3XQAYbJIHrI1XSg",
"name": "my-local-ce",
Expand All @@ -235,7 +235,9 @@ void testList(OutputType format, MockServerClient mock) throws JsonProcessingExc
"gpu": 0,
"cpu": 2,
"memory": 8192,
"mountData": [],
"mountData": [
"v1-user-1ccf131810375d303bf0402dd8423433"
],
"condaEnvironment": null
},
"dateCreated": "2025-01-10T17:26:36.83703Z",
Expand Down Expand Up @@ -327,7 +329,7 @@ void testListWithOffset(OutputType format, MockServerClient mock) throws JsonPro
""", DataStudioDto.class),
parseJson("""
{
"sessionId": "c779bf09",
"sessionId": "3e8370e7",
"workspaceId": 75887156211589,
"parentCheckpoint": null,
"user": {
Expand All @@ -336,9 +338,9 @@ void testListWithOffset(OutputType format, MockServerClient mock) throws JsonPro
"email": "[email protected]",
"avatar": null
},
"name": "studio-d456",
"description": null,
"studioUrl": "http://ac779bf09.studio.localhost:9191",
"name": "studio-a66d",
"description": "my first studio",
"studioUrl": "http://a3e8370e7.studio.localhost:9191",
"computeEnv": {
"id": "61DYXYj3XQAYbJIHrI1XSg",
"name": "my-local-ce",
Expand All @@ -353,7 +355,9 @@ void testListWithOffset(OutputType format, MockServerClient mock) throws JsonPro
"gpu": 0,
"cpu": 2,
"memory": 8192,
"mountData": [],
"mountData": [
"v1-user-1ccf131810375d303bf0402dd8423433"
],
"condaEnvironment": null
},
"dateCreated": "2025-01-10T17:26:36.83703Z",
Expand Down Expand Up @@ -570,6 +574,62 @@ void testStart(OutputType format, MockServerClient mock) {
"[organization1 / workspace1]", "http://localhost:"+mock.getPort()+"/orgs/organization1/workspaces/workspace1", true));
}

@ParameterizedTest
@EnumSource(OutputType.class)
void testStartByName(OutputType format, MockServerClient mock) {

mock.when(
request().withMethod("GET").withPath("/user-info"), exactly(1)
).respond(
response().withStatusCode(200).withBody(loadResource("user")).withContentType(MediaType.APPLICATION_JSON)
);

mock.when(
request().withMethod("GET").withPath("/user/1264/workspaces"), exactly(1)
).respond(
response().withStatusCode(200).withBody(loadResource("workspaces/workspaces_list")).withContentType(MediaType.APPLICATION_JSON)
);

mock.when(
request().withMethod("GET").withPath("/studios").withQueryStringParameter("workspaceId", "75887156211589"), exactly(1)
).respond(
response().withStatusCode(200).withBody(loadResource("datastudios/datastudios_list_response")).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_studio_stopped")).withContentType(MediaType.APPLICATION_JSON)
);

mock.when(
request().withMethod("PUT").withPath("/studios/3e8370e7/start").withQueryStringParameter("workspaceId", "75887156211589").withBody(json("""
{
"configuration": {
"gpu": 0,
"cpu": 2,
"memory": 8192,
"mountData": [
"v1-user-1ccf131810375d303bf0402dd8423433"
]
},
"description": "my first studio"
}
"""
)

), exactly(1)
).respond(
response().withStatusCode(200).withBody(loadResource("datastudios/datastudios_start_response")).withContentType(MediaType.APPLICATION_JSON)
);


ExecOut out = exec(format, mock, "studios", "start", "-w", "organization1/workspace1", "-n" ,"studio-a66d");

assertOutput(format, out, new DataStudioStartSubmitted("studio-a66d", 75887156211589L,
"[organization1 / workspace1]", "http://localhost:"+mock.getPort()+"/orgs/organization1/workspaces/workspace1", true));
}

@ParameterizedTest
@EnumSource(OutputType.class)
void testStartWithConfigOverride(OutputType format, MockServerClient mock) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
"progress": null
},
{
"sessionId": "c779bf09",
"sessionId": "3e8370e7",
"workspaceId": 75887156211589,
"parentCheckpoint": null,
"user": {
Expand All @@ -53,9 +53,9 @@
"email": "[email protected]",
"avatar": null
},
"name": "studio-d456",
"description": null,
"studioUrl": "http://ac779bf09.studio.localhost:9191",
"name": "studio-a66d",
"description": "my first studio",
"studioUrl": "http://a3e8370e7.studio.localhost:9191",
"computeEnv": {
"id": "61DYXYj3XQAYbJIHrI1XSg",
"name": "my-local-ce",
Expand All @@ -70,7 +70,9 @@
"gpu": 0,
"cpu": 2,
"memory": 8192,
"mountData": [],
"mountData": [
"v1-user-1ccf131810375d303bf0402dd8423433"
],
"condaEnvironment": null
},
"dateCreated": "2025-01-10T17:26:36.83703Z",
Expand Down

0 comments on commit 7d31056

Please sign in to comment.