Implement BranchRule class to facilitate ApprovalProjectRule and ExternalStatusCheck associations when protected branches are not linked
Related: #372844 (comment 1112495519)
Context
As part of the BranchRulesMVC we are creating additional GraphQL types to expose the branch rule information. The proposed UI designs show that branch rules can optionally have protected badge if the branchRule has a branchProtection record i.e. who can merge and push to the branch(es) matching the branchProtect.name. We also have branch rules which match All Branches and All Protected Branches.
The current implementation cannot achieve this due to the structure of the data. We expose project -> branchRules -> branchProtection, however, in this case the branch rule and branch protection objects are actually the same ProtectedBranch record in the database.
Current structure
erDiagram
project ||--|{ branchProtection : has_many
project {
string id FK "Global ID of the project"
}
branchProtection ||--|{ mergeAccessLevel : has_many
branchProtection ||--|{ pushAccessLevel : has_many
branchProtection ||--|{ pushAccessLevel : has_many
branchProtection ||--|{ unprotectAccessLevel : has_many-EE
branchProtection {
string name
boolean allowForcePush
boolean codeOwnerApprovalRequired
integer matchingBranchesCount
string createdAt
string updatedAt
}
mergeAccessLevel {
integer accessLevel
string accessLevelDescription
}
pushAccessLevel {
integer accessLevel
string accessLevelDescription
}
unprotectAccessLevel {
integer accessLevel
string accessLevelDescription
}
branchProtection ||--o{ approvalProjectRule : has_and_belongs_to_many-EE
branchProtection ||--o{ externalStatusCheck : has_and_belongs_to_many-EE
approvalProjectRule {
string id
string name
string type "Enum value upcased e.g. REPORT_APPROVER"
}
externalStatusCheck {
string url
}
Currently approvalProjectRule and externalStatusCheck can both be orphans too when the user selects "All branches" or "All protected branches"
Proposal
To solve this I'm proposing we introduce a new class called BranchRule which would behave as though it were an ActiveRecord model exposing the required details for branch_rule.project, branch_rule.protected_branch, branch_rule.approval_project_rules, and branch_rule.external_status_checks. This change would allow us to display All branches and All Protected Branches through the API as we could query for them separately to the project.protected_branches and then manually initialize BranchRule objects before resolving the query.
Proposed structure
erDiagram
project ||--|{ branchRule : has_many
project {
string id FK "Global ID of the project"
}
branchRule ||--o| branchProtection : self
branchRule {
string name
integer matchingBranchesCount
string createdAt
string updatedAt
}
branchProtection ||--|{ mergeAccessLevel : has_many
branchProtection ||--|{ pushAccessLevel : has_many
branchProtection ||--|{ pushAccessLevel : has_many
branchProtection ||--|{ unprotectAccessLevel : has_many-EE
branchProtection {
boolean allowForcePush
boolean codeOwnerApprovalRequired
}
mergeAccessLevel {
integer accessLevel
string accessLevelDescription
}
pushAccessLevel {
integer accessLevel
string accessLevelDescription
}
unprotectAccessLevel {
integer accessLevel
string accessLevelDescription
}
branchRule ||--o{ approvalProjectRule : has_and_belongs_to_many-EE
branchRule ||--o{ externalStatusCheck : has_and_belongs_to_many-EE
approvalProjectRule {
string id
string name
string type "Enum value upcased e.g. REPORT_APPROVER"
}
externalStatusCheck {
string url
}
I think we can implement this system without needing to migrate any data if we use the BranchRule class to present each ProtectedBranch. We can also bring in these records that use All branches and All protected branches by querying for them separately and then building individual BranchRule objects for the graphql logic to consume.