fix: handle mixed-format bucket configurations in migration (#2033)
This change modifies the migration `20251001113831` to flexibly parse bucket configuration filters. This fixes this migration issue: ``` json: cannot unmarshal object into Go struct field bucketConfigurationCatchup.filter of type string ``` This occurred when a single `bucket_configuration` JSON array contained mixed formats - some buckets with old string filters and some with already-converted object filters.
This commit is contained in:
parent
02cd648a96
commit
6f0b685e38
|
|
@ -17,21 +17,31 @@
|
||||||
package migration
|
package migration
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
"src.techknowlogick.com/xormigrate"
|
"src.techknowlogick.com/xormigrate"
|
||||||
"xorm.io/xorm"
|
"xorm.io/xorm"
|
||||||
"xorm.io/xorm/schemas"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Old bucket configuration format (filter as string)
|
// Full bucket filter struct with all fields to avoid dropping data during migration
|
||||||
|
type bucketFilterCatchup struct {
|
||||||
|
Search string `json:"s,omitempty"`
|
||||||
|
SortBy []string `json:"sort_by,omitempty"`
|
||||||
|
OrderBy []string `json:"order_by,omitempty"`
|
||||||
|
Filter string `json:"filter,omitempty"`
|
||||||
|
FilterIncludeNulls bool `json:"filter_include_nulls,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flexible bucket configuration format that can handle both string and object filters
|
||||||
type bucketConfigurationCatchup struct {
|
type bucketConfigurationCatchup struct {
|
||||||
Title string `json:"title"`
|
Title string `json:"title"`
|
||||||
Filter string `json:"filter"`
|
Filter json.RawMessage `json:"filter"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// New bucket configuration format (filter as object)
|
// New bucket configuration format (filter as object)
|
||||||
type bucketConfigurationCatchupNew struct {
|
type bucketConfigurationCatchupNew struct {
|
||||||
Title string `json:"title"`
|
Title string `json:"title"`
|
||||||
Filter *taskCollection20241118123644 `json:"filter"`
|
Filter *bucketFilterCatchup `json:"filter"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Old format project view
|
// Old format project view
|
||||||
|
|
@ -61,19 +71,9 @@ func init() {
|
||||||
Migrate: func(tx *xorm.Engine) (err error) {
|
Migrate: func(tx *xorm.Engine) (err error) {
|
||||||
oldViews := []*projectViewBucketsCatchup{}
|
oldViews := []*projectViewBucketsCatchup{}
|
||||||
|
|
||||||
// Find views with bucket_configuration in old string format
|
// Find all filter-mode views - we'll check individual buckets in code
|
||||||
// Only check views with bucket_configuration_mode = 2 (filter mode)
|
err = tx.Where("bucket_configuration_mode = 2").
|
||||||
// Pattern: bucket_configuration contains "filter":"<string>" but not "filter":{"filter":
|
Find(&oldViews)
|
||||||
if tx.Dialect().URI().DBType == schemas.POSTGRES {
|
|
||||||
err = tx.Where("bucket_configuration_mode = 2 AND bucket_configuration::text like '%\"filter\":\"%'").
|
|
||||||
And("bucket_configuration::text not like '%\"filter\":{\"filter\":%'").
|
|
||||||
Find(&oldViews)
|
|
||||||
} else {
|
|
||||||
err = tx.Where("bucket_configuration_mode = 2 AND bucket_configuration like '%\"filter\":\"%'").
|
|
||||||
And("bucket_configuration not like '%\"filter\":{\"filter\":%'").
|
|
||||||
Find(&oldViews)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -84,15 +84,43 @@ func init() {
|
||||||
ID: view.ID,
|
ID: view.ID,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
needsUpdate := false
|
||||||
|
|
||||||
// Convert each bucket configuration from old to new format
|
// Convert each bucket configuration from old to new format
|
||||||
for _, configuration := range view.BucketConfiguration {
|
for _, configuration := range view.BucketConfiguration {
|
||||||
newView.BucketConfiguration = append(newView.BucketConfiguration,
|
newConfig := &bucketConfigurationCatchupNew{
|
||||||
&bucketConfigurationCatchupNew{
|
Title: configuration.Title,
|
||||||
Title: configuration.Title,
|
}
|
||||||
Filter: &taskCollection20241118123644{
|
|
||||||
Filter: configuration.Filter, // Wrap string in object
|
// Check if filter is a string (old format) or object (already converted)
|
||||||
},
|
if len(configuration.Filter) > 0 {
|
||||||
})
|
switch configuration.Filter[0] {
|
||||||
|
case '"':
|
||||||
|
// It's a JSON string - extract and wrap in object
|
||||||
|
var filterString string
|
||||||
|
if err := json.Unmarshal(configuration.Filter, &filterString); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
newConfig.Filter = &bucketFilterCatchup{
|
||||||
|
Filter: filterString,
|
||||||
|
}
|
||||||
|
needsUpdate = true
|
||||||
|
case '{':
|
||||||
|
// It's already an object - preserve all fields
|
||||||
|
var existingFilter bucketFilterCatchup
|
||||||
|
if err := json.Unmarshal(configuration.Filter, &existingFilter); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
newConfig.Filter = &existingFilter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
newView.BucketConfiguration = append(newView.BucketConfiguration, newConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only update if we actually found string filters to convert
|
||||||
|
if !needsUpdate {
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update only the bucket_configuration column
|
// Update only the bucket_configuration column
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue