Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions web/src/pages/admin/autoscan/sourceForm.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ describe("editedSourceBody", () => {
const source = pollSource();
const draft = {
...draftFromSource(source, descriptor),
configDirty: true,
label: " Basement ",
intervalStr: "1200",
mappings: [newMapping("/mnt/media/tv", " /data/tv "), newMapping("", "")],
Expand Down Expand Up @@ -67,6 +68,38 @@ describe("editedSourceBody", () => {
expect(editedSourceBody(source, draft, {}).connection_id).toBeNull();
});

it("sends the stored config back when the config was not edited", () => {
// Saving rebuilds the config from the form, which fills in declared
// defaults. Sending that for a label change would make the server
// restart the source's poll marker from now.
const source = pollSource();
const draft = { ...draftFromSource(source, descriptor), label: "Renamed", intervalStr: "1200" };
expect(
editedSourceBody(source, draft, { lookback: "24h", window: "7d" }).source_config,
).toEqual(source.source_config);
});

it("still migrates a row holding legacy CephFS keys on its first save", () => {
const source = pollSource({
plugin_id: "silo.autoscan.cephfs",
capability_id: "cephfs",
source_config: { tv_nested_paths: "/b" },
});
const draft = { ...draftFromSource(source, descriptor), label: "Renamed" };
expect(editedSourceBody(source, draft, { tv_flat_paths: "/b" }).source_config).toEqual({
tv_flat_paths: "/b",
});
});

it("counts a webhook provider change as a config edit", () => {
const source = webhookSource();
const draft = draftFromSource(source, descriptor);
draft.sourceConfig = { ...draft.sourceConfig, webhook_provider: "radarr" };
expect(editedSourceBody(source, draft, { webhook_provider: "radarr" }).source_config).toEqual({
webhook_provider: "radarr",
});
});

it("round-trips a webhook source unchanged", () => {
const source = webhookSource();
const draft = draftFromSource(source, descriptor);
Expand Down
27 changes: 26 additions & 1 deletion web/src/pages/admin/autoscan/sourceForm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,25 @@ export function draftConnectionId(draft: Pick<SourceDraft, "connectionId">): str
return draft.connectionId && draft.connectionId !== "__none__" ? draft.connectionId : null;
}

/**
* Whether the save has to send the config built from the form: the schema
* form marked the draft dirty, the webhook provider (which has its own
* control) differs from the stored one, or the row still holds legacy keys
* that its first save migrates forward.
*/
function configEdited(source: AutoscanSource, draft: SourceDraft): boolean {
if (draft.configDirty) return true;
const stored = source.source_config ?? {};
if (
ownsLegacyAliases(source) &&
Object.keys(LEGACY_CONFIG_KEY_ALIASES).some((key) => key in stored)
) {
return true;
}
const chosen = draft.sourceConfig[WEBHOOK_PROVIDER_KEY];
return chosen !== undefined && chosen !== stored[WEBHOOK_PROVIDER_KEY];
}

/**
* The complete PUT body for an edited source. Every field is sent: the edit
* dialog owns all of them, and the list's enabled switch owns `enabled`, which
Expand All @@ -243,7 +262,13 @@ export function editedSourceBody(
// rather than clearing it if one slips through.
poll_interval_seconds: interval.valid ? interval.seconds : source.poll_interval_seconds,
path_rewrites: usableMappings(draft.mappings),
source_config: normalizeSourceConfig(serializedConfig),
// A config the operator did not touch goes back exactly as stored.
// Rebuilding it fills in defaults and drops keys the descriptor no longer
// declares, and the server restarts a poll source from now whenever its
// source_config changes.
source_config: configEdited(source, draft)
Comment thread
Quick104 marked this conversation as resolved.
? normalizeSourceConfig(serializedConfig)
: { ...(source.source_config ?? {}) },
label: draft.label.trim(),
};
}
Loading