[Service] add resource table accepted/tizen/unified/20230727.173056
authorJaeyun Jung <jy1210.jung@samsung.com>
Mon, 24 Jul 2023 06:03:35 +0000 (15:03 +0900)
committerjaeyun-jung <39614140+jaeyun-jung@users.noreply.github.com>
Mon, 24 Jul 2023 08:39:41 +0000 (17:39 +0900)
Add new table for ml-resource, and add functions for ml-agent.

Signed-off-by: Jaeyun Jung <jy1210.jung@samsung.com>
daemon/service-db.cc
daemon/service-db.hh
tests/daemon/unittest_service_db.cc

index d484585..0ae1618 100644 (file)
  */
 #define TBL_VER_MODEL_INFO (1)
 
+/**
+ * @brief The version of resource table schema. It should be a positive integer.
+ */
+#define TBL_VER_RESOURCE_INFO (1)
+
 typedef enum {
   TBL_DB_INFO = 0,
   TBL_PIPELINE_DESCRIPTION = 1,
   TBL_MODEL_INFO = 2,
+  TBL_RESOURCE_INFO = 3,
 
   TBL_MAX
 } mlsvc_table_e;
 
-const char *g_mlsvc_table_schema_v1[]
-    = { [TBL_DB_INFO] = "tblMLDBInfo (name TEXT PRIMARY KEY NOT NULL, version INTEGER DEFAULT 1)",
-        [TBL_PIPELINE_DESCRIPTION] = "tblPipeline (key TEXT PRIMARY KEY NOT NULL, description TEXT, CHECK (length(description) > 0))",
-        [TBL_MODEL_INFO]
-        = "tblModel (key TEXT NOT NULL, version INTEGER DEFAULT 1, active TEXT DEFAULT 'F', "
-          "path TEXT, description TEXT, app_info TEXT, PRIMARY KEY (key, version), CHECK (length(path) > 0), CHECK (active IN ('T', 'F')))",
-        NULL };
+const char *g_mlsvc_table_schema_v1[] = { [TBL_DB_INFO] = "tblMLDBInfo (name TEXT PRIMARY KEY NOT NULL, version INTEGER DEFAULT 1)",
+  [TBL_PIPELINE_DESCRIPTION] = "tblPipeline (key TEXT PRIMARY KEY NOT NULL, description TEXT, CHECK (length(description) > 0))",
+  [TBL_MODEL_INFO]
+  = "tblModel (key TEXT NOT NULL, version INTEGER DEFAULT 1, active TEXT DEFAULT 'F', "
+    "path TEXT, description TEXT, app_info TEXT, PRIMARY KEY (key, version), CHECK (length(path) > 0), CHECK (active IN ('T', 'F')))",
+  [TBL_RESOURCE_INFO] = "tblResource (key TEXT NOT NULL, path TEXT, description TEXT, PRIMARY KEY (key, path), CHECK (length(path) > 0))",
+  NULL };
 
 const char **g_mlsvc_table_schema = g_mlsvc_table_schema_v1;
 
@@ -131,6 +137,17 @@ MLServiceDB::initDB ()
   if (!set_table_version ("tblModel", TBL_VER_MODEL_INFO))
     return;
 
+  /* Check resource table. */
+  if ((tbl_ver = get_table_version ("tblResource", TBL_VER_RESOURCE_INFO)) < 0)
+    return;
+
+  if (tbl_ver != TBL_VER_RESOURCE_INFO) {
+    /** @todo update resource table if table schema is changed */
+  }
+
+  if (!set_table_version ("tblResource", TBL_VER_RESOURCE_INFO))
+    return;
+
   if (!set_transaction (false))
     return;
 
@@ -400,6 +417,28 @@ MLServiceDB::is_model_activated (const std::string key, const guint version)
 }
 
 /**
+ * @brief Check the resource is registered.
+ */
+bool
+MLServiceDB::is_resource_registered (const std::string key)
+{
+  sqlite3_stmt *res;
+  gchar *sql;
+  bool registered;
+
+  sql = g_strdup_printf ("SELECT EXISTS(SELECT 1 FROM tblResource WHERE key = ?1)");
+
+  registered
+      = !(sqlite3_prepare_v2 (_db, sql, -1, &res, nullptr) != SQLITE_OK
+          || sqlite3_bind_text (res, 1, key.c_str (), -1, nullptr) != SQLITE_OK
+          || sqlite3_step (res) != SQLITE_ROW || sqlite3_column_int (res, 0) != 1);
+  sqlite3_finalize (res);
+  g_free (sql);
+
+  return registered;
+}
+
+/**
  * @brief Set the model with the given name.
  * @param[in] name Unique name for model.
  * @param[in] model The model to be stored.
@@ -674,3 +713,123 @@ MLServiceDB::delete_model (const std::string name, const guint version)
                                  + " and version " + std::to_string (version));
   }
 }
+
+/**
+ * @brief Set the resource with given name.
+ * @param[in] name Unique name of ml-resource.
+ * @param[in] path The path to be stored.
+ * @param[in] description The description for ml-resource.
+ */
+void
+MLServiceDB::set_resource (const std::string name, const std::string path,
+    const std::string description)
+{
+  sqlite3_stmt *res;
+
+  if (name.empty () || path.empty ())
+    throw std::invalid_argument ("Invalid name or path parameter!");
+
+  std::string key_with_prefix = DB_KEY_PREFIX + std::string ("_resource_");
+  key_with_prefix += name;
+
+  if (!set_transaction (true))
+    throw std::runtime_error ("Failed to begin transaction.");
+
+  if (sqlite3_prepare_v2 (_db,
+          "INSERT OR REPLACE INTO tblResource VALUES (?1, ?2, ?3)", -1, &res, nullptr)
+          != SQLITE_OK
+      || sqlite3_bind_text (res, 1, key_with_prefix.c_str (), -1, nullptr) != SQLITE_OK
+      || sqlite3_bind_text (res, 2, path.c_str (), -1, nullptr) != SQLITE_OK
+      || sqlite3_bind_text (res, 3, description.c_str (), -1, nullptr) != SQLITE_OK
+      || sqlite3_step (res) != SQLITE_DONE) {
+    sqlite3_finalize (res);
+    throw std::runtime_error ("Failed to add the resource " + name);
+  }
+
+  sqlite3_finalize (res);
+
+  if (!set_transaction (false))
+    throw std::runtime_error ("Failed to end transaction.");
+
+  long long int last_id = sqlite3_last_insert_rowid (_db);
+  if (last_id == 0) {
+    _E ("Failed to get last inserted row id: %s", sqlite3_errmsg (_db));
+    throw std::runtime_error ("Failed to get last inserted row id.");
+  }
+}
+
+/**
+ * @brief Get the resource with given name.
+ * @param[in] name The unique name to retrieve.
+ * @param[out] resource The resource corresponding with the given name.
+ */
+void
+MLServiceDB::get_resource (const std::string name, std::string &resource)
+{
+  char *sql;
+  char *value = nullptr;
+  sqlite3_stmt *res;
+
+  if (name.empty ())
+    throw std::invalid_argument ("Invalid name parameters!");
+
+  std::string key_with_prefix = DB_KEY_PREFIX + std::string ("_resource_");
+  key_with_prefix += name;
+
+  /* existence check */
+  if (!is_resource_registered (key_with_prefix))
+    throw std::invalid_argument ("There is no resource with name " + name);
+
+  sql = g_strdup ("SELECT json_group_array(json_object('key', key, 'path', path, 'description', description)) FROM tblResource WHERE key = ?1");
+
+  if (sqlite3_prepare_v2 (_db, sql, -1, &res, nullptr) == SQLITE_OK
+      && sqlite3_bind_text (res, 1, key_with_prefix.c_str (), -1, nullptr) == SQLITE_OK
+      && sqlite3_step (res) == SQLITE_ROW)
+    value = g_strdup_printf ("%s", sqlite3_column_text (res, 0));
+
+  sqlite3_finalize (res);
+  g_free (sql);
+
+  if (!value)
+    throw std::invalid_argument ("Failed to get resource with name " + name);
+
+  resource = std::string (value);
+  g_free (value);
+}
+
+/**
+ * @brief Delete the resource.
+ * @param[in] name The unique name to delete
+ */
+void
+MLServiceDB::delete_resource (const std::string name)
+{
+  char *sql;
+  sqlite3_stmt *res;
+
+  if (name.empty ())
+    throw std::invalid_argument ("Invalid name parameters!");
+
+  std::string key_with_prefix = DB_KEY_PREFIX + std::string ("_resource_");
+  key_with_prefix += name;
+
+  /* existence check */
+  if (!is_resource_registered (key_with_prefix))
+    throw std::invalid_argument ("There is no resource with name " + name);
+
+  sql = g_strdup ("DELETE FROM tblResource WHERE key = ?1");
+
+  if (sqlite3_prepare_v2 (_db, sql, -1, &res, nullptr) != SQLITE_OK
+      || sqlite3_bind_text (res, 1, key_with_prefix.c_str (), -1, nullptr) != SQLITE_OK
+      || sqlite3_step (res) != SQLITE_DONE) {
+    sqlite3_finalize (res);
+    g_free (sql);
+    throw std::runtime_error ("Failed to delete resource with name " + name);
+  }
+
+  sqlite3_finalize (res);
+  g_free (sql);
+
+  if (sqlite3_changes (_db) == 0)
+    throw std::invalid_argument ("There is no resource with name " + name);
+}
index 87060be..b5fbe0d 100644 (file)
@@ -40,6 +40,10 @@ class MLServiceDB
   virtual void activate_model (const std::string name, const guint version);
   virtual void get_model (const std::string name, std::string &model, const gint version);
   virtual void delete_model (const std::string name, const guint version);
