Skip to content

Commit

Permalink
Added chart annotation endpoints (#4604)
Browse files Browse the repository at this point in the history
Co-authored-by: jryu01 <[email protected]>
  • Loading branch information
jryu01 and jryu01 authored Aug 30, 2024
1 parent fabf682 commit 7c31972
Show file tree
Hide file tree
Showing 5 changed files with 295 additions and 0 deletions.
9 changes: 9 additions & 0 deletions packages/client/hmi-client/src/types/Types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,15 @@ export interface Artifact extends TerariumAsset {
metadata?: any;
}

export interface ChartAnnotation extends TerariumAsset {
nodeId: string;
outputId: string;
chartId: string;
layerSpec: any;
llmGenerated: boolean;
metadata: any;
}

export interface CsvAsset {
csv: string[][];
stats?: CsvColumnStats[];
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
package software.uncharted.terarium.hmiserver.controller.dataservice;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import jakarta.transaction.Transactional;
import java.io.IOException;
import java.util.List;
import java.util.UUID;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.annotation.Secured;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.server.ResponseStatusException;
import software.uncharted.terarium.hmiserver.models.dataservice.ChartAnnotation;
import software.uncharted.terarium.hmiserver.models.dataservice.ResponseDeleted;
import software.uncharted.terarium.hmiserver.security.Roles;
import software.uncharted.terarium.hmiserver.service.CurrentUserService;
import software.uncharted.terarium.hmiserver.service.data.ChartAnnotationService;
import software.uncharted.terarium.hmiserver.service.data.ProjectAssetService;
import software.uncharted.terarium.hmiserver.service.data.ProjectService;
import software.uncharted.terarium.hmiserver.utils.rebac.Schema;

@RequestMapping("/chart-annotations")
@RestController
@Slf4j
@RequiredArgsConstructor
@Transactional
public class ChartAnnotationController {

final ChartAnnotationService chartAnnotationService;

final ProjectAssetService projectAssetService;

final ProjectService projectService;

final CurrentUserService currentUserService;

private static class SearchRequestBody {

public UUID nodeId;
}

@PostMapping("/search")
@Secured(Roles.USER)
@Operation(summary = "Gets a list of chart annotations by provided node ID")
@ApiResponses(
value = {
@ApiResponse(
responseCode = "200",
description = "chart annotations found.",
content = @Content(
mediaType = MediaType.APPLICATION_JSON_VALUE,
schema = @io.swagger.v3.oas.annotations.media.Schema(implementation = ChartAnnotation.class)
)
),
@ApiResponse(responseCode = "204", description = "There was no chart annotation found", content = @Content),
@ApiResponse(
responseCode = "500",
description = "There was an issue retrieving the chart annotations from the data store",
content = @Content
)
}
)
public ResponseEntity<List<ChartAnnotation>> getChartAnnotationsByNodeId(
@RequestParam(name = "project-id", required = false) final UUID projectId,
@RequestBody final SearchRequestBody body
) {
final Schema.Permission permission = projectService.checkPermissionCanRead(
currentUserService.get().getId(),
projectId
);

final List<ChartAnnotation> chartAnnotations = chartAnnotationService.getAnnotationsByNodeId(
body.nodeId,
permission
);

return ResponseEntity.ok(chartAnnotations);
}

@PostMapping
@Secured(Roles.USER)
@Operation(summary = "Create a new annotation")
@ApiResponses(
value = {
@ApiResponse(
responseCode = "201",
description = "Annotation created.",
content = @Content(
mediaType = MediaType.APPLICATION_JSON_VALUE,
schema = @io.swagger.v3.oas.annotations.media.Schema(implementation = ChartAnnotation.class)
)
),
@ApiResponse(
responseCode = "500",
description = "There was an issue creating the chart annotation",
content = @Content
)
}
)
public ResponseEntity<ChartAnnotation> createChartAnnotation(
@RequestBody final ChartAnnotation item,
@RequestParam(name = "project-id", required = false) final UUID projectId
) {
final Schema.Permission permission = projectService.checkPermissionCanWrite(
currentUserService.get().getId(),
projectId
);
try {
return ResponseEntity.status(HttpStatus.CREATED).body(
chartAnnotationService.createAsset(item, projectId, permission)
);
} catch (final IOException e) {
final String error = "Unable to create chart annotation";
log.error(error, e);
throw new ResponseStatusException(org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR, error);
}
}

@DeleteMapping("/{id}")
@Secured(Roles.USER)
@Operation(summary = "Delete a chart annotation by ID")
@ApiResponses(
value = {
@ApiResponse(
responseCode = "200",
description = "Delete chart annotation",
content = {
@Content(
mediaType = "application/json",
schema = @io.swagger.v3.oas.annotations.media.Schema(implementation = ResponseDeleted.class)
)
}
),
@ApiResponse(
responseCode = "500",
description = "There was an issue deleting the chart annotation",
content = @Content
)
}
)
public ResponseEntity<ResponseDeleted> deleteChartAnnotation(
@PathVariable("id") final UUID id,
@RequestParam(name = "project-id", required = false) final UUID projectId
) {
final Schema.Permission permission = projectService.checkPermissionCanWrite(
currentUserService.get().getId(),
projectId
);

try {
chartAnnotationService.deleteAsset(id, projectId, permission);
return ResponseEntity.ok(new ResponseDeleted("ChartAnnotation", id));
} catch (final Exception e) {
final String error = String.format("Failed to delete chart annotation %s", id);
log.error(error, e);
throw new ResponseStatusException(org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR, error);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package software.uncharted.terarium.hmiserver.models.dataservice;

import com.fasterxml.jackson.annotation.JsonAlias;
import com.fasterxml.jackson.databind.JsonNode;
import io.hypersistence.utils.hibernate.type.json.JsonType;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import java.util.UUID;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import org.hibernate.annotations.Type;
import software.uncharted.terarium.hmiserver.annotations.TSModel;
import software.uncharted.terarium.hmiserver.models.TerariumAsset;

@Data
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = true)
@TSModel
@Entity
public class ChartAnnotation extends TerariumAsset {

// TODO: Add chart type enums once we have the full list of the charts we support

// @TSModel
// public enum ChartType {
// @JsonAlias("variable")
// VARIABLE,
// @JsonAlias("parameter")
// PARAMETER,
// @JsonAlias("loss")
// LOSS,
// @JsonAlias("error")
// ERROR
// }

private UUID nodeId;
private UUID outputId;

private UUID chartId;

// TODO: add chart type to specify which type of chart this annotation is applied to
// @Enumerated(EnumType.STRING)
// private ChartType chartType;

@Type(JsonType.class)
@Column(columnDefinition = "json")
private JsonNode layerSpec;

private boolean llmGenerated;

@Type(JsonType.class)
@Column(columnDefinition = "json")
private JsonNode metadata;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package software.uncharted.terarium.hmiserver.repository.data;

import java.util.List;
import java.util.UUID;
import org.springframework.stereotype.Repository;
import software.uncharted.terarium.hmiserver.models.dataservice.ChartAnnotation;
import software.uncharted.terarium.hmiserver.repository.PSCrudSoftDeleteRepository;

@Repository
public interface ChartAnnotationRepository extends PSCrudSoftDeleteRepository<ChartAnnotation, UUID> {
List<ChartAnnotation> findByNodeIdAndDeletedOnIsNull(UUID nodeId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package software.uncharted.terarium.hmiserver.service.data;

import com.fasterxml.jackson.databind.ObjectMapper;
import io.micrometer.observation.annotation.Observed;
import java.util.List;
import java.util.UUID;
import org.springframework.stereotype.Service;
import software.uncharted.terarium.hmiserver.configuration.Config;
import software.uncharted.terarium.hmiserver.models.dataservice.ChartAnnotation;
import software.uncharted.terarium.hmiserver.repository.data.ChartAnnotationRepository;
import software.uncharted.terarium.hmiserver.service.s3.S3ClientService;
import software.uncharted.terarium.hmiserver.utils.rebac.Schema;

@Service
public class ChartAnnotationService
extends TerariumAssetServiceWithoutSearch<ChartAnnotation, ChartAnnotationRepository> {

public ChartAnnotationService(
final ObjectMapper objectMapper,
final Config config,
final ProjectService projectService,
final ProjectAssetService projectAssetService,
final ChartAnnotationRepository repository,
final S3ClientService s3ClientService
) {
super(
objectMapper,
config,
projectService,
projectAssetService,
repository,
s3ClientService,
ChartAnnotation.class
);
}

public List<ChartAnnotation> getAnnotationsByNodeId(UUID nodeId, final Schema.Permission hasReadPermission) {
return repository.findByNodeIdAndDeletedOnIsNull(nodeId);
}

@Override
@Observed(name = "function_profile")
protected String getAssetPath() {
throw new UnsupportedOperationException("Chart annotations are not stored in S3");
}
}

0 comments on commit 7c31972

Please sign in to comment.