Rules library (#24748)

* Add DQ Rules Library

* Add DQ Rules Library - Add Tests and enable testDefinitions through APIs to list

* Add DQ Rules Library - Add Tests and enable testDefinitions through APIs to list

* Add DQ Rules Library - Add Tests and enable testDefinitions through APIs to list

* Update generated TypeScript types

* Add DQ Rules Library - Add Tests and enable testDefinitions through APIs to list

* Add DQ Rules Library - Add Tests and enable testDefinitions through APIs to list

* Update generated TypeScript types

* Refactor tests to use toStrictEqual for string comparisons and improve consistency

- Updated various test files to replace `toBe` with `toStrictEqual` for string assertions in ImportStatus, SummaryCard, TabsLabel, and others.
- Enhanced regex tests to ensure accurate validation of entity names and tags.
- Added new translations for test platform warnings in en-us.json.
- Improved utility tests for alerts, authentication, CSV handling, and task messages to use `toEqual` for better clarity.

* Refactor TestDefinitionForm and TestDefinitionList components to use updated API methods and improve SQL expression handling

* Enhance TestDefinitionList component with permission checks for edit and delete actions, and update tests to reflect changes in permission handling

* Remove debug log from handleSubmit in TestDefinitionForm component

* Add permission loading state and enhance permission handling in TestDefinitionList component

* Update generated TypeScript types

* Update generated TypeScript types

* Update generated TypeScript types

* fix build failure

* Revert "Update generated TypeScript types"

This reverts commit 67b062216f.

* Enhance TestDefinitionForm and TestDefinitionList components with improved UI and pagination handling

* fix: update RulesLibrary tests and enhance TestDefinitionForm styling

* fix: Enhance TestDefinitionForm with error handling and improved UX

* fix: Update test definition handling and improve rendering in TestDefinitionList

* fix: Refactor TestDefinitionPermissions tests for improved permission checks and API context handling

* fix: Update system test definition retrieval to use findLast for improved accuracy

* feat: Add end-to-end tests for Rules Library and Test Definition Permissions

* fix: Update edit button visibility check to use beDisabled for better clarity

* fix: Refactor response handling in TestDefinitionPermissions tests for improved reliability

* move migrations execution order

* fix: remove existing columns

* style: remove migration extra line break

* chore: fix migration

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Shailesh Parmar <shailesh.parmar.webdev@gmail.com>
Co-authored-by: TeddyCr <teddy.crepineau@gmail.com>
This commit is contained in:
Sriharsha Chintalapani 2026-01-13 23:12:30 -08:00 committed by GitHub
parent f5cf3190c4
commit 69ef1371bc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
74 changed files with 4077 additions and 343 deletions

View file

@ -30,3 +30,23 @@ CREATE TABLE IF NOT EXISTS audit_log_event (
KEY idx_audit_log_service_name_ts (service_name, event_ts DESC),
KEY idx_audit_log_created_at (created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
-- Add enabled field to test_definition table for Rules Library feature
-- This allows administrators to enable/disable test definitions in the rules library
-- Add virtual column for enabled field
-- CAST is needed to convert JSON boolean (true/false) to TINYINT (1/0)
ALTER TABLE test_definition
ADD COLUMN enabled TINYINT(1)
GENERATED ALWAYS AS (COALESCE(CAST(json_extract(json, '$.enabled') AS UNSIGNED), 1))
VIRTUAL;
-- Add index for filtering enabled/disabled test definitions
CREATE INDEX idx_test_definition_enabled ON test_definition(enabled);
-- Set all existing test definitions to enabled by default
UPDATE test_definition
SET json = JSON_SET(json, '$.enabled', true)
WHERE json_extract(json, '$.enabled') IS NULL;

View file

@ -23,6 +23,29 @@ CREATE TABLE IF NOT EXISTS audit_log_event (
created_at BIGINT DEFAULT (EXTRACT(EPOCH FROM NOW()) * 1000)::BIGINT
);
-- Add indexes for efficient filtering
CREATE INDEX IF NOT EXISTS idx_audit_log_actor_type_ts ON audit_log_event (actor_type, event_ts DESC);
CREATE INDEX IF NOT EXISTS idx_audit_log_service_name_ts ON audit_log_event (service_name, event_ts DESC);
CREATE INDEX IF NOT EXISTS idx_audit_log_created_at ON audit_log_event (created_at);
-- Add enabled field to test_definition table for Rules Library feature
-- This allows administrators to enable/disable test definitions in the rules library
-- Add generated column for enabled field with default true for existing rows
ALTER TABLE test_definition
ADD COLUMN IF NOT EXISTS enabled BOOLEAN GENERATED ALWAYS AS (
COALESCE((json ->> 'enabled')::boolean, true)
) STORED;
-- Add index for filtering enabled/disabled test definitions
CREATE INDEX IF NOT EXISTS idx_test_definition_enabled ON test_definition(enabled);
-- Set all existing test definitions to enabled by default
UPDATE test_definition
SET json = jsonb_set(json::jsonb, '{enabled}', 'true'::jsonb, true)::json
WHERE json ->> 'enabled' IS NULL;
CREATE UNIQUE INDEX IF NOT EXISTS idx_audit_log_event_change_event_id
ON audit_log_event (change_event_id);

View file

@ -6286,12 +6286,14 @@ public interface CollectionDAO {
String testPlatform = filter.getQueryParam("testPlatform");
String supportedDataType = filter.getQueryParam("supportedDataType");
String supportedService = filter.getQueryParam("supportedService");
String enabled = filter.getQueryParam("enabled");
String condition = filter.getCondition();
if (entityType == null
&& testPlatform == null
&& supportedDataType == null
&& supportedService == null) {
&& supportedService == null
&& enabled == null) {
return EntityDAO.super.listBefore(filter, limit, beforeName, beforeId);
}
@ -6331,6 +6333,12 @@ public interface CollectionDAO {
+ "OR json->>'supportedServices' LIKE :supportedServiceLike) ");
}
if (enabled != null) {
String enabledValue = Boolean.parseBoolean(enabled) ? "TRUE" : "FALSE";
mysqlCondition.append("AND enabled=" + enabledValue + " ");
psqlCondition.append("AND enabled=" + enabledValue + " ");
}
return listBefore(
getTableName(),
filter.getQueryParams(),
@ -6347,12 +6355,14 @@ public interface CollectionDAO {
String testPlatform = filter.getQueryParam("testPlatform");
String supportedDataType = filter.getQueryParam("supportedDataType");
String supportedService = filter.getQueryParam("supportedService");
String enabled = filter.getQueryParam("enabled");
String condition = filter.getCondition();
if (entityType == null
&& testPlatform == null
&& supportedDataType == null
&& supportedService == null) {
&& supportedService == null
&& enabled == null) {
return EntityDAO.super.listAfter(filter, limit, afterName, afterId);
}
@ -6392,6 +6402,12 @@ public interface CollectionDAO {
+ "OR json->>'supportedServices' LIKE :supportedServiceLike) ");
}
if (enabled != null) {
String enabledValue = Boolean.parseBoolean(enabled) ? "TRUE" : "FALSE";
mysqlCondition.append("AND enabled=" + enabledValue + " ");
psqlCondition.append("AND enabled=" + enabledValue + " ");
}
return listAfter(
getTableName(),
filter.getQueryParams(),
@ -6408,12 +6424,14 @@ public interface CollectionDAO {
String testPlatform = filter.getQueryParam("testPlatform");
String supportedDataType = filter.getQueryParam("supportedDataType");
String supportedService = filter.getQueryParam("supportedService");
String enabled = filter.getQueryParam("enabled");
String condition = filter.getCondition();
if (entityType == null
&& testPlatform == null
&& supportedDataType == null
&& supportedService == null) {
&& supportedService == null
&& enabled == null) {
return EntityDAO.super.listCount(filter);
}
@ -6452,6 +6470,13 @@ public interface CollectionDAO {
+ "OR json->>'supportedServices' IS NULL "
+ "OR json->>'supportedServices' LIKE :supportedServiceLike) ");
}
if (enabled != null) {
String enabledValue = Boolean.parseBoolean(enabled) ? "TRUE" : "FALSE";
mysqlCondition.append("AND enabled=").append(enabledValue).append(" ");
psqlCondition.append("AND enabled=").append(enabledValue).append(" ");
}
return listCount(
getTableName(),
filter.getQueryParams(),

View file

@ -482,6 +482,14 @@ public class TestCaseRepository extends EntityRepository<TestCase> {
Entity.getEntity(test.getTestDefinition(), "", Include.NON_DELETED);
test.setTestDefinition(testDefinition.getEntityReference());
// Validate that the test definition is enabled (only for new test cases, not updates)
if (!update && Boolean.FALSE.equals(testDefinition.getEnabled())) {
throw new IllegalArgumentException(
String.format(
"Test definition '%s' is disabled and cannot be used to create test cases",
testDefinition.getName()));
}
validateTestParameters(
test.getParameterValues(),
testDefinition.getParameterDefinition(),

View file

@ -2,14 +2,19 @@ package org.openmetadata.service.jdbi3;
import static org.openmetadata.service.Entity.TEST_DEFINITION;
import jakarta.ws.rs.BadRequestException;
import lombok.extern.slf4j.Slf4j;
import org.jdbi.v3.sqlobject.transaction.Transaction;
import org.openmetadata.common.utils.CommonUtil;
import org.openmetadata.schema.tests.TestDefinition;
import org.openmetadata.schema.type.Include;
import org.openmetadata.schema.type.ProviderType;
import org.openmetadata.schema.type.change.ChangeSource;
import org.openmetadata.service.Entity;
import org.openmetadata.service.resources.dqtests.TestDefinitionResource;
import org.openmetadata.service.util.EntityUtil;
@Slf4j
public class TestDefinitionRepository extends EntityRepository<TestDefinition> {
public TestDefinitionRepository() {
super(
@ -37,6 +42,55 @@ public class TestDefinitionRepository extends EntityRepository<TestDefinition> {
if (CommonUtil.nullOrEmpty(entity.getTestPlatforms())) {
throw new IllegalArgumentException("testPlatforms must not be empty");
}
// Set enabled to true by default if not specified
if (entity.getEnabled() == null) {
entity.setEnabled(true);
}
// For updates to system test definitions, only allow changes to the enabled field
if (update && entity.getProvider() == ProviderType.SYSTEM) {
TestDefinition existing = find(entity.getId(), Include.ALL);
if (existing != null) {
validateSystemTestDefinitionUpdate(existing, entity);
}
}
}
private void validateSystemTestDefinitionUpdate(TestDefinition existing, TestDefinition updated) {
// Check if any field other than 'enabled' is being changed
if (!existing.getEntityType().equals(updated.getEntityType())) {
throw new BadRequestException(
"System test definitions cannot have their entity type modified");
}
if (!existing.getTestPlatforms().equals(updated.getTestPlatforms())) {
throw new BadRequestException(
"System test definitions cannot have their test platforms modified");
}
if (!CommonUtil.nullOrEmpty(existing.getSupportedDataTypes())
&& !existing.getSupportedDataTypes().equals(updated.getSupportedDataTypes())) {
throw new BadRequestException(
"System test definitions cannot have their supported data types modified");
}
if (!CommonUtil.nullOrEmpty(existing.getParameterDefinition())
&& !existing.getParameterDefinition().equals(updated.getParameterDefinition())) {
throw new BadRequestException(
"System test definitions cannot have their parameter definitions modified");
}
if (existing.getDataQualityDimension() != null
&& !existing.getDataQualityDimension().equals(updated.getDataQualityDimension())) {
throw new BadRequestException(
"System test definitions cannot have their data quality dimension modified");
}
if (!CommonUtil.nullOrEmpty(existing.getSupportedServices())
&& !existing.getSupportedServices().equals(updated.getSupportedServices())) {
throw new BadRequestException(
"System test definitions cannot have their supported services modified");
}
if (existing.getSqlExpression() != null
&& !existing.getSqlExpression().equals(updated.getSqlExpression())) {
throw new BadRequestException(
"System test definitions cannot have their SQL expression modified");
}
}
@Override
@ -49,6 +103,15 @@ public class TestDefinitionRepository extends EntityRepository<TestDefinition> {
// No relationships to store beyond what is stored in the super class
}
@Override
protected void preDelete(TestDefinition entity, String deletedBy) {
// Prevent deletion of system test definitions
if (entity.getProvider() == ProviderType.SYSTEM) {
throw new BadRequestException(
"System test definitions cannot be deleted. They can only be disabled by setting enabled=false.");
}
}
@Override
public EntityRepository<TestDefinition>.EntityUpdater getUpdater(
TestDefinition original,
@ -67,13 +130,30 @@ public class TestDefinitionRepository extends EntityRepository<TestDefinition> {
@Transaction
@Override
public void entitySpecificUpdate(boolean consolidatingChanges) {
recordChange("testPlatforms", original.getTestPlatforms(), updated.getTestPlatforms());
recordChange(
"supportedDataTypes", original.getSupportedDataTypes(), updated.getSupportedDataTypes());
recordChange(
"parameterDefinition",
original.getParameterDefinition(),
updated.getParameterDefinition());
// For system test definitions, only allow enabled field changes
if (original.getProvider() == ProviderType.SYSTEM) {
// Only record enabled field changes for system test definitions
recordChange("enabled", original.getEnabled(), updated.getEnabled());
} else {
// For user/automation test definitions, allow all changes
recordChange("testPlatforms", original.getTestPlatforms(), updated.getTestPlatforms());
recordChange(
"supportedDataTypes",
original.getSupportedDataTypes(),
updated.getSupportedDataTypes());
recordChange(
"parameterDefinition",
original.getParameterDefinition(),
updated.getParameterDefinition());
recordChange("enabled", original.getEnabled(), updated.getEnabled());
recordChange(
"dataQualityDimension",
original.getDataQualityDimension(),
updated.getDataQualityDimension());
recordChange(
"supportedServices", original.getSupportedServices(), updated.getSupportedServices());
recordChange("sqlExpression", original.getSqlExpression(), updated.getSqlExpression());
}
}
}
}

View file

@ -155,7 +155,13 @@ public class TestDefinitionResource
"Filter test definitions by supported service. Returns test definitions that either "
+ "have an empty supportedServices list (supporting all services) or include the specified service.")
@QueryParam("supportedService")
String supportedServiceParam) {
String supportedServiceParam,
@Parameter(
description =
"Filter by enabled status (true/false). If not specified, returns all test definitions.",
schema = @Schema(type = "boolean"))
@QueryParam("enabled")
Boolean enabledParam) {
ListFilter filter = new ListFilter(include);
if (entityType != null) {
filter.addQueryParam("entityType", entityType);
@ -169,6 +175,9 @@ public class TestDefinitionResource
if (supportedServiceParam != null) {
filter.addQueryParam("supportedService", supportedServiceParam);
}
if (enabledParam != null) {
filter.addQueryParam("enabled", String.valueOf(enabledParam));
}
return super.listInternal(
uriInfo, securityContext, fieldsParam, filter, limitParam, before, after);
}

View file

@ -30,6 +30,7 @@
],
"supportsDynamicAssertion": true,
"provider": "system",
"enabled": true,
"dataQualityDimension": "Accuracy"
}

View file

@ -30,6 +30,7 @@
],
"supportsDynamicAssertion": true,
"provider": "system",
"enabled": true,
"dataQualityDimension": "Accuracy"
}

View file

@ -30,6 +30,7 @@
],
"supportsDynamicAssertion": true,
"provider": "system",
"enabled": true,
"dataQualityDimension": "Accuracy"
}

View file

@ -30,6 +30,7 @@
],
"supportsDynamicAssertion": true,
"provider": "system",
"enabled": true,
"dataQualityDimension": "Accuracy"
}

View file

@ -30,6 +30,7 @@
],
"supportsDynamicAssertion": true,
"provider": "system",
"enabled": true,
"dataQualityDimension": "Accuracy"
}

View file

@ -39,6 +39,7 @@
],
"supportsRowLevelPassedFailed": false,
"provider": "system",
"enabled": true,
"dataQualityDimension": "Accuracy"
}

View file

@ -31,5 +31,6 @@
"supportsRowLevelPassedFailed": true,
"supportsDynamicAssertion": true,
"provider": "system",
"enabled": true,
"dataQualityDimension": "Accuracy"
}

View file

@ -22,5 +22,6 @@
}
],
"provider": "system",
"enabled": true,
"dataQualityDimension": "Completeness"
}

View file

@ -30,6 +30,7 @@
],
"supportsDynamicAssertion": true,
"provider": "system",
"enabled": true,
"dataQualityDimension": "Accuracy"
}

View file

@ -31,5 +31,6 @@
"supportsRowLevelPassedFailed": true,
"supportsDynamicAssertion": true,
"provider": "system",
"enabled": true,
"dataQualityDimension": "Accuracy"
}

View file

@ -24,5 +24,6 @@
],
"supportsRowLevelPassedFailed": true,
"provider": "system",
"enabled": true,
"dataQualityDimension": "Validity"
}

View file

@ -17,5 +17,6 @@
],
"supportsRowLevelPassedFailed": true,
"provider": "system",
"enabled": true,
"dataQualityDimension": "Validity"
}

View file

@ -8,5 +8,6 @@
"supportedDataTypes": ["NUMBER","TINYINT","SMALLINT","INT","BIGINT","BYTEINT","BYTES","FLOAT","DOUBLE","DECIMAL","NUMERIC","TIMESTAMP","TIMESTAMPZ","TIME","DATE","DATETIME","INTERVAL","STRING","MEDIUMTEXT","TEXT","CHAR","VARCHAR","BOOLEAN","BINARY","VARBINARY","ARRAY","BLOB","LONGBLOB","MEDIUMBLOB","MAP","STRUCT","UNION","SET","GEOGRAPHY","ENUM","JSON","UUID","VARIANT","GEOMETRY","POINT","POLYGON","LOWCARDINALITY"],
"supportsRowLevelPassedFailed": true,
"provider": "system",
"enabled": true,
"dataQualityDimension": "Completeness"
}

View file

@ -51,5 +51,6 @@
],
"supportsRowLevelPassedFailed": true,
"provider": "system",
"enabled": true,
"dataQualityDimension": "Uniqueness"
}

View file

@ -17,5 +17,6 @@
],
"supportsRowLevelPassedFailed": true,
"provider": "system",
"enabled": true,
"dataQualityDimension": "Validity"
}

View file

@ -17,5 +17,6 @@
],
"supportsRowLevelPassedFailed": true,
"provider": "system",
"enabled": true,
"dataQualityDimension": "Validity"
}

View file

@ -28,5 +28,6 @@
}
],
"provider": "system",
"enabled": true,
"dataQualityDimension": "Integrity"
}

View file

@ -15,5 +15,6 @@
}
],
"provider": "system",
"enabled": true,
"dataQualityDimension": "Integrity"
}

View file

@ -15,6 +15,7 @@
}
],
"provider": "system",
"enabled": true,
"dataQualityDimension": "Integrity"
}

View file

@ -21,6 +21,7 @@
}
],
"provider": "system",
"enabled": true,
"dataQualityDimension": "Integrity"
}

View file

@ -46,6 +46,7 @@
],
"supportsRowLevelPassedFailed": true,
"provider": "system",
"enabled": true,
"dataQualityDimension": "SQL"
}

View file

@ -59,6 +59,7 @@
}
],
"provider": "system",
"enabled": true,
"dataQualityDimension": "Consistency",
"supportedServices": [
"Snowflake",

View file

@ -29,5 +29,6 @@
],
"supportsDynamicAssertion": true,
"provider": "system",
"enabled": true,
"dataQualityDimension": "Integrity"
}

View file

@ -15,5 +15,6 @@
}
],
"provider": "system",
"enabled": true,
"dataQualityDimension": "Integrity"
}

View file

@ -51,6 +51,7 @@
}
],
"provider": "system",
"enabled": true,
"dataQualityDimension": "Integrity"
}

View file

@ -59,6 +59,7 @@ import static org.openmetadata.service.util.TestUtils.dateToTimestamp;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.flipkart.zjsonpatch.JsonDiff;
import es.org.elasticsearch.client.Request;
import es.org.elasticsearch.client.Response;
import es.org.elasticsearch.client.RestClient;
@ -87,7 +88,6 @@ import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInfo;
import org.junit.jupiter.api.TestMethodOrder;
@ -100,7 +100,6 @@ import org.openmetadata.schema.api.policies.CreatePolicy;
import org.openmetadata.schema.api.teams.CreateRole;
import org.openmetadata.schema.api.teams.CreateTeam;
import org.openmetadata.schema.api.teams.CreateUser;
import org.openmetadata.schema.api.tests.CreateLogicalTestCases;
import org.openmetadata.schema.api.tests.CreateTestCase;
import org.openmetadata.schema.api.tests.CreateTestCaseResolutionStatus;
import org.openmetadata.schema.api.tests.CreateTestCaseResult;
@ -144,8 +143,6 @@ import org.openmetadata.schema.type.TableData;
import org.openmetadata.schema.type.TagLabel;
import org.openmetadata.schema.type.TaskStatus;
import org.openmetadata.schema.type.TestDefinitionEntityType;
import org.openmetadata.schema.type.csv.CsvImportResult;
import org.openmetadata.schema.utils.EntityInterfaceUtil;
import org.openmetadata.schema.utils.JsonUtils;
import org.openmetadata.schema.utils.ResultList;
import org.openmetadata.search.IndexMapping;
@ -163,7 +160,6 @@ import org.openmetadata.service.search.SearchIndexUtils;
import org.openmetadata.service.search.SearchRepository;
import org.openmetadata.service.search.indexes.TestCaseIndex;
import org.openmetadata.service.security.SecurityUtil;
import org.openmetadata.service.util.FullyQualifiedName;
import org.openmetadata.service.util.TestUtils;
import org.openmetadata.service.util.incidentSeverityClassifier.IncidentSeverityClassifierInterface;
import org.testcontainers.shaded.com.google.common.collect.ImmutableMap;
@ -5560,355 +5556,143 @@ public class TestCaseResourceTest extends EntityResourceTest<TestCase, CreateTes
}
@Test
@Tag("ImportExport")
void testImportExport_TableLevel(TestInfo test) throws IOException {
// Create a table with columns
TableResourceTest tableResourceTest = new TableResourceTest();
Column c1 = new Column().withName("c1").withDataType(BIGINT);
Column c2 = new Column().withName("c2").withDataType(BIGINT);
CreateTable createTable =
tableResourceTest
.createRequest(test)
.withColumns(listOf(c1, c2))
.withTableConstraints(null);
Table table = tableResourceTest.createEntity(createTable, ADMIN_AUTH_HEADERS);
String tableFqn = table.getFullyQualifiedName();
void test_createTestCaseWithDisabledDefinition_400(TestInfo test) throws Exception {
// Get the test definition
TestDefinition testDefinition = TEST_DEFINITION1;
// Get test definitions by name
TestDefinition tableRowCountDef =
Entity.getEntityByName(
TEST_DEFINITION, "tableRowCountToBeBetween", "", Include.NON_DELETED);
TestDefinition columnUniquenessDef =
Entity.getEntityByName(TEST_DEFINITION, "columnValuesToBeUnique", "", Include.NON_DELETED);
// Disable the test definition using JSON Patch
String originalJson = JsonUtils.pojoToJson(testDefinition);
testDefinition.setEnabled(false);
String updatedJson = JsonUtils.pojoToJson(testDefinition);
// Create test cases for the table
CreateTestCase createTableTest =
createRequest(getEntityName(test, 1))
.withEntityLink(String.format("<#E::table::%s>", tableFqn))
.withTestDefinition(tableRowCountDef.getFullyQualifiedName())
.withDisplayName("Table Row Count Test")
.withParameterValues(
listOf(
new TestCaseParameterValue().withName("minValue").withValue("10"),
new TestCaseParameterValue().withName("maxValue").withValue("100")));
ObjectMapper mapper = new ObjectMapper();
JsonNode patch = JsonDiff.asJson(mapper.readTree(originalJson), mapper.readTree(updatedJson));
CreateTestCase createColumnTest =
createRequest(getEntityName(test, 2))
.withEntityLink(String.format("<#E::table::%s::columns::c1>", tableFqn))
.withTestDefinition(columnUniquenessDef.getFullyQualifiedName())
.withDisplayName("Column Uniqueness Test")
.withDescription("Column uniqueness test description");
WebTarget target =
getResource("dataQuality/testDefinitions").path(testDefinition.getId().toString());
TestUtils.patch(target, patch, TestDefinition.class, ADMIN_AUTH_HEADERS);
TestCase tableTest = createEntity(createTableTest, ADMIN_AUTH_HEADERS);
TestCase columnTest = createEntity(createColumnTest, ADMIN_AUTH_HEADERS);
// Try to create a test case with the disabled definition
CreateTestCase create = createRequest(getEntityName(test));
create.setEntityLink(TABLE_LINK);
create.setTestDefinition(testDefinition.getFullyQualifiedName());
// Export test cases for the table
String exportedCsv = exportCsv(tableFqn);
assertNotNull(exportedCsv);
assertTrue(exportedCsv.contains(tableTest.getName()));
assertTrue(exportedCsv.contains(columnTest.getName()));
// CSV quote-escapes values by doubling quotes, so "x" becomes ""x""
String csvEscapedTableFqn = tableFqn.replace("\"", "\"\"");
String csvEscapedColumnFqn = (tableFqn + ".c1").replace("\"", "\"\"");
assertTrue(exportedCsv.contains(csvEscapedTableFqn)); // Should contain table FQN
assertTrue(exportedCsv.contains(csvEscapedColumnFqn)); // Should contain column FQN
assertResponse(
() -> createEntity(create, ADMIN_AUTH_HEADERS),
BAD_REQUEST,
"Test definition '"
+ testDefinition.getName()
+ "' is disabled and cannot be used to create test cases");
// Verify CSV format uses FQN instead of EntityLink
assertFalse(exportedCsv.contains("<#E::table::")); // Should not contain EntityLink format
// Re-enable the test definition for cleanup using JSON Patch
TestDefinition disabled =
Entity.getEntity(Entity.TEST_DEFINITION, testDefinition.getId(), "", null);
originalJson = JsonUtils.pojoToJson(disabled);
disabled.setEnabled(true);
updatedJson = JsonUtils.pojoToJson(disabled);
// Modify the CSV and import it back
String modifiedCsv =
exportedCsv
.replace(tableTest.getDisplayName(), "Modified Table Test")
.replace(columnTest.getDescription(), "Modified column test description");
CsvImportResult result = importCsv(tableFqn, modifiedCsv, false);
assertEquals(org.openmetadata.schema.type.ApiStatus.SUCCESS, result.getStatus());
assertEquals(3, result.getNumberOfRowsPassed() + result.getNumberOfRowsFailed());
// Verify the changes were applied
TestCase updatedTableTest = getEntity(tableTest.getId(), ADMIN_AUTH_HEADERS);
assertEquals("Modified Table Test", updatedTableTest.getDisplayName());
TestCase updatedColumnTest = getEntity(columnTest.getId(), ADMIN_AUTH_HEADERS);
assertEquals("Modified column test description", updatedColumnTest.getDescription());
patch = JsonDiff.asJson(mapper.readTree(originalJson), mapper.readTree(updatedJson));
TestUtils.patch(target, patch, TestDefinition.class, ADMIN_AUTH_HEADERS);
}
@Test
@Tag("ImportExport")
void testImportExport_TestSuiteLevel(TestInfo test) throws IOException {
// Create resource test instances
TestSuiteResourceTest testSuiteResourceTest = new TestSuiteResourceTest();
TableResourceTest tableResourceTest = new TableResourceTest();
void test_existingTestCasesContinueToWork(TestInfo test) throws Exception {
// Create a test case with an enabled definition (use table-level test)
CreateTestCase create = createRequest(getEntityName(test));
create.setEntityLink(TABLE_LINK);
create.setTestDefinition(TEST_DEFINITION5.getFullyQualifiedName());
create.setParameterValues(
List.of(new TestCaseParameterValue().withName("value").withValue("100")));
TestCase testCase = createEntity(create, ADMIN_AUTH_HEADERS);
assertNotNull(testCase);
// Create a logical test suite
CreateTestSuite createTestSuite =
testSuiteResourceTest
.createRequest(getEntityName(test))
.withDescription("Logical test suite for import/export test");
TestSuite testSuite = testSuiteResourceTest.createEntity(createTestSuite, ADMIN_AUTH_HEADERS);
// Disable the test definition using JSON Patch
String originalJson = JsonUtils.pojoToJson(TEST_DEFINITION5);
TEST_DEFINITION5.setEnabled(false);
String updatedJson = JsonUtils.pojoToJson(TEST_DEFINITION5);
// Create tables and test cases
Column c1 = new Column().withName("c1").withDataType(BIGINT);
CreateTable createTable1 =
tableResourceTest.createRequest(test, 1).withColumns(listOf(c1)).withTableConstraints(null);
Table table1 = tableResourceTest.createEntity(createTable1, ADMIN_AUTH_HEADERS);
ObjectMapper mapper = new ObjectMapper();
JsonNode patch = JsonDiff.asJson(mapper.readTree(originalJson), mapper.readTree(updatedJson));
CreateTable createTable2 =
tableResourceTest.createRequest(test, 2).withColumns(listOf(c1)).withTableConstraints(null);
Table table2 = tableResourceTest.createEntity(createTable2, ADMIN_AUTH_HEADERS);
WebTarget target =
getResource("dataQuality/testDefinitions").path(TEST_DEFINITION5.getId().toString());
TestUtils.patch(target, patch, TestDefinition.class, ADMIN_AUTH_HEADERS);
// Get test definition
TestDefinition tableRowCountDef =
Entity.getEntityByName(
TEST_DEFINITION, "tableRowCountToBeBetween", "", Include.NON_DELETED);
// Existing test case should still be retrievable
TestCase retrieved = getEntity(testCase.getId(), ADMIN_AUTH_HEADERS);
assertNotNull(retrieved);
assertEquals(testCase.getId(), retrieved.getId());
// Create test cases in the logical test suite
CreateTestCase createTest1 =
createRequest(getEntityName(test, 1))
.withEntityLink(String.format("<#E::table::%s>", table1.getFullyQualifiedName()))
.withTestDefinition(tableRowCountDef.getFullyQualifiedName())
.withParameterValues(
listOf(
new TestCaseParameterValue().withName("minValue").withValue("10"),
new TestCaseParameterValue().withName("maxValue").withValue("100")));
// Should be able to update the existing test case
String oldDescription = testCase.getDescription();
String updatedDescription = "Updated description for existing test case";
String json = JsonUtils.pojoToJson(testCase);
testCase.setDescription(updatedDescription);
ChangeDescription change = getChangeDescription(testCase, MINOR_UPDATE);
fieldUpdated(change, "description", oldDescription, updatedDescription);
patchEntityAndCheck(testCase, json, ADMIN_AUTH_HEADERS, MINOR_UPDATE, change);
CreateTestCase createTest2 =
createRequest(getEntityName(test, 2))
.withEntityLink(String.format("<#E::table::%s>", table2.getFullyQualifiedName()))
.withTestDefinition(tableRowCountDef.getFullyQualifiedName())
.withParameterValues(
listOf(
new TestCaseParameterValue().withName("minValue").withValue("5"),
new TestCaseParameterValue().withName("maxValue").withValue("50")));
// Re-enable the test definition for cleanup using JSON Patch
TestDefinition disabled =
Entity.getEntity(Entity.TEST_DEFINITION, TEST_DEFINITION5.getId(), "", null);
originalJson = JsonUtils.pojoToJson(disabled);
disabled.setEnabled(true);
updatedJson = JsonUtils.pojoToJson(disabled);
TestCase test1 = createEntity(createTest1, ADMIN_AUTH_HEADERS);
TestCase test2 = createEntity(createTest2, ADMIN_AUTH_HEADERS);
// Add test cases to logical test suite using the existing endpoint
addTestCasesToLogicalTestSuiteViaAPI(testSuite.getId(), listOf(test1.getId(), test2.getId()));
// Export test cases from the test suite
String exportedCsv = exportCsv(testSuite.getFullyQualifiedName());
assertNotNull(exportedCsv);
assertTrue(exportedCsv.contains(test1.getName()));
assertTrue(exportedCsv.contains(test2.getName()));
// Import modified CSV
String modifiedCsv = exportedCsv.replace("10", "20").replace("100", "200");
CsvImportResult result = importCsv(testSuite.getFullyQualifiedName(), modifiedCsv, false);
LOG.info("Import result status: {}", result.getStatus());
LOG.info("Import result CSV:\n{}", result.getImportResultsCsv());
assertEquals(org.openmetadata.schema.type.ApiStatus.SUCCESS, result.getStatus());
// Verify parameter values were updated
TestCase updatedTest1 = getEntity(test1.getId(), ADMIN_AUTH_HEADERS);
assertEquals(
"20",
updatedTest1.getParameterValues().stream()
.filter(p -> p.getName().equals("minValue"))
.findFirst()
.get()
.getValue());
patch = JsonDiff.asJson(mapper.readTree(originalJson), mapper.readTree(updatedJson));
TestUtils.patch(target, patch, TestDefinition.class, ADMIN_AUTH_HEADERS);
}
@Test
@Tag("ImportExport")
void testImportExport_PlatformWide(TestInfo test) throws IOException {
// Create multiple tables and test cases
TableResourceTest tableResourceTest = new TableResourceTest();
Column c1 = new Column().withName("c1").withDataType(BIGINT);
CreateTable createTable1 =
tableResourceTest.createRequest(test, 1).withColumns(listOf(c1)).withTableConstraints(null);
Table table1 = tableResourceTest.createEntity(createTable1, ADMIN_AUTH_HEADERS);
void test_cannotCreateNewTestCaseWhenDefinitionDisabled(TestInfo test) throws Exception {
// Create a test case with an enabled definition (use table-level test)
CreateTestCase create1 = createRequest(getEntityName(test) + "_1");
create1.setEntityLink(TABLE_LINK);
create1.setTestDefinition(TEST_DEFINITION4.getFullyQualifiedName());
create1.setParameterValues(
List.of(
new TestCaseParameterValue().withName("minValue").withValue("10"),
new TestCaseParameterValue().withName("maxValue").withValue("100")));
TestCase testCase1 = createEntity(create1, ADMIN_AUTH_HEADERS);
assertNotNull(testCase1);
CreateTable createTable2 =
tableResourceTest.createRequest(test, 2).withColumns(listOf(c1)).withTableConstraints(null);
Table table2 = tableResourceTest.createEntity(createTable2, ADMIN_AUTH_HEADERS);
// Disable the test definition using JSON Patch
String originalJson = JsonUtils.pojoToJson(TEST_DEFINITION4);
TEST_DEFINITION4.setEnabled(false);
String updatedJson = JsonUtils.pojoToJson(TEST_DEFINITION4);
// Get test definition
TestDefinition tableRowCountDef =
Entity.getEntityByName(
TEST_DEFINITION, "tableRowCountToBeBetween", "", Include.NON_DELETED);
ObjectMapper mapper = new ObjectMapper();
JsonNode patch = JsonDiff.asJson(mapper.readTree(originalJson), mapper.readTree(updatedJson));
// Create test cases
CreateTestCase createTest1 =
createRequest(getEntityName(test, 1))
.withEntityLink(String.format("<#E::table::%s>", table1.getFullyQualifiedName()))
.withTestDefinition(tableRowCountDef.getFullyQualifiedName())
.withParameterValues(
listOf(
new TestCaseParameterValue().withName("minValue").withValue("10"),
new TestCaseParameterValue().withName("maxValue").withValue("100")));
WebTarget target =
getResource("dataQuality/testDefinitions").path(TEST_DEFINITION4.getId().toString());
TestUtils.patch(target, patch, TestDefinition.class, ADMIN_AUTH_HEADERS);
CreateTestCase createTest2 =
createRequest(getEntityName(test, 2))
.withEntityLink(String.format("<#E::table::%s>", table2.getFullyQualifiedName()))
.withTestDefinition(tableRowCountDef.getFullyQualifiedName())
.withParameterValues(
listOf(
new TestCaseParameterValue().withName("minValue").withValue("5"),
new TestCaseParameterValue().withName("maxValue").withValue("50")));
// Try to create another test case with the disabled definition - should fail
CreateTestCase create2 = createRequest(getEntityName(test) + "_2");
create2.setEntityLink(TABLE_LINK);
create2.setTestDefinition(TEST_DEFINITION4.getFullyQualifiedName());
create2.setParameterValues(
List.of(
new TestCaseParameterValue().withName("minValue").withValue("10"),
new TestCaseParameterValue().withName("maxValue").withValue("100")));
TestCase test1 = createEntity(createTest1, ADMIN_AUTH_HEADERS);
TestCase test2 = createEntity(createTest2, ADMIN_AUTH_HEADERS);
assertResponse(
() -> createEntity(create2, ADMIN_AUTH_HEADERS),
BAD_REQUEST,
"Test definition '"
+ TEST_DEFINITION4.getName()
+ "' is disabled and cannot be used to create test cases");
// Export all test cases platform-wide using "*"
String exportedCsv = exportCsv("*");
assertNotNull(exportedCsv);
// Re-enable the test definition for cleanup using JSON Patch
TestDefinition disabled =
Entity.getEntity(Entity.TEST_DEFINITION, TEST_DEFINITION4.getId(), "", null);
originalJson = JsonUtils.pojoToJson(disabled);
disabled.setEnabled(true);
updatedJson = JsonUtils.pojoToJson(disabled);
// Verify exported CSV contains test cases from multiple tables
assertTrue(exportedCsv.contains(test1.getName()));
assertTrue(exportedCsv.contains(test2.getName()));
// Count number of test cases in export (excluding header)
long testCaseCount = exportedCsv.lines().count() - 1;
assertTrue(testCaseCount >= 2, "Should export at least 2 test cases");
}
@Test
@Tag("ImportExport")
void testImportCsv_CreateAndUpdate(TestInfo test) throws IOException {
TableResourceTest tableResourceTest = new TableResourceTest();
Column c1 = new Column().withName("c1").withDataType(BIGINT);
CreateTable createTable =
tableResourceTest.createRequest(test).withColumns(listOf(c1)).withTableConstraints(null);
Table table = tableResourceTest.createEntity(createTable, ADMIN_AUTH_HEADERS);
// Get test definition
TestDefinition tableRowCountDef =
Entity.getEntityByName(
TEST_DEFINITION, "tableRowCountToBeBetween", "", Include.NON_DELETED);
// Escape quotes in FQN for CSV format (double the quotes and wrap in quotes)
String entityFQN = "\"" + table.getFullyQualifiedName().replace("\"", "\"\"") + "\"";
// Test 1: Create a new test case via CSV import with tags
// Properly escape parameter values for CSV - double the quotes and wrap in quotes
String paramValues1 =
"\"{\"\"name\"\":\"\"minValue\"\",\"\"value\"\":10};{\"\"name\"\":\"\"maxValue\"\",\"\"value\"\":100}\"";
String createCsv =
"name*,displayName,description,testDefinition*,entityFQN*,testSuite,parameterValues,computePassedFailedRowCount,useDynamicAssertion,inspectionQuery,tags,glossaryTerms\n"
+ String.format(
"%s,New Test Case,Initial description,%s,%s,,%s,false,false,,PII.Sensitive,",
getEntityName(test),
tableRowCountDef.getFullyQualifiedName(),
entityFQN,
paramValues1);
CsvImportResult createResult = importCsv(table.getFullyQualifiedName(), createCsv, false);
LOG.info("Create import status: {}", createResult.getStatus());
LOG.info("Create import result:\n{}", createResult.getImportResultsCsv());
assertEquals(org.openmetadata.schema.type.ApiStatus.SUCCESS, createResult.getStatus());
// Note: NumberOfRowsPassed includes the header row, so 1 data row = 2 total
assertEquals(2, createResult.getNumberOfRowsPassed());
// Verify test case was created with tags
String testCaseFqn =
FullyQualifiedName.add(
table.getFullyQualifiedName(), EntityInterfaceUtil.quoteName(getEntityName(test)));
TestCase created = getEntityByName(testCaseFqn, "tags", ADMIN_AUTH_HEADERS);
assertNotNull(created);
assertEquals("New Test Case", created.getDisplayName());
assertEquals("Initial description", created.getDescription());
assertEquals(2, created.getParameterValues().size());
assertNotNull(created.getTags());
assertEquals(1, created.getTags().size());
assertEquals("PII.Sensitive", created.getTags().get(0).getTagFQN());
// Test 2: Update the existing test case via CSV import with different tags
String paramValues2 =
"\"{\"\"name\"\":\"\"minValue\"\",\"\"value\"\":5};{\"\"name\"\":\"\"maxValue\"\",\"\"value\"\":50}\"";
String updateCsv =
"name*,displayName,description,testDefinition*,entityFQN*,testSuite,parameterValues,computePassedFailedRowCount,useDynamicAssertion,inspectionQuery,tags,glossaryTerms\n"
+ String.format(
"%s,Updated Test Case,Updated description,%s,%s,,%s,true,false,SELECT * FROM table,PersonalData.Personal,",
getEntityName(test),
tableRowCountDef.getFullyQualifiedName(),
entityFQN,
paramValues2);
CsvImportResult updateResult = importCsv(table.getFullyQualifiedName(), updateCsv, false);
LOG.info("Update import status: {}", updateResult.getStatus());
LOG.info("Update import result:\n{}", updateResult.getImportResultsCsv());
assertEquals(org.openmetadata.schema.type.ApiStatus.SUCCESS, updateResult.getStatus());
// Note: NumberOfRowsPassed includes the header row, so 1 data row = 2 total
assertEquals(2, updateResult.getNumberOfRowsPassed());
// Verify test case was updated with new tags
TestCase updated = getEntityByName(testCaseFqn, "tags", ADMIN_AUTH_HEADERS);
assertNotNull(updated);
assertEquals("Updated Test Case", updated.getDisplayName());
assertEquals("Updated description", updated.getDescription());
assertEquals(2, updated.getParameterValues().size());
assertEquals("5", updated.getParameterValues().get(0).getValue());
assertEquals(true, updated.getComputePassedFailedRowCount());
assertEquals("SELECT * FROM table", updated.getInspectionQuery());
assertNotNull(updated.getTags());
assertEquals(1, updated.getTags().size());
assertEquals("PersonalData.Personal", updated.getTags().get(0).getTagFQN());
}
@Test
@Tag("ImportExport")
void testImportCsv_DryRun(TestInfo test) throws IOException {
TableResourceTest tableResourceTest = new TableResourceTest();
Column c1 = new Column().withName("c1").withDataType(BIGINT);
CreateTable createTable =
tableResourceTest.createRequest(test).withColumns(listOf(c1)).withTableConstraints(null);
Table table = tableResourceTest.createEntity(createTable, ADMIN_AUTH_HEADERS);
// Get test definition
TestDefinition tableRowCountDef =
Entity.getEntityByName(
TEST_DEFINITION, "tableRowCountToBeBetween", "", Include.NON_DELETED);
// Escape quotes in FQN for CSV format (double the quotes and wrap in quotes)
String entityFQN = "\"" + table.getFullyQualifiedName().replace("\"", "\"\"") + "\"";
// Headers with * for required fields (name, testDefinition, entityFQN are required)
String csv =
"name*,displayName,description,testDefinition*,entityFQN*,testSuite,parameterValues,computePassedFailedRowCount,useDynamicAssertion,inspectionQuery,tags,glossaryTerms\n"
+ String.format(
"%s,Dry run test,Test description,%s,%s,,,false,false,,,",
getEntityName(test), tableRowCountDef.getFullyQualifiedName(), entityFQN);
// Dry run should not create the entity
CsvImportResult dryRunResult = importCsv(table.getFullyQualifiedName(), csv, true);
if (dryRunResult.getStatus() != org.openmetadata.schema.type.ApiStatus.SUCCESS) {
LOG.info("Dry run import status: {}", dryRunResult.getStatus());
LOG.info("Dry run abort reason: {}", dryRunResult.getAbortReason());
LOG.info("Dry run import result:\n{}", dryRunResult.getImportResultsCsv());
}
assertEquals(org.openmetadata.schema.type.ApiStatus.SUCCESS, dryRunResult.getStatus());
assertTrue(dryRunResult.getDryRun());
// Verify entity was not created - use FQN which includes entityFQN + test case name
String testCaseFqn =
FullyQualifiedName.add(
table.getFullyQualifiedName(), EntityInterfaceUtil.quoteName(getEntityName(test)));
assertThrows(
HttpResponseException.class, () -> getEntityByName(testCaseFqn, ADMIN_AUTH_HEADERS));
// Actual import should create the entity
CsvImportResult actualResult = importCsv(table.getFullyQualifiedName(), csv, false);
LOG.info("Actual import status: {}", actualResult.getStatus());
LOG.info("Actual import result:\n{}", actualResult.getImportResultsCsv());
assertEquals(org.openmetadata.schema.type.ApiStatus.SUCCESS, actualResult.getStatus());
assertFalse(actualResult.getDryRun());
// Verify entity was created
TestCase created = getEntityByName(testCaseFqn, ADMIN_AUTH_HEADERS);
assertNotNull(created);
assertEquals("Dry run test", created.getDisplayName());
}
private void addTestCasesToLogicalTestSuiteViaAPI(UUID testSuiteId, List<UUID> testCaseIds)
throws HttpResponseException {
CreateLogicalTestCases createLogicalTestCases =
new CreateLogicalTestCases().withTestSuiteId(testSuiteId).withTestCaseIds(testCaseIds);
WebTarget target = getCollection().path("/logicalTestCases");
org.openmetadata.service.util.TestUtils.put(
target, createLogicalTestCases, TestSuite.class, OK, ADMIN_AUTH_HEADERS);
patch = JsonDiff.asJson(mapper.readTree(originalJson), mapper.readTree(updatedJson));
TestUtils.patch(target, patch, TestDefinition.class, ADMIN_AUTH_HEADERS);
}
// ========================================

View file

@ -1,13 +1,20 @@
package org.openmetadata.service.resources.dqtests;
import static jakarta.ws.rs.core.Response.Status.BAD_REQUEST;
import static jakarta.ws.rs.core.Response.Status.FORBIDDEN;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.openmetadata.service.exception.CatalogExceptionMessage.permissionNotAllowed;
import static org.openmetadata.service.util.TestUtils.ADMIN_AUTH_HEADERS;
import static org.openmetadata.service.util.TestUtils.TEST_AUTH_HEADERS;
import static org.openmetadata.service.util.TestUtils.TEST_USER_NAME;
import static org.openmetadata.service.util.TestUtils.assertListNotNull;
import static org.openmetadata.service.util.TestUtils.assertListNull;
import static org.openmetadata.service.util.TestUtils.assertResponse;
import static org.openmetadata.service.util.TestUtils.assertResponseContains;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.flipkart.zjsonpatch.JsonDiff;
import java.io.IOException;
import java.util.List;
import java.util.Map;
@ -22,7 +29,9 @@ import org.openmetadata.schema.tests.TestCaseParameter;
import org.openmetadata.schema.tests.TestDefinition;
import org.openmetadata.schema.tests.TestPlatform;
import org.openmetadata.schema.type.ColumnDataType;
import org.openmetadata.schema.type.MetadataOperation;
import org.openmetadata.schema.type.TestDefinitionEntityType;
import org.openmetadata.schema.utils.JsonUtils;
import org.openmetadata.schema.utils.ResultList;
import org.openmetadata.service.Entity;
import org.openmetadata.service.resources.EntityResourceTest;
@ -222,4 +231,274 @@ public class TestDefinitionResourceTest
public void assertFieldChange(String fieldName, Object expected, Object actual) {
assertCommonFieldChange(fieldName, expected, actual);
}
@Test
void test_enableDisableTestDefinition(TestInfo test) throws Exception {
// Create a test definition
TestDefinition testDef = createEntity(createRequest(test), ADMIN_AUTH_HEADERS);
assertEquals(true, testDef.getEnabled(), "Test definition should be enabled by default");
// Disable it using JSON Patch
String originalJson = JsonUtils.pojoToJson(testDef);
testDef.setEnabled(false);
String updatedJson = JsonUtils.pojoToJson(testDef);
ObjectMapper mapper = new ObjectMapper();
JsonNode patch = JsonDiff.asJson(mapper.readTree(originalJson), mapper.readTree(updatedJson));
TestDefinition disabled = patchEntity(testDef.getId(), patch, ADMIN_AUTH_HEADERS);
assertEquals(false, disabled.getEnabled(), "Test definition should be disabled");
// Verify disabled state persists
TestDefinition retrieved = getEntity(testDef.getId(), ADMIN_AUTH_HEADERS);
assertEquals(false, retrieved.getEnabled(), "Test definition should remain disabled");
// Enable it back using JSON Patch
originalJson = JsonUtils.pojoToJson(retrieved);
retrieved.setEnabled(true);
updatedJson = JsonUtils.pojoToJson(retrieved);
patch = JsonDiff.asJson(mapper.readTree(originalJson), mapper.readTree(updatedJson));
TestDefinition enabled = patchEntity(retrieved.getId(), patch, ADMIN_AUTH_HEADERS);
assertEquals(true, enabled.getEnabled(), "Test definition should be enabled");
// Verify enabled state persists
retrieved = getEntity(testDef.getId(), ADMIN_AUTH_HEADERS);
assertEquals(true, retrieved.getEnabled(), "Test definition should remain enabled");
}
@Test
void test_defaultEnabledTrue(TestInfo test) throws HttpResponseException {
// Create test definition without specifying enabled field
TestDefinition testDef = createEntity(createRequest(test), ADMIN_AUTH_HEADERS);
assertEquals(true, testDef.getEnabled(), "Test definition should default to enabled=true");
// Verify via GET as well
TestDefinition retrieved = getEntity(testDef.getId(), ADMIN_AUTH_HEADERS);
assertEquals(true, retrieved.getEnabled(), "Retrieved test definition should be enabled");
}
@Test
void test_systemTestDefinitionCanBeDisabled(TestInfo test) throws Exception {
// Get a system test definition
TestDefinition systemDef = getEntityByName("columnValuesToBeNotNull", "", ADMIN_AUTH_HEADERS);
assertEquals(
true, systemDef.getEnabled(), "System test definition should be enabled by default");
// Disable system test definition using JSON Patch
String originalJson = JsonUtils.pojoToJson(systemDef);
systemDef.setEnabled(false);
String updatedJson = JsonUtils.pojoToJson(systemDef);
ObjectMapper mapper = new ObjectMapper();
JsonNode patch = JsonDiff.asJson(mapper.readTree(originalJson), mapper.readTree(updatedJson));
TestDefinition disabled = patchEntity(systemDef.getId(), patch, ADMIN_AUTH_HEADERS);
assertEquals(false, disabled.getEnabled(), "System test definition should be disableable");
// Re-enable for cleanup using JSON Patch
originalJson = JsonUtils.pojoToJson(disabled);
disabled.setEnabled(true);
updatedJson = JsonUtils.pojoToJson(disabled);
patch = JsonDiff.asJson(mapper.readTree(originalJson), mapper.readTree(updatedJson));
patchEntity(disabled.getId(), patch, ADMIN_AUTH_HEADERS);
}
@Test
void test_listTestDefinitions_filterByEnabled(TestInfo test) throws Exception {
// Create two test definitions
TestDefinition enabledDef = createEntity(createRequest(test, 1), ADMIN_AUTH_HEADERS);
TestDefinition toDisableDef = createEntity(createRequest(test, 2), ADMIN_AUTH_HEADERS);
// Disable the second one using JSON Patch
String originalJson = JsonUtils.pojoToJson(toDisableDef);
toDisableDef.setEnabled(false);
String updatedJson = JsonUtils.pojoToJson(toDisableDef);
ObjectMapper mapper = new ObjectMapper();
JsonNode patch = JsonDiff.asJson(mapper.readTree(originalJson), mapper.readTree(updatedJson));
TestDefinition disabledDef = patchEntity(toDisableDef.getId(), patch, ADMIN_AUTH_HEADERS);
assertEquals(false, disabledDef.getEnabled(), "Test definition should be disabled");
// Test 1: enabled=true should return only enabled test definitions
Map<String, String> enabledTrueParams = Map.of("limit", "1000", "enabled", "true");
ResultList<TestDefinition> enabledList = listEntities(enabledTrueParams, ADMIN_AUTH_HEADERS);
boolean hasEnabled =
enabledList.getData().stream().anyMatch(td -> td.getId().equals(enabledDef.getId()));
boolean hasDisabled =
enabledList.getData().stream().anyMatch(td -> td.getId().equals(disabledDef.getId()));
Assertions.assertTrue(hasEnabled, "enabled=true list should include enabled test definition");
Assertions.assertFalse(
hasDisabled, "enabled=true list should NOT include disabled test definition");
// Verify all enabled definitions in the enabled list are actually enabled
boolean allEnabled =
enabledList.getData().stream()
.allMatch(td -> td.getEnabled() == null || Boolean.TRUE.equals(td.getEnabled()));
Assertions.assertTrue(
allEnabled, "All test definitions in enabled=true list should be enabled");
// Test 2: enabled=false should return only disabled test definitions
Map<String, String> enabledFalseParams = Map.of("limit", "1000", "enabled", "false");
ResultList<TestDefinition> disabledList = listEntities(enabledFalseParams, ADMIN_AUTH_HEADERS);
hasEnabled =
disabledList.getData().stream().anyMatch(td -> td.getId().equals(enabledDef.getId()));
hasDisabled =
disabledList.getData().stream().anyMatch(td -> td.getId().equals(disabledDef.getId()));
Assertions.assertFalse(
hasEnabled, "enabled=false list should NOT include enabled test definition");
Assertions.assertTrue(
hasDisabled, "enabled=false list should include disabled test definition");
// Verify all disabled definitions in the disabled list are actually disabled
boolean allDisabled =
disabledList.getData().stream().allMatch(td -> Boolean.FALSE.equals(td.getEnabled()));
Assertions.assertTrue(
allDisabled, "All test definitions in enabled=false list should be disabled");
// Test 3: Default behavior (no enabled param) should return all test definitions
Map<String, String> defaultParams = Map.of("limit", "1000");
ResultList<TestDefinition> defaultList = listEntities(defaultParams, ADMIN_AUTH_HEADERS);
hasEnabled =
defaultList.getData().stream().anyMatch(td -> td.getId().equals(enabledDef.getId()));
hasDisabled =
defaultList.getData().stream().anyMatch(td -> td.getId().equals(disabledDef.getId()));
Assertions.assertTrue(hasEnabled, "Default list should include enabled test definition");
Assertions.assertTrue(hasDisabled, "Default list should include disabled test definition");
// Re-enable for cleanup using JSON Patch
originalJson = JsonUtils.pojoToJson(disabledDef);
disabledDef.setEnabled(true);
updatedJson = JsonUtils.pojoToJson(disabledDef);
patch = JsonDiff.asJson(mapper.readTree(originalJson), mapper.readTree(updatedJson));
patchEntity(disabledDef.getId(), patch, ADMIN_AUTH_HEADERS);
}
@Test
void test_createTestDefinition_asNonAdmin_403() {
// Non-admin user without CREATE permission should not be able to create test definition
CreateTestDefinition create = createRequest("unauthorizedTestDef");
assertResponse(
() -> createEntity(create, TEST_AUTH_HEADERS),
FORBIDDEN,
permissionNotAllowed(TEST_USER_NAME, List.of(MetadataOperation.CREATE)));
}
@Test
void test_patchTestDefinition_asNonAdmin_403(TestInfo test) throws Exception {
// Create a test definition as admin
TestDefinition testDef = createEntity(createRequest(test), ADMIN_AUTH_HEADERS);
// Try to patch (enable/disable) as non-admin user
String originalJson = JsonUtils.pojoToJson(testDef);
testDef.setEnabled(false);
String updatedJson = JsonUtils.pojoToJson(testDef);
ObjectMapper mapper = new ObjectMapper();
JsonNode patch = JsonDiff.asJson(mapper.readTree(originalJson), mapper.readTree(updatedJson));
// Non-admin user without EDIT_ALL permission should not be able to patch
assertResponse(
() -> patchEntity(testDef.getId(), patch, TEST_AUTH_HEADERS),
FORBIDDEN,
permissionNotAllowed(TEST_USER_NAME, List.of(MetadataOperation.EDIT_ALL)));
}
@Test
void test_deleteTestDefinition_asNonAdmin_403(TestInfo test) throws HttpResponseException {
// Create a test definition as admin
TestDefinition testDef = createEntity(createRequest(test), ADMIN_AUTH_HEADERS);
// Non-admin user without DELETE permission should not be able to delete
assertResponse(
() -> deleteEntity(testDef.getId(), TEST_AUTH_HEADERS),
FORBIDDEN,
permissionNotAllowed(TEST_USER_NAME, List.of(MetadataOperation.DELETE)));
}
@Test
void test_systemTestDefinitionCannotModifyEntityType(TestInfo test) throws Exception {
// Get a system test definition
TestDefinition systemDef = getEntityByName("columnValuesToBeNotNull", "", ADMIN_AUTH_HEADERS);
assertEquals(
org.openmetadata.schema.type.ProviderType.SYSTEM,
systemDef.getProvider(),
"Should be a system test definition");
// Try to modify entity type using JSON Patch - should fail
String originalJson = JsonUtils.pojoToJson(systemDef);
systemDef.setEntityType(TestDefinitionEntityType.TABLE);
String updatedJson = JsonUtils.pojoToJson(systemDef);
ObjectMapper mapper = new ObjectMapper();
JsonNode patch = JsonDiff.asJson(mapper.readTree(originalJson), mapper.readTree(updatedJson));
assertResponse(
() -> patchEntity(systemDef.getId(), patch, ADMIN_AUTH_HEADERS),
BAD_REQUEST,
"System test definitions cannot have their entity type modified");
}
@Test
void test_systemTestDefinitionCannotBeDeleted(TestInfo test) throws HttpResponseException {
// Get a system test definition
TestDefinition systemDef = getEntityByName("columnValuesToBeNotNull", "", ADMIN_AUTH_HEADERS);
assertEquals(
org.openmetadata.schema.type.ProviderType.SYSTEM,
systemDef.getProvider(),
"Should be a system test definition");
// Try to delete the system test definition - should fail
assertResponse(
() -> deleteEntity(systemDef.getId(), ADMIN_AUTH_HEADERS),
BAD_REQUEST,
"System entity [columnValuesToBeNotNull] of type testDefinition can not be deleted.");
}
@Test
void test_userTestDefinitionCanBeModified(TestInfo test) throws Exception {
// Create a user test definition
TestDefinition userDef = createEntity(createRequest(test), ADMIN_AUTH_HEADERS);
assertEquals(
org.openmetadata.schema.type.ProviderType.USER,
userDef.getProvider(),
"Should be a user test definition");
// Modify entity type using JSON Patch - should succeed
String originalJson = JsonUtils.pojoToJson(userDef);
userDef.setEntityType(TestDefinitionEntityType.TABLE);
String updatedJson = JsonUtils.pojoToJson(userDef);
ObjectMapper mapper = new ObjectMapper();
JsonNode patch = JsonDiff.asJson(mapper.readTree(originalJson), mapper.readTree(updatedJson));
TestDefinition modified = patchEntity(userDef.getId(), patch, ADMIN_AUTH_HEADERS);
assertEquals(
TestDefinitionEntityType.TABLE,
modified.getEntityType(),
"User test definition entity type should be modifiable");
}
@Test
void test_userTestDefinitionCanBeDeleted(TestInfo test) throws HttpResponseException {
// Create a user test definition
TestDefinition userDef = createEntity(createRequest(test), ADMIN_AUTH_HEADERS);
assertEquals(
org.openmetadata.schema.type.ProviderType.USER,
userDef.getProvider(),
"Should be a user test definition");
// Delete the user test definition - should succeed
deleteEntity(userDef.getId(), ADMIN_AUTH_HEADERS);
// Verify it's deleted
assertResponseContains(
() -> getEntity(userDef.getId(), ADMIN_AUTH_HEADERS),
jakarta.ws.rs.core.Response.Status.NOT_FOUND,
"not found");
}
}

View file

@ -47,6 +47,9 @@
"$ref": "../../tests/testDefinition.json#/definitions/testCaseParameterDefinition"
}
},
"dataQualityDimension": {
"$ref": "../../tests/testDefinition.json#/definitions/dataQualityDimensions"
},
"supportedServices": {
"description": "List of services that this test definition supports. When empty, it implies all services are supported.",
"type": "array",
@ -61,6 +64,10 @@
"items": {
"type": "string"
}
},
"sqlExpression": {
"description": "SQL expression template for custom SQL-based test definitions. Supports substitution variables: {table} and {column} for runtime entity references, and {{paramName}} for user-defined parameters.",
"$ref": "../../type/basic.json#/definitions/sqlQuery"
}
},
"required": ["name", "description","entityType", "testPlatforms"],

View file

@ -65,7 +65,9 @@
"DeleteScim",
"ViewScim",
"Impersonate",
"AuditLogs"
"AuditLogs",
"ViewTestDefinitionLibrary",
"EditTestDefinitionLibrary"
]
}
},

View file

@ -217,6 +217,11 @@
"type": "boolean",
"default": false
},
"enabled": {
"description": "When `true` indicates the test definition is available for creating test cases. System test definitions can only be disabled by users with appropriate permissions.",
"type": "boolean",
"default": true
},
"supportedServices": {
"description": "List of services that this test definition supports. When empty, it implies all services are supported.",
"type": "array",
@ -224,6 +229,10 @@
"type": "string"
},
"default": []
},
"sqlExpression": {
"description": "SQL expression template for custom SQL-based test definitions. Supports substitution variables: {table} and {column} for runtime entity references, and {{paramName}} for user-defined parameters. This field is only applicable for test definitions with testPlatforms set to 'OpenMetadata' and is used to execute custom SQL queries for data quality validation.",
"$ref": "../type/basic.json#/definitions/sqlQuery"
}
},
"required": ["name", "description", "testPlatforms"],

View file

@ -0,0 +1,520 @@
/*
* Copyright 2024 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 test, { expect } from '@playwright/test';
import { redirectToHomePage, uuid } from '../../../utils/common';
const TEST_DEFINITION_NAME = `aCustomTestDefinition${uuid()}`;
const TEST_DEFINITION_DISPLAY_NAME = `Custom Test Definition ${uuid()}`;
const UPDATE_TEST_DEFINITION_DISPLAY_NAME = `Updated Custom Test Definition ${uuid()}`;
const TEST_DEFINITION_DESCRIPTION =
'A This is a custom test definition for E2E testing';
test.use({ storageState: 'playwright/.auth/admin.json' });
test.describe('Rules Library', () => {
test.beforeEach(async ({ page }) => {
await redirectToHomePage(page);
});
test('should navigate to Rules Library page', async ({ page }) => {
// Navigate directly to Rules Library
await page.goto('/rules-library');
// Wait for page to load
await page.waitForSelector('[data-testid="test-definition-table"]', {
state: 'visible',
timeout: 30000,
});
// Verify URL
await expect(page).toHaveURL(/.*\/rules-library/);
});
test('should display test definitions table with columns', async ({
page,
}) => {
// Navigate to Rules Library and wait for response
const responsePromise = page.waitForResponse(
(response) =>
response.url().includes('/api/v1/dataQuality/testDefinitions') &&
response.request().method() === 'GET'
);
await page.goto('/rules-library');
await responsePromise;
// Verify table is displayed
await expect(page.getByTestId('test-definition-table')).toBeVisible();
});
test('should display system test definitions', async ({ page }) => {
// Wait for API response before navigation
const responsePromise = page.waitForResponse(
(response) =>
response.url().includes('/api/v1/dataQuality/testDefinitions') &&
response.request().method() === 'GET'
);
await page.goto('/rules-library');
await responsePromise;
// Verify at least one test definition is displayed
const testDefinitionRows = page.locator(
'[data-testid="test-definition-table"] tbody tr'
);
await expect(testDefinitionRows).not.toHaveCount(0);
});
test('should create, edit, and delete a test definition', async ({ page }) => {
await test.step('Create a new test definition', async () => {
// Navigate to Rules Library
await page.goto('/rules-library');
// Click add button
await page.getByTestId('add-test-definition-button').click();
// Wait for drawer to open
await page.waitForSelector('.ant-drawer', { state: 'visible' });
// Verify drawer title
await expect(page.locator('.ant-drawer-title')).toContainText(
'Add Test Definition'
);
// Fill in form fields
await page.locator('#name').fill(TEST_DEFINITION_NAME);
await page.locator('#displayName').fill(TEST_DEFINITION_DISPLAY_NAME);
await page.locator('#description').fill(TEST_DEFINITION_DESCRIPTION);
// Select entity type
await page.locator('#entityType').click();
await page
.locator('.ant-select-item-option-content:has-text("TABLE")')
.first()
.click();
// Select test platform
await page.locator('#testPlatforms').click();
await page
.locator('.ant-select-item-option-content:has-text("dbt")')
.first()
.click();
// Wait for POST response when creating test definition
const testDefinitionResponse = page.waitForResponse(
(response) =>
response.url().includes('/api/v1/dataQuality/testDefinitions') &&
response.request().method() === 'POST'
);
// Click save
await page.getByTestId('save-test-definition').click();
// Wait for API response
const responseData = await testDefinitionResponse;
expect(responseData.status()).toBe(201);
// Wait for success toast
await expect(page.getByText(/created successfully/i)).toBeVisible();
// Verify test definition appears in table
await expect(page.getByTestId(TEST_DEFINITION_NAME)).toBeVisible();
});
await test.step('Edit Test Definition', async () => {
// Wait for table to load
await page.waitForSelector('[data-testid="test-definition-table"]', {
state: 'visible',
});
// Find and click edit button on first row
const firstEditButton = page.getByTestId(`edit-test-definition-${TEST_DEFINITION_NAME}`).first();
await firstEditButton.click();
// Wait for drawer to open
await page.waitForSelector('.ant-drawer', { state: 'visible' });
// Verify drawer title
await expect(page.locator('.ant-drawer-title')).toContainText(
'Edit Test Definition'
);
// Verify name field is disabled in edit mode
const nameInput = page.locator('#name');
await expect(nameInput).toBeDisabled();
// Update display name
const displayNameInput = page.getByLabel('Display Name');
await displayNameInput.clear();
await displayNameInput.fill(UPDATE_TEST_DEFINITION_DISPLAY_NAME);
// Wait for POST response when creating test definition
const testDefinitionResponse = page.waitForResponse(
(response) =>
response.url().includes('/api/v1/dataQuality/testDefinitions') &&
response.request().method() === 'PATCH'
);
// Click save
await page.getByTestId('save-test-definition').click();
// Wait for API response
const responseData = await testDefinitionResponse;
expect(responseData.status()).toBe(200);
// Wait for success toast
await expect(page.getByText(/updated successfully/i)).toBeVisible();
});
await test.step('should enable/disable test definition', async () => {
// Wait for table to load
await page.waitForSelector('[data-testid="test-definition-table"]', {
state: 'visible',
});
// Find first enabled switch
const firstSwitch = page.getByTestId(`enable-switch-${TEST_DEFINITION_NAME}`)
// Wait for API call
const testDefinitionResponse = page.waitForResponse(
(response) =>
response.url().includes('/api/v1/dataQuality/testDefinitions') &&
response.request().method() === 'PATCH'
);
// Toggle the switch
await firstSwitch.click();
// Wait for API response
const responseData = await testDefinitionResponse;
expect(responseData.status()).toBe(200);
// Wait for success toast
await expect(page.getByText(/updated successfully/i)).toBeVisible();
// Verify switch state changed
await expect(firstSwitch).toHaveAttribute(
'aria-checked',
String("false")
);
});
await test.step('should delete a test definition', async () => {
// Wait for table to load
await page.waitForSelector('[data-testid="test-definition-table"]', {
state: 'visible',
});
// Find and click delete button
const deleteButton = page.getByTestId(`delete-test-definition-${TEST_DEFINITION_NAME}`);
await deleteButton.click();
// Wait for confirmation modal
await page.waitForSelector('.ant-modal', { state: 'visible' });
// Verify modal content
await expect(page.getByText(`Delete ${UPDATE_TEST_DEFINITION_DISPLAY_NAME}`)).toBeVisible();
await page.getByTestId("confirmation-text-input").fill("DELETE");
// Wait for API call
const deleteTestDefinitionResponse = page.waitForResponse(
(response) =>
response.url().includes('/api/v1/dataQuality/testDefinitions') &&
response.request().method() === 'DELETE'
);
// Click confirm delete
await page.getByTestId("confirm-button").click();
const response = await deleteTestDefinitionResponse;
expect(response.status()).toBe(200);
// Wait for success toast
await expect(page.getByText(/deleted successfully/i)).toBeVisible();
// Verify test definition is removed from table
await expect(page.getByText(TEST_DEFINITION_NAME)).not.toBeVisible();
});
});
test('should validate required fields in create form', async ({ page }) => {
// Navigate to Rules Library
await page.goto('/rules-library');
// Click add button
await page.getByTestId('add-test-definition-button').click();
// Wait for drawer to open
await page.waitForSelector('.ant-drawer', { state: 'visible' });
// Click save without filling required fields
await page.getByTestId('save-test-definition').click();
// Verify validation errors appear for required fields
await expect(
page.locator('.ant-form-item-explain-error').first()
).toBeVisible();
});
test('should cancel form and close drawer', async ({ page }) => {
// Navigate to Rules Library
await page.goto('/rules-library');
// Click add button
await page.getByTestId('add-test-definition-button').click();
// Wait for drawer to open
await page.waitForSelector('.ant-drawer', { state: 'visible' });
// Fill in some fields
await page.locator('#name').fill('testName');
// Click cancel
await page.getByRole('button', { name: /Cancel/i }).click();
// Verify drawer is closed
await expect(page.locator('.ant-drawer')).not.toBeVisible();
});
test('should display pagination when test definitions exceed page size', async ({
page,
}) => {
// Wait for API response promise before navigation
const responsePromise = page.waitForResponse(
(response) =>
response.url().includes('/api/v1/dataQuality/testDefinitions') &&
response.request().method() === 'GET'
);
await page.goto('/rules-library');
const response = await responsePromise;
const data = await response.json();
// Check if pagination component exists (NextPrevious component)
const hasMultiplePages = data.paging && data.paging.total > 15;
if (!hasMultiplePages) {
// Skip test if there aren't enough test definitions for pagination
return;
}
// Pagination uses NextPrevious component, not ant-pagination
await expect(
page
.getByTestId('next')
).toBeVisible();
await expect(
page
.getByTestId('previous')
).toBeVisible();
});
test('should search and filter test definitions', async ({ page }) => {
// Navigate to Rules Library
await page.goto('/rules-library');
// Wait for table to load
await page.waitForSelector('[data-testid="test-definition-table"]', {
state: 'visible',
});
// Get initial row count
const initialRows = await page
.locator('[data-testid="test-definition-table"] tbody tr')
.count();
// Use table search/filter if available
const searchInput = page.getByPlaceholder(/Search/i);
if (await searchInput.isVisible()) {
await searchInput.fill('column');
// Wait for filtered results
await page.waitForTimeout(500);
// Verify filtered results
const filteredRows = await page
.locator('[data-testid="test-definition-table"] tbody tr')
.count();
// Filtered results should be less than or equal to initial results
expect(filteredRows).toBeLessThanOrEqual(initialRows);
}
});
test('should display test platform badges correctly', async ({ page }) => {
// Navigate to Rules Library
await page.goto('/rules-library');
// Wait for table to load
await page.waitForSelector('[data-testid="test-definition-table"]', {
state: 'visible',
});
// Verify test platform tags/badges are displayed
const platformTags = page.locator('.ant-tag, .ant-badge');
const tagCount = await platformTags.count();
expect(tagCount).toBeGreaterThan(0);
});
test('should not show edit and delete buttons for system test definitions', async ({
page,
}) => {
// Wait for API response before navigation
const responsePromise = page.waitForResponse(
(response) =>
response.url().includes('/api/v1/dataQuality/testDefinitions') &&
response.request().method() === 'GET'
);
await page.goto('/rules-library');
const response = await responsePromise;
const data = await response.json();
// Find a system test definition (e.g., columnValueLengthsToBeBetween)
const systemTestDef = data.data.find(
(def: { provider: string; name: string }) =>
def.provider === 'system'
);
// Verify edit button does not exist for system test definition
const editButton = page.getByTestId(
`edit-test-definition-${systemTestDef.name}`
);
await expect(editButton).toBeDisabled();
// Verify delete button does not exist for system test definition
const deleteButton = page.getByTestId(
`delete-test-definition-${systemTestDef.name}`
);
await expect(deleteButton).toBeDisabled();
// Verify enabled switch still exists and is functional
const row = page.locator(`[data-row-key="${systemTestDef.id}"]`);
const enabledSwitch = row.getByRole('switch');
await expect(enabledSwitch).toBeVisible();
});
test('should allow enabling/disabling system test definitions', async ({
page,
}) => {
// Wait for API response before navigation
const responsePromise = page.waitForResponse(
(response) =>
response.url().includes('/api/v1/dataQuality/testDefinitions') &&
response.request().method() === 'GET'
);
await page.goto('/rules-library');
const response = await responsePromise;
const data = await response.json();
// Wait for table to load
await page.waitForSelector('[data-testid="test-definition-table"]', {
state: 'visible',
});
// Find a system test definition
const systemTestDef = data.data.findLast(
(def: { provider: string }) => def.provider === 'system'
);
const enabledSwitch = page.getByTestId(`enable-switch-${systemTestDef.name}`);
// Wait for API call and verify success
const patchResponse = page.waitForResponse(
(response) =>
response.url().includes('/api/v1/dataQuality/testDefinitions') &&
response.request().method() === 'PATCH'
);
// Toggle the switch
await enabledSwitch.click();
const disableResponse = await patchResponse;
expect(disableResponse.status()).toBe(200);
// Verify switch state changed
await expect(enabledSwitch).toHaveAttribute(
'aria-checked',
String("false")
);
const patchResponse2 = page.waitForResponse(
(response) =>
response.url().includes('/api/v1/dataQuality/testDefinitions') &&
response.request().method() === 'PATCH'
);
// Toggle back to original state
await enabledSwitch.click();
const enableResponse = await patchResponse2;
expect(enableResponse.status()).toBe(200);
});
test('should display correct provider type for test definitions', async ({
page,
}) => {
// Wait for API response before navigation
const responsePromise = page.waitForResponse(
(response) =>
response.url().includes('/api/v1/dataQuality/testDefinitions') &&
response.request().method() === 'GET'
);
await page.goto('/rules-library');
const response = await responsePromise;
const data = await response.json();
// Verify we have both system and user test definitions (if user ones exist)
const systemDefs = data.data.filter(
(def: { provider: string }) => def.provider === 'system'
);
const userDefs = data.data.filter(
(def: { provider: string }) => def.provider === 'user'
);
// Should have system test definitions
expect(systemDefs.length).toBeGreaterThan(0);
// Verify system definitions have provider = 'system'
systemDefs.forEach((def: { provider: string }) => {
expect(def.provider).toBe('system');
});
// If user definitions exist, verify they have provider = 'user'
if (userDefs.length > 0) {
userDefs.forEach((def: { provider: string }) => {
expect(def.provider).toBe('user');
});
}
});
});

View file

@ -0,0 +1,476 @@
/*
* Copyright 2024 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 { test as base, expect, Page } from '@playwright/test';
import { PolicyClass } from '../../../support/access-control/PoliciesClass';
import { RolesClass } from '../../../support/access-control/RolesClass';
import { UserClass } from '../../../support/user/UserClass';
import { performAdminLogin } from '../../../utils/admin';
import { getApiContext, redirectToHomePage, uuid } from '../../../utils/common';
const actionNotAllowed = async (page: Page) => {
// Verify "Add Test Definition" button is NOT visible (no Create permission)
const addButton = page.getByTestId('add-test-definition-button');
await expect(addButton).not.toBeVisible();
// Verify edit buttons are NOT visible for user test definitions
const editButtons = page.getByTestId(/edit-test-definition-/).first();
await expect(editButtons).toBeDisabled();
// Verify delete buttons are NOT visible
const deleteButtons = page.getByTestId(/delete-test-definition-/).first();
await expect(deleteButtons).toBeDisabled();
// Verify enabled/disabled switches are NOT interactive (no EditAll permission)
const firstSwitch = page.getByRole('switch').first();
await expect(firstSwitch).toBeDisabled();
}
// Define permission policies for different roles
const TEST_DEFINITION_VIEW_ONLY_RULES = [
{
name: `test-definition-view-only-${uuid()}`,
resources: ['testDefinition'],
operations: ['ViewAll', 'ViewBasic'],
effect: 'allow',
},
];
const TEST_DEFINITION_DATA_CONSUMER_RULES = [
{
name: `test-definition-data-consumer-${uuid()}`,
resources: ['testDefinition'],
operations: ['ViewAll', 'ViewBasic'],
effect: 'allow',
},
];
const TEST_DEFINITION_DATA_STEWARD_RULES = [
{
name: `test-definition-data-steward-${uuid()}`,
resources: ['testDefinition'],
operations: ['ViewAll', 'ViewBasic', 'EditAll'],
effect: 'allow',
},
];
// Create policy and role instances
const dataConsumerPolicy = new PolicyClass();
const dataConsumerRole = new RolesClass();
const dataConsumerUser = new UserClass();
const dataStewardPolicy = new PolicyClass();
const dataStewardRole = new RolesClass();
const dataStewardUser = new UserClass();
const viewOnlyPolicy = new PolicyClass();
const viewOnlyRole = new RolesClass();
const viewOnlyUser = new UserClass();
const test = base.extend<{
adminPage: Page;
dataConsumerPage: Page;
dataStewardPage: Page;
viewOnlyPage: Page;
}>({
adminPage: async ({ browser }, use) => {
const { page } = await performAdminLogin(browser);
await use(page);
await page.close();
},
dataConsumerPage: async ({ browser }, use) => {
const page = await browser.newPage();
await dataConsumerUser.login(page);
await use(page);
await page.close();
},
dataStewardPage: async ({ browser }, use) => {
const page = await browser.newPage();
await dataStewardUser.login(page);
await use(page);
await page.close();
},
viewOnlyPage: async ({ browser }, use) => {
const page = await browser.newPage();
await viewOnlyUser.login(page);
await use(page);
await page.close();
},
});
test.beforeAll(async ({ browser }) => {
const { apiContext, afterAction } = await performAdminLogin(browser);
// Create view-only user with policy and role
await viewOnlyUser.create(apiContext, false);
const viewOnlyPolicyResponse = await viewOnlyPolicy.create(
apiContext,
TEST_DEFINITION_VIEW_ONLY_RULES
);
const viewOnlyRoleResponse = await viewOnlyRole.create(apiContext, [
viewOnlyPolicyResponse.fullyQualifiedName,
]);
await viewOnlyUser.patch({
apiContext,
patchData: [
{
op: 'add',
path: '/roles/0',
value: {
id: viewOnlyRoleResponse.id,
type: 'role',
name: viewOnlyRoleResponse.name,
},
},
],
});
// Create data consumer user with policy and role
await dataConsumerUser.create(apiContext, false);
const dataConsumerPolicyResponse = await dataConsumerPolicy.create(
apiContext,
TEST_DEFINITION_DATA_CONSUMER_RULES
);
const dataConsumerRoleResponse = await dataConsumerRole.create(apiContext, [
dataConsumerPolicyResponse.fullyQualifiedName,
]);
await dataConsumerUser.patch({
apiContext,
patchData: [
{
op: 'add',
path: '/roles/0',
value: {
id: dataConsumerRoleResponse.id,
type: 'role',
name: dataConsumerRoleResponse.name,
},
},
],
});
// Create data steward user with policy and role
await dataStewardUser.create(apiContext, false);
const dataStewardPolicyResponse = await dataStewardPolicy.create(
apiContext,
TEST_DEFINITION_DATA_STEWARD_RULES
);
const dataStewardRoleResponse = await dataStewardRole.create(apiContext, [
dataStewardPolicyResponse.fullyQualifiedName,
]);
await dataStewardUser.patch({
apiContext,
patchData: [
{
op: 'add',
path: '/roles/0',
value: {
id: dataStewardRoleResponse.id,
type: 'role',
name: dataStewardRoleResponse.name,
},
},
],
});
await afterAction();
});
test.afterAll(async ({ browser }) => {
const { apiContext, afterAction } = await performAdminLogin(browser);
// Cleanup users, roles, and policies
await viewOnlyUser.delete(apiContext);
await viewOnlyRole.delete(apiContext);
await viewOnlyPolicy.delete(apiContext);
await dataConsumerUser.delete(apiContext);
await dataConsumerRole.delete(apiContext);
await dataConsumerPolicy.delete(apiContext);
await dataStewardUser.delete(apiContext);
await dataStewardRole.delete(apiContext);
await dataStewardPolicy.delete(apiContext);
await afterAction();
});
test.describe('Test Definition Permissions - View Only User', () => {
test('should allow viewing test definitions but not create, edit, or delete', async ({
viewOnlyPage,
}) => {
await redirectToHomePage(viewOnlyPage);
// Navigate to Rules Library
await viewOnlyPage.goto('/rules-library');
// Wait for table to load
await viewOnlyPage.waitForSelector(
'[data-testid="test-definition-table"]',
{
state: 'visible',
}
);
// Verify user can view the table
await expect(
viewOnlyPage.getByTestId('test-definition-table')
).toBeVisible();
await actionNotAllowed(viewOnlyPage);
});
});
test.describe('Test Definition Permissions - Data Consumer', () => {
test('should allow viewing test definitions but not create, edit, or delete', async ({
dataConsumerPage,
}) => {
await redirectToHomePage(dataConsumerPage);
// Navigate to Rules Library
await dataConsumerPage.goto('/rules-library');
// Wait for table to load
await dataConsumerPage.waitForSelector(
'[data-testid="test-definition-table"]',
{
state: 'visible',
}
);
// Verify user can view the table
await expect(
dataConsumerPage.getByTestId('test-definition-table')
).toBeVisible();
await actionNotAllowed(dataConsumerPage);
});
});
test.describe('Test Definition Permissions - Data Steward', () => {
test('should allow viewing and editing but not creating or deleting test definitions', async ({
dataStewardPage,
}) => {
await redirectToHomePage(dataStewardPage);
// Navigate to Rules Library
await dataStewardPage.goto('/rules-library');
// Wait for table to load
await dataStewardPage.waitForSelector(
'[data-testid="test-definition-table"]',
{
state: 'visible',
}
);
// Verify user can view the table
await expect(
dataStewardPage.getByTestId('test-definition-table')
).toBeVisible();
// Data Steward should NOT have Create permission
const addButton = dataStewardPage.getByTestId('add-test-definition-button');
await expect(addButton).not.toBeVisible();
// Data Steward should be able to toggle enabled/disabled switches (EditAll permission)
const firstSwitch = dataStewardPage.getByRole('switch').first();
await expect(firstSwitch).toBeEnabled();
// Wait for API call
const response = dataStewardPage.waitForResponse(
(response) =>
response.url().includes('/api/v1/dataQuality/testDefinitions/') &&
response.request().method() === 'PATCH'
);
// Try to toggle the switch
await firstSwitch.click();
await response;
// Verify switch state changed
await expect(firstSwitch).toHaveAttribute(
'aria-checked',
String("false")
);
const response2 = dataStewardPage.waitForResponse(
(response) =>
response.url().includes('/api/v1/dataQuality/testDefinitions/') &&
response.request().method() === 'PATCH'
);
// Toggle back to original state
await firstSwitch.click();
await response2;
await expect(firstSwitch).toHaveAttribute('aria-checked', String("true"));
// Data Steward should NOT see delete buttons (no Delete permission)
const deleteButtons = dataStewardPage.getByTestId(
/delete-test-definition-/
);
await expect(deleteButtons.first()).toBeDisabled();
});
test('should not be able to edit system test definitions', async ({
dataStewardPage,
}) => {
await redirectToHomePage(dataStewardPage);
// Wait for API response to get test definitions
const response = dataStewardPage.waitForResponse((response) =>
response.url().includes('/api/v1/dataQuality/testDefinitions')
);
// Navigate to Rules Library
await dataStewardPage.goto('/rules-library');
const responseResolved = await response;
const data = await responseResolved.json();
// Find a system test definition
const systemTestDef = data.data.find(
(def: { provider: string }) => def.provider === 'system'
);
// Verify edit button does not exist for system test definition
const editButton = dataStewardPage.getByTestId(
`edit-test-definition-${systemTestDef.name}`
);
await expect(editButton).toBeDisabled();
// Verify enabled switch exists and can be toggled
const row = dataStewardPage.locator(
`[data-row-key="${systemTestDef.id}"]`
);
const enabledSwitch = row.getByRole('switch');
await expect(enabledSwitch).toBeVisible();
await expect(enabledSwitch).toBeEnabled();
});
});
test.describe('Test Definition Permissions - API Level Validation', () => {
test('should prevent unauthorized users from creating test definitions via API', async ({
dataConsumerPage,
}) => {
await redirectToHomePage(dataConsumerPage);
const { apiContext } = await getApiContext(dataConsumerPage);
// Try to create a test definition via API (should fail)
const createResponse = await apiContext.post(
'/api/v1/dataQuality/testDefinitions',
{
data: {
name: 'unauthorizedTest',
description: 'Should not be created',
entityType: 'COLUMN',
testPlatforms: ['OpenMetadata'],
},
}
);
// Verify the request failed with 403 Forbidden
expect(createResponse.status()).toBe(403);
});
test('should prevent unauthorized users from deleting test definitions via API', async ({
dataStewardPage,
adminPage
}) => {
await redirectToHomePage(dataStewardPage);
await redirectToHomePage(adminPage);
const { apiContext } = await getApiContext(dataStewardPage);
const { apiContext: adminApiContext } = await getApiContext(adminPage);
const createResponse = await adminApiContext.post(
'/api/v1/dataQuality/testDefinitions',
{
data: {
name: `unauthorizedTest${uuid()}`,
description: `unauthorizedTest`,
entityType: 'COLUMN',
testPlatforms: ['OpenMetadata'],
},
}
);
const data = await createResponse.json();
// Try to delete the test definition (should fail - no Delete permission)
const deleteResponse = await apiContext.delete(
`/api/v1/dataQuality/testDefinitions/${data.id}`
);
// Verify the request failed with 403 Forbidden
expect(deleteResponse.status()).toBe(403);
});
test('should prevent all users from modifying system test definition entity type via API', async ({
adminPage,
}) => {
await redirectToHomePage(adminPage);
const { apiContext } = await getApiContext(adminPage);
const response = adminPage.waitForResponse((response) =>
response.url().includes('/api/v1/dataQuality/testDefinitions')
);
// Navigate to Rules Library
await adminPage.goto('/rules-library');
const responseResolved = await response;
const data = await responseResolved.json();
// Find a system test definition with COLUMN entity type
const systemTestDef = data.data.find(
(def: { provider: string; entityType: string }) =>
def.provider === 'system' && def.entityType === 'COLUMN'
);
// Try to patch the test definition to change entity type (should fail even for admin)
const patchResponse = await apiContext.patch(
`/api/v1/dataQuality/testDefinitions/${systemTestDef.id}`,
{
data: [
{
op: 'replace',
path: '/entityType',
value: 'TABLE',
},
],
headers: {
'Content-Type': 'application/json-patch+json',
},
}
);
// Verify the request failed with 400 Bad Request
expect(patchResponse.status()).toBe(400);
const errorBody = await patchResponse.json();
expect(errorBody.message).toContain(
'System test definitions cannot have their entity type modified'
);
});
});

View file

@ -242,6 +242,10 @@ const IncidentManagerPage = withSuspenseFallback(
React.lazy(() => import('../../pages/IncidentManager/IncidentManagerPage'))
);
const RulesLibraryPage = withSuspenseFallback(
React.lazy(() => import('../../pages/RulesLibrary/RulesLibraryPage'))
);
const IncidentManagerDetailPage = withSuspenseFallback(
React.lazy(
() =>
@ -586,6 +590,18 @@ const AuthenticatedAppRouter: FunctionComponent = () => {
}
path={ROUTES.INCIDENT_MANAGER}
/>
<Route
element={
<AdminProtectedRoute
hasPermission={userPermissions.hasViewPermissions(
ResourceEntity.TEST_DEFINITION,
permissions
)}>
<RulesLibraryPage />
</AdminProtectedRoute>
}
path={ROUTES.RULES_LIBRARY}
/>
{[
ROUTES.TEST_CASE_DETAILS,

View file

@ -0,0 +1,437 @@
/*
* Copyright 2024 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 {
MinusCircleOutlined,
PlusOutlined,
QuestionCircleOutlined,
} from '@ant-design/icons';
import {
Button,
Card,
Drawer,
Form,
Input,
Select,
Space,
Switch,
Tooltip,
Typography,
} from 'antd';
import { AxiosError } from 'axios';
import { compare } from 'fast-json-patch';
import React, { useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { ReactComponent as CloseIcon } from '../../../assets/svg/close.svg';
import { CSMode } from '../../../enums/codemirror.enum';
import { CreateTestDefinition } from '../../../generated/api/tests/createTestDefinition';
import {
DataQualityDimensions,
DataType,
EntityType,
TestDataType,
TestDefinition,
TestPlatform,
} from '../../../generated/tests/testDefinition';
import {
createTestDefinition,
patchTestDefinition,
} from '../../../rest/testAPI';
import { createScrollToErrorHandler } from '../../../utils/formUtils';
import { showSuccessToast } from '../../../utils/ToastUtils';
import AlertBar from '../../AlertBar/AlertBar';
import FormItemLabel from '../../common/Form/FormItemLabel';
import CodeEditor from '../../Database/SchemaEditor/CodeEditor';
interface TestDefinitionFormProps {
initialValues?: TestDefinition;
onSuccess: () => void;
onCancel: () => void;
}
const TestDefinitionForm: React.FC<TestDefinitionFormProps> = ({
initialValues,
onSuccess,
onCancel,
}) => {
const { t } = useTranslation();
const [form] = Form.useForm();
const [isSubmitting, setIsSubmitting] = useState(false);
const [errorMessage, setErrorMessage] = useState<string>('');
const isEditMode = Boolean(initialValues);
const scrollToError = useMemo(() => createScrollToErrorHandler(), []);
const handleSubmit = async (values: TestDefinition) => {
setIsSubmitting(true);
setErrorMessage('');
try {
if (isEditMode && initialValues) {
const updatedValues = {
...initialValues,
...values,
};
const patch = compare(initialValues, updatedValues);
if (patch.length > 0) {
await patchTestDefinition(initialValues?.id ?? '', patch);
}
showSuccessToast(
t('server.entity-updated-success', {
entity: t('label.test-definition'),
})
);
} else {
const payload: CreateTestDefinition = {
name: values.name,
displayName: values.displayName,
description: values.description,
sqlExpression: values.sqlExpression,
entityType: values.entityType ?? EntityType.Table,
testPlatforms: values.testPlatforms,
dataQualityDimension: values.dataQualityDimension,
supportedDataTypes: values.supportedDataTypes,
parameterDefinition: values.parameterDefinition,
};
await createTestDefinition(payload);
showSuccessToast(
t('server.entity-created-success', {
entity: t('label.test-definition'),
})
);
}
onSuccess();
} catch (error) {
const errorMsg =
(error as AxiosError<{ message: string }>)?.response?.data?.message ||
(isEditMode
? t('server.update-entity-error', {
entity: t('label.test-definition'),
})
: t('server.create-entity-error', {
entity: t('label.test-definition'),
}));
setErrorMessage(errorMsg);
} finally {
setIsSubmitting(false);
}
};
return (
<Drawer
destroyOnClose
open
className="custom-drawer-style"
closable={false}
extra={
<Button
className="drawer-close-icon flex-center"
data-testid="close-drawer-button"
icon={<CloseIcon />}
type="link"
onClick={onCancel}
/>
}
footer={
<Space className="w-full justify-end">
<Button onClick={onCancel}>{t('label.cancel')}</Button>
<Button
data-testid="save-test-definition"
htmlType="submit"
loading={isSubmitting}
type="primary"
onClick={() => form.submit()}>
{t('label.save')}
</Button>
</Space>
}
title={
isEditMode
? t('label.edit-entity', { entity: t('label.test-definition') })
: t('label.add-entity', { entity: t('label.test-definition') })
}
width={720}
onClose={onCancel}>
{errorMessage && (
<div className="m-b-md">
<AlertBar
defafultExpand
className="test-definition-form-alert"
message={errorMessage}
type="error"
/>
</div>
)}
<Form
className="new-form-style"
form={form}
initialValues={{
...initialValues,
testPlatforms: initialValues?.testPlatforms || [
TestPlatform.OpenMetadata,
],
}}
layout="vertical"
onFinish={handleSubmit}
onFinishFailed={scrollToError}>
<Form.Item
label={t('label.name')}
name="name"
rules={[
{
required: true,
message: t('message.field-text-is-required', {
fieldText: t('label.name'),
}),
},
]}>
<Input
disabled={isEditMode}
placeholder={t('label.enter-entity-name', {
entity: t('label.test-definition'),
})}
/>
</Form.Item>
<Form.Item label={t('label.display-name')} name="displayName">
<Input
placeholder={t('label.enter-entity-name', {
entity: t('label.display-name'),
})}
/>
</Form.Item>
<Form.Item
label={t('label.description')}
name="description"
rules={[
{
required: true,
message: t('message.field-text-is-required', {
fieldText: t('label.description'),
}),
},
]}>
<Input.TextArea
placeholder={t('label.enter-entity-description', {
entity: t('label.test-definition'),
})}
rows={4}
/>
</Form.Item>
<Form.Item name="sqlExpression">
<CodeEditor
refreshEditor
showCopyButton
className="custom-query-editor query-editor-h-200"
mode={{ name: CSMode.SQL }}
title={
<div className="ant-form-item-label">
<label className="d-flex align-items-center">
<Typography.Text className="form-label-title">
{t('label.sql-query')}
</Typography.Text>
<Tooltip title={t('message.test-definition-sql-query-help')}>
<QuestionCircleOutlined className="ant-form-item-tooltip" />
</Tooltip>
</label>
</div>
}
/>
</Form.Item>
<Form.Item
label={t('label.entity-type')}
name="entityType"
rules={[
{
required: true,
message: t('message.field-text-is-required', {
fieldText: t('label.entity-type'),
}),
},
]}>
<Select
disabled={isEditMode}
id="entityType"
options={Object.values(EntityType).map((type) => ({
label: type,
value: type,
}))}
placeholder={t('label.select-field', {
field: t('label.entity-type'),
})}
/>
</Form.Item>
<Form.Item
label={t('label.test-platform-plural')}
name="testPlatforms"
rules={[
{
required: true,
message: t('message.field-text-is-required', {
fieldText: t('label.test-platform-plural'),
}),
},
]}>
<Select
id="testPlatforms"
mode="multiple"
options={Object.values(TestPlatform).map((platform) => ({
label: platform,
value: platform,
}))}
placeholder={t('label.select-field', {
field: t('label.test-platform-plural'),
})}
/>
</Form.Item>
<Form.Item
label={t('label.data-quality-dimension')}
name="dataQualityDimension">
<Select
options={Object.values(DataQualityDimensions).map((dimension) => ({
label: dimension,
value: dimension,
}))}
placeholder={t('label.select-field', {
field: t('label.data-quality-dimension'),
})}
/>
</Form.Item>
<Form.Item
label={t('label.supported-data-type-plural')}
name="supportedDataTypes">
<Select
mode="multiple"
options={Object.values(DataType).map((dataType) => ({
label: dataType,
value: dataType,
}))}
placeholder={t('label.select-field', {
field: t('label.supported-data-type-plural'),
})}
/>
</Form.Item>
<Form.Item
label={
<FormItemLabel
helperText={t('message.test-definition-parameters-description')}
label={t('label.parameter-plural')}
/>
}>
<Form.List name="parameterDefinition">
{(fields, { add, remove }) => (
<>
{fields.map(({ key, name, ...restField }) => (
<Card
extra={<MinusCircleOutlined onClick={() => remove(name)} />}
key={key}
size="small"
style={{ marginTop: 16 }}
title={`${t('label.parameter')} ${name + 1}`}>
<Form.Item
{...restField}
label={t('label.name')}
name={[name, 'name']}
rules={[
{
required: true,
message: t('message.field-text-is-required', {
fieldText: t('label.name'),
}),
},
]}>
<Input placeholder={t('label.parameter-name')} />
</Form.Item>
<Form.Item
{...restField}
label={t('label.display-name')}
name={[name, 'displayName']}>
<Input placeholder={t('label.parameter-display-name')} />
</Form.Item>
<Form.Item
{...restField}
label={t('label.description')}
name={[name, 'description']}>
<Input.TextArea
placeholder={t('label.parameter-description')}
rows={2}
/>
</Form.Item>
<Form.Item
{...restField}
label={t('label.data-type')}
name={[name, 'dataType']}
rules={[
{
required: true,
message: t('message.field-text-is-required', {
fieldText: t('label.data-type'),
}),
},
]}>
<Select
options={Object.values(TestDataType).map((type) => ({
label: type,
value: type,
}))}
placeholder={t('label.select-field', {
field: t('label.data-type'),
})}
/>
</Form.Item>
<Form.Item
{...restField}
label={t('label.required')}
name={[name, 'required']}
valuePropName="checked">
<Switch />
</Form.Item>
</Card>
))}
<Button
block
icon={<PlusOutlined />}
style={{ marginTop: 16 }}
type="dashed"
onClick={() => add()}>
{t('label.add-entity', { entity: t('label.parameter') })}
</Button>
</>
)}
</Form.List>
</Form.Item>
{isEditMode && (
<Form.Item
label={t('label.enabled')}
name="enabled"
style={{ marginTop: 16 }}
valuePropName="checked">
<Switch />
</Form.Item>
)}
</Form>
</Drawer>
);
};
export default TestDefinitionForm;

View file

@ -0,0 +1,387 @@
/*
* Copyright 2024 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 {
act,
fireEvent,
render,
screen,
waitFor,
} from '@testing-library/react';
import {
DataQualityDimensions,
DataType,
EntityType,
TestDefinition,
TestPlatform,
} from '../../../generated/tests/testDefinition';
import {
createTestDefinition,
patchTestDefinition,
} from '../../../rest/testAPI';
import TestDefinitionForm from './TestDefinitionForm.component';
const mockOnSuccess = jest.fn();
const mockOnCancel = jest.fn();
const mockInitialValues: TestDefinition = {
id: 'test-def-1',
name: 'columnValuesToBeNotNull',
displayName: 'Column Values To Be Not Null',
description: 'Ensures that all values in a column are not null',
entityType: EntityType.Column,
testPlatforms: [TestPlatform.OpenMetadata],
dataQualityDimension: DataQualityDimensions.Completeness,
supportedDataTypes: [DataType.String, DataType.Int],
enabled: true,
sqlExpression: 'SELECT * FROM {table} WHERE {column} IS NOT NULL',
};
jest.mock('../../../rest/testAPI', () => ({
createTestDefinition: jest.fn(),
patchTestDefinition: jest.fn(),
}));
jest.mock('../../../utils/ToastUtils', () => ({
showSuccessToast: jest.fn(),
}));
jest.mock('../../AlertBar/AlertBar', () => ({
__esModule: true,
default: jest
.fn()
.mockImplementation(({ message }) => (
<div data-testid="alert-bar">{message}</div>
)),
}));
jest.mock('../../../utils/formUtils', () => ({
createScrollToErrorHandler: jest.fn(() => jest.fn()),
}));
jest.mock('../../Database/SchemaEditor/CodeEditor', () => ({
__esModule: true,
default: jest
.fn()
.mockImplementation(({ value, onChange }) => (
<textarea
data-testid="code-editor"
value={value}
onChange={(e) => onChange?.(e.target.value)}
/>
)),
}));
describe('TestDefinitionForm Component', () => {
beforeEach(() => {
jest.clearAllMocks();
(createTestDefinition as jest.Mock).mockResolvedValue({});
(patchTestDefinition as jest.Mock).mockResolvedValue({});
});
describe('Rendering', () => {
it('should render form in create mode with all required fields', () => {
render(
<TestDefinitionForm onCancel={mockOnCancel} onSuccess={mockOnSuccess} />
);
expect(screen.getByLabelText('label.name')).toBeInTheDocument();
expect(screen.getByLabelText('label.display-name')).toBeInTheDocument();
expect(screen.getByLabelText('label.description')).toBeInTheDocument();
expect(screen.getByTestId('code-editor')).toBeInTheDocument();
expect(screen.getByLabelText('label.entity-type')).toBeInTheDocument();
expect(
screen.getByLabelText('label.test-platform-plural')
).toBeInTheDocument();
expect(
screen.getByLabelText('label.data-quality-dimension')
).toBeInTheDocument();
expect(
screen.getByLabelText('label.supported-data-type-plural')
).toBeInTheDocument();
expect(screen.getByTestId('save-test-definition')).toBeInTheDocument();
});
it('should render form in edit mode with initial values populated', () => {
render(
<TestDefinitionForm
initialValues={mockInitialValues}
onCancel={mockOnCancel}
onSuccess={mockOnSuccess}
/>
);
expect(screen.getByText('label.edit-entity')).toBeInTheDocument();
const nameInput = screen.getByLabelText('label.name') as HTMLInputElement;
expect(nameInput.value).toBe('columnValuesToBeNotNull');
expect(nameInput).toBeDisabled();
const displayNameInput = screen.getByLabelText(
'label.display-name'
) as HTMLInputElement;
expect(displayNameInput.value).toBe('Column Values To Be Not Null');
const descriptionInput = screen.getByLabelText(
'label.description'
) as HTMLTextAreaElement;
expect(descriptionInput.value).toBe(
'Ensures that all values in a column are not null'
);
});
it('should render SQL query editor section', () => {
render(
<TestDefinitionForm onCancel={mockOnCancel} onSuccess={mockOnSuccess} />
);
expect(screen.getByTestId('code-editor')).toBeInTheDocument();
});
it('should render parameter section with add button', () => {
render(
<TestDefinitionForm onCancel={mockOnCancel} onSuccess={mockOnSuccess} />
);
const addButtons = screen.getAllByRole('button', {
name: /label.add-entity/i,
});
expect(addButtons.length).toBeGreaterThan(0);
});
});
describe('Form Field Behavior', () => {
it('should disable name field in edit mode', () => {
render(
<TestDefinitionForm
initialValues={mockInitialValues}
onCancel={mockOnCancel}
onSuccess={mockOnSuccess}
/>
);
const nameInput = screen.getByLabelText('label.name');
expect(nameInput).toBeDisabled();
});
it('should enable name field in create mode', () => {
render(
<TestDefinitionForm onCancel={mockOnCancel} onSuccess={mockOnSuccess} />
);
const nameInput = screen.getByLabelText('label.name');
expect(nameInput).not.toBeDisabled();
});
it('should show enabled switch in edit mode', () => {
render(
<TestDefinitionForm
initialValues={mockInitialValues}
onCancel={mockOnCancel}
onSuccess={mockOnSuccess}
/>
);
const enabledSwitch = screen.getByRole('switch');
expect(enabledSwitch).toBeInTheDocument();
expect(enabledSwitch).toBeChecked();
});
it('should not show enabled switch in create mode', () => {
render(
<TestDefinitionForm onCancel={mockOnCancel} onSuccess={mockOnSuccess} />
);
const switches = screen.queryAllByRole('switch');
expect(switches).toHaveLength(0);
});
it('should update SQL expression when typing in editor', async () => {
render(
<TestDefinitionForm onCancel={mockOnCancel} onSuccess={mockOnSuccess} />
);
const sqlEditor = screen.getByTestId(
'code-editor'
) as HTMLTextAreaElement;
await act(async () => {
fireEvent.change(sqlEditor, {
target: { value: 'SELECT * FROM {table} WHERE {column} IS NOT NULL' },
});
});
expect(sqlEditor.value).toBe(
'SELECT * FROM {table} WHERE {column} IS NOT NULL'
);
});
it('should populate SQL expression in edit mode', () => {
render(
<TestDefinitionForm
initialValues={mockInitialValues}
onCancel={mockOnCancel}
onSuccess={mockOnSuccess}
/>
);
const sqlEditor = screen.getByTestId('code-editor');
expect(sqlEditor).toHaveValue(
'SELECT * FROM {table} WHERE {column} IS NOT NULL'
);
});
});
describe('Parameter Management', () => {
it('should add new parameter when add button is clicked', async () => {
render(
<TestDefinitionForm onCancel={mockOnCancel} onSuccess={mockOnSuccess} />
);
const addButtons = screen.getAllByText('label.add-entity');
const parameterAddButton = addButtons[addButtons.length - 1];
await act(async () => {
fireEvent.click(parameterAddButton);
});
await waitFor(() => {
expect(screen.getByText('label.parameter 1')).toBeInTheDocument();
expect(
screen.getByPlaceholderText('label.parameter-name')
).toBeInTheDocument();
});
});
it('should remove parameter when remove button is clicked', async () => {
render(
<TestDefinitionForm onCancel={mockOnCancel} onSuccess={mockOnSuccess} />
);
const addButtons = screen.getAllByText('label.add-entity');
const parameterAddButton = addButtons[addButtons.length - 1];
await act(async () => {
fireEvent.click(parameterAddButton);
});
await waitFor(() => {
expect(screen.getByText('label.parameter 1')).toBeInTheDocument();
});
const removeButton = screen.getByLabelText('minus-circle');
await act(async () => {
fireEvent.click(removeButton);
});
await waitFor(() => {
expect(screen.queryByText('label.parameter 1')).not.toBeInTheDocument();
});
});
});
describe('Form Validation', () => {
it('should show validation errors when required fields are empty', async () => {
render(
<TestDefinitionForm onCancel={mockOnCancel} onSuccess={mockOnSuccess} />
);
const saveButton = screen.getByTestId('save-test-definition');
await act(async () => {
fireEvent.click(saveButton);
});
await waitFor(() => {
const errors = screen.getAllByText('message.field-text-is-required');
expect(errors.length).toBeGreaterThan(0);
});
});
});
describe('Form Submission', () => {
it('should have save button that triggers form submission', () => {
render(
<TestDefinitionForm onCancel={mockOnCancel} onSuccess={mockOnSuccess} />
);
const saveButton = screen.getByTestId('save-test-definition');
expect(saveButton).toBeInTheDocument();
expect(saveButton).toHaveTextContent('label.save');
});
it('should render form with proper structure for submission', () => {
render(
<TestDefinitionForm onCancel={mockOnCancel} onSuccess={mockOnSuccess} />
);
expect(screen.getByLabelText('label.name')).toBeInTheDocument();
expect(screen.getByLabelText('label.description')).toBeInTheDocument();
expect(screen.getByLabelText('label.entity-type')).toBeInTheDocument();
expect(
screen.getByLabelText('label.test-platform-plural')
).toBeInTheDocument();
expect(screen.getByTestId('save-test-definition')).toBeInTheDocument();
});
it('should initialize with testPlatforms field in edit mode', () => {
render(
<TestDefinitionForm
initialValues={mockInitialValues}
onCancel={mockOnCancel}
onSuccess={mockOnSuccess}
/>
);
const testPlatformField = screen.getByLabelText(
'label.test-platform-plural'
);
expect(testPlatformField).toBeInTheDocument();
});
});
describe('User Interactions', () => {
it('should call onCancel when cancel button is clicked', () => {
render(
<TestDefinitionForm onCancel={mockOnCancel} onSuccess={mockOnSuccess} />
);
const cancelButton = screen.getByText('label.cancel');
fireEvent.click(cancelButton);
expect(mockOnCancel).toHaveBeenCalled();
});
it('should close drawer when clicking close icon', () => {
render(
<TestDefinitionForm onCancel={mockOnCancel} onSuccess={mockOnSuccess} />
);
const drawer = screen.getByRole('dialog');
expect(drawer).toBeInTheDocument();
});
});
});

View file

@ -0,0 +1,511 @@
/*
* Copyright 2024 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 {
Button,
Card,
Col,
Row,
Skeleton,
Space,
Switch,
Tooltip,
Typography,
} from 'antd';
import { ColumnsType } from 'antd/lib/table';
import { AxiosError } from 'axios';
import { compare } from 'fast-json-patch';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { ReactComponent as IconEdit } from '../../../assets/svg/edit-new.svg';
import { ReactComponent as IconDelete } from '../../../assets/svg/ic-delete.svg';
import { usePermissionProvider } from '../../../context/PermissionProvider/PermissionProvider';
import {
OperationPermission,
ResourceEntity,
} from '../../../context/PermissionProvider/PermissionProvider.interface';
import { ERROR_PLACEHOLDER_TYPE } from '../../../enums/common.enum';
import { ProviderType } from '../../../generated/entity/bot';
import { Operation } from '../../../generated/entity/policies/policy';
import { TestDefinition } from '../../../generated/tests/testDefinition';
import { Paging } from '../../../generated/type/paging';
import { usePaging } from '../../../hooks/paging/usePaging';
import {
deleteTestDefinitionByFqn,
getListTestDefinitions,
patchTestDefinition,
} from '../../../rest/testAPI';
import { getEntityName } from '../../../utils/EntityUtils';
import {
checkPermission,
DEFAULT_ENTITY_PERMISSION,
} from '../../../utils/PermissionsUtils';
import { showErrorToast, showSuccessToast } from '../../../utils/ToastUtils';
import ErrorPlaceHolder from '../../common/ErrorWithPlaceholder/ErrorPlaceHolder';
import Loader from '../../common/Loader/Loader';
import { PagingHandlerParams } from '../../common/NextPrevious/NextPrevious.interface';
import RichTextEditorPreviewerNew from '../../common/RichTextEditor/RichTextEditorPreviewNew';
import Table from '../../common/Table/Table';
import EntityDeleteModal from '../../Modals/EntityDeleteModal/EntityDeleteModal';
import TestDefinitionForm from '../TestDefinitionForm/TestDefinitionForm.component';
const TestDefinitionList = () => {
const { t } = useTranslation();
const { permissions, getEntityPermissionByFqn } = usePermissionProvider();
const {
currentPage,
paging,
pageSize,
handlePagingChange,
handlePageChange,
showPagination,
pagingCursor,
} = usePaging();
const [testDefinitions, setTestDefinitions] = useState<TestDefinition[]>([]);
const [isLoading, setIsLoading] = useState(true);
const [selectedDefinition, setSelectedDefinition] = useState<
TestDefinition | undefined
>();
const [isFormVisible, setIsFormVisible] = useState(false);
const [isDeleteModalVisible, setIsDeleteModalVisible] = useState(false);
const [definitionToDelete, setDefinitionToDelete] = useState<
TestDefinition | undefined
>();
const [testDefinitionPermissions, setTestDefinitionPermissions] = useState<
Record<string, OperationPermission>
>({});
const [permissionLoading, setPermissionLoading] = useState(true);
const createPermission = useMemo(
() =>
checkPermission(
Operation.Create,
ResourceEntity.TEST_DEFINITION,
permissions
),
[permissions]
);
const viewPermission = useMemo(
() =>
checkPermission(
Operation.ViewBasic,
ResourceEntity.TEST_DEFINITION,
permissions
) ||
checkPermission(
Operation.ViewAll,
ResourceEntity.TEST_DEFINITION,
permissions
),
[permissions]
);
const fetchTestDefinitionPermissions = useCallback(
async (definitions: TestDefinition[]) => {
try {
setPermissionLoading(true);
if (!definitions.length) {
setTestDefinitionPermissions({});
return;
}
// Fetch permissions for all definitions (including system definitions)
const permissionPromises: Promise<OperationPermission>[] =
definitions.map((def) =>
getEntityPermissionByFqn(
ResourceEntity.TEST_DEFINITION,
def.fullyQualifiedName ?? ''
)
);
const permissionResponses = await Promise.allSettled(
permissionPromises
);
const permissionsMap = definitions.reduce((acc, def, idx) => {
const response = permissionResponses[idx];
return {
...acc,
[def.name]:
response?.status === 'fulfilled'
? response.value
: DEFAULT_ENTITY_PERMISSION,
};
}, {} as Record<string, OperationPermission>);
setTestDefinitionPermissions(permissionsMap);
} catch (error) {
showErrorToast(error as AxiosError);
} finally {
setPermissionLoading(false);
}
},
[getEntityPermissionByFqn]
);
const fetchTestDefinitions = useCallback(
async (pagingOffset?: Partial<Paging>) => {
setIsLoading(true);
try {
const { data, paging: responsePaging } = await getListTestDefinitions({
after: pagingOffset?.after,
before: pagingOffset?.before,
limit: pageSize,
});
setTestDefinitions(data);
handlePagingChange(responsePaging);
// Fetch permissions asynchronously to avoid blocking list render
fetchTestDefinitionPermissions(data);
} catch (error) {
showErrorToast(error as AxiosError);
} finally {
setIsLoading(false);
}
},
[pageSize, handlePagingChange, fetchTestDefinitionPermissions]
);
useEffect(() => {
const { cursorType, cursorValue } = pagingCursor ?? {};
if (cursorType && cursorValue) {
fetchTestDefinitions({ [cursorType]: cursorValue });
} else {
fetchTestDefinitions();
}
}, [pageSize, pagingCursor]);
const handleEnableToggle = async (
record: TestDefinition,
checked: boolean
) => {
try {
const updatedData = { ...record, enabled: checked };
const patch = compare(record, updatedData);
await patchTestDefinition(record.id ?? '', patch);
showSuccessToast(
t('server.entity-updated-success', {
entity: t('label.test-definition'),
})
);
// Optimistically update the local state instead of re-fetching
setTestDefinitions((prev) =>
prev.map((item) =>
item.id === record.id
? {
...item,
enabled: checked,
}
: item
)
);
} catch (error) {
showErrorToast(error as AxiosError);
}
};
const handleEdit = (record: TestDefinition) => {
setSelectedDefinition(record);
setIsFormVisible(true);
};
const handleDeleteClick = (record: TestDefinition) => {
setDefinitionToDelete(record);
setIsDeleteModalVisible(true);
};
const handleDeleteConfirm = async () => {
if (!definitionToDelete) {
return;
}
try {
await deleteTestDefinitionByFqn(
definitionToDelete.fullyQualifiedName ?? ''
);
showSuccessToast(
t('server.entity-deleted-success', {
entity: t('label.test-definition'),
})
);
setIsDeleteModalVisible(false);
setDefinitionToDelete(undefined);
fetchTestDefinitions();
} catch (error) {
showErrorToast(error as AxiosError);
}
};
const handleDeleteCancel = () => {
setIsDeleteModalVisible(false);
setDefinitionToDelete(undefined);
};
const handleFormSuccess = () => {
setIsFormVisible(false);
setSelectedDefinition(undefined);
fetchTestDefinitions();
};
const handleFormCancel = () => {
setIsFormVisible(false);
setSelectedDefinition(undefined);
};
const columns: ColumnsType<TestDefinition> = useMemo(
() => [
{
title: t('label.name'),
dataIndex: 'name',
key: 'name',
width: 250,
render: (name: string, record: TestDefinition) => (
<Typography.Text data-testid={name}>
{getEntityName(record)}
</Typography.Text>
),
},
{
title: t('label.description'),
dataIndex: 'description',
key: 'description',
ellipsis: true,
render: (description: string) => (
<RichTextEditorPreviewerNew markdown={description} />
),
},
{
title: t('label.entity-type'),
dataIndex: 'entityType',
key: 'entityType',
width: 150,
render: (entityType: string) => (
<Typography.Text>{entityType}</Typography.Text>
),
},
{
title: t('label.test-platform-plural'),
dataIndex: 'testPlatforms',
key: 'testPlatforms',
width: 200,
render: (testPlatforms: string[]) => (
<Typography.Text>{testPlatforms?.join(', ') ?? '--'}</Typography.Text>
),
},
{
title: t('label.enabled'),
dataIndex: 'enabled',
key: 'enabled',
width: 100,
render: (enabled: boolean, record: TestDefinition) => {
const entityPermissions = testDefinitionPermissions[record.name];
const hasEditPermission = entityPermissions?.[Operation.EditAll];
if (permissionLoading || !entityPermissions) {
return (
<Skeleton.Button active size="small" style={{ width: 32 }} />
);
}
return (
<Tooltip
title={
!hasEditPermission && t('message.no-permission-for-action')
}>
<Switch
checked={enabled ?? true}
data-testid={`enable-switch-${record.name}`}
disabled={!hasEditPermission}
size="small"
onChange={(checked) => handleEnableToggle(record, checked)}
/>
</Tooltip>
);
},
},
{
title: t('label.action-plural'),
key: 'actions',
width: 120,
fixed: 'right',
render: (_, record: TestDefinition) => {
const isSystemProvider = record.provider === ProviderType.System;
const entityPermissions = testDefinitionPermissions[record.name];
const hasEditPermission = entityPermissions?.[Operation.EditAll];
const hasDeletePermission = entityPermissions?.[Operation.Delete];
if (permissionLoading || !entityPermissions) {
return (
<Skeleton.Button active size="small" style={{ width: 24 }} />
);
}
const editTooltip = isSystemProvider
? t('message.system-test-definition-edit-warning')
: !hasEditPermission
? t('message.no-permission-for-action')
: t('label.edit');
const deleteTooltip = isSystemProvider
? t('message.system-test-definition-delete-warning')
: !hasDeletePermission
? t('message.no-permission-for-action')
: t('label.delete');
return (
<Space size={0}>
<Tooltip title={editTooltip}>
<Button
data-testid={`edit-test-definition-${record.name}`}
disabled={isSystemProvider || !hasEditPermission}
icon={<IconEdit height={16} width={16} />}
type="text"
onClick={() => handleEdit(record)}
/>
</Tooltip>
<Tooltip title={deleteTooltip}>
<Button
data-testid={`delete-test-definition-${record.name}`}
disabled={isSystemProvider || !hasDeletePermission}
icon={<IconDelete height={16} width={16} />}
type="text"
onClick={() => handleDeleteClick(record)}
/>
</Tooltip>
</Space>
);
},
},
],
[t, testDefinitionPermissions]
);
const handlePageChangeCallback = ({
cursorType,
currentPage,
}: PagingHandlerParams) => {
if (cursorType && paging) {
fetchTestDefinitions({
[cursorType]: paging[cursorType],
total: paging.total,
} as Paging);
handlePageChange(
currentPage,
{ cursorType, cursorValue: paging[cursorType] },
pageSize
);
}
};
const customPaginationProps = useMemo(
() => ({
currentPage: currentPage,
pageSize: pageSize,
paging: paging,
pagingHandler: handlePageChangeCallback,
showPagination: showPagination,
}),
[
currentPage,
paging,
pageSize,
handlePagingChange,
handlePageChange,
showPagination,
]
);
if (isLoading) {
return <Loader />;
}
if (!viewPermission) {
return <ErrorPlaceHolder type={ERROR_PLACEHOLDER_TYPE.PERMISSION} />;
}
return (
<>
<Row className="p-b-md" gutter={[16, 16]}>
<Col span={24}>
<Card>
<Row justify="space-between">
<Col>
<Typography.Title level={5}>
{t('label.test-definition-plural')}
</Typography.Title>
<Typography.Text type="secondary">
{t('message.page-sub-header-for-test-definitions')}
</Typography.Text>
</Col>
{createPermission && (
<Col>
<Button
data-testid="add-test-definition-button"
type="primary"
onClick={() => setIsFormVisible(true)}>
{t('label.add-entity', {
entity: t('label.test-definition'),
})}
</Button>
</Col>
)}
</Row>
</Card>
</Col>
<Col span={24}>
{testDefinitions.length > 0 ? (
<Table
bordered
columns={columns}
customPaginationProps={customPaginationProps}
data-testid="test-definition-table"
dataSource={testDefinitions}
loading={isLoading}
pagination={false}
rowKey="id"
scroll={{ x: 1200 }}
size="small"
/>
) : (
<ErrorPlaceHolder />
)}
</Col>
</Row>
{isFormVisible && (
<TestDefinitionForm
initialValues={selectedDefinition}
onCancel={handleFormCancel}
onSuccess={handleFormSuccess}
/>
)}
<EntityDeleteModal
entityName={getEntityName(definitionToDelete)}
entityType={t('label.test-definition')}
visible={isDeleteModalVisible}
onCancel={handleDeleteCancel}
onConfirm={handleDeleteConfirm}
/>
</>
);
};
export default TestDefinitionList;

View file

@ -0,0 +1,445 @@
/*
* Copyright 2024 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 { fireEvent, render, screen, waitFor } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';
import { ResourceEntity } from '../../../context/PermissionProvider/PermissionProvider.interface';
import { ProviderType } from '../../../generated/entity/bot';
import {
deleteTestDefinitionByFqn,
getListTestDefinitions,
patchTestDefinition,
} from '../../../rest/testAPI';
import { showErrorToast, showSuccessToast } from '../../../utils/ToastUtils';
import TestDefinitionForm from '../TestDefinitionForm/TestDefinitionForm.component';
import TestDefinitionList from './TestDefinitionList.component';
const mockTestDefinitions = {
data: [
{
id: 'test-def-1',
name: 'columnValuesToBeNotNull',
fullyQualifiedName: 'columnValuesToBeNotNull',
displayName: 'Column Values To Be Not Null',
description: 'Ensures that all values in a column are not null',
entityType: 'COLUMN',
testPlatforms: ['OpenMetadata'],
enabled: true,
provider: ProviderType.User,
},
{
id: 'test-def-2',
name: 'tableRowCountToBeBetween',
fullyQualifiedName: 'tableRowCountToBeBetween',
displayName: 'Table Row Count To Be Between',
description: 'Ensures table row count is between min and max values',
entityType: 'TABLE',
testPlatforms: ['OpenMetadata', 'DBT'],
enabled: false,
provider: ProviderType.System,
},
],
paging: {
total: 2,
},
};
jest.mock('../../../rest/testAPI', () => ({
getListTestDefinitions: jest
.fn()
.mockImplementation(() => Promise.resolve(mockTestDefinitions)),
patchTestDefinition: jest.fn().mockImplementation(() => Promise.resolve({})),
deleteTestDefinitionByFqn: jest
.fn()
.mockImplementation(() => Promise.resolve({})),
}));
jest.mock('../../../utils/ToastUtils', () => ({
showSuccessToast: jest.fn(),
showErrorToast: jest.fn(),
}));
jest.mock('../TestDefinitionForm/TestDefinitionForm.component', () => ({
__esModule: true,
default: jest
.fn()
.mockImplementation(() => (
<div data-testid="test-definition-form">TestDefinitionForm</div>
)),
}));
jest.mock('../../../components/PageLayoutV1/PageLayoutV1', () => ({
__esModule: true,
default: jest
.fn()
.mockImplementation(({ children }) => (
<div data-testid="page-layout">{children}</div>
)),
}));
jest.mock('../../../hooks/paging/usePaging', () => ({
usePaging: jest.fn().mockReturnValue({
currentPage: 1,
pageSize: 15,
paging: { total: 2 },
handlePagingChange: jest.fn(),
handlePageChange: jest.fn(),
handlePageSizeChange: jest.fn(),
showPagination: false,
}),
}));
jest.mock('../../../context/PermissionProvider/PermissionProvider', () => ({
usePermissionProvider: jest.fn().mockReturnValue({
getEntityPermissionByFqn: jest.fn().mockResolvedValue({
Create: true,
Delete: true,
ViewAll: true,
ViewBasic: true,
EditAll: true,
}),
permissions: {
testDefinition: {
Create: true,
Delete: true,
ViewAll: true,
ViewBasic: true,
EditAll: true,
},
},
}),
}));
jest.mock('../../Modals/EntityDeleteModal/EntityDeleteModal', () => ({
__esModule: true,
default: jest.fn().mockImplementation(({ visible, onConfirm, onCancel }) =>
visible ? (
<div data-testid="entity-delete-modal">
<button onClick={onCancel}>Cancel</button>
<button onClick={onConfirm}>Confirm</button>
</div>
) : null
),
}));
describe('TestDefinitionList Component', () => {
beforeEach(() => {
jest.clearAllMocks();
});
it('should render component with test definitions table', async () => {
render(<TestDefinitionList />, { wrapper: MemoryRouter });
await waitFor(() => {
expect(screen.getByTestId('test-definition-table')).toBeInTheDocument();
});
expect(
screen.getByText('Column Values To Be Not Null')
).toBeInTheDocument();
expect(
screen.getByText('Table Row Count To Be Between')
).toBeInTheDocument();
});
it('should render all table columns', async () => {
render(<TestDefinitionList />, { wrapper: MemoryRouter });
await waitFor(() => {
const tableHeaders = screen.getAllByRole('columnheader');
const labels = tableHeaders.map((header) => header.textContent);
expect(labels).toContain('label.name');
expect(labels).toContain('label.description');
expect(labels).toContain('label.entity-type');
expect(labels).toContain('label.test-platform-plural');
expect(labels).toContain('label.enabled');
expect(labels).toContain('label.action-plural');
});
});
it('should fetch test definitions on mount', async () => {
render(<TestDefinitionList />, { wrapper: MemoryRouter });
await waitFor(() => {
expect(getListTestDefinitions).toHaveBeenCalledWith({
after: undefined,
before: undefined,
limit: 15,
});
});
});
it('should render enabled switch for each test definition', async () => {
render(<TestDefinitionList />, { wrapper: MemoryRouter });
const switches = await screen.findAllByRole('switch');
expect(switches).toHaveLength(2);
});
it('should call patchTestDefinition when enable switch is toggled', async () => {
render(<TestDefinitionList />, { wrapper: MemoryRouter });
await waitFor(() => {
expect(screen.getByTestId('test-definition-table')).toBeInTheDocument();
});
const switches = await screen.findAllByRole('switch');
fireEvent.click(switches[1]);
await waitFor(() => {
expect(patchTestDefinition).toHaveBeenCalled();
expect(showSuccessToast).toHaveBeenCalled();
});
});
it('should render edit and delete action buttons', async () => {
render(<TestDefinitionList />, { wrapper: MemoryRouter });
const editButtons = await screen.findAllByTestId(/edit-test-definition-/);
const deleteButtons = await screen.findAllByTestId(
/delete-test-definition-/
);
expect(editButtons).toHaveLength(2);
expect(deleteButtons).toHaveLength(2);
});
it('should open form drawer when edit button is clicked', async () => {
render(<TestDefinitionList />, { wrapper: MemoryRouter });
await waitFor(() => {
const editButtons = screen.getAllByTestId(/edit-test-definition-/);
fireEvent.click(editButtons[0]);
});
await waitFor(() => {
expect(screen.getByTestId('test-definition-form')).toBeInTheDocument();
});
});
it('should show delete confirmation modal when delete button is clicked', async () => {
render(<TestDefinitionList />, { wrapper: MemoryRouter });
await waitFor(() => {
const deleteButtons = screen.getAllByTestId(/delete-test-definition-/);
fireEvent.click(deleteButtons[0]);
});
await waitFor(() => {
expect(screen.getByTestId('entity-delete-modal')).toBeInTheDocument();
});
});
it('should call deleteTestDefinitionByFqn when delete is confirmed', async () => {
render(<TestDefinitionList />, { wrapper: MemoryRouter });
await waitFor(() => {
const deleteButtons = screen.getAllByTestId(/delete-test-definition-/);
fireEvent.click(deleteButtons[0]);
});
await waitFor(() => {
const confirmButton = screen.getByText('Confirm');
fireEvent.click(confirmButton);
});
await waitFor(() => {
expect(deleteTestDefinitionByFqn).toHaveBeenCalledWith(
'columnValuesToBeNotNull'
);
expect(showSuccessToast).toHaveBeenCalled();
});
});
it('should render add test definition button', async () => {
render(<TestDefinitionList />, { wrapper: MemoryRouter });
await waitFor(() => {
expect(
screen.getByTestId('add-test-definition-button')
).toBeInTheDocument();
});
});
it('should open form drawer when add button is clicked', async () => {
render(<TestDefinitionList />, { wrapper: MemoryRouter });
const addButton = await screen.findByTestId('add-test-definition-button');
fireEvent.click(addButton);
await waitFor(() => {
expect(screen.getByTestId('test-definition-form')).toBeInTheDocument();
});
});
it('should show error toast when API call fails', async () => {
const mockError = new Error('API Error');
(getListTestDefinitions as jest.Mock).mockRejectedValueOnce(mockError);
render(<TestDefinitionList />, { wrapper: MemoryRouter });
await waitFor(() => {
expect(showErrorToast).toHaveBeenCalledWith(mockError);
});
});
it('should refresh list after successful create/update', async () => {
render(<TestDefinitionList />, { wrapper: MemoryRouter });
const addButton = await screen.findByTestId('add-test-definition-button');
fireEvent.click(addButton);
await waitFor(() => {
expect(screen.getByTestId('test-definition-form')).toBeInTheDocument();
});
const initialCallCount = (getListTestDefinitions as jest.Mock).mock.calls
.length;
const onSuccessCallback = (TestDefinitionForm as jest.Mock).mock.calls[0][0]
.onSuccess;
onSuccessCallback();
await waitFor(() => {
expect(getListTestDefinitions).toHaveBeenCalledTimes(
initialCallCount + 1
);
});
});
it('should disable edit and delete buttons for System test definitions', async () => {
render(<TestDefinitionList />, { wrapper: MemoryRouter });
await waitFor(() => {
const editButtons = screen.getAllByTestId(/edit-test-definition-/);
const deleteButtons = screen.getAllByTestId(/delete-test-definition-/);
// First definition is User provider - should be enabled
expect(editButtons[0]).not.toBeDisabled();
expect(deleteButtons[0]).not.toBeDisabled();
// Second definition is System provider - should be disabled
expect(editButtons[1]).toBeDisabled();
expect(deleteButtons[1]).toBeDisabled();
});
});
it('should not fetch permissions for System test definitions', async () => {
const mockGetEntityPermissionByFqn = jest.fn().mockResolvedValue({
Create: true,
Delete: true,
ViewAll: true,
ViewBasic: true,
EditAll: true,
});
const { usePermissionProvider } = jest.requireMock(
'../../../context/PermissionProvider/PermissionProvider'
);
(usePermissionProvider as jest.Mock).mockReturnValue({
getEntityPermissionByFqn: mockGetEntityPermissionByFqn,
permissions: {
testDefinition: {
Create: true,
Delete: true,
ViewAll: true,
ViewBasic: true,
EditAll: true,
},
},
});
render(<TestDefinitionList />, { wrapper: MemoryRouter });
await waitFor(() => {
// Permissions fetched for all definitions (including system)
expect(mockGetEntityPermissionByFqn).toHaveBeenCalledTimes(2);
expect(mockGetEntityPermissionByFqn).toHaveBeenCalledWith(
ResourceEntity.TEST_DEFINITION,
'columnValuesToBeNotNull'
);
expect(mockGetEntityPermissionByFqn).toHaveBeenCalledWith(
ResourceEntity.TEST_DEFINITION,
'tableRowCountToBeBetween'
);
});
});
it('should disable enabled switch when user lacks EditAll permission', async () => {
const { usePermissionProvider } = jest.requireMock(
'../../../context/PermissionProvider/PermissionProvider'
);
(usePermissionProvider as jest.Mock).mockReturnValue({
getEntityPermissionByFqn: jest.fn().mockResolvedValue({
Create: false,
Delete: false,
ViewAll: true,
ViewBasic: true,
EditAll: false,
}),
permissions: {
testDefinition: {
Create: true,
Delete: true,
ViewAll: true,
ViewBasic: true,
EditAll: true,
},
},
});
render(<TestDefinitionList />, { wrapper: MemoryRouter });
const switches = await screen.findAllByRole('switch');
// First definition should be disabled due to lack of EditAll permission
expect(switches[0]).toBeDisabled();
});
it('should enable switch when user has EditAll permission', async () => {
const { usePermissionProvider } = jest.requireMock(
'../../../context/PermissionProvider/PermissionProvider'
);
(usePermissionProvider as jest.Mock).mockReturnValue({
getEntityPermissionByFqn: jest.fn().mockResolvedValue({
Create: true,
Delete: true,
ViewAll: true,
ViewBasic: true,
EditAll: true,
}),
permissions: {
testDefinition: {
Create: true,
Delete: true,
ViewAll: true,
ViewBasic: true,
EditAll: true,
},
},
});
render(<TestDefinitionList />, { wrapper: MemoryRouter });
const switches = await screen.findAllByRole('switch');
// Both definitions should be enabled with EditAll permission
expect(switches[0]).not.toBeDisabled();
expect(switches[1]).not.toBeDisabled();
});
});

View file

@ -95,6 +95,13 @@ export const SIDEBAR_LIST: Array<LeftSidebarItem> = [
icon: AlertIcon,
dataTestId: `app-bar-item-${SidebarItem.OBSERVABILITY_ALERT}`,
},
{
key: ROUTES.RULES_LIBRARY,
title: 'label.rules-library',
redirect_url: ROUTES.RULES_LIBRARY,
icon: DataQualityIcon,
dataTestId: `app-bar-item-rules-library`,
},
],
},
{

View file

@ -303,6 +303,7 @@ export const ROUTES = {
OBSERVABILITY_ALERT_DETAILS_WITH_TAB: `/observability/alert/${PLACEHOLDER_ROUTE_FQN}/${PLACEHOLDER_ROUTE_TAB}`,
ADD_OBSERVABILITY_ALERTS: '/observability/alerts/add',
EDIT_OBSERVABILITY_ALERTS: `/observability/alerts/edit/${PLACEHOLDER_ROUTE_FQN}`,
RULES_LIBRARY: '/rules-library',
// Notification Alerts
NOTIFICATIONS: `/settings/${GlobalSettingsMenuCategory.NOTIFICATIONS}`,

View file

@ -182,6 +182,7 @@ export enum Operation {
EditStatus = "EditStatus",
EditTags = "EditTags",
EditTeams = "EditTeams",
EditTestDefinitionLibrary = "EditTestDefinitionLibrary",
EditTests = "EditTests",
EditTier = "EditTier",
EditUsage = "EditUsage",
@ -200,6 +201,7 @@ export enum Operation {
ViewSampleData = "ViewSampleData",
ViewScim = "ViewScim",
ViewTestCaseFailedRowsSample = "ViewTestCaseFailedRowsSample",
ViewTestDefinitionLibrary = "ViewTestDefinitionLibrary",
ViewTests = "ViewTests",
ViewUsage = "ViewUsage",
}

View file

@ -14,6 +14,7 @@
* Schema corresponding to a Test Definition
*/
export interface CreateTestDefinition {
dataQualityDimension?: DataQualityDimensions;
/**
* Description of the testcase.
*/
@ -37,7 +38,13 @@ export interface CreateTestDefinition {
owners?: EntityReference[];
parameterDefinition?: TestCaseParameterDefinition[];
provider?: ProviderType;
supportedDataTypes?: DataType[];
/**
* SQL expression template for custom SQL-based test definitions. Supports substitution
* variables: {table} and {column} for runtime entity references, and {{paramName}} for
* user-defined parameters.
*/
sqlExpression?: string;
supportedDataTypes?: DataType[];
/**
* List of services that this test definition supports. When empty, it implies all services
* are supported.
@ -46,6 +53,20 @@ export interface CreateTestDefinition {
testPlatforms: TestPlatform[];
}
/**
* This enum defines the dimension a test case belongs to.
*/
export enum DataQualityDimensions {
Accuracy = "Accuracy",
Completeness = "Completeness",
Consistency = "Consistency",
Integrity = "Integrity",
NoDimension = "NoDimension",
SQL = "SQL",
Uniqueness = "Uniqueness",
Validity = "Validity",
}
/**
* This enum defines the type for which this test definition applies to.
*/

View file

@ -64,6 +64,7 @@ export enum Operation {
EditStatus = "EditStatus",
EditTags = "EditTags",
EditTeams = "EditTeams",
EditTestDefinitionLibrary = "EditTestDefinitionLibrary",
EditTests = "EditTests",
EditTier = "EditTier",
EditUsage = "EditUsage",
@ -82,6 +83,7 @@ export enum Operation {
ViewSampleData = "ViewSampleData",
ViewScim = "ViewScim",
ViewTestCaseFailedRowsSample = "ViewTestCaseFailedRowsSample",
ViewTestDefinitionLibrary = "ViewTestDefinitionLibrary",
ViewTests = "ViewTests",
ViewUsage = "ViewUsage",
}

View file

@ -106,6 +106,7 @@ export enum Operation {
EditStatus = "EditStatus",
EditTags = "EditTags",
EditTeams = "EditTeams",
EditTestDefinitionLibrary = "EditTestDefinitionLibrary",
EditTests = "EditTests",
EditTier = "EditTier",
EditUsage = "EditUsage",
@ -124,6 +125,7 @@ export enum Operation {
ViewSampleData = "ViewSampleData",
ViewScim = "ViewScim",
ViewTestCaseFailedRowsSample = "ViewTestCaseFailedRowsSample",
ViewTestDefinitionLibrary = "ViewTestDefinitionLibrary",
ViewTests = "ViewTests",
ViewUsage = "ViewUsage",
}

View file

@ -89,6 +89,7 @@ export enum Operation {
EditStatus = "EditStatus",
EditTags = "EditTags",
EditTeams = "EditTeams",
EditTestDefinitionLibrary = "EditTestDefinitionLibrary",
EditTests = "EditTests",
EditTier = "EditTier",
EditUsage = "EditUsage",
@ -107,6 +108,7 @@ export enum Operation {
ViewSampleData = "ViewSampleData",
ViewScim = "ViewScim",
ViewTestCaseFailedRowsSample = "ViewTestCaseFailedRowsSample",
ViewTestDefinitionLibrary = "ViewTestDefinitionLibrary",
ViewTests = "ViewTests",
ViewUsage = "ViewUsage",
}

View file

@ -334,6 +334,7 @@ export enum Operation {
EditStatus = "EditStatus",
EditTags = "EditTags",
EditTeams = "EditTeams",
EditTestDefinitionLibrary = "EditTestDefinitionLibrary",
EditTests = "EditTests",
EditTier = "EditTier",
EditUsage = "EditUsage",
@ -352,6 +353,7 @@ export enum Operation {
ViewSampleData = "ViewSampleData",
ViewScim = "ViewScim",
ViewTestCaseFailedRowsSample = "ViewTestCaseFailedRowsSample",
ViewTestDefinitionLibrary = "ViewTestDefinitionLibrary",
ViewTests = "ViewTests",
ViewUsage = "ViewUsage",
}

View file

@ -36,7 +36,12 @@ export interface TestDefinition {
* Domains the asset belongs to. When not set, the asset inherits the domain from the parent
* it belongs to.
*/
domains?: EntityReference[];
domains?: EntityReference[];
/**
* When `true` indicates the test definition is available for creating test cases. System
* test definitions can only be disabled by users with appropriate permissions.
*/
enabled?: boolean;
entityType?: EntityType;
/**
* FullyQualifiedName same as `name`.
@ -64,7 +69,15 @@ export interface TestDefinition {
owners?: EntityReference[];
parameterDefinition?: TestCaseParameterDefinition[];
provider?: ProviderType;
supportedDataTypes?: DataType[];
/**
* SQL expression template for custom SQL-based test definitions. Supports substitution
* variables: {table} and {column} for runtime entity references, and {{paramName}} for
* user-defined parameters. This field is only applicable for test definitions with
* testPlatforms set to 'OpenMetadata' and is used to execute custom SQL queries for data
* quality validation.
*/
sqlExpression?: string;
supportedDataTypes?: DataType[];
/**
* List of services that this test definition supports. When empty, it implies all services
* are supported.

View file

@ -431,6 +431,7 @@
"data-profiler-metrics": "مقاييس محلل البيانات",
"data-proportion-plural": "نسب البيانات",
"data-quality": "جودة البيانات",
"data-quality-dimension": "Data Quality Dimension",
"data-quality-test": "اختبار جودة البيانات",
"data-quality-test-plural": "اختبارات جودة البيانات",
"data-quartile-plural": "رباعيات البيانات",
@ -613,6 +614,7 @@
"enter-custom-unit-of-measurement": "أدخل وحدة قياس مخصصة واضغط زر الإدخال",
"enter-each-value-and-press-enter": "أدخل كل قيمة ثم اضغط Enter",
"enter-entity": "أدخل {{entity}}",
"enter-entity-description": "Enter {{entity}} description",
"enter-entity-name": "أدخل اسم {{entity}}",
"enter-entity-value": "أدخل قيمة {{entity}}",
"enter-field-description": "أدخل وصف {{field}}",
@ -1253,6 +1255,9 @@
"page-not-found": "الصفحة غير موجودة",
"page-views-by-data-asset-plural": "مشاهدات الصفحة حسب أصول البيانات",
"parameter": "معامل",
"parameter-description": "Parameter Description",
"parameter-display-name": "Parameter Display Name",
"parameter-name": "Parameter Name",
"parameter-plural": "معاملات",
"parent": "الأصل",
"parsing-timeout-limit": "حد مهلة تحليل الاستعلام",
@ -1436,6 +1441,7 @@
"request-method": "طريقة الطلب",
"request-schema-field": "حقل مخطط الطلب",
"request-tag-plural": "طلب وسوم",
"required": "Required",
"requirement-plural": "متطلبات",
"reset": "إعادة تعيين",
"reset-default-layout": "إعادة تعيين التخطيط الافتراضي",
@ -1495,6 +1501,7 @@
"rule-with-name": "- القاعدة: {{ruleName}}",
"rules-count": "({{count}} قواعد)",
"rules-evaluated": "القواعد التي تم تقييمها",
"rules-library": "Rules Library",
"run": "تشغيل",
"run-agent-plural": "تشغيل الوكلاء",
"run-at": "تشغيل في",
@ -1645,7 +1652,9 @@
"specific-data-asset-plural": "أصول بيانات محددة",
"spreadsheet": "جدول بيانات",
"spreadsheet-plural": "جداول بيانات",
"sql-expression": "SQL Expression",
"sql-function": "دالة SQL",
"sql-query": "SQL Query",
"sql-uppercase": "SQL",
"sql-uppercase-query": "استعلام SQL",
"sso": "SSO",
@ -1705,6 +1714,7 @@
"sunday": "الأحد",
"support": "دعم",
"support-url": "رابط الدعم",
"supported-data-type-plural": "Supported Data Types",
"supported-language-plural": "اللغات المدعومة",
"switch-persona": "تبديل الشخصية",
"sync-alert-offset": "مزامنة التنبيه",
@ -1770,10 +1780,13 @@
"test-case-plural": "حالات اختبار",
"test-case-resolution-status": "حالة قرار حالة الاختبار",
"test-case-result": "نتائج حالة الاختبار",
"test-definition": "Test Definition",
"test-definition-plural": "Test Definitions",
"test-email": "اختبار البريد الإلكتروني",
"test-email-connection": "اختبار اتصال البريد الإلكتروني",
"test-entity": "اختبار {{entity}}",
"test-level-lowercase": "مستوى الاختبار",
"test-platform-plural": "Test Platforms",
"test-plural": "اختبارات",
"test-plural-type": "{{type}} اختبارات",
"test-suite": "مجموعة اختبار",
@ -2534,6 +2547,7 @@
"page-sub-header-for-storages": "استيعاب البيانات الوصفية من خدمات التخزين الأكثر شيوعًا.",
"page-sub-header-for-table-profile": "راقب وافهم هيكل جداولك باستخدام المُحلل (profiler).",
"page-sub-header-for-teams": "مثل هيكلك التنظيمي بالكامل بفرق هرمية.",
"page-sub-header-for-test-definitions": "Manage and configure test definitions for data quality rules.",
"page-sub-header-for-users": "عرض وإدارة المستخدمين العاديين في مؤسستك. بالنسبة للمستخدمين المسؤولين، يرجى زيارة صفحة المسؤول.",
"paid-addon-description": "<0>{{app}}</0> هو إضافة مدفوعة لعملاء Collate.",
"passed-x-checks": "اجتاز {{count}} من عمليات التحقق",
@ -2660,6 +2674,8 @@
"synonym-placeholder": "لإضافة مرادف، ما عليك سوى كتابته والضغط على Enter",
"system-alert-edit-message": "لا يُسمح بتحرير تنبيه تم إنشاؤه بواسطة النظام.",
"system-tag-delete-disable-message": "لا يُسمح بحذف علامات تم إنشاؤها بواسطة النظام. يمكنك محاولة تعطيل العلامة بدلاً من ذلك.",
"system-test-definition-delete-warning": "Deleting a system generated test definition is not allowed.",
"system-test-definition-edit-warning": "Editing a system generated test definition is not allowed.",
"tag-update-confirmation": "هل ترغب في المتابعة بتحديث العلامات؟",
"tailor-experience-for-persona": "صمم التجربة خصيصًا لشخصية {{persona}}، فقط جربها!",
"take-quick-product-tour": "قم بجولة سريعة في المنتج لتبدأ!",
@ -2675,6 +2691,8 @@
"default": "افتراضي",
"withIp": " من {{ip}}"
},
"test-definition-parameters-description": "Defines configurable input parameters for test cases. Each parameter includes a name, data type (string, int, date, etc.), display name, and optional validation rules. Parameters can be referenced in SQL expressions using {{paramName}} syntax and allow users to customize test behavior when creating test cases.",
"test-definition-sql-query-help": "SQL expression template for custom SQL-based test definitions. Supports substitution variables: {table} and {column} for runtime entity references, and {{paramName}} for user-defined parameters. This field is only applicable for test definitions with testPlatforms set to 'OpenMetadata' and is used to execute custom SQL queries for data quality validation.",
"test-your-connection-before-creating-service": "اختبر اتصالاتك قبل إنشاء الخدمة",
"testing-your-connection-may-take-two-minutes": "قد يستغرق اختبار اتصالاتك ما يصل إلى دقيقتين",
"this-action-is-not-allowed-for-deleted-entities": "هذا الإجراء غير مسموح به للكيانات المحذوفة.",
@ -2774,7 +2792,9 @@
"entities-deleted-successfully": "تم حذف {{count}} {{entity}}s بنجاح",
"entity-already-exist": "{{entity}} \"{{name}}\" موجود بالفعل. لا يُسمح بتكرار {{entityPlural}}.",
"entity-already-exist-message-without-name": "يوجد بالفعل {{entity}} بالتفاصيل المُعطاة. لا يُسمح بتكرار {{entityPlural}}.",
"entity-created-success": "{{entity}} created successfully!",
"entity-creation-error": "خطأ أثناء إنشاء {{entity}}",
"entity-deleted-success": "{{entity}} deleted successfully!",
"entity-deleted-successfully": "تم حذف \"{{entity}}\" بنجاح!",
"entity-details-fetch-error": "خطأ أثناء جلب التفاصيل لـ {{entityType}} {{entityName}}",
"entity-feed-fetch-error": "خطأ أثناء جلب عدد موجز الكيان!",
@ -2784,6 +2804,7 @@
"entity-limit-reached": "تم الوصول إلى حد {{entity}}",
"entity-removing-error": "خطأ أثناء إزالة {{entity}}",
"entity-unfollow-error": "خطأ أثناء إلغاء متابعة {{entity}}",
"entity-updated-success": "{{entity}} updated successfully!",
"entity-updating-error": "خطأ أثناء تحديث {{entity}}",
"error-selected-node-name-details": "خطأ أثناء الحصول على تفاصيل {{selectedNodeName}}",
"error-while-renewing-id-token-with-message": "خطأ أثناء تجديد رمز الهوية المميز من Auth0 SSO: {{message}}",
@ -2824,6 +2845,10 @@
"task-resolved-successfully": "تم حل المهمة بنجاح",
"team-moved-error": "خطأ أثناء نقل الفريق",
"test-connection-error": "خطأ أثناء اختبار الاتصال!",
"test-definition-parameters-description": "Define parameters that users can configure when creating test cases from this definition. Parameters allow for flexible and reusable test definitions.",
"test-definition-sql-expression-placeholder": "SELECT * FROM {table} WHERE {column} < {{minValue}} OR {column} > {{maxValue}}",
"test-definition-sql-expression-tooltip": "SQL query template using parameter placeholders in double curly braces (e.g., {{paramName}}). Use {table} and {column} for runtime entity references.",
"test-definition-sql-query-help": "Write SQL query template with substitution variables. Use {table} for table name, {column} for column name (resolved at runtime). Use {{paramName}} for user parameters defined below (e.g., {{minValue}}, {{maxValue}}).",
"this-action-cannot-be-undone": "لا يمكن التراجع عن هذا الإجراء.",
"unauthorized-user": "مستخدم غير مصرح به! يرجى التحقق من البريد الإلكتروني أو كلمة المرور",
"unexpected-error": "حدث خطأ غير متوقع.",

View file

@ -431,6 +431,7 @@
"data-profiler-metrics": "Data Profiler Metriken",
"data-proportion-plural": "Datenverhältnisse",
"data-quality": "Datenqualität",
"data-quality-dimension": "Data Quality Dimension",
"data-quality-test": "Datenqualitätstest",
"data-quality-test-plural": "Datenqualitätstests",
"data-quartile-plural": "Datenquartile",
@ -613,6 +614,7 @@
"enter-custom-unit-of-measurement": "Geben Sie die benutzerdefinierte Maßeinheit ein und drücken Sie Enter",
"enter-each-value-and-press-enter": "Geben Sie jeden Wert ein und drücken Sie die Eingabetaste",
"enter-entity": "{{entity}} eingeben",
"enter-entity-description": "Enter {{entity}} description",
"enter-entity-name": "Geben Sie einen Namen für {{entity}} ein",
"enter-entity-value": "Enter {{entity}} Value",
"enter-field-description": "Geben Sie eine Beschreibung für {{field}} ein",
@ -1253,6 +1255,9 @@
"page-not-found": "Seite nicht gefunden",
"page-views-by-data-asset-plural": "Seitenaufrufe nach Datenanlage",
"parameter": "Parameter",
"parameter-description": "Parameter Description",
"parameter-display-name": "Parameter Display Name",
"parameter-name": "Parameter Name",
"parameter-plural": "Parameter",
"parent": "Übergeordnet",
"parsing-timeout-limit": "Analyse-Timeout-Limit",
@ -1436,6 +1441,7 @@
"request-method": "Anfrage Methode",
"request-schema-field": "Anfrage Feld Schema",
"request-tag-plural": "Tag-Anfragen",
"required": "Required",
"requirement-plural": "Anforderungen",
"reset": "Zurücksetzen",
"reset-default-layout": "Standard Layout zurücksetzen",
@ -1495,6 +1501,7 @@
"rule-with-name": "- Regel: {{ruleName}}",
"rules-count": "({{count}} Regeln)",
"rules-evaluated": "Regeln ausgewertet",
"rules-library": "Rules Library",
"run": "Ausführen",
"run-agent-plural": "Agenten ausführen",
"run-at": "Ausführen um",
@ -1645,7 +1652,9 @@
"specific-data-asset-plural": "Spezifische Datenbestände",
"spreadsheet": "Tabellenkalkulation",
"spreadsheet-plural": "Tabellenkalkulationen",
"sql-expression": "SQL Expression",
"sql-function": "SQL-Funktion",
"sql-query": "SQL Query",
"sql-uppercase": "SQL",
"sql-uppercase-query": "SQL-Abfrage",
"sso": "sso",
@ -1705,6 +1714,7 @@
"sunday": "Sonntag",
"support": "Unterstützung",
"support-url": "Support-URL",
"supported-data-type-plural": "Supported Data Types",
"supported-language-plural": "Unterstützte Sprachen",
"switch-persona": "Persona wechseln",
"sync-alert-offset": "Warnung synchronisieren",
@ -1770,10 +1780,13 @@
"test-case-plural": "Testfälle",
"test-case-resolution-status": "Testfall-Auflösungsstatus",
"test-case-result": "Testfall Ergebnisse",
"test-definition": "Test Definition",
"test-definition-plural": "Test Definitions",
"test-email": "Test Email",
"test-email-connection": "Email-Connection testen",
"test-entity": "{{entity}}-Test",
"test-level-lowercase": "testebene",
"test-platform-plural": "Test Platforms",
"test-plural": "Tests",
"test-plural-type": "{{type}}-Tests",
"test-suite": "Test-Suite",
@ -2534,6 +2547,7 @@
"page-sub-header-for-storages": "Ingestion von Metadaten aus den am häufigsten verwendeten Speicherdiensten.",
"page-sub-header-for-table-profile": "Überwache und verstehe die Struktur deiner Tabellen mit dem Profiler.",
"page-sub-header-for-teams": "Stelle deine gesamte Organisationsstruktur mit hierarchischen Teams dar.",
"page-sub-header-for-test-definitions": "Manage and configure test definitions for data quality rules.",
"page-sub-header-for-users": "Verwalten Sie reguläre Benutzer in Ihrer Organisation. Für Administratoren besuchen Sie bitte die Administratorseite.",
"paid-addon-description": "<0>{{app}}</0> ist ein kostenpflichtiges Add-on für Collate-Kunden.",
"passed-x-checks": "{{count}} Prüfungen bestanden",
@ -2660,6 +2674,8 @@
"synonym-placeholder": "Um ein Synonym hinzuzufügen, geben Sie es einfach ein und drücken Sie Enter",
"system-alert-edit-message": "Das Bearbeiten eines systemgenerierten Alarms ist nicht erlaubt.",
"system-tag-delete-disable-message": "Das Löschen von systemgenerierten Tags ist nicht zulässig. Sie können versuchen, den Tag stattdessen zu deaktivieren.",
"system-test-definition-delete-warning": "Deleting a system generated test definition is not allowed.",
"system-test-definition-edit-warning": "Editing a system generated test definition is not allowed.",
"tag-update-confirmation": "Möchten Sie mit der Aktualisierung der Tags fortfahren?",
"tailor-experience-for-persona": "Passen Sie die Erfahrung speziell für das {{persona}}-Persona an, probieren Sie es aus!",
"take-quick-product-tour": "Machen Sie eine Produkttour, um loszulegen!",
@ -2675,6 +2691,8 @@
"default": "Bitte stellen Sie sicher, dass {{service_type}} so konfiguriert ist, dass Verbindungen erlaubt sind",
"withIp": " von {{ip}}"
},
"test-definition-parameters-description": "Defines configurable input parameters for test cases. Each parameter includes a name, data type (string, int, date, etc.), display name, and optional validation rules. Parameters can be referenced in SQL expressions using {{paramName}} syntax and allow users to customize test behavior when creating test cases.",
"test-definition-sql-query-help": "SQL expression template for custom SQL-based test definitions. Supports substitution variables: {table} and {column} for runtime entity references, and {{paramName}} for user-defined parameters. This field is only applicable for test definitions with testPlatforms set to 'OpenMetadata' and is used to execute custom SQL queries for data quality validation.",
"test-your-connection-before-creating-service": "Testen Sie Ihre Verbindungen, bevor Sie den Dienst erstellen",
"testing-your-connection-may-take-two-minutes": "Das Testen Ihrer Verbindungen kann bis zu 2 Minuten dauern",
"this-action-is-not-allowed-for-deleted-entities": "Diese Aktion ist für gelöschte Entitäten nicht erlaubt.",
@ -2774,7 +2792,9 @@
"entities-deleted-successfully": "{{count}} {{entity}}s erfolgreich gelöscht",
"entity-already-exist": "{{entity}} \"{{name}}\" existiert bereits. Duplizierte {{entityPlural}} sind nicht erlaubt.",
"entity-already-exist-message-without-name": "Ein {{entity}} mit den angegebenen Details existiert bereits. Duplizierte {{entityPlural}} sind nicht erlaubt.",
"entity-created-success": "{{entity}} created successfully!",
"entity-creation-error": "Fehler beim Erstellen von {{entity}}",
"entity-deleted-success": "{{entity}} deleted successfully!",
"entity-deleted-successfully": "{{entity}} erfolgreich gelöscht!",
"entity-details-fetch-error": "Fehler beim Abrufen von Details für {{entityType}} {{entityName}}",
"entity-feed-fetch-error": "Fehler beim Abrufen der Anzahl der Entitätsnachrichten!",
@ -2784,6 +2804,7 @@
"entity-limit-reached": "{{entity}} Limit erreicht",
"entity-removing-error": "Fehler beim Entfernen von {{entity}}",
"entity-unfollow-error": "Fehler beim Nicht-Folgen von {{entity}}",
"entity-updated-success": "{{entity}} updated successfully!",
"entity-updating-error": "Fehler beim Aktualisieren von {{entity}}",
"error-selected-node-name-details": "Fehler beim Abrufen von Details für {{selectedNodeName}}",
"error-while-renewing-id-token-with-message": "Fehler beim Erneuern des ID-Tokens von Auth0 SSO: {{message}}",
@ -2824,6 +2845,10 @@
"task-resolved-successfully": "Aufgabe erfolgreich gelöst",
"team-moved-error": "Fehler beim Verschieben des Teams",
"test-connection-error": "Fehler beim Testen der Verbindung!",
"test-definition-parameters-description": "Define parameters that users can configure when creating test cases from this definition. Parameters allow for flexible and reusable test definitions.",
"test-definition-sql-expression-placeholder": "SELECT * FROM {table} WHERE {column} < {{minValue}} OR {column} > {{maxValue}}",
"test-definition-sql-expression-tooltip": "SQL query template using parameter placeholders in double curly braces (e.g., {{paramName}}). Use {table} and {column} for runtime entity references.",
"test-definition-sql-query-help": "Write SQL query template with substitution variables. Use {table} for table name, {column} for column name (resolved at runtime). Use {{paramName}} for user parameters defined below (e.g., {{minValue}}, {{maxValue}}).",
"this-action-cannot-be-undone": "This action cannot be undone.",
"unauthorized-user": "Unbefugter Benutzer! Bitte überprüfen Sie E-Mail oder Passwort.",
"unexpected-error": "Ein unerwarteter Fehler ist aufgetreten.",

View file

@ -431,6 +431,7 @@
"data-profiler-metrics": "Data Profiler Metrics",
"data-proportion-plural": "Data Proportions",
"data-quality": "Data Quality",
"data-quality-dimension": "Data Quality Dimension",
"data-quality-test": "Data Quality Test",
"data-quality-test-plural": "Data Quality Tests",
"data-quartile-plural": "Data Quartiles",
@ -613,6 +614,7 @@
"enter-custom-unit-of-measurement": "Enter Custom Measurement Unit and Press Enter",
"enter-each-value-and-press-enter": "Enter each value and press Enter",
"enter-entity": "Enter {{entity}}",
"enter-entity-description": "Enter {{entity}} description",
"enter-entity-name": "Enter {{entity}} name",
"enter-entity-value": "Enter {{entity}} Value",
"enter-field-description": "Enter {{field}} description",
@ -1253,6 +1255,9 @@
"page-not-found": "Page Not Found",
"page-views-by-data-asset-plural": "Page Views by Data Assets",
"parameter": "Parameter",
"parameter-description": "Parameter Description",
"parameter-display-name": "Parameter Display Name",
"parameter-name": "Parameter Name",
"parameter-plural": "Parameters",
"parent": "Parent",
"parsing-timeout-limit": "Query Parsing Timeout Limit",
@ -1436,6 +1441,7 @@
"request-method": "Request Method",
"request-schema-field": "Request Schema Field",
"request-tag-plural": "Request Tags",
"required": "Required",
"requirement-plural": "Requirements",
"reset": "Reset",
"reset-default-layout": "Reset Default Layout",
@ -1495,6 +1501,7 @@
"rule-with-name": "- Rule: {{ruleName}}",
"rules-count": "({{count}} rules)",
"rules-evaluated": "Rules Evaluated",
"rules-library": "Rules Library",
"run": "Run",
"run-agent-plural": "Run Agents",
"run-at": "Run at",
@ -1645,7 +1652,9 @@
"specific-data-asset-plural": "Specific Data Assets",
"spreadsheet": "Spreadsheet",
"spreadsheet-plural": "Spreadsheets",
"sql-expression": "SQL Expression",
"sql-function": "SQL Function",
"sql-query": "SQL Query",
"sql-uppercase": "SQL",
"sql-uppercase-query": "SQL Query",
"sso": "SSO",
@ -1705,6 +1714,7 @@
"sunday": "Sunday",
"support": "Support",
"support-url": "Support URL",
"supported-data-type-plural": "Supported Data Types",
"supported-language-plural": "Supported Languages",
"switch-persona": "Switch Persona",
"sync-alert-offset": "Sync Alert",
@ -1770,10 +1780,13 @@
"test-case-plural": "Test Cases",
"test-case-resolution-status": "Test Case Resolution Status",
"test-case-result": "Test Case Results",
"test-definition": "Test Definition",
"test-definition-plural": "Test Definitions",
"test-email": "Test Email",
"test-email-connection": "Test Email Connection",
"test-entity": "Test {{entity}}",
"test-level-lowercase": "test level",
"test-platform-plural": "Test Platforms",
"test-plural": "Tests",
"test-plural-type": "{{type}} Tests",
"test-suite": "Test Suite",
@ -2534,6 +2547,7 @@
"page-sub-header-for-storages": "Ingest metadata from the most popular storage services.",
"page-sub-header-for-table-profile": "Monitor and understand your tables structure with the profiler.",
"page-sub-header-for-teams": "Represent your entire organizational structure with hierarchical teams.",
"page-sub-header-for-test-definitions": "Manage and configure test definitions for data quality rules.",
"page-sub-header-for-users": "View and manage regular users in your organization. For admin users, please visit the Admin page.",
"paid-addon-description": "<0>{{app}}</0> is a paid add-on for Collate customers.",
"passed-x-checks": "Passed {{count}} checks",
@ -2660,6 +2674,8 @@
"synonym-placeholder": "To add a synonym, simply type it in and press Enter",
"system-alert-edit-message": "Editing a system generated alert is not allowed.",
"system-tag-delete-disable-message": "Deleting a system generated tags is not allowed. You can try disabling the tag instead.",
"system-test-definition-delete-warning": "Deleting a system generated test definition is not allowed.",
"system-test-definition-edit-warning": "Editing a system generated test definition is not allowed.",
"tag-update-confirmation": "Would you like to proceed with updating the tags?",
"tailor-experience-for-persona": "Tailor the experience specifically for the {{persona}} persona, just give it a spin!",
"take-quick-product-tour": "Take a product tour to get started!",
@ -2675,6 +2691,8 @@
"default": "Please ensure that {{service_type}} is configured to allow connections",
"withIp": " from {{ip}}"
},
"test-definition-parameters-description": "Defines configurable input parameters for test cases. Each parameter includes a name, data type (string, int, date, etc.), display name, and optional validation rules. Parameters can be referenced in SQL expressions using {{paramName}} syntax and allow users to customize test behavior when creating test cases.",
"test-definition-sql-query-help": "SQL expression template for custom SQL-based test definitions. Supports substitution variables: {table} and {column} for runtime entity references, and {{paramName}} for user-defined parameters. This field is only applicable for test definitions with testPlatforms set to 'OpenMetadata' and is used to execute custom SQL queries for data quality validation.",
"test-your-connection-before-creating-service": "Test your connections before creating the service",
"testing-your-connection-may-take-two-minutes": "Testing your connections may take up-to 2 minutes",
"this-action-is-not-allowed-for-deleted-entities": "This action is not allowed for deleted entities.",
@ -2774,7 +2792,9 @@
"entities-deleted-successfully": "Successfully deleted {{count}} {{entity}}s",
"entity-already-exist": "{{entity}} \"{{name}}\" already exists. Duplicated {{entityPlural}} are not allowed.",
"entity-already-exist-message-without-name": "A {{entity}} with the given details already exists. Duplicated {{entityPlural}} are not allowed.",
"entity-created-success": "{{entity}} created successfully!",
"entity-creation-error": "Error while creating {{entity}}",
"entity-deleted-success": "{{entity}} deleted successfully!",
"entity-deleted-successfully": "\"{{entity}}\" deleted successfully!",
"entity-details-fetch-error": "Error while fetching details for {{entityType}} {{entityName}}",
"entity-feed-fetch-error": "Error while fetching entity feed count!",
@ -2784,6 +2804,7 @@
"entity-limit-reached": "{{entity}} limit reached",
"entity-removing-error": "Error while removing {{entity}}",
"entity-unfollow-error": "Error while unfollowing {{entity}}",
"entity-updated-success": "{{entity}} updated successfully!",
"entity-updating-error": "Error while updating {{entity}}",
"error-selected-node-name-details": "Error while getting {{selectedNodeName}} details",
"error-while-renewing-id-token-with-message": "Error while renewing id token from Auth0 SSO: {{message}}",
@ -2824,6 +2845,10 @@
"task-resolved-successfully": "Task resolved successfully",
"team-moved-error": "Error while moving team",
"test-connection-error": "Error while testing connection!",
"test-definition-parameters-description": "Define parameters that users can configure when creating test cases from this definition. Parameters allow for flexible and reusable test definitions.",
"test-definition-sql-expression-placeholder": "SELECT * FROM {table} WHERE {column} < {{minValue}} OR {column} > {{maxValue}}",
"test-definition-sql-expression-tooltip": "SQL query template using parameter placeholders in double curly braces (e.g., {{paramName}}). Use {table} and {column} for runtime entity references.",
"test-definition-sql-query-help": "Write SQL query template with substitution variables. Use {table} for table name, {column} for column name (resolved at runtime). Use {{paramName}} for user parameters defined below (e.g., {{minValue}}, {{maxValue}}).",
"this-action-cannot-be-undone": "This action cannot be undone.",
"unauthorized-user": "UnAuthorized user! please check email or password",
"unexpected-error": "An unexpected error occurred.",

View file

@ -431,6 +431,7 @@
"data-profiler-metrics": "Métricas de perfilado",
"data-proportion-plural": "Proporciones de datos",
"data-quality": "Tests de calidad",
"data-quality-dimension": "Data Quality Dimension",
"data-quality-test": "Test de calidad de datos",
"data-quality-test-plural": "Pruebas de Calidad de Datos",
"data-quartile-plural": "cuartiles",
@ -613,6 +614,7 @@
"enter-custom-unit-of-measurement": "Introduce la unidad de medida personalizada y pulsa Enter",
"enter-each-value-and-press-enter": "Introduzca cada valor y presione Enter",
"enter-entity": "Ingrese {{entity}}",
"enter-entity-description": "Enter {{entity}} description",
"enter-entity-name": "Ingrese el nombre de {{entity}}",
"enter-entity-value": "Ingrese valor de {{entity}}",
"enter-field-description": "Ingrese la descripción de {{field}}",
@ -1253,6 +1255,9 @@
"page-not-found": "Página no encontrada",
"page-views-by-data-asset-plural": "Vistas de página por activos de datos",
"parameter": "Parámetro",
"parameter-description": "Parameter Description",
"parameter-display-name": "Parameter Display Name",
"parameter-name": "Parameter Name",
"parameter-plural": "Parámetros",
"parent": "Padre",
"parsing-timeout-limit": "Límite Tiempo de Espera de Análisis de Consultas",
@ -1436,6 +1441,7 @@
"request-method": "Petición Método",
"request-schema-field": "Petición Campo del Esquema",
"request-tag-plural": "Etiquetas de solicitud",
"required": "Required",
"requirement-plural": "Requisitos",
"reset": "Restablecer",
"reset-default-layout": "Restablecer el panel por defecto",
@ -1495,6 +1501,7 @@
"rule-with-name": "- Regla: {{ruleName}}",
"rules-count": "({{count}} reglas)",
"rules-evaluated": "Reglas Evaluadas",
"rules-library": "Rules Library",
"run": "Ejecutar",
"run-agent-plural": "Ejecutar Agentes",
"run-at": "Ejecutar a las",
@ -1645,7 +1652,9 @@
"specific-data-asset-plural": "Activos de Datos Específicos",
"spreadsheet": "Hoja de cálculo",
"spreadsheet-plural": "Hojas de cálculo",
"sql-expression": "SQL Expression",
"sql-function": "Función SQL",
"sql-query": "SQL Query",
"sql-uppercase": "SQL",
"sql-uppercase-query": "Consulta SQL",
"sso": "sso",
@ -1705,6 +1714,7 @@
"sunday": "Domingo",
"support": "Soporte",
"support-url": "URL de soporte",
"supported-data-type-plural": "Supported Data Types",
"supported-language-plural": "Idiomas soportados",
"switch-persona": "Cambiar Persona",
"sync-alert-offset": "Sincronizar alerta",
@ -1770,10 +1780,13 @@
"test-case-plural": "Casos de Prueba",
"test-case-resolution-status": "Estado de Resolución del Caso de Prueba",
"test-case-result": "Resultados del caso de prueba",
"test-definition": "Test Definition",
"test-definition-plural": "Test Definitions",
"test-email": "Email de prueba",
"test-email-connection": "Probar conexión de email",
"test-entity": "Prueba {{entity}}",
"test-level-lowercase": "nivel de prueba",
"test-platform-plural": "Test Platforms",
"test-plural": "Pruebas",
"test-plural-type": "Pruebas de {{type}}",
"test-suite": "Suite de Pruebas",
@ -2534,6 +2547,7 @@
"page-sub-header-for-storages": "Ingresa metadatos desde los servicios de almacenamiento más populares.",
"page-sub-header-for-table-profile": "Monitorea y comprende la estructura de tus tablas con el perfilador.",
"page-sub-header-for-teams": "Representa toda la estructura organizativa con equipos jerárquicos.",
"page-sub-header-for-test-definitions": "Manage and configure test definitions for data quality rules.",
"page-sub-header-for-users": "Gestione los usuarios regulares en su organización. Para administradores, visite la página de Administradores.",
"paid-addon-description": "<0>{{app}}</0> es un complemento (add-on) de pago para clientes de Collate.",
"passed-x-checks": "Pasaron {{count}} verificaciones",
@ -2660,6 +2674,8 @@
"synonym-placeholder": "Para agregar un sinónimo, simplemente escríbelo y presiona Enter",
"system-alert-edit-message": "No está permitido editar una alerta generada por el sistema.",
"system-tag-delete-disable-message": "No se permite eliminar una etiqueta generada por el sistema. Puedes intentar desactivar la etiqueta en su lugar.",
"system-test-definition-delete-warning": "Deleting a system generated test definition is not allowed.",
"system-test-definition-edit-warning": "Editing a system generated test definition is not allowed.",
"tag-update-confirmation": "¿Le gustaría proceder con la actualización de etiquetas?",
"tailor-experience-for-persona": "¡Adapte la experiencia específicamente para el persona {{persona}}, pruébelo!",
"take-quick-product-tour": "¡Realiza un recorrido rápido del producto para comenzar!",
@ -2675,6 +2691,8 @@
"default": "Por favor, asegúrese de que {{service_type}} esté configurado para permitir conexiones",
"withIp": " desde {{ip}}"
},
"test-definition-parameters-description": "Defines configurable input parameters for test cases. Each parameter includes a name, data type (string, int, date, etc.), display name, and optional validation rules. Parameters can be referenced in SQL expressions using {{paramName}} syntax and allow users to customize test behavior when creating test cases.",
"test-definition-sql-query-help": "SQL expression template for custom SQL-based test definitions. Supports substitution variables: {table} and {column} for runtime entity references, and {{paramName}} for user-defined parameters. This field is only applicable for test definitions with testPlatforms set to 'OpenMetadata' and is used to execute custom SQL queries for data quality validation.",
"test-your-connection-before-creating-service": "Prueba la conexión antes de crear el servicio",
"testing-your-connection-may-take-two-minutes": "Probar la conexión puede tardar hasta 2 minutos",
"this-action-is-not-allowed-for-deleted-entities": "Esta acción no está permitida para entidades eliminadas.",
@ -2774,7 +2792,9 @@
"entities-deleted-successfully": "{{count}} {{entity}}s eliminados exitosamente",
"entity-already-exist": "{{entity}} \"{{name}}\" ya existe. No se permiten {{entity}} duplicados.",
"entity-already-exist-message-without-name": "Un {{entity}} con los detalles proporcionados ya existe. No se permiten duplicados de {{entityPlural}}.",
"entity-created-success": "{{entity}} created successfully!",
"entity-creation-error": "Error al crear {{entity}}",
"entity-deleted-success": "{{entity}} deleted successfully!",
"entity-deleted-successfully": "¡{{entity}} eliminado exitosamente!",
"entity-details-fetch-error": "Error al recuperar detalles para {{entityType}} {{entityName}}",
"entity-feed-fetch-error": "¡Error al obtener el recuento de alimentación de la entidad!",
@ -2784,6 +2804,7 @@
"entity-limit-reached": "Límite de {{entity}} alcanzado",
"entity-removing-error": "Error al eliminar {{entity}}",
"entity-unfollow-error": "Error al dejar de seguir {{entity}}",
"entity-updated-success": "{{entity}} updated successfully!",
"entity-updating-error": "Error al actualizar {{entity}}",
"error-selected-node-name-details": "Error al obstener detalles de {{selectedNodeName}}",
"error-while-renewing-id-token-with-message": "Error al renovar el token de identificación desde Auth0 SSO: {{message}}",
@ -2824,6 +2845,10 @@
"task-resolved-successfully": "Tarea resuelta con éxito.",
"team-moved-error": "Error al mover el equipo.",
"test-connection-error": "Error al probar la conexión.",
"test-definition-parameters-description": "Define parameters that users can configure when creating test cases from this definition. Parameters allow for flexible and reusable test definitions.",
"test-definition-sql-expression-placeholder": "SELECT * FROM {table} WHERE {column} < {{minValue}} OR {column} > {{maxValue}}",
"test-definition-sql-expression-tooltip": "SQL query template using parameter placeholders in double curly braces (e.g., {{paramName}}). Use {table} and {column} for runtime entity references.",
"test-definition-sql-query-help": "Write SQL query template with substitution variables. Use {table} for table name, {column} for column name (resolved at runtime). Use {{paramName}} for user parameters defined below (e.g., {{minValue}}, {{maxValue}}).",
"this-action-cannot-be-undone": "This action cannot be undone.",
"unauthorized-user": "¡Usuario no autorizado! Por favor revise el correo electrónico o la contraseña.",
"unexpected-error": "Se produjo un error inesperado.",

View file

@ -431,6 +431,7 @@
"data-profiler-metrics": "Métriques du Profileur de Données",
"data-proportion-plural": "Proportions des Données",
"data-quality": "Qualité des Données",
"data-quality-dimension": "Data Quality Dimension",
"data-quality-test": "Test de Qualité des Données",
"data-quality-test-plural": "Tests de Qualité des Données",
"data-quartile-plural": "Quartiles des Données",
@ -613,6 +614,7 @@
"enter-custom-unit-of-measurement": "Entrez l'unité de mesure personnalisée et appuyez sur Entrée",
"enter-each-value-and-press-enter": "Entrez chaque valeur et appuyez sur Entrée",
"enter-entity": "Entrer {{entity}}",
"enter-entity-description": "Enter {{entity}} description",
"enter-entity-name": "Entrer un nom pour {{entity}}",
"enter-entity-value": "Entrer une valeur pour {{entity}}",
"enter-field-description": "Entrer une description pour {{field}}",
@ -1253,6 +1255,9 @@
"page-not-found": "Page Non Trouvée",
"page-views-by-data-asset-plural": "Vues de Page par Actif de Données",
"parameter": "Paramètre",
"parameter-description": "Parameter Description",
"parameter-display-name": "Parameter Display Name",
"parameter-name": "Parameter Name",
"parameter-plural": "Paramètres",
"parent": "Parent",
"parsing-timeout-limit": "Limite de Temps d'Analyse de Requête",
@ -1436,6 +1441,7 @@
"request-method": "Méthode de Requête",
"request-schema-field": "Champ de Schéma de Requête",
"request-tag-plural": "Demander l'ajout de tags",
"required": "Required",
"requirement-plural": "Critères",
"reset": "Réinitialiser",
"reset-default-layout": "Réinitialiser la Disposition par Défaut",
@ -1495,6 +1501,7 @@
"rule-with-name": "- Rule: {{ruleName}}",
"rules-count": "({{count}} rules)",
"rules-evaluated": "Règles Évaluées",
"rules-library": "Rules Library",
"run": "Exécuter",
"run-agent-plural": "Exécuter les Agents",
"run-at": "Exécuté à",
@ -1645,7 +1652,9 @@
"specific-data-asset-plural": "Actifs de Données Spécifiques",
"spreadsheet": "Feuille de calcul",
"spreadsheet-plural": "Feuilles de calcul",
"sql-expression": "SQL Expression",
"sql-function": "Fonction SQL",
"sql-query": "SQL Query",
"sql-uppercase": "SQL",
"sql-uppercase-query": "Requête SQL",
"sso": "sso",
@ -1705,6 +1714,7 @@
"sunday": "Dimanche",
"support": "Support",
"support-url": "URL de Support",
"supported-data-type-plural": "Supported Data Types",
"supported-language-plural": "Languages Supportés",
"switch-persona": "Changer de Persona",
"sync-alert-offset": "Synchroniser l'alerte",
@ -1770,10 +1780,13 @@
"test-case-plural": "Cas de Tests",
"test-case-resolution-status": "Statut de Résolution du Cas de Test",
"test-case-result": "Résultat du cas de test",
"test-definition": "Test Definition",
"test-definition-plural": "Test Definitions",
"test-email": "Test Email",
"test-email-connection": "Tester la connexion Email",
"test-entity": "Test {{entity}}",
"test-level-lowercase": "niveau de test",
"test-platform-plural": "Test Platforms",
"test-plural": "Tests",
"test-plural-type": "Tests {{type}}",
"test-suite": "Ensemble de Tests",
@ -2534,6 +2547,7 @@
"page-sub-header-for-storages": "Ingestion de métadonnées à partir des services de stockage les plus populaires.",
"page-sub-header-for-table-profile": "Surveillez et comprenez la structure de vos tables avec le profilage.",
"page-sub-header-for-teams": "Représentez toute la structure organisationnelle avec des équipes hiérarchiques.",
"page-sub-header-for-test-definitions": "Manage and configure test definitions for data quality rules.",
"page-sub-header-for-users": "Gérez les utilisateurs réguliers de votre organisation. Pour les administrateurs, visitez la page Administrateurs.",
"paid-addon-description": "<0>{{app}}</0> est un add-on payant pour les clients utilisateurs de Collate.",
"passed-x-checks": "{{count}} vérifications réussies",
@ -2660,6 +2674,8 @@
"synonym-placeholder": "Pour ajouter un synonyme, tapez-le et appuyez sur Entrée",
"system-alert-edit-message": "La modification d'une alerte générée par le système n'est pas autorisée.",
"system-tag-delete-disable-message": "Supprimer un tag système n'est pas permis. Tentez plutôt de désactiver ce tag.",
"system-test-definition-delete-warning": "Deleting a system generated test definition is not allowed.",
"system-test-definition-edit-warning": "Editing a system generated test definition is not allowed.",
"tag-update-confirmation": "Souhaitez-vous procéder à la mise à jour des étiquettes ?",
"tailor-experience-for-persona": "Adaptez l'expérience spécifiquement pour le persona {{persona}}, essayez-le !",
"take-quick-product-tour": "Faites une visite guidée pour vous lancer!",
@ -2675,6 +2691,8 @@
"default": "Veuillez vérifier que {{service_type}} est configuré pour autoriser les connexions",
"withIp": " depuis {{ip}}"
},
"test-definition-parameters-description": "Defines configurable input parameters for test cases. Each parameter includes a name, data type (string, int, date, etc.), display name, and optional validation rules. Parameters can be referenced in SQL expressions using {{paramName}} syntax and allow users to customize test behavior when creating test cases.",
"test-definition-sql-query-help": "SQL expression template for custom SQL-based test definitions. Supports substitution variables: {table} and {column} for runtime entity references, and {{paramName}} for user-defined parameters. This field is only applicable for test definitions with testPlatforms set to 'OpenMetadata' and is used to execute custom SQL queries for data quality validation.",
"test-your-connection-before-creating-service": "Testez la connexion avant de créer le service",
"testing-your-connection-may-take-two-minutes": "Le test de votre connexion peut prendre jusqu'à 2 minutes",
"this-action-is-not-allowed-for-deleted-entities": "Cette action n'est pas permise pour les entités supprimées.",
@ -2774,7 +2792,9 @@
"entities-deleted-successfully": "{{count}} {{entity}}s supprimé(e)s avec succès",
"entity-already-exist": "{{entity}} \"{{name}}\" existe déjà. Les doublons de {{entity}} ne sont pas autorisés.",
"entity-already-exist-message-without-name": "Un(e) {{entity}} avec les détails fournis existe déjà. Les doublons de {{entityPlural}} ne sont pas autorisés.",
"entity-created-success": "{{entity}} created successfully!",
"entity-creation-error": "Erreur lors de la création de {{entity}}",
"entity-deleted-success": "{{entity}} deleted successfully!",
"entity-deleted-successfully": "{{entity}} supprimé(e) avec succès !",
"entity-details-fetch-error": "Erreur lors de la récupération des détails pour {{entityType}} {{entityName}}",
"entity-feed-fetch-error": "Erreur lors de la récupération du nombre d'alimentation de l'entité !",
@ -2784,6 +2804,7 @@
"entity-limit-reached": "Limite atteinte pour {{entity}}",
"entity-removing-error": "Erreur lors de la suppression de {{entity}}",
"entity-unfollow-error": "Erreur lors de l'arrêt de suivi de {{entity}}",
"entity-updated-success": "{{entity}} updated successfully!",
"entity-updating-error": "Erreur lors de la mise à jour de {{entity}}",
"error-selected-node-name-details": "Erreur lors de la récupération des détails de {{selectedNodeName}}",
"error-while-renewing-id-token-with-message": "Erreur lors du renouvellement du jeton d'identification à partir d'Auth0 SSO : {{message}}",
@ -2824,6 +2845,10 @@
"task-resolved-successfully": "Tâche résolue avec succès",
"team-moved-error": "Erreur lors du déplacement de l'équipe",
"test-connection-error": "Erreur lors du test de la connexion !",
"test-definition-parameters-description": "Define parameters that users can configure when creating test cases from this definition. Parameters allow for flexible and reusable test definitions.",
"test-definition-sql-expression-placeholder": "SELECT * FROM {table} WHERE {column} < {{minValue}} OR {column} > {{maxValue}}",
"test-definition-sql-expression-tooltip": "SQL query template using parameter placeholders in double curly braces (e.g., {{paramName}}). Use {table} and {column} for runtime entity references.",
"test-definition-sql-query-help": "Write SQL query template with substitution variables. Use {table} for table name, {column} for column name (resolved at runtime). Use {{paramName}} for user parameters defined below (e.g., {{minValue}}, {{maxValue}}).",
"this-action-cannot-be-undone": "Cette action ne peut pas être annulée.",
"unauthorized-user": "Utilisateur non autorisé ! Veuillez vérifier votre e-mail ou votre mot de passe",
"unexpected-error": "Une erreur inattendue est survenue.",

View file

@ -431,6 +431,7 @@
"data-profiler-metrics": "Métricas de perfilador de datos",
"data-proportion-plural": "Proporcións de datos",
"data-quality": "Calidade dos datos",
"data-quality-dimension": "Data Quality Dimension",
"data-quality-test": "Proba de calidade de datos",
"data-quality-test-plural": "Pruebas de Calidad de Datos",
"data-quartile-plural": "Cuartís de datos",
@ -613,6 +614,7 @@
"enter-custom-unit-of-measurement": "Introduce a unidade de medida personalizada e preme Enter",
"enter-each-value-and-press-enter": "Enter each value and press Enter",
"enter-entity": "Introducir {{entity}}",
"enter-entity-description": "Enter {{entity}} description",
"enter-entity-name": "Introducir o nome de {{entity}}",
"enter-entity-value": "Introducir o valor de {{entity}}",
"enter-field-description": "Introducir a descrición de {{field}}",
@ -1253,6 +1255,9 @@
"page-not-found": "Páxina non atopada",
"page-views-by-data-asset-plural": "Visualizacións de páxinas por activos de datos",
"parameter": "Parámetro",
"parameter-description": "Parameter Description",
"parameter-display-name": "Parameter Display Name",
"parameter-name": "Parameter Name",
"parameter-plural": "Parámetros",
"parent": "Pai",
"parsing-timeout-limit": "Límite de tempo de análise de consultas",
@ -1436,6 +1441,7 @@
"request-method": "Método de solicitude",
"request-schema-field": "Campo de esquema de solicitude",
"request-tag-plural": "Solicitar etiquetas",
"required": "Required",
"requirement-plural": "Requisitos",
"reset": "Restablecer",
"reset-default-layout": "Restablecer deseño predeterminado",
@ -1495,6 +1501,7 @@
"rule-with-name": "- Rule: {{ruleName}}",
"rules-count": "({{count}} rules)",
"rules-evaluated": "Rules Evaluated",
"rules-library": "Rules Library",
"run": "Executar",
"run-agent-plural": "Executar Agentes",
"run-at": "Executar ás",
@ -1645,7 +1652,9 @@
"specific-data-asset-plural": "Activos de datos específicos",
"spreadsheet": "Folla de cálculo",
"spreadsheet-plural": "Follas de cálculo",
"sql-expression": "SQL Expression",
"sql-function": "Función SQL",
"sql-query": "SQL Query",
"sql-uppercase": "SQL",
"sql-uppercase-query": "Consulta SQL",
"sso": "sso",
@ -1705,6 +1714,7 @@
"sunday": "Domingo",
"support": "Soporte",
"support-url": "URL de soporte",
"supported-data-type-plural": "Supported Data Types",
"supported-language-plural": "Linguas soportadas",
"switch-persona": "Cambiar Persona",
"sync-alert-offset": "Sincronizar alerta",
@ -1770,10 +1780,13 @@
"test-case-plural": "Casos de proba",
"test-case-resolution-status": "Estado da Resolución do Caso de Proba",
"test-case-result": "Resultados do caso de proba",
"test-definition": "Test Definition",
"test-definition-plural": "Test Definitions",
"test-email": "Correo electrónico de proba",
"test-email-connection": "Probar a conexión de correo electrónico",
"test-entity": "Proba {{entity}}",
"test-level-lowercase": "nivel de proba",
"test-platform-plural": "Test Platforms",
"test-plural": "Probas",
"test-plural-type": "Probas de {{type}}",
"test-suite": "Conxunto de probas",
@ -2534,6 +2547,7 @@
"page-sub-header-for-storages": "Inxesta metadatos dos servizos de almacenamento máis populares.",
"page-sub-header-for-table-profile": "Supervisa e comprende a estrutura das túas táboas co perfilador.",
"page-sub-header-for-teams": "Representa toda a estrutura organizativa cunha xerarquía de equipos.",
"page-sub-header-for-test-definitions": "Manage and configure test definitions for data quality rules.",
"page-sub-header-for-users": "Xestione os usuarios regulares na súa organización. Para administradores, visite a páxina de Administradores.",
"paid-addon-description": "<0>{{app}}</0> é un complemento de pago para os clientes de Collate.",
"passed-x-checks": "Pasaron {{count}} verificacións",
@ -2660,6 +2674,8 @@
"synonym-placeholder": "Para engadir un sinónimo, simplemente escríbeo e preme Enter",
"system-alert-edit-message": "Non está permitido editar unha alerta xerada polo sistema.",
"system-tag-delete-disable-message": "Non está permitido eliminar etiquetas xeradas polo sistema. Podes intentar desactivar a etiqueta no seu lugar.",
"system-test-definition-delete-warning": "Deleting a system generated test definition is not allowed.",
"system-test-definition-edit-warning": "Editing a system generated test definition is not allowed.",
"tag-update-confirmation": "Desexas proceder coa actualización das etiquetas?",
"tailor-experience-for-persona": "¡Adapta a experiencia especificamente para o persona {{persona}}, proba!",
"take-quick-product-tour": "Fai un tour rápido polo produto para comezar!",
@ -2675,6 +2691,8 @@
"default": "Asegúrese de que {{service_type}} estea configurado para permitir conexións",
"withIp": " dende {{ip}}"
},
"test-definition-parameters-description": "Defines configurable input parameters for test cases. Each parameter includes a name, data type (string, int, date, etc.), display name, and optional validation rules. Parameters can be referenced in SQL expressions using {{paramName}} syntax and allow users to customize test behavior when creating test cases.",
"test-definition-sql-query-help": "SQL expression template for custom SQL-based test definitions. Supports substitution variables: {table} and {column} for runtime entity references, and {{paramName}} for user-defined parameters. This field is only applicable for test definitions with testPlatforms set to 'OpenMetadata' and is used to execute custom SQL queries for data quality validation.",
"test-your-connection-before-creating-service": "Proba as túas conexións antes de crear o servizo",
"testing-your-connection-may-take-two-minutes": "A proba das túas conexións pode levar ata 2 minutos",
"this-action-is-not-allowed-for-deleted-entities": "Esta acción non está permitida para entidades eliminadas.",
@ -2774,7 +2792,9 @@
"entities-deleted-successfully": "Successfully deleted {{count}} {{entity}}s",
"entity-already-exist": "{{entity}} \"{{name}}\" xa existe. Non se permiten {{entityPlural}} duplicados.",
"entity-already-exist-message-without-name": "Xa existe un {{entity}} cos detalles proporcionados. Non se permiten {{entityPlural}} duplicados.",
"entity-created-success": "{{entity}} created successfully!",
"entity-creation-error": "Erro ao crear {{entity}}",
"entity-deleted-success": "{{entity}} deleted successfully!",
"entity-deleted-successfully": "\"{{entity}}\" eliminado correctamente!",
"entity-details-fetch-error": "Erro ao obter detalles de {{entityType}} {{entityName}}",
"entity-feed-fetch-error": "Erro ao obter o número de feeds da entidade!",
@ -2784,6 +2804,7 @@
"entity-limit-reached": "Alcanzouse o límite de {{entity}}",
"entity-removing-error": "Erro ao eliminar {{entity}}",
"entity-unfollow-error": "Erro ao deixar de seguir {{entity}}",
"entity-updated-success": "{{entity}} updated successfully!",
"entity-updating-error": "Erro ao actualizar {{entity}}",
"error-selected-node-name-details": "Erro ao obter detalles de {{selectedNodeName}}",
"error-while-renewing-id-token-with-message": "Erro ao renovar o token de identificación de Auth0 SSO: {{message}}",
@ -2824,6 +2845,10 @@
"task-resolved-successfully": "Tarefa resolta correctamente",
"team-moved-error": "Erro ao mover o equipo",
"test-connection-error": "Erro ao probar a conexión!",
"test-definition-parameters-description": "Define parameters that users can configure when creating test cases from this definition. Parameters allow for flexible and reusable test definitions.",
"test-definition-sql-expression-placeholder": "SELECT * FROM {table} WHERE {column} < {{minValue}} OR {column} > {{maxValue}}",
"test-definition-sql-expression-tooltip": "SQL query template using parameter placeholders in double curly braces (e.g., {{paramName}}). Use {table} and {column} for runtime entity references.",
"test-definition-sql-query-help": "Write SQL query template with substitution variables. Use {table} for table name, {column} for column name (resolved at runtime). Use {{paramName}} for user parameters defined below (e.g., {{minValue}}, {{maxValue}}).",
"this-action-cannot-be-undone": "This action cannot be undone.",
"unauthorized-user": "Usuario non autorizado! Comproba o correo electrónico ou o contrasinal",
"unexpected-error": "Produciuse un erro inesperado.",

View file

@ -431,6 +431,7 @@
"data-profiler-metrics": "מדדי לפרופיילר הנתונים",
"data-proportion-plural": "יחסי נתונים",
"data-quality": "איכות נתונים",
"data-quality-dimension": "Data Quality Dimension",
"data-quality-test": "בקרת איכות נתונים",
"data-quality-test-plural": "בדיקות איכות נתונים",
"data-quartile-plural": "רביעונים (Quaertiles)",
@ -613,6 +614,7 @@
"enter-custom-unit-of-measurement": "הזן יחידת מדידה מותאמת אישית ולחץ Enter",
"enter-each-value-and-press-enter": "הזן כל ערך ולחץ Enter",
"enter-entity": "הזן {{entity}}",
"enter-entity-description": "Enter {{entity}} description",
"enter-entity-name": "הזן שם {{entity}}",
"enter-entity-value": "הזן ערך {{entity}}",
"enter-field-description": "הזן תיאור של {{field}}",
@ -1253,6 +1255,9 @@
"page-not-found": "הדף לא נמצא",
"page-views-by-data-asset-plural": "צפיות בדפים לפי נכסי נתונים",
"parameter": "פרמטר",
"parameter-description": "Parameter Description",
"parameter-display-name": "Parameter Display Name",
"parameter-name": "Parameter Name",
"parameter-plural": "פרמטרים",
"parent": "הורה",
"parsing-timeout-limit": "מגבלת זמן לפענוח שאילתא",
@ -1436,6 +1441,7 @@
"request-method": "Request Method",
"request-schema-field": "Request Schema Field",
"request-tag-plural": "בקשת תגיות",
"required": "Required",
"requirement-plural": "דרישות",
"reset": "איפוס",
"reset-default-layout": "איפוס פריסת ברירת מחדל",
@ -1495,6 +1501,7 @@
"rule-with-name": "- Rule: {{ruleName}}",
"rules-count": "({{count}} rules)",
"rules-evaluated": "כללים שהוערכו",
"rules-library": "Rules Library",
"run": "הרץ",
"run-agent-plural": "הפעל סוכנים",
"run-at": "הרץ ב",
@ -1645,7 +1652,9 @@
"specific-data-asset-plural": "נכסי נתונים ספציפיים",
"spreadsheet": "גיליון אלקטרוני",
"spreadsheet-plural": "גיליונות אלקטרוניים",
"sql-expression": "SQL Expression",
"sql-function": "פונקציית SQL",
"sql-query": "SQL Query",
"sql-uppercase": "SQL",
"sql-uppercase-query": "שאילתת SQL",
"sso": "sso",
@ -1705,6 +1714,7 @@
"sunday": "ראשון",
"support": "תמיכה",
"support-url": "כתובת תמיכה",
"supported-data-type-plural": "Supported Data Types",
"supported-language-plural": "שפות נתמכות",
"switch-persona": "החלף פרסונה",
"sync-alert-offset": "סנכרן התראה",
@ -1770,10 +1780,13 @@
"test-case-plural": "בדיקות נתונים",
"test-case-resolution-status": "סטטוס פתרון מקרי בדיקה",
"test-case-result": "Test Case Results",
"test-definition": "Test Definition",
"test-definition-plural": "Test Definitions",
"test-email": "Test Email",
"test-email-connection": "Test Email Connection",
"test-entity": "בדיקה {{entity}}",
"test-level-lowercase": "רמת בדיקה",
"test-platform-plural": "Test Platforms",
"test-plural": "בדיקות",
"test-plural-type": "בדיקות {{type}}",
"test-suite": "מערכת בדיקות",
@ -2534,6 +2547,7 @@
"page-sub-header-for-storages": "שלב מטה-דאטה משירותי האחסון הפופולריים ביותר.",
"page-sub-header-for-table-profile": "נטרו, נתחו והבינו את מבנה הטבלאות שלכם באמצועות הפרופיילר.",
"page-sub-header-for-teams": "ייצג את מבנה הארגון באמצעות היררכיה של צוותים",
"page-sub-header-for-test-definitions": "Manage and configure test definitions for data quality rules.",
"page-sub-header-for-users": "נהל משתמשים רגילים בארגון שלך. עבור מנהלים, בקר בדף המנהלים.",
"paid-addon-description": "<0>{{app}}</0> הוא תוסף בתשלום ללקוחות Collate.",
"passed-x-checks": "Passed {{count}} checks",
@ -2660,6 +2674,8 @@
"synonym-placeholder": "כדי להוסיף שם נרדף, פשוט הקלד אותו ולחץ Enter",
"system-alert-edit-message": "עריכת התראה שנוצרה על ידי המערכת אינה מותרת.",
"system-tag-delete-disable-message": "מחיקת תגיות שנוצרו באופן מערכתי אינה מותרת. ניתן לנסות לנטרל את התג במקום.",
"system-test-definition-delete-warning": "Deleting a system generated test definition is not allowed.",
"system-test-definition-edit-warning": "Editing a system generated test definition is not allowed.",
"tag-update-confirmation": "האם ברצונך להמשיך בעדכון התגים?",
"tailor-experience-for-persona": "התאם את החוויה במיוחד עבור אישיות {{persona}}, נסה את זה!",
"take-quick-product-tour": "התחל סיור במוצר כדי להתחיל!",
@ -2675,6 +2691,8 @@
"default": "אנא וודא ש‑{{service_type}} מוגדר לאפשר חיבורים",
"withIp": " מ‑{{ip}}"
},
"test-definition-parameters-description": "Defines configurable input parameters for test cases. Each parameter includes a name, data type (string, int, date, etc.), display name, and optional validation rules. Parameters can be referenced in SQL expressions using {{paramName}} syntax and allow users to customize test behavior when creating test cases.",
"test-definition-sql-query-help": "SQL expression template for custom SQL-based test definitions. Supports substitution variables: {table} and {column} for runtime entity references, and {{paramName}} for user-defined parameters. This field is only applicable for test definitions with testPlatforms set to 'OpenMetadata' and is used to execute custom SQL queries for data quality validation.",
"test-your-connection-before-creating-service": "בדוק את החיבור שלך לפני שתיצור את השירות",
"testing-your-connection-may-take-two-minutes": "בדיקת החיבור שלך עשויה לקחת עד 2 דקות",
"this-action-is-not-allowed-for-deleted-entities": "פעולה זו אינה מותרת עבור ישויות שנמחקו.",
@ -2774,7 +2792,9 @@
"entities-deleted-successfully": "{{count}} {{entity}}s נמחקו בהצלחה",
"entity-already-exist": "{{entity}} \"{{name}}\" כבר קיים. אין אפשרות ליצור {{entityPlural}} כפולים.",
"entity-already-exist-message-without-name": "{{entity}} עם הפרטים הנתונים כבר קיים. אין אפשרות ליצור {{entityPlural}} כפולים.",
"entity-created-success": "{{entity}} created successfully!",
"entity-creation-error": "שגיאה במהלך יצירת {{entity}}",
"entity-deleted-success": "{{entity}} deleted successfully!",
"entity-deleted-successfully": "{{entity}} נמחק בהצלחה!",
"entity-details-fetch-error": "שגיאה במהלך אחזור פרטים עבור {{entityType}} {{entityName}}",
"entity-feed-fetch-error": "שגיאה במהלך אחזור ספירת הזרמת הפריטים!",
@ -2784,6 +2804,7 @@
"entity-limit-reached": "{{entity}} limit reached",
"entity-removing-error": "שגיאה במהלך הסרת {{entity}}",
"entity-unfollow-error": "שגיאה במהלך ביטול עקיבה אחרי {{entity}}",
"entity-updated-success": "{{entity}} updated successfully!",
"entity-updating-error": "שגיאה במהלך עדכון {{entity}}",
"error-selected-node-name-details": "שגיאה במהלך קבלת פרטי {{selectedNodeName}}",
"error-while-renewing-id-token-with-message": "שגיאה במהלך חידוש טוקן ID ממערכת ה-Single Sign-On של Auth0: {{message}}",
@ -2824,6 +2845,10 @@
"task-resolved-successfully": "המשימה נפתרה בהצלחה",
"team-moved-error": "שגיאה במהלך העברת הקבוצה",
"test-connection-error": "שגיאה במהלך בדיקת החיבור!",
"test-definition-parameters-description": "Define parameters that users can configure when creating test cases from this definition. Parameters allow for flexible and reusable test definitions.",
"test-definition-sql-expression-placeholder": "SELECT * FROM {table} WHERE {column} < {{minValue}} OR {column} > {{maxValue}}",
"test-definition-sql-expression-tooltip": "SQL query template using parameter placeholders in double curly braces (e.g., {{paramName}}). Use {table} and {column} for runtime entity references.",
"test-definition-sql-query-help": "Write SQL query template with substitution variables. Use {table} for table name, {column} for column name (resolved at runtime). Use {{paramName}} for user parameters defined below (e.g., {{minValue}}, {{maxValue}}).",
"this-action-cannot-be-undone": "This action cannot be undone.",
"unauthorized-user": "משתמש לא מורשה! יש לבדוק את כתובת האימייל או הסיסמה",
"unexpected-error": "אירעה שגיאה לא צפויה.",

View file

@ -431,6 +431,7 @@
"data-profiler-metrics": "データプロファイラメトリクス",
"data-proportion-plural": "データ構成比",
"data-quality": "データ品質",
"data-quality-dimension": "Data Quality Dimension",
"data-quality-test": "データ品質テスト",
"data-quality-test-plural": "データ品質テスト",
"data-quartile-plural": "データ四分位数",
@ -613,6 +614,7 @@
"enter-custom-unit-of-measurement": "カスタム測定単位を入力してEnterを押してください",
"enter-each-value-and-press-enter": "各値を入力してEnterキーを押してください",
"enter-entity": "{{entity}} を入力",
"enter-entity-description": "Enter {{entity}} description",
"enter-entity-name": "{{entity}} の名前を入力",
"enter-entity-value": "{{entity}} の値を入力",
"enter-field-description": "{{field}} の説明を入力",
@ -1253,6 +1255,9 @@
"page-not-found": "ページが見つかりません",
"page-views-by-data-asset-plural": "データアセットごとのページビュー",
"parameter": "パラメータ",
"parameter-description": "Parameter Description",
"parameter-display-name": "Parameter Display Name",
"parameter-name": "Parameter Name",
"parameter-plural": "パラメータ",
"parent": "親",
"parsing-timeout-limit": "クエリ解析のタイムアウト上限",
@ -1436,6 +1441,7 @@
"request-method": "リクエストメソッド",
"request-schema-field": "リクエストスキーマフィールド",
"request-tag-plural": "タグをリクエスト",
"required": "Required",
"requirement-plural": "要件",
"reset": "リセット",
"reset-default-layout": "レイアウトをデフォルトにリセット",
@ -1495,6 +1501,7 @@
"rule-with-name": "- ルール: {{ruleName}}",
"rules-count": "{{count}} ルール)",
"rules-evaluated": "評価されたルール",
"rules-library": "Rules Library",
"run": "実行",
"run-agent-plural": "エージェントを実行",
"run-at": "実行日時",
@ -1645,7 +1652,9 @@
"specific-data-asset-plural": "特定のデータアセット",
"spreadsheet": "スプレッドシート",
"spreadsheet-plural": "スプレッドシート",
"sql-expression": "SQL Expression",
"sql-function": "SQL 関数",
"sql-query": "SQL Query",
"sql-uppercase": "SQL",
"sql-uppercase-query": "SQL クエリ",
"sso": "sso",
@ -1705,6 +1714,7 @@
"sunday": "日曜日",
"support": "サポート",
"support-url": "サポート URL",
"supported-data-type-plural": "Supported Data Types",
"supported-language-plural": "対応言語",
"switch-persona": "ペルソナ切り替え",
"sync-alert-offset": "アラートを同期",
@ -1770,10 +1780,13 @@
"test-case-plural": "テストケース",
"test-case-resolution-status": "テストケースの解決状況",
"test-case-result": "テストケース結果",
"test-definition": "Test Definition",
"test-definition-plural": "Test Definitions",
"test-email": "テストメール",
"test-email-connection": "メール接続をテスト",
"test-entity": "{{entity}} をテスト",
"test-level-lowercase": "テストレベル",
"test-platform-plural": "Test Platforms",
"test-plural": "テスト",
"test-plural-type": "{{type}}テスト",
"test-suite": "テストスイート",
@ -2534,6 +2547,7 @@
"page-sub-header-for-storages": "人気のストレージサービスからメタデータを取り込みます。",
"page-sub-header-for-table-profile": "プロファイラーでテーブル構造を監視・理解します。",
"page-sub-header-for-teams": "階層的なチーム構造で組織全体を表現します。",
"page-sub-header-for-test-definitions": "Manage and configure test definitions for data quality rules.",
"page-sub-header-for-users": "組織の一般ユーザーを管理します。管理者については、管理者ページをご覧ください。",
"paid-addon-description": "<0>{{app}}</0> は Collate の有料アドオンです。",
"passed-x-checks": "Passed {{count}} checks",
@ -2660,6 +2674,8 @@
"synonym-placeholder": "シニムを追加するには、入力してEnterを押してください",
"system-alert-edit-message": "システム生成アラートの編集はできません。",
"system-tag-delete-disable-message": "システム生成タグは削除できません。代わりに無効化をお試しください。",
"system-test-definition-delete-warning": "Deleting a system generated test definition is not allowed.",
"system-test-definition-edit-warning": "Editing a system generated test definition is not allowed.",
"tag-update-confirmation": "タグを更新しますか?",
"tailor-experience-for-persona": "{{persona}}向けに最適化された体験をカスタマイズしてご利用ください!",
"take-quick-product-tour": "クイックツアーで製品を体験しましょう!",
@ -2675,6 +2691,8 @@
"default": "{{service_type}} が接続を許可するように構成されていることを確認してください",
"withIp": "{{ip}} からの接続が許可されていることを確認してください"
},
"test-definition-parameters-description": "Defines configurable input parameters for test cases. Each parameter includes a name, data type (string, int, date, etc.), display name, and optional validation rules. Parameters can be referenced in SQL expressions using {{paramName}} syntax and allow users to customize test behavior when creating test cases.",
"test-definition-sql-query-help": "SQL expression template for custom SQL-based test definitions. Supports substitution variables: {table} and {column} for runtime entity references, and {{paramName}} for user-defined parameters. This field is only applicable for test definitions with testPlatforms set to 'OpenMetadata' and is used to execute custom SQL queries for data quality validation.",
"test-your-connection-before-creating-service": "サービスを作成する前に接続テストを実行してください。",
"testing-your-connection-may-take-two-minutes": "接続テストには最大で2分かかることがあります。",
"this-action-is-not-allowed-for-deleted-entities": "削除されたエンティティにはこの操作を行うことができません。",
@ -2774,7 +2792,9 @@
"entities-deleted-successfully": "{{count}}個の{{entity}}を正常に削除しました",
"entity-already-exist": "{{entity}}「{{name}}」はすでに存在します。同じ{{entity}}は作成できません。",
"entity-already-exist-message-without-name": "指定された情報で{{entity}}がすでに存在します。同じ{{entityPlural}}は作成できません。",
"entity-created-success": "{{entity}} created successfully!",
"entity-creation-error": "{{entity}}の作成中にエラーが発生しました",
"entity-deleted-success": "{{entity}} deleted successfully!",
"entity-deleted-successfully": "{{entity}}の削除に成功しました!",
"entity-details-fetch-error": "{{entityType}} {{entityName}}の詳細取得中にエラーが発生しました",
"entity-feed-fetch-error": "エンティティのフィード件数取得中にエラーが発生しました!",
@ -2784,6 +2804,7 @@
"entity-limit-reached": "{{entity}}の上限に達しました",
"entity-removing-error": "{{entity}}の削除中にエラーが発生しました",
"entity-unfollow-error": "{{entity}}のフォロー解除中にエラーが発生しました",
"entity-updated-success": "{{entity}} updated successfully!",
"entity-updating-error": "{{entity}}の更新中にエラーが発生しました",
"error-selected-node-name-details": "{{selectedNodeName}}の詳細取得中にエラーが発生しました",
"error-while-renewing-id-token-with-message": "Auth0 SSOによるIDトークン更新中にエラーが発生しました{{message}}",
@ -2824,6 +2845,10 @@
"task-resolved-successfully": "タスクが正常に解決されました",
"team-moved-error": "チームの移動中にエラーが発生しました",
"test-connection-error": "接続テスト中にエラーが発生しました!",
"test-definition-parameters-description": "Define parameters that users can configure when creating test cases from this definition. Parameters allow for flexible and reusable test definitions.",
"test-definition-sql-expression-placeholder": "SELECT * FROM {table} WHERE {column} < {{minValue}} OR {column} > {{maxValue}}",
"test-definition-sql-expression-tooltip": "SQL query template using parameter placeholders in double curly braces (e.g., {{paramName}}). Use {table} and {column} for runtime entity references.",
"test-definition-sql-query-help": "Write SQL query template with substitution variables. Use {table} for table name, {column} for column name (resolved at runtime). Use {{paramName}} for user parameters defined below (e.g., {{minValue}}, {{maxValue}}).",
"this-action-cannot-be-undone": "This action cannot be undone.",
"unauthorized-user": "認証されていないユーザーです!メールアドレスまたはパスワードをご確認ください。",
"unexpected-error": "予期しないエラーが発生しました。",

View file

@ -431,6 +431,7 @@
"data-profiler-metrics": "데이터 프로파일러 메트릭",
"data-proportion-plural": "데이터 비율",
"data-quality": "데이터 품질",
"data-quality-dimension": "Data Quality Dimension",
"data-quality-test": "데이터 품질 테스트",
"data-quality-test-plural": "데이터 품질 테스트들",
"data-quartile-plural": "데이터 사분위수",
@ -613,6 +614,7 @@
"enter-custom-unit-of-measurement": "사용자 지정 측정 단위를 입력하고 Enter를 누르세요",
"enter-each-value-and-press-enter": "각 값을 입력하고 Enter를 누르세요",
"enter-entity": "{{entity}} 입력",
"enter-entity-description": "Enter {{entity}} description",
"enter-entity-name": "{{entity}} 이름 입력",
"enter-entity-value": "{{entity}} 값 입력",
"enter-field-description": "{{field}} 설명 입력",
@ -1253,6 +1255,9 @@
"page-not-found": "페이지를 찾을 수 없습니다",
"page-views-by-data-asset-plural": "데이터 자산별 페이지 조회수",
"parameter": "매개변수",
"parameter-description": "Parameter Description",
"parameter-display-name": "Parameter Display Name",
"parameter-name": "Parameter Name",
"parameter-plural": "매개변수들",
"parent": "상위",
"parsing-timeout-limit": "쿼리 구문 분석 시간 제한",
@ -1436,6 +1441,7 @@
"request-method": "요청 메서드",
"request-schema-field": "요청 스키마 필드",
"request-tag-plural": "태그 요청",
"required": "Required",
"requirement-plural": "요구사항들",
"reset": "초기화",
"reset-default-layout": "기본 레이아웃 초기화",
@ -1495,6 +1501,7 @@
"rule-with-name": "- Rule: {{ruleName}}",
"rules-count": "({{count}} rules)",
"rules-evaluated": "평가된 규칙",
"rules-library": "Rules Library",
"run": "실행",
"run-agent-plural": "에이전트 실행",
"run-at": "실행 시간",
@ -1645,7 +1652,9 @@
"specific-data-asset-plural": "특정 데이터 자산들",
"spreadsheet": "스프레드시트",
"spreadsheet-plural": "스프레드시트",
"sql-expression": "SQL Expression",
"sql-function": "SQL 함수",
"sql-query": "SQL Query",
"sql-uppercase": "SQL",
"sql-uppercase-query": "SQL 쿼리",
"sso": "sso",
@ -1705,6 +1714,7 @@
"sunday": "일요일",
"support": "지원",
"support-url": "지원 URL",
"supported-data-type-plural": "Supported Data Types",
"supported-language-plural": "지원되는 언어들",
"switch-persona": "페르소나 전환",
"sync-alert-offset": "동기화 알람",
@ -1770,10 +1780,13 @@
"test-case-plural": "테스트 케이스들",
"test-case-resolution-status": "테스트 케이스 해결 상태",
"test-case-result": "테스트 케이스 결과",
"test-definition": "Test Definition",
"test-definition-plural": "Test Definitions",
"test-email": "이메일 테스트",
"test-email-connection": "이메일 연결 테스트",
"test-entity": "{{entity}} 테스트",
"test-level-lowercase": "테스트 레벨",
"test-platform-plural": "Test Platforms",
"test-plural": "테스트들",
"test-plural-type": "{{type}} 테스트",
"test-suite": "테스트 스위트",
@ -2534,6 +2547,7 @@
"page-sub-header-for-storages": "가장 인기 있는 저장소 서비스에서 메타데이터를 수집하세요.",
"page-sub-header-for-table-profile": "프로파일러를 사용하여 테이블 구조를 모니터링하고 이해하세요.",
"page-sub-header-for-teams": "계층적 팀으로 전체 조직 구조를 표현하세요.",
"page-sub-header-for-test-definitions": "Manage and configure test definitions for data quality rules.",
"page-sub-header-for-users": "계층적 팀으로 전체 조직 구조를 표현하세요.",
"paid-addon-description": "<0>{{app}}</0>은(는) Collate 고객을 위한 유료 애드온입니다.",
"passed-x-checks": "{{count}}개 검사 통과",
@ -2660,6 +2674,8 @@
"synonym-placeholder": "동의어를 추가하려면 입력하고 Enter 키를 누르세요",
"system-alert-edit-message": "시스템에서 생성된 알림은 편집할 수 없습니다.",
"system-tag-delete-disable-message": "시스템에서 생성된 태그를 삭제하는 것은 허용되지 않습니다. 대신 태그를 비활성화할 수 있습니다.",
"system-test-definition-delete-warning": "Deleting a system generated test definition is not allowed.",
"system-test-definition-edit-warning": "Editing a system generated test definition is not allowed.",
"tag-update-confirmation": "새 태그를 추가하시겠습니까?",
"tailor-experience-for-persona": "{{persona}} 페르소나에 맞게 경험을 맞춤 설정하고 시도해보세요!",
"take-quick-product-tour": "시작하려면 제품 투어를 진행하세요!",
@ -2675,6 +2691,8 @@
"default": "{{service_type}} 가 연결을 허용하도록 구성되어 있는지 확인하세요",
"withIp": " {{ip}} 에서"
},
"test-definition-parameters-description": "Defines configurable input parameters for test cases. Each parameter includes a name, data type (string, int, date, etc.), display name, and optional validation rules. Parameters can be referenced in SQL expressions using {{paramName}} syntax and allow users to customize test behavior when creating test cases.",
"test-definition-sql-query-help": "SQL expression template for custom SQL-based test definitions. Supports substitution variables: {table} and {column} for runtime entity references, and {{paramName}} for user-defined parameters. This field is only applicable for test definitions with testPlatforms set to 'OpenMetadata' and is used to execute custom SQL queries for data quality validation.",
"test-your-connection-before-creating-service": "서비스를 생성하기 전에 연결을 테스트하세요",
"testing-your-connection-may-take-two-minutes": "연결 테스트는 최대 2분이 소요될 수 있습니다",
"this-action-is-not-allowed-for-deleted-entities": "이 작업은 삭제된 엔티티에 대해 허용되지 않습니다.",
@ -2774,7 +2792,9 @@
"entities-deleted-successfully": "{{count}}개의 {{entity}} 삭제 성공",
"entity-already-exist": "{{entity}} \"{{name}}\"이(가) 이미 존재합니다. 중복된 {{entityPlural}}은(는) 허용되지 않습니다.",
"entity-already-exist-message-without-name": "주어진 세부 정보를 가진 {{entity}}가 이미 존재합니다. 중복된 {{entityPlural}}은(는) 허용되지 않습니다.",
"entity-created-success": "{{entity}} created successfully!",
"entity-creation-error": "{{entity}} 생성 중 오류가 발생했습니다",
"entity-deleted-success": "{{entity}} deleted successfully!",
"entity-deleted-successfully": "\"{{entity}}\"이(가) 성공적으로 삭제되었습니다!",
"entity-details-fetch-error": "{{entityType}} {{entityName}}의 세부 정보를 가져오는 중 오류가 발생했습니다",
"entity-feed-fetch-error": "엔티티 피드 수를 가져오는 중 오류가 발생했습니다!",
@ -2784,6 +2804,7 @@
"entity-limit-reached": "{{entity}} 한도에 도달했습니다",
"entity-removing-error": "{{entity}} 제거 중 오류가 발생했습니다",
"entity-unfollow-error": "{{entity}} 언팔로우 중 오류가 발생했습니다",
"entity-updated-success": "{{entity}} updated successfully!",
"entity-updating-error": "{{entity}} 업데이트 중 오류가 발생했습니다",
"error-selected-node-name-details": "{{selectedNodeName}} 세부 정보를 가져오는 중 오류가 발생했습니다",
"error-while-renewing-id-token-with-message": "Auth0 SSO에서 ID 토큰 갱신 중 오류 발생: {{message}}",
@ -2824,6 +2845,10 @@
"task-resolved-successfully": "작업이 성공적으로 해결되었습니다",
"team-moved-error": "팀 이동 중 오류가 발생했습니다",
"test-connection-error": "연결 테스트 중 오류가 발생했습니다!",
"test-definition-parameters-description": "Define parameters that users can configure when creating test cases from this definition. Parameters allow for flexible and reusable test definitions.",
"test-definition-sql-expression-placeholder": "SELECT * FROM {table} WHERE {column} < {{minValue}} OR {column} > {{maxValue}}",
"test-definition-sql-expression-tooltip": "SQL query template using parameter placeholders in double curly braces (e.g., {{paramName}}). Use {table} and {column} for runtime entity references.",
"test-definition-sql-query-help": "Write SQL query template with substitution variables. Use {table} for table name, {column} for column name (resolved at runtime). Use {{paramName}} for user parameters defined below (e.g., {{minValue}}, {{maxValue}}).",
"this-action-cannot-be-undone": "This action cannot be undone.",
"unauthorized-user": "인증되지 않은 사용자입니다! 이메일 또는 비밀번호를 확인해주세요",
"unexpected-error": "예기치 않은 오류가 발생했습니다.",

View file

@ -431,6 +431,7 @@
"data-profiler-metrics": "डेटा प्रोफाइलर मेट्रिक्स",
"data-proportion-plural": "डेटा प्रमाण",
"data-quality": "डेटा गुणवत्ता",
"data-quality-dimension": "Data Quality Dimension",
"data-quality-test": "डेटा गुणवत्ता चाचणी",
"data-quality-test-plural": "डेटा गुणवत्ता परीक्षा",
"data-quartile-plural": "डेटा चतुर्थांश",
@ -613,6 +614,7 @@
"enter-custom-unit-of-measurement": "कस्टम मापन एकक प्रविष्ट करा आणि Enter दाबा",
"enter-each-value-and-press-enter": "Enter each value and press Enter",
"enter-entity": "{{entity}} प्रविष्ट करा",
"enter-entity-description": "Enter {{entity}} description",
"enter-entity-name": "{{entity}} नाव प्रविष्ट करा",
"enter-entity-value": "{{entity}} मूल्य प्रविष्ट करा",
"enter-field-description": "{{field}} वर्णन प्रविष्ट करा",
@ -1253,6 +1255,9 @@
"page-not-found": "पृष्ठ सापडले नाही",
"page-views-by-data-asset-plural": "डेटा ॲसेट्सने पृष्ठ दृश्ये",
"parameter": "पॅरामीटर",
"parameter-description": "Parameter Description",
"parameter-display-name": "Parameter Display Name",
"parameter-name": "Parameter Name",
"parameter-plural": "पॅरामीटर्स",
"parent": "पालक",
"parsing-timeout-limit": "क्वेरी पार्सिंग टाइमआउट मर्यादा",
@ -1436,6 +1441,7 @@
"request-method": "विनंती पद्धत",
"request-schema-field": "विनंती स्कीमा फील्ड",
"request-tag-plural": "विनंती टॅग",
"required": "Required",
"requirement-plural": "आवश्यकता",
"reset": "रीसेट",
"reset-default-layout": "मूलभूत लेआउट रीसेट करा",
@ -1495,6 +1501,7 @@
"rule-with-name": "- Rule: {{ruleName}}",
"rules-count": "({{count}} rules)",
"rules-evaluated": "Rules Evaluated",
"rules-library": "Rules Library",
"run": "चालवा",
"run-agent-plural": "एजेंट्स चलवा",
"run-at": "येथे चालवा",
@ -1645,7 +1652,9 @@
"specific-data-asset-plural": "विशिष्ट डेटा ॲसेट",
"spreadsheet": "स्प्रेडशीट",
"spreadsheet-plural": "स्प्रेडशीट्स",
"sql-expression": "SQL Expression",
"sql-function": "SQL फंक्शन",
"sql-query": "SQL Query",
"sql-uppercase": "SQL",
"sql-uppercase-query": "SQL क्वेरी",
"sso": "sso",
@ -1705,6 +1714,7 @@
"sunday": "रविवार",
"support": "समर्थन",
"support-url": "समर्थन URL",
"supported-data-type-plural": "Supported Data Types",
"supported-language-plural": "समर्थित भाषा",
"switch-persona": "व्यक्तिमत्त्व बदला",
"sync-alert-offset": "सूचना समक्रमित करा",
@ -1770,10 +1780,13 @@
"test-case-plural": "चाचणी प्रकरणे",
"test-case-resolution-status": "टेस्ट केस स्थिती सुलभीकरण",
"test-case-result": "चाचणी प्रकरण परिणाम",
"test-definition": "Test Definition",
"test-definition-plural": "Test Definitions",
"test-email": "चाचणी ईमेल",
"test-email-connection": "चाचणी ईमेल कनेक्शन",
"test-entity": "चाचणी {{entity}}",
"test-level-lowercase": "चाचणी पातळी",
"test-platform-plural": "Test Platforms",
"test-plural": "चाचण्या",
"test-plural-type": "{{type}} चाचण्या",
"test-suite": "चाचणी संच",
@ -2534,6 +2547,7 @@
"page-sub-header-for-storages": "सर्वात लोकप्रिय स्टोरेज सेवांमधून मेटाडेटा अंतर्ग्रहण करा.",
"page-sub-header-for-table-profile": "प्रोफाइलरसह तुमच्या तक्त्यांची रचना मॉनिटर आणि समजून घ्या",
"page-sub-header-for-teams": "सांकेतिक टीम्ससह तुमची संपूर्ण संस्थात्मक रचना दर्शवा.",
"page-sub-header-for-test-definitions": "Manage and configure test definitions for data quality rules.",
"page-sub-header-for-users": "आपल्या संस्थेतील नियमित वापरकर्त्यांचे व्यवस्थापन करा. प्रशासकांसाठी, प्रशासक पृष्ठास भेट द्या.",
"paid-addon-description": "<0>{{app}}</0> हे Collate ग्राहकांसाठी एक सशुल्क अॅड-ऑन आहे.",
"passed-x-checks": "{{count}} तपासण्या उत्तीर्ण",
@ -2660,6 +2674,8 @@
"synonym-placeholder": "समानार्थी शब्द जोडण्यासाठी, फक्त टाइप करा आणि Enter दाबा",
"system-alert-edit-message": "सिस्टम व्युत्पन्न अलर्ट संपादित करण्याची परवानगी नाही.",
"system-tag-delete-disable-message": "सिस्टम व्युत्पन्न टॅग्स हटविण्याची परवानगी नाही. त्याऐवजी तुम्ही टॅग अक्षम करण्याचा प्रयत्न करू शकता.",
"system-test-definition-delete-warning": "Deleting a system generated test definition is not allowed.",
"system-test-definition-edit-warning": "Editing a system generated test definition is not allowed.",
"tag-update-confirmation": "नवीन टॅग जोडण्यासाठी तुम्हाला पुढे जायचे आहे का?",
"tailor-experience-for-persona": "{{persona}} पर्सोनासाठी अनुभव अनुकूलित करा, प्रयत्न करा!",
"take-quick-product-tour": "प्रारंभ करण्यासाठी एक उत्पादन टूर घ्या!",
@ -2675,6 +2691,8 @@
"default": "कृपया खात्री करा की {{service_type}} कनेक्शनसाठी कॉन्फिगर केले गेले आहे",
"withIp": " {{ip}} वरून"
},
"test-definition-parameters-description": "Defines configurable input parameters for test cases. Each parameter includes a name, data type (string, int, date, etc.), display name, and optional validation rules. Parameters can be referenced in SQL expressions using {{paramName}} syntax and allow users to customize test behavior when creating test cases.",
"test-definition-sql-query-help": "SQL expression template for custom SQL-based test definitions. Supports substitution variables: {table} and {column} for runtime entity references, and {{paramName}} for user-defined parameters. This field is only applicable for test definitions with testPlatforms set to 'OpenMetadata' and is used to execute custom SQL queries for data quality validation.",
"test-your-connection-before-creating-service": "सेवा तयार करण्यापूर्वी तुमचे कनेक्शन चाचणी करा",
"testing-your-connection-may-take-two-minutes": "तुमचे कनेक्शन चाचणी करण्यास 2 मिनिटे लागू शकतात",
"this-action-is-not-allowed-for-deleted-entities": "मिटवलेल्या घटकांसाठी ही क्रिया अनुमत नाही.",
@ -2774,7 +2792,9 @@
"entities-deleted-successfully": "Successfully deleted {{count}} {{entity}}s",
"entity-already-exist": "{{entity}} \"{{name}}\" आधीच अस्तित्वात आहे. डुप्लिकेट {{entityPlural}} अनुमत नाहीत.",
"entity-already-exist-message-without-name": "दिलेल्या तपशीलांसह {{entity}} आधीच अस्तित्वात आहे. डुप्लिकेट {{entityPlural}} अनुमत नाहीत.",
"entity-created-success": "{{entity}} created successfully!",
"entity-creation-error": "{{entity}} तयार करताना त्रुटी",
"entity-deleted-success": "{{entity}} deleted successfully!",
"entity-deleted-successfully": "\"{{entity}}\" यशस्वीरित्या मिटवले!",
"entity-details-fetch-error": "{{entityType}} {{entityName}} साठी तपशील मिळवताना त्रुटी",
"entity-feed-fetch-error": "घटक फीड गणना मिळवताना त्रुटी!",
@ -2784,6 +2804,7 @@
"entity-limit-reached": "{{entity}} मर्यादा गाठली",
"entity-removing-error": "{{entity}} काढताना त्रुटी",
"entity-unfollow-error": "{{entity}} अनफॉलो करताना त्रुटी",
"entity-updated-success": "{{entity}} updated successfully!",
"entity-updating-error": "{{entity}} अद्यतनित करताना त्रुटी",
"error-selected-node-name-details": "{{selectedNodeName}} तपशील मिळवताना त्रुटी",
"error-while-renewing-id-token-with-message": "Auth0 SSO मधून id टोकन नूतनीकरण करताना त्रुटी: {{message}}",
@ -2824,6 +2845,10 @@
"task-resolved-successfully": "कार्य यशस्वीरित्या सोडवले",
"team-moved-error": "टीम हलवताना त्रुटी",
"test-connection-error": "कनेक्शन चाचणी करताना त्रुटी!",
"test-definition-parameters-description": "Define parameters that users can configure when creating test cases from this definition. Parameters allow for flexible and reusable test definitions.",
"test-definition-sql-expression-placeholder": "SELECT * FROM {table} WHERE {column} < {{minValue}} OR {column} > {{maxValue}}",
"test-definition-sql-expression-tooltip": "SQL query template using parameter placeholders in double curly braces (e.g., {{paramName}}). Use {table} and {column} for runtime entity references.",
"test-definition-sql-query-help": "Write SQL query template with substitution variables. Use {table} for table name, {column} for column name (resolved at runtime). Use {{paramName}} for user parameters defined below (e.g., {{minValue}}, {{maxValue}}).",
"this-action-cannot-be-undone": "This action cannot be undone.",
"unauthorized-user": "अनधिकृत वापरकर्ता! कृपया ईमेल किंवा पासवर्ड तपासा",
"unexpected-error": "अप्रत्याशित त्रुटी आली.",

View file

@ -431,6 +431,7 @@
"data-profiler-metrics": "Dataprofilemetrieken",
"data-proportion-plural": "Dataproporties",
"data-quality": "Datakwaliteit",
"data-quality-dimension": "Data Quality Dimension",
"data-quality-test": "Datakwaliteitstest",
"data-quality-test-plural": "Data Qualiteits Tests",
"data-quartile-plural": "Datakwartielen",
@ -613,6 +614,7 @@
"enter-custom-unit-of-measurement": "Voer een aangepaste maateenheid in en druk op Enter",
"enter-each-value-and-press-enter": "Voer elke waarde in en druk op Enter",
"enter-entity": "{{entity}} invoeren",
"enter-entity-description": "Enter {{entity}} description",
"enter-entity-name": "{{entity}}-naam invoeren",
"enter-entity-value": "{{entity}}-waarde invoeren",
"enter-field-description": "{{field}}-beschrijving invoeren",
@ -1253,6 +1255,9 @@
"page-not-found": "Pagina niet gevonden",
"page-views-by-data-asset-plural": "Paginaweergaven per data-asset",
"parameter": "Parameter",
"parameter-description": "Parameter Description",
"parameter-display-name": "Parameter Display Name",
"parameter-name": "Parameter Name",
"parameter-plural": "Parameters",
"parent": "Ouder",
"parsing-timeout-limit": "Time-outlimiet voor query-analyse",
@ -1436,6 +1441,7 @@
"request-method": "Request Method",
"request-schema-field": "Request Schema Field",
"request-tag-plural": "Aanvraagtags",
"required": "Required",
"requirement-plural": "Vereisten",
"reset": "Herstellen",
"reset-default-layout": "Standaardindeling herstellen",
@ -1495,6 +1501,7 @@
"rule-with-name": "- Rule: {{ruleName}}",
"rules-count": "({{count}} rules)",
"rules-evaluated": "Regels geëvalueerd",
"rules-library": "Rules Library",
"run": "Uitvoeren",
"run-agent-plural": "Agents Uitvoeren",
"run-at": "Uitvoeren op",
@ -1645,7 +1652,9 @@
"specific-data-asset-plural": "Specifieke data-assets",
"spreadsheet": "Spreadsheet",
"spreadsheet-plural": "Spreadsheets",
"sql-expression": "SQL Expression",
"sql-function": "SQL-functie",
"sql-query": "SQL Query",
"sql-uppercase": "SQL",
"sql-uppercase-query": "SQL-Query",
"sso": "sso",
@ -1705,6 +1714,7 @@
"sunday": "zondag",
"support": "Ondersteuning",
"support-url": "Ondersteunings-URL",
"supported-data-type-plural": "Supported Data Types",
"supported-language-plural": "Ondersteunde talen",
"switch-persona": "Persona wisselen",
"sync-alert-offset": "Waarschuwing synchroniseren",
@ -1770,10 +1780,13 @@
"test-case-plural": "Testcases",
"test-case-resolution-status": "Test Case Resolution Status",
"test-case-result": "Testcaseresultaten",
"test-definition": "Test Definition",
"test-definition-plural": "Test Definitions",
"test-email": "Test E-mail",
"test-email-connection": "Test emailconnectie",
"test-entity": "Test {{entity}}",
"test-level-lowercase": "testniveau",
"test-platform-plural": "Test Platforms",
"test-plural": "Tests",
"test-plural-type": "{{type}}-testen",
"test-suite": "Testsuite",
@ -2534,6 +2547,7 @@
"page-sub-header-for-storages": "Ingest metadata van de meestgebruikte opslagservices.",
"page-sub-header-for-table-profile": "Volg en begrijp de structuur van je tabellen met de profiler.",
"page-sub-header-for-teams": "Vertegenwoordig je hele organisatiestructuur met hiërarchische teams.",
"page-sub-header-for-test-definitions": "Manage and configure test definitions for data quality rules.",
"page-sub-header-for-users": "Beheer reguliere gebruikers in uw organisatie. Voor beheerders, bezoek de Beheerders pagina.",
"paid-addon-description": "<0>{{app}}</0> is een betaalde add-on voor Collate-klanten.",
"passed-x-checks": "{{count}} controles geslaagd",
@ -2660,6 +2674,8 @@
"synonym-placeholder": "Voeg een synoniem toe door het gewoon in te typen en op Enter te drukken",
"system-alert-edit-message": "Het bewerken van een door het systeem gegenereerde alert is niet toegestaan.",
"system-tag-delete-disable-message": "Het verwijderen van door het systeem gegenereerde tags is niet toegestaan. Je kunt proberen de tag uit te schakelen.",
"system-test-definition-delete-warning": "Deleting a system generated test definition is not allowed.",
"system-test-definition-edit-warning": "Editing a system generated test definition is not allowed.",
"tag-update-confirmation": "Wilt u doorgaan met het bijwerken van de tags?",
"tailor-experience-for-persona": "Pas de ervaring specifiek aan voor de {{persona}} persona, probeer het eens!",
"take-quick-product-tour": "Maak een producttour om aan de slag te gaan!",
@ -2675,6 +2691,8 @@
"default": "Zorg ervoor dat {{service_type}} is geconfigureerd om verbindingen toe te staan",
"withIp": " vanaf {{ip}}"
},
"test-definition-parameters-description": "Defines configurable input parameters for test cases. Each parameter includes a name, data type (string, int, date, etc.), display name, and optional validation rules. Parameters can be referenced in SQL expressions using {{paramName}} syntax and allow users to customize test behavior when creating test cases.",
"test-definition-sql-query-help": "SQL expression template for custom SQL-based test definitions. Supports substitution variables: {table} and {column} for runtime entity references, and {{paramName}} for user-defined parameters. This field is only applicable for test definitions with testPlatforms set to 'OpenMetadata' and is used to execute custom SQL queries for data quality validation.",
"test-your-connection-before-creating-service": "Test je connecties voordat je de service maakt",
"testing-your-connection-may-take-two-minutes": "Het testen van je connecties kan tot 2 minuten duren",
"this-action-is-not-allowed-for-deleted-entities": "Deze actie is niet toegestaan voor verwijderde entiteiten.",
@ -2774,7 +2792,9 @@
"entities-deleted-successfully": "{{count}} {{entity}}s succesvol verwijderd",
"entity-already-exist": "{{entity}} \"{{name}}\" bestaat al. Gedupliceerde {{entityPlural}} zijn niet toegestaan.",
"entity-already-exist-message-without-name": "Een {{entity}} met de opgegeven data bestaat al. Gedupliceerde {{entityPlural}} zijn niet toegestaan.",
"entity-created-success": "{{entity}} created successfully!",
"entity-creation-error": "Fout bij het maken van {{entity}}",
"entity-deleted-success": "{{entity}} deleted successfully!",
"entity-deleted-successfully": "{{entity}} succesvol verwijderd!",
"entity-details-fetch-error": "Fout bij het ophalen van details voor {{entityType}} {{entityName}}",
"entity-feed-fetch-error": "Fout bij het ophalen van feedtelling voor entiteit!",
@ -2784,6 +2804,7 @@
"entity-limit-reached": "{{entity}} limit reached",
"entity-removing-error": "Fout bij het verwijderen van {{entity}}",
"entity-unfollow-error": "Fout bij het ontvolgen van {{entity}}",
"entity-updated-success": "{{entity}} updated successfully!",
"entity-updating-error": "Fout bij het updaten van {{entity}}",
"error-selected-node-name-details": "Fout bij het ophalen van details voor {{selectedNodeName}}",
"error-while-renewing-id-token-with-message": "Fout bij het vernieuwen van id-token vanuit Auth0 SSO: {{message}}",
@ -2824,6 +2845,10 @@
"task-resolved-successfully": "Taak succesvol opgelost",
"team-moved-error": "Fout bij het verplaatsen van team",
"test-connection-error": "Fout bij het testen van de connectie!",
"test-definition-parameters-description": "Define parameters that users can configure when creating test cases from this definition. Parameters allow for flexible and reusable test definitions.",
"test-definition-sql-expression-placeholder": "SELECT * FROM {table} WHERE {column} < {{minValue}} OR {column} > {{maxValue}}",
"test-definition-sql-expression-tooltip": "SQL query template using parameter placeholders in double curly braces (e.g., {{paramName}}). Use {table} and {column} for runtime entity references.",
"test-definition-sql-query-help": "Write SQL query template with substitution variables. Use {table} for table name, {column} for column name (resolved at runtime). Use {{paramName}} for user parameters defined below (e.g., {{minValue}}, {{maxValue}}).",
"this-action-cannot-be-undone": "This action cannot be undone.",
"unauthorized-user": "Ongeautoriseerde gebruiker! controleer e-mail of wachtwoord",
"unexpected-error": "Er is een onverwachte fout opgetreden.",

View file

@ -431,6 +431,7 @@
"data-profiler-metrics": "شاخص‌های پروفایل داده",
"data-proportion-plural": "نسبت‌های داده",
"data-quality": "کیفیت داده",
"data-quality-dimension": "Data Quality Dimension",
"data-quality-test": "آزمون کیفیت داده",
"data-quality-test-plural": "Pruebas de Calidad de Datos",
"data-quartile-plural": "چهارک‌های داده",
@ -613,6 +614,7 @@
"enter-custom-unit-of-measurement": "واحد اندازه‌گیری سفارشی را وارد کرده و Enter را بزنید",
"enter-each-value-and-press-enter": "Enter each value and press Enter",
"enter-entity": "ورود {{entity}}",
"enter-entity-description": "Enter {{entity}} description",
"enter-entity-name": "نام {{entity}} را وارد کنید",
"enter-entity-value": "مقدار {{entity}} را وارد کنید",
"enter-field-description": "توضیحات {{field}} را وارد کنید",
@ -1253,6 +1255,9 @@
"page-not-found": "صفحه یافت نشد",
"page-views-by-data-asset-plural": "بازدیدهای صفحه توسط دارایی‌های داده",
"parameter": "پارامتر",
"parameter-description": "Parameter Description",
"parameter-display-name": "Parameter Display Name",
"parameter-name": "Parameter Name",
"parameter-plural": "پارامترها",
"parent": "والد",
"parsing-timeout-limit": "محدودیت زمان برای تجزیه پرس و جو",
@ -1436,6 +1441,7 @@
"request-method": "روش درخواست",
"request-schema-field": "فیلد اسکیما درخواست",
"request-tag-plural": "برچسب‌های درخواست",
"required": "Required",
"requirement-plural": "نیازمندی‌ها",
"reset": "بازنشانی",
"reset-default-layout": "بازنشانی طرح‌بندی پیش‌فرض",
@ -1495,6 +1501,7 @@
"rule-with-name": "- Rule: {{ruleName}}",
"rules-count": "({{count}} rules)",
"rules-evaluated": "Rules Evaluated",
"rules-library": "Rules Library",
"run": "اجرا",
"run-agent-plural": "اجرای عامل‌ها",
"run-at": "اجرا در",
@ -1645,7 +1652,9 @@
"specific-data-asset-plural": "دارایی‌های داده خاص",
"spreadsheet": "صفحه گسترده",
"spreadsheet-plural": "صفحات گسترده",
"sql-expression": "SQL Expression",
"sql-function": "تابع SQL",
"sql-query": "SQL Query",
"sql-uppercase": "SQL",
"sql-uppercase-query": "پرس و جوی SQL",
"sso": "sso",
@ -1705,6 +1714,7 @@
"sunday": "یکشنبه",
"support": "پشتیبانی",
"support-url": "آدرس پشتیبانی",
"supported-data-type-plural": "Supported Data Types",
"supported-language-plural": "زبان‌های پشتیبانی‌شده",
"switch-persona": "تغییر شخصیت",
"sync-alert-offset": "همگام‌سازی هشدار",
@ -1770,10 +1780,13 @@
"test-case-plural": "موردهای تست",
"test-case-resolution-status": "وضعیت حل موارد آزمون",
"test-case-result": "نتایج مورد تست",
"test-definition": "Test Definition",
"test-definition-plural": "Test Definitions",
"test-email": "ایمیل تست",
"test-email-connection": "تست اتصال ایمیل",
"test-entity": "تست {{entity}}",
"test-level-lowercase": "سطح تست",
"test-platform-plural": "Test Platforms",
"test-plural": "تست‌ها",
"test-plural-type": "Testy {{type}}",
"test-suite": "مجموعه تست",
@ -2534,6 +2547,7 @@
"page-sub-header-for-storages": "ورود متادیتا از پرکاربردترین سرویس‌های ذخیره‌سازی.",
"page-sub-header-for-table-profile": "نظارت و درک ساختار جداول خود با استفاده از پروفایلر.",
"page-sub-header-for-teams": "نمایش ساختار کامل سازمانی خود با تیم‌های سلسله‌مراتبی.",
"page-sub-header-for-test-definitions": "Manage and configure test definitions for data quality rules.",
"page-sub-header-for-users": "کاربران عادی سازمان خود را مدیریت کنید. برای مدیران، به صفحه مدیران مراجعه کنید.",
"paid-addon-description": "<0>{{app}}</0> افزونه پولی برای مشتریان Collate است.",
"passed-x-checks": "{{count}} بررسی موفق",
@ -2660,6 +2674,8 @@
"synonym-placeholder": "برای افزودن یک مترادف، آن را تایپ کنید و Enter را فشار دهید.",
"system-alert-edit-message": "ویرایش هشدار تولید شده توسط سیستم مجاز نیست.",
"system-tag-delete-disable-message": "حذف برچسب‌های تولید شده توسط سیستم مجاز نیست. می‌توانید برچسب را غیرفعال کنید.",
"system-test-definition-delete-warning": "Deleting a system generated test definition is not allowed.",
"system-test-definition-edit-warning": "Editing a system generated test definition is not allowed.",
"tag-update-confirmation": "آیا می‌خواهید به‌روزرسانی برچسب‌ها را ادامه دهید؟",
"tailor-experience-for-persona": "Tailor the experience specifically for the {{persona}} persona, just give it a spin!",
"take-quick-product-tour": "یک تور محصولی برای شروع بگذارید!",
@ -2675,6 +2691,8 @@
"default": "لطفاً اطمینان حاصل کنید که {{service_type}} به درستی برای اجازه اتصال پیکربندی شده است",
"withIp": " از {{ip}}"
},
"test-definition-parameters-description": "Defines configurable input parameters for test cases. Each parameter includes a name, data type (string, int, date, etc.), display name, and optional validation rules. Parameters can be referenced in SQL expressions using {{paramName}} syntax and allow users to customize test behavior when creating test cases.",
"test-definition-sql-query-help": "SQL expression template for custom SQL-based test definitions. Supports substitution variables: {table} and {column} for runtime entity references, and {{paramName}} for user-defined parameters. This field is only applicable for test definitions with testPlatforms set to 'OpenMetadata' and is used to execute custom SQL queries for data quality validation.",
"test-your-connection-before-creating-service": "اتصالات خود را قبل از ایجاد سرویس تست کنید.",
"testing-your-connection-may-take-two-minutes": "تست اتصالات شما ممکن است تا ۲ دقیقه طول بکشد.",
"this-action-is-not-allowed-for-deleted-entities": "این اقدام برای موجودیت‌های حذف شده مجاز نیست.",
@ -2774,7 +2792,9 @@
"entities-deleted-successfully": "Successfully deleted {{count}} {{entity}}s",
"entity-already-exist": "{{entity}} \"{{name}}\" قبلاً وجود دارد. موجودیت‌های تکراری مجاز نیستند.",
"entity-already-exist-message-without-name": "یک {{entity}} با جزئیات داده شده قبلاً وجود دارد. موجودیت‌های تکراری مجاز نیستند.",
"entity-created-success": "{{entity}} created successfully!",
"entity-creation-error": "خطا در ایجاد {{entity}}",
"entity-deleted-success": "{{entity}} deleted successfully!",
"entity-deleted-successfully": "\"{{entity}}\" با موفقیت حذف شد!",
"entity-details-fetch-error": "خطا در بازیابی جزئیات برای {{entityType}} {{entityName}}",
"entity-feed-fetch-error": "خطا در بازیابی تعداد فید موجودیت!",
@ -2784,6 +2804,7 @@
"entity-limit-reached": "محدودیت {{entity}} رسید.",
"entity-removing-error": "خطا در حذف {{entity}}",
"entity-unfollow-error": "خطا در لغو دنبال کردن {{entity}}",
"entity-updated-success": "{{entity}} updated successfully!",
"entity-updating-error": "خطا در به‌روزرسانی {{entity}}",
"error-selected-node-name-details": "خطا در دریافت جزئیات {{selectedNodeName}}",
"error-while-renewing-id-token-with-message": "خطا در تجدید توکن id از Auth0 SSO: {{message}}",
@ -2824,6 +2845,10 @@
"task-resolved-successfully": "وظیفه با موفقیت حل شد.",
"team-moved-error": "خطا در انتقال تیم.",
"test-connection-error": "خطا در تست اتصال!",
"test-definition-parameters-description": "Define parameters that users can configure when creating test cases from this definition. Parameters allow for flexible and reusable test definitions.",
"test-definition-sql-expression-placeholder": "SELECT * FROM {table} WHERE {column} < {{minValue}} OR {column} > {{maxValue}}",
"test-definition-sql-expression-tooltip": "SQL query template using parameter placeholders in double curly braces (e.g., {{paramName}}). Use {table} and {column} for runtime entity references.",
"test-definition-sql-query-help": "Write SQL query template with substitution variables. Use {table} for table name, {column} for column name (resolved at runtime). Use {{paramName}} for user parameters defined below (e.g., {{minValue}}, {{maxValue}}).",
"this-action-cannot-be-undone": "This action cannot be undone.",
"unauthorized-user": "کاربر غیرمجاز! لطفاً ایمیل یا رمز عبور را بررسی کنید.",
"unexpected-error": "یک خطای غیرمنتظره رخ داده است.",

View file

@ -431,6 +431,7 @@
"data-profiler-metrics": "Métricas do Examinador de Dados",
"data-proportion-plural": "Proporções de Dados",
"data-quality": "Qualidade de Dados",
"data-quality-dimension": "Data Quality Dimension",
"data-quality-test": "Teste de Qualidade de Dados",
"data-quality-test-plural": "Total de testes",
"data-quartile-plural": "Quartis de Dados",
@ -613,6 +614,7 @@
"enter-custom-unit-of-measurement": "Digite a unidade de medida personalizada e pressione Enter",
"enter-each-value-and-press-enter": "Digite cada valor e pressione Enter",
"enter-entity": "Inserir {{entity}}",
"enter-entity-description": "Enter {{entity}} description",
"enter-entity-name": "Inserir nome de {{entity}}",
"enter-entity-value": "Inserir Valor de {{entity}}",
"enter-field-description": "Inserir descrição de {{field}}",
@ -1253,6 +1255,9 @@
"page-not-found": "Página Não Encontrada",
"page-views-by-data-asset-plural": "Visualizações de Página por Ativos de Dados",
"parameter": "Parâmetro",
"parameter-description": "Parameter Description",
"parameter-display-name": "Parameter Display Name",
"parameter-name": "Parameter Name",
"parameter-plural": "Parâmetros",
"parent": "Pai",
"parsing-timeout-limit": "Limite de Tempo de Análise de Consulta",
@ -1436,6 +1441,7 @@
"request-method": "Método de solicitação",
"request-schema-field": "Campo de esquema da requisição",
"request-tag-plural": "Solicitar Tags",
"required": "Required",
"requirement-plural": "Requisitos",
"reset": "Redefinir",
"reset-default-layout": "Redefinir Layout Padrão",
@ -1495,6 +1501,7 @@
"rule-with-name": "- Rule: {{ruleName}}",
"rules-count": "({{count}} rules)",
"rules-evaluated": "Regras avaliadas",
"rules-library": "Rules Library",
"run": "Executar",
"run-agent-plural": "Executar Agentes",
"run-at": "Executar em",
@ -1645,7 +1652,9 @@
"specific-data-asset-plural": "Ativos de Dados Específicos",
"spreadsheet": "Planilha",
"spreadsheet-plural": "Planilhas",
"sql-expression": "SQL Expression",
"sql-function": "Função SQL",
"sql-query": "SQL Query",
"sql-uppercase": "SQL",
"sql-uppercase-query": "Consulta SQL",
"sso": "sso",
@ -1705,6 +1714,7 @@
"sunday": "Domingo",
"support": "Suporte",
"support-url": "URL de Suporte",
"supported-data-type-plural": "Supported Data Types",
"supported-language-plural": "Idiomas Suportados",
"switch-persona": "Trocar Persona",
"sync-alert-offset": "Sincronizar alerta",
@ -1770,10 +1780,13 @@
"test-case-plural": "Casos de Teste",
"test-case-resolution-status": "Status de Resolução do Caso de Teste",
"test-case-result": "Resultados do Caso de Teste",
"test-definition": "Test Definition",
"test-definition-plural": "Test Definitions",
"test-email": "E-mail de Teste",
"test-email-connection": "Testar conexão de e-mail",
"test-entity": "Teste {{entity}}",
"test-level-lowercase": "nível de teste",
"test-platform-plural": "Test Platforms",
"test-plural": "Testes",
"test-plural-type": "Testes de {{type}}",
"test-suite": "Conjunto de Testes",
@ -2534,6 +2547,7 @@
"page-sub-header-for-storages": "Ingestão de metadados dos serviços de armazenamento mais populares.",
"page-sub-header-for-table-profile": "Monitore e compreenda a estrutura de suas tabelas com o examinador.",
"page-sub-header-for-teams": "Represente toda a estrutura organizacional com equipes hierárquicas.",
"page-sub-header-for-test-definitions": "Manage and configure test definitions for data quality rules.",
"page-sub-header-for-users": "Gerencie os usuários regulares em sua organização. Para administradores, visite a página de Administradores.",
"paid-addon-description": "<0>{{app}}</0> é um complemento pago para clientes do Collate.",
"passed-x-checks": "{{count}} verificações aprovadas",
@ -2660,6 +2674,8 @@
"synonym-placeholder": "Para adicionar um sinônimo, basta digitá-lo e pressionar Enter",
"system-alert-edit-message": "Não é permitido editar um alerta gerado pelo sistema.",
"system-tag-delete-disable-message": "Não é permitido excluir tags geradas pelo sistema. Você pode tentar desativar a tag em vez disso.",
"system-test-definition-delete-warning": "Deleting a system generated test definition is not allowed.",
"system-test-definition-edit-warning": "Editing a system generated test definition is not allowed.",
"tag-update-confirmation": "Você gostaria de continuar com a atualização das tags?",
"tailor-experience-for-persona": "Adapte a experiência especificamente para o persona {{persona}}, experimente!",
"take-quick-product-tour": "Faça um tour pelo produto para começar!",
@ -2675,6 +2691,8 @@
"default": "Por favor, certifiquese de que o {{service_type}} está configurado para permitir conexões",
"withIp": " de {{ip}}"
},
"test-definition-parameters-description": "Defines configurable input parameters for test cases. Each parameter includes a name, data type (string, int, date, etc.), display name, and optional validation rules. Parameters can be referenced in SQL expressions using {{paramName}} syntax and allow users to customize test behavior when creating test cases.",
"test-definition-sql-query-help": "SQL expression template for custom SQL-based test definitions. Supports substitution variables: {table} and {column} for runtime entity references, and {{paramName}} for user-defined parameters. This field is only applicable for test definitions with testPlatforms set to 'OpenMetadata' and is used to execute custom SQL queries for data quality validation.",
"test-your-connection-before-creating-service": "Teste suas conexões antes de criar o serviço",
"testing-your-connection-may-take-two-minutes": "Testar suas conexões pode levar até 2 minutos",
"this-action-is-not-allowed-for-deleted-entities": "Esta ação não é permitida para entidades excluídas.",
@ -2774,7 +2792,9 @@
"entities-deleted-successfully": "{{count}} {{entity}}s excluídos com sucesso",
"entity-already-exist": "{{entity}} \"{{name}}\" já existe. {{entityPlural}} duplicados não são permitidos.",
"entity-already-exist-message-without-name": "Um {{entity}} com os detalhes fornecidos já existe. {{entityPlural}} duplicados não são permitidos.",
"entity-created-success": "{{entity}} created successfully!",
"entity-creation-error": "Erro ao criar {{entity}}",
"entity-deleted-success": "{{entity}} deleted successfully!",
"entity-deleted-successfully": "{{entity}} excluído com sucesso!",
"entity-details-fetch-error": "Erro ao buscar detalhes para {{entityType}} {{entityName}}",
"entity-feed-fetch-error": "Erro ao buscar contagem de feed de entidade!",
@ -2784,6 +2804,7 @@
"entity-limit-reached": "{{entity}} limite atingido",
"entity-removing-error": "Erro ao remover {{entity}}",
"entity-unfollow-error": "Erro ao deixar de seguir {{entity}}",
"entity-updated-success": "{{entity}} updated successfully!",
"entity-updating-error": "Erro ao atualizar {{entity}}",
"error-selected-node-name-details": "Erro ao obter detalhes de {{selectedNodeName}}",
"error-while-renewing-id-token-with-message": "Erro ao renovar o token de identificação do Auth0 SSO: {{message}}",
@ -2824,6 +2845,10 @@
"task-resolved-successfully": "Tarefa resolvida com sucesso",
"team-moved-error": "Erro ao mover a equipe",
"test-connection-error": "Erro ao testar a conexão!",
"test-definition-parameters-description": "Define parameters that users can configure when creating test cases from this definition. Parameters allow for flexible and reusable test definitions.",
"test-definition-sql-expression-placeholder": "SELECT * FROM {table} WHERE {column} < {{minValue}} OR {column} > {{maxValue}}",
"test-definition-sql-expression-tooltip": "SQL query template using parameter placeholders in double curly braces (e.g., {{paramName}}). Use {table} and {column} for runtime entity references.",
"test-definition-sql-query-help": "Write SQL query template with substitution variables. Use {table} for table name, {column} for column name (resolved at runtime). Use {{paramName}} for user parameters defined below (e.g., {{minValue}}, {{maxValue}}).",
"this-action-cannot-be-undone": "This action cannot be undone.",
"unauthorized-user": "Usuário não autorizado! Por favor, verifique o e-mail ou a senha",
"unexpected-error": "Ocorreu um erro inesperado.",

View file

@ -431,6 +431,7 @@
"data-profiler-metrics": "Métricas do Examinador de Dados",
"data-proportion-plural": "Proporções de Dados",
"data-quality": "Qualidade de Dados",
"data-quality-dimension": "Data Quality Dimension",
"data-quality-test": "Teste de Qualidade de Dados",
"data-quality-test-plural": "Testes de Qualidade de Dados",
"data-quartile-plural": "Quartis de Dados",
@ -613,6 +614,7 @@
"enter-custom-unit-of-measurement": "Digite a unidade de medida personalizada e prima Enter",
"enter-each-value-and-press-enter": "Introduza cada valor e prima Enter",
"enter-entity": "Inserir {{entity}}",
"enter-entity-description": "Enter {{entity}} description",
"enter-entity-name": "Inserir nome de {{entity}}",
"enter-entity-value": "Inserir Valor de {{entity}}",
"enter-field-description": "Inserir descrição de {{field}}",
@ -1253,6 +1255,9 @@
"page-not-found": "Página Não Encontrada",
"page-views-by-data-asset-plural": "Visualizações de Página por Ativos de Dados",
"parameter": "Parâmetro",
"parameter-description": "Parameter Description",
"parameter-display-name": "Parameter Display Name",
"parameter-name": "Parameter Name",
"parameter-plural": "Parâmetros",
"parent": "Pai",
"parsing-timeout-limit": "Limite de Tempo de Análise de Consulta",
@ -1436,6 +1441,7 @@
"request-method": "Request Method",
"request-schema-field": "Request Schema Field",
"request-tag-plural": "Solicitar Etiquetas",
"required": "Required",
"requirement-plural": "Requisitos",
"reset": "Redefinir",
"reset-default-layout": "Redefinir Layout Padrão",
@ -1495,6 +1501,7 @@
"rule-with-name": "- Rule: {{ruleName}}",
"rules-count": "({{count}} rules)",
"rules-evaluated": "Regras avaliadas",
"rules-library": "Rules Library",
"run": "Executar",
"run-agent-plural": "Executar Agentes",
"run-at": "Executar em",
@ -1645,7 +1652,9 @@
"specific-data-asset-plural": "Ativos de Dados Específicos",
"spreadsheet": "Folha de cálculo",
"spreadsheet-plural": "Folhas de cálculo",
"sql-expression": "SQL Expression",
"sql-function": "Função SQL",
"sql-query": "SQL Query",
"sql-uppercase": "SQL",
"sql-uppercase-query": "Consulta SQL",
"sso": "sso",
@ -1705,6 +1714,7 @@
"sunday": "Domingo",
"support": "Suporte",
"support-url": "URL de Suporte",
"supported-data-type-plural": "Supported Data Types",
"supported-language-plural": "Idiomas Suportados",
"switch-persona": "Trocar Persona",
"sync-alert-offset": "Sync Alert",
@ -1770,10 +1780,13 @@
"test-case-plural": "Casos de Teste",
"test-case-resolution-status": "Estado da Resolução do Caso de Teste",
"test-case-result": "Resultados do Caso de Teste",
"test-definition": "Test Definition",
"test-definition-plural": "Test Definitions",
"test-email": "E-mail de Teste",
"test-email-connection": "Test Email Connection",
"test-entity": "Teste {{entity}}",
"test-level-lowercase": "nível de teste",
"test-platform-plural": "Test Platforms",
"test-plural": "Testes",
"test-plural-type": "Testes de {{type}}",
"test-suite": "Conjunto de Testes",
@ -2534,6 +2547,7 @@
"page-sub-header-for-storages": "Ingestão de metadados dos serviços de armazenamento mais populares.",
"page-sub-header-for-table-profile": "Monitorize e compreenda a estrutura de suas tabelas com o examinador.",
"page-sub-header-for-teams": "Represente toda a estrutura organizacional com equipas hierárquicas.",
"page-sub-header-for-test-definitions": "Manage and configure test definitions for data quality rules.",
"page-sub-header-for-users": "Veja e gerencie os utilizadores regulares em sua organização. Para utilizadores administradores, visite a página de Administradores.",
"paid-addon-description": "<0>{{app}}</0> é um complemento pago para clientes Collate.",
"passed-x-checks": "{{count}} verificações aprovadas",
@ -2660,6 +2674,8 @@
"synonym-placeholder": "Para adicionar um sinónimo, basta digitá-lo e pressionar Enter",
"system-alert-edit-message": "Editing a system generated alert is not allowed.",
"system-tag-delete-disable-message": "Não é permitido excluir tags geradas pelo sistema. Você pode tentar desativar a tag em vez disso.",
"system-test-definition-delete-warning": "Deleting a system generated test definition is not allowed.",
"system-test-definition-edit-warning": "Editing a system generated test definition is not allowed.",
"tag-update-confirmation": "Would you like to proceed with updating the tags?",
"tailor-experience-for-persona": "Adapte a experiência especificamente para o persona {{persona}}, experimente!",
"take-quick-product-tour": "Faça um tour pelo produto para começar!",
@ -2675,6 +2691,8 @@
"default": "Por favor, assegure-se de que o {{service_type}} está configurado para permitir ligações",
"withIp": " a partir de {{ip}}"
},
"test-definition-parameters-description": "Defines configurable input parameters for test cases. Each parameter includes a name, data type (string, int, date, etc.), display name, and optional validation rules. Parameters can be referenced in SQL expressions using {{paramName}} syntax and allow users to customize test behavior when creating test cases.",
"test-definition-sql-query-help": "SQL expression template for custom SQL-based test definitions. Supports substitution variables: {table} and {column} for runtime entity references, and {{paramName}} for user-defined parameters. This field is only applicable for test definitions with testPlatforms set to 'OpenMetadata' and is used to execute custom SQL queries for data quality validation.",
"test-your-connection-before-creating-service": "Teste suas conexões antes de criar o serviço",
"testing-your-connection-may-take-two-minutes": "Testar suas conexões pode levar até 2 minutos",
"this-action-is-not-allowed-for-deleted-entities": "Esta ação não é permitida para entidades excluídas.",
@ -2774,7 +2792,9 @@
"entities-deleted-successfully": "Successfully deleted {{count}} {{entity}}s",
"entity-already-exist": "{{entity}} \"{{name}}\" já existe. {{entityPlural}} duplicados não são permitidos.",
"entity-already-exist-message-without-name": "Um {{entity}} com os detalhes fornecidos já existe. {{entityPlural}} duplicados não são permitidos.",
"entity-created-success": "{{entity}} created successfully!",
"entity-creation-error": "Erro ao criar {{entity}}",
"entity-deleted-success": "{{entity}} deleted successfully!",
"entity-deleted-successfully": "{{entity}} excluído com sucesso!",
"entity-details-fetch-error": "Erro ao buscar detalhes para {{entityType}} {{entityName}}",
"entity-feed-fetch-error": "Erro ao buscar contagem de feed de entidade!",
@ -2784,6 +2804,7 @@
"entity-limit-reached": "{{entity}} limit reached",
"entity-removing-error": "Erro ao remover {{entity}}",
"entity-unfollow-error": "Erro ao deixar de seguir {{entity}}",
"entity-updated-success": "{{entity}} updated successfully!",
"entity-updating-error": "Erro ao atualizar {{entity}}",
"error-selected-node-name-details": "Erro ao obter detalhes de {{selectedNodeName}}",
"error-while-renewing-id-token-with-message": "Erro ao renovar o token de identificação do Auth0 SSO: {{message}}",
@ -2824,6 +2845,10 @@
"task-resolved-successfully": "Tarefa resolvida com sucesso",
"team-moved-error": "Erro ao mover a equipa",
"test-connection-error": "Erro ao testar a conexão!",
"test-definition-parameters-description": "Define parameters that users can configure when creating test cases from this definition. Parameters allow for flexible and reusable test definitions.",
"test-definition-sql-expression-placeholder": "SELECT * FROM {table} WHERE {column} < {{minValue}} OR {column} > {{maxValue}}",
"test-definition-sql-expression-tooltip": "SQL query template using parameter placeholders in double curly braces (e.g., {{paramName}}). Use {table} and {column} for runtime entity references.",
"test-definition-sql-query-help": "Write SQL query template with substitution variables. Use {table} for table name, {column} for column name (resolved at runtime). Use {{paramName}} for user parameters defined below (e.g., {{minValue}}, {{maxValue}}).",
"this-action-cannot-be-undone": "This action cannot be undone.",
"unauthorized-user": "Utilizador não autorizado! Por favor, verifique o e-mail ou a senha",
"unexpected-error": "Ocorreu um erro inesperado.",

View file

@ -431,6 +431,7 @@
"data-profiler-metrics": "Метрики профилирования данных",
"data-proportion-plural": "Распределение данных",
"data-quality": "Качество данных",
"data-quality-dimension": "Data Quality Dimension",
"data-quality-test": "Тест качества данных",
"data-quality-test-plural": "Тесты качества данных",
"data-quartile-plural": "Качество данных",
@ -613,6 +614,7 @@
"enter-custom-unit-of-measurement": "Введите пользовательскую единицу измерения и нажмите Enter",
"enter-each-value-and-press-enter": "Введите каждое значение и нажмите Enter",
"enter-entity": "Введите объект «{{entity}}»",
"enter-entity-description": "Enter {{entity}} description",
"enter-entity-name": "Введите имя объекта «{{entity}}»",
"enter-entity-value": "Введите значение объекта «{{entity}}»",
"enter-field-description": "Введите описание {{field}}",
@ -1253,6 +1255,9 @@
"page-not-found": "Страница не найдена",
"page-views-by-data-asset-plural": "Просмотры объектов данных",
"parameter": "Параметр",
"parameter-description": "Parameter Description",
"parameter-display-name": "Parameter Display Name",
"parameter-name": "Parameter Name",
"parameter-plural": "Параметры",
"parent": "Родитель",
"parsing-timeout-limit": "Ограничение по времени парсинга запроса",
@ -1436,6 +1441,7 @@
"request-method": "Метод запроса",
"request-schema-field": "Поле схемы запроса",
"request-tag-plural": "Предложить тег",
"required": "Required",
"requirement-plural": "Требования",
"reset": "Сбросить",
"reset-default-layout": "Сбросить стандартный макет",
@ -1495,6 +1501,7 @@
"rule-with-name": "- Правило: {{ruleName}}",
"rules-count": "({{count}} правил)",
"rules-evaluated": "Правила оценены",
"rules-library": "Rules Library",
"run": "Запустить",
"run-agent-plural": "Запустить агенты",
"run-at": "Запустить в ",
@ -1645,7 +1652,9 @@
"specific-data-asset-plural": "Специфические объекты данных",
"spreadsheet": "Электронная таблица",
"spreadsheet-plural": "Электронные таблицы",
"sql-expression": "SQL Expression",
"sql-function": "SQL-функция",
"sql-query": "SQL Query",
"sql-uppercase": "SQL",
"sql-uppercase-query": "SQL-запрос",
"sso": "sso",
@ -1705,6 +1714,7 @@
"sunday": "Воскресенье",
"support": "Поддержка",
"support-url": "URL поддержки",
"supported-data-type-plural": "Supported Data Types",
"supported-language-plural": "Поддерживаемые языки",
"switch-persona": "Переключить Персону",
"sync-alert-offset": "Синхронизировать оповещение",
@ -1770,10 +1780,13 @@
"test-case-plural": "Проверки",
"test-case-resolution-status": "Статус решения тестового случая",
"test-case-result": "Результат проверки",
"test-definition": "Test Definition",
"test-definition-plural": "Test Definitions",
"test-email": "Протестировать электронную почту",
"test-email-connection": "Протестировать подключение электронной почты",
"test-entity": "Проверка объекта «{{entity}}»",
"test-level-lowercase": "уровень теста",
"test-platform-plural": "Test Platforms",
"test-plural": "Проверки",
"test-plural-type": "Тесты {{type}}",
"test-suite": "Тестирование",
@ -2534,6 +2547,7 @@
"page-sub-header-for-storages": "Получайте метаданные из самых популярных служб хранения.",
"page-sub-header-for-table-profile": "Отслеживайте и анализируйте структуру ваших таблиц с помощью профилировщика.",
"page-sub-header-for-teams": "Представьте организационную структуру в виде команд.",
"page-sub-header-for-test-definitions": "Manage and configure test definitions for data quality rules.",
"page-sub-header-for-users": "Управляйте пользователями.",
"paid-addon-description": "<0>{{app}}</0> платное дополнение для пользователей Collate",
"passed-x-checks": "Пройдено {{count}} проверок",
@ -2660,6 +2674,8 @@
"synonym-placeholder": "Введите синоним и нажмите Enter.",
"system-alert-edit-message": "Редактирование системного оповещения запрещено.",
"system-tag-delete-disable-message": "Нельзя удалить системный тег. Отключите его, если он не нужен",
"system-test-definition-delete-warning": "Deleting a system generated test definition is not allowed.",
"system-test-definition-edit-warning": "Editing a system generated test definition is not allowed.",
"tag-update-confirmation": "Продолжить добавление нового тега?",
"tailor-experience-for-persona": "Настройте опыт специально для персонажа {{persona}}, попробуйте!",
"take-quick-product-tour": "Ознакомьтесь с продуктом, чтобы начать работу!",
@ -2675,6 +2691,8 @@
"default": "Пожалуйста, убедитесь, что {{service_type}} настроен на разрешение подключений",
"withIp": " с {{ip}}"
},
"test-definition-parameters-description": "Defines configurable input parameters for test cases. Each parameter includes a name, data type (string, int, date, etc.), display name, and optional validation rules. Parameters can be referenced in SQL expressions using {{paramName}} syntax and allow users to customize test behavior when creating test cases.",
"test-definition-sql-query-help": "SQL expression template for custom SQL-based test definitions. Supports substitution variables: {table} and {column} for runtime entity references, and {{paramName}} for user-defined parameters. This field is only applicable for test definitions with testPlatforms set to 'OpenMetadata' and is used to execute custom SQL queries for data quality validation.",
"test-your-connection-before-creating-service": "Проверьте свои подключения перед созданием сервиса",
"testing-your-connection-may-take-two-minutes": "Проверка подключений может занять до 2 минут.",
"this-action-is-not-allowed-for-deleted-entities": "Это действие невозможно для удаленных объектов.",
@ -2774,7 +2792,9 @@
"entities-deleted-successfully": "Успешно удалено {{count}} объектов «{{entity}}»",
"entity-already-exist": "{{entity}} \"{{name}}\" уже существует.",
"entity-already-exist-message-without-name": "{{entity}} с указанными данными уже существует.",
"entity-created-success": "{{entity}} created successfully!",
"entity-creation-error": "Ошибка при создании объекта «{{entity}}»!",
"entity-deleted-success": "{{entity}} deleted successfully!",
"entity-deleted-successfully": "Объект «{{entity}}» удален!",
"entity-details-fetch-error": "Ошибка при получении сведений для объекта «{{entityType}}» {{entityName}}",
"entity-feed-fetch-error": "Ошибка получения событий ленты!",
@ -2784,6 +2804,7 @@
"entity-limit-reached": "Достигнут лимит объекта «{{entity}}»",
"entity-removing-error": "Ошибка при удалении объекта «{{entity}}».",
"entity-unfollow-error": "Ошибка при отмене подписки на объект «{{entity}}»",
"entity-updated-success": "{{entity}} updated successfully!",
"entity-updating-error": "Ошибка при обновлении объекта «{{entity}}»",
"error-selected-node-name-details": "Ошибка при получении сведений о {{selectedNodeName}}",
"error-while-renewing-id-token-with-message": "Ошибка при обновлении токена идентификатора из системы единого входа Auth0: {{message}}",
@ -2824,6 +2845,10 @@
"task-resolved-successfully": "Задача решена",
"team-moved-error": "Ошибка при перемещении команды",
"test-connection-error": "Ошибка при проверке соединения!",
"test-definition-parameters-description": "Define parameters that users can configure when creating test cases from this definition. Parameters allow for flexible and reusable test definitions.",
"test-definition-sql-expression-placeholder": "SELECT * FROM {table} WHERE {column} < {{minValue}} OR {column} > {{maxValue}}",
"test-definition-sql-expression-tooltip": "SQL query template using parameter placeholders in double curly braces (e.g., {{paramName}}). Use {table} and {column} for runtime entity references.",
"test-definition-sql-query-help": "Write SQL query template with substitution variables. Use {table} for table name, {column} for column name (resolved at runtime). Use {{paramName}} for user parameters defined below (e.g., {{minValue}}, {{maxValue}}).",
"this-action-cannot-be-undone": "Это действие нельзя отменить.",
"unauthorized-user": "Неавторизованный пользователь! пожалуйста, проверьте электронную почту или пароль",
"unexpected-error": "Произошла непредвиденная ошибка.",

View file

@ -431,6 +431,7 @@
"data-profiler-metrics": "เมตริกโปรไฟล์ข้อมูล",
"data-proportion-plural": "สัดส่วนข้อมูล",
"data-quality": "คุณภาพข้อมูล",
"data-quality-dimension": "Data Quality Dimension",
"data-quality-test": "การทดสอบคุณภาพข้อมูล",
"data-quality-test-plural": "การทดสอบคุณภาพของข้อมูล",
"data-quartile-plural": "ควอไทล์ข้อมูล",
@ -613,6 +614,7 @@
"enter-custom-unit-of-measurement": "กรอกหน่วยวัดที่กำหนดเองและกด Enter",
"enter-each-value-and-press-enter": "ป้อนค่าแต่ละค่าแล้วกด Enter",
"enter-entity": "ป้อน {{entity}}",
"enter-entity-description": "Enter {{entity}} description",
"enter-entity-name": "ป้อนชื่อ {{entity}}",
"enter-entity-value": "ป้อนค่าของ {{entity}}",
"enter-field-description": "ป้อนคำอธิบาย {{field}}",
@ -1253,6 +1255,9 @@
"page-not-found": "ไม่พบหน้า",
"page-views-by-data-asset-plural": "จำนวนการเข้าชมหน้าโดยสินทรัพย์ข้อมูล",
"parameter": "พารามิเตอร์",
"parameter-description": "Parameter Description",
"parameter-display-name": "Parameter Display Name",
"parameter-name": "Parameter Name",
"parameter-plural": "พารามิเตอร์หลายรายการ",
"parent": "ผู้ปกครอง",
"parsing-timeout-limit": "ขีดจำกัดเวลาในการวิเคราะห์คำถาม",
@ -1436,6 +1441,7 @@
"request-method": "วิธีการของคำขอ",
"request-schema-field": "ฟิลด์สคีมาของคำขอ",
"request-tag-plural": "แท็กคำขอ",
"required": "Required",
"requirement-plural": "ข้อกำหนด",
"reset": "รีเซ็ต",
"reset-default-layout": "รีเซ็ตเลย์เอาต์เริ่มต้น",
@ -1495,6 +1501,7 @@
"rule-with-name": "- Rule: {{ruleName}}",
"rules-count": "({{count}} rules)",
"rules-evaluated": "Rules Evaluated",
"rules-library": "Rules Library",
"run": "รัน",
"run-agent-plural": "รันเอเจนต์",
"run-at": "รันที่",
@ -1645,7 +1652,9 @@
"specific-data-asset-plural": "สินทรัพย์ข้อมูลเฉพาะ",
"spreadsheet": "สเปรดชีต",
"spreadsheet-plural": "สเปรดชีต",
"sql-expression": "SQL Expression",
"sql-function": "ฟังก์ชัน SQL",
"sql-query": "SQL Query",
"sql-uppercase": "SQL",
"sql-uppercase-query": "คำสั่ง SQL",
"sso": "sso",
@ -1705,6 +1714,7 @@
"sunday": "วันอาทิตย์",
"support": "สนับสนุน",
"support-url": "URL การสนับสนุน",
"supported-data-type-plural": "Supported Data Types",
"supported-language-plural": "ภาษาที่รองรับ",
"switch-persona": "สลับเปอร์โซนา",
"sync-alert-offset": "ซิงค์การแจ้งเตือน",
@ -1770,10 +1780,13 @@
"test-case-plural": "กรณีทดสอบหลายรายการ",
"test-case-resolution-status": "สถานะการแก้ไขกรณีทดสอบ",
"test-case-result": "ผลกรณีทดสอบ",
"test-definition": "Test Definition",
"test-definition-plural": "Test Definitions",
"test-email": "อีเมลทดสอบ",
"test-email-connection": "ทดสอบการเชื่อมต่ออีเมล",
"test-entity": "ทดสอบ {{entity}}",
"test-level-lowercase": "ระดับการทดสอบ",
"test-platform-plural": "Test Platforms",
"test-plural": "การทดสอบหลายรายการ",
"test-plural-type": "การทดสอบ {{type}}",
"test-suite": "ชุดทดสอบ",
@ -2534,6 +2547,7 @@
"page-sub-header-for-storages": "นำเข้าข้อมูลเมตาจากบริการจัดเก็บที่ได้รับความนิยมมากที่สุด",
"page-sub-header-for-table-profile": "ติดตามและเข้าใจโครงสร้างตารางของคุณด้วยโปรไฟล์เลอร์",
"page-sub-header-for-teams": "แสดงโครงสร้างองค์กรของคุณทั้งหมดด้วยทีมที่มีลำดับชั้น",
"page-sub-header-for-test-definitions": "Manage and configure test definitions for data quality rules.",
"page-sub-header-for-users": "จัดการผู้ใช้ทั่วไปในองค์กรของคุณ สำหรับผู้ดูแลระบบ โปรดไปที่หน้าผู้ดูแลระบบ",
"paid-addon-description": "<0>{{app}}</0> เป็นส่วนเสริมที่ต้องชำระเงินสำหรับลูกค้า Collate",
"passed-x-checks": "Passed {{count}} checks",
@ -2660,6 +2674,8 @@
"synonym-placeholder": "เพื่อเพิ่มคำพ้อง เพียงแค่พิมพ์และกด Enter",
"system-alert-edit-message": "ไม่อนุญาตให้แก้ไขการแจ้งเตือนที่สร้างโดยระบบ",
"system-tag-delete-disable-message": "ไม่อนุญาตให้ลบแท็กที่สร้างโดยระบบ คุณสามารถลองปิดใช้งานแทน",
"system-test-definition-delete-warning": "Deleting a system generated test definition is not allowed.",
"system-test-definition-edit-warning": "Editing a system generated test definition is not allowed.",
"tag-update-confirmation": "คุณต้องการดำเนินการอัปเดตแท็กหรือไม่?",
"tailor-experience-for-persona": "ปรับแต่งประสบการณ์เฉพาะสำหรับบุคลิกภาพ {{persona}} ลองดูสิ!",
"take-quick-product-tour": "เข้าร่วมทัวร์ผลิตภัณฑ์เพื่อเริ่มต้น!",
@ -2675,6 +2691,8 @@
"default": "โปรดตรวจสอบว่าได้กำหนดค่าของ {{service_type}} ให้อนุญาตการเชื่อมต่อแล้ว",
"withIp": " จาก {{ip}}"
},
"test-definition-parameters-description": "Defines configurable input parameters for test cases. Each parameter includes a name, data type (string, int, date, etc.), display name, and optional validation rules. Parameters can be referenced in SQL expressions using {{paramName}} syntax and allow users to customize test behavior when creating test cases.",
"test-definition-sql-query-help": "SQL expression template for custom SQL-based test definitions. Supports substitution variables: {table} and {column} for runtime entity references, and {{paramName}} for user-defined parameters. This field is only applicable for test definitions with testPlatforms set to 'OpenMetadata' and is used to execute custom SQL queries for data quality validation.",
"test-your-connection-before-creating-service": "ทดสอบการเชื่อมต่อของคุณก่อนที่จะสร้างบริการ",
"testing-your-connection-may-take-two-minutes": "การทดสอบการเชื่อมต่ออาจใช้เวลาถึง 2 นาที",
"this-action-is-not-allowed-for-deleted-entities": "การกระทำนี้ไม่อนุญาตสำหรับเอนทิตีที่ถูกลบ",
@ -2774,7 +2792,9 @@
"entities-deleted-successfully": "ลบ {{entity}} {{count}} รายการสำเร็จ",
"entity-already-exist": "{{entity}} \"{{name}}\" มีอยู่แล้ว การทำซ้ำ {{entityPlural}} ไม่ได้รับอนุญาต",
"entity-already-exist-message-without-name": "มี {{entity}} ที่มีรายละเอียดที่ระบุไว้แล้ว การทำซ้ำ {{entityPlural}} ไม่ได้รับอนุญาต",
"entity-created-success": "{{entity}} created successfully!",
"entity-creation-error": "เกิดข้อผิดพลาดขณะสร้าง {{entity}}",
"entity-deleted-success": "{{entity}} deleted successfully!",
"entity-deleted-successfully": "\"{{entity}}\" ถูกลบสำเร็จ!",
"entity-details-fetch-error": "เกิดข้อผิดพลาดขณะดึงรายละเอียดสำหรับ {{entityType}} {{entityName}}",
"entity-feed-fetch-error": "เกิดข้อผิดพลาดขณะดึงจำนวนฟีดของเอนทิตี!",
@ -2784,6 +2804,7 @@
"entity-limit-reached": "ถึงขีดจำกัดของ {{entity}} แล้ว",
"entity-removing-error": "เกิดข้อผิดพลาดขณะลบ {{entity}}",
"entity-unfollow-error": "เกิดข้อผิดพลาดขณะเลิกติดตาม {{entity}}",
"entity-updated-success": "{{entity}} updated successfully!",
"entity-updating-error": "เกิดข้อผิดพลาดขณะอัปเดต {{entity}}",
"error-selected-node-name-details": "เกิดข้อผิดพลาดขณะดึงรายละเอียด {{selectedNodeName}}",
"error-while-renewing-id-token-with-message": "เกิดข้อผิดพลาดขณะต่ออายุโทเค็นไอดีจาก Auth0 SSO: {{message}}",
@ -2824,6 +2845,10 @@
"task-resolved-successfully": "งานถูกแก้ไขสำเร็จแล้ว",
"team-moved-error": "เกิดข้อผิดพลาดขณะย้ายทีม",
"test-connection-error": "เกิดข้อผิดพลาดขณะทดสอบการเชื่อมต่อ!",
"test-definition-parameters-description": "Define parameters that users can configure when creating test cases from this definition. Parameters allow for flexible and reusable test definitions.",
"test-definition-sql-expression-placeholder": "SELECT * FROM {table} WHERE {column} < {{minValue}} OR {column} > {{maxValue}}",
"test-definition-sql-expression-tooltip": "SQL query template using parameter placeholders in double curly braces (e.g., {{paramName}}). Use {table} and {column} for runtime entity references.",
"test-definition-sql-query-help": "Write SQL query template with substitution variables. Use {table} for table name, {column} for column name (resolved at runtime). Use {{paramName}} for user parameters defined below (e.g., {{minValue}}, {{maxValue}}).",
"this-action-cannot-be-undone": "This action cannot be undone.",
"unauthorized-user": "ผู้ใช้ที่ไม่ได้รับอนุญาต! โปรดตรวจสอบอีเมลหรือรหัสผ่าน",
"unexpected-error": "เกิดข้อผิดพลาดที่ไม่คาดคิดขึ้น",

View file

@ -431,6 +431,7 @@
"data-profiler-metrics": "Veri Profilleyici Metrikleri",
"data-proportion-plural": "Veri Oranları",
"data-quality": "Veri Kalitesi",
"data-quality-dimension": "Data Quality Dimension",
"data-quality-test": "Veri Kalitesi Testi",
"data-quality-test-plural": "Veri Kalitesi Testleri",
"data-quartile-plural": "Veri Dörttebirleri",
@ -613,6 +614,7 @@
"enter-custom-unit-of-measurement": "Özel ölçü birimini girin ve Enter'a basın",
"enter-each-value-and-press-enter": "Her değeri girin ve Enter tuşuna basın",
"enter-entity": "{{entity}} Girin",
"enter-entity-description": "Enter {{entity}} description",
"enter-entity-name": "{{entity}} adı girin",
"enter-entity-value": "{{entity}} Değeri Girin",
"enter-field-description": "{{field}} açıklaması girin",
@ -1253,6 +1255,9 @@
"page-not-found": "Sayfa Bulunamadı",
"page-views-by-data-asset-plural": "Veri Varlıklarına Göre Sayfa Görüntülemeleri",
"parameter": "Parametre",
"parameter-description": "Parameter Description",
"parameter-display-name": "Parameter Display Name",
"parameter-name": "Parameter Name",
"parameter-plural": "Parametreler",
"parent": "Üst",
"parsing-timeout-limit": "Sorgu Ayrıştırma Zaman Aşımı Limiti",
@ -1436,6 +1441,7 @@
"request-method": "İstek Metodu",
"request-schema-field": "İstek Şema Alanı",
"request-tag-plural": "Etiket İste",
"required": "Required",
"requirement-plural": "Gereksinimler",
"reset": "Sıfırla",
"reset-default-layout": "Varsayılan Düzeni Sıfırla",
@ -1495,6 +1501,7 @@
"rule-with-name": "- Rule: {{ruleName}}",
"rules-count": "({{count}} rules)",
"rules-evaluated": "Rules Evaluated",
"rules-library": "Rules Library",
"run": "Çalıştır",
"run-agent-plural": "Agent'ları Çalıştır",
"run-at": "Şu zamanda çalıştır",
@ -1645,7 +1652,9 @@
"specific-data-asset-plural": "Belirli Veri Varlıkları",
"spreadsheet": "Hesap tablosu",
"spreadsheet-plural": "Hesap tabloları",
"sql-expression": "SQL Expression",
"sql-function": "SQL Fonksiyonu",
"sql-query": "SQL Query",
"sql-uppercase": "SQL",
"sql-uppercase-query": "SQL Sorgusu",
"sso": "sso",
@ -1705,6 +1714,7 @@
"sunday": "Pazar",
"support": "Destek",
"support-url": "Destek URL'si",
"supported-data-type-plural": "Supported Data Types",
"supported-language-plural": "Desteklenen Diller",
"switch-persona": "Persona Değiştir",
"sync-alert-offset": "Senkronizasyon Uyarısı Ofseti",
@ -1770,10 +1780,13 @@
"test-case-plural": "Test Senaryoları",
"test-case-resolution-status": "Test Durumu Çözüm Durumu",
"test-case-result": "Test Senaryosu Sonuçları",
"test-definition": "Test Definition",
"test-definition-plural": "Test Definitions",
"test-email": "Test E-postası",
"test-email-connection": "Test E-posta Bağlantısı",
"test-entity": "Test {{entity}}",
"test-level-lowercase": "test seviyesi",
"test-platform-plural": "Test Platforms",
"test-plural": "Testler",
"test-plural-type": "{{type}} Testleri",
"test-suite": "Test Paketi",
@ -2534,6 +2547,7 @@
"page-sub-header-for-storages": "En popüler depolama servislerinden metadata alın.",
"page-sub-header-for-table-profile": "Profilleyici ile tablo yapınızı izleyin ve anlayın.",
"page-sub-header-for-teams": "Hiyerarşik takımlarla tüm organizasyon yapınızı temsil edin.",
"page-sub-header-for-test-definitions": "Manage and configure test definitions for data quality rules.",
"page-sub-header-for-users": "Kuruluşunuzdaki normal kullanıcıları görüntüleyin ve yönetin. Yönetici kullanıcılar için lütfen Yönetici sayfasını ziyaret edin.",
"paid-addon-description": "<0>{{app}}</0>, Collate müşterileri için ücretli bir eklentidir.",
"passed-x-checks": "{{count}} kontrol geçildi",
@ -2660,6 +2674,8 @@
"synonym-placeholder": "Bir eş anlamlı eklemek için yazın ve Enter tuşuna basın",
"system-alert-edit-message": "Sistem tarafından oluşturulan bir uyarıyı düzenlemeye izin verilmez.",
"system-tag-delete-disable-message": "Sistem tarafından oluşturulan etiketleri silmeye izin verilmez. Bunun yerine etiketi devre dışı bırakmayı deneyebilirsiniz.",
"system-test-definition-delete-warning": "Deleting a system generated test definition is not allowed.",
"system-test-definition-edit-warning": "Editing a system generated test definition is not allowed.",
"tag-update-confirmation": "Etiketleri güncellemekle devam etmek istiyor musunuz?",
"tailor-experience-for-persona": "Deneyimi özellikle {{persona}} kişiliği için uyarlayın, deneyin!",
"take-quick-product-tour": "Başlamak için hızlı bir ürün turuna katılın!",
@ -2675,6 +2691,8 @@
"default": "Lütfen {{service_type}}'in bağlantılara izin verecek şekilde yapılandırıldığından emin olun",
"withIp": " {{ip}} adresinden"
},
"test-definition-parameters-description": "Defines configurable input parameters for test cases. Each parameter includes a name, data type (string, int, date, etc.), display name, and optional validation rules. Parameters can be referenced in SQL expressions using {{paramName}} syntax and allow users to customize test behavior when creating test cases.",
"test-definition-sql-query-help": "SQL expression template for custom SQL-based test definitions. Supports substitution variables: {table} and {column} for runtime entity references, and {{paramName}} for user-defined parameters. This field is only applicable for test definitions with testPlatforms set to 'OpenMetadata' and is used to execute custom SQL queries for data quality validation.",
"test-your-connection-before-creating-service": "Servisi oluşturmadan önce bağlantılarınızı test edin",
"testing-your-connection-may-take-two-minutes": "Bağlantılarınızı test etmek 2 dakika kadar sürebilir",
"this-action-is-not-allowed-for-deleted-entities": "Bu eylem silinmiş varlıklar için izin verilmez.",
@ -2774,7 +2792,9 @@
"entities-deleted-successfully": "{{count}} {{entity}} başarıyla silindi",
"entity-already-exist": "{{entity}} \"{{name}}\" zaten mevcut. Yinelenen {{entityPlural}} öğelerine izin verilmez.",
"entity-already-exist-message-without-name": "Verilen ayrıntılara sahip bir {{entity}} zaten mevcut. Yinelenen {{entityPlural}} öğelerine izin verilmez.",
"entity-created-success": "{{entity}} created successfully!",
"entity-creation-error": "{{entity}} oluşturulurken hata oluştu",
"entity-deleted-success": "{{entity}} deleted successfully!",
"entity-deleted-successfully": "\"{{entity}}\" başarıyla silindi!",
"entity-details-fetch-error": "{{entityType}} {{entityName}} için ayrıntılar alınırken hata oluştu",
"entity-feed-fetch-error": "Varlık akış sayısı alınırken hata oluştu!",
@ -2784,6 +2804,7 @@
"entity-limit-reached": "{{entity}} sınırına ulaşıldı",
"entity-removing-error": "{{entity}} kaldırılırken hata oluştu",
"entity-unfollow-error": "{{entity}} takibi bırakılırken hata oluştu",
"entity-updated-success": "{{entity}} updated successfully!",
"entity-updating-error": "{{entity}} güncellenirken hata oluştu",
"error-selected-node-name-details": "{{selectedNodeName}} ayrıntıları alınırken hata oluştu",
"error-while-renewing-id-token-with-message": "Auth0 SSO'dan kimlik anahtarı yenilenirken hata oluştu: {{message}}",
@ -2824,6 +2845,10 @@
"task-resolved-successfully": "Görev başarıyla çözüldü",
"team-moved-error": "Takım taşınırken hata oluştu",
"test-connection-error": "Bağlantı test edilirken hata oluştu!",
"test-definition-parameters-description": "Define parameters that users can configure when creating test cases from this definition. Parameters allow for flexible and reusable test definitions.",
"test-definition-sql-expression-placeholder": "SELECT * FROM {table} WHERE {column} < {{minValue}} OR {column} > {{maxValue}}",
"test-definition-sql-expression-tooltip": "SQL query template using parameter placeholders in double curly braces (e.g., {{paramName}}). Use {table} and {column} for runtime entity references.",
"test-definition-sql-query-help": "Write SQL query template with substitution variables. Use {table} for table name, {column} for column name (resolved at runtime). Use {{paramName}} for user parameters defined below (e.g., {{minValue}}, {{maxValue}}).",
"this-action-cannot-be-undone": "This action cannot be undone.",
"unauthorized-user": "Yetkisiz kullanıcı! lütfen e-postayı veya şifreyi kontrol edin",
"unexpected-error": "Beklenmedik bir hata oluştu.",

View file

@ -431,6 +431,7 @@
"data-profiler-metrics": "数据分析器指标",
"data-proportion-plural": "数据比例",
"data-quality": "数据质控",
"data-quality-dimension": "Data Quality Dimension",
"data-quality-test": "数据质控测试",
"data-quality-test-plural": "总测试数",
"data-quartile-plural": "数据四分位数",
@ -613,6 +614,7 @@
"enter-custom-unit-of-measurement": "输入自定义计量单位并按Enter键",
"enter-each-value-and-press-enter": "输入每个值并按Enter键",
"enter-entity": "输入{{entity}}",
"enter-entity-description": "Enter {{entity}} description",
"enter-entity-name": "输入{{entity}}的名称",
"enter-entity-value": "输入{{entity}}的值",
"enter-field-description": "输入{{field}}的描述",
@ -1253,6 +1255,9 @@
"page-not-found": "没有找到页面",
"page-views-by-data-asset-plural": "数据资产页面浏览量",
"parameter": "参数",
"parameter-description": "Parameter Description",
"parameter-display-name": "Parameter Display Name",
"parameter-name": "Parameter Name",
"parameter-plural": "参数",
"parent": "父级",
"parsing-timeout-limit": "查询语句解析超时限制",
@ -1436,6 +1441,7 @@
"request-method": "Request Method",
"request-schema-field": "Request Schema Field",
"request-tag-plural": "请求补充标签",
"required": "Required",
"requirement-plural": "需求",
"reset": "重置",
"reset-default-layout": "重置默认布局",
@ -1495,6 +1501,7 @@
"rule-with-name": "- Rule: {{ruleName}}",
"rules-count": "({{count}} rules)",
"rules-evaluated": "已评估规则",
"rules-library": "Rules Library",
"run": "运行",
"run-agent-plural": "运行代理",
"run-at": "运行于",
@ -1645,7 +1652,9 @@
"specific-data-asset-plural": "特定数据资产",
"spreadsheet": "电子表格",
"spreadsheet-plural": "电子表格",
"sql-expression": "SQL Expression",
"sql-function": "SQL函数",
"sql-query": "SQL Query",
"sql-uppercase": "SQL",
"sql-uppercase-query": "SQL查询",
"sso": "sso",
@ -1705,6 +1714,7 @@
"sunday": "星期天",
"support": "支持",
"support-url": "支持 URL",
"supported-data-type-plural": "Supported Data Types",
"supported-language-plural": "支持的语言",
"switch-persona": "切换角色",
"sync-alert-offset": "同步警报",
@ -1770,10 +1780,13 @@
"test-case-plural": "测试用例",
"test-case-resolution-status": "测试用例解决状态",
"test-case-result": "测试用例结果",
"test-definition": "Test Definition",
"test-definition-plural": "Test Definitions",
"test-email": "测试邮箱",
"test-email-connection": "测试邮箱连接",
"test-entity": "测试{{entity}}",
"test-level-lowercase": "测试级别",
"test-platform-plural": "Test Platforms",
"test-plural": "测试",
"test-plural-type": "{{type}}测试",
"test-suite": "质控测试集",
@ -2534,6 +2547,7 @@
"page-sub-header-for-storages": "从最流行的存储类型服务中提取元数据",
"page-sub-header-for-table-profile": "通过数据分析工具了解和跟踪您的数据表结构",
"page-sub-header-for-teams": "将组织机构的架构通过团队进行分层分级",
"page-sub-header-for-test-definitions": "Manage and configure test definitions for data quality rules.",
"page-sub-header-for-users": "管理您组织中的普通用户。如需管理管理员,请访问管理员页面。",
"paid-addon-description": "<0>{{app}}</0>是Collate客户的付费附加组件。",
"passed-x-checks": "通过 {{count}} 项检查",
@ -2660,6 +2674,8 @@
"synonym-placeholder": "输入并按回车键以新增一个同义词",
"system-alert-edit-message": "不允许编辑系统生成的告警。",
"system-tag-delete-disable-message": "无法删除系统默认的标签, 您可以尝试禁用标签",
"system-test-definition-delete-warning": "Deleting a system generated test definition is not allowed.",
"system-test-definition-edit-warning": "Editing a system generated test definition is not allowed.",
"tag-update-confirmation": "您要继续更新标签吗?",
"tailor-experience-for-persona": "为{{persona}}角色量身定制体验,试试看!",
"take-quick-product-tour": "快速查看产品导览",
@ -2675,6 +2691,8 @@
"default": "请确保 {{service_type}} 已配置为允许连接",
"withIp": " 来自 {{ip}}"
},
"test-definition-parameters-description": "Defines configurable input parameters for test cases. Each parameter includes a name, data type (string, int, date, etc.), display name, and optional validation rules. Parameters can be referenced in SQL expressions using {{paramName}} syntax and allow users to customize test behavior when creating test cases.",
"test-definition-sql-query-help": "SQL expression template for custom SQL-based test definitions. Supports substitution variables: {table} and {column} for runtime entity references, and {{paramName}} for user-defined parameters. This field is only applicable for test definitions with testPlatforms set to 'OpenMetadata' and is used to execute custom SQL queries for data quality validation.",
"test-your-connection-before-creating-service": "在创建服务之前测试您的连接",
"testing-your-connection-may-take-two-minutes": "连接测试可能最多需要 2 分钟",
"this-action-is-not-allowed-for-deleted-entities": "已删除的实体不允许执行此操作",
@ -2774,7 +2792,9 @@
"entities-deleted-successfully": "成功删除{{count}}个{{entity}}",
"entity-already-exist": "与该名称\"{{name}}\"同名的{{entity}}已经存在, 不允许重名",
"entity-already-exist-message-without-name": "已存在相同细节的{{entity}}, 不允许重复",
"entity-created-success": "{{entity}} created successfully!",
"entity-creation-error": "创建{{entity}}时发生错误",
"entity-deleted-success": "{{entity}} deleted successfully!",
"entity-deleted-successfully": "{{entity}}删除成功!",
"entity-details-fetch-error": "获取{{entityType}}{{entityName}}的详细信息时发生错误!",
"entity-feed-fetch-error": "在获取信息流计数信息时发生错误!",
@ -2784,6 +2804,7 @@
"entity-limit-reached": "{{entity}} limit reached",
"entity-removing-error": "删除{{entity}}时发生错误",
"entity-unfollow-error": "取消关注{{entity}}时发生错误",
"entity-updated-success": "{{entity}} updated successfully!",
"entity-updating-error": "更新{{entity}}时发生错误",
"error-selected-node-name-details": "获取{{selectedNodeName}}详细信息时发生错误",
"error-while-renewing-id-token-with-message": "从 Auth0 SSO 续订 ID 令牌时发生错误:{{message}}",
@ -2824,6 +2845,10 @@
"task-resolved-successfully": "任务已成功解决",
"team-moved-error": "移动团队时发生错误",
"test-connection-error": "测试连接时发生错误!",
"test-definition-parameters-description": "Define parameters that users can configure when creating test cases from this definition. Parameters allow for flexible and reusable test definitions.",
"test-definition-sql-expression-placeholder": "SELECT * FROM {table} WHERE {column} < {{minValue}} OR {column} > {{maxValue}}",
"test-definition-sql-expression-tooltip": "SQL query template using parameter placeholders in double curly braces (e.g., {{paramName}}). Use {table} and {column} for runtime entity references.",
"test-definition-sql-query-help": "Write SQL query template with substitution variables. Use {table} for table name, {column} for column name (resolved at runtime). Use {{paramName}} for user parameters defined below (e.g., {{minValue}}, {{maxValue}}).",
"this-action-cannot-be-undone": "This action cannot be undone.",
"unauthorized-user": "未经授权的用户! 请检查电子邮箱或密码",
"unexpected-error": "发生意外错误",

View file

@ -431,6 +431,7 @@
"data-profiler-metrics": "資料分析器指標",
"data-proportion-plural": "資料比例",
"data-quality": "資料品質",
"data-quality-dimension": "Data Quality Dimension",
"data-quality-test": "資料品質測試",
"data-quality-test-plural": "資料品質測試",
"data-quartile-plural": "資料四分位數",
@ -613,6 +614,7 @@
"enter-custom-unit-of-measurement": "輸入自訂計量單位並按Enter鍵",
"enter-each-value-and-press-enter": "輸入每個值並按Enter鍵",
"enter-entity": "輸入 {{entity}}",
"enter-entity-description": "Enter {{entity}} description",
"enter-entity-name": "輸入 {{entity}} 名稱",
"enter-entity-value": "輸入 {{entity}} 值",
"enter-field-description": "輸入 {{field}} 描述",
@ -1253,6 +1255,9 @@
"page-not-found": "找不到頁面",
"page-views-by-data-asset-plural": "依資料資產的頁面瀏覽量",
"parameter": "參數",
"parameter-description": "Parameter Description",
"parameter-display-name": "Parameter Display Name",
"parameter-name": "Parameter Name",
"parameter-plural": "參數",
"parent": "父項",
"parsing-timeout-limit": "查詢剖析逾時限制",
@ -1436,6 +1441,7 @@
"request-method": "請求方法",
"request-schema-field": "請求結構欄位",
"request-tag-plural": "請求標籤",
"required": "Required",
"requirement-plural": "需求",
"reset": "重設",
"reset-default-layout": "重設預設版面配置",
@ -1495,6 +1501,7 @@
"rule-with-name": "- 規則:{{ruleName}}",
"rules-count": "({{count}} 個規則)",
"rules-evaluated": "已評估的規則",
"rules-library": "Rules Library",
"run": "執行",
"run-agent-plural": "執行代理程式",
"run-at": "執行於",
@ -1645,7 +1652,9 @@
"specific-data-asset-plural": "特定資料資產",
"spreadsheet": "試算表",
"spreadsheet-plural": "試算表",
"sql-expression": "SQL Expression",
"sql-function": "SQL 函數",
"sql-query": "SQL Query",
"sql-uppercase": "SQL",
"sql-uppercase-query": "SQL 查詢",
"sso": "sso",
@ -1705,6 +1714,7 @@
"sunday": "星期日",
"support": "支援",
"support-url": "支援 URL",
"supported-data-type-plural": "Supported Data Types",
"supported-language-plural": "支援的語言",
"switch-persona": "切換角色",
"sync-alert-offset": "同步警示",
@ -1770,10 +1780,13 @@
"test-case-plural": "測試案例",
"test-case-resolution-status": "測試案例解決狀態",
"test-case-result": "測試案例結果",
"test-definition": "Test Definition",
"test-definition-plural": "Test Definitions",
"test-email": "測試電子郵件",
"test-email-connection": "測試電子郵件連線",
"test-entity": "測試 {{entity}}",
"test-level-lowercase": "測試層級",
"test-platform-plural": "Test Platforms",
"test-plural": "測試",
"test-plural-type": "{{type}}測試",
"test-suite": "測試套件",
@ -2534,6 +2547,7 @@
"page-sub-header-for-storages": "從最受歡迎的儲存服務擷取元資料。",
"page-sub-header-for-table-profile": "使用分析器監控和了解您的資料表結構。",
"page-sub-header-for-teams": "用階層式團隊代表您的整個組織結構。",
"page-sub-header-for-test-definitions": "Manage and configure test definitions for data quality rules.",
"page-sub-header-for-users": "檢視和管理您組織中的一般使用者。對於管理員使用者,請造訪管理員頁面。",
"paid-addon-description": "<0>{{app}}</0> 是 Collate 客戶的付費附加元件。",
"passed-x-checks": "通過 {{count}} 個檢查",
@ -2660,6 +2674,8 @@
"synonym-placeholder": "若要新增同義詞,只需輸入並按 Enter",
"system-alert-edit-message": "不允許編輯系統產生的警示。",
"system-tag-delete-disable-message": "不允許刪除系統產生的標籤。您可以嘗試停用該標籤。",
"system-test-definition-delete-warning": "Deleting a system generated test definition is not allowed.",
"system-test-definition-edit-warning": "Editing a system generated test definition is not allowed.",
"tag-update-confirmation": "您想繼續更新標籤嗎?",
"tailor-experience-for-persona": "專為 {{persona}} 角色量身打造體驗,試試看吧!",
"take-quick-product-tour": "進行產品導覽以開始!",
@ -2675,6 +2691,8 @@
"default": "請確保 {{service_type}} 已設定為允許連線",
"withIp": "從 {{ip}}"
},
"test-definition-parameters-description": "Defines configurable input parameters for test cases. Each parameter includes a name, data type (string, int, date, etc.), display name, and optional validation rules. Parameters can be referenced in SQL expressions using {{paramName}} syntax and allow users to customize test behavior when creating test cases.",
"test-definition-sql-query-help": "SQL expression template for custom SQL-based test definitions. Supports substitution variables: {table} and {column} for runtime entity references, and {{paramName}} for user-defined parameters. This field is only applicable for test definitions with testPlatforms set to 'OpenMetadata' and is used to execute custom SQL queries for data quality validation.",
"test-your-connection-before-creating-service": "在建立服務之前測試您的連線",
"testing-your-connection-may-take-two-minutes": "測試您的連線最多可能需要 2 分鐘",
"this-action-is-not-allowed-for-deleted-entities": "此操作不允許用於已刪除的實體。",
@ -2774,7 +2792,9 @@
"entities-deleted-successfully": "成功刪除{{count}}個{{entity}}",
"entity-already-exist": "{{entity}} \"{{name}}\" 已存在。不允許重複的 {{entityPlural}}。",
"entity-already-exist-message-without-name": "具有相同詳細資料的 {{entity}} 已存在。不允許重複的 {{entityPlural}}。",
"entity-created-success": "{{entity}} created successfully!",
"entity-creation-error": "建立 {{entity}} 時發生錯誤",
"entity-deleted-success": "{{entity}} deleted successfully!",
"entity-deleted-successfully": "\"{{entity}}\" 刪除成功!",
"entity-details-fetch-error": "擷取 {{entityType}} {{entityName}} 詳細資料時發生錯誤",
"entity-feed-fetch-error": "擷取實體摘要計數時發生錯誤!",
@ -2784,6 +2804,7 @@
"entity-limit-reached": "{{entity}} 已達上限",
"entity-removing-error": "移除 {{entity}} 時發生錯誤",
"entity-unfollow-error": "取消追蹤 {{entity}} 時發生錯誤",
"entity-updated-success": "{{entity}} updated successfully!",
"entity-updating-error": "更新 {{entity}} 時發生錯誤",
"error-selected-node-name-details": "取得 {{selectedNodeName}} 詳細資料時發生錯誤",
"error-while-renewing-id-token-with-message": "從 Auth0 SSO 更新 ID 權杖時發生錯誤:{{message}}",
@ -2824,6 +2845,10 @@
"task-resolved-successfully": "任務解決成功",
"team-moved-error": "移動團隊時發生錯誤",
"test-connection-error": "測試連線時發生錯誤!",
"test-definition-parameters-description": "Define parameters that users can configure when creating test cases from this definition. Parameters allow for flexible and reusable test definitions.",
"test-definition-sql-expression-placeholder": "SELECT * FROM {table} WHERE {column} < {{minValue}} OR {column} > {{maxValue}}",
"test-definition-sql-expression-tooltip": "SQL query template using parameter placeholders in double curly braces (e.g., {{paramName}}). Use {table} and {column} for runtime entity references.",
"test-definition-sql-query-help": "Write SQL query template with substitution variables. Use {table} for table name, {column} for column name (resolved at runtime). Use {{paramName}} for user parameters defined below (e.g., {{minValue}}, {{maxValue}}).",
"this-action-cannot-be-undone": "This action cannot be undone.",
"unauthorized-user": "未經授權的使用者!請檢查電子郵件或密碼",
"unexpected-error": "發生未預期的錯誤。",

View file

@ -0,0 +1,66 @@
/*
* Copyright 2024 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 { render, screen } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';
import RulesLibraryPage from './RulesLibraryPage';
jest.mock(
'../../components/RulesLibrary/TestDefinitionList/TestDefinitionList.component',
() => ({
__esModule: true,
default: jest
.fn()
.mockImplementation(() => (
<div data-testid="test-definition-list">TestDefinitionList</div>
)),
})
);
jest.mock('../../components/PageLayoutV1/PageLayoutV1', () => ({
__esModule: true,
default: jest.fn().mockImplementation(({ children, pageTitle }) => (
<div data-testid="page-layout">
<h1>{pageTitle}</h1>
{children}
</div>
)),
}));
describe('RulesLibraryPage Component', () => {
it('should render page layout with correct title', () => {
render(<RulesLibraryPage />, { wrapper: MemoryRouter });
expect(screen.getByTestId('page-layout')).toBeInTheDocument();
expect(screen.getByText('label.rules-library')).toBeInTheDocument();
});
it('should render TestDefinitionList component', () => {
render(<RulesLibraryPage />, { wrapper: MemoryRouter });
expect(screen.getByTestId('test-definition-list')).toBeInTheDocument();
});
it('should render with correct layout structure', () => {
const { container } = render(<RulesLibraryPage />, {
wrapper: MemoryRouter,
});
const row = container.querySelector('.ant-row');
expect(row).toBeInTheDocument();
const col = container.querySelector('.ant-col-24');
expect(col).toBeInTheDocument();
});
});

View file

@ -0,0 +1,33 @@
/*
* Copyright 2024 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 { Col, Row } from 'antd';
import { useTranslation } from 'react-i18next';
import PageLayoutV1 from '../../components/PageLayoutV1/PageLayoutV1';
import TestDefinitionList from '../../components/RulesLibrary/TestDefinitionList/TestDefinitionList.component';
const RulesLibraryPage = () => {
const { t } = useTranslation();
return (
<PageLayoutV1 pageTitle={t('label.rules-library')}>
<Row className="p-t-md" gutter={[16, 16]}>
<Col span={24}>
<TestDefinitionList />
</Col>
</Row>
</PageLayoutV1>
);
};
export default RulesLibraryPage;

View file

@ -17,6 +17,7 @@ import { PagingResponse } from 'Models';
import { SORT_ORDER } from '../enums/common.enum';
import { TestCaseType, TestSuiteType } from '../enums/TestSuite.enum';
import { CreateTestCase } from '../generated/api/tests/createTestCase';
import { CreateTestDefinition } from '../generated/api/tests/createTestDefinition';
import { CreateTestSuite } from '../generated/api/tests/createTestSuite';
import { DataQualityReport } from '../generated/tests/dataQualityReport';
import {
@ -78,8 +79,9 @@ export type ListTestCaseParamsBySearch = ListTestCaseParams & {
export type ListTestDefinitionsParams = ListParams & {
entityType?: EntityType;
testPlatform: TestPlatform;
testPlatform?: TestPlatform;
supportedDataType?: string;
enabled?: boolean;
};
export type ListTestCaseResultsParams = Omit<
@ -279,6 +281,49 @@ export const getTestDefinitionById = async (
return response.data;
};
export const createTestDefinition = async (data: CreateTestDefinition) => {
const response = await APIClient.post<TestDefinition>(
testDefinitionUrl,
data
);
return response.data;
};
export const updateTestDefinition = async (data: TestDefinition) => {
const response = await APIClient.put<TestDefinition>(testDefinitionUrl, data);
return response.data;
};
export const patchTestDefinition = async (id: string, patch: Operation[]) => {
const response = await APIClient.patch<TestDefinition>(
`${testDefinitionUrl}/${id}`,
patch
);
return response.data;
};
export const deleteTestDefinitionByFqn = async (
fqn: string,
paramsValue?: { hardDelete?: boolean; recursive?: boolean }
) => {
const params = {
hardDelete: true,
recursive: true,
...paramsValue,
};
const response = await APIClient.delete<TestDefinition>(
`${testDefinitionUrl}/name/${fqn}`,
{
params,
}
);
return response.data;
};
// testSuite Section
export const getListTestSuites = async (params?: ListTestSuitePrams) => {
const response = await APIClient.get<{