diff --git a/go/cms_test.go b/go/cms_test.go index 367aaa4..0174872 100644 --- a/go/cms_test.go +++ b/go/cms_test.go @@ -49,7 +49,7 @@ func TestCMS(t *testing.T) { assert.NoError(t, err) assert.Equal(t, &Item{ ID: "a", - Fields: []Field{{ID: "f", Type: "text", Value: "t"}}, + Fields: []*Field{{ID: "f", Type: "text", Value: "t"}}, }, item) item, err = c.CreateItem(ctx, "a", nil, nil) @@ -57,7 +57,7 @@ func TestCMS(t *testing.T) { assert.NoError(t, err) assert.Equal(t, &Item{ ID: "a", - Fields: []Field{{ID: "f", Type: "text", Value: "t"}}, + Fields: []*Field{{ID: "f", Type: "text", Value: "t"}}, }, item) item, err = c.CreateItemByKey(ctx, "ppp", "mmm", nil, nil) @@ -65,7 +65,7 @@ func TestCMS(t *testing.T) { assert.NoError(t, err) assert.Equal(t, &Item{ ID: "a", - Fields: []Field{{ID: "f", Type: "text", Value: "t"}}, + Fields: []*Field{{ID: "f", Type: "text", Value: "t"}}, }, item) item, err = c.UpdateItem(ctx, "a", nil, nil) @@ -73,7 +73,7 @@ func TestCMS(t *testing.T) { assert.NoError(t, err) assert.Equal(t, &Item{ ID: "a", - Fields: []Field{{ID: "f", Type: "text", Value: "t"}}, + Fields: []*Field{{ID: "f", Type: "text", Value: "t"}}, }, item) err = c.DeleteItem(ctx, "a") @@ -431,6 +431,6 @@ func parseToken(r *http.Request) string { var testItems = lo.Map(lo.Range(500), func(i, _ int) Item { return Item{ ID: strconv.Itoa(i), - Fields: []Field{{ID: "f", Type: "text", Value: "t"}}, + Fields: []*Field{{ID: "f", Type: "text", Value: "t"}}, } }) diff --git a/go/cmswebhook/payload.go b/go/cmswebhook/payload.go index 83b2e94..da6d3ff 100644 --- a/go/cmswebhook/payload.go +++ b/go/cmswebhook/payload.go @@ -95,9 +95,11 @@ type Integration struct { type Machine struct{} type ItemData struct { - Item *cms.Item `json:"item,omitempty"` - Model *cms.Model `json:"model,omitempty"` - Schema *cms.Schema `json:"schema,omitempty"` + Item *cms.Item `json:"item,omitempty"` + Model *cms.Model `json:"model,omitempty"` + Schema *cms.Schema `json:"schema,omitempty"` + ReferencedItems []*cms.Item `json:"referencedItems,omitempty"` + Changes []cms.FieldChange `json:"changes,omitempty"` } type AssetData cms.Asset diff --git a/go/model.go b/go/model.go index a936a16..9988f69 100644 --- a/go/model.go +++ b/go/model.go @@ -1,7 +1,6 @@ package cms import ( - "encoding/json" "reflect" "strings" "time" @@ -68,7 +67,6 @@ func (a *Asset) ToPublic() *PublicAsset { URL: a.URL, ContentType: a.ContentType, ArchiveExtractionStatus: a.ArchiveExtractionStatus, - // Files: , } } @@ -93,10 +91,13 @@ func (r Items) HasNext() bool { } type Item struct { - ID string `json:"id"` - ModelID string `json:"modelId"` - Fields []Field `json:"fields"` - MetadataFields []Field `json:"metadataFields,omitempty"` + ID string `json:"id"` + ModelID string `json:"modelId"` + Fields []*Field `json:"fields"` + MetadataFields []*Field `json:"metadataFields,omitempty"` + ReferencedItems []*Item `json:"referencedItems,omitempty"` + OriginalItemID *string `json:"originalItemId,omitempty"` + MetadataItemID *string `json:"metadataItemId,omitempty"` } func (i *Item) Clone() *Item { @@ -119,21 +120,21 @@ func (i Item) MetadataField(id string) *Field { } func (i Item) FieldByGroup(id, group string) *Field { - f, ok := lo.Find(i.Fields, func(f Field) bool { + f, ok := lo.Find(i.Fields, func(f *Field) bool { return f.ID == id && f.Group == group }) if ok { - return &f + return f } return nil } func (i Item) MetadataFieldByGroup(id, group string) *Field { - f, ok := lo.Find(i.MetadataFields, func(f Field) bool { + f, ok := lo.Find(i.MetadataFields, func(f *Field) bool { return f.ID == id && f.Group == group }) if ok { - return &f + return f } return nil } @@ -143,11 +144,11 @@ func (i Item) FieldByKey(key string) *Field { } func (i Item) FieldByKeyAndGroup(key, group string) *Field { - f, ok := lo.Find(i.Fields, func(f Field) bool { + f, ok := lo.Find(i.Fields, func(f *Field) bool { return f.Key == key && f.Group == group }) if ok { - return &f + return f } return nil } @@ -157,32 +158,32 @@ func (i Item) MetadataFieldByKey(key string) *Field { } func (i Item) MetadataFieldByKeyAndGroup(key, group string) *Field { - f, ok := lo.Find(i.MetadataFields, func(f Field) bool { + f, ok := lo.Find(i.MetadataFields, func(f *Field) bool { return f.Key == key && f.Group == group }) if ok { - return &f + return f } return nil } func (i Item) Group(g string) Item { - fields := lo.Filter(i.Fields, func(f Field, _ int) bool { + fields := lo.Map(lo.Filter(i.Fields, func(f *Field, _ int) bool { return f.Group == g + }), func(f *Field, _ int) *Field { + g := f.Clone() + g.Group = "" + return g }) - metadataFields := lo.Filter(i.MetadataFields, func(f Field, _ int) bool { + metadataFields := lo.Map(lo.Filter(i.MetadataFields, func(f *Field, _ int) bool { return f.Group == g + }), func(f *Field, _ int) *Field { + g := f.Clone() + g.Group = "" + return g }) - for i := range fields { - fields[i].Group = "" - } - - for i := range metadataFields { - metadataFields[i].Group = "" - } - return Item{ ID: g, ModelID: i.ModelID, @@ -216,7 +217,7 @@ func (d Item) Unmarshal(i any) { continue } - isMetadata := strings.Contains(opts, ",metadata") + isMetadata := strings.HasSuffix(opts, ",metadata") vf := v.FieldByName(f.Name) if key == "id" { @@ -234,9 +235,9 @@ func (d Item) Unmarshal(i any) { } if itf != nil && itf.Type == "group" { - groupIDs := itf.ValueStrings() + groupIDs := itf.GetValue().Strings() if len(groupIDs) == 0 { - if groupID := itf.ValueString(); groupID != nil { + if groupID := itf.GetValue().String(); groupID != nil { groupIDs = []string{*groupID} } } @@ -290,15 +291,15 @@ func (d Item) Unmarshal(i any) { if itf != nil { if f.Type.Kind() == reflect.String { - if itfv := itf.ValueString(); itfv != nil { + if itfv := itf.GetValue().String(); itfv != nil { vf.SetString(*itfv) } } else if f.Type.Kind() == reflect.Slice && f.Type.Elem().Kind() == reflect.String { if te := f.Type.Elem(); te.Name() == "string" { - if itfv := itf.ValueStrings(); itfv != nil { + if itfv := itf.GetValue().Strings(); itfv != nil { vf.Set(reflect.ValueOf(itfv)) } - } else if itfv := itf.ValueStrings(); itfv != nil { + } else if itfv := itf.GetValue().Strings(); itfv != nil { s := reflect.MakeSlice(f.Type, 0, len(itfv)) for _, v := range itfv { rv := reflect.ValueOf(v).Convert(te) @@ -340,7 +341,7 @@ func Marshal(i any, item *Item) { continue } - isMetadata := strings.Contains(opts, ",metadata") + isMetadata := strings.HasSuffix(opts, ",metadata") ty, _, _ := strings.Cut(opts, ",") vf := v.FieldByName(f.Name) @@ -434,7 +435,7 @@ func Marshal(i any, item *Item) { } if value != nil { - f := Field{ + f := &Field{ Key: key, Type: ty, Value: value, @@ -459,70 +460,11 @@ type Field struct { Value any `json:"value"` } -func (f *Field) ValueString() *string { - return FieldValue[string](f) -} - -func (f *Field) ValueStrings() []string { - return FieldValues[string](f) -} - -func (f *Field) ValueBool() *bool { - return FieldValue[bool](f) -} - -func (f *Field) ValueBools() []bool { - return FieldValues[bool](f) -} - -func (f *Field) ValueInt() *int { - return FieldValue[int](f) -} - -func (f *Field) ValueInts() []int { - return FieldValues[int](f) -} - -func (f *Field) ValueFloat() *float64 { - return FieldValue[float64](f) -} - -func (f *Field) ValueFloats() []float64 { - return FieldValues[float64](f) -} - -func (f *Field) ValueJSON() (any, error) { +func (f *Field) GetValue() *Value { if f == nil { - return nil, nil - } - s := f.ValueString() - if s == nil { - return nil, nil - } - - var j any - err := json.Unmarshal([]byte(*s), &j) - return j, err -} - -func (f *Field) ValueJSONs() ([]any, error) { - if f == nil { - return nil, nil - } - values := f.ValueStrings() - if values == nil { - return nil, nil - } - - var res []any - for _, v := range values { - var r any - if err := json.Unmarshal([]byte(v), &r); err != nil { - return nil, err - } - res = append(res, r) + return nil } - return res, nil + return &Value{value: f.Value} } func (f *Field) Clone() *Field { @@ -537,35 +479,33 @@ func (f *Field) Clone() *Field { } } -func FieldValue[T any](f *Field) *T { - if f == nil { - return nil - } +type FieldChangeType string - if v, ok := f.Value.(T); ok { - return &v - } +const ( + FieldChangeTypeCreate FieldChangeType = "add" + FieldChangeTypeUpdate FieldChangeType = "update" + FieldChangeTypeDelete FieldChangeType = "delete" +) - return nil +type FieldChange struct { + ID string `json:"id,omitempty"` + Type FieldChangeType `json:"type"` + PreviousValue any `json:"previousValue"` + CurrentValue any `json:"currentValue"` } -func FieldValues[T any](f *Field) []T { +func (f *FieldChange) GetPreviousValue() *Value { if f == nil { return nil } + return &Value{value: f.PreviousValue} +} - if v, ok := f.Value.([]T); ok { - return v - } - - if v, ok := f.Value.([]any); ok { - return lo.FilterMap(v, func(e any, _ int) (T, bool) { - s, ok := e.(T) - return s, ok - }) +func (f *FieldChange) GetCurrentValue() *Value { + if f == nil { + return nil } - - return nil + return &Value{value: f.CurrentValue} } type Schema struct { diff --git a/go/model_test.go b/go/model_test.go index 6ef53e5..c2a4328 100644 --- a/go/model_test.go +++ b/go/model_test.go @@ -11,7 +11,7 @@ func TestItem_Group(t *testing.T) { item := Item{ ID: "xxx", ModelID: "xxx", - Fields: []Field{ + Fields: []*Field{ {Key: "aaa", Value: "bbb"}, {Key: "bbb", Value: []string{"ccc", "bbb"}}, {Key: "ccc", Value: []string{"a", "b"}}, @@ -19,7 +19,7 @@ func TestItem_Group(t *testing.T) { {Key: "ggg", Value: []string{"1", "2"}}, {Key: "aaa", Group: "1", Value: "123"}, }, - MetadataFields: []Field{ + MetadataFields: []*Field{ {Key: "eee", Value: "xxx"}, }, } @@ -28,10 +28,10 @@ func TestItem_Group(t *testing.T) { assert.Equal(t, Item{ ID: "1", ModelID: "xxx", - Fields: []Field{ + Fields: []*Field{ {Key: "aaa", Value: "123"}, }, - MetadataFields: []Field{}, + MetadataFields: []*Field{}, }, g) } @@ -57,7 +57,7 @@ func TestItem_Unmarshal(t *testing.T) { Item{ ID: "xxx", - Fields: []Field{ + Fields: []*Field{ {Key: "aaa", Value: "bbb"}, {Key: "bbb", Value: []string{"ccc", "bbb"}}, {Key: "ccc", Value: []string{"a", "b"}}, @@ -66,7 +66,7 @@ func TestItem_Unmarshal(t *testing.T) { {Key: "hhh", Type: "group", Value: []string{"1"}}, {Key: "aaa", Group: "1", Value: "123"}, }, - MetadataFields: []Field{ + MetadataFields: []*Field{ {Key: "eee", Value: "xxx"}, }, }.Unmarshal(&s) @@ -120,7 +120,7 @@ func TestMarshal(t *testing.T) { expected := &Item{ ID: "xxx", - Fields: []Field{ + Fields: []*Field{ {Key: "aaa", Type: "text", Value: "bbb"}, {Key: "bbb", Type: "select", Value: []string{"ccc", "bbb"}}, {Key: "ccc", Type: "", Value: "x"}, @@ -131,7 +131,7 @@ func TestMarshal(t *testing.T) { {Key: "aaa", Group: "2", Type: "text", Value: "hhh"}, {Key: "hhh", Type: "group", Value: []string{"2"}}, }, - MetadataFields: []Field{ + MetadataFields: []*Field{ {Key: "fff", Type: "text", Value: "fff"}, }, } @@ -155,13 +155,13 @@ func TestItem_Field(t *testing.T) { assert.Equal(t, &Field{ ID: "bbb", Value: "ccc", Type: "string", }, Item{ - Fields: []Field{ + Fields: []*Field{ {ID: "aaa", Value: "bbb", Type: "string"}, {ID: "bbb", Value: "ccc", Type: "string"}, }, }.Field("bbb")) assert.Nil(t, Item{ - Fields: []Field{ + Fields: []*Field{ {ID: "aaa", Key: "bbb", Type: "string"}, {ID: "bbb", Key: "ccc", Type: "string"}, }, @@ -172,13 +172,13 @@ func TestItem_MetadataField(t *testing.T) { assert.Equal(t, &Field{ ID: "bbb", Value: "ccc", Type: "string", }, Item{ - MetadataFields: []Field{ + MetadataFields: []*Field{ {ID: "aaa", Value: "bbb", Type: "string"}, {ID: "bbb", Value: "ccc", Type: "string"}, }, }.MetadataField("bbb")) assert.Nil(t, Item{ - MetadataFields: []Field{ + MetadataFields: []*Field{ {ID: "aaa", Key: "bbb", Type: "string"}, {ID: "bbb", Key: "ccc", Type: "string"}, }, @@ -189,13 +189,13 @@ func TestItem_FieldByKey(t *testing.T) { assert.Equal(t, &Field{ ID: "bbb", Key: "ccc", Type: "string", }, Item{ - Fields: []Field{ + Fields: []*Field{ {ID: "aaa", Key: "bbb", Type: "string"}, {ID: "bbb", Key: "ccc", Type: "string"}, }, }.FieldByKey("ccc")) assert.Nil(t, Item{ - Fields: []Field{ + Fields: []*Field{ {ID: "aaa", Key: "aaa", Type: "string"}, {ID: "bbb", Key: "ccc", Type: "string"}, }, @@ -206,13 +206,13 @@ func TestItem_MetadataFieldByKey(t *testing.T) { assert.Equal(t, &Field{ ID: "bbb", Key: "ccc", Type: "string", }, Item{ - MetadataFields: []Field{ + MetadataFields: []*Field{ {ID: "aaa", Key: "bbb", Type: "string"}, {ID: "bbb", Key: "ccc", Type: "string"}, }, }.MetadataFieldByKey("ccc")) assert.Nil(t, Item{ - MetadataFields: []Field{ + MetadataFields: []*Field{ {ID: "aaa", Key: "aaa", Type: "string"}, {ID: "bbb", Key: "ccc", Type: "string"}, }, @@ -222,58 +222,58 @@ func TestItem_MetadataFieldByKey(t *testing.T) { func TestField_ValueString(t *testing.T) { assert.Equal(t, lo.ToPtr("ccc"), (&Field{ Value: "ccc", - }).ValueString()) + }).GetValue().String()) assert.Nil(t, (&Field{ Value: 1, - }).ValueString()) + }).GetValue().String()) } func TestField_ValueStrings(t *testing.T) { assert.Equal(t, []string{"ccc", "ddd"}, (&Field{ Value: []string{"ccc", "ddd"}, - }).ValueStrings()) + }).GetValue().Strings()) assert.Equal(t, []string{"ccc", "ddd"}, (&Field{ Value: []any{"ccc", "ddd", 1}, - }).ValueStrings()) + }).GetValue().Strings()) assert.Nil(t, (&Field{ Value: "ccc", - }).ValueStrings()) + }).GetValue().Strings()) } func TestField_ValueBool(t *testing.T) { assert.Equal(t, lo.ToPtr(true), (&Field{ Value: true, - }).ValueBool()) + }).GetValue().Bool()) assert.Nil(t, (&Field{ Value: 1, - }).ValueBool()) + }).GetValue().Bool()) } func TestField_ValueInt(t *testing.T) { assert.Equal(t, lo.ToPtr(100), (&Field{ Value: 100, - }).ValueInt()) + }).GetValue().Int()) assert.Nil(t, (&Field{ Value: "100", - }).ValueInt()) + }).GetValue().Int()) } func TestField_ValueFloat(t *testing.T) { assert.Equal(t, lo.ToPtr(100.1), (&Field{ Value: 100.1, - }).ValueFloat()) + }).GetValue().Float()) assert.Nil(t, (&Field{ Value: 100, - }).ValueFloat()) + }).GetValue().Float()) assert.Nil(t, (&Field{ Value: "100.1", - }).ValueFloat()) + }).GetValue().Float()) } func TestField_ValueJSON(t *testing.T) { r, err := (&Field{ Value: `{"foo":"bar"}`, - }).ValueJSON() + }).GetValue().JSON() assert.NoError(t, err) assert.Equal(t, map[string]any{"foo": "bar"}, r) } @@ -281,7 +281,7 @@ func TestField_ValueJSON(t *testing.T) { func TestField_ValueJSONs(t *testing.T) { r, err := (&Field{ Value: []string{`{"foo":"bar"}`, `{"foo":"hoge"}`}, - }).ValueJSONs() + }).GetValue().JSONs() assert.NoError(t, err) assert.Equal(t, []any{map[string]any{"foo": "bar"}, map[string]any{"foo": "hoge"}}, r) } diff --git a/go/model_value.go b/go/model_value.go new file mode 100644 index 0000000..fd26dfa --- /dev/null +++ b/go/model_value.go @@ -0,0 +1,116 @@ +package cms + +import ( + "encoding/json" + + "github.com/samber/lo" +) + +type Value struct { + value any +} + +func NewValeu(value any) *Value { + return &Value{value: value} +} + +func (v *Value) Interface() any { + return v.value +} + +func (v *Value) String() *string { + return getValue[string](v) +} + +func (v *Value) Int() *int { + return getValue[int](v) +} + +func (v *Value) Float() *float64 { + return getValue[float64](v) +} + +func (v *Value) Bool() *bool { + return getValue[bool](v) +} + +func (v *Value) Strings() []string { + return getValues[string](v) +} + +func (v *Value) Ints() []int { + return getValues[int](v) +} + +func (v *Value) Floats() []float64 { + return getValues[float64](v) +} + +func (v *Value) Bools() []bool { + return getValues[bool](v) +} + +func (f *Value) JSON() (any, error) { + if f == nil { + return nil, nil + } + s := f.String() + if s == nil { + return nil, nil + } + + var j any + err := json.Unmarshal([]byte(*s), &j) + return j, err +} + +func (v *Value) JSONs() ([]any, error) { + if v == nil { + return nil, nil + } + values := v.Strings() + if values == nil { + return nil, nil + } + + var res []any + for _, v := range values { + var r any + if err := json.Unmarshal([]byte(v), &r); err != nil { + return nil, err + } + res = append(res, r) + } + return res, nil +} + +func getValue[T any](v *Value) *T { + if v == nil { + return nil + } + + if v, ok := v.value.(T); ok { + return &v + } + + return nil +} + +func getValues[T any](v *Value) []T { + if v == nil { + return nil + } + + if v, ok := v.value.([]T); ok { + return v + } + + if v, ok := v.value.([]any); ok { + return lo.FilterMap(v, func(e any, _ int) (T, bool) { + s, ok := e.(T) + return s, ok + }) + } + + return nil +}