You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nlpcraft.apache.org by se...@apache.org on 2021/04/16 11:04:28 UTC

[incubator-nlpcraft] branch NLPCRAFT-292 created (now c4f2dd6)

This is an automated email from the ASF dual-hosted git repository.

sergeykamov pushed a change to branch NLPCRAFT-292
in repository https://gitbox.apache.org/repos/asf/incubator-nlpcraft.git.


      at c4f2dd6  WIP.

This branch includes the following new commits:

     new c4f2dd6  WIP.

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


[incubator-nlpcraft] 01/01: WIP.

Posted by se...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

sergeykamov pushed a commit to branch NLPCRAFT-292
in repository https://gitbox.apache.org/repos/asf/incubator-nlpcraft.git

commit c4f2dd69f3880b9a669bfab6b0cd258413085874
Author: Sergey Kamov <sk...@gmail.com>
AuthorDate: Fri Apr 16 14:04:18 2021 +0300

    WIP.
---
 .../apache/nlpcraft/common/debug/NCLogHolder.scala |  17 ++-
 .../nlpcraft/probe/mgrs/cmd/NCCommandManager.scala |   2 +-
 .../probe/mgrs/nlp/NCProbeEnrichmentManager.scala  |  18 +--
 .../nlpcraft/server/mdo/NCQueryStateMdo.scala      |   2 +-
 .../nlpcraft/server/probe/NCProbeManager.scala     |  12 +-
 .../nlpcraft/server/query/NCQueryManager.scala     |  42 +++---
 .../nlpcraft/server/rest/NCBasicRestApi.scala      |   6 +-
 .../apache/nlpcraft/common/debug/NCLogSpec.scala   | 159 +++++++++++++++++++++
 .../apache/nlpcraft/model/meta/NCMetaSpec.scala    |  81 +----------
 .../{NCMetaSpec.scala => NCMetaSpecAdapter.scala}  |  75 ++--------
 .../apache/nlpcraft/server/rest/NCRestSpec.scala   |   2 +-
 11 files changed, 226 insertions(+), 190 deletions(-)

