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

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

commit 5ec9a27b5c9e4e47e5d405181b7f7a3cff517eb8
Author: Sergey Kamov <[email protected]>
AuthorDate: Wed Mar 17 16:04:17 2021 +0300

    WIP.
---
 nlpcraft/src/main/resources/sql/create_schema.sql  | 19 ++++--
 nlpcraft/src/main/resources/sql/drop_schema.sql    |  3 +-
 .../nlpcraft/server/company/NCCompanyManager.scala | 52 ++++++++++++---
 .../nlpcraft/server/rest/NCBasicRestApi.scala      | 32 ++++++---
 .../apache/nlpcraft/server/sql/NCSqlManager.scala  | 78 +++++++++++++++++++---
 .../nlpcraft/server/user/NCUserManager.scala       |  3 +-
 .../nlpcraft/server/rest/NCRestCompanySpec.scala   | 67 ++++++++++++++-----
 openapi/nlpcraft_swagger.yml                       | 15 +++++
 sql/mysql/drop_schema.sql                          | 13 ++--
 sql/mysql/schema.sql                               | 14 ++++
 sql/oracle/drop_schema.sql                         |  8 +++
 sql/oracle/schema.sql                              | 19 +++++-
 sql/postgres/drop_schema.sql                       | 13 ++--
 sql/postgres/schema.sql                            | 15 +++++
 14 files changed, 286 insertions(+), 65 deletions(-)

diff --git a/nlpcraft/src/main/resources/sql/create_schema.sql 
b/nlpcraft/src/main/resources/sql/create_schema.sql
index d10cc4e..570e215 100644
--- a/nlpcraft/src/main/resources/sql/create_schema.sql
+++ b/nlpcraft/src/main/resources/sql/create_schema.sql
@@ -34,9 +34,21 @@ CREATE TABLE nc_company (
     last_modified_on TIMESTAMP NOT NULL
 ) WITH "template=replicated, atomicity=transactional";
 
