Skip to content

Commit

Permalink
[OPIK-159] Experiment delete endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
thiagohora committed Oct 3, 2024
1 parent cf0a072 commit 54870fb
Show file tree
Hide file tree
Showing 5 changed files with 158 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.DELETE;
import jakarta.ws.rs.DefaultValue;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
Expand Down Expand Up @@ -132,6 +133,20 @@ public Response create(
return Response.created(uri).build();
}

@DELETE
@Path("/{id}")
@Operation(operationId = "deleteExperimentById", summary = "Delete experiment", description = "Delete experiment", responses = {
@ApiResponse(responseCode = "204", description = "No content")})
public Response deleteExperimentById(@PathParam("id") UUID id) {

log.info("Deleting experiment by id '{}'", id);
experimentService.delete(id)
.contextWrite(ctx -> setRequestContext(ctx, requestContext))
.block();
log.info("Deleted experiment by id '{}'", id);
return Response.noContent().build();
}

// Experiment Item Resources

@GET
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,13 @@
import org.stringtemplate.v4.ST;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.core.publisher.SignalType;

import java.math.BigDecimal;
import java.time.Instant;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
Expand Down Expand Up @@ -362,6 +364,13 @@ AND ilike(name, CONCAT('%', :name, '%'))
;
""";

private static final String DELETE_BY_ID = """
DELETE FROM experiments
WHERE id = :id
AND workspace_id = :workspace_id
;
""";

private final @NonNull ConnectionFactory connectionFactory;

Mono<Void> insert(@NonNull Experiment experiment) {
Expand Down Expand Up @@ -526,4 +535,28 @@ public Flux<WorkspaceAndResourceId> getExperimentWorkspaces(@NonNull Set<UUID> e
row.get("workspace_id", String.class),
row.get("id", UUID.class))));
}

public Mono<Long> delete(UUID id) {
Preconditions.checkArgument(Objects.nonNull(id), "Argument 'id' must not be null");

log.info("Deleting experiment by id '{}'", id);

return Mono.from(connectionFactory.create())
.flatMapMany(connection -> delete(id, connection))
.reduce(Long::sum)
.doFinally(signalType -> {
if (signalType == SignalType.ON_COMPLETE) {
log.info("Deleted experiment by id '{}'", id);
}
});
}

private Publisher<Long> delete(UUID id, Connection connection) {

var statement = connection.createStatement(DELETE_BY_ID)
.bind("id", id);

return makeFluxContextAware(bindWorkspaceIdToFlux(statement))
.flatMap(Result::getRowsUpdated);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,12 @@
import org.stringtemplate.v4.ST;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.core.publisher.SignalType;

import java.time.Instant;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;

Expand Down Expand Up @@ -117,6 +119,13 @@ INSERT INTO experiment_items (
;
""";

public static final String DELETE_BY_EXPERIMENT_ID = """
DELETE FROM experiment_items
WHERE experiment_id = :experiment_id
AND workspace_id = :workspace_id
;
""";

private final @NonNull ConnectionFactory connectionFactory;

public Flux<ExperimentSummary> findExperimentSummaryByDatasetIds(Collection<UUID> datasetIds) {
Expand Down Expand Up @@ -260,4 +269,27 @@ private Publisher<? extends Result> delete(Set<UUID> ids, Connection connection)

return makeFluxContextAware(bindWorkspaceIdToFlux(statement));
}

public Mono<Long> deleteByExperimentId(UUID id) {
Preconditions.checkArgument(Objects.nonNull(id), "Argument 'id' must not be null");

log.info("Deleting experiment items by experiment id '{}'", id);

return Mono.from(connectionFactory.create())
.flatMapMany(connection -> deleteByExperimentId(id, connection))
.reduce(0L, Long::sum)
.doFinally(signalType -> {
if (signalType == SignalType.ON_COMPLETE) {
log.info("Deleted experiment items by experiment id '{}'", id);
}
});
}

