Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(graphQL): add missing subqueries/mutations #319

Merged
merged 18 commits into from
Apr 13, 2024
34 changes: 34 additions & 0 deletions src/main/java/io/cryostat/graphql/ActiveRecordings.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import java.time.Duration;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
Expand Down Expand Up @@ -322,6 +323,39 @@ public RecordingOptions asOptions() {
}
}

@Blocking
@Transactional
@Description("Updates the metadata labels for an existing Flight Recording.")
public Uni<ActiveRecording> doPutMetadata(
@Source ActiveRecording recording, MetadataLabels metadataInput) {
return Uni.createFrom()
.item(
() -> {
return recordingHelper.updateRecordingMetadata(
recording.id, metadataInput.getLabels());
});
}

@SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
public static class MetadataLabels {

private Map<String, String> labels;

public MetadataLabels() {}

public MetadataLabels(Map<String, String> labels) {
this.labels = new HashMap<>(labels);
}

public Map<String, String> getLabels() {
return new HashMap<>(labels);
}

public void setLabels(Map<String, String> labels) {
this.labels = new HashMap<>(labels);
}
}

@SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
public static class RecordingMetadata {
public @Nullable Map<String, String> labels;
Expand Down
28 changes: 28 additions & 0 deletions src/main/java/io/cryostat/graphql/ArchivedRecordings.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,20 @@
import java.util.Objects;
import java.util.function.Predicate;

import io.cryostat.graphql.ActiveRecordings.MetadataLabels;
import io.cryostat.graphql.TargetNodes.AggregateInfo;
import io.cryostat.graphql.TargetNodes.Recordings;
import io.cryostat.graphql.matchers.LabelSelectorMatcher;
import io.cryostat.recordings.RecordingHelper;
import io.cryostat.recordings.Recordings.ArchivedRecording;
import io.cryostat.recordings.Recordings.Metadata;

import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import io.smallrye.common.annotation.Blocking;
import io.smallrye.graphql.api.Nullable;
import jakarta.inject.Inject;
import org.eclipse.microprofile.graphql.GraphQLApi;
import org.eclipse.microprofile.graphql.NonNull;
import org.eclipse.microprofile.graphql.Query;
import org.eclipse.microprofile.graphql.Source;

Expand Down Expand Up @@ -69,6 +72,31 @@ public TargetNodes.ArchivedRecordings archived(
return out;
}

@NonNull
public ArchivedRecording doDelete(@Source ArchivedRecording recording) {
recordingHelper.deleteArchivedRecording(recording.jvmId(), recording.name());
return recording;
}

@NonNull
public ArchivedRecording doPutMetadata(
@Source ArchivedRecording recording, MetadataLabels metadataInput) {
recordingHelper.updateArchivedRecordingMetadata(
recording.jvmId(), recording.name(), metadataInput.getLabels());

String downloadUrl = recordingHelper.downloadUrl(recording.jvmId(), recording.name());
String reportUrl = recordingHelper.reportUrl(recording.jvmId(), recording.name());

return new ArchivedRecording(
recording.jvmId(),
recording.name(),
downloadUrl,
reportUrl,
new Metadata(metadataInput.getLabels()),
recording.size(),
recording.archivedTime());
}

@SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
public static class ArchivedRecordingsFilter implements Predicate<ArchivedRecording> {
public @Nullable String name;
Expand Down
11 changes: 9 additions & 2 deletions src/main/java/io/cryostat/recordings/ActiveRecording.java
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,10 @@ public static ActiveRecording getByName(String name) {
return find("name", name).singleResult();
}

public void setMetadata(Metadata metadata) {
this.metadata = metadata;
}

@Transactional
public static boolean deleteFromTarget(Target target, String recordingName) {
Optional<ActiveRecording> recording =
Expand Down Expand Up @@ -285,16 +289,19 @@ public record ActiveRecordingEvent(
Objects.requireNonNull(payload);
}

public record Payload(String target, LinkedRecordingDescriptor recording) {
public record Payload(
String target, LinkedRecordingDescriptor recording, String jvmId) {
public Payload {
Objects.requireNonNull(target);
Objects.requireNonNull(recording);
Objects.requireNonNull(jvmId);
}

public static Payload of(RecordingHelper helper, ActiveRecording recording) {
return new Payload(
recording.target.connectUrl.toString(),
helper.toExternalForm(recording));
helper.toExternalForm(recording),
recording.target.jvmId);
}
}
}
Expand Down
90 changes: 90 additions & 0 deletions src/main/java/io/cryostat/recordings/RecordingHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@
import jakarta.inject.Named;
import jakarta.transaction.Transactional;
import jakarta.ws.rs.BadRequestException;
import jakarta.ws.rs.NotFoundException;
import jakarta.ws.rs.ServerErrorException;
import jdk.jfr.RecordingState;
import org.apache.commons.codec.binary.Base64;
Expand All @@ -109,8 +110,10 @@
import software.amazon.awssdk.services.s3.model.DeleteObjectRequest;
import software.amazon.awssdk.services.s3.model.GetObjectRequest;
import software.amazon.awssdk.services.s3.model.GetObjectTaggingRequest;
import software.amazon.awssdk.services.s3.model.HeadObjectRequest;
import software.amazon.awssdk.services.s3.model.ListObjectsV2Request;
import software.amazon.awssdk.services.s3.model.NoSuchKeyException;
import software.amazon.awssdk.services.s3.model.PutObjectTaggingRequest;
import software.amazon.awssdk.services.s3.model.S3Object;
import software.amazon.awssdk.services.s3.model.Tag;
import software.amazon.awssdk.services.s3.model.Tagging;
Expand All @@ -136,6 +139,7 @@ public class RecordingHelper {
@Inject EventOptionsBuilder.Factory eventOptionsBuilderFactory;
@Inject TargetTemplateService.Factory targetTemplateServiceFactory;
@Inject S3TemplateService customTemplateService;
@Inject RecordingHelper recordingHelper;

@Inject
@Named(Producers.BASE64_URL)
Expand Down Expand Up @@ -557,6 +561,7 @@ public List<ArchivedRecording> listArchivedRecordings() {
getArchivedRecordingMetadata(jvmId, filename)
.orElseGet(Metadata::empty);
return new ArchivedRecording(
jvmId,
andrewazores marked this conversation as resolved.
Show resolved Hide resolved
filename,
downloadUrl(jvmId, filename),
reportUrl(jvmId, filename),
Expand Down Expand Up @@ -595,6 +600,7 @@ public List<ArchivedRecording> listArchivedRecordings(String jvmId) {
getArchivedRecordingMetadata(jvmId, filename)
.orElseGet(Metadata::empty);
return new ArchivedRecording(
jvmId,
filename,
downloadUrl(jvmId, filename),
reportUrl(jvmId, filename),
Expand Down Expand Up @@ -733,6 +739,7 @@ public ArchivedRecording archiveRecording(
new Notification(event.category().category(), event.payload()));
}
return new ArchivedRecording(
activeRecording.target.jvmId,
filename,
downloadUrl(activeRecording.target.jvmId, filename),
reportUrl(activeRecording.target.jvmId, filename),
Expand Down Expand Up @@ -869,6 +876,7 @@ public void deleteArchivedRecording(String jvmId, String filename) {
ArchivedRecordingEvent.Payload.of(
target.map(t -> t.connectUrl).orElse(null),
new ArchivedRecording(
jvmId,
filename,
downloadUrl(jvmId, filename),
reportUrl(jvmId, filename),
Expand Down Expand Up @@ -945,6 +953,88 @@ private Metadata taggingToMetadata(List<Tag> tagSet) {
return new Metadata(labels, expiry);
}

@Blocking
public ActiveRecording updateRecordingMetadata(
long recordingId, Map<String, String> newLabels) {
ActiveRecording recording = ActiveRecording.findById(recordingId);

if (recording == null) {
throw new NotFoundException("Recording not found for ID: " + recordingId);
}

if (!recording.metadata.labels().equals(newLabels)) {
Metadata updatedMetadata = new Metadata(newLabels);
recording.setMetadata(updatedMetadata);
recording.persist();

notify(
new ActiveRecordingEvent(
Recordings.RecordingEventCategory.METADATA_UPDATED,
ActiveRecordingEvent.Payload.of(recordingHelper, recording)));
}
return recording;
}

private void notify(ActiveRecordingEvent event) {
bus.publish(
MessagingServer.class.getName(),
new Notification(event.category().category(), event.payload()));
}

@Blocking
public ArchivedRecording updateArchivedRecordingMetadata(
String jvmId, String filename, Map<String, String> updatedLabels) {
String key = archivedRecordingKey(jvmId, filename);
Optional<Metadata> existingMetadataOpt = getArchivedRecordingMetadata(key);

if (existingMetadataOpt.isEmpty()) {
throw new NotFoundException(
"Could not find metadata for archived recording with key: " + key);
}

Metadata updatedMetadata = new Metadata(updatedLabels);

Tagging tagging = createMetadataTagging(updatedMetadata);
storage.putObjectTagging(
PutObjectTaggingRequest.builder()
.bucket(archiveBucket)
.key(key)
.tagging(tagging)
.build());

var response =
storage.headObject(
HeadObjectRequest.builder().bucket(archiveBucket).key(key).build());
long size = response.contentLength();
Instant lastModified = response.lastModified();

ArchivedRecording updatedRecording =
new ArchivedRecording(
jvmId,
filename,
downloadUrl(jvmId, filename),
reportUrl(jvmId, filename),
updatedMetadata,
size,
lastModified.getEpochSecond());

notifyArchiveMetadataUpdate(updatedRecording);
return updatedRecording;
}

private void notifyArchiveMetadataUpdate(ArchivedRecording updatedRecording) {

var event =
new ArchivedRecordingEvent(
Recordings.RecordingEventCategory.METADATA_UPDATED,
new ArchivedRecordingEvent.Payload(
updatedRecording.downloadUrl(), updatedRecording));
bus.publish(event.category().category(), event.payload().recording());
bus.publish(
MessagingServer.class.getName(),
new Notification(event.category().category(), event.payload()));
}

@Blocking
public Uni<String> uploadToJFRDatasource(long targetEntityId, long remoteId) throws Exception {
Target target = Target.getTargetById(targetEntityId);
Expand Down
10 changes: 10 additions & 0 deletions src/main/java/io/cryostat/recordings/Recordings.java
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@ public void agentPush(
ArchivedRecordingEvent.Payload.of(
target.map(t -> t.connectUrl).orElse(null),
new ArchivedRecording(
jvmId,
recording.fileName(),
recordingHelper.downloadUrl(jvmId, recording.fileName()),
recordingHelper.reportUrl(jvmId, recording.fileName()),
Expand Down Expand Up @@ -275,6 +276,7 @@ public List<ArchivedRecording> agentGet(@RestPath String jvmId) {
.orElseGet(Metadata::empty);
result.add(
new ArchivedRecording(
jvmId,
filename,
recordingHelper.downloadUrl(jvmId, filename),
recordingHelper.reportUrl(jvmId, filename),
Expand Down Expand Up @@ -338,6 +340,7 @@ Map<String, Object> doUpload(FileUpload recording, Metadata metadata, String jvm
ArchivedRecordingEvent.Payload.of(
target.map(t -> t.connectUrl).orElse(null),
new ArchivedRecording(
jvmId,
filename,
recordingHelper.downloadUrl(jvmId, filename),
recordingHelper.reportUrl(jvmId, filename),
Expand Down Expand Up @@ -395,6 +398,7 @@ public Collection<ArchivedRecordingDirectory> listFsArchives() {
connectUrl, id, new ArrayList<>()));
dir.recordings.add(
new ArchivedRecording(
jvmId,
filename,
recordingHelper.downloadUrl(jvmId, filename),
recordingHelper.reportUrl(jvmId, filename),
Expand Down Expand Up @@ -432,6 +436,7 @@ public Collection<ArchivedRecordingDirectory> listFsArchives(@RestPath String jv
connectUrl, id, new ArrayList<>()));
dir.recordings.add(
new ArchivedRecording(
jvmId,
filename,
recordingHelper.downloadUrl(jvmId, filename),
recordingHelper.reportUrl(jvmId, filename),
Expand Down Expand Up @@ -768,6 +773,7 @@ public void deleteArchivedRecording(@RestPath String jvmId, @RestPath String fil
ArchivedRecordingEvent.Payload.of(
URI.create(connectUrl),
new ArchivedRecording(
jvmId,
filename,
recordingHelper.downloadUrl(jvmId, filename),
recordingHelper.reportUrl(jvmId, filename),
Expand Down Expand Up @@ -1127,13 +1133,15 @@ public record LinkedRecordingDescriptor(

// TODO include jvmId and filename
public record ArchivedRecording(
String jvmId,
String name,
String downloadUrl,
String reportUrl,
Metadata metadata,
long size,
long archivedTime) {
public ArchivedRecording {
Objects.requireNonNull(jvmId);
Objects.requireNonNull(name);
Objects.requireNonNull(downloadUrl);
Objects.requireNonNull(reportUrl);
Expand Down Expand Up @@ -1183,6 +1191,7 @@ public static Metadata empty() {
public static final String ACTIVE_RECORDING_DELETED = "ActiveRecordingDeleted";
public static final String ACTIVE_RECORDING_SAVED = "ActiveRecordingSaved";
public static final String SNAPSHOT_RECORDING_CREATED = "SnapshotCreated";
public static final String RECORDING_METADATA_UPDATED = "RecordingMetadataUpdated";

public enum RecordingEventCategory {
ACTIVE_CREATED(ACTIVE_RECORDING_CREATED),
Expand All @@ -1192,6 +1201,7 @@ public enum RecordingEventCategory {
ARCHIVED_CREATED(ARCHIVED_RECORDING_CREATED),
ARCHIVED_DELETED(ARCHIVED_RECORDING_DELETED),
SNAPSHOT_CREATED(SNAPSHOT_RECORDING_CREATED),
METADATA_UPDATED(RECORDING_METADATA_UPDATED),
;

private final String category;
Expand Down
12 changes: 9 additions & 3 deletions src/main/java/io/cryostat/targets/Target.java
Original file line number Diff line number Diff line change
Expand Up @@ -188,10 +188,11 @@ public enum EventKind {
}

@SuppressFBWarnings(value = {"EI_EXPOSE_REP", "EI_EXPOSE_REP2"})
public record TargetDiscovery(EventKind kind, Target serviceRef) {
public record TargetDiscovery(EventKind kind, Target serviceRef, String jvmId) {
public TargetDiscovery {
Objects.requireNonNull(kind);
Objects.requireNonNull(serviceRef);
Objects.requireNonNull(jvmId);
}
}

Expand Down Expand Up @@ -279,14 +280,19 @@ private void notify(EventKind eventKind, Target target) {
MessagingServer.class.getName(),
new Notification(
TARGET_JVM_DISCOVERY,
new TargetDiscoveryEvent(new TargetDiscovery(eventKind, target))));
bus.publish(TARGET_JVM_DISCOVERY, new TargetDiscovery(eventKind, target));
new TargetDiscoveryEvent(
new TargetDiscovery(eventKind, target, target.jvmId))));
bus.publish(TARGET_JVM_DISCOVERY, new TargetDiscovery(eventKind, target, target.jvmId));
}

public record TargetDiscoveryEvent(TargetDiscovery event) {
public TargetDiscoveryEvent {
Objects.requireNonNull(event);
}

public String jvmId() {
return event.serviceRef().jvmId;
}
}
}
}
Loading