Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
b0a3b10
feat(entity-template): implement cascade delete for templates and add…
walterspieler-dkt Jul 9, 2026
9376bad
chore: resolve merge conflicts with origin/main
Copilot Aug 3, 2026
48dff4c
Merge branch 'main' into CPDE-2895-idp-core-specify-and-validate-beha…
walterspieler-dkt Aug 3, 2026
cb918a4
Merge branch 'main' into CPDE-2895-idp-core-specify-and-validate-beha…
walterspieler-dkt Aug 3, 2026
ed8061d
Merge branch 'main' into CPDE-2895-idp-core-specify-and-validate-beha…
brandPittCode Aug 26, 2026
73d125a
feat(core): add delete entity template workflow
brandPittCode Aug 26, 2026
a207269
feat(core): add check when deleting entity template
brandPittCode Aug 26, 2026
5c1ed8e
Merge branch 'main' into CPDE-2895-idp-core-specify-and-validate-beha…
brandPittCode Aug 26, 2026
8132dc6
feat(core)!: add check when deleting entity template
brandPittCode Aug 26, 2026
8ddd2ac
feat(core): add check when deleting entity template
brandPittCode Aug 26, 2026
f242b9f
feat(core): add check when deleting entity template
brandPittCode Aug 26, 2026
acfd844
feat(core): add check when deleting entity template
brandPittCode Aug 26, 2026
a3a3194
feat(core): add check when deleting entity template
brandPittCode Aug 26, 2026
98c59b7
feat(core): add check when deleting entity template
brandPittCode Aug 26, 2026
67cd17e
feat(core): add check when deleting entity template
brandPittCode Aug 27, 2026
aacc4f5
feat(core): add check when deleting entity template
brandPittCode Aug 27, 2026
a910924
feat(core): add check when deleting entity template
brandPittCode Aug 27, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/src/features/graph.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
---
title: Entity Relationship Graph
description: Visualize entity relationships and dependencies as interactive graphs
status: 🕐 Done
---

