Skip to content

Conversation

@JohnRDOrazio
Copy link
Member

@JohnRDOrazio JohnRDOrazio commented Aug 21, 2025

Summary by CodeRabbit

  • New Features
    • Introduced PSR-7/PSR-15 API with new handlers for Calendar (v5.0), Decrees, Missals, Events, Schemas, Tests, and Regional Data.
    • Added robust content negotiation, CORS/preflight handling, ETag/304 caching, and problem+json error responses.
    • New front controller under public/; start script now serves from public/.
  • Documentation
    • Updated requirements and server start/stop instructions; clarified environment variable guidance.
  • Chores
    • Added API_BASE_PATH env setting; expanded Composer deps; broadened .gitignore and VSCode settings; prefer-stable enabled.
  • Tests
    • Added and reorganized PHPUnit tests for routes and behaviors.
  • Refactor
    • Replaced legacy paths with route-based handlers and improved locale/i18n handling.

Part 1: refactor `/calendar` path, `/calendars` path, `/missals` path, and `/decrees` path
Fixes #337
and better handling of content-type for the response in CalendarHandler
I had initially made them handlers by taking the response and operating on it, but now with error middleware in place they are more transparent, and have gone back to being simple ParamsInterface classes
and realign tests
and fix a couple bugs that the Test interface caught!
Other fixes:
* add feedback to start-server.sh
* add php.supp to .gitignore (when debugging with valgrind)
* add empty check to acceptValues
@JohnRDOrazio JohnRDOrazio added this to the v5.0 milestone Aug 21, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 21, 2025

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

Massive refactor from path-based scripts to a PSR-7/PSR-15 architecture: new public/index front controller, handler classes per route, middleware pipeline with centralized error handling, router redesign with dynamic API path discovery, new HTTP enums/exceptions, enum-based data paths, updated params, tests reorganized, and tooling/config tweaks.

Changes

