From df38b5be3e31006f09eabfb571a546ed0dc11986 Mon Sep 17 00:00:00 2001
From: Cedric CAFFY <cedric.caffy@cern.ch>
Date: Wed, 4 Mar 2020 10:51:35 +0100
Subject: [PATCH] Implemented Catalogue::deleteVirtualOrganization + NOT ALL
 unit tests

---
 catalogue/CMakeLists.txt                      |  5 +-
 catalogue/Catalogue.hpp                       |  8 +-
 catalogue/CatalogueRetryWrapper.hpp           |  4 +
 catalogue/CatalogueTest.cpp                   | 24 +++++
 catalogue/DummyCatalogue.hpp                  |  1 +
 catalogue/RdbmsCatalogue.cpp                  | 95 +++++++++++++++++++
 catalogue/RdbmsCatalogue.hpp                  | 24 +++++
 ...irtualOrganizationUsedByStorageClasses.cpp | 39 ++++++++
 ...irtualOrganizationUsedByStorageClasses.hpp | 49 ++++++++++
 ...fiedVirtualOrganizationUsedByTapepools.cpp | 39 ++++++++
 ...fiedVirtualOrganizationUsedByTapepools.hpp | 49 ++++++++++
 11 files changed, 335 insertions(+), 2 deletions(-)
 create mode 100644 catalogue/UserSpecifiedVirtualOrganizationUsedByStorageClasses.cpp
 create mode 100644 catalogue/UserSpecifiedVirtualOrganizationUsedByStorageClasses.hpp
 create mode 100644 catalogue/UserSpecifiedVirtualOrganizationUsedByTapepools.cpp
 create mode 100644 catalogue/UserSpecifiedVirtualOrganizationUsedByTapepools.hpp

diff --git a/catalogue/CMakeLists.txt b/catalogue/CMakeLists.txt
index 7b94693178..1b22008d88 100644
--- a/catalogue/CMakeLists.txt
+++ b/catalogue/CMakeLists.txt
@@ -83,7 +83,10 @@ set (CATALOGUE_LIB_SRC_FILES
   UserSpecifiedStorageClassUsedByArchiveFiles.cpp
   UserSpecifiedStorageClassUsedByArchiveRoutes.cpp
   UserSpecifiedAZeroCapacity.cpp
-  UserSpecifiedAZeroCopyNb.cpp)
+  UserSpecifiedAZeroCopyNb.cpp
+  UserSpecifiedVirtualOrganizationUsedByTapepools.cpp
+  UserSpecifiedVirtualOrganizationUsedByStorageClasses.cpp
+)
 
 add_library (ctacatalogue SHARED
    ${CATALOGUE_LIB_SRC_FILES})
diff --git a/catalogue/Catalogue.hpp b/catalogue/Catalogue.hpp
index 1e0b4613de..37fcc0e2f3 100644
--- a/catalogue/Catalogue.hpp
+++ b/catalogue/Catalogue.hpp
@@ -233,6 +233,12 @@ public:
    */
   virtual void createVirtualOrganization(const common::dataStructures::SecurityIdentity &admin, const common::dataStructures::VirtualOrganization &vo) = 0;
   
+  /**
+   * Deletes the specified Virtual Organization
+   * @param voName the name of the VirtualOrganization to delete
+   */
+  virtual void deleteVirtualOrganization(const std::string &voName) = 0;
+  
   /**
    * Creates the specified storage class.
    *
@@ -247,7 +253,7 @@ public:
    * Deletes the specified storage class.
    *
    * @param storageClassName The name of the storage class which is only
-   * guaranteed to be unique within its disk isntance.
+   * guaranteed to be unique within its disk instance.
    */
   virtual void deleteStorageClass(const std::string &storageClassName) = 0;
 
diff --git a/catalogue/CatalogueRetryWrapper.hpp b/catalogue/CatalogueRetryWrapper.hpp
index b82233e422..7919c7d1fe 100644
--- a/catalogue/CatalogueRetryWrapper.hpp
+++ b/catalogue/CatalogueRetryWrapper.hpp
@@ -119,6 +119,10 @@ public:
   void createVirtualOrganization(const common::dataStructures::SecurityIdentity &admin, const common::dataStructures::VirtualOrganization &vo) override {
     return retryOnLostConnection(m_log, [&]{return m_catalogue->createVirtualOrganization(admin, vo);}, m_maxTriesToConnect);
   }
+  
+  void deleteVirtualOrganization(const std::string &voName) override {
+    return retryOnLostConnection(m_log, [&]{return m_catalogue->deleteVirtualOrganization(voName);}, m_maxTriesToConnect);
+  }
     
   void createStorageClass(const common::dataStructures::SecurityIdentity &admin, const common::dataStructures::StorageClass &storageClass) override {
     return retryOnLostConnection(m_log, [&]{return m_catalogue->createStorageClass(admin, storageClass);}, m_maxTriesToConnect);
diff --git a/catalogue/CatalogueTest.cpp b/catalogue/CatalogueTest.cpp
index e5d5897e9f..9413b1ea60 100644
--- a/catalogue/CatalogueTest.cpp
+++ b/catalogue/CatalogueTest.cpp
@@ -15325,4 +15325,28 @@ TEST_P(cta_catalogue_CatalogueTest, createVirtualOrganizationEmptyName) {
   ASSERT_THROW(m_catalogue->createVirtualOrganization(m_admin,vo),cta::exception::UserError);
 }
 
+TEST_P(cta_catalogue_CatalogueTest, deleteVirtualOrganization) {
+  using namespace cta;
+
+  common::dataStructures::VirtualOrganization vo;
+  vo.name = "vo";
+  vo.comment = "comment";
+  
+  ASSERT_NO_THROW(m_catalogue->createVirtualOrganization(m_admin,vo));
+  
+  ASSERT_NO_THROW(m_catalogue->deleteVirtualOrganization(vo.name));
+}
+
+TEST_P(cta_catalogue_CatalogueTest, deleteVirtualOrganizationNameDoesNotExist) {
+  using namespace cta;
+
+  common::dataStructures::VirtualOrganization vo;
+  vo.name = "vo";
+  vo.comment = "comment";
+  
+  ASSERT_NO_THROW(m_catalogue->createVirtualOrganization(m_admin,vo));
+  
+  ASSERT_THROW(m_catalogue->deleteVirtualOrganization("DOES_NOT_EXIST"),cta::exception::UserError);
+}
+
 } // namespace unitTests
