Skip to content

feat(permissions): add page permissions + handle orphaned relation fields#71

Merged
EricFernandezPort merged 4 commits into
port-experimental:mainfrom
bgilleran-port:feature/page-permissions
May 29, 2026
Merged

feat(permissions): add page permissions + handle orphaned relation fields#71
EricFernandezPort merged 4 commits into
port-experimental:mainfrom
bgilleran-port:feature/page-permissions

Conversation

@bgilleran-port

Copy link
Copy Markdown
Contributor

Summary

  • Page-level permissions: Adds full lifecycle support for page permissions across export, import, migrate, diff/compare, and all CLI commands — following the same pattern as blueprint and action permissions.
  • Orphaned relation/property handling: When importing blueprint permissions, the API may reject the payload with 422 invalid_permissions because it references relations or properties that don't exist on the target blueprint (e.g., _rule_result relations auto-created by scorecards that were later deleted). This fix detects the 422, parses invalidRelations/invalidProperties from the API response, strips those keys from both top-level and nested entities.updateRelations/updateProperties scopes, retries the PATCH, and surfaces a warning to the user.

Changes

Page permissions (521d5a8)

  • api/requests.go: GetPagePermissions + UpdatePagePermissions methods
  • export/collector.go: Fetch page permissions during export
  • export/export.go: Include page_permissions in tar + JSON output
  • import_module/loader.go: Load page_permissions from tar and JSON (both PascalCase and snake_case)
  • import_module/diff.go: Compare page permissions in diff step
  • import_module/import.go: Apply page permission changes during import
  • migrate/migrate.go: Collect and apply page permissions during migration
  • commands/: Updated validResources maps and output counts in export, import, compare, migrate

Orphaned fields fix (2da20d4)

  • import_module/import.go: Added isInvalidPermissionsError, ParseInvalidPermissionFields, SanitizePermissions helpers; updated importPermissions with retry-on-422 logic for blueprint, action, and page permissions
  • migrate/migrate.go: Same retry pattern in migrate permission loops; added Warnings field to Result struct
  • .gitignore: Ignore platform-specific binaries (port-darwin-*, port-linux-*, port-windows-*)

Test plan

  • Unit tests for isInvalidPermissionsError, ParseInvalidPermissionFields, SanitizePermissions (top-level and nested)
  • Unit test for retry flow: mock API returns 422 first call, succeeds on retry with stripped payload
  • Unit test for retry-still-fails case: error propagates correctly
  • Existing TestImportPermissions_CountsOnlySuccesses updated for page permissions return value
  • E2E verified: created scorecard on source org → orphaned _test_3 relation on _rule_result → exported → imported to target org → fix stripped orphaned fields and retry succeeded
  • All tests pass: go test ./...
  • gofmt clean

Made with Cursor

BGilleran522 and others added 4 commits May 28, 2026 15:17
… migrate

- Add GetPagePermissions and UpdatePagePermissions API methods
  (PATCH /v1/pages/:page_identifier/permissions)
- Add PagePermissions field to export.Data struct
- Collect page permissions during export alongside page data, with
  warnings on fetch failures
- Include page_permissions in tar.gz and JSON export output
- Load page_permissions from both tar and JSON formats in loader
  (supports PascalCase and snake_case keys for backward compat)
- Add page permissions to diff comparison, gated by
  shouldImport("page-permissions") for --include flag support
- Apply page permission changes during import after resource import
- Add PagePermissionsUpdated to import and migrate Result structs,
  populated in both dry-run and real execution
- Collect and apply page permissions in migrate module
- Add "page-permissions" to valid --include resources across all
  commands (import, export, compare, migrate)
- Display page permission counts in CLI text and JSON output

Co-authored-by: Cursor <cursoragent@cursor.com>
When importing blueprint permissions, the payload may reference relations
or properties that don't exist on the target blueprint. This commonly
occurs with system blueprints like _rule_result: Port auto-creates
relations when scorecards exist, but doesn't remove them when scorecards
are deleted. The exported permissions carry those orphaned references,
causing a 422 invalid_permissions error on the target.

This fix detects the 422 response, parses the invalidRelations and
invalidProperties from the API error body, strips those keys from
both the top-level and nested entities.updateRelations/updateProperties
scopes, and retries the PATCH. A warning is surfaced to the user about
the stripped fields.

Applies to both import and migrate paths.

Co-authored-by: Cursor <cursoragent@cursor.com>
- Surface result.Warnings and page_permissions_updated in migrate command
  text and JSON output (were silently discarded)
- Record warning instead of silently dropping page permission fetch
  failures in exportFromSource
- Add tests: page permissions retry-on-422 in importPermissions,
  importToTarget retry path, collector gating, and exportFromSource
  warning recording

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Entire-Checkpoint: bea11f65f84f
Co-authored-by: Cursor <cursoragent@cursor.com>
@EricFernandezPort EricFernandezPort merged commit ba05515 into port-experimental:main May 29, 2026
4 checks passed
bgilleran-port pushed a commit to bgilleran-port/port-cli that referenced this pull request Jun 5, 2026
Add missing tests for the page permissions feature introduced in PRs port-experimental#70
and port-experimental#71, following Eric's established test patterns:

- TestCollector_CollectsPagePermissions: positive collection verification
- TestLoader_LoadJSON_PagePermissions: JSON round-trip loading
- TestComparePermissions_DetectsExtraFieldsAsChange: diff detection
- TestComparePermissions_NormalizesStringSliceOrder: slice normalization
- Updated TestDiffResult_PermissionsFields to include PagePermissions

Co-authored-by: Cursor <cursoragent@cursor.com>
EricFernandezPort added a commit that referenced this pull request Jun 5, 2026
…nt fields (#75)

* fix(migrate): add topological sorting and retry for dependent blueprint fields

The migrate module was applying aggregationProperties, mirrorProperties,
and ownership concurrently without dependency ordering, while the import
module already had these protections. This caused the Port API to silently
nullify pathFilter in aggregation properties and drop inherited ownership
when dependencies weren't ready yet.

Changes:
- Phase 2c (mirrorProperties): collect failures for retry after Phase 2d
- Phase 2d (aggregationProperties): use TopologicalSortAggProps to ensure
  cross-blueprint agg prop dependencies are applied in order
- Phase 2c retry: re-apply failed mirror props after agg props exist
- Phase 2e (ownership): use TopologicalSortOwnership to ensure inherited
  ownership chains are applied in dependency order
- Phase 3 retry: re-apply failed agg props after all other phases complete

Fixes: aggregationProperties.pathFilter being silently nullified and
inherited ownership being dropped during port migrate.

Co-authored-by: Cursor <cursoragent@cursor.com>

* test(permissions): add test coverage for page permissions

Add missing tests for the page permissions feature introduced in PRs #70
and #71, following Eric's established test patterns:

- TestCollector_CollectsPagePermissions: positive collection verification
- TestLoader_LoadJSON_PagePermissions: JSON round-trip loading
- TestComparePermissions_DetectsExtraFieldsAsChange: diff detection
- TestComparePermissions_NormalizesStringSliceOrder: slice normalization
- Updated TestDiffResult_PermissionsFields to include PagePermissions

Co-authored-by: Cursor <cursoragent@cursor.com>

* test(migrate): add tests for dependent field ordering and retry

- TestImportToTarget_AggPropsAppliedInTopologicalOrder: verify cross-
  blueprint agg prop dependencies are applied in correct order
- TestImportToTarget_FailedAggPropsRetried: verify failed agg prop
  updates are retried in a second pass
- TestImportToTarget_OwnershipAppliedInTopologicalOrder: verify
  inherited ownership is applied after its dependency

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(review): apply pre-landing review fixes

- Validate that page-permissions requires pages in --include (export, import, migrate, compare)
  to prevent silent empty export and idempotency-breaking force-overwrite
- Fix invalidPermBodyPattern regex: add (?s) flag so multi-line JSON 422 bodies are matched
- Fix migrate errors section mislabeled as "Warnings:" → "Errors:"
- Add blueprint_permissions_updated and action_permissions_updated to migrate JSON output
- Strengthen import_test retryBody nil guard: use t.Fatal instead of silent skip
- Update ValidationWarning.Type comment to include "orphaned_permission_field"
- Rewrite CHANGELOG 0.2.19 entry to describe features and fixes, not test names

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Entire-Checkpoint: a0896ec6ff15

* style: run make format

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Entire-Checkpoint: 55a24cc72286

---------

Co-authored-by: Bill Gilleran <bgilleran@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: EricFernandezPort <ericf@getport.io>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants