diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/BaseEntityIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/BaseEntityIT.java
index 046896796584..591034e698a0 100644
--- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/BaseEntityIT.java
+++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/BaseEntityIT.java
@@ -1083,6 +1083,50 @@ void test_entityWithInvalidTag(TestNamespace ns) {
"Patching entity with invalid tag should fail");
}
+ /**
+ * Generic regression: adding a classification tag via PATCH must succeed for any entity
+ * regardless of which optional fields (columns, dataModel, etc.) are populated. New
+ * EntityRepository subclasses get this coverage automatically by extending BaseEntityIT.
+ */
+ @Test
+ void patch_addClassificationTag_200_OK(TestNamespace ns) {
+ if (!supportsTags || !supportsPatch) {
+ return;
+ }
+
+ T entity = createEntity(createMinimalRequest(ns));
+ TagLabel tag = personalDataTagLabel();
+ entity.setTags(List.of(tag));
+
+ T patched = patchEntity(entity.getId().toString(), entity);
+
+ T fetched = getEntityWithFields(patched.getId().toString(), "tags");
+ assertNotNull(fetched.getTags(), "tags should not be null after PATCH");
+ assertTagsContain(fetched.getTags(), List.of(tag));
+ }
+
+ /**
+ * Generic regression: adding a glossary term via PATCH must succeed for any entity. Mirrors
+ * the classification-tag test but with TagSource.GLOSSARY, which exercises a different
+ * server-side application path.
+ */
+ @Test
+ void patch_addGlossaryTerm_200_OK(TestNamespace ns) {
+ if (!supportsTags || !supportsPatch) {
+ return;
+ }
+
+ T entity = createEntity(createMinimalRequest(ns));
+ TagLabel term = glossaryTermLabel();
+ entity.setTags(List.of(term));
+
+ T patched = patchEntity(entity.getId().toString(), entity);
+
+ T fetched = getEntityWithFields(patched.getId().toString(), "tags");
+ assertNotNull(fetched.getTags(), "tags should not be null after PATCH");
+ assertTagsContain(fetched.getTags(), List.of(term));
+ }
+
@Test
void test_tagUpdateOptimization_PUT(TestNamespace ns) {
if (!supportsTags) {
@@ -2008,7 +2052,7 @@ void patch_validEntityOwner_200(TestNamespace ns) {
@Test
void get_deletedEntityVersion_200(TestNamespace ns) {
- if (!supportsSoftDelete || !supportsPatch) return;
+ if (!supportsSoftDelete || !supportsPatch || !supportsGetByVersion) return;
K createRequest = createMinimalRequest(ns);
T entity = createEntity(createRequest);
diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DirectoryResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DirectoryResourceIT.java
index 5396fbb823b8..be5784b10bb8 100644
--- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DirectoryResourceIT.java
+++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DirectoryResourceIT.java
@@ -5,28 +5,165 @@
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.api.parallel.Execution;
import org.junit.jupiter.api.parallel.ExecutionMode;
import org.openmetadata.it.factories.DriveServiceTestFactory;
import org.openmetadata.it.util.SdkClients;
import org.openmetadata.it.util.TestNamespace;
-import org.openmetadata.it.util.TestNamespaceExtension;
+import org.openmetadata.schema.api.data.CreateDirectory;
import org.openmetadata.schema.entity.data.Directory;
import org.openmetadata.schema.entity.services.DriveService;
+import org.openmetadata.schema.type.EntityHistory;
import org.openmetadata.sdk.fluent.Directories;
-
+import org.openmetadata.sdk.models.ListParams;
+import org.openmetadata.sdk.models.ListResponse;
+import org.openmetadata.sdk.services.drives.DirectoryService;
+
+/**
+ * Integration tests for Directory entity operations.
+ *
+ *
Extends BaseEntityIT to inherit common entity tests. Adds Directory-specific tests for
+ * drive-service linkage and naming.
+ */
@Execution(ExecutionMode.CONCURRENT)
-@ExtendWith(TestNamespaceExtension.class)
-public class DirectoryResourceIT {
+public class DirectoryResourceIT extends BaseEntityIT {
+
+ {
+ supportsFollowers = false;
+ supportsDomains = false;
+ supportsDataProducts = false;
+ supportsCustomExtension = false;
+ supportsBulkAPI = false;
+ supportsDataContract = false;
+ }
@BeforeAll
static void setup() {
Directories.setDefaultClient(SdkClients.adminClient());
}
+ // ===================================================================
+ // ABSTRACT METHOD IMPLEMENTATIONS (Required by BaseEntityIT)
+ // ===================================================================
+
+ @Override
+ protected CreateDirectory createMinimalRequest(TestNamespace ns) {
+ DriveService driveService = DriveServiceTestFactory.createGoogleDrive(ns);
+ return new CreateDirectory()
+ .withName(ns.prefix("directory"))
+ .withService(driveService.getFullyQualifiedName())
+ .withDescription("Test directory created by integration test");
+ }
+
+ @Override
+ protected CreateDirectory createRequest(String name, TestNamespace ns) {
+ DriveService driveService = DriveServiceTestFactory.createGoogleDrive(ns);
+ return new CreateDirectory().withName(name).withService(driveService.getFullyQualifiedName());
+ }
+
+ @Override
+ protected Directory createEntity(CreateDirectory createRequest) {
+ return getDirectoryService().create(createRequest);
+ }
+
+ @Override
+ protected Directory getEntity(String id) {
+ return getDirectoryService().get(id);
+ }
+
+ @Override
+ protected Directory getEntityByName(String fqn) {
+ return getDirectoryService().getByName(fqn);
+ }
+
+ @Override
+ protected Directory patchEntity(String id, Directory entity) {
+ return getDirectoryService().update(id, entity);
+ }
+
+ @Override
+ protected void deleteEntity(String id) {
+ getDirectoryService().delete(id);
+ }
+
+ @Override
+ protected void restoreEntity(String id) {
+ getDirectoryService().restore(id);
+ }
+
+ @Override
+ protected void hardDeleteEntity(String id) {
+ Map params = new HashMap<>();
+ params.put("hardDelete", "true");
+ getDirectoryService().delete(id, params);
+ }
+
+ @Override
+ protected String getEntityType() {
+ return "directory";
+ }
+
+ @Override
+ protected void validateCreatedEntity(Directory entity, CreateDirectory createRequest) {
+ assertEquals(createRequest.getName(), entity.getName());
+ assertNotNull(entity.getService(), "Directory must have a service");
+ assertEquals(
+ createRequest.getService(),
+ entity.getService().getFullyQualifiedName(),
+ "Service FQN should match");
+
+ if (createRequest.getDescription() != null) {
+ assertEquals(createRequest.getDescription(), entity.getDescription());
+ }
+
+ assertTrue(
+ entity.getFullyQualifiedName().contains(entity.getName()),
+ "FQN should contain directory name");
+ }
+
+ @Override
+ protected ListResponse listEntities(ListParams params) {
+ return getDirectoryService().list(params);
+ }
+
+ @Override
+ protected Directory getEntityWithFields(String id, String fields) {
+ return getDirectoryService().get(id, fields);
+ }
+
+ @Override
+ protected Directory getEntityByNameWithFields(String fqn, String fields) {
+ return getDirectoryService().getByName(fqn, fields);
+ }
+
+ @Override
+ protected Directory getEntityIncludeDeleted(String id) {
+ return getDirectoryService().get(id, null, "deleted");
+ }
+
+ @Override
+ protected EntityHistory getVersionHistory(UUID id) {
+ return getDirectoryService().getVersionList(id);
+ }
+
+ @Override
+ protected Directory getVersion(UUID id, Double version) {
+ return getDirectoryService().getVersion(id.toString(), version);
+ }
+
+ private DirectoryService getDirectoryService() {
+ return new DirectoryService(SdkClients.adminClient().getHttpClient());
+ }
+
+ // ===================================================================
+ // DIRECTORY-SPECIFIC TESTS
+ // ===================================================================
+
@Test
void test_createAndGetDirectory(TestNamespace ns) {
DriveService driveService = DriveServiceTestFactory.createGoogleDrive(ns);
@@ -57,6 +194,89 @@ void test_createAndGetDirectory(TestNamespace ns) {
assertEquals(created.getDisplayName(), fetched.getDisplayName());
}
+ @Test
+ void test_createDirectoryWithoutService_fails(TestNamespace ns) {
+ String directoryName = ns.prefix("test_directory_no_service");
+
+ assertThrows(
+ Exception.class,
+ () -> Directories.create().name(directoryName).execute(),
+ "Creating directory without service should fail");
+ }
+
+ @Test
+ void test_createDirectoryWithInvalidService_fails(TestNamespace ns) {
+ String directoryName = ns.prefix("test_directory_invalid_service");
+ String invalidServiceFqn = "invalidDriveService_" + ns.prefix("nonexistent");
+
+ assertThrows(
+ Exception.class,
+ () -> Directories.create().name(directoryName).withService(invalidServiceFqn).execute(),
+ "Creating directory with invalid service should fail");
+ }
+
+ @Test
+ void test_directoryFullyQualifiedName(TestNamespace ns) {
+ DriveService driveService = DriveServiceTestFactory.createGoogleDrive(ns);
+ assertNotNull(driveService);
+
+ String directoryName = ns.prefix("test_directory_fqn");
+ Directory created =
+ Directories.create()
+ .name(directoryName)
+ .withService(driveService.getFullyQualifiedName())
+ .execute();
+
+ assertNotNull(created);
+ assertNotNull(created.getFullyQualifiedName());
+ assertTrue(created.getFullyQualifiedName().contains(driveService.getName()));
+ assertTrue(created.getFullyQualifiedName().contains(directoryName));
+ }
+
+ @Test
+ void test_createDirectoryWithAllFields(TestNamespace ns) {
+ DriveService driveService = DriveServiceTestFactory.createGoogleDrive(ns);
+ assertNotNull(driveService);
+
+ String directoryName = ns.prefix("test_directory_full");
+ Directory created =
+ Directories.create()
+ .name(directoryName)
+ .withService(driveService.getFullyQualifiedName())
+ .withDisplayName("Complete Test Directory")
+ .withDescription("A directory with all fields populated for testing")
+ .execute();
+
+ assertNotNull(created);
+ assertNotNull(created.getId());
+ assertEquals(directoryName, created.getName());
+ assertEquals("Complete Test Directory", created.getDisplayName());
+ assertEquals("A directory with all fields populated for testing", created.getDescription());
+ assertNotNull(created.getService());
+ assertNotNull(created.getFullyQualifiedName());
+ assertTrue(
+ created.getFullyQualifiedName().contains(directoryName),
+ "FQN should contain directory name");
+ }
+
+ @Test
+ void test_createDirectoryMinimalRequest(TestNamespace ns) {
+ DriveService driveService = DriveServiceTestFactory.createGoogleDrive(ns);
+ assertNotNull(driveService);
+
+ String directoryName = ns.prefix("test_directory_minimal");
+ Directory created =
+ Directories.create()
+ .name(directoryName)
+ .withService(driveService.getFullyQualifiedName())
+ .execute();
+
+ assertNotNull(created);
+ assertNotNull(created.getId());
+ assertEquals(directoryName, created.getName());
+ assertNotNull(created.getService());
+ }
+
@Test
void test_getByName(TestNamespace ns) {
DriveService driveService = DriveServiceTestFactory.createGoogleDrive(ns);
@@ -126,34 +346,6 @@ void test_deleteDirectory(TestNamespace ns) {
"Getting deleted directory should fail");
}
- @Test
- void test_createDirectoryMinimalRequest(TestNamespace ns) {
- DriveService driveService = DriveServiceTestFactory.createGoogleDrive(ns);
- assertNotNull(driveService);
-
- String directoryName = ns.prefix("test_directory_minimal");
- Directory created =
- Directories.create()
- .name(directoryName)
- .withService(driveService.getFullyQualifiedName())
- .execute();
-
- assertNotNull(created);
- assertNotNull(created.getId());
- assertEquals(directoryName, created.getName());
- assertNotNull(created.getService());
- }
-
- @Test
- void test_createDirectoryWithoutService_fails(TestNamespace ns) {
- String directoryName = ns.prefix("test_directory_no_service");
-
- assertThrows(
- Exception.class,
- () -> Directories.create().name(directoryName).execute(),
- "Creating directory without service should fail");
- }
-
@Test
void test_findDirectoryById(TestNamespace ns) {
DriveService driveService = DriveServiceTestFactory.createGoogleDrive(ns);
@@ -246,32 +438,6 @@ void test_createMultipleDirectories(TestNamespace ns) {
}
}
- @Test
- void test_createDirectoryWithAllFields(TestNamespace ns) {
- DriveService driveService = DriveServiceTestFactory.createGoogleDrive(ns);
- assertNotNull(driveService);
-
- String directoryName = ns.prefix("test_directory_full");
- Directory created =
- Directories.create()
- .name(directoryName)
- .withService(driveService.getFullyQualifiedName())
- .withDisplayName("Complete Test Directory")
- .withDescription("A directory with all fields populated for testing")
- .execute();
-
- assertNotNull(created);
- assertNotNull(created.getId());
- assertEquals(directoryName, created.getName());
- assertEquals("Complete Test Directory", created.getDisplayName());
- assertEquals("A directory with all fields populated for testing", created.getDescription());
- assertNotNull(created.getService());
- assertNotNull(created.getFullyQualifiedName());
- assertTrue(
- created.getFullyQualifiedName().contains(directoryName),
- "FQN should contain directory name");
- }
-
@Test
void test_getNonExistentDirectory_fails(TestNamespace ns) {
String nonExistentId = "non-existent-directory-id-12345";
@@ -291,33 +457,4 @@ void test_getByNameNonExistent_fails(TestNamespace ns) {
() -> Directories.getByName(nonExistentFqn),
"Getting directory by non-existent FQN should fail");
}
-
- @Test
- void test_createDirectoryWithInvalidService_fails(TestNamespace ns) {
- String directoryName = ns.prefix("test_directory_invalid_service");
- String invalidServiceFqn = "invalidDriveService_" + ns.prefix("nonexistent");
-
- assertThrows(
- Exception.class,
- () -> Directories.create().name(directoryName).withService(invalidServiceFqn).execute(),
- "Creating directory with invalid service should fail");
- }
-
- @Test
- void test_directoryFullyQualifiedName(TestNamespace ns) {
- DriveService driveService = DriveServiceTestFactory.createGoogleDrive(ns);
- assertNotNull(driveService);
-
- String directoryName = ns.prefix("test_directory_fqn");
- Directory created =
- Directories.create()
- .name(directoryName)
- .withService(driveService.getFullyQualifiedName())
- .execute();
-
- assertNotNull(created);
- assertNotNull(created.getFullyQualifiedName());
- assertTrue(created.getFullyQualifiedName().contains(driveService.getName()));
- assertTrue(created.getFullyQualifiedName().contains(directoryName));
- }
}
diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/FileResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/FileResourceIT.java
index da75ae9acd04..6784dc9a3a6c 100644
--- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/FileResourceIT.java
+++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/FileResourceIT.java
@@ -5,34 +5,176 @@
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.Arrays;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
+import java.util.UUID;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.api.parallel.Execution;
import org.junit.jupiter.api.parallel.ExecutionMode;
import org.openmetadata.it.factories.DriveServiceTestFactory;
import org.openmetadata.it.util.SdkClients;
import org.openmetadata.it.util.TestNamespace;
-import org.openmetadata.it.util.TestNamespaceExtension;
+import org.openmetadata.schema.api.data.CreateFile;
import org.openmetadata.schema.entity.data.File;
import org.openmetadata.schema.entity.services.DriveService;
import org.openmetadata.schema.type.Column;
import org.openmetadata.schema.type.ColumnDataType;
+import org.openmetadata.schema.type.EntityHistory;
import org.openmetadata.schema.type.FileType;
import org.openmetadata.sdk.fluent.Files;
-
+import org.openmetadata.sdk.models.ListParams;
+import org.openmetadata.sdk.models.ListResponse;
+import org.openmetadata.sdk.services.drives.FileService;
+
+/**
+ * Integration tests for File entity operations.
+ *
+ * Extends BaseEntityIT to inherit common entity tests. Adds File-specific tests for columns
+ * (optional, since not all file types are tabular), file metadata, and drive-service linkage.
+ */
@Execution(ExecutionMode.CONCURRENT)
-@ExtendWith(TestNamespaceExtension.class)
-public class FileResourceIT {
+public class FileResourceIT extends BaseEntityIT {
+
+ {
+ supportsFollowers = false;
+ supportsDomains = false;
+ supportsDataProducts = false;
+ supportsCustomExtension = false;
+ supportsBulkAPI = false;
+ supportsDataContract = false;
+ }
@BeforeAll
public static void setup() {
Files.setDefaultClient(SdkClients.adminClient());
}
+ // ===================================================================
+ // ABSTRACT METHOD IMPLEMENTATIONS (Required by BaseEntityIT)
+ // ===================================================================
+
+ @Override
+ protected CreateFile createMinimalRequest(TestNamespace ns) {
+ DriveService driveService = DriveServiceTestFactory.createGoogleDrive(ns);
+ return new CreateFile()
+ .withName(ns.prefix("file"))
+ .withService(driveService.getFullyQualifiedName())
+ .withDescription("Test file created by integration test");
+ }
+
+ @Override
+ protected CreateFile createRequest(String name, TestNamespace ns) {
+ DriveService driveService = DriveServiceTestFactory.createGoogleDrive(ns);
+ return new CreateFile().withName(name).withService(driveService.getFullyQualifiedName());
+ }
+
+ @Override
+ protected File createEntity(CreateFile createRequest) {
+ return getFileService().create(createRequest);
+ }
+
+ @Override
+ protected File getEntity(String id) {
+ return getFileService().get(id);
+ }
+
+ @Override
+ protected File getEntityByName(String fqn) {
+ return getFileService().getByName(fqn);
+ }
+
+ @Override
+ protected File patchEntity(String id, File entity) {
+ return getFileService().update(id, entity);
+ }
+
+ @Override
+ protected void deleteEntity(String id) {
+ getFileService().delete(id);
+ }
+
+ @Override
+ protected void restoreEntity(String id) {
+ getFileService().restore(id);
+ }
+
+ @Override
+ protected void hardDeleteEntity(String id) {
+ Map params = new HashMap<>();
+ params.put("hardDelete", "true");
+ getFileService().delete(id, params);
+ }
+
+ @Override
+ protected String getEntityType() {
+ return "file";
+ }
+
+ @Override
+ protected void validateCreatedEntity(File entity, CreateFile createRequest) {
+ assertEquals(createRequest.getName(), entity.getName());
+ assertNotNull(entity.getService(), "File must have a service");
+ assertEquals(
+ createRequest.getService(),
+ entity.getService().getFullyQualifiedName(),
+ "Service FQN should match");
+
+ if (createRequest.getDescription() != null) {
+ assertEquals(createRequest.getDescription(), entity.getDescription());
+ }
+
+ if (createRequest.getColumns() != null && !createRequest.getColumns().isEmpty()) {
+ assertNotNull(entity.getColumns());
+ assertEquals(createRequest.getColumns().size(), entity.getColumns().size());
+ }
+
+ assertTrue(
+ entity.getFullyQualifiedName().contains(entity.getName()), "FQN should contain file name");
+ }
+
+ @Override
+ protected ListResponse listEntities(ListParams params) {
+ return getFileService().list(params);
+ }
+
+ @Override
+ protected File getEntityWithFields(String id, String fields) {
+ return getFileService().get(id, fields);
+ }
+
+ @Override
+ protected File getEntityByNameWithFields(String fqn, String fields) {
+ return getFileService().getByName(fqn, fields);
+ }
+
+ @Override
+ protected File getEntityIncludeDeleted(String id) {
+ return getFileService().get(id, null, "deleted");
+ }
+
+ @Override
+ protected EntityHistory getVersionHistory(UUID id) {
+ return getFileService().getVersionList(id);
+ }
+
+ @Override
+ protected File getVersion(UUID id, Double version) {
+ return getFileService().getVersion(id.toString(), version);
+ }
+
+ private FileService getFileService() {
+ return new FileService(SdkClients.adminClient().getHttpClient());
+ }
+
+ // ===================================================================
+ // FILE-SPECIFIC TESTS
+ // ===================================================================
+
@Test
void test_createAndGetFile(TestNamespace ns) {
DriveService driveService = DriveServiceTestFactory.createGoogleDrive(ns);
@@ -49,7 +191,8 @@ void test_createAndGetFile(TestNamespace ns) {
assertNotNull(createdFile.getId());
assertEquals(fileName, createdFile.getName());
assertNotNull(createdFile.getService());
- assertEquals(driveService.getFullyQualifiedName(), createdFile.getService().getName());
+ assertEquals(
+ driveService.getFullyQualifiedName(), createdFile.getService().getFullyQualifiedName());
File retrievedFile = Files.get(createdFile.getId().toString());
assertNotNull(retrievedFile);
@@ -80,80 +223,141 @@ void test_getFileByName(TestNamespace ns) {
}
@Test
- void test_getFileByNameWithFields(TestNamespace ns) {
+ void test_createFileWithoutService_shouldFail(TestNamespace ns) {
+ String fileName = ns.prefix("test_file_no_service");
+
+ assertThrows(
+ Exception.class,
+ () -> Files.create().name(fileName).execute(),
+ "Creating file without service should fail");
+ }
+
+ @Test
+ void test_multipleFilesInSameService(TestNamespace ns) {
DriveService driveService = DriveServiceTestFactory.createGoogleDrive(ns);
- String fileName = ns.prefix("test_file_with_fields");
- File createdFile =
+ File file1 =
Files.create()
- .name(fileName)
+ .name(ns.prefix("file_1"))
.withService(driveService.getFullyQualifiedName())
- .withDescription("File with specific fields")
.execute();
- assertNotNull(createdFile);
+ File file2 =
+ Files.create()
+ .name(ns.prefix("file_2"))
+ .withService(driveService.getFullyQualifiedName())
+ .execute();
- File retrievedFile = Files.getByName(createdFile.getFullyQualifiedName(), "owners,tags");
- assertNotNull(retrievedFile);
- assertEquals(createdFile.getId(), retrievedFile.getId());
+ File file3 =
+ Files.create()
+ .name(ns.prefix("file_3"))
+ .withService(driveService.getFullyQualifiedName())
+ .execute();
+
+ assertNotNull(file1);
+ assertNotNull(file2);
+ assertNotNull(file3);
+
+ assertNotEquals(file1.getId(), file2.getId());
+ assertNotEquals(file2.getId(), file3.getId());
+ assertNotEquals(file1.getId(), file3.getId());
+
+ assertEquals(driveService.getFullyQualifiedName(), file1.getService().getFullyQualifiedName());
+ assertEquals(driveService.getFullyQualifiedName(), file2.getService().getFullyQualifiedName());
+ assertEquals(driveService.getFullyQualifiedName(), file3.getService().getFullyQualifiedName());
}
@Test
- void test_findFileById(TestNamespace ns) {
+ void test_createFileWithoutColumns(TestNamespace ns) {
DriveService driveService = DriveServiceTestFactory.createGoogleDrive(ns);
- String fileName = ns.prefix("test_file_find");
+ String fileName = ns.prefix("test_file_no_columns");
File createdFile =
- Files.create().name(fileName).withService(driveService.getFullyQualifiedName()).execute();
+ Files.create()
+ .name(fileName)
+ .withService(driveService.getFullyQualifiedName())
+ .withFileType(FileType.Text)
+ .withMimeType("text/plain")
+ .execute();
assertNotNull(createdFile);
-
- File foundFile = Files.find(createdFile.getId().toString()).fetch();
- assertNotNull(foundFile);
- assertEquals(createdFile.getId(), foundFile.getId());
- assertEquals(fileName, foundFile.getName());
+ assertEquals(FileType.Text, createdFile.getFileType());
+ assertNull(createdFile.getColumns(), "Columns should be null for a file without columns");
}
@Test
- void test_findFileByNameWithFields(TestNamespace ns) {
+ void test_createCsvFileWithColumns(TestNamespace ns) {
DriveService driveService = DriveServiceTestFactory.createGoogleDrive(ns);
- String fileName = ns.prefix("test_file_find_by_name");
+ String fileName = ns.prefix("test_csv_with_columns");
+ List columns =
+ Arrays.asList(
+ new Column().withName("id").withDataType(ColumnDataType.INT),
+ new Column().withName("name").withDataType(ColumnDataType.STRING),
+ new Column().withName("price").withDataType(ColumnDataType.DOUBLE));
+
File createdFile =
Files.create()
.name(fileName)
.withService(driveService.getFullyQualifiedName())
- .withDescription("Find by name with fields")
+ .withFileType(FileType.CSV)
+ .withMimeType("text/csv")
+ .withColumns(columns)
.execute();
- assertNotNull(createdFile);
-
- File foundFile =
- Files.findByName(createdFile.getFullyQualifiedName())
- .withFields("owners", "tags", "domains")
- .fetch();
- assertNotNull(foundFile);
- assertEquals(createdFile.getId(), foundFile.getId());
+ assertNotNull(createdFile.getColumns());
+ assertEquals(3, createdFile.getColumns().size());
}
@Test
- void test_deleteFile(TestNamespace ns) {
+ void test_getFileWithColumnsField(TestNamespace ns) {
DriveService driveService = DriveServiceTestFactory.createGoogleDrive(ns);
- String fileName = ns.prefix("test_file_delete");
+ String fileName = ns.prefix("test_csv_get_columns");
+ List columns =
+ Arrays.asList(
+ new Column().withName("col1").withDataType(ColumnDataType.STRING),
+ new Column().withName("col2").withDataType(ColumnDataType.INT));
+
File createdFile =
- Files.create().name(fileName).withService(driveService.getFullyQualifiedName()).execute();
+ Files.create()
+ .name(fileName)
+ .withService(driveService.getFullyQualifiedName())
+ .withFileType(FileType.CSV)
+ .withColumns(columns)
+ .execute();
- assertNotNull(createdFile);
- String fileId = createdFile.getId().toString();
+ File retrievedFile = Files.getByName(createdFile.getFullyQualifiedName(), "columns");
+ assertNotNull(retrievedFile.getColumns());
+ assertEquals(2, retrievedFile.getColumns().size());
+ assertEquals("col1", retrievedFile.getColumns().get(0).getName());
+ assertEquals("col2", retrievedFile.getColumns().get(1).getName());
+ }
- File beforeDelete = Files.get(fileId);
- assertNotNull(beforeDelete);
+ @Test
+ void test_patchFileWithoutColumns_doesNotNpe(TestNamespace ns) {
+ // Regression: PATCH on a file without columns must not NPE in
+ // ColumnEntityUpdater.updateColumns. Reproduces the failure seen when
+ // editing tags/description on PDF/image files (no columns defined).
+ DriveService driveService = DriveServiceTestFactory.createGoogleDrive(ns);
- Files.delete(fileId);
+ String fileName = ns.prefix("patch_no_columns");
+ File createdFile =
+ Files.create()
+ .name(fileName)
+ .withService(driveService.getFullyQualifiedName())
+ .withFileType(FileType.PDF)
+ .withMimeType("application/pdf")
+ .withDescription("Initial description")
+ .execute();
- assertThrows(
- Exception.class, () -> Files.get(fileId), "Getting deleted file should throw exception");
+ assertNull(createdFile.getColumns());
+
+ createdFile.setDescription("Updated description");
+ File patched = getFileService().update(createdFile.getId().toString(), createdFile);
+
+ assertEquals("Updated description", patched.getDescription());
+ assertNull(patched.getColumns());
}
@Test
@@ -176,22 +380,28 @@ void test_createFileWithDisplayName(TestNamespace ns) {
}
@Test
- void test_createFileWithDescription(TestNamespace ns) {
+ void test_fileWithAllOptionalFields(TestNamespace ns) {
DriveService driveService = DriveServiceTestFactory.createGoogleDrive(ns);
- String fileName = ns.prefix("test_file_desc");
- String description = "This is a detailed description of the test file";
+ String fileName = ns.prefix("test_file_full");
+ String displayName = "Complete Test File";
+ String description = "A file with all optional fields populated";
File createdFile =
Files.create()
.name(fileName)
+ .withDisplayName(displayName)
.withDescription(description)
.withService(driveService.getFullyQualifiedName())
.execute();
assertNotNull(createdFile);
assertEquals(fileName, createdFile.getName());
+ assertEquals(displayName, createdFile.getDisplayName());
assertEquals(description, createdFile.getDescription());
+ assertNotNull(createdFile.getService());
+ assertEquals(
+ driveService.getFullyQualifiedName(), createdFile.getService().getFullyQualifiedName());
}
@Test
@@ -210,215 +420,118 @@ void test_createFileMinimal(TestNamespace ns) {
}
@Test
- void test_createFileWithoutService_shouldFail(TestNamespace ns) {
- String fileName = ns.prefix("test_file_no_service");
-
- assertThrows(
- Exception.class,
- () -> Files.create().name(fileName).execute(),
- "Creating file without service should fail");
- }
-
- @Test
- void test_multipleFilesInSameService(TestNamespace ns) {
- DriveService driveService = DriveServiceTestFactory.createGoogleDrive(ns);
-
- File file1 =
- Files.create()
- .name(ns.prefix("file_1"))
- .withService(driveService.getFullyQualifiedName())
- .execute();
-
- File file2 =
- Files.create()
- .name(ns.prefix("file_2"))
- .withService(driveService.getFullyQualifiedName())
- .execute();
-
- File file3 =
- Files.create()
- .name(ns.prefix("file_3"))
- .withService(driveService.getFullyQualifiedName())
- .execute();
-
- assertNotNull(file1);
- assertNotNull(file2);
- assertNotNull(file3);
-
- assertNotEquals(file1.getId(), file2.getId());
- assertNotEquals(file2.getId(), file3.getId());
- assertNotEquals(file1.getId(), file3.getId());
-
- assertEquals(driveService.getFullyQualifiedName(), file1.getService().getFullyQualifiedName());
- assertEquals(driveService.getFullyQualifiedName(), file2.getService().getFullyQualifiedName());
- assertEquals(driveService.getFullyQualifiedName(), file3.getService().getFullyQualifiedName());
- }
-
- @Test
- void test_fileWithAllOptionalFields(TestNamespace ns) {
+ void test_createFileWithDescription(TestNamespace ns) {
DriveService driveService = DriveServiceTestFactory.createGoogleDrive(ns);
- String fileName = ns.prefix("test_file_full");
- String displayName = "Complete Test File";
- String description = "A file with all optional fields populated";
+ String fileName = ns.prefix("test_file_desc");
+ String description = "This is a detailed description of the test file";
File createdFile =
Files.create()
.name(fileName)
- .withDisplayName(displayName)
.withDescription(description)
.withService(driveService.getFullyQualifiedName())
.execute();
assertNotNull(createdFile);
assertEquals(fileName, createdFile.getName());
- assertEquals(displayName, createdFile.getDisplayName());
assertEquals(description, createdFile.getDescription());
- assertNotNull(createdFile.getService());
- assertEquals(driveService.getFullyQualifiedName(), createdFile.getService().getName());
}
@Test
- void test_getFileWithNonExistentId_shouldFail(TestNamespace ns) {
- String nonExistentId = "00000000-0000-0000-0000-000000000000";
+ void test_deleteFile(TestNamespace ns) {
+ DriveService driveService = DriveServiceTestFactory.createGoogleDrive(ns);
- assertThrows(
- Exception.class,
- () -> Files.get(nonExistentId),
- "Getting file with non-existent ID should fail");
- }
+ String fileName = ns.prefix("test_file_delete");
+ File createdFile =
+ Files.create().name(fileName).withService(driveService.getFullyQualifiedName()).execute();
- @Test
- void test_getFileByNameWithNonExistentFQN_shouldFail(TestNamespace ns) {
- String nonExistentFQN = "nonexistent.service.nonexistent.file";
+ assertNotNull(createdFile);
+ String fileId = createdFile.getId().toString();
+
+ File beforeDelete = Files.get(fileId);
+ assertNotNull(beforeDelete);
+
+ Files.delete(fileId);
assertThrows(
- Exception.class,
- () -> Files.getByName(nonExistentFQN),
- "Getting file with non-existent FQN should fail");
+ Exception.class, () -> Files.get(fileId), "Getting deleted file should throw exception");
}
@Test
- void test_createFileWithoutColumns(TestNamespace ns) {
- // This test verifies that files can be created without columns (columns are optional)
+ void test_findFileById(TestNamespace ns) {
DriveService driveService = DriveServiceTestFactory.createGoogleDrive(ns);
- String fileName = ns.prefix("test_file_no_columns");
+ String fileName = ns.prefix("test_file_find");
File createdFile =
- Files.create()
- .name(fileName)
- .withService(driveService.getFullyQualifiedName())
- .withFileType(FileType.Text)
- .withMimeType("text/plain")
- .execute();
+ Files.create().name(fileName).withService(driveService.getFullyQualifiedName()).execute();
assertNotNull(createdFile);
- assertNotNull(createdFile.getId());
- assertEquals(fileName, createdFile.getName());
- assertEquals(FileType.Text, createdFile.getFileType());
- // Columns should be null for a file without columns
- assertNull(createdFile.getColumns());
+
+ File foundFile = Files.find(createdFile.getId().toString()).fetch();
+ assertNotNull(foundFile);
+ assertEquals(createdFile.getId(), foundFile.getId());
+ assertEquals(fileName, foundFile.getName());
}
@Test
- void test_createCsvFileWithColumns(TestNamespace ns) {
- // This test verifies that CSV files can be created with column definitions
+ void test_findFileByNameWithFields(TestNamespace ns) {
DriveService driveService = DriveServiceTestFactory.createGoogleDrive(ns);
- String fileName = ns.prefix("test_csv_with_columns");
- List columns =
- Arrays.asList(
- new Column().withName("id").withDataType(ColumnDataType.INT),
- new Column().withName("name").withDataType(ColumnDataType.STRING),
- new Column().withName("price").withDataType(ColumnDataType.DOUBLE));
-
+ String fileName = ns.prefix("test_file_find_by_name");
File createdFile =
Files.create()
.name(fileName)
.withService(driveService.getFullyQualifiedName())
- .withFileType(FileType.CSV)
- .withMimeType("text/csv")
- .withColumns(columns)
+ .withDescription("Find by name with fields")
.execute();
assertNotNull(createdFile);
- assertNotNull(createdFile.getId());
- assertEquals(fileName, createdFile.getName());
- assertEquals(FileType.CSV, createdFile.getFileType());
- assertNotNull(createdFile.getColumns());
- assertEquals(3, createdFile.getColumns().size());
+
+ File foundFile =
+ Files.findByName(createdFile.getFullyQualifiedName())
+ .withFields("owners", "tags", "domains")
+ .fetch();
+ assertNotNull(foundFile);
+ assertEquals(createdFile.getId(), foundFile.getId());
}
@Test
- void test_getFileWithColumnsField(TestNamespace ns) {
- // This test verifies that columns are returned when explicitly requested
+ void test_getFileByNameWithFields(TestNamespace ns) {
DriveService driveService = DriveServiceTestFactory.createGoogleDrive(ns);
- String fileName = ns.prefix("test_csv_get_columns");
- List columns =
- Arrays.asList(
- new Column().withName("col1").withDataType(ColumnDataType.STRING),
- new Column().withName("col2").withDataType(ColumnDataType.INT));
-
+ String fileName = ns.prefix("test_file_with_fields");
File createdFile =
Files.create()
.name(fileName)
.withService(driveService.getFullyQualifiedName())
- .withFileType(FileType.CSV)
- .withColumns(columns)
+ .withDescription("File with specific fields")
.execute();
assertNotNull(createdFile);
- // Retrieve with columns field
- File retrievedFile = Files.getByName(createdFile.getFullyQualifiedName(), "columns");
+ File retrievedFile = Files.getByName(createdFile.getFullyQualifiedName(), "owners,tags");
assertNotNull(retrievedFile);
- assertNotNull(retrievedFile.getColumns());
- assertEquals(2, retrievedFile.getColumns().size());
- assertEquals("col1", retrievedFile.getColumns().get(0).getName());
- assertEquals("col2", retrievedFile.getColumns().get(1).getName());
+ assertEquals(createdFile.getId(), retrievedFile.getId());
}
@Test
- void test_createImageFileWithoutColumns(TestNamespace ns) {
- // This test verifies that non-structured files like images work without columns
- DriveService driveService = DriveServiceTestFactory.createGoogleDrive(ns);
-
- String fileName = ns.prefix("test_image_file");
- File createdFile =
- Files.create()
- .name(fileName)
- .withService(driveService.getFullyQualifiedName())
- .withFileType(FileType.Image)
- .withMimeType("image/png")
- .execute();
+ void test_getFileWithNonExistentId_shouldFail(TestNamespace ns) {
+ String nonExistentId = "00000000-0000-0000-0000-000000000000";
- assertNotNull(createdFile);
- assertNotNull(createdFile.getId());
- assertEquals(FileType.Image, createdFile.getFileType());
- assertEquals("image/png", createdFile.getMimeType());
- // Image files should not have columns
- assertNull(createdFile.getColumns());
+ assertThrows(
+ Exception.class,
+ () -> Files.get(nonExistentId),
+ "Getting file with non-existent ID should fail");
}
@Test
- void test_createPdfFileWithoutColumns(TestNamespace ns) {
- // This test verifies that PDF files work without columns
- DriveService driveService = DriveServiceTestFactory.createGoogleDrive(ns);
-
- String fileName = ns.prefix("test_pdf_file");
- File createdFile =
- Files.create()
- .name(fileName)
- .withService(driveService.getFullyQualifiedName())
- .withFileType(FileType.PDF)
- .withMimeType("application/pdf")
- .execute();
+ void test_getFileByNameWithNonExistentFQN_shouldFail(TestNamespace ns) {
+ String nonExistentFQN = "nonexistent.service.nonexistent.file";
- assertNotNull(createdFile);
- assertNotNull(createdFile.getId());
- assertEquals(FileType.PDF, createdFile.getFileType());
- // PDF files should not have columns
- assertNull(createdFile.getColumns());
+ assertThrows(
+ Exception.class,
+ () -> Files.getByName(nonExistentFQN),
+ "Getting file with non-existent FQN should fail");
}
}
diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/FolderResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/FolderResourceIT.java
new file mode 100644
index 000000000000..7c71089dd13e
--- /dev/null
+++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/FolderResourceIT.java
@@ -0,0 +1,227 @@
+package org.openmetadata.it.tests;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.time.Duration;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+import org.awaitility.Awaitility;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.parallel.Execution;
+import org.junit.jupiter.api.parallel.ExecutionMode;
+import org.openmetadata.it.util.SdkClients;
+import org.openmetadata.it.util.TestNamespace;
+import org.openmetadata.schema.api.data.CreateFolder;
+import org.openmetadata.schema.entity.data.Folder;
+import org.openmetadata.schema.type.EntityHistory;
+import org.openmetadata.sdk.exceptions.ApiException;
+import org.openmetadata.sdk.models.ListParams;
+import org.openmetadata.sdk.models.ListResponse;
+import org.openmetadata.sdk.services.drives.FolderService;
+
+/**
+ * Integration tests for Folder entity operations.
+ *
+ * Folder is a service-less drive entity at {@code /v1/drive/folders}. It supports owners,
+ * tags, and domains, but does not expose version history, followers, custom extensions, or bulk
+ * endpoints (see the {@code supports*} flags below). Extends BaseEntityIT so generic CRUD / tag /
+ * domain coverage runs automatically.
+ */
+@Execution(ExecutionMode.CONCURRENT)
+public class FolderResourceIT extends BaseEntityIT {
+
+ {
+ supportsFollowers = false;
+ supportsDataProducts = false;
+ supportsCustomExtension = false;
+ supportsBulkAPI = false;
+ supportsDataContract = false;
+ supportsVersionHistory = false;
+ supportsGetByVersion = false;
+ }
+
+ // ===================================================================
+ // ABSTRACT METHOD IMPLEMENTATIONS (Required by BaseEntityIT)
+ // ===================================================================
+
+ @Override
+ protected CreateFolder createMinimalRequest(TestNamespace ns) {
+ return new CreateFolder()
+ .withName(ns.prefix("folder"))
+ .withDescription("Test folder created by integration test");
+ }
+
+ @Override
+ protected CreateFolder createRequest(String name, TestNamespace ns) {
+ return new CreateFolder().withName(name);
+ }
+
+ @Override
+ protected Folder createEntity(CreateFolder createRequest) {
+ return getFolderService().create(createRequest);
+ }
+
+ @Override
+ protected Folder getEntity(String id) {
+ return getFolderService().get(id);
+ }
+
+ @Override
+ protected Folder getEntityByName(String fqn) {
+ return getFolderService().getByName(fqn);
+ }
+
+ @Override
+ protected Folder patchEntity(String id, Folder entity) {
+ return getFolderService().update(id, entity);
+ }
+
+ @Override
+ protected void deleteEntity(String id) {
+ getFolderService().delete(id);
+ }
+
+ @Override
+ protected void restoreEntity(String id) {
+ getFolderService().restore(id);
+ }
+
+ @Override
+ protected void hardDeleteEntity(String id) {
+ Map params = new HashMap<>();
+ params.put("hardDelete", "true");
+ getFolderService().delete(id, params);
+ // FolderResource hard-delete is asynchronous: it returns 200 immediately and removes
+ // the row in the background. Poll with include=deleted until the entity is fully gone
+ // (server returns 404) so BaseEntityIT.delete_entityAsAdmin_hardDelete_200 sees the
+ // post-condition. Other exceptions (e.g., transient 500s, network errors) must propagate
+ // so the test doesn't silently pass on real failures — Awaitility re-polls on throw and
+ // surfaces the last exception when the timeout window expires.
+ Awaitility.await()
+ .pollInterval(Duration.ofMillis(200))
+ .atMost(Duration.ofSeconds(15))
+ .until(
+ () -> {
+ try {
+ getFolderService().get(id, null, "deleted");
+ return false;
+ } catch (ApiException e) {
+ if (e.getStatusCode() == 404) {
+ return true;
+ }
+ throw e;
+ }
+ });
+ }
+
+ @Override
+ protected String getEntityType() {
+ return "folder";
+ }
+
+ @Override
+ protected void validateCreatedEntity(Folder entity, CreateFolder createRequest) {
+ assertEquals(createRequest.getName(), entity.getName());
+
+ if (createRequest.getDescription() != null) {
+ assertEquals(createRequest.getDescription(), entity.getDescription());
+ }
+
+ assertTrue(
+ entity.getFullyQualifiedName().contains(entity.getName()),
+ "FQN should contain folder name");
+ }
+
+ @Override
+ protected ListResponse listEntities(ListParams params) {
+ return getFolderService().list(params);
+ }
+
+ @Override
+ protected Folder getEntityWithFields(String id, String fields) {
+ return getFolderService().get(id, fields);
+ }
+
+ @Override
+ protected Folder getEntityByNameWithFields(String fqn, String fields) {
+ return getFolderService().getByName(fqn, fields);
+ }
+
+ @Override
+ protected Folder getEntityIncludeDeleted(String id) {
+ return getFolderService().get(id, null, "deleted");
+ }
+
+ @Override
+ protected EntityHistory getVersionHistory(UUID id) {
+ throw new UnsupportedOperationException("Folder does not expose version history");
+ }
+
+ @Override
+ protected Folder getVersion(UUID id, Double version) {
+ throw new UnsupportedOperationException("Folder does not expose individual versions");
+ }
+
+ private FolderService getFolderService() {
+ return new FolderService(SdkClients.adminClient().getHttpClient());
+ }
+
+ // ===================================================================
+ // FOLDER-SPECIFIC TESTS
+ // ===================================================================
+
+ @Test
+ void test_createFolder_minimalRequest(TestNamespace ns) {
+ String folderName = ns.prefix("folder_minimal");
+ Folder folder = getFolderService().create(new CreateFolder().withName(folderName));
+
+ assertNotNull(folder.getId());
+ assertEquals(folderName, folder.getName());
+ assertNotNull(folder.getFullyQualifiedName());
+ }
+
+ @Test
+ void test_createNestedFolder(TestNamespace ns) {
+ Folder parent =
+ getFolderService()
+ .create(
+ new CreateFolder()
+ .withName(ns.prefix("parent_folder"))
+ .withDescription("Parent folder"));
+
+ Folder child =
+ getFolderService()
+ .create(
+ new CreateFolder()
+ .withName(ns.prefix("child_folder"))
+ .withParent(parent.getFullyQualifiedName())
+ .withDescription("Child folder"));
+
+ assertNotNull(child.getParent());
+ assertEquals(parent.getId(), child.getParent().getId());
+ assertTrue(
+ child.getFullyQualifiedName().contains(parent.getName()),
+ "Nested folder FQN should contain parent name");
+ }
+
+ @Test
+ void test_createFolderWithoutName_fails(TestNamespace ns) {
+ assertThrows(
+ Exception.class,
+ () -> getFolderService().create(new CreateFolder()),
+ "Creating folder without name should fail");
+ }
+
+ @Test
+ void test_rootFolderHasNoParent(TestNamespace ns) {
+ Folder folder =
+ getFolderService().create(new CreateFolder().withName(ns.prefix("root_folder")));
+
+ assertNull(folder.getParent());
+ }
+}
diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/SpreadsheetResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/SpreadsheetResourceIT.java
index a438cfd40e04..46d1224bb1c8 100644
--- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/SpreadsheetResourceIT.java
+++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/SpreadsheetResourceIT.java
@@ -6,28 +6,47 @@
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.api.parallel.Execution;
import org.junit.jupiter.api.parallel.ExecutionMode;
import org.openmetadata.it.factories.DriveServiceTestFactory;
import org.openmetadata.it.util.SdkClients;
import org.openmetadata.it.util.TestNamespace;
-import org.openmetadata.it.util.TestNamespaceExtension;
+import org.openmetadata.schema.api.data.CreateSpreadsheet;
import org.openmetadata.schema.entity.data.Directory;
import org.openmetadata.schema.entity.data.Spreadsheet;
import org.openmetadata.schema.entity.services.DriveService;
+import org.openmetadata.schema.type.EntityHistory;
import org.openmetadata.schema.type.EntityReference;
import org.openmetadata.sdk.fluent.Directories;
import org.openmetadata.sdk.fluent.Spreadsheets;
import org.openmetadata.sdk.fluent.Worksheets;
import org.openmetadata.sdk.models.ListParams;
import org.openmetadata.sdk.models.ListResponse;
-
+import org.openmetadata.sdk.services.drives.SpreadsheetService;
+
+/**
+ * Integration tests for Spreadsheet entity operations.
+ *
+ * Extends BaseEntityIT to inherit common entity tests. Adds Spreadsheet-specific tests for
+ * directory hierarchy, root-filter listing, and worksheet relationships.
+ */
@Execution(ExecutionMode.CONCURRENT)
-@ExtendWith(TestNamespaceExtension.class)
-public class SpreadsheetResourceIT {
+public class SpreadsheetResourceIT extends BaseEntityIT {
+
+ {
+ supportsFollowers = false;
+ supportsDomains = false;
+ supportsDataProducts = false;
+ supportsCustomExtension = false;
+ supportsBulkAPI = false;
+ supportsDataContract = false;
+ }
@BeforeAll
static void setup() {
@@ -36,117 +55,123 @@ static void setup() {
Worksheets.setDefaultClient(SdkClients.adminClient());
}
- @Test
- void test_createSpreadsheet(TestNamespace ns) {
- DriveService driveService = DriveServiceTestFactory.createGoogleDrive(ns);
+ // ===================================================================
+ // ABSTRACT METHOD IMPLEMENTATIONS (Required by BaseEntityIT)
+ // ===================================================================
- Spreadsheet spreadsheet =
- Spreadsheets.create()
- .name(ns.prefix("spreadsheet"))
- .withDescription("Test spreadsheet")
- .withService(driveService.getFullyQualifiedName())
- .execute();
-
- assertNotNull(spreadsheet);
- assertNotNull(spreadsheet.getId());
- assertEquals(ns.prefix("spreadsheet"), spreadsheet.getName());
- assertEquals("Test spreadsheet", spreadsheet.getDescription());
- assertNotNull(spreadsheet.getService());
- assertEquals(
- driveService.getFullyQualifiedName(), spreadsheet.getService().getFullyQualifiedName());
+ @Override
+ protected CreateSpreadsheet createMinimalRequest(TestNamespace ns) {
+ DriveService driveService = DriveServiceTestFactory.createGoogleDrive(ns);
+ return new CreateSpreadsheet()
+ .withName(ns.prefix("spreadsheet"))
+ .withService(driveService.getFullyQualifiedName())
+ .withDescription("Test spreadsheet created by integration test");
}
- @Test
- void test_getSpreadsheetById(TestNamespace ns) {
+ @Override
+ protected CreateSpreadsheet createRequest(String name, TestNamespace ns) {
DriveService driveService = DriveServiceTestFactory.createGoogleDrive(ns);
+ return new CreateSpreadsheet().withName(name).withService(driveService.getFullyQualifiedName());
+ }
- Spreadsheet created =
- Spreadsheets.create()
- .name(ns.prefix("spreadsheet_get"))
- .withService(driveService.getFullyQualifiedName())
- .execute();
+ @Override
+ protected Spreadsheet createEntity(CreateSpreadsheet createRequest) {
+ return getSpreadsheetService().create(createRequest);
+ }
- Spreadsheet fetched = Spreadsheets.get(created.getId().toString());
+ @Override
+ protected Spreadsheet getEntity(String id) {
+ return getSpreadsheetService().get(id);
+ }
- assertNotNull(fetched);
- assertEquals(created.getId(), fetched.getId());
- assertEquals(created.getName(), fetched.getName());
- assertEquals(
- created.getFullyQualifiedName(),
- fetched.getFullyQualifiedName(),
- "FQN should match between created and fetched");
+ @Override
+ protected Spreadsheet getEntityByName(String fqn) {
+ return getSpreadsheetService().getByName(fqn);
}
- @Test
- void test_getSpreadsheetByName(TestNamespace ns) {
- DriveService driveService = DriveServiceTestFactory.createGoogleDrive(ns);
+ @Override
+ protected Spreadsheet patchEntity(String id, Spreadsheet entity) {
+ return getSpreadsheetService().update(id, entity);
+ }
- Spreadsheet created =
- Spreadsheets.create()
- .name(ns.prefix("spreadsheet_getByName"))
- .withService(driveService.getFullyQualifiedName())
- .execute();
+ @Override
+ protected void deleteEntity(String id) {
+ getSpreadsheetService().delete(id);
+ }
- Spreadsheet fetched = Spreadsheets.getByName(created.getFullyQualifiedName());
+ @Override
+ protected void restoreEntity(String id) {
+ getSpreadsheetService().restore(id);
+ }
- assertNotNull(fetched);
- assertEquals(created.getId(), fetched.getId());
- assertEquals(created.getName(), fetched.getName());
- assertEquals(created.getFullyQualifiedName(), fetched.getFullyQualifiedName());
+ @Override
+ protected void hardDeleteEntity(String id) {
+ Map params = new HashMap<>();
+ params.put("hardDelete", "true");
+ getSpreadsheetService().delete(id, params);
}
- @Test
- void test_deleteSpreadsheet(TestNamespace ns) {
- DriveService driveService = DriveServiceTestFactory.createGoogleDrive(ns);
+ @Override
+ protected String getEntityType() {
+ return "spreadsheet";
+ }
- Spreadsheet created =
- Spreadsheets.create()
- .name(ns.prefix("spreadsheet_delete"))
- .withService(driveService.getFullyQualifiedName())
- .execute();
+ @Override
+ protected void validateCreatedEntity(Spreadsheet entity, CreateSpreadsheet createRequest) {
+ assertEquals(createRequest.getName(), entity.getName());
+ assertNotNull(entity.getService(), "Spreadsheet must have a service");
+ assertEquals(
+ createRequest.getService(),
+ entity.getService().getFullyQualifiedName(),
+ "Service FQN should match");
- assertNotNull(created.getId());
+ if (createRequest.getDescription() != null) {
+ assertEquals(createRequest.getDescription(), entity.getDescription());
+ }
- Spreadsheets.delete(created.getId().toString());
+ assertTrue(
+ entity.getFullyQualifiedName().contains(entity.getName()),
+ "FQN should contain spreadsheet name");
+ }
- assertThrows(
- Exception.class,
- () -> Spreadsheets.get(created.getId().toString()),
- "Getting deleted spreadsheet should fail");
+ @Override
+ protected ListResponse listEntities(ListParams params) {
+ return getSpreadsheetService().list(params);
}
- @Test
- void test_createSpreadsheetWithOptionalFields(TestNamespace ns) {
- DriveService driveService = DriveServiceTestFactory.createGoogleDrive(ns);
+ @Override
+ protected Spreadsheet getEntityWithFields(String id, String fields) {
+ return getSpreadsheetService().get(id, fields);
+ }
- Spreadsheet spreadsheet =
- Spreadsheets.create()
- .name(ns.prefix("spreadsheet_optional"))
- .withDisplayName("Display Name for Spreadsheet")
- .withDescription("Spreadsheet with optional fields")
- .withService(driveService.getFullyQualifiedName())
- .execute();
+ @Override
+ protected Spreadsheet getEntityByNameWithFields(String fqn, String fields) {
+ return getSpreadsheetService().getByName(fqn, fields);
+ }
- assertNotNull(spreadsheet);
- assertEquals("Display Name for Spreadsheet", spreadsheet.getDisplayName());
- assertEquals("Spreadsheet with optional fields", spreadsheet.getDescription());
+ @Override
+ protected Spreadsheet getEntityIncludeDeleted(String id) {
+ return getSpreadsheetService().get(id, null, "deleted");
}
- @Test
- void test_createSpreadsheetMinimal(TestNamespace ns) {
- DriveService driveService = DriveServiceTestFactory.createGoogleDrive(ns);
+ @Override
+ protected EntityHistory getVersionHistory(UUID id) {
+ return getSpreadsheetService().getVersionList(id);
+ }
- Spreadsheet spreadsheet =
- Spreadsheets.create()
- .name(ns.prefix("minimal_spreadsheet"))
- .withService(driveService.getFullyQualifiedName())
- .execute();
+ @Override
+ protected Spreadsheet getVersion(UUID id, Double version) {
+ return getSpreadsheetService().getVersion(id.toString(), version);
+ }
- assertNotNull(spreadsheet);
- assertNotNull(spreadsheet.getId());
- assertEquals(ns.prefix("minimal_spreadsheet"), spreadsheet.getName());
+ private SpreadsheetService getSpreadsheetService() {
+ return new SpreadsheetService(SdkClients.adminClient().getHttpClient());
}
+ // ===================================================================
+ // SPREADSHEET-SPECIFIC TESTS
+ // ===================================================================
+
@Test
void test_createSpreadsheetWithoutService_fails(TestNamespace ns) {
assertThrows(
@@ -167,81 +192,6 @@ void test_createSpreadsheetWithInvalidService_fails(TestNamespace ns) {
"Creating spreadsheet with invalid service should fail");
}
- @Test
- void test_finderWithFields(TestNamespace ns) {
- DriveService driveService = DriveServiceTestFactory.createGoogleDrive(ns);
-
- Spreadsheet created =
- Spreadsheets.create()
- .name(ns.prefix("spreadsheet_fields"))
- .withDescription("Test spreadsheet for fields")
- .withService(driveService.getFullyQualifiedName())
- .execute();
-
- Spreadsheet fetched =
- Spreadsheets.find(created.getId().toString()).withFields("service", "owners").fetch();
-
- assertNotNull(fetched);
- assertEquals(created.getId(), fetched.getId());
- assertNotNull(fetched.getService());
- }
-
- @Test
- void test_finderByNameWithFields(TestNamespace ns) {
- DriveService driveService = DriveServiceTestFactory.createGoogleDrive(ns);
-
- Spreadsheet created =
- Spreadsheets.create()
- .name(ns.prefix("spreadsheet_name_fields"))
- .withService(driveService.getFullyQualifiedName())
- .execute();
-
- Spreadsheet fetched =
- Spreadsheets.findByName(created.getFullyQualifiedName())
- .withFields("service", "tags")
- .fetch();
-
- assertNotNull(fetched);
- assertEquals(created.getId(), fetched.getId());
- assertEquals(created.getFullyQualifiedName(), fetched.getFullyQualifiedName());
- }
-
- @Test
- void test_createMultipleSpreadsheetsUnderSameService(TestNamespace ns) {
- DriveService driveService = DriveServiceTestFactory.createGoogleDrive(ns);
-
- for (int i = 0; i < 3; i++) {
- Spreadsheet spreadsheet =
- Spreadsheets.create()
- .name(ns.prefix("spreadsheet_" + i))
- .withService(driveService.getFullyQualifiedName())
- .execute();
-
- assertNotNull(spreadsheet);
- assertNotNull(spreadsheet.getId());
- assertTrue(
- spreadsheet.getFullyQualifiedName().contains(ns.prefix("spreadsheet_" + i)),
- "FQN should contain spreadsheet name");
- }
- }
-
- @Test
- void test_getByNameWithFields(TestNamespace ns) {
- DriveService driveService = DriveServiceTestFactory.createGoogleDrive(ns);
-
- Spreadsheet created =
- Spreadsheets.create()
- .name(ns.prefix("spreadsheet_byname_fields"))
- .withService(driveService.getFullyQualifiedName())
- .execute();
-
- Spreadsheet fetched = Spreadsheets.getByName(created.getFullyQualifiedName(), "service,owners");
-
- assertNotNull(fetched);
- assertEquals(created.getId(), fetched.getId());
- assertNotNull(fetched.getService());
- }
-
@Test
void test_spreadsheetFQNStructure(TestNamespace ns) {
DriveService driveService = DriveServiceTestFactory.createGoogleDrive(ns);
@@ -325,6 +275,127 @@ void test_spreadsheetInDirectory(TestNamespace ns) {
directory.getFullyQualifiedName(), spreadsheet.getDirectory().getFullyQualifiedName());
}
+ @Test
+ void test_listSpreadsheetsByDirectory(TestNamespace ns) {
+ DriveService driveService = DriveServiceTestFactory.createGoogleDrive(ns);
+
+ Directory dir1 =
+ Directories.create()
+ .name(ns.prefix("reports"))
+ .withService(driveService.getFullyQualifiedName())
+ .withPath("/reports")
+ .execute();
+
+ Directory dir2 =
+ Directories.create()
+ .name(ns.prefix("analytics"))
+ .withService(driveService.getFullyQualifiedName())
+ .withPath("/analytics")
+ .execute();
+
+ for (int i = 0; i < 2; i++) {
+ Spreadsheets.create()
+ .name(ns.prefix("report_" + i))
+ .withService(driveService.getFullyQualifiedName())
+ .withParent(dir1.getEntityReference())
+ .execute();
+ Spreadsheets.create()
+ .name(ns.prefix("analytics_" + i))
+ .withService(driveService.getFullyQualifiedName())
+ .withParent(dir2.getEntityReference())
+ .execute();
+ }
+
+ ListParams params = new ListParams().withDirectory(dir1.getFullyQualifiedName());
+ ListResponse list = SdkClients.adminClient().spreadsheets().list(params);
+ assertTrue(list.getData().size() >= 2);
+ assertTrue(
+ list.getData().stream()
+ .allMatch(
+ s -> s.getDirectory() != null && s.getDirectory().getId().equals(dir1.getId())));
+
+ params = new ListParams().withDirectory(dir2.getFullyQualifiedName());
+ list = SdkClients.adminClient().spreadsheets().list(params);
+ assertTrue(list.getData().size() >= 2);
+ assertTrue(
+ list.getData().stream()
+ .allMatch(
+ s -> s.getDirectory() != null && s.getDirectory().getId().equals(dir2.getId())));
+ }
+
+ @Test
+ void test_listSpreadsheetsWithRootParameter(TestNamespace ns) {
+ DriveService driveService = DriveServiceTestFactory.createGoogleDrive(ns);
+
+ Directory sheetsDir =
+ Directories.create()
+ .name(ns.prefix("sheetsDir"))
+ .withService(driveService.getFullyQualifiedName())
+ .withPath("/sheets")
+ .execute();
+
+ Spreadsheets.create()
+ .name(ns.prefix("rootSpreadsheet1"))
+ .withService(driveService.getFullyQualifiedName())
+ .execute();
+
+ Spreadsheets.create()
+ .name(ns.prefix("rootSpreadsheet2"))
+ .withService(driveService.getFullyQualifiedName())
+ .execute();
+
+ Spreadsheets.create()
+ .name(ns.prefix("childSpreadsheet1"))
+ .withService(driveService.getFullyQualifiedName())
+ .withParent(sheetsDir.getEntityReference())
+ .execute();
+
+ Spreadsheets.create()
+ .name(ns.prefix("childSpreadsheet2"))
+ .withService(driveService.getFullyQualifiedName())
+ .withParent(sheetsDir.getEntityReference())
+ .execute();
+
+ ListParams params = new ListParams().withService(driveService.getFullyQualifiedName());
+ ListResponse allSpreadsheets =
+ SdkClients.adminClient().spreadsheets().list(params);
+ assertTrue(allSpreadsheets.getData().size() >= 4);
+
+ params = new ListParams().withService(driveService.getFullyQualifiedName()).withRoot("true");
+ ListResponse rootSpreadsheets =
+ SdkClients.adminClient().spreadsheets().list(params);
+ assertTrue(rootSpreadsheets.getData().size() >= 2);
+
+ for (Spreadsheet spreadsheet : rootSpreadsheets.getData()) {
+ assertNull(spreadsheet.getDirectory());
+ assertTrue(
+ spreadsheet.getName().equals(ns.prefix("rootSpreadsheet1"))
+ || spreadsheet.getName().equals(ns.prefix("rootSpreadsheet2")));
+ }
+
+ params = new ListParams().withService(driveService.getFullyQualifiedName()).withRoot("false");
+ ListResponse nonRootSpreadsheets =
+ SdkClients.adminClient().spreadsheets().list(params);
+ assertTrue(nonRootSpreadsheets.getData().size() >= 4);
+ }
+
+ @Test
+ void test_createSpreadsheetWithOptionalFields(TestNamespace ns) {
+ DriveService driveService = DriveServiceTestFactory.createGoogleDrive(ns);
+
+ Spreadsheet spreadsheet =
+ Spreadsheets.create()
+ .name(ns.prefix("spreadsheet_optional"))
+ .withDisplayName("Display Name for Spreadsheet")
+ .withDescription("Spreadsheet with optional fields")
+ .withService(driveService.getFullyQualifiedName())
+ .execute();
+
+ assertNotNull(spreadsheet);
+ assertEquals("Display Name for Spreadsheet", spreadsheet.getDisplayName());
+ assertEquals("Spreadsheet with optional fields", spreadsheet.getDescription());
+ }
+
@Test
void test_updateSpreadsheet(TestNamespace ns) {
DriveService driveService = DriveServiceTestFactory.createGoogleDrive(ns);
@@ -355,24 +426,7 @@ void test_updateSpreadsheet(TestNamespace ns) {
assertEquals(Integer.valueOf(1024000), updated.getSize());
}
- @Test
- void test_patchSpreadsheetAttributes(TestNamespace ns) {
- DriveService driveService = DriveServiceTestFactory.createGoogleDrive(ns);
-
- Spreadsheet spreadsheet =
- Spreadsheets.create()
- .name(ns.prefix("patchSpreadsheet"))
- .withService(driveService.getFullyQualifiedName())
- .execute();
-
- Spreadsheet fetched = Spreadsheets.get(spreadsheet.getId().toString());
- fetched.setDescription("patched description");
- Spreadsheet patched =
- Spreadsheets.update(spreadsheet.getId().toString()).entity(fetched).execute();
- assertEquals("patched description", patched.getDescription());
- }
-
- @org.junit.jupiter.api.Disabled(
+ @Disabled(
"Worksheet relationship not returned in spreadsheet fields - backend setFields needs worksheets support")
@Test
void test_spreadsheetWithWorksheets(TestNamespace ns) {
@@ -481,152 +535,48 @@ void test_spreadsheetFQNPatterns(TestNamespace ns) {
+ ns.prefix("budget"),
dirSpreadsheet.getFullyQualifiedName());
assertNotNull(dirSpreadsheet.getDirectory());
- assertEquals(dir2.getId(), dirSpreadsheet.getDirectory().getId());
- }
-
- @Test
- void test_spreadsheetsWithAndWithoutDirectory(TestNamespace ns) {
- DriveService driveService = DriveServiceTestFactory.createGoogleDrive(ns);
-
- Directory directory =
- Directories.create()
- .name(ns.prefix("reports"))
- .withService(driveService.getFullyQualifiedName())
- .withPath("/reports")
- .execute();
-
- for (int i = 0; i < 2; i++) {
- Spreadsheets.create()
- .name(ns.prefix("direct_spreadsheet_" + i))
- .withService(driveService.getFullyQualifiedName())
- .execute();
- }
-
- for (int i = 0; i < 2; i++) {
- Spreadsheets.create()
- .name(ns.prefix("dir_spreadsheet_" + i))
- .withService(driveService.getFullyQualifiedName())
- .withParent(directory.getEntityReference())
- .execute();
- }
-
- ListParams params = new ListParams().withService(driveService.getFullyQualifiedName());
- ListResponse list = SdkClients.adminClient().spreadsheets().list(params);
- assertTrue(list.getData().size() >= 4);
-
- long withDirectory = list.getData().stream().filter(s -> s.getDirectory() != null).count();
- long withoutDirectory = list.getData().stream().filter(s -> s.getDirectory() == null).count();
- assertTrue(withDirectory >= 2);
- assertTrue(withoutDirectory >= 2);
-
- params = new ListParams().withDirectory(directory.getFullyQualifiedName());
- list = SdkClients.adminClient().spreadsheets().list(params);
- assertTrue(list.getData().size() >= 2);
- assertTrue(list.getData().stream().allMatch(s -> s.getDirectory() != null));
- }
-
- @Test
- void test_listSpreadsheetsByDirectory(TestNamespace ns) {
- DriveService driveService = DriveServiceTestFactory.createGoogleDrive(ns);
-
- Directory dir1 =
- Directories.create()
- .name(ns.prefix("reports"))
- .withService(driveService.getFullyQualifiedName())
- .withPath("/reports")
- .execute();
-
- Directory dir2 =
- Directories.create()
- .name(ns.prefix("analytics"))
- .withService(driveService.getFullyQualifiedName())
- .withPath("/analytics")
- .execute();
-
- for (int i = 0; i < 2; i++) {
- Spreadsheets.create()
- .name(ns.prefix("report_" + i))
- .withService(driveService.getFullyQualifiedName())
- .withParent(dir1.getEntityReference())
- .execute();
- Spreadsheets.create()
- .name(ns.prefix("analytics_" + i))
- .withService(driveService.getFullyQualifiedName())
- .withParent(dir2.getEntityReference())
- .execute();
- }
-
- ListParams params = new ListParams().withDirectory(dir1.getFullyQualifiedName());
- ListResponse list = SdkClients.adminClient().spreadsheets().list(params);
- assertTrue(list.getData().size() >= 2);
- assertTrue(
- list.getData().stream()
- .allMatch(
- s -> s.getDirectory() != null && s.getDirectory().getId().equals(dir1.getId())));
-
- params = new ListParams().withDirectory(dir2.getFullyQualifiedName());
- list = SdkClients.adminClient().spreadsheets().list(params);
- assertTrue(list.getData().size() >= 2);
- assertTrue(
- list.getData().stream()
- .allMatch(
- s -> s.getDirectory() != null && s.getDirectory().getId().equals(dir2.getId())));
+ assertEquals(dir2.getId(), dirSpreadsheet.getDirectory().getId());
}
@Test
- void test_listSpreadsheetsWithRootParameter(TestNamespace ns) {
+ void test_spreadsheetsWithAndWithoutDirectory(TestNamespace ns) {
DriveService driveService = DriveServiceTestFactory.createGoogleDrive(ns);
- Directory sheetsDir =
+ Directory directory =
Directories.create()
- .name(ns.prefix("sheetsDir"))
+ .name(ns.prefix("reports"))
.withService(driveService.getFullyQualifiedName())
- .withPath("/sheets")
+ .withPath("/reports")
.execute();
- Spreadsheets.create()
- .name(ns.prefix("rootSpreadsheet1"))
- .withService(driveService.getFullyQualifiedName())
- .execute();
-
- Spreadsheets.create()
- .name(ns.prefix("rootSpreadsheet2"))
- .withService(driveService.getFullyQualifiedName())
- .execute();
-
- Spreadsheets.create()
- .name(ns.prefix("childSpreadsheet1"))
- .withService(driveService.getFullyQualifiedName())
- .withParent(sheetsDir.getEntityReference())
- .execute();
+ for (int i = 0; i < 2; i++) {
+ Spreadsheets.create()
+ .name(ns.prefix("direct_spreadsheet_" + i))
+ .withService(driveService.getFullyQualifiedName())
+ .execute();
+ }
- Spreadsheets.create()
- .name(ns.prefix("childSpreadsheet2"))
- .withService(driveService.getFullyQualifiedName())
- .withParent(sheetsDir.getEntityReference())
- .execute();
+ for (int i = 0; i < 2; i++) {
+ Spreadsheets.create()
+ .name(ns.prefix("dir_spreadsheet_" + i))
+ .withService(driveService.getFullyQualifiedName())
+ .withParent(directory.getEntityReference())
+ .execute();
+ }
ListParams params = new ListParams().withService(driveService.getFullyQualifiedName());
- ListResponse allSpreadsheets =
- SdkClients.adminClient().spreadsheets().list(params);
- assertTrue(allSpreadsheets.getData().size() >= 4);
-
- params = new ListParams().withService(driveService.getFullyQualifiedName()).withRoot("true");
- ListResponse rootSpreadsheets =
- SdkClients.adminClient().spreadsheets().list(params);
- assertTrue(rootSpreadsheets.getData().size() >= 2);
+ ListResponse list = SdkClients.adminClient().spreadsheets().list(params);
+ assertTrue(list.getData().size() >= 4);
- for (Spreadsheet spreadsheet : rootSpreadsheets.getData()) {
- assertNull(spreadsheet.getDirectory());
- assertTrue(
- spreadsheet.getName().equals(ns.prefix("rootSpreadsheet1"))
- || spreadsheet.getName().equals(ns.prefix("rootSpreadsheet2")));
- }
+ long withDirectory = list.getData().stream().filter(s -> s.getDirectory() != null).count();
+ long withoutDirectory = list.getData().stream().filter(s -> s.getDirectory() == null).count();
+ assertTrue(withDirectory >= 2);
+ assertTrue(withoutDirectory >= 2);
- params = new ListParams().withService(driveService.getFullyQualifiedName()).withRoot("false");
- ListResponse nonRootSpreadsheets =
- SdkClients.adminClient().spreadsheets().list(params);
- assertTrue(nonRootSpreadsheets.getData().size() >= 4);
+ params = new ListParams().withDirectory(directory.getFullyQualifiedName());
+ list = SdkClients.adminClient().spreadsheets().list(params);
+ assertTrue(list.getData().size() >= 2);
+ assertTrue(list.getData().stream().allMatch(s -> s.getDirectory() != null));
}
@Test
@@ -720,8 +670,7 @@ void test_listSpreadsheetsWithRootParameterEmptyResult(TestNamespace ns) {
}
}
- @org.junit.jupiter.api.Disabled(
- "Root filter not working reliably with parallel tests - needs investigation")
+ @Disabled("Root filter not working reliably with parallel tests - needs investigation")
@Test
void test_listSpreadsheetsWithRootParameterAcrossMultipleServices(TestNamespace ns) {
DriveService service1 = DriveServiceTestFactory.createGoogleDrive(ns, "googleSheetsService");
@@ -812,4 +761,190 @@ void test_listSpreadsheetsWithRootParameterAcrossMultipleServices(TestNamespace
SdkClients.adminClient().spreadsheets().list(params);
assertTrue(allExcelSpreadsheets.getData().size() >= 5);
}
+
+ @Test
+ void test_createSpreadsheet(TestNamespace ns) {
+ DriveService driveService = DriveServiceTestFactory.createGoogleDrive(ns);
+
+ Spreadsheet spreadsheet =
+ Spreadsheets.create()
+ .name(ns.prefix("spreadsheet"))
+ .withDescription("Test spreadsheet")
+ .withService(driveService.getFullyQualifiedName())
+ .execute();
+
+ assertNotNull(spreadsheet);
+ assertNotNull(spreadsheet.getId());
+ assertEquals(ns.prefix("spreadsheet"), spreadsheet.getName());
+ assertEquals("Test spreadsheet", spreadsheet.getDescription());
+ assertNotNull(spreadsheet.getService());
+ assertEquals(
+ driveService.getFullyQualifiedName(), spreadsheet.getService().getFullyQualifiedName());
+ }
+
+ @Test
+ void test_createSpreadsheetMinimal(TestNamespace ns) {
+ DriveService driveService = DriveServiceTestFactory.createGoogleDrive(ns);
+
+ Spreadsheet spreadsheet =
+ Spreadsheets.create()
+ .name(ns.prefix("minimal_spreadsheet"))
+ .withService(driveService.getFullyQualifiedName())
+ .execute();
+
+ assertNotNull(spreadsheet);
+ assertNotNull(spreadsheet.getId());
+ assertEquals(ns.prefix("minimal_spreadsheet"), spreadsheet.getName());
+ }
+
+ @Test
+ void test_getSpreadsheetById(TestNamespace ns) {
+ DriveService driveService = DriveServiceTestFactory.createGoogleDrive(ns);
+
+ Spreadsheet created =
+ Spreadsheets.create()
+ .name(ns.prefix("spreadsheet_get"))
+ .withService(driveService.getFullyQualifiedName())
+ .execute();
+
+ Spreadsheet fetched = Spreadsheets.get(created.getId().toString());
+
+ assertNotNull(fetched);
+ assertEquals(created.getId(), fetched.getId());
+ assertEquals(created.getName(), fetched.getName());
+ assertEquals(
+ created.getFullyQualifiedName(),
+ fetched.getFullyQualifiedName(),
+ "FQN should match between created and fetched");
+ }
+
+ @Test
+ void test_getSpreadsheetByName(TestNamespace ns) {
+ DriveService driveService = DriveServiceTestFactory.createGoogleDrive(ns);
+
+ Spreadsheet created =
+ Spreadsheets.create()
+ .name(ns.prefix("spreadsheet_getByName"))
+ .withService(driveService.getFullyQualifiedName())
+ .execute();
+
+ Spreadsheet fetched = Spreadsheets.getByName(created.getFullyQualifiedName());
+
+ assertNotNull(fetched);
+ assertEquals(created.getId(), fetched.getId());
+ assertEquals(created.getName(), fetched.getName());
+ assertEquals(created.getFullyQualifiedName(), fetched.getFullyQualifiedName());
+ }
+
+ @Test
+ void test_deleteSpreadsheet(TestNamespace ns) {
+ DriveService driveService = DriveServiceTestFactory.createGoogleDrive(ns);
+
+ Spreadsheet created =
+ Spreadsheets.create()
+ .name(ns.prefix("spreadsheet_delete"))
+ .withService(driveService.getFullyQualifiedName())
+ .execute();
+
+ assertNotNull(created.getId());
+
+ Spreadsheets.delete(created.getId().toString());
+
+ assertThrows(
+ Exception.class,
+ () -> Spreadsheets.get(created.getId().toString()),
+ "Getting deleted spreadsheet should fail");
+ }
+
+ @Test
+ void test_finderWithFields(TestNamespace ns) {
+ DriveService driveService = DriveServiceTestFactory.createGoogleDrive(ns);
+
+ Spreadsheet created =
+ Spreadsheets.create()
+ .name(ns.prefix("spreadsheet_fields"))
+ .withDescription("Test spreadsheet for fields")
+ .withService(driveService.getFullyQualifiedName())
+ .execute();
+
+ Spreadsheet fetched =
+ Spreadsheets.find(created.getId().toString()).withFields("service", "owners").fetch();
+
+ assertNotNull(fetched);
+ assertEquals(created.getId(), fetched.getId());
+ assertNotNull(fetched.getService());
+ }
+
+ @Test
+ void test_finderByNameWithFields(TestNamespace ns) {
+ DriveService driveService = DriveServiceTestFactory.createGoogleDrive(ns);
+
+ Spreadsheet created =
+ Spreadsheets.create()
+ .name(ns.prefix("spreadsheet_name_fields"))
+ .withService(driveService.getFullyQualifiedName())
+ .execute();
+
+ Spreadsheet fetched =
+ Spreadsheets.findByName(created.getFullyQualifiedName())
+ .withFields("service", "tags")
+ .fetch();
+
+ assertNotNull(fetched);
+ assertEquals(created.getId(), fetched.getId());
+ assertEquals(created.getFullyQualifiedName(), fetched.getFullyQualifiedName());
+ }
+
+ @Test
+ void test_getByNameWithFields(TestNamespace ns) {
+ DriveService driveService = DriveServiceTestFactory.createGoogleDrive(ns);
+
+ Spreadsheet created =
+ Spreadsheets.create()
+ .name(ns.prefix("spreadsheet_byname_fields"))
+ .withService(driveService.getFullyQualifiedName())
+ .execute();
+
+ Spreadsheet fetched = Spreadsheets.getByName(created.getFullyQualifiedName(), "service,owners");
+
+ assertNotNull(fetched);
+ assertEquals(created.getId(), fetched.getId());
+ assertNotNull(fetched.getService());
+ }
+
+ @Test
+ void test_createMultipleSpreadsheetsUnderSameService(TestNamespace ns) {
+ DriveService driveService = DriveServiceTestFactory.createGoogleDrive(ns);
+
+ for (int i = 0; i < 3; i++) {
+ Spreadsheet spreadsheet =
+ Spreadsheets.create()
+ .name(ns.prefix("spreadsheet_" + i))
+ .withService(driveService.getFullyQualifiedName())
+ .execute();
+
+ assertNotNull(spreadsheet);
+ assertNotNull(spreadsheet.getId());
+ assertTrue(
+ spreadsheet.getFullyQualifiedName().contains(ns.prefix("spreadsheet_" + i)),
+ "FQN should contain spreadsheet name");
+ }
+ }
+
+ @Test
+ void test_patchSpreadsheetAttributes(TestNamespace ns) {
+ DriveService driveService = DriveServiceTestFactory.createGoogleDrive(ns);
+
+ Spreadsheet spreadsheet =
+ Spreadsheets.create()
+ .name(ns.prefix("patchSpreadsheet"))
+ .withService(driveService.getFullyQualifiedName())
+ .execute();
+
+ Spreadsheet fetched = Spreadsheets.get(spreadsheet.getId().toString());
+ fetched.setDescription("patched description");
+ Spreadsheet patched =
+ Spreadsheets.update(spreadsheet.getId().toString()).entity(fetched).execute();
+ assertEquals("patched description", patched.getDescription());
+ }
}
diff --git a/openmetadata-sdk/src/main/java/org/openmetadata/sdk/services/drives/FolderService.java b/openmetadata-sdk/src/main/java/org/openmetadata/sdk/services/drives/FolderService.java
new file mode 100644
index 000000000000..6915e39c6bac
--- /dev/null
+++ b/openmetadata-sdk/src/main/java/org/openmetadata/sdk/services/drives/FolderService.java
@@ -0,0 +1,23 @@
+package org.openmetadata.sdk.services.drives;
+
+import org.openmetadata.schema.api.data.CreateFolder;
+import org.openmetadata.schema.entity.data.Folder;
+import org.openmetadata.sdk.exceptions.OpenMetadataException;
+import org.openmetadata.sdk.network.HttpClient;
+import org.openmetadata.sdk.network.HttpMethod;
+import org.openmetadata.sdk.services.EntityServiceBase;
+
+public class FolderService extends EntityServiceBase {
+ public FolderService(HttpClient httpClient) {
+ super(httpClient, "/v1/drive/folders");
+ }
+
+ @Override
+ protected Class getEntityClass() {
+ return Folder.class;
+ }
+
+ public Folder create(CreateFolder request) throws OpenMetadataException {
+ return httpClient.execute(HttpMethod.POST, basePath, request, Folder.class);
+ }
+}
diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/EntityRepository.java b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/EntityRepository.java
index a02f786c1573..bbcd40434df1 100644
--- a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/EntityRepository.java
+++ b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/EntityRepository.java
@@ -8746,6 +8746,8 @@ public void updateColumns(
List origColumns,
List updatedColumns,
BiPredicate columnMatch) {
+ origColumns = listOrEmpty(origColumns);
+ updatedColumns = listOrEmpty(updatedColumns);
List deletedColumns = new ArrayList<>();
List addedColumns = new ArrayList<>();
HashMap originalUpdatedColumnFqns = new HashMap<>();
diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/drive/FolderResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/drive/FolderResource.java
index 74de4d4137a2..b50a26d7a472 100644
--- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/drive/FolderResource.java
+++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/drive/FolderResource.java
@@ -9,6 +9,8 @@
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
+import jakarta.validation.constraints.Max;
+import jakarta.validation.constraints.Min;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.DELETE;
import jakarta.ws.rs.DefaultValue;
@@ -97,7 +99,12 @@ public ResultList list(
@Context UriInfo uriInfo,
@Context SecurityContext securityContext,
@QueryParam("fields") String fieldsParam,
- @QueryParam("limit") @DefaultValue("10") int limit,
+ @Parameter(description = "Limit the number of folders returned. (0 to 1000000, default = 10)")
+ @DefaultValue("10")
+ @QueryParam("limit")
+ @Min(value = 0, message = "must be greater than or equal to 0")
+ @Max(value = 1000000, message = "must be less than or equal to 1000000")
+ int limit,
@QueryParam("before") String before,
@QueryParam("after") String after,
@QueryParam("include") @DefaultValue("non-deleted") Include include) {