User tests: Successful: Unsuccessful:
Hi all,
We run several Joomla sites that are written to through the Web Services API, and found records staying checked out after a PATCH was refused. The next person to open the record in the backend was told it was checked out by the API user. Related, but not the same case: #42219 (that report was about successful updates and could not be reproduced).
ApiController::edit() checks the record out and then calls save(). save() throws when the form validation fails (InvalidParameterException) or when the model refuses the save (Exception\Save), and both happen before the check-in at the end of save(). So every refused PATCH left the record checked out to the API user.
edit() now wraps the save() call and checks the record back in before rethrowing. It only does so when this request took the check-out, so a lock the user already held stays in place. This mirrors the HTML forms: FormController keeps the record checked out after a refused save because the user returns to the edit form and later saves or cancels, but an API request has no follow-up edit or cancel, so the refusal ends the edit. A successful save is unchanged (save() still does its own check-in), and FormModel::checkin() already returns early for tables without check-out columns.
Tests:
tests/Unit/Libraries/Cms/MVC/Controller/ApiControllerTest.php covers a refused edit and a successful edit.tests/System/integration/api/com_content/Articles.cy.js, "does not leave an article checked out when an update is refused", which sends a PATCH with an empty title and checks checked_out afterwards.Both new tests fail without this change (the Cypress case reports the API user's id as the checked_out value) and pass with it. Run locally with the system-test Docker image: npx cypress run --spec tests/System/integration/install/Installation.cy.js,tests/System/integration/api/com_content/Articles.cy.js.
Test on current 6.2-dev.
PATCH /api/index.php/v1/content/articles/{id} with body {"title": ""}The article shows the checked-out lock, held by the API user. Opening it as another user reports that it is checked out, and it stays that way until someone checks it in.
The API still answers with "Field required: Title", and the article is not checked out. A valid PATCH (for example {"title": "New title"}) still saves and leaves the article checked in, as before.
Please select:
Documentation link for guide.joomla.org:
No documentation changes for guide.joomla.org needed
Pull Request link for manual.joomla.org:
No documentation changes for manual.joomla.org needed
Thanks for taking a look. Happy to change the approach or the test if you'd prefer it done differently.
| Status | New | ⇒ | Pending |
| Category | ⇒ | Libraries JavaScript Unit Tests |
Only security fixes go into 5.4 now so please rebase to 6.2-dev
| Labels |
Added:
Unit/System Tests
Test instructions missing
PR-5.4-dev
|
||
| Title |
|
||||||
Rebased onto 6.2-dev as requested, thanks @brianteeman. I re-ran the tests locally on 6.2-dev after the rebase: the full unit suite and the new system test both pass. Could someone remove the PR-5.4-dev and "Test instructions missing" labels? The test instructions are in the description.
| Labels |
Added:
PR-6.2-dev
|
||
I have tested this item ✅ successfully on b2f7a43
I have tested this item ✅ successfully on b2f7a43
I have tested this item ✅ successfully on b2f7a43
Hi @genr8r, thank you for the PR, I have successfully tested your PR fix.
I have tested this item ✅ successfully on b2f7a43
Hi @genr8r, thank you for the PR, I have successfully tested your PR fix.
| Status | Pending | ⇒ | Ready to Commit |
RTC
RTC
| Labels |
Added:
RTC
bug
Webservices
Removed: Test instructions missing PR-5.4-dev |
||
Hi @genr8r – thanks for the PR. If the article is already checked out by the token's user (e.g. open in the backend), checkout() still succeeds, so a refused PATCH now checks it in and removes that lock. Could edit() only check in on failure if this request took the lock?
| Status | Ready to Commit | ⇒ | Pending |
Back to pending
Back to pending
| Labels |
Added:
Updates Requested
Removed: RTC |
||
Thanks @MacJoom, good catch. edit() now records whether the row was already checked out before calling checkout(), and only checks it back in on a refused save if this request took the lock. I added a unit test for that case, and confirmed it against a real database with a temporary Cypress spec: a refused PATCH keeps a lock the token's user already held, and still releases one it took itself. One thing I left alone: a successful PATCH still releases an existing lock through the check-in at the end of save(). That is existing behaviour, so I kept it out of this PR, but I can change it here if you'd prefer.
Please add more information to your issue. Without test instructions and/or any description we will close this issue within 4 weeks. Thanks.
This is an automated message from the J!Tracker Application.