diff --git a/nlpcraft/src/main/scala/org/apache/nlpcraft/common/debug/NCLogHolder.scala b/nlpcraft/src/main/scala/org/apache/nlpcraft/common/debug/NCLogHolder.scala
index 1131c3a..f630229 100644
--- a/nlpcraft/src/main/scala/org/apache/nlpcraft/common/debug/NCLogHolder.scala
+++ b/nlpcraft/src/main/scala/org/apache/nlpcraft/common/debug/NCLogHolder.scala
@@ -36,8 +36,9 @@ import org.apache.nlpcraft.common.debug.NCLogHolder._
   * Log data holder.
   */
 class NCLogHolder extends Serializable {
-    private val intents = new util.ArrayList[IntentJson]()
-    private var queryContext: ContextJson = _
+    // Level set for test reasons.
+    private[debug] val intents = new util.ArrayList[IntentJson]()
+    private[debug] var queryContext: ContextJson = _
 
     case class TokenJson(
         metadata: util.Map[String, Object],
@@ -48,7 +49,7 @@ class NCLogHolder extends Serializable {
         groups: util.List[String]
     )
     
-    case class UserJson( // TODO: add metadata
+    case class UserJson(
         id: Long,
         firstName: String,
         lastName: String,
@@ -56,10 +57,10 @@ class NCLogHolder extends Serializable {
         avatarUrl: String,
         isAdmin: Boolean,
         signupTimestamp: Long,
-        meta: JavaMeta // TODO: remove.
+        meta: JavaMeta
     )
     
-    case class CompanyJson( // TODO: add metadata
+    case class CompanyJson(
         id: Long,
         name: String,
         website: String,
@@ -67,7 +68,8 @@ class NCLogHolder extends Serializable {
         city: String,
         region: String,
         postalCode: String,
-        address: String
+        address: String,
+        meta: JavaMeta
     )
 
     case class RequestJson(
@@ -171,7 +173,8 @@ class NCLogHolder extends Serializable {
             region = comp.getRegion.orElse(null),
             address = comp.getAddress.orElse(null),
             city = comp.getCity.orElse(null),
-            postalCode = comp.getPostalCode.orElse(null)
+            postalCode = comp.getPostalCode.orElse(null),
+            meta = comp.getMetadata
         )
 
         val reqJs = RequestJson(
diff --git a/nlpcraft/src/main/scala/org/apache/nlpcraft/probe/mgrs/cmd/NCCommandManager.scala b/nlpcraft/src/main/scala/org/apache/nlpcraft/probe/mgrs/cmd/NCCommandManager.scala
index 4206a11..9831e94 100644
--- a/nlpcraft/src/main/scala/org/apache/nlpcraft/probe/mgrs/cmd/NCCommandManager.scala
+++ b/nlpcraft/src/main/scala/org/apache/nlpcraft/probe/mgrs/cmd/NCCommandManager.scala
@@ -101,7 +101,7 @@ object NCCommandManager extends NCService {
                             usrId = msg.data[Long]("userId"),
                             senMeta = msg.data[java.util.Map[String, JSerializable]]("senMeta").asScala,
                             mdlId = msg.data[String]("mdlId"),
-                            logEnable = msg.data[Boolean]("logEnable"),
+                            enableLog = msg.data[Boolean]("enableLog"),
                             span
                     )
 
diff --git a/nlpcraft/src/main/scala/org/apache/nlpcraft/probe/mgrs/nlp/NCProbeEnrichmentManager.scala b/nlpcraft/src/main/scala/org/apache/nlpcraft/probe/mgrs/nlp/NCProbeEnrichmentManager.scala
index aab00ed..8b9258b 100644
--- a/nlpcraft/src/main/scala/org/apache/nlpcraft/probe/mgrs/nlp/NCProbeEnrichmentManager.scala
+++ b/nlpcraft/src/main/scala/org/apache/nlpcraft/probe/mgrs/nlp/NCProbeEnrichmentManager.scala
@@ -144,7 +144,7 @@ object NCProbeEnrichmentManager extends NCService with NCOpenCensusModelStats {
       * @param usrId User ID.
       * @param senMeta Sentence meta data.
       * @param mdlId Model ID.
-      * @param logEnable Log enabled flag.
+      * @param enableLog Log enabled flag.
       * @param parent Optional parent span.
       */
     @throws[NCE]
@@ -155,7 +155,7 @@ object NCProbeEnrichmentManager extends NCService with NCOpenCensusModelStats {
         usrId: Long,
         senMeta: Map[String, Serializable],
         mdlId: String,
-        logEnable: Boolean,
+        enableLog: Boolean,
         parent: Span = null
     ): Unit = {
         val span = startSpan("ask", parent,
@@ -166,7 +166,7 @@ object NCProbeEnrichmentManager extends NCService with NCOpenCensusModelStats {
         )
 
         startMs.set(System.currentTimeMillis())
-    
+
         try
             ask0(
                 srvReqId,
@@ -175,7 +175,7 @@ object NCProbeEnrichmentManager extends NCService with NCOpenCensusModelStats {
                 usrId,
                 senMeta,
                 mdlId,
-                logEnable,
+                enableLog,
                 span
             )
         catch {
@@ -213,7 +213,7 @@ object NCProbeEnrichmentManager extends NCService with NCOpenCensusModelStats {
       * @param usrId User ID.
       * @param senMeta Sentence meta data.
       * @param mdlId Model ID.
-      * @param logEnable Log enable flag.
+      * @param enableLog Log enable flag.
       */
     @throws[NCE]
     private def ask0(
@@ -223,7 +223,7 @@ object NCProbeEnrichmentManager extends NCService with NCOpenCensusModelStats {
         usrId: Long,
         senMeta: Map[String, Serializable],
         mdlId: String,
-        logEnable: Boolean,
+        enableLog: Boolean,
         span: Span
     ): Unit = {
         require(nlpSens.nonEmpty)
@@ -248,7 +248,7 @@ object NCProbeEnrichmentManager extends NCService with NCOpenCensusModelStats {
         tbl += (s"${b("Server Request ID")}", m(srvReqId))
 
         logger.info(s"New request received from server:\n$tbl")
-        
+
         /**
           *
           * @param code Pre or post checker error code.
@@ -337,7 +337,7 @@ object NCProbeEnrichmentManager extends NCService with NCOpenCensusModelStats {
             NCConnectionManager.send(msg, span)
 
             val durMs = System.currentTimeMillis() - startMs.get
-            
+
             if (errMsg.isEmpty)
                 logger.info(s"" +
                     s"\n" +
@@ -580,7 +580,7 @@ object NCProbeEnrichmentManager extends NCService with NCOpenCensusModelStats {
         conv.updateTokens(span)
 
         var logKey: String = null
-        val logHldr = if (logEnable) new NCLogHolder else null
+        val logHldr = if (enableLog) new NCLogHolder else null
         
         // Create model query context.
         val ctx: NCContext = new NCContext {
diff --git a/nlpcraft/src/main/scala/org/apache/nlpcraft/server/mdo/NCQueryStateMdo.scala b/nlpcraft/src/main/scala/org/apache/nlpcraft/server/mdo/NCQueryStateMdo.scala
index 7419aed..27f440a 100644
--- a/nlpcraft/src/main/scala/org/apache/nlpcraft/server/mdo/NCQueryStateMdo.scala
+++ b/nlpcraft/src/main/scala/org/apache/nlpcraft/server/mdo/NCQueryStateMdo.scala
@@ -39,7 +39,7 @@ case class NCQueryStateMdo(
     @NCMdoField createTstamp: Timestamp, // Creation timestamp.
     @NCMdoField var updateTstamp: Timestamp, // Last update timestamp.
     @NCMdoField var status: String,
-    @NCMdoField enabledLog: Boolean,
+    @NCMdoField enableLog: Boolean,
     @NCMdoField var logJson: Option[String] = None,
     @NCMdoField var intentId: Option[String] = None,
     // Query OK.
diff --git a/nlpcraft/src/main/scala/org/apache/nlpcraft/server/probe/NCProbeManager.scala b/nlpcraft/src/main/scala/org/apache/nlpcraft/server/probe/NCProbeManager.scala
index 36c1385..34b654b 100644
--- a/nlpcraft/src/main/scala/org/apache/nlpcraft/server/probe/NCProbeManager.scala
+++ b/nlpcraft/src/main/scala/org/apache/nlpcraft/server/probe/NCProbeManager.scala
@@ -835,7 +835,7 @@ object NCProbeManager extends NCService {
       * @param data
       * @param usrMeta
       * @param companyMeta
-      * @param logEnable
+      * @param enableLog
       * @param parent
       */
     @throws[NCE]
@@ -851,7 +851,7 @@ object NCProbeManager extends NCService {
         data: Option[String],
         usrMeta: Option[Map[String, String]],
         companyMeta: Option[Map[String, String]],
-        logEnable: Boolean,
+        enableLog: Boolean,
         parent: Span = null): Unit = {
         startScopedSpan("askProbe", parent, "srvReqId" → srvReqId, "usrId" → usr.id, "mdlId" → mdlId, "txt" → txt) { span ⇒
             def convertMeta(metaOpt: Option[Map[String, String]]): util.HashMap[String, String] =
@@ -860,13 +860,13 @@ object NCProbeManager extends NCService {
                         val map = new util.HashMap[String, String]()
 
                         meta.foreach { case (k, v) ⇒ map.put(k, v) }
-    
+
                         map
                     case None ⇒ null
                 }
 
             val senMeta = new util.HashMap[String, java.io.Serializable]()
-    
+
             Map(
                 "NORMTEXT" → nlpSen.text,
                 "USER_AGENT" → usrAgent.orNull,
@@ -893,7 +893,7 @@ object NCProbeManager extends NCService {
             ).
                 filter(_._2 != null).
                 foreach(p ⇒ senMeta.put(p._1, p._2.asInstanceOf[java.io.Serializable]))
-            
+
             getProbeForModelId(mdlId) match {
                 case Some(holder) ⇒
                     sendToProbe(
@@ -907,7 +907,7 @@ object NCProbeManager extends NCService {
                             "senMeta" → senMeta,
                             "userId" → usr.id,
                             "mdlId" → mdlId,
-                            "logEnable" → logEnable
+                            "enableLog" → enableLog
                         ),
                         span
                     )
diff --git a/nlpcraft/src/main/scala/org/apache/nlpcraft/server/query/NCQueryManager.scala b/nlpcraft/src/main/scala/org/apache/nlpcraft/server/query/NCQueryManager.scala
index 572ecfb..ad6fdb1 100644
--- a/nlpcraft/src/main/scala/org/apache/nlpcraft/server/query/NCQueryManager.scala
+++ b/nlpcraft/src/main/scala/org/apache/nlpcraft/server/query/NCQueryManager.scala
@@ -112,7 +112,7 @@ object NCQueryManager extends NCService with NCIgniteInstance with NCOpenCensusS
       * @param usrAgent
       * @param rmtAddr
       * @param data
-      * @param enabledLog
+      * @param enableLog
       * @return
       */
     @throws[NCE]
@@ -123,25 +123,25 @@ object NCQueryManager extends NCService with NCIgniteInstance with NCOpenCensusS
         usrAgent: Option[String],
         rmtAddr: Option[String],
         data: Option[String],
-        enabledLog: Boolean,
+        enableLog: Boolean,
         parent: Span = null
     ): Future[NCQueryStateMdo] = {
         val srvReqId = U.genGuid()
-    
+
         startScopedSpan("syncAsk", parent,
             "srvReqId" → srvReqId,
             "usrId" → usrId,
             "txt" → txt,
             "mdlId" → mdlId,
-            "enableLog" → enabledLog,
+            "enableLog" → enableLog,
             "usrAgent" → usrAgent.orNull,
             "rmtAddr" → rmtAddr.orNull
         ) { span ⇒
             val promise = Promise[NCQueryStateMdo]()
-    
+
             asyncAsks.put(srvReqId, promise)
-            
-            spawnAskFuture(srvReqId, usrId, txt, mdlId, usrAgent, rmtAddr, data, enabledLog, span)
+
+            spawnAskFuture(srvReqId, usrId, txt, mdlId, usrAgent, rmtAddr, data, enableLog, span)
             
             promise.future
         }
@@ -156,7 +156,7 @@ object NCQueryManager extends NCService with NCIgniteInstance with NCOpenCensusS
       * @param usrAgent
       * @param rmtAddr
       * @param data
-      * @param enabledLog
+      * @param enableLog
       * @return Server request ID for newly submitted request.
       */
     @throws[NCE]
@@ -167,7 +167,7 @@ object NCQueryManager extends NCService with NCIgniteInstance with NCOpenCensusS
         usrAgent: Option[String],
         rmtAddr: Option[String],
         data: Option[String],
-        enabledLog: Boolean,
+        enableLog: Boolean,
         parent: Span = null
     ): String = {
         val srvReqId = U.genGuid()
@@ -177,10 +177,10 @@ object NCQueryManager extends NCService with NCIgniteInstance with NCOpenCensusS
             "usrId" → usrId,
             "txt" → txt,
             "mdlId" → mdlId,
-            "enableLog" → enabledLog,
+            "enableLog" → enableLog,
             "usrAgent" → usrAgent.orNull,
             "rmtAddr" → rmtAddr.orNull) { span ⇒
-            spawnAskFuture(srvReqId, usrId, txt, mdlId, usrAgent, rmtAddr, data, enabledLog, span)
+            spawnAskFuture(srvReqId, usrId, txt, mdlId, usrAgent, rmtAddr, data, enableLog, span)
             
             srvReqId
         }
@@ -199,7 +199,7 @@ object NCQueryManager extends NCService with NCIgniteInstance with NCOpenCensusS
       * @param usrAgent
       * @param rmtAddr
       * @param data
-      * @param enabledLog
+      * @param enableLog
       * @return
       */
     @throws[NCE]
@@ -211,13 +211,13 @@ object NCQueryManager extends NCService with NCIgniteInstance with NCOpenCensusS
         usrAgent: Option[String],
         rmtAddr: Option[String],
         data: Option[String],
-        enabledLog: Boolean,
+        enableLog: Boolean,
         parent: Span = null
     ): Unit = {
         val txt0 = txt.trim()
-        
+
         val rcvTstamp = U.nowUtcTs()
-        
+
         val usr = NCUserManager.getUserById(usrId, parent).getOrElse(throw new NCE(s"Unknown user ID: $usrId"))
         val company = NCCompanyManager.getCompany(usr.companyId, parent).getOrElse(throw new NCE(s"Unknown company ID: ${usr.companyId}"))
 
@@ -236,7 +236,7 @@ object NCQueryManager extends NCService with NCIgniteInstance with NCOpenCensusS
         // Check input length.
         if (txt0.split(" ").length > MAX_WORDS)
             throw new NCE(s"User input is too long (max is $MAX_WORDS words).")
-        
+
         catching(wrapIE) {
             // Enlist for tracking.
             cache += srvReqId → NCQueryStateMdo(
@@ -246,7 +246,7 @@ object NCQueryManager extends NCService with NCIgniteInstance with NCOpenCensusS
                 companyId = company.id,
                 email = usr.email,
                 status = QRY_ENLISTED, // Initial status.
-                enabledLog = enabledLog,
+                enableLog = enableLog,
                 text = txt0,
                 userAgent = usrAgent,
                 remoteAddress = rmtAddr,
@@ -254,7 +254,7 @@ object NCQueryManager extends NCService with NCIgniteInstance with NCOpenCensusS
                 updateTstamp = rcvTstamp
             )
         }
-        
+
         // Add processing log.
         NCProcessLogManager.newEntry(
             usrId,
@@ -268,7 +268,7 @@ object NCQueryManager extends NCService with NCIgniteInstance with NCOpenCensusS
             data.orNull,
             parent
         )
-        
+
         Future {
             startScopedSpan("future", parent, "srvReqId" → srvReqId) { span ⇒
                 val tbl = NCAsciiTable()
@@ -284,7 +284,7 @@ object NCQueryManager extends NCService with NCIgniteInstance with NCOpenCensusS
                 logger.info(s"New request received:\n$tbl")
 
                 val enabledBuiltInToks = NCProbeManager.getModel(mdlId, span).enabledBuiltInTokens
-                
+
                 // Enrich the user input and send it to the probe.
                 NCProbeManager.askProbe(
                     srvReqId,
@@ -298,7 +298,7 @@ object NCQueryManager extends NCService with NCIgniteInstance with NCOpenCensusS
                     data,
                     usrMeta,
                     compMeta,
-                    enabledLog,
+                    enableLog,
                     span
                 )
             }
diff --git a/nlpcraft/src/main/scala/org/apache/nlpcraft/server/rest/NCBasicRestApi.scala b/nlpcraft/src/main/scala/org/apache/nlpcraft/server/rest/NCBasicRestApi.scala
index bfd8d2a..a681a8f 100644
--- a/nlpcraft/src/main/scala/org/apache/nlpcraft/server/rest/NCBasicRestApi.scala
+++ b/nlpcraft/src/main/scala/org/apache/nlpcraft/server/rest/NCBasicRestApi.scala
@@ -91,7 +91,7 @@ class NCBasicRestApi extends NCRestApi with LazyLogging with NCOpenCensusTrace w
         usrAgent: Option[String],
         rmtAddr: Option[String],
         data: Option[String],
-        enabledLog: Boolean,
+        enableLog: Boolean,
         parent: Span
     )
 
@@ -603,7 +603,7 @@ class NCBasicRestApi extends NCRestApi with LazyLogging with NCOpenCensusTrace w
                     h.usrAgent,
                     h.rmtAddr,
                     h.data,
-                    h.enabledLog,
+                    h.enableLog,
                     h.parent
                 )
 
@@ -634,7 +634,7 @@ class NCBasicRestApi extends NCRestApi with LazyLogging with NCOpenCensusTrace w
                     h.usrAgent,
                     h.rmtAddr,
                     h.data,
-                    h.enabledLog,
+                    h.enableLog,
                     h.parent
                 )
 
diff --git a/nlpcraft/src/test/scala/org/apache/nlpcraft/common/debug/NCLogSpec.scala b/nlpcraft/src/test/scala/org/apache/nlpcraft/common/debug/NCLogSpec.scala
new file mode 100644
index 0000000..8bfd78c
--- /dev/null
+++ b/nlpcraft/src/test/scala/org/apache/nlpcraft/common/debug/NCLogSpec.scala
@@ -0,0 +1,159 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.nlpcraft.common.debug
+
+import com.google.gson.reflect.TypeToken
+import com.google.gson.{GsonBuilder, JsonElement}
+import org.apache.http.HttpResponse
+import org.apache.http.client.methods.HttpPost
+import org.apache.http.entity.StringEntity
+import org.apache.http.impl.client.HttpClients
+import org.apache.http.util.EntityUtils
+import org.apache.nlpcraft.NCTestEnvironment
+import org.apache.nlpcraft.common.JavaMeta
+import org.apache.nlpcraft.model.meta.NCMetaSpecAdapter
+import org.apache.nlpcraft.model.tools.test.NCTestClientBuilder._
+import org.apache.nlpcraft.model.{NCIntent, NCIntentMatch, NCModelAdapter, NCResult}
+import org.junit.jupiter.api.Test
+
+import java.lang.Boolean.TRUE
+import java.lang.reflect.Type
+import java.util
+import scala.collection.JavaConverters._
+import scala.language.implicitConversions
+
+object NCLogSpecModel {
+    val MDL_ID = "nlpcraft.any.model.test"
+    val RESULT = "OK"
+}
+
+import org.apache.nlpcraft.common.debug.NCLogSpecModel._
+
+/**
+  */
+class NCLogSpecModel extends NCModelAdapter(MDL_ID, "IDL Test Model", "1.0") {
+    @NCIntent("intent=i term(a)={tok_id()=='nlpcraft:nlp'}")
+    private def callback(ctx: NCIntentMatch): NCResult = NCResult.text(RESULT)
+}
+
+/**
+  * Log test.
+  */
+@NCTestEnvironment(model = classOf[NCLogSpecModel], startClient = false)
+class NCLogSpec extends NCMetaSpecAdapter {
+    private val CLIENT = HttpClients.createDefault
+    private val GSON = new GsonBuilder().setPrettyPrinting().create
+    private val TYPE_RESP = new TypeToken[util.HashMap[String, AnyRef]]() {}.getType
+    private val TYPE_LOG = new TypeToken[NCLogHolder]() {}.getType
+
+    private def postHttp(url: String, params: (String, AnyRef)*): String = {
+        val post = new HttpPost(DFLT_BASEURL + url)
+
+        try {
+            post.setHeader("Content-Type", "application/json")
+            post.setEntity(new StringEntity(GSON.toJson(params.toMap.asJava), "UTF-8"))
+
+            CLIENT.execute(
+                post,
+                (resp: HttpResponse) ⇒ {
+                    val code = resp.getStatusLine.getStatusCode
+                    val entity = resp.getEntity
+
+                    code match {
+                        case 200 ⇒ EntityUtils.toString(entity)
+                        case _ ⇒ throw new Exception(s"Unexpected response [code=$code, entity=$entity]")
+                    }
+                }
+            )
+        }
+        finally
+            post.releaseConnection()
+    }
+
+    private def extract[T](js: JsonElement, t: Type): T = GSON.fromJson(js, t)
+    private def getField[T](m: util.Map[String, AnyRef], fn: String): T = m.get(fn).asInstanceOf[T]
+
+    private def ask(txt: String): Map[String, Any] = {
+        require(tkn != null)
+
+        val res: java.util.HashMap[String, Any] = extract(
+            GSON.toJsonTree(
+                getField(
+                    GSON.fromJson(
+                        postHttp("ask/sync", "acsTok" → tkn, "txt" → txt, "mdlId" → MDL_ID, "enableLog" → TRUE),
+                        TYPE_RESP
+                    ),
+                    "state"
+                )
+            ),
+            classOf[java.util.HashMap[String, Any]]
+        )
+
+        val m = res.asScala.toMap
+
+        require(m("resBody") == RESULT, s"Unexpected result: ${m("resBody")}")
+
+        m
+    }
+
+    private def check(meta: MetaHolder): Unit = {
+        val data = ask("test")
+
+        val log: NCLogHolder =
+            GSON.fromJson(GSON.toJson(data("logHolder").asInstanceOf[JavaMeta]), TYPE_LOG)
+
+        val ctx = log.queryContext
+
+        println(s"Company meta=${ctx.request.company.meta}")
+        println(s"User meta=${ctx.request.user.meta}")
+
+        def norm(m: JavaMeta): JavaMeta = if (m != null && m.isEmpty) null else m
+
+        require(
+            norm(ctx.request.company.meta) == norm(meta.companyMeta),
+            s"Unexpected company meta [expected=${meta.companyMeta}, meta=${ctx.request.company.meta}"
+        )
+        require(
+            norm(ctx.request.user.meta) == norm(meta.userMeta),
+            s"Unexpected user meta [expected=${meta.userMeta}, meta=${ctx.request.user.meta}"
+        )
+    }
+
+    @Test
+    def testLogMeta(): Unit = {
+        val meta = getMeta()
+
+        try {
+            check(meta)
+
+            def mkMeta(k: String, v: Object): JavaMeta = Map(k → v).asJava
+
+            val newMeta = MetaHolder(userMeta = mkMeta("userKey", "v1"), companyMeta = mkMeta("compKey", "v2"))
+
+            setMeta(newMeta)
+
+            check(newMeta)
+        }
+        finally {
+            setMeta(meta)
+
+            check(meta)
+        }
+    }
+}
+
diff --git a/nlpcraft/src/test/scala/org/apache/nlpcraft/model/meta/NCMetaSpec.scala b/nlpcraft/src/test/scala/org/apache/nlpcraft/model/meta/NCMetaSpec.scala
index b851629..a1b4e2c 100644
--- a/nlpcraft/src/test/scala/org/apache/nlpcraft/model/meta/NCMetaSpec.scala
+++ b/nlpcraft/src/test/scala/org/apache/nlpcraft/model/meta/NCMetaSpec.scala
@@ -19,7 +19,6 @@ package org.apache.nlpcraft.model.meta
 
 import org.apache.nlpcraft.model.`abstract`.NCAbstractTokensModel
 import org.apache.nlpcraft.model.{NCElement, NCIntent, NCIntentMatch, NCResult}
-import org.apache.nlpcraft.server.rest.NCRestSpec
 import org.apache.nlpcraft.{NCTestElement, NCTestEnvironment}
 import org.junit.jupiter.api.Test
 
@@ -46,90 +45,20 @@ class NcMetaModel extends NCAbstractTokensModel {
 }
 
 @NCTestEnvironment(model = classOf[NcMetaModel], startClient = true)
-class NCMetaSpec extends NCRestSpec {
-    type Meta = java.util.Map[String, String]
-    case class MetaHolder(userMeta: Meta, companyMeta: Meta)
-
-    private def get(): MetaHolder = {
-        var userMeta: Meta = null
-        var companyMeta: Meta = null
-
-        post("user/get")(
-            ("$.properties", (props: java.util.Map[String, String]) ⇒ userMeta = props)
-        )
-
-        post("company/get")(
-            ("$.properties", (props: java.util.Map[String, String]) ⇒ companyMeta = props)
-        )
-
-        MetaHolder(userMeta, companyMeta)
-    }
-
-    private def runTest(h: MetaHolder): Unit = {
-        def convert(m: Meta): Meta = if (m == null) util.Collections.emptyMap() else m
-
-        // 1. We have to save all existing company's fields for following updates.
-        var compName: String = null
-        var compWebsite: String = null
-        var compCountry: String = null
-        var compRegion: String = null
-        var compCity: String = null
-        var compAddress: String = null
-        var compPostalCode: String = null
-
-        post("company/get")(
-            ("$.name", (v: String) ⇒ compName = v),
-            ("$.website", (v: String) ⇒ compWebsite = v),
-            ("$.country", (v: String) ⇒ compCountry = v),
-            ("$.region", (v: String) ⇒ compRegion = v),
-            ("$.city", (v: String) ⇒ compCity = v),
-            ("$.address", (v: String) ⇒ compAddress = v),
-            ("$.postalCode", (v: String) ⇒ compPostalCode = v)
-        )
-
-        post("company/update",
-            "name" → compName,
-            "website" → compWebsite,
-            "country" → compCountry,
-            "region" → compRegion,
-            "city" → compCity,
-            "address" → compAddress,
-            "postalCode" → compPostalCode,
-            "properties" → convert(h.companyMeta)
-        )()
-
-        // 2. We have to save all existing user's fields for following updates.
-        var usrFirstName: String = null
-        var usrLastName: String = null
-        var usrAvatarUrl: String = null
-
-        post("user/get")(
-            ("$.firstName", (v: String) ⇒ usrFirstName = v),
-            ("$.lastName", (v: String) ⇒ usrLastName = v),
-            ("$.avatarUrl", (v: String) ⇒ usrAvatarUrl = v)
-        )
-
-        post("user/update",
-        "firstName" → usrFirstName,
-            "lastName" → usrLastName,
-            "avatarUrl" → usrAvatarUrl,
-            "properties" → convert(h.userMeta)
-        )()
-    }
-
+class NCMetaSpec extends NCMetaSpecAdapter {
     @Test
     def testWithoutMeta(): Unit = require(getClient.ask("a").isFailed)
 
     @Test
     def testWithMeta(): Unit = {
-        val currUserCompMeta = get()
+        val currUserCompMeta = getMeta()
         val sys = new SystemProperties
 
-        val m = Map("k1" → "v1").asJava
+        val m = Map("k1" → "v1".asInstanceOf[Object]).asJava
 
         try {
             // Sets company and user metadata.
-            runTest(MetaHolder(m, m))
+            setMeta(MetaHolder(m, m))
 
             // It is not enough.
             require(getClient.ask("a").isFailed)
@@ -143,7 +72,7 @@ class NCMetaSpec extends NCRestSpec {
         finally {
             sys.remove("k1")
 
-            runTest(currUserCompMeta)
+            setMeta(currUserCompMeta)
         }
     }
 }
diff --git a/nlpcraft/src/test/scala/org/apache/nlpcraft/model/meta/NCMetaSpec.scala b/nlpcraft/src/test/scala/org/apache/nlpcraft/model/meta/NCMetaSpecAdapter.scala
similarity index 56%
copy from nlpcraft/src/test/scala/org/apache/nlpcraft/model/meta/NCMetaSpec.scala
copy to nlpcraft/src/test/scala/org/apache/nlpcraft/model/meta/NCMetaSpecAdapter.scala
index b851629..d5705ca 100644
--- a/nlpcraft/src/test/scala/org/apache/nlpcraft/model/meta/NCMetaSpec.scala
+++ b/nlpcraft/src/test/scala/org/apache/nlpcraft/model/meta/NCMetaSpecAdapter.scala
@@ -17,56 +17,31 @@
 
 package org.apache.nlpcraft.model.meta
 
-import org.apache.nlpcraft.model.`abstract`.NCAbstractTokensModel
-import org.apache.nlpcraft.model.{NCElement, NCIntent, NCIntentMatch, NCResult}
+import org.apache.nlpcraft.common.JavaMeta
 import org.apache.nlpcraft.server.rest.NCRestSpec
-import org.apache.nlpcraft.{NCTestElement, NCTestEnvironment}
-import org.junit.jupiter.api.Test
 
 import java.util
-import scala.collection.JavaConverters._
-import scala.sys.SystemProperties
 
-/**
-  * Model for test following meta usage: company, user and system.
-  */
-class NcMetaModel extends NCAbstractTokensModel {
-    override def getElements: util.Set[NCElement] = Set(NCTestElement("a", "a"))
+abstract class NCMetaSpecAdapter extends NCRestSpec {
+    case class MetaHolder(userMeta: JavaMeta, companyMeta: JavaMeta)
 
-    @NCIntent(
-        "intent=i " +
-        "  term(t)={" +
-        "      tok_id() == 'a' && " +
-        "      meta_user('k1') == 'v1' && " +
-        "      meta_company('k1') == 'v1' && " +
-        "      meta_sys('k1') == 'v1'" +
-        "  }"
-    )
-    def onIntent(ctx: NCIntentMatch): NCResult = NCResult.text("OK")
-}
-
-@NCTestEnvironment(model = classOf[NcMetaModel], startClient = true)
-class NCMetaSpec extends NCRestSpec {
-    type Meta = java.util.Map[String, String]
-    case class MetaHolder(userMeta: Meta, companyMeta: Meta)
-
-    private def get(): MetaHolder = {
-        var userMeta: Meta = null
-        var companyMeta: Meta = null
+    protected def getMeta(): MetaHolder = {
+        var userMeta: JavaMeta = null
+        var companyMeta: JavaMeta = null
 
         post("user/get")(
-            ("$.properties", (props: java.util.Map[String, String]) ⇒ userMeta = props)
+            ("$.properties", (props: JavaMeta) ⇒ userMeta = props)
         )
 
         post("company/get")(
-            ("$.properties", (props: java.util.Map[String, String]) ⇒ companyMeta = props)
+            ("$.properties", (props: JavaMeta) ⇒ companyMeta = props)
         )
 
         MetaHolder(userMeta, companyMeta)
     }
 
-    private def runTest(h: MetaHolder): Unit = {
-        def convert(m: Meta): Meta = if (m == null) util.Collections.emptyMap() else m
+    protected def setMeta(h: MetaHolder): Unit = {
+        def convert(m: JavaMeta): JavaMeta = if (m == null) util.Collections.emptyMap() else m
 
         // 1. We have to save all existing company's fields for following updates.
         var compName: String = null
@@ -116,34 +91,4 @@ class NCMetaSpec extends NCRestSpec {
             "properties" → convert(h.userMeta)
         )()
     }
-
-    @Test
-    def testWithoutMeta(): Unit = require(getClient.ask("a").isFailed)
-
-    @Test
-    def testWithMeta(): Unit = {
-        val currUserCompMeta = get()
-        val sys = new SystemProperties
-
-        val m = Map("k1" → "v1").asJava
-
-        try {
-            // Sets company and user metadata.
-            runTest(MetaHolder(m, m))
-
-            // It is not enough.
-            require(getClient.ask("a").isFailed)
-
-            // Sets sys metadata.
-            sys.put("k1", "v1")
-
-            // Ok.
-            require(getClient.ask("a").isOk)
-        }
-        finally {
-            sys.remove("k1")
-
-            runTest(currUserCompMeta)
-        }
-    }
 }
diff --git a/nlpcraft/src/test/scala/org/apache/nlpcraft/server/rest/NCRestSpec.scala b/nlpcraft/src/test/scala/org/apache/nlpcraft/server/rest/NCRestSpec.scala
index 47b6f95..9aaba98 100644
--- a/nlpcraft/src/test/scala/org/apache/nlpcraft/server/rest/NCRestSpec.scala
+++ b/nlpcraft/src/test/scala/org/apache/nlpcraft/server/rest/NCRestSpec.scala
@@ -127,7 +127,7 @@ class NCRestSpec extends NCTestContext {
     type ResponseList = java.util.List[ResponseContent]
     type JList[T] = java.util.List[T]
 
-    private var tkn: String = _
+    protected var tkn: String = _
 
     /**
       *