gitbay/Repos/WebhookViewModels.swift

v1.13.1
gitbay-ios/gitbay/Repos/WebhookViewModels.swift history · blame · raw

106 lines · 3407 bytes

  1import Foundation
  2import Observation
  3
  4/// `webhook list`, `webhook add` and `webhook remove` for one repository.
  5@Observable
  6@MainActor
  7final class WebhookListViewModel {
  8
  9    private(set) var state: LoadState<[Webhook]> = .loading
 10    private(set) var actionError: String?
 11    private(set) var working = false
 12
 13    private let client: GitbayClient
 14    let repoPath: String
 15
 16    init(client: GitbayClient, repoPath: String) {
 17        self.client = client
 18        self.repoPath = repoPath
 19    }
 20
 21    func load() async {
 22        state = .loading
 23        do {
 24            let hooks = try await client.readList(["webhook", "list", repoPath], of: Webhook.self)
 25            state = hooks.isEmpty ? .empty("No webhooks yet.") : .loaded(hooks)
 26        } catch {
 27            state = .from(error)
 28        }
 29    }
 30
 31    /// `webhook add <repo> <url> [--secret -] [--events <e>]` — an empty
 32    /// secret or event list is left out, so the server applies its default.
 33    /// The secret travels on stdin; the server refuses it in argv.
 34    func add(url: String, secret: String, events: String) async -> Bool {
 35        let url = url.trimmingCharacters(in: .whitespaces)
 36        guard !url.isEmpty else { return false }
 37        var argv = ["webhook", "add", repoPath, url]
 38        let secret = secret.trimmingCharacters(in: .whitespaces)
 39        if !secret.isEmpty { argv += ["--secret", "-"] }
 40        let events = events.trimmingCharacters(in: .whitespaces)
 41        if !events.isEmpty { argv += ["--events", events] }
 42        await perform(argv, stdin: secret.isEmpty ? nil : secret + "\n")
 43        return actionError == nil
 44    }
 45
 46    func remove(id: Int64) async {
 47        await perform(["webhook", "remove", repoPath, String(id)])
 48    }
 49
 50    private func perform(_ argv: [String], stdin: String? = nil) async {
 51        working = true
 52        actionError = nil
 53        defer { working = false }
 54        do {
 55            try await client.run(argv, stdin: stdin)
 56            await load()
 57        } catch let error as GitbayError {
 58            actionError = error.userFacingMessage
 59        } catch {
 60            actionError = GitbayError.transport(error).userFacingMessage
 61        }
 62    }
 63}
 64
 65/// `webhook deliveries` and `webhook redeliver`.
 66@Observable
 67@MainActor
 68final class WebhookDeliveriesViewModel {
 69
 70    private(set) var state: LoadState<[WebhookDelivery]> = .loading
 71    private(set) var actionError: String?
 72    private(set) var working = false
 73
 74    private let client: GitbayClient
 75    let repoPath: String
 76
 77    init(client: GitbayClient, repoPath: String) {
 78        self.client = client
 79        self.repoPath = repoPath
 80    }
 81
 82    func load() async {
 83        state = .loading
 84        do {
 85            let rows = try await client.readList(
 86                ["webhook", "deliveries", repoPath], of: WebhookDelivery.self)
 87            state = rows.isEmpty ? .empty("No deliveries yet.") : .loaded(rows)
 88        } catch {
 89            state = .from(error)
 90        }
 91    }
 92
 93    func redeliver(id: Int64) async {
 94        working = true
 95        actionError = nil
 96        defer { working = false }
 97        do {
 98            try await client.run(["webhook", "redeliver", repoPath, String(id)])
 99            await load()
100        } catch let error as GitbayError {
101            actionError = error.userFacingMessage
102        } catch {
103            actionError = GitbayError.transport(error).userFacingMessage
104        }
105    }
106}