Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
84 changes: 39 additions & 45 deletions backend/src/main/kotlin/dev/dres/mgmt/TemplateManager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -91,17 +91,47 @@ object TemplateManager {
dbEvaluationTemplate.description = apiEvaluationTemplate.description
dbEvaluationTemplate.modified = DateTime.now()

/* Update task type information. */
/*
* Deletions in reverse dependency order to avoid Xodus constraint failures.
* DbTaskGroup.type has onTargetDelete=CASCADE, so deleting a DbTaskType triggers
* cascade deletion of DbTaskGroup. But DbTaskTemplate.taskGroup has no onTargetDelete
* (implicit FAIL), so that cascade is blocked while any task template still exists.
* Solution: delete task templates first, then groups, then types.
*/

/* 1. Delete task templates. */
val taskIds = apiEvaluationTemplate.tasks.mapNotNull { it.id }.toTypedArray()
val taskTemplatesToDeleteQuery = DbTaskTemplate.query(
DbTaskTemplate::evaluation eq dbEvaluationTemplate and not(DbTaskTemplate::id.containsIn(*taskIds))
)
val hintsToDelIds = taskTemplatesToDeleteQuery.toList().map {
it.hints.toList().map { hint -> hint.entityId }
}.flatten().toTypedArray()
val targetsToDelIds = taskTemplatesToDeleteQuery.toList().map {
it.targets.toList().map { target -> target.entityId }
}.flatten().toTypedArray()
dbEvaluationTemplate.tasks.removeAll(taskTemplatesToDeleteQuery)
DbHint.all().toList().filter { hintsToDelIds.contains(it.entityId) }.forEach { it.delete() }
DbTaskTemplateTarget.all().toList().filter { targetsToDelIds.contains(it.entityId) }.forEach { it.delete() }

/* 2. Delete task groups. */
val taskGroups = apiEvaluationTemplate.taskGroups.map { it.name }.toTypedArray()
dbEvaluationTemplate.taskGroups.removeAll(
DbTaskGroup.query(DbTaskGroup::evaluation eq dbEvaluationTemplate and not(DbTaskGroup::name.containsIn(*taskGroups)))
)

/* 3. Delete task types — now safe, no task group references them. */
val taskTypes = apiEvaluationTemplate.taskTypes.map { it.name }.toTypedArray()
val taskTypesToDeleteQuery = DbTaskType.query(DbTaskType::evaluation eq dbEvaluationTemplate and not(DbTaskType::name.containsIn(*taskTypes)))
/* Manual cleanup, as removeAll does not cleanup related children. */
val configuredOptionsToDelIds= taskTypesToDeleteQuery.toList().map {
it.configurations.toList().map{opt -> opt.entityId}
val configuredOptionsToDelIds = taskTypesToDeleteQuery.toList().map {
it.configurations.toList().map { opt -> opt.entityId }
}.flatten().toTypedArray()
DbConfiguredOption.all().toList().filter{configuredOptionsToDelIds.contains(it.entityId)}.forEach{it.delete()}

DbConfiguredOption.all().toList().filter { configuredOptionsToDelIds.contains(it.entityId) }.forEach { it.delete() }
dbEvaluationTemplate.taskTypes.removeAll(taskTypesToDeleteQuery)

/* Updates/additions in forward dependency order so references can be resolved. */

/* 4. Update task types. */
for (apiTaskType in apiEvaluationTemplate.taskTypes) {
val taskType =
DbTaskType.findOrNew(DbTaskType.query((DbTaskType::name eq apiTaskType.name) and (DbTaskType::evaluation eq dbEvaluationTemplate))) {
Expand All @@ -118,73 +148,37 @@ object TemplateManager {
taskType.options.addAll(apiTaskType.taskOptions.map { it.toDb() })
taskType.configurations.clear()
taskType.configurations.addAll(apiTaskType.configuration.entries.map {
if(!it.key.contains(".") || it.key.split(".").size != 2){
if (!it.key.contains(".") || it.key.split(".").size != 2) {
throw IllegalArgumentException("Type Configurations must be in the form of DOMAIN.KEY, but ${it.key} given")
}
DbConfiguredOption.new {
this.key = it.key
this.value = it.value
}
})

/* Establish relationship if entry is new. */
if (taskType.isNew) {
dbEvaluationTemplate.taskTypes.add(taskType)
}
}

/* Update task group information. */
val taskGroups = apiEvaluationTemplate.taskGroups.map { it.name }.toTypedArray()
dbEvaluationTemplate.taskGroups.removeAll(
DbTaskGroup.query(DbTaskGroup::evaluation eq dbEvaluationTemplate and not(DbTaskGroup::name.containsIn(*taskGroups)))
)
/* 5. Update task groups. */
for (apiTaskGroup in apiEvaluationTemplate.taskGroups) {
val taskGroup =
DbTaskGroup.findOrNew(DbTaskGroup.query((DbTaskGroup::name eq apiTaskGroup.name) and (DbTaskGroup::evaluation eq dbEvaluationTemplate))) {
this.name = apiTaskGroup.name
}

/* Update task type if it has changed. */
if (taskGroup.getSafe(DbTaskGroup::type)?.name != apiTaskGroup.name) {
taskGroup.type =
DbTaskType.query((DbTaskType::name eq apiTaskGroup.type) and (DbTaskType::evaluation eq dbEvaluationTemplate))
.firstOrNull()
?: throw IllegalArgumentException("Unknown task group ${apiTaskGroup.type} for evaluation ${apiEvaluationTemplate.id}.")
}

/* Establish relationship if entry is new. */
if (taskGroup.isNew) {
dbEvaluationTemplate.taskGroups.add(taskGroup)
}
}

/* Update task information: Remove deleted tasks. */
val taskIds = apiEvaluationTemplate.tasks.mapNotNull { it.id }.toTypedArray()
val taskTemplatesToDeleteQuery = DbTaskTemplate.query(
DbTaskTemplate::evaluation eq dbEvaluationTemplate and not(
DbTaskTemplate::id.containsIn(*taskIds)
)
)
val hintsToDelIds = taskTemplatesToDeleteQuery.toList().map {
it.hints.toList().map { hint -> hint.entityId }
}.flatten().toTypedArray()
val targetsToDelIds = taskTemplatesToDeleteQuery.toList().map{
it.targets.toList().map{target -> target.entityId}
}.flatten().toTypedArray()

dbEvaluationTemplate.tasks.removeAll(
taskTemplatesToDeleteQuery
)
/*
DbTaskTemplate has children relationships with both, DbHint and DbTaskTarget.
Despite being written in the documentation, for some reason the .removeAll above does not
delete the children, hence we have to take care of it ourselves.
https://jetbrains.github.io/xodus-dnq/properties.html
*/
DbHint.all().toList().filter{hintsToDelIds.contains(it.entityId)}.forEach { it.delete() }
DbTaskTemplateTarget.all().toList().filter{targetsToDelIds.contains(it.entityId)}.forEach{it.delete()}

/* Update task information: Remaining tasks. */
/* 6. Update task templates: remaining tasks. */
apiEvaluationTemplate.tasks.forEachIndexed { idx, apiTask ->
val task = if (apiTask.id != null) {
dbEvaluationTemplate.tasks.filter { it.id eq apiTask.id }.firstOrNull()
Expand Down
Loading