This PR ensures that when a user reassigns a task that already has a scratch org, one of two things happens:
The org is also transferred to the target user.
The reassignment is not completed and the user is shown an error message.
Metecho no longer deletes scratch orgs during transfers.
In order for a person to be a valid transfer target, all of these things must be true.
The GitHubUser must have push permission to the project (for dev only)
The GitHubUser must have a corresponding User, who can own the ScratchOrg object.
The org must exist.
The source and target user must have the same Dev Hub username (this really should just be same Dev Hub, but it's a proxy check as this data is much easier to access)
There are three areas in the code where org transfer is implicated. Note that these areas also implicate assignment, not just reassignment. This means that each area of code involved in the process has a lot of different states to check before it takes action.
The UI, when a list of potential transferees is shown.
This is a AssignTaskRoleModal component.
The relevant data props are cascaded down from detail.tsx. The githubUsers prop comes from the project, which is just a Project API GET. (See ProjectSerializer - this returns all GitHubCollaborators with access to the project)
epicUsers is a filtered view of the same data based on which users are associated with the Epic (also an API call).
These props are filtered in the AssignTaskRoleModal component. If the org type is not ORG_TYPES.QA, the value of permissions.push must be true. No filtering is done on permissions for QA orgs.
No filtering is done on whether a User is present, the org exists, or the Dev Hub username is the same.
Those checks would be more challenging to implement and slow down the user experience.
Instead, once the user selects a target, we call can_reassign() via the API and then present the outcome.
The API, when a can_reassign call is made to validate a transfer;
The API, when a TaskAssignee record is created or modify to effect a transfer. This is done inside the TaskAssigneeSerializer.
The previous code did not work correctly (orgs were always deleted!), repeated itself in numerous places, was very difficult to follow, and was not tested effectively for the org transfer case.
In this PR, the API checks are factored into a new module, metecho.api.reassignment, using full typing and breaking down checks into reusable elements. The code in can_reassign and the TaskAssigneeSerializer is simplified. The existing tests for TaskAssigneeSerializer and the can_reassign() API call are completely rewritten, and cover the new module effectively. The front end now detects issues via can_reassign() and presents them to the user in a toast message, rather than deleting an existing scratch org.
The one remaining case in which an org is deleted is if a transfer is attempted but fails.
To test hands-on:
Open local in Dev Container
Run yarn serve
Access the CCI-Food-Bank project
Create a task and scratch org
Open a terminal and run python manage.py shell.
Create a secondary user that can receive a scratch org transfer:
from metecho.api.models import *
gh = GitHubUser.objects.get(login='davidjray') # Assuming you are not David Ray
u = User.objects.create(username = 'Test Recipient', email='d.reed@salesforce.com')
SocialAccount.objects.create(user=u, provider='github', uid=gh.pk)
Transfer the task to the created user. You should see the scratch org transfer to them as well.
Attempt to transfer the task to a different user, who does not have an account. You should see a Toast error message with details of the issue.
This PR ensures that when a user reassigns a task that already has a scratch org, one of two things happens:
Metecho no longer deletes scratch orgs during transfers.
In order for a person to be a valid transfer target, all of these things must be true.
GitHubUser
must havepush
permission to the project (for dev only)GitHubUser
must have a correspondingUser
, who can own theScratchOrg
object.There are three areas in the code where org transfer is implicated. Note that these areas also implicate assignment, not just reassignment. This means that each area of code involved in the process has a lot of different states to check before it takes action.
The UI, when a list of potential transferees is shown.
AssignTaskRoleModal
component.detail.tsx
. The githubUsers prop comes from theproject
, which is just a Project API GET. (See ProjectSerializer - this returns all GitHubCollaborators with access to the project)AssignTaskRoleModal
component. If the org type is notORG_TYPES.QA
, the value ofpermissions.push
must be true. No filtering is done on permissions for QA orgs.can_reassign()
via the API and then present the outcome.The API, when a
can_reassign
call is made to validate a transfer;The API, when a
TaskAssignee
record is created or modify to effect a transfer. This is done inside theTaskAssigneeSerializer
.The previous code did not work correctly (orgs were always deleted!), repeated itself in numerous places, was very difficult to follow, and was not tested effectively for the org transfer case.
In this PR, the API checks are factored into a new module,
metecho.api.reassignment
, using full typing and breaking down checks into reusable elements. The code incan_reassign
and theTaskAssigneeSerializer
is simplified. The existing tests forTaskAssigneeSerializer
and thecan_reassign()
API call are completely rewritten, and cover the new module effectively. The front end now detects issues viacan_reassign()
and presents them to the user in a toast message, rather than deleting an existing scratch org.The one remaining case in which an org is deleted is if a transfer is attempted but fails.
To test hands-on:
Open local in Dev Container
Run
yarn serve
Access the CCI-Food-Bank project
Create a task and scratch org
Open a terminal and run
python manage.py shell
.Create a secondary user that can receive a scratch org transfer:
from metecho.api.models import * gh = GitHubUser.objects.get(login='davidjray') # Assuming you are not David Ray u = User.objects.create(username = 'Test Recipient', email='d.reed@salesforce.com') SocialAccount.objects.create(user=u, provider='github', uid=gh.pk)
Transfer the task to the created user. You should see the scratch org transfer to them as well.
Attempt to transfer the task to a different user, who does not have an account. You should see a Toast error message with details of the issue.