Skip to content

ref: migrate externalIssues to scrapsForm#112094

Merged
TkDodo merged 32 commits intomasterfrom
tkdodo/ref/de-1052-externalissues-to-backendformadapter
Apr 9, 2026
Merged

ref: migrate externalIssues to scrapsForm#112094
TkDodo merged 32 commits intomasterfrom
tkdodo/ref/de-1052-externalissues-to-backendformadapter

Conversation

@TkDodo
Copy link
Copy Markdown
Collaborator

@TkDodo TkDodo commented Apr 2, 2026

This PR migrates externalIssues - both the ExternalIssueFor and the TicketRuleModal over to the new form system with the BackendJsonFormAdapter.

Note that the previous adapter version we had only supported auto-save fields because of settings, so this PR adds “normal” forms:

  • BackendJsonAutoSaveForm for auto-save
  • BackendJsonSubmitForm` for regular forms

Rendering is “duplicated” on purpose, the fields, schemas and some utils are shared though. On type level, it’s quite hard to create an abstraction that can be shared between auto-save and submit, as auto-save creates one form per field.

@linear-code
Copy link
Copy Markdown

linear-code bot commented Apr 2, 2026

@github-actions github-actions bot added the Scope: Frontend Automatically applied to PRs that change frontend components label Apr 2, 2026
@github-actions github-actions bot added the Scope: Backend Automatically applied to PRs that change backend components label Apr 2, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 2, 2026

🚨 Warning: This pull request contains Frontend and Backend changes!

It's discouraged to make changes to Sentry's Frontend and Backend in a single pull request. The Frontend and Backend are not atomically deployed. If the changes are interdependent of each other, they must be separated into two pull requests and be made forward or backwards compatible, such that the Backend or Frontend can be safely deployed independently.

Have questions? Please ask in the #discuss-dev-infra channel.

@TkDodo TkDodo marked this pull request as ready for review April 2, 2026 17:03
@TkDodo TkDodo requested review from a team as code owners April 2, 2026 17:03
if (field.required) {
shape[field.name] = z
.any()
.refine(val => val !== null && val !== undefined && val !== '', {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

should we use .trim() here? AFAIK Django does that by default

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

thank you!. shouldn't we check if the message 'this field is required' is displayed?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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


const hasFormErrors = useMemo(() => {
return hasErrorInFields({fields: formFields});
return formFields.some(field => field.name === 'error' && field.type === 'blank');
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

does this need to be inside of a useMemo?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

nope. claude is so eager with those: a39ea1f

bodyText={
<Fragment>
<Header closeButton>
<h4>{title}</h4>
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

don't we want to have Heading here?

Suggested change
<h4>{title}</h4>
<Heading as="h4">{title}</Heading>

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

thanks, I missed one: dee0e81

link=""
ticketType=""
instance={{
integration: 1 as any,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
integration: 1 as any,
integration: '1',

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Comment on lines +110 to +112
// Both values should be visible as selected tags
expect(screen.getAllByText('bug').length).toBeGreaterThanOrEqual(1);
expect(screen.getAllByText('feature').length).toBeGreaterThanOrEqual(1);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think this is a bit vague. It's possible that similar words could appear elsewhere on the page, which might lead to false positives. Wouldn't it be better to verify that these values are actually being passed on submit instead?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

we didn’t really make a request, but now we do: 35ef6f1

Comment on lines +272 to +276
await userEvent.click(screen.getByRole('button', {name: 'Create Issue'}));

// Should NOT have made an API request
expect(submitRequest).not.toHaveBeenCalled();
expect(closeModal).not.toHaveBeenCalled();
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

shouldn't we check if a user gets a feedback? similar to the test below

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

the error toast in the test below is a feature of the mutation, which needs to happen in code we made here. the indicator being shown to the user is a feature of the form elements, which is something we check in the form field tests. but we can check if the fields are invalid: bcdc94b

// Simulate backend returning HTML error page or non-array response
MockApiClient.addMockResponse({
url: '/search',
body: '<html>502 Bad Gateway</html>' as any,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

shouldn't we use the status code here instead, so we can remove the any?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

we need a successful non-json to be returned for this test. but we can remove the assertion: 8ff81cf

@priscilawebdev
Copy link
Copy Markdown
Member

priscilawebdev commented Apr 8, 2026

I just tested the changes with the GitHub integration, and everything worked exactly as expected 🙌

Copy link
Copy Markdown
Member

@leeandher leeandher left a comment

Choose a reason for hiding this comment

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

looks good! wasn't super exhaustive but pulled it locally to review and it seemed totally fine. ran it locally and tested with jira for dynamic form fields, worked great! changes triggered new renders of the fields, previous values persisted, and the final submission matched the ui <3 thank you for working through this!

@TkDodo TkDodo merged commit fe82c02 into master Apr 9, 2026
57 checks passed
@TkDodo TkDodo deleted the tkdodo/ref/de-1052-externalissues-to-backendformadapter branch April 9, 2026 09:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Scope: Backend Automatically applied to PRs that change backend components Scope: Frontend Automatically applied to PRs that change frontend components

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants