Release custom fields mapping (#169)#175
Merged
Merged
Conversation
* feat: add migration for pagerduty_custom_fields table (#140) * feat: add CustomFieldsTab component to PagerDutyPage (#141) * feat: add CustomFieldsTab component to PagerDutyPage * refactor: remove unused MAX_MAPPINGS constant and related logic from CustomFieldsTab * feat: implement custom fields management for PagerDuty integration (#145) * feat: implement custom fields management for PagerDuty integration * feat: enhance error handling for custom fields in PagerDuty integration * feat: remove unused custom field retrieval methods and limit checks in CustomFieldsController * feat: update error handling for custom field creation and limit checks in CustomFieldsController and CustomFieldsTab * feat: add tests for custom fields creation and retrieval endpoints * fix: remove type duplication * fix import and mock api * fix unit tests * remove type duplication * fix unit tests * fix unit tests and error messages * implement insertCustomField method with error handling and retrieval * feat: enhance custom field methods to support account filtering and improve error handling * feat: add AddCustomFieldModal component and integrate it into CustomFieldsTab; update getCustomFields test * feat: update createCustomField method to return structured result; enhance error handling and update related tests * DEVECO-533: Edit custom fields (#148) * feat: add updateCustomField functionality to PagerDuty integration - Implemented updateCustomField method in the PagerDuty API to allow updating existing custom fields. - Enhanced error handling for name and entity path conflicts during updates. - Added corresponding tests for updateCustomField functionality in both frontend and backend. - Updated AddCustomFieldModal to support editing existing custom fields. - Integrated update functionality into CustomFieldsTab with a new action menu for editing custom fields. * feat: refactor custom fields controller and add helper functions - Introduced customFieldsHelpers module to encapsulate validation and error handling logic. - Refactored createCustomField and updateCustomField methods to utilize new helper functions for input validation and error management. - Enhanced error handling for entity path uniqueness and improved response messages. - Normalized description handling for custom fields during creation and updates. * feat: update CustomFieldsTab UI and functionality - Replaced Link component with a Button for adding new custom fields, enhancing user interaction. - Updated the section subtitle to better reflect the purpose of the tab. - Added a new Description column in the custom fields table for improved data visibility. - Adjusted the empty state message to accommodate the new column layout. - Swapped MoreVertIcon for MoreHorizIcon in the action menu for consistency. * feat: enhance error handling in PagerDutyClient for custom fields * feat: add CustomFieldModal component for managing custom fields - Replaced AddCustomFieldModal references in CustomFieldsTab with the new CustomFieldModal. * refactor: remove unused disabled menu items from CustomFieldsTab - Removed "Disable" and "Delete" menu items from the action menu in CustomFieldsTab to streamline the UI and eliminate unnecessary options. * fix: prevent race conditions in CustomFieldsTab update logic - Captured field ID early in the update process to avoid potential race conditions when updating custom fields. - Updated references to use the captured field ID instead of directly accessing the menuField ID. * fix: improve error handling in handlePagerDutyError function - Updated error handling logic to provide more specific messages for different HTTP error statuses (400, 401, 403, 404, 409, 429). - Enhanced clarity of error messages related to custom field limits and authentication issues in PagerDuty integration. * refactor: update CustomFieldModal and CustomFieldsTab components - Refactored CustomFieldModal to use new dialog components from @backstage/ui, improving UI consistency. * feat: add unique indexes for custom fields in migrations - Introduced a new migration to add unique indexes on the `pagerduty_custom_fields` table for `backstageEntityMappingPath` and `pagerdutySubdomain`, as well as `pagerdutyCustomFieldDisplayName` and `pagerdutySubdomain`. - Removed the `findCustomFieldByEntityPath` method from the `PagerDutyBackendStore` interface and its implementation to streamline the codebase. - Updated the `CustomFieldsController` to handle database errors more effectively during custom field creation and updates, improving overall error management. * refactor: consolidate error handling and validation in CustomFieldsController - Removed the customFieldsHelpers module and integrated its functionality directly into the CustomFieldsController. - Updated methods to use class methods for validation and error handling, improving code organization and readability. - Enhanced error management for database and PagerDuty interactions, providing clearer responses for various error scenarios. - Added private methods for input validation, sanitization, and description normalization to streamline custom field processing. * fix: prevent unnecessary form resets in CustomFieldModal * refactor: improve state management and modal handling in CustomFieldsTab - Renamed state variable for clarity from `menuField` to `selectedCustomField`. - Consolidated modal handling logic into a single function to streamline opening and closing of modals. - Updated the CustomFieldModal to handle both editing and adding custom fields more efficiently. - Enhanced error handling and state management during custom field updates. * feat: implement uniqueness validation for custom fields in CustomFieldsController - Added a new method to validate uniqueness constraints for display names and entity paths before updating PagerDuty, preventing synchronization issues. - Enhanced error handling to return appropriate conflict messages when duplicates are detected. - Introduced tests to ensure validation logic works correctly and prevents invalid updates. * feat: enhance custom field management with delete and update functionality - Added methods to delete custom fields and update their PagerDuty IDs in the PagerDutyBackendDatabase. - Updated CustomFieldsController to handle the creation and updating of custom fields with rollback mechanisms for error handling. - Implemented tests to ensure proper rollback behavior when PagerDuty operations fail, maintaining data consistency between Backstage and PagerDuty. * fix: Rebase after service mappings release * fix: Remove inline styles * feat: Push custom fields to PagerDuty during catalog processing (#162) * feat: Push custom fields to PD during catalog processing * refactor: Extract custom fields push into dedicated processor Split custom field value sync out of PagerDutyEntityProcessor into a new PagerDutyCustomFieldsProcessor, return the PagerDuty response body from the sync endpoint, and consolidate DB row mapping via a helper. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat: sync logs tab + record sync failures/skips (#168) * feat: add sync logs tab to display custom field sync history Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat: record custom field sync failures and skips in sync logs Catalog processor now writes a sync log entry whenever a custom field push to PagerDuty fails or a field is skipped because its entity path does not resolve. Writes are fire-and-forget so they don't slow down catalog processing. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor: group custom fields components and extract shared error mapper Move CustomFields-related components into a dedicated subfolder and share the toFieldErrors helper through a new customFieldErrors util to remove the duplicated implementation. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test: add smoke tests and polish sync logs tab Add frontend smoke tests for ButtonTabs, CustomFieldsTabPanel, SyncLogsFilters, SyncLogsTab, and the CustomFields wrapper. Switch SyncLogsTab to receive filters through useTable's filter prop so pagination resets on filter changes, display full date+time in the Timestamp column, and widen that column so values fit on one line. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix: classify INVALID_PATH sync logs as warning instead of error Path-resolution failures are expected when entities lack an optional field, so they shouldn't be surfaced as errors in the sync logs tab. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat: add visual empty state for custom fields and sync logs tables Replaces plain text empty states with a centered layout. The custom fields empty state shows two disabled input placeholders with an arrow icon to hint at the mapping concept; the sync logs empty state is centered using a Flex wrapper. Styles use makeStyles throughout. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: remove unused AutoMatchEntityMappingsResponse import Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat: enable/disable custom fields (#170) Add a toggle to enable or disable a custom field from the Custom Fields tab. Disabled fields are filtered out of the enabled view and do not count against PagerDuty's custom field hard limit. Backend: - Add setCustomFieldEnabled to the DB store - Add PATCH /custom-fields/:id/enabled route + controller using the DB-first -> PagerDuty -> rollback-on-failure pattern - Add BackstageCustomFieldToggleEnabledRequest to common types - Add router tests covering disable, validation, 404, and rollback Frontend: - Add setCustomFieldEnabled to PagerDutyApi/PagerDutyClient + mock - Add Enable/Disable menu action with error snackbar and disabled-row styling in CustomFieldsTabPanel Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat: start/stop custom field data sync (DEVECO-537) (#171) * feat: start/stop custom field data sync (DEVECO-537) Wire up the org-wide data sync toggle so customers must opt in before any custom field values are pushed to PagerDuty during catalog processing. - Persist the toggle in the settings table under settings::data-sync (value enabled/disabled), validated separately by id in the router so the dependency-strategy setting is untouched. - Gate PagerDutyCustomFieldsProcessor on isDataSyncEnabled(); default to disabled when the setting was never saved (opt-in). - Wire the frontend Switch to React Query (useQuery read + useMutation write with optimistic update/rollback); unsaved is treated as disabled with no write on load. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: prevent SSRF via origin allow-list and path segment encoding Add an allow-list origin guard in fetchWithRetries that validates every outgoing request URL against configured PagerDuty API origins, rejecting any URL whose host isn't in the server-controlled set. Also encodeURIComponent each user-controlled path segment (serviceId, fieldId) and rebuild the getSerivcesByIdsAndAccount query string with URLSearchParams to eliminate raw interpolation sinks, satisfying CodeQL js/request-forgery (alert #8). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat: delete custom field (DEVECO-534) (#172) Implements the delete custom field feature end-to-end: DELETE endpoint on the backend, PagerDuty API call to remove the global custom field, and a self-contained DeleteCustomFieldDialog component in the frontend that owns its own state and logic. Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * feat: automated cleanup for custom field sync logs (#173) * feat: automated cleanup for custom field sync logs Adds a scheduled background task that keeps the sync logs table bounded: rows older than 30 days are deleted and a global hard cap of 500k rows trims the oldest entries beyond it. Deletes run in 10k batches via id-subquery (portable across SQLite and Postgres) so concurrent sync inserts are never blocked, and the task runs with a global scheduler lock so only one replica executes it. All thresholds are configurable under pagerDuty.customFieldsSyncLogs. Also adds a Refresh button next to Export CSV on the Sync Logs tab, a bare timestamp index needed by the TTL deletes, and fixes two pre-existing lint errors (duplicate describe title, explicit any). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix: address PR review feedback for sync logs cleanup - Fix getSyncLogs total returning NaN (count property access lost when removing `as any`); type the count query via a generic instead. - Extract repeated batch sleep delay into BATCH_SLEEP_MS constant. - Make the batch-budget-exhausted warning name the config knobs to tune. - Extract SYNC_LOGS_CLEANUP_TASK_ID / timeout / initial-delay constants and share the task id between plugin.ts and plugin.test.ts. - Add coverage for the age-empties-table / cap-no-op path. - Fix stale olderThanMinutes -> olderThanDays in DB test options. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> * refactor: adjust for Backstage 1.51.2 and port PagerDutyPage to @backstage/ui (#174) * refactor: adjust for Backstage 1.51.2 upgrade Bump Backstage version 1.47.3 -> 1.51.2 and adapt to breaking changes: - Move catalogProcessingExtensionPoint import out of /alpha - Drop removed variant="gridItem" prop from Entity* cards - Switch @backstage/ui to managed backstage:^ version - Add explicit blueprint defineParams type annotations in dev - Consolidate PagerDuty nav item into PageBlueprint title/icon params Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor: restructure PagerDuty page into Configuration and ServiceMapping pages Split the monolithic PagerDutyPage into three SubPageBlueprint extensions (Service Mapping, Custom Fields, Configuration) so each tab is lazily loaded and independently navigable via the Backstage frontend plugin system. Also switches `tableProps.loading` to `tableProps.isPending` in MappingsTableContent and SyncLogsTab to align with the @backstage/ui@0.15.0 API (loading is deprecated). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * chore: bump ui-refactors prerelease versions to .8 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor: reposition alerts and tidy styles on PagerDuty pages Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore: Fix edited package jsons * refactor: add PagerDuty service caching and batch insert chunking Wire a 20-minute CacheService layer into getAllServices and getServiceById so repeated calls within a request (auto-match, confirm, bulk mapping) hit the cache rather than the PagerDuty API. Also batches getSerivcesByIdsAndAccount into 50-id chunks to stay within API gateway URL-length limits, and chunks bulkInsertEntityMappings into 200-row transactions to avoid SQLite's 500-term compound SELECT limit. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: surface freshly-created mappings before processor writes annotations back The entity list read path computed status only when a PagerDuty service could be resolved from the entity's catalog annotations. A just-created mapping has a DB row but no annotation yet (the entity processor writes it back on the next catalog processing cycle), so the entity showed NotMapped despite a matching mapping row. When a stored mapping is matched by entityRef but the service can't be resolved from the (still-empty) annotations, fall back to the mapping's serviceId so the mapping appears immediately as OutOfSync and progresses to InSync once the annotation lands. Resolution reuses the already-fetched current-page services, so no extra API or DB calls are made. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: drop malformed service ids before batch PagerDuty lookup The entity-mapping page returned 400 "Failed to get service. Caller provided invalid arguments." whenever the status filter was set. Status is computed at request time, so that filter is the only path that calls getServicesByIds, which packed every collected service id into a single GET /services?id[]=... request. PagerDuty rejects the whole batch with a 400 if any one id is malformed (a pasted service URL, trailing whitespace, a typo), so one bad annotation failed the entire page. - Add isValidServiceId and sanitize ids in getSerivcesByIdsAndAccount before batching (removes the 400 trigger). - Make getServicesByIdsBatch treat a list-endpoint 400 as no matches rather than throwing; single-service lookups still throw on 400. - Fix getServicesByIds to accumulate results across accounts instead of overwriting (multi-account bug). - Thread a logger into the mappings controller and warn about malformed annotation ids so operators know which entity to fix. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: evict service cache after integration creation to prevent duplicates Pass the cache through to createServiceIntegration and delete the per-service and all-services cache entries immediately after a new integration is created, so the next read re-fetches fresh data instead of returning a stale integrations array that could cause duplicate Backstage integrations within the cache TTL. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: restore Custom Fields and Configuration tabs on legacy PagerDutyPage The @backstage/ui port reduced the legacy PagerDutyPage routable extension to rendering only ServiceMappingPage, silently dropping the Custom Fields and Configuration tabs for any consumer still on the old Frontend System. Restore all three tabs via TabbedLayout at their original paths (/service-mapping, /custom-fields, /settings), reusing the new @backstage/ui page components so the legacy path stays consistent with the alpha SubPageBlueprint path. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat: link mapping table entity names to their catalog page The Name column on the service-mapping page rendered the entity name as plain text. Add a NameCell that links to the entity's catalog page, mirroring ServiceCell's underlined link styling. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat: client-side nav for mapping names; handle 404 on custom-field delete NameCell now renders the entity name via core-components' router-aware Link (to=) instead of CellText's href, which emitted a plain <a> and forced a full page reload when navigating to the catalog entity. customFieldsController treats a 404 from PagerDuty on delete as already-deleted and proceeds to remove the Backstage record so the two sides don't drift, with tests covering the success, missing-record, PD-404, and PD-500 paths. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: clarify mapping toast wording about async catalog re-processing Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat: add "info" sync-log type for successful custom-field syncs Records a per-field SYNC_SUCCESS log when the entity processor pushes custom field values to PagerDuty, giving operators positive sync signal instead of only failures. Severity is computed from errorCode (not stored), so the binary error/warning derivation is reworked to three-way: warning now excludes both error and info codes in the backend query and the frontend badge. Adds a blue "Info" badge and a matching option in the Severity filter. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: address PR review findings on service caching and config save Resolves four findings from the PR #174 review: - getServicesByIdsBatch: set `limit` to the batch size on the `id[]` query so a full 50-id batch isn't truncated to PagerDuty's default 25-item page (services 26+ were silently dropped, leaving entities NotMapped). - getAllServices: treat an empty cached list as a valid hit (`cached != null`) so zero-service accounts use the cache instead of re-fetching the full paginated list on every call. - router POST /services/:serviceId/integration/:vendorId: pass `cache` to createServiceIntegration so the per-service and all-services cache entries are evicted after a standalone integration is created, preventing a stale read within the TTL from creating a duplicate. - ConfigurationPage: await storeSettings and, on failure, roll back the optimistic radio selection and surface an error alert instead of silently dropping the rejected promise. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore: Generate changese * fix: Linter issues --------- Co-authored-by: Renan Oliveira <97949554+renannoliveira@users.noreply.github.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
renannoliveira
approved these changes
Jul 2, 2026
gaberduty
approved these changes
Jul 2, 2026
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.
feat: add migration for pagerduty_custom_fields table (feat: add migration for pagerduty_custom_fields table #140)
feat: add CustomFieldsTab component to PagerDutyPage (feat: add CustomFieldsTab component to PagerDutyPage #141)
feat: add CustomFieldsTab component to PagerDutyPage
refactor: remove unused MAX_MAPPINGS constant and related logic from CustomFieldsTab
feat: implement custom fields management for PagerDuty integration (feat: implement custom fields management for PagerDuty integration #145)
feat: implement custom fields management for PagerDuty integration
feat: enhance error handling for custom fields in PagerDuty integration
feat: remove unused custom field retrieval methods and limit checks in CustomFieldsController
feat: update error handling for custom field creation and limit checks in CustomFieldsController and CustomFieldsTab
feat: add tests for custom fields creation and retrieval endpoints
fix: remove type duplication
fix import and mock api
fix unit tests
remove type duplication
fix unit tests
fix unit tests and error messages
implement insertCustomField method with error handling and retrieval
feat: enhance custom field methods to support account filtering and improve error handling
feat: add AddCustomFieldModal component and integrate it into CustomFieldsTab; update getCustomFields test
feat: update createCustomField method to return structured result; enhance error handling and update related tests
DEVECO-533: Edit custom fields (DEVECO-533: Edit custom fields #148)
feat: add updateCustomField functionality to PagerDuty integration
feat: enhance error handling in PagerDutyClient for custom fields
feat: add CustomFieldModal component for managing custom fields
pagerduty_custom_fieldstable forbackstageEntityMappingPathandpagerdutySubdomain, as well aspagerdutyCustomFieldDisplayNameandpagerdutySubdomain.findCustomFieldByEntityPathmethod from thePagerDutyBackendStoreinterface and its implementation to streamline the codebase.CustomFieldsControllerto handle database errors more effectively during custom field creation and updates, improving overall error management.fix: prevent unnecessary form resets in CustomFieldModal
refactor: improve state management and modal handling in CustomFieldsTab
menuFieldtoselectedCustomField.fix: Rebase after service mappings release
fix: Remove inline styles
feat: Push custom fields to PagerDuty during catalog processing (feat: Push custom fields to PagerDuty during catalog processing #162)
feat: Push custom fields to PD during catalog processing
refactor: Extract custom fields push into dedicated processor
Split custom field value sync out of PagerDutyEntityProcessor into a new PagerDutyCustomFieldsProcessor, return the PagerDuty response body from the sync endpoint, and consolidate DB row mapping via a helper.
feat: sync logs tab + record sync failures/skips (feat: sync logs tab + record sync failures/skips #168)
feat: add sync logs tab to display custom field sync history
feat: record custom field sync failures and skips in sync logs
Catalog processor now writes a sync log entry whenever a custom field push to PagerDuty fails or a field is skipped because its entity path does not resolve. Writes are fire-and-forget so they don't slow down catalog processing.
Move CustomFields-related components into a dedicated subfolder and share the toFieldErrors helper through a new customFieldErrors util to remove the duplicated implementation.
Add frontend smoke tests for ButtonTabs, CustomFieldsTabPanel, SyncLogsFilters, SyncLogsTab, and the CustomFields wrapper. Switch SyncLogsTab to receive filters through useTable's filter prop so pagination resets on filter changes, display full date+time in the Timestamp column, and widen that column so values fit on one line.
Path-resolution failures are expected when entities lack an optional field, so they shouldn't be surfaced as errors in the sync logs tab.
Replaces plain text empty states with a centered layout. The custom fields empty state shows two disabled input placeholders with an arrow icon to hint at the mapping concept; the sync logs empty state is centered using a Flex wrapper. Styles use makeStyles throughout.
fix: remove unused AutoMatchEntityMappingsResponse import
feat: enable/disable custom fields (feat: enable/disable custom fields #170)
Add a toggle to enable or disable a custom field from the Custom Fields tab. Disabled fields are filtered out of the enabled view and do not count against PagerDuty's custom field hard limit.
Backend:
Frontend:
feat: start/stop custom field data sync (DEVECO-537) (feat: start/stop custom field data sync (DEVECO-537) #171)
feat: start/stop custom field data sync (DEVECO-537)
Wire up the org-wide data sync toggle so customers must opt in before any custom field values are pushed to PagerDuty during catalog processing.
Add an allow-list origin guard in fetchWithRetries that validates every outgoing request URL against configured PagerDuty API origins, rejecting any URL whose host isn't in the server-controlled set. Also encodeURIComponent each user-controlled path segment (serviceId, fieldId) and rebuild the getSerivcesByIdsAndAccount query string with URLSearchParams to eliminate raw interpolation sinks, satisfying CodeQL js/request-forgery (alert #8).
Implements the delete custom field feature end-to-end: DELETE endpoint on the backend, PagerDuty API call to remove the global custom field, and a self-contained DeleteCustomFieldDialog component in the frontend that owns its own state and logic.
feat: automated cleanup for custom field sync logs (feat: automated cleanup for custom field sync logs #173)
feat: automated cleanup for custom field sync logs
Adds a scheduled background task that keeps the sync logs table bounded: rows older than 30 days are deleted and a global hard cap of 500k rows trims the oldest entries beyond it. Deletes run in 10k batches via id-subquery (portable across SQLite and Postgres) so concurrent sync inserts are never blocked, and the task runs with a global scheduler lock so only one replica executes it. All thresholds are configurable under pagerDuty.customFieldsSyncLogs.
Also adds a Refresh button next to Export CSV on the Sync Logs tab, a bare timestamp index needed by the TTL deletes, and fixes two pre-existing lint errors (duplicate describe title, explicit any).
as any); type the count query via a generic instead.refactor: adjust for Backstage 1.51.2 and port PagerDutyPage to @backstage/ui (refactor: adjust for Backstage 1.51.2 and port PagerDutyPage to @backstage/ui #174)
refactor: adjust for Backstage 1.51.2 upgrade
Bump Backstage version 1.47.3 -> 1.51.2 and adapt to breaking changes:
Split the monolithic PagerDutyPage into three SubPageBlueprint extensions (Service Mapping, Custom Fields, Configuration) so each tab is lazily loaded and independently navigable via the Backstage frontend plugin system.
Also switches
tableProps.loadingtotableProps.isPendingin MappingsTableContent and SyncLogsTab to align with the @backstage/ui@0.15.0 API (loading is deprecated).chore: bump ui-refactors prerelease versions to .8
refactor: reposition alerts and tidy styles on PagerDuty pages
chore: Fix edited package jsons
refactor: add PagerDuty service caching and batch insert chunking
Wire a 20-minute CacheService layer into getAllServices and getServiceById so repeated calls within a request (auto-match, confirm, bulk mapping) hit the cache rather than the PagerDuty API. Also batches getSerivcesByIdsAndAccount into 50-id chunks to stay within API gateway URL-length limits, and chunks bulkInsertEntityMappings into 200-row transactions to avoid SQLite's 500-term compound SELECT limit.
The entity list read path computed status only when a PagerDuty service could be resolved from the entity's catalog annotations. A just-created mapping has a DB row but no annotation yet (the entity processor writes it back on the next catalog processing cycle), so the entity showed NotMapped despite a matching mapping row.
When a stored mapping is matched by entityRef but the service can't be resolved from the (still-empty) annotations, fall back to the mapping's serviceId so the mapping appears immediately as OutOfSync and progresses to InSync once the annotation lands. Resolution reuses the already-fetched current-page services, so no extra API or DB calls are made.
The entity-mapping page returned 400 "Failed to get service. Caller provided invalid arguments." whenever the status filter was set. Status is computed at request time, so that filter is the only path that calls getServicesByIds, which packed every collected service id into a single GET /services?id[]=... request. PagerDuty rejects the whole batch with a 400 if any one id is malformed (a pasted service URL, trailing whitespace, a typo), so one bad annotation failed the entire page.
Pass the cache through to createServiceIntegration and delete the per-service and all-services cache entries immediately after a new integration is created, so the next read re-fetches fresh data instead of returning a stale integrations array that could cause duplicate Backstage integrations within the cache TTL.
The @backstage/ui port reduced the legacy PagerDutyPage routable extension to rendering only ServiceMappingPage, silently dropping the Custom Fields and Configuration tabs for any consumer still on the old Frontend System. Restore all three tabs via TabbedLayout at their original paths (/service-mapping, /custom-fields, /settings), reusing the new @backstage/ui page components so the legacy path stays consistent with the alpha SubPageBlueprint path.
The Name column on the service-mapping page rendered the entity name as plain text. Add a NameCell that links to the entity's catalog page, mirroring ServiceCell's underlined link styling.
NameCell now renders the entity name via core-components' router-aware Link (to=) instead of CellText's href, which emitted a plain and forced a full page reload when navigating to the catalog entity.
customFieldsController treats a 404 from PagerDuty on delete as already-deleted and proceeds to remove the Backstage record so the two sides don't drift, with tests covering the success, missing-record, PD-404, and PD-500 paths.
fix: clarify mapping toast wording about async catalog re-processing
feat: add "info" sync-log type for successful custom-field syncs
Records a per-field SYNC_SUCCESS log when the entity processor pushes custom field values to PagerDuty, giving operators positive sync signal instead of only failures.
Severity is computed from errorCode (not stored), so the binary error/warning derivation is reworked to three-way: warning now excludes both error and info codes in the backend query and the frontend badge. Adds a blue "Info" badge and a matching option in the Severity filter.
Resolves four findings from the PR #174 review:
limitto the batch size on theid[]query so a full 50-id batch isn't truncated to PagerDuty's default 25-item page (services 26+ were silently dropped, leaving entities NotMapped).cached != null) so zero-service accounts use the cache instead of re-fetching the full paginated list on every call.cacheto createServiceIntegration so the per-service and all-services cache entries are evicted after a standalone integration is created, preventing a stale read within the TTL from creating a duplicate.chore: Generate changese
fix: Linter issues
Description
Please include a summary of the change or which issue is fixed.
Issue number: (e.g. #123)
Affected plugin
Type of change
Checklist
If this is a breaking change 👇
Acknowledgement
By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.
Disclaimer: We value your time and bandwidth. As such, any pull requests created on non-triaged issues might not be successful.