From 5e04e48294710834a3a3a66dd42015e4aaf928cd Mon Sep 17 00:00:00 2001 From: Sriharsha Chintalapani Date: Fri, 6 Mar 2026 14:36:14 -0800 Subject: [PATCH 01/20] Feature #18173: Improve Version API, through paginatio, get x latest versions, specifict time, specific metadata changes --- .../ingestion/ometa/mixins/version_mixin.py | 16 +- ingestion/src/metadata/sdk/entities/base.py | 20 +- .../it/tests/APICollectionResourceIT.java | 5 + .../it/tests/APIEndpointResourceIT.java | 5 + .../it/tests/APIServiceResourceIT.java | 5 + .../openmetadata/it/tests/BaseEntityIT.java | 77 +++++++ .../openmetadata/it/tests/BotResourceIT.java | 5 + .../it/tests/ChartResourceIT.java | 5 + .../it/tests/ClassificationResourceIT.java | 5 + .../it/tests/ContainerResourceIT.java | 5 + .../tests/DashboardDataModelResourceIT.java | 5 + .../it/tests/DashboardResourceIT.java | 5 + .../it/tests/DashboardServiceResourceIT.java | 5 + .../it/tests/DataContractResourceIT.java | 5 + .../it/tests/DataInsightChartResourceIT.java | 5 + .../it/tests/DataProductResourceIT.java | 5 + .../it/tests/DatabaseResourceIT.java | 5 + .../it/tests/DatabaseSchemaResourceIT.java | 5 + .../it/tests/DatabaseServiceResourceIT.java | 5 + .../it/tests/DomainResourceIT.java | 5 + .../it/tests/DriveServiceResourceIT.java | 5 + .../it/tests/EventSubscriptionResourceIT.java | 5 + .../it/tests/GlossaryResourceIT.java | 5 + .../it/tests/GlossaryTermResourceIT.java | 5 + .../it/tests/IngestionPipelineResourceIT.java | 5 + .../openmetadata/it/tests/KpiResourceIT.java | 5 + .../it/tests/LLMModelResourceIT.java | 5 + .../it/tests/LLMServiceResourceIT.java | 5 + .../it/tests/LearningResourceIT.java | 10 + .../it/tests/MessagingServiceResourceIT.java | 5 + .../it/tests/MetadataServiceResourceIT.java | 5 + .../it/tests/MetricResourceIT.java | 5 + .../it/tests/MlModelResourceIT.java | 5 + .../it/tests/MlModelServiceResourceIT.java | 5 + .../tests/NotificationTemplateResourceIT.java | 5 + .../it/tests/PersonaResourceIT.java | 5 + .../it/tests/PipelineResourceIT.java | 5 + .../it/tests/PipelineServiceResourceIT.java | 5 + .../it/tests/PolicyResourceIT.java | 5 + .../it/tests/QueryResourceIT.java | 5 + .../openmetadata/it/tests/RoleResourceIT.java | 5 + .../it/tests/SearchIndexResourceIT.java | 5 + .../it/tests/SearchServiceResourceIT.java | 5 + .../it/tests/StorageServiceResourceIT.java | 5 + .../it/tests/StoredProcedureResourceIT.java | 5 + .../it/tests/TableResourceIT.java | 5 + .../openmetadata/it/tests/TagResourceIT.java | 5 + .../openmetadata/it/tests/TeamResourceIT.java | 5 + .../it/tests/TestCaseResourceIT.java | 5 + .../it/tests/TestDefinitionResourceIT.java | 5 + .../it/tests/TestSuiteResourceIT.java | 5 + .../it/tests/TopicResourceIT.java | 5 + .../openmetadata/it/tests/UserResourceIT.java | 5 + .../it/tests/WorksheetResourceIT.java | 5 + .../sdk/fluent/DatabaseSchemas.java | 5 + .../openmetadata/sdk/fluent/Databases.java | 5 + .../org/openmetadata/sdk/fluent/Domains.java | 5 + .../openmetadata/sdk/fluent/Pipelines.java | 5 + .../org/openmetadata/sdk/fluent/Roles.java | 5 + .../org/openmetadata/sdk/fluent/Tables.java | 5 + .../org/openmetadata/sdk/fluent/Teams.java | 5 + .../org/openmetadata/sdk/fluent/Topics.java | 5 + .../org/openmetadata/sdk/fluent/Users.java | 5 + .../sdk/services/EntityServiceBase.java | 15 ++ .../service/jdbi3/CollectionDAO.java | 5 + .../service/jdbi3/EntityRepository.java | 39 +++- .../service/resources/EntityResource.java | 21 +- .../resources/ai/AIApplicationResource.java | 15 +- .../ai/AIGovernancePolicyResource.java | 15 +- .../resources/ai/LLMModelResource.java | 15 +- .../resources/ai/PromptTemplateResource.java | 15 +- .../analytics/WebAnalyticEventResource.java | 15 +- .../resources/apis/APICollectionResource.java | 15 +- .../resources/apis/APIEndpointResource.java | 15 +- .../apps/AppMarketPlaceResource.java | 15 +- .../service/resources/apps/AppResource.java | 15 +- .../automations/WorkflowResource.java | 15 +- .../service/resources/bots/BotResource.java | 15 +- .../resources/charts/ChartResource.java | 15 +- .../dashboards/DashboardResource.java | 15 +- .../resources/data/DataContractResource.java | 15 +- .../resources/databases/DatabaseResource.java | 15 +- .../databases/DatabaseSchemaResource.java | 15 +- .../databases/StoredProcedureResource.java | 15 +- .../resources/databases/TableResource.java | 15 +- .../datainsight/DataInsightChartResource.java | 15 +- .../DashboardDataModelResource.java | 15 +- .../resources/docstore/DocStoreResource.java | 15 +- .../domains/DataProductResource.java | 15 +- .../resources/domains/DomainResource.java | 15 +- .../resources/dqtests/TestCaseResource.java | 18 +- .../dqtests/TestDefinitionResource.java | 15 +- .../resources/dqtests/TestSuiteResource.java | 15 +- .../resources/drives/DirectoryResource.java | 15 +- .../resources/drives/FileResource.java | 15 +- .../resources/drives/SpreadsheetResource.java | 15 +- .../resources/drives/WorksheetResource.java | 15 +- .../events/NotificationTemplateResource.java | 15 +- .../EventSubscriptionResource.java | 15 +- .../resources/glossary/GlossaryResource.java | 15 +- .../glossary/GlossaryTermResource.java | 15 +- .../WorkflowDefinitionResource.java | 15 +- .../service/resources/kpi/KpiResource.java | 15 +- .../learning/LearningResourceResource.java | 15 +- .../resources/metrics/MetricResource.java | 15 +- .../resources/mlmodels/MlModelResource.java | 15 +- .../resources/pipelines/PipelineResource.java | 15 +- .../resources/policies/PolicyResource.java | 15 +- .../resources/query/QueryResource.java | 15 +- .../searchindex/SearchIndexResource.java | 15 +- .../apiservices/APIServiceResource.java | 15 +- .../dashboard/DashboardServiceResource.java | 15 +- .../database/DatabaseServiceResource.java | 15 +- .../services/drive/DriveServiceResource.java | 15 +- .../IngestionPipelineResource.java | 15 +- .../services/llm/LLMServiceResource.java | 15 +- .../messaging/MessagingServiceResource.java | 15 +- .../metadata/MetadataServiceResource.java | 15 +- .../mlmodel/MlModelServiceResource.java | 15 +- .../pipeline/PipelineServiceResource.java | 15 +- .../searchIndexes/SearchServiceResource.java | 15 +- .../security/SecurityServiceResource.java | 15 +- .../storage/StorageServiceResource.java | 15 +- .../resources/storages/ContainerResource.java | 15 +- .../tags/ClassificationResource.java | 15 +- .../service/resources/tags/TagResource.java | 15 +- .../resources/teams/PersonaResource.java | 15 +- .../service/resources/teams/RoleResource.java | 15 +- .../service/resources/teams/TeamResource.java | 15 +- .../service/resources/teams/UserResource.java | 15 +- .../resources/topics/TopicResource.java | 15 +- .../service/resources/types/TypeResource.java | 15 +- .../json/schema/type/entityHistory.json | 3 + .../PaginatedVersionHistory.spec.ts | 132 ++++++++++++ .../APIEndpointVersion.interface.ts | 3 + .../APIEndpointVersion/APIEndpointVersion.tsx | 12 +- .../ChartVersion/ChartVersion.component.tsx | 18 +- .../ContainerVersion.component.tsx | 12 +- .../ContainerVersion.interface.ts | 3 + .../DashboardVersion.component.tsx | 12 +- .../DashboardVersion.interface.ts | 3 + .../DataModelVersion.component.tsx | 12 +- .../DataModelVersion.interface.ts | 3 + .../StoredProcedureVersion.component.tsx | 12 +- .../StoredProcedureVersion.interface.ts | 3 + .../TableVersion/TableVersion.component.tsx | 12 +- .../TableVersion/TableVersion.interface.ts | 3 + .../DirectoryVersion.interface.ts | 3 + .../DirectoryVersion/DirectoryVersion.tsx | 12 +- .../File/FileVersion/FileVersion.interface.ts | 3 + .../File/FileVersion/FileVersion.tsx | 12 +- .../SpreadsheetVersion.interface.ts | 3 + .../SpreadsheetVersion/SpreadsheetVersion.tsx | 12 +- .../WorksheetVersion.interface.ts | 3 + .../WorksheetVersion/WorksheetVersion.tsx | 12 +- .../EntityVersionTimeLine.tsx | 46 +++- .../EntityVersionTimeline.interface.ts | 3 + .../MetricVersion/MetricVersion.interface.ts | 3 + .../Metric/MetricVersion/MetricVersion.tsx | 12 +- .../MlModelVersion.component.tsx | 15 +- .../MlModelVersion.interface.tsx | 3 + .../PipelineVersion.component.tsx | 12 +- .../PipelineVersion.interface.ts | 3 + .../SearchIndexVersion.interface.ts | 3 + .../SearchIndexVersion/SearchIndexVersion.tsx | 12 +- .../TopicVersion/TopicVersion.component.tsx | 12 +- .../TopicVersion/TopicVersion.interface.ts | 3 + .../ui/src/generated/type/entityHistory.ts | 12 ++ .../EntityVersionPage.component.tsx | 197 +++++++++++++++--- .../resources/ui/src/rest/SearchIndexAPI.ts | 7 +- .../ui/src/rest/apiCollectionsAPI.ts | 7 +- .../resources/ui/src/rest/apiEndpointsAPI.ts | 7 +- .../main/resources/ui/src/rest/chartsAPI.ts | 7 +- .../resources/ui/src/rest/dashboardAPI.ts | 7 +- .../resources/ui/src/rest/dataModelsAPI.ts | 7 +- .../resources/ui/src/rest/dataProductAPI.ts | 7 +- .../main/resources/ui/src/rest/databaseAPI.ts | 14 +- .../main/resources/ui/src/rest/domainAPI.ts | 7 +- .../main/resources/ui/src/rest/driveAPI.ts | 5 +- .../main/resources/ui/src/rest/glossaryAPI.ts | 14 +- .../main/resources/ui/src/rest/metricsAPI.ts | 8 +- .../main/resources/ui/src/rest/mlModelAPI.ts | 7 +- .../main/resources/ui/src/rest/pipelineAPI.ts | 7 +- .../main/resources/ui/src/rest/serviceAPI.ts | 5 +- .../main/resources/ui/src/rest/storageAPI.ts | 7 +- .../ui/src/rest/storedProceduresAPI.ts | 7 +- .../main/resources/ui/src/rest/tableAPI.ts | 7 +- .../src/main/resources/ui/src/rest/tagAPI.ts | 7 +- .../src/main/resources/ui/src/rest/testAPI.ts | 2 +- .../main/resources/ui/src/rest/topicsAPI.ts | 7 +- 190 files changed, 2003 insertions(+), 265 deletions(-) create mode 100644 openmetadata-ui/src/main/resources/ui/playwright/e2e/VersionPages/PaginatedVersionHistory.spec.ts diff --git a/ingestion/src/metadata/ingestion/ometa/mixins/version_mixin.py b/ingestion/src/metadata/ingestion/ometa/mixins/version_mixin.py index e6ef9fd628fb..255a296827bf 100644 --- a/ingestion/src/metadata/ingestion/ometa/mixins/version_mixin.py +++ b/ingestion/src/metadata/ingestion/ometa/mixins/version_mixin.py @@ -89,6 +89,8 @@ def get_list_entity_versions( self, entity_id: Union[str, basic.Uuid], entity: Type[T], + limit: Optional[int] = None, + offset: Optional[int] = None, ) -> Union[Response, EntityVersionHistory]: """ Retrieve the list of versions for a specific entity @@ -99,6 +101,10 @@ def get_list_entity_versions( the entity type entity_id: Union[str, basic.Uuid] the ID for a specific entity + limit: Optional[int] + maximum number of versions to return + offset: Optional[int] + offset for pagination Returns ------- @@ -107,7 +113,15 @@ def get_list_entity_versions( """ path = f"{model_str(entity_id)}/versions" - resp = self.client.get(f"{self.get_suffix(entity)}/{path}") + params = {} + if limit is not None: + params["limit"] = limit + if offset is not None: + params["offset"] = offset + + resp = self.client.get( + f"{self.get_suffix(entity)}/{path}", data=params if params else None + ) if self._use_raw_data: return resp diff --git a/ingestion/src/metadata/sdk/entities/base.py b/ingestion/src/metadata/sdk/entities/base.py index d17bc47af37f..43552f342ec5 100644 --- a/ingestion/src/metadata/sdk/entities/base.py +++ b/ingestion/src/metadata/sdk/entities/base.py @@ -318,17 +318,27 @@ def import_csv(cls, name: str) -> CsvImportOperation[TEntity]: return CsvImportOperation(client=client, entity=cls.entity_type(), name=name) @classmethod - def get_versions(cls, entity_id: UuidLike) -> Sequence[TEntity]: + def get_versions( + cls, + entity_id: UuidLike, + limit: Optional[int] = None, + offset: Optional[int] = None, + ) -> Sequence[TEntity]: """Fetch all historical versions for an entity.""" client = cls._get_client() list_versions = cast( Callable[..., Any], getattr(client, "get_list_entity_versions") ) - history = list_versions( - entity=cls.entity_type(), - entity_id=cls._stringify_identifier(entity_id), - ) + kwargs: Dict[str, Any] = { + "entity": cls.entity_type(), + "entity_id": cls._stringify_identifier(entity_id), + } + if limit is not None: + kwargs["limit"] = limit + if offset is not None: + kwargs["offset"] = offset + history = list_versions(**kwargs) versions = cast(Sequence[Any], getattr(history, "versions", []) or []) return [cls._coerce_entity(item) for item in versions] diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/APICollectionResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/APICollectionResourceIT.java index 3ade23e04beb..81042979efd8 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/APICollectionResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/APICollectionResourceIT.java @@ -154,6 +154,11 @@ protected EntityHistory getVersionHistory(UUID id) { return SdkClients.adminClient().apiCollections().getVersionList(id); } + @Override + protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offset) { + return SdkClients.adminClient().apiCollections().getVersionList(id, limit, offset); + } + @Override protected APICollection getVersion(UUID id, Double version) { return SdkClients.adminClient().apiCollections().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/APIEndpointResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/APIEndpointResourceIT.java index b0cfd1cb0544..0c745cc218e6 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/APIEndpointResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/APIEndpointResourceIT.java @@ -185,6 +185,11 @@ protected EntityHistory getVersionHistory(UUID id) { return SdkClients.adminClient().apiEndpoints().getVersionList(id); } + @Override + protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offset) { + return SdkClients.adminClient().apiEndpoints().getVersionList(id, limit, offset); + } + @Override protected APIEndpoint getVersion(UUID id, Double version) { return SdkClients.adminClient().apiEndpoints().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/APIServiceResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/APIServiceResourceIT.java index 91c57c32623b..7fd09c8101f4 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/APIServiceResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/APIServiceResourceIT.java @@ -142,6 +142,11 @@ protected EntityHistory getVersionHistory(UUID id) { return SdkClients.adminClient().apiServices().getVersionList(id); } + @Override + protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offset) { + return SdkClients.adminClient().apiServices().getVersionList(id, limit, offset); + } + @Override protected ApiService getVersion(UUID id, Double version) { return SdkClients.adminClient().apiServices().getVersion(id.toString(), version); 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 4a8445583582..b69b081dd6b3 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 @@ -42,6 +42,7 @@ import org.openmetadata.sdk.exceptions.InvalidRequestException; import org.openmetadata.sdk.fluent.Users; import org.openmetadata.sdk.network.HttpMethod; +import org.openmetadata.sdk.network.RequestOptions; import org.openmetadata.service.util.TestUtils; /** @@ -1350,6 +1351,76 @@ void get_entityVersionHistory_200(TestNamespace ns) { assertTrue(history.getVersions().size() >= 2, "Should have at least 2 versions"); } + @Test + void get_entityVersionHistory_paginated_200(TestNamespace ns) { + if (!supportsPatch) return; + + K createRequest = createMinimalRequest(ns); + T created = createEntity(createRequest); + + String entityId = created.getId().toString(); + String patchPath = getResourcePath() + entityId; + RequestOptions patchOptions = + RequestOptions.builder().header("Content-Type", "application/json-patch+json").build(); + + OpenMetadataClient admin = SdkClients.adminClient(); + OpenMetadataClient user1 = SdkClients.user1Client(); + + admin + .getHttpClient() + .executeForString( + HttpMethod.PATCH, + patchPath, + "[{\"op\":\"add\",\"path\":\"/description\",\"value\":\"Admin patch 1\"}]", + patchOptions); + + try { + user1 + .getHttpClient() + .executeForString( + HttpMethod.PATCH, + patchPath, + "[{\"op\":\"replace\",\"path\":\"/description\",\"value\":\"User1 patch 2\"}]", + patchOptions); + } catch (Exception e) { + Assumptions.assumeTrue( + false, "Skipping: user1 cannot patch this entity type - " + e.getMessage()); + } + + admin + .getHttpClient() + .executeForString( + HttpMethod.PATCH, + patchPath, + "[{\"op\":\"replace\",\"path\":\"/description\",\"value\":\"Admin patch 3\"}]", + patchOptions); + + org.openmetadata.schema.type.EntityHistory allVersions = getVersionHistory(created.getId()); + int totalVersions = allVersions.getVersions().size(); + assertTrue( + totalVersions >= 3, "Should have at least 3 versions after alternating-user patches"); + + org.openmetadata.schema.type.EntityHistory page1 = + getVersionHistoryPaginated(created.getId(), 1, 0); + assertNotNull(page1.getPaging(), "Paging metadata should be present"); + assertEquals(1, (int) page1.getPaging().getLimit()); + assertEquals(0, (int) page1.getPaging().getOffset()); + assertEquals(totalVersions, (int) page1.getPaging().getTotal()); + assertEquals(1, page1.getVersions().size()); + + org.openmetadata.schema.type.EntityHistory page2 = + getVersionHistoryPaginated(created.getId(), 1, 1); + assertNotNull(page2.getPaging()); + assertEquals(1, (int) page2.getPaging().getLimit()); + assertEquals(1, (int) page2.getPaging().getOffset()); + assertEquals(totalVersions, (int) page2.getPaging().getTotal()); + assertEquals(1, page2.getVersions().size()); + + org.openmetadata.schema.type.EntityHistory unpaginated = getVersionHistory(created.getId()); + assertNotNull(unpaginated.getVersions()); + assertEquals(totalVersions, unpaginated.getVersions().size()); + } + @Test void get_specificVersion_200(TestNamespace ns) { if (!supportsPatch) return; // Specific version tests require patch support @@ -1374,6 +1445,12 @@ protected org.openmetadata.schema.type.EntityHistory getVersionHistory(UUID id) "Version history not implemented - override in subclass"); } + protected org.openmetadata.schema.type.EntityHistory getVersionHistoryPaginated( + UUID id, int limit, int offset) { + throw new UnsupportedOperationException( + "Paginated version history not implemented - override in subclass"); + } + protected T getVersion(UUID id, Double version) { throw new UnsupportedOperationException("Get version not implemented - override in subclass"); } diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/BotResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/BotResourceIT.java index e84986c45b87..895caf553864 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/BotResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/BotResourceIT.java @@ -168,6 +168,11 @@ protected EntityHistory getVersionHistory(UUID id) { return SdkClients.adminClient().bots().getVersionList(id); } + @Override + protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offset) { + return SdkClients.adminClient().bots().getVersionList(id, limit, offset); + } + @Override protected Bot getVersion(UUID id, Double version) { return SdkClients.adminClient().bots().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/ChartResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/ChartResourceIT.java index 3eed40e3777b..65d9e5d71b08 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/ChartResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/ChartResourceIT.java @@ -156,6 +156,11 @@ protected EntityHistory getVersionHistory(UUID id) { return SdkClients.adminClient().charts().getVersionList(id); } + @Override + protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offset) { + return SdkClients.adminClient().charts().getVersionList(id, limit, offset); + } + @Override protected Chart getVersion(UUID id, Double version) { return SdkClients.adminClient().charts().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/ClassificationResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/ClassificationResourceIT.java index 4427ca3c0fae..a2b2bf201b48 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/ClassificationResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/ClassificationResourceIT.java @@ -152,6 +152,11 @@ protected EntityHistory getVersionHistory(UUID id) { return SdkClients.adminClient().classifications().getVersionList(id); } + @Override + protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offset) { + return SdkClients.adminClient().classifications().getVersionList(id, limit, offset); + } + @Override protected Classification getVersion(UUID id, Double version) { return SdkClients.adminClient().classifications().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/ContainerResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/ContainerResourceIT.java index 6b53b13386ca..8d2ba161b45a 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/ContainerResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/ContainerResourceIT.java @@ -155,6 +155,11 @@ protected EntityHistory getVersionHistory(UUID id) { return SdkClients.adminClient().containers().getVersionList(id); } + @Override + protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offset) { + return SdkClients.adminClient().containers().getVersionList(id, limit, offset); + } + @Override protected Container getVersion(UUID id, Double version) { return SdkClients.adminClient().containers().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DashboardDataModelResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DashboardDataModelResourceIT.java index e30bbdb54da2..d566ab38deba 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DashboardDataModelResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DashboardDataModelResourceIT.java @@ -187,6 +187,11 @@ protected EntityHistory getVersionHistory(UUID id) { return SdkClients.adminClient().dashboardDataModels().getVersionList(id); } + @Override + protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offset) { + return SdkClients.adminClient().dashboardDataModels().getVersionList(id, limit, offset); + } + @Override protected DashboardDataModel getVersion(UUID id, Double version) { return SdkClients.adminClient().dashboardDataModels().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DashboardResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DashboardResourceIT.java index 5035a0153a89..6d696b02cb21 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DashboardResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DashboardResourceIT.java @@ -157,6 +157,11 @@ protected EntityHistory getVersionHistory(UUID id) { return SdkClients.adminClient().dashboards().getVersionList(id); } + @Override + protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offset) { + return SdkClients.adminClient().dashboards().getVersionList(id, limit, offset); + } + @Override protected Dashboard getVersion(UUID id, Double version) { return SdkClients.adminClient().dashboards().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DashboardServiceResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DashboardServiceResourceIT.java index cc6c150583dd..1c1c097202d1 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DashboardServiceResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DashboardServiceResourceIT.java @@ -143,6 +143,11 @@ protected EntityHistory getVersionHistory(UUID id) { return SdkClients.adminClient().dashboardServices().getVersionList(id); } + @Override + protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offset) { + return SdkClients.adminClient().dashboardServices().getVersionList(id, limit, offset); + } + @Override protected DashboardService getVersion(UUID id, Double version) { return SdkClients.adminClient().dashboardServices().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DataContractResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DataContractResourceIT.java index a5fa74bb37d5..3203219e7a1b 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DataContractResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DataContractResourceIT.java @@ -228,6 +228,11 @@ protected EntityHistory getVersionHistory(UUID id) { return SdkClients.adminClient().dataContracts().getVersionList(id); } + @Override + protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offset) { + return SdkClients.adminClient().dataContracts().getVersionList(id, limit, offset); + } + @Override protected DataContract getVersion(UUID id, Double version) { return SdkClients.adminClient().dataContracts().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DataInsightChartResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DataInsightChartResourceIT.java index 321d15a5b875..14a9fbe10d4f 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DataInsightChartResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DataInsightChartResourceIT.java @@ -154,6 +154,11 @@ protected EntityHistory getVersionHistory(UUID id) { return getDataInsightChartService().getVersionList(id); } + @Override + protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offset) { + return getDataInsightChartService().getVersionList(id, limit, offset); + } + @Override protected DataInsightChart getVersion(UUID id, Double version) { return getDataInsightChartService().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DataProductResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DataProductResourceIT.java index 53ce0987f921..8c4c5ebbb97c 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DataProductResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DataProductResourceIT.java @@ -201,6 +201,11 @@ protected EntityHistory getVersionHistory(UUID id) { return SdkClients.adminClient().dataProducts().getVersionList(id); } + @Override + protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offset) { + return SdkClients.adminClient().dataProducts().getVersionList(id, limit, offset); + } + @Override protected DataProduct getVersion(UUID id, Double version) { return SdkClients.adminClient().dataProducts().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DatabaseResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DatabaseResourceIT.java index 3d06716a3acc..3439c8a01ca0 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DatabaseResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DatabaseResourceIT.java @@ -1349,6 +1349,11 @@ protected EntityHistory getVersionHistory(UUID id) { return SdkClients.adminClient().databases().getVersionList(id); } + @Override + protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offset) { + return SdkClients.adminClient().databases().getVersionList(id, limit, offset); + } + @Override protected Database getVersion(UUID id, Double version) { return SdkClients.adminClient().databases().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DatabaseSchemaResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DatabaseSchemaResourceIT.java index f98e75c85789..777475e36e95 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DatabaseSchemaResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DatabaseSchemaResourceIT.java @@ -170,6 +170,11 @@ protected EntityHistory getVersionHistory(UUID id) { return SdkClients.adminClient().databaseSchemas().getVersionList(id); } + @Override + protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offset) { + return SdkClients.adminClient().databaseSchemas().getVersionList(id, limit, offset); + } + @Override protected DatabaseSchema getVersion(UUID id, Double version) { return SdkClients.adminClient().databaseSchemas().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DatabaseServiceResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DatabaseServiceResourceIT.java index df2fbbd2c35d..32f4189e12fb 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DatabaseServiceResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DatabaseServiceResourceIT.java @@ -166,6 +166,11 @@ protected EntityHistory getVersionHistory(UUID id) { return SdkClients.adminClient().databaseServices().getVersionList(id); } + @Override + protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offset) { + return SdkClients.adminClient().databaseServices().getVersionList(id, limit, offset); + } + @Override protected DatabaseService getVersion(UUID id, Double version) { return SdkClients.adminClient().databaseServices().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DomainResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DomainResourceIT.java index c36b0bcbd661..a8ca443f045d 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DomainResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DomainResourceIT.java @@ -430,6 +430,11 @@ protected EntityHistory getVersionHistory(UUID id) { return SdkClients.adminClient().domains().getVersionList(id); } + @Override + protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offset) { + return SdkClients.adminClient().domains().getVersionList(id, limit, offset); + } + @Override protected Domain getVersion(UUID id, Double version) { return SdkClients.adminClient().domains().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DriveServiceResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DriveServiceResourceIT.java index bd2a7e29859b..f40168fb8b1a 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DriveServiceResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DriveServiceResourceIT.java @@ -156,6 +156,11 @@ protected EntityHistory getVersionHistory(UUID id) { return SdkClients.adminClient().driveServices().getVersionList(id); } + @Override + protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offset) { + return SdkClients.adminClient().driveServices().getVersionList(id, limit, offset); + } + @Override protected DriveService getVersion(UUID id, Double version) { return SdkClients.adminClient().driveServices().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/EventSubscriptionResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/EventSubscriptionResourceIT.java index dbe6c856d4e9..edd87bad2685 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/EventSubscriptionResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/EventSubscriptionResourceIT.java @@ -183,6 +183,11 @@ protected EntityHistory getVersionHistory(UUID id) { return SdkClients.adminClient().eventSubscriptions().getVersionList(id); } + @Override + protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offset) { + return SdkClients.adminClient().eventSubscriptions().getVersionList(id, limit, offset); + } + @Override protected EventSubscription getVersion(UUID id, Double version) { return SdkClients.adminClient().eventSubscriptions().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/GlossaryResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/GlossaryResourceIT.java index 61a31f482257..08708b7bdd62 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/GlossaryResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/GlossaryResourceIT.java @@ -1028,6 +1028,11 @@ protected EntityHistory getVersionHistory(UUID id) { return SdkClients.adminClient().glossaries().getVersionList(id); } + @Override + protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offset) { + return SdkClients.adminClient().glossaries().getVersionList(id, limit, offset); + } + @Override protected Glossary getVersion(UUID id, Double version) { return SdkClients.adminClient().glossaries().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/GlossaryTermResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/GlossaryTermResourceIT.java index 595477282757..dc8171f8366d 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/GlossaryTermResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/GlossaryTermResourceIT.java @@ -170,6 +170,11 @@ protected EntityHistory getVersionHistory(UUID id) { return SdkClients.adminClient().glossaryTerms().getVersionList(id); } + @Override + protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offset) { + return SdkClients.adminClient().glossaryTerms().getVersionList(id, limit, offset); + } + @Override protected GlossaryTerm getVersion(UUID id, Double version) { return SdkClients.adminClient().glossaryTerms().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/IngestionPipelineResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/IngestionPipelineResourceIT.java index c6b01a522f7d..1a4af12ac26a 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/IngestionPipelineResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/IngestionPipelineResourceIT.java @@ -223,6 +223,11 @@ protected EntityHistory getVersionHistory(UUID id) { return SdkClients.adminClient().ingestionPipelines().getVersionList(id); } + @Override + protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offset) { + return SdkClients.adminClient().ingestionPipelines().getVersionList(id, limit, offset); + } + @Override protected IngestionPipeline getVersion(UUID id, Double version) { return SdkClients.adminClient().ingestionPipelines().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/KpiResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/KpiResourceIT.java index 0930ad427a2f..9ee527a30e05 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/KpiResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/KpiResourceIT.java @@ -167,6 +167,11 @@ protected EntityHistory getVersionHistory(UUID id) { return getKpiService().getVersionList(id); } + @Override + protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offset) { + return getKpiService().getVersionList(id, limit, offset); + } + @Override protected Kpi getVersion(UUID id, Double version) { return getKpiService().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/LLMModelResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/LLMModelResourceIT.java index a4983858f3e2..041b974d1e4f 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/LLMModelResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/LLMModelResourceIT.java @@ -145,6 +145,11 @@ protected EntityHistory getVersionHistory(UUID id) { return SdkClients.adminClient().llmModels().getVersionList(id); } + @Override + protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offset) { + return SdkClients.adminClient().llmModels().getVersionList(id, limit, offset); + } + @Override protected LLMModel getVersion(UUID id, Double version) { return SdkClients.adminClient().llmModels().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/LLMServiceResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/LLMServiceResourceIT.java index 09d8c523c26c..3382ebb6cb4b 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/LLMServiceResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/LLMServiceResourceIT.java @@ -134,6 +134,11 @@ protected EntityHistory getVersionHistory(UUID id) { return SdkClients.adminClient().llmServices().getVersionList(id); } + @Override + protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offset) { + return SdkClients.adminClient().llmServices().getVersionList(id, limit, offset); + } + @Override protected LLMService getVersion(UUID id, Double version) { return SdkClients.adminClient().llmServices().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/LearningResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/LearningResourceIT.java index de34ffa41bef..3cd1af36d8c3 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/LearningResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/LearningResourceIT.java @@ -118,6 +118,11 @@ protected String getEntityType() { return "learningResource"; } + @Override + protected String getResourcePath() { + return "/v1/learning/resources/"; + } + @Override protected void validateCreatedEntity( LearningResource entity, CreateLearningResource createRequest) { @@ -167,6 +172,11 @@ protected EntityHistory getVersionHistory(UUID id) { return getLearningResourceService().getVersionList(id); } + @Override + protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offset) { + return getLearningResourceService().getVersionList(id, limit, offset); + } + @Override protected LearningResource getVersion(UUID id, Double version) { return getLearningResourceService().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/MessagingServiceResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/MessagingServiceResourceIT.java index c2d8d2ba5304..a509045f2013 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/MessagingServiceResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/MessagingServiceResourceIT.java @@ -139,6 +139,11 @@ protected EntityHistory getVersionHistory(UUID id) { return SdkClients.adminClient().messagingServices().getVersionList(id); } + @Override + protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offset) { + return SdkClients.adminClient().messagingServices().getVersionList(id, limit, offset); + } + @Override protected MessagingService getVersion(UUID id, Double version) { return SdkClients.adminClient().messagingServices().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/MetadataServiceResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/MetadataServiceResourceIT.java index 82d89ff30911..8ca252e30d8a 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/MetadataServiceResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/MetadataServiceResourceIT.java @@ -149,6 +149,11 @@ protected EntityHistory getVersionHistory(UUID id) { return SdkClients.adminClient().metadataServices().getVersionList(id); } + @Override + protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offset) { + return SdkClients.adminClient().metadataServices().getVersionList(id, limit, offset); + } + @Override protected MetadataService getVersion(UUID id, Double version) { return SdkClients.adminClient().metadataServices().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/MetricResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/MetricResourceIT.java index 0f098737f648..abea445768ae 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/MetricResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/MetricResourceIT.java @@ -141,6 +141,11 @@ protected EntityHistory getVersionHistory(UUID id) { return SdkClients.adminClient().metrics().getVersionList(id); } + @Override + protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offset) { + return SdkClients.adminClient().metrics().getVersionList(id, limit, offset); + } + @Override protected Metric getVersion(UUID id, Double version) { return SdkClients.adminClient().metrics().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/MlModelResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/MlModelResourceIT.java index 47ca49acf919..ca3654da0539 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/MlModelResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/MlModelResourceIT.java @@ -158,6 +158,11 @@ protected EntityHistory getVersionHistory(UUID id) { return SdkClients.adminClient().mlModels().getVersionList(id); } + @Override + protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offset) { + return SdkClients.adminClient().mlModels().getVersionList(id, limit, offset); + } + @Override protected MlModel getVersion(UUID id, Double version) { return SdkClients.adminClient().mlModels().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/MlModelServiceResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/MlModelServiceResourceIT.java index 6eea0aef2931..2cd3d3ddf2b9 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/MlModelServiceResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/MlModelServiceResourceIT.java @@ -144,6 +144,11 @@ protected EntityHistory getVersionHistory(UUID id) { return SdkClients.adminClient().mlModelServices().getVersionList(id); } + @Override + protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offset) { + return SdkClients.adminClient().mlModelServices().getVersionList(id, limit, offset); + } + @Override protected MlModelService getVersion(UUID id, Double version) { return SdkClients.adminClient().mlModelServices().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/NotificationTemplateResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/NotificationTemplateResourceIT.java index 8c336d36ab32..544939e05b4f 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/NotificationTemplateResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/NotificationTemplateResourceIT.java @@ -843,6 +843,11 @@ protected EntityHistory getVersionHistory(UUID id) { return SdkClients.adminClient().notificationTemplates().getVersionList(id); } + @Override + protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offset) { + return SdkClients.adminClient().notificationTemplates().getVersionList(id, limit, offset); + } + @Override protected NotificationTemplate getVersion(UUID id, Double version) { return SdkClients.adminClient().notificationTemplates().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/PersonaResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/PersonaResourceIT.java index 9db5836d6a2b..3662ca78e42d 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/PersonaResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/PersonaResourceIT.java @@ -137,6 +137,11 @@ protected EntityHistory getVersionHistory(UUID id) { return SdkClients.adminClient().personas().getVersionList(id); } + @Override + protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offset) { + return SdkClients.adminClient().personas().getVersionList(id, limit, offset); + } + @Override protected Persona getVersion(UUID id, Double version) { return SdkClients.adminClient().personas().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/PipelineResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/PipelineResourceIT.java index e1469e15e8ce..216d9289534e 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/PipelineResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/PipelineResourceIT.java @@ -164,6 +164,11 @@ protected EntityHistory getVersionHistory(UUID id) { return SdkClients.adminClient().pipelines().getVersionList(id); } + @Override + protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offset) { + return SdkClients.adminClient().pipelines().getVersionList(id, limit, offset); + } + @Override protected Pipeline getVersion(UUID id, Double version) { return SdkClients.adminClient().pipelines().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/PipelineServiceResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/PipelineServiceResourceIT.java index 393577788eca..8969a31289a5 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/PipelineServiceResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/PipelineServiceResourceIT.java @@ -137,6 +137,11 @@ protected EntityHistory getVersionHistory(UUID id) { return SdkClients.adminClient().pipelineServices().getVersionList(id); } + @Override + protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offset) { + return SdkClients.adminClient().pipelineServices().getVersionList(id, limit, offset); + } + @Override protected PipelineService getVersion(UUID id, Double version) { return SdkClients.adminClient().pipelineServices().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/PolicyResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/PolicyResourceIT.java index d676a1e4418b..293b4ce920c2 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/PolicyResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/PolicyResourceIT.java @@ -493,6 +493,11 @@ protected EntityHistory getVersionHistory(UUID id) { return SdkClients.adminClient().policies().getVersionList(id); } + @Override + protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offset) { + return SdkClients.adminClient().policies().getVersionList(id, limit, offset); + } + @Override protected Policy getVersion(UUID id, Double version) { return SdkClients.adminClient().policies().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/QueryResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/QueryResourceIT.java index adbd8e91f363..a544af9f6e5d 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/QueryResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/QueryResourceIT.java @@ -254,6 +254,11 @@ protected EntityHistory getVersionHistory(UUID id) { return SdkClients.adminClient().queries().getVersionList(id); } + @Override + protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offset) { + return SdkClients.adminClient().queries().getVersionList(id, limit, offset); + } + @Override protected Query getVersion(UUID id, Double version) { return SdkClients.adminClient().queries().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/RoleResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/RoleResourceIT.java index 037407c1fe03..2b52833162c7 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/RoleResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/RoleResourceIT.java @@ -379,6 +379,11 @@ protected EntityHistory getVersionHistory(UUID id) { return SdkClients.adminClient().roles().getVersionList(id); } + @Override + protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offset) { + return SdkClients.adminClient().roles().getVersionList(id, limit, offset); + } + @Override protected Role getVersion(UUID id, Double version) { return SdkClients.adminClient().roles().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/SearchIndexResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/SearchIndexResourceIT.java index a5e804897eac..0e6f9d5a9ed7 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/SearchIndexResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/SearchIndexResourceIT.java @@ -183,6 +183,11 @@ protected EntityHistory getVersionHistory(UUID id) { return SdkClients.adminClient().searchIndexes().getVersionList(id); } + @Override + protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offset) { + return SdkClients.adminClient().searchIndexes().getVersionList(id, limit, offset); + } + @Override protected SearchIndex getVersion(UUID id, Double version) { return SdkClients.adminClient().searchIndexes().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/SearchServiceResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/SearchServiceResourceIT.java index fb9433577278..52adaa1f3612 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/SearchServiceResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/SearchServiceResourceIT.java @@ -134,6 +134,11 @@ protected EntityHistory getVersionHistory(UUID id) { return SdkClients.adminClient().searchServices().getVersionList(id); } + @Override + protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offset) { + return SdkClients.adminClient().searchServices().getVersionList(id, limit, offset); + } + @Override protected SearchService getVersion(UUID id, Double version) { return SdkClients.adminClient().searchServices().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/StorageServiceResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/StorageServiceResourceIT.java index dfddf7050646..8f6c07700930 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/StorageServiceResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/StorageServiceResourceIT.java @@ -130,6 +130,11 @@ protected EntityHistory getVersionHistory(UUID id) { return SdkClients.adminClient().storageServices().getVersionList(id); } + @Override + protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offset) { + return SdkClients.adminClient().storageServices().getVersionList(id, limit, offset); + } + @Override protected StorageService getVersion(UUID id, Double version) { return SdkClients.adminClient().storageServices().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/StoredProcedureResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/StoredProcedureResourceIT.java index a3fbe92aa85c..6a6b4f110083 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/StoredProcedureResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/StoredProcedureResourceIT.java @@ -172,6 +172,11 @@ protected EntityHistory getVersionHistory(UUID id) { return SdkClients.adminClient().storedProcedures().getVersionList(id); } + @Override + protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offset) { + return SdkClients.adminClient().storedProcedures().getVersionList(id, limit, offset); + } + @Override protected StoredProcedure getVersion(UUID id, Double version) { return SdkClients.adminClient().storedProcedures().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TableResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TableResourceIT.java index b57946b95cb0..9c8f129b2771 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TableResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TableResourceIT.java @@ -4906,6 +4906,11 @@ protected EntityHistory getVersionHistory(UUID id) { return SdkClients.adminClient().tables().getVersionList(id); } + @Override + protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offset) { + return SdkClients.adminClient().tables().getVersionList(id, limit, offset); + } + @Override protected Table getVersion(UUID id, Double version) { return SdkClients.adminClient().tables().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TagResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TagResourceIT.java index 7093d3f44362..354f7358d4e4 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TagResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TagResourceIT.java @@ -173,6 +173,11 @@ protected EntityHistory getVersionHistory(UUID id) { return SdkClients.adminClient().tags().getVersionList(id); } + @Override + protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offset) { + return SdkClients.adminClient().tags().getVersionList(id, limit, offset); + } + @Override protected Tag getVersion(UUID id, Double version) { return SdkClients.adminClient().tags().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TeamResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TeamResourceIT.java index 5d6bf4efccd3..1c07e3e53ee6 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TeamResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TeamResourceIT.java @@ -1051,6 +1051,11 @@ protected EntityHistory getVersionHistory(UUID id) { return SdkClients.adminClient().teams().getVersionList(id); } + @Override + protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offset) { + return SdkClients.adminClient().teams().getVersionList(id, limit, offset); + } + @Override protected Team getVersion(UUID id, Double version) { return SdkClients.adminClient().teams().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TestCaseResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TestCaseResourceIT.java index 1e130fa5cf0f..e755073d6950 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TestCaseResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TestCaseResourceIT.java @@ -270,6 +270,11 @@ protected EntityHistory getVersionHistory(java.util.UUID id) { return SdkClients.adminClient().testCases().getVersionList(id); } + @Override + protected EntityHistory getVersionHistoryPaginated(java.util.UUID id, int limit, int offset) { + return SdkClients.adminClient().testCases().getVersionList(id, limit, offset); + } + @Override protected TestCase getVersion(java.util.UUID id, Double version) { return SdkClients.adminClient().testCases().getVersion(id, version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TestDefinitionResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TestDefinitionResourceIT.java index e65a45312c9d..64cda661f912 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TestDefinitionResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TestDefinitionResourceIT.java @@ -155,6 +155,11 @@ protected EntityHistory getVersionHistory(UUID id) { return SdkClients.adminClient().testDefinitions().getVersionList(id); } + @Override + protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offset) { + return SdkClients.adminClient().testDefinitions().getVersionList(id, limit, offset); + } + @Override protected TestDefinition getVersion(UUID id, Double version) { return SdkClients.adminClient().testDefinitions().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TestSuiteResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TestSuiteResourceIT.java index 26eaa18bfaae..8a392a79abd0 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TestSuiteResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TestSuiteResourceIT.java @@ -178,6 +178,11 @@ protected EntityHistory getVersionHistory(UUID id) { return SdkClients.adminClient().testSuites().getVersionList(id); } + @Override + protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offset) { + return SdkClients.adminClient().testSuites().getVersionList(id, limit, offset); + } + @Override protected TestSuite getVersion(UUID id, Double version) { return SdkClients.adminClient().testSuites().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TopicResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TopicResourceIT.java index 4a1c58802e43..f8ebccfe1e9e 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TopicResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TopicResourceIT.java @@ -157,6 +157,11 @@ protected EntityHistory getVersionHistory(UUID id) { return SdkClients.adminClient().topics().getVersionList(id); } + @Override + protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offset) { + return SdkClients.adminClient().topics().getVersionList(id, limit, offset); + } + @Override protected Topic getVersion(UUID id, Double version) { return SdkClients.adminClient().topics().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/UserResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/UserResourceIT.java index 30bae1076ab5..459548bc2c94 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/UserResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/UserResourceIT.java @@ -2297,6 +2297,11 @@ protected EntityHistory getVersionHistory(UUID id) { return Users.getVersionList(id); } + @Override + protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offset) { + return Users.getVersionList(id, limit, offset); + } + @Override protected User getVersion(UUID id, Double version) { return Users.getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/WorksheetResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/WorksheetResourceIT.java index ab91015115a7..acab1d908554 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/WorksheetResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/WorksheetResourceIT.java @@ -170,6 +170,11 @@ protected EntityHistory getVersionHistory(UUID id) { return getWorksheetService().getVersionList(id); } + @Override + protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offset) { + return getWorksheetService().getVersionList(id, limit, offset); + } + @Override protected Worksheet getVersion(UUID id, Double version) { return getWorksheetService().getVersion(id.toString(), version); diff --git a/openmetadata-sdk/src/main/java/org/openmetadata/sdk/fluent/DatabaseSchemas.java b/openmetadata-sdk/src/main/java/org/openmetadata/sdk/fluent/DatabaseSchemas.java index bb8e03771e59..3af03f4777b7 100644 --- a/openmetadata-sdk/src/main/java/org/openmetadata/sdk/fluent/DatabaseSchemas.java +++ b/openmetadata-sdk/src/main/java/org/openmetadata/sdk/fluent/DatabaseSchemas.java @@ -118,6 +118,11 @@ public static org.openmetadata.schema.type.EntityHistory getVersionList(java.uti return getClient().databaseSchemas().getVersionList(id); } + public static org.openmetadata.schema.type.EntityHistory getVersionList( + java.util.UUID id, int limit, int offset) { + return getClient().databaseSchemas().getVersionList(id, limit, offset); + } + public static DatabaseSchema getVersion(String id, Double version) { return getClient().databaseSchemas().getVersion(id, version); } diff --git a/openmetadata-sdk/src/main/java/org/openmetadata/sdk/fluent/Databases.java b/openmetadata-sdk/src/main/java/org/openmetadata/sdk/fluent/Databases.java index 7741c54c1f07..7b4d31c3a095 100644 --- a/openmetadata-sdk/src/main/java/org/openmetadata/sdk/fluent/Databases.java +++ b/openmetadata-sdk/src/main/java/org/openmetadata/sdk/fluent/Databases.java @@ -118,6 +118,11 @@ public static org.openmetadata.schema.type.EntityHistory getVersionList(java.uti return getClient().databases().getVersionList(id); } + public static org.openmetadata.schema.type.EntityHistory getVersionList( + java.util.UUID id, int limit, int offset) { + return getClient().databases().getVersionList(id, limit, offset); + } + public static Database getVersion(String id, Double version) { return getClient().databases().getVersion(id, version); } diff --git a/openmetadata-sdk/src/main/java/org/openmetadata/sdk/fluent/Domains.java b/openmetadata-sdk/src/main/java/org/openmetadata/sdk/fluent/Domains.java index b5d3d76404a7..a4070333a6cc 100644 --- a/openmetadata-sdk/src/main/java/org/openmetadata/sdk/fluent/Domains.java +++ b/openmetadata-sdk/src/main/java/org/openmetadata/sdk/fluent/Domains.java @@ -115,6 +115,11 @@ public static org.openmetadata.schema.type.EntityHistory getVersionList(java.uti return getClient().domains().getVersionList(id); } + public static org.openmetadata.schema.type.EntityHistory getVersionList( + java.util.UUID id, int limit, int offset) { + return getClient().domains().getVersionList(id, limit, offset); + } + public static Domain getVersion(String id, Double version) { return getClient().domains().getVersion(id, version); } diff --git a/openmetadata-sdk/src/main/java/org/openmetadata/sdk/fluent/Pipelines.java b/openmetadata-sdk/src/main/java/org/openmetadata/sdk/fluent/Pipelines.java index 8097fa8cc4d3..c87994739320 100644 --- a/openmetadata-sdk/src/main/java/org/openmetadata/sdk/fluent/Pipelines.java +++ b/openmetadata-sdk/src/main/java/org/openmetadata/sdk/fluent/Pipelines.java @@ -124,6 +124,11 @@ public static org.openmetadata.schema.type.EntityHistory getVersionList(java.uti return getClient().pipelines().getVersionList(id); } + public static org.openmetadata.schema.type.EntityHistory getVersionList( + java.util.UUID id, int limit, int offset) { + return getClient().pipelines().getVersionList(id, limit, offset); + } + public static Pipeline getVersion(String id, Double version) { return getClient().pipelines().getVersion(id, version); } diff --git a/openmetadata-sdk/src/main/java/org/openmetadata/sdk/fluent/Roles.java b/openmetadata-sdk/src/main/java/org/openmetadata/sdk/fluent/Roles.java index 705b7b499460..2817cf33878c 100644 --- a/openmetadata-sdk/src/main/java/org/openmetadata/sdk/fluent/Roles.java +++ b/openmetadata-sdk/src/main/java/org/openmetadata/sdk/fluent/Roles.java @@ -78,6 +78,11 @@ public static org.openmetadata.schema.type.EntityHistory getVersionList(java.uti return getClient().roles().getVersionList(id); } + public static org.openmetadata.schema.type.EntityHistory getVersionList( + java.util.UUID id, int limit, int offset) { + return getClient().roles().getVersionList(id, limit, offset); + } + public static Role getVersion(String id, Double version) { return getClient().roles().getVersion(id, version); } diff --git a/openmetadata-sdk/src/main/java/org/openmetadata/sdk/fluent/Tables.java b/openmetadata-sdk/src/main/java/org/openmetadata/sdk/fluent/Tables.java index c11760f1f2d9..a9e3b4b6b31e 100644 --- a/openmetadata-sdk/src/main/java/org/openmetadata/sdk/fluent/Tables.java +++ b/openmetadata-sdk/src/main/java/org/openmetadata/sdk/fluent/Tables.java @@ -153,6 +153,11 @@ public static org.openmetadata.schema.type.EntityHistory getVersionList(java.uti return getClient().tables().getVersionList(id); } + public static org.openmetadata.schema.type.EntityHistory getVersionList( + java.util.UUID id, int limit, int offset) { + return getClient().tables().getVersionList(id, limit, offset); + } + public static Table getVersion(String id, Double version) { return getClient().tables().getVersion(id, version); } diff --git a/openmetadata-sdk/src/main/java/org/openmetadata/sdk/fluent/Teams.java b/openmetadata-sdk/src/main/java/org/openmetadata/sdk/fluent/Teams.java index 95e7de4cee63..eee59cefda20 100644 --- a/openmetadata-sdk/src/main/java/org/openmetadata/sdk/fluent/Teams.java +++ b/openmetadata-sdk/src/main/java/org/openmetadata/sdk/fluent/Teams.java @@ -116,6 +116,11 @@ public static org.openmetadata.schema.type.EntityHistory getVersionList(java.uti return getClient().teams().getVersionList(id); } + public static org.openmetadata.schema.type.EntityHistory getVersionList( + java.util.UUID id, int limit, int offset) { + return getClient().teams().getVersionList(id, limit, offset); + } + public static Team getVersion(String id, Double version) { return getClient().teams().getVersion(id, version); } diff --git a/openmetadata-sdk/src/main/java/org/openmetadata/sdk/fluent/Topics.java b/openmetadata-sdk/src/main/java/org/openmetadata/sdk/fluent/Topics.java index f31328e0b714..8d608dc0cb6f 100644 --- a/openmetadata-sdk/src/main/java/org/openmetadata/sdk/fluent/Topics.java +++ b/openmetadata-sdk/src/main/java/org/openmetadata/sdk/fluent/Topics.java @@ -115,6 +115,11 @@ public static org.openmetadata.schema.type.EntityHistory getVersionList(java.uti return getClient().topics().getVersionList(id); } + public static org.openmetadata.schema.type.EntityHistory getVersionList( + java.util.UUID id, int limit, int offset) { + return getClient().topics().getVersionList(id, limit, offset); + } + public static Topic getVersion(String id, Double version) { return getClient().topics().getVersion(id, version); } diff --git a/openmetadata-sdk/src/main/java/org/openmetadata/sdk/fluent/Users.java b/openmetadata-sdk/src/main/java/org/openmetadata/sdk/fluent/Users.java index 35d54df0bb27..34b533cf0df2 100644 --- a/openmetadata-sdk/src/main/java/org/openmetadata/sdk/fluent/Users.java +++ b/openmetadata-sdk/src/main/java/org/openmetadata/sdk/fluent/Users.java @@ -119,6 +119,11 @@ public static org.openmetadata.schema.type.EntityHistory getVersionList(java.uti return getClient().users().getVersionList(id); } + public static org.openmetadata.schema.type.EntityHistory getVersionList( + java.util.UUID id, int limit, int offset) { + return getClient().users().getVersionList(id, limit, offset); + } + public static User getVersion(String id, Double version) { return getClient().users().getVersion(id, version); } diff --git a/openmetadata-sdk/src/main/java/org/openmetadata/sdk/services/EntityServiceBase.java b/openmetadata-sdk/src/main/java/org/openmetadata/sdk/services/EntityServiceBase.java index ae243efc51bf..88cfdb452c47 100644 --- a/openmetadata-sdk/src/main/java/org/openmetadata/sdk/services/EntityServiceBase.java +++ b/openmetadata-sdk/src/main/java/org/openmetadata/sdk/services/EntityServiceBase.java @@ -5,6 +5,7 @@ import com.flipkart.zjsonpatch.JsonDiff; import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; @@ -603,6 +604,20 @@ public EntityHistory getVersionList(String id) throws OpenMetadataException { return httpClient.execute(HttpMethod.GET, path, null, EntityHistory.class); } + public EntityHistory getVersionList(UUID id, int limit, int offset) throws OpenMetadataException { + return getVersionList(id.toString(), limit, offset); + } + + public EntityHistory getVersionList(String id, int limit, int offset) + throws OpenMetadataException { + String path = basePath + "/" + id + "/versions"; + Map queryParams = new HashMap<>(); + queryParams.put("limit", String.valueOf(limit)); + queryParams.put("offset", String.valueOf(offset)); + RequestOptions options = RequestOptions.builder().queryParams(queryParams).build(); + return httpClient.execute(HttpMethod.GET, path, null, EntityHistory.class, options); + } + /** * Get a specific version of an entity. * diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/CollectionDAO.java b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/CollectionDAO.java index a5fcbc3def6b..c5832d63fabf 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/CollectionDAO.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/CollectionDAO.java @@ -1421,6 +1421,11 @@ List getExtensionsWithOffset( @Bind("limit") int limit, @Bind("offset") int offset); + @SqlQuery( + "SELECT COUNT(*) FROM entity_extension WHERE id = :id AND extension " + + "LIKE CONCAT (:extensionPrefix, '.%')") + int getExtensionCount(@BindUUID("id") UUID id, @Bind("extensionPrefix") String extensionPrefix); + @SqlUpdate("DELETE FROM entity_extension WHERE id = :id AND extension = :extension") void delete(@BindUUID("id") UUID id, @Bind("extension") String extension); 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 366dcf6490ba..dd26b5cd31a6 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 @@ -189,6 +189,7 @@ import org.openmetadata.schema.type.FieldChange; import org.openmetadata.schema.type.Include; import org.openmetadata.schema.type.LifeCycle; +import org.openmetadata.schema.type.Paging; import org.openmetadata.schema.type.ProviderType; import org.openmetadata.schema.type.Relationship; import org.openmetadata.schema.type.SuggestionType; @@ -1258,23 +1259,41 @@ public final EntityHistoryWithOffset listVersionsWithOffset(UUID id, int limit, T latest = setFieldsInternal(find(id, ALL), putFields); setInheritedFields(latest, putFields); String extensionPrefix = EntityUtil.getVersionExtensionPrefix(entityType); - List records = - daoCollection - .entityExtensionDAO() - .getExtensionsWithOffset(id, extensionPrefix, limit, offset); - List oldVersions = new ArrayList<>(); - records.forEach(r -> oldVersions.add(new EntityVersionPair(r))); - oldVersions.sort(EntityUtil.compareVersion.reversed()); final List versions = new ArrayList<>(); + int dbLimit; + int dbOffset; if (offset == 0) { versions.add(JsonUtils.pojoToJson(latest)); + dbLimit = limit - 1; + dbOffset = 0; + } else { + dbLimit = limit; + dbOffset = offset - 1; } - oldVersions.forEach(version -> versions.add(version.getEntityJson())); - return new EntityHistoryWithOffset( - new EntityHistory().withEntityType(entityType).withVersions(versions), offset + limit); + if (dbLimit > 0) { + List records = + daoCollection + .entityExtensionDAO() + .getExtensionsWithOffset(id, extensionPrefix, dbLimit, dbOffset); + List oldVersions = new ArrayList<>(); + records.forEach(r -> oldVersions.add(new EntityVersionPair(r))); + oldVersions.sort(EntityUtil.compareVersion.reversed()); + oldVersions.forEach(version -> versions.add(version.getEntityJson())); + } + + int extensionCount = daoCollection.entityExtensionDAO().getExtensionCount(id, extensionPrefix); + int total = extensionCount + 1; + Paging paging = new Paging(); + paging.setOffset(offset); + paging.setLimit(limit); + paging.setTotal(total); + + EntityHistory entityHistory = + new EntityHistory().withEntityType(entityType).withVersions(versions).withPaging(paging); + return new EntityHistoryWithOffset(entityHistory, offset + limit); } public final ResultList listWithOffset( diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/EntityResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/EntityResource.java index c8de55a1ebca..f4cf3b8398ad 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/EntityResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/EntityResource.java @@ -335,8 +335,14 @@ public T getVersionInternal( } protected EntityHistory listVersionsInternal(SecurityContext securityContext, UUID id) { + return listVersionsInternal(securityContext, id, 0, 0); + } + + protected EntityHistory listVersionsInternal( + SecurityContext securityContext, UUID id, int limit, int offset) { OperationContext operationContext = new OperationContext(entityType, VIEW_BASIC); - return listVersionsInternal(securityContext, id, operationContext, getResourceContextById(id)); + return listVersionsInternal( + securityContext, id, limit, offset, operationContext, getResourceContextById(id)); } protected EntityHistory listVersionsInternal( @@ -344,7 +350,20 @@ protected EntityHistory listVersionsInternal( UUID id, OperationContext operationContext, ResourceContextInterface resourceContext) { + return listVersionsInternal(securityContext, id, 0, 0, operationContext, resourceContext); + } + + protected EntityHistory listVersionsInternal( + SecurityContext securityContext, + UUID id, + int limit, + int offset, + OperationContext operationContext, + ResourceContextInterface resourceContext) { authorizer.authorize(securityContext, operationContext, resourceContext); + if (limit > 0) { + return repository.listVersionsWithOffset(id, limit, offset).entityHistory(); + } return repository.listVersions(id); } diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/ai/AIApplicationResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/ai/AIApplicationResource.java index 2a8d91b6ac11..193a2fa197de 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/ai/AIApplicationResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/ai/AIApplicationResource.java @@ -424,8 +424,19 @@ public EntityHistory listVersions( @Context SecurityContext securityContext, @Parameter(description = "Id of the AI Application", schema = @Schema(type = "UUID")) @PathParam("id") - UUID id) { - return super.listVersionsInternal(securityContext, id); + UUID id, + @Parameter(description = "Limit the number of versions returned") + @QueryParam("limit") + @DefaultValue("0") + @Min(0) + @Max(1000) + int limit, + @Parameter(description = "Offset of the versions to return") + @QueryParam("offset") + @DefaultValue("0") + @Min(0) + int offset) { + return super.listVersionsInternal(securityContext, id, limit, offset); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/ai/AIGovernancePolicyResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/ai/AIGovernancePolicyResource.java index a469b705e839..1879dadbc087 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/ai/AIGovernancePolicyResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/ai/AIGovernancePolicyResource.java @@ -409,8 +409,19 @@ public EntityHistory listVersions( @Context SecurityContext securityContext, @Parameter(description = "Id of the AI Governance Policy", schema = @Schema(type = "UUID")) @PathParam("id") - UUID id) { - return super.listVersionsInternal(securityContext, id); + UUID id, + @Parameter(description = "Limit the number of versions returned") + @QueryParam("limit") + @DefaultValue("0") + @Min(0) + @Max(1000) + int limit, + @Parameter(description = "Offset of the versions to return") + @QueryParam("offset") + @DefaultValue("0") + @Min(0) + int offset) { + return super.listVersionsInternal(securityContext, id, limit, offset); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/ai/LLMModelResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/ai/LLMModelResource.java index 1bfbc7aeb4fe..bd3ca7f2a099 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/ai/LLMModelResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/ai/LLMModelResource.java @@ -418,8 +418,19 @@ public EntityHistory listVersions( @Context SecurityContext securityContext, @Parameter(description = "Id of the LLM Model", schema = @Schema(type = "UUID")) @PathParam("id") - UUID id) { - return super.listVersionsInternal(securityContext, id); + UUID id, + @Parameter(description = "Limit the number of versions returned") + @QueryParam("limit") + @DefaultValue("0") + @Min(0) + @Max(1000) + int limit, + @Parameter(description = "Offset of the versions to return") + @QueryParam("offset") + @DefaultValue("0") + @Min(0) + int offset) { + return super.listVersionsInternal(securityContext, id, limit, offset); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/ai/PromptTemplateResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/ai/PromptTemplateResource.java index 416ec1d43bdb..e817b38a27d7 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/ai/PromptTemplateResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/ai/PromptTemplateResource.java @@ -407,8 +407,19 @@ public EntityHistory listVersions( @Context SecurityContext securityContext, @Parameter(description = "Id of the Prompt Template", schema = @Schema(type = "UUID")) @PathParam("id") - UUID id) { - return super.listVersionsInternal(securityContext, id); + UUID id, + @Parameter(description = "Limit the number of versions returned") + @QueryParam("limit") + @DefaultValue("0") + @Min(0) + @Max(1000) + int limit, + @Parameter(description = "Offset of the versions to return") + @QueryParam("offset") + @DefaultValue("0") + @Min(0) + int offset) { + return super.listVersionsInternal(securityContext, id, limit, offset); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/analytics/WebAnalyticEventResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/analytics/WebAnalyticEventResource.java index d7471083d91c..6afb324f105e 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/analytics/WebAnalyticEventResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/analytics/WebAnalyticEventResource.java @@ -470,8 +470,19 @@ public EntityHistory listVersions( @Context SecurityContext securityContext, @Parameter(description = "Id of the web analytic event", schema = @Schema(type = "UUID")) @PathParam("id") - UUID id) { - return super.listVersionsInternal(securityContext, id); + UUID id, + @Parameter(description = "Limit the number of versions returned") + @QueryParam("limit") + @DefaultValue("0") + @Min(0) + @Max(1000) + int limit, + @Parameter(description = "Offset of the versions to return") + @QueryParam("offset") + @DefaultValue("0") + @Min(0) + int offset) { + return super.listVersionsInternal(securityContext, id, limit, offset); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/apis/APICollectionResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/apis/APICollectionResource.java index 3f575db7f5c2..2f7da928644e 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/apis/APICollectionResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/apis/APICollectionResource.java @@ -177,8 +177,19 @@ public EntityHistory listVersions( @Context SecurityContext securityContext, @Parameter(description = "Id of the API Collection", schema = @Schema(type = "UUID")) @PathParam("id") - UUID id) { - return super.listVersionsInternal(securityContext, id); + UUID id, + @Parameter(description = "Limit the number of versions returned") + @QueryParam("limit") + @DefaultValue("0") + @Min(0) + @Max(1000) + int limit, + @Parameter(description = "Offset of the versions to return") + @QueryParam("offset") + @DefaultValue("0") + @Min(0) + int offset) { + return super.listVersionsInternal(securityContext, id, limit, offset); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/apis/APIEndpointResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/apis/APIEndpointResource.java index 4f76cd9e7123..a2c2ca90a32c 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/apis/APIEndpointResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/apis/APIEndpointResource.java @@ -176,8 +176,19 @@ public EntityHistory listVersions( @Context SecurityContext securityContext, @Parameter(description = "Id of the APIEndpoint", schema = @Schema(type = "UUID")) @PathParam("id") - UUID id) { - return super.listVersionsInternal(securityContext, id); + UUID id, + @Parameter(description = "Limit the number of versions returned") + @QueryParam("limit") + @DefaultValue("0") + @Min(0) + @Max(1000) + int limit, + @Parameter(description = "Offset of the versions to return") + @QueryParam("offset") + @DefaultValue("0") + @Min(0) + int offset) { + return super.listVersionsInternal(securityContext, id, limit, offset); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/apps/AppMarketPlaceResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/apps/AppMarketPlaceResource.java index 168650c7a4cf..38c4097e109b 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/apps/AppMarketPlaceResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/apps/AppMarketPlaceResource.java @@ -159,8 +159,19 @@ public EntityHistory listVersions( @Context UriInfo uriInfo, @Context SecurityContext securityContext, @Parameter(description = "Id of the app", schema = @Schema(type = "UUID")) @PathParam("id") - UUID id) { - return super.listVersionsInternal(securityContext, id); + UUID id, + @Parameter(description = "Limit the number of versions returned") + @QueryParam("limit") + @DefaultValue("0") + @Min(0) + @Max(1000) + int limit, + @Parameter(description = "Offset of the versions to return") + @QueryParam("offset") + @DefaultValue("0") + @Min(0) + int offset) { + return super.listVersionsInternal(securityContext, id, limit, offset); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/apps/AppResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/apps/AppResource.java index 11ecda8469da..d0ae7916eeb1 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/apps/AppResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/apps/AppResource.java @@ -574,8 +574,19 @@ public EntityHistory listVersions( @Context UriInfo uriInfo, @Context SecurityContext securityContext, @Parameter(description = "Id of the app", schema = @Schema(type = "UUID")) @PathParam("id") - UUID id) { - return super.listVersionsInternal(securityContext, id); + UUID id, + @Parameter(description = "Limit the number of versions returned") + @QueryParam("limit") + @DefaultValue("0") + @Min(0) + @Max(1000) + int limit, + @Parameter(description = "Offset of the versions to return") + @QueryParam("offset") + @DefaultValue("0") + @Min(0) + int offset) { + return super.listVersionsInternal(securityContext, id, limit, offset); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/automations/WorkflowResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/automations/WorkflowResource.java index 56330671553e..a9e4a15288b0 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/automations/WorkflowResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/automations/WorkflowResource.java @@ -200,8 +200,19 @@ public EntityHistory listVersions( @Context SecurityContext securityContext, @Parameter(description = "Id of the Workflow", schema = @Schema(type = "UUID")) @PathParam("id") - UUID id) { - return super.listVersionsInternal(securityContext, id); + UUID id, + @Parameter(description = "Limit the number of versions returned") + @QueryParam("limit") + @DefaultValue("0") + @Min(0) + @Max(1000) + int limit, + @Parameter(description = "Offset of the versions to return") + @QueryParam("offset") + @DefaultValue("0") + @Min(0) + int offset) { + return super.listVersionsInternal(securityContext, id, limit, offset); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/bots/BotResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/bots/BotResource.java index 6175e4eac8fd..e7ac81bb9663 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/bots/BotResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/bots/BotResource.java @@ -257,8 +257,19 @@ public EntityHistory listVersions( @Context UriInfo uriInfo, @Context SecurityContext securityContext, @Parameter(description = "Id of the bot", schema = @Schema(type = "UUID")) @PathParam("id") - UUID id) { - return super.listVersionsInternal(securityContext, id); + UUID id, + @Parameter(description = "Limit the number of versions returned") + @QueryParam("limit") + @DefaultValue("0") + @Min(0) + @Max(1000) + int limit, + @Parameter(description = "Offset of the versions to return") + @QueryParam("offset") + @DefaultValue("0") + @Min(0) + int offset) { + return super.listVersionsInternal(securityContext, id, limit, offset); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/charts/ChartResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/charts/ChartResource.java index 96ff75878e9e..ef27ce15c838 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/charts/ChartResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/charts/ChartResource.java @@ -174,8 +174,19 @@ public EntityHistory listVersions( @Context UriInfo uriInfo, @Context SecurityContext securityContext, @Parameter(description = "Id of the chart", schema = @Schema(type = "UUID")) @PathParam("id") - UUID id) { - return super.listVersionsInternal(securityContext, id); + UUID id, + @Parameter(description = "Limit the number of versions returned") + @QueryParam("limit") + @DefaultValue("0") + @Min(0) + @Max(1000) + int limit, + @Parameter(description = "Offset of the versions to return") + @QueryParam("offset") + @DefaultValue("0") + @Min(0) + int offset) { + return super.listVersionsInternal(securityContext, id, limit, offset); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/dashboards/DashboardResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/dashboards/DashboardResource.java index 3ac05657402a..14d6460d3e3e 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/dashboards/DashboardResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/dashboards/DashboardResource.java @@ -180,8 +180,19 @@ public EntityHistory listVersions( @Context SecurityContext securityContext, @Parameter(description = "Id of the dashboard", schema = @Schema(type = "UUID")) @PathParam("id") - UUID id) { - return super.listVersionsInternal(securityContext, id); + UUID id, + @Parameter(description = "Limit the number of versions returned") + @QueryParam("limit") + @DefaultValue("0") + @Min(0) + @Max(1000) + int limit, + @Parameter(description = "Offset of the versions to return") + @QueryParam("offset") + @DefaultValue("0") + @Min(0) + int offset) { + return super.listVersionsInternal(securityContext, id, limit, offset); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/data/DataContractResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/data/DataContractResource.java index 6d3440766c89..9377b3c35a74 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/data/DataContractResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/data/DataContractResource.java @@ -352,8 +352,19 @@ public EntityHistory listVersions( @Context UriInfo uriInfo, @Context SecurityContext securityContext, @Parameter(description = "Data contract Id", schema = @Schema(type = "UUID")) @PathParam("id") - UUID id) { - return super.listVersionsInternal(securityContext, id); + UUID id, + @Parameter(description = "Limit the number of versions returned") + @QueryParam("limit") + @DefaultValue("0") + @Min(0) + @Max(1000) + int limit, + @Parameter(description = "Offset of the versions to return") + @QueryParam("offset") + @DefaultValue("0") + @Min(0) + int offset) { + return super.listVersionsInternal(securityContext, id, limit, offset); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/databases/DatabaseResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/databases/DatabaseResource.java index 3e10c4b358c5..5a3d68c327e3 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/databases/DatabaseResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/databases/DatabaseResource.java @@ -185,8 +185,19 @@ public EntityHistory listVersions( @Context SecurityContext securityContext, @Parameter(description = "Id of the database", schema = @Schema(type = "UUID")) @PathParam("id") - UUID id) { - return super.listVersionsInternal(securityContext, id); + UUID id, + @Parameter(description = "Limit the number of versions returned") + @QueryParam("limit") + @DefaultValue("0") + @Min(0) + @Max(1000) + int limit, + @Parameter(description = "Offset of the versions to return") + @QueryParam("offset") + @DefaultValue("0") + @Min(0) + int offset) { + return super.listVersionsInternal(securityContext, id, limit, offset); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/databases/DatabaseSchemaResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/databases/DatabaseSchemaResource.java index f6d234b5a349..d90788ec25e7 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/databases/DatabaseSchemaResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/databases/DatabaseSchemaResource.java @@ -185,8 +185,19 @@ public EntityHistory listVersions( @Context SecurityContext securityContext, @Parameter(description = "Database schema Id", schema = @Schema(type = "UUID")) @PathParam("id") - UUID id) { - return super.listVersionsInternal(securityContext, id); + UUID id, + @Parameter(description = "Limit the number of versions returned") + @QueryParam("limit") + @DefaultValue("0") + @Min(0) + @Max(1000) + int limit, + @Parameter(description = "Offset of the versions to return") + @QueryParam("offset") + @DefaultValue("0") + @Min(0) + int offset) { + return super.listVersionsInternal(securityContext, id, limit, offset); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/databases/StoredProcedureResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/databases/StoredProcedureResource.java index 760da9de6b55..e8fd1e1563fa 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/databases/StoredProcedureResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/databases/StoredProcedureResource.java @@ -141,8 +141,19 @@ public EntityHistory listVersions( @Context SecurityContext securityContext, @Parameter(description = "Stored Procedure Id", schema = @Schema(type = "UUID")) @PathParam("id") - UUID id) { - return super.listVersionsInternal(securityContext, id); + UUID id, + @Parameter(description = "Limit the number of versions returned") + @QueryParam("limit") + @DefaultValue("0") + @Min(0) + @Max(1000) + int limit, + @Parameter(description = "Offset of the versions to return") + @QueryParam("offset") + @DefaultValue("0") + @Min(0) + int offset) { + return super.listVersionsInternal(securityContext, id, limit, offset); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/databases/TableResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/databases/TableResource.java index 1a41301813f2..04964c334873 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/databases/TableResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/databases/TableResource.java @@ -351,8 +351,19 @@ public EntityHistory listVersions( @Context UriInfo uriInfo, @Context SecurityContext securityContext, @Parameter(description = "Table Id", schema = @Schema(type = "string")) @PathParam("id") - UUID id) { - return super.listVersionsInternal(securityContext, id); + UUID id, + @Parameter(description = "Limit the number of versions returned") + @QueryParam("limit") + @DefaultValue("0") + @Min(0) + @Max(1000) + int limit, + @Parameter(description = "Offset of the versions to return") + @QueryParam("offset") + @DefaultValue("0") + @Min(0) + int offset) { + return super.listVersionsInternal(securityContext, id, limit, offset); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/datainsight/DataInsightChartResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/datainsight/DataInsightChartResource.java index 9a28ee188715..1aa90ce8b8d7 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/datainsight/DataInsightChartResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/datainsight/DataInsightChartResource.java @@ -166,8 +166,19 @@ public EntityHistory listVersions( @Context SecurityContext securityContext, @Parameter(description = "Id of the data insight chart", schema = @Schema(type = "UUID")) @PathParam("id") - UUID id) { - return super.listVersionsInternal(securityContext, id); + UUID id, + @Parameter(description = "Limit the number of versions returned") + @QueryParam("limit") + @DefaultValue("0") + @Min(0) + @Max(1000) + int limit, + @Parameter(description = "Offset of the versions to return") + @QueryParam("offset") + @DefaultValue("0") + @Min(0) + int offset) { + return super.listVersionsInternal(securityContext, id, limit, offset); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/datamodels/DashboardDataModelResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/datamodels/DashboardDataModelResource.java index 8a69e00aa088..71dd51b10ac3 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/datamodels/DashboardDataModelResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/datamodels/DashboardDataModelResource.java @@ -183,8 +183,19 @@ public EntityHistory listVersions( @Context SecurityContext securityContext, @Parameter(description = "Id of the dashboard datamodel", schema = @Schema(type = "UUID")) @PathParam("id") - UUID id) { - return super.listVersionsInternal(securityContext, id); + UUID id, + @Parameter(description = "Limit the number of versions returned") + @QueryParam("limit") + @DefaultValue("0") + @Min(0) + @Max(1000) + int limit, + @Parameter(description = "Offset of the versions to return") + @QueryParam("offset") + @DefaultValue("0") + @Min(0) + int offset) { + return super.listVersionsInternal(securityContext, id, limit, offset); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/docstore/DocStoreResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/docstore/DocStoreResource.java index 48b581669f9d..d070b7869552 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/docstore/DocStoreResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/docstore/DocStoreResource.java @@ -180,8 +180,19 @@ public EntityHistory listVersions( @Context SecurityContext securityContext, @Parameter(description = "Id of the Document", schema = @Schema(type = "UUID")) @PathParam("id") - UUID id) { - return super.listVersionsInternal(securityContext, id); + UUID id, + @Parameter(description = "Limit the number of versions returned") + @QueryParam("limit") + @DefaultValue("0") + @Min(0) + @Max(1000) + int limit, + @Parameter(description = "Offset of the versions to return") + @QueryParam("offset") + @DefaultValue("0") + @Min(0) + int offset) { + return super.listVersionsInternal(securityContext, id, limit, offset); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/domains/DataProductResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/domains/DataProductResource.java index 7a991d27eba7..4d01c4ab5404 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/domains/DataProductResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/domains/DataProductResource.java @@ -262,8 +262,19 @@ public EntityHistory listVersions( @Context SecurityContext securityContext, @Parameter(description = "Id of the dataProduct", schema = @Schema(type = "UUID")) @PathParam("id") - UUID id) { - return super.listVersionsInternal(securityContext, id); + UUID id, + @Parameter(description = "Limit the number of versions returned") + @QueryParam("limit") + @DefaultValue("0") + @Min(0) + @Max(1000) + int limit, + @Parameter(description = "Offset of the versions to return") + @QueryParam("offset") + @DefaultValue("0") + @Min(0) + int offset) { + return super.listVersionsInternal(securityContext, id, limit, offset); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/domains/DomainResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/domains/DomainResource.java index 1cca6cfdedcc..37f779efe354 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/domains/DomainResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/domains/DomainResource.java @@ -215,8 +215,19 @@ public EntityHistory listVersions( @Context UriInfo uriInfo, @Context SecurityContext securityContext, @Parameter(description = "Id of the domain", schema = @Schema(type = "UUID")) @PathParam("id") - UUID id) { - return super.listVersionsInternal(securityContext, id); + UUID id, + @Parameter(description = "Limit the number of versions returned") + @QueryParam("limit") + @DefaultValue("0") + @Min(0) + @Max(1000) + int limit, + @Parameter(description = "Offset of the versions to return") + @QueryParam("offset") + @DefaultValue("0") + @Min(0) + int offset) { + return super.listVersionsInternal(securityContext, id, limit, offset); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/dqtests/TestCaseResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/dqtests/TestCaseResource.java index 6fb00cd46146..6a576fbdde25 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/dqtests/TestCaseResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/dqtests/TestCaseResource.java @@ -500,14 +500,24 @@ public EntityHistory listVersions( @Context SecurityContext securityContext, @Parameter(description = "Id of the test case", schema = @Schema(type = "UUID")) @PathParam("id") - UUID id) { + UUID id, + @Parameter(description = "Limit the number of versions returned") + @QueryParam("limit") + @DefaultValue("0") + @Min(0) + @Max(1000) + int limit, + @Parameter(description = "Offset of the versions to return") + @QueryParam("offset") + @DefaultValue("0") + @Min(0) + int offset) { ResourceContextInterface resourceContext = TestCaseResourceContext.builder().id(id).build(); - // Override OperationContext to change the entity to table and operation from VIEW_ALL to - // VIEW_TESTS OperationContext operationContext = new OperationContext(Entity.TABLE, MetadataOperation.VIEW_TESTS); - return super.listVersionsInternal(securityContext, id, operationContext, resourceContext); + return super.listVersionsInternal( + securityContext, id, limit, offset, operationContext, resourceContext); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/dqtests/TestDefinitionResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/dqtests/TestDefinitionResource.java index 923e699ee405..a26a8cdbf492 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/dqtests/TestDefinitionResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/dqtests/TestDefinitionResource.java @@ -202,8 +202,19 @@ public EntityHistory listVersions( @Context SecurityContext securityContext, @Parameter(description = "Id of the test definition", schema = @Schema(type = "UUID")) @PathParam("id") - UUID id) { - return super.listVersionsInternal(securityContext, id); + UUID id, + @Parameter(description = "Limit the number of versions returned") + @QueryParam("limit") + @DefaultValue("0") + @Min(0) + @Max(1000) + int limit, + @Parameter(description = "Offset of the versions to return") + @QueryParam("offset") + @DefaultValue("0") + @Min(0) + int offset) { + return super.listVersionsInternal(securityContext, id, limit, offset); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/dqtests/TestSuiteResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/dqtests/TestSuiteResource.java index fed7540d002f..78c4833009a5 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/dqtests/TestSuiteResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/dqtests/TestSuiteResource.java @@ -338,8 +338,19 @@ public EntityHistory listVersions( @Context SecurityContext securityContext, @Parameter(description = "Id of the test suite", schema = @Schema(type = "UUID")) @PathParam("id") - UUID id) { - return super.listVersionsInternal(securityContext, id); + UUID id, + @Parameter(description = "Limit the number of versions returned") + @QueryParam("limit") + @DefaultValue("0") + @Min(0) + @Max(1000) + int limit, + @Parameter(description = "Offset of the versions to return") + @QueryParam("offset") + @DefaultValue("0") + @Min(0) + int offset) { + return super.listVersionsInternal(securityContext, id, limit, offset); } @GET 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 016c1fdef4ab..45b0916c4fb7 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 @@ -523,8 +523,19 @@ public EntityHistory listVersions( @Context SecurityContext securityContext, @Parameter(description = "Id of the directory", schema = @Schema(type = "UUID")) @PathParam("id") - UUID id) { - return super.listVersionsInternal(securityContext, id); + UUID id, + @Parameter(description = "Limit the number of versions returned") + @QueryParam("limit") + @DefaultValue("0") + @Min(0) + @Max(1000) + int limit, + @Parameter(description = "Offset of the versions to return") + @QueryParam("offset") + @DefaultValue("0") + @Min(0) + int offset) { + return super.listVersionsInternal(securityContext, id, limit, offset); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/drives/FileResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/drives/FileResource.java index bcbbdd6088a9..8958d55e1949 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/drives/FileResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/drives/FileResource.java @@ -533,8 +533,19 @@ public EntityHistory listVersions( @Context UriInfo uriInfo, @Context SecurityContext securityContext, @Parameter(description = "Id of the file", schema = @Schema(type = "UUID")) @PathParam("id") - UUID id) { - return super.listVersionsInternal(securityContext, id); + UUID id, + @Parameter(description = "Limit the number of versions returned") + @QueryParam("limit") + @DefaultValue("0") + @Min(0) + @Max(1000) + int limit, + @Parameter(description = "Offset of the versions to return") + @QueryParam("offset") + @DefaultValue("0") + @Min(0) + int offset) { + return super.listVersionsInternal(securityContext, id, limit, offset); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/drives/SpreadsheetResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/drives/SpreadsheetResource.java index 28335091455c..bf55e3908579 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/drives/SpreadsheetResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/drives/SpreadsheetResource.java @@ -503,8 +503,19 @@ public EntityHistory listVersions( @Context SecurityContext securityContext, @Parameter(description = "Id of the spreadsheet", schema = @Schema(type = "UUID")) @PathParam("id") - UUID id) { - return super.listVersionsInternal(securityContext, id); + UUID id, + @Parameter(description = "Limit the number of versions returned") + @QueryParam("limit") + @DefaultValue("0") + @Min(0) + @Max(1000) + int limit, + @Parameter(description = "Offset of the versions to return") + @QueryParam("offset") + @DefaultValue("0") + @Min(0) + int offset) { + return super.listVersionsInternal(securityContext, id, limit, offset); } @GET 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 c094d0bbb595..cc090096599b 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 @@ -481,8 +481,19 @@ public EntityHistory listVersions( @Context SecurityContext securityContext, @Parameter(description = "Id of the worksheet", schema = @Schema(type = "UUID")) @PathParam("id") - UUID id) { - return super.listVersionsInternal(securityContext, id); + UUID id, + @Parameter(description = "Limit the number of versions returned") + @QueryParam("limit") + @DefaultValue("0") + @Min(0) + @Max(1000) + int limit, + @Parameter(description = "Offset of the versions to return") + @QueryParam("offset") + @DefaultValue("0") + @Min(0) + int offset) { + return super.listVersionsInternal(securityContext, id, limit, offset); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/events/NotificationTemplateResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/events/NotificationTemplateResource.java index 48886f978a64..a0f457f2ecb4 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/events/NotificationTemplateResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/events/NotificationTemplateResource.java @@ -317,8 +317,19 @@ public EntityHistory listVersions( @Context SecurityContext securityContext, @Parameter(description = "Id of the notification template", schema = @Schema(type = "UUID")) @PathParam("id") - UUID id) { - return super.listVersionsInternal(securityContext, id); + UUID id, + @Parameter(description = "Limit the number of versions returned") + @QueryParam("limit") + @DefaultValue("0") + @Min(0) + @Max(1000) + int limit, + @Parameter(description = "Offset of the versions to return") + @QueryParam("offset") + @DefaultValue("0") + @Min(0) + int offset) { + return super.listVersionsInternal(securityContext, id, limit, offset); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/events/subscription/EventSubscriptionResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/events/subscription/EventSubscriptionResource.java index e12a0fcf8a2f..e8215382bd1f 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/events/subscription/EventSubscriptionResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/events/subscription/EventSubscriptionResource.java @@ -460,8 +460,19 @@ public EntityHistory listEventSubscriptionVersions( @Context SecurityContext securityContext, @Parameter(description = "Id of the Event Subscription", schema = @Schema(type = "UUID")) @PathParam("id") - UUID id) { - return super.listVersionsInternal(securityContext, id); + UUID id, + @Parameter(description = "Limit the number of versions returned") + @QueryParam("limit") + @DefaultValue("0") + @Min(0) + @Max(1000) + int limit, + @Parameter(description = "Offset of the versions to return") + @QueryParam("offset") + @DefaultValue("0") + @Min(0) + int offset) { + return super.listVersionsInternal(securityContext, id, limit, offset); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/glossary/GlossaryResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/glossary/GlossaryResource.java index 57b8c03e391f..1bf199686503 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/glossary/GlossaryResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/glossary/GlossaryResource.java @@ -251,8 +251,19 @@ public EntityHistory listVersions( @Context SecurityContext securityContext, @Parameter(description = "Id of the glossary", schema = @Schema(type = "UUID")) @PathParam("id") - UUID id) { - return super.listVersionsInternal(securityContext, id); + UUID id, + @Parameter(description = "Limit the number of versions returned") + @QueryParam("limit") + @DefaultValue("0") + @Min(0) + @Max(1000) + int limit, + @Parameter(description = "Offset of the versions to return") + @QueryParam("offset") + @DefaultValue("0") + @Min(0) + int offset) { + return super.listVersionsInternal(securityContext, id, limit, offset); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/glossary/GlossaryTermResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/glossary/GlossaryTermResource.java index c84e9a3908fa..4ef68b1d2f29 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/glossary/GlossaryTermResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/glossary/GlossaryTermResource.java @@ -507,8 +507,19 @@ public EntityHistory listVersions( @Context SecurityContext securityContext, @Parameter(description = "Id of the glossary term", schema = @Schema(type = "UUID")) @PathParam("id") - UUID id) { - return super.listVersionsInternal(securityContext, id); + UUID id, + @Parameter(description = "Limit the number of versions returned") + @QueryParam("limit") + @DefaultValue("0") + @Min(0) + @Max(1000) + int limit, + @Parameter(description = "Offset of the versions to return") + @QueryParam("offset") + @DefaultValue("0") + @Min(0) + int offset) { + return super.listVersionsInternal(securityContext, id, limit, offset); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/governance/WorkflowDefinitionResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/governance/WorkflowDefinitionResource.java index aa9509e7bac9..b74f652e520b 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/governance/WorkflowDefinitionResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/governance/WorkflowDefinitionResource.java @@ -155,8 +155,19 @@ public EntityHistory listVersions( @Context SecurityContext securityContext, @Parameter(description = "Id of the Workflow Definition", schema = @Schema(type = "UUID")) @PathParam("id") - UUID id) { - return super.listVersionsInternal(securityContext, id); + UUID id, + @Parameter(description = "Limit the number of versions returned") + @QueryParam("limit") + @DefaultValue("0") + @Min(0) + @Max(1000) + int limit, + @Parameter(description = "Offset of the versions to return") + @QueryParam("offset") + @DefaultValue("0") + @Min(0) + int offset) { + return super.listVersionsInternal(securityContext, id, limit, offset); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/kpi/KpiResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/kpi/KpiResource.java index e8d345599fb6..2c402d21b813 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/kpi/KpiResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/kpi/KpiResource.java @@ -161,8 +161,19 @@ public EntityHistory listVersions( @Context UriInfo uriInfo, @Context SecurityContext securityContext, @Parameter(description = "Id of the KPI", schema = @Schema(type = "UUID")) @PathParam("id") - UUID id) { - return super.listVersionsInternal(securityContext, id); + UUID id, + @Parameter(description = "Limit the number of versions returned") + @QueryParam("limit") + @DefaultValue("0") + @Min(0) + @Max(1000) + int limit, + @Parameter(description = "Offset of the versions to return") + @QueryParam("offset") + @DefaultValue("0") + @Min(0) + int offset) { + return super.listVersionsInternal(securityContext, id, limit, offset); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/learning/LearningResourceResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/learning/LearningResourceResource.java index 3874474e37cd..dbfd8c834117 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/learning/LearningResourceResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/learning/LearningResourceResource.java @@ -280,8 +280,19 @@ public EntityHistory listVersions( @Context SecurityContext securityContext, @Parameter(description = "Id of the learning resource", schema = @Schema(type = "UUID")) @PathParam("id") - UUID id) { - return listVersionsInternal(securityContext, id); + UUID id, + @Parameter(description = "Limit the number of versions returned") + @QueryParam("limit") + @DefaultValue("0") + @Min(0) + @Max(1000) + int limit, + @Parameter(description = "Offset of the versions to return") + @QueryParam("offset") + @DefaultValue("0") + @Min(0) + int offset) { + return listVersionsInternal(securityContext, id, limit, offset); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/metrics/MetricResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/metrics/MetricResource.java index 9865a2218bd3..b0cf2bd5a752 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/metrics/MetricResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/metrics/MetricResource.java @@ -250,8 +250,19 @@ public EntityHistory listVersions( @Context UriInfo uriInfo, @Context SecurityContext securityContext, @Parameter(description = "Id of the metric", schema = @Schema(type = "UUID")) @PathParam("id") - UUID id) { - return super.listVersionsInternal(securityContext, id); + UUID id, + @Parameter(description = "Limit the number of versions returned") + @QueryParam("limit") + @DefaultValue("0") + @Min(0) + @Max(1000) + int limit, + @Parameter(description = "Offset of the versions to return") + @QueryParam("offset") + @DefaultValue("0") + @Min(0) + int offset) { + return super.listVersionsInternal(securityContext, id, limit, offset); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/mlmodels/MlModelResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/mlmodels/MlModelResource.java index 785c2358cba3..633915e98bc1 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/mlmodels/MlModelResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/mlmodels/MlModelResource.java @@ -475,8 +475,19 @@ public EntityHistory listVersions( @Context SecurityContext securityContext, @Parameter(description = "Id of the ML Model", schema = @Schema(type = "UUID")) @PathParam("id") - UUID id) { - return super.listVersionsInternal(securityContext, id); + UUID id, + @Parameter(description = "Limit the number of versions returned") + @QueryParam("limit") + @DefaultValue("0") + @Min(0) + @Max(1000) + int limit, + @Parameter(description = "Offset of the versions to return") + @QueryParam("offset") + @DefaultValue("0") + @Min(0) + int offset) { + return super.listVersionsInternal(securityContext, id, limit, offset); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/pipelines/PipelineResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/pipelines/PipelineResource.java index c307cdd2e5e3..8a81797ade4d 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/pipelines/PipelineResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/pipelines/PipelineResource.java @@ -195,8 +195,19 @@ public EntityHistory listVersions( @Context SecurityContext securityContext, @Parameter(description = "Id of the pipeline", schema = @Schema(type = "UUID")) @PathParam("id") - UUID id) { - return super.listVersionsInternal(securityContext, id); + UUID id, + @Parameter(description = "Limit the number of versions returned") + @QueryParam("limit") + @DefaultValue("0") + @Min(0) + @Max(1000) + int limit, + @Parameter(description = "Offset of the versions to return") + @QueryParam("offset") + @DefaultValue("0") + @Min(0) + int offset) { + return super.listVersionsInternal(securityContext, id, limit, offset); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/policies/PolicyResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/policies/PolicyResource.java index 80c59d85b093..1558727fc7a2 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/policies/PolicyResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/policies/PolicyResource.java @@ -302,8 +302,19 @@ public EntityHistory listVersions( @Context UriInfo uriInfo, @Context SecurityContext securityContext, @Parameter(description = "Id of the policy", schema = @Schema(type = "UUID")) @PathParam("id") - UUID id) { - return super.listVersionsInternal(securityContext, id); + UUID id, + @Parameter(description = "Limit the number of versions returned") + @QueryParam("limit") + @DefaultValue("0") + @Min(0) + @Max(1000) + int limit, + @Parameter(description = "Offset of the versions to return") + @QueryParam("offset") + @DefaultValue("0") + @Min(0) + int offset) { + return super.listVersionsInternal(securityContext, id, limit, offset); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/query/QueryResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/query/QueryResource.java index 695855121df7..6165f4087480 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/query/QueryResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/query/QueryResource.java @@ -264,8 +264,19 @@ public EntityHistory listVersions( @Context UriInfo uriInfo, @Context SecurityContext securityContext, @Parameter(description = "Query Id", schema = @Schema(type = "string")) @PathParam("id") - UUID id) { - return super.listVersionsInternal(securityContext, id); + UUID id, + @Parameter(description = "Limit the number of versions returned") + @QueryParam("limit") + @DefaultValue("0") + @Min(0) + @Max(1000) + int limit, + @Parameter(description = "Offset of the versions to return") + @QueryParam("offset") + @DefaultValue("0") + @Min(0) + int offset) { + return super.listVersionsInternal(securityContext, id, limit, offset); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/searchindex/SearchIndexResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/searchindex/SearchIndexResource.java index 2570ba07642a..d6d4961f3c92 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/searchindex/SearchIndexResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/searchindex/SearchIndexResource.java @@ -178,8 +178,19 @@ public EntityHistory listVersions( @Context SecurityContext securityContext, @Parameter(description = "Id of the SearchIndex", schema = @Schema(type = "UUID")) @PathParam("id") - UUID id) { - return super.listVersionsInternal(securityContext, id); + UUID id, + @Parameter(description = "Limit the number of versions returned") + @QueryParam("limit") + @DefaultValue("0") + @Min(0) + @Max(1000) + int limit, + @Parameter(description = "Offset of the versions to return") + @QueryParam("offset") + @DefaultValue("0") + @Min(0) + int offset) { + return super.listVersionsInternal(securityContext, id, limit, offset); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/apiservices/APIServiceResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/apiservices/APIServiceResource.java index b8555d192487..ddf73954d223 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/apiservices/APIServiceResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/apiservices/APIServiceResource.java @@ -305,8 +305,19 @@ public EntityHistory listVersions( @Context UriInfo uriInfo, @Context SecurityContext securityContext, @Parameter(description = "API service Id", schema = @Schema(type = "string")) @PathParam("id") - UUID id) { - EntityHistory entityHistory = super.listVersionsInternal(securityContext, id); + UUID id, + @Parameter(description = "Limit the number of versions returned") + @QueryParam("limit") + @DefaultValue("0") + @Min(0) + @Max(1000) + int limit, + @Parameter(description = "Offset of the versions to return") + @QueryParam("offset") + @DefaultValue("0") + @Min(0) + int offset) { + EntityHistory entityHistory = super.listVersionsInternal(securityContext, id, limit, offset); List versions = entityHistory.getVersions().stream() diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/dashboard/DashboardServiceResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/dashboard/DashboardServiceResource.java index 5ebcc81c1391..48d2b72a2863 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/dashboard/DashboardServiceResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/dashboard/DashboardServiceResource.java @@ -347,8 +347,19 @@ public EntityHistory listVersions( @Context SecurityContext securityContext, @Parameter(description = "Id of the dashboard service", schema = @Schema(type = "UUID")) @PathParam("id") - UUID id) { - EntityHistory entityHistory = super.listVersionsInternal(securityContext, id); + UUID id, + @Parameter(description = "Limit the number of versions returned") + @QueryParam("limit") + @DefaultValue("0") + @Min(0) + @Max(1000) + int limit, + @Parameter(description = "Offset of the versions to return") + @QueryParam("offset") + @DefaultValue("0") + @Min(0) + int offset) { + EntityHistory entityHistory = super.listVersionsInternal(securityContext, id, limit, offset); List versions = entityHistory.getVersions().stream() diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/database/DatabaseServiceResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/database/DatabaseServiceResource.java index 394a145c493f..c87e8906512d 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/database/DatabaseServiceResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/database/DatabaseServiceResource.java @@ -304,8 +304,19 @@ public EntityHistory listVersions( @Context SecurityContext securityContext, @Parameter(description = "Id of the database service", schema = @Schema(type = "UUID")) @PathParam("id") - UUID id) { - EntityHistory entityHistory = super.listVersionsInternal(securityContext, id); + UUID id, + @Parameter(description = "Limit the number of versions returned") + @QueryParam("limit") + @DefaultValue("0") + @Min(0) + @Max(1000) + int limit, + @Parameter(description = "Offset of the versions to return") + @QueryParam("offset") + @DefaultValue("0") + @Min(0) + int offset) { + EntityHistory entityHistory = super.listVersionsInternal(securityContext, id, limit, offset); List versions = entityHistory.getVersions().stream() diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/drive/DriveServiceResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/drive/DriveServiceResource.java index fa3b7c45231c..58cc9145fec5 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/drive/DriveServiceResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/drive/DriveServiceResource.java @@ -378,8 +378,19 @@ public EntityHistory listVersions( @Context SecurityContext securityContext, @Parameter(description = "Id of the drive service", schema = @Schema(type = "UUID")) @PathParam("id") - UUID id) { - EntityHistory entityHistory = super.listVersionsInternal(securityContext, id); + UUID id, + @Parameter(description = "Limit the number of versions returned") + @QueryParam("limit") + @DefaultValue("0") + @Min(0) + @Max(1000) + int limit, + @Parameter(description = "Offset of the versions to return") + @QueryParam("offset") + @DefaultValue("0") + @Min(0) + int offset) { + EntityHistory entityHistory = super.listVersionsInternal(securityContext, id, limit, offset); List versions = entityHistory.getVersions().stream() .map( diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/ingestionpipelines/IngestionPipelineResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/ingestionpipelines/IngestionPipelineResource.java index ab8680397b28..b33912fc5b96 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/ingestionpipelines/IngestionPipelineResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/ingestionpipelines/IngestionPipelineResource.java @@ -394,8 +394,19 @@ public EntityHistory listVersions( @Context SecurityContext securityContext, @Parameter(description = "Id of the ingestion pipeline", schema = @Schema(type = "UUID")) @PathParam("id") - UUID id) { - return super.listVersionsInternal(securityContext, id); + UUID id, + @Parameter(description = "Limit the number of versions returned") + @QueryParam("limit") + @DefaultValue("0") + @Min(0) + @Max(1000) + int limit, + @Parameter(description = "Offset of the versions to return") + @QueryParam("offset") + @DefaultValue("0") + @Min(0) + int offset) { + return super.listVersionsInternal(securityContext, id, limit, offset); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/llm/LLMServiceResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/llm/LLMServiceResource.java index f2fe8332044e..2cb9ef38fb1e 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/llm/LLMServiceResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/llm/LLMServiceResource.java @@ -288,8 +288,19 @@ public EntityHistory listVersions( @Context SecurityContext securityContext, @Parameter(description = "Id of the LLM service", schema = @Schema(type = "UUID")) @PathParam("id") - UUID id) { - EntityHistory entityHistory = super.listVersionsInternal(securityContext, id); + UUID id, + @Parameter(description = "Limit the number of versions returned") + @QueryParam("limit") + @DefaultValue("0") + @Min(0) + @Max(1000) + int limit, + @Parameter(description = "Offset of the versions to return") + @QueryParam("offset") + @DefaultValue("0") + @Min(0) + int offset) { + EntityHistory entityHistory = super.listVersionsInternal(securityContext, id, limit, offset); List versions = entityHistory.getVersions().stream() diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/messaging/MessagingServiceResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/messaging/MessagingServiceResource.java index 5ac5acc91193..c428d564f5ed 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/messaging/MessagingServiceResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/messaging/MessagingServiceResource.java @@ -349,8 +349,19 @@ public EntityHistory listVersions( @Context SecurityContext securityContext, @Parameter(description = "Id of the messaging service", schema = @Schema(type = "UUID")) @PathParam("id") - UUID id) { - EntityHistory entityHistory = super.listVersionsInternal(securityContext, id); + UUID id, + @Parameter(description = "Limit the number of versions returned") + @QueryParam("limit") + @DefaultValue("0") + @Min(0) + @Max(1000) + int limit, + @Parameter(description = "Offset of the versions to return") + @QueryParam("offset") + @DefaultValue("0") + @Min(0) + int offset) { + EntityHistory entityHistory = super.listVersionsInternal(securityContext, id, limit, offset); List versions = entityHistory.getVersions().stream() diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/metadata/MetadataServiceResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/metadata/MetadataServiceResource.java index 9cab5010d6d8..6246ed029817 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/metadata/MetadataServiceResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/metadata/MetadataServiceResource.java @@ -391,8 +391,19 @@ public EntityHistory listVersions( @Context SecurityContext securityContext, @Parameter(description = "Id of the metadata service", schema = @Schema(type = "UUID")) @PathParam("id") - UUID id) { - EntityHistory entityHistory = super.listVersionsInternal(securityContext, id); + UUID id, + @Parameter(description = "Limit the number of versions returned") + @QueryParam("limit") + @DefaultValue("0") + @Min(0) + @Max(1000) + int limit, + @Parameter(description = "Offset of the versions to return") + @QueryParam("offset") + @DefaultValue("0") + @Min(0) + int offset) { + EntityHistory entityHistory = super.listVersionsInternal(securityContext, id, limit, offset); List versions = entityHistory.getVersions().stream() diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/mlmodel/MlModelServiceResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/mlmodel/MlModelServiceResource.java index 1568140a2bf4..a67cb9da6212 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/mlmodel/MlModelServiceResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/mlmodel/MlModelServiceResource.java @@ -363,8 +363,19 @@ public EntityHistory listVersions( @Context SecurityContext securityContext, @Parameter(description = "Id of the ML Model service", schema = @Schema(type = "UUID")) @PathParam("id") - UUID id) { - EntityHistory entityHistory = super.listVersionsInternal(securityContext, id); + UUID id, + @Parameter(description = "Limit the number of versions returned") + @QueryParam("limit") + @DefaultValue("0") + @Min(0) + @Max(1000) + int limit, + @Parameter(description = "Offset of the versions to return") + @QueryParam("offset") + @DefaultValue("0") + @Min(0) + int offset) { + EntityHistory entityHistory = super.listVersionsInternal(securityContext, id, limit, offset); List versions = entityHistory.getVersions().stream() diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/pipeline/PipelineServiceResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/pipeline/PipelineServiceResource.java index c8d79e38a3b5..7cc957675e18 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/pipeline/PipelineServiceResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/pipeline/PipelineServiceResource.java @@ -363,8 +363,19 @@ public EntityHistory listVersions( @Context SecurityContext securityContext, @Parameter(description = "Id of the pipeline service", schema = @Schema(type = "UUID")) @PathParam("id") - UUID id) { - EntityHistory entityHistory = super.listVersionsInternal(securityContext, id); + UUID id, + @Parameter(description = "Limit the number of versions returned") + @QueryParam("limit") + @DefaultValue("0") + @Min(0) + @Max(1000) + int limit, + @Parameter(description = "Offset of the versions to return") + @QueryParam("offset") + @DefaultValue("0") + @Min(0) + int offset) { + EntityHistory entityHistory = super.listVersionsInternal(securityContext, id, limit, offset); List versions = entityHistory.getVersions().stream() diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/searchIndexes/SearchServiceResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/searchIndexes/SearchServiceResource.java index 7d3b9adb9b6f..363dadf92613 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/searchIndexes/SearchServiceResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/searchIndexes/SearchServiceResource.java @@ -353,8 +353,19 @@ public EntityHistory listVersions( @Context SecurityContext securityContext, @Parameter(description = "search service Id", schema = @Schema(type = "string")) @PathParam("id") - UUID id) { - EntityHistory entityHistory = super.listVersionsInternal(securityContext, id); + UUID id, + @Parameter(description = "Limit the number of versions returned") + @QueryParam("limit") + @DefaultValue("0") + @Min(0) + @Max(1000) + int limit, + @Parameter(description = "Offset of the versions to return") + @QueryParam("offset") + @DefaultValue("0") + @Min(0) + int offset) { + EntityHistory entityHistory = super.listVersionsInternal(securityContext, id, limit, offset); List versions = entityHistory.getVersions().stream() diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/security/SecurityServiceResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/security/SecurityServiceResource.java index 2782b0cb20fe..bffebcb35e45 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/security/SecurityServiceResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/security/SecurityServiceResource.java @@ -299,8 +299,19 @@ public EntityHistory listVersions( @Context SecurityContext securityContext, @Parameter(description = "Id of the security service", schema = @Schema(type = "UUID")) @PathParam("id") - UUID id) { - EntityHistory entityHistory = super.listVersionsInternal(securityContext, id); + UUID id, + @Parameter(description = "Limit the number of versions returned") + @QueryParam("limit") + @DefaultValue("0") + @Min(0) + @Max(1000) + int limit, + @Parameter(description = "Offset of the versions to return") + @QueryParam("offset") + @DefaultValue("0") + @Min(0) + int offset) { + EntityHistory entityHistory = super.listVersionsInternal(securityContext, id, limit, offset); List versions = entityHistory.getVersions().stream() diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/storage/StorageServiceResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/storage/StorageServiceResource.java index a9e24a8bdfc5..289dbe03e7a4 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/storage/StorageServiceResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/storage/StorageServiceResource.java @@ -347,8 +347,19 @@ public EntityHistory listVersions( @Context SecurityContext securityContext, @Parameter(description = "storage service Id", schema = @Schema(type = "string")) @PathParam("id") - UUID id) { - EntityHistory entityHistory = super.listVersionsInternal(securityContext, id); + UUID id, + @Parameter(description = "Limit the number of versions returned") + @QueryParam("limit") + @DefaultValue("0") + @Min(0) + @Max(1000) + int limit, + @Parameter(description = "Offset of the versions to return") + @QueryParam("offset") + @DefaultValue("0") + @Min(0) + int offset) { + EntityHistory entityHistory = super.listVersionsInternal(securityContext, id, limit, offset); List versions = entityHistory.getVersions().stream() diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/storages/ContainerResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/storages/ContainerResource.java index a2e65b5beb10..a276109689c7 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/storages/ContainerResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/storages/ContainerResource.java @@ -468,8 +468,19 @@ public EntityHistory listVersions( @Context UriInfo uriInfo, @Context SecurityContext securityContext, @Parameter(description = "Container Id", schema = @Schema(type = "string")) @PathParam("id") - UUID id) { - return super.listVersionsInternal(securityContext, id); + UUID id, + @Parameter(description = "Limit the number of versions returned") + @QueryParam("limit") + @DefaultValue("0") + @Min(0) + @Max(1000) + int limit, + @Parameter(description = "Offset of the versions to return") + @QueryParam("offset") + @DefaultValue("0") + @Min(0) + int offset) { + return super.listVersionsInternal(securityContext, id, limit, offset); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/tags/ClassificationResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/tags/ClassificationResource.java index 89ebc8ab3f30..0326d012fb50 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/tags/ClassificationResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/tags/ClassificationResource.java @@ -255,8 +255,19 @@ public EntityHistory listVersions( @Context SecurityContext securityContext, @Parameter(description = "Id of the classification", schema = @Schema(type = "UUID")) @PathParam("id") - UUID id) { - return super.listVersionsInternal(securityContext, id); + UUID id, + @Parameter(description = "Limit the number of versions returned") + @QueryParam("limit") + @DefaultValue("0") + @Min(0) + @Max(1000) + int limit, + @Parameter(description = "Offset of the versions to return") + @QueryParam("offset") + @DefaultValue("0") + @Min(0) + int offset) { + return super.listVersionsInternal(securityContext, id, limit, offset); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/tags/TagResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/tags/TagResource.java index 00f943464369..919ed112219f 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/tags/TagResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/tags/TagResource.java @@ -329,8 +329,19 @@ public EntityHistory listVersions( @Context UriInfo uriInfo, @Context SecurityContext securityContext, @Parameter(description = "Id of the tag", schema = @Schema(type = "UUID")) @PathParam("id") - UUID id) { - return super.listVersionsInternal(securityContext, id); + UUID id, + @Parameter(description = "Limit the number of versions returned") + @QueryParam("limit") + @DefaultValue("0") + @Min(0) + @Max(1000) + int limit, + @Parameter(description = "Offset of the versions to return") + @QueryParam("offset") + @DefaultValue("0") + @Min(0) + int offset) { + return super.listVersionsInternal(securityContext, id, limit, offset); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/teams/PersonaResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/teams/PersonaResource.java index 3c9b4cd8a2c6..2a089575b3fa 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/teams/PersonaResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/teams/PersonaResource.java @@ -151,8 +151,19 @@ public EntityHistory listVersions( @Context SecurityContext securityContext, @Parameter(description = "Id of the Persona", schema = @Schema(type = "UUID")) @PathParam("id") - UUID id) { - return super.listVersionsInternal(securityContext, id); + UUID id, + @Parameter(description = "Limit the number of versions returned") + @QueryParam("limit") + @DefaultValue("0") + @Min(0) + @Max(1000) + int limit, + @Parameter(description = "Offset of the versions to return") + @QueryParam("offset") + @DefaultValue("0") + @Min(0) + int offset) { + return super.listVersionsInternal(securityContext, id, limit, offset); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/teams/RoleResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/teams/RoleResource.java index 5f9dde00c015..e5dd507c200d 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/teams/RoleResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/teams/RoleResource.java @@ -207,8 +207,19 @@ public EntityHistory listVersions( @Context UriInfo uriInfo, @Context SecurityContext securityContext, @Parameter(description = "Id of the role", schema = @Schema(type = "UUID")) @PathParam("id") - UUID id) { - return super.listVersionsInternal(securityContext, id); + UUID id, + @Parameter(description = "Limit the number of versions returned") + @QueryParam("limit") + @DefaultValue("0") + @Min(0) + @Max(1000) + int limit, + @Parameter(description = "Offset of the versions to return") + @QueryParam("offset") + @DefaultValue("0") + @Min(0) + int offset) { + return super.listVersionsInternal(securityContext, id, limit, offset); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/teams/TeamResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/teams/TeamResource.java index 53e75e02932d..2a4983c27b78 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/teams/TeamResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/teams/TeamResource.java @@ -252,8 +252,19 @@ public EntityHistory listVersions( @Context UriInfo uriInfo, @Context SecurityContext securityContext, @Parameter(description = "Id of the team", schema = @Schema(type = "UUID")) @PathParam("id") - UUID id) { - return super.listVersionsInternal(securityContext, id); + UUID id, + @Parameter(description = "Limit the number of versions returned") + @QueryParam("limit") + @DefaultValue("0") + @Min(0) + @Max(1000) + int limit, + @Parameter(description = "Offset of the versions to return") + @QueryParam("offset") + @DefaultValue("0") + @Min(0) + int offset) { + return super.listVersionsInternal(securityContext, id, limit, offset); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/teams/UserResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/teams/UserResource.java index 4e2733057ca8..d7e685d45da7 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/teams/UserResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/teams/UserResource.java @@ -396,8 +396,19 @@ public EntityHistory listVersions( @Context UriInfo uriInfo, @Context SecurityContext securityContext, @Parameter(description = "Id of the user", schema = @Schema(type = "UUID")) @PathParam("id") - UUID id) { - return super.listVersionsInternal(securityContext, id); + UUID id, + @Parameter(description = "Limit the number of versions returned") + @QueryParam("limit") + @DefaultValue("0") + @Min(0) + @Max(1000) + int limit, + @Parameter(description = "Offset of the versions to return") + @QueryParam("offset") + @DefaultValue("0") + @Min(0) + int offset) { + return super.listVersionsInternal(securityContext, id, limit, offset); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/topics/TopicResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/topics/TopicResource.java index 6b092a86f0c8..e8a2a05e02e1 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/topics/TopicResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/topics/TopicResource.java @@ -177,8 +177,19 @@ public EntityHistory listVersions( @Context UriInfo uriInfo, @Context SecurityContext securityContext, @Parameter(description = "Id of the topic", schema = @Schema(type = "UUID")) @PathParam("id") - UUID id) { - return super.listVersionsInternal(securityContext, id); + UUID id, + @Parameter(description = "Limit the number of versions returned") + @QueryParam("limit") + @DefaultValue("0") + @Min(0) + @Max(1000) + int limit, + @Parameter(description = "Offset of the versions to return") + @QueryParam("offset") + @DefaultValue("0") + @Min(0) + int offset) { + return super.listVersionsInternal(securityContext, id, limit, offset); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/types/TypeResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/types/TypeResource.java index 7d95ad8e90c6..fffa938c9d83 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/types/TypeResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/types/TypeResource.java @@ -303,8 +303,19 @@ public EntityHistory listVersions( @Context UriInfo uriInfo, @Context SecurityContext securityContext, @Parameter(description = "Id of the type", schema = @Schema(type = "UUID")) @PathParam("id") - UUID id) { - return super.listVersionsInternal(securityContext, id); + UUID id, + @Parameter(description = "Limit the number of versions returned") + @QueryParam("limit") + @DefaultValue("0") + @Min(0) + @Max(1000) + int limit, + @Parameter(description = "Offset of the versions to return") + @QueryParam("offset") + @DefaultValue("0") + @Min(0) + int offset) { + return super.listVersionsInternal(securityContext, id, limit, offset); } @GET diff --git a/openmetadata-spec/src/main/resources/json/schema/type/entityHistory.json b/openmetadata-spec/src/main/resources/json/schema/type/entityHistory.json index f3fa910d3fc5..54987ff6a90f 100644 --- a/openmetadata-spec/src/main/resources/json/schema/type/entityHistory.json +++ b/openmetadata-spec/src/main/resources/json/schema/type/entityHistory.json @@ -111,6 +111,9 @@ "versions": { "descriptions": "All the versions of the entity ordered from the latest to the oldest version. Note the array element object has schema that corresponds to schema of the entity from the `entityType` attribute. For example, if `entityType` is `table`, then the schema of the object in the array is `table.json`.", "type": "array" + }, + "paging": { + "$ref": "paging.json" } }, "required": ["entityType", "versions"], diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/VersionPages/PaginatedVersionHistory.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/VersionPages/PaginatedVersionHistory.spec.ts new file mode 100644 index 000000000000..0ead6156a413 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/VersionPages/PaginatedVersionHistory.spec.ts @@ -0,0 +1,132 @@ +/* + * Copyright 2026 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. + */ +import { expect, test } from '@playwright/test'; +import { TableClass } from '../../support/entity/TableClass'; +import { UserClass } from '../../support/user/UserClass'; +import { performAdminLogin } from '../../utils/admin'; +import { redirectToHomePage } from '../../utils/common'; + +test.describe('Paginated Version History', () => { + const table = new TableClass(); + const user2 = new UserClass(); + + test.beforeAll('Setup entity with versions via alternating users', async ({ + browser, + }) => { + test.setTimeout(120_000); + const { apiContext: adminApi, afterAction } = + await performAdminLogin(browser); + + await user2.create(adminApi); + await user2.setAdminRole(adminApi); + const user2Page = await browser.newPage(); + await user2.login(user2Page); + const user2Token = await user2Page.evaluate(() => + localStorage.getItem('oidcIdToken') + ); + const user2Api = await browser.newContext().then((ctx) => + ctx.request.newContext({ + baseURL: process.env.PLAYWRIGHT_BASE_URL ?? 'http://localhost:8585', + extraHTTPHeaders: { + Authorization: `Bearer ${user2Token}`, + }, + }) + ); + await user2Page.close(); + + await table.create(adminApi); + + const fqn = table.entityResponseData?.fullyQualifiedName; + + await adminApi.patch(`/api/v1/tables/name/${fqn}`, { + data: [ + { + op: 'add', + path: '/description', + value: 'Admin patch 1', + }, + ], + headers: { 'Content-Type': 'application/json-patch+json' }, + }); + + await user2Api.patch(`/api/v1/tables/name/${fqn}`, { + data: [ + { + op: 'replace', + path: '/description', + value: 'User2 patch 2', + }, + ], + headers: { 'Content-Type': 'application/json-patch+json' }, + }); + + await adminApi.patch(`/api/v1/tables/name/${fqn}`, { + data: [ + { + op: 'replace', + path: '/description', + value: 'Admin patch 3', + }, + ], + headers: { 'Content-Type': 'application/json-patch+json' }, + }); + + await user2Api.dispose(); + await afterAction(); + }); + + test.afterAll('Cleanup', async ({ browser }) => { + const { apiContext, afterAction } = await performAdminLogin(browser); + await table.delete(apiContext); + await user2.delete(apiContext); + await afterAction(); + }); + + test.use({ storageState: 'playwright/.auth/admin.json' }); + + test('should call versions API with pagination params and return paging metadata', async ({ + page, + }) => { + test.slow(); + + await redirectToHomePage(page); + await table.visitEntityPage(page); + await page.waitForLoadState('networkidle'); + + const versionsApiCall = page.waitForResponse( + (response) => + response.url().includes('/versions') && + response.url().includes('limit=') && + response.status() === 200 + ); + + await page.locator('[data-testid="version-button"]').click(); + const response = await versionsApiCall; + const responseBody = await response.json(); + + expect(responseBody.paging).toBeDefined(); + expect(responseBody.paging.total).toBeGreaterThanOrEqual(3); + expect(responseBody.paging.limit).toBe(20); + expect(responseBody.paging.offset).toBe(0); + + const versionSelectors = page.locator( + '[data-testid^="version-selector-v"]' + ); + + await expect(versionSelectors.first()).toBeVisible(); + + const count = await versionSelectors.count(); + + expect(count).toBeGreaterThanOrEqual(3); + }); +}); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/APIEndpoint/APIEndpointVersion/APIEndpointVersion.interface.ts b/openmetadata-ui/src/main/resources/ui/src/components/APIEndpoint/APIEndpointVersion/APIEndpointVersion.interface.ts index c934baddbb6f..1eabd30c4228 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/APIEndpoint/APIEndpointVersion/APIEndpointVersion.interface.ts +++ b/openmetadata-ui/src/main/resources/ui/src/components/APIEndpoint/APIEndpointVersion/APIEndpointVersion.interface.ts @@ -26,6 +26,9 @@ export interface APIEndpointVersionProp tier: TagLabel; slashedApiEndpointName: TitleBreadcrumbProps['titleLinks']; versionList: EntityHistory; + onLoadMore?: () => void; + hasMore?: boolean; + isLoadingMore?: boolean; backHandler: () => void; versionHandler: (v: string) => void; entityPermissions: OperationPermission; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/APIEndpoint/APIEndpointVersion/APIEndpointVersion.tsx b/openmetadata-ui/src/main/resources/ui/src/components/APIEndpoint/APIEndpointVersion/APIEndpointVersion.tsx index a83c59e4367b..3273724ef031 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/APIEndpoint/APIEndpointVersion/APIEndpointVersion.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/APIEndpoint/APIEndpointVersion/APIEndpointVersion.tsx @@ -49,6 +49,9 @@ const APIEndpointVersion: FC = ({ tier, slashedApiEndpointName, versionList, + onLoadMore, + hasMore, + isLoadingMore, backHandler, versionHandler, entityPermissions, @@ -141,7 +144,8 @@ const APIEndpointVersion: FC = ({ + flex="220px" + > = ({ data={currentVersionData} permissions={entityPermissions} type={EntityType.API_ENDPOINT} - onUpdate={() => Promise.resolve()}> + onUpdate={() => Promise.resolve()} + > = ({ ); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Chart/ChartVersion/ChartVersion.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Chart/ChartVersion/ChartVersion.component.tsx index b17639b02cd8..c0f127508da4 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Chart/ChartVersion/ChartVersion.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Chart/ChartVersion/ChartVersion.component.tsx @@ -51,6 +51,9 @@ export interface ChartVersionProp { tier: TagLabel; slashedChartName: string[]; versionList: EntityHistory; + onLoadMore?: () => void; + hasMore?: boolean; + isLoadingMore?: boolean; deleted?: boolean; backHandler: () => void; versionHandler: (v: string) => void; @@ -67,6 +70,9 @@ const ChartVersion: FC = ({ tier, slashedChartName, versionList, + onLoadMore, + hasMore, + isLoadingMore, deleted = false, backHandler, versionHandler, @@ -158,7 +164,8 @@ const ChartVersion: FC = ({ + flex="220px" + > = ({ return ( <>
+ className={`version-data ${deleted ? 'version-data--deleted' : ''}`} + > = ({ data={currentVersionData} permissions={entityPermissions} type={EntityType.CHART} - onUpdate={() => Promise.resolve()}> + onUpdate={() => Promise.resolve()} + > = ({ ); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Container/ContainerVersion/ContainerVersion.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Container/ContainerVersion/ContainerVersion.component.tsx index 6f6d7205a327..19dadb2f54cb 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Container/ContainerVersion/ContainerVersion.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Container/ContainerVersion/ContainerVersion.component.tsx @@ -61,6 +61,9 @@ const ContainerVersion: React.FC = ({ tier, breadCrumbList, versionList, + onLoadMore, + hasMore, + isLoadingMore, deleted = false, backHandler, versionHandler, @@ -188,7 +191,8 @@ const ContainerVersion: React.FC = ({ + flex="220px" + > = ({ data={currentVersionData} permissions={entityPermissions} type={EntityType.CONTAINER as CustomizeEntityType} - onUpdate={() => Promise.resolve()}> + onUpdate={() => Promise.resolve()} + > = ({ ); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Container/ContainerVersion/ContainerVersion.interface.ts b/openmetadata-ui/src/main/resources/ui/src/components/Container/ContainerVersion/ContainerVersion.interface.ts index cd96245165d2..24360cdfe1c4 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Container/ContainerVersion/ContainerVersion.interface.ts +++ b/openmetadata-ui/src/main/resources/ui/src/components/Container/ContainerVersion/ContainerVersion.interface.ts @@ -26,6 +26,9 @@ export interface ContainerVersionProp { tier: TagLabel; breadCrumbList: TitleBreadcrumbProps['titleLinks']; versionList: EntityHistory; + onLoadMore?: () => void; + hasMore?: boolean; + isLoadingMore?: boolean; deleted?: boolean; backHandler: () => void; versionHandler: (v: string) => void; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Dashboard/DashboardVersion/DashboardVersion.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Dashboard/DashboardVersion/DashboardVersion.component.tsx index 17c99f86dd55..1ca5c664c5aa 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Dashboard/DashboardVersion/DashboardVersion.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Dashboard/DashboardVersion/DashboardVersion.component.tsx @@ -57,6 +57,9 @@ const DashboardVersion: FC = ({ tier, slashedDashboardName, versionList, + onLoadMore, + hasMore, + isLoadingMore, deleted = false, backHandler, versionHandler, @@ -204,7 +207,8 @@ const DashboardVersion: FC = ({ + flex="220px" + > = ({ data={currentVersionData} permissions={entityPermissions} type={EntityType.DASHBOARD} - onUpdate={() => Promise.resolve()}> + onUpdate={() => Promise.resolve()} + > = ({ ); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Dashboard/DashboardVersion/DashboardVersion.interface.ts b/openmetadata-ui/src/main/resources/ui/src/components/Dashboard/DashboardVersion/DashboardVersion.interface.ts index 937d14b8a966..29cc84c37377 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Dashboard/DashboardVersion/DashboardVersion.interface.ts +++ b/openmetadata-ui/src/main/resources/ui/src/components/Dashboard/DashboardVersion/DashboardVersion.interface.ts @@ -27,6 +27,9 @@ export interface DashboardVersionProp { tier: TagLabel; slashedDashboardName: TitleBreadcrumbProps['titleLinks']; versionList: EntityHistory; + onLoadMore?: () => void; + hasMore?: boolean; + isLoadingMore?: boolean; deleted?: boolean; backHandler: () => void; versionHandler: (v: string) => void; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Dashboard/DataModel/DataModelVersion/DataModelVersion.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Dashboard/DataModel/DataModelVersion/DataModelVersion.component.tsx index bdb2c6e40fa8..b1d17a110865 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Dashboard/DataModel/DataModelVersion/DataModelVersion.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Dashboard/DataModel/DataModelVersion/DataModelVersion.component.tsx @@ -59,6 +59,9 @@ const DataModelVersion: FC = ({ tier, slashedDataModelName, versionList, + onLoadMore, + hasMore, + isLoadingMore, deleted = false, backHandler, versionHandler, @@ -172,7 +175,8 @@ const DataModelVersion: FC = ({ + flex="220px" + > = ({ data={currentVersionData} permissions={entityPermissions} type={EntityType.DASHBOARD_DATA_MODEL} - onUpdate={() => Promise.resolve()}> + onUpdate={() => Promise.resolve()} + > = ({
); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Dashboard/DataModel/DataModelVersion/DataModelVersion.interface.ts b/openmetadata-ui/src/main/resources/ui/src/components/Dashboard/DataModel/DataModelVersion/DataModelVersion.interface.ts index a4bcaad1a5eb..20fff9b9e783 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Dashboard/DataModel/DataModelVersion/DataModelVersion.interface.ts +++ b/openmetadata-ui/src/main/resources/ui/src/components/Dashboard/DataModel/DataModelVersion/DataModelVersion.interface.ts @@ -27,6 +27,9 @@ export interface DataModelVersionProp { tier: TagLabel; slashedDataModelName: TitleBreadcrumbProps['titleLinks']; versionList: EntityHistory; + onLoadMore?: () => void; + hasMore?: boolean; + isLoadingMore?: boolean; deleted?: boolean; backHandler: () => void; versionHandler: (v: string) => void; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Database/StoredProcedureVersion/StoredProcedureVersion.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Database/StoredProcedureVersion/StoredProcedureVersion.component.tsx index 51dcbd7eb120..547ffabaef56 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Database/StoredProcedureVersion/StoredProcedureVersion.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Database/StoredProcedureVersion/StoredProcedureVersion.component.tsx @@ -51,6 +51,9 @@ const StoredProcedureVersion = ({ tier, slashedTableName, versionList, + onLoadMore, + hasMore, + isLoadingMore, deleted = false, backHandler, versionHandler, @@ -137,7 +140,8 @@ const StoredProcedureVersion = ({ + flex="220px" + > Promise.resolve()}> + onUpdate={() => Promise.resolve()} + > ); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Database/StoredProcedureVersion/StoredProcedureVersion.interface.ts b/openmetadata-ui/src/main/resources/ui/src/components/Database/StoredProcedureVersion/StoredProcedureVersion.interface.ts index 790adf8796c4..460f21d8fa9d 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Database/StoredProcedureVersion/StoredProcedureVersion.interface.ts +++ b/openmetadata-ui/src/main/resources/ui/src/components/Database/StoredProcedureVersion/StoredProcedureVersion.interface.ts @@ -27,6 +27,9 @@ export interface StoredProcedureVersionProp { tier: TagLabel; slashedTableName: TitleBreadcrumbProps['titleLinks']; versionList: EntityHistory; + onLoadMore?: () => void; + hasMore?: boolean; + isLoadingMore?: boolean; deleted?: boolean; backHandler: () => void; versionHandler: (v: string) => void; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Database/TableVersion/TableVersion.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Database/TableVersion/TableVersion.component.tsx index 75f1726dade3..74867a3358c6 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Database/TableVersion/TableVersion.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Database/TableVersion/TableVersion.component.tsx @@ -72,6 +72,9 @@ const TableVersion: React.FC = ({ tier, slashedTableName, versionList, + onLoadMore, + hasMore, + isLoadingMore, deleted = false, backHandler, versionHandler, @@ -298,7 +301,8 @@ const TableVersion: React.FC = ({ + flex="220px" + > = ({ data={currentVersionData} permissions={entityPermissions} type={EntityType.TABLE} - onUpdate={() => Promise.resolve()}> + onUpdate={() => Promise.resolve()} + > = ({ ); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Database/TableVersion/TableVersion.interface.ts b/openmetadata-ui/src/main/resources/ui/src/components/Database/TableVersion/TableVersion.interface.ts index 8f45b5c18918..191129aee1c1 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Database/TableVersion/TableVersion.interface.ts +++ b/openmetadata-ui/src/main/resources/ui/src/components/Database/TableVersion/TableVersion.interface.ts @@ -27,6 +27,9 @@ export interface TableVersionProp { tier: TagLabel; slashedTableName: TitleBreadcrumbProps['titleLinks']; versionList: EntityHistory; + onLoadMore?: () => void; + hasMore?: boolean; + isLoadingMore?: boolean; deleted?: boolean; backHandler: () => void; versionHandler: (v: string) => void; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DriveService/Directory/DirectoryVersion/DirectoryVersion.interface.ts b/openmetadata-ui/src/main/resources/ui/src/components/DriveService/Directory/DirectoryVersion/DirectoryVersion.interface.ts index 3b5e2e825b3e..1cfd27274aa3 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/DriveService/Directory/DirectoryVersion/DirectoryVersion.interface.ts +++ b/openmetadata-ui/src/main/resources/ui/src/components/DriveService/Directory/DirectoryVersion/DirectoryVersion.interface.ts @@ -26,6 +26,9 @@ export interface DirectoryVersionProps { tier: TagLabel; breadCrumbList: TitleBreadcrumbProps['titleLinks']; versionList: EntityHistory; + onLoadMore?: () => void; + hasMore?: boolean; + isLoadingMore?: boolean; deleted?: boolean; backHandler: () => void; versionHandler: (v: string) => void; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DriveService/Directory/DirectoryVersion/DirectoryVersion.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DriveService/Directory/DirectoryVersion/DirectoryVersion.tsx index e64826de0090..b2ae778de93b 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/DriveService/Directory/DirectoryVersion/DirectoryVersion.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/DriveService/Directory/DirectoryVersion/DirectoryVersion.tsx @@ -65,6 +65,9 @@ const DirectoryVersion = ({ tier, breadCrumbList, versionList, + onLoadMore, + hasMore, + isLoadingMore, deleted = false, backHandler, versionHandler, @@ -195,7 +198,8 @@ const DirectoryVersion = ({ + flex="220px" + > Promise.resolve()}> + onUpdate={() => Promise.resolve()} + > ); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DriveService/File/FileVersion/FileVersion.interface.ts b/openmetadata-ui/src/main/resources/ui/src/components/DriveService/File/FileVersion/FileVersion.interface.ts index cb6ad433bb1e..38545db09334 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/DriveService/File/FileVersion/FileVersion.interface.ts +++ b/openmetadata-ui/src/main/resources/ui/src/components/DriveService/File/FileVersion/FileVersion.interface.ts @@ -26,6 +26,9 @@ export interface FileVersionProps { tier: TagLabel; breadCrumbList: TitleBreadcrumbProps['titleLinks']; versionList: EntityHistory; + onLoadMore?: () => void; + hasMore?: boolean; + isLoadingMore?: boolean; deleted?: boolean; backHandler: () => void; versionHandler: (v: string) => void; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DriveService/File/FileVersion/FileVersion.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DriveService/File/FileVersion/FileVersion.tsx index 8767bba821cf..7734b60b4c58 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/DriveService/File/FileVersion/FileVersion.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/DriveService/File/FileVersion/FileVersion.tsx @@ -53,6 +53,9 @@ const FileVersion = ({ tier, breadCrumbList, versionList, + onLoadMore, + hasMore, + isLoadingMore, deleted = false, backHandler, versionHandler, @@ -150,7 +153,8 @@ const FileVersion = ({ + flex="220px" + > Promise.resolve()}> + onUpdate={() => Promise.resolve()} + > ); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DriveService/Spreadsheet/SpreadsheetVersion/SpreadsheetVersion.interface.ts b/openmetadata-ui/src/main/resources/ui/src/components/DriveService/Spreadsheet/SpreadsheetVersion/SpreadsheetVersion.interface.ts index 69da73f88b32..c917d7a10e1c 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/DriveService/Spreadsheet/SpreadsheetVersion/SpreadsheetVersion.interface.ts +++ b/openmetadata-ui/src/main/resources/ui/src/components/DriveService/Spreadsheet/SpreadsheetVersion/SpreadsheetVersion.interface.ts @@ -26,6 +26,9 @@ export interface SpreadsheetVersionProps { tier: TagLabel; breadCrumbList: TitleBreadcrumbProps['titleLinks']; versionList: EntityHistory; + onLoadMore?: () => void; + hasMore?: boolean; + isLoadingMore?: boolean; deleted?: boolean; backHandler: () => void; versionHandler: (v: string) => void; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DriveService/Spreadsheet/SpreadsheetVersion/SpreadsheetVersion.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DriveService/Spreadsheet/SpreadsheetVersion/SpreadsheetVersion.tsx index 4cc904f87739..3cbcd6317a73 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/DriveService/Spreadsheet/SpreadsheetVersion/SpreadsheetVersion.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/DriveService/Spreadsheet/SpreadsheetVersion/SpreadsheetVersion.tsx @@ -65,6 +65,9 @@ const SpreadsheetVersion = ({ tier, breadCrumbList, versionList, + onLoadMore, + hasMore, + isLoadingMore, deleted = false, backHandler, versionHandler, @@ -190,7 +193,8 @@ const SpreadsheetVersion = ({ + flex="220px" + > Promise.resolve()}> + onUpdate={() => Promise.resolve()} + > ); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DriveService/Worksheet/WorksheetVersion/WorksheetVersion.interface.ts b/openmetadata-ui/src/main/resources/ui/src/components/DriveService/Worksheet/WorksheetVersion/WorksheetVersion.interface.ts index f449d7f62f4c..1d4a79a9ace7 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/DriveService/Worksheet/WorksheetVersion/WorksheetVersion.interface.ts +++ b/openmetadata-ui/src/main/resources/ui/src/components/DriveService/Worksheet/WorksheetVersion/WorksheetVersion.interface.ts @@ -26,6 +26,9 @@ export interface WorksheetVersionProps { tier: TagLabel; breadCrumbList: TitleBreadcrumbProps['titleLinks']; versionList: EntityHistory; + onLoadMore?: () => void; + hasMore?: boolean; + isLoadingMore?: boolean; deleted?: boolean; backHandler: () => void; versionHandler: (v: string) => void; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DriveService/Worksheet/WorksheetVersion/WorksheetVersion.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DriveService/Worksheet/WorksheetVersion/WorksheetVersion.tsx index 9e2e2cf4ce97..0fa5c9a7e420 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/DriveService/Worksheet/WorksheetVersion/WorksheetVersion.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/DriveService/Worksheet/WorksheetVersion/WorksheetVersion.tsx @@ -61,6 +61,9 @@ const WorksheetVersion = ({ tier, breadCrumbList, versionList, + onLoadMore, + hasMore, + isLoadingMore, deleted = false, backHandler, versionHandler, @@ -183,7 +186,8 @@ const WorksheetVersion = ({ + flex="220px" + > Promise.resolve()}> + onUpdate={() => Promise.resolve()} + > ); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityVersionTimeLine/EntityVersionTimeLine.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityVersionTimeLine/EntityVersionTimeLine.tsx index eb74e2a1c924..a1a2250c849e 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityVersionTimeLine/EntityVersionTimeLine.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityVersionTimeLine/EntityVersionTimeLine.tsx @@ -13,11 +13,12 @@ import { Button, Col, Divider, Drawer, Row, Tooltip, Typography } from 'antd'; import classNames from 'classnames'; import { isEmpty, toString } from 'lodash'; -import { forwardRef, useEffect, useMemo } from 'react'; +import { forwardRef, useCallback, useEffect, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { Link } from 'react-router-dom'; import { useLimitStore } from '../../../context/LimitsProvider/useLimitsStore'; import { EntityHistory } from '../../../generated/type/entityHistory'; +import { useElementInView } from '../../../hooks/useElementInView'; import { useUserProfile } from '../../../hooks/user-profile/useUserProfile'; import { formatDateTime } from '../../../utils/date-time/DateTimeUtils'; import { getEntityName } from '../../../utils/EntityUtils'; @@ -26,6 +27,7 @@ import { renderVersionButton, } from '../../../utils/EntityVersionUtils'; import { getUserPath } from '../../../utils/RouterUtils'; +import Loader from '../../common/Loader/Loader'; import UserPopOverCard from '../../common/PopOverCard/UserPopOverCard'; import CloseIcon from '../../Modals/CloseIcon.component'; import './entity-version-timeline.less'; @@ -61,7 +63,8 @@ export const VersionButton = forwardRef< className )} ref={ref} - onClick={() => onVersionSelect(toString(versionNumber))}> + onClick={() => onVersionSelect(toString(versionNumber))} + >
+ })} + > {versionText} {isMajorVersion ? ( + style={{ backgroundColor: '#EEEAF8' }} + > {t('label.major')} ) : null} @@ -94,7 +99,8 @@ export const VersionButton = forwardRef<
+ })} + > {getSummary({ changeDescription: changeDescription, isGlossaryTerm: !isEmpty(glossary), @@ -104,7 +110,8 @@ export const VersionButton = forwardRef< + userName={updatedBy} + > {getEntityName(user)} @@ -124,11 +131,28 @@ const EntityVersionTimeLine: React.FC = ({ versionHandler, onBack, entityType, + onLoadMore, + hasMore = false, + isLoadingMore = false, }) => { const { t } = useTranslation(); const { resourceLimit, getResourceLimit } = useLimitStore(); + const [sentinelRef, isInView] = useElementInView({ + threshold: 0.1, + }); + + const handleLoadMore = useCallback(() => { + if (isInView && hasMore && !isLoadingMore && onLoadMore) { + onLoadMore(); + } + }, [isInView, hasMore, isLoadingMore, onLoadMore]); + + useEffect(() => { + handleLoadMore(); + }, [handleLoadMore]); + useEffect(() => { entityType && getResourceLimit(entityType); }, [entityType]); @@ -181,15 +205,18 @@ const EntityVersionTimeLine: React.FC = ({ block className="m-t-lg" href="/settings/billing/plans" - type="primary"> + type="primary" + > See Upgrade Options
) : null} +
} /> + {isLoadingMore ? : null}
); - }, [versionList, currentVersion, versionHandler]); + }, [versionList, currentVersion, versionHandler, isLoadingMore, sentinelRef]); return ( = ({ } - width={330}> + width={330} + > {versions} ); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityVersionTimeLine/EntityVersionTimeline.interface.ts b/openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityVersionTimeLine/EntityVersionTimeline.interface.ts index 6f52f7cf8ed5..057ce91254db 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityVersionTimeLine/EntityVersionTimeline.interface.ts +++ b/openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityVersionTimeLine/EntityVersionTimeline.interface.ts @@ -20,6 +20,9 @@ export type EntityVersionTimelineProps = { versionHandler: (v: string) => void; onBack: () => void; entityType?: EntityType; + onLoadMore?: () => void; + hasMore?: boolean; + isLoadingMore?: boolean; }; export type EntityVersionButtonProps = { diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Metric/MetricVersion/MetricVersion.interface.ts b/openmetadata-ui/src/main/resources/ui/src/components/Metric/MetricVersion/MetricVersion.interface.ts index 74d95937517a..87de2969de6c 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Metric/MetricVersion/MetricVersion.interface.ts +++ b/openmetadata-ui/src/main/resources/ui/src/components/Metric/MetricVersion/MetricVersion.interface.ts @@ -25,6 +25,9 @@ export interface MetricVersionProp { tier: TagLabel; slashedMetricName: TitleBreadcrumbProps['titleLinks']; versionList: EntityHistory; + onLoadMore?: () => void; + hasMore?: boolean; + isLoadingMore?: boolean; backHandler: () => void; versionHandler: (v: string) => void; entityPermissions: OperationPermission; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Metric/MetricVersion/MetricVersion.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Metric/MetricVersion/MetricVersion.tsx index 4d6ed141147c..4870e8caf34a 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Metric/MetricVersion/MetricVersion.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Metric/MetricVersion/MetricVersion.tsx @@ -49,6 +49,9 @@ const MetricVersion: FC = ({ tier, slashedMetricName, versionList, + onLoadMore, + hasMore, + isLoadingMore, backHandler, versionHandler, entityPermissions, @@ -145,7 +148,8 @@ const MetricVersion: FC = ({ + flex="220px" + > = ({ data={currentVersionData} permissions={entityPermissions} type={EntityType.METRIC as CustomizeEntityType} - onUpdate={() => Promise.resolve()}> + onUpdate={() => Promise.resolve()} + > = ({ ); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/MlModel/MlModelVersion/MlModelVersion.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/MlModel/MlModelVersion/MlModelVersion.component.tsx index dc1852011c52..fa959d54e67c 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/MlModel/MlModelVersion/MlModelVersion.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/MlModel/MlModelVersion/MlModelVersion.component.tsx @@ -66,6 +66,9 @@ const MlModelVersion: FC = ({ tier, slashedMlModelName, versionList, + onLoadMore, + hasMore, + isLoadingMore, deleted = false, backHandler, versionHandler, @@ -184,7 +187,8 @@ const MlModelVersion: FC = ({ bordered className="m-b-xlg" data-testid={`feature-card-${feature.name ?? ''}`} - key={feature.fullyQualifiedName}> + key={feature.fullyQualifiedName} + > @@ -297,7 +301,8 @@ const MlModelVersion: FC = ({ + flex="220px" + > = ({ data={currentVersionData} permissions={entityPermissions} type={EntityType.MLMODEL} - onUpdate={() => Promise.resolve()}> + onUpdate={() => Promise.resolve()} + > = ({ ); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/MlModel/MlModelVersion/MlModelVersion.interface.tsx b/openmetadata-ui/src/main/resources/ui/src/components/MlModel/MlModelVersion/MlModelVersion.interface.tsx index 3981d7f2b838..8673634ef38f 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/MlModel/MlModelVersion/MlModelVersion.interface.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/MlModel/MlModelVersion/MlModelVersion.interface.tsx @@ -27,6 +27,9 @@ export interface MlModelVersionProp { tier: TagLabel; slashedMlModelName: TitleBreadcrumbProps['titleLinks']; versionList: EntityHistory; + onLoadMore?: () => void; + hasMore?: boolean; + isLoadingMore?: boolean; deleted?: boolean; backHandler: () => void; versionHandler: (v: string) => void; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Pipeline/PipelineVersion/PipelineVersion.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Pipeline/PipelineVersion/PipelineVersion.component.tsx index 9030acf92ea0..98618470b9f3 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Pipeline/PipelineVersion/PipelineVersion.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Pipeline/PipelineVersion/PipelineVersion.component.tsx @@ -61,6 +61,9 @@ const PipelineVersion: FC = ({ tier, slashedPipelineName, versionList, + onLoadMore, + hasMore, + isLoadingMore, deleted = false, backHandler, versionHandler, @@ -214,7 +217,8 @@ const PipelineVersion: FC = ({ + flex="220px" + > = ({ data={currentVersionData} permissions={entityPermissions} type={EntityType.PIPELINE} - onUpdate={() => Promise.resolve()}> + onUpdate={() => Promise.resolve()} + > = ({ ); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Pipeline/PipelineVersion/PipelineVersion.interface.ts b/openmetadata-ui/src/main/resources/ui/src/components/Pipeline/PipelineVersion/PipelineVersion.interface.ts index 1a8698d79939..c99cc2f80210 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Pipeline/PipelineVersion/PipelineVersion.interface.ts +++ b/openmetadata-ui/src/main/resources/ui/src/components/Pipeline/PipelineVersion/PipelineVersion.interface.ts @@ -27,6 +27,9 @@ export interface PipelineVersionProp { tier: TagLabel; slashedPipelineName: TitleBreadcrumbProps['titleLinks']; versionList: EntityHistory; + onLoadMore?: () => void; + hasMore?: boolean; + isLoadingMore?: boolean; deleted?: boolean; backHandler: () => void; versionHandler: (v: string) => void; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/SearchIndexVersion/SearchIndexVersion.interface.ts b/openmetadata-ui/src/main/resources/ui/src/components/SearchIndexVersion/SearchIndexVersion.interface.ts index ffd62dbe2115..b41cef4a56fe 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/SearchIndexVersion/SearchIndexVersion.interface.ts +++ b/openmetadata-ui/src/main/resources/ui/src/components/SearchIndexVersion/SearchIndexVersion.interface.ts @@ -26,6 +26,9 @@ export interface SearchIndexVersionProps { tier: TagLabel; breadCrumbList: TitleBreadcrumbProps['titleLinks']; versionList: EntityHistory; + onLoadMore?: () => void; + hasMore?: boolean; + isLoadingMore?: boolean; deleted?: boolean; backHandler: () => void; versionHandler: (v: string) => void; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/SearchIndexVersion/SearchIndexVersion.tsx b/openmetadata-ui/src/main/resources/ui/src/components/SearchIndexVersion/SearchIndexVersion.tsx index 7db4d61f9b5b..79536dfae405 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/SearchIndexVersion/SearchIndexVersion.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/SearchIndexVersion/SearchIndexVersion.tsx @@ -55,6 +55,9 @@ const SearchIndexVersion: React.FC = ({ tier, breadCrumbList, versionList, + onLoadMore, + hasMore, + isLoadingMore, deleted = false, backHandler, versionHandler, @@ -168,7 +171,8 @@ const SearchIndexVersion: React.FC = ({ + flex="220px" + > = ({ data={currentVersionData} permissions={entityPermissions} type={EntityType.SEARCH_INDEX} - onUpdate={() => Promise.resolve()}> + onUpdate={() => Promise.resolve()} + > = ({ ); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Topic/TopicVersion/TopicVersion.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Topic/TopicVersion/TopicVersion.component.tsx index 2a5fce72314e..2afce360b256 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Topic/TopicVersion/TopicVersion.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Topic/TopicVersion/TopicVersion.component.tsx @@ -51,6 +51,9 @@ const TopicVersion: FC = ({ tier, slashedTopicName, versionList, + onLoadMore, + hasMore, + isLoadingMore, deleted = false, backHandler, versionHandler, @@ -161,7 +164,8 @@ const TopicVersion: FC = ({ + flex="220px" + > = ({ data={currentVersionData} permissions={entityPermissions} type={EntityType.TOPIC} - onUpdate={() => Promise.resolve()}> + onUpdate={() => Promise.resolve()} + > = ({ ); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Topic/TopicVersion/TopicVersion.interface.ts b/openmetadata-ui/src/main/resources/ui/src/components/Topic/TopicVersion/TopicVersion.interface.ts index 4488aeec14b6..572fba1a01b2 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Topic/TopicVersion/TopicVersion.interface.ts +++ b/openmetadata-ui/src/main/resources/ui/src/components/Topic/TopicVersion/TopicVersion.interface.ts @@ -27,6 +27,9 @@ export interface TopicVersionProp { tier: TagLabel; slashedTopicName: TitleBreadcrumbProps['titleLinks']; versionList: EntityHistory; + onLoadMore?: () => void; + hasMore?: boolean; + isLoadingMore?: boolean; deleted?: boolean; backHandler: () => void; versionHandler: (v: string) => void; diff --git a/openmetadata-ui/src/main/resources/ui/src/generated/type/entityHistory.ts b/openmetadata-ui/src/main/resources/ui/src/generated/type/entityHistory.ts index 8f9d0c6328de..eb245023d536 100644 --- a/openmetadata-ui/src/main/resources/ui/src/generated/type/entityHistory.ts +++ b/openmetadata-ui/src/main/resources/ui/src/generated/type/entityHistory.ts @@ -19,5 +19,17 @@ export interface EntityHistory { * produced. */ entityType: string; + /** + * Type used for cursor based pagination information in GET list responses. + */ + paging?: Paging; versions: any[]; } + +export interface Paging { + after?: string; + before?: string; + limit?: number; + offset?: number; + total: number; +} diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/EntityVersionPage/EntityVersionPage.component.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/EntityVersionPage/EntityVersionPage.component.tsx index dc4ff0f6b531..0c4584f04b8d 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/EntityVersionPage/EntityVersionPage.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/EntityVersionPage/EntityVersionPage.component.tsx @@ -17,6 +17,7 @@ import { useCallback, useEffect, useMemo, + useRef, useState, } from 'react'; import { useTranslation } from 'react-i18next'; @@ -144,6 +145,8 @@ import DatabaseSchemaVersionPage from '../DatabaseSchemaVersionPage/DatabaseSche import DatabaseVersionPage from '../DatabaseVersionPage/DatabaseVersionPage'; import './EntityVersionPage.less'; +const VERSION_PAGE_SIZE = 20; + export type VersionData = | Table | Topic @@ -186,6 +189,12 @@ const EntityVersionPage: FunctionComponent = () => { {} as EntityHistory ); const [isVersionLoading, setIsVersionLoading] = useState(true); + const [hasMoreVersions, setHasMoreVersions] = useState(false); + const [isLoadingMore, setIsLoadingMore] = useState(false); + const versionFetcherRef = + useRef< + (params?: { limit?: number; offset?: number }) => Promise + >(); const backHandler = useCallback( () => navigate(getEntityDetailsPath(entityType, decodedEntityFQN, tab)), @@ -239,8 +248,20 @@ const EntityVersionPage: FunctionComponent = () => { [entityPermissions] ); + const updateVersionState = useCallback((versions: EntityHistory) => { + setVersionList(versions); + if (versions.paging) { + const loaded = + (versions.paging.offset ?? 0) + (versions.versions?.length ?? 0); + setHasMoreVersions(loaded < versions.paging.total); + } else { + setHasMoreVersions(false); + } + }, []); + const fetchEntityVersions = useCallback(async () => { setIsLoading(true); + const paginationParams = { limit: VERSION_PAGE_SIZE, offset: 0 }; try { switch (entityType) { case EntityType.TABLE: { @@ -249,10 +270,11 @@ const EntityVersionPage: FunctionComponent = () => { }); setEntityId(id); + versionFetcherRef.current = (p) => getTableVersions(id, p); - const versions = await getTableVersions(id); + const versions = await getTableVersions(id, paginationParams); - setVersionList(versions); + updateVersionState(versions); break; } @@ -263,10 +285,11 @@ const EntityVersionPage: FunctionComponent = () => { }); setEntityId(id); + versionFetcherRef.current = (p) => getTopicVersions(id, p); - const versions = await getTopicVersions(id); + const versions = await getTopicVersions(id, paginationParams); - setVersionList(versions); + updateVersionState(versions); break; } @@ -277,10 +300,11 @@ const EntityVersionPage: FunctionComponent = () => { }); setEntityId(id); + versionFetcherRef.current = (p) => getDashboardVersions(id, p); - const versions = await getDashboardVersions(id); + const versions = await getDashboardVersions(id, paginationParams); - setVersionList(versions); + updateVersionState(versions); break; } @@ -291,10 +315,11 @@ const EntityVersionPage: FunctionComponent = () => { }); setEntityId(id); + versionFetcherRef.current = (p) => getPipelineVersions(id, p); - const versions = await getPipelineVersions(id); + const versions = await getPipelineVersions(id, paginationParams); - setVersionList(versions); + updateVersionState(versions); break; } @@ -305,10 +330,11 @@ const EntityVersionPage: FunctionComponent = () => { }); setEntityId(id); + versionFetcherRef.current = (p) => getMlModelVersions(id, p); - const versions = await getMlModelVersions(id); + const versions = await getMlModelVersions(id, paginationParams); - setVersionList(versions); + updateVersionState(versions); break; } @@ -319,10 +345,11 @@ const EntityVersionPage: FunctionComponent = () => { }); setEntityId(id); + versionFetcherRef.current = (p) => getContainerVersions(id, p); - const versions = await getContainerVersions(id); + const versions = await getContainerVersions(id, paginationParams); - setVersionList(versions); + updateVersionState(versions); break; } @@ -333,10 +360,11 @@ const EntityVersionPage: FunctionComponent = () => { }); setEntityId(id); + versionFetcherRef.current = (p) => getSearchIndexVersions(id, p); - const versions = await getSearchIndexVersions(id); + const versions = await getSearchIndexVersions(id, paginationParams); - setVersionList(versions); + updateVersionState(versions); break; } @@ -347,10 +375,15 @@ const EntityVersionPage: FunctionComponent = () => { }); setEntityId(id ?? ''); + versionFetcherRef.current = (p) => + getDataModelVersionsList(id ?? '', p); - const versions = await getDataModelVersionsList(id ?? ''); + const versions = await getDataModelVersionsList( + id ?? '', + paginationParams + ); - setVersionList(versions); + updateVersionState(versions); break; } @@ -361,10 +394,15 @@ const EntityVersionPage: FunctionComponent = () => { }); setEntityId(id ?? ''); + versionFetcherRef.current = (p) => + getStoredProceduresVersionsList(id ?? '', p); - const versions = await getStoredProceduresVersionsList(id ?? ''); + const versions = await getStoredProceduresVersionsList( + id ?? '', + paginationParams + ); - setVersionList(versions); + updateVersionState(versions); break; } @@ -374,10 +412,15 @@ const EntityVersionPage: FunctionComponent = () => { }); setEntityId(id ?? ''); + versionFetcherRef.current = (p) => + getApiEndPointVersions(id ?? '', p); - const versions = await getApiEndPointVersions(id ?? ''); + const versions = await getApiEndPointVersions( + id ?? '', + paginationParams + ); - setVersionList(versions); + updateVersionState(versions); break; } @@ -387,10 +430,11 @@ const EntityVersionPage: FunctionComponent = () => { }); setEntityId(id ?? ''); + versionFetcherRef.current = (p) => getMetricVersions(id ?? '', p); - const versions = await getMetricVersions(id ?? ''); + const versions = await getMetricVersions(id ?? '', paginationParams); - setVersionList(versions); + updateVersionState(versions); break; } @@ -400,10 +444,11 @@ const EntityVersionPage: FunctionComponent = () => { }); setEntityId(id ?? ''); + versionFetcherRef.current = (p) => getChartVersions(id ?? '', p); - const versions = await getChartVersions(id ?? ''); + const versions = await getChartVersions(id ?? '', paginationParams); - setVersionList(versions); + updateVersionState(versions); break; } @@ -413,10 +458,20 @@ const EntityVersionPage: FunctionComponent = () => { case EntityType.WORKSHEET: { const { id } = await getDriveAssetByFqn(decodedEntityFQN, entityType); setEntityId(id ?? ''); + versionFetcherRef.current = (p) => + getDriveAssetsVersions( + id ?? '', + entityType, + p + ) as unknown as Promise; + + const versions = await getDriveAssetsVersions( + id ?? '', + entityType, + paginationParams + ); - const versions = await getDriveAssetsVersions(id ?? '', entityType); - - setVersionList(versions as unknown as EntityHistory); + updateVersionState(versions as unknown as EntityHistory); break; } @@ -429,6 +484,43 @@ const EntityVersionPage: FunctionComponent = () => { } }, [entityType, decodedEntityFQN, viewVersionPermission]); + const fetchMoreVersions = useCallback(async () => { + if (!versionFetcherRef.current || isLoadingMore || !hasMoreVersions) { + return; + } + setIsLoadingMore(true); + try { + const currentOffset = versionList.versions?.length ?? 0; + const moreVersions = await versionFetcherRef.current({ + limit: VERSION_PAGE_SIZE, + offset: currentOffset, + }); + + setVersionList((prev) => { + const combined = { + ...moreVersions, + versions: [ + ...(prev.versions ?? []), + ...(moreVersions.versions ?? []), + ], + }; + + return combined; + }); + + if (moreVersions.paging) { + const totalLoaded = + (versionList.versions?.length ?? 0) + + (moreVersions.versions?.length ?? 0); + setHasMoreVersions(totalLoaded < moreVersions.paging.total); + } else { + setHasMoreVersions(false); + } + } finally { + setIsLoadingMore(false); + } + }, [isLoadingMore, hasMoreVersions, versionList]); + const fetchCurrentVersion = useCallback( async (id: string) => { setIsVersionLoading(true); @@ -622,6 +714,8 @@ const EntityVersionPage: FunctionComponent = () => { deleted={currentVersionData.deleted} domains={domains} entityPermissions={entityPermissions} + hasMore={hasMoreVersions} + isLoadingMore={isLoadingMore} isVersionLoading={isVersionLoading} owners={owners} slashedTableName={slashedEntityName} @@ -629,6 +723,7 @@ const EntityVersionPage: FunctionComponent = () => { version={version} versionHandler={versionHandler} versionList={versionList} + onLoadMore={fetchMoreVersions} /> ); } @@ -641,6 +736,8 @@ const EntityVersionPage: FunctionComponent = () => { deleted={currentVersionData.deleted} domains={domains} entityPermissions={entityPermissions} + hasMore={hasMoreVersions} + isLoadingMore={isLoadingMore} isVersionLoading={isVersionLoading} owners={owners} slashedTopicName={slashedEntityName} @@ -648,6 +745,7 @@ const EntityVersionPage: FunctionComponent = () => { version={version} versionHandler={versionHandler} versionList={versionList} + onLoadMore={fetchMoreVersions} /> ); } @@ -661,6 +759,8 @@ const EntityVersionPage: FunctionComponent = () => { deleted={currentVersionData.deleted} domains={domains} entityPermissions={entityPermissions} + hasMore={hasMoreVersions} + isLoadingMore={isLoadingMore} isVersionLoading={isVersionLoading} owners={owners} slashedDashboardName={slashedEntityName} @@ -668,6 +768,7 @@ const EntityVersionPage: FunctionComponent = () => { version={version} versionHandler={versionHandler} versionList={versionList} + onLoadMore={fetchMoreVersions} /> ); } @@ -681,6 +782,8 @@ const EntityVersionPage: FunctionComponent = () => { deleted={currentVersionData.deleted} domains={domains} entityPermissions={entityPermissions} + hasMore={hasMoreVersions} + isLoadingMore={isLoadingMore} isVersionLoading={isVersionLoading} owners={owners} slashedPipelineName={slashedEntityName} @@ -688,6 +791,7 @@ const EntityVersionPage: FunctionComponent = () => { version={version} versionHandler={versionHandler} versionList={versionList} + onLoadMore={fetchMoreVersions} /> ); } @@ -701,6 +805,8 @@ const EntityVersionPage: FunctionComponent = () => { deleted={currentVersionData.deleted} domains={domains} entityPermissions={entityPermissions} + hasMore={hasMoreVersions} + isLoadingMore={isLoadingMore} isVersionLoading={isVersionLoading} owners={owners} slashedMlModelName={slashedEntityName} @@ -708,6 +814,7 @@ const EntityVersionPage: FunctionComponent = () => { version={version} versionHandler={versionHandler} versionList={versionList} + onLoadMore={fetchMoreVersions} /> ); } @@ -721,12 +828,15 @@ const EntityVersionPage: FunctionComponent = () => { deleted={currentVersionData.deleted} domains={domains} entityPermissions={entityPermissions} + hasMore={hasMoreVersions} + isLoadingMore={isLoadingMore} isVersionLoading={isVersionLoading} owners={owners} tier={tier as TagLabel} version={version} versionHandler={versionHandler} versionList={versionList} + onLoadMore={fetchMoreVersions} /> ); } @@ -740,12 +850,15 @@ const EntityVersionPage: FunctionComponent = () => { deleted={currentVersionData.deleted} domains={domains} entityPermissions={entityPermissions} + hasMore={hasMoreVersions} + isLoadingMore={isLoadingMore} isVersionLoading={isVersionLoading} owners={owners} tier={tier as TagLabel} version={version} versionHandler={versionHandler} versionList={versionList} + onLoadMore={fetchMoreVersions} /> ); } @@ -759,6 +872,8 @@ const EntityVersionPage: FunctionComponent = () => { deleted={currentVersionData.deleted} domains={domains} entityPermissions={entityPermissions} + hasMore={hasMoreVersions} + isLoadingMore={isLoadingMore} isVersionLoading={isVersionLoading} owners={owners} slashedDataModelName={slashedEntityName} @@ -766,6 +881,7 @@ const EntityVersionPage: FunctionComponent = () => { version={version} versionHandler={versionHandler} versionList={versionList} + onLoadMore={fetchMoreVersions} /> ); } @@ -779,6 +895,8 @@ const EntityVersionPage: FunctionComponent = () => { deleted={currentVersionData.deleted} domains={domains} entityPermissions={entityPermissions} + hasMore={hasMoreVersions} + isLoadingMore={isLoadingMore} isVersionLoading={isVersionLoading} owners={owners} slashedTableName={slashedEntityName} @@ -786,6 +904,7 @@ const EntityVersionPage: FunctionComponent = () => { version={version} versionHandler={versionHandler} versionList={versionList} + onLoadMore={fetchMoreVersions} /> ); } @@ -797,6 +916,8 @@ const EntityVersionPage: FunctionComponent = () => { currentVersionData={currentVersionData as APIEndpoint} domains={domains} entityPermissions={entityPermissions} + hasMore={hasMoreVersions} + isLoadingMore={isLoadingMore} isVersionLoading={isVersionLoading} owners={owners} slashedApiEndpointName={slashedEntityName} @@ -804,6 +925,7 @@ const EntityVersionPage: FunctionComponent = () => { version={version} versionHandler={versionHandler} versionList={versionList} + onLoadMore={fetchMoreVersions} /> ); } @@ -814,6 +936,8 @@ const EntityVersionPage: FunctionComponent = () => { currentVersionData={currentVersionData as Metric} domains={domains} entityPermissions={entityPermissions} + hasMore={hasMoreVersions} + isLoadingMore={isLoadingMore} isVersionLoading={isVersionLoading} owners={owners} slashedMetricName={slashedEntityName} @@ -821,6 +945,7 @@ const EntityVersionPage: FunctionComponent = () => { version={version} versionHandler={versionHandler} versionList={versionList} + onLoadMore={fetchMoreVersions} /> ); } @@ -833,6 +958,8 @@ const EntityVersionPage: FunctionComponent = () => { deleted={currentVersionData.deleted} domains={domains} entityPermissions={entityPermissions} + hasMore={hasMoreVersions} + isLoadingMore={isLoadingMore} isVersionLoading={isVersionLoading} owners={owners} slashedChartName={slashedEntityName as unknown as string[]} @@ -840,6 +967,7 @@ const EntityVersionPage: FunctionComponent = () => { version={version} versionHandler={versionHandler} versionList={versionList} + onLoadMore={fetchMoreVersions} /> ); } @@ -853,12 +981,15 @@ const EntityVersionPage: FunctionComponent = () => { deleted={currentVersionData.deleted} domains={domains} entityPermissions={entityPermissions} + hasMore={hasMoreVersions} + isLoadingMore={isLoadingMore} isVersionLoading={isVersionLoading} owners={owners} tier={tier as TagLabel} version={version} versionHandler={versionHandler} versionList={versionList} + onLoadMore={fetchMoreVersions} /> ); } @@ -872,12 +1003,15 @@ const EntityVersionPage: FunctionComponent = () => { deleted={currentVersionData.deleted} domains={domains} entityPermissions={entityPermissions} + hasMore={hasMoreVersions} + isLoadingMore={isLoadingMore} isVersionLoading={isVersionLoading} owners={owners} tier={tier as TagLabel} version={version} versionHandler={versionHandler} versionList={versionList} + onLoadMore={fetchMoreVersions} /> ); } @@ -891,12 +1025,15 @@ const EntityVersionPage: FunctionComponent = () => { deleted={currentVersionData.deleted} domains={domains} entityPermissions={entityPermissions} + hasMore={hasMoreVersions} + isLoadingMore={isLoadingMore} isVersionLoading={isVersionLoading} owners={owners} tier={tier as TagLabel} version={version} versionHandler={versionHandler} versionList={versionList} + onLoadMore={fetchMoreVersions} /> ); } @@ -910,12 +1047,15 @@ const EntityVersionPage: FunctionComponent = () => { deleted={currentVersionData.deleted} domains={domains} entityPermissions={entityPermissions} + hasMore={hasMoreVersions} + isLoadingMore={isLoadingMore} isVersionLoading={isVersionLoading} owners={owners} tier={tier as TagLabel} version={version} versionHandler={versionHandler} versionList={versionList} + onLoadMore={fetchMoreVersions} /> ); } @@ -964,7 +1104,8 @@ const EntityVersionPage: FunctionComponent = () => { className="version-page-container" pageTitle={t('label.entity-detail-plural', { entity: getEntityName(currentVersionData), - })}> + })} + > {versionComponent()} ); diff --git a/openmetadata-ui/src/main/resources/ui/src/rest/SearchIndexAPI.ts b/openmetadata-ui/src/main/resources/ui/src/rest/SearchIndexAPI.ts index e447be3e8c5a..55a9c70946b9 100644 --- a/openmetadata-ui/src/main/resources/ui/src/rest/SearchIndexAPI.ts +++ b/openmetadata-ui/src/main/resources/ui/src/rest/SearchIndexAPI.ts @@ -135,10 +135,13 @@ export const updateSearchIndexVotes = async (id: string, data: QueryVote) => { return response.data; }; -export const getSearchIndexVersions = async (id: string) => { +export const getSearchIndexVersions = async ( + id: string, + params?: { limit?: number; offset?: number } +) => { const url = `/searchIndexes/${id}/versions`; - const response = await APIClient.get(url); + const response = await APIClient.get(url, { params }); return response.data; }; diff --git a/openmetadata-ui/src/main/resources/ui/src/rest/apiCollectionsAPI.ts b/openmetadata-ui/src/main/resources/ui/src/rest/apiCollectionsAPI.ts index e48046ebe92b..60174b620ddd 100644 --- a/openmetadata-ui/src/main/resources/ui/src/rest/apiCollectionsAPI.ts +++ b/openmetadata-ui/src/main/resources/ui/src/rest/apiCollectionsAPI.ts @@ -80,10 +80,13 @@ export const restoreApiCollection = async (id: string) => { return response.data; }; -export const getApiCollectionVersions = async (id: string) => { +export const getApiCollectionVersions = async ( + id: string, + params?: { limit?: number; offset?: number } +) => { const url = `/apiCollections/${id}/versions`; - const response = await APIClient.get(url); + const response = await APIClient.get(url, { params }); return response.data; }; diff --git a/openmetadata-ui/src/main/resources/ui/src/rest/apiEndpointsAPI.ts b/openmetadata-ui/src/main/resources/ui/src/rest/apiEndpointsAPI.ts index 0bdb5c915e0e..dafa0b7cf0e2 100644 --- a/openmetadata-ui/src/main/resources/ui/src/rest/apiEndpointsAPI.ts +++ b/openmetadata-ui/src/main/resources/ui/src/rest/apiEndpointsAPI.ts @@ -80,10 +80,13 @@ export const restoreApiEndPoint = async (id: string) => { return response.data; }; -export const getApiEndPointVersions = async (id: string) => { +export const getApiEndPointVersions = async ( + id: string, + params?: { limit?: number; offset?: number } +) => { const url = `/apiEndpoints/${id}/versions`; - const response = await APIClient.get(url); + const response = await APIClient.get(url, { params }); return response.data; }; diff --git a/openmetadata-ui/src/main/resources/ui/src/rest/chartsAPI.ts b/openmetadata-ui/src/main/resources/ui/src/rest/chartsAPI.ts index 596f79ed3ecc..579046bcf85b 100644 --- a/openmetadata-ui/src/main/resources/ui/src/rest/chartsAPI.ts +++ b/openmetadata-ui/src/main/resources/ui/src/rest/chartsAPI.ts @@ -40,10 +40,13 @@ export type ListDataModelParams = { const BASE_URL = '/charts'; -export const getChartVersions = async (id: string) => { +export const getChartVersions = async ( + id: string, + params?: { limit?: number; offset?: number } +) => { const url = `${BASE_URL}/${id}/versions`; - const response = await APIClient.get(url); + const response = await APIClient.get(url, { params }); return response.data; }; diff --git a/openmetadata-ui/src/main/resources/ui/src/rest/dashboardAPI.ts b/openmetadata-ui/src/main/resources/ui/src/rest/dashboardAPI.ts index 58de2132d21a..77d68f45d53c 100644 --- a/openmetadata-ui/src/main/resources/ui/src/rest/dashboardAPI.ts +++ b/openmetadata-ui/src/main/resources/ui/src/rest/dashboardAPI.ts @@ -40,10 +40,13 @@ export type ListDataModelParams = { const BASE_URL = '/dashboards'; -export const getDashboardVersions = async (id: string) => { +export const getDashboardVersions = async ( + id: string, + params?: { limit?: number; offset?: number } +) => { const url = `${BASE_URL}/${id}/versions`; - const response = await APIClient.get(url); + const response = await APIClient.get(url, { params }); return response.data; }; diff --git a/openmetadata-ui/src/main/resources/ui/src/rest/dataModelsAPI.ts b/openmetadata-ui/src/main/resources/ui/src/rest/dataModelsAPI.ts index 3b626a6d4c0b..9b6dc957a84e 100644 --- a/openmetadata-ui/src/main/resources/ui/src/rest/dataModelsAPI.ts +++ b/openmetadata-ui/src/main/resources/ui/src/rest/dataModelsAPI.ts @@ -71,10 +71,13 @@ export const removeDataModelFollower = async (id: string, userId: string) => { return response.data; }; -export const getDataModelVersionsList = async (id: string) => { +export const getDataModelVersionsList = async ( + id: string, + params?: { limit?: number; offset?: number } +) => { const url = `${URL}/${id}/versions`; - const response = await APIClient.get(url); + const response = await APIClient.get(url, { params }); return response.data; }; diff --git a/openmetadata-ui/src/main/resources/ui/src/rest/dataProductAPI.ts b/openmetadata-ui/src/main/resources/ui/src/rest/dataProductAPI.ts index a71b43a6b5a5..18cf8e3ae100 100644 --- a/openmetadata-ui/src/main/resources/ui/src/rest/dataProductAPI.ts +++ b/openmetadata-ui/src/main/resources/ui/src/rest/dataProductAPI.ts @@ -76,9 +76,12 @@ export const deleteDataProduct = (id: string) => { return APIClient.delete(`${BASE_URL}/${id}`); }; -export const getDataProductVersionsList = async (id: string) => { +export const getDataProductVersionsList = async ( + id: string, + params?: { limit?: number; offset?: number } +) => { const url = `${BASE_URL}/${id}/versions`; - const response = await APIClient.get(url); + const response = await APIClient.get(url, { params }); return response.data; }; diff --git a/openmetadata-ui/src/main/resources/ui/src/rest/databaseAPI.ts b/openmetadata-ui/src/main/resources/ui/src/rest/databaseAPI.ts index 0f0a570e46b2..a08bdd95d4f0 100644 --- a/openmetadata-ui/src/main/resources/ui/src/rest/databaseAPI.ts +++ b/openmetadata-ui/src/main/resources/ui/src/rest/databaseAPI.ts @@ -187,10 +187,13 @@ export const restoreDatabase = async (id: string) => { return response.data; }; -export const getDatabaseVersions = async (id: string) => { +export const getDatabaseVersions = async ( + id: string, + params?: { limit?: number; offset?: number } +) => { const url = `/databases/${id}/versions`; - const response = await APIClient.get(url); + const response = await APIClient.get(url, { params }); return response.data; }; @@ -203,10 +206,13 @@ export const getDatabaseVersionData = async (id: string, version: string) => { return response.data; }; -export const getDatabaseSchemaVersions = async (id: string) => { +export const getDatabaseSchemaVersions = async ( + id: string, + params?: { limit?: number; offset?: number } +) => { const url = `/databaseSchemas/${id}/versions`; - const response = await APIClient.get(url); + const response = await APIClient.get(url, { params }); return response.data; }; diff --git a/openmetadata-ui/src/main/resources/ui/src/rest/domainAPI.ts b/openmetadata-ui/src/main/resources/ui/src/rest/domainAPI.ts index 9d4b7860f0da..25d50818f247 100644 --- a/openmetadata-ui/src/main/resources/ui/src/rest/domainAPI.ts +++ b/openmetadata-ui/src/main/resources/ui/src/rest/domainAPI.ts @@ -78,9 +78,12 @@ export const getDomainByName = async (fqn: string, params?: ListParams) => { return response.data; }; -export const getDomainVersionsList = async (id: string) => { +export const getDomainVersionsList = async ( + id: string, + params?: { limit?: number; offset?: number } +) => { const url = `${BASE_URL}/${id}/versions`; - const response = await APIClient.get(url); + const response = await APIClient.get(url, { params }); return response.data; }; diff --git a/openmetadata-ui/src/main/resources/ui/src/rest/driveAPI.ts b/openmetadata-ui/src/main/resources/ui/src/rest/driveAPI.ts index 844d65a134c2..01f22f9d4833 100644 --- a/openmetadata-ui/src/main/resources/ui/src/rest/driveAPI.ts +++ b/openmetadata-ui/src/main/resources/ui/src/rest/driveAPI.ts @@ -175,12 +175,13 @@ export const updateDriveAssetVotes = async < export const getDriveAssetsVersions = async ( id: string, - entityType: DriveAssetEntityTypes + entityType: DriveAssetEntityTypes, + params?: { limit?: number; offset?: number } ) => { const API = APIByEntityType[entityType]; const url = `${BASE_URL}${API}/${id}/versions`; - const response = await APIClient.get(url); + const response = await APIClient.get(url, { params }); return response.data; }; diff --git a/openmetadata-ui/src/main/resources/ui/src/rest/glossaryAPI.ts b/openmetadata-ui/src/main/resources/ui/src/rest/glossaryAPI.ts index 1a192b276869..15d95ecba6a1 100644 --- a/openmetadata-ui/src/main/resources/ui/src/rest/glossaryAPI.ts +++ b/openmetadata-ui/src/main/resources/ui/src/rest/glossaryAPI.ts @@ -202,10 +202,13 @@ export const exportGlossaryTermsInCSVFormat = async (glossaryName: string) => { return response.data; }; -export const getGlossaryVersionsList = async (id: string) => { +export const getGlossaryVersionsList = async ( + id: string, + params?: { limit?: number; offset?: number } +) => { const url = `/glossaries/${id}/versions`; - const response = await APIClient.get(url); + const response = await APIClient.get(url, { params }); return response.data; }; @@ -217,10 +220,13 @@ export const getGlossaryVersion = async (id: string, version: string) => { return response.data; }; -export const getGlossaryTermsVersionsList = async (id: string) => { +export const getGlossaryTermsVersionsList = async ( + id: string, + params?: { limit?: number; offset?: number } +) => { const url = `/glossaryTerms/${id}/versions`; - const response = await APIClient.get(url); + const response = await APIClient.get(url, { params }); return response.data; }; diff --git a/openmetadata-ui/src/main/resources/ui/src/rest/metricsAPI.ts b/openmetadata-ui/src/main/resources/ui/src/rest/metricsAPI.ts index d2c65a081b8e..477bc7b72e43 100644 --- a/openmetadata-ui/src/main/resources/ui/src/rest/metricsAPI.ts +++ b/openmetadata-ui/src/main/resources/ui/src/rest/metricsAPI.ts @@ -68,9 +68,13 @@ export const restoreMetric = async (id: string) => { return response.data; }; -export const getMetricVersions = async (id: string) => { +export const getMetricVersions = async ( + id: string, + params?: { limit?: number; offset?: number } +) => { const response = await APIClient.get( - `/metrics/${id}/versions` + `/metrics/${id}/versions`, + { params } ); return response.data; diff --git a/openmetadata-ui/src/main/resources/ui/src/rest/mlModelAPI.ts b/openmetadata-ui/src/main/resources/ui/src/rest/mlModelAPI.ts index 816373440631..72f12059f5bf 100644 --- a/openmetadata-ui/src/main/resources/ui/src/rest/mlModelAPI.ts +++ b/openmetadata-ui/src/main/resources/ui/src/rest/mlModelAPI.ts @@ -28,10 +28,13 @@ import APIClient from './index'; const BASE_URL = '/mlmodels'; -export const getMlModelVersions = async (id: string) => { +export const getMlModelVersions = async ( + id: string, + params?: { limit?: number; offset?: number } +) => { const url = `${BASE_URL}/${id}/versions`; - const response = await APIClient.get(url); + const response = await APIClient.get(url, { params }); return response.data; }; diff --git a/openmetadata-ui/src/main/resources/ui/src/rest/pipelineAPI.ts b/openmetadata-ui/src/main/resources/ui/src/rest/pipelineAPI.ts index a8a78eda76da..938434815ea9 100644 --- a/openmetadata-ui/src/main/resources/ui/src/rest/pipelineAPI.ts +++ b/openmetadata-ui/src/main/resources/ui/src/rest/pipelineAPI.ts @@ -29,10 +29,13 @@ import { ListTestCaseResultsParams } from './testAPI'; const BASE_URL = '/pipelines'; -export const getPipelineVersions = async (id: string) => { +export const getPipelineVersions = async ( + id: string, + params?: { limit?: number; offset?: number } +) => { const url = `${BASE_URL}/${id}/versions`; - const response = await APIClient.get(url); + const response = await APIClient.get(url, { params }); return response.data; }; diff --git a/openmetadata-ui/src/main/resources/ui/src/rest/serviceAPI.ts b/openmetadata-ui/src/main/resources/ui/src/rest/serviceAPI.ts index 62b342b21f7d..0d9414bfd9e6 100644 --- a/openmetadata-ui/src/main/resources/ui/src/rest/serviceAPI.ts +++ b/openmetadata-ui/src/main/resources/ui/src/rest/serviceAPI.ts @@ -164,11 +164,12 @@ export const patchDomainSupportedService = async ( export const getServiceVersions = async ( serviceCategory: string, - id: string + id: string, + params?: { limit?: number; offset?: number } ) => { const url = `/services/${serviceCategory}/${id}/versions`; - const response = await APIClient.get(url); + const response = await APIClient.get(url, { params }); return response.data; }; diff --git a/openmetadata-ui/src/main/resources/ui/src/rest/storageAPI.ts b/openmetadata-ui/src/main/resources/ui/src/rest/storageAPI.ts index e61b96beab5c..73c49daf1928 100644 --- a/openmetadata-ui/src/main/resources/ui/src/rest/storageAPI.ts +++ b/openmetadata-ui/src/main/resources/ui/src/rest/storageAPI.ts @@ -124,10 +124,13 @@ export const removeContainerFollower = async (id: string, userId: string) => { return response.data; }; -export const getContainerVersions = async (id: string) => { +export const getContainerVersions = async ( + id: string, + params?: { limit?: number; offset?: number } +) => { const url = `${BASE_URL}/${id}/versions`; - const response = await APIClient.get(url); + const response = await APIClient.get(url, { params }); return response.data; }; diff --git a/openmetadata-ui/src/main/resources/ui/src/rest/storedProceduresAPI.ts b/openmetadata-ui/src/main/resources/ui/src/rest/storedProceduresAPI.ts index 5d76a379d71c..a4d9f0d704a3 100644 --- a/openmetadata-ui/src/main/resources/ui/src/rest/storedProceduresAPI.ts +++ b/openmetadata-ui/src/main/resources/ui/src/rest/storedProceduresAPI.ts @@ -104,10 +104,13 @@ export const removeStoredProceduresFollower = async ( return response.data; }; -export const getStoredProceduresVersionsList = async (id: string) => { +export const getStoredProceduresVersionsList = async ( + id: string, + params?: { limit?: number; offset?: number } +) => { const url = `${URL}/${id}/versions`; - const response = await APIClient.get(url); + const response = await APIClient.get(url, { params }); return response.data; }; diff --git a/openmetadata-ui/src/main/resources/ui/src/rest/tableAPI.ts b/openmetadata-ui/src/main/resources/ui/src/rest/tableAPI.ts index c66fff886616..7148a70a3568 100644 --- a/openmetadata-ui/src/main/resources/ui/src/rest/tableAPI.ts +++ b/openmetadata-ui/src/main/resources/ui/src/rest/tableAPI.ts @@ -45,10 +45,13 @@ export type TableListParams = { const BASE_URL = '/tables'; -export const getTableVersions = async (id: string) => { +export const getTableVersions = async ( + id: string, + params?: { limit?: number; offset?: number } +) => { const url = `${BASE_URL}/${id}/versions`; - const response = await APIClient.get(url); + const response = await APIClient.get(url, { params }); return response.data; }; diff --git a/openmetadata-ui/src/main/resources/ui/src/rest/tagAPI.ts b/openmetadata-ui/src/main/resources/ui/src/rest/tagAPI.ts index 46ce9caa878e..f095729e2762 100644 --- a/openmetadata-ui/src/main/resources/ui/src/rest/tagAPI.ts +++ b/openmetadata-ui/src/main/resources/ui/src/rest/tagAPI.ts @@ -177,10 +177,13 @@ export const deleteTag = async (tagId: string) => { return response.data; }; -export const getClassificationVersionsList = async (id: string) => { +export const getClassificationVersionsList = async ( + id: string, + params?: { limit?: number; offset?: number } +) => { const url = `${BASE_URL}/${id}/versions`; - const response = await APIClient.get(url); + const response = await APIClient.get(url, { params }); return response.data; }; diff --git a/openmetadata-ui/src/main/resources/ui/src/rest/testAPI.ts b/openmetadata-ui/src/main/resources/ui/src/rest/testAPI.ts index c2e2e2ee4ae7..b55a46eba4a1 100644 --- a/openmetadata-ui/src/main/resources/ui/src/rest/testAPI.ts +++ b/openmetadata-ui/src/main/resources/ui/src/rest/testAPI.ts @@ -226,7 +226,7 @@ export const removeTestCaseFromTestSuite = async ( export const getTestCaseVersionList = async (id: string) => { const url = `${testCaseUrl}/${id}/versions`; - const response = await APIClient.get(url); + const response = await APIClient.get(url, { params }); return response.data; }; diff --git a/openmetadata-ui/src/main/resources/ui/src/rest/topicsAPI.ts b/openmetadata-ui/src/main/resources/ui/src/rest/topicsAPI.ts index 5b99690a8f62..c4a1bd286d3c 100644 --- a/openmetadata-ui/src/main/resources/ui/src/rest/topicsAPI.ts +++ b/openmetadata-ui/src/main/resources/ui/src/rest/topicsAPI.ts @@ -28,10 +28,13 @@ import APIClient from './index'; const BASE_URL = '/topics'; -export const getTopicVersions = async (id: string) => { +export const getTopicVersions = async ( + id: string, + params?: { limit?: number; offset?: number } +) => { const url = `${BASE_URL}/${id}/versions`; - const response = await APIClient.get(url); + const response = await APIClient.get(url, { params }); return response.data; }; From e9bd2cc35b5fba8dce3f4c26f36c104290011ba0 Mon Sep 17 00:00:00 2001 From: Sriharsha Chintalapani Date: Fri, 6 Mar 2026 19:08:45 -0800 Subject: [PATCH 02/20] Feature #18173: Version API Improvements, Last x versions order by desc, versions from specific timeline, versions for specific metadata changes, sdk support and UI integration --- .agents/skills/playwright-validation/SKILL.md | 175 +++++++++++++ .agents/skills/playwright/SKILL.md | 123 +++++++++ .../skills/writing-playwright-tests/SKILL.md | 10 + AGENTS.md | 242 ++++++++++++++++++ .../native/1.13.0/mysql/schemaChanges.sql | 6 + .../native/1.13.0/postgres/schemaChanges.sql | 6 + .../ingestion/ometa/mixins/version_mixin.py | 51 ++++ ingestion/src/metadata/sdk/entities/base.py | 27 ++ .../it/tests/APICollectionResourceIT.java | 8 + .../it/tests/APIEndpointResourceIT.java | 6 + .../it/tests/APIServiceResourceIT.java | 6 + .../openmetadata/it/tests/BaseEntityIT.java | 32 +++ .../openmetadata/it/tests/BotResourceIT.java | 6 + .../it/tests/ChartResourceIT.java | 6 + .../it/tests/ClassificationResourceIT.java | 8 + .../it/tests/ContainerResourceIT.java | 6 + .../tests/DashboardDataModelResourceIT.java | 8 + .../it/tests/DashboardResourceIT.java | 6 + .../it/tests/DashboardServiceResourceIT.java | 8 + .../it/tests/DataContractResourceIT.java | 6 + .../it/tests/DataInsightChartResourceIT.java | 6 + .../it/tests/DataProductResourceIT.java | 6 + .../it/tests/DatabaseResourceIT.java | 6 + .../it/tests/DatabaseSchemaResourceIT.java | 8 + .../it/tests/DatabaseServiceResourceIT.java | 8 + .../it/tests/DomainResourceIT.java | 6 + .../it/tests/DriveServiceResourceIT.java | 6 + .../it/tests/EventSubscriptionResourceIT.java | 8 + .../it/tests/GlossaryResourceIT.java | 6 + .../it/tests/GlossaryTermResourceIT.java | 6 + .../it/tests/IngestionPipelineResourceIT.java | 8 + .../openmetadata/it/tests/KpiResourceIT.java | 6 + .../it/tests/LLMModelResourceIT.java | 6 + .../it/tests/LLMServiceResourceIT.java | 6 + .../it/tests/LearningResourceIT.java | 6 + .../it/tests/MessagingServiceResourceIT.java | 8 + .../it/tests/MetadataServiceResourceIT.java | 8 + .../it/tests/MetricResourceIT.java | 6 + .../it/tests/MlModelResourceIT.java | 6 + .../it/tests/MlModelServiceResourceIT.java | 8 + .../tests/NotificationTemplateResourceIT.java | 8 + .../it/tests/PersonaResourceIT.java | 6 + .../it/tests/PipelineResourceIT.java | 6 + .../it/tests/PipelineServiceResourceIT.java | 8 + .../it/tests/PolicyResourceIT.java | 6 + .../it/tests/QueryResourceIT.java | 6 + .../openmetadata/it/tests/RoleResourceIT.java | 6 + .../it/tests/SearchIndexResourceIT.java | 6 + .../it/tests/SearchServiceResourceIT.java | 8 + .../it/tests/StorageServiceResourceIT.java | 8 + .../it/tests/StoredProcedureResourceIT.java | 8 + .../it/tests/TableResourceIT.java | 6 + .../openmetadata/it/tests/TagResourceIT.java | 6 + .../openmetadata/it/tests/TeamResourceIT.java | 6 + .../it/tests/TestCaseResourceIT.java | 6 + .../it/tests/TestDefinitionResourceIT.java | 8 + .../it/tests/TestSuiteResourceIT.java | 6 + .../it/tests/TopicResourceIT.java | 6 + .../openmetadata/it/tests/UserResourceIT.java | 6 + .../it/tests/WorksheetResourceIT.java | 6 + .../sdk/fluent/DatabaseSchemas.java | 15 ++ .../openmetadata/sdk/fluent/Databases.java | 15 ++ .../org/openmetadata/sdk/fluent/Domains.java | 15 ++ .../openmetadata/sdk/fluent/Pipelines.java | 15 ++ .../org/openmetadata/sdk/fluent/Roles.java | 15 ++ .../org/openmetadata/sdk/fluent/Tables.java | 15 ++ .../org/openmetadata/sdk/fluent/Teams.java | 15 ++ .../org/openmetadata/sdk/fluent/Topics.java | 15 ++ .../org/openmetadata/sdk/fluent/Users.java | 15 ++ .../sdk/services/EntityServiceBase.java | 36 +++ .../service/jdbi3/CollectionDAO.java | 23 ++ .../service/jdbi3/EntityRepository.java | 79 ++++++ .../service/resources/EntityResource.java | 25 +- .../resources/ai/AIApplicationResource.java | 9 +- .../ai/AIGovernancePolicyResource.java | 9 +- .../resources/ai/LLMModelResource.java | 9 +- .../resources/ai/PromptTemplateResource.java | 9 +- .../analytics/WebAnalyticEventResource.java | 9 +- .../resources/apis/APICollectionResource.java | 9 +- .../resources/apis/APIEndpointResource.java | 9 +- .../apps/AppMarketPlaceResource.java | 9 +- .../service/resources/apps/AppResource.java | 9 +- .../automations/WorkflowResource.java | 9 +- .../service/resources/bots/BotResource.java | 9 +- .../resources/charts/ChartResource.java | 9 +- .../dashboards/DashboardResource.java | 9 +- .../resources/data/DataContractResource.java | 9 +- .../resources/databases/DatabaseResource.java | 9 +- .../databases/DatabaseSchemaResource.java | 9 +- .../databases/StoredProcedureResource.java | 9 +- .../resources/databases/TableResource.java | 9 +- .../datainsight/DataInsightChartResource.java | 9 +- .../DashboardDataModelResource.java | 9 +- .../resources/docstore/DocStoreResource.java | 9 +- .../domains/DataProductResource.java | 9 +- .../resources/domains/DomainResource.java | 9 +- .../resources/dqtests/TestCaseResource.java | 9 +- .../dqtests/TestDefinitionResource.java | 9 +- .../resources/dqtests/TestSuiteResource.java | 9 +- .../resources/drives/DirectoryResource.java | 9 +- .../resources/drives/FileResource.java | 9 +- .../resources/drives/SpreadsheetResource.java | 9 +- .../resources/drives/WorksheetResource.java | 9 +- .../events/NotificationTemplateResource.java | 9 +- .../EventSubscriptionResource.java | 9 +- .../resources/glossary/GlossaryResource.java | 9 +- .../glossary/GlossaryTermResource.java | 9 +- .../WorkflowDefinitionResource.java | 9 +- .../service/resources/kpi/KpiResource.java | 9 +- .../learning/LearningResourceResource.java | 9 +- .../resources/metrics/MetricResource.java | 9 +- .../resources/mlmodels/MlModelResource.java | 9 +- .../resources/pipelines/PipelineResource.java | 9 +- .../resources/policies/PolicyResource.java | 9 +- .../resources/query/QueryResource.java | 9 +- .../searchindex/SearchIndexResource.java | 9 +- .../apiservices/APIServiceResource.java | 10 +- .../dashboard/DashboardServiceResource.java | 10 +- .../database/DatabaseServiceResource.java | 10 +- .../services/drive/DriveServiceResource.java | 10 +- .../IngestionPipelineResource.java | 9 +- .../services/llm/LLMServiceResource.java | 10 +- .../messaging/MessagingServiceResource.java | 10 +- .../metadata/MetadataServiceResource.java | 10 +- .../mlmodel/MlModelServiceResource.java | 10 +- .../pipeline/PipelineServiceResource.java | 10 +- .../searchIndexes/SearchServiceResource.java | 10 +- .../security/SecurityServiceResource.java | 10 +- .../storage/StorageServiceResource.java | 10 +- .../resources/storages/ContainerResource.java | 9 +- .../tags/ClassificationResource.java | 9 +- .../service/resources/tags/TagResource.java | 9 +- .../resources/teams/PersonaResource.java | 9 +- .../service/resources/teams/RoleResource.java | 9 +- .../service/resources/teams/TeamResource.java | 9 +- .../service/resources/teams/UserResource.java | 9 +- .../resources/topics/TopicResource.java | 9 +- .../service/resources/types/TypeResource.java | 9 +- .../PaginatedVersionHistory.spec.ts | 191 +++++++++----- .../resources/ui/src/rest/SearchIndexAPI.ts | 2 +- .../ui/src/rest/apiCollectionsAPI.ts | 2 +- .../resources/ui/src/rest/apiEndpointsAPI.ts | 2 +- .../main/resources/ui/src/rest/chartsAPI.ts | 2 +- .../resources/ui/src/rest/dashboardAPI.ts | 2 +- .../resources/ui/src/rest/dataModelsAPI.ts | 2 +- .../resources/ui/src/rest/dataProductAPI.ts | 2 +- .../main/resources/ui/src/rest/databaseAPI.ts | 4 +- .../main/resources/ui/src/rest/domainAPI.ts | 2 +- .../main/resources/ui/src/rest/driveAPI.ts | 2 +- .../main/resources/ui/src/rest/glossaryAPI.ts | 4 +- .../main/resources/ui/src/rest/metricsAPI.ts | 2 +- .../main/resources/ui/src/rest/mlModelAPI.ts | 2 +- .../main/resources/ui/src/rest/pipelineAPI.ts | 2 +- .../main/resources/ui/src/rest/serviceAPI.ts | 2 +- .../main/resources/ui/src/rest/storageAPI.ts | 2 +- .../ui/src/rest/storedProceduresAPI.ts | 2 +- .../main/resources/ui/src/rest/tableAPI.ts | 2 +- .../src/main/resources/ui/src/rest/tagAPI.ts | 2 +- .../main/resources/ui/src/rest/topicsAPI.ts | 2 +- 159 files changed, 1916 insertions(+), 226 deletions(-) create mode 100644 .agents/skills/playwright-validation/SKILL.md create mode 100644 .agents/skills/playwright/SKILL.md create mode 100644 .agents/skills/writing-playwright-tests/SKILL.md create mode 100644 AGENTS.md create mode 100644 bootstrap/sql/migrations/native/1.13.0/mysql/schemaChanges.sql create mode 100644 bootstrap/sql/migrations/native/1.13.0/postgres/schemaChanges.sql diff --git a/.agents/skills/playwright-validation/SKILL.md b/.agents/skills/playwright-validation/SKILL.md new file mode 100644 index 000000000000..8c5c1132c392 --- /dev/null +++ b/.agents/skills/playwright-validation/SKILL.md @@ -0,0 +1,175 @@ +--- +name: playwright-validation +description: Use when validating UI changes in a branch require Playwright E2E testing. Reviews branch changes, validates UI with Playwright MCP, and adds missing test cases. +--- + +# Playwright Validation Skill + +This skill guides you through validating UI changes and ensuring comprehensive Playwright E2E test coverage. + +## When to Use + +- After completing UI feature development +- Before creating a PR for UI changes +- When reviewing UI-related branches +- To verify existing Playwright tests cover all scenarios + +## Workflow + +### Phase 1: Review Branch Changes + +1. **Identify changed files vs main:** + ```bash + git diff main --stat + git diff main --name-only | grep -E "\.(tsx?|less|css|scss)$" + ``` + +2. **Focus on UI component changes:** + ```bash + git diff main -- "openmetadata-ui/src/main/resources/ui/src/components/**" --stat + ``` + +3. **Check for existing Playwright tests:** + ```bash + git diff main --name-only | grep -E "playwright.*\.spec\.ts$" + ``` + +4. **Read the changed component files** to understand the UI modifications + +### Phase 2: Review Existing Playwright Tests + +1. **Locate relevant test files:** + - Check `playwright/e2e/Pages/` for page-level tests + - Check `playwright/e2e/Features/` for feature-specific tests + - Use Glob/Grep to find tests related to the feature + +2. **Analyze test coverage:** + - Read the existing test file(s) + - Identify the test scenarios already covered + - Note any gaps in coverage based on the UI changes + +3. **Review test utilities:** + - Check `playwright/utils/` for helper functions + - Check `playwright/support/` for entity classes and fixtures + +### Phase 3: Validate with Playwright MCP + +1. **Start the browser and navigate:** + ``` + mcp__playwright__browser_navigate to http://localhost:8585 + ``` + +2. **Authenticate if needed:** + - Use `mcp__playwright__browser_fill_form` for login + - Default admin: `admin@open-metadata.org` / `admin` + +3. **Navigate to the feature area:** + - Use `mcp__playwright__browser_click` for navigation + - Use `mcp__playwright__browser_snapshot` to inspect page state + +4. **Validate UI behavior:** + - Test the main user flows + - Verify visual elements (icons, badges, labels) + - Check interactive elements (buttons, dropdowns, forms) + - Verify state changes and API calls + +5. **Document findings:** + - Note what works correctly + - Identify any issues or missing functionality + - List scenarios not covered by existing tests + +### Phase 4: Add Missing Test Cases + +1. **Create a TodoWrite checklist** of missing test scenarios + +2. **For each missing test case:** + + a. **Add necessary test fixtures** in `beforeAll`: + - Create new entity instances (TableClass, DataProduct, etc.) + - Set up required relationships (domains, assets) + + b. **Add cleanup** in `afterAll`: + - Delete created entities in reverse order + + c. **Write the test** following the pattern: + ```typescript + test('Descriptive Test Name - What it validates', async ({ page }) => { + test.setTimeout(300000); + + await test.step('Step description', async () => { + // Test actions and assertions + }); + + await test.step('Next step', async () => { + // More actions and assertions + }); + }); + ``` + +3. **Test patterns to cover:** + - Happy path (expected behavior) + - Edge cases (empty states, max values) + - Error handling (invalid inputs, failed requests) + - State transitions (before/after actions) + - UI feedback (loading states, success/error messages) + - Permissions (disabled buttons, restricted actions) + +4. **Run lint check:** + ```bash + yarn eslint playwright/e2e/Pages/YourTest.spec.ts + ``` + +## Common Test Utilities + +### Navigation +```typescript +import { sidebarClick } from '../../utils/sidebar'; +import { redirectToHomePage } from '../../utils/common'; +import { selectDataProduct, selectDomain } from '../../utils/domain'; +``` + +### Waiting +```typescript +import { waitForAllLoadersToDisappear } from '../../utils/entity'; +await page.waitForLoadState('networkidle'); +await page.waitForSelector('[data-testid="loader"]', { state: 'detached' }); +``` + +### API Responses +```typescript +const response = page.waitForResponse('/api/v1/endpoint*'); +await someAction(); +await response; +expect((await response).status()).toBe(200); +``` + +### Assertions +```typescript +await expect(page.getByTestId('element')).toBeVisible(); +await expect(page.getByTestId('element')).toContainText('text'); +await expect(page.locator('.class')).not.toBeVisible(); +``` + +## Checklist Before Completion + +- [ ] All UI changes have corresponding test coverage +- [ ] Tests cover both positive and negative scenarios +- [ ] Tests verify visual indicators (icons, badges, states) +- [ ] Tests validate API interactions +- [ ] Lint check passes with no errors +- [ ] Test fixtures are properly created and cleaned up +- [ ] Test timeouts are appropriate (300000ms for complex tests) + +## Example: Data Contract Inheritance Tests + +For reference, see the comprehensive test coverage in: +`playwright/e2e/Pages/DataContractInheritance.spec.ts` + +This file demonstrates: +- Multiple entity setup in beforeAll +- Domain assignment patches +- Contract creation and validation +- Inheritance icon verification +- Action button state verification (disabled/enabled) +- API response validation (POST vs PATCH) +- Fallback behavior testing diff --git a/.agents/skills/playwright/SKILL.md b/.agents/skills/playwright/SKILL.md new file mode 100644 index 000000000000..11a77631743f --- /dev/null +++ b/.agents/skills/playwright/SKILL.md @@ -0,0 +1,123 @@ +--- +name: playwright-test +description: Generate robust, zero-flakiness Playwright E2E tests following OpenMetadata patterns. Creates comprehensive test files with proper waits, API validation, multi-role permissions, and complete entity lifecycle management. +user-invocable: true +--- + +# Playwright Test Generator - OpenMetadata + +Generate production-ready, zero-flakiness Playwright tests following OpenMetadata conventions. + +## Usage + +``` +/playwright-test +Feature: +Category: +Entity: +Domain: +Scenarios: + - + - + - +Roles: (optional, defaults to admin) +``` + +## Quick Example + +``` +/playwright-test +Feature: Data Quality Rules +Category: Features +Entity: Table +Domain: Observability +Scenarios: + - Admin can create and configure data quality rules + - Data consumer can view test results but not edit rules + - Test results are persisted after page reload +Roles: admin, dataConsumer +``` + +--- + +## Instructions + +### Step 1: Read the Handbook FIRST + +**CRITICAL**: Before generating any tests, read and apply ALL patterns from: + +``` +openmetadata-ui/src/main/resources/ui/playwright/PLAYWRIGHT_DEVELOPER_HANDBOOK.md +``` + +The handbook contains: +- Testing philosophy (user-centric, behavior-focused) +- Anti-flakiness patterns (the :visible selector chain pattern, etc.) +- Test timeout strategies (test.slow() vs test.setTimeout()) +- Common test patterns (form submission, dropdowns, multi-role testing) +- Locator priority guidelines +- Support classes reference +- Domain tags +- Validation checklist + +**Apply ALL handbook principles before proceeding.** + +--- + +### Step 2: Generate Test Using Handbook Template + +Use the **Test File Structure Template** from the handbook. It includes: +- Proper imports (performAdminLogin, entity classes, utilities) +- test.describe with domain tags +- beforeAll/afterAll for entity lifecycle via API +- test.slow() for timeout handling +- test.step() for clear organization +- API response validation pattern + +--- + +### Step 3: Apply Common Test Patterns from Handbook + +Reference the **Common Test Patterns** section for: +- Form submission with API validation +- Dropdown selection (with :visible chain pattern) +- Multi-role permission testing +- Data persistence verification +- Navigation patterns +- Search and filter patterns + +--- + +### Step 4: Validate Against Handbook Checklist + +Before returning the generated test, verify ALL items from the handbook's **Validation Checklist**: + +- ✅ Structure & Organization (test.step, domain tags, imports, beforeAll/afterAll) +- ✅ Anti-Flakiness (no waitForTimeout, no networkidle, no force: true, no positional selectors, no stored :visible locators) +- ✅ API & Network (waitForResponse before actions, status code validation) +- ✅ Waits & Assertions (waitForAllLoadersToDisappear, semantic locators, proper assertions) +- ✅ Coverage & Roles (multi-role tests, data persistence, error handling) + +--- + +## Key Reminders + +**All patterns, rules, and best practices are in the handbook.** + +Read and apply the handbook sections in order: +1. **Anti-Flakiness Patterns** (CRITICAL - #1 cause of flaky tests) +2. **Test File Structure Template** (for proper test structure) +3. **Common Test Patterns** (for specific scenarios) +4. **Validation Checklist** (before returning generated test) + +--- + +## Final Notes + +- Generate **production-ready** tests that pass 10/10 times +- Follow ALL patterns from the handbook exactly +- No comments for obvious code (e.g., `// Create entity` before `entity.create()`) +- Test independence - each test runs in any order +- Reference examples: `playwright/e2e/Pages/DataContractInheritance.spec.ts`, `playwright/e2e/Features/Table.spec.ts` + +**Generate tests that are production-ready, maintainable, and zero-flakiness by following the handbook patterns exactly.** diff --git a/.agents/skills/writing-playwright-tests/SKILL.md b/.agents/skills/writing-playwright-tests/SKILL.md new file mode 100644 index 000000000000..49674a6966a2 --- /dev/null +++ b/.agents/skills/writing-playwright-tests/SKILL.md @@ -0,0 +1,10 @@ +--- +name: writing-playwright-tests +description: Use when writing new Playwright E2E tests or adding test cases. Provides testing philosophy, patterns, and best practices from the Playwright Developer Handbook. +--- + +# Writing Playwright Tests Skill + +This skill guides you through writing Playwright E2E tests following OpenMetadata standards. + +**Reference**: @openmetadata-ui/src/main/resources/ui/playwright/PLAYWRIGHT_DEVELOPER_HANDBOOK.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 000000000000..c694fdad0294 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,242 @@ +# AGENTS.md + +This file provides guidance to Codex (Codex.ai/code) when working with code in this repository. + +## About OpenMetadata + +OpenMetadata is a unified metadata platform for data discovery, data observability, and data governance. This is a multi-module project with Java backend services, React frontend, Python ingestion framework, and comprehensive Docker infrastructure. + +## Architecture Overview + +- **Backend**: Java 21 + Dropwizard REST API framework, multi-module Maven project +- **Frontend**: React + TypeScript + Ant Design, built with Webpack and Yarn +- **Ingestion**: Python 3.10-3.12 with Pydantic 2.x, 75+ data source connectors +- **Database**: MySQL (default) or PostgreSQL with Flyway migrations +- **Search**: Elasticsearch 7.17+ or OpenSearch 2.6+ for metadata discovery +- **Infrastructure**: Apache Airflow for workflow orchestration + +## Essential Development Commands + +### Prerequisites and Setup +```bash +make prerequisites # Check system requirements +make install_dev_env # Install all development dependencies +make yarn_install_cache # Install UI dependencies +``` + +### Frontend Development +```bash +cd openmetadata-ui/src/main/resources/ui +yarn start # Start development server on localhost:3000 +yarn test # Run Jest unit tests +yarn test path/to/test.spec.ts # Run a specific test file +yarn test:watch # Run tests in watch mode +yarn playwright:run # Run E2E tests +yarn lint # ESLint check +yarn lint:fix # ESLint with auto-fix +yarn build # Production build +``` + +### Backend Development +```bash +mvn clean package -DskipTests # Build without tests +mvn clean package -DonlyBackend -pl !openmetadata-ui # Backend only +mvn test # Run unit tests +mvn verify # Run integration tests +mvn spotless:apply # Format Java code +``` + +### Python Ingestion Development +```bash +cd ingestion +make install_dev_env # Install in development mode +make generate # Generate Pydantic models from JSON schemas +make unit_ingestion_dev_env # Run unit tests +make lint # Run pylint +make py_format # Format with black, isort, pycln +make static-checks # Run type checking with basedpyright +``` + +### Full Local Environment +```bash +./docker/run_local_docker.sh -m ui -d mysql # Complete local setup with UI +./docker/run_local_docker.sh -m no-ui -d postgresql # Backend only with PostgreSQL +./docker/run_local_docker.sh -s true # Skip Maven build step +``` + +### Testing +```bash +make run_e2e_tests # Full E2E test suite +make unit_ingestion # Python unit tests with coverage +yarn test:coverage # Frontend test coverage +``` + +## Code Generation and Schemas + +OpenMetadata uses a schema-first approach with JSON Schema definitions driving code generation: + +```bash +make generate # Generate all models from schemas +make py_antlr # Generate Python ANTLR parsers +make js_antlr # Generate JavaScript ANTLR parsers +yarn parse-schema # Parse JSON schemas for frontend (connection and ingestion schemas) +``` + +### Schema Architecture +- **Source schemas** in `openmetadata-spec/` define the canonical data models +- **Connection schemas** are pre-processed at build time via `parseSchemas.js` to resolve all `$ref` references +- **Application schemas** in `openmetadata-ui/.../ApplicationSchemas/` are resolved at runtime using `schemaResolver.ts` +- JSON schemas with `$ref` references to external files require resolution before use in forms + +## Key Directories + +- `openmetadata-service/` - Core Java backend services and REST APIs +- `openmetadata-ui/src/main/resources/ui/` - React frontend application +- `ingestion/` - Python ingestion framework with connectors +- `openmetadata-spec/` - JSON Schema specifications for all entities +- `bootstrap/sql/` - Database schema migrations and sample data +- `conf/` - Configuration files for different environments +- `docker/` - Docker configurations for local and production deployment + +## Development Workflow + +1. **Schema Changes**: Modify JSON schemas in `openmetadata-spec/`, then run `mvn clean install` on openmetadata-spec to update models +2. **Backend**: Develop in Java using Dropwizard patterns, test with `mvn test`, format with `mvn spotless:apply` +3. **Frontend**: Use React/TypeScript with Ant Design components, test with Jest/Playwright +4. **Ingestion**: Python connectors follow plugin pattern, use `make install_dev_env` for development +5. **Full Testing**: Use `make run_e2e_tests` before major changes + +## Frontend Architecture Patterns + +### React Component Patterns +- **File Naming**: Components use `ComponentName.component.tsx`, interfaces use `ComponentName.interface.ts` +- **State Management**: Use `useState` with proper typing, avoid `any` +- **Side Effects**: Use `useEffect` with proper dependency arrays +- **Performance**: Use `useCallback` for event handlers, `useMemo` for expensive computations +- **Custom Hooks**: Prefix with `use`, place in `src/hooks/`, return typed objects +- **Internationalization**: Use `useTranslation` hook from react-i18next, access with `t('key')` +- **Component Structure**: Functional components only, no class components +- **Props**: Define interfaces for all component props, place in `.interface.ts` files +- **Loading States**: Use object state for multiple loading states: `useState>({})` +- **Error Handling**: Use `showErrorToast` and `showSuccessToast` utilities from ToastUtils +- **Navigation**: Use `useNavigate` from react-router-dom, not direct history manipulation +- **Data Fetching**: Async functions with try-catch blocks, update loading states appropriately + +### State Management +- Use Zustand stores for global state (e.g., `useLimitStore`, `useWelcomeStore`) +- Keep component state local when possible with `useState` +- Use context providers for feature-specific shared state (e.g., `ApplicationsProvider`) + +### Styling + +- **MUI Migration**: The project is gradually migrating from Ant Design to Material-UI (MUI) v7.3.1 +- **Preferred Approach**: Use MUI components v7.3.1 and styles wherever possible for new features +- **Theme and Styles**: MUI theme data and styles are defined in `openmetadata-ui-core-components` +- **Colors and Design Tokens**: Always reference theme colors and design tokens from the MUI theme, not hardcoded values +- **Legacy Components**: Ant Design components remain in existing code but should be replaced with MUI equivalents when refactoring +- Do not add unnecessary spacing between logs and code. +- In Java, avoid wildcards imports (e.g., use `import java.util.List;` instead of `import java.util.*;`) +- Custom styles in `.less` files with component-specific naming (legacy pattern) +- Follow BEM naming convention for custom CSS classes +- Use CSS modules where appropriate + +### UI considerations + +- Do not use string literals at any place. You should use useTranslation hook and use it like const {t} = useTranslation(). And for example if you want to have "Run" as string, you should be using { t('label.run') }, this label is defined in locales. + + +### Application Configuration +- Applications use `ApplicationsClassBase` for schema loading and configuration +- Dynamic imports handle application-specific schemas and assets +- Form schemas use React JSON Schema Form (RJSF) with custom UI widgets + +### Service Utilities +- Each service type has dedicated utility files (e.g., `DatabaseServiceUtils.tsx`) +- Connection schemas are imported statically and pre-resolved +- Service configurations use switch statements to map types to schemas + +### Type Safety +- All API responses have generated TypeScript interfaces in `generated/` +- Custom types extend base interfaces when needed +- Avoid type assertions unless absolutely necessary +- Use discriminated unions for action types and state variants + +## Database and Migrations + +- Flyway handles schema migrations in `bootstrap/sql/migrations/` +- Use Docker containers for local database setup +- Default MySQL, PostgreSQL supported as alternative +- Sample data loaded automatically in development environment + +## Security and Authentication + +- JWT-based authentication with OAuth2/SAML support +- Role-based access control defined in Java entities +- Security configurations in `conf/openmetadata.yaml` +- Never commit secrets - use environment variables or secure vaults + +## Code Generation Standards + +### Comments Policy +- **Do NOT add unnecessary comments** - write self-documenting code +- **NEVER add single-line comments that describe what the code obviously does** +- Only include comments for: + - Complex business logic that isn't obvious + - Non-obvious algorithms or workarounds + - Public API JavaDoc documentation + - TODO/FIXME with ticket references +- Bad examples (NEVER do this): + - `// Create user` before `createUser()` + - `// Get client` before `SdkClients.adminClient()` + - `// Verify domain is set` before `assertNotNull(entity.getDomain())` + - `// User names are lowercased` when the code `toLowerCase()` makes it obvious +- If the code needs a comment to be understood, refactor the code to be clearer instead + +### Java Code Requirements +- **Always mention** running `mvn spotless:apply` when generating/modifying .java files +- Use clear, descriptive variable and method names instead of comments +- Follow existing project patterns and conventions +- Generate production-ready code, not tutorial code +- Create integration tests in openmetadata-integration-tests +- Do not use Fully Qualified Names in the code such as org.openmetadata.schema.type.Status instead import the class name +- Do not import wild-card packages instead import exactly required packages + +### TypeScript/Frontend Code Requirements +- **NEVER use `any` type** in TypeScript code - always use proper types +- Use `unknown` when the type is truly unknown and add type guards +- Import types from existing type definitions (e.g., `RJSFSchema` from `@rjsf/utils`) +- Follow ESLint rules strictly - the project enforces no-console, proper formatting +- Add `// eslint-disable-next-line` comments only when absolutely necessary +- **Import Organization** (in order): + 1. External libraries (React, Ant Design, etc.) + 2. Internal absolute imports from `generated/`, `constants/`, `hooks/`, etc. + 3. Relative imports for utilities and components + 4. Asset imports (SVGs, styles) + 5. Type imports grouped separately when needed + +### Python Code Requirements +- **Use pytest, not unittest** - write tests using pytest style with plain `assert` statements +- Use pytest fixtures for test setup instead of `setUp`/`tearDown` methods +- Use `unittest.mock` for mocking (MagicMock, patch) - this is compatible with pytest +- Test classes should not inherit from `TestCase` - use plain classes prefixed with `Test` +- Use `assert x == y` instead of `self.assertEqual(x, y)` +- Use `assert x is None` instead of `self.assertIsNone(x)` +- Use `assert "text" in string` instead of `self.assertIn("text", string)` + +### Python Ingestion Connector Guidelines +- **Keep connector-specific logic in connector-specific files**, not in generic/shared files like `builders.py` +- Example: Redshift IAM auth should be in `ingestion/src/metadata/ingestion/source/database/redshift/connection.py`, not in `ingestion/src/metadata/ingestion/connections/builders.py` +- This keeps the codebase modular and prevents generic utilities from becoming cluttered with connector-specific edge cases + +### Testing Philosophy +- **Test real behavior, not mock wiring** - if a test requires mocking 3+ classes just to verify a method call, it's testing the wrong thing +- **Prefer integration tests** over heavily-mocked unit tests. This project has full integration test infrastructure (OpenMetadataApplicationTest, Docker containers, real OpenSearch). Use it. +- **Mocks are for boundaries, not internals** - mock external services (HTTP clients, third-party APIs), not your own classes. If you're mocking static methods left and right to test internal plumbing, write an integration test instead. +- **A test that mocks everything proves nothing** - it only verifies that your mocks are wired correctly, not that the system works +- **Ask "what breaks if this test passes but the code is wrong?"** - if the answer is "nothing, because everything real is mocked out", delete the test and write a better one +- **Test the outcome, not the implementation** - assert on observable results (API responses, database state, stats values) rather than verifying internal method calls with `verify()` + +### Response Format +- Provide clean code blocks without unnecessary explanations +- Assume readers are experienced developers +- Focus on functionality over education diff --git a/bootstrap/sql/migrations/native/1.13.0/mysql/schemaChanges.sql b/bootstrap/sql/migrations/native/1.13.0/mysql/schemaChanges.sql new file mode 100644 index 000000000000..69fd1cb3b006 --- /dev/null +++ b/bootstrap/sql/migrations/native/1.13.0/mysql/schemaChanges.sql @@ -0,0 +1,6 @@ +-- Add changeDescriptionDoc generated column to entity_extension for efficient field-change filtering +-- Supports filtering entity versions by specific metadata changes (e.g., tags, schema, description) +ALTER TABLE entity_extension + ADD COLUMN changeDescriptionDoc TEXT + GENERATED ALWAYS AS (JSON_UNQUOTE(JSON_EXTRACT(json, '$.changeDescription'))) + STORED; diff --git a/bootstrap/sql/migrations/native/1.13.0/postgres/schemaChanges.sql b/bootstrap/sql/migrations/native/1.13.0/postgres/schemaChanges.sql new file mode 100644 index 000000000000..017aca30e38d --- /dev/null +++ b/bootstrap/sql/migrations/native/1.13.0/postgres/schemaChanges.sql @@ -0,0 +1,6 @@ +-- Add changeDescriptionDoc generated column to entity_extension for efficient field-change filtering +-- Supports filtering entity versions by specific metadata changes (e.g., tags, schema, description) +ALTER TABLE entity_extension + ADD COLUMN IF NOT EXISTS changeDescriptionDoc TEXT + GENERATED ALWAYS AS (json ->> 'changeDescription') + STORED; diff --git a/ingestion/src/metadata/ingestion/ometa/mixins/version_mixin.py b/ingestion/src/metadata/ingestion/ometa/mixins/version_mixin.py index 255a296827bf..d60a20e77815 100644 --- a/ingestion/src/metadata/ingestion/ometa/mixins/version_mixin.py +++ b/ingestion/src/metadata/ingestion/ometa/mixins/version_mixin.py @@ -91,6 +91,7 @@ def get_list_entity_versions( entity: Type[T], limit: Optional[int] = None, offset: Optional[int] = None, + field_changed: Optional[str] = None, ) -> Union[Response, EntityVersionHistory]: """ Retrieve the list of versions for a specific entity @@ -105,6 +106,8 @@ def get_list_entity_versions( maximum number of versions to return offset: Optional[int] offset for pagination + field_changed: Optional[str] + filter versions by field name that was changed Returns ------- @@ -118,6 +121,8 @@ def get_list_entity_versions( params["limit"] = limit if offset is not None: params["offset"] = offset + if field_changed is not None: + params["fieldChanged"] = field_changed resp = self.client.get( f"{self.get_suffix(entity)}/{path}", data=params if params else None @@ -127,3 +132,49 @@ def get_list_entity_versions( return resp return EntityVersionHistory(**resp) + + def get_entity_history_by_timeline( + self, + entity: Type[T], + start_ts: int, + end_ts: int, + limit: int = 10, + before: Optional[str] = None, + after: Optional[str] = None, + ) -> Response: + """ + Retrieve entity versions within a time range + + Parameters + ---------- + entity: T + the entity type + start_ts: int + start timestamp in milliseconds since epoch + end_ts: int + end timestamp in milliseconds since epoch + limit: int + maximum number of results to return + before: Optional[str] + cursor for backward pagination + after: Optional[str] + cursor for forward pagination + + Returns + ------- + Response + paginated list of entity versions within the time range + """ + params = { + "startTs": start_ts, + "endTs": end_ts, + "limit": limit, + } + if before is not None: + params["before"] = before + if after is not None: + params["after"] = after + + return self.client.get( + f"{self.get_suffix(entity)}/history", data=params + ) diff --git a/ingestion/src/metadata/sdk/entities/base.py b/ingestion/src/metadata/sdk/entities/base.py index 43552f342ec5..7ddeace28fcd 100644 --- a/ingestion/src/metadata/sdk/entities/base.py +++ b/ingestion/src/metadata/sdk/entities/base.py @@ -323,6 +323,7 @@ def get_versions( entity_id: UuidLike, limit: Optional[int] = None, offset: Optional[int] = None, + field_changed: Optional[str] = None, ) -> Sequence[TEntity]: """Fetch all historical versions for an entity.""" @@ -338,10 +339,36 @@ def get_versions( kwargs["limit"] = limit if offset is not None: kwargs["offset"] = offset + if field_changed is not None: + kwargs["field_changed"] = field_changed history = list_versions(**kwargs) versions = cast(Sequence[Any], getattr(history, "versions", []) or []) return [cls._coerce_entity(item) for item in versions] + @classmethod + def get_versions_by_timeline( + cls, + start_ts: int, + end_ts: int, + limit: int = 10, + before: Optional[str] = None, + after: Optional[str] = None, + ) -> Any: + """Fetch entity versions within a time range.""" + + client = cls._get_client() + get_history = cast( + Callable[..., Any], getattr(client, "get_entity_history_by_timeline") + ) + return get_history( + entity=cls.entity_type(), + start_ts=start_ts, + end_ts=end_ts, + limit=limit, + before=before, + after=after, + ) + @classmethod def get_specific_version(cls, entity_id: UuidLike, version: str) -> TEntity: """Fetch a specific entity version.""" diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/APICollectionResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/APICollectionResourceIT.java index 81042979efd8..769f18a25195 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/APICollectionResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/APICollectionResourceIT.java @@ -159,6 +159,14 @@ protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offse return SdkClients.adminClient().apiCollections().getVersionList(id, limit, offset); } + @Override + protected EntityHistory getVersionHistoryWithFieldChanged( + UUID id, int limit, int offset, String fieldChanged) { + return SdkClients.adminClient() + .apiCollections() + .getVersionList(id, limit, offset, fieldChanged); + } + @Override protected APICollection getVersion(UUID id, Double version) { return SdkClients.adminClient().apiCollections().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/APIEndpointResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/APIEndpointResourceIT.java index 0c745cc218e6..f9789560ea71 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/APIEndpointResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/APIEndpointResourceIT.java @@ -190,6 +190,12 @@ protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offse return SdkClients.adminClient().apiEndpoints().getVersionList(id, limit, offset); } + @Override + protected EntityHistory getVersionHistoryWithFieldChanged( + UUID id, int limit, int offset, String fieldChanged) { + return SdkClients.adminClient().apiEndpoints().getVersionList(id, limit, offset, fieldChanged); + } + @Override protected APIEndpoint getVersion(UUID id, Double version) { return SdkClients.adminClient().apiEndpoints().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/APIServiceResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/APIServiceResourceIT.java index 7fd09c8101f4..b5612a4c4167 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/APIServiceResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/APIServiceResourceIT.java @@ -147,6 +147,12 @@ protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offse return SdkClients.adminClient().apiServices().getVersionList(id, limit, offset); } + @Override + protected EntityHistory getVersionHistoryWithFieldChanged( + UUID id, int limit, int offset, String fieldChanged) { + return SdkClients.adminClient().apiServices().getVersionList(id, limit, offset, fieldChanged); + } + @Override protected ApiService getVersion(UUID id, Double version) { return SdkClients.adminClient().apiServices().getVersion(id.toString(), version); 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 b69b081dd6b3..cf484a08cdd5 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 @@ -1440,6 +1440,32 @@ void get_specificVersion_200(TestNamespace ns) { } } + @Test + void get_entityVersionHistory_fieldChanged_200(TestNamespace ns) { + if (!supportsPatch) return; + + K createRequest = createMinimalRequest(ns); + T created = createEntity(createRequest); + + created.setDescription("Description change for fieldChanged test"); + patchEntity(created.getId().toString(), created); + + org.openmetadata.schema.type.EntityHistory filtered = + getVersionHistoryWithFieldChanged(created.getId(), 100, 0, "description"); + assertNotNull(filtered, "Filtered version history should not be null"); + assertNotNull(filtered.getPaging(), "Paging metadata should be present"); + assertTrue( + filtered.getVersions().size() >= 1, + "Should have at least 1 version with description change"); + assertTrue(filtered.getPaging().getTotal() >= 1, "Total should reflect filtered count"); + + org.openmetadata.schema.type.EntityHistory noMatch = + getVersionHistoryWithFieldChanged(created.getId(), 100, 0, "nonExistentField_xyz_12345"); + assertNotNull(noMatch); + assertEquals(0, noMatch.getVersions().size(), "No versions should match a bogus field name"); + assertEquals(0, (int) noMatch.getPaging().getTotal()); + } + protected org.openmetadata.schema.type.EntityHistory getVersionHistory(UUID id) { throw new UnsupportedOperationException( "Version history not implemented - override in subclass"); @@ -1451,6 +1477,12 @@ protected org.openmetadata.schema.type.EntityHistory getVersionHistoryPaginated( "Paginated version history not implemented - override in subclass"); } + protected org.openmetadata.schema.type.EntityHistory getVersionHistoryWithFieldChanged( + UUID id, int limit, int offset, String fieldChanged) { + throw new UnsupportedOperationException( + "fieldChanged version history not implemented - override in subclass"); + } + protected T getVersion(UUID id, Double version) { throw new UnsupportedOperationException("Get version not implemented - override in subclass"); } diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/BotResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/BotResourceIT.java index 895caf553864..dca1c214d29f 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/BotResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/BotResourceIT.java @@ -173,6 +173,12 @@ protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offse return SdkClients.adminClient().bots().getVersionList(id, limit, offset); } + @Override + protected EntityHistory getVersionHistoryWithFieldChanged( + UUID id, int limit, int offset, String fieldChanged) { + return SdkClients.adminClient().bots().getVersionList(id, limit, offset, fieldChanged); + } + @Override protected Bot getVersion(UUID id, Double version) { return SdkClients.adminClient().bots().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/ChartResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/ChartResourceIT.java index 65d9e5d71b08..1bd2e5d7517f 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/ChartResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/ChartResourceIT.java @@ -161,6 +161,12 @@ protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offse return SdkClients.adminClient().charts().getVersionList(id, limit, offset); } + @Override + protected EntityHistory getVersionHistoryWithFieldChanged( + UUID id, int limit, int offset, String fieldChanged) { + return SdkClients.adminClient().charts().getVersionList(id, limit, offset, fieldChanged); + } + @Override protected Chart getVersion(UUID id, Double version) { return SdkClients.adminClient().charts().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/ClassificationResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/ClassificationResourceIT.java index a2b2bf201b48..ea46f7a41e13 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/ClassificationResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/ClassificationResourceIT.java @@ -157,6 +157,14 @@ protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offse return SdkClients.adminClient().classifications().getVersionList(id, limit, offset); } + @Override + protected EntityHistory getVersionHistoryWithFieldChanged( + UUID id, int limit, int offset, String fieldChanged) { + return SdkClients.adminClient() + .classifications() + .getVersionList(id, limit, offset, fieldChanged); + } + @Override protected Classification getVersion(UUID id, Double version) { return SdkClients.adminClient().classifications().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/ContainerResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/ContainerResourceIT.java index 8d2ba161b45a..8c3b2895f5d3 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/ContainerResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/ContainerResourceIT.java @@ -160,6 +160,12 @@ protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offse return SdkClients.adminClient().containers().getVersionList(id, limit, offset); } + @Override + protected EntityHistory getVersionHistoryWithFieldChanged( + UUID id, int limit, int offset, String fieldChanged) { + return SdkClients.adminClient().containers().getVersionList(id, limit, offset, fieldChanged); + } + @Override protected Container getVersion(UUID id, Double version) { return SdkClients.adminClient().containers().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DashboardDataModelResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DashboardDataModelResourceIT.java index d566ab38deba..c05f17ce28ed 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DashboardDataModelResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DashboardDataModelResourceIT.java @@ -192,6 +192,14 @@ protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offse return SdkClients.adminClient().dashboardDataModels().getVersionList(id, limit, offset); } + @Override + protected EntityHistory getVersionHistoryWithFieldChanged( + UUID id, int limit, int offset, String fieldChanged) { + return SdkClients.adminClient() + .dashboardDataModels() + .getVersionList(id, limit, offset, fieldChanged); + } + @Override protected DashboardDataModel getVersion(UUID id, Double version) { return SdkClients.adminClient().dashboardDataModels().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DashboardResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DashboardResourceIT.java index 6d696b02cb21..c8726ce87052 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DashboardResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DashboardResourceIT.java @@ -162,6 +162,12 @@ protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offse return SdkClients.adminClient().dashboards().getVersionList(id, limit, offset); } + @Override + protected EntityHistory getVersionHistoryWithFieldChanged( + UUID id, int limit, int offset, String fieldChanged) { + return SdkClients.adminClient().dashboards().getVersionList(id, limit, offset, fieldChanged); + } + @Override protected Dashboard getVersion(UUID id, Double version) { return SdkClients.adminClient().dashboards().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DashboardServiceResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DashboardServiceResourceIT.java index 1c1c097202d1..2c4676c9145b 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DashboardServiceResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DashboardServiceResourceIT.java @@ -148,6 +148,14 @@ protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offse return SdkClients.adminClient().dashboardServices().getVersionList(id, limit, offset); } + @Override + protected EntityHistory getVersionHistoryWithFieldChanged( + UUID id, int limit, int offset, String fieldChanged) { + return SdkClients.adminClient() + .dashboardServices() + .getVersionList(id, limit, offset, fieldChanged); + } + @Override protected DashboardService getVersion(UUID id, Double version) { return SdkClients.adminClient().dashboardServices().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DataContractResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DataContractResourceIT.java index 3203219e7a1b..e71d7fbcb507 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DataContractResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DataContractResourceIT.java @@ -233,6 +233,12 @@ protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offse return SdkClients.adminClient().dataContracts().getVersionList(id, limit, offset); } + @Override + protected EntityHistory getVersionHistoryWithFieldChanged( + UUID id, int limit, int offset, String fieldChanged) { + return SdkClients.adminClient().dataContracts().getVersionList(id, limit, offset, fieldChanged); + } + @Override protected DataContract getVersion(UUID id, Double version) { return SdkClients.adminClient().dataContracts().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DataInsightChartResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DataInsightChartResourceIT.java index 14a9fbe10d4f..6607636cca8c 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DataInsightChartResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DataInsightChartResourceIT.java @@ -159,6 +159,12 @@ protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offse return getDataInsightChartService().getVersionList(id, limit, offset); } + @Override + protected EntityHistory getVersionHistoryWithFieldChanged( + UUID id, int limit, int offset, String fieldChanged) { + return getDataInsightChartService().getVersionList(id, limit, offset, fieldChanged); + } + @Override protected DataInsightChart getVersion(UUID id, Double version) { return getDataInsightChartService().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DataProductResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DataProductResourceIT.java index 8c4c5ebbb97c..c82619e2ae7c 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DataProductResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DataProductResourceIT.java @@ -206,6 +206,12 @@ protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offse return SdkClients.adminClient().dataProducts().getVersionList(id, limit, offset); } + @Override + protected EntityHistory getVersionHistoryWithFieldChanged( + UUID id, int limit, int offset, String fieldChanged) { + return SdkClients.adminClient().dataProducts().getVersionList(id, limit, offset, fieldChanged); + } + @Override protected DataProduct getVersion(UUID id, Double version) { return SdkClients.adminClient().dataProducts().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DatabaseResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DatabaseResourceIT.java index 3439c8a01ca0..8950997ebf6a 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DatabaseResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DatabaseResourceIT.java @@ -1354,6 +1354,12 @@ protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offse return SdkClients.adminClient().databases().getVersionList(id, limit, offset); } + @Override + protected EntityHistory getVersionHistoryWithFieldChanged( + UUID id, int limit, int offset, String fieldChanged) { + return SdkClients.adminClient().databases().getVersionList(id, limit, offset, fieldChanged); + } + @Override protected Database getVersion(UUID id, Double version) { return SdkClients.adminClient().databases().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DatabaseSchemaResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DatabaseSchemaResourceIT.java index 777475e36e95..734d31c92f85 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DatabaseSchemaResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DatabaseSchemaResourceIT.java @@ -175,6 +175,14 @@ protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offse return SdkClients.adminClient().databaseSchemas().getVersionList(id, limit, offset); } + @Override + protected EntityHistory getVersionHistoryWithFieldChanged( + UUID id, int limit, int offset, String fieldChanged) { + return SdkClients.adminClient() + .databaseSchemas() + .getVersionList(id, limit, offset, fieldChanged); + } + @Override protected DatabaseSchema getVersion(UUID id, Double version) { return SdkClients.adminClient().databaseSchemas().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DatabaseServiceResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DatabaseServiceResourceIT.java index 32f4189e12fb..8dfbc7ea7abd 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DatabaseServiceResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DatabaseServiceResourceIT.java @@ -171,6 +171,14 @@ protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offse return SdkClients.adminClient().databaseServices().getVersionList(id, limit, offset); } + @Override + protected EntityHistory getVersionHistoryWithFieldChanged( + UUID id, int limit, int offset, String fieldChanged) { + return SdkClients.adminClient() + .databaseServices() + .getVersionList(id, limit, offset, fieldChanged); + } + @Override protected DatabaseService getVersion(UUID id, Double version) { return SdkClients.adminClient().databaseServices().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DomainResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DomainResourceIT.java index a8ca443f045d..a188bdb3ed79 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DomainResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DomainResourceIT.java @@ -435,6 +435,12 @@ protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offse return SdkClients.adminClient().domains().getVersionList(id, limit, offset); } + @Override + protected EntityHistory getVersionHistoryWithFieldChanged( + UUID id, int limit, int offset, String fieldChanged) { + return SdkClients.adminClient().domains().getVersionList(id, limit, offset, fieldChanged); + } + @Override protected Domain getVersion(UUID id, Double version) { return SdkClients.adminClient().domains().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DriveServiceResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DriveServiceResourceIT.java index f40168fb8b1a..b16e246a9ab6 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DriveServiceResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/DriveServiceResourceIT.java @@ -161,6 +161,12 @@ protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offse return SdkClients.adminClient().driveServices().getVersionList(id, limit, offset); } + @Override + protected EntityHistory getVersionHistoryWithFieldChanged( + UUID id, int limit, int offset, String fieldChanged) { + return SdkClients.adminClient().driveServices().getVersionList(id, limit, offset, fieldChanged); + } + @Override protected DriveService getVersion(UUID id, Double version) { return SdkClients.adminClient().driveServices().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/EventSubscriptionResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/EventSubscriptionResourceIT.java index edd87bad2685..72c02377dec6 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/EventSubscriptionResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/EventSubscriptionResourceIT.java @@ -188,6 +188,14 @@ protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offse return SdkClients.adminClient().eventSubscriptions().getVersionList(id, limit, offset); } + @Override + protected EntityHistory getVersionHistoryWithFieldChanged( + UUID id, int limit, int offset, String fieldChanged) { + return SdkClients.adminClient() + .eventSubscriptions() + .getVersionList(id, limit, offset, fieldChanged); + } + @Override protected EventSubscription getVersion(UUID id, Double version) { return SdkClients.adminClient().eventSubscriptions().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/GlossaryResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/GlossaryResourceIT.java index 08708b7bdd62..d12d173a5969 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/GlossaryResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/GlossaryResourceIT.java @@ -1033,6 +1033,12 @@ protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offse return SdkClients.adminClient().glossaries().getVersionList(id, limit, offset); } + @Override + protected EntityHistory getVersionHistoryWithFieldChanged( + UUID id, int limit, int offset, String fieldChanged) { + return SdkClients.adminClient().glossaries().getVersionList(id, limit, offset, fieldChanged); + } + @Override protected Glossary getVersion(UUID id, Double version) { return SdkClients.adminClient().glossaries().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/GlossaryTermResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/GlossaryTermResourceIT.java index dc8171f8366d..6733c805621f 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/GlossaryTermResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/GlossaryTermResourceIT.java @@ -175,6 +175,12 @@ protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offse return SdkClients.adminClient().glossaryTerms().getVersionList(id, limit, offset); } + @Override + protected EntityHistory getVersionHistoryWithFieldChanged( + UUID id, int limit, int offset, String fieldChanged) { + return SdkClients.adminClient().glossaryTerms().getVersionList(id, limit, offset, fieldChanged); + } + @Override protected GlossaryTerm getVersion(UUID id, Double version) { return SdkClients.adminClient().glossaryTerms().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/IngestionPipelineResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/IngestionPipelineResourceIT.java index 1a4af12ac26a..e71b2a388a3b 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/IngestionPipelineResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/IngestionPipelineResourceIT.java @@ -228,6 +228,14 @@ protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offse return SdkClients.adminClient().ingestionPipelines().getVersionList(id, limit, offset); } + @Override + protected EntityHistory getVersionHistoryWithFieldChanged( + UUID id, int limit, int offset, String fieldChanged) { + return SdkClients.adminClient() + .ingestionPipelines() + .getVersionList(id, limit, offset, fieldChanged); + } + @Override protected IngestionPipeline getVersion(UUID id, Double version) { return SdkClients.adminClient().ingestionPipelines().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/KpiResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/KpiResourceIT.java index 9ee527a30e05..32c88fe0b4c8 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/KpiResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/KpiResourceIT.java @@ -172,6 +172,12 @@ protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offse return getKpiService().getVersionList(id, limit, offset); } + @Override + protected EntityHistory getVersionHistoryWithFieldChanged( + UUID id, int limit, int offset, String fieldChanged) { + return getKpiService().getVersionList(id, limit, offset, fieldChanged); + } + @Override protected Kpi getVersion(UUID id, Double version) { return getKpiService().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/LLMModelResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/LLMModelResourceIT.java index 041b974d1e4f..35f4a3d2d1fe 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/LLMModelResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/LLMModelResourceIT.java @@ -150,6 +150,12 @@ protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offse return SdkClients.adminClient().llmModels().getVersionList(id, limit, offset); } + @Override + protected EntityHistory getVersionHistoryWithFieldChanged( + UUID id, int limit, int offset, String fieldChanged) { + return SdkClients.adminClient().llmModels().getVersionList(id, limit, offset, fieldChanged); + } + @Override protected LLMModel getVersion(UUID id, Double version) { return SdkClients.adminClient().llmModels().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/LLMServiceResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/LLMServiceResourceIT.java index 3382ebb6cb4b..fb6db997ee72 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/LLMServiceResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/LLMServiceResourceIT.java @@ -139,6 +139,12 @@ protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offse return SdkClients.adminClient().llmServices().getVersionList(id, limit, offset); } + @Override + protected EntityHistory getVersionHistoryWithFieldChanged( + UUID id, int limit, int offset, String fieldChanged) { + return SdkClients.adminClient().llmServices().getVersionList(id, limit, offset, fieldChanged); + } + @Override protected LLMService getVersion(UUID id, Double version) { return SdkClients.adminClient().llmServices().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/LearningResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/LearningResourceIT.java index 3cd1af36d8c3..926defe21fd8 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/LearningResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/LearningResourceIT.java @@ -177,6 +177,12 @@ protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offse return getLearningResourceService().getVersionList(id, limit, offset); } + @Override + protected EntityHistory getVersionHistoryWithFieldChanged( + UUID id, int limit, int offset, String fieldChanged) { + return getLearningResourceService().getVersionList(id, limit, offset, fieldChanged); + } + @Override protected LearningResource getVersion(UUID id, Double version) { return getLearningResourceService().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/MessagingServiceResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/MessagingServiceResourceIT.java index a509045f2013..a4e7aca5216d 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/MessagingServiceResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/MessagingServiceResourceIT.java @@ -144,6 +144,14 @@ protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offse return SdkClients.adminClient().messagingServices().getVersionList(id, limit, offset); } + @Override + protected EntityHistory getVersionHistoryWithFieldChanged( + UUID id, int limit, int offset, String fieldChanged) { + return SdkClients.adminClient() + .messagingServices() + .getVersionList(id, limit, offset, fieldChanged); + } + @Override protected MessagingService getVersion(UUID id, Double version) { return SdkClients.adminClient().messagingServices().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/MetadataServiceResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/MetadataServiceResourceIT.java index 8ca252e30d8a..68896e615ec1 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/MetadataServiceResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/MetadataServiceResourceIT.java @@ -154,6 +154,14 @@ protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offse return SdkClients.adminClient().metadataServices().getVersionList(id, limit, offset); } + @Override + protected EntityHistory getVersionHistoryWithFieldChanged( + UUID id, int limit, int offset, String fieldChanged) { + return SdkClients.adminClient() + .metadataServices() + .getVersionList(id, limit, offset, fieldChanged); + } + @Override protected MetadataService getVersion(UUID id, Double version) { return SdkClients.adminClient().metadataServices().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/MetricResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/MetricResourceIT.java index abea445768ae..32fa33350cc2 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/MetricResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/MetricResourceIT.java @@ -146,6 +146,12 @@ protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offse return SdkClients.adminClient().metrics().getVersionList(id, limit, offset); } + @Override + protected EntityHistory getVersionHistoryWithFieldChanged( + UUID id, int limit, int offset, String fieldChanged) { + return SdkClients.adminClient().metrics().getVersionList(id, limit, offset, fieldChanged); + } + @Override protected Metric getVersion(UUID id, Double version) { return SdkClients.adminClient().metrics().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/MlModelResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/MlModelResourceIT.java index ca3654da0539..e0c95eb5b6b1 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/MlModelResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/MlModelResourceIT.java @@ -163,6 +163,12 @@ protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offse return SdkClients.adminClient().mlModels().getVersionList(id, limit, offset); } + @Override + protected EntityHistory getVersionHistoryWithFieldChanged( + UUID id, int limit, int offset, String fieldChanged) { + return SdkClients.adminClient().mlModels().getVersionList(id, limit, offset, fieldChanged); + } + @Override protected MlModel getVersion(UUID id, Double version) { return SdkClients.adminClient().mlModels().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/MlModelServiceResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/MlModelServiceResourceIT.java index 2cd3d3ddf2b9..6e1b74b5b78c 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/MlModelServiceResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/MlModelServiceResourceIT.java @@ -149,6 +149,14 @@ protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offse return SdkClients.adminClient().mlModelServices().getVersionList(id, limit, offset); } + @Override + protected EntityHistory getVersionHistoryWithFieldChanged( + UUID id, int limit, int offset, String fieldChanged) { + return SdkClients.adminClient() + .mlModelServices() + .getVersionList(id, limit, offset, fieldChanged); + } + @Override protected MlModelService getVersion(UUID id, Double version) { return SdkClients.adminClient().mlModelServices().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/NotificationTemplateResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/NotificationTemplateResourceIT.java index 544939e05b4f..55aefa973c0d 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/NotificationTemplateResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/NotificationTemplateResourceIT.java @@ -848,6 +848,14 @@ protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offse return SdkClients.adminClient().notificationTemplates().getVersionList(id, limit, offset); } + @Override + protected EntityHistory getVersionHistoryWithFieldChanged( + UUID id, int limit, int offset, String fieldChanged) { + return SdkClients.adminClient() + .notificationTemplates() + .getVersionList(id, limit, offset, fieldChanged); + } + @Override protected NotificationTemplate getVersion(UUID id, Double version) { return SdkClients.adminClient().notificationTemplates().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/PersonaResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/PersonaResourceIT.java index 3662ca78e42d..52101bdc08a7 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/PersonaResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/PersonaResourceIT.java @@ -142,6 +142,12 @@ protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offse return SdkClients.adminClient().personas().getVersionList(id, limit, offset); } + @Override + protected EntityHistory getVersionHistoryWithFieldChanged( + UUID id, int limit, int offset, String fieldChanged) { + return SdkClients.adminClient().personas().getVersionList(id, limit, offset, fieldChanged); + } + @Override protected Persona getVersion(UUID id, Double version) { return SdkClients.adminClient().personas().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/PipelineResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/PipelineResourceIT.java index 216d9289534e..aaa1b3cfef46 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/PipelineResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/PipelineResourceIT.java @@ -169,6 +169,12 @@ protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offse return SdkClients.adminClient().pipelines().getVersionList(id, limit, offset); } + @Override + protected EntityHistory getVersionHistoryWithFieldChanged( + UUID id, int limit, int offset, String fieldChanged) { + return SdkClients.adminClient().pipelines().getVersionList(id, limit, offset, fieldChanged); + } + @Override protected Pipeline getVersion(UUID id, Double version) { return SdkClients.adminClient().pipelines().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/PipelineServiceResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/PipelineServiceResourceIT.java index 8969a31289a5..2369b0ec94c0 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/PipelineServiceResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/PipelineServiceResourceIT.java @@ -142,6 +142,14 @@ protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offse return SdkClients.adminClient().pipelineServices().getVersionList(id, limit, offset); } + @Override + protected EntityHistory getVersionHistoryWithFieldChanged( + UUID id, int limit, int offset, String fieldChanged) { + return SdkClients.adminClient() + .pipelineServices() + .getVersionList(id, limit, offset, fieldChanged); + } + @Override protected PipelineService getVersion(UUID id, Double version) { return SdkClients.adminClient().pipelineServices().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/PolicyResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/PolicyResourceIT.java index 293b4ce920c2..4dcd689820ef 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/PolicyResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/PolicyResourceIT.java @@ -498,6 +498,12 @@ protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offse return SdkClients.adminClient().policies().getVersionList(id, limit, offset); } + @Override + protected EntityHistory getVersionHistoryWithFieldChanged( + UUID id, int limit, int offset, String fieldChanged) { + return SdkClients.adminClient().policies().getVersionList(id, limit, offset, fieldChanged); + } + @Override protected Policy getVersion(UUID id, Double version) { return SdkClients.adminClient().policies().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/QueryResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/QueryResourceIT.java index a544af9f6e5d..8e542be29157 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/QueryResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/QueryResourceIT.java @@ -259,6 +259,12 @@ protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offse return SdkClients.adminClient().queries().getVersionList(id, limit, offset); } + @Override + protected EntityHistory getVersionHistoryWithFieldChanged( + UUID id, int limit, int offset, String fieldChanged) { + return SdkClients.adminClient().queries().getVersionList(id, limit, offset, fieldChanged); + } + @Override protected Query getVersion(UUID id, Double version) { return SdkClients.adminClient().queries().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/RoleResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/RoleResourceIT.java index 2b52833162c7..70848c3f807b 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/RoleResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/RoleResourceIT.java @@ -384,6 +384,12 @@ protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offse return SdkClients.adminClient().roles().getVersionList(id, limit, offset); } + @Override + protected EntityHistory getVersionHistoryWithFieldChanged( + UUID id, int limit, int offset, String fieldChanged) { + return SdkClients.adminClient().roles().getVersionList(id, limit, offset, fieldChanged); + } + @Override protected Role getVersion(UUID id, Double version) { return SdkClients.adminClient().roles().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/SearchIndexResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/SearchIndexResourceIT.java index 0e6f9d5a9ed7..6d5987e01624 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/SearchIndexResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/SearchIndexResourceIT.java @@ -188,6 +188,12 @@ protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offse return SdkClients.adminClient().searchIndexes().getVersionList(id, limit, offset); } + @Override + protected EntityHistory getVersionHistoryWithFieldChanged( + UUID id, int limit, int offset, String fieldChanged) { + return SdkClients.adminClient().searchIndexes().getVersionList(id, limit, offset, fieldChanged); + } + @Override protected SearchIndex getVersion(UUID id, Double version) { return SdkClients.adminClient().searchIndexes().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/SearchServiceResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/SearchServiceResourceIT.java index 52adaa1f3612..f276efa683db 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/SearchServiceResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/SearchServiceResourceIT.java @@ -139,6 +139,14 @@ protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offse return SdkClients.adminClient().searchServices().getVersionList(id, limit, offset); } + @Override + protected EntityHistory getVersionHistoryWithFieldChanged( + UUID id, int limit, int offset, String fieldChanged) { + return SdkClients.adminClient() + .searchServices() + .getVersionList(id, limit, offset, fieldChanged); + } + @Override protected SearchService getVersion(UUID id, Double version) { return SdkClients.adminClient().searchServices().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/StorageServiceResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/StorageServiceResourceIT.java index 8f6c07700930..3ca81dd461b0 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/StorageServiceResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/StorageServiceResourceIT.java @@ -135,6 +135,14 @@ protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offse return SdkClients.adminClient().storageServices().getVersionList(id, limit, offset); } + @Override + protected EntityHistory getVersionHistoryWithFieldChanged( + UUID id, int limit, int offset, String fieldChanged) { + return SdkClients.adminClient() + .storageServices() + .getVersionList(id, limit, offset, fieldChanged); + } + @Override protected StorageService getVersion(UUID id, Double version) { return SdkClients.adminClient().storageServices().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/StoredProcedureResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/StoredProcedureResourceIT.java index 6a6b4f110083..4ab7137ec202 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/StoredProcedureResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/StoredProcedureResourceIT.java @@ -177,6 +177,14 @@ protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offse return SdkClients.adminClient().storedProcedures().getVersionList(id, limit, offset); } + @Override + protected EntityHistory getVersionHistoryWithFieldChanged( + UUID id, int limit, int offset, String fieldChanged) { + return SdkClients.adminClient() + .storedProcedures() + .getVersionList(id, limit, offset, fieldChanged); + } + @Override protected StoredProcedure getVersion(UUID id, Double version) { return SdkClients.adminClient().storedProcedures().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TableResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TableResourceIT.java index 9c8f129b2771..3defe69f849e 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TableResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TableResourceIT.java @@ -4911,6 +4911,12 @@ protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offse return SdkClients.adminClient().tables().getVersionList(id, limit, offset); } + @Override + protected EntityHistory getVersionHistoryWithFieldChanged( + UUID id, int limit, int offset, String fieldChanged) { + return SdkClients.adminClient().tables().getVersionList(id, limit, offset, fieldChanged); + } + @Override protected Table getVersion(UUID id, Double version) { return SdkClients.adminClient().tables().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TagResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TagResourceIT.java index 354f7358d4e4..301e664acac4 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TagResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TagResourceIT.java @@ -178,6 +178,12 @@ protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offse return SdkClients.adminClient().tags().getVersionList(id, limit, offset); } + @Override + protected EntityHistory getVersionHistoryWithFieldChanged( + UUID id, int limit, int offset, String fieldChanged) { + return SdkClients.adminClient().tags().getVersionList(id, limit, offset, fieldChanged); + } + @Override protected Tag getVersion(UUID id, Double version) { return SdkClients.adminClient().tags().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TeamResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TeamResourceIT.java index 1c07e3e53ee6..d7be80f81462 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TeamResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TeamResourceIT.java @@ -1056,6 +1056,12 @@ protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offse return SdkClients.adminClient().teams().getVersionList(id, limit, offset); } + @Override + protected EntityHistory getVersionHistoryWithFieldChanged( + UUID id, int limit, int offset, String fieldChanged) { + return SdkClients.adminClient().teams().getVersionList(id, limit, offset, fieldChanged); + } + @Override protected Team getVersion(UUID id, Double version) { return SdkClients.adminClient().teams().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TestCaseResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TestCaseResourceIT.java index e755073d6950..70611eb44cda 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TestCaseResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TestCaseResourceIT.java @@ -275,6 +275,12 @@ protected EntityHistory getVersionHistoryPaginated(java.util.UUID id, int limit, return SdkClients.adminClient().testCases().getVersionList(id, limit, offset); } + @Override + protected EntityHistory getVersionHistoryWithFieldChanged( + java.util.UUID id, int limit, int offset, String fieldChanged) { + return SdkClients.adminClient().testCases().getVersionList(id, limit, offset, fieldChanged); + } + @Override protected TestCase getVersion(java.util.UUID id, Double version) { return SdkClients.adminClient().testCases().getVersion(id, version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TestDefinitionResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TestDefinitionResourceIT.java index 64cda661f912..777af1acc934 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TestDefinitionResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TestDefinitionResourceIT.java @@ -160,6 +160,14 @@ protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offse return SdkClients.adminClient().testDefinitions().getVersionList(id, limit, offset); } + @Override + protected EntityHistory getVersionHistoryWithFieldChanged( + UUID id, int limit, int offset, String fieldChanged) { + return SdkClients.adminClient() + .testDefinitions() + .getVersionList(id, limit, offset, fieldChanged); + } + @Override protected TestDefinition getVersion(UUID id, Double version) { return SdkClients.adminClient().testDefinitions().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TestSuiteResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TestSuiteResourceIT.java index 8a392a79abd0..f4f6efd7f719 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TestSuiteResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TestSuiteResourceIT.java @@ -183,6 +183,12 @@ protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offse return SdkClients.adminClient().testSuites().getVersionList(id, limit, offset); } + @Override + protected EntityHistory getVersionHistoryWithFieldChanged( + UUID id, int limit, int offset, String fieldChanged) { + return SdkClients.adminClient().testSuites().getVersionList(id, limit, offset, fieldChanged); + } + @Override protected TestSuite getVersion(UUID id, Double version) { return SdkClients.adminClient().testSuites().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TopicResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TopicResourceIT.java index f8ebccfe1e9e..4ebbf908cd50 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TopicResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TopicResourceIT.java @@ -162,6 +162,12 @@ protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offse return SdkClients.adminClient().topics().getVersionList(id, limit, offset); } + @Override + protected EntityHistory getVersionHistoryWithFieldChanged( + UUID id, int limit, int offset, String fieldChanged) { + return SdkClients.adminClient().topics().getVersionList(id, limit, offset, fieldChanged); + } + @Override protected Topic getVersion(UUID id, Double version) { return SdkClients.adminClient().topics().getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/UserResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/UserResourceIT.java index 459548bc2c94..5fde15c77ea8 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/UserResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/UserResourceIT.java @@ -2302,6 +2302,12 @@ protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offse return Users.getVersionList(id, limit, offset); } + @Override + protected EntityHistory getVersionHistoryWithFieldChanged( + UUID id, int limit, int offset, String fieldChanged) { + return Users.getVersionList(id, limit, offset, fieldChanged); + } + @Override protected User getVersion(UUID id, Double version) { return Users.getVersion(id.toString(), version); diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/WorksheetResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/WorksheetResourceIT.java index acab1d908554..8eb36d30d99c 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/WorksheetResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/WorksheetResourceIT.java @@ -175,6 +175,12 @@ protected EntityHistory getVersionHistoryPaginated(UUID id, int limit, int offse return getWorksheetService().getVersionList(id, limit, offset); } + @Override + protected EntityHistory getVersionHistoryWithFieldChanged( + UUID id, int limit, int offset, String fieldChanged) { + return getWorksheetService().getVersionList(id, limit, offset, fieldChanged); + } + @Override protected Worksheet getVersion(UUID id, Double version) { return getWorksheetService().getVersion(id.toString(), version); diff --git a/openmetadata-sdk/src/main/java/org/openmetadata/sdk/fluent/DatabaseSchemas.java b/openmetadata-sdk/src/main/java/org/openmetadata/sdk/fluent/DatabaseSchemas.java index 3af03f4777b7..438c6d7fc83b 100644 --- a/openmetadata-sdk/src/main/java/org/openmetadata/sdk/fluent/DatabaseSchemas.java +++ b/openmetadata-sdk/src/main/java/org/openmetadata/sdk/fluent/DatabaseSchemas.java @@ -123,6 +123,21 @@ public static org.openmetadata.schema.type.EntityHistory getVersionList( return getClient().databaseSchemas().getVersionList(id, limit, offset); } + public static org.openmetadata.schema.type.EntityHistory getVersionList( + java.util.UUID id, int limit, int offset, String fieldChanged) { + return getClient().databaseSchemas().getVersionList(id, limit, offset, fieldChanged); + } + + public static org.openmetadata.schema.utils.ResultList getEntityHistory( + long startTs, long endTs) { + return getClient().databaseSchemas().getEntityHistory(startTs, endTs); + } + + public static org.openmetadata.schema.utils.ResultList getEntityHistory( + long startTs, long endTs, int limit, String before, String after) { + return getClient().databaseSchemas().getEntityHistory(startTs, endTs, limit, before, after); + } + public static DatabaseSchema getVersion(String id, Double version) { return getClient().databaseSchemas().getVersion(id, version); } diff --git a/openmetadata-sdk/src/main/java/org/openmetadata/sdk/fluent/Databases.java b/openmetadata-sdk/src/main/java/org/openmetadata/sdk/fluent/Databases.java index 7b4d31c3a095..3cfc3c4cd151 100644 --- a/openmetadata-sdk/src/main/java/org/openmetadata/sdk/fluent/Databases.java +++ b/openmetadata-sdk/src/main/java/org/openmetadata/sdk/fluent/Databases.java @@ -123,6 +123,21 @@ public static org.openmetadata.schema.type.EntityHistory getVersionList( return getClient().databases().getVersionList(id, limit, offset); } + public static org.openmetadata.schema.type.EntityHistory getVersionList( + java.util.UUID id, int limit, int offset, String fieldChanged) { + return getClient().databases().getVersionList(id, limit, offset, fieldChanged); + } + + public static org.openmetadata.schema.utils.ResultList getEntityHistory( + long startTs, long endTs) { + return getClient().databases().getEntityHistory(startTs, endTs); + } + + public static org.openmetadata.schema.utils.ResultList getEntityHistory( + long startTs, long endTs, int limit, String before, String after) { + return getClient().databases().getEntityHistory(startTs, endTs, limit, before, after); + } + public static Database getVersion(String id, Double version) { return getClient().databases().getVersion(id, version); } diff --git a/openmetadata-sdk/src/main/java/org/openmetadata/sdk/fluent/Domains.java b/openmetadata-sdk/src/main/java/org/openmetadata/sdk/fluent/Domains.java index a4070333a6cc..9a7da31e853f 100644 --- a/openmetadata-sdk/src/main/java/org/openmetadata/sdk/fluent/Domains.java +++ b/openmetadata-sdk/src/main/java/org/openmetadata/sdk/fluent/Domains.java @@ -120,6 +120,21 @@ public static org.openmetadata.schema.type.EntityHistory getVersionList( return getClient().domains().getVersionList(id, limit, offset); } + public static org.openmetadata.schema.type.EntityHistory getVersionList( + java.util.UUID id, int limit, int offset, String fieldChanged) { + return getClient().domains().getVersionList(id, limit, offset, fieldChanged); + } + + public static org.openmetadata.schema.utils.ResultList getEntityHistory( + long startTs, long endTs) { + return getClient().domains().getEntityHistory(startTs, endTs); + } + + public static org.openmetadata.schema.utils.ResultList getEntityHistory( + long startTs, long endTs, int limit, String before, String after) { + return getClient().domains().getEntityHistory(startTs, endTs, limit, before, after); + } + public static Domain getVersion(String id, Double version) { return getClient().domains().getVersion(id, version); } diff --git a/openmetadata-sdk/src/main/java/org/openmetadata/sdk/fluent/Pipelines.java b/openmetadata-sdk/src/main/java/org/openmetadata/sdk/fluent/Pipelines.java index c87994739320..b525952f32d3 100644 --- a/openmetadata-sdk/src/main/java/org/openmetadata/sdk/fluent/Pipelines.java +++ b/openmetadata-sdk/src/main/java/org/openmetadata/sdk/fluent/Pipelines.java @@ -129,6 +129,21 @@ public static org.openmetadata.schema.type.EntityHistory getVersionList( return getClient().pipelines().getVersionList(id, limit, offset); } + public static org.openmetadata.schema.type.EntityHistory getVersionList( + java.util.UUID id, int limit, int offset, String fieldChanged) { + return getClient().pipelines().getVersionList(id, limit, offset, fieldChanged); + } + + public static org.openmetadata.schema.utils.ResultList getEntityHistory( + long startTs, long endTs) { + return getClient().pipelines().getEntityHistory(startTs, endTs); + } + + public static org.openmetadata.schema.utils.ResultList getEntityHistory( + long startTs, long endTs, int limit, String before, String after) { + return getClient().pipelines().getEntityHistory(startTs, endTs, limit, before, after); + } + public static Pipeline getVersion(String id, Double version) { return getClient().pipelines().getVersion(id, version); } diff --git a/openmetadata-sdk/src/main/java/org/openmetadata/sdk/fluent/Roles.java b/openmetadata-sdk/src/main/java/org/openmetadata/sdk/fluent/Roles.java index 2817cf33878c..70145ef9fe05 100644 --- a/openmetadata-sdk/src/main/java/org/openmetadata/sdk/fluent/Roles.java +++ b/openmetadata-sdk/src/main/java/org/openmetadata/sdk/fluent/Roles.java @@ -83,6 +83,21 @@ public static org.openmetadata.schema.type.EntityHistory getVersionList( return getClient().roles().getVersionList(id, limit, offset); } + public static org.openmetadata.schema.type.EntityHistory getVersionList( + java.util.UUID id, int limit, int offset, String fieldChanged) { + return getClient().roles().getVersionList(id, limit, offset, fieldChanged); + } + + public static org.openmetadata.schema.utils.ResultList getEntityHistory( + long startTs, long endTs) { + return getClient().roles().getEntityHistory(startTs, endTs); + } + + public static org.openmetadata.schema.utils.ResultList getEntityHistory( + long startTs, long endTs, int limit, String before, String after) { + return getClient().roles().getEntityHistory(startTs, endTs, limit, before, after); + } + public static Role getVersion(String id, Double version) { return getClient().roles().getVersion(id, version); } diff --git a/openmetadata-sdk/src/main/java/org/openmetadata/sdk/fluent/Tables.java b/openmetadata-sdk/src/main/java/org/openmetadata/sdk/fluent/Tables.java index a9e3b4b6b31e..0257b73db164 100644 --- a/openmetadata-sdk/src/main/java/org/openmetadata/sdk/fluent/Tables.java +++ b/openmetadata-sdk/src/main/java/org/openmetadata/sdk/fluent/Tables.java @@ -158,6 +158,21 @@ public static org.openmetadata.schema.type.EntityHistory getVersionList( return getClient().tables().getVersionList(id, limit, offset); } + public static org.openmetadata.schema.type.EntityHistory getVersionList( + java.util.UUID id, int limit, int offset, String fieldChanged) { + return getClient().tables().getVersionList(id, limit, offset, fieldChanged); + } + + public static org.openmetadata.schema.utils.ResultList getEntityHistory( + long startTs, long endTs) { + return getClient().tables().getEntityHistory(startTs, endTs); + } + + public static org.openmetadata.schema.utils.ResultList getEntityHistory( + long startTs, long endTs, int limit, String before, String after) { + return getClient().tables().getEntityHistory(startTs, endTs, limit, before, after); + } + public static Table getVersion(String id, Double version) { return getClient().tables().getVersion(id, version); } diff --git a/openmetadata-sdk/src/main/java/org/openmetadata/sdk/fluent/Teams.java b/openmetadata-sdk/src/main/java/org/openmetadata/sdk/fluent/Teams.java index eee59cefda20..e4ecf0922015 100644 --- a/openmetadata-sdk/src/main/java/org/openmetadata/sdk/fluent/Teams.java +++ b/openmetadata-sdk/src/main/java/org/openmetadata/sdk/fluent/Teams.java @@ -121,6 +121,21 @@ public static org.openmetadata.schema.type.EntityHistory getVersionList( return getClient().teams().getVersionList(id, limit, offset); } + public static org.openmetadata.schema.type.EntityHistory getVersionList( + java.util.UUID id, int limit, int offset, String fieldChanged) { + return getClient().teams().getVersionList(id, limit, offset, fieldChanged); + } + + public static org.openmetadata.schema.utils.ResultList getEntityHistory( + long startTs, long endTs) { + return getClient().teams().getEntityHistory(startTs, endTs); + } + + public static org.openmetadata.schema.utils.ResultList getEntityHistory( + long startTs, long endTs, int limit, String before, String after) { + return getClient().teams().getEntityHistory(startTs, endTs, limit, before, after); + } + public static Team getVersion(String id, Double version) { return getClient().teams().getVersion(id, version); } diff --git a/openmetadata-sdk/src/main/java/org/openmetadata/sdk/fluent/Topics.java b/openmetadata-sdk/src/main/java/org/openmetadata/sdk/fluent/Topics.java index 8d608dc0cb6f..db1b2dbec774 100644 --- a/openmetadata-sdk/src/main/java/org/openmetadata/sdk/fluent/Topics.java +++ b/openmetadata-sdk/src/main/java/org/openmetadata/sdk/fluent/Topics.java @@ -120,6 +120,21 @@ public static org.openmetadata.schema.type.EntityHistory getVersionList( return getClient().topics().getVersionList(id, limit, offset); } + public static org.openmetadata.schema.type.EntityHistory getVersionList( + java.util.UUID id, int limit, int offset, String fieldChanged) { + return getClient().topics().getVersionList(id, limit, offset, fieldChanged); + } + + public static org.openmetadata.schema.utils.ResultList getEntityHistory( + long startTs, long endTs) { + return getClient().topics().getEntityHistory(startTs, endTs); + } + + public static org.openmetadata.schema.utils.ResultList getEntityHistory( + long startTs, long endTs, int limit, String before, String after) { + return getClient().topics().getEntityHistory(startTs, endTs, limit, before, after); + } + public static Topic getVersion(String id, Double version) { return getClient().topics().getVersion(id, version); } diff --git a/openmetadata-sdk/src/main/java/org/openmetadata/sdk/fluent/Users.java b/openmetadata-sdk/src/main/java/org/openmetadata/sdk/fluent/Users.java index 34b533cf0df2..b6b82311673b 100644 --- a/openmetadata-sdk/src/main/java/org/openmetadata/sdk/fluent/Users.java +++ b/openmetadata-sdk/src/main/java/org/openmetadata/sdk/fluent/Users.java @@ -124,6 +124,21 @@ public static org.openmetadata.schema.type.EntityHistory getVersionList( return getClient().users().getVersionList(id, limit, offset); } + public static org.openmetadata.schema.type.EntityHistory getVersionList( + java.util.UUID id, int limit, int offset, String fieldChanged) { + return getClient().users().getVersionList(id, limit, offset, fieldChanged); + } + + public static org.openmetadata.schema.utils.ResultList getEntityHistory( + long startTs, long endTs) { + return getClient().users().getEntityHistory(startTs, endTs); + } + + public static org.openmetadata.schema.utils.ResultList getEntityHistory( + long startTs, long endTs, int limit, String before, String after) { + return getClient().users().getEntityHistory(startTs, endTs, limit, before, after); + } + public static User getVersion(String id, Double version) { return getClient().users().getVersion(id, version); } diff --git a/openmetadata-sdk/src/main/java/org/openmetadata/sdk/services/EntityServiceBase.java b/openmetadata-sdk/src/main/java/org/openmetadata/sdk/services/EntityServiceBase.java index 88cfdb452c47..ca995e86c187 100644 --- a/openmetadata-sdk/src/main/java/org/openmetadata/sdk/services/EntityServiceBase.java +++ b/openmetadata-sdk/src/main/java/org/openmetadata/sdk/services/EntityServiceBase.java @@ -16,6 +16,7 @@ import okhttp3.HttpUrl; import org.openmetadata.schema.type.EntityHistory; import org.openmetadata.schema.type.api.BulkOperationResult; +import org.openmetadata.schema.utils.ResultList; import org.openmetadata.sdk.exceptions.OpenMetadataException; import org.openmetadata.sdk.models.AllModels; import org.openmetadata.sdk.models.ListParams; @@ -610,14 +611,49 @@ public EntityHistory getVersionList(UUID id, int limit, int offset) throws OpenM public EntityHistory getVersionList(String id, int limit, int offset) throws OpenMetadataException { + return getVersionList(id, limit, offset, null); + } + + public EntityHistory getVersionList(UUID id, int limit, int offset, String fieldChanged) + throws OpenMetadataException { + return getVersionList(id.toString(), limit, offset, fieldChanged); + } + + public EntityHistory getVersionList(String id, int limit, int offset, String fieldChanged) + throws OpenMetadataException { String path = basePath + "/" + id + "/versions"; Map queryParams = new HashMap<>(); queryParams.put("limit", String.valueOf(limit)); queryParams.put("offset", String.valueOf(offset)); + if (fieldChanged != null && !fieldChanged.isEmpty()) { + queryParams.put("fieldChanged", fieldChanged); + } RequestOptions options = RequestOptions.builder().queryParams(queryParams).build(); return httpClient.execute(HttpMethod.GET, path, null, EntityHistory.class, options); } + public ResultList getEntityHistory(long startTs, long endTs) throws OpenMetadataException { + return getEntityHistory(startTs, endTs, 10, null, null); + } + + public ResultList getEntityHistory( + long startTs, long endTs, int limit, String before, String after) + throws OpenMetadataException { + String path = basePath + "/history"; + Map queryParams = new HashMap<>(); + queryParams.put("startTs", String.valueOf(startTs)); + queryParams.put("endTs", String.valueOf(endTs)); + queryParams.put("limit", String.valueOf(limit)); + if (before != null) { + queryParams.put("before", before); + } + if (after != null) { + queryParams.put("after", after); + } + RequestOptions options = RequestOptions.builder().queryParams(queryParams).build(); + return httpClient.execute(HttpMethod.GET, path, null, ResultList.class, options); + } + /** * Get a specific version of an entity. * diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/CollectionDAO.java b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/CollectionDAO.java index c5832d63fabf..39a56cb1b2e9 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/CollectionDAO.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/CollectionDAO.java @@ -1426,6 +1426,29 @@ List getExtensionsWithOffset( + "LIKE CONCAT (:extensionPrefix, '.%')") int getExtensionCount(@BindUUID("id") UUID id, @Bind("extensionPrefix") String extensionPrefix); + @RegisterRowMapper(ExtensionMapper.class) + @SqlQuery( + "SELECT extension, json FROM entity_extension WHERE id = :id " + + "AND extension LIKE CONCAT(:extensionPrefix, '.%') " + + "AND changeDescriptionDoc LIKE CONCAT('%', :fieldName, '%') " + + "ORDER BY extension DESC " + + "LIMIT :limit OFFSET :offset") + List getExtensionsWithFieldChanged( + @BindUUID("id") UUID id, + @Bind("extensionPrefix") String extensionPrefix, + @Bind("fieldName") String fieldName, + @Bind("limit") int limit, + @Bind("offset") int offset); + + @SqlQuery( + "SELECT COUNT(*) FROM entity_extension WHERE id = :id " + + "AND extension LIKE CONCAT(:extensionPrefix, '.%') " + + "AND changeDescriptionDoc LIKE CONCAT('%', :fieldName, '%') ") + int getExtensionCountWithFieldChanged( + @BindUUID("id") UUID id, + @Bind("extensionPrefix") String extensionPrefix, + @Bind("fieldName") String fieldName); + @SqlUpdate("DELETE FROM entity_extension WHERE id = :id AND extension = :extension") void delete(@BindUUID("id") UUID id, @Bind("extension") String extension); 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 dd26b5cd31a6..c0061fa4a84c 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 @@ -1256,10 +1256,19 @@ public final T getVersion(UUID id, String version) { } public final EntityHistoryWithOffset listVersionsWithOffset(UUID id, int limit, int offset) { + return listVersionsWithOffset(id, limit, offset, null); + } + + public final EntityHistoryWithOffset listVersionsWithOffset( + UUID id, int limit, int offset, String fieldChanged) { T latest = setFieldsInternal(find(id, ALL), putFields); setInheritedFields(latest, putFields); String extensionPrefix = EntityUtil.getVersionExtensionPrefix(entityType); + if (fieldChanged != null && !fieldChanged.isEmpty()) { + return listVersionsWithFieldFilter(id, latest, extensionPrefix, limit, offset, fieldChanged); + } + final List versions = new ArrayList<>(); int dbLimit; int dbOffset; @@ -1296,6 +1305,76 @@ public final EntityHistoryWithOffset listVersionsWithOffset(UUID id, int limit, return new EntityHistoryWithOffset(entityHistory, offset + limit); } + private EntityHistoryWithOffset listVersionsWithFieldFilter( + UUID id, T latest, String extensionPrefix, int limit, int offset, String fieldChanged) { + final List versions = new ArrayList<>(); + boolean latestMatches = latestVersionMatchesFieldChanged(latest, fieldChanged); + + int dbLimit; + int dbOffset; + + if (offset == 0) { + if (latestMatches) { + versions.add(JsonUtils.pojoToJson(latest)); + dbLimit = limit - 1; + } else { + dbLimit = limit; + } + dbOffset = 0; + } else { + dbLimit = limit; + dbOffset = latestMatches ? offset - 1 : offset; + } + + if (dbLimit > 0) { + List records = + daoCollection + .entityExtensionDAO() + .getExtensionsWithFieldChanged(id, extensionPrefix, fieldChanged, dbLimit, dbOffset); + List oldVersions = new ArrayList<>(); + records.forEach(r -> oldVersions.add(new EntityVersionPair(r))); + oldVersions.sort(EntityUtil.compareVersion.reversed()); + oldVersions.forEach(version -> versions.add(version.getEntityJson())); + } + + int extensionCount = + daoCollection + .entityExtensionDAO() + .getExtensionCountWithFieldChanged(id, extensionPrefix, fieldChanged); + int total = extensionCount + (latestMatches ? 1 : 0); + + Paging paging = new Paging(); + paging.setOffset(offset); + paging.setLimit(limit); + paging.setTotal(total); + + EntityHistory entityHistory = + new EntityHistory().withEntityType(entityType).withVersions(versions).withPaging(paging); + return new EntityHistoryWithOffset(entityHistory, offset + limit); + } + + private boolean latestVersionMatchesFieldChanged(T latest, String fieldChanged) { + ChangeDescription cd = latest.getChangeDescription(); + if (cd == null) { + return false; + } + return fieldChangeListContains(cd.getFieldsAdded(), fieldChanged) + || fieldChangeListContains(cd.getFieldsUpdated(), fieldChanged) + || fieldChangeListContains(cd.getFieldsDeleted(), fieldChanged); + } + + private boolean fieldChangeListContains(List fieldChanges, String fieldName) { + if (fieldChanges == null) { + return false; + } + for (FieldChange fc : fieldChanges) { + if (fc.getName() != null && fc.getName().contains(fieldName)) { + return true; + } + } + return false; + } + public final ResultList listWithOffset( ListWithOffsetFunction> callable, Function countCallable, diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/EntityResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/EntityResource.java index f4cf3b8398ad..670e6d5accc5 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/EntityResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/EntityResource.java @@ -335,14 +335,25 @@ public T getVersionInternal( } protected EntityHistory listVersionsInternal(SecurityContext securityContext, UUID id) { - return listVersionsInternal(securityContext, id, 0, 0); + return listVersionsInternal(securityContext, id, 0, 0, null); } protected EntityHistory listVersionsInternal( SecurityContext securityContext, UUID id, int limit, int offset) { + return listVersionsInternal(securityContext, id, limit, offset, null); + } + + protected EntityHistory listVersionsInternal( + SecurityContext securityContext, UUID id, int limit, int offset, String fieldChanged) { OperationContext operationContext = new OperationContext(entityType, VIEW_BASIC); return listVersionsInternal( - securityContext, id, limit, offset, operationContext, getResourceContextById(id)); + securityContext, + id, + limit, + offset, + fieldChanged, + operationContext, + getResourceContextById(id)); } protected EntityHistory listVersionsInternal( @@ -350,7 +361,7 @@ protected EntityHistory listVersionsInternal( UUID id, OperationContext operationContext, ResourceContextInterface resourceContext) { - return listVersionsInternal(securityContext, id, 0, 0, operationContext, resourceContext); + return listVersionsInternal(securityContext, id, 0, 0, null, operationContext, resourceContext); } protected EntityHistory listVersionsInternal( @@ -358,11 +369,17 @@ protected EntityHistory listVersionsInternal( UUID id, int limit, int offset, + String fieldChanged, OperationContext operationContext, ResourceContextInterface resourceContext) { authorizer.authorize(securityContext, operationContext, resourceContext); if (limit > 0) { - return repository.listVersionsWithOffset(id, limit, offset).entityHistory(); + return repository.listVersionsWithOffset(id, limit, offset, fieldChanged).entityHistory(); + } + if (fieldChanged != null && !fieldChanged.isEmpty()) { + return repository + .listVersionsWithOffset(id, Integer.MAX_VALUE, 0, fieldChanged) + .entityHistory(); } return repository.listVersions(id); } diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/ai/AIApplicationResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/ai/AIApplicationResource.java index 193a2fa197de..b56a8b6a9f49 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/ai/AIApplicationResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/ai/AIApplicationResource.java @@ -435,8 +435,13 @@ public EntityHistory listVersions( @QueryParam("offset") @DefaultValue("0") @Min(0) - int offset) { - return super.listVersionsInternal(securityContext, id, limit, offset); + int offset, + @Parameter( + description = + "Filter versions by field changes. Returns only versions where the specified field was added, updated, or deleted") + @QueryParam("fieldChanged") + String fieldChanged) { + return super.listVersionsInternal(securityContext, id, limit, offset, fieldChanged); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/ai/AIGovernancePolicyResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/ai/AIGovernancePolicyResource.java index 1879dadbc087..9d5910d65fad 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/ai/AIGovernancePolicyResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/ai/AIGovernancePolicyResource.java @@ -420,8 +420,13 @@ public EntityHistory listVersions( @QueryParam("offset") @DefaultValue("0") @Min(0) - int offset) { - return super.listVersionsInternal(securityContext, id, limit, offset); + int offset, + @Parameter( + description = + "Filter versions by field changes. Returns only versions where the specified field was added, updated, or deleted") + @QueryParam("fieldChanged") + String fieldChanged) { + return super.listVersionsInternal(securityContext, id, limit, offset, fieldChanged); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/ai/LLMModelResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/ai/LLMModelResource.java index bd3ca7f2a099..0b4eb82e9673 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/ai/LLMModelResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/ai/LLMModelResource.java @@ -429,8 +429,13 @@ public EntityHistory listVersions( @QueryParam("offset") @DefaultValue("0") @Min(0) - int offset) { - return super.listVersionsInternal(securityContext, id, limit, offset); + int offset, + @Parameter( + description = + "Filter versions by field changes. Returns only versions where the specified field was added, updated, or deleted") + @QueryParam("fieldChanged") + String fieldChanged) { + return super.listVersionsInternal(securityContext, id, limit, offset, fieldChanged); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/ai/PromptTemplateResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/ai/PromptTemplateResource.java index e817b38a27d7..308a146a77e6 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/ai/PromptTemplateResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/ai/PromptTemplateResource.java @@ -418,8 +418,13 @@ public EntityHistory listVersions( @QueryParam("offset") @DefaultValue("0") @Min(0) - int offset) { - return super.listVersionsInternal(securityContext, id, limit, offset); + int offset, + @Parameter( + description = + "Filter versions by field changes. Returns only versions where the specified field was added, updated, or deleted") + @QueryParam("fieldChanged") + String fieldChanged) { + return super.listVersionsInternal(securityContext, id, limit, offset, fieldChanged); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/analytics/WebAnalyticEventResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/analytics/WebAnalyticEventResource.java index 6afb324f105e..6ca318827f83 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/analytics/WebAnalyticEventResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/analytics/WebAnalyticEventResource.java @@ -481,8 +481,13 @@ public EntityHistory listVersions( @QueryParam("offset") @DefaultValue("0") @Min(0) - int offset) { - return super.listVersionsInternal(securityContext, id, limit, offset); + int offset, + @Parameter( + description = + "Filter versions by field changes. Returns only versions where the specified field was added, updated, or deleted") + @QueryParam("fieldChanged") + String fieldChanged) { + return super.listVersionsInternal(securityContext, id, limit, offset, fieldChanged); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/apis/APICollectionResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/apis/APICollectionResource.java index 2f7da928644e..e24444b44b0e 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/apis/APICollectionResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/apis/APICollectionResource.java @@ -188,8 +188,13 @@ public EntityHistory listVersions( @QueryParam("offset") @DefaultValue("0") @Min(0) - int offset) { - return super.listVersionsInternal(securityContext, id, limit, offset); + int offset, + @Parameter( + description = + "Filter versions by field changes. Returns only versions where the specified field was added, updated, or deleted") + @QueryParam("fieldChanged") + String fieldChanged) { + return super.listVersionsInternal(securityContext, id, limit, offset, fieldChanged); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/apis/APIEndpointResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/apis/APIEndpointResource.java index a2c2ca90a32c..c18f8c2f2aa2 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/apis/APIEndpointResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/apis/APIEndpointResource.java @@ -187,8 +187,13 @@ public EntityHistory listVersions( @QueryParam("offset") @DefaultValue("0") @Min(0) - int offset) { - return super.listVersionsInternal(securityContext, id, limit, offset); + int offset, + @Parameter( + description = + "Filter versions by field changes. Returns only versions where the specified field was added, updated, or deleted") + @QueryParam("fieldChanged") + String fieldChanged) { + return super.listVersionsInternal(securityContext, id, limit, offset, fieldChanged); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/apps/AppMarketPlaceResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/apps/AppMarketPlaceResource.java index 38c4097e109b..0d53130494da 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/apps/AppMarketPlaceResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/apps/AppMarketPlaceResource.java @@ -170,8 +170,13 @@ public EntityHistory listVersions( @QueryParam("offset") @DefaultValue("0") @Min(0) - int offset) { - return super.listVersionsInternal(securityContext, id, limit, offset); + int offset, + @Parameter( + description = + "Filter versions by field changes. Returns only versions where the specified field was added, updated, or deleted") + @QueryParam("fieldChanged") + String fieldChanged) { + return super.listVersionsInternal(securityContext, id, limit, offset, fieldChanged); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/apps/AppResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/apps/AppResource.java index d0ae7916eeb1..76e17887f585 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/apps/AppResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/apps/AppResource.java @@ -585,8 +585,13 @@ public EntityHistory listVersions( @QueryParam("offset") @DefaultValue("0") @Min(0) - int offset) { - return super.listVersionsInternal(securityContext, id, limit, offset); + int offset, + @Parameter( + description = + "Filter versions by field changes. Returns only versions where the specified field was added, updated, or deleted") + @QueryParam("fieldChanged") + String fieldChanged) { + return super.listVersionsInternal(securityContext, id, limit, offset, fieldChanged); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/automations/WorkflowResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/automations/WorkflowResource.java index a9e4a15288b0..dbe2037d1b48 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/automations/WorkflowResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/automations/WorkflowResource.java @@ -211,8 +211,13 @@ public EntityHistory listVersions( @QueryParam("offset") @DefaultValue("0") @Min(0) - int offset) { - return super.listVersionsInternal(securityContext, id, limit, offset); + int offset, + @Parameter( + description = + "Filter versions by field changes. Returns only versions where the specified field was added, updated, or deleted") + @QueryParam("fieldChanged") + String fieldChanged) { + return super.listVersionsInternal(securityContext, id, limit, offset, fieldChanged); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/bots/BotResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/bots/BotResource.java index e7ac81bb9663..7bbece09531f 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/bots/BotResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/bots/BotResource.java @@ -268,8 +268,13 @@ public EntityHistory listVersions( @QueryParam("offset") @DefaultValue("0") @Min(0) - int offset) { - return super.listVersionsInternal(securityContext, id, limit, offset); + int offset, + @Parameter( + description = + "Filter versions by field changes. Returns only versions where the specified field was added, updated, or deleted") + @QueryParam("fieldChanged") + String fieldChanged) { + return super.listVersionsInternal(securityContext, id, limit, offset, fieldChanged); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/charts/ChartResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/charts/ChartResource.java index ef27ce15c838..7e50a699043a 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/charts/ChartResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/charts/ChartResource.java @@ -185,8 +185,13 @@ public EntityHistory listVersions( @QueryParam("offset") @DefaultValue("0") @Min(0) - int offset) { - return super.listVersionsInternal(securityContext, id, limit, offset); + int offset, + @Parameter( + description = + "Filter versions by field changes. Returns only versions where the specified field was added, updated, or deleted") + @QueryParam("fieldChanged") + String fieldChanged) { + return super.listVersionsInternal(securityContext, id, limit, offset, fieldChanged); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/dashboards/DashboardResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/dashboards/DashboardResource.java index 14d6460d3e3e..97f88b62541e 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/dashboards/DashboardResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/dashboards/DashboardResource.java @@ -191,8 +191,13 @@ public EntityHistory listVersions( @QueryParam("offset") @DefaultValue("0") @Min(0) - int offset) { - return super.listVersionsInternal(securityContext, id, limit, offset); + int offset, + @Parameter( + description = + "Filter versions by field changes. Returns only versions where the specified field was added, updated, or deleted") + @QueryParam("fieldChanged") + String fieldChanged) { + return super.listVersionsInternal(securityContext, id, limit, offset, fieldChanged); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/data/DataContractResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/data/DataContractResource.java index 9377b3c35a74..fde9815618db 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/data/DataContractResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/data/DataContractResource.java @@ -363,8 +363,13 @@ public EntityHistory listVersions( @QueryParam("offset") @DefaultValue("0") @Min(0) - int offset) { - return super.listVersionsInternal(securityContext, id, limit, offset); + int offset, + @Parameter( + description = + "Filter versions by field changes. Returns only versions where the specified field was added, updated, or deleted") + @QueryParam("fieldChanged") + String fieldChanged) { + return super.listVersionsInternal(securityContext, id, limit, offset, fieldChanged); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/databases/DatabaseResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/databases/DatabaseResource.java index 5a3d68c327e3..f71dcf6c228c 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/databases/DatabaseResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/databases/DatabaseResource.java @@ -196,8 +196,13 @@ public EntityHistory listVersions( @QueryParam("offset") @DefaultValue("0") @Min(0) - int offset) { - return super.listVersionsInternal(securityContext, id, limit, offset); + int offset, + @Parameter( + description = + "Filter versions by field changes. Returns only versions where the specified field was added, updated, or deleted") + @QueryParam("fieldChanged") + String fieldChanged) { + return super.listVersionsInternal(securityContext, id, limit, offset, fieldChanged); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/databases/DatabaseSchemaResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/databases/DatabaseSchemaResource.java index d90788ec25e7..69badb0bdf1a 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/databases/DatabaseSchemaResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/databases/DatabaseSchemaResource.java @@ -196,8 +196,13 @@ public EntityHistory listVersions( @QueryParam("offset") @DefaultValue("0") @Min(0) - int offset) { - return super.listVersionsInternal(securityContext, id, limit, offset); + int offset, + @Parameter( + description = + "Filter versions by field changes. Returns only versions where the specified field was added, updated, or deleted") + @QueryParam("fieldChanged") + String fieldChanged) { + return super.listVersionsInternal(securityContext, id, limit, offset, fieldChanged); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/databases/StoredProcedureResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/databases/StoredProcedureResource.java index e8fd1e1563fa..2ecd88aab4fd 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/databases/StoredProcedureResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/databases/StoredProcedureResource.java @@ -152,8 +152,13 @@ public EntityHistory listVersions( @QueryParam("offset") @DefaultValue("0") @Min(0) - int offset) { - return super.listVersionsInternal(securityContext, id, limit, offset); + int offset, + @Parameter( + description = + "Filter versions by field changes. Returns only versions where the specified field was added, updated, or deleted") + @QueryParam("fieldChanged") + String fieldChanged) { + return super.listVersionsInternal(securityContext, id, limit, offset, fieldChanged); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/databases/TableResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/databases/TableResource.java index 04964c334873..5ef46ddf5a64 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/databases/TableResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/databases/TableResource.java @@ -362,8 +362,13 @@ public EntityHistory listVersions( @QueryParam("offset") @DefaultValue("0") @Min(0) - int offset) { - return super.listVersionsInternal(securityContext, id, limit, offset); + int offset, + @Parameter( + description = + "Filter versions by field changes. Returns only versions where the specified field was added, updated, or deleted") + @QueryParam("fieldChanged") + String fieldChanged) { + return super.listVersionsInternal(securityContext, id, limit, offset, fieldChanged); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/datainsight/DataInsightChartResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/datainsight/DataInsightChartResource.java index 1aa90ce8b8d7..22af33c4d8bc 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/datainsight/DataInsightChartResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/datainsight/DataInsightChartResource.java @@ -177,8 +177,13 @@ public EntityHistory listVersions( @QueryParam("offset") @DefaultValue("0") @Min(0) - int offset) { - return super.listVersionsInternal(securityContext, id, limit, offset); + int offset, + @Parameter( + description = + "Filter versions by field changes. Returns only versions where the specified field was added, updated, or deleted") + @QueryParam("fieldChanged") + String fieldChanged) { + return super.listVersionsInternal(securityContext, id, limit, offset, fieldChanged); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/datamodels/DashboardDataModelResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/datamodels/DashboardDataModelResource.java index 71dd51b10ac3..30d971892777 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/datamodels/DashboardDataModelResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/datamodels/DashboardDataModelResource.java @@ -194,8 +194,13 @@ public EntityHistory listVersions( @QueryParam("offset") @DefaultValue("0") @Min(0) - int offset) { - return super.listVersionsInternal(securityContext, id, limit, offset); + int offset, + @Parameter( + description = + "Filter versions by field changes. Returns only versions where the specified field was added, updated, or deleted") + @QueryParam("fieldChanged") + String fieldChanged) { + return super.listVersionsInternal(securityContext, id, limit, offset, fieldChanged); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/docstore/DocStoreResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/docstore/DocStoreResource.java index d070b7869552..cf77839497b6 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/docstore/DocStoreResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/docstore/DocStoreResource.java @@ -191,8 +191,13 @@ public EntityHistory listVersions( @QueryParam("offset") @DefaultValue("0") @Min(0) - int offset) { - return super.listVersionsInternal(securityContext, id, limit, offset); + int offset, + @Parameter( + description = + "Filter versions by field changes. Returns only versions where the specified field was added, updated, or deleted") + @QueryParam("fieldChanged") + String fieldChanged) { + return super.listVersionsInternal(securityContext, id, limit, offset, fieldChanged); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/domains/DataProductResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/domains/DataProductResource.java index 4d01c4ab5404..7a50dfa2c3f8 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/domains/DataProductResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/domains/DataProductResource.java @@ -273,8 +273,13 @@ public EntityHistory listVersions( @QueryParam("offset") @DefaultValue("0") @Min(0) - int offset) { - return super.listVersionsInternal(securityContext, id, limit, offset); + int offset, + @Parameter( + description = + "Filter versions by field changes. Returns only versions where the specified field was added, updated, or deleted") + @QueryParam("fieldChanged") + String fieldChanged) { + return super.listVersionsInternal(securityContext, id, limit, offset, fieldChanged); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/domains/DomainResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/domains/DomainResource.java index 37f779efe354..29d5e2c95567 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/domains/DomainResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/domains/DomainResource.java @@ -226,8 +226,13 @@ public EntityHistory listVersions( @QueryParam("offset") @DefaultValue("0") @Min(0) - int offset) { - return super.listVersionsInternal(securityContext, id, limit, offset); + int offset, + @Parameter( + description = + "Filter versions by field changes. Returns only versions where the specified field was added, updated, or deleted") + @QueryParam("fieldChanged") + String fieldChanged) { + return super.listVersionsInternal(securityContext, id, limit, offset, fieldChanged); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/dqtests/TestCaseResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/dqtests/TestCaseResource.java index 6a576fbdde25..ec092a02f5a5 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/dqtests/TestCaseResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/dqtests/TestCaseResource.java @@ -511,13 +511,18 @@ public EntityHistory listVersions( @QueryParam("offset") @DefaultValue("0") @Min(0) - int offset) { + int offset, + @Parameter( + description = + "Filter versions by field changes. Returns only versions where the specified field was added, updated, or deleted") + @QueryParam("fieldChanged") + String fieldChanged) { ResourceContextInterface resourceContext = TestCaseResourceContext.builder().id(id).build(); OperationContext operationContext = new OperationContext(Entity.TABLE, MetadataOperation.VIEW_TESTS); return super.listVersionsInternal( - securityContext, id, limit, offset, operationContext, resourceContext); + securityContext, id, limit, offset, fieldChanged, operationContext, resourceContext); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/dqtests/TestDefinitionResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/dqtests/TestDefinitionResource.java index a26a8cdbf492..8773e2873c43 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/dqtests/TestDefinitionResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/dqtests/TestDefinitionResource.java @@ -213,8 +213,13 @@ public EntityHistory listVersions( @QueryParam("offset") @DefaultValue("0") @Min(0) - int offset) { - return super.listVersionsInternal(securityContext, id, limit, offset); + int offset, + @Parameter( + description = + "Filter versions by field changes. Returns only versions where the specified field was added, updated, or deleted") + @QueryParam("fieldChanged") + String fieldChanged) { + return super.listVersionsInternal(securityContext, id, limit, offset, fieldChanged); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/dqtests/TestSuiteResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/dqtests/TestSuiteResource.java index 78c4833009a5..cf0a908a94c2 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/dqtests/TestSuiteResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/dqtests/TestSuiteResource.java @@ -349,8 +349,13 @@ public EntityHistory listVersions( @QueryParam("offset") @DefaultValue("0") @Min(0) - int offset) { - return super.listVersionsInternal(securityContext, id, limit, offset); + int offset, + @Parameter( + description = + "Filter versions by field changes. Returns only versions where the specified field was added, updated, or deleted") + @QueryParam("fieldChanged") + String fieldChanged) { + return super.listVersionsInternal(securityContext, id, limit, offset, fieldChanged); } @GET 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 45b0916c4fb7..8144322ff3b5 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 @@ -534,8 +534,13 @@ public EntityHistory listVersions( @QueryParam("offset") @DefaultValue("0") @Min(0) - int offset) { - return super.listVersionsInternal(securityContext, id, limit, offset); + int offset, + @Parameter( + description = + "Filter versions by field changes. Returns only versions where the specified field was added, updated, or deleted") + @QueryParam("fieldChanged") + String fieldChanged) { + return super.listVersionsInternal(securityContext, id, limit, offset, fieldChanged); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/drives/FileResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/drives/FileResource.java index 8958d55e1949..d60684afee95 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/drives/FileResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/drives/FileResource.java @@ -544,8 +544,13 @@ public EntityHistory listVersions( @QueryParam("offset") @DefaultValue("0") @Min(0) - int offset) { - return super.listVersionsInternal(securityContext, id, limit, offset); + int offset, + @Parameter( + description = + "Filter versions by field changes. Returns only versions where the specified field was added, updated, or deleted") + @QueryParam("fieldChanged") + String fieldChanged) { + return super.listVersionsInternal(securityContext, id, limit, offset, fieldChanged); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/drives/SpreadsheetResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/drives/SpreadsheetResource.java index bf55e3908579..bb3f42d44407 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/drives/SpreadsheetResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/drives/SpreadsheetResource.java @@ -514,8 +514,13 @@ public EntityHistory listVersions( @QueryParam("offset") @DefaultValue("0") @Min(0) - int offset) { - return super.listVersionsInternal(securityContext, id, limit, offset); + int offset, + @Parameter( + description = + "Filter versions by field changes. Returns only versions where the specified field was added, updated, or deleted") + @QueryParam("fieldChanged") + String fieldChanged) { + return super.listVersionsInternal(securityContext, id, limit, offset, fieldChanged); } @GET 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 cc090096599b..fe7801b92625 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 @@ -492,8 +492,13 @@ public EntityHistory listVersions( @QueryParam("offset") @DefaultValue("0") @Min(0) - int offset) { - return super.listVersionsInternal(securityContext, id, limit, offset); + int offset, + @Parameter( + description = + "Filter versions by field changes. Returns only versions where the specified field was added, updated, or deleted") + @QueryParam("fieldChanged") + String fieldChanged) { + return super.listVersionsInternal(securityContext, id, limit, offset, fieldChanged); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/events/NotificationTemplateResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/events/NotificationTemplateResource.java index a0f457f2ecb4..e89fd31678e6 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/events/NotificationTemplateResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/events/NotificationTemplateResource.java @@ -328,8 +328,13 @@ public EntityHistory listVersions( @QueryParam("offset") @DefaultValue("0") @Min(0) - int offset) { - return super.listVersionsInternal(securityContext, id, limit, offset); + int offset, + @Parameter( + description = + "Filter versions by field changes. Returns only versions where the specified field was added, updated, or deleted") + @QueryParam("fieldChanged") + String fieldChanged) { + return super.listVersionsInternal(securityContext, id, limit, offset, fieldChanged); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/events/subscription/EventSubscriptionResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/events/subscription/EventSubscriptionResource.java index e8215382bd1f..50458406a7eb 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/events/subscription/EventSubscriptionResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/events/subscription/EventSubscriptionResource.java @@ -471,8 +471,13 @@ public EntityHistory listEventSubscriptionVersions( @QueryParam("offset") @DefaultValue("0") @Min(0) - int offset) { - return super.listVersionsInternal(securityContext, id, limit, offset); + int offset, + @Parameter( + description = + "Filter versions by field changes. Returns only versions where the specified field was added, updated, or deleted") + @QueryParam("fieldChanged") + String fieldChanged) { + return super.listVersionsInternal(securityContext, id, limit, offset, fieldChanged); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/glossary/GlossaryResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/glossary/GlossaryResource.java index 1bf199686503..4b03ca3facb4 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/glossary/GlossaryResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/glossary/GlossaryResource.java @@ -262,8 +262,13 @@ public EntityHistory listVersions( @QueryParam("offset") @DefaultValue("0") @Min(0) - int offset) { - return super.listVersionsInternal(securityContext, id, limit, offset); + int offset, + @Parameter( + description = + "Filter versions by field changes. Returns only versions where the specified field was added, updated, or deleted") + @QueryParam("fieldChanged") + String fieldChanged) { + return super.listVersionsInternal(securityContext, id, limit, offset, fieldChanged); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/glossary/GlossaryTermResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/glossary/GlossaryTermResource.java index 4ef68b1d2f29..7b5049560bc0 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/glossary/GlossaryTermResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/glossary/GlossaryTermResource.java @@ -518,8 +518,13 @@ public EntityHistory listVersions( @QueryParam("offset") @DefaultValue("0") @Min(0) - int offset) { - return super.listVersionsInternal(securityContext, id, limit, offset); + int offset, + @Parameter( + description = + "Filter versions by field changes. Returns only versions where the specified field was added, updated, or deleted") + @QueryParam("fieldChanged") + String fieldChanged) { + return super.listVersionsInternal(securityContext, id, limit, offset, fieldChanged); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/governance/WorkflowDefinitionResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/governance/WorkflowDefinitionResource.java index b74f652e520b..60f0acaffe6a 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/governance/WorkflowDefinitionResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/governance/WorkflowDefinitionResource.java @@ -166,8 +166,13 @@ public EntityHistory listVersions( @QueryParam("offset") @DefaultValue("0") @Min(0) - int offset) { - return super.listVersionsInternal(securityContext, id, limit, offset); + int offset, + @Parameter( + description = + "Filter versions by field changes. Returns only versions where the specified field was added, updated, or deleted") + @QueryParam("fieldChanged") + String fieldChanged) { + return super.listVersionsInternal(securityContext, id, limit, offset, fieldChanged); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/kpi/KpiResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/kpi/KpiResource.java index 2c402d21b813..4302d568a7aa 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/kpi/KpiResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/kpi/KpiResource.java @@ -172,8 +172,13 @@ public EntityHistory listVersions( @QueryParam("offset") @DefaultValue("0") @Min(0) - int offset) { - return super.listVersionsInternal(securityContext, id, limit, offset); + int offset, + @Parameter( + description = + "Filter versions by field changes. Returns only versions where the specified field was added, updated, or deleted") + @QueryParam("fieldChanged") + String fieldChanged) { + return super.listVersionsInternal(securityContext, id, limit, offset, fieldChanged); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/learning/LearningResourceResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/learning/LearningResourceResource.java index dbfd8c834117..c63600785610 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/learning/LearningResourceResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/learning/LearningResourceResource.java @@ -291,8 +291,13 @@ public EntityHistory listVersions( @QueryParam("offset") @DefaultValue("0") @Min(0) - int offset) { - return listVersionsInternal(securityContext, id, limit, offset); + int offset, + @Parameter( + description = + "Filter versions by field changes. Returns only versions where the specified field was added, updated, or deleted") + @QueryParam("fieldChanged") + String fieldChanged) { + return listVersionsInternal(securityContext, id, limit, offset, fieldChanged); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/metrics/MetricResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/metrics/MetricResource.java index b0cf2bd5a752..bc1219468594 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/metrics/MetricResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/metrics/MetricResource.java @@ -261,8 +261,13 @@ public EntityHistory listVersions( @QueryParam("offset") @DefaultValue("0") @Min(0) - int offset) { - return super.listVersionsInternal(securityContext, id, limit, offset); + int offset, + @Parameter( + description = + "Filter versions by field changes. Returns only versions where the specified field was added, updated, or deleted") + @QueryParam("fieldChanged") + String fieldChanged) { + return super.listVersionsInternal(securityContext, id, limit, offset, fieldChanged); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/mlmodels/MlModelResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/mlmodels/MlModelResource.java index 633915e98bc1..148098f3156b 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/mlmodels/MlModelResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/mlmodels/MlModelResource.java @@ -486,8 +486,13 @@ public EntityHistory listVersions( @QueryParam("offset") @DefaultValue("0") @Min(0) - int offset) { - return super.listVersionsInternal(securityContext, id, limit, offset); + int offset, + @Parameter( + description = + "Filter versions by field changes. Returns only versions where the specified field was added, updated, or deleted") + @QueryParam("fieldChanged") + String fieldChanged) { + return super.listVersionsInternal(securityContext, id, limit, offset, fieldChanged); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/pipelines/PipelineResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/pipelines/PipelineResource.java index 8a81797ade4d..93436b46b9cd 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/pipelines/PipelineResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/pipelines/PipelineResource.java @@ -206,8 +206,13 @@ public EntityHistory listVersions( @QueryParam("offset") @DefaultValue("0") @Min(0) - int offset) { - return super.listVersionsInternal(securityContext, id, limit, offset); + int offset, + @Parameter( + description = + "Filter versions by field changes. Returns only versions where the specified field was added, updated, or deleted") + @QueryParam("fieldChanged") + String fieldChanged) { + return super.listVersionsInternal(securityContext, id, limit, offset, fieldChanged); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/policies/PolicyResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/policies/PolicyResource.java index 1558727fc7a2..1198f82eca0e 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/policies/PolicyResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/policies/PolicyResource.java @@ -313,8 +313,13 @@ public EntityHistory listVersions( @QueryParam("offset") @DefaultValue("0") @Min(0) - int offset) { - return super.listVersionsInternal(securityContext, id, limit, offset); + int offset, + @Parameter( + description = + "Filter versions by field changes. Returns only versions where the specified field was added, updated, or deleted") + @QueryParam("fieldChanged") + String fieldChanged) { + return super.listVersionsInternal(securityContext, id, limit, offset, fieldChanged); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/query/QueryResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/query/QueryResource.java index 6165f4087480..d058bfe3ee67 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/query/QueryResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/query/QueryResource.java @@ -275,8 +275,13 @@ public EntityHistory listVersions( @QueryParam("offset") @DefaultValue("0") @Min(0) - int offset) { - return super.listVersionsInternal(securityContext, id, limit, offset); + int offset, + @Parameter( + description = + "Filter versions by field changes. Returns only versions where the specified field was added, updated, or deleted") + @QueryParam("fieldChanged") + String fieldChanged) { + return super.listVersionsInternal(securityContext, id, limit, offset, fieldChanged); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/searchindex/SearchIndexResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/searchindex/SearchIndexResource.java index d6d4961f3c92..999a4e62fa1e 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/searchindex/SearchIndexResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/searchindex/SearchIndexResource.java @@ -189,8 +189,13 @@ public EntityHistory listVersions( @QueryParam("offset") @DefaultValue("0") @Min(0) - int offset) { - return super.listVersionsInternal(securityContext, id, limit, offset); + int offset, + @Parameter( + description = + "Filter versions by field changes. Returns only versions where the specified field was added, updated, or deleted") + @QueryParam("fieldChanged") + String fieldChanged) { + return super.listVersionsInternal(securityContext, id, limit, offset, fieldChanged); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/apiservices/APIServiceResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/apiservices/APIServiceResource.java index ddf73954d223..774d5153bee5 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/apiservices/APIServiceResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/apiservices/APIServiceResource.java @@ -316,8 +316,14 @@ public EntityHistory listVersions( @QueryParam("offset") @DefaultValue("0") @Min(0) - int offset) { - EntityHistory entityHistory = super.listVersionsInternal(securityContext, id, limit, offset); + int offset, + @Parameter( + description = + "Filter versions by field changes. Returns only versions where the specified field was added, updated, or deleted") + @QueryParam("fieldChanged") + String fieldChanged) { + EntityHistory entityHistory = + super.listVersionsInternal(securityContext, id, limit, offset, fieldChanged); List versions = entityHistory.getVersions().stream() diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/dashboard/DashboardServiceResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/dashboard/DashboardServiceResource.java index 48d2b72a2863..36105d6c87ec 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/dashboard/DashboardServiceResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/dashboard/DashboardServiceResource.java @@ -358,8 +358,14 @@ public EntityHistory listVersions( @QueryParam("offset") @DefaultValue("0") @Min(0) - int offset) { - EntityHistory entityHistory = super.listVersionsInternal(securityContext, id, limit, offset); + int offset, + @Parameter( + description = + "Filter versions by field changes. Returns only versions where the specified field was added, updated, or deleted") + @QueryParam("fieldChanged") + String fieldChanged) { + EntityHistory entityHistory = + super.listVersionsInternal(securityContext, id, limit, offset, fieldChanged); List versions = entityHistory.getVersions().stream() diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/database/DatabaseServiceResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/database/DatabaseServiceResource.java index c87e8906512d..5619f3086fa8 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/database/DatabaseServiceResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/database/DatabaseServiceResource.java @@ -315,8 +315,14 @@ public EntityHistory listVersions( @QueryParam("offset") @DefaultValue("0") @Min(0) - int offset) { - EntityHistory entityHistory = super.listVersionsInternal(securityContext, id, limit, offset); + int offset, + @Parameter( + description = + "Filter versions by field changes. Returns only versions where the specified field was added, updated, or deleted") + @QueryParam("fieldChanged") + String fieldChanged) { + EntityHistory entityHistory = + super.listVersionsInternal(securityContext, id, limit, offset, fieldChanged); List versions = entityHistory.getVersions().stream() diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/drive/DriveServiceResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/drive/DriveServiceResource.java index 58cc9145fec5..7c3705004273 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/drive/DriveServiceResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/drive/DriveServiceResource.java @@ -389,8 +389,14 @@ public EntityHistory listVersions( @QueryParam("offset") @DefaultValue("0") @Min(0) - int offset) { - EntityHistory entityHistory = super.listVersionsInternal(securityContext, id, limit, offset); + int offset, + @Parameter( + description = + "Filter versions by field changes. Returns only versions where the specified field was added, updated, or deleted") + @QueryParam("fieldChanged") + String fieldChanged) { + EntityHistory entityHistory = + super.listVersionsInternal(securityContext, id, limit, offset, fieldChanged); List versions = entityHistory.getVersions().stream() .map( diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/ingestionpipelines/IngestionPipelineResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/ingestionpipelines/IngestionPipelineResource.java index b33912fc5b96..34c416c1816e 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/ingestionpipelines/IngestionPipelineResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/ingestionpipelines/IngestionPipelineResource.java @@ -405,8 +405,13 @@ public EntityHistory listVersions( @QueryParam("offset") @DefaultValue("0") @Min(0) - int offset) { - return super.listVersionsInternal(securityContext, id, limit, offset); + int offset, + @Parameter( + description = + "Filter versions by field changes. Returns only versions where the specified field was added, updated, or deleted") + @QueryParam("fieldChanged") + String fieldChanged) { + return super.listVersionsInternal(securityContext, id, limit, offset, fieldChanged); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/llm/LLMServiceResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/llm/LLMServiceResource.java index 2cb9ef38fb1e..e71dbf588027 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/llm/LLMServiceResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/llm/LLMServiceResource.java @@ -299,8 +299,14 @@ public EntityHistory listVersions( @QueryParam("offset") @DefaultValue("0") @Min(0) - int offset) { - EntityHistory entityHistory = super.listVersionsInternal(securityContext, id, limit, offset); + int offset, + @Parameter( + description = + "Filter versions by field changes. Returns only versions where the specified field was added, updated, or deleted") + @QueryParam("fieldChanged") + String fieldChanged) { + EntityHistory entityHistory = + super.listVersionsInternal(securityContext, id, limit, offset, fieldChanged); List versions = entityHistory.getVersions().stream() diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/messaging/MessagingServiceResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/messaging/MessagingServiceResource.java index c428d564f5ed..c17a48f8bf6f 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/messaging/MessagingServiceResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/messaging/MessagingServiceResource.java @@ -360,8 +360,14 @@ public EntityHistory listVersions( @QueryParam("offset") @DefaultValue("0") @Min(0) - int offset) { - EntityHistory entityHistory = super.listVersionsInternal(securityContext, id, limit, offset); + int offset, + @Parameter( + description = + "Filter versions by field changes. Returns only versions where the specified field was added, updated, or deleted") + @QueryParam("fieldChanged") + String fieldChanged) { + EntityHistory entityHistory = + super.listVersionsInternal(securityContext, id, limit, offset, fieldChanged); List versions = entityHistory.getVersions().stream() diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/metadata/MetadataServiceResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/metadata/MetadataServiceResource.java index 6246ed029817..1a0e6d8cbf2e 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/metadata/MetadataServiceResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/metadata/MetadataServiceResource.java @@ -402,8 +402,14 @@ public EntityHistory listVersions( @QueryParam("offset") @DefaultValue("0") @Min(0) - int offset) { - EntityHistory entityHistory = super.listVersionsInternal(securityContext, id, limit, offset); + int offset, + @Parameter( + description = + "Filter versions by field changes. Returns only versions where the specified field was added, updated, or deleted") + @QueryParam("fieldChanged") + String fieldChanged) { + EntityHistory entityHistory = + super.listVersionsInternal(securityContext, id, limit, offset, fieldChanged); List versions = entityHistory.getVersions().stream() diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/mlmodel/MlModelServiceResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/mlmodel/MlModelServiceResource.java index a67cb9da6212..ddd55b36ebd9 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/mlmodel/MlModelServiceResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/mlmodel/MlModelServiceResource.java @@ -374,8 +374,14 @@ public EntityHistory listVersions( @QueryParam("offset") @DefaultValue("0") @Min(0) - int offset) { - EntityHistory entityHistory = super.listVersionsInternal(securityContext, id, limit, offset); + int offset, + @Parameter( + description = + "Filter versions by field changes. Returns only versions where the specified field was added, updated, or deleted") + @QueryParam("fieldChanged") + String fieldChanged) { + EntityHistory entityHistory = + super.listVersionsInternal(securityContext, id, limit, offset, fieldChanged); List versions = entityHistory.getVersions().stream() diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/pipeline/PipelineServiceResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/pipeline/PipelineServiceResource.java index 7cc957675e18..0e663f23dcef 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/pipeline/PipelineServiceResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/pipeline/PipelineServiceResource.java @@ -374,8 +374,14 @@ public EntityHistory listVersions( @QueryParam("offset") @DefaultValue("0") @Min(0) - int offset) { - EntityHistory entityHistory = super.listVersionsInternal(securityContext, id, limit, offset); + int offset, + @Parameter( + description = + "Filter versions by field changes. Returns only versions where the specified field was added, updated, or deleted") + @QueryParam("fieldChanged") + String fieldChanged) { + EntityHistory entityHistory = + super.listVersionsInternal(securityContext, id, limit, offset, fieldChanged); List versions = entityHistory.getVersions().stream() diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/searchIndexes/SearchServiceResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/searchIndexes/SearchServiceResource.java index 363dadf92613..8a61d4836719 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/searchIndexes/SearchServiceResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/searchIndexes/SearchServiceResource.java @@ -364,8 +364,14 @@ public EntityHistory listVersions( @QueryParam("offset") @DefaultValue("0") @Min(0) - int offset) { - EntityHistory entityHistory = super.listVersionsInternal(securityContext, id, limit, offset); + int offset, + @Parameter( + description = + "Filter versions by field changes. Returns only versions where the specified field was added, updated, or deleted") + @QueryParam("fieldChanged") + String fieldChanged) { + EntityHistory entityHistory = + super.listVersionsInternal(securityContext, id, limit, offset, fieldChanged); List versions = entityHistory.getVersions().stream() diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/security/SecurityServiceResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/security/SecurityServiceResource.java index bffebcb35e45..c5713edded8e 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/security/SecurityServiceResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/security/SecurityServiceResource.java @@ -310,8 +310,14 @@ public EntityHistory listVersions( @QueryParam("offset") @DefaultValue("0") @Min(0) - int offset) { - EntityHistory entityHistory = super.listVersionsInternal(securityContext, id, limit, offset); + int offset, + @Parameter( + description = + "Filter versions by field changes. Returns only versions where the specified field was added, updated, or deleted") + @QueryParam("fieldChanged") + String fieldChanged) { + EntityHistory entityHistory = + super.listVersionsInternal(securityContext, id, limit, offset, fieldChanged); List versions = entityHistory.getVersions().stream() diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/storage/StorageServiceResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/storage/StorageServiceResource.java index 289dbe03e7a4..a47fbd760446 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/storage/StorageServiceResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/services/storage/StorageServiceResource.java @@ -358,8 +358,14 @@ public EntityHistory listVersions( @QueryParam("offset") @DefaultValue("0") @Min(0) - int offset) { - EntityHistory entityHistory = super.listVersionsInternal(securityContext, id, limit, offset); + int offset, + @Parameter( + description = + "Filter versions by field changes. Returns only versions where the specified field was added, updated, or deleted") + @QueryParam("fieldChanged") + String fieldChanged) { + EntityHistory entityHistory = + super.listVersionsInternal(securityContext, id, limit, offset, fieldChanged); List versions = entityHistory.getVersions().stream() diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/storages/ContainerResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/storages/ContainerResource.java index a276109689c7..55e428b5eebd 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/storages/ContainerResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/storages/ContainerResource.java @@ -479,8 +479,13 @@ public EntityHistory listVersions( @QueryParam("offset") @DefaultValue("0") @Min(0) - int offset) { - return super.listVersionsInternal(securityContext, id, limit, offset); + int offset, + @Parameter( + description = + "Filter versions by field changes. Returns only versions where the specified field was added, updated, or deleted") + @QueryParam("fieldChanged") + String fieldChanged) { + return super.listVersionsInternal(securityContext, id, limit, offset, fieldChanged); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/tags/ClassificationResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/tags/ClassificationResource.java index 0326d012fb50..043139051a44 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/tags/ClassificationResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/tags/ClassificationResource.java @@ -266,8 +266,13 @@ public EntityHistory listVersions( @QueryParam("offset") @DefaultValue("0") @Min(0) - int offset) { - return super.listVersionsInternal(securityContext, id, limit, offset); + int offset, + @Parameter( + description = + "Filter versions by field changes. Returns only versions where the specified field was added, updated, or deleted") + @QueryParam("fieldChanged") + String fieldChanged) { + return super.listVersionsInternal(securityContext, id, limit, offset, fieldChanged); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/tags/TagResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/tags/TagResource.java index 919ed112219f..2d04d2f79b6c 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/tags/TagResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/tags/TagResource.java @@ -340,8 +340,13 @@ public EntityHistory listVersions( @QueryParam("offset") @DefaultValue("0") @Min(0) - int offset) { - return super.listVersionsInternal(securityContext, id, limit, offset); + int offset, + @Parameter( + description = + "Filter versions by field changes. Returns only versions where the specified field was added, updated, or deleted") + @QueryParam("fieldChanged") + String fieldChanged) { + return super.listVersionsInternal(securityContext, id, limit, offset, fieldChanged); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/teams/PersonaResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/teams/PersonaResource.java index 2a089575b3fa..5628ff137935 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/teams/PersonaResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/teams/PersonaResource.java @@ -162,8 +162,13 @@ public EntityHistory listVersions( @QueryParam("offset") @DefaultValue("0") @Min(0) - int offset) { - return super.listVersionsInternal(securityContext, id, limit, offset); + int offset, + @Parameter( + description = + "Filter versions by field changes. Returns only versions where the specified field was added, updated, or deleted") + @QueryParam("fieldChanged") + String fieldChanged) { + return super.listVersionsInternal(securityContext, id, limit, offset, fieldChanged); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/teams/RoleResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/teams/RoleResource.java index e5dd507c200d..d742ec3afb9a 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/teams/RoleResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/teams/RoleResource.java @@ -218,8 +218,13 @@ public EntityHistory listVersions( @QueryParam("offset") @DefaultValue("0") @Min(0) - int offset) { - return super.listVersionsInternal(securityContext, id, limit, offset); + int offset, + @Parameter( + description = + "Filter versions by field changes. Returns only versions where the specified field was added, updated, or deleted") + @QueryParam("fieldChanged") + String fieldChanged) { + return super.listVersionsInternal(securityContext, id, limit, offset, fieldChanged); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/teams/TeamResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/teams/TeamResource.java index 2a4983c27b78..332ddf81b1d7 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/teams/TeamResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/teams/TeamResource.java @@ -263,8 +263,13 @@ public EntityHistory listVersions( @QueryParam("offset") @DefaultValue("0") @Min(0) - int offset) { - return super.listVersionsInternal(securityContext, id, limit, offset); + int offset, + @Parameter( + description = + "Filter versions by field changes. Returns only versions where the specified field was added, updated, or deleted") + @QueryParam("fieldChanged") + String fieldChanged) { + return super.listVersionsInternal(securityContext, id, limit, offset, fieldChanged); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/teams/UserResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/teams/UserResource.java index d7e685d45da7..b4bebf262fe1 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/teams/UserResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/teams/UserResource.java @@ -407,8 +407,13 @@ public EntityHistory listVersions( @QueryParam("offset") @DefaultValue("0") @Min(0) - int offset) { - return super.listVersionsInternal(securityContext, id, limit, offset); + int offset, + @Parameter( + description = + "Filter versions by field changes. Returns only versions where the specified field was added, updated, or deleted") + @QueryParam("fieldChanged") + String fieldChanged) { + return super.listVersionsInternal(securityContext, id, limit, offset, fieldChanged); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/topics/TopicResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/topics/TopicResource.java index e8a2a05e02e1..0abd798a23fb 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/topics/TopicResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/topics/TopicResource.java @@ -188,8 +188,13 @@ public EntityHistory listVersions( @QueryParam("offset") @DefaultValue("0") @Min(0) - int offset) { - return super.listVersionsInternal(securityContext, id, limit, offset); + int offset, + @Parameter( + description = + "Filter versions by field changes. Returns only versions where the specified field was added, updated, or deleted") + @QueryParam("fieldChanged") + String fieldChanged) { + return super.listVersionsInternal(securityContext, id, limit, offset, fieldChanged); } @GET diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/types/TypeResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/types/TypeResource.java index fffa938c9d83..53cd6e7be036 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/types/TypeResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/types/TypeResource.java @@ -314,8 +314,13 @@ public EntityHistory listVersions( @QueryParam("offset") @DefaultValue("0") @Min(0) - int offset) { - return super.listVersionsInternal(securityContext, id, limit, offset); + int offset, + @Parameter( + description = + "Filter versions by field changes. Returns only versions where the specified field was added, updated, or deleted") + @QueryParam("fieldChanged") + String fieldChanged) { + return super.listVersionsInternal(securityContext, id, limit, offset, fieldChanged); } @GET diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/VersionPages/PaginatedVersionHistory.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/VersionPages/PaginatedVersionHistory.spec.ts index 0ead6156a413..df26e525c2cc 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/VersionPages/PaginatedVersionHistory.spec.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/VersionPages/PaginatedVersionHistory.spec.ts @@ -10,98 +10,71 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { expect, test } from '@playwright/test'; +import { Page, expect, test as base } from '@playwright/test'; import { TableClass } from '../../support/entity/TableClass'; import { UserClass } from '../../support/user/UserClass'; import { performAdminLogin } from '../../utils/admin'; import { redirectToHomePage } from '../../utils/common'; +const table = new TableClass(); +const adminUser = new UserClass(); + +const test = base.extend<{ page: Page }>({ + page: async ({ browser }, use) => { + const adminPage = await browser.newPage(); + await adminUser.login(adminPage); + await use(adminPage); + await adminPage.close(); + }, +}); + test.describe('Paginated Version History', () => { - const table = new TableClass(); - const user2 = new UserClass(); + test.beforeAll( + 'Setup entity with versions', + async ({ browser }) => { + test.setTimeout(120_000); - test.beforeAll('Setup entity with versions via alternating users', async ({ - browser, - }) => { - test.setTimeout(120_000); - const { apiContext: adminApi, afterAction } = - await performAdminLogin(browser); - - await user2.create(adminApi); - await user2.setAdminRole(adminApi); - const user2Page = await browser.newPage(); - await user2.login(user2Page); - const user2Token = await user2Page.evaluate(() => - localStorage.getItem('oidcIdToken') - ); - const user2Api = await browser.newContext().then((ctx) => - ctx.request.newContext({ - baseURL: process.env.PLAYWRIGHT_BASE_URL ?? 'http://localhost:8585', - extraHTTPHeaders: { - Authorization: `Bearer ${user2Token}`, - }, - }) - ); - await user2Page.close(); + const { apiContext, afterAction } = await performAdminLogin(browser); - await table.create(adminApi); + await adminUser.create(apiContext); + await adminUser.setAdminRole(apiContext); - const fqn = table.entityResponseData?.fullyQualifiedName; + await table.create(apiContext); - await adminApi.patch(`/api/v1/tables/name/${fqn}`, { - data: [ - { - op: 'add', - path: '/description', - value: 'Admin patch 1', - }, - ], - headers: { 'Content-Type': 'application/json-patch+json' }, - }); - - await user2Api.patch(`/api/v1/tables/name/${fqn}`, { - data: [ - { - op: 'replace', - path: '/description', - value: 'User2 patch 2', - }, - ], - headers: { 'Content-Type': 'application/json-patch+json' }, - }); - - await adminApi.patch(`/api/v1/tables/name/${fqn}`, { - data: [ - { - op: 'replace', - path: '/description', - value: 'Admin patch 3', - }, - ], - headers: { 'Content-Type': 'application/json-patch+json' }, - }); - - await user2Api.dispose(); - await afterAction(); - }); + await table.patch({ + apiContext, + patchData: [ + { + op: 'add', + path: '/description', + value: 'Description for pagination test', + }, + ], + }); + + await afterAction(); + } + ); test.afterAll('Cleanup', async ({ browser }) => { const { apiContext, afterAction } = await performAdminLogin(browser); await table.delete(apiContext); - await user2.delete(apiContext); + await adminUser.delete(apiContext); await afterAction(); }); - test.use({ storageState: 'playwright/.auth/admin.json' }); - test('should call versions API with pagination params and return paging metadata', async ({ page, }) => { test.slow(); await redirectToHomePage(page); - await table.visitEntityPage(page); + + const fqn = table.entityResponseData?.fullyQualifiedName; + + await page.goto(`/table/${fqn}`); await page.waitForLoadState('networkidle'); + await page.waitForSelector('[data-testid="version-button"]'); const versionsApiCall = page.waitForResponse( (response) => @@ -115,7 +88,7 @@ test.describe('Paginated Version History', () => { const responseBody = await response.json(); expect(responseBody.paging).toBeDefined(); - expect(responseBody.paging.total).toBeGreaterThanOrEqual(3); + expect(responseBody.paging.total).toBeGreaterThanOrEqual(2); expect(responseBody.paging.limit).toBe(20); expect(responseBody.paging.offset).toBe(0); @@ -127,6 +100,84 @@ test.describe('Paginated Version History', () => { const count = await versionSelectors.count(); - expect(count).toBeGreaterThanOrEqual(3); + expect(count).toBeGreaterThanOrEqual(2); + }); + + test('should load more versions on scroll via infinite scroll', async ({ + page, + }) => { + test.slow(); + + await redirectToHomePage(page); + + const fqn = table.entityResponseData?.fullyQualifiedName; + const entityId = table.entityResponseData?.id; + let totalVersionCount = 0; + let callCount = 0; + + // Intercept the versions API to simulate pagination by modifying the response. + // The first call returns only the first version; the sentinel triggers the second call + // which returns the remaining versions. + await page.route( + (url) => + url.pathname.includes(`${entityId}/versions`) && + !url.pathname.includes('/versions/'), + async (route) => { + callCount++; + const currentCall = callCount; + + const response = await route.fetch(); + const body = await response.json(); + + if (currentCall === 1 && body.versions?.length >= 2) { + totalVersionCount = body.versions.length; + + await route.fulfill({ + response, + json: { + ...body, + versions: [body.versions[0]], + paging: { offset: 0, limit: 1, total: totalVersionCount }, + }, + }); + } else if (currentCall === 2 && totalVersionCount > 0) { + // Second call: return remaining versions from the full set + // The server may return different data since offset differs, + // so we use the cached total to build proper paging + await route.fulfill({ + response, + json: { + ...body, + paging: { + offset: 1, + limit: 1, + total: totalVersionCount, + }, + }, + }); + } else { + await route.fulfill({ response, json: body }); + } + } + ); + + await page.goto(`/table/${fqn}`); + await page.waitForLoadState('networkidle'); + await page.waitForSelector('[data-testid="version-button"]'); + + await page.locator('[data-testid="version-button"]').click(); + + // The sentinel is immediately visible with only 1 version, so infinite scroll + // auto-triggers the second API call. Wait for both versions to render. + const versionSelectors = page.locator( + '[data-testid^="version-selector-v"]' + ); + + await expect(versionSelectors.nth(1)).toBeVisible({ timeout: 15_000 }); + + const totalCount = await versionSelectors.count(); + + expect(totalCount).toBeGreaterThanOrEqual(2); + expect(callCount).toBeGreaterThanOrEqual(2); }); }); diff --git a/openmetadata-ui/src/main/resources/ui/src/rest/SearchIndexAPI.ts b/openmetadata-ui/src/main/resources/ui/src/rest/SearchIndexAPI.ts index 55a9c70946b9..02f27aab2e89 100644 --- a/openmetadata-ui/src/main/resources/ui/src/rest/SearchIndexAPI.ts +++ b/openmetadata-ui/src/main/resources/ui/src/rest/SearchIndexAPI.ts @@ -137,7 +137,7 @@ export const updateSearchIndexVotes = async (id: string, data: QueryVote) => { export const getSearchIndexVersions = async ( id: string, - params?: { limit?: number; offset?: number } + params?: { limit?: number; offset?: number; fieldChanged?: string } ) => { const url = `/searchIndexes/${id}/versions`; diff --git a/openmetadata-ui/src/main/resources/ui/src/rest/apiCollectionsAPI.ts b/openmetadata-ui/src/main/resources/ui/src/rest/apiCollectionsAPI.ts index 60174b620ddd..06c969ba2cc4 100644 --- a/openmetadata-ui/src/main/resources/ui/src/rest/apiCollectionsAPI.ts +++ b/openmetadata-ui/src/main/resources/ui/src/rest/apiCollectionsAPI.ts @@ -82,7 +82,7 @@ export const restoreApiCollection = async (id: string) => { export const getApiCollectionVersions = async ( id: string, - params?: { limit?: number; offset?: number } + params?: { limit?: number; offset?: number; fieldChanged?: string } ) => { const url = `/apiCollections/${id}/versions`; diff --git a/openmetadata-ui/src/main/resources/ui/src/rest/apiEndpointsAPI.ts b/openmetadata-ui/src/main/resources/ui/src/rest/apiEndpointsAPI.ts index dafa0b7cf0e2..d6a8faa29490 100644 --- a/openmetadata-ui/src/main/resources/ui/src/rest/apiEndpointsAPI.ts +++ b/openmetadata-ui/src/main/resources/ui/src/rest/apiEndpointsAPI.ts @@ -82,7 +82,7 @@ export const restoreApiEndPoint = async (id: string) => { export const getApiEndPointVersions = async ( id: string, - params?: { limit?: number; offset?: number } + params?: { limit?: number; offset?: number; fieldChanged?: string } ) => { const url = `/apiEndpoints/${id}/versions`; diff --git a/openmetadata-ui/src/main/resources/ui/src/rest/chartsAPI.ts b/openmetadata-ui/src/main/resources/ui/src/rest/chartsAPI.ts index 579046bcf85b..451e1c7e9190 100644 --- a/openmetadata-ui/src/main/resources/ui/src/rest/chartsAPI.ts +++ b/openmetadata-ui/src/main/resources/ui/src/rest/chartsAPI.ts @@ -42,7 +42,7 @@ const BASE_URL = '/charts'; export const getChartVersions = async ( id: string, - params?: { limit?: number; offset?: number } + params?: { limit?: number; offset?: number; fieldChanged?: string } ) => { const url = `${BASE_URL}/${id}/versions`; diff --git a/openmetadata-ui/src/main/resources/ui/src/rest/dashboardAPI.ts b/openmetadata-ui/src/main/resources/ui/src/rest/dashboardAPI.ts index 77d68f45d53c..81bfa8bf8de6 100644 --- a/openmetadata-ui/src/main/resources/ui/src/rest/dashboardAPI.ts +++ b/openmetadata-ui/src/main/resources/ui/src/rest/dashboardAPI.ts @@ -42,7 +42,7 @@ const BASE_URL = '/dashboards'; export const getDashboardVersions = async ( id: string, - params?: { limit?: number; offset?: number } + params?: { limit?: number; offset?: number; fieldChanged?: string } ) => { const url = `${BASE_URL}/${id}/versions`; diff --git a/openmetadata-ui/src/main/resources/ui/src/rest/dataModelsAPI.ts b/openmetadata-ui/src/main/resources/ui/src/rest/dataModelsAPI.ts index 9b6dc957a84e..871f89b4df30 100644 --- a/openmetadata-ui/src/main/resources/ui/src/rest/dataModelsAPI.ts +++ b/openmetadata-ui/src/main/resources/ui/src/rest/dataModelsAPI.ts @@ -73,7 +73,7 @@ export const removeDataModelFollower = async (id: string, userId: string) => { export const getDataModelVersionsList = async ( id: string, - params?: { limit?: number; offset?: number } + params?: { limit?: number; offset?: number; fieldChanged?: string } ) => { const url = `${URL}/${id}/versions`; diff --git a/openmetadata-ui/src/main/resources/ui/src/rest/dataProductAPI.ts b/openmetadata-ui/src/main/resources/ui/src/rest/dataProductAPI.ts index 18cf8e3ae100..460a76f66266 100644 --- a/openmetadata-ui/src/main/resources/ui/src/rest/dataProductAPI.ts +++ b/openmetadata-ui/src/main/resources/ui/src/rest/dataProductAPI.ts @@ -78,7 +78,7 @@ export const deleteDataProduct = (id: string) => { export const getDataProductVersionsList = async ( id: string, - params?: { limit?: number; offset?: number } + params?: { limit?: number; offset?: number; fieldChanged?: string } ) => { const url = `${BASE_URL}/${id}/versions`; const response = await APIClient.get(url, { params }); diff --git a/openmetadata-ui/src/main/resources/ui/src/rest/databaseAPI.ts b/openmetadata-ui/src/main/resources/ui/src/rest/databaseAPI.ts index a08bdd95d4f0..68d1226ce4da 100644 --- a/openmetadata-ui/src/main/resources/ui/src/rest/databaseAPI.ts +++ b/openmetadata-ui/src/main/resources/ui/src/rest/databaseAPI.ts @@ -189,7 +189,7 @@ export const restoreDatabase = async (id: string) => { export const getDatabaseVersions = async ( id: string, - params?: { limit?: number; offset?: number } + params?: { limit?: number; offset?: number; fieldChanged?: string } ) => { const url = `/databases/${id}/versions`; @@ -208,7 +208,7 @@ export const getDatabaseVersionData = async (id: string, version: string) => { export const getDatabaseSchemaVersions = async ( id: string, - params?: { limit?: number; offset?: number } + params?: { limit?: number; offset?: number; fieldChanged?: string } ) => { const url = `/databaseSchemas/${id}/versions`; diff --git a/openmetadata-ui/src/main/resources/ui/src/rest/domainAPI.ts b/openmetadata-ui/src/main/resources/ui/src/rest/domainAPI.ts index 25d50818f247..27d90d9d0dc9 100644 --- a/openmetadata-ui/src/main/resources/ui/src/rest/domainAPI.ts +++ b/openmetadata-ui/src/main/resources/ui/src/rest/domainAPI.ts @@ -80,7 +80,7 @@ export const getDomainByName = async (fqn: string, params?: ListParams) => { export const getDomainVersionsList = async ( id: string, - params?: { limit?: number; offset?: number } + params?: { limit?: number; offset?: number; fieldChanged?: string } ) => { const url = `${BASE_URL}/${id}/versions`; const response = await APIClient.get(url, { params }); diff --git a/openmetadata-ui/src/main/resources/ui/src/rest/driveAPI.ts b/openmetadata-ui/src/main/resources/ui/src/rest/driveAPI.ts index 01f22f9d4833..646e3deaf8f9 100644 --- a/openmetadata-ui/src/main/resources/ui/src/rest/driveAPI.ts +++ b/openmetadata-ui/src/main/resources/ui/src/rest/driveAPI.ts @@ -176,7 +176,7 @@ export const updateDriveAssetVotes = async < export const getDriveAssetsVersions = async ( id: string, entityType: DriveAssetEntityTypes, - params?: { limit?: number; offset?: number } + params?: { limit?: number; offset?: number; fieldChanged?: string } ) => { const API = APIByEntityType[entityType]; const url = `${BASE_URL}${API}/${id}/versions`; diff --git a/openmetadata-ui/src/main/resources/ui/src/rest/glossaryAPI.ts b/openmetadata-ui/src/main/resources/ui/src/rest/glossaryAPI.ts index 15d95ecba6a1..32ce9dac1e9b 100644 --- a/openmetadata-ui/src/main/resources/ui/src/rest/glossaryAPI.ts +++ b/openmetadata-ui/src/main/resources/ui/src/rest/glossaryAPI.ts @@ -204,7 +204,7 @@ export const exportGlossaryTermsInCSVFormat = async (glossaryName: string) => { export const getGlossaryVersionsList = async ( id: string, - params?: { limit?: number; offset?: number } + params?: { limit?: number; offset?: number; fieldChanged?: string } ) => { const url = `/glossaries/${id}/versions`; @@ -222,7 +222,7 @@ export const getGlossaryVersion = async (id: string, version: string) => { export const getGlossaryTermsVersionsList = async ( id: string, - params?: { limit?: number; offset?: number } + params?: { limit?: number; offset?: number; fieldChanged?: string } ) => { const url = `/glossaryTerms/${id}/versions`; diff --git a/openmetadata-ui/src/main/resources/ui/src/rest/metricsAPI.ts b/openmetadata-ui/src/main/resources/ui/src/rest/metricsAPI.ts index 477bc7b72e43..155703937add 100644 --- a/openmetadata-ui/src/main/resources/ui/src/rest/metricsAPI.ts +++ b/openmetadata-ui/src/main/resources/ui/src/rest/metricsAPI.ts @@ -70,7 +70,7 @@ export const restoreMetric = async (id: string) => { export const getMetricVersions = async ( id: string, - params?: { limit?: number; offset?: number } + params?: { limit?: number; offset?: number; fieldChanged?: string } ) => { const response = await APIClient.get( `/metrics/${id}/versions`, diff --git a/openmetadata-ui/src/main/resources/ui/src/rest/mlModelAPI.ts b/openmetadata-ui/src/main/resources/ui/src/rest/mlModelAPI.ts index 72f12059f5bf..d3c158884c3c 100644 --- a/openmetadata-ui/src/main/resources/ui/src/rest/mlModelAPI.ts +++ b/openmetadata-ui/src/main/resources/ui/src/rest/mlModelAPI.ts @@ -30,7 +30,7 @@ const BASE_URL = '/mlmodels'; export const getMlModelVersions = async ( id: string, - params?: { limit?: number; offset?: number } + params?: { limit?: number; offset?: number; fieldChanged?: string } ) => { const url = `${BASE_URL}/${id}/versions`; diff --git a/openmetadata-ui/src/main/resources/ui/src/rest/pipelineAPI.ts b/openmetadata-ui/src/main/resources/ui/src/rest/pipelineAPI.ts index 938434815ea9..aa8641c937e9 100644 --- a/openmetadata-ui/src/main/resources/ui/src/rest/pipelineAPI.ts +++ b/openmetadata-ui/src/main/resources/ui/src/rest/pipelineAPI.ts @@ -31,7 +31,7 @@ const BASE_URL = '/pipelines'; export const getPipelineVersions = async ( id: string, - params?: { limit?: number; offset?: number } + params?: { limit?: number; offset?: number; fieldChanged?: string } ) => { const url = `${BASE_URL}/${id}/versions`; diff --git a/openmetadata-ui/src/main/resources/ui/src/rest/serviceAPI.ts b/openmetadata-ui/src/main/resources/ui/src/rest/serviceAPI.ts index 0d9414bfd9e6..2b16f5368c5f 100644 --- a/openmetadata-ui/src/main/resources/ui/src/rest/serviceAPI.ts +++ b/openmetadata-ui/src/main/resources/ui/src/rest/serviceAPI.ts @@ -165,7 +165,7 @@ export const patchDomainSupportedService = async ( export const getServiceVersions = async ( serviceCategory: string, id: string, - params?: { limit?: number; offset?: number } + params?: { limit?: number; offset?: number; fieldChanged?: string } ) => { const url = `/services/${serviceCategory}/${id}/versions`; diff --git a/openmetadata-ui/src/main/resources/ui/src/rest/storageAPI.ts b/openmetadata-ui/src/main/resources/ui/src/rest/storageAPI.ts index 73c49daf1928..66383dc6162c 100644 --- a/openmetadata-ui/src/main/resources/ui/src/rest/storageAPI.ts +++ b/openmetadata-ui/src/main/resources/ui/src/rest/storageAPI.ts @@ -126,7 +126,7 @@ export const removeContainerFollower = async (id: string, userId: string) => { export const getContainerVersions = async ( id: string, - params?: { limit?: number; offset?: number } + params?: { limit?: number; offset?: number; fieldChanged?: string } ) => { const url = `${BASE_URL}/${id}/versions`; diff --git a/openmetadata-ui/src/main/resources/ui/src/rest/storedProceduresAPI.ts b/openmetadata-ui/src/main/resources/ui/src/rest/storedProceduresAPI.ts index a4d9f0d704a3..cbdb2595c356 100644 --- a/openmetadata-ui/src/main/resources/ui/src/rest/storedProceduresAPI.ts +++ b/openmetadata-ui/src/main/resources/ui/src/rest/storedProceduresAPI.ts @@ -106,7 +106,7 @@ export const removeStoredProceduresFollower = async ( export const getStoredProceduresVersionsList = async ( id: string, - params?: { limit?: number; offset?: number } + params?: { limit?: number; offset?: number; fieldChanged?: string } ) => { const url = `${URL}/${id}/versions`; diff --git a/openmetadata-ui/src/main/resources/ui/src/rest/tableAPI.ts b/openmetadata-ui/src/main/resources/ui/src/rest/tableAPI.ts index 7148a70a3568..644ef54baa8c 100644 --- a/openmetadata-ui/src/main/resources/ui/src/rest/tableAPI.ts +++ b/openmetadata-ui/src/main/resources/ui/src/rest/tableAPI.ts @@ -47,7 +47,7 @@ const BASE_URL = '/tables'; export const getTableVersions = async ( id: string, - params?: { limit?: number; offset?: number } + params?: { limit?: number; offset?: number; fieldChanged?: string } ) => { const url = `${BASE_URL}/${id}/versions`; diff --git a/openmetadata-ui/src/main/resources/ui/src/rest/tagAPI.ts b/openmetadata-ui/src/main/resources/ui/src/rest/tagAPI.ts index f095729e2762..a143bdc655fb 100644 --- a/openmetadata-ui/src/main/resources/ui/src/rest/tagAPI.ts +++ b/openmetadata-ui/src/main/resources/ui/src/rest/tagAPI.ts @@ -179,7 +179,7 @@ export const deleteTag = async (tagId: string) => { export const getClassificationVersionsList = async ( id: string, - params?: { limit?: number; offset?: number } + params?: { limit?: number; offset?: number; fieldChanged?: string } ) => { const url = `${BASE_URL}/${id}/versions`; diff --git a/openmetadata-ui/src/main/resources/ui/src/rest/topicsAPI.ts b/openmetadata-ui/src/main/resources/ui/src/rest/topicsAPI.ts index c4a1bd286d3c..0803420a3f74 100644 --- a/openmetadata-ui/src/main/resources/ui/src/rest/topicsAPI.ts +++ b/openmetadata-ui/src/main/resources/ui/src/rest/topicsAPI.ts @@ -30,7 +30,7 @@ const BASE_URL = '/topics'; export const getTopicVersions = async ( id: string, - params?: { limit?: number; offset?: number } + params?: { limit?: number; offset?: number; fieldChanged?: string } ) => { const url = `${BASE_URL}/${id}/versions`; From ae8dba41aabe09572d9f8b25751150784e5c5290 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 7 Mar 2026 03:15:15 +0000 Subject: [PATCH 03/20] Update generated TypeScript types --- .../ui/src/generated/type/entityHistory.ts | 29 ++++++++++++++----- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/openmetadata-ui/src/main/resources/ui/src/generated/type/entityHistory.ts b/openmetadata-ui/src/main/resources/ui/src/generated/type/entityHistory.ts index eb245023d536..39e8ac51c092 100644 --- a/openmetadata-ui/src/main/resources/ui/src/generated/type/entityHistory.ts +++ b/openmetadata-ui/src/main/resources/ui/src/generated/type/entityHistory.ts @@ -19,17 +19,32 @@ export interface EntityHistory { * produced. */ entityType: string; - /** - * Type used for cursor based pagination information in GET list responses. - */ - paging?: Paging; + paging?: Paging; versions: any[]; } +/** + * Type used for cursor based pagination information in GET list responses. + */ export interface Paging { - after?: string; + /** + * After cursor used for getting the next page (see API pagination for details). + */ + after?: string; + /** + * Before cursor used for getting the previous page (see API pagination for details). + */ before?: string; - limit?: number; + /** + * Limit used in case of offset based pagination. + */ + limit?: number; + /** + * Offset used in case of offset based pagination. + */ offset?: number; - total: number; + /** + * Total number of entries available to page through. + */ + total: number; } From 64cb18bb722616a53870cc9bf73b197db6f9f426 Mon Sep 17 00:00:00 2001 From: Sriharsha Chintalapani Date: Fri, 6 Mar 2026 19:34:47 -0800 Subject: [PATCH 04/20] address comments --- .../ingestion/ometa/mixins/version_mixin.py | 4 ++-- .../service/jdbi3/EntityRepository.java | 7 ++++--- .../service/resources/EntityResource.java | 2 +- .../EntityVersionPage.component.tsx | 16 +++++++--------- 4 files changed, 14 insertions(+), 15 deletions(-) diff --git a/ingestion/src/metadata/ingestion/ometa/mixins/version_mixin.py b/ingestion/src/metadata/ingestion/ometa/mixins/version_mixin.py index d60a20e77815..ba151f5b9dac 100644 --- a/ingestion/src/metadata/ingestion/ometa/mixins/version_mixin.py +++ b/ingestion/src/metadata/ingestion/ometa/mixins/version_mixin.py @@ -141,7 +141,7 @@ def get_entity_history_by_timeline( limit: int = 10, before: Optional[str] = None, after: Optional[str] = None, - ) -> Response: + ) -> dict: """ Retrieve entity versions within a time range @@ -162,7 +162,7 @@ def get_entity_history_by_timeline( Returns ------- - Response + dict paginated list of entity versions within the time range """ params = { 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 3b683293af8d..ded1901b3b03 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 @@ -1302,7 +1302,7 @@ public final EntityHistoryWithOffset listVersionsWithOffset( EntityHistory entityHistory = new EntityHistory().withEntityType(entityType).withVersions(versions).withPaging(paging); - return new EntityHistoryWithOffset(entityHistory, offset + limit); + return new EntityHistoryWithOffset(entityHistory, Math.min(offset + limit, total)); } private EntityHistoryWithOffset listVersionsWithFieldFilter( @@ -1350,7 +1350,7 @@ private EntityHistoryWithOffset listVersionsWithFieldFilter( EntityHistory entityHistory = new EntityHistory().withEntityType(entityType).withVersions(versions).withPaging(paging); - return new EntityHistoryWithOffset(entityHistory, offset + limit); + return new EntityHistoryWithOffset(entityHistory, Math.min(offset + limit, total)); } private boolean latestVersionMatchesFieldChanged(T latest, String fieldChanged) { @@ -1368,7 +1368,8 @@ private boolean fieldChangeListContains(List fieldChanges, String f return false; } for (FieldChange fc : fieldChanges) { - if (fc.getName() != null && fc.getName().contains(fieldName)) { + if (fc.getName() != null + && (fc.getName().equals(fieldName) || fc.getName().endsWith("." + fieldName))) { return true; } } diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/EntityResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/EntityResource.java index 670e6d5accc5..7fa04127d8b8 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/EntityResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/EntityResource.java @@ -378,7 +378,7 @@ protected EntityHistory listVersionsInternal( } if (fieldChanged != null && !fieldChanged.isEmpty()) { return repository - .listVersionsWithOffset(id, Integer.MAX_VALUE, 0, fieldChanged) + .listVersionsWithOffset(id, Integer.MAX_VALUE, offset, fieldChanged) .entityHistory(); } return repository.listVersions(id); diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/EntityVersionPage/EntityVersionPage.component.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/EntityVersionPage/EntityVersionPage.component.tsx index 0c4584f04b8d..81d409e6a77c 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/EntityVersionPage/EntityVersionPage.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/EntityVersionPage/EntityVersionPage.component.tsx @@ -505,17 +505,15 @@ const EntityVersionPage: FunctionComponent = () => { ], }; + if (moreVersions.paging) { + const totalLoaded = combined.versions?.length ?? 0; + setHasMoreVersions(totalLoaded < moreVersions.paging.total); + } else { + setHasMoreVersions(false); + } + return combined; }); - - if (moreVersions.paging) { - const totalLoaded = - (versionList.versions?.length ?? 0) + - (moreVersions.versions?.length ?? 0); - setHasMoreVersions(totalLoaded < moreVersions.paging.total); - } else { - setHasMoreVersions(false); - } } finally { setIsLoadingMore(false); } From 3216f72eaea42b50bde10cc07ae6642448985329 Mon Sep 17 00:00:00 2001 From: Sriharsha Chintalapani Date: Fri, 6 Mar 2026 20:24:47 -0800 Subject: [PATCH 05/20] fix py check --- .../src/metadata/ingestion/ometa/mixins/version_mixin.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/ingestion/src/metadata/ingestion/ometa/mixins/version_mixin.py b/ingestion/src/metadata/ingestion/ometa/mixins/version_mixin.py index ba151f5b9dac..a7fd7be7f750 100644 --- a/ingestion/src/metadata/ingestion/ometa/mixins/version_mixin.py +++ b/ingestion/src/metadata/ingestion/ometa/mixins/version_mixin.py @@ -175,6 +175,4 @@ def get_entity_history_by_timeline( if after is not None: params["after"] = after - return self.client.get( - f"{self.get_suffix(entity)}/history", data=params - ) + return self.client.get(f"{self.get_suffix(entity)}/history", data=params) From c5162d2df6fece8e419b70e74235e8725dbc21fd Mon Sep 17 00:00:00 2001 From: Sriharsha Chintalapani Date: Fri, 6 Mar 2026 23:42:49 -0800 Subject: [PATCH 06/20] Address comments --- .../native/1.13.0/mysql/schemaChanges.sql | 12 ++++-- .../service/jdbi3/CollectionDAO.java | 43 +++++++++++++------ .../service/jdbi3/EntityRepository.java | 13 ++---- .../src/main/resources/ui/src/rest/testAPI.ts | 5 ++- 4 files changed, 47 insertions(+), 26 deletions(-) diff --git a/bootstrap/sql/migrations/native/1.13.0/mysql/schemaChanges.sql b/bootstrap/sql/migrations/native/1.13.0/mysql/schemaChanges.sql index 595a93e585c7..5c96062ddbd2 100644 --- a/bootstrap/sql/migrations/native/1.13.0/mysql/schemaChanges.sql +++ b/bootstrap/sql/migrations/native/1.13.0/mysql/schemaChanges.sql @@ -24,7 +24,11 @@ WHERE JSON_CONTAINS_PATH(json, 'one', '$.preview'); -- Add changeDescriptionDoc generated column to entity_extension for efficient field-change filtering -- Supports filtering entity versions by specific metadata changes (e.g., tags, schema, description) -ALTER TABLE entity_extension - ADD COLUMN changeDescriptionDoc TEXT - GENERATED ALWAYS AS (JSON_UNQUOTE(JSON_EXTRACT(json, '$.changeDescription'))) - STORED; +SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS + WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'entity_extension' AND COLUMN_NAME = 'changeDescriptionDoc'); +SET @sql = IF(@col_exists = 0, + 'ALTER TABLE entity_extension ADD COLUMN changeDescriptionDoc TEXT GENERATED ALWAYS AS (JSON_UNQUOTE(JSON_EXTRACT(json, ''$.changeDescription''))) STORED', + 'SELECT 1'); +PREPARE stmt FROM @sql; +EXECUTE stmt; +DEALLOCATE PREPARE stmt; diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/CollectionDAO.java b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/CollectionDAO.java index 70fc8d015574..3394a45dc0b9 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/CollectionDAO.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/CollectionDAO.java @@ -1410,11 +1410,20 @@ int getEntityHistoryByTimestampRangeCount( @Bind("entityType") String entityType); @RegisterRowMapper(ExtensionMapper.class) - @SqlQuery( - "SELECT extension, json FROM entity_extension WHERE id = :id AND extension " - + "LIKE CONCAT (:extensionPrefix, '.%') " - + "ORDER BY extension DESC " - + "LIMIT :limit OFFSET :offset") + @ConnectionAwareSqlQuery( + value = + "SELECT extension, json FROM entity_extension WHERE id = :id AND extension " + + "LIKE CONCAT(:extensionPrefix, '.%') " + + "ORDER BY CAST(SUBSTRING_INDEX(extension, '.', -2) AS DECIMAL(10,1)) DESC " + + "LIMIT :limit OFFSET :offset", + connectionType = MYSQL) + @ConnectionAwareSqlQuery( + value = + "SELECT extension, json FROM entity_extension WHERE id = :id AND extension " + + "LIKE CONCAT(:extensionPrefix, '.%') " + + "ORDER BY CAST(regexp_replace(extension, '^.*version\\.', '') AS DECIMAL(10,1)) DESC " + + "LIMIT :limit OFFSET :offset", + connectionType = POSTGRES) List getExtensionsWithOffset( @BindUUID("id") UUID id, @Bind("extensionPrefix") String extensionPrefix, @@ -1427,12 +1436,22 @@ List getExtensionsWithOffset( int getExtensionCount(@BindUUID("id") UUID id, @Bind("extensionPrefix") String extensionPrefix); @RegisterRowMapper(ExtensionMapper.class) - @SqlQuery( - "SELECT extension, json FROM entity_extension WHERE id = :id " - + "AND extension LIKE CONCAT(:extensionPrefix, '.%') " - + "AND changeDescriptionDoc LIKE CONCAT('%', :fieldName, '%') " - + "ORDER BY extension DESC " - + "LIMIT :limit OFFSET :offset") + @ConnectionAwareSqlQuery( + value = + "SELECT extension, json FROM entity_extension WHERE id = :id " + + "AND extension LIKE CONCAT(:extensionPrefix, '.%') " + + "AND changeDescriptionDoc LIKE CONCAT('%', :fieldName, '%') ESCAPE '\\' " + + "ORDER BY CAST(SUBSTRING_INDEX(extension, '.', -2) AS DECIMAL(10,1)) DESC " + + "LIMIT :limit OFFSET :offset", + connectionType = MYSQL) + @ConnectionAwareSqlQuery( + value = + "SELECT extension, json FROM entity_extension WHERE id = :id " + + "AND extension LIKE CONCAT(:extensionPrefix, '.%') " + + "AND changeDescriptionDoc LIKE CONCAT('%', :fieldName, '%') ESCAPE '\\' " + + "ORDER BY CAST(regexp_replace(extension, '^.*version\\.', '') AS DECIMAL(10,1)) DESC " + + "LIMIT :limit OFFSET :offset", + connectionType = POSTGRES) List getExtensionsWithFieldChanged( @BindUUID("id") UUID id, @Bind("extensionPrefix") String extensionPrefix, @@ -1443,7 +1462,7 @@ List getExtensionsWithFieldChanged( @SqlQuery( "SELECT COUNT(*) FROM entity_extension WHERE id = :id " + "AND extension LIKE CONCAT(:extensionPrefix, '.%') " - + "AND changeDescriptionDoc LIKE CONCAT('%', :fieldName, '%') ") + + "AND changeDescriptionDoc LIKE CONCAT('%', :fieldName, '%') ESCAPE '\\' ") int getExtensionCountWithFieldChanged( @BindUUID("id") UUID id, @Bind("extensionPrefix") String extensionPrefix, 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 ded1901b3b03..51faf2ec6e55 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 @@ -1266,7 +1266,8 @@ public final EntityHistoryWithOffset listVersionsWithOffset( String extensionPrefix = EntityUtil.getVersionExtensionPrefix(entityType); if (fieldChanged != null && !fieldChanged.isEmpty()) { - return listVersionsWithFieldFilter(id, latest, extensionPrefix, limit, offset, fieldChanged); + String sanitized = fieldChanged.replace("\\", "\\\\").replace("%", "\\%").replace("_", "\\_"); + return listVersionsWithFieldFilter(id, latest, extensionPrefix, limit, offset, sanitized); } final List versions = new ArrayList<>(); @@ -1287,10 +1288,7 @@ public final EntityHistoryWithOffset listVersionsWithOffset( daoCollection .entityExtensionDAO() .getExtensionsWithOffset(id, extensionPrefix, dbLimit, dbOffset); - List oldVersions = new ArrayList<>(); - records.forEach(r -> oldVersions.add(new EntityVersionPair(r))); - oldVersions.sort(EntityUtil.compareVersion.reversed()); - oldVersions.forEach(version -> versions.add(version.getEntityJson())); + records.forEach(r -> versions.add(new EntityVersionPair(r).getEntityJson())); } int extensionCount = daoCollection.entityExtensionDAO().getExtensionCount(id, extensionPrefix); @@ -1331,10 +1329,7 @@ private EntityHistoryWithOffset listVersionsWithFieldFilter( daoCollection .entityExtensionDAO() .getExtensionsWithFieldChanged(id, extensionPrefix, fieldChanged, dbLimit, dbOffset); - List oldVersions = new ArrayList<>(); - records.forEach(r -> oldVersions.add(new EntityVersionPair(r))); - oldVersions.sort(EntityUtil.compareVersion.reversed()); - oldVersions.forEach(version -> versions.add(version.getEntityJson())); + records.forEach(r -> versions.add(new EntityVersionPair(r).getEntityJson())); } int extensionCount = diff --git a/openmetadata-ui/src/main/resources/ui/src/rest/testAPI.ts b/openmetadata-ui/src/main/resources/ui/src/rest/testAPI.ts index b55a46eba4a1..ad85fe24db6f 100644 --- a/openmetadata-ui/src/main/resources/ui/src/rest/testAPI.ts +++ b/openmetadata-ui/src/main/resources/ui/src/rest/testAPI.ts @@ -223,7 +223,10 @@ export const removeTestCaseFromTestSuite = async ( return response.data; }; -export const getTestCaseVersionList = async (id: string) => { +export const getTestCaseVersionList = async ( + id: string, + params?: { limit?: number; offset?: number; fieldChanged?: string } +) => { const url = `${testCaseUrl}/${id}/versions`; const response = await APIClient.get(url, { params }); From a44431ea66947a044e5077bf06891bf53f6ca395 Mon Sep 17 00:00:00 2001 From: Sriharsha Chintalapani Date: Fri, 6 Mar 2026 23:54:20 -0800 Subject: [PATCH 07/20] Address comments --- .../openmetadata/it/tests/BaseEntityIT.java | 3 + .../service/jdbi3/EntityRepository.java | 56 +++---------------- 2 files changed, 11 insertions(+), 48 deletions(-) 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 cf484a08cdd5..2c384e896a87 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 @@ -1450,6 +1450,9 @@ void get_entityVersionHistory_fieldChanged_200(TestNamespace ns) { created.setDescription("Description change for fieldChanged test"); patchEntity(created.getId().toString(), created); + created.setDescription("Second description change for fieldChanged test"); + patchEntity(created.getId().toString(), created); + org.openmetadata.schema.type.EntityHistory filtered = getVersionHistoryWithFieldChanged(created.getId(), 100, 0, "description"); assertNotNull(filtered, "Filtered version history should not be null"); 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 51faf2ec6e55..9ccb1fabe7b4 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 @@ -1261,15 +1261,16 @@ public final EntityHistoryWithOffset listVersionsWithOffset(UUID id, int limit, public final EntityHistoryWithOffset listVersionsWithOffset( UUID id, int limit, int offset, String fieldChanged) { - T latest = setFieldsInternal(find(id, ALL), putFields); - setInheritedFields(latest, putFields); String extensionPrefix = EntityUtil.getVersionExtensionPrefix(entityType); if (fieldChanged != null && !fieldChanged.isEmpty()) { String sanitized = fieldChanged.replace("\\", "\\\\").replace("%", "\\%").replace("_", "\\_"); - return listVersionsWithFieldFilter(id, latest, extensionPrefix, limit, offset, sanitized); + return listVersionsWithFieldFilter(id, extensionPrefix, limit, offset, sanitized); } + T latest = setFieldsInternal(find(id, ALL), putFields); + setInheritedFields(latest, putFields); + final List versions = new ArrayList<>(); int dbLimit; int dbOffset; @@ -1304,39 +1305,21 @@ public final EntityHistoryWithOffset listVersionsWithOffset( } private EntityHistoryWithOffset listVersionsWithFieldFilter( - UUID id, T latest, String extensionPrefix, int limit, int offset, String fieldChanged) { + UUID id, String extensionPrefix, int limit, int offset, String fieldChanged) { final List versions = new ArrayList<>(); - boolean latestMatches = latestVersionMatchesFieldChanged(latest, fieldChanged); - - int dbLimit; - int dbOffset; - - if (offset == 0) { - if (latestMatches) { - versions.add(JsonUtils.pojoToJson(latest)); - dbLimit = limit - 1; - } else { - dbLimit = limit; - } - dbOffset = 0; - } else { - dbLimit = limit; - dbOffset = latestMatches ? offset - 1 : offset; - } - if (dbLimit > 0) { + if (limit > 0) { List records = daoCollection .entityExtensionDAO() - .getExtensionsWithFieldChanged(id, extensionPrefix, fieldChanged, dbLimit, dbOffset); + .getExtensionsWithFieldChanged(id, extensionPrefix, fieldChanged, limit, offset); records.forEach(r -> versions.add(new EntityVersionPair(r).getEntityJson())); } - int extensionCount = + int total = daoCollection .entityExtensionDAO() .getExtensionCountWithFieldChanged(id, extensionPrefix, fieldChanged); - int total = extensionCount + (latestMatches ? 1 : 0); Paging paging = new Paging(); paging.setOffset(offset); @@ -1348,29 +1331,6 @@ private EntityHistoryWithOffset listVersionsWithFieldFilter( return new EntityHistoryWithOffset(entityHistory, Math.min(offset + limit, total)); } - private boolean latestVersionMatchesFieldChanged(T latest, String fieldChanged) { - ChangeDescription cd = latest.getChangeDescription(); - if (cd == null) { - return false; - } - return fieldChangeListContains(cd.getFieldsAdded(), fieldChanged) - || fieldChangeListContains(cd.getFieldsUpdated(), fieldChanged) - || fieldChangeListContains(cd.getFieldsDeleted(), fieldChanged); - } - - private boolean fieldChangeListContains(List fieldChanges, String fieldName) { - if (fieldChanges == null) { - return false; - } - for (FieldChange fc : fieldChanges) { - if (fc.getName() != null - && (fc.getName().equals(fieldName) || fc.getName().endsWith("." + fieldName))) { - return true; - } - } - return false; - } - public final ResultList listWithOffset( ListWithOffsetFunction> callable, Function countCallable, From 21f59df06b78ca30e55f6e624b9acae07f35e6c8 Mon Sep 17 00:00:00 2001 From: Sriharsha Chintalapani Date: Sat, 7 Mar 2026 09:23:49 -0800 Subject: [PATCH 08/20] Fix tests --- .../service/jdbi3/CollectionDAO.java | 45 ++++++------------- .../service/jdbi3/EntityRepository.java | 2 +- 2 files changed, 14 insertions(+), 33 deletions(-) diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/CollectionDAO.java b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/CollectionDAO.java index 3394a45dc0b9..9e6c4b7141d2 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/CollectionDAO.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/CollectionDAO.java @@ -1410,20 +1410,11 @@ int getEntityHistoryByTimestampRangeCount( @Bind("entityType") String entityType); @RegisterRowMapper(ExtensionMapper.class) - @ConnectionAwareSqlQuery( - value = - "SELECT extension, json FROM entity_extension WHERE id = :id AND extension " - + "LIKE CONCAT(:extensionPrefix, '.%') " - + "ORDER BY CAST(SUBSTRING_INDEX(extension, '.', -2) AS DECIMAL(10,1)) DESC " - + "LIMIT :limit OFFSET :offset", - connectionType = MYSQL) - @ConnectionAwareSqlQuery( - value = - "SELECT extension, json FROM entity_extension WHERE id = :id AND extension " - + "LIKE CONCAT(:extensionPrefix, '.%') " - + "ORDER BY CAST(regexp_replace(extension, '^.*version\\.', '') AS DECIMAL(10,1)) DESC " - + "LIMIT :limit OFFSET :offset", - connectionType = POSTGRES) + @SqlQuery( + "SELECT extension, json FROM entity_extension WHERE id = :id AND extension " + + "LIKE CONCAT(:extensionPrefix, '.%') " + + "ORDER BY extension DESC " + + "LIMIT :limit OFFSET :offset") List getExtensionsWithOffset( @BindUUID("id") UUID id, @Bind("extensionPrefix") String extensionPrefix, @@ -1432,26 +1423,16 @@ List getExtensionsWithOffset( @SqlQuery( "SELECT COUNT(*) FROM entity_extension WHERE id = :id AND extension " - + "LIKE CONCAT (:extensionPrefix, '.%')") + + "LIKE CONCAT(:extensionPrefix, '.%')") int getExtensionCount(@BindUUID("id") UUID id, @Bind("extensionPrefix") String extensionPrefix); @RegisterRowMapper(ExtensionMapper.class) - @ConnectionAwareSqlQuery( - value = - "SELECT extension, json FROM entity_extension WHERE id = :id " - + "AND extension LIKE CONCAT(:extensionPrefix, '.%') " - + "AND changeDescriptionDoc LIKE CONCAT('%', :fieldName, '%') ESCAPE '\\' " - + "ORDER BY CAST(SUBSTRING_INDEX(extension, '.', -2) AS DECIMAL(10,1)) DESC " - + "LIMIT :limit OFFSET :offset", - connectionType = MYSQL) - @ConnectionAwareSqlQuery( - value = - "SELECT extension, json FROM entity_extension WHERE id = :id " - + "AND extension LIKE CONCAT(:extensionPrefix, '.%') " - + "AND changeDescriptionDoc LIKE CONCAT('%', :fieldName, '%') ESCAPE '\\' " - + "ORDER BY CAST(regexp_replace(extension, '^.*version\\.', '') AS DECIMAL(10,1)) DESC " - + "LIMIT :limit OFFSET :offset", - connectionType = POSTGRES) + @SqlQuery( + "SELECT extension, json FROM entity_extension WHERE id = :id " + + "AND extension LIKE CONCAT(:extensionPrefix, '.%') " + + "AND changeDescriptionDoc LIKE CONCAT('%', :fieldName, '%') ESCAPE '!' " + + "ORDER BY extension DESC " + + "LIMIT :limit OFFSET :offset") List getExtensionsWithFieldChanged( @BindUUID("id") UUID id, @Bind("extensionPrefix") String extensionPrefix, @@ -1462,7 +1443,7 @@ List getExtensionsWithFieldChanged( @SqlQuery( "SELECT COUNT(*) FROM entity_extension WHERE id = :id " + "AND extension LIKE CONCAT(:extensionPrefix, '.%') " - + "AND changeDescriptionDoc LIKE CONCAT('%', :fieldName, '%') ESCAPE '\\' ") + + "AND changeDescriptionDoc LIKE CONCAT('%', :fieldName, '%') ESCAPE '!' ") int getExtensionCountWithFieldChanged( @BindUUID("id") UUID id, @Bind("extensionPrefix") String extensionPrefix, 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 9ccb1fabe7b4..3f18f57401f1 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 @@ -1264,7 +1264,7 @@ public final EntityHistoryWithOffset listVersionsWithOffset( String extensionPrefix = EntityUtil.getVersionExtensionPrefix(entityType); if (fieldChanged != null && !fieldChanged.isEmpty()) { - String sanitized = fieldChanged.replace("\\", "\\\\").replace("%", "\\%").replace("_", "\\_"); + String sanitized = fieldChanged.replace("!", "!!").replace("%", "!%").replace("_", "!_"); return listVersionsWithFieldFilter(id, extensionPrefix, limit, offset, sanitized); } From 4de5e68b6c9495d40c7f701182e028a03cd8de60 Mon Sep 17 00:00:00 2001 From: Sriharsha Chintalapani Date: Sat, 7 Mar 2026 20:55:49 -0800 Subject: [PATCH 09/20] Fix tests --- .../native/1.13.0/mysql/schemaChanges.sql | 2 +- .../openmetadata/it/tests/BaseEntityIT.java | 3 - .../service/jdbi3/EntityRepository.java | 67 ++++++++++++++++--- 3 files changed, 59 insertions(+), 13 deletions(-) diff --git a/bootstrap/sql/migrations/native/1.13.0/mysql/schemaChanges.sql b/bootstrap/sql/migrations/native/1.13.0/mysql/schemaChanges.sql index 5c96062ddbd2..ec42887eb217 100644 --- a/bootstrap/sql/migrations/native/1.13.0/mysql/schemaChanges.sql +++ b/bootstrap/sql/migrations/native/1.13.0/mysql/schemaChanges.sql @@ -27,7 +27,7 @@ WHERE JSON_CONTAINS_PATH(json, 'one', '$.preview'); SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'entity_extension' AND COLUMN_NAME = 'changeDescriptionDoc'); SET @sql = IF(@col_exists = 0, - 'ALTER TABLE entity_extension ADD COLUMN changeDescriptionDoc TEXT GENERATED ALWAYS AS (JSON_UNQUOTE(JSON_EXTRACT(json, ''$.changeDescription''))) STORED', + 'ALTER TABLE entity_extension ADD COLUMN changeDescriptionDoc TEXT GENERATED ALWAYS AS (JSON_UNQUOTE(JSON_EXTRACT(json, ''$.changeDescription''))) VIRTUAL', 'SELECT 1'); PREPARE stmt FROM @sql; EXECUTE stmt; 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 2c384e896a87..cf484a08cdd5 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 @@ -1450,9 +1450,6 @@ void get_entityVersionHistory_fieldChanged_200(TestNamespace ns) { created.setDescription("Description change for fieldChanged test"); patchEntity(created.getId().toString(), created); - created.setDescription("Second description change for fieldChanged test"); - patchEntity(created.getId().toString(), created); - org.openmetadata.schema.type.EntityHistory filtered = getVersionHistoryWithFieldChanged(created.getId(), 100, 0, "description"); assertNotNull(filtered, "Filtered version history should not be null"); 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 3f18f57401f1..c605721df12a 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 @@ -1263,14 +1263,15 @@ public final EntityHistoryWithOffset listVersionsWithOffset( UUID id, int limit, int offset, String fieldChanged) { String extensionPrefix = EntityUtil.getVersionExtensionPrefix(entityType); + T latest = setFieldsInternal(find(id, ALL), putFields); + setInheritedFields(latest, putFields); + if (fieldChanged != null && !fieldChanged.isEmpty()) { String sanitized = fieldChanged.replace("!", "!!").replace("%", "!%").replace("_", "!_"); - return listVersionsWithFieldFilter(id, extensionPrefix, limit, offset, sanitized); + return listVersionsWithFieldFilter( + id, latest, extensionPrefix, limit, offset, sanitized, fieldChanged); } - T latest = setFieldsInternal(find(id, ALL), putFields); - setInheritedFields(latest, putFields); - final List versions = new ArrayList<>(); int dbLimit; int dbOffset; @@ -1305,21 +1306,46 @@ public final EntityHistoryWithOffset listVersionsWithOffset( } private EntityHistoryWithOffset listVersionsWithFieldFilter( - UUID id, String extensionPrefix, int limit, int offset, String fieldChanged) { + UUID id, + T latest, + String extensionPrefix, + int limit, + int offset, + String sanitizedFieldChanged, + String rawFieldChanged) { final List versions = new ArrayList<>(); + boolean latestMatches = latestVersionMatchesFieldChanged(latest, rawFieldChanged); - if (limit > 0) { + int dbLimit; + int dbOffset; + + if (latestMatches) { + if (offset == 0) { + versions.add(JsonUtils.pojoToJson(latest)); + dbLimit = limit - 1; + } else { + dbLimit = limit; + } + dbOffset = Math.max(0, offset - 1); + } else { + dbLimit = limit; + dbOffset = offset; + } + + if (dbLimit > 0) { List records = daoCollection .entityExtensionDAO() - .getExtensionsWithFieldChanged(id, extensionPrefix, fieldChanged, limit, offset); + .getExtensionsWithFieldChanged( + id, extensionPrefix, sanitizedFieldChanged, dbLimit, dbOffset); records.forEach(r -> versions.add(new EntityVersionPair(r).getEntityJson())); } - int total = + int extensionCount = daoCollection .entityExtensionDAO() - .getExtensionCountWithFieldChanged(id, extensionPrefix, fieldChanged); + .getExtensionCountWithFieldChanged(id, extensionPrefix, sanitizedFieldChanged); + int total = extensionCount + (latestMatches ? 1 : 0); Paging paging = new Paging(); paging.setOffset(offset); @@ -1331,6 +1357,29 @@ private EntityHistoryWithOffset listVersionsWithFieldFilter( return new EntityHistoryWithOffset(entityHistory, Math.min(offset + limit, total)); } + private boolean latestVersionMatchesFieldChanged(T latest, String fieldChanged) { + ChangeDescription cd = latest.getChangeDescription(); + if (cd == null) { + return false; + } + return fieldChangeListContains(cd.getFieldsAdded(), fieldChanged) + || fieldChangeListContains(cd.getFieldsUpdated(), fieldChanged) + || fieldChangeListContains(cd.getFieldsDeleted(), fieldChanged); + } + + private boolean fieldChangeListContains(List fieldChanges, String fieldName) { + if (fieldChanges == null) { + return false; + } + for (FieldChange fc : fieldChanges) { + if (fc.getName() != null + && (fc.getName().equals(fieldName) || fc.getName().endsWith("." + fieldName))) { + return true; + } + } + return false; + } + public final ResultList listWithOffset( ListWithOffsetFunction> callable, Function countCallable, From 6a12855bd289c87ab49b01c41e1ff38ec10084e8 Mon Sep 17 00:00:00 2001 From: Sriharsha Chintalapani Date: Sun, 8 Mar 2026 09:28:22 -0700 Subject: [PATCH 10/20] Fix tests --- .../openmetadata/it/tests/BaseEntityIT.java | 70 +++++++++++-------- .../sdk/services/EntityServiceBase.java | 42 ++++++++++- .../sdk/services/EntityServiceBaseTest.java | 35 ++++++++++ .../service/jdbi3/CollectionDAO.java | 46 +++++------- .../service/jdbi3/EntityRepository.java | 59 ++++++++-------- scripts/datamodel_generation.py | 24 +++++++ 6 files changed, 187 insertions(+), 89 deletions(-) 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 cf484a08cdd5..ee58a7f7c391 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 @@ -1366,39 +1366,28 @@ void get_entityVersionHistory_paginated_200(TestNamespace ns) { OpenMetadataClient admin = SdkClients.adminClient(); OpenMetadataClient user1 = SdkClients.user1Client(); - admin - .getHttpClient() - .executeForString( - HttpMethod.PATCH, - patchPath, - "[{\"op\":\"add\",\"path\":\"/description\",\"value\":\"Admin patch 1\"}]", - patchOptions); - - try { - user1 - .getHttpClient() - .executeForString( - HttpMethod.PATCH, - patchPath, - "[{\"op\":\"replace\",\"path\":\"/description\",\"value\":\"User1 patch 2\"}]", - patchOptions); - } catch (Exception e) { - Assumptions.assumeTrue( - false, "Skipping: user1 cannot patch this entity type - " + e.getMessage()); + for (int i = 1; i <= 10; i++) { + OpenMetadataClient patchClient = i % 2 == 0 ? user1 : admin; + String op = i == 1 ? "add" : "replace"; + String value = (i % 2 == 0 ? "User1" : "Admin") + " patch " + i; + try { + patchClient + .getHttpClient() + .executeForString( + HttpMethod.PATCH, + patchPath, + String.format( + "[{\"op\":\"%s\",\"path\":\"/description\",\"value\":\"%s\"}]", op, value), + patchOptions); + } catch (Exception e) { + Assumptions.assumeTrue( + false, "Skipping: user1 cannot patch this entity type - " + e.getMessage()); + } } - admin - .getHttpClient() - .executeForString( - HttpMethod.PATCH, - patchPath, - "[{\"op\":\"replace\",\"path\":\"/description\",\"value\":\"Admin patch 3\"}]", - patchOptions); - org.openmetadata.schema.type.EntityHistory allVersions = getVersionHistory(created.getId()); int totalVersions = allVersions.getVersions().size(); - assertTrue( - totalVersions >= 3, "Should have at least 3 versions after alternating-user patches"); + assertTrue(totalVersions >= 11, "Should have at least 11 versions after repeated patches"); org.openmetadata.schema.type.EntityHistory page1 = getVersionHistoryPaginated(created.getId(), 1, 0); @@ -1416,6 +1405,11 @@ void get_entityVersionHistory_paginated_200(TestNamespace ns) { assertEquals(totalVersions, (int) page2.getPaging().getTotal()); assertEquals(1, page2.getVersions().size()); + double page1Version = versionOfHistoryEntry(page1.getVersions().get(0)); + double page2Version = versionOfHistoryEntry(page2.getVersions().get(0)); + assertTrue(page1Version > 1.0, "First page should contain a version after the 1.0 boundary"); + assertTrue(page1Version > page2Version, "Paginated results should remain newest-first"); + org.openmetadata.schema.type.EntityHistory unpaginated = getVersionHistory(created.getId()); assertNotNull(unpaginated.getVersions()); assertEquals(totalVersions, unpaginated.getVersions().size()); @@ -1459,6 +1453,15 @@ void get_entityVersionHistory_fieldChanged_200(TestNamespace ns) { "Should have at least 1 version with description change"); assertTrue(filtered.getPaging().getTotal() >= 1, "Total should reflect filtered count"); + org.openmetadata.schema.type.EntityHistory substringNoMatch = + getVersionHistoryWithFieldChanged(created.getId(), 100, 0, "script"); + assertNotNull(substringNoMatch); + assertEquals( + 0, + substringNoMatch.getVersions().size(), + "Substring matches should not be treated as field-name matches"); + assertEquals(0, (int) substringNoMatch.getPaging().getTotal()); + org.openmetadata.schema.type.EntityHistory noMatch = getVersionHistoryWithFieldChanged(created.getId(), 100, 0, "nonExistentField_xyz_12345"); assertNotNull(noMatch); @@ -1466,6 +1469,15 @@ void get_entityVersionHistory_fieldChanged_200(TestNamespace ns) { assertEquals(0, (int) noMatch.getPaging().getTotal()); } + private double versionOfHistoryEntry(Object versionEntry) { + JsonNode node = + versionEntry instanceof String versionJson + ? JsonUtils.readValue(versionJson, JsonNode.class) + : JsonUtils.pojoToJsonNode(versionEntry); + assertTrue(node.hasNonNull("version"), "Version history entry should include version"); + return node.get("version").asDouble(); + } + protected org.openmetadata.schema.type.EntityHistory getVersionHistory(UUID id) { throw new UnsupportedOperationException( "Version history not implemented - override in subclass"); diff --git a/openmetadata-sdk/src/main/java/org/openmetadata/sdk/services/EntityServiceBase.java b/openmetadata-sdk/src/main/java/org/openmetadata/sdk/services/EntityServiceBase.java index ca995e86c187..6d40c27b2d59 100644 --- a/openmetadata-sdk/src/main/java/org/openmetadata/sdk/services/EntityServiceBase.java +++ b/openmetadata-sdk/src/main/java/org/openmetadata/sdk/services/EntityServiceBase.java @@ -14,7 +14,9 @@ import java.util.UUID; import java.util.concurrent.CompletableFuture; import okhttp3.HttpUrl; +import org.openmetadata.schema.system.EntityError; import org.openmetadata.schema.type.EntityHistory; +import org.openmetadata.schema.type.Paging; import org.openmetadata.schema.type.api.BulkOperationResult; import org.openmetadata.schema.utils.ResultList; import org.openmetadata.sdk.exceptions.OpenMetadataException; @@ -651,7 +653,8 @@ public ResultList getEntityHistory( queryParams.put("after", after); } RequestOptions options = RequestOptions.builder().queryParams(queryParams).build(); - return httpClient.execute(HttpMethod.GET, path, null, ResultList.class, options); + String responseStr = httpClient.executeForString(HttpMethod.GET, path, null, options); + return deserializeResultList(responseStr); } /** @@ -713,4 +716,41 @@ public T getVersion(String id, Double version, String fields) throws OpenMetadat protected Class> getListResponseClass() { return (Class>) (Class) ListResponse.class; } + + protected ResultList deserializeResultList(String json) throws OpenMetadataException { + try { + JsonNode rootNode = objectMapper.readTree(json); + + ResultList result = new ResultList<>(); + + if (rootNode.has("data") && rootNode.get("data").isArray()) { + List items = new ArrayList<>(); + for (JsonNode node : rootNode.get("data")) { + items.add(objectMapper.treeToValue(node, getEntityClass())); + } + result.setData(items); + } + + if (rootNode.has("paging") && !rootNode.get("paging").isNull()) { + result.setPaging(objectMapper.treeToValue(rootNode.get("paging"), Paging.class)); + } + + if (rootNode.has("errors") && rootNode.get("errors").isArray()) { + List errors = new ArrayList<>(); + for (JsonNode node : rootNode.get("errors")) { + errors.add(objectMapper.treeToValue(node, EntityError.class)); + } + result.setErrors(errors); + } + + if (rootNode.has("warningsCount") && !rootNode.get("warningsCount").isNull()) { + result.setWarningsCount(rootNode.get("warningsCount").asInt()); + } + + return result; + } catch (Exception e) { + throw new OpenMetadataException( + "Failed to deserialize result list response: " + e.getMessage(), e); + } + } } diff --git a/openmetadata-sdk/src/test/java/org/openmetadata/sdk/services/EntityServiceBaseTest.java b/openmetadata-sdk/src/test/java/org/openmetadata/sdk/services/EntityServiceBaseTest.java index fb05cdf1ead9..1a41d3af01fb 100644 --- a/openmetadata-sdk/src/test/java/org/openmetadata/sdk/services/EntityServiceBaseTest.java +++ b/openmetadata-sdk/src/test/java/org/openmetadata/sdk/services/EntityServiceBaseTest.java @@ -12,6 +12,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.openmetadata.schema.entity.data.Table; +import org.openmetadata.schema.utils.ResultList; import org.openmetadata.sdk.client.OpenMetadataClient; import org.openmetadata.sdk.models.ListResponse; import org.openmetadata.sdk.network.HttpMethod; @@ -196,6 +197,40 @@ void testList() { assertEquals(2, result.getData().size()); } + @Test + void testGetEntityHistory() { + String jsonResponse = + "{\"data\":[" + + "{\"id\":\"550e8400-e29b-41d4-a716-446655440001\",\"name\":\"table1\",\"fullyQualifiedName\":\"service.database.schema.table1\"}" + + "],\"paging\":{\"total\":1,\"offset\":0,\"limit\":5},\"warningsCount\":2}"; + + ArgumentCaptor paramsCaptor = ArgumentCaptor.forClass(RequestOptions.class); + + when(mockHttpClient.executeForString( + eq(HttpMethod.GET), eq("/v1/tables/history"), isNull(), paramsCaptor.capture())) + .thenReturn(jsonResponse); + + ResultList result = + tableService.getEntityHistory(100L, 200L, 5, "before-cursor", "after-cursor"); + + assertNotNull(result); + assertEquals(1, result.getData().size()); + assertInstanceOf(Table.class, result.getData().get(0)); + assertEquals("table1", result.getData().get(0).getName()); + assertNotNull(result.getPaging()); + assertEquals(1, result.getPaging().getTotal()); + assertEquals(0, result.getPaging().getOffset()); + assertEquals(5, result.getPaging().getLimit()); + assertEquals(2, result.getWarningsCount()); + + RequestOptions capturedOptions = paramsCaptor.getValue(); + assertEquals("100", capturedOptions.getQueryParams().get("startTs")); + assertEquals("200", capturedOptions.getQueryParams().get("endTs")); + assertEquals("5", capturedOptions.getQueryParams().get("limit")); + assertEquals("before-cursor", capturedOptions.getQueryParams().get("before")); + assertEquals("after-cursor", capturedOptions.getQueryParams().get("after")); + } + @Test void testDelete() { String tableId = "table-123"; diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/CollectionDAO.java b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/CollectionDAO.java index 9e6c4b7141d2..62e9de6a1bf9 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/CollectionDAO.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/CollectionDAO.java @@ -1409,12 +1409,25 @@ int getEntityHistoryByTimestampRangeCount( @Bind("endTs") long endTs, @Bind("entityType") String entityType); + @ConnectionAwareSqlQuery( + value = + "SELECT extension, json FROM entity_extension WHERE id = :id AND extension " + + "LIKE CONCAT(:extensionPrefix, '.%') " + + "ORDER BY " + + "CAST(SUBSTRING_INDEX(SUBSTRING_INDEX(extension, '.', 3), '.', -1) AS UNSIGNED) DESC, " + + "CAST(SUBSTRING_INDEX(extension, '.', -1) AS UNSIGNED) DESC " + + "LIMIT :limit OFFSET :offset", + connectionType = MYSQL) + @ConnectionAwareSqlQuery( + value = + "SELECT extension, json FROM entity_extension WHERE id = :id AND extension " + + "LIKE CONCAT(:extensionPrefix, '.%') " + + "ORDER BY " + + "CAST(split_part(extension, '.', 3) AS INTEGER) DESC, " + + "CAST(split_part(extension, '.', 4) AS INTEGER) DESC " + + "LIMIT :limit OFFSET :offset", + connectionType = POSTGRES) @RegisterRowMapper(ExtensionMapper.class) - @SqlQuery( - "SELECT extension, json FROM entity_extension WHERE id = :id AND extension " - + "LIKE CONCAT(:extensionPrefix, '.%') " - + "ORDER BY extension DESC " - + "LIMIT :limit OFFSET :offset") List getExtensionsWithOffset( @BindUUID("id") UUID id, @Bind("extensionPrefix") String extensionPrefix, @@ -1426,29 +1439,6 @@ List getExtensionsWithOffset( + "LIKE CONCAT(:extensionPrefix, '.%')") int getExtensionCount(@BindUUID("id") UUID id, @Bind("extensionPrefix") String extensionPrefix); - @RegisterRowMapper(ExtensionMapper.class) - @SqlQuery( - "SELECT extension, json FROM entity_extension WHERE id = :id " - + "AND extension LIKE CONCAT(:extensionPrefix, '.%') " - + "AND changeDescriptionDoc LIKE CONCAT('%', :fieldName, '%') ESCAPE '!' " - + "ORDER BY extension DESC " - + "LIMIT :limit OFFSET :offset") - List getExtensionsWithFieldChanged( - @BindUUID("id") UUID id, - @Bind("extensionPrefix") String extensionPrefix, - @Bind("fieldName") String fieldName, - @Bind("limit") int limit, - @Bind("offset") int offset); - - @SqlQuery( - "SELECT COUNT(*) FROM entity_extension WHERE id = :id " - + "AND extension LIKE CONCAT(:extensionPrefix, '.%') " - + "AND changeDescriptionDoc LIKE CONCAT('%', :fieldName, '%') ESCAPE '!' ") - int getExtensionCountWithFieldChanged( - @BindUUID("id") UUID id, - @Bind("extensionPrefix") String extensionPrefix, - @Bind("fieldName") String fieldName); - @SqlUpdate("DELETE FROM entity_extension WHERE id = :id AND extension = :extension") void delete(@BindUUID("id") UUID id, @Bind("extension") String extension); 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 c605721df12a..a013d34aa3d7 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 @@ -1267,9 +1267,7 @@ public final EntityHistoryWithOffset listVersionsWithOffset( setInheritedFields(latest, putFields); if (fieldChanged != null && !fieldChanged.isEmpty()) { - String sanitized = fieldChanged.replace("!", "!!").replace("%", "!%").replace("_", "!_"); - return listVersionsWithFieldFilter( - id, latest, extensionPrefix, limit, offset, sanitized, fieldChanged); + return listVersionsWithFieldFilter(id, latest, extensionPrefix, limit, offset, fieldChanged); } final List versions = new ArrayList<>(); @@ -1306,46 +1304,39 @@ public final EntityHistoryWithOffset listVersionsWithOffset( } private EntityHistoryWithOffset listVersionsWithFieldFilter( - UUID id, - T latest, - String extensionPrefix, - int limit, - int offset, - String sanitizedFieldChanged, - String rawFieldChanged) { + UUID id, T latest, String extensionPrefix, int limit, int offset, String rawFieldChanged) { final List versions = new ArrayList<>(); boolean latestMatches = latestVersionMatchesFieldChanged(latest, rawFieldChanged); - int dbLimit; - int dbOffset; + List matchingHistoricalVersions = new ArrayList<>(); + List records = + daoCollection.entityExtensionDAO().getExtensions(id, extensionPrefix); + records.forEach( + record -> { + if (historicalVersionMatchesFieldChanged(record, rawFieldChanged)) { + matchingHistoricalVersions.add(new EntityVersionPair(record)); + } + }); + matchingHistoricalVersions.sort(EntityUtil.compareVersion.reversed()); + int historicalOffset = offset; + int historicalLimit = limit; if (latestMatches) { if (offset == 0) { versions.add(JsonUtils.pojoToJson(latest)); - dbLimit = limit - 1; - } else { - dbLimit = limit; + historicalLimit = limit - 1; } - dbOffset = Math.max(0, offset - 1); - } else { - dbLimit = limit; - dbOffset = offset; + historicalOffset = Math.max(0, offset - 1); } - if (dbLimit > 0) { - List records = - daoCollection - .entityExtensionDAO() - .getExtensionsWithFieldChanged( - id, extensionPrefix, sanitizedFieldChanged, dbLimit, dbOffset); - records.forEach(r -> versions.add(new EntityVersionPair(r).getEntityJson())); + if (historicalLimit > 0 && historicalOffset < matchingHistoricalVersions.size()) { + int toIndex = Math.min(historicalOffset + historicalLimit, matchingHistoricalVersions.size()); + matchingHistoricalVersions + .subList(historicalOffset, toIndex) + .forEach(version -> versions.add(version.getEntityJson())); } - int extensionCount = - daoCollection - .entityExtensionDAO() - .getExtensionCountWithFieldChanged(id, extensionPrefix, sanitizedFieldChanged); - int total = extensionCount + (latestMatches ? 1 : 0); + int total = matchingHistoricalVersions.size() + (latestMatches ? 1 : 0); Paging paging = new Paging(); paging.setOffset(offset); @@ -1380,6 +1371,12 @@ private boolean fieldChangeListContains(List fieldChanges, String f return false; } + private boolean historicalVersionMatchesFieldChanged( + ExtensionRecord extensionRecord, String fieldChanged) { + T historicalVersion = JsonUtils.readValue(extensionRecord.extensionJson(), entityClass); + return latestVersionMatchesFieldChanged(historicalVersion, fieldChanged); + } + public final ResultList listWithOffset( ListWithOffsetFunction> callable, Function countCallable, diff --git a/scripts/datamodel_generation.py b/scripts/datamodel_generation.py index ab1a847002e5..b90c66493368 100644 --- a/scripts/datamodel_generation.py +++ b/scripts/datamodel_generation.py @@ -65,6 +65,30 @@ file_.write("from typing import Union # custom generate import\n\n") +# datamodel-code-generator emits a module alias for paging.json that pydantic +# later fails to resolve while importing the generated model during pytest +# plugin bootstrap. Import the concrete type directly instead. +DIRECT_IMPORT_FIXES = { + f"{ingestion_path}src/metadata/generated/schema/type/entityHistory.py": [ + ( + "from . import changeSummaryMap, paging", + "from . import changeSummaryMap\nfrom .paging import Paging", + ), + ("from . import paging as paging_module", "from .paging import Paging"), + ("Optional[paging.Paging]", "Optional[Paging]"), + ("Optional[paging_module.Paging]", "Optional[Paging]"), + ], +} + +for file_path, replacements in DIRECT_IMPORT_FIXES.items(): + with open(file_path, "r", encoding=UTF_8) as file_: + content = file_.read() + for old_value, new_value in replacements: + content = content.replace(old_value, new_value) + with open(file_path, "w", encoding=UTF_8) as file_: + file_.write(content) + + # unsupported rust regex pattern for pydantic v2 # https://docs.pydantic.dev/2.7/api/config/#pydantic.config.ConfigDict.regex_engine # We'll remove validation from the client and let it fail on the server, rather than on the model generation From ee726a4a7fa28db816f9618272cb2e33dfcf1dc6 Mon Sep 17 00:00:00 2001 From: Sriharsha Chintalapani Date: Sun, 8 Mar 2026 11:59:11 -0700 Subject: [PATCH 11/20] Better way to lookup versions --- .../native/1.13.0/mysql/schemaChanges.sql | 37 ++++- .../native/1.13.0/postgres/schemaChanges.sql | 15 +- .../openmetadata/it/tests/BaseEntityIT.java | 13 ++ .../service/jdbi3/CollectionDAO.java | 91 ++++++++++- .../service/jdbi3/EntityRepository.java | 107 ++++++------ .../migration/mysql/v1130/Migration.java | 20 +++ .../migration/postgres/v1130/Migration.java | 20 +++ .../migration/utils/v1130/MigrationUtil.java | 95 +++++++++++ .../service/resources/EntityResource.java | 13 +- .../service/util/VersionFieldChangeUtil.java | 154 ++++++++++++++++++ .../util/VersionFieldChangeUtilTest.java | 49 ++++++ 11 files changed, 536 insertions(+), 78 deletions(-) create mode 100644 openmetadata-service/src/main/java/org/openmetadata/service/migration/mysql/v1130/Migration.java create mode 100644 openmetadata-service/src/main/java/org/openmetadata/service/migration/postgres/v1130/Migration.java create mode 100644 openmetadata-service/src/main/java/org/openmetadata/service/migration/utils/v1130/MigrationUtil.java create mode 100644 openmetadata-service/src/main/java/org/openmetadata/service/util/VersionFieldChangeUtil.java create mode 100644 openmetadata-service/src/test/java/org/openmetadata/service/util/VersionFieldChangeUtilTest.java diff --git a/bootstrap/sql/migrations/native/1.13.0/mysql/schemaChanges.sql b/bootstrap/sql/migrations/native/1.13.0/mysql/schemaChanges.sql index ec42887eb217..d94096c4fdba 100644 --- a/bootstrap/sql/migrations/native/1.13.0/mysql/schemaChanges.sql +++ b/bootstrap/sql/migrations/native/1.13.0/mysql/schemaChanges.sql @@ -22,12 +22,37 @@ SET json = JSON_SET( ) WHERE JSON_CONTAINS_PATH(json, 'one', '$.preview'); --- Add changeDescriptionDoc generated column to entity_extension for efficient field-change filtering --- Supports filtering entity versions by specific metadata changes (e.g., tags, schema, description) -SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'entity_extension' AND COLUMN_NAME = 'changeDescriptionDoc'); -SET @sql = IF(@col_exists = 0, - 'ALTER TABLE entity_extension ADD COLUMN changeDescriptionDoc TEXT GENERATED ALWAYS AS (JSON_UNQUOTE(JSON_EXTRACT(json, ''$.changeDescription''))) VIRTUAL', +SET @version_col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS + WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'entity_extension' AND COLUMN_NAME = 'versionNum'); +SET @sql = IF(@version_col_exists = 0, + 'ALTER TABLE entity_extension ADD COLUMN versionNum DOUBLE NULL', + 'SELECT 1'); +PREPARE stmt FROM @sql; +EXECUTE stmt; +DEALLOCATE PREPARE stmt; + +SET @changed_fields_col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS + WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'entity_extension' AND COLUMN_NAME = 'changedFieldKeys'); +SET @sql = IF(@changed_fields_col_exists = 0, + 'ALTER TABLE entity_extension ADD COLUMN changedFieldKeys JSON NULL', + 'SELECT 1'); +PREPARE stmt FROM @sql; +EXECUTE stmt; +DEALLOCATE PREPARE stmt; + +SET @version_index_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS + WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'entity_extension' AND INDEX_NAME = 'idx_entity_extension_version_order'); +SET @sql = IF(@version_index_exists = 0, + 'CREATE INDEX idx_entity_extension_version_order ON entity_extension (id, versionNum)', + 'SELECT 1'); +PREPARE stmt FROM @sql; +EXECUTE stmt; +DEALLOCATE PREPARE stmt; + +SET @changed_fields_index_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS + WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'entity_extension' AND INDEX_NAME = 'idx_entity_extension_changed_field_keys'); +SET @sql = IF(@changed_fields_index_exists = 0, + 'CREATE INDEX idx_entity_extension_changed_field_keys ON entity_extension ((CAST(changedFieldKeys->''$'' AS CHAR(512) ARRAY)))', 'SELECT 1'); PREPARE stmt FROM @sql; EXECUTE stmt; diff --git a/bootstrap/sql/migrations/native/1.13.0/postgres/schemaChanges.sql b/bootstrap/sql/migrations/native/1.13.0/postgres/schemaChanges.sql index a224826a1713..2c43c2f5b6e4 100644 --- a/bootstrap/sql/migrations/native/1.13.0/postgres/schemaChanges.sql +++ b/bootstrap/sql/migrations/native/1.13.0/postgres/schemaChanges.sql @@ -22,9 +22,14 @@ SET json = (json - 'preview') || jsonb_build_object( ) WHERE jsonb_exists(json, 'preview'); --- Add changeDescriptionDoc generated column to entity_extension for efficient field-change filtering --- Supports filtering entity versions by specific metadata changes (e.g., tags, schema, description) ALTER TABLE entity_extension - ADD COLUMN IF NOT EXISTS changeDescriptionDoc TEXT - GENERATED ALWAYS AS (json ->> 'changeDescription') - STORED; + ADD COLUMN IF NOT EXISTS versionNum DOUBLE PRECISION, + ADD COLUMN IF NOT EXISTS changedFieldKeys JSONB; + +CREATE INDEX IF NOT EXISTS idx_entity_extension_version_order + ON entity_extension (id, versionNum DESC) + WHERE versionNum IS NOT NULL; + +CREATE INDEX IF NOT EXISTS idx_entity_extension_changed_field_keys + ON entity_extension USING GIN (changedFieldKeys) + WHERE changedFieldKeys IS NOT NULL; 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 ee58a7f7c391..cc1078465b03 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 @@ -1453,6 +1453,19 @@ void get_entityVersionHistory_fieldChanged_200(TestNamespace ns) { "Should have at least 1 version with description change"); assertTrue(filtered.getPaging().getTotal() >= 1, "Total should reflect filtered count"); + org.openmetadata.schema.type.EntityHistory filteredWithDefaultLimit = + getVersionHistoryWithFieldChanged(created.getId(), 0, 0, "description"); + assertNotNull(filteredWithDefaultLimit); + assertNotNull(filteredWithDefaultLimit.getPaging()); + assertEquals( + 100, + (int) filteredWithDefaultLimit.getPaging().getLimit(), + "Filtered history should use a bounded default page size when limit is omitted"); + assertEquals( + filtered.getPaging().getTotal(), + filteredWithDefaultLimit.getPaging().getTotal(), + "Default pagination should preserve the same filtered total"); + org.openmetadata.schema.type.EntityHistory substringNoMatch = getVersionHistoryWithFieldChanged(created.getId(), 100, 0, "script"); assertNotNull(substringNoMatch); diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/CollectionDAO.java b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/CollectionDAO.java index 62e9de6a1bf9..23efdb4aeb20 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/CollectionDAO.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/CollectionDAO.java @@ -176,6 +176,8 @@ import org.openmetadata.service.resources.tags.TagLabelUtil; import org.openmetadata.service.util.EntityUtil; import org.openmetadata.service.util.FullyQualifiedName; +import org.openmetadata.service.util.VersionFieldChangeUtil.VersionExtensionMetadata; +import org.openmetadata.service.util.VersionFieldChangeUtil.VersionExtensionRecord; import org.openmetadata.service.util.jdbi.BindConcat; import org.openmetadata.service.util.jdbi.BindFQN; import org.openmetadata.service.util.jdbi.BindJsonContains; @@ -1280,6 +1282,35 @@ void insertMany( @Bind("jsonSchema") String jsonSchema, @Bind("json") List json); + @ConnectionAwareSqlUpdate( + value = + "REPLACE INTO entity_extension(id, extension, jsonSchema, json, versionNum, changedFieldKeys) " + + "VALUES (:id, :extension, :jsonSchema, :json, :versionNum, :changedFieldKeys)", + connectionType = MYSQL) + @ConnectionAwareSqlUpdate( + value = + "INSERT INTO entity_extension(id, extension, jsonSchema, json, versionNum, changedFieldKeys) " + + "VALUES (:id, :extension, :jsonSchema, (:json :: jsonb), :versionNum, (:changedFieldKeys :: jsonb)) " + + "ON CONFLICT (id, extension) DO UPDATE SET jsonSchema = EXCLUDED.jsonSchema, " + + "json = EXCLUDED.json, versionNum = EXCLUDED.versionNum, changedFieldKeys = EXCLUDED.changedFieldKeys", + connectionType = POSTGRES) + void insertVersionExtension(@BindBean VersionExtensionRecord versionExtensionRecord); + + @Transaction + @ConnectionAwareSqlBatch( + value = + "REPLACE INTO entity_extension(id, extension, jsonSchema, json, versionNum, changedFieldKeys) " + + "VALUES (:id, :extension, :jsonSchema, :json, :versionNum, :changedFieldKeys)", + connectionType = MYSQL) + @ConnectionAwareSqlBatch( + value = + "INSERT INTO entity_extension(id, extension, jsonSchema, json, versionNum, changedFieldKeys) " + + "VALUES (:id, :extension, :jsonSchema, (:json :: jsonb), :versionNum, (:changedFieldKeys :: jsonb)) " + + "ON CONFLICT (id, extension) DO UPDATE SET jsonSchema = EXCLUDED.jsonSchema, " + + "json = EXCLUDED.json, versionNum = EXCLUDED.versionNum, changedFieldKeys = EXCLUDED.changedFieldKeys", + connectionType = POSTGRES) + void insertVersionExtensions(@BindBean List versionExtensionRecords); + @ConnectionAwareSqlUpdate( value = "UPDATE entity_extension SET json = :json where (json -> '$.id') = :id", connectionType = MYSQL) @@ -1340,6 +1371,22 @@ List getExtensionsByPrefixBatch( void bulkUpsertExtensions( @BindBean List extensionWithIdObjects); + @Transaction + @ConnectionAwareSqlBatch( + value = + "UPDATE entity_extension " + + "SET versionNum = :versionNum, changedFieldKeys = :changedFieldKeys " + + "WHERE id = :id AND extension = :extension", + connectionType = MYSQL) + @ConnectionAwareSqlBatch( + value = + "UPDATE entity_extension " + + "SET versionNum = :versionNum, changedFieldKeys = (:changedFieldKeys :: jsonb) " + + "WHERE id = :id AND extension = :extension", + connectionType = POSTGRES) + void updateVersionExtensionMetadata( + @BindBean List versionExtensionMetadata); + @RegisterRowMapper(ExtensionMapper.class) @SqlQuery( "SELECT extension, json FROM entity_extension WHERE id = :id AND extension " @@ -1413,18 +1460,14 @@ int getEntityHistoryByTimestampRangeCount( value = "SELECT extension, json FROM entity_extension WHERE id = :id AND extension " + "LIKE CONCAT(:extensionPrefix, '.%') " - + "ORDER BY " - + "CAST(SUBSTRING_INDEX(SUBSTRING_INDEX(extension, '.', 3), '.', -1) AS UNSIGNED) DESC, " - + "CAST(SUBSTRING_INDEX(extension, '.', -1) AS UNSIGNED) DESC " + + "ORDER BY versionNum DESC " + "LIMIT :limit OFFSET :offset", connectionType = MYSQL) @ConnectionAwareSqlQuery( value = "SELECT extension, json FROM entity_extension WHERE id = :id AND extension " + "LIKE CONCAT(:extensionPrefix, '.%') " - + "ORDER BY " - + "CAST(split_part(extension, '.', 3) AS INTEGER) DESC, " - + "CAST(split_part(extension, '.', 4) AS INTEGER) DESC " + + "ORDER BY versionNum DESC " + "LIMIT :limit OFFSET :offset", connectionType = POSTGRES) @RegisterRowMapper(ExtensionMapper.class) @@ -1434,6 +1477,42 @@ List getExtensionsWithOffset( @Bind("limit") int limit, @Bind("offset") int offset); + @ConnectionAwareSqlQuery( + value = + "SELECT extension, json FROM entity_extension " + + "WHERE id = :id " + + "AND JSON_CONTAINS(changedFieldKeys, JSON_ARRAY(:fieldPath)) " + + "ORDER BY versionNum DESC " + + "LIMIT :limit OFFSET :offset", + connectionType = MYSQL) + @ConnectionAwareSqlQuery( + value = + "SELECT extension, json FROM entity_extension " + + "WHERE id = :id " + + "AND jsonb_exists(changedFieldKeys, :fieldPath) " + + "ORDER BY versionNum DESC " + + "LIMIT :limit OFFSET :offset", + connectionType = POSTGRES) + @RegisterRowMapper(ExtensionMapper.class) + List getExtensionsWithFieldChanged( + @BindUUID("id") UUID id, + @Bind("fieldPath") String fieldPath, + @Bind("limit") int limit, + @Bind("offset") int offset); + + @ConnectionAwareSqlQuery( + value = + "SELECT COUNT(*) FROM entity_extension WHERE id = :id " + + "AND JSON_CONTAINS(changedFieldKeys, JSON_ARRAY(:fieldPath))", + connectionType = MYSQL) + @ConnectionAwareSqlQuery( + value = + "SELECT COUNT(*) FROM entity_extension WHERE id = :id " + + "AND jsonb_exists(changedFieldKeys, :fieldPath)", + connectionType = POSTGRES) + int getExtensionCountByFieldChanged( + @BindUUID("id") UUID id, @Bind("fieldPath") String fieldPath); + @SqlQuery( "SELECT COUNT(*) FROM entity_extension WHERE id = :id AND extension " + "LIKE CONCAT(:extensionPrefix, '.%')") 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 a013d34aa3d7..4f2ea6508196 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 @@ -247,6 +247,7 @@ import org.openmetadata.service.util.RestUtil.DeleteResponse; import org.openmetadata.service.util.RestUtil.PatchResponse; import org.openmetadata.service.util.RestUtil.PutResponse; +import org.openmetadata.service.util.VersionFieldChangeUtil; import software.amazon.awssdk.utils.Either; /** @@ -1267,7 +1268,7 @@ public final EntityHistoryWithOffset listVersionsWithOffset( setInheritedFields(latest, putFields); if (fieldChanged != null && !fieldChanged.isEmpty()) { - return listVersionsWithFieldFilter(id, latest, extensionPrefix, limit, offset, fieldChanged); + return listVersionsWithFieldFilter(id, latest, limit, offset, fieldChanged); } final List versions = new ArrayList<>(); @@ -1304,20 +1305,11 @@ public final EntityHistoryWithOffset listVersionsWithOffset( } private EntityHistoryWithOffset listVersionsWithFieldFilter( - UUID id, T latest, String extensionPrefix, int limit, int offset, String rawFieldChanged) { + UUID id, T latest, int limit, int offset, String rawFieldChanged) { final List versions = new ArrayList<>(); boolean latestMatches = latestVersionMatchesFieldChanged(latest, rawFieldChanged); - - List matchingHistoricalVersions = new ArrayList<>(); - List records = - daoCollection.entityExtensionDAO().getExtensions(id, extensionPrefix); - records.forEach( - record -> { - if (historicalVersionMatchesFieldChanged(record, rawFieldChanged)) { - matchingHistoricalVersions.add(new EntityVersionPair(record)); - } - }); - matchingHistoricalVersions.sort(EntityUtil.compareVersion.reversed()); + int matchingHistoricalVersionCount = + daoCollection.entityExtensionDAO().getExtensionCountByFieldChanged(id, rawFieldChanged); int historicalOffset = offset; int historicalLimit = limit; @@ -1329,14 +1321,16 @@ record -> { historicalOffset = Math.max(0, offset - 1); } - if (historicalLimit > 0 && historicalOffset < matchingHistoricalVersions.size()) { - int toIndex = Math.min(historicalOffset + historicalLimit, matchingHistoricalVersions.size()); - matchingHistoricalVersions - .subList(historicalOffset, toIndex) - .forEach(version -> versions.add(version.getEntityJson())); + if (historicalLimit > 0 && historicalOffset < matchingHistoricalVersionCount) { + List records = + daoCollection + .entityExtensionDAO() + .getExtensionsWithFieldChanged( + id, rawFieldChanged, historicalLimit, historicalOffset); + records.forEach(record -> versions.add(new EntityVersionPair(record).getEntityJson())); } - int total = matchingHistoricalVersions.size() + (latestMatches ? 1 : 0); + int total = matchingHistoricalVersionCount + (latestMatches ? 1 : 0); Paging paging = new Paging(); paging.setOffset(offset); @@ -1349,32 +1343,22 @@ record -> { } private boolean latestVersionMatchesFieldChanged(T latest, String fieldChanged) { - ChangeDescription cd = latest.getChangeDescription(); - if (cd == null) { - return false; - } - return fieldChangeListContains(cd.getFieldsAdded(), fieldChanged) - || fieldChangeListContains(cd.getFieldsUpdated(), fieldChanged) - || fieldChangeListContains(cd.getFieldsDeleted(), fieldChanged); + return VersionFieldChangeUtil.matchesFieldChanged(latest.getChangeDescription(), fieldChanged); } - private boolean fieldChangeListContains(List fieldChanges, String fieldName) { - if (fieldChanges == null) { - return false; - } - for (FieldChange fc : fieldChanges) { - if (fc.getName() != null - && (fc.getName().equals(fieldName) || fc.getName().endsWith("." + fieldName))) { - return true; - } - } - return false; - } - - private boolean historicalVersionMatchesFieldChanged( - ExtensionRecord extensionRecord, String fieldChanged) { - T historicalVersion = JsonUtils.readValue(extensionRecord.extensionJson(), entityClass); - return latestVersionMatchesFieldChanged(historicalVersion, fieldChanged); + private VersionFieldChangeUtil.VersionExtensionRecord buildVersionExtensionRecord( + UUID id, + String extensionName, + String entityJson, + Double version, + ChangeDescription changeDescription) { + return new VersionFieldChangeUtil.VersionExtensionRecord( + id, + extensionName, + entityType, + entityJson, + version, + VersionFieldChangeUtil.getChangedFieldKeysJson(changeDescription)); } public final ResultList listWithOffset( @@ -6342,7 +6326,13 @@ private void storeEntityHistory() { String extensionName = EntityUtil.getVersionExtension(entityType, original.getVersion()); daoCollection .entityExtensionDAO() - .insert(original.getId(), extensionName, entityType, JsonUtils.pojoToJson(original)); + .insertVersionExtension( + buildVersionExtensionRecord( + original.getId(), + extensionName, + JsonUtils.pojoToJson(original), + original.getVersion(), + original.getChangeDescription())); } private void removeEntityHistory(Double version) { @@ -7907,7 +7897,13 @@ public void createChangeEventForBulkOperation( String extensionName = EntityUtil.getVersionExtension(entityType, original.getVersion()); daoCollection .entityExtensionDAO() - .insert(original.getId(), extensionName, entityType, JsonUtils.pojoToJson(original)); + .insertVersionExtension( + buildVersionExtensionRecord( + original.getId(), + extensionName, + JsonUtils.pojoToJson(original), + original.getVersion(), + original.getChangeDescription())); // Directly update the entity in the database without calling other versioning methods dao.update(updated.getId(), updated.getFullyQualifiedName(), JsonUtils.pojoToJson(updated)); @@ -8104,21 +8100,20 @@ private void bulkUpdateEntities( // Batch DB writes try { // Batch version history inserts - List historyIds = new ArrayList<>(); - List historyExtensions = new ArrayList<>(); - List historyJsons = new ArrayList<>(); + List historyRecords = new ArrayList<>(); for (EntityUpdater updater : updaters) { if (updater.isVersionChanged()) { - historyIds.add(updater.getOriginal().getId()); - historyExtensions.add( - EntityUtil.getVersionExtension(entityType, updater.getOriginal().getVersion())); - historyJsons.add(JsonUtils.pojoToJson(updater.getOriginal())); + historyRecords.add( + buildVersionExtensionRecord( + updater.getOriginal().getId(), + EntityUtil.getVersionExtension(entityType, updater.getOriginal().getVersion()), + JsonUtils.pojoToJson(updater.getOriginal()), + updater.getOriginal().getVersion(), + updater.getOriginal().getChangeDescription())); } } - if (!historyIds.isEmpty()) { - daoCollection - .entityExtensionDAO() - .insertMany(historyIds, historyExtensions, entityType, historyJsons); + if (!historyRecords.isEmpty()) { + daoCollection.entityExtensionDAO().insertVersionExtensions(historyRecords); } // Batch entity row updates diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/migration/mysql/v1130/Migration.java b/openmetadata-service/src/main/java/org/openmetadata/service/migration/mysql/v1130/Migration.java new file mode 100644 index 000000000000..f1b7b5050725 --- /dev/null +++ b/openmetadata-service/src/main/java/org/openmetadata/service/migration/mysql/v1130/Migration.java @@ -0,0 +1,20 @@ +package org.openmetadata.service.migration.mysql.v1130; + +import lombok.SneakyThrows; +import org.openmetadata.service.jdbi3.locator.ConnectionType; +import org.openmetadata.service.migration.api.MigrationProcessImpl; +import org.openmetadata.service.migration.utils.MigrationFile; +import org.openmetadata.service.migration.utils.v1130.MigrationUtil; + +public class Migration extends MigrationProcessImpl { + + public Migration(MigrationFile migrationFile) { + super(migrationFile); + } + + @Override + @SneakyThrows + public void runDataMigration() { + new MigrationUtil(handle, ConnectionType.MYSQL).backfillVersionFieldChanges(); + } +} diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/migration/postgres/v1130/Migration.java b/openmetadata-service/src/main/java/org/openmetadata/service/migration/postgres/v1130/Migration.java new file mode 100644 index 000000000000..5dce47ecaf7e --- /dev/null +++ b/openmetadata-service/src/main/java/org/openmetadata/service/migration/postgres/v1130/Migration.java @@ -0,0 +1,20 @@ +package org.openmetadata.service.migration.postgres.v1130; + +import lombok.SneakyThrows; +import org.openmetadata.service.jdbi3.locator.ConnectionType; +import org.openmetadata.service.migration.api.MigrationProcessImpl; +import org.openmetadata.service.migration.utils.MigrationFile; +import org.openmetadata.service.migration.utils.v1130.MigrationUtil; + +public class Migration extends MigrationProcessImpl { + + public Migration(MigrationFile migrationFile) { + super(migrationFile); + } + + @Override + @SneakyThrows + public void runDataMigration() { + new MigrationUtil(handle, ConnectionType.POSTGRES).backfillVersionFieldChanges(); + } +} diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/migration/utils/v1130/MigrationUtil.java b/openmetadata-service/src/main/java/org/openmetadata/service/migration/utils/v1130/MigrationUtil.java new file mode 100644 index 000000000000..4c3c95d5eab4 --- /dev/null +++ b/openmetadata-service/src/main/java/org/openmetadata/service/migration/utils/v1130/MigrationUtil.java @@ -0,0 +1,95 @@ +package org.openmetadata.service.migration.utils.v1130; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import lombok.extern.slf4j.Slf4j; +import org.jdbi.v3.core.Handle; +import org.openmetadata.service.jdbi3.CollectionDAO; +import org.openmetadata.service.jdbi3.locator.ConnectionType; +import org.openmetadata.service.util.EntityUtil; +import org.openmetadata.service.util.VersionFieldChangeUtil; + +@Slf4j +public class MigrationUtil { + private static final int BATCH_SIZE = 500; + private static final String MYSQL_SELECT_VERSION_HISTORY = + "SELECT id, extension, CAST(json AS CHAR) AS json " + + "FROM entity_extension " + + "WHERE extension LIKE :extensionPattern " + + "AND (:lastId = '' OR id > :lastId OR (id = :lastId AND extension > :lastExtension)) " + + "ORDER BY id ASC, extension ASC " + + "LIMIT :limit"; + private static final String POSTGRES_SELECT_VERSION_HISTORY = + "SELECT id, extension, CAST(json AS TEXT) AS json " + + "FROM entity_extension " + + "WHERE extension LIKE :extensionPattern " + + "AND (:lastId = '' OR id > :lastId OR (id = :lastId AND extension > :lastExtension)) " + + "ORDER BY id ASC, extension ASC " + + "LIMIT :limit"; + + private final Handle handle; + private final ConnectionType connectionType; + private final CollectionDAO.EntityExtensionDAO entityExtensionDAO; + + public MigrationUtil(Handle handle, ConnectionType connectionType) { + this.handle = handle; + this.connectionType = connectionType; + this.entityExtensionDAO = handle.attach(CollectionDAO.class).entityExtensionDAO(); + } + + public void backfillVersionFieldChanges() { + String lastId = ""; + String lastExtension = ""; + int processedVersions = 0; + int updatedRows = 0; + + while (true) { + List> rows = + handle + .createQuery( + connectionType == ConnectionType.MYSQL + ? MYSQL_SELECT_VERSION_HISTORY + : POSTGRES_SELECT_VERSION_HISTORY) + .bind("extensionPattern", "%.version.%") + .bind("lastId", lastId) + .bind("lastExtension", lastExtension) + .bind("limit", BATCH_SIZE) + .mapToMap() + .list(); + + if (rows.isEmpty()) { + break; + } + + List versionExtensionMetadata = + new ArrayList<>(); + for (Map row : rows) { + String id = row.get("id").toString(); + String extension = row.get("extension").toString(); + String json = row.get("json").toString(); + versionExtensionMetadata.add( + new VersionFieldChangeUtil.VersionExtensionMetadata( + java.util.UUID.fromString(id), + extension, + EntityUtil.getVersion(extension), + VersionFieldChangeUtil.getChangedFieldKeysJson(json))); + } + + if (!versionExtensionMetadata.isEmpty()) { + entityExtensionDAO.updateVersionExtensionMetadata(versionExtensionMetadata); + updatedRows += versionExtensionMetadata.size(); + } + + Map lastRow = rows.get(rows.size() - 1); + lastId = lastRow.get("id").toString(); + lastExtension = lastRow.get("extension").toString(); + processedVersions += rows.size(); + } + + LOG.info( + "Backfilled {} version history rows and updated {} searchable metadata rows on entity_extension", + processedVersions, + updatedRows); + } +} diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/EntityResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/EntityResource.java index 7fa04127d8b8..1fe561b4aee9 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/EntityResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/EntityResource.java @@ -103,6 +103,8 @@ @Slf4j public abstract class EntityResource> { + private static final int DEFAULT_FIELD_CHANGED_VERSION_LIMIT = 100; + protected final Class entityClass; protected final String entityType; protected final Set allowedFields; @@ -373,14 +375,15 @@ protected EntityHistory listVersionsInternal( OperationContext operationContext, ResourceContextInterface resourceContext) { authorizer.authorize(securityContext, operationContext, resourceContext); - if (limit > 0) { - return repository.listVersionsWithOffset(id, limit, offset, fieldChanged).entityHistory(); - } - if (fieldChanged != null && !fieldChanged.isEmpty()) { + if (!nullOrEmpty(fieldChanged)) { + int effectiveLimit = limit > 0 ? limit : DEFAULT_FIELD_CHANGED_VERSION_LIMIT; return repository - .listVersionsWithOffset(id, Integer.MAX_VALUE, offset, fieldChanged) + .listVersionsWithOffset(id, effectiveLimit, offset, fieldChanged) .entityHistory(); } + if (limit > 0) { + return repository.listVersionsWithOffset(id, limit, offset, null).entityHistory(); + } return repository.listVersions(id); } diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/util/VersionFieldChangeUtil.java b/openmetadata-service/src/main/java/org/openmetadata/service/util/VersionFieldChangeUtil.java new file mode 100644 index 000000000000..3de9b6128d9c --- /dev/null +++ b/openmetadata-service/src/main/java/org/openmetadata/service/util/VersionFieldChangeUtil.java @@ -0,0 +1,154 @@ +/* + * 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.util; + +import static org.openmetadata.common.utils.CommonUtil.listOrEmpty; +import static org.openmetadata.common.utils.CommonUtil.nullOrEmpty; + +import com.fasterxml.jackson.databind.JsonNode; +import java.util.ArrayList; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; +import java.util.UUID; +import lombok.Getter; +import org.openmetadata.schema.type.ChangeDescription; +import org.openmetadata.schema.type.FieldChange; +import org.openmetadata.schema.utils.JsonUtils; + +public final class VersionFieldChangeUtil { + private static final String CHANGE_DESCRIPTION = "changeDescription"; + private static final String FIELD_NAME = "name"; + private static final String FIELDS_ADDED = "fieldsAdded"; + private static final String FIELDS_UPDATED = "fieldsUpdated"; + private static final String FIELDS_DELETED = "fieldsDeleted"; + + private VersionFieldChangeUtil() {} + + public static boolean matchesFieldChanged( + ChangeDescription changeDescription, String fieldChanged) { + if (nullOrEmpty(fieldChanged)) { + return false; + } + return extractFieldChangeKeys(changeDescription).contains(fieldChanged); + } + + public static Set extractFieldChangeKeys(ChangeDescription changeDescription) { + Set fieldChangeKeys = new LinkedHashSet<>(); + addFieldChangeKeys( + fieldChangeKeys, + listOrEmpty(changeDescription == null ? null : changeDescription.getFieldsAdded())); + addFieldChangeKeys( + fieldChangeKeys, + listOrEmpty(changeDescription == null ? null : changeDescription.getFieldsUpdated())); + addFieldChangeKeys( + fieldChangeKeys, + listOrEmpty(changeDescription == null ? null : changeDescription.getFieldsDeleted())); + return fieldChangeKeys; + } + + public static Set extractFieldChangeKeys(String entityJson) { + Set fieldChangeKeys = new LinkedHashSet<>(); + JsonNode changeDescriptionNode = JsonUtils.readTree(entityJson).get(CHANGE_DESCRIPTION); + if (changeDescriptionNode == null || changeDescriptionNode.isNull()) { + return fieldChangeKeys; + } + + addFieldChangeKeys(fieldChangeKeys, changeDescriptionNode.get(FIELDS_ADDED)); + addFieldChangeKeys(fieldChangeKeys, changeDescriptionNode.get(FIELDS_UPDATED)); + addFieldChangeKeys(fieldChangeKeys, changeDescriptionNode.get(FIELDS_DELETED)); + return fieldChangeKeys; + } + + public static String getChangedFieldKeysJson(ChangeDescription changeDescription) { + return JsonUtils.pojoToJson(new ArrayList<>(extractFieldChangeKeys(changeDescription))); + } + + public static String getChangedFieldKeysJson(String entityJson) { + return JsonUtils.pojoToJson(new ArrayList<>(extractFieldChangeKeys(entityJson))); + } + + private static void addFieldChangeKeys( + Set fieldChangeKeys, List fieldChanges) { + for (FieldChange fieldChange : fieldChanges) { + addFieldNameSuffixes(fieldChangeKeys, fieldChange.getName()); + } + } + + private static void addFieldChangeKeys(Set fieldChangeKeys, JsonNode fieldChanges) { + if (fieldChanges == null || !fieldChanges.isArray()) { + return; + } + + for (JsonNode fieldChange : fieldChanges) { + JsonNode fieldName = fieldChange.get(FIELD_NAME); + if (fieldName != null && !fieldName.isNull()) { + addFieldNameSuffixes(fieldChangeKeys, fieldName.asText()); + } + } + } + + private static void addFieldNameSuffixes(Set fieldChangeKeys, String fieldName) { + if (nullOrEmpty(fieldName)) { + return; + } + + String[] pathParts = fieldName.split("\\."); + for (int index = 0; index < pathParts.length; index++) { + fieldChangeKeys.add( + String.join(".", java.util.Arrays.copyOfRange(pathParts, index, pathParts.length))); + } + } + + @Getter + public static class VersionExtensionRecord { + private final UUID id; + private final String extension; + private final String jsonSchema; + private final String json; + private final Double versionNum; + private final String changedFieldKeys; + + public VersionExtensionRecord( + UUID id, + String extension, + String jsonSchema, + String json, + Double versionNum, + String changedFieldKeys) { + this.id = id; + this.extension = extension; + this.jsonSchema = jsonSchema; + this.json = json; + this.versionNum = versionNum; + this.changedFieldKeys = changedFieldKeys; + } + } + + @Getter + public static class VersionExtensionMetadata { + private final UUID id; + private final String extension; + private final Double versionNum; + private final String changedFieldKeys; + + public VersionExtensionMetadata( + UUID id, String extension, Double versionNum, String changedFieldKeys) { + this.id = id; + this.extension = extension; + this.versionNum = versionNum; + this.changedFieldKeys = changedFieldKeys; + } + } +} diff --git a/openmetadata-service/src/test/java/org/openmetadata/service/util/VersionFieldChangeUtilTest.java b/openmetadata-service/src/test/java/org/openmetadata/service/util/VersionFieldChangeUtilTest.java new file mode 100644 index 000000000000..74c0c9fa66f3 --- /dev/null +++ b/openmetadata-service/src/test/java/org/openmetadata/service/util/VersionFieldChangeUtilTest.java @@ -0,0 +1,49 @@ +package org.openmetadata.service.util; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Set; +import org.junit.jupiter.api.Test; +import org.openmetadata.schema.type.ChangeDescription; +import org.openmetadata.schema.type.FieldChange; +import org.openmetadata.schema.utils.JsonUtils; + +class VersionFieldChangeUtilTest { + + @Test + void extractsSuffixKeysFromChangeDescription() { + ChangeDescription changeDescription = + new ChangeDescription() + .withFieldsUpdated( + java.util.List.of( + new FieldChange().withName("columns.tags.tagFQN"), + new FieldChange().withName("description"))); + + Set fieldChangeKeys = VersionFieldChangeUtil.extractFieldChangeKeys(changeDescription); + + assertTrue(fieldChangeKeys.contains("columns.tags.tagFQN")); + assertTrue(fieldChangeKeys.contains("tags.tagFQN")); + assertTrue(fieldChangeKeys.contains("tagFQN")); + assertTrue(fieldChangeKeys.contains("description")); + } + + @Test + void serializesUniqueFieldChangeKeysPerSuffix() { + ChangeDescription changeDescription = + new ChangeDescription() + .withFieldsAdded( + java.util.List.of(new FieldChange().withName("schema.fields.description"))) + .withFieldsUpdated( + java.util.List.of(new FieldChange().withName("schema.fields.description"))); + + var keys = + JsonUtils.readObjects( + VersionFieldChangeUtil.getChangedFieldKeysJson(changeDescription), String.class); + + assertEquals(3, keys.size()); + assertTrue(keys.contains("schema.fields.description")); + assertTrue(keys.contains("fields.description")); + assertTrue(keys.contains("description")); + } +} From 9782d3368652fff8d2ece36f51cc799899f2e9d5 Mon Sep 17 00:00:00 2001 From: Sriharsha Chintalapani Date: Sun, 8 Mar 2026 13:22:36 -0700 Subject: [PATCH 12/20] Fix pytests --- .../mysql/postDataMigrationSQLScript.sql | 66 +++++++ .../postgres/postDataMigrationSQLScript.sql | 55 ++++++ .../openmetadata/it/tests/BaseEntityIT.java | 48 ++++++ .../service/jdbi3/CollectionDAO.java | 9 +- .../service/jdbi3/EntityRepository.java | 163 +++++++++++++++--- .../migration/mysql/v1130/Migration.java | 20 --- .../migration/postgres/v1130/Migration.java | 20 --- .../migration/utils/v1130/MigrationUtil.java | 95 ---------- .../service/util/VersionFieldChangeUtil.java | 7 + .../util/VersionFieldChangeUtilTest.java | 30 ++++ 10 files changed, 356 insertions(+), 157 deletions(-) delete mode 100644 openmetadata-service/src/main/java/org/openmetadata/service/migration/mysql/v1130/Migration.java delete mode 100644 openmetadata-service/src/main/java/org/openmetadata/service/migration/postgres/v1130/Migration.java delete mode 100644 openmetadata-service/src/main/java/org/openmetadata/service/migration/utils/v1130/MigrationUtil.java diff --git a/bootstrap/sql/migrations/native/1.13.0/mysql/postDataMigrationSQLScript.sql b/bootstrap/sql/migrations/native/1.13.0/mysql/postDataMigrationSQLScript.sql index ef09c917b4eb..57d31b8e87b2 100644 --- a/bootstrap/sql/migrations/native/1.13.0/mysql/postDataMigrationSQLScript.sql +++ b/bootstrap/sql/migrations/native/1.13.0/mysql/postDataMigrationSQLScript.sql @@ -3,3 +3,69 @@ SET json = JSON_REMOVE(json, '$.sourceConfig.config.computeMetrics') WHERE JSON_EXTRACT(json, '$.sourceConfig.config.computeMetrics') IS NOT NULL AND pipelineType = 'profiler'; +WITH RECURSIVE +field_changes AS ( + SELECT e.id, e.extension, jt.field_name + FROM entity_extension AS e + JOIN JSON_TABLE( + COALESCE(JSON_EXTRACT(e.json, '$.changeDescription.fieldsAdded'), JSON_ARRAY()), + '$[*]' COLUMNS (field_name VARCHAR(1024) PATH '$.name') + ) AS jt ON TRUE + WHERE e.extension LIKE '%.version.%' + + UNION ALL + + SELECT e.id, e.extension, jt.field_name + FROM entity_extension AS e + JOIN JSON_TABLE( + COALESCE(JSON_EXTRACT(e.json, '$.changeDescription.fieldsUpdated'), JSON_ARRAY()), + '$[*]' COLUMNS (field_name VARCHAR(1024) PATH '$.name') + ) AS jt ON TRUE + WHERE e.extension LIKE '%.version.%' + + UNION ALL + + SELECT e.id, e.extension, jt.field_name + FROM entity_extension AS e + JOIN JSON_TABLE( + COALESCE(JSON_EXTRACT(e.json, '$.changeDescription.fieldsDeleted'), JSON_ARRAY()), + '$[*]' COLUMNS (field_name VARCHAR(1024) PATH '$.name') + ) AS jt ON TRUE + WHERE e.extension LIKE '%.version.%' +), +suffixes AS ( + SELECT id, extension, field_name AS suffix + FROM field_changes + WHERE field_name IS NOT NULL + AND field_name <> '' + + UNION ALL + + SELECT id, extension, SUBSTRING(suffix, LOCATE('.', suffix) + 1) + FROM suffixes + WHERE LOCATE('.', suffix) > 0 +), +distinct_suffixes AS ( + SELECT DISTINCT id, extension, suffix + FROM suffixes +), +version_metadata AS ( + SELECT + e.id, + e.extension, + CAST(SUBSTRING_INDEX(e.extension, '.version.', -1) AS DOUBLE) AS version_num, + CASE + WHEN COUNT(ds.suffix) = 0 THEN JSON_ARRAY() + ELSE JSON_ARRAYAGG(ds.suffix) + END AS changed_field_keys + FROM entity_extension AS e + LEFT JOIN distinct_suffixes AS ds + ON ds.id = e.id AND ds.extension = e.extension + WHERE e.extension LIKE '%.version.%' + GROUP BY e.id, e.extension +) +UPDATE entity_extension AS e +JOIN version_metadata AS vm + ON vm.id = e.id AND vm.extension = e.extension +SET e.versionNum = vm.version_num, + e.changedFieldKeys = vm.changed_field_keys; diff --git a/bootstrap/sql/migrations/native/1.13.0/postgres/postDataMigrationSQLScript.sql b/bootstrap/sql/migrations/native/1.13.0/postgres/postDataMigrationSQLScript.sql index 9b5f3b1876a5..1c489405d560 100644 --- a/bootstrap/sql/migrations/native/1.13.0/postgres/postDataMigrationSQLScript.sql +++ b/bootstrap/sql/migrations/native/1.13.0/postgres/postDataMigrationSQLScript.sql @@ -2,3 +2,58 @@ UPDATE ingestion_pipeline_entity SET json = (json::jsonb #- '{sourceConfig,config,computeMetrics}')::json WHERE json::jsonb -> 'sourceConfig' -> 'config' -> 'computeMetrics' IS NOT NULL AND pipelineType = 'profiler'; + +WITH version_metadata AS ( + SELECT + e.id, + e.extension, + split_part(e.extension, '.version.', 2)::DOUBLE PRECISION AS version_num, + COALESCE( + ( + SELECT jsonb_agg(suffix ORDER BY suffix) + FROM ( + SELECT DISTINCT + array_to_string( + ARRAY( + SELECT part + FROM unnest(field_paths.path_parts) WITH ORDINALITY AS parts(part, ord) + WHERE ord >= indexes.path_index + ORDER BY ord + ), + '.' + ) AS suffix + FROM ( + SELECT string_to_array(field_name, '.') AS path_parts + FROM ( + SELECT field_change ->> 'name' AS field_name + FROM jsonb_array_elements( + COALESCE(e.json -> 'changeDescription' -> 'fieldsAdded', '[]'::jsonb) + ) AS field_change + UNION ALL + SELECT field_change ->> 'name' AS field_name + FROM jsonb_array_elements( + COALESCE(e.json -> 'changeDescription' -> 'fieldsUpdated', '[]'::jsonb) + ) AS field_change + UNION ALL + SELECT field_change ->> 'name' AS field_name + FROM jsonb_array_elements( + COALESCE(e.json -> 'changeDescription' -> 'fieldsDeleted', '[]'::jsonb) + ) AS field_change + ) AS field_changes + WHERE field_name IS NOT NULL + AND field_name <> '' + ) AS field_paths + CROSS JOIN LATERAL generate_subscripts(field_paths.path_parts, 1) AS indexes(path_index) + ) AS suffixes + ), + '[]'::jsonb + ) AS changed_field_keys + FROM entity_extension AS e + WHERE e.extension LIKE '%.version.%' +) +UPDATE entity_extension AS e +SET versionNum = version_metadata.version_num, + changedFieldKeys = version_metadata.changed_field_keys +FROM version_metadata +WHERE e.id = version_metadata.id + AND e.extension = version_metadata.extension; 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 cc1078465b03..ac6a41b0cffd 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 @@ -43,7 +43,9 @@ import org.openmetadata.sdk.fluent.Users; import org.openmetadata.sdk.network.HttpMethod; import org.openmetadata.sdk.network.RequestOptions; +import org.openmetadata.service.Entity; import org.openmetadata.service.util.TestUtils; +import org.openmetadata.service.util.VersionFieldChangeUtil; /** * Base class for all entity integration tests. @@ -1482,6 +1484,52 @@ void get_entityVersionHistory_fieldChanged_200(TestNamespace ns) { assertEquals(0, (int) noMatch.getPaging().getTotal()); } + @Test + void get_entityVersionHistory_fieldChanged_ignoresWrongExtensionPrefix(TestNamespace ns) { + if (!supportsPatch) return; + + K createRequest = createMinimalRequest(ns); + T created = createEntity(createRequest); + + created.setDescription("Description change for extension prefix test"); + patchEntity(created.getId().toString(), created); + + org.openmetadata.schema.type.EntityHistory baseline = + getVersionHistoryWithFieldChanged(created.getId(), 100, 0, "description"); + assertNotNull(baseline); + assertNotNull(baseline.getPaging()); + + String rogueExtension = "rogue.version.999.0"; + try { + Entity.getCollectionDAO() + .entityExtensionDAO() + .insertVersionExtension( + new VersionFieldChangeUtil.VersionExtensionRecord( + created.getId(), + rogueExtension, + getEntityType(), + JsonUtils.pojoToJson(created), + 999.0, + JsonUtils.pojoToJson(List.of("description")))); + + org.openmetadata.schema.type.EntityHistory filtered = + getVersionHistoryWithFieldChanged(created.getId(), 100, 0, "description"); + + assertNotNull(filtered); + assertNotNull(filtered.getPaging()); + assertEquals( + baseline.getPaging().getTotal(), + filtered.getPaging().getTotal(), + "Filtered history should ignore extension rows outside the entity version prefix"); + assertEquals( + baseline.getVersions().size(), + filtered.getVersions().size(), + "Filtered history should not include rows from a rogue extension prefix"); + } finally { + Entity.getCollectionDAO().entityExtensionDAO().delete(created.getId(), rogueExtension); + } + } + private double versionOfHistoryEntry(Object versionEntry) { JsonNode node = versionEntry instanceof String versionJson diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/CollectionDAO.java b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/CollectionDAO.java index 23efdb4aeb20..df2e1fa3d489 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/CollectionDAO.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/CollectionDAO.java @@ -1481,6 +1481,7 @@ List getExtensionsWithOffset( value = "SELECT extension, json FROM entity_extension " + "WHERE id = :id " + + "AND extension LIKE CONCAT(:extensionPrefix, '.%') " + "AND JSON_CONTAINS(changedFieldKeys, JSON_ARRAY(:fieldPath)) " + "ORDER BY versionNum DESC " + "LIMIT :limit OFFSET :offset", @@ -1489,6 +1490,7 @@ List getExtensionsWithOffset( value = "SELECT extension, json FROM entity_extension " + "WHERE id = :id " + + "AND extension LIKE CONCAT(:extensionPrefix, '.%') " + "AND jsonb_exists(changedFieldKeys, :fieldPath) " + "ORDER BY versionNum DESC " + "LIMIT :limit OFFSET :offset", @@ -1496,6 +1498,7 @@ List getExtensionsWithOffset( @RegisterRowMapper(ExtensionMapper.class) List getExtensionsWithFieldChanged( @BindUUID("id") UUID id, + @Bind("extensionPrefix") String extensionPrefix, @Bind("fieldPath") String fieldPath, @Bind("limit") int limit, @Bind("offset") int offset); @@ -1503,15 +1506,19 @@ List getExtensionsWithFieldChanged( @ConnectionAwareSqlQuery( value = "SELECT COUNT(*) FROM entity_extension WHERE id = :id " + + "AND extension LIKE CONCAT(:extensionPrefix, '.%') " + "AND JSON_CONTAINS(changedFieldKeys, JSON_ARRAY(:fieldPath))", connectionType = MYSQL) @ConnectionAwareSqlQuery( value = "SELECT COUNT(*) FROM entity_extension WHERE id = :id " + + "AND extension LIKE CONCAT(:extensionPrefix, '.%') " + "AND jsonb_exists(changedFieldKeys, :fieldPath)", connectionType = POSTGRES) int getExtensionCountByFieldChanged( - @BindUUID("id") UUID id, @Bind("fieldPath") String fieldPath); + @BindUUID("id") UUID id, + @Bind("extensionPrefix") String extensionPrefix, + @Bind("fieldPath") String fieldPath); @SqlQuery( "SELECT COUNT(*) FROM entity_extension WHERE id = :id AND extension " 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 4f2ea6508196..16b1813096cf 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 @@ -1306,10 +1306,29 @@ public final EntityHistoryWithOffset listVersionsWithOffset( private EntityHistoryWithOffset listVersionsWithFieldFilter( UUID id, T latest, int limit, int offset, String rawFieldChanged) { + try { + return listVersionsWithFieldFilterIndexed(id, latest, limit, offset, rawFieldChanged); + } catch (RuntimeException e) { + if (!isVersionFieldMetadataSchemaMissing(e)) { + throw e; + } + LOG.debug( + "Version history metadata columns are unavailable for entityType={}; using in-memory fieldChanged filtering", + entityType, + e); + return listVersionsWithFieldFilterFallback(id, latest, limit, offset, rawFieldChanged); + } + } + + private EntityHistoryWithOffset listVersionsWithFieldFilterIndexed( + UUID id, T latest, int limit, int offset, String rawFieldChanged) { final List versions = new ArrayList<>(); boolean latestMatches = latestVersionMatchesFieldChanged(latest, rawFieldChanged); + String extensionPrefix = EntityUtil.getVersionExtensionPrefix(entityType); int matchingHistoricalVersionCount = - daoCollection.entityExtensionDAO().getExtensionCountByFieldChanged(id, rawFieldChanged); + daoCollection + .entityExtensionDAO() + .getExtensionCountByFieldChanged(id, extensionPrefix, rawFieldChanged); int historicalOffset = offset; int historicalLimit = limit; @@ -1326,7 +1345,7 @@ private EntityHistoryWithOffset listVersionsWithFieldFilter( daoCollection .entityExtensionDAO() .getExtensionsWithFieldChanged( - id, rawFieldChanged, historicalLimit, historicalOffset); + id, extensionPrefix, rawFieldChanged, historicalLimit, historicalOffset); records.forEach(record -> versions.add(new EntityVersionPair(record).getEntityJson())); } @@ -1342,10 +1361,74 @@ private EntityHistoryWithOffset listVersionsWithFieldFilter( return new EntityHistoryWithOffset(entityHistory, Math.min(offset + limit, total)); } + private EntityHistoryWithOffset listVersionsWithFieldFilterFallback( + UUID id, T latest, int limit, int offset, String rawFieldChanged) { + final List versions = new ArrayList<>(); + boolean latestMatches = latestVersionMatchesFieldChanged(latest, rawFieldChanged); + String extensionPrefix = EntityUtil.getVersionExtensionPrefix(entityType); + List matchingHistoricalVersions = new ArrayList<>(); + + daoCollection + .entityExtensionDAO() + .getExtensions(id, extensionPrefix) + .forEach( + record -> { + if (VersionFieldChangeUtil.matchesFieldChanged( + record.extensionJson(), rawFieldChanged)) { + matchingHistoricalVersions.add(new EntityVersionPair(record)); + } + }); + matchingHistoricalVersions.sort(EntityUtil.compareVersion.reversed()); + + int historicalOffset = offset; + int historicalLimit = limit; + if (latestMatches) { + if (offset == 0) { + versions.add(JsonUtils.pojoToJson(latest)); + historicalLimit = limit - 1; + } + historicalOffset = Math.max(0, offset - 1); + } + + if (historicalLimit > 0 && historicalOffset < matchingHistoricalVersions.size()) { + matchingHistoricalVersions.stream() + .skip(historicalOffset) + .limit(historicalLimit) + .forEach(version -> versions.add(version.getEntityJson())); + } + + int total = matchingHistoricalVersions.size() + (latestMatches ? 1 : 0); + + Paging paging = new Paging(); + paging.setOffset(offset); + paging.setLimit(limit); + paging.setTotal(total); + + EntityHistory entityHistory = + new EntityHistory().withEntityType(entityType).withVersions(versions).withPaging(paging); + return new EntityHistoryWithOffset(entityHistory, Math.min(offset + limit, total)); + } + private boolean latestVersionMatchesFieldChanged(T latest, String fieldChanged) { return VersionFieldChangeUtil.matchesFieldChanged(latest.getChangeDescription(), fieldChanged); } + private boolean isVersionFieldMetadataSchemaMissing(Throwable throwable) { + Throwable current = throwable; + while (current != null) { + String message = current.getMessage(); + if (message != null) { + String normalizedMessage = message.toLowerCase(Locale.ROOT); + if (normalizedMessage.contains("changedfieldkeys") + || normalizedMessage.contains("versionnum")) { + return true; + } + } + current = current.getCause(); + } + return false; + } + private VersionFieldChangeUtil.VersionExtensionRecord buildVersionExtensionRecord( UUID id, String extensionName, @@ -1361,6 +1444,48 @@ private VersionFieldChangeUtil.VersionExtensionRecord buildVersionExtensionRecor VersionFieldChangeUtil.getChangedFieldKeysJson(changeDescription)); } + private void insertVersionExtensionRecord( + VersionFieldChangeUtil.VersionExtensionRecord versionExtensionRecord) { + try { + daoCollection.entityExtensionDAO().insertVersionExtension(versionExtensionRecord); + } catch (RuntimeException e) { + if (!isVersionFieldMetadataSchemaMissing(e)) { + throw e; + } + daoCollection + .entityExtensionDAO() + .insert( + versionExtensionRecord.getId(), + versionExtensionRecord.getExtension(), + versionExtensionRecord.getJsonSchema(), + versionExtensionRecord.getJson()); + } + } + + private void insertVersionExtensionRecords( + List versionExtensionRecords) { + try { + daoCollection.entityExtensionDAO().insertVersionExtensions(versionExtensionRecords); + } catch (RuntimeException e) { + if (!isVersionFieldMetadataSchemaMissing(e)) { + throw e; + } + daoCollection + .entityExtensionDAO() + .insertMany( + versionExtensionRecords.stream() + .map(VersionFieldChangeUtil.VersionExtensionRecord::getId) + .toList(), + versionExtensionRecords.stream() + .map(VersionFieldChangeUtil.VersionExtensionRecord::getExtension) + .toList(), + entityType, + versionExtensionRecords.stream() + .map(VersionFieldChangeUtil.VersionExtensionRecord::getJson) + .toList()); + } + } + public final ResultList listWithOffset( ListWithOffsetFunction> callable, Function countCallable, @@ -6324,15 +6449,13 @@ public final void storeUpdateWithOptimisticLocking() { private void storeEntityHistory() { String extensionName = EntityUtil.getVersionExtension(entityType, original.getVersion()); - daoCollection - .entityExtensionDAO() - .insertVersionExtension( - buildVersionExtensionRecord( - original.getId(), - extensionName, - JsonUtils.pojoToJson(original), - original.getVersion(), - original.getChangeDescription())); + insertVersionExtensionRecord( + buildVersionExtensionRecord( + original.getId(), + extensionName, + JsonUtils.pojoToJson(original), + original.getVersion(), + original.getChangeDescription())); } private void removeEntityHistory(Double version) { @@ -7895,15 +8018,13 @@ public void createChangeEventForBulkOperation( // Store history of the previous version String extensionName = EntityUtil.getVersionExtension(entityType, original.getVersion()); - daoCollection - .entityExtensionDAO() - .insertVersionExtension( - buildVersionExtensionRecord( - original.getId(), - extensionName, - JsonUtils.pojoToJson(original), - original.getVersion(), - original.getChangeDescription())); + insertVersionExtensionRecord( + buildVersionExtensionRecord( + original.getId(), + extensionName, + JsonUtils.pojoToJson(original), + original.getVersion(), + original.getChangeDescription())); // Directly update the entity in the database without calling other versioning methods dao.update(updated.getId(), updated.getFullyQualifiedName(), JsonUtils.pojoToJson(updated)); @@ -8113,7 +8234,7 @@ private void bulkUpdateEntities( } } if (!historyRecords.isEmpty()) { - daoCollection.entityExtensionDAO().insertVersionExtensions(historyRecords); + insertVersionExtensionRecords(historyRecords); } // Batch entity row updates diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/migration/mysql/v1130/Migration.java b/openmetadata-service/src/main/java/org/openmetadata/service/migration/mysql/v1130/Migration.java deleted file mode 100644 index f1b7b5050725..000000000000 --- a/openmetadata-service/src/main/java/org/openmetadata/service/migration/mysql/v1130/Migration.java +++ /dev/null @@ -1,20 +0,0 @@ -package org.openmetadata.service.migration.mysql.v1130; - -import lombok.SneakyThrows; -import org.openmetadata.service.jdbi3.locator.ConnectionType; -import org.openmetadata.service.migration.api.MigrationProcessImpl; -import org.openmetadata.service.migration.utils.MigrationFile; -import org.openmetadata.service.migration.utils.v1130.MigrationUtil; - -public class Migration extends MigrationProcessImpl { - - public Migration(MigrationFile migrationFile) { - super(migrationFile); - } - - @Override - @SneakyThrows - public void runDataMigration() { - new MigrationUtil(handle, ConnectionType.MYSQL).backfillVersionFieldChanges(); - } -} diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/migration/postgres/v1130/Migration.java b/openmetadata-service/src/main/java/org/openmetadata/service/migration/postgres/v1130/Migration.java deleted file mode 100644 index 5dce47ecaf7e..000000000000 --- a/openmetadata-service/src/main/java/org/openmetadata/service/migration/postgres/v1130/Migration.java +++ /dev/null @@ -1,20 +0,0 @@ -package org.openmetadata.service.migration.postgres.v1130; - -import lombok.SneakyThrows; -import org.openmetadata.service.jdbi3.locator.ConnectionType; -import org.openmetadata.service.migration.api.MigrationProcessImpl; -import org.openmetadata.service.migration.utils.MigrationFile; -import org.openmetadata.service.migration.utils.v1130.MigrationUtil; - -public class Migration extends MigrationProcessImpl { - - public Migration(MigrationFile migrationFile) { - super(migrationFile); - } - - @Override - @SneakyThrows - public void runDataMigration() { - new MigrationUtil(handle, ConnectionType.POSTGRES).backfillVersionFieldChanges(); - } -} diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/migration/utils/v1130/MigrationUtil.java b/openmetadata-service/src/main/java/org/openmetadata/service/migration/utils/v1130/MigrationUtil.java deleted file mode 100644 index 4c3c95d5eab4..000000000000 --- a/openmetadata-service/src/main/java/org/openmetadata/service/migration/utils/v1130/MigrationUtil.java +++ /dev/null @@ -1,95 +0,0 @@ -package org.openmetadata.service.migration.utils.v1130; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import lombok.extern.slf4j.Slf4j; -import org.jdbi.v3.core.Handle; -import org.openmetadata.service.jdbi3.CollectionDAO; -import org.openmetadata.service.jdbi3.locator.ConnectionType; -import org.openmetadata.service.util.EntityUtil; -import org.openmetadata.service.util.VersionFieldChangeUtil; - -@Slf4j -public class MigrationUtil { - private static final int BATCH_SIZE = 500; - private static final String MYSQL_SELECT_VERSION_HISTORY = - "SELECT id, extension, CAST(json AS CHAR) AS json " - + "FROM entity_extension " - + "WHERE extension LIKE :extensionPattern " - + "AND (:lastId = '' OR id > :lastId OR (id = :lastId AND extension > :lastExtension)) " - + "ORDER BY id ASC, extension ASC " - + "LIMIT :limit"; - private static final String POSTGRES_SELECT_VERSION_HISTORY = - "SELECT id, extension, CAST(json AS TEXT) AS json " - + "FROM entity_extension " - + "WHERE extension LIKE :extensionPattern " - + "AND (:lastId = '' OR id > :lastId OR (id = :lastId AND extension > :lastExtension)) " - + "ORDER BY id ASC, extension ASC " - + "LIMIT :limit"; - - private final Handle handle; - private final ConnectionType connectionType; - private final CollectionDAO.EntityExtensionDAO entityExtensionDAO; - - public MigrationUtil(Handle handle, ConnectionType connectionType) { - this.handle = handle; - this.connectionType = connectionType; - this.entityExtensionDAO = handle.attach(CollectionDAO.class).entityExtensionDAO(); - } - - public void backfillVersionFieldChanges() { - String lastId = ""; - String lastExtension = ""; - int processedVersions = 0; - int updatedRows = 0; - - while (true) { - List> rows = - handle - .createQuery( - connectionType == ConnectionType.MYSQL - ? MYSQL_SELECT_VERSION_HISTORY - : POSTGRES_SELECT_VERSION_HISTORY) - .bind("extensionPattern", "%.version.%") - .bind("lastId", lastId) - .bind("lastExtension", lastExtension) - .bind("limit", BATCH_SIZE) - .mapToMap() - .list(); - - if (rows.isEmpty()) { - break; - } - - List versionExtensionMetadata = - new ArrayList<>(); - for (Map row : rows) { - String id = row.get("id").toString(); - String extension = row.get("extension").toString(); - String json = row.get("json").toString(); - versionExtensionMetadata.add( - new VersionFieldChangeUtil.VersionExtensionMetadata( - java.util.UUID.fromString(id), - extension, - EntityUtil.getVersion(extension), - VersionFieldChangeUtil.getChangedFieldKeysJson(json))); - } - - if (!versionExtensionMetadata.isEmpty()) { - entityExtensionDAO.updateVersionExtensionMetadata(versionExtensionMetadata); - updatedRows += versionExtensionMetadata.size(); - } - - Map lastRow = rows.get(rows.size() - 1); - lastId = lastRow.get("id").toString(); - lastExtension = lastRow.get("extension").toString(); - processedVersions += rows.size(); - } - - LOG.info( - "Backfilled {} version history rows and updated {} searchable metadata rows on entity_extension", - processedVersions, - updatedRows); - } -} diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/util/VersionFieldChangeUtil.java b/openmetadata-service/src/main/java/org/openmetadata/service/util/VersionFieldChangeUtil.java index 3de9b6128d9c..4bc8f5c92352 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/util/VersionFieldChangeUtil.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/util/VersionFieldChangeUtil.java @@ -44,6 +44,13 @@ public static boolean matchesFieldChanged( return extractFieldChangeKeys(changeDescription).contains(fieldChanged); } + public static boolean matchesFieldChanged(String entityJson, String fieldChanged) { + if (nullOrEmpty(fieldChanged)) { + return false; + } + return extractFieldChangeKeys(entityJson).contains(fieldChanged); + } + public static Set extractFieldChangeKeys(ChangeDescription changeDescription) { Set fieldChangeKeys = new LinkedHashSet<>(); addFieldChangeKeys( diff --git a/openmetadata-service/src/test/java/org/openmetadata/service/util/VersionFieldChangeUtilTest.java b/openmetadata-service/src/test/java/org/openmetadata/service/util/VersionFieldChangeUtilTest.java index 74c0c9fa66f3..ec5be8b94a80 100644 --- a/openmetadata-service/src/test/java/org/openmetadata/service/util/VersionFieldChangeUtilTest.java +++ b/openmetadata-service/src/test/java/org/openmetadata/service/util/VersionFieldChangeUtilTest.java @@ -1,6 +1,7 @@ package org.openmetadata.service.util; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.Set; @@ -46,4 +47,33 @@ void serializesUniqueFieldChangeKeysPerSuffix() { assertTrue(keys.contains("fields.description")); assertTrue(keys.contains("description")); } + + @Test + void matchesExactSuffixesButNotSubstrings() { + ChangeDescription changeDescription = + new ChangeDescription() + .withFieldsUpdated( + java.util.List.of(new FieldChange().withName("schema.fields.description"))); + + assertTrue(VersionFieldChangeUtil.matchesFieldChanged(changeDescription, "description")); + assertTrue(VersionFieldChangeUtil.matchesFieldChanged(changeDescription, "fields.description")); + assertFalse(VersionFieldChangeUtil.matchesFieldChanged(changeDescription, "script")); + assertFalse(VersionFieldChangeUtil.matchesFieldChanged(changeDescription, "fields")); + } + + @Test + void matchesFieldChangedFromSerializedEntityJson() { + String entityJson = + JsonUtils.pojoToJson( + java.util.Map.of( + "changeDescription", + new ChangeDescription() + .withFieldsUpdated( + java.util.List.of( + new FieldChange().withName("schema.fields.description"))))); + + assertTrue(VersionFieldChangeUtil.matchesFieldChanged(entityJson, "description")); + assertTrue(VersionFieldChangeUtil.matchesFieldChanged(entityJson, "fields.description")); + assertFalse(VersionFieldChangeUtil.matchesFieldChanged(entityJson, "owners")); + } } From 8fd7b079e33240129b3236d1eec9e7dee0f75249 Mon Sep 17 00:00:00 2001 From: Sriharsha Chintalapani Date: Sun, 8 Mar 2026 15:30:10 -0700 Subject: [PATCH 13/20] Fix tests --- .../mysql/postDataMigrationSQLScript.sql | 18 ++++ .../native/1.13.0/mysql/schemaChanges.sql | 32 +++--- .../openmetadata/it/tests/BaseEntityIT.java | 57 +++++++++-- .../service/jdbi3/CollectionDAO.java | 41 ++++++-- .../service/jdbi3/EntityRepository.java | 98 +++++++++++-------- .../utils/MigrationSqlStatementHashTest.java | 80 +++++++++++++++ .../EntityVersionPage.component.tsx | 22 ++--- 7 files changed, 263 insertions(+), 85 deletions(-) create mode 100644 openmetadata-service/src/test/java/org/openmetadata/service/migration/utils/MigrationSqlStatementHashTest.java diff --git a/bootstrap/sql/migrations/native/1.13.0/mysql/postDataMigrationSQLScript.sql b/bootstrap/sql/migrations/native/1.13.0/mysql/postDataMigrationSQLScript.sql index 57d31b8e87b2..540b1211890d 100644 --- a/bootstrap/sql/migrations/native/1.13.0/mysql/postDataMigrationSQLScript.sql +++ b/bootstrap/sql/migrations/native/1.13.0/mysql/postDataMigrationSQLScript.sql @@ -3,6 +3,24 @@ SET json = JSON_REMOVE(json, '$.sourceConfig.config.computeMetrics') WHERE JSON_EXTRACT(json, '$.sourceConfig.config.computeMetrics') IS NOT NULL AND pipelineType = 'profiler'; +SET @post_version_col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS + WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'entity_extension' AND COLUMN_NAME = 'versionNum'); +SET @post_add_version_num_sql = IF(@post_version_col_exists = 0, + 'ALTER TABLE entity_extension ADD COLUMN versionNum DOUBLE NULL', + 'SELECT 1'); +PREPARE post_add_version_num_stmt FROM @post_add_version_num_sql; +EXECUTE post_add_version_num_stmt; +DEALLOCATE PREPARE post_add_version_num_stmt; + +SET @post_changed_fields_col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS + WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'entity_extension' AND COLUMN_NAME = 'changedFieldKeys'); +SET @post_add_changed_field_keys_sql = IF(@post_changed_fields_col_exists = 0, + 'ALTER TABLE entity_extension ADD COLUMN changedFieldKeys JSON NULL', + 'SELECT 1'); +PREPARE post_add_changed_field_keys_stmt FROM @post_add_changed_field_keys_sql; +EXECUTE post_add_changed_field_keys_stmt; +DEALLOCATE PREPARE post_add_changed_field_keys_stmt; + WITH RECURSIVE field_changes AS ( SELECT e.id, e.extension, jt.field_name diff --git a/bootstrap/sql/migrations/native/1.13.0/mysql/schemaChanges.sql b/bootstrap/sql/migrations/native/1.13.0/mysql/schemaChanges.sql index d94096c4fdba..24fc561fbd96 100644 --- a/bootstrap/sql/migrations/native/1.13.0/mysql/schemaChanges.sql +++ b/bootstrap/sql/migrations/native/1.13.0/mysql/schemaChanges.sql @@ -24,36 +24,36 @@ WHERE JSON_CONTAINS_PATH(json, 'one', '$.preview'); SET @version_col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'entity_extension' AND COLUMN_NAME = 'versionNum'); -SET @sql = IF(@version_col_exists = 0, +SET @add_version_num_sql = IF(@version_col_exists = 0, 'ALTER TABLE entity_extension ADD COLUMN versionNum DOUBLE NULL', 'SELECT 1'); -PREPARE stmt FROM @sql; -EXECUTE stmt; -DEALLOCATE PREPARE stmt; +PREPARE add_version_num_stmt FROM @add_version_num_sql; +EXECUTE add_version_num_stmt; +DEALLOCATE PREPARE add_version_num_stmt; SET @changed_fields_col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'entity_extension' AND COLUMN_NAME = 'changedFieldKeys'); -SET @sql = IF(@changed_fields_col_exists = 0, +SET @add_changed_field_keys_sql = IF(@changed_fields_col_exists = 0, 'ALTER TABLE entity_extension ADD COLUMN changedFieldKeys JSON NULL', 'SELECT 1'); -PREPARE stmt FROM @sql; -EXECUTE stmt; -DEALLOCATE PREPARE stmt; +PREPARE add_changed_field_keys_stmt FROM @add_changed_field_keys_sql; +EXECUTE add_changed_field_keys_stmt; +DEALLOCATE PREPARE add_changed_field_keys_stmt; SET @version_index_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'entity_extension' AND INDEX_NAME = 'idx_entity_extension_version_order'); -SET @sql = IF(@version_index_exists = 0, +SET @add_version_index_sql = IF(@version_index_exists = 0, 'CREATE INDEX idx_entity_extension_version_order ON entity_extension (id, versionNum)', 'SELECT 1'); -PREPARE stmt FROM @sql; -EXECUTE stmt; -DEALLOCATE PREPARE stmt; +PREPARE add_version_index_stmt FROM @add_version_index_sql; +EXECUTE add_version_index_stmt; +DEALLOCATE PREPARE add_version_index_stmt; SET @changed_fields_index_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'entity_extension' AND INDEX_NAME = 'idx_entity_extension_changed_field_keys'); -SET @sql = IF(@changed_fields_index_exists = 0, +SET @add_changed_field_keys_index_sql = IF(@changed_fields_index_exists = 0, 'CREATE INDEX idx_entity_extension_changed_field_keys ON entity_extension ((CAST(changedFieldKeys->''$'' AS CHAR(512) ARRAY)))', 'SELECT 1'); -PREPARE stmt FROM @sql; -EXECUTE stmt; -DEALLOCATE PREPARE stmt; +PREPARE add_changed_field_keys_index_stmt FROM @add_changed_field_keys_index_sql; +EXECUTE add_changed_field_keys_index_stmt; +DEALLOCATE PREPARE add_changed_field_keys_index_stmt; 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 ac6a41b0cffd..1efa10c3a95d 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 @@ -10,6 +10,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; import java.net.http.HttpResponse; import java.time.Duration; import java.util.ArrayList; @@ -44,8 +45,8 @@ import org.openmetadata.sdk.network.HttpMethod; import org.openmetadata.sdk.network.RequestOptions; import org.openmetadata.service.Entity; +import org.openmetadata.service.util.EntityUtil; import org.openmetadata.service.util.TestUtils; -import org.openmetadata.service.util.VersionFieldChangeUtil; /** * Base class for all entity integration tests. @@ -1504,13 +1505,12 @@ void get_entityVersionHistory_fieldChanged_ignoresWrongExtensionPrefix(TestNames Entity.getCollectionDAO() .entityExtensionDAO() .insertVersionExtension( - new VersionFieldChangeUtil.VersionExtensionRecord( - created.getId(), - rogueExtension, - getEntityType(), - JsonUtils.pojoToJson(created), - 999.0, - JsonUtils.pojoToJson(List.of("description")))); + created.getId(), + rogueExtension, + getEntityType(), + JsonUtils.pojoToJson(created), + 999.0, + JsonUtils.pojoToJson(List.of("description"))); org.openmetadata.schema.type.EntityHistory filtered = getVersionHistoryWithFieldChanged(created.getId(), 100, 0, "description"); @@ -1530,6 +1530,47 @@ void get_entityVersionHistory_fieldChanged_ignoresWrongExtensionPrefix(TestNames } } + @Test + void get_entityVersionHistory_paginated_ordersNullVersionNumRowsByVersion(TestNamespace ns) { + if (!supportsVersionHistory) return; + + K createRequest = createMinimalRequest(ns); + T created = createEntity(createRequest); + + List insertedVersions = List.of(9.0, 10.0, 11.0); + + try { + for (Double version : insertedVersions) { + ObjectNode historicalVersion = (ObjectNode) JsonUtils.pojoToJsonNode(created); + historicalVersion.put("version", version); + Entity.getCollectionDAO() + .entityExtensionDAO() + .insert( + created.getId(), + EntityUtil.getVersionExtension(getEntityType(), version), + getEntityType(), + JsonUtils.pojoToJson(historicalVersion)); + } + + org.openmetadata.schema.type.EntityHistory paginatedHistory = + getVersionHistoryPaginated(created.getId(), 3, 1); + + assertNotNull(paginatedHistory); + assertNotNull(paginatedHistory.getPaging()); + assertEquals(4, (int) paginatedHistory.getPaging().getTotal()); + assertEquals(3, paginatedHistory.getVersions().size()); + assertEquals(11.0, versionOfHistoryEntry(paginatedHistory.getVersions().get(0)), 0.001); + assertEquals(10.0, versionOfHistoryEntry(paginatedHistory.getVersions().get(1)), 0.001); + assertEquals(9.0, versionOfHistoryEntry(paginatedHistory.getVersions().get(2)), 0.001); + } finally { + for (Double version : insertedVersions) { + Entity.getCollectionDAO() + .entityExtensionDAO() + .delete(created.getId(), EntityUtil.getVersionExtension(getEntityType(), version)); + } + } + } + private double versionOfHistoryEntry(Object versionEntry) { JsonNode node = versionEntry instanceof String versionJson diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/CollectionDAO.java b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/CollectionDAO.java index df2e1fa3d489..6750d9b9cae2 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/CollectionDAO.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/CollectionDAO.java @@ -177,7 +177,6 @@ import org.openmetadata.service.util.EntityUtil; import org.openmetadata.service.util.FullyQualifiedName; import org.openmetadata.service.util.VersionFieldChangeUtil.VersionExtensionMetadata; -import org.openmetadata.service.util.VersionFieldChangeUtil.VersionExtensionRecord; import org.openmetadata.service.util.jdbi.BindConcat; import org.openmetadata.service.util.jdbi.BindFQN; import org.openmetadata.service.util.jdbi.BindJsonContains; @@ -1294,7 +1293,13 @@ void insertMany( + "ON CONFLICT (id, extension) DO UPDATE SET jsonSchema = EXCLUDED.jsonSchema, " + "json = EXCLUDED.json, versionNum = EXCLUDED.versionNum, changedFieldKeys = EXCLUDED.changedFieldKeys", connectionType = POSTGRES) - void insertVersionExtension(@BindBean VersionExtensionRecord versionExtensionRecord); + void insertVersionExtension( + @BindUUID("id") UUID id, + @Bind("extension") String extension, + @Bind("jsonSchema") String jsonSchema, + @Bind("json") String json, + @Bind("versionNum") Double versionNum, + @Bind("changedFieldKeys") String changedFieldKeys); @Transaction @ConnectionAwareSqlBatch( @@ -1309,7 +1314,13 @@ void insertMany( + "ON CONFLICT (id, extension) DO UPDATE SET jsonSchema = EXCLUDED.jsonSchema, " + "json = EXCLUDED.json, versionNum = EXCLUDED.versionNum, changedFieldKeys = EXCLUDED.changedFieldKeys", connectionType = POSTGRES) - void insertVersionExtensions(@BindBean List versionExtensionRecords); + void insertVersionExtensions( + @BindUUID("id") List id, + @Bind("extension") List extension, + @Bind("jsonSchema") String jsonSchema, + @Bind("json") List json, + @Bind("versionNum") List versionNum, + @Bind("changedFieldKeys") List changedFieldKeys); @ConnectionAwareSqlUpdate( value = "UPDATE entity_extension SET json = :json where (json -> '$.id') = :id", @@ -1460,14 +1471,14 @@ int getEntityHistoryByTimestampRangeCount( value = "SELECT extension, json FROM entity_extension WHERE id = :id AND extension " + "LIKE CONCAT(:extensionPrefix, '.%') " - + "ORDER BY versionNum DESC " + + "ORDER BY COALESCE(versionNum, CAST(SUBSTRING_INDEX(extension, '.version.', -1) AS DOUBLE)) DESC, extension DESC " + "LIMIT :limit OFFSET :offset", connectionType = MYSQL) @ConnectionAwareSqlQuery( value = "SELECT extension, json FROM entity_extension WHERE id = :id AND extension " + "LIKE CONCAT(:extensionPrefix, '.%') " - + "ORDER BY versionNum DESC " + + "ORDER BY COALESCE(versionNum, split_part(extension, '.version.', 2)::DOUBLE PRECISION) DESC, extension DESC " + "LIMIT :limit OFFSET :offset", connectionType = POSTGRES) @RegisterRowMapper(ExtensionMapper.class) @@ -1483,7 +1494,7 @@ List getExtensionsWithOffset( + "WHERE id = :id " + "AND extension LIKE CONCAT(:extensionPrefix, '.%') " + "AND JSON_CONTAINS(changedFieldKeys, JSON_ARRAY(:fieldPath)) " - + "ORDER BY versionNum DESC " + + "ORDER BY COALESCE(versionNum, CAST(SUBSTRING_INDEX(extension, '.version.', -1) AS DOUBLE)) DESC, extension DESC " + "LIMIT :limit OFFSET :offset", connectionType = MYSQL) @ConnectionAwareSqlQuery( @@ -1492,7 +1503,7 @@ List getExtensionsWithOffset( + "WHERE id = :id " + "AND extension LIKE CONCAT(:extensionPrefix, '.%') " + "AND jsonb_exists(changedFieldKeys, :fieldPath) " - + "ORDER BY versionNum DESC " + + "ORDER BY COALESCE(versionNum, split_part(extension, '.version.', 2)::DOUBLE PRECISION) DESC, extension DESC " + "LIMIT :limit OFFSET :offset", connectionType = POSTGRES) @RegisterRowMapper(ExtensionMapper.class) @@ -1520,6 +1531,22 @@ int getExtensionCountByFieldChanged( @Bind("extensionPrefix") String extensionPrefix, @Bind("fieldPath") String fieldPath); + @ConnectionAwareSqlQuery( + value = + "SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS " + + "WHERE TABLE_SCHEMA = DATABASE() " + + "AND TABLE_NAME = 'entity_extension' " + + "AND COLUMN_NAME IN ('versionNum', 'changedFieldKeys')", + connectionType = MYSQL) + @ConnectionAwareSqlQuery( + value = + "SELECT COUNT(*) FROM information_schema.columns " + + "WHERE table_schema = current_schema() " + + "AND table_name = 'entity_extension' " + + "AND column_name IN ('versionnum', 'changedfieldkeys')", + connectionType = POSTGRES) + int getVersionFieldMetadataColumnCount(); + @SqlQuery( "SELECT COUNT(*) FROM entity_extension WHERE id = :id AND extension " + "LIKE CONCAT(:extensionPrefix, '.%')") 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 16b1813096cf..d77684c8551d 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 @@ -338,6 +338,7 @@ public Integer load(String key) throws Exception { protected final boolean supportsFollower; protected final boolean supportsExtension; protected final boolean supportsVotes; + private volatile boolean versionFieldMetadataColumnsAvailable; @Getter protected final boolean supportsDomains; protected final boolean supportsDataProducts; @Getter protected final boolean supportsReviewers; @@ -1285,11 +1286,20 @@ public final EntityHistoryWithOffset listVersionsWithOffset( } if (dbLimit > 0) { - List records = - daoCollection - .entityExtensionDAO() - .getExtensionsWithOffset(id, extensionPrefix, dbLimit, dbOffset); - records.forEach(r -> versions.add(new EntityVersionPair(r).getEntityJson())); + if (hasVersionFieldMetadataColumns()) { + List records = + daoCollection + .entityExtensionDAO() + .getExtensionsWithOffset(id, extensionPrefix, dbLimit, dbOffset); + records.forEach(r -> versions.add(new EntityVersionPair(r).getEntityJson())); + } else { + daoCollection.entityExtensionDAO().getExtensions(id, extensionPrefix).stream() + .map(EntityVersionPair::new) + .sorted(EntityUtil.compareVersion.reversed()) + .skip(dbOffset) + .limit(dbLimit) + .forEach(version -> versions.add(version.getEntityJson())); + } } int extensionCount = daoCollection.entityExtensionDAO().getExtensionCount(id, extensionPrefix); @@ -1306,18 +1316,13 @@ public final EntityHistoryWithOffset listVersionsWithOffset( private EntityHistoryWithOffset listVersionsWithFieldFilter( UUID id, T latest, int limit, int offset, String rawFieldChanged) { - try { + if (hasVersionFieldMetadataColumns()) { return listVersionsWithFieldFilterIndexed(id, latest, limit, offset, rawFieldChanged); - } catch (RuntimeException e) { - if (!isVersionFieldMetadataSchemaMissing(e)) { - throw e; - } - LOG.debug( - "Version history metadata columns are unavailable for entityType={}; using in-memory fieldChanged filtering", - entityType, - e); - return listVersionsWithFieldFilterFallback(id, latest, limit, offset, rawFieldChanged); } + LOG.debug( + "Version history metadata columns are unavailable for entityType={}; using in-memory fieldChanged filtering", + entityType); + return listVersionsWithFieldFilterFallback(id, latest, limit, offset, rawFieldChanged); } private EntityHistoryWithOffset listVersionsWithFieldFilterIndexed( @@ -1413,20 +1418,13 @@ private boolean latestVersionMatchesFieldChanged(T latest, String fieldChanged) return VersionFieldChangeUtil.matchesFieldChanged(latest.getChangeDescription(), fieldChanged); } - private boolean isVersionFieldMetadataSchemaMissing(Throwable throwable) { - Throwable current = throwable; - while (current != null) { - String message = current.getMessage(); - if (message != null) { - String normalizedMessage = message.toLowerCase(Locale.ROOT); - if (normalizedMessage.contains("changedfieldkeys") - || normalizedMessage.contains("versionnum")) { - return true; - } - } - current = current.getCause(); + private boolean hasVersionFieldMetadataColumns() { + if (versionFieldMetadataColumnsAvailable) { + return true; } - return false; + versionFieldMetadataColumnsAvailable = + daoCollection.entityExtensionDAO().getVersionFieldMetadataColumnCount() >= 2; + return versionFieldMetadataColumnsAvailable; } private VersionFieldChangeUtil.VersionExtensionRecord buildVersionExtensionRecord( @@ -1446,12 +1444,17 @@ private VersionFieldChangeUtil.VersionExtensionRecord buildVersionExtensionRecor private void insertVersionExtensionRecord( VersionFieldChangeUtil.VersionExtensionRecord versionExtensionRecord) { - try { - daoCollection.entityExtensionDAO().insertVersionExtension(versionExtensionRecord); - } catch (RuntimeException e) { - if (!isVersionFieldMetadataSchemaMissing(e)) { - throw e; - } + if (hasVersionFieldMetadataColumns()) { + daoCollection + .entityExtensionDAO() + .insertVersionExtension( + versionExtensionRecord.getId(), + versionExtensionRecord.getExtension(), + versionExtensionRecord.getJsonSchema(), + versionExtensionRecord.getJson(), + versionExtensionRecord.getVersionNum(), + versionExtensionRecord.getChangedFieldKeys()); + } else { daoCollection .entityExtensionDAO() .insert( @@ -1464,12 +1467,27 @@ private void insertVersionExtensionRecord( private void insertVersionExtensionRecords( List versionExtensionRecords) { - try { - daoCollection.entityExtensionDAO().insertVersionExtensions(versionExtensionRecords); - } catch (RuntimeException e) { - if (!isVersionFieldMetadataSchemaMissing(e)) { - throw e; - } + if (hasVersionFieldMetadataColumns()) { + daoCollection + .entityExtensionDAO() + .insertVersionExtensions( + versionExtensionRecords.stream() + .map(VersionFieldChangeUtil.VersionExtensionRecord::getId) + .toList(), + versionExtensionRecords.stream() + .map(VersionFieldChangeUtil.VersionExtensionRecord::getExtension) + .toList(), + entityType, + versionExtensionRecords.stream() + .map(VersionFieldChangeUtil.VersionExtensionRecord::getJson) + .toList(), + versionExtensionRecords.stream() + .map(VersionFieldChangeUtil.VersionExtensionRecord::getVersionNum) + .toList(), + versionExtensionRecords.stream() + .map(VersionFieldChangeUtil.VersionExtensionRecord::getChangedFieldKeys) + .toList()); + } else { daoCollection .entityExtensionDAO() .insertMany( diff --git a/openmetadata-service/src/test/java/org/openmetadata/service/migration/utils/MigrationSqlStatementHashTest.java b/openmetadata-service/src/test/java/org/openmetadata/service/migration/utils/MigrationSqlStatementHashTest.java new file mode 100644 index 000000000000..985348d4dc85 --- /dev/null +++ b/openmetadata-service/src/test/java/org/openmetadata/service/migration/utils/MigrationSqlStatementHashTest.java @@ -0,0 +1,80 @@ +package org.openmetadata.service.migration.utils; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import org.flywaydb.core.api.configuration.ClassicConfiguration; +import org.flywaydb.core.api.configuration.Configuration; +import org.flywaydb.core.internal.parser.Parser; +import org.flywaydb.core.internal.parser.ParsingContext; +import org.flywaydb.core.internal.resource.filesystem.FileSystemResource; +import org.flywaydb.core.internal.sqlscript.SqlStatementIterator; +import org.flywaydb.database.mysql.MySQLParser; +import org.junit.jupiter.api.Test; +import org.openmetadata.service.util.EntityUtil; + +class MigrationSqlStatementHashTest { + + @Test + void mysql1130MigrationStatementsHaveUniqueHashesWithinEachFile() { + assertUniqueStatementHashes( + resolveRepoRoot() + .resolve("bootstrap/sql/migrations/native/1.13.0/mysql/schemaChanges.sql")); + assertUniqueStatementHashes( + resolveRepoRoot() + .resolve( + "bootstrap/sql/migrations/native/1.13.0/mysql/postDataMigrationSQLScript.sql")); + } + + private void assertUniqueStatementHashes(Path sqlFile) { + List statements = parseStatements(sqlFile); + Map duplicateHashes = + statements.stream() + .collect( + Collectors.groupingBy(EntityUtil::hash, LinkedHashMap::new, Collectors.counting())) + .entrySet() + .stream() + .filter(entry -> entry.getValue() > 1) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + + assertTrue( + duplicateHashes.isEmpty(), + () -> "Duplicate migration statement hashes found in " + sqlFile + ": " + duplicateHashes); + } + + private List parseStatements(Path sqlFile) { + Configuration configuration = new ClassicConfiguration(); + Parser parser = new MySQLParser(configuration, new ParsingContext()); + try (SqlStatementIterator iterator = + parser.parse( + new FileSystemResource(null, sqlFile.toString(), StandardCharsets.UTF_8, true))) { + List statements = new ArrayList<>(); + while (iterator.hasNext()) { + statements.add(iterator.next().getSql()); + } + return statements; + } catch (Exception e) { + throw new RuntimeException("Failed to parse migration SQL file " + sqlFile, e); + } + } + + private Path resolveRepoRoot() { + Path current = Paths.get("").toAbsolutePath(); + while (current != null + && !Files.isDirectory(current.resolve("bootstrap/sql/migrations/native"))) { + current = current.getParent(); + } + assertNotNull( + current, "Unable to locate repository root containing bootstrap/sql/migrations/native"); + return current; + } +} diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/EntityVersionPage/EntityVersionPage.component.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/EntityVersionPage/EntityVersionPage.component.tsx index 81d409e6a77c..6bbb15bee6c4 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/EntityVersionPage/EntityVersionPage.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/EntityVersionPage/EntityVersionPage.component.tsx @@ -495,24 +495,18 @@ const EntityVersionPage: FunctionComponent = () => { limit: VERSION_PAGE_SIZE, offset: currentOffset, }); + const appendedVersions = moreVersions.versions ?? []; + const totalLoaded = currentOffset + appendedVersions.length; + + setHasMoreVersions( + moreVersions.paging ? totalLoaded < moreVersions.paging.total : false + ); setVersionList((prev) => { - const combined = { + return { ...moreVersions, - versions: [ - ...(prev.versions ?? []), - ...(moreVersions.versions ?? []), - ], + versions: [...(prev.versions ?? []), ...appendedVersions], }; - - if (moreVersions.paging) { - const totalLoaded = combined.versions?.length ?? 0; - setHasMoreVersions(totalLoaded < moreVersions.paging.total); - } else { - setHasMoreVersions(false); - } - - return combined; }); } finally { setIsLoadingMore(false); From d1ac8f7b82f7d0462d8191ffa85d0f985bdae253 Mon Sep 17 00:00:00 2001 From: Sriharsha Chintalapani Date: Mon, 9 Mar 2026 19:32:29 -0700 Subject: [PATCH 14/20] Address comments --- .../mysql/postDataMigrationSQLScript.sql | 43 +- .../native/1.13.0/mysql/schemaChanges.sql | 40 +- .../postgres/postDataMigrationSQLScript.sql | 61 ++- .../openmetadata/it/tests/BaseEntityIT.java | 77 ++++ .../it/tests/TableResourceIT.java | 28 ++ .../service/jdbi3/CollectionDAO.java | 179 ++++++-- .../service/jdbi3/EntityRepository.java | 418 ++++++++++-------- .../service/util/VersionFieldChangeUtil.java | 29 +- .../utils/MigrationSqlStatementHashTest.java | 18 + .../util/VersionFieldChangeUtilTest.java | 36 +- 10 files changed, 570 insertions(+), 359 deletions(-) diff --git a/bootstrap/sql/migrations/native/1.13.0/mysql/postDataMigrationSQLScript.sql b/bootstrap/sql/migrations/native/1.13.0/mysql/postDataMigrationSQLScript.sql index 540b1211890d..0aadf9a6adf2 100644 --- a/bootstrap/sql/migrations/native/1.13.0/mysql/postDataMigrationSQLScript.sql +++ b/bootstrap/sql/migrations/native/1.13.0/mysql/postDataMigrationSQLScript.sql @@ -3,25 +3,7 @@ SET json = JSON_REMOVE(json, '$.sourceConfig.config.computeMetrics') WHERE JSON_EXTRACT(json, '$.sourceConfig.config.computeMetrics') IS NOT NULL AND pipelineType = 'profiler'; -SET @post_version_col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'entity_extension' AND COLUMN_NAME = 'versionNum'); -SET @post_add_version_num_sql = IF(@post_version_col_exists = 0, - 'ALTER TABLE entity_extension ADD COLUMN versionNum DOUBLE NULL', - 'SELECT 1'); -PREPARE post_add_version_num_stmt FROM @post_add_version_num_sql; -EXECUTE post_add_version_num_stmt; -DEALLOCATE PREPARE post_add_version_num_stmt; - -SET @post_changed_fields_col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'entity_extension' AND COLUMN_NAME = 'changedFieldKeys'); -SET @post_add_changed_field_keys_sql = IF(@post_changed_fields_col_exists = 0, - 'ALTER TABLE entity_extension ADD COLUMN changedFieldKeys JSON NULL', - 'SELECT 1'); -PREPARE post_add_changed_field_keys_stmt FROM @post_add_changed_field_keys_sql; -EXECUTE post_add_changed_field_keys_stmt; -DEALLOCATE PREPARE post_add_changed_field_keys_stmt; - -WITH RECURSIVE +WITH field_changes AS ( SELECT e.id, e.extension, jt.field_name FROM entity_extension AS e @@ -51,21 +33,11 @@ field_changes AS ( ) AS jt ON TRUE WHERE e.extension LIKE '%.version.%' ), -suffixes AS ( - SELECT id, extension, field_name AS suffix +distinct_field_changes AS ( + SELECT DISTINCT id, extension, field_name FROM field_changes WHERE field_name IS NOT NULL AND field_name <> '' - - UNION ALL - - SELECT id, extension, SUBSTRING(suffix, LOCATE('.', suffix) + 1) - FROM suffixes - WHERE LOCATE('.', suffix) > 0 -), -distinct_suffixes AS ( - SELECT DISTINCT id, extension, suffix - FROM suffixes ), version_metadata AS ( SELECT @@ -73,12 +45,13 @@ version_metadata AS ( e.extension, CAST(SUBSTRING_INDEX(e.extension, '.version.', -1) AS DOUBLE) AS version_num, CASE - WHEN COUNT(ds.suffix) = 0 THEN JSON_ARRAY() - ELSE JSON_ARRAYAGG(ds.suffix) + WHEN COUNT(fc.field_name) = 0 THEN JSON_ARRAY() + ELSE JSON_ARRAYAGG(fc.field_name) END AS changed_field_keys FROM entity_extension AS e - LEFT JOIN distinct_suffixes AS ds - ON ds.id = e.id AND ds.extension = e.extension + LEFT JOIN distinct_field_changes AS fc + ON fc.id = e.id + AND fc.extension = e.extension WHERE e.extension LIKE '%.version.%' GROUP BY e.id, e.extension ) diff --git a/bootstrap/sql/migrations/native/1.13.0/mysql/schemaChanges.sql b/bootstrap/sql/migrations/native/1.13.0/mysql/schemaChanges.sql index 24fc561fbd96..34d59d0d446c 100644 --- a/bootstrap/sql/migrations/native/1.13.0/mysql/schemaChanges.sql +++ b/bootstrap/sql/migrations/native/1.13.0/mysql/schemaChanges.sql @@ -22,38 +22,12 @@ SET json = JSON_SET( ) WHERE JSON_CONTAINS_PATH(json, 'one', '$.preview'); -SET @version_col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'entity_extension' AND COLUMN_NAME = 'versionNum'); -SET @add_version_num_sql = IF(@version_col_exists = 0, - 'ALTER TABLE entity_extension ADD COLUMN versionNum DOUBLE NULL', - 'SELECT 1'); -PREPARE add_version_num_stmt FROM @add_version_num_sql; -EXECUTE add_version_num_stmt; -DEALLOCATE PREPARE add_version_num_stmt; +ALTER TABLE entity_extension + ADD COLUMN versionNum DOUBLE NULL, + ADD COLUMN changedFieldKeys JSON NULL; -SET @changed_fields_col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'entity_extension' AND COLUMN_NAME = 'changedFieldKeys'); -SET @add_changed_field_keys_sql = IF(@changed_fields_col_exists = 0, - 'ALTER TABLE entity_extension ADD COLUMN changedFieldKeys JSON NULL', - 'SELECT 1'); -PREPARE add_changed_field_keys_stmt FROM @add_changed_field_keys_sql; -EXECUTE add_changed_field_keys_stmt; -DEALLOCATE PREPARE add_changed_field_keys_stmt; +CREATE INDEX idx_entity_extension_version_order + ON entity_extension (id, versionNum); -SET @version_index_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'entity_extension' AND INDEX_NAME = 'idx_entity_extension_version_order'); -SET @add_version_index_sql = IF(@version_index_exists = 0, - 'CREATE INDEX idx_entity_extension_version_order ON entity_extension (id, versionNum)', - 'SELECT 1'); -PREPARE add_version_index_stmt FROM @add_version_index_sql; -EXECUTE add_version_index_stmt; -DEALLOCATE PREPARE add_version_index_stmt; - -SET @changed_fields_index_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS - WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'entity_extension' AND INDEX_NAME = 'idx_entity_extension_changed_field_keys'); -SET @add_changed_field_keys_index_sql = IF(@changed_fields_index_exists = 0, - 'CREATE INDEX idx_entity_extension_changed_field_keys ON entity_extension ((CAST(changedFieldKeys->''$'' AS CHAR(512) ARRAY)))', - 'SELECT 1'); -PREPARE add_changed_field_keys_index_stmt FROM @add_changed_field_keys_index_sql; -EXECUTE add_changed_field_keys_index_stmt; -DEALLOCATE PREPARE add_changed_field_keys_index_stmt; +CREATE INDEX idx_entity_extension_changed_field_keys + ON entity_extension ((CAST(changedFieldKeys->'$' AS CHAR(512) ARRAY))); diff --git a/bootstrap/sql/migrations/native/1.13.0/postgres/postDataMigrationSQLScript.sql b/bootstrap/sql/migrations/native/1.13.0/postgres/postDataMigrationSQLScript.sql index 1c489405d560..478d6d607be5 100644 --- a/bootstrap/sql/migrations/native/1.13.0/postgres/postDataMigrationSQLScript.sql +++ b/bootstrap/sql/migrations/native/1.13.0/postgres/postDataMigrationSQLScript.sql @@ -10,41 +10,36 @@ WITH version_metadata AS ( split_part(e.extension, '.version.', 2)::DOUBLE PRECISION AS version_num, COALESCE( ( - SELECT jsonb_agg(suffix ORDER BY suffix) + SELECT jsonb_agg(field_name ORDER BY field_name) FROM ( SELECT DISTINCT - array_to_string( - ARRAY( - SELECT part - FROM unnest(field_paths.path_parts) WITH ORDINALITY AS parts(part, ord) - WHERE ord >= indexes.path_index - ORDER BY ord - ), - '.' - ) AS suffix - FROM ( - SELECT string_to_array(field_name, '.') AS path_parts - FROM ( - SELECT field_change ->> 'name' AS field_name - FROM jsonb_array_elements( - COALESCE(e.json -> 'changeDescription' -> 'fieldsAdded', '[]'::jsonb) - ) AS field_change - UNION ALL - SELECT field_change ->> 'name' AS field_name - FROM jsonb_array_elements( - COALESCE(e.json -> 'changeDescription' -> 'fieldsUpdated', '[]'::jsonb) - ) AS field_change - UNION ALL - SELECT field_change ->> 'name' AS field_name - FROM jsonb_array_elements( - COALESCE(e.json -> 'changeDescription' -> 'fieldsDeleted', '[]'::jsonb) - ) AS field_change - ) AS field_changes - WHERE field_name IS NOT NULL - AND field_name <> '' - ) AS field_paths - CROSS JOIN LATERAL generate_subscripts(field_paths.path_parts, 1) AS indexes(path_index) - ) AS suffixes + field_change ->> 'name' AS field_name + FROM jsonb_array_elements( + COALESCE(e.json -> 'changeDescription' -> 'fieldsAdded', '[]'::jsonb) + ) AS field_change + WHERE field_change ->> 'name' IS NOT NULL + AND field_change ->> 'name' <> '' + + UNION + + SELECT DISTINCT + field_change ->> 'name' AS field_name + FROM jsonb_array_elements( + COALESCE(e.json -> 'changeDescription' -> 'fieldsUpdated', '[]'::jsonb) + ) AS field_change + WHERE field_change ->> 'name' IS NOT NULL + AND field_change ->> 'name' <> '' + + UNION + + SELECT DISTINCT + field_change ->> 'name' AS field_name + FROM jsonb_array_elements( + COALESCE(e.json -> 'changeDescription' -> 'fieldsDeleted', '[]'::jsonb) + ) AS field_change + WHERE field_change ->> 'name' IS NOT NULL + AND field_change ->> 'name' <> '' + ) AS exact_field_names ), '[]'::jsonb ) AS changed_field_keys 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 1efa10c3a95d..90693572a678 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 @@ -10,6 +10,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; import java.net.http.HttpResponse; import java.time.Duration; @@ -1478,6 +1479,15 @@ void get_entityVersionHistory_fieldChanged_200(TestNamespace ns) { "Substring matches should not be treated as field-name matches"); assertEquals(0, (int) substringNoMatch.getPaging().getTotal()); + org.openmetadata.schema.type.EntityHistory nestedPathNoMatch = + getVersionHistoryWithFieldChanged(created.getId(), 100, 0, "nested.description"); + assertNotNull(nestedPathNoMatch); + assertEquals( + 0, + nestedPathNoMatch.getVersions().size(), + "Exact field filters should not match nested paths for a top-level description change"); + assertEquals(0, (int) nestedPathNoMatch.getPaging().getTotal()); + org.openmetadata.schema.type.EntityHistory noMatch = getVersionHistoryWithFieldChanged(created.getId(), 100, 0, "nonExistentField_xyz_12345"); assertNotNull(noMatch); @@ -1571,6 +1581,73 @@ void get_entityVersionHistory_paginated_ordersNullVersionNumRowsByVersion(TestNa } } + @Test + void get_entityVersionHistory_fieldChanged_matchesNullChangedFieldKeysRows(TestNamespace ns) { + if (!supportsPatch) return; + + K createRequest = createMinimalRequest(ns); + T created = createEntity(createRequest); + + created.setDescription("Description change for null changedFieldKeys test"); + patchEntity(created.getId().toString(), created); + + org.openmetadata.schema.type.EntityHistory baseline = + getVersionHistoryWithFieldChanged(created.getId(), 100, 0, "description"); + assertNotNull(baseline); + assertNotNull(baseline.getPaging()); + + double legacyVersion = 77.0; + String legacyExtension = EntityUtil.getVersionExtension(getEntityType(), legacyVersion); + + try { + ObjectNode historicalVersion = (ObjectNode) JsonUtils.pojoToJsonNode(created); + historicalVersion.put("version", legacyVersion); + ObjectNode changeDescription = historicalVersion.putObject("changeDescription"); + ArrayNode fieldsUpdated = changeDescription.putArray("fieldsUpdated"); + fieldsUpdated.addObject().put("name", "nested.description"); + + Entity.getCollectionDAO() + .entityExtensionDAO() + .insert( + created.getId(), + legacyExtension, + getEntityType(), + JsonUtils.pojoToJson(historicalVersion)); + + org.openmetadata.schema.type.EntityHistory filtered = + getVersionHistoryWithFieldChanged(created.getId(), 100, 0, "description"); + + assertNotNull(filtered); + assertNotNull(filtered.getPaging()); + assertEquals( + baseline.getPaging().getTotal(), + (int) filtered.getPaging().getTotal(), + "Exact field filters should not match nested legacy rows without changedFieldKeys"); + assertFalse( + filtered.getVersions().stream() + .anyMatch( + version -> Math.abs(versionOfHistoryEntry(version) - legacyVersion) < 0.001), + "Top-level description filters should not include nested legacy rows"); + + org.openmetadata.schema.type.EntityHistory nestedFiltered = + getVersionHistoryWithFieldChanged(created.getId(), 100, 0, "nested.description"); + + assertNotNull(nestedFiltered); + assertNotNull(nestedFiltered.getPaging()); + assertEquals( + 1, + (int) nestedFiltered.getPaging().getTotal(), + "Exact nested field filters should still match legacy rows without changedFieldKeys"); + assertTrue( + nestedFiltered.getVersions().stream() + .anyMatch( + version -> Math.abs(versionOfHistoryEntry(version) - legacyVersion) < 0.001), + "Nested field filters should include the legacy row without changedFieldKeys"); + } finally { + Entity.getCollectionDAO().entityExtensionDAO().delete(created.getId(), legacyExtension); + } + } + private double versionOfHistoryEntry(Object versionEntry) { JsonNode node = versionEntry instanceof String versionJson diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TableResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TableResourceIT.java index 3defe69f849e..d98d9eb843bf 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TableResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TableResourceIT.java @@ -4917,6 +4917,34 @@ protected EntityHistory getVersionHistoryWithFieldChanged( return SdkClients.adminClient().tables().getVersionList(id, limit, offset, fieldChanged); } + @Test + void get_entityVersionHistory_fieldChanged_requiresExactColumnDescriptionPath(TestNamespace ns) { + Table created = createEntity(createMinimalRequest(ns)); + Column nameColumn = findColumnByName(created.getColumns(), "name"); + assertNotNull(nameColumn); + + nameColumn.setDescription("Column description change for exact field filter test"); + patchEntity(created.getId().toString(), created); + + EntityHistory topLevelDescriptionHistory = + getVersionHistoryWithFieldChanged(created.getId(), 100, 0, "description"); + assertNotNull(topLevelDescriptionHistory); + assertNotNull(topLevelDescriptionHistory.getPaging()); + assertEquals(0, (int) topLevelDescriptionHistory.getPaging().getTotal()); + + EntityHistory columnDescriptionHistory = + getVersionHistoryWithFieldChanged(created.getId(), 100, 0, "columns.description"); + assertNotNull(columnDescriptionHistory); + assertNotNull(columnDescriptionHistory.getPaging()); + assertEquals(0, (int) columnDescriptionHistory.getPaging().getTotal()); + + EntityHistory exactColumnDescriptionHistory = + getVersionHistoryWithFieldChanged(created.getId(), 100, 0, "columns.name.description"); + assertNotNull(exactColumnDescriptionHistory); + assertNotNull(exactColumnDescriptionHistory.getPaging()); + assertTrue(exactColumnDescriptionHistory.getPaging().getTotal() >= 1); + } + @Override protected Table getVersion(UUID id, Double version) { return SdkClients.adminClient().tables().getVersion(id.toString(), version); diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/CollectionDAO.java b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/CollectionDAO.java index 6750d9b9cae2..80d237efafd1 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/CollectionDAO.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/CollectionDAO.java @@ -176,7 +176,6 @@ import org.openmetadata.service.resources.tags.TagLabelUtil; import org.openmetadata.service.util.EntityUtil; import org.openmetadata.service.util.FullyQualifiedName; -import org.openmetadata.service.util.VersionFieldChangeUtil.VersionExtensionMetadata; import org.openmetadata.service.util.jdbi.BindConcat; import org.openmetadata.service.util.jdbi.BindFQN; import org.openmetadata.service.util.jdbi.BindJsonContains; @@ -1382,22 +1381,6 @@ List getExtensionsByPrefixBatch( void bulkUpsertExtensions( @BindBean List extensionWithIdObjects); - @Transaction - @ConnectionAwareSqlBatch( - value = - "UPDATE entity_extension " - + "SET versionNum = :versionNum, changedFieldKeys = :changedFieldKeys " - + "WHERE id = :id AND extension = :extension", - connectionType = MYSQL) - @ConnectionAwareSqlBatch( - value = - "UPDATE entity_extension " - + "SET versionNum = :versionNum, changedFieldKeys = (:changedFieldKeys :: jsonb) " - + "WHERE id = :id AND extension = :extension", - connectionType = POSTGRES) - void updateVersionExtensionMetadata( - @BindBean List versionExtensionMetadata); - @RegisterRowMapper(ExtensionMapper.class) @SqlQuery( "SELECT extension, json FROM entity_extension WHERE id = :id AND extension " @@ -1490,20 +1473,58 @@ List getExtensionsWithOffset( @ConnectionAwareSqlQuery( value = - "SELECT extension, json FROM entity_extension " - + "WHERE id = :id " - + "AND extension LIKE CONCAT(:extensionPrefix, '.%') " - + "AND JSON_CONTAINS(changedFieldKeys, JSON_ARRAY(:fieldPath)) " - + "ORDER BY COALESCE(versionNum, CAST(SUBSTRING_INDEX(extension, '.version.', -1) AS DOUBLE)) DESC, extension DESC " + "SELECT extension, json FROM entity_extension WHERE id = :id AND extension " + + "LIKE CONCAT(:extensionPrefix, '.%') " + + "ORDER BY CAST(SUBSTRING_INDEX(extension, '.version.', -1) AS DOUBLE) DESC, extension DESC " + "LIMIT :limit OFFSET :offset", connectionType = MYSQL) @ConnectionAwareSqlQuery( value = - "SELECT extension, json FROM entity_extension " - + "WHERE id = :id " - + "AND extension LIKE CONCAT(:extensionPrefix, '.%') " - + "AND jsonb_exists(changedFieldKeys, :fieldPath) " - + "ORDER BY COALESCE(versionNum, split_part(extension, '.version.', 2)::DOUBLE PRECISION) DESC, extension DESC " + "SELECT extension, json FROM entity_extension WHERE id = :id AND extension " + + "LIKE CONCAT(:extensionPrefix, '.%') " + + "ORDER BY split_part(extension, '.version.', 2)::DOUBLE PRECISION DESC, extension DESC " + + "LIMIT :limit OFFSET :offset", + connectionType = POSTGRES) + @RegisterRowMapper(ExtensionMapper.class) + List getExtensionsWithOffsetLegacy( + @BindUUID("id") UUID id, + @Bind("extensionPrefix") String extensionPrefix, + @Bind("limit") int limit, + @Bind("offset") int offset); + + @ConnectionAwareSqlQuery( + value = + "SELECT e.extension, e.json FROM entity_extension e " + + "WHERE e.id = :id " + + "AND e.extension LIKE CONCAT(:extensionPrefix, '.%') " + + "AND (JSON_CONTAINS(e.changedFieldKeys, JSON_ARRAY(:fieldPath)) " + + "OR (e.changedFieldKeys IS NULL AND EXISTS (" + + "SELECT 1 FROM JSON_TABLE(" + + "JSON_MERGE_PRESERVE(" + + "COALESCE(JSON_EXTRACT(e.json, '$.changeDescription.fieldsAdded'), JSON_ARRAY()), " + + "COALESCE(JSON_EXTRACT(e.json, '$.changeDescription.fieldsUpdated'), JSON_ARRAY()), " + + "COALESCE(JSON_EXTRACT(e.json, '$.changeDescription.fieldsDeleted'), JSON_ARRAY())" + + "), '$[*]' COLUMNS (field_name VARCHAR(1024) PATH '$.name')) field_changes " + + "WHERE field_name = :fieldPath" + + "))) " + + "ORDER BY COALESCE(e.versionNum, CAST(SUBSTRING_INDEX(e.extension, '.version.', -1) AS DOUBLE)) DESC, e.extension DESC " + + "LIMIT :limit OFFSET :offset", + connectionType = MYSQL) + @ConnectionAwareSqlQuery( + value = + "SELECT e.extension, e.json FROM entity_extension e " + + "WHERE e.id = :id " + + "AND e.extension LIKE CONCAT(:extensionPrefix, '.%') " + + "AND (jsonb_exists(e.changedFieldKeys, :fieldPath) " + + "OR (e.changedFieldKeys IS NULL AND EXISTS (" + + "SELECT 1 FROM jsonb_array_elements(" + + "COALESCE(e.json #> '{changeDescription,fieldsAdded}', '[]'::jsonb) " + + "|| COALESCE(e.json #> '{changeDescription,fieldsUpdated}', '[]'::jsonb) " + + "|| COALESCE(e.json #> '{changeDescription,fieldsDeleted}', '[]'::jsonb)" + + ") AS field_change " + + "WHERE field_change ->> 'name' = :fieldPath " + + "))) " + + "ORDER BY COALESCE(e.versionNum, split_part(e.extension, '.version.', 2)::DOUBLE PRECISION) DESC, e.extension DESC " + "LIMIT :limit OFFSET :offset", connectionType = POSTGRES) @RegisterRowMapper(ExtensionMapper.class) @@ -1516,15 +1537,32 @@ List getExtensionsWithFieldChanged( @ConnectionAwareSqlQuery( value = - "SELECT COUNT(*) FROM entity_extension WHERE id = :id " - + "AND extension LIKE CONCAT(:extensionPrefix, '.%') " - + "AND JSON_CONTAINS(changedFieldKeys, JSON_ARRAY(:fieldPath))", + "SELECT COUNT(*) FROM entity_extension e WHERE e.id = :id " + + "AND e.extension LIKE CONCAT(:extensionPrefix, '.%') " + + "AND (JSON_CONTAINS(e.changedFieldKeys, JSON_ARRAY(:fieldPath)) " + + "OR (e.changedFieldKeys IS NULL AND EXISTS (" + + "SELECT 1 FROM JSON_TABLE(" + + "JSON_MERGE_PRESERVE(" + + "COALESCE(JSON_EXTRACT(e.json, '$.changeDescription.fieldsAdded'), JSON_ARRAY()), " + + "COALESCE(JSON_EXTRACT(e.json, '$.changeDescription.fieldsUpdated'), JSON_ARRAY()), " + + "COALESCE(JSON_EXTRACT(e.json, '$.changeDescription.fieldsDeleted'), JSON_ARRAY())" + + "), '$[*]' COLUMNS (field_name VARCHAR(1024) PATH '$.name')) field_changes " + + "WHERE field_name = :fieldPath" + + ")))", connectionType = MYSQL) @ConnectionAwareSqlQuery( value = - "SELECT COUNT(*) FROM entity_extension WHERE id = :id " - + "AND extension LIKE CONCAT(:extensionPrefix, '.%') " - + "AND jsonb_exists(changedFieldKeys, :fieldPath)", + "SELECT COUNT(*) FROM entity_extension e WHERE e.id = :id " + + "AND e.extension LIKE CONCAT(:extensionPrefix, '.%') " + + "AND (jsonb_exists(e.changedFieldKeys, :fieldPath) " + + "OR (e.changedFieldKeys IS NULL AND EXISTS (" + + "SELECT 1 FROM jsonb_array_elements(" + + "COALESCE(e.json #> '{changeDescription,fieldsAdded}', '[]'::jsonb) " + + "|| COALESCE(e.json #> '{changeDescription,fieldsUpdated}', '[]'::jsonb) " + + "|| COALESCE(e.json #> '{changeDescription,fieldsDeleted}', '[]'::jsonb)" + + ") AS field_change " + + "WHERE field_change ->> 'name' = :fieldPath " + + ")))", connectionType = POSTGRES) int getExtensionCountByFieldChanged( @BindUUID("id") UUID id, @@ -1533,19 +1571,76 @@ int getExtensionCountByFieldChanged( @ConnectionAwareSqlQuery( value = - "SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS " - + "WHERE TABLE_SCHEMA = DATABASE() " - + "AND TABLE_NAME = 'entity_extension' " - + "AND COLUMN_NAME IN ('versionNum', 'changedFieldKeys')", + "SELECT e.extension, e.json FROM entity_extension e " + + "WHERE e.id = :id " + + "AND e.extension LIKE CONCAT(:extensionPrefix, '.%') " + + "AND EXISTS (" + + "SELECT 1 FROM JSON_TABLE(" + + "JSON_MERGE_PRESERVE(" + + "COALESCE(JSON_EXTRACT(e.json, '$.changeDescription.fieldsAdded'), JSON_ARRAY()), " + + "COALESCE(JSON_EXTRACT(e.json, '$.changeDescription.fieldsUpdated'), JSON_ARRAY()), " + + "COALESCE(JSON_EXTRACT(e.json, '$.changeDescription.fieldsDeleted'), JSON_ARRAY())" + + "), '$[*]' COLUMNS (field_name VARCHAR(1024) PATH '$.name')) field_changes " + + "WHERE field_name = :fieldPath" + + ") " + + "ORDER BY CAST(SUBSTRING_INDEX(e.extension, '.version.', -1) AS DOUBLE) DESC, e.extension DESC " + + "LIMIT :limit OFFSET :offset", connectionType = MYSQL) @ConnectionAwareSqlQuery( value = - "SELECT COUNT(*) FROM information_schema.columns " - + "WHERE table_schema = current_schema() " - + "AND table_name = 'entity_extension' " - + "AND column_name IN ('versionnum', 'changedfieldkeys')", + "SELECT e.extension, e.json FROM entity_extension e " + + "WHERE e.id = :id " + + "AND e.extension LIKE CONCAT(:extensionPrefix, '.%') " + + "AND EXISTS (" + + "SELECT 1 FROM jsonb_array_elements(" + + "COALESCE(e.json #> '{changeDescription,fieldsAdded}', '[]'::jsonb) " + + "|| COALESCE(e.json #> '{changeDescription,fieldsUpdated}', '[]'::jsonb) " + + "|| COALESCE(e.json #> '{changeDescription,fieldsDeleted}', '[]'::jsonb)" + + ") AS field_change " + + "WHERE field_change ->> 'name' = :fieldPath " + + ") " + + "ORDER BY split_part(e.extension, '.version.', 2)::DOUBLE PRECISION DESC, e.extension DESC " + + "LIMIT :limit OFFSET :offset", connectionType = POSTGRES) - int getVersionFieldMetadataColumnCount(); + @RegisterRowMapper(ExtensionMapper.class) + List getExtensionsWithFieldChangedLegacy( + @BindUUID("id") UUID id, + @Bind("extensionPrefix") String extensionPrefix, + @Bind("fieldPath") String fieldPath, + @Bind("limit") int limit, + @Bind("offset") int offset); + + @ConnectionAwareSqlQuery( + value = + "SELECT COUNT(*) FROM entity_extension e WHERE e.id = :id " + + "AND e.extension LIKE CONCAT(:extensionPrefix, '.%') " + + "AND EXISTS (" + + "SELECT 1 FROM JSON_TABLE(" + + "JSON_MERGE_PRESERVE(" + + "COALESCE(JSON_EXTRACT(e.json, '$.changeDescription.fieldsAdded'), JSON_ARRAY()), " + + "COALESCE(JSON_EXTRACT(e.json, '$.changeDescription.fieldsUpdated'), JSON_ARRAY()), " + + "COALESCE(JSON_EXTRACT(e.json, '$.changeDescription.fieldsDeleted'), JSON_ARRAY())" + + "), '$[*]' COLUMNS (field_name VARCHAR(1024) PATH '$.name')) field_changes " + + "WHERE field_name = :fieldPath" + + ")", + connectionType = MYSQL) + @ConnectionAwareSqlQuery( + value = + "SELECT COUNT(*) FROM entity_extension e WHERE e.id = :id " + + "AND e.extension LIKE CONCAT(:extensionPrefix, '.%') " + + "AND EXISTS (" + + "SELECT 1 FROM jsonb_array_elements(" + + "COALESCE(e.json #> '{changeDescription,fieldsAdded}', '[]'::jsonb) " + + "|| COALESCE(e.json #> '{changeDescription,fieldsUpdated}', '[]'::jsonb) " + + "|| COALESCE(e.json #> '{changeDescription,fieldsDeleted}', '[]'::jsonb)" + + ") AS field_change " + + "WHERE field_change ->> 'name' = :fieldPath " + + ")", + connectionType = POSTGRES) + int getExtensionCountByFieldChangedLegacy( + @BindUUID("id") UUID id, + @Bind("extensionPrefix") String extensionPrefix, + @Bind("fieldPath") String fieldPath); @SqlQuery( "SELECT COUNT(*) FROM entity_extension WHERE id = :id AND extension " 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 d77684c8551d..4f73df18d254 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 @@ -141,6 +141,7 @@ import java.util.function.BiConsumer; import java.util.function.BiPredicate; import java.util.function.Function; +import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.stream.Stream; import java.util.stream.StreamSupport; @@ -320,6 +321,9 @@ public Integer load(String key) throws Exception { }); private final String collectionPath; + private static final long VERSION_FIELD_METADATA_RETRY_MILLIS = 30_000L; + private static volatile boolean versionFieldMetadataColumnsAvailable = true; + private static volatile long nextVersionFieldMetadataRetryAt; @Getter public final Class entityClass; @Getter protected final String entityType; @Getter protected final EntityDAO dao; @@ -338,7 +342,6 @@ public Integer load(String key) throws Exception { protected final boolean supportsFollower; protected final boolean supportsExtension; protected final boolean supportsVotes; - private volatile boolean versionFieldMetadataColumnsAvailable; @Getter protected final boolean supportsDomains; protected final boolean supportsDataProducts; @Getter protected final boolean supportsReviewers; @@ -1263,168 +1266,205 @@ public final EntityHistoryWithOffset listVersionsWithOffset(UUID id, int limit, public final EntityHistoryWithOffset listVersionsWithOffset( UUID id, int limit, int offset, String fieldChanged) { + T latest = getLatestVersionEntity(id); + if (hasFieldChangedFilter(fieldChanged)) { + return listVersionsWithFieldFilter(id, latest, limit, offset, fieldChanged); + } + return listAllVersionsWithOffset(id, latest, limit, offset); + } + + private EntityHistoryWithOffset listAllVersionsWithOffset( + UUID id, T latest, int limit, int offset) { + List versions = new ArrayList<>(); String extensionPrefix = EntityUtil.getVersionExtensionPrefix(entityType); + HistoryWindow historyWindow = buildHistoryWindow(true, latest, limit, offset, versions); + addExtensionRecords(versions, getHistoricalVersionPage(id, extensionPrefix, historyWindow)); + int extensionCount = daoCollection.entityExtensionDAO().getExtensionCount(id, extensionPrefix); + return buildEntityHistoryWithOffset(versions, offset, limit, extensionCount + 1); + } + + private EntityHistoryWithOffset listVersionsWithFieldFilter( + UUID id, T latest, int limit, int offset, String rawFieldChanged) { + List versions = new ArrayList<>(); + boolean latestMatches = latestVersionMatchesFieldChanged(latest, rawFieldChanged); + HistoryWindow historyWindow = + buildHistoryWindow(latestMatches, latest, limit, offset, versions); + FieldChangedHistoryPage historicalVersions = + getFieldChangedHistoryPage(id, rawFieldChanged, historyWindow); + addExtensionRecords(versions, historicalVersions.records()); + int total = historicalVersions.total() + (latestMatches ? 1 : 0); + return buildEntityHistoryWithOffset(versions, offset, limit, total); + } + private boolean latestVersionMatchesFieldChanged(T latest, String fieldChanged) { + return VersionFieldChangeUtil.matchesFieldChanged(latest.getChangeDescription(), fieldChanged); + } + + private T getLatestVersionEntity(UUID id) { T latest = setFieldsInternal(find(id, ALL), putFields); setInheritedFields(latest, putFields); + return latest; + } - if (fieldChanged != null && !fieldChanged.isEmpty()) { - return listVersionsWithFieldFilter(id, latest, limit, offset, fieldChanged); - } - - final List versions = new ArrayList<>(); - int dbLimit; - int dbOffset; + private boolean hasFieldChangedFilter(String fieldChanged) { + return fieldChanged != null && !fieldChanged.isEmpty(); + } - if (offset == 0) { + private HistoryWindow buildHistoryWindow( + boolean includeLatest, T latest, int limit, int offset, List versions) { + int historicalOffset = includeLatest ? Math.max(0, offset - 1) : offset; + int historicalLimit = limit; + if (includeLatest && offset == 0) { versions.add(JsonUtils.pojoToJson(latest)); - dbLimit = limit - 1; - dbOffset = 0; - } else { - dbLimit = limit; - dbOffset = offset - 1; + historicalLimit = limit - 1; } + return new HistoryWindow(historicalLimit, historicalOffset); + } - if (dbLimit > 0) { - if (hasVersionFieldMetadataColumns()) { - List records = + private List getHistoricalVersionPage( + UUID id, String extensionPrefix, HistoryWindow historyWindow) { + if (!historyWindow.hasItems()) { + return List.of(); + } + return readVersionHistory( + () -> daoCollection .entityExtensionDAO() - .getExtensionsWithOffset(id, extensionPrefix, dbLimit, dbOffset); - records.forEach(r -> versions.add(new EntityVersionPair(r).getEntityJson())); - } else { - daoCollection.entityExtensionDAO().getExtensions(id, extensionPrefix).stream() - .map(EntityVersionPair::new) - .sorted(EntityUtil.compareVersion.reversed()) - .skip(dbOffset) - .limit(dbLimit) - .forEach(version -> versions.add(version.getEntityJson())); - } - } - - int extensionCount = daoCollection.entityExtensionDAO().getExtensionCount(id, extensionPrefix); - int total = extensionCount + 1; - Paging paging = new Paging(); - paging.setOffset(offset); - paging.setLimit(limit); - paging.setTotal(total); + .getExtensionsWithOffset( + id, extensionPrefix, historyWindow.limit(), historyWindow.offset()), + () -> + daoCollection + .entityExtensionDAO() + .getExtensionsWithOffsetLegacy( + id, extensionPrefix, historyWindow.limit(), historyWindow.offset())); + } - EntityHistory entityHistory = - new EntityHistory().withEntityType(entityType).withVersions(versions).withPaging(paging); - return new EntityHistoryWithOffset(entityHistory, Math.min(offset + limit, total)); + private FieldChangedHistoryPage getFieldChangedHistoryPage( + UUID id, String fieldChanged, HistoryWindow historyWindow) { + String extensionPrefix = EntityUtil.getVersionExtensionPrefix(entityType); + return readVersionHistory( + () -> getIndexedFieldChangedHistoryPage(id, extensionPrefix, fieldChanged, historyWindow), + () -> getLegacyFieldChangedHistoryPage(id, extensionPrefix, fieldChanged, historyWindow)); } - private EntityHistoryWithOffset listVersionsWithFieldFilter( - UUID id, T latest, int limit, int offset, String rawFieldChanged) { - if (hasVersionFieldMetadataColumns()) { - return listVersionsWithFieldFilterIndexed(id, latest, limit, offset, rawFieldChanged); + private FieldChangedHistoryPage getIndexedFieldChangedHistoryPage( + UUID id, String extensionPrefix, String fieldChanged, HistoryWindow historyWindow) { + int total = + daoCollection + .entityExtensionDAO() + .getExtensionCountByFieldChanged(id, extensionPrefix, fieldChanged); + if (!historyWindow.isInRange(total)) { + return new FieldChangedHistoryPage(total, List.of()); } - LOG.debug( - "Version history metadata columns are unavailable for entityType={}; using in-memory fieldChanged filtering", - entityType); - return listVersionsWithFieldFilterFallback(id, latest, limit, offset, rawFieldChanged); + List records = + daoCollection + .entityExtensionDAO() + .getExtensionsWithFieldChanged( + id, extensionPrefix, fieldChanged, historyWindow.limit(), historyWindow.offset()); + return new FieldChangedHistoryPage(total, records); } - private EntityHistoryWithOffset listVersionsWithFieldFilterIndexed( - UUID id, T latest, int limit, int offset, String rawFieldChanged) { - final List versions = new ArrayList<>(); - boolean latestMatches = latestVersionMatchesFieldChanged(latest, rawFieldChanged); - String extensionPrefix = EntityUtil.getVersionExtensionPrefix(entityType); - int matchingHistoricalVersionCount = + private FieldChangedHistoryPage getLegacyFieldChangedHistoryPage( + UUID id, String extensionPrefix, String fieldChanged, HistoryWindow historyWindow) { + int total = + daoCollection + .entityExtensionDAO() + .getExtensionCountByFieldChangedLegacy(id, extensionPrefix, fieldChanged); + if (!historyWindow.isInRange(total)) { + return new FieldChangedHistoryPage(total, List.of()); + } + List records = daoCollection .entityExtensionDAO() - .getExtensionCountByFieldChanged(id, extensionPrefix, rawFieldChanged); + .getExtensionsWithFieldChangedLegacy( + id, extensionPrefix, fieldChanged, historyWindow.limit(), historyWindow.offset()); + return new FieldChangedHistoryPage(total, records); + } - int historicalOffset = offset; - int historicalLimit = limit; - if (latestMatches) { - if (offset == 0) { - versions.add(JsonUtils.pojoToJson(latest)); - historicalLimit = limit - 1; + private void addExtensionRecords(List versions, List records) { + records.forEach(record -> versions.add(new EntityVersionPair(record).getEntityJson())); + } + + private R readVersionHistory(Supplier indexedRead, Supplier legacyRead) { + if (!shouldUseVersionFieldMetadata()) { + return legacyRead.get(); + } + try { + R result = indexedRead.get(); + markVersionFieldMetadataAvailable(); + return result; + } catch (RuntimeException e) { + if (!handleMissingVersionFieldMetadata(e)) { + throw e; } - historicalOffset = Math.max(0, offset - 1); + return legacyRead.get(); } + } - if (historicalLimit > 0 && historicalOffset < matchingHistoricalVersionCount) { - List records = - daoCollection - .entityExtensionDAO() - .getExtensionsWithFieldChanged( - id, extensionPrefix, rawFieldChanged, historicalLimit, historicalOffset); - records.forEach(record -> versions.add(new EntityVersionPair(record).getEntityJson())); - } + private boolean shouldUseVersionFieldMetadata() { + return versionFieldMetadataColumnsAvailable + || System.currentTimeMillis() >= nextVersionFieldMetadataRetryAt; + } + + private static void markVersionFieldMetadataAvailable() { + versionFieldMetadataColumnsAvailable = true; + nextVersionFieldMetadataRetryAt = 0L; + } - int total = matchingHistoricalVersionCount + (latestMatches ? 1 : 0); + private static void markVersionFieldMetadataUnavailable() { + versionFieldMetadataColumnsAvailable = false; + nextVersionFieldMetadataRetryAt = + System.currentTimeMillis() + VERSION_FIELD_METADATA_RETRY_MILLIS; + } + private EntityHistoryWithOffset buildEntityHistoryWithOffset( + List versions, int offset, int limit, int total) { Paging paging = new Paging(); paging.setOffset(offset); paging.setLimit(limit); paging.setTotal(total); - EntityHistory entityHistory = new EntityHistory().withEntityType(entityType).withVersions(versions).withPaging(paging); return new EntityHistoryWithOffset(entityHistory, Math.min(offset + limit, total)); } - private EntityHistoryWithOffset listVersionsWithFieldFilterFallback( - UUID id, T latest, int limit, int offset, String rawFieldChanged) { - final List versions = new ArrayList<>(); - boolean latestMatches = latestVersionMatchesFieldChanged(latest, rawFieldChanged); - String extensionPrefix = EntityUtil.getVersionExtensionPrefix(entityType); - List matchingHistoricalVersions = new ArrayList<>(); + private boolean handleMissingVersionFieldMetadata(RuntimeException e) { + if (!isMissingVersionFieldMetadataColumnException(e)) { + return false; + } + markVersionFieldMetadataUnavailable(); + return true; + } - daoCollection - .entityExtensionDAO() - .getExtensions(id, extensionPrefix) - .forEach( - record -> { - if (VersionFieldChangeUtil.matchesFieldChanged( - record.extensionJson(), rawFieldChanged)) { - matchingHistoricalVersions.add(new EntityVersionPair(record)); - } - }); - matchingHistoricalVersions.sort(EntityUtil.compareVersion.reversed()); + private boolean isMissingVersionFieldMetadataColumnException(Throwable throwable) { + SQLException sqlException = findSqlException(throwable); + return sqlException != null && isMissingColumnSqlState(sqlException.getSQLState()); + } - int historicalOffset = offset; - int historicalLimit = limit; - if (latestMatches) { - if (offset == 0) { - versions.add(JsonUtils.pojoToJson(latest)); - historicalLimit = limit - 1; + private static SQLException findSqlException(Throwable throwable) { + while (throwable != null) { + if (throwable instanceof SQLException sqlException) { + return sqlException; } - historicalOffset = Math.max(0, offset - 1); + throwable = throwable.getCause(); } - - if (historicalLimit > 0 && historicalOffset < matchingHistoricalVersions.size()) { - matchingHistoricalVersions.stream() - .skip(historicalOffset) - .limit(historicalLimit) - .forEach(version -> versions.add(version.getEntityJson())); - } - - int total = matchingHistoricalVersions.size() + (latestMatches ? 1 : 0); - - Paging paging = new Paging(); - paging.setOffset(offset); - paging.setLimit(limit); - paging.setTotal(total); - - EntityHistory entityHistory = - new EntityHistory().withEntityType(entityType).withVersions(versions).withPaging(paging); - return new EntityHistoryWithOffset(entityHistory, Math.min(offset + limit, total)); + return null; } - private boolean latestVersionMatchesFieldChanged(T latest, String fieldChanged) { - return VersionFieldChangeUtil.matchesFieldChanged(latest.getChangeDescription(), fieldChanged); + private static boolean isMissingColumnSqlState(String sqlState) { + return "42S22".equals(sqlState) || "42703".equals(sqlState); } - private boolean hasVersionFieldMetadataColumns() { - if (versionFieldMetadataColumnsAvailable) { - return true; + private record FieldChangedHistoryPage(int total, List records) {} + + private record HistoryWindow(int limit, int offset) { + private boolean hasItems() { + return limit > 0; + } + + private boolean isInRange(int total) { + return hasItems() && offset < total; } - versionFieldMetadataColumnsAvailable = - daoCollection.entityExtensionDAO().getVersionFieldMetadataColumnCount() >= 2; - return versionFieldMetadataColumnsAvailable; } private VersionFieldChangeUtil.VersionExtensionRecord buildVersionExtensionRecord( @@ -1444,66 +1484,98 @@ private VersionFieldChangeUtil.VersionExtensionRecord buildVersionExtensionRecor private void insertVersionExtensionRecord( VersionFieldChangeUtil.VersionExtensionRecord versionExtensionRecord) { - if (hasVersionFieldMetadataColumns()) { - daoCollection - .entityExtensionDAO() - .insertVersionExtension( - versionExtensionRecord.getId(), - versionExtensionRecord.getExtension(), - versionExtensionRecord.getJsonSchema(), - versionExtensionRecord.getJson(), - versionExtensionRecord.getVersionNum(), - versionExtensionRecord.getChangedFieldKeys()); - } else { - daoCollection - .entityExtensionDAO() - .insert( - versionExtensionRecord.getId(), - versionExtensionRecord.getExtension(), - versionExtensionRecord.getJsonSchema(), - versionExtensionRecord.getJson()); - } + writeVersionHistory( + () -> insertVersionExtensionWithMetadata(versionExtensionRecord), + () -> insertVersionExtensionLegacy(versionExtensionRecord)); } private void insertVersionExtensionRecords( List versionExtensionRecords) { - if (hasVersionFieldMetadataColumns()) { - daoCollection - .entityExtensionDAO() - .insertVersionExtensions( - versionExtensionRecords.stream() - .map(VersionFieldChangeUtil.VersionExtensionRecord::getId) - .toList(), - versionExtensionRecords.stream() - .map(VersionFieldChangeUtil.VersionExtensionRecord::getExtension) - .toList(), - entityType, - versionExtensionRecords.stream() - .map(VersionFieldChangeUtil.VersionExtensionRecord::getJson) - .toList(), - versionExtensionRecords.stream() - .map(VersionFieldChangeUtil.VersionExtensionRecord::getVersionNum) - .toList(), - versionExtensionRecords.stream() - .map(VersionFieldChangeUtil.VersionExtensionRecord::getChangedFieldKeys) - .toList()); - } else { - daoCollection - .entityExtensionDAO() - .insertMany( - versionExtensionRecords.stream() - .map(VersionFieldChangeUtil.VersionExtensionRecord::getId) - .toList(), - versionExtensionRecords.stream() - .map(VersionFieldChangeUtil.VersionExtensionRecord::getExtension) - .toList(), - entityType, - versionExtensionRecords.stream() - .map(VersionFieldChangeUtil.VersionExtensionRecord::getJson) - .toList()); + writeVersionHistory( + () -> insertVersionExtensionsWithMetadata(versionExtensionRecords), + () -> insertVersionExtensionsLegacy(versionExtensionRecords)); + } + + private void writeVersionHistory(Runnable indexedWrite, Runnable legacyWrite) { + if (!shouldUseVersionFieldMetadata()) { + legacyWrite.run(); + return; + } + try { + indexedWrite.run(); + markVersionFieldMetadataAvailable(); + } catch (RuntimeException e) { + if (!handleMissingVersionFieldMetadata(e)) { + throw e; + } + legacyWrite.run(); } } + private void insertVersionExtensionWithMetadata( + VersionFieldChangeUtil.VersionExtensionRecord versionExtensionRecord) { + daoCollection + .entityExtensionDAO() + .insertVersionExtension( + versionExtensionRecord.getId(), + versionExtensionRecord.getExtension(), + versionExtensionRecord.getJsonSchema(), + versionExtensionRecord.getJson(), + versionExtensionRecord.getVersionNum(), + versionExtensionRecord.getChangedFieldKeys()); + } + + private void insertVersionExtensionLegacy( + VersionFieldChangeUtil.VersionExtensionRecord versionExtensionRecord) { + daoCollection + .entityExtensionDAO() + .insert( + versionExtensionRecord.getId(), + versionExtensionRecord.getExtension(), + versionExtensionRecord.getJsonSchema(), + versionExtensionRecord.getJson()); + } + + private void insertVersionExtensionsWithMetadata( + List versionExtensionRecords) { + daoCollection + .entityExtensionDAO() + .insertVersionExtensions( + versionExtensionRecords.stream() + .map(VersionFieldChangeUtil.VersionExtensionRecord::getId) + .toList(), + versionExtensionRecords.stream() + .map(VersionFieldChangeUtil.VersionExtensionRecord::getExtension) + .toList(), + entityType, + versionExtensionRecords.stream() + .map(VersionFieldChangeUtil.VersionExtensionRecord::getJson) + .toList(), + versionExtensionRecords.stream() + .map(VersionFieldChangeUtil.VersionExtensionRecord::getVersionNum) + .toList(), + versionExtensionRecords.stream() + .map(VersionFieldChangeUtil.VersionExtensionRecord::getChangedFieldKeys) + .toList()); + } + + private void insertVersionExtensionsLegacy( + List versionExtensionRecords) { + daoCollection + .entityExtensionDAO() + .insertMany( + versionExtensionRecords.stream() + .map(VersionFieldChangeUtil.VersionExtensionRecord::getId) + .toList(), + versionExtensionRecords.stream() + .map(VersionFieldChangeUtil.VersionExtensionRecord::getExtension) + .toList(), + entityType, + versionExtensionRecords.stream() + .map(VersionFieldChangeUtil.VersionExtensionRecord::getJson) + .toList()); + } + public final ResultList listWithOffset( ListWithOffsetFunction> callable, Function countCallable, diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/util/VersionFieldChangeUtil.java b/openmetadata-service/src/main/java/org/openmetadata/service/util/VersionFieldChangeUtil.java index 4bc8f5c92352..cd8e84c2b161 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/util/VersionFieldChangeUtil.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/util/VersionFieldChangeUtil.java @@ -89,7 +89,7 @@ public static String getChangedFieldKeysJson(String entityJson) { private static void addFieldChangeKeys( Set fieldChangeKeys, List fieldChanges) { for (FieldChange fieldChange : fieldChanges) { - addFieldNameSuffixes(fieldChangeKeys, fieldChange.getName()); + addFieldName(fieldChangeKeys, fieldChange.getName()); } } @@ -101,21 +101,16 @@ private static void addFieldChangeKeys(Set fieldChangeKeys, JsonNode fie for (JsonNode fieldChange : fieldChanges) { JsonNode fieldName = fieldChange.get(FIELD_NAME); if (fieldName != null && !fieldName.isNull()) { - addFieldNameSuffixes(fieldChangeKeys, fieldName.asText()); + addFieldName(fieldChangeKeys, fieldName.asText()); } } } - private static void addFieldNameSuffixes(Set fieldChangeKeys, String fieldName) { + private static void addFieldName(Set fieldChangeKeys, String fieldName) { if (nullOrEmpty(fieldName)) { return; } - - String[] pathParts = fieldName.split("\\."); - for (int index = 0; index < pathParts.length; index++) { - fieldChangeKeys.add( - String.join(".", java.util.Arrays.copyOfRange(pathParts, index, pathParts.length))); - } + fieldChangeKeys.add(fieldName); } @Getter @@ -142,20 +137,4 @@ public VersionExtensionRecord( this.changedFieldKeys = changedFieldKeys; } } - - @Getter - public static class VersionExtensionMetadata { - private final UUID id; - private final String extension; - private final Double versionNum; - private final String changedFieldKeys; - - public VersionExtensionMetadata( - UUID id, String extension, Double versionNum, String changedFieldKeys) { - this.id = id; - this.extension = extension; - this.versionNum = versionNum; - this.changedFieldKeys = changedFieldKeys; - } - } } diff --git a/openmetadata-service/src/test/java/org/openmetadata/service/migration/utils/MigrationSqlStatementHashTest.java b/openmetadata-service/src/test/java/org/openmetadata/service/migration/utils/MigrationSqlStatementHashTest.java index 985348d4dc85..26678d870673 100644 --- a/openmetadata-service/src/test/java/org/openmetadata/service/migration/utils/MigrationSqlStatementHashTest.java +++ b/openmetadata-service/src/test/java/org/openmetadata/service/migration/utils/MigrationSqlStatementHashTest.java @@ -1,5 +1,6 @@ package org.openmetadata.service.migration.utils; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -35,6 +36,17 @@ void mysql1130MigrationStatementsHaveUniqueHashesWithinEachFile() { "bootstrap/sql/migrations/native/1.13.0/mysql/postDataMigrationSQLScript.sql")); } + @Test + void mysql1130MigrationFilesDoNotQueryInformationSchema() throws Exception { + assertDoesNotReferenceInformationSchema( + resolveRepoRoot() + .resolve("bootstrap/sql/migrations/native/1.13.0/mysql/schemaChanges.sql")); + assertDoesNotReferenceInformationSchema( + resolveRepoRoot() + .resolve( + "bootstrap/sql/migrations/native/1.13.0/mysql/postDataMigrationSQLScript.sql")); + } + private void assertUniqueStatementHashes(Path sqlFile) { List statements = parseStatements(sqlFile); Map duplicateHashes = @@ -67,6 +79,12 @@ private List parseStatements(Path sqlFile) { } } + private void assertDoesNotReferenceInformationSchema(Path sqlFile) throws Exception { + String sql = Files.readString(sqlFile, StandardCharsets.UTF_8).toLowerCase(); + assertFalse( + sql.contains("information_schema"), () -> sqlFile + " should not query INFORMATION_SCHEMA"); + } + private Path resolveRepoRoot() { Path current = Paths.get("").toAbsolutePath(); while (current != null diff --git a/openmetadata-service/src/test/java/org/openmetadata/service/util/VersionFieldChangeUtilTest.java b/openmetadata-service/src/test/java/org/openmetadata/service/util/VersionFieldChangeUtilTest.java index ec5be8b94a80..42490873ca4e 100644 --- a/openmetadata-service/src/test/java/org/openmetadata/service/util/VersionFieldChangeUtilTest.java +++ b/openmetadata-service/src/test/java/org/openmetadata/service/util/VersionFieldChangeUtilTest.java @@ -13,7 +13,7 @@ class VersionFieldChangeUtilTest { @Test - void extractsSuffixKeysFromChangeDescription() { + void extractsExactFieldKeysFromChangeDescription() { ChangeDescription changeDescription = new ChangeDescription() .withFieldsUpdated( @@ -24,41 +24,40 @@ void extractsSuffixKeysFromChangeDescription() { Set fieldChangeKeys = VersionFieldChangeUtil.extractFieldChangeKeys(changeDescription); assertTrue(fieldChangeKeys.contains("columns.tags.tagFQN")); - assertTrue(fieldChangeKeys.contains("tags.tagFQN")); - assertTrue(fieldChangeKeys.contains("tagFQN")); assertTrue(fieldChangeKeys.contains("description")); + assertEquals(2, fieldChangeKeys.size()); } @Test - void serializesUniqueFieldChangeKeysPerSuffix() { + void serializesUniqueFieldChangeKeysExactlyOnce() { ChangeDescription changeDescription = new ChangeDescription() .withFieldsAdded( - java.util.List.of(new FieldChange().withName("schema.fields.description"))) + java.util.List.of(new FieldChange().withName("columns.name.description"))) .withFieldsUpdated( - java.util.List.of(new FieldChange().withName("schema.fields.description"))); + java.util.List.of(new FieldChange().withName("columns.name.description"))); var keys = JsonUtils.readObjects( VersionFieldChangeUtil.getChangedFieldKeysJson(changeDescription), String.class); - assertEquals(3, keys.size()); - assertTrue(keys.contains("schema.fields.description")); - assertTrue(keys.contains("fields.description")); - assertTrue(keys.contains("description")); + assertEquals(1, keys.size()); + assertTrue(keys.contains("columns.name.description")); } @Test - void matchesExactSuffixesButNotSubstrings() { + void matchesExactFieldNamesButNotNestedPathsOrSubstrings() { ChangeDescription changeDescription = new ChangeDescription() .withFieldsUpdated( - java.util.List.of(new FieldChange().withName("schema.fields.description"))); + java.util.List.of(new FieldChange().withName("columns.name.description"))); - assertTrue(VersionFieldChangeUtil.matchesFieldChanged(changeDescription, "description")); - assertTrue(VersionFieldChangeUtil.matchesFieldChanged(changeDescription, "fields.description")); + assertTrue( + VersionFieldChangeUtil.matchesFieldChanged(changeDescription, "columns.name.description")); + assertFalse(VersionFieldChangeUtil.matchesFieldChanged(changeDescription, "description")); + assertFalse( + VersionFieldChangeUtil.matchesFieldChanged(changeDescription, "columns.description")); assertFalse(VersionFieldChangeUtil.matchesFieldChanged(changeDescription, "script")); - assertFalse(VersionFieldChangeUtil.matchesFieldChanged(changeDescription, "fields")); } @Test @@ -70,10 +69,11 @@ void matchesFieldChangedFromSerializedEntityJson() { new ChangeDescription() .withFieldsUpdated( java.util.List.of( - new FieldChange().withName("schema.fields.description"))))); + new FieldChange().withName("columns.name.description"))))); - assertTrue(VersionFieldChangeUtil.matchesFieldChanged(entityJson, "description")); - assertTrue(VersionFieldChangeUtil.matchesFieldChanged(entityJson, "fields.description")); + assertTrue(VersionFieldChangeUtil.matchesFieldChanged(entityJson, "columns.name.description")); + assertFalse(VersionFieldChangeUtil.matchesFieldChanged(entityJson, "description")); + assertFalse(VersionFieldChangeUtil.matchesFieldChanged(entityJson, "columns.description")); assertFalse(VersionFieldChangeUtil.matchesFieldChanged(entityJson, "owners")); } } From 31d5e9cf907e9d3848089e9440ee0cd847a1015f Mon Sep 17 00:00:00 2001 From: mohitdeuex Date: Mon, 20 Apr 2026 22:37:01 +0530 Subject: [PATCH 15/20] chore(migrations): move version API schema additions from 1.13.0 to 1.12.7 Moves the PR's new entity_extension columns (versionNum, changedFieldKeys), indexes, and backfill scripts from the 1.13.0 migration directory into a new 1.12.7 directory. Keeps 1.13.0 identical to upstream main; only this PR's additions land in 1.12.7. Also updates MigrationSqlStatementHashTest to exercise the relocated files. --- .../mysql/postDataMigrationSQLScript.sql | 57 ++++++++++++++++++ .../native/1.12.7/mysql/schemaChanges.sql | 9 +++ .../postgres/postDataMigrationSQLScript.sql | 49 ++++++++++++++++ .../native/1.12.7/postgres/schemaChanges.sql | 11 ++++ .../mysql/postDataMigrationSQLScript.sql | 58 ------------------- .../native/1.13.0/mysql/schemaChanges.sql | 10 ---- .../postgres/postDataMigrationSQLScript.sql | 50 ---------------- .../native/1.13.0/postgres/schemaChanges.sql | 12 ---- .../utils/MigrationSqlStatementHashTest.java | 12 ++-- 9 files changed, 132 insertions(+), 136 deletions(-) create mode 100644 bootstrap/sql/migrations/native/1.12.7/mysql/postDataMigrationSQLScript.sql create mode 100644 bootstrap/sql/migrations/native/1.12.7/mysql/schemaChanges.sql create mode 100644 bootstrap/sql/migrations/native/1.12.7/postgres/postDataMigrationSQLScript.sql create mode 100644 bootstrap/sql/migrations/native/1.12.7/postgres/schemaChanges.sql diff --git a/bootstrap/sql/migrations/native/1.12.7/mysql/postDataMigrationSQLScript.sql b/bootstrap/sql/migrations/native/1.12.7/mysql/postDataMigrationSQLScript.sql new file mode 100644 index 000000000000..1958763f08fc --- /dev/null +++ b/bootstrap/sql/migrations/native/1.12.7/mysql/postDataMigrationSQLScript.sql @@ -0,0 +1,57 @@ +WITH +field_changes AS ( + SELECT e.id, e.extension, jt.field_name + FROM entity_extension AS e + JOIN JSON_TABLE( + COALESCE(JSON_EXTRACT(e.json, '$.changeDescription.fieldsAdded'), JSON_ARRAY()), + '$[*]' COLUMNS (field_name VARCHAR(1024) PATH '$.name') + ) AS jt ON TRUE + WHERE e.extension LIKE '%.version.%' + + UNION ALL + + SELECT e.id, e.extension, jt.field_name + FROM entity_extension AS e + JOIN JSON_TABLE( + COALESCE(JSON_EXTRACT(e.json, '$.changeDescription.fieldsUpdated'), JSON_ARRAY()), + '$[*]' COLUMNS (field_name VARCHAR(1024) PATH '$.name') + ) AS jt ON TRUE + WHERE e.extension LIKE '%.version.%' + + UNION ALL + + SELECT e.id, e.extension, jt.field_name + FROM entity_extension AS e + JOIN JSON_TABLE( + COALESCE(JSON_EXTRACT(e.json, '$.changeDescription.fieldsDeleted'), JSON_ARRAY()), + '$[*]' COLUMNS (field_name VARCHAR(1024) PATH '$.name') + ) AS jt ON TRUE + WHERE e.extension LIKE '%.version.%' +), +distinct_field_changes AS ( + SELECT DISTINCT id, extension, field_name + FROM field_changes + WHERE field_name IS NOT NULL + AND field_name <> '' +), +version_metadata AS ( + SELECT + e.id, + e.extension, + CAST(SUBSTRING_INDEX(e.extension, '.version.', -1) AS DOUBLE) AS version_num, + CASE + WHEN COUNT(fc.field_name) = 0 THEN JSON_ARRAY() + ELSE JSON_ARRAYAGG(fc.field_name) + END AS changed_field_keys + FROM entity_extension AS e + LEFT JOIN distinct_field_changes AS fc + ON fc.id = e.id + AND fc.extension = e.extension + WHERE e.extension LIKE '%.version.%' + GROUP BY e.id, e.extension +) +UPDATE entity_extension AS e +JOIN version_metadata AS vm + ON vm.id = e.id AND vm.extension = e.extension +SET e.versionNum = vm.version_num, + e.changedFieldKeys = vm.changed_field_keys; diff --git a/bootstrap/sql/migrations/native/1.12.7/mysql/schemaChanges.sql b/bootstrap/sql/migrations/native/1.12.7/mysql/schemaChanges.sql new file mode 100644 index 000000000000..0a96628fd32c --- /dev/null +++ b/bootstrap/sql/migrations/native/1.12.7/mysql/schemaChanges.sql @@ -0,0 +1,9 @@ +ALTER TABLE entity_extension + ADD COLUMN versionNum DOUBLE NULL, + ADD COLUMN changedFieldKeys JSON NULL; + +CREATE INDEX idx_entity_extension_version_order + ON entity_extension (id, versionNum); + +CREATE INDEX idx_entity_extension_changed_field_keys + ON entity_extension ((CAST(changedFieldKeys->'$' AS CHAR(512) ARRAY))); diff --git a/bootstrap/sql/migrations/native/1.12.7/postgres/postDataMigrationSQLScript.sql b/bootstrap/sql/migrations/native/1.12.7/postgres/postDataMigrationSQLScript.sql new file mode 100644 index 000000000000..dcaea9a5813c --- /dev/null +++ b/bootstrap/sql/migrations/native/1.12.7/postgres/postDataMigrationSQLScript.sql @@ -0,0 +1,49 @@ +WITH version_metadata AS ( + SELECT + e.id, + e.extension, + split_part(e.extension, '.version.', 2)::DOUBLE PRECISION AS version_num, + COALESCE( + ( + SELECT jsonb_agg(field_name ORDER BY field_name) + FROM ( + SELECT DISTINCT + field_change ->> 'name' AS field_name + FROM jsonb_array_elements( + COALESCE(e.json -> 'changeDescription' -> 'fieldsAdded', '[]'::jsonb) + ) AS field_change + WHERE field_change ->> 'name' IS NOT NULL + AND field_change ->> 'name' <> '' + + UNION + + SELECT DISTINCT + field_change ->> 'name' AS field_name + FROM jsonb_array_elements( + COALESCE(e.json -> 'changeDescription' -> 'fieldsUpdated', '[]'::jsonb) + ) AS field_change + WHERE field_change ->> 'name' IS NOT NULL + AND field_change ->> 'name' <> '' + + UNION + + SELECT DISTINCT + field_change ->> 'name' AS field_name + FROM jsonb_array_elements( + COALESCE(e.json -> 'changeDescription' -> 'fieldsDeleted', '[]'::jsonb) + ) AS field_change + WHERE field_change ->> 'name' IS NOT NULL + AND field_change ->> 'name' <> '' + ) AS exact_field_names + ), + '[]'::jsonb + ) AS changed_field_keys + FROM entity_extension AS e + WHERE e.extension LIKE '%.version.%' +) +UPDATE entity_extension AS e +SET versionNum = version_metadata.version_num, + changedFieldKeys = version_metadata.changed_field_keys +FROM version_metadata +WHERE e.id = version_metadata.id + AND e.extension = version_metadata.extension; diff --git a/bootstrap/sql/migrations/native/1.12.7/postgres/schemaChanges.sql b/bootstrap/sql/migrations/native/1.12.7/postgres/schemaChanges.sql new file mode 100644 index 000000000000..99bc7b7ff037 --- /dev/null +++ b/bootstrap/sql/migrations/native/1.12.7/postgres/schemaChanges.sql @@ -0,0 +1,11 @@ +ALTER TABLE entity_extension + ADD COLUMN IF NOT EXISTS versionNum DOUBLE PRECISION, + ADD COLUMN IF NOT EXISTS changedFieldKeys JSONB; + +CREATE INDEX IF NOT EXISTS idx_entity_extension_version_order + ON entity_extension (id, versionNum DESC) + WHERE versionNum IS NOT NULL; + +CREATE INDEX IF NOT EXISTS idx_entity_extension_changed_field_keys + ON entity_extension USING GIN (changedFieldKeys) + WHERE changedFieldKeys IS NOT NULL; diff --git a/bootstrap/sql/migrations/native/1.13.0/mysql/postDataMigrationSQLScript.sql b/bootstrap/sql/migrations/native/1.13.0/mysql/postDataMigrationSQLScript.sql index a7ce9aea08c9..7b7ceca2a1aa 100644 --- a/bootstrap/sql/migrations/native/1.13.0/mysql/postDataMigrationSQLScript.sql +++ b/bootstrap/sql/migrations/native/1.13.0/mysql/postDataMigrationSQLScript.sql @@ -3,64 +3,6 @@ SET json = JSON_REMOVE(json, '$.sourceConfig.config.computeMetrics') WHERE JSON_EXTRACT(json, '$.sourceConfig.config.computeMetrics') IS NOT NULL AND pipelineType = 'profiler'; -WITH -field_changes AS ( - SELECT e.id, e.extension, jt.field_name - FROM entity_extension AS e - JOIN JSON_TABLE( - COALESCE(JSON_EXTRACT(e.json, '$.changeDescription.fieldsAdded'), JSON_ARRAY()), - '$[*]' COLUMNS (field_name VARCHAR(1024) PATH '$.name') - ) AS jt ON TRUE - WHERE e.extension LIKE '%.version.%' - - UNION ALL - - SELECT e.id, e.extension, jt.field_name - FROM entity_extension AS e - JOIN JSON_TABLE( - COALESCE(JSON_EXTRACT(e.json, '$.changeDescription.fieldsUpdated'), JSON_ARRAY()), - '$[*]' COLUMNS (field_name VARCHAR(1024) PATH '$.name') - ) AS jt ON TRUE - WHERE e.extension LIKE '%.version.%' - - UNION ALL - - SELECT e.id, e.extension, jt.field_name - FROM entity_extension AS e - JOIN JSON_TABLE( - COALESCE(JSON_EXTRACT(e.json, '$.changeDescription.fieldsDeleted'), JSON_ARRAY()), - '$[*]' COLUMNS (field_name VARCHAR(1024) PATH '$.name') - ) AS jt ON TRUE - WHERE e.extension LIKE '%.version.%' -), -distinct_field_changes AS ( - SELECT DISTINCT id, extension, field_name - FROM field_changes - WHERE field_name IS NOT NULL - AND field_name <> '' -), -version_metadata AS ( - SELECT - e.id, - e.extension, - CAST(SUBSTRING_INDEX(e.extension, '.version.', -1) AS DOUBLE) AS version_num, - CASE - WHEN COUNT(fc.field_name) = 0 THEN JSON_ARRAY() - ELSE JSON_ARRAYAGG(fc.field_name) - END AS changed_field_keys - FROM entity_extension AS e - LEFT JOIN distinct_field_changes AS fc - ON fc.id = e.id - AND fc.extension = e.extension - WHERE e.extension LIKE '%.version.%' - GROUP BY e.id, e.extension -) -UPDATE entity_extension AS e -JOIN version_metadata AS vm - ON vm.id = e.id AND vm.extension = e.extension -SET e.versionNum = vm.version_num, - e.changedFieldKeys = vm.changed_field_keys; - -- Set randomizedSample to false where it was true (old default behavior) UPDATE ingestion_pipeline_entity SET json = JSON_SET(json, '$.sourceConfig.config.randomizedSample', false) diff --git a/bootstrap/sql/migrations/native/1.13.0/mysql/schemaChanges.sql b/bootstrap/sql/migrations/native/1.13.0/mysql/schemaChanges.sql index 960aeb100652..97b95dbcf802 100644 --- a/bootstrap/sql/migrations/native/1.13.0/mysql/schemaChanges.sql +++ b/bootstrap/sql/migrations/native/1.13.0/mysql/schemaChanges.sql @@ -22,16 +22,6 @@ SET json = JSON_SET( ) WHERE JSON_CONTAINS_PATH(json, 'one', '$.preview'); -ALTER TABLE entity_extension - ADD COLUMN versionNum DOUBLE NULL, - ADD COLUMN changedFieldKeys JSON NULL; - -CREATE INDEX idx_entity_extension_version_order - ON entity_extension (id, versionNum); - -CREATE INDEX idx_entity_extension_changed_field_keys - ON entity_extension ((CAST(changedFieldKeys->'$' AS CHAR(512) ARRAY))); - -- Reduce deadlocks for entity_usage upserts by making the unique key follow the lookup predicate -- (id, usageDate) instead of (usageDate, id). SET @migrate_usage_date_idx_sql := ( diff --git a/bootstrap/sql/migrations/native/1.13.0/postgres/postDataMigrationSQLScript.sql b/bootstrap/sql/migrations/native/1.13.0/postgres/postDataMigrationSQLScript.sql index 7bd38719f6d0..33bbd1dd5dfe 100644 --- a/bootstrap/sql/migrations/native/1.13.0/postgres/postDataMigrationSQLScript.sql +++ b/bootstrap/sql/migrations/native/1.13.0/postgres/postDataMigrationSQLScript.sql @@ -3,56 +3,6 @@ SET json = (json::jsonb #- '{sourceConfig,config,computeMetrics}')::json WHERE json::jsonb -> 'sourceConfig' -> 'config' -> 'computeMetrics' IS NOT NULL AND pipelineType = 'profiler'; -WITH version_metadata AS ( - SELECT - e.id, - e.extension, - split_part(e.extension, '.version.', 2)::DOUBLE PRECISION AS version_num, - COALESCE( - ( - SELECT jsonb_agg(field_name ORDER BY field_name) - FROM ( - SELECT DISTINCT - field_change ->> 'name' AS field_name - FROM jsonb_array_elements( - COALESCE(e.json -> 'changeDescription' -> 'fieldsAdded', '[]'::jsonb) - ) AS field_change - WHERE field_change ->> 'name' IS NOT NULL - AND field_change ->> 'name' <> '' - - UNION - - SELECT DISTINCT - field_change ->> 'name' AS field_name - FROM jsonb_array_elements( - COALESCE(e.json -> 'changeDescription' -> 'fieldsUpdated', '[]'::jsonb) - ) AS field_change - WHERE field_change ->> 'name' IS NOT NULL - AND field_change ->> 'name' <> '' - - UNION - - SELECT DISTINCT - field_change ->> 'name' AS field_name - FROM jsonb_array_elements( - COALESCE(e.json -> 'changeDescription' -> 'fieldsDeleted', '[]'::jsonb) - ) AS field_change - WHERE field_change ->> 'name' IS NOT NULL - AND field_change ->> 'name' <> '' - ) AS exact_field_names - ), - '[]'::jsonb - ) AS changed_field_keys - FROM entity_extension AS e - WHERE e.extension LIKE '%.version.%' -) -UPDATE entity_extension AS e -SET versionNum = version_metadata.version_num, - changedFieldKeys = version_metadata.changed_field_keys -FROM version_metadata -WHERE e.id = version_metadata.id - AND e.extension = version_metadata.extension; - -- Set randomizedSample to false where it was true (old default behavior) UPDATE ingestion_pipeline_entity SET json = jsonb_set(json::jsonb, '{sourceConfig,config,randomizedSample}', 'false'::jsonb)::json diff --git a/bootstrap/sql/migrations/native/1.13.0/postgres/schemaChanges.sql b/bootstrap/sql/migrations/native/1.13.0/postgres/schemaChanges.sql index 8a5bd8ce0f68..cfd3d9f74be6 100644 --- a/bootstrap/sql/migrations/native/1.13.0/postgres/schemaChanges.sql +++ b/bootstrap/sql/migrations/native/1.13.0/postgres/schemaChanges.sql @@ -22,18 +22,6 @@ SET json = (json - 'preview') || jsonb_build_object( ) WHERE jsonb_exists(json, 'preview'); -ALTER TABLE entity_extension - ADD COLUMN IF NOT EXISTS versionNum DOUBLE PRECISION, - ADD COLUMN IF NOT EXISTS changedFieldKeys JSONB; - -CREATE INDEX IF NOT EXISTS idx_entity_extension_version_order - ON entity_extension (id, versionNum DESC) - WHERE versionNum IS NOT NULL; - -CREATE INDEX IF NOT EXISTS idx_entity_extension_changed_field_keys - ON entity_extension USING GIN (changedFieldKeys) - WHERE changedFieldKeys IS NOT NULL; - -- Reduce deadlocks for entity_usage upserts by reordering the unique constraint columns -- to (id, usageDate) so that row-level locks follow the lookup predicate order. DO $$ diff --git a/openmetadata-service/src/test/java/org/openmetadata/service/migration/utils/MigrationSqlStatementHashTest.java b/openmetadata-service/src/test/java/org/openmetadata/service/migration/utils/MigrationSqlStatementHashTest.java index 26678d870673..d00aaa4b08bb 100644 --- a/openmetadata-service/src/test/java/org/openmetadata/service/migration/utils/MigrationSqlStatementHashTest.java +++ b/openmetadata-service/src/test/java/org/openmetadata/service/migration/utils/MigrationSqlStatementHashTest.java @@ -26,25 +26,25 @@ class MigrationSqlStatementHashTest { @Test - void mysql1130MigrationStatementsHaveUniqueHashesWithinEachFile() { + void mysql1127MigrationStatementsHaveUniqueHashesWithinEachFile() { assertUniqueStatementHashes( resolveRepoRoot() - .resolve("bootstrap/sql/migrations/native/1.13.0/mysql/schemaChanges.sql")); + .resolve("bootstrap/sql/migrations/native/1.12.7/mysql/schemaChanges.sql")); assertUniqueStatementHashes( resolveRepoRoot() .resolve( - "bootstrap/sql/migrations/native/1.13.0/mysql/postDataMigrationSQLScript.sql")); + "bootstrap/sql/migrations/native/1.12.7/mysql/postDataMigrationSQLScript.sql")); } @Test - void mysql1130MigrationFilesDoNotQueryInformationSchema() throws Exception { + void mysql1127MigrationFilesDoNotQueryInformationSchema() throws Exception { assertDoesNotReferenceInformationSchema( resolveRepoRoot() - .resolve("bootstrap/sql/migrations/native/1.13.0/mysql/schemaChanges.sql")); + .resolve("bootstrap/sql/migrations/native/1.12.7/mysql/schemaChanges.sql")); assertDoesNotReferenceInformationSchema( resolveRepoRoot() .resolve( - "bootstrap/sql/migrations/native/1.13.0/mysql/postDataMigrationSQLScript.sql")); + "bootstrap/sql/migrations/native/1.12.7/mysql/postDataMigrationSQLScript.sql")); } private void assertUniqueStatementHashes(Path sqlFile) { From e878b290d1ec4a6577613b7ab9168b8df72344f0 Mon Sep 17 00:00:00 2001 From: mohitdeuex Date: Thu, 23 Apr 2026 11:31:36 +0530 Subject: [PATCH 16/20] fix(versions): address CI failures and review feedback - testAPI.test.ts: update getTestCaseVersionList mock expectation to include the new params argument (APIClient.get is called with { params } since the function now supports limit/offset/fieldChanged). - PaginatedVersionHistory.spec.ts: replace banned networkidle waits and waitForSelector with web-first assertion on version-button visibility (satisfies playwright/no-networkidle and playwright/no-wait-for-selector). - EntityVersionTimeLine.tsx: implement infinite scroll via IntersectionObserver on a sentinel element at the bottom of the version list. Hooks up the onLoadMore/hasMore/isLoadingMore props that were in the interface but previously unused. - EntityVersionPage.component.tsx: fix stale-closure bugs in fetchMoreVersions (gitar-bot review). Use versionListRef for currentOffset and isLoadingMoreRef to gate concurrent invocations so IntersectionObserver double-firing does not cause duplicate appends. - EntityResource.java: accept offset > 0 with default limit when no fieldChanged is provided, so pagination params are no longer silently ignored (Copilot review). - datamodel_generation.py: raise explicit errors if generated files or expected replacement targets are missing, instead of silently succeeding when the generator output drifts (Copilot review). --- .../service/resources/EntityResource.java | 7 ++- .../PaginatedVersionHistory.spec.ts | 10 ++-- .../EntityVersionTimeLine.tsx | 53 +++++++++++++++++-- .../EntityVersionPage.component.tsx | 28 ++++++---- .../resources/ui/src/rest/testAPI.test.ts | 3 +- scripts/datamodel_generation.py | 9 ++++ 6 files changed, 91 insertions(+), 19 deletions(-) diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/EntityResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/EntityResource.java index 2c22ad43c3bf..98d1b405d5ab 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/EntityResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/EntityResource.java @@ -413,8 +413,11 @@ protected EntityHistory listVersionsInternal( .listVersionsWithOffset(id, effectiveLimit, offset, fieldChanged) .entityHistory(); } - if (limit > 0) { - return repository.listVersionsWithOffset(id, limit, offset, null).entityHistory(); + if (limit > 0 || offset > 0) { + int effectiveLimit = limit > 0 ? limit : DEFAULT_FIELD_CHANGED_VERSION_LIMIT; + return repository + .listVersionsWithOffset(id, effectiveLimit, offset, null) + .entityHistory(); } return repository.listVersions(id); } diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/VersionPages/PaginatedVersionHistory.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/VersionPages/PaginatedVersionHistory.spec.ts index df26e525c2cc..03aea4fe2572 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/VersionPages/PaginatedVersionHistory.spec.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/VersionPages/PaginatedVersionHistory.spec.ts @@ -73,8 +73,9 @@ test.describe('Paginated Version History', () => { const fqn = table.entityResponseData?.fullyQualifiedName; await page.goto(`/table/${fqn}`); - await page.waitForLoadState('networkidle'); - await page.waitForSelector('[data-testid="version-button"]'); + await expect( + page.locator('[data-testid="version-button"]') + ).toBeVisible(); const versionsApiCall = page.waitForResponse( (response) => @@ -162,8 +163,9 @@ test.describe('Paginated Version History', () => { ); await page.goto(`/table/${fqn}`); - await page.waitForLoadState('networkidle'); - await page.waitForSelector('[data-testid="version-button"]'); + await expect( + page.locator('[data-testid="version-button"]') + ).toBeVisible(); await page.locator('[data-testid="version-button"]').click(); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityVersionTimeLine/EntityVersionTimeLine.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityVersionTimeLine/EntityVersionTimeLine.tsx index 9b20d732694f..d45180a2e479 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityVersionTimeLine/EntityVersionTimeLine.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityVersionTimeLine/EntityVersionTimeLine.tsx @@ -10,10 +10,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Button, Col, Divider, Drawer, Row, Tooltip, Typography } from 'antd'; +import { Button, Col, Divider, Drawer, Row, Spin, Tooltip, Typography } from 'antd'; import classNames from 'classnames'; import { isEmpty, toString } from 'lodash'; -import { forwardRef, useEffect, useMemo } from 'react'; +import { forwardRef, useEffect, useMemo, useRef } from 'react'; import { useTranslation } from 'react-i18next'; import { Link } from 'react-router-dom'; import { useLimitStore } from '../../../context/LimitsProvider/useLimitsStore'; @@ -126,15 +126,41 @@ const EntityVersionTimeLine: React.FC = ({ versionHandler, onBack, entityType, + onLoadMore, + hasMore, + isLoadingMore, }) => { const { t } = useTranslation(); const { resourceLimit, getResourceLimit } = useLimitStore(); + const sentinelRef = useRef(null); useEffect(() => { entityType && getResourceLimit(entityType); }, [entityType]); + useEffect(() => { + const node = sentinelRef.current; + if ( + !node || + !onLoadMore || + !hasMore || + typeof IntersectionObserver === 'undefined' + ) { + return; + } + const observer = new IntersectionObserver((entries) => { + for (const entry of entries) { + if (entry.isIntersecting) { + onLoadMore(); + } + } + }); + observer.observe(node); + + return () => observer.disconnect(); + }, [onLoadMore, hasMore, isLoadingMore]); + const { configuredLimit: { maxVersions } = { maxVersions: -1 } } = resourceLimit[entityType ?? ''] ?? {}; @@ -162,6 +188,19 @@ const EntityVersionTimeLine: React.FC = ({ {versions?.map((v) => { return renderVersionButton(v, currentVersion, versionHandler); })} + {onLoadMore && hasMore ? ( +
+ {isLoadingMore ? ( +
+ +
+ ) : null} +
+ ) : null} {hiddenVersions?.length > 0 ? ( <> @@ -191,7 +230,15 @@ const EntityVersionTimeLine: React.FC = ({ ) : null} ); - }, [versionList, currentVersion, versionHandler]); + }, [ + versionList, + currentVersion, + versionHandler, + maxVersions, + onLoadMore, + hasMore, + isLoadingMore, + ]); return ( { const [isVersionLoading, setIsVersionLoading] = useState(true); const [hasMoreVersions, setHasMoreVersions] = useState(false); const [isLoadingMore, setIsLoadingMore] = useState(false); + const isLoadingMoreRef = useRef(false); + const versionListRef = useRef({} as EntityHistory); const versionFetcherRef = useRef< (params?: { limit?: number; offset?: number }) => Promise >(); + useEffect(() => { + versionListRef.current = versionList; + }, [versionList]); + const backHandler = useCallback( () => navigate(getEntityDetailsPath(entityType, decodedEntityFQN, tab)), [entityType, decodedEntityFQN, tab] @@ -485,12 +491,17 @@ const EntityVersionPage: FunctionComponent = () => { }, [entityType, decodedEntityFQN, viewVersionPermission]); const fetchMoreVersions = useCallback(async () => { - if (!versionFetcherRef.current || isLoadingMore || !hasMoreVersions) { + if ( + !versionFetcherRef.current || + isLoadingMoreRef.current || + !hasMoreVersions + ) { return; } + isLoadingMoreRef.current = true; setIsLoadingMore(true); try { - const currentOffset = versionList.versions?.length ?? 0; + const currentOffset = versionListRef.current.versions?.length ?? 0; const moreVersions = await versionFetcherRef.current({ limit: VERSION_PAGE_SIZE, offset: currentOffset, @@ -502,16 +513,15 @@ const EntityVersionPage: FunctionComponent = () => { moreVersions.paging ? totalLoaded < moreVersions.paging.total : false ); - setVersionList((prev) => { - return { - ...moreVersions, - versions: [...(prev.versions ?? []), ...appendedVersions], - }; - }); + setVersionList((prev) => ({ + ...moreVersions, + versions: [...(prev.versions ?? []), ...appendedVersions], + })); } finally { + isLoadingMoreRef.current = false; setIsLoadingMore(false); } - }, [isLoadingMore, hasMoreVersions, versionList]); + }, [hasMoreVersions]); const fetchCurrentVersion = useCallback( async (id: string) => { diff --git a/openmetadata-ui/src/main/resources/ui/src/rest/testAPI.test.ts b/openmetadata-ui/src/main/resources/ui/src/rest/testAPI.test.ts index 53de6da5e453..f92df3c7037d 100644 --- a/openmetadata-ui/src/main/resources/ui/src/rest/testAPI.test.ts +++ b/openmetadata-ui/src/main/resources/ui/src/rest/testAPI.test.ts @@ -451,7 +451,8 @@ describe('testAPI tests', () => { const result = await getTestCaseVersionList('test-case-id'); expect(mockGet).toHaveBeenCalledWith( - '/dataQuality/testCases/test-case-id/versions' + '/dataQuality/testCases/test-case-id/versions', + { params: undefined } ); expect(result).toEqual(mockVersions); }); diff --git a/scripts/datamodel_generation.py b/scripts/datamodel_generation.py index b90c66493368..bd44760bc024 100644 --- a/scripts/datamodel_generation.py +++ b/scripts/datamodel_generation.py @@ -81,9 +81,18 @@ } for file_path, replacements in DIRECT_IMPORT_FIXES.items(): + if not os.path.exists(file_path): + raise FileNotFoundError( + f"Expected generated file not found for DIRECT_IMPORT_FIXES: {file_path}" + ) with open(file_path, "r", encoding=UTF_8) as file_: content = file_.read() for old_value, new_value in replacements: + if old_value not in content: + raise RuntimeError( + f"DIRECT_IMPORT_FIXES replacement target {old_value!r} not found in {file_path}. " + "The generator output may have changed; update datamodel_generation.py." + ) content = content.replace(old_value, new_value) with open(file_path, "w", encoding=UTF_8) as file_: file_.write(content) From 357359c0f8506a0bec648f11c3676c4b41859544 Mon Sep 17 00:00:00 2001 From: mohitdeuex Date: Thu, 23 Apr 2026 12:15:53 +0530 Subject: [PATCH 17/20] fix(checkstyle): format Java, ESLint/Prettier on UI, relax datamodel_generation strict check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Java: spotless:apply on EntityResource.java (line-break formatting). - Python: relax datamodel_generation.py DIRECT_IMPORT_FIXES check — replacement targets are alternative forms the generator may or may not emit. Only require the final marker ('from .paging import Paging') is present after replacements; the prior strict per-target check broke 'make generate'. - UI lint: organize-imports, ESLint --fix, Prettier on all version-related files touched by the PR (resolves lint-src + lint-playwright CI checks). - EntityVersionTimeLine: guard IntersectionObserver effect with isLoadingMore so the observer is torn down while a fetch is in flight (Copilot review). - EntityVersionTimeline.test.tsx: add unit tests covering sentinel rendering conditions (hasMore, onLoadMore) and the isLoadingMore observer-guard (Copilot review). --- .../service/resources/EntityResource.java | 4 +- .../PaginatedVersionHistory.spec.ts | 49 ++++---- .../APIEndpointVersion/APIEndpointVersion.tsx | 6 +- .../ChartVersion/ChartVersion.component.tsx | 9 +- .../ContainerVersion.component.tsx | 6 +- .../DashboardVersion.component.tsx | 6 +- .../DataModelVersion.component.tsx | 6 +- .../ColumnDetailPanel.component.tsx | 21 ++-- .../ColumnDetailPanel.test.tsx | 12 +- .../KeyProfileMetrics.component.tsx | 3 +- .../StoredProcedureVersion.component.tsx | 6 +- .../TableVersion/TableVersion.component.tsx | 6 +- .../DirectoryVersion/DirectoryVersion.tsx | 6 +- .../File/FileVersion/FileVersion.tsx | 6 +- .../SpreadsheetVersion/SpreadsheetVersion.tsx | 6 +- .../WorksheetVersion/WorksheetVersion.tsx | 6 +- .../EntityVersionTimeLine.tsx | 12 +- .../EntityVersionTimeline.test.tsx | 112 +++++++++++++++++- .../Metric/MetricVersion/MetricVersion.tsx | 6 +- .../MlModelVersion.component.tsx | 9 +- .../PipelineVersion.component.tsx | 6 +- .../SearchIndexVersion/SearchIndexVersion.tsx | 6 +- .../IngestionListTable.test.tsx | 3 +- .../IngestionListTable/IngestionListTable.tsx | 3 +- .../IngestionRecentRuns.component.tsx | 6 +- .../TopicVersion/TopicVersion.component.tsx | 6 +- .../EntityVersionPage.component.tsx | 3 +- .../resources/ui/src/utils/CommonUtils.tsx | 9 +- scripts/datamodel_generation.py | 18 ++- 29 files changed, 211 insertions(+), 146 deletions(-) diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/EntityResource.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/EntityResource.java index 98d1b405d5ab..aceee3161f19 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/EntityResource.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/EntityResource.java @@ -415,9 +415,7 @@ protected EntityHistory listVersionsInternal( } if (limit > 0 || offset > 0) { int effectiveLimit = limit > 0 ? limit : DEFAULT_FIELD_CHANGED_VERSION_LIMIT; - return repository - .listVersionsWithOffset(id, effectiveLimit, offset, null) - .entityHistory(); + return repository.listVersionsWithOffset(id, effectiveLimit, offset, null).entityHistory(); } return repository.listVersions(id); } diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/VersionPages/PaginatedVersionHistory.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/VersionPages/PaginatedVersionHistory.spec.ts index 03aea4fe2572..cd3b5c543459 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/VersionPages/PaginatedVersionHistory.spec.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/VersionPages/PaginatedVersionHistory.spec.ts @@ -10,7 +10,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Page, expect, test as base } from '@playwright/test'; +import { expect, Page, test as base } from '@playwright/test'; import { TableClass } from '../../support/entity/TableClass'; import { UserClass } from '../../support/user/UserClass'; import { performAdminLogin } from '../../utils/admin'; @@ -29,32 +29,29 @@ const test = base.extend<{ page: Page }>({ }); test.describe('Paginated Version History', () => { - test.beforeAll( - 'Setup entity with versions', - async ({ browser }) => { - test.setTimeout(120_000); + test.beforeAll('Setup entity with versions', async ({ browser }) => { + test.setTimeout(120_000); - const { apiContext, afterAction } = await performAdminLogin(browser); + const { apiContext, afterAction } = await performAdminLogin(browser); - await adminUser.create(apiContext); - await adminUser.setAdminRole(apiContext); + await adminUser.create(apiContext); + await adminUser.setAdminRole(apiContext); - await table.create(apiContext); + await table.create(apiContext); - await table.patch({ - apiContext, - patchData: [ - { - op: 'add', - path: '/description', - value: 'Description for pagination test', - }, - ], - }); + await table.patch({ + apiContext, + patchData: [ + { + op: 'add', + path: '/description', + value: 'Description for pagination test', + }, + ], + }); - await afterAction(); - } - ); + await afterAction(); + }); test.afterAll('Cleanup', async ({ browser }) => { const { apiContext, afterAction } = await performAdminLogin(browser); @@ -73,9 +70,7 @@ test.describe('Paginated Version History', () => { const fqn = table.entityResponseData?.fullyQualifiedName; await page.goto(`/table/${fqn}`); - await expect( - page.locator('[data-testid="version-button"]') - ).toBeVisible(); + await expect(page.locator('[data-testid="version-button"]')).toBeVisible(); const versionsApiCall = page.waitForResponse( (response) => @@ -163,9 +158,7 @@ test.describe('Paginated Version History', () => { ); await page.goto(`/table/${fqn}`); - await expect( - page.locator('[data-testid="version-button"]') - ).toBeVisible(); + await expect(page.locator('[data-testid="version-button"]')).toBeVisible(); await page.locator('[data-testid="version-button"]').click(); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/APIEndpoint/APIEndpointVersion/APIEndpointVersion.tsx b/openmetadata-ui/src/main/resources/ui/src/components/APIEndpoint/APIEndpointVersion/APIEndpointVersion.tsx index 3273724ef031..440907defaf2 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/APIEndpoint/APIEndpointVersion/APIEndpointVersion.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/APIEndpoint/APIEndpointVersion/APIEndpointVersion.tsx @@ -144,8 +144,7 @@ const APIEndpointVersion: FC = ({
+ flex="220px"> = ({ data={currentVersionData} permissions={entityPermissions} type={EntityType.API_ENDPOINT} - onUpdate={() => Promise.resolve()} - > + onUpdate={() => Promise.resolve()}> = ({ + flex="220px"> = ({ return ( <>
+ className={`version-data ${deleted ? 'version-data--deleted' : ''}`}>
= ({ data={currentVersionData} permissions={entityPermissions} type={EntityType.CHART} - onUpdate={() => Promise.resolve()} - > + onUpdate={() => Promise.resolve()}> = ({ + flex="220px"> = ({ data={currentVersionData} permissions={entityPermissions} type={EntityType.CONTAINER as CustomizeEntityType} - onUpdate={() => Promise.resolve()} - > + onUpdate={() => Promise.resolve()}> = ({ + flex="220px"> = ({ data={currentVersionData} permissions={entityPermissions} type={EntityType.DASHBOARD} - onUpdate={() => Promise.resolve()} - > + onUpdate={() => Promise.resolve()}> = ({ + flex="220px"> = ({ data={currentVersionData} permissions={entityPermissions} type={EntityType.DASHBOARD_DATA_MODEL} - onUpdate={() => Promise.resolve()} - > + onUpdate={() => Promise.resolve()}> ({ return (
+ key={breadcrumb.fullyQualifiedName}>
({ isLastItem ? undefined : () => handleBreadcrumbClick(breadcrumb) - } - > + }> {getEntityName(breadcrumb)} {index < breadcrumbPath.length - 1 && ( @@ -853,13 +851,11 @@ export const ColumnDetailPanel = ({ mouseEnterDelay={0.5} placement="topLeft" title={getEntityName(activeColumn)} - trigger="hover" - > + trigger="hover"> + ellipsis={{ tooltip: true }}> {stringToHTML( (activeColumn as { displayName?: string }).displayName || activeColumn.name || @@ -898,8 +894,7 @@ export const ColumnDetailPanel = ({ + ellipsis={{ tooltip: true }}> {stringToHTML(activeColumn.name || '')} )} @@ -920,8 +915,7 @@ export const ColumnDetailPanel = ({ + trigger="hover">
{getDataTypeDisplay(activeColumn) || ''}
@@ -1010,8 +1004,7 @@ export const ColumnDetailPanel = ({ placement="right" title={columnTitle} width="40%" - onClose={onClose} - > + onClose={onClose}> {localToast.open && (
({ data-testid={props['data-testid'] || 'button'} disabled={disabled} onClick={onClick} - {...props} - > + {...props}> {children} )), @@ -143,8 +142,7 @@ jest.mock('../../common/DescriptionSection/DescriptionSection', () => ({ data-testid="update-description" onClick={async () => { await onDescriptionUpdate('Updated description'); - }} - > + }}> Update Description )} @@ -171,8 +169,7 @@ jest.mock('../../common/TagsSection/TagsSection', () => ({ } catch { // Error is handled by the component } - }} - > + }}> Update Tags )} @@ -199,8 +196,7 @@ jest.mock('../../common/GlossaryTermsSection/GlossaryTermsSection', () => ({ } catch { // Error is handled by the component } - }} - > + }}> Update Glossary Terms )} diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Database/ColumnDetailPanel/KeyProfileMetrics/KeyProfileMetrics.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Database/ColumnDetailPanel/KeyProfileMetrics/KeyProfileMetrics.component.tsx index 90a22ccabc13..ff30f2eafd31 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Database/ColumnDetailPanel/KeyProfileMetrics/KeyProfileMetrics.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Database/ColumnDetailPanel/KeyProfileMetrics/KeyProfileMetrics.component.tsx @@ -40,8 +40,7 @@ export const KeyProfileMetrics = ({ profile }: KeyProfileMetricsProps) => {
+ key={metric.label}>
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Database/StoredProcedureVersion/StoredProcedureVersion.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Database/StoredProcedureVersion/StoredProcedureVersion.component.tsx index 547ffabaef56..c2922e145537 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Database/StoredProcedureVersion/StoredProcedureVersion.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Database/StoredProcedureVersion/StoredProcedureVersion.component.tsx @@ -140,8 +140,7 @@ const StoredProcedureVersion = ({
+ flex="220px"> Promise.resolve()} - > + onUpdate={() => Promise.resolve()}> = ({ + flex="220px"> = ({ data={currentVersionData} permissions={entityPermissions} type={EntityType.TABLE} - onUpdate={() => Promise.resolve()} - > + onUpdate={() => Promise.resolve()}> + flex="220px"> Promise.resolve()} - > + onUpdate={() => Promise.resolve()}> + flex="220px"> Promise.resolve()} - > + onUpdate={() => Promise.resolve()}> + flex="220px"> Promise.resolve()} - > + onUpdate={() => Promise.resolve()}> + flex="220px"> Promise.resolve()} - > + onUpdate={() => Promise.resolve()}> = ({ !node || !onLoadMore || !hasMore || + isLoadingMore || typeof IntersectionObserver === 'undefined' ) { return; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityVersionTimeLine/EntityVersionTimeline.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityVersionTimeLine/EntityVersionTimeline.test.tsx index 6bdbb82598f3..db593bb1f8b4 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityVersionTimeLine/EntityVersionTimeline.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityVersionTimeLine/EntityVersionTimeline.test.tsx @@ -11,8 +11,12 @@ * limitations under the License. */ import { fireEvent, render, screen } from '@testing-library/react'; +import { MemoryRouter } from 'react-router-dom'; import { ChangeDescription } from '../../../generated/entity/type'; -import { VersionButton } from './EntityVersionTimeLine'; +import { EntityHistory } from '../../../generated/type/entityHistory'; +import EntityVersionTimeLine, { + VersionButton, +} from './EntityVersionTimeLine'; jest.mock('../../common/PopOverCard/UserPopOverCard', () => ({ __esModule: true, @@ -32,6 +36,20 @@ jest.mock('../../../utils/EntityVersionUtils', () => ({ __esModule: true, getSummary: jest.fn().mockReturnValue('Some change description'), isMajorVersion: jest.fn().mockReturnValue(false), + renderVersionButton: jest.fn((v) => ( +
+ {v.version} +
+ )), +})); + +jest.mock('../../../context/LimitsProvider/useLimitsStore', () => ({ + useLimitStore: jest.fn().mockImplementation(() => ({ + resourceLimit: {}, + getResourceLimit: jest.fn(), + })), })); describe('VersionButton', () => { @@ -105,3 +123,95 @@ describe('VersionButton', () => { expect(onVersionSelect).toHaveBeenCalledWith('1.0'); }); }); + +describe('EntityVersionTimeLine infinite scroll', () => { + const versionList: EntityHistory = { + entityType: 'table', + versions: [ + JSON.stringify({ version: 1.1, updatedBy: 'u', updatedAt: 1 }), + JSON.stringify({ version: 1.0, updatedBy: 'u', updatedAt: 0 }), + ], + }; + + let observerCallback: IntersectionObserverCallback | undefined; + + class MockIO implements IntersectionObserver { + readonly root = null; + readonly rootMargin = ''; + readonly thresholds = []; + constructor(cb: IntersectionObserverCallback) { + observerCallback = cb; + } + observe() { + // noop + } + unobserve() { + // noop + } + disconnect() { + observerCallback = undefined; + } + takeRecords(): IntersectionObserverEntry[] { + return []; + } + } + + beforeAll(() => { + ( + window as unknown as { IntersectionObserver: typeof IntersectionObserver } + ).IntersectionObserver = MockIO as unknown as typeof IntersectionObserver; + }); + + beforeEach(() => { + observerCallback = undefined; + }); + + const renderTimeline = ( + overrides: Partial[0]> = {} + ) => + render( + + + + ); + + it('does not render sentinel when hasMore is false', () => { + renderTimeline({ hasMore: false, onLoadMore: jest.fn() }); + + expect( + screen.queryByTestId('version-load-more-sentinel') + ).not.toBeInTheDocument(); + }); + + it('does not render sentinel when onLoadMore is not provided', () => { + renderTimeline({ hasMore: true }); + + expect( + screen.queryByTestId('version-load-more-sentinel') + ).not.toBeInTheDocument(); + }); + + it('renders sentinel when onLoadMore and hasMore are true', () => { + renderTimeline({ hasMore: true, onLoadMore: jest.fn() }); + + expect( + screen.getByTestId('version-load-more-sentinel') + ).toBeInTheDocument(); + }); + + it('does not register the observer while isLoadingMore is true', () => { + renderTimeline({ + hasMore: true, + isLoadingMore: true, + onLoadMore: jest.fn(), + }); + + expect(observerCallback).toBeUndefined(); + }); +}); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Metric/MetricVersion/MetricVersion.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Metric/MetricVersion/MetricVersion.tsx index 4870e8caf34a..0e79b859eeac 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Metric/MetricVersion/MetricVersion.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Metric/MetricVersion/MetricVersion.tsx @@ -148,8 +148,7 @@ const MetricVersion: FC = ({
+ flex="220px"> = ({ data={currentVersionData} permissions={entityPermissions} type={EntityType.METRIC as CustomizeEntityType} - onUpdate={() => Promise.resolve()} - > + onUpdate={() => Promise.resolve()}> = ({ bordered className="m-b-xlg" data-testid={`feature-card-${feature.name ?? ''}`} - key={feature.fullyQualifiedName} - > + key={feature.fullyQualifiedName}> @@ -301,8 +300,7 @@ const MlModelVersion: FC = ({ + flex="220px"> = ({ data={currentVersionData} permissions={entityPermissions} type={EntityType.MLMODEL} - onUpdate={() => Promise.resolve()} - > + onUpdate={() => Promise.resolve()}> = ({ + flex="220px"> = ({ data={currentVersionData} permissions={entityPermissions} type={EntityType.PIPELINE} - onUpdate={() => Promise.resolve()} - > + onUpdate={() => Promise.resolve()}> = ({ + flex="220px"> = ({ data={currentVersionData} permissions={entityPermissions} type={EntityType.SEARCH_INDEX} - onUpdate={() => Promise.resolve()} - > + onUpdate={() => Promise.resolve()}> id: 'id', name: 'name', state: 'waiting', - })} - > + })}> handleDeleteSelection diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Settings/Services/Ingestion/IngestionListTable/IngestionListTable.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Settings/Services/Ingestion/IngestionListTable/IngestionListTable.tsx index 7dda30a90d07..78d5f8cf2683 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Settings/Services/Ingestion/IngestionListTable/IngestionListTable.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Settings/Services/Ingestion/IngestionListTable/IngestionListTable.tsx @@ -442,8 +442,7 @@ function IngestionListTable({ <>
+ data-testid="ingestion-table">
handleRunStatusClick(r)} - > + onClick={() => handleRunStatusClick(r)}> {i === recentRunStatus.length - 1 ? upperFirst(pipelineState) : ''} @@ -169,8 +168,7 @@ export const IngestionRecentRuns = < )} } - key={`${runId}-timestamp`} - > + key={`${runId}-timestamp`}> {status} ) : ( diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Topic/TopicVersion/TopicVersion.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Topic/TopicVersion/TopicVersion.component.tsx index 2afce360b256..76534e5091bf 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Topic/TopicVersion/TopicVersion.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Topic/TopicVersion/TopicVersion.component.tsx @@ -164,8 +164,7 @@ const TopicVersion: FC = ({ + flex="220px"> = ({ data={currentVersionData} permissions={entityPermissions} type={EntityType.TOPIC} - onUpdate={() => Promise.resolve()} - > + onUpdate={() => Promise.resolve()}> { className="version-page-container" pageTitle={t('label.entity-detail-plural', { entity: getEntityName(currentVersionData), - })} - > + })}> {versionComponent()} ); diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/CommonUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/CommonUtils.tsx index a1dfcbb3dd34..0ffebc873e1e 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/CommonUtils.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/utils/CommonUtils.tsx @@ -215,13 +215,11 @@ export const getCountBadge = ( 'p-x-xss m-x-xss global-border rounded-4 text-center', clsBG, className - )} - > + )}> + title={count.toString()}> {count} @@ -330,8 +328,7 @@ export const errorMsg = (value: string) => {
+ data-testid="error-message"> {value}
diff --git a/scripts/datamodel_generation.py b/scripts/datamodel_generation.py index bd44760bc024..10778c8a737f 100644 --- a/scripts/datamodel_generation.py +++ b/scripts/datamodel_generation.py @@ -80,6 +80,12 @@ ], } +DIRECT_IMPORT_FIXES_EXPECTED = { + f"{ingestion_path}src/metadata/generated/schema/type/entityHistory.py": ( + "from .paging import Paging" + ), +} + for file_path, replacements in DIRECT_IMPORT_FIXES.items(): if not os.path.exists(file_path): raise FileNotFoundError( @@ -88,12 +94,14 @@ with open(file_path, "r", encoding=UTF_8) as file_: content = file_.read() for old_value, new_value in replacements: - if old_value not in content: - raise RuntimeError( - f"DIRECT_IMPORT_FIXES replacement target {old_value!r} not found in {file_path}. " - "The generator output may have changed; update datamodel_generation.py." - ) content = content.replace(old_value, new_value) + expected_marker = DIRECT_IMPORT_FIXES_EXPECTED.get(file_path) + if expected_marker is not None and expected_marker not in content: + raise RuntimeError( + f"DIRECT_IMPORT_FIXES produced no change in {file_path} " + f"(expected {expected_marker!r} to be present). " + "The generator output may have changed; update datamodel_generation.py." + ) with open(file_path, "w", encoding=UTF_8) as file_: file_.write(content) From bc5054f768a4850415443780919a8606bc47042e Mon Sep 17 00:00:00 2001 From: mohitdeuex Date: Thu, 23 Apr 2026 12:22:43 +0530 Subject: [PATCH 18/20] fix(ui-checkstyle): prettier+eslint on EntityVersionTimeline.test.tsx Collapse import line and reorder JSX props (callbacks last) per repo lint rules. Reruns ui-checkstyle-changed caught these in the new test file from the previous commit. --- .../EntityVersionTimeLine/EntityVersionTimeline.test.tsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityVersionTimeLine/EntityVersionTimeline.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityVersionTimeLine/EntityVersionTimeline.test.tsx index db593bb1f8b4..a3b1fab1bd85 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityVersionTimeLine/EntityVersionTimeline.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityVersionTimeLine/EntityVersionTimeline.test.tsx @@ -14,9 +14,7 @@ import { fireEvent, render, screen } from '@testing-library/react'; import { MemoryRouter } from 'react-router-dom'; import { ChangeDescription } from '../../../generated/entity/type'; import { EntityHistory } from '../../../generated/type/entityHistory'; -import EntityVersionTimeLine, { - VersionButton, -} from './EntityVersionTimeLine'; +import EntityVersionTimeLine, { VersionButton } from './EntityVersionTimeLine'; jest.mock('../../common/PopOverCard/UserPopOverCard', () => ({ __esModule: true, @@ -173,9 +171,9 @@ describe('EntityVersionTimeLine infinite scroll', () => { From 57b6b60e66a292942b8a86191dce06b64785ff9d Mon Sep 17 00:00:00 2001 From: mohitdeuex Date: Thu, 23 Apr 2026 13:33:25 +0530 Subject: [PATCH 19/20] test(playwright): address @aniketkatkar97 review on PaginatedVersionHistory spec - Add waitUntil: 'domcontentloaded' to every page.goto() call. - Wait for loaders (waitForAllLoadersToDisappear) before asserting the version-button to avoid racing the initial entity render. - Replace the manual { timeout: 15_000 } on versionSelectors.nth(1) with an explicit waitForResponse on the second paginated /versions call (offset > 0). This deterministically synchronises on the infinite-scroll fetch instead of a wall-clock timeout. --- .../PaginatedVersionHistory.spec.ts | 31 +++++++++++++------ 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/VersionPages/PaginatedVersionHistory.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/VersionPages/PaginatedVersionHistory.spec.ts index cd3b5c543459..ac516437a8c3 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/VersionPages/PaginatedVersionHistory.spec.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/VersionPages/PaginatedVersionHistory.spec.ts @@ -15,6 +15,7 @@ import { TableClass } from '../../support/entity/TableClass'; import { UserClass } from '../../support/user/UserClass'; import { performAdminLogin } from '../../utils/admin'; import { redirectToHomePage } from '../../utils/common'; +import { waitForAllLoadersToDisappear } from '../../utils/entity'; const table = new TableClass(); const adminUser = new UserClass(); @@ -69,8 +70,9 @@ test.describe('Paginated Version History', () => { const fqn = table.entityResponseData?.fullyQualifiedName; - await page.goto(`/table/${fqn}`); - await expect(page.locator('[data-testid="version-button"]')).toBeVisible(); + await page.goto(`/table/${fqn}`, { waitUntil: 'domcontentloaded' }); + await waitForAllLoadersToDisappear(page); + await expect(page.getByTestId('version-button')).toBeVisible(); const versionsApiCall = page.waitForResponse( (response) => @@ -79,7 +81,7 @@ test.describe('Paginated Version History', () => { response.status() === 200 ); - await page.locator('[data-testid="version-button"]').click(); + await page.getByTestId('version-button').click(); const response = await versionsApiCall; const responseBody = await response.json(); @@ -157,18 +159,29 @@ test.describe('Paginated Version History', () => { } ); - await page.goto(`/table/${fqn}`); - await expect(page.locator('[data-testid="version-button"]')).toBeVisible(); + await page.goto(`/table/${fqn}`, { waitUntil: 'domcontentloaded' }); + await waitForAllLoadersToDisappear(page); + await expect(page.getByTestId('version-button')).toBeVisible(); - await page.locator('[data-testid="version-button"]').click(); + // Await the second paginated call explicitly rather than relying on a + // manual timeout on the next-version locator — the infinite-scroll + // sentinel must trigger a fetch with offset>0 for this test to pass. + const secondPageCall = page.waitForResponse( + (response) => + response.url().includes(`${entityId}/versions`) && + response.url().includes('offset=') && + !response.url().includes('offset=0') && + response.status() === 200 + ); + + await page.getByTestId('version-button').click(); + await secondPageCall; - // The sentinel is immediately visible with only 1 version, so infinite scroll - // auto-triggers the second API call. Wait for both versions to render. const versionSelectors = page.locator( '[data-testid^="version-selector-v"]' ); - await expect(versionSelectors.nth(1)).toBeVisible({ timeout: 15_000 }); + await expect(versionSelectors.nth(1)).toBeVisible(); const totalCount = await versionSelectors.count(); From c4d8f6e795d841d0bda2e248c64bd470899b3153 Mon Sep 17 00:00:00 2001 From: mohitdeuex Date: Thu, 23 Apr 2026 14:00:01 +0530 Subject: [PATCH 20/20] =?UTF-8?q?fix:=20address=20Copilot=20review=20?= =?UTF-8?q?=E2=80=94=20one-shot=20observer=20+=20local=20SQL=20splitter?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. EntityVersionTimeLine.tsx: call observer.unobserve(entry.target) as soon as the sentinel first intersects so onLoadMore fires only once per attached observer. The effect reattaches a fresh observer after isLoadingMore flips back to false, so subsequent pages still load — we just no longer rely on the parent's in-flight ref as the sole stopgap against repeated fires for the same page. 2. MigrationSqlStatementHashTest.java: replace Flyway's non-public org.flywaydb.core.internal.* parser classes with a small, local SQL statement splitter. Handles line (--) and block comments, single-, double-, and backtick-quoted strings, backslash escapes, and doubled- quote escapes. Removes a brittle dependency on Flyway internals that could break on upgrades. Tested: - mvn test -pl openmetadata-service -Dtest=MigrationSqlStatementHashTest → 2 tests pass. - yarn test EntityVersionTimeline.test.tsx → 8/8 tests pass. --- .../utils/MigrationSqlStatementHashTest.java | 94 +++++++++++++++---- .../EntityVersionTimeLine.tsx | 5 + 2 files changed, 81 insertions(+), 18 deletions(-) diff --git a/openmetadata-service/src/test/java/org/openmetadata/service/migration/utils/MigrationSqlStatementHashTest.java b/openmetadata-service/src/test/java/org/openmetadata/service/migration/utils/MigrationSqlStatementHashTest.java index d00aaa4b08bb..185d6403e5f4 100644 --- a/openmetadata-service/src/test/java/org/openmetadata/service/migration/utils/MigrationSqlStatementHashTest.java +++ b/openmetadata-service/src/test/java/org/openmetadata/service/migration/utils/MigrationSqlStatementHashTest.java @@ -13,13 +13,6 @@ import java.util.List; import java.util.Map; import java.util.stream.Collectors; -import org.flywaydb.core.api.configuration.ClassicConfiguration; -import org.flywaydb.core.api.configuration.Configuration; -import org.flywaydb.core.internal.parser.Parser; -import org.flywaydb.core.internal.parser.ParsingContext; -import org.flywaydb.core.internal.resource.filesystem.FileSystemResource; -import org.flywaydb.core.internal.sqlscript.SqlStatementIterator; -import org.flywaydb.database.mysql.MySQLParser; import org.junit.jupiter.api.Test; import org.openmetadata.service.util.EntityUtil; @@ -63,19 +56,84 @@ private void assertUniqueStatementHashes(Path sqlFile) { () -> "Duplicate migration statement hashes found in " + sqlFile + ": " + duplicateHashes); } + // Splits a SQL script into top-level statements on unquoted, uncommented semicolons. + // Handles line comments, block comments, single- and double-quoted string literals + // (with doubled-quote escapes), backtick-quoted MySQL identifiers, and backslash- + // escaped characters inside strings. Avoids depending on Flyway's internal parser APIs. private List parseStatements(Path sqlFile) { - Configuration configuration = new ClassicConfiguration(); - Parser parser = new MySQLParser(configuration, new ParsingContext()); - try (SqlStatementIterator iterator = - parser.parse( - new FileSystemResource(null, sqlFile.toString(), StandardCharsets.UTF_8, true))) { - List statements = new ArrayList<>(); - while (iterator.hasNext()) { - statements.add(iterator.next().getSql()); - } - return statements; + String sql; + try { + sql = Files.readString(sqlFile, StandardCharsets.UTF_8); } catch (Exception e) { - throw new RuntimeException("Failed to parse migration SQL file " + sqlFile, e); + throw new RuntimeException("Failed to read migration SQL file " + sqlFile, e); + } + + List statements = new ArrayList<>(); + StringBuilder current = new StringBuilder(); + int i = 0; + int n = sql.length(); + char quote = 0; + + while (i < n) { + char c = sql.charAt(i); + char next = i + 1 < n ? sql.charAt(i + 1) : '\0'; + + if (quote == 0) { + if (c == '-' && next == '-') { + while (i < n && sql.charAt(i) != '\n') { + i++; + } + continue; + } + if (c == '/' && next == '*') { + i += 2; + while (i + 1 < n && !(sql.charAt(i) == '*' && sql.charAt(i + 1) == '/')) { + i++; + } + i = Math.min(n, i + 2); + continue; + } + if (c == '\'' || c == '"' || c == '`') { + quote = c; + current.append(c); + i++; + continue; + } + if (c == ';') { + addIfNotBlank(statements, current.toString()); + current.setLength(0); + i++; + continue; + } + current.append(c); + i++; + } else { + if (c == '\\' && next != '\0') { + current.append(c).append(next); + i += 2; + continue; + } + if (c == quote && next == quote) { + current.append(c).append(next); + i += 2; + continue; + } + if (c == quote) { + quote = 0; + } + current.append(c); + i++; + } + } + + addIfNotBlank(statements, current.toString()); + return statements; + } + + private void addIfNotBlank(List statements, String statement) { + String trimmed = statement.trim(); + if (!trimmed.isEmpty()) { + statements.add(trimmed); } } diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityVersionTimeLine/EntityVersionTimeLine.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityVersionTimeLine/EntityVersionTimeLine.tsx index 002697b74640..bb310ac2c44a 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityVersionTimeLine/EntityVersionTimeLine.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityVersionTimeLine/EntityVersionTimeLine.tsx @@ -162,6 +162,11 @@ const EntityVersionTimeLine: React.FC = ({ const observer = new IntersectionObserver((entries) => { for (const entry of entries) { if (entry.isIntersecting) { + // Stop observing after the first intersect so a sentinel that stays + // in view does not fire onLoadMore repeatedly for the same page. + // The effect re-runs when isLoadingMore flips back to false and a + // fresh observer is attached for the next page. + observer.unobserve(entry.target); onLoadMore(); } }