gitbay/Repos/WebhookViewModels.swift
106 lines · 3407 bytes
8 symbols in this file
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}