Cohort / File(s) Summary
Environment & Tooling
/.env.example, /.gitignore, /.vscode/*, /README.md, /phpcs.xml, /phpstan.neon.dist, /composer.json, /start-server.sh
Added API_BASE_PATH env; broadened ignore patterns; VSCode phpcs exclude tweak; README clarifications; phpcs scope refined; removed PHPStan bootstrap stub; added PSR-7/middleware deps and prefer-stable; server starts from public and logs PID/URL.
Front Controller & Dev Server
/public/index.php, /public/LitCalTestServer.php, /index.php
New PSR bootstrap with env validation and Router::route(); dynamic project-root discovery in test server; legacy root index.php removed.
Router & Server Pipeline
/src/Router.php, /src/Http/Server/MiddlewarePipeline.php
Router rewritten to PSR-7 request creation, handler dispatch, middleware pipeline, computed API paths (apiBase/apiPath/apiFilePath).
Middleware
/src/Http/Middleware/ErrorHandlingMiddleware.php, /src/Http/Middleware/LoggingMiddleware.php
Added error-handling (problem+json) and logging middleware implementing PSR-15.
HTTP Enums (new/moved)
/src/Http/Enum/*
Added/moved enums: AcceptHeader, AcceptabilityLevel, Charset, ContentEncoding, RequestContentType, RequestMethod (moved), ReturnTypeParam, StatusCode.
Legacy Enums Removed
/src/Enum/AcceptHeader.php, /src/Enum/RequestContentType.php, /src/Enum/ReturnType.php, /src/Enum/StatusCode.php
Removed previous Accept/Request/Return/Status types superseded by Http enums.
Data Path Enums
/src/Enum/JsonData.php, /src/Enum/JsonDataConstants.php, /src/Enum/LitSchema.php
JsonData converted to string-backed enum with path() using Router; new JsonDataConstants; LitSchema converted to enum with path(), error(), fromURL().
Core Removal
/src/Core.php
Deleted legacy bootstrap/CORS/content-negotiation/IO class.
Handlers (new/refactor)
/src/Handlers/*
Added/refactored PSR-15 handlers: Calendar, Decrees, Easter, Events, Metadata, Missals, RegionalData, Schemas, Tests; new AbstractHandler with CORS, validation, body parsing, response encoding.
Paths Removed
/src/Paths/*.php
Removed DecreesPath, MissalsPath, RegionalDataPath, SchemasPath, TestsPath in favor of Handlers.
HTTP Exceptions
/src/Http/Exception/*
Added ApiException base and specific exceptions (Validation, NotFound, NotAcceptable, MethodNotAllowed, UnsupportedMediaType, UnsupportedCharset, ServiceUnavailable, Implementation, Yaml).
Negotiation Utility
/src/Http/Negotiator.php
Added Accept/Language/Encoding parsing and selection with browser heuristics.
Params Changes
/src/Params/*
Reworked Calendar, Decrees, Easter (new), Events, Missals, RegionalData: exception-based validation, constructor/signature changes, new properties (Payload, i18n), path-based initialization.
Models & Utilities
/src/Models/.../DecreeItem.php, /src/Models/Lectionary/ReadingsGeneralRoman.php, /src/Utilities.php, /src/Health.php, /src/Test/LitTestRunner.php, /tools/phpstan-stubs/constants.php
Switched to Router::$apiPath for api_path; replaced constants with ->path(); Utilities signatures/errors updated; Health path/schema logic refactored; TestRunner uses path(); removed PHPStan API_BASE_PATH stub.
Tests
/phpunit_tests/bootstrap.php, /phpunit_tests/ApiTestCase.php, /phpunit_tests/Routes/*
Bootstrap discovers project root; ApiTestCase adds findProjectRoot(); new RegionalData tests; Readonly tests moved under Routes\Readonly with path resolution updates and minor renames.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Client
  participant Front as public/index.php
  participant Router as Router
  participant Pipe as MiddlewarePipeline
  participant Err as ErrorHandlingMiddleware
  participant H as <Handler>
  participant Emitter as SAPI Emitter

  Client->>Front: HTTP request
  Front->>Router: bootstrap + Router::route()
  Router->>Router: getApiPaths() / build PSR-7 request
  Router->>Pipe: handle(request)
  Pipe->>Err: process(request, next)
  Err->>H: process→handle(request)
  H-->>Err: Response (or throws ApiException)
  Err-->>Pipe: Response (problem+json on error)
  Pipe-->>Router: Response
  Router->>Emitter: emitResponse(Response)
  Emitter-->>Client: HTTP response
Loading
sequenceDiagram
  autonumber
  actor Client
  participant H as RegionalDataHandler
  participant FS as Filesystem
  participant Schema as LitSchema
  participant Enc as Encoder

  Client->>H: PUT /data/nation/{code} (JSON)
  H->>H: validate method, Accept (INTERMEDIATE), Content-Type
  H->>H: parse body → RegionalDataParams
  H->>Schema: validateDataAgainstSchema()
  Schema-->>H: ok / throws 422
  H->>FS: write i18n files + calendar file
  FS-->>H: OK / error
  H->>Enc: encodeResponseBody(JSON|YAML)
  Enc-->>H: Body + headers
  H-->>Client: 201 Created
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120–180 minutes

Poem

a whisk of ears, I wire the routes,
new paws tread PSR’s sturdy flutes;
middleware burrows, handlers hop,
enums bloom where constants stop.
with tidy nests for tests to run,
I thump “200” — changes done!
now fetch the moon, and cache the sun. 🐇✨

✨ Finishing Touches
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch JohnRDOrazio/issue337

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@JohnRDOrazio
Copy link
Member Author

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 21, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 50

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (19)
phpunit_tests/ApiTestCase.php (1)

25-29: Ensure tests respect API_BASE_PATH by normalizing base_uri and using relative paths

The current TestCase hard-codes base_uri without any API_BASE_PATH and all HTTP calls use absolute paths (leading “/”), which override Guzzle’s base path. On deployments under a non-root path (e.g. /api), requests like ->get('/calendar') will hit http://host/calendar instead of http://host/api/calendar.

Please apply the following critical fixes:

  • In phpunit_tests/ApiTestCase.php:
    • Read and normalize API_BASE_PATH (ensure it has a trailing slash).
    • Include it in the base_uri when instantiating new Client([...]).
    • Update the health-check get('/') to get('').
    • Update setUp() client creation and all calls to use '' instead of '/…'.
--- a/phpunit_tests/ApiTestCase.php
+++ b/phpunit_tests/ApiTestCase.php
@@ protected static function setUpBeforeClass(): void
-        $client = new Client([
-            'base_uri'    => "{$_ENV['API_PROTOCOL']}://{$_ENV['API_HOST']}:{$_ENV['API_PORT']}",
+        $rawBase = $_ENV['API_BASE_PATH'] ?? '/';
+        $path    = rtrim('/'.trim($rawBase, '/'), '/').'/';  // "/" or "/api/" or "/api/v1/"
+        $baseUri = sprintf(
+            '%s://%s:%d%s',
+            $_ENV['API_PROTOCOL'],
+            $_ENV['API_HOST'],
+            (int) $_ENV['API_PORT'],
+            $path
+        );
+        $client = new Client([
+            'base_uri'    => $baseUri,
             'http_errors' => false,
             'timeout'     => 2.0,
         ]);
@@
-        $response           = $client->get('/');
+        $response           = $client->get('');
 
 protected function setUp(): void
@@
-        $this->http = new Client([
-            'base_uri'    => "{$_ENV['API_PROTOCOL']}://{$_ENV['API_HOST']}:{$_ENV['API_PORT']}",
+        $this->http = new Client([
+            'base_uri'    => $baseUri,
             'http_errors' => false,
             'timeout'     => 2.0,
         ]);
  • In every test under phpunit_tests/Routes/** replace absolute paths with relative ones:
    • E.g. change
      $this->http->get('/calendar');
      to
      $this->http->get('calendar');
    • And similarly for ->post('/schemas'), ->get('/events/...'), etc.

Affected test files include (but are not limited to):

  • phpunit_tests/Routes/Readonly/CalendarTest.php
  • phpunit_tests/Routes/Readonly/CalendarsTest.php
  • phpunit_tests/Routes/Readonly/EasterTest.php
  • phpunit_tests/Routes/Readonly/EventsTest.php
  • phpunit_tests/Routes/Readonly/SchemasTest.php
  • phpunit_tests/Routes/ReadWrite/RegionalDataTest.php

After applying these changes, verify with:

API_BASE_PATH=/ composer start && composer test
API_BASE_PATH=/api composer start && composer test
phpunit_tests/Routes/Readonly/EasterTest.php (1)

13-15: Use relative path so Guzzle appends API_BASE_PATH

With base_uri including API_BASE_PATH, absolute paths like “/easter” override the base path. Use a relative path.

-        $response = $this->http->get('/easter');
+        $response = $this->http->get('easter');
phpunit_tests/Routes/Readonly/SchemasTest.php (2)

23-28: Regex should account for API_BASE_PATH

Endpoints may be served under a base path (e.g., “/api” or “/api/v1”). Incorporate API_BASE_PATH into the expected URL pattern.

-        $regex = sprintf(
-            '/^%s:\/\/%s:%d\/schemas\/(?:[A-Z][A-Za-z]+|openapi)\.json$/',
-            $_ENV['API_PROTOCOL'],
-            $_ENV['API_HOST'],
-            $_ENV['API_PORT']
-        );
+        $basePathRaw = $_ENV['API_BASE_PATH'] ?? '/';
+        $basePath    = rtrim('/' . trim($basePathRaw, '/'), '/'); // "" or "/api" or "/api/v1"
+        $regex = sprintf(
+            '/^%s:\/\/%s:%d%s\/schemas\/(?:[A-Z][A-Za-z]+|openapi)\.json$/',
+            $_ENV['API_PROTOCOL'],
+            $_ENV['API_HOST'],
+            $_ENV['API_PORT'],
+            preg_quote($basePath, '/')
+        );

13-16: Make request paths relative so base_uri path is honored

Absolute paths bypass the base path in base_uri. Switch to relative paths.

-        $response = $this->http->get('/schemas');
+        $response = $this->http->get('schemas');
-        $response = $this->http->post('/schemas');
+        $response = $this->http->post('schemas');
-        $response = $this->http->get('/schemas', [
+        $response = $this->http->get('schemas', [
             'headers' => ['Accept' => 'application/yaml']
         ]);
-        $response = $this->http->put('/schemas');
+        $response = $this->http->put('schemas');
-        $response = $this->http->patch('/schemas');
+        $response = $this->http->patch('schemas');
-        $response = $this->http->delete('/schemas');
+        $response = $this->http->delete('schemas');

Also applies to: 51-56, 70-75, 79-81, 85-87, 91-93

phpunit_tests/Routes/Readonly/CalendarsTest.php (2)

327-334: Fail fast if project root is not found.

ApiTestCase::findProjectRoot() returns ?string. If null, $filePath becomes "/jsondata/world_dioceses.json", yielding a misleading “File not found” later.

-            $root     = ApiTestCase::findProjectRoot();
-            $filePath = $root . '/jsondata/world_dioceses.json';
+            $root = ApiTestCase::findProjectRoot();
+            if (null === $root) {
+                throw new \RuntimeException('Unable to locate project root from ApiTestCase::findProjectRoot().');
+            }
+            $filePath = $root . '/jsondata/world_dioceses.json';

360-365: Regex should escape protocol/host; dots in 127.0.0.1 currently match any char.

preg pattern is built with raw $_ENV values; host like 127.0.0.1 makes the regex too permissive. Escape dynamic parts.

-            self::$WIDER_REGION_API_PATH_PATTERN = sprintf(
-                '/^%s:\/\/%s:%d\/data\/widerregion\/(Europe|Africa|Asia|Oceania|Americas)\?locale=\{locale\}$/',
-                $_ENV['API_PROTOCOL'],
-                $_ENV['API_HOST'],
-                $_ENV['API_PORT']
-            );
+            $proto = preg_quote((string)($_ENV['API_PROTOCOL'] ?? ''), '/');
+            $host  = preg_quote((string)($_ENV['API_HOST'] ?? ''), '/');
+            $port  = (int)($_ENV['API_PORT'] ?? 0);
+            self::$WIDER_REGION_API_PATH_PATTERN = sprintf(
+                '/^%s:\/\/%s:%d\/data\/widerregion\/(Europe|Africa|Asia|Oceania|Americas)\?locale=\{locale\}$/',
+                $proto,
+                $host,
+                $port
+            );
phpunit_tests/Routes/Readonly/EventsTest.php (2)

20-21: Fix PHPUnit assertion call: too many arguments

assertSame expects (expected, actual, message). There’s a duplicated message arg that will cause a type error.

-        $this->assertSame(JSON_ERROR_NONE, json_last_error(), 'Invalid JSON: ' . json_last_error_msg(), 'Invalid JSON: ' . json_last_error_msg());
+        $this->assertSame(JSON_ERROR_NONE, json_last_error(), 'Invalid JSON: ' . json_last_error_msg());

12-12: Initialize typed static property to avoid “uninitialized typed property” pitfalls

Safer to declare the static property as nullable and initialize it, then check against null.

-    private static object $metadata;
+    private static ?object $metadata = null;
@@
-        if (false === isset(self::$metadata)) {
+        if (self::$metadata === null) {

Also applies to: 160-166

src/Utilities.php (2)

475-483: Incorrect English ordinal suffix logic (11/12/13 edge cases).

Current logic yields “111st” instead of “111th”. Handle 11–13 as special cases using % 100.

-        if ($ord === 1 || ( $ord % 10 === 1  && $ord <> 11 )) {
-            $ord_suffix = 'st';
-        } elseif ($ord === 2 || ( $ord % 10 === 2  && $ord <> 12 )) {
-            $ord_suffix = 'nd';
-        } elseif ($ord === 3 || ( $ord % 10 === 3  && $ord <> 13 )) {
-            $ord_suffix = 'rd';
-        } else {
-            $ord_suffix = 'th';
-        }
+        $mod100 = $ord % 100;
+        if ($mod100 >= 11 && $mod100 <= 13) {
+            $ord_suffix = 'th';
+        } else {
+            switch ($ord % 10) {
+                case 1: $ord_suffix = 'st'; break;
+                case 2: $ord_suffix = 'nd'; break;
+                case 3: $ord_suffix = 'rd'; break;
+                default: $ord_suffix = 'th';
+            }
+        }

498-503: Guard Latin ordinals array bounds.

Accessing $latinOrdinals[$num] without checking may emit notices for out-of-range keys. Throw a domain error if missing.

-            case 'la':
-                $ordinal = $latinOrdinals[$num];
-                break;
+            case 'la':
+                if (!array_key_exists($num, $latinOrdinals)) {
+                    throw new ServiceUnavailableException('Missing Latin ordinal for ' . $num);
+                }
+                $ordinal = $latinOrdinals[$num];
+                break;
src/Params/RegionalDataParams.php (1)

26-26: Make payload nullable and initialize; avoid uninitialized typed property.

$payload is only set conditionally; accessing it when not provided will fatally error.

-    public DiocesanData|NationalData|WiderRegionData $payload;
+    public DiocesanData|NationalData|WiderRegionData|null $payload = null;
src/Health.php (1)

317-320: Bug: preg_match check is always “true” unless an error occurs

preg_match() returns 1 (match), 0 (no match), or false (error). Comparing with !== false turns both 1 and 0 into true, so $isVersionedDataPath is effectively always true unless there’s a regex error. This will incorrectly rewrite non-versioned paths.

-        $isVersionedDataPath  = preg_match($versionedPattern, $dataPath) !== false;
+        $isVersionedDataPath  = preg_match($versionedPattern, $dataPath) === 1;
src/Handlers/EasterHandler.php (1)

126-130: Wrong day-of-week source for Julian date string

  • You format the Julian date line with the day-of-week taken from the Gregorian timestamp.
-                    $julianDateString        = self::$dayOfTheWeek->format($gregorian_easter->format('U'))
+                    $julianDateString        = self::$dayOfTheWeek->format($julian_easter->format('U'))
                                                     . ', ' . self::$dayMonthYear->format($julian_easter->format('U'));
src/Enum/RomanMissal.php (1)

323-331: Use GlobIterator (or glob()) instead of DirectoryIterator with glob://

  • DirectoryIterator("glob://$i18n_path*.json") is non-standard; GlobIterator is the SPL type intended for patterns.

Apply:

-            if ($i18n_path) {
-                $iterator = new \DirectoryIterator("glob://$i18n_path*.json");
-                foreach ($iterator as $file) {
-                    $locales[] = $file->getBasename('.json');
-                }
-            }
+            if ($i18n_path) {
+                /** @var \GlobIterator $iterator */
+                $iterator = new \GlobIterator($i18n_path . '*.json', \FilesystemIterator::KEY_AS_PATHNAME);
+                foreach ($iterator as $file) {
+                    /** @var \SplFileInfo $file */
+                    $locales[] = $file->getBasename('.json');
+                }
+            }

