From d19efe7103154d98521b5ff8a0a751681e90652b Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 3 Oct 2023 10:49:45 +0200 Subject: [PATCH] Allow importing unknown settings, add export key selection, improve json keys --- sync/setting_single.go | 8 ++++---- sync/settings.go | 34 ++++++++++++++++++++++++++++++---- sync/util.go | 17 +++++++++-------- 3 files changed, 43 insertions(+), 16 deletions(-) diff --git a/sync/setting_single.go b/sync/setting_single.go index 0d2f0d64..af116302 100644 --- a/sync/setting_single.go +++ b/sync/setting_single.go @@ -85,7 +85,7 @@ func handleExportSingleSetting(ar *api.Request) (data []byte, err error) { if len(q) > 0 { request = &ExportRequest{ From: q.Get("from"), - Key: q.Get("key"), + Keys: q["key"], // Get []string by direct map access. } } else { request = &ExportRequest{} @@ -95,12 +95,12 @@ func handleExportSingleSetting(ar *api.Request) (data []byte, err error) { } // Check parameters. - if request.From == "" || request.Key == "" { - return nil, errors.New("missing parameters") + if request.From == "" || len(request.Keys) != 1 { + return nil, errors.New("missing or malformed parameters") } // Export. - export, err := ExportSingleSetting(request.Key, request.From) + export, err := ExportSingleSetting(request.Keys[0], request.From) if err != nil { return nil, err } diff --git a/sync/settings.go b/sync/settings.go index e6f789cd..921350bd 100644 --- a/sync/settings.go +++ b/sync/settings.go @@ -28,6 +28,8 @@ type SettingsImportRequest struct { // any settings would be replaced or deleted. Reset bool `json:"reset"` + AllowUnknown bool `json:"allowUnknown"` + Export *SettingsExport `json:"export"` } @@ -42,6 +44,10 @@ func registerSettingsAPI() error { Method: http.MethodGet, Field: "from", Description: "Specify where to export from.", + }, { + Method: http.MethodGet, + Field: "key", + Description: "Optionally select a single setting to export. Repeat to export selection.", }}, BelongsTo: module, DataFunc: handleExportSettings, @@ -67,6 +73,10 @@ func registerSettingsAPI() error { Method: http.MethodPost, Field: "reset", Description: "Replace all existing settings.", + }, { + Method: http.MethodPost, + Field: "allowUnknown", + Description: "Allow importing of unknown values.", }}, BelongsTo: module, StructFunc: handleImportSettings, @@ -85,6 +95,7 @@ func handleExportSettings(ar *api.Request) (data []byte, err error) { if len(q) > 0 { request = &ExportRequest{ From: q.Get("from"), + Keys: q["key"], // Get []string by direct map access. } } else { request = &ExportRequest{} @@ -99,7 +110,7 @@ func handleExportSettings(ar *api.Request) (data []byte, err error) { } // Export. - export, err := ExportSettings(request.From) + export, err := ExportSettings(request.From, request.Keys) if err != nil { return nil, err } @@ -120,7 +131,8 @@ func handleImportSettings(ar *api.Request) (any, error) { RawExport: string(ar.InputData), RawMime: ar.Header.Get("Content-Type"), }, - Reset: q.Has("reset"), + Reset: q.Has("reset"), + AllowUnknown: q.Has("allowUnknown"), } } else { request = &SettingsImportRequest{} @@ -151,7 +163,7 @@ func handleImportSettings(ar *api.Request) (any, error) { } // ExportSettings exports the global settings. -func ExportSettings(from string) (*SettingsExport, error) { +func ExportSettings(from string, keys []string) (*SettingsExport, error) { var settings map[string]any if from == ExportTargetGlobal { // Collect all changed global settings. @@ -175,6 +187,17 @@ func ExportSettings(from string) (*SettingsExport, error) { settings = config.Flatten(p.Config) } + // Only extract some setting keys, if wanted. + if len(keys) > 0 { + selection := make(map[string]any, len(keys)) + for _, key := range keys { + if v, ok := settings[key]; ok { + selection[key] = v + } + } + settings = selection + } + // Check if there any changed settings. if len(settings) == 0 { return nil, ErrUnchanged @@ -237,7 +260,10 @@ func ImportSettings(r *SettingsImportRequest) (*ImportResult, error) { return nil, err } if checked < len(settings) { - return nil, fmt.Errorf("%w: the export contains unknown settings", ErrInvalidImportRequest) + result.ContainsUnknown = true + if !r.AllowUnknown { + return nil, fmt.Errorf("%w: the export contains unknown settings", ErrInvalidImportRequest) + } } // Import global settings. diff --git a/sync/util.go b/sync/util.go index 5d9a6154..5a0e5f3a 100644 --- a/sync/util.go +++ b/sync/util.go @@ -35,8 +35,8 @@ var ( // ExportRequest is a request for an export. type ExportRequest struct { - From string `json:"from"` - Key string `json:"key"` + From string `json:"from"` + Keys []string `json:"keys"` } // ImportRequest is a request to import an export. @@ -44,16 +44,17 @@ type ImportRequest struct { // Where the export should be import to. Target string `json:"target"` // Only validate, but do not actually change anything. - ValidateOnly bool `json:"validate_only"` + ValidateOnly bool `json:"validateOnly"` - RawExport string `json:"raw_export"` - RawMime string `json:"raw_mime"` + RawExport string `json:"rawExport"` + RawMime string `json:"rawMime"` } // ImportResult is returned by successful import operations. type ImportResult struct { - RestartRequired bool `json:"restart_required"` - ReplacesExisting bool `json:"replaces_existing"` + RestartRequired bool `json:"restartRequired"` + ReplacesExisting bool `json:"replacesExisting"` + ContainsUnknown bool `json:"containsUnknown"` } // Errors. @@ -113,7 +114,7 @@ func serializeExport(export any, ar *api.Request) ([]byte, error) { case dsd.JSON: data, err = filesig.AddJSONChecksum(data) case dsd.YAML: - data, err = filesig.AddYAMLChecksum(data, filesig.TextPlacementTop) + data, err = filesig.AddYAMLChecksum(data, filesig.TextPlacementBottom) default: return nil, dsd.ErrIncompatibleFormat }