gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[libeufin] 02/02: native dbinit


From: gnunet
Subject: [libeufin] 02/02: native dbinit
Date: Sun, 24 Sep 2023 17:00:21 +0200

This is an automated email from the git hooks/post-receive script.

dold pushed a commit to branch master
in repository libeufin.

commit 58b39bd5f70c3edc3594b0571cd6b5575ed9512e
Author: Florian Dold <florian@dold.me>
AuthorDate: Sun Sep 24 17:00:22 2023 +0200

    native dbinit
---
 .../tech/libeufin/bank/CorebankApiHandlers.kt      |   6 --
 .../src/main/kotlin/tech/libeufin/bank/Database.kt |  72 +++++++++++++-
 bank/src/main/kotlin/tech/libeufin/bank/Main.kt    |  34 ++++++-
 bank/src/test/kotlin/Common.kt                     |  20 ++--
 bank/src/test/kotlin/TalerApiTest.kt               |   6 +-
 contrib/libeufin-bank-dbinit                       | 107 ---------------------
 contrib/libeufin-bank.conf                         |   2 +-
 util/src/main/kotlin/TalerConfig.kt                |  14 ++-
 util/src/test/kotlin/TalerConfigTest.kt            |   1 +
 9 files changed, 128 insertions(+), 134 deletions(-)

diff --git a/bank/src/main/kotlin/tech/libeufin/bank/CorebankApiHandlers.kt 
b/bank/src/main/kotlin/tech/libeufin/bank/CorebankApiHandlers.kt
index bfae12c4..e1939073 100644
--- a/bank/src/main/kotlin/tech/libeufin/bank/CorebankApiHandlers.kt
+++ b/bank/src/main/kotlin/tech/libeufin/bank/CorebankApiHandlers.kt
@@ -272,9 +272,6 @@ fun Routing.accountsMgmtHandlers(db: Database, ctx: 
BankApplicationContext) {
     }
 
     post("/withdrawals/{withdrawal_id}/abort") {
-        val c = call.authenticateBankRequest(db, TokenScope.readonly) ?: throw 
unauthorized()
-        // Admin allowed to abort.
-        if (!call.getResourceName("USERNAME").canI(c)) throw forbidden()
         val op = getWithdrawal(db, call.expectUriComponent("withdrawal_id"))
         // Idempotency:
         if (op.aborted) {
@@ -290,9 +287,6 @@ fun Routing.accountsMgmtHandlers(db: Database, ctx: 
BankApplicationContext) {
     }
 
     post("/withdrawals/{withdrawal_id}/confirm") {
-        val c = call.authenticateBankRequest(db, TokenScope.readwrite) ?: 
throw unauthorized()
-        // No admin allowed.
-        if (!call.getResourceName("USERNAME").canI(c, withAdmin = false)) 
throw forbidden()
         val op = getWithdrawal(db, call.expectUriComponent("withdrawal_id"))
         // Checking idempotency:
         if (op.confirmationDone) {
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/Database.kt 
b/bank/src/main/kotlin/tech/libeufin/bank/Database.kt
index 8bcb5e56..44d0b61a 100644
--- a/bank/src/main/kotlin/tech/libeufin/bank/Database.kt
+++ b/bank/src/main/kotlin/tech/libeufin/bank/Database.kt
@@ -24,9 +24,10 @@ import org.postgresql.jdbc.PgConnection
 import org.slf4j.Logger
 import org.slf4j.LoggerFactory
 import tech.libeufin.util.getJdbcConnectionFromPg
-import java.net.URI
+import java.io.File
 import java.sql.DriverManager
 import java.sql.PreparedStatement
+import java.sql.ResultSet
 import java.sql.SQLException
 import java.util.*
 import kotlin.math.abs
@@ -41,6 +42,75 @@ fun BankAccountTransaction.expectRowId(): Long = 
this.dbRowId ?: throw internalS
 
 private val logger: Logger = 
LoggerFactory.getLogger("tech.libeufin.bank.Database")
 
+fun initializeDatabaseTables(dbConfig: String, sqlDir: String) {
+    logger.info("doing DB initialization, sqldir $sqlDir, dbConfig $dbConfig")
+    val jdbcConnStr = getJdbcConnectionFromPg(dbConfig)
+    logger.info("connecting to database via JDBC string '$jdbcConnStr'")
+    val dbConn = 
DriverManager.getConnection(jdbcConnStr).unwrap(PgConnection::class.java)
+    if (dbConn == null) {
+        throw Error("could not open database")
+    }
+    val sqlVersioning = File("$sqlDir/versioning.sql").readText()
+    dbConn.execSQLUpdate(sqlVersioning)
+
+    val checkStmt = dbConn.prepareStatement("SELECT count(*) as n FROM 
_v.patches where patch_name = ?")
+
+    for (n in 1..9999) {
+        val numStr = n.toString().padStart(4, '0')
+        val patchName = "libeufin-bank-$numStr"
+
+        checkStmt.setString(1, patchName)
+        val res = checkStmt.executeQuery()
+        if (!res.next()) {
+            throw Error("unable to query patches")
+        }
+
+        val patchCount = res.getInt("n")
+        if (patchCount >= 1) {
+            logger.info("patch $patchName already applied")
+            continue
+        }
+
+        val path = File("$sqlDir/libeufin-bank-$numStr.sql")
+        if (!path.exists()) {
+            logger.info("path $path doesn't exist anymore, stopping")
+            break
+        }
+        logger.info("applying patch $path")
+        val sqlPatchText = path.readText()
+        dbConn.execSQLUpdate(sqlPatchText)
+    }
+    val sqlProcedures = File("$sqlDir/procedures.sql").readText()
+    dbConn.execSQLUpdate(sqlProcedures)
+}
+
+private fun countRows(rs: ResultSet): Int {
+    var size = 0
+    while (rs.next()) {
+        size++
+    }
+    return size
+}
+
+fun resetDatabaseTables(dbConfig: String, sqlDir: String) {
+    logger.info("doing DB initialization, sqldir $sqlDir, dbConfig $dbConfig")
+    val jdbcConnStr = getJdbcConnectionFromPg(dbConfig)
+    logger.info("connecting to database via JDBC string '$jdbcConnStr'")
+    val dbConn = 
DriverManager.getConnection(jdbcConnStr).unwrap(PgConnection::class.java)
+    if (dbConn == null) {
+        throw Error("could not open database")
+    }
+
+    val queryRes = dbConn.execSQLQuery("SELECT schema_name FROM 
information_schema.schemata WHERE schema_name='_v'")
+    if (countRows(queryRes) == 0) {
+        logger.info("versioning schema not present, not running drop sql")
+        return
+    }
+
+    val sqlDrop = File("$sqlDir/libeufin-bank-drop.sql").readText()
+    dbConn.execSQLUpdate(sqlDrop)
+}
+
 class Database(private val dbConfig: String, private val bankCurrency: String) 
{
     private var dbConn: PgConnection? = null
     private var dbCtr: Int = 0
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/Main.kt 
b/bank/src/main/kotlin/tech/libeufin/bank/Main.kt
index cb4bb7f9..025ff5e8 100644
--- a/bank/src/main/kotlin/tech/libeufin/bank/Main.kt
+++ b/bank/src/main/kotlin/tech/libeufin/bank/Main.kt
@@ -287,6 +287,7 @@ fun Application.corebankWebApp(db: Database, ctx: 
BankApplicationContext) {
 class LibeufinBankCommand : CliktCommand() {
     init {
         versionOption(getVersion())
+        subcommands(ServeBank(), BankDbInit())
     }
 
     override fun run() = Unit
@@ -374,6 +375,35 @@ fun readBankApplicationContextFromConfig(cfg: 
TalerConfig): BankApplicationConte
     )
 }
 
+
+class BankDbInit : CliktCommand("Initialize the libeufin-bank database", name 
= "dbinit") {
+    private val configFile by option(
+        "--config", "-c",
+        help = "set the configuration file"
+    )
+
+    private val requestReset by option(
+        "--reset", "-r",
+        help = "reset database (DANGEROUS: All existing data is lost)"
+    ).flag()
+
+    init {
+        context {
+            helpFormatter = CliktHelpFormatter(showDefaultValues = true)
+        }
+    }
+
+    override fun run() {
+        val config = TalerConfig.load(this.configFile)
+        val dbConnStr = config.requireValueString("libeufin-bankdb", "config")
+        val sqlDir = config.requireValuePath("libeufin-bankdb-postgres", 
"sql_dir")
+        if (requestReset) {
+            resetDatabaseTables(dbConnStr, sqlDir)
+        }
+        initializeDatabaseTables(dbConnStr, sqlDir)
+    }
+}
+
 class ServeBank : CliktCommand("Run libeufin-bank HTTP server", name = 
"serve") {
     private val configFile by option(
         "--config", "-c",
@@ -388,7 +418,7 @@ class ServeBank : CliktCommand("Run libeufin-bank HTTP 
server", name = "serve")
     override fun run() {
         val config = TalerConfig.load(this.configFile)
         val ctx = readBankApplicationContextFromConfig(config)
-        val dbConnStr = config.requireValueString("libeufin-bank-db-postgres", 
"config")
+        val dbConnStr = config.requireValueString("libeufin-bankdb", "config")
         logger.info("using database '$dbConnStr'")
         val serveMethod = config.requireValueString("libeufin-bank", "serve")
         if (serveMethod.lowercase() != "tcp") {
@@ -407,5 +437,5 @@ class ServeBank : CliktCommand("Run libeufin-bank HTTP 
server", name = "serve")
 }
 
 fun main(args: Array<String>) {
-    LibeufinBankCommand().subcommands(ServeBank()).main(args)
+    LibeufinBankCommand().main(args)
 }
diff --git a/bank/src/test/kotlin/Common.kt b/bank/src/test/kotlin/Common.kt
index 8206c93d..01a36985 100644
--- a/bank/src/test/kotlin/Common.kt
+++ b/bank/src/test/kotlin/Common.kt
@@ -17,25 +17,19 @@
  * <http://www.gnu.org/licenses/>
  */
 
-import tech.libeufin.bank.BankApplicationContext
-import tech.libeufin.bank.Database
-import tech.libeufin.bank.TalerAmount
+import tech.libeufin.bank.*
 import tech.libeufin.util.execCommand
 
 /**
  * Init the database and sets the currency to KUDOS.
  */
 fun initDb(): Database {
-    execCommand(
-        listOf(
-            "libeufin-bank-dbinit",
-            "-d",
-            "libeufincheck",
-            "-r"
-        ),
-        throwIfFails = true
-    )
-    return Database("postgresql:///libeufincheck", "KUDOS")
+    val config = TalerConfig.load()
+    val sqlPath = config.requireValuePath("libeufin-bankdb-postgres", 
"SQL_DIR")
+    val dbConnStr = "postgresql:///libeufincheck"
+    resetDatabaseTables(dbConnStr, sqlPath)
+    initializeDatabaseTables(dbConnStr, sqlPath)
+    return Database(dbConnStr, "KUDOS")
 }
 
 fun getTestContext(
diff --git a/bank/src/test/kotlin/TalerApiTest.kt 
b/bank/src/test/kotlin/TalerApiTest.kt
index 64a33bac..e76c367d 100644
--- a/bank/src/test/kotlin/TalerApiTest.kt
+++ b/bank/src/test/kotlin/TalerApiTest.kt
@@ -262,7 +262,7 @@ class TalerApiTest {
             application {
                 corebankWebApp(db, ctx)
             }
-            client.post("/accounts/foo/withdrawals/${uuid}/abort") {
+            client.post("/withdrawals/${uuid}/abort") {
                 expectSuccess = true
                 basicAuth("foo", "pw")
             }
@@ -292,7 +292,7 @@ class TalerApiTest {
             }
             val opId = 
Json.decodeFromString<BankAccountCreateWithdrawalResponse>(r.bodyAsText())
             // Getting the withdrawal from the bank.  Throws (failing the 
test) if not found.
-            client.get("/accounts/foo/withdrawals/${opId.withdrawal_id}") {
+            client.get("/withdrawals/${opId.withdrawal_id}") {
                 expectSuccess = true
                 basicAuth("foo", "pw")
             }
@@ -328,7 +328,7 @@ class TalerApiTest {
             application {
                 corebankWebApp(db, ctx)
             }
-            client.post("/accounts/foo/withdrawals/${uuid}/confirm") {
+            client.post("/withdrawals/${uuid}/confirm") {
                 expectSuccess = true // Sufficient to assert on success.
                 basicAuth("foo", "pw")
             }
diff --git a/contrib/libeufin-bank-dbinit b/contrib/libeufin-bank-dbinit
deleted file mode 100755
index 8425070c..00000000
--- a/contrib/libeufin-bank-dbinit
+++ /dev/null
@@ -1,107 +0,0 @@
-#!/bin/bash
-
-set -eu
-
-# The only CLI argument is 'nexus' or 'sandbox',
-# indicating which service will get its database prepared.
-
-fail () {
-  echo $1
-  exit 1
-}
-
-usage_and_exit () {
-  echo Usage: libeufin-bank-dbinit OPTIONS
-  echo
-  echo By default, this command creates and/or patches the Sandbox tables.
-  echo Pass '-r' to drop the tables before creating them again.
-  echo
-  echo 'Supported options:'
-  echo '  -d DB_CONN  -- required.  Pass DB_CONN as the postgres connection 
string.  Passed verbatim to Psql'
-  echo '  -l LOC      -- required.  Pass LOC as the SQL files location.  
Typically $prefix/share/libeufin/sql/bank'
-  echo '  -h           -- print this help'
-  echo '  -r           -- drop all the tables before creating them again'
-  exit 0
-}
-
-run_sql_file () {
-  # -q doesn't hide all the output, hence the
-  # redirection to /dev/null.
-  psql -d $DB_CONNECTION \
-    -q \
-    -f $1 \
-    --set ON_ERROR_STOP=1 > /dev/null
-}
-
-get_patch_path () {
-  echo "$PATCHES_LOCATION/$1"
-}
-
-# The real check happens (by the caller)
-# by checking the returned text.
-check_patch_applied () {
-  psql -d $DB_CONNECTION \
-  -t \
-  -c "SELECT applied_by FROM _v.patches WHERE patch_name = '$1' LIMIT 1"
-}
-
-# Iterates over the .sql migration files and applies
-# the new ones.
-iterate_over_patches () {
-  cd $PATCHES_LOCATION
-  for patch_filename in $(ls -1 -v libeufin-bank-[0-9][0-9][0-9][0-9].sql); do
-    patch_name=$(echo $patch_filename | cut -f1 -d.) # drops the final .sql
-    echo Checking patch: "$patch_name"
-    maybe_applied=$(check_patch_applied "$patch_name")
-    if test -n "$maybe_applied"; then continue; fi
-    # patch not applied, apply it.
-    echo Patch $patch_name not applied, applying it.
-    run_sql_file $patch_filename
-  done
-  cd - > /dev/null # cd to previous location.
-}
-
-if test $# -eq 0; then
-  usage_and_exit
-fi
-
-while getopts ":d:l:hr" OPTION; do
-  case "$OPTION" in 
-    d)
-      DB_CONNECTION="$OPTARG" # only one required.
-      ;;
-    l)
-      PATCHES_LOCATION="$OPTARG"
-      ;;
-    r)
-      DROP="YES"
-      ;;
-    h)
-      usage_and_exit
-      ;;
-    ?)
-      fail 'Unrecognized command line option'
-    ;;
-  esac
-done
-
-# Checking required options.
-if test -z "${PATCHES_LOCATION:-}"; then
-  # This value is substituted by GNU make at installation time.
-  PATCHES_LOCATION=__BANK_STATIC_PATCHES_LOCATION__
-fi
-if test -z "${DB_CONNECTION:-}"; then
-  fail "Required option '-d' was missing."
-fi
-
-run_sql_file $(get_patch_path "versioning.sql")
-if test "${DROP:-}" = "YES"; then
-  maybe_applied=$(check_patch_applied "libeufin-bank-0001")
-  if test -n "$maybe_applied"; then 
-    run_sql_file $(get_patch_path "libeufin-bank-drop.sql")
-  else
-    echo "Nothing to drop"
-  fi
-fi
-iterate_over_patches
-run_sql_file $(get_patch_path "procedures.sql")
diff --git a/contrib/libeufin-bank.conf b/contrib/libeufin-bank.conf
index be718d9e..04be761f 100644
--- a/contrib/libeufin-bank.conf
+++ b/contrib/libeufin-bank.conf
@@ -14,4 +14,4 @@ CONFIG = postgresql:///libeufinbank
 
 [libeufin-bankdb-postgres]
 # Where are the SQL files to setup our tables?
-SQL_DIR = $DATADIR/sql/bank/
+SQL_DIR = $DATADIR/sql/libeufin-bank/
diff --git a/util/src/main/kotlin/TalerConfig.kt 
b/util/src/main/kotlin/TalerConfig.kt
index ce98c7f0..a0f5bf91 100644
--- a/util/src/main/kotlin/TalerConfig.kt
+++ b/util/src/main/kotlin/TalerConfig.kt
@@ -170,6 +170,14 @@ class TalerConfig {
         return pathsub(entry.value)
     }
 
+    fun requireValuePath(section: String, option: String): String {
+        val res = lookupValuePath(section, option)
+        if (res == null) {
+            throw TalerConfigError("expected path for section $section option 
$option")
+        }
+        return res
+    }
+
     /**
      * Create a string representation of the loaded configuration.
      */
@@ -314,10 +322,14 @@ class TalerConfig {
         }
 
         fun getTalerInstallPath(): String {
+            return getInstallPathFromBinary("taler-config")
+        }
+
+        fun getInstallPathFromBinary(name: String): String {
             val pathEnv = System.getenv("PATH")
             val paths = pathEnv.split(":")
             for (p in paths) {
-                val possiblePath = Paths.get(p, "taler-config").toString()
+                val possiblePath = Paths.get(p, name).toString()
                 if (File(possiblePath).exists()) {
                     return Paths.get(p, "..").toRealPath().toString()
                 }
diff --git a/util/src/test/kotlin/TalerConfigTest.kt 
b/util/src/test/kotlin/TalerConfigTest.kt
index f5d0dd50..4587c535 100644
--- a/util/src/test/kotlin/TalerConfigTest.kt
+++ b/util/src/test/kotlin/TalerConfigTest.kt
@@ -18,6 +18,7 @@
  */
 
 import org.junit.Test
+import java.nio.file.FileSystems
 import kotlin.test.assertEquals
 
 class TalerConfigTest {

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]