diff --git a/catalogue/DummyCatalogue.hpp b/catalogue/DummyCatalogue.hpp
index c2cff5b265..204070ee42 100644
--- a/catalogue/DummyCatalogue.hpp
+++ b/catalogue/DummyCatalogue.hpp
@@ -89,6 +89,7 @@ public:
   void modifyAdminUserComment(const common::dataStructures::SecurityIdentity& admin, const std::string& username, const std::string& comment) override { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
   
   void createVirtualOrganization(const common::dataStructures::SecurityIdentity &admin, const common::dataStructures::VirtualOrganization &vo) override { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
+  void deleteVirtualOrganization(const std::string &voName) override { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
   void modifyArchiveRouteComment(const common::dataStructures::SecurityIdentity& admin, const std::string& storageClassName, const uint32_t copyNb, const std::string& comment) override { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
   void modifyArchiveRouteTapePoolName(const common::dataStructures::SecurityIdentity& admin, const std::string& storageClassName, const uint32_t copyNb, const std::string& tapePoolName) override { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
   void modifyLogicalLibraryName(const common::dataStructures::SecurityIdentity &admin, const std::string &currentName, const std::string &newName) override { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
diff --git a/catalogue/RdbmsCatalogue.cpp b/catalogue/RdbmsCatalogue.cpp
index 37f4cf8bad..e19a1eb3f4 100644
--- a/catalogue/RdbmsCatalogue.cpp
+++ b/catalogue/RdbmsCatalogue.cpp
@@ -395,6 +395,45 @@ void RdbmsCatalogue::createVirtualOrganization(const common::dataStructures::Sec
   }
 }
 
+//------------------------------------------------------------------------------
+// deleteVirtualOrganization
+//------------------------------------------------------------------------------
+void RdbmsCatalogue::deleteVirtualOrganization(const std::string &voName){
+  try {
+    auto conn = m_connPool.getConn();
+
+    if(virtualOrganizationIsUsedByStorageClasses(conn, voName)) {
+      throw UserSpecifiedStorageClassUsedByArchiveRoutes(std::string("The ") + voName +
+        " Virtual Organization is being used by one or more storage classes");
+    }
+
+    if(virtualOrganizationIsUsedByTapepools(conn, voName)) {
+      throw UserSpecifiedStorageClassUsedByArchiveFiles(std::string("The ") + voName +
+        " Virtual Organization is being used by one or more Tapepools");
+    }
+
+    const char *const sql =
+      "DELETE FROM "
+        "VIRTUAL_ORGANIZATION "
+      "WHERE "
+        "VIRTUAL_ORGANIZATION_NAME = :VIRTUAL_ORGANIZATION_NAME";
+    auto stmt = conn.createStmt(sql);
+
+    stmt.bindString(":VIRTUAL_ORGANIZATION_NAME", voName);
+
+    stmt.executeNonQuery();
+    if(0 == stmt.getNbAffectedRows()) {
+      throw exception::UserError(std::string("Cannot delete Virtual Organization : ") +
+        voName + " because it does not exist");
+    }
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
 //------------------------------------------------------------------------------
 // createStorageClass
 //------------------------------------------------------------------------------
@@ -600,6 +639,62 @@ bool RdbmsCatalogue::storageClassIsUsedByArchiveFiles(rdbms::Conn &conn, const s
   }
 }
 
+//------------------------------------------------------------------------------
+// virtualOrganizationIsUsedByStorageClasses
+//------------------------------------------------------------------------------
+bool RdbmsCatalogue::virtualOrganizationIsUsedByStorageClasses(rdbms::Conn &conn, const std::string &voName) const {
+  try {
+    const char *const sql =
+      "SELECT "
+        "VIRTUAL_ORGANIZATION_NAME AS VIRTUAL_ORGANIZATION_NAME "
+      "FROM "
+        "VIRTUAL_ORGANIZATION "
+      "INNER JOIN "
+        "STORAGE_CLASS "
+      "ON "
+        "VIRTUAL_ORGANIZATION.VIRTUAL_ORGANIZATION_ID = STORAGE_CLASS.VIRTUAL_ORGANIZATION_ID "
+      "WHERE "
+        "VIRTUAL_ORGANIZATION_NAME = :VIRTUAL_ORGANIZATION_NAME";
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":VIRTUAL_ORGANIZATION_NAME", voName);
+    auto rset = stmt.executeQuery();
+    return rset.next();
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+//------------------------------------------------------------------------------
+// virtualOrganizationIsUsedByTapepools
+//------------------------------------------------------------------------------
+bool RdbmsCatalogue::virtualOrganizationIsUsedByTapepools(rdbms::Conn &conn, const std::string &voName) const {
+  try {
+    const char *const sql =
+      "SELECT "
+        "VIRTUAL_ORGANIZATION_NAME AS VIRTUAL_ORGANIZATION_NAME "
+      "FROM "
+        "VIRTUAL_ORGANIZATION "
+      "INNER JOIN "
+        "TAPE_POOL "
+      "ON "
+        "VIRTUAL_ORGANIZATION.VIRTUAL_ORGANIZATION_ID = TAPE_POOL.VIRTUAL_ORGANIZATION_ID "
+      "WHERE "
+        "VIRTUAL_ORGANIZATION_NAME = :VIRTUAL_ORGANIZATION_NAME";
+    auto stmt = conn.createStmt(sql);
+    stmt.bindString(":VIRTUAL_ORGANIZATION_NAME", voName);
+    auto rset = stmt.executeQuery();
+    return rset.next();
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
 //------------------------------------------------------------------------------
 // getStorageClasses
 //------------------------------------------------------------------------------
diff --git a/catalogue/RdbmsCatalogue.hpp b/catalogue/RdbmsCatalogue.hpp
index 7fbbf3d241..26f0bd358e 100644
--- a/catalogue/RdbmsCatalogue.hpp
+++ b/catalogue/RdbmsCatalogue.hpp
@@ -218,6 +218,12 @@ public:
    * @param vo the Virtual Organization
    */
   void createVirtualOrganization(const common::dataStructures::SecurityIdentity &admin, const common::dataStructures::VirtualOrganization &vo) override;
+  
+  /**
+   * Deletes the specified Virtual Organization
+   * @param voName the name of the VirtualOrganization to delete
+   */
+  void deleteVirtualOrganization(const std::string &voName) override;
 
   /**
    * Creates the specified storage class.
@@ -1551,6 +1557,24 @@ protected:
    * @param storageClassName The name of the storage class.
    */
   bool storageClassIsUsedByArchiveFiles(rdbms::Conn &conn, const std::string &storageClassName) const;
+  
+  /**
+   * Returns true if the specified Virtual Organization is currently being used by one
+   * or more StorageClasses
+   *
+   * @param conn The database connection.
+   * @param voName The name of the Virtual Organization.
+   */
+  bool virtualOrganizationIsUsedByStorageClasses(rdbms::Conn &conn, const std::string &voName) const;
+  
+  /**
+   * Returns true if the specified Virtual Organization is currently being used by one
+   * or more Tapepools
+   *
+   * @param conn The database connection.
+   * @param voName The name of the Virtual Organization.
+   */
+  bool virtualOrganizationIsUsedByTapepools(rdbms::Conn &conn, const std::string &voName) const;
 
   /**
    * Returns the ID of the specified logical library or nullopt if the logical
diff --git a/catalogue/UserSpecifiedVirtualOrganizationUsedByStorageClasses.cpp b/catalogue/UserSpecifiedVirtualOrganizationUsedByStorageClasses.cpp
new file mode 100644
index 0000000000..8437c1c059
--- /dev/null
+++ b/catalogue/UserSpecifiedVirtualOrganizationUsedByStorageClasses.cpp
@@ -0,0 +1,39 @@
+/*
+ * The CERN Tape Archive (CTA) project
+ * Copyright (C) 2015  CERN
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "catalogue/UserSpecifiedVirtualOrganizationUsedByStorageClasses.hpp"
+
+namespace cta {
+namespace catalogue {
+
+
+//------------------------------------------------------------------------------
+// constructor
+//------------------------------------------------------------------------------
+UserSpecifiedVirtualOrganizationUsedByStorageClasses::UserSpecifiedVirtualOrganizationUsedByStorageClasses(const std::string &context,
+  const bool embedBacktrace): cta::exception::UserError(context, embedBacktrace) {
+}
+
+//------------------------------------------------------------------------------
+// destructor
+//------------------------------------------------------------------------------
+UserSpecifiedVirtualOrganizationUsedByStorageClasses::~UserSpecifiedVirtualOrganizationUsedByStorageClasses() {
+}
+
+} // namespace catalogue
+} // namespace cta
diff --git a/catalogue/UserSpecifiedVirtualOrganizationUsedByStorageClasses.hpp b/catalogue/UserSpecifiedVirtualOrganizationUsedByStorageClasses.hpp
new file mode 100644
index 0000000000..fb21065afc
--- /dev/null
+++ b/catalogue/UserSpecifiedVirtualOrganizationUsedByStorageClasses.hpp
@@ -0,0 +1,49 @@
+/*
+ * The CERN Tape Archive (CTA) project
+ * Copyright (C) 2015  CERN
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "common/exception/UserError.hpp"
+
+namespace cta {
+namespace catalogue {
+
+/**
+ * User specified a storage class which is currently being used by one or more
+ * archive files.
+ */
+class UserSpecifiedVirtualOrganizationUsedByStorageClasses: public exception::UserError {
+public:
+  /**
+   * Constructor.
+   *
+   * @param context optional context string added to the message
+   * at initialisation time.
+   * @param embedBacktrace whether to embed a backtrace of where the
+   * exception was throw in the message
+   */
+  UserSpecifiedVirtualOrganizationUsedByStorageClasses(const std::string &context = "", const bool embedBacktrace = true);
+
+  /**
+   * Destructor.
+   */
+  ~UserSpecifiedVirtualOrganizationUsedByStorageClasses() override;
+}; // class UserSpecifiedVirtualOrganizationUsedByStorageClasses
+
+} // namespace catalogue
+} // namespace cta
diff --git a/catalogue/UserSpecifiedVirtualOrganizationUsedByTapepools.cpp b/catalogue/UserSpecifiedVirtualOrganizationUsedByTapepools.cpp
new file mode 100644
index 0000000000..7c9d6264e6
--- /dev/null
+++ b/catalogue/UserSpecifiedVirtualOrganizationUsedByTapepools.cpp
@@ -0,0 +1,39 @@
+/*
+ * The CERN Tape Archive (CTA) project
+ * Copyright (C) 2015  CERN
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "catalogue/UserSpecifiedVirtualOrganizationUsedByTapepools.hpp"
+
+namespace cta {
+namespace catalogue {
+
+
+//------------------------------------------------------------------------------
+// constructor
+//------------------------------------------------------------------------------
+UserSpecifiedVirtualOrganizationUsedByTapepools::UserSpecifiedVirtualOrganizationUsedByTapepools(const std::string &context,
+  const bool embedBacktrace): cta::exception::UserError(context, embedBacktrace) {
+}
+
+//------------------------------------------------------------------------------
+// destructor
+//------------------------------------------------------------------------------
+UserSpecifiedVirtualOrganizationUsedByTapepools::~UserSpecifiedVirtualOrganizationUsedByTapepools() {
+}
+
+} // namespace catalogue
+} // namespace cta
diff --git a/catalogue/UserSpecifiedVirtualOrganizationUsedByTapepools.hpp b/catalogue/UserSpecifiedVirtualOrganizationUsedByTapepools.hpp
new file mode 100644
index 0000000000..47b5e69952
--- /dev/null
+++ b/catalogue/UserSpecifiedVirtualOrganizationUsedByTapepools.hpp
@@ -0,0 +1,49 @@
+/*
+ * The CERN Tape Archive (CTA) project
+ * Copyright (C) 2015  CERN
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "common/exception/UserError.hpp"
+
+namespace cta {
+namespace catalogue {
+
+/**
+ * User specified a storage class which is currently being used by one or more
+ * archive files.
+ */
+class UserSpecifiedVirtualOrganizationUsedByTapepools: public exception::UserError {
+public:
+  /**
+   * Constructor.
+   *
+   * @param context optional context string added to the message
+   * at initialisation time.
+   * @param embedBacktrace whether to embed a backtrace of where the
+   * exception was throw in the message
+   */
+  UserSpecifiedVirtualOrganizationUsedByTapepools(const std::string &context = "", const bool embedBacktrace = true);
+
+  /**
+   * Destructor.
+   */
+  ~UserSpecifiedVirtualOrganizationUsedByTapepools() override;
+}; // class UserSpecifiedVirtualOrganizationUsedByTapepools
+
+} // namespace catalogue
+} // namespace cta
-- 
GitLab