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:
commit
4fc468defc
18 changed files with 277 additions and 88 deletions
|
@ -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},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
@ -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 &&
|
||||
|
|
|
@ -40,6 +40,9 @@ var spaceViewRequiredRelations = []domain.RelationKey{
|
|||
bundle.RelationKeySpaceShareableStatus,
|
||||
bundle.RelationKeySpaceAccessType,
|
||||
bundle.RelationKeyLatestAclHeadId,
|
||||
bundle.RelationKeyChatId,
|
||||
bundle.RelationKeyReadersLimit,
|
||||
bundle.RelationKeyWritersLimit,
|
||||
}
|
||||
|
||||
type spaceService interface {
|
||||
|
|
|
@ -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{}))
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -105,6 +105,7 @@ func (ind *indexer) initQuery() {
|
|||
model.ObjectType_image,
|
||||
model.ObjectType_video,
|
||||
model.ObjectType_audio,
|
||||
model.ObjectType_pdf,
|
||||
}),
|
||||
},
|
||||
{
|
||||
|
|
|
@ -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,
|
||||
}),
|
||||
},
|
||||
{
|
||||
|
|
|
@ -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
10
go.mod
|
@ -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
28
go.sum
|
@ -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=
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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})
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue