Skip to content

Commit

Permalink
Update README.md
Browse files Browse the repository at this point in the history
  • Loading branch information
matapatos committed May 15, 2024
1 parent 18439e3 commit f52d5a3
Show file tree
Hide file tree
Showing 16 changed files with 244 additions and 338 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# WP-FastEndpoints

<img src="https://raw.githubusercontent.com/matapatos/wp-fastendpoints/main/docs/images/wp-fastendpoints-wallpaper.svg" alt="WordPress REST endpoints made easy">
<img src="https://raw.githubusercontent.com/matapatos/wp-fastendpoints/main/docs/images/wp-fastendpoints-wallpaper.png" alt="WordPress REST endpoints made easy">
<p align="center">
<a href="https://github.com/matapatos/wp-fastendpoints/actions"><img alt="GitHub Actions Workflow Status (main)" src="https://img.shields.io/github/actions/workflow/status/matapatos/wp-fastendpoints/tests.yml"></a>
<a href="https://codecov.io/gh/matapatos/wp-fastendpoints" ><img alt="Code Coverage" src="https://codecov.io/gh/matapatos/wp-fastendpoints/graph/badge.svg?token=8N7N9NMGLG"/></a>
Expand Down
Binary file modified docs/images/wp-fastendpoints-wallpaper.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 1 addition & 2 deletions src/Contracts/Endpoint.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,9 @@ interface Endpoint
*
* @param string $namespace WordPress REST namespace.
* @param string $restBase Endpoint REST base.
* @param array<string> $schemaDirs Array of directories to look for JSON schemas. Default value: [].
* @return true|false true if successfully registered a REST route or false otherwise.
*/
public function register(string $namespace, string $restBase, array $schemaDirs = []): bool;
public function register(string $namespace, string $restBase): bool;

/**
* Checks if the current user has the given WP capabilities. Example usage:
Expand Down
185 changes: 29 additions & 156 deletions src/Contracts/JsonSchema.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,12 @@

namespace Wp\FastEndpoints\Contracts;

use Exception;
use Opis\JsonSchema\Errors\ErrorFormatter;
use Opis\JsonSchema\Resolvers\SchemaResolver;
use Opis\JsonSchema\Parsers\SchemaParser;
use Opis\JsonSchema\SchemaLoader;
use Opis\JsonSchema\ValidationResult;
use Opis\JsonSchema\Validator;
use TypeError;
use Wp\FastEndpoints\Helpers\Arr;
use Wp\FastEndpoints\Schemas\Opis\Parsers\ResponseSchemaParser;
use Wp\FastEndpoints\Schemas\SchemaResolver;

/**
* Abstract class that holds logic to search and retrieve the contents of a
Expand All @@ -39,30 +36,12 @@ abstract class JsonSchema extends Middleware
*/
protected string $suffix;

/**
* The filepath of the JSON SchemaMiddleware: absolute or relative path
*
* @since 0.9.0
*/
protected ?string $filepath = null;

/**
* The JSON SchemaMiddleware
*
* @since 0.9.0
*
* @var mixed
* @since 1.2.1
*/
protected $contents;

/**
* Directories where to look for a schema
*
* @since 0.9.0
*
* @var array<string>
*/
protected array $schemaDirs = [];
protected string|array $schema;

/**
* Class used to format validation errors which are shown to the user
Expand All @@ -74,45 +53,23 @@ abstract class JsonSchema extends Middleware
/**
* Validator used for checking data against their JSON schema
*/
protected static Validator $validator;
protected Validator $validator;

/**
* Creates a new instance of JsonSchema
*
* @since 0.9.0
*
* @param string|array $schema File name or path to the JSON schema or a JSON schema as an array.
* @param ?SchemaResolver $schemaResolver The validator to be used for validation.
*
* @since 0.9.0
*/
public function __construct(string|array $schema)
public function __construct(string|array $schema, ?SchemaResolver $schemaResolver = null)
{
parent::__construct();
$this->schema = $schema;
$this->validator = $this->createValidatorWithResolver($schemaResolver);
$this->errorFormatter = new ErrorFormatter();
$this->suffix = $this->getSuffix();
if (is_array($schema)) {
$this->contents = $schema;

return;
}

$this->filepath = $schema;
if (! \str_ends_with($schema, '.json')) {
$this->filepath .= '.json';
}
}

/**
* Retrieves the default validator for checking responses against a JSON schema
*
* @return Validator the default validator
*/
protected static function getDefaultValidator(): Validator
{
if (! isset(self::$validator)) {
$loader = new SchemaLoader(new ResponseSchemaParser(), new SchemaResolver(), true);
self::$validator = new Validator($loader);
}

return self::$validator;
}

