mirror of
https://code.castopod.org/adaures/castopod
synced 2025-04-19 13:01:19 +00:00
parent
ea20206ee6
commit
e64001d006
@ -41,3 +41,8 @@ cache.handler="file"
|
||||
# cache.redis.password=null
|
||||
# cache.redis.port=6379
|
||||
# cache.redis.database=0
|
||||
|
||||
#REST API configuration
|
||||
#--------------------------------------------------------------------
|
||||
# 0/1 Disabled/Enabled
|
||||
REST_API_ENABLED=1
|
@ -65,7 +65,19 @@ lint-js:
|
||||
|
||||
tests:
|
||||
stage: quality
|
||||
services:
|
||||
- mariadb
|
||||
variables:
|
||||
MYSQL_DATABASE: "tests"
|
||||
MYSQL_ROOT_PASSWORD: "R00Tp4ssW0RD"
|
||||
MYSQL_USER: "tests_user"
|
||||
MYSQL_PASSWORD: "password"
|
||||
|
||||
script:
|
||||
- apt-get install -y mariadb-client libmariadb-dev
|
||||
|
||||
- echo "SHOW DATABASES;" | mysql --user=root --password="$MYSQL_ROOT_PASSWORD" --host=mariadb "$MYSQL_DATABASE"
|
||||
|
||||
# run phpunit without code coverage
|
||||
# TODO: add code coverage
|
||||
- vendor/bin/phpunit --no-coverage
|
||||
|
@ -50,6 +50,7 @@ class Autoload extends AutoloadConfig
|
||||
'Modules\Install' => ROOTPATH . 'modules/Install/',
|
||||
'Modules\Fediverse' => ROOTPATH . 'modules/Fediverse/',
|
||||
'Modules\WebSub' => ROOTPATH . 'modules/WebSub/',
|
||||
'Modules\Api\Rest\V1' => ROOTPATH . 'modules/Api/Rest/V1',
|
||||
'Config' => APPPATH . 'Config/',
|
||||
'ViewComponents' => APPPATH . 'Libraries/ViewComponents/',
|
||||
'ViewThemes' => APPPATH . 'Libraries/ViewThemes/',
|
||||
|
@ -10,6 +10,7 @@ use CodeIgniter\Filters\DebugToolbar;
|
||||
use CodeIgniter\Filters\Honeypot;
|
||||
use CodeIgniter\Filters\InvalidChars;
|
||||
use CodeIgniter\Filters\SecureHeaders;
|
||||
use Modules\Api\Rest\V1\Filters\ApiFilter;
|
||||
use Modules\Auth\Filters\PermissionFilter;
|
||||
use Modules\Fediverse\Filters\AllowCorsFilter;
|
||||
use Modules\Fediverse\Filters\FediverseFilter;
|
||||
@ -34,6 +35,7 @@ class Filters extends BaseConfig
|
||||
'permission' => PermissionFilter::class,
|
||||
'fediverse' => FediverseFilter::class,
|
||||
'allow-cors' => AllowCorsFilter::class,
|
||||
'rest-api' => ApiFilter::class,
|
||||
];
|
||||
|
||||
/**
|
||||
|
141
app/Database/Seeds/FakeSinglePodcastApiSeeder.php
Normal file
141
app/Database/Seeds/FakeSinglePodcastApiSeeder.php
Normal file
@ -0,0 +1,141 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Database\Seeds;
|
||||
|
||||
use CodeIgniter\Database\Seeder;
|
||||
|
||||
class FakeSinglePodcastApiSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* @return array<mixed>
|
||||
*/
|
||||
public static function cover(): array
|
||||
{
|
||||
return [
|
||||
'id' => 1,
|
||||
'file_path' => 'podcasts/Handle/cover.jpg',
|
||||
'file_size' => 400000,
|
||||
'file_mimetype' => 'image/jpeg',
|
||||
'file_metadata' => '{"FILE":{"FileName":"cover.jpg","FileDateTime":1654861723,"FileSize":468541,"FileType":2,"MimeType":"image\/jpeg","SectionsFound":"COMMENT"},"COMPUTED":{"html":"width=\"1400\" height=\"1400\"","Height":1400,"Width":1400,"IsColor":1},"COMMENT":["CREATOR: gd-jpeg v1.0 (using IJG JPEG v62), quality = 90\n"],"sizes":{"tiny":{"width":40,"height":40,"mimetype":"image\/webp","extension":"webp"},"thumbnail":{"width":150,"height":150,"mimetype":"image\/webp","extension":"webp"},"medium":{"width":320,"height":320,"mimetype":"image\/webp","extension":"webp"},"large":{"width":1024,"height":1024,"mimetype":"image\/webp","extension":"webp"},"feed":{"width":1400,"height":1400},"id3":{"width":500,"height":500},"og":{"width":1200,"height":1200},"federation":{"width":400,"height":400},"webmanifest192":{"width":192,"height":192,"mimetype":"image\/png","extension":"png"},"webmanifest512":{"width":512,"height":512,"mimetype":"image\/png","extension":"png"}}}',
|
||||
'type' => 'image',
|
||||
'description' => null,
|
||||
'language_code' => null,
|
||||
'uploaded_by' => 1,
|
||||
'updated_by' => 1,
|
||||
'uploaded_at' => '2022-06-13 8:00:00',
|
||||
'updated_at' => '2022-06-13 8:00:00',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<mixed>
|
||||
*/
|
||||
public static function banner(): array
|
||||
{
|
||||
return [
|
||||
'id' => 2,
|
||||
'file_path' => 'podcasts/Handle/banner.jpg',
|
||||
'file_size' => 400000,
|
||||
'file_mimetype' => 'image/jpeg',
|
||||
'file_metadata' => '{"FILE":{"FileName":"banner.jpg","FileDateTime":1654861724,"FileSize":98209,"FileType":2,"MimeType":"image\/jpeg","SectionsFound":""},"COMPUTED":{"html":"width=\"1500\" height=\"500\"","Height":500,"Width":1500,"IsColor":1},"sizes":{"small":{"width":320,"height":128,"mimetype":"image\/webp","extension":"webp"},"medium":{"width":960,"height":320,"mimetype":"image\/webp","extension":"webp"},"federation":{"width":1500,"height":500}}}',
|
||||
'type' => 'image',
|
||||
'description' => null,
|
||||
'language_code' => null,
|
||||
'uploaded_by' => 1,
|
||||
'updated_by' => 1,
|
||||
'uploaded_at' => '2022-06-13 8:00:00',
|
||||
'updated_at' => '2022-06-13 8:00:00',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<mixed>
|
||||
*/
|
||||
public static function actor(): array
|
||||
{
|
||||
return [
|
||||
'id' => 1,
|
||||
'uri' => getenv('app_baseURL') . '@Handle',
|
||||
'username' => 'Handle',
|
||||
'domain' => getenv('app_baseURL'),
|
||||
'private_key' => 'private_key',
|
||||
'public_key' => 'public_key',
|
||||
'display_name' => 'Title',
|
||||
'summary' => '<p>description</p>',
|
||||
'avatar_image_url' => getenv('app_baseURL') . 'media/podcasts/Handle',
|
||||
'avatar_image_mimetype' => 'image/webp',
|
||||
'cover_image_url' => null,
|
||||
'cover_image_mimetype' => null,
|
||||
'inbox_url' => getenv('app_baseURL') . '@Handle/inbox',
|
||||
'outbox_url' => getenv('app_baseURL') . '@Handle/outbox',
|
||||
'followers_url' => getenv('app_baseURL') . '@Handle/followers',
|
||||
'followers_count' => 0,
|
||||
'posts_count' => 0,
|
||||
'is_blocked' => 0,
|
||||
'created_at' => '2022-06-13 8:00:00',
|
||||
'updated_at' => '2022-06-13 8:00:00',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<mixed>
|
||||
*/
|
||||
public static function podcast(): array
|
||||
{
|
||||
return [
|
||||
'id' => 1,
|
||||
'guid' => '0d341200-0234-5de7-99a6-a7d02bea4ce2',
|
||||
'actor_id' => 1,
|
||||
'handle' => 'Handle',
|
||||
'title' => 'Title',
|
||||
'description_markdown' => 'description',
|
||||
'description_html' => '<p>description</p>',
|
||||
'cover_id' => 1,
|
||||
'banner_id' => 2,
|
||||
'language_code' => 'en',
|
||||
'category_id' => 1,
|
||||
'parental_advisory' => null,
|
||||
'owner_name' => 'Owner',
|
||||
'owner_email' => 'Owner@gmail.com',
|
||||
'publisher' => '',
|
||||
'type' => 'episodic',
|
||||
'copyright' => '',
|
||||
'episode_description_footer_markdown' => null,
|
||||
'episode_description_footer_html' => null,
|
||||
'is_blocked' => 0,
|
||||
'is_completed' => 0,
|
||||
'is_locked' => 1,
|
||||
'imported_feed_url' => null,
|
||||
'new_feed_url' => null,
|
||||
'payment_pointer' => null,
|
||||
'location_name' => null,
|
||||
'location_geo' => null,
|
||||
'location_osm' => null,
|
||||
'custom_rss' => null,
|
||||
'is_published_on_hubs' => 0,
|
||||
'partner_id' => null,
|
||||
'partner_link_url' => null,
|
||||
'partner_image_url' => null,
|
||||
'created_by' => 1,
|
||||
'updated_by' => 1,
|
||||
'created_at' => '2022-06-13 8:00:00',
|
||||
'updated_at' => '2022-06-13 8:00:00',
|
||||
];
|
||||
}
|
||||
|
||||
public function run(): void
|
||||
{
|
||||
$this->call(AppSeeder::class);
|
||||
$this->call(TestSeeder::class);
|
||||
$this->db->table('media')
|
||||
->insert(self::cover());
|
||||
$this->db->table('media')
|
||||
->insert(self::banner());
|
||||
$this->db->table('fediverse_actors')
|
||||
->insert(self::actor());
|
||||
$this->db->table('podcasts')
|
||||
->insert(self::podcast());
|
||||
}
|
||||
}
|
19
modules/Api/Rest/V1/Config/Api.php
Normal file
19
modules/Api/Rest/V1/Config/Api.php
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Modules\Api\Rest\V1\Config;
|
||||
|
||||
use CodeIgniter\Config\BaseConfig;
|
||||
|
||||
class Api extends BaseConfig
|
||||
{
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Rest API gateway
|
||||
* --------------------------------------------------------------------------
|
||||
* Defines a base route for all API pages
|
||||
*/
|
||||
public string $gateway = 'api/rest/v1/';
|
||||
}
|
21
modules/Api/Rest/V1/Config/Routes.php
Normal file
21
modules/Api/Rest/V1/Config/Routes.php
Normal file
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Modules\Api\Rest\V1\Config;
|
||||
|
||||
$routes = service('routes');
|
||||
|
||||
$routes->group(
|
||||
config('Api')
|
||||
->gateway . 'podcasts',
|
||||
[
|
||||
'namespace' => 'Modules\Api\Rest\V1\Controllers',
|
||||
'filter' => 'rest-api',
|
||||
],
|
||||
function ($routes): void {
|
||||
$routes->get('/', 'PodcastController::list');
|
||||
$routes->get('(:num)', 'PodcastController::view/$1');
|
||||
$routes->get('(:any)', 'ExceptionController::notFound');
|
||||
}
|
||||
);
|
20
modules/Api/Rest/V1/Config/Services.php
Normal file
20
modules/Api/Rest/V1/Config/Services.php
Normal file
@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Modules\Api\Rest\V1\Config;
|
||||
|
||||
use CodeIgniter\Config\BaseService;
|
||||
use Modules\Api\Rest\V1\Core\Exceptions;
|
||||
|
||||
class Services extends BaseService
|
||||
{
|
||||
public static function restApiExceptions(bool $getShared = true)
|
||||
{
|
||||
if ($getShared) {
|
||||
return static::getSharedInstance('restApiExceptions');
|
||||
}
|
||||
|
||||
return new Exceptions(config('Exceptions'), static::request(), static::response());
|
||||
}
|
||||
}
|
19
modules/Api/Rest/V1/Controllers/ExceptionController.php
Normal file
19
modules/Api/Rest/V1/Controllers/ExceptionController.php
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Modules\Api\Rest\V1\Controllers;
|
||||
|
||||
use CodeIgniter\API\ResponseTrait;
|
||||
use CodeIgniter\Controller;
|
||||
use CodeIgniter\HTTP\Response;
|
||||
|
||||
class ExceptionController extends Controller
|
||||
{
|
||||
use ResponseTrait;
|
||||
|
||||
public function notFound(): Response
|
||||
{
|
||||
return $this->failNotFound('Podcast not found');
|
||||
}
|
||||
}
|
42
modules/Api/Rest/V1/Controllers/PodcastController.php
Normal file
42
modules/Api/Rest/V1/Controllers/PodcastController.php
Normal file
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Modules\Api\Rest\V1\Controllers;
|
||||
|
||||
use App\Entities\Podcast;
|
||||
use App\Models\PodcastModel;
|
||||
use CodeIgniter\API\ResponseTrait;
|
||||
use CodeIgniter\Controller;
|
||||
use CodeIgniter\HTTP\Response;
|
||||
use Modules\Api\Rest\V1\Config\Services;
|
||||
|
||||
class PodcastController extends Controller
|
||||
{
|
||||
use ResponseTrait;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
Services::restApiExceptions()->initialize();
|
||||
}
|
||||
|
||||
public function list(): Response
|
||||
{
|
||||
$data = (new PodcastModel())->findAll();
|
||||
array_map(function ($podcast): void {
|
||||
$podcast->feed_url = $podcast->getFeedUrl();
|
||||
}, $data);
|
||||
return $this->respond($data);
|
||||
}
|
||||
|
||||
public function view(int $id): Response
|
||||
{
|
||||
$data = (new PodcastModel())->getPodcastById($id);
|
||||
if (! $data instanceof Podcast) {
|
||||
return $this->failNotFound('Podcast not found');
|
||||
}
|
||||
|
||||
$data->feed_url = $data->getFeedUrl();
|
||||
return $this->respond($data);
|
||||
}
|
||||
}
|
32
modules/Api/Rest/V1/Core/Exceptions.php
Normal file
32
modules/Api/Rest/V1/Core/Exceptions.php
Normal file
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Modules\Api\Rest\V1\Core;
|
||||
|
||||
use Throwable;
|
||||
|
||||
class Exceptions extends \CodeIgniter\Debug\Exceptions
|
||||
{
|
||||
protected function render(Throwable $exception, int $statusCode): void
|
||||
{
|
||||
header('Content-Type: application/json');
|
||||
$data = [
|
||||
'status' => $statusCode,
|
||||
'error' => $statusCode,
|
||||
'messages' => [
|
||||
'error' => 'Unexpected error',
|
||||
],
|
||||
];
|
||||
if (ENVIRONMENT === 'development') {
|
||||
$data['messages'] = array_merge($data['messages'], [
|
||||
'message' => $exception->getMessage(),
|
||||
'file' => $exception->getFile(),
|
||||
'line' => $exception->getLine(),
|
||||
'trace' => $exception->getTrace(),
|
||||
]);
|
||||
}
|
||||
|
||||
echo json_encode($data);
|
||||
}
|
||||
}
|
25
modules/Api/Rest/V1/Filters/ApiFilter.php
Normal file
25
modules/Api/Rest/V1/Filters/ApiFilter.php
Normal file
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Modules\Api\Rest\V1\Filters;
|
||||
|
||||
use CodeIgniter\Exceptions\PageNotFoundException;
|
||||
use CodeIgniter\Filters\FilterInterface;
|
||||
use CodeIgniter\HTTP\RequestInterface;
|
||||
use CodeIgniter\HTTP\ResponseInterface;
|
||||
|
||||
class ApiFilter implements FilterInterface
|
||||
{
|
||||
public function before(RequestInterface $request, $arguments = null): void
|
||||
{
|
||||
if (! getenv('REST_API_ENABLED')) {
|
||||
throw PageNotFoundException::forPageNotFound();
|
||||
}
|
||||
}
|
||||
|
||||
public function after(RequestInterface $request, ResponseInterface $response, $arguments = null): void
|
||||
{
|
||||
// Do something here
|
||||
}
|
||||
}
|
328
modules/Api/Rest/V1/podcast.json
Normal file
328
modules/Api/Rest/V1/podcast.json
Normal file
@ -0,0 +1,328 @@
|
||||
{
|
||||
"openapi": "3.0.0",
|
||||
"info": {
|
||||
"version": "1.0.0",
|
||||
"title": "Castopod podcasts"
|
||||
},
|
||||
"paths": {
|
||||
"/api/rest/v1/podcasts": {
|
||||
"get": {
|
||||
"summary": "List all podcasts",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Object of podcasts",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/Podcasts"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "unexpected error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/Error"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/rest/v1/podcasts/{id}": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"description": "The id of the podcast to retrieve",
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"format": "int64",
|
||||
"minimum": 1,
|
||||
"maxLength": 10
|
||||
}
|
||||
}
|
||||
],
|
||||
"get": {
|
||||
"summary": "Info for a specific podcast",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Expected response to a valid request",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/Podcast"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "unexpected error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/Error"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"Podcast": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"id",
|
||||
"guid",
|
||||
"actor_id",
|
||||
"handle",
|
||||
"title",
|
||||
"description_markdown",
|
||||
"description_html",
|
||||
"cover_id",
|
||||
"language_code",
|
||||
"category_id",
|
||||
"owner_name",
|
||||
"owner_email",
|
||||
"type",
|
||||
"is_blocked",
|
||||
"is_completed",
|
||||
"is_locked",
|
||||
"is_published_on_hubs",
|
||||
"created_by",
|
||||
"updated_by",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
"feed_url"
|
||||
],
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "integer",
|
||||
"format": "int64",
|
||||
"minimum": 1,
|
||||
"maxLength": 10
|
||||
},
|
||||
"guid": {
|
||||
"type": "string",
|
||||
"maxLength": 36
|
||||
},
|
||||
"actor_id": {
|
||||
"type": "integer",
|
||||
"format": "int64",
|
||||
"minimum": 1,
|
||||
"maxLength": 10
|
||||
},
|
||||
"handle": {
|
||||
"type": "string",
|
||||
"maxLength": 32
|
||||
},
|
||||
"title": {
|
||||
"type": "string",
|
||||
"maxLength": 128
|
||||
},
|
||||
"description_markdown": {
|
||||
"type": "string"
|
||||
},
|
||||
"description_html": {
|
||||
"type": "string"
|
||||
},
|
||||
"cover_id": {
|
||||
"type": "integer",
|
||||
"format": "int64",
|
||||
"minimum": 1,
|
||||
"maxLength": 10
|
||||
},
|
||||
"banner_id": {
|
||||
"type": "integer",
|
||||
"format": "int64",
|
||||
"minimum": 1,
|
||||
"maxLength": 10
|
||||
},
|
||||
"language_code": {
|
||||
"type": "string",
|
||||
"maxLength": 2
|
||||
},
|
||||
"category_id": {
|
||||
"type": "integer",
|
||||
"format": "int64",
|
||||
"minimum": 1
|
||||
},
|
||||
"parental_advisory": {
|
||||
"type": "string",
|
||||
"enum": ["clean", "explicit"]
|
||||
},
|
||||
"owner_name": {
|
||||
"type": "string",
|
||||
"maxLength": 128
|
||||
},
|
||||
"owner_email": {
|
||||
"type": "string",
|
||||
"maxLength": 255
|
||||
},
|
||||
"publisher": {
|
||||
"type": "string",
|
||||
"maxLength": 128
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": ["episodic", "serial"]
|
||||
},
|
||||
"copyright": {
|
||||
"type": "string",
|
||||
"maxLength": 128
|
||||
},
|
||||
"episode_description_footer_markdown": {
|
||||
"type": "string"
|
||||
},
|
||||
"episode_description_footer_html": {
|
||||
"type": "string"
|
||||
},
|
||||
"is_blocked": {
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"enum": [0, 1],
|
||||
"minLength": 1
|
||||
},
|
||||
"is_completed": {
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"enum": [0, 1],
|
||||
"minLength": 1
|
||||
},
|
||||
"is_locked": {
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"enum": [0, 1],
|
||||
"minLength": 1
|
||||
},
|
||||
"imported_feed_url": {
|
||||
"type": "string",
|
||||
"maxLength": 512
|
||||
},
|
||||
"new_feed_url": {
|
||||
"type": "string",
|
||||
"maxLength": 512
|
||||
},
|
||||
"payment_pointer": {
|
||||
"type": "string",
|
||||
"maxLength": 128
|
||||
},
|
||||
"location_name": {
|
||||
"type": "string",
|
||||
"maxLength": 128
|
||||
},
|
||||
"location_geo": {
|
||||
"type": "string",
|
||||
"maxLength": 32
|
||||
},
|
||||
"location_osm": {
|
||||
"type": "string",
|
||||
"maxLength": 12
|
||||
},
|
||||
"custom_rss": {
|
||||
"type": "string"
|
||||
},
|
||||
"is_published_on_hubs": {
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"enum": [0, 1],
|
||||
"minLength": 1
|
||||
},
|
||||
"partner_id": {
|
||||
"type": "string",
|
||||
"maxLength": 32
|
||||
},
|
||||
"partner_link_url": {
|
||||
"type": "string",
|
||||
"maxLength": 512
|
||||
},
|
||||
"partner_image_url": {
|
||||
"type": "string",
|
||||
"maxLength": 512
|
||||
},
|
||||
"created_by": {
|
||||
"type": "integer",
|
||||
"format": "int64",
|
||||
"minimum": 1,
|
||||
"maxLength": 10
|
||||
},
|
||||
"updated_by": {
|
||||
"type": "integer",
|
||||
"format": "int64",
|
||||
"minimum": 1,
|
||||
"maxLength": 10
|
||||
},
|
||||
"created_at": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"date": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"timezone_type": {
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"timezone": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"updated_at": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"date": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"timezone_type": {
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"timezone": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"feed_url": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Podcasts": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/Podcast"
|
||||
}
|
||||
},
|
||||
"Error": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"status": {
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"error": {
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"messages": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"error": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -45,13 +45,12 @@
|
||||
<!-- Directory containing the front controller (index.php) -->
|
||||
<const name="PUBLICPATH" value="./public/"/>
|
||||
<!-- Database configuration -->
|
||||
<!-- Uncomment to provide your own database for testing
|
||||
<env name="database.tests.hostname" value="localhost"/>
|
||||
<env name="database.tests.hostname" value="mariadb"/>
|
||||
<env name="database.tests.database" value="tests"/>
|
||||
<env name="database.tests.username" value="tests_user"/>
|
||||
<env name="database.tests.password" value=""/>
|
||||
<env name="database.tests.password" value="password"/>
|
||||
<env name="database.tests.DBDriver" value="MySQLi"/>
|
||||
<env name="database.tests.DBPrefix" value="tests_"/>
|
||||
-->
|
||||
<env name="REST_API_ENABLED" value="1"/>
|
||||
</php>
|
||||
</phpunit>
|
||||
</phpunit>
|
||||
|
109
tests/modules/Api/Rest/V1/PodcastTest.php
Normal file
109
tests/modules/Api/Rest/V1/PodcastTest.php
Normal file
@ -0,0 +1,109 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace modules\Api\Rest\V1;
|
||||
|
||||
use App\Database\Seeds\FakeSinglePodcastApiSeeder;
|
||||
use CodeIgniter\Test\CIUnitTestCase;
|
||||
use CodeIgniter\Test\DatabaseTestTrait;
|
||||
use CodeIgniter\Test\FeatureTestTrait;
|
||||
|
||||
class PodcastTest extends CIUnitTestCase
|
||||
{
|
||||
use FeatureTestTrait;
|
||||
use DatabaseTestTrait;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $migrate = true;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $migrateOnce = false;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
protected $namespace;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $seed = 'FakeSinglePodcastApiSeeder';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $basePath = 'app/Database';
|
||||
|
||||
/**
|
||||
* @var array<mixed>
|
||||
*/
|
||||
private array $podcast = [];
|
||||
|
||||
private string $podcastApiUrl;
|
||||
|
||||
/**
|
||||
* @param array<mixed> $data
|
||||
*/
|
||||
public function __construct(?string $name = null, array $data = [], $dataName = '')
|
||||
{
|
||||
parent::__construct($name, $data, $dataName);
|
||||
$this->podcast = FakeSinglePodcastApiSeeder::podcast();
|
||||
$this->podcast['created_at'] = [];
|
||||
$this->podcast['updated_at'] = [];
|
||||
$this->podcastApiUrl = config('Api')
|
||||
->gateway;
|
||||
}
|
||||
|
||||
public function testList(): void
|
||||
{
|
||||
$result = $this->call('get', $this->podcastApiUrl . 'podcasts');
|
||||
$result->assertStatus(200);
|
||||
$result->assertHeader('Content-Type', 'application/json; charset=UTF-8');
|
||||
$result->assertJSONFragment([
|
||||
0 => $this->podcast,
|
||||
]);
|
||||
}
|
||||
|
||||
public function testView(): void
|
||||
{
|
||||
$result = $this->call('get', $this->podcastApiUrl . 'podcasts/1');
|
||||
$result->assertStatus(200);
|
||||
$result->assertHeader('Content-Type', 'application/json; charset=UTF-8');
|
||||
$result->assertJSONFragment($this->podcast);
|
||||
}
|
||||
|
||||
public function testViewNotFound(): void
|
||||
{
|
||||
$result = $this->call('get', $this->podcastApiUrl . 'podcasts/2');
|
||||
$result->assertStatus(404);
|
||||
$result->assertJSONExact(
|
||||
[
|
||||
'status' => 404,
|
||||
'error' => 404,
|
||||
'messages' => [
|
||||
'error' => 'Podcast not found',
|
||||
],
|
||||
]
|
||||
);
|
||||
$result->assertHeader('Content-Type', 'application/json; charset=UTF-8');
|
||||
}
|
||||
|
||||
/*
|
||||
* Refreshing database to fetch empty array of podcasts
|
||||
*/
|
||||
public function testListEmpty(): void
|
||||
{
|
||||
$this->regressDatabase();
|
||||
$this->migrateDatabase();
|
||||
$result = $this->call('get', $this->podcastApiUrl . 'podcasts');
|
||||
$result->assertStatus(200);
|
||||
$result->assertHeader('Content-Type', 'application/json; charset=UTF-8');
|
||||
$result->assertJSONExact([]);
|
||||
$this->seed($this->seed);
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user