diff --git a/docs/docs/variability4tosca/specification/index.md b/docs/docs/variability4tosca/specification/index.md index a3ff521cb5..da0a1ceb9c 100644 --- a/docs/docs/variability4tosca/specification/index.md +++ b/docs/docs/variability4tosca/specification/index.md @@ -259,14 +259,16 @@ In contrast, a semantic condition targets semantic aspect of elements or the ty Depending on the context, other default conditions are more applicable. The following default conditions can be chosen instead of the ones introduced above. -| Element | Consistency | Semantic | Default Conditions | -|--------------------------------------------------------|-------------|----------|--------------------------------------------------------------------------------| -| Node Template with Incoming Relations (incomingnaive) | false | true | Check if any incoming relation is present using `has_incoming_relation_naive`. | -| Node Template with Incoming Relations (source) | false | true | Check if any source of any incoming relation is present. | -| Node Template with Host (host) | false | true | Check if any host is present. | -| Node Template with Artifact (artifactnaive) | false | true | Check if any artifact is present using `has_artifact_naive`. | -| Relation (Source) | true | false | Check if the source of the relation is present. | -| Relation (Target) | true | false | Check if the target of the relation is present. | +| Element | Consistency | Semantic | Default Conditions | +|-------------------------------------------------------|-------------|----------|--------------------------------------------------------------------------------| +| Node Template with Incoming Relations (incomingnaive) | false | true | Check if any incoming relation is present using `has_incoming_relation_naive`. | +| Node Template with Incoming Relations (source) | false | true | Check if any source of any incoming relation is present. | +| Node Template with Outgoing Relations (outgoing) | false | true | Check if any outgoing relation is present. | +| Node Template with Outgoing Relations (outgoingnaive) | false | true | Check if any outgoing relation is present using `has_outgoing_relation_naive`. | +| Node Template with Host (host) | false | true | Check if any host is present. | +| Node Template with Artifact (artifactnaive) | false | true | Check if any artifact is present using `has_artifact_naive`. | +| Relation (Source) | true | false | Check if the source of the relation is present. | +| Relation (Target) | true | false | Check if the target of the relation is present. | ### Type-Specific Default Conditions @@ -741,6 +743,8 @@ The following presence operators can be used inside a logic expression. | has_source | Node: String | SELF | CONTAINER | Boolean | Returns if any source of any incoming relation of the node template is present. | | has_incoming_relation | Node: String | SELF | CONTAINER | Boolean | Returns if the node template is target of at least one present incoming relationship. | | has_incoming_relation_naive | Node: String | SELF | CONTAINER | Boolean | Returns if the node template is target of at least one present incoming relationship in a naive way that will result in a circle considering the default condition of relations. | +| has_outgoing_relation | Node: String | SELF | CONTAINER | Boolean | Returns if the node template is source of at least one present outgoing relationship. | +| has_outgoing_relation_naive | Node: String | SELF | CONTAINER | Boolean | Returns if the node template is source of at least one present outgoing relationship in a naive way that will result in a circle considering the default condition of relations. | | has_artifact | Node: String | SELF | CONTAINER | Boolean | Returns if any artifact of the node template is present. | | has_artifact_naive | Node: String | SELF | CONTAINER | Boolean | Returns if any artifact of the node template is present in a naive way that will result in a circle considering the default condition of artifacts. | | relation_presence | Tuple(Node: String | SELF | CONTAINER, Relation: String | Number) | Boolean | Returns if relation is present. | diff --git a/src/graph/node.ts b/src/graph/node.ts index 91a0a32964..7499f402eb 100644 --- a/src/graph/node.ts +++ b/src/graph/node.ts @@ -129,6 +129,18 @@ export default class Node extends Element { ) } + if (it === 'outgoing') { + return conditions.push( + this.isSource ? {has_outgoing_relation: this.toscaId, _cached_element: this} : true + ) + } + + if (it === 'outgoingnaive') { + return conditions.push( + this.isSource ? {has_outgoing_relation_naive: this.toscaId, _cached_element: this} : true + ) + } + if (it === 'artifact') { return conditions.push(this.hasArtifact ? {has_artifact: this.toscaId, _cached_element: this} : true) } diff --git a/src/resolver/solver.ts b/src/resolver/solver.ts index 0354bf7169..2d8ea18c88 100644 --- a/src/resolver/solver.ts +++ b/src/resolver/solver.ts @@ -451,6 +451,28 @@ export default class Solver { return MiniSat.or(node.ingoing.map(it => it.id)) } + /** + * has_outgoing_relation + */ + if (check.isDefined(expression.has_outgoing_relation)) { + const node = this.graph.getNode(expression.has_outgoing_relation, { + element, + cached, + }) + return MiniSat.or(node.outgoing.map(it => MiniSat.and(it.explicitId, it.target.id))) + } + + /** + * has_outgoing_relation_naive + */ + if (check.isDefined(expression.has_outgoing_relation_naive)) { + const node = this.graph.getNode(expression.has_outgoing_relation_naive, { + element, + cached, + }) + return MiniSat.or(node.outgoing.map(it => it.id)) + } + /** * has_artifact */ diff --git a/src/specification/variability.ts b/src/specification/variability.ts index 6636c6f8cd..5fccbc185c 100644 --- a/src/specification/variability.ts +++ b/src/specification/variability.ts @@ -31,6 +31,8 @@ export type NodeDefaultConditionMode = | 'artifact' | 'artifactnaive' | 'incoming-artifact' + | 'outgoing' + | 'outgoingnaive' export type RelationDefaultConditionMode = 'source-target' | 'source' | 'target' export type DefaultOptions = { @@ -202,6 +204,8 @@ export type LogicExpression = has_source?: string | 'SELF' | 'CONTAINER' has_incoming_relation?: string | 'SELF' | 'CONTAINER' has_incoming_relation_naive?: string | 'SELF' | 'CONTAINER' + has_outgoing_relation?: string | 'SELF' | 'CONTAINER' + has_outgoing_relation_naive?: string | 'SELF' | 'CONTAINER' has_artifact?: string | 'SELF' | 'CONTAINER' has_artifact_naive?: string | 'SELF' | 'CONTAINER' diff --git a/tests/conformance/pruning/outgoing-relation-absent/expected.yaml b/tests/conformance/pruning/outgoing-relation-absent/expected.yaml new file mode 100644 index 0000000000..00d0e3caeb --- /dev/null +++ b/tests/conformance/pruning/outgoing-relation-absent/expected.yaml @@ -0,0 +1,6 @@ +tosca_definitions_version: tosca_simple_yaml_1_3 + +topology_template: + node_templates: + target: + type: target diff --git a/tests/conformance/pruning/outgoing-relation-absent/template.yaml b/tests/conformance/pruning/outgoing-relation-absent/template.yaml new file mode 100644 index 0000000000..f533469042 --- /dev/null +++ b/tests/conformance/pruning/outgoing-relation-absent/template.yaml @@ -0,0 +1,22 @@ +tosca_definitions_version: tosca_variability_1_0 + +topology_template: + variability: + options: + node_default_condition: true + node_default_condition_mode: outgoing + + relation_default_condition: true + relation_default_condition_mode: source-target + + type_default_condition: true + node_templates: + source: + type: source + requirements: + - relation: + node: target + conditions: false + + target: + type: target diff --git a/tests/conformance/pruning/outgoing-relation-absent/test.yaml b/tests/conformance/pruning/outgoing-relation-absent/test.yaml new file mode 100644 index 0000000000..642f7dc0bb --- /dev/null +++ b/tests/conformance/pruning/outgoing-relation-absent/test.yaml @@ -0,0 +1,8 @@ +description: | + - The node "source" has an outgoing relation "relation" to node "target". + - The node default condition mode "outgoing" is used. + - Thus, there is no cycle. + + - "relation" is absent + - Thus, "source" is absent + - "target" is always present diff --git a/tests/conformance/pruning/outgoing-relation-present/expected.yaml b/tests/conformance/pruning/outgoing-relation-present/expected.yaml new file mode 100644 index 0000000000..9a6940e854 --- /dev/null +++ b/tests/conformance/pruning/outgoing-relation-present/expected.yaml @@ -0,0 +1,12 @@ +tosca_definitions_version: tosca_simple_yaml_1_3 + +topology_template: + node_templates: + source: + type: source + requirements: + - relation: + node: target + + target: + type: target diff --git a/tests/conformance/pruning/outgoing-relation-present/template.yaml b/tests/conformance/pruning/outgoing-relation-present/template.yaml new file mode 100644 index 0000000000..c0e0837882 --- /dev/null +++ b/tests/conformance/pruning/outgoing-relation-present/template.yaml @@ -0,0 +1,22 @@ +tosca_definitions_version: tosca_variability_1_0 + +topology_template: + variability: + options: + node_default_condition: true + node_default_condition_mode: outgoing + + relation_default_condition: true + relation_default_condition_mode: source-target + + type_default_condition: true + node_templates: + source: + type: source + requirements: + - relation: + node: target + conditions: true + + target: + type: target diff --git a/tests/conformance/pruning/outgoing-relation-present/test.yaml b/tests/conformance/pruning/outgoing-relation-present/test.yaml new file mode 100644 index 0000000000..680d6f96c9 --- /dev/null +++ b/tests/conformance/pruning/outgoing-relation-present/test.yaml @@ -0,0 +1,8 @@ +description: | + - The node "source" has an outgoing relation "relation" to node "target". + - The node default condition mode "outgoing" is used. + - Thus, there is no cycle. + + - "relation" is present + - Thus, "source" is present + - "target" is always present diff --git a/tests/conformance/pruning/outgoing-target-absent/expected.yaml b/tests/conformance/pruning/outgoing-target-absent/expected.yaml new file mode 100644 index 0000000000..7f3f0f125b --- /dev/null +++ b/tests/conformance/pruning/outgoing-target-absent/expected.yaml @@ -0,0 +1 @@ +tosca_definitions_version: tosca_simple_yaml_1_3 diff --git a/tests/conformance/pruning/outgoing-target-absent/template.yaml b/tests/conformance/pruning/outgoing-target-absent/template.yaml new file mode 100644 index 0000000000..d3be533fa9 --- /dev/null +++ b/tests/conformance/pruning/outgoing-target-absent/template.yaml @@ -0,0 +1,22 @@ +tosca_definitions_version: tosca_variability_1_0 + +topology_template: + variability: + options: + node_default_condition: true + node_default_condition_mode: outgoing + + relation_default_condition: true + relation_default_condition_mode: source-target + + type_default_condition: true + node_templates: + source: + type: source + requirements: + - relation: + node: target + + target: + type: target + conditions: false diff --git a/tests/conformance/pruning/outgoing-target-absent/test.yaml b/tests/conformance/pruning/outgoing-target-absent/test.yaml new file mode 100644 index 0000000000..ba67175c01 --- /dev/null +++ b/tests/conformance/pruning/outgoing-target-absent/test.yaml @@ -0,0 +1,8 @@ +description: | + - The node "source" has an outgoing relation "relation" to node "target". + - The node default condition mode "outgoing" is used. + - Thus, there is no cycle. + + - "target" is absent + - Thus, "relation" is absent + - Thus, "source" is absent diff --git a/tests/conformance/pruning/outgoing-target-present/expected.yaml b/tests/conformance/pruning/outgoing-target-present/expected.yaml new file mode 100644 index 0000000000..9a6940e854 --- /dev/null +++ b/tests/conformance/pruning/outgoing-target-present/expected.yaml @@ -0,0 +1,12 @@ +tosca_definitions_version: tosca_simple_yaml_1_3 + +topology_template: + node_templates: + source: + type: source + requirements: + - relation: + node: target + + target: + type: target diff --git a/tests/conformance/pruning/outgoing-target-present/template.yaml b/tests/conformance/pruning/outgoing-target-present/template.yaml new file mode 100644 index 0000000000..66668c41c6 --- /dev/null +++ b/tests/conformance/pruning/outgoing-target-present/template.yaml @@ -0,0 +1,21 @@ +tosca_definitions_version: tosca_variability_1_0 + +topology_template: + variability: + options: + node_default_condition: true + node_default_condition_mode: outgoing + + relation_default_condition: true + relation_default_condition_mode: source-target + + type_default_condition: true + node_templates: + source: + type: source + requirements: + - relation: + node: target + + target: + type: target diff --git a/tests/conformance/pruning/outgoing-target-present/test.yaml b/tests/conformance/pruning/outgoing-target-present/test.yaml new file mode 100644 index 0000000000..a57a6f98fd --- /dev/null +++ b/tests/conformance/pruning/outgoing-target-present/test.yaml @@ -0,0 +1,5 @@ +description: | + - The node "source" has an outgoing relation "relation" to node "target". + - The node default condition mode "outgoing" is used. + - Thus, there is no cycle. + - Nothing is removed. diff --git a/tests/conformance/pruning/outgoingnaive-target-present/expected.yaml b/tests/conformance/pruning/outgoingnaive-target-present/expected.yaml new file mode 100644 index 0000000000..00d0e3caeb --- /dev/null +++ b/tests/conformance/pruning/outgoingnaive-target-present/expected.yaml @@ -0,0 +1,6 @@ +tosca_definitions_version: tosca_simple_yaml_1_3 + +topology_template: + node_templates: + target: + type: target diff --git a/tests/conformance/pruning/outgoingnaive-target-present/template.yaml b/tests/conformance/pruning/outgoingnaive-target-present/template.yaml new file mode 100644 index 0000000000..a1331b0e49 --- /dev/null +++ b/tests/conformance/pruning/outgoingnaive-target-present/template.yaml @@ -0,0 +1,21 @@ +tosca_definitions_version: tosca_variability_1_0 + +topology_template: + variability: + options: + node_default_condition: true + node_default_condition_mode: outgoingnaive + + relation_default_condition: true + relation_default_condition_mode: source-target + + type_default_condition: true + node_templates: + source: + type: source + requirements: + - relation: + node: target + + target: + type: target diff --git a/tests/conformance/pruning/outgoingnaive-target-present/test.yaml b/tests/conformance/pruning/outgoingnaive-target-present/test.yaml new file mode 100644 index 0000000000..50a2a5f09d --- /dev/null +++ b/tests/conformance/pruning/outgoingnaive-target-present/test.yaml @@ -0,0 +1,6 @@ +description: | + - The node "source" has an outgoing relation "relation" to node "target". + - The node default condition mode "outgoingnaive" is used. + - Thus, there is a cycle: "source" checks if "relation" is present and "relation" checks if "source" is present. + - Thus, "source" and "relation" can be removed. + - "target" is present since it has no default condition assigned.