Skip to content

Conversation

@sandornagy517
Copy link
Contributor

Description

To improve loading performance of the mappings page we swap the direction of the data loaded. Until now were were mapping Backstage entities to PagerDuty services, now we swap this and will do the opposite. In this PR I've updated the mappings table to the new Backstage UI table and updated the way we load the data for the component.

image

Affected plugin

  • backstage-plugin
  • backstage-plugin-backend
  • backstage-plugin-scaffolder-actions
  • backstage-plugin-entity-processor

Type of change

  • New feature (non-breaking change which adds functionality)
  • Fix (non-breaking change which fixes an issue)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)

Checklist

  • I have performed a self-review of this change
  • Changes have been tested
  • Changes have been tested in dark theme
  • Changes are documented
  • Changes generate no new warnings
  • PR title follows conventional commit semantics

If this is a breaking change 👇

  • I have documented the migration process
  • I have implemented necessary warnings (if it can live side by side)

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.

@sandornagy517 sandornagy517 requested a review from a team as a code owner December 10, 2025 13:48
@sandornagy517 sandornagy517 changed the base branch from main to next December 10, 2025 13:48
Copy link
Contributor

@jhfgloria jhfgloria left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left some comments. The PR is quite big (and visual), so probably I would need to test it locally to make more sense of it.

await Promise.all(
Object.entries(EndpointConfig).map(async ([account, _]) => {
services = await Promise.all(
ids.map(async id => await getServiceById(id, account)),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mapping => mapping.serviceId === service.id,
e =>
e.integrationKey ===
ent?.metadata.annotations?.['pagerduty.com/integration-key'],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are there any guarantees that at this point the integration-key was set? 🤔 Why not searching by integration-key OR service-id.

Comment on lines 232 to 243
result.mappings.push({
entityRef: '',
entityName: '',
integrationKey: entityMapping?.integrationKey,
serviceId: entityMapping?.serviceId ?? '',
status: 'NotMapped',
serviceName: '',
team: '',
escalationPolicy: '',
serviceUrl: '',
account: '',
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to push this empty state?

});

// GET /mapping/entity
router.get('/mapping/entity', async (_, response) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please don't remove any methods. Instead mark them as deprecated. There is a chance that people only update the frontend or just the backend and then there will be missing methods.

search: debouncedSearchQuery,
searchFields: ['metadata.name', 'spec.owner'],
}),
staleTime: 5 * 60 * 1000,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why 5 minutes? 🤔 Is it worth caching at all?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm, not sure why did I include this back then, just removed it!

const [pageSize, setPageSize] = useState(10);
const [searchQuery, setSearchQuery] = useState('');
const [debouncedSearchQuery, setDebouncedSearchQuery] = useState('');
useDebounce(() => setDebouncedSearchQuery(searchQuery), 500, [searchQuery]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use debounce used like this is so awkward... Shouldn't it return something? What's the point of the hook at all? 🤔

}
}

export async function getServicesByIdsServiceDirectory(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we need to expose the calling service from PagerDuty. getServicesByIdsAndAccount would be more readable and not expose the fact that the call is made to ServicesDirectory.

};

const formattedEntity = {
name: entity?.metadata?.name,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why would the entity be null? Doesn't it come from the catalog API? Also, if it is null would be better practice to early return here, instead of dragging the ? around forever?

Comment on lines 801 to 806
const foundServices = pagerDutyServices.filter(
s => s.id === serviceId,
);
if (foundServices.length > 0) {
service = foundServices[0];
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const foundServices = pagerDutyServices.filter(
s => s.id === serviceId,
);
if (foundServices.length > 0) {
service = foundServices[0];
}
service = pagerDutyServices.find(s => s.id === serviceId);

account,
);
} catch (e) {
// Service might not exist, just continue
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this type of error is only valid if the error is a 404. Otherwise you will not know if there is a map or not and could decide to map something else.

Copy link
Contributor

@jhfgloria jhfgloria left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! Let's just make some tests in the ServiceMappingComponent (I think it is the topmost component) and test the router.ts new endpoint (which I also think is the topmost backend piece of software). There is no tests in the PR and I think there should be.

@sandornagy517 sandornagy517 force-pushed the feat/swap_mapping_direction branch from c38fcb1 to ed2ba57 Compare January 23, 2026 10:54
@sandornagy517 sandornagy517 force-pushed the feat/swap_mapping_direction branch from ed2ba57 to 582e3ba Compare January 23, 2026 11:01
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