-CREATE INDEX company_idx_1 ON nc_company(name);
-CREATE INDEX company_idx_2 ON nc_company(auth_token);
-CREATE INDEX company_idx_3 ON nc_company(auth_token_hash);
+CREATE INDEX nc_company_idx_1 ON nc_company(name);
+CREATE INDEX nc_company_idx_2 ON nc_company(auth_token);
+CREATE INDEX nc_company_idx_3 ON nc_company(auth_token_hash);
+
+DROP TABLE IF EXISTS nc_company_property;
+CREATE TABLE nc_company_property (
+    id LONG PRIMARY KEY,
+    company_id LONG NOT NULL, -- Foreign key nc_company.id.
+    property VARCHAR NOT NULL,
+    value VARCHAR NULL,
+    created_on TIMESTAMP NOT NULL,
+    last_modified_on TIMESTAMP NOT NULL
+) WITH "template=replicated, atomicity=transactional";
+
+CREATE INDEX nc_company_property_idx_1 ON nc_company_property(company_id);
 
 DROP TABLE IF EXISTS nc_user;
 CREATE TABLE nc_user (
@@ -57,7 +69,6 @@ CREATE INDEX nc_user_idx_1 ON nc_user(email);
 CREATE INDEX nc_user_idx_2 ON nc_user(company_id, ext_id);
 CREATE INDEX nc_user_idx_3 ON nc_user(company_id);
 
-
 DROP TABLE IF EXISTS nc_user_property;
 CREATE TABLE nc_user_property (
     id LONG PRIMARY KEY,
diff --git a/nlpcraft/src/main/resources/sql/drop_schema.sql 
b/nlpcraft/src/main/resources/sql/drop_schema.sql
index 8756fd4..b1ce0b9 100644
--- a/nlpcraft/src/main/resources/sql/drop_schema.sql
+++ b/nlpcraft/src/main/resources/sql/drop_schema.sql
@@ -18,6 +18,7 @@
 DROP TABLE IF EXISTS proc_log;
 DROP TABLE IF EXISTS nc_user_property;
 DROP TABLE IF EXISTS nc_user;
+DROP TABLE IF EXISTS nc_company_property;
 DROP TABLE IF EXISTS nc_company;
 DROP TABLE IF EXISTS passwd_pool;
-DROP TABLE IF EXISTS feedback CASCADE;
\ No newline at end of file
+DROP TABLE IF EXISTS feedback;
\ No newline at end of file
diff --git 
a/nlpcraft/src/main/scala/org/apache/nlpcraft/server/company/NCCompanyManager.scala
 
b/nlpcraft/src/main/scala/org/apache/nlpcraft/server/company/NCCompanyManager.scala
index 8274de4..41a6444 100644
--- 
a/nlpcraft/src/main/scala/org/apache/nlpcraft/server/company/NCCompanyManager.scala
+++ 
b/nlpcraft/src/main/scala/org/apache/nlpcraft/server/company/NCCompanyManager.scala
@@ -21,7 +21,7 @@ import io.opencensus.trace.Span
 import org.apache.ignite.{IgniteAtomicSequence, IgniteSemaphore}
 import org.apache.nlpcraft.common.{NCService, _}
 import org.apache.nlpcraft.server.ignite.NCIgniteInstance
-import org.apache.nlpcraft.server.mdo.NCCompanyMdo
+import org.apache.nlpcraft.server.mdo.{NCCompanyMdo, NCCompanyPropertyMdo}
 import org.apache.nlpcraft.server.sql.{NCSql, NCSqlManager}
 import org.apache.nlpcraft.server.user.NCUserManager
 
@@ -98,8 +98,9 @@ object NCCompanyManager extends NCService with 
NCIgniteInstance {
                         adminFirstName = "Hermann",
                         adminLastName = "Minkowski",
                         adminAvatarUrl = None,
-                        () ⇒ U.DFLT_PROBE_TOKEN,
-                        span
+                        props = None,
+                        mkToken = () ⇒ U.DFLT_PROBE_TOKEN,
+                        parent = span
                     )
 
                     logger.info(s"Default admin user ($adminEmail/$adminPwd) 
created for default company: $compName")
@@ -149,12 +150,13 @@ object NCCompanyManager extends NCService with 
NCIgniteInstance {
       * @param id
       * @param name
       * @param website
-      * @param address
-      * @param city
+      * @param country
       * @param region
+      * @param city
+      * @param address
       * @param postalCode
-      * @param country
-      * parent Optional parent span.
+      * @param props
+      * @param parent
       */
     @throws[NCE]
     def updateCompany(
@@ -166,6 +168,7 @@ object NCCompanyManager extends NCService with 
NCIgniteInstance {
         city: Option[String],
         address: Option[String],
         postalCode: Option[String],
+        props: Option[Map[String, String]],
         parent: Span = null
     ): Unit =
         startScopedSpan("updateCompany", parent, "id" → id) { span ⇒
@@ -191,6 +194,7 @@ object NCCompanyManager extends NCService with 
NCIgniteInstance {
                             city = city,
                             address = address,
                             postalCode = postalCode,
+                            propsOpt = props,
                             parent = span
                         )
          
@@ -250,12 +254,19 @@ object NCCompanyManager extends NCService with 
NCIgniteInstance {
       *
       * @param name
       * @param website
-      * @param address
-      * @param city
+      * @param country
       * @param region
+      * @param city
+      * @param address
       * @param postalCode
-      * @param country
-      * @param parent Optional parent span.
+      * @param adminEmail
+      * @param adminPwd
+      * @param adminFirstName
+      * @param adminLastName
+      * @param adminAvatarUrl
+      * @param props
+      * @param mkToken
+      * @param parent
       */
     @throws[NCE]
     private def addCompany0(
@@ -271,6 +282,7 @@ object NCCompanyManager extends NCService with 
NCIgniteInstance {
         adminFirstName: String,
         adminLastName: String,
         adminAvatarUrl: Option[String],
+        props: Option[Map[String, String]],
         mkToken: () ⇒ String,
         parent: Span = null
     ): NCCompanyCreationData = {
@@ -299,6 +311,7 @@ object NCCompanyManager extends NCService with 
NCIgniteInstance {
                             city,
                             address,
                             postalCode,
+                            props,
                             span
                         )
     
@@ -340,6 +353,7 @@ object NCCompanyManager extends NCService with 
NCIgniteInstance {
       * @param adminPwd
       * @param adminFirstName
       * @param adminLastName
+      * @param props
       * @param adminAvatarUrl
       * @param parent Optional parent span.
       */
@@ -357,6 +371,7 @@ object NCCompanyManager extends NCService with 
NCIgniteInstance {
         adminFirstName: String,
         adminLastName: String,
         adminAvatarUrl: Option[String],
+        props: Option[Map[String, String]],
         parent: Span = null
     ): NCCompanyCreationData = startScopedSpan("addCompany", parent, "name" → 
name) { _ ⇒
         addCompany0(
@@ -372,7 +387,22 @@ object NCCompanyManager extends NCService with 
NCIgniteInstance {
             adminFirstName,
             adminLastName,
             adminAvatarUrl,
+            props,
             () ⇒ mkToken()
         )
     }
+
+    /**
+      * Gets company properties for given company ID.
+      *
+      * @param id User ID.
+      * @param parent Optional parent span.
+      */
+    @throws[NCE]
+    def getCompanyProperties(id: Long, parent: Span = null): 
Seq[NCCompanyPropertyMdo] =
+        startScopedSpan("getCompanyProperties", parent, "companyId" → id) { 
span ⇒
+            NCSql.sql {
+                NCSqlManager.getCompanyProperties(id, 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 48204a0..bfd8d2a 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
@@ -232,7 +232,7 @@ class NCBasicRestApi extends NCRestApi with LazyLogging 
with NCOpenCensusTrace w
       * @param propsOpt Optional properties.
       */
     @throws[TooLargeField]
-    private def checkUserProperties(propsOpt: Option[Map[String, String]]): 
Unit =
+    private def checkProperties(propsOpt: Option[Map[String, String]]): Unit =
         propsOpt match {
             case Some(props) ⇒
                 props.foreach { case (k, v) ⇒
@@ -880,7 +880,8 @@ class NCBasicRestApi extends NCRestApi with LazyLogging 
with NCOpenCensusTrace w
             adminPasswd: String,
             adminFirstName: String,
             adminLastName: String,
-            adminAvatarUrl: Option[String]
+            adminAvatarUrl: Option[String],
+            properties: Option[Map[String, String]]
         )
         case class Res$Company$Add(
             status: String,
@@ -888,7 +889,7 @@ class NCBasicRestApi extends NCRestApi with LazyLogging 
with NCOpenCensusTrace w
             adminId: Long
         )
 
-        implicit val reqFmt: RootJsonFormat[Req$Company$Add] = 
jsonFormat13(Req$Company$Add)
+        implicit val reqFmt: RootJsonFormat[Req$Company$Add] = 
jsonFormat14(Req$Company$Add)
         implicit val resFmt: RootJsonFormat[Res$Company$Add] = 
jsonFormat3(Res$Company$Add)
 
         //noinspection DuplicatedCod
@@ -910,6 +911,8 @@ class NCBasicRestApi extends NCRestApi with LazyLogging 
with NCOpenCensusTrace w
                     "adminAvatarUrl" → req.adminAvatarUrl
                 )
 
+                checkProperties(req.properties)
+
                 // Via REST only administrators of already created companies 
can create new companies.
                 authenticateAsAdmin(req.acsTok)
 
@@ -926,6 +929,7 @@ class NCBasicRestApi extends NCRestApi with LazyLogging 
with NCOpenCensusTrace w
                     req.adminFirstName,
                     req.adminLastName,
                     req.adminAvatarUrl,
+                    req.properties,
                     span
                 )
 
@@ -953,11 +957,12 @@ class NCBasicRestApi extends NCRestApi with LazyLogging 
with NCOpenCensusTrace w
             region: Option[String],
             city: Option[String],
             address: Option[String],
-            postalCode: Option[String]
+            postalCode: Option[String],
+            properties: Option[Map[String, String]]
         )
 
         implicit val reqFmt: RootJsonFormat[Req$Company$Get] = 
jsonFormat1(Req$Company$Get)
-        implicit val resFmt: RootJsonFormat[Res$Company$Get] = 
jsonFormat9(Res$Company$Get)
+        implicit val resFmt: RootJsonFormat[Res$Company$Get] = 
jsonFormat10(Res$Company$Get)
 
         entity(as[Req$Company$Get]) { req ⇒
             startScopedSpan("company$get", "acsTok" → req.acsTok) { span ⇒
@@ -969,6 +974,8 @@ class NCBasicRestApi extends NCRestApi with LazyLogging 
with NCOpenCensusTrace w
                     getCompany(acsUsr.companyId, span).
                     getOrElse(throw new NCE(s"Company not found: 
${acsUsr.companyId}"))
 
+                val props = 
NCCompanyManager.getCompanyProperties(acsUsr.companyId, span)
+
                 complete {
                     Res$Company$Get(API_OK,
                         company.id,
@@ -978,7 +985,8 @@ class NCBasicRestApi extends NCRestApi with LazyLogging 
with NCOpenCensusTrace w
                         company.region,
                         company.city,
                         company.address,
-                        company.postalCode
+                        company.postalCode,
+                        if (props.isEmpty) None else Some(props.map(p ⇒ 
p.property → p.value).toMap)
                     )
                 }
             }
@@ -1001,13 +1009,14 @@ class NCBasicRestApi extends NCRestApi with LazyLogging 
with NCOpenCensusTrace w
             region: Option[String],
             city: Option[String],
             address: Option[String],
-            postalCode: Option[String]
+            postalCode: Option[String],
+            properties: Option[Map[String, String]]
         )
         case class Res$Company$Update(
             status: String
         )
 
-        implicit val reqFmt: RootJsonFormat[Req$Company$Update] = 
jsonFormat8(Req$Company$Update)
+        implicit val reqFmt: RootJsonFormat[Req$Company$Update] = 
jsonFormat9(Req$Company$Update)
         implicit val resFmt: RootJsonFormat[Res$Company$Update] = 
jsonFormat1(Res$Company$Update)
 
         entity(as[Req$Company$Update]) { req ⇒
@@ -1022,6 +1031,8 @@ class NCBasicRestApi extends NCRestApi with LazyLogging 
with NCOpenCensusTrace w
                     "postalCode" → req.postalCode
                 )
 
+                checkProperties(req.properties)
+
                 val admUsr = authenticateAsAdmin(req.acsTok)
 
                 NCCompanyManager.updateCompany(
@@ -1033,6 +1044,7 @@ class NCBasicRestApi extends NCRestApi with LazyLogging 
with NCOpenCensusTrace w
                     req.city,
                     req.address,
                     req.postalCode,
+                    req.properties,
                     span
                 )
 
@@ -1325,7 +1337,7 @@ class NCBasicRestApi extends NCRestApi with LazyLogging 
with NCOpenCensusTrace w
                     "usrExtId" → req.usrExtId
                 )
 
-                checkUserProperties(req.properties)
+                checkProperties(req.properties)
 
                 val admUsr = authenticateAsAdmin(req.acsTok)
 
@@ -1381,7 +1393,7 @@ class NCBasicRestApi extends NCRestApi with LazyLogging 
with NCOpenCensusTrace w
                     "avatarUrl" → req.avatarUrl
                 )
 
-                checkUserProperties(req.properties)
+                checkProperties(req.properties)
 
                 val acsUsr = authenticate(req.acsTok)
 
diff --git 
a/nlpcraft/src/main/scala/org/apache/nlpcraft/server/sql/NCSqlManager.scala 
b/nlpcraft/src/main/scala/org/apache/nlpcraft/server/sql/NCSqlManager.scala
index 93d8788..00fe81b 100644
--- a/nlpcraft/src/main/scala/org/apache/nlpcraft/server/sql/NCSqlManager.scala
+++ b/nlpcraft/src/main/scala/org/apache/nlpcraft/server/sql/NCSqlManager.scala
@@ -17,8 +17,6 @@
 
 package org.apache.nlpcraft.server.sql
 
-import java.sql.Timestamp
-
 import io.opencensus.trace.Span
 import org.apache.ignite.IgniteAtomicSequence
 import org.apache.nlpcraft.common.config.NCConfigurable
@@ -28,6 +26,7 @@ import org.apache.nlpcraft.server.ignite.NCIgniteInstance
 import org.apache.nlpcraft.server.mdo._
 import org.apache.nlpcraft.server.sql.NCSql.Implicits._
 
+import java.sql.Timestamp
 import scala.util.control.Exception.catching
 
 /**
@@ -35,7 +34,9 @@ import scala.util.control.Exception.catching
   * Note that all functions in this class expect outside `NCSql.sql()` block.
   */
 object NCSqlManager extends NCService with NCIgniteInstance {
-    private final val DB_TABLES = Seq("nc_company", "nc_user", 
"nc_user_property", "passwd_pool", "proc_log", "feedback")
+    private final val DB_TABLES = Seq(
+        "nc_company", "nc_company_property", "nc_user", "nc_user_property", 
"passwd_pool", "proc_log", "feedback"
+    )
     private final val CACHE_2_CLEAR = Seq(
         "user-token-signin-cache",
         "user-id-signin-cache",
@@ -50,6 +51,7 @@ object NCSqlManager extends NCService with NCIgniteInstance {
     }
 
     @volatile private var usersPropsSeq: IgniteAtomicSequence = _
+    @volatile private var compPropsSeq: IgniteAtomicSequence = _
 
     /**
      *
@@ -68,6 +70,7 @@ object NCSqlManager extends NCService with NCIgniteInstance {
      
         catching(wrapIE) {
             usersPropsSeq = NCSql.mkSeq(ignite, "usersPropsSeq", 
"nc_user_property", "id")
+            compPropsSeq = NCSql.mkSeq(ignite, "companiesPropsSeq", 
"nc_company_property", "id")
         }
      
         ackStarted()
@@ -276,7 +279,6 @@ object NCSqlManager extends NCService with NCIgniteInstance 
{
             n
         }
 
-
     /**
       *
       * @param id
@@ -308,6 +310,36 @@ object NCSqlManager extends NCService with 
NCIgniteInstance {
         }
 
     /**
+      *
+      * @param id
+      * @param propsOpt
+      * @param parent Optional parent span.
+      */
+    private def addCompanyProperties(id: Long, propsOpt: Option[Map[String, 
String]], parent: Span): Unit =
+        startScopedSpan("addCompanyProperties", parent, "companyId" → id) { _ ⇒
+            propsOpt match {
+                case Some(props) ⇒
+                    val now = U.nowUtcTs()
+
+                    props.foreach { case (k, v) ⇒
+                        NCSql.insert(
+                            s"""
+                               |INSERT INTO nc_company_property (id, 
company_id, property, value, created_on, last_modified_on)
+                               |VALUES(?, ?, ?, ?, ?, ?)
+                            """.stripMargin,
+                            compPropsSeq.getAndIncrement(),
+                            id,
+                            k,
+                            v,
+                            now,
+                            now
+                        )
+                    }
+                case None ⇒ // No-op.
+            }
+        }
+
+    /**
       * Updates user.
       *
       * @param id ID of the user to update.
@@ -342,6 +374,7 @@ object NCSqlManager extends NCService with NCIgniteInstance 
{
       * @param city Company's city. Optional.
       * @param address Company's address. Optional.
       * @param postalCode Company's postal code. Optional.
+      * @param propsOpt Company's property. Optional.
       * @param parent Optional parent span.
       */
     @throws[NCE]
@@ -354,9 +387,11 @@ object NCSqlManager extends NCService with 
NCIgniteInstance {
         city: Option[String],
         address: Option[String],
         postalCode: Option[String],
-        parent: Span): Int =
+        propsOpt: Option[Map[String, String]],
+        parent: Span
+    ): Int =
         startScopedSpan("updateCompany", parent, "compId" → id) { _ ⇒
-            NCSql.update(
+            val res = NCSql.update(
                 s"""
                    |UPDATE nc_company
                    |SET
@@ -380,6 +415,12 @@ object NCSqlManager extends NCService with 
NCIgniteInstance {
                 U.nowUtcTs(),
                 id
             )
+
+            NCSql.delete("DELETE FROM nc_company_property WHERE company_id = 
?", id)
+
+            addCompanyProperties(id, propsOpt, parent)
+
+            res
         }
 
     /**
@@ -503,6 +544,20 @@ object NCSqlManager extends NCService with 
NCIgniteInstance {
         }
 
     /**
+      * Gets company properties for given ID.
+      *
+      * @param id User ID.
+      * @param parent Optional parent span.
+      * @return Company properties.
+      *
+      */
+    @throws[NCE]
+    def getCompanyProperties(id: Long, parent: Span): 
Seq[NCCompanyPropertyMdo] =
+        startScopedSpan("getCompanyProperties", parent, "usrId" → id) { _ ⇒
+            NCSql.select[NCCompanyPropertyMdo]("SELECT * FROM 
nc_company_property WHERE company_id = ?", id)
+        }
+
+    /**
       * Gets user properties for given external ID.
       *
       * @param companyId Company ID.
@@ -574,6 +629,7 @@ object NCSqlManager extends NCService with NCIgniteInstance 
{
       * @param city Company's city. Optional.
       * @param address Company's address. Optional.
       * @param postalCode Company's postal code. Optional.
+      * @param propsOpt Company's properties. Optional.
       * @param parent Optional parent span.
       */
     @throws[NCE]
@@ -587,7 +643,9 @@ object NCSqlManager extends NCService with NCIgniteInstance 
{
         city: Option[String],
         address: Option[String],
         postalCode: Option[String],
-        parent: Span): Unit =
+        propsOpt: Option[Map[String, String]],
+        parent: Span
+    ): Unit =
         startScopedSpan("addCompany", parent, "compId" → id, "name" → name, 
"tkn" → tkn) { _ ⇒
             val now = U.nowUtcTs()
     
@@ -623,6 +681,8 @@ object NCSqlManager extends NCService with NCIgniteInstance 
{
                 now,
                 now
             )
+
+            addCompanyProperties(id, propsOpt, parent)
         }
 
     /**
@@ -637,7 +697,7 @@ object NCSqlManager extends NCService with NCIgniteInstance 
{
       * @param avatarUrl User's avatar URL. Optional.
       * @param passwdSalt Salt for password Blowfish hashing. Optional.
       * @param isAdmin Whether or not the user is admin.
-      * @param propsOpt User properties.  Optional.
+      * @param propsOpt User properties. Optional.
       * @param parent Optional parent span.
       */
     @throws[NCE]
@@ -1020,7 +1080,7 @@ object NCSqlManager extends NCService with 
NCIgniteInstance {
       */
     @throws[NCE]
     private def executeScript(sqlPath: String): Unit = 
startScopedSpan("executeScript", "sqlPath" → sqlPath) { _ ⇒
-        U.readResource(sqlPath, "UTF-8").
+        U.readResource(sqlPath).
             map(_.strip).
             filter(p ⇒ !p.startsWith("--")).
             mkString("\n").
diff --git 
a/nlpcraft/src/main/scala/org/apache/nlpcraft/server/user/NCUserManager.scala 
b/nlpcraft/src/main/scala/org/apache/nlpcraft/server/user/NCUserManager.scala
index c7e95e5..4471b31 100644
--- 
a/nlpcraft/src/main/scala/org/apache/nlpcraft/server/user/NCUserManager.scala
+++ 
b/nlpcraft/src/main/scala/org/apache/nlpcraft/server/user/NCUserManager.scala
@@ -392,7 +392,8 @@ object NCUserManager extends NCService with 
NCIgniteInstance {
         lastName: String,
         avatarUrl: Option[String],
         props: Option[Map[String, String]],
-        parent: Span = null): Unit =
+        parent: Span = null
+    ): Unit =
         startScopedSpan("updateUser", parent, "usrId" → id) { span ⇒
             NCSql.sql {
                 if (NCSqlManager.updateUser(id, firstName, lastName, 
avatarUrl, props, span) != 1)
diff --git 
a/nlpcraft/src/test/scala/org/apache/nlpcraft/server/rest/NCRestCompanySpec.scala
 
b/nlpcraft/src/test/scala/org/apache/nlpcraft/server/rest/NCRestCompanySpec.scala
index fa31849..6206fa8 100644
--- 
a/nlpcraft/src/test/scala/org/apache/nlpcraft/server/rest/NCRestCompanySpec.scala
+++ 
b/nlpcraft/src/test/scala/org/apache/nlpcraft/server/rest/NCRestCompanySpec.scala
@@ -20,7 +20,11 @@ package org.apache.nlpcraft.server.rest
 import org.junit.jupiter.api.Assertions._
 import org.junit.jupiter.api.Test
 
+import scala.collection.JavaConverters._
+
 class NCRestCompanySpec extends NCRestSpec {
+    private final val PROPS = Map("k1" → "v1", "k2" → "v2").asJava
+
     @Test
     def testCurrentCompany(): Unit = {
         var compName: String = null
@@ -93,7 +97,8 @@ class NCRestCompanySpec extends NCRestSpec {
                 "region" → "region2",
                 "city" → "city2",
                 "address" → "address2",
-                "postalCode" → "postalCode2"
+                "postalCode" → "postalCode2",
+                "properties" → PROPS
             )()
 
             // Checks company fields.
@@ -104,7 +109,8 @@ class NCRestCompanySpec extends NCRestSpec {
                 ("$.region", (region: String) ⇒ assertEquals("region2", 
region)),
                 ("$.city", (city: String) ⇒ assertEquals("city2", city)),
                 ("$.address", (address: String) ⇒ assertEquals("address2", 
address)),
-                ("$.postalCode", (postalCode: String) ⇒ 
assertEquals("postalCode2", postalCode))
+                ("$.postalCode", (postalCode: String) ⇒ 
assertEquals("postalCode2", postalCode)),
+                ("$.properties", (properties: java.util.Map[String, String]) ⇒ 
assertEquals(PROPS, properties))
             )
 
             // Updates company.
@@ -118,7 +124,8 @@ class NCRestCompanySpec extends NCRestSpec {
                 ("$.region", (region: String) ⇒ assertEquals(null, region)),
                 ("$.city", (city: String) ⇒ assertEquals(null, city)),
                 ("$.address", (address: String) ⇒ assertEquals(null, address)),
-                ("$.postalCode", (postalCode: String) ⇒ assertEquals(null, 
postalCode))
+                ("$.postalCode", (postalCode: String) ⇒ assertEquals(null, 
postalCode)),
+                ("$.properties", (properties: java.util.Map[String, String]) ⇒ 
assertEquals(null, properties)),
             )
 
             // Resets token.
@@ -130,31 +137,31 @@ class NCRestCompanySpec extends NCRestSpec {
 
     @Test
     def testParameters(): Unit = {
-        testCompany()
-        testCompany(website = Some("website"))
-        testCompany(
+        testParameters0()
+        testParameters0(website = Some("website"))
+        testParameters0(
             website = Some("website"),
             country = Some("country")
         )
-        testCompany(
+        testParameters0(
             website = Some("website"),
             country = Some("country"),
             region = Some("region")
         )
-        testCompany(
+        testParameters0(
             website = Some("website"),
             country = Some("country"),
             region = Some("region"),
             city = Some("city")
         )
-        testCompany(
+        testParameters0(
             website = Some("website"),
             country = Some("country"),
             region = Some("region"),
             city = Some("city"),
             address = Some("address")
         )
-        testCompany(
+        testParameters0(
             website = Some("website"),
             country = Some("country"),
             region = Some("region"),
@@ -162,7 +169,7 @@ class NCRestCompanySpec extends NCRestSpec {
             address = Some("address"),
             postalCode = Some("postalCode")
         )
-        testCompany(
+        testParameters0(
             website = Some("website"),
             country = Some("country"),
             region = Some("region"),
@@ -171,6 +178,16 @@ class NCRestCompanySpec extends NCRestSpec {
             postalCode = Some("postalCode"),
             adminAvatarUrl = Some("adminAvatarUrl")
         )
+        testParameters0(
+            website = Some("website"),
+            country = Some("country"),
+            region = Some("region"),
+            city = Some("city"),
+            address = Some("address"),
+            postalCode = Some("postalCode"),
+            adminAvatarUrl = Some("adminAvatarUrl"),
+            properties = Some(PROPS)
+        )
     }
 
     /**
@@ -183,14 +200,15 @@ class NCRestCompanySpec extends NCRestSpec {
       * @param postalCode
       * @param adminAvatarUrl
       */
-    private def testCompany(
+    private def testParameters0(
         website: Option[String] = None,
         country: Option[String] = None,
         region: Option[String] = None,
         city: Option[String] = None,
         address: Option[String] = None,
         postalCode: Option[String] = None,
-        adminAvatarUrl: Option[String] = None
+        adminAvatarUrl: Option[String] = None,
+        properties: Option[java.util.Map[String, String]] = None
     ): Unit = {
         val compName = rnd()
 
@@ -210,11 +228,30 @@ class NCRestCompanySpec extends NCRestSpec {
             "adminPasswd" → adminPswd,
             "adminFirstName" → "firstName",
             "adminLastName" → "lastName",
-            "adminAvatarUrl" → adminAvatarUrl.orNull
+            "adminAvatarUrl" → adminAvatarUrl.orNull,
+            "properties" → properties.orNull
         )(
             ("$.adminId", (id: Number) ⇒ assertNotNull(id))
         )
 
-        post("company/delete", signin(adminEmail, adminPswd))()
+        def check(paramOpt: Option[Any], exp: Any, js: Any): Unit =
+            paramOpt match {
+                case Some(_) ⇒ assertEquals(exp, js)
+                case None ⇒ assertEquals(null, js)
+            }
+
+        val adminTkn = signin(adminEmail, adminPswd)
+
+        post("company/get", adminTkn)(
+            ("$.website", (websiteJs: String) ⇒ check(website, "website", 
websiteJs)),
+            ("$.country", (countryJs: String) ⇒ check(country, "country", 
countryJs)),
+            ("$.region", (regionJs: String) ⇒ check(region, "region", 
regionJs)),
+            ("$.city", (cityJs: String) ⇒ check(city, "city", cityJs)),
+            ("$.address", (addressJs: String) ⇒ check(address, "address", 
addressJs)),
+            ("$.postalCode", (postalCodeJs: String) ⇒ check(postalCode, 
"postalCode", postalCodeJs)),
+            ("$.properties", (propertiesJs: java.util.Map[String, String]) ⇒ 
check(properties, PROPS, propertiesJs))
+        )
+
+        post("company/delete", adminTkn)()
     }
 }
diff --git a/openapi/nlpcraft_swagger.yml b/openapi/nlpcraft_swagger.yml
index 872ff19..ac72473 100644
--- a/openapi/nlpcraft_swagger.yml
+++ b/openapi/nlpcraft_swagger.yml
@@ -1154,6 +1154,11 @@ paths:
               postalCode:
                 description: Company postal code.
                 type: string
+              properties:
+                type: object
+                description: Additional company properties.
+                additionalProperties:
+                  type: string
         '400':
           description: Failed operation.
           schema:
@@ -1233,6 +1238,11 @@ paths:
                 type: string
                 description: <em>Optional.</em> Admin user avatar URL.
                 maxLength: 512000
+              properties:
+                type: object
+                description: <em>Optional.</em> Additional company properties.
+                additionalProperties:
+                  type: string
       responses:
         '200':
           description: Successful operation.
@@ -1311,6 +1321,11 @@ paths:
                 type: string
                 description: <em>Optional.</em> Company postal code.
                 maxLength: 512
+              properties:
+                type: object
+                description: <em>Optional.</em> Additional company properties.
+                additionalProperties:
+                  type: string
       responses:
         '200':
           description: Successful operation.
diff --git a/sql/mysql/drop_schema.sql b/sql/mysql/drop_schema.sql
index d461dda..fba01aa 100644
--- a/sql/mysql/drop_schema.sql
+++ b/sql/mysql/drop_schema.sql
@@ -17,9 +17,10 @@
 
 USE nlpcraft;
 
-DROP TABLE IF EXISTS proc_log CASCADE;
-DROP TABLE IF EXISTS nc_user_property CASCADE;
-DROP TABLE IF EXISTS nc_user CASCADE;
-DROP TABLE IF EXISTS nc_company CASCADE;
-DROP TABLE IF EXISTS passwd_pool CASCADE;
-DROP TABLE IF EXISTS feedback CASCADE;
+DROP TABLE IF EXISTS proc_log;
+DROP TABLE IF EXISTS nc_user_property;
+DROP TABLE IF EXISTS nc_user;
+DROP TABLE IF EXISTS nc_company_property;
+DROP TABLE IF EXISTS nc_company;
+DROP TABLE IF EXISTS passwd_pool;
+DROP TABLE IF EXISTS feedback;
diff --git a/sql/mysql/schema.sql b/sql/mysql/schema.sql
index 0e1bc7d..cac0bf8 100644
--- a/sql/mysql/schema.sql
+++ b/sql/mysql/schema.sql
@@ -46,6 +46,20 @@ CREATE TABLE nc_company (
 CREATE UNIQUE INDEX nc_company_idx_1 ON nc_company(name);
 CREATE UNIQUE INDEX nc_company_idx_2 ON nc_company(auth_token);
 CREATE UNIQUE INDEX nc_company_idx_3 ON nc_company(auth_token_hash);
+
+--
+-- Company properties table.
+--
+CREATE TABLE nc_company_property (
+    id SERIAL PRIMARY KEY,
+    company_id BIGINT UNSIGNED NOT NULL,
+    property VARCHAR(64) NOT NULL,
+    value VARCHAR(512) NULL,
+    created_on TIMESTAMP(3) NOT NULL DEFAULT current_timestamp(3),
+    last_modified_on TIMESTAMP(3) NOT NULL DEFAULT current_timestamp(3),
+    FOREIGN KEY (company_id) REFERENCES nc_company(id)
+);
+
 --
 -- User table.
 --
diff --git a/sql/oracle/drop_schema.sql b/sql/oracle/drop_schema.sql
index a20720f..652bdb8 100644
--- a/sql/oracle/drop_schema.sql
+++ b/sql/oracle/drop_schema.sql
@@ -40,6 +40,14 @@ EXCEPTION WHEN OTHERS THEN
 END;
 
 BEGIN
+  EXECUTE IMMEDIATE 'DROP TABLE nc_company_property';
+EXCEPTION WHEN OTHERS THEN
+  IF SQLCODE != -942 THEN
+    RAISE;
+END IF;
+END;
+
+BEGIN
   EXECUTE IMMEDIATE 'DROP TABLE nc_company';
 EXCEPTION WHEN OTHERS THEN
   IF SQLCODE != -942 THEN
diff --git a/sql/oracle/schema.sql b/sql/oracle/schema.sql
index 09a61ef..1ad495c 100644
--- a/sql/oracle/schema.sql
+++ b/sql/oracle/schema.sql
@@ -46,6 +46,21 @@ CREATE UNIQUE INDEX nc_company_idx_2 ON 
nc_company(auth_token);
 CREATE UNIQUE INDEX nc_company_idx_3 ON nc_company(auth_token_hash);
 
 --
+-- Company properties table.
+--
+CREATE TABLE nc_company_property (
+    id NUMBER PRIMARY KEY,
+    company_id NUMBER NOT NULL,
+    property VARCHAR2(64) NOT NULL,
+    value VARCHAR2(512) NULL,
+    created_on DATE DEFAULT sysdate NOT NULL,
+    last_modified_on DATE DEFAULT sysdate NOT NULL,
+    CONSTRAINT fk_company_id_company_property FOREIGN KEY (company_id) 
REFERENCES nc_company(id)
+);
+
+CREATE INDEX nc_company_property_idx1 ON nc_company_property(company_id);
+
+--
 -- User table.
 --
 CREATE TABLE nc_user (
@@ -60,7 +75,7 @@ CREATE TABLE nc_user (
     passwd_salt VARCHAR2(64) NULL,
     created_on DATE DEFAULT sysdate NOT NULL,
     last_modified_on DATE DEFAULT sysdate NOT NULL,
-    CONSTRAINT fk_company_id FOREIGN KEY (company_id)REFERENCES nc_company(id)
+    CONSTRAINT fk_company_id_user FOREIGN KEY (company_id) REFERENCES 
nc_company(id)
 );
 
 CREATE UNIQUE INDEX nc_user_idx_1 ON nc_user(email);
@@ -77,7 +92,7 @@ CREATE TABLE nc_user_property (
     value VARCHAR2(512) NULL,
     created_on DATE DEFAULT sysdate NOT NULL,
     last_modified_on DATE DEFAULT sysdate NOT NULL,
-    CONSTRAINT fk_user_id FOREIGN KEY (user_id)REFERENCES nc_user(id)
+    CONSTRAINT fk_user_id_user_property FOREIGN KEY (user_id) REFERENCES 
nc_user(id)
 );
 
 CREATE INDEX nc_user_property_idx1 ON nc_user_property(user_id);
diff --git a/sql/postgres/drop_schema.sql b/sql/postgres/drop_schema.sql
index 4cd8646..b1ce0b9 100644
--- a/sql/postgres/drop_schema.sql
+++ b/sql/postgres/drop_schema.sql
@@ -15,9 +15,10 @@
 -- limitations under the License.
 --
 
-DROP TABLE IF EXISTS proc_log CASCADE;
-DROP TABLE IF EXISTS nc_user_property CASCADE;
-DROP TABLE IF EXISTS nc_user CASCADE;
-DROP TABLE IF EXISTS nc_company CASCADE;
-DROP TABLE IF EXISTS passwd_pool CASCADE;
-DROP TABLE IF EXISTS feedback CASCADE;
\ No newline at end of file
+DROP TABLE IF EXISTS proc_log;
+DROP TABLE IF EXISTS nc_user_property;
+DROP TABLE IF EXISTS nc_user;
+DROP TABLE IF EXISTS nc_company_property;
+DROP TABLE IF EXISTS nc_company;
+DROP TABLE IF EXISTS passwd_pool;
+DROP TABLE IF EXISTS feedback;
\ No newline at end of file
diff --git a/sql/postgres/schema.sql b/sql/postgres/schema.sql
index 79c3af4..561418e 100644
--- a/sql/postgres/schema.sql
+++ b/sql/postgres/schema.sql
@@ -46,6 +46,21 @@ CREATE UNIQUE INDEX nc_company_idx_2 ON 
nc_company(auth_token);
 CREATE UNIQUE INDEX nc_company_idx_3 ON nc_company(auth_token_hash);
 
 --
+-- Company properties table.
+--
+CREATE TABLE nc_company_property (
+    id SERIAL PRIMARY KEY,
+    company_id BIGINT NOT NULL,
+    property VARCHAR(64) NOT NULL,
+    value VARCHAR(512) NULL,
+    created_on TIMESTAMP(3) NOT NULL DEFAULT current_timestamp(3),
+    last_modified_on TIMESTAMP(3) NOT NULL DEFAULT current_timestamp(3),
+    FOREIGN KEY (company_id) REFERENCES nc_company(id)
+);
+
+CREATE INDEX nc_company_property_idx1 ON nc_company_property(company_id);
+
+--
 --
 -- User table.
 --

Reply via email to