fix(agent-config): stabilize patch and target resolution
This commit is contained in:
parent
9b7f20c486
commit
b69de71029
2 changed files with 271 additions and 37 deletions
|
|
@ -746,16 +746,12 @@ function connectDatasource(
|
|||
);
|
||||
}
|
||||
|
||||
const target = resolveTarget(document, operation.target);
|
||||
if (!target) {
|
||||
return failure(
|
||||
"item_not_found",
|
||||
`Target not found: ${operation.target.id}`,
|
||||
operationIndex,
|
||||
targetPath(operation.target),
|
||||
);
|
||||
const targetResolution = resolveTarget(document, operation.target, operationIndex);
|
||||
if (!targetResolution.ok) {
|
||||
return { ok: false, errors: targetResolution.errors };
|
||||
}
|
||||
|
||||
const target = targetResolution.target;
|
||||
(target as DatasourceAgentTarget).datasource = operation.datasource;
|
||||
return { ok: true, document };
|
||||
}
|
||||
|
|
@ -798,16 +794,12 @@ function setStatusRule(
|
|||
);
|
||||
}
|
||||
|
||||
const target = resolveTarget(document, operation.target);
|
||||
if (!target) {
|
||||
return failure(
|
||||
"item_not_found",
|
||||
`Target not found: ${operation.target.id}`,
|
||||
operationIndex,
|
||||
targetPath(operation.target),
|
||||
);
|
||||
const targetResolution = resolveTarget(document, operation.target, operationIndex);
|
||||
if (!targetResolution.ok) {
|
||||
return { ok: false, errors: targetResolution.errors };
|
||||
}
|
||||
|
||||
const target = targetResolution.target;
|
||||
if (operation.thresholds && operation.target.kind === "telemetry") {
|
||||
(target as TelemetryCard).thresholds = operation.thresholds;
|
||||
}
|
||||
|
|
@ -881,8 +873,8 @@ function removeItem(
|
|||
}
|
||||
|
||||
if (operation.target.kind === "service") {
|
||||
const location = findService(document, operation.target.id, operation.target.groupId);
|
||||
if (!location) {
|
||||
const matches = findServices(document, operation.target.id, operation.target.groupId);
|
||||
if (!matches.length) {
|
||||
return failure(
|
||||
"item_not_found",
|
||||
`Service not found: ${operation.target.id}`,
|
||||
|
|
@ -890,7 +882,17 @@ function removeItem(
|
|||
"/serviceGroups",
|
||||
);
|
||||
}
|
||||
location.group.services = location.group.services.filter((service) => service.id !== operation.target.id);
|
||||
if (!operation.target.groupId && matches.length > 1) {
|
||||
return failure(
|
||||
"ambiguous_target",
|
||||
`Service exists in multiple groups: ${operation.target.id}`,
|
||||
operationIndex,
|
||||
"/serviceGroups",
|
||||
);
|
||||
}
|
||||
|
||||
const match = matches[0];
|
||||
match.group.services = match.group.services.filter((service) => service.id !== operation.target.id);
|
||||
return { ok: true, document };
|
||||
}
|
||||
|
||||
|
|
@ -938,25 +940,83 @@ function removeItem(
|
|||
function resolveTarget(
|
||||
document: DashboardDocument,
|
||||
target: ConnectDatasourceOperation["target"],
|
||||
): ResolvedAgentTarget | null {
|
||||
operationIndex: number,
|
||||
): ResolveTargetResult {
|
||||
if (target.kind === "telemetry") {
|
||||
return document.telemetry.find((card) => card.id === target.id) || null;
|
||||
const card = document.telemetry.find((item) => item.id === target.id);
|
||||
if (!card) {
|
||||
return resolutionFailure(
|
||||
"item_not_found",
|
||||
`Telemetry card not found: ${target.id}`,
|
||||
operationIndex,
|
||||
"/telemetry",
|
||||
);
|
||||
}
|
||||
return { ok: true, target: card };
|
||||
}
|
||||
if (target.kind === "service") {
|
||||
return findService(document, target.id, target.groupId)?.service || null;
|
||||
const matches = findServices(document, target.id, target.groupId);
|
||||
if (!matches.length) {
|
||||
return resolutionFailure(
|
||||
"item_not_found",
|
||||
`Service not found: ${target.id}`,
|
||||
operationIndex,
|
||||
"/serviceGroups",
|
||||
);
|
||||
}
|
||||
if (!target.groupId && matches.length > 1) {
|
||||
return resolutionFailure(
|
||||
"ambiguous_target",
|
||||
`Service exists in multiple groups: ${target.id}`,
|
||||
operationIndex,
|
||||
"/serviceGroups",
|
||||
);
|
||||
}
|
||||
return { ok: true, target: matches[0].service };
|
||||
}
|
||||
if (target.kind === "statusItem") {
|
||||
const strips = target.stripId
|
||||
? document.statusStrips.filter((strip) => strip.id === target.stripId)
|
||||
: document.statusStrips;
|
||||
return strips.flatMap((strip) => strip.items).find((item) => item.id === target.id) || null;
|
||||
const matches = findStatusItems(document, target.id, target.stripId);
|
||||
if (!matches.length) {
|
||||
return resolutionFailure(
|
||||
"item_not_found",
|
||||
`Status item not found: ${target.id}`,
|
||||
operationIndex,
|
||||
"/statusStrips",
|
||||
);
|
||||
}
|
||||
if (!target.stripId && matches.length > 1) {
|
||||
return resolutionFailure(
|
||||
"ambiguous_target",
|
||||
`Status item exists in multiple strips: ${target.id}`,
|
||||
operationIndex,
|
||||
"/statusStrips",
|
||||
);
|
||||
}
|
||||
return { ok: true, target: matches[0].item };
|
||||
}
|
||||
if (target.kind === "module") {
|
||||
return (document.modules || []).find((module) => module.id === target.id) || null;
|
||||
const module = (document.modules || []).find((item) => item.id === target.id);
|
||||
if (!module) {
|
||||
return resolutionFailure(
|
||||
"item_not_found",
|
||||
`Module not found: ${target.id}`,
|
||||
operationIndex,
|
||||
"/modules",
|
||||
);
|
||||
}
|
||||
return { ok: true, target: module };
|
||||
}
|
||||
return null;
|
||||
return resolutionFailure(
|
||||
"item_not_found",
|
||||
`Target not found: ${target.id}`,
|
||||
operationIndex,
|
||||
targetPath(target),
|
||||
);
|
||||
}
|
||||
|
||||
type ResolveTargetResult =
|
||||
| { ok: true; target: ResolvedAgentTarget }
|
||||
| { ok: false; errors: AgentConfigError[] };
|
||||
type ResolvedAgentTarget = TelemetryCard | ServiceEntry | StatusItem | DashboardModule;
|
||||
type DatasourceAgentTarget = TelemetryCard | ServiceEntry | DashboardModule;
|
||||
type DetailAgentTarget = TelemetryCard | ServiceEntry | DashboardModule;
|
||||
|
|
@ -995,15 +1055,15 @@ function supportsStringValue(
|
|||
return kind === "statusItem" || kind === "module";
|
||||
}
|
||||
|
||||
function findService(document: DashboardDocument, id: string, groupId?: string) {
|
||||
function findServices(document: DashboardDocument, id: string, groupId?: string) {
|
||||
const groups = groupId
|
||||
? document.serviceGroups.filter((group) => group.id === groupId)
|
||||
: document.serviceGroups;
|
||||
for (const group of groups) {
|
||||
const service = group.services.find((item) => item.id === id);
|
||||
if (service) return { group, service };
|
||||
}
|
||||
return null;
|
||||
return groups.flatMap((group) =>
|
||||
group.services.flatMap((service) =>
|
||||
service.id === id ? [{ group, service }] : [],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
function findStatusItems(document: DashboardDocument, id: string, stripId?: string) {
|
||||
|
|
@ -1011,10 +1071,29 @@ function findStatusItems(document: DashboardDocument, id: string, stripId?: stri
|
|||
? document.statusStrips.filter((strip) => strip.id === stripId)
|
||||
: document.statusStrips;
|
||||
return strips.flatMap((strip) =>
|
||||
strip.items.some((item) => item.id === id) ? [{ strip }] : [],
|
||||
strip.items.flatMap((item) => (item.id === id ? [{ strip, item }] : [])),
|
||||
);
|
||||
}
|
||||
|
||||
function resolutionFailure(
|
||||
code: string,
|
||||
message: string,
|
||||
operationIndex: number,
|
||||
path: string,
|
||||
): ResolveTargetResult {
|
||||
return {
|
||||
ok: false,
|
||||
errors: [
|
||||
{
|
||||
code,
|
||||
message,
|
||||
operationIndex,
|
||||
path,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
function targetPath(target: ConnectDatasourceOperation["target"]): string {
|
||||
if (target.kind === "telemetry") return "/telemetry";
|
||||
if (target.kind === "service") return "/serviceGroups";
|
||||
|
|
@ -1140,12 +1219,18 @@ function createJsonPatch(
|
|||
return [{ op: "replace", path: path || "/", value: after }];
|
||||
}
|
||||
const operations: JsonPatchOperation[] = [];
|
||||
const max = Math.max(before.length, after.length);
|
||||
for (let index = 0; index < max; index += 1) {
|
||||
const sharedLength = Math.min(before.length, after.length);
|
||||
for (let index = 0; index < sharedLength; index += 1) {
|
||||
operations.push(
|
||||
...createJsonPatch(before[index], after[index], `${path}/${index}`),
|
||||
);
|
||||
}
|
||||
for (let index = before.length - 1; index >= after.length; index -= 1) {
|
||||
operations.push({ op: "remove", path: `${path}/${index}` });
|
||||
}
|
||||
for (let index = before.length; index < after.length; index += 1) {
|
||||
operations.push({ op: "add", path: `${path}/${index}`, value: after[index] });
|
||||
}
|
||||
return operations;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue