Fix #1876: Allow dots in entity names (#2700)

Add getter and setter for `name` into EntityInterface

Refactor code in utility method.

Rename the escape method.

Fix webhook.

Fix pipeline tests

Fix airflowPipeline tests

Fix role, team, and user tests

Replace . with _DOT_ to keep compatibility with the past and avoid a migration.

Remove the Python code that was replacing the . with _DOT_.

Restore "no dots" for services and implement the JSON Schema rule.

** Entites escaping dots
1. AirflowPipeline
2. Chart
3. Dashboard
4. Database
5. Location
6. Metrics
7. MlModel
8. Pipeline
9. Table
10. Topic

** Entities rejecting dots
1. Database Service
2. Dashboard Service
3. Messaging Service
4. Pipeline Service
5. Storage Service

** Entities accepting dots
1. Bots
2. Policy
3. Report
4. Role
5. Team
6. User
7. Webhook

** Fields escaping dots
1. Column
2. Task

Add escape for Column and TableConstraints

Add escape to Task

Remove the Python code. It will be added in a follow-up PR.

Fix Pipeline entity tests
This commit is contained in:
Alberto Miorin 2022-02-17 08:21:53 +01:00 committed by GitHub
parent 419448a28f
commit b3b5f44c23
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
64 changed files with 474 additions and 53 deletions

View File