private Publisher<Long> deleteByExperimentId(UUID id, Connection connection) {
Statement statement = connection.createStatement(DELETE_BY_EXPERIMENT_ID)
.bind("experiment_id", id);

return makeFluxContextAware(bindWorkspaceIdToFlux(statement))
.flatMap(Result::getRowsUpdated);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
public class ExperimentService {

private final @NonNull ExperimentDAO experimentDAO;
private final @NonNull ExperimentItemDAO experimentItemDAO;
private final @NonNull DatasetService datasetService;
private final @NonNull IdGenerator idGenerator;
private final @NonNull NameGenerator nameGenerator;
Expand Down Expand Up @@ -147,4 +148,10 @@ public Mono<Boolean> validateExperimentWorkspace(@NonNull String workspaceId, @N
return experimentDAO.getExperimentWorkspaces(experimentIds)
.all(experimentWorkspace -> workspaceId.equals(experimentWorkspace.workspaceId()));
}

public Mono<Void> delete(@NonNull UUID id) {
return experimentDAO.delete(id)
.then(Mono.defer(() -> experimentItemDAO.deleteByExperimentId(id)))
.then();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1626,6 +1626,77 @@ void getById() {
}
}

@Nested
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class DeleteExperimentItems {

@Test
void deleteExperimentById__whenExperimentExists__thenReturnNoContent() {
var experiment = podamFactory.manufacturePojo(Experiment.class);

createAndAssert(experiment, API_KEY, TEST_WORKSPACE);

deleteExperimentAndAssert(experiment.id(), API_KEY, TEST_WORKSPACE);
}

private void deleteExperimentAndAssert(UUID id, String apiKey, String workspaceName) {
try (var actualResponse = client.target(getExperimentsPath())
.path(id.toString())
.request()
.header(HttpHeaders.AUTHORIZATION, apiKey)
.header(WORKSPACE_HEADER, workspaceName)
.delete()) {

assertThat(actualResponse.getStatusInfo().getStatusCode()).isEqualTo(204);
}

getExperimentAndAssertNotFound(id, apiKey, workspaceName);
}

private void getExperimentAndAssertNotFound(UUID id, String apiKey, String workspaceName) {
try (var actualResponse = client.target(getExperimentsPath())
.path(id.toString())
.request()
.header(HttpHeaders.AUTHORIZATION, apiKey)
.header(WORKSPACE_HEADER, workspaceName)
.get()) {

assertThat(actualResponse.getStatusInfo().getStatusCode()).isEqualTo(404);
}
}

@Test
void deleteExperimentById__whenExperimentDoesNotExist__thenReturnNoContent() {
var experiment = podamFactory.manufacturePojo(Experiment.class);

getExperimentAndAssertNotFound(experiment.id(), API_KEY, TEST_WORKSPACE);

deleteExperimentAndAssert(experiment.id(), API_KEY, TEST_WORKSPACE);
}

@Test
void deleteExperimentById__whenExperimentHasItems__thenReturnNoContent() {
var experiment = podamFactory.manufacturePojo(Experiment.class);

createAndAssert(experiment, API_KEY, TEST_WORKSPACE);

var experimentItems = PodamFactoryUtils.manufacturePojoList(podamFactory, ExperimentItem.class).stream()
.map(experimentItem -> experimentItem.toBuilder().experimentId(experiment.id()).build())
.collect(Collectors.toUnmodifiableSet());

var createRequest = ExperimentItemsBatch.builder().experimentItems(experimentItems).build();

createAndAssert(createRequest, API_KEY, TEST_WORKSPACE);

deleteExperimentAndAssert(experiment.id(), API_KEY, TEST_WORKSPACE);

experimentItems
.stream()
.parallel()
.forEach(experimentItem -> getAndAssertNotFound(experimentItem.id(), API_KEY, TEST_WORKSPACE));
}
}

@Nested
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class StreamExperimentItems {
Expand Down

0 comments on commit 54870fb

Please sign in to comment.