krz/hutch

an ios client for sourcehut

clone: git clone https://gitbay.org/krz/hutch.git

v3.10.0: Hutch/Networking/RepositoryDeployKeyService.swift · raw

  1import Foundation
  2
  3/// A per-repository deploy key (git.sr.ht `SSHKey` under `Repository.deployKeys`).
  4struct RepositoryDeployKey: Decodable, Sendable, Identifiable, Hashable {
  5    let rid: String
  6    let keyType: String
  7    let fingerprintSHA256: String
  8    let comment: String?
  9    let access: AccessMode
 10
 11    var id: String { rid }
 12}
 13
 14private struct DeployKeysQueryResponse: Decodable, Sendable {
 15    let repository: DeployKeysRepository?
 16}
 17
 18private struct DeployKeysRepository: Decodable, Sendable {
 19    let deployKeys: DeployKeysPage
 20}
 21
 22private struct DeployKeysPage: Decodable, Sendable {
 23    let results: [RepositoryDeployKey]
 24}
 25
 26/// `createDeployKey`'s response returns an empty `access` (the stored value is
 27/// correct  the list query reports it), so we select only `rid` here and let
 28/// callers reload rather than decode the partial key.
 29private struct CreateDeployKeyResponse: Decodable, Sendable {}
 30
 31/// Delete returns the removed key; only success matters here.
 32private struct DeleteDeployKeyResponse: Decodable, Sendable {}
 33
 34/// Deploy keys are a git.sr.ht capability (`createDeployKey` / `deleteDeployKey`),
 35/// owner-only, alongside repository ACLs.
 36struct RepositoryDeployKeyService: Sendable {
 37    private let client: SRHTClient
 38
 39    init(client: SRHTClient) {
 40        self.client = client
 41    }
 42
 43    func fetchDeployKeys(repositoryRid: String) async throws -> [RepositoryDeployKey] {
 44        let response = try await client.execute(
 45            service: .git,
 46            query: Self.deployKeysQuery,
 47            variables: ["rid": repositoryRid],
 48            responseType: DeployKeysQueryResponse.self
 49        )
 50        return response.repository?.deployKeys.results ?? []
 51    }
 52
 53    func createDeployKey(repositoryRid: String, mode: AccessMode, key: String) async throws {
 54        _ = try await client.execute(
 55            service: .git,
 56            query: Self.createDeployKeyMutation,
 57            variables: ["repo": repositoryRid, "mode": mode.rawValue, "key": key],
 58            responseType: CreateDeployKeyResponse.self
 59        )
 60    }
 61
 62    func deleteDeployKey(rid: String) async throws {
 63        _ = try await client.execute(
 64            service: .git,
 65            query: Self.deleteDeployKeyMutation,
 66            variables: ["rid": rid],
 67            responseType: DeleteDeployKeyResponse.self
 68        )
 69    }
 70}
 71
 72private extension RepositoryDeployKeyService {
 73    static let deployKeysQuery = """
 74    query repositoryDeployKeys($rid: ID!) {
 75        repository(rid: $rid) {
 76            deployKeys {
 77                results {
 78                    rid
 79                    keyType
 80                    fingerprintSHA256
 81                    comment
 82                    access
 83                }
 84            }
 85        }
 86    }
 87    """
 88
 89    static let createDeployKeyMutation = """
 90    mutation createDeployKey($repo: ID!, $mode: AccessMode!, $key: String!) {
 91        createDeployKey(repo: $repo, mode: $mode, key: $key) { rid }
 92    }
 93    """
 94
 95    static let deleteDeployKeyMutation = """
 96    mutation deleteDeployKey($rid: ID!) {
 97        deleteDeployKey(rid: $rid) { rid }
 98    }
 99    """
100}