feat(protocol): define native session credentials
This commit is contained in:
+227
-23
@@ -1,10 +1,10 @@
|
||||
// Code generated by tools/generate.py; DO NOT EDIT.
|
||||
import Foundation
|
||||
public typealias JSONObject = [String: String]
|
||||
public let schemaSHA256 = "b2bb0a8ac8ef56dbc0e1443eeb5b3028be9e71ec2f5fd8e73928d71b7cd9340c"
|
||||
public let currentWireVersion = "1"
|
||||
public let nMinus1WireVersion = "0"
|
||||
public let nMinus2WireVersion = "-1"
|
||||
public let schemaSHA256 = "dea3dd210c53d5a2d37050dd6afd8b0ac5bb8edcb7ab25a02e4026489ce8a00f"
|
||||
public let currentWireVersion = "2"
|
||||
public let nMinus1WireVersion = "1"
|
||||
public let nMinus2WireVersion = "0"
|
||||
public struct ContractValidationError: Error, Equatable { public let field: String; public let code: String }
|
||||
private struct AnyCodingKey: CodingKey { let stringValue: String; let intValue: Int?; init?(stringValue: String) { self.stringValue = stringValue; self.intValue = nil }; init?(intValue: Int) { self.stringValue = String(intValue); self.intValue = intValue } }
|
||||
private func validBase64URL(_ value: String) -> Bool {
|
||||
@@ -16,6 +16,23 @@ private func validBase64URL(_ value: String) -> Bool {
|
||||
guard let decoded = Data(base64Encoded: standard) else { return false }
|
||||
return decoded.base64EncodedString().replacingOccurrences(of: "+", with: "-").replacingOccurrences(of: "/", with: "_").replacingOccurrences(of: "=", with: "") == value
|
||||
}
|
||||
private func validRFC3339UTC(_ value: String) -> Bool {
|
||||
let bytes = Array(value.utf8)
|
||||
guard (20...30).contains(bytes.count), bytes[4] == 45, bytes[7] == 45, bytes[10] == 84, bytes[13] == 58, bytes[16] == 58, bytes.last == 90 else { return false }
|
||||
func digits(_ range: Range<Int>) -> Int? {
|
||||
var result = 0
|
||||
for index in range { guard bytes[index] >= 48 && bytes[index] <= 57 else { return nil }; result = result * 10 + Int(bytes[index] - 48) }
|
||||
return result
|
||||
}
|
||||
guard let year = digits(0..<4), let month = digits(5..<7), let day = digits(8..<10), let hour = digits(11..<13), let minute = digits(14..<16), let second = digits(17..<19), hour <= 23, minute <= 59, second <= 59 else { return false }
|
||||
let leap = year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)
|
||||
let days: Int
|
||||
switch month { case 1, 3, 5, 7, 8, 10, 12: days = 31; case 4, 6, 9, 11: days = 30; case 2: days = leap ? 29 : 28; default: return false }
|
||||
guard day > 0 && day <= days else { return false }
|
||||
if bytes.count == 20 { return true }
|
||||
let fraction = bytes[20..<(bytes.count - 1)]
|
||||
return bytes[19] == 46 && !fraction.isEmpty && fraction.count <= 9 && fraction.allSatisfy { $0 >= 48 && $0 <= 57 } && fraction.last != 48
|
||||
}
|
||||
|
||||
public struct AllocationPolicy: Codable, Equatable {
|
||||
public let minimumKbps: Int64
|
||||
@@ -217,7 +234,7 @@ public struct BrokerSession: Codable, Equatable {
|
||||
try self.policySnapshot.validate()
|
||||
if let value = self.reconnectDeadline {
|
||||
if value.utf8.count > 64 { throw ContractValidationError(field: "reconnect_deadline", code: "max_length") }
|
||||
if ISO8601DateFormatter().date(from: value) == nil { throw ContractValidationError(field: "reconnect_deadline", code: "invalid_time") }
|
||||
if !validRFC3339UTC(value) { throw ContractValidationError(field: "reconnect_deadline", code: "invalid_time") }
|
||||
}
|
||||
if let value = self.outcome {
|
||||
if value.utf8.count > 64 { throw ContractValidationError(field: "outcome", code: "max_length") }
|
||||
@@ -235,10 +252,10 @@ public struct BrokerSession: Codable, Equatable {
|
||||
if !self.correlationId.isEmpty && self.correlationId.utf8.count < 1 { throw ContractValidationError(field: "correlation_id", code: "min_length") }
|
||||
if self.correlationId.utf8.count > 128 { throw ContractValidationError(field: "correlation_id", code: "max_length") }
|
||||
if self.requestedAt.utf8.count > 64 { throw ContractValidationError(field: "requested_at", code: "max_length") }
|
||||
if ISO8601DateFormatter().date(from: self.requestedAt) == nil { throw ContractValidationError(field: "requested_at", code: "invalid_time") }
|
||||
if !validRFC3339UTC(self.requestedAt) { throw ContractValidationError(field: "requested_at", code: "invalid_time") }
|
||||
if let value = self.endedAt {
|
||||
if value.utf8.count > 64 { throw ContractValidationError(field: "ended_at", code: "max_length") }
|
||||
if ISO8601DateFormatter().date(from: value) == nil { throw ContractValidationError(field: "ended_at", code: "invalid_time") }
|
||||
if !validRFC3339UTC(value) { throw ContractValidationError(field: "ended_at", code: "invalid_time") }
|
||||
}
|
||||
if self.version < 1 { throw ContractValidationError(field: "version", code: "minimum") }
|
||||
if let value = self.requestedDisplayMode {
|
||||
@@ -253,6 +270,51 @@ public struct BrokerSession: Codable, Equatable {
|
||||
public func encodeJSON() throws -> Data { try validate(); return try JSONEncoder().encode(self) }
|
||||
}
|
||||
|
||||
public struct BrowserAuthenticatedSession: Codable, Equatable {
|
||||
public let username: String
|
||||
public let provider: String
|
||||
public let roles: [String]
|
||||
public let role: String
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case username = "username"
|
||||
case provider = "provider"
|
||||
case roles = "roles"
|
||||
case role = "role"
|
||||
}
|
||||
|
||||
public init(username: String, provider: String, roles: [String], role: String) throws {
|
||||
self.username = username
|
||||
self.provider = provider
|
||||
self.roles = roles
|
||||
self.role = role
|
||||
try validate()
|
||||
}
|
||||
|
||||
public init(from decoder: Decoder) throws {
|
||||
let all = try decoder.container(keyedBy: AnyCodingKey.self)
|
||||
for key in all.allKeys where CodingKeys(stringValue: key.stringValue) == nil { throw ContractValidationError(field: key.stringValue, code: "unknown_field") }
|
||||
let c = try decoder.container(keyedBy: CodingKeys.self)
|
||||
try self.init(username: try c.decode(String.self, forKey: .username), provider: try c.decode(String.self, forKey: .provider), roles: try c.decode([String].self, forKey: .roles), role: try c.decode(String.self, forKey: .role))
|
||||
}
|
||||
|
||||
public func validate() throws {
|
||||
if self.username.isEmpty { throw ContractValidationError(field: "username", code: "required") }
|
||||
if !self.username.isEmpty && self.username.utf8.count < 1 { throw ContractValidationError(field: "username", code: "min_length") }
|
||||
if self.username.utf8.count > 256 { throw ContractValidationError(field: "username", code: "max_length") }
|
||||
if self.provider.isEmpty { throw ContractValidationError(field: "provider", code: "required") }
|
||||
if !self.provider.isEmpty && self.provider.utf8.count < 1 { throw ContractValidationError(field: "provider", code: "min_length") }
|
||||
if self.provider.utf8.count > 64 { throw ContractValidationError(field: "provider", code: "max_length") }
|
||||
if self.roles.count > 16 { throw ContractValidationError(field: "roles", code: "max_items") }
|
||||
for item in self.roles where item.utf8.count < 1 { throw ContractValidationError(field: "roles", code: "min_item_length") }
|
||||
for item in self.roles where item.utf8.count > 64 { throw ContractValidationError(field: "roles", code: "max_item_length") }
|
||||
for item in self.roles where item.utf8.count > 64 { throw ContractValidationError(field: "roles", code: "max_item_bytes") }
|
||||
if !["user", "admin"].contains(self.role) { throw ContractValidationError(field: "role", code: "invalid_value") }
|
||||
}
|
||||
|
||||
public static func decodeJSON(_ data: Data) throws -> Self { try JSONDecoder().decode(Self.self, from: data) }
|
||||
public func encodeJSON() throws -> Data { try validate(); return try JSONEncoder().encode(self) }
|
||||
}
|
||||
|
||||
public struct CapabilityProfile: Codable, Equatable {
|
||||
public let transport: String
|
||||
public let framing: String
|
||||
@@ -551,7 +613,7 @@ public struct DeviceChallenge: Codable, Equatable {
|
||||
if !self.challenge.isEmpty && self.challenge.utf8.count < 1 { throw ContractValidationError(field: "challenge", code: "min_length") }
|
||||
if self.challenge.utf8.count > 256 { throw ContractValidationError(field: "challenge", code: "max_length") }
|
||||
if self.expiresAt.utf8.count > 64 { throw ContractValidationError(field: "expires_at", code: "max_length") }
|
||||
if ISO8601DateFormatter().date(from: self.expiresAt) == nil { throw ContractValidationError(field: "expires_at", code: "invalid_time") }
|
||||
if !validRFC3339UTC(self.expiresAt) { throw ContractValidationError(field: "expires_at", code: "invalid_time") }
|
||||
if self.algorithm != "ed25519" { throw ContractValidationError(field: "algorithm", code: "invalid_value") }
|
||||
if self.signatureFormat != "ed25519-domain-separated-v1" { throw ContractValidationError(field: "signature_format", code: "invalid_value") }
|
||||
}
|
||||
@@ -831,7 +893,7 @@ public struct EventEnvelope: Codable, Equatable {
|
||||
if self.version < 1 { throw ContractValidationError(field: "version", code: "minimum") }
|
||||
try self.resource.validate()
|
||||
if self.occurredAt.utf8.count > 64 { throw ContractValidationError(field: "occurred_at", code: "max_length") }
|
||||
if ISO8601DateFormatter().date(from: self.occurredAt) == nil { throw ContractValidationError(field: "occurred_at", code: "invalid_time") }
|
||||
if !validRFC3339UTC(self.occurredAt) { throw ContractValidationError(field: "occurred_at", code: "invalid_time") }
|
||||
if self.correlationId.isEmpty { throw ContractValidationError(field: "correlation_id", code: "required") }
|
||||
if !self.correlationId.isEmpty && self.correlationId.utf8.count < 1 { throw ContractValidationError(field: "correlation_id", code: "min_length") }
|
||||
if self.correlationId.utf8.count > 128 { throw ContractValidationError(field: "correlation_id", code: "max_length") }
|
||||
@@ -1036,7 +1098,7 @@ public struct GatewayDrain: Codable, Equatable {
|
||||
if !self.reason.isEmpty && self.reason.utf8.count < 1 { throw ContractValidationError(field: "reason", code: "min_length") }
|
||||
if self.reason.utf8.count > 256 { throw ContractValidationError(field: "reason", code: "max_length") }
|
||||
if self.deadline.utf8.count > 64 { throw ContractValidationError(field: "deadline", code: "max_length") }
|
||||
if ISO8601DateFormatter().date(from: self.deadline) == nil { throw ContractValidationError(field: "deadline", code: "invalid_time") }
|
||||
if !validRFC3339UTC(self.deadline) { throw ContractValidationError(field: "deadline", code: "invalid_time") }
|
||||
}
|
||||
|
||||
public static func decodeJSON(_ data: Data) throws -> Self { try JSONDecoder().decode(Self.self, from: data) }
|
||||
@@ -1089,7 +1151,7 @@ public struct GatewayHeartbeat: Codable, Equatable {
|
||||
if self.gatewayId.utf8.count > 128 { throw ContractValidationError(field: "gateway_id", code: "max_length") }
|
||||
if self.sequence < 1 { throw ContractValidationError(field: "sequence", code: "minimum") }
|
||||
if self.observedAt.utf8.count > 64 { throw ContractValidationError(field: "observed_at", code: "max_length") }
|
||||
if ISO8601DateFormatter().date(from: self.observedAt) == nil { throw ContractValidationError(field: "observed_at", code: "invalid_time") }
|
||||
if !validRFC3339UTC(self.observedAt) { throw ContractValidationError(field: "observed_at", code: "invalid_time") }
|
||||
if self.activeConnections < 0 { throw ContractValidationError(field: "active_connections", code: "minimum") }
|
||||
if self.activeConnections > 1000000 { throw ContractValidationError(field: "active_connections", code: "maximum") }
|
||||
if self.egressKbps < 0 { throw ContractValidationError(field: "egress_kbps", code: "minimum") }
|
||||
@@ -1185,6 +1247,8 @@ public struct GatewayRegistration: Codable, Equatable {
|
||||
if self.bandwidthCapacityKbps < 1 { throw ContractValidationError(field: "bandwidth_capacity_kbps", code: "minimum") }
|
||||
if self.bandwidthCapacityKbps > 1000000000 { throw ContractValidationError(field: "bandwidth_capacity_kbps", code: "maximum") }
|
||||
if self.features.count > 64 { throw ContractValidationError(field: "features", code: "max_items") }
|
||||
for item in self.features where item.utf8.count < 1 { throw ContractValidationError(field: "features", code: "min_item_length") }
|
||||
for item in self.features where item.utf8.count > 64 { throw ContractValidationError(field: "features", code: "max_item_length") }
|
||||
try self.capabilities.validate()
|
||||
if protocolMinVersion > protocolMaxVersion { throw ContractValidationError(field: "protocol_version", code: "invalid_order") }
|
||||
}
|
||||
@@ -1333,7 +1397,7 @@ public struct GrantReference: Codable, Equatable {
|
||||
if !self.opaqueValue.isEmpty && self.opaqueValue.utf8.count < 43 { throw ContractValidationError(field: "opaque_value", code: "min_length") }
|
||||
if self.opaqueValue.utf8.count > 256 { throw ContractValidationError(field: "opaque_value", code: "max_length") }
|
||||
if self.expiresAt.utf8.count > 64 { throw ContractValidationError(field: "expires_at", code: "max_length") }
|
||||
if ISO8601DateFormatter().date(from: self.expiresAt) == nil { throw ContractValidationError(field: "expires_at", code: "invalid_time") }
|
||||
if !validRFC3339UTC(self.expiresAt) { throw ContractValidationError(field: "expires_at", code: "invalid_time") }
|
||||
if self.audience.isEmpty { throw ContractValidationError(field: "audience", code: "required") }
|
||||
if !self.audience.isEmpty && self.audience.utf8.count < 1 { throw ContractValidationError(field: "audience", code: "min_length") }
|
||||
if self.audience.utf8.count > 128 { throw ContractValidationError(field: "audience", code: "max_length") }
|
||||
@@ -1451,6 +1515,8 @@ public struct ManifestGateway: Codable, Equatable {
|
||||
if self.id.utf8.count > 128 { throw ContractValidationError(field: "id", code: "max_length") }
|
||||
if self.addresses.count < 1 { throw ContractValidationError(field: "addresses", code: "min_items") }
|
||||
if self.addresses.count > 4 { throw ContractValidationError(field: "addresses", code: "max_items") }
|
||||
for item in self.addresses where item.utf8.count < 1 { throw ContractValidationError(field: "addresses", code: "min_item_length") }
|
||||
for item in self.addresses where item.utf8.count > 256 { throw ContractValidationError(field: "addresses", code: "max_item_length") }
|
||||
if self.publicIdentity.isEmpty { throw ContractValidationError(field: "public_identity", code: "required") }
|
||||
if !self.publicIdentity.isEmpty && self.publicIdentity.utf8.count < 1 { throw ContractValidationError(field: "public_identity", code: "min_length") }
|
||||
if self.publicIdentity.utf8.count > 256 { throw ContractValidationError(field: "public_identity", code: "max_length") }
|
||||
@@ -1522,7 +1588,60 @@ public struct ManifestTunnel: Codable, Equatable {
|
||||
public func validate() throws {
|
||||
if self.versions.count < 1 { throw ContractValidationError(field: "versions", code: "min_items") }
|
||||
if self.versions.count > 4 { throw ContractValidationError(field: "versions", code: "max_items") }
|
||||
for item in self.versions where item.utf8.count < 1 { throw ContractValidationError(field: "versions", code: "min_item_length") }
|
||||
for item in self.versions where item.utf8.count > 64 { throw ContractValidationError(field: "versions", code: "max_item_length") }
|
||||
if self.features.count > 32 { throw ContractValidationError(field: "features", code: "max_items") }
|
||||
for item in self.features where item.utf8.count < 1 { throw ContractValidationError(field: "features", code: "min_item_length") }
|
||||
for item in self.features where item.utf8.count > 64 { throw ContractValidationError(field: "features", code: "max_item_length") }
|
||||
}
|
||||
|
||||
public static func decodeJSON(_ data: Data) throws -> Self { try JSONDecoder().decode(Self.self, from: data) }
|
||||
public func encodeJSON() throws -> Data { try validate(); return try JSONEncoder().encode(self) }
|
||||
}
|
||||
|
||||
public struct NativeAuthenticatedSession: Codable, Equatable {
|
||||
public let username: String
|
||||
public let provider: String
|
||||
public let roles: [String]
|
||||
public let role: String
|
||||
public let nativeIdentity: NativeSessionIdentity
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case username = "username"
|
||||
case provider = "provider"
|
||||
case roles = "roles"
|
||||
case role = "role"
|
||||
case nativeIdentity = "native_identity"
|
||||
}
|
||||
|
||||
public init(username: String, provider: String, roles: [String], role: String, nativeIdentity: NativeSessionIdentity) throws {
|
||||
self.username = username
|
||||
self.provider = provider
|
||||
self.roles = roles
|
||||
self.role = role
|
||||
self.nativeIdentity = nativeIdentity
|
||||
try validate()
|
||||
}
|
||||
|
||||
public init(from decoder: Decoder) throws {
|
||||
let all = try decoder.container(keyedBy: AnyCodingKey.self)
|
||||
for key in all.allKeys where CodingKeys(stringValue: key.stringValue) == nil { throw ContractValidationError(field: key.stringValue, code: "unknown_field") }
|
||||
let c = try decoder.container(keyedBy: CodingKeys.self)
|
||||
try self.init(username: try c.decode(String.self, forKey: .username), provider: try c.decode(String.self, forKey: .provider), roles: try c.decode([String].self, forKey: .roles), role: try c.decode(String.self, forKey: .role), nativeIdentity: try c.decode(NativeSessionIdentity.self, forKey: .nativeIdentity))
|
||||
}
|
||||
|
||||
public func validate() throws {
|
||||
if self.username.isEmpty { throw ContractValidationError(field: "username", code: "required") }
|
||||
if !self.username.isEmpty && self.username.utf8.count < 1 { throw ContractValidationError(field: "username", code: "min_length") }
|
||||
if self.username.utf8.count > 256 { throw ContractValidationError(field: "username", code: "max_length") }
|
||||
if self.provider.isEmpty { throw ContractValidationError(field: "provider", code: "required") }
|
||||
if !self.provider.isEmpty && self.provider.utf8.count < 1 { throw ContractValidationError(field: "provider", code: "min_length") }
|
||||
if self.provider.utf8.count > 64 { throw ContractValidationError(field: "provider", code: "max_length") }
|
||||
if self.roles.count > 16 { throw ContractValidationError(field: "roles", code: "max_items") }
|
||||
for item in self.roles where item.utf8.count < 1 { throw ContractValidationError(field: "roles", code: "min_item_length") }
|
||||
for item in self.roles where item.utf8.count > 64 { throw ContractValidationError(field: "roles", code: "max_item_length") }
|
||||
for item in self.roles where item.utf8.count > 64 { throw ContractValidationError(field: "roles", code: "max_item_bytes") }
|
||||
if !["user", "admin"].contains(self.role) { throw ContractValidationError(field: "role", code: "invalid_value") }
|
||||
try self.nativeIdentity.validate()
|
||||
}
|
||||
|
||||
public static func decodeJSON(_ data: Data) throws -> Self { try JSONDecoder().decode(Self.self, from: data) }
|
||||
@@ -1576,10 +1695,10 @@ public struct NativeCredential: Codable, Equatable {
|
||||
if !self.refreshToken.isEmpty && self.refreshToken.utf8.count < 1 { throw ContractValidationError(field: "refresh_token", code: "min_length") }
|
||||
if self.refreshToken.utf8.count > 256 { throw ContractValidationError(field: "refresh_token", code: "max_length") }
|
||||
if self.expiresAt.utf8.count > 64 { throw ContractValidationError(field: "expires_at", code: "max_length") }
|
||||
if ISO8601DateFormatter().date(from: self.expiresAt) == nil { throw ContractValidationError(field: "expires_at", code: "invalid_time") }
|
||||
if !validRFC3339UTC(self.expiresAt) { throw ContractValidationError(field: "expires_at", code: "invalid_time") }
|
||||
if let value = self.refreshExpiresAt {
|
||||
if value.utf8.count > 64 { throw ContractValidationError(field: "refresh_expires_at", code: "max_length") }
|
||||
if ISO8601DateFormatter().date(from: value) == nil { throw ContractValidationError(field: "refresh_expires_at", code: "invalid_time") }
|
||||
if !validRFC3339UTC(value) { throw ContractValidationError(field: "refresh_expires_at", code: "invalid_time") }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1587,6 +1706,91 @@ public struct NativeCredential: Codable, Equatable {
|
||||
public func encodeJSON() throws -> Data { try validate(); return try JSONEncoder().encode(self) }
|
||||
}
|
||||
|
||||
public struct NativeSessionIdentity: Codable, Equatable {
|
||||
public let clientDeviceId: String
|
||||
public let deviceKeyId: String
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case clientDeviceId = "client_device_id"
|
||||
case deviceKeyId = "device_key_id"
|
||||
}
|
||||
|
||||
public init(clientDeviceId: String, deviceKeyId: String) throws {
|
||||
self.clientDeviceId = clientDeviceId
|
||||
self.deviceKeyId = deviceKeyId
|
||||
try validate()
|
||||
}
|
||||
|
||||
public init(from decoder: Decoder) throws {
|
||||
let all = try decoder.container(keyedBy: AnyCodingKey.self)
|
||||
for key in all.allKeys where CodingKeys(stringValue: key.stringValue) == nil { throw ContractValidationError(field: key.stringValue, code: "unknown_field") }
|
||||
let c = try decoder.container(keyedBy: CodingKeys.self)
|
||||
try self.init(clientDeviceId: try c.decode(String.self, forKey: .clientDeviceId), deviceKeyId: try c.decode(String.self, forKey: .deviceKeyId))
|
||||
}
|
||||
|
||||
public func validate() throws {
|
||||
if self.clientDeviceId.isEmpty { throw ContractValidationError(field: "client_device_id", code: "required") }
|
||||
if !self.clientDeviceId.isEmpty && self.clientDeviceId.utf8.count < 1 { throw ContractValidationError(field: "client_device_id", code: "min_length") }
|
||||
if self.clientDeviceId.utf8.count > 128 { throw ContractValidationError(field: "client_device_id", code: "max_length") }
|
||||
if self.deviceKeyId.isEmpty { throw ContractValidationError(field: "device_key_id", code: "required") }
|
||||
if !self.deviceKeyId.isEmpty && self.deviceKeyId.utf8.count < 1 { throw ContractValidationError(field: "device_key_id", code: "min_length") }
|
||||
if self.deviceKeyId.utf8.count > 128 { throw ContractValidationError(field: "device_key_id", code: "max_length") }
|
||||
}
|
||||
|
||||
public static func decodeJSON(_ data: Data) throws -> Self { try JSONDecoder().decode(Self.self, from: data) }
|
||||
public func encodeJSON() throws -> Data { try validate(); return try JSONEncoder().encode(self) }
|
||||
}
|
||||
|
||||
public struct NativeTunnelCredential: Codable, Equatable {
|
||||
public let clientDeviceId: String
|
||||
public let deviceKeyId: String
|
||||
public let certificateChainPem: String
|
||||
public let trustBundlePem: String
|
||||
public let expiresAt: String
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case clientDeviceId = "client_device_id"
|
||||
case deviceKeyId = "device_key_id"
|
||||
case certificateChainPem = "certificate_chain_pem"
|
||||
case trustBundlePem = "trust_bundle_pem"
|
||||
case expiresAt = "expires_at"
|
||||
}
|
||||
|
||||
public init(clientDeviceId: String, deviceKeyId: String, certificateChainPem: String, trustBundlePem: String, expiresAt: String) throws {
|
||||
self.clientDeviceId = clientDeviceId
|
||||
self.deviceKeyId = deviceKeyId
|
||||
self.certificateChainPem = certificateChainPem
|
||||
self.trustBundlePem = trustBundlePem
|
||||
self.expiresAt = expiresAt
|
||||
try validate()
|
||||
}
|
||||
|
||||
public init(from decoder: Decoder) throws {
|
||||
let all = try decoder.container(keyedBy: AnyCodingKey.self)
|
||||
for key in all.allKeys where CodingKeys(stringValue: key.stringValue) == nil { throw ContractValidationError(field: key.stringValue, code: "unknown_field") }
|
||||
let c = try decoder.container(keyedBy: CodingKeys.self)
|
||||
try self.init(clientDeviceId: try c.decode(String.self, forKey: .clientDeviceId), deviceKeyId: try c.decode(String.self, forKey: .deviceKeyId), certificateChainPem: try c.decode(String.self, forKey: .certificateChainPem), trustBundlePem: try c.decode(String.self, forKey: .trustBundlePem), expiresAt: try c.decode(String.self, forKey: .expiresAt))
|
||||
}
|
||||
|
||||
public func validate() throws {
|
||||
if self.clientDeviceId.isEmpty { throw ContractValidationError(field: "client_device_id", code: "required") }
|
||||
if !self.clientDeviceId.isEmpty && self.clientDeviceId.utf8.count < 1 { throw ContractValidationError(field: "client_device_id", code: "min_length") }
|
||||
if self.clientDeviceId.utf8.count > 128 { throw ContractValidationError(field: "client_device_id", code: "max_length") }
|
||||
if self.deviceKeyId.isEmpty { throw ContractValidationError(field: "device_key_id", code: "required") }
|
||||
if !self.deviceKeyId.isEmpty && self.deviceKeyId.utf8.count < 1 { throw ContractValidationError(field: "device_key_id", code: "min_length") }
|
||||
if self.deviceKeyId.utf8.count > 128 { throw ContractValidationError(field: "device_key_id", code: "max_length") }
|
||||
if self.certificateChainPem.isEmpty { throw ContractValidationError(field: "certificate_chain_pem", code: "required") }
|
||||
if !self.certificateChainPem.isEmpty && self.certificateChainPem.utf8.count < 1 { throw ContractValidationError(field: "certificate_chain_pem", code: "min_length") }
|
||||
if self.certificateChainPem.utf8.count > 65536 { throw ContractValidationError(field: "certificate_chain_pem", code: "max_length") }
|
||||
if self.trustBundlePem.isEmpty { throw ContractValidationError(field: "trust_bundle_pem", code: "required") }
|
||||
if !self.trustBundlePem.isEmpty && self.trustBundlePem.utf8.count < 1 { throw ContractValidationError(field: "trust_bundle_pem", code: "min_length") }
|
||||
if self.trustBundlePem.utf8.count > 65536 { throw ContractValidationError(field: "trust_bundle_pem", code: "max_length") }
|
||||
if self.expiresAt.utf8.count > 64 { throw ContractValidationError(field: "expires_at", code: "max_length") }
|
||||
if !validRFC3339UTC(self.expiresAt) { throw ContractValidationError(field: "expires_at", code: "invalid_time") }
|
||||
}
|
||||
|
||||
public static func decodeJSON(_ data: Data) throws -> Self { try JSONDecoder().decode(Self.self, from: data) }
|
||||
public func encodeJSON() throws -> Data { try validate(); return try JSONEncoder().encode(self) }
|
||||
}
|
||||
|
||||
public struct PageInfo: Codable, Equatable {
|
||||
public let limit: Int64
|
||||
public let nextCursor: String
|
||||
@@ -1703,7 +1907,7 @@ public struct ProviderSessionWork: Codable, Equatable {
|
||||
if self.gatewayId.utf8.count > 128 { throw ContractValidationError(field: "gateway_id", code: "max_length") }
|
||||
if self.reconnectSequence < 0 { throw ContractValidationError(field: "reconnect_sequence", code: "minimum") }
|
||||
if self.expiresAt.utf8.count > 64 { throw ContractValidationError(field: "expires_at", code: "max_length") }
|
||||
if ISO8601DateFormatter().date(from: self.expiresAt) == nil { throw ContractValidationError(field: "expires_at", code: "invalid_time") }
|
||||
if !validRFC3339UTC(self.expiresAt) { throw ContractValidationError(field: "expires_at", code: "invalid_time") }
|
||||
if self.providerProfile != "apollo" { throw ContractValidationError(field: "provider_profile", code: "invalid_value") }
|
||||
if self.providerIdentity.isEmpty { throw ContractValidationError(field: "provider_identity", code: "required") }
|
||||
if !self.providerIdentity.isEmpty && self.providerIdentity.utf8.count < 1 { throw ContractValidationError(field: "provider_identity", code: "min_length") }
|
||||
@@ -1781,6 +1985,8 @@ public struct ProviderState: Codable, Equatable {
|
||||
if self.sessionId.utf8.count > 128 { throw ContractValidationError(field: "session_id", code: "max_length") }
|
||||
if !["starting", "ready", "disconnected", "terminating", "terminated", "cleanup_pending", "failed"].contains(self.state) { throw ContractValidationError(field: "state", code: "invalid_value") }
|
||||
if self.channels.count > 8 { throw ContractValidationError(field: "channels", code: "max_items") }
|
||||
for item in self.channels where item.utf8.count < 1 { throw ContractValidationError(field: "channels", code: "min_item_length") }
|
||||
for item in self.channels where item.utf8.count > 64 { throw ContractValidationError(field: "channels", code: "max_item_length") }
|
||||
}
|
||||
|
||||
public static func decodeJSON(_ data: Data) throws -> Self { try JSONDecoder().decode(Self.self, from: data) }
|
||||
@@ -1868,7 +2074,7 @@ public struct ReauthGrant: Codable, Equatable {
|
||||
if !self.purpose.isEmpty && self.purpose.utf8.count < 1 { throw ContractValidationError(field: "purpose", code: "min_length") }
|
||||
if self.purpose.utf8.count > 64 { throw ContractValidationError(field: "purpose", code: "max_length") }
|
||||
if self.expiresAt.utf8.count > 64 { throw ContractValidationError(field: "expires_at", code: "max_length") }
|
||||
if ISO8601DateFormatter().date(from: self.expiresAt) == nil { throw ContractValidationError(field: "expires_at", code: "invalid_time") }
|
||||
if !validRFC3339UTC(self.expiresAt) { throw ContractValidationError(field: "expires_at", code: "invalid_time") }
|
||||
}
|
||||
|
||||
public static func decodeJSON(_ data: Data) throws -> Self { try JSONDecoder().decode(Self.self, from: data) }
|
||||
@@ -2169,7 +2375,7 @@ public struct SessionAuthority: Codable, Equatable {
|
||||
if self.audience.utf8.count > 256 { throw ContractValidationError(field: "audience", code: "max_length") }
|
||||
if self.reconnectSequence < 0 { throw ContractValidationError(field: "reconnect_sequence", code: "minimum") }
|
||||
if self.expiresAt.utf8.count > 64 { throw ContractValidationError(field: "expires_at", code: "max_length") }
|
||||
if ISO8601DateFormatter().date(from: self.expiresAt) == nil { throw ContractValidationError(field: "expires_at", code: "invalid_time") }
|
||||
if !validRFC3339UTC(self.expiresAt) { throw ContractValidationError(field: "expires_at", code: "invalid_time") }
|
||||
try self.capabilities.validate()
|
||||
if !["apollo"].contains(self.providerProfile) { throw ContractValidationError(field: "provider_profile", code: "invalid_value") }
|
||||
if self.providerIdentity.isEmpty { throw ContractValidationError(field: "provider_identity", code: "required") }
|
||||
@@ -2186,23 +2392,20 @@ public struct SessionRequest: Codable, Equatable {
|
||||
public let deviceKeyId: String
|
||||
public let poolId: String
|
||||
public let idempotencyKey: String
|
||||
public let policySnapshot: AllocationPolicy
|
||||
public let requestedDisplayMode: DisplayMode?
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case clientDeviceId = "client_device_id"
|
||||
case deviceKeyId = "device_key_id"
|
||||
case poolId = "pool_id"
|
||||
case idempotencyKey = "idempotency_key"
|
||||
case policySnapshot = "policy_snapshot"
|
||||
case requestedDisplayMode = "requested_display_mode"
|
||||
}
|
||||
|
||||
public init(clientDeviceId: String, deviceKeyId: String, poolId: String, idempotencyKey: String, policySnapshot: AllocationPolicy, requestedDisplayMode: DisplayMode?) throws {
|
||||
public init(clientDeviceId: String, deviceKeyId: String, poolId: String, idempotencyKey: String, requestedDisplayMode: DisplayMode?) throws {
|
||||
self.clientDeviceId = clientDeviceId
|
||||
self.deviceKeyId = deviceKeyId
|
||||
self.poolId = poolId
|
||||
self.idempotencyKey = idempotencyKey
|
||||
self.policySnapshot = policySnapshot
|
||||
self.requestedDisplayMode = requestedDisplayMode
|
||||
try validate()
|
||||
}
|
||||
@@ -2211,7 +2414,7 @@ public struct SessionRequest: Codable, Equatable {
|
||||
let all = try decoder.container(keyedBy: AnyCodingKey.self)
|
||||
for key in all.allKeys where CodingKeys(stringValue: key.stringValue) == nil { throw ContractValidationError(field: key.stringValue, code: "unknown_field") }
|
||||
let c = try decoder.container(keyedBy: CodingKeys.self)
|
||||
try self.init(clientDeviceId: try c.decode(String.self, forKey: .clientDeviceId), deviceKeyId: try c.decode(String.self, forKey: .deviceKeyId), poolId: try c.decode(String.self, forKey: .poolId), idempotencyKey: try c.decode(String.self, forKey: .idempotencyKey), policySnapshot: try c.decode(AllocationPolicy.self, forKey: .policySnapshot), requestedDisplayMode: try c.contains(.requestedDisplayMode) ? c.decode(DisplayMode.self, forKey: .requestedDisplayMode) : nil)
|
||||
try self.init(clientDeviceId: try c.decode(String.self, forKey: .clientDeviceId), deviceKeyId: try c.decode(String.self, forKey: .deviceKeyId), poolId: try c.decode(String.self, forKey: .poolId), idempotencyKey: try c.decode(String.self, forKey: .idempotencyKey), requestedDisplayMode: try c.contains(.requestedDisplayMode) ? c.decode(DisplayMode.self, forKey: .requestedDisplayMode) : nil)
|
||||
}
|
||||
|
||||
public func validate() throws {
|
||||
@@ -2227,7 +2430,6 @@ public struct SessionRequest: Codable, Equatable {
|
||||
if self.idempotencyKey.isEmpty { throw ContractValidationError(field: "idempotency_key", code: "required") }
|
||||
if !self.idempotencyKey.isEmpty && self.idempotencyKey.utf8.count < 1 { throw ContractValidationError(field: "idempotency_key", code: "min_length") }
|
||||
if self.idempotencyKey.utf8.count > 256 { throw ContractValidationError(field: "idempotency_key", code: "max_length") }
|
||||
try self.policySnapshot.validate()
|
||||
if let value = self.requestedDisplayMode {
|
||||
try value.validate()
|
||||
}
|
||||
@@ -2372,7 +2574,9 @@ public struct VersionNegotiation: Codable, Equatable {
|
||||
public func validate() throws {
|
||||
if self.supportedVersions.count < 1 { throw ContractValidationError(field: "supported_versions", code: "min_items") }
|
||||
if self.supportedVersions.count > 3 { throw ContractValidationError(field: "supported_versions", code: "max_items") }
|
||||
for item in self.supportedVersions where item.utf8.count > 16 { throw ContractValidationError(field: "supported_versions", code: "max_item_length") }
|
||||
if self.features.count > 64 { throw ContractValidationError(field: "features", code: "max_items") }
|
||||
for item in self.features where item.utf8.count > 64 { throw ContractValidationError(field: "features", code: "max_item_length") }
|
||||
}
|
||||
|
||||
public static func decodeJSON(_ data: Data) throws -> Self { try JSONDecoder().decode(Self.self, from: data) }
|
||||
|
||||
Reference in New Issue
Block a user