From f271d02c3993916def4abed59c28dd8f7cd0ac32 Mon Sep 17 00:00:00 2001 From: Bruno Michel Date: Mon, 27 Nov 2023 10:54:34 +0100 Subject: [PATCH 1/2] Revert "Remove an old hack for migration path on references" This reverts commit 21e94d28be2cc65cc23110328e82b115820cb2d1. The hack is still needed, as some Cozy instances have references in the bogus format in production. --- web/data/references_test.go | 59 ++++++++++++++++++++++++++++++++++++- web/files/references.go | 28 ++++++++++++++++++ 2 files changed, 86 insertions(+), 1 deletion(-) diff --git a/web/data/references_test.go b/web/data/references_test.go index 9b46d02773a..285a02ba2c1 100644 --- a/web/data/references_test.go +++ b/web/data/references_test.go @@ -44,7 +44,7 @@ func TestReferences(t *testing.T) { }, }) - t.Run("ListNotSynchronizedOn", func(t *testing.T) { + t.Run("ListReferencesHandler", func(t *testing.T) { e := testutils.CreateTestClient(t, ts.URL) // Make doc @@ -230,6 +230,41 @@ func TestReferences(t *testing.T) { obj.Value("data").Array().Length().Equal(1) obj.Path("$.data[0].id").Equal(fdoc.ID()) + // Add dummy references on io.cozy.apps%2ffoobaz and io.cozy.apps%2Ffooqux + foobazRef := couchdb.DocReference{ + ID: "io.cozy.apps%2ffoobaz", + Type: Type, + } + fooquxRef := couchdb.DocReference{ + ID: "io.cozy.apps%2Ffooqux", + Type: Type, + } + fdoc.ReferencedBy = append(fdoc.ReferencedBy, foobazRef, fooquxRef) + err = couchdb.UpdateDoc(testInstance, fdoc) + assert.NoError(t, err) + + // Check that we can find the reference with %2f + obj = e.GET("/data/"+Type+"/io.cozy.apps%2ffoobar/relationships/references"). + WithHeader("Authorization", "Bearer "+token). + Expect().Status(200). + JSON(httpexpect.ContentOpts{MediaType: "application/vnd.api+json"}). + Object() + + obj.Path("$.meta.count").Equal(1) + obj.Value("data").Array().Length().Equal(1) + obj.Path("$.data[0].id").Equal(fdoc.ID()) + + // Check that we can find the reference with %2F + obj = e.GET("/data/"+Type+"/io.cozy.apps%2Ffoobar/relationships/references"). + WithHeader("Authorization", "Bearer "+token). + Expect().Status(200). + JSON(httpexpect.ContentOpts{MediaType: "application/vnd.api+json"}). + Object() + + obj.Path("$.meta.count").Equal(1) + obj.Value("data").Array().Length().Equal(1) + obj.Path("$.data[0].id").Equal(fdoc.ID()) + // Remove the reference with a / e.DELETE("/data/"+Type+"/io.cozy.apps%2Ffoobar/relationships/references"). WithHeader("Authorization", "Bearer "+token). @@ -241,6 +276,28 @@ func TestReferences(t *testing.T) { }`)). Expect().Status(204) + // Remove the reference with a %2f + e.DELETE("/data/"+Type+"/io.cozy.apps%2ffoobaz/relationships/references"). + WithHeader("Authorization", "Bearer "+token). + WithHeader("Content-Type", "application/vnd.api+json"). + WithBytes([]byte(`{ + "data": [ + {"id": "` + fdoc.ID() + `", "type": "` + consts.Files + `"} + ] + }`)). + Expect().Status(204) + + // Remove the reference with a %2F + e.DELETE("/data/"+Type+"/io.cozy.apps%2Ffooqux/relationships/references"). + WithHeader("Authorization", "Bearer "+token). + WithHeader("Content-Type", "application/vnd.api+json"). + WithBytes([]byte(`{ + "data": [ + {"id": "` + fdoc.ID() + `", "type": "` + consts.Files + `"} + ] + }`)). + Expect().Status(204) + // Check that all the references have been removed fdoc2, err := testInstance.VFS().FileByID(fdoc.ID()) assert.NoError(t, err) diff --git a/web/files/references.go b/web/files/references.go index 1801aa84760..9193f3a14a8 100644 --- a/web/files/references.go +++ b/web/files/references.go @@ -77,6 +77,17 @@ func ListReferencesHandler(c echo.Context) error { if len(resCount.Rows) > 0 { count = int(resCount.Rows[0].Value.(float64)) } + + // XXX Some references can contains %2f instead of in the id (legacy), + // and to preserve compatibility, we try to find those documents if no + // documents with the correct reference are found. + if count == 0 && strings.Contains(id, "/") { + key[1] = c.Param("docid") + err = couchdb.ExecView(instance, couchdb.FilesReferencedByView, reqCount, &resCount) + if err == nil && len(resCount.Rows) > 0 { + count = int(resCount.Rows[0].Value.(float64)) + } + } meta := &jsonapi.Meta{Count: &count} sort := c.QueryParam("sort") @@ -242,6 +253,17 @@ func RemoveReferencesHandler(c echo.Context) error { ID: id, } + // XXX References with an ID that contains a / could have it encoded as %2F + // (legacy). We delete the references for both versions to preserve + // compatibility. + var altRef *couchdb.DocReference + if strings.Contains(id, "/") { + altRef = &couchdb.DocReference{ + Type: doctype, + ID: c.Param("docid"), + } + } + if err := middlewares.AllowTypeAndID(c, permission.DELETE, doctype, id); err != nil { if middlewares.AllowWholeType(c, permission.PATCH, consts.Files) != nil { return err @@ -259,12 +281,18 @@ func RemoveReferencesHandler(c echo.Context) error { if dir != nil { oldDir := dir.Clone() dir.RemoveReferencedBy(docRef) + if altRef != nil { + dir.RemoveReferencedBy(*altRef) + } updateDirCozyMetadata(c, dir) docs[i] = dir oldDocs[i] = oldDir } else { oldFile := file.Clone().(*vfs.FileDoc) file.RemoveReferencedBy(docRef) + if altRef != nil { + file.RemoveReferencedBy(*altRef) + } updateFileCozyMetadata(c, file, false) _, _ = file.Path(fs) // Ensure the fullpath is filled to realtime _, _ = oldFile.Path(fs) // Ensure the fullpath is filled to realtime From 0963cb38f41addbe41015531386c7781977be2a9 Mon Sep 17 00:00:00 2001 From: Bruno Michel Date: Mon, 27 Nov 2023 11:43:26 +0100 Subject: [PATCH 2/2] Update web/files/references.go Co-authored-by: Erwan Guyader --- web/files/references.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/files/references.go b/web/files/references.go index 9193f3a14a8..922bd32eb9f 100644 --- a/web/files/references.go +++ b/web/files/references.go @@ -78,7 +78,7 @@ func ListReferencesHandler(c echo.Context) error { count = int(resCount.Rows[0].Value.(float64)) } - // XXX Some references can contains %2f instead of in the id (legacy), + // XXX Some references can contain `%2f` instead of `/` in the id (legacy), // and to preserve compatibility, we try to find those documents if no // documents with the correct reference are found. if count == 0 && strings.Contains(id, "/") {