You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@atlas.apache.org by ni...@apache.org on 2026/08/20 12:56:57 UTC

(atlas) branch atlas-2.6 updated (0f4d63d4e4 -> 23899b586a)

This is an automated email from the ASF dual-hosted git repository.

asf-gitbox-commits pushed a change to branch atlas-2.6
in repository https://gitbox.apache.org/repos/asf/atlas.git


    from 0f4d63d4e4 ATLAS-5372: Prevent cross-user overwrite in saved search create API via caller-supplied guid (#724) (Cherrypicked from c1cc1ff9114f19248ec642d61eb861a1dab75919) Co-authored-by: chaitalithombare <ch...@apache.org>
     new 765c52ec78 ATLAS-5309 : Allow unauthenticated access to Swagger apidocs static assets (#673)
     new c51c386cde ATLAS-5353: Upgrade Netty version to 4.2.15.Final (#719)
     new fef1091877 ATLAS-5312: Add delete propagation support via propagateDelete flag o… (#670)
     new 2c8319a9ce ATLAS-5279: changes for handling rename propagation (#609)
     new 23899b586a ATLAS-5385: Rename header auth property keys for atlas-2.6

The 5 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 addons/models/6000-Trino/6000-trino_model.json     |   6 +-
 .../002-delete_propagation_typedef_patch.json      |  37 ++
 build-tools/src/main/resources/ui-dist/index.js    |   6 +-
 distro/src/conf/atlas-application.properties       |  14 +-
 docs/src/documents/TypeSystem.md                   | 155 +++++++
 .../model/typedef/AtlasRelationshipEndDef.java     |  28 +-
 .../org/apache/atlas/type/AtlasEntityType.java     |  57 +++
 ...ionTarget.java => DeletePropagationTarget.java} |  26 +-
 .../model/typedef/TestAtlasRelationshipEndDef.java |  66 +++
 .../atlas/type/TestDeletePropagationTarget.java    | 122 ++++++
 pom.xml                                            |   4 +-
 .../bootstrap/AtlasTypeDefStoreInitializer.java    |  61 ++-
 .../repository/store/graph/v1/DeleteHandlerV1.java |  40 ++
 .../graph/v2/EntityDeletePropagationHandler.java   | 133 ++++++
 .../AtlasTypeDefStoreInitializerTest.java          | 234 ++++++++--
 .../v2/EntityDeletePropagationHandlerTest.java     | 476 +++++++++++++++++++++
 .../web/filters/AtlasHeaderPreAuthFilter.java      |   8 +-
 .../atlas/web/security/AtlasSecurityConfig.java    |   3 +-
 webapp/src/main/resources/spring-security.xml      |   1 +
 .../web/filters/AtlasHeaderPreAuthFilterTest.java  |  20 +-
 20 files changed, 1398 insertions(+), 99 deletions(-)
 create mode 100644 addons/models/6000-Trino/patches/002-delete_propagation_typedef_patch.json
 copy intg/src/main/java/org/apache/atlas/type/{RenamePropagationTarget.java => DeletePropagationTarget.java} (61%)
 create mode 100644 intg/src/test/java/org/apache/atlas/type/TestDeletePropagationTarget.java
 create mode 100644 repository/src/main/java/org/apache/atlas/repository/store/graph/v2/EntityDeletePropagationHandler.java
 create mode 100644 repository/src/test/java/org/apache/atlas/repository/store/graph/v2/EntityDeletePropagationHandlerTest.java


(atlas) 02/05: ATLAS-5353: Upgrade Netty version to 4.2.15.Final (#719)

Posted by ni...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

asf-gitbox-commits pushed a commit to branch atlas-2.6
in repository https://gitbox.apache.org/repos/asf/atlas.git

commit c51c386cdef2603866b1210f4fda56bce067fd22
Author: chaitalicod <36...@users.noreply.github.com>
AuthorDate: Thu Aug 6 16:11:47 2026 +0530

    ATLAS-5353: Upgrade Netty version to 4.2.15.Final (#719)
    
    Co-authored-by: chaitalithombare <ch...@apache.org>
---
 pom.xml | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/pom.xml b/pom.xml
index 81cc16461f..7c33fd273d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -158,7 +158,9 @@
         <lucene-solr.version>8.11.3</lucene-solr.version>
         <maven-plugin-sortpom.version>3.0.1</maven-plugin-sortpom.version>
         <maven-site-plugin.version>3.7</maven-site-plugin.version>
-        <netty.version>4.1.125.Final</netty.version>
+        <maven-source-plugin.version>3.2.1</maven-source-plugin.version>
+        <mockito.version>3.12.4</mockito.version>
+        <netty.version>4.2.15.Final</netty.version>
         <nimbus-jose-jwt.version>9.37.3</nimbus-jose-jwt.version>
         <node-for-v1.version>v22.22.1</node-for-v1.version>
         <node-for-v2.version>v12.16.0</node-for-v2.version>


(atlas) 01/05: ATLAS-5309 : Allow unauthenticated access to Swagger apidocs static assets (#673)

Posted by ni...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

asf-gitbox-commits pushed a commit to branch atlas-2.6
in repository https://gitbox.apache.org/repos/asf/atlas.git

commit 765c52ec78381dde733aaaf6fe7961d82c812f69
Author: pareshd <pa...@freestoneinfotech.com>
AuthorDate: Tue Jun 16 18:18:21 2026 +0530

    ATLAS-5309 : Allow unauthenticated access to Swagger apidocs static assets (#673)
    
    Changes :-
            Added /apidocs/** to security exclusions in AtlasSecurityConfig.java and spring-security.xml to allow anonymous access to Swagger UI assets and OpenAPI documentation.
            Updated index.js to handle anonymous users and prevent CSRF-related JavaScript errors when no session is available.
    
    Testing
            Verified Swagger UI and openapi.json are accessible without authentication.
            Confirmed Swagger UI loads successfully for anonymous users.
    
    Co-authored-by: Paresh Devalia <pa...@cloudera.com>
---
 build-tools/src/main/resources/ui-dist/index.js                     | 6 +++++-
 .../java/org/apache/atlas/web/security/AtlasSecurityConfig.java     | 3 ++-
 webapp/src/main/resources/spring-security.xml                       | 1 +
 3 files changed, 8 insertions(+), 2 deletions(-)

diff --git a/build-tools/src/main/resources/ui-dist/index.js b/build-tools/src/main/resources/ui-dist/index.js
index 932df7bd90..92a971f73e 100644
--- a/build-tools/src/main/resources/ui-dist/index.js
+++ b/build-tools/src/main/resources/ui-dist/index.js
@@ -86,6 +86,10 @@
     function fetchCsrfHeader() {
         var response = getSessionDetails();
 
+        if (!response) {
+            return;
+        }
+
         if (!csrfEnabled && response['atlas.rest-csrf.enabled']) {
             var str = "" + response['atlas.rest-csrf.enabled'];
             csrfEnabled = (str.toLowerCase() == 'true');
@@ -123,4 +127,4 @@
         });
         return response;
     };
-})();
\ No newline at end of file
+})();
diff --git a/webapp/src/main/java/org/apache/atlas/web/security/AtlasSecurityConfig.java b/webapp/src/main/java/org/apache/atlas/web/security/AtlasSecurityConfig.java
index 532f3319ca..4ab0accaa9 100644
--- a/webapp/src/main/java/org/apache/atlas/web/security/AtlasSecurityConfig.java
+++ b/webapp/src/main/java/org/apache/atlas/web/security/AtlasSecurityConfig.java
@@ -190,7 +190,8 @@ public class AtlasSecurityConfig extends WebSecurityConfigurerAdapter {
                 "/libs/**", "/n/libs/**",
                 "/js/**", "/n/js/**",
                 "/ieerror.html", "/migration-status.html",
-                "/api/atlas/admin/status"));
+                "/api/atlas/admin/status",
+                "/apidocs/**"));
 
         if (!keycloakEnabled) {
             matchers.add("/login.jsp");
diff --git a/webapp/src/main/resources/spring-security.xml b/webapp/src/main/resources/spring-security.xml
index ea41d46ab7..3d2d55bec4 100644
--- a/webapp/src/main/resources/spring-security.xml
+++ b/webapp/src/main/resources/spring-security.xml
@@ -28,6 +28,7 @@
     <security:http pattern="/ieerror.html" security="none" />
     <security:http pattern="/api/atlas/admin/status" security="none" />
     <security:http pattern="/api/atlas/admin/metrics" security="none" />
+    <security:http pattern="/apidocs/**" security="none" />
 
     <security:http create-session="always"
                    entry-point-ref="entryPoint">


(atlas) 03/05: ATLAS-5312: Add delete propagation support via propagateDelete flag o… (#670)

Posted by ni...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

asf-gitbox-commits pushed a commit to branch atlas-2.6
in repository https://gitbox.apache.org/repos/asf/atlas.git

commit fef1091877428edb0c380f16463c0f084fa34ff2
Author: bhor-sanket <sa...@gmail.com>
AuthorDate: Mon Aug 10 09:19:24 2026 +0530

    ATLAS-5312: Add delete propagation support via propagateDelete flag o… (#670)
---
 addons/models/6000-Trino/6000-trino_model.json     |   6 +-
 .../002-delete_propagation_typedef_patch.json      |  37 ++
 docs/src/documents/RenamePropagation.md            | 200 ---------
 docs/src/documents/TypeSystem.md                   | 155 +++++++
 .../model/typedef/AtlasRelationshipEndDef.java     |  26 +-
 .../org/apache/atlas/type/AtlasEntityType.java     |  57 +++
 .../apache/atlas/type/DeletePropagationTarget.java |  59 +++
 .../model/typedef/TestAtlasRelationshipEndDef.java |  66 +++
 .../atlas/type/TestDeletePropagationTarget.java    | 122 ++++++
 .../bootstrap/AtlasTypeDefStoreInitializer.java    |  61 ++-
 .../repository/store/graph/v1/DeleteHandlerV1.java |  40 ++
 .../graph/v2/EntityDeletePropagationHandler.java   | 133 ++++++
 .../AtlasTypeDefStoreInitializerTest.java          | 234 ++++++++--
 .../v2/EntityDeletePropagationHandlerTest.java     | 476 +++++++++++++++++++++
 14 files changed, 1419 insertions(+), 253 deletions(-)

diff --git a/addons/models/6000-Trino/6000-trino_model.json b/addons/models/6000-Trino/6000-trino_model.json
index aa254ee6ed..a8aa885de0 100644
--- a/addons/models/6000-Trino/6000-trino_model.json
+++ b/addons/models/6000-Trino/6000-trino_model.json
@@ -128,7 +128,7 @@
       "relationshipCategory": "AGGREGATION",
       "propagateTags":        "NONE",
       "endDef1": { "type": "trino_table",  "name": "trinoschema", "isContainer": false, "cardinality": "SINGLE", "isLegacyAttribute": false },
-      "endDef2": { "type": "trino_schema", "name": "tables",      "isContainer": true,  "cardinality": "SET" }
+      "endDef2": { "type": "trino_schema", "name": "tables",      "isContainer": true,  "cardinality": "SET", "propagateDelete": true }
     },
     {
       "name":                 "trino_table_columns",
@@ -172,7 +172,7 @@
       "typeVersion":          "1.0",
       "relationshipCategory": "ASSOCIATION",
       "propagateTags":        "BOTH",
-      "endDef1": { "type": "hive_db",      "name": "trino_schema",  "cardinality": "SET"    },
+      "endDef1": { "type": "hive_db",      "name": "trino_schema",  "cardinality": "SET", "propagateDelete": true },
       "endDef2": { "type": "trino_schema", "name": "hive_db",       "cardinality": "SINGLE" }
     },
     {
@@ -181,7 +181,7 @@
       "typeVersion":          "1.0",
       "relationshipCategory": "ASSOCIATION",
       "propagateTags":        "BOTH",
-      "endDef1": { "type": "hive_table",  "name": "trino_table", "cardinality": "SET", "propagateRename": true, "propagateAttributes": [ { "source": "name", "target": "name" } ]   },
+      "endDef1": { "type": "hive_table",  "name": "trino_table", "cardinality": "SET", "propagateRename": true, "propagateDelete": true, "propagateAttributes": [ { "source": "name", "target": "name" } ] },
       "endDef2": { "type": "trino_table", "name": "hive_table",  "cardinality": "SINGLE" }
     },
     {
diff --git a/addons/models/6000-Trino/patches/002-delete_propagation_typedef_patch.json b/addons/models/6000-Trino/patches/002-delete_propagation_typedef_patch.json
new file mode 100644
index 0000000000..21b95d5d47
--- /dev/null
+++ b/addons/models/6000-Trino/patches/002-delete_propagation_typedef_patch.json
@@ -0,0 +1,37 @@
+{
+  "patches": [
+    {
+      "id": "TYPEDEF_PATCH_6000_002_A",
+      "description": "Set propagateDelete=true on trino_table_hive_table endDef1 (hive_table → trino_table)",
+      "action": "SET_PROPAGATE_DELETE",
+      "typeName": "trino_table_hive_table",
+      "applyToVersion": "1.1",
+      "updateToVersion": "1.2",
+      "params": {
+        "endDefToken": "endDef1"
+      }
+    },
+    {
+      "id": "TYPEDEF_PATCH_6000_002_B",
+      "description": "Set propagateDelete=true on trino_schema_hive_db endDef1 (hive_db → trino_schema)",
+      "action": "SET_PROPAGATE_DELETE",
+      "typeName": "trino_schema_hive_db",
+      "applyToVersion": "1.0",
+      "updateToVersion": "1.1",
+      "params": {
+        "endDefToken": "endDef1"
+      }
+    },
+    {
+      "id": "TYPEDEF_PATCH_6000_002_C",
+      "description": "Set propagateDelete=true on trino_table_schema endDef2 (trino_schema → trino_table)",
+      "action": "SET_PROPAGATE_DELETE",
+      "typeName": "trino_table_schema",
+      "applyToVersion": "1.0",
+      "updateToVersion": "1.1",
+      "params": {
+        "endDefToken": "endDef2"
+      }
+    }
+  ]
+}
diff --git a/docs/src/documents/RenamePropagation.md b/docs/src/documents/RenamePropagation.md
deleted file mode 100644
index b261c2adfe..0000000000
--- a/docs/src/documents/RenamePropagation.md
+++ /dev/null
@@ -1,200 +0,0 @@
----
-
-## name: Rename Propagation
-
-route: /RenamePropagation
-menu: Documentation
-submenu: Features
-
-# Rename propagation
-
-**Rename propagation** is an Atlas feature for **partial entity updates**: when a renamed instance’s `**name`** (or a mapped source attribute) changes, the server can find **related entities** whose **unique attribute** (for most types, `**qualifiedName`**) still embeds the old segment, recompute that unique attribute from the type’s `**autoComputeFormat`**, and **persist those updates in the same transaction** as the triggering entity. Discovery is driven by **typedefs** (relationship e [...]
-
----
-
-## Why it exists
-
-The unique attribute (qualified name) is often a **composite string** built from ancestor names (database, table, cluster, and so on). If only the root entity is updated in a hook payload, **downstream** instances can keep an outdated unique attribute until something rewrites them. Rename propagation closes that gap by following **marked relationship edges** from the updated vertex and applying the same **parse → replace one template segment → rebuild** logic per dependent type.
-
----
-
-## Example: Hive database rename
-
-Assume a Hive model where:
-
-- `**hive_db`** has unique attribute (qualified name) shaped like `{name}@{clusterName}` (for example `sales@cm`).
-- `**hive_table`** uses `{db.name}.{name}@{clusterName}` (for example `sales.orders@cm`).
-- `**hive_column**` uses `{table.db.name}.{table.name}.{name}@{clusterName}`.
-
-The `**hive_table_db**` relationship links each table to its database; `**hive_table_columns**` links columns to a table. Relationship ends that opt in with `**propagateRename**` tell Atlas: when the **trigger** side is renamed, walk to neighbors and refresh their unique attribute (qualified name) where the template depends on that rename.
-
-1. An integration renames the database `**sales`** → `**sales_archive`** (partial update; `**name**` and thus the unique attribute on the `hive_db` vertex change).
-2. Atlas detects that the entity’s **unique attribute (qualified name)** changed and that `**hive_db`** has **rename propagation targets** from typedef resolution.
-3. `**EntityRenameHandler`** follows the configured relationship from the database vertex to related `**hive_table`** vertices, recomputes each table’s string (for example `sales.orders@cm` → `sales_archive.orders@cm`), and registers those rows on the mutation context.
-4. If `**hive_table**` also declares propagation targets, the same process continues to `**hive_column**` rows (for example `sales.orders.col1@cm` → `sales_archive.orders.col1@cm`).
-
-All of this happens **without** requiring the hook to send every table and column in one batch.
-
----
-
-## Example: Hive table → Trino table → Trino column
-
-Assume a Hive and Trino model where:
-
-- `**hive_table**` has unique attribute (qualified name) shaped like `{db.name}.{name}@{clusterName}` (for example `default.orders@cluster`).
-- `**trino_table**` uses `{trinoschema.catalog.name}.{trinoschema.name}.{name}@{trinoschema.catalog.instance.name}` (for example `cat1.schema1.orders@inst1`).
-- `**trino_column**` uses `{table.trinoschema.catalog.name}.{table.trinoschema.name}.{table.name}.{name}@{table.trinoschema.catalog.instance.name}` (for example `cat1.schema1.orders.col1@inst1`).
-
-The `**trino_table_hive_table**` relationship links each Trino table to its Hive table; `**trino_table_columns**` links columns to a table. The `**propagateRename**` flag on the `**hive_table**` end of `**trino_table_hive_table**` tells Atlas: when the **trigger** Hive table is renamed, walk to linked `**trino_table**` vertices and refresh their unique attribute (qualified name) where the `**autoComputeFormat**` template depends on that rename.
-
-1. An integration renames the Hive table `**orders**` → `**orders_v2**` (partial update; `**name**` and thus the unique attribute on the `**hive_table**` vertex change).
-2. Atlas detects that the entity's **unique attribute (qualified name)** changed and that `**hive_table**` has **rename propagation targets** from typedef resolution.
-3. `**EntityRenameHandler**` follows `**trino_table_hive_table**` from the Hive table vertex to related `**trino_table**` vertices, recomputes each table's string (for example `cat1.schema1.orders@inst1` → `cat1.schema1.orders_v2@inst1`), and registers those rows on the mutation context.
-4. If `**trino_table**` also declares propagation targets, the same process continues to `**trino_column**` rows (for example `cat1.schema1.orders.col1@inst1` → `cat1.schema1.orders_v2.col1@inst1`).
-
-All of this happens **without** requiring the hook to send every Trino table and column in one batch.
-
----
-
-## How it works (two stages)
-
-### 1. Typedef resolution (startup / type updates)
-
-While the type system resolves references, Atlas builds **per-entity-type** metadata used later at runtime:
-
-- `**propagateRename`** on a `**AtlasRelationshipEndDef`** marks which end acts as the **rename source** for that relationship so instances reached through that edge can be updated when the source side’s name changes.
-- `**attributeDefOverrides`** on `**AtlasEntityDef`** can set `**autoComputeFormat**` for the **unique attribute** (qualified name), describing how the string is composed from named segments.
-- For paths that do not use explicit `**propagateAttributes`** maps on the relationship end, `**AtlasEntityType`** also maintains `**autoComputeFormatPathByRefTypeNameMap**`: referenced type name → **dotted path** in the template for the segment to replace when that referenced type was renamed.
-- Optional `**propagateAttributes`** on a relationship end lists `**source`** / `**target**` attribute pairs so values such as `**name**` can be written to the right stub fields on dependents when models need more than template substitution alone.
-
-Together, these produce a list of `**RenamePropagationTarget**` entries on each `**AtlasEntityType**`.
-
-### 2. Entity update (runtime)
-
-On **partial update**, `**AtlasEntityStoreV2`** compares the stored **unique attribute (qualified name)** on the graph vertex to the incoming value. If it changed and the type has **rename propagation targets**, `**EntityRenameHandler`** walks the graph along those relationships, recomputes each dependent’s unique attribute from its effective `**autoComputeFormat`**, and adds minimal **dependent stubs** (guid, new unique attribute, optional mapped attributes) to the **same** mutation con [...]
-
----
-
-## Key typedef concepts
-
-
-| Concept                                     | Where it lives            | Role                                                                                                                                                                                                 |
-| ------------------------------------------- | ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| `**attributeDefOverrides`**                 | `AtlasEntityDef`          | Supplies per-type overrides such as `**autoComputeFormat`** for the unique attribute (qualified name); merged during hierarchy resolution and stored on the entity type vertex when supported.       |
-| `**propagateRename**`                       | `AtlasRelationshipEndDef` | Marks the end whose entity type is the **trigger** for propagation across that relationship.                                                                                                         |
-| `**propagateAttributes`**                   | `AtlasRelationshipEndDef` | Optional maps from a **source** attribute on the trigger side to **target** attribute names on the dependent entity.                                                                                 |
-| `**RenamePropagationTarget**`               | `AtlasEntityType`         | Precomputed link: target type name, relationship attribute for edge traversal, and propagate-attribute list.                                                                                         |
-| `**autoComputeFormatPathByRefTypeNameMap`** | `AtlasEntityType`         | Referenced entity type name → dotted path in the dependent’s **autoComputeFormat** for the segment to rewrite when that type was renamed (when `**propagateAttributes`** is not used for that path). |
-
-
----
-
-## Implementation reference
-
-
-| Component                                                | Role                                                                                                                                                               |
-| -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
-| `**AtlasEntityType`**                                    | Registers rename propagation targets and builds `**autoComputeFormatPathByRefTypeNameMap**` during reference resolution.                                           |
-| `**EntityRenameHandler**`                                | Traverses edges, parses/rebuilds the unique attribute string, registers `**DependentUpdate**` entries on the mutation context.                                     |
-| `**AtlasEntityStoreV2**`                                 | Detects a changed unique attribute (qualified name) on partial update and invokes the handler.                                                                     |
-| `**AtlasEntityDefStoreV2` / `AtlasTypeDefGraphStoreV2**` | Persist and read `**attributeDefOverrides**` via the type vertex property keyed by `**TYPE_ATTR_DEF_OVERRIDES_PROPERTY_KEY**`.                                     |
-| `**AtlasTypeDefStoreInitializer**`                       | Applies typedef patches such as `**SET_ATTRIBUTE_DEF_OVERRIDES**` and `**SET_PROPAGATE_RENAME**` (patch params use `**endDefToken**`: `"endDef1"` or `"endDef2"`). |
-| `**GraphBackedSearchIndexer**`                           | Indexes the overrides property for search where applicable.                                                                                                        |
-
-
----
-
-## Models and patches
-
-- **Bootstrap model JSON** — Declare `**attributeDefOverrides`** for the unique attribute (qualified name) and `**propagateRename`** / `**propagateAttributes**` on relationship ends as required by your connectors (for example Hive, Trino).
-- **Patch files** — Ship one-off typedef updates under `**addons/.../patches/`** so each patch runs once and is tracked (for example `**AtlasPatchRegistry`**), avoiding reliance on version-only bumps on every restart.
-
----
-
-## Configuring `autoComputeFormat` on an entity type
-
-Rename propagation recomputes a dependent’s **unique attribute** (usually `**qualifiedName**`) by **parsing** the stored string against the type’s `**autoComputeFormat**`, **replacing** the segment that came from the renamed entity, and **rebuilding** the string. You supply that template on the **entity typedef** as an override on the unique attribute, for **any** entity type whose composite id should stay aligned after upstream renames.
-
-### Where to declare it
-
-In **bootstrap model JSON** add an `**attributeDefOverrides`** array on the `**entityDefs**` entry. Each override targets an attribute by name; set `**autoComputeFormat**` on the **unique** attribute (conventionally `**qualifiedName`**):
-
-```json
-{
-  "name": "my_column",
-  "superTypes": ["DataSet"],
-  "attributeDefOverrides": [
-    {
-      "name": "qualifiedName",
-      "autoComputeFormat": "{table.db.name}.{table.name}.{name}@{clusterName}"
-    }
-  ],
-  "attributeDefs": [ ]
-}
-```
-
-Atlas merges overrides with inherited definitions so the resolved `**AtlasEntityDef**` carries `**autoComputeFormat**` on `**qualifiedName**`.
-
-### Placeholder syntax
-
-- Use **curly braces** for each slot: `{segmentKey}`.
-- **Literals** between placeholders (for example `.`, `/`, `@`) are **delimiters**. When parsing an existing value, the character **immediately after** each `}` in the format marks where that slot ends in the stored string (the **last** placeholder consumes the rest of the string). Pick delimiters that do not appear inside real slot values, or parsing becomes ambiguous.
-- **Dots inside a key** are part of the key string only (they are not JSON nesting in the model file).
-
-### How to design the format for a new entity type
-
-Start from **one real qualified-name string** your connector already produces (for example `sales.orders@prod`). Notice how it is built from smaller parts and which **fixed characters** separate those parts (here: `.`, `.`, and `@`).
-
-1. Write the **same separators in the same order** in `**autoComputeFormat**`.
-2. Replace each **variable part** with `{...}`:
-   - **`{name}`** — this entity’s own **name** (always a single word inside the braces).
-   - **`{db.name}`**, **`{table.db.name}`**, and similar — each segment **before** the final `.name` is one **relationship hop** (the relationship attribute name on the typedef at that step). Together they lead to a related entity; the trailing `.name` means “use that entity’s **name** attribute.”
-   - **`{clusterName}`** (one word inside the braces) — a normal attribute on **this** same entity, not a relationship hop.
-
-Dots **inside** `{...}` are part of the placeholder key, not nested JSON in the model file. Choose separators that will not appear inside a single part of the real string, so parsing stays unambiguous.
-
-### Example: table under a database (Hive-style shape)
-
-If `**my_table**` links to a database via relationship attribute `**db**` and carries a cluster label `**clusterName**`, a common pattern is:
-
-```json
-"attributeDefOverrides": [
-  {
-    "name": "qualifiedName",
-    "autoComputeFormat": "{db.name}.{name}@{clusterName}"
-  }
-]
-```
-
-- `{db.name}` — database’s `**name**` reached through `**db**`.
-- `{name}` — this table’s `**name**`.
-- `@{clusterName}` — literal `@` then this entity’s `**clusterName**` attribute.
-
-### Example: deeper chain (Trino column)
-
-For `**trino_column**`, the unique string chains catalog, schema, table, column, and instance:
-
-```json
-"attributeDefOverrides": [
-  {
-    "name": "qualifiedName",
-    "autoComputeFormat": "{table.trinoschema.catalog.name}.{table.trinoschema.name}.{table.name}.{name}@{table.trinoschema.catalog.instance.name}"
-  }
-]
-```
-
-Meaning of each slot:
-
-- `{table.trinoschema.catalog.name}` — from this column, follow `**table**` → `**trinoschema**` → `**catalog**`, then use that catalog entity’s `**name**`.
-- `{table.trinoschema.name}` — schema `**name**` along the same branch.
-- `{table.name}` — table `**name**`.
-- `{name}` — this column’s own `**name**`.
-- `@{table.trinoschema.catalog.instance.name}` — after `@`, follow `**instance**` from the catalog and use that instance’s `**name**`.
-
----
-
-## Tests
-
-- `**AtlasTypeDefStoreInitializerTest**` — Patch handlers for overrides and `**SET_PROPAGATE_RENAME**`.
-- `**EntityRenameHandlerTest**` — End-to-end behavior: unique attribute recompute, mapped attributes, and multi-hop propagation through typedefs.
-
diff --git a/docs/src/documents/TypeSystem.md b/docs/src/documents/TypeSystem.md
index 7cfbc70d16..0c5dad69b4 100644
--- a/docs/src/documents/TypeSystem.md
+++ b/docs/src/documents/TypeSystem.md
@@ -204,3 +204,158 @@ example, an ETL process that transforms a hive table with raw data to another hi
 be a specific type that extends the Process type. A Process type has two specific attributes, inputs and outputs. Both
 inputs and outputs are arrays of DataSet entities. Thus, an instance of a Process type can use these inputs and outputs
 to capture how the lineage of a DataSet evolves.
+
+## Relationship propagation model extensions
+
+Atlas supports automatic propagation of rename and delete operations through relationships. This is driven by fields on
+**entity type definitions** and **relationship end definitions** in the model JSON.
+
+### Entity definition: attributeDefOverrides
+
+An entity type can declare `attributeDefOverrides` to specify how its unique attribute (qualifiedName) is composed from
+related entities. The `autoComputeFormat` template tells Atlas how to parse and rebuild the qualifiedName when an
+upstream entity is renamed.
+
+<SyntaxHighlighter wrapLines={true} language="json" style={theme.dark}>
+{`"entityDefs": [{
+    "name": "trino_table",
+    "superTypes": ["DataSet"],
+    "attributeDefOverrides": [
+        {
+            "name": "qualifiedName",
+            "autoComputeFormat": "{trinoschema.catalog.name}.{trinoschema.name}.{name}@{trinoschema.catalog.instance.name}"
+        }
+    ]
+}]`}
+</SyntaxHighlighter>
+
+Each `{placeholder}` maps to an attribute path: `{name}` refers to this entity's own name, while dotted paths like
+`{trinoschema.catalog.name}` traverse relationship attributes to reach a related entity's name.
+
+#### Placeholder syntax
+
+- Use **curly braces** for each slot: `{segmentKey}`.
+- **Literals** between placeholders (e.g. `.`, `/`, `@`) act as **delimiters**. When parsing an existing qualifiedName,
+  the character immediately after each `}` marks where that slot ends in the stored string. The **last** placeholder
+  consumes the rest of the string. Choose delimiters that do not appear inside real slot values to avoid ambiguity.
+- **Dots inside a key** (e.g. `{table.db.name}`) are part of the key string — they represent relationship hops, not
+  JSON nesting.
+
+#### How to design autoComputeFormat for a new entity type
+
+Start from **one real qualifiedName string** your connector already produces (e.g. `sales.orders@prod`). Notice how it
+is built from smaller parts and which **fixed characters** separate those parts (here `.` and `@`).
+
+1. Write the **same separators in the same order** in `autoComputeFormat`.
+2. Replace each **variable part** with a `{placeholder}`:
+   - **`{name}`** — this entity's own `name` attribute.
+   - **`{db.name}`**, **`{table.db.name}`**, etc. — each segment before the final `.name` is one **relationship hop**
+     (the relationship attribute name on the typedef at that step). Together they navigate to a related entity; the
+     trailing `.name` means "use that entity's `name` attribute."
+   - **`{clusterName}`** (single word, no dots) — a plain attribute on **this** entity, not a relationship hop.
+
+##### Example: table under a database (Hive-style)
+
+If `my_table` links to a database via relationship attribute `db` and carries a cluster label `clusterName`:
+
+<SyntaxHighlighter wrapLines={true} language="json" style={theme.dark}>
+{`"attributeDefOverrides": [
+    {
+        "name": "qualifiedName",
+        "autoComputeFormat": "{db.name}.{name}@{clusterName}"
+    }
+]`}
+</SyntaxHighlighter>
+
+- `{db.name}` — the database's `name`, reached through relationship attribute `db`.
+- `{name}` — this table's own `name`.
+- `@{clusterName}` — literal `@` followed by this entity's `clusterName` attribute.
+
+##### Example: deeper chain (Trino column)
+
+For `trino_column`, the qualifiedName chains catalog, schema, table, column, and instance:
+
+<SyntaxHighlighter wrapLines={true} language="json" style={theme.dark}>
+{`"attributeDefOverrides": [
+    {
+        "name": "qualifiedName",
+        "autoComputeFormat": "{table.trinoschema.catalog.name}.{table.trinoschema.name}.{table.name}.{name}@{table.trinoschema.catalog.instance.name}"
+    }
+]`}
+</SyntaxHighlighter>
+
+- `{table.trinoschema.catalog.name}` — from this column, follow `table` → `trinoschema` → `catalog`, use that entity's `name`.
+- `{table.trinoschema.name}` — schema `name` along the same path.
+- `{table.name}` — the parent table's `name`.
+- `{name}` — this column's own `name`.
+- `@{table.trinoschema.catalog.instance.name}` — after `@`, follow `instance` from the catalog and use its `name`.
+
+
+### Relationship end definition: propagateRename
+
+Setting `propagateRename: true` on a relationship end marks that end as the **trigger** for rename propagation. When an
+entity on that end is renamed, Atlas traverses the relationship to dependent entities and recomputes their qualifiedName
+using their `autoComputeFormat`.
+
+<SyntaxHighlighter wrapLines={true} language="json" style={theme.dark}>
+{`"relationshipDefs": [{
+    "name": "trino_table_hive_table",
+    "endDef1": {
+        "type": "hive_table",
+        "name": "trinoTables",
+        "propagateRename": true
+    },
+    "endDef2": {
+        "type": "trino_table",
+        "name": "hiveTable"
+    }
+}]`}
+</SyntaxHighlighter>
+
+An optional `propagateAttributes` list on the same end can map source attributes to target attributes for cases where
+simple template substitution is not sufficient:
+
+<SyntaxHighlighter wrapLines={true} language="json" style={theme.dark}>
+{`"endDef1": {
+    "type": "hive_table",
+    "name": "trinoTables",
+    "propagateRename": true,
+    "propagateAttributes": [
+        { "source": "name", "target": "hiveTableName" }
+    ]
+}`}
+</SyntaxHighlighter>
+
+### Relationship end definition: propagateDelete
+
+Setting `propagateDelete: true` on a relationship end marks that end as the **trigger** for delete propagation. When an
+entity on that end is deleted, Atlas propagates the deletion to dependent entities reached through the relationship.
+
+<SyntaxHighlighter wrapLines={true} language="json" style={theme.dark}>
+{`"relationshipDefs": [{
+    "name": "trino_table_hive_table",
+    "endDef1": {
+        "type": "hive_table",
+        "name": "trinoTables",
+        "propagateRename": true,
+        "propagateDelete": true
+    },
+    "endDef2": {
+        "type": "trino_table",
+        "name": "hiveTable"
+    }
+}]`}
+</SyntaxHighlighter>
+
+In this example, deleting a `hive_table` entity will automatically propagate deletion to any linked `trino_table`
+entities. If those targets also have delete propagation configured on their own relationships (e.g.,
+`trino_table_columns`), the propagation continues recursively.
+
+### Summary of propagation fields
+
+| Field | Location | Purpose |
+|-------|----------|---------|
+| `attributeDefOverrides.autoComputeFormat` | Entity definition | Template for computing qualifiedName from related entities |
+| `propagateRename` | Relationship end definition | Triggers qualifiedName recomputation on dependents when this end is renamed |
+| `propagateAttributes` | Relationship end definition | Maps source attributes to target attributes during rename |
+| `propagateDelete` | Relationship end definition | Triggers propagation of deletion to dependents when this end is deleted |
\ No newline at end of file
diff --git a/intg/src/main/java/org/apache/atlas/model/typedef/AtlasRelationshipEndDef.java b/intg/src/main/java/org/apache/atlas/model/typedef/AtlasRelationshipEndDef.java
index c7921b6ca3..568b7535ff 100644
--- a/intg/src/main/java/org/apache/atlas/model/typedef/AtlasRelationshipEndDef.java
+++ b/intg/src/main/java/org/apache/atlas/model/typedef/AtlasRelationshipEndDef.java
@@ -86,6 +86,15 @@ public class AtlasRelationshipEndDef implements Serializable {
      * entity {@code name} attribute is synchronized.
      */
     private List<Map<String, String>> propagateAttributes;
+    /**
+     * When set, deleting an entity of this end's type cascades the delete to entities on the
+     * other end of the relationship. Use with caution: enabling this flag means ALL related
+     * entities reachable via this relationship will be deleted — the effect is recursive if
+     * the target type also has propagateDelete configured on its relationships. This is
+     * irreversible in a single transaction; ensure the modeled semantics truly require cascade
+     * (e.g., the target entity cannot meaningfully exist without the source).
+     */
+    private boolean propagateDelete;
 
     /**
      * Base constructor
@@ -177,6 +186,7 @@ public class AtlasRelationshipEndDef implements Serializable {
             setDescription(other.description);
             setIsPropagateRename(other.propagateRename);
             setPropagateAttributes(other.propagateAttributes);
+            setIsPropagateDelete(other.propagateDelete);
         }
     }
 
@@ -254,6 +264,7 @@ public class AtlasRelationshipEndDef implements Serializable {
         sb.append(", isLegacyAttribute==>'").append(isLegacyAttribute).append('\'');
         sb.append(", propagateRename==>'").append(propagateRename).append('\'');
         sb.append(", propagateAttributes==>'").append(propagateAttributes).append('\'');
+        sb.append(", propagateDelete==>'").append(propagateDelete).append('\'');
         sb.append('}');
 
         return sb;
@@ -261,7 +272,7 @@ public class AtlasRelationshipEndDef implements Serializable {
 
     @Override
     public int hashCode() {
-        return Objects.hash(type, getName(), description, isContainer, cardinality, isLegacyAttribute, propagateRename, propagateAttributes);
+        return Objects.hash(type, getName(), description, isContainer, cardinality, isLegacyAttribute, propagateRename, propagateAttributes, propagateDelete);
     }
 
     @Override
@@ -281,7 +292,8 @@ public class AtlasRelationshipEndDef implements Serializable {
                 cardinality == that.cardinality &&
                 isLegacyAttribute == that.isLegacyAttribute &&
                 propagateRename == that.propagateRename &&
-                Objects.equals(propagateAttributes, that.propagateAttributes);
+                Objects.equals(propagateAttributes, that.propagateAttributes) &&
+                propagateDelete == that.propagateDelete;
     }
 
     @JsonProperty("propagateRename")
@@ -302,6 +314,16 @@ public class AtlasRelationshipEndDef implements Serializable {
         this.propagateAttributes = (propagateAttributes != null) ? Collections.unmodifiableList(propagateAttributes) : null;
     }
 
+    @JsonProperty("propagateDelete")
+    public boolean getIsPropagateDelete() {
+        return propagateDelete;
+    }
+
+    @JsonProperty("propagateDelete")
+    public void setIsPropagateDelete(boolean propagateDelete) {
+        this.propagateDelete = propagateDelete;
+    }
+
     @Override
     public String toString() {
         return toString(new StringBuilder()).toString();
diff --git a/intg/src/main/java/org/apache/atlas/type/AtlasEntityType.java b/intg/src/main/java/org/apache/atlas/type/AtlasEntityType.java
index 9309d1c254..922aada3d4 100644
--- a/intg/src/main/java/org/apache/atlas/type/AtlasEntityType.java
+++ b/intg/src/main/java/org/apache/atlas/type/AtlasEntityType.java
@@ -116,6 +116,7 @@ public class AtlasEntityType extends AtlasStructType {
      * are resolved from the model.
      */
     private List<RenamePropagationTarget>                    renamePropagationTargets      = Collections.emptyList();
+    private List<DeletePropagationTarget>                    deletePropagationTargets      = Collections.emptyList();
     /**
      * For this type's qualifiedName {@code autoComputeFormat}: each key is another entity type name
      * that appears in the template (via relationship hops ending in {@code .name}); the value is the
@@ -267,6 +268,10 @@ public class AtlasEntityType extends AtlasStructType {
         return renamePropagationTargets;
     }
 
+    public List<DeletePropagationTarget> getDeletePropagationTargets() {
+        return deletePropagationTargets;
+    }
+
     public Map<String, String> getAutoComputeFormatPathByRefTypeNameMap() {
         return autoComputeFormatPathByRefTypeNameMap;
     }
@@ -403,6 +408,7 @@ public class AtlasEntityType extends AtlasStructType {
 
             tagPropagationEdges.addAll(superType.tagPropagationEdges);
             renamePropagationTargets.addAll(superType.renamePropagationTargets);
+            deletePropagationTargets.addAll(superType.deletePropagationTargets);
         }
 
         ownedRefAttributes = new ArrayList<>();
@@ -430,6 +436,7 @@ public class AtlasEntityType extends AtlasStructType {
         ownedRefAttributes       = Collections.unmodifiableList(ownedRefAttributes);
         tagPropagationEdges      = Collections.unmodifiableSet(tagPropagationEdges);
         renamePropagationTargets = Collections.unmodifiableList(renamePropagationTargets);
+        deletePropagationTargets = Collections.unmodifiableList(deletePropagationTargets);
 
         entityDef.setSubTypes(subTypes);
 
@@ -580,6 +587,7 @@ public class AtlasEntityType extends AtlasStructType {
         this.businessAttributes     = new HashMap<>(); // this will be populated in resolveReferences(), from AtlasBusinessMetadataType
         this.tagPropagationEdges    = new HashSet<>(); // this will be populated in resolveReferencesPhase2()
         this.renamePropagationTargets              = new ArrayList<>(); // this will be populated in resolveReferencesPhase2()
+        this.deletePropagationTargets              = new ArrayList<>(); // this will be populated in resolveReferencesPhase2()
         this.autoComputeFormatPathByRefTypeNameMap = new HashMap<>(); // this will be populated in resolveReferencesPhase3()
 
         this.typeAndAllSubTypes.add(this.getTypeName());
@@ -891,6 +899,7 @@ public class AtlasEntityType extends AtlasStructType {
         }
 
         addRenamePropagationTargetIfTriggered(relationshipType, attribute);
+        addDeletePropagationTargetIfTriggered(relationshipType, attribute);
     }
 
     /**
@@ -1122,6 +1131,54 @@ public class AtlasEntityType extends AtlasStructType {
         }
     }
 
+    private void addDeletePropagationTargetIfTriggered(AtlasRelationshipType relationshipType, AtlasAttribute relationshipAttribute) {
+        AtlasRelationshipEndDef endDef1 = relationshipType.getRelationshipDef().getEndDef1();
+        AtlasRelationshipEndDef endDef2 = relationshipType.getRelationshipDef().getEndDef2();
+
+        if (endDef1 == null || endDef2 == null) {
+            LOG.debug("addDeletePropagationTargetIfTriggered({}): relationship type '{}' missing endDef — skipping",
+                    getTypeName(), relationshipType.getTypeName());
+            return;
+        }
+
+        String thisTypeName = getTypeName();
+        boolean triggerOnEnd1 = StringUtils.equals(relationshipType.getEnd1Type().getTypeName(), thisTypeName) && endDef1.getIsPropagateDelete();
+        boolean triggerOnEnd2 = StringUtils.equals(relationshipType.getEnd2Type().getTypeName(), thisTypeName) && endDef2.getIsPropagateDelete();
+
+        if (!triggerOnEnd1 && !triggerOnEnd2) {
+            return;
+        }
+
+        String targetTypeName = triggerOnEnd1
+                ? relationshipType.getEnd2Type().getTypeName()
+                : relationshipType.getEnd1Type().getTypeName();
+
+        RelationshipCategory category = relationshipType.getRelationshipDef().getRelationshipCategory();
+        DeletePropagationTarget target = new DeletePropagationTarget(targetTypeName, relationshipAttribute);
+
+        String relAttrName = relationshipAttribute.getAttributeDef() != null
+                ? relationshipAttribute.getAttributeDef().getName()
+                : null;
+
+        if (!deletePropagationTargets.contains(target)) {
+            deletePropagationTargets.add(target);
+
+            LOG.info("addDeletePropagationTargetIfTriggered({}): delete propagation via relationship '{}' attribute '{}' -> target type '{}' (trigger {}, category {})",
+                    thisTypeName,
+                    relationshipType.getTypeName(),
+                    relAttrName,
+                    targetTypeName,
+                    triggerOnEnd1 ? "end1" : "end2",
+                    category);
+        } else {
+            LOG.debug("addDeletePropagationTargetIfTriggered({}): duplicate propagation target skipped — relationship '{}', attribute '{}', target type '{}'",
+                    thisTypeName,
+                    relationshipType.getTypeName(),
+                    relAttrName,
+                    targetTypeName);
+        }
+    }
+
     boolean isAssignableFrom(AtlasObjectId objId) {
         return AtlasTypeUtil.isValid(objId) && (StringUtils.equals(objId.getTypeName(), getTypeName()) || isSuperTypeOf(objId.getTypeName()));
     }
diff --git a/intg/src/main/java/org/apache/atlas/type/DeletePropagationTarget.java b/intg/src/main/java/org/apache/atlas/type/DeletePropagationTarget.java
new file mode 100644
index 0000000000..66d2674501
--- /dev/null
+++ b/intg/src/main/java/org/apache/atlas/type/DeletePropagationTarget.java
@@ -0,0 +1,59 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.apache.atlas.type;
+
+import java.util.Objects;
+
+/**
+ * Typedef-time representation of a delete propagation target for an entity type.
+ */
+public class DeletePropagationTarget {
+    private final String                         targetTypeName;
+    private final AtlasStructType.AtlasAttribute relAttr;
+
+    public DeletePropagationTarget(String targetTypeName, AtlasStructType.AtlasAttribute relAttr) {
+        this.targetTypeName = targetTypeName;
+        this.relAttr        = relAttr;
+    }
+
+    public String getTargetTypeName() {
+        return targetTypeName;
+    }
+
+    public AtlasStructType.AtlasAttribute getRelAttr() {
+        return relAttr;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        DeletePropagationTarget that = (DeletePropagationTarget) o;
+        return Objects.equals(targetTypeName, that.targetTypeName)
+                && relAttr == that.relAttr;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(targetTypeName, System.identityHashCode(relAttr));
+    }
+}
diff --git a/intg/src/test/java/org/apache/atlas/model/typedef/TestAtlasRelationshipEndDef.java b/intg/src/test/java/org/apache/atlas/model/typedef/TestAtlasRelationshipEndDef.java
index 1185275019..1eabbcf848 100644
--- a/intg/src/test/java/org/apache/atlas/model/typedef/TestAtlasRelationshipEndDef.java
+++ b/intg/src/test/java/org/apache/atlas/model/typedef/TestAtlasRelationshipEndDef.java
@@ -363,4 +363,70 @@ public class TestAtlasRelationshipEndDef {
         endDef.setIsLegacyAttribute(false);
         assertFalse(endDef.getIsLegacyAttribute());
     }
+
+    @Test
+    public void testPropagateDeleteDefaultFalse() {
+        AtlasRelationshipEndDef endDef = new AtlasRelationshipEndDef();
+
+        assertFalse(endDef.getIsPropagateDelete());
+    }
+
+    @Test
+    public void testPropagateDeleteSetterGetter() {
+        AtlasRelationshipEndDef endDef = new AtlasRelationshipEndDef();
+
+        endDef.setIsPropagateDelete(true);
+        assertTrue(endDef.getIsPropagateDelete());
+
+        endDef.setIsPropagateDelete(false);
+        assertFalse(endDef.getIsPropagateDelete());
+    }
+
+    @Test
+    public void testPropagateDeleteCopyConstructor() {
+        AtlasRelationshipEndDef original = new AtlasRelationshipEndDef("Type1", "attr1", Cardinality.SINGLE);
+        original.setIsPropagateDelete(true);
+
+        AtlasRelationshipEndDef copy = new AtlasRelationshipEndDef(original);
+
+        assertTrue(copy.getIsPropagateDelete());
+    }
+
+    @Test
+    public void testPropagateDeleteEquality() {
+        AtlasRelationshipEndDef endDef1 = new AtlasRelationshipEndDef("Type1", "attr1", Cardinality.SINGLE);
+        AtlasRelationshipEndDef endDef2 = new AtlasRelationshipEndDef("Type1", "attr1", Cardinality.SINGLE);
+
+        assertEquals(endDef1, endDef2);
+
+        endDef1.setIsPropagateDelete(true);
+        assertNotEquals(endDef1, endDef2);
+
+        endDef2.setIsPropagateDelete(true);
+        assertEquals(endDef1, endDef2);
+        assertEquals(endDef1.hashCode(), endDef2.hashCode());
+    }
+
+    @Test
+    public void testPropagateDeleteSerialization() {
+        AtlasRelationshipEndDef original = new AtlasRelationshipEndDef("Database", "tables", Cardinality.SET, true);
+        original.setIsPropagateDelete(true);
+
+        String jsonString = AtlasType.toJson(original);
+        assertTrue(jsonString.contains("propagateDelete"));
+
+        AtlasRelationshipEndDef deserialized = AtlasType.fromJson(jsonString, AtlasRelationshipEndDef.class);
+        assertTrue(deserialized.getIsPropagateDelete());
+        assertEquals(deserialized, original);
+    }
+
+    @Test
+    public void testPropagateDeleteToString() {
+        AtlasRelationshipEndDef endDef = new AtlasRelationshipEndDef("Type1", "attr1", Cardinality.SINGLE);
+        endDef.setIsPropagateDelete(true);
+
+        String str = endDef.toString();
+        assertTrue(str.contains("propagateDelete"));
+        assertTrue(str.contains("true"));
+    }
 }
diff --git a/intg/src/test/java/org/apache/atlas/type/TestDeletePropagationTarget.java b/intg/src/test/java/org/apache/atlas/type/TestDeletePropagationTarget.java
new file mode 100644
index 0000000000..6a3120d24f
--- /dev/null
+++ b/intg/src/test/java/org/apache/atlas/type/TestDeletePropagationTarget.java
@@ -0,0 +1,122 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.apache.atlas.type;
+
+import org.apache.atlas.type.AtlasStructType.AtlasAttribute;
+import org.testng.annotations.Test;
+
+import static org.mockito.Mockito.mock;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNotEquals;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertTrue;
+
+public class TestDeletePropagationTarget {
+    @Test
+    public void testConstructorAndGetters() {
+        AtlasAttribute relAttr = mock(AtlasAttribute.class);
+
+        DeletePropagationTarget target = new DeletePropagationTarget("trino_table", relAttr);
+
+        assertEquals(target.getTargetTypeName(), "trino_table");
+        assertEquals(target.getRelAttr(), relAttr);
+    }
+
+    @Test
+    public void testConstructorWithNullValues() {
+        DeletePropagationTarget target = new DeletePropagationTarget(null, null);
+
+        assertNull(target.getTargetTypeName());
+        assertNull(target.getRelAttr());
+    }
+
+    @Test
+    public void testEqualsSameInstance() {
+        AtlasAttribute relAttr = mock(AtlasAttribute.class);
+        DeletePropagationTarget target = new DeletePropagationTarget("trino_table", relAttr);
+
+        assertTrue(target.equals(target));
+    }
+
+    @Test
+    public void testEqualsSameValues() {
+        AtlasAttribute relAttr = mock(AtlasAttribute.class);
+        DeletePropagationTarget target1 = new DeletePropagationTarget("trino_table", relAttr);
+        DeletePropagationTarget target2 = new DeletePropagationTarget("trino_table", relAttr);
+
+        assertEquals(target1, target2);
+        assertEquals(target1.hashCode(), target2.hashCode());
+    }
+
+    @Test
+    public void testEqualsIdentityOnRelAttr() {
+        AtlasAttribute relAttr1 = mock(AtlasAttribute.class);
+        AtlasAttribute relAttr2 = mock(AtlasAttribute.class);
+        DeletePropagationTarget target1 = new DeletePropagationTarget("trino_table", relAttr1);
+        DeletePropagationTarget target2 = new DeletePropagationTarget("trino_table", relAttr2);
+
+        assertNotEquals(target1, target2);
+    }
+
+    @Test
+    public void testEqualsDifferentTypeName() {
+        AtlasAttribute relAttr = mock(AtlasAttribute.class);
+        DeletePropagationTarget target1 = new DeletePropagationTarget("trino_table", relAttr);
+        DeletePropagationTarget target2 = new DeletePropagationTarget("trino_schema", relAttr);
+
+        assertNotEquals(target1, target2);
+    }
+
+    @Test
+    public void testEqualsNull() {
+        AtlasAttribute relAttr = mock(AtlasAttribute.class);
+        DeletePropagationTarget target = new DeletePropagationTarget("trino_table", relAttr);
+
+        assertFalse(target.equals(null));
+    }
+
+    @Test
+    public void testEqualsDifferentClass() {
+        AtlasAttribute relAttr = mock(AtlasAttribute.class);
+        DeletePropagationTarget target = new DeletePropagationTarget("trino_table", relAttr);
+
+        assertFalse(target.equals("not a target"));
+    }
+
+    @Test
+    public void testHashCodeConsistency() {
+        AtlasAttribute relAttr = mock(AtlasAttribute.class);
+        DeletePropagationTarget target = new DeletePropagationTarget("trino_table", relAttr);
+
+        int hash1 = target.hashCode();
+        int hash2 = target.hashCode();
+
+        assertEquals(hash1, hash2);
+    }
+
+    @Test
+    public void testHashCodeDifferentForDifferentTargets() {
+        AtlasAttribute relAttr1 = mock(AtlasAttribute.class);
+        AtlasAttribute relAttr2 = mock(AtlasAttribute.class);
+        DeletePropagationTarget target1 = new DeletePropagationTarget("trino_table", relAttr1);
+        DeletePropagationTarget target2 = new DeletePropagationTarget("trino_schema", relAttr2);
+
+        assertNotEquals(target1.hashCode(), target2.hashCode());
+    }
+}
diff --git a/repository/src/main/java/org/apache/atlas/repository/store/bootstrap/AtlasTypeDefStoreInitializer.java b/repository/src/main/java/org/apache/atlas/repository/store/bootstrap/AtlasTypeDefStoreInitializer.java
index b7483f4ba8..31e748096c 100644
--- a/repository/src/main/java/org/apache/atlas/repository/store/bootstrap/AtlasTypeDefStoreInitializer.java
+++ b/repository/src/main/java/org/apache/atlas/repository/store/bootstrap/AtlasTypeDefStoreInitializer.java
@@ -465,7 +465,7 @@ public class AtlasTypeDefStoreInitializer implements ActiveStateChangeHandler {
                     new RemoveLegacyRefAttributesPatchHandler(typeDefStore, typeRegistry),
                     new UpdateTypeDefOptionsPatchHandler(typeDefStore, typeRegistry),
                     new SetServiceTypePatchHandler(typeDefStore, typeRegistry),
-                    new AttributeDefOverridesAndPropagateRenamePatchHandler(typeDefStore, typeRegistry),
+                    new AttributeDefOverridesAndOperationPropagationPatchHandler(typeDefStore, typeRegistry),
                     new UpdateAttributeMetadataHandler(typeDefStore, typeRegistry, graph),
                     new AddSuperTypePatchHandler(typeDefStore, typeRegistry),
                     new AddMandatoryAttributePatchHandler(typeDefStore, typeRegistry)
@@ -1184,19 +1184,20 @@ public class AtlasTypeDefStoreInitializer implements ActiveStateChangeHandler {
     }
 
     /**
-     * Handles {@code SET_ATTRIBUTE_DEF_OVERRIDES} on entity typedefs and {@code SET_PROPAGATE_RENAME} on relationship typedef end defs.
+     * Handles {@code SET_ATTRIBUTE_DEF_OVERRIDES} on entity typedefs, {@code SET_PROPAGATE_RENAME} and {@code SET_PROPAGATE_DELETE} on relationship typedef end defs.
      * Optional {@code params.propagateAttributes} for {@code SET_PROPAGATE_RENAME}: a JSON array of objects, each copied to a {@code HashMap<String,String>} on the patched end.
      */
-    static class AttributeDefOverridesAndPropagateRenamePatchHandler extends PatchHandler {
+    static class AttributeDefOverridesAndOperationPropagationPatchHandler extends PatchHandler {
         private static final String ACTION_SET_ATTRIBUTE_DEF_OVERRIDES = "SET_ATTRIBUTE_DEF_OVERRIDES";
         private static final String ACTION_SET_PROPAGATE_RENAME        = "SET_PROPAGATE_RENAME";
+        private static final String ACTION_SET_PROPAGATE_DELETE        = "SET_PROPAGATE_DELETE";
         /** Patch JSON key under {@code params}: value is {@code "endDef1"} or {@code "endDef2"} (string, not numeric). */
         private static final String PARAM_END_DEF_TOKEN                = "endDefToken";
         /** Optional: JSON array of objects under {@code params}; each object becomes one {@code Map<String,String>}. */
         private static final String PARAM_PROPAGATE_ATTRIBUTES         = "propagateAttributes";
 
-        public AttributeDefOverridesAndPropagateRenamePatchHandler(AtlasTypeDefStore typeDefStore, AtlasTypeRegistry typeRegistry) {
-            super(typeDefStore, typeRegistry, new String[] {ACTION_SET_ATTRIBUTE_DEF_OVERRIDES, ACTION_SET_PROPAGATE_RENAME});
+        public AttributeDefOverridesAndOperationPropagationPatchHandler(AtlasTypeDefStore typeDefStore, AtlasTypeRegistry typeRegistry) {
+            super(typeDefStore, typeRegistry, new String[] {ACTION_SET_ATTRIBUTE_DEF_OVERRIDES, ACTION_SET_PROPAGATE_RENAME, ACTION_SET_PROPAGATE_DELETE});
         }
 
         @Override
@@ -1215,7 +1216,7 @@ public class AtlasTypeDefStoreInitializer implements ActiveStateChangeHandler {
                 return SKIPPED;
             }
 
-            LOG.debug("AttributeDefOverridesAndPropagateRenamePatchHandler.applyPatch(): id={}; action={}; typeName={}",
+            LOG.debug("AttributeDefOverridesAndOperationPropagationPatchHandler.applyPatch(): id={}; action={}; typeName={}",
                     patch.getId(), patch.getAction(), typeName);
 
             String action = patch.getAction();
@@ -1234,6 +1235,13 @@ public class AtlasTypeDefStoreInitializer implements ActiveStateChangeHandler {
                 }
 
                 return applyPropagateRename(patch, (AtlasRelationshipDef) typeDef);
+            } else if (ACTION_SET_PROPAGATE_DELETE.equals(action)) {
+                if (!(typeDef instanceof AtlasRelationshipDef)) {
+                    throw new AtlasBaseException(AtlasErrorCode.PATCH_NOT_APPLICABLE_FOR_TYPE, patch.getAction(),
+                            typeDef.getClass().getSimpleName());
+                }
+
+                return applyPropagateDelete(patch, (AtlasRelationshipDef) typeDef);
             }
 
             throw new AtlasBaseException(AtlasErrorCode.PATCH_INVALID_DATA, patch.getAction(), typeName);
@@ -1247,7 +1255,7 @@ public class AtlasTypeDefStoreInitializer implements ActiveStateChangeHandler {
 
             int overrideCount = CollectionUtils.isEmpty(patch.getAttributeDefs()) ? 0 : patch.getAttributeDefs().size();
 
-            LOG.debug("AttributeDefOverridesAndPropagateRenamePatchHandler.applyEntityDefOverrides(): entityType={}; overrideCount={}; updateToVersion={}",
+            LOG.debug("AttributeDefOverridesAndOperationPropagationPatchHandler.applyEntityDefOverrides(): entityType={}; overrideCount={}; updateToVersion={}",
                     typeDef.getName(), overrideCount, patch.getUpdateToVersion());
 
             typeDefStore.updateEntityDefByName(typeDef.getName(), updatedDef);
@@ -1290,7 +1298,7 @@ public class AtlasTypeDefStoreInitializer implements ActiveStateChangeHandler {
 
             updatedDef.setTypeVersion(patch.getUpdateToVersion());
 
-            LOG.debug("AttributeDefOverridesAndPropagateRenamePatchHandler.applyPropagateRename(): relationshipType={}; endDefToken={}; updateToVersion={}",
+            LOG.debug("AttributeDefOverridesAndOperationPropagationPatchHandler.applyPropagateRename(): relationshipType={}; endDefToken={}; updateToVersion={}",
                     typeDef.getName(), endDefToken, patch.getUpdateToVersion());
 
             typeDefStore.updateRelationshipDefByName(typeDef.getName(), updatedDef);
@@ -1301,6 +1309,43 @@ public class AtlasTypeDefStoreInitializer implements ActiveStateChangeHandler {
             return APPLIED;
         }
 
+        private PatchStatus applyPropagateDelete(TypeDefPatch patch, AtlasRelationshipDef typeDef) throws AtlasBaseException {
+            Object endDefObj = patch.getParams() != null ? patch.getParams().get(PARAM_END_DEF_TOKEN) : null;
+            String endDefToken = endDefObj != null ? String.valueOf(endDefObj).trim() : null;
+            boolean useEndDef2 = "endDef2".equalsIgnoreCase(endDefToken);
+            boolean useEndDef1 = "endDef1".equalsIgnoreCase(endDefToken);
+
+            if (endDefToken == null || (!useEndDef1 && !useEndDef2)) {
+                throw new AtlasBaseException(AtlasErrorCode.INVALID_PARAMETERS,
+                        "SET_PROPAGATE_DELETE patch for '" + typeDef.getName()
+                                + "' must set params." + PARAM_END_DEF_TOKEN + " to the string \"endDef1\" or \"endDef2\", got: "
+                                + endDefToken);
+            }
+
+            AtlasRelationshipDef updatedDef = new AtlasRelationshipDef(typeDef);
+
+            if (useEndDef2) {
+                AtlasRelationshipEndDef end2 = new AtlasRelationshipEndDef(updatedDef.getEndDef2());
+
+                end2.setIsPropagateDelete(true);
+                updatedDef.setEndDef2(end2);
+            } else {
+                AtlasRelationshipEndDef end1 = new AtlasRelationshipEndDef(updatedDef.getEndDef1());
+
+                end1.setIsPropagateDelete(true);
+                updatedDef.setEndDef1(end1);
+            }
+
+            updatedDef.setTypeVersion(patch.getUpdateToVersion());
+
+            typeDefStore.updateRelationshipDefByName(typeDef.getName(), updatedDef);
+
+            LOG.info("patch applied: id={}; action={}; relationshipType={}; endDefToken={}; typeVersion={}",
+                    patch.getId(), ACTION_SET_PROPAGATE_DELETE, typeDef.getName(), endDefToken, patch.getUpdateToVersion());
+
+            return APPLIED;
+        }
+
         /** If {@code params} contains {@code propagateAttributes}, coerces via {@link AtlasJson#getMapper()} {@code convertValue} to {@code List<Map<String,String>>}. */
         private static void setPropagateAttributesFromParamsIfPresent(Map<String, Object> params, AtlasRelationshipEndDef endDef) throws AtlasBaseException {
             if (MapUtils.isEmpty(params) || !params.containsKey(PARAM_PROPAGATE_ATTRIBUTES)) {
diff --git a/repository/src/main/java/org/apache/atlas/repository/store/graph/v1/DeleteHandlerV1.java b/repository/src/main/java/org/apache/atlas/repository/store/graph/v1/DeleteHandlerV1.java
index 5041d28747..a34e4e3906 100644
--- a/repository/src/main/java/org/apache/atlas/repository/store/graph/v1/DeleteHandlerV1.java
+++ b/repository/src/main/java/org/apache/atlas/repository/store/graph/v1/DeleteHandlerV1.java
@@ -39,6 +39,7 @@ import org.apache.atlas.repository.graphdb.AtlasGraph;
 import org.apache.atlas.repository.graphdb.AtlasUniqueKeyHandler;
 import org.apache.atlas.repository.graphdb.AtlasVertex;
 import org.apache.atlas.repository.store.graph.v2.AtlasGraphUtilsV2;
+import org.apache.atlas.repository.store.graph.v2.EntityDeletePropagationHandler;
 import org.apache.atlas.repository.store.graph.v2.EntityGraphRetriever;
 import org.apache.atlas.repository.store.graph.v2.tasks.ClassificationTask;
 import org.apache.atlas.tasks.TaskManagement;
@@ -143,6 +144,7 @@ public abstract class DeleteHandlerV1 {
     private final boolean              softDelete;
     private final TaskManagement       taskManagement;
     private final AtlasUniqueKeyHandler uniqueKeyHandler;
+    private final EntityDeletePropagationHandler deletePropagationHandler;
 
     public DeleteHandlerV1(AtlasGraph graph, AtlasTypeRegistry typeRegistry, boolean shouldUpdateInverseReference, boolean softDelete, TaskManagement taskManagement) {
         this.typeRegistry                  = typeRegistry;
@@ -152,6 +154,7 @@ public abstract class DeleteHandlerV1 {
         this.softDelete                    = softDelete;
         this.taskManagement                = taskManagement;
         this.uniqueKeyHandler              = graph.getUniqueKeyHandler();
+        this.deletePropagationHandler      = new EntityDeletePropagationHandler(typeRegistry);
     }
 
     /**
@@ -215,9 +218,46 @@ public abstract class DeleteHandlerV1 {
                 getColumnLineageEntities(instanceVertex, deletionCandidateVertices);
             }
         }
+
+        // Propagation applies only during delete (ACTIVE→DELETED), not purge (cleanup of already-DELETED entities)
+        if (!requestContext.isPurgeRequested()) {
+            collectDeletePropagationCandidates(requestContext, deletionCandidateVertices, instanceVertexGuids);
+        }
+
         return deletionCandidateVertices;
     }
 
+    private void collectDeletePropagationCandidates(RequestContext requestContext, Set<AtlasVertex> deletionCandidateVertices,
+                                                    Set<String> visitedGuids) throws AtlasBaseException {
+        Set<AtlasVertex> propagationSources = new HashSet<>(deletionCandidateVertices);
+
+        for (AtlasVertex vertex : propagationSources) {
+            String          typeName   = GraphHelper.getTypeName(vertex);
+            AtlasEntityType entityType = typeRegistry.getEntityTypeByName(typeName);
+
+            if (entityType == null || CollectionUtils.isEmpty(entityType.getDeletePropagationTargets())) {
+                continue;
+            }
+
+            LOG.debug("collectDeletePropagationCandidates(): checking propagation targets for entity type='{}', guid='{}'",
+                    typeName, AtlasGraphUtilsV2.getIdFromVertex(vertex));
+
+            Set<AtlasVertex> propagatedVertices = deletePropagationHandler.collectPropagatedVertices(vertex, entityType, visitedGuids);
+
+            for (AtlasVertex propagatedVertex : propagatedVertices) {
+                for (GraphHelper.VertexInfo vertexInfo : getOwnedVertices(propagatedVertex)) {
+                    requestContext.recordEntityDelete(vertexInfo.getEntity());
+                    deletionCandidateVertices.add(vertexInfo.getVertex());
+                }
+            }
+        }
+
+        if (deletionCandidateVertices.size() > propagationSources.size()) {
+            LOG.info("collectDeletePropagationCandidates(): added {} entities via delete propagation",
+                    deletionCandidateVertices.size() - propagationSources.size());
+        }
+    }
+
     /*
         actually delete traits and then the vertex along its references
     */
diff --git a/repository/src/main/java/org/apache/atlas/repository/store/graph/v2/EntityDeletePropagationHandler.java b/repository/src/main/java/org/apache/atlas/repository/store/graph/v2/EntityDeletePropagationHandler.java
new file mode 100644
index 0000000000..a5c45b870a
--- /dev/null
+++ b/repository/src/main/java/org/apache/atlas/repository/store/graph/v2/EntityDeletePropagationHandler.java
@@ -0,0 +1,133 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.apache.atlas.repository.store.graph.v2;
+
+import org.apache.atlas.model.instance.AtlasEntity;
+import org.apache.atlas.repository.graph.GraphHelper;
+import org.apache.atlas.repository.graphdb.AtlasEdge;
+import org.apache.atlas.repository.graphdb.AtlasVertex;
+import org.apache.atlas.type.AtlasEntityType;
+import org.apache.atlas.type.AtlasStructType.AtlasAttribute;
+import org.apache.atlas.type.AtlasTypeRegistry;
+import org.apache.atlas.type.DeletePropagationTarget;
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+import javax.inject.Inject;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+@Component
+public class EntityDeletePropagationHandler {
+    private static final Logger LOG = LoggerFactory.getLogger(EntityDeletePropagationHandler.class);
+
+    private final AtlasTypeRegistry typeRegistry;
+
+    @Inject
+    public EntityDeletePropagationHandler(AtlasTypeRegistry typeRegistry) {
+        this.typeRegistry = typeRegistry;
+    }
+
+    public Set<AtlasVertex> collectPropagatedVertices(AtlasVertex sourceVertex, AtlasEntityType entityType, Set<String> visitedGuids) {
+        Set<AtlasVertex> propagatedVertices = new HashSet<>();
+
+        collectPropagatedVerticesRecursive(sourceVertex, entityType, visitedGuids, propagatedVertices);
+
+        return propagatedVertices;
+    }
+
+    private void collectPropagatedVerticesRecursive(AtlasVertex sourceVertex, AtlasEntityType sourceEntityType,
+                                                    Set<String> visitedGuids, Set<AtlasVertex> propagatedVertices) {
+        for (DeletePropagationTarget propagationTarget : sourceEntityType.getDeletePropagationTargets()) {
+            AtlasAttribute relAttr = propagationTarget.getRelAttr();
+
+            if (relAttr == null) {
+                continue;
+            }
+
+            for (AtlasEdge edge : toList(GraphHelper.getEdgesForLabel(sourceVertex, relAttr.getRelationshipEdgeLabel(), relAttr.getRelationshipEdgeDirection()))) {
+                if (GraphHelper.getStatus(edge) == AtlasEntity.Status.DELETED) {
+                    LOG.debug("collectPropagatedVertices(): skip — soft-deleted edge (label={})", relAttr.getRelationshipEdgeLabel());
+                    continue;
+                }
+
+                AtlasVertex targetVertex = getOtherVertex(edge, sourceVertex);
+
+                if (targetVertex == null) {
+                    continue;
+                }
+
+                if (GraphHelper.getStatus(targetVertex) == AtlasEntity.Status.DELETED) {
+                    LOG.debug("collectPropagatedVertices(): skip — already-deleted target vertex (guid={})",
+                            AtlasGraphUtilsV2.getIdFromVertex(targetVertex));
+                    continue;
+                }
+
+                String targetGuid = AtlasGraphUtilsV2.getIdFromVertex(targetVertex);
+
+                if (StringUtils.isBlank(targetGuid) || !visitedGuids.add(targetGuid)) {
+                    LOG.debug("collectPropagatedVertices(): skip — already visited (guid={})", targetGuid);
+                    continue;
+                }
+
+                String          targetTypeName   = GraphHelper.getTypeName(targetVertex);
+                AtlasEntityType targetEntityType = typeRegistry.getEntityTypeByName(targetTypeName);
+
+                if (targetEntityType == null) {
+                    LOG.debug("collectPropagatedVertices(): skip — no typedef for target type '{}' (guid={})", targetTypeName, targetGuid);
+                    continue;
+                }
+
+                propagatedVertices.add(targetVertex);
+
+                LOG.info("collectPropagatedVertices(): propagating delete from '{}' to '{}' (guid={})",
+                        sourceEntityType.getTypeName(), targetTypeName, targetGuid);
+
+                if (CollectionUtils.isNotEmpty(targetEntityType.getDeletePropagationTargets())) {
+                    collectPropagatedVerticesRecursive(targetVertex, targetEntityType, visitedGuids, propagatedVertices);
+                }
+            }
+        }
+    }
+
+    private AtlasVertex getOtherVertex(AtlasEdge edge, AtlasVertex anchor) {
+        AtlasVertex out = edge.getOutVertex();
+        AtlasVertex in  = edge.getInVertex();
+
+        return out != null && out.equals(anchor) ? in : out;
+    }
+
+    private <T> List<T> toList(Iterator<T> iterator) {
+        List<T> list = new ArrayList<>();
+
+        if (iterator != null) {
+            while (iterator.hasNext()) {
+                list.add(iterator.next());
+            }
+        }
+
+        return list;
+    }
+}
diff --git a/repository/src/test/java/org/apache/atlas/repository/store/bootstrap/AtlasTypeDefStoreInitializerTest.java b/repository/src/test/java/org/apache/atlas/repository/store/bootstrap/AtlasTypeDefStoreInitializerTest.java
index 8e43bcb1c2..6c1ce5fcfd 100644
--- a/repository/src/test/java/org/apache/atlas/repository/store/bootstrap/AtlasTypeDefStoreInitializerTest.java
+++ b/repository/src/test/java/org/apache/atlas/repository/store/bootstrap/AtlasTypeDefStoreInitializerTest.java
@@ -929,20 +929,21 @@ public class AtlasTypeDefStoreInitializerTest {
     }
 
     @Test
-    public void testAttributeDefOverridesAndPropagateRenamePatchHandlerSupportedActions() {
-        AtlasTypeDefStoreInitializer.AttributeDefOverridesAndPropagateRenamePatchHandler handler =
-                new AtlasTypeDefStoreInitializer.AttributeDefOverridesAndPropagateRenamePatchHandler(typeDefStore, typeRegistry);
+    public void testAttributeDefOverridesAndOperationPropagationPatchHandlerSupportedActions() {
+        AtlasTypeDefStoreInitializer.AttributeDefOverridesAndOperationPropagationPatchHandler handler =
+                new AtlasTypeDefStoreInitializer.AttributeDefOverridesAndOperationPropagationPatchHandler(typeDefStore, typeRegistry);
 
         String[] actions = handler.getSupportedActions();
-        assertEquals(actions.length, 2);
+        assertEquals(actions.length, 3);
         assertEquals(actions[0], "SET_ATTRIBUTE_DEF_OVERRIDES");
         assertEquals(actions[1], "SET_PROPAGATE_RENAME");
+        assertEquals(actions[2], "SET_PROPAGATE_DELETE");
     }
 
     @Test
-    public void testAttributeDefOverridesAndPropagateRenamePatchHandlerApplyOverrides() throws Exception {
-        AtlasTypeDefStoreInitializer.AttributeDefOverridesAndPropagateRenamePatchHandler handler =
-                new AtlasTypeDefStoreInitializer.AttributeDefOverridesAndPropagateRenamePatchHandler(typeDefStore, typeRegistry);
+    public void testAttributeDefOverridesAndOperationPropagationPatchHandlerApplyOverrides() throws Exception {
+        AtlasTypeDefStoreInitializer.AttributeDefOverridesAndOperationPropagationPatchHandler handler =
+                new AtlasTypeDefStoreInitializer.AttributeDefOverridesAndOperationPropagationPatchHandler(typeDefStore, typeRegistry);
 
         Object patch = createMockTypeDefPatch("SET_ATTRIBUTE_DEF_OVERRIDES", "TestEntity", "1.0", "2.0");
 
@@ -959,9 +960,9 @@ public class AtlasTypeDefStoreInitializerTest {
     }
 
     @Test
-    public void testAttributeDefOverridesAndPropagateRenamePatchHandlerApplyOverridesSkipped() throws Exception {
-        AtlasTypeDefStoreInitializer.AttributeDefOverridesAndPropagateRenamePatchHandler handler =
-                new AtlasTypeDefStoreInitializer.AttributeDefOverridesAndPropagateRenamePatchHandler(typeDefStore, typeRegistry);
+    public void testAttributeDefOverridesAndOperationPropagationPatchHandlerApplyOverridesSkipped() throws Exception {
+        AtlasTypeDefStoreInitializer.AttributeDefOverridesAndOperationPropagationPatchHandler handler =
+                new AtlasTypeDefStoreInitializer.AttributeDefOverridesAndOperationPropagationPatchHandler(typeDefStore, typeRegistry);
 
         Object patch = createMockTypeDefPatch("SET_ATTRIBUTE_DEF_OVERRIDES", "TestEntity", "2.0", "3.0");
 
@@ -977,9 +978,9 @@ public class AtlasTypeDefStoreInitializerTest {
     }
 
     @Test
-    public void testAttributeDefOverridesAndPropagateRenamePatchHandlerApplyOverridesWrongType() throws Exception {
-        AtlasTypeDefStoreInitializer.AttributeDefOverridesAndPropagateRenamePatchHandler handler =
-                new AtlasTypeDefStoreInitializer.AttributeDefOverridesAndPropagateRenamePatchHandler(typeDefStore, typeRegistry);
+    public void testAttributeDefOverridesAndOperationPropagationPatchHandlerApplyOverridesWrongType() throws Exception {
+        AtlasTypeDefStoreInitializer.AttributeDefOverridesAndOperationPropagationPatchHandler handler =
+                new AtlasTypeDefStoreInitializer.AttributeDefOverridesAndOperationPropagationPatchHandler(typeDefStore, typeRegistry);
 
         Object patch = createMockTypeDefPatch("SET_ATTRIBUTE_DEF_OVERRIDES", "TestRelationship", "1.0", "2.0");
 
@@ -997,9 +998,9 @@ public class AtlasTypeDefStoreInitializerTest {
     }
 
     @Test
-    public void testAttributeDefOverridesAndPropagateRenamePatchHandlerPropagateRenameEnd2WithPropagateAttributes() throws Exception {
-        AtlasTypeDefStoreInitializer.AttributeDefOverridesAndPropagateRenamePatchHandler handler =
-                new AtlasTypeDefStoreInitializer.AttributeDefOverridesAndPropagateRenamePatchHandler(typeDefStore, typeRegistry);
+    public void testAttributeDefOverridesAndOperationPropagationPatchHandlerPropagateRenameEnd2WithPropagateAttributes() throws Exception {
+        AtlasTypeDefStoreInitializer.AttributeDefOverridesAndOperationPropagationPatchHandler handler =
+                new AtlasTypeDefStoreInitializer.AttributeDefOverridesAndOperationPropagationPatchHandler(typeDefStore, typeRegistry);
 
         Object patch = createMockTypeDefPatch("SET_PROPAGATE_RENAME", "TestRelationship", "1.0", "2.0");
 
@@ -1031,9 +1032,9 @@ public class AtlasTypeDefStoreInitializerTest {
     }
 
     @Test
-    public void testAttributeDefOverridesAndPropagateRenamePatchHandlerPropagateRenamePropagateAttributesInvalidShape() throws Exception {
-        AtlasTypeDefStoreInitializer.AttributeDefOverridesAndPropagateRenamePatchHandler handler =
-                new AtlasTypeDefStoreInitializer.AttributeDefOverridesAndPropagateRenamePatchHandler(typeDefStore, typeRegistry);
+    public void testAttributeDefOverridesAndOperationPropagationPatchHandlerPropagateRenamePropagateAttributesInvalidShape() throws Exception {
+        AtlasTypeDefStoreInitializer.AttributeDefOverridesAndOperationPropagationPatchHandler handler =
+                new AtlasTypeDefStoreInitializer.AttributeDefOverridesAndOperationPropagationPatchHandler(typeDefStore, typeRegistry);
 
         Object patch = createMockTypeDefPatch("SET_PROPAGATE_RENAME", "TestRelationship", "1.0", "2.0");
 
@@ -1053,9 +1054,9 @@ public class AtlasTypeDefStoreInitializerTest {
     }
 
     @Test
-    public void testAttributeDefOverridesAndPropagateRenamePatchHandlerPropagateRenameEnd1() throws Exception {
-        AtlasTypeDefStoreInitializer.AttributeDefOverridesAndPropagateRenamePatchHandler handler =
-                new AtlasTypeDefStoreInitializer.AttributeDefOverridesAndPropagateRenamePatchHandler(typeDefStore, typeRegistry);
+    public void testAttributeDefOverridesAndOperationPropagationPatchHandlerPropagateRenameEnd1() throws Exception {
+        AtlasTypeDefStoreInitializer.AttributeDefOverridesAndOperationPropagationPatchHandler handler =
+                new AtlasTypeDefStoreInitializer.AttributeDefOverridesAndOperationPropagationPatchHandler(typeDefStore, typeRegistry);
 
         Object patch = createMockTypeDefPatch("SET_PROPAGATE_RENAME", "TestRelationship", "1.0", "2.0");
 
@@ -1076,9 +1077,9 @@ public class AtlasTypeDefStoreInitializerTest {
     }
 
     @Test
-    public void testAttributeDefOverridesAndPropagateRenamePatchHandlerPropagateRenameEnd2() throws Exception {
-        AtlasTypeDefStoreInitializer.AttributeDefOverridesAndPropagateRenamePatchHandler handler =
-                new AtlasTypeDefStoreInitializer.AttributeDefOverridesAndPropagateRenamePatchHandler(typeDefStore, typeRegistry);
+    public void testAttributeDefOverridesAndOperationPropagationPatchHandlerPropagateRenameEnd2() throws Exception {
+        AtlasTypeDefStoreInitializer.AttributeDefOverridesAndOperationPropagationPatchHandler handler =
+                new AtlasTypeDefStoreInitializer.AttributeDefOverridesAndOperationPropagationPatchHandler(typeDefStore, typeRegistry);
 
         Object patch = createMockTypeDefPatch("SET_PROPAGATE_RENAME", "TestRelationship", "1.0", "2.0");
 
@@ -1099,9 +1100,9 @@ public class AtlasTypeDefStoreInitializerTest {
     }
 
     @Test
-    public void testAttributeDefOverridesAndPropagateRenamePatchHandlerPropagateRenameInvalidEndDef() throws Exception {
-        AtlasTypeDefStoreInitializer.AttributeDefOverridesAndPropagateRenamePatchHandler handler =
-                new AtlasTypeDefStoreInitializer.AttributeDefOverridesAndPropagateRenamePatchHandler(typeDefStore, typeRegistry);
+    public void testAttributeDefOverridesAndOperationPropagationPatchHandlerPropagateRenameInvalidEndDef() throws Exception {
+        AtlasTypeDefStoreInitializer.AttributeDefOverridesAndOperationPropagationPatchHandler handler =
+                new AtlasTypeDefStoreInitializer.AttributeDefOverridesAndOperationPropagationPatchHandler(typeDefStore, typeRegistry);
 
         Object patch = createMockTypeDefPatch("SET_PROPAGATE_RENAME", "TestRelationship", "1.0", "2.0");
 
@@ -1120,9 +1121,9 @@ public class AtlasTypeDefStoreInitializerTest {
     }
 
     @Test
-    public void testAttributeDefOverridesAndPropagateRenamePatchHandlerPropagateRenameMissingParam() throws Exception {
-        AtlasTypeDefStoreInitializer.AttributeDefOverridesAndPropagateRenamePatchHandler handler =
-                new AtlasTypeDefStoreInitializer.AttributeDefOverridesAndPropagateRenamePatchHandler(typeDefStore, typeRegistry);
+    public void testAttributeDefOverridesAndOperationPropagationPatchHandlerPropagateRenameMissingParam() throws Exception {
+        AtlasTypeDefStoreInitializer.AttributeDefOverridesAndOperationPropagationPatchHandler handler =
+                new AtlasTypeDefStoreInitializer.AttributeDefOverridesAndOperationPropagationPatchHandler(typeDefStore, typeRegistry);
 
         Object patch = createMockTypeDefPatch("SET_PROPAGATE_RENAME", "TestRelationship", "1.0", "2.0");
 
@@ -1139,9 +1140,9 @@ public class AtlasTypeDefStoreInitializerTest {
     }
 
     @Test
-    public void testAttributeDefOverridesAndPropagateRenamePatchHandlerPropagateRenameWrongType() throws Exception {
-        AtlasTypeDefStoreInitializer.AttributeDefOverridesAndPropagateRenamePatchHandler handler =
-                new AtlasTypeDefStoreInitializer.AttributeDefOverridesAndPropagateRenamePatchHandler(typeDefStore, typeRegistry);
+    public void testAttributeDefOverridesAndOperationPropagationPatchHandlerPropagateRenameWrongType() throws Exception {
+        AtlasTypeDefStoreInitializer.AttributeDefOverridesAndOperationPropagationPatchHandler handler =
+                new AtlasTypeDefStoreInitializer.AttributeDefOverridesAndOperationPropagationPatchHandler(typeDefStore, typeRegistry);
 
         Object patch = createMockTypeDefPatch("SET_PROPAGATE_RENAME", "TestEntity", "1.0", "2.0");
 
@@ -1156,9 +1157,9 @@ public class AtlasTypeDefStoreInitializerTest {
     }
 
     @Test
-    public void testAttributeDefOverridesAndPropagateRenamePatchHandlerUnknownType() throws Exception {
-        AtlasTypeDefStoreInitializer.AttributeDefOverridesAndPropagateRenamePatchHandler handler =
-                new AtlasTypeDefStoreInitializer.AttributeDefOverridesAndPropagateRenamePatchHandler(typeDefStore, typeRegistry);
+    public void testAttributeDefOverridesAndOperationPropagationPatchHandlerUnknownType() throws Exception {
+        AtlasTypeDefStoreInitializer.AttributeDefOverridesAndOperationPropagationPatchHandler handler =
+                new AtlasTypeDefStoreInitializer.AttributeDefOverridesAndOperationPropagationPatchHandler(typeDefStore, typeRegistry);
 
         Object patch = createMockTypeDefPatch("SET_ATTRIBUTE_DEF_OVERRIDES", "UnknownType", "1.0", "2.0");
         when(typeRegistry.getTypeDefByName("UnknownType")).thenReturn(null);
@@ -1167,9 +1168,9 @@ public class AtlasTypeDefStoreInitializerTest {
     }
 
     @Test
-    public void testAttributeDefOverridesAndPropagateRenamePatchHandlerInvalidAction() throws Exception {
-        AtlasTypeDefStoreInitializer.AttributeDefOverridesAndPropagateRenamePatchHandler handler =
-                new AtlasTypeDefStoreInitializer.AttributeDefOverridesAndPropagateRenamePatchHandler(typeDefStore, typeRegistry);
+    public void testAttributeDefOverridesAndOperationPropagationPatchHandlerInvalidAction() throws Exception {
+        AtlasTypeDefStoreInitializer.AttributeDefOverridesAndOperationPropagationPatchHandler handler =
+                new AtlasTypeDefStoreInitializer.AttributeDefOverridesAndOperationPropagationPatchHandler(typeDefStore, typeRegistry);
 
         Object patch = createMockTypeDefPatch("SET_ATTRIBUTE_DEF_OVERRIDES", "TestEntity", "1.0", "2.0");
         setField(patch, "action", "NOT_A_SUPPORTED_PATCH_ACTION");
@@ -1180,6 +1181,159 @@ public class AtlasTypeDefStoreInitializerTest {
         expectThrows(AtlasBaseException.class, () -> handler.applyPatch((AtlasTypeDefStoreInitializer.TypeDefPatch) patch));
     }
 
+    @Test
+    public void testPropagateDeletePatchHandlerApplyEnd1() throws Exception {
+        AtlasTypeDefStoreInitializer.AttributeDefOverridesAndOperationPropagationPatchHandler handler =
+                new AtlasTypeDefStoreInitializer.AttributeDefOverridesAndOperationPropagationPatchHandler(typeDefStore, typeRegistry);
+
+        Object patch = createMockTypeDefPatch("SET_PROPAGATE_DELETE", "TestRelationship", "1.0", "2.0");
+
+        AtlasRelationshipDef relationshipDef = new AtlasRelationshipDef();
+        relationshipDef.setName("TestRelationship");
+        relationshipDef.setTypeVersion("1.0");
+        relationshipDef.setEndDef1(new AtlasRelationshipEndDef("E1", "a1", AtlasAttributeDef.Cardinality.SINGLE));
+        relationshipDef.setEndDef2(new AtlasRelationshipEndDef("E2", "a2", AtlasAttributeDef.Cardinality.SINGLE));
+        when(typeRegistry.getTypeDefByName("TestRelationship")).thenReturn(relationshipDef);
+
+        Map<String, Object> params = new HashMap<>();
+        params.put("endDefToken", "endDef1");
+        setField(patch, "params", params);
+
+        PatchStatus result = handler.applyPatch((AtlasTypeDefStoreInitializer.TypeDefPatch) patch);
+        assertEquals(result, APPLIED);
+
+        ArgumentCaptor<AtlasRelationshipDef> captor = ArgumentCaptor.forClass(AtlasRelationshipDef.class);
+        verify(typeDefStore).updateRelationshipDefByName(eq("TestRelationship"), captor.capture());
+        assertTrue(captor.getValue().getEndDef1().getIsPropagateDelete());
+        assertFalse(captor.getValue().getEndDef2().getIsPropagateDelete());
+    }
+
+    @Test
+    public void testPropagateDeletePatchHandlerApplyEnd2() throws Exception {
+        AtlasTypeDefStoreInitializer.AttributeDefOverridesAndOperationPropagationPatchHandler handler =
+                new AtlasTypeDefStoreInitializer.AttributeDefOverridesAndOperationPropagationPatchHandler(typeDefStore, typeRegistry);
+
+        Object patch = createMockTypeDefPatch("SET_PROPAGATE_DELETE", "TestRelationship", "1.0", "2.0");
+
+        AtlasRelationshipDef relationshipDef = new AtlasRelationshipDef();
+        relationshipDef.setName("TestRelationship");
+        relationshipDef.setTypeVersion("1.0");
+        relationshipDef.setEndDef1(new AtlasRelationshipEndDef("E1", "a1", AtlasAttributeDef.Cardinality.SINGLE));
+        relationshipDef.setEndDef2(new AtlasRelationshipEndDef("E2", "a2", AtlasAttributeDef.Cardinality.SINGLE));
+        when(typeRegistry.getTypeDefByName("TestRelationship")).thenReturn(relationshipDef);
+
+        Map<String, Object> params = new HashMap<>();
+        params.put("endDefToken", "endDef2");
+        setField(patch, "params", params);
+
+        PatchStatus result = handler.applyPatch((AtlasTypeDefStoreInitializer.TypeDefPatch) patch);
+        assertEquals(result, APPLIED);
+
+        ArgumentCaptor<AtlasRelationshipDef> captor = ArgumentCaptor.forClass(AtlasRelationshipDef.class);
+        verify(typeDefStore).updateRelationshipDefByName(eq("TestRelationship"), captor.capture());
+        assertFalse(captor.getValue().getEndDef1().getIsPropagateDelete());
+        assertTrue(captor.getValue().getEndDef2().getIsPropagateDelete());
+    }
+
+    @Test
+    public void testPropagateDeletePatchHandlerInvalidEndDef() throws Exception {
+        AtlasTypeDefStoreInitializer.AttributeDefOverridesAndOperationPropagationPatchHandler handler =
+                new AtlasTypeDefStoreInitializer.AttributeDefOverridesAndOperationPropagationPatchHandler(typeDefStore, typeRegistry);
+
+        Object patch = createMockTypeDefPatch("SET_PROPAGATE_DELETE", "TestRelationship", "1.0", "2.0");
+
+        AtlasRelationshipDef relationshipDef = new AtlasRelationshipDef();
+        relationshipDef.setName("TestRelationship");
+        relationshipDef.setTypeVersion("1.0");
+        relationshipDef.setEndDef1(new AtlasRelationshipEndDef("E1", "a1", AtlasAttributeDef.Cardinality.SINGLE));
+        relationshipDef.setEndDef2(new AtlasRelationshipEndDef("E2", "a2", AtlasAttributeDef.Cardinality.SINGLE));
+        when(typeRegistry.getTypeDefByName("TestRelationship")).thenReturn(relationshipDef);
+
+        Map<String, Object> params = new HashMap<>();
+        params.put("endDefToken", "endDef3");
+        setField(patch, "params", params);
+
+        expectThrows(AtlasBaseException.class, () -> handler.applyPatch((AtlasTypeDefStoreInitializer.TypeDefPatch) patch));
+    }
+
+    @Test
+    public void testPropagateDeletePatchHandlerMissingParam() throws Exception {
+        AtlasTypeDefStoreInitializer.AttributeDefOverridesAndOperationPropagationPatchHandler handler =
+                new AtlasTypeDefStoreInitializer.AttributeDefOverridesAndOperationPropagationPatchHandler(typeDefStore, typeRegistry);
+
+        Object patch = createMockTypeDefPatch("SET_PROPAGATE_DELETE", "TestRelationship", "1.0", "2.0");
+
+        AtlasRelationshipDef relationshipDef = new AtlasRelationshipDef();
+        relationshipDef.setName("TestRelationship");
+        relationshipDef.setTypeVersion("1.0");
+        relationshipDef.setEndDef1(new AtlasRelationshipEndDef("E1", "a1", AtlasAttributeDef.Cardinality.SINGLE));
+        relationshipDef.setEndDef2(new AtlasRelationshipEndDef("E2", "a2", AtlasAttributeDef.Cardinality.SINGLE));
+        when(typeRegistry.getTypeDefByName("TestRelationship")).thenReturn(relationshipDef);
+
+        setField(patch, "params", new HashMap<String, Object>());
+
+        expectThrows(AtlasBaseException.class, () -> handler.applyPatch((AtlasTypeDefStoreInitializer.TypeDefPatch) patch));
+    }
+
+    @Test
+    public void testPropagateDeletePatchHandlerNullParams() throws Exception {
+        AtlasTypeDefStoreInitializer.AttributeDefOverridesAndOperationPropagationPatchHandler handler =
+                new AtlasTypeDefStoreInitializer.AttributeDefOverridesAndOperationPropagationPatchHandler(typeDefStore, typeRegistry);
+
+        Object patch = createMockTypeDefPatch("SET_PROPAGATE_DELETE", "TestRelationship", "1.0", "2.0");
+
+        AtlasRelationshipDef relationshipDef = new AtlasRelationshipDef();
+        relationshipDef.setName("TestRelationship");
+        relationshipDef.setTypeVersion("1.0");
+        relationshipDef.setEndDef1(new AtlasRelationshipEndDef("E1", "a1", AtlasAttributeDef.Cardinality.SINGLE));
+        relationshipDef.setEndDef2(new AtlasRelationshipEndDef("E2", "a2", AtlasAttributeDef.Cardinality.SINGLE));
+        when(typeRegistry.getTypeDefByName("TestRelationship")).thenReturn(relationshipDef);
+
+        setField(patch, "params", null);
+
+        expectThrows(AtlasBaseException.class, () -> handler.applyPatch((AtlasTypeDefStoreInitializer.TypeDefPatch) patch));
+    }
+
+    @Test
+    public void testPropagateDeletePatchHandlerWrongType() throws Exception {
+        AtlasTypeDefStoreInitializer.AttributeDefOverridesAndOperationPropagationPatchHandler handler =
+                new AtlasTypeDefStoreInitializer.AttributeDefOverridesAndOperationPropagationPatchHandler(typeDefStore, typeRegistry);
+
+        Object patch = createMockTypeDefPatch("SET_PROPAGATE_DELETE", "TestEntity", "1.0", "2.0");
+
+        AtlasEntityDef entityDef = new AtlasEntityDef("TestEntity", "desc", "1.0");
+        when(typeRegistry.getTypeDefByName("TestEntity")).thenReturn(entityDef);
+
+        Map<String, Object> params = new HashMap<>();
+        params.put("endDefToken", "endDef1");
+        setField(patch, "params", params);
+
+        expectThrows(AtlasBaseException.class, () -> handler.applyPatch((AtlasTypeDefStoreInitializer.TypeDefPatch) patch));
+    }
+
+    @Test
+    public void testPropagateDeletePatchHandlerSkipped() throws Exception {
+        AtlasTypeDefStoreInitializer.AttributeDefOverridesAndOperationPropagationPatchHandler handler =
+                new AtlasTypeDefStoreInitializer.AttributeDefOverridesAndOperationPropagationPatchHandler(typeDefStore, typeRegistry);
+
+        Object patch = createMockTypeDefPatch("SET_PROPAGATE_DELETE", "TestRelationship", "2.0", "3.0");
+
+        AtlasRelationshipDef relationshipDef = new AtlasRelationshipDef();
+        relationshipDef.setName("TestRelationship");
+        relationshipDef.setTypeVersion("1.0");
+        relationshipDef.setEndDef1(new AtlasRelationshipEndDef("E1", "a1", AtlasAttributeDef.Cardinality.SINGLE));
+        relationshipDef.setEndDef2(new AtlasRelationshipEndDef("E2", "a2", AtlasAttributeDef.Cardinality.SINGLE));
+        when(typeRegistry.getTypeDefByName("TestRelationship")).thenReturn(relationshipDef);
+
+        Map<String, Object> params = new HashMap<>();
+        params.put("endDefToken", "endDef1");
+        setField(patch, "params", params);
+
+        PatchStatus result = handler.applyPatch((AtlasTypeDefStoreInitializer.TypeDefPatch) patch);
+        assertEquals(result, SKIPPED);
+        verify(typeDefStore, never()).updateRelationshipDefByName(anyString(), any());
+    }
+
     @Test
     public void testUpdateAttributeMetadataHandlerComprehensive() throws Exception {
         AtlasTypeDefStoreInitializer.UpdateAttributeMetadataHandler handler = new AtlasTypeDefStoreInitializer.UpdateAttributeMetadataHandler(typeDefStore, typeRegistry, graph);
diff --git a/repository/src/test/java/org/apache/atlas/repository/store/graph/v2/EntityDeletePropagationHandlerTest.java b/repository/src/test/java/org/apache/atlas/repository/store/graph/v2/EntityDeletePropagationHandlerTest.java
new file mode 100644
index 0000000000..37769794d7
--- /dev/null
+++ b/repository/src/test/java/org/apache/atlas/repository/store/graph/v2/EntityDeletePropagationHandlerTest.java
@@ -0,0 +1,476 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.apache.atlas.repository.store.graph.v2;
+
+import org.apache.atlas.model.instance.AtlasEntity;
+import org.apache.atlas.repository.graph.GraphHelper;
+import org.apache.atlas.repository.graphdb.AtlasEdge;
+import org.apache.atlas.repository.graphdb.AtlasVertex;
+import org.apache.atlas.type.AtlasEntityType;
+import org.apache.atlas.type.AtlasStructType.AtlasAttribute;
+import org.apache.atlas.type.AtlasTypeRegistry;
+import org.apache.atlas.type.DeletePropagationTarget;
+import org.mockito.Mock;
+import org.mockito.MockedStatic;
+import org.mockito.MockitoAnnotations;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.mockStatic;
+import static org.mockito.Mockito.when;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+public class EntityDeletePropagationHandlerTest {
+    @Mock private AtlasTypeRegistry typeRegistry;
+
+    private EntityDeletePropagationHandler handler;
+
+    private MockedStatic<GraphHelper>       graphHelperMock;
+    private MockedStatic<AtlasGraphUtilsV2> graphUtilsMock;
+
+    @BeforeMethod
+    public void setUp() {
+        MockitoAnnotations.openMocks(this);
+        handler = new EntityDeletePropagationHandler(typeRegistry);
+    }
+
+    @AfterMethod
+    public void tearDown() {
+        if (graphHelperMock != null) {
+            graphHelperMock.close();
+            graphHelperMock = null;
+        }
+        if (graphUtilsMock != null) {
+            graphUtilsMock.close();
+            graphUtilsMock = null;
+        }
+    }
+
+    @Test
+    public void testNoPropagationTargets_returnsEmpty() {
+        AtlasEntityType entityType = mock(AtlasEntityType.class);
+        AtlasVertex     srcVertex  = mock(AtlasVertex.class);
+
+        when(entityType.getDeletePropagationTargets()).thenReturn(Collections.emptyList());
+
+        Set<String> visitedGuids = new HashSet<>();
+        Set<AtlasVertex> result = handler.collectPropagatedVertices(srcVertex, entityType, visitedGuids);
+
+        assertTrue(result.isEmpty());
+    }
+
+    @Test
+    public void testNullRelAttr_skipped() {
+        openStaticMocks();
+
+        DeletePropagationTarget target = mock(DeletePropagationTarget.class);
+        when(target.getRelAttr()).thenReturn(null);
+
+        AtlasEntityType entityType = mock(AtlasEntityType.class);
+        AtlasVertex     srcVertex  = mock(AtlasVertex.class);
+        when(entityType.getDeletePropagationTargets()).thenReturn(Collections.singletonList(target));
+
+        Set<String> visitedGuids = new HashSet<>();
+        Set<AtlasVertex> result = handler.collectPropagatedVertices(srcVertex, entityType, visitedGuids);
+
+        assertTrue(result.isEmpty());
+    }
+
+    @Test
+    public void testNoEdges_returnsEmpty() {
+        openStaticMocks();
+
+        AtlasAttribute relAttr = mock(AtlasAttribute.class);
+        when(relAttr.getRelationshipEdgeLabel()).thenReturn("__trino_table.schema");
+        when(relAttr.getRelationshipEdgeDirection()).thenReturn(null);
+
+        DeletePropagationTarget target = mock(DeletePropagationTarget.class);
+        when(target.getRelAttr()).thenReturn(relAttr);
+
+        AtlasEntityType entityType = mock(AtlasEntityType.class);
+        AtlasVertex     srcVertex  = mock(AtlasVertex.class);
+        when(entityType.getDeletePropagationTargets()).thenReturn(Collections.singletonList(target));
+
+        graphHelperMock.when(() -> GraphHelper.getEdgesForLabel(eq(srcVertex), anyString(), any()))
+                .thenReturn(Collections.<AtlasEdge>emptyList().iterator());
+
+        Set<String> visitedGuids = new HashSet<>();
+        Set<AtlasVertex> result = handler.collectPropagatedVertices(srcVertex, entityType, visitedGuids);
+
+        assertTrue(result.isEmpty());
+    }
+
+    @Test
+    public void testDeletedEdge_skipped() {
+        openStaticMocks();
+
+        AtlasAttribute relAttr = mock(AtlasAttribute.class);
+        when(relAttr.getRelationshipEdgeLabel()).thenReturn("__trino_table.schema");
+        when(relAttr.getRelationshipEdgeDirection()).thenReturn(null);
+
+        DeletePropagationTarget target = mock(DeletePropagationTarget.class);
+        when(target.getRelAttr()).thenReturn(relAttr);
+
+        AtlasEntityType entityType = mock(AtlasEntityType.class);
+        AtlasVertex     srcVertex  = mock(AtlasVertex.class);
+        AtlasEdge       edge       = mock(AtlasEdge.class);
+        when(entityType.getDeletePropagationTargets()).thenReturn(Collections.singletonList(target));
+
+        graphHelperMock.when(() -> GraphHelper.getEdgesForLabel(eq(srcVertex), anyString(), any()))
+                .thenReturn(Collections.singletonList(edge).iterator());
+        graphHelperMock.when(() -> GraphHelper.getStatus(edge)).thenReturn(AtlasEntity.Status.DELETED);
+
+        Set<String> visitedGuids = new HashSet<>();
+        Set<AtlasVertex> result = handler.collectPropagatedVertices(srcVertex, entityType, visitedGuids);
+
+        assertTrue(result.isEmpty());
+    }
+
+    @Test
+    public void testDeletedTargetVertex_skipped() {
+        openStaticMocks();
+
+        AtlasAttribute relAttr = mock(AtlasAttribute.class);
+        when(relAttr.getRelationshipEdgeLabel()).thenReturn("__trino_table.schema");
+        when(relAttr.getRelationshipEdgeDirection()).thenReturn(null);
+
+        DeletePropagationTarget target = mock(DeletePropagationTarget.class);
+        when(target.getRelAttr()).thenReturn(relAttr);
+
+        AtlasEntityType entityType   = mock(AtlasEntityType.class);
+        AtlasVertex     srcVertex    = mock(AtlasVertex.class);
+        AtlasVertex     targetVertex = mock(AtlasVertex.class);
+        AtlasEdge       edge         = mock(AtlasEdge.class);
+
+        when(entityType.getDeletePropagationTargets()).thenReturn(Collections.singletonList(target));
+        when(edge.getOutVertex()).thenReturn(srcVertex);
+        when(edge.getInVertex()).thenReturn(targetVertex);
+
+        graphHelperMock.when(() -> GraphHelper.getEdgesForLabel(eq(srcVertex), anyString(), any()))
+                .thenReturn(Collections.singletonList(edge).iterator());
+        graphHelperMock.when(() -> GraphHelper.getStatus(edge)).thenReturn(AtlasEntity.Status.ACTIVE);
+        graphHelperMock.when(() -> GraphHelper.getStatus(targetVertex)).thenReturn(AtlasEntity.Status.DELETED);
+
+        Set<String> visitedGuids = new HashSet<>();
+        Set<AtlasVertex> result = handler.collectPropagatedVertices(srcVertex, entityType, visitedGuids);
+
+        assertTrue(result.isEmpty());
+    }
+
+    @Test
+    public void testAlreadyVisitedGuid_skipped() {
+        openStaticMocks();
+
+        AtlasAttribute relAttr = mock(AtlasAttribute.class);
+        when(relAttr.getRelationshipEdgeLabel()).thenReturn("__trino_table.schema");
+        when(relAttr.getRelationshipEdgeDirection()).thenReturn(null);
+
+        DeletePropagationTarget target = mock(DeletePropagationTarget.class);
+        when(target.getRelAttr()).thenReturn(relAttr);
+
+        AtlasEntityType entityType   = mock(AtlasEntityType.class);
+        AtlasVertex     srcVertex    = mock(AtlasVertex.class);
+        AtlasVertex     targetVertex = mock(AtlasVertex.class);
+        AtlasEdge       edge         = mock(AtlasEdge.class);
+
+        when(entityType.getDeletePropagationTargets()).thenReturn(Collections.singletonList(target));
+        when(edge.getOutVertex()).thenReturn(srcVertex);
+        when(edge.getInVertex()).thenReturn(targetVertex);
+
+        graphHelperMock.when(() -> GraphHelper.getEdgesForLabel(eq(srcVertex), anyString(), any()))
+                .thenReturn(Collections.singletonList(edge).iterator());
+        graphHelperMock.when(() -> GraphHelper.getStatus(edge)).thenReturn(AtlasEntity.Status.ACTIVE);
+        graphHelperMock.when(() -> GraphHelper.getStatus(targetVertex)).thenReturn(AtlasEntity.Status.ACTIVE);
+        graphUtilsMock.when(() -> AtlasGraphUtilsV2.getIdFromVertex(targetVertex)).thenReturn("already-visited-guid");
+
+        Set<String> visitedGuids = new HashSet<>();
+        visitedGuids.add("already-visited-guid");
+
+        Set<AtlasVertex> result = handler.collectPropagatedVertices(srcVertex, entityType, visitedGuids);
+
+        assertTrue(result.isEmpty());
+    }
+
+    @Test
+    public void testSingleTargetPropagated() {
+        openStaticMocks();
+
+        AtlasAttribute relAttr = mock(AtlasAttribute.class);
+        when(relAttr.getRelationshipEdgeLabel()).thenReturn("__trino_table.schema");
+        when(relAttr.getRelationshipEdgeDirection()).thenReturn(null);
+
+        DeletePropagationTarget target = mock(DeletePropagationTarget.class);
+        when(target.getRelAttr()).thenReturn(relAttr);
+
+        AtlasEntityType sourceType   = mock(AtlasEntityType.class);
+        AtlasEntityType targetType   = mock(AtlasEntityType.class);
+        AtlasVertex     srcVertex    = mock(AtlasVertex.class);
+        AtlasVertex     targetVertex = mock(AtlasVertex.class);
+        AtlasEdge       edge         = mock(AtlasEdge.class);
+
+        when(sourceType.getDeletePropagationTargets()).thenReturn(Collections.singletonList(target));
+        when(sourceType.getTypeName()).thenReturn("trino_schema");
+        when(edge.getOutVertex()).thenReturn(srcVertex);
+        when(edge.getInVertex()).thenReturn(targetVertex);
+
+        graphHelperMock.when(() -> GraphHelper.getEdgesForLabel(eq(srcVertex), anyString(), any()))
+                .thenReturn(Collections.singletonList(edge).iterator());
+        graphHelperMock.when(() -> GraphHelper.getStatus(edge)).thenReturn(AtlasEntity.Status.ACTIVE);
+        graphHelperMock.when(() -> GraphHelper.getStatus(targetVertex)).thenReturn(AtlasEntity.Status.ACTIVE);
+        graphHelperMock.when(() -> GraphHelper.getTypeName(targetVertex)).thenReturn("trino_table");
+        graphUtilsMock.when(() -> AtlasGraphUtilsV2.getIdFromVertex(targetVertex)).thenReturn("target-guid-001");
+
+        when(typeRegistry.getEntityTypeByName("trino_table")).thenReturn(targetType);
+        when(targetType.getDeletePropagationTargets()).thenReturn(Collections.emptyList());
+
+        Set<String> visitedGuids = new HashSet<>();
+        Set<AtlasVertex> result = handler.collectPropagatedVertices(srcVertex, sourceType, visitedGuids);
+
+        assertEquals(result.size(), 1);
+        assertTrue(result.contains(targetVertex));
+        assertTrue(visitedGuids.contains("target-guid-001"));
+    }
+
+    @Test
+    public void testRecursivePropagation() {
+        openStaticMocks();
+
+        // Level 1: trino_schema -> trino_table
+        AtlasAttribute relAttr1 = mock(AtlasAttribute.class);
+        when(relAttr1.getRelationshipEdgeLabel()).thenReturn("__trino_table.schema");
+        when(relAttr1.getRelationshipEdgeDirection()).thenReturn(null);
+
+        DeletePropagationTarget target1 = mock(DeletePropagationTarget.class);
+        when(target1.getRelAttr()).thenReturn(relAttr1);
+
+        // Level 2: trino_table -> trino_column
+        AtlasAttribute relAttr2 = mock(AtlasAttribute.class);
+        when(relAttr2.getRelationshipEdgeLabel()).thenReturn("__trino_column.table");
+        when(relAttr2.getRelationshipEdgeDirection()).thenReturn(null);
+
+        DeletePropagationTarget target2 = mock(DeletePropagationTarget.class);
+        when(target2.getRelAttr()).thenReturn(relAttr2);
+
+        AtlasEntityType schemaType  = mock(AtlasEntityType.class);
+        AtlasEntityType tableType   = mock(AtlasEntityType.class);
+        AtlasEntityType columnType  = mock(AtlasEntityType.class);
+        AtlasVertex     schemaVertex = mock(AtlasVertex.class);
+        AtlasVertex     tableVertex  = mock(AtlasVertex.class);
+        AtlasVertex     columnVertex = mock(AtlasVertex.class);
+        AtlasEdge       edge1        = mock(AtlasEdge.class);
+        AtlasEdge       edge2        = mock(AtlasEdge.class);
+
+        when(schemaType.getDeletePropagationTargets()).thenReturn(Collections.singletonList(target1));
+        when(schemaType.getTypeName()).thenReturn("trino_schema");
+        when(tableType.getDeletePropagationTargets()).thenReturn(Collections.singletonList(target2));
+        when(tableType.getTypeName()).thenReturn("trino_table");
+        when(columnType.getDeletePropagationTargets()).thenReturn(Collections.emptyList());
+
+        // Edge from schema -> table
+        when(edge1.getOutVertex()).thenReturn(schemaVertex);
+        when(edge1.getInVertex()).thenReturn(tableVertex);
+        graphHelperMock.when(() -> GraphHelper.getEdgesForLabel(eq(schemaVertex), eq("__trino_table.schema"), any()))
+                .thenReturn(Collections.singletonList(edge1).iterator());
+        graphHelperMock.when(() -> GraphHelper.getStatus(edge1)).thenReturn(AtlasEntity.Status.ACTIVE);
+        graphHelperMock.when(() -> GraphHelper.getStatus(tableVertex)).thenReturn(AtlasEntity.Status.ACTIVE);
+        graphHelperMock.when(() -> GraphHelper.getTypeName(tableVertex)).thenReturn("trino_table");
+        graphUtilsMock.when(() -> AtlasGraphUtilsV2.getIdFromVertex(tableVertex)).thenReturn("table-guid");
+
+        // Edge from table -> column
+        when(edge2.getOutVertex()).thenReturn(tableVertex);
+        when(edge2.getInVertex()).thenReturn(columnVertex);
+        graphHelperMock.when(() -> GraphHelper.getEdgesForLabel(eq(tableVertex), eq("__trino_column.table"), any()))
+                .thenReturn(Collections.singletonList(edge2).iterator());
+        graphHelperMock.when(() -> GraphHelper.getStatus(edge2)).thenReturn(AtlasEntity.Status.ACTIVE);
+        graphHelperMock.when(() -> GraphHelper.getStatus(columnVertex)).thenReturn(AtlasEntity.Status.ACTIVE);
+        graphHelperMock.when(() -> GraphHelper.getTypeName(columnVertex)).thenReturn("trino_column");
+        graphUtilsMock.when(() -> AtlasGraphUtilsV2.getIdFromVertex(columnVertex)).thenReturn("column-guid");
+
+        when(typeRegistry.getEntityTypeByName("trino_table")).thenReturn(tableType);
+        when(typeRegistry.getEntityTypeByName("trino_column")).thenReturn(columnType);
+
+        Set<String> visitedGuids = new HashSet<>();
+        Set<AtlasVertex> result = handler.collectPropagatedVertices(schemaVertex, schemaType, visitedGuids);
+
+        assertEquals(result.size(), 2);
+        assertTrue(result.contains(tableVertex));
+        assertTrue(result.contains(columnVertex));
+    }
+
+    @Test
+    public void testCyclePrevention() {
+        openStaticMocks();
+
+        AtlasAttribute relAttr = mock(AtlasAttribute.class);
+        when(relAttr.getRelationshipEdgeLabel()).thenReturn("__cyclic_edge");
+        when(relAttr.getRelationshipEdgeDirection()).thenReturn(null);
+
+        DeletePropagationTarget target = mock(DeletePropagationTarget.class);
+        when(target.getRelAttr()).thenReturn(relAttr);
+
+        AtlasEntityType entityType = mock(AtlasEntityType.class);
+        AtlasVertex     vertexA    = mock(AtlasVertex.class);
+        AtlasVertex     vertexB    = mock(AtlasVertex.class);
+        AtlasEdge       edgeAtoB   = mock(AtlasEdge.class);
+        AtlasEdge       edgeBtoA   = mock(AtlasEdge.class);
+
+        when(entityType.getDeletePropagationTargets()).thenReturn(Collections.singletonList(target));
+        when(entityType.getTypeName()).thenReturn("cyclic_type");
+
+        // A -> B
+        when(edgeAtoB.getOutVertex()).thenReturn(vertexA);
+        when(edgeAtoB.getInVertex()).thenReturn(vertexB);
+        graphHelperMock.when(() -> GraphHelper.getEdgesForLabel(eq(vertexA), anyString(), any()))
+                .thenReturn(Collections.singletonList(edgeAtoB).iterator());
+        graphHelperMock.when(() -> GraphHelper.getStatus(edgeAtoB)).thenReturn(AtlasEntity.Status.ACTIVE);
+        graphHelperMock.when(() -> GraphHelper.getStatus(vertexB)).thenReturn(AtlasEntity.Status.ACTIVE);
+        graphHelperMock.when(() -> GraphHelper.getTypeName(vertexB)).thenReturn("cyclic_type");
+        graphUtilsMock.when(() -> AtlasGraphUtilsV2.getIdFromVertex(vertexB)).thenReturn("guid-B");
+
+        // B -> A (cycle): vertexA guid is already in visitedGuids since it's the source
+        when(edgeBtoA.getOutVertex()).thenReturn(vertexB);
+        when(edgeBtoA.getInVertex()).thenReturn(vertexA);
+        graphHelperMock.when(() -> GraphHelper.getEdgesForLabel(eq(vertexB), anyString(), any()))
+                .thenReturn(Collections.singletonList(edgeBtoA).iterator());
+        graphHelperMock.when(() -> GraphHelper.getStatus(edgeBtoA)).thenReturn(AtlasEntity.Status.ACTIVE);
+        graphHelperMock.when(() -> GraphHelper.getStatus(vertexA)).thenReturn(AtlasEntity.Status.ACTIVE);
+        graphUtilsMock.when(() -> AtlasGraphUtilsV2.getIdFromVertex(vertexA)).thenReturn("guid-A");
+
+        when(typeRegistry.getEntityTypeByName("cyclic_type")).thenReturn(entityType);
+
+        Set<String> visitedGuids = new HashSet<>();
+        visitedGuids.add("guid-A");
+
+        Set<AtlasVertex> result = handler.collectPropagatedVertices(vertexA, entityType, visitedGuids);
+
+        assertEquals(result.size(), 1);
+        assertTrue(result.contains(vertexB));
+    }
+
+    @Test
+    public void testMultipleTargetsFromSameSource() {
+        openStaticMocks();
+
+        AtlasAttribute relAttr1 = mock(AtlasAttribute.class);
+        when(relAttr1.getRelationshipEdgeLabel()).thenReturn("__edge_label_1");
+        when(relAttr1.getRelationshipEdgeDirection()).thenReturn(null);
+
+        AtlasAttribute relAttr2 = mock(AtlasAttribute.class);
+        when(relAttr2.getRelationshipEdgeLabel()).thenReturn("__edge_label_2");
+        when(relAttr2.getRelationshipEdgeDirection()).thenReturn(null);
+
+        DeletePropagationTarget target1 = mock(DeletePropagationTarget.class);
+        when(target1.getRelAttr()).thenReturn(relAttr1);
+
+        DeletePropagationTarget target2 = mock(DeletePropagationTarget.class);
+        when(target2.getRelAttr()).thenReturn(relAttr2);
+
+        AtlasEntityType sourceType  = mock(AtlasEntityType.class);
+        AtlasEntityType targetType1 = mock(AtlasEntityType.class);
+        AtlasEntityType targetType2 = mock(AtlasEntityType.class);
+        AtlasVertex     srcVertex   = mock(AtlasVertex.class);
+        AtlasVertex     vertex1     = mock(AtlasVertex.class);
+        AtlasVertex     vertex2     = mock(AtlasVertex.class);
+        AtlasEdge       edge1       = mock(AtlasEdge.class);
+        AtlasEdge       edge2       = mock(AtlasEdge.class);
+
+        when(sourceType.getDeletePropagationTargets()).thenReturn(Arrays.asList(target1, target2));
+        when(sourceType.getTypeName()).thenReturn("source_type");
+
+        when(edge1.getOutVertex()).thenReturn(srcVertex);
+        when(edge1.getInVertex()).thenReturn(vertex1);
+        graphHelperMock.when(() -> GraphHelper.getEdgesForLabel(eq(srcVertex), eq("__edge_label_1"), any()))
+                .thenReturn(Collections.singletonList(edge1).iterator());
+        graphHelperMock.when(() -> GraphHelper.getStatus(edge1)).thenReturn(AtlasEntity.Status.ACTIVE);
+        graphHelperMock.when(() -> GraphHelper.getStatus(vertex1)).thenReturn(AtlasEntity.Status.ACTIVE);
+        graphHelperMock.when(() -> GraphHelper.getTypeName(vertex1)).thenReturn("target_type_1");
+        graphUtilsMock.when(() -> AtlasGraphUtilsV2.getIdFromVertex(vertex1)).thenReturn("guid-1");
+
+        when(edge2.getOutVertex()).thenReturn(srcVertex);
+        when(edge2.getInVertex()).thenReturn(vertex2);
+        graphHelperMock.when(() -> GraphHelper.getEdgesForLabel(eq(srcVertex), eq("__edge_label_2"), any()))
+                .thenReturn(Collections.singletonList(edge2).iterator());
+        graphHelperMock.when(() -> GraphHelper.getStatus(edge2)).thenReturn(AtlasEntity.Status.ACTIVE);
+        graphHelperMock.when(() -> GraphHelper.getStatus(vertex2)).thenReturn(AtlasEntity.Status.ACTIVE);
+        graphHelperMock.when(() -> GraphHelper.getTypeName(vertex2)).thenReturn("target_type_2");
+        graphUtilsMock.when(() -> AtlasGraphUtilsV2.getIdFromVertex(vertex2)).thenReturn("guid-2");
+
+        when(typeRegistry.getEntityTypeByName("target_type_1")).thenReturn(targetType1);
+        when(typeRegistry.getEntityTypeByName("target_type_2")).thenReturn(targetType2);
+        when(targetType1.getDeletePropagationTargets()).thenReturn(Collections.emptyList());
+        when(targetType2.getDeletePropagationTargets()).thenReturn(Collections.emptyList());
+
+        Set<String> visitedGuids = new HashSet<>();
+        Set<AtlasVertex> result = handler.collectPropagatedVertices(srcVertex, sourceType, visitedGuids);
+
+        assertEquals(result.size(), 2);
+        assertTrue(result.contains(vertex1));
+        assertTrue(result.contains(vertex2));
+    }
+
+    @Test
+    public void testNullTargetEntityType_skipped() {
+        openStaticMocks();
+
+        AtlasAttribute relAttr = mock(AtlasAttribute.class);
+        when(relAttr.getRelationshipEdgeLabel()).thenReturn("__edge_label");
+        when(relAttr.getRelationshipEdgeDirection()).thenReturn(null);
+
+        DeletePropagationTarget target = mock(DeletePropagationTarget.class);
+        when(target.getRelAttr()).thenReturn(relAttr);
+
+        AtlasEntityType sourceType   = mock(AtlasEntityType.class);
+        AtlasVertex     srcVertex    = mock(AtlasVertex.class);
+        AtlasVertex     targetVertex = mock(AtlasVertex.class);
+        AtlasEdge       edge         = mock(AtlasEdge.class);
+
+        when(sourceType.getDeletePropagationTargets()).thenReturn(Collections.singletonList(target));
+        when(edge.getOutVertex()).thenReturn(srcVertex);
+        when(edge.getInVertex()).thenReturn(targetVertex);
+
+        graphHelperMock.when(() -> GraphHelper.getEdgesForLabel(eq(srcVertex), anyString(), any()))
+                .thenReturn(Collections.singletonList(edge).iterator());
+        graphHelperMock.when(() -> GraphHelper.getStatus(edge)).thenReturn(AtlasEntity.Status.ACTIVE);
+        graphHelperMock.when(() -> GraphHelper.getStatus(targetVertex)).thenReturn(AtlasEntity.Status.ACTIVE);
+        graphHelperMock.when(() -> GraphHelper.getTypeName(targetVertex)).thenReturn("unknown_type");
+        graphUtilsMock.when(() -> AtlasGraphUtilsV2.getIdFromVertex(targetVertex)).thenReturn("unknown-guid");
+
+        when(typeRegistry.getEntityTypeByName("unknown_type")).thenReturn(null);
+
+        Set<String> visitedGuids = new HashSet<>();
+        Set<AtlasVertex> result = handler.collectPropagatedVertices(srcVertex, sourceType, visitedGuids);
+
+        assertTrue(result.isEmpty());
+    }
+
+    private void openStaticMocks() {
+        graphHelperMock = mockStatic(GraphHelper.class);
+        graphUtilsMock  = mockStatic(AtlasGraphUtilsV2.class);
+    }
+}


(atlas) 04/05: ATLAS-5279: changes for handling rename propagation (#609)

Posted by ni...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

asf-gitbox-commits pushed a commit to branch atlas-2.6
in repository https://gitbox.apache.org/repos/asf/atlas.git

commit 2c8319a9cea5d819d212fe25fedbcb7aefa6e1c9
Author: bhor-sanket <sa...@gmail.com>
AuthorDate: Tue Jun 9 09:11:10 2026 +0530

    ATLAS-5279: changes for handling rename propagation (#609)
    
    * ATLAS-5279:changes for handling rename propagation
    
    * ATLAS-5279: code cleanup: improve formatting, refactor structure, and add logging
    
    * ATLAS-5279: changes to add Rename Propagation README file, updated solrconfig for newly added field and handled deleted column use-case
    
    * ATLAS-5279: changes to add Rename Propagation README file, updated solrconfig for newly added field and handled deleted column use-case
    
    * ATLAS-5279: changes to resolve review comments
    
    * ATLAS-5279: changes to add attributeDefOverrides for trino column
    
    * ATLAS-5279: Added support for attributeDefOverrides and propagateRename flag to handle Hive table to Trino table rename propagation
    
    * Revert "ATLAS-5279: Added support for attributeDefOverrides and propagateRename flag to handle Hive table to Trino table rename propagation"
    
    This reverts commit d257b82fdf6d9291993ef52790df7195cd07d0fa.
    
    * ATLAS-5279: Added support for attributeDefOverrides and propagateRename flag to handle Hive table to Trino table rename propagation
    
    * ATLAS-5279: changes to Hive table → Trino table → Trino column example in RenamePropagation.md
    
    ---------
    
    Co-authored-by: sanket.bhor <sa...@cloudera.com>
---
 docs/src/documents/RenamePropagation.md            | 200 +++++++++++++++++++++
 .../model/typedef/AtlasRelationshipEndDef.java     |   2 +-
 2 files changed, 201 insertions(+), 1 deletion(-)

diff --git a/docs/src/documents/RenamePropagation.md b/docs/src/documents/RenamePropagation.md
new file mode 100644
index 0000000000..b261c2adfe
--- /dev/null
+++ b/docs/src/documents/RenamePropagation.md
@@ -0,0 +1,200 @@
+---
+
+## name: Rename Propagation
+
+route: /RenamePropagation
+menu: Documentation
+submenu: Features
+
+# Rename propagation
+
+**Rename propagation** is an Atlas feature for **partial entity updates**: when a renamed instance’s `**name`** (or a mapped source attribute) changes, the server can find **related entities** whose **unique attribute** (for most types, `**qualifiedName`**) still embeds the old segment, recompute that unique attribute from the type’s `**autoComputeFormat`**, and **persist those updates in the same transaction** as the triggering entity. Discovery is driven by **typedefs** (relationship e [...]
+
+---
+
+## Why it exists
+
+The unique attribute (qualified name) is often a **composite string** built from ancestor names (database, table, cluster, and so on). If only the root entity is updated in a hook payload, **downstream** instances can keep an outdated unique attribute until something rewrites them. Rename propagation closes that gap by following **marked relationship edges** from the updated vertex and applying the same **parse → replace one template segment → rebuild** logic per dependent type.
+
+---
+
+## Example: Hive database rename
+
+Assume a Hive model where:
+
+- `**hive_db`** has unique attribute (qualified name) shaped like `{name}@{clusterName}` (for example `sales@cm`).
+- `**hive_table`** uses `{db.name}.{name}@{clusterName}` (for example `sales.orders@cm`).
+- `**hive_column**` uses `{table.db.name}.{table.name}.{name}@{clusterName}`.
+
+The `**hive_table_db**` relationship links each table to its database; `**hive_table_columns**` links columns to a table. Relationship ends that opt in with `**propagateRename**` tell Atlas: when the **trigger** side is renamed, walk to neighbors and refresh their unique attribute (qualified name) where the template depends on that rename.
+
+1. An integration renames the database `**sales`** → `**sales_archive`** (partial update; `**name**` and thus the unique attribute on the `hive_db` vertex change).
+2. Atlas detects that the entity’s **unique attribute (qualified name)** changed and that `**hive_db`** has **rename propagation targets** from typedef resolution.
+3. `**EntityRenameHandler`** follows the configured relationship from the database vertex to related `**hive_table`** vertices, recomputes each table’s string (for example `sales.orders@cm` → `sales_archive.orders@cm`), and registers those rows on the mutation context.
+4. If `**hive_table**` also declares propagation targets, the same process continues to `**hive_column**` rows (for example `sales.orders.col1@cm` → `sales_archive.orders.col1@cm`).
+
+All of this happens **without** requiring the hook to send every table and column in one batch.
+
+---
+
+## Example: Hive table → Trino table → Trino column
+
+Assume a Hive and Trino model where:
+
+- `**hive_table**` has unique attribute (qualified name) shaped like `{db.name}.{name}@{clusterName}` (for example `default.orders@cluster`).
+- `**trino_table**` uses `{trinoschema.catalog.name}.{trinoschema.name}.{name}@{trinoschema.catalog.instance.name}` (for example `cat1.schema1.orders@inst1`).
+- `**trino_column**` uses `{table.trinoschema.catalog.name}.{table.trinoschema.name}.{table.name}.{name}@{table.trinoschema.catalog.instance.name}` (for example `cat1.schema1.orders.col1@inst1`).
+
+The `**trino_table_hive_table**` relationship links each Trino table to its Hive table; `**trino_table_columns**` links columns to a table. The `**propagateRename**` flag on the `**hive_table**` end of `**trino_table_hive_table**` tells Atlas: when the **trigger** Hive table is renamed, walk to linked `**trino_table**` vertices and refresh their unique attribute (qualified name) where the `**autoComputeFormat**` template depends on that rename.
+
+1. An integration renames the Hive table `**orders**` → `**orders_v2**` (partial update; `**name**` and thus the unique attribute on the `**hive_table**` vertex change).
+2. Atlas detects that the entity's **unique attribute (qualified name)** changed and that `**hive_table**` has **rename propagation targets** from typedef resolution.
+3. `**EntityRenameHandler**` follows `**trino_table_hive_table**` from the Hive table vertex to related `**trino_table**` vertices, recomputes each table's string (for example `cat1.schema1.orders@inst1` → `cat1.schema1.orders_v2@inst1`), and registers those rows on the mutation context.
+4. If `**trino_table**` also declares propagation targets, the same process continues to `**trino_column**` rows (for example `cat1.schema1.orders.col1@inst1` → `cat1.schema1.orders_v2.col1@inst1`).
+
+All of this happens **without** requiring the hook to send every Trino table and column in one batch.
+
+---
+
+## How it works (two stages)
+
+### 1. Typedef resolution (startup / type updates)
+
+While the type system resolves references, Atlas builds **per-entity-type** metadata used later at runtime:
+
+- `**propagateRename`** on a `**AtlasRelationshipEndDef`** marks which end acts as the **rename source** for that relationship so instances reached through that edge can be updated when the source side’s name changes.
+- `**attributeDefOverrides`** on `**AtlasEntityDef`** can set `**autoComputeFormat**` for the **unique attribute** (qualified name), describing how the string is composed from named segments.
+- For paths that do not use explicit `**propagateAttributes`** maps on the relationship end, `**AtlasEntityType`** also maintains `**autoComputeFormatPathByRefTypeNameMap**`: referenced type name → **dotted path** in the template for the segment to replace when that referenced type was renamed.
+- Optional `**propagateAttributes`** on a relationship end lists `**source`** / `**target**` attribute pairs so values such as `**name**` can be written to the right stub fields on dependents when models need more than template substitution alone.
+
+Together, these produce a list of `**RenamePropagationTarget**` entries on each `**AtlasEntityType**`.
+
+### 2. Entity update (runtime)
+
+On **partial update**, `**AtlasEntityStoreV2`** compares the stored **unique attribute (qualified name)** on the graph vertex to the incoming value. If it changed and the type has **rename propagation targets**, `**EntityRenameHandler`** walks the graph along those relationships, recomputes each dependent’s unique attribute from its effective `**autoComputeFormat`**, and adds minimal **dependent stubs** (guid, new unique attribute, optional mapped attributes) to the **same** mutation con [...]
+
+---
+
+## Key typedef concepts
+
+
+| Concept                                     | Where it lives            | Role                                                                                                                                                                                                 |
+| ------------------------------------------- | ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `**attributeDefOverrides`**                 | `AtlasEntityDef`          | Supplies per-type overrides such as `**autoComputeFormat`** for the unique attribute (qualified name); merged during hierarchy resolution and stored on the entity type vertex when supported.       |
+| `**propagateRename**`                       | `AtlasRelationshipEndDef` | Marks the end whose entity type is the **trigger** for propagation across that relationship.                                                                                                         |
+| `**propagateAttributes`**                   | `AtlasRelationshipEndDef` | Optional maps from a **source** attribute on the trigger side to **target** attribute names on the dependent entity.                                                                                 |
+| `**RenamePropagationTarget**`               | `AtlasEntityType`         | Precomputed link: target type name, relationship attribute for edge traversal, and propagate-attribute list.                                                                                         |
+| `**autoComputeFormatPathByRefTypeNameMap`** | `AtlasEntityType`         | Referenced entity type name → dotted path in the dependent’s **autoComputeFormat** for the segment to rewrite when that type was renamed (when `**propagateAttributes`** is not used for that path). |
+
+
+---
+
+## Implementation reference
+
+
+| Component                                                | Role                                                                                                                                                               |
+| -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
+| `**AtlasEntityType`**                                    | Registers rename propagation targets and builds `**autoComputeFormatPathByRefTypeNameMap**` during reference resolution.                                           |
+| `**EntityRenameHandler**`                                | Traverses edges, parses/rebuilds the unique attribute string, registers `**DependentUpdate**` entries on the mutation context.                                     |
+| `**AtlasEntityStoreV2**`                                 | Detects a changed unique attribute (qualified name) on partial update and invokes the handler.                                                                     |
+| `**AtlasEntityDefStoreV2` / `AtlasTypeDefGraphStoreV2**` | Persist and read `**attributeDefOverrides**` via the type vertex property keyed by `**TYPE_ATTR_DEF_OVERRIDES_PROPERTY_KEY**`.                                     |
+| `**AtlasTypeDefStoreInitializer**`                       | Applies typedef patches such as `**SET_ATTRIBUTE_DEF_OVERRIDES**` and `**SET_PROPAGATE_RENAME**` (patch params use `**endDefToken**`: `"endDef1"` or `"endDef2"`). |
+| `**GraphBackedSearchIndexer**`                           | Indexes the overrides property for search where applicable.                                                                                                        |
+
+
+---
+
+## Models and patches
+
+- **Bootstrap model JSON** — Declare `**attributeDefOverrides`** for the unique attribute (qualified name) and `**propagateRename`** / `**propagateAttributes**` on relationship ends as required by your connectors (for example Hive, Trino).
+- **Patch files** — Ship one-off typedef updates under `**addons/.../patches/`** so each patch runs once and is tracked (for example `**AtlasPatchRegistry`**), avoiding reliance on version-only bumps on every restart.
+
+---
+
+## Configuring `autoComputeFormat` on an entity type
+
+Rename propagation recomputes a dependent’s **unique attribute** (usually `**qualifiedName**`) by **parsing** the stored string against the type’s `**autoComputeFormat**`, **replacing** the segment that came from the renamed entity, and **rebuilding** the string. You supply that template on the **entity typedef** as an override on the unique attribute, for **any** entity type whose composite id should stay aligned after upstream renames.
+
+### Where to declare it
+
+In **bootstrap model JSON** add an `**attributeDefOverrides`** array on the `**entityDefs**` entry. Each override targets an attribute by name; set `**autoComputeFormat**` on the **unique** attribute (conventionally `**qualifiedName`**):
+
+```json
+{
+  "name": "my_column",
+  "superTypes": ["DataSet"],
+  "attributeDefOverrides": [
+    {
+      "name": "qualifiedName",
+      "autoComputeFormat": "{table.db.name}.{table.name}.{name}@{clusterName}"
+    }
+  ],
+  "attributeDefs": [ ]
+}
+```
+
+Atlas merges overrides with inherited definitions so the resolved `**AtlasEntityDef**` carries `**autoComputeFormat**` on `**qualifiedName**`.
+
+### Placeholder syntax
+
+- Use **curly braces** for each slot: `{segmentKey}`.
+- **Literals** between placeholders (for example `.`, `/`, `@`) are **delimiters**. When parsing an existing value, the character **immediately after** each `}` in the format marks where that slot ends in the stored string (the **last** placeholder consumes the rest of the string). Pick delimiters that do not appear inside real slot values, or parsing becomes ambiguous.
+- **Dots inside a key** are part of the key string only (they are not JSON nesting in the model file).
+
+### How to design the format for a new entity type
+
+Start from **one real qualified-name string** your connector already produces (for example `sales.orders@prod`). Notice how it is built from smaller parts and which **fixed characters** separate those parts (here: `.`, `.`, and `@`).
+
+1. Write the **same separators in the same order** in `**autoComputeFormat**`.
+2. Replace each **variable part** with `{...}`:
+   - **`{name}`** — this entity’s own **name** (always a single word inside the braces).
+   - **`{db.name}`**, **`{table.db.name}`**, and similar — each segment **before** the final `.name` is one **relationship hop** (the relationship attribute name on the typedef at that step). Together they lead to a related entity; the trailing `.name` means “use that entity’s **name** attribute.”
+   - **`{clusterName}`** (one word inside the braces) — a normal attribute on **this** same entity, not a relationship hop.
+
+Dots **inside** `{...}` are part of the placeholder key, not nested JSON in the model file. Choose separators that will not appear inside a single part of the real string, so parsing stays unambiguous.
+
+### Example: table under a database (Hive-style shape)
+
+If `**my_table**` links to a database via relationship attribute `**db**` and carries a cluster label `**clusterName**`, a common pattern is:
+
+```json
+"attributeDefOverrides": [
+  {
+    "name": "qualifiedName",
+    "autoComputeFormat": "{db.name}.{name}@{clusterName}"
+  }
+]
+```
+
+- `{db.name}` — database’s `**name**` reached through `**db**`.
+- `{name}` — this table’s `**name**`.
+- `@{clusterName}` — literal `@` then this entity’s `**clusterName**` attribute.
+
+### Example: deeper chain (Trino column)
+
+For `**trino_column**`, the unique string chains catalog, schema, table, column, and instance:
+
+```json
+"attributeDefOverrides": [
+  {
+    "name": "qualifiedName",
+    "autoComputeFormat": "{table.trinoschema.catalog.name}.{table.trinoschema.name}.{table.name}.{name}@{table.trinoschema.catalog.instance.name}"
+  }
+]
+```
+
+Meaning of each slot:
+
+- `{table.trinoschema.catalog.name}` — from this column, follow `**table**` → `**trinoschema**` → `**catalog**`, then use that catalog entity’s `**name**`.
+- `{table.trinoschema.name}` — schema `**name**` along the same branch.
+- `{table.name}` — table `**name**`.
+- `{name}` — this column’s own `**name**`.
+- `@{table.trinoschema.catalog.instance.name}` — after `@`, follow `**instance**` from the catalog and use that instance’s `**name**`.
+
+---
+
+## Tests
+
+- `**AtlasTypeDefStoreInitializerTest**` — Patch handlers for overrides and `**SET_PROPAGATE_RENAME**`.
+- `**EntityRenameHandlerTest**` — End-to-end behavior: unique attribute recompute, mapped attributes, and multi-hop propagation through typedefs.
+
diff --git a/intg/src/main/java/org/apache/atlas/model/typedef/AtlasRelationshipEndDef.java b/intg/src/main/java/org/apache/atlas/model/typedef/AtlasRelationshipEndDef.java
index 568b7535ff..9a6e26fd48 100644
--- a/intg/src/main/java/org/apache/atlas/model/typedef/AtlasRelationshipEndDef.java
+++ b/intg/src/main/java/org/apache/atlas/model/typedef/AtlasRelationshipEndDef.java
@@ -1,5 +1,5 @@
 /**
- * Licensed to the Apache Software Foundation (ASF) under oneØ
+ * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
  * regarding copyright ownership.  The ASF licenses this file


(atlas) 05/05: ATLAS-5385: Rename header auth property keys for atlas-2.6

Posted by ni...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

asf-gitbox-commits pushed a commit to branch atlas-2.6
in repository https://gitbox.apache.org/repos/asf/atlas.git

commit 23899b586a4497d62de160b3beb327afa9a633e4
Author: Ramachandran Krishnan <ra...@gmail.com>
AuthorDate: Thu Aug 20 16:17:53 2026 +0530

    ATLAS-5385: Rename header auth property keys for atlas-2.6
    
    Rename atlas.authn.header.* to atlas.authentication.method.header.* and
    colocate header auth examples with other authentication properties.
---
 distro/src/conf/atlas-application.properties         | 14 +++++++-------
 .../atlas/web/filters/AtlasHeaderPreAuthFilter.java  |  8 ++++----
 .../web/filters/AtlasHeaderPreAuthFilterTest.java    | 20 +++++++++-----------
 3 files changed, 20 insertions(+), 22 deletions(-)

diff --git a/distro/src/conf/atlas-application.properties b/distro/src/conf/atlas-application.properties
index 126143a204..2facfddb4d 100755
--- a/distro/src/conf/atlas-application.properties
+++ b/distro/src/conf/atlas-application.properties
@@ -167,6 +167,12 @@ atlas.authentication.method.file.filename=${sys:atlas.home}/conf/users-credentia
 #atlas.authentication.method.ldap.ad.user.searchfilter=(sAMAccountName={0})
 #atlas.authentication.method.ldap.ad.default.role=<default role>
 
+######### Header Based Authentication #########
+#atlas.authentication.method.header.enabled=false
+#atlas.authentication.method.header.username=username
+#atlas.authentication.method.header.roles=roles
+#atlas.authentication.method.header.request-id=requestid
+
 #########  JAAS Configuration ########
 
 #atlas.jaas.KafkaClient.loginModuleName = com.sun.security.auth.module.Krb5LoginModule
@@ -281,10 +287,4 @@ atlas.search.gremlin.enable=false
 
 ######### Skip check for the same attribute name in Parent type and Child type #########
 
-#atlas.skip.check.for.parent.child.attribute.name=true
-
-######### Header Based Authentication #########
-#atlas.authn.header.enabled=false
-#atlas.authn.header.username=x-awc-username
-#atlas.authn.header.roles=x-awc-roles
-#atlas.authn.header.requestid=x-awc-requestid
\ No newline at end of file
+#atlas.skip.check.for.parent.child.attribute.name=true
\ No newline at end of file
diff --git a/webapp/src/main/java/org/apache/atlas/web/filters/AtlasHeaderPreAuthFilter.java b/webapp/src/main/java/org/apache/atlas/web/filters/AtlasHeaderPreAuthFilter.java
index 423995bc64..ca136d336c 100644
--- a/webapp/src/main/java/org/apache/atlas/web/filters/AtlasHeaderPreAuthFilter.java
+++ b/webapp/src/main/java/org/apache/atlas/web/filters/AtlasHeaderPreAuthFilter.java
@@ -50,10 +50,10 @@ import java.util.UUID;
 public class AtlasHeaderPreAuthFilter implements Filter {
     private static final Logger LOG = LoggerFactory.getLogger(AtlasHeaderPreAuthFilter.class);
 
-    public static final String PROP_HEADER_AUTH_ENABLED = "atlas.authn.header.enabled";
-    public static final String PROP_USERNAME_HEADER     = "atlas.authn.header.username";
-    public static final String PROP_ROLES_HEADER        = "atlas.authn.header.roles";
-    public static final String PROP_REQUEST_ID_HEADER   = "atlas.authn.header.requestid";
+    public static final String PROP_HEADER_AUTH_ENABLED = "atlas.authentication.method.header.enabled";
+    public static final String PROP_USERNAME_HEADER     = "atlas.authentication.method.header.username";
+    public static final String PROP_ROLES_HEADER        = "atlas.authentication.method.header.roles";
+    public static final String PROP_REQUEST_ID_HEADER   = "atlas.authentication.method.header.request-id";
     public static final String REQUEST_ID_ATTRIBUTE     = "atlas.request.id";
 
     private Configuration configuration;
diff --git a/webapp/src/test/java/org/apache/atlas/web/filters/AtlasHeaderPreAuthFilterTest.java b/webapp/src/test/java/org/apache/atlas/web/filters/AtlasHeaderPreAuthFilterTest.java
index cd959aaa68..b74452e3a2 100644
--- a/webapp/src/test/java/org/apache/atlas/web/filters/AtlasHeaderPreAuthFilterTest.java
+++ b/webapp/src/test/java/org/apache/atlas/web/filters/AtlasHeaderPreAuthFilterTest.java
@@ -74,11 +74,11 @@ public class AtlasHeaderPreAuthFilterTest {
     public void testDoFilterEnabledWithUsernameAndRoles() throws Exception {
         when(configuration.getBoolean(AtlasHeaderPreAuthFilter.PROP_HEADER_AUTH_ENABLED, false)).thenReturn(true);
         when(configuration.getString(AtlasHeaderPreAuthFilter.PROP_USERNAME_HEADER, ""))
-                .thenReturn("x-user");
+                .thenReturn("X-Forwarded-User");
         when(configuration.getString(AtlasHeaderPreAuthFilter.PROP_ROLES_HEADER, ""))
-                .thenReturn("x-roles");
-        when(request.getHeader("x-user")).thenReturn("alice");
-        when(request.getHeader("x-roles")).thenReturn("ROLE_ADMIN, ROLE_USER");
+                .thenReturn("X-Forwarded-Groups");
+        when(request.getHeader("X-Forwarded-User")).thenReturn("alice");
+        when(request.getHeader("X-Forwarded-Groups")).thenReturn("ROLE_ADMIN, ROLE_USER");
 
         try (MockedStatic<ApplicationProperties> appProps = org.mockito.Mockito.mockStatic(ApplicationProperties.class)) {
             appProps.when(ApplicationProperties::get).thenReturn(configuration);
@@ -104,10 +104,10 @@ public class AtlasHeaderPreAuthFilterTest {
     public void testDoFilterEnabledWithoutUsernameDoesNotAuthenticate() throws IOException, ServletException {
         when(configuration.getBoolean(AtlasHeaderPreAuthFilter.PROP_HEADER_AUTH_ENABLED, false)).thenReturn(true);
         when(configuration.getString(AtlasHeaderPreAuthFilter.PROP_USERNAME_HEADER, ""))
-                .thenReturn("x-user");
+                .thenReturn("X-Forwarded-User");
         when(configuration.getString(AtlasHeaderPreAuthFilter.PROP_ROLES_HEADER, ""))
-                .thenReturn("x-roles");
-        when(request.getHeader("x-user")).thenReturn("   ");
+                .thenReturn("X-Forwarded-Groups");
+        when(request.getHeader("X-Forwarded-User")).thenReturn("   ");
 
         try (MockedStatic<ApplicationProperties> appProps = org.mockito.Mockito.mockStatic(ApplicationProperties.class)) {
             appProps.when(ApplicationProperties::get).thenReturn(configuration);
@@ -128,10 +128,8 @@ public class AtlasHeaderPreAuthFilterTest {
 
         when(configuration.getBoolean(AtlasHeaderPreAuthFilter.PROP_HEADER_AUTH_ENABLED, false)).thenReturn(true);
         when(configuration.getString(AtlasHeaderPreAuthFilter.PROP_USERNAME_HEADER, ""))
-                .thenReturn("x-user");
-        when(configuration.getString(AtlasHeaderPreAuthFilter.PROP_ROLES_HEADER, ""))
-                .thenReturn("x-roles");
-        when(request.getHeader("x-user")).thenReturn("alice");
+                .thenReturn("X-Forwarded-User");
+        when(request.getHeader("X-Forwarded-User")).thenReturn("alice");
 
         try (MockedStatic<ApplicationProperties> appProps = org.mockito.Mockito.mockStatic(ApplicationProperties.class)) {
             appProps.when(ApplicationProperties::get).thenReturn(configuration);