Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 0 additions & 61 deletions core/subscription/mock_subscription/mock_Service.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

71 changes: 63 additions & 8 deletions core/subscription/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,6 @@ type SubscribeResponse struct {
type Service interface {
Search(req SubscribeRequest) (resp *SubscribeResponse, err error)
SubscribeIdsReq(req pb.RpcObjectSubscribeIdsRequest) (resp *pb.RpcObjectSubscribeIdsResponse, err error)
SubscribeIds(subId string, ids []string) (records []*domain.Details, err error)
SubscribeGroups(req SubscribeGroupsRequest) (*pb.RpcObjectGroupsSubscribeResponse, error)
Unsubscribe(subIds ...string) (err error)
UnsubscribeAndReturnIds(spaceId string, subId string) ([]string, error)
Expand Down Expand Up @@ -231,10 +230,6 @@ func (s *service) SubscribeIdsReq(req pb.RpcObjectSubscribeIdsRequest) (resp *pb
return spaceSubs.SubscribeIdsReq(req)
}

func (s *service) SubscribeIds(subId string, ids []string) (records []*domain.Details, err error) {
return
}

func (s *service) SubscribeGroups(req SubscribeGroupsRequest) (*pb.RpcObjectGroupsSubscribeResponse, error) {
// todo: removed temp fix after we will have session-scoped subscriptions
// this is to prevent multiple subscriptions with the same id in different spaces
Expand Down Expand Up @@ -936,8 +931,8 @@ func (s *spaceSubscriptions) onChangeWithinContext(entries []*entry, proc func(c
}

func (s *spaceSubscriptions) filtersFromSource(sources []string) (database.Filter, error) {
var relTypeFilter database.FiltersOr
var (
relTypeFilter database.FiltersOr
relKeys []string
typeUniqueKeys []string
)
Expand Down Expand Up @@ -974,13 +969,73 @@ func (s *spaceSubscriptions) filtersFromSource(sources []string) (database.Filte
}

for _, relKey := range relKeys {
relTypeFilter = append(relTypeFilter, database.FilterExists{
existsFilter := database.FilterExists{
Key: domain.RelationKey(relKey),
})
}

typeIds, err := s.typesRecommendingRelation(relKey)
if err != nil || len(typeIds) == 0 {
relTypeFilter = append(relTypeFilter, existsFilter)
continue
}

nestedTypeFilter, err := database.MakeFilter("", database.FilterRequest{
RelationKey: bundle.RelationKeyType,
Condition: model.BlockContentDataviewFilter_In,
Value: domain.StringList(typeIds),
}, s.objectStore)
if err != nil {
relTypeFilter = append(relTypeFilter, existsFilter)
continue
}

relTypeFilter = append(relTypeFilter, database.FiltersOr{existsFilter, nestedTypeFilter})
}
return relTypeFilter, nil
}

func (s *spaceSubscriptions) typesRecommendingRelation(relKey string) ([]string, error) {
uk, err := domain.NewUniqueKey(smartblock.SmartBlockTypeRelation, relKey)
if err != nil {
return nil, fmt.Errorf("create relation unique key: %w", err)
}
relDetails, err := s.objectStore.GetObjectByUniqueKey(uk)
if err != nil {
return nil, fmt.Errorf("get relation object: %w", err)
}
relId := relDetails.GetString(bundle.RelationKeyId)
if relId == "" {
return nil, fmt.Errorf("relation object has no id")
}

records, err := s.objectStore.QueryRaw(&database.Filters{
FilterObj: database.FiltersAnd{
database.FilterEq{
Key: bundle.RelationKeyResolvedLayout,
Cond: model.BlockContentDataviewFilter_Equal,
Value: domain.Int64(int64(model.ObjectType_objectType)),
},
database.FiltersOr{
database.FilterAllIn{Key: bundle.RelationKeyRecommendedRelations, Strings: []string{relId}},
database.FilterAllIn{Key: bundle.RelationKeyRecommendedFeaturedRelations, Strings: []string{relId}},
database.FilterAllIn{Key: bundle.RelationKeyRecommendedFileRelations, Strings: []string{relId}},
database.FilterAllIn{Key: bundle.RelationKeyRecommendedHiddenRelations, Strings: []string{relId}},
},
},
}, 0, 0)
if err != nil {
return nil, fmt.Errorf("query types recommending relation: %w", err)
}

var typeIds []string
for _, rec := range records {
if typeId := rec.Details.GetString(bundle.RelationKeyId); typeId != "" {
typeIds = append(typeIds, typeId)
}
}
return typeIds, nil
}

func (s *spaceSubscriptions) depIdsFromFilter(filters []database.FilterRequest) (depIds []string) {
for _, f := range filters {
if s.ds.isRelationObject(f.RelationKey) {
Expand Down
82 changes: 82 additions & 0 deletions core/subscription/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1276,6 +1276,88 @@ func TestService_Search(t *testing.T) {
assert.Len(t, sub.Dependencies, 0)
assert.Len(t, sub.Records, 0)
})
t.Run("relation source includes objects whose type recommends the relation", func(t *testing.T) {
// given
fx := newFixtureWithRealObjectStore(t)
defer fx.a.Close(context.Background())
defer fx.ctrl.Finish()

relationKey := "done"
relationUK, err := domain.NewUniqueKey(smartblock.SmartBlockTypeRelation, relationKey)
require.NoError(t, err)

projectTypeUK, err := domain.NewUniqueKey(smartblock.SmartBlockTypeObjectType, "project")
require.NoError(t, err)

noteTypeUK, err := domain.NewUniqueKey(smartblock.SmartBlockTypeObjectType, "note")
require.NoError(t, err)

fx.store.AddObjects(t, testSpaceId, []objectstore.TestObject{
// Relation object
{
bundle.RelationKeyId: domain.String("rel-done-id"),
bundle.RelationKeyUniqueKey: domain.String(relationUK.Marshal()),
bundle.RelationKeyRelationKey: domain.String(relationKey),
bundle.RelationKeyResolvedLayout: domain.Int64(int64(model.ObjectType_relation)),
},
// Type "Project" that recommends the "done" relation
{
bundle.RelationKeyId: domain.String("type-project-id"),
bundle.RelationKeyUniqueKey: domain.String(projectTypeUK.Marshal()),
bundle.RelationKeyResolvedLayout: domain.Int64(int64(model.ObjectType_objectType)),
bundle.RelationKeyRecommendedRelations: domain.StringList([]string{"rel-done-id"}),
},
// Type "Note" that does NOT recommend the "done" relation
{
bundle.RelationKeyId: domain.String("type-note-id"),
bundle.RelationKeyUniqueKey: domain.String(noteTypeUK.Marshal()),
bundle.RelationKeyResolvedLayout: domain.Int64(int64(model.ObjectType_objectType)),
},
// Project object WITHOUT the "done" key in details (the bug case)
{
bundle.RelationKeyId: domain.String("project1"),
bundle.RelationKeyName: domain.String("Project 1"),
bundle.RelationKeyResolvedLayout: domain.Int64(int64(model.ObjectType_basic)),
bundle.RelationKeyType: domain.String("type-project-id"),
},
// Project object WITH the "done" key set (backward compat)
{
bundle.RelationKeyId: domain.String("project2"),
bundle.RelationKeyName: domain.String("Project 2"),
bundle.RelationKeyResolvedLayout: domain.Int64(int64(model.ObjectType_basic)),
bundle.RelationKeyType: domain.String("type-project-id"),
domain.RelationKey(relationKey): domain.Bool(true),
},
// Note object WITHOUT the "done" key (negative case — should not match)
{
bundle.RelationKeyId: domain.String("note1"),
bundle.RelationKeyName: domain.String("Note 1"),
bundle.RelationKeyResolvedLayout: domain.Int64(int64(model.ObjectType_basic)),
bundle.RelationKeyType: domain.String("type-note-id"),
},
})

// when
resp, err := fx.Search(SubscribeRequest{
SpaceId: testSpaceId,
SubId: "test-rel-source",
Keys: []string{bundle.RelationKeyId.String(), bundle.RelationKeyName.String()},
Source: []string{relationUK.Marshal()},
NoDepSubscription: true,
})

// then
require.NoError(t, err)
require.Len(t, resp.Records, 2)

ids := make([]string, len(resp.Records))
for i, r := range resp.Records {
ids[i] = r.GetString(bundle.RelationKeyId)
}
assert.Contains(t, ids, "project1")
assert.Contains(t, ids, "project2")
assert.NotContains(t, ids, "note1")
})
}

func addTestObjects(t *testing.T, source string, relationKey domain.RelationKey, option1, option2, testSpaceId string, fx *fixtureRealStore) error {
Expand Down
Loading