1
0
Fork 0
mirror of https://github.com/anyproto/anytype-heart.git synced 2025-06-09 09:35:00 +09:00

GO-4146 Merge remote-tracking branch 'origin/main' into GO-4146-new-spacestore

# Conflicts:
#	go.mod
This commit is contained in:
mcrakhman 2025-02-13 14:02:27 +01:00
commit 4fc468defc
No known key found for this signature in database
GPG key ID: DED12CFEF5B8396B
18 changed files with 277 additions and 88 deletions

View file

@ -476,11 +476,21 @@ func (c *Config) GetNetworkMode() pb.RpcAccountNetworkMode {
}
func (c *Config) GetPublishServer() publishclient.Config {
publishPeerId := "12D3KooWEQPgbxGPvkny8kikS3zqfziM7JsQBnJHXHL9ByCcATs7"
publishAddr := "anytype-publish-server-yamux-fb3a0765ead8fc08.elb.eu-central-2.amazonaws.com:443"
if peerId := os.Getenv("ANYTYPE_PUBLISH_PEERID"); peerId != "" {
if addr := os.Getenv("ANYTYPE_PUBLISH_ADDRESS"); addr != "" {
publishPeerId = peerId
publishAddr = addr
}
}
return publishclient.Config{
Addrs: []publishclient.PublishServerAddr{
{
PeerId: "12D3KooWEQPgbxGPvkny8kikS3zqfziM7JsQBnJHXHL9ByCcATs7",
Addrs: []string{"yamux://anytype-publish-server-yamux-fb3a0765ead8fc08.elb.eu-central-2.amazonaws.com:443"},
PeerId: publishPeerId,
Addrs: []string{"yamux://" + publishAddr},
},
},
}

View file

@ -83,6 +83,12 @@ type accountObject struct {
crdtDb anystore.DB
}
// required relations for spaceview beside the bundle.RequiredInternalRelations
var accountRequiredRelations = []domain.RelationKey{
bundle.RelationKeyProfileOwnerIdentity,
bundle.RelationKeySharedSpacesLimit,
}
func (a *accountObject) SetDetails(ctx session.Context, details []domain.Detail, showEvent bool) (err error) {
return a.bs.SetDetails(ctx, details, showEvent)
}
@ -111,6 +117,8 @@ func New(
}
func (a *accountObject) Init(ctx *smartblock.InitContext) error {
ctx.RequiredInternalRelationKeys = append(ctx.RequiredInternalRelationKeys, accountRequiredRelations...)
err := a.SmartBlock.Init(ctx)
if err != nil {
return err

View file

@ -63,6 +63,7 @@ func TestDependenciesSubscription(t *testing.T) {
bundle.RelationKeyId: domain.String(mainObjId),
bundle.RelationKeySpaceId: domain.String(testSpaceId),
bundle.RelationKeyName: domain.String("Main object"),
bundle.RelationKeyLayout: domain.Int64(int64(model.ObjectType_todo)),
})
fx.Doc.(*state.State).SetDetails(objDetails)
@ -79,6 +80,7 @@ func TestDependenciesSubscription(t *testing.T) {
bundle.RelationKeyId.String(): pbtypes.String(mainObjId),
bundle.RelationKeySpaceId.String(): pbtypes.String(testSpaceId),
bundle.RelationKeyName.String(): pbtypes.String("Main object"),
bundle.RelationKeyLayout.String(): pbtypes.Int64(int64(model.ObjectType_todo)),
},
},
},

View file

@ -312,7 +312,7 @@ func (sb *smartBlock) Type() smartblock.SmartBlockType {
}
func (sb *smartBlock) ObjectTypeID() string {
return sb.Doc.Details().GetString(bundle.RelationKeyType)
return sb.Doc.LocalDetails().GetString(bundle.RelationKeyType)
}
func (sb *smartBlock) Init(ctx *InitContext) (err error) {
@ -505,10 +505,35 @@ func (sb *smartBlock) fetchMeta() (details []*model.ObjectViewDetailsSet, err er
Details: rec.Details.ToProto(),
})
}
// TODO: GO-4222 remove this hack after primitives merge
injectLayout(details)
go sb.metaListener(recordsCh)
return
}
// TODO: GO-4222 remove this hack after primitives merge
func injectLayout(details []*model.ObjectViewDetailsSet) {
rootDetailsProto := details[0].Details
rootDetails := domain.NewDetailsFromProto(rootDetailsProto)
if rootDetails.Has(bundle.RelationKeyLayout) {
// no hack needed if object contains layout detail
return
}
typeId := rootDetails.GetString(bundle.RelationKeyType)
layout := model.ObjectType_basic // fallback
for _, detailsModel := range details {
if detailsModel.Id == typeId {
// nolint:gosec
layout = model.ObjectTypeLayout(domain.NewDetailsFromProto(detailsModel.Details).GetInt64(bundle.RelationKeyRecommendedLayout))
break
}
}
rootDetailsProto.Fields[bundle.RelationKeyLayout.String()] = pbtypes.Int64(int64(layout))
}
func (sb *smartBlock) partitionIdsBySpace(ids []string) map[string][]string {
perSpace := map[string][]string{}
for _, id := range ids {
@ -774,7 +799,7 @@ func (sb *smartBlock) Apply(s *state.State, flags ...ApplyFlag) (err error) {
if !act.IsEmpty() {
if len(changes) == 0 && !doSnapshot {
log.Errorf("apply 0 changes %s: %v", st.RootId(), anonymize.Events(msgsToEvents(msgs)))
log.With("sbType", sb.Type().String()).Errorf("apply 0 changes %s: %v", st.RootId(), anonymize.Events(msgsToEvents(msgs)))
}
err = pushChange()
if err != nil {
@ -1334,18 +1359,40 @@ func (sb *smartBlock) getDocInfo(st *state.State) DocInfo {
heads = []string{lastChangeId}
}
}
details := sb.CombinedDetails()
// TODO: GO-4222 remove this hack after primitives merge
sb.injectLayout(details)
return DocInfo{
Id: sb.Id(),
Space: sb.Space(),
Links: links,
Heads: heads,
Creator: creator,
Details: sb.CombinedDetails(),
Details: details,
Type: sb.ObjectTypeKey(),
SmartblockType: sb.Type(),
}
}
// TODO: GO-4222 remove this hack after primitives merge
func (sb *smartBlock) injectLayout(details *domain.Details) {
if details.Has(bundle.RelationKeyLayout) {
// no hack needed if object contains layout detail
return
}
layout := model.ObjectType_basic // fallback
records, err := sb.objectStore.SpaceIndex(sb.SpaceID()).QueryByIds([]string{sb.ObjectTypeID()})
if err == nil && len(records) != 0 {
// nolint:gosec
layout = model.ObjectTypeLayout(records[0].Details.GetInt64(bundle.RelationKeyRecommendedLayout))
}
details.SetInt64(bundle.RelationKeyLayout, int64(layout))
}
func (sb *smartBlock) runIndexer(s *state.State, opts ...IndexOption) {
docInfo := sb.getDocInfo(s)
if err := sb.indexer.Index(docInfo, opts...); err != nil {
@ -1368,11 +1415,12 @@ func removeInternalFlags(s *state.State) {
}
func (sb *smartBlock) setRestrictionsDetail(s *state.State) {
rawRestrictions := make([]float64, len(sb.Restrictions().Object))
for i, r := range sb.Restrictions().Object {
rawRestrictions[i] = float64(r)
currentRestrictions := restriction.NewObjectRestrictionsFromValue(s.LocalDetails().Get(bundle.RelationKeyRestrictions))
if currentRestrictions.Equal(sb.Restrictions().Object) {
return
}
s.SetLocalDetail(bundle.RelationKeyRestrictions, domain.Float64List(rawRestrictions))
s.SetLocalDetail(bundle.RelationKeyRestrictions, sb.Restrictions().Object.ToValue())
// todo: verify this logic with clients
if sb.Restrictions().Object.Check(model.Restrictions_Details) != nil &&

View file

@ -40,6 +40,9 @@ var spaceViewRequiredRelations = []domain.RelationKey{
bundle.RelationKeySpaceShareableStatus,
bundle.RelationKeySpaceAccessType,
bundle.RelationKeyLatestAclHeadId,
bundle.RelationKeyChatId,
bundle.RelationKeyReadersLimit,
bundle.RelationKeyWritersLimit,
}
type spaceService interface {

View file

@ -232,7 +232,7 @@ var WithDefaultFeaturedRelations = func(s *state.State) {
fr = []string{bundle.RelationKeyType.String(), bundle.RelationKeySetOf.String(), bundle.RelationKeyBacklinks.String()}
case model.ObjectType_collection:
fr = []string{bundle.RelationKeyType.String(), bundle.RelationKeyBacklinks.String()}
case model.ObjectType_file, model.ObjectType_image, model.ObjectType_audio, model.ObjectType_video:
case model.ObjectType_file, model.ObjectType_image, model.ObjectType_audio, model.ObjectType_video, model.ObjectType_pdf:
fr = []string{bundle.RelationKeyType.String(), bundle.RelationKeyTag.String(), bundle.RelationKeyBacklinks.String()}
// Tag is not added to details of object explicitly as it is not system relation
s.SetDetail(bundle.RelationKeyTag, domain.StringList([]string{}))

View file

@ -3,14 +3,12 @@ package restriction
import (
"fmt"
"github.com/gogo/protobuf/types"
"github.com/samber/lo"
"github.com/anyproto/anytype-heart/core/domain"
"github.com/anyproto/anytype-heart/pkg/lib/bundle"
"github.com/anyproto/anytype-heart/pkg/lib/core/smartblock"
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
"github.com/anyproto/anytype-heart/util/pbtypes"
)
var (
@ -169,6 +167,16 @@ func GetRestrictionsBySBType(sbType smartblock.SmartBlockType) []int {
type ObjectRestrictions []model.RestrictionsObjectRestriction
func NewObjectRestrictionsFromValue(v domain.Value) ObjectRestrictions {
raw := v.Int64List()
restrictions := make(ObjectRestrictions, len(raw))
for i, restriction := range raw {
// nolint:gosec
restrictions[i] = model.RestrictionsObjectRestriction(restriction)
}
return restrictions
}
func (or ObjectRestrictions) Check(cr ...model.RestrictionsObjectRestriction) (err error) {
for _, r := range cr {
for _, er := range or {
@ -198,12 +206,12 @@ func (or ObjectRestrictions) Copy() ObjectRestrictions {
return obj
}
func (or ObjectRestrictions) ToPB() *types.Value {
var ints = make([]int, len(or))
func (or ObjectRestrictions) ToValue() domain.Value {
var ints = make([]int64, len(or))
for i, v := range or {
ints[i] = int(v)
ints[i] = int64(v)
}
return pbtypes.IntList(ints...)
return domain.Int64List(ints)
}
func getObjectRestrictions(rh RestrictionHolder) (r ObjectRestrictions) {

View file

@ -400,7 +400,7 @@ func (h *MD) getLinkInfo(docId string) (title, filename string, ok bool) {
title = info.GetString(bundle.RelationKeyName)
// if object is a file
layout := info.GetInt64(bundle.RelationKeyLayout)
if layout == int64(model.ObjectType_file) || layout == int64(model.ObjectType_image) || layout == int64(model.ObjectType_audio) || layout == int64(model.ObjectType_video) {
if layout == int64(model.ObjectType_file) || layout == int64(model.ObjectType_image) || layout == int64(model.ObjectType_audio) || layout == int64(model.ObjectType_video) || layout == int64(model.ObjectType_pdf) {
ext := info.GetString(bundle.RelationKeyFileExt)
if ext != "" {
ext = "." + ext

View file

@ -4,9 +4,13 @@ import (
"fmt"
"github.com/anyproto/any-store/anyenc"
"github.com/anyproto/any-sync/app/logger"
"github.com/gogo/protobuf/types"
"go.uber.org/zap"
)
var log = logger.NewNamed("core.domain")
// Detail is Key-Value pair
type Detail struct {
Key RelationKey
@ -52,7 +56,7 @@ func NewDetailsFromAnyEnc(v *anyenc.Value) (*Details, error) {
// key is copied
err := setValueFromAnyEnc(res, RelationKey(k), v)
if err != nil {
visitErr = err
visitErr = fmt.Errorf("key %s: %w", k, err)
}
})
return res, visitErr
@ -92,10 +96,25 @@ func setValueFromAnyEnc(d *Details, key RelationKey, val *anyenc.Value) error {
return nil
}
firstVal := arrVals[0]
if firstVal.Type() == anyenc.TypeString {
var arrayType anyenc.Type
for _, arrVal := range arrVals {
if arrVal.Type() == anyenc.TypeNumber {
arrayType = anyenc.TypeNumber
break
}
if arrVal.Type() == anyenc.TypeString {
arrayType = anyenc.TypeString
break
}
}
if arrayType == anyenc.TypeString {
res := make([]string, 0, len(arrVals))
for _, arrVal := range arrVals {
for i, arrVal := range arrVals {
if arrVal.Type() != anyenc.TypeString {
// todo: make it not possible to create such an arrays and remove this
log.With(zap.String("key", key.String())).With(zap.Int("index", i)).Error(fmt.Sprintf("array item: expected string, got %s", arrVal.Type()))
continue
}
v, err := arrVal.StringBytes()
if err != nil {
return fmt.Errorf("array item: string: %w", err)
@ -104,9 +123,14 @@ func setValueFromAnyEnc(d *Details, key RelationKey, val *anyenc.Value) error {
}
d.SetStringList(key, res)
return nil
} else if firstVal.Type() == anyenc.TypeNumber {
} else if arrayType == anyenc.TypeNumber {
res := make([]float64, 0, len(arrVals))
for _, arrVal := range arrVals {
for i, arrVal := range arrVals {
if arrVal.Type() != anyenc.TypeNumber {
// todo: make it not possible to create such an arrays and remove this
log.With(zap.String("key", key.String())).With(zap.Int("index", i)).Error(fmt.Sprintf("array item: expected number, got %s", arrVal.Type()))
continue
}
v, err := arrVal.Float64()
if err != nil {
return fmt.Errorf("array item: number: %w", err)
@ -116,7 +140,16 @@ func setValueFromAnyEnc(d *Details, key RelationKey, val *anyenc.Value) error {
d.SetFloat64List(key, res)
return nil
} else {
return fmt.Errorf("unsupported array type %s", firstVal.Type())
var elTypes []string
for _, arrVal := range arrVals {
elTypes = append(elTypes, arrVal.Type().String())
}
d.SetStringList(key, []string{})
log.With(zap.String("key", key.String())).Error(fmt.Sprintf("unsupported array: %v", elTypes))
// todo: make it not possible to create such an arrays and remove this logic
return nil
}
}
d.Set(key, Null())

View file

@ -105,6 +105,7 @@ func (ind *indexer) initQuery() {
model.ObjectType_image,
model.ObjectType_video,
model.ObjectType_audio,
model.ObjectType_pdf,
}),
},
{

View file

@ -277,6 +277,7 @@ func (i *indexer) removeOldFiles(spaceId string, flags reindexFlags) error {
model.ObjectType_image,
model.ObjectType_video,
model.ObjectType_audio,
model.ObjectType_pdf,
}),
},
{

View file

@ -32,7 +32,7 @@ var documentRelationsWhiteList = append(slices.Clone(allObjectsRelationsWhiteLis
var todoRelationsWhiteList = append(slices.Clone(documentRelationsWhiteList), bundle.RelationKeyDone.String())
var bookmarkRelationsWhiteList = append(slices.Clone(documentRelationsWhiteList), bundle.RelationKeyPicture.String())
var bookmarkRelationsWhiteList = append(slices.Clone(documentRelationsWhiteList), bundle.RelationKeyPicture.String(), bundle.RelationKeySource.String())
var derivedObjectsWhiteList = append(slices.Clone(allObjectsRelationsWhiteList), bundle.RelationKeyUniqueKey.String())
@ -63,6 +63,7 @@ var publishingRelationsWhiteList = map[model.ObjectTypeLayout][]string{
model.ObjectType_objectType: derivedObjectsWhiteList,
model.ObjectType_relation: relationsWhiteList,
model.ObjectType_file: fileRelationsWhiteList,
model.ObjectType_pdf: fileRelationsWhiteList,
model.ObjectType_dashboard: allObjectsRelationsWhiteList,
model.ObjectType_image: imageRelationsWhiteList,
model.ObjectType_note: documentRelationsWhiteList,

10
go.mod
View file

@ -106,9 +106,9 @@ require (
golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8
golang.org/x/image v0.24.0
golang.org/x/mobile v0.0.0-20241108191957-fa514ef75a0f
golang.org/x/net v0.34.0
golang.org/x/net v0.35.0
golang.org/x/oauth2 v0.26.0
golang.org/x/sys v0.29.0
golang.org/x/sys v0.30.0
golang.org/x/text v0.22.0
google.golang.org/grpc v1.70.0
gopkg.in/Graylog2/go-gelf.v2 v2.0.0-20180125164251-1832d8546a9f
@ -281,10 +281,10 @@ require (
go.opentelemetry.io/otel/metric v1.32.0 // indirect
go.opentelemetry.io/otel/trace v1.32.0 // indirect
golang.org/x/arch v0.8.0 // indirect
golang.org/x/crypto v0.32.0 // indirect
golang.org/x/crypto v0.33.0 // indirect
golang.org/x/mod v0.22.0 // indirect
golang.org/x/sync v0.11.0 // indirect
golang.org/x/term v0.28.0 // indirect
golang.org/x/term v0.29.0 // indirect
golang.org/x/time v0.10.0 // indirect
golang.org/x/tools v0.29.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20241202173237-19429a94021a // indirect
@ -293,7 +293,7 @@ require (
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
lukechampine.com/blake3 v1.3.0 // indirect
modernc.org/libc v1.61.9 // indirect
modernc.org/libc v1.61.13 // indirect
modernc.org/mathutil v1.7.1 // indirect
modernc.org/memory v1.8.2 // indirect
modernc.org/sqlite v1.34.5 // indirect

28
go.sum
View file

@ -1192,8 +1192,8 @@ golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliY
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@ -1309,8 +1309,8 @@ golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@ -1426,8 +1426,8 @@ golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
@ -1438,8 +1438,8 @@ golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg=
golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU=
golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@ -1689,14 +1689,14 @@ lukechampine.com/blake3 v1.3.0 h1:sJ3XhFINmHSrYCgl958hscfIa3bw8x4DqMP3u1YvoYE=
lukechampine.com/blake3 v1.3.0/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k=
modernc.org/cc/v4 v4.24.4 h1:TFkx1s6dCkQpd6dKurBNmpo+G8Zl4Sq/ztJ+2+DEsh0=
modernc.org/cc/v4 v4.24.4/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
modernc.org/ccgo/v4 v4.23.13 h1:PFiaemQwE/jdwi8XEHyEV+qYWoIuikLP3T4rvDeJb00=
modernc.org/ccgo/v4 v4.23.13/go.mod h1:vdN4h2WR5aEoNondUx26K7G8X+nuBscYnAEWSRmN2/0=
modernc.org/ccgo/v4 v4.23.16 h1:Z2N+kk38b7SfySC1ZkpGLN2vthNJP1+ZzGZIlH7uBxo=
modernc.org/ccgo/v4 v4.23.16/go.mod h1:nNma8goMTY7aQZQNTyN9AIoJfxav4nvTnvKThAeMDdo=
modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE=
modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ=
modernc.org/gc/v2 v2.6.1 h1:+Qf6xdG8l7B27TQ8D8lw/iFMUj1RXRBOuMUWziJOsk8=
modernc.org/gc/v2 v2.6.1/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
modernc.org/libc v1.61.9 h1:PLSBXVkifXGELtJ5BOnBUyAHr7lsatNwFU/RRo4kfJM=
modernc.org/libc v1.61.9/go.mod h1:61xrnzk/aR8gr5bR7Uj/lLFLuXu2/zMpIjcry63Eumk=
modernc.org/gc/v2 v2.6.3 h1:aJVhcqAte49LF+mGveZ5KPlsp4tdGdAOT4sipJXADjw=
modernc.org/gc/v2 v2.6.3/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
modernc.org/libc v1.61.13 h1:3LRd6ZO1ezsFiX1y+bHd1ipyEHIJKvuprv0sLTBwLW8=
modernc.org/libc v1.61.13/go.mod h1:8F/uJWL/3nNil0Lgt1Dpz+GgkApWh04N3el3hxJcA6E=
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
modernc.org/memory v1.8.2 h1:cL9L4bcoAObu4NkxOlKWBWtNHIsnnACGF/TbqQ6sbcI=

View file

@ -292,20 +292,6 @@ func (g *gateway) getImage(ctx context.Context, r *http.Request) (files.File, io
urlParts := strings.Split(r.URL.Path, "/")
imageId := urlParts[2]
var id domain.FullFileId
// Treat id as fileId. We need to handle raw fileIds for backward compatibility in case of spaceview. See editor.SpaceView for details.
if domain.IsFileId(imageId) {
id = domain.FullFileId{
FileId: domain.FileId(imageId),
}
} else {
var err error
id, err = g.fileObjectService.GetFileIdFromObjectWaitLoad(ctx, imageId)
if err != nil {
return nil, nil, fmt.Errorf("get file hash from object id: %w", err)
}
}
retryOptions := []retry.Option{
retry.Context(ctx),
retry.Attempts(0),
@ -316,6 +302,20 @@ func (g *gateway) getImage(ctx context.Context, r *http.Request) (files.File, io
}
result, err := retry.DoWithData(func() (*getImageReaderResult, error) {
var id domain.FullFileId
// Treat id as fileId. We need to handle raw fileIds for backward compatibility in case of spaceview. See editor.SpaceView for details.
if domain.IsFileId(imageId) {
id = domain.FullFileId{
FileId: domain.FileId(imageId),
}
} else {
var err error
id, err = g.fileObjectService.GetFileIdFromObjectWaitLoad(ctx, imageId)
if err != nil {
return nil, fmt.Errorf("get file hash from object id: %w", err)
}
}
res, err := g.getImageReader(ctx, id, r)
if err != nil {
return nil, err

View file

@ -434,7 +434,7 @@ func (s *dsObjectStore) QueryByIds(ids []string) (records []database.Record, err
SpaceID: s.SpaceId(),
})
if err != nil {
log.Errorf("QueryByIds failed to GetDetailsFromIdBasedSource id: %s", id)
log.With("id", id).Errorf("QueryByIds failed to GetDetailsFromIdBasedSource id: %s", err.Error())
continue
}
details.SetString(bundle.RelationKeyId, id)
@ -444,12 +444,12 @@ func (s *dsObjectStore) QueryByIds(ids []string) (records []database.Record, err
}
doc, err := s.objects.FindId(s.componentCtx, id)
if err != nil {
log.Infof("QueryByIds failed to find id: %s", id)
log.With("id", id).Infof("QueryByIds failed to find id: %s", err.Error())
continue
}
details, err := domain.NewDetailsFromAnyEnc(doc.Value())
if err != nil {
log.Errorf("QueryByIds failed to extract details: %s", id)
log.With("id", id).Errorf("QueryByIds failed to extract details: %s", err.Error())
continue
}
records = append(records, database.Record{Details: details})

View file

@ -3,6 +3,7 @@ package text
import (
"unicode"
"unicode/utf16"
"unicode/utf8"
)
const TruncateEllipsis = " …"
@ -11,42 +12,61 @@ func TruncateEllipsized(text string, length int) string {
return Truncate(text, length, TruncateEllipsis)
}
func Truncate(text string, length int, ending string) string {
length -= UTF16RuneCountString(ending)
if UTF16RuneCountString(text) <= length {
return text
func Truncate(str string, maxLen int, ending string) string {
if isUtf16LengthLessOrEqual(str, maxLen) {
return str
}
utf16Text := StrToUTF16(text)
var lastWordIndex, lastNonSpace, currentLen, endTextPos int
for i, r := range utf16Text {
currentLen++
if unicode.IsSpace(rune(r)) {
maxLen -= UTF16RuneCountString(ending)
var (
utf16Len int
lastWordIndex int
lastNonSpace int
)
for i, r := range str {
runeSize := utf16.RuneLen(r)
if unicode.IsSpace(r) {
lastWordIndex = lastNonSpace
} else if unicode.In(rune(r), unicode.Han, unicode.Hangul, unicode.Hiragana, unicode.Katakana) {
lastWordIndex = i
} else {
lastNonSpace = i + 1
lastNonSpace = i + utf8.RuneLen(r)
}
if currentLen > length {
utf16Len += runeSize
if utf16Len > maxLen {
var runeEnd int
if lastWordIndex == 0 {
endTextPos = i
runeEnd = i
} else {
endTextPos = lastWordIndex
runeEnd = lastWordIndex
}
if ending == "" {
return str[:runeEnd]
} else {
return str[:runeEnd] + ending
}
out := utf16Text[0:endTextPos]
return UTF16ToStr(out) + ending
}
}
return UTF16ToStr(utf16Text)
return str
}
func isUtf16LengthLessOrEqual(str string, maxLen int) bool {
var n int
for _, s := range str {
n += utf16.RuneLen(s)
if n > maxLen {
return false
}
}
return true
}
func UTF16RuneCountString(str string) int {
buf := make([]uint16, 0, 2)
var n int
for _, s := range str {
buf = utf16.AppendRune(buf, s)
n += len(buf)
buf = buf[:0]
n += utf16.RuneLen(s)
}
return n
}

View file

@ -6,24 +6,72 @@ import (
"github.com/stretchr/testify/assert"
)
func TestTruncate(t *testing.T) {
func TestTruncateEllipsized(t *testing.T) {
tests := []struct {
name string
text string
length int
expected string
}{
{
name: "3 spaces",
text: " ",
length: 3,
expected: " ",
},
{
name: "4 spaces",
text: " ",
length: 3,
expected: " …",
},
{
name: "Space",
text: " ",
length: 3,
expected: " ",
},
{
name: "Divine emojie not fit",
text: "🌍",
length: 1,
expected: " …",
},
{
name: "Divine emojies fit",
text: "🌍",
length: 4,
expected: "🌍",
},
{
name: "Text with divine emojies not fit",
text: "Hello 🌍",
length: 7,
expected: "Hello …",
},
{
name: "Text with divine emojies fit",
text: "Hello 🌍",
length: 10,
expected: "Hello 🌍",
},
{
name: "Text shorter than length",
text: "Hello, world!",
length: 20,
expected: "Hello, world!",
},
{
name: "Text shorter than length",
text: "Hello, world!",
length: 12,
expected: "Hello, …",
},
{
name: "Text equal to length",
text: "Hello, world!",
length: 13,
expected: "Hello, …",
expected: "Hello, world!",
},
{
name: "Text longer than length with space truncation",
@ -44,10 +92,16 @@ func TestTruncate(t *testing.T) {
expected: "こんにちは、世界!",
},
{
name: "Text longer than length with mixed characters",
name: "Text longer than length with mixed characters space",
text: "Hello, こんにちは 世界!",
length: 15,
expected: "Hello, こんにちは …",
},
{
name: "Text longer than length with mixed characters no space",
text: "Hello, こんにちは、世界!",
length: 17,
expected: "Hello, こんにちは、世 …",
length: 15,
expected: "Hello, …",
},
{
name: "Text with ellipsis already present",