2  * Copyright © 2017-2018 AT&T Intellectual Property.
 
   3  * Modifications Copyright © 2018 IBM.
 
   5  * Licensed under the Apache License, Version 2.0 (the "License");
 
   6  * you may not use this file except in compliance with the License.
 
   7  * You may obtain a copy of the License at
 
   9  *     http://www.apache.org/licenses/LICENSE-2.0
 
  11  * Unless required by applicable law or agreed to in writing, software
 
  12  * distributed under the License is distributed on an "AS IS" BASIS,
 
  13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 
  14  * See the License for the specific language governing permissions and
 
  15  * limitations under the License.
 
  18 package org.onap.ccsdk.apps.controllerblueprints.core.service
 
  21 import com.att.eelf.configuration.EELFLogger
 
  22 import com.att.eelf.configuration.EELFManager
 
  23 import com.fasterxml.jackson.databind.JsonNode
 
  24 import com.fasterxml.jackson.databind.node.NullNode
 
  25 import com.fasterxml.jackson.databind.node.ObjectNode
 
  26 import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
 
  27 import org.onap.ccsdk.apps.controllerblueprints.core.BluePrintConstants
 
  28 import org.onap.ccsdk.apps.controllerblueprints.core.BluePrintError
 
  29 import org.onap.ccsdk.apps.controllerblueprints.core.BluePrintProcessorException
 
  30 import org.onap.ccsdk.apps.controllerblueprints.core.data.ArtifactDefinition
 
  31 import org.onap.ccsdk.apps.controllerblueprints.core.data.NodeTemplate
 
  32 import org.onap.ccsdk.apps.controllerblueprints.core.data.PropertyDefinition
 
  33 import org.onap.ccsdk.apps.controllerblueprints.core.utils.JacksonUtils
 
  35 interface BluePrintRuntimeService<T> {
 
  39     fun bluePrintContext(): BluePrintContext
 
  41     fun getExecutionContext(): T
 
  43     fun setExecutionContext(executionContext: T)
 
  45     fun put(key: String, value: JsonNode)
 
  47     fun get(key: String): JsonNode?
 
  49     fun check(key: String): Boolean
 
  53     fun getAsString(key: String): String?
 
  55     fun getAsBoolean(key: String): Boolean?
 
  57     fun getAsInt(key: String): Int?
 
  59     fun getAsDouble(key: String): Double?
 
  61     fun getBluePrintError(): BluePrintError
 
  63     fun setBluePrintError(bluePrintError: BluePrintError)
 
  65     fun resolveNodeTemplatePropertyAssignments(nodeTemplateName: String,
 
  66                                                propertyDefinitions: MutableMap<String, PropertyDefinition>,
 
  67                                                propertyAssignments: MutableMap<String, JsonNode>): MutableMap<String, JsonNode>
 
  69     fun resolveNodeTemplateProperties(nodeTemplateName: String): MutableMap<String, JsonNode>
 
  71     fun resolveNodeTemplateCapabilityProperties(nodeTemplateName: String, capabilityName: String): MutableMap<String,
 
  74     fun resolveNodeTemplateInterfaceOperationInputs(nodeTemplateName: String, interfaceName: String,
 
  75                                                     operationName: String): MutableMap<String, JsonNode>
 
  77     fun resolveNodeTemplateInterfaceOperationOutputs(nodeTemplateName: String, interfaceName: String,
 
  78                                                      operationName: String): MutableMap<String, JsonNode>
 
  80     fun resolveNodeTemplateArtifact(nodeTemplateName: String, artifactName: String): String
 
  82     fun resolveNodeTemplateArtifactDefinition(nodeTemplateName: String, artifactName: String): ArtifactDefinition
 
  84     fun setInputValue(propertyName: String, propertyDefinition: PropertyDefinition, value: JsonNode)
 
  86     fun setWorkflowInputValue(workflowName: String, propertyName: String, propertyDefinition: PropertyDefinition, value: JsonNode)
 
  88     fun setNodeTemplatePropertyValue(nodeTemplateName: String, propertyName: String, value: JsonNode)
 
  90     fun setNodeTemplateAttributeValue(nodeTemplateName: String, attributeName: String, value: JsonNode)
 
  92     fun setNodeTemplateOperationPropertyValue(nodeTemplateName: String, interfaceName: String,
 
  93                                               operationName: String, propertyName: String, value: JsonNode)
 
  95     fun setNodeTemplateOperationInputValue(nodeTemplateName: String, interfaceName: String,
 
  96                                            operationName: String, propertyName: String, value: JsonNode)
 
  98     fun setNodeTemplateOperationOutputValue(nodeTemplateName: String, interfaceName: String,
 
  99                                             operationName: String, propertyName: String, value: JsonNode)
 
 101     fun getInputValue(propertyName: String): JsonNode
 
 103     fun getNodeTemplateOperationOutputValue(nodeTemplateName: String, interfaceName: String,
 
 104                                             operationName: String, propertyName: String): JsonNode
 
 106     fun getNodeTemplatePropertyValue(nodeTemplateName: String, propertyName: String): JsonNode?
 
 108     fun getNodeTemplateAttributeValue(nodeTemplateName: String, attributeName: String): JsonNode?
 
 110     fun assignInputs(jsonNode: JsonNode)
 
 112     fun assignWorkflowInputs(workflowName: String, jsonNode: JsonNode)
 
 114     fun getJsonForNodeTemplateAttributeProperties(nodeTemplateName: String, keys: List<String>): JsonNode
 
 120  * @author Brinda Santh
 
 122 open class DefaultBluePrintRuntimeService(private var id: String, private var bluePrintContext: BluePrintContext)
 
 123     : BluePrintRuntimeService<MutableMap<String, JsonNode>> {
 
 126     private val log: EELFLogger = EELFManager.getInstance().getLogger(BluePrintRuntimeService::class.toString())
 
 128     private var store: MutableMap<String, JsonNode> = hashMapOf()
 
 130     private var bluePrintError = BluePrintError()
 
 132     override fun id(): String {
 
 136     override fun bluePrintContext(): BluePrintContext {
 
 137         return bluePrintContext
 
 140     override fun getExecutionContext(): MutableMap<String, JsonNode> {
 
 144     @Suppress("UNCHECKED_CAST")
 
 145     override fun setExecutionContext(executionContext: MutableMap<String, JsonNode>) {
 
 146         this.store = executionContext
 
 149     override fun put(key: String, value: JsonNode) {
 
 153     override fun get(key: String): JsonNode {
 
 154         return store[key] ?: throw BluePrintProcessorException("failed to get execution property($key)")
 
 157     override fun check(key: String): Boolean {
 
 158         return store.containsKey(key)
 
 161     override fun cleanRuntime() {
 
 165     private fun getJsonNode(key: String): JsonNode {
 
 169     override fun getAsString(key: String): String? {
 
 170         return get(key).asText()
 
 173     override fun getAsBoolean(key: String): Boolean? {
 
 174         return get(key).asBoolean()
 
 177     override fun getAsInt(key: String): Int? {
 
 178         return get(key).asInt()
 
 181     override fun getAsDouble(key: String): Double? {
 
 182         return get(key).asDouble()
 
 185     override fun getBluePrintError(): BluePrintError {
 
 186         return this.bluePrintError
 
 189     override fun setBluePrintError(bluePrintError: BluePrintError) {
 
 190         this.bluePrintError = bluePrintError
 
 194      * Resolve any property assignments for the node
 
 196     override fun resolveNodeTemplatePropertyAssignments(nodeTemplateName: String,
 
 197                                                         propertyDefinitions: MutableMap<String, PropertyDefinition>,
 
 198                                                         propertyAssignments: MutableMap<String, JsonNode>)
 
 199             : MutableMap<String, JsonNode> {
 
 201         val propertyAssignmentValue: MutableMap<String, JsonNode> = hashMapOf()
 
 203         propertyDefinitions.forEach { nodeTypePropertyName, nodeTypeProperty ->
 
 204             // Get the Express or Value for the Node Template
 
 205             val propertyAssignment: JsonNode? = propertyAssignments[nodeTypePropertyName]
 
 207             var resolvedValue: JsonNode = NullNode.getInstance()
 
 208             if (propertyAssignment != null) {
 
 209                 // Resolve the Expressing
 
 210                 val propertyAssignmentExpression = PropertyAssignmentService(this)
 
 211                 resolvedValue = propertyAssignmentExpression.resolveAssignmentExpression(nodeTemplateName,
 
 212                         nodeTypePropertyName, propertyAssignment)
 
 214                 // Assign default value to the Operation
 
 215                 nodeTypeProperty.defaultValue?.let {
 
 216                     resolvedValue = JacksonUtils.jsonNodeFromObject(nodeTypeProperty.defaultValue!!)
 
 219             // Set for Return of method
 
 220             propertyAssignmentValue[nodeTypePropertyName] = resolvedValue
 
 222         return propertyAssignmentValue
 
 225     override fun resolveNodeTemplateProperties(nodeTemplateName: String): MutableMap<String, JsonNode> {
 
 226         log.info("resolveNodeTemplatePropertyValues for node template ({})", nodeTemplateName)
 
 228         val nodeTemplate: NodeTemplate = bluePrintContext.nodeTemplateByName(nodeTemplateName)
 
 230         val propertyAssignments: MutableMap<String, JsonNode> = nodeTemplate.properties!!
 
 232         // Get the Node Type Definitions
 
 233         val nodeTypePropertieDefinitions: MutableMap<String, PropertyDefinition> = bluePrintContext
 
 234                 .nodeTypeChainedProperties(nodeTemplate.type)!!
 
 237          * Resolve the NodeTemplate Property Assignment Values.
 
 239         return resolveNodeTemplatePropertyAssignments(nodeTemplateName, nodeTypePropertieDefinitions,
 
 243     override fun resolveNodeTemplateCapabilityProperties(nodeTemplateName: String, capabilityName: String):
 
 244             MutableMap<String, JsonNode> {
 
 245         log.info("resolveNodeTemplateCapabilityProperties for node template($nodeTemplateName) capability " +
 
 247         val nodeTemplate: NodeTemplate = bluePrintContext.nodeTemplateByName(nodeTemplateName)
 
 249         val propertyAssignments = nodeTemplate.capabilities?.get(capabilityName)?.properties ?: hashMapOf()
 
 251         val propertyDefinitions = bluePrintContext.nodeTemplateNodeType(nodeTemplateName)
 
 252                 .capabilities?.get(capabilityName)?.properties ?: hashMapOf()
 
 255          * Resolve the Capability Property Assignment Values.
 
 257         return resolveNodeTemplatePropertyAssignments(nodeTemplateName, propertyDefinitions,
 
 261     override fun resolveNodeTemplateInterfaceOperationInputs(nodeTemplateName: String,
 
 262                                                              interfaceName: String,
 
 263                                                              operationName: String): MutableMap<String, JsonNode> {
 
 264         log.info("resolveNodeTemplateInterfaceOperationInputs for node template ($nodeTemplateName),interface name " +
 
 265                 "($interfaceName), operationName($operationName)")
 
 267         val propertyAssignments: MutableMap<String, JsonNode> =
 
 268                 bluePrintContext.nodeTemplateInterfaceOperationInputs(nodeTemplateName, interfaceName, operationName)
 
 271         val nodeTypeName = bluePrintContext.nodeTemplateByName(nodeTemplateName).type
 
 273         val nodeTypeInterfaceOperationInputs: MutableMap<String, PropertyDefinition> =
 
 274                 bluePrintContext.nodeTypeInterfaceOperationInputs(nodeTypeName, interfaceName, operationName)
 
 277         log.info("input definition for node template ($nodeTemplateName), values ($propertyAssignments)")
 
 280          * Resolve the Property Input Assignment Values.
 
 282         return resolveNodeTemplatePropertyAssignments(nodeTemplateName, nodeTypeInterfaceOperationInputs,
 
 288     override fun resolveNodeTemplateInterfaceOperationOutputs(nodeTemplateName: String,
 
 289                                                               interfaceName: String,
 
 290                                                               operationName: String): MutableMap<String, JsonNode> {
 
 291         log.info("resolveNodeTemplateInterfaceOperationOutputs for node template ($nodeTemplateName),interface name " +
 
 292                 "($interfaceName), operationName($operationName)")
 
 294         val propertyAssignments: MutableMap<String, JsonNode> =
 
 295                 bluePrintContext.nodeTemplateInterfaceOperationOutputs(nodeTemplateName, interfaceName, operationName)
 
 298         val nodeTypeName = bluePrintContext.nodeTemplateByName(nodeTemplateName).type
 
 300         val nodeTypeInterfaceOperationOutputs: MutableMap<String, PropertyDefinition> =
 
 301                 bluePrintContext.nodeTypeInterfaceOperationOutputs(nodeTypeName, interfaceName, operationName)
 
 304          * Resolve the Property Output Assignment Values.
 
 306         val propertyAssignmentValue = resolveNodeTemplatePropertyAssignments(nodeTemplateName,
 
 307                 nodeTypeInterfaceOperationOutputs, propertyAssignments)
 
 309         // Store  operation output values into context
 
 310         propertyAssignmentValue.forEach { key, value ->
 
 311             setNodeTemplateOperationOutputValue(nodeTemplateName, interfaceName, operationName, key, value)
 
 313         return propertyAssignmentValue
 
 316     override fun resolveNodeTemplateArtifact(nodeTemplateName: String, artifactName: String): String {
 
 317         val artifactDefinition: ArtifactDefinition = resolveNodeTemplateArtifactDefinition(nodeTemplateName, artifactName)
 
 318         val propertyAssignmentExpression = PropertyAssignmentService(this)
 
 319         return propertyAssignmentExpression.artifactContent(artifactDefinition)
 
 322     override fun resolveNodeTemplateArtifactDefinition(nodeTemplateName: String, artifactName: String): ArtifactDefinition {
 
 323         val nodeTemplate = bluePrintContext.nodeTemplateByName(nodeTemplateName)
 
 325         return nodeTemplate.artifacts?.get(artifactName)
 
 326                 ?: throw BluePrintProcessorException("failed to get artifat definition($artifactName) from the node " +
 
 331     override fun setInputValue(propertyName: String, propertyDefinition: PropertyDefinition, value: JsonNode) {
 
 332         val path = StringBuilder(BluePrintConstants.PATH_INPUTS)
 
 333                 .append(BluePrintConstants.PATH_DIVIDER).append(propertyName).toString()
 
 334         log.trace("setting input path ({}), values ({})", path, value)
 
 338     override fun setWorkflowInputValue(workflowName: String, propertyName: String,
 
 339                                        propertyDefinition: PropertyDefinition, value: JsonNode) {
 
 340         val path: String = StringBuilder(BluePrintConstants.PATH_NODE_WORKFLOWS)
 
 341                 .append(BluePrintConstants.PATH_DIVIDER).append(workflowName)
 
 342                 .append(BluePrintConstants.PATH_DIVIDER).append(BluePrintConstants.PATH_INPUTS)
 
 343                 .append(BluePrintConstants.PATH_DIVIDER).append(BluePrintConstants.PATH_PROPERTIES)
 
 344                 .append(BluePrintConstants.PATH_DIVIDER).append(propertyName).toString()
 
 348     override fun setNodeTemplatePropertyValue(nodeTemplateName: String, propertyName: String, value: JsonNode) {
 
 350         val path: String = StringBuilder(BluePrintConstants.PATH_NODE_TEMPLATES)
 
 351                 .append(BluePrintConstants.PATH_DIVIDER).append(nodeTemplateName)
 
 352                 .append(BluePrintConstants.PATH_DIVIDER).append(BluePrintConstants.PATH_PROPERTIES)
 
 353                 .append(BluePrintConstants.PATH_DIVIDER).append(propertyName).toString()
 
 357     override fun setNodeTemplateAttributeValue(nodeTemplateName: String, attributeName: String, value: JsonNode) {
 
 359         val path: String = StringBuilder(BluePrintConstants.PATH_NODE_TEMPLATES)
 
 360                 .append(BluePrintConstants.PATH_DIVIDER).append(nodeTemplateName)
 
 361                 .append(BluePrintConstants.PATH_DIVIDER).append(BluePrintConstants.PATH_ATTRIBUTES)
 
 362                 .append(BluePrintConstants.PATH_DIVIDER).append(attributeName).toString()
 
 366     override fun setNodeTemplateOperationPropertyValue(nodeTemplateName: String, interfaceName: String, operationName: String, propertyName: String,
 
 368         val path: String = StringBuilder(BluePrintConstants.PATH_NODE_TEMPLATES)
 
 369                 .append(BluePrintConstants.PATH_DIVIDER).append(nodeTemplateName)
 
 370                 .append(BluePrintConstants.PATH_DIVIDER).append(BluePrintConstants.PATH_INTERFACES)
 
 371                 .append(BluePrintConstants.PATH_DIVIDER).append(interfaceName)
 
 372                 .append(BluePrintConstants.PATH_DIVIDER).append(BluePrintConstants.PATH_OPERATIONS)
 
 373                 .append(BluePrintConstants.PATH_DIVIDER).append(operationName)
 
 374                 .append(BluePrintConstants.PATH_DIVIDER).append(BluePrintConstants.PATH_PROPERTIES)
 
 375                 .append(BluePrintConstants.PATH_DIVIDER).append(propertyName).toString()
 
 376         log.trace("setting operation property path ({}), values ({})", path, value)
 
 380     override fun setNodeTemplateOperationInputValue(nodeTemplateName: String, interfaceName: String,
 
 381                                                     operationName: String, propertyName: String,
 
 383         val path: String = StringBuilder(BluePrintConstants.PATH_NODE_TEMPLATES)
 
 384                 .append(BluePrintConstants.PATH_DIVIDER).append(nodeTemplateName)
 
 385                 .append(BluePrintConstants.PATH_DIVIDER).append(BluePrintConstants.PATH_INTERFACES)
 
 386                 .append(BluePrintConstants.PATH_DIVIDER).append(interfaceName)
 
 387                 .append(BluePrintConstants.PATH_DIVIDER).append(BluePrintConstants.PATH_OPERATIONS)
 
 388                 .append(BluePrintConstants.PATH_DIVIDER).append(operationName)
 
 389                 .append(BluePrintConstants.PATH_DIVIDER).append(BluePrintConstants.PATH_INPUTS)
 
 390                 .append(BluePrintConstants.PATH_DIVIDER).append(BluePrintConstants.PATH_PROPERTIES)
 
 391                 .append(BluePrintConstants.PATH_DIVIDER).append(propertyName).toString()
 
 395     override fun setNodeTemplateOperationOutputValue(nodeTemplateName: String, interfaceName: String,
 
 396                                                      operationName: String, propertyName: String,
 
 398         val path: String = StringBuilder(BluePrintConstants.PATH_NODE_TEMPLATES)
 
 399                 .append(BluePrintConstants.PATH_DIVIDER).append(nodeTemplateName)
 
 400                 .append(BluePrintConstants.PATH_DIVIDER).append(BluePrintConstants.PATH_INTERFACES)
 
 401                 .append(BluePrintConstants.PATH_DIVIDER).append(interfaceName)
 
 402                 .append(BluePrintConstants.PATH_DIVIDER).append(BluePrintConstants.PATH_OPERATIONS)
 
 403                 .append(BluePrintConstants.PATH_DIVIDER).append(operationName)
 
 404                 .append(BluePrintConstants.PATH_DIVIDER).append(BluePrintConstants.PATH_OUTPUTS)
 
 405                 .append(BluePrintConstants.PATH_DIVIDER).append(BluePrintConstants.PATH_PROPERTIES)
 
 406                 .append(BluePrintConstants.PATH_DIVIDER).append(propertyName).toString()
 
 411     override fun getInputValue(propertyName: String): JsonNode {
 
 412         val path = StringBuilder(BluePrintConstants.PATH_INPUTS)
 
 413                 .append(BluePrintConstants.PATH_DIVIDER).append(propertyName).toString()
 
 414         return getJsonNode(path)
 
 417     override fun getNodeTemplateOperationOutputValue(nodeTemplateName: String, interfaceName: String,
 
 418                                                      operationName: String, propertyName: String): JsonNode {
 
 419         val path: String = StringBuilder(BluePrintConstants.PATH_NODE_TEMPLATES)
 
 420                 .append(BluePrintConstants.PATH_DIVIDER).append(nodeTemplateName)
 
 421                 .append(BluePrintConstants.PATH_DIVIDER).append(BluePrintConstants.PATH_INTERFACES)
 
 422                 .append(BluePrintConstants.PATH_DIVIDER).append(interfaceName)
 
 423                 .append(BluePrintConstants.PATH_DIVIDER).append(BluePrintConstants.PATH_OPERATIONS)
 
 424                 .append(BluePrintConstants.PATH_DIVIDER).append(operationName)
 
 425                 .append(BluePrintConstants.PATH_DIVIDER).append(BluePrintConstants.PATH_OUTPUTS)
 
 426                 .append(BluePrintConstants.PATH_DIVIDER).append(BluePrintConstants.PATH_PROPERTIES)
 
 427                 .append(BluePrintConstants.PATH_DIVIDER).append(propertyName).toString()
 
 428         return getJsonNode(path)
 
 431     override fun getNodeTemplatePropertyValue(nodeTemplateName: String, propertyName: String): JsonNode {
 
 432         val path: String = StringBuilder(BluePrintConstants.PATH_NODE_TEMPLATES)
 
 433                 .append(BluePrintConstants.PATH_DIVIDER).append(nodeTemplateName)
 
 434                 .append(BluePrintConstants.PATH_DIVIDER).append(BluePrintConstants.PATH_PROPERTIES)
 
 435                 .append(BluePrintConstants.PATH_DIVIDER).append(propertyName).toString()
 
 436         return getJsonNode(path)
 
 439     override fun getNodeTemplateAttributeValue(nodeTemplateName: String, attributeName: String): JsonNode {
 
 440         val path: String = StringBuilder(BluePrintConstants.PATH_NODE_TEMPLATES)
 
 441                 .append(BluePrintConstants.PATH_DIVIDER).append(nodeTemplateName)
 
 442                 .append(BluePrintConstants.PATH_DIVIDER).append(BluePrintConstants.PATH_ATTRIBUTES)
 
 443                 .append(BluePrintConstants.PATH_DIVIDER).append(attributeName).toString()
 
 444         return getJsonNode(path)
 
 447     override fun assignInputs(jsonNode: JsonNode) {
 
 448         log.info("assignInputs from input JSON ({})", jsonNode.toString())
 
 449         bluePrintContext.inputs?.forEach { propertyName, property ->
 
 450             val valueNode: JsonNode = jsonNode.at(BluePrintConstants.PATH_DIVIDER + propertyName)
 
 451                     ?: NullNode.getInstance()
 
 452             setInputValue(propertyName, property, valueNode)
 
 456     override fun assignWorkflowInputs(workflowName: String, jsonNode: JsonNode) {
 
 457         log.info("assign workflow {} input value ({})", workflowName, jsonNode.toString())
 
 459         val dynamicInputPropertiesName = "$workflowName-properties"
 
 461         bluePrintContext.workflowByName(workflowName).inputs?.forEach { propertyName, property ->
 
 462             if (propertyName != dynamicInputPropertiesName) {
 
 463                 val valueNode: JsonNode = jsonNode.at(BluePrintConstants.PATH_DIVIDER + propertyName)
 
 464                         ?: NullNode.getInstance()
 
 465                 setInputValue(propertyName, property, valueNode)
 
 468         // Load Dynamic data Types
 
 469         val workflowDynamicInputs: JsonNode? = jsonNode.get(dynamicInputPropertiesName)
 
 471         workflowDynamicInputs?.let {
 
 472             bluePrintContext.dataTypeByName("dt-$dynamicInputPropertiesName")?.properties?.forEach { propertyName, property ->
 
 473                 val valueNode: JsonNode = workflowDynamicInputs.at(BluePrintConstants.PATH_DIVIDER + propertyName)
 
 474                         ?: NullNode.getInstance()
 
 475                 setInputValue(propertyName, property, valueNode)
 
 481     override fun getJsonForNodeTemplateAttributeProperties(nodeTemplateName: String, keys: List<String>): JsonNode {
 
 483         val jsonNode: ObjectNode = jacksonObjectMapper().createObjectNode()
 
 484         val path: String = StringBuilder(BluePrintConstants.PATH_NODE_TEMPLATES)
 
 485                 .append(BluePrintConstants.PATH_DIVIDER).append(nodeTemplateName)
 
 486                 .append(BluePrintConstants.PATH_DIVIDER).append(BluePrintConstants.PATH_ATTRIBUTES)
 
 487                 .append(BluePrintConstants.PATH_DIVIDER).toString()
 
 491             val key = it.replace(path, "")
 
 492             if (keys.contains(key)) {
 
 493                 val value = store[it] as JsonNode
 
 494                 jsonNode.set(key, value)