Skip to content
Clone
98e06cb
183 lines 7.2 KB
/**
* Protection-rule domain: branch-protection policies (CRUD) with their push /
* merge / approval whitelists, and the tag-protection policies with their own
* whitelists. Both policy kinds are plain CRUD on the server.
*/
import { toMemberUserRow, unwrapApi } from '@app/lib'
import type {
EnvelopeBranchPolicyDto,
EnvelopeTagPolicyDto,
EnvelopeVecBranchWhitelistDto,
EnvelopeVecTagWhitelistDto,
WhitelistKind,
} from '@app/services/api'
import Apis, { type MemberSummaryPage } from '@app/services/api'
export type { WhitelistKind }
export type BranchPolicy = NonNullable<EnvelopeBranchPolicyDto['data']>
export type BranchWhitelistRow = NonNullable<NonNullable<EnvelopeVecBranchWhitelistDto['data']>[number]>
export type TagWhitelistRow = NonNullable<NonNullable<EnvelopeVecTagWhitelistDto['data']>[number]>
export type TagPolicy = NonNullable<EnvelopeTagPolicyDto['data']>
export const listPolicies = (space: string, repo: string) =>
unwrapApi<BranchPolicy[] | null>(
Apis.protection.list_policies({ pathParams: { space_name: space, repo_name: repo } }),
).then((rows) => rows ?? [])
/** Fresh copy of one policy (edit dialog opens from the list row). */
export const getPolicy = (space: string, repo: string, policyId: string) =>
unwrapApi<BranchPolicy>(
Apis.protection.get_policy({ pathParams: { space_name: space, repo_name: repo, policy_id: policyId } }),
)
export const createPolicy = (space: string, repo: string, cmd: CreatePolicyInput) =>
unwrapApi<BranchPolicy>(
Apis.protection.create_policy({
pathParams: { space_name: space, repo_name: repo },
data: {
branch_pattern: cmd.branch_pattern,
required_approvals: cmd.required_approvals,
enable_push: cmd.enable_push,
enable_force_push: cmd.enable_force_push,
enable_push_whitelist: cmd.enable_push_whitelist,
enable_merge_whitelist: cmd.enable_merge_whitelist,
enable_approvals_whitelist: cmd.enable_approvals_whitelist,
block_on_rejected_reviews: cmd.block_on_rejected_reviews,
block_on_change_requested_reviews: cmd.block_on_change_requested_reviews,
block_on_outdated_branch: cmd.block_on_outdated_branch,
dismiss_stale_approvals: cmd.dismiss_stale_approvals,
require_signed_commits: cmd.require_signed_commits,
protected_file_patterns: cmd.protected_file_patterns || null,
unprotected_file_patterns: cmd.unprotected_file_patterns || null,
},
}),
)
export const updatePolicy = (space: string, repo: string, policyId: string, cmd: Partial<CreatePolicyInput>) =>
unwrapApi<BranchPolicy>(
Apis.protection.update_policy({
pathParams: { space_name: space, repo_name: repo, policy_id: policyId },
// Single `Option<String>`s on the server: absent means "unchanged", and an
// empty string is what actually clears a pattern list (null would no-op).
data: cmd,
}),
)
export const deletePolicy = (space: string, repo: string, policyId: string) =>
unwrapApi<null>(
Apis.protection.delete_policy({ pathParams: { space_name: space, repo_name: repo, policy_id: policyId } }),
)
export const listBranchWhitelists = (space: string, repo: string, policyId: string) =>
unwrapApi<BranchWhitelistRow[] | null>(
Apis.branch_protection.list_branch_whitelists({
pathParams: { space_name: space, repo_name: repo, policy_id: policyId },
}),
).then((rows) => rows ?? [])
export const addBranchWhitelist = (
space: string,
repo: string,
policyId: string,
cmd: { kind: WhitelistKind; user_id?: number | null; group_id?: string | null },
) =>
unwrapApi<BranchWhitelistRow>(
Apis.branch_protection.add_branch_whitelist({
pathParams: { space_name: space, repo_name: repo, policy_id: policyId },
data: { kind: cmd.kind, user_id: cmd.user_id ?? null, group_id: cmd.group_id ?? null },
}),
)
export const removeBranchWhitelist = (space: string, repo: string, policyId: string, whitelistId: string) =>
unwrapApi<null>(
Apis.branch_protection.remove_branch_whitelist({
pathParams: { space_name: space, repo_name: repo, policy_id: policyId, whitelist_id: whitelistId },
}),
)
export const listTagPolicies = (space: string, repo: string) =>
unwrapApi<TagPolicy[] | null>(
Apis.tag_protection.list_tag_policies({ pathParams: { space_name: space, repo_name: repo } }),
).then((rows) => rows ?? [])
export const createTagPolicy = (space: string, repo: string, namePattern: string) =>
unwrapApi<TagPolicy>(
Apis.tag_protection.create_tag_policy({
pathParams: { space_name: space, repo_name: repo },
data: { name_pattern: namePattern },
}),
)
export const updateTagPolicy = (space: string, repo: string, policyId: string, namePattern: string) =>
unwrapApi<TagPolicy>(
Apis.tag_protection.update_tag_policy({
pathParams: { space_name: space, repo_name: repo, policy_id: policyId },
data: { name_pattern: namePattern },
}),
)
export const deleteTagPolicy = (space: string, repo: string, policyId: string) =>
unwrapApi<null>(
Apis.tag_protection.delete_tag_policy({
pathParams: { space_name: space, repo_name: repo, policy_id: policyId },
}),
)
export const listTagWhitelists = (space: string, repo: string, policyId: string) =>
unwrapApi<TagWhitelistRow[] | null>(
Apis.tag_protection.list_tag_whitelists({
pathParams: { space_name: space, repo_name: repo, policy_id: policyId },
}),
).then((rows) => rows ?? [])
export const addTagWhitelist = (space: string, repo: string, policyId: string, userId: number) =>
unwrapApi<TagWhitelistRow>(
Apis.tag_protection.add_tag_whitelist({
pathParams: { space_name: space, repo_name: repo, policy_id: policyId },
data: { user_id: userId },
}),
)
export const removeTagWhitelist = (space: string, repo: string, policyId: string, userId: number) =>
unwrapApi<null>(
Apis.tag_protection.remove_tag_whitelist({
pathParams: { space_name: space, repo_name: repo, policy_id: policyId, user_id: userId },
}),
)
/** Create/update payload minus server-managed fields. */
export interface CreatePolicyInput {
branch_pattern: string
required_approvals?: number | null
enable_push?: boolean | null
enable_force_push?: boolean | null
enable_push_whitelist?: boolean | null
enable_merge_whitelist?: boolean | null
enable_approvals_whitelist?: boolean | null
block_on_rejected_reviews?: boolean | null
block_on_change_requested_reviews?: boolean | null
block_on_outdated_branch?: boolean | null
dismiss_stale_approvals?: boolean | null
require_signed_commits?: boolean | null
protected_file_patterns?: string | null
unprotected_file_patterns?: string | null
}
/** Space members as user options for the whitelist pickers. */
export interface UserOption {
user_id: number
username: string
display_name: string
}
export const listUserOptions = async (space: string): Promise<UserOption[]> => {
const page = await unwrapApi<MemberSummaryPage | null>(
Apis.space.list_space_members({ pathParams: { space_name: space }, params: { limit: 100 } }),
)
return (page?.items ?? []).map((row) => {
const user = toMemberUserRow(row).user
return { user_id: user.user_id, username: user.username, display_name: user.display_name }
})
}