Skip to content

Commit 37ef2fb

Browse files
authored
Merge pull request #1020 from silk-framework/fix/mappingCreatorUx-CMEM-7320
Add option to have tree formatted labels for the rule search endpoint
2 parents 2340538 + 125228f commit 37ef2fb

5 files changed

Lines changed: 438 additions & 175 deletions

File tree

silk-workbench/silk-workbench-rules/app/controllers/transform/TransformTaskApi.scala

Lines changed: 74 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ import org.silkframework.runtime.activity.{Activity, UserContext}
2626
import org.silkframework.runtime.plugin.PluginContext
2727
import org.silkframework.runtime.resource.ResourceManager
2828
import org.silkframework.runtime.serialization.{ReadContext, WriteContext}
29-
import org.silkframework.runtime.templating.{GlobalTemplateVariables, TemplateVariablesReader}
3029
import org.silkframework.runtime.validation.{BadUserInputException, NotFoundException, ValidationError, ValidationException}
3130
import org.silkframework.serialization.json.JsonParseException
3231
import org.silkframework.serialization.json.JsonSerializers._
@@ -276,22 +275,46 @@ class TransformTaskApi @Inject() () extends InjectedController with UserContextA
276275
implicit val (project, task) = getProjectAndTask[TransformSpec](projectName, taskName)
277276
implicit val prefixes: Prefixes = project.config.prefixes
278277
validateJson[RuleAutoCompletionRequest] { requestData =>
279-
val allRules = task.data.allRulesRecursive
278+
val searchQuery = requestData.searchQuery.getOrElse("")
279+
val format = requestData.format.getOrElse(false)
280+
val maxResults = requestData.limit.getOrElse(Int.MaxValue)
280281
val filter: TransformRule => Boolean = (requestData.objectRulesOnly.getOrElse(false), requestData.valueRulesOnly.getOrElse(false)) match {
281282
case (true, false) => (r: TransformRule) => r.isInstanceOf[ContainerTransformRule]
282283
case (false, true) => (r: TransformRule) => r.isInstanceOf[ValueTransformRule]
283284
case _ => (_: TransformRule) => true
284285
}
285-
val completions: Seq[Completion] = allRules
286-
.filter(filter)
287-
.map(r => Completion(
288-
value = r.id,
289-
label = Some(r.fullLabel),
290-
description = r.metaData.description,
291-
category = Categories.rules,
292-
isCompletion = true
293-
))
294-
Ok(Completions(completions).filterAndSort(requestData.searchQuery.getOrElse(""), requestData.limit.getOrElse(Int.MaxValue), multiWordFilter = true).toJson)
286+
if(searchQuery.trim.isEmpty) {
287+
val formattedLabels = format && !requestData.valueRulesOnly.getOrElse(false)
288+
val formattedLabelsByRule = if(formattedLabels) {
289+
TransformTaskApi.formattedRulesInTreeOrder(task.data.mappingRule, filter).toMap
290+
} else {
291+
Map.empty[TransformRule, String]
292+
}
293+
val completions = TransformTaskApi.rulesInTreeOrder(task.data.mappingRule)
294+
.filter(filter)
295+
.map { rule =>
296+
Completion(
297+
value = rule.id,
298+
label = Some(formattedLabelsByRule.getOrElse(rule, rule.fullLabel)),
299+
description = rule.metaData.description,
300+
category = Categories.rules,
301+
isCompletion = true
302+
)
303+
}
304+
.take(maxResults)
305+
Ok(Completions(completions).toJson)
306+
} else {
307+
val completions: Seq[Completion] = task.data.allRulesRecursive
308+
.filter(filter)
309+
.map(r => Completion(
310+
value = r.id,
311+
label = Some(r.fullLabel),
312+
description = r.metaData.description,
313+
category = Categories.rules,
314+
isCompletion = true
315+
))
316+
Ok(Completions(completions).filterAndSort(searchQuery, maxResults, multiWordFilter = true).toJson)
317+
}
295318
}
296319
}
297320

@@ -1055,5 +1078,43 @@ object TransformTaskApi {
10551078

10561079
// The property that is set when copying a root mapping rule that will be converted into an object mapping rule
10571080
final val ROOT_COPY_TARGET_PROPERTY = "urn:temp:child"
1058-
}
10591081

1082+
private def rulesInTreeOrder(rule: TransformRule): Seq[TransformRule] = {
1083+
rule +: rule.rules.allRules.flatMap(rulesInTreeOrder)
1084+
}
1085+
1086+
private def formattedRulesInTreeOrder(rule: TransformRule, filter: TransformRule => Boolean)
1087+
(implicit prefixes: Prefixes): Seq[(TransformRule, String)] = {
1088+
def hasVisibleNodes(currentRule: TransformRule): Boolean = {
1089+
filter(currentRule) || currentRule.rules.allRules.exists(hasVisibleNodes)
1090+
}
1091+
1092+
def visibleChildren(currentRule: TransformRule): Seq[TransformRule] = {
1093+
currentRule.rules.allRules.filter(hasVisibleNodes)
1094+
}
1095+
1096+
def render(currentRule: TransformRule,
1097+
ancestorLastStates: Seq[Boolean],
1098+
isLast: Boolean,
1099+
isRoot: Boolean): Seq[(TransformRule, String)] = {
1100+
val currentPrefix =
1101+
if(isRoot) {
1102+
""
1103+
} else {
1104+
ancestorLastStates.map(last => if(last) " " else "").mkString + (if(isLast) "└─ " else "├─ ")
1105+
}
1106+
val currentEntry = if(filter(currentRule)) {
1107+
Seq(currentRule -> (currentPrefix + currentRule.fullLabel))
1108+
} else {
1109+
Seq.empty
1110+
}
1111+
val nextAncestorLastStates = if(isRoot) ancestorLastStates else ancestorLastStates :+ isLast
1112+
val children = visibleChildren(currentRule)
1113+
currentEntry ++ children.zipWithIndex.flatMap { case (child, index) =>
1114+
render(child, nextAncestorLastStates, isLast = index == children.size - 1, isRoot = false)
1115+
}
1116+
}
1117+
1118+
render(rule, Seq.empty, isLast = true, isRoot = true)
1119+
}
1120+
}

silk-workbench/silk-workbench-rules/app/controllers/transform/autoCompletion/RuleAutoCompletionRequest.scala

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,20 @@ package controllers.transform.autoCompletion
33
import org.silkframework.runtime.validation.ValidationException
44
import play.api.libs.json.{Format, Json}
55

6+
/**
7+
* Rule auto-completion request.
8+
* @param valueRulesOnly If only value rules should be returned.
9+
* @param objectRulesOnly If only object rules should be returned.
10+
* @param searchQuery The optional text search query.
11+
* @param limit Optional limit of results returned.
12+
* @param format If true, the labels will be formatted in some cases, resembling the tree structure of the rules.
13+
*/
614
case class RuleAutoCompletionRequest(valueRulesOnly: Option[Boolean],
715
objectRulesOnly: Option[Boolean],
816
searchQuery: Option[String],
9-
limit: Option[Int]) {
17+
limit: Option[Int],
18+
format: Option[Boolean]
19+
) {
1020
if(valueRulesOnly.getOrElse(false) && objectRulesOnly.getOrElse(false)) {
1121
throw new ValidationException("valueRulesOnly and objectRulesOnly cannot be both be true at the same time!")
1222
}

0 commit comments

Comments
 (0)