fix(protocol): fence gateway work recovery

This commit is contained in:
sechmachine
2026-08-13 07:43:42 +07:00
parent 8eacc4fda9
commit 6e18bc9ee6
21 changed files with 572 additions and 146 deletions
+71 -27
View File
@@ -14,7 +14,7 @@ import (
"time"
)
const SchemaSHA256 = "614fa11dd1f49b8e10bf21b8c10eadbc1468d16bfcc6ed0a26d29671a5e61300"
const SchemaSHA256 = "8c1ed430127cf5774a919f14ff4770b2509d322c8518bf30c412e1e89eeffced"
const ProtocolVersion = "1.0.0"
const CurrentWireVersion = "2"
const NMinus1WireVersion = "1"
@@ -330,8 +330,9 @@ type GatewayQualityAck struct {
ReconnectSequence int64 `json:"reconnect_sequence"`
OperationID string `json:"operation_id"`
Revision int64 `json:"revision"`
LeaseGeneration int64 `json:"lease_generation"`
Outcome string `json:"outcome"`
CurrentAppliedRevision int64 `json:"current_applied_revision"`
CurrentAppliedRevision *int64 `json:"current_applied_revision,omitempty"`
FailureCode string `json:"failure_code,omitempty"`
}
@@ -342,6 +343,7 @@ type GatewayQualityWork struct {
ReconnectSequence int64 `json:"reconnect_sequence"`
OperationID string `json:"operation_id"`
Revision int64 `json:"revision"`
LeaseGeneration int64 `json:"lease_generation"`
LeaseExpiresAt string `json:"lease_expires_at"`
SelectedDescriptor SelectedSessionDescriptor `json:"selected_descriptor"`
CurrentAppliedRevision *int64 `json:"current_applied_revision,omitempty"`
@@ -352,8 +354,10 @@ type GatewayQualityWorkRequest struct {
SessionID string `json:"session_id"`
GatewayID string `json:"gateway_id"`
ReconnectSequence int64 `json:"reconnect_sequence"`
OperationID string `json:"operation_id"`
Revision int64 `json:"revision"`
Acquisition string `json:"acquisition"`
OperationID string `json:"operation_id,omitempty"`
Revision *int64 `json:"revision,omitempty"`
LeaseGeneration *int64 `json:"lease_generation,omitempty"`
CurrentAppliedRevision *int64 `json:"current_applied_revision,omitempty"`
}
@@ -397,7 +401,8 @@ type GatewayStopWorkRequest struct {
SessionID string `json:"session_id"`
GatewayID string `json:"gateway_id"`
ReconnectSequence int64 `json:"reconnect_sequence"`
OperationID string `json:"operation_id"`
Acquisition string `json:"acquisition"`
OperationID string `json:"operation_id,omitempty"`
}
type GatewayTelemetry struct {
@@ -3487,13 +3492,19 @@ func (v GatewayQualityAck) Validate() error {
if v.Revision != 0 && v.Revision < 1 {
violations = append(violations, FieldViolation{Field: "revision", Code: "minimum"})
}
if v.LeaseGeneration == 0 {
violations = append(violations, FieldViolation{Field: "lease_generation", Code: "required"})
}
if v.LeaseGeneration != 0 && v.LeaseGeneration < 1 {
violations = append(violations, FieldViolation{Field: "lease_generation", Code: "minimum"})
}
if v.Outcome == "" {
violations = append(violations, FieldViolation{Field: "outcome", Code: "required"})
}
if v.Outcome != "" && !(v.Outcome == "applied" || v.Outcome == "not_applied" || v.Outcome == "uncertain") {
if v.Outcome != "" && !(v.Outcome == "applied" || v.Outcome == "proven_prior" || v.Outcome == "unknown") {
violations = append(violations, FieldViolation{Field: "outcome", Code: "invalid_value"})
}
if v.CurrentAppliedRevision != 0 && v.CurrentAppliedRevision < 0 {
if v.CurrentAppliedRevision != nil && *v.CurrentAppliedRevision != 0 && *v.CurrentAppliedRevision < 0 {
violations = append(violations, FieldViolation{Field: "current_applied_revision", Code: "minimum"})
}
if len(v.FailureCode) < 1 && v.FailureCode != "" {
@@ -3502,6 +3513,15 @@ func (v GatewayQualityAck) Validate() error {
if len(v.FailureCode) > 128 {
violations = append(violations, FieldViolation{Field: "failure_code", Code: "max_length"})
}
if v.Outcome == "applied" && (v.CurrentAppliedRevision == nil || *v.CurrentAppliedRevision != v.Revision) {
violations = append(violations, FieldViolation{Field: "current_applied_revision", Code: "invalid_tagged_value"})
}
if v.Outcome == "proven_prior" && (v.CurrentAppliedRevision == nil || *v.CurrentAppliedRevision >= v.Revision) {
violations = append(violations, FieldViolation{Field: "current_applied_revision", Code: "invalid_tagged_value"})
}
if v.Outcome == "unknown" && v.CurrentAppliedRevision != nil {
violations = append(violations, FieldViolation{Field: "current_applied_revision", Code: "invalid_tagged_value"})
}
if len(violations) > 0 {
return ValidationError{Violations: violations}
}
@@ -3520,12 +3540,12 @@ func DecodeGatewayQualityAck(data []byte) (GatewayQualityAck, error) {
if err := json.Unmarshal(data, &fields); err != nil {
return value, err
}
if raw, ok := fields["current_applied_revision"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
return value, ValidationError{Violations: []FieldViolation{{Field: "current_applied_revision", Code: "required"}}}
}
if raw, ok := fields["gateway_id"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
return value, ValidationError{Violations: []FieldViolation{{Field: "gateway_id", Code: "required"}}}
}
if raw, ok := fields["lease_generation"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
return value, ValidationError{Violations: []FieldViolation{{Field: "lease_generation", Code: "required"}}}
}
if raw, ok := fields["operation_id"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
return value, ValidationError{Violations: []FieldViolation{{Field: "operation_id", Code: "required"}}}
}
@@ -3616,6 +3636,12 @@ func (v GatewayQualityWork) Validate() error {
if v.Revision != 0 && v.Revision < 1 {
violations = append(violations, FieldViolation{Field: "revision", Code: "minimum"})
}
if v.LeaseGeneration == 0 {
violations = append(violations, FieldViolation{Field: "lease_generation", Code: "required"})
}
if v.LeaseGeneration != 0 && v.LeaseGeneration < 1 {
violations = append(violations, FieldViolation{Field: "lease_generation", Code: "minimum"})
}
if v.LeaseExpiresAt == "" {
violations = append(violations, FieldViolation{Field: "lease_expires_at", Code: "required"})
}
@@ -3660,6 +3686,9 @@ func DecodeGatewayQualityWork(data []byte) (GatewayQualityWork, error) {
if raw, ok := fields["lease_expires_at"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
return value, ValidationError{Violations: []FieldViolation{{Field: "lease_expires_at", Code: "required"}}}
}
if raw, ok := fields["lease_generation"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
return value, ValidationError{Violations: []FieldViolation{{Field: "lease_generation", Code: "required"}}}
}
if raw, ok := fields["operation_id"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
return value, ValidationError{Violations: []FieldViolation{{Field: "operation_id", Code: "required"}}}
}
@@ -3732,8 +3761,11 @@ func (v GatewayQualityWorkRequest) Validate() error {
if v.ReconnectSequence != 0 && v.ReconnectSequence < 0 {
violations = append(violations, FieldViolation{Field: "reconnect_sequence", Code: "minimum"})
}
if v.OperationID == "" {
violations = append(violations, FieldViolation{Field: "operation_id", Code: "required"})
if v.Acquisition == "" {
violations = append(violations, FieldViolation{Field: "acquisition", Code: "required"})
}
if v.Acquisition != "" && !(v.Acquisition == "poll" || v.Acquisition == "prompt" || v.Acquisition == "observation") {
violations = append(violations, FieldViolation{Field: "acquisition", Code: "invalid_value"})
}
if len(v.OperationID) < 36 && v.OperationID != "" {
violations = append(violations, FieldViolation{Field: "operation_id", Code: "min_length"})
@@ -3744,15 +3776,24 @@ func (v GatewayQualityWorkRequest) Validate() error {
if v.OperationID != "" && !validCanonicalUUID(v.OperationID) {
violations = append(violations, FieldViolation{Field: "operation_id", Code: "invalid_uuid"})
}
if v.Revision == 0 {
violations = append(violations, FieldViolation{Field: "revision", Code: "required"})
}
if v.Revision != 0 && v.Revision < 1 {
if v.Revision != nil && *v.Revision != 0 && *v.Revision < 1 {
violations = append(violations, FieldViolation{Field: "revision", Code: "minimum"})
}
if v.LeaseGeneration != nil && *v.LeaseGeneration != 0 && *v.LeaseGeneration < 1 {
violations = append(violations, FieldViolation{Field: "lease_generation", Code: "minimum"})
}
if v.CurrentAppliedRevision != nil && *v.CurrentAppliedRevision != 0 && *v.CurrentAppliedRevision < 0 {
violations = append(violations, FieldViolation{Field: "current_applied_revision", Code: "minimum"})
}
if v.Acquisition == "poll" && (v.OperationID != "" || v.Revision != nil || v.LeaseGeneration != nil || v.CurrentAppliedRevision != nil) {
violations = append(violations, FieldViolation{Field: "acquisition", Code: "invalid_tagged_value"})
}
if v.Acquisition == "prompt" && (v.OperationID == "" || v.Revision == nil || v.LeaseGeneration != nil || v.CurrentAppliedRevision != nil) {
violations = append(violations, FieldViolation{Field: "acquisition", Code: "invalid_tagged_value"})
}
if v.Acquisition == "observation" && (v.OperationID == "" || v.Revision == nil || v.LeaseGeneration == nil || v.CurrentAppliedRevision == nil) {
violations = append(violations, FieldViolation{Field: "acquisition", Code: "invalid_tagged_value"})
}
if len(violations) > 0 {
return ValidationError{Violations: violations}
}
@@ -3771,18 +3812,15 @@ func DecodeGatewayQualityWorkRequest(data []byte) (GatewayQualityWorkRequest, er
if err := json.Unmarshal(data, &fields); err != nil {
return value, err
}
if raw, ok := fields["acquisition"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
return value, ValidationError{Violations: []FieldViolation{{Field: "acquisition", Code: "required"}}}
}
if raw, ok := fields["gateway_id"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
return value, ValidationError{Violations: []FieldViolation{{Field: "gateway_id", Code: "required"}}}
}
if raw, ok := fields["operation_id"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
return value, ValidationError{Violations: []FieldViolation{{Field: "operation_id", Code: "required"}}}
}
if raw, ok := fields["reconnect_sequence"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
return value, ValidationError{Violations: []FieldViolation{{Field: "reconnect_sequence", Code: "required"}}}
}
if raw, ok := fields["revision"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
return value, ValidationError{Violations: []FieldViolation{{Field: "revision", Code: "required"}}}
}
if raw, ok := fields["session_id"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
return value, ValidationError{Violations: []FieldViolation{{Field: "session_id", Code: "required"}}}
}
@@ -4273,8 +4311,11 @@ func (v GatewayStopWorkRequest) Validate() error {
if v.ReconnectSequence != 0 && v.ReconnectSequence < 0 {
violations = append(violations, FieldViolation{Field: "reconnect_sequence", Code: "minimum"})
}
if v.OperationID == "" {
violations = append(violations, FieldViolation{Field: "operation_id", Code: "required"})
if v.Acquisition == "" {
violations = append(violations, FieldViolation{Field: "acquisition", Code: "required"})
}
if v.Acquisition != "" && !(v.Acquisition == "poll" || v.Acquisition == "prompt") {
violations = append(violations, FieldViolation{Field: "acquisition", Code: "invalid_value"})
}
if len(v.OperationID) < 36 && v.OperationID != "" {
violations = append(violations, FieldViolation{Field: "operation_id", Code: "min_length"})
@@ -4285,6 +4326,9 @@ func (v GatewayStopWorkRequest) Validate() error {
if v.OperationID != "" && !validCanonicalUUID(v.OperationID) {
violations = append(violations, FieldViolation{Field: "operation_id", Code: "invalid_uuid"})
}
if v.Acquisition == "poll" && v.OperationID != "" || v.Acquisition == "prompt" && v.OperationID == "" {
violations = append(violations, FieldViolation{Field: "acquisition", Code: "invalid_tagged_value"})
}
if len(violations) > 0 {
return ValidationError{Violations: violations}
}
@@ -4303,12 +4347,12 @@ func DecodeGatewayStopWorkRequest(data []byte) (GatewayStopWorkRequest, error) {
if err := json.Unmarshal(data, &fields); err != nil {
return value, err
}
if raw, ok := fields["acquisition"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
return value, ValidationError{Violations: []FieldViolation{{Field: "acquisition", Code: "required"}}}
}
if raw, ok := fields["gateway_id"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
return value, ValidationError{Violations: []FieldViolation{{Field: "gateway_id", Code: "required"}}}
}
if raw, ok := fields["operation_id"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
return value, ValidationError{Violations: []FieldViolation{{Field: "operation_id", Code: "required"}}}
}
if raw, ok := fields["reconnect_sequence"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
return value, ValidationError{Violations: []FieldViolation{{Field: "reconnect_sequence", Code: "required"}}}
}
+2 -2
View File
@@ -12,7 +12,7 @@
"3"
]
},
"generator_sha256": "9f6e95fac0f3c8389d8111e6ba0a03b48e155b71e345a11640ef824b8b3a4910",
"generator_sha256": "c5756780f59a82d8f56762e52f58e22a4d6aca6648157f45e5bad6559b9deeec",
"protocol_version": "1.0.0",
"schema_sha256": "614fa11dd1f49b8e10bf21b8c10eadbc1468d16bfcc6ed0a26d29671a5e61300"
"schema_sha256": "8c1ed430127cf5774a919f14ff4770b2509d322c8518bf30c412e1e89eeffced"
}
Binary file not shown.
+58 -28
View File
@@ -1,6 +1,6 @@
// Code generated by tools/generate.py; DO NOT EDIT.
#![allow(non_snake_case)]
pub const SCHEMA_SHA256: &str = "614fa11dd1f49b8e10bf21b8c10eadbc1468d16bfcc6ed0a26d29671a5e61300";
pub const SCHEMA_SHA256: &str = "8c1ed430127cf5774a919f14ff4770b2509d322c8518bf30c412e1e89eeffced";
pub const CURRENT_WIRE_VERSION: &str = "2";
pub const N_MINUS_1_WIRE_VERSION: &str = "1";
pub const N_MINUS_2_WIRE_VERSION: &str = "0";
@@ -1055,14 +1055,15 @@ pub struct GatewayQualityAck {
reconnectSequence: i64,
operationId: String,
revision: i64,
leaseGeneration: i64,
outcome: String,
currentAppliedRevision: i64,
currentAppliedRevision: Option<i64>,
failureCode: Option<String>,
}
impl GatewayQualityAck {
pub fn new(version: String, sessionId: String, gatewayId: String, reconnectSequence: i64, operationId: String, revision: i64, outcome: String, currentAppliedRevision: i64, failureCode: Option<String>) -> Result<Self, ValidationError> {
let value = Self { version, sessionId, gatewayId, reconnectSequence, operationId, revision, outcome, currentAppliedRevision, failureCode };
pub fn new(version: String, sessionId: String, gatewayId: String, reconnectSequence: i64, operationId: String, revision: i64, leaseGeneration: i64, outcome: String, currentAppliedRevision: Option<i64>, failureCode: Option<String>) -> Result<Self, ValidationError> {
let value = Self { version, sessionId, gatewayId, reconnectSequence, operationId, revision, leaseGeneration, outcome, currentAppliedRevision, failureCode };
value.validate()?;
Ok(value)
}
@@ -1080,12 +1081,18 @@ impl GatewayQualityAck {
if self.operationId.len() > 36 { return Err(ValidationError::new("operation_id", "max_length")); }
if !valid_canonical_uuid(self.operationId.as_str()) { return Err(ValidationError::new("operation_id", "invalid_uuid")); }
if self.revision < 1 { return Err(ValidationError::new("revision", "minimum")); }
if self.outcome != "applied" && self.outcome != "not_applied" && self.outcome != "uncertain" { return Err(ValidationError::new("outcome", "invalid_value")); }
if self.currentAppliedRevision < 0 { return Err(ValidationError::new("current_applied_revision", "minimum")); }
if self.leaseGeneration < 1 { return Err(ValidationError::new("lease_generation", "minimum")); }
if self.outcome != "applied" && self.outcome != "proven_prior" && self.outcome != "unknown" { return Err(ValidationError::new("outcome", "invalid_value")); }
if let Some(value) = &self.currentAppliedRevision {
if *value < 0 { return Err(ValidationError::new("current_applied_revision", "minimum")); }
}
if let Some(value) = &self.failureCode {
if !value.is_empty() && value.len() < 1 { return Err(ValidationError::new("failure_code", "min_length")); }
if value.len() > 128 { return Err(ValidationError::new("failure_code", "max_length")); }
}
if self.outcome == "applied" && self.currentAppliedRevision != Some(self.revision) { return Err(ValidationError::new("current_applied_revision", "invalid_tagged_value")); }
if self.outcome == "proven_prior" && self.currentAppliedRevision.map_or(true, |current| current >= self.revision) { return Err(ValidationError::new("current_applied_revision", "invalid_tagged_value")); }
if self.outcome == "unknown" && self.currentAppliedRevision.is_some() { return Err(ValidationError::new("current_applied_revision", "invalid_tagged_value")); }
Ok(())
}
pub fn version(&self) -> &String { &self.version }
@@ -1094,8 +1101,9 @@ impl GatewayQualityAck {
pub fn reconnectSequence(&self) -> &i64 { &self.reconnectSequence }
pub fn operationId(&self) -> &String { &self.operationId }
pub fn revision(&self) -> &i64 { &self.revision }
pub fn leaseGeneration(&self) -> &i64 { &self.leaseGeneration }
pub fn outcome(&self) -> &String { &self.outcome }
pub fn currentAppliedRevision(&self) -> &i64 { &self.currentAppliedRevision }
pub fn currentAppliedRevision(&self) -> &Option<i64> { &self.currentAppliedRevision }
pub fn failureCode(&self) -> &Option<String> { &self.failureCode }
}
@@ -1107,14 +1115,15 @@ pub struct GatewayQualityWork {
reconnectSequence: i64,
operationId: String,
revision: i64,
leaseGeneration: i64,
leaseExpiresAt: String,
selectedDescriptor: SelectedSessionDescriptor,
currentAppliedRevision: Option<i64>,
}
impl GatewayQualityWork {
pub fn new(version: String, sessionId: String, gatewayId: String, reconnectSequence: i64, operationId: String, revision: i64, leaseExpiresAt: String, selectedDescriptor: SelectedSessionDescriptor, currentAppliedRevision: Option<i64>) -> Result<Self, ValidationError> {
let value = Self { version, sessionId, gatewayId, reconnectSequence, operationId, revision, leaseExpiresAt, selectedDescriptor, currentAppliedRevision };
pub fn new(version: String, sessionId: String, gatewayId: String, reconnectSequence: i64, operationId: String, revision: i64, leaseGeneration: i64, leaseExpiresAt: String, selectedDescriptor: SelectedSessionDescriptor, currentAppliedRevision: Option<i64>) -> Result<Self, ValidationError> {
let value = Self { version, sessionId, gatewayId, reconnectSequence, operationId, revision, leaseGeneration, leaseExpiresAt, selectedDescriptor, currentAppliedRevision };
value.validate()?;
Ok(value)
}
@@ -1132,6 +1141,7 @@ impl GatewayQualityWork {
if self.operationId.len() > 36 { return Err(ValidationError::new("operation_id", "max_length")); }
if !valid_canonical_uuid(self.operationId.as_str()) { return Err(ValidationError::new("operation_id", "invalid_uuid")); }
if self.revision < 1 { return Err(ValidationError::new("revision", "minimum")); }
if self.leaseGeneration < 1 { return Err(ValidationError::new("lease_generation", "minimum")); }
if self.leaseExpiresAt.len() > 64 { return Err(ValidationError::new("lease_expires_at", "max_length")); }
if !valid_rfc3339_utc(self.leaseExpiresAt.as_str()) { return Err(ValidationError::new("lease_expires_at", "invalid_time")); }
self.selectedDescriptor.validate().map_err(|_| ValidationError::new("selected_descriptor", "invalid_object"))?;
@@ -1146,6 +1156,7 @@ impl GatewayQualityWork {
pub fn reconnectSequence(&self) -> &i64 { &self.reconnectSequence }
pub fn operationId(&self) -> &String { &self.operationId }
pub fn revision(&self) -> &i64 { &self.revision }
pub fn leaseGeneration(&self) -> &i64 { &self.leaseGeneration }
pub fn leaseExpiresAt(&self) -> &String { &self.leaseExpiresAt }
pub fn selectedDescriptor(&self) -> &SelectedSessionDescriptor { &self.selectedDescriptor }
pub fn currentAppliedRevision(&self) -> &Option<i64> { &self.currentAppliedRevision }
@@ -1157,14 +1168,16 @@ pub struct GatewayQualityWorkRequest {
sessionId: String,
gatewayId: String,
reconnectSequence: i64,
operationId: String,
revision: i64,
acquisition: String,
operationId: Option<String>,
revision: Option<i64>,
leaseGeneration: Option<i64>,
currentAppliedRevision: Option<i64>,
}
impl GatewayQualityWorkRequest {
pub fn new(version: String, sessionId: String, gatewayId: String, reconnectSequence: i64, operationId: String, revision: i64, currentAppliedRevision: Option<i64>) -> Result<Self, ValidationError> {
let value = Self { version, sessionId, gatewayId, reconnectSequence, operationId, revision, currentAppliedRevision };
pub fn new(version: String, sessionId: String, gatewayId: String, reconnectSequence: i64, acquisition: String, operationId: Option<String>, revision: Option<i64>, leaseGeneration: Option<i64>, currentAppliedRevision: Option<i64>) -> Result<Self, ValidationError> {
let value = Self { version, sessionId, gatewayId, reconnectSequence, acquisition, operationId, revision, leaseGeneration, currentAppliedRevision };
value.validate()?;
Ok(value)
}
@@ -1177,22 +1190,34 @@ impl GatewayQualityWorkRequest {
if !self.gatewayId.is_empty() && self.gatewayId.len() < 1 { return Err(ValidationError::new("gateway_id", "min_length")); }
if self.gatewayId.len() > 128 { return Err(ValidationError::new("gateway_id", "max_length")); }
if self.reconnectSequence < 0 { return Err(ValidationError::new("reconnect_sequence", "minimum")); }
if self.operationId.is_empty() { return Err(ValidationError::new("operation_id", "required")); }
if !self.operationId.is_empty() && self.operationId.len() < 36 { return Err(ValidationError::new("operation_id", "min_length")); }
if self.operationId.len() > 36 { return Err(ValidationError::new("operation_id", "max_length")); }
if !valid_canonical_uuid(self.operationId.as_str()) { return Err(ValidationError::new("operation_id", "invalid_uuid")); }
if self.revision < 1 { return Err(ValidationError::new("revision", "minimum")); }
if self.acquisition != "poll" && self.acquisition != "prompt" && self.acquisition != "observation" { return Err(ValidationError::new("acquisition", "invalid_value")); }
if let Some(value) = &self.operationId {
if !value.is_empty() && value.len() < 36 { return Err(ValidationError::new("operation_id", "min_length")); }
if value.len() > 36 { return Err(ValidationError::new("operation_id", "max_length")); }
if !valid_canonical_uuid(value.as_str()) { return Err(ValidationError::new("operation_id", "invalid_uuid")); }
}
if let Some(value) = &self.revision {
if *value < 1 { return Err(ValidationError::new("revision", "minimum")); }
}
if let Some(value) = &self.leaseGeneration {
if *value < 1 { return Err(ValidationError::new("lease_generation", "minimum")); }
}
if let Some(value) = &self.currentAppliedRevision {
if *value < 0 { return Err(ValidationError::new("current_applied_revision", "minimum")); }
}
if self.acquisition == "poll" && (self.operationId.is_some() || self.revision.is_some() || self.leaseGeneration.is_some() || self.currentAppliedRevision.is_some()) { return Err(ValidationError::new("acquisition", "invalid_tagged_value")); }
if self.acquisition == "prompt" && (self.operationId.is_none() || self.revision.is_none() || self.leaseGeneration.is_some() || self.currentAppliedRevision.is_some()) { return Err(ValidationError::new("acquisition", "invalid_tagged_value")); }
if self.acquisition == "observation" && (self.operationId.is_none() || self.revision.is_none() || self.leaseGeneration.is_none() || self.currentAppliedRevision.is_none()) { return Err(ValidationError::new("acquisition", "invalid_tagged_value")); }
Ok(())
}
pub fn version(&self) -> &String { &self.version }
pub fn sessionId(&self) -> &String { &self.sessionId }
pub fn gatewayId(&self) -> &String { &self.gatewayId }
pub fn reconnectSequence(&self) -> &i64 { &self.reconnectSequence }
pub fn operationId(&self) -> &String { &self.operationId }
pub fn revision(&self) -> &i64 { &self.revision }
pub fn acquisition(&self) -> &String { &self.acquisition }
pub fn operationId(&self) -> &Option<String> { &self.operationId }
pub fn revision(&self) -> &Option<i64> { &self.revision }
pub fn leaseGeneration(&self) -> &Option<i64> { &self.leaseGeneration }
pub fn currentAppliedRevision(&self) -> &Option<i64> { &self.currentAppliedRevision }
}
@@ -1362,12 +1387,13 @@ pub struct GatewayStopWorkRequest {
sessionId: String,
gatewayId: String,
reconnectSequence: i64,
operationId: String,
acquisition: String,
operationId: Option<String>,
}
impl GatewayStopWorkRequest {
pub fn new(version: String, sessionId: String, gatewayId: String, reconnectSequence: i64, operationId: String) -> Result<Self, ValidationError> {
let value = Self { version, sessionId, gatewayId, reconnectSequence, operationId };
pub fn new(version: String, sessionId: String, gatewayId: String, reconnectSequence: i64, acquisition: String, operationId: Option<String>) -> Result<Self, ValidationError> {
let value = Self { version, sessionId, gatewayId, reconnectSequence, acquisition, operationId };
value.validate()?;
Ok(value)
}
@@ -1380,17 +1406,21 @@ impl GatewayStopWorkRequest {
if !self.gatewayId.is_empty() && self.gatewayId.len() < 1 { return Err(ValidationError::new("gateway_id", "min_length")); }
if self.gatewayId.len() > 128 { return Err(ValidationError::new("gateway_id", "max_length")); }
if self.reconnectSequence < 0 { return Err(ValidationError::new("reconnect_sequence", "minimum")); }
if self.operationId.is_empty() { return Err(ValidationError::new("operation_id", "required")); }
if !self.operationId.is_empty() && self.operationId.len() < 36 { return Err(ValidationError::new("operation_id", "min_length")); }
if self.operationId.len() > 36 { return Err(ValidationError::new("operation_id", "max_length")); }
if !valid_canonical_uuid(self.operationId.as_str()) { return Err(ValidationError::new("operation_id", "invalid_uuid")); }
if self.acquisition != "poll" && self.acquisition != "prompt" { return Err(ValidationError::new("acquisition", "invalid_value")); }
if let Some(value) = &self.operationId {
if !value.is_empty() && value.len() < 36 { return Err(ValidationError::new("operation_id", "min_length")); }
if value.len() > 36 { return Err(ValidationError::new("operation_id", "max_length")); }
if !valid_canonical_uuid(value.as_str()) { return Err(ValidationError::new("operation_id", "invalid_uuid")); }
}
if self.acquisition == "poll" && self.operationId.is_some() || self.acquisition == "prompt" && self.operationId.is_none() { return Err(ValidationError::new("acquisition", "invalid_tagged_value")); }
Ok(())
}
pub fn version(&self) -> &String { &self.version }
pub fn sessionId(&self) -> &String { &self.sessionId }
pub fn gatewayId(&self) -> &String { &self.gatewayId }
pub fn reconnectSequence(&self) -> &i64 { &self.reconnectSequence }
pub fn operationId(&self) -> &String { &self.operationId }
pub fn acquisition(&self) -> &String { &self.acquisition }
pub fn operationId(&self) -> &Option<String> { &self.operationId }
}
#[derive(Debug, Clone, PartialEq, Eq)]
+65 -28
View File
@@ -1,13 +1,14 @@
// Code generated by tools/generate.py; DO NOT EDIT.
import Foundation
public typealias JSONObject = [String: String]
public let schemaSHA256 = "614fa11dd1f49b8e10bf21b8c10eadbc1468d16bfcc6ed0a26d29671a5e61300"
public let schemaSHA256 = "8c1ed430127cf5774a919f14ff4770b2509d322c8518bf30c412e1e89eeffced"
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 rejectDuplicateJSONKeys(_ data: Data) throws {
guard data.count <= 1_048_576 else { throw ContractValidationError(field: "json", code: "payload_too_large") }
var index = 0
func skipWhitespace() { while index < data.count && [9, 10, 13, 32].contains(data[index]) { index += 1 } }
func parseString() throws -> String {
@@ -21,7 +22,8 @@ private func rejectDuplicateJSONKeys(_ data: Data) throws {
}
throw ContractValidationError(field: "json", code: "invalid_json")
}
func parseValue() throws {
func parseValue(_ depth: Int) throws {
guard depth <= 64 else { throw ContractValidationError(field: "json", code: "nesting_too_deep") }
skipWhitespace()
guard index < data.count else { throw ContractValidationError(field: "json", code: "invalid_json") }
if data[index] == 123 {
@@ -36,7 +38,7 @@ private func rejectDuplicateJSONKeys(_ data: Data) throws {
skipWhitespace()
guard index < data.count, data[index] == 58 else { throw ContractValidationError(field: "json", code: "invalid_json") }
index += 1
try parseValue()
try parseValue(depth + 1)
skipWhitespace()
guard index < data.count else { throw ContractValidationError(field: "json", code: "invalid_json") }
if data[index] == 125 { index += 1; return }
@@ -49,7 +51,7 @@ private func rejectDuplicateJSONKeys(_ data: Data) throws {
skipWhitespace()
if index < data.count, data[index] == 93 { index += 1; return }
while true {
try parseValue()
try parseValue(depth + 1)
skipWhitespace()
guard index < data.count else { throw ContractValidationError(field: "json", code: "invalid_json") }
if data[index] == 93 { index += 1; return }
@@ -62,7 +64,7 @@ private func rejectDuplicateJSONKeys(_ data: Data) throws {
while index < data.count && ![9, 10, 13, 32, 44, 93, 125].contains(data[index]) { index += 1 }
guard index > start else { throw ContractValidationError(field: "json", code: "invalid_json") }
}
try parseValue()
try parseValue(0)
skipWhitespace()
guard index == data.count else { throw ContractValidationError(field: "json", code: "trailing_json") }
}
@@ -1434,8 +1436,9 @@ public struct GatewayQualityAck: Codable, Equatable {
public let reconnectSequence: Int64
public let operationId: String
public let revision: Int64
public let leaseGeneration: Int64
public let outcome: String
public let currentAppliedRevision: Int64
public let currentAppliedRevision: Int64?
public let failureCode: String?
enum CodingKeys: String, CodingKey {
case version = "version"
@@ -1444,18 +1447,20 @@ public struct GatewayQualityAck: Codable, Equatable {
case reconnectSequence = "reconnect_sequence"
case operationId = "operation_id"
case revision = "revision"
case leaseGeneration = "lease_generation"
case outcome = "outcome"
case currentAppliedRevision = "current_applied_revision"
case failureCode = "failure_code"
}
public init(version: String, sessionId: String, gatewayId: String, reconnectSequence: Int64, operationId: String, revision: Int64, outcome: String, currentAppliedRevision: Int64, failureCode: String?) throws {
public init(version: String, sessionId: String, gatewayId: String, reconnectSequence: Int64, operationId: String, revision: Int64, leaseGeneration: Int64, outcome: String, currentAppliedRevision: Int64?, failureCode: String?) throws {
self.version = version
self.sessionId = sessionId
self.gatewayId = gatewayId
self.reconnectSequence = reconnectSequence
self.operationId = operationId
self.revision = revision
self.leaseGeneration = leaseGeneration
self.outcome = outcome
self.currentAppliedRevision = currentAppliedRevision
self.failureCode = failureCode
@@ -1466,7 +1471,7 @@ public struct GatewayQualityAck: 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(version: try c.decode(String.self, forKey: .version), sessionId: try c.decode(String.self, forKey: .sessionId), gatewayId: try c.decode(String.self, forKey: .gatewayId), reconnectSequence: try c.decode(Int64.self, forKey: .reconnectSequence), operationId: try c.decode(String.self, forKey: .operationId), revision: try c.decode(Int64.self, forKey: .revision), outcome: try c.decode(String.self, forKey: .outcome), currentAppliedRevision: try c.decode(Int64.self, forKey: .currentAppliedRevision), failureCode: try c.decodeIfPresent(String.self, forKey: .failureCode))
try self.init(version: try c.decode(String.self, forKey: .version), sessionId: try c.decode(String.self, forKey: .sessionId), gatewayId: try c.decode(String.self, forKey: .gatewayId), reconnectSequence: try c.decode(Int64.self, forKey: .reconnectSequence), operationId: try c.decode(String.self, forKey: .operationId), revision: try c.decode(Int64.self, forKey: .revision), leaseGeneration: try c.decode(Int64.self, forKey: .leaseGeneration), outcome: try c.decode(String.self, forKey: .outcome), currentAppliedRevision: try c.decodeIfPresent(Int64.self, forKey: .currentAppliedRevision), failureCode: try c.decodeIfPresent(String.self, forKey: .failureCode))
}
public func validate() throws {
@@ -1483,12 +1488,18 @@ public struct GatewayQualityAck: Codable, Equatable {
if self.operationId.utf8.count > 36 { throw ContractValidationError(field: "operation_id", code: "max_length") }
if !validCanonicalUUID(self.operationId) { throw ContractValidationError(field: "operation_id", code: "invalid_uuid") }
if self.revision < 1 { throw ContractValidationError(field: "revision", code: "minimum") }
if !["applied", "not_applied", "uncertain"].contains(self.outcome) { throw ContractValidationError(field: "outcome", code: "invalid_value") }
if self.currentAppliedRevision < 0 { throw ContractValidationError(field: "current_applied_revision", code: "minimum") }
if self.leaseGeneration < 1 { throw ContractValidationError(field: "lease_generation", code: "minimum") }
if !["applied", "proven_prior", "unknown"].contains(self.outcome) { throw ContractValidationError(field: "outcome", code: "invalid_value") }
if let value = self.currentAppliedRevision {
if value < 0 { throw ContractValidationError(field: "current_applied_revision", code: "minimum") }
}
if let value = self.failureCode {
if !value.isEmpty && value.utf8.count < 1 { throw ContractValidationError(field: "failure_code", code: "min_length") }
if value.utf8.count > 128 { throw ContractValidationError(field: "failure_code", code: "max_length") }
}
if outcome == "applied" && currentAppliedRevision != revision { throw ContractValidationError(field: "current_applied_revision", code: "invalid_tagged_value") }
if outcome == "proven_prior" && (currentAppliedRevision == nil || currentAppliedRevision! >= revision) { throw ContractValidationError(field: "current_applied_revision", code: "invalid_tagged_value") }
if outcome == "unknown" && currentAppliedRevision != nil { throw ContractValidationError(field: "current_applied_revision", code: "invalid_tagged_value") }
}
public static func decodeJSON(_ data: Data) throws -> Self { try rejectDuplicateJSONKeys(data); return try JSONDecoder().decode(Self.self, from: data) }
@@ -1502,6 +1513,7 @@ public struct GatewayQualityWork: Codable, Equatable {
public let reconnectSequence: Int64
public let operationId: String
public let revision: Int64
public let leaseGeneration: Int64
public let leaseExpiresAt: String
public let selectedDescriptor: SelectedSessionDescriptor
public let currentAppliedRevision: Int64?
@@ -1512,18 +1524,20 @@ public struct GatewayQualityWork: Codable, Equatable {
case reconnectSequence = "reconnect_sequence"
case operationId = "operation_id"
case revision = "revision"
case leaseGeneration = "lease_generation"
case leaseExpiresAt = "lease_expires_at"
case selectedDescriptor = "selected_descriptor"
case currentAppliedRevision = "current_applied_revision"
}
public init(version: String, sessionId: String, gatewayId: String, reconnectSequence: Int64, operationId: String, revision: Int64, leaseExpiresAt: String, selectedDescriptor: SelectedSessionDescriptor, currentAppliedRevision: Int64?) throws {
public init(version: String, sessionId: String, gatewayId: String, reconnectSequence: Int64, operationId: String, revision: Int64, leaseGeneration: Int64, leaseExpiresAt: String, selectedDescriptor: SelectedSessionDescriptor, currentAppliedRevision: Int64?) throws {
self.version = version
self.sessionId = sessionId
self.gatewayId = gatewayId
self.reconnectSequence = reconnectSequence
self.operationId = operationId
self.revision = revision
self.leaseGeneration = leaseGeneration
self.leaseExpiresAt = leaseExpiresAt
self.selectedDescriptor = selectedDescriptor
self.currentAppliedRevision = currentAppliedRevision
@@ -1534,7 +1548,7 @@ public struct GatewayQualityWork: 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(version: try c.decode(String.self, forKey: .version), sessionId: try c.decode(String.self, forKey: .sessionId), gatewayId: try c.decode(String.self, forKey: .gatewayId), reconnectSequence: try c.decode(Int64.self, forKey: .reconnectSequence), operationId: try c.decode(String.self, forKey: .operationId), revision: try c.decode(Int64.self, forKey: .revision), leaseExpiresAt: try c.decode(String.self, forKey: .leaseExpiresAt), selectedDescriptor: try c.decode(SelectedSessionDescriptor.self, forKey: .selectedDescriptor), currentAppliedRevision: try c.decodeIfPresent(Int64.self, forKey: .currentAppliedRevision))
try self.init(version: try c.decode(String.self, forKey: .version), sessionId: try c.decode(String.self, forKey: .sessionId), gatewayId: try c.decode(String.self, forKey: .gatewayId), reconnectSequence: try c.decode(Int64.self, forKey: .reconnectSequence), operationId: try c.decode(String.self, forKey: .operationId), revision: try c.decode(Int64.self, forKey: .revision), leaseGeneration: try c.decode(Int64.self, forKey: .leaseGeneration), leaseExpiresAt: try c.decode(String.self, forKey: .leaseExpiresAt), selectedDescriptor: try c.decode(SelectedSessionDescriptor.self, forKey: .selectedDescriptor), currentAppliedRevision: try c.decodeIfPresent(Int64.self, forKey: .currentAppliedRevision))
}
public func validate() throws {
@@ -1551,6 +1565,7 @@ public struct GatewayQualityWork: Codable, Equatable {
if self.operationId.utf8.count > 36 { throw ContractValidationError(field: "operation_id", code: "max_length") }
if !validCanonicalUUID(self.operationId) { throw ContractValidationError(field: "operation_id", code: "invalid_uuid") }
if self.revision < 1 { throw ContractValidationError(field: "revision", code: "minimum") }
if self.leaseGeneration < 1 { throw ContractValidationError(field: "lease_generation", code: "minimum") }
if self.leaseExpiresAt.utf8.count > 64 { throw ContractValidationError(field: "lease_expires_at", code: "max_length") }
if !validRFC3339UTC(self.leaseExpiresAt) { throw ContractValidationError(field: "lease_expires_at", code: "invalid_time") }
try self.selectedDescriptor.validate()
@@ -1568,26 +1583,32 @@ public struct GatewayQualityWorkRequest: Codable, Equatable {
public let sessionId: String
public let gatewayId: String
public let reconnectSequence: Int64
public let operationId: String
public let revision: Int64
public let acquisition: String
public let operationId: String?
public let revision: Int64?
public let leaseGeneration: Int64?
public let currentAppliedRevision: Int64?
enum CodingKeys: String, CodingKey {
case version = "version"
case sessionId = "session_id"
case gatewayId = "gateway_id"
case reconnectSequence = "reconnect_sequence"
case acquisition = "acquisition"
case operationId = "operation_id"
case revision = "revision"
case leaseGeneration = "lease_generation"
case currentAppliedRevision = "current_applied_revision"
}
public init(version: String, sessionId: String, gatewayId: String, reconnectSequence: Int64, operationId: String, revision: Int64, currentAppliedRevision: Int64?) throws {
public init(version: String, sessionId: String, gatewayId: String, reconnectSequence: Int64, acquisition: String, operationId: String?, revision: Int64?, leaseGeneration: Int64?, currentAppliedRevision: Int64?) throws {
self.version = version
self.sessionId = sessionId
self.gatewayId = gatewayId
self.reconnectSequence = reconnectSequence
self.acquisition = acquisition
self.operationId = operationId
self.revision = revision
self.leaseGeneration = leaseGeneration
self.currentAppliedRevision = currentAppliedRevision
try validate()
}
@@ -1596,7 +1617,7 @@ public struct GatewayQualityWorkRequest: 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(version: try c.decode(String.self, forKey: .version), sessionId: try c.decode(String.self, forKey: .sessionId), gatewayId: try c.decode(String.self, forKey: .gatewayId), reconnectSequence: try c.decode(Int64.self, forKey: .reconnectSequence), operationId: try c.decode(String.self, forKey: .operationId), revision: try c.decode(Int64.self, forKey: .revision), currentAppliedRevision: try c.decodeIfPresent(Int64.self, forKey: .currentAppliedRevision))
try self.init(version: try c.decode(String.self, forKey: .version), sessionId: try c.decode(String.self, forKey: .sessionId), gatewayId: try c.decode(String.self, forKey: .gatewayId), reconnectSequence: try c.decode(Int64.self, forKey: .reconnectSequence), acquisition: try c.decode(String.self, forKey: .acquisition), operationId: try c.decodeIfPresent(String.self, forKey: .operationId), revision: try c.decodeIfPresent(Int64.self, forKey: .revision), leaseGeneration: try c.decodeIfPresent(Int64.self, forKey: .leaseGeneration), currentAppliedRevision: try c.decodeIfPresent(Int64.self, forKey: .currentAppliedRevision))
}
public func validate() throws {
@@ -1608,14 +1629,24 @@ public struct GatewayQualityWorkRequest: Codable, Equatable {
if !self.gatewayId.isEmpty && self.gatewayId.utf8.count < 1 { throw ContractValidationError(field: "gateway_id", code: "min_length") }
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.operationId.isEmpty { throw ContractValidationError(field: "operation_id", code: "required") }
if !self.operationId.isEmpty && self.operationId.utf8.count < 36 { throw ContractValidationError(field: "operation_id", code: "min_length") }
if self.operationId.utf8.count > 36 { throw ContractValidationError(field: "operation_id", code: "max_length") }
if !validCanonicalUUID(self.operationId) { throw ContractValidationError(field: "operation_id", code: "invalid_uuid") }
if self.revision < 1 { throw ContractValidationError(field: "revision", code: "minimum") }
if !["poll", "prompt", "observation"].contains(self.acquisition) { throw ContractValidationError(field: "acquisition", code: "invalid_value") }
if let value = self.operationId {
if !value.isEmpty && value.utf8.count < 36 { throw ContractValidationError(field: "operation_id", code: "min_length") }
if value.utf8.count > 36 { throw ContractValidationError(field: "operation_id", code: "max_length") }
if !validCanonicalUUID(value) { throw ContractValidationError(field: "operation_id", code: "invalid_uuid") }
}
if let value = self.revision {
if value < 1 { throw ContractValidationError(field: "revision", code: "minimum") }
}
if let value = self.leaseGeneration {
if value < 1 { throw ContractValidationError(field: "lease_generation", code: "minimum") }
}
if let value = self.currentAppliedRevision {
if value < 0 { throw ContractValidationError(field: "current_applied_revision", code: "minimum") }
}
if acquisition == "poll" && (operationId != nil || revision != nil || leaseGeneration != nil || currentAppliedRevision != nil) { throw ContractValidationError(field: "acquisition", code: "invalid_tagged_value") }
if acquisition == "prompt" && (operationId == nil || revision == nil || leaseGeneration != nil || currentAppliedRevision != nil) { throw ContractValidationError(field: "acquisition", code: "invalid_tagged_value") }
if acquisition == "observation" && (operationId == nil || revision == nil || leaseGeneration == nil || currentAppliedRevision == nil) { throw ContractValidationError(field: "acquisition", code: "invalid_tagged_value") }
}
public static func decodeJSON(_ data: Data) throws -> Self { try rejectDuplicateJSONKeys(data); return try JSONDecoder().decode(Self.self, from: data) }
@@ -1834,20 +1865,23 @@ public struct GatewayStopWorkRequest: Codable, Equatable {
public let sessionId: String
public let gatewayId: String
public let reconnectSequence: Int64
public let operationId: String
public let acquisition: String
public let operationId: String?
enum CodingKeys: String, CodingKey {
case version = "version"
case sessionId = "session_id"
case gatewayId = "gateway_id"
case reconnectSequence = "reconnect_sequence"
case acquisition = "acquisition"
case operationId = "operation_id"
}
public init(version: String, sessionId: String, gatewayId: String, reconnectSequence: Int64, operationId: String) throws {
public init(version: String, sessionId: String, gatewayId: String, reconnectSequence: Int64, acquisition: String, operationId: String?) throws {
self.version = version
self.sessionId = sessionId
self.gatewayId = gatewayId
self.reconnectSequence = reconnectSequence
self.acquisition = acquisition
self.operationId = operationId
try validate()
}
@@ -1856,7 +1890,7 @@ public struct GatewayStopWorkRequest: 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(version: try c.decode(String.self, forKey: .version), sessionId: try c.decode(String.self, forKey: .sessionId), gatewayId: try c.decode(String.self, forKey: .gatewayId), reconnectSequence: try c.decode(Int64.self, forKey: .reconnectSequence), operationId: try c.decode(String.self, forKey: .operationId))
try self.init(version: try c.decode(String.self, forKey: .version), sessionId: try c.decode(String.self, forKey: .sessionId), gatewayId: try c.decode(String.self, forKey: .gatewayId), reconnectSequence: try c.decode(Int64.self, forKey: .reconnectSequence), acquisition: try c.decode(String.self, forKey: .acquisition), operationId: try c.decodeIfPresent(String.self, forKey: .operationId))
}
public func validate() throws {
@@ -1868,10 +1902,13 @@ public struct GatewayStopWorkRequest: Codable, Equatable {
if !self.gatewayId.isEmpty && self.gatewayId.utf8.count < 1 { throw ContractValidationError(field: "gateway_id", code: "min_length") }
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.operationId.isEmpty { throw ContractValidationError(field: "operation_id", code: "required") }
if !self.operationId.isEmpty && self.operationId.utf8.count < 36 { throw ContractValidationError(field: "operation_id", code: "min_length") }
if self.operationId.utf8.count > 36 { throw ContractValidationError(field: "operation_id", code: "max_length") }
if !validCanonicalUUID(self.operationId) { throw ContractValidationError(field: "operation_id", code: "invalid_uuid") }
if !["poll", "prompt"].contains(self.acquisition) { throw ContractValidationError(field: "acquisition", code: "invalid_value") }
if let value = self.operationId {
if !value.isEmpty && value.utf8.count < 36 { throw ContractValidationError(field: "operation_id", code: "min_length") }
if value.utf8.count > 36 { throw ContractValidationError(field: "operation_id", code: "max_length") }
if !validCanonicalUUID(value) { throw ContractValidationError(field: "operation_id", code: "invalid_uuid") }
}
if acquisition == "poll" && operationId != nil || acquisition == "prompt" && operationId == nil { throw ContractValidationError(field: "acquisition", code: "invalid_tagged_value") }
}
public static func decodeJSON(_ data: Data) throws -> Self { try rejectDuplicateJSONKeys(data); return try JSONDecoder().decode(Self.self, from: data) }