Multiple fixes
* Implemented ScriptRunner with environment expansion and cleaner code. * Added ApiClient plugin (com.noccy.apiclient) * Renamed CHANGELOG.md to VERSIONS.md * Shuffled buildtools * Added first unittests
This commit is contained in:
84
plugins/com.noccy.apiclient/Api/Catalog.php
Normal file
84
plugins/com.noccy.apiclient/Api/Catalog.php
Normal file
@ -0,0 +1,84 @@
|
||||
<?php // "name":"Call on web APIs", "author":"Noccy"
|
||||
|
||||
namespace SparkPlug\Com\Noccy\ApiClient\Api;
|
||||
|
||||
use JsonSerializable;
|
||||
|
||||
class Catalog implements JsonSerializable
|
||||
{
|
||||
private array $properties = [];
|
||||
|
||||
private array $methods = [];
|
||||
|
||||
private ?string $name;
|
||||
|
||||
private ?string $info;
|
||||
|
||||
public function __construct(array $catalog=[])
|
||||
{
|
||||
$catalog = $catalog['catalog']??[];
|
||||
$this->name = $catalog['name']??null;
|
||||
$this->info = $catalog['info']??null;
|
||||
foreach ($catalog['props']??[] as $k=>$v) {
|
||||
$this->properties[$k] = $v;
|
||||
}
|
||||
foreach ($catalog['methods']??[] as $k=>$v) {
|
||||
$this->methods[$k] = new Method($v);
|
||||
}
|
||||
}
|
||||
|
||||
public static function createFromFile(string $filename): Catalog
|
||||
{
|
||||
$json = file_get_contents($filename);
|
||||
$catalog = json_decode($json, true);
|
||||
$catalog['name'] = basename($filename, ".json");
|
||||
return new Catalog($catalog);
|
||||
}
|
||||
|
||||
public function getName(): ?string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function getInfo(): ?string
|
||||
{
|
||||
return $this->info;
|
||||
}
|
||||
|
||||
public function getProperties(): array
|
||||
{
|
||||
return $this->properties;
|
||||
}
|
||||
|
||||
public function applyProperties(array $props)
|
||||
{
|
||||
$this->properties = array_merge($this->properties, $props);
|
||||
}
|
||||
|
||||
public function addMethod(string $name, Method $method)
|
||||
{
|
||||
$this->methods[$name] = $method;
|
||||
}
|
||||
|
||||
public function getMethod(string $method): ?Method
|
||||
{
|
||||
return $this->methods[$method]??null;
|
||||
}
|
||||
|
||||
public function getMethods(): array
|
||||
{
|
||||
return $this->methods;
|
||||
}
|
||||
|
||||
public function jsonSerialize(): mixed
|
||||
{
|
||||
return [
|
||||
'catalog' => [
|
||||
'name' => $this->name,
|
||||
'info' => $this->info,
|
||||
'props' => $this->properties,
|
||||
'methods' => $this->methods,
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
36
plugins/com.noccy.apiclient/Api/Method.php
Normal file
36
plugins/com.noccy.apiclient/Api/Method.php
Normal file
@ -0,0 +1,36 @@
|
||||
<?php // "name":"Call on web APIs", "author":"Noccy"
|
||||
|
||||
namespace SparkPlug\Com\Noccy\ApiClient\Api;
|
||||
|
||||
use JsonSerializable;
|
||||
|
||||
class Method implements JsonSerializable
|
||||
{
|
||||
private array $properties = [];
|
||||
|
||||
private ?string $info;
|
||||
|
||||
public function __construct(array $method)
|
||||
{
|
||||
$this->properties = $method['props']??[];
|
||||
$this->info = $method['info']??null;
|
||||
}
|
||||
|
||||
public function getProperties(): array
|
||||
{
|
||||
return $this->properties;
|
||||
}
|
||||
|
||||
public function getInfo(): ?string
|
||||
{
|
||||
return $this->info;
|
||||
}
|
||||
|
||||
public function jsonSerialize(): mixed
|
||||
{
|
||||
return [
|
||||
'info' => $this->info,
|
||||
'props' => $this->properties,
|
||||
];
|
||||
}
|
||||
}
|
13
plugins/com.noccy.apiclient/Api/Profile.php
Normal file
13
plugins/com.noccy.apiclient/Api/Profile.php
Normal file
@ -0,0 +1,13 @@
|
||||
<?php // "name":"Call on web APIs", "author":"Noccy"
|
||||
|
||||
namespace SparkPlug\Com\Noccy\ApiClient\Api;
|
||||
|
||||
class Profile
|
||||
{
|
||||
private array $properties = [];
|
||||
|
||||
public function getProperties(): array
|
||||
{
|
||||
return $this->properties;
|
||||
}
|
||||
}
|
91
plugins/com.noccy.apiclient/Commands/ApiCatalogCommand.php
Normal file
91
plugins/com.noccy.apiclient/Commands/ApiCatalogCommand.php
Normal file
@ -0,0 +1,91 @@
|
||||
<?php
|
||||
|
||||
namespace SparkPlug\Com\Noccy\ApiClient\Commands;
|
||||
|
||||
use Spark\Commands\Command;
|
||||
use SparkPlug;
|
||||
use SparkPlug\Com\Noccy\ApiClient\Api\Catalog;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class ApiCatalogCommand extends Command
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName("api:catalog")
|
||||
->setDescription("Manage the API catalogs")
|
||||
->addOption("create", "c", InputOption::VALUE_REQUIRED, "Create a new catalog")
|
||||
->addOption("remove", "r", InputOption::VALUE_REQUIRED, "Remove a catalog")
|
||||
->addOption("set-props", null, InputOption::VALUE_REQUIRED, "Apply properties to a catalog")
|
||||
->addArgument("properties", InputArgument::IS_ARRAY, "Default properties for the catalog")
|
||||
->addOption("list", null, InputOption::VALUE_NONE, "Only list catalogs, not methods")
|
||||
;
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$api = get_plugin('com.noccy.apiclient');
|
||||
$list = $input->getOption("list");
|
||||
$dest = get_environment()->getConfigDirectory() . "/api/catalogs";
|
||||
|
||||
if ($create = $input->getOption("create")) {
|
||||
if (file_exists($dest."/".$create.".json")) {
|
||||
$output->writeln("<error>Catalog {$create} already exists!</>");
|
||||
return Command::FAILURE;
|
||||
}
|
||||
$catalog = new Catalog([
|
||||
'catalog' => [
|
||||
'name' => $create
|
||||
]
|
||||
]);
|
||||
file_put_contents($dest."/".$create.".json", json_encode($catalog, JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES));
|
||||
$output->writeln("<info>Created new catalog {$create}</>");
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
if ($remove = $input->getOption("remove")) {
|
||||
if (!file_exists($dest."/".$remove.".json")) {
|
||||
$output->writeln("<error>Catalog {$remove} does not exist!</>");
|
||||
return Command::FAILURE;
|
||||
}
|
||||
unlink($dest."/".$remove.".json");
|
||||
$output->writeln("<info>Removed catalog {$remove}</>");
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
if ($setprops = $input->getOption("set-props")) {
|
||||
$proparr = [];
|
||||
$props = $input->getArgument("properties");
|
||||
foreach ($props as $str) {
|
||||
if (!str_contains($str,"=")) {
|
||||
$output->writeln("<error>Ignoring parameter argument '{$str}'</>");
|
||||
} else {
|
||||
[$k,$v] = explode("=",$str,2);
|
||||
$proparr[$k] = $v;
|
||||
}
|
||||
}
|
||||
$catalog = $api->getCatalog($setprops);
|
||||
$catalog->applyProperties($proparr);
|
||||
$api->saveCatalog($catalog);
|
||||
$output->writeln("<info>Updated properties on catalog {$setprops}</>");
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
|
||||
$catalogs = $api->getCatalogNames();
|
||||
foreach ($catalogs as $catalog) {
|
||||
$c = $api->getCatalog($catalog);
|
||||
if ($list) {
|
||||
$output->writeln($catalog);
|
||||
} else {
|
||||
$output->writeln("\u{25e9} <options=bold>{$catalog}</>: <fg=gray>{$c->getInfo()}</>");
|
||||
$ms = $c->getMethods();
|
||||
foreach ($ms as $name=>$m) {
|
||||
$last = ($m === end($ms));
|
||||
$output->writeln(($last?"\u{2514}\u{2500}":"\u{251c}\u{2500}")."\u{25a2} {$catalog}.{$name}: <info>{$m->getInfo()}</>");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
}
|
35
plugins/com.noccy.apiclient/Commands/ApiLogsCommand.php
Normal file
35
plugins/com.noccy.apiclient/Commands/ApiLogsCommand.php
Normal file
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace SparkPlug\Com\Noccy\ApiClient\Commands;
|
||||
|
||||
use Spark\Commands\Command;
|
||||
use SparkPlug;
|
||||
use SparkPlug\Com\Noccy\ApiClient\Api\Method;
|
||||
use SparkPlug\Com\Noccy\ApiClient\ApiClientPlugin;
|
||||
use SparkPlug\Com\Noccy\ApiClient\Request\RequestBuilder;
|
||||
use Symfony\Component\Console\Helper\Table;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class ApiLogsCommand extends Command
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName("api:logs")
|
||||
->setDescription("Show previous requests and manage the log")
|
||||
->addOption("clear", null, InputOption::VALUE_NONE, "Clear the log")
|
||||
->addOption("write", "w", InputOption::VALUE_REQUIRED, "Write the formatted entries to a file")
|
||||
;
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
/** @var ApiClientPlugin */
|
||||
$plugin = get_plugin('com.noccy.apiclient');
|
||||
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
}
|
22
plugins/com.noccy.apiclient/Commands/ApiProfileCommand.php
Normal file
22
plugins/com.noccy.apiclient/Commands/ApiProfileCommand.php
Normal file
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace SparkPlug\Com\Noccy\ApiClient\Commands;
|
||||
|
||||
use Spark\Commands\Command;
|
||||
use SparkPlug;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class ApiProfileCommand extends Command
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName("api:profile")
|
||||
->setDescription("Manage API profiles");
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
}
|
134
plugins/com.noccy.apiclient/Commands/ApiRequestCommand.php
Normal file
134
plugins/com.noccy.apiclient/Commands/ApiRequestCommand.php
Normal file
@ -0,0 +1,134 @@
|
||||
<?php
|
||||
|
||||
namespace SparkPlug\Com\Noccy\ApiClient\Commands;
|
||||
|
||||
use Spark\Commands\Command;
|
||||
use SparkPlug;
|
||||
use SparkPlug\Com\Noccy\ApiClient\Api\Method;
|
||||
use SparkPlug\Com\Noccy\ApiClient\ApiClientPlugin;
|
||||
use SparkPlug\Com\Noccy\ApiClient\Request\RequestBuilder;
|
||||
use Symfony\Component\Console\Helper\Table;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class ApiRequestCommand extends Command
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName("api:request")
|
||||
->setDescription("Send a request")
|
||||
->addOption("profile", "p", InputOption::VALUE_REQUIRED, "Use profile for request")
|
||||
->addOption("save", "s", InputOption::VALUE_NONE, "Save to catalog")
|
||||
->addArgument("method", InputArgument::OPTIONAL, "Request URL or catalog.method")
|
||||
->addArgument("props", InputArgument::IS_ARRAY, "Parameter key=value pairs")
|
||||
;
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
/** @var ApiClientPlugin */
|
||||
$plugin = get_plugin('com.noccy.apiclient');
|
||||
|
||||
$separator = str_repeat("\u{2500}", 40);
|
||||
|
||||
$method = $input->getArgument("method");
|
||||
$builder = new RequestBuilder();
|
||||
if (str_contains($method, "://")) {
|
||||
$builder->setProperties([
|
||||
'url' => $method
|
||||
]);
|
||||
} else {
|
||||
if (str_contains($method, '.')) {
|
||||
[$catalog,$method] = explode(".", $method, 2);
|
||||
$catalogObj = $plugin->getCatalog($catalog);
|
||||
// if (!$catalogObj) {
|
||||
// $output->writeln("<error>No such catalog {$catalog}</>");
|
||||
// return Command::FAILURE;
|
||||
// }
|
||||
$methodObj = $catalogObj->getMethod($method);
|
||||
// if (!$methodObj) {
|
||||
// $output->writeln("<error>No such method {$method} in catalog {$catalog}</>");
|
||||
// return Command::FAILURE;
|
||||
// }
|
||||
$builder->setCatalog($catalogObj);
|
||||
$builder->setMethod($methodObj);
|
||||
}
|
||||
}
|
||||
$props = [];
|
||||
$propstr = $input->getArgument("props");
|
||||
foreach ($propstr as $str) {
|
||||
if (!str_contains($str,"=")) {
|
||||
$output->writeln("<error>Ignoring parameter argument '{$str}'</>");
|
||||
} else {
|
||||
[$k,$v] = explode("=",$str,2);
|
||||
$props[$k] = $v;
|
||||
}
|
||||
}
|
||||
$builder->addProperties($props);
|
||||
|
||||
if ($input->getOption("save")) {
|
||||
$catalogObj = $plugin->getCatalog($catalog);
|
||||
$methodObj = new Method([
|
||||
'name' => $method,
|
||||
'info' => $props['method.info']??null,
|
||||
'props' => $props
|
||||
]);
|
||||
|
||||
$catalogObj->addMethod($method, $methodObj);
|
||||
$plugin->saveCatalog($catalogObj);
|
||||
$output->writeln("<info>Saved method {$method} to catalog {$catalog}</>");
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
if ($profile = $input->getOption("profile")) {
|
||||
$profileObj = $plugin->getProfile($profile);
|
||||
$builder->setProfile($profileObj);
|
||||
}
|
||||
|
||||
$request = $builder->getRequest();
|
||||
|
||||
$table = new Table($output);
|
||||
$table->setStyle('compact');
|
||||
$table->setHeaders([ "Request Info", "" ]);
|
||||
foreach ($request->getInfo() as $i=>$v) {
|
||||
$table->addRow([$i,$v]);
|
||||
}
|
||||
$table->render();
|
||||
|
||||
$table = new Table($output);
|
||||
$table->setStyle('compact');
|
||||
$table->setHeaders([ "Request Headers", "" ]);
|
||||
foreach ($request->getHeaders() as $i=>$v) {
|
||||
$table->addRow([$i,join("\n",$v)]);
|
||||
}
|
||||
$table->render();
|
||||
|
||||
$output->writeln($separator);
|
||||
$response = $request->send();
|
||||
|
||||
$rheaders = $response->getHeaders();
|
||||
$table = new Table($output);
|
||||
$table->setStyle('compact');
|
||||
$table->setHeaders([ "Response headers", "" ]);
|
||||
foreach ($rheaders as $h=>$v) {
|
||||
$table->addRow([$h,join("\n",$v)]);
|
||||
}
|
||||
$table->render();
|
||||
$body = (string)$response->getBody();
|
||||
$output->writeln($separator);
|
||||
|
||||
$parseAs = $builder->getCalculatedProperty('response.parse');
|
||||
if ($parseAs == 'json') {
|
||||
dump(json_decode($body));
|
||||
} else {
|
||||
$output->writeln($body);
|
||||
}
|
||||
|
||||
$output->writeln($separator);
|
||||
$output->writeln(strlen($body)." bytes");
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
}
|
73
plugins/com.noccy.apiclient/README.md
Normal file
73
plugins/com.noccy.apiclient/README.md
Normal file
@ -0,0 +1,73 @@
|
||||
# ApiClient for Spark
|
||||
|
||||
## Installation
|
||||
|
||||
To install, downlad and extract the plugin directory into your plugin directory.
|
||||
|
||||
## Usage
|
||||
|
||||
*Note: Profiles are not yet implemented*
|
||||
|
||||
You should make a catalog and a profile first. You don't have to, but this will
|
||||
save you some time.
|
||||
|
||||
$ spark api:catalog --create mysite \
|
||||
protocol=http \
|
||||
urlbase=http://127.0.0.1:80/api/
|
||||
$ spark api:profile --create apiuser \
|
||||
--catalog mysite \
|
||||
auth.username=apiuser \
|
||||
auth.token=APITOKEN
|
||||
|
||||
You can now add some requests:
|
||||
|
||||
$ spark api:request --add mysite.info \
|
||||
url=v1/info \
|
||||
http.method=POST \
|
||||
response.parse=json
|
||||
|
||||
And send them:
|
||||
|
||||
$ spark api:request -p apiuser mysite.info
|
||||
|
||||
## Internals
|
||||
|
||||
ApiClient works on a map of properties, populated with the defaults from the
|
||||
catalog. The request properties are then appied, followed by the profile
|
||||
properties.
|
||||
|
||||
### Properties
|
||||
|
||||
```
|
||||
# Core properties
|
||||
protocol={"http"|"websocket"|"xmlrpc"|"jsonrpc"}
|
||||
# Final URL is [urlbase+]url
|
||||
urlbase={url}
|
||||
url={url}
|
||||
|
||||
# Authentication options
|
||||
auth.username={username}
|
||||
auth.password={password}
|
||||
auth.token={token}
|
||||
auth.type={"basic"|"bearer"}
|
||||
|
||||
# HTTP options
|
||||
http.method={"GET"|"POST"|...}
|
||||
http.version={"1.0"|"1.1"|"2.0"}
|
||||
http.header.{name}={value}
|
||||
http.query.{field}={value}
|
||||
http.body={raw-body}
|
||||
http.body.json={object}
|
||||
|
||||
# RPC options
|
||||
rpc.method={string}
|
||||
rpc.argument.{index}={value}
|
||||
|
||||
# Request handling
|
||||
request.follow-redirecs={"auto"|"no"|"yes"}
|
||||
request.max-redirects={number}
|
||||
|
||||
# Response handling
|
||||
response.parse={"none"|"json"|"yaml"|"xml"}
|
||||
response.good="200,201,202,203,204,205,206,207,208"
|
||||
```
|
91
plugins/com.noccy.apiclient/Request/HttpRequest.php
Normal file
91
plugins/com.noccy.apiclient/Request/HttpRequest.php
Normal file
@ -0,0 +1,91 @@
|
||||
<?php
|
||||
|
||||
namespace SparkPlug\Com\Noccy\ApiClient\Request;
|
||||
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Psr7\Response;
|
||||
|
||||
class HttpRequest extends Request
|
||||
{
|
||||
|
||||
private string $method = 'GET';
|
||||
|
||||
private string $version = '1.1';
|
||||
|
||||
private ?string $url = null;
|
||||
|
||||
private array $query = [];
|
||||
|
||||
private array $headers = [];
|
||||
|
||||
public function __construct(array $props)
|
||||
{
|
||||
foreach ($props as $prop=>$value) {
|
||||
|
||||
if (str_starts_with($prop, 'http.')) {
|
||||
$this->handleHttpProp(substr($prop,5), $value);
|
||||
} elseif ($prop == 'url') {
|
||||
$this->url = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function handleHttpProp(string $prop, $value)
|
||||
{
|
||||
|
||||
if (str_starts_with($prop, 'query.')) {
|
||||
$this->query[substr($prop, 6)] = $value;
|
||||
} elseif (str_starts_with($prop, 'header.')) {
|
||||
$this->headers[substr($prop, 7)] = $value;
|
||||
} elseif ($prop === 'method') {
|
||||
$this->method = strtoupper($value);
|
||||
} elseif ($prop === 'version') {
|
||||
$this->version = $value;
|
||||
} else {
|
||||
fprintf(STDERR, "Warning: unhandled prop: http.%s (%s)\n", $prop, $value);
|
||||
}
|
||||
}
|
||||
|
||||
public function getInfo(): array
|
||||
{
|
||||
$query = http_build_query($this->query);
|
||||
$headers = [];
|
||||
foreach ($this->headers as $k=>$v) {
|
||||
// Convert to Proper-Case unless UPPERCASE
|
||||
if ($k !== strtoupper($k))
|
||||
$k = ucwords($k, '-');
|
||||
// Build the header
|
||||
$headers[] = sprintf("<options=bold>%s</>: %s", $k, $v);
|
||||
}
|
||||
return [
|
||||
'protocol' => sprintf("HTTP/%s %s", $this->version, $this->method),
|
||||
'query' => $this->url . "?" . $query,
|
||||
'body' => "Empty body"
|
||||
];
|
||||
}
|
||||
|
||||
public function getHeaders(): array
|
||||
{
|
||||
$headers = [];
|
||||
foreach ($this->headers as $k=>$v) {
|
||||
// Convert to Proper-Case unless UPPERCASE
|
||||
if ($k !== strtoupper($k))
|
||||
$k = ucwords($k, '-');
|
||||
// Build the header
|
||||
$headers[$k] = (array)$v;
|
||||
}
|
||||
return $headers;
|
||||
}
|
||||
|
||||
public function send(): ?Response
|
||||
{
|
||||
$query = http_build_query($this->query);
|
||||
$url = $this->url . ($query?'?'.$query:'');
|
||||
$config = [];
|
||||
$client = new Client($config);
|
||||
$options = [];
|
||||
$response = $client->request($this->method, $url, $options);
|
||||
return $response;
|
||||
}
|
||||
|
||||
}
|
26
plugins/com.noccy.apiclient/Request/JsonRpcRequest.php
Normal file
26
plugins/com.noccy.apiclient/Request/JsonRpcRequest.php
Normal file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace SparkPlug\Com\Noccy\ApiClient\Request;
|
||||
|
||||
use GuzzleHttp\Psr7\Response;
|
||||
|
||||
class JsonRpcRequest extends Request
|
||||
{
|
||||
|
||||
public function getInfo(): array
|
||||
{
|
||||
return [
|
||||
];
|
||||
}
|
||||
|
||||
public function send(): ?Response
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getHeaders(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
}
|
16
plugins/com.noccy.apiclient/Request/Request.php
Normal file
16
plugins/com.noccy.apiclient/Request/Request.php
Normal file
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace SparkPlug\Com\Noccy\ApiClient\Request;
|
||||
|
||||
use GuzzleHttp\Psr7\Response;
|
||||
|
||||
abstract class Request
|
||||
{
|
||||
|
||||
abstract public function send(): ?Response;
|
||||
|
||||
abstract public function getInfo(): array;
|
||||
|
||||
abstract public function getHeaders(): array;
|
||||
|
||||
}
|
100
plugins/com.noccy.apiclient/Request/RequestBuilder.php
Normal file
100
plugins/com.noccy.apiclient/Request/RequestBuilder.php
Normal file
@ -0,0 +1,100 @@
|
||||
<?php
|
||||
|
||||
namespace SparkPlug\Com\Noccy\ApiClient\Request;
|
||||
|
||||
use SparkPlug\Com\Noccy\ApiClient\Api\Catalog;
|
||||
use SparkPlug\Com\Noccy\ApiClient\Api\Method;
|
||||
use SparkPlug\Com\Noccy\ApiClient\Api\Profile;
|
||||
|
||||
class RequestBuilder
|
||||
{
|
||||
public static $Protocols = [
|
||||
'http' => HttpRequest::class,
|
||||
'websocket' => WebSocketRequest::class,
|
||||
'jsonrpc' => JsonRpcRequest::class,
|
||||
];
|
||||
|
||||
private ?Catalog $catalog = null;
|
||||
|
||||
private ?Method $method = null;
|
||||
|
||||
private ?Profile $profile = null;
|
||||
|
||||
private array $props = [];
|
||||
|
||||
public function setCatalog(?Catalog $catalog)
|
||||
{
|
||||
$this->catalog = $catalog;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setMethod(?Method $method)
|
||||
{
|
||||
$this->method = $method;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setProfile(?Profile $profile)
|
||||
{
|
||||
$this->profile = $profile;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setProperties(array $properties)
|
||||
{
|
||||
$this->props = $properties;
|
||||
}
|
||||
|
||||
public function addProperties(array $properties)
|
||||
{
|
||||
$this->props = array_merge(
|
||||
$this->props,
|
||||
$properties
|
||||
);
|
||||
}
|
||||
|
||||
private function buildProperties()
|
||||
{
|
||||
$props = [];
|
||||
if ($this->catalog) {
|
||||
$add = $this->catalog->getProperties();
|
||||
$props = array_merge($props, $add);
|
||||
}
|
||||
if ($this->method) {
|
||||
$add = $this->method->getProperties();
|
||||
$props = array_merge($props, $add);
|
||||
}
|
||||
if ($this->profile) {
|
||||
$add = $this->profile->getProperties();
|
||||
$props = array_merge($props, $add);
|
||||
}
|
||||
$props = array_merge($props, $this->props);
|
||||
$props = array_filter($props);
|
||||
return $props;
|
||||
}
|
||||
|
||||
public function getCalculatedProperty(string $name)
|
||||
{
|
||||
$props = $this->buildProperties();
|
||||
return $props[$name] ?? null;
|
||||
}
|
||||
|
||||
public function getRequest(): Request
|
||||
{
|
||||
$props = $this->buildProperties();
|
||||
$protocol = $props['protocol']??'http';
|
||||
|
||||
if (!$handler = self::$Protocols[$protocol]??null) {
|
||||
throw new \Exception("Invalid protocol for request: {$protocol}");
|
||||
}
|
||||
|
||||
$base = $props['urlbase']??null;
|
||||
$url = $props['url']??null;
|
||||
if ($base) {
|
||||
$props['url'] = $base . $url;
|
||||
}
|
||||
|
||||
$request = new $handler($props);
|
||||
return $request;
|
||||
}
|
||||
}
|
26
plugins/com.noccy.apiclient/Request/WebsocketRequest.php
Normal file
26
plugins/com.noccy.apiclient/Request/WebsocketRequest.php
Normal file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace SparkPlug\Com\Noccy\ApiClient\Request;
|
||||
|
||||
use GuzzleHttp\Psr7\Response;
|
||||
|
||||
class WebsocketRequest extends Request
|
||||
{
|
||||
|
||||
public function getInfo(): array
|
||||
{
|
||||
return [
|
||||
];
|
||||
}
|
||||
|
||||
public function send(): ?Response
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getHeaders(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
}
|
109
plugins/com.noccy.apiclient/sparkplug.php
Normal file
109
plugins/com.noccy.apiclient/sparkplug.php
Normal file
@ -0,0 +1,109 @@
|
||||
<?php // "name":"Call on web APIs", "author":"Noccy"
|
||||
|
||||
namespace SparkPlug\Com\Noccy\ApiClient;
|
||||
|
||||
use SparkPlug;
|
||||
|
||||
class ApiClientPlugin extends SparkPlug
|
||||
{
|
||||
private array $catalogs = [];
|
||||
|
||||
private array $profiles = [];
|
||||
|
||||
public function load()
|
||||
{
|
||||
register_command(new Commands\ApiCatalogCommand());
|
||||
register_command(new Commands\ApiRequestCommand());
|
||||
register_command(new Commands\ApiProfileCommand());
|
||||
register_command(new Commands\ApiLogsCommand());
|
||||
}
|
||||
|
||||
private function loadCatalogs()
|
||||
{
|
||||
$env = get_environment();
|
||||
$catalogDir = $env->getConfigDirectory() . "/api/catalogs";
|
||||
if (file_exists($catalogDir)) {
|
||||
$catalogFiles = glob($catalogDir."/*.json");
|
||||
foreach ($catalogFiles as $catalogFile) {
|
||||
$name = basename($catalogFile, ".json");
|
||||
$this->catalogs[$name] = Api\Catalog::createFromFile($catalogFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function loadProfiles()
|
||||
{
|
||||
$env = get_environment();
|
||||
$catalogDir = $env->getConfigDirectory() . "/api/profiles";
|
||||
if (file_exists($catalogDir)) {
|
||||
$catalogFiles = glob($catalogDir."/*.json");
|
||||
foreach ($catalogFiles as $catalogFile) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function createCatalog(string $name): ?Api\Catalog
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function saveCatalog(Api\Catalog $catalog)
|
||||
{
|
||||
$env = get_environment();
|
||||
$catalogDir = $env->getConfigDirectory() . "/api/catalogs";
|
||||
$catalogFile = $catalogDir . "/" . $catalog->getName() . ".json";
|
||||
|
||||
if (!is_dir($catalogDir)) {
|
||||
mkdir($catalogDir, 0777, true);
|
||||
}
|
||||
|
||||
file_put_contents($catalogFile."~", json_encode($catalog, JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES));
|
||||
rename($catalogFile."~", $catalogFile);
|
||||
}
|
||||
|
||||
public function deleteCatalog(string $name)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function getCatalog(string $name): ?Api\Catalog
|
||||
{
|
||||
if (empty($this->catalogs)) $this->loadCatalogs();
|
||||
|
||||
return $this->catalogs[$name]??null;
|
||||
}
|
||||
|
||||
public function getCatalogNames(): array
|
||||
{
|
||||
if (empty($this->catalogs)) $this->loadCatalogs();
|
||||
|
||||
return array_keys($this->catalogs);
|
||||
}
|
||||
|
||||
public function saveProfile(string $name, Api\Profile $profile)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function deleteProfile(string $name)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function getProfile(string $name): ?Api\Profile
|
||||
{
|
||||
if (empty($this->profiles)) $this->loadProfiles();
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getProfileNames(): array
|
||||
{
|
||||
if (empty($this->profiles)) $this->loadProfiles();
|
||||
|
||||
return array_keys($this->profiles);
|
||||
}
|
||||
}
|
||||
|
||||
register_plugin("com.noccy.apiclient", new ApiClientPlugin);
|
@ -65,7 +65,7 @@ class DockerDbExportCommand extends Command
|
||||
$cmd = sprintf("mysqldump -u%s -p%s %s", $dbuser, $dbpass, $database);
|
||||
break;
|
||||
}
|
||||
$this->exportFromService($service, $cmd, $output);
|
||||
$this->exportFromService($service, $cmd, $output, $input->getOption("output"));
|
||||
} elseif ($dsn) {
|
||||
$url = parse_url($dsn);
|
||||
if (empty($url)) {
|
||||
@ -77,9 +77,13 @@ class DockerDbExportCommand extends Command
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
|
||||
private function exportFromService(string $service, string $command, OutputInterface $output)
|
||||
private function exportFromService(string $service, string $command, OutputInterface $output, ?string $outfile=null)
|
||||
{
|
||||
$cmd = sprintf("docker-compose exec -T %s %s", $service, $command);
|
||||
if ($outfile) {
|
||||
$cmd .= " > ".escapeshellarg($outfile);
|
||||
}
|
||||
$output->writeln(sprintf("→ <info>%s</>", $cmd));
|
||||
passthru($cmd);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user