@ -84,6 +84,7 @@ public class AirflowPipelineRepository extends EntityRepository<AirflowPipeline>
@Override
public void prepare(AirflowPipeline airflowPipeline) throws IOException, ParseException {
EntityUtil.escapeReservedChars(getEntityInterface(airflowPipeline));
EntityReference entityReference =
helper(helper(airflowPipeline).findEntity("service", List.of(DATABASE_SERVICE, DASHBOARD_SERVICE)))
.toEntityReference();
@ -147,6 +148,11 @@ public class AirflowPipelineRepository extends EntityRepository<AirflowPipeline>
return entity.getDisplayName();
}
@Override
public String getName() {
return entity.getName();
}
@Override
public Boolean isDeleted() {
return entity.getDeleted();
@ -224,6 +230,11 @@ public class AirflowPipelineRepository extends EntityRepository<AirflowPipeline>
entity.setDisplayName(displayName);
}
@Override
public void setName(String name) {
entity.setName(name);
}
@Override
public void setUpdateDetails(String updatedBy, long updatedAt) {
entity.setUpdatedBy(updatedBy);

View File

@ -55,9 +55,7 @@ public class BotsRepository extends EntityRepository<Bots> {
}
@Override
public void prepare(Bots entity) {
/* Nothing to do */
}
public void prepare(Bots entity) {}
@Override
public void storeEntity(Bots entity, boolean update) throws IOException {
@ -91,6 +89,11 @@ public class BotsRepository extends EntityRepository<Bots> {
return entity.getDisplayName();
}
@Override
public String getName() {
return entity.getName();
}
@Override
public Boolean isDeleted() {
return entity.getDeleted();
@ -161,6 +164,11 @@ public class BotsRepository extends EntityRepository<Bots> {
entity.setDisplayName(displayName);
}
@Override
public void setName(String name) {
entity.setName(name);
}
@Override
public void setUpdateDetails(String updatedBy, long updatedAt) {
entity.setUpdatedBy(updatedBy);

View File

@ -60,6 +60,7 @@ public class ChartRepository extends EntityRepository<Chart> {
@Override
public void prepare(Chart chart) throws IOException, ParseException {
EntityUtil.escapeReservedChars(getEntityInterface(chart));
DashboardService dashboardService = helper(chart).findEntity("service", DASHBOARD_SERVICE);
chart.setService(helper(dashboardService).toEntityReference());
chart.setServiceType(dashboardService.getServiceType());
@ -142,6 +143,11 @@ public class ChartRepository extends EntityRepository<Chart> {
return entity.getDisplayName();
}
@Override
public String getName() {
return entity.getName();
}
@Override
public Boolean isDeleted() {
return entity.getDeleted();
@ -222,6 +228,11 @@ public class ChartRepository extends EntityRepository<Chart> {
entity.setDisplayName(displayName);
}
@Override
public void setName(String name) {
entity.setName(name);
}
@Override
public void setUpdateDetails(String updatedBy, long updatedAt) {
entity.setUpdatedBy(updatedBy);

View File

@ -128,6 +128,7 @@ public class DashboardRepository extends EntityRepository<Dashboard> {
@Override
public void prepare(Dashboard dashboard) throws IOException {
EntityUtil.escapeReservedChars(getEntityInterface(dashboard));
populateService(dashboard);
dashboard.setFullyQualifiedName(getFQN(dashboard));
EntityUtil.populateOwner(daoCollection.userDAO(), daoCollection.teamDAO(), dashboard.getOwner()); // Validate owner
@ -243,6 +244,11 @@ public class DashboardRepository extends EntityRepository<Dashboard> {
return entity.getDisplayName();
}
@Override
public String getName() {
return entity.getName();
}
@Override
public Boolean isDeleted() {
return entity.getDeleted();
@ -326,6 +332,11 @@ public class DashboardRepository extends EntityRepository<Dashboard> {
entity.setDisplayName(displayName);
}
@Override
public void setName(String name) {
entity.setName(name);
}
@Override
public void setUpdateDetails(String updatedBy, long updatedAt) {
entity.setUpdatedBy(updatedBy);

View File

@ -115,6 +115,11 @@ public class DashboardServiceRepository extends EntityRepository<DashboardServic
return entity.getDisplayName();
}
@Override
public String getName() {
return entity.getName();
}
@Override
public Boolean isDeleted() {
return entity.getDeleted();
@ -185,6 +190,11 @@ public class DashboardServiceRepository extends EntityRepository<DashboardServic
entity.setDisplayName(displayName);
}
@Override
public void setName(String name) {
entity.setName(name);
}
@Override
public void setUpdateDetails(String updatedBy, long updatedAt) {
entity.setUpdatedBy(updatedBy);

View File

@ -73,6 +73,7 @@ public class DatabaseRepository extends EntityRepository<Database> {
@Override
public void prepare(Database database) throws IOException {
EntityUtil.escapeReservedChars(getEntityInterface(database));
populateService(database);
database.setFullyQualifiedName(getFQN(database));
database.setOwner(
@ -210,6 +211,11 @@ public class DatabaseRepository extends EntityRepository<Database> {
return entity.getDisplayName();
}
@Override
public String getName() {
return entity.getName();
}
@Override
public Boolean isDeleted() {
return entity.getDeleted();
@ -282,6 +288,11 @@ public class DatabaseRepository extends EntityRepository<Database> {
entity.setDisplayName(displayName);
}
@Override
public void setName(String name) {
entity.setName(name);
}
@Override
public void setUpdateDetails(String updatedBy, long updatedAt) {
entity.setUpdatedBy(updatedBy);

View File

@ -174,6 +174,11 @@ public class DatabaseServiceRepository extends EntityRepository<DatabaseService>
return entity.getDisplayName();
}
@Override
public String getName() {
return entity.getName();
}
@Override
public EntityReference getOwner() {
return entity.getOwner();
@ -244,6 +249,11 @@ public class DatabaseServiceRepository extends EntityRepository<DatabaseService>
entity.setDisplayName(displayName);
}
@Override
public void setName(String name) {
entity.setName(name);
}
@Override
public void setUpdateDetails(String updatedBy, long updatedAt) {
entity.setUpdatedBy(updatedBy);

View File

@ -136,6 +136,11 @@ public class GlossaryRepository extends EntityRepository<Glossary> {
return entity.getDisplayName();
}
@Override
public String getName() {
return entity.getName();
}
@Override
public Boolean isDeleted() {
return entity.getDeleted();
@ -216,6 +221,11 @@ public class GlossaryRepository extends EntityRepository<Glossary> {
entity.setDisplayName(displayName);
}
@Override
public void setName(String name) {
entity.setName(name);
}
@Override
public void setUpdateDetails(String updatedBy, long updatedAt) {
entity.setUpdatedBy(updatedBy);

View File

@ -252,6 +252,11 @@ public class GlossaryTermRepository extends EntityRepository<GlossaryTerm> {
return entity.getDisplayName();
}
@Override
public String getName() {
return entity.getName();
}
@Override
public Boolean isDeleted() {
return entity.getDeleted();
@ -332,6 +337,11 @@ public class GlossaryTermRepository extends EntityRepository<GlossaryTerm> {
entity.setDisplayName(displayName);
}
@Override
public void setName(String name) {
entity.setName(name);
}
@Override
public void setUpdateDetails(String updatedBy, long updatedAt) {
entity.setUpdatedBy(updatedBy);

View File

@ -177,6 +177,7 @@ public class LocationRepository extends EntityRepository<Location> {
@Override
public void prepare(Location location) throws IOException {
EntityUtil.escapeReservedChars(getEntityInterface(location));
StorageService storageService = getService(location.getService().getId(), location.getService().getType());
location.setService(
new StorageServiceRepository.StorageServiceEntityInterface(storageService).getEntityReference());
@ -260,6 +261,11 @@ public class LocationRepository extends EntityRepository<Location> {
return entity.getDisplayName();
}
@Override
public String getName() {
return entity.getName();
}
@Override
public Boolean isDeleted() {
return entity.getDeleted();
@ -347,6 +353,11 @@ public class LocationRepository extends EntityRepository<Location> {
entity.setDisplayName(displayName);
}
@Override
public void setName(String name) {
entity.setName(name);
}
@Override
public void setUpdateDetails(String updatedBy, long updatedAt) {
entity.setUpdatedBy(updatedBy);

View File

@ -119,6 +119,11 @@ public class MessagingServiceRepository extends EntityRepository<MessagingServic
return entity.getDisplayName();
}
@Override
public String getName() {
return entity.getName();
}
@Override
public Boolean isDeleted() {
return entity.getDeleted();
@ -189,6 +194,11 @@ public class MessagingServiceRepository extends EntityRepository<MessagingServic
entity.setDisplayName(displayName);
}
@Override
public void setName(String name) {
entity.setName(name);
}
@Override
public void setUpdateDetails(String updatedBy, long updatedAt) {
entity.setUpdatedBy(updatedBy);

View File

@ -75,6 +75,7 @@ public class MetricsRepository extends EntityRepository<Metrics> {
@Override
public void prepare(Metrics metrics) throws IOException {
EntityUtil.escapeReservedChars(getEntityInterface(metrics));
metrics.setFullyQualifiedName(getFQN(metrics));
EntityUtil.populateOwner(daoCollection.userDAO(), daoCollection.teamDAO(), metrics.getOwner()); // Validate owner
metrics.setService(getService(metrics.getService()));
@ -139,6 +140,11 @@ public class MetricsRepository extends EntityRepository<Metrics> {
return entity.getDisplayName();
}
@Override
public String getName() {
return entity.getName();
}
@Override
public Boolean isDeleted() {
return entity.getDeleted();
@ -219,6 +225,11 @@ public class MetricsRepository extends EntityRepository<Metrics> {
entity.setDisplayName(displayName);
}
@Override
public void setName(String name) {
entity.setName(name);
}
@Override
public void setUpdateDetails(String updatedBy, long updatedAt) {
entity.setUpdatedBy(updatedBy);

View File

@ -147,6 +147,7 @@ public class MlModelRepository extends EntityRepository<MlModel> {
@Override
public void prepare(MlModel mlModel) throws IOException {
EntityUtil.escapeReservedChars(getEntityInterface(mlModel));
mlModel.setFullyQualifiedName(getFQN(mlModel));
if (mlModel.getMlFeatures() != null && !mlModel.getMlFeatures().isEmpty()) {
@ -250,6 +251,11 @@ public class MlModelRepository extends EntityRepository<MlModel> {
return entity.getDisplayName();
}
@Override
public String getName() {
return entity.getName();
}
@Override
public Boolean isDeleted() {
return entity.getDeleted();
@ -335,6 +341,11 @@ public class MlModelRepository extends EntityRepository<MlModel> {
entity.setDisplayName(displayName);
}
@Override
public void setName(String name) {
entity.setName(name);
}
@Override
public void setUpdateDetails(String updatedBy, long updatedAt) {
entity.setUpdatedBy(updatedBy);

View File

@ -163,6 +163,8 @@ public class PipelineRepository extends EntityRepository<Pipeline> {
@Override
public void prepare(Pipeline pipeline) throws IOException {
EntityUtil.escapeReservedChars(getEntityInterface(pipeline));
EntityUtil.escapeReservedChars(pipeline.getTasks());
populateService(pipeline);
pipeline.setFullyQualifiedName(getFQN(pipeline));
EntityUtil.populateOwner(daoCollection.userDAO(), daoCollection.teamDAO(), pipeline.getOwner()); // Validate owner
@ -242,6 +244,11 @@ public class PipelineRepository extends EntityRepository<Pipeline> {
return entity.getDisplayName();
}
@Override
public String getName() {
return entity.getName();
}
@Override
public Boolean isDeleted() {
return entity.getDeleted();
@ -324,6 +331,11 @@ public class PipelineRepository extends EntityRepository<Pipeline> {
entity.setDisplayName(displayName);
}
@Override
public void setName(String name) {
entity.setName(name);
}
@Override
public void setUpdateDetails(String updatedBy, long updatedAt) {
entity.setUpdatedBy(updatedBy);

View File

@ -115,6 +115,11 @@ public class PipelineServiceRepository extends EntityRepository<PipelineService>
return entity.getDisplayName();
}
@Override
public String getName() {
return entity.getName();
}
@Override
public Boolean isDeleted() {
return entity.getDeleted();
@ -185,6 +190,11 @@ public class PipelineServiceRepository extends EntityRepository<PipelineService>
entity.setDisplayName(displayName);
}
@Override
public void setName(String name) {
entity.setName(name);
}
@Override
public void setUpdateDetails(String updatedBy, long updatedAt) {
entity.setUpdatedBy(updatedBy);

View File

@ -285,6 +285,11 @@ public class PolicyRepository extends EntityRepository<Policy> {
return entity.getDisplayName();
}
@Override
public String getName() {
return entity.getName();
}
@Override
public Boolean isDeleted() {
return entity.getDeleted();
@ -354,6 +359,11 @@ public class PolicyRepository extends EntityRepository<Policy> {
entity.setDisplayName(displayName);
}
@Override
public void setName(String name) {
entity.setName(name);
}
@Override
public void setUpdateDetails(String updatedBy, long updatedAt) {
entity.setUpdatedBy(updatedBy);

View File

@ -112,6 +112,11 @@ public class ReportRepository extends EntityRepository<Report> {
return entity.getDisplayName();
}
@Override
public String getName() {
return entity.getName();
}
@Override
public Boolean isDeleted() {
return entity.getDeleted();
@ -192,6 +197,11 @@ public class ReportRepository extends EntityRepository<Report> {
entity.setDisplayName(displayName);
}
@Override
public void setName(String name) {
entity.setName(name);
}
@Override
public void setUpdateDetails(String updatedBy, long updatedAt) {
entity.setUpdatedBy(updatedBy);

View File

@ -201,6 +201,11 @@ public class RoleRepository extends EntityRepository<Role> {
return entity.getDisplayName();
}
@Override
public String getName() {
return entity.getName();
}
@Override
public Boolean isDeleted() {
return entity.getDeleted();
@ -262,6 +267,11 @@ public class RoleRepository extends EntityRepository<Role> {
entity.setDisplayName(displayName);
}
@Override
public void setName(String name) {
entity.setName(name);
}
@Override
public void setUpdateDetails(String updatedBy, long updatedAt) {
entity.setUpdatedBy(updatedBy);

View File

@ -108,6 +108,11 @@ public class StorageServiceRepository extends EntityRepository<StorageService> {
return entity.getDisplayName();
}
@Override
public String getName() {
return entity.getName();
}
@Override
public Boolean isDeleted() {
return entity.getDeleted();
@ -178,6 +183,11 @@ public class StorageServiceRepository extends EntityRepository<StorageService> {
entity.setDisplayName(displayName);
}
@Override
public void setName(String name) {
entity.setName(name);
}
@Override
public void setUpdateDetails(String updatedBy, long updatedAt) {
entity.setUpdatedBy(updatedBy);

View File

@ -302,6 +302,9 @@ public class TableRepository extends EntityRepository<Table> {
@Override
public void prepare(Table table) throws IOException, ParseException {
EntityUtil.escapeReservedChars(getEntityInterface(table));
EntityUtil.escapeReservedChars(table.getColumns());
EntityUtil.escapeReservedChars(table.getTableConstraints());
Database database = helper(table).findEntity("database", DATABASE);
table.setDatabase(helper(database).toEntityReference());
DatabaseService databaseService = helper(database).findEntity("service", DATABASE_SERVICE);
@ -391,6 +394,7 @@ public class TableRepository extends EntityRepository<Table> {
return new Column()
.withDescription(column.getDescription())
.withName(column.getName())
.withDisplayName(column.getDisplayName())
.withFullyQualifiedName(column.getFullyQualifiedName())
.withArrayDataType(column.getArrayDataType())
.withConstraint(column.getConstraint())
@ -659,6 +663,11 @@ public class TableRepository extends EntityRepository<Table> {
return entity.getDisplayName();
}
@Override
public String getName() {
return entity.getName();
}
@Override
public Boolean isDeleted() {
return entity.getDeleted();
@ -744,6 +753,11 @@ public class TableRepository extends EntityRepository<Table> {
entity.setDisplayName(displayName);
}
@Override
public void setName(String name) {
entity.setName(name);
}
@Override
public void setUpdateDetails(String updatedBy, long updatedAt) {
entity.setUpdatedBy(updatedBy);

View File

@ -164,6 +164,11 @@ public class TeamRepository extends EntityRepository<Team> {
return entity.getDisplayName();
}
@Override
public String getName() {
return entity.getName();
}
@Override
public Boolean isDeleted() {
return entity.getDeleted();
@ -225,6 +230,11 @@ public class TeamRepository extends EntityRepository<Team> {
entity.setDisplayName(displayName);
}
@Override
public void setName(String name) {
entity.setName(name);
}
@Override
public void setUpdateDetails(String updatedBy, long updatedAt) {
entity.setUpdatedBy(updatedBy);

View File

@ -68,6 +68,7 @@ public class TopicRepository extends EntityRepository<Topic> {
@Override
public void prepare(Topic topic) throws IOException, ParseException {
EntityUtil.escapeReservedChars(getEntityInterface(topic));
MessagingService messagingService = helper(topic).findEntity("service", MESSAGING_SERVICE);
topic.setService(helper(messagingService).toEntityReference());
topic.setServiceType(messagingService.getServiceType());
@ -156,6 +157,11 @@ public class TopicRepository extends EntityRepository<Topic> {
return entity.getDisplayName();
}
@Override
public String getName() {
return entity.getName();
}
@Override
public Boolean isDeleted() {
return entity.getDeleted();
@ -236,6 +242,11 @@ public class TopicRepository extends EntityRepository<Topic> {
entity.setDisplayName(displayName);
}
@Override
public void setName(String name) {
entity.setName(name);
}
@Override
public void setUpdateDetails(String updatedBy, long updatedAt) {
entity.setUpdatedBy(updatedBy);

View File

@ -219,6 +219,11 @@ public class UserRepository extends EntityRepository<User> {
return entity.getDisplayName();
}
@Override
public String getName() {
return entity.getName();
}
@Override
public Boolean isDeleted() {
return entity.getDeleted();
@ -280,6 +285,11 @@ public class UserRepository extends EntityRepository<User> {
entity.setDisplayName(displayName);
}
@Override
public void setName(String name) {
entity.setName(name);
}
@Override
public void setUpdateDetails(String updatedBy, long updatedAt) {
entity.setUpdatedBy(updatedBy);

View File

@ -84,9 +84,7 @@ public class WebhookRepository extends EntityRepository<Webhook> {
}
@Override
public void prepare(Webhook entity) throws IOException {
// Nothing to prepare
}
public void prepare(Webhook entity) throws IOException {}
@Override
public void storeEntity(Webhook entity, boolean update) throws IOException {
@ -185,6 +183,11 @@ public class WebhookRepository extends EntityRepository<Webhook> {
@Override
public String getDisplayName() {
return entity.getDisplayName();
}
@Override
public String getName() {
return entity.getName();
}
@ -250,7 +253,12 @@ public class WebhookRepository extends EntityRepository<Webhook> {
@Override
public void setDisplayName(String displayName) {
/* No display name */
entity.setDisplayName(displayName);
}
@Override
public void setName(String name) {
entity.setName(name);
}
@Override

View File

@ -28,6 +28,8 @@ public interface EntityInterface<T> {
String getDisplayName();
String getName();
Boolean isDeleted();
default EntityReference getOwner() {
@ -72,6 +74,8 @@ public interface EntityInterface<T> {
void setDisplayName(String displayName);
void setName(String name);
void setUpdateDetails(String updatedBy, long updatedAt);
void setChangeDescription(Double newVersion, ChangeDescription changeDescription);

View File

@ -476,4 +476,32 @@ public final class EntityUtil {
.withEventType(EventType.ENTITY_SOFT_DELETED)
.withEntities(eventFilter.getEntities())));
}
public static void escapeReservedChars(EntityInterface entityInterface) {
entityInterface.setDisplayName(
entityInterface.getDisplayName() != null ? entityInterface.getDisplayName() : entityInterface.getName());
entityInterface.setName(entityInterface.getName().replace(".", "_DOT_"));
}
public static void escapeReservedChars(List<?> collection) {
if (collection == null || collection.isEmpty()) {
return;
}
for (Object object : collection) {
if (object instanceof Column) {
Column column = (Column) object;
column.setDisplayName(column.getDisplayName() != null ? column.getDisplayName() : column.getName());
column.setName(column.getName().replace(".", "_DOT_"));
escapeReservedChars(column.getChildren());
} else if (object instanceof TableConstraint) {
TableConstraint constraint = (TableConstraint) object;
constraint.setColumns(
constraint.getColumns().stream().map(s -> s.replace(".", "_DOT_")).collect(Collectors.toList()));
} else if (object instanceof Task) {
Task task = (Task) object;
task.setDisplayName(task.getDisplayName() != null ? task.getDisplayName() : task.getName());
task.setName(task.getName().replace(".", "_DOT_"));
}
}
}
}

View File

@ -9,7 +9,8 @@
"description": "Name that identifies the this entity instance uniquely",
"type": "string",
"minLength": 1,
"maxLength": 128
"maxLength": 128,
"pattern": "^[^.]*$"
},
"description": {
"description": "Description of dashboard service entity.",

View File

@ -10,7 +10,8 @@
"description": "Name that identifies the this entity instance uniquely",
"type": "string",
"minLength": 1,
"maxLength": 128
"maxLength": 128,
"pattern": "^[^.]*$"
},
"description": {
"description": "Description of Database entity.",

View File

@ -9,7 +9,8 @@
"description": "Name that identifies the this entity instance uniquely",
"type": "string",
"minLength": 1,
"maxLength": 128
"maxLength": 128,
"pattern": "^[^.]*$"
},
"description": {
"description": "Description of messaging service entity.",

View File

@ -9,7 +9,8 @@
"description": "Name that identifies the this entity instance uniquely",
"type": "string",
"minLength": 1,
"maxLength": 128
"maxLength": 128,
"pattern": "^[^.]*$"
},
"description": {
"description": "Description of pipeline service entity.",

View File

@ -9,7 +9,8 @@
"description": "Name that identifies the this entity instance uniquely",
"type": "string",
"minLength": 1,
"maxLength": 128
"maxLength": 128,
"pattern": "^[^.]*$"
},
"description": {
"description": "Description of Storage entity.",

View File

@ -10,8 +10,7 @@
"description": "Name that identifies the database.",
"type": "string",
"minLength": 1,
"maxLength": 128,
"pattern": "^[^.]*$"
"maxLength": 128
}
},
"properties": {

View File

@ -103,15 +103,13 @@
"description": "Local name (not fully qualified name) of the column. ColumnName is `-` when the column is not named in struct dataType. For example, BigQuery supports struct with unnamed fields.",
"type": "string",
"minLength": 1,
"maxLength": 128,
"pattern": "^[^.]*$"
"maxLength": 128
},
"tableName": {
"description": "Local name (not fully qualified name) of a table.",
"description": "Local name (not fully qualified name) of a table. Dots will be escaped automatically",
"type": "string",
"minLength": 1,
"maxLength": 128,
"pattern": "^[^.]*$"
"maxLength": 128
},
"fullyQualifiedColumnName": {
"description": "Fully qualified name of the column that includes `serviceName.databaseName.tableName.columnName[.nestedColumnName]`. When columnName is null for dataType struct fields, `field_#` where `#` is field index is used. For map dataType, for key the field name `key` is used and for the value field `value` is used.",
@ -127,6 +125,10 @@
"name": {
"$ref": "#/definitions/columnName"
},
"displayName": {
"description": "Display Name that identifies this column name.",
"type": "string"
},
"dataType": {
"description": "Data type of the column (int, date etc.).",
"$ref": "#/definitions/dataType"

View File

@ -10,8 +10,7 @@
"type": "string",
"javaType": "org.openmetadata.catalog.type.topic.TopicName",
"minLength": 1,
"maxLength": 128,
"pattern": "^[^.]*$"
"maxLength": 128
},
"schemaType": {
"description": "Schema type used for the message.",

View File

@ -16,6 +16,10 @@
"minLength": 1,
"maxLength": 128
},
"displayName": {
"description": "Display Name that identifies this webhook.",
"type": "string"
},
"description": {
"description": "Description of the application.",
"type": "string"

View File

@ -37,7 +37,8 @@
"description": "Name that identifies this dashboard service.",
"type": "string",
"minLength": 1,
"maxLength": 128
"maxLength": 128,
"pattern": "^[^.]*$"
},
"displayName": {
"description": "Display Name that identifies this dashboard service.",

View File

@ -139,7 +139,8 @@
"description": "Name that identifies this database service.",
"type": "string",
"minLength": 1,
"maxLength": 128
"maxLength": 128,
"pattern": "^[^.]*$"
},
"displayName": {
"description": "Display Name that identifies this database service.",

View File

@ -36,7 +36,8 @@
"description": "Name that identifies this messaging service.",
"type": "string",
"minLength": 1,
"maxLength": 128
"maxLength": 128,
"pattern": "^[^.]*$"
},
"serviceType": {
"description": "Type of messaging service such as Kafka or Pulsar...",

View File

@ -31,7 +31,8 @@
"description": "Name that identifies this pipeline service.",
"type": "string",
"minLength": 1,
"maxLength": 128
"maxLength": 128,
"pattern": "^[^.]*$"
},
"serviceType": {
"description": "Type of pipeline service such as Airflow or Prefect...",

View File

@ -14,7 +14,8 @@
"description": "Name that identifies this storage service.",
"type": "string",
"minLength": 1,
"maxLength": 128
"maxLength": 128,
"pattern": "^[^.]*$"
},
"displayName": {
"description": "Display Name that identifies this storage service.",

View File

@ -28,7 +28,8 @@ public abstract class EntityOperationsResourceTest<T, K> extends EntityResourceT
boolean supportsFollowers,
boolean supportsOwner,
boolean supportsTags,
boolean supportsAuthorizedMetadataOperations) {
boolean supportsAuthorizedMetadataOperations,
boolean supportsDots) {
super(
entityType,
entityClass,
@ -38,7 +39,8 @@ public abstract class EntityOperationsResourceTest<T, K> extends EntityResourceT
supportsFollowers,
supportsOwner,
supportsTags,
supportsAuthorizedMetadataOperations);
supportsAuthorizedMetadataOperations,
supportsDots);
}
// Override the resource path name of regular entities api/v1/<entities> to api/operations/v1/<operations>

View File

@ -147,6 +147,7 @@ public abstract class EntityResourceTest<T, K> extends CatalogApplicationTest {
private final boolean supportsFollowers;
private final boolean supportsOwner;
private final boolean supportsTags;
private final boolean supportsDots;
protected boolean supportsPatch = true;
protected boolean supportsSoftDelete = true;
private final boolean supportsAuthorizedMetadataOperations;
@ -205,7 +206,9 @@ public abstract class EntityResourceTest<T, K> extends CatalogApplicationTest {
boolean supportsFollowers,
boolean supportsOwner,
boolean supportsTags,
boolean supportsAuthorizedMetadataOperations) {
boolean supportsAuthorizedMetadataOperations,
boolean supportsDots) {
this.entityType = entityTYpe;
this.entityClass = entityClass;
this.entityListClass = entityListClass;
@ -215,6 +218,7 @@ public abstract class EntityResourceTest<T, K> extends CatalogApplicationTest {
this.supportsOwner = supportsOwner;
this.supportsTags = supportsTags;
this.supportsAuthorizedMetadataOperations = supportsAuthorizedMetadataOperations;
this.supportsDots = supportsDots;
ENTITY_RESOURCE_TEST_MAP.put(entityTYpe, this);
}
@ -735,6 +739,22 @@ public abstract class EntityResourceTest<T, K> extends CatalogApplicationTest {
assertResponse(() -> createEntity(create, ADMIN_AUTH_HEADERS), CONFLICT, ENTITY_ALREADY_EXISTS);
}
@Test
void post_entityWithDots_200(TestInfo test) throws HttpResponseException {
if (!supportsDots) {
return;
}
String name = String.format("%s_%s_foo.bar", entityType, test.getDisplayName());
final K request = createRequest(name, null, null, null);
T entity = createEntity(request, ADMIN_AUTH_HEADERS);
EntityInterface<T> entityInterface = getEntityInterface(entity);
assertEquals(name, entityInterface.getDisplayName());
String[] split = entityInterface.getFullyQualifiedName().split("\\.");
String actualName = split[split.length - 1];
assertTrue(actualName.contains("foo_DOT_bar"));
assertTrue(!actualName.contains("foo.bar"));
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Common entity tests for PUT operations
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@ -1030,6 +1050,7 @@ public abstract class EntityResourceTest<T, K> extends CatalogApplicationTest {
entity = getEntity(entityInterface.getId(), ADMIN_AUTH_HEADERS);
entityInterface = getEntityInterface(entity);
String oldDisplayName = entityInterface.getDisplayName();
//
// Add displayName, description, owner, and tags when previously they were null
@ -1043,7 +1064,6 @@ public abstract class EntityResourceTest<T, K> extends CatalogApplicationTest {
// Field changes
ChangeDescription change = getChangeDescription(entityInterface.getVersion());
change.getFieldsAdded().add(new FieldChange().withName("description").withNewValue("description"));
change.getFieldsAdded().add(new FieldChange().withName("displayName").withNewValue("displayName"));
if (supportsOwner) {
entityInterface.setOwner(TEAM_OWNER1);
change.getFieldsAdded().add(new FieldChange().withName("owner").withNewValue(TEAM_OWNER1));
@ -1053,6 +1073,13 @@ public abstract class EntityResourceTest<T, K> extends CatalogApplicationTest {
entityInterface.getTags().add(USER_ADDRESS_TAG_LABEL);
change.getFieldsAdded().add(new FieldChange().withName("tags").withNewValue(entityInterface.getTags()));
}
if (supportsDots) {
change
.getFieldsUpdated()
.add(new FieldChange().withName("displayName").withOldValue(oldDisplayName).withNewValue("displayName"));
} else {
change.getFieldsAdded().add(new FieldChange().withName("displayName").withNewValue("displayName"));
}
entity = patchEntityAndCheck(entity, origJson, ADMIN_AUTH_HEADERS, MINOR_UPDATE, change);
entityInterface = getEntityInterface(entity);
@ -1099,14 +1126,18 @@ public abstract class EntityResourceTest<T, K> extends CatalogApplicationTest {
List<TagLabel> removedTags = entityInterface.getTags();
entityInterface.setDescription(null);
entityInterface.setDisplayName(null);
if (!supportsDots) {
entityInterface.setDisplayName(null);
}
entityInterface.setOwner(null);
entityInterface.setTags(null);
// Field changes
change = getChangeDescription(entityInterface.getVersion());
change.getFieldsDeleted().add(new FieldChange().withName("description").withOldValue("description1"));
change.getFieldsDeleted().add(new FieldChange().withName("displayName").withOldValue("displayName1"));
if (!supportsDots) {
change.getFieldsDeleted().add(new FieldChange().withName("displayName").withOldValue("displayName1"));
}
if (supportsOwner) {
change.getFieldsDeleted().add(new FieldChange().withName("owner").withOldValue(USER_OWNER1));
}

View File

@ -45,7 +45,7 @@ import org.openmetadata.catalog.util.TestUtils;
public class ChartResourceTest extends EntityResourceTest<Chart, CreateChart> {
public ChartResourceTest() {
super(Entity.CHART, Chart.class, ChartList.class, "charts", ChartResource.FIELDS, true, true, true, true);
super(Entity.CHART, Chart.class, ChartList.class, "charts", ChartResource.FIELDS, true, true, true, true, true);
}
@BeforeAll

View File

@ -68,6 +68,7 @@ public class DashboardResourceTest extends EntityResourceTest<Dashboard, CreateD
true,
true,
true,
true,
true);
}

View File

@ -57,6 +57,7 @@ public class DatabaseResourceTest extends EntityResourceTest<Database, CreateDat
false,
true,
false,
true,
true);
}

View File

@ -21,6 +21,7 @@ import static javax.ws.rs.core.Response.Status.OK;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.openmetadata.catalog.exception.CatalogExceptionMessage.entityNotFound;
import static org.openmetadata.catalog.exception.CatalogExceptionMessage.invalidColumnFQN;
import static org.openmetadata.catalog.type.ColumnDataType.ARRAY;
@ -58,6 +59,7 @@ import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.Response.Status;
import lombok.extern.slf4j.Slf4j;
@ -113,7 +115,7 @@ import org.openmetadata.catalog.util.TestUtils;
public class TableResourceTest extends EntityResourceTest<Table, CreateTable> {
public TableResourceTest() {
super(Entity.TABLE, Table.class, TableList.class, "tables", TableResource.FIELDS, true, true, true, true);
super(Entity.TABLE, Table.class, TableList.class, "tables", TableResource.FIELDS, true, true, true, true, true);
}
@BeforeAll
@ -187,6 +189,23 @@ public class TableResourceTest extends EntityResourceTest<Table, CreateTable> {
assertEquals(expectedFQN, table.getFullyQualifiedName());
}
@Test
void post_tableWithColumnWithDots(TestInfo test) throws IOException {
CreateTable create = createRequest(test);
List<Column> columns = new ArrayList<>();
columns.add(getColumn("col.umn", INT, null));
TableConstraint constraint =
new TableConstraint().withConstraintType(ConstraintType.UNIQUE).withColumns(List.of(columns.get(0).getName()));
create.setColumns(columns);
create.setTableConstraints(List.of(constraint));
Table created = createAndCheckEntity(create, ADMIN_AUTH_HEADERS);
Column column = created.getColumns().get(0);
assertTrue(column.getName().equals("col_DOT_umn"));
assertTrue(column.getDisplayName().contains("col.umn"));
assertTrue(column.getFullyQualifiedName().contains("col_DOT_umn"));
assertEquals("col_DOT_umn", created.getTableConstraints().get(0).getColumns().get(0));
}
public static Column getColumn(String name, ColumnDataType columnDataType, TagLabel tag) {
return getColumn(name, columnDataType, null, tag);
}
@ -1322,7 +1341,9 @@ public class TableResourceTest extends EntityResourceTest<Table, CreateTable> {
private static void assertColumn(Column expectedColumn, Column actualColumn) throws HttpResponseException {
assertNotNull(actualColumn.getFullyQualifiedName());
assertEquals(expectedColumn.getName(), actualColumn.getName());
assertTrue(
expectedColumn.getName().equals(actualColumn.getName())
|| expectedColumn.getName().equals(actualColumn.getDisplayName()));
assertEquals(expectedColumn.getDescription(), actualColumn.getDescription());
assertEquals(expectedColumn.getDataType(), actualColumn.getDataType());
assertEquals(expectedColumn.getArrayDataType(), actualColumn.getArrayDataType());
@ -1460,12 +1481,28 @@ public class TableResourceTest extends EntityResourceTest<Table, CreateTable> {
assertEquals(createRequest.getTableType(), createdEntity.getTableType());
assertColumns(createRequest.getColumns(), createdEntity.getColumns());
validateDatabase(createRequest.getDatabase(), createdEntity.getDatabase());
assertEquals(createRequest.getTableConstraints(), createdEntity.getTableConstraints());
validateTableConstraints(createRequest.getTableConstraints(), createdEntity.getTableConstraints());
TestUtils.validateTags(createRequest.getTags(), createdEntity.getTags());
TestUtils.validateEntityReference(createdEntity.getFollowers());
assertListNotNull(createdEntity.getService(), createdEntity.getServiceType());
}
private void validateTableConstraints(List<TableConstraint> expected, List<TableConstraint> actual) {
if (expected == null || actual == null) {
assertEquals(expected, actual);
return;
}
List<TableConstraint> copy = new ArrayList<>();
actual.forEach(c -> copy.add(cloneAndUnescape(c)));
assertEquals(expected, copy);
}
private TableConstraint cloneAndUnescape(TableConstraint constraint) {
return new TableConstraint()
.withConstraintType(constraint.getConstraintType())
.withColumns(constraint.getColumns().stream().map(e -> e.replace("_DOT_", ".")).collect(Collectors.toList()));
}
@Override
public void validateUpdatedEntity(Table updated, CreateTable request, Map<String, String> authHeaders)
throws HttpResponseException {

View File

@ -64,7 +64,7 @@ public class WebhookResourceTest extends EntityResourceTest<Webhook, CreateWebho
}
public WebhookResourceTest() {
super(Entity.WEBHOOK, Webhook.class, WebhookList.class, "webhook", "", false, false, false, false);
super(Entity.WEBHOOK, Webhook.class, WebhookList.class, "webhook", "", false, false, false, false, false);
supportsPatch = false;
}

View File

@ -44,7 +44,8 @@ public class GlossaryResourceTest extends EntityResourceTest<Glossary, CreateGlo
false,
true,
true,
true);
true,
false);
}
@BeforeAll

View File

@ -76,6 +76,7 @@ public class GlossaryTermResourceTest extends EntityResourceTest<GlossaryTerm, C
false,
false,
true,
false,
false);
}

View File

@ -58,6 +58,7 @@ public class LocationResourceTest extends EntityResourceTest<Location, CreateLoc
true,
true,
true,
true,
true);
}

View File

@ -103,7 +103,17 @@ public class MlModelResourceTest extends EntityResourceTest<MlModel, CreateMlMod
new MlHyperParameter().withName("random").withValue("hello"));
public MlModelResourceTest() {
super(Entity.MLMODEL, MlModel.class, MlModelList.class, "mlmodels", MlModelResource.FIELDS, true, true, true, true);
super(
Entity.MLMODEL,
MlModel.class,
MlModelList.class,
"mlmodels",
MlModelResource.FIELDS,
true,
true,
true,
true,
true);
}
@BeforeAll

View File

@ -97,6 +97,7 @@ public class AirflowPipelineResourceTest extends EntityOperationsResourceTest<Ai
false,
true,
false,
true,
true);
}
@ -149,7 +150,6 @@ public class AirflowPipelineResourceTest extends EntityOperationsResourceTest<Ai
createRequest.getDescription(),
TestUtils.getPrincipal(authHeaders),
createRequest.getOwner());
assertEquals(createRequest.getDisplayName(), ingestion.getDisplayName());
assertEquals(createRequest.getConcurrency(), ingestion.getConcurrency());
validatePipelineConfig(createRequest.getPipelineConfig(), ingestion.getPipelineConfig());
}

View File

@ -18,6 +18,7 @@ import static javax.ws.rs.core.Response.Status.OK;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.openmetadata.catalog.util.TestUtils.ADMIN_AUTH_HEADERS;
import static org.openmetadata.catalog.util.TestUtils.UpdateType.MINOR_UPDATE;
import static org.openmetadata.catalog.util.TestUtils.assertListNotNull;
@ -77,6 +78,7 @@ public class PipelineResourceTest extends EntityResourceTest<Pipeline, CreatePip
true,
true,
true,
true,
true);
}
@ -119,13 +121,28 @@ public class PipelineResourceTest extends EntityResourceTest<Pipeline, CreatePip
createRequest.getDescription(),
TestUtils.getPrincipal(authHeaders),
createRequest.getOwner());
assertEquals(createRequest.getDisplayName(), pipeline.getDisplayName());
assertNotNull(pipeline.getServiceType());
assertService(createRequest.getService(), pipeline.getService());
assertEquals(createRequest.getTasks(), pipeline.getTasks());
validateTasks(createRequest.getTasks(), pipeline.getTasks());
TestUtils.validateTags(createRequest.getTags(), pipeline.getTags());
}
private void validateTasks(List<Task> expected, List<Task> actual) {
if (expected == null || actual == null) {
assertEquals(expected, actual);
return;
}
assertEquals(expected.size(), actual.size());
int i = 0;
for (Task expectedTask : expected) {
Task actualTask = actual.get(i);
assertTrue(
expectedTask.getName().equals(actualTask.getName())
|| expectedTask.getName().equals(actualTask.getDisplayName()));
i++;
}
}
@Override
public void validateUpdatedEntity(Pipeline pipeline, CreatePipeline request, Map<String, String> authHeaders)
throws HttpResponseException {
@ -142,7 +159,7 @@ public class PipelineResourceTest extends EntityResourceTest<Pipeline, CreatePip
expected.getOwner());
assertEquals(expected.getDisplayName(), updated.getDisplayName());
assertService(expected.getService(), updated.getService());
assertEquals(expected.getTasks(), updated.getTasks());
validateTasks(expected.getTasks(), updated.getTasks());
TestUtils.validateTags(expected.getTags(), updated.getTags());
}
@ -160,7 +177,7 @@ public class PipelineResourceTest extends EntityResourceTest<Pipeline, CreatePip
@SuppressWarnings("unchecked")
List<Task> expectedTasks = (List<Task>) expected;
List<Task> actualTasks = JsonUtils.readObjects(actual.toString(), Task.class);
assertEquals(expectedTasks, actualTasks);
validateTasks(expectedTasks, actualTasks);
} else {
assertCommonFieldChange(fieldName, expected, actual);
}
@ -209,6 +226,17 @@ public class PipelineResourceTest extends EntityResourceTest<Pipeline, CreatePip
}
}
@Test
void post_pipelineWithTasksWithDots(TestInfo test) throws IOException, URISyntaxException {
CreatePipeline create = createRequest(test);
Task task = new Task().withName("ta.sk").withDescription("description").withTaskUrl(new URI("http://localhost:0"));
create.setTasks(List.of(task));
Pipeline created = createAndCheckEntity(create, ADMIN_AUTH_HEADERS);
Task actualTask = created.getTasks().get(0);
assertTrue(actualTask.getName().equals("ta_DOT_sk"));
assertTrue(actualTask.getDisplayName().contains("ta.sk"));
}
@Test
void put_PipelineUrlUpdate_200(TestInfo test) throws IOException, URISyntaxException {
CreatePipeline request =

View File

@ -66,7 +66,17 @@ public class PolicyResourceTest extends EntityResourceTest<Policy, CreatePolicy>
private static Location location;
public PolicyResourceTest() {
super(Entity.POLICY, Policy.class, PolicyList.class, "policies", PolicyResource.FIELDS, false, true, false, false);
super(
Entity.POLICY,
Policy.class,
PolicyList.class,
"policies",
PolicyResource.FIELDS,
false,
true,
false,
false,
false);
}
@BeforeAll

View File

@ -57,6 +57,7 @@ public class DashboardServiceResourceTest extends EntityResourceTest<DashboardSe
false,
true,
false,
false,
false);
this.supportsPatch = false;
}

View File

@ -75,6 +75,7 @@ public class DatabaseServiceResourceTest extends EntityResourceTest<DatabaseServ
false,
true,
false,
false,
false);
this.supportsPatch = false;
}

View File

@ -64,6 +64,7 @@ public class MessagingServiceResourceTest extends EntityResourceTest<MessagingSe
false,
true,
false,
false,
false);
supportsPatch = false;
}

View File

@ -61,6 +61,7 @@ public class PipelineServiceResourceTest extends EntityResourceTest<PipelineServ
false,
true,
false,
false,
false);
this.supportsPatch = false;
}

View File

@ -46,6 +46,7 @@ public class StorageServiceResourceTest extends EntityResourceTest<StorageServic
false,
true,
false,
false,
false);
this.supportsPatch = false;
}

View File

@ -46,7 +46,7 @@ import org.openmetadata.catalog.util.TestUtils;
public class RoleResourceTest extends EntityResourceTest<Role, CreateRole> {
public RoleResourceTest() {
super(Entity.ROLE, Role.class, RoleList.class, "roles", null, false, false, false, false);
super(Entity.ROLE, Role.class, RoleList.class, "roles", null, false, false, false, false, false);
}
@Test
@ -141,8 +141,6 @@ public class RoleResourceTest extends EntityResourceTest<Role, CreateRole> {
public void validateCreatedEntity(Role role, CreateRole createRequest, Map<String, String> authHeaders) {
validateCommonEntityFields(
getEntityInterface(role), createRequest.getDescription(), TestUtils.getPrincipal(authHeaders), null);
assertEquals(createRequest.getDisplayName(), role.getDisplayName());
}
@Override

View File

@ -65,7 +65,7 @@ public class TeamResourceTest extends EntityResourceTest<Team, CreateTeam> {
final Profile PROFILE = new Profile().withImages(new ImageList().withImage(URI.create("http://image.com")));
public TeamResourceTest() {
super(Entity.TEAM, Team.class, TeamList.class, "teams", TeamResource.FIELDS, false, false, false, false);
super(Entity.TEAM, Team.class, TeamList.class, "teams", TeamResource.FIELDS, false, false, false, false, false);
}
@Test
@ -251,7 +251,6 @@ public class TeamResourceTest extends EntityResourceTest<Team, CreateTeam> {
validateCommonEntityFields(
getEntityInterface(team), createRequest.getDescription(), TestUtils.getPrincipal(authHeaders), null);
assertEquals(createRequest.getDisplayName(), team.getDisplayName());
assertEquals(createRequest.getProfile(), team.getProfile());
List<EntityReference> expectedUsers = new ArrayList<>();

View File

@ -83,7 +83,7 @@ public class UserResourceTest extends EntityResourceTest<User, CreateUser> {
final Profile PROFILE = new Profile().withImages(new ImageList().withImage(URI.create("http://image.com")));
public UserResourceTest() {
super(Entity.USER, User.class, UserList.class, "users", UserResource.FIELDS, false, false, false, false);
super(Entity.USER, User.class, UserList.class, "users", UserResource.FIELDS, false, false, false, false, false);
}
@Test

View File

@ -51,7 +51,7 @@ import org.openmetadata.catalog.util.TestUtils.UpdateType;
public class TopicResourceTest extends EntityResourceTest<Topic, CreateTopic> {
public TopicResourceTest() {
super(Entity.TOPIC, Topic.class, TopicList.class, "topics", TopicResource.FIELDS, true, true, true, true);
super(Entity.TOPIC, Topic.class, TopicList.class, "topics", TopicResource.FIELDS, true, true, true, true, true);
}
@Test

View File

@ -7,5 +7,5 @@ Provides metadata version information.
from incremental import Version
__version__ = Version("metadata", 0, 9, 0, dev=6)
__version__ = Version("metadata", 0, 9, 0, dev=7)
__all__ = ["__version__"]