/**
Expand All @@ -129,70 +86,6 @@ protected function getSuffix(): string
return "fastendpoints_{$suffix}";
}

/**
* Appends an additional directory where to look for the schema
*
* @since 0.9.0
*
* @param string|array<string> $schemaDir Directory path or an array of directories where to
* look for JSON schemas.
*
* @throws TypeError if the provided schemas are a valid directory
*/
public function appendSchemaDir(string|array $schemaDir): void
{
$schemaDir = Arr::wrap($schemaDir);
foreach ($schemaDir as $dir) {
if (! \is_string($dir)) {
throw new TypeError(\sprintf(\esc_html__('Expected a directory as a string but got: %s'), gettype($dir)));
}

if (! $dir) {
throw new TypeError(\esc_html__('Invalid schema directory'));
}

if (\is_file($dir)) {
/* translators: 1: Directory */
throw new TypeError(\sprintf(\esc_html__('Expected a directory with schemas but got a file: %s'), \esc_html($dir)));
}

if (! \is_dir($dir)) {
/* translators: 1: Directory */
throw new TypeError(\sprintf(\esc_html__('SchemaMiddleware directory not found: %s'), \esc_html($dir)));
}
}

$this->schemaDirs = $this->schemaDirs + $schemaDir;
}

/**
* Validates if the given JSON schema filepath is an absolute path or if it exists
* in the given schema directory
*
* @since 0.9.0
*
* @throws Exception if no schema is specified or cannot be found.
*/
protected function getValidSchemaFilepath(): string
{
if (! $this->filepath) {
throw new Exception('No schema filepath specified');
}

if (\is_file($this->filepath)) {
return $this->filepath;
}

foreach ($this->schemaDirs as $dir) {
$filepath = \path_join($dir, $this->filepath);
if (\is_file($filepath)) {
return $filepath;
}
}

throw new Exception("SchemaMiddleware filepath not found {$this->filepath}");
}

/**
* Retrieves a properly formatted error from Opis/json-schema
*
Expand All @@ -206,52 +99,32 @@ protected function getError(ValidationResult $result): mixed
}

/**
* Retrieves the content of a file
*
* @since 0.9.0
*
* @param string $filePath the file to be loaded
* @return bool|string A string with the file content or false on error
* Retrieves the JSON schema to be used for validation
*/
protected function getFileContents(string $filePath): bool|string
public function getSchema(): mixed
{
return \file_get_contents($filePath);
$schema = $this->schema;
if (! str_ends_with($schema, '.json')) {
$schema .= '.json';
}

if (filter_var($schema, FILTER_VALIDATE_URL) === false) {
$schema = $this->validator->resolver()->getDefaultPrefix();
}

return $schema;
}

/**
* Retrieves the JSON contents of the schema
* Retrieves a JSON schema validator with a given SchemaResolver
*
* @since 0.9.0
* @param ?SchemaResolver $resolver
*/
public function getContents(): mixed
public function createValidatorWithResolver(?SchemaResolver $resolver): Validator
{
if ($this->contents || is_array($this->contents)) {
$this->contents = \apply_filters($this->suffix.'_contents', $this->contents, $this);

return $this->contents;
}

$filepath = $this->getValidSchemaFilepath();

// Read JSON file and retrieve it's content.
$result = $this->getFileContents($filepath);
if ($result === false) {
/* translators: 1: SchemaMiddleware filepath */
\wp_die(\sprintf(\esc_html__('Unable to read file: %s'), \esc_html($this->filepath)));
}

$this->contents = \json_decode($result, true);
if ($this->contents === null && \json_last_error() !== \JSON_ERROR_NONE) {
/* translators: 1: SchemaMiddleware filepath, 2: JSON error message */
\wp_die(\sprintf(
\esc_html__('Invalid json file: %1$s %2$s'),
\esc_html($this->filepath),
\esc_html(\json_last_error_msg()),
));
}

$this->contents = \apply_filters($this->suffix.'_contents', $this->contents, $this);
$resolver = $resolver ?? new SchemaResolver();
$schemaLoader = new SchemaLoader(new SchemaParser(), $resolver, true);

return $this->contents;
return new Validator($schemaLoader);
}
}
7 changes: 4 additions & 3 deletions src/Contracts/Router.php
Original file line number Diff line number Diff line change
Expand Up @@ -94,11 +94,12 @@ public function includeRouter(Router &$router): void;
/**
* Includes a router as a sub router
*
* @since 0.9.0
* @param string $dir Directory path where to look for JSON schemas.
* @param string $uriPrefix Prefix used to associate schema directory.
*
* @param string|array<string> $dir Directory path where to look for JSON schemas.
* @since 0.9.0
*/
public function appendSchemaDir(string|array $dir): void;
public function appendSchemaDir(string $dir, string $uriPrefix): void;

