%s>', $warn ? 'yellow' : 'red', $string);
42 | }
43 | }
44 |
45 | if (! function_exists('bytes_to_human')) {
46 | function bytes_to_human(float $bytes): string
47 | {
48 | $bytes = intval($bytes);
49 | $units = ['B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
50 | $factor = floor((strlen($bytes) - 1) / 3);
51 |
52 | return sprintf(
53 | '%s %s',
54 | round($bytes / (1024 ** $factor)),
55 | $units[$factor]
56 | );
57 | }
58 | }
59 |
60 | if (! function_exists('time_to_human')) {
61 | function time_to_human(float $milliseconds, bool $digital = true): string
62 | {
63 | return $digital ? ms_to_time($milliseconds) : sprintf('%s s', number_format($milliseconds, 9));
64 | }
65 | }
66 |
67 | if (! function_exists('ms_to_time')) {
68 | function ms_to_time(float $milliseconds)
69 | {
70 | $seconds = floor($milliseconds / 1000);
71 | $minutes = floor($seconds / 60);
72 | $hours = floor($minutes / 60);
73 |
74 | $minutes = $minutes - ($hours * 60);
75 | $seconds = $seconds - ($hours * 60 * 60) - ($minutes * 60);
76 | $ms = $milliseconds % 1000;
77 |
78 | $timeFormat = sprintf('%02d:%02d:%02d.%03d', $hours, $minutes, $seconds, $ms);
79 | if ($timeFormat === '00:00:00.000') {
80 | return time_to_human($milliseconds, false);
81 | }
82 |
83 | return $timeFormat;
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/config/filesystems.php:
--------------------------------------------------------------------------------
1 | env('FILESYSTEM_DISK', 'local'),
17 |
18 | /*
19 | |--------------------------------------------------------------------------
20 | | Filesystem Disks
21 | |--------------------------------------------------------------------------
22 | |
23 | | Here you may configure as many filesystem "disks" as you wish, and you
24 | | may even configure multiple disks of the same driver. Defaults have
25 | | been set up for each driver as an example of the required values.
26 | |
27 | | Supported Drivers: "local", "ftp", "sftp", "s3"
28 | |
29 | */
30 |
31 | 'disks' => [
32 |
33 | 'local' => [
34 | 'driver' => 'local',
35 | 'root' => storage_path('app'),
36 | 'throw' => false,
37 | ],
38 |
39 | 'public' => [
40 | 'driver' => 'local',
41 | 'root' => storage_path('app/public'),
42 | 'url' => env('APP_URL').'/storage',
43 | 'visibility' => 'public',
44 | 'throw' => false,
45 | ],
46 |
47 | 's3' => [
48 | 'driver' => 's3',
49 | 'key' => env('AWS_ACCESS_KEY_ID'),
50 | 'secret' => env('AWS_SECRET_ACCESS_KEY'),
51 | 'region' => env('AWS_DEFAULT_REGION'),
52 | 'bucket' => env('AWS_BUCKET'),
53 | 'url' => env('AWS_URL'),
54 | 'endpoint' => env('AWS_ENDPOINT'),
55 | 'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false),
56 | 'throw' => false,
57 | ],
58 |
59 | ],
60 |
61 | /*
62 | |--------------------------------------------------------------------------
63 | | Symbolic Links
64 | |--------------------------------------------------------------------------
65 | |
66 | | Here you may configure the symbolic links that will be created when the
67 | | `storage:link` Artisan command is executed. The array keys should be
68 | | the locations of the links and the values should be their targets.
69 | |
70 | */
71 |
72 | 'links' => [
73 | public_path('storage') => storage_path('app/public'),
74 | ],
75 |
76 | ];
77 |
--------------------------------------------------------------------------------
/config/sanctum.php:
--------------------------------------------------------------------------------
1 | explode(',', env('SANCTUM_STATEFUL_DOMAINS', sprintf(
19 | '%s%s',
20 | 'localhost,localhost:3000,127.0.0.1,127.0.0.1:8000,::1',
21 | Sanctum::currentApplicationUrlWithPort()
22 | ))),
23 |
24 | /*
25 | |--------------------------------------------------------------------------
26 | | Sanctum Guards
27 | |--------------------------------------------------------------------------
28 | |
29 | | This array contains the authentication guards that will be checked when
30 | | Sanctum is trying to authenticate a request. If none of these guards
31 | | are able to authenticate the request, Sanctum will use the bearer
32 | | token that's present on an incoming request for authentication.
33 | |
34 | */
35 |
36 | 'guard' => ['web'],
37 |
38 | /*
39 | |--------------------------------------------------------------------------
40 | | Expiration Minutes
41 | |--------------------------------------------------------------------------
42 | |
43 | | This value controls the number of minutes until an issued token will be
44 | | considered expired. If this value is null, personal access tokens do
45 | | not expire. This won't tweak the lifetime of first-party sessions.
46 | |
47 | */
48 |
49 | 'expiration' => null,
50 |
51 | /*
52 | |--------------------------------------------------------------------------
53 | | Sanctum Middleware
54 | |--------------------------------------------------------------------------
55 | |
56 | | When authenticating your first-party SPA with Sanctum you may need to
57 | | customize some of the middleware Sanctum uses while processing the
58 | | request. You may change the middleware listed below as required.
59 | |
60 | */
61 | 'middleware' => [
62 | 'authenticate_session' => Laravel\Sanctum\Http\Middleware\AuthenticateSession::class,
63 | 'encrypt_cookies' => Illuminate\Cookie\Middleware\EncryptCookies::class,
64 | 'validate_csrf_token' => Illuminate\Foundation\Http\Middleware\ValidateCsrfToken::class,
65 | ],
66 |
67 | ];
68 |
--------------------------------------------------------------------------------
/app/Console/Commands/KataRunCommand.php:
--------------------------------------------------------------------------------
1 | option('all')) {
23 | return $this->handleRun();
24 | }
25 |
26 | $challenges = $this->option('challenge');
27 | if (count($challenges) > 0) {
28 | $challenges = collect($challenges)->map(
29 | fn (string $challenge) => sprintf('App\\Challenges\\A\\%s', $challenge)
30 | )->toArray();
31 |
32 | return $this->handleRun($challenges);
33 | }
34 |
35 | $classNames = collect(config('laravel-kata.challenges'))->map(
36 | fn ($namespace) => str_replace('App\\Challenges\\A\\', '', $namespace)
37 | )->toArray();
38 |
39 | $classNames = $this->smartChoice('Challenges', $classNames);
40 |
41 | $challenges = collect($classNames)->map(
42 | fn ($className) => sprintf('App\\Challenges\\A\\%s', $className)
43 | )->toArray();
44 |
45 | return $this->handleRun($challenges);
46 | }
47 |
48 | protected function handleRun(array $challenges = []): int
49 | {
50 | $configChallenges = config('laravel-kata.challenges');
51 |
52 | $challenges = ! empty($challenges) ? $challenges : $configChallenges;
53 | $methods = $this->option('method');
54 |
55 | $this->kataRunner = app()->makeWith(KataRunner::class, [
56 | 'command' => $this,
57 | 'challenges' => $challenges,
58 | 'methods' => $methods,
59 | ]);
60 |
61 | try {
62 | $this->kataRunner->run();
63 | } catch (KataChallengeScoreException $exception) {
64 | if (app()->runningUnitTests()) {
65 | throw $exception;
66 | }
67 |
68 | $warning = sprintf('%s', $exception->getMessage());
69 | if (in_array($exception::class, config('laravel-kata.ignore-exceptions'))) {
70 | $this->warn($warning);
71 |
72 | return self::SUCCESS;
73 | }
74 |
75 | $this->error($warning);
76 |
77 | return self::FAILURE;
78 | }
79 |
80 | return self::SUCCESS;
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/packages/larawell/laravel-plus/src/Console/Commands/Traits/SmartChoice.php:
--------------------------------------------------------------------------------
1 | $choiceValue) {
21 | $newChoices[sprintf('#%s', $choiceKey)] = $choiceValue;
22 | }
23 |
24 | $choices = $this->_multiChoice($question, $newChoices, $default);
25 |
26 | Cache::set($key, $choices);
27 |
28 | $newChoices = [];
29 | foreach ($choices as $choiceKey => $choiceValue) {
30 | $newChoices[substr($choiceKey, 1)] = $choiceValue;
31 | }
32 |
33 | return $newChoices;
34 | }
35 |
36 | private function _multiChoice(
37 | string $question,
38 | array $choices,
39 | array $chosen
40 | ): array {
41 | $displayChoices = [];
42 | foreach ($choices as $choiceKey => $choiceTitle) {
43 | $displayChoices[$choiceKey] = in_array($choiceKey, array_keys($chosen))
44 | ? sprintf('%s', $choiceTitle)
45 | : sprintf('%s', $choiceTitle);
46 | }
47 |
48 | $displayChoices = array_merge($displayChoices, [
49 | '' => '- - - - - - - - - -',
50 | 't' => 'Toggle all',
51 | 'n' => 'Next',
52 | ]);
53 |
54 | $displayQuestion = empty($chosen)
55 | ? $question
56 | : sprintf('%s (%d)', $question, count($chosen));
57 |
58 | /** @var \Illuminate\Console\Command $this */
59 | $choice = $this->choice($displayQuestion, $displayChoices);
60 | if (empty($choice)) {
61 | $this->warn('Nothing selected');
62 |
63 | return $this->_multiChoice($question, $choices, $chosen);
64 | }
65 |
66 | if ($choice === 't') {
67 | $chosen = count($chosen) === count($choices) ? [] : $choices;
68 |
69 | return $this->_multiChoice($question, $choices, $chosen);
70 | }
71 |
72 | if ($choice === 'n') {
73 | return $chosen;
74 | }
75 |
76 | if (isset($chosen[$choice])) {
77 | unset($chosen[$choice]);
78 | } else {
79 | $chosen[$choice] = $choices[$choice];
80 | }
81 |
82 | return $this->_multiChoice($question, $choices, $chosen);
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | # Laravel Kata
14 | The greatest collection of the worst code.
15 |
16 | ### Concepts
17 | - Practice the fundamentals
18 | - Expose common mistakes
19 | - Lab to theorycraft mechanics
20 |
21 | ### Dependencies
22 | - Docker
23 | - Composer
24 | - NPM
25 |
26 | ## Getting started
27 | ```
28 | npm i
29 | npm run restart
30 |
31 | ./vendor/bin/sail kata:run
32 | ```
33 |
34 | ## Sample challenge
35 | Calculate the value of pi.
36 |
37 | ### Solution A
38 | ```php
39 | namespace App\Challenges\A;
40 |
41 | class Sample
42 | {
43 | public function calculatePi(): float
44 | {
45 | $denominator = 1;
46 | $sum = 0;
47 |
48 | for ($i = 0; $i < 100000; $i++) {
49 | $sum = ($i % 2 === 0)
50 | ? $sum + (4 / $denominator)
51 | : $sum - (4 / $denominator);
52 |
53 | $denominator += 2;
54 | }
55 |
56 | return $this->return(round($sum, 2));
57 | }
58 | }
59 | ```
60 |
61 | ### Solution B
62 | ```php
63 | namespace App\Challenges\B;
64 |
65 | class Sample
66 | {
67 | public function calculatePi(): float
68 | {
69 | return $this->return(round(M_PI, 2));
70 | }
71 | }
72 | ```
73 |
74 | ### Report
75 |
76 |
77 |
78 |
79 | ## [What are katas?](https://www.youtube.com/watch?v=r_8Rw16uscg)
80 | Katas are code challenges focused on improving skill and technique. Some train programming fundamentals, while others focus on complex problem solving. Some are puzzles meant to test your creative problem solving, while others are based on real world coding scenarios.
81 |
82 | > The term was first coined by Dave Thomas, co-author of the book The Pragmatic Programmer as an acknowledgment to the Japanese concept of kata in the martial arts. Dave's version of the concept defines a code kata as an exercise in programming which helps a programmer sharpen their skills through practice and repetition. - [Codewars](https://docs.codewars.com/concepts/kata/)
83 |
--------------------------------------------------------------------------------
/app/Challenges/A/FxConversion.php:
--------------------------------------------------------------------------------
1 | calculateTotalExchangeRate($iteration);
40 | }
41 |
42 | protected function calculateTotalExchangeRate(int $iteration, bool $useSingleton = false): float
43 | {
44 | $amount = 420.69;
45 | $total = 0;
46 |
47 | $dateFrom = Carbon::createFromFormat('Y-m-d', self::DATE_FROM);
48 | $dateTo = $dateFrom->copy()->addDays($iteration);
49 |
50 | $dateToMax = Carbon::createFromFormat('Y-m-d', self::DATE_TO);
51 | if ($dateTo > $dateToMax) {
52 | $dateTo = $dateToMax;
53 | }
54 |
55 | $fxConversionModule = $useSingleton ? FxConversionModule::make() : null;
56 |
57 | $dates = [];
58 | $carbonPeriod = CarbonPeriod::create($dateFrom, $dateTo);
59 | foreach ($carbonPeriod as $date) {
60 | $dates[] = $date;
61 | $total += $useSingleton
62 | ? $fxConversionModule->convert(self::BASE_CURRENCY_CODE, self::TARGET_CURRENCY_CODE, $date, $amount)
63 | : FxConversionModule::convert(self::BASE_CURRENCY_CODE, self::TARGET_CURRENCY_CODE, $date, $amount);
64 | }
65 |
66 | // No do it in reverse
67 | while (! empty($dates)) {
68 | $date = array_pop($dates);
69 | $total += $useSingleton
70 | ? $fxConversionModule->convert(self::BASE_CURRENCY_CODE, self::TARGET_CURRENCY_CODE, $date, $amount)
71 | : FxConversionModule::convert(self::BASE_CURRENCY_CODE, self::TARGET_CURRENCY_CODE, $date, $amount);
72 | }
73 |
74 | return $total;
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/app/Challenges/B/Concurrent.php:
--------------------------------------------------------------------------------
1 | run($this->makeFunctions($iteration));
15 |
16 | return array_sum($results);
17 | }
18 |
19 | public function sequentialVsProcess(int $iteration): int
20 | {
21 | $results = Concurrency::driver('process')->run($this->makeFunctions($iteration));
22 |
23 | return array_sum($results);
24 | }
25 |
26 | public function sequentialVsSync(int $iteration): int
27 | {
28 | $results = Concurrency::driver('sync')->run($this->makeFunctions($iteration));
29 |
30 | return array_sum($results);
31 | }
32 |
33 | public function sequentialVsUpsertsProcess(int $iteration): string
34 | {
35 | return $this->sequentialVsForkUpsertsByDriver($iteration, 'process');
36 | }
37 |
38 | public function sequentialVsUpsertsFork(int $iteration): string
39 | {
40 | return $this->sequentialVsForkUpsertsByDriver($iteration, 'fork');
41 | }
42 |
43 | public function sequentialVsUpsertsSync(int $iteration): string
44 | {
45 | return $this->sequentialVsForkUpsertsByDriver($iteration, 'sync');
46 | }
47 |
48 | private function sequentialVsForkUpsertsByDriver(int $iteration, string $driver): string
49 | {
50 | if (! in_array($driver, ['sync', 'fork', 'process'])) {
51 | throw new InvalidArgument(sprintf('Invalid driver: %s', $driver));
52 | }
53 |
54 | ExperimentARecord::truncate();
55 | $recordsChunked = $this->makeRecordsChunked($iteration, 'b');
56 |
57 | $functions = [];
58 | foreach ($recordsChunked as $index => $chunk) {
59 | $functions[] = function () use ($chunk) {
60 | ExperimentARecord::upsertPrototype($chunk, [
61 | 'unique_field_1', 'unique_field_2', 'unique_field_3',
62 | ], [
63 | 'position', 'update_field_1', 'update_field_2', 'update_field_3',
64 | ]);
65 | };
66 | }
67 |
68 | Concurrency::driver($driver)->run($functions);
69 |
70 | return $this->getExperimentARecordState();
71 | }
72 |
73 | private function makeFunctions(int $iteration): array
74 | {
75 | $fns = [];
76 | for ($i = 0; $i < $iteration; $i++) {
77 | $fns[] = function () use ($iteration, $i) {
78 | usleep(1000); // 1ms
79 |
80 | return $iteration * $i;
81 | };
82 | }
83 |
84 | return $fns;
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/tests/Data/Traits/HasExitHintsTraitTest.php:
--------------------------------------------------------------------------------
1 | instance = new class
12 | {
13 | use HasExitHintsTrait;
14 |
15 | protected $command;
16 |
17 | public function __construct()
18 | {
19 | $this->command = new class
20 | {
21 | public function info(string $message): void {}
22 |
23 | public function warn(string $message): void {}
24 | };
25 | }
26 |
27 | public function addHint(string $message): void
28 | {
29 | $this->addExitHint($message);
30 | }
31 |
32 | public function getHints(): Collection
33 | {
34 | return $this->getExitHints();
35 | }
36 |
37 | public function getRandomHint(): string
38 | {
39 | return $this->getRandomExitHint();
40 | }
41 |
42 | public function addViolations(array $violations): void
43 | {
44 | $this->addExitHintsFromViolations($violations);
45 | }
46 | };
47 | });
48 |
49 | it('can get hints', function () {
50 | $hint = sprintf('The hint at %s', now()->toDateTimeString());
51 | $this->instance->addHint($hint);
52 | expect($this->instance)
53 | ->getHints()
54 | ->toBeInstanceOf(Collection::class)
55 | ->toContain($hint);
56 | });
57 |
58 | it('can get random hint', function () {
59 | $hint = sprintf('The hint at %s', now()->toDateTimeString());
60 | $this->instance->addHint($hint);
61 | expect($this->instance)
62 | ->getRandomHint()->toBe($hint);
63 | });
64 |
65 | it('can add violations', function () {
66 | $violation = [
67 | 'beginLine' => 11,
68 | 'endLine' => 11,
69 | 'package' => null,
70 | 'function' => null,
71 | 'class' => null,
72 | 'method' => null,
73 | 'description' => 'Avoid variables with short names like $a. Configured minimum length is 3.',
74 | 'rule' => 'ShortVariable',
75 | 'ruleSet' => 'Naming Rules',
76 | 'externalInfoUrl' => 'https://phpmd.org/rules/naming.html#shortvariable',
77 | 'priority' => 3,
78 | ];
79 |
80 | $this->instance->addViolations([$violation]);
81 |
82 | $expected = <<<'STR'
83 | ### Naming Rules (ShortVariable)
84 | Avoid variables with short names like $a. Configured minimum length is 3.
85 |
86 | https://phpmd.org/rules/naming.html#shortvariable
87 |
88 | (i) {"class":null,"method":null,"function":null,"beginLine":11,"endLine":11}
89 | STR;
90 | expect($this->instance)
91 | ->getRandomHint()->toBe($expected);
92 | });
93 |
--------------------------------------------------------------------------------
/bin/restart.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | : '
4 | # Init Script
5 | Convenient script to ensure environment is ready to go
6 |
7 | ## Objectives
8 | - Ensure dependencies are installed
9 | - Auto configure for new + existing environments
10 | - Ensure it is executable without interactions (for CI/CD)
11 |
12 | ## Wishlist
13 | - Identify and flag new dependency configs, maybe by comparing .env with .env.example
14 | - Optimise and introduce as a git hook
15 | - Auto spin up environment with sail
16 | - Ensure all dependencies are available, like `composer`, `php`, etc
17 | '
18 |
19 | PATH_TO_SCRIPT_DIR="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"
20 | PATH_TO_REPO="$PATH_TO_SCRIPT_DIR/../"
21 |
22 | # Always load the example env
23 | if [ ! -f "$PATH_TO_REPO/.env" ]; then
24 | cp "$PATH_TO_REPO/.env.example" "$PATH_TO_REPO/.env"
25 | fi
26 |
27 | source $PATH_TO_REPO/.env
28 |
29 | if [ "${CI_MODE}" == "circleci" ]; then
30 | echo "Running in CircliCI"
31 |
32 | echo $'\n# CircleCI\n' >> $PATH_TO_REPO/.env.new
33 | cat $PATH_TO_REPO/.env.circleci >> $PATH_TO_REPO/.env.new
34 |
35 | echo $'\n# Default env\n' >> $PATH_TO_REPO/.env.new
36 | cat $PATH_TO_REPO/.env >> $PATH_TO_REPO/.env.new
37 |
38 | mv $PATH_TO_REPO/.env.new $PATH_TO_REPO/.env
39 | source $PATH_TO_REPO/.env
40 |
41 | composer install
42 | mysql -h127.0.0.1 -uroot -p$DB_ROOT_PASSWORD -e "DROP DATABASE IF EXISTS $DB_DATABASE; CREATE DATABASE $DB_DATABASE;"
43 | mysql -h127.0.0.1 -uroot -p$DB_ROOT_PASSWORD -e "DROP DATABASE IF EXISTS $DB_TEST_DATABASE; CREATE DATABASE $DB_TEST_DATABASE;"
44 | mysql -h127.0.0.1 -uroot -p$DB_ROOT_PASSWORD -e "GRANT ALL PRIVILEGES ON *.* TO 'sail'@'%'; FLUSH PRIVILEGES;"
45 |
46 | # experimenting
47 | echo "Serving Laravel..."
48 | nohup php artisan serve &
49 |
50 | php artisan migrate:fresh --seed --no-interaction --force
51 | php artisan migrate:fresh --env=testing --database=$DB_TEST_DATABASE --seed --force --no-interaction
52 | exit 0
53 | fi
54 |
55 | echo "Running in local"
56 |
57 | # Install dependencies
58 | ./vendor/bin/sail composer install
59 |
60 | # Launch sail environment
61 | ./vendor/bin/sail down --rmi local -v
62 | ./vendor/bin/sail up -d --build
63 |
64 | # Give mysql some time
65 | echo "Sleeping for 9 seconds to give MySQL some time..."
66 | sleep 9
67 |
68 | docker exec -it kata-mysql mysql -uroot -p$DB_ROOT_PASSWORD -e "DROP DATABASE IF EXISTS $DB_DATABASE; CREATE DATABASE $DB_DATABASE;"
69 | docker exec -it kata-mysql mysql -uroot -p$DB_ROOT_PASSWORD -e "DROP DATABASE IF EXISTS $DB_TEST_DATABASE; CREATE DATABASE $DB_TEST_DATABASE;"
70 | docker exec -it kata-mysql mysql -uroot -p$DB_ROOT_PASSWORD -e "GRANT ALL PRIVILEGES ON *.* TO '$DB_USERNAME'@'%'; FLUSH PRIVILEGES;"
71 |
72 | ./vendor/bin/sail artisan migrate:fresh --seed --force --no-interaction
73 | ./vendor/bin/sail artisan migrate:fresh --env=testing --database=$DB_TEST_DATABASE --seed --force --no-interaction
74 |
--------------------------------------------------------------------------------
/app/Http/Kernel.php:
--------------------------------------------------------------------------------
1 |
15 | */
16 | protected $middleware = [
17 | \Illuminate\Foundation\Http\Middleware\InvokeDeferredCallbacks::class,
18 | // \App\Http\Middleware\TrustHosts::class,
19 | \App\Http\Middleware\TrustProxies::class,
20 | \Illuminate\Http\Middleware\HandleCors::class,
21 | \App\Http\Middleware\PreventRequestsDuringMaintenance::class,
22 | \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
23 | \App\Http\Middleware\TrimStrings::class,
24 | \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
25 | ];
26 |
27 | /**
28 | * The application's route middleware groups.
29 | *
30 | * @var array>
31 | */
32 | protected $middlewareGroups = [
33 | 'web' => [
34 | \App\Http\Middleware\EncryptCookies::class,
35 | \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
36 | \Illuminate\Session\Middleware\StartSession::class,
37 | \Illuminate\View\Middleware\ShareErrorsFromSession::class,
38 | \App\Http\Middleware\VerifyCsrfToken::class,
39 | \Illuminate\Routing\Middleware\SubstituteBindings::class,
40 | ],
41 |
42 | 'api' => [
43 | // \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
44 | 'throttle:api',
45 | \Illuminate\Routing\Middleware\SubstituteBindings::class,
46 | ],
47 |
48 | 'mock' => [
49 | // \Illuminate\Routing\Middleware\SubstituteBindings::class,
50 | ],
51 | ];
52 |
53 | /**
54 | * The application's route middleware.
55 | *
56 | * These middleware may be assigned to groups or used individually.
57 | *
58 | * @var array
59 | */
60 | protected $routeMiddleware = [
61 | 'auth' => \App\Http\Middleware\Authenticate::class,
62 | 'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
63 | 'auth.session' => \Illuminate\Session\Middleware\AuthenticateSession::class,
64 | 'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
65 | 'can' => \Illuminate\Auth\Middleware\Authorize::class,
66 | 'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
67 | 'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
68 | 'signed' => \App\Http\Middleware\ValidateSignature::class,
69 | 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
70 | 'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
71 | ];
72 | }
73 |
--------------------------------------------------------------------------------
/routes/api.php:
--------------------------------------------------------------------------------
1 | get('/user', function (Request $request) {
20 | return $request->user();
21 | });
22 |
23 | Route::any('/', function (Request $request) {
24 | $message = "I'm a teapot!";
25 |
26 | return response($message, 418);
27 | });
28 |
29 | Route::get('health', function (Request $request) {
30 | return JsonResource::make([
31 | 'success' => true,
32 | ]);
33 | });
34 |
35 | /**
36 | * Get the list of challenges
37 | */
38 | Route::get('kata', function (Request $request) {
39 | return JsonResource::make([
40 | 'success' => true,
41 | 'data' => collect(config('laravel-kata.challenges', []))
42 | ->map(function ($className) {
43 | $classNameParts = explode('\\', $className);
44 |
45 | return array_pop($classNameParts);
46 | })
47 | ->toArray(),
48 | ]);
49 | });
50 |
51 | /**
52 | * Get the list of challenge's methods
53 | */
54 | Route::get('kata/{challenge}', function (Request $request, string $challenge) {
55 | $class = sprintf(
56 | 'App\\Challenges\\A\\%s',
57 | $challenge
58 | );
59 |
60 | try {
61 | $reflectionClass = new ReflectionClass($class);
62 | } catch (ReflectionException $exception) {
63 | throw new Exception(sprintf(
64 | 'Something bad happened: %s',
65 | $exception->getMessage()
66 | ));
67 | }
68 |
69 | $data = collect($reflectionClass->getMethods())
70 | ->filter(fn (ReflectionMethod $method) => $method->class === $class)
71 | ->filter(fn (ReflectionMethod $method) => $method->isPublic())
72 | ->map(fn ($method) => $method->name)
73 | ->toArray();
74 |
75 | return JsonResource::make([
76 | 'success' => true,
77 | 'data' => $data,
78 | ]);
79 | });
80 |
81 | /**
82 | * Run the challenge
83 | */
84 | Route::get('kata/{challenge}/run', function (Request $request, string $challenge) {
85 | /** @var KataRunner $kataRunner */
86 | $kataRunner = app()->makeWith(KataRunner::class, [
87 | 'command' => null,
88 | 'challenges' => [
89 | sprintf('App\\Challenges\\A\\%s', $challenge),
90 | ],
91 | ]);
92 |
93 | $data = $kataRunner->run();
94 |
95 | return JsonResource::make([
96 | 'success' => true,
97 | 'data' => $data,
98 | ]);
99 | });
100 |
--------------------------------------------------------------------------------