From 6d96281f6b1db125f52c7624a011addbba9fa9ac Mon Sep 17 00:00:00 2001 From: Brian Zhang Date: Sat, 24 Sep 2022 22:40:11 +1000 Subject: [PATCH 1/5] edited frontend table to use uuid --- postgres/02-create_frontend_table.sql | 5 ++++- postgres/03-create_person_table.sql | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/postgres/02-create_frontend_table.sql b/postgres/02-create_frontend_table.sql index 2b950751..08ab2f17 100644 --- a/postgres/02-create_frontend_table.sql +++ b/postgres/02-create_frontend_table.sql @@ -1,6 +1,9 @@ /* front end url to id */ DROP TABLE IF EXISTS frontend; CREATE TABLE frontend ( - FrontendID SERIAL PRIMARY KEY, + FrontendID uuid PRIMARY KEY DEFAULT uuid_generate_v4(), FrontendURL VARCHAR(100) + + CONSTRAINT fk_AccessFilesystem FOREIGN KEY (FrontendID) + REFERENCES filesystem(EntityID) ); \ No newline at end of file diff --git a/postgres/03-create_person_table.sql b/postgres/03-create_person_table.sql index 61f1b42a..71329600 100644 --- a/postgres/03-create_person_table.sql +++ b/postgres/03-create_person_table.sql @@ -6,7 +6,7 @@ CREATE TABLE person ( Password CHAR(64) NOT NULL, isOfGroup INT, - frontendid INT, + frontendid uuid PRIMARY KEY DEFAULT uuid_generate_v4(), CONSTRAINT fk_AccessLevel FOREIGN KEY (isOfGroup) REFERENCES groups(UID), From 4edd20d4ae2682286e8387ac2f897da563016c05 Mon Sep 17 00:00:00 2001 From: Brian Zhang Date: Sun, 25 Sep 2022 01:34:54 +1000 Subject: [PATCH 2/5] refactored to support multifrontend uuid --- backend/database/repositories/filesystem.go | 26 +- backend/database/repositories/frontends.go | 24 +- .../repositories/repository_interfaces.go | 5 +- .../repositories/tests/filesystem_test.go | 497 +++++++++--------- backend/go.mod | 1 + backend/go.sum | 2 - postgres/02-create_frontend_table.sql | 6 +- ...ble.sql => 03-create_filesystem_table.sql} | 5 +- ...n_table.sql => 04-create_person_table.sql} | 6 +- postgres/05-create_dummy_data.sql | 58 ++ postgres/06-create_dummy_data.sql | 58 -- 11 files changed, 355 insertions(+), 333 deletions(-) rename postgres/{05-create_filesystem_table.sql => 03-create_filesystem_table.sql} (93%) rename postgres/{03-create_person_table.sql => 04-create_person_table.sql} (91%) create mode 100644 postgres/05-create_dummy_data.sql delete mode 100644 postgres/06-create_dummy_data.sql diff --git a/backend/database/repositories/filesystem.go b/backend/database/repositories/filesystem.go index fa3178ff..a696642d 100644 --- a/backend/database/repositories/filesystem.go +++ b/backend/database/repositories/filesystem.go @@ -51,15 +51,16 @@ func (rep filesystemRepository) query(query string, input ...interface{}) (Files // Returns: entry struct containing the entity that was just created func (rep filesystemRepository) CreateEntry(file FilesystemEntry) (FilesystemEntry, error) { - if file.ParentFileID == FilesystemRootID { - // determine root ID - root, err := rep.GetRoot() - if err != nil { - return FilesystemEntry{}, errors.New("failed to get root") - } + // TODO: I feel like this is useless? + // if file.ParentFileID == FilesystemRootID { + // // determine root ID + // root, err := rep.GetRoot() + // if err != nil { + // return FilesystemEntry{}, errors.New("failed to get root") + // } - file.ParentFileID = root.EntityID - } + // file.ParentFileID = root.EntityID + // } var newID uuid.UUID err := rep.ctx.Query("SELECT new_entity($1, $2, $3, $4)", []interface{}{file.ParentFileID, file.LogicalName, file.OwnerUserId, file.IsDocument}, &newID) @@ -69,18 +70,19 @@ func (rep filesystemRepository) CreateEntry(file FilesystemEntry) (FilesystemEnt return rep.GetEntryWithID(newID) } +// TODO: Change this func (rep filesystemRepository) GetEntryWithID(ID uuid.UUID) (FilesystemEntry, error) { if ID == FilesystemRootID { - return rep.GetRoot() + return rep.GetRoot("A") } result, err := rep.query("SELECT * FROM filesystem WHERE EntityID = $1", ID) return result, err } -func (rep filesystemRepository) GetRoot() (FilesystemEntry, error) { - // Root is currently set to ID 1 - return rep.query("SELECT * FROM filesystem WHERE EntityID = $1", FilesystemRootID) +// TODO: Is this even necessary anymore? frontend table's GetIDWithName does same thing +func (rep filesystemRepository) GetRoot(frontend string) (FilesystemEntry, error) { + return rep.query("SELECT * FROM frontend WHERE FrontendLogicalname = $1", frontend) } func (rep filesystemRepository) GetEntryWithParentID(ID uuid.UUID) (FilesystemEntry, error) { diff --git a/backend/database/repositories/frontends.go b/backend/database/repositories/frontends.go index fda708d8..0086580f 100644 --- a/backend/database/repositories/frontends.go +++ b/backend/database/repositories/frontends.go @@ -1,18 +1,30 @@ package repositories +import "github.com/google/uuid" + type frontendsRepository struct { embeddedContext } -const InvalidFrontend = -1 +var InvalidFrontend = uuid.Nil // GetFrontendFromURL is the implementation of the frontend repository for frontendRepository -func (rep frontendsRepository) GetFrontendFromURL(url string) int { - var frontendId int - err := rep.ctx.Query("SELECT FrontendId from frontend where FrontendUrl = $1;", []interface{}{url}, &frontendId) +func (rep frontendsRepository) GetFrontendFromURL(url string) (uuid.UUID, error) { + var frontendId uuid.UUID + err := rep.ctx.Query("SELECT FrontendID from frontend where FrontendUrl = $1;", []interface{}{url}, &frontendId) if err != nil { - return InvalidFrontend + return InvalidFrontend, err } - return frontendId + return frontendId, nil +} + +// Get FrontendID (uuid) with logical name +func (rep frontendsRepository) GetIDWithName(name string) (uuid.UUID, error) { + var frontendId uuid.UUID + err := rep.ctx.Query("SELECT FrontendID from frontend where FrontendLogicalName = $1;", []interface{}{name}, &frontendId) + if err != nil { + return InvalidFrontend, err + } + return frontendId, nil } diff --git a/backend/database/repositories/repository_interfaces.go b/backend/database/repositories/repository_interfaces.go index a65c1f5f..4822c1a5 100644 --- a/backend/database/repositories/repository_interfaces.go +++ b/backend/database/repositories/repository_interfaces.go @@ -29,7 +29,7 @@ type ( // mocked/real should implement FilesystemRepository interface { GetEntryWithID(ID uuid.UUID) (FilesystemEntry, error) - GetRoot() (FilesystemEntry, error) + GetRoot(frontend string) (FilesystemEntry, error) GetEntryWithParentID(ID uuid.UUID) (FilesystemEntry, error) GetIDWithPath(path string) (uuid.UUID, error) @@ -72,7 +72,8 @@ type ( // repository interface for getting information from the frontend table FrontendsRepository interface { - GetFrontendFromURL(url string) int + GetIDWithName(name string) (uuid.UUID, error) + GetFrontendFromURL(url string) (uuid.UUID, error) } ) diff --git a/backend/database/repositories/tests/filesystem_test.go b/backend/database/repositories/tests/filesystem_test.go index c161baa9..850fd6ff 100644 --- a/backend/database/repositories/tests/filesystem_test.go +++ b/backend/database/repositories/tests/filesystem_test.go @@ -1,275 +1,282 @@ package repositories import ( - "fmt" - "log" "os" "testing" "cms.csesoc.unsw.edu.au/database/contexts" "cms.csesoc.unsw.edu.au/database/repositories" - "github.com/google/uuid" - "github.com/jackc/pgx/v4" + "github.com/gofrs/uuid" "github.com/stretchr/testify/assert" ) var ( - repo = repositories.NewFilesystemRepo() - testContext = repo.GetContext().(*contexts.TestingContext) + repo = repositories.NewFilesystemRepo() + testContext = repo.GetContext().(*contexts.TestingContext) + frontendRepo = repositories.NewFrontendsRepo() ) func TestMain(m *testing.M) { os.Exit(m.Run()) } -func TestRootRetrieval(t *testing.T) { - assert := assert.New(t) - testContext.RunTest(func() { - root, err := repo.GetRoot() - if assert.Nil(err) { - assert.Equal("root", root.LogicalName) - assert.False(root.IsDocument) - assert.GreaterOrEqual(len(root.ChildrenIDs), 0) - } - }) -} - -func TestRootInsert(t *testing.T) { - assert := assert.New(t) - - testContext.RunTest(func() { - // ==== Test setup ==== - root, _ := repo.GetRoot() - - newDir, _ := repo.CreateEntry(repositories.FilesystemEntry{ - LogicalName: "test_directory", ParentFileID: repositories.FilesystemRootID, - OwnerUserId: repositories.GROUPS_ADMIN, IsDocument: false, - }) - - newDoc, _ := repo.CreateEntry(repositories.FilesystemEntry{ - LogicalName: "test_doc", ParentFileID: newDir.EntityID, - OwnerUserId: repositories.GROUPS_ADMIN, IsDocument: true, - }) - - // === Assertions ==== - var docCount int - var dirCount int - - if assert.Nil(testContext.Query("SELECT COUNT(*) FROM filesystem WHERE EntityID = $1", []interface{}{newDir.EntityID}, &dirCount)) { - assert.Equal(dirCount, 1) - } - - if assert.Nil(testContext.Query("SELECT COUNT(*) FROM filesystem WHERE EntityID = $1", []interface{}{newDoc.EntityID}, &docCount)) { - assert.Equal(docCount, 1) - } - - if rows, err := testContext.QueryRow("SELECT EntityID FROM filesystem WHERE Parent = $1", []interface{}{root.EntityID}); assert.Nil(err) { - childrenArr := scanArray[uuid.UUID](rows) - assert.Contains(childrenArr, newDir.EntityID) - } - - if rows, err := testContext.QueryRow("SELECT EntityID FROM filesystem WHERE Parent = $1", []interface{}{newDir.EntityID}); assert.Nil(err) { - childrenArr := scanArray[uuid.UUID](rows) - assert.Contains(childrenArr, newDoc.EntityID) - } - }) -} - -func TestDocumentInfoRetrieval(t *testing.T) { - assert := assert.New(t) - - testContext.RunTest(func() { - // ==== Setup ==== - newDoc, err := repo.CreateEntry(repositories.FilesystemEntry{ - LogicalName: "test_doc", ParentFileID: repositories.FilesystemRootID, - OwnerUserId: repositories.GROUPS_ADMIN, IsDocument: true, - }) - // ==== Assertions ==== - if err != nil { - log.Fatalf(err.Error()) - } - - // Query again for existence in database - if info, err := repo.GetEntryWithID(newDoc.EntityID); assert.Nil(err) { - assert.True(info.IsDocument) - assert.Equal("test_doc", info.LogicalName) - assert.Empty(info.ChildrenIDs) - } - }) -} - -func TestEntityDeletion(t *testing.T) { +func TestFrontendRoot(t *testing.T) { assert := assert.New(t) - - testContext.RunTest(func() { - // ====== Setup ====== - root, _ := repo.GetRoot() - - newDir, _ := repo.CreateEntry(repositories.FilesystemEntry{ - LogicalName: "cool_dir", OwnerUserId: repositories.GROUPS_ADMIN, - ParentFileID: repositories.FilesystemRootID, IsDocument: false, - }) - - newDoc, _ := repo.CreateEntry(repositories.FilesystemEntry{ - LogicalName: "cool_doc", OwnerUserId: repositories.GROUPS_ADMIN, - ParentFileID: newDir.EntityID, IsDocument: true, - }) - - // ====== Assertions ====== - assert.True(testContext.WillFail(func() error { return repo.DeleteEntryWithID(root.EntityID) })) - assert.True(testContext.WillFail(func() error { return repo.DeleteEntryWithID(newDir.EntityID) })) - - assert.Nil(repo.DeleteEntryWithID(newDoc.EntityID)) - info, _ := repo.GetEntryWithID(newDir.EntityID) - assert.NotContains(info.ChildrenIDs, newDoc.EntityID) - assert.Nil(repo.DeleteEntryWithID(newDir.EntityID)) - - root, _ = repo.GetRoot() - assert.NotContains(root.ChildrenIDs, newDir.EntityID) - - // ======= Secondary setup ========== - anotherDirectory, _ := repo.CreateEntry(repositories.FilesystemEntry{ - LogicalName: "cheese", OwnerUserId: repositories.GROUPS_ADMIN, - ParentFileID: repositories.FilesystemRootID, IsDocument: false, - }) - - nestedDirectory, _ := repo.CreateEntry(repositories.FilesystemEntry{ - LogicalName: "cheeseBurger", OwnerUserId: repositories.GROUPS_ADMIN, - ParentFileID: anotherDirectory.EntityID, IsDocument: false, - }) - - file, _ := repo.CreateEntry(repositories.FilesystemEntry{ - LogicalName: "spinach", OwnerUserId: repositories.GROUPS_ADMIN, - ParentFileID: nestedDirectory.EntityID, IsDocument: false, - }) - - // ====== Secondary Assertions ====== - assert.True(testContext.WillFail(func() error { return repo.DeleteEntryWithID(nestedDirectory.EntityID) })) - assert.Nil(repo.DeleteEntryWithID(file.EntityID)) - assert.Nil(repo.DeleteEntryWithID(nestedDirectory.EntityID)) - assert.Nil(repo.DeleteEntryWithID(anotherDirectory.EntityID)) - - root, _ = repo.GetRoot() - assert.NotContains(root.ChildrenIDs, anotherDirectory.EntityID) - }) -} - -func TestEntityRename(t *testing.T) { - assert := assert.New(t) - - getEntity := func(name string, permissions int, parent uuid.UUID, isDocument bool) repositories.FilesystemEntry { - return repositories.FilesystemEntry{ - LogicalName: name, - OwnerUserId: permissions, - ParentFileID: parent, - IsDocument: isDocument, - } - } - testContext.RunTest(func() { - // ===== Test setup ===== - newDir, _ := repo.CreateEntry(getEntity("cool_dir", repositories.GROUPS_ADMIN, repositories.FilesystemRootID, false)) - newDoc, _ := repo.CreateEntry(getEntity("cool_doc", repositories.GROUPS_ADMIN, newDir.EntityID, false)) - newDoc1, _ := repo.CreateEntry(getEntity("cool_doc1", repositories.GROUPS_ADMIN, newDir.EntityID, false)) - newDoc2, _ := repo.CreateEntry(getEntity("cool_doc2", repositories.GROUPS_ADMIN, newDir.EntityID, false)) - - // ===== Assertions ====== - assert.True(testContext.WillFail(func() error { return repo.RenameEntity(newDoc.EntityID, "cool_doc2") })) - assert.True(testContext.WillFail(func() error { return repo.RenameEntity(newDoc1.EntityID, "cool_doc2") })) - assert.True(testContext.WillFail(func() error { return repo.RenameEntity(newDoc2.EntityID, "cool_doc1") })) - - assert.Nil(repo.RenameEntity(newDoc.EntityID, "yabba dabba doo")) - assert.Nil(repo.RenameEntity(newDir.EntityID, "zoinks")) + rootID, _ := frontendRepo.GetIDWithName("A") + assert.NotEqual(rootID, uuid.Nil) }) } -func TestEntityChildren(t *testing.T) { +func TestRootRetrieval(t *testing.T) { assert := assert.New(t) - getEntity := func(name string, permissions int, isDocument bool, parent uuid.UUID) repositories.FilesystemEntry { - return repositories.FilesystemEntry{ - LogicalName: name, - OwnerUserId: permissions, - ParentFileID: parent, - IsDocument: isDocument, - } - } - testContext.RunTest(func() { - // Test setup - dir1, _ := repo.CreateEntry(getEntity("d1", repositories.GROUPS_ADMIN, false, repositories.FilesystemRootID)) - dir2, _ := repo.CreateEntry(getEntity("d2", repositories.GROUPS_ADMIN, false, repositories.FilesystemRootID)) - dir3, _ := repo.CreateEntry(getEntity("d3", repositories.GROUPS_ADMIN, false, repositories.FilesystemRootID)) - dir4, _ := repo.CreateEntry(getEntity("d4", repositories.GROUPS_ADMIN, false, repositories.FilesystemRootID)) - emptyDir, _ := repo.CreateEntry(getEntity("de", repositories.GROUPS_ADMIN, false, repositories.FilesystemRootID)) - - for x := 1; x < 10; x++ { - if x%3 == 0 { - repo.CreateEntry(getEntity("cool_doc"+fmt.Sprint(x), repositories.GROUPS_ADMIN, false, dir1.EntityID)) - } - if x%5 == 0 { - repo.CreateEntry(getEntity("cool_doc"+fmt.Sprint(x), repositories.GROUPS_ADMIN, false, dir2.EntityID)) - } - if x%2 == 0 { - repo.CreateEntry(getEntity("cool_doc"+fmt.Sprint(x), repositories.GROUPS_ADMIN, false, dir3.EntityID)) - } - repo.CreateEntry(getEntity("cool_doc"+fmt.Sprint(x), repositories.GROUPS_ADMIN, false, dir4.EntityID)) - } - - d1_kids, _ := repo.GetEntryWithID(dir1.EntityID) - d2_kids, _ := repo.GetEntryWithID(dir2.EntityID) - d3_kids, _ := repo.GetEntryWithID(dir3.EntityID) - d4_kids, _ := repo.GetEntryWithID(dir4.EntityID) - de_kids, _ := repo.GetEntryWithID(emptyDir.EntityID) - - assert.True(len(d1_kids.ChildrenIDs) == 3) - assert.True(len(d2_kids.ChildrenIDs) == 1) - assert.True(len(d3_kids.ChildrenIDs) == 4) - assert.True(len(d4_kids.ChildrenIDs) == 9) - assert.True(len(de_kids.ChildrenIDs) == 0) - }) -} + rootID, _ := frontendRepo.GetIDWithName("A") + // assert.Nil(err) -func TestGetIDWithPath(t *testing.T) { - assert := assert.New(t) - getEntity := func(name string, permissions int, isDocument bool, parent uuid.UUID) repositories.FilesystemEntry { - return repositories.FilesystemEntry{ - LogicalName: name, - OwnerUserId: permissions, - ParentFileID: parent, - IsDocument: isDocument, - } - } - - testContext.RunTest(func() { - // Test setup - dir1, _ := repo.CreateEntry(getEntity("d1", repositories.GROUPS_ADMIN, false, repositories.FilesystemRootID)) - currentDir := dir1 - for x := 1; x < 3; x++ { - newDir, _ := repo.CreateEntry(getEntity("cool_doc"+fmt.Sprint(x), repositories.GROUPS_ADMIN, false, currentDir.EntityID)) - currentDir = newDir - } - - child2id, _ := repo.GetIDWithPath("/d1/cool_doc1/cool_doc2") - child1id, _ := repo.GetIDWithPath("/d1/cool_doc1") - child2, _ := repo.GetEntryWithID(child2id) - child1, _ := repo.GetEntryWithID(child1id) - _, error1 := repo.GetIDWithPath("/d1/cool_doc2/cool_doc1") - _, error2 := repo.GetIDWithPath("/d1/cool_doc1/cool_doc2/cool_doc1") - - assert.True(error1 != nil) - assert.True(error2 != nil) - assert.True(child1.EntityID == child2.ParentFileID) - assert.True(dir1.EntityID == child1.ParentFileID) + root, _ := repo.GetEntryWithID(rootID) + assert.Equal("A", root.LogicalName) + assert.False(root.IsDocument) + assert.GreaterOrEqual(len(root.ChildrenIDs), 0) }) } -func scanArray[T any](rows pgx.Rows) []T { - arr := []T{} - for rows.Next() { - var x T - rows.Scan(&x) - arr = append(arr, x) - } - return arr -} +// func TestRootInsert(t *testing.T) { +// assert := assert.New(t) + +// testContext.RunTest(func() { +// // ==== Test setup ==== +// root, _ := repo.GetRoot() + +// newDir, _ := repo.CreateEntry(repositories.FilesystemEntry{ +// LogicalName: "test_directory", ParentFileID: repositories.FilesystemRootID, +// OwnerUserId: repositories.GROUPS_ADMIN, IsDocument: false, +// }) + +// newDoc, _ := repo.CreateEntry(repositories.FilesystemEntry{ +// LogicalName: "test_doc", ParentFileID: newDir.EntityID, +// OwnerUserId: repositories.GROUPS_ADMIN, IsDocument: true, +// }) + +// // === Assertions ==== +// var docCount int +// var dirCount int + +// if assert.Nil(testContext.Query("SELECT COUNT(*) FROM filesystem WHERE EntityID = $1", []interface{}{newDir.EntityID}, &dirCount)) { +// assert.Equal(dirCount, 1) +// } + +// if assert.Nil(testContext.Query("SELECT COUNT(*) FROM filesystem WHERE EntityID = $1", []interface{}{newDoc.EntityID}, &docCount)) { +// assert.Equal(docCount, 1) +// } + +// if rows, err := testContext.QueryRow("SELECT EntityID FROM filesystem WHERE Parent = $1", []interface{}{root.EntityID}); assert.Nil(err) { +// childrenArr := scanArray[uuid.UUID](rows) +// assert.Contains(childrenArr, newDir.EntityID) +// } + +// if rows, err := testContext.QueryRow("SELECT EntityID FROM filesystem WHERE Parent = $1", []interface{}{newDir.EntityID}); assert.Nil(err) { +// childrenArr := scanArray[uuid.UUID](rows) +// assert.Contains(childrenArr, newDoc.EntityID) +// } +// }) +// } + +// func TestDocumentInfoRetrieval(t *testing.T) { +// assert := assert.New(t) + +// testContext.RunTest(func() { +// // ==== Setup ==== +// newDoc, err := repo.CreateEntry(repositories.FilesystemEntry{ +// LogicalName: "test_doc", ParentFileID: repositories.FilesystemRootID, +// OwnerUserId: repositories.GROUPS_ADMIN, IsDocument: true, +// }) +// // ==== Assertions ==== +// if err != nil { +// log.Fatalf(err.Error()) +// } + +// // Query again for existence in database +// if info, err := repo.GetEntryWithID(newDoc.EntityID); assert.Nil(err) { +// assert.True(info.IsDocument) +// assert.Equal("test_doc", info.LogicalName) +// assert.Empty(info.ChildrenIDs) +// } +// }) +// } + +// func TestEntityDeletion(t *testing.T) { +// assert := assert.New(t) + +// testContext.RunTest(func() { +// // ====== Setup ====== +// root, _ := repo.GetRoot() + +// newDir, _ := repo.CreateEntry(repositories.FilesystemEntry{ +// LogicalName: "cool_dir", OwnerUserId: repositories.GROUPS_ADMIN, +// ParentFileID: repositories.FilesystemRootID, IsDocument: false, +// }) + +// newDoc, _ := repo.CreateEntry(repositories.FilesystemEntry{ +// LogicalName: "cool_doc", OwnerUserId: repositories.GROUPS_ADMIN, +// ParentFileID: newDir.EntityID, IsDocument: true, +// }) + +// // ====== Assertions ====== +// assert.True(testContext.WillFail(func() error { return repo.DeleteEntryWithID(root.EntityID) })) +// assert.True(testContext.WillFail(func() error { return repo.DeleteEntryWithID(newDir.EntityID) })) + +// assert.Nil(repo.DeleteEntryWithID(newDoc.EntityID)) +// info, _ := repo.GetEntryWithID(newDir.EntityID) +// assert.NotContains(info.ChildrenIDs, newDoc.EntityID) +// assert.Nil(repo.DeleteEntryWithID(newDir.EntityID)) + +// root, _ = repo.GetRoot() +// assert.NotContains(root.ChildrenIDs, newDir.EntityID) + +// // ======= Secondary setup ========== +// anotherDirectory, _ := repo.CreateEntry(repositories.FilesystemEntry{ +// LogicalName: "cheese", OwnerUserId: repositories.GROUPS_ADMIN, +// ParentFileID: repositories.FilesystemRootID, IsDocument: false, +// }) + +// nestedDirectory, _ := repo.CreateEntry(repositories.FilesystemEntry{ +// LogicalName: "cheeseBurger", OwnerUserId: repositories.GROUPS_ADMIN, +// ParentFileID: anotherDirectory.EntityID, IsDocument: false, +// }) + +// file, _ := repo.CreateEntry(repositories.FilesystemEntry{ +// LogicalName: "spinach", OwnerUserId: repositories.GROUPS_ADMIN, +// ParentFileID: nestedDirectory.EntityID, IsDocument: false, +// }) + +// // ====== Secondary Assertions ====== +// assert.True(testContext.WillFail(func() error { return repo.DeleteEntryWithID(nestedDirectory.EntityID) })) +// assert.Nil(repo.DeleteEntryWithID(file.EntityID)) +// assert.Nil(repo.DeleteEntryWithID(nestedDirectory.EntityID)) +// assert.Nil(repo.DeleteEntryWithID(anotherDirectory.EntityID)) + +// root, _ = repo.GetRoot() +// assert.NotContains(root.ChildrenIDs, anotherDirectory.EntityID) +// }) +// } + +// func TestEntityRename(t *testing.T) { +// assert := assert.New(t) + +// getEntity := func(name string, permissions int, parent uuid.UUID, isDocument bool) repositories.FilesystemEntry { +// return repositories.FilesystemEntry{ +// LogicalName: name, +// OwnerUserId: permissions, +// ParentFileID: parent, +// IsDocument: isDocument, +// } +// } + +// testContext.RunTest(func() { +// // ===== Test setup ===== +// newDir, _ := repo.CreateEntry(getEntity("cool_dir", repositories.GROUPS_ADMIN, repositories.FilesystemRootID, false)) +// newDoc, _ := repo.CreateEntry(getEntity("cool_doc", repositories.GROUPS_ADMIN, newDir.EntityID, false)) +// newDoc1, _ := repo.CreateEntry(getEntity("cool_doc1", repositories.GROUPS_ADMIN, newDir.EntityID, false)) +// newDoc2, _ := repo.CreateEntry(getEntity("cool_doc2", repositories.GROUPS_ADMIN, newDir.EntityID, false)) + +// // ===== Assertions ====== +// assert.True(testContext.WillFail(func() error { return repo.RenameEntity(newDoc.EntityID, "cool_doc2") })) +// assert.True(testContext.WillFail(func() error { return repo.RenameEntity(newDoc1.EntityID, "cool_doc2") })) +// assert.True(testContext.WillFail(func() error { return repo.RenameEntity(newDoc2.EntityID, "cool_doc1") })) + +// assert.Nil(repo.RenameEntity(newDoc.EntityID, "yabba dabba doo")) +// assert.Nil(repo.RenameEntity(newDir.EntityID, "zoinks")) +// }) +// } + +// func TestEntityChildren(t *testing.T) { +// assert := assert.New(t) +// getEntity := func(name string, permissions int, isDocument bool, parent uuid.UUID) repositories.FilesystemEntry { +// return repositories.FilesystemEntry{ +// LogicalName: name, +// OwnerUserId: permissions, +// ParentFileID: parent, +// IsDocument: isDocument, +// } +// } + +// testContext.RunTest(func() { +// // Test setup +// dir1, _ := repo.CreateEntry(getEntity("d1", repositories.GROUPS_ADMIN, false, repositories.FilesystemRootID)) +// dir2, _ := repo.CreateEntry(getEntity("d2", repositories.GROUPS_ADMIN, false, repositories.FilesystemRootID)) +// dir3, _ := repo.CreateEntry(getEntity("d3", repositories.GROUPS_ADMIN, false, repositories.FilesystemRootID)) +// dir4, _ := repo.CreateEntry(getEntity("d4", repositories.GROUPS_ADMIN, false, repositories.FilesystemRootID)) +// emptyDir, _ := repo.CreateEntry(getEntity("de", repositories.GROUPS_ADMIN, false, repositories.FilesystemRootID)) + +// for x := 1; x < 10; x++ { +// if x%3 == 0 { +// repo.CreateEntry(getEntity("cool_doc"+fmt.Sprint(x), repositories.GROUPS_ADMIN, false, dir1.EntityID)) +// } +// if x%5 == 0 { +// repo.CreateEntry(getEntity("cool_doc"+fmt.Sprint(x), repositories.GROUPS_ADMIN, false, dir2.EntityID)) +// } +// if x%2 == 0 { +// repo.CreateEntry(getEntity("cool_doc"+fmt.Sprint(x), repositories.GROUPS_ADMIN, false, dir3.EntityID)) +// } +// repo.CreateEntry(getEntity("cool_doc"+fmt.Sprint(x), repositories.GROUPS_ADMIN, false, dir4.EntityID)) +// } + +// d1_kids, _ := repo.GetEntryWithID(dir1.EntityID) +// d2_kids, _ := repo.GetEntryWithID(dir2.EntityID) +// d3_kids, _ := repo.GetEntryWithID(dir3.EntityID) +// d4_kids, _ := repo.GetEntryWithID(dir4.EntityID) +// de_kids, _ := repo.GetEntryWithID(emptyDir.EntityID) + +// assert.True(len(d1_kids.ChildrenIDs) == 3) +// assert.True(len(d2_kids.ChildrenIDs) == 1) +// assert.True(len(d3_kids.ChildrenIDs) == 4) +// assert.True(len(d4_kids.ChildrenIDs) == 9) +// assert.True(len(de_kids.ChildrenIDs) == 0) +// }) +// } + +// func TestGetIDWithPath(t *testing.T) { +// assert := assert.New(t) +// getEntity := func(name string, permissions int, isDocument bool, parent uuid.UUID) repositories.FilesystemEntry { +// return repositories.FilesystemEntry{ +// LogicalName: name, +// OwnerUserId: permissions, +// ParentFileID: parent, +// IsDocument: isDocument, +// } +// } + +// testContext.RunTest(func() { +// // Test setup +// dir1, _ := repo.CreateEntry(getEntity("d1", repositories.GROUPS_ADMIN, false, repositories.FilesystemRootID)) +// currentDir := dir1 +// for x := 1; x < 3; x++ { +// newDir, _ := repo.CreateEntry(getEntity("cool_doc"+fmt.Sprint(x), repositories.GROUPS_ADMIN, false, currentDir.EntityID)) +// currentDir = newDir +// } + +// child2id, _ := repo.GetIDWithPath("/d1/cool_doc1/cool_doc2") +// child1id, _ := repo.GetIDWithPath("/d1/cool_doc1") +// child2, _ := repo.GetEntryWithID(child2id) +// child1, _ := repo.GetEntryWithID(child1id) +// _, error1 := repo.GetIDWithPath("/d1/cool_doc2/cool_doc1") +// _, error2 := repo.GetIDWithPath("/d1/cool_doc1/cool_doc2/cool_doc1") + +// assert.True(error1 != nil) +// assert.True(error2 != nil) +// assert.True(child1.EntityID == child2.ParentFileID) +// assert.True(dir1.EntityID == child1.ParentFileID) +// }) +// } + +// func scanArray[T any](rows pgx.Rows) []T { +// arr := []T{} +// for rows.Next() { +// var x T +// rows.Scan(&x) +// arr = append(arr, x) +// } +// return arr +// } diff --git a/backend/go.mod b/backend/go.mod index b936571d..fe052773 100644 --- a/backend/go.mod +++ b/backend/go.mod @@ -4,6 +4,7 @@ go 1.18 require ( github.com/emirpasic/gods v1.18.1 + github.com/gofrs/uuid v4.0.0+incompatible github.com/golang/mock v1.6.0 github.com/google/uuid v1.3.0 github.com/gorilla/schema v1.2.0 diff --git a/backend/go.sum b/backend/go.sum index cdf53798..c3845f51 100644 --- a/backend/go.sum +++ b/backend/go.sum @@ -239,8 +239,6 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= -github.com/tidwall/gjson v1.14.1 h1:iymTbGkQBhveq21bEvAQ81I0LEBork8BFe1CUZXdyuo= -github.com/tidwall/gjson v1.14.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/gjson v1.14.3 h1:9jvXn7olKEHU1S9vwoMGliaT8jq1vJ7IH/n9zD9Dnlw= github.com/tidwall/gjson v1.14.3/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= diff --git a/postgres/02-create_frontend_table.sql b/postgres/02-create_frontend_table.sql index 08ab2f17..483d95ea 100644 --- a/postgres/02-create_frontend_table.sql +++ b/postgres/02-create_frontend_table.sql @@ -2,8 +2,6 @@ DROP TABLE IF EXISTS frontend; CREATE TABLE frontend ( FrontendID uuid PRIMARY KEY DEFAULT uuid_generate_v4(), - FrontendURL VARCHAR(100) - - CONSTRAINT fk_AccessFilesystem FOREIGN KEY (FrontendID) - REFERENCES filesystem(EntityID) + FrontendLogicalName VARCHAR(100), + FrontendURL VARCHAR(100), ); \ No newline at end of file diff --git a/postgres/05-create_filesystem_table.sql b/postgres/03-create_filesystem_table.sql similarity index 93% rename from postgres/05-create_filesystem_table.sql rename to postgres/03-create_filesystem_table.sql index 53e7a23d..fa292aaf 100644 --- a/postgres/05-create_filesystem_table.sql +++ b/postgres/03-create_filesystem_table.sql @@ -22,7 +22,10 @@ CREATE TABLE filesystem ( REFERENCES groups(UID), /* Unique name constraint: there should not exist an entity of the same type with the same parent and logical name. */ - CONSTRAINT unique_name UNIQUE (Parent, LogicalName, IsDocument) + CONSTRAINT unique_name UNIQUE (Parent, LogicalName, IsDocument), + /* Frontend Root ID is derived first and then referenced here */ + CONSTRAINT fk_AccessFrontend FOREIGN KEY (EntityID) + REFERENCES frontend(FrontendID) ); /* Utility procedure :) */ diff --git a/postgres/03-create_person_table.sql b/postgres/04-create_person_table.sql similarity index 91% rename from postgres/03-create_person_table.sql rename to postgres/04-create_person_table.sql index 71329600..0f2f2ed3 100644 --- a/postgres/03-create_person_table.sql +++ b/postgres/04-create_person_table.sql @@ -20,11 +20,11 @@ CREATE TABLE person ( /* create user function plpgsql */ DROP FUNCTION IF EXISTS create_normal_user; -CREATE OR REPLACE FUNCTION create_normal_user (email VARCHAR, name VARCHAR, password VARCHAR, frontendID INT) RETURNS void +CREATE OR REPLACE FUNCTION create_normal_user (email VARCHAR, name VARCHAR, password VARCHAR, frontendID uuid) RETURNS void LANGUAGE plpgsql AS $$ DECLARE BEGIN - INSERT INTO person (Email, First_name, Password, isOfGroup, frontendID) - VALUES (email, name, encode(sha256(password::BYTEA), 'hex'), 2, 1); + INSERT INTO person (Email, First_name, Password, isOfGroup, frontendid) + VALUES (email, name, encode(sha256(password::BYTEA), 'hex'), 2, frontendID); END $$; \ No newline at end of file diff --git a/postgres/05-create_dummy_data.sql b/postgres/05-create_dummy_data.sql new file mode 100644 index 00000000..3f03b9ab --- /dev/null +++ b/postgres/05-create_dummy_data.sql @@ -0,0 +1,58 @@ +SET timezone = 'Australia/Sydney'; + +/* Create default groups */ +INSERT INTO groups (Name, Permission) VALUES ('adminA', 'delete'); +INSERT INTO groups (name, Permission) VALUES ('userA', 'write'); + +/* create a dummy frontend called A */ +INSERT INTO frontend (FrontendLogicalName, FrontendURL) VALUES ('A'::VARCHAR, 'http://localhost:8080'::VARCHAR); + +/* Setup FS table and modify constraints */ +/* Insert root directory and then add our constraints */ +DO $$ +DECLARE + randomGroup groups.UID%type; + rootID filesystem.EntityID%type; +BEGIN + SELECT frontend.FrontendID INTO rootID FROM frontend WHERE FrontendLogicalName = 'A'::VARCHAR; + SELECT groups.UID INTO randomGroup FROM groups WHERE Name = 'adminA'::VARCHAR; + /* Insert the root directory */ + INSERT INTO filesystem (EntityID, LogicalName, OwnedBy, Parent) + VALUES (rootID, 'Folder0', randomGroup, uuid_nil()); + + -- /* Set parent to uuid_nil() because postgres driver has issue supporting NULL values */ + -- UPDATE filesystem SET Parent = uuid_nil() WHERE EntityID = rootID; + + /* insert "has parent" constraint*/ + EXECUTE 'ALTER TABLE filesystem + ADD CONSTRAINT has_parent CHECK (Parent != NULL)'; +END $$; + + +-- /* Insert dummy data */ +-- DO $$ +-- DECLARE +-- rootID filesystem.EntityID%type; +-- newEntity filesystem.EntityID%type; +-- wasPopping filesystem.EntityID%type; +-- oldEntity filesystem.EntityID%type; +-- BEGIN +-- SELECT frontend.FrontendID INTO rootID FROM frontend WHERE FrontendLogicalName = 'A'::VARCHAR; + +-- newEntity := (SELECT new_entity(rootID, 'downloads'::VARCHAR, 1, false)); +-- oldEntity := (SELECT new_entity(rootID, 'documents'::VARCHAR, 1, false)); + +-- wasPopping := (SELECT new_entity(oldEntity, 'cool_document'::VARCHAR, 1, true)); +-- wasPopping := (SELECT new_entity(oldEntity, 'cool_document_round_2'::VARCHAR, 1, true)); +-- PERFORM delete_entity(wasPopping); +-- wasPopping := (SELECT new_entity(oldEntity, 'cool_document_round_2'::VARCHAR, 1, true)); +-- END $$; + + +-- /* inserting two accounts into db */ +-- DO LANGUAGE plpgsql $$ +-- BEGIN +-- EXECUTE create_normal_user('z0000000@ad.unsw.edu.au', 'adam', 'password', 1); +-- EXECUTE create_normal_user('john.smith@gmail.com', 'john', 'password', 1); +-- EXECUTE create_normal_user('jane.doe@gmail.com', 'jane', 'password', 1); +-- END $$; \ No newline at end of file diff --git a/postgres/06-create_dummy_data.sql b/postgres/06-create_dummy_data.sql deleted file mode 100644 index ecc43899..00000000 --- a/postgres/06-create_dummy_data.sql +++ /dev/null @@ -1,58 +0,0 @@ -SET timezone = 'Australia/Sydney'; - -/* Create default groups */ -INSERT INTO groups (Name, Permission) VALUES ('admin', 'delete'); -INSERT INTO groups (name, Permission) VALUES ('user', 'write'); - -/* Setup FS table and modify constraints */ -/* Insert root directory and then add our constraints */ -DO $$ -DECLARE - randomGroup groups.UID%type; - rootID filesystem.EntityID%type; -BEGIN - SELECT groups.UID INTO randomGroup FROM groups WHERE Name = 'admin'::VARCHAR; - /* Insert the root directory */ - INSERT INTO filesystem (EntityID, LogicalName, OwnedBy) - VALUES (uuid_nil(), 'root', randomGroup); - SELECT filesystem.EntityID INTO rootID FROM filesystem WHERE LogicalName = 'root'::VARCHAR; - /* Set parent to uuid_nil() because postgres driver has issue supporting NULL values */ - UPDATE filesystem SET Parent = uuid_nil() WHERE EntityID = rootID; - - /* insert "has parent" constraint*/ - EXECUTE 'ALTER TABLE filesystem - ADD CONSTRAINT has_parent CHECK (Parent != NULL)'; -END $$; - - - -/* create a dummy frontend */ -INSERT INTO frontend (FrontendURL) VALUES ('http://localhost:8080'::VARCHAR); - -/* Insert dummy data */ -DO $$ -DECLARE - rootID filesystem.EntityID%type; - newEntity filesystem.EntityID%type; - wasPopping filesystem.EntityID%type; - oldEntity filesystem.EntityID%type; -BEGIN - SELECT filesystem.EntityID INTO rootID FROM filesystem WHERE EntityID = uuid_nil(); - - newEntity := (SELECT new_entity(rootID, 'downloads'::VARCHAR, 1, false)); - oldEntity := (SELECT new_entity(rootID, 'documents'::VARCHAR, 1, false)); - - wasPopping := (SELECT new_entity(oldEntity, 'cool_document'::VARCHAR, 1, true)); - wasPopping := (SELECT new_entity(oldEntity, 'cool_document_round_2'::VARCHAR, 1, true)); - PERFORM delete_entity(wasPopping); - wasPopping := (SELECT new_entity(oldEntity, 'cool_document_round_2'::VARCHAR, 1, true)); -END $$; - - -/* inserting two accounts into db */ -DO LANGUAGE plpgsql $$ -BEGIN - EXECUTE create_normal_user('z0000000@ad.unsw.edu.au', 'adam', 'password', 1); - EXECUTE create_normal_user('john.smith@gmail.com', 'john', 'password', 1); - EXECUTE create_normal_user('jane.doe@gmail.com', 'jane', 'password', 1); -END $$; \ No newline at end of file From 655b2dbeb1155cbbe44739fb31c962c18ed98942 Mon Sep 17 00:00:00 2001 From: Brian Zhang Date: Sun, 25 Sep 2022 01:42:45 +1000 Subject: [PATCH 3/5] fixed old int expectations --- backend/database/repositories/main.go | 3 ++- backend/database/repositories/person.go | 4 +++- backend/endpoints/dependency_factory.go | 3 ++- backend/endpoints/main.go | 5 +++-- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/backend/database/repositories/main.go b/backend/database/repositories/main.go index 4b859cac..f983466d 100644 --- a/backend/database/repositories/main.go +++ b/backend/database/repositories/main.go @@ -4,6 +4,7 @@ import ( "sync" "cms.csesoc.unsw.edu.au/database/contexts" + "github.com/google/uuid" ) // Start up a database connection with a provided context @@ -36,7 +37,7 @@ func NewFrontendsRepo() FrontendsRepository { } // NewPersonRepo instantiates a new person repository -func NewPersonRepo(frontendId int) PersonRepository { +func NewPersonRepo(frontendId uuid.UUID) PersonRepository { return personRepository{ frontendId, embeddedContext{getContext()}, diff --git a/backend/database/repositories/person.go b/backend/database/repositories/person.go index ac7281cb..4330320f 100644 --- a/backend/database/repositories/person.go +++ b/backend/database/repositories/person.go @@ -6,11 +6,13 @@ package repositories import ( "log" + + "github.com/google/uuid" ) // Implements IPersonRepository type personRepository struct { - frontEndID int + frontEndID uuid.UUID embeddedContext } diff --git a/backend/endpoints/dependency_factory.go b/backend/endpoints/dependency_factory.go index 58e8b392..adb4ef4f 100644 --- a/backend/endpoints/dependency_factory.go +++ b/backend/endpoints/dependency_factory.go @@ -5,6 +5,7 @@ package endpoints import ( repos "cms.csesoc.unsw.edu.au/database/repositories" "cms.csesoc.unsw.edu.au/internal/logger" + "github.com/google/uuid" ) type ( @@ -25,7 +26,7 @@ type ( // DependencyProvider is a simple implementation of the dependency factory that supports the injection of "dynamic" dependencies DependencyProvider struct { Log *logger.Log - FrontEndID int + FrontEndID uuid.UUID } ) diff --git a/backend/endpoints/main.go b/backend/endpoints/main.go index 758833b7..54970493 100644 --- a/backend/endpoints/main.go +++ b/backend/endpoints/main.go @@ -8,6 +8,7 @@ import ( "cms.csesoc.unsw.edu.au/database/repositories" "cms.csesoc.unsw.edu.au/internal/logger" "cms.csesoc.unsw.edu.au/internal/session" + "github.com/google/uuid" ) // Basic organization of a response we will receive from the API @@ -65,7 +66,7 @@ func (fn handler[T, V]) ServeHTTP(w http.ResponseWriter, r *http.Request) { } // acquire the frontend ID and error out if the client isn't registered to use the CMS - frontendId := getFrontendId(r) + frontendId, _ := getFrontendId(r) if frontendId == repositories.InvalidFrontend { writeResponse(w, handlerResponse[empty]{ Status: http.StatusUnauthorized, @@ -116,7 +117,7 @@ func (fn rawHandler[T, V]) ServeHTTP(w http.ResponseWriter, r *http.Request) { } // getFrontendID gets the frontend id for an incoming http request -func getFrontendId(r *http.Request) int { +func getFrontendId(r *http.Request) (uuid.UUID, error) { frontendRepo := repositories.NewFrontendsRepo() return frontendRepo.GetFrontendFromURL(r.URL.Host) } From 5b15de8be607333341420829efaeff666f5640a6 Mon Sep 17 00:00:00 2001 From: Brian Zhang Date: Sun, 25 Sep 2022 02:05:50 +1000 Subject: [PATCH 4/5] merged to main --- .../repositories/tests/filesystem_test.go | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/backend/database/repositories/tests/filesystem_test.go b/backend/database/repositories/tests/filesystem_test.go index 850fd6ff..33335055 100644 --- a/backend/database/repositories/tests/filesystem_test.go +++ b/backend/database/repositories/tests/filesystem_test.go @@ -24,21 +24,25 @@ func TestMain(m *testing.M) { func TestFrontendRoot(t *testing.T) { assert := assert.New(t) testContext.RunTest(func() { - rootID, _ := frontendRepo.GetIDWithName("A") - assert.NotEqual(rootID, uuid.Nil) + rootID, err := frontendRepo.GetIDWithName("A") + if assert.Nil(err) { + assert.NotEqual(rootID, uuid.Nil) + } }) } func TestRootRetrieval(t *testing.T) { assert := assert.New(t) testContext.RunTest(func() { - rootID, _ := frontendRepo.GetIDWithName("A") - // assert.Nil(err) - - root, _ := repo.GetEntryWithID(rootID) - assert.Equal("A", root.LogicalName) - assert.False(root.IsDocument) - assert.GreaterOrEqual(len(root.ChildrenIDs), 0) + rootID, err := frontendRepo.GetIDWithName("A") + assert.Nil(err) + + root, err := repo.GetEntryWithID(rootID) + if assert.Nil(err) { + assert.Equal("A", root.LogicalName) + assert.False(root.IsDocument) + assert.GreaterOrEqual(len(root.ChildrenIDs), 0) + } }) } From 62a40aa040dc6fbc9a2c17e61a9d7c8ed2a79923 Mon Sep 17 00:00:00 2001 From: Brian Zhang Date: Sun, 25 Sep 2022 17:08:16 +1000 Subject: [PATCH 5/5] working frontend table refactor with tests --- backend/database/repositories/filesystem.go | 7 -- .../repositories/tests/filesystem_test.go | 52 ++++++++++++-- postgres/01-create_groups_table.sql | 3 +- postgres/02-create_frontend_table.sql | 2 +- postgres/03-create_filesystem_table.sql | 17 +++-- postgres/04-create_person_table.sql | 2 +- postgres/05-create_dummy_data.sql | 67 ++++++++++--------- 7 files changed, 91 insertions(+), 59 deletions(-) diff --git a/backend/database/repositories/filesystem.go b/backend/database/repositories/filesystem.go index a696642d..8cabb0be 100644 --- a/backend/database/repositories/filesystem.go +++ b/backend/database/repositories/filesystem.go @@ -13,9 +13,6 @@ type filesystemRepository struct { embeddedContext } -// The ID for root, set this as the ID in a specified request -var FilesystemRootID uuid.UUID = uuid.Nil - // We really should use an ORM jesus this is ugly func (rep filesystemRepository) query(query string, input ...interface{}) (FilesystemEntry, error) { entity := FilesystemEntry{} @@ -72,10 +69,6 @@ func (rep filesystemRepository) CreateEntry(file FilesystemEntry) (FilesystemEnt // TODO: Change this func (rep filesystemRepository) GetEntryWithID(ID uuid.UUID) (FilesystemEntry, error) { - if ID == FilesystemRootID { - return rep.GetRoot("A") - } - result, err := rep.query("SELECT * FROM filesystem WHERE EntityID = $1", ID) return result, err } diff --git a/backend/database/repositories/tests/filesystem_test.go b/backend/database/repositories/tests/filesystem_test.go index 33335055..6c42a01b 100644 --- a/backend/database/repositories/tests/filesystem_test.go +++ b/backend/database/repositories/tests/filesystem_test.go @@ -2,12 +2,13 @@ package repositories import ( "os" + "sort" "testing" "cms.csesoc.unsw.edu.au/database/contexts" "cms.csesoc.unsw.edu.au/database/repositories" - "github.com/gofrs/uuid" + "github.com/google/uuid" "github.com/stretchr/testify/assert" ) @@ -34,15 +35,52 @@ func TestFrontendRoot(t *testing.T) { func TestRootRetrieval(t *testing.T) { assert := assert.New(t) testContext.RunTest(func() { - rootID, err := frontendRepo.GetIDWithName("A") - assert.Nil(err) + rootID, _ := frontendRepo.GetIDWithName("A") root, err := repo.GetEntryWithID(rootID) - if assert.Nil(err) { - assert.Equal("A", root.LogicalName) - assert.False(root.IsDocument) - assert.GreaterOrEqual(len(root.ChildrenIDs), 0) + assert.Nil(err) + + assert.Equal("A", root.LogicalName) + assert.False(root.IsDocument) + assert.GreaterOrEqual(len(root.ChildrenIDs), 2) + + /* TEST DATA HIERARCHY + (A) + / \ + (documents) (downloads) + / \ + (cool_document) (cool_document_round_2) + + */ + expected := []string{"downloads", "documents"} + var actual []string + documents := uuid.Nil + for _, child := range root.ChildrenIDs { + file, err := repo.GetEntryWithID(child) + assert.Nil(err) + filename := file.LogicalName + if filename == "documents" { + documents = file.EntityID + } + actual = append(actual, filename) + } + sort.Strings(expected) + sort.Strings(actual) + assert.Equal(expected, actual) + + documents_folder, err := repo.GetEntryWithID(documents) + assert.Nil(err) + + expected = []string{"cool_document", "cool_document_round_2"} + actual = []string{} + for _, leaf := range documents_folder.ChildrenIDs { + document, err := repo.GetEntryWithID(leaf) + assert.Nil(err) + actual = append(actual, document.LogicalName) } + sort.Strings(expected) + sort.Strings(actual) + assert.Equal(expected, actual) }) } diff --git a/postgres/01-create_groups_table.sql b/postgres/01-create_groups_table.sql index 95934cd4..deea0393 100644 --- a/postgres/01-create_groups_table.sql +++ b/postgres/01-create_groups_table.sql @@ -1,4 +1,5 @@ -CREATE EXTENSION hstore; +CREATE EXTENSION IF NOT EXISTS hstore; +CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; SET timezone = 'Australia/Sydney'; CREATE TYPE permissions_enum as ENUM ('read', 'write', 'delete'); diff --git a/postgres/02-create_frontend_table.sql b/postgres/02-create_frontend_table.sql index 483d95ea..886fb432 100644 --- a/postgres/02-create_frontend_table.sql +++ b/postgres/02-create_frontend_table.sql @@ -3,5 +3,5 @@ DROP TABLE IF EXISTS frontend; CREATE TABLE frontend ( FrontendID uuid PRIMARY KEY DEFAULT uuid_generate_v4(), FrontendLogicalName VARCHAR(100), - FrontendURL VARCHAR(100), + FrontendURL VARCHAR(100) ); \ No newline at end of file diff --git a/postgres/03-create_filesystem_table.sql b/postgres/03-create_filesystem_table.sql index fa292aaf..d050735c 100644 --- a/postgres/03-create_filesystem_table.sql +++ b/postgres/03-create_filesystem_table.sql @@ -1,6 +1,3 @@ -SET timezone = 'Australia/Sydney'; -CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; - /** The filesystem table models all file heirachies in our system **/ @@ -14,18 +11,20 @@ CREATE TABLE filesystem ( CreatedAt TIMESTAMP NOT NULL DEFAULT NOW(), OwnedBy INT, - /* Pain */ - Parent uuid REFERENCES filesystem(EntityID) DEFAULT NULL, + /** + If parent is uuid_nil(), it is a frontend root. + Also note that neither entityID and parent are foreign keys to frontend table since + keys must always exist in frontend table. This violates the purpose of it to only + store frontend roots. + **/ + Parent uuid DEFAULT uuid_nil(), /* FK Constraint */ CONSTRAINT fk_owner FOREIGN KEY (OwnedBy) REFERENCES groups(UID), /* Unique name constraint: there should not exist an entity of the same type with the same parent and logical name. */ - CONSTRAINT unique_name UNIQUE (Parent, LogicalName, IsDocument), - /* Frontend Root ID is derived first and then referenced here */ - CONSTRAINT fk_AccessFrontend FOREIGN KEY (EntityID) - REFERENCES frontend(FrontendID) + CONSTRAINT unique_name UNIQUE (Parent, LogicalName, IsDocument) ); /* Utility procedure :) */ diff --git a/postgres/04-create_person_table.sql b/postgres/04-create_person_table.sql index 0f2f2ed3..e80840eb 100644 --- a/postgres/04-create_person_table.sql +++ b/postgres/04-create_person_table.sql @@ -6,7 +6,7 @@ CREATE TABLE person ( Password CHAR(64) NOT NULL, isOfGroup INT, - frontendid uuid PRIMARY KEY DEFAULT uuid_generate_v4(), + frontendid uuid DEFAULT uuid_generate_v4(), CONSTRAINT fk_AccessLevel FOREIGN KEY (isOfGroup) REFERENCES groups(UID), diff --git a/postgres/05-create_dummy_data.sql b/postgres/05-create_dummy_data.sql index 3f03b9ab..1ef1b604 100644 --- a/postgres/05-create_dummy_data.sql +++ b/postgres/05-create_dummy_data.sql @@ -1,9 +1,10 @@ -SET timezone = 'Australia/Sydney'; - /* Create default groups */ INSERT INTO groups (Name, Permission) VALUES ('adminA', 'delete'); INSERT INTO groups (name, Permission) VALUES ('userA', 'write'); +/* create a god root node */ +INSERT INTO frontend (FrontendID, FrontendLogicalName, FrontendURL) VALUES (uuid_nil(), 'God'::VARCHAR, ''::VARCHAR); + /* create a dummy frontend called A */ INSERT INTO frontend (FrontendLogicalName, FrontendURL) VALUES ('A'::VARCHAR, 'http://localhost:8080'::VARCHAR); @@ -18,41 +19,41 @@ BEGIN SELECT groups.UID INTO randomGroup FROM groups WHERE Name = 'adminA'::VARCHAR; /* Insert the root directory */ INSERT INTO filesystem (EntityID, LogicalName, OwnedBy, Parent) - VALUES (rootID, 'Folder0', randomGroup, uuid_nil()); - - -- /* Set parent to uuid_nil() because postgres driver has issue supporting NULL values */ - -- UPDATE filesystem SET Parent = uuid_nil() WHERE EntityID = rootID; + VALUES (rootID, 'A', randomGroup, uuid_nil()); - /* insert "has parent" constraint*/ + /* insert "has parent" constraint */ EXECUTE 'ALTER TABLE filesystem ADD CONSTRAINT has_parent CHECK (Parent != NULL)'; END $$; --- /* Insert dummy data */ --- DO $$ --- DECLARE --- rootID filesystem.EntityID%type; --- newEntity filesystem.EntityID%type; --- wasPopping filesystem.EntityID%type; --- oldEntity filesystem.EntityID%type; --- BEGIN --- SELECT frontend.FrontendID INTO rootID FROM frontend WHERE FrontendLogicalName = 'A'::VARCHAR; +/* Insert dummy data */ +DO $$ +DECLARE + rootID filesystem.EntityID%type; + newEntity filesystem.EntityID%type; + wasPopping filesystem.EntityID%type; + oldEntity filesystem.EntityID%type; +BEGIN + SELECT frontend.FrontendID INTO rootID FROM frontend WHERE FrontendLogicalName = 'A'::VARCHAR; --- newEntity := (SELECT new_entity(rootID, 'downloads'::VARCHAR, 1, false)); --- oldEntity := (SELECT new_entity(rootID, 'documents'::VARCHAR, 1, false)); - --- wasPopping := (SELECT new_entity(oldEntity, 'cool_document'::VARCHAR, 1, true)); --- wasPopping := (SELECT new_entity(oldEntity, 'cool_document_round_2'::VARCHAR, 1, true)); --- PERFORM delete_entity(wasPopping); --- wasPopping := (SELECT new_entity(oldEntity, 'cool_document_round_2'::VARCHAR, 1, true)); --- END $$; - - --- /* inserting two accounts into db */ --- DO LANGUAGE plpgsql $$ --- BEGIN --- EXECUTE create_normal_user('z0000000@ad.unsw.edu.au', 'adam', 'password', 1); --- EXECUTE create_normal_user('john.smith@gmail.com', 'john', 'password', 1); --- EXECUTE create_normal_user('jane.doe@gmail.com', 'jane', 'password', 1); --- END $$; \ No newline at end of file + newEntity := (SELECT new_entity(rootID, 'downloads'::VARCHAR, 1, false)); + oldEntity := (SELECT new_entity(rootID, 'documents'::VARCHAR, 1, false)); + + wasPopping := (SELECT new_entity(oldEntity, 'cool_document'::VARCHAR, 1, true)); + wasPopping := (SELECT new_entity(oldEntity, 'cool_document_round_2'::VARCHAR, 1, true)); + PERFORM delete_entity(wasPopping); + wasPopping := (SELECT new_entity(oldEntity, 'cool_document_round_2'::VARCHAR, 1, true)); +END $$; + + +/* inserting three accounts into db */ +DO LANGUAGE plpgsql $$ +DECLARE + rootID filesystem.EntityID%type; +BEGIN + SELECT frontend.FrontendID INTO rootID FROM frontend WHERE FrontendLogicalName = 'A'::VARCHAR; + EXECUTE create_normal_user('z0000000@ad.unsw.edu.au', 'adam', 'password', rootID); + EXECUTE create_normal_user('john.smith@gmail.com', 'john', 'password', rootID); + EXECUTE create_normal_user('jane.doe@gmail.com', 'jane', 'password', rootID); +END $$; \ No newline at end of file