Commit de7d41d4 by Robbie Hott

Created temporary ark generation, tests, CI scripts, and updated config

parent 91ffda54
Pipeline #683 passed with stage
in 3 minutes 11 seconds
......@@ -2,4 +2,3 @@ composer.lock
composer.phar
vendor/
.idea/
src/ark/Config.php
before_script:
- composer -n -q install
stages:
- test
run_test:
script:
- vendor/bin/phpunit --coverage-text --colors=never
stage: test
......@@ -4,4 +4,4 @@ Manager for temporary and permanent ARKs. The ArkManager facilitates creation o
## Install Procedures
You can use Composer to automatically install this package. However, it does have some configuration dependencies. Copy `Config_dist.php` to `Config.php` and fill in the required configuration paramaters for your environment. Likewise, if you need to set up your own environment, run the `install.php` script found in the `install` directory.
You can use Composer to automatically install this package. However, it does have some configuration dependencies. Update `Config.php` to fill in the required configuration paramaters for your environment. Likewise, if you need to set up your own environment, run the `install.php` script found in the `install` directory.
<?php
/**
* Full Install Script
*
* @author Robbie Hott
* @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause
* @copyright 2015 the Rector and Visitors of the University of Virginia, and
* the Regents of the University of California
*/
// Move to the directory containing this script
chdir(dirname(__FILE__));
// Autoload the SNAC server codebase
include("../vendor/autoload.php");
use \ark\Config as Config;
$automate = false;
if ($argc == 2 && $argv[1] == "automate")
$automate = true;
$host = Config::$DATABASE["host"];
$port = Config::$DATABASE["port"];
$database = Config::$DATABASE["database"];
$password = Config::$DATABASE["password"];
$user = Config::$DATABASE["user"];
// Try to create the database
echo "Would you like to try creating the PostgreSQL database?\n ('yes' or 'no'): ";
$response = "no";
if (!$automate)
$response = trim(fgets(STDIN));
else
echo "no\n";
if ($response == "yes") {
echo " Trying to create the database. This script requires SUDO\n".
" privileges to switch to the postgres user to create the \n".
" database. Enter the password for SUDO next:\n";
$retval = 0;
// Run a system shell command, that sudos bash, then su's to postgres user,
// then creates the user and database from the Config class.
system("
sudo bash -c \"
su postgres -c '
createuser -D -l -R -P $user <<EOF
$password
$password
EOF
psql <<EOF
create database $database;
grant create,connect on database $database to $user;
EOF'
\"\n", $retval);
if ($retval != 0) {
echo " There was a problem creating the database. Use the\n".
" following commands to create the database:\n\n".
" As the postgres user, in a shell:\n".
" postgres@server$ createuser -D -l -R -P $user\n\n".
" In the postgres shell as the root pgsql user:\n".
" psql> create database $database;\n".
" psql> grant create,connect on database $database to $user;\n\n";
}
} else {
echo " Not creating the database.\n\n";
}
// Try to connect to the database
echo "Attempting to make a database connection.\n\n";
$dbHandle = pg_connect("host=$host port=$port dbname=$database user=$user password=$password");
// If the connection does not throw an exception, but the connector is false, then throw.
if ($dbHandle === false) {
die("ERR: Unable to connect to database.\n");
}
echo "Would you like to load the schema into the database?\n ('yes' or 'no'): ";
$response = "no";
if (!$automate)
$response = trim(fgets(STDIN));
else
echo "no\n";
if ($response == "yes") {
echo " Running the SQL initialization script\n";
$res = pg_query($dbHandle, file_get_contents("sql_files/schema.sql"));
if (!$res) {
$error = pg_last_error($dbHandle);
echo " ERR: Unable to run script due to the following error:\n";
echo $error."\n";
die();
}
echo " Successfully loaded the schema.\n";
} else {
echo " Not loading the schema. The schema can be found in sql_files/schema.sql.\n\n";
}
-- Sequence for numeric IDs (really just a count)
CREATE SEQUENCE "id_seq";
-- Table to hold the unassigned arks
create table unassigned_arks (
id int default nextval('id_seq'), -- Integer ID for the database
ark text,
timestamp timestamp default now(),
primary key (id)
);
create unique index unassigned_arks_idx1 on unassigned_arks (ark);
<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="test/bootstrap.php">
<testsuites>
<testsuite name="ARK_tests">
<directory>test</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory suffix=".php">src</directory>
<exclude>
<directory>src/snac/Config.php</directory>
</exclude>
</whitelist>
</filter>
</phpunit>
......@@ -33,7 +33,7 @@ class ArkManager {
* Sets up the ArkManager
*/
public function __construct() {
$db = new \ark\database\DatabaseConnector();
}
/**
......@@ -44,10 +44,25 @@ class ArkManager {
* to resolve.
*
* @param integer $num optional The number of arks to generate. If more than one, this method will return an array
* @return string|string[] A temporary ark or a list of temporary arks
* @return string|string[]|null A temporary ark or a list of temporary arks
*/
public function mintTemporaryArk($num=1) {
if (!is_numeric($num) || $num <= 0)
return null;
// Set up minting factory
$prefix = Config::$ARK_URL . "/ark:/" . Config::$TEMPORARY_INSTITUTION_CODE . "/Z";
if ($num == 1)
return $prefix . $this->generateRandomString();
$arks = array();
for ($i = 0; $i < $num; $i++) {
// Store a temporary ark
$ark = $prefix . $this->generateRandomString();
array_push($arks, $ark);
}
return $arks;
}
/**
......@@ -62,4 +77,15 @@ class ArkManager {
}
private function generateRandomString() {
$length = 7;
$characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$cLength = strlen($characters);
$string = '';
for ($i = 0; $i < $length; $i++) {
$string .= $characters[random_int(0, $cLength - 1)];
}
return $string;
}
}
......@@ -35,12 +35,22 @@ class Config {
* @var array database connection information
*/
public static $DATABASE = array (
"database" => "db_name",
"host" => "hostname.com",
"database" => "ark_manager",
"host" => "localhost",
"port" => 5432,
"user" => "user_id",
"password" => "full_password"
"user" => "ark_manager",
"password" => "arkarkark"
);
/**
* @var string Base URL for Arks
*/
public static $ARK_URL = "http://snaccooperative.org";
/**
* @var string Temporary Institution Code for temporary Arks
*/
public static $TEMPORARY_INSTITUTION_CODE = "99999";
}
<?php
/**
* Database Connector Class File
*
* Contains the thin-layer database connector that handles exceptions
*
* @author Robbie Hott
* @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause
* @copyright 2015 the Rector and Visitors of the University of Virginia, and
* the Regents of the University of California
*/
namespace ark\database;
use \ark\Config as Config;
/**
* Database Connector Class
*
* This class provides a thin layer in front of the standard PHP Postgres library functions, so that
* correct error handling may happen throughout the code. *
* @author Robbie Hott
*
*/
class DatabaseConnector {
/**
* @var \resource Database handle for postgres connection
*/
private $dbHandle = null;
/**
* Convert php boolean to Postgres
*
* pg_execute() doesn't know to convert boolean to 't' and 'f' as required by Postgres. We can do it
* ourselves. Interestingly, the return value is a single character string containing t or f. In SQL it
* would be literally 't'.
*
* Treat string 'true' as true, and 'false' as false just in case.
*
* @param string $arg A php boolean of whatever type as long as it will test true or false.
*
* @return string Either 't or 'f', for use as boolean in Postgres SQL.
*/
public static function boolToPg($arg) {
if ($arg === true || $arg == 'true') {
return 't';
} else {
return 'f';
}
}
/**
* Convert Postgres boolean to php
*
* pg_execute() doesn't know to convert boolean to 't' and 'f' from Postgres to php true and false. We do
* it ourselves.
*
* This is for Postgres boolean types which may only have values 't' or 'f'. Any other value is an error.
*
* @param string $arg A php boolean of whatever type as long as it will test true or false.
*
* @return boolean Return php true or false.
*/
public static function pgToBool($arg) {
if ($arg == 't') {
return true;
} else {
return false;
}
}
/**
* Constructor
*
* Opens the connection to the database on construct
*/
public function __construct() {
// Read the configuration file
$host = Config::$DATABASE["host"];
$port = Config::$DATABASE["port"];
$database = Config::$DATABASE["database"];
$password = Config::$DATABASE["password"];
$user = Config::$DATABASE["user"];
// Try to connect to the database
$this->dbHandle = \pg_connect("host=$host port=$port dbname=$database user=$user password=$password");
// If the connection does not throw an exception, but the connector is false, then throw.
if ($this->dbHandle === false) {
throw new \Exception("Unable to connect to back-end database.");
}
}
/**
* Prepare A Statement
*
* Calls php postgres pg_prepare method. The statement should be named, and the query given.
*
* @param string $statementName Name for the statement (allows multiple prepares)
* @param string $query Query to prepare (with $1, $2, .. placeholders)
*/
public function prepare($statementName, $query) {
$result = \pg_prepare($this->dbHandle, $statementName, $query);
// Check for error
if ($result === false) {
$errorMessage = \pg_last_error($this->dbHandle);
throw new \Exception("Database Prepare Error: " . $errorMessage);
}
}
/**
* Execute a prepared database statement
*
* Executes the statement prepared earlier as $statementName, with the given array of values used to fill the
* placeholders in the prepared statement. Any values passed in the array will be converted to strings.
*
* @param string $statementName Statement name to execute
* @param mixed[] $values Parameters to fill the prepared statement (will be cast to string)
* @return \resource Postgres resource for the result
*/
public function execute($statementName, $values) {
$result = \pg_execute($this->dbHandle, $statementName, $values);
// Check for error
if ($result === false) {
$errorMessage = \pg_last_error($this->dbHandle);
throw new \Exception("Database Execute Error: " . $errorMessage);
}
$resultError = \pg_result_error($result);
if ($resultError === false) {
throw new \Exception("Database Execute Error: Could not return results -- malformed result");
} else if (!empty($resultError)) {
throw new \Exception("Database Execute Error: " . $resultError);
}
return $result;
}
/**
* Prepare and Execute a database statement. From the php docs on prepare(): [The first argument] stmtname
* may be "" to create an unnamed statement, in which case any pre-existing unnamed statement is
* automatically replaced; otherwise it is an error if the statement name is already defined in the
* current session.
*
* Handles both the prepare and execute stages.
*
* @param string $query Query to prepare (with $1, $2, .. placeholders)
* @param mixed[] $values Parameters to fill the prepared statement (will be cast to string)
* @return \resource Postgres resource for the result
*/
public function query($query, $values) {
$this->prepare("", $query);
return $this->execute("", $values);
}
/**
* Need to add some docs and perhaps throw an exception if the query exists and can't be deallocated. If
* the query doesn't exist we don't particularly care.
*
* @param string $query The name of the query to deallocate. Query names are lower case strings. Things
* break with mixed case.
*/
public function deallocate($query) {
pg_query($this->dbHandle, "deallocate $query");
}
/**
* Fetch the next row
*
* Fetches the next row from the given resource and returns it as an associative array.
*
* @param \resource $resource Postgres result resource (From $db->execute())
* @return string[] Next row from the database as an associative array, or false if no rows to return
*/
public function fetchRow($resource) {
$row = \pg_fetch_assoc($resource);
return $row;
}
}
<?php
/**
* Ark Manager Test File
*
* @author Robbie Hott
* @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause
* @copyright 2015 the Rector and Visitors of the University of Virginia, and
* the Regents of the University of California
*/
namespace test\ark;
use \ark\ArkManager;
/**
* Ark Manager Test Class
*/
class ArkManagerTest extends \PHPUnit_Framework_TestCase {
public $am = null;
public function setUp() {
$this->am = new ArkManager();
}
public function testBadTemporaryArkCount() {
$this->assertNull($this->am->mintTemporaryArk(null));
$this->assertNull($this->am->mintTemporaryArk(-1));
$this->assertNull($this->am->mintTemporaryArk('one'));
$this->assertNull($this->am->mintTemporaryArk(0));
}
public function testOneTemporaryArk() {
$ark = $this->am->mintTemporaryArk();
$this->assertNotNull($ark, "Minting should have returned one ark");
$this->assertFalse(is_array($ark), "One Ark should be given as a string");
$ark = $this->am->mintTemporaryArk(1);
$this->assertNotNull($ark, "Minting should have returned one ark");
$this->assertFalse(is_array($ark), "One Ark should be given as a string");
}
public function testManyTemporaryArk() {
$arks = $this->am->mintTemporaryArk(5);
$this->assertNotNull($arks, "Minting should have returned array of ark");
$this->assertTrue(is_array($arks), "Arks should be given as an array of strings");
$this->assertEquals(5, count($arks), "We asked for 5, but were given " . count($arks));
$arks = $this->am->mintTemporaryArk(25);
$this->assertNotNull($arks, "Minting should have returned array of ark");
$this->assertTrue(is_array($arks), "Arks should be given as an array of strings");
$this->assertEquals(25, count($arks), "We asked for 25, but were given " . count($arks));
}
public function testTemporaryArkRandomness() {
$arks = $this->am->mintTemporaryArk(25000);
$this->assertNotNull($arks, "Minting should have returned array of ark");
$this->assertTrue(is_array($arks), "Arks should be given as an array of strings");
$this->assertEquals(25000, count($arks), "We asked for 25000, but were given " . count($arks));
for ($i = 0; $i < 1000; $i++) {
$ark = $this->am->mintTemporaryArk();
$this->assertNotContains($ark, $arks, "Ark was found to repeat: " . $ark);
}
}
}
<?php
/**
* PHPUnit Bootstrap Loader File
*
* Loads all dependencies of the tests using Composer's autoloader, then
* create the log file approprate for the tests.
*
* @author Robbie Hott
* @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause
* @copyright 2015 the Rector and Visitors of the University of Virginia, and
* the Regents of the University of California
*/
/**
* Load the dependencies
*/
include ("vendor/autoload.php");
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment