api-swift / Sources / IrdAPI / Gate / GatedOperations.swift
517 words
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
import Foundation

/// How the gate finds the domain a gated mutation touches.
enum GateSource: Sendable {
    /// A field of the JSON body (`domain`, `hostname`, `zone`).
    case bodyField(String)
    /// A path parameter holding the domain.
    case pathParam(String)
    /// A path parameter holding a registration id, resolved through the index.
    case registrationId(pathParam: String)
    /// One read with the same path parameters, then a field of its answer.
    case resolve(GateResolve)
}

struct GateResolve: Sendable {
    let read: IrdOperation
    let extract: Extract

    enum Extract: Sendable {
        /// A domain at this key path of the read's body.
        case domain(path: [String])
        /// A registration id at this key path.
        case registrationId(path: [String])
        /// The row of `array` whose `id` equals the `idParam` path parameter; then `field`.
        case domainInArray(array: String, idParam: String, field: String)
    }
}

/// Every gated mutation, keyed by `"METHOD /path/{template}"`. Ported from the
/// sibling TypeScript client; reads and everything not touching a domain are absent.
enum GatedOperations {
    static let table: [String: GateSource] = {
        var t: [String: GateSource] = [:]
        for k in ["POST /domains/registrations", "POST /domains/transfers-in", "POST /domains/trades-in",
                  "POST /domains/adoptions", "POST /dns/claims", "POST /ipfs/domains"] {
            t[k] = .bodyField("domain")
        }
        for k in ["POST /tunnels", "POST /p2p/apps/{id}/domains"] { t[k] = .bodyField("hostname") }
        t["POST /vps/instances/{id}/domains"] = .bodyField("zone")
        for k in ["PUT /dns/domains/{domain}/records", "DELETE /dns/domains/{domain}/records"] { t[k] = .pathParam("domain") }
        for k in ["POST /domains/registrations/{id}/renew", "PUT /domains/registrations/{id}/auto-renew",
                  "PUT /domains/registrations/{id}/sensitive", "PUT /domains/registrations/{id}/nameservers",
                  "POST /domains/registrations/{id}/hosts", "PUT /domains/registrations/{id}/hosts/{host}",
                  "DELETE /domains/registrations/{id}/hosts/{host}", "POST /domains/registrations/{id}/dsdata",
                  "DELETE /domains/registrations/{id}/dsdata", "POST /domains/registrations/{id}/transfer-out",
                  "PUT /domains/registrations/{id}/registrant", "POST /domains/registrations/{id}/registrant-email/start",
                  "POST /domains/registrations/{id}/registrant-email/confirm", "POST /domains/registrations/{id}/registrant-email/adopt",
                  "POST /domains/registrations/{id}/trade", "POST /domains/registrations/{id}/dns",
                  "DELETE /domains/registrations/{id}/dns", "POST /domains/registrations/{id}/dns/dnssec",
                  "DELETE /domains/registrations/{id}/dns/dnssec", "POST /domains/registrations/{id}/dns/nameservers",
                  "POST /domains/registrations/{id}/grants", "DELETE /domains/registrations/{id}/grants/{grant-id}"] {
            t[k] = .registrationId(pathParam: "id")
        }
        func read(_ path: String, _ module: String, _ name: String) -> IrdOperation {
            IrdOperation(method: .get, pathTemplate: path, module: module, name: name, security: .keyOrJwt)
        }
        t["POST /domains/actions/{id}/cancel"] = .resolve(GateResolve(
            read: read("/domains/actions/{id}", "registrations", "getAction"), extract: .registrationId(path: ["action", "registration_id"])))
        let claim = GateResolve(read: read("/dns/claims/{id}", "dns", "getClaim"), extract: .domain(path: ["claim", "domain"]))
        for k in ["DELETE /dns/claims/{id}", "PUT /dns/claims/{id}/seed", "DELETE /dns/claims/{id}/seed"] { t[k] = .resolve(claim) }
        let tunnel = GateResolve(read: read("/tunnels/{id}", "tunnels", "get"), extract: .domain(path: ["tunnel", "hostname"]))
        for k in ["DELETE /tunnels/{id}", "PATCH /tunnels/{id}"] { t[k] = .resolve(tunnel) }
        let gateway = GateResolve(read: read("/ipfs/domains/{id}", "ipfs", "getDomain"), extract: .domain(path: ["domain", "domain"]))
        for k in ["POST /ipfs/domains/{id}/pins", "DELETE /ipfs/domains/{id}"] { t[k] = .resolve(gateway) }
        t["DELETE /p2p/apps/{id}/domains/{domain-id}"] = .resolve(GateResolve(
            read: read("/p2p/apps/{id}/domains", "p2p", "listAppDomains"),
            extract: .domainInArray(array: "domains", idParam: "domain-id", field: "hostname")))
        t["DELETE /vps/instances/{id}/domains/{connection-id}"] = .resolve(GateResolve(
            read: read("/vps/instances/{id}/domains", "vps", "listInstanceDomains"),
            extract: .domainInArray(array: "connections", idParam: "connection-id", field: "zone_origin")))
        return t
    }()
}

extension IrdClient {
    /// Turns a gate source into the target to check, issuing the resolve read when needed.
    func gateTarget(for source: GateSource, params: [String: String], body: JSONValue?) async throws -> GateTarget {
        switch source {
        case .bodyField(let field):
            return .domain(body?[field]?.stringValue)
        case .pathParam(let name):
            return .domain(params[name])
        case .registrationId(let name):
            return .registrationId(params[name])
        case .resolve(let r):
            let json = try await perform(r.read, params: params, query: [], body: nil, gated: false).json
            switch r.extract {
            case .domain(let path):
                return .domain(json.at(path)?.stringValue)
            case .registrationId(let path):
                return .registrationId(json.at(path)?.stringValue)
            case .domainInArray(let array, let idParam, let field):
                let row = json[array]?.arrayValue?.first { $0["id"]?.stringValue == params[idParam] }
                return .domain(row?[field]?.stringValue)
            }
        }
    }
}

static mirror of HEAD · about · clone: git clone https://ird.cod-sursa.ro/api-swift.git