Allow importing unknown settings, add export key selection, improve json keys

This commit is contained in:
Daniel 2023-10-03 10:49:45 +02:00
parent bed5c72a6b
commit d19efe7103
3 changed files with 43 additions and 16 deletions

View file

@ -85,7 +85,7 @@ func handleExportSingleSetting(ar *api.Request) (data []byte, err error) {
if len(q) > 0 { if len(q) > 0 {
request = &ExportRequest{ request = &ExportRequest{
From: q.Get("from"), From: q.Get("from"),
Key: q.Get("key"), Keys: q["key"], // Get []string by direct map access.
} }
} else { } else {
request = &ExportRequest{} request = &ExportRequest{}
@ -95,12 +95,12 @@ func handleExportSingleSetting(ar *api.Request) (data []byte, err error) {
} }
// Check parameters. // Check parameters.
if request.From == "" || request.Key == "" { if request.From == "" || len(request.Keys) != 1 {
return nil, errors.New("missing parameters") return nil, errors.New("missing or malformed parameters")
} }
// Export. // Export.
export, err := ExportSingleSetting(request.Key, request.From) export, err := ExportSingleSetting(request.Keys[0], request.From)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -28,6 +28,8 @@ type SettingsImportRequest struct {
// any settings would be replaced or deleted. // any settings would be replaced or deleted.
Reset bool `json:"reset"` Reset bool `json:"reset"`
AllowUnknown bool `json:"allowUnknown"`
Export *SettingsExport `json:"export"` Export *SettingsExport `json:"export"`
} }
@ -42,6 +44,10 @@ func registerSettingsAPI() error {
Method: http.MethodGet, Method: http.MethodGet,
Field: "from", Field: "from",
Description: "Specify where to export 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, BelongsTo: module,
DataFunc: handleExportSettings, DataFunc: handleExportSettings,
@ -67,6 +73,10 @@ func registerSettingsAPI() error {
Method: http.MethodPost, Method: http.MethodPost,
Field: "reset", Field: "reset",
Description: "Replace all existing settings.", Description: "Replace all existing settings.",
}, {
Method: http.MethodPost,
Field: "allowUnknown",
Description: "Allow importing of unknown values.",
}}, }},
BelongsTo: module, BelongsTo: module,
StructFunc: handleImportSettings, StructFunc: handleImportSettings,
@ -85,6 +95,7 @@ func handleExportSettings(ar *api.Request) (data []byte, err error) {
if len(q) > 0 { if len(q) > 0 {
request = &ExportRequest{ request = &ExportRequest{
From: q.Get("from"), From: q.Get("from"),
Keys: q["key"], // Get []string by direct map access.
} }
} else { } else {
request = &ExportRequest{} request = &ExportRequest{}
@ -99,7 +110,7 @@ func handleExportSettings(ar *api.Request) (data []byte, err error) {
} }
// Export. // Export.
export, err := ExportSettings(request.From) export, err := ExportSettings(request.From, request.Keys)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -120,7 +131,8 @@ func handleImportSettings(ar *api.Request) (any, error) {
RawExport: string(ar.InputData), RawExport: string(ar.InputData),
RawMime: ar.Header.Get("Content-Type"), RawMime: ar.Header.Get("Content-Type"),
}, },
Reset: q.Has("reset"), Reset: q.Has("reset"),
AllowUnknown: q.Has("allowUnknown"),
} }
} else { } else {
request = &SettingsImportRequest{} request = &SettingsImportRequest{}
@ -151,7 +163,7 @@ func handleImportSettings(ar *api.Request) (any, error) {
} }
// ExportSettings exports the global settings. // ExportSettings exports the global settings.
func ExportSettings(from string) (*SettingsExport, error) { func ExportSettings(from string, keys []string) (*SettingsExport, error) {
var settings map[string]any var settings map[string]any
if from == ExportTargetGlobal { if from == ExportTargetGlobal {
// Collect all changed global settings. // Collect all changed global settings.
@ -175,6 +187,17 @@ func ExportSettings(from string) (*SettingsExport, error) {
settings = config.Flatten(p.Config) 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. // Check if there any changed settings.
if len(settings) == 0 { if len(settings) == 0 {
return nil, ErrUnchanged return nil, ErrUnchanged
@ -237,7 +260,10 @@ func ImportSettings(r *SettingsImportRequest) (*ImportResult, error) {
return nil, err return nil, err
} }
if checked < len(settings) { 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. // Import global settings.

View file

@ -35,8 +35,8 @@ var (
// ExportRequest is a request for an export. // ExportRequest is a request for an export.
type ExportRequest struct { type ExportRequest struct {
From string `json:"from"` From string `json:"from"`
Key string `json:"key"` Keys []string `json:"keys"`
} }
// ImportRequest is a request to import an export. // ImportRequest is a request to import an export.
@ -44,16 +44,17 @@ type ImportRequest struct {
// Where the export should be import to. // Where the export should be import to.
Target string `json:"target"` Target string `json:"target"`
// Only validate, but do not actually change anything. // Only validate, but do not actually change anything.
ValidateOnly bool `json:"validate_only"` ValidateOnly bool `json:"validateOnly"`
RawExport string `json:"raw_export"` RawExport string `json:"rawExport"`
RawMime string `json:"raw_mime"` RawMime string `json:"rawMime"`
} }
// ImportResult is returned by successful import operations. // ImportResult is returned by successful import operations.
type ImportResult struct { type ImportResult struct {
RestartRequired bool `json:"restart_required"` RestartRequired bool `json:"restartRequired"`
ReplacesExisting bool `json:"replaces_existing"` ReplacesExisting bool `json:"replacesExisting"`
ContainsUnknown bool `json:"containsUnknown"`
} }
// Errors. // Errors.
@ -113,7 +114,7 @@ func serializeExport(export any, ar *api.Request) ([]byte, error) {
case dsd.JSON: case dsd.JSON:
data, err = filesig.AddJSONChecksum(data) data, err = filesig.AddJSONChecksum(data)
case dsd.YAML: case dsd.YAML:
data, err = filesig.AddYAMLChecksum(data, filesig.TextPlacementTop) data, err = filesig.AddYAMLChecksum(data, filesig.TextPlacementBottom)
default: default:
return nil, dsd.ErrIncompatibleFormat return nil, dsd.ErrIncompatibleFormat
} }