Skip to content

Commit

Permalink
feat(graphql): recording metadata/labels handling (#1227)
Browse files Browse the repository at this point in the history
Signed-off-by: Atif Ali <56743004+aali309@users.noreply.github.com>
Co-authored-by: Andrew Azores <aazores@redhat.com>
  • Loading branch information
aali309 and andrewazores authored Apr 13, 2024
1 parent dcc22a5 commit 5bd950b
Show file tree
Hide file tree
Showing 9 changed files with 315 additions and 114 deletions.
32 changes: 26 additions & 6 deletions src/app/Archives/AllArchivedRecordingsTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -168,12 +168,32 @@ export const AllArchivedRecordingsTable: React.FC<AllArchivedRecordingsTableProp
}, [context.settings, refreshDirectoriesAndCounts]);

React.useEffect(() => {
addSubscription(
context.notificationChannel.messages(NotificationCategory.RecordingMetadataUpdated).subscribe(() => {
refreshDirectoriesAndCounts();
}),
);
}, [addSubscription, context.notificationChannel, refreshDirectoriesAndCounts]);
const subscription = context.notificationChannel
.messages(NotificationCategory.RecordingMetadataUpdated)
.subscribe((event) => {
const updatedRecordingInfo = event.message;

setDirectories((currentDirectories) => {
const newDirectories = currentDirectories.map((directory) => ({
...directory,
recordings: directory.recordings.map((recording) => {
if (recording.name === updatedRecordingInfo.recording.name) {
return {
...recording,
metadata: { ...recording.metadata, labels: updatedRecordingInfo.recording.metadata.labels },
};
}
return recording;
}),
}));
return newDirectories;
});
});

return () => {
subscription.unsubscribe();
};
}, [context.notificationChannel]);

React.useEffect(() => {
addSubscription(
Expand Down
152 changes: 115 additions & 37 deletions src/app/Archives/AllTargetsArchivedRecordingsTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { ErrorView } from '@app/ErrorView/ErrorView';
import { authFailMessage, isAuthFail } from '@app/ErrorView/types';
import { ArchivedRecordingsTable } from '@app/Recordings/ArchivedRecordingsTable';
import { LoadingView } from '@app/Shared/Components/LoadingView';
import { Target, TargetDiscoveryEvent, NotificationCategory } from '@app/Shared/Services/api.types';
import { Target, TargetDiscoveryEvent, NotificationCategory, Metadata } from '@app/Shared/Services/api.types';
import { isEqualTarget, indexOfTarget, includesTarget } from '@app/Shared/Services/api.utils';
import { ServiceContext } from '@app/Shared/Services/Services';
import { useSort } from '@app/utils/hooks/useSort';
Expand Down Expand Up @@ -70,10 +70,21 @@ const tableColumns: TableColumn[] = [
},
];

interface ArchivedRecording {
jvmId?: string;
name: string;
downloadUrl: string;
reportUrl: string;
metadata: Metadata;
size: number;
archivedTime: number;
}

type ArchivesForTarget = {
target: Target;
targetAsObs: Observable<Target>;
archiveCount: number;
recordings: ArchivedRecording[];
};

export interface AllTargetsArchivedRecordingsTableProps {}
Expand All @@ -89,16 +100,25 @@ export const AllTargetsArchivedRecordingsTable: React.FC<AllTargetsArchivedRecor
const addSubscription = useSubscriptions();
const [sortBy, getSortParams] = useSort();

const updateCount = React.useCallback(
(connectUrl: string, delta: number) => {
const handleNotification = React.useCallback(
(_: string, recording: ArchivedRecording, delta: number) => {
setArchivesForTargets((old) => {
const idx = old.findIndex(({ target }) => target.connectUrl === connectUrl);
if (idx >= 0) {
const matched = old[idx];
old.splice(idx, 1, { ...matched, archiveCount: matched.archiveCount + delta });
return [...old];
const matchingTargets = old.filter(({ target }) => target.jvmId === recording.jvmId);
for (const matchedTarget of matchingTargets) {
const targetIdx = old.findIndex(({ target }) => target.connectUrl === matchedTarget.target.connectUrl);

const recordings = [...matchedTarget.recordings];
if (delta === 1) {
recordings.push(recording);
} else {
const recordingIdx = recordings.findIndex((r) => r.name === recording.name);
recordings.splice(recordingIdx, 1);
}

old.splice(targetIdx, 1, { ...matchedTarget, archiveCount: matchedTarget.archiveCount + delta, recordings });
}
return old;

return [...old];
});
},
[setArchivesForTargets],
Expand All @@ -112,6 +132,7 @@ export const AllTargetsArchivedRecordingsTable: React.FC<AllTargetsArchivedRecor
setArchivesForTargets(
targetNodes.map((node) => {
const target: Target = {
jvmId: node.target.jvmId,
connectUrl: node.target.connectUrl,
alias: node.target.alias,
labels: [],
Expand All @@ -123,7 +144,8 @@ export const AllTargetsArchivedRecordingsTable: React.FC<AllTargetsArchivedRecor
return {
target,
targetAsObs: of(target),
archiveCount: node.target.archivedRecordings.aggregate.count,
archiveCount: node?.archiveCount ?? 0,
recordings: node?.recordings ?? [],
};
}),
);
Expand All @@ -139,27 +161,71 @@ export const AllTargetsArchivedRecordingsTable: React.FC<AllTargetsArchivedRecor
[setIsLoading, setErrorMessage],
);

/* eslint-disable @typescript-eslint/no-explicit-any */
const refreshArchivesForTargets = React.useCallback(() => {
setIsLoading(true);
addSubscription(
context.api
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
.graphql<any>(
`query AllTargetsArchives {
targetNodes {
target {
connectUrl
alias
archivedRecordings {
aggregate {
count
}
}
}
}
}`,
targetNodes {
target {
connectUrl
alias
jvmId
archivedRecordings {
data {
jvmId
name
downloadUrl
reportUrl
metadata {
labels {
key
value
}
}
size
archivedTime
}
aggregate {
count
}
}
}
}
}`,
)
.pipe(
map((v) => {
return v.data.targetNodes.map((node) => {
return {
target: {
jvmId: node.target.jvmId,
connectUrl: node.target.connectUrl,
alias: node.target.alias,
labels: [],
annotations: {
cryostat: [],
platform: [],
},
},
targetAsObs: of({
jvmId: node.target.jvmId,
connectUrl: node.target.connectUrl,
alias: node.target.alias,
labels: [],
annotations: {
cryostat: [],
platform: [],
},
}),
archiveCount: node.target.archivedRecordings.aggregate.count,
recordings: node.target.archivedRecordings.data as ArchivedRecording[],
};
});
}),
)
.pipe(map((v) => v.data.targetNodes))
.subscribe({
next: handleArchivesForTargets,
error: handleError,
Expand All @@ -177,6 +243,19 @@ export const AllTargetsArchivedRecordingsTable: React.FC<AllTargetsArchivedRecor
targetNodes(filter: { name: $connectUrl }) {
target {
archivedRecordings {
data {
name
downloadUrl
reportUrl
metadata {
labels {
key
value
}
}
size
archivedTime
}
aggregate {
count
}
Expand All @@ -193,7 +272,8 @@ export const AllTargetsArchivedRecordingsTable: React.FC<AllTargetsArchivedRecor
{
target: target,
targetAsObs: of(target),
archiveCount: v.data.targetNodes[0].target.archivedRecordings.aggregate.count,
archiveCount: v.data.targetNodes[0]?.target?.archivedRecordings?.aggregate?.count ?? 0,
recordings: v.data.targetNodes[0]?.target?.archivedRecordings,
},
];
});
Expand Down Expand Up @@ -309,29 +389,27 @@ export const AllTargetsArchivedRecordingsTable: React.FC<AllTargetsArchivedRecor
);
}, [addSubscription, context.notificationChannel, handleTargetNotification]);

React.useEffect(() => {
addSubscription(
context.notificationChannel.messages(NotificationCategory.ActiveRecordingSaved).subscribe((v) => {
updateCount(v.message.target, 1);
}),
);
}, [addSubscription, context.notificationChannel, updateCount]);

React.useEffect(() => {
addSubscription(
context.notificationChannel.messages(NotificationCategory.ArchivedRecordingCreated).subscribe((v) => {
updateCount(v.message.target, 1);
handleNotification(v.message.target, v.message.recording, 1);
}),
);
}, [addSubscription, context.notificationChannel, updateCount]);
}, [addSubscription, context.notificationChannel, handleNotification]);

React.useEffect(() => {
addSubscription(
context.notificationChannel.messages(NotificationCategory.ArchivedRecordingDeleted).subscribe((v) => {
updateCount(v.message.target, -1);
handleNotification(v.message.target, v.message.recording, -1);
}),
);
}, [addSubscription, context.notificationChannel, updateCount]);
}, [
addSubscription,
context.notificationChannel,
handleNotification,
refreshArchivesForTargets,
getCountForNewTarget,
]);

const toggleExpanded = React.useCallback(
(target) => {
Expand Down
42 changes: 31 additions & 11 deletions src/app/RecordingMetadata/BulkEditLabels.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ export const BulkEditLabels: React.FC<BulkEditLabelsProps> = ({
return !includesLabel(toDelete, label);
});
if (directory) {
tasks.push(context.api.postRecordingMetadataFromPath(directory.jvmId, r.name, updatedLabels).pipe(first()));
tasks.push(context.api.postRecordingMetadataForJvmId(directory.jvmId, r.name, updatedLabels).pipe(first()));
}
if (isTargetRecording) {
tasks.push(context.api.postTargetRecordingMetadata(r.name, updatedLabels).pipe(first()));
Expand Down Expand Up @@ -171,7 +171,10 @@ export const BulkEditLabels: React.FC<BulkEditLabelsProps> = ({
downloadUrl
reportUrl
metadata {
labels
labels {
key
value
}
}
}
}
Expand All @@ -195,8 +198,13 @@ export const BulkEditLabels: React.FC<BulkEditLabelsProps> = ({
downloadUrl
reportUrl
metadata {
labels
labels {
key
value
}
}
size
archivedTime
}
}
}
Expand Down Expand Up @@ -246,14 +254,26 @@ export const BulkEditLabels: React.FC<BulkEditLabelsProps> = ({
]).subscribe((parts) => {
const currentTarget = parts[0];
const event = parts[1];
if (currentTarget?.connectUrl != event.message.target && currentTarget?.jvmId != event.message.jvmId) {
return;
}
setRecordings((old) =>
old.map((o) =>
o.name == event.message.recordingName ? { ...o, metadata: { labels: event.message.metadata.labels } } : o,
),
);

const isMatch =
currentTarget?.connectUrl === event.message.target ||
currentTarget?.jvmId === event.message.recording.jvmId ||
currentTarget?.connectUrl === 'uploads';

setRecordings((oldRecordings) => {
return oldRecordings.map((recording) => {
if (isMatch && recording.name === event.message.recording.name) {
const updatedRecording = {
...recording,
metadata: {
labels: event.message.recording.metadata.labels,
},
};
return updatedRecording;
}
return recording;
});
});
}),
);
}, [addSubscription, context.target, context.notificationChannel, setRecordings, isUploadsTable]);
Expand Down
14 changes: 9 additions & 5 deletions src/app/Recordings/ActiveRecordingsTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -315,11 +315,15 @@ export const ActiveRecordingsTable: React.FC<ActiveRecordingsTableProps> = (prop
if (currentTarget?.connectUrl != event.message.target && currentTarget?.jvmId != event.message.jvmId) {
return;
}
setRecordings((old) =>
old.map((o) =>
o.name == event.message.recordingName ? { ...o, metadata: { labels: event.message.metadata.labels } } : o,
),
);
setRecordings((old) => {
return old.map((o) => {
if (o.name == event.message.recording.name) {
const updatedRecording = { ...o, metadata: { labels: event.message.recording.metadata.labels } };
return updatedRecording;
}
return o;
});
});
}),
);
}, [addSubscription, context, context.notificationChannel, setRecordings]);
Expand Down
Loading

0 comments on commit 5bd950b

Please sign in to comment.