experimental graphql api
This commit is contained in:
parent
8a6d6d437e
commit
e79470d974
8 changed files with 226 additions and 8 deletions
|
@ -4,6 +4,8 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace Shimmie2;
|
namespace Shimmie2;
|
||||||
|
|
||||||
|
use GQLA\Expose;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class Image
|
* Class Image
|
||||||
*
|
*
|
||||||
|
@ -13,18 +15,27 @@ namespace Shimmie2;
|
||||||
* image per se, but could be a video, sound file, or any
|
* image per se, but could be a video, sound file, or any
|
||||||
* other supported upload type.
|
* other supported upload type.
|
||||||
*/
|
*/
|
||||||
|
#[Expose(name: "Post")]
|
||||||
class Image
|
class Image
|
||||||
{
|
{
|
||||||
public const IMAGE_DIR = "images";
|
public const IMAGE_DIR = "images";
|
||||||
public const THUMBNAIL_DIR = "thumbs";
|
public const THUMBNAIL_DIR = "thumbs";
|
||||||
|
|
||||||
|
#[Expose]
|
||||||
public ?int $id = null;
|
public ?int $id = null;
|
||||||
|
#[Expose]
|
||||||
public int $height = 0;
|
public int $height = 0;
|
||||||
|
#[Expose]
|
||||||
public int $width = 0;
|
public int $width = 0;
|
||||||
|
#[Expose]
|
||||||
public string $hash;
|
public string $hash;
|
||||||
|
#[Expose]
|
||||||
public int $filesize;
|
public int $filesize;
|
||||||
|
#[Expose]
|
||||||
public string $filename;
|
public string $filename;
|
||||||
|
#[Expose]
|
||||||
private string $ext;
|
private string $ext;
|
||||||
|
#[Expose]
|
||||||
private string $mime;
|
private string $mime;
|
||||||
|
|
||||||
/** @var ?string[] */
|
/** @var ?string[] */
|
||||||
|
@ -73,6 +84,7 @@ class Image
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[Expose(extends: "Query", name: "post_by_id")]
|
||||||
public static function by_id(int $id): ?Image
|
public static function by_id(int $id): ?Image
|
||||||
{
|
{
|
||||||
global $database;
|
global $database;
|
||||||
|
@ -142,7 +154,8 @@ class Image
|
||||||
* #param string[] $tags
|
* #param string[] $tags
|
||||||
* #return Image[]
|
* #return Image[]
|
||||||
*/
|
*/
|
||||||
public static function find_images(int $start, ?int $limit = null, array $tags=[]): array
|
#[Expose(extends: "Query", name: "posts", type: "[Post]", args: ["tags" => "[string]"])]
|
||||||
|
public static function find_images(?int $start = 0, ?int $limit = null, array $tags=[]): array
|
||||||
{
|
{
|
||||||
$result = self::find_images_internal($start, $limit, $tags);
|
$result = self::find_images_internal($start, $limit, $tags);
|
||||||
|
|
||||||
|
@ -349,6 +362,7 @@ class Image
|
||||||
/**
|
/**
|
||||||
* Find the User who owns this Image
|
* Find the User who owns this Image
|
||||||
*/
|
*/
|
||||||
|
#[Expose(name: "owner")]
|
||||||
public function get_owner(): User
|
public function get_owner(): User
|
||||||
{
|
{
|
||||||
return User::by_id($this->owner_id);
|
return User::by_id($this->owner_id);
|
||||||
|
@ -449,6 +463,7 @@ class Image
|
||||||
*
|
*
|
||||||
* #return string[]
|
* #return string[]
|
||||||
*/
|
*/
|
||||||
|
#[Expose(name: "tags", type: "[string]")]
|
||||||
public function get_tag_array(): array
|
public function get_tag_array(): array
|
||||||
{
|
{
|
||||||
global $database;
|
global $database;
|
||||||
|
@ -476,6 +491,7 @@ class Image
|
||||||
/**
|
/**
|
||||||
* Get the URL for the full size image
|
* Get the URL for the full size image
|
||||||
*/
|
*/
|
||||||
|
#[Expose(name: "image_link")]
|
||||||
public function get_image_link(): string
|
public function get_image_link(): string
|
||||||
{
|
{
|
||||||
return $this->get_link(ImageConfig::ILINK, '_images/$hash/$id%20-%20$tags.$ext', 'image/$id.$ext');
|
return $this->get_link(ImageConfig::ILINK, '_images/$hash/$id%20-%20$tags.$ext', 'image/$id.$ext');
|
||||||
|
@ -484,6 +500,7 @@ class Image
|
||||||
/**
|
/**
|
||||||
* Get the nicely formatted version of the file name
|
* Get the nicely formatted version of the file name
|
||||||
*/
|
*/
|
||||||
|
#[Expose(name: "nice_name")]
|
||||||
public function get_nice_image_name(): string
|
public function get_nice_image_name(): string
|
||||||
{
|
{
|
||||||
$plte = new ParseLinkTemplateEvent('$id - $tags.$ext', $this);
|
$plte = new ParseLinkTemplateEvent('$id - $tags.$ext', $this);
|
||||||
|
@ -494,6 +511,7 @@ class Image
|
||||||
/**
|
/**
|
||||||
* Get the URL for the thumbnail
|
* Get the URL for the thumbnail
|
||||||
*/
|
*/
|
||||||
|
#[Expose(name: "thumb_link")]
|
||||||
public function get_thumb_link(): string
|
public function get_thumb_link(): string
|
||||||
{
|
{
|
||||||
global $config;
|
global $config;
|
||||||
|
@ -528,6 +546,7 @@ class Image
|
||||||
* Get the tooltip for this image, formatted according to the
|
* Get the tooltip for this image, formatted according to the
|
||||||
* configured template.
|
* configured template.
|
||||||
*/
|
*/
|
||||||
|
#[Expose(name: "tooltip")]
|
||||||
public function get_tooltip(): string
|
public function get_tooltip(): string
|
||||||
{
|
{
|
||||||
global $config;
|
global $config;
|
||||||
|
@ -540,6 +559,7 @@ class Image
|
||||||
* Get the info for this image, formatted according to the
|
* Get the info for this image, formatted according to the
|
||||||
* configured template.
|
* configured template.
|
||||||
*/
|
*/
|
||||||
|
#[Expose(name: "info")]
|
||||||
public function get_info(): string
|
public function get_info(): string
|
||||||
{
|
{
|
||||||
global $config;
|
global $config;
|
||||||
|
|
|
@ -4,6 +4,75 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace Shimmie2;
|
namespace Shimmie2;
|
||||||
|
|
||||||
|
use GQLA\Expose;
|
||||||
|
|
||||||
|
#[Expose(name: "TagUsage")]
|
||||||
|
class TagUsage {
|
||||||
|
#[Expose]
|
||||||
|
public string $tag;
|
||||||
|
#[Expose]
|
||||||
|
public int $uses;
|
||||||
|
|
||||||
|
public function __construct(string $tag, int $uses)
|
||||||
|
{
|
||||||
|
$this->tag = $tag;
|
||||||
|
$this->uses = $uses;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Expose(extends: "Query", name: "tags", type: '[TagUsage]')]
|
||||||
|
public static function tags(string $search, int $limit=10): array {
|
||||||
|
global $cache, $database;
|
||||||
|
|
||||||
|
if (!$search) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$search = strtolower($search);
|
||||||
|
if (
|
||||||
|
$search == '' ||
|
||||||
|
$search[0] == '_' ||
|
||||||
|
$search[0] == '%' ||
|
||||||
|
strlen($search) > 32
|
||||||
|
) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$cache_key = "tagusage-$search";
|
||||||
|
$limitSQL = "";
|
||||||
|
$search = str_replace('_', '\_', $search);
|
||||||
|
$search = str_replace('%', '\%', $search);
|
||||||
|
$SQLarr = ["search"=>"$search%"]; #, "cat_search"=>"%:$search%"];
|
||||||
|
if ($limit !== 0) {
|
||||||
|
$limitSQL = "LIMIT :limit";
|
||||||
|
$SQLarr['limit'] = $limit;
|
||||||
|
$cache_key .= "-" . $limit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$res = $cache->get($cache_key);
|
||||||
|
if (!$res) {
|
||||||
|
$res = $database->get_pairs(
|
||||||
|
"
|
||||||
|
SELECT tag, count
|
||||||
|
FROM tags
|
||||||
|
WHERE LOWER(tag) LIKE LOWER(:search)
|
||||||
|
-- OR LOWER(tag) LIKE LOWER(:cat_search)
|
||||||
|
AND count > 0
|
||||||
|
ORDER BY count DESC
|
||||||
|
$limitSQL
|
||||||
|
",
|
||||||
|
$SQLarr
|
||||||
|
);
|
||||||
|
$cache->set($cache_key, $res, 600);
|
||||||
|
}
|
||||||
|
|
||||||
|
$counts = [];
|
||||||
|
foreach($res as $k => $v) {
|
||||||
|
$counts[] = new TagUsage($k, $v);
|
||||||
|
}
|
||||||
|
return $counts;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class Tag
|
* Class Tag
|
||||||
*
|
*
|
||||||
|
|
|
@ -4,6 +4,8 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace Shimmie2;
|
namespace Shimmie2;
|
||||||
|
|
||||||
|
use GQLA\Expose;
|
||||||
|
|
||||||
function _new_user(array $row): User
|
function _new_user(array $row): User
|
||||||
{
|
{
|
||||||
return new User($row);
|
return new User($row);
|
||||||
|
@ -17,9 +19,12 @@ function _new_user(array $row): User
|
||||||
*
|
*
|
||||||
* The currently logged in user will always be accessible via the global variable $user.
|
* The currently logged in user will always be accessible via the global variable $user.
|
||||||
*/
|
*/
|
||||||
|
#[Expose(name: "User")]
|
||||||
class User
|
class User
|
||||||
{
|
{
|
||||||
|
#[Expose]
|
||||||
public int $id;
|
public int $id;
|
||||||
|
#[Expose]
|
||||||
public string $name;
|
public string $name;
|
||||||
public ?string $email;
|
public ?string $email;
|
||||||
public string $join_date;
|
public string $join_date;
|
||||||
|
@ -58,6 +63,13 @@ class User
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[Expose(extends: "Query")]
|
||||||
|
public static function me(): User
|
||||||
|
{
|
||||||
|
global $user;
|
||||||
|
return $user;
|
||||||
|
}
|
||||||
|
|
||||||
public static function by_session(string $name, string $session): ?User
|
public static function by_session(string $name, string $session): ?User
|
||||||
{
|
{
|
||||||
global $cache, $config, $database;
|
global $cache, $config, $database;
|
||||||
|
|
|
@ -4,6 +4,8 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace Shimmie2;
|
namespace Shimmie2;
|
||||||
|
|
||||||
|
use GQLA\Expose;
|
||||||
|
|
||||||
require_once "vendor/ifixit/php-akismet/akismet.class.php";
|
require_once "vendor/ifixit/php-akismet/akismet.class.php";
|
||||||
|
|
||||||
class CommentPostingEvent extends Event
|
class CommentPostingEvent extends Event
|
||||||
|
@ -41,6 +43,7 @@ class CommentPostingException extends SCoreException
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[Expose(name: "Comment")]
|
||||||
class Comment
|
class Comment
|
||||||
{
|
{
|
||||||
public ?User $owner;
|
public ?User $owner;
|
||||||
|
@ -48,10 +51,12 @@ class Comment
|
||||||
public string $owner_name;
|
public string $owner_name;
|
||||||
public ?string $owner_email;
|
public ?string $owner_email;
|
||||||
public string $owner_class;
|
public string $owner_class;
|
||||||
|
#[Expose]
|
||||||
public string $comment;
|
public string $comment;
|
||||||
public int $comment_id;
|
public int $comment_id;
|
||||||
public int $image_id;
|
public int $image_id;
|
||||||
public string $poster_ip;
|
public string $poster_ip;
|
||||||
|
#[Expose]
|
||||||
public string $posted;
|
public string $posted;
|
||||||
|
|
||||||
public function __construct($row)
|
public function __construct($row)
|
||||||
|
@ -78,6 +83,7 @@ class Comment
|
||||||
", ["owner_id"=>$user->id]);
|
", ["owner_id"=>$user->id]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[Expose(name: "owner")]
|
||||||
public function get_owner(): User
|
public function get_owner(): User
|
||||||
{
|
{
|
||||||
if (empty($this->owner)) {
|
if (empty($this->owner)) {
|
||||||
|
@ -85,6 +91,18 @@ class Comment
|
||||||
}
|
}
|
||||||
return $this->owner;
|
return $this->owner;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[Expose(extends: "Post", name: "comments", type: "[Comment]")]
|
||||||
|
public function get_comments(Image $post): array {
|
||||||
|
return CommentList::get_comments($post->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Expose(extends: "Mutation", name: "create_comment")]
|
||||||
|
public function create_comment(int $post_id, string $comment): bool {
|
||||||
|
global $user;
|
||||||
|
send_event(new CommentPostingEvent($post_id, $user, $comment));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class CommentList extends Extension
|
class CommentList extends Extension
|
||||||
|
@ -427,7 +445,7 @@ class CommentList extends Extension
|
||||||
/**
|
/**
|
||||||
* #return Comment[]
|
* #return Comment[]
|
||||||
*/
|
*/
|
||||||
private function get_generic_comments(string $query, array $args): array
|
private static function get_generic_comments(string $query, array $args): array
|
||||||
{
|
{
|
||||||
global $database;
|
global $database;
|
||||||
$rows = $database->get_all($query, $args);
|
$rows = $database->get_all($query, $args);
|
||||||
|
@ -441,9 +459,9 @@ class CommentList extends Extension
|
||||||
/**
|
/**
|
||||||
* #return Comment[]
|
* #return Comment[]
|
||||||
*/
|
*/
|
||||||
private function get_recent_comments(int $count): array
|
private static function get_recent_comments(int $count): array
|
||||||
{
|
{
|
||||||
return $this->get_generic_comments("
|
return CommentList::get_generic_comments("
|
||||||
SELECT
|
SELECT
|
||||||
users.id as user_id, users.name as user_name, users.email as user_email, users.class as user_class,
|
users.id as user_id, users.name as user_name, users.email as user_email, users.class as user_class,
|
||||||
comments.comment as comment, comments.id as comment_id,
|
comments.comment as comment, comments.id as comment_id,
|
||||||
|
@ -459,9 +477,9 @@ class CommentList extends Extension
|
||||||
/**
|
/**
|
||||||
* #return Comment[]
|
* #return Comment[]
|
||||||
*/
|
*/
|
||||||
private function get_user_comments(int $user_id, int $count, int $offset=0): array
|
private static function get_user_comments(int $user_id, int $count, int $offset=0): array
|
||||||
{
|
{
|
||||||
return $this->get_generic_comments("
|
return CommentList::get_generic_comments("
|
||||||
SELECT
|
SELECT
|
||||||
users.id as user_id, users.name as user_name, users.email as user_email, users.class as user_class,
|
users.id as user_id, users.name as user_name, users.email as user_email, users.class as user_class,
|
||||||
comments.comment as comment, comments.id as comment_id,
|
comments.comment as comment, comments.id as comment_id,
|
||||||
|
@ -476,11 +494,12 @@ class CommentList extends Extension
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* public just for Image::get_comments()
|
||||||
* #return Comment[]
|
* #return Comment[]
|
||||||
*/
|
*/
|
||||||
private function get_comments(int $image_id): array
|
public static function get_comments(int $image_id): array
|
||||||
{
|
{
|
||||||
return $this->get_generic_comments("
|
return CommentList::get_generic_comments("
|
||||||
SELECT
|
SELECT
|
||||||
users.id as user_id, users.name as user_name, users.email as user_email, users.class as user_class,
|
users.id as user_id, users.name as user_name, users.email as user_email, users.class as user_class,
|
||||||
comments.comment as comment, comments.id as comment_id,
|
comments.comment as comment, comments.id as comment_id,
|
||||||
|
|
17
ext/graphql/info.php
Normal file
17
ext/graphql/info.php
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Shimmie2;
|
||||||
|
|
||||||
|
class GraphQLInfo extends ExtensionInfo
|
||||||
|
{
|
||||||
|
public const KEY = "graphql";
|
||||||
|
|
||||||
|
public string $key = self::KEY;
|
||||||
|
public string $name = "GraphQL";
|
||||||
|
public string $url = self::SHIMMIE_URL;
|
||||||
|
public array $authors = self::SHISH_AUTHOR;
|
||||||
|
public string $license = self::LICENSE_GPLV2;
|
||||||
|
public string $description = "Add a graphql API";
|
||||||
|
}
|
60
ext/graphql/main.php
Normal file
60
ext/graphql/main.php
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Shimmie2;
|
||||||
|
|
||||||
|
use GraphQL\GraphQL as GQL;
|
||||||
|
use GraphQL\Server\StandardServer;
|
||||||
|
use GraphQL\Error\DebugFlag;
|
||||||
|
use GraphQL\Utils\SchemaPrinter;
|
||||||
|
|
||||||
|
class GraphQL extends Extension
|
||||||
|
{
|
||||||
|
public function onPageRequest(PageRequestEvent $event)
|
||||||
|
{
|
||||||
|
global $page;
|
||||||
|
if($event->page_matches("graphql")) {
|
||||||
|
$t1 = ftime();
|
||||||
|
$server = new StandardServer([
|
||||||
|
'schema' => \GQLA\genSchema(),
|
||||||
|
]);
|
||||||
|
$t2 = ftime();
|
||||||
|
$resp = $server->executeRequest();
|
||||||
|
$body = $resp->toArray();
|
||||||
|
$t3 = ftime();
|
||||||
|
$body['stats'] = get_debug_info_arr();
|
||||||
|
$body['stats']['graphql_schema_time'] = round($t2 - $t1, 2);
|
||||||
|
$body['stats']['graphql_execute_time'] = round($t3 - $t2, 2);
|
||||||
|
$page->set_mode(PageMode::DATA);
|
||||||
|
$page->set_mime("application/json");
|
||||||
|
$page->set_data(\json_encode($body, JSON_UNESCAPED_UNICODE));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onCommand(CommandEvent $event)
|
||||||
|
{
|
||||||
|
if ($event->cmd == "help") {
|
||||||
|
print "\tgraphql <query string>\n";
|
||||||
|
print "\t\teg 'graphql \"{ post_by_id(id: 18) { id, hash } }\"'\n\n";
|
||||||
|
print "\tgraphql-schema\n";
|
||||||
|
print "\t\tdump the schema\n\n";
|
||||||
|
}
|
||||||
|
if ($event->cmd == "graphql") {
|
||||||
|
$t1 = ftime();
|
||||||
|
$schema = \GQLA\genSchema();
|
||||||
|
$t2 = ftime();
|
||||||
|
$debug = DebugFlag::INCLUDE_DEBUG_MESSAGE | DebugFlag::RETHROW_INTERNAL_EXCEPTIONS;
|
||||||
|
$body = GQL::executeQuery($schema, $event->args[0])->toArray($debug);
|
||||||
|
$t3 = ftime();
|
||||||
|
$body['stats'] = get_debug_info_arr();
|
||||||
|
$body['stats']['graphql_schema_time'] = round($t2 - $t1, 2);
|
||||||
|
$body['stats']['graphql_execute_time'] = round($t3 - $t2, 2);
|
||||||
|
echo \json_encode($body, JSON_UNESCAPED_UNICODE|JSON_PRETTY_PRINT);
|
||||||
|
}
|
||||||
|
if ($event->cmd == "graphql-schema") {
|
||||||
|
$schema = \GQLA\genSchema();
|
||||||
|
echo(SchemaPrinter::doPrint($schema));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
14
ext/graphql/test.php
Normal file
14
ext/graphql/test.php
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Shimmie2;
|
||||||
|
|
||||||
|
class GraphQLTest extends ShimmiePHPUnitTestCase
|
||||||
|
{
|
||||||
|
public function testSchema()
|
||||||
|
{
|
||||||
|
$schema = \GQLA\genSchema();
|
||||||
|
$schema->assertValid();
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,6 +4,8 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace Shimmie2;
|
namespace Shimmie2;
|
||||||
|
|
||||||
|
use GQLA\Expose;
|
||||||
|
|
||||||
class SendPMEvent extends Event
|
class SendPMEvent extends Event
|
||||||
{
|
{
|
||||||
public PM $pm;
|
public PM $pm;
|
||||||
|
@ -15,16 +17,21 @@ class SendPMEvent extends Event
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[Expose]
|
||||||
class PM
|
class PM
|
||||||
{
|
{
|
||||||
|
#[Expose]
|
||||||
public int $id;
|
public int $id;
|
||||||
public int $from_id;
|
public int $from_id;
|
||||||
public string $from_ip;
|
public string $from_ip;
|
||||||
public int $to_id;
|
public int $to_id;
|
||||||
/** @var mixed */
|
/** @var mixed */
|
||||||
public $sent_date;
|
public $sent_date;
|
||||||
|
#[Expose]
|
||||||
public string $subject;
|
public string $subject;
|
||||||
|
#[Expose]
|
||||||
public string $message;
|
public string $message;
|
||||||
|
#[Expose]
|
||||||
public bool $is_read;
|
public bool $is_read;
|
||||||
|
|
||||||
public function __construct($from_id=0, string $from_ip="0.0.0.0", int $to_id=0, string $subject="A Message", string $message="Some Text", bool $read=false)
|
public function __construct($from_id=0, string $from_ip="0.0.0.0", int $to_id=0, string $subject="A Message", string $message="Some Text", bool $read=false)
|
||||||
|
|
Reference in a new issue