-
Notifications
You must be signed in to change notification settings - Fork 28
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add a generic JSON-based storage class and an extension of it that will be used in the API to store client blobs. This is a similar structure to the Settings class / user_settings table but enforces a JSON value.
- Loading branch information
Showing
9 changed files
with
218 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
<?php | ||
|
||
class StorageTest extends PHPUnit\Framework\TestCase | ||
{ | ||
//------------------------------------------------------------------------ | ||
// Basic JSON storage test | ||
|
||
public function test_valid_json(): void | ||
{ | ||
$storage = new JsonStorage("username"); | ||
$storage->set("setting", "{}"); | ||
$value = $storage->get("setting"); | ||
$this->assertEquals("{}", $value); | ||
$value = $storage->delete("setting"); | ||
$this->assertEquals(null, $value); | ||
} | ||
|
||
public function test_invalid_json(): void | ||
{ | ||
$this->expectException(ValueError::class); | ||
$storage = new JsonStorage("username"); | ||
$storage->set("setting", "blearg"); | ||
} | ||
|
||
//------------------------------------------------------------------------ | ||
// API client storage test | ||
|
||
public function test_valid_client(): void | ||
{ | ||
global $api_client_storage_keys; | ||
$api_client_storage_keys = ["valid"]; | ||
|
||
$storage = new ApiClientStorage("valid", "username"); | ||
$storage->set("{}"); | ||
$value = $storage->get(); | ||
$this->assertEquals("{}", $value); | ||
$value = $storage->delete(); | ||
$this->assertEquals(null, $value); | ||
} | ||
|
||
public function test_invalid_client(): void | ||
{ | ||
global $api_client_storage_keys; | ||
$api_client_storage_keys = []; | ||
|
||
$this->expectException(ValueError::class); | ||
|
||
$storage = new ApiClientStorage("invalid", "username"); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
<?php | ||
$relPath = '../../../pinc/'; | ||
include_once($relPath.'base.inc'); | ||
|
||
// ------------------------------------------------------------ | ||
|
||
echo "Creating json_storage table...\n"; | ||
|
||
$sql = " | ||
CREATE TABLE json_storage ( | ||
username varchar(25) NOT NULL, | ||
setting varchar(32) NOT NULL, | ||
value json NOT NULL, | ||
timestamp int NOT NULL default 0, | ||
PRIMARY KEY (username, setting) | ||
) | ||
"; | ||
|
||
mysqli_query(DPDatabase::get_connection(), $sql) or die(mysqli_error(DPDatabase::get_connection())); | ||
|
||
// ------------------------------------------------------------ | ||
|
||
echo "\nDone!\n"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
<?php | ||
|
||
class ApiClientStorage | ||
{ | ||
private JsonStorage $storage; | ||
private string $setting; | ||
|
||
public function __construct(string $client, string $username) | ||
{ | ||
global $api_client_storage_keys; | ||
|
||
if (!in_array($client, $api_client_storage_keys)) { | ||
throw new ValueError("$client is not an accepted client"); | ||
} | ||
$this->setting = "client:$client"; | ||
$this->storage = new JsonStorage($username); | ||
} | ||
|
||
// ------------------------------------------------------------------------- | ||
|
||
public function set(string $value): void | ||
{ | ||
$this->storage->set($this->setting, $value); | ||
} | ||
|
||
public function get(): string | ||
{ | ||
// if the setting isn't set, JsonStorage will return null but we want | ||
// to always return valid JSON for the API so we return an empty JSON | ||
// doc. | ||
return $this->storage->get($this->setting) ?? "{}"; | ||
} | ||
|
||
public function delete(): void | ||
{ | ||
$this->storage->delete($this->setting); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
<?php | ||
|
||
class JsonStorage | ||
{ | ||
private string $username; | ||
|
||
public function __construct(string $username) | ||
{ | ||
$this->username = $username; | ||
} | ||
|
||
// ------------------------------------------------------------------------- | ||
|
||
/** | ||
* Set or update a json_storage object. | ||
*/ | ||
public function set(string $setting, string $value): void | ||
{ | ||
// It's possible $value could be very large and including it twice | ||
// in the query will cause it to be too big. The default max query | ||
// size is 64MB which means this will fail if the JSON blob is | ||
// just shy of 32MB. That seems plenty big. | ||
$sql = sprintf( | ||
" | ||
INSERT INTO json_storage | ||
SET | ||
username = '%s', | ||
setting = '%s', | ||
value = '%s', | ||
timestamp = UNIX_TIMESTAMP() | ||
ON DUPLICATE KEY UPDATE | ||
value = '%s', | ||
timestamp = UNIX_TIMESTAMP() | ||
", | ||
DPDatabase::escape($this->username), | ||
DPDatabase::escape($setting), | ||
DPDatabase::escape($value), | ||
DPDatabase::escape($value) | ||
); | ||
// We rely on MySQL to validate the JSON is valid or throw an exception. | ||
// It's going to do the check anyway and saves us from de-serializing | ||
// it here. | ||
try { | ||
DPDatabase::query($sql, true, false); | ||
} catch (DBQueryError $e) { | ||
if (startswith($e->getPrevious()->getMessage(), "Invalid JSON")) { | ||
throw new ValueError("Error persisting data, invalid JSON"); | ||
} | ||
throw $e; | ||
} | ||
} | ||
|
||
/** | ||
* Get a json_storage object. | ||
*/ | ||
public function get(string $setting): ?string | ||
{ | ||
$sql = sprintf( | ||
" | ||
SELECT value | ||
FROM json_storage | ||
WHERE username = '%s' AND setting = '%s' | ||
", | ||
DPDatabase::escape($this->username), | ||
DPDatabase::escape($setting) | ||
); | ||
$result = DPDatabase::query($sql); | ||
$value = null; | ||
while ($row = mysqli_fetch_assoc($result)) { | ||
$value = $row['value']; | ||
} | ||
mysqli_free_result($result); | ||
return $value; | ||
} | ||
|
||
/** | ||
* Delete a json_storage object. | ||
*/ | ||
public function delete(string $setting): void | ||
{ | ||
$sql = sprintf( | ||
" | ||
DELETE FROM json_storage | ||
WHERE username = '%s' AND setting = '%s' | ||
", | ||
DPDatabase::escape($this->username), | ||
DPDatabase::escape($setting) | ||
); | ||
DPDatabase::query($sql); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters