From b016f6d8122e1511df064d2d01eac48043efdaf2 Mon Sep 17 00:00:00 2001 From: Chili Palmer Date: Fri, 17 Jul 2026 08:57:20 +0200 Subject: [PATCH] fix: update_project_metadata keeps fields absent from attrs instead of resetting them --- API.md | 2 +- lib/bds/metadata.ex | 22 ++++++++++++++++++++++ lib/bds/scripting/api_docs.ex | 3 ++- lib/bds/ui/settings_form.ex | 24 +----------------------- test/bds/metadata_test.exs | 21 +++++++++++++++++++++ 5 files changed, 47 insertions(+), 25 deletions(-) diff --git a/API.md b/API.md index 30493a2..b38257c 100644 --- a/API.md +++ b/API.md @@ -3443,7 +3443,7 @@ local result = bds.meta.sync_on_startup() ### meta.update_project_metadata -Update metadata for the current project. +Update metadata for the current project. Keys omitted from updates keep their current values. **Parameters** diff --git a/lib/bds/metadata.ex b/lib/bds/metadata.ex index 1735f0e..63b418f 100644 --- a/lib/bds/metadata.ex +++ b/lib/bds/metadata.ex @@ -318,6 +318,8 @@ defmodule BDS.Metadata do } end + # Keys absent from attrs are left out entirely, so the merge in + # update_project_metadata/2 keeps their current values (partial update). defp normalize_project_metadata_attrs(attrs, project) do %{ name: attr(attrs, :name) || project.name, @@ -333,6 +335,26 @@ defmodule BDS.Metadata do semantic_similarity_enabled: attr(attrs, :semantic_similarity_enabled) || false, blog_languages: normalize_language_list(attr(attrs, :blog_languages) || []) } + |> Map.take(present_attr_keys(attrs)) + end + + defp present_attr_keys(attrs) do + Enum.filter( + [ + :name, + :description, + :public_url, + :main_language, + :default_author, + :max_posts_per_page, + :image_import_concurrency, + :blogmark_category, + :pico_theme, + :semantic_similarity_enabled, + :blog_languages + ], + fn key -> Map.has_key?(attrs, key) or Map.has_key?(attrs, Atom.to_string(key)) end + ) end defp normalize_category_settings(settings) do diff --git a/lib/bds/scripting/api_docs.ex b/lib/bds/scripting/api_docs.ex index 30ad802..2f19e10 100644 --- a/lib/bds/scripting/api_docs.ex +++ b/lib/bds/scripting/api_docs.ex @@ -762,7 +762,8 @@ defmodule BDS.Scripting.ApiDocs do %{ module: "meta", name: "update_project_metadata", - description: "Update metadata for the current project.", + description: + "Update metadata for the current project. Keys omitted from updates keep their current values.", params: [%{name: "updates", type: "table", required: true}], returns: "ProjectMetadata | nil" }, diff --git a/lib/bds/ui/settings_form.ex b/lib/bds/ui/settings_form.ex index 44ae8d1..b6c8177 100644 --- a/lib/bds/ui/settings_form.ex +++ b/lib/bds/ui/settings_form.ex @@ -388,30 +388,8 @@ defmodule BDS.UI.SettingsForm do ) end - # Project, technology and style all live in the same metadata record; - # `Metadata.update_project_metadata/2` treats missing keys as nil, so the - # current values are always passed along and only the edits override them. defp save_project_metadata(project_id, overrides) do - {:ok, metadata} = Metadata.get_project_metadata(project_id) - - attrs = - metadata - |> Map.take([ - :name, - :description, - :public_url, - :main_language, - :default_author, - :max_posts_per_page, - :image_import_concurrency, - :blogmark_category, - :pico_theme, - :semantic_similarity_enabled, - :blog_languages - ]) - |> Map.merge(overrides) - - case Metadata.update_project_metadata(project_id, attrs) do + case Metadata.update_project_metadata(project_id, overrides) do {:ok, _metadata} -> :ok {:error, reason} -> {:error, reason} end diff --git a/test/bds/metadata_test.exs b/test/bds/metadata_test.exs index 1444b1c..926e91b 100644 --- a/test/bds/metadata_test.exs +++ b/test/bds/metadata_test.exs @@ -54,6 +54,27 @@ defmodule BDS.MetadataTest do assert loaded.blog_languages == ["de", "fr"] end + test "update_project_metadata keeps fields that are absent from the attrs", %{ + project: project + } do + assert {:ok, _metadata} = + BDS.Metadata.update_project_metadata(project.id, %{ + description: "Keep me", + public_url: "https://example.com", + default_author: "Writer", + blog_languages: ["de"] + }) + + assert {:ok, metadata} = + BDS.Metadata.update_project_metadata(project.id, %{pico_theme: "blue"}) + + assert metadata.pico_theme == "blue" + assert metadata.description == "Keep me" + assert metadata.public_url == "https://example.com" + assert metadata.default_author == "Writer" + assert metadata.blog_languages == ["de"] + end + test "update_project_metadata keeps committed database changes when filesystem flush fails", %{ project: project, temp_dir: temp_dir