mirror of
https://codeberg.org/vlw/vlw.se.git
synced 2025-09-14 13:23:41 +02:00
wip: 2024-06-13T07:32:13+0200 (1718256733)
This commit is contained in:
parent
fd04c3d5ae
commit
4b1fbf331c
33 changed files with 418 additions and 1454 deletions
|
@ -1,39 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
|
|
||||||
use Reflect\Path;
|
|
||||||
use Reflect\Response;
|
|
||||||
|
|
||||||
use VLW\API\Databases\VLWdb\VLWdb;
|
|
||||||
use VLW\API\Databases\VLWdb\Models\Coffee\CoffeeModel;
|
|
||||||
|
|
||||||
require_once Path::root("src/databases/VLWdb.php");
|
|
||||||
require_once Path::root("src/databases/models/Coffee.php");
|
|
||||||
|
|
||||||
class GET_Coffee extends VLWdb {
|
|
||||||
const LIST_LIMIT = 20;
|
|
||||||
|
|
||||||
public function __construct() {
|
|
||||||
parent::__construct();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return a 503 Service Unavailable error if something went wrong with the database call
|
|
||||||
private function resp_database_error(): Response {
|
|
||||||
return new Response("Failed to get work data, please try again later", 503);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function main(): Response {
|
|
||||||
// Get the last LIST_LIMIT coffees from the database
|
|
||||||
$resp = $this->db->for(CoffeeModel::TABLE)
|
|
||||||
->order([CoffeeModel::DATE_TIMESTAMP_CREATED->value => "DESC"])
|
|
||||||
->limit(self::LIST_LIMIT)
|
|
||||||
->select([
|
|
||||||
CoffeeModel::ID->value,
|
|
||||||
CoffeeModel::DATE_TIMESTAMP_CREATED->value
|
|
||||||
]);
|
|
||||||
|
|
||||||
return parent::is_mysqli_result($resp)
|
|
||||||
? new Response($resp->fetch_all(MYSQLI_ASSOC))
|
|
||||||
: $this->resp_database_error();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,36 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
use Reflect\Path;
|
|
||||||
use Reflect\Response;
|
|
||||||
|
|
||||||
use VLW\API\Databases\VLWdb\VLWdb;
|
|
||||||
use VLW\API\Databases\VLWdb\Models\Coffee\CoffeeModel;
|
|
||||||
|
|
||||||
require_once Path::root("src/databases/VLWdb.php");
|
|
||||||
require_once Path::root("src/databases/models/Coffee.php");
|
|
||||||
|
|
||||||
class POST_Coffee extends VLWdb {
|
|
||||||
public function __construct() {
|
|
||||||
parent::__construct();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return a 503 Service Unavailable error if something went wrong with the database call
|
|
||||||
private function resp_database_error(): Response {
|
|
||||||
return new Response("Failed to record coffee! Ugh please take a note somewhere else", 503);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function main(): Response {
|
|
||||||
// Generate UUID for entity
|
|
||||||
$id = parent::gen_uuid4();
|
|
||||||
|
|
||||||
// Attempt to create new entity
|
|
||||||
$insert = $this->db->for(CoffeeModel::TABLE)
|
|
||||||
->insert([
|
|
||||||
CoffeeModel::ID->value => $id,
|
|
||||||
CoffeeModel::DATE_TIMESTAMP_CREATED->value => time(),
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Return 201 Created and entity id if successful
|
|
||||||
return $insert ? new Response($id, 201) : $this->resp_database_error();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,95 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
use Reflect\Path;
|
|
||||||
use Reflect\Response;
|
|
||||||
use ReflectRules\Type;
|
|
||||||
use ReflectRules\Rules;
|
|
||||||
use ReflectRules\Ruleset;
|
|
||||||
|
|
||||||
use VLW\API\Databases\VLWdb\VLWdb;
|
|
||||||
use VLW\API\Databases\VLWdb\Models\Media\MediaModel;
|
|
||||||
|
|
||||||
use victorwesterlund\xEnum;
|
|
||||||
|
|
||||||
require_once Path::root("src/databases/VLWdb.php");
|
|
||||||
require_once Path::root("src/databases/models/Media.php");
|
|
||||||
|
|
||||||
enum MediaDispositionEnum: string {
|
|
||||||
use xEnum;
|
|
||||||
|
|
||||||
case METADATA = "metadata";
|
|
||||||
case INLINE = "inline";
|
|
||||||
case DOWNLOAD = "download";
|
|
||||||
}
|
|
||||||
|
|
||||||
class GET_Media extends VLWdb {
|
|
||||||
const GET_DISPOSITION_KEY = "disposition";
|
|
||||||
|
|
||||||
protected Ruleset $ruleset;
|
|
||||||
|
|
||||||
public function __construct() {
|
|
||||||
parent::__construct();
|
|
||||||
$this->ruleset = new Ruleset(strict: true);
|
|
||||||
|
|
||||||
$this->ruleset->GET([
|
|
||||||
(new Rules(MediaModel::ID->value))
|
|
||||||
->required()
|
|
||||||
->type(Type::STRING)
|
|
||||||
->min(1)
|
|
||||||
->max(parent::MYSQL_VARCHAR_MAX_LENGTH),
|
|
||||||
|
|
||||||
(new Rules(self::GET_DISPOSITION_KEY))
|
|
||||||
->type(Type::ENUM, MediaDispositionEnum::values())
|
|
||||||
->default(MediaDispositionEnum::METADATA->value)
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// # Helper methods
|
|
||||||
|
|
||||||
private function fetch_srcset(string $id): array {
|
|
||||||
$resp = $this->db->for(WorkTagsModel::TABLE)
|
|
||||||
->where([WorkTagsModel::ANCHOR->value => $id])
|
|
||||||
->select(WorkTagsModel::NAME->value);
|
|
||||||
|
|
||||||
return parent::is_mysqli_result($resp) ? $resp->fetch_all(MYSQLI_ASSOC) : [];
|
|
||||||
}
|
|
||||||
|
|
||||||
// # Responses
|
|
||||||
|
|
||||||
// Return 422 Unprocessable Content error if request validation failed
|
|
||||||
private function resp_rules_invalid(): Response {
|
|
||||||
return new Response($this->ruleset->get_errors(), 422);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return a 503 Service Unavailable error if something went wrong with the database call
|
|
||||||
private function resp_database_error(): Response {
|
|
||||||
return new Response("Failed to get work data, please try again later", 503);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function main(): Response {
|
|
||||||
// Bail out if request validation failed
|
|
||||||
if (!$this->ruleset->is_valid()) {
|
|
||||||
return $this->resp_rules_invalid();
|
|
||||||
}
|
|
||||||
|
|
||||||
$resp = $this->db->for(MediaModel::TABLE)
|
|
||||||
->where([MediaModel::ID->value => $_GET[MediaModel::ID->value]])
|
|
||||||
->select([
|
|
||||||
MediaModel::ID->value,
|
|
||||||
MediaModel::NAME->value,
|
|
||||||
MediaModel::TYPE->value,
|
|
||||||
MediaModel::MIME->value,
|
|
||||||
MediaModel::EXTENSION->value,
|
|
||||||
MediaModel::SRCSET->value,
|
|
||||||
MediaModel::DATE_TIMESTAMP_CREATED->value,
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Bail out if something went wrong retrieving rows from the database
|
|
||||||
if (!parent::is_mysqli_result($resp)) {
|
|
||||||
return $this->resp_database_error();
|
|
||||||
}
|
|
||||||
|
|
||||||
$media = $resp->fetch_assoc();
|
|
||||||
$test = true;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,117 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
use Reflect\Path;
|
|
||||||
use Reflect\Response;
|
|
||||||
use ReflectRules\Type;
|
|
||||||
use ReflectRules\Rules;
|
|
||||||
use ReflectRules\Ruleset;
|
|
||||||
|
|
||||||
use Reflect\Method;
|
|
||||||
use function Reflect\Call;
|
|
||||||
|
|
||||||
use VLW\API\Databases\VLWdb\VLWdb;
|
|
||||||
use VLW\API\Databases\VLWdb\Models\Media\MediaModel;
|
|
||||||
use VLW\API\Databases\VLWdb\Models\Media\MediaTypeEnum;
|
|
||||||
|
|
||||||
require_once Path::root("src/databases/VLWdb.php");
|
|
||||||
require_once Path::root("src/databases/models/Media.php");
|
|
||||||
|
|
||||||
class POST_Media extends VLWdb {
|
|
||||||
protected Ruleset $ruleset;
|
|
||||||
|
|
||||||
public function __construct() {
|
|
||||||
parent::__construct();
|
|
||||||
$this->ruleset = new Ruleset(strict: true);
|
|
||||||
|
|
||||||
$this->ruleset->POST([
|
|
||||||
(new Rules(MediaModel::ID->value))
|
|
||||||
->type(Type::STRING)
|
|
||||||
->min(1)
|
|
||||||
->max(parent::MYSQL_VARCHAR_MAX_LENGTH)
|
|
||||||
->default(parent::gen_uuid4()),
|
|
||||||
|
|
||||||
(new Rules(MediaModel::NAME->value))
|
|
||||||
->type(Type::STRING)
|
|
||||||
->min(1)
|
|
||||||
->max(parent::MYSQL_VARCHAR_MAX_LENGTH)
|
|
||||||
->default(null),
|
|
||||||
|
|
||||||
(new Rules(MediaModel::TYPE->value))
|
|
||||||
->type(Type::ENUM, MediaTypeEnum::values())
|
|
||||||
->default(null),
|
|
||||||
|
|
||||||
(new Rules(MediaModel::EXTENSION->value))
|
|
||||||
->type(Type::STRING)
|
|
||||||
->min(3)
|
|
||||||
->max(parent::MYSQL_VARCHAR_MAX_LENGTH)
|
|
||||||
->default(null),
|
|
||||||
|
|
||||||
(new Rules(MediaModel::MIME->value))
|
|
||||||
->type(Type::STRING)
|
|
||||||
->min(3)
|
|
||||||
->max(parent::MYSQL_VARCHAR_MAX_LENGTH)
|
|
||||||
->default(null),
|
|
||||||
|
|
||||||
(new Rules(MediaModel::SRCSET->value))
|
|
||||||
->type(Type::STRING)
|
|
||||||
->min(1)
|
|
||||||
->max(parent::MYSQL_VARCHAR_MAX_LENGTH)
|
|
||||||
->default(null)
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// # Helper methods
|
|
||||||
|
|
||||||
// Returns true if an srcset exists for provided key
|
|
||||||
private static function media_srcset_exists(): bool {
|
|
||||||
// No srcet get parameter has been set
|
|
||||||
if (empty($_POST[MediaModel::SRCSET->value])) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the provided srcset exists by calling the srcset endpoint
|
|
||||||
return Call("media/srcset?id={$_POST[MediaModel::SRCSET->value]}", Method::GET)->ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
// # Responses
|
|
||||||
|
|
||||||
// Return 422 Unprocessable Content error if request validation failed
|
|
||||||
private function resp_rules_invalid(): Response {
|
|
||||||
return new Response($this->ruleset->get_errors(), 422);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return a 503 Service Unavailable error if something went wrong with the database call
|
|
||||||
private function resp_database_error(): Response {
|
|
||||||
return new Response("Failed to get work data, please try again later", 503);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function main(): Response {
|
|
||||||
// Bail out if request validation failed
|
|
||||||
if (!$this->ruleset->is_valid()) {
|
|
||||||
return $this->resp_rules_invalid();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bail out if an srcset doesn't exist
|
|
||||||
if (!self::media_srcset_exists()) {
|
|
||||||
return new Response("No media srcset exists with id '{$_POST[MediaModel::SRCSET->value]}'", 404);
|
|
||||||
}
|
|
||||||
|
|
||||||
$insert = $this->db->for(MediaModel::TABLE)
|
|
||||||
->insert([
|
|
||||||
MediaModel::ID->value => $_POST[MediaModel::ID->value],
|
|
||||||
MediaModel::NAME->value => $_POST[MediaModel::NAME->value],
|
|
||||||
MediaModel::MIME->value => $_POST[MediaModel::MIME->value],
|
|
||||||
// Strip dots from extension string if set
|
|
||||||
MediaModel::EXTENSION->value => $_POST[MediaModel::EXTENSION->value]
|
|
||||||
? str_replace(".", "", $_POST[MediaModel::EXTENSION->value])
|
|
||||||
: null,
|
|
||||||
MediaModel::SRCSET->value => $_POST[MediaModel::SRCSET->value],
|
|
||||||
MediaModel::DATE_TIMESTAMP_CREATED->value => time()
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Return media id if insert was successful
|
|
||||||
return $insert
|
|
||||||
? new Response($_POST[MediaModel::ID->value], 201)
|
|
||||||
: $this->resp_database_error();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,106 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
use Reflect\Path;
|
|
||||||
use Reflect\Response;
|
|
||||||
use ReflectRules\Type;
|
|
||||||
use ReflectRules\Rules;
|
|
||||||
use ReflectRules\Ruleset;
|
|
||||||
|
|
||||||
use VLW\API\Databases\VLWdb\VLWdb;
|
|
||||||
use VLW\API\Databases\VLWdb\Models\Media\MediaModel;
|
|
||||||
use VLW\API\Databases\VLWdb\Models\MediaSrcset\MediaSrcsetModel;
|
|
||||||
|
|
||||||
require_once Path::root("src/databases/VLWdb.php");
|
|
||||||
require_once Path::root("src/databases/models/Media.php");
|
|
||||||
require_once Path::root("src/databases/models/MediaSrcset.php");
|
|
||||||
|
|
||||||
class GET_MediaSrcset extends VLWdb {
|
|
||||||
protected Ruleset $ruleset;
|
|
||||||
|
|
||||||
public function __construct() {
|
|
||||||
parent::__construct();
|
|
||||||
$this->ruleset = new Ruleset(strict: true);
|
|
||||||
|
|
||||||
$this->ruleset->GET([
|
|
||||||
(new Rules(MediaSrcsetModel::ID->value))
|
|
||||||
->required()
|
|
||||||
->type(Type::STRING)
|
|
||||||
->min(1)
|
|
||||||
->max(parent::MYSQL_VARCHAR_MAX_LENGTH)
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// # Helper methods
|
|
||||||
|
|
||||||
// Get metadata for the requested srcset
|
|
||||||
private function get_srcset(): array|false {
|
|
||||||
$srcset = $this->db->for(MediaSrcsetModel::TABLE)
|
|
||||||
->where([MediaSrcsetModel::ID->value => $_GET[MediaSrcsetModel::ID->value]])
|
|
||||||
->select([MediaSrcsetModel::ANCHOR_DEFAULT->value]);
|
|
||||||
|
|
||||||
// Something went wrong retrieving rows from the database
|
|
||||||
if (!parent::is_mysqli_result($srcset)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return assoc array of srcset data if it exists
|
|
||||||
return $srcset->num_rows === 1 ? $srcset->fetch_assoc() : false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get all media entities that are part of the requested srcset
|
|
||||||
private function get_srcset_media(): mysqli_result|false {
|
|
||||||
$media = $this->db->for(MediaModel::TABLE)
|
|
||||||
->where([MediaModel::SRCSET->value => $_GET[MediaSrcsetModel::ID->value]])
|
|
||||||
->select([
|
|
||||||
MediaModel::ID->value,
|
|
||||||
MediaModel::TYPE->value,
|
|
||||||
MediaModel::MIME->value,
|
|
||||||
MediaModel::EXTENSION->value
|
|
||||||
]);
|
|
||||||
|
|
||||||
return parent::is_mysqli_result($media) ? $media : false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// # Responses
|
|
||||||
|
|
||||||
// Return 422 Unprocessable Content error if request validation failed
|
|
||||||
private function resp_rules_invalid(): Response {
|
|
||||||
return new Response($this->ruleset->get_errors(), 422);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return a 503 Service Unavailable error if something went wrong with the database call
|
|
||||||
private function resp_database_error(): Response {
|
|
||||||
return new Response("Failed to get work data, please try again later", 503);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function main(): Response {
|
|
||||||
// Bail out if request validation failed
|
|
||||||
if (!$this->ruleset->is_valid()) {
|
|
||||||
return $this->resp_rules_invalid();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get srcset data
|
|
||||||
$srcset = $this->get_srcset();
|
|
||||||
if (!$srcset) {
|
|
||||||
return new Response("No media srcset exist with id '{$_GET[MediaSrcsetModel::ID->value]}'", 404);
|
|
||||||
}
|
|
||||||
|
|
||||||
$media = $this->get_srcset_media();
|
|
||||||
if (!$media) {
|
|
||||||
return new Response("Failed to fetch srcset media", 500);
|
|
||||||
}
|
|
||||||
|
|
||||||
$media_entities = $media->fetch_all(MYSQLI_ASSOC);
|
|
||||||
|
|
||||||
// This is the id of the media entity that is considered the default or "fallback"
|
|
||||||
$srcet_default_media_id = $srcset[MediaSrcsetModel::ANCHOR_DEFAULT->value];
|
|
||||||
|
|
||||||
// Return assoc array of all media entities that are in this srcset
|
|
||||||
return new Response([
|
|
||||||
// Return default media entity separately from the rest of the srcset as an assoc array
|
|
||||||
"default" => array_filter($media_entities, fn(array $entity) => $entity[MediaModel::ID->value] === $srcet_default_media_id)[0],
|
|
||||||
// Return all media that isn't default as array of assoc arrays
|
|
||||||
"srcset" => array_filter($media_entities, fn(array $entity) => $entity[MediaModel::ID->value] !== $srcet_default_media_id)
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,55 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
use Reflect\Path;
|
|
||||||
use Reflect\Response;
|
|
||||||
|
|
||||||
use Reflect\Method;
|
|
||||||
use function Reflect\Call;
|
|
||||||
|
|
||||||
use VLW\API\Databases\VLWdb\VLWdb;
|
|
||||||
use VLW\API\Databases\VLWdb\Models\MediaSrcset\MediaSrcsetModel;
|
|
||||||
|
|
||||||
require_once Path::root("src/databases/VLWdb.php");
|
|
||||||
require_once Path::root("src/databases/models/Media.php");
|
|
||||||
require_once Path::root("src/databases/models/MediaSrcset.php");
|
|
||||||
|
|
||||||
class POST_MediaSrcset extends VLWdb {
|
|
||||||
public function __construct() {
|
|
||||||
parent::__construct();
|
|
||||||
}
|
|
||||||
|
|
||||||
// # Responses
|
|
||||||
|
|
||||||
// Return a 503 Service Unavailable error if something went wrong with the database call
|
|
||||||
private function resp_database_error(): Response {
|
|
||||||
return new Response("Failed to get work data, please try again later", 503);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function main(): Response {
|
|
||||||
// Generate a random UUID for this srcset
|
|
||||||
$id = parent::gen_uuid4();
|
|
||||||
|
|
||||||
// Ensure an srcset with the generated id doesn't exist, although it shouldn't realistically ever happen
|
|
||||||
$srcset_existing = Call("media/srcset?id={$id}", Method::GET);
|
|
||||||
if ($srcset_existing->code !== 404) {
|
|
||||||
// Wow a UUID4 collision... buy a lottery ticket
|
|
||||||
if ($srcset_existing->code === 200) {
|
|
||||||
return $this->main();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Failed to get srcset
|
|
||||||
return new Response("Something went wrong when checking if the srcset exists", 500);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create new srcset entity
|
|
||||||
$insert = $this->db->for(MediaSrcsetModel::TABLE)
|
|
||||||
->insert([
|
|
||||||
MediaSrcsetModel::ID->value => $id
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Return created srcset id if successful
|
|
||||||
return $insert
|
|
||||||
? new Response($id, 201)
|
|
||||||
: $this->resp_database_error();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -6,14 +6,11 @@
|
||||||
use ReflectRules\Rules;
|
use ReflectRules\Rules;
|
||||||
use ReflectRules\Ruleset;
|
use ReflectRules\Ruleset;
|
||||||
|
|
||||||
use Reflect\Method;
|
|
||||||
use function Reflect\Call;
|
|
||||||
|
|
||||||
use VLW\API\Databases\VLWdb\VLWdb;
|
use VLW\API\Databases\VLWdb\VLWdb;
|
||||||
use VLW\API\Databases\VLWdb\Models\Messages\MessagesModel;
|
use VLW\API\Databases\VLWdb\Models\Messages\MessagesModel;
|
||||||
|
|
||||||
require_once Path::root("src/databases/VLWdb.php");
|
require_once Path::root("src/databases/VLWdb.php");
|
||||||
require_once Path::root("src/databases/models/Messages.php");
|
require_once Path::root("src/databases/models/Messages/Messages.php");
|
||||||
|
|
||||||
class POST_Messages extends VLWdb {
|
class POST_Messages extends VLWdb {
|
||||||
protected Ruleset $ruleset;
|
protected Ruleset $ruleset;
|
||||||
|
@ -36,44 +33,15 @@
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// # Responses
|
|
||||||
|
|
||||||
// Return 422 Unprocessable Content error if request validation failed
|
|
||||||
private function resp_rules_invalid(): Response {
|
|
||||||
return new Response($this->ruleset->get_errors(), 422);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return a 503 Service Unavailable error if something went wrong with the database call
|
|
||||||
private function resp_database_error(): Response {
|
|
||||||
return new Response("Failed to get work data, please try again later", 503);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function main(): Response {
|
public function main(): Response {
|
||||||
//return new Response(["hello" => "maybe"], 500);
|
// Use copy of request body as entity
|
||||||
|
$entity = $_POST;
|
||||||
|
|
||||||
// Bail out if request validation failed
|
$entity[MessagesModel::ID->value] = parent::gen_uuid4();
|
||||||
if (!$this->ruleset->is_valid()) {
|
$entity[MessagesModel::DATE_CREATED->value] = time();
|
||||||
return $this->resp_rules_invalid();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate UUID for entity
|
return $this->db->for(MessagesModel::TABLE)->insert($_POST) === true
|
||||||
$id = parent::gen_uuid4();
|
? new Response($entity[MessagesModel::ID->value], 201)
|
||||||
|
: new Response("Failed to create message", 500);
|
||||||
// Attempt to create new entity
|
|
||||||
$insert = $this->db->for(MessagesModel::TABLE)
|
|
||||||
->insert([
|
|
||||||
MessagesModel::ID->value => $id,
|
|
||||||
MessagesModel::EMAIL->value => $_POST["email"],
|
|
||||||
MessagesModel::MESSAGE->value => $_POST["message"],
|
|
||||||
MessagesModel::DATE_TIMESTAMP_CREATED->value => time(),
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Bail out if insert failed
|
|
||||||
if (!$insert) {
|
|
||||||
return $this->resp_database_error();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return 201 Created and entity id
|
|
||||||
return new Response($id, 201);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,223 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
use Reflect\Path;
|
|
||||||
use Reflect\Response;
|
|
||||||
use ReflectRules\Type;
|
|
||||||
use ReflectRules\Rules;
|
|
||||||
use ReflectRules\Ruleset;
|
|
||||||
|
|
||||||
use Reflect\Method;
|
|
||||||
use function Reflect\Call;
|
|
||||||
|
|
||||||
use VLW\API\Databases\VLWdb\Models\Work\WorkModel;
|
|
||||||
use VLW\API\Databases\VLWdb\Models\Work\WorkTagsModel;
|
|
||||||
use VLW\API\Databases\VLWdb\Models\Work\WorkTagsNameEnum;
|
|
||||||
use VLW\API\Databases\VLWdb\Models\Work\WorkActionsModel;
|
|
||||||
|
|
||||||
require_once Path::root("src/databases/models/Work.php");
|
|
||||||
require_once Path::root("src/databases/models/WorkTags.php");
|
|
||||||
require_once Path::root("src/databases/models/WorkActions.php");
|
|
||||||
|
|
||||||
// "Virtual" database model for the POST request body since we're not writing to a db directly
|
|
||||||
enum ReleasesPostModel: string {
|
|
||||||
case GITHUB_USER = "user";
|
|
||||||
case GITHUB_REPO = "repo";
|
|
||||||
case GITHUB_TAG = "tag";
|
|
||||||
}
|
|
||||||
|
|
||||||
class POST_Releases {
|
|
||||||
// Base URL of the GitHub API (no tailing slash)
|
|
||||||
const GITHUB_API = "https://api.github.com";
|
|
||||||
|
|
||||||
const REGEX_HANDLE = "/@[\w]+/";
|
|
||||||
const REGEX_URL = "/\b(?:https?):\/\/\S+\b/";
|
|
||||||
|
|
||||||
protected Ruleset $ruleset;
|
|
||||||
|
|
||||||
protected CurlHandle $curl;
|
|
||||||
|
|
||||||
public function __construct() {
|
|
||||||
$this->ruleset = new Ruleset(strict: true);
|
|
||||||
|
|
||||||
$this->ruleset->POST([
|
|
||||||
(new Rules(ReleasesPostModel::GITHUB_USER->value))
|
|
||||||
->required()
|
|
||||||
->type(Type::STRING)
|
|
||||||
->min(1),
|
|
||||||
|
|
||||||
(new Rules(ReleasesPostModel::GITHUB_REPO->value))
|
|
||||||
->required()
|
|
||||||
->type(Type::STRING)
|
|
||||||
->min(1),
|
|
||||||
|
|
||||||
(new Rules(ReleasesPostModel::GITHUB_TAG->value))
|
|
||||||
->required()
|
|
||||||
->type(Type::STRING)
|
|
||||||
->type(Type::NUMBER)
|
|
||||||
->min(1)
|
|
||||||
]);
|
|
||||||
|
|
||||||
$this->curl = curl_init();
|
|
||||||
|
|
||||||
curl_setopt($this->curl, CURLOPT_USERAGENT, $_ENV["github"]["user_agent"]);
|
|
||||||
curl_setopt($this->curl, CURLOPT_HEADER, true);
|
|
||||||
curl_setopt($this->curl, CURLOPT_RETURNTRANSFER, true);
|
|
||||||
curl_setopt($this->curl, CURLOPT_HTTPHEADER, [
|
|
||||||
"Accept" => "application/vnd.github+json",
|
|
||||||
"Authorization" => "token {$_ENV["github"]["api_key"]}",
|
|
||||||
"X-GitHub-Api-Version" => "2022-11-28"
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// # GitHub
|
|
||||||
|
|
||||||
// Generate HTML from a GitHub "auto-generate" release body
|
|
||||||
protected static function gh_auto_release_md_to_html(string $md): string {
|
|
||||||
$output = "";
|
|
||||||
|
|
||||||
// Parse each line of markdown
|
|
||||||
$lines = explode(PHP_EOL, $md);
|
|
||||||
|
|
||||||
foreach ($lines as $i => $line) {
|
|
||||||
// Ignore header line from releases
|
|
||||||
if ($i < 1) continue;
|
|
||||||
|
|
||||||
// Replace all URLs with HTMLAnchor tags, they will be PRs
|
|
||||||
$links = [];
|
|
||||||
preg_match_all(self::REGEX_URL, $line, $links, PREG_UNMATCHED_AS_NULL);
|
|
||||||
foreach ($links as $i => $link) {
|
|
||||||
if (empty($link)) continue;
|
|
||||||
|
|
||||||
// Last crumb from link pathname will be the PR id
|
|
||||||
$pr_id = explode("/", $link[$i]);
|
|
||||||
$pr_id = end($pr_id);
|
|
||||||
|
|
||||||
$line = str_replace($link, "<a href='{$link[$i]}'>{$pr_id}</a>", $line);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Replace all at-handles with links to GitHub user profiles
|
|
||||||
$handles = [];
|
|
||||||
preg_match_all(self::REGEX_HANDLE, $line, $handles, PREG_UNMATCHED_AS_NULL);
|
|
||||||
foreach ($handles as $i => $handle) {
|
|
||||||
if (empty($handle)) continue;
|
|
||||||
|
|
||||||
// GitHub user URL without the "@"
|
|
||||||
$url = "https://github.com/" . substr($handle[$i], 1);
|
|
||||||
|
|
||||||
$line = str_replace($handle, "<a href='{$url}'>{$handle[$i]}</a>", $line);
|
|
||||||
}
|
|
||||||
|
|
||||||
$output .= "<p>{$line}</p>";
|
|
||||||
}
|
|
||||||
|
|
||||||
return $output;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return fully qualified URL to GitHub API releases endpoint
|
|
||||||
private static function get_url(): string {
|
|
||||||
return implode("/", [
|
|
||||||
self::GITHUB_API,
|
|
||||||
"repos",
|
|
||||||
$_POST[ReleasesPostModel::GITHUB_USER->value],
|
|
||||||
$_POST[ReleasesPostModel::GITHUB_REPO->value],
|
|
||||||
"releases",
|
|
||||||
"tags",
|
|
||||||
$_POST[ReleasesPostModel::GITHUB_TAG->value],
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fetch release information from GitHub API
|
|
||||||
private function fetch_release_data(): array {
|
|
||||||
$url = self::get_url();
|
|
||||||
curl_setopt($this->curl, CURLOPT_URL, self::get_url());
|
|
||||||
|
|
||||||
$resp = curl_exec($this->curl);
|
|
||||||
|
|
||||||
$header_size = curl_getinfo($this->curl, CURLINFO_HEADER_SIZE);
|
|
||||||
$header = substr($resp, 0, $header_size);
|
|
||||||
$body = substr($resp, $header_size);
|
|
||||||
|
|
||||||
return json_decode($body, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// # Sup
|
|
||||||
|
|
||||||
private function create_link_to_release_page(string $id, string $href): Response {
|
|
||||||
return Call("work/actions?id={$id}", Method::POST, [
|
|
||||||
WorkActionsModel::DISPLAY_TEXT->value => "Release details",
|
|
||||||
WorkActionsModel::HREF->value => $href,
|
|
||||||
WorkActionsModel::EXTERNAL->value => true
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a tag for entity
|
|
||||||
private function create_tag(string $id, WorkTagsNameEnum $tag): Response {
|
|
||||||
return Call("work/tags?id={$id}", Method::POST, [
|
|
||||||
// Set "RELEASE" tag on new entity
|
|
||||||
WorkTagsModel::NAME->value => $tag->value
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// # Responses
|
|
||||||
|
|
||||||
// Return 422 Unprocessable Content error if request validation failed
|
|
||||||
private function resp_rules_invalid(): Response {
|
|
||||||
return new Response($this->ruleset->get_errors(), 422);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function main(): Response {
|
|
||||||
// Bail out if request validation failed
|
|
||||||
if (!$this->ruleset->is_valid()) {
|
|
||||||
return $this->resp_rules_invalid();
|
|
||||||
}
|
|
||||||
|
|
||||||
$data = $this->fetch_release_data();
|
|
||||||
if (!$data) {
|
|
||||||
return new Response("Failed to fetch release data", 500);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Transform repo name to lowercase for summary title
|
|
||||||
$title = strtolower($_POST["repo"]);
|
|
||||||
|
|
||||||
// Use repo name and tag name as heading for summary
|
|
||||||
$summary = "<h3>Release {$title}@{$data["name"]}</h3>";
|
|
||||||
// Append HTML-ified release notes from GitHub to summary
|
|
||||||
$summary .= self::gh_auto_release_md_to_html($data["body"]);
|
|
||||||
|
|
||||||
$date_published = new \DateTime($data["published_at"], new \DateTimeZone("UTC"));
|
|
||||||
|
|
||||||
// Create work entity
|
|
||||||
$work_entity = Call("work", Method::POST, [
|
|
||||||
WorkModel::SUMMARY->value => $summary,
|
|
||||||
// Convert time created to Unix timestamp for work endpoint
|
|
||||||
WorkModel::DATE_TIMESTAMP_CREATED->value => $date_published->format("U"),
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Bail out if creating the work entity failed
|
|
||||||
if (!$work_entity->ok) {
|
|
||||||
return new Response("Failed to create work entity for release", 500);
|
|
||||||
}
|
|
||||||
|
|
||||||
$work_entity_id = $work_entity->output();
|
|
||||||
|
|
||||||
// Create entity tags for release
|
|
||||||
$tags = [
|
|
||||||
WorkTagsNameEnum::VLW,
|
|
||||||
WorkTagsNameEnum::RELEASE
|
|
||||||
];
|
|
||||||
foreach ($tags as $tag) {
|
|
||||||
// Create entity tag for release or exit if failed to create
|
|
||||||
if (!$this->create_tag($work_entity_id, $tag)->ok) {
|
|
||||||
return new Response("Failed to create {$tag->name} tag for release entity", 500);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create link to release page on GitHub
|
|
||||||
if (!$this->create_link_to_release_page($work_entity_id, $data["html_url"])) {
|
|
||||||
return new Response("Failed to create link to release page on GitHub", 500);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Response($work_entity_id, 201);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,15 +1,16 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
|
||||||
use Reflect\Path;
|
use Reflect\Path;
|
||||||
use Reflect\Response;
|
use Reflect\Response;
|
||||||
use ReflectRules\Type;
|
use ReflectRules\Type;
|
||||||
use ReflectRules\Rules;
|
use ReflectRules\Rules;
|
||||||
use ReflectRules\Ruleset;
|
use ReflectRules\Ruleset;
|
||||||
|
|
||||||
|
use const VLW\API\RESP_DELETE_OK;
|
||||||
use VLW\API\Databases\VLWdb\VLWdb;
|
use VLW\API\Databases\VLWdb\VLWdb;
|
||||||
use VLW\API\Databases\VLWdb\Models\Work\WorkModel;
|
use VLW\API\Databases\VLWdb\Models\Work\WorkModel;
|
||||||
|
|
||||||
|
require_once Path::root("src/Endpoints.php");
|
||||||
require_once Path::root("src/databases/VLWdb.php");
|
require_once Path::root("src/databases/VLWdb.php");
|
||||||
require_once Path::root("src/databases/models/Work.php");
|
require_once Path::root("src/databases/models/Work.php");
|
||||||
|
|
||||||
|
@ -17,44 +18,47 @@
|
||||||
protected Ruleset $ruleset;
|
protected Ruleset $ruleset;
|
||||||
|
|
||||||
public function __construct() {
|
public function __construct() {
|
||||||
parent::__construct();
|
|
||||||
$this->ruleset = new Ruleset(strict: true);
|
$this->ruleset = new Ruleset(strict: true);
|
||||||
|
|
||||||
$this->ruleset->GET([
|
$this->ruleset->POST([
|
||||||
(new Rules("id"))
|
(new Rules(WorkModel::ID->value))
|
||||||
->required()
|
|
||||||
->type(Type::STRING)
|
->type(Type::STRING)
|
||||||
->min(1)
|
->min(1)
|
||||||
->max(parent::MYSQL_VARCHAR_MAX_LENGTH)
|
->max(parent::MYSQL_VARCHAR_MAX_LENGTH),
|
||||||
|
|
||||||
|
(new Rules(WorkModel::TITLE->value))
|
||||||
|
->type(Type::STRING)
|
||||||
|
->min(3)
|
||||||
|
->max(parent::MYSQL_VARCHAR_MAX_LENGTH),
|
||||||
|
|
||||||
|
(new Rules(WorkModel::SUMMARY->value))
|
||||||
|
->type(Type::STRING)
|
||||||
|
->min(1)
|
||||||
|
->max(parent::MYSQL_TEXT_MAX_LENGTH),
|
||||||
|
|
||||||
|
(new Rules(WorkModel::IS_LISTABLE->value))
|
||||||
|
->type(Type::BOOLEAN),
|
||||||
|
|
||||||
|
(new Rules(WorkModel::IS_READABLE->value))
|
||||||
|
->type(Type::BOOLEAN),
|
||||||
|
|
||||||
|
(new Rules(WorkModel::DATE_MODIFIED->value))
|
||||||
|
->type(Type::NUMBER)
|
||||||
|
->min(1)
|
||||||
|
->max(parent::MYSQL_INT_MAX_LENGHT),
|
||||||
|
|
||||||
|
(new Rules(WorkModel::DATE_CREATED->value))
|
||||||
|
->type(Type::NUMBER)
|
||||||
|
->min(1)
|
||||||
|
->max(parent::MYSQL_INT_MAX_LENGHT)
|
||||||
]);
|
]);
|
||||||
}
|
|
||||||
|
|
||||||
// # Responses
|
parent::__construct($this->ruleset);
|
||||||
|
|
||||||
// Return 422 Unprocessable Content error if request validation failed
|
|
||||||
private function resp_rules_invalid(): Response {
|
|
||||||
return new Response($this->ruleset->get_errors(), 422);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return a 503 Service Unavailable error if something went wrong with the database call
|
|
||||||
private function resp_database_error(): Response {
|
|
||||||
return new Response("Failed to delete work data, please try again later", 503);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function main(): Response {
|
public function main(): Response {
|
||||||
// Bail out if request validation failed
|
return $this->db->for(FieldsEnumsModel::TABLE)->delete($_POST) === true
|
||||||
if (!$this->ruleset->is_valid()) {
|
? new Response(RESP_DELETE_OK)
|
||||||
return $this->resp_rules_invalid();
|
: new Response("Failed to delete work entity", 500);
|
||||||
}
|
|
||||||
|
|
||||||
// Attempt to update the entity
|
|
||||||
$update = $this->db->for(WorkModel::TABLE)
|
|
||||||
->where([WorkModel::ID->value => $_GET["id"]])
|
|
||||||
->update([
|
|
||||||
WorkModel::IS_LISTABLE->value => false,
|
|
||||||
WorkModel::IS_READABLE->value => false
|
|
||||||
]);
|
|
||||||
|
|
||||||
return $update ? new Response($_GET["id"]) : $this->resp_database_error();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,136 +1,76 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
use Reflect\Call;
|
|
||||||
use Reflect\Path;
|
use Reflect\Path;
|
||||||
use Reflect\Method;
|
|
||||||
use Reflect\Response;
|
use Reflect\Response;
|
||||||
use ReflectRules\Type;
|
use ReflectRules\Type;
|
||||||
use ReflectRules\Rules;
|
use ReflectRules\Rules;
|
||||||
use ReflectRules\Ruleset;
|
use ReflectRules\Ruleset;
|
||||||
|
|
||||||
use VLW\API\Endpoints;
|
|
||||||
|
|
||||||
use VLW\API\Databases\VLWdb\VLWdb;
|
use VLW\API\Databases\VLWdb\VLWdb;
|
||||||
use VLW\API\Databases\VLWdb\Models\Work\WorkModel;
|
use VLW\API\Databases\VLWdb\Models\Work\WorkModel;
|
||||||
use VLW\API\Databases\VLWdb\Models\Work\WorkTagsModel;
|
|
||||||
use VLW\API\Databases\VLWdb\Models\Work\WorkActionsModel;
|
|
||||||
|
|
||||||
require_once Path::root("src/databases/VLWdb.php");
|
require_once Path::root("src/databases/VLWdb.php");
|
||||||
require_once Path::root("src/databases/models/Work.php");
|
require_once Path::root("src/databases/models/Work.php");
|
||||||
require_once Path::root("src/databases/models/WorkTags.php");
|
|
||||||
require_once Path::root("src/databases/models/WorkActions.php");
|
|
||||||
|
|
||||||
class GET_Work extends VLWdb {
|
class GET_Work extends VLWdb {
|
||||||
protected Ruleset $ruleset;
|
protected Ruleset $ruleset;
|
||||||
|
|
||||||
public function __construct() {
|
public function __construct() {
|
||||||
parent::__construct();
|
|
||||||
$this->ruleset = new Ruleset(strict: true);
|
$this->ruleset = new Ruleset(strict: true);
|
||||||
|
|
||||||
$this->ruleset->GET([
|
$this->ruleset->GET([
|
||||||
(new Rules("id"))
|
(new Rules(WorkModel::ID->value))
|
||||||
->type(Type::STRING)
|
->type(Type::STRING)
|
||||||
->min(1)
|
->min(1)
|
||||||
->max(parent::MYSQL_VARCHAR_MAX_LENGTH)
|
->max(parent::MYSQL_VARCHAR_MAX_LENGTH),
|
||||||
->default(null)
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// # Helper methods
|
(new Rules(WorkModel::TITLE->value))
|
||||||
|
->type(Type::STRING)
|
||||||
|
->min(3)
|
||||||
|
->max(parent::MYSQL_VARCHAR_MAX_LENGTH),
|
||||||
|
|
||||||
private function fetch_row_tags(string $id): array {
|
(new Rules(WorkModel::SUMMARY->value))
|
||||||
$resp = $this->db->for(WorkTagsModel::TABLE)
|
->type(Type::STRING)
|
||||||
->where([WorkTagsModel::ANCHOR->value => $id])
|
->min(1)
|
||||||
->select(WorkTagsModel::NAME->value);
|
->max(parent::MYSQL_TEXT_MAX_LENGTH),
|
||||||
|
|
||||||
return parent::is_mysqli_result($resp) ? $resp->fetch_all(MYSQLI_ASSOC) : [];
|
(new Rules(WorkModel::IS_LISTABLE->value))
|
||||||
}
|
->type(Type::BOOLEAN)
|
||||||
|
->default(true),
|
||||||
|
|
||||||
// # Responses
|
(new Rules(WorkModel::IS_READABLE->value))
|
||||||
|
->type(Type::BOOLEAN)
|
||||||
|
->default(true),
|
||||||
|
|
||||||
// Return 422 Unprocessable Content error if request validation failed
|
(new Rules(WorkModel::DATE_MODIFIED->value))
|
||||||
private function resp_rules_invalid(): Response {
|
->type(Type::NUMBER)
|
||||||
return new Response($this->ruleset->get_errors(), 422);
|
->min(1)
|
||||||
}
|
->max(parent::MYSQL_INT_MAX_LENGHT),
|
||||||
|
|
||||||
// Return a 503 Service Unavailable error if something went wrong with the database call
|
(new Rules(WorkModel::DATE_CREATED->value))
|
||||||
private function resp_database_error(): Response {
|
->type(Type::NUMBER)
|
||||||
return new Response("Failed to get work data, please try again later", 503);
|
->min(1)
|
||||||
}
|
->max(parent::MYSQL_INT_MAX_LENGHT)
|
||||||
|
|
||||||
private function resp_item_details(string $id): Response {
|
|
||||||
$resp = $this->db->for(WorkModel::TABLE)
|
|
||||||
->where([
|
|
||||||
WorkModel::ID->value => $id,
|
|
||||||
WorkModel::IS_READABLE->value => true
|
|
||||||
])
|
|
||||||
->limit(1)
|
|
||||||
->select([
|
|
||||||
WorkModel::ID->value,
|
|
||||||
WorkModel::TITLE->value,
|
|
||||||
WorkModel::SUMMARY->value,
|
|
||||||
WorkModel::COVER_SRCSET->value,
|
|
||||||
WorkModel::DATE_YEAR->value,
|
|
||||||
WorkModel::DATE_MONTH->value,
|
|
||||||
WorkModel::DATE_DAY->value,
|
|
||||||
WorkModel::DATE_TIMESTAMP_MODIFIED->value,
|
|
||||||
WorkModel::DATE_TIMESTAMP_CREATED->value
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Bail out if something went wrong retrieving rows from the database
|
parent::__construct($this->ruleset);
|
||||||
if (!parent::is_mysqli_result($resp)) {
|
|
||||||
return $this->resp_database_error();
|
|
||||||
}
|
|
||||||
|
|
||||||
return $resp->num_rows === 1
|
|
||||||
? new Response($resp->fetch_assoc())
|
|
||||||
: new Response("No entity with id '{$id}' was found", 404);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function main(): Response {
|
public function main(): Response {
|
||||||
// Bail out if request validation failed
|
$response = $this->db->for(WorkModel::TABLE)
|
||||||
if (!$this->ruleset->is_valid()) {
|
->where($_GET)
|
||||||
return $this->resp_rules_invalid();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return details about a specific item by id
|
|
||||||
if (!empty($_GET["id"])) {
|
|
||||||
return $this->resp_item_details($_GET["id"]);
|
|
||||||
}
|
|
||||||
|
|
||||||
$resp = $this->db->for(WorkModel::TABLE)
|
|
||||||
->where([WorkModel::IS_LISTABLE->value => true])
|
|
||||||
->order([WorkModel::DATE_TIMESTAMP_CREATED->value => "DESC"])
|
|
||||||
->select([
|
->select([
|
||||||
WorkModel::ID->value,
|
WorkModel::ID->value,
|
||||||
WorkModel::TITLE->value,
|
WorkModel::TITLE->value,
|
||||||
WorkModel::SUMMARY->value,
|
WorkModel::SUMMARY->value,
|
||||||
WorkModel::COVER_SRCSET->value,
|
WorkModel::IS_LISTABLE->value,
|
||||||
WorkModel::DATE_YEAR->value,
|
WorkModel::IS_READABLE->value,
|
||||||
WorkModel::DATE_MONTH->value,
|
WorkModel::DATE_MODIFIED->value,
|
||||||
WorkModel::DATE_DAY->value,
|
WorkModel::DATE_CREATED->value
|
||||||
WorkModel::DATE_TIMESTAMP_MODIFIED->value,
|
|
||||||
WorkModel::DATE_TIMESTAMP_CREATED->value
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Bail out if something went wrong retrieving rows from the database
|
return $response->num_rows > 0
|
||||||
if (!parent::is_mysqli_result($resp)) {
|
? new Response($response->fetch_all(MYSQLI_ASSOC))
|
||||||
return $this->resp_database_error();
|
: new Response([], 404);
|
||||||
}
|
|
||||||
|
|
||||||
// Resolve foreign keys
|
|
||||||
$rows = [];
|
|
||||||
while ($row = $resp->fetch_assoc()) {
|
|
||||||
$row["tags"] = $this->fetch_row_tags($row["id"]);
|
|
||||||
|
|
||||||
// Fetch actions for work entity by id from endpoint
|
|
||||||
$row["actions"] = (new Call(Endpoints::WORK_ACTIONS->value))
|
|
||||||
->params([WorkActionsModel::ANCHOR->value => $row[WorkModel::ID->value]])
|
|
||||||
->get()->output();
|
|
||||||
|
|
||||||
$rows[] = $row;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Response($rows);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,30 +1,28 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use Reflect\Call;
|
||||||
use Reflect\Path;
|
use Reflect\Path;
|
||||||
use Reflect\Response;
|
use Reflect\Response;
|
||||||
use ReflectRules\Type;
|
use ReflectRules\Type;
|
||||||
use ReflectRules\Rules;
|
use ReflectRules\Rules;
|
||||||
use ReflectRules\Ruleset;
|
use ReflectRules\Ruleset;
|
||||||
|
|
||||||
use Reflect\Method;
|
use VLW\API\Endpoints;
|
||||||
use function Reflect\Call;
|
|
||||||
|
|
||||||
use VLW\API\Databases\VLWdb\VLWdb;
|
use VLW\API\Databases\VLWdb\VLWdb;
|
||||||
use VLW\API\Databases\VLWdb\Models\Work\WorkModel;
|
use VLW\API\Databases\VLWdb\Models\Work\{
|
||||||
use VLW\API\Databases\VLWdb\Models\WorkPermalinks\WorkPermalinksModel;
|
WorkModel,
|
||||||
|
WorkPermalinksModel
|
||||||
|
};
|
||||||
|
|
||||||
|
require_once Path::root("src/Endpoints.php");
|
||||||
require_once Path::root("src/databases/VLWdb.php");
|
require_once Path::root("src/databases/VLWdb.php");
|
||||||
require_once Path::root("src/databases/models/Work.php");
|
require_once Path::root("src/databases/models/Work/Work.php");
|
||||||
require_once Path::root("src/databases/models/WorkPermalinks.php");
|
require_once Path::root("src/databases/models/Work/WorkPermalinks.php");
|
||||||
|
|
||||||
class PATCH_Work extends VLWdb {
|
class PATCH_Work extends VLWdb {
|
||||||
protected Ruleset $ruleset;
|
protected Ruleset $ruleset;
|
||||||
|
|
||||||
protected Response $current_entity;
|
|
||||||
protected array $updated_entity;
|
|
||||||
|
|
||||||
public function __construct() {
|
public function __construct() {
|
||||||
parent::__construct();
|
|
||||||
$this->ruleset = new Ruleset(strict: true);
|
$this->ruleset = new Ruleset(strict: true);
|
||||||
|
|
||||||
$this->ruleset->GET([
|
$this->ruleset->GET([
|
||||||
|
@ -52,19 +50,19 @@
|
||||||
(new Rules(WorkModel::IS_READABLE->value))
|
(new Rules(WorkModel::IS_READABLE->value))
|
||||||
->type(Type::BOOLEAN),
|
->type(Type::BOOLEAN),
|
||||||
|
|
||||||
(new Rules(WorkModel::DATE_TIMESTAMP_CREATED->value))
|
(new Rules(WorkModel::DATE_MODIFIED->value))
|
||||||
->type(Type::NUMBER)
|
->type(Type::NUMBER)
|
||||||
->min(0)
|
->min(1)
|
||||||
|
->max(parent::MYSQL_INT_MAX_LENGHT)
|
||||||
|
->default(time()),
|
||||||
|
|
||||||
|
(new Rules(WorkModel::DATE_CREATED->value))
|
||||||
|
->type(Type::NUMBER)
|
||||||
|
->min(1)
|
||||||
->max(parent::MYSQL_INT_MAX_LENGHT)
|
->max(parent::MYSQL_INT_MAX_LENGHT)
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->get_existing_entity();
|
parent::__construct();
|
||||||
|
|
||||||
// Copy all provided post data into a new array
|
|
||||||
$this->updated_entity = $_POST;
|
|
||||||
|
|
||||||
// Set date modified timestamp
|
|
||||||
$this->updated_entity[WorkModel::DATE_TIMESTAMP_MODIFIED->value] = time();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate a slug URL from string
|
// Generate a slug URL from string
|
||||||
|
@ -72,130 +70,46 @@
|
||||||
return strtolower(trim(preg_replace('/[^A-Za-z0-9-]+/', '-', $input)));
|
return strtolower(trim(preg_replace('/[^A-Za-z0-9-]+/', '-', $input)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// # Helper methods
|
// Compute and return modeled year, month, and day from Unix timestamp in request body
|
||||||
|
private static function gen_date_created(): array {
|
||||||
private function get_existing_entity(): Response {
|
return [
|
||||||
// Check if an entity already exists with slugified title from GET endpoint
|
WorkModel::DATE_YEAR->value => date("Y", $_POST[WorkModel::DATE_CREATED->value]),
|
||||||
$this->current_entity = Call("work?id={$_GET["id"]}", Method::GET);
|
WorkModel::DATE_MONTH ->value => date("n", $_POST[WorkModel::DATE_CREATED->value]),
|
||||||
|
WorkModel::DATE_DAY->value => date("j", $_POST[WorkModel::DATE_CREATED->value])
|
||||||
// Response is not 404 (Not found) so we can't create the entity
|
];
|
||||||
if ($this->current_entity->code !== 200) {
|
|
||||||
// Response is not a valid entity, something went wrong
|
|
||||||
if ($this->current_entity->code !== 404) {
|
|
||||||
return $this->resp_database_error();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return 402 Conflict
|
private function get_entity_by_id(string $id): Response {
|
||||||
return new Response("No entity with id '{$_GET["id"]}' was found", 404);
|
return (new Call(Endpoints::WORK->value))->params([
|
||||||
}
|
WorkModel::ID->value => $id
|
||||||
|
])->get();
|
||||||
return $this->current_entity;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create new permalink for entity slug
|
|
||||||
private function create_permalink(string $slug): bool {
|
|
||||||
$create = Call("work/permalinks", Method::POST, [
|
|
||||||
WorkPermalinksModel::SLUG->value => $slug,
|
|
||||||
WorkPermalinksModel::ANCHOR->value => $slug
|
|
||||||
]);
|
|
||||||
|
|
||||||
return $create->ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ## Updated entity
|
|
||||||
|
|
||||||
private function change_slug(): bool {
|
|
||||||
if (!array_key_exists(WorkModel::ID->value, $this->updated_entity)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate new permalink for entity id
|
|
||||||
return $this->create_permalink($this->updated_entity[WorkModel::ID->value]);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function timestamp_to_dates(): void {
|
|
||||||
if (!array_key_exists(WorkModel::DATE_TIMESTAMP_CREATED->value, $this->updated_entity)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get timestamp from post data
|
|
||||||
$timestamp = $this->updated_entity[WorkModel::DATE_TIMESTAMP_CREATED->value];
|
|
||||||
|
|
||||||
// Update fractured dates from timestamp
|
|
||||||
$this->updated_entity[WorkModel::DATE_YEAR->value] = date("Y", $timestamp);
|
|
||||||
$this->updated_entity[WorkModel::DATE_MONTH ->value] = date("n", $timestamp);
|
|
||||||
$this->updated_entity[WorkModel::DATE_DAY->value] = date("j", $timestamp);
|
|
||||||
}
|
|
||||||
|
|
||||||
// # Responses
|
|
||||||
|
|
||||||
// Return 422 Unprocessable Content error if request validation failed
|
|
||||||
private function resp_rules_invalid(): Response {
|
|
||||||
return new Response($this->ruleset->get_errors(), 422);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return a 503 Service Unavailable error if something went wrong with the database call
|
|
||||||
private function resp_database_error(): Response {
|
|
||||||
return new Response("Failed to get work data, please try again later", 503);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return a 422 Unprocessable Entity if there is nothing to change
|
|
||||||
private function resp_no_changes(): Response {
|
|
||||||
return new Response("No columns to update", 422);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Rollback changes and return error response
|
|
||||||
private function resp_permalink_error_rollback(): Response {
|
|
||||||
$update = $this->db->for(WorkModel::TABLE)
|
|
||||||
->where([WorkModel::ID->value => $_GET["id"]])
|
|
||||||
->update($this->current_entity->output());
|
|
||||||
|
|
||||||
return $update
|
|
||||||
? new Response("Failed to create new permalink for updated entity. Changes have been rolled back", 500)
|
|
||||||
: new Reponse("Failed to create new permalink for updated entity. Changes failed to rollback, this is bad.", 500);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function main(): Response {
|
public function main(): Response {
|
||||||
// Bail out if request validation failed
|
// Use copy of request body as entity
|
||||||
if (!$this->ruleset->is_valid()) {
|
$entity = $_POST;
|
||||||
return $this->resp_rules_invalid();
|
|
||||||
|
// Generate a new slug id from title if changed
|
||||||
|
if ($_POST[WorkModel::TITLE->value]) {
|
||||||
|
$slug = $_POST[WorkModel::TITLE->value];
|
||||||
|
|
||||||
|
// Bail out if the slug generated from the new tite already exist
|
||||||
|
if ($this->get_entity_by_id($slug)) {
|
||||||
|
return new Response("An entity with this title already exist", 409);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Empty payload, nothing to do
|
// Add the new slug to update entity
|
||||||
if (empty($_POST)) {
|
$entity[WorkModel::ID] = $slug;
|
||||||
return $this->resp_no_changes();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate new slug for entity if title is updated
|
// Generate new work date fields from timestamp
|
||||||
if (array_key_exists(WorkModel::TITLE->value, $_POST)) {
|
if ($_POST[WorkModel::DATE_CREATED->value]) {
|
||||||
// Generate URL slug from title text or UUID if undefined
|
array_merge($entity, self::gen_date_created());
|
||||||
$slug = self::gen_slug($_POST["title"]);
|
|
||||||
|
|
||||||
// Save generated slug from title if it's different from existing slug
|
|
||||||
if ($slug !== $this->current_entity->output()[WorkModel::ID->value]) {
|
|
||||||
$this->updated_entity[WorkModel::ID->value] = $slug;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update fractured dates from timestamp
|
// Update entity by existing id
|
||||||
$this->timestamp_to_dates();
|
return $this->db->for(WorkModel::TABLE)->where([WorkModel::ID->value => $_GET[WorkModel::ID->value]])->update($entity) === true
|
||||||
|
? new Response($_GET[WorkModel::ID->value])
|
||||||
// Attempt to update the entity
|
: new Response("Failed to update entity", 500);
|
||||||
$update = $this->db->for(WorkModel::TABLE)
|
|
||||||
->where([WorkModel::ID->value => $_GET["id"]])
|
|
||||||
->update($this->updated_entity);
|
|
||||||
|
|
||||||
// Bail out if update failed
|
|
||||||
if (!$update) {
|
|
||||||
return $this->resp_database_error();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create new slug for entity if title was changed
|
|
||||||
if (!$this->change_slug()) {
|
|
||||||
return $this->resp_permalink_error_rollback();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return 200 OK and new or existing entity slug as body
|
|
||||||
return new Response($this->current_entity->output()[WorkModel::ID->value]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,27 +1,28 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use Reflect\Call;
|
||||||
use Reflect\Path;
|
use Reflect\Path;
|
||||||
use Reflect\Response;
|
use Reflect\Response;
|
||||||
use ReflectRules\Type;
|
use ReflectRules\Type;
|
||||||
use ReflectRules\Rules;
|
use ReflectRules\Rules;
|
||||||
use ReflectRules\Ruleset;
|
use ReflectRules\Ruleset;
|
||||||
|
|
||||||
use Reflect\Method;
|
use VLW\API\Endpoints;
|
||||||
use function Reflect\Call;
|
|
||||||
|
|
||||||
use VLW\API\Databases\VLWdb\VLWdb;
|
use VLW\API\Databases\VLWdb\VLWdb;
|
||||||
use VLW\API\Databases\VLWdb\Models\Work\WorkModel;
|
use VLW\API\Databases\VLWdb\Models\Work\{
|
||||||
use VLW\API\Databases\VLWdb\Models\WorkPermalinks\WorkPermalinksModel;
|
WorkModel,
|
||||||
|
WorkPermalinksModel
|
||||||
|
};
|
||||||
|
|
||||||
|
require_once Path::root("src/Endpoints.php");
|
||||||
require_once Path::root("src/databases/VLWdb.php");
|
require_once Path::root("src/databases/VLWdb.php");
|
||||||
require_once Path::root("src/databases/models/Work.php");
|
require_once Path::root("src/databases/models/Work/Work.php");
|
||||||
require_once Path::root("src/databases/models/WorkPermalinks.php");
|
require_once Path::root("src/databases/models/Work/WorkPermalinks.php");
|
||||||
|
|
||||||
class POST_Work extends VLWdb {
|
class POST_Work extends VLWdb {
|
||||||
protected Ruleset $ruleset;
|
protected Ruleset $ruleset;
|
||||||
|
|
||||||
public function __construct() {
|
public function __construct() {
|
||||||
parent::__construct();
|
|
||||||
$this->ruleset = new Ruleset(strict: true);
|
$this->ruleset = new Ruleset(strict: true);
|
||||||
|
|
||||||
$this->ruleset->POST([
|
$this->ruleset->POST([
|
||||||
|
@ -37,12 +38,22 @@
|
||||||
->max(parent::MYSQL_TEXT_MAX_LENGTH)
|
->max(parent::MYSQL_TEXT_MAX_LENGTH)
|
||||||
->default(null),
|
->default(null),
|
||||||
|
|
||||||
(new Rules(WorkModel::DATE_TIMESTAMP_CREATED->value))
|
(new Rules(WorkModel::IS_LISTABLE->value))
|
||||||
|
->type(Type::BOOLEAN)
|
||||||
|
->default(false),
|
||||||
|
|
||||||
|
(new Rules(WorkModel::IS_READABLE->value))
|
||||||
|
->type(Type::BOOLEAN)
|
||||||
|
->default(false),
|
||||||
|
|
||||||
|
(new Rules(WorkModel::DATE_CREATED->value))
|
||||||
->type(Type::NUMBER)
|
->type(Type::NUMBER)
|
||||||
->min(1)
|
->min(1)
|
||||||
->max(parent::MYSQL_INT_MAX_LENGHT)
|
->max(parent::MYSQL_INT_MAX_LENGHT)
|
||||||
->default(null)
|
->default(time())
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
parent::__construct($this->ruleset);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate a slug URL from string
|
// Generate a slug URL from string
|
||||||
|
@ -50,84 +61,51 @@
|
||||||
return strtolower(trim(preg_replace('/[^A-Za-z0-9-]+/', '-', $input)));
|
return strtolower(trim(preg_replace('/[^A-Za-z0-9-]+/', '-', $input)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create permalink for entity slug
|
// Compute and return modeled year, month, and day from a Unix timestamp
|
||||||
private function create_permalink(string $slug): bool {
|
private static function gen_date_created(): array {
|
||||||
$create = Call("work/permalinks", Method::POST, [
|
// Use provided timestamp in request
|
||||||
WorkPermalinksModel::SLUG->value => $slug,
|
$date_created = $_POST[WorkModel::DATE_CREATED->value];
|
||||||
WorkPermalinksModel::ANCHOR->value => $slug
|
|
||||||
]);
|
|
||||||
|
|
||||||
return $create->ok;
|
return [
|
||||||
|
WorkModel::DATE_YEAR->value => date("Y", $date_created),
|
||||||
|
WorkModel::DATE_MONTH ->value => date("n", $date_created),
|
||||||
|
WorkModel::DATE_DAY->value => date("j", $date_created)
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
// # Responses
|
private function get_entity_by_id(string $id): Response {
|
||||||
|
return (new Call(Endpoints::WORK->value))->params([
|
||||||
// Return 422 Unprocessable Content error if request validation failed
|
WorkModel::ID->value => $id
|
||||||
private function resp_rules_invalid(): Response {
|
])->get();
|
||||||
return new Response($this->ruleset->get_errors(), 422);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return a 503 Service Unavailable error if something went wrong with the database call
|
|
||||||
private function resp_database_error(): Response {
|
|
||||||
return new Response("Failed to get work data, please try again later", 503);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function main(): Response {
|
public function main(): Response {
|
||||||
// Bail out if request validation failed
|
// Use copy of request body as entity
|
||||||
if (!$this->ruleset->is_valid()) {
|
$entity = $_POST;
|
||||||
return $this->resp_rules_invalid();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate URL slug from title text or UUID if undefined
|
// Generate URL slug from title text or UUID if undefined
|
||||||
$slug = !empty($_POST["title"]) ? self::gen_slug($_POST["title"]) : parent::gen_uuid4();
|
$entity[WorkModel::ID->value] = $_POST[WorkModel::TITLE->value]
|
||||||
|
? self::gen_slug($_POST[WorkModel::TITLE->value])
|
||||||
|
: parent::gen_uuid4();
|
||||||
|
|
||||||
// Check if an entity already exists with slugified title from GET endpoint
|
// Bail out here if a work entry with id had been created already
|
||||||
$existing_entity = Call("work?id={$slug}", Method::GET);
|
if ($this->get_entity_by_id($entity[WorkModel::ID->value])->ok) {
|
||||||
// Response is not 404 (Not found) so we can't create the entity
|
return new Response("An entity with id '{$slug}' already exist", 409);
|
||||||
if ($existing_entity->code !== 404) {
|
|
||||||
// Response is not a valid entity, something went wrong
|
|
||||||
if ($existing_entity->code !== 200) {
|
|
||||||
return $this->resp_database_error();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return 402 Conflict
|
// Generate the necessary date fields
|
||||||
return new Response("Entity with id '{$slug}' already exists", 402);
|
array_merge($entity, self::gen_date_created());
|
||||||
|
|
||||||
|
// Let's try to insert the new entity
|
||||||
|
if (!$this->db->for(WorkModel::TABLE)->insert($entity)) {
|
||||||
|
return new Response("Failed to insert work entry", 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get created timestamp from payload or use current time if not specified
|
// Generate permalink for new entity
|
||||||
$created_timestamp = $_POST[WorkModel::DATE_TIMESTAMP_CREATED->value]
|
return (new Call(Endpoints::WORK_PERMALINKS->value))->post([
|
||||||
? $_POST[WorkModel::DATE_TIMESTAMP_CREATED->value]
|
WorkPermalinksModel::ID => $entity[WorkModel::ID->value],
|
||||||
: time();
|
WorkPermalinksModel::REF_WORK_ID => $entity[WorkModel::ID->value],
|
||||||
|
WorkPermalinksModel::DATE_CREATED => time()
|
||||||
// Attempt to create new entity
|
|
||||||
$insert = $this->db->for(WorkModel::TABLE)
|
|
||||||
->insert([
|
|
||||||
WorkModel::ID->value => $slug,
|
|
||||||
WorkModel::TITLE->value => $_POST["title"],
|
|
||||||
WorkModel::SUMMARY->value => $_POST["summary"],
|
|
||||||
WorkModel::IS_LISTABLE->value => true,
|
|
||||||
WorkModel::IS_READABLE->value => true,
|
|
||||||
WorkModel::DATE_YEAR->value => date("Y", $created_timestamp),
|
|
||||||
WorkModel::DATE_MONTH ->value => date("n", $created_timestamp),
|
|
||||||
WorkModel::DATE_DAY->value => date("j", $created_timestamp),
|
|
||||||
WorkModel::DATE_TIMESTAMP_MODIFIED->value => null,
|
|
||||||
WorkModel::DATE_TIMESTAMP_CREATED->value => $created_timestamp,
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Bail out if insert failed
|
|
||||||
if (!$insert) {
|
|
||||||
return $this->resp_database_error();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create permalink for new entity
|
|
||||||
if (!$this->create_permalink($slug)) {
|
|
||||||
// Rollback created entity if permalink creation failed
|
|
||||||
Call("work", Method::DELETE, [WorkModel::ID->value => $slug]);
|
|
||||||
|
|
||||||
return new Response("Failed to create permalink", 500);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return 201 Created and entity slug as body
|
|
||||||
return new Response($slug, 201);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -6,9 +6,7 @@
|
||||||
use ReflectRules\Rules;
|
use ReflectRules\Rules;
|
||||||
use ReflectRules\Ruleset;
|
use ReflectRules\Ruleset;
|
||||||
|
|
||||||
use Reflect\Method;
|
use const VLW\API\RESP_DELETE_OK;
|
||||||
use function Reflect\Call;
|
|
||||||
|
|
||||||
use VLW\API\Databases\VLWdb\VLWdb;
|
use VLW\API\Databases\VLWdb\VLWdb;
|
||||||
use VLW\API\Databases\VLWdb\Models\Work\WorkActionsModel;
|
use VLW\API\Databases\VLWdb\Models\Work\WorkActionsModel;
|
||||||
|
|
||||||
|
@ -23,51 +21,15 @@
|
||||||
$this->ruleset = new Ruleset(strict: true);
|
$this->ruleset = new Ruleset(strict: true);
|
||||||
|
|
||||||
$this->ruleset->POST([
|
$this->ruleset->POST([
|
||||||
(new Rules("id"))
|
(new Rules(WorkActionsModel::REF_WORK_ID->value))
|
||||||
->required()
|
|
||||||
->type(Type::STRING)
|
|
||||||
->min(1)
|
->min(1)
|
||||||
->max(parent::MYSQL_VARCHAR_MAX_LENGTH)
|
->max(parent::MYSQL_VARCHAR_MAX_LENGTH)
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// # Responses
|
|
||||||
|
|
||||||
// Return 422 Unprocessable Content error if request validation failed
|
|
||||||
private function resp_rules_invalid(): Response {
|
|
||||||
return new Response($this->ruleset->get_errors(), 422);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return a 503 Service Unavailable error if something went wrong with the database call
|
|
||||||
private function resp_database_error(): Response {
|
|
||||||
return new Response("Failed to get work data, please try again later", 503);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function main(): Response {
|
public function main(): Response {
|
||||||
// Bail out if request validation failed
|
return $this->db->for(WorkActionsModel::TABLE)->delete($_POST) === true
|
||||||
if (!$this->ruleset->is_valid()) {
|
? new Response(RESP_DELETE_OK)
|
||||||
return $this->resp_rules_invalid();
|
: new Response("Failed to delete action for work entity", 500);
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure the action exists by id
|
|
||||||
$existing_action = $this->db->for(WorkActionsModel::TABLE)
|
|
||||||
->where([
|
|
||||||
WorkActionsModel::ID->value => $_POST["id"]
|
|
||||||
])
|
|
||||||
->select(null);
|
|
||||||
|
|
||||||
// Return idempotent deletion if the action does not exist
|
|
||||||
if ($existing_action->num_rows === 0) {
|
|
||||||
return new Response($_POST["id"]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Attempt to delete action by id
|
|
||||||
$delete = $this->db->for(WorkActionsModel::TABLE)
|
|
||||||
->delete([
|
|
||||||
WorkActionsModel::ID->value => $_POST["id"]
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Return 201 Created and entity id as body if insert was successful
|
|
||||||
return $delete === true ? new Response($_POST["id"], 201) : $this->resp_database_error();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -16,52 +16,31 @@
|
||||||
protected Ruleset $ruleset;
|
protected Ruleset $ruleset;
|
||||||
|
|
||||||
public function __construct() {
|
public function __construct() {
|
||||||
parent::__construct();
|
|
||||||
$this->ruleset = new Ruleset(strict: true);
|
$this->ruleset = new Ruleset(strict: true);
|
||||||
|
|
||||||
$this->ruleset->GET([
|
$this->ruleset->GET([
|
||||||
(new Rules(WorkActionsModel::ANCHOR->value))
|
(new Rules(WorkActionsModel::REF_WORK_ID->value))
|
||||||
->required()
|
|
||||||
->type(Type::STRING)
|
->type(Type::STRING)
|
||||||
->min(1)
|
->min(1)
|
||||||
->max(parent::MYSQL_VARCHAR_MAX_LENGTH)
|
->max(parent::MYSQL_VARCHAR_MAX_LENGTH)
|
||||||
]);
|
]);
|
||||||
}
|
|
||||||
|
|
||||||
// # Responses
|
parent::__construct($this->ruleset);
|
||||||
|
|
||||||
// Return 422 Unprocessable Content error if request validation failed
|
|
||||||
private function resp_rules_invalid(): Response {
|
|
||||||
return new Response($this->ruleset->get_errors(), 422);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return a 503 Service Unavailable error if something went wrong with the database call
|
|
||||||
private function resp_database_error(): Response {
|
|
||||||
return new Response("Failed to get work data, please try again later", 503);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function main(): Response {
|
public function main(): Response {
|
||||||
// Bail out if request validation failed
|
$response = $this->db->for(WorkActionsModel::TABLE)
|
||||||
if (!$this->ruleset->is_valid()) {
|
->where($_GET)
|
||||||
return $this->resp_rules_invalid();
|
|
||||||
}
|
|
||||||
|
|
||||||
$resp = $this->db->for(WorkActionsModel::TABLE)
|
|
||||||
->where([WorkActionsModel::ANCHOR->value => $_GET[WorkActionsModel::ANCHOR->value]])
|
|
||||||
->select([
|
->select([
|
||||||
|
WorkActionsModel::REF_WORK_ID->value,
|
||||||
WorkActionsModel::DISPLAY_TEXT->value,
|
WorkActionsModel::DISPLAY_TEXT->value,
|
||||||
WorkActionsModel::HREF->value,
|
WorkActionsModel::HREF->value,
|
||||||
WorkActionsModel::CLASS_LIST->value,
|
WorkActionsModel::CLASS_LIST->value,
|
||||||
WorkActionsModel::EXTERNAL->value
|
WorkActionsModel::EXTERNAL->value
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Bail out if something went wrong retrieving rows from the database
|
return $response->num_rows > 0
|
||||||
if (!parent::is_mysqli_result($resp)) {
|
? new Response($response->fetch_all(MYSQLI_ASSOC))
|
||||||
return $this->resp_database_error();
|
: new Response([], 404);
|
||||||
}
|
|
||||||
|
|
||||||
return $resp->num_rows > 0
|
|
||||||
? new Response($resp->fetch_all(MYSQLI_ASSOC))
|
|
||||||
: new Response([]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,36 +1,36 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use Reflect\Call;
|
||||||
use Reflect\Path;
|
use Reflect\Path;
|
||||||
use Reflect\Response;
|
use Reflect\Response;
|
||||||
use ReflectRules\Type;
|
use ReflectRules\Type;
|
||||||
use ReflectRules\Rules;
|
use ReflectRules\Rules;
|
||||||
use ReflectRules\Ruleset;
|
use ReflectRules\Ruleset;
|
||||||
|
|
||||||
use Reflect\Method;
|
use VLW\API\Endpoints;
|
||||||
use function Reflect\Call;
|
|
||||||
|
|
||||||
use VLW\API\Databases\VLWdb\VLWdb;
|
use VLW\API\Databases\VLWdb\VLWdb;
|
||||||
use VLW\API\Databases\VLWdb\Models\Work\WorkActionsModel;
|
use VLW\API\Databases\VLWdb\Models\Work\{
|
||||||
|
WorkModel,
|
||||||
|
WorkActionsModel
|
||||||
|
};
|
||||||
|
|
||||||
|
require_once Path::root("src/Endpoints.php");
|
||||||
require_once Path::root("src/databases/VLWdb.php");
|
require_once Path::root("src/databases/VLWdb.php");
|
||||||
require_once Path::root("src/databases/models/WorkActions.php");
|
require_once Path::root("src/databases/models/Work/Work.php");
|
||||||
|
require_once Path::root("src/databases/models/Work/WorkActions.php");
|
||||||
|
|
||||||
class POST_WorkActions extends VLWdb {
|
class POST_WorkActions extends VLWdb {
|
||||||
protected Ruleset $ruleset;
|
protected Ruleset $ruleset;
|
||||||
|
|
||||||
public function __construct() {
|
public function __construct() {
|
||||||
parent::__construct();
|
|
||||||
$this->ruleset = new Ruleset(strict: true);
|
$this->ruleset = new Ruleset(strict: true);
|
||||||
|
|
||||||
$this->ruleset->GET([
|
|
||||||
(new Rules("id"))
|
|
||||||
->required()
|
|
||||||
->type(Type::STRING)
|
|
||||||
->min(1)
|
|
||||||
->max(parent::MYSQL_VARCHAR_MAX_LENGTH)
|
|
||||||
]);
|
|
||||||
|
|
||||||
$this->ruleset->POST([
|
$this->ruleset->POST([
|
||||||
|
(new Rules(WorkActionsModel::REF_WORK_ID->value))
|
||||||
|
->required()
|
||||||
|
->min(1)
|
||||||
|
->max(parent::MYSQL_VARCHAR_MAX_LENGTH),
|
||||||
|
|
||||||
(new Rules(WorkActionsModel::DISPLAY_TEXT->value))
|
(new Rules(WorkActionsModel::DISPLAY_TEXT->value))
|
||||||
->required()
|
->required()
|
||||||
->type(Type::STRING)
|
->type(Type::STRING)
|
||||||
|
@ -47,56 +47,31 @@
|
||||||
(new Rules(WorkActionsModel::CLASS_LIST->value))
|
(new Rules(WorkActionsModel::CLASS_LIST->value))
|
||||||
->type(Type::ARRAY)
|
->type(Type::ARRAY)
|
||||||
->min(1)
|
->min(1)
|
||||||
->max(4)
|
|
||||||
->default([]),
|
->default([]),
|
||||||
|
|
||||||
(new Rules(WorkActionsModel::EXTERNAL->value))
|
(new Rules(WorkActionsModel::EXTERNAL->value))
|
||||||
->type(Type::BOOLEAN)
|
->type(Type::BOOLEAN)
|
||||||
->default(false)
|
->default(false)
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
parent::__construct($this->ruleset);
|
||||||
}
|
}
|
||||||
|
|
||||||
// # Responses
|
private static function get_entity(): Response {
|
||||||
|
return (new Call(Endpoints::WORK->value))->params([
|
||||||
// Return 422 Unprocessable Content error if request validation failed
|
WorkModel::ID->value => $_POST[WorkActionsModel::REF_WORK_ID->value]
|
||||||
private function resp_rules_invalid(): Response {
|
])->get();
|
||||||
return new Response($this->ruleset->get_errors(), 422);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return a 503 Service Unavailable error if something went wrong with the database call
|
|
||||||
private function resp_database_error(): Response {
|
|
||||||
return new Response("Failed to get work data, please try again later", 503);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function main(): Response {
|
public function main(): Response {
|
||||||
// Bail out if request validation failed
|
// Bail out if work entity could not be fetched
|
||||||
if (!$this->ruleset->is_valid()) {
|
$entity = self::get_entity();
|
||||||
return $this->resp_rules_invalid();
|
if (!$entity->ok) {
|
||||||
|
return $entity;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure an entity with the provided id exists
|
return $this->db->for(WorkActionsModel::TABLE)->insert($_POST) === true
|
||||||
$entity = Call("work?id={$_GET["id"]}", Method::GET);
|
? new Response($_POST[WorkActionsModel::REF_WORK_ID->value], 201)
|
||||||
if ($entity->code !== 200) {
|
: new Response("Failed to add action to work entity", 500);
|
||||||
// Response from endpoint is not 404, something went wrong
|
|
||||||
if ($entity->code !== 404) {
|
|
||||||
return $this->resp_database_error();
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Response("No entity with id '{$_GET["id"]}' was found", 404);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Attempt to create action for entity
|
|
||||||
$insert = $this->db->for(WorkActionsModel::TABLE)
|
|
||||||
->insert([
|
|
||||||
WorkActionsModel::ID->value => parent::gen_uuid4(),
|
|
||||||
WorkActionsModel::ANCHOR->value => $_GET["id"],
|
|
||||||
WorkActionsModel::DISPLAY_TEXT->value => $_POST[WorkActionsModel::DISPLAY_TEXT->value],
|
|
||||||
WorkActionsModel::HREF->value => $_POST[WorkActionsModel::HREF->value],
|
|
||||||
WorkActionsModel::CLASS_LIST->value => implode(",", $_POST[WorkActionsModel::CLASS_LIST->value]),
|
|
||||||
WorkActionsModel::EXTERNAL->value => $_POST[WorkActionsModel::EXTERNAL->value],
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Return 201 Created and entity id as body if insert was successful
|
|
||||||
return $insert === true ? new Response($_GET["id"], 201) : $this->resp_database_error();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,6 +1,5 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
|
||||||
use Reflect\Path;
|
use Reflect\Path;
|
||||||
use Reflect\Response;
|
use Reflect\Response;
|
||||||
use ReflectRules\Type;
|
use ReflectRules\Type;
|
||||||
|
@ -8,53 +7,43 @@
|
||||||
use ReflectRules\Ruleset;
|
use ReflectRules\Ruleset;
|
||||||
|
|
||||||
use VLW\API\Databases\VLWdb\VLWdb;
|
use VLW\API\Databases\VLWdb\VLWdb;
|
||||||
use VLW\API\Databases\VLWdb\Models\WorkPermalinks\WorkPermalinksModel;
|
use VLW\API\Databases\VLWdb\Models\Work\WorkPermalinksModel;
|
||||||
|
|
||||||
require_once Path::root("src/databases/VLWdb.php");
|
require_once Path::root("src/databases/VLWdb.php");
|
||||||
require_once Path::root("src/databases/models/WorkPermalinks.php");
|
require_once Path::root("src/databases/models/Work/WorkPermalinks.php");
|
||||||
|
|
||||||
class GET_WorkPermalinks extends VLWdb {
|
class GET_WorkPermalinks extends VLWdb {
|
||||||
protected Ruleset $ruleset;
|
protected Ruleset $ruleset;
|
||||||
|
|
||||||
public function __construct() {
|
public function __construct() {
|
||||||
parent::__construct();
|
|
||||||
$this->ruleset = new Ruleset(strict: true);
|
$this->ruleset = new Ruleset(strict: true);
|
||||||
|
|
||||||
$this->ruleset->GET([
|
$this->ruleset->GET([
|
||||||
(new Rules("id"))
|
(new Rules(WorkPermalinksModel::ID->value))
|
||||||
->required()
|
->type(Type::STRING)
|
||||||
|
->min(1)
|
||||||
|
->max(parent::MYSQL_VARCHAR_MAX_LENGTH),
|
||||||
|
|
||||||
|
(new Rules(WorkPermalinksModel::REF_WORK_ID->value))
|
||||||
->type(Type::STRING)
|
->type(Type::STRING)
|
||||||
->min(1)
|
->min(1)
|
||||||
->max(parent::MYSQL_VARCHAR_MAX_LENGTH)
|
->max(parent::MYSQL_VARCHAR_MAX_LENGTH)
|
||||||
]);
|
]);
|
||||||
}
|
|
||||||
|
|
||||||
// # Responses
|
parent::__construct($this->ruleset);
|
||||||
|
|
||||||
// Return 422 Unprocessable Content error if request validation failed
|
|
||||||
private function resp_rules_invalid(): Response {
|
|
||||||
return new Response($this->ruleset->get_errors(), 422);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return a 503 Service Unavailable error if something went wrong with the database call
|
|
||||||
private function resp_database_error(): Response {
|
|
||||||
return new Response("Failed to resolve permalink, please try again later", 503);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function main(): Response {
|
public function main(): Response {
|
||||||
// Bail out if request validation failed
|
$response = $this->db->for(WorkPermalinksModel::TABLE)
|
||||||
if (!$this->ruleset->is_valid()) {
|
->where($_GET)
|
||||||
return $this->resp_rules_invalid();
|
->select([
|
||||||
}
|
WorkPermalinksModel::ID->value,
|
||||||
|
WorkPermalinksModel::REF_WORK_ID->value,
|
||||||
|
WorkPermalinksModel::DATE_CREATED->value
|
||||||
|
]);
|
||||||
|
|
||||||
// Get all anchors that match the requested slug
|
return $response->num_rows > 0
|
||||||
$resolve = $this->db->for(WorkPermalinksModel::TABLE)
|
? new Response($response->fetch_all(MYSQLI_ASSOC))
|
||||||
->where([WorkPermalinksModel::SLUG->value => $_GET["id"]])
|
: new Response([], 404);
|
||||||
->select(WorkPermalinksModel::ANCHOR->value);
|
|
||||||
|
|
||||||
// Return array of all matched work table ids. Or empty array if none found
|
|
||||||
return parent::is_mysqli_result($resolve)
|
|
||||||
? new Response(array_column($resolve->fetch_all(MYSQLI_ASSOC), WorkPermalinksModel::ANCHOR->value))
|
|
||||||
: $this->resp_database_error();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,83 +1,62 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use Reflect\Call;
|
||||||
use Reflect\Path;
|
use Reflect\Path;
|
||||||
use Reflect\Response;
|
use Reflect\Response;
|
||||||
use ReflectRules\Type;
|
use ReflectRules\Type;
|
||||||
use ReflectRules\Rules;
|
use ReflectRules\Rules;
|
||||||
use ReflectRules\Ruleset;
|
use ReflectRules\Ruleset;
|
||||||
|
|
||||||
use Reflect\Method;
|
|
||||||
use function Reflect\Call;
|
|
||||||
|
|
||||||
use VLW\API\Databases\VLWdb\VLWdb;
|
use VLW\API\Databases\VLWdb\VLWdb;
|
||||||
use VLW\API\Databases\VLWdb\Models\WorkPermalinks\WorkPermalinksModel;
|
use VLW\API\Databases\VLWdb\Models\Work\WorkPermalinksModel;
|
||||||
|
|
||||||
require_once Path::root("src/databases/VLWdb.php");
|
require_once Path::root("src/databases/VLWdb.php");
|
||||||
require_once Path::root("src/databases/models/WorkPermalinks.php");
|
require_once Path::root("src/databases/models/Work/WorkPermalinks.php");
|
||||||
|
|
||||||
class POST_WorkPermalinks extends VLWdb {
|
class POST_WorkPermalinks extends VLWdb {
|
||||||
protected Ruleset $ruleset;
|
protected Ruleset $ruleset;
|
||||||
|
|
||||||
public function __construct() {
|
public function __construct() {
|
||||||
parent::__construct();
|
|
||||||
$this->ruleset = new Ruleset(strict: true);
|
$this->ruleset = new Ruleset(strict: true);
|
||||||
|
|
||||||
$this->ruleset->POST([
|
$this->ruleset->POST([
|
||||||
(new Rules("slug"))
|
(new Rules(WorkPermalinksModel::ID->value))
|
||||||
->required()
|
->required()
|
||||||
->type(Type::STRING)
|
->type(Type::STRING)
|
||||||
->min(1)
|
->min(1)
|
||||||
->max(parent::MYSQL_VARCHAR_MAX_LENGTH),
|
->max(parent::MYSQL_VARCHAR_MAX_LENGTH),
|
||||||
|
|
||||||
(new Rules("anchor"))
|
(new Rules(WorkPermalinksModel::REF_WORK_ID->value))
|
||||||
->required()
|
->required()
|
||||||
->type(Type::STRING)
|
->type(Type::STRING)
|
||||||
->min(1)
|
->min(1)
|
||||||
->max(parent::MYSQL_VARCHAR_MAX_LENGTH)
|
->max(parent::MYSQL_VARCHAR_MAX_LENGTH),
|
||||||
|
|
||||||
|
(new Rules(WorkPermalinksModel::DATE_CREATED->value))
|
||||||
|
->type(Type::NUMBER)
|
||||||
|
->min(1)
|
||||||
|
->max(parent::MYSQL_INT_MAX_LENGHT)
|
||||||
|
->default(time())
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
parent::__construct($this->ruleset);
|
||||||
}
|
}
|
||||||
|
|
||||||
// # Responses
|
private static function get_entity(): Response {
|
||||||
|
return (new Call(Endpoints::WORK->value))->params([
|
||||||
// Return 422 Unprocessable Content error if request validation failed
|
WorkModel::ID->value => $_POST[WorkTagsModel::REF_WORK_ID->value]
|
||||||
private function resp_rules_invalid(): Response {
|
])->get();
|
||||||
return new Response($this->ruleset->get_errors(), 422);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return a 503 Service Unavailable error if something went wrong with the database call
|
|
||||||
private function resp_database_error(): Response {
|
|
||||||
return new Response("Failed to resolve permalink, please try again later", 503);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function main(): Response {
|
public function main(): Response {
|
||||||
// Bail out if request validation failed
|
// Bail out if work entity could not be fetched
|
||||||
if (!$this->ruleset->is_valid()) {
|
$entity = self::get_entity();
|
||||||
return $this->resp_rules_invalid();
|
if (!$entity->ok) {
|
||||||
|
return $entity;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if an entity exists with slug
|
return $this->db->for(WorkPermalinksModel::TABLE)->insert($_POST) === true
|
||||||
$existing_entity = Call("work?id={$_POST["slug"]}", Method::GET);
|
? new Response($_POST[WorkPermalinksModel::ID->value], 201)
|
||||||
// Response is not 404 (Not found) so we can't create the entity
|
: new Response("Failed to add permalink to work entity", 500);
|
||||||
if ($existing_entity->code !== 200) {
|
|
||||||
// Response is not a valid entity, something went wrong
|
|
||||||
if ($existing_entity->code !== 404) {
|
|
||||||
return $this->resp_database_error();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return 402 Conflict
|
|
||||||
return new Response("No work entity with id '{$_POST["slug"]}' was found to permalink", 404);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Attempt to create new entity
|
|
||||||
$insert = $this->db->for(WorkPermalinksModel::TABLE)
|
|
||||||
->insert([
|
|
||||||
WorkPermalinksModel::SLUG->value => $_POST["slug"],
|
|
||||||
WorkPermalinksModel::ANCHOR->value => $_POST["anchor"],
|
|
||||||
WorkPermalinksModel::DATE_TIMESTAMP_CREATED->value => time(),
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Return 201 Created and entity slug as body if insert was successful
|
|
||||||
return $insert === true ? new Response($_POST["slug"], 201) : $this->resp_database_error();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -6,75 +6,34 @@
|
||||||
use ReflectRules\Rules;
|
use ReflectRules\Rules;
|
||||||
use ReflectRules\Ruleset;
|
use ReflectRules\Ruleset;
|
||||||
|
|
||||||
use Reflect\Method;
|
use const VLW\API\RESP_DELETE_OK;
|
||||||
use function Reflect\Call;
|
|
||||||
|
|
||||||
use VLW\API\Databases\VLWdb\VLWdb;
|
use VLW\API\Databases\VLWdb\VLWdb;
|
||||||
use VLW\API\Databases\VLWdb\Models\Work\WorkTagsModel;
|
use VLW\API\Databases\VLWdb\Models\Work\WorkTagsModel;
|
||||||
use VLW\API\Databases\VLWdb\Models\Work\WorkTagsNameEnum;
|
|
||||||
|
|
||||||
require_once Path::root("src/databases/VLWdb.php");
|
require_once Path::root("src/databases/VLWdb.php");
|
||||||
require_once Path::root("src/databases/models/WorkTags.php");
|
require_once Path::root("src/databases/models/Work/WorkTags.php");
|
||||||
|
|
||||||
class DELETE_WorkTags extends VLWdb {
|
class DELETE_WorkTags extends VLWdb {
|
||||||
protected Ruleset $ruleset;
|
private Ruleset $ruleset;
|
||||||
|
|
||||||
public function __construct() {
|
public function __construct() {
|
||||||
parent::__construct();
|
|
||||||
$this->ruleset = new Ruleset(strict: true);
|
$this->ruleset = new Ruleset(strict: true);
|
||||||
|
|
||||||
$this->ruleset->POST([
|
$this->ruleset->GET([
|
||||||
(new Rules("id"))
|
(new Rules(WorkTagsModel::REF_WORK_ID->value))
|
||||||
->required()
|
|
||||||
->type(Type::STRING)
|
|
||||||
->min(1)
|
->min(1)
|
||||||
->max(parent::MYSQL_VARCHAR_MAX_LENGTH),
|
->max(parent::MYSQL_VARCHAR_MAX_LENGTH),
|
||||||
|
|
||||||
(new Rules(WorkTagsModel::NAME->value))
|
(new Rules(WorkTagsModel::NAME->value))
|
||||||
->required()
|
|
||||||
->type(Type::ENUM, WorkTagsNameEnum::names())
|
->type(Type::ENUM, WorkTagsNameEnum::names())
|
||||||
]);
|
]);
|
||||||
}
|
|
||||||
|
|
||||||
// # Responses
|
parent::__construct($this->ruleset);
|
||||||
|
|
||||||
// Return 422 Unprocessable Content error if request validation failed
|
|
||||||
private function resp_rules_invalid(): Response {
|
|
||||||
return new Response($this->ruleset->get_errors(), 422);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return a 503 Service Unavailable error if something went wrong with the database call
|
|
||||||
private function resp_database_error(): Response {
|
|
||||||
return new Response("Failed to get work data, please try again later", 503);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function main(): Response {
|
public function main(): Response {
|
||||||
// Bail out if request validation failed
|
return $this->db->for(WorkTagsModel::TABLE)->delete($_POST) === true
|
||||||
if (!$this->ruleset->is_valid()) {
|
? new Response(RESP_DELETE_OK)
|
||||||
return $this->resp_rules_invalid();
|
: new Response("Failed to delete value from document", 500);
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure the tag exists for entity id
|
|
||||||
$existing_tag = $this->db->for(WorkTagsModel::TABLE)
|
|
||||||
->where([
|
|
||||||
WorkTagsModel::ANCHOR->value => $_POST["id"],
|
|
||||||
WorkTagsModel::NAME->value => $_POST["name"]
|
|
||||||
])
|
|
||||||
->select(null);
|
|
||||||
|
|
||||||
// Return idempotent deletion if the tag does not exist
|
|
||||||
if ($existing_tag->num_rows === 0) {
|
|
||||||
return new Response($_POST["id"]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Attempt to delete tag for entity
|
|
||||||
$delete = $this->db->for(WorkTagsModel::TABLE)
|
|
||||||
->delete([
|
|
||||||
WorkTagsModel::ANCHOR->value => $_POST["id"],
|
|
||||||
WorkTagsModel::NAME->value => $_POST["name"]
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Return 201 Created and entity id as body if insert was successful
|
|
||||||
return $delete === true ? new Response($_POST["id"], 201) : $this->resp_database_error();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
45
api/endpoints/work/tags/GET.php
Normal file
45
api/endpoints/work/tags/GET.php
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Reflect\Path;
|
||||||
|
use Reflect\Response;
|
||||||
|
use ReflectRules\Type;
|
||||||
|
use ReflectRules\Rules;
|
||||||
|
use ReflectRules\Ruleset;
|
||||||
|
|
||||||
|
use VLW\API\Databases\VLWdb\VLWdb;
|
||||||
|
use VLW\API\Databases\VLWdb\Models\Work\WorkTagsModel;
|
||||||
|
|
||||||
|
require_once Path::root("src/databases/VLWdb.php");
|
||||||
|
require_once Path::root("src/databases/models/Work/WorkTags.php");
|
||||||
|
|
||||||
|
class GET_WorkTags extends VLWdb {
|
||||||
|
private Ruleset $ruleset;
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
$this->ruleset = new Ruleset(strict: true);
|
||||||
|
|
||||||
|
$this->ruleset->GET([
|
||||||
|
(new Rules(WorkTagsModel::REF_WORK_ID->value))
|
||||||
|
->min(1)
|
||||||
|
->max(parent::MYSQL_VARCHAR_MAX_LENGTH),
|
||||||
|
|
||||||
|
(new Rules(WorkTagsModel::NAME->value))
|
||||||
|
->type(Type::ENUM, WorkTagsNameEnum::names())
|
||||||
|
]);
|
||||||
|
|
||||||
|
parent::__construct($this->ruleset);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function main(): Response {
|
||||||
|
$response = $this->db->for(FieldsEnumsModel::TABLE)
|
||||||
|
->where($_GET)
|
||||||
|
->select([
|
||||||
|
FieldsEnumsModel::REF_WORK_ID->value,
|
||||||
|
FieldsEnumsModel::NAME->value
|
||||||
|
]);
|
||||||
|
|
||||||
|
return $response->num_rows > 0
|
||||||
|
? new Response($response->fetch_all(MYSQLI_ASSOC))
|
||||||
|
: new Response([], 404);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,93 +1,60 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use Reflect\Call;
|
||||||
use Reflect\Path;
|
use Reflect\Path;
|
||||||
use Reflect\Response;
|
use Reflect\Response;
|
||||||
use ReflectRules\Type;
|
use ReflectRules\Type;
|
||||||
use ReflectRules\Rules;
|
use ReflectRules\Rules;
|
||||||
use ReflectRules\Ruleset;
|
use ReflectRules\Ruleset;
|
||||||
|
|
||||||
use Reflect\Method;
|
use VLW\API\Endpoints;
|
||||||
use function Reflect\Call;
|
|
||||||
|
|
||||||
use VLW\API\Databases\VLWdb\VLWdb;
|
use VLW\API\Databases\VLWdb\VLWdb;
|
||||||
use VLW\API\Databases\VLWdb\Models\Work\WorkTagsModel;
|
use VLW\API\Databases\VLWdb\Models\Work\{
|
||||||
use VLW\API\Databases\VLWdb\Models\Work\WorkTagsNameEnum;
|
WorkModel,
|
||||||
|
WorkTagsModel,
|
||||||
|
WorkTagsNameEnum
|
||||||
|
};
|
||||||
|
|
||||||
|
require_once Path::root("src/Endpoints.php");
|
||||||
require_once Path::root("src/databases/VLWdb.php");
|
require_once Path::root("src/databases/VLWdb.php");
|
||||||
require_once Path::root("src/databases/models/WorkTags.php");
|
require_once Path::root("src/databases/models/Work/Work.php");
|
||||||
|
require_once Path::root("src/databases/models/Work/WorkTags.php");
|
||||||
|
|
||||||
class POST_WorkTags extends VLWdb {
|
class POST_WorkTags extends VLWdb {
|
||||||
protected Ruleset $ruleset;
|
protected Ruleset $ruleset;
|
||||||
|
|
||||||
public function __construct() {
|
public function __construct() {
|
||||||
parent::__construct();
|
|
||||||
$this->ruleset = new Ruleset(strict: true);
|
$this->ruleset = new Ruleset(strict: true);
|
||||||
|
|
||||||
$this->ruleset->GET([
|
|
||||||
(new Rules("id"))
|
|
||||||
->required()
|
|
||||||
->type(Type::STRING)
|
|
||||||
->min(1)
|
|
||||||
->max(parent::MYSQL_VARCHAR_MAX_LENGTH)
|
|
||||||
]);
|
|
||||||
|
|
||||||
$this->ruleset->POST([
|
$this->ruleset->POST([
|
||||||
|
(new Rules(WorkTagsModel::REF_WORK_ID->value))
|
||||||
|
->required()
|
||||||
|
->min(1)
|
||||||
|
->max(parent::MYSQL_VARCHAR_MAX_LENGTH),
|
||||||
|
|
||||||
(new Rules(WorkTagsModel::NAME->value))
|
(new Rules(WorkTagsModel::NAME->value))
|
||||||
->required()
|
->required()
|
||||||
->type(Type::ENUM, WorkTagsNameEnum::names())
|
->type(Type::ENUM, WorkTagsNameEnum::names())
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
parent::__construct($this->ruleset);
|
||||||
}
|
}
|
||||||
|
|
||||||
// # Responses
|
private static function get_entity(): Response {
|
||||||
|
return (new Call(Endpoints::WORK->value))->params([
|
||||||
// Return 422 Unprocessable Content error if request validation failed
|
WorkModel::ID->value => $_POST[WorkTagsModel::REF_WORK_ID->value]
|
||||||
private function resp_rules_invalid(): Response {
|
])->get();
|
||||||
return new Response($this->ruleset->get_errors(), 422);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return a 503 Service Unavailable error if something went wrong with the database call
|
|
||||||
private function resp_database_error(): Response {
|
|
||||||
return new Response("Failed to get work data, please try again later", 503);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function main(): Response {
|
public function main(): Response {
|
||||||
// Bail out if request validation failed
|
// Bail out if work entity could not be fetched
|
||||||
if (!$this->ruleset->is_valid()) {
|
$entity = self::get_entity();
|
||||||
return $this->resp_rules_invalid();
|
if (!$entity->ok) {
|
||||||
|
return $entity;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure an entity with the provided id exists
|
return $this->db->for(WorkTagsModel::TABLE)->insert($_POST) === true
|
||||||
$entity = Call("work?id={$_GET["id"]}", Method::GET);
|
? new Response($_POST[WorkTagsModel::REF_WORK_ID->value], 201)
|
||||||
if ($entity->code !== 200) {
|
: new Response("Failed to add tag to work entity", 500);
|
||||||
// Response from endpoint is not 404, something went wrong
|
|
||||||
if ($entity->code !== 404) {
|
|
||||||
return $this->resp_database_error();
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Response("No entity with id '{$_GET["id"]}' was found", 404);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure the tag does not already exist for entity
|
|
||||||
$existing_tag = $this->db->for(WorkTagsModel::TABLE)
|
|
||||||
->where([
|
|
||||||
WorkTagsModel::ANCHOR->value => $_GET["id"],
|
|
||||||
WorkTagsModel::NAME->value => $_POST["name"]
|
|
||||||
])
|
|
||||||
->select(null);
|
|
||||||
|
|
||||||
// Bail out if this tag already exists
|
|
||||||
if ($existing_tag->num_rows !== 0) {
|
|
||||||
return new Response("Tag '{$_POST["name"]}' is already set on entity id '{$_GET["id"]}'", 402);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Attempt to create tag for entity
|
|
||||||
$insert = $this->db->for(WorkTagsModel::TABLE)
|
|
||||||
->insert([
|
|
||||||
WorkTagsModel::ANCHOR->value => $_GET["id"],
|
|
||||||
WorkTagsModel::NAME->value => $_POST["name"]
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Return 201 Created and entity id as body if insert was successful
|
|
||||||
return $insert === true ? new Response($_GET["id"], 201) : $this->resp_database_error();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
11
api/src/Endpoints.php
Normal file
11
api/src/Endpoints.php
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace VLW\API;
|
||||||
|
|
||||||
|
// Default string to return when a DELETE request is successful
|
||||||
|
const RESP_DELETE_OK = "OK";
|
||||||
|
|
||||||
|
// Enum of all available VLW endpoints grouped by category
|
||||||
|
enum Endpoints: string {
|
||||||
|
case WORK = "/work";
|
||||||
|
}
|
|
@ -2,6 +2,12 @@
|
||||||
|
|
||||||
namespace VLW\API\Databases\VLWdb;
|
namespace VLW\API\Databases\VLWdb;
|
||||||
|
|
||||||
|
use Reflect\ENV;
|
||||||
|
use Reflect\Path;
|
||||||
|
use Reflect\Request;
|
||||||
|
use Reflect\Response;
|
||||||
|
use ReflectRules\Ruleset;
|
||||||
|
|
||||||
use libmysqldriver\MySQL;
|
use libmysqldriver\MySQL;
|
||||||
|
|
||||||
class VLWdb {
|
class VLWdb {
|
||||||
|
@ -11,9 +17,12 @@
|
||||||
const MYSQL_VARCHAR_MAX_LENGTH = 255;
|
const MYSQL_VARCHAR_MAX_LENGTH = 255;
|
||||||
const MYSQL_INT_MAX_LENGHT = 2147483647;
|
const MYSQL_INT_MAX_LENGHT = 2147483647;
|
||||||
|
|
||||||
protected MySQL $db;
|
protected readonly MySQL $db;
|
||||||
|
|
||||||
|
public function __construct(Ruleset $ruleset) {
|
||||||
|
// Validate provided Ruleset before attempting to connect to the database
|
||||||
|
self::eval_ruleset_or_exit($ruleset);
|
||||||
|
|
||||||
public function __construct() {
|
|
||||||
// Create new MariaDB connection
|
// Create new MariaDB connection
|
||||||
$this->db = new MySQL(
|
$this->db = new MySQL(
|
||||||
$_ENV["vlwdb"]["mariadb_host"],
|
$_ENV["vlwdb"]["mariadb_host"],
|
||||||
|
@ -46,7 +55,8 @@
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function is_mysqli_result(\mysqli_result|bool $resp): bool {
|
// Bail out if provided ReflectRules\Ruleset is invalid
|
||||||
return $resp instanceof \mysqli_result;
|
private static function eval_ruleset_or_exit(Ruleset $ruleset): ?Response {
|
||||||
|
return !$ruleset->is_valid() ? new Response($ruleset->get_errors(), 422) : null;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,10 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace VLW\API\Databases\VLWdb\Models\Coffee;
|
|
||||||
|
|
||||||
enum CoffeeModel: string {
|
|
||||||
const TABLE = "coffee";
|
|
||||||
|
|
||||||
case ID = "id";
|
|
||||||
case DATE_TIMESTAMP_CREATED = "date_timestamp_created";
|
|
||||||
}
|
|
|
@ -1,24 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace VLW\API\Databases\VLWdb\Models\Media;
|
|
||||||
|
|
||||||
use victorwesterlund\xEnum;
|
|
||||||
|
|
||||||
enum MediaTypeEnum: string {
|
|
||||||
use xEnum;
|
|
||||||
|
|
||||||
case BLOB = "BLOB";
|
|
||||||
case IMAGE = "IMAGE";
|
|
||||||
}
|
|
||||||
|
|
||||||
enum MediaModel: string {
|
|
||||||
const TABLE = "media";
|
|
||||||
|
|
||||||
case ID = "id";
|
|
||||||
case NAME = "name";
|
|
||||||
case TYPE = "type";
|
|
||||||
case MIME = "mime";
|
|
||||||
case EXTENSION = "extension";
|
|
||||||
case SRCSET = "srcset";
|
|
||||||
case DATE_TIMESTAMP_CREATED = "date_timestamp_created";
|
|
||||||
}
|
|
|
@ -1,10 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace VLW\API\Databases\VLWdb\Models\MediaSrcset;
|
|
||||||
|
|
||||||
enum MediaSrcsetModel: string {
|
|
||||||
const TABLE = "media_srcset";
|
|
||||||
|
|
||||||
case ID = "id";
|
|
||||||
case ANCHOR_DEFAULT = "anchor_default";
|
|
||||||
}
|
|
0
api/src/databases/models/Messages.php → api/src/databases/models/Messages/Messages.php
Executable file → Normal file
0
api/src/databases/models/Messages.php → api/src/databases/models/Messages/Messages.php
Executable file → Normal file
|
@ -1,19 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace VLW\API\Databases\VLWdb\Models\Work;
|
|
||||||
|
|
||||||
enum WorkModel: string {
|
|
||||||
const TABLE = "work";
|
|
||||||
|
|
||||||
case ID = "id";
|
|
||||||
case TITLE = "title";
|
|
||||||
case SUMMARY = "summary";
|
|
||||||
case COVER_SRCSET = "cover_srcset";
|
|
||||||
case IS_LISTABLE = "is_listable";
|
|
||||||
case IS_READABLE = "is_readable";
|
|
||||||
case DATE_YEAR = "date_year";
|
|
||||||
case DATE_MONTH = "date_month";
|
|
||||||
case DATE_DAY = "date_day";
|
|
||||||
case DATE_TIMESTAMP_MODIFIED = "date_timestamp_modified";
|
|
||||||
case DATE_TIMESTAMP_CREATED = "date_timestamp_created";
|
|
||||||
}
|
|
19
api/src/databases/models/Work/Work.php
Normal file
19
api/src/databases/models/Work/Work.php
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace VLW\API\Databases\VLWdb\Models\Work;
|
||||||
|
|
||||||
|
enum WorkModel: string {
|
||||||
|
const TABLE = "work";
|
||||||
|
|
||||||
|
case ID = "id";
|
||||||
|
case TITLE = "title";
|
||||||
|
case SUMMARY = "summary";
|
||||||
|
case COVER_SRCSET = "cover_srcset";
|
||||||
|
case IS_LISTABLE = "is_listable";
|
||||||
|
case IS_READABLE = "is_readable";
|
||||||
|
case DATE_YEAR = "date_year";
|
||||||
|
case DATE_MONTH = "date_month";
|
||||||
|
case DATE_DAY = "date_day";
|
||||||
|
case DATE_MODIFIED = "date_modified";
|
||||||
|
case DATE_CREATED = "date_created";
|
||||||
|
}
|
3
api/src/databases/models/WorkActions.php → api/src/databases/models/Work/WorkActions.php
Executable file → Normal file
3
api/src/databases/models/WorkActions.php → api/src/databases/models/Work/WorkActions.php
Executable file → Normal file
|
@ -5,8 +5,7 @@
|
||||||
enum WorkActionsModel: string {
|
enum WorkActionsModel: string {
|
||||||
const TABLE = "work_actions";
|
const TABLE = "work_actions";
|
||||||
|
|
||||||
case ID = "id";
|
case REF_WORK_ID = "ref_work_id";
|
||||||
case ANCHOR = "anchor";
|
|
||||||
case DISPLAY_TEXT = "display_text";
|
case DISPLAY_TEXT = "display_text";
|
||||||
case HREF = "href";
|
case HREF = "href";
|
||||||
case CLASS_LIST = "class_list";
|
case CLASS_LIST = "class_list";
|
0
api/src/databases/models/WorkMedia.php → api/src/databases/models/Work/WorkMedia.php
Executable file → Normal file
0
api/src/databases/models/WorkMedia.php → api/src/databases/models/Work/WorkMedia.php
Executable file → Normal file
2
api/src/databases/models/WorkPermalinks.php → api/src/databases/models/Work/WorkPermalinks.php
Executable file → Normal file
2
api/src/databases/models/WorkPermalinks.php → api/src/databases/models/Work/WorkPermalinks.php
Executable file → Normal file
|
@ -1,6 +1,6 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace VLW\API\Databases\VLWdb\Models\WorkPermalinks;
|
namespace VLW\API\Databases\VLWdb\Models\Work;
|
||||||
|
|
||||||
enum WorkPermalinksModel: string {
|
enum WorkPermalinksModel: string {
|
||||||
const TABLE = "work_permalinks";
|
const TABLE = "work_permalinks";
|
20
api/src/databases/models/Work/WorkTags.php
Normal file
20
api/src/databases/models/Work/WorkTags.php
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace VLW\API\Databases\VLWdb\Models\Work;
|
||||||
|
|
||||||
|
use victorwesterlund\xEnum;
|
||||||
|
|
||||||
|
enum WorkTagsNameEnum {
|
||||||
|
use xEnum;
|
||||||
|
|
||||||
|
case VLW;
|
||||||
|
case RELEASE;
|
||||||
|
case WEBSITE;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum WorkTagsModel: string {
|
||||||
|
const TABLE = "work_tags";
|
||||||
|
|
||||||
|
case REF_WORK_ID = "ref_work_id";
|
||||||
|
case NAME = "name";
|
||||||
|
}
|
|
@ -1,20 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace VLW\API\Databases\VLWdb\Models\Work;
|
|
||||||
|
|
||||||
use victorwesterlund\xEnum;
|
|
||||||
|
|
||||||
enum WorkTagsNameEnum: string {
|
|
||||||
use xEnum;
|
|
||||||
|
|
||||||
case VLW = "VLW";
|
|
||||||
case RELEASE = "RELEASE";
|
|
||||||
case WEBSITE = "WEBSITE";
|
|
||||||
}
|
|
||||||
|
|
||||||
enum WorkTagsModel: string {
|
|
||||||
const TABLE = "work_tags";
|
|
||||||
|
|
||||||
case ANCHOR = "anchor";
|
|
||||||
case NAME = "name";
|
|
||||||
}
|
|
Loading…
Add table
Reference in a new issue