//
// ============LICENSE_START=======================================================
//  Copyright (C) 2016-2018 Ericsson. All rights reserved.
// ================================================================================
// This file is licensed under the CREATIVE COMMONS ATTRIBUTION 4.0 INTERNATIONAL LICENSE
// Full license text at https://creativecommons.org/licenses/by/4.0/legalcode
// 
// SPDX-License-Identifier: CC-BY-4.0
// ============LICENSE_END=========================================================
//
// @author Sven van der Meer (sven.van.der.meer@ericsson.com)
//

== Logic Cheatsheet

Examples given here use Javascript (if not stated otherwise), other execution environments will be similar.


=== Add Nashorn

First line in the logic use this import.

.JS Nashorn
[source,javascript,options="nowrap"]
----
load("nashorn:mozilla_compat.js");
----


=== Finish Logic with Success or Error

To finish logic, i.e. return to APEX, with success use the following lines close to the end of the logic.

.JS Success
[source,javascript,options="nowrap"]
----
var returnValueType = Java.type("java.lang.Boolean");
var returnValue = new returnValueType(true);
----

To notify a problem, finish with an error.

.JS Fail
[source,javascript,options="nowrap"]
----
var returnValueType = Java.type("java.lang.Boolean");
var returnValue = new returnValueType(false);
----


=== Logic Logging

Logging can be made easy using a local variable for the logger.
Line 1 below does that.
Then we start with a trace log with the task (or task logic) identifier followed by the infields.

.JS Logging
[source,javascript,options="nowrap"]
----
var logger = executor.logger;
logger.trace("start: " + executor.subject.id);
logger.trace("-- infields: " + executor.inFields);
----

For larger logging blocks you can use the standard logging API to detect log levels, for instance:

.JS Logging Blocks
[source,javascript,options="nowrap"]
----
if(logger.isTraceEnabled()){
    // trace logging block here
}
----

Note: the shown logger here logs to `org.onap.policy.apex.executionlogging`.
The behavior of the actual logging can be specified in the `$APEX_HOME/etc/logback.xml`.

If you want to log into the APEX root logger (which is sometimes necessary to report serious logic errors to the top),
then import the required class and use this logger.

.JS Root Logger
[source,javascript,options="nowrap"]
----
importClass(org.slf4j.LoggerFactory);
var rootLogger = LoggerFactory.getLogger(logger.ROOT_LOGGER_NAME);

rootLogger.error("Serious error in logic detected: " + executor.subject.id);
----

=== Local Variable for Infields

It is a good idea to use local variables for `infields`.
This avoids long code lines and policy evolution.
The following example assumes infields named `nodeName` and `nodeAlias`.

.JS Infields Local Var
[source,javascript,options="nowrap"]
----
var ifNodeName = executor.inFields["nodeName"];
var ifNodeAlias = executor.inFields["nodeAlias"];
----


=== Local Variable for Context Albums

Similar to the `infields` it is good practice to use local variables for context albums as well.
The following example assumes that a task can access a context album `albumTopoNodes`.
The second line gets a particular node from this context album.

.JS Infields Local Var
[source,javascript,options="nowrap"]
----
var albumTopoNodes = executor.getContextAlbum("albumTopoNodes");
var ctxtNode = albumTopoNodes.get(ifNodeName);
----


=== Set Outfields in Logic

The task logic needs to set outfields with content generated.
The exception are outfields that are a direct copy from an infield of the same name, APEX does that autmatically.

.JS Set Outfields
[source,javascript,options="nowrap"]
----
executor.outFields["report"] = "node ctxt :: added node " + ifNodeName;
----


=== Create a instance of an Outfield using Schemas

If an outfield is not an atomic type (string, integer, etc.) but uses a complex schema (with a Java or Avro backend), APEX can help to create new instances.
The `executor` provides a field called `subject`, which provides a schem helper with an API for this.
The complete API of the schema helper is documented here: link:https://ericsson.github.io/apex-docs/javadocs/index.html[API Doc: SchemaHelper].

