Browse Source

Add database links support into `DBAPI`

pull/8/head
Anton Tarasenko 3 years ago
parent
commit
0aa3ba9fb2
  1. 63
      sources/Data/Database/DBAPI.uc
  2. 8
      sources/Data/Database/Local/LocalDatabase.uc
  3. 38
      sources/Data/Database/Local/LocalDatabaseInstance.uc
  4. 47
      sources/Data/Database/Tests/TEST_DatabaseCommon.uc
  5. 9
      sources/Manifest.uc

63
sources/Data/Database/DBAPI.uc

@ -34,6 +34,69 @@ private final function CreateLocalDBMapIfMissing()
} }
} }
/**
* Loads database based on the link.
*
* Links have the form of "<type>:<db_name>", followed by the JSON pointer
* (possibly empty one) to the object inside it. "<type>" can be either "local"
* or "remote" and "<db_name>" refers to the database that we are expected
* to load. This name has to consist of numbers and latin letters only.
*
* @param databaseLink Link from which to extract database's name.
* @return Database named "<db_name>" of type "<type>" from the `databaseLink`.
*/
public final function Database Load(Text databaseLink)
{
local Parser parser;
local Database result;
local MutableText databaseName;
if (databaseLink == none) {
return none;
}
parser = _.text.Parse(databaseLink);
// Only local DBs are supported for now!
parser.Match(P("local:"));
if (!parser.Ok())
{
parser.FreeSelf();
return none;
}
parser.MUntil(databaseName, _.text.GetCharacter("/"));
result = LoadLocal(databaseName);
databaseName.FreeSelf();
return result;
}
/**
* Extracts `JSONPointer` from the database path, given by `databaseLink`.
*
* Links have the form of "<type>:<db_name>", followed by the JSON pointer
* (possibly empty one) to the object inside it. "<type>" can be either "local"
* or "remote" and "<db_name>" refers to the database that we are to use to
* get the data, specified in the link.
* This method returns `JSONPointer` that comes after type-name pair.
*
* @param Link from which to extract `JSONPointer`.
* @return `JSONPointer` from the database link.
*/
public final function JSONPointer GetPointer(Text databaseLink)
{
local int slashIndex;
local Text textPointer;
local JSONPointer result;
if (databaseLink == none) {
return none;
}
slashIndex = databaseLink.IndexOf(P("/"));
if (slashIndex < 0) {
return JSONPointer(_.memory.Allocate(class'JSONPointer'));
}
textPointer = databaseLink.Copy(slashIndex);
result = _.json.Pointer(textPointer);
textPointer.FreeSelf();
return result;
}
/** /**
* Creates new local database with name `databaseName`. * Creates new local database with name `databaseName`.
* *

8
sources/Data/Database/Local/LocalDatabase.uc

@ -26,6 +26,7 @@ class LocalDatabase extends AcediaObject
config(AcediaDB); config(AcediaDB);
var config private string root; var config private string root;
var config private bool enforceAcediaStructure;
public final function Text GetPackageName() public final function Text GetPackageName()
{ {
@ -42,6 +43,12 @@ public final function Text GetRootName()
return __().text.FromString(root); return __().text.FromString(root);
} }
public final function bool RequiresAcediaStructure()
{
return enforceAcediaStructure;
}
/** /**
* Changes caller's root name. * Changes caller's root name.
* *
@ -97,4 +104,5 @@ public final function DeleteSelf()
defaultproperties defaultproperties
{ {
enforceAcediaStructure = false
} }

38
sources/Data/Database/Local/LocalDatabaseInstance.uc

@ -86,6 +86,8 @@ var private DBTask lastTask;
// Remember task's life version to make sure we still have the correct copy // Remember task's life version to make sure we still have the correct copy
var private int lastTaskLifeVersion; var private int lastTaskLifeVersion;
var private LoggerAPI.Definition warnDataErasedForAcediaStructure;
protected function Constructor() protected function Constructor()
{ {
_.unreal.OnTick(self).connect = CompleteAllTasks; _.unreal.OnTick(self).connect = CompleteAllTasks;
@ -334,11 +336,40 @@ public function DBIncrementTask IncrementData(
*/ */
public final function Initialize(LocalDatabase config, DBRecord root) public final function Initialize(LocalDatabase config, DBRecord root)
{ {
if (configEntry != none) { if (configEntry != none) return;
return; if (config == none) return;
}
configEntry = config; configEntry = config;
rootRecord = root; rootRecord = root;
if (config.RequiresAcediaStructure()) {
CreateAcediaStructure(config);
}
}
// Create JSON object at "/users" if something else (or nothing)
// is stored there
private final function CreateAcediaStructure(LocalDatabase config)
{
local DataType usersType;
local JSONPointer usersPointer;
local AssociativeArray emptyObject;
if (config == none) return;
if (rootRecord == none) return;
usersPointer = _.json.Pointer(P("/users"));
usersType = rootRecord.GetObjectType(usersPointer);
if (usersType != JSON_Object)
{
emptyObject = _.collections.EmptyAssociativeArray();
rootRecord.SaveObject(usersPointer, emptyObject);
emptyObject.FreeSelf();
if (usersType != JSON_Undefined)
{
_.logger.Auto(warnDataErasedForAcediaStructure)
.Arg(config.GetPackageName());
}
}
usersPointer.FreeSelf();
} }
/** /**
@ -356,4 +387,5 @@ public final function LocalDatabase GetConfig()
defaultproperties defaultproperties
{ {
usesObjectPool = false usesObjectPool = false
warnDataErasedForAcediaStructure = (l=LOG_Warning,m="Data was erased at \"/users\" in database \"%1\" to create Acedia's database structure.")
} }

47
sources/Data/Database/Tests/TEST_DatabaseCommon.uc

@ -0,0 +1,47 @@
/**
* Set of tests for `DBRecord` class.
* Copyright 2021 Anton Tarasenko
*------------------------------------------------------------------------------
* This file is part of Acedia.
*
* Acedia 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, version 3 of the License, or
* (at your option) any later version.
*
* Acedia 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 Acedia. If not, see <https://www.gnu.org/licenses/>.
*/
class TEST_DatabaseCommon extends TestCase
abstract;
protected static function TESTS()
{
local JSONPointer pointer;
Context("Testing extracting `JSONPointer` from database link.");
Issue("`JSONPointer` is incorrectly extracted.");
pointer =
__().db.GetPointer(__().text.FromString("local:default/huh/what/is/"));
TEST_ExpectNotNone(pointer);
TEST_ExpectTrue(pointer.ToText().ToString() == "/huh/what/is/");
pointer = __().db.GetPointer(__().text.FromString("remote:db"));
TEST_ExpectNotNone(pointer);
TEST_ExpectTrue(pointer.ToText().ToString() == "");
pointer = __().db.GetPointer(__().text.FromString("remote:"));
TEST_ExpectNotNone(pointer);
TEST_ExpectTrue(pointer.ToText().ToString() == "");
pointer = __().db.GetPointer(__().text.FromString("/just/a/pointer"));
TEST_ExpectNotNone(pointer);
TEST_ExpectTrue(pointer.ToText().ToString() == "/just/a/pointer");
}
defaultproperties
{
caseGroup = "Database"
caseName = "Common database tests"
}

9
sources/Manifest.uc

@ -55,8 +55,9 @@ defaultproperties
testCases(19) = class'TEST_Command' testCases(19) = class'TEST_Command'
testCases(20) = class'TEST_CommandDataBuilder' testCases(20) = class'TEST_CommandDataBuilder'
testCases(21) = class'TEST_LogMessage' testCases(21) = class'TEST_LogMessage'
testCases(22) = class'TEST_LocalDatabase' testCases(22) = class'TEST_DatabaseCommon'
testCases(23) = class'TEST_AcediaConfig' testCases(23) = class'TEST_LocalDatabase'
testCases(24) = class'TEST_UTF8EncoderDecoder' testCases(24) = class'TEST_AcediaConfig'
testCases(25) = class'TEST_AvariceStreamReader' testCases(25) = class'TEST_UTF8EncoderDecoder'
testCases(26) = class'TEST_AvariceStreamReader'
} }
Loading…
Cancel
Save