feat(permissions): add page permissions + handle orphaned relation fields#71
Merged
EricFernandezPort merged 4 commits intoMay 29, 2026
Conversation
… 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
approved these changes
May 29, 2026
3 tasks
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
422 invalid_permissionsbecause it references relations or properties that don't exist on the target blueprint (e.g.,_rule_resultrelations auto-created by scorecards that were later deleted). This fix detects the 422, parsesinvalidRelations/invalidPropertiesfrom the API response, strips those keys from both top-level and nestedentities.updateRelations/updatePropertiesscopes, retries the PATCH, and surfaces a warning to the user.Changes
Page permissions (
521d5a8)api/requests.go:GetPagePermissions+UpdatePagePermissionsmethodsexport/collector.go: Fetch page permissions during exportexport/export.go: Includepage_permissionsin tar + JSON outputimport_module/loader.go: Loadpage_permissionsfrom tar and JSON (both PascalCase and snake_case)import_module/diff.go: Compare page permissions in diff stepimport_module/import.go: Apply page permission changes during importmigrate/migrate.go: Collect and apply page permissions during migrationcommands/: UpdatedvalidResourcesmaps and output counts in export, import, compare, migrateOrphaned fields fix (
2da20d4)import_module/import.go: AddedisInvalidPermissionsError,ParseInvalidPermissionFields,SanitizePermissionshelpers; updatedimportPermissionswith retry-on-422 logic for blueprint, action, and page permissionsmigrate/migrate.go: Same retry pattern in migrate permission loops; addedWarningsfield toResultstruct.gitignore: Ignore platform-specific binaries (port-darwin-*,port-linux-*,port-windows-*)Test plan
isInvalidPermissionsError,ParseInvalidPermissionFields,SanitizePermissions(top-level and nested)TestImportPermissions_CountsOnlySuccessesupdated for page permissions return value_test_3relation on_rule_result→ exported → imported to target org → fix stripped orphaned fields and retry succeededgo test ./...gofmtcleanMade with Cursor