The Internal Developer Platform provides a powerful graph endpoint that returns entity relationships in a standardized format. You can easily visualize these relationships using frontend libraries like React Flow, D3.js, or similar graph visualization tools.
Expand Down
38 changes: 22 additions & 16 deletions docs/src/static/swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,12 @@ paths:
responses:
'204':
description: Template deleted successfully
'400':
description: 'Cannot delete template: it is a target for other relations'
content:
"*/*":
schema:
"$ref": "#/components/schemas/ErrorResponse"
'404':
description: Template not found with the provided identifier
content:
Expand Down Expand Up @@ -1833,13 +1839,13 @@ components:
format: int32
last:
type: boolean
numberOfElements:
type: integer
format: int32
sort:
"$ref": "#/components/schemas/SortObject"
first:
type: boolean
numberOfElements:
type: integer
format: int32
size:
type: integer
format: int32
Expand All @@ -1851,8 +1857,6 @@ components:
PageableObject:
type: object
properties:
unpaged:
type: boolean
paged:
type: boolean
pageNumber:
Expand All @@ -1863,16 +1867,18 @@ components:
format: int32
sort:
"$ref": "#/components/schemas/SortObject"
unpaged:
type: boolean
offset:
type: integer
format: int64
SortObject:
type: object
properties:
unsorted:
type: boolean
sorted:
type: boolean
unsorted:
type: boolean
empty:
type: boolean
WebhookConnectorPageResponse:
Expand All @@ -1893,13 +1899,13 @@ components:
format: int32
last:
type: boolean
numberOfElements:
type: integer
format: int32
sort:
"$ref": "#/components/schemas/SortObject"
first:
type: boolean
numberOfElements:
type: integer
format: int32
size:
type: integer
format: int32
Expand All @@ -1926,13 +1932,13 @@ components:
format: int32
last:
type: boolean
numberOfElements:
type: integer
format: int32
sort:
"$ref": "#/components/schemas/SortObject"
first:
type: boolean
numberOfElements:
type: integer
format: int32
size:
type: integer
format: int32
Expand All @@ -1959,13 +1965,13 @@ components:
format: int32
last:
type: boolean
numberOfElements:
type: integer
format: int32
sort:
"$ref": "#/components/schemas/SortObject"
first:
type: boolean
numberOfElements:
type: integer
format: int32
size:
type: integer
format: int32
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ public class ValidationMessages {
public static final String RELATION_TARGET_TEMPLATE_MISMATCH = "Relation '%s' targets entities with template '%s', but entity '%s' has template '%s'";
public static final String RELATION_TARGET_TEMPLATE_CANNOT_CHANGE = "Cannot change target template of relation '%s' from '%s' to '%s'. Target template cannot be modified after creation. Please delete and recreate the relation instead.";
public static final String RELATION_CANNOT_TARGET_ITSELF = "Relation '%s' cannot reference its own template '%s' as the target.";
public static final String TEMPLATE_IS_RELATION_TARGET = "Cannot delete template '%s': it is referenced as a target in another template's relation definition.";

// Entity input validation messages
public static final String ENTITY_NAME_MANDATORY = "Entity name is mandatory and cannot be blank";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.decathlon.idp_core.domain.exception.entity_template;

import static com.decathlon.idp_core.domain.constant.ValidationMessages.TEMPLATE_IS_RELATION_TARGET;

/// Exception thrown when attempting to delete a template that is still referenced
/// as a `targetTemplateIdentifier` in another template's relation definition.
///
/// This exception enforces the template relation target integrity business rule:
/// a template may not be deleted while other templates declare relations that
/// point to it as the target, because doing so would create dangling relation
/// definitions and break the integrity of those templates.
///
/// **Why this exception exists:**
/// - Prevents orphaned relation definitions in other templates
/// - Provides a clear, domain-specific error for callers
public class EntityTemplateIsRelationTargetException extends RuntimeException {

private final String identifier;

/// Constructs a new exception for the template identifier that cannot be
/// deleted.
///
/// @param identifier the identifier of the template blocked from deletion
public EntityTemplateIsRelationTargetException(String identifier) {
super(String.format(TEMPLATE_IS_RELATION_TARGET, identifier));
this.identifier = identifier;
}

public String getIdentifier() {
return identifier;
}
}
Comment thread
brandPittCode marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -79,4 +79,17 @@ PaginatedResult<Entity> search(SearchFilterNode filter, String query,

void deleteByTemplateIdentifierAndIdentifier(String templateIdentifier, String entityIdentifier);

/// Deletes all entities that belong to the given template.
///
/// **Business contract:** Called as part of the template cascade-delete flow
/// after all
/// referential integrity checks have passed. Implementors must ensure that
/// entity-owned
/// child records (properties, relations) are also removed.
///
/// @param templateIdentifier the business identifier of the template whose
/// entities
/// should be deleted
void deleteAllByTemplateIdentifier(String templateIdentifier);

}
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,20 @@ public interface EntityTemplateRepositoryPort {

void deleteByIdentifier(String identifier);

/// Returns `true` if any template **other than** the one identified by
/// `identifier`
/// declares a relation definition whose `targetTemplateIdentifier` equals
/// `identifier`.
///
/// Self-referential relation definitions (a template pointing to itself) are
/// excluded
/// because they are removed as part of the cascade when the template itself is
/// deleted.
///
/// @param identifier the business identifier of the template to check
/// @return `true` when at least one other template targets this template in a
/// relation
boolean existsRelationTargetingTemplate(String identifier);

boolean existsById(UUID id);
}
Original file line number Diff line number Diff line change
Expand Up @@ -156,20 +156,24 @@ public EntityTemplate updateEntityTemplate(String identifier,
return savedTemplate;
}

/// Deletes an entity template by business identifier with existence validation.
/// Deletes an entity template by business identifier with existence validation
/// and cascade purge of all its entities.
///
/// **Contract:** Validates template existence before deletion to ensure
/// referential
/// integrity. Deletion cascades through persistence layer according to
/// configured
/// relationships. This operation is irreversible once committed.
/// **Contract:** Validates that the template exists and is not referenced as a
/// relation target in another template. Then purges all entities belonging to
/// the template before deleting the template itself. Both the entity purge and
/// the template deletion happen within the same transaction so any failure
/// rolls back the entire operation.
///
/// @param identifier unique business identifier of template to delete
/// @param templateIdentifier unique business identifier of template to delete
/// @throws EntityTemplateNotFoundException when template doesn't exist
/// @throws EntityTemplateIsRelationTargetException when another template
/// references this template as a relation target
@Transactional
public void deleteEntityTemplate(String identifier) {
entityTemplateValidationService.validateForDeletion(identifier);
entityTemplateRepositoryPort.deleteByIdentifier(identifier);
public void deleteEntityTemplate(String templateIdentifier) {
entityTemplateValidationService.validateForDeletion(templateIdentifier);
entityRepositoryPort.deleteAllByTemplateIdentifier(templateIdentifier);
entityTemplateRepositoryPort.deleteByIdentifier(templateIdentifier);
}

private List<PropertyDefinition> mergePropertyDefinitions(List<PropertyDefinition> existing,
Expand Down
Loading
Loading