From 3ef8b3030a185ed83f2d023b2e8f3165e1a205c2 Mon Sep 17 00:00:00 2001 From: Jiadong Bai Date: Fri, 9 Feb 2024 17:25:33 -0800 Subject: [PATCH 01/14] add dataset schema and fs service --- .../web/model/jooq/generated/Indexes.java | 19 + .../texera/web/model/jooq/generated/Keys.java | 24 ++ .../web/model/jooq/generated/Tables.java | 18 + .../web/model/jooq/generated/TexeraDb.java | 23 +- .../enums/DatasetUserAccessPrivilege.java | 49 +++ .../model/jooq/generated/tables/Dataset.java | 189 +++++++++ .../generated/tables/DatasetUserAccess.java | 167 ++++++++ .../jooq/generated/tables/DatasetVersion.java | 184 +++++++++ .../generated/tables/daos/DatasetDao.java | 147 +++++++ .../tables/daos/DatasetUserAccessDao.java | 85 ++++ .../tables/daos/DatasetVersionDao.java | 133 +++++++ .../generated/tables/interfaces/IDataset.java | 102 +++++ .../tables/interfaces/IDatasetUserAccess.java | 63 +++ .../tables/interfaces/IDatasetVersion.java | 92 +++++ .../jooq/generated/tables/pojos/Dataset.java | 166 ++++++++ .../tables/pojos/DatasetUserAccess.java | 101 +++++ .../tables/pojos/DatasetVersion.java | 150 +++++++ .../tables/records/DatasetRecord.java | 368 ++++++++++++++++++ .../records/DatasetUserAccessRecord.java | 207 ++++++++++ .../tables/records/DatasetVersionRecord.java | 328 ++++++++++++++++ .../user/dataset/DatasetResource.scala | 5 + ...VersionControlLocalFileStorageService.java | 238 +++++++++++ core/scripts/sql/texera_ddl.sql | 42 ++ core/scripts/sql/update/07.sql | 41 ++ 24 files changed, 2940 insertions(+), 1 deletion(-) create mode 100644 core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/enums/DatasetUserAccessPrivilege.java create mode 100644 core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/tables/Dataset.java create mode 100644 core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/tables/DatasetUserAccess.java create mode 100644 core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/tables/DatasetVersion.java create mode 100644 core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/tables/daos/DatasetDao.java create mode 100644 core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/tables/daos/DatasetUserAccessDao.java create mode 100644 core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/tables/daos/DatasetVersionDao.java create mode 100644 core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/tables/interfaces/IDataset.java create mode 100644 core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/tables/interfaces/IDatasetUserAccess.java create mode 100644 core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/tables/interfaces/IDatasetVersion.java create mode 100644 core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/tables/pojos/Dataset.java create mode 100644 core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/tables/pojos/DatasetUserAccess.java create mode 100644 core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/tables/pojos/DatasetVersion.java create mode 100644 core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/tables/records/DatasetRecord.java create mode 100644 core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/tables/records/DatasetUserAccessRecord.java create mode 100644 core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/tables/records/DatasetVersionRecord.java create mode 100644 core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/DatasetResource.scala create mode 100644 core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/service/GitVersionControlLocalFileStorageService.java create mode 100644 core/scripts/sql/update/07.sql diff --git a/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/Indexes.java b/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/Indexes.java index e12fe67f2cc..d2ca0386055 100644 --- a/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/Indexes.java +++ b/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/Indexes.java @@ -4,6 +4,9 @@ package edu.uci.ics.texera.web.model.jooq.generated; +import edu.uci.ics.texera.web.model.jooq.generated.tables.Dataset; +import edu.uci.ics.texera.web.model.jooq.generated.tables.DatasetUserAccess; +import edu.uci.ics.texera.web.model.jooq.generated.tables.DatasetVersion; import edu.uci.ics.texera.web.model.jooq.generated.tables.File; import edu.uci.ics.texera.web.model.jooq.generated.tables.FileOfProject; import edu.uci.ics.texera.web.model.jooq.generated.tables.FileOfWorkflow; @@ -36,6 +39,14 @@ public class Indexes { // INDEX definitions // ------------------------------------------------------------------------- + public static final Index DATASET_IDX_DATASET_NAME_DESCRIPTION = Indexes0.DATASET_IDX_DATASET_NAME_DESCRIPTION; + public static final Index DATASET_OWNER_UID = Indexes0.DATASET_OWNER_UID; + public static final Index DATASET_PRIMARY = Indexes0.DATASET_PRIMARY; + public static final Index DATASET_USER_ACCESS_PRIMARY = Indexes0.DATASET_USER_ACCESS_PRIMARY; + public static final Index DATASET_USER_ACCESS_UID = Indexes0.DATASET_USER_ACCESS_UID; + public static final Index DATASET_VERSION_DID = Indexes0.DATASET_VERSION_DID; + public static final Index DATASET_VERSION_IDX_DATASET_VERSION_NAME = Indexes0.DATASET_VERSION_IDX_DATASET_VERSION_NAME; + public static final Index DATASET_VERSION_PRIMARY = Indexes0.DATASET_VERSION_PRIMARY; public static final Index FILE_IDX_FILE_NAME_DESCRIPTION = Indexes0.FILE_IDX_FILE_NAME_DESCRIPTION; public static final Index FILE_OWNER_UID = Indexes0.FILE_OWNER_UID; public static final Index FILE_PRIMARY = Indexes0.FILE_PRIMARY; @@ -77,6 +88,14 @@ public class Indexes { // ------------------------------------------------------------------------- private static class Indexes0 { + public static Index DATASET_IDX_DATASET_NAME_DESCRIPTION = Internal.createIndex("idx_dataset_name_description", Dataset.DATASET, new OrderField[] { Dataset.DATASET.NAME, Dataset.DATASET.DESCRIPTION }, false); + public static Index DATASET_OWNER_UID = Internal.createIndex("owner_uid", Dataset.DATASET, new OrderField[] { Dataset.DATASET.OWNER_UID }, false); + public static Index DATASET_PRIMARY = Internal.createIndex("PRIMARY", Dataset.DATASET, new OrderField[] { Dataset.DATASET.DID }, true); + public static Index DATASET_USER_ACCESS_PRIMARY = Internal.createIndex("PRIMARY", DatasetUserAccess.DATASET_USER_ACCESS, new OrderField[] { DatasetUserAccess.DATASET_USER_ACCESS.DID, DatasetUserAccess.DATASET_USER_ACCESS.UID }, true); + public static Index DATASET_USER_ACCESS_UID = Internal.createIndex("uid", DatasetUserAccess.DATASET_USER_ACCESS, new OrderField[] { DatasetUserAccess.DATASET_USER_ACCESS.UID }, false); + public static Index DATASET_VERSION_DID = Internal.createIndex("did", DatasetVersion.DATASET_VERSION, new OrderField[] { DatasetVersion.DATASET_VERSION.DID }, false); + public static Index DATASET_VERSION_IDX_DATASET_VERSION_NAME = Internal.createIndex("idx_dataset_version_name", DatasetVersion.DATASET_VERSION, new OrderField[] { DatasetVersion.DATASET_VERSION.NAME }, false); + public static Index DATASET_VERSION_PRIMARY = Internal.createIndex("PRIMARY", DatasetVersion.DATASET_VERSION, new OrderField[] { DatasetVersion.DATASET_VERSION.DVID }, true); public static Index FILE_IDX_FILE_NAME_DESCRIPTION = Internal.createIndex("idx_file_name_description", File.FILE, new OrderField[] { File.FILE.NAME, File.FILE.DESCRIPTION }, false); public static Index FILE_OWNER_UID = Internal.createIndex("owner_uid", File.FILE, new OrderField[] { File.FILE.OWNER_UID, File.FILE.NAME }, true); public static Index FILE_PRIMARY = Internal.createIndex("PRIMARY", File.FILE, new OrderField[] { File.FILE.FID }, true); diff --git a/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/Keys.java b/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/Keys.java index 181307949b7..7d265f769f8 100644 --- a/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/Keys.java +++ b/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/Keys.java @@ -4,6 +4,9 @@ package edu.uci.ics.texera.web.model.jooq.generated; +import edu.uci.ics.texera.web.model.jooq.generated.tables.Dataset; +import edu.uci.ics.texera.web.model.jooq.generated.tables.DatasetUserAccess; +import edu.uci.ics.texera.web.model.jooq.generated.tables.DatasetVersion; import edu.uci.ics.texera.web.model.jooq.generated.tables.File; import edu.uci.ics.texera.web.model.jooq.generated.tables.FileOfProject; import edu.uci.ics.texera.web.model.jooq.generated.tables.FileOfWorkflow; @@ -20,6 +23,9 @@ import edu.uci.ics.texera.web.model.jooq.generated.tables.WorkflowRuntimeStatistics; import edu.uci.ics.texera.web.model.jooq.generated.tables.WorkflowUserAccess; import edu.uci.ics.texera.web.model.jooq.generated.tables.WorkflowVersion; +import edu.uci.ics.texera.web.model.jooq.generated.tables.records.DatasetRecord; +import edu.uci.ics.texera.web.model.jooq.generated.tables.records.DatasetUserAccessRecord; +import edu.uci.ics.texera.web.model.jooq.generated.tables.records.DatasetVersionRecord; import edu.uci.ics.texera.web.model.jooq.generated.tables.records.FileOfProjectRecord; import edu.uci.ics.texera.web.model.jooq.generated.tables.records.FileOfWorkflowRecord; import edu.uci.ics.texera.web.model.jooq.generated.tables.records.FileRecord; @@ -55,6 +61,8 @@ public class Keys { // IDENTITY definitions // ------------------------------------------------------------------------- + public static final Identity IDENTITY_DATASET = Identities0.IDENTITY_DATASET; + public static final Identity IDENTITY_DATASET_VERSION = Identities0.IDENTITY_DATASET_VERSION; public static final Identity IDENTITY_FILE = Identities0.IDENTITY_FILE; public static final Identity IDENTITY_PROJECT = Identities0.IDENTITY_PROJECT; public static final Identity IDENTITY_USER = Identities0.IDENTITY_USER; @@ -66,6 +74,9 @@ public class Keys { // UNIQUE and PRIMARY KEY definitions // ------------------------------------------------------------------------- + public static final UniqueKey KEY_DATASET_PRIMARY = UniqueKeys0.KEY_DATASET_PRIMARY; + public static final UniqueKey KEY_DATASET_USER_ACCESS_PRIMARY = UniqueKeys0.KEY_DATASET_USER_ACCESS_PRIMARY; + public static final UniqueKey KEY_DATASET_VERSION_PRIMARY = UniqueKeys0.KEY_DATASET_VERSION_PRIMARY; public static final UniqueKey KEY_FILE_OWNER_UID = UniqueKeys0.KEY_FILE_OWNER_UID; public static final UniqueKey KEY_FILE_PRIMARY = UniqueKeys0.KEY_FILE_PRIMARY; public static final UniqueKey KEY_FILE_OF_PROJECT_PRIMARY = UniqueKeys0.KEY_FILE_OF_PROJECT_PRIMARY; @@ -91,6 +102,10 @@ public class Keys { // FOREIGN KEY definitions // ------------------------------------------------------------------------- + public static final ForeignKey DATASET_IBFK_1 = ForeignKeys0.DATASET_IBFK_1; + public static final ForeignKey DATASET_USER_ACCESS_IBFK_1 = ForeignKeys0.DATASET_USER_ACCESS_IBFK_1; + public static final ForeignKey DATASET_USER_ACCESS_IBFK_2 = ForeignKeys0.DATASET_USER_ACCESS_IBFK_2; + public static final ForeignKey DATASET_VERSION_IBFK_1 = ForeignKeys0.DATASET_VERSION_IBFK_1; public static final ForeignKey FILE_IBFK_1 = ForeignKeys0.FILE_IBFK_1; public static final ForeignKey FILE_OF_PROJECT_IBFK_1 = ForeignKeys0.FILE_OF_PROJECT_IBFK_1; public static final ForeignKey FILE_OF_PROJECT_IBFK_2 = ForeignKeys0.FILE_OF_PROJECT_IBFK_2; @@ -120,6 +135,8 @@ public class Keys { // ------------------------------------------------------------------------- private static class Identities0 { + public static Identity IDENTITY_DATASET = Internal.createIdentity(Dataset.DATASET, Dataset.DATASET.DID); + public static Identity IDENTITY_DATASET_VERSION = Internal.createIdentity(DatasetVersion.DATASET_VERSION, DatasetVersion.DATASET_VERSION.DVID); public static Identity IDENTITY_FILE = Internal.createIdentity(File.FILE, File.FILE.FID); public static Identity IDENTITY_PROJECT = Internal.createIdentity(Project.PROJECT, Project.PROJECT.PID); public static Identity IDENTITY_USER = Internal.createIdentity(User.USER, User.USER.UID); @@ -129,6 +146,9 @@ private static class Identities0 { } private static class UniqueKeys0 { + public static final UniqueKey KEY_DATASET_PRIMARY = Internal.createUniqueKey(Dataset.DATASET, "KEY_dataset_PRIMARY", Dataset.DATASET.DID); + public static final UniqueKey KEY_DATASET_USER_ACCESS_PRIMARY = Internal.createUniqueKey(DatasetUserAccess.DATASET_USER_ACCESS, "KEY_dataset_user_access_PRIMARY", DatasetUserAccess.DATASET_USER_ACCESS.DID, DatasetUserAccess.DATASET_USER_ACCESS.UID); + public static final UniqueKey KEY_DATASET_VERSION_PRIMARY = Internal.createUniqueKey(DatasetVersion.DATASET_VERSION, "KEY_dataset_version_PRIMARY", DatasetVersion.DATASET_VERSION.DVID); public static final UniqueKey KEY_FILE_OWNER_UID = Internal.createUniqueKey(File.FILE, "KEY_file_owner_uid", File.FILE.OWNER_UID, File.FILE.NAME); public static final UniqueKey KEY_FILE_PRIMARY = Internal.createUniqueKey(File.FILE, "KEY_file_PRIMARY", File.FILE.FID); public static final UniqueKey KEY_FILE_OF_PROJECT_PRIMARY = Internal.createUniqueKey(FileOfProject.FILE_OF_PROJECT, "KEY_file_of_project_PRIMARY", FileOfProject.FILE_OF_PROJECT.FID, FileOfProject.FILE_OF_PROJECT.PID); @@ -152,6 +172,10 @@ private static class UniqueKeys0 { } private static class ForeignKeys0 { + public static final ForeignKey DATASET_IBFK_1 = Internal.createForeignKey(edu.uci.ics.texera.web.model.jooq.generated.Keys.KEY_USER_PRIMARY, Dataset.DATASET, "dataset_ibfk_1", Dataset.DATASET.OWNER_UID); + public static final ForeignKey DATASET_USER_ACCESS_IBFK_1 = Internal.createForeignKey(edu.uci.ics.texera.web.model.jooq.generated.Keys.KEY_DATASET_PRIMARY, DatasetUserAccess.DATASET_USER_ACCESS, "dataset_user_access_ibfk_1", DatasetUserAccess.DATASET_USER_ACCESS.DID); + public static final ForeignKey DATASET_USER_ACCESS_IBFK_2 = Internal.createForeignKey(edu.uci.ics.texera.web.model.jooq.generated.Keys.KEY_USER_PRIMARY, DatasetUserAccess.DATASET_USER_ACCESS, "dataset_user_access_ibfk_2", DatasetUserAccess.DATASET_USER_ACCESS.UID); + public static final ForeignKey DATASET_VERSION_IBFK_1 = Internal.createForeignKey(edu.uci.ics.texera.web.model.jooq.generated.Keys.KEY_DATASET_PRIMARY, DatasetVersion.DATASET_VERSION, "dataset_version_ibfk_1", DatasetVersion.DATASET_VERSION.DID); public static final ForeignKey FILE_IBFK_1 = Internal.createForeignKey(edu.uci.ics.texera.web.model.jooq.generated.Keys.KEY_USER_PRIMARY, File.FILE, "file_ibfk_1", File.FILE.OWNER_UID); public static final ForeignKey FILE_OF_PROJECT_IBFK_1 = Internal.createForeignKey(edu.uci.ics.texera.web.model.jooq.generated.Keys.KEY_FILE_PRIMARY, FileOfProject.FILE_OF_PROJECT, "file_of_project_ibfk_1", FileOfProject.FILE_OF_PROJECT.FID); public static final ForeignKey FILE_OF_PROJECT_IBFK_2 = Internal.createForeignKey(edu.uci.ics.texera.web.model.jooq.generated.Keys.KEY_PROJECT_PRIMARY, FileOfProject.FILE_OF_PROJECT, "file_of_project_ibfk_2", FileOfProject.FILE_OF_PROJECT.PID); diff --git a/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/Tables.java b/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/Tables.java index c1358fc5152..814fccf3aa9 100644 --- a/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/Tables.java +++ b/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/Tables.java @@ -4,6 +4,9 @@ package edu.uci.ics.texera.web.model.jooq.generated; +import edu.uci.ics.texera.web.model.jooq.generated.tables.Dataset; +import edu.uci.ics.texera.web.model.jooq.generated.tables.DatasetUserAccess; +import edu.uci.ics.texera.web.model.jooq.generated.tables.DatasetVersion; import edu.uci.ics.texera.web.model.jooq.generated.tables.File; import edu.uci.ics.texera.web.model.jooq.generated.tables.FileOfProject; import edu.uci.ics.texera.web.model.jooq.generated.tables.FileOfWorkflow; @@ -28,6 +31,21 @@ @SuppressWarnings({ "all", "unchecked", "rawtypes" }) public class Tables { + /** + * The table texera_db.dataset. + */ + public static final Dataset DATASET = Dataset.DATASET; + + /** + * The table texera_db.dataset_user_access. + */ + public static final DatasetUserAccess DATASET_USER_ACCESS = DatasetUserAccess.DATASET_USER_ACCESS; + + /** + * The table texera_db.dataset_version. + */ + public static final DatasetVersion DATASET_VERSION = DatasetVersion.DATASET_VERSION; + /** * The table texera_db.file. */ diff --git a/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/TexeraDb.java b/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/TexeraDb.java index 7d3123704e5..294e07a37c8 100644 --- a/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/TexeraDb.java +++ b/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/TexeraDb.java @@ -4,6 +4,9 @@ package edu.uci.ics.texera.web.model.jooq.generated; +import edu.uci.ics.texera.web.model.jooq.generated.tables.Dataset; +import edu.uci.ics.texera.web.model.jooq.generated.tables.DatasetUserAccess; +import edu.uci.ics.texera.web.model.jooq.generated.tables.DatasetVersion; import edu.uci.ics.texera.web.model.jooq.generated.tables.File; import edu.uci.ics.texera.web.model.jooq.generated.tables.FileOfProject; import edu.uci.ics.texera.web.model.jooq.generated.tables.FileOfWorkflow; @@ -36,13 +39,28 @@ @SuppressWarnings({ "all", "unchecked", "rawtypes" }) public class TexeraDb extends SchemaImpl { - private static final long serialVersionUID = 304546079; + private static final long serialVersionUID = -1679894118; /** * The reference instance of texera_db */ public static final TexeraDb TEXERA_DB = new TexeraDb(); + /** + * The table texera_db.dataset. + */ + public final Dataset DATASET = edu.uci.ics.texera.web.model.jooq.generated.tables.Dataset.DATASET; + + /** + * The table texera_db.dataset_user_access. + */ + public final DatasetUserAccess DATASET_USER_ACCESS = edu.uci.ics.texera.web.model.jooq.generated.tables.DatasetUserAccess.DATASET_USER_ACCESS; + + /** + * The table texera_db.dataset_version. + */ + public final DatasetVersion DATASET_VERSION = edu.uci.ics.texera.web.model.jooq.generated.tables.DatasetVersion.DATASET_VERSION; + /** * The table texera_db.file. */ @@ -145,6 +163,9 @@ public final List> getTables() { private final List> getTables0() { return Arrays.>asList( + Dataset.DATASET, + DatasetUserAccess.DATASET_USER_ACCESS, + DatasetVersion.DATASET_VERSION, File.FILE, FileOfProject.FILE_OF_PROJECT, FileOfWorkflow.FILE_OF_WORKFLOW, diff --git a/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/enums/DatasetUserAccessPrivilege.java b/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/enums/DatasetUserAccessPrivilege.java new file mode 100644 index 00000000000..09b530b3124 --- /dev/null +++ b/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/enums/DatasetUserAccessPrivilege.java @@ -0,0 +1,49 @@ +/* + * This file is generated by jOOQ. + */ +package edu.uci.ics.texera.web.model.jooq.generated.enums; + + +import org.jooq.Catalog; +import org.jooq.EnumType; +import org.jooq.Schema; + + +/** + * This class is generated by jOOQ. + */ +@SuppressWarnings({ "all", "unchecked", "rawtypes" }) +public enum DatasetUserAccessPrivilege implements EnumType { + + NONE("NONE"), + + READ("READ"), + + WRITE("WRITE"); + + private final String literal; + + private DatasetUserAccessPrivilege(String literal) { + this.literal = literal; + } + + @Override + public Catalog getCatalog() { + return null; + } + + @Override + public Schema getSchema() { + return null; + } + + @Override + public String getName() { + return "dataset_user_access_privilege"; + } + + @Override + public String getLiteral() { + return literal; + } +} diff --git a/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/tables/Dataset.java b/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/tables/Dataset.java new file mode 100644 index 00000000000..7e80fb1ecfc --- /dev/null +++ b/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/tables/Dataset.java @@ -0,0 +1,189 @@ +/* + * This file is generated by jOOQ. + */ +package edu.uci.ics.texera.web.model.jooq.generated.tables; + + +import edu.uci.ics.texera.web.model.jooq.generated.Indexes; +import edu.uci.ics.texera.web.model.jooq.generated.Keys; +import edu.uci.ics.texera.web.model.jooq.generated.TexeraDb; +import edu.uci.ics.texera.web.model.jooq.generated.tables.records.DatasetRecord; + +import java.sql.Timestamp; +import java.util.Arrays; +import java.util.List; + +import org.jooq.Field; +import org.jooq.ForeignKey; +import org.jooq.Identity; +import org.jooq.Index; +import org.jooq.Name; +import org.jooq.Record; +import org.jooq.Row7; +import org.jooq.Schema; +import org.jooq.Table; +import org.jooq.TableField; +import org.jooq.UniqueKey; +import org.jooq.impl.DSL; +import org.jooq.impl.TableImpl; +import org.jooq.types.UInteger; + + +/** + * This class is generated by jOOQ. + */ +@SuppressWarnings({ "all", "unchecked", "rawtypes" }) +public class Dataset extends TableImpl { + + private static final long serialVersionUID = 578477320; + + /** + * The reference instance of texera_db.dataset + */ + public static final Dataset DATASET = new Dataset(); + + /** + * The class holding records for this type + */ + @Override + public Class getRecordType() { + return DatasetRecord.class; + } + + /** + * The column texera_db.dataset.did. + */ + public final TableField DID = createField(DSL.name("did"), org.jooq.impl.SQLDataType.INTEGERUNSIGNED.nullable(false).identity(true), this, ""); + + /** + * The column texera_db.dataset.owner_uid. + */ + public final TableField OWNER_UID = createField(DSL.name("owner_uid"), org.jooq.impl.SQLDataType.INTEGERUNSIGNED.nullable(false), this, ""); + + /** + * The column texera_db.dataset.name. + */ + public final TableField NAME = createField(DSL.name("name"), org.jooq.impl.SQLDataType.VARCHAR(128).nullable(false), this, ""); + + /** + * The column texera_db.dataset.is_public. + */ + public final TableField IS_PUBLIC = createField(DSL.name("is_public"), org.jooq.impl.SQLDataType.TINYINT.nullable(false).defaultValue(org.jooq.impl.DSL.inline("1", org.jooq.impl.SQLDataType.TINYINT)), this, ""); + + /** + * The column texera_db.dataset.storage_path. + */ + public final TableField STORAGE_PATH = createField(DSL.name("storage_path"), org.jooq.impl.SQLDataType.VARCHAR(512).nullable(false), this, ""); + + /** + * The column texera_db.dataset.description. + */ + public final TableField DESCRIPTION = createField(DSL.name("description"), org.jooq.impl.SQLDataType.VARCHAR(512).nullable(false), this, ""); + + /** + * The column texera_db.dataset.creation_time. + */ + public final TableField CREATION_TIME = createField(DSL.name("creation_time"), org.jooq.impl.SQLDataType.TIMESTAMP.nullable(false).defaultValue(org.jooq.impl.DSL.field("CURRENT_TIMESTAMP", org.jooq.impl.SQLDataType.TIMESTAMP)), this, ""); + + /** + * Create a texera_db.dataset table reference + */ + public Dataset() { + this(DSL.name("dataset"), null); + } + + /** + * Create an aliased texera_db.dataset table reference + */ + public Dataset(String alias) { + this(DSL.name(alias), DATASET); + } + + /** + * Create an aliased texera_db.dataset table reference + */ + public Dataset(Name alias) { + this(alias, DATASET); + } + + private Dataset(Name alias, Table aliased) { + this(alias, aliased, null); + } + + private Dataset(Name alias, Table aliased, Field[] parameters) { + super(alias, null, aliased, parameters, DSL.comment("")); + } + + public Dataset(Table child, ForeignKey key) { + super(child, key, DATASET); + } + + @Override + public Schema getSchema() { + return TexeraDb.TEXERA_DB; + } + + @Override + public List getIndexes() { + return Arrays.asList(Indexes.DATASET_IDX_DATASET_NAME_DESCRIPTION, Indexes.DATASET_OWNER_UID, Indexes.DATASET_PRIMARY); + } + + @Override + public Identity getIdentity() { + return Keys.IDENTITY_DATASET; + } + + @Override + public UniqueKey getPrimaryKey() { + return Keys.KEY_DATASET_PRIMARY; + } + + @Override + public List> getKeys() { + return Arrays.>asList(Keys.KEY_DATASET_PRIMARY); + } + + @Override + public List> getReferences() { + return Arrays.>asList(Keys.DATASET_IBFK_1); + } + + public User user() { + return new User(this, Keys.DATASET_IBFK_1); + } + + @Override + public Dataset as(String alias) { + return new Dataset(DSL.name(alias), this); + } + + @Override + public Dataset as(Name alias) { + return new Dataset(alias, this); + } + + /** + * Rename this table + */ + @Override + public Dataset rename(String name) { + return new Dataset(DSL.name(name), null); + } + + /** + * Rename this table + */ + @Override + public Dataset rename(Name name) { + return new Dataset(name, null); + } + + // ------------------------------------------------------------------------- + // Row7 type methods + // ------------------------------------------------------------------------- + + @Override + public Row7 fieldsRow() { + return (Row7) super.fieldsRow(); + } +} diff --git a/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/tables/DatasetUserAccess.java b/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/tables/DatasetUserAccess.java new file mode 100644 index 00000000000..143b70ac291 --- /dev/null +++ b/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/tables/DatasetUserAccess.java @@ -0,0 +1,167 @@ +/* + * This file is generated by jOOQ. + */ +package edu.uci.ics.texera.web.model.jooq.generated.tables; + + +import edu.uci.ics.texera.web.model.jooq.generated.Indexes; +import edu.uci.ics.texera.web.model.jooq.generated.Keys; +import edu.uci.ics.texera.web.model.jooq.generated.TexeraDb; +import edu.uci.ics.texera.web.model.jooq.generated.enums.DatasetUserAccessPrivilege; +import edu.uci.ics.texera.web.model.jooq.generated.tables.records.DatasetUserAccessRecord; + +import java.util.Arrays; +import java.util.List; + +import org.jooq.Field; +import org.jooq.ForeignKey; +import org.jooq.Index; +import org.jooq.Name; +import org.jooq.Record; +import org.jooq.Row3; +import org.jooq.Schema; +import org.jooq.Table; +import org.jooq.TableField; +import org.jooq.UniqueKey; +import org.jooq.impl.DSL; +import org.jooq.impl.TableImpl; +import org.jooq.types.UInteger; + + +/** + * This class is generated by jOOQ. + */ +@SuppressWarnings({ "all", "unchecked", "rawtypes" }) +public class DatasetUserAccess extends TableImpl { + + private static final long serialVersionUID = -996212423; + + /** + * The reference instance of texera_db.dataset_user_access + */ + public static final DatasetUserAccess DATASET_USER_ACCESS = new DatasetUserAccess(); + + /** + * The class holding records for this type + */ + @Override + public Class getRecordType() { + return DatasetUserAccessRecord.class; + } + + /** + * The column texera_db.dataset_user_access.did. + */ + public final TableField DID = createField(DSL.name("did"), org.jooq.impl.SQLDataType.INTEGERUNSIGNED.nullable(false), this, ""); + + /** + * The column texera_db.dataset_user_access.uid. + */ + public final TableField UID = createField(DSL.name("uid"), org.jooq.impl.SQLDataType.INTEGERUNSIGNED.nullable(false), this, ""); + + /** + * The column texera_db.dataset_user_access.privilege. + */ + public final TableField PRIVILEGE = createField(DSL.name("privilege"), org.jooq.impl.SQLDataType.VARCHAR(5).nullable(false).defaultValue(org.jooq.impl.DSL.inline("NONE", org.jooq.impl.SQLDataType.VARCHAR)).asEnumDataType(edu.uci.ics.texera.web.model.jooq.generated.enums.DatasetUserAccessPrivilege.class), this, ""); + + /** + * Create a texera_db.dataset_user_access table reference + */ + public DatasetUserAccess() { + this(DSL.name("dataset_user_access"), null); + } + + /** + * Create an aliased texera_db.dataset_user_access table reference + */ + public DatasetUserAccess(String alias) { + this(DSL.name(alias), DATASET_USER_ACCESS); + } + + /** + * Create an aliased texera_db.dataset_user_access table reference + */ + public DatasetUserAccess(Name alias) { + this(alias, DATASET_USER_ACCESS); + } + + private DatasetUserAccess(Name alias, Table aliased) { + this(alias, aliased, null); + } + + private DatasetUserAccess(Name alias, Table aliased, Field[] parameters) { + super(alias, null, aliased, parameters, DSL.comment("")); + } + + public DatasetUserAccess(Table child, ForeignKey key) { + super(child, key, DATASET_USER_ACCESS); + } + + @Override + public Schema getSchema() { + return TexeraDb.TEXERA_DB; + } + + @Override + public List getIndexes() { + return Arrays.asList(Indexes.DATASET_USER_ACCESS_PRIMARY, Indexes.DATASET_USER_ACCESS_UID); + } + + @Override + public UniqueKey getPrimaryKey() { + return Keys.KEY_DATASET_USER_ACCESS_PRIMARY; + } + + @Override + public List> getKeys() { + return Arrays.>asList(Keys.KEY_DATASET_USER_ACCESS_PRIMARY); + } + + @Override + public List> getReferences() { + return Arrays.>asList(Keys.DATASET_USER_ACCESS_IBFK_1, Keys.DATASET_USER_ACCESS_IBFK_2); + } + + public Dataset dataset() { + return new Dataset(this, Keys.DATASET_USER_ACCESS_IBFK_1); + } + + public User user() { + return new User(this, Keys.DATASET_USER_ACCESS_IBFK_2); + } + + @Override + public DatasetUserAccess as(String alias) { + return new DatasetUserAccess(DSL.name(alias), this); + } + + @Override + public DatasetUserAccess as(Name alias) { + return new DatasetUserAccess(alias, this); + } + + /** + * Rename this table + */ + @Override + public DatasetUserAccess rename(String name) { + return new DatasetUserAccess(DSL.name(name), null); + } + + /** + * Rename this table + */ + @Override + public DatasetUserAccess rename(Name name) { + return new DatasetUserAccess(name, null); + } + + // ------------------------------------------------------------------------- + // Row3 type methods + // ------------------------------------------------------------------------- + + @Override + public Row3 fieldsRow() { + return (Row3) super.fieldsRow(); + } +} diff --git a/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/tables/DatasetVersion.java b/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/tables/DatasetVersion.java new file mode 100644 index 00000000000..699bde6aca7 --- /dev/null +++ b/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/tables/DatasetVersion.java @@ -0,0 +1,184 @@ +/* + * This file is generated by jOOQ. + */ +package edu.uci.ics.texera.web.model.jooq.generated.tables; + + +import edu.uci.ics.texera.web.model.jooq.generated.Indexes; +import edu.uci.ics.texera.web.model.jooq.generated.Keys; +import edu.uci.ics.texera.web.model.jooq.generated.TexeraDb; +import edu.uci.ics.texera.web.model.jooq.generated.tables.records.DatasetVersionRecord; + +import java.sql.Timestamp; +import java.util.Arrays; +import java.util.List; + +import org.jooq.Field; +import org.jooq.ForeignKey; +import org.jooq.Identity; +import org.jooq.Index; +import org.jooq.Name; +import org.jooq.Record; +import org.jooq.Row6; +import org.jooq.Schema; +import org.jooq.Table; +import org.jooq.TableField; +import org.jooq.UniqueKey; +import org.jooq.impl.DSL; +import org.jooq.impl.TableImpl; +import org.jooq.types.UInteger; + + +/** + * This class is generated by jOOQ. + */ +@SuppressWarnings({ "all", "unchecked", "rawtypes" }) +public class DatasetVersion extends TableImpl { + + private static final long serialVersionUID = 25893167; + + /** + * The reference instance of texera_db.dataset_version + */ + public static final DatasetVersion DATASET_VERSION = new DatasetVersion(); + + /** + * The class holding records for this type + */ + @Override + public Class getRecordType() { + return DatasetVersionRecord.class; + } + + /** + * The column texera_db.dataset_version.dvid. + */ + public final TableField DVID = createField(DSL.name("dvid"), org.jooq.impl.SQLDataType.INTEGERUNSIGNED.nullable(false).identity(true), this, ""); + + /** + * The column texera_db.dataset_version.did. + */ + public final TableField DID = createField(DSL.name("did"), org.jooq.impl.SQLDataType.INTEGERUNSIGNED.nullable(false), this, ""); + + /** + * The column texera_db.dataset_version.creator_uid. + */ + public final TableField CREATOR_UID = createField(DSL.name("creator_uid"), org.jooq.impl.SQLDataType.INTEGERUNSIGNED.nullable(false), this, ""); + + /** + * The column texera_db.dataset_version.name. + */ + public final TableField NAME = createField(DSL.name("name"), org.jooq.impl.SQLDataType.VARCHAR(128).nullable(false), this, ""); + + /** + * The column texera_db.dataset_version.version_hash. + */ + public final TableField VERSION_HASH = createField(DSL.name("version_hash"), org.jooq.impl.SQLDataType.VARCHAR(64).nullable(false), this, ""); + + /** + * The column texera_db.dataset_version.creation_time. + */ + public final TableField CREATION_TIME = createField(DSL.name("creation_time"), org.jooq.impl.SQLDataType.TIMESTAMP.nullable(false).defaultValue(org.jooq.impl.DSL.field("CURRENT_TIMESTAMP", org.jooq.impl.SQLDataType.TIMESTAMP)), this, ""); + + /** + * Create a texera_db.dataset_version table reference + */ + public DatasetVersion() { + this(DSL.name("dataset_version"), null); + } + + /** + * Create an aliased texera_db.dataset_version table reference + */ + public DatasetVersion(String alias) { + this(DSL.name(alias), DATASET_VERSION); + } + + /** + * Create an aliased texera_db.dataset_version table reference + */ + public DatasetVersion(Name alias) { + this(alias, DATASET_VERSION); + } + + private DatasetVersion(Name alias, Table aliased) { + this(alias, aliased, null); + } + + private DatasetVersion(Name alias, Table aliased, Field[] parameters) { + super(alias, null, aliased, parameters, DSL.comment("")); + } + + public DatasetVersion(Table child, ForeignKey key) { + super(child, key, DATASET_VERSION); + } + + @Override + public Schema getSchema() { + return TexeraDb.TEXERA_DB; + } + + @Override + public List getIndexes() { + return Arrays.asList(Indexes.DATASET_VERSION_DID, Indexes.DATASET_VERSION_IDX_DATASET_VERSION_NAME, Indexes.DATASET_VERSION_PRIMARY); + } + + @Override + public Identity getIdentity() { + return Keys.IDENTITY_DATASET_VERSION; + } + + @Override + public UniqueKey getPrimaryKey() { + return Keys.KEY_DATASET_VERSION_PRIMARY; + } + + @Override + public List> getKeys() { + return Arrays.>asList(Keys.KEY_DATASET_VERSION_PRIMARY); + } + + @Override + public List> getReferences() { + return Arrays.>asList(Keys.DATASET_VERSION_IBFK_1); + } + + public Dataset dataset() { + return new Dataset(this, Keys.DATASET_VERSION_IBFK_1); + } + + @Override + public DatasetVersion as(String alias) { + return new DatasetVersion(DSL.name(alias), this); + } + + @Override + public DatasetVersion as(Name alias) { + return new DatasetVersion(alias, this); + } + + /** + * Rename this table + */ + @Override + public DatasetVersion rename(String name) { + return new DatasetVersion(DSL.name(name), null); + } + + /** + * Rename this table + */ + @Override + public DatasetVersion rename(Name name) { + return new DatasetVersion(name, null); + } + + // ------------------------------------------------------------------------- + // Row6 type methods + // ------------------------------------------------------------------------- + + @Override + public Row6 fieldsRow() { + return (Row6) super.fieldsRow(); + } +} diff --git a/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/tables/daos/DatasetDao.java b/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/tables/daos/DatasetDao.java new file mode 100644 index 00000000000..e7c4b99fe2a --- /dev/null +++ b/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/tables/daos/DatasetDao.java @@ -0,0 +1,147 @@ +/* + * This file is generated by jOOQ. + */ +package edu.uci.ics.texera.web.model.jooq.generated.tables.daos; + + +import edu.uci.ics.texera.web.model.jooq.generated.tables.Dataset; +import edu.uci.ics.texera.web.model.jooq.generated.tables.records.DatasetRecord; + +import java.sql.Timestamp; +import java.util.List; + +import org.jooq.Configuration; +import org.jooq.impl.DAOImpl; +import org.jooq.types.UInteger; + + +/** + * This class is generated by jOOQ. + */ +@SuppressWarnings({ "all", "unchecked", "rawtypes" }) +public class DatasetDao extends DAOImpl { + + /** + * Create a new DatasetDao without any configuration + */ + public DatasetDao() { + super(Dataset.DATASET, edu.uci.ics.texera.web.model.jooq.generated.tables.pojos.Dataset.class); + } + + /** + * Create a new DatasetDao with an attached configuration + */ + public DatasetDao(Configuration configuration) { + super(Dataset.DATASET, edu.uci.ics.texera.web.model.jooq.generated.tables.pojos.Dataset.class, configuration); + } + + @Override + public UInteger getId(edu.uci.ics.texera.web.model.jooq.generated.tables.pojos.Dataset object) { + return object.getDid(); + } + + /** + * Fetch records that have did BETWEEN lowerInclusive AND upperInclusive + */ + public List fetchRangeOfDid(UInteger lowerInclusive, UInteger upperInclusive) { + return fetchRange(Dataset.DATASET.DID, lowerInclusive, upperInclusive); + } + + /** + * Fetch records that have did IN (values) + */ + public List fetchByDid(UInteger... values) { + return fetch(Dataset.DATASET.DID, values); + } + + /** + * Fetch a unique record that has did = value + */ + public edu.uci.ics.texera.web.model.jooq.generated.tables.pojos.Dataset fetchOneByDid(UInteger value) { + return fetchOne(Dataset.DATASET.DID, value); + } + + /** + * Fetch records that have owner_uid BETWEEN lowerInclusive AND upperInclusive + */ + public List fetchRangeOfOwnerUid(UInteger lowerInclusive, UInteger upperInclusive) { + return fetchRange(Dataset.DATASET.OWNER_UID, lowerInclusive, upperInclusive); + } + + /** + * Fetch records that have owner_uid IN (values) + */ + public List fetchByOwnerUid(UInteger... values) { + return fetch(Dataset.DATASET.OWNER_UID, values); + } + + /** + * Fetch records that have name BETWEEN lowerInclusive AND upperInclusive + */ + public List fetchRangeOfName(String lowerInclusive, String upperInclusive) { + return fetchRange(Dataset.DATASET.NAME, lowerInclusive, upperInclusive); + } + + /** + * Fetch records that have name IN (values) + */ + public List fetchByName(String... values) { + return fetch(Dataset.DATASET.NAME, values); + } + + /** + * Fetch records that have is_public BETWEEN lowerInclusive AND upperInclusive + */ + public List fetchRangeOfIsPublic(Byte lowerInclusive, Byte upperInclusive) { + return fetchRange(Dataset.DATASET.IS_PUBLIC, lowerInclusive, upperInclusive); + } + + /** + * Fetch records that have is_public IN (values) + */ + public List fetchByIsPublic(Byte... values) { + return fetch(Dataset.DATASET.IS_PUBLIC, values); + } + + /** + * Fetch records that have storage_path BETWEEN lowerInclusive AND upperInclusive + */ + public List fetchRangeOfStoragePath(String lowerInclusive, String upperInclusive) { + return fetchRange(Dataset.DATASET.STORAGE_PATH, lowerInclusive, upperInclusive); + } + + /** + * Fetch records that have storage_path IN (values) + */ + public List fetchByStoragePath(String... values) { + return fetch(Dataset.DATASET.STORAGE_PATH, values); + } + + /** + * Fetch records that have description BETWEEN lowerInclusive AND upperInclusive + */ + public List fetchRangeOfDescription(String lowerInclusive, String upperInclusive) { + return fetchRange(Dataset.DATASET.DESCRIPTION, lowerInclusive, upperInclusive); + } + + /** + * Fetch records that have description IN (values) + */ + public List fetchByDescription(String... values) { + return fetch(Dataset.DATASET.DESCRIPTION, values); + } + + /** + * Fetch records that have creation_time BETWEEN lowerInclusive AND upperInclusive + */ + public List fetchRangeOfCreationTime(Timestamp lowerInclusive, Timestamp upperInclusive) { + return fetchRange(Dataset.DATASET.CREATION_TIME, lowerInclusive, upperInclusive); + } + + /** + * Fetch records that have creation_time IN (values) + */ + public List fetchByCreationTime(Timestamp... values) { + return fetch(Dataset.DATASET.CREATION_TIME, values); + } +} diff --git a/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/tables/daos/DatasetUserAccessDao.java b/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/tables/daos/DatasetUserAccessDao.java new file mode 100644 index 00000000000..7b719aef073 --- /dev/null +++ b/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/tables/daos/DatasetUserAccessDao.java @@ -0,0 +1,85 @@ +/* + * This file is generated by jOOQ. + */ +package edu.uci.ics.texera.web.model.jooq.generated.tables.daos; + + +import edu.uci.ics.texera.web.model.jooq.generated.enums.DatasetUserAccessPrivilege; +import edu.uci.ics.texera.web.model.jooq.generated.tables.DatasetUserAccess; +import edu.uci.ics.texera.web.model.jooq.generated.tables.records.DatasetUserAccessRecord; + +import java.util.List; + +import org.jooq.Configuration; +import org.jooq.Record2; +import org.jooq.impl.DAOImpl; +import org.jooq.types.UInteger; + + +/** + * This class is generated by jOOQ. + */ +@SuppressWarnings({ "all", "unchecked", "rawtypes" }) +public class DatasetUserAccessDao extends DAOImpl> { + + /** + * Create a new DatasetUserAccessDao without any configuration + */ + public DatasetUserAccessDao() { + super(DatasetUserAccess.DATASET_USER_ACCESS, edu.uci.ics.texera.web.model.jooq.generated.tables.pojos.DatasetUserAccess.class); + } + + /** + * Create a new DatasetUserAccessDao with an attached configuration + */ + public DatasetUserAccessDao(Configuration configuration) { + super(DatasetUserAccess.DATASET_USER_ACCESS, edu.uci.ics.texera.web.model.jooq.generated.tables.pojos.DatasetUserAccess.class, configuration); + } + + @Override + public Record2 getId(edu.uci.ics.texera.web.model.jooq.generated.tables.pojos.DatasetUserAccess object) { + return compositeKeyRecord(object.getDid(), object.getUid()); + } + + /** + * Fetch records that have did BETWEEN lowerInclusive AND upperInclusive + */ + public List fetchRangeOfDid(UInteger lowerInclusive, UInteger upperInclusive) { + return fetchRange(DatasetUserAccess.DATASET_USER_ACCESS.DID, lowerInclusive, upperInclusive); + } + + /** + * Fetch records that have did IN (values) + */ + public List fetchByDid(UInteger... values) { + return fetch(DatasetUserAccess.DATASET_USER_ACCESS.DID, values); + } + + /** + * Fetch records that have uid BETWEEN lowerInclusive AND upperInclusive + */ + public List fetchRangeOfUid(UInteger lowerInclusive, UInteger upperInclusive) { + return fetchRange(DatasetUserAccess.DATASET_USER_ACCESS.UID, lowerInclusive, upperInclusive); + } + + /** + * Fetch records that have uid IN (values) + */ + public List fetchByUid(UInteger... values) { + return fetch(DatasetUserAccess.DATASET_USER_ACCESS.UID, values); + } + + /** + * Fetch records that have privilege BETWEEN lowerInclusive AND upperInclusive + */ + public List fetchRangeOfPrivilege(DatasetUserAccessPrivilege lowerInclusive, DatasetUserAccessPrivilege upperInclusive) { + return fetchRange(DatasetUserAccess.DATASET_USER_ACCESS.PRIVILEGE, lowerInclusive, upperInclusive); + } + + /** + * Fetch records that have privilege IN (values) + */ + public List fetchByPrivilege(DatasetUserAccessPrivilege... values) { + return fetch(DatasetUserAccess.DATASET_USER_ACCESS.PRIVILEGE, values); + } +} diff --git a/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/tables/daos/DatasetVersionDao.java b/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/tables/daos/DatasetVersionDao.java new file mode 100644 index 00000000000..7508635b901 --- /dev/null +++ b/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/tables/daos/DatasetVersionDao.java @@ -0,0 +1,133 @@ +/* + * This file is generated by jOOQ. + */ +package edu.uci.ics.texera.web.model.jooq.generated.tables.daos; + + +import edu.uci.ics.texera.web.model.jooq.generated.tables.DatasetVersion; +import edu.uci.ics.texera.web.model.jooq.generated.tables.records.DatasetVersionRecord; + +import java.sql.Timestamp; +import java.util.List; + +import org.jooq.Configuration; +import org.jooq.impl.DAOImpl; +import org.jooq.types.UInteger; + + +/** + * This class is generated by jOOQ. + */ +@SuppressWarnings({ "all", "unchecked", "rawtypes" }) +public class DatasetVersionDao extends DAOImpl { + + /** + * Create a new DatasetVersionDao without any configuration + */ + public DatasetVersionDao() { + super(DatasetVersion.DATASET_VERSION, edu.uci.ics.texera.web.model.jooq.generated.tables.pojos.DatasetVersion.class); + } + + /** + * Create a new DatasetVersionDao with an attached configuration + */ + public DatasetVersionDao(Configuration configuration) { + super(DatasetVersion.DATASET_VERSION, edu.uci.ics.texera.web.model.jooq.generated.tables.pojos.DatasetVersion.class, configuration); + } + + @Override + public UInteger getId(edu.uci.ics.texera.web.model.jooq.generated.tables.pojos.DatasetVersion object) { + return object.getDvid(); + } + + /** + * Fetch records that have dvid BETWEEN lowerInclusive AND upperInclusive + */ + public List fetchRangeOfDvid(UInteger lowerInclusive, UInteger upperInclusive) { + return fetchRange(DatasetVersion.DATASET_VERSION.DVID, lowerInclusive, upperInclusive); + } + + /** + * Fetch records that have dvid IN (values) + */ + public List fetchByDvid(UInteger... values) { + return fetch(DatasetVersion.DATASET_VERSION.DVID, values); + } + + /** + * Fetch a unique record that has dvid = value + */ + public edu.uci.ics.texera.web.model.jooq.generated.tables.pojos.DatasetVersion fetchOneByDvid(UInteger value) { + return fetchOne(DatasetVersion.DATASET_VERSION.DVID, value); + } + + /** + * Fetch records that have did BETWEEN lowerInclusive AND upperInclusive + */ + public List fetchRangeOfDid(UInteger lowerInclusive, UInteger upperInclusive) { + return fetchRange(DatasetVersion.DATASET_VERSION.DID, lowerInclusive, upperInclusive); + } + + /** + * Fetch records that have did IN (values) + */ + public List fetchByDid(UInteger... values) { + return fetch(DatasetVersion.DATASET_VERSION.DID, values); + } + + /** + * Fetch records that have creator_uid BETWEEN lowerInclusive AND upperInclusive + */ + public List fetchRangeOfCreatorUid(UInteger lowerInclusive, UInteger upperInclusive) { + return fetchRange(DatasetVersion.DATASET_VERSION.CREATOR_UID, lowerInclusive, upperInclusive); + } + + /** + * Fetch records that have creator_uid IN (values) + */ + public List fetchByCreatorUid(UInteger... values) { + return fetch(DatasetVersion.DATASET_VERSION.CREATOR_UID, values); + } + + /** + * Fetch records that have name BETWEEN lowerInclusive AND upperInclusive + */ + public List fetchRangeOfName(String lowerInclusive, String upperInclusive) { + return fetchRange(DatasetVersion.DATASET_VERSION.NAME, lowerInclusive, upperInclusive); + } + + /** + * Fetch records that have name IN (values) + */ + public List fetchByName(String... values) { + return fetch(DatasetVersion.DATASET_VERSION.NAME, values); + } + + /** + * Fetch records that have version_hash BETWEEN lowerInclusive AND upperInclusive + */ + public List fetchRangeOfVersionHash(String lowerInclusive, String upperInclusive) { + return fetchRange(DatasetVersion.DATASET_VERSION.VERSION_HASH, lowerInclusive, upperInclusive); + } + + /** + * Fetch records that have version_hash IN (values) + */ + public List fetchByVersionHash(String... values) { + return fetch(DatasetVersion.DATASET_VERSION.VERSION_HASH, values); + } + + /** + * Fetch records that have creation_time BETWEEN lowerInclusive AND upperInclusive + */ + public List fetchRangeOfCreationTime(Timestamp lowerInclusive, Timestamp upperInclusive) { + return fetchRange(DatasetVersion.DATASET_VERSION.CREATION_TIME, lowerInclusive, upperInclusive); + } + + /** + * Fetch records that have creation_time IN (values) + */ + public List fetchByCreationTime(Timestamp... values) { + return fetch(DatasetVersion.DATASET_VERSION.CREATION_TIME, values); + } +} diff --git a/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/tables/interfaces/IDataset.java b/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/tables/interfaces/IDataset.java new file mode 100644 index 00000000000..413252a5aee --- /dev/null +++ b/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/tables/interfaces/IDataset.java @@ -0,0 +1,102 @@ +/* + * This file is generated by jOOQ. + */ +package edu.uci.ics.texera.web.model.jooq.generated.tables.interfaces; + + +import java.io.Serializable; +import java.sql.Timestamp; + +import org.jooq.types.UInteger; + + +/** + * This class is generated by jOOQ. + */ +@SuppressWarnings({ "all", "unchecked", "rawtypes" }) +public interface IDataset extends Serializable { + + /** + * Setter for texera_db.dataset.did. + */ + public void setDid(UInteger value); + + /** + * Getter for texera_db.dataset.did. + */ + public UInteger getDid(); + + /** + * Setter for texera_db.dataset.owner_uid. + */ + public void setOwnerUid(UInteger value); + + /** + * Getter for texera_db.dataset.owner_uid. + */ + public UInteger getOwnerUid(); + + /** + * Setter for texera_db.dataset.name. + */ + public void setName(String value); + + /** + * Getter for texera_db.dataset.name. + */ + public String getName(); + + /** + * Setter for texera_db.dataset.is_public. + */ + public void setIsPublic(Byte value); + + /** + * Getter for texera_db.dataset.is_public. + */ + public Byte getIsPublic(); + + /** + * Setter for texera_db.dataset.storage_path. + */ + public void setStoragePath(String value); + + /** + * Getter for texera_db.dataset.storage_path. + */ + public String getStoragePath(); + + /** + * Setter for texera_db.dataset.description. + */ + public void setDescription(String value); + + /** + * Getter for texera_db.dataset.description. + */ + public String getDescription(); + + /** + * Setter for texera_db.dataset.creation_time. + */ + public void setCreationTime(Timestamp value); + + /** + * Getter for texera_db.dataset.creation_time. + */ + public Timestamp getCreationTime(); + + // ------------------------------------------------------------------------- + // FROM and INTO + // ------------------------------------------------------------------------- + + /** + * Load data from another generated Record/POJO implementing the common interface IDataset + */ + public void from(edu.uci.ics.texera.web.model.jooq.generated.tables.interfaces.IDataset from); + + /** + * Copy data into another generated Record/POJO implementing the common interface IDataset + */ + public E into(E into); +} diff --git a/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/tables/interfaces/IDatasetUserAccess.java b/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/tables/interfaces/IDatasetUserAccess.java new file mode 100644 index 00000000000..d0a2abe8db0 --- /dev/null +++ b/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/tables/interfaces/IDatasetUserAccess.java @@ -0,0 +1,63 @@ +/* + * This file is generated by jOOQ. + */ +package edu.uci.ics.texera.web.model.jooq.generated.tables.interfaces; + + +import edu.uci.ics.texera.web.model.jooq.generated.enums.DatasetUserAccessPrivilege; + +import java.io.Serializable; + +import org.jooq.types.UInteger; + + +/** + * This class is generated by jOOQ. + */ +@SuppressWarnings({ "all", "unchecked", "rawtypes" }) +public interface IDatasetUserAccess extends Serializable { + + /** + * Setter for texera_db.dataset_user_access.did. + */ + public void setDid(UInteger value); + + /** + * Getter for texera_db.dataset_user_access.did. + */ + public UInteger getDid(); + + /** + * Setter for texera_db.dataset_user_access.uid. + */ + public void setUid(UInteger value); + + /** + * Getter for texera_db.dataset_user_access.uid. + */ + public UInteger getUid(); + + /** + * Setter for texera_db.dataset_user_access.privilege. + */ + public void setPrivilege(DatasetUserAccessPrivilege value); + + /** + * Getter for texera_db.dataset_user_access.privilege. + */ + public DatasetUserAccessPrivilege getPrivilege(); + + // ------------------------------------------------------------------------- + // FROM and INTO + // ------------------------------------------------------------------------- + + /** + * Load data from another generated Record/POJO implementing the common interface IDatasetUserAccess + */ + public void from(edu.uci.ics.texera.web.model.jooq.generated.tables.interfaces.IDatasetUserAccess from); + + /** + * Copy data into another generated Record/POJO implementing the common interface IDatasetUserAccess + */ + public E into(E into); +} diff --git a/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/tables/interfaces/IDatasetVersion.java b/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/tables/interfaces/IDatasetVersion.java new file mode 100644 index 00000000000..38ab3a5c0e8 --- /dev/null +++ b/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/tables/interfaces/IDatasetVersion.java @@ -0,0 +1,92 @@ +/* + * This file is generated by jOOQ. + */ +package edu.uci.ics.texera.web.model.jooq.generated.tables.interfaces; + + +import java.io.Serializable; +import java.sql.Timestamp; + +import org.jooq.types.UInteger; + + +/** + * This class is generated by jOOQ. + */ +@SuppressWarnings({ "all", "unchecked", "rawtypes" }) +public interface IDatasetVersion extends Serializable { + + /** + * Setter for texera_db.dataset_version.dvid. + */ + public void setDvid(UInteger value); + + /** + * Getter for texera_db.dataset_version.dvid. + */ + public UInteger getDvid(); + + /** + * Setter for texera_db.dataset_version.did. + */ + public void setDid(UInteger value); + + /** + * Getter for texera_db.dataset_version.did. + */ + public UInteger getDid(); + + /** + * Setter for texera_db.dataset_version.creator_uid. + */ + public void setCreatorUid(UInteger value); + + /** + * Getter for texera_db.dataset_version.creator_uid. + */ + public UInteger getCreatorUid(); + + /** + * Setter for texera_db.dataset_version.name. + */ + public void setName(String value); + + /** + * Getter for texera_db.dataset_version.name. + */ + public String getName(); + + /** + * Setter for texera_db.dataset_version.version_hash. + */ + public void setVersionHash(String value); + + /** + * Getter for texera_db.dataset_version.version_hash. + */ + public String getVersionHash(); + + /** + * Setter for texera_db.dataset_version.creation_time. + */ + public void setCreationTime(Timestamp value); + + /** + * Getter for texera_db.dataset_version.creation_time. + */ + public Timestamp getCreationTime(); + + // ------------------------------------------------------------------------- + // FROM and INTO + // ------------------------------------------------------------------------- + + /** + * Load data from another generated Record/POJO implementing the common interface IDatasetVersion + */ + public void from(edu.uci.ics.texera.web.model.jooq.generated.tables.interfaces.IDatasetVersion from); + + /** + * Copy data into another generated Record/POJO implementing the common interface IDatasetVersion + */ + public E into(E into); +} diff --git a/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/tables/pojos/Dataset.java b/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/tables/pojos/Dataset.java new file mode 100644 index 00000000000..aea39303d03 --- /dev/null +++ b/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/tables/pojos/Dataset.java @@ -0,0 +1,166 @@ +/* + * This file is generated by jOOQ. + */ +package edu.uci.ics.texera.web.model.jooq.generated.tables.pojos; + + +import edu.uci.ics.texera.web.model.jooq.generated.tables.interfaces.IDataset; + +import java.sql.Timestamp; + +import org.jooq.types.UInteger; + + +/** + * This class is generated by jOOQ. + */ +@SuppressWarnings({ "all", "unchecked", "rawtypes" }) +public class Dataset implements IDataset { + + private static final long serialVersionUID = 860030245; + + private UInteger did; + private UInteger ownerUid; + private String name; + private Byte isPublic; + private String storagePath; + private String description; + private Timestamp creationTime; + + public Dataset() {} + + public Dataset(IDataset value) { + this.did = value.getDid(); + this.ownerUid = value.getOwnerUid(); + this.name = value.getName(); + this.isPublic = value.getIsPublic(); + this.storagePath = value.getStoragePath(); + this.description = value.getDescription(); + this.creationTime = value.getCreationTime(); + } + + public Dataset( + UInteger did, + UInteger ownerUid, + String name, + Byte isPublic, + String storagePath, + String description, + Timestamp creationTime + ) { + this.did = did; + this.ownerUid = ownerUid; + this.name = name; + this.isPublic = isPublic; + this.storagePath = storagePath; + this.description = description; + this.creationTime = creationTime; + } + + @Override + public UInteger getDid() { + return this.did; + } + + @Override + public void setDid(UInteger did) { + this.did = did; + } + + @Override + public UInteger getOwnerUid() { + return this.ownerUid; + } + + @Override + public void setOwnerUid(UInteger ownerUid) { + this.ownerUid = ownerUid; + } + + @Override + public String getName() { + return this.name; + } + + @Override + public void setName(String name) { + this.name = name; + } + + @Override + public Byte getIsPublic() { + return this.isPublic; + } + + @Override + public void setIsPublic(Byte isPublic) { + this.isPublic = isPublic; + } + + @Override + public String getStoragePath() { + return this.storagePath; + } + + @Override + public void setStoragePath(String storagePath) { + this.storagePath = storagePath; + } + + @Override + public String getDescription() { + return this.description; + } + + @Override + public void setDescription(String description) { + this.description = description; + } + + @Override + public Timestamp getCreationTime() { + return this.creationTime; + } + + @Override + public void setCreationTime(Timestamp creationTime) { + this.creationTime = creationTime; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder("Dataset ("); + + sb.append(did); + sb.append(", ").append(ownerUid); + sb.append(", ").append(name); + sb.append(", ").append(isPublic); + sb.append(", ").append(storagePath); + sb.append(", ").append(description); + sb.append(", ").append(creationTime); + + sb.append(")"); + return sb.toString(); + } + + // ------------------------------------------------------------------------- + // FROM and INTO + // ------------------------------------------------------------------------- + + @Override + public void from(IDataset from) { + setDid(from.getDid()); + setOwnerUid(from.getOwnerUid()); + setName(from.getName()); + setIsPublic(from.getIsPublic()); + setStoragePath(from.getStoragePath()); + setDescription(from.getDescription()); + setCreationTime(from.getCreationTime()); + } + + @Override + public E into(E into) { + into.from(this); + return into; + } +} diff --git a/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/tables/pojos/DatasetUserAccess.java b/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/tables/pojos/DatasetUserAccess.java new file mode 100644 index 00000000000..c4ac78da249 --- /dev/null +++ b/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/tables/pojos/DatasetUserAccess.java @@ -0,0 +1,101 @@ +/* + * This file is generated by jOOQ. + */ +package edu.uci.ics.texera.web.model.jooq.generated.tables.pojos; + + +import edu.uci.ics.texera.web.model.jooq.generated.enums.DatasetUserAccessPrivilege; +import edu.uci.ics.texera.web.model.jooq.generated.tables.interfaces.IDatasetUserAccess; + +import org.jooq.types.UInteger; + + +/** + * This class is generated by jOOQ. + */ +@SuppressWarnings({ "all", "unchecked", "rawtypes" }) +public class DatasetUserAccess implements IDatasetUserAccess { + + private static final long serialVersionUID = 1783622787; + + private UInteger did; + private UInteger uid; + private DatasetUserAccessPrivilege privilege; + + public DatasetUserAccess() {} + + public DatasetUserAccess(IDatasetUserAccess value) { + this.did = value.getDid(); + this.uid = value.getUid(); + this.privilege = value.getPrivilege(); + } + + public DatasetUserAccess( + UInteger did, + UInteger uid, + DatasetUserAccessPrivilege privilege + ) { + this.did = did; + this.uid = uid; + this.privilege = privilege; + } + + @Override + public UInteger getDid() { + return this.did; + } + + @Override + public void setDid(UInteger did) { + this.did = did; + } + + @Override + public UInteger getUid() { + return this.uid; + } + + @Override + public void setUid(UInteger uid) { + this.uid = uid; + } + + @Override + public DatasetUserAccessPrivilege getPrivilege() { + return this.privilege; + } + + @Override + public void setPrivilege(DatasetUserAccessPrivilege privilege) { + this.privilege = privilege; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder("DatasetUserAccess ("); + + sb.append(did); + sb.append(", ").append(uid); + sb.append(", ").append(privilege); + + sb.append(")"); + return sb.toString(); + } + + // ------------------------------------------------------------------------- + // FROM and INTO + // ------------------------------------------------------------------------- + + @Override + public void from(IDatasetUserAccess from) { + setDid(from.getDid()); + setUid(from.getUid()); + setPrivilege(from.getPrivilege()); + } + + @Override + public E into(E into) { + into.from(this); + return into; + } +} diff --git a/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/tables/pojos/DatasetVersion.java b/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/tables/pojos/DatasetVersion.java new file mode 100644 index 00000000000..4c177e80a7b --- /dev/null +++ b/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/tables/pojos/DatasetVersion.java @@ -0,0 +1,150 @@ +/* + * This file is generated by jOOQ. + */ +package edu.uci.ics.texera.web.model.jooq.generated.tables.pojos; + + +import edu.uci.ics.texera.web.model.jooq.generated.tables.interfaces.IDatasetVersion; + +import java.sql.Timestamp; + +import org.jooq.types.UInteger; + + +/** + * This class is generated by jOOQ. + */ +@SuppressWarnings({ "all", "unchecked", "rawtypes" }) +public class DatasetVersion implements IDatasetVersion { + + private static final long serialVersionUID = -1253265124; + + private UInteger dvid; + private UInteger did; + private UInteger creatorUid; + private String name; + private String versionHash; + private Timestamp creationTime; + + public DatasetVersion() {} + + public DatasetVersion(IDatasetVersion value) { + this.dvid = value.getDvid(); + this.did = value.getDid(); + this.creatorUid = value.getCreatorUid(); + this.name = value.getName(); + this.versionHash = value.getVersionHash(); + this.creationTime = value.getCreationTime(); + } + + public DatasetVersion( + UInteger dvid, + UInteger did, + UInteger creatorUid, + String name, + String versionHash, + Timestamp creationTime + ) { + this.dvid = dvid; + this.did = did; + this.creatorUid = creatorUid; + this.name = name; + this.versionHash = versionHash; + this.creationTime = creationTime; + } + + @Override + public UInteger getDvid() { + return this.dvid; + } + + @Override + public void setDvid(UInteger dvid) { + this.dvid = dvid; + } + + @Override + public UInteger getDid() { + return this.did; + } + + @Override + public void setDid(UInteger did) { + this.did = did; + } + + @Override + public UInteger getCreatorUid() { + return this.creatorUid; + } + + @Override + public void setCreatorUid(UInteger creatorUid) { + this.creatorUid = creatorUid; + } + + @Override + public String getName() { + return this.name; + } + + @Override + public void setName(String name) { + this.name = name; + } + + @Override + public String getVersionHash() { + return this.versionHash; + } + + @Override + public void setVersionHash(String versionHash) { + this.versionHash = versionHash; + } + + @Override + public Timestamp getCreationTime() { + return this.creationTime; + } + + @Override + public void setCreationTime(Timestamp creationTime) { + this.creationTime = creationTime; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder("DatasetVersion ("); + + sb.append(dvid); + sb.append(", ").append(did); + sb.append(", ").append(creatorUid); + sb.append(", ").append(name); + sb.append(", ").append(versionHash); + sb.append(", ").append(creationTime); + + sb.append(")"); + return sb.toString(); + } + + // ------------------------------------------------------------------------- + // FROM and INTO + // ------------------------------------------------------------------------- + + @Override + public void from(IDatasetVersion from) { + setDvid(from.getDvid()); + setDid(from.getDid()); + setCreatorUid(from.getCreatorUid()); + setName(from.getName()); + setVersionHash(from.getVersionHash()); + setCreationTime(from.getCreationTime()); + } + + @Override + public E into(E into) { + into.from(this); + return into; + } +} diff --git a/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/tables/records/DatasetRecord.java b/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/tables/records/DatasetRecord.java new file mode 100644 index 00000000000..05eb41a1765 --- /dev/null +++ b/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/tables/records/DatasetRecord.java @@ -0,0 +1,368 @@ +/* + * This file is generated by jOOQ. + */ +package edu.uci.ics.texera.web.model.jooq.generated.tables.records; + + +import edu.uci.ics.texera.web.model.jooq.generated.tables.Dataset; +import edu.uci.ics.texera.web.model.jooq.generated.tables.interfaces.IDataset; + +import java.sql.Timestamp; + +import org.jooq.Field; +import org.jooq.Record1; +import org.jooq.Record7; +import org.jooq.Row7; +import org.jooq.impl.UpdatableRecordImpl; +import org.jooq.types.UInteger; + + +/** + * This class is generated by jOOQ. + */ +@SuppressWarnings({ "all", "unchecked", "rawtypes" }) +public class DatasetRecord extends UpdatableRecordImpl implements Record7, IDataset { + + private static final long serialVersionUID = -1105041155; + + /** + * Setter for texera_db.dataset.did. + */ + @Override + public void setDid(UInteger value) { + set(0, value); + } + + /** + * Getter for texera_db.dataset.did. + */ + @Override + public UInteger getDid() { + return (UInteger) get(0); + } + + /** + * Setter for texera_db.dataset.owner_uid. + */ + @Override + public void setOwnerUid(UInteger value) { + set(1, value); + } + + /** + * Getter for texera_db.dataset.owner_uid. + */ + @Override + public UInteger getOwnerUid() { + return (UInteger) get(1); + } + + /** + * Setter for texera_db.dataset.name. + */ + @Override + public void setName(String value) { + set(2, value); + } + + /** + * Getter for texera_db.dataset.name. + */ + @Override + public String getName() { + return (String) get(2); + } + + /** + * Setter for texera_db.dataset.is_public. + */ + @Override + public void setIsPublic(Byte value) { + set(3, value); + } + + /** + * Getter for texera_db.dataset.is_public. + */ + @Override + public Byte getIsPublic() { + return (Byte) get(3); + } + + /** + * Setter for texera_db.dataset.storage_path. + */ + @Override + public void setStoragePath(String value) { + set(4, value); + } + + /** + * Getter for texera_db.dataset.storage_path. + */ + @Override + public String getStoragePath() { + return (String) get(4); + } + + /** + * Setter for texera_db.dataset.description. + */ + @Override + public void setDescription(String value) { + set(5, value); + } + + /** + * Getter for texera_db.dataset.description. + */ + @Override + public String getDescription() { + return (String) get(5); + } + + /** + * Setter for texera_db.dataset.creation_time. + */ + @Override + public void setCreationTime(Timestamp value) { + set(6, value); + } + + /** + * Getter for texera_db.dataset.creation_time. + */ + @Override + public Timestamp getCreationTime() { + return (Timestamp) get(6); + } + + // ------------------------------------------------------------------------- + // Primary key information + // ------------------------------------------------------------------------- + + @Override + public Record1 key() { + return (Record1) super.key(); + } + + // ------------------------------------------------------------------------- + // Record7 type implementation + // ------------------------------------------------------------------------- + + @Override + public Row7 fieldsRow() { + return (Row7) super.fieldsRow(); + } + + @Override + public Row7 valuesRow() { + return (Row7) super.valuesRow(); + } + + @Override + public Field field1() { + return Dataset.DATASET.DID; + } + + @Override + public Field field2() { + return Dataset.DATASET.OWNER_UID; + } + + @Override + public Field field3() { + return Dataset.DATASET.NAME; + } + + @Override + public Field field4() { + return Dataset.DATASET.IS_PUBLIC; + } + + @Override + public Field field5() { + return Dataset.DATASET.STORAGE_PATH; + } + + @Override + public Field field6() { + return Dataset.DATASET.DESCRIPTION; + } + + @Override + public Field field7() { + return Dataset.DATASET.CREATION_TIME; + } + + @Override + public UInteger component1() { + return getDid(); + } + + @Override + public UInteger component2() { + return getOwnerUid(); + } + + @Override + public String component3() { + return getName(); + } + + @Override + public Byte component4() { + return getIsPublic(); + } + + @Override + public String component5() { + return getStoragePath(); + } + + @Override + public String component6() { + return getDescription(); + } + + @Override + public Timestamp component7() { + return getCreationTime(); + } + + @Override + public UInteger value1() { + return getDid(); + } + + @Override + public UInteger value2() { + return getOwnerUid(); + } + + @Override + public String value3() { + return getName(); + } + + @Override + public Byte value4() { + return getIsPublic(); + } + + @Override + public String value5() { + return getStoragePath(); + } + + @Override + public String value6() { + return getDescription(); + } + + @Override + public Timestamp value7() { + return getCreationTime(); + } + + @Override + public DatasetRecord value1(UInteger value) { + setDid(value); + return this; + } + + @Override + public DatasetRecord value2(UInteger value) { + setOwnerUid(value); + return this; + } + + @Override + public DatasetRecord value3(String value) { + setName(value); + return this; + } + + @Override + public DatasetRecord value4(Byte value) { + setIsPublic(value); + return this; + } + + @Override + public DatasetRecord value5(String value) { + setStoragePath(value); + return this; + } + + @Override + public DatasetRecord value6(String value) { + setDescription(value); + return this; + } + + @Override + public DatasetRecord value7(Timestamp value) { + setCreationTime(value); + return this; + } + + @Override + public DatasetRecord values(UInteger value1, UInteger value2, String value3, Byte value4, String value5, String value6, Timestamp value7) { + value1(value1); + value2(value2); + value3(value3); + value4(value4); + value5(value5); + value6(value6); + value7(value7); + return this; + } + + // ------------------------------------------------------------------------- + // FROM and INTO + // ------------------------------------------------------------------------- + + @Override + public void from(IDataset from) { + setDid(from.getDid()); + setOwnerUid(from.getOwnerUid()); + setName(from.getName()); + setIsPublic(from.getIsPublic()); + setStoragePath(from.getStoragePath()); + setDescription(from.getDescription()); + setCreationTime(from.getCreationTime()); + } + + @Override + public E into(E into) { + into.from(this); + return into; + } + + // ------------------------------------------------------------------------- + // Constructors + // ------------------------------------------------------------------------- + + /** + * Create a detached DatasetRecord + */ + public DatasetRecord() { + super(Dataset.DATASET); + } + + /** + * Create a detached, initialised DatasetRecord + */ + public DatasetRecord(UInteger did, UInteger ownerUid, String name, Byte isPublic, String storagePath, String description, Timestamp creationTime) { + super(Dataset.DATASET); + + set(0, did); + set(1, ownerUid); + set(2, name); + set(3, isPublic); + set(4, storagePath); + set(5, description); + set(6, creationTime); + } +} diff --git a/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/tables/records/DatasetUserAccessRecord.java b/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/tables/records/DatasetUserAccessRecord.java new file mode 100644 index 00000000000..75a4cf0d5fc --- /dev/null +++ b/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/tables/records/DatasetUserAccessRecord.java @@ -0,0 +1,207 @@ +/* + * This file is generated by jOOQ. + */ +package edu.uci.ics.texera.web.model.jooq.generated.tables.records; + + +import edu.uci.ics.texera.web.model.jooq.generated.enums.DatasetUserAccessPrivilege; +import edu.uci.ics.texera.web.model.jooq.generated.tables.DatasetUserAccess; +import edu.uci.ics.texera.web.model.jooq.generated.tables.interfaces.IDatasetUserAccess; + +import org.jooq.Field; +import org.jooq.Record2; +import org.jooq.Record3; +import org.jooq.Row3; +import org.jooq.impl.UpdatableRecordImpl; +import org.jooq.types.UInteger; + + +/** + * This class is generated by jOOQ. + */ +@SuppressWarnings({ "all", "unchecked", "rawtypes" }) +public class DatasetUserAccessRecord extends UpdatableRecordImpl implements Record3, IDatasetUserAccess { + + private static final long serialVersionUID = -417509378; + + /** + * Setter for texera_db.dataset_user_access.did. + */ + @Override + public void setDid(UInteger value) { + set(0, value); + } + + /** + * Getter for texera_db.dataset_user_access.did. + */ + @Override + public UInteger getDid() { + return (UInteger) get(0); + } + + /** + * Setter for texera_db.dataset_user_access.uid. + */ + @Override + public void setUid(UInteger value) { + set(1, value); + } + + /** + * Getter for texera_db.dataset_user_access.uid. + */ + @Override + public UInteger getUid() { + return (UInteger) get(1); + } + + /** + * Setter for texera_db.dataset_user_access.privilege. + */ + @Override + public void setPrivilege(DatasetUserAccessPrivilege value) { + set(2, value); + } + + /** + * Getter for texera_db.dataset_user_access.privilege. + */ + @Override + public DatasetUserAccessPrivilege getPrivilege() { + return (DatasetUserAccessPrivilege) get(2); + } + + // ------------------------------------------------------------------------- + // Primary key information + // ------------------------------------------------------------------------- + + @Override + public Record2 key() { + return (Record2) super.key(); + } + + // ------------------------------------------------------------------------- + // Record3 type implementation + // ------------------------------------------------------------------------- + + @Override + public Row3 fieldsRow() { + return (Row3) super.fieldsRow(); + } + + @Override + public Row3 valuesRow() { + return (Row3) super.valuesRow(); + } + + @Override + public Field field1() { + return DatasetUserAccess.DATASET_USER_ACCESS.DID; + } + + @Override + public Field field2() { + return DatasetUserAccess.DATASET_USER_ACCESS.UID; + } + + @Override + public Field field3() { + return DatasetUserAccess.DATASET_USER_ACCESS.PRIVILEGE; + } + + @Override + public UInteger component1() { + return getDid(); + } + + @Override + public UInteger component2() { + return getUid(); + } + + @Override + public DatasetUserAccessPrivilege component3() { + return getPrivilege(); + } + + @Override + public UInteger value1() { + return getDid(); + } + + @Override + public UInteger value2() { + return getUid(); + } + + @Override + public DatasetUserAccessPrivilege value3() { + return getPrivilege(); + } + + @Override + public DatasetUserAccessRecord value1(UInteger value) { + setDid(value); + return this; + } + + @Override + public DatasetUserAccessRecord value2(UInteger value) { + setUid(value); + return this; + } + + @Override + public DatasetUserAccessRecord value3(DatasetUserAccessPrivilege value) { + setPrivilege(value); + return this; + } + + @Override + public DatasetUserAccessRecord values(UInteger value1, UInteger value2, DatasetUserAccessPrivilege value3) { + value1(value1); + value2(value2); + value3(value3); + return this; + } + + // ------------------------------------------------------------------------- + // FROM and INTO + // ------------------------------------------------------------------------- + + @Override + public void from(IDatasetUserAccess from) { + setDid(from.getDid()); + setUid(from.getUid()); + setPrivilege(from.getPrivilege()); + } + + @Override + public E into(E into) { + into.from(this); + return into; + } + + // ------------------------------------------------------------------------- + // Constructors + // ------------------------------------------------------------------------- + + /** + * Create a detached DatasetUserAccessRecord + */ + public DatasetUserAccessRecord() { + super(DatasetUserAccess.DATASET_USER_ACCESS); + } + + /** + * Create a detached, initialised DatasetUserAccessRecord + */ + public DatasetUserAccessRecord(UInteger did, UInteger uid, DatasetUserAccessPrivilege privilege) { + super(DatasetUserAccess.DATASET_USER_ACCESS); + + set(0, did); + set(1, uid); + set(2, privilege); + } +} diff --git a/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/tables/records/DatasetVersionRecord.java b/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/tables/records/DatasetVersionRecord.java new file mode 100644 index 00000000000..144b34b87e6 --- /dev/null +++ b/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/tables/records/DatasetVersionRecord.java @@ -0,0 +1,328 @@ +/* + * This file is generated by jOOQ. + */ +package edu.uci.ics.texera.web.model.jooq.generated.tables.records; + + +import edu.uci.ics.texera.web.model.jooq.generated.tables.DatasetVersion; +import edu.uci.ics.texera.web.model.jooq.generated.tables.interfaces.IDatasetVersion; + +import java.sql.Timestamp; + +import org.jooq.Field; +import org.jooq.Record1; +import org.jooq.Record6; +import org.jooq.Row6; +import org.jooq.impl.UpdatableRecordImpl; +import org.jooq.types.UInteger; + + +/** + * This class is generated by jOOQ. + */ +@SuppressWarnings({ "all", "unchecked", "rawtypes" }) +public class DatasetVersionRecord extends UpdatableRecordImpl implements Record6, IDatasetVersion { + + private static final long serialVersionUID = -870558780; + + /** + * Setter for texera_db.dataset_version.dvid. + */ + @Override + public void setDvid(UInteger value) { + set(0, value); + } + + /** + * Getter for texera_db.dataset_version.dvid. + */ + @Override + public UInteger getDvid() { + return (UInteger) get(0); + } + + /** + * Setter for texera_db.dataset_version.did. + */ + @Override + public void setDid(UInteger value) { + set(1, value); + } + + /** + * Getter for texera_db.dataset_version.did. + */ + @Override + public UInteger getDid() { + return (UInteger) get(1); + } + + /** + * Setter for texera_db.dataset_version.creator_uid. + */ + @Override + public void setCreatorUid(UInteger value) { + set(2, value); + } + + /** + * Getter for texera_db.dataset_version.creator_uid. + */ + @Override + public UInteger getCreatorUid() { + return (UInteger) get(2); + } + + /** + * Setter for texera_db.dataset_version.name. + */ + @Override + public void setName(String value) { + set(3, value); + } + + /** + * Getter for texera_db.dataset_version.name. + */ + @Override + public String getName() { + return (String) get(3); + } + + /** + * Setter for texera_db.dataset_version.version_hash. + */ + @Override + public void setVersionHash(String value) { + set(4, value); + } + + /** + * Getter for texera_db.dataset_version.version_hash. + */ + @Override + public String getVersionHash() { + return (String) get(4); + } + + /** + * Setter for texera_db.dataset_version.creation_time. + */ + @Override + public void setCreationTime(Timestamp value) { + set(5, value); + } + + /** + * Getter for texera_db.dataset_version.creation_time. + */ + @Override + public Timestamp getCreationTime() { + return (Timestamp) get(5); + } + + // ------------------------------------------------------------------------- + // Primary key information + // ------------------------------------------------------------------------- + + @Override + public Record1 key() { + return (Record1) super.key(); + } + + // ------------------------------------------------------------------------- + // Record6 type implementation + // ------------------------------------------------------------------------- + + @Override + public Row6 fieldsRow() { + return (Row6) super.fieldsRow(); + } + + @Override + public Row6 valuesRow() { + return (Row6) super.valuesRow(); + } + + @Override + public Field field1() { + return DatasetVersion.DATASET_VERSION.DVID; + } + + @Override + public Field field2() { + return DatasetVersion.DATASET_VERSION.DID; + } + + @Override + public Field field3() { + return DatasetVersion.DATASET_VERSION.CREATOR_UID; + } + + @Override + public Field field4() { + return DatasetVersion.DATASET_VERSION.NAME; + } + + @Override + public Field field5() { + return DatasetVersion.DATASET_VERSION.VERSION_HASH; + } + + @Override + public Field field6() { + return DatasetVersion.DATASET_VERSION.CREATION_TIME; + } + + @Override + public UInteger component1() { + return getDvid(); + } + + @Override + public UInteger component2() { + return getDid(); + } + + @Override + public UInteger component3() { + return getCreatorUid(); + } + + @Override + public String component4() { + return getName(); + } + + @Override + public String component5() { + return getVersionHash(); + } + + @Override + public Timestamp component6() { + return getCreationTime(); + } + + @Override + public UInteger value1() { + return getDvid(); + } + + @Override + public UInteger value2() { + return getDid(); + } + + @Override + public UInteger value3() { + return getCreatorUid(); + } + + @Override + public String value4() { + return getName(); + } + + @Override + public String value5() { + return getVersionHash(); + } + + @Override + public Timestamp value6() { + return getCreationTime(); + } + + @Override + public DatasetVersionRecord value1(UInteger value) { + setDvid(value); + return this; + } + + @Override + public DatasetVersionRecord value2(UInteger value) { + setDid(value); + return this; + } + + @Override + public DatasetVersionRecord value3(UInteger value) { + setCreatorUid(value); + return this; + } + + @Override + public DatasetVersionRecord value4(String value) { + setName(value); + return this; + } + + @Override + public DatasetVersionRecord value5(String value) { + setVersionHash(value); + return this; + } + + @Override + public DatasetVersionRecord value6(Timestamp value) { + setCreationTime(value); + return this; + } + + @Override + public DatasetVersionRecord values(UInteger value1, UInteger value2, UInteger value3, String value4, String value5, Timestamp value6) { + value1(value1); + value2(value2); + value3(value3); + value4(value4); + value5(value5); + value6(value6); + return this; + } + + // ------------------------------------------------------------------------- + // FROM and INTO + // ------------------------------------------------------------------------- + + @Override + public void from(IDatasetVersion from) { + setDvid(from.getDvid()); + setDid(from.getDid()); + setCreatorUid(from.getCreatorUid()); + setName(from.getName()); + setVersionHash(from.getVersionHash()); + setCreationTime(from.getCreationTime()); + } + + @Override + public E into(E into) { + into.from(this); + return into; + } + + // ------------------------------------------------------------------------- + // Constructors + // ------------------------------------------------------------------------- + + /** + * Create a detached DatasetVersionRecord + */ + public DatasetVersionRecord() { + super(DatasetVersion.DATASET_VERSION); + } + + /** + * Create a detached, initialised DatasetVersionRecord + */ + public DatasetVersionRecord(UInteger dvid, UInteger did, UInteger creatorUid, String name, String versionHash, Timestamp creationTime) { + super(DatasetVersion.DATASET_VERSION); + + set(0, dvid); + set(1, did); + set(2, creatorUid); + set(3, name); + set(4, versionHash); + set(5, creationTime); + } +} diff --git a/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/DatasetResource.scala b/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/DatasetResource.scala new file mode 100644 index 00000000000..7eff87b5ce5 --- /dev/null +++ b/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/DatasetResource.scala @@ -0,0 +1,5 @@ +package edu.uci.ics.texera.web.resource.dashboard.user.dataset + +class DatasetResource { + +} diff --git a/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/service/GitVersionControlLocalFileStorageService.java b/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/service/GitVersionControlLocalFileStorageService.java new file mode 100644 index 00000000000..bb00d0c83df --- /dev/null +++ b/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/service/GitVersionControlLocalFileStorageService.java @@ -0,0 +1,238 @@ +package edu.uci.ics.texera.web.resource.dashboard.user.dataset.service; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.StringTokenizer; + +/** + * Git-based implementation of the VersionControlFileStorageService, using local file storage. + */ +public class GitVersionControlLocalFileStorageService { + + /** + * Writes content from the InputStream to a file at the given path within the repository. + * + * @param repoPath The repository base path. + * @param relativeFilePath The relative path within the repository to write the file. + * @param inputStream The InputStream from which to read the content. + * @throws IOException If an I/O error occurs. + */ + public static void writeFile(String repoPath, String relativeFilePath, InputStream inputStream) throws IOException { + Path filePath = Paths.get(repoPath, relativeFilePath); + Files.createDirectories(filePath.getParent()); + Files.copy(inputStream, filePath, StandardCopyOption.REPLACE_EXISTING); + } + + /** + * Deletes a file at the given path within the repository. + * + * @param repoPath The repository base path. + * @param relativeFilePath The relative path within the repository of the file to delete. + * @throws IOException If an I/O error occurs. + */ + public static void deleteFile(String repoPath, String relativeFilePath) throws IOException { + Path filePath = Paths.get(repoPath, relativeFilePath); + Files.deleteIfExists(filePath); + } + + /** + * Deletes the entire directory specified by the repository path. + * + * @param repoPath The path of the directory to delete. + * @throws IOException If an I/O error occurs. + */ + public static void deleteDirectory(String repoPath) throws IOException { + Path directoryPath = Paths.get(repoPath); + Files.walk(directoryPath) + .sorted(Comparator.reverseOrder()) + .map(Path::toFile) + .forEach(File::delete); + } + + /** + * Initializes a new repository for version control at the specified path. + * + * @param baseRepoPath Path to initialize the repository at. + * @throws IOException If an I/O error occurs. + * @throws InterruptedException If the operation is interrupted. + */ + public static void initRepo(String baseRepoPath) throws IOException, InterruptedException { + GitSystemCall.initRepo(baseRepoPath); + } + + /** + * Creates a new version in the repository with the given version name. + * + * @param baseRepoPath The repository path. + * @param versionName The name or message associated with the version. + * @return The commit hash of the created version. + * @throws IOException If an I/O error occurs. + * @throws InterruptedException If the operation is interrupted. + */ + public static String createVersion(String baseRepoPath, String versionName) throws IOException, InterruptedException { + return GitSystemCall.addAndCommit(baseRepoPath, versionName); + } + + /** + * Retrieves the file tree hierarchy of a specific version identified by its commit hash. + * + * @param baseRepoPath The repository path. + * @param versionCommitHashVal The commit hash of the version. + * @return A map representing the file tree hierarchy of the version. + * @throws IOException If an I/O error occurs. + * @throws InterruptedException If the operation is interrupted. + */ + public static Map retrieveFileTreeOfVersion(String baseRepoPath, String versionCommitHashVal) throws IOException, InterruptedException { + return GitSystemCall.getFileTreeHierarchy(baseRepoPath, versionCommitHashVal); + } + + /** + * Retrieves the content of a specific file from a specific version identified by its commit hash. + * Writes the file content to the provided OutputStream. + * + * @param baseRepoPath The repository path. + * @param commitHash The commit hash of the version from which the file content is retrieved. + * @param filePath The path of the file within the repository. + * @param outputStream The OutputStream to which the file content is written. + * @throws IOException If an I/O error occurs. + * @throws InterruptedException If the operation is interrupted. + */ + public static void retrieveFileContentOfVersion(String baseRepoPath, String commitHash, String filePath, OutputStream outputStream) throws IOException, InterruptedException { + GitSystemCall.showFileContentOfCommit(baseRepoPath, commitHash, filePath, outputStream); + } + + /** + * Recovers the repository to its latest version, discarding any uncommitted changes. + * + * @param baseRepoPath The repository path. + * @throws IOException If an I/O error occurs. + * @throws InterruptedException If the operation is interrupted. + */ + public static void recoverToLatestVersion(String baseRepoPath) throws IOException, InterruptedException { + if (GitSystemCall.hasUncommittedChanges(baseRepoPath)) { + GitSystemCall.rollbackToLatestCommit(baseRepoPath); + } + } + + /** + * Utility class for executing Git commands in a system-agnostic manner. + */ + static class GitSystemCall { + + public static Charset BYTE_2_STRING_CHARSET = StandardCharsets.UTF_8; + + // utility function to execute git-related system call + private static byte[] executeGitCommand(String workingDirectory, String... args) throws IOException, InterruptedException { + List commands = new ArrayList<>(); + commands.add("git"); + Collections.addAll(commands, args); + + ProcessBuilder builder = new ProcessBuilder(commands); + builder.directory(new File(workingDirectory)); + builder.redirectErrorStream(true); + Process process = builder.start(); + + try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { + byte[] buffer = new byte[1024]; + int length; + while ((length = process.getInputStream().read(buffer)) != -1) { + outputStream.write(buffer, 0, length); + } + + int exitCode = process.waitFor(); + if (exitCode != 0) { + throw new IOException("Failed to execute Git command: " + String.join(" ", commands)); + } + + return outputStream.toByteArray(); + } + } + + public static void initRepo(String path) throws IOException, InterruptedException { + executeGitCommand(path, "init"); + } + + // this function adds all changes to the stage and create a commit, it will return the hash of that commit + public static String addAndCommit(String repoPath, String commitMessage) throws IOException, InterruptedException { + // Adding all files and committing + executeGitCommand(repoPath, "add", "."); + executeGitCommand(repoPath, "commit", "-m", commitMessage); + + // Retrieving the full commit hash of the latest commit + byte[] commitHashOutput = executeGitCommand(repoPath, "rev-parse", "HEAD"); + return new String(commitHashOutput, StandardCharsets.UTF_8).trim(); + } + + // write the file content of a certain commit into the output file stream + public static void showFileContentOfCommit(String repoPath, String commitHash, String filePath, OutputStream outputStream) throws IOException, InterruptedException { + byte[] fileContent = executeGitCommand(repoPath, "show", commitHash + ":" + filePath); + outputStream.write(fileContent); + } + + // get the file tree of a certain commit as a Map + public static Map getFileTreeHierarchy(String repoPath, String commitHash) throws IOException, InterruptedException { + String treeOutput = new String(executeGitCommand(repoPath, "ls-tree", "-r", commitHash), BYTE_2_STRING_CHARSET); + return parseFileTree(treeOutput); + } + + // utility function to parse the file tree as the map + private static Map parseFileTree(String treeOutput) { + Map fileTree = new HashMap<>(); + StringTokenizer st = new StringTokenizer(treeOutput, "\n"); + while (st.hasMoreTokens()) { + String line = st.nextToken(); + String[] parts = line.split("\\s+"); + + if (parts.length > 3) { + String type = parts[1]; // "blob" for files, "tree" for directories + String path = parts[3]; + + if (type.equals("blob")) { + String[] pathParts = path.split("/"); + addToFileTree(fileTree, pathParts, 0); + } + } + } + return fileTree; + } + + private static void addToFileTree(Map tree, String[] pathParts, int index) { + if (index == pathParts.length - 1) { + // It's a file, add it to the map + tree.put(pathParts[index], "file"); + } else { + // It's a directory, recurse + tree.computeIfAbsent(pathParts[index], k -> new HashMap()); + @SuppressWarnings("unchecked") + Map subTree = (Map) tree.get(pathParts[index]); + addToFileTree(subTree, pathParts, index + 1); + } + } + + // rollback the repo to its latest commit + public static void rollbackToLatestCommit(String repoPath) throws IOException, InterruptedException { + executeGitCommand(repoPath, "reset", "--hard", "HEAD"); + } + + // check if a repo has uncommitted changes. + public static boolean hasUncommittedChanges(String repoPath) throws IOException, InterruptedException { + String statusOutput = new String(executeGitCommand(repoPath, "status", "--porcelain"), BYTE_2_STRING_CHARSET); + return !statusOutput.isEmpty(); + } + } +} diff --git a/core/scripts/sql/texera_ddl.sql b/core/scripts/sql/texera_ddl.sql index 87feb08d4ef..3eeb24b1e10 100644 --- a/core/scripts/sql/texera_ddl.sql +++ b/core/scripts/sql/texera_ddl.sql @@ -15,6 +15,9 @@ DROP TABLE IF EXISTS `workflow_of_project`; DROP TABLE IF EXISTS `file_of_workflow`; DROP TABLE IF EXISTS `file_of_project`; DROP TABLE IF EXISTS `workflow_executions`; +DROP TABLE IF EXISTS `dataset`; +DROP TABLE IF EXISTS `dataset_user_access`; +DROP TABLE IF EXISTS `dataset_version`; SET PERSIST time_zone = '+00:00'; -- this line is mandatory SET PERSIST sql_mode=(SELECT REPLACE(@@sql_mode,'ONLY_FULL_GROUP_BY','')); @@ -205,6 +208,41 @@ CREATE TABLE IF NOT EXISTS workflow_runtime_statistics FOREIGN KEY (`execution_id`) REFERENCES `workflow_executions` (`eid`) ON DELETE CASCADE ) ENGINE = INNODB; +CREATE TABLE IF NOT EXISTS dataset +( + `did` INT UNSIGNED AUTO_INCREMENT NOT NULL, + `owner_uid` INT UNSIGNED NOT NULL, + `name` VARCHAR(128) NOT NULL, + `is_public` TINYINT NOT NULL DEFAULT 1, + `storage_path` VARCHAR(512) NOT NULL, + `description` VARCHAR(512) NOT NULL, + `creation_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY(`did`), + FOREIGN KEY (`owner_uid`) REFERENCES `user` (`uid`) ON DELETE CASCADE + ) ENGINE = INNODB; + +CREATE TABLE IF NOT EXISTS dataset_user_access +( + `did` INT UNSIGNED NOT NULL, + `uid` INT UNSIGNED NOT NULL, + `privilege` ENUM('NONE', 'READ', 'WRITE') NOT NULL DEFAULT 'NONE', + PRIMARY KEY(`did`, `uid`), + FOREIGN KEY (`did`) REFERENCES `dataset` (`did`) ON DELETE CASCADE, + FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON DELETE CASCADE + ) ENGINE = INNODB; + +CREATE TABLE IF NOT EXISTS dataset_version +( + `dvid` INT UNSIGNED AUTO_INCREMENT NOT NULL, + `did` INT UNSIGNED NOT NULL, + `creator_uid` INT UNSIGNED NOT NULL, + `name` VARCHAR(128) NOT NULL, + `version_hash` VARCHAR(64) NOT NULL, + `creation_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY(`dvid`), + FOREIGN KEY (`did`) REFERENCES `dataset` (`did`) ON DELETE CASCADE + ) ENGINE = INNODB; + -- create fulltext search indexes CREATE FULLTEXT INDEX `idx_workflow_name_description_content` ON `texera_db`.`workflow` (name, description, content); @@ -214,3 +252,7 @@ CREATE FULLTEXT INDEX `idx_user_name` ON `texera_db`.`user` (name); CREATE FULLTEXT INDEX `idx_user_project_name_description` ON `texera_db`.`project` (name, description); CREATE FULLTEXT INDEX `idx_file_name_description` ON `texera_db`.`file` (name, description); + +CREATE FULLTEXT INDEX `idx_dataset_name_description` ON `texera_db`.`dataset` (name, description); + +CREATE FULLTEXT INDEX `idx_dataset_version_name` ON `texera_db`.`dataset_version` (name); \ No newline at end of file diff --git a/core/scripts/sql/update/07.sql b/core/scripts/sql/update/07.sql new file mode 100644 index 00000000000..0b8d8463f30 --- /dev/null +++ b/core/scripts/sql/update/07.sql @@ -0,0 +1,41 @@ +USE `texera_db`; + +-- Create new tables for dataset management +CREATE TABLE IF NOT EXISTS dataset +( + `did` INT UNSIGNED AUTO_INCREMENT NOT NULL, + `owner_uid` INT UNSIGNED NOT NULL, + `name` VARCHAR(128) NOT NULL, + `is_public` TINYINT NOT NULL DEFAULT 1, + `storage_path` VARCHAR(512) NOT NULL, + `description` VARCHAR(512) NOT NULL, + `creation_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY(`did`), + FOREIGN KEY (`owner_uid`) REFERENCES `user` (`uid`) ON DELETE CASCADE +) ENGINE = INNODB; + +CREATE TABLE IF NOT EXISTS dataset_user_access +( + `did` INT UNSIGNED NOT NULL, + `uid` INT UNSIGNED NOT NULL, + `privilege` ENUM('NONE', 'READ', 'WRITE') NOT NULL DEFAULT 'NONE', + PRIMARY KEY(`did`, `uid`), + FOREIGN KEY (`did`) REFERENCES `dataset` (`did`) ON DELETE CASCADE, + FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON DELETE CASCADE +) ENGINE = INNODB; + +CREATE TABLE IF NOT EXISTS dataset_version +( + `dvid` INT UNSIGNED AUTO_INCREMENT NOT NULL, + `did` INT UNSIGNED NOT NULL, + `creator_uid` INT UNSIGNED NOT NULL, + `name` VARCHAR(128) NOT NULL, + `version_hash` VARCHAR(64) NOT NULL, + `creation_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY(`dvid`), + FOREIGN KEY (`did`) REFERENCES `dataset` (`did`) ON DELETE CASCADE +) ENGINE = INNODB; + +-- Create fulltext indexes for the new tables +CREATE FULLTEXT INDEX `idx_dataset_name_description` ON `texera_db`.`dataset` (name, description); +CREATE FULLTEXT INDEX `idx_dataset_version_name` ON `texera_db`.`dataset_version` (name); \ No newline at end of file From b624037e08053dd319adf1a8484f725912fc4f72 Mon Sep 17 00:00:00 2001 From: Jiadong Bai Date: Fri, 9 Feb 2024 17:30:48 -0800 Subject: [PATCH 02/14] remove the empty DatasetResource --- .../resource/dashboard/user/dataset/DatasetResource.scala | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/DatasetResource.scala diff --git a/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/DatasetResource.scala b/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/DatasetResource.scala deleted file mode 100644 index 7eff87b5ce5..00000000000 --- a/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/DatasetResource.scala +++ /dev/null @@ -1,5 +0,0 @@ -package edu.uci.ics.texera.web.resource.dashboard.user.dataset - -class DatasetResource { - -} From d4837c3f6c496fc871354342a043dfe4f66e5d01 Mon Sep 17 00:00:00 2001 From: Jiadong Bai Date: Mon, 12 Feb 2024 22:00:06 -0800 Subject: [PATCH 03/14] add jgit methods --- ...VersionControlLocalFileStorageService.java | 172 ++++-------------- .../dashboard/user/dataset/type/FileNode.java | 40 ++++ .../dataset/utils/JGitVersionControl.java | 151 +++++++++++++++ 3 files changed, 223 insertions(+), 140 deletions(-) create mode 100644 core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/type/FileNode.java create mode 100644 core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/utils/JGitVersionControl.java diff --git a/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/service/GitVersionControlLocalFileStorageService.java b/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/service/GitVersionControlLocalFileStorageService.java index bb00d0c83df..65b7eba695b 100644 --- a/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/service/GitVersionControlLocalFileStorageService.java +++ b/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/service/GitVersionControlLocalFileStorageService.java @@ -1,5 +1,9 @@ package edu.uci.ics.texera.web.resource.dashboard.user.dataset.service; +import edu.uci.ics.texera.web.resource.dashboard.user.dataset.type.FileNode; +import edu.uci.ics.texera.web.resource.dashboard.user.dataset.utils.JGitVersionControl; +import org.eclipse.jgit.api.errors.GitAPIException; + import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; @@ -17,6 +21,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.StringTokenizer; /** @@ -25,39 +30,37 @@ public class GitVersionControlLocalFileStorageService { /** - * Writes content from the InputStream to a file at the given path within the repository. + * Writes content from the InputStream to a file at the given path. * - * @param repoPath The repository base path. - * @param relativeFilePath The relative path within the repository to write the file. + * @param filePath The path within the repository to write the file. * @param inputStream The InputStream from which to read the content. * @throws IOException If an I/O error occurs. */ - public static void writeFile(String repoPath, String relativeFilePath, InputStream inputStream) throws IOException { - Path filePath = Paths.get(repoPath, relativeFilePath); + public static void writeFile(Path filePath, InputStream inputStream) throws IOException { Files.createDirectories(filePath.getParent()); Files.copy(inputStream, filePath, StandardCopyOption.REPLACE_EXISTING); } /** - * Deletes a file at the given path within the repository. + * Deletes a file at the given path. * - * @param repoPath The repository base path. - * @param relativeFilePath The relative path within the repository of the file to delete. + * @param filePath The path of the file to delete. * @throws IOException If an I/O error occurs. */ - public static void deleteFile(String repoPath, String relativeFilePath) throws IOException { - Path filePath = Paths.get(repoPath, relativeFilePath); + public static void deleteFile(Path filePath) throws IOException { + if (Files.isDirectory(filePath)) { + throw new IllegalArgumentException("Provided path is a directory, not a file: " + filePath); + } Files.deleteIfExists(filePath); } /** - * Deletes the entire directory specified by the repository path. + * Deletes the entire directory specified by the path. * - * @param repoPath The path of the directory to delete. + * @param directoryPath The path of the directory to delete. * @throws IOException If an I/O error occurs. */ - public static void deleteDirectory(String repoPath) throws IOException { - Path directoryPath = Paths.get(repoPath); + public static void deleteDirectory(Path directoryPath) throws IOException { Files.walk(directoryPath) .sorted(Comparator.reverseOrder()) .map(Path::toFile) @@ -69,10 +72,10 @@ public static void deleteDirectory(String repoPath) throws IOException { * * @param baseRepoPath Path to initialize the repository at. * @throws IOException If an I/O error occurs. - * @throws InterruptedException If the operation is interrupted. + * @throws GitAPIException If the JGit operation is interrupted. */ - public static void initRepo(String baseRepoPath) throws IOException, InterruptedException { - GitSystemCall.initRepo(baseRepoPath); + public static void initRepo(Path baseRepoPath) throws IOException, GitAPIException { + JGitVersionControl.initRepo(baseRepoPath); } /** @@ -82,10 +85,10 @@ public static void initRepo(String baseRepoPath) throws IOException, Interrupted * @param versionName The name or message associated with the version. * @return The commit hash of the created version. * @throws IOException If an I/O error occurs. - * @throws InterruptedException If the operation is interrupted. + * @throws GitAPIException If the JGit operation is interrupted. */ - public static String createVersion(String baseRepoPath, String versionName) throws IOException, InterruptedException { - return GitSystemCall.addAndCommit(baseRepoPath, versionName); + public static String createVersion(Path baseRepoPath, String versionName) throws IOException, GitAPIException { + return JGitVersionControl.addAndCommit(baseRepoPath, versionName); } /** @@ -93,12 +96,10 @@ public static String createVersion(String baseRepoPath, String versionName) thro * * @param baseRepoPath The repository path. * @param versionCommitHashVal The commit hash of the version. - * @return A map representing the file tree hierarchy of the version. - * @throws IOException If an I/O error occurs. - * @throws InterruptedException If the operation is interrupted. + * @return A set of file nodes at the root level of the given repo at given version */ - public static Map retrieveFileTreeOfVersion(String baseRepoPath, String versionCommitHashVal) throws IOException, InterruptedException { - return GitSystemCall.getFileTreeHierarchy(baseRepoPath, versionCommitHashVal); + public static Set retrieveFileTreeOfVersion(Path baseRepoPath, String versionCommitHashVal) throws Exception { + return JGitVersionControl.getFileTreeOfCommit(baseRepoPath, versionCommitHashVal); } /** @@ -110,10 +111,10 @@ public static Map retrieveFileTreeOfVersion(String baseRepoPath, * @param filePath The path of the file within the repository. * @param outputStream The OutputStream to which the file content is written. * @throws IOException If an I/O error occurs. - * @throws InterruptedException If the operation is interrupted. + * @throws GitAPIException If the operation is interrupted. */ - public static void retrieveFileContentOfVersion(String baseRepoPath, String commitHash, String filePath, OutputStream outputStream) throws IOException, InterruptedException { - GitSystemCall.showFileContentOfCommit(baseRepoPath, commitHash, filePath, outputStream); + public static void retrieveFileContentOfVersion(Path baseRepoPath, String commitHash, Path filePath, OutputStream outputStream) throws IOException, GitAPIException { + JGitVersionControl.showFileContentOfCommit(baseRepoPath, commitHash, filePath, outputStream); } /** @@ -121,118 +122,9 @@ public static void retrieveFileContentOfVersion(String baseRepoPath, String comm * * @param baseRepoPath The repository path. * @throws IOException If an I/O error occurs. - * @throws InterruptedException If the operation is interrupted. + * @throws GitAPIException If the operation is interrupted. */ - public static void recoverToLatestVersion(String baseRepoPath) throws IOException, InterruptedException { - if (GitSystemCall.hasUncommittedChanges(baseRepoPath)) { - GitSystemCall.rollbackToLatestCommit(baseRepoPath); - } - } - - /** - * Utility class for executing Git commands in a system-agnostic manner. - */ - static class GitSystemCall { - - public static Charset BYTE_2_STRING_CHARSET = StandardCharsets.UTF_8; - - // utility function to execute git-related system call - private static byte[] executeGitCommand(String workingDirectory, String... args) throws IOException, InterruptedException { - List commands = new ArrayList<>(); - commands.add("git"); - Collections.addAll(commands, args); - - ProcessBuilder builder = new ProcessBuilder(commands); - builder.directory(new File(workingDirectory)); - builder.redirectErrorStream(true); - Process process = builder.start(); - - try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { - byte[] buffer = new byte[1024]; - int length; - while ((length = process.getInputStream().read(buffer)) != -1) { - outputStream.write(buffer, 0, length); - } - - int exitCode = process.waitFor(); - if (exitCode != 0) { - throw new IOException("Failed to execute Git command: " + String.join(" ", commands)); - } - - return outputStream.toByteArray(); - } - } - - public static void initRepo(String path) throws IOException, InterruptedException { - executeGitCommand(path, "init"); - } - - // this function adds all changes to the stage and create a commit, it will return the hash of that commit - public static String addAndCommit(String repoPath, String commitMessage) throws IOException, InterruptedException { - // Adding all files and committing - executeGitCommand(repoPath, "add", "."); - executeGitCommand(repoPath, "commit", "-m", commitMessage); - - // Retrieving the full commit hash of the latest commit - byte[] commitHashOutput = executeGitCommand(repoPath, "rev-parse", "HEAD"); - return new String(commitHashOutput, StandardCharsets.UTF_8).trim(); - } - - // write the file content of a certain commit into the output file stream - public static void showFileContentOfCommit(String repoPath, String commitHash, String filePath, OutputStream outputStream) throws IOException, InterruptedException { - byte[] fileContent = executeGitCommand(repoPath, "show", commitHash + ":" + filePath); - outputStream.write(fileContent); - } - - // get the file tree of a certain commit as a Map - public static Map getFileTreeHierarchy(String repoPath, String commitHash) throws IOException, InterruptedException { - String treeOutput = new String(executeGitCommand(repoPath, "ls-tree", "-r", commitHash), BYTE_2_STRING_CHARSET); - return parseFileTree(treeOutput); - } - - // utility function to parse the file tree as the map - private static Map parseFileTree(String treeOutput) { - Map fileTree = new HashMap<>(); - StringTokenizer st = new StringTokenizer(treeOutput, "\n"); - while (st.hasMoreTokens()) { - String line = st.nextToken(); - String[] parts = line.split("\\s+"); - - if (parts.length > 3) { - String type = parts[1]; // "blob" for files, "tree" for directories - String path = parts[3]; - - if (type.equals("blob")) { - String[] pathParts = path.split("/"); - addToFileTree(fileTree, pathParts, 0); - } - } - } - return fileTree; - } - - private static void addToFileTree(Map tree, String[] pathParts, int index) { - if (index == pathParts.length - 1) { - // It's a file, add it to the map - tree.put(pathParts[index], "file"); - } else { - // It's a directory, recurse - tree.computeIfAbsent(pathParts[index], k -> new HashMap()); - @SuppressWarnings("unchecked") - Map subTree = (Map) tree.get(pathParts[index]); - addToFileTree(subTree, pathParts, index + 1); - } - } - - // rollback the repo to its latest commit - public static void rollbackToLatestCommit(String repoPath) throws IOException, InterruptedException { - executeGitCommand(repoPath, "reset", "--hard", "HEAD"); - } - - // check if a repo has uncommitted changes. - public static boolean hasUncommittedChanges(String repoPath) throws IOException, InterruptedException { - String statusOutput = new String(executeGitCommand(repoPath, "status", "--porcelain"), BYTE_2_STRING_CHARSET); - return !statusOutput.isEmpty(); - } + public static void recoverToLatestVersion(Path baseRepoPath) throws IOException, GitAPIException { + JGitVersionControl.rollbackToLatestCommit(baseRepoPath); } } diff --git a/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/type/FileNode.java b/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/type/FileNode.java new file mode 100644 index 00000000000..480f4dbaf03 --- /dev/null +++ b/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/type/FileNode.java @@ -0,0 +1,40 @@ +package edu.uci.ics.texera.web.resource.dashboard.user.dataset.type; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.HashSet; +import java.util.Set; + +public class FileNode { + private final Path path; + private final Set children; + + public FileNode(Path path) { + this.path = path; + this.children = new HashSet<>(); + } + + public boolean isFile() { + return Files.isRegularFile(path); + } + + public boolean isDirectory() { + return Files.isDirectory(path); + } + + public Path getPath() { + return path; + } + + public void addChildNode(FileNode child) { + if (!child.getPath().getParent().equals(this.path)) { + throw new IllegalArgumentException("Child node is not a direct subpath of the parent node"); + } + this.children.add(child); + } + + public Set getChildren() { + return children; + } + +} diff --git a/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/utils/JGitVersionControl.java b/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/utils/JGitVersionControl.java new file mode 100644 index 00000000000..19d6e4a2e1f --- /dev/null +++ b/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/utils/JGitVersionControl.java @@ -0,0 +1,151 @@ +package edu.uci.ics.texera.web.resource.dashboard.user.dataset.utils; + +import edu.uci.ics.texera.web.resource.dashboard.user.dataset.type.FileNode; +import org.eclipse.jgit.api.Git; +import org.eclipse.jgit.api.ResetCommand; +import org.eclipse.jgit.api.Status; +import org.eclipse.jgit.api.errors.GitAPIException; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.lib.ObjectLoader; +import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.revwalk.RevWalk; +import org.eclipse.jgit.storage.file.FileRepositoryBuilder; +import org.eclipse.jgit.treewalk.TreeWalk; +import org.eclipse.jgit.treewalk.filter.PathFilter; + +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +public class JGitVersionControl { + + public static void initRepo(Path path) throws GitAPIException, IOException { + File gitDir = path.resolve(".git").toFile(); + if (gitDir.exists()) { + throw new IOException("Repository already exists at " + path); + } else { + Git.init().setDirectory(path.toFile()).call().close(); + } + } + + public static void showFileContentOfCommit(Path repoPath, String commitHash, Path filePath, OutputStream outputStream) throws IOException, GitAPIException { + if (!filePath.startsWith(repoPath)) { + throw new IllegalArgumentException("File path must be under the repository path."); + } + + String relativePath = repoPath.relativize(filePath).toString(); + if (Files.isDirectory(filePath)) { + throw new IllegalArgumentException("File path points to a directory, not a file."); + } + + try (Repository repository = new FileRepositoryBuilder() + .setGitDir(repoPath.resolve(".git").toFile()) + .build(); + RevWalk revWalk = new RevWalk(repository)) { + + RevCommit commit = revWalk.parseCommit(repository.resolve(commitHash)); + TreeWalk treeWalk = TreeWalk.forPath(repository, relativePath, commit.getTree()); + + if (treeWalk != null) { + ObjectId objectId = treeWalk.getObjectId(0); + ObjectLoader loader = repository.open(objectId); + + loader.copyTo(outputStream); + } else { + throw new IOException("File not found in commit: " + filePath); + } + } + } + + public static Set getFileTreeOfCommit(Path repoPath, String commitHash) throws Exception { + Map pathToFileNodeMap = new HashMap<>(); + Set rootNodes = new HashSet<>(); + + try (Repository repository = new FileRepositoryBuilder() + .setGitDir(repoPath.resolve(".git").toFile()) + .build(); + Git git = new Git(repository)) { + ObjectId commitId = repository.resolve(commitHash); + + try (RevWalk revWalk = new RevWalk(repository)) { + RevCommit commit = revWalk.parseCommit(commitId); + try (TreeWalk treeWalk = new TreeWalk(repository)) { + treeWalk.addTree(commit.getTree()); + treeWalk.setRecursive(false); // Only walk this commit's root level + + while (treeWalk.next()) { + String pathStr = treeWalk.getPathString(); + Path path = repoPath.resolve(pathStr); // Resolve against repoPath for accuracy + FileNode node = new FileNode(path); + + // Here we're directly working with root level nodes + if (!treeWalk.isSubtree() || treeWalk.getDepth() == 0) { + rootNodes.add(node); + } else { + // For deeper levels, establish parent-child relationships + String parentPathStr = path.getParent().toString(); + FileNode parentNode = pathToFileNodeMap.getOrDefault(parentPathStr, new FileNode(path.getParent())); + parentNode.addChildNode(node); + pathToFileNodeMap.putIfAbsent(parentPathStr, parentNode); + } + + pathToFileNodeMap.put(pathStr, node); + } + } + } + } + + return rootNodes; // Return the set of FileNode objects at the root level of the repository for the specific commit + } + + public static String addAndCommit(Path repoPath, String commitMessage) throws IOException, GitAPIException { + FileRepositoryBuilder builder = new FileRepositoryBuilder(); + try (Repository repository = builder.setGitDir(repoPath.resolve(".git").toFile()) + .readEnvironment() // scan environment GIT_* variables + .findGitDir() // scan up the file system tree + .build()) { + + try (Git git = new Git(repository)) { + // Add all files to the staging area + git.add().addFilepattern(".").call(); + + // Commit the changes + RevCommit commit = git.commit().setMessage(commitMessage).call(); + + // Return the commit hash + return commit.getId().getName(); + } + } + } + + public static void rollbackToLatestCommit(Path repoPath) throws IOException, GitAPIException { + try (Repository repository = new FileRepositoryBuilder() + .setGitDir(repoPath.resolve(".git").toFile()) + .build(); + Git git = new Git(repository)) { + + git.reset().setMode(ResetCommand.ResetType.HARD).call(); + } + } + + public static boolean hasUncommittedChanges(Path repoPath) throws IOException, GitAPIException { + try (Repository repository = new FileRepositoryBuilder() + .setGitDir(repoPath.resolve(".git").toFile()) + .readEnvironment() + .findGitDir() + .build(); + Git git = new Git(repository)) { + + Status status = git.status().call(); + return !status.isClean(); + } + } +} From a0303cc5b15968cc935d8284eb9d326b9243fe7d Mon Sep 17 00:00:00 2001 From: Jiadong Bai Date: Mon, 12 Feb 2024 23:53:19 -0800 Subject: [PATCH 04/14] add spec --- core/amber/build.sbt | 3 + ...ionControlLocalFileStorageServiceSpec.java | 72 +++++++++++++++++++ 2 files changed, 75 insertions(+) create mode 100644 core/amber/src/test/java/edu/uci/ics/texera/web/resource/dashboard/user/dataset/GitVersionControlLocalFileStorageServiceSpec.java diff --git a/core/amber/build.sbt b/core/amber/build.sbt index 279aab6f0c3..2a4847cfefa 100644 --- a/core/amber/build.sbt +++ b/core/amber/build.sbt @@ -186,6 +186,9 @@ libraryDependencies += "org.scalamock" %% "scalamock" % "5.2.0" % Test libraryDependencies += "ch.vorburger.mariaDB4j" % "mariaDB4j" % "2.4.0" % Test // https://www.scalatest.org/getting_started_with_fun_suite libraryDependencies += "org.scalatest" %% "scalatest" % "3.2.15" % Test +// JUnit related dependencies +libraryDependencies += "junit" % "junit" % "4.13.2" % Test // JUnit dependency for Java tests +libraryDependencies += "com.novocode" % "junit-interface" % "0.11" % Test // SBT interface for JUnit ///////////////////////////////////////////////////////////////////////////// // Workflow version control related diff --git a/core/amber/src/test/java/edu/uci/ics/texera/web/resource/dashboard/user/dataset/GitVersionControlLocalFileStorageServiceSpec.java b/core/amber/src/test/java/edu/uci/ics/texera/web/resource/dashboard/user/dataset/GitVersionControlLocalFileStorageServiceSpec.java new file mode 100644 index 00000000000..70715041cea --- /dev/null +++ b/core/amber/src/test/java/edu/uci/ics/texera/web/resource/dashboard/user/dataset/GitVersionControlLocalFileStorageServiceSpec.java @@ -0,0 +1,72 @@ +package edu.uci.ics.texera.web.resource.dashboard.user.dataset; + +import edu.uci.ics.texera.web.resource.dashboard.user.dataset.service.GitVersionControlLocalFileStorageService; +import org.eclipse.jgit.api.errors.GitAPIException; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.Assert; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +public class GitVersionControlLocalFileStorageServiceSpec { + + private Path testRepoPath; + private final String testFileName = "testFile.txt"; + private final String testFileContent = "This is a test file"; + + @Before + public void setUp() throws IOException { + // Create a temporary directory for the repository + testRepoPath = Files.createTempDirectory("testRepo"); + } + + @After + public void tearDown() throws IOException { + // Clean up the test repository directory + GitVersionControlLocalFileStorageService.deleteDirectory(testRepoPath); + } + + @Test + public void testInitRepo() throws IOException, GitAPIException { + GitVersionControlLocalFileStorageService.initRepo(testRepoPath); + Assert.assertTrue(Files.exists(testRepoPath.resolve(".git"))); + } + + @Test + public void testWriteAndDeleteFile() throws IOException { + Path filePath = testRepoPath.resolve(testFileName); + try (ByteArrayInputStream input = new ByteArrayInputStream(testFileContent.getBytes())) { + GitVersionControlLocalFileStorageService.writeFile(filePath, input); + } + Assert.assertTrue(Files.exists(filePath)); + + GitVersionControlLocalFileStorageService.deleteFile(filePath); + Assert.assertFalse(Files.exists(filePath)); + } + + @Test + public void testRetrieveFileContentOfVersion() throws IOException, GitAPIException { + // Initialize the repository and write a test file + GitVersionControlLocalFileStorageService.initRepo(testRepoPath); + Path filePath = testRepoPath.resolve(testFileName); + try (ByteArrayInputStream input = new ByteArrayInputStream(testFileContent.getBytes())) { + GitVersionControlLocalFileStorageService.writeFile(filePath, input); + } + + // Commit the test file to the repository + String commitHash = GitVersionControlLocalFileStorageService.createVersion(testRepoPath, "Initial commit"); + + // Retrieve the file content of the committed version + ByteArrayOutputStream output = new ByteArrayOutputStream(); + GitVersionControlLocalFileStorageService.retrieveFileContentOfVersion(testRepoPath, commitHash, filePath, output); + String retrievedContent = output.toString(); + + Assert.assertEquals(testFileContent, retrievedContent); + } +} From 0f11dafb31a0b4c3e3bd9f850e5e5b22c93fe0e3 Mon Sep 17 00:00:00 2001 From: Jiadong Bai Date: Tue, 13 Feb 2024 14:47:25 -0800 Subject: [PATCH 05/14] add unit tests --- ...VersionControlLocalFileStorageService.java | 33 ++-- .../dashboard/user/dataset/type/FileNode.java | 14 ++ .../dataset/utils/JGitVersionControl.java | 112 +++++++++---- ...ionControlLocalFileStorageServiceSpec.java | 147 ++++++++++++++---- 4 files changed, 233 insertions(+), 73 deletions(-) diff --git a/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/service/GitVersionControlLocalFileStorageService.java b/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/service/GitVersionControlLocalFileStorageService.java index 65b7eba695b..d000d0dd792 100644 --- a/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/service/GitVersionControlLocalFileStorageService.java +++ b/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/service/GitVersionControlLocalFileStorageService.java @@ -4,25 +4,15 @@ import edu.uci.ics.texera.web.resource.dashboard.user.dataset.utils.JGitVersionControl; import org.eclipse.jgit.api.errors.GitAPIException; -import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.nio.charset.Charset; -import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.Paths; import java.nio.file.StandardCopyOption; -import java.util.ArrayList; -import java.util.Collections; import java.util.Comparator; -import java.util.HashMap; -import java.util.List; -import java.util.Map; import java.util.Set; -import java.util.StringTokenizer; /** * Git-based implementation of the VersionControlFileStorageService, using local file storage. @@ -31,6 +21,7 @@ public class GitVersionControlLocalFileStorageService { /** * Writes content from the InputStream to a file at the given path. + * This function WILL create the missing parent directory along the path * * @param filePath The path within the repository to write the file. * @param inputStream The InputStream from which to read the content. @@ -71,11 +62,12 @@ public static void deleteDirectory(Path directoryPath) throws IOException { * Initializes a new repository for version control at the specified path. * * @param baseRepoPath Path to initialize the repository at. + * @return The branch identifier * @throws IOException If an I/O error occurs. * @throws GitAPIException If the JGit operation is interrupted. */ - public static void initRepo(Path baseRepoPath) throws IOException, GitAPIException { - JGitVersionControl.initRepo(baseRepoPath); + public static String initRepo(Path baseRepoPath) throws IOException, GitAPIException { + return JGitVersionControl.initRepo(baseRepoPath); } /** @@ -118,13 +110,24 @@ public static void retrieveFileContentOfVersion(Path baseRepoPath, String commit } /** - * Recovers the repository to its latest version, discarding any uncommitted changes. + * Check if there is any uncommitted change in the given repo + * @param repoPath The repository path + * @return True if there are uncommitted changes. + * @throws GitAPIException + * @throws IOException + */ + public static boolean hasUncommittedChanges(Path repoPath) throws GitAPIException, IOException { + return JGitVersionControl.hasUncommittedChanges(repoPath); + } + + /** + * Recovers the repository to its latest version, discarding any uncommitted changes if any. * * @param baseRepoPath The repository path. * @throws IOException If an I/O error occurs. * @throws GitAPIException If the operation is interrupted. */ - public static void recoverToLatestVersion(Path baseRepoPath) throws IOException, GitAPIException { - JGitVersionControl.rollbackToLatestCommit(baseRepoPath); + public static void discardUncommittedChanges(Path baseRepoPath) throws IOException, GitAPIException { + JGitVersionControl.discardUncommittedChanges(baseRepoPath); } } diff --git a/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/type/FileNode.java b/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/type/FileNode.java index 480f4dbaf03..4e4ebeb570a 100644 --- a/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/type/FileNode.java +++ b/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/type/FileNode.java @@ -3,6 +3,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.HashSet; +import java.util.Objects; import java.util.Set; public class FileNode { @@ -37,4 +38,17 @@ public Set getChildren() { return children; } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + FileNode fileNode = (FileNode) o; + return Objects.equals(path, fileNode.path) && + Objects.equals(children, fileNode.children); + } + + @Override + public int hashCode() { + return Objects.hash(path, children); + } } diff --git a/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/utils/JGitVersionControl.java b/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/utils/JGitVersionControl.java index 19d6e4a2e1f..4f2d5f30dd1 100644 --- a/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/utils/JGitVersionControl.java +++ b/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/utils/JGitVersionControl.java @@ -6,34 +6,48 @@ import org.eclipse.jgit.api.Status; import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.ObjectLoader; import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevWalk; import org.eclipse.jgit.storage.file.FileRepositoryBuilder; import org.eclipse.jgit.treewalk.TreeWalk; -import org.eclipse.jgit.treewalk.filter.PathFilter; import java.io.File; import java.io.IOException; import java.io.OutputStream; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.Paths; +import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; public class JGitVersionControl { - public static void initRepo(Path path) throws GitAPIException, IOException { + public static String initRepo(Path path) throws GitAPIException, IOException { File gitDir = path.resolve(".git").toFile(); if (gitDir.exists()) { throw new IOException("Repository already exists at " + path); } else { - Git.init().setDirectory(path.toFile()).call().close(); + try (Git git = Git.init().setDirectory(path.toFile()).call()) { + // Repository initialization logic remains the same + + // Retrieve the default branch name + Ref head = git.getRepository().exactRef("HEAD"); + if (head != null && head.getTarget() != null) { + String refName = head.getTarget().getName(); + // HEAD will be in the form of 'ref: refs/heads/defaultBranchName' + if (refName.startsWith("refs/heads/")) { + return refName.substring("refs/heads/".length()); + } + } + } } + return null; // or throw an exception if preferred } public static void showFileContentOfCommit(Path repoPath, String commitHash, Path filePath, OutputStream outputStream) throws IOException, GitAPIException { @@ -72,38 +86,48 @@ public static Set getFileTreeOfCommit(Path repoPath, String commitHash try (Repository repository = new FileRepositoryBuilder() .setGitDir(repoPath.resolve(".git").toFile()) .build(); - Git git = new Git(repository)) { + RevWalk revWalk = new RevWalk(repository)) { ObjectId commitId = repository.resolve(commitHash); - - try (RevWalk revWalk = new RevWalk(repository)) { - RevCommit commit = revWalk.parseCommit(commitId); - try (TreeWalk treeWalk = new TreeWalk(repository)) { - treeWalk.addTree(commit.getTree()); - treeWalk.setRecursive(false); // Only walk this commit's root level - - while (treeWalk.next()) { - String pathStr = treeWalk.getPathString(); - Path path = repoPath.resolve(pathStr); // Resolve against repoPath for accuracy - FileNode node = new FileNode(path); - - // Here we're directly working with root level nodes - if (!treeWalk.isSubtree() || treeWalk.getDepth() == 0) { - rootNodes.add(node); - } else { - // For deeper levels, establish parent-child relationships - String parentPathStr = path.getParent().toString(); - FileNode parentNode = pathToFileNodeMap.getOrDefault(parentPathStr, new FileNode(path.getParent())); - parentNode.addChildNode(node); - pathToFileNodeMap.putIfAbsent(parentPathStr, parentNode); + RevCommit commit = revWalk.parseCommit(commitId); + + try (TreeWalk treeWalk = new TreeWalk(repository)) { + treeWalk.addTree(commit.getTree()); + treeWalk.setRecursive(true); + + while (treeWalk.next()) { + Path fullPath = repoPath.resolve(treeWalk.getPathString()); + String pathStr = fullPath.toString(); + + // Determine if the current path is at the root level + if (treeWalk.getDepth() == 0) { + FileNode rootNode = new FileNode(fullPath); + rootNodes.add(rootNode); + pathToFileNodeMap.put(pathStr, rootNode); + } else { + // For child nodes, find or create the parent node based on the directory structure + Path parentPath = fullPath.getParent(); + String parentPathStr = parentPath.toString(); + FileNode parentNode = pathToFileNodeMap.get(parentPathStr); + + if (parentNode == null) { + parentNode = new FileNode(parentPath); + pathToFileNodeMap.put(parentPathStr, parentNode); + // Determine if this parent should be added to rootNodes + if (parentPath.getParent().equals(repoPath)) { + rootNodes.add(parentNode); + } } - pathToFileNodeMap.put(pathStr, node); + FileNode childNode = new FileNode(fullPath); + parentNode.addChildNode(childNode); + // Map child node to its path for potential future children + pathToFileNodeMap.put(pathStr, childNode); } } } } - return rootNodes; // Return the set of FileNode objects at the root level of the repository for the specific commit + return rootNodes; } public static String addAndCommit(Path repoPath, String commitMessage) throws IOException, GitAPIException { @@ -126,13 +150,17 @@ public static String addAndCommit(Path repoPath, String commitMessage) throws IO } } - public static void rollbackToLatestCommit(Path repoPath) throws IOException, GitAPIException { + public static void discardUncommittedChanges(Path repoPath) throws IOException, GitAPIException { try (Repository repository = new FileRepositoryBuilder() .setGitDir(repoPath.resolve(".git").toFile()) .build(); Git git = new Git(repository)) { + // Reset hard to discard changes in tracked files git.reset().setMode(ResetCommand.ResetType.HARD).call(); + + // Clean the working directory to remove untracked files + git.clean().setCleanDirectories(true).call(); } } @@ -148,4 +176,30 @@ public static boolean hasUncommittedChanges(Path repoPath) throws IOException, G return !status.isClean(); } } + + public static List getAllCommitHashes(Path repoPath, String branchName) throws IOException, GitAPIException { + List commitHashes = new ArrayList<>(); + + try (Repository repository = new FileRepositoryBuilder() + .setGitDir(repoPath.resolve(".git").toFile()) + .build(); + RevWalk revWalk = new RevWalk(repository)) { + + Ref branchRef = repository.exactRef("refs/heads/" + branchName); + if (branchRef == null) { + throw new IllegalArgumentException("Branch not found: " + branchName); + } + RevCommit startCommit = revWalk.parseCommit(branchRef.getObjectId()); + revWalk.markStart(startCommit); + + for (RevCommit commit : revWalk) { + commitHashes.add(commit.getId().getName()); + } + } + + return commitHashes; + } + + public JGitVersionControl() { + } } diff --git a/core/amber/src/test/java/edu/uci/ics/texera/web/resource/dashboard/user/dataset/GitVersionControlLocalFileStorageServiceSpec.java b/core/amber/src/test/java/edu/uci/ics/texera/web/resource/dashboard/user/dataset/GitVersionControlLocalFileStorageServiceSpec.java index 70715041cea..16daeb527da 100644 --- a/core/amber/src/test/java/edu/uci/ics/texera/web/resource/dashboard/user/dataset/GitVersionControlLocalFileStorageServiceSpec.java +++ b/core/amber/src/test/java/edu/uci/ics/texera/web/resource/dashboard/user/dataset/GitVersionControlLocalFileStorageServiceSpec.java @@ -1,6 +1,7 @@ package edu.uci.ics.texera.web.resource.dashboard.user.dataset; import edu.uci.ics.texera.web.resource.dashboard.user.dataset.service.GitVersionControlLocalFileStorageService; +import edu.uci.ics.texera.web.resource.dashboard.user.dataset.type.FileNode; import org.eclipse.jgit.api.errors.GitAPIException; import org.junit.After; import org.junit.Before; @@ -12,18 +13,60 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; public class GitVersionControlLocalFileStorageServiceSpec { private Path testRepoPath; - private final String testFileName = "testFile.txt"; - private final String testFileContent = "This is a test file"; + + private String testRepoMainBranchId; + + private List testRepoMasterCommitHashes; + private final String testFile1Name = "testFile1.txt"; + + private final String testFile2Name = "testFile2.txt"; + private final String testDirectoryName = "testDir"; + + private final String testFile1ContentV1 = "This is a test file1 v1"; + private final String testFile1ContentV2 = "This is a test file1 v2"; + private final String testFile1ContentV3 = "This is a test file1 v3"; + + private final String testFile2Content = "This is a test file2 in the testDir"; + + private void writeFileToRepo(Path filePath, String fileContent) throws IOException { + try (ByteArrayInputStream input = new ByteArrayInputStream(fileContent.getBytes())) { + GitVersionControlLocalFileStorageService.writeFile(filePath, input); + } + } @Before - public void setUp() throws IOException { + public void setUp() throws IOException, GitAPIException { // Create a temporary directory for the repository testRepoPath = Files.createTempDirectory("testRepo"); + testRepoMainBranchId = GitVersionControlLocalFileStorageService.initRepo(testRepoPath); + + // Version 1 + Path file1Path = testRepoPath.resolve(testFile1Name); + writeFileToRepo(file1Path, testFile1ContentV1); + String v1Commit = GitVersionControlLocalFileStorageService.createVersion(testRepoPath, "v1"); + + // Version 2 + writeFileToRepo(file1Path, testFile1ContentV2); + String v2Commit = GitVersionControlLocalFileStorageService.createVersion(testRepoPath, "v2"); + + // Version 3 + writeFileToRepo(file1Path, testFile1ContentV3); + String v3Commit = GitVersionControlLocalFileStorageService.createVersion(testRepoPath, "v3"); + + // Retrieve all commit hashes after creating versions + testRepoMasterCommitHashes = new ArrayList<>() {{ + add(v1Commit); + add(v2Commit); + add(v3Commit); + }}; } @After @@ -33,40 +76,86 @@ public void tearDown() throws IOException { } @Test - public void testInitRepo() throws IOException, GitAPIException { - GitVersionControlLocalFileStorageService.initRepo(testRepoPath); - Assert.assertTrue(Files.exists(testRepoPath.resolve(".git"))); + public void testFileContentAcrossVersions() throws IOException, GitAPIException { + // File path for the test file + Path filePath = testRepoPath.resolve(testFile1Name); + + // testRepoMasterCommitHashes is populated in chronological order: v1, v2, v3 + // Retrieve and compare file content for version 1 + ByteArrayOutputStream outputV1 = new ByteArrayOutputStream(); + GitVersionControlLocalFileStorageService.retrieveFileContentOfVersion(testRepoPath, testRepoMasterCommitHashes.get(0), filePath, outputV1); + String retrievedContentV1 = outputV1.toString(); + Assert.assertEquals( + "Content for version 1 does not match", + testFile1ContentV1, + retrievedContentV1); + + // Retrieve and compare file content for version 2 + ByteArrayOutputStream outputV2 = new ByteArrayOutputStream(); + GitVersionControlLocalFileStorageService.retrieveFileContentOfVersion(testRepoPath, testRepoMasterCommitHashes.get(1), filePath, outputV2); + String retrievedContentV2 = outputV2.toString(); + Assert.assertEquals( + "Content for version 2 does not match", + testFile1ContentV2, + retrievedContentV2); + + // Retrieve and compare file content for version 3 + ByteArrayOutputStream outputV3 = new ByteArrayOutputStream(); + GitVersionControlLocalFileStorageService.retrieveFileContentOfVersion(testRepoPath, testRepoMasterCommitHashes.get(2), filePath, outputV3); + String retrievedContentV3 = outputV3.toString(); + Assert.assertEquals( + "Content for version 3 does not match", + testFile1ContentV3, + retrievedContentV3); } @Test - public void testWriteAndDeleteFile() throws IOException { - Path filePath = testRepoPath.resolve(testFileName); - try (ByteArrayInputStream input = new ByteArrayInputStream(testFileContent.getBytes())) { - GitVersionControlLocalFileStorageService.writeFile(filePath, input); - } - Assert.assertTrue(Files.exists(filePath)); + public void testFileTreeRetrieval() throws Exception { + // File path for the test file + Path file1Path = testRepoPath.resolve(testFile1Name); + + Set fileNodes = new HashSet<>() {{ + add(new FileNode(file1Path)); + }}; - GitVersionControlLocalFileStorageService.deleteFile(filePath); - Assert.assertFalse(Files.exists(filePath)); + // first retrieve the latest version's file tree + Assert.assertEquals("File Tree should match", + fileNodes, + GitVersionControlLocalFileStorageService.retrieveFileTreeOfVersion(testRepoPath, testRepoMasterCommitHashes.get(testRepoMasterCommitHashes.size() - 1))); + + // now we add a new file testDir/testFile2.txt + Path testDirPath = testRepoPath.resolve(testDirectoryName); + Path file2Path = testDirPath.resolve(testFile2Name); + writeFileToRepo(file2Path, testFile2Content); + + String v4Hash = GitVersionControlLocalFileStorageService.createVersion(testRepoPath, "v4"); + testRepoMasterCommitHashes.add(v4Hash); + + FileNode dirNode = new FileNode(testDirPath); + dirNode.addChildNode(new FileNode(file2Path)); + // update the expected fileNodes + fileNodes.add(dirNode); + + // check the file tree + Assert.assertEquals( + "File Tree should match", + fileNodes, + GitVersionControlLocalFileStorageService.retrieveFileTreeOfVersion(testRepoPath, v4Hash)); } @Test - public void testRetrieveFileContentOfVersion() throws IOException, GitAPIException { - // Initialize the repository and write a test file - GitVersionControlLocalFileStorageService.initRepo(testRepoPath); - Path filePath = testRepoPath.resolve(testFileName); - try (ByteArrayInputStream input = new ByteArrayInputStream(testFileContent.getBytes())) { - GitVersionControlLocalFileStorageService.writeFile(filePath, input); - } + public void testUncommittedCheckAndRecoverToLatest() throws Exception { + Path tempFilePath = testRepoPath.resolve("tempFile"); + String content = "some random content"; + writeFileToRepo(tempFilePath, content); - // Commit the test file to the repository - String commitHash = GitVersionControlLocalFileStorageService.createVersion(testRepoPath, "Initial commit"); + Assert.assertTrue( + "There should be some uncommitted changes", + GitVersionControlLocalFileStorageService.hasUncommittedChanges(testRepoPath)); - // Retrieve the file content of the committed version - ByteArrayOutputStream output = new ByteArrayOutputStream(); - GitVersionControlLocalFileStorageService.retrieveFileContentOfVersion(testRepoPath, commitHash, filePath, output); - String retrievedContent = output.toString(); + GitVersionControlLocalFileStorageService.discardUncommittedChanges(testRepoPath); - Assert.assertEquals(testFileContent, retrievedContent); + Assert.assertFalse("There should be no uncommitted changes", + GitVersionControlLocalFileStorageService.hasUncommittedChanges(testRepoPath)); } } From 34693c2e4588d1aff7cc78d7016a74d686990c6c Mon Sep 17 00:00:00 2001 From: Jiadong Bai Date: Tue, 13 Feb 2024 16:41:34 -0800 Subject: [PATCH 06/14] fix the test --- ...VersionControlLocalFileStorageService.java | 34 +++++++++++++++++-- ...ionControlLocalFileStorageServiceSpec.java | 20 +++++++++-- 2 files changed, 48 insertions(+), 6 deletions(-) diff --git a/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/service/GitVersionControlLocalFileStorageService.java b/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/service/GitVersionControlLocalFileStorageService.java index d000d0dd792..b9b657d173e 100644 --- a/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/service/GitVersionControlLocalFileStorageService.java +++ b/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/service/GitVersionControlLocalFileStorageService.java @@ -2,6 +2,7 @@ import edu.uci.ics.texera.web.resource.dashboard.user.dataset.type.FileNode; import edu.uci.ics.texera.web.resource.dashboard.user.dataset.utils.JGitVersionControl; +import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.errors.GitAPIException; import java.io.File; @@ -9,6 +10,7 @@ import java.io.InputStream; import java.io.OutputStream; import java.nio.file.Files; +import java.nio.file.NoSuchFileException; import java.nio.file.Path; import java.nio.file.StandardCopyOption; import java.util.Comparator; @@ -38,11 +40,15 @@ public static void writeFile(Path filePath, InputStream inputStream) throws IOEx * @param filePath The path of the file to delete. * @throws IOException If an I/O error occurs. */ - public static void deleteFile(Path filePath) throws IOException { + public static void deleteFile(Path repoPath, Path filePath) throws IOException, GitAPIException { if (Files.isDirectory(filePath)) { throw new IllegalArgumentException("Provided path is a directory, not a file: " + filePath); } - Files.deleteIfExists(filePath); + Files.delete(filePath); + try (Git git = Git.open(repoPath.toFile())) { + String relativePath = repoPath.relativize(filePath).toString(); + git.rm().addFilepattern(relativePath).call(); // Stages the file deletion + } } /** @@ -51,7 +57,7 @@ public static void deleteFile(Path filePath) throws IOException { * @param directoryPath The path of the directory to delete. * @throws IOException If an I/O error occurs. */ - public static void deleteDirectory(Path directoryPath) throws IOException { + public static void deleteRepo(Path directoryPath) throws IOException { Files.walk(directoryPath) .sorted(Comparator.reverseOrder()) .map(Path::toFile) @@ -83,6 +89,28 @@ public static String createVersion(Path baseRepoPath, String versionName) throws return JGitVersionControl.addAndCommit(baseRepoPath, versionName); } + /** + * Executes a group of file operations as a single versioned transaction. The version is bumped after the operations finish. + * + * @param baseRepoPath The repository path. + * @param versionName The name or message associated with the version. + * @param operations The file operations to be executed within this versioned transaction. + * @throws IOException If an I/O error occurs. + * @throws GitAPIException If a Git operation fails. + */ + public static void withCreateVersion(Path baseRepoPath, String versionName, Runnable operations) throws IOException, GitAPIException { + try { + // Execute the provided file operations + operations.run(); + + // After successful execution, create a new version with the specified name + JGitVersionControl.addAndCommit(baseRepoPath, versionName); + } catch (Exception e) { + // Handle possible exceptions (you might want to log or rethrow depending on your use case) + throw e; + } + } + /** * Retrieves the file tree hierarchy of a specific version identified by its commit hash. * diff --git a/core/amber/src/test/java/edu/uci/ics/texera/web/resource/dashboard/user/dataset/GitVersionControlLocalFileStorageServiceSpec.java b/core/amber/src/test/java/edu/uci/ics/texera/web/resource/dashboard/user/dataset/GitVersionControlLocalFileStorageServiceSpec.java index 16daeb527da..92cd2fbc8b8 100644 --- a/core/amber/src/test/java/edu/uci/ics/texera/web/resource/dashboard/user/dataset/GitVersionControlLocalFileStorageServiceSpec.java +++ b/core/amber/src/test/java/edu/uci/ics/texera/web/resource/dashboard/user/dataset/GitVersionControlLocalFileStorageServiceSpec.java @@ -72,7 +72,7 @@ public void setUp() throws IOException, GitAPIException { @After public void tearDown() throws IOException { // Clean up the test repository directory - GitVersionControlLocalFileStorageService.deleteDirectory(testRepoPath); + GitVersionControlLocalFileStorageService.deleteRepo(testRepoPath); } @Test @@ -113,9 +113,9 @@ public void testFileContentAcrossVersions() throws IOException, GitAPIException public void testFileTreeRetrieval() throws Exception { // File path for the test file Path file1Path = testRepoPath.resolve(testFile1Name); - + FileNode file1Node = new FileNode(file1Path); Set fileNodes = new HashSet<>() {{ - add(new FileNode(file1Path)); + add(file1Node); }}; // first retrieve the latest version's file tree @@ -141,6 +141,20 @@ public void testFileTreeRetrieval() throws Exception { "File Tree should match", fileNodes, GitVersionControlLocalFileStorageService.retrieveFileTreeOfVersion(testRepoPath, v4Hash)); + + // now we delete the file1, check the filetree + GitVersionControlLocalFileStorageService.deleteFile(testRepoPath, file1Path); + fileNodes.remove(file1Node); + String v5Hash = GitVersionControlLocalFileStorageService.createVersion(testRepoPath, "v5"); + + ByteArrayOutputStream output = new ByteArrayOutputStream(); + + Assert.assertEquals( + "File1 should be gone", + fileNodes, + GitVersionControlLocalFileStorageService.retrieveFileTreeOfVersion(testRepoPath, v5Hash) + ); + } @Test From d6c73e7838d25afd5d1d7bb74b0b9e4772b106a7 Mon Sep 17 00:00:00 2001 From: Jiadong Bai Date: Tue, 13 Feb 2024 22:24:04 -0800 Subject: [PATCH 07/14] fix createVersion with withCreateVersion --- ...VersionControlLocalFileStorageService.java | 19 +----- ...ionControlLocalFileStorageServiceSpec.java | 67 +++++++++++++------ 2 files changed, 49 insertions(+), 37 deletions(-) diff --git a/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/service/GitVersionControlLocalFileStorageService.java b/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/service/GitVersionControlLocalFileStorageService.java index b9b657d173e..0c709f354f9 100644 --- a/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/service/GitVersionControlLocalFileStorageService.java +++ b/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/service/GitVersionControlLocalFileStorageService.java @@ -76,19 +76,6 @@ public static String initRepo(Path baseRepoPath) throws IOException, GitAPIExcep return JGitVersionControl.initRepo(baseRepoPath); } - /** - * Creates a new version in the repository with the given version name. - * - * @param baseRepoPath The repository path. - * @param versionName The name or message associated with the version. - * @return The commit hash of the created version. - * @throws IOException If an I/O error occurs. - * @throws GitAPIException If the JGit operation is interrupted. - */ - public static String createVersion(Path baseRepoPath, String versionName) throws IOException, GitAPIException { - return JGitVersionControl.addAndCommit(baseRepoPath, versionName); - } - /** * Executes a group of file operations as a single versioned transaction. The version is bumped after the operations finish. * @@ -98,15 +85,13 @@ public static String createVersion(Path baseRepoPath, String versionName) throws * @throws IOException If an I/O error occurs. * @throws GitAPIException If a Git operation fails. */ - public static void withCreateVersion(Path baseRepoPath, String versionName, Runnable operations) throws IOException, GitAPIException { + public static String withCreateVersion(Path baseRepoPath, String versionName, Runnable operations) throws IOException, GitAPIException { try { // Execute the provided file operations operations.run(); - // After successful execution, create a new version with the specified name - JGitVersionControl.addAndCommit(baseRepoPath, versionName); + return JGitVersionControl.addAndCommit(baseRepoPath, versionName); } catch (Exception e) { - // Handle possible exceptions (you might want to log or rethrow depending on your use case) throw e; } } diff --git a/core/amber/src/test/java/edu/uci/ics/texera/web/resource/dashboard/user/dataset/GitVersionControlLocalFileStorageServiceSpec.java b/core/amber/src/test/java/edu/uci/ics/texera/web/resource/dashboard/user/dataset/GitVersionControlLocalFileStorageServiceSpec.java index 92cd2fbc8b8..e83429738c5 100644 --- a/core/amber/src/test/java/edu/uci/ics/texera/web/resource/dashboard/user/dataset/GitVersionControlLocalFileStorageServiceSpec.java +++ b/core/amber/src/test/java/edu/uci/ics/texera/web/resource/dashboard/user/dataset/GitVersionControlLocalFileStorageServiceSpec.java @@ -48,24 +48,43 @@ public void setUp() throws IOException, GitAPIException { testRepoPath = Files.createTempDirectory("testRepo"); testRepoMainBranchId = GitVersionControlLocalFileStorageService.initRepo(testRepoPath); - // Version 1 Path file1Path = testRepoPath.resolve(testFile1Name); - writeFileToRepo(file1Path, testFile1ContentV1); - String v1Commit = GitVersionControlLocalFileStorageService.createVersion(testRepoPath, "v1"); - - // Version 2 - writeFileToRepo(file1Path, testFile1ContentV2); - String v2Commit = GitVersionControlLocalFileStorageService.createVersion(testRepoPath, "v2"); + // Version 1 + String v1Hash = GitVersionControlLocalFileStorageService.withCreateVersion( + testRepoPath, + "v1", + () -> { + try { + writeFileToRepo(file1Path, testFile1ContentV1); + } catch (IOException e) { + throw new RuntimeException(e); + }}); + + String v2Hash = GitVersionControlLocalFileStorageService.withCreateVersion( + testRepoPath, + "v2", + () -> { + try { + writeFileToRepo(file1Path, testFile1ContentV2); + } catch (IOException e) { + throw new RuntimeException(e); + }}); // Version 3 - writeFileToRepo(file1Path, testFile1ContentV3); - String v3Commit = GitVersionControlLocalFileStorageService.createVersion(testRepoPath, "v3"); + String v3Hash = GitVersionControlLocalFileStorageService.withCreateVersion( + testRepoPath, + "v3", + () -> { + try { + writeFileToRepo(file1Path, testFile1ContentV3); + } catch (IOException e) { + throw new RuntimeException(e); + }}); - // Retrieve all commit hashes after creating versions testRepoMasterCommitHashes = new ArrayList<>() {{ - add(v1Commit); - add(v2Commit); - add(v3Commit); + add(v1Hash); + add(v2Hash); + add(v3Hash); }}; } @@ -126,9 +145,14 @@ public void testFileTreeRetrieval() throws Exception { // now we add a new file testDir/testFile2.txt Path testDirPath = testRepoPath.resolve(testDirectoryName); Path file2Path = testDirPath.resolve(testFile2Name); - writeFileToRepo(file2Path, testFile2Content); - String v4Hash = GitVersionControlLocalFileStorageService.createVersion(testRepoPath, "v4"); + String v4Hash = GitVersionControlLocalFileStorageService.withCreateVersion(testRepoPath, "v4", () -> { + try { + writeFileToRepo(file2Path, testFile2Content); + } catch (IOException e) { + throw new RuntimeException(e); + } + }); testRepoMasterCommitHashes.add(v4Hash); FileNode dirNode = new FileNode(testDirPath); @@ -143,12 +167,15 @@ public void testFileTreeRetrieval() throws Exception { GitVersionControlLocalFileStorageService.retrieveFileTreeOfVersion(testRepoPath, v4Hash)); // now we delete the file1, check the filetree - GitVersionControlLocalFileStorageService.deleteFile(testRepoPath, file1Path); - fileNodes.remove(file1Node); - String v5Hash = GitVersionControlLocalFileStorageService.createVersion(testRepoPath, "v5"); - - ByteArrayOutputStream output = new ByteArrayOutputStream(); + String v5Hash = GitVersionControlLocalFileStorageService.withCreateVersion(testRepoPath, "v5", () -> { + try { + GitVersionControlLocalFileStorageService.deleteFile(testRepoPath, file1Path); + } catch (IOException | GitAPIException e) { + throw new RuntimeException(e); + } + }); + fileNodes.remove(file1Node); Assert.assertEquals( "File1 should be gone", fileNodes, From 2b313d71e6bfc10171fe3862581d8256569514ad Mon Sep 17 00:00:00 2001 From: Jiadong Bai Date: Tue, 13 Feb 2024 22:41:51 -0800 Subject: [PATCH 08/14] separate the add and commit --- ...VersionControlLocalFileStorageService.java | 22 ++++++++--------- .../dataset/utils/JGitVersionControl.java | 24 +++++++++++++++---- ...ionControlLocalFileStorageServiceSpec.java | 14 +++++------ 3 files changed, 37 insertions(+), 23 deletions(-) diff --git a/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/service/GitVersionControlLocalFileStorageService.java b/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/service/GitVersionControlLocalFileStorageService.java index 0c709f354f9..7fc93c56396 100644 --- a/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/service/GitVersionControlLocalFileStorageService.java +++ b/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/service/GitVersionControlLocalFileStorageService.java @@ -10,7 +10,6 @@ import java.io.InputStream; import java.io.OutputStream; import java.nio.file.Files; -import java.nio.file.NoSuchFileException; import java.nio.file.Path; import java.nio.file.StandardCopyOption; import java.util.Comparator; @@ -22,37 +21,38 @@ public class GitVersionControlLocalFileStorageService { /** - * Writes content from the InputStream to a file at the given path. + * Writes content from the InputStream to a file at the given path. And stage the file addition/modification to git * This function WILL create the missing parent directory along the path * * @param filePath The path within the repository to write the file. * @param inputStream The InputStream from which to read the content. * @throws IOException If an I/O error occurs. */ - public static void writeFile(Path filePath, InputStream inputStream) throws IOException { + public static void writeFileToRepo(Path repoPath, Path filePath, InputStream inputStream) throws IOException, GitAPIException { Files.createDirectories(filePath.getParent()); Files.copy(inputStream, filePath, StandardCopyOption.REPLACE_EXISTING); + // stage the addition/modification + JGitVersionControl.add(repoPath, filePath); } /** - * Deletes a file at the given path. + * Deletes a file at the given path. If file does not exist, error will be thrown * * @param filePath The path of the file to delete. * @throws IOException If an I/O error occurs. */ - public static void deleteFile(Path repoPath, Path filePath) throws IOException, GitAPIException { + public static void removeFileFromRepo(Path repoPath, Path filePath) throws IOException, GitAPIException { if (Files.isDirectory(filePath)) { throw new IllegalArgumentException("Provided path is a directory, not a file: " + filePath); } Files.delete(filePath); - try (Git git = Git.open(repoPath.toFile())) { - String relativePath = repoPath.relativize(filePath).toString(); - git.rm().addFilepattern(relativePath).call(); // Stages the file deletion - } + + // stage the deletion + JGitVersionControl.rm(repoPath, filePath); } /** - * Deletes the entire directory specified by the path. + * Deletes the entire repository specified by the path. * * @param directoryPath The path of the directory to delete. * @throws IOException If an I/O error occurs. @@ -90,7 +90,7 @@ public static String withCreateVersion(Path baseRepoPath, String versionName, Ru // Execute the provided file operations operations.run(); // After successful execution, create a new version with the specified name - return JGitVersionControl.addAndCommit(baseRepoPath, versionName); + return JGitVersionControl.commit(baseRepoPath, versionName); } catch (Exception e) { throw e; } diff --git a/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/utils/JGitVersionControl.java b/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/utils/JGitVersionControl.java index 4f2d5f30dd1..afb1fcfdfd8 100644 --- a/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/utils/JGitVersionControl.java +++ b/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/utils/JGitVersionControl.java @@ -130,7 +130,24 @@ public static Set getFileTreeOfCommit(Path repoPath, String commitHash return rootNodes; } - public static String addAndCommit(Path repoPath, String commitMessage) throws IOException, GitAPIException { + public static void add(Path repoPath, Path filePath) throws IOException, GitAPIException { + // Open the Git repository + try (Git git = Git.open(repoPath.toFile())) { + // Calculate the file's path relative to the repository root + String relativePath = repoPath.relativize(filePath).normalize().toString().replace("\\", "/"); + // Stage the file addition/modification + git.add().addFilepattern(relativePath).call(); + } + } + + public static void rm(Path repoPath, Path filePath) throws IOException, GitAPIException { + try (Git git = Git.open(repoPath.toFile())) { + String relativePath = repoPath.relativize(filePath).toString(); + git.rm().addFilepattern(relativePath).call(); // Stages the file deletion + } + } + + public static String commit(Path repoPath, String commitMessage) throws IOException, GitAPIException { FileRepositoryBuilder builder = new FileRepositoryBuilder(); try (Repository repository = builder.setGitDir(repoPath.resolve(".git").toFile()) .readEnvironment() // scan environment GIT_* variables @@ -138,10 +155,7 @@ public static String addAndCommit(Path repoPath, String commitMessage) throws IO .build()) { try (Git git = new Git(repository)) { - // Add all files to the staging area - git.add().addFilepattern(".").call(); - - // Commit the changes + // Commit the changes that have been staged RevCommit commit = git.commit().setMessage(commitMessage).call(); // Return the commit hash diff --git a/core/amber/src/test/java/edu/uci/ics/texera/web/resource/dashboard/user/dataset/GitVersionControlLocalFileStorageServiceSpec.java b/core/amber/src/test/java/edu/uci/ics/texera/web/resource/dashboard/user/dataset/GitVersionControlLocalFileStorageServiceSpec.java index e83429738c5..5e949a688a0 100644 --- a/core/amber/src/test/java/edu/uci/ics/texera/web/resource/dashboard/user/dataset/GitVersionControlLocalFileStorageServiceSpec.java +++ b/core/amber/src/test/java/edu/uci/ics/texera/web/resource/dashboard/user/dataset/GitVersionControlLocalFileStorageServiceSpec.java @@ -36,9 +36,9 @@ public class GitVersionControlLocalFileStorageServiceSpec { private final String testFile2Content = "This is a test file2 in the testDir"; - private void writeFileToRepo(Path filePath, String fileContent) throws IOException { + private void writeFileToRepo(Path filePath, String fileContent) throws IOException, GitAPIException { try (ByteArrayInputStream input = new ByteArrayInputStream(fileContent.getBytes())) { - GitVersionControlLocalFileStorageService.writeFile(filePath, input); + GitVersionControlLocalFileStorageService.writeFileToRepo(testRepoPath, filePath, input); } } @@ -56,7 +56,7 @@ public void setUp() throws IOException, GitAPIException { () -> { try { writeFileToRepo(file1Path, testFile1ContentV1); - } catch (IOException e) { + } catch (IOException | GitAPIException e) { throw new RuntimeException(e); }}); @@ -66,7 +66,7 @@ public void setUp() throws IOException, GitAPIException { () -> { try { writeFileToRepo(file1Path, testFile1ContentV2); - } catch (IOException e) { + } catch (IOException | GitAPIException e) { throw new RuntimeException(e); }}); @@ -77,7 +77,7 @@ public void setUp() throws IOException, GitAPIException { () -> { try { writeFileToRepo(file1Path, testFile1ContentV3); - } catch (IOException e) { + } catch (IOException | GitAPIException e) { throw new RuntimeException(e); }}); @@ -149,7 +149,7 @@ public void testFileTreeRetrieval() throws Exception { String v4Hash = GitVersionControlLocalFileStorageService.withCreateVersion(testRepoPath, "v4", () -> { try { writeFileToRepo(file2Path, testFile2Content); - } catch (IOException e) { + } catch (IOException | GitAPIException e) { throw new RuntimeException(e); } }); @@ -169,7 +169,7 @@ public void testFileTreeRetrieval() throws Exception { // now we delete the file1, check the filetree String v5Hash = GitVersionControlLocalFileStorageService.withCreateVersion(testRepoPath, "v5", () -> { try { - GitVersionControlLocalFileStorageService.deleteFile(testRepoPath, file1Path); + GitVersionControlLocalFileStorageService.removeFileFromRepo(testRepoPath, file1Path); } catch (IOException | GitAPIException e) { throw new RuntimeException(e); } From 661227846d9e7d2e4693e8b62ec2270b5631effa Mon Sep 17 00:00:00 2001 From: Jiadong Bai Date: Tue, 13 Feb 2024 23:06:16 -0800 Subject: [PATCH 09/14] add comments --- ...VersionControlLocalFileStorageService.java | 27 +++++++++++++------ .../dataset/utils/JGitVersionControl.java | 4 +-- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/service/GitVersionControlLocalFileStorageService.java b/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/service/GitVersionControlLocalFileStorageService.java index 7fc93c56396..b28ec7247e3 100644 --- a/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/service/GitVersionControlLocalFileStorageService.java +++ b/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/service/GitVersionControlLocalFileStorageService.java @@ -5,6 +5,7 @@ import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.errors.GitAPIException; +import javax.annotation.concurrent.NotThreadSafe; import java.io.File; import java.io.IOException; import java.io.InputStream; @@ -23,7 +24,8 @@ public class GitVersionControlLocalFileStorageService { /** * Writes content from the InputStream to a file at the given path. And stage the file addition/modification to git * This function WILL create the missing parent directory along the path - * + + * This method is NOT THREAD SAFE, as it did the file I/O and the git add operation * @param filePath The path within the repository to write the file. * @param inputStream The InputStream from which to read the content. * @throws IOException If an I/O error occurs. @@ -37,7 +39,8 @@ public static void writeFileToRepo(Path repoPath, Path filePath, InputStream inp /** * Deletes a file at the given path. If file does not exist, error will be thrown - * + + * This method is NOT THREAD SAFE, as it did the file I/O and the git rm operation * @param filePath The path of the file to delete. * @throws IOException If an I/O error occurs. */ @@ -53,7 +56,8 @@ public static void removeFileFromRepo(Path repoPath, Path filePath) throws IOExc /** * Deletes the entire repository specified by the path. - * + + * This method is NOT THREAD SAFE, as it did the file I/O recursively * @param directoryPath The path of the directory to delete. * @throws IOException If an I/O error occurs. */ @@ -66,7 +70,8 @@ public static void deleteRepo(Path directoryPath) throws IOException { /** * Initializes a new repository for version control at the specified path. - * + + * This method is THREAD SAFE * @param baseRepoPath Path to initialize the repository at. * @return The branch identifier * @throws IOException If an I/O error occurs. @@ -78,7 +83,8 @@ public static String initRepo(Path baseRepoPath) throws IOException, GitAPIExcep /** * Executes a group of file operations as a single versioned transaction. The version is bumped after the operations finish. - * + + * This method is NOT THREAD SAFE as it potentially does lots of file I/O along with git operations * @param baseRepoPath The repository path. * @param versionName The name or message associated with the version. * @param operations The file operations to be executed within this versioned transaction. @@ -98,7 +104,8 @@ public static String withCreateVersion(Path baseRepoPath, String versionName, Ru /** * Retrieves the file tree hierarchy of a specific version identified by its commit hash. - * + + * This method is THREAD SAFE * @param baseRepoPath The repository path. * @param versionCommitHashVal The commit hash of the version. * @return A set of file nodes at the root level of the given repo at given version @@ -110,7 +117,8 @@ public static Set retrieveFileTreeOfVersion(Path baseRepoPath, String /** * Retrieves the content of a specific file from a specific version identified by its commit hash. * Writes the file content to the provided OutputStream. - * + + * This method is THREAD SAFE * @param baseRepoPath The repository path. * @param commitHash The commit hash of the version from which the file content is retrieved. * @param filePath The path of the file within the repository. @@ -124,6 +132,8 @@ public static void retrieveFileContentOfVersion(Path baseRepoPath, String commit /** * Check if there is any uncommitted change in the given repo + + * This method is THREAD SAFE * @param repoPath The repository path * @return True if there are uncommitted changes. * @throws GitAPIException @@ -135,7 +145,8 @@ public static boolean hasUncommittedChanges(Path repoPath) throws GitAPIExceptio /** * Recovers the repository to its latest version, discarding any uncommitted changes if any. - * + + * This method is NOT THREAD SAFE * @param baseRepoPath The repository path. * @throws IOException If an I/O error occurs. * @throws GitAPIException If the operation is interrupted. diff --git a/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/utils/JGitVersionControl.java b/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/utils/JGitVersionControl.java index afb1fcfdfd8..7433af04754 100644 --- a/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/utils/JGitVersionControl.java +++ b/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/utils/JGitVersionControl.java @@ -34,8 +34,6 @@ public static String initRepo(Path path) throws GitAPIException, IOException { throw new IOException("Repository already exists at " + path); } else { try (Git git = Git.init().setDirectory(path.toFile()).call()) { - // Repository initialization logic remains the same - // Retrieve the default branch name Ref head = git.getRepository().exactRef("HEAD"); if (head != null && head.getTarget() != null) { @@ -47,7 +45,7 @@ public static String initRepo(Path path) throws GitAPIException, IOException { } } } - return null; // or throw an exception if preferred + return null; } public static void showFileContentOfCommit(Path repoPath, String commitHash, Path filePath, OutputStream outputStream) throws IOException, GitAPIException { From 6768dbd0ecb25d020702866cd5756f50219d8b22 Mon Sep 17 00:00:00 2001 From: Jiadong Bai Date: Tue, 13 Feb 2024 23:10:39 -0800 Subject: [PATCH 10/14] remove redundancy --- .../dataset/utils/JGitVersionControl.java | 24 ------------------- 1 file changed, 24 deletions(-) diff --git a/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/utils/JGitVersionControl.java b/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/utils/JGitVersionControl.java index 7433af04754..2dfaa3748a3 100644 --- a/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/utils/JGitVersionControl.java +++ b/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/utils/JGitVersionControl.java @@ -129,7 +129,6 @@ public static Set getFileTreeOfCommit(Path repoPath, String commitHash } public static void add(Path repoPath, Path filePath) throws IOException, GitAPIException { - // Open the Git repository try (Git git = Git.open(repoPath.toFile())) { // Calculate the file's path relative to the repository root String relativePath = repoPath.relativize(filePath).normalize().toString().replace("\\", "/"); @@ -189,29 +188,6 @@ public static boolean hasUncommittedChanges(Path repoPath) throws IOException, G } } - public static List getAllCommitHashes(Path repoPath, String branchName) throws IOException, GitAPIException { - List commitHashes = new ArrayList<>(); - - try (Repository repository = new FileRepositoryBuilder() - .setGitDir(repoPath.resolve(".git").toFile()) - .build(); - RevWalk revWalk = new RevWalk(repository)) { - - Ref branchRef = repository.exactRef("refs/heads/" + branchName); - if (branchRef == null) { - throw new IllegalArgumentException("Branch not found: " + branchName); - } - RevCommit startCommit = revWalk.parseCommit(branchRef.getObjectId()); - revWalk.markStart(startCommit); - - for (RevCommit commit : revWalk) { - commitHashes.add(commit.getId().getName()); - } - } - - return commitHashes; - } - public JGitVersionControl() { } } From 41b91260d0fd4c657524d46a624e44f4b0fa8a04 Mon Sep 17 00:00:00 2001 From: Jiadong Bai Date: Thu, 15 Feb 2024 16:49:48 -0800 Subject: [PATCH 11/14] fix some comments --- ...=> GitVersionControlLocalFileStorage.java} | 29 ++--- .../dataset/utils/JGitVersionControl.java | 118 +++++++++--------- ...itVersionControlLocalFileStorageSpec.java} | 40 +++--- 3 files changed, 88 insertions(+), 99 deletions(-) rename core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/service/{GitVersionControlLocalFileStorageService.java => GitVersionControlLocalFileStorage.java} (86%) rename core/amber/src/test/java/edu/uci/ics/texera/web/resource/dashboard/user/dataset/{GitVersionControlLocalFileStorageServiceSpec.java => GitVersionControlLocalFileStorageSpec.java} (73%) diff --git a/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/service/GitVersionControlLocalFileStorageService.java b/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/service/GitVersionControlLocalFileStorage.java similarity index 86% rename from core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/service/GitVersionControlLocalFileStorageService.java rename to core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/service/GitVersionControlLocalFileStorage.java index b28ec7247e3..354dfcf0115 100644 --- a/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/service/GitVersionControlLocalFileStorageService.java +++ b/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/service/GitVersionControlLocalFileStorage.java @@ -2,10 +2,8 @@ import edu.uci.ics.texera.web.resource.dashboard.user.dataset.type.FileNode; import edu.uci.ics.texera.web.resource.dashboard.user.dataset.utils.JGitVersionControl; -import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.errors.GitAPIException; -import javax.annotation.concurrent.NotThreadSafe; import java.io.File; import java.io.IOException; import java.io.InputStream; @@ -19,7 +17,7 @@ /** * Git-based implementation of the VersionControlFileStorageService, using local file storage. */ -public class GitVersionControlLocalFileStorageService { +public class GitVersionControlLocalFileStorage { /** * Writes content from the InputStream to a file at the given path. And stage the file addition/modification to git @@ -38,7 +36,8 @@ public static void writeFileToRepo(Path repoPath, Path filePath, InputStream inp } /** - * Deletes a file at the given path. If file does not exist, error will be thrown + * Deletes a file at the given path. + * If the file path is pointing to a directory, or if file does not exist, error will be thrown * This method is NOT THREAD SAFE, as it did the file I/O and the git rm operation * @param filePath The path of the file to delete. @@ -48,8 +47,8 @@ public static void removeFileFromRepo(Path repoPath, Path filePath) throws IOExc if (Files.isDirectory(filePath)) { throw new IllegalArgumentException("Provided path is a directory, not a file: " + filePath); } - Files.delete(filePath); + Files.delete(filePath); // stage the deletion JGitVersionControl.rm(repoPath, filePath); } @@ -92,26 +91,22 @@ public static String initRepo(Path baseRepoPath) throws IOException, GitAPIExcep * @throws GitAPIException If a Git operation fails. */ public static String withCreateVersion(Path baseRepoPath, String versionName, Runnable operations) throws IOException, GitAPIException { - try { - // Execute the provided file operations - operations.run(); - // After successful execution, create a new version with the specified name - return JGitVersionControl.commit(baseRepoPath, versionName); - } catch (Exception e) { - throw e; - } + // Execute the provided file operations + operations.run(); + // After successful execution, create a new version with the specified name + return JGitVersionControl.commit(baseRepoPath, versionName); } /** - * Retrieves the file tree hierarchy of a specific version identified by its commit hash. + * Retrieves the set of file nodes at the root level, identified by its commit hash. * This method is THREAD SAFE * @param baseRepoPath The repository path. * @param versionCommitHashVal The commit hash of the version. * @return A set of file nodes at the root level of the given repo at given version */ - public static Set retrieveFileTreeOfVersion(Path baseRepoPath, String versionCommitHashVal) throws Exception { - return JGitVersionControl.getFileTreeOfCommit(baseRepoPath, versionCommitHashVal); + public static Set retrieveRootFileNodesOfVersion(Path baseRepoPath, String versionCommitHashVal) throws Exception { + return JGitVersionControl.getRootFileNodeOfCommit(baseRepoPath, versionCommitHashVal); } /** @@ -127,7 +122,7 @@ public static Set retrieveFileTreeOfVersion(Path baseRepoPath, String * @throws GitAPIException If the operation is interrupted. */ public static void retrieveFileContentOfVersion(Path baseRepoPath, String commitHash, Path filePath, OutputStream outputStream) throws IOException, GitAPIException { - JGitVersionControl.showFileContentOfCommit(baseRepoPath, commitHash, filePath, outputStream); + JGitVersionControl.readFileContentOfCommit(baseRepoPath, commitHash, filePath, outputStream); } /** diff --git a/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/utils/JGitVersionControl.java b/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/utils/JGitVersionControl.java index 2dfaa3748a3..905149d9e95 100644 --- a/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/utils/JGitVersionControl.java +++ b/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/utils/JGitVersionControl.java @@ -19,10 +19,8 @@ import java.io.OutputStream; import java.nio.file.Files; import java.nio.file.Path; -import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; -import java.util.List; import java.util.Map; import java.util.Set; @@ -32,28 +30,28 @@ public static String initRepo(Path path) throws GitAPIException, IOException { File gitDir = path.resolve(".git").toFile(); if (gitDir.exists()) { throw new IOException("Repository already exists at " + path); - } else { - try (Git git = Git.init().setDirectory(path.toFile()).call()) { - // Retrieve the default branch name - Ref head = git.getRepository().exactRef("HEAD"); - if (head != null && head.getTarget() != null) { - String refName = head.getTarget().getName(); - // HEAD will be in the form of 'ref: refs/heads/defaultBranchName' - if (refName.startsWith("refs/heads/")) { - return refName.substring("refs/heads/".length()); - } - } + } + // try-with-resource make sure the resource is released + try (Git git = Git.init().setDirectory(path.toFile()).call()) { + // Retrieve the default branch name + Ref head = git.getRepository().exactRef("HEAD"); + if (head == null || head.getTarget() == null) { + return null; } + String refName = head.getTarget().getName(); + // HEAD should be in the form of 'ref: refs/heads/defaultBranchName' + if (!refName.startsWith("refs/heads/")) { + return null; + } + return refName.substring("refs/heads/".length()); } - return null; } - public static void showFileContentOfCommit(Path repoPath, String commitHash, Path filePath, OutputStream outputStream) throws IOException, GitAPIException { + public static void readFileContentOfCommit(Path repoPath, String commitHash, Path filePath, OutputStream outputStream) throws IOException { if (!filePath.startsWith(repoPath)) { throw new IllegalArgumentException("File path must be under the repository path."); } - String relativePath = repoPath.relativize(filePath).toString(); if (Files.isDirectory(filePath)) { throw new IllegalArgumentException("File path points to a directory, not a file."); } @@ -64,20 +62,19 @@ public static void showFileContentOfCommit(Path repoPath, String commitHash, Pat RevWalk revWalk = new RevWalk(repository)) { RevCommit commit = revWalk.parseCommit(repository.resolve(commitHash)); - TreeWalk treeWalk = TreeWalk.forPath(repository, relativePath, commit.getTree()); - - if (treeWalk != null) { - ObjectId objectId = treeWalk.getObjectId(0); - ObjectLoader loader = repository.open(objectId); - - loader.copyTo(outputStream); - } else { + TreeWalk treeWalk = + TreeWalk.forPath(repository, repoPath.relativize(filePath).toString(), commit.getTree()); + if (treeWalk == null) { throw new IOException("File not found in commit: " + filePath); } + ObjectId objectId = treeWalk.getObjectId(0); + ObjectLoader loader = repository.open(objectId); + + loader.copyTo(outputStream); } } - public static Set getFileTreeOfCommit(Path repoPath, String commitHash) throws Exception { + public static Set getRootFileNodeOfCommit(Path repoPath, String commitHash) throws Exception { Map pathToFileNodeMap = new HashMap<>(); Set rootNodes = new HashSet<>(); @@ -88,62 +85,59 @@ public static Set getFileTreeOfCommit(Path repoPath, String commitHash ObjectId commitId = repository.resolve(commitHash); RevCommit commit = revWalk.parseCommit(commitId); - try (TreeWalk treeWalk = new TreeWalk(repository)) { - treeWalk.addTree(commit.getTree()); - treeWalk.setRecursive(true); - - while (treeWalk.next()) { - Path fullPath = repoPath.resolve(treeWalk.getPathString()); - String pathStr = fullPath.toString(); - - // Determine if the current path is at the root level - if (treeWalk.getDepth() == 0) { - FileNode rootNode = new FileNode(fullPath); - rootNodes.add(rootNode); - pathToFileNodeMap.put(pathStr, rootNode); - } else { - // For child nodes, find or create the parent node based on the directory structure - Path parentPath = fullPath.getParent(); - String parentPathStr = parentPath.toString(); - FileNode parentNode = pathToFileNodeMap.get(parentPathStr); - - if (parentNode == null) { - parentNode = new FileNode(parentPath); - pathToFileNodeMap.put(parentPathStr, parentNode); - // Determine if this parent should be added to rootNodes - if (parentPath.getParent().equals(repoPath)) { - rootNodes.add(parentNode); - } + // initialize the treeWalk to traverse the file tree + TreeWalk treeWalk = new TreeWalk(repository); + treeWalk.addTree(commit.getTree()); + treeWalk.setRecursive(true); + + while (treeWalk.next()) { + Path fullPath = repoPath.resolve(treeWalk.getPathString()); + String pathStr = fullPath.toString(); + + // Determine if the current path is at the root level + if (treeWalk.getDepth() == 0) { + FileNode rootNode = new FileNode(fullPath); + rootNodes.add(rootNode); + pathToFileNodeMap.put(pathStr, rootNode); + } else { + // For child nodes, find or create the parent node based on the directory structure + Path parentPath = fullPath.getParent(); + String parentPathStr = parentPath.toString(); + FileNode parentNode = pathToFileNodeMap.get(parentPathStr); + + if (parentNode == null) { + parentNode = new FileNode(parentPath); + pathToFileNodeMap.put(parentPathStr, parentNode); + // Determine if this parent should be added to rootNodes + if (parentPath.getParent().equals(repoPath)) { + rootNodes.add(parentNode); } - - FileNode childNode = new FileNode(fullPath); - parentNode.addChildNode(childNode); - // Map child node to its path for potential future children - pathToFileNodeMap.put(pathStr, childNode); } + + FileNode childNode = new FileNode(fullPath); + parentNode.addChildNode(childNode); + // Map child node to its path for potential future children + pathToFileNodeMap.put(pathStr, childNode); } } } - return rootNodes; } public static void add(Path repoPath, Path filePath) throws IOException, GitAPIException { try (Git git = Git.open(repoPath.toFile())) { - // Calculate the file's path relative to the repository root - String relativePath = repoPath.relativize(filePath).normalize().toString().replace("\\", "/"); // Stage the file addition/modification - git.add().addFilepattern(relativePath).call(); + git.add().addFilepattern(repoPath.relativize(filePath).toString()).call(); } } public static void rm(Path repoPath, Path filePath) throws IOException, GitAPIException { try (Git git = Git.open(repoPath.toFile())) { - String relativePath = repoPath.relativize(filePath).toString(); - git.rm().addFilepattern(relativePath).call(); // Stages the file deletion + git.rm().addFilepattern(repoPath.relativize(filePath).toString()).call(); // Stages the file deletion } } + // create a commit, and return the commit hash public static String commit(Path repoPath, String commitMessage) throws IOException, GitAPIException { FileRepositoryBuilder builder = new FileRepositoryBuilder(); try (Repository repository = builder.setGitDir(repoPath.resolve(".git").toFile()) diff --git a/core/amber/src/test/java/edu/uci/ics/texera/web/resource/dashboard/user/dataset/GitVersionControlLocalFileStorageServiceSpec.java b/core/amber/src/test/java/edu/uci/ics/texera/web/resource/dashboard/user/dataset/GitVersionControlLocalFileStorageSpec.java similarity index 73% rename from core/amber/src/test/java/edu/uci/ics/texera/web/resource/dashboard/user/dataset/GitVersionControlLocalFileStorageServiceSpec.java rename to core/amber/src/test/java/edu/uci/ics/texera/web/resource/dashboard/user/dataset/GitVersionControlLocalFileStorageSpec.java index 5e949a688a0..751c5dd66bb 100644 --- a/core/amber/src/test/java/edu/uci/ics/texera/web/resource/dashboard/user/dataset/GitVersionControlLocalFileStorageServiceSpec.java +++ b/core/amber/src/test/java/edu/uci/ics/texera/web/resource/dashboard/user/dataset/GitVersionControlLocalFileStorageSpec.java @@ -1,6 +1,6 @@ package edu.uci.ics.texera.web.resource.dashboard.user.dataset; -import edu.uci.ics.texera.web.resource.dashboard.user.dataset.service.GitVersionControlLocalFileStorageService; +import edu.uci.ics.texera.web.resource.dashboard.user.dataset.service.GitVersionControlLocalFileStorage; import edu.uci.ics.texera.web.resource.dashboard.user.dataset.type.FileNode; import org.eclipse.jgit.api.errors.GitAPIException; import org.junit.After; @@ -18,7 +18,7 @@ import java.util.List; import java.util.Set; -public class GitVersionControlLocalFileStorageServiceSpec { +public class GitVersionControlLocalFileStorageSpec { private Path testRepoPath; @@ -38,7 +38,7 @@ public class GitVersionControlLocalFileStorageServiceSpec { private void writeFileToRepo(Path filePath, String fileContent) throws IOException, GitAPIException { try (ByteArrayInputStream input = new ByteArrayInputStream(fileContent.getBytes())) { - GitVersionControlLocalFileStorageService.writeFileToRepo(testRepoPath, filePath, input); + GitVersionControlLocalFileStorage.writeFileToRepo(testRepoPath, filePath, input); } } @@ -46,11 +46,11 @@ private void writeFileToRepo(Path filePath, String fileContent) throws IOExcepti public void setUp() throws IOException, GitAPIException { // Create a temporary directory for the repository testRepoPath = Files.createTempDirectory("testRepo"); - testRepoMainBranchId = GitVersionControlLocalFileStorageService.initRepo(testRepoPath); + testRepoMainBranchId = GitVersionControlLocalFileStorage.initRepo(testRepoPath); Path file1Path = testRepoPath.resolve(testFile1Name); // Version 1 - String v1Hash = GitVersionControlLocalFileStorageService.withCreateVersion( + String v1Hash = GitVersionControlLocalFileStorage.withCreateVersion( testRepoPath, "v1", () -> { @@ -60,7 +60,7 @@ public void setUp() throws IOException, GitAPIException { throw new RuntimeException(e); }}); - String v2Hash = GitVersionControlLocalFileStorageService.withCreateVersion( + String v2Hash = GitVersionControlLocalFileStorage.withCreateVersion( testRepoPath, "v2", () -> { @@ -71,7 +71,7 @@ public void setUp() throws IOException, GitAPIException { }}); // Version 3 - String v3Hash = GitVersionControlLocalFileStorageService.withCreateVersion( + String v3Hash = GitVersionControlLocalFileStorage.withCreateVersion( testRepoPath, "v3", () -> { @@ -91,7 +91,7 @@ public void setUp() throws IOException, GitAPIException { @After public void tearDown() throws IOException { // Clean up the test repository directory - GitVersionControlLocalFileStorageService.deleteRepo(testRepoPath); + GitVersionControlLocalFileStorage.deleteRepo(testRepoPath); } @Test @@ -102,7 +102,7 @@ public void testFileContentAcrossVersions() throws IOException, GitAPIException // testRepoMasterCommitHashes is populated in chronological order: v1, v2, v3 // Retrieve and compare file content for version 1 ByteArrayOutputStream outputV1 = new ByteArrayOutputStream(); - GitVersionControlLocalFileStorageService.retrieveFileContentOfVersion(testRepoPath, testRepoMasterCommitHashes.get(0), filePath, outputV1); + GitVersionControlLocalFileStorage.retrieveFileContentOfVersion(testRepoPath, testRepoMasterCommitHashes.get(0), filePath, outputV1); String retrievedContentV1 = outputV1.toString(); Assert.assertEquals( "Content for version 1 does not match", @@ -111,7 +111,7 @@ public void testFileContentAcrossVersions() throws IOException, GitAPIException // Retrieve and compare file content for version 2 ByteArrayOutputStream outputV2 = new ByteArrayOutputStream(); - GitVersionControlLocalFileStorageService.retrieveFileContentOfVersion(testRepoPath, testRepoMasterCommitHashes.get(1), filePath, outputV2); + GitVersionControlLocalFileStorage.retrieveFileContentOfVersion(testRepoPath, testRepoMasterCommitHashes.get(1), filePath, outputV2); String retrievedContentV2 = outputV2.toString(); Assert.assertEquals( "Content for version 2 does not match", @@ -120,7 +120,7 @@ public void testFileContentAcrossVersions() throws IOException, GitAPIException // Retrieve and compare file content for version 3 ByteArrayOutputStream outputV3 = new ByteArrayOutputStream(); - GitVersionControlLocalFileStorageService.retrieveFileContentOfVersion(testRepoPath, testRepoMasterCommitHashes.get(2), filePath, outputV3); + GitVersionControlLocalFileStorage.retrieveFileContentOfVersion(testRepoPath, testRepoMasterCommitHashes.get(2), filePath, outputV3); String retrievedContentV3 = outputV3.toString(); Assert.assertEquals( "Content for version 3 does not match", @@ -140,13 +140,13 @@ public void testFileTreeRetrieval() throws Exception { // first retrieve the latest version's file tree Assert.assertEquals("File Tree should match", fileNodes, - GitVersionControlLocalFileStorageService.retrieveFileTreeOfVersion(testRepoPath, testRepoMasterCommitHashes.get(testRepoMasterCommitHashes.size() - 1))); + GitVersionControlLocalFileStorage.retrieveRootFileNodesOfVersion(testRepoPath, testRepoMasterCommitHashes.get(testRepoMasterCommitHashes.size() - 1))); // now we add a new file testDir/testFile2.txt Path testDirPath = testRepoPath.resolve(testDirectoryName); Path file2Path = testDirPath.resolve(testFile2Name); - String v4Hash = GitVersionControlLocalFileStorageService.withCreateVersion(testRepoPath, "v4", () -> { + String v4Hash = GitVersionControlLocalFileStorage.withCreateVersion(testRepoPath, "v4", () -> { try { writeFileToRepo(file2Path, testFile2Content); } catch (IOException | GitAPIException e) { @@ -164,12 +164,12 @@ public void testFileTreeRetrieval() throws Exception { Assert.assertEquals( "File Tree should match", fileNodes, - GitVersionControlLocalFileStorageService.retrieveFileTreeOfVersion(testRepoPath, v4Hash)); + GitVersionControlLocalFileStorage.retrieveRootFileNodesOfVersion(testRepoPath, v4Hash)); // now we delete the file1, check the filetree - String v5Hash = GitVersionControlLocalFileStorageService.withCreateVersion(testRepoPath, "v5", () -> { + String v5Hash = GitVersionControlLocalFileStorage.withCreateVersion(testRepoPath, "v5", () -> { try { - GitVersionControlLocalFileStorageService.removeFileFromRepo(testRepoPath, file1Path); + GitVersionControlLocalFileStorage.removeFileFromRepo(testRepoPath, file1Path); } catch (IOException | GitAPIException e) { throw new RuntimeException(e); } @@ -179,7 +179,7 @@ public void testFileTreeRetrieval() throws Exception { Assert.assertEquals( "File1 should be gone", fileNodes, - GitVersionControlLocalFileStorageService.retrieveFileTreeOfVersion(testRepoPath, v5Hash) + GitVersionControlLocalFileStorage.retrieveRootFileNodesOfVersion(testRepoPath, v5Hash) ); } @@ -192,11 +192,11 @@ public void testUncommittedCheckAndRecoverToLatest() throws Exception { Assert.assertTrue( "There should be some uncommitted changes", - GitVersionControlLocalFileStorageService.hasUncommittedChanges(testRepoPath)); + GitVersionControlLocalFileStorage.hasUncommittedChanges(testRepoPath)); - GitVersionControlLocalFileStorageService.discardUncommittedChanges(testRepoPath); + GitVersionControlLocalFileStorage.discardUncommittedChanges(testRepoPath); Assert.assertFalse("There should be no uncommitted changes", - GitVersionControlLocalFileStorageService.hasUncommittedChanges(testRepoPath)); + GitVersionControlLocalFileStorage.hasUncommittedChanges(testRepoPath)); } } From e40c9a965b4ed3460be7fc716b458ccb4c978673 Mon Sep 17 00:00:00 2001 From: Jiadong Bai Date: Fri, 16 Feb 2024 15:37:14 -0800 Subject: [PATCH 12/14] fmt fix --- .../dataset/GitVersionControlLocalFileStorageSpec.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/core/amber/src/test/java/edu/uci/ics/texera/web/resource/dashboard/user/dataset/GitVersionControlLocalFileStorageSpec.java b/core/amber/src/test/java/edu/uci/ics/texera/web/resource/dashboard/user/dataset/GitVersionControlLocalFileStorageSpec.java index 751c5dd66bb..32adfa51584 100644 --- a/core/amber/src/test/java/edu/uci/ics/texera/web/resource/dashboard/user/dataset/GitVersionControlLocalFileStorageSpec.java +++ b/core/amber/src/test/java/edu/uci/ics/texera/web/resource/dashboard/user/dataset/GitVersionControlLocalFileStorageSpec.java @@ -22,8 +22,6 @@ public class GitVersionControlLocalFileStorageSpec { private Path testRepoPath; - private String testRepoMainBranchId; - private List testRepoMasterCommitHashes; private final String testFile1Name = "testFile1.txt"; @@ -46,7 +44,7 @@ private void writeFileToRepo(Path filePath, String fileContent) throws IOExcepti public void setUp() throws IOException, GitAPIException { // Create a temporary directory for the repository testRepoPath = Files.createTempDirectory("testRepo"); - testRepoMainBranchId = GitVersionControlLocalFileStorage.initRepo(testRepoPath); + GitVersionControlLocalFileStorage.initRepo(testRepoPath); Path file1Path = testRepoPath.resolve(testFile1Name); // Version 1 @@ -58,7 +56,8 @@ public void setUp() throws IOException, GitAPIException { writeFileToRepo(file1Path, testFile1ContentV1); } catch (IOException | GitAPIException e) { throw new RuntimeException(e); - }}); + } + }); String v2Hash = GitVersionControlLocalFileStorage.withCreateVersion( testRepoPath, From 5bd6effbc7fa25306f0ec395dbf76b66ebb35723 Mon Sep 17 00:00:00 2001 From: Jiadong Bai Date: Fri, 16 Feb 2024 15:40:48 -0800 Subject: [PATCH 13/14] remove redundant --- .../dashboard/user/dataset/utils/JGitVersionControl.java | 3 --- .../user/dataset/GitVersionControlLocalFileStorageSpec.java | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/utils/JGitVersionControl.java b/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/utils/JGitVersionControl.java index 905149d9e95..c547a44aea1 100644 --- a/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/utils/JGitVersionControl.java +++ b/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/dataset/utils/JGitVersionControl.java @@ -181,7 +181,4 @@ public static boolean hasUncommittedChanges(Path repoPath) throws IOException, G return !status.isClean(); } } - - public JGitVersionControl() { - } } diff --git a/core/amber/src/test/java/edu/uci/ics/texera/web/resource/dashboard/user/dataset/GitVersionControlLocalFileStorageSpec.java b/core/amber/src/test/java/edu/uci/ics/texera/web/resource/dashboard/user/dataset/GitVersionControlLocalFileStorageSpec.java index 32adfa51584..7c698ab91a5 100644 --- a/core/amber/src/test/java/edu/uci/ics/texera/web/resource/dashboard/user/dataset/GitVersionControlLocalFileStorageSpec.java +++ b/core/amber/src/test/java/edu/uci/ics/texera/web/resource/dashboard/user/dataset/GitVersionControlLocalFileStorageSpec.java @@ -132,7 +132,7 @@ public void testFileTreeRetrieval() throws Exception { // File path for the test file Path file1Path = testRepoPath.resolve(testFile1Name); FileNode file1Node = new FileNode(file1Path); - Set fileNodes = new HashSet<>() {{ + Set fileNodes = new HashSet() {{ add(file1Node); }}; From 98c9b73be59e1628e0fee2869b4bdc9947ada90f Mon Sep 17 00:00:00 2001 From: Jiadong Bai Date: Fri, 16 Feb 2024 15:44:26 -0800 Subject: [PATCH 14/14] fix lint --- .../user/dataset/GitVersionControlLocalFileStorageSpec.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/amber/src/test/java/edu/uci/ics/texera/web/resource/dashboard/user/dataset/GitVersionControlLocalFileStorageSpec.java b/core/amber/src/test/java/edu/uci/ics/texera/web/resource/dashboard/user/dataset/GitVersionControlLocalFileStorageSpec.java index 7c698ab91a5..67236d96767 100644 --- a/core/amber/src/test/java/edu/uci/ics/texera/web/resource/dashboard/user/dataset/GitVersionControlLocalFileStorageSpec.java +++ b/core/amber/src/test/java/edu/uci/ics/texera/web/resource/dashboard/user/dataset/GitVersionControlLocalFileStorageSpec.java @@ -80,7 +80,7 @@ public void setUp() throws IOException, GitAPIException { throw new RuntimeException(e); }}); - testRepoMasterCommitHashes = new ArrayList<>() {{ + testRepoMasterCommitHashes = new ArrayList() {{ add(v1Hash); add(v2Hash); add(v3Hash);