chore: import upstream snapshot with attribution
Integration Tests - MySQL + Elasticsearch / Detect Changes (push) Has been cancelled
Integration Tests - MySQL + Elasticsearch / integration-tests-mysql-elasticsearch (push) Has been cancelled
Integration Tests - PostgreSQL + Elasticsearch + Redis / Detect Changes (push) Has been cancelled
Integration Tests - PostgreSQL + Elasticsearch + Redis / integration-tests-postgres-elasticsearch-redis (push) Has been cancelled
Integration Tests - PostgreSQL + OpenSearch / Detect Changes (push) Has been cancelled
Integration Tests - PostgreSQL + OpenSearch / integration-tests-postgres-opensearch (push) Has been cancelled
Java Checkstyle / java-checkstyle (push) Has been cancelled
Maven Collate Tests / maven-collate-ci (push) Has been cancelled
OpenMetadata Service Unit Tests / openmetadata-service-unit-tests-status (push) Has been cancelled
Publish Package to Maven Central Repository / publish-maven-packages (push) Has been cancelled
OpenMetadata Service Unit Tests / Detect Changes (push) Has been cancelled
OpenMetadata Service Unit Tests / openmetadata-service-unit-tests (push) Has been cancelled
OpenMetadata Service Unit Tests / k8s_operator-unit-tests (push) Has been cancelled
Integration Tests - MySQL + Elasticsearch / Detect Changes (push) Has been cancelled
Integration Tests - MySQL + Elasticsearch / integration-tests-mysql-elasticsearch (push) Has been cancelled
Integration Tests - PostgreSQL + Elasticsearch + Redis / Detect Changes (push) Has been cancelled
Integration Tests - PostgreSQL + Elasticsearch + Redis / integration-tests-postgres-elasticsearch-redis (push) Has been cancelled
Integration Tests - PostgreSQL + OpenSearch / Detect Changes (push) Has been cancelled
Integration Tests - PostgreSQL + OpenSearch / integration-tests-postgres-opensearch (push) Has been cancelled
Java Checkstyle / java-checkstyle (push) Has been cancelled
Maven Collate Tests / maven-collate-ci (push) Has been cancelled
OpenMetadata Service Unit Tests / openmetadata-service-unit-tests-status (push) Has been cancelled
Publish Package to Maven Central Repository / publish-maven-packages (push) Has been cancelled
OpenMetadata Service Unit Tests / Detect Changes (push) Has been cancelled
OpenMetadata Service Unit Tests / openmetadata-service-unit-tests (push) Has been cancelled
OpenMetadata Service Unit Tests / k8s_operator-unit-tests (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
package org.openmetadata.schema;
|
||||
|
||||
public interface AppRuntime {
|
||||
Boolean getEnabled();
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright 2021 Collate
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.openmetadata.schema;
|
||||
|
||||
import java.util.List;
|
||||
import org.openmetadata.schema.type.EntityReference;
|
||||
|
||||
/** Interface to be implemented by all entities to provide a way to access all the common fields. */
|
||||
@SuppressWarnings("unused")
|
||||
public interface BulkAssetsRequestInterface {
|
||||
// Lower case entity name to canonical entity name map
|
||||
List<EntityReference> getAssets();
|
||||
|
||||
void setAssets(List<EntityReference> assets);
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2022 Collate
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.openmetadata.schema;
|
||||
|
||||
import java.util.List;
|
||||
import org.openmetadata.schema.type.Column;
|
||||
|
||||
/**
|
||||
* Interface to be implemented by entities with a list of Column and FullyQualifiedName. It is used
|
||||
* when adding lineage between different entities.
|
||||
*/
|
||||
public interface ColumnsEntityInterface {
|
||||
|
||||
String getFullyQualifiedName();
|
||||
|
||||
List<Column> getColumns();
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright 2021 Collate
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.openmetadata.schema;
|
||||
|
||||
import java.util.List;
|
||||
import org.openmetadata.schema.type.EntityReference;
|
||||
import org.openmetadata.schema.type.LifeCycle;
|
||||
import org.openmetadata.schema.type.TagLabel;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public interface CreateEntity {
|
||||
String getName();
|
||||
|
||||
String getDisplayName();
|
||||
|
||||
String getDescription();
|
||||
|
||||
default List<EntityReference> getOwners() {
|
||||
return null;
|
||||
}
|
||||
|
||||
default List<EntityReference> getReviewers() {
|
||||
return null;
|
||||
}
|
||||
|
||||
default List<TagLabel> getTags() {
|
||||
return null;
|
||||
}
|
||||
|
||||
default Object getExtension() {
|
||||
return null;
|
||||
}
|
||||
|
||||
default List<String> getDomains() {
|
||||
return null;
|
||||
}
|
||||
|
||||
default List<String> getDataProducts() {
|
||||
return null;
|
||||
}
|
||||
|
||||
default LifeCycle getLifeCycle() {
|
||||
return null;
|
||||
}
|
||||
|
||||
<K extends CreateEntity> K withName(String name);
|
||||
|
||||
<K extends CreateEntity> K withDisplayName(String displayName);
|
||||
|
||||
<K extends CreateEntity> K withDescription(String description);
|
||||
|
||||
default void setOwners(List<EntityReference> owners) {}
|
||||
|
||||
default void setDomains(List<String> domains) {}
|
||||
|
||||
default void setDataProducts(List<String> dataProducts) {}
|
||||
|
||||
default void setTags(List<TagLabel> tags) {
|
||||
/* no-op implementation to be overridden */
|
||||
}
|
||||
|
||||
default void setReviewers(List<EntityReference> reviewers) {}
|
||||
|
||||
default <K extends CreateEntity> K withExtension(Object extension) {
|
||||
return (K) this;
|
||||
}
|
||||
|
||||
default <K extends CreateEntity> K withLifeCycle(LifeCycle lifeCycle) {
|
||||
return (K) this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package org.openmetadata.schema;
|
||||
|
||||
public interface DataInsightInterface {
|
||||
Long getTimestamp();
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright 2021 Collate
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.openmetadata.schema;
|
||||
|
||||
/**
|
||||
* Interface to be implemented by all doc store entities to provide a way to access all the common
|
||||
* fields.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public interface DocStoreEntityInterface {
|
||||
String getEntityType();
|
||||
|
||||
Object getData();
|
||||
}
|
||||
@@ -0,0 +1,254 @@
|
||||
/*
|
||||
* Copyright 2021 Collate
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.openmetadata.schema;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import java.net.URI;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import org.openmetadata.common.utils.CommonUtil;
|
||||
import org.openmetadata.schema.entity.type.Style;
|
||||
import org.openmetadata.schema.type.*;
|
||||
import org.openmetadata.schema.utils.EntityInterfaceUtil;
|
||||
|
||||
/** Interface to be implemented by all entities to provide a way to access all the common fields. */
|
||||
@SuppressWarnings("unused")
|
||||
public interface EntityInterface {
|
||||
// Lower case entity name to canonical entity name map
|
||||
Map<String, String> CANONICAL_ENTITY_NAME_MAP = new HashMap<>();
|
||||
Map<String, Class<? extends EntityInterface>> ENTITY_TYPE_TO_CLASS_MAP = new HashMap<>();
|
||||
|
||||
UUID getId();
|
||||
|
||||
String getDescription();
|
||||
|
||||
String getDisplayName();
|
||||
|
||||
String getName();
|
||||
|
||||
default Boolean getDeleted() {
|
||||
return null;
|
||||
}
|
||||
|
||||
Double getVersion();
|
||||
|
||||
String getUpdatedBy();
|
||||
|
||||
Long getUpdatedAt();
|
||||
|
||||
default String getImpersonatedBy() {
|
||||
return null;
|
||||
}
|
||||
|
||||
URI getHref();
|
||||
|
||||
ChangeDescription getChangeDescription();
|
||||
|
||||
ChangeDescription getIncrementalChangeDescription();
|
||||
|
||||
default UsageDetails getUsageSummary() {
|
||||
return null;
|
||||
}
|
||||
|
||||
default List<EntityReference> getOwners() {
|
||||
return null;
|
||||
}
|
||||
|
||||
default List<TagLabel> getTags() {
|
||||
return null;
|
||||
}
|
||||
|
||||
default EntityStatus getEntityStatus() {
|
||||
return null;
|
||||
}
|
||||
|
||||
default ProviderType getProvider() {
|
||||
return null;
|
||||
}
|
||||
|
||||
default List<EntityReference> getFollowers() {
|
||||
return null;
|
||||
}
|
||||
|
||||
default Votes getVotes() {
|
||||
return null;
|
||||
}
|
||||
|
||||
default EntityReference getService() {
|
||||
return null;
|
||||
}
|
||||
|
||||
String getFullyQualifiedName();
|
||||
|
||||
default Object getExtension() {
|
||||
return null;
|
||||
}
|
||||
|
||||
default List<EntityReference> getChildren() {
|
||||
return null;
|
||||
}
|
||||
|
||||
default List<EntityReference> getReviewers() {
|
||||
return null;
|
||||
}
|
||||
|
||||
default List<EntityReference> getExperts() {
|
||||
return null;
|
||||
}
|
||||
|
||||
default List<EntityReference> getDomains() {
|
||||
return null;
|
||||
}
|
||||
|
||||
default List<EntityReference> getDataProducts() {
|
||||
return null;
|
||||
}
|
||||
|
||||
default EntityReference getDataContract() {
|
||||
return null;
|
||||
}
|
||||
|
||||
default Style getStyle() {
|
||||
return null;
|
||||
}
|
||||
|
||||
default LifeCycle getLifeCycle() {
|
||||
return null;
|
||||
}
|
||||
|
||||
default AssetCertification getCertification() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Source hash (fingerprint) of the entity as computed by the ingestion connector. Only entities
|
||||
* whose JSON schema declares {@code sourceHash} override this; all others inherit {@code null}.
|
||||
* Used by the bulk update path to skip entities whose source content is unchanged.
|
||||
*/
|
||||
default String getSourceHash() {
|
||||
return null;
|
||||
}
|
||||
|
||||
void setId(UUID id);
|
||||
|
||||
void setDescription(String description);
|
||||
|
||||
void setDisplayName(String displayName);
|
||||
|
||||
void setName(String name);
|
||||
|
||||
void setVersion(Double newVersion);
|
||||
|
||||
void setChangeDescription(ChangeDescription changeDescription);
|
||||
|
||||
void setIncrementalChangeDescription(ChangeDescription incrementalChangeDescription);
|
||||
|
||||
default void setUsageSummary(UsageDetails usageSummary) {}
|
||||
|
||||
void setFullyQualifiedName(String fullyQualifiedName);
|
||||
|
||||
default void setDeleted(Boolean flag) {}
|
||||
|
||||
void setUpdatedBy(String admin);
|
||||
|
||||
void setUpdatedAt(Long updatedAt);
|
||||
|
||||
default void setImpersonatedBy(String botName) {
|
||||
/* no-op implementation to be overridden */
|
||||
}
|
||||
|
||||
void setHref(URI href);
|
||||
|
||||
default void setTags(List<TagLabel> tags) {
|
||||
/* no-op implementation to be overridden */
|
||||
}
|
||||
|
||||
default void setEntityStatus(EntityStatus approvalStatus) {
|
||||
/* no-op implementation to be overridden */
|
||||
}
|
||||
|
||||
default void setOwners(List<EntityReference> owners) {
|
||||
/* no-op implementation to be overridden */
|
||||
}
|
||||
|
||||
default void setExtension(Object extension) {
|
||||
/* no-op implementation to be overridden */
|
||||
}
|
||||
|
||||
default void setChildren(List<EntityReference> entityReference) {
|
||||
/* no-op implementation to be overridden */
|
||||
}
|
||||
|
||||
default void setReviewers(List<EntityReference> entityReference) {
|
||||
/* no-op implementation to be overridden */
|
||||
}
|
||||
|
||||
default void setExperts(List<EntityReference> entityReference) {
|
||||
/* no-op implementation to be overridden */
|
||||
}
|
||||
|
||||
default void setDomains(List<EntityReference> entityReference) {
|
||||
/* no-op implementation to be overridden */
|
||||
}
|
||||
|
||||
default void setDataProducts(List<EntityReference> dataProducts) {
|
||||
/* no-op implementation to be overridden */
|
||||
}
|
||||
|
||||
default void setDataContract(EntityReference dataContract) {
|
||||
/* no-op implementation to be overridden */
|
||||
}
|
||||
|
||||
default void setFollowers(List<EntityReference> followers) {
|
||||
/* no-op implementation to be overridden */
|
||||
}
|
||||
|
||||
default void setVotes(Votes vote) {
|
||||
/* no-op implementation to be overridden */
|
||||
}
|
||||
|
||||
default void setStyle(Style style) {
|
||||
/* no-op implementation to be overridden */
|
||||
}
|
||||
|
||||
default void setLifeCycle(LifeCycle lifeCycle) {
|
||||
/* no-op implementation to be overridden */
|
||||
}
|
||||
|
||||
default void setCertification(AssetCertification certification) {
|
||||
/* no-op implementation to be overridden */
|
||||
}
|
||||
|
||||
<T extends EntityInterface> T withHref(URI href);
|
||||
|
||||
@JsonIgnore
|
||||
default EntityReference getEntityReference() {
|
||||
return new EntityReference()
|
||||
.withId(getId())
|
||||
.withName(getName())
|
||||
.withFullyQualifiedName(
|
||||
getFullyQualifiedName() == null
|
||||
? EntityInterfaceUtil.quoteName(getName())
|
||||
: getFullyQualifiedName())
|
||||
.withDescription(getDescription())
|
||||
.withDisplayName(CommonUtil.nullOrEmpty(getDisplayName()) ? getName() : getDisplayName())
|
||||
.withType(
|
||||
CANONICAL_ENTITY_NAME_MAP.get(this.getClass().getSimpleName().toLowerCase(Locale.ROOT)))
|
||||
.withDeleted(getDeleted())
|
||||
.withHref(getHref());
|
||||
}
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
package org.openmetadata.schema;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import java.text.Format;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import org.openmetadata.schema.type.EntityReference;
|
||||
|
||||
public interface EntityTimeSeriesInterface {
|
||||
Map<String, String> CANONICAL_ENTITY_NAME_MAP = new HashMap<>();
|
||||
Map<String, Class<? extends EntityTimeSeriesInterface>> ENTITY_TYPE_TO_CLASS_MAP =
|
||||
new HashMap<>();
|
||||
|
||||
UUID getId();
|
||||
|
||||
Long getTimestamp();
|
||||
|
||||
void setId(UUID id);
|
||||
|
||||
@JsonIgnore
|
||||
default Date getDateParsedTimestamp() {
|
||||
return new Date(getTimestamp());
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
default String getStrParsedTimestamp() {
|
||||
Date date = new Date(getTimestamp());
|
||||
Format formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
return formatter.format(date);
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
default String getIso8601StrDate() {
|
||||
Date date = new Date(getTimestamp());
|
||||
Format formatter = new SimpleDateFormat("yyyy-MM-dd");
|
||||
return formatter.format(date);
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
default EntityReference getEntityReference() {
|
||||
return new EntityReference()
|
||||
.withId(getId())
|
||||
.withType(
|
||||
CANONICAL_ENTITY_NAME_MAP.get(
|
||||
this.getClass().getSimpleName().toLowerCase(Locale.ROOT)));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright 2022 Collate
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.openmetadata.schema;
|
||||
|
||||
/** Interface which could be implemented by Enums classes */
|
||||
public interface EnumInterface {
|
||||
String value();
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package org.openmetadata.schema;
|
||||
|
||||
import java.util.List;
|
||||
import org.openmetadata.schema.type.TagLabel;
|
||||
|
||||
public interface FieldInterface {
|
||||
String getName();
|
||||
|
||||
String getDisplayName();
|
||||
|
||||
String getDescription();
|
||||
|
||||
String getDataTypeDisplay();
|
||||
|
||||
String getFullyQualifiedName();
|
||||
|
||||
List<TagLabel> getTags();
|
||||
|
||||
default void setTags(List<TagLabel> tags) {
|
||||
/* no-op implementation to be overridden */
|
||||
}
|
||||
|
||||
List<? extends FieldInterface> getChildren();
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package org.openmetadata.schema;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import org.openmetadata.schema.type.Function.ParameterType;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(value = ElementType.METHOD)
|
||||
public @interface Function {
|
||||
String name();
|
||||
|
||||
String input();
|
||||
|
||||
String description();
|
||||
|
||||
String[] examples();
|
||||
|
||||
ParameterType paramInputType() default ParameterType.NOT_REQUIRED;
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package org.openmetadata.schema;
|
||||
|
||||
import java.util.List;
|
||||
import org.openmetadata.schema.entity.type.Style;
|
||||
import org.openmetadata.schema.type.AssetCertification;
|
||||
import org.openmetadata.schema.type.ChangeDescription;
|
||||
import org.openmetadata.schema.type.EntityReference;
|
||||
import org.openmetadata.schema.type.LifeCycle;
|
||||
import org.openmetadata.schema.type.TagLabel;
|
||||
import org.openmetadata.schema.type.Votes;
|
||||
|
||||
/**
|
||||
* Lightweight interface for named objects that don't need full entity semantics. Use this for
|
||||
* objects like Task, Workflow instances, etc. that need identity and versioning but not owners,
|
||||
* followers, votes, or other entity-specific features.
|
||||
*
|
||||
* <p>Extends EntityInterface to reuse existing repository infrastructure, but provides explicit
|
||||
* null/no-op defaults for features that don't apply to lightweight entities.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public interface NamedEntityInterface extends EntityInterface {
|
||||
|
||||
@Override
|
||||
default List<EntityReference> getOwners() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
default void setOwners(List<EntityReference> owners) {}
|
||||
|
||||
@Override
|
||||
default List<EntityReference> getFollowers() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
default void setFollowers(List<EntityReference> followers) {}
|
||||
|
||||
@Override
|
||||
default Votes getVotes() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
default void setVotes(Votes votes) {}
|
||||
|
||||
@Override
|
||||
default List<EntityReference> getDataProducts() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
default void setDataProducts(List<EntityReference> dataProducts) {}
|
||||
|
||||
@Override
|
||||
default List<TagLabel> getTags() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
default void setTags(List<TagLabel> tags) {}
|
||||
|
||||
@Override
|
||||
default Object getExtension() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
default void setExtension(Object extension) {}
|
||||
|
||||
@Override
|
||||
default Style getStyle() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
default void setStyle(Style style) {}
|
||||
|
||||
@Override
|
||||
default LifeCycle getLifeCycle() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
default void setLifeCycle(LifeCycle lifeCycle) {}
|
||||
|
||||
@Override
|
||||
default AssetCertification getCertification() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
default void setCertification(AssetCertification certification) {}
|
||||
|
||||
@Override
|
||||
default List<EntityReference> getExperts() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
default void setExperts(List<EntityReference> experts) {}
|
||||
|
||||
@Override
|
||||
default List<EntityReference> getChildren() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
default void setChildren(List<EntityReference> children) {}
|
||||
|
||||
@Override
|
||||
default ChangeDescription getIncrementalChangeDescription() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
default void setIncrementalChangeDescription(ChangeDescription changeDescription) {}
|
||||
|
||||
@Override
|
||||
default EntityReference getService() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright 2022 Collate
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.openmetadata.schema;
|
||||
|
||||
/**
|
||||
* Interface to be implemented by all services entities to provide a way to access all the common
|
||||
* fields.
|
||||
*/
|
||||
public interface ServiceConnectionEntityInterface {
|
||||
|
||||
Object getConfig();
|
||||
|
||||
void setConfig(Object config);
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2022 Collate
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.openmetadata.schema;
|
||||
|
||||
import java.util.List;
|
||||
import org.openmetadata.schema.entity.services.connections.TestConnectionResult;
|
||||
import org.openmetadata.schema.type.EntityReference;
|
||||
|
||||
/**
|
||||
* Interface to be implemented by all services entities to provide a way to access all the common
|
||||
* fields.
|
||||
*/
|
||||
public interface ServiceEntityInterface extends EntityInterface {
|
||||
|
||||
ServiceConnectionEntityInterface getConnection();
|
||||
|
||||
ServiceEntityInterface withOwners(List<EntityReference> owners);
|
||||
|
||||
void setPipelines(List<EntityReference> pipelines);
|
||||
|
||||
void setIngestionRunner(EntityReference ingestionRunner);
|
||||
|
||||
void setTestConnectionResult(TestConnectionResult testConnectionResult);
|
||||
|
||||
EnumInterface getServiceType();
|
||||
|
||||
default EntityReference getIngestionRunner() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2021 Collate
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.openmetadata.schema;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
public interface SubscriptionAction {
|
||||
default Set<String> getReceivers() {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
default Boolean getSendToAdmins() {
|
||||
return false;
|
||||
}
|
||||
|
||||
default Boolean getSendToOwners() {
|
||||
return false;
|
||||
}
|
||||
|
||||
default Boolean getSendToFollowers() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package org.openmetadata.schema;
|
||||
|
||||
import java.util.UUID;
|
||||
import org.openmetadata.schema.auth.TokenType;
|
||||
|
||||
public interface TokenInterface {
|
||||
UUID getToken();
|
||||
|
||||
UUID getUserId();
|
||||
|
||||
TokenType getTokenType();
|
||||
|
||||
Long getExpiryDate();
|
||||
|
||||
void setToken(UUID id);
|
||||
|
||||
void setUserId(UUID id);
|
||||
|
||||
void setTokenType(TokenType type);
|
||||
|
||||
void setExpiryDate(Long expiry);
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2026 Collate
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.openmetadata.schema.entity.ai;
|
||||
|
||||
/**
|
||||
* Common registration/approval milestones shared by {@code GovernanceMetadata} (AI Application) and
|
||||
* {@code McpGovernanceMetadata} (MCP Server), which are otherwise distinct generated types with no
|
||||
* shared supertype. Applied via {@code javaInterfaces} in the schema so the intake/approval logic
|
||||
* can stamp these fields once instead of duplicating it per governance-metadata type. The
|
||||
* {@code registrationStatus} enum is intentionally excluded — each type generates its own nested
|
||||
* enum with identical values.
|
||||
*/
|
||||
public interface AIGovernanceRegistration {
|
||||
String getRegisteredBy();
|
||||
|
||||
void setRegisteredBy(String registeredBy);
|
||||
|
||||
Long getRegisteredAt();
|
||||
|
||||
void setRegisteredAt(Long registeredAt);
|
||||
|
||||
String getApprovedBy();
|
||||
|
||||
void setApprovedBy(String approvedBy);
|
||||
|
||||
Long getApprovedAt();
|
||||
|
||||
void setApprovedAt(Long approvedAt);
|
||||
|
||||
String getApprovalComments();
|
||||
|
||||
void setApprovalComments(String approvalComments);
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2021 Collate
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.openmetadata.schema.exception;
|
||||
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import org.openmetadata.sdk.exception.WebServiceException;
|
||||
|
||||
public class JsonParsingException extends WebServiceException {
|
||||
private static final String MESSAGE = "JSON parsing failed with message [%s].";
|
||||
|
||||
public static final String JSON_PARSING_ERROR = "JSON_PARSING_EXCEPTION";
|
||||
|
||||
public JsonParsingException(String exceptionMessage) {
|
||||
super(
|
||||
Response.Status.INTERNAL_SERVER_ERROR,
|
||||
JSON_PARSING_ERROR,
|
||||
String.format(MESSAGE, exceptionMessage));
|
||||
}
|
||||
|
||||
public JsonParsingException(String exceptionMessage, Throwable cause) {
|
||||
super(
|
||||
Response.Status.INTERNAL_SERVER_ERROR,
|
||||
JSON_PARSING_ERROR,
|
||||
String.format(MESSAGE, exceptionMessage),
|
||||
cause);
|
||||
}
|
||||
}
|
||||
+141
@@ -0,0 +1,141 @@
|
||||
package org.openmetadata.schema.governance.workflows.elements;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonSubTypes;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.openmetadata.common.utils.CommonUtil;
|
||||
import org.openmetadata.schema.governance.workflows.elements.nodes.automatedTask.ApplyRecognizerFeedbackTaskDefinition;
|
||||
import org.openmetadata.schema.governance.workflows.elements.nodes.automatedTask.CheckChangeDescriptionTaskDefinition;
|
||||
import org.openmetadata.schema.governance.workflows.elements.nodes.automatedTask.CheckEntityAttributesTaskDefinition;
|
||||
import org.openmetadata.schema.governance.workflows.elements.nodes.automatedTask.CreateAndRunAIAutomationTaskDefinition;
|
||||
import org.openmetadata.schema.governance.workflows.elements.nodes.automatedTask.CreateAndRunIngestionPipelineTaskDefinition;
|
||||
import org.openmetadata.schema.governance.workflows.elements.nodes.automatedTask.DataCompletenessTaskDefinition;
|
||||
import org.openmetadata.schema.governance.workflows.elements.nodes.automatedTask.PolicyAgentTaskDefinition;
|
||||
import org.openmetadata.schema.governance.workflows.elements.nodes.automatedTask.RejectRecognizerFeedbackTaskDefinition;
|
||||
import org.openmetadata.schema.governance.workflows.elements.nodes.automatedTask.RollbackEntityTaskDefinition;
|
||||
import org.openmetadata.schema.governance.workflows.elements.nodes.automatedTask.RunAppTaskDefinition;
|
||||
import org.openmetadata.schema.governance.workflows.elements.nodes.automatedTask.SetEntityAttributeTaskDefinition;
|
||||
import org.openmetadata.schema.governance.workflows.elements.nodes.automatedTask.SetEntityCertificationTaskDefinition;
|
||||
import org.openmetadata.schema.governance.workflows.elements.nodes.automatedTask.SetGlossaryTermStatusTaskDefinition;
|
||||
import org.openmetadata.schema.governance.workflows.elements.nodes.automatedTask.SinkTaskDefinition;
|
||||
import org.openmetadata.schema.governance.workflows.elements.nodes.endEvent.EndEventDefinition;
|
||||
import org.openmetadata.schema.governance.workflows.elements.nodes.gateway.ParallelGatewayDefinition;
|
||||
import org.openmetadata.schema.governance.workflows.elements.nodes.startEvent.StartEventDefinition;
|
||||
import org.openmetadata.schema.governance.workflows.elements.nodes.userTask.CreateRecognizerFeedbackApprovalTaskDefinition;
|
||||
import org.openmetadata.schema.governance.workflows.elements.nodes.userTask.UserApprovalTaskDefinition;
|
||||
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "subType")
|
||||
@JsonSubTypes({
|
||||
@JsonSubTypes.Type(
|
||||
value = CheckEntityAttributesTaskDefinition.class,
|
||||
name = "checkEntityAttributesTask"),
|
||||
@JsonSubTypes.Type(
|
||||
value = CheckChangeDescriptionTaskDefinition.class,
|
||||
name = "checkChangeDescriptionTask"),
|
||||
@JsonSubTypes.Type(
|
||||
value = SetEntityCertificationTaskDefinition.class,
|
||||
name = "setEntityCertificationTask"),
|
||||
@JsonSubTypes.Type(
|
||||
value = SetEntityAttributeTaskDefinition.class,
|
||||
name = "setEntityAttributeTask"),
|
||||
@JsonSubTypes.Type(value = RollbackEntityTaskDefinition.class, name = "rollbackEntityTask"),
|
||||
@JsonSubTypes.Type(value = DataCompletenessTaskDefinition.class, name = "dataCompletenessTask"),
|
||||
@JsonSubTypes.Type(value = StartEventDefinition.class, name = "startEvent"),
|
||||
@JsonSubTypes.Type(value = EndEventDefinition.class, name = "endEvent"),
|
||||
@JsonSubTypes.Type(
|
||||
value = SetGlossaryTermStatusTaskDefinition.class,
|
||||
name = "setGlossaryTermStatusTask"),
|
||||
@JsonSubTypes.Type(value = UserApprovalTaskDefinition.class, name = "userApprovalTask"),
|
||||
@JsonSubTypes.Type(
|
||||
value = CreateAndRunIngestionPipelineTaskDefinition.class,
|
||||
name = "createAndRunIngestionPipelineTask"),
|
||||
@JsonSubTypes.Type(
|
||||
value = CreateAndRunAIAutomationTaskDefinition.class,
|
||||
name = "createAndRunAIAutomationTask"),
|
||||
@JsonSubTypes.Type(value = RunAppTaskDefinition.class, name = "runAppTask"),
|
||||
@JsonSubTypes.Type(value = ParallelGatewayDefinition.class, name = "parallelGateway"),
|
||||
@JsonSubTypes.Type(value = SinkTaskDefinition.class, name = "sinkTask"),
|
||||
@JsonSubTypes.Type(
|
||||
value = CreateRecognizerFeedbackApprovalTaskDefinition.class,
|
||||
name = "createRecognizerFeedbackApprovalTask"),
|
||||
@JsonSubTypes.Type(
|
||||
value = ApplyRecognizerFeedbackTaskDefinition.class,
|
||||
name = "applyRecognizerFeedbackTask"),
|
||||
@JsonSubTypes.Type(
|
||||
value = RejectRecognizerFeedbackTaskDefinition.class,
|
||||
name = "rejectRecognizerFeedbackTask"),
|
||||
@JsonSubTypes.Type(value = PolicyAgentTaskDefinition.class, name = "policyAgentTask")
|
||||
})
|
||||
public interface WorkflowNodeDefinitionInterface {
|
||||
String getType();
|
||||
|
||||
String getSubType();
|
||||
|
||||
String getName();
|
||||
|
||||
String getDisplayName();
|
||||
|
||||
String getDescription();
|
||||
|
||||
default Object getConfig() {
|
||||
return null;
|
||||
}
|
||||
;
|
||||
|
||||
default List<String> getInput() {
|
||||
return null;
|
||||
}
|
||||
;
|
||||
|
||||
default List<String> getOutput() {
|
||||
return null;
|
||||
}
|
||||
;
|
||||
|
||||
default Object getInputNamespaceMap() {
|
||||
return null;
|
||||
}
|
||||
|
||||
void setType(String type);
|
||||
|
||||
void setSubType(String subType);
|
||||
|
||||
void setName(String name);
|
||||
|
||||
void setDisplayName(String displayName);
|
||||
|
||||
void setDescription(String description);
|
||||
|
||||
default void setConfig(Map<String, Object> config) {
|
||||
/* no-op implementation to be overridden */
|
||||
}
|
||||
|
||||
default void setInput(List<String> inputs) {
|
||||
/* no-op implementation to be overridden */
|
||||
}
|
||||
|
||||
default void setOutput(List<String> outputs) {
|
||||
/* no-op implementation to be overridden */
|
||||
}
|
||||
|
||||
default void setInputNamespaceMap(Object inputNamespaceMap) {
|
||||
/* no-op implementation to be overridden */
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
default String getNodeDisplayName() {
|
||||
return CommonUtil.nullOrEmpty(getDisplayName()) ? getName() : getDisplayName();
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
default NodeType getNodeType() {
|
||||
return NodeType.fromValue(getType());
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
default NodeSubType getNodeSubType() {
|
||||
return NodeSubType.fromValue(getSubType());
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
package org.openmetadata.schema.governance.workflows.elements;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonSubTypes;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import java.util.Set;
|
||||
import org.openmetadata.schema.governance.workflows.elements.triggers.EventBasedEntityTriggerDefinition;
|
||||
import org.openmetadata.schema.governance.workflows.elements.triggers.NoOpTriggerDefinition;
|
||||
import org.openmetadata.schema.governance.workflows.elements.triggers.PeriodicBatchEntityTriggerDefinition;
|
||||
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
|
||||
@JsonSubTypes({
|
||||
@JsonSubTypes.Type(value = EventBasedEntityTriggerDefinition.class, name = "eventBasedEntity"),
|
||||
@JsonSubTypes.Type(value = NoOpTriggerDefinition.class, name = "noOp"),
|
||||
@JsonSubTypes.Type(
|
||||
value = PeriodicBatchEntityTriggerDefinition.class,
|
||||
name = "periodicBatchEntity"),
|
||||
})
|
||||
public interface WorkflowTriggerInterface {
|
||||
// TODO If set as enum, it results in null when the JSON is deserialized.
|
||||
// Maybe there can be another way to validate it on deserialization.
|
||||
String getType();
|
||||
|
||||
Object getConfig();
|
||||
|
||||
Set<String> getOutput();
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package org.openmetadata.schema.utils;
|
||||
|
||||
public final class EntityInterfaceUtil {
|
||||
/** Adds quotes to name as required */
|
||||
// TODO change this FullyQualifiedName
|
||||
public static String quoteName(String name) {
|
||||
if (name != null && !name.contains("\"")) {
|
||||
return name.contains(".") ? "\"" + name + "\"" : name;
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
/** Inverse of {@link #quoteName(String)}: strips a single enclosing pair of quotes if present. */
|
||||
public static String unquoteName(String name) {
|
||||
if (name != null && name.length() > 1 && name.startsWith("\"") && name.endsWith("\"")) {
|
||||
return name.substring(1, name.length() - 1);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,223 @@
|
||||
/*
|
||||
* Copyright 2021 Collate
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.openmetadata.schema.utils;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Base64;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
import org.openmetadata.schema.system.EntityError;
|
||||
import org.openmetadata.schema.type.Paging;
|
||||
|
||||
/**
|
||||
* Class used for generating JSON response for APIs returning list of objects in the following format: { "data" : [ {
|
||||
* json for object 1}, {json for object 2}, ... ] }
|
||||
*/
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
@JsonPropertyOrder({"data"})
|
||||
public class ResultList<T> {
|
||||
|
||||
@JsonProperty("data")
|
||||
@NotNull
|
||||
private List<T> data;
|
||||
|
||||
@JsonProperty("paging")
|
||||
private Paging paging;
|
||||
|
||||
@JsonProperty("errors")
|
||||
private List<EntityError> errors;
|
||||
|
||||
@JsonProperty("warningsCount")
|
||||
private Integer warningsCount;
|
||||
|
||||
/**
|
||||
* Records read but not indexed for a non-failure reason (e.g. stale-relationship orphans).
|
||||
* Carried separately from {@link #errors} so callers can surface them as warnings — and record
|
||||
* them — without failing the batch.
|
||||
*/
|
||||
@JsonProperty("warnings")
|
||||
private List<EntityError> warnings;
|
||||
|
||||
public ResultList() {}
|
||||
|
||||
public ResultList(List<T> data) {
|
||||
this.data = data;
|
||||
this.paging = null;
|
||||
this.errors = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cursor functionality. User has request 'limit' number of entries. The data provided must have 'limit' + 1 number of
|
||||
* entries.
|
||||
*
|
||||
* <p>--------------------------------------------------------------------------------------------------------------
|
||||
* Consider forward scrolling:
|
||||
* --------------------------------------------------------------------------------------------------------------
|
||||
* Query GET .../entities?limit=pagesize CASE 0: No before or after parameters in the query Returns: page1
|
||||
* beforeCursor = null, -> Indicates first page afterCursor = last record in page1
|
||||
*
|
||||
* <p>Query GET .../entities?limit=pagesize&after={last record in page1} Returns: page2 beforeCursor = first record in
|
||||
* page2 afterCursor = last record in page2
|
||||
*
|
||||
* <p>Query GET .../entities?limit=pagesize&after={last record in page2} CASE 1: Page 3 has less than limit number of
|
||||
* entries and hence partial page is returned Returns: partial page 3 beforeCursor = first record in page3 afterCursor
|
||||
* = null -------- FORWARD SCROLLING ENDS -------------
|
||||
*
|
||||
* <p>CASE 2: Page 3 has exactly page number of entries and not entries to follow after Returns: page3 beforeCursor =
|
||||
* first record in page3 afterCursor = null -------- FORWARD SCROLLING ENDS -------------
|
||||
*
|
||||
* <p>--------------------------------------------------------------------------------------------------------------
|
||||
* Consider backward scrolling from the previous state:
|
||||
* --------------------------------------------------------------------------------------------------------------
|
||||
*
|
||||
* <p>Query GET .../entities?limit=pagesize&before={last record in page2 + 1} Returns: page2 beforeCursor = first
|
||||
* record in page2 afterCursor = last record in page2
|
||||
*
|
||||
* <p>Query GET .../entities?limit=pagesize&before={first record page 2} CASE 3: Page 1 does not have {@code limit}
|
||||
* number entries and hence partial page is returned Returns: page1 beforeCursor = null afterCursor = last record in
|
||||
* page1 -------- BACKWARD SCROLLING ENDS -------------
|
||||
*
|
||||
* <p>CASE 4: Page 1 has exactly page number of entries Returns: page1 beforeCursor = null afterCursor = Empty string
|
||||
* to start at page1 -------- BACKWARD SCROLLING ENDS -------------
|
||||
*/
|
||||
public ResultList(List<T> data, String beforeCursor, String afterCursor, int total) {
|
||||
this.data = data;
|
||||
paging =
|
||||
new Paging()
|
||||
.withBefore(
|
||||
beforeCursor == null
|
||||
? null
|
||||
: Base64.getUrlEncoder()
|
||||
.encodeToString(beforeCursor.getBytes(StandardCharsets.UTF_8)))
|
||||
.withAfter(
|
||||
afterCursor == null
|
||||
? null
|
||||
: Base64.getUrlEncoder()
|
||||
.encodeToString(afterCursor.getBytes(StandardCharsets.UTF_8)))
|
||||
.withTotal(total);
|
||||
}
|
||||
|
||||
public ResultList(List<T> data, Integer offset, int total) {
|
||||
this.data = data;
|
||||
paging = new Paging().withBefore(null).withAfter(null).withTotal(total).withOffset(offset);
|
||||
}
|
||||
|
||||
/* Conveniently map the data to another type without the need to create a new ResultList */
|
||||
public <S> ResultList<S> map(Function<T, S> mapper) {
|
||||
return new ResultList<>(data.stream().map(mapper).collect(Collectors.toList()), paging);
|
||||
}
|
||||
|
||||
/* Conveniently filter the data without the need to create a new ResultList */
|
||||
public ResultList<T> filter(Predicate<T> predicate) {
|
||||
return new ResultList<>(data.stream().filter(predicate).collect(Collectors.toList()), paging);
|
||||
}
|
||||
|
||||
public ResultList(List<T> data, Integer offset, Integer limit, Integer total) {
|
||||
this.data = data;
|
||||
paging =
|
||||
new Paging()
|
||||
.withBefore(null)
|
||||
.withAfter(null)
|
||||
.withTotal(total)
|
||||
.withOffset(offset)
|
||||
.withLimit(limit);
|
||||
}
|
||||
|
||||
public ResultList(List<T> data, Paging other) {
|
||||
this.data = data;
|
||||
paging =
|
||||
new Paging()
|
||||
.withBefore(null)
|
||||
.withAfter(null)
|
||||
.withTotal(other.getTotal())
|
||||
.withOffset(other.getOffset())
|
||||
.withLimit(other.getLimit());
|
||||
}
|
||||
|
||||
public ResultList(
|
||||
List<T> data, List<EntityError> errors, String beforeCursor, String afterCursor, int total) {
|
||||
this.data = data;
|
||||
this.errors = errors;
|
||||
paging =
|
||||
new Paging()
|
||||
.withBefore(
|
||||
beforeCursor == null
|
||||
? null
|
||||
: Base64.getUrlEncoder()
|
||||
.encodeToString(beforeCursor.getBytes(StandardCharsets.UTF_8)))
|
||||
.withAfter(
|
||||
afterCursor == null
|
||||
? null
|
||||
: Base64.getUrlEncoder()
|
||||
.encodeToString(afterCursor.getBytes(StandardCharsets.UTF_8)))
|
||||
.withTotal(total);
|
||||
}
|
||||
|
||||
@JsonProperty("data")
|
||||
public List<T> getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
@JsonProperty("data")
|
||||
public void setData(List<T> data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
@JsonProperty("errors")
|
||||
public List<EntityError> getErrors() {
|
||||
return errors;
|
||||
}
|
||||
|
||||
@JsonProperty("errors")
|
||||
public void setErrors(List<EntityError> data) {
|
||||
this.errors = data;
|
||||
}
|
||||
|
||||
@JsonProperty("paging")
|
||||
public Paging getPaging() {
|
||||
return paging;
|
||||
}
|
||||
|
||||
@JsonProperty("paging")
|
||||
public ResultList<T> setPaging(Paging paging) {
|
||||
this.paging = paging;
|
||||
return this;
|
||||
}
|
||||
|
||||
@JsonProperty("warningsCount")
|
||||
public Integer getWarningsCount() {
|
||||
return warningsCount;
|
||||
}
|
||||
|
||||
@JsonProperty("warningsCount")
|
||||
public void setWarningsCount(Integer warningsCount) {
|
||||
this.warningsCount = warningsCount;
|
||||
}
|
||||
|
||||
@JsonProperty("warnings")
|
||||
public List<EntityError> getWarnings() {
|
||||
return warnings;
|
||||
}
|
||||
|
||||
@JsonProperty("warnings")
|
||||
public void setWarnings(List<EntityError> warnings) {
|
||||
this.warnings = warnings;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package org.openmetadata.schema.utils;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.Properties;
|
||||
import java.util.regex.Pattern;
|
||||
import org.openmetadata.schema.api.OpenMetadataServerVersion;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public final class VersionUtils {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(VersionUtils.class);
|
||||
|
||||
private VersionUtils() {}
|
||||
|
||||
public static OpenMetadataServerVersion getOpenMetadataServerVersion(String resourceName) {
|
||||
OpenMetadataServerVersion version = new OpenMetadataServerVersion();
|
||||
try {
|
||||
InputStream fileInput = VersionUtils.class.getResourceAsStream(resourceName);
|
||||
Properties props = new Properties();
|
||||
props.load(fileInput);
|
||||
version.setVersion(props.getProperty("version", "unknown"));
|
||||
version.setRevision(props.getProperty("revision", "unknown"));
|
||||
|
||||
String timestampAsString = props.getProperty("timestamp");
|
||||
Long timestamp = timestampAsString != null ? Long.valueOf(timestampAsString) : null;
|
||||
version.setTimestamp(timestamp);
|
||||
} catch (Exception ie) {
|
||||
LOG.warn("Failed to read catalog version file");
|
||||
}
|
||||
return version;
|
||||
}
|
||||
|
||||
public static String[] getVersionFromString(String input) {
|
||||
return input.split(Pattern.quote("."));
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the {@code MAJOR.MINOR} portion of a version string, ignoring the patch level and any
|
||||
* qualifier such as {@code -SNAPSHOT}. Two versions sharing the same major/minor differ only by a
|
||||
* patch release (e.g. {@code 1.12.8} and {@code 1.12.9} both yield {@code 1.12}).
|
||||
*/
|
||||
public static String getMajorMinorVersion(String version) {
|
||||
String majorMinor = version;
|
||||
if (version != null) {
|
||||
String[] parts = getVersionFromString(version);
|
||||
if (parts.length >= 2) {
|
||||
majorMinor = parts[0] + "." + parts[1];
|
||||
}
|
||||
}
|
||||
return majorMinor;
|
||||
}
|
||||
}
|
||||
+149
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
* Copyright 2021 Collate
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.openmetadata.sdk;
|
||||
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import java.net.URL;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.openmetadata.schema.ServiceEntityInterface;
|
||||
import org.openmetadata.schema.entity.app.App;
|
||||
import org.openmetadata.schema.entity.app.AppMarketPlaceDefinition;
|
||||
import org.openmetadata.schema.entity.automations.Workflow;
|
||||
import org.openmetadata.schema.entity.services.ingestionPipelines.IngestionPipeline;
|
||||
import org.openmetadata.schema.entity.services.ingestionPipelines.PipelineServiceClientResponse;
|
||||
import org.openmetadata.schema.entity.services.ingestionPipelines.PipelineStatus;
|
||||
import org.openmetadata.schema.entity.services.ingestionPipelines.PipelineType;
|
||||
|
||||
/**
|
||||
* Client to make API calls to add, deleted, and deploy pipelines on a PipelineService, such as
|
||||
* Airflow. Core abstractions are as follows:
|
||||
*
|
||||
* <ul>
|
||||
* <li>A PipelineService is a service such as AirFlow to which a pipeline can be deployed
|
||||
* <li>A Pipeline is a workflow for performing certain tasks. Example - ingestion pipeline is a
|
||||
* workflow that connects to a database service or other services and collect metadata.
|
||||
* <li>Pipeline uses `Connection` to a service as dependency. A Pipeline might need to connection
|
||||
* to database service to collect metadata, OpenMetadata to user metadata over APIs, etc.
|
||||
* </ul>
|
||||
*/
|
||||
public interface PipelineServiceClientInterface {
|
||||
String HEALTHY_STATUS = "healthy";
|
||||
String UNHEALTHY_STATUS = "unhealthy";
|
||||
String STATUS_KEY = "status";
|
||||
String APP_TRIGGER = "run_application";
|
||||
|
||||
String DEPLOYMENT_ERROR = "DEPLOYMENT_ERROR";
|
||||
String TRIGGER_ERROR = "TRIGGER_ERROR";
|
||||
Map<String, String> TYPE_TO_TASK =
|
||||
Map.of(
|
||||
PipelineType.METADATA.toString(),
|
||||
"ingestion_task",
|
||||
PipelineType.PROFILER.toString(),
|
||||
"profiler_task",
|
||||
PipelineType.AUTO_CLASSIFICATION.toString(),
|
||||
"auto_classification_task",
|
||||
PipelineType.LINEAGE.toString(),
|
||||
"lineage_task",
|
||||
PipelineType.DBT.toString(),
|
||||
"dbt_task",
|
||||
PipelineType.USAGE.toString(),
|
||||
"usage_task",
|
||||
PipelineType.TEST_SUITE.toString(),
|
||||
"test_suite_task",
|
||||
PipelineType.DATA_INSIGHT.toString(),
|
||||
"data_insight_task",
|
||||
PipelineType.APPLICATION.toString(),
|
||||
"application_task");
|
||||
|
||||
URL validateServiceURL(String serviceURL);
|
||||
|
||||
String getBasicAuthenticationHeader(String username, String password);
|
||||
|
||||
Boolean validServerClientVersions(String clientVersion, String serverVersion);
|
||||
|
||||
Response getHostIp();
|
||||
|
||||
/**
|
||||
* Check the pipeline service status with an exception backoff to make sure we don't raise any
|
||||
* false positives.
|
||||
*/
|
||||
String getServiceStatusBackoff();
|
||||
|
||||
/* Check the status of pipeline service to ensure it is healthy */
|
||||
PipelineServiceClientResponse getServiceStatus();
|
||||
|
||||
List<PipelineStatus> getQueuedPipelineStatus(IngestionPipeline ingestionPipeline);
|
||||
|
||||
/**
|
||||
* This workflow can be used to execute any necessary async automations from the pipeline service.
|
||||
* This will be the new Test Connection endpoint. The UI can create a new workflow and trigger it
|
||||
* in the server, and keep polling the results.
|
||||
*/
|
||||
PipelineServiceClientResponse runAutomationsWorkflow(Workflow workflow);
|
||||
|
||||
PipelineServiceClientResponse runApplicationFlow(App application);
|
||||
|
||||
PipelineServiceClientResponse validateAppRegistration(AppMarketPlaceDefinition app);
|
||||
|
||||
/* Deploy a pipeline to the pipeline service */
|
||||
PipelineServiceClientResponse deployPipeline(
|
||||
IngestionPipeline ingestionPipeline, ServiceEntityInterface service);
|
||||
|
||||
/* Deploy run the pipeline at the pipeline service */
|
||||
PipelineServiceClientResponse runPipeline(
|
||||
IngestionPipeline ingestionPipeline, ServiceEntityInterface service);
|
||||
|
||||
/* Deploy run the pipeline at the pipeline service with ad-hoc custom configuration.
|
||||
* This might not be supported by some pipeline service clients.*/
|
||||
default PipelineServiceClientResponse runPipeline(
|
||||
IngestionPipeline ingestionPipeline,
|
||||
ServiceEntityInterface service,
|
||||
Map<String, Object> config) {
|
||||
throw new UnsupportedOperationException(
|
||||
"This operation is not supported by this pipeline service");
|
||||
}
|
||||
|
||||
/* Stop and delete a pipeline at the pipeline service */
|
||||
PipelineServiceClientResponse deletePipeline(IngestionPipeline ingestionPipeline);
|
||||
|
||||
/* Get the status of a deployed pipeline */
|
||||
List<PipelineStatus> getQueuedPipelineStatusInternal(IngestionPipeline ingestionPipeline);
|
||||
|
||||
/* Toggle the state of an Ingestion Pipeline as enabled/disabled */
|
||||
PipelineServiceClientResponse toggleIngestion(IngestionPipeline ingestionPipeline);
|
||||
|
||||
/* Get the all last run logs of a deployed pipeline */
|
||||
Map<String, String> getLastIngestionLogs(IngestionPipeline ingestionPipeline, String after);
|
||||
|
||||
/* Get logs for a specific pipeline run identified by runId.
|
||||
* When runId is null or blank, falls back to getLastIngestionLogs (latest run). */
|
||||
default Map<String, String> getIngestionLogs(
|
||||
IngestionPipeline ingestionPipeline, String after, String runId) {
|
||||
return getLastIngestionLogs(ingestionPipeline, after);
|
||||
}
|
||||
|
||||
/* Get the all last run logs of a deployed pipeline */
|
||||
PipelineServiceClientResponse killIngestion(IngestionPipeline ingestionPipeline);
|
||||
|
||||
/* Stop a specific run of a deployed pipeline identified by its run ID.
|
||||
* Default is a no-op: clients that do not support per-run stopping return success without
|
||||
* taking any action. The DB status is already marked STOPPED before this is called. */
|
||||
default PipelineServiceClientResponse killIngestionRun(
|
||||
IngestionPipeline ingestionPipeline, String runId) {
|
||||
return new PipelineServiceClientResponse().withCode(200).withPlatform(getPlatform());
|
||||
}
|
||||
|
||||
String getPlatform();
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package org.openmetadata.sdk.exception;
|
||||
|
||||
import jakarta.ws.rs.core.Response;
|
||||
|
||||
public class AssetServiceException extends WebServiceException {
|
||||
private static final String BY_NAME_MESSAGE = "AssetService Exception [%s] due to [%s].";
|
||||
private static final String ERROR_TYPE = "ASSET_SERVICE_ERROR";
|
||||
|
||||
public AssetServiceException(String message) {
|
||||
super(Response.Status.BAD_REQUEST, ERROR_TYPE, message);
|
||||
}
|
||||
|
||||
public AssetServiceException(Response.Status status, String message) {
|
||||
super(status, ERROR_TYPE, message);
|
||||
}
|
||||
|
||||
public static AssetServiceException byMessage(
|
||||
String name, String errorMessage, Response.Status status) {
|
||||
return new AssetServiceException(status, buildMessageByName(name, errorMessage));
|
||||
}
|
||||
|
||||
public static AssetServiceException byMessage(String name, String errorMessage) {
|
||||
return new AssetServiceException(
|
||||
Response.Status.BAD_REQUEST, buildMessageByName(name, errorMessage));
|
||||
}
|
||||
|
||||
private static String buildMessageByName(String name, String errorMessage) {
|
||||
return String.format(BY_NAME_MESSAGE, name, errorMessage);
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package org.openmetadata.sdk.exception;
|
||||
|
||||
import jakarta.ws.rs.core.Response;
|
||||
|
||||
public class AttachmentException extends WebServiceException {
|
||||
private static final String BY_NAME_MESSAGE = "Attachment Exception [%s] due to [%s].";
|
||||
private static final String ERROR_TYPE = "ATTACHMENT_ERROR";
|
||||
|
||||
public AttachmentException(String message) {
|
||||
super(Response.Status.BAD_REQUEST, ERROR_TYPE, message);
|
||||
}
|
||||
|
||||
public AttachmentException(Response.Status status, String message) {
|
||||
super(status, ERROR_TYPE, message);
|
||||
}
|
||||
|
||||
public static AttachmentException byMessage(
|
||||
String name, String errorMessage, Response.Status status) {
|
||||
return new AttachmentException(status, buildMessageByName(name, errorMessage));
|
||||
}
|
||||
|
||||
public static AttachmentException byMessage(String name, String errorMessage) {
|
||||
return new AttachmentException(
|
||||
Response.Status.BAD_REQUEST, buildMessageByName(name, errorMessage));
|
||||
}
|
||||
|
||||
private static String buildMessageByName(String name, String errorMessage) {
|
||||
return String.format(BY_NAME_MESSAGE, name, errorMessage);
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package org.openmetadata.sdk.exception;
|
||||
|
||||
import jakarta.ws.rs.core.Response;
|
||||
|
||||
public class CSVExportException extends WebServiceException {
|
||||
private static final String BY_NAME_MESSAGE = "CSVExport Exception [%s] due to [%s].";
|
||||
private static final String ERROR_TYPE = "CSV_EXPORT_ERROR";
|
||||
|
||||
public CSVExportException(String message) {
|
||||
super(Response.Status.BAD_REQUEST, ERROR_TYPE, message);
|
||||
}
|
||||
|
||||
public CSVExportException(Response.Status status, String message) {
|
||||
super(status, ERROR_TYPE, message);
|
||||
}
|
||||
|
||||
public static CSVExportException byMessage(
|
||||
String name, String errorMessage, Response.Status status) {
|
||||
return new CSVExportException(status, buildMessageByName(name, errorMessage));
|
||||
}
|
||||
|
||||
public static CSVExportException byMessage(String name, String errorMessage) {
|
||||
return new CSVExportException(
|
||||
Response.Status.BAD_REQUEST, buildMessageByName(name, errorMessage));
|
||||
}
|
||||
|
||||
private static String buildMessageByName(String name, String errorMessage) {
|
||||
return String.format(BY_NAME_MESSAGE, name, errorMessage);
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package org.openmetadata.sdk.exception;
|
||||
|
||||
import jakarta.ws.rs.core.Response;
|
||||
|
||||
public class EntitySpecViolationException extends WebServiceException {
|
||||
private static final String BY_NAME_MESSAGE = "Entity Spec Violation [%s] due to [%s].";
|
||||
private static final String ERROR_TYPE = "ENTITY_SPEC_VIOLATION";
|
||||
|
||||
public EntitySpecViolationException(String message) {
|
||||
super(Response.Status.BAD_REQUEST, ERROR_TYPE, message);
|
||||
}
|
||||
|
||||
public EntitySpecViolationException(Response.Status status, String message) {
|
||||
super(status, ERROR_TYPE, message);
|
||||
}
|
||||
|
||||
public static EntitySpecViolationException byMessage(
|
||||
String name, String errorMessage, Response.Status status) {
|
||||
return new EntitySpecViolationException(status, buildMessageByName(name, errorMessage));
|
||||
}
|
||||
|
||||
public static EntitySpecViolationException byMessage(String name, String errorMessage) {
|
||||
return new EntitySpecViolationException(
|
||||
Response.Status.BAD_REQUEST, buildMessageByName(name, errorMessage));
|
||||
}
|
||||
|
||||
private static String buildMessageByName(String name, String errorMessage) {
|
||||
return String.format(BY_NAME_MESSAGE, name, errorMessage);
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package org.openmetadata.sdk.exception;
|
||||
|
||||
import jakarta.ws.rs.core.Response;
|
||||
|
||||
public class EntityUpdateException extends WebServiceException {
|
||||
private static final String BY_NAME_MESSAGE = "Entity Update Exception [%s] due to [%s].";
|
||||
private static final String ERROR_TYPE = "ENTITY_UPDATE_EXCEPTION";
|
||||
|
||||
public EntityUpdateException(String message) {
|
||||
super(Response.Status.BAD_REQUEST, ERROR_TYPE, message);
|
||||
}
|
||||
|
||||
public EntityUpdateException(Response.Status status, String message) {
|
||||
super(status, ERROR_TYPE, message);
|
||||
}
|
||||
|
||||
public static EntityUpdateException byMessage(
|
||||
String name, String errorMessage, Response.Status status) {
|
||||
return new EntityUpdateException(status, buildMessageByName(name, errorMessage));
|
||||
}
|
||||
|
||||
public static EntityUpdateException byMessage(String name, String errorMessage) {
|
||||
return new EntityUpdateException(
|
||||
Response.Status.BAD_REQUEST, buildMessageByName(name, errorMessage));
|
||||
}
|
||||
|
||||
private static String buildMessageByName(String name, String errorMessage) {
|
||||
return String.format(BY_NAME_MESSAGE, name, errorMessage);
|
||||
}
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2021 Collate
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.openmetadata.sdk.exception;
|
||||
|
||||
import jakarta.ws.rs.core.Response;
|
||||
|
||||
public class PipelineServiceClientException extends WebServiceException {
|
||||
private static final String BY_NAME_MESSAGE = "Pipeline Exception [%s] due to [%s].";
|
||||
private static final String ERROR_TYPE = "PIPELINE_SERVICE_ERROR";
|
||||
|
||||
public PipelineServiceClientException(String message) {
|
||||
super(Response.Status.BAD_REQUEST, ERROR_TYPE, message);
|
||||
}
|
||||
|
||||
private PipelineServiceClientException(Response.Status status, String message) {
|
||||
super(status, ERROR_TYPE, message);
|
||||
}
|
||||
|
||||
public static PipelineServiceClientException byMessage(
|
||||
String name, String errorMessage, Response.Status status) {
|
||||
return new PipelineServiceClientException(status, buildMessageByName(name, errorMessage));
|
||||
}
|
||||
|
||||
public static PipelineServiceClientException byMessage(String name, String errorMessage) {
|
||||
return new PipelineServiceClientException(
|
||||
Response.Status.BAD_REQUEST, buildMessageByName(name, errorMessage));
|
||||
}
|
||||
|
||||
private static String buildMessageByName(String name, String errorMessage) {
|
||||
return String.format(BY_NAME_MESSAGE, name, errorMessage);
|
||||
}
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2021 Collate
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.openmetadata.sdk.exception;
|
||||
|
||||
import jakarta.ws.rs.core.Response;
|
||||
|
||||
public class PipelineServiceVersionException extends WebServiceException {
|
||||
|
||||
private static final String BY_NAME_MESSAGE =
|
||||
"Pipeline Service [%s] Version mismatch due to [%s].";
|
||||
private static final String ERROR_TYPE = "PIPELINE_SERVICE_VERSION_MISMATCH";
|
||||
|
||||
public PipelineServiceVersionException(String message) {
|
||||
super(Response.Status.INTERNAL_SERVER_ERROR, ERROR_TYPE, message);
|
||||
}
|
||||
|
||||
private PipelineServiceVersionException(Response.Status status, String message) {
|
||||
super(status, ERROR_TYPE, message);
|
||||
}
|
||||
|
||||
public static PipelineServiceVersionException byMessage(
|
||||
String name, String errorMessage, Response.Status status) {
|
||||
return new PipelineServiceVersionException(status, buildMessageByName(name, errorMessage));
|
||||
}
|
||||
|
||||
public static PipelineServiceVersionException byMessage(String name, String errorMessage) {
|
||||
return new PipelineServiceVersionException(
|
||||
Response.Status.INTERNAL_SERVER_ERROR, buildMessageByName(name, errorMessage));
|
||||
}
|
||||
|
||||
public static String buildMessageByName(String name, String errorMessage) {
|
||||
return String.format(BY_NAME_MESSAGE, name, errorMessage);
|
||||
}
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package org.openmetadata.sdk.exception;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public class SchemaProcessingException extends Exception {
|
||||
public enum ErrorType {
|
||||
RESOURCE_NOT_FOUND,
|
||||
UNSUPPORTED_URL,
|
||||
OTHER
|
||||
}
|
||||
|
||||
private final ErrorType errorType;
|
||||
|
||||
public SchemaProcessingException(String message, ErrorType errorType) {
|
||||
super(message);
|
||||
this.errorType = errorType;
|
||||
}
|
||||
|
||||
public SchemaProcessingException(String message, Throwable cause, ErrorType errorType) {
|
||||
super(message, cause);
|
||||
this.errorType = errorType;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package org.openmetadata.sdk.exception;
|
||||
|
||||
import jakarta.ws.rs.core.Response;
|
||||
|
||||
public class SearchException extends WebServiceException {
|
||||
private static final String BY_NAME_MESSAGE =
|
||||
"Search Index Not Found Exception [%s] due to [%s].";
|
||||
private static final String ERROR_TYPE = "SEARCH_ERROR";
|
||||
|
||||
public SearchException(String message) {
|
||||
super(Response.Status.INTERNAL_SERVER_ERROR, ERROR_TYPE, message);
|
||||
}
|
||||
|
||||
private SearchException(Response.Status status, String message) {
|
||||
super(status, ERROR_TYPE, message);
|
||||
}
|
||||
|
||||
public static SearchException byMessage(
|
||||
String name, String errorMessage, Response.Status status) {
|
||||
return new SearchException(status, buildMessageByName(name, errorMessage));
|
||||
}
|
||||
|
||||
public static SearchException byMessage(String name, String errorMessage) {
|
||||
return new SearchException(Response.Status.BAD_REQUEST, buildMessageByName(name, errorMessage));
|
||||
}
|
||||
|
||||
private static String buildMessageByName(String name, String errorMessage) {
|
||||
return String.format(BY_NAME_MESSAGE, name, errorMessage);
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
package org.openmetadata.sdk.exception;
|
||||
|
||||
import jakarta.ws.rs.core.Response;
|
||||
|
||||
public class SearchIndexNotFoundException extends WebServiceException {
|
||||
private static final String BY_NAME_MESSAGE =
|
||||
"Search Index Not Found Exception [%s] due to [%s].";
|
||||
private static final String ERROR_TYPE = "SEARCH_INDEX_NOT_FOUND";
|
||||
|
||||
public SearchIndexNotFoundException(String message) {
|
||||
super(Response.Status.INTERNAL_SERVER_ERROR, ERROR_TYPE, message);
|
||||
}
|
||||
|
||||
private SearchIndexNotFoundException(Response.Status status, String message) {
|
||||
super(status, ERROR_TYPE, message);
|
||||
}
|
||||
|
||||
public static SearchIndexNotFoundException byMessage(
|
||||
String name, String errorMessage, Response.Status status) {
|
||||
return new SearchIndexNotFoundException(status, buildMessageByName(name, errorMessage));
|
||||
}
|
||||
|
||||
public static SearchIndexNotFoundException byMessage(String name, String errorMessage) {
|
||||
return new SearchIndexNotFoundException(
|
||||
Response.Status.BAD_REQUEST, buildMessageByName(name, errorMessage));
|
||||
}
|
||||
|
||||
private static String buildMessageByName(String name, String errorMessage) {
|
||||
return String.format(BY_NAME_MESSAGE, name, errorMessage);
|
||||
}
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
package org.openmetadata.sdk.exception;
|
||||
|
||||
import jakarta.ws.rs.core.Response;
|
||||
|
||||
public class SuggestionException extends WebServiceException {
|
||||
private static final String BY_NAME_MESSAGE =
|
||||
"Search Index Not Found Exception [%s] due to [%s].";
|
||||
private static final String ERROR_TYPE = "SUGGESTION_EXCEPTION";
|
||||
|
||||
public SuggestionException(String message) {
|
||||
super(Response.Status.INTERNAL_SERVER_ERROR, ERROR_TYPE, message);
|
||||
}
|
||||
|
||||
private SuggestionException(Response.Status status, String message) {
|
||||
super(status, ERROR_TYPE, message);
|
||||
}
|
||||
|
||||
public SuggestionException(Response.Status status, String errorType, String message) {
|
||||
super(status, errorType, message);
|
||||
}
|
||||
|
||||
public static SuggestionException byMessage(
|
||||
String name, String errorMessage, Response.Status status) {
|
||||
return new SuggestionException(status, buildMessageByName(name, errorMessage));
|
||||
}
|
||||
|
||||
public static SuggestionException byMessage(
|
||||
String name, String errorType, String errorMessage, Response.Status status) {
|
||||
return new SuggestionException(status, errorType, buildMessageByName(name, errorMessage));
|
||||
}
|
||||
|
||||
public static SuggestionException byMessage(String name, String errorMessage) {
|
||||
return new SuggestionException(
|
||||
Response.Status.BAD_REQUEST, buildMessageByName(name, errorMessage));
|
||||
}
|
||||
|
||||
private static String buildMessageByName(String name, String errorMessage) {
|
||||
return String.format(BY_NAME_MESSAGE, name, errorMessage);
|
||||
}
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
package org.openmetadata.sdk.exception;
|
||||
|
||||
import jakarta.ws.rs.core.Response;
|
||||
|
||||
public class UserCreationException extends WebServiceException {
|
||||
private static final String BY_NAME_MESSAGE = "User Creation Exception [%s] due to [%s].";
|
||||
private static final String ERROR_TYPE = "USER_CREATION_EXCEPTION";
|
||||
|
||||
public UserCreationException(String message) {
|
||||
super(Response.Status.INTERNAL_SERVER_ERROR, ERROR_TYPE, message);
|
||||
}
|
||||
|
||||
private UserCreationException(Response.Status status, String message) {
|
||||
super(status, ERROR_TYPE, message);
|
||||
}
|
||||
|
||||
public UserCreationException(Response.Status status, String errorType, String message) {
|
||||
super(status, errorType, message);
|
||||
}
|
||||
|
||||
public static UserCreationException byMessage(
|
||||
String name, String errorMessage, Response.Status status) {
|
||||
return new UserCreationException(status, buildMessageByName(name, errorMessage));
|
||||
}
|
||||
|
||||
public static UserCreationException byMessage(
|
||||
String name, String errorType, String errorMessage, Response.Status status) {
|
||||
return new UserCreationException(status, errorType, buildMessageByName(name, errorMessage));
|
||||
}
|
||||
|
||||
public static UserCreationException byMessage(String name, String errorMessage) {
|
||||
return new UserCreationException(
|
||||
Response.Status.BAD_REQUEST, buildMessageByName(name, errorMessage));
|
||||
}
|
||||
|
||||
private static String buildMessageByName(String name, String errorMessage) {
|
||||
return String.format(BY_NAME_MESSAGE, name, errorMessage);
|
||||
}
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
package org.openmetadata.sdk.exception;
|
||||
|
||||
import jakarta.ws.rs.core.Response;
|
||||
|
||||
public class WebServiceException extends RuntimeException {
|
||||
private final Response.Status status;
|
||||
private final String errorType;
|
||||
private final String message;
|
||||
|
||||
public WebServiceException(Response.Status status, String errorType, String message) {
|
||||
super(message);
|
||||
this.status = status;
|
||||
this.errorType = errorType;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public WebServiceException(
|
||||
Response.Status status, String errorType, String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
this.status = status;
|
||||
this.errorType = errorType;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public Response getResponse() {
|
||||
return Response.status(status)
|
||||
.type(jakarta.ws.rs.core.MediaType.APPLICATION_JSON_TYPE)
|
||||
.entity(new ErrorMessage(status.getStatusCode(), errorType, message))
|
||||
.build();
|
||||
}
|
||||
|
||||
public static class ErrorMessage {
|
||||
private final int code;
|
||||
private final String errorType;
|
||||
private final String message;
|
||||
|
||||
public ErrorMessage(int code, String errorType, String message) {
|
||||
this.code = code;
|
||||
this.errorType = errorType;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getErrorType() {
|
||||
return errorType;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package org.openmetadata.search;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import java.util.List;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
@Jacksonized
|
||||
@Getter
|
||||
@Builder
|
||||
public class IndexMapping {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(IndexMapping.class);
|
||||
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
|
||||
@Getter String indexName;
|
||||
String indexMappingFile;
|
||||
String alias;
|
||||
List<String> parentAliases;
|
||||
List<String> childAliases;
|
||||
public static final String INDEX_NAME_SEPARATOR = "_";
|
||||
private static final String DATA_ASSET_ALIAS = "dataAsset";
|
||||
|
||||
public String getIndexName(String clusterAlias) {
|
||||
return clusterAlias != null && !clusterAlias.isEmpty()
|
||||
? clusterAlias + INDEX_NAME_SEPARATOR + indexName
|
||||
: indexName;
|
||||
}
|
||||
|
||||
public String getAlias(String clusterAlias) {
|
||||
return clusterAlias != null && !clusterAlias.isEmpty()
|
||||
? clusterAlias + INDEX_NAME_SEPARATOR + alias
|
||||
: alias;
|
||||
}
|
||||
|
||||
public List<String> getParentAliases(String clusterAlias) {
|
||||
return clusterAlias != null && !clusterAlias.isEmpty()
|
||||
? parentAliases.stream().map(a -> clusterAlias + INDEX_NAME_SEPARATOR + a).toList()
|
||||
: parentAliases;
|
||||
}
|
||||
|
||||
public List<String> getChildAliases(String clusterAlias) {
|
||||
return clusterAlias != null && !clusterAlias.isEmpty()
|
||||
? childAliases.stream().map(a -> clusterAlias + INDEX_NAME_SEPARATOR + a).toList()
|
||||
: childAliases;
|
||||
}
|
||||
|
||||
public String getIndexMappingFile(String language) {
|
||||
return String.format(indexMappingFile, language).replaceFirst("^/", "");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
package org.openmetadata.search;
|
||||
|
||||
import jakarta.json.JsonObject;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import lombok.Getter;
|
||||
import org.openmetadata.schema.exception.JsonParsingException;
|
||||
import org.openmetadata.schema.service.configuration.elasticsearch.ElasticSearchConfiguration;
|
||||
import org.openmetadata.schema.utils.JsonUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@Getter
|
||||
public class IndexMappingLoader {
|
||||
|
||||
private static volatile IndexMappingLoader instance;
|
||||
private ElasticSearchConfiguration elasticSearchConfiguration;
|
||||
private String searchIndexMappingLanguage;
|
||||
|
||||
private static final String OM_INDEX_MAPPING_FILE_PATH = "elasticsearch/indexMapping.json";
|
||||
private static final String COLLATE_INDEX_MAPPING_FILE_PATH =
|
||||
"elasticsearch/collate/indexMapping.json";
|
||||
private static final Logger LOG = LoggerFactory.getLogger(IndexMappingLoader.class);
|
||||
|
||||
@Getter Map<String, IndexMapping> indexMapping = new HashMap<>();
|
||||
@Getter Map<String, Map<String, Object>> entityIndexMapping = new HashMap<>();
|
||||
|
||||
private IndexMappingLoader(ElasticSearchConfiguration elasticSearchConfiguration)
|
||||
throws IOException {
|
||||
this.elasticSearchConfiguration = elasticSearchConfiguration;
|
||||
if (elasticSearchConfiguration.getSearchIndexMappingLanguage() == null) {
|
||||
this.searchIndexMappingLanguage = "en";
|
||||
} else {
|
||||
this.searchIndexMappingLanguage =
|
||||
elasticSearchConfiguration.getSearchIndexMappingLanguage().toString().toLowerCase();
|
||||
}
|
||||
loadIndexMapping();
|
||||
loadEntityIndexMapping();
|
||||
}
|
||||
|
||||
private IndexMappingLoader() throws IOException {
|
||||
this.searchIndexMappingLanguage = "en";
|
||||
loadIndexMapping();
|
||||
loadEntityIndexMapping();
|
||||
}
|
||||
|
||||
public static void init(ElasticSearchConfiguration elasticSearchConfiguration)
|
||||
throws IOException {
|
||||
synchronized (IndexMappingLoader.class) {
|
||||
if (instance == null) {
|
||||
instance = new IndexMappingLoader(elasticSearchConfiguration);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void init() throws IOException {
|
||||
synchronized (IndexMappingLoader.class) {
|
||||
if (instance == null) {
|
||||
instance = new IndexMappingLoader();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static IndexMappingLoader getInstance() {
|
||||
IndexMappingLoader result = instance;
|
||||
if (result != null) {
|
||||
return result;
|
||||
} else {
|
||||
throw new IllegalStateException("IndexMappingLoader is not initialized. Call init() first.");
|
||||
}
|
||||
}
|
||||
|
||||
private void loadIndexMapping() throws IOException {
|
||||
Set<String> entities;
|
||||
try (InputStream inputStream =
|
||||
IndexMappingLoader.class.getClassLoader().getResourceAsStream(OM_INDEX_MAPPING_FILE_PATH)) {
|
||||
|
||||
if (inputStream == null) {
|
||||
throw new IOException("Could not find " + OM_INDEX_MAPPING_FILE_PATH + " in classpath");
|
||||
}
|
||||
|
||||
JsonObject jsonPayload =
|
||||
JsonUtils.readJson(new String(inputStream.readAllBytes())).asJsonObject();
|
||||
entities = jsonPayload.keySet();
|
||||
for (String s : entities) {
|
||||
indexMapping.put(s, JsonUtils.readValue(jsonPayload.get(s).toString(), IndexMapping.class));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new JsonParsingException("Failed to load indexMapping.json", e);
|
||||
}
|
||||
try (InputStream inputStream2 =
|
||||
IndexMappingLoader.class
|
||||
.getClassLoader()
|
||||
.getResourceAsStream(COLLATE_INDEX_MAPPING_FILE_PATH)) {
|
||||
if (inputStream2 != null) {
|
||||
JsonObject jsonPayload =
|
||||
JsonUtils.readJson(new String(inputStream2.readAllBytes())).asJsonObject();
|
||||
entities = jsonPayload.keySet();
|
||||
for (String s : entities) {
|
||||
indexMapping.put(
|
||||
s, JsonUtils.readValue(jsonPayload.get(s).toString(), IndexMapping.class));
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.warn("Failed to load indexMapping.json");
|
||||
}
|
||||
}
|
||||
|
||||
private void loadEntityIndexMapping() throws IOException {
|
||||
if (entityIndexMapping == null) {
|
||||
throw new IllegalStateException(
|
||||
"Entity index map is not loaded. Call loadIndexMapping() first.");
|
||||
}
|
||||
|
||||
for (Map.Entry<String, IndexMapping> entry : indexMapping.entrySet()) {
|
||||
String entityName = entry.getKey();
|
||||
IndexMapping indexMapping = entry.getValue();
|
||||
try (InputStream inputStream =
|
||||
IndexMappingLoader.class
|
||||
.getClassLoader()
|
||||
.getResourceAsStream(indexMapping.getIndexMappingFile(searchIndexMappingLanguage))) {
|
||||
if (inputStream == null) {
|
||||
throw new IOException(
|
||||
"Could not find "
|
||||
+ indexMapping.getIndexMappingFile(searchIndexMappingLanguage)
|
||||
+ " in classpath");
|
||||
}
|
||||
Map<String, Object> jsonMap =
|
||||
JsonUtils.getMapFromJson(new String(inputStream.readAllBytes()));
|
||||
entityIndexMapping.put(entityName, jsonMap);
|
||||
} catch (Exception e) {
|
||||
throw new JsonParsingException("Failed to load index mapping for " + entityName, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+261
@@ -0,0 +1,261 @@
|
||||
/*
|
||||
* Copyright 2021 Collate
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.openmetadata.service.clients.pipeline;
|
||||
|
||||
import io.github.resilience4j.retry.Retry;
|
||||
import io.github.resilience4j.retry.RetryConfig;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Base64;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.regex.MatchResult;
|
||||
import java.util.regex.Pattern;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.openmetadata.common.utils.CommonUtil;
|
||||
import org.openmetadata.schema.api.configuration.pipelineServiceClient.PipelineServiceClientConfiguration;
|
||||
import org.openmetadata.schema.entity.services.ingestionPipelines.IngestionPipeline;
|
||||
import org.openmetadata.schema.entity.services.ingestionPipelines.PipelineServiceClientResponse;
|
||||
import org.openmetadata.schema.entity.services.ingestionPipelines.PipelineStatus;
|
||||
import org.openmetadata.sdk.PipelineServiceClientInterface;
|
||||
import org.openmetadata.sdk.exception.PipelineServiceClientException;
|
||||
import org.openmetadata.sdk.exception.PipelineServiceVersionException;
|
||||
|
||||
/**
|
||||
* Client to make API calls to add, deleted, and deploy pipelines on a PipelineService, such as
|
||||
* Airflow. Core abstractions are as follows:
|
||||
*
|
||||
* <ul>
|
||||
* <li>A PipelineService is a service such as AirFlow to which a pipeline can be deployed
|
||||
* <li>A Pipeline is a workflow for performing certain tasks. Example - ingestion pipeline is a
|
||||
* workflow that connects to a database service or other services and collect metadata.
|
||||
* <li>Pipeline uses `Connection` to a service as dependency. A Pipeline might need to connection
|
||||
* to database service to collect metadata, OpenMetadata to user metadata over APIs, etc.
|
||||
* </ul>
|
||||
*/
|
||||
@Slf4j
|
||||
public abstract class PipelineServiceClient implements PipelineServiceClientInterface {
|
||||
protected final boolean pipelineServiceClientEnabled;
|
||||
protected final String hostIp;
|
||||
|
||||
protected final boolean ingestionIpInfoEnabled;
|
||||
|
||||
@Getter @Setter private String platform;
|
||||
|
||||
protected static final String AUTH_HEADER = "Authorization";
|
||||
protected static final String CONTENT_HEADER = "Content-Type";
|
||||
protected static final String CONTENT_TYPE = "application/json";
|
||||
private static final Integer MAX_ATTEMPTS = 3;
|
||||
private static final Integer BACKOFF_TIME_SECONDS = 5;
|
||||
private static final String DISABLED_STATUS = "disabled";
|
||||
|
||||
protected static final String SERVER_VERSION;
|
||||
|
||||
static {
|
||||
String rawServerVersion;
|
||||
try {
|
||||
rawServerVersion = getServerVersion();
|
||||
} catch (IOException e) {
|
||||
rawServerVersion = "unknown";
|
||||
}
|
||||
SERVER_VERSION = rawServerVersion;
|
||||
}
|
||||
|
||||
public PipelineServiceClient(
|
||||
PipelineServiceClientConfiguration pipelineServiceClientConfiguration) {
|
||||
this.pipelineServiceClientEnabled = pipelineServiceClientConfiguration.getEnabled();
|
||||
this.hostIp = pipelineServiceClientConfiguration.getHostIp();
|
||||
this.ingestionIpInfoEnabled = pipelineServiceClientConfiguration.getIngestionIpInfoEnabled();
|
||||
}
|
||||
|
||||
public final URL validateServiceURL(String serviceURL) {
|
||||
try {
|
||||
return new URL(serviceURL);
|
||||
} catch (MalformedURLException e) {
|
||||
throw new PipelineServiceClientException(serviceURL + " Malformed.");
|
||||
}
|
||||
}
|
||||
|
||||
public final String getBasicAuthenticationHeader(String username, String password) {
|
||||
String valueToEncode = username + ":" + password;
|
||||
return "Basic " + Base64.getEncoder().encodeToString(valueToEncode.getBytes());
|
||||
}
|
||||
|
||||
public static String getServerVersion() throws IOException {
|
||||
InputStream fileInput = PipelineServiceClient.class.getResourceAsStream("/catalog/VERSION");
|
||||
Properties props = new Properties();
|
||||
if (fileInput != null) {
|
||||
props.load(fileInput);
|
||||
}
|
||||
return props.getProperty("version", "unknown");
|
||||
}
|
||||
|
||||
public final String getVersionFromString(String version) {
|
||||
if (version != null) {
|
||||
return Pattern.compile("(\\d+)\\.(\\d+)(?:\\.(\\d+))?")
|
||||
.matcher(version)
|
||||
.results()
|
||||
.map(this::formatVersionMatch)
|
||||
.findFirst()
|
||||
.orElseThrow(
|
||||
() ->
|
||||
new PipelineServiceVersionException(
|
||||
String.format("Cannot extract version x.y from %s", version)));
|
||||
} else {
|
||||
throw new PipelineServiceVersionException("Received version as null");
|
||||
}
|
||||
}
|
||||
|
||||
private String formatVersionMatch(MatchResult match) {
|
||||
String patch = match.group(3) != null ? match.group(3) : "0";
|
||||
return match.group(1) + "." + match.group(2) + "." + patch;
|
||||
}
|
||||
|
||||
private String getMajorMinorVersion(String fullVersion) {
|
||||
String[] versionParts = fullVersion.split("\\.");
|
||||
if (versionParts.length >= 2) {
|
||||
return versionParts[0] + "." + versionParts[1];
|
||||
}
|
||||
return fullVersion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Boolean validServerClientVersions(String clientVersion, String serverVersion) {
|
||||
String clientFullVersion = getVersionFromString(clientVersion);
|
||||
String serverFullVersion = getVersionFromString(serverVersion);
|
||||
|
||||
return getMajorMinorVersion(clientFullVersion).equals(getMajorMinorVersion(serverFullVersion));
|
||||
}
|
||||
|
||||
public String buildVersionMismatchErrorMessage(String ingestionVersion, String serverVersion) {
|
||||
if (getVersionFromString(ingestionVersion).compareTo(getVersionFromString(serverVersion)) < 0) {
|
||||
return String.format(
|
||||
"Ingestion version [%s] is older than Server Version [%s]. Please upgrade your ingestion client.",
|
||||
ingestionVersion, serverVersion);
|
||||
}
|
||||
return String.format(
|
||||
"Server version [%s] is older than Ingestion Version [%s]. Please upgrade your server or downgrade the ingestion client.",
|
||||
serverVersion, ingestionVersion);
|
||||
}
|
||||
|
||||
/** To build the response of getServiceStatus */
|
||||
protected PipelineServiceClientResponse buildHealthyStatus(String ingestionVersion) {
|
||||
return new PipelineServiceClientResponse()
|
||||
.withCode(200)
|
||||
.withVersion(ingestionVersion)
|
||||
.withPlatform(this.getPlatform());
|
||||
}
|
||||
|
||||
/** To build the response of getServiceStatus */
|
||||
protected PipelineServiceClientResponse buildUnhealthyStatus(String reason) {
|
||||
return new PipelineServiceClientResponse()
|
||||
.withCode(500)
|
||||
.withReason(reason)
|
||||
.withPlatform(this.getPlatform());
|
||||
}
|
||||
|
||||
public final Response getHostIp() {
|
||||
|
||||
if (this.ingestionIpInfoEnabled) {
|
||||
return getHostIpInternal();
|
||||
}
|
||||
return Response.status(Response.Status.NO_CONTENT).build();
|
||||
}
|
||||
|
||||
private Response getHostIpInternal() {
|
||||
Map<String, String> body;
|
||||
try {
|
||||
body = CommonUtil.nullOrEmpty(this.hostIp) ? requestGetHostIp() : Map.of("ip", this.hostIp);
|
||||
return Response.ok(body, MediaType.APPLICATION_JSON_TYPE).build();
|
||||
} catch (Exception e) {
|
||||
LOG.error("Failed to get Pipeline Service host IP. {}", e.getMessage());
|
||||
// We don't want the request to fail for an informative ping
|
||||
body =
|
||||
Map.of(
|
||||
"ip",
|
||||
"Failed to find the IP of Airflow Container. Please make sure https://api.ipify.org, "
|
||||
+ "https://api.my-ip.io/ip reachable from your network or that the `hostIp` setting is configured.");
|
||||
return Response.ok(body, MediaType.APPLICATION_JSON_TYPE).build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the pipeline service status with an exception backoff to make sure we don't raise any
|
||||
* false positives.
|
||||
*/
|
||||
public String getServiceStatusBackoff() {
|
||||
RetryConfig retryConfig =
|
||||
RetryConfig.<String>custom()
|
||||
.maxAttempts(MAX_ATTEMPTS)
|
||||
.waitDuration(Duration.ofMillis(BACKOFF_TIME_SECONDS * 1_000L))
|
||||
.retryOnResult(response -> !HEALTHY_STATUS.equals(response))
|
||||
.failAfterMaxAttempts(false)
|
||||
.build();
|
||||
|
||||
Retry retry = Retry.of("getServiceStatus", retryConfig);
|
||||
|
||||
Supplier<String> responseSupplier =
|
||||
() -> {
|
||||
try {
|
||||
PipelineServiceClientResponse status = getServiceStatus();
|
||||
return status.getCode() != 200 ? UNHEALTHY_STATUS : HEALTHY_STATUS;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
};
|
||||
|
||||
return retry.executeSupplier(responseSupplier);
|
||||
}
|
||||
|
||||
/* Check the status of pipeline service to ensure it is healthy */
|
||||
public PipelineServiceClientResponse getServiceStatus() {
|
||||
if (pipelineServiceClientEnabled) {
|
||||
return getServiceStatusInternal();
|
||||
}
|
||||
return buildHealthyStatus(DISABLED_STATUS).withPlatform(DISABLED_STATUS);
|
||||
}
|
||||
|
||||
public List<PipelineStatus> getQueuedPipelineStatus(IngestionPipeline ingestionPipeline) {
|
||||
List<PipelineStatus> result = new ArrayList<>();
|
||||
if (pipelineServiceClientEnabled) {
|
||||
try {
|
||||
List<PipelineStatus> internal = getQueuedPipelineStatusInternal(ingestionPipeline);
|
||||
if (internal != null) {
|
||||
result.addAll(internal);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.warn(
|
||||
"Failed to fetch queued pipeline status for {}: {}. Returning stored statuses only.",
|
||||
ingestionPipeline.getFullyQualifiedName(),
|
||||
e.getMessage());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
protected abstract PipelineServiceClientResponse getServiceStatusInternal();
|
||||
|
||||
/* Get the Pipeline Service host IP to whitelist in source systems. Should return a map in the shape "ip: 111.11.11.1" */
|
||||
protected abstract Map<String, String> requestGetHostIp();
|
||||
}
|
||||
+116
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright 2025 Collate
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.openmetadata.service.logstorage;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Interface for pluggable log storage implementations for ingestion pipelines.
|
||||
* Each implementation should support storing, streaming, and retrieving logs
|
||||
* for pipeline runs identified by FQN and runId.
|
||||
*/
|
||||
public interface LogStorageInterface {
|
||||
|
||||
/**
|
||||
* Initialize the log storage with configuration
|
||||
* @param config Configuration map specific to the implementation
|
||||
*/
|
||||
void initialize(Map<String, Object> config) throws IOException;
|
||||
|
||||
/**
|
||||
* Append log content for a pipeline run
|
||||
* @param pipelineFQN Fully qualified name of the pipeline
|
||||
* @param runId Unique run identifier
|
||||
* @param logContent Log content to append
|
||||
*/
|
||||
void appendLogs(String pipelineFQN, UUID runId, String logContent) throws IOException;
|
||||
|
||||
/**
|
||||
* Stream log content for a pipeline run
|
||||
* @param pipelineFQN Fully qualified name of the pipeline
|
||||
* @param runId Unique run identifier
|
||||
* @return InputStream for reading logs
|
||||
*/
|
||||
InputStream getLogInputStream(String pipelineFQN, UUID runId) throws IOException;
|
||||
|
||||
/**
|
||||
* Get logs with pagination support
|
||||
* @param pipelineFQN Fully qualified name of the pipeline
|
||||
* @param runId Unique run identifier
|
||||
* @param afterCursor Cursor for pagination (null for beginning)
|
||||
* @param limit Maximum number of lines to return
|
||||
* @return Map containing "logs" (content), "after" (next cursor), and "total" (total size)
|
||||
*/
|
||||
Map<String, Object> getLogs(String pipelineFQN, UUID runId, String afterCursor, int limit)
|
||||
throws IOException;
|
||||
|
||||
/**
|
||||
* Get the latest run ID for a pipeline
|
||||
* @param pipelineFQN Fully qualified name of the pipeline
|
||||
* @return Latest run ID or null if no runs exist
|
||||
*/
|
||||
UUID getLatestRunId(String pipelineFQN) throws IOException;
|
||||
|
||||
/**
|
||||
* List available runs for a pipeline
|
||||
* @param pipelineFQN Fully qualified name of the pipeline
|
||||
* @param limit Maximum number of runs to return
|
||||
* @return List of run IDs sorted by timestamp (newest first)
|
||||
*/
|
||||
List<UUID> listRuns(String pipelineFQN, int limit) throws IOException;
|
||||
|
||||
/**
|
||||
* Delete logs for a specific run
|
||||
* @param pipelineFQN Fully qualified name of the pipeline
|
||||
* @param runId Unique run identifier
|
||||
*/
|
||||
void deleteLogs(String pipelineFQN, UUID runId) throws IOException;
|
||||
|
||||
/**
|
||||
* Delete all logs for a pipeline
|
||||
* @param pipelineFQN Fully qualified name of the pipeline
|
||||
*/
|
||||
void deleteAllLogs(String pipelineFQN) throws IOException;
|
||||
|
||||
/**
|
||||
* Check if logs exist for a run
|
||||
* @param pipelineFQN Fully qualified name of the pipeline
|
||||
* @param runId Unique run identifier
|
||||
* @return true if logs exist
|
||||
*/
|
||||
boolean logsExist(String pipelineFQN, UUID runId) throws IOException;
|
||||
|
||||
/**
|
||||
* Get the storage type identifier
|
||||
* @return Storage type (e.g., "s3", "file", "database")
|
||||
*/
|
||||
String getStorageType();
|
||||
|
||||
/**
|
||||
* Close and finalize the log stream for a specific pipeline run.
|
||||
* This ensures any buffered data is written and the stream is properly closed.
|
||||
* @param pipelineFQN Fully qualified name of the pipeline
|
||||
* @param runId Unique run identifier
|
||||
*/
|
||||
void closeStream(String pipelineFQN, UUID runId) throws IOException;
|
||||
|
||||
/**
|
||||
* Clean up resources
|
||||
*/
|
||||
void close() throws IOException;
|
||||
}
|
||||
Reference in New Issue
Block a user