[Studio] Keep import execution available for read-only configurations - #684
[Studio] Keep import execution available for read-only configurations#684robertSt7 wants to merge 6 commits into
Conversation
The studio frontend had no test runner, so logic that lives only in the React layer could not be covered. Adds Jest with the SWC transform, mirroring the setup of the studio UI bundle, and enables the test step of the shared studio frontend build workflow. Co-Authored-By: Claude <noreply@anthropic.com>
Uploading an import file and cancelling a running import were unavailable whenever the configuration itself could not be persisted. A configuration that comes from the Symfony configuration is reported as not writeable unless the instance runs in debug mode, so those actions were dimmed in production even for users who are allowed to update the configuration. The reason is that the writeability was folded into the single "is writeable" flag that also puts the whole configuration form into a read-only state. Both buttons had no explicit disabled state and therefore inherited it from the form. The capability computation is extracted into resolveConfigCapabilities(), which keeps the two aspects apart: editing the configuration requires the update permission and a writeable storage, while executing an import only requires the permission - the same authorisation the upload, start and cancel endpoints apply. The execution controls now always pass an explicit disabled state, so they reflect the permission instead of the read-only form. As a side effect the start button is gated on the permission as well, which it was not before. Co-Authored-By: Claude <noreply@anthropic.com>
|
@copilot review |
There was a problem hiding this comment.
Pull request overview
Separates configuration writeability from import execution permissions so authorized users can run imports on read-only configurations.
Changes:
- Adds centralized configuration capabilities and React context.
- Explicitly gates upload, start, and cancel actions by update permission.
- Adds Jest tests and CI execution.
Reviewed changes
Copilot reviewed 21 out of 41 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
.github/workflows/studio-frontend-build.yaml |
Enables frontend tests in CI. |
assets/studio/eslint.config.mjs |
Adds Jest globals. |
assets/studio/jest.config.ts |
Configures Jest with SWC. |
assets/studio/js/src/modules/data-importer/components/config-capabilities-context.tsx |
Provides capabilities through React context. |
assets/studio/js/src/modules/data-importer/components/data-importer-detail-view.tsx |
Resolves and distributes capabilities. |
assets/studio/js/src/modules/data-importer/components/tabs/execution-tab/execution-status/execution-status.tsx |
Applies execution permission to start and cancel controls. |
assets/studio/js/src/modules/data-importer/components/tabs/execution-tab/manual-execution-button/manual-execution-button.tsx |
Gates starting imports by permission. |
assets/studio/js/src/modules/data-importer/dynamic-types/loader/upload/upload-loader-settings.tsx |
Gates file uploads by permission. |
assets/studio/js/src/modules/data-importer/utils/config-capabilities.test.ts |
Tests the capability matrix. |
assets/studio/js/src/modules/data-importer/utils/config-capabilities.ts |
Defines capability resolution. |
assets/studio/package-lock.json |
Locks new test dependencies. |
assets/studio/package.json |
Adds Jest tooling and test script. |
assets/studio/tsconfig.json |
Adds Jest type definitions. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| { /* Cancelling a run is an execution action, so `disabled` is passed | ||
| explicitly instead of inheriting the read-only form state. */ } | ||
| <Button | ||
| disabled={ !canRunImport } |
Here is the review of the PR changes: SummaryThe fix correctly separates two independent concerns that were incorrectly conflated:
The backend endpoints for upload/start/cancel only check the user permission, so the UI should mirror that. Code quality observations✅ Correct
|
The upload and start controls already show why they are unavailable when the user may not update the configuration. The cancel button was left without that explanation, which is the situation this change set out to avoid. Co-Authored-By: Claude <noreply@anthropic.com>
|
@copilot review |
The tooltip was already added to the Cancel button in commit |
|



Issue
https://github.com/pimcore/service-operations/issues/977
The "Upload File" button and the "Cancel" action of a running import were dimmed on instances that
do not run with
APP_DEBUG=true/APP_ENV=dev, even for an admin user. Reproducible withimport configurations stored under
symfony-config.Root cause
Configuration::getConfiguration()(data-hub) exposesgeneral.writeable, which comes fromLocationAwareConfigRepository::isWriteable():So for configurations whose write target is the Symfony configuration,
general.writeableisfalsein every non-debug environment — that is theAPP_DEBUGdependency from the report.#642 ("Enforce per-config permissions in the data importer config UI") folded that flag into the
single
isWriteablevalue indata-importer-detail-view.tsx:That value is also passed to
BaseDetailView disabled={ !isWriteable }, which ends up on the antdFormasdisabled. antd propagates it throughDisabledContextto every control inside theform, and antd's
Buttonmerges it ascustomDisabled ?? contextDisabled. Controls that pass anexplicit
disabledboolean therefore keep working, while controls that pass none inherit it.The only two controls in the data importer form without an explicit
disabledwere the"Upload File" button (
upload-loader-settings.tsx) and the "Cancel" button(
execution-status.tsx) — exactly the two actions named in the report. "Start Import" alreadypassed an explicit boolean, which is why it was not reported as dimmed.
The backend was never affected:
ImportService::uploadImportFile(),startImport()andcancelExecution()authorise against the per-configurationupdatepermission(
Configuration::isAllowed('update')) and do not consult writeability at all. Verified stillpresent on
2026.2.What changed
utils/config-capabilities.tswithresolveConfigCapabilities(), which keeps the twoaspects apart:
canSaveConfig/canDeleteConfig— permission and writeable storage (unchanged, [Studio] Enforce per-config permissions in the data importer config UI #642'sintent is preserved: a read-only configuration stays read-only and cannot be saved or deleted).
canRunImport— permission only, mirroring what the upload/start/cancel endpoints enforce.ConfigCapabilitiesProvidercontext, so the upload loader settings — rendered through thedynamic type registry — can read the capabilities without changing
DynamicTypeLoaderAbstract::renderSettings()(no BC break for custom loader types).disabledboolean, so they reflect the execution permission instead of inheriting the read-only form state,
and show a tooltip when the permission is missing.
No permission check was removed or widened. The UI gate is now exactly the backend gate. As a
side effect "Start Import" is now also gated on the update permission — previously it was enabled
for a read-only user and the request failed with 403.
Tests
The regression lives in the React layer, which had no test runner in this repository. Added Jest
(SWC transform, mirroring the studio UI bundle setup) and enabled the
teststep of the sharedreusable-studio-frontend-build.yamlworkflow, plusutils/config-capabilities.test.tscovering the regression and the surrounding permission matrix.Verified locally in
assets/studio:npm ci— clean install from the regenerated lockfile (332 packages added, only 10 dev-only@babel/*patch bumps, nothing removed).npm run test— 7 passed. The regression case fails against the pre-fix logic(
Expected: true, Received: falseforcanRunImporton a non-writeable configuration) andpasses after.
npm run check-typesandnpm run lint— clean.npm run build— succeeds; the generated build output was reverted so CI produces the committedbundle.
Not verified locally: the PHP Codeception suite (no
vendor/in this workspace) — no PHP wastouched, so it relies on CI. The fix was not exercised against a running Pimcore instance; the
mechanism was established from the antd
DisabledContextimplementation and the compiledBaseDetailViewin the installed data-hub bundle rather than from a browser session.🤖 Generated with Claude Code