If the backend is Avro, then an import of the Avro schema library is required:

.JS Import Avro
[source,javascript,options="nowrap"]
----
importClass(org.apache.avro.generic.GenericData.Array);
importClass(org.apache.avro.generic.GenericRecord);
importClass(org.apache.avro.Schema);
----

If the backend is Java, then the Java class implementing the schema needs to be imported.

The following example assumes an outfield `situation`.
The `subject` method `getOutFieldSchemaHelper()` is used to create a new instance.

.JS Outfield Instance with Schema
[source,javascript,options="nowrap"]
----
var situation = executor.subject.getOutFieldSchemaHelper("situation").createNewInstance();
----

If the schema backend is Java, the new instance will be as implemented in the Java class.
If the schema backend is Avro, the new instance will have all fields from the Avro schema specification, but set to `null`.
So any entry here needs to be done separately.
For instance, the `situation` schema has a field `problemID` which we set.

.JS Outfield Instance with Schema, set
[source,javascript,options="nowrap"]
----
situation.put("problemID", "my-problem");
----


=== Create a instance of an Context Album entry using Schemas

Context album instances can be created using very similar to the outfields.
Here, the schema helper comes from the context album directly.
The API of the schema helper is the same as for outfields, see link:https://ericsson.github.io/apex-docs/javadocs/index.html[API Doc: SchemaHelper].

If the backend is Avro, then an import of the Avro schema library is required:

.JS Import Avro
[source,javascript,options="nowrap"]
----
importClass(org.apache.avro.generic.GenericData.Array);
importClass(org.apache.avro.generic.GenericRecord);
importClass(org.apache.avro.Schema);
----

If the backend is Java, then the Java class implementing the schema needs to be imported.

The following example creates a new instance of a context album instance named `albumProblemMap`.

.JS Outfield Instance with Schema
[source,javascript,options="nowrap"]
----
var albumProblemMap = executor.getContextAlbum("albumProblemMap");
var linkProblem = albumProblemMap.getSchemaHelper().createNewInstance();
----

This can of course be also done in a single call without the local variable for the context album.

.JS Outfield Instance with Schema, one line
[source,javascript,options="nowrap"]
----
var linkProblem = executor.getContextAlbum("albumProblemMap").getSchemaHelper().createNewInstance();
----

If the schema backend is Java, the new instance will be as implemented in the Java class.
If the schema backend is Avro, the new instance will have all fields from the Avro schema specification, but set to `null`.
So any entry here needs to be done separately (see above in outfields for an example).


=== Enumerates

When dealing with enumerates (Avro or Java defined), it is sometimes and in some execution environments necessary to convert them to a string.
For example, assume an Avro enumerate schema as:

.Avro Enumerate Schema
[source,json,options="nowrap"]
----
{
  "type": "enum",
  "name": "Status",
  "symbols" : [
    "UP",
    "DOWN"
  ]
}

----

Using a switch over a field initialized with this enumerate in Javascript will fail.
Instead, use the `toString` method, for example:

.JS Outfield Instance with Schema, one line
[source,javascript,options="nowrap"]
----
var switchTest = executor.inFields["status"];
switch(switchTest.toString()){
  case "UP": ...; break;
  case "DOWN": ...; break;
  default: ...;
}
----


=== MVEL Initialize Outfields First!

In MVEL, we observed a problem when accessing (setting) outfields without a prior access to them.
So in any MVEL task logic, before setting any outfield, simply do a get (with any string), to load the outfields into the MVEL cache.

.MVEL Outfield Initialization
[source,java,options="nowrap"]
----
outFields.get("initialize outfields");
----


=== Using Java in Scripting Logic

Since APEX executes the logic inside a JVM, most scripting languages provide access to all standard Java classes.
Simply add an import for the required class and then use it as in actual Java.

The following example imports `java.util.arraylist` into a Javascript logic, and then creates a new list.

.JS Import ArrayList
[source,javascript,options="nowrap"]
----
importClass(java.util.ArrayList);
var myList = new ArrayList();
----


