diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/DirectoryRepository.java b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/DirectoryRepository.java index 5ce795d44e0..41b9363e921 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/DirectoryRepository.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/DirectoryRepository.java @@ -146,13 +146,71 @@ public class DirectoryRepository extends EntityRepository { public void clearFields(Directory directory, EntityUtil.Fields fields) { directory.withUsageSummary( fields.contains("usageSummary") ? directory.getUsageSummary() : null); + directory.withNumberOfFiles( + fields.contains("numberOfFiles") ? directory.getNumberOfFiles() : null); + directory.withNumberOfSubDirectories( + fields.contains("numberOfSubDirectories") ? directory.getNumberOfSubDirectories() : null); } @Override public void setFields(Directory directory, EntityUtil.Fields fields) { directory.withService(getService(directory)); directory.withParent(getParentDirectory(directory)); - directory.withChildren(fields.contains("children") ? getChildrenRefs(directory) : null); + + // Calculate and set directory statistics + if (fields.contains("children") + || fields.contains("numberOfFiles") + || fields.contains("numberOfSubDirectories") + || fields.contains("totalSize")) { + List children = getChildrenRefs(directory); + directory.withChildren(fields.contains("children") ? children : null); + + // Calculate statistics from children + if (children != null && !children.isEmpty()) { + int fileCount = 0; + int dirCount = 0; + long totalSize = 0L; + + for (EntityReference child : children) { + if (FILE.equals(child.getType())) { + fileCount++; + // Get file size if available + try { + org.openmetadata.schema.entity.data.File file = + Entity.getEntity(child, "", Include.NON_DELETED); + if (file.getSize() != null) { + totalSize += file.getSize(); + } + } catch (Exception e) { + // Ignore if file can't be loaded + } + } else if (DIRECTORY.equals(child.getType())) { + dirCount++; + } else if (SPREADSHEET.equals(child.getType())) { + fileCount++; // Count spreadsheets as files + try { + org.openmetadata.schema.entity.data.Spreadsheet spreadsheet = + Entity.getEntity(child, "", Include.NON_DELETED); + if (spreadsheet.getSize() != null) { + totalSize += spreadsheet.getSize(); + } + } catch (Exception e) { + // Ignore if spreadsheet can't be loaded + } + } + } + + directory.withNumberOfFiles(fileCount); + directory.withNumberOfSubDirectories(dirCount); + // Convert long to Integer, checking for overflow + directory.withTotalSize( + totalSize > 0 + ? (totalSize > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) totalSize) + : null); + } + } else { + directory.withChildren(null); + } } @Override diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/DriveServiceRepository.java b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/DriveServiceRepository.java index 71f060c6cb7..17742bf2fcb 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/DriveServiceRepository.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/DriveServiceRepository.java @@ -47,7 +47,7 @@ import org.openmetadata.schema.type.csv.CsvHeader; import org.openmetadata.schema.type.csv.CsvImportResult; import org.openmetadata.service.Entity; import org.openmetadata.service.exception.EntityNotFoundException; -import org.openmetadata.service.resources.services.DriveServiceResource; +import org.openmetadata.service.resources.services.drive.DriveServiceResource; import org.openmetadata.service.util.EntityUtil; import org.openmetadata.service.util.FullyQualifiedName; diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/SpreadsheetRepository.java b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/SpreadsheetRepository.java index 94390ee0919..9c268bf0eb4 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/SpreadsheetRepository.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/SpreadsheetRepository.java @@ -263,7 +263,9 @@ public class SpreadsheetRepository extends EntityRepository { new CsvHeader().withName("domain"), new CsvHeader().withName("dataProducts"), new CsvHeader().withName("experts"), - new CsvHeader().withName("reviewers")); + new CsvHeader().withName("reviewers"), + new CsvHeader().withName("createdTime"), + new CsvHeader().withName("modifiedTime")); DOCUMENTATION = new CsvDocumentation().withHeaders(HEADERS).withSummary("Spreadsheet"); } @@ -339,7 +341,11 @@ public class SpreadsheetRepository extends EntityRepository { Pair.of(9, TagLabel.TagSource.CLASSIFICATION), Pair.of(10, TagLabel.TagSource.GLOSSARY)))) .withDomains(getDomains(printer, csvRecord, 11)) - .withDataProducts(getDataProducts(printer, csvRecord, 12)); + .withDataProducts(getDataProducts(printer, csvRecord, 12)) + .withCreatedTime( + nullOrEmpty(csvRecord.get(15)) ? null : Long.parseLong(csvRecord.get(15))) + .withModifiedTime( + nullOrEmpty(csvRecord.get(16)) ? null : Long.parseLong(csvRecord.get(16))); if (processRecord) { createEntity(printer, csvRecord, newSpreadsheet, SPREADSHEET); @@ -353,7 +359,11 @@ public class SpreadsheetRepository extends EntityRepository { addField(recordList, entity.getDisplayName()); addField(recordList, entity.getDescription()); addField(recordList, entity.getDirectory().getFullyQualifiedName()); - addField(recordList, entity.getMimeType().toString()); + addField(recordList, entity.getMimeType() != null ? entity.getMimeType().toString() : ""); + addField( + recordList, entity.getCreatedTime() != null ? entity.getCreatedTime().toString() : ""); + addField( + recordList, entity.getModifiedTime() != null ? entity.getModifiedTime().toString() : ""); addField(recordList, entity.getPath()); addField(recordList, entity.getSize() != null ? entity.getSize().toString() : ""); addField(recordList, entity.getFileVersion()); @@ -415,6 +425,8 @@ public class SpreadsheetRepository extends EntityRepository { @Override public void entitySpecificUpdate(boolean consolidatingChanges) { recordChange("mimeType", original.getMimeType(), updated.getMimeType()); + recordChange("createdTime", original.getCreatedTime(), updated.getCreatedTime()); + recordChange("modifiedTime", original.getModifiedTime(), updated.getModifiedTime()); recordChange("path", original.getPath(), updated.getPath()); recordChange("driveFileId", original.getDriveFileId(), updated.getDriveFileId()); recordChange("size", original.getSize(), updated.getSize()); diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/drives/DirectoryMapper.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/drives/DirectoryMapper.java index 12b389b86e3..170bd8febf0 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/drives/DirectoryMapper.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/drives/DirectoryMapper.java @@ -18,6 +18,7 @@ public class DirectoryMapper implements EntityMapper : null) .withPath(create.getPath()) .withIsShared(create.getIsShared()) - .withSourceUrl(create.getSourceUrl()); + .withSourceUrl(create.getSourceUrl()) + .withDirectoryType(create.getDirectoryType()); } } diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/drives/DirectoryResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/drives/DirectoryResource.java index b7d13faf06a..7037e94e691 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/drives/DirectoryResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/drives/DirectoryResource.java @@ -76,7 +76,7 @@ import org.openmetadata.service.security.Authorizer; public class DirectoryResource extends EntityResource { public static final String COLLECTION_PATH = "v1/drives/directories/"; static final String FIELDS = - "owners,children,parent,usageSummary,tags,extension,domains,sourceHash,lifeCycle,votes,followers"; + "owners,children,parent,usageSummary,tags,extension,domains,sourceHash,lifeCycle,votes,followers,numberOfFiles,numberOfSubDirectories,totalSize,directoryType"; private final DirectoryMapper mapper = new DirectoryMapper(); @Override diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/drives/SpreadsheetMapper.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/drives/SpreadsheetMapper.java index 90d10b59937..9e0e5a662cf 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/drives/SpreadsheetMapper.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/drives/SpreadsheetMapper.java @@ -18,7 +18,9 @@ public class SpreadsheetMapper implements EntityMapper { public static final String COLLECTION_PATH = "v1/drives/spreadsheets/"; static final String FIELDS = - "owners,directory,worksheets,usageSummary,tags,extension,domains,sourceHash,lifeCycle,votes,followers"; + "owners,directory,worksheets,usageSummary,tags,extension,domains,sourceHash,lifeCycle,votes,followers,mimeType,createdTime,modifiedTime"; private final SpreadsheetMapper mapper = new SpreadsheetMapper(); @Override diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/drives/WorksheetResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/drives/WorksheetResource.java index 2aaccaac76b..1b28787cd4c 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/drives/WorksheetResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/drives/WorksheetResource.java @@ -73,7 +73,7 @@ import org.openmetadata.service.security.Authorizer; public class WorksheetResource extends EntityResource { public static final String COLLECTION_PATH = "v1/drives/worksheets/"; static final String FIELDS = - "owners,spreadsheet,columns,sampleData,usageSummary,tags,extension,domains,sourceHash,lifeCycle,votes,followers"; + "owners,spreadsheet,columns,sampleData,usageSummary,tags,extension,domains,sourceHash,lifeCycle,votes,followers,rowCount"; private final WorksheetMapper mapper = new WorksheetMapper(); @Override diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/drive/DriveServiceMapper.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/drive/DriveServiceMapper.java new file mode 100644 index 00000000000..b9f75101ed0 --- /dev/null +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/drive/DriveServiceMapper.java @@ -0,0 +1,15 @@ +package org.openmetadata.service.resources.services.drive; + +import org.openmetadata.schema.api.services.CreateDriveService; +import org.openmetadata.schema.entity.services.DriveService; +import org.openmetadata.service.mapper.EntityMapper; + +public class DriveServiceMapper implements EntityMapper { + @Override + public DriveService createToEntity(CreateDriveService create, String user) { + return copy(new DriveService(), create, user) + .withServiceType(create.getServiceType()) + .withConnection(create.getConnection()) + .withIngestionRunner(create.getIngestionRunner()); + } +} diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/DriveServiceResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/drive/DriveServiceResource.java similarity index 98% rename from openmetadata-service/src/main/java/org/openmetadata/service/resources/services/DriveServiceResource.java rename to openmetadata-service/src/main/java/org/openmetadata/service/resources/services/drive/DriveServiceResource.java index bd7442b1ad7..1bc6e295649 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/DriveServiceResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/drive/DriveServiceResource.java @@ -11,7 +11,7 @@ * limitations under the License. */ -package org.openmetadata.service.resources.services; +package org.openmetadata.service.resources.services.drive; import io.swagger.v3.oas.annotations.ExternalDocumentation; import io.swagger.v3.oas.annotations.Operation; @@ -64,6 +64,7 @@ import org.openmetadata.service.Entity; import org.openmetadata.service.jdbi3.DriveServiceRepository; import org.openmetadata.service.limits.Limits; import org.openmetadata.service.resources.Collection; +import org.openmetadata.service.resources.services.ServiceEntityResource; import org.openmetadata.service.security.Authorizer; import org.openmetadata.service.security.policyevaluator.OperationContext; @@ -81,6 +82,7 @@ public class DriveServiceResource extends ServiceEntityResource { public static final String COLLECTION_PATH = "v1/services/driveServices/"; public static final String FIELDS = "pipelines,owners,tags,domains,followers"; + private final DriveServiceMapper driveServiceMapper = new DriveServiceMapper(); @Override public DriveService addHref(UriInfo uriInfo, DriveService service) { @@ -660,10 +662,7 @@ public class DriveServiceResource } private DriveService getService(CreateDriveService create, String user) { - return repository - .copy(new DriveService(), create, user) - .withServiceType(create.getServiceType()) - .withConnection(create.getConnection()); + return driveServiceMapper.createToEntity(create, user); } @Override diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/secrets/converter/ClassConverterFactory.java b/openmetadata-service/src/main/java/org/openmetadata/service/secrets/converter/ClassConverterFactory.java index 23ee42358ea..b6622388cba 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/secrets/converter/ClassConverterFactory.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/secrets/converter/ClassConverterFactory.java @@ -46,6 +46,7 @@ import org.openmetadata.schema.services.connections.database.UnityCatalogConnect import org.openmetadata.schema.services.connections.database.datalake.GCSConfig; import org.openmetadata.schema.services.connections.database.deltalake.StorageConfig; import org.openmetadata.schema.services.connections.database.iceberg.IcebergFileSystem; +import org.openmetadata.schema.services.connections.drive.GoogleDriveConnection; import org.openmetadata.schema.services.connections.mlmodel.VertexAIConnection; import org.openmetadata.schema.services.connections.pipeline.AirflowConnection; import org.openmetadata.schema.services.connections.pipeline.MatillionConnection; @@ -78,6 +79,7 @@ public final class ClassConverterFactory { Map.entry(GCSConfig.class, new GCPConfigClassConverter()), Map.entry(GCPCredentials.class, new GcpCredentialsClassConverter()), Map.entry(GCSConnection.class, new GcpConnectionClassConverter()), + Map.entry(GoogleDriveConnection.class, new GoogleDriveConnectionClassConverter()), Map.entry(HiveConnection.class, new HiveConnectionClassConverter()), Map.entry(IcebergConnection.class, new IcebergConnectionClassConverter()), Map.entry(IcebergFileSystem.class, new IcebergFileSystemClassConverter()), diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/secrets/converter/GoogleDriveConnectionClassConverter.java b/openmetadata-service/src/main/java/org/openmetadata/service/secrets/converter/GoogleDriveConnectionClassConverter.java new file mode 100644 index 00000000000..04338a0f497 --- /dev/null +++ b/openmetadata-service/src/main/java/org/openmetadata/service/secrets/converter/GoogleDriveConnectionClassConverter.java @@ -0,0 +1,38 @@ +/* + * Copyright 2021 Collate + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.openmetadata.service.secrets.converter; + +import java.util.List; +import org.openmetadata.schema.security.credentials.GCPCredentials; +import org.openmetadata.schema.services.connections.drive.GoogleDriveConnection; +import org.openmetadata.schema.utils.JsonUtils; + +/** Converter class to get a `GoogleDriveConnection` object. */ +public class GoogleDriveConnectionClassConverter extends ClassConverter { + + public GoogleDriveConnectionClassConverter() { + super(GoogleDriveConnection.class); + } + + @Override + public Object convert(Object object) { + GoogleDriveConnection googleDriveConnection = + (GoogleDriveConnection) JsonUtils.convertValue(object, this.clazz); + + tryToConvertOrFail(googleDriveConnection.getCredentials(), List.of(GCPCredentials.class)) + .ifPresent(obj -> googleDriveConnection.setCredentials((GCPCredentials) obj)); + + return googleDriveConnection; + } +} diff --git a/openmetadata-service/src/test/java/org/openmetadata/service/resources/services/DriveServiceResourceTest.java b/openmetadata-service/src/test/java/org/openmetadata/service/resources/services/DriveServiceResourceTest.java index 99cbde427af..eeecb7e8457 100644 --- a/openmetadata-service/src/test/java/org/openmetadata/service/resources/services/DriveServiceResourceTest.java +++ b/openmetadata-service/src/test/java/org/openmetadata/service/resources/services/DriveServiceResourceTest.java @@ -55,6 +55,7 @@ import org.openmetadata.schema.type.FileType; import org.openmetadata.schema.utils.JsonUtils; import org.openmetadata.schema.utils.ResultList; import org.openmetadata.service.Entity; +import org.openmetadata.service.resources.services.drive.DriveServiceResource; import org.openmetadata.service.util.TestUtils; @Slf4j diff --git a/openmetadata-spec/src/main/resources/json/schema/api/data/createSpreadsheet.json b/openmetadata-spec/src/main/resources/json/schema/api/data/createSpreadsheet.json index 75e26c1fcc1..ee43c416db6 100644 --- a/openmetadata-spec/src/main/resources/json/schema/api/data/createSpreadsheet.json +++ b/openmetadata-spec/src/main/resources/json/schema/api/data/createSpreadsheet.json @@ -5,7 +5,9 @@ "description": "Create Spreadsheet entity request", "type": "object", "javaType": "org.openmetadata.schema.api.data.CreateSpreadsheet", - "javaInterfaces": ["org.openmetadata.schema.CreateEntity"], + "javaInterfaces": [ + "org.openmetadata.schema.CreateEntity" + ], "properties": { "name": { "description": "Name that identifies this spreadsheet.", @@ -92,8 +94,19 @@ "extension": { "description": "Entity extension data with custom attributes added to the entity.", "$ref": "../../type/basic.json#/definitions/entityExtension" + }, + "createdTime": { + "description": "Spreadsheet creation timestamp", + "$ref": "../../type/basic.json#/definitions/timestamp" + }, + "modifiedTime": { + "description": "Last modification timestamp", + "$ref": "../../type/basic.json#/definitions/timestamp" } }, - "required": ["name", "service"], + "required": [ + "name", + "service" + ], "additionalProperties": false } \ No newline at end of file diff --git a/openmetadata-spec/src/main/resources/json/schema/api/services/createDriveService.json b/openmetadata-spec/src/main/resources/json/schema/api/services/createDriveService.json index b941d05ed0b..7534d2c2bee 100644 --- a/openmetadata-spec/src/main/resources/json/schema/api/services/createDriveService.json +++ b/openmetadata-spec/src/main/resources/json/schema/api/services/createDriveService.json @@ -5,7 +5,9 @@ "description": "Create Drive Service entity request", "type": "object", "javaType": "org.openmetadata.schema.api.services.CreateDriveService", - "javaInterfaces": ["org.openmetadata.schema.CreateEntity"], + "javaInterfaces": [ + "org.openmetadata.schema.CreateEntity" + ], "properties": { "name": { "description": "Name that identifies this drive service.", @@ -53,8 +55,15 @@ "$ref": "../../type/basic.json#/definitions/fullyQualifiedEntityName" }, "default": null + }, + "ingestionRunner": { + "description": "The ingestion agent responsible for executing the ingestion pipeline.", + "$ref": "../../type/entityReference.json" } }, - "required": ["name", "serviceType"], + "required": [ + "name", + "serviceType" + ], "additionalProperties": false } \ No newline at end of file diff --git a/openmetadata-ui/src/main/resources/ui/src/generated/api/data/createSpreadsheet.ts b/openmetadata-ui/src/main/resources/ui/src/generated/api/data/createSpreadsheet.ts index 9e06d051244..be8c300f87d 100644 --- a/openmetadata-ui/src/main/resources/ui/src/generated/api/data/createSpreadsheet.ts +++ b/openmetadata-ui/src/main/resources/ui/src/generated/api/data/createSpreadsheet.ts @@ -14,6 +14,10 @@ * Create Spreadsheet entity request */ export interface CreateSpreadsheet { + /** + * Spreadsheet creation timestamp + */ + createdTime?: number; /** * List of fully qualified names of data products this entity is part of. */ @@ -50,6 +54,10 @@ export interface CreateSpreadsheet { * MIME type of the spreadsheet file */ mimeType?: SpreadsheetMIMEType; + /** + * Last modification timestamp + */ + modifiedTime?: number; /** * Name that identifies this spreadsheet. */ diff --git a/openmetadata-ui/src/main/resources/ui/src/generated/api/services/createDriveService.ts b/openmetadata-ui/src/main/resources/ui/src/generated/api/services/createDriveService.ts index a7dcb6c8e02..41e7a2061be 100644 --- a/openmetadata-ui/src/main/resources/ui/src/generated/api/services/createDriveService.ts +++ b/openmetadata-ui/src/main/resources/ui/src/generated/api/services/createDriveService.ts @@ -31,6 +31,10 @@ export interface CreateDriveService { * Fully qualified names of the domains the Drive Service belongs to. */ domains?: string[]; + /** + * The ingestion agent responsible for executing the ingestion pipeline. + */ + ingestionRunner?: EntityReference; /** * Name that identifies this drive service. */ @@ -293,14 +297,16 @@ export enum DriveServiceType { } /** - * Owners of this Drive service. + * The ingestion agent responsible for executing the ingestion pipeline. * - * This schema defines the EntityReferenceList type used for referencing an entity. + * This schema defines the EntityReference type used for referencing an entity. * EntityReference is used for capturing relationships from one entity to another. For * example, a table has an attribute called database of type EntityReference that captures * the relationship of a table `belongs to a` database. * - * This schema defines the EntityReference type used for referencing an entity. + * Owners of this Drive service. + * + * This schema defines the EntityReferenceList type used for referencing an entity. * EntityReference is used for capturing relationships from one entity to another. For * example, a table has an attribute called database of type EntityReference that captures * the relationship of a table `belongs to a` database. diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/de-de.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/de-de.json index ec2c7d5ee0d..6671c3ea649 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/de-de.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/de-de.json @@ -1608,6 +1608,7 @@ "sub-domain-lowercase-plural": "subdomains", "sub-domain-plural": "Subdomains", "sub-team-plural": "Unter-Teams", + "subdirectory-plural": "Unterverzeichnisse", "submit": "Einreichen", "subscription": "Subscription", "success": "Erfolg", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/en-us.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/en-us.json index 7dd3341da05..ff5804df160 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/en-us.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/en-us.json @@ -1608,6 +1608,7 @@ "sub-domain-lowercase-plural": "sub domains", "sub-domain-plural": "Sub Domains", "sub-team-plural": "Sub Teams", + "subdirectory-plural": "Subdirectories", "submit": "Submit", "subscription": "Subscription", "success": "Success", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/es-es.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/es-es.json index 7b4d25b353e..1e1731fc360 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/es-es.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/es-es.json @@ -1608,6 +1608,7 @@ "sub-domain-lowercase-plural": "subdominios", "sub-domain-plural": "Subdominios", "sub-team-plural": "Subequipos", + "subdirectory-plural": "Subdirectorios", "submit": "Enviar", "subscription": "Subscripción", "success": "Éxito", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/fr-fr.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/fr-fr.json index f0c1f421301..1a6924dd123 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/fr-fr.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/fr-fr.json @@ -1608,6 +1608,7 @@ "sub-domain-lowercase-plural": "sous-domaines", "sub-domain-plural": "Sous-Domaines", "sub-team-plural": "Sous-Équipes", + "subdirectory-plural": "Sous-répertoires", "submit": "Soumettre", "subscription": "Subscription", "success": "Succès", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/gl-es.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/gl-es.json index a4fd5266f4d..c04a735d52e 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/gl-es.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/gl-es.json @@ -1608,6 +1608,7 @@ "sub-domain-lowercase-plural": "subdominios", "sub-domain-plural": "Subdominios", "sub-team-plural": "Subequipos", + "subdirectory-plural": "Subdirectorios", "submit": "Enviar", "subscription": "Subscrición", "success": "Éxito", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/he-he.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/he-he.json index 6e3efeab461..568df642089 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/he-he.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/he-he.json @@ -1608,6 +1608,7 @@ "sub-domain-lowercase-plural": "תתי-תחומים", "sub-domain-plural": "תתי-תחומים", "sub-team-plural": "תתי-צוותים", + "subdirectory-plural": "תתי-תיקיות", "submit": "שלח", "subscription": "מינוי", "success": "הצלחה", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ja-jp.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ja-jp.json index f0c4d9725c6..d201dc67630 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ja-jp.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ja-jp.json @@ -1608,6 +1608,7 @@ "sub-domain-lowercase-plural": "サブドメイン", "sub-domain-plural": "サブドメイン", "sub-team-plural": "サブチーム", + "subdirectory-plural": "サブディレクトリ", "submit": "送信", "subscription": "サブスクリプション", "success": "成功", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ko-kr.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ko-kr.json index f5fecd683f9..8c42492a6d0 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ko-kr.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ko-kr.json @@ -1608,6 +1608,7 @@ "sub-domain-lowercase-plural": "하위 도메인들", "sub-domain-plural": "하위 도메인들", "sub-team-plural": "하위 팀들", + "subdirectory-plural": "하위 디렉토리", "submit": "제출", "subscription": "구독", "success": "성공", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/mr-in.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/mr-in.json index f1436a77cac..4222925ce78 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/mr-in.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/mr-in.json @@ -1608,6 +1608,7 @@ "sub-domain-lowercase-plural": "उप डोमेन्स", "sub-domain-plural": "उप डोमेन्स", "sub-team-plural": "उप टीम्स", + "subdirectory-plural": "उपनिर्देशिका", "submit": "प्रस्तुत करा", "subscription": "सदस्यता", "success": "यश", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/nl-nl.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/nl-nl.json index 04f76ea60de..24aedae4081 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/nl-nl.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/nl-nl.json @@ -1608,6 +1608,7 @@ "sub-domain-lowercase-plural": "subdomeinen", "sub-domain-plural": "Subdomeinen", "sub-team-plural": "Subteams", + "subdirectory-plural": "Submappen", "submit": "Indienen", "subscription": "Abonnement", "success": "Succes", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/pr-pr.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/pr-pr.json index c9914d77878..e030ebabe8a 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/pr-pr.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/pr-pr.json @@ -1608,6 +1608,7 @@ "sub-domain-lowercase-plural": "زیر دامنه‌ها", "sub-domain-plural": "زیر دامنه‌ها", "sub-team-plural": "زیر تیم‌ها", + "subdirectory-plural": "زیرشاخه‌ها", "submit": "ارسال", "subscription": "اشتراک", "success": "موفقیت", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-br.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-br.json index d750e01f501..6a7981304eb 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-br.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-br.json @@ -1608,6 +1608,7 @@ "sub-domain-lowercase-plural": "subdomínios", "sub-domain-plural": "Subdomínios", "sub-team-plural": "Subequipes", + "subdirectory-plural": "Subdiretórios", "submit": "Enviar", "subscription": "Assinatura", "success": "Sucesso", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-pt.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-pt.json index 79abb4633d2..7ffed3be2ae 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-pt.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-pt.json @@ -1608,6 +1608,7 @@ "sub-domain-lowercase-plural": "subdomínios", "sub-domain-plural": "Subdomínios", "sub-team-plural": "Subequipas", + "subdirectory-plural": "Subdiretórios", "submit": "Enviar", "subscription": "Assinatura", "success": "Sucesso", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ru-ru.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ru-ru.json index 9950660e80a..ce909c056a1 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ru-ru.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ru-ru.json @@ -1608,6 +1608,7 @@ "sub-domain-lowercase-plural": "поддомены", "sub-domain-plural": "Поддомены", "sub-team-plural": "Подгруппы", + "subdirectory-plural": "Подкаталоги", "submit": "Подтвердить", "subscription": "Подписка", "success": "Успешно", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/th-th.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/th-th.json index 63fa924c0a5..cabbd24d72c 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/th-th.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/th-th.json @@ -1608,6 +1608,7 @@ "sub-domain-lowercase-plural": "โดเมนย่อยหลายรายการ", "sub-domain-plural": "โดเมนย่อยหลายรายการ", "sub-team-plural": "ทีมย่อย", + "subdirectory-plural": "ไดเรกทอรีย่อยหลายรายการ", "submit": "ส่ง", "subscription": "การสมัครสมาชิก", "success": "สำเร็จ", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/tr-tr.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/tr-tr.json index c34abaed1fc..2f1220b633b 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/tr-tr.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/tr-tr.json @@ -1608,6 +1608,7 @@ "sub-domain-lowercase-plural": "alt alan adları", "sub-domain-plural": "Alt Alan Adları", "sub-team-plural": "Alt Takımlar", + "subdirectory-plural": "Alt dizinler", "submit": "Gönder", "subscription": "Abonelik", "success": "Başarılı", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-cn.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-cn.json index 10b49f52daa..c032c6ad09d 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-cn.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-cn.json @@ -1608,6 +1608,7 @@ "sub-domain-lowercase-plural": "子域", "sub-domain-plural": "子域", "sub-team-plural": "子团队", + "subdirectory-plural": "子目录", "submit": "提交", "subscription": "订阅", "success": "成功", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-tw.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-tw.json index c6d2c247dad..67de37b82d5 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-tw.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-tw.json @@ -1608,6 +1608,7 @@ "sub-domain-lowercase-plural": "子領域", "sub-domain-plural": "子領域", "sub-team-plural": "子團隊", + "subdirectory-plural": "子目錄", "submit": "提交", "subscription": "訂閱", "success": "成功", diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/DataAssetsHeader.utils.test.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/DataAssetsHeader.utils.test.tsx index 171355ad387..6db002d079a 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/DataAssetsHeader.utils.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/utils/DataAssetsHeader.utils.test.tsx @@ -11,8 +11,14 @@ * limitations under the License. */ +import { render } from '@testing-library/react'; +import { Tooltip, Typography } from 'antd'; import React from 'react'; import { EntityType } from '../enums/entity.enum'; +import { + Spreadsheet, + SpreadsheetMIMEType, +} from '../generated/entity/data/spreadsheet'; import { mockContainerData } from '../mocks/ContainerVersion.mock'; import { MOCK_DASHBOARD_DATA_MODEL } from '../mocks/DashboardDataModel.mock'; import { mockDashboardData } from '../mocks/dashboardVersion.mock'; @@ -35,13 +41,14 @@ import { mockStoredProcedureData } from '../mocks/StoredProcedure.mock'; import { MOCK_TABLE } from '../mocks/TableData.mock'; import { mockTopicData } from '../mocks/TopicVersion.mock'; import { + ExtraInfoLabel, getDataAssetsHeaderInfo, getEntityExtraInfoLength, } from './DataAssetsHeader.utils'; +// Mock only ExtraInfoLink, not ExtraInfoLabel as we want to test it jest.mock('./DataAssetsHeader.utils', () => ({ ...jest.requireActual('./DataAssetsHeader.utils'), - ExtraInfoLabel: jest.fn().mockImplementation(({ value }) => value), ExtraInfoLink: jest.fn().mockImplementation(({ value }) => value), })); jest.mock('./EntityUtils', () => ({ @@ -87,6 +94,17 @@ jest.mock('./TableUtils', () => ({ getUsagePercentile: jest.fn().mockReturnValue('getUsagePercentile'), })); +jest.mock('./date-time/DateTimeUtils', () => ({ + ...jest.requireActual('./date-time/DateTimeUtils'), + formatDateTime: jest + .fn() + .mockImplementation((timestamp) => `formatted-${timestamp}`), + getEpochMillisForPastDays: jest + .fn() + .mockImplementation((days) => Date.now() - days * 24 * 60 * 60 * 1000), + getCurrentMillis: jest.fn().mockReturnValue(Date.now()), +})); + jest.mock('../constants/constants', () => ({ ...jest.requireActual('../constants/constants'), NO_DATA_PLACEHOLDER: jest.fn().mockReturnValue('---'), @@ -506,6 +524,76 @@ describe('Tests for DataAssetsHeaderUtils', () => { ); }); + // Test for Spreadsheet entity + it('Function getDataAssetsHeaderInfo should return data for Spreadsheet entity', () => { + const mockSpreadsheet: Spreadsheet = { + id: 'spreadsheet-123', + name: 'test-spreadsheet', + fullyQualifiedName: 'service.directory.test-spreadsheet', + mimeType: + 'application/vnd.google-apps.spreadsheet' as SpreadsheetMIMEType, + createdTime: 1609459200000, + modifiedTime: 1640995200000, + service: { + id: 'service-123', + name: 'google-drive', + type: 'driveService', + }, + }; + + const assetData = getDataAssetsHeaderInfo( + EntityType.SPREADSHEET, + mockSpreadsheet, + 'test-spreadsheet', + [] + ); + + // contains all breadcrumbs + expect(assetData.breadcrumbs).toEqual([{ name: 'entityName', url: 'url' }]); + + // contains extra data + expect(JSON.stringify(assetData.extraInfo)).toContain('label.mime-type'); + expect(JSON.stringify(assetData.extraInfo)).toContain( + 'application/vnd.google-apps.spreadsheet' + ); + + expect(JSON.stringify(assetData.extraInfo)).toContain('label.created-time'); + expect(JSON.stringify(assetData.extraInfo)).toContain( + 'formatted-1609459200000' + ); + + expect(JSON.stringify(assetData.extraInfo)).toContain( + 'label.modified-time' + ); + expect(JSON.stringify(assetData.extraInfo)).toContain( + 'formatted-1640995200000' + ); + + // Test with missing optional data + const assetWithNoExtraData = getDataAssetsHeaderInfo( + EntityType.SPREADSHEET, + { + ...mockSpreadsheet, + mimeType: undefined, + createdTime: undefined, + modifiedTime: undefined, + }, + 'test-spreadsheet', + [] + ); + + // Should not contain extra data when fields are undefined + expect(JSON.stringify(assetWithNoExtraData.extraInfo)).not.toContain( + 'label.mime-type' + ); + expect(JSON.stringify(assetWithNoExtraData.extraInfo)).not.toContain( + 'label.created-time' + ); + expect(JSON.stringify(assetWithNoExtraData.extraInfo)).not.toContain( + 'label.modified-time' + ); + }); + // Test for Search entity it('Function getDataAssetsHeaderInfo should return data for Search entity', () => { // Search Service @@ -652,6 +740,30 @@ describe('Tests for DataAssetsHeaderUtils', () => { }); }); +describe('ExtraInfoLabel', () => { + it('should handle React node as value', () => { + const nodeValue = ( + + + Truncated text value + + + ); + + const { container } = render( + + ); + + // Check that the component renders without error + expect( + container.querySelector('.extra-info-container') + ).toBeInTheDocument(); + expect( + container.querySelector('.extra-info-label-heading') + ).toHaveTextContent('MIME Type'); + }); +}); + describe('getEntityExtraInfoLength', () => { it('should return 0 for non-React elements', () => { expect(getEntityExtraInfoLength(null)).toBe(0); diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/DataAssetsHeader.utils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/DataAssetsHeader.utils.tsx index 21af5c30030..819e7babfe8 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/DataAssetsHeader.utils.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/utils/DataAssetsHeader.utils.tsx @@ -63,6 +63,7 @@ import { PipelineService } from '../generated/entity/services/pipelineService'; import { SearchService } from '../generated/entity/services/searchService'; import { SecurityService } from '../generated/entity/services/securityService'; import { StorageService } from '../generated/entity/services/storageService'; +import { formatDateTime } from './date-time/DateTimeUtils'; import { getBreadcrumbForEntitiesWithServiceOnly, getBreadcrumbForEntityWithParent, @@ -103,7 +104,7 @@ export const ExtraInfoLabel = ({ return (
{!isEmpty(label) && ( {label} @@ -834,7 +835,13 @@ export const getDataAssetsHeaderInfo = ( /> + + {spreadsheet.mimeType} + + + } /> )} @@ -846,7 +853,7 @@ export const getDataAssetsHeaderInfo = ( /> )} @@ -858,7 +865,7 @@ export const getDataAssetsHeaderInfo = ( /> )}