Skip to content

Add State Admin API endpoints - #293

Open
boo-code wants to merge 2 commits into
PrestaShop:devfrom
boo-code:feat/state-endpoints
Open

boo-code wants to merge 2 commits into
PrestaShop:devfrom
boo-code:feat/state-endpoints

Conversation

@boo-code

@boo-code boo-code commented Jun 27, 2026 •

Copy link
Copy Markdown
Questions Answers
Description? Adds GET / POST / PATCH / DELETE /states Admin API endpoints, exposing the existing State CQRS (Add/Edit/Delete commands + GetStateForEditing) as an ApiPlatform resource. This completes the geographic data model exposed by the API, alongside the existing Country and Zone resources.
Type? new feature
BC breaks? no
Deprecations? no
Fixed ticket? Contributes to PrestaShop/PrestaShop#39630
Sponsor company Boring Investments
How to test? Run the new StateEndpointTest (composer run-module-tests). It covers create/get/update/delete, protected-endpoint authentication, unknown and 0 ids, the field constraints on create and update, and the delete of a state used by an address (422). Endpoints require a client with the state_read / state_write scopes.

Note

testDeleteState asserts the 204 response and a 404 on a non-existent id rather than re-fetching the just-deleted state. State::delete() removes the row with a direct query without going through parent::delete(), so the ObjectModel cache is not cleared within the same PHP process — this is only observable in the single-process test runner (each real API request is a fresh process). This mirrors the existing ZoneEndpointTest delete convention.

Verified locally against a 9.2.1 install — StateEndpointTest passes (30 tests, 236 assertions); php-cs-fixer and PHPStan are clean on the added files.

@ps-jarvis

Copy link
Copy Markdown

Hello @boo-code!

This is your first pull request on ps_apiresources repository of the PrestaShop project.

Thank you, and welcome to this Open Source community!

@github-project-automation github-project-automation Bot moved this to Ready for review in PR Dashboard Jun 27, 2026
@boo-code
boo-code force-pushed the feat/state-endpoints branch from bca041e to 33905ef Compare July 29, 2026 01:42
),
],
normalizationContext: ['skip_null_values' => false],
exceptionToStatus: [

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

AddStateHandler throws the base StateException itself (invalid field values, wrapped PrestaShopException), and DeleteStateHandler throws DeleteStateException when an address uses the state (NY or FL in the demo data), so both answer 500 today. Could you map StateException to 422 after StateNotFoundException (it also covers StateConstraintException), and add a test deleting a state used by an address?

#[ApiProperty(identifier: true)]
public int $stateId;

#[Assert\NotBlank(groups: ['Create'])]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Only presence is checked, so values the back-office StateType refuses reach the legacy model: isoCode: "ABCDEF" or name: "<b>" make AddStateHandler throw StateException, and countryId: 0 or zoneId: 0 throw CountryConstraintException / ZoneException from the command, all answering 500. Could you add the form constraints in the Default group, as Country does (Length(max: 64) and TypedRegex(TYPE_GENERIC_NAME) on name, Length(max: 7) and TypedRegex(TYPE_STATE_ISO_CODE) on isoCode, Assert\Positive on countryId and zoneId)?

{
$state = $this->createItem('/states', $this->getCreatePayload(), ['state_write']);

$this->assertArrayHasKey('stateId', $state);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Could you assert the whole POST response here, as ZoneEndpointTest::testAddZone does? An expectedState(int $stateId, array $overrides = []) helper reused by the add, get and update steps would also remove the duplicated expected arrays.

$return = $this->deleteItem('/states/' . $stateId, ['state_write']);
$this->assertNull($return);

// Fetching a state that does not exist returns a 404 (StateNotFoundException mapping).

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Since daffd28, ApiTestCase::requestApi() clears the legacy Cache and the ObjectModel static cache before each request, so after a rebase the deleted state should answer 404 here (ZoneEndpointTest checks the deleted id too, not an unknown one). Could you GET '/states/' . $stateId and move the 999999 check to its own test?

$this->getItem('/states/999999', ['state_read'], Response::HTTP_NOT_FOUND);
}

public function testInvalidState(): void

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Only the blank name is covered; CountryEndpointTest on dev is a good model for the rest. Could you add the 404 on GET, PATCH and DELETE of an unknown id, /states/0 on each operation (422 through StateId on GET and PATCH, 404 through NoStateId on DELETE), one invalid case per field on POST and PATCH, and the delete of a state used by an address?

@ps-jarvis ps-jarvis moved this from Ready for review to Waiting for author in PR Dashboard Oct 29, 2026
boo-code and others added 2 commits October 29, 2026 19:12
Expose the State CQRS (Add/Edit/Delete + GetStateForEditing) as an Admin API
resource at /states, completing the geographic data model alongside the
existing Country and Zone resources.

Adds StateEndpointTest covering create/get/update/delete, protected-endpoint
authentication and validation.

Refs #39630.
@boo-code
boo-code force-pushed the feat/state-endpoints branch from 33905ef to 8cd6edc Compare October 29, 2026 18:47
@boo-code

Copy link
Copy Markdown
Author

All done:

  • StateException mapped to 422 after StateNotFoundException; it also covers DeleteStateException, CannotAddStateException and CannotUpdateStateException.
  • Length(max: 64) + TypedRegex(TYPE_GENERIC_NAME) on name, Length(max: 7) + TypedRegex(TYPE_STATE_ISO_CODE) on isoCode, Positive on countryId and zoneId, in Default.
  • Whole POST response asserted through expectedState(), reused by the get and update steps.
  • Rebased on dev: the deleted id answers 404; 999999 moved to testNotFoundState.
  • Added /states/0 (422 on GET and PATCH, 404 on DELETE), one invalid case per field on POST and PATCH, and the delete of New York -> 422.

The new cases on the previous resource: 3 tests fail, all with 500. After the change: 30/30 on a 9.2.1 core.

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

Projects

Status: Waiting for author

Development

Successfully merging this pull request may close these issues.

3 participants