feat(protocol): negotiate display and native input
Verify Protocol / module (push) Successful in 1m17s
Verify Protocol / verify (push) Successful in 55s

This commit is contained in:
sechmachine
2026-08-10 23:07:04 +07:00
parent 346bf5fe4d
commit 408d4f9cc3
24 changed files with 707 additions and 60 deletions
+143 -23
View File
@@ -13,7 +13,7 @@ import (
"time"
)
const SchemaSHA256 = "a86cbdaf2cfb884e3d98467968007e731ca55c6f6eb6dbd5cd6b95e062a9b058"
const SchemaSHA256 = "b2bb0a8ac8ef56dbc0e1443eeb5b3028be9e71ec2f5fd8e73928d71b7cd9340c"
const ProtocolVersion = "1.0.0"
const CurrentWireVersion = "1"
const NMinus1WireVersion = "0"
@@ -50,21 +50,23 @@ type AssignedDesktop struct {
}
type BrokerSession struct {
ID string `json:"id"`
PrincipalID string `json:"principal_id"`
PoolID string `json:"pool_id"`
AssignmentID string `json:"assignment_id,omitempty"`
State string `json:"state"`
PolicySnapshot AllocationPolicy `json:"policy_snapshot"`
ReconnectDeadline string `json:"reconnect_deadline,omitempty"`
Outcome string `json:"outcome,omitempty"`
FailureCode string `json:"failure_code,omitempty"`
CleanupState string `json:"cleanup_state"`
IdempotencyKey string `json:"idempotency_key"`
CorrelationID string `json:"correlation_id"`
RequestedAt string `json:"requested_at"`
EndedAt string `json:"ended_at,omitempty"`
Version int64 `json:"version"`
ID string `json:"id"`
PrincipalID string `json:"principal_id"`
PoolID string `json:"pool_id"`
AssignmentID string `json:"assignment_id,omitempty"`
State string `json:"state"`
PolicySnapshot AllocationPolicy `json:"policy_snapshot"`
ReconnectDeadline string `json:"reconnect_deadline,omitempty"`
Outcome string `json:"outcome,omitempty"`
FailureCode string `json:"failure_code,omitempty"`
CleanupState string `json:"cleanup_state"`
IdempotencyKey string `json:"idempotency_key"`
CorrelationID string `json:"correlation_id"`
RequestedAt string `json:"requested_at"`
EndedAt string `json:"ended_at,omitempty"`
Version int64 `json:"version"`
RequestedDisplayMode *DisplayMode `json:"requested_display_mode,omitempty"`
EffectiveDisplayMode *DisplayMode `json:"effective_display_mode,omitempty"`
}
type CapabilityProfile struct {
@@ -134,6 +136,12 @@ type DeviceRegistrationRequest struct {
PublicKey string `json:"public_key"`
}
type DisplayMode struct {
ResolutionWidth int64 `json:"resolution_width"`
ResolutionHeight int64 `json:"resolution_height"`
Fps int64 `json:"fps"`
}
type EntitledPool struct {
PoolID string `json:"pool_id"`
Name string `json:"name"`
@@ -263,8 +271,9 @@ type ManifestGateway struct {
}
type ManifestProfile struct {
ID string `json:"id"`
Bounds ManifestBounds `json:"bounds"`
ID string `json:"id"`
Bounds ManifestBounds `json:"bounds"`
DisplayMode *DisplayMode `json:"display_mode,omitempty"`
}
type ManifestTunnel struct {
@@ -383,11 +392,12 @@ type SessionAuthority struct {
}
type SessionRequest struct {
ClientDeviceID string `json:"client_device_id"`
DeviceKeyID string `json:"device_key_id"`
PoolID string `json:"pool_id"`
IdempotencyKey string `json:"idempotency_key"`
PolicySnapshot AllocationPolicy `json:"policy_snapshot"`
ClientDeviceID string `json:"client_device_id"`
DeviceKeyID string `json:"device_key_id"`
PoolID string `json:"pool_id"`
IdempotencyKey string `json:"idempotency_key"`
PolicySnapshot AllocationPolicy `json:"policy_snapshot"`
RequestedDisplayMode *DisplayMode `json:"requested_display_mode,omitempty"`
}
type StableError struct {
@@ -767,6 +777,16 @@ func (v BrokerSession) Validate() error {
if v.Version != 0 && v.Version < 1 {
violations = append(violations, FieldViolation{Field: "version", Code: "minimum"})
}
if v.RequestedDisplayMode != nil {
if err := v.RequestedDisplayMode.Validate(); err != nil {
violations = append(violations, FieldViolation{Field: "requested_display_mode", Code: "invalid_object"})
}
}
if v.EffectiveDisplayMode != nil {
if err := v.EffectiveDisplayMode.Validate(); err != nil {
violations = append(violations, FieldViolation{Field: "effective_display_mode", Code: "invalid_object"})
}
}
if len(violations) > 0 {
return ValidationError{Violations: violations}
}
@@ -812,6 +832,12 @@ func DecodeBrokerSession(data []byte) (BrokerSession, error) {
if raw, ok := fields["version"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
return value, ValidationError{Violations: []FieldViolation{{Field: "version", Code: "required"}}}
}
if raw, ok := fields["requested_display_mode"]; ok && bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
return value, ValidationError{Violations: []FieldViolation{{Field: "requested_display_mode", Code: "invalid_object"}}}
}
if raw, ok := fields["effective_display_mode"]; ok && bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
return value, ValidationError{Violations: []FieldViolation{{Field: "effective_display_mode", Code: "invalid_object"}}}
}
decoder := json.NewDecoder(bytes.NewReader(data))
decoder.DisallowUnknownFields()
if err := decoder.Decode(&value); err != nil {
@@ -1625,6 +1651,84 @@ func EncodeDeviceRegistrationRequest(value DeviceRegistrationRequest) ([]byte, e
return json.Marshal(value)
}
func (v DisplayMode) Validate() error {
var violations []FieldViolation
if v.ResolutionWidth == 0 {
violations = append(violations, FieldViolation{Field: "resolution_width", Code: "required"})
}
if v.ResolutionWidth != 0 && v.ResolutionWidth < 320 {
violations = append(violations, FieldViolation{Field: "resolution_width", Code: "minimum"})
}
if v.ResolutionWidth > 16384 {
violations = append(violations, FieldViolation{Field: "resolution_width", Code: "maximum"})
}
if v.ResolutionHeight == 0 {
violations = append(violations, FieldViolation{Field: "resolution_height", Code: "required"})
}
if v.ResolutionHeight != 0 && v.ResolutionHeight < 200 {
violations = append(violations, FieldViolation{Field: "resolution_height", Code: "minimum"})
}
if v.ResolutionHeight > 8640 {
violations = append(violations, FieldViolation{Field: "resolution_height", Code: "maximum"})
}
if v.Fps == 0 {
violations = append(violations, FieldViolation{Field: "fps", Code: "required"})
}
if v.Fps != 0 && v.Fps < 1 {
violations = append(violations, FieldViolation{Field: "fps", Code: "minimum"})
}
if v.Fps > 240 {
violations = append(violations, FieldViolation{Field: "fps", Code: "maximum"})
}
if len(violations) > 0 {
return ValidationError{Violations: violations}
}
return nil
}
func DecodeDisplayMode(data []byte) (DisplayMode, error) {
var value DisplayMode
if len(data) > 1024*1024 {
return value, errors.New("protocol payload exceeds limit")
}
var fields map[string]json.RawMessage
if err := json.Unmarshal(data, &fields); err != nil {
return value, err
}
if raw, ok := fields["fps"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
return value, ValidationError{Violations: []FieldViolation{{Field: "fps", Code: "required"}}}
}
if raw, ok := fields["resolution_height"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
return value, ValidationError{Violations: []FieldViolation{{Field: "resolution_height", Code: "required"}}}
}
if raw, ok := fields["resolution_width"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
return value, ValidationError{Violations: []FieldViolation{{Field: "resolution_width", Code: "required"}}}
}
decoder := json.NewDecoder(bytes.NewReader(data))
decoder.DisallowUnknownFields()
if err := decoder.Decode(&value); err != nil {
return value, err
}
var trailing any
if err := decoder.Decode(&trailing); err != io.EOF {
if err == nil {
return value, errors.New("trailing JSON value")
}
return value, err
}
if err := value.Validate(); err != nil {
return value, err
}
return value, nil
}
func EncodeDisplayMode(value DisplayMode) ([]byte, error) {
if err := value.Validate(); err != nil {
return nil, err
}
return json.Marshal(value)
}
func (v EntitledPool) Validate() error {
var violations []FieldViolation
if v.PoolID == "" {
@@ -3202,6 +3306,11 @@ func (v ManifestProfile) Validate() error {
if err := v.Bounds.Validate(); err != nil {
violations = append(violations, FieldViolation{Field: "bounds", Code: "invalid_object"})
}
if v.DisplayMode != nil {
if err := v.DisplayMode.Validate(); err != nil {
violations = append(violations, FieldViolation{Field: "display_mode", Code: "invalid_object"})
}
}
if len(violations) > 0 {
return ValidationError{Violations: violations}
}
@@ -3223,6 +3332,9 @@ func DecodeManifestProfile(data []byte) (ManifestProfile, error) {
if raw, ok := fields["id"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
return value, ValidationError{Violations: []FieldViolation{{Field: "id", Code: "required"}}}
}
if raw, ok := fields["display_mode"]; ok && bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
return value, ValidationError{Violations: []FieldViolation{{Field: "display_mode", Code: "invalid_object"}}}
}
decoder := json.NewDecoder(bytes.NewReader(data))
decoder.DisallowUnknownFields()
if err := decoder.Decode(&value); err != nil {
@@ -4655,6 +4767,11 @@ func (v SessionRequest) Validate() error {
if err := v.PolicySnapshot.Validate(); err != nil {
violations = append(violations, FieldViolation{Field: "policy_snapshot", Code: "invalid_object"})
}
if v.RequestedDisplayMode != nil {
if err := v.RequestedDisplayMode.Validate(); err != nil {
violations = append(violations, FieldViolation{Field: "requested_display_mode", Code: "invalid_object"})
}
}
if len(violations) > 0 {
return ValidationError{Violations: violations}
}
@@ -4685,6 +4802,9 @@ func DecodeSessionRequest(data []byte) (SessionRequest, error) {
if raw, ok := fields["pool_id"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
return value, ValidationError{Violations: []FieldViolation{{Field: "pool_id", Code: "required"}}}
}
if raw, ok := fields["requested_display_mode"]; ok && bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
return value, ValidationError{Violations: []FieldViolation{{Field: "requested_display_mode", Code: "invalid_object"}}}
}
decoder := json.NewDecoder(bytes.NewReader(data))
decoder.DisallowUnknownFields()
if err := decoder.Decode(&value); err != nil {
+2 -2
View File
@@ -12,7 +12,7 @@
"2"
]
},
"generator_sha256": "00fdba050eb924a54dd3d63aac0a38560341b675f0de4e3e9631ee895057a9b6",
"generator_sha256": "992235a56d3467313148f86e47931f247591e4c8de737b55ac9c9eee35725fc5",
"protocol_version": "1.0.0",
"schema_sha256": "a86cbdaf2cfb884e3d98467968007e731ca55c6f6eb6dbd5cd6b95e062a9b058"
"schema_sha256": "b2bb0a8ac8ef56dbc0e1443eeb5b3028be9e71ec2f5fd8e73928d71b7cd9340c"
}
+54 -7
View File
@@ -1,6 +1,6 @@
// Code generated by tools/generate.py; DO NOT EDIT.
#![allow(non_snake_case)]
pub const SCHEMA_SHA256: &str = "a86cbdaf2cfb884e3d98467968007e731ca55c6f6eb6dbd5cd6b95e062a9b058";
pub const SCHEMA_SHA256: &str = "b2bb0a8ac8ef56dbc0e1443eeb5b3028be9e71ec2f5fd8e73928d71b7cd9340c";
pub const CURRENT_WIRE_VERSION: &str = "1";
pub const N_MINUS_1_WIRE_VERSION: &str = "0";
pub const N_MINUS_2_WIRE_VERSION: &str = "-1";
@@ -136,11 +136,13 @@ pub struct BrokerSession {
requestedAt: String,
endedAt: Option<String>,
version: i64,
requestedDisplayMode: Option<DisplayMode>,
effectiveDisplayMode: Option<DisplayMode>,
}
impl BrokerSession {
pub fn new(id: String, principalId: String, poolId: String, assignmentId: Option<String>, state: String, policySnapshot: AllocationPolicy, reconnectDeadline: Option<String>, outcome: Option<String>, failureCode: Option<String>, cleanupState: String, idempotencyKey: String, correlationId: String, requestedAt: String, endedAt: Option<String>, version: i64) -> Result<Self, ValidationError> {
let value = Self { id, principalId, poolId, assignmentId, state, policySnapshot, reconnectDeadline, outcome, failureCode, cleanupState, idempotencyKey, correlationId, requestedAt, endedAt, version };
pub fn new(id: String, principalId: String, poolId: String, assignmentId: Option<String>, state: String, policySnapshot: AllocationPolicy, reconnectDeadline: Option<String>, outcome: Option<String>, failureCode: Option<String>, cleanupState: String, idempotencyKey: String, correlationId: String, requestedAt: String, endedAt: Option<String>, version: i64, requestedDisplayMode: Option<DisplayMode>, effectiveDisplayMode: Option<DisplayMode>) -> Result<Self, ValidationError> {
let value = Self { id, principalId, poolId, assignmentId, state, policySnapshot, reconnectDeadline, outcome, failureCode, cleanupState, idempotencyKey, correlationId, requestedAt, endedAt, version, requestedDisplayMode, effectiveDisplayMode };
value.validate()?;
Ok(value)
}
@@ -184,6 +186,12 @@ impl BrokerSession {
if value.len() > 64 { return Err(ValidationError::new("ended_at", "max_length")); }
}
if self.version < 1 { return Err(ValidationError::new("version", "minimum")); }
if let Some(value) = &self.requestedDisplayMode {
value.validate().map_err(|_| ValidationError::new("requested_display_mode", "invalid_object"))?;
}
if let Some(value) = &self.effectiveDisplayMode {
value.validate().map_err(|_| ValidationError::new("effective_display_mode", "invalid_object"))?;
}
Ok(())
}
pub fn id(&self) -> &String { &self.id }
@@ -201,6 +209,8 @@ impl BrokerSession {
pub fn requestedAt(&self) -> &String { &self.requestedAt }
pub fn endedAt(&self) -> &Option<String> { &self.endedAt }
pub fn version(&self) -> &i64 { &self.version }
pub fn requestedDisplayMode(&self) -> &Option<DisplayMode> { &self.requestedDisplayMode }
pub fn effectiveDisplayMode(&self) -> &Option<DisplayMode> { &self.effectiveDisplayMode }
}
#[derive(Debug, Clone, PartialEq, Eq)]
@@ -494,6 +504,33 @@ impl DeviceRegistrationRequest {
pub fn publicKey(&self) -> &String { &self.publicKey }
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DisplayMode {
resolutionWidth: i64,
resolutionHeight: i64,
fps: i64,
}
impl DisplayMode {
pub fn new(resolutionWidth: i64, resolutionHeight: i64, fps: i64) -> Result<Self, ValidationError> {
let value = Self { resolutionWidth, resolutionHeight, fps };
value.validate()?;
Ok(value)
}
pub fn validate(&self) -> Result<(), ValidationError> {
if self.resolutionWidth < 320 { return Err(ValidationError::new("resolution_width", "minimum")); }
if self.resolutionWidth > 16384 { return Err(ValidationError::new("resolution_width", "maximum")); }
if self.resolutionHeight < 200 { return Err(ValidationError::new("resolution_height", "minimum")); }
if self.resolutionHeight > 8640 { return Err(ValidationError::new("resolution_height", "maximum")); }
if self.fps < 1 { return Err(ValidationError::new("fps", "minimum")); }
if self.fps > 240 { return Err(ValidationError::new("fps", "maximum")); }
Ok(())
}
pub fn resolutionWidth(&self) -> &i64 { &self.resolutionWidth }
pub fn resolutionHeight(&self) -> &i64 { &self.resolutionHeight }
pub fn fps(&self) -> &i64 { &self.fps }
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EntitledPool {
poolId: String,
@@ -1080,11 +1117,12 @@ impl ManifestGateway {
pub struct ManifestProfile {
id: String,
bounds: ManifestBounds,
displayMode: Option<DisplayMode>,
}
impl ManifestProfile {
pub fn new(id: String, bounds: ManifestBounds) -> Result<Self, ValidationError> {
let value = Self { id, bounds };
pub fn new(id: String, bounds: ManifestBounds, displayMode: Option<DisplayMode>) -> Result<Self, ValidationError> {
let value = Self { id, bounds, displayMode };
value.validate()?;
Ok(value)
}
@@ -1093,10 +1131,14 @@ impl ManifestProfile {
if !self.id.is_empty() && self.id.len() < 1 { return Err(ValidationError::new("id", "min_length")); }
if self.id.len() > 128 { return Err(ValidationError::new("id", "max_length")); }
self.bounds.validate().map_err(|_| ValidationError::new("bounds", "invalid_object"))?;
if let Some(value) = &self.displayMode {
value.validate().map_err(|_| ValidationError::new("display_mode", "invalid_object"))?;
}
Ok(())
}
pub fn id(&self) -> &String { &self.id }
pub fn bounds(&self) -> &ManifestBounds { &self.bounds }
pub fn displayMode(&self) -> &Option<DisplayMode> { &self.displayMode }
}
#[derive(Debug, Clone, PartialEq, Eq)]
@@ -1613,11 +1655,12 @@ pub struct SessionRequest {
poolId: String,
idempotencyKey: String,
policySnapshot: AllocationPolicy,
requestedDisplayMode: Option<DisplayMode>,
}
impl SessionRequest {
pub fn new(clientDeviceId: String, deviceKeyId: String, poolId: String, idempotencyKey: String, policySnapshot: AllocationPolicy) -> Result<Self, ValidationError> {
let value = Self { clientDeviceId, deviceKeyId, poolId, idempotencyKey, policySnapshot };
pub fn new(clientDeviceId: String, deviceKeyId: String, poolId: String, idempotencyKey: String, policySnapshot: AllocationPolicy, requestedDisplayMode: Option<DisplayMode>) -> Result<Self, ValidationError> {
let value = Self { clientDeviceId, deviceKeyId, poolId, idempotencyKey, policySnapshot, requestedDisplayMode };
value.validate()?;
Ok(value)
}
@@ -1635,6 +1678,9 @@ impl SessionRequest {
if !self.idempotencyKey.is_empty() && self.idempotencyKey.len() < 1 { return Err(ValidationError::new("idempotency_key", "min_length")); }
if self.idempotencyKey.len() > 256 { return Err(ValidationError::new("idempotency_key", "max_length")); }
self.policySnapshot.validate().map_err(|_| ValidationError::new("policy_snapshot", "invalid_object"))?;
if let Some(value) = &self.requestedDisplayMode {
value.validate().map_err(|_| ValidationError::new("requested_display_mode", "invalid_object"))?;
}
Ok(())
}
pub fn clientDeviceId(&self) -> &String { &self.clientDeviceId }
@@ -1642,6 +1688,7 @@ impl SessionRequest {
pub fn poolId(&self) -> &String { &self.poolId }
pub fn idempotencyKey(&self) -> &String { &self.idempotencyKey }
pub fn policySnapshot(&self) -> &AllocationPolicy { &self.policySnapshot }
pub fn requestedDisplayMode(&self) -> &Option<DisplayMode> { &self.requestedDisplayMode }
}
#[derive(Debug, Clone, PartialEq, Eq)]
+68 -7
View File
@@ -1,7 +1,7 @@
// Code generated by tools/generate.py; DO NOT EDIT.
import Foundation
public typealias JSONObject = [String: String]
public let schemaSHA256 = "a86cbdaf2cfb884e3d98467968007e731ca55c6f6eb6dbd5cd6b95e062a9b058"
public let schemaSHA256 = "b2bb0a8ac8ef56dbc0e1443eeb5b3028be9e71ec2f5fd8e73928d71b7cd9340c"
public let currentWireVersion = "1"
public let nMinus1WireVersion = "0"
public let nMinus2WireVersion = "-1"
@@ -148,6 +148,8 @@ public struct BrokerSession: Codable, Equatable {
public let requestedAt: String
public let endedAt: String?
public let version: Int64
public let requestedDisplayMode: DisplayMode?
public let effectiveDisplayMode: DisplayMode?
enum CodingKeys: String, CodingKey {
case id = "id"
case principalId = "principal_id"
@@ -164,9 +166,11 @@ public struct BrokerSession: Codable, Equatable {
case requestedAt = "requested_at"
case endedAt = "ended_at"
case version = "version"
case requestedDisplayMode = "requested_display_mode"
case effectiveDisplayMode = "effective_display_mode"
}
public init(id: String, principalId: String, poolId: String, assignmentId: String?, state: String, policySnapshot: AllocationPolicy, reconnectDeadline: String?, outcome: String?, failureCode: String?, cleanupState: String, idempotencyKey: String, correlationId: String, requestedAt: String, endedAt: String?, version: Int64) throws {
public init(id: String, principalId: String, poolId: String, assignmentId: String?, state: String, policySnapshot: AllocationPolicy, reconnectDeadline: String?, outcome: String?, failureCode: String?, cleanupState: String, idempotencyKey: String, correlationId: String, requestedAt: String, endedAt: String?, version: Int64, requestedDisplayMode: DisplayMode?, effectiveDisplayMode: DisplayMode?) throws {
self.id = id
self.principalId = principalId
self.poolId = poolId
@@ -182,6 +186,8 @@ public struct BrokerSession: Codable, Equatable {
self.requestedAt = requestedAt
self.endedAt = endedAt
self.version = version
self.requestedDisplayMode = requestedDisplayMode
self.effectiveDisplayMode = effectiveDisplayMode
try validate()
}
@@ -189,7 +195,7 @@ public struct BrokerSession: 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(id: try c.decode(String.self, forKey: .id), principalId: try c.decode(String.self, forKey: .principalId), poolId: try c.decode(String.self, forKey: .poolId), assignmentId: try c.decodeIfPresent(String.self, forKey: .assignmentId), state: try c.decode(String.self, forKey: .state), policySnapshot: try c.decode(AllocationPolicy.self, forKey: .policySnapshot), reconnectDeadline: try c.decodeIfPresent(String.self, forKey: .reconnectDeadline), outcome: try c.decodeIfPresent(String.self, forKey: .outcome), failureCode: try c.decodeIfPresent(String.self, forKey: .failureCode), cleanupState: try c.decode(String.self, forKey: .cleanupState), idempotencyKey: try c.decode(String.self, forKey: .idempotencyKey), correlationId: try c.decode(String.self, forKey: .correlationId), requestedAt: try c.decode(String.self, forKey: .requestedAt), endedAt: try c.decodeIfPresent(String.self, forKey: .endedAt), version: try c.decode(Int64.self, forKey: .version))
try self.init(id: try c.decode(String.self, forKey: .id), principalId: try c.decode(String.self, forKey: .principalId), poolId: try c.decode(String.self, forKey: .poolId), assignmentId: try c.decodeIfPresent(String.self, forKey: .assignmentId), state: try c.decode(String.self, forKey: .state), policySnapshot: try c.decode(AllocationPolicy.self, forKey: .policySnapshot), reconnectDeadline: try c.decodeIfPresent(String.self, forKey: .reconnectDeadline), outcome: try c.decodeIfPresent(String.self, forKey: .outcome), failureCode: try c.decodeIfPresent(String.self, forKey: .failureCode), cleanupState: try c.decode(String.self, forKey: .cleanupState), idempotencyKey: try c.decode(String.self, forKey: .idempotencyKey), correlationId: try c.decode(String.self, forKey: .correlationId), requestedAt: try c.decode(String.self, forKey: .requestedAt), endedAt: try c.decodeIfPresent(String.self, forKey: .endedAt), version: try c.decode(Int64.self, forKey: .version), requestedDisplayMode: try c.contains(.requestedDisplayMode) ? c.decode(DisplayMode.self, forKey: .requestedDisplayMode) : nil, effectiveDisplayMode: try c.contains(.effectiveDisplayMode) ? c.decode(DisplayMode.self, forKey: .effectiveDisplayMode) : nil)
}
public func validate() throws {
@@ -235,6 +241,12 @@ public struct BrokerSession: Codable, Equatable {
if ISO8601DateFormatter().date(from: value) == nil { throw ContractValidationError(field: "ended_at", code: "invalid_time") }
}
if self.version < 1 { throw ContractValidationError(field: "version", code: "minimum") }
if let value = self.requestedDisplayMode {
try value.validate()
}
if let value = self.effectiveDisplayMode {
try value.validate()
}
}
public static func decodeJSON(_ data: Data) throws -> Self { try JSONDecoder().decode(Self.self, from: data) }
@@ -632,6 +644,43 @@ public struct DeviceRegistrationRequest: Codable, Equatable {
public func encodeJSON() throws -> Data { try validate(); return try JSONEncoder().encode(self) }
}
public struct DisplayMode: Codable, Equatable {
public let resolutionWidth: Int64
public let resolutionHeight: Int64
public let fps: Int64
enum CodingKeys: String, CodingKey {
case resolutionWidth = "resolution_width"
case resolutionHeight = "resolution_height"
case fps = "fps"
}
public init(resolutionWidth: Int64, resolutionHeight: Int64, fps: Int64) throws {
self.resolutionWidth = resolutionWidth
self.resolutionHeight = resolutionHeight
self.fps = fps
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(resolutionWidth: try c.decode(Int64.self, forKey: .resolutionWidth), resolutionHeight: try c.decode(Int64.self, forKey: .resolutionHeight), fps: try c.decode(Int64.self, forKey: .fps))
}
public func validate() throws {
if self.resolutionWidth < 320 { throw ContractValidationError(field: "resolution_width", code: "minimum") }
if self.resolutionWidth > 16384 { throw ContractValidationError(field: "resolution_width", code: "maximum") }
if self.resolutionHeight < 200 { throw ContractValidationError(field: "resolution_height", code: "minimum") }
if self.resolutionHeight > 8640 { throw ContractValidationError(field: "resolution_height", code: "maximum") }
if self.fps < 1 { throw ContractValidationError(field: "fps", code: "minimum") }
if self.fps > 240 { throw ContractValidationError(field: "fps", code: "maximum") }
}
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 EntitledPool: Codable, Equatable {
public let poolId: String
public let name: String
@@ -1414,14 +1463,17 @@ public struct ManifestGateway: Codable, Equatable {
public struct ManifestProfile: Codable, Equatable {
public let id: String
public let bounds: ManifestBounds
public let displayMode: DisplayMode?
enum CodingKeys: String, CodingKey {
case id = "id"
case bounds = "bounds"
case displayMode = "display_mode"
}
public init(id: String, bounds: ManifestBounds) throws {
public init(id: String, bounds: ManifestBounds, displayMode: DisplayMode?) throws {
self.id = id
self.bounds = bounds
self.displayMode = displayMode
try validate()
}
@@ -1429,7 +1481,7 @@ public struct ManifestProfile: 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(id: try c.decode(String.self, forKey: .id), bounds: try c.decode(ManifestBounds.self, forKey: .bounds))
try self.init(id: try c.decode(String.self, forKey: .id), bounds: try c.decode(ManifestBounds.self, forKey: .bounds), displayMode: try c.contains(.displayMode) ? c.decode(DisplayMode.self, forKey: .displayMode) : nil)
}
public func validate() throws {
@@ -1437,6 +1489,9 @@ public struct ManifestProfile: Codable, Equatable {
if !self.id.isEmpty && self.id.utf8.count < 1 { throw ContractValidationError(field: "id", code: "min_length") }
if self.id.utf8.count > 128 { throw ContractValidationError(field: "id", code: "max_length") }
try self.bounds.validate()
if let value = self.displayMode {
try value.validate()
}
}
public static func decodeJSON(_ data: Data) throws -> Self { try JSONDecoder().decode(Self.self, from: data) }
@@ -2132,20 +2187,23 @@ public struct SessionRequest: Codable, Equatable {
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) throws {
public init(clientDeviceId: String, deviceKeyId: String, poolId: String, idempotencyKey: String, policySnapshot: AllocationPolicy, requestedDisplayMode: DisplayMode?) throws {
self.clientDeviceId = clientDeviceId
self.deviceKeyId = deviceKeyId
self.poolId = poolId
self.idempotencyKey = idempotencyKey
self.policySnapshot = policySnapshot
self.requestedDisplayMode = requestedDisplayMode
try validate()
}
@@ -2153,7 +2211,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))
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)
}
public func validate() throws {
@@ -2170,6 +2228,9 @@ public struct SessionRequest: Codable, Equatable {
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()
}
}
public static func decodeJSON(_ data: Data) throws -> Self { try JSONDecoder().decode(Self.self, from: data) }