Add at the top of the file if you prefer imports:

use GlobIterator;
use FilesystemIterator;
use SplFileInfo;
src/Handlers/CalendarHandler.php (4)

4308-4316: Add cURL timeouts to avoid hanging on GitHub API

External calls should be bounded. Add sane connect/read timeouts.

             $ch = curl_init();
             curl_setopt($ch, CURLOPT_URL, $GithubReleasesAPI);
             curl_setopt($ch, CURLOPT_USERAGENT, 'LiturgicalCalendar');
             curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+            curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
+            curl_setopt($ch, CURLOPT_TIMEOUT, 10);
             //curl_setopt($ch, CURLOPT_HTTPHEADER, $GhRequestHeaders);

736-738: Replace die() with exceptions in handlers

Calling die() inside a PSR-15 handler can kill the worker and bypass middleware/error handling. Throw typed exceptions instead.

-            die("createPropriumDeTemporeLiturgicalEventByKey requires a key from the Proprium de Tempore, instead got $key");
+            throw new \InvalidArgumentException(sprintf(
+                'createPropriumDeTemporeLiturgicalEventByKey requires a key from the Proprium de Tempore; got %s',
+                var_export($key, true)
+            ));
-                        die("this is strange, {$event_key} is not suppressed? Where is it?");
+                        throw new \RuntimeException("Unexpected state: '{$event_key}' is not suppressed and cannot be reinstated.");

Also applies to: 3845-3846


1605-1613: Bug: wrong date variable used when resolving coinciding events (late Ordinary Time loop)

Inside the second loop you use $firstOrdinaryDate when you should be looking at $lastOrdinary. This breaks supersession messages and can throw.

-                $litEvent = $this->Cal->solemnityFromDate($firstOrdinaryDate) ?? $this->Cal->feastLordFromDate($firstOrdinaryDate);
+                $litEvent = $this->Cal->solemnityFromDate($lastOrdinary) ?? $this->Cal->feastLordFromDate($lastOrdinary);
-                if (null === $litEvent) {
-                    throw new \RuntimeException('We were expecting to find either a Solemnity or a Feast of the Lord on ' . $lastOrdinary->format('Y-m-d') . ' but no LiturgicalEvent was not found');
-                }
+                if (null === $litEvent) {
+                    throw new \RuntimeException('Expected Solemnity or Feast of the Lord on ' . $lastOrdinary->format('Y-m-d') . ' but none was found');
+                }

4243-4250: Probable typo: calls dateIsNotSunday() on collection instead of the local helper

LiturgicalEventCollection likely doesn’t expose dateIsNotSunday(DateTime). You defined a local static (self::dateIsNotSunday), which is used elsewhere. Use it here too.

-                    } elseif ($liturgicalEvent->grade->value <= LitGrade::FEAST->value && $this->Cal->notInSolemnities($currentLitEventDate) && $this->Cal->dateIsNotSunday($currentLitEventDate)) {
+                    } elseif ($liturgicalEvent->grade->value <= LitGrade::FEAST->value && $this->Cal->notInSolemnities($currentLitEventDate) && self::dateIsNotSunday($currentLitEventDate)) {
src/Handlers/MetadataHandler.php (1)

71-76: Bug: corpus_christi setting points at Ascension enum

In buildNationalCalendarData(), 'corpus_christi' is assigned Ascension::THURSDAY rather than CorpusChristi::THURSDAY. This will serialize the wrong value and break downstream behavior.

Apply this diff (and import the enum):

@@
-use LiturgicalCalendar\Api\Enum\Route;
+use LiturgicalCalendar\Api\Enum\Route;
+use LiturgicalCalendar\Api\Enum\CorpusChristi;
@@
-                'corpus_christi'      => Ascension::THURSDAY->value,
+                'corpus_christi'      => CorpusChristi::THURSDAY->value,

Also applies to: 5-13

♻️ Duplicate comments (1)
src/Handlers/RegionalDataHandler.php (1)

1089-1102: Static analysis false positive: validateDataAgainstSchema is used

  • Despite PHPMD flagging it as unused, it is invoked in handle() for PUT/PATCH (see Lines 1446, 1452, 1458).

No action needed.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review continued from previous batch...

@JohnRDOrazio JohnRDOrazio merged commit a77047c into development Aug 21, 2025
2 checks passed
@JohnRDOrazio JohnRDOrazio deleted the JohnRDOrazio/issue337 branch August 21, 2025 14:09
@JohnRDOrazio
Copy link
Member Author

JohnRDOrazio commented Aug 22, 2025

fixes #338 #337

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

move the main index.php entrypoint to a public folder Refactor for PSR7, PSR15, and PSR17 compliance

2 participants