+  virtual void set_resource (const std::string name, const std::string path,
+      const std::string description);
+  virtual void get_resource (const std::string name, std::string &resource);
+  virtual void delete_resource (const std::string name);
 
   static MLServiceDB &getInstance (void);
 
@@ -54,6 +58,7 @@ class MLServiceDB
   bool set_transaction (bool begin);
   bool is_model_registered (const std::string key, const guint version);
   bool is_model_activated (const std::string key, const guint version);
+  bool is_resource_registered (const std::string key);
 
   std::string _path;
   bool _initialized;
index 2fa5097..2bb5387 100644 (file)
@@ -423,6 +423,219 @@ TEST (serviceDBNotInitalized, delete_model_n)
 }
 
 /**
+ * @brief Negative test for set_resource. Invalid param case (empty name or path).
+ */
+TEST (serviceDB, set_resource_n)
+{
+  MLServiceDB &db = MLServiceDB::getInstance ();
+  int gotException = 0;
+
+  db.connectDB ();
+
+  try {
+    db.set_resource ("", "resource", "description");
+  } catch (const std::exception &e) {
+    g_critical ("Got Exception: %s", e.what ());
+    gotException = 1;
+  }
+  EXPECT_EQ (gotException, 1);
+
+  gotException = 0;
+  try {
+    db.set_resource ("test", "", "description");
+  } catch (const std::exception &e) {
+    g_critical ("Got Exception: %s", e.what ());
+    gotException = 1;
+  }
+  EXPECT_EQ (gotException, 1);
+
+  db.disconnectDB ();
+}
+
+/**
+ * @brief Check resources.
+ */
+TEST (serviceDB, get_resource)
+{
+  MLServiceDB &db = MLServiceDB::getInstance ();
+  int gotException = 0;
+
+  db.connectDB ();
+
+  /* No exception to add, get, and delete resources with name 'test'. */
+  try {
+    std::string res_description;
+    gchar *pos;
+
+    db.set_resource ("test", "test_resource1", "res1_description");
+    db.set_resource ("test", "test_resource2", "res2_description");
+
+    db.get_resource ("test", res_description);
+
+    /* Check res description contains added string. */
+    pos = g_strstr_len (res_description.c_str (), -1, "test_resource1");
+    EXPECT_TRUE (pos != NULL);
+    pos = g_strstr_len (res_description.c_str (), -1, "test_resource2");
+    EXPECT_TRUE (pos != NULL);
+
+    db.delete_resource ("test");
+  } catch (const std::exception &e) {
+    g_critical ("Got Exception: %s", e.what ());
+    gotException = 1;
+  }
+  EXPECT_EQ (gotException, 0);
+
+  db.disconnectDB ();
+}
+
+/**
+ * @brief Negative test for get_resource. Empty name.
+ */
+TEST (serviceDB, get_resource_n)
+{
+  MLServiceDB &db = MLServiceDB::getInstance ();
+  int gotException = 0;
+
+  db.connectDB ();
+
+  try {
+    std::string res_description;
+    db.get_resource ("", res_description);
+  } catch (const std::exception &e) {
+    g_critical ("Got Exception: %s", e.what ());
+    gotException = 1;
+  }
+  EXPECT_EQ (gotException, 1);
+
+  db.disconnectDB ();
+}
+
+/**
+ * @brief Negative test for get_resource. Empty name or unregistered name.
+ */
+TEST (serviceDB, get_resource_unregistered_n)
+{
+  MLServiceDB &db = MLServiceDB::getInstance ();
+  int gotException = 0;
+
+  db.connectDB ();
+
+  /* Test condition, remove all resource with name 'test'. */
+  db.set_resource ("test", "test_resource", "");
+  db.delete_resource ("test");
+
+  gotException = 0;
+  try {
+    std::string res_description;
+    db.get_resource ("test", res_description);
+  } catch (const std::exception &e) {
+    g_critical ("Got Exception: %s", e.what ());
+    gotException = 1;
+  }
+  EXPECT_EQ (gotException, 1);
+
+  db.disconnectDB ();
+}
+
+/**
+ * @brief Negative test for delete_resource. Empty name.
+ */
+TEST (serviceDB, delete_resource_n)
+{
+  MLServiceDB &db = MLServiceDB::getInstance ();
+  int gotException = 0;
+
+  db.connectDB ();
+
+  try {
+    db.delete_resource ("");
+  } catch (const std::exception &e) {
+    g_critical ("Got Exception: %s", e.what ());
+    gotException = 1;
+  }
+  EXPECT_EQ (gotException, 1);
+
+  db.disconnectDB ();
+}
+
+/**
+ * @brief Negative test for delete_resource. Resource is not registered.
+ */
+TEST (serviceDB, delete_resource_unregistered_n)
+{
+  MLServiceDB &db = MLServiceDB::getInstance ();
+  int gotException = 0;
+
+  db.connectDB ();
+
+  /* Test condition, remove all resource with name 'test'. */
+  db.set_resource ("test", "test_resource", "");
+  db.delete_resource ("test");
+
+  try {
+    db.delete_resource ("test");
+  } catch (const std::exception &e) {
+    g_critical ("Got Exception: %s", e.what ());
+    gotException = 1;
+  }
+  EXPECT_EQ (gotException, 1);
+
+  db.disconnectDB ();
+}
+
+/**
+ * @brief Negative test for set_resource. DB is not initialized.
+ */
+TEST (serviceDBNotInitalized, set_resource_n)
+{
+  MLServiceDB &db = MLServiceDB::getInstance ();
+  int gotException = 0;
+
+  try {
+    db.set_resource ("test", "resource", "description");
+  } catch (const std::exception &e) {
+    g_critical ("Got Exception: %s", e.what ());
+    gotException = 1;
+  }
+  EXPECT_EQ (gotException, 1);
+}
+
+/**
+ * @brief Negative test for get_resource. DB is not initialized.
+ */
+TEST (serviceDBNotInitalized, get_resource_n)
+{
+  MLServiceDB &db = MLServiceDB::getInstance ();
+  int gotException = 0;
+
+  try {
+    std::string res_description;
+    db.get_resource ("test", res_description);
+  } catch (const std::exception &e) {
+    g_critical ("Got Exception: %s", e.what ());
+    gotException = 1;
+  }
+  EXPECT_EQ (gotException, 1);
+}
+
+/**
+ * @brief Negative test for delete_resource. DB is not initialized.
+ */
+TEST (serviceDBNotInitalized, delete_resource_n)
+{
+  MLServiceDB &db = MLServiceDB::getInstance ();
+  int gotException = 0;
+
+  try {
+    db.delete_resource ("test");
+  } catch (const std::exception &e) {
+    g_critical ("Got Exception: %s", e.what ());
+    gotException = 1;
+  }
+  EXPECT_EQ (gotException, 1);
+}
+
+/**
  * @brief Main gtest
  */
 int