diff --git a/core/api/model/property.go b/core/api/model/property.go index 3900b174b..ac05a799f 100644 --- a/core/api/model/property.go +++ b/core/api/model/property.go @@ -44,7 +44,7 @@ type PropertyResponse struct { type CreatePropertyRequest struct { Name string `json:"name" binding:"required" example:"Last modified date"` // The name of the property Format PropertyFormat `json:"format" binding:"required" enums:"text,number,select,multi_select,date,files,checkbox,url,email,phone,objects"` // The format of the property - Key *string `json:"key,omitempty" example:"some_user_defined_property_key"` // The key of the property + Key string `json:"key" example:"some_user_defined_property_key"` // The key of the property } type UpdatePropertyRequest struct { diff --git a/core/api/service/property.go b/core/api/service/property.go index b9233e2a0..c6814963c 100644 --- a/core/api/service/property.go +++ b/core/api/service/property.go @@ -9,6 +9,8 @@ import ( "time" "github.com/gogo/protobuf/types" + "github.com/iancoleman/strcase" + "github.com/mozillazg/go-unidecode" apimodel "github.com/anyproto/anytype-heart/core/api/model" "github.com/anyproto/anytype-heart/core/api/pagination" @@ -141,7 +143,7 @@ func (s *Service) ListProperties(ctx context.Context, spaceId string, offset int filteredRecords := make([]*types.Struct, 0, len(resp.Records)) for _, record := range resp.Records { - rk, _ := s.getPropertyFromStruct(record) + rk, _, _ := s.getPropertyFromStruct(record) if _, isExcluded := excludedSystemProperties[rk]; isExcluded { continue } @@ -153,7 +155,7 @@ func (s *Service) ListProperties(ctx context.Context, spaceId string, offset int properties = make([]apimodel.Property, 0, len(paginatedProperties)) for _, record := range paginatedProperties { - _, property := s.getPropertyFromStruct(record) + _, _, property := s.getPropertyFromStruct(record) properties = append(properties, property) } @@ -181,7 +183,7 @@ func (s *Service) GetProperty(ctx context.Context, spaceId string, propertyId st } } - rk, property := s.getPropertyFromStruct(resp.ObjectView.Details[0].Details) + rk, _, property := s.getPropertyFromStruct(resp.ObjectView.Details[0].Details) if _, isExcluded := excludedSystemProperties[rk]; isExcluded { return apimodel.Property{}, ErrPropertyNotFound } @@ -198,11 +200,10 @@ func (s *Service) CreateProperty(ctx context.Context, spaceId string, request ap }, } - if request.Key != nil { - details.Fields[bundle.RelationKeyApiId.String()] = pbtypes.String(s.sanitizedString(*request.Key)) + if request.Key != "" { + details.Fields[bundle.RelationKeyApiId.String()] = pbtypes.String(s.sanitizedString(request.Key)) } else { - // TODO: transliterate instead - details.Fields[bundle.RelationKeyApiId.String()] = pbtypes.String(strings.ReplaceAll(request.Name, " ", "_")) + details.Fields[bundle.RelationKeyApiId.String()] = pbtypes.String(transliterate(request.Name)) } resp := s.mw.ObjectCreateRelation(ctx, &pb.RpcObjectCreateRelationRequest{ @@ -564,9 +565,10 @@ func (s *Service) getPropertyMapFromStore(ctx context.Context, spaceId string, k propertyMap := make(map[string]*apimodel.Property, len(resp.Records)) for _, record := range resp.Records { - rk, p := s.getPropertyFromStruct(record) + rk, apiId, p := s.getPropertyFromStruct(record) prop := p propertyMap[rk] = &prop + propertyMap[apiId] = &prop // TODO: add under api key as well, double check if keyByPropertyId { propertyMap[p.Id] = &prop // add property under id as key to map as well } @@ -576,18 +578,19 @@ func (s *Service) getPropertyMapFromStore(ctx context.Context, spaceId string, k } // getPropertyFromStruct maps a property's details into an apimodel.Property and returns its relation key. -func (s *Service) getPropertyFromStruct(details *types.Struct) (string, apimodel.Property) { +func (s *Service) getPropertyFromStruct(details *types.Struct) (string, string, apimodel.Property) { rk := details.Fields[bundle.RelationKeyRelationKey.String()].GetStringValue() + key := util.ToPropertyApiKey(rk) // apiId as key takes precedence over relation key - key := util.ToPropertyApiKey(rk) + var apiId string if apiIDField, exists := details.Fields[bundle.RelationKeyApiId.String()]; exists { - if apiID := apiIDField.GetStringValue(); apiID != "" { - key = apiID + if apiId = apiIDField.GetStringValue(); apiId != "" { + key = apiId } } - return rk, apimodel.Property{ + return rk, apiId, apimodel.Property{ Object: "property", Id: details.Fields[bundle.RelationKeyId.String()].GetStringValue(), Key: key, @@ -796,3 +799,9 @@ func (s *Service) buildPropertyWithValue(id string, key string, name string, for return nil } + +func transliterate(str string) string { + trimmed := strings.TrimSpace(str) + ascii := unidecode.Unidecode(trimmed) + return strcase.ToSnake(ascii) +} diff --git a/go.mod b/go.mod index 58349cbb6..e97d2a256 100644 --- a/go.mod +++ b/go.mod @@ -226,6 +226,7 @@ require ( github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/mozillazg/go-unidecode v0.2.0 // indirect github.com/multiformats/go-base36 v0.2.0 // indirect github.com/multiformats/go-multiaddr v0.15.0 // indirect github.com/multiformats/go-multicodec v0.9.0 // indirect diff --git a/go.sum b/go.sum index df0b0061f..c0a001f77 100644 --- a/go.sum +++ b/go.sum @@ -770,6 +770,8 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/mozillazg/go-unidecode v0.2.0 h1:vFGEzAH9KSwyWmXCOblazEWDh7fOkpmy/Z4ArmamSUc= +github.com/mozillazg/go-unidecode v0.2.0/go.mod h1:zB48+/Z5toiRolOZy9ksLryJ976VIwmDmpQ2quyt1aA= github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/multiformats/go-base32 v0.1.0 h1:pVx9xoSPqEIQG8o+UbAe7DNi51oej1NtK+aGkbLYxPE=