Skip to content

Commit

Permalink
Add a reference to sourceAccountIdentifier (#4360)
Browse files Browse the repository at this point in the history
When the stack executes a konnector that imports files, it checks that a
directory exists for those files. And if the directory doesn't exist, it
creates it. We have added a referencedBy on the directory for the
sourceAccountIdentifier to be able to deal with several accounts for the
same konnector, as each account should have its own directory.
  • Loading branch information
nono authored Mar 26, 2024
2 parents 9af163d + 8ffadd2 commit da7e4d5
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 13 deletions.
4 changes: 0 additions & 4 deletions model/vfs/cozy_metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,6 @@ type FilesCozyMetadata struct {
UploadedBy *UploadedByEntry `json:"uploadedBy,omitempty"`
// Instance URL where the content has been changed the last time
UploadedOn string `json:"uploadedOn,omitempty"`
// Identifier of the account in io.cozy.accounts (for konnectors)
SourceAccount string `json:"sourceAccount,omitempty"`
// Identifier unique to the account targeted by the connector (login most of the time)
SourceIdentifier string `json:"sourceAccountIdentifier,omitempty"`
}

// NewCozyMetadata initializes a new FilesCozyMetadata struct
Expand Down
3 changes: 3 additions & 0 deletions pkg/consts/doctype.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,4 +136,7 @@ const (
// AuthConfirmations doc type used for realtime events when confirming
// authentication.
AuthConfirmations = "io.cozy.auth.confirmations"
// SourceAccountIdentifier doc type is used to link a directory to the
// konnector account that imports documents inside it.
SourceAccountIdentifier = "io.cozy.accounts.sourceAccountIdentifier"
)
4 changes: 4 additions & 0 deletions pkg/metadata/metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ type CozyMetadata struct {
UpdatedAt time.Time `json:"updatedAt"`
// List of objects representing the applications which modified the cozy document
UpdatedByApps []*UpdatedByAppEntry `json:"updatedByApps,omitempty"`
// Identifier of the account in io.cozy.accounts (for konnectors)
SourceAccount string `json:"sourceAccount,omitempty"`
// Identifier unique to the account targeted by the connector (login most of the time)
SourceIdentifier string `json:"sourceAccountIdentifier,omitempty"`
}

// New initializes a new CozyMetadata structure
Expand Down
37 changes: 34 additions & 3 deletions worker/exec/konnector.go
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,11 @@ func (w *konnectorWorker) ensureFolderToSave(ctx *job.TaskContext, inst *instanc
return err
}

var sourceAccountIdentifier string
if acc != nil && acc.Metadata != nil {
sourceAccountIdentifier = acc.Metadata.SourceIdentifier
}

// 2. Check if the konnector has a reference to a folder
start := []string{consts.Konnectors, consts.Konnectors + "/" + w.slug}
end := []string{start[0], start[1], couchdb.MaxString}
Expand All @@ -341,10 +346,14 @@ func (w *konnectorWorker) ensureFolderToSave(ctx *job.TaskContext, inst *instanc
for _, row := range res.Rows {
dir := &vfs.DirDoc{}
if err := couchdb.GetDoc(inst, consts.Files, row.ID, dir); err == nil {
if !strings.HasPrefix(dir.Fullpath, vfs.TrashDirName) {
count++
dirID = row.ID
if strings.HasPrefix(dir.Fullpath, vfs.TrashDirName) {
continue
}
if !hasCompatibleSourceAccountIdentifier(dir, sourceAccountIdentifier) {
continue
}
count++
dirID = row.ID
}
}
if count == 1 {
Expand Down Expand Up @@ -383,6 +392,12 @@ func (w *konnectorWorker) ensureFolderToSave(ctx *job.TaskContext, inst *instanc
Type: consts.Konnectors,
ID: consts.Konnectors + "/" + w.slug,
})
if sourceAccountIdentifier != "" {
dir.AddReferencedBy(couchdb.DocReference{
Type: consts.SourceAccountIdentifier,
ID: sourceAccountIdentifier,
})
}
instanceURL := inst.PageURL("/", nil)
if dir.CozyMetadata == nil {
dir.CozyMetadata = vfs.NewCozyMetadata(instanceURL)
Expand Down Expand Up @@ -424,6 +439,22 @@ func computeFolderPath(inst *instance.Instance, slug string, acc *account.Accoun
return fmt.Sprintf("/%s/%s/%s", admin, title, accountName)
}

func hasCompatibleSourceAccountIdentifier(dir *vfs.DirDoc, sourceAccountIdentifier string) bool {
if sourceAccountIdentifier == "" {
return true
}
nb := 0
for _, ref := range dir.ReferencedBy {
if ref.Type == consts.SourceAccountIdentifier {
if ref.ID == sourceAccountIdentifier {
return true
}
nb++
}
}
return nb == 0
}

// ensurePermissions checks that the konnector has the permissions to write
// files in the folder referenced by the konnector, and adds the permission if
// needed.
Expand Down
24 changes: 18 additions & 6 deletions worker/exec/konnector_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"github.com/cozy/cozy-stack/pkg/couchdb"
"github.com/cozy/cozy-stack/pkg/crypto"
"github.com/cozy/cozy-stack/pkg/i18n"
"github.com/cozy/cozy-stack/pkg/metadata"
"github.com/cozy/cozy-stack/pkg/prefixer"
"github.com/cozy/cozy-stack/pkg/realtime"
"github.com/cozy/cozy-stack/tests/testutils"
Expand Down Expand Up @@ -357,7 +358,11 @@ echo "{\"type\": \"toto\", \"message\": \"COZY_URL=${COZY_URL}\"}"

wg.Wait()

acc := &account.Account{}
acc := &account.Account{
Metadata: &metadata.CozyMetadata{
SourceIdentifier: "identifier1",
},
}

// Folder is created from DefaultFolderPath
wg.Add(1)
Expand Down Expand Up @@ -387,9 +392,12 @@ echo "{\"type\": \"toto\", \"message\": \"COZY_URL=${COZY_URL}\"}"
wg.Wait()

dir, err := fs.DirByPath("/Administrative/toto")
assert.NoError(t, err)
assert.Len(t, dir.ReferencedBy, 1)
require.NoError(t, err)
require.Len(t, dir.ReferencedBy, 2)
assert.Equal(t, dir.ReferencedBy[0].Type, "io.cozy.konnectors")
assert.Equal(t, dir.ReferencedBy[0].ID, "io.cozy.konnectors/my-konnector-1")
assert.Equal(t, dir.ReferencedBy[1].Type, "io.cozy.accounts.sourceAccountIdentifier")
assert.Equal(t, dir.ReferencedBy[1].ID, "identifier1")
assert.Equal(t, "my-konnector-1", dir.CozyMetadata.CreatedByApp)
assert.Contains(t, dir.CozyMetadata.CreatedOn, inst.Domain)
assert.Len(t, dir.CozyMetadata.UpdatedByApps, 1)
Expand Down Expand Up @@ -428,7 +436,11 @@ echo "{\"type\": \"toto\", \"message\": \"COZY_URL=${COZY_URL}\"}"

dir, err = fs.DirByPath("/Administrative/Trainline/account-1")
require.NoError(t, err)
require.Len(t, dir.ReferencedBy, 1)
require.Len(t, dir.ReferencedBy, 2)
assert.Equal(t, dir.ReferencedBy[0].Type, "io.cozy.konnectors")
assert.Equal(t, dir.ReferencedBy[0].ID, "io.cozy.konnectors/my-konnector-1")
assert.Equal(t, dir.ReferencedBy[1].Type, "io.cozy.accounts.sourceAccountIdentifier")
assert.Equal(t, dir.ReferencedBy[1].ID, "identifier1")
assert.Equal(t, dir.ReferencedBy[0].ID, "io.cozy.konnectors/my-konnector-1")
assert.Equal(t, "my-konnector-1", dir.CozyMetadata.CreatedByApp)
assert.Contains(t, dir.CozyMetadata.CreatedOn, inst.Domain)
Expand Down Expand Up @@ -470,11 +482,11 @@ func TestBeforeHookKonnector(t *testing.T) {
WorkerType: "konnector",
})

shouldExec, err := beforeHookKonnector(j)
shouldExec, _ := beforeHookKonnector(j)
assert.False(t, shouldExec)

testutils.WithFlag(t, inst, "harvest.skip-maintenance-for", map[string]interface{}{"list": []string{slug}})
shouldExec, err = beforeHookKonnector(j)
shouldExec, _ = beforeHookKonnector(j)
assert.True(t, shouldExec)
})
}
Expand Down

0 comments on commit da7e4d5

Please sign in to comment.