/**
* Adds all actions required to register the defined endpoints
Expand Down
28 changes: 17 additions & 11 deletions src/Endpoint.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
use Wp\FastEndpoints\Helpers\WpError;
use Wp\FastEndpoints\Schemas\ResponseMiddleware;
use Wp\FastEndpoints\Schemas\SchemaMiddleware;
use Wp\FastEndpoints\Schemas\SchemaResolver;
use WP_Error;
use WP_Http;
use WP_REST_Request;
Expand Down Expand Up @@ -62,6 +63,13 @@ class Endpoint implements EndpointInterface
*/
protected $handler;

/**
* Finds the correct JSON schema to be loaded
*
* @since 1.2.1
*/
protected SchemaResolver $schemaResolver;

/**
* Same as the register_rest_route $override parameter
*
Expand Down Expand Up @@ -133,13 +141,14 @@ class Endpoint implements EndpointInterface
* WP FastEndpoints arguments.
* @param bool $override Same as the WordPress register_rest_route $override parameter. Default value: false.
*/
public function __construct(string $method, string $route, callable $handler, array $args = [], bool $override = false)
public function __construct(string $method, string $route, callable $handler, array $args = [], bool $override = false, ?SchemaResolver $schemaResolver = null)
{
$this->method = $method;
$this->route = $route;
$this->handler = $handler;
$this->args = $args;
$this->override = $override;
$this->schemaResolver = $schemaResolver ?? new SchemaResolver();
$this->invoker = new Invoker();
}

Expand All @@ -152,21 +161,18 @@ public function __construct(string $method, string $route, callable $handler, ar
*
* @param string $namespace WordPress REST namespace.
* @param string $restBase Endpoint REST base.
* @param array<string> $schemaDirs Array of directories to look for JSON schemas. Default value: [].
* @return true|false true if successfully registered a REST route or false otherwise.
*/
public function register(string $namespace, string $restBase, array $schemaDirs = []): bool
public function register(string $namespace, string $restBase): bool
{
$args = [
'methods' => $this->method,
'callback' => [$this, 'callback'],
'permission_callback' => $this->permissionHandlers ? [$this, 'permissionCallback'] : '__return_true',
];
if ($this->schema) {
$this->schema->appendSchemaDir($schemaDirs);
$args['schema'] = [$this->schema, 'getContents'];
$args['schema'] = [$this->schema, 'getSchema'];
}
$this->responseSchema?->appendSchemaDir($schemaDirs);
// Override default arguments.
$args = \array_merge($args, $this->args);
$args = \apply_filters('fastendpoints_endpoint_args', $args, $namespace, $restBase, $this);
Expand Down Expand Up @@ -228,7 +234,7 @@ public function hasCap(string $capability, ...$args): Endpoint
*/
public function schema(string|array $schema): Endpoint
{
$this->schema = new SchemaMiddleware($schema);
$this->schema = new SchemaMiddleware($schema, $this->schemaResolver);
$this->onRequestHandlers[] = [$this->schema, 'onRequest'];

return $this;
Expand All @@ -249,7 +255,7 @@ public function schema(string|array $schema): Endpoint
*/
public function returns(string|array $schema, string|bool|null $removeAdditionalProperties = true): Endpoint
{
$this->responseSchema = new ResponseMiddleware($schema, $removeAdditionalProperties);
$this->responseSchema = new ResponseMiddleware($schema, $removeAdditionalProperties, $this->schemaResolver);
$this->onResponseHandlers[] = [$this->responseSchema, 'onResponse'];

return $this;
Expand Down Expand Up @@ -308,20 +314,20 @@ public function callback(WP_REST_Request $request): WP_REST_Response|WP_Error
] + $request->get_url_params();
// Request handlers.
$result = $this->runHandlers($this->onRequestHandlers, $dependencies);
if (\is_wp_error($result) || $result instanceof \WP_REST_Response) {
if (\is_wp_error($result) || $result instanceof WP_REST_Response) {
return $result;
}

// Main handler.
$result = $this->invoker->call($this->handler, $dependencies);
if (\is_wp_error($result) || $result instanceof \WP_REST_Response) {
if (\is_wp_error($result) || $result instanceof WP_REST_Response) {
return $result;
}
$dependencies['response']->set_data($result);

// ResponseMiddleware handlers.
$result = $this->runHandlers($this->onResponseHandlers, $dependencies);
if (\is_wp_error($result) || $result instanceof \WP_REST_Response) {
if (\is_wp_error($result) || $result instanceof WP_REST_Response) {
return $result;
}

Expand Down
8 changes: 8 additions & 0 deletions src/Helpers/Arr.php
Original file line number Diff line number Diff line change
Expand Up @@ -79,4 +79,12 @@ public static function recursiveKeyValueSearch(array $haystack, string|int $sear

return $path;
}

/**
* Converts an object to an array
*/
public static function fromObjectToArray(mixed $data): mixed
{
return is_object($data) ? array_map([Arr::class, __FUNCTION__], (array) $data) : $data;
}
}
Loading

0 comments on commit f52d5a3

Please sign in to comment.