7984 lines
312 KiB
Go
7984 lines
312 KiB
Go
// Code generated by tools/generate.py; DO NOT EDIT.
|
|
package protocol
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/base64"
|
|
"encoding/binary"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"reflect"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
const SchemaSHA256 = "614fa11dd1f49b8e10bf21b8c10eadbc1468d16bfcc6ed0a26d29671a5e61300"
|
|
const ProtocolVersion = "1.0.0"
|
|
const CurrentWireVersion = "2"
|
|
const NMinus1WireVersion = "1"
|
|
const NMinus2WireVersion = "0"
|
|
|
|
type FieldViolation struct {
|
|
Field string `json:"field"`
|
|
Code string `json:"code"`
|
|
}
|
|
|
|
type ValidationError struct {
|
|
Violations []FieldViolation
|
|
}
|
|
|
|
func (e ValidationError) Error() string { return "protocol validation failed" }
|
|
|
|
func validCanonicalUUID(value string) bool {
|
|
if len(value) != 36 || value[8] != '-' || value[13] != '-' || value[18] != '-' || value[23] != '-' {
|
|
return false
|
|
}
|
|
for index, char := range []byte(value) {
|
|
if index == 8 || index == 13 || index == 18 || index == 23 {
|
|
continue
|
|
}
|
|
if !((char >= '0' && char <= '9') || (char >= 'a' && char <= 'f')) {
|
|
return false
|
|
}
|
|
}
|
|
return value != "00000000-0000-0000-0000-000000000000"
|
|
}
|
|
|
|
func rejectDuplicateJSONKeys(data []byte) error {
|
|
decoder := json.NewDecoder(bytes.NewReader(data))
|
|
var scan func(json.Token) error
|
|
scan = func(token json.Token) error {
|
|
delim, ok := token.(json.Delim)
|
|
if !ok {
|
|
return nil
|
|
}
|
|
switch delim {
|
|
case '{':
|
|
seen := map[string]struct{}{}
|
|
for decoder.More() {
|
|
keyToken, err := decoder.Token()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
key, ok := keyToken.(string)
|
|
if !ok {
|
|
return errors.New("invalid JSON object key")
|
|
}
|
|
if _, exists := seen[key]; exists {
|
|
return errors.New("duplicate JSON object key")
|
|
}
|
|
seen[key] = struct{}{}
|
|
value, err := decoder.Token()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := scan(value); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
_, err := decoder.Token()
|
|
return err
|
|
case '[':
|
|
for decoder.More() {
|
|
value, err := decoder.Token()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := scan(value); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
_, err := decoder.Token()
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
token, err := decoder.Token()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return scan(token)
|
|
}
|
|
|
|
type AllocationPolicy struct {
|
|
MinimumKbps int64 `json:"minimum_kbps"`
|
|
TargetKbps int64 `json:"target_kbps"`
|
|
MaximumKbps int64 `json:"maximum_kbps"`
|
|
Tier string `json:"tier"`
|
|
Audience string `json:"audience"`
|
|
Protocol string `json:"protocol"`
|
|
ProtocolVersion int64 `json:"protocol_version"`
|
|
GrantTTLSeconds int64 `json:"grant_ttl_seconds"`
|
|
ReservationLeaseSeconds int64 `json:"reservation_lease_seconds"`
|
|
}
|
|
|
|
type AssignedDesktop struct {
|
|
AssignmentID string `json:"assignment_id"`
|
|
PoolID string `json:"pool_id"`
|
|
Name string `json:"name"`
|
|
Availability string `json:"availability"`
|
|
QualityLimits SessionQualityLimits `json:"quality_limits"`
|
|
}
|
|
|
|
type AudioProfile struct {
|
|
Codec string `json:"codec"`
|
|
SampleRateHz int64 `json:"sample_rate_hz"`
|
|
Channels int64 `json:"channels"`
|
|
ChannelLayout string `json:"channel_layout"`
|
|
PacketDurationMs int64 `json:"packet_duration_ms"`
|
|
}
|
|
|
|
type BitratePreference struct {
|
|
Mode string `json:"mode"`
|
|
TargetKbps *int64 `json:"target_kbps,omitempty"`
|
|
}
|
|
|
|
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"`
|
|
RequestedDisplayMode *DisplayMode `json:"requested_display_mode,omitempty"`
|
|
EffectiveDisplayMode *DisplayMode `json:"effective_display_mode,omitempty"`
|
|
RequestedVideoProfiles []VideoProfile `json:"requested_video_profiles"`
|
|
RequestedBitratePreference BitratePreference `json:"requested_bitrate_preference"`
|
|
SelectedDescriptor *SelectedSessionDescriptor `json:"selected_descriptor,omitempty"`
|
|
}
|
|
|
|
type BrowserAuthenticatedSession struct {
|
|
Username string `json:"username"`
|
|
Provider string `json:"provider"`
|
|
Roles []string `json:"roles"`
|
|
Role string `json:"role"`
|
|
}
|
|
|
|
type CapabilityProfile struct {
|
|
Transport string `json:"transport"`
|
|
Framing string `json:"framing"`
|
|
Media string `json:"media"`
|
|
SourceRateControl string `json:"source_rate_control"`
|
|
VideoProfiles []VideoProfile `json:"video_profiles"`
|
|
AudioProfiles []AudioProfile `json:"audio_profiles"`
|
|
}
|
|
|
|
type ChannelFrame struct {
|
|
Version string `json:"version"`
|
|
FlowID string `json:"flow_id"`
|
|
Sequence int64 `json:"sequence"`
|
|
Flags int64 `json:"flags"`
|
|
FragmentIndex int64 `json:"fragment_index"`
|
|
FragmentCount int64 `json:"fragment_count"`
|
|
TimestampMs int64 `json:"timestamp_ms"`
|
|
Payload string `json:"payload"`
|
|
}
|
|
|
|
type ClientSessionAuthority struct {
|
|
Version string `json:"version"`
|
|
SessionID string `json:"session_id"`
|
|
GatewayID string `json:"gateway_id"`
|
|
Audience string `json:"audience"`
|
|
ReconnectSequence int64 `json:"reconnect_sequence"`
|
|
ExpiresAt string `json:"expires_at"`
|
|
Capabilities CapabilityProfile `json:"capabilities"`
|
|
SelectedDescriptor SelectedSessionDescriptor `json:"selected_descriptor"`
|
|
}
|
|
|
|
type ClipboardPolicy struct {
|
|
ClientToProviderEnabled bool `json:"client_to_provider_enabled"`
|
|
ProviderToClientEnabled bool `json:"provider_to_client_enabled"`
|
|
MaxTextBytes int64 `json:"max_text_bytes"`
|
|
MaxUpdatesPerMinute int64 `json:"max_updates_per_minute"`
|
|
}
|
|
|
|
type ClipboardText struct {
|
|
Text string `json:"text"`
|
|
Encoding string `json:"encoding"`
|
|
}
|
|
|
|
type ConnectionManifest struct {
|
|
Version string `json:"version"`
|
|
Purpose string `json:"purpose"`
|
|
SessionID string `json:"session_id"`
|
|
ReconnectSequence int64 `json:"reconnect_sequence"`
|
|
Gateway ManifestGateway `json:"gateway"`
|
|
Tunnel ManifestTunnel `json:"tunnel"`
|
|
Profile ManifestProfile `json:"profile"`
|
|
Grant GrantReference `json:"grant"`
|
|
CorrelationID string `json:"correlation_id"`
|
|
SelectedDescriptor SelectedSessionDescriptor `json:"selected_descriptor"`
|
|
}
|
|
|
|
type DeviceChallenge struct {
|
|
DeviceID string `json:"device_id"`
|
|
ServerID string `json:"server_id"`
|
|
PrincipalID string `json:"principal_id"`
|
|
Challenge string `json:"challenge"`
|
|
ExpiresAt string `json:"expires_at"`
|
|
Algorithm string `json:"algorithm"`
|
|
SignatureFormat string `json:"signature_format"`
|
|
}
|
|
|
|
type DeviceProofRequest struct {
|
|
Challenge string `json:"challenge"`
|
|
Signature string `json:"signature"`
|
|
}
|
|
|
|
type DeviceRegistrationRequest struct {
|
|
Name string `json:"name"`
|
|
Platform string `json:"platform"`
|
|
DeviceSubject string `json:"device_subject"`
|
|
Algorithm string `json:"algorithm"`
|
|
PublicKey string `json:"public_key"`
|
|
}
|
|
|
|
type DisplayLimitOverrideRequest struct {
|
|
AllowDisplayLimitOverride bool `json:"allow_display_limit_override"`
|
|
ExpectedVersion int64 `json:"expected_version"`
|
|
}
|
|
|
|
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"`
|
|
AssignmentState string `json:"assignment_state"`
|
|
QualityLimits SessionQualityLimits `json:"quality_limits"`
|
|
}
|
|
|
|
type ErrorEnvelope struct {
|
|
Status bool `json:"status"`
|
|
Error string `json:"error"`
|
|
Code string `json:"code"`
|
|
Message string `json:"message"`
|
|
Resolution string `json:"resolution"`
|
|
RequestID string `json:"request_id"`
|
|
Violations []FieldViolation `json:"violations"`
|
|
}
|
|
|
|
type EventEnvelope struct {
|
|
EventID string `json:"event_id"`
|
|
Sequence int64 `json:"sequence"`
|
|
Type string `json:"type"`
|
|
Version int64 `json:"version"`
|
|
Resource ResourceLink `json:"resource"`
|
|
OccurredAt string `json:"occurred_at"`
|
|
CorrelationID string `json:"correlation_id"`
|
|
Payload map[string]any `json:"payload"`
|
|
}
|
|
|
|
type EventResume struct {
|
|
Cursor string `json:"cursor"`
|
|
LastSequence int64 `json:"last_sequence"`
|
|
}
|
|
|
|
type GatewayClipboardAudit struct {
|
|
Version string `json:"version"`
|
|
SessionID string `json:"session_id"`
|
|
Direction string `json:"direction"`
|
|
Outcome string `json:"outcome"`
|
|
TextBytes int64 `json:"text_bytes"`
|
|
Reason string `json:"reason"`
|
|
}
|
|
|
|
type GatewayClipboardText struct {
|
|
Direction string `json:"direction"`
|
|
Text string `json:"text"`
|
|
Encoding string `json:"encoding"`
|
|
LoopToken string `json:"loop_token"`
|
|
}
|
|
|
|
type GatewayDrain struct {
|
|
Version string `json:"version"`
|
|
GatewayID string `json:"gateway_id"`
|
|
Sequence int64 `json:"sequence"`
|
|
Reason string `json:"reason"`
|
|
Deadline string `json:"deadline"`
|
|
}
|
|
|
|
type GatewayHeartbeat struct {
|
|
Version string `json:"version"`
|
|
GatewayID string `json:"gateway_id"`
|
|
Sequence int64 `json:"sequence"`
|
|
ObservedAt string `json:"observed_at"`
|
|
ActiveConnections int64 `json:"active_connections"`
|
|
EgressKbps int64 `json:"egress_kbps"`
|
|
State string `json:"state"`
|
|
Telemetry GatewayTelemetry `json:"telemetry"`
|
|
}
|
|
|
|
type GatewayQualityAck struct {
|
|
Version string `json:"version"`
|
|
SessionID string `json:"session_id"`
|
|
GatewayID string `json:"gateway_id"`
|
|
ReconnectSequence int64 `json:"reconnect_sequence"`
|
|
OperationID string `json:"operation_id"`
|
|
Revision int64 `json:"revision"`
|
|
Outcome string `json:"outcome"`
|
|
CurrentAppliedRevision int64 `json:"current_applied_revision"`
|
|
FailureCode string `json:"failure_code,omitempty"`
|
|
}
|
|
|
|
type GatewayQualityWork struct {
|
|
Version string `json:"version"`
|
|
SessionID string `json:"session_id"`
|
|
GatewayID string `json:"gateway_id"`
|
|
ReconnectSequence int64 `json:"reconnect_sequence"`
|
|
OperationID string `json:"operation_id"`
|
|
Revision int64 `json:"revision"`
|
|
LeaseExpiresAt string `json:"lease_expires_at"`
|
|
SelectedDescriptor SelectedSessionDescriptor `json:"selected_descriptor"`
|
|
CurrentAppliedRevision *int64 `json:"current_applied_revision,omitempty"`
|
|
}
|
|
|
|
type GatewayQualityWorkRequest struct {
|
|
Version string `json:"version"`
|
|
SessionID string `json:"session_id"`
|
|
GatewayID string `json:"gateway_id"`
|
|
ReconnectSequence int64 `json:"reconnect_sequence"`
|
|
OperationID string `json:"operation_id"`
|
|
Revision int64 `json:"revision"`
|
|
CurrentAppliedRevision *int64 `json:"current_applied_revision,omitempty"`
|
|
}
|
|
|
|
type GatewayRegistration struct {
|
|
Version string `json:"version"`
|
|
GatewayID string `json:"gateway_id"`
|
|
InstanceIdentity string `json:"instance_identity"`
|
|
CertificateIdentity string `json:"certificate_identity"`
|
|
PublicIdentity string `json:"public_identity"`
|
|
Address string `json:"address"`
|
|
ProviderIdentity string `json:"provider_identity"`
|
|
ProtocolMinVersion int64 `json:"protocol_min_version"`
|
|
ProtocolMaxVersion int64 `json:"protocol_max_version"`
|
|
ConnectionCapacity int64 `json:"connection_capacity"`
|
|
BandwidthCapacityKbps int64 `json:"bandwidth_capacity_kbps"`
|
|
Features []string `json:"features"`
|
|
Capabilities CapabilityProfile `json:"capabilities"`
|
|
}
|
|
|
|
type GatewayStopAck struct {
|
|
Version string `json:"version"`
|
|
SessionID string `json:"session_id"`
|
|
GatewayID string `json:"gateway_id"`
|
|
ReconnectSequence int64 `json:"reconnect_sequence"`
|
|
OperationID string `json:"operation_id"`
|
|
Outcome string `json:"outcome"`
|
|
FailureCode string `json:"failure_code,omitempty"`
|
|
}
|
|
|
|
type GatewayStopWork struct {
|
|
Version string `json:"version"`
|
|
SessionID string `json:"session_id"`
|
|
GatewayID string `json:"gateway_id"`
|
|
ReconnectSequence int64 `json:"reconnect_sequence"`
|
|
OperationID string `json:"operation_id"`
|
|
Attempt int64 `json:"attempt"`
|
|
}
|
|
|
|
type GatewayStopWorkRequest struct {
|
|
Version string `json:"version"`
|
|
SessionID string `json:"session_id"`
|
|
GatewayID string `json:"gateway_id"`
|
|
ReconnectSequence int64 `json:"reconnect_sequence"`
|
|
OperationID string `json:"operation_id"`
|
|
}
|
|
|
|
type GatewayTelemetry struct {
|
|
AdmittedSessions int64 `json:"admitted_sessions"`
|
|
AdmissionRejects int64 `json:"admission_rejects"`
|
|
Reconnects int64 `json:"reconnects"`
|
|
DrainTransitions int64 `json:"drain_transitions"`
|
|
MediaDrops int64 `json:"media_drops"`
|
|
MediaPackets int64 `json:"media_packets"`
|
|
MediaBytes int64 `json:"media_bytes"`
|
|
QueueDelayMicros int64 `json:"queue_delay_micros"`
|
|
ProcessingDelayMicros int64 `json:"processing_delay_micros"`
|
|
ProcessingSamples int64 `json:"processing_samples"`
|
|
PacingDelayMicros int64 `json:"pacing_delay_micros"`
|
|
ProviderErrors int64 `json:"provider_errors"`
|
|
InputRejected int64 `json:"input_rejected"`
|
|
ControlRttMicros int64 `json:"control_rtt_micros"`
|
|
ControlJitterMicros int64 `json:"control_jitter_micros"`
|
|
ControlLossPpm int64 `json:"control_loss_ppm"`
|
|
PendingReliable int64 `json:"pending_reliable"`
|
|
ProviderState string `json:"provider_state"`
|
|
}
|
|
|
|
type GrantReference struct {
|
|
OpaqueValue string `json:"opaque_value"`
|
|
ExpiresAt string `json:"expires_at"`
|
|
Audience string `json:"audience"`
|
|
}
|
|
|
|
type LoginRequest struct {
|
|
Provider string `json:"provider,omitempty"`
|
|
Username string `json:"username"`
|
|
Password string `json:"password"`
|
|
}
|
|
|
|
type ManifestBounds struct {
|
|
MinimumKbps int64 `json:"minimum_kbps"`
|
|
TargetKbps int64 `json:"target_kbps"`
|
|
MaximumKbps int64 `json:"maximum_kbps"`
|
|
}
|
|
|
|
type ManifestGateway struct {
|
|
ID string `json:"id"`
|
|
Addresses []string `json:"addresses"`
|
|
PublicIdentity string `json:"public_identity"`
|
|
}
|
|
|
|
type ManifestProfile struct {
|
|
ID string `json:"id"`
|
|
Bounds ManifestBounds `json:"bounds"`
|
|
DisplayMode *DisplayMode `json:"display_mode,omitempty"`
|
|
}
|
|
|
|
type ManifestTunnel struct {
|
|
Versions []string `json:"versions"`
|
|
Features []string `json:"features"`
|
|
}
|
|
|
|
type NativeAuthenticatedSession struct {
|
|
Username string `json:"username"`
|
|
Provider string `json:"provider"`
|
|
Roles []string `json:"roles"`
|
|
Role string `json:"role"`
|
|
NativeIdentity NativeSessionIdentity `json:"native_identity"`
|
|
}
|
|
|
|
type NativeCredential struct {
|
|
DeviceID string `json:"device_id,omitempty"`
|
|
FamilyID string `json:"family_id"`
|
|
AccessToken string `json:"access_token"`
|
|
RefreshToken string `json:"refresh_token"`
|
|
ExpiresAt string `json:"expires_at"`
|
|
RefreshExpiresAt string `json:"refresh_expires_at,omitempty"`
|
|
}
|
|
|
|
type NativeSessionIdentity struct {
|
|
ClientDeviceID string `json:"client_device_id"`
|
|
DeviceKeyID string `json:"device_key_id"`
|
|
}
|
|
|
|
type NativeTunnelCredential struct {
|
|
ClientDeviceID string `json:"client_device_id"`
|
|
DeviceKeyID string `json:"device_key_id"`
|
|
CertificateChainPem string `json:"certificate_chain_pem"`
|
|
TrustBundlePem string `json:"trust_bundle_pem"`
|
|
ExpiresAt string `json:"expires_at"`
|
|
}
|
|
|
|
type PageInfo struct {
|
|
Limit int64 `json:"limit"`
|
|
NextCursor string `json:"next_cursor"`
|
|
}
|
|
|
|
type ProviderSessionWork struct {
|
|
Version string `json:"version"`
|
|
SessionID string `json:"session_id"`
|
|
GatewayID string `json:"gateway_id"`
|
|
ReconnectSequence int64 `json:"reconnect_sequence"`
|
|
ExpiresAt string `json:"expires_at"`
|
|
ProviderProfile string `json:"provider_profile"`
|
|
ProviderIdentity string `json:"provider_identity"`
|
|
PolicyVersionID string `json:"policy_version_id"`
|
|
StreamPolicy ProviderStreamPolicy `json:"stream_policy"`
|
|
ApplicationID string `json:"application_id"`
|
|
ClientID string `json:"client_id"`
|
|
ManagementHost string `json:"management_host"`
|
|
ManagementPort int64 `json:"management_port"`
|
|
StreamHost string `json:"stream_host"`
|
|
StreamPort int64 `json:"stream_port"`
|
|
ClientCertificatePem string `json:"client_certificate_pem"`
|
|
ClientPrivateKeyPem string `json:"client_private_key_pem"`
|
|
ServerCertificatePem string `json:"server_certificate_pem"`
|
|
ClipboardPolicy ClipboardPolicy `json:"clipboard_policy"`
|
|
ProviderApplicationTerminationAllowed bool `json:"provider_application_termination_allowed"`
|
|
}
|
|
|
|
type ProviderState struct {
|
|
Version string `json:"version"`
|
|
SessionID string `json:"session_id"`
|
|
State string `json:"state"`
|
|
CleanupPending bool `json:"cleanup_pending"`
|
|
Channels []string `json:"channels"`
|
|
}
|
|
|
|
type ProviderStreamPolicy struct {
|
|
VideoProfile VideoProfile `json:"video_profile"`
|
|
AudioProfile AudioProfile `json:"audio_profile"`
|
|
DisplayMode DisplayMode `json:"display_mode"`
|
|
BitrateTargetKbps int64 `json:"bitrate_target_kbps"`
|
|
BitrateMaximumKbps int64 `json:"bitrate_maximum_kbps"`
|
|
}
|
|
|
|
type QualityChangeOperation struct {
|
|
OperationID string `json:"operation_id"`
|
|
SessionID string `json:"session_id"`
|
|
Revision int64 `json:"revision"`
|
|
State string `json:"state"`
|
|
RequestedBitratePreference BitratePreference `json:"requested_bitrate_preference"`
|
|
EffectiveBitrateKbps int64 `json:"effective_bitrate_kbps"`
|
|
GoverningPolicyVersion string `json:"governing_policy_version"`
|
|
SessionVersion int64 `json:"session_version"`
|
|
CreatedAt string `json:"created_at"`
|
|
DeadlineAt string `json:"deadline_at"`
|
|
UpdatedAt string `json:"updated_at"`
|
|
FailureCode string `json:"failure_code,omitempty"`
|
|
}
|
|
|
|
type QualityChangeRequest struct {
|
|
BitratePreference BitratePreference `json:"bitrate_preference"`
|
|
ExpectedSessionVersion int64 `json:"expected_session_version"`
|
|
ExpectedPolicyVersion string `json:"expected_policy_version"`
|
|
}
|
|
|
|
type ReauthGrant struct {
|
|
Token string `json:"token"`
|
|
Purpose string `json:"purpose"`
|
|
ExpiresAt string `json:"expires_at"`
|
|
}
|
|
|
|
type ReauthRequest struct {
|
|
Password string `json:"password"`
|
|
Purpose string `json:"purpose"`
|
|
}
|
|
|
|
type ReconnectRequest struct {
|
|
ClientDeviceID string `json:"client_device_id"`
|
|
DeviceKeyID string `json:"device_key_id"`
|
|
ExpectedVersion int64 `json:"expected_version"`
|
|
DisplayRelaunchConfirmed bool `json:"display_relaunch_confirmed"`
|
|
}
|
|
|
|
type RefreshRequest struct {
|
|
FamilyID string `json:"family_id"`
|
|
RefreshToken string `json:"refresh_token"`
|
|
}
|
|
|
|
type Resource struct {
|
|
ID string `json:"id"`
|
|
Kind string `json:"kind"`
|
|
Name string `json:"name"`
|
|
State string `json:"state"`
|
|
AssignmentState string `json:"assignment_state,omitempty"`
|
|
Version int64 `json:"version"`
|
|
Links []ResourceLink `json:"links"`
|
|
}
|
|
|
|
type ResourceLink struct {
|
|
Type string `json:"type"`
|
|
ID string `json:"id"`
|
|
Version int64 `json:"version"`
|
|
}
|
|
|
|
type ResourceList struct {
|
|
AssignedDesktops []AssignedDesktop `json:"assigned_desktops"`
|
|
EntitledPools []EntitledPool `json:"entitled_pools"`
|
|
Page PageInfo `json:"page"`
|
|
}
|
|
|
|
type SelectedSessionDescriptor struct {
|
|
VideoProfile VideoProfile `json:"video_profile"`
|
|
AudioProfile AudioProfile `json:"audio_profile"`
|
|
DisplayMode DisplayMode `json:"display_mode"`
|
|
BitrateTargetKbps int64 `json:"bitrate_target_kbps"`
|
|
BitrateMaximumKbps int64 `json:"bitrate_maximum_kbps"`
|
|
Adjustment SessionAdjustment `json:"adjustment"`
|
|
MediaTimestampBasis string `json:"media_timestamp_basis"`
|
|
}
|
|
|
|
type SessionAdjustment struct {
|
|
DisplayReason string `json:"display_reason"`
|
|
BitrateReason string `json:"bitrate_reason"`
|
|
}
|
|
|
|
type SessionAuthority struct {
|
|
Version string `json:"version"`
|
|
SessionID string `json:"session_id"`
|
|
GatewayID string `json:"gateway_id"`
|
|
Audience string `json:"audience"`
|
|
ReconnectSequence int64 `json:"reconnect_sequence"`
|
|
ExpiresAt string `json:"expires_at"`
|
|
Capabilities CapabilityProfile `json:"capabilities"`
|
|
ProviderProfile string `json:"provider_profile"`
|
|
ProviderIdentity string `json:"provider_identity"`
|
|
SelectedDescriptor SelectedSessionDescriptor `json:"selected_descriptor"`
|
|
}
|
|
|
|
type SessionQualityLimits struct {
|
|
PolicyVersionID string `json:"policy_version_id"`
|
|
PolicyDisplayLimit DisplayMode `json:"policy_display_limit"`
|
|
SelectableDisplayMaximum DisplayMode `json:"selectable_display_maximum"`
|
|
DisplayLimitOverride bool `json:"display_limit_override"`
|
|
BitrateMinimumKbps int64 `json:"bitrate_minimum_kbps"`
|
|
BitrateTargetKbps int64 `json:"bitrate_target_kbps"`
|
|
BitrateMaximumKbps int64 `json:"bitrate_maximum_kbps"`
|
|
}
|
|
|
|
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"`
|
|
VideoProfiles []VideoProfile `json:"video_profiles"`
|
|
BitratePreference BitratePreference `json:"bitrate_preference"`
|
|
RequestedDisplayMode *DisplayMode `json:"requested_display_mode,omitempty"`
|
|
}
|
|
|
|
type StableError struct {
|
|
Version string `json:"version"`
|
|
Code string `json:"code"`
|
|
Message string `json:"message"`
|
|
Retryable bool `json:"retryable"`
|
|
}
|
|
|
|
type StopOperation struct {
|
|
OperationID string `json:"operation_id"`
|
|
SessionID string `json:"session_id"`
|
|
State string `json:"state"`
|
|
SessionVersion int64 `json:"session_version"`
|
|
CreatedAt string `json:"created_at"`
|
|
DeadlineAt string `json:"deadline_at"`
|
|
UpdatedAt string `json:"updated_at"`
|
|
FailureCode string `json:"failure_code,omitempty"`
|
|
}
|
|
|
|
type TunnelAdmissionRequest struct {
|
|
Version string `json:"version"`
|
|
SessionID string `json:"session_id"`
|
|
GatewayID string `json:"gateway_id"`
|
|
Audience string `json:"audience"`
|
|
Grant string `json:"grant"`
|
|
ReconnectSequence int64 `json:"reconnect_sequence"`
|
|
ClientNonce string `json:"client_nonce"`
|
|
DeviceSignature string `json:"device_signature"`
|
|
Capabilities CapabilityProfile `json:"capabilities"`
|
|
}
|
|
|
|
type VersionNegotiation struct {
|
|
SupportedVersions []string `json:"supported_versions"`
|
|
Features []string `json:"features"`
|
|
}
|
|
|
|
type VideoProfile struct {
|
|
Codec string `json:"codec"`
|
|
BitDepth int64 `json:"bit_depth"`
|
|
ChromaSubsampling string `json:"chroma_subsampling"`
|
|
ColorSpace string `json:"color_space"`
|
|
TransferFunction string `json:"transfer_function"`
|
|
}
|
|
|
|
func (v AllocationPolicy) Validate() error {
|
|
var violations []FieldViolation
|
|
if v.MinimumKbps == 0 {
|
|
violations = append(violations, FieldViolation{Field: "minimum_kbps", Code: "required"})
|
|
}
|
|
if v.MinimumKbps != 0 && v.MinimumKbps < 1 {
|
|
violations = append(violations, FieldViolation{Field: "minimum_kbps", Code: "minimum"})
|
|
}
|
|
if v.MinimumKbps > 100000000 {
|
|
violations = append(violations, FieldViolation{Field: "minimum_kbps", Code: "maximum"})
|
|
}
|
|
if v.TargetKbps == 0 {
|
|
violations = append(violations, FieldViolation{Field: "target_kbps", Code: "required"})
|
|
}
|
|
if v.TargetKbps != 0 && v.TargetKbps < 1 {
|
|
violations = append(violations, FieldViolation{Field: "target_kbps", Code: "minimum"})
|
|
}
|
|
if v.TargetKbps > 100000000 {
|
|
violations = append(violations, FieldViolation{Field: "target_kbps", Code: "maximum"})
|
|
}
|
|
if v.MaximumKbps == 0 {
|
|
violations = append(violations, FieldViolation{Field: "maximum_kbps", Code: "required"})
|
|
}
|
|
if v.MaximumKbps != 0 && v.MaximumKbps < 1 {
|
|
violations = append(violations, FieldViolation{Field: "maximum_kbps", Code: "minimum"})
|
|
}
|
|
if v.MaximumKbps > 100000000 {
|
|
violations = append(violations, FieldViolation{Field: "maximum_kbps", Code: "maximum"})
|
|
}
|
|
if v.Tier == "" {
|
|
violations = append(violations, FieldViolation{Field: "tier", Code: "required"})
|
|
}
|
|
if v.Tier != "" && !(v.Tier == "standard" || v.Tier == "priority" || v.Tier == "premium") {
|
|
violations = append(violations, FieldViolation{Field: "tier", Code: "invalid_value"})
|
|
}
|
|
if v.Audience == "" {
|
|
violations = append(violations, FieldViolation{Field: "audience", Code: "required"})
|
|
}
|
|
if len(v.Audience) < 1 && v.Audience != "" {
|
|
violations = append(violations, FieldViolation{Field: "audience", Code: "min_length"})
|
|
}
|
|
if len(v.Audience) > 256 {
|
|
violations = append(violations, FieldViolation{Field: "audience", Code: "max_length"})
|
|
}
|
|
if v.Protocol == "" {
|
|
violations = append(violations, FieldViolation{Field: "protocol", Code: "required"})
|
|
}
|
|
if len(v.Protocol) < 1 && v.Protocol != "" {
|
|
violations = append(violations, FieldViolation{Field: "protocol", Code: "min_length"})
|
|
}
|
|
if len(v.Protocol) > 64 {
|
|
violations = append(violations, FieldViolation{Field: "protocol", Code: "max_length"})
|
|
}
|
|
if v.ProtocolVersion == 0 {
|
|
violations = append(violations, FieldViolation{Field: "protocol_version", Code: "required"})
|
|
}
|
|
if v.ProtocolVersion != 0 && v.ProtocolVersion < 1 {
|
|
violations = append(violations, FieldViolation{Field: "protocol_version", Code: "minimum"})
|
|
}
|
|
if v.ProtocolVersion > 100 {
|
|
violations = append(violations, FieldViolation{Field: "protocol_version", Code: "maximum"})
|
|
}
|
|
if v.GrantTTLSeconds == 0 {
|
|
violations = append(violations, FieldViolation{Field: "grant_ttl_seconds", Code: "required"})
|
|
}
|
|
if v.GrantTTLSeconds != 0 && v.GrantTTLSeconds < 5 {
|
|
violations = append(violations, FieldViolation{Field: "grant_ttl_seconds", Code: "minimum"})
|
|
}
|
|
if v.GrantTTLSeconds > 300 {
|
|
violations = append(violations, FieldViolation{Field: "grant_ttl_seconds", Code: "maximum"})
|
|
}
|
|
if v.ReservationLeaseSeconds == 0 {
|
|
violations = append(violations, FieldViolation{Field: "reservation_lease_seconds", Code: "required"})
|
|
}
|
|
if v.ReservationLeaseSeconds != 0 && v.ReservationLeaseSeconds < 5 {
|
|
violations = append(violations, FieldViolation{Field: "reservation_lease_seconds", Code: "minimum"})
|
|
}
|
|
if v.ReservationLeaseSeconds > 3600 {
|
|
violations = append(violations, FieldViolation{Field: "reservation_lease_seconds", Code: "maximum"})
|
|
}
|
|
if v.MinimumKbps > v.TargetKbps || v.TargetKbps > v.MaximumKbps {
|
|
violations = append(violations, FieldViolation{Field: "bounds", Code: "invalid_order"})
|
|
}
|
|
if len(violations) > 0 {
|
|
return ValidationError{Violations: violations}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func DecodeAllocationPolicy(data []byte) (AllocationPolicy, error) {
|
|
var value AllocationPolicy
|
|
if len(data) > 1024*1024 {
|
|
return value, errors.New("protocol payload exceeds limit")
|
|
}
|
|
if err := rejectDuplicateJSONKeys(data); err != nil {
|
|
return value, err
|
|
}
|
|
var fields map[string]json.RawMessage
|
|
if err := json.Unmarshal(data, &fields); err != nil {
|
|
return value, err
|
|
}
|
|
if raw, ok := fields["audience"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "audience", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["grant_ttl_seconds"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "grant_ttl_seconds", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["maximum_kbps"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "maximum_kbps", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["minimum_kbps"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "minimum_kbps", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["protocol"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "protocol", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["protocol_version"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "protocol_version", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["reservation_lease_seconds"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "reservation_lease_seconds", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["target_kbps"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "target_kbps", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["tier"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "tier", 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 EncodeAllocationPolicy(value AllocationPolicy) ([]byte, error) {
|
|
if err := value.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(value)
|
|
}
|
|
|
|
func (v AssignedDesktop) Validate() error {
|
|
var violations []FieldViolation
|
|
if v.AssignmentID == "" {
|
|
violations = append(violations, FieldViolation{Field: "assignment_id", Code: "required"})
|
|
}
|
|
if len(v.AssignmentID) < 1 && v.AssignmentID != "" {
|
|
violations = append(violations, FieldViolation{Field: "assignment_id", Code: "min_length"})
|
|
}
|
|
if len(v.AssignmentID) > 128 {
|
|
violations = append(violations, FieldViolation{Field: "assignment_id", Code: "max_length"})
|
|
}
|
|
if v.PoolID == "" {
|
|
violations = append(violations, FieldViolation{Field: "pool_id", Code: "required"})
|
|
}
|
|
if len(v.PoolID) < 1 && v.PoolID != "" {
|
|
violations = append(violations, FieldViolation{Field: "pool_id", Code: "min_length"})
|
|
}
|
|
if len(v.PoolID) > 128 {
|
|
violations = append(violations, FieldViolation{Field: "pool_id", Code: "max_length"})
|
|
}
|
|
if v.Name == "" {
|
|
violations = append(violations, FieldViolation{Field: "name", Code: "required"})
|
|
}
|
|
if len(v.Name) < 1 && v.Name != "" {
|
|
violations = append(violations, FieldViolation{Field: "name", Code: "min_length"})
|
|
}
|
|
if len(v.Name) > 256 {
|
|
violations = append(violations, FieldViolation{Field: "name", Code: "max_length"})
|
|
}
|
|
if v.Availability == "" {
|
|
violations = append(violations, FieldViolation{Field: "availability", Code: "required"})
|
|
}
|
|
if len(v.Availability) < 1 && v.Availability != "" {
|
|
violations = append(violations, FieldViolation{Field: "availability", Code: "min_length"})
|
|
}
|
|
if len(v.Availability) > 64 {
|
|
violations = append(violations, FieldViolation{Field: "availability", Code: "max_length"})
|
|
}
|
|
if reflect.DeepEqual(v.QualityLimits, SessionQualityLimits{}) {
|
|
violations = append(violations, FieldViolation{Field: "quality_limits", Code: "required"})
|
|
}
|
|
if err := v.QualityLimits.Validate(); err != nil {
|
|
violations = append(violations, FieldViolation{Field: "quality_limits", Code: "invalid_object"})
|
|
}
|
|
if len(violations) > 0 {
|
|
return ValidationError{Violations: violations}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func DecodeAssignedDesktop(data []byte) (AssignedDesktop, error) {
|
|
var value AssignedDesktop
|
|
if len(data) > 1024*1024 {
|
|
return value, errors.New("protocol payload exceeds limit")
|
|
}
|
|
if err := rejectDuplicateJSONKeys(data); err != nil {
|
|
return value, err
|
|
}
|
|
var fields map[string]json.RawMessage
|
|
if err := json.Unmarshal(data, &fields); err != nil {
|
|
return value, err
|
|
}
|
|
if raw, ok := fields["assignment_id"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "assignment_id", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["availability"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "availability", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["name"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "name", Code: "required"}}}
|
|
}
|
|
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["quality_limits"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "quality_limits", 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 EncodeAssignedDesktop(value AssignedDesktop) ([]byte, error) {
|
|
if err := value.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(value)
|
|
}
|
|
|
|
func (v AudioProfile) Validate() error {
|
|
var violations []FieldViolation
|
|
if v.Codec == "" {
|
|
violations = append(violations, FieldViolation{Field: "codec", Code: "required"})
|
|
}
|
|
if v.Codec != "opus" && v.Codec != "" {
|
|
violations = append(violations, FieldViolation{Field: "codec", Code: "invalid_value"})
|
|
}
|
|
if v.SampleRateHz == 0 {
|
|
violations = append(violations, FieldViolation{Field: "sample_rate_hz", Code: "required"})
|
|
}
|
|
if v.SampleRateHz != 0 && v.SampleRateHz < 48000 {
|
|
violations = append(violations, FieldViolation{Field: "sample_rate_hz", Code: "minimum"})
|
|
}
|
|
if v.SampleRateHz > 48000 {
|
|
violations = append(violations, FieldViolation{Field: "sample_rate_hz", Code: "maximum"})
|
|
}
|
|
if v.Channels == 0 {
|
|
violations = append(violations, FieldViolation{Field: "channels", Code: "required"})
|
|
}
|
|
if v.Channels != 0 && v.Channels < 2 {
|
|
violations = append(violations, FieldViolation{Field: "channels", Code: "minimum"})
|
|
}
|
|
if v.Channels > 2 {
|
|
violations = append(violations, FieldViolation{Field: "channels", Code: "maximum"})
|
|
}
|
|
if v.ChannelLayout == "" {
|
|
violations = append(violations, FieldViolation{Field: "channel_layout", Code: "required"})
|
|
}
|
|
if v.ChannelLayout != "stereo" && v.ChannelLayout != "" {
|
|
violations = append(violations, FieldViolation{Field: "channel_layout", Code: "invalid_value"})
|
|
}
|
|
if v.PacketDurationMs == 0 {
|
|
violations = append(violations, FieldViolation{Field: "packet_duration_ms", Code: "required"})
|
|
}
|
|
if v.PacketDurationMs != 0 && v.PacketDurationMs < 5 {
|
|
violations = append(violations, FieldViolation{Field: "packet_duration_ms", Code: "minimum"})
|
|
}
|
|
if v.PacketDurationMs > 5 {
|
|
violations = append(violations, FieldViolation{Field: "packet_duration_ms", Code: "maximum"})
|
|
}
|
|
if len(violations) > 0 {
|
|
return ValidationError{Violations: violations}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func DecodeAudioProfile(data []byte) (AudioProfile, error) {
|
|
var value AudioProfile
|
|
if len(data) > 1024*1024 {
|
|
return value, errors.New("protocol payload exceeds limit")
|
|
}
|
|
if err := rejectDuplicateJSONKeys(data); err != nil {
|
|
return value, err
|
|
}
|
|
var fields map[string]json.RawMessage
|
|
if err := json.Unmarshal(data, &fields); err != nil {
|
|
return value, err
|
|
}
|
|
if raw, ok := fields["channel_layout"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "channel_layout", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["channels"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "channels", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["codec"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "codec", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["packet_duration_ms"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "packet_duration_ms", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["sample_rate_hz"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "sample_rate_hz", 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 EncodeAudioProfile(value AudioProfile) ([]byte, error) {
|
|
if err := value.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(value)
|
|
}
|
|
|
|
func (v BitratePreference) Validate() error {
|
|
var violations []FieldViolation
|
|
if v.Mode == "" {
|
|
violations = append(violations, FieldViolation{Field: "mode", Code: "required"})
|
|
}
|
|
if v.Mode != "" && !(v.Mode == "auto" || v.Mode == "explicit") {
|
|
violations = append(violations, FieldViolation{Field: "mode", Code: "invalid_value"})
|
|
}
|
|
if v.TargetKbps != nil && *v.TargetKbps != 0 && *v.TargetKbps < 100 {
|
|
violations = append(violations, FieldViolation{Field: "target_kbps", Code: "minimum"})
|
|
}
|
|
if v.TargetKbps != nil && *v.TargetKbps > 1000000 {
|
|
violations = append(violations, FieldViolation{Field: "target_kbps", Code: "maximum"})
|
|
}
|
|
if v.Mode == "auto" && v.TargetKbps != nil || v.Mode == "explicit" && v.TargetKbps == nil {
|
|
violations = append(violations, FieldViolation{Field: "target_kbps", Code: "invalid_tagged_value"})
|
|
}
|
|
if len(violations) > 0 {
|
|
return ValidationError{Violations: violations}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func DecodeBitratePreference(data []byte) (BitratePreference, error) {
|
|
var value BitratePreference
|
|
if len(data) > 1024*1024 {
|
|
return value, errors.New("protocol payload exceeds limit")
|
|
}
|
|
if err := rejectDuplicateJSONKeys(data); err != nil {
|
|
return value, err
|
|
}
|
|
var fields map[string]json.RawMessage
|
|
if err := json.Unmarshal(data, &fields); err != nil {
|
|
return value, err
|
|
}
|
|
if raw, ok := fields["mode"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "mode", 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 EncodeBitratePreference(value BitratePreference) ([]byte, error) {
|
|
if err := value.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(value)
|
|
}
|
|
|
|
func (v BrokerSession) Validate() error {
|
|
var violations []FieldViolation
|
|
if v.ID == "" {
|
|
violations = append(violations, FieldViolation{Field: "id", Code: "required"})
|
|
}
|
|
if len(v.ID) < 1 && v.ID != "" {
|
|
violations = append(violations, FieldViolation{Field: "id", Code: "min_length"})
|
|
}
|
|
if len(v.ID) > 128 {
|
|
violations = append(violations, FieldViolation{Field: "id", Code: "max_length"})
|
|
}
|
|
if v.PrincipalID == "" {
|
|
violations = append(violations, FieldViolation{Field: "principal_id", Code: "required"})
|
|
}
|
|
if len(v.PrincipalID) < 1 && v.PrincipalID != "" {
|
|
violations = append(violations, FieldViolation{Field: "principal_id", Code: "min_length"})
|
|
}
|
|
if len(v.PrincipalID) > 128 {
|
|
violations = append(violations, FieldViolation{Field: "principal_id", Code: "max_length"})
|
|
}
|
|
if v.PoolID == "" {
|
|
violations = append(violations, FieldViolation{Field: "pool_id", Code: "required"})
|
|
}
|
|
if len(v.PoolID) < 1 && v.PoolID != "" {
|
|
violations = append(violations, FieldViolation{Field: "pool_id", Code: "min_length"})
|
|
}
|
|
if len(v.PoolID) > 128 {
|
|
violations = append(violations, FieldViolation{Field: "pool_id", Code: "max_length"})
|
|
}
|
|
if len(v.AssignmentID) > 128 {
|
|
violations = append(violations, FieldViolation{Field: "assignment_id", Code: "max_length"})
|
|
}
|
|
if v.State == "" {
|
|
violations = append(violations, FieldViolation{Field: "state", Code: "required"})
|
|
}
|
|
if len(v.State) < 1 && v.State != "" {
|
|
violations = append(violations, FieldViolation{Field: "state", Code: "min_length"})
|
|
}
|
|
if len(v.State) > 64 {
|
|
violations = append(violations, FieldViolation{Field: "state", Code: "max_length"})
|
|
}
|
|
if reflect.DeepEqual(v.PolicySnapshot, AllocationPolicy{}) {
|
|
violations = append(violations, FieldViolation{Field: "policy_snapshot", Code: "required"})
|
|
}
|
|
if err := v.PolicySnapshot.Validate(); err != nil {
|
|
violations = append(violations, FieldViolation{Field: "policy_snapshot", Code: "invalid_object"})
|
|
}
|
|
if len(v.ReconnectDeadline) > 64 {
|
|
violations = append(violations, FieldViolation{Field: "reconnect_deadline", Code: "max_length"})
|
|
}
|
|
if v.ReconnectDeadline != "" {
|
|
if parsed, err := time.Parse(time.RFC3339Nano, v.ReconnectDeadline); err != nil || parsed.UTC().Format(time.RFC3339Nano) != v.ReconnectDeadline {
|
|
violations = append(violations, FieldViolation{Field: "reconnect_deadline", Code: "invalid_time"})
|
|
}
|
|
}
|
|
if len(v.Outcome) > 64 {
|
|
violations = append(violations, FieldViolation{Field: "outcome", Code: "max_length"})
|
|
}
|
|
if len(v.FailureCode) > 128 {
|
|
violations = append(violations, FieldViolation{Field: "failure_code", Code: "max_length"})
|
|
}
|
|
if v.CleanupState == "" {
|
|
violations = append(violations, FieldViolation{Field: "cleanup_state", Code: "required"})
|
|
}
|
|
if len(v.CleanupState) < 1 && v.CleanupState != "" {
|
|
violations = append(violations, FieldViolation{Field: "cleanup_state", Code: "min_length"})
|
|
}
|
|
if len(v.CleanupState) > 64 {
|
|
violations = append(violations, FieldViolation{Field: "cleanup_state", Code: "max_length"})
|
|
}
|
|
if v.IdempotencyKey == "" {
|
|
violations = append(violations, FieldViolation{Field: "idempotency_key", Code: "required"})
|
|
}
|
|
if len(v.IdempotencyKey) < 1 && v.IdempotencyKey != "" {
|
|
violations = append(violations, FieldViolation{Field: "idempotency_key", Code: "min_length"})
|
|
}
|
|
if len(v.IdempotencyKey) > 256 {
|
|
violations = append(violations, FieldViolation{Field: "idempotency_key", Code: "max_length"})
|
|
}
|
|
if v.CorrelationID == "" {
|
|
violations = append(violations, FieldViolation{Field: "correlation_id", Code: "required"})
|
|
}
|
|
if len(v.CorrelationID) < 1 && v.CorrelationID != "" {
|
|
violations = append(violations, FieldViolation{Field: "correlation_id", Code: "min_length"})
|
|
}
|
|
if len(v.CorrelationID) > 128 {
|
|
violations = append(violations, FieldViolation{Field: "correlation_id", Code: "max_length"})
|
|
}
|
|
if v.RequestedAt == "" {
|
|
violations = append(violations, FieldViolation{Field: "requested_at", Code: "required"})
|
|
}
|
|
if len(v.RequestedAt) > 64 {
|
|
violations = append(violations, FieldViolation{Field: "requested_at", Code: "max_length"})
|
|
}
|
|
if v.RequestedAt != "" {
|
|
if parsed, err := time.Parse(time.RFC3339Nano, v.RequestedAt); err != nil || parsed.UTC().Format(time.RFC3339Nano) != v.RequestedAt {
|
|
violations = append(violations, FieldViolation{Field: "requested_at", Code: "invalid_time"})
|
|
}
|
|
}
|
|
if len(v.EndedAt) > 64 {
|
|
violations = append(violations, FieldViolation{Field: "ended_at", Code: "max_length"})
|
|
}
|
|
if v.EndedAt != "" {
|
|
if parsed, err := time.Parse(time.RFC3339Nano, v.EndedAt); err != nil || parsed.UTC().Format(time.RFC3339Nano) != v.EndedAt {
|
|
violations = append(violations, FieldViolation{Field: "ended_at", Code: "invalid_time"})
|
|
}
|
|
}
|
|
if v.Version == 0 {
|
|
violations = append(violations, FieldViolation{Field: "version", Code: "required"})
|
|
}
|
|
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 v.RequestedVideoProfiles == nil {
|
|
violations = append(violations, FieldViolation{Field: "requested_video_profiles", Code: "required"})
|
|
}
|
|
if len(v.RequestedVideoProfiles) < 1 {
|
|
violations = append(violations, FieldViolation{Field: "requested_video_profiles", Code: "min_items"})
|
|
}
|
|
if len(v.RequestedVideoProfiles) > 12 {
|
|
violations = append(violations, FieldViolation{Field: "requested_video_profiles", Code: "max_items"})
|
|
}
|
|
for index, item := range v.RequestedVideoProfiles {
|
|
for prior := 0; prior < index; prior++ {
|
|
if reflect.DeepEqual(item, v.RequestedVideoProfiles[prior]) {
|
|
violations = append(violations, FieldViolation{Field: "requested_video_profiles", Code: "duplicate_item"})
|
|
}
|
|
}
|
|
}
|
|
for index := range v.RequestedVideoProfiles {
|
|
if err := v.RequestedVideoProfiles[index].Validate(); err != nil {
|
|
violations = append(violations, FieldViolation{Field: fmt.Sprintf("requested_video_profiles[%d]", index), Code: "invalid_item"})
|
|
}
|
|
}
|
|
if reflect.DeepEqual(v.RequestedBitratePreference, BitratePreference{}) {
|
|
violations = append(violations, FieldViolation{Field: "requested_bitrate_preference", Code: "required"})
|
|
}
|
|
if err := v.RequestedBitratePreference.Validate(); err != nil {
|
|
violations = append(violations, FieldViolation{Field: "requested_bitrate_preference", Code: "invalid_object"})
|
|
}
|
|
if v.SelectedDescriptor != nil {
|
|
if err := v.SelectedDescriptor.Validate(); err != nil {
|
|
violations = append(violations, FieldViolation{Field: "selected_descriptor", Code: "invalid_object"})
|
|
}
|
|
}
|
|
if len(violations) > 0 {
|
|
return ValidationError{Violations: violations}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func DecodeBrokerSession(data []byte) (BrokerSession, error) {
|
|
var value BrokerSession
|
|
if len(data) > 1024*1024 {
|
|
return value, errors.New("protocol payload exceeds limit")
|
|
}
|
|
if err := rejectDuplicateJSONKeys(data); err != nil {
|
|
return value, err
|
|
}
|
|
var fields map[string]json.RawMessage
|
|
if err := json.Unmarshal(data, &fields); err != nil {
|
|
return value, err
|
|
}
|
|
if raw, ok := fields["cleanup_state"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "cleanup_state", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["correlation_id"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "correlation_id", Code: "required"}}}
|
|
}
|
|
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["idempotency_key"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "idempotency_key", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["policy_snapshot"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "policy_snapshot", Code: "required"}}}
|
|
}
|
|
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["principal_id"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "principal_id", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["requested_at"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "requested_at", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["requested_bitrate_preference"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "requested_bitrate_preference", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["requested_video_profiles"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "requested_video_profiles", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["state"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "state", Code: "required"}}}
|
|
}
|
|
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"}}}
|
|
}
|
|
if raw, ok := fields["selected_descriptor"]; ok && bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "selected_descriptor", Code: "invalid_object"}}}
|
|
}
|
|
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 EncodeBrokerSession(value BrokerSession) ([]byte, error) {
|
|
if err := value.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(value)
|
|
}
|
|
|
|
func (v BrowserAuthenticatedSession) Validate() error {
|
|
var violations []FieldViolation
|
|
if v.Username == "" {
|
|
violations = append(violations, FieldViolation{Field: "username", Code: "required"})
|
|
}
|
|
if len(v.Username) < 1 && v.Username != "" {
|
|
violations = append(violations, FieldViolation{Field: "username", Code: "min_length"})
|
|
}
|
|
if len(v.Username) > 256 {
|
|
violations = append(violations, FieldViolation{Field: "username", Code: "max_length"})
|
|
}
|
|
if v.Provider == "" {
|
|
violations = append(violations, FieldViolation{Field: "provider", Code: "required"})
|
|
}
|
|
if len(v.Provider) < 1 && v.Provider != "" {
|
|
violations = append(violations, FieldViolation{Field: "provider", Code: "min_length"})
|
|
}
|
|
if len(v.Provider) > 64 {
|
|
violations = append(violations, FieldViolation{Field: "provider", Code: "max_length"})
|
|
}
|
|
if v.Roles == nil {
|
|
violations = append(violations, FieldViolation{Field: "roles", Code: "required"})
|
|
}
|
|
if len(v.Roles) > 16 {
|
|
violations = append(violations, FieldViolation{Field: "roles", Code: "max_items"})
|
|
}
|
|
for _, item := range v.Roles {
|
|
if len(item) < 1 {
|
|
violations = append(violations, FieldViolation{Field: "roles", Code: "min_item_length"})
|
|
}
|
|
}
|
|
for _, item := range v.Roles {
|
|
if len(item) > 64 {
|
|
violations = append(violations, FieldViolation{Field: "roles", Code: "max_item_length"})
|
|
}
|
|
}
|
|
for _, item := range v.Roles {
|
|
if len(item) > 64 {
|
|
violations = append(violations, FieldViolation{Field: "roles", Code: "max_item_bytes"})
|
|
}
|
|
}
|
|
if v.Role == "" {
|
|
violations = append(violations, FieldViolation{Field: "role", Code: "required"})
|
|
}
|
|
if v.Role != "" && !(v.Role == "user" || v.Role == "admin") {
|
|
violations = append(violations, FieldViolation{Field: "role", Code: "invalid_value"})
|
|
}
|
|
if len(violations) > 0 {
|
|
return ValidationError{Violations: violations}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func DecodeBrowserAuthenticatedSession(data []byte) (BrowserAuthenticatedSession, error) {
|
|
var value BrowserAuthenticatedSession
|
|
if len(data) > 1024*1024 {
|
|
return value, errors.New("protocol payload exceeds limit")
|
|
}
|
|
if err := rejectDuplicateJSONKeys(data); err != nil {
|
|
return value, err
|
|
}
|
|
var fields map[string]json.RawMessage
|
|
if err := json.Unmarshal(data, &fields); err != nil {
|
|
return value, err
|
|
}
|
|
if raw, ok := fields["provider"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "provider", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["role"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "role", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["roles"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "roles", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["username"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "username", 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 EncodeBrowserAuthenticatedSession(value BrowserAuthenticatedSession) ([]byte, error) {
|
|
if err := value.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(value)
|
|
}
|
|
|
|
func (v CapabilityProfile) Validate() error {
|
|
var violations []FieldViolation
|
|
if v.Transport == "" {
|
|
violations = append(violations, FieldViolation{Field: "transport", Code: "required"})
|
|
}
|
|
if len(v.Transport) < 1 && v.Transport != "" {
|
|
violations = append(violations, FieldViolation{Field: "transport", Code: "min_length"})
|
|
}
|
|
if len(v.Transport) > 64 {
|
|
violations = append(violations, FieldViolation{Field: "transport", Code: "max_length"})
|
|
}
|
|
if v.Framing == "" {
|
|
violations = append(violations, FieldViolation{Field: "framing", Code: "required"})
|
|
}
|
|
if v.Framing != "" && !(v.Framing == "datagram-v1" || v.Framing == "datagram-v2") {
|
|
violations = append(violations, FieldViolation{Field: "framing", Code: "invalid_value"})
|
|
}
|
|
if v.Media == "" {
|
|
violations = append(violations, FieldViolation{Field: "media", Code: "required"})
|
|
}
|
|
if len(v.Media) < 1 && v.Media != "" {
|
|
violations = append(violations, FieldViolation{Field: "media", Code: "min_length"})
|
|
}
|
|
if len(v.Media) > 64 {
|
|
violations = append(violations, FieldViolation{Field: "media", Code: "max_length"})
|
|
}
|
|
if v.SourceRateControl == "" {
|
|
violations = append(violations, FieldViolation{Field: "source_rate_control", Code: "required"})
|
|
}
|
|
if len(v.SourceRateControl) < 1 && v.SourceRateControl != "" {
|
|
violations = append(violations, FieldViolation{Field: "source_rate_control", Code: "min_length"})
|
|
}
|
|
if len(v.SourceRateControl) > 64 {
|
|
violations = append(violations, FieldViolation{Field: "source_rate_control", Code: "max_length"})
|
|
}
|
|
if v.VideoProfiles == nil {
|
|
violations = append(violations, FieldViolation{Field: "video_profiles", Code: "required"})
|
|
}
|
|
if len(v.VideoProfiles) < 1 {
|
|
violations = append(violations, FieldViolation{Field: "video_profiles", Code: "min_items"})
|
|
}
|
|
if len(v.VideoProfiles) > 12 {
|
|
violations = append(violations, FieldViolation{Field: "video_profiles", Code: "max_items"})
|
|
}
|
|
for index, item := range v.VideoProfiles {
|
|
for prior := 0; prior < index; prior++ {
|
|
if reflect.DeepEqual(item, v.VideoProfiles[prior]) {
|
|
violations = append(violations, FieldViolation{Field: "video_profiles", Code: "duplicate_item"})
|
|
}
|
|
}
|
|
}
|
|
for index := range v.VideoProfiles {
|
|
if err := v.VideoProfiles[index].Validate(); err != nil {
|
|
violations = append(violations, FieldViolation{Field: fmt.Sprintf("video_profiles[%d]", index), Code: "invalid_item"})
|
|
}
|
|
}
|
|
if v.AudioProfiles == nil {
|
|
violations = append(violations, FieldViolation{Field: "audio_profiles", Code: "required"})
|
|
}
|
|
if len(v.AudioProfiles) < 1 {
|
|
violations = append(violations, FieldViolation{Field: "audio_profiles", Code: "min_items"})
|
|
}
|
|
if len(v.AudioProfiles) > 1 {
|
|
violations = append(violations, FieldViolation{Field: "audio_profiles", Code: "max_items"})
|
|
}
|
|
for index, item := range v.AudioProfiles {
|
|
for prior := 0; prior < index; prior++ {
|
|
if reflect.DeepEqual(item, v.AudioProfiles[prior]) {
|
|
violations = append(violations, FieldViolation{Field: "audio_profiles", Code: "duplicate_item"})
|
|
}
|
|
}
|
|
}
|
|
for index := range v.AudioProfiles {
|
|
if err := v.AudioProfiles[index].Validate(); err != nil {
|
|
violations = append(violations, FieldViolation{Field: fmt.Sprintf("audio_profiles[%d]", index), Code: "invalid_item"})
|
|
}
|
|
}
|
|
if len(violations) > 0 {
|
|
return ValidationError{Violations: violations}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func DecodeCapabilityProfile(data []byte) (CapabilityProfile, error) {
|
|
var value CapabilityProfile
|
|
if len(data) > 1024*1024 {
|
|
return value, errors.New("protocol payload exceeds limit")
|
|
}
|
|
if err := rejectDuplicateJSONKeys(data); err != nil {
|
|
return value, err
|
|
}
|
|
var fields map[string]json.RawMessage
|
|
if err := json.Unmarshal(data, &fields); err != nil {
|
|
return value, err
|
|
}
|
|
if raw, ok := fields["audio_profiles"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "audio_profiles", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["framing"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "framing", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["media"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "media", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["source_rate_control"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "source_rate_control", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["transport"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "transport", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["video_profiles"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "video_profiles", 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 EncodeCapabilityProfile(value CapabilityProfile) ([]byte, error) {
|
|
if err := value.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(value)
|
|
}
|
|
|
|
func (v ChannelFrame) Validate() error {
|
|
var violations []FieldViolation
|
|
if v.Version == "" {
|
|
violations = append(violations, FieldViolation{Field: "version", Code: "required"})
|
|
}
|
|
if v.Version != "1" && v.Version != "" {
|
|
violations = append(violations, FieldViolation{Field: "version", Code: "invalid_value"})
|
|
}
|
|
if v.FlowID == "" {
|
|
violations = append(violations, FieldViolation{Field: "flow_id", Code: "required"})
|
|
}
|
|
if len(v.FlowID) < 1 && v.FlowID != "" {
|
|
violations = append(violations, FieldViolation{Field: "flow_id", Code: "min_length"})
|
|
}
|
|
if len(v.FlowID) > 64 {
|
|
violations = append(violations, FieldViolation{Field: "flow_id", Code: "max_length"})
|
|
}
|
|
if v.Sequence != 0 && v.Sequence < 0 {
|
|
violations = append(violations, FieldViolation{Field: "sequence", Code: "minimum"})
|
|
}
|
|
if v.Flags != 0 && v.Flags < 0 {
|
|
violations = append(violations, FieldViolation{Field: "flags", Code: "minimum"})
|
|
}
|
|
if v.Flags > 255 {
|
|
violations = append(violations, FieldViolation{Field: "flags", Code: "maximum"})
|
|
}
|
|
if v.FragmentIndex != 0 && v.FragmentIndex < 0 {
|
|
violations = append(violations, FieldViolation{Field: "fragment_index", Code: "minimum"})
|
|
}
|
|
if v.FragmentIndex > 15 {
|
|
violations = append(violations, FieldViolation{Field: "fragment_index", Code: "maximum"})
|
|
}
|
|
if v.FragmentCount == 0 {
|
|
violations = append(violations, FieldViolation{Field: "fragment_count", Code: "required"})
|
|
}
|
|
if v.FragmentCount != 0 && v.FragmentCount < 1 {
|
|
violations = append(violations, FieldViolation{Field: "fragment_count", Code: "minimum"})
|
|
}
|
|
if v.FragmentCount > 16 {
|
|
violations = append(violations, FieldViolation{Field: "fragment_count", Code: "maximum"})
|
|
}
|
|
if v.TimestampMs != 0 && v.TimestampMs < 0 {
|
|
violations = append(violations, FieldViolation{Field: "timestamp_ms", Code: "minimum"})
|
|
}
|
|
if v.Payload == "" {
|
|
violations = append(violations, FieldViolation{Field: "payload", Code: "required"})
|
|
}
|
|
if len(v.Payload) > 87384 {
|
|
violations = append(violations, FieldViolation{Field: "payload", Code: "max_length"})
|
|
}
|
|
if len(v.Payload) > 65536 {
|
|
violations = append(violations, FieldViolation{Field: "payload", Code: "max_bytes"})
|
|
}
|
|
if v.FragmentIndex >= v.FragmentCount {
|
|
violations = append(violations, FieldViolation{Field: "fragment_index", Code: "invalid_order"})
|
|
}
|
|
if len(violations) > 0 {
|
|
return ValidationError{Violations: violations}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func DecodeChannelFrame(data []byte) (ChannelFrame, error) {
|
|
var value ChannelFrame
|
|
if len(data) > 1024*1024 {
|
|
return value, errors.New("protocol payload exceeds limit")
|
|
}
|
|
if err := rejectDuplicateJSONKeys(data); err != nil {
|
|
return value, err
|
|
}
|
|
var fields map[string]json.RawMessage
|
|
if err := json.Unmarshal(data, &fields); err != nil {
|
|
return value, err
|
|
}
|
|
if raw, ok := fields["flags"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "flags", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["flow_id"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "flow_id", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["fragment_count"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "fragment_count", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["fragment_index"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "fragment_index", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["payload"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "payload", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["sequence"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "sequence", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["timestamp_ms"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "timestamp_ms", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["version"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "version", 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 EncodeChannelFrame(value ChannelFrame) ([]byte, error) {
|
|
if err := value.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(value)
|
|
}
|
|
|
|
func (v ClientSessionAuthority) Validate() error {
|
|
var violations []FieldViolation
|
|
if v.Version == "" {
|
|
violations = append(violations, FieldViolation{Field: "version", Code: "required"})
|
|
}
|
|
if v.Version != "1" && v.Version != "" {
|
|
violations = append(violations, FieldViolation{Field: "version", Code: "invalid_value"})
|
|
}
|
|
if v.SessionID == "" {
|
|
violations = append(violations, FieldViolation{Field: "session_id", Code: "required"})
|
|
}
|
|
if len(v.SessionID) < 1 && v.SessionID != "" {
|
|
violations = append(violations, FieldViolation{Field: "session_id", Code: "min_length"})
|
|
}
|
|
if len(v.SessionID) > 128 {
|
|
violations = append(violations, FieldViolation{Field: "session_id", Code: "max_length"})
|
|
}
|
|
if v.GatewayID == "" {
|
|
violations = append(violations, FieldViolation{Field: "gateway_id", Code: "required"})
|
|
}
|
|
if len(v.GatewayID) < 1 && v.GatewayID != "" {
|
|
violations = append(violations, FieldViolation{Field: "gateway_id", Code: "min_length"})
|
|
}
|
|
if len(v.GatewayID) > 128 {
|
|
violations = append(violations, FieldViolation{Field: "gateway_id", Code: "max_length"})
|
|
}
|
|
if v.Audience == "" {
|
|
violations = append(violations, FieldViolation{Field: "audience", Code: "required"})
|
|
}
|
|
if len(v.Audience) < 1 && v.Audience != "" {
|
|
violations = append(violations, FieldViolation{Field: "audience", Code: "min_length"})
|
|
}
|
|
if len(v.Audience) > 256 {
|
|
violations = append(violations, FieldViolation{Field: "audience", Code: "max_length"})
|
|
}
|
|
if v.ReconnectSequence != 0 && v.ReconnectSequence < 0 {
|
|
violations = append(violations, FieldViolation{Field: "reconnect_sequence", Code: "minimum"})
|
|
}
|
|
if v.ExpiresAt == "" {
|
|
violations = append(violations, FieldViolation{Field: "expires_at", Code: "required"})
|
|
}
|
|
if len(v.ExpiresAt) > 64 {
|
|
violations = append(violations, FieldViolation{Field: "expires_at", Code: "max_length"})
|
|
}
|
|
if v.ExpiresAt != "" {
|
|
if parsed, err := time.Parse(time.RFC3339Nano, v.ExpiresAt); err != nil || parsed.UTC().Format(time.RFC3339Nano) != v.ExpiresAt {
|
|
violations = append(violations, FieldViolation{Field: "expires_at", Code: "invalid_time"})
|
|
}
|
|
}
|
|
if reflect.DeepEqual(v.Capabilities, CapabilityProfile{}) {
|
|
violations = append(violations, FieldViolation{Field: "capabilities", Code: "required"})
|
|
}
|
|
if err := v.Capabilities.Validate(); err != nil {
|
|
violations = append(violations, FieldViolation{Field: "capabilities", Code: "invalid_object"})
|
|
}
|
|
if reflect.DeepEqual(v.SelectedDescriptor, SelectedSessionDescriptor{}) {
|
|
violations = append(violations, FieldViolation{Field: "selected_descriptor", Code: "required"})
|
|
}
|
|
if err := v.SelectedDescriptor.Validate(); err != nil {
|
|
violations = append(violations, FieldViolation{Field: "selected_descriptor", Code: "invalid_object"})
|
|
}
|
|
if len(violations) > 0 {
|
|
return ValidationError{Violations: violations}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func DecodeClientSessionAuthority(data []byte) (ClientSessionAuthority, error) {
|
|
var value ClientSessionAuthority
|
|
if len(data) > 1024*1024 {
|
|
return value, errors.New("protocol payload exceeds limit")
|
|
}
|
|
if err := rejectDuplicateJSONKeys(data); err != nil {
|
|
return value, err
|
|
}
|
|
var fields map[string]json.RawMessage
|
|
if err := json.Unmarshal(data, &fields); err != nil {
|
|
return value, err
|
|
}
|
|
if raw, ok := fields["audience"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "audience", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["capabilities"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "capabilities", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["expires_at"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "expires_at", 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["reconnect_sequence"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "reconnect_sequence", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["selected_descriptor"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "selected_descriptor", 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"}}}
|
|
}
|
|
if raw, ok := fields["version"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "version", 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 EncodeClientSessionAuthority(value ClientSessionAuthority) ([]byte, error) {
|
|
if err := value.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(value)
|
|
}
|
|
|
|
func (v ClipboardPolicy) Validate() error {
|
|
var violations []FieldViolation
|
|
if v.MaxTextBytes == 0 {
|
|
violations = append(violations, FieldViolation{Field: "max_text_bytes", Code: "required"})
|
|
}
|
|
if v.MaxTextBytes != 0 && v.MaxTextBytes < 1 {
|
|
violations = append(violations, FieldViolation{Field: "max_text_bytes", Code: "minimum"})
|
|
}
|
|
if v.MaxTextBytes > 65536 {
|
|
violations = append(violations, FieldViolation{Field: "max_text_bytes", Code: "maximum"})
|
|
}
|
|
if v.MaxUpdatesPerMinute == 0 {
|
|
violations = append(violations, FieldViolation{Field: "max_updates_per_minute", Code: "required"})
|
|
}
|
|
if v.MaxUpdatesPerMinute != 0 && v.MaxUpdatesPerMinute < 1 {
|
|
violations = append(violations, FieldViolation{Field: "max_updates_per_minute", Code: "minimum"})
|
|
}
|
|
if v.MaxUpdatesPerMinute > 120 {
|
|
violations = append(violations, FieldViolation{Field: "max_updates_per_minute", Code: "maximum"})
|
|
}
|
|
if len(violations) > 0 {
|
|
return ValidationError{Violations: violations}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func DecodeClipboardPolicy(data []byte) (ClipboardPolicy, error) {
|
|
var value ClipboardPolicy
|
|
if len(data) > 1024*1024 {
|
|
return value, errors.New("protocol payload exceeds limit")
|
|
}
|
|
if err := rejectDuplicateJSONKeys(data); err != nil {
|
|
return value, err
|
|
}
|
|
var fields map[string]json.RawMessage
|
|
if err := json.Unmarshal(data, &fields); err != nil {
|
|
return value, err
|
|
}
|
|
if raw, ok := fields["client_to_provider_enabled"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "client_to_provider_enabled", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["max_text_bytes"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "max_text_bytes", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["max_updates_per_minute"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "max_updates_per_minute", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["provider_to_client_enabled"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "provider_to_client_enabled", 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 EncodeClipboardPolicy(value ClipboardPolicy) ([]byte, error) {
|
|
if err := value.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(value)
|
|
}
|
|
|
|
func (v ClipboardText) Validate() error {
|
|
var violations []FieldViolation
|
|
if v.Text == "" {
|
|
violations = append(violations, FieldViolation{Field: "text", Code: "required"})
|
|
}
|
|
if len(v.Text) > 65536 {
|
|
violations = append(violations, FieldViolation{Field: "text", Code: "max_length"})
|
|
}
|
|
if v.Encoding == "" {
|
|
violations = append(violations, FieldViolation{Field: "encoding", Code: "required"})
|
|
}
|
|
if v.Encoding != "utf-8" && v.Encoding != "" {
|
|
violations = append(violations, FieldViolation{Field: "encoding", Code: "invalid_value"})
|
|
}
|
|
if len(violations) > 0 {
|
|
return ValidationError{Violations: violations}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func DecodeClipboardText(data []byte) (ClipboardText, error) {
|
|
var value ClipboardText
|
|
if len(data) > 1024*1024 {
|
|
return value, errors.New("protocol payload exceeds limit")
|
|
}
|
|
if err := rejectDuplicateJSONKeys(data); err != nil {
|
|
return value, err
|
|
}
|
|
var fields map[string]json.RawMessage
|
|
if err := json.Unmarshal(data, &fields); err != nil {
|
|
return value, err
|
|
}
|
|
if raw, ok := fields["encoding"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "encoding", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["text"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "text", 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 EncodeClipboardText(value ClipboardText) ([]byte, error) {
|
|
if err := value.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(value)
|
|
}
|
|
|
|
func (v ConnectionManifest) Validate() error {
|
|
var violations []FieldViolation
|
|
if v.Version == "" {
|
|
violations = append(violations, FieldViolation{Field: "version", Code: "required"})
|
|
}
|
|
if v.Version != "1" && v.Version != "" {
|
|
violations = append(violations, FieldViolation{Field: "version", Code: "invalid_value"})
|
|
}
|
|
if v.Purpose == "" {
|
|
violations = append(violations, FieldViolation{Field: "purpose", Code: "required"})
|
|
}
|
|
if v.Purpose != "" && !(v.Purpose == "launch" || v.Purpose == "reconnect") {
|
|
violations = append(violations, FieldViolation{Field: "purpose", Code: "invalid_value"})
|
|
}
|
|
if v.SessionID == "" {
|
|
violations = append(violations, FieldViolation{Field: "session_id", Code: "required"})
|
|
}
|
|
if len(v.SessionID) < 1 && v.SessionID != "" {
|
|
violations = append(violations, FieldViolation{Field: "session_id", Code: "min_length"})
|
|
}
|
|
if len(v.SessionID) > 128 {
|
|
violations = append(violations, FieldViolation{Field: "session_id", Code: "max_length"})
|
|
}
|
|
if v.ReconnectSequence != 0 && v.ReconnectSequence < 0 {
|
|
violations = append(violations, FieldViolation{Field: "reconnect_sequence", Code: "minimum"})
|
|
}
|
|
if reflect.DeepEqual(v.Gateway, ManifestGateway{}) {
|
|
violations = append(violations, FieldViolation{Field: "gateway", Code: "required"})
|
|
}
|
|
if err := v.Gateway.Validate(); err != nil {
|
|
violations = append(violations, FieldViolation{Field: "gateway", Code: "invalid_object"})
|
|
}
|
|
if reflect.DeepEqual(v.Tunnel, ManifestTunnel{}) {
|
|
violations = append(violations, FieldViolation{Field: "tunnel", Code: "required"})
|
|
}
|
|
if err := v.Tunnel.Validate(); err != nil {
|
|
violations = append(violations, FieldViolation{Field: "tunnel", Code: "invalid_object"})
|
|
}
|
|
if reflect.DeepEqual(v.Profile, ManifestProfile{}) {
|
|
violations = append(violations, FieldViolation{Field: "profile", Code: "required"})
|
|
}
|
|
if err := v.Profile.Validate(); err != nil {
|
|
violations = append(violations, FieldViolation{Field: "profile", Code: "invalid_object"})
|
|
}
|
|
if reflect.DeepEqual(v.Grant, GrantReference{}) {
|
|
violations = append(violations, FieldViolation{Field: "grant", Code: "required"})
|
|
}
|
|
if err := v.Grant.Validate(); err != nil {
|
|
violations = append(violations, FieldViolation{Field: "grant", Code: "invalid_object"})
|
|
}
|
|
if v.CorrelationID == "" {
|
|
violations = append(violations, FieldViolation{Field: "correlation_id", Code: "required"})
|
|
}
|
|
if len(v.CorrelationID) < 1 && v.CorrelationID != "" {
|
|
violations = append(violations, FieldViolation{Field: "correlation_id", Code: "min_length"})
|
|
}
|
|
if len(v.CorrelationID) > 128 {
|
|
violations = append(violations, FieldViolation{Field: "correlation_id", Code: "max_length"})
|
|
}
|
|
if reflect.DeepEqual(v.SelectedDescriptor, SelectedSessionDescriptor{}) {
|
|
violations = append(violations, FieldViolation{Field: "selected_descriptor", Code: "required"})
|
|
}
|
|
if err := v.SelectedDescriptor.Validate(); err != nil {
|
|
violations = append(violations, FieldViolation{Field: "selected_descriptor", Code: "invalid_object"})
|
|
}
|
|
if len(violations) > 0 {
|
|
return ValidationError{Violations: violations}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func DecodeConnectionManifest(data []byte) (ConnectionManifest, error) {
|
|
var value ConnectionManifest
|
|
if len(data) > 1024*1024 {
|
|
return value, errors.New("protocol payload exceeds limit")
|
|
}
|
|
if err := rejectDuplicateJSONKeys(data); err != nil {
|
|
return value, err
|
|
}
|
|
var fields map[string]json.RawMessage
|
|
if err := json.Unmarshal(data, &fields); err != nil {
|
|
return value, err
|
|
}
|
|
if raw, ok := fields["correlation_id"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "correlation_id", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["gateway"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "gateway", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["grant"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "grant", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["profile"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "profile", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["purpose"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "purpose", 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["selected_descriptor"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "selected_descriptor", 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"}}}
|
|
}
|
|
if raw, ok := fields["tunnel"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "tunnel", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["version"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "version", 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 EncodeConnectionManifest(value ConnectionManifest) ([]byte, error) {
|
|
if err := value.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(value)
|
|
}
|
|
|
|
func (v DeviceChallenge) Validate() error {
|
|
var violations []FieldViolation
|
|
if v.DeviceID == "" {
|
|
violations = append(violations, FieldViolation{Field: "device_id", Code: "required"})
|
|
}
|
|
if len(v.DeviceID) < 1 && v.DeviceID != "" {
|
|
violations = append(violations, FieldViolation{Field: "device_id", Code: "min_length"})
|
|
}
|
|
if len(v.DeviceID) > 128 {
|
|
violations = append(violations, FieldViolation{Field: "device_id", Code: "max_length"})
|
|
}
|
|
if v.ServerID == "" {
|
|
violations = append(violations, FieldViolation{Field: "server_id", Code: "required"})
|
|
}
|
|
if len(v.ServerID) < 1 && v.ServerID != "" {
|
|
violations = append(violations, FieldViolation{Field: "server_id", Code: "min_length"})
|
|
}
|
|
if len(v.ServerID) > 128 {
|
|
violations = append(violations, FieldViolation{Field: "server_id", Code: "max_length"})
|
|
}
|
|
if v.PrincipalID == "" {
|
|
violations = append(violations, FieldViolation{Field: "principal_id", Code: "required"})
|
|
}
|
|
if len(v.PrincipalID) < 1 && v.PrincipalID != "" {
|
|
violations = append(violations, FieldViolation{Field: "principal_id", Code: "min_length"})
|
|
}
|
|
if len(v.PrincipalID) > 128 {
|
|
violations = append(violations, FieldViolation{Field: "principal_id", Code: "max_length"})
|
|
}
|
|
if v.Challenge == "" {
|
|
violations = append(violations, FieldViolation{Field: "challenge", Code: "required"})
|
|
}
|
|
if len(v.Challenge) < 1 && v.Challenge != "" {
|
|
violations = append(violations, FieldViolation{Field: "challenge", Code: "min_length"})
|
|
}
|
|
if len(v.Challenge) > 256 {
|
|
violations = append(violations, FieldViolation{Field: "challenge", Code: "max_length"})
|
|
}
|
|
if v.ExpiresAt == "" {
|
|
violations = append(violations, FieldViolation{Field: "expires_at", Code: "required"})
|
|
}
|
|
if len(v.ExpiresAt) > 64 {
|
|
violations = append(violations, FieldViolation{Field: "expires_at", Code: "max_length"})
|
|
}
|
|
if v.ExpiresAt != "" {
|
|
if parsed, err := time.Parse(time.RFC3339Nano, v.ExpiresAt); err != nil || parsed.UTC().Format(time.RFC3339Nano) != v.ExpiresAt {
|
|
violations = append(violations, FieldViolation{Field: "expires_at", Code: "invalid_time"})
|
|
}
|
|
}
|
|
if v.Algorithm == "" {
|
|
violations = append(violations, FieldViolation{Field: "algorithm", Code: "required"})
|
|
}
|
|
if v.Algorithm != "ed25519" && v.Algorithm != "" {
|
|
violations = append(violations, FieldViolation{Field: "algorithm", Code: "invalid_value"})
|
|
}
|
|
if v.SignatureFormat == "" {
|
|
violations = append(violations, FieldViolation{Field: "signature_format", Code: "required"})
|
|
}
|
|
if v.SignatureFormat != "ed25519-domain-separated-v1" && v.SignatureFormat != "" {
|
|
violations = append(violations, FieldViolation{Field: "signature_format", Code: "invalid_value"})
|
|
}
|
|
if len(violations) > 0 {
|
|
return ValidationError{Violations: violations}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func DecodeDeviceChallenge(data []byte) (DeviceChallenge, error) {
|
|
var value DeviceChallenge
|
|
if len(data) > 1024*1024 {
|
|
return value, errors.New("protocol payload exceeds limit")
|
|
}
|
|
if err := rejectDuplicateJSONKeys(data); err != nil {
|
|
return value, err
|
|
}
|
|
var fields map[string]json.RawMessage
|
|
if err := json.Unmarshal(data, &fields); err != nil {
|
|
return value, err
|
|
}
|
|
if raw, ok := fields["algorithm"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "algorithm", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["challenge"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "challenge", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["device_id"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "device_id", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["expires_at"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "expires_at", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["principal_id"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "principal_id", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["server_id"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "server_id", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["signature_format"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "signature_format", 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 EncodeDeviceChallenge(value DeviceChallenge) ([]byte, error) {
|
|
if err := value.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(value)
|
|
}
|
|
|
|
func (v DeviceProofRequest) Validate() error {
|
|
var violations []FieldViolation
|
|
if v.Challenge == "" {
|
|
violations = append(violations, FieldViolation{Field: "challenge", Code: "required"})
|
|
}
|
|
if len(v.Challenge) < 1 && v.Challenge != "" {
|
|
violations = append(violations, FieldViolation{Field: "challenge", Code: "min_length"})
|
|
}
|
|
if len(v.Challenge) > 256 {
|
|
violations = append(violations, FieldViolation{Field: "challenge", Code: "max_length"})
|
|
}
|
|
if v.Signature == "" {
|
|
violations = append(violations, FieldViolation{Field: "signature", Code: "required"})
|
|
}
|
|
if len(v.Signature) < 1 && v.Signature != "" {
|
|
violations = append(violations, FieldViolation{Field: "signature", Code: "min_length"})
|
|
}
|
|
if len(v.Signature) > 256 {
|
|
violations = append(violations, FieldViolation{Field: "signature", Code: "max_length"})
|
|
}
|
|
if len(violations) > 0 {
|
|
return ValidationError{Violations: violations}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func DecodeDeviceProofRequest(data []byte) (DeviceProofRequest, error) {
|
|
var value DeviceProofRequest
|
|
if len(data) > 1024*1024 {
|
|
return value, errors.New("protocol payload exceeds limit")
|
|
}
|
|
if err := rejectDuplicateJSONKeys(data); err != nil {
|
|
return value, err
|
|
}
|
|
var fields map[string]json.RawMessage
|
|
if err := json.Unmarshal(data, &fields); err != nil {
|
|
return value, err
|
|
}
|
|
if raw, ok := fields["challenge"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "challenge", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["signature"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "signature", 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 EncodeDeviceProofRequest(value DeviceProofRequest) ([]byte, error) {
|
|
if err := value.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(value)
|
|
}
|
|
|
|
func (v DeviceRegistrationRequest) Validate() error {
|
|
var violations []FieldViolation
|
|
if v.Name == "" {
|
|
violations = append(violations, FieldViolation{Field: "name", Code: "required"})
|
|
}
|
|
if len(v.Name) < 1 && v.Name != "" {
|
|
violations = append(violations, FieldViolation{Field: "name", Code: "min_length"})
|
|
}
|
|
if len(v.Name) > 128 {
|
|
violations = append(violations, FieldViolation{Field: "name", Code: "max_length"})
|
|
}
|
|
if v.Platform == "" {
|
|
violations = append(violations, FieldViolation{Field: "platform", Code: "required"})
|
|
}
|
|
if len(v.Platform) < 1 && v.Platform != "" {
|
|
violations = append(violations, FieldViolation{Field: "platform", Code: "min_length"})
|
|
}
|
|
if len(v.Platform) > 64 {
|
|
violations = append(violations, FieldViolation{Field: "platform", Code: "max_length"})
|
|
}
|
|
if v.DeviceSubject == "" {
|
|
violations = append(violations, FieldViolation{Field: "device_subject", Code: "required"})
|
|
}
|
|
if len(v.DeviceSubject) < 1 && v.DeviceSubject != "" {
|
|
violations = append(violations, FieldViolation{Field: "device_subject", Code: "min_length"})
|
|
}
|
|
if len(v.DeviceSubject) > 256 {
|
|
violations = append(violations, FieldViolation{Field: "device_subject", Code: "max_length"})
|
|
}
|
|
if v.Algorithm == "" {
|
|
violations = append(violations, FieldViolation{Field: "algorithm", Code: "required"})
|
|
}
|
|
if v.Algorithm != "ed25519" && v.Algorithm != "" {
|
|
violations = append(violations, FieldViolation{Field: "algorithm", Code: "invalid_value"})
|
|
}
|
|
if v.PublicKey == "" {
|
|
violations = append(violations, FieldViolation{Field: "public_key", Code: "required"})
|
|
}
|
|
if len(v.PublicKey) < 1 && v.PublicKey != "" {
|
|
violations = append(violations, FieldViolation{Field: "public_key", Code: "min_length"})
|
|
}
|
|
if len(v.PublicKey) > 256 {
|
|
violations = append(violations, FieldViolation{Field: "public_key", Code: "max_length"})
|
|
}
|
|
if len(violations) > 0 {
|
|
return ValidationError{Violations: violations}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func DecodeDeviceRegistrationRequest(data []byte) (DeviceRegistrationRequest, error) {
|
|
var value DeviceRegistrationRequest
|
|
if len(data) > 1024*1024 {
|
|
return value, errors.New("protocol payload exceeds limit")
|
|
}
|
|
if err := rejectDuplicateJSONKeys(data); err != nil {
|
|
return value, err
|
|
}
|
|
var fields map[string]json.RawMessage
|
|
if err := json.Unmarshal(data, &fields); err != nil {
|
|
return value, err
|
|
}
|
|
if raw, ok := fields["algorithm"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "algorithm", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["device_subject"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "device_subject", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["name"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "name", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["platform"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "platform", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["public_key"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "public_key", 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 EncodeDeviceRegistrationRequest(value DeviceRegistrationRequest) ([]byte, error) {
|
|
if err := value.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(value)
|
|
}
|
|
|
|
func (v DisplayLimitOverrideRequest) Validate() error {
|
|
var violations []FieldViolation
|
|
if v.ExpectedVersion == 0 {
|
|
violations = append(violations, FieldViolation{Field: "expected_version", Code: "required"})
|
|
}
|
|
if v.ExpectedVersion != 0 && v.ExpectedVersion < 1 {
|
|
violations = append(violations, FieldViolation{Field: "expected_version", Code: "minimum"})
|
|
}
|
|
if len(violations) > 0 {
|
|
return ValidationError{Violations: violations}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func DecodeDisplayLimitOverrideRequest(data []byte) (DisplayLimitOverrideRequest, error) {
|
|
var value DisplayLimitOverrideRequest
|
|
if len(data) > 1024*1024 {
|
|
return value, errors.New("protocol payload exceeds limit")
|
|
}
|
|
if err := rejectDuplicateJSONKeys(data); err != nil {
|
|
return value, err
|
|
}
|
|
var fields map[string]json.RawMessage
|
|
if err := json.Unmarshal(data, &fields); err != nil {
|
|
return value, err
|
|
}
|
|
if raw, ok := fields["allow_display_limit_override"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "allow_display_limit_override", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["expected_version"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "expected_version", 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 EncodeDisplayLimitOverrideRequest(value DisplayLimitOverrideRequest) ([]byte, error) {
|
|
if err := value.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
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")
|
|
}
|
|
if err := rejectDuplicateJSONKeys(data); err != nil {
|
|
return value, err
|
|
}
|
|
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 == "" {
|
|
violations = append(violations, FieldViolation{Field: "pool_id", Code: "required"})
|
|
}
|
|
if len(v.PoolID) < 1 && v.PoolID != "" {
|
|
violations = append(violations, FieldViolation{Field: "pool_id", Code: "min_length"})
|
|
}
|
|
if len(v.PoolID) > 128 {
|
|
violations = append(violations, FieldViolation{Field: "pool_id", Code: "max_length"})
|
|
}
|
|
if v.Name == "" {
|
|
violations = append(violations, FieldViolation{Field: "name", Code: "required"})
|
|
}
|
|
if len(v.Name) < 1 && v.Name != "" {
|
|
violations = append(violations, FieldViolation{Field: "name", Code: "min_length"})
|
|
}
|
|
if len(v.Name) > 256 {
|
|
violations = append(violations, FieldViolation{Field: "name", Code: "max_length"})
|
|
}
|
|
if v.AssignmentState == "" {
|
|
violations = append(violations, FieldViolation{Field: "assignment_state", Code: "required"})
|
|
}
|
|
if len(v.AssignmentState) < 1 && v.AssignmentState != "" {
|
|
violations = append(violations, FieldViolation{Field: "assignment_state", Code: "min_length"})
|
|
}
|
|
if len(v.AssignmentState) > 64 {
|
|
violations = append(violations, FieldViolation{Field: "assignment_state", Code: "max_length"})
|
|
}
|
|
if reflect.DeepEqual(v.QualityLimits, SessionQualityLimits{}) {
|
|
violations = append(violations, FieldViolation{Field: "quality_limits", Code: "required"})
|
|
}
|
|
if err := v.QualityLimits.Validate(); err != nil {
|
|
violations = append(violations, FieldViolation{Field: "quality_limits", Code: "invalid_object"})
|
|
}
|
|
if len(violations) > 0 {
|
|
return ValidationError{Violations: violations}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func DecodeEntitledPool(data []byte) (EntitledPool, error) {
|
|
var value EntitledPool
|
|
if len(data) > 1024*1024 {
|
|
return value, errors.New("protocol payload exceeds limit")
|
|
}
|
|
if err := rejectDuplicateJSONKeys(data); err != nil {
|
|
return value, err
|
|
}
|
|
var fields map[string]json.RawMessage
|
|
if err := json.Unmarshal(data, &fields); err != nil {
|
|
return value, err
|
|
}
|
|
if raw, ok := fields["assignment_state"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "assignment_state", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["name"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "name", Code: "required"}}}
|
|
}
|
|
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["quality_limits"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "quality_limits", 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 EncodeEntitledPool(value EntitledPool) ([]byte, error) {
|
|
if err := value.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(value)
|
|
}
|
|
|
|
func (v ErrorEnvelope) Validate() error {
|
|
var violations []FieldViolation
|
|
if v.Error == "" {
|
|
violations = append(violations, FieldViolation{Field: "error", Code: "required"})
|
|
}
|
|
if len(v.Error) < 1 && v.Error != "" {
|
|
violations = append(violations, FieldViolation{Field: "error", Code: "min_length"})
|
|
}
|
|
if len(v.Error) > 512 {
|
|
violations = append(violations, FieldViolation{Field: "error", Code: "max_length"})
|
|
}
|
|
if v.Code == "" {
|
|
violations = append(violations, FieldViolation{Field: "code", Code: "required"})
|
|
}
|
|
if len(v.Code) < 1 && v.Code != "" {
|
|
violations = append(violations, FieldViolation{Field: "code", Code: "min_length"})
|
|
}
|
|
if len(v.Code) > 128 {
|
|
violations = append(violations, FieldViolation{Field: "code", Code: "max_length"})
|
|
}
|
|
if v.Message == "" {
|
|
violations = append(violations, FieldViolation{Field: "message", Code: "required"})
|
|
}
|
|
if len(v.Message) < 1 && v.Message != "" {
|
|
violations = append(violations, FieldViolation{Field: "message", Code: "min_length"})
|
|
}
|
|
if len(v.Message) > 512 {
|
|
violations = append(violations, FieldViolation{Field: "message", Code: "max_length"})
|
|
}
|
|
if v.Resolution == "" {
|
|
violations = append(violations, FieldViolation{Field: "resolution", Code: "required"})
|
|
}
|
|
if len(v.Resolution) < 1 && v.Resolution != "" {
|
|
violations = append(violations, FieldViolation{Field: "resolution", Code: "min_length"})
|
|
}
|
|
if len(v.Resolution) > 128 {
|
|
violations = append(violations, FieldViolation{Field: "resolution", Code: "max_length"})
|
|
}
|
|
if v.RequestID == "" {
|
|
violations = append(violations, FieldViolation{Field: "request_id", Code: "required"})
|
|
}
|
|
if len(v.RequestID) < 1 && v.RequestID != "" {
|
|
violations = append(violations, FieldViolation{Field: "request_id", Code: "min_length"})
|
|
}
|
|
if len(v.RequestID) > 128 {
|
|
violations = append(violations, FieldViolation{Field: "request_id", Code: "max_length"})
|
|
}
|
|
if v.Violations == nil {
|
|
violations = append(violations, FieldViolation{Field: "violations", Code: "required"})
|
|
}
|
|
if len(v.Violations) > 16 {
|
|
violations = append(violations, FieldViolation{Field: "violations", Code: "max_items"})
|
|
}
|
|
for index := range v.Violations {
|
|
if err := v.Violations[index].Validate(); err != nil {
|
|
violations = append(violations, FieldViolation{Field: fmt.Sprintf("violations[%d]", index), Code: "invalid_item"})
|
|
}
|
|
}
|
|
if len(violations) > 0 {
|
|
return ValidationError{Violations: violations}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func DecodeErrorEnvelope(data []byte) (ErrorEnvelope, error) {
|
|
var value ErrorEnvelope
|
|
if len(data) > 1024*1024 {
|
|
return value, errors.New("protocol payload exceeds limit")
|
|
}
|
|
if err := rejectDuplicateJSONKeys(data); err != nil {
|
|
return value, err
|
|
}
|
|
var fields map[string]json.RawMessage
|
|
if err := json.Unmarshal(data, &fields); err != nil {
|
|
return value, err
|
|
}
|
|
if raw, ok := fields["code"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "code", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["error"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "error", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["message"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "message", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["request_id"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "request_id", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["resolution"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "resolution", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["status"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "status", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["violations"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "violations", 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 EncodeErrorEnvelope(value ErrorEnvelope) ([]byte, error) {
|
|
if err := value.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(value)
|
|
}
|
|
|
|
func (v EventEnvelope) Validate() error {
|
|
var violations []FieldViolation
|
|
if v.EventID == "" {
|
|
violations = append(violations, FieldViolation{Field: "event_id", Code: "required"})
|
|
}
|
|
if len(v.EventID) < 1 && v.EventID != "" {
|
|
violations = append(violations, FieldViolation{Field: "event_id", Code: "min_length"})
|
|
}
|
|
if len(v.EventID) > 128 {
|
|
violations = append(violations, FieldViolation{Field: "event_id", Code: "max_length"})
|
|
}
|
|
if v.Sequence == 0 {
|
|
violations = append(violations, FieldViolation{Field: "sequence", Code: "required"})
|
|
}
|
|
if v.Sequence != 0 && v.Sequence < 1 {
|
|
violations = append(violations, FieldViolation{Field: "sequence", Code: "minimum"})
|
|
}
|
|
if v.Type == "" {
|
|
violations = append(violations, FieldViolation{Field: "type", Code: "required"})
|
|
}
|
|
if len(v.Type) < 1 && v.Type != "" {
|
|
violations = append(violations, FieldViolation{Field: "type", Code: "min_length"})
|
|
}
|
|
if len(v.Type) > 128 {
|
|
violations = append(violations, FieldViolation{Field: "type", Code: "max_length"})
|
|
}
|
|
if v.Version == 0 {
|
|
violations = append(violations, FieldViolation{Field: "version", Code: "required"})
|
|
}
|
|
if v.Version != 0 && v.Version < 1 {
|
|
violations = append(violations, FieldViolation{Field: "version", Code: "minimum"})
|
|
}
|
|
if reflect.DeepEqual(v.Resource, ResourceLink{}) {
|
|
violations = append(violations, FieldViolation{Field: "resource", Code: "required"})
|
|
}
|
|
if err := v.Resource.Validate(); err != nil {
|
|
violations = append(violations, FieldViolation{Field: "resource", Code: "invalid_object"})
|
|
}
|
|
if v.OccurredAt == "" {
|
|
violations = append(violations, FieldViolation{Field: "occurred_at", Code: "required"})
|
|
}
|
|
if len(v.OccurredAt) > 64 {
|
|
violations = append(violations, FieldViolation{Field: "occurred_at", Code: "max_length"})
|
|
}
|
|
if v.OccurredAt != "" {
|
|
if parsed, err := time.Parse(time.RFC3339Nano, v.OccurredAt); err != nil || parsed.UTC().Format(time.RFC3339Nano) != v.OccurredAt {
|
|
violations = append(violations, FieldViolation{Field: "occurred_at", Code: "invalid_time"})
|
|
}
|
|
}
|
|
if v.CorrelationID == "" {
|
|
violations = append(violations, FieldViolation{Field: "correlation_id", Code: "required"})
|
|
}
|
|
if len(v.CorrelationID) < 1 && v.CorrelationID != "" {
|
|
violations = append(violations, FieldViolation{Field: "correlation_id", Code: "min_length"})
|
|
}
|
|
if len(v.CorrelationID) > 128 {
|
|
violations = append(violations, FieldViolation{Field: "correlation_id", Code: "max_length"})
|
|
}
|
|
if v.Payload == nil {
|
|
violations = append(violations, FieldViolation{Field: "payload", Code: "required"})
|
|
}
|
|
if len(violations) > 0 {
|
|
return ValidationError{Violations: violations}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func DecodeEventEnvelope(data []byte) (EventEnvelope, error) {
|
|
var value EventEnvelope
|
|
if len(data) > 1024*1024 {
|
|
return value, errors.New("protocol payload exceeds limit")
|
|
}
|
|
if err := rejectDuplicateJSONKeys(data); err != nil {
|
|
return value, err
|
|
}
|
|
var fields map[string]json.RawMessage
|
|
if err := json.Unmarshal(data, &fields); err != nil {
|
|
return value, err
|
|
}
|
|
if raw, ok := fields["correlation_id"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "correlation_id", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["event_id"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "event_id", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["occurred_at"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "occurred_at", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["payload"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "payload", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["resource"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "resource", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["sequence"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "sequence", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["type"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "type", Code: "required"}}}
|
|
}
|
|
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["payload"]; ok && len(raw) > 16384 {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "payload", Code: "max_bytes"}}}
|
|
}
|
|
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 EncodeEventEnvelope(value EventEnvelope) ([]byte, error) {
|
|
if err := value.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(value)
|
|
}
|
|
|
|
func (v EventResume) Validate() error {
|
|
var violations []FieldViolation
|
|
if v.Cursor == "" {
|
|
violations = append(violations, FieldViolation{Field: "cursor", Code: "required"})
|
|
}
|
|
if len(v.Cursor) > 512 {
|
|
violations = append(violations, FieldViolation{Field: "cursor", Code: "max_length"})
|
|
}
|
|
if v.LastSequence != 0 && v.LastSequence < 0 {
|
|
violations = append(violations, FieldViolation{Field: "last_sequence", Code: "minimum"})
|
|
}
|
|
if len(violations) > 0 {
|
|
return ValidationError{Violations: violations}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func DecodeEventResume(data []byte) (EventResume, error) {
|
|
var value EventResume
|
|
if len(data) > 1024*1024 {
|
|
return value, errors.New("protocol payload exceeds limit")
|
|
}
|
|
if err := rejectDuplicateJSONKeys(data); err != nil {
|
|
return value, err
|
|
}
|
|
var fields map[string]json.RawMessage
|
|
if err := json.Unmarshal(data, &fields); err != nil {
|
|
return value, err
|
|
}
|
|
if raw, ok := fields["cursor"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "cursor", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["last_sequence"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "last_sequence", 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 EncodeEventResume(value EventResume) ([]byte, error) {
|
|
if err := value.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(value)
|
|
}
|
|
|
|
func (v FieldViolation) Validate() error {
|
|
var violations []FieldViolation
|
|
if v.Field == "" {
|
|
violations = append(violations, FieldViolation{Field: "field", Code: "required"})
|
|
}
|
|
if len(v.Field) < 1 && v.Field != "" {
|
|
violations = append(violations, FieldViolation{Field: "field", Code: "min_length"})
|
|
}
|
|
if len(v.Field) > 128 {
|
|
violations = append(violations, FieldViolation{Field: "field", Code: "max_length"})
|
|
}
|
|
if v.Code == "" {
|
|
violations = append(violations, FieldViolation{Field: "code", Code: "required"})
|
|
}
|
|
if len(v.Code) < 1 && v.Code != "" {
|
|
violations = append(violations, FieldViolation{Field: "code", Code: "min_length"})
|
|
}
|
|
if len(v.Code) > 64 {
|
|
violations = append(violations, FieldViolation{Field: "code", Code: "max_length"})
|
|
}
|
|
if len(violations) > 0 {
|
|
return ValidationError{Violations: violations}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func DecodeFieldViolation(data []byte) (FieldViolation, error) {
|
|
var value FieldViolation
|
|
if len(data) > 1024*1024 {
|
|
return value, errors.New("protocol payload exceeds limit")
|
|
}
|
|
if err := rejectDuplicateJSONKeys(data); err != nil {
|
|
return value, err
|
|
}
|
|
var fields map[string]json.RawMessage
|
|
if err := json.Unmarshal(data, &fields); err != nil {
|
|
return value, err
|
|
}
|
|
if raw, ok := fields["code"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "code", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["field"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "field", 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 EncodeFieldViolation(value FieldViolation) ([]byte, error) {
|
|
if err := value.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(value)
|
|
}
|
|
|
|
func (v GatewayClipboardAudit) Validate() error {
|
|
var violations []FieldViolation
|
|
if v.Version == "" {
|
|
violations = append(violations, FieldViolation{Field: "version", Code: "required"})
|
|
}
|
|
if v.Version != "1" && v.Version != "" {
|
|
violations = append(violations, FieldViolation{Field: "version", Code: "invalid_value"})
|
|
}
|
|
if v.SessionID == "" {
|
|
violations = append(violations, FieldViolation{Field: "session_id", Code: "required"})
|
|
}
|
|
if len(v.SessionID) < 1 && v.SessionID != "" {
|
|
violations = append(violations, FieldViolation{Field: "session_id", Code: "min_length"})
|
|
}
|
|
if len(v.SessionID) > 128 {
|
|
violations = append(violations, FieldViolation{Field: "session_id", Code: "max_length"})
|
|
}
|
|
if v.Direction == "" {
|
|
violations = append(violations, FieldViolation{Field: "direction", Code: "required"})
|
|
}
|
|
if v.Direction != "" && !(v.Direction == "client_to_provider" || v.Direction == "provider_to_client") {
|
|
violations = append(violations, FieldViolation{Field: "direction", Code: "invalid_value"})
|
|
}
|
|
if v.Outcome == "" {
|
|
violations = append(violations, FieldViolation{Field: "outcome", Code: "required"})
|
|
}
|
|
if v.Outcome != "" && !(v.Outcome == "forwarded" || v.Outcome == "suppressed" || v.Outcome == "rejected") {
|
|
violations = append(violations, FieldViolation{Field: "outcome", Code: "invalid_value"})
|
|
}
|
|
if v.TextBytes != 0 && v.TextBytes < 0 {
|
|
violations = append(violations, FieldViolation{Field: "text_bytes", Code: "minimum"})
|
|
}
|
|
if v.TextBytes > 65536 {
|
|
violations = append(violations, FieldViolation{Field: "text_bytes", Code: "maximum"})
|
|
}
|
|
if v.Reason == "" {
|
|
violations = append(violations, FieldViolation{Field: "reason", Code: "required"})
|
|
}
|
|
if v.Reason != "" && !(v.Reason == "forwarded" || v.Reason == "loop" || v.Reason == "policy" || v.Reason == "rate" || v.Reason == "provider" || v.Reason == "malformed") {
|
|
violations = append(violations, FieldViolation{Field: "reason", Code: "invalid_value"})
|
|
}
|
|
if len(violations) > 0 {
|
|
return ValidationError{Violations: violations}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func DecodeGatewayClipboardAudit(data []byte) (GatewayClipboardAudit, error) {
|
|
var value GatewayClipboardAudit
|
|
if len(data) > 1024*1024 {
|
|
return value, errors.New("protocol payload exceeds limit")
|
|
}
|
|
if err := rejectDuplicateJSONKeys(data); err != nil {
|
|
return value, err
|
|
}
|
|
var fields map[string]json.RawMessage
|
|
if err := json.Unmarshal(data, &fields); err != nil {
|
|
return value, err
|
|
}
|
|
if raw, ok := fields["direction"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "direction", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["outcome"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "outcome", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["reason"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "reason", 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"}}}
|
|
}
|
|
if raw, ok := fields["text_bytes"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "text_bytes", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["version"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "version", 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 EncodeGatewayClipboardAudit(value GatewayClipboardAudit) ([]byte, error) {
|
|
if err := value.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(value)
|
|
}
|
|
|
|
func (v GatewayClipboardText) Validate() error {
|
|
var violations []FieldViolation
|
|
if v.Direction == "" {
|
|
violations = append(violations, FieldViolation{Field: "direction", Code: "required"})
|
|
}
|
|
if v.Direction != "" && !(v.Direction == "client_to_provider" || v.Direction == "provider_to_client") {
|
|
violations = append(violations, FieldViolation{Field: "direction", Code: "invalid_value"})
|
|
}
|
|
if v.Text == "" {
|
|
violations = append(violations, FieldViolation{Field: "text", Code: "required"})
|
|
}
|
|
if len(v.Text) > 65536 {
|
|
violations = append(violations, FieldViolation{Field: "text", Code: "max_length"})
|
|
}
|
|
if len(v.Text) > 65536 {
|
|
violations = append(violations, FieldViolation{Field: "text", Code: "max_bytes"})
|
|
}
|
|
if v.Encoding == "" {
|
|
violations = append(violations, FieldViolation{Field: "encoding", Code: "required"})
|
|
}
|
|
if v.Encoding != "utf-8" && v.Encoding != "" {
|
|
violations = append(violations, FieldViolation{Field: "encoding", Code: "invalid_value"})
|
|
}
|
|
if v.LoopToken == "" {
|
|
violations = append(violations, FieldViolation{Field: "loop_token", Code: "required"})
|
|
}
|
|
if len(v.LoopToken) < 16 && v.LoopToken != "" {
|
|
violations = append(violations, FieldViolation{Field: "loop_token", Code: "min_length"})
|
|
}
|
|
if len(v.LoopToken) > 128 {
|
|
violations = append(violations, FieldViolation{Field: "loop_token", Code: "max_length"})
|
|
}
|
|
if v.LoopToken != "" {
|
|
if _, err := base64.RawURLEncoding.Strict().DecodeString(v.LoopToken); err != nil {
|
|
violations = append(violations, FieldViolation{Field: "loop_token", Code: "invalid_format"})
|
|
}
|
|
}
|
|
if len(violations) > 0 {
|
|
return ValidationError{Violations: violations}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func DecodeGatewayClipboardText(data []byte) (GatewayClipboardText, error) {
|
|
var value GatewayClipboardText
|
|
if len(data) > 1024*1024 {
|
|
return value, errors.New("protocol payload exceeds limit")
|
|
}
|
|
if err := rejectDuplicateJSONKeys(data); err != nil {
|
|
return value, err
|
|
}
|
|
var fields map[string]json.RawMessage
|
|
if err := json.Unmarshal(data, &fields); err != nil {
|
|
return value, err
|
|
}
|
|
if raw, ok := fields["direction"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "direction", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["encoding"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "encoding", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["loop_token"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "loop_token", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["text"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "text", 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 EncodeGatewayClipboardText(value GatewayClipboardText) ([]byte, error) {
|
|
if err := value.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(value)
|
|
}
|
|
|
|
func (v GatewayDrain) Validate() error {
|
|
var violations []FieldViolation
|
|
if v.Version == "" {
|
|
violations = append(violations, FieldViolation{Field: "version", Code: "required"})
|
|
}
|
|
if v.Version != "1" && v.Version != "" {
|
|
violations = append(violations, FieldViolation{Field: "version", Code: "invalid_value"})
|
|
}
|
|
if v.GatewayID == "" {
|
|
violations = append(violations, FieldViolation{Field: "gateway_id", Code: "required"})
|
|
}
|
|
if len(v.GatewayID) < 1 && v.GatewayID != "" {
|
|
violations = append(violations, FieldViolation{Field: "gateway_id", Code: "min_length"})
|
|
}
|
|
if len(v.GatewayID) > 128 {
|
|
violations = append(violations, FieldViolation{Field: "gateway_id", Code: "max_length"})
|
|
}
|
|
if v.Sequence == 0 {
|
|
violations = append(violations, FieldViolation{Field: "sequence", Code: "required"})
|
|
}
|
|
if v.Sequence != 0 && v.Sequence < 1 {
|
|
violations = append(violations, FieldViolation{Field: "sequence", Code: "minimum"})
|
|
}
|
|
if v.Reason == "" {
|
|
violations = append(violations, FieldViolation{Field: "reason", Code: "required"})
|
|
}
|
|
if len(v.Reason) < 1 && v.Reason != "" {
|
|
violations = append(violations, FieldViolation{Field: "reason", Code: "min_length"})
|
|
}
|
|
if len(v.Reason) > 256 {
|
|
violations = append(violations, FieldViolation{Field: "reason", Code: "max_length"})
|
|
}
|
|
if v.Deadline == "" {
|
|
violations = append(violations, FieldViolation{Field: "deadline", Code: "required"})
|
|
}
|
|
if len(v.Deadline) > 64 {
|
|
violations = append(violations, FieldViolation{Field: "deadline", Code: "max_length"})
|
|
}
|
|
if v.Deadline != "" {
|
|
if parsed, err := time.Parse(time.RFC3339Nano, v.Deadline); err != nil || parsed.UTC().Format(time.RFC3339Nano) != v.Deadline {
|
|
violations = append(violations, FieldViolation{Field: "deadline", Code: "invalid_time"})
|
|
}
|
|
}
|
|
if len(violations) > 0 {
|
|
return ValidationError{Violations: violations}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func DecodeGatewayDrain(data []byte) (GatewayDrain, error) {
|
|
var value GatewayDrain
|
|
if len(data) > 1024*1024 {
|
|
return value, errors.New("protocol payload exceeds limit")
|
|
}
|
|
if err := rejectDuplicateJSONKeys(data); err != nil {
|
|
return value, err
|
|
}
|
|
var fields map[string]json.RawMessage
|
|
if err := json.Unmarshal(data, &fields); err != nil {
|
|
return value, err
|
|
}
|
|
if raw, ok := fields["deadline"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "deadline", 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["reason"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "reason", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["sequence"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "sequence", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["version"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "version", 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 EncodeGatewayDrain(value GatewayDrain) ([]byte, error) {
|
|
if err := value.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(value)
|
|
}
|
|
|
|
func (v GatewayHeartbeat) Validate() error {
|
|
var violations []FieldViolation
|
|
if v.Version == "" {
|
|
violations = append(violations, FieldViolation{Field: "version", Code: "required"})
|
|
}
|
|
if v.Version != "1" && v.Version != "" {
|
|
violations = append(violations, FieldViolation{Field: "version", Code: "invalid_value"})
|
|
}
|
|
if v.GatewayID == "" {
|
|
violations = append(violations, FieldViolation{Field: "gateway_id", Code: "required"})
|
|
}
|
|
if len(v.GatewayID) < 1 && v.GatewayID != "" {
|
|
violations = append(violations, FieldViolation{Field: "gateway_id", Code: "min_length"})
|
|
}
|
|
if len(v.GatewayID) > 128 {
|
|
violations = append(violations, FieldViolation{Field: "gateway_id", Code: "max_length"})
|
|
}
|
|
if v.Sequence == 0 {
|
|
violations = append(violations, FieldViolation{Field: "sequence", Code: "required"})
|
|
}
|
|
if v.Sequence != 0 && v.Sequence < 1 {
|
|
violations = append(violations, FieldViolation{Field: "sequence", Code: "minimum"})
|
|
}
|
|
if v.ObservedAt == "" {
|
|
violations = append(violations, FieldViolation{Field: "observed_at", Code: "required"})
|
|
}
|
|
if len(v.ObservedAt) > 64 {
|
|
violations = append(violations, FieldViolation{Field: "observed_at", Code: "max_length"})
|
|
}
|
|
if v.ObservedAt != "" {
|
|
if parsed, err := time.Parse(time.RFC3339Nano, v.ObservedAt); err != nil || parsed.UTC().Format(time.RFC3339Nano) != v.ObservedAt {
|
|
violations = append(violations, FieldViolation{Field: "observed_at", Code: "invalid_time"})
|
|
}
|
|
}
|
|
if v.ActiveConnections != 0 && v.ActiveConnections < 0 {
|
|
violations = append(violations, FieldViolation{Field: "active_connections", Code: "minimum"})
|
|
}
|
|
if v.ActiveConnections > 1000000 {
|
|
violations = append(violations, FieldViolation{Field: "active_connections", Code: "maximum"})
|
|
}
|
|
if v.EgressKbps != 0 && v.EgressKbps < 0 {
|
|
violations = append(violations, FieldViolation{Field: "egress_kbps", Code: "minimum"})
|
|
}
|
|
if v.EgressKbps > 1000000000 {
|
|
violations = append(violations, FieldViolation{Field: "egress_kbps", Code: "maximum"})
|
|
}
|
|
if v.State == "" {
|
|
violations = append(violations, FieldViolation{Field: "state", Code: "required"})
|
|
}
|
|
if v.State != "" && !(v.State == "ready" || v.State == "draining" || v.State == "offline") {
|
|
violations = append(violations, FieldViolation{Field: "state", Code: "invalid_value"})
|
|
}
|
|
if reflect.DeepEqual(v.Telemetry, GatewayTelemetry{}) {
|
|
violations = append(violations, FieldViolation{Field: "telemetry", Code: "required"})
|
|
}
|
|
if err := v.Telemetry.Validate(); err != nil {
|
|
violations = append(violations, FieldViolation{Field: "telemetry", Code: "invalid_object"})
|
|
}
|
|
if len(violations) > 0 {
|
|
return ValidationError{Violations: violations}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func DecodeGatewayHeartbeat(data []byte) (GatewayHeartbeat, error) {
|
|
var value GatewayHeartbeat
|
|
if len(data) > 1024*1024 {
|
|
return value, errors.New("protocol payload exceeds limit")
|
|
}
|
|
if err := rejectDuplicateJSONKeys(data); err != nil {
|
|
return value, err
|
|
}
|
|
var fields map[string]json.RawMessage
|
|
if err := json.Unmarshal(data, &fields); err != nil {
|
|
return value, err
|
|
}
|
|
if raw, ok := fields["active_connections"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "active_connections", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["egress_kbps"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "egress_kbps", 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["observed_at"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "observed_at", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["sequence"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "sequence", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["state"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "state", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["telemetry"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "telemetry", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["version"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "version", 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 EncodeGatewayHeartbeat(value GatewayHeartbeat) ([]byte, error) {
|
|
if err := value.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(value)
|
|
}
|
|
|
|
func (v GatewayQualityAck) Validate() error {
|
|
var violations []FieldViolation
|
|
if v.Version == "" {
|
|
violations = append(violations, FieldViolation{Field: "version", Code: "required"})
|
|
}
|
|
if v.Version != "1" && v.Version != "" {
|
|
violations = append(violations, FieldViolation{Field: "version", Code: "invalid_value"})
|
|
}
|
|
if v.SessionID == "" {
|
|
violations = append(violations, FieldViolation{Field: "session_id", Code: "required"})
|
|
}
|
|
if len(v.SessionID) < 1 && v.SessionID != "" {
|
|
violations = append(violations, FieldViolation{Field: "session_id", Code: "min_length"})
|
|
}
|
|
if len(v.SessionID) > 128 {
|
|
violations = append(violations, FieldViolation{Field: "session_id", Code: "max_length"})
|
|
}
|
|
if v.GatewayID == "" {
|
|
violations = append(violations, FieldViolation{Field: "gateway_id", Code: "required"})
|
|
}
|
|
if len(v.GatewayID) < 1 && v.GatewayID != "" {
|
|
violations = append(violations, FieldViolation{Field: "gateway_id", Code: "min_length"})
|
|
}
|
|
if len(v.GatewayID) > 128 {
|
|
violations = append(violations, FieldViolation{Field: "gateway_id", Code: "max_length"})
|
|
}
|
|
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 len(v.OperationID) < 36 && v.OperationID != "" {
|
|
violations = append(violations, FieldViolation{Field: "operation_id", Code: "min_length"})
|
|
}
|
|
if len(v.OperationID) > 36 {
|
|
violations = append(violations, FieldViolation{Field: "operation_id", Code: "max_length"})
|
|
}
|
|
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 {
|
|
violations = append(violations, FieldViolation{Field: "revision", 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") {
|
|
violations = append(violations, FieldViolation{Field: "outcome", Code: "invalid_value"})
|
|
}
|
|
if v.CurrentAppliedRevision != 0 && v.CurrentAppliedRevision < 0 {
|
|
violations = append(violations, FieldViolation{Field: "current_applied_revision", Code: "minimum"})
|
|
}
|
|
if len(v.FailureCode) < 1 && v.FailureCode != "" {
|
|
violations = append(violations, FieldViolation{Field: "failure_code", Code: "min_length"})
|
|
}
|
|
if len(v.FailureCode) > 128 {
|
|
violations = append(violations, FieldViolation{Field: "failure_code", Code: "max_length"})
|
|
}
|
|
if len(violations) > 0 {
|
|
return ValidationError{Violations: violations}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func DecodeGatewayQualityAck(data []byte) (GatewayQualityAck, error) {
|
|
var value GatewayQualityAck
|
|
if len(data) > 1024*1024 {
|
|
return value, errors.New("protocol payload exceeds limit")
|
|
}
|
|
if err := rejectDuplicateJSONKeys(data); err != nil {
|
|
return value, err
|
|
}
|
|
var fields map[string]json.RawMessage
|
|
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["operation_id"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "operation_id", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["outcome"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "outcome", 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"}}}
|
|
}
|
|
if raw, ok := fields["version"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "version", 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 EncodeGatewayQualityAck(value GatewayQualityAck) ([]byte, error) {
|
|
if err := value.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(value)
|
|
}
|
|
|
|
func (v GatewayQualityWork) Validate() error {
|
|
var violations []FieldViolation
|
|
if v.Version == "" {
|
|
violations = append(violations, FieldViolation{Field: "version", Code: "required"})
|
|
}
|
|
if v.Version != "1" && v.Version != "" {
|
|
violations = append(violations, FieldViolation{Field: "version", Code: "invalid_value"})
|
|
}
|
|
if v.SessionID == "" {
|
|
violations = append(violations, FieldViolation{Field: "session_id", Code: "required"})
|
|
}
|
|
if len(v.SessionID) < 1 && v.SessionID != "" {
|
|
violations = append(violations, FieldViolation{Field: "session_id", Code: "min_length"})
|
|
}
|
|
if len(v.SessionID) > 128 {
|
|
violations = append(violations, FieldViolation{Field: "session_id", Code: "max_length"})
|
|
}
|
|
if v.GatewayID == "" {
|
|
violations = append(violations, FieldViolation{Field: "gateway_id", Code: "required"})
|
|
}
|
|
if len(v.GatewayID) < 1 && v.GatewayID != "" {
|
|
violations = append(violations, FieldViolation{Field: "gateway_id", Code: "min_length"})
|
|
}
|
|
if len(v.GatewayID) > 128 {
|
|
violations = append(violations, FieldViolation{Field: "gateway_id", Code: "max_length"})
|
|
}
|
|
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 len(v.OperationID) < 36 && v.OperationID != "" {
|
|
violations = append(violations, FieldViolation{Field: "operation_id", Code: "min_length"})
|
|
}
|
|
if len(v.OperationID) > 36 {
|
|
violations = append(violations, FieldViolation{Field: "operation_id", Code: "max_length"})
|
|
}
|
|
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 {
|
|
violations = append(violations, FieldViolation{Field: "revision", Code: "minimum"})
|
|
}
|
|
if v.LeaseExpiresAt == "" {
|
|
violations = append(violations, FieldViolation{Field: "lease_expires_at", Code: "required"})
|
|
}
|
|
if len(v.LeaseExpiresAt) > 64 {
|
|
violations = append(violations, FieldViolation{Field: "lease_expires_at", Code: "max_length"})
|
|
}
|
|
if v.LeaseExpiresAt != "" {
|
|
if parsed, err := time.Parse(time.RFC3339Nano, v.LeaseExpiresAt); err != nil || parsed.UTC().Format(time.RFC3339Nano) != v.LeaseExpiresAt {
|
|
violations = append(violations, FieldViolation{Field: "lease_expires_at", Code: "invalid_time"})
|
|
}
|
|
}
|
|
if reflect.DeepEqual(v.SelectedDescriptor, SelectedSessionDescriptor{}) {
|
|
violations = append(violations, FieldViolation{Field: "selected_descriptor", Code: "required"})
|
|
}
|
|
if err := v.SelectedDescriptor.Validate(); err != nil {
|
|
violations = append(violations, FieldViolation{Field: "selected_descriptor", Code: "invalid_object"})
|
|
}
|
|
if v.CurrentAppliedRevision != nil && *v.CurrentAppliedRevision != 0 && *v.CurrentAppliedRevision < 0 {
|
|
violations = append(violations, FieldViolation{Field: "current_applied_revision", Code: "minimum"})
|
|
}
|
|
if len(violations) > 0 {
|
|
return ValidationError{Violations: violations}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func DecodeGatewayQualityWork(data []byte) (GatewayQualityWork, error) {
|
|
var value GatewayQualityWork
|
|
if len(data) > 1024*1024 {
|
|
return value, errors.New("protocol payload exceeds limit")
|
|
}
|
|
if err := rejectDuplicateJSONKeys(data); err != nil {
|
|
return value, err
|
|
}
|
|
var fields map[string]json.RawMessage
|
|
if err := json.Unmarshal(data, &fields); err != nil {
|
|
return value, err
|
|
}
|
|
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_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["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["selected_descriptor"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "selected_descriptor", 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"}}}
|
|
}
|
|
if raw, ok := fields["version"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "version", 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 EncodeGatewayQualityWork(value GatewayQualityWork) ([]byte, error) {
|
|
if err := value.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(value)
|
|
}
|
|
|
|
func (v GatewayQualityWorkRequest) Validate() error {
|
|
var violations []FieldViolation
|
|
if v.Version == "" {
|
|
violations = append(violations, FieldViolation{Field: "version", Code: "required"})
|
|
}
|
|
if v.Version != "1" && v.Version != "" {
|
|
violations = append(violations, FieldViolation{Field: "version", Code: "invalid_value"})
|
|
}
|
|
if v.SessionID == "" {
|
|
violations = append(violations, FieldViolation{Field: "session_id", Code: "required"})
|
|
}
|
|
if len(v.SessionID) < 1 && v.SessionID != "" {
|
|
violations = append(violations, FieldViolation{Field: "session_id", Code: "min_length"})
|
|
}
|
|
if len(v.SessionID) > 128 {
|
|
violations = append(violations, FieldViolation{Field: "session_id", Code: "max_length"})
|
|
}
|
|
if v.GatewayID == "" {
|
|
violations = append(violations, FieldViolation{Field: "gateway_id", Code: "required"})
|
|
}
|
|
if len(v.GatewayID) < 1 && v.GatewayID != "" {
|
|
violations = append(violations, FieldViolation{Field: "gateway_id", Code: "min_length"})
|
|
}
|
|
if len(v.GatewayID) > 128 {
|
|
violations = append(violations, FieldViolation{Field: "gateway_id", Code: "max_length"})
|
|
}
|
|
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 len(v.OperationID) < 36 && v.OperationID != "" {
|
|
violations = append(violations, FieldViolation{Field: "operation_id", Code: "min_length"})
|
|
}
|
|
if len(v.OperationID) > 36 {
|
|
violations = append(violations, FieldViolation{Field: "operation_id", Code: "max_length"})
|
|
}
|
|
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 {
|
|
violations = append(violations, FieldViolation{Field: "revision", Code: "minimum"})
|
|
}
|
|
if v.CurrentAppliedRevision != nil && *v.CurrentAppliedRevision != 0 && *v.CurrentAppliedRevision < 0 {
|
|
violations = append(violations, FieldViolation{Field: "current_applied_revision", Code: "minimum"})
|
|
}
|
|
if len(violations) > 0 {
|
|
return ValidationError{Violations: violations}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func DecodeGatewayQualityWorkRequest(data []byte) (GatewayQualityWorkRequest, error) {
|
|
var value GatewayQualityWorkRequest
|
|
if len(data) > 1024*1024 {
|
|
return value, errors.New("protocol payload exceeds limit")
|
|
}
|
|
if err := rejectDuplicateJSONKeys(data); err != nil {
|
|
return value, err
|
|
}
|
|
var fields map[string]json.RawMessage
|
|
if err := json.Unmarshal(data, &fields); err != nil {
|
|
return value, err
|
|
}
|
|
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"}}}
|
|
}
|
|
if raw, ok := fields["version"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "version", 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 EncodeGatewayQualityWorkRequest(value GatewayQualityWorkRequest) ([]byte, error) {
|
|
if err := value.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(value)
|
|
}
|
|
|
|
func (v GatewayRegistration) Validate() error {
|
|
var violations []FieldViolation
|
|
if v.Version == "" {
|
|
violations = append(violations, FieldViolation{Field: "version", Code: "required"})
|
|
}
|
|
if v.Version != "1" && v.Version != "" {
|
|
violations = append(violations, FieldViolation{Field: "version", Code: "invalid_value"})
|
|
}
|
|
if v.GatewayID == "" {
|
|
violations = append(violations, FieldViolation{Field: "gateway_id", Code: "required"})
|
|
}
|
|
if len(v.GatewayID) < 1 && v.GatewayID != "" {
|
|
violations = append(violations, FieldViolation{Field: "gateway_id", Code: "min_length"})
|
|
}
|
|
if len(v.GatewayID) > 128 {
|
|
violations = append(violations, FieldViolation{Field: "gateway_id", Code: "max_length"})
|
|
}
|
|
if v.InstanceIdentity == "" {
|
|
violations = append(violations, FieldViolation{Field: "instance_identity", Code: "required"})
|
|
}
|
|
if len(v.InstanceIdentity) < 1 && v.InstanceIdentity != "" {
|
|
violations = append(violations, FieldViolation{Field: "instance_identity", Code: "min_length"})
|
|
}
|
|
if len(v.InstanceIdentity) > 512 {
|
|
violations = append(violations, FieldViolation{Field: "instance_identity", Code: "max_length"})
|
|
}
|
|
if v.CertificateIdentity == "" {
|
|
violations = append(violations, FieldViolation{Field: "certificate_identity", Code: "required"})
|
|
}
|
|
if len(v.CertificateIdentity) < 1 && v.CertificateIdentity != "" {
|
|
violations = append(violations, FieldViolation{Field: "certificate_identity", Code: "min_length"})
|
|
}
|
|
if len(v.CertificateIdentity) > 512 {
|
|
violations = append(violations, FieldViolation{Field: "certificate_identity", Code: "max_length"})
|
|
}
|
|
if v.PublicIdentity == "" {
|
|
violations = append(violations, FieldViolation{Field: "public_identity", Code: "required"})
|
|
}
|
|
if len(v.PublicIdentity) < 1 && v.PublicIdentity != "" {
|
|
violations = append(violations, FieldViolation{Field: "public_identity", Code: "min_length"})
|
|
}
|
|
if len(v.PublicIdentity) > 256 {
|
|
violations = append(violations, FieldViolation{Field: "public_identity", Code: "max_length"})
|
|
}
|
|
if v.Address == "" {
|
|
violations = append(violations, FieldViolation{Field: "address", Code: "required"})
|
|
}
|
|
if len(v.Address) < 1 && v.Address != "" {
|
|
violations = append(violations, FieldViolation{Field: "address", Code: "min_length"})
|
|
}
|
|
if len(v.Address) > 256 {
|
|
violations = append(violations, FieldViolation{Field: "address", Code: "max_length"})
|
|
}
|
|
if v.ProviderIdentity == "" {
|
|
violations = append(violations, FieldViolation{Field: "provider_identity", Code: "required"})
|
|
}
|
|
if len(v.ProviderIdentity) < 1 && v.ProviderIdentity != "" {
|
|
violations = append(violations, FieldViolation{Field: "provider_identity", Code: "min_length"})
|
|
}
|
|
if len(v.ProviderIdentity) > 256 {
|
|
violations = append(violations, FieldViolation{Field: "provider_identity", Code: "max_length"})
|
|
}
|
|
if v.ProtocolMinVersion == 0 {
|
|
violations = append(violations, FieldViolation{Field: "protocol_min_version", Code: "required"})
|
|
}
|
|
if v.ProtocolMinVersion != 0 && v.ProtocolMinVersion < 1 {
|
|
violations = append(violations, FieldViolation{Field: "protocol_min_version", Code: "minimum"})
|
|
}
|
|
if v.ProtocolMinVersion > 100 {
|
|
violations = append(violations, FieldViolation{Field: "protocol_min_version", Code: "maximum"})
|
|
}
|
|
if v.ProtocolMaxVersion == 0 {
|
|
violations = append(violations, FieldViolation{Field: "protocol_max_version", Code: "required"})
|
|
}
|
|
if v.ProtocolMaxVersion != 0 && v.ProtocolMaxVersion < 1 {
|
|
violations = append(violations, FieldViolation{Field: "protocol_max_version", Code: "minimum"})
|
|
}
|
|
if v.ProtocolMaxVersion > 100 {
|
|
violations = append(violations, FieldViolation{Field: "protocol_max_version", Code: "maximum"})
|
|
}
|
|
if v.ConnectionCapacity == 0 {
|
|
violations = append(violations, FieldViolation{Field: "connection_capacity", Code: "required"})
|
|
}
|
|
if v.ConnectionCapacity != 0 && v.ConnectionCapacity < 1 {
|
|
violations = append(violations, FieldViolation{Field: "connection_capacity", Code: "minimum"})
|
|
}
|
|
if v.ConnectionCapacity > 1000000 {
|
|
violations = append(violations, FieldViolation{Field: "connection_capacity", Code: "maximum"})
|
|
}
|
|
if v.BandwidthCapacityKbps == 0 {
|
|
violations = append(violations, FieldViolation{Field: "bandwidth_capacity_kbps", Code: "required"})
|
|
}
|
|
if v.BandwidthCapacityKbps != 0 && v.BandwidthCapacityKbps < 1 {
|
|
violations = append(violations, FieldViolation{Field: "bandwidth_capacity_kbps", Code: "minimum"})
|
|
}
|
|
if v.BandwidthCapacityKbps > 1000000000 {
|
|
violations = append(violations, FieldViolation{Field: "bandwidth_capacity_kbps", Code: "maximum"})
|
|
}
|
|
if v.Features == nil {
|
|
violations = append(violations, FieldViolation{Field: "features", Code: "required"})
|
|
}
|
|
if len(v.Features) > 64 {
|
|
violations = append(violations, FieldViolation{Field: "features", Code: "max_items"})
|
|
}
|
|
for _, item := range v.Features {
|
|
if len(item) < 1 {
|
|
violations = append(violations, FieldViolation{Field: "features", Code: "min_item_length"})
|
|
}
|
|
}
|
|
for _, item := range v.Features {
|
|
if len(item) > 64 {
|
|
violations = append(violations, FieldViolation{Field: "features", Code: "max_item_length"})
|
|
}
|
|
}
|
|
if reflect.DeepEqual(v.Capabilities, CapabilityProfile{}) {
|
|
violations = append(violations, FieldViolation{Field: "capabilities", Code: "required"})
|
|
}
|
|
if err := v.Capabilities.Validate(); err != nil {
|
|
violations = append(violations, FieldViolation{Field: "capabilities", Code: "invalid_object"})
|
|
}
|
|
if v.ProtocolMinVersion > v.ProtocolMaxVersion {
|
|
violations = append(violations, FieldViolation{Field: "protocol_version", Code: "invalid_order"})
|
|
}
|
|
if len(violations) > 0 {
|
|
return ValidationError{Violations: violations}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func DecodeGatewayRegistration(data []byte) (GatewayRegistration, error) {
|
|
var value GatewayRegistration
|
|
if len(data) > 1024*1024 {
|
|
return value, errors.New("protocol payload exceeds limit")
|
|
}
|
|
if err := rejectDuplicateJSONKeys(data); err != nil {
|
|
return value, err
|
|
}
|
|
var fields map[string]json.RawMessage
|
|
if err := json.Unmarshal(data, &fields); err != nil {
|
|
return value, err
|
|
}
|
|
if raw, ok := fields["address"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "address", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["bandwidth_capacity_kbps"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "bandwidth_capacity_kbps", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["capabilities"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "capabilities", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["certificate_identity"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "certificate_identity", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["connection_capacity"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "connection_capacity", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["features"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "features", 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["instance_identity"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "instance_identity", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["protocol_max_version"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "protocol_max_version", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["protocol_min_version"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "protocol_min_version", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["provider_identity"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "provider_identity", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["public_identity"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "public_identity", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["version"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "version", 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 EncodeGatewayRegistration(value GatewayRegistration) ([]byte, error) {
|
|
if err := value.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(value)
|
|
}
|
|
|
|
func (v GatewayStopAck) Validate() error {
|
|
var violations []FieldViolation
|
|
if v.Version == "" {
|
|
violations = append(violations, FieldViolation{Field: "version", Code: "required"})
|
|
}
|
|
if v.Version != "1" && v.Version != "" {
|
|
violations = append(violations, FieldViolation{Field: "version", Code: "invalid_value"})
|
|
}
|
|
if v.SessionID == "" {
|
|
violations = append(violations, FieldViolation{Field: "session_id", Code: "required"})
|
|
}
|
|
if len(v.SessionID) < 1 && v.SessionID != "" {
|
|
violations = append(violations, FieldViolation{Field: "session_id", Code: "min_length"})
|
|
}
|
|
if len(v.SessionID) > 128 {
|
|
violations = append(violations, FieldViolation{Field: "session_id", Code: "max_length"})
|
|
}
|
|
if v.GatewayID == "" {
|
|
violations = append(violations, FieldViolation{Field: "gateway_id", Code: "required"})
|
|
}
|
|
if len(v.GatewayID) < 1 && v.GatewayID != "" {
|
|
violations = append(violations, FieldViolation{Field: "gateway_id", Code: "min_length"})
|
|
}
|
|
if len(v.GatewayID) > 128 {
|
|
violations = append(violations, FieldViolation{Field: "gateway_id", Code: "max_length"})
|
|
}
|
|
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 len(v.OperationID) < 36 && v.OperationID != "" {
|
|
violations = append(violations, FieldViolation{Field: "operation_id", Code: "min_length"})
|
|
}
|
|
if len(v.OperationID) > 36 {
|
|
violations = append(violations, FieldViolation{Field: "operation_id", Code: "max_length"})
|
|
}
|
|
if v.OperationID != "" && !validCanonicalUUID(v.OperationID) {
|
|
violations = append(violations, FieldViolation{Field: "operation_id", Code: "invalid_uuid"})
|
|
}
|
|
if v.Outcome == "" {
|
|
violations = append(violations, FieldViolation{Field: "outcome", Code: "required"})
|
|
}
|
|
if v.Outcome != "" && !(v.Outcome == "applied" || v.Outcome == "failed" || v.Outcome == "termination_unconfirmed") {
|
|
violations = append(violations, FieldViolation{Field: "outcome", Code: "invalid_value"})
|
|
}
|
|
if len(v.FailureCode) < 1 && v.FailureCode != "" {
|
|
violations = append(violations, FieldViolation{Field: "failure_code", Code: "min_length"})
|
|
}
|
|
if len(v.FailureCode) > 128 {
|
|
violations = append(violations, FieldViolation{Field: "failure_code", Code: "max_length"})
|
|
}
|
|
if len(violations) > 0 {
|
|
return ValidationError{Violations: violations}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func DecodeGatewayStopAck(data []byte) (GatewayStopAck, error) {
|
|
var value GatewayStopAck
|
|
if len(data) > 1024*1024 {
|
|
return value, errors.New("protocol payload exceeds limit")
|
|
}
|
|
if err := rejectDuplicateJSONKeys(data); err != nil {
|
|
return value, err
|
|
}
|
|
var fields map[string]json.RawMessage
|
|
if err := json.Unmarshal(data, &fields); err != nil {
|
|
return value, err
|
|
}
|
|
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["outcome"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "outcome", 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["session_id"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "session_id", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["version"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "version", 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 EncodeGatewayStopAck(value GatewayStopAck) ([]byte, error) {
|
|
if err := value.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(value)
|
|
}
|
|
|
|
func (v GatewayStopWork) Validate() error {
|
|
var violations []FieldViolation
|
|
if v.Version == "" {
|
|
violations = append(violations, FieldViolation{Field: "version", Code: "required"})
|
|
}
|
|
if v.Version != "1" && v.Version != "" {
|
|
violations = append(violations, FieldViolation{Field: "version", Code: "invalid_value"})
|
|
}
|
|
if v.SessionID == "" {
|
|
violations = append(violations, FieldViolation{Field: "session_id", Code: "required"})
|
|
}
|
|
if len(v.SessionID) < 1 && v.SessionID != "" {
|
|
violations = append(violations, FieldViolation{Field: "session_id", Code: "min_length"})
|
|
}
|
|
if len(v.SessionID) > 128 {
|
|
violations = append(violations, FieldViolation{Field: "session_id", Code: "max_length"})
|
|
}
|
|
if v.GatewayID == "" {
|
|
violations = append(violations, FieldViolation{Field: "gateway_id", Code: "required"})
|
|
}
|
|
if len(v.GatewayID) < 1 && v.GatewayID != "" {
|
|
violations = append(violations, FieldViolation{Field: "gateway_id", Code: "min_length"})
|
|
}
|
|
if len(v.GatewayID) > 128 {
|
|
violations = append(violations, FieldViolation{Field: "gateway_id", Code: "max_length"})
|
|
}
|
|
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 len(v.OperationID) < 36 && v.OperationID != "" {
|
|
violations = append(violations, FieldViolation{Field: "operation_id", Code: "min_length"})
|
|
}
|
|
if len(v.OperationID) > 36 {
|
|
violations = append(violations, FieldViolation{Field: "operation_id", Code: "max_length"})
|
|
}
|
|
if v.OperationID != "" && !validCanonicalUUID(v.OperationID) {
|
|
violations = append(violations, FieldViolation{Field: "operation_id", Code: "invalid_uuid"})
|
|
}
|
|
if v.Attempt == 0 {
|
|
violations = append(violations, FieldViolation{Field: "attempt", Code: "required"})
|
|
}
|
|
if v.Attempt != 0 && v.Attempt < 1 {
|
|
violations = append(violations, FieldViolation{Field: "attempt", Code: "minimum"})
|
|
}
|
|
if v.Attempt > 1 {
|
|
violations = append(violations, FieldViolation{Field: "attempt", Code: "maximum"})
|
|
}
|
|
if len(violations) > 0 {
|
|
return ValidationError{Violations: violations}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func DecodeGatewayStopWork(data []byte) (GatewayStopWork, error) {
|
|
var value GatewayStopWork
|
|
if len(data) > 1024*1024 {
|
|
return value, errors.New("protocol payload exceeds limit")
|
|
}
|
|
if err := rejectDuplicateJSONKeys(data); err != nil {
|
|
return value, err
|
|
}
|
|
var fields map[string]json.RawMessage
|
|
if err := json.Unmarshal(data, &fields); err != nil {
|
|
return value, err
|
|
}
|
|
if raw, ok := fields["attempt"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "attempt", 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["session_id"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "session_id", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["version"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "version", 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 EncodeGatewayStopWork(value GatewayStopWork) ([]byte, error) {
|
|
if err := value.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(value)
|
|
}
|
|
|
|
func (v GatewayStopWorkRequest) Validate() error {
|
|
var violations []FieldViolation
|
|
if v.Version == "" {
|
|
violations = append(violations, FieldViolation{Field: "version", Code: "required"})
|
|
}
|
|
if v.Version != "1" && v.Version != "" {
|
|
violations = append(violations, FieldViolation{Field: "version", Code: "invalid_value"})
|
|
}
|
|
if v.SessionID == "" {
|
|
violations = append(violations, FieldViolation{Field: "session_id", Code: "required"})
|
|
}
|
|
if len(v.SessionID) < 1 && v.SessionID != "" {
|
|
violations = append(violations, FieldViolation{Field: "session_id", Code: "min_length"})
|
|
}
|
|
if len(v.SessionID) > 128 {
|
|
violations = append(violations, FieldViolation{Field: "session_id", Code: "max_length"})
|
|
}
|
|
if v.GatewayID == "" {
|
|
violations = append(violations, FieldViolation{Field: "gateway_id", Code: "required"})
|
|
}
|
|
if len(v.GatewayID) < 1 && v.GatewayID != "" {
|
|
violations = append(violations, FieldViolation{Field: "gateway_id", Code: "min_length"})
|
|
}
|
|
if len(v.GatewayID) > 128 {
|
|
violations = append(violations, FieldViolation{Field: "gateway_id", Code: "max_length"})
|
|
}
|
|
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 len(v.OperationID) < 36 && v.OperationID != "" {
|
|
violations = append(violations, FieldViolation{Field: "operation_id", Code: "min_length"})
|
|
}
|
|
if len(v.OperationID) > 36 {
|
|
violations = append(violations, FieldViolation{Field: "operation_id", Code: "max_length"})
|
|
}
|
|
if v.OperationID != "" && !validCanonicalUUID(v.OperationID) {
|
|
violations = append(violations, FieldViolation{Field: "operation_id", Code: "invalid_uuid"})
|
|
}
|
|
if len(violations) > 0 {
|
|
return ValidationError{Violations: violations}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func DecodeGatewayStopWorkRequest(data []byte) (GatewayStopWorkRequest, error) {
|
|
var value GatewayStopWorkRequest
|
|
if len(data) > 1024*1024 {
|
|
return value, errors.New("protocol payload exceeds limit")
|
|
}
|
|
if err := rejectDuplicateJSONKeys(data); err != nil {
|
|
return value, err
|
|
}
|
|
var fields map[string]json.RawMessage
|
|
if err := json.Unmarshal(data, &fields); err != nil {
|
|
return value, err
|
|
}
|
|
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["session_id"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "session_id", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["version"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "version", 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 EncodeGatewayStopWorkRequest(value GatewayStopWorkRequest) ([]byte, error) {
|
|
if err := value.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(value)
|
|
}
|
|
|
|
func (v GatewayTelemetry) Validate() error {
|
|
var violations []FieldViolation
|
|
if v.AdmittedSessions != 0 && v.AdmittedSessions < 0 {
|
|
violations = append(violations, FieldViolation{Field: "admitted_sessions", Code: "minimum"})
|
|
}
|
|
if v.AdmittedSessions > 9223372036854775807 {
|
|
violations = append(violations, FieldViolation{Field: "admitted_sessions", Code: "maximum"})
|
|
}
|
|
if v.AdmissionRejects != 0 && v.AdmissionRejects < 0 {
|
|
violations = append(violations, FieldViolation{Field: "admission_rejects", Code: "minimum"})
|
|
}
|
|
if v.AdmissionRejects > 9223372036854775807 {
|
|
violations = append(violations, FieldViolation{Field: "admission_rejects", Code: "maximum"})
|
|
}
|
|
if v.Reconnects != 0 && v.Reconnects < 0 {
|
|
violations = append(violations, FieldViolation{Field: "reconnects", Code: "minimum"})
|
|
}
|
|
if v.Reconnects > 9223372036854775807 {
|
|
violations = append(violations, FieldViolation{Field: "reconnects", Code: "maximum"})
|
|
}
|
|
if v.DrainTransitions != 0 && v.DrainTransitions < 0 {
|
|
violations = append(violations, FieldViolation{Field: "drain_transitions", Code: "minimum"})
|
|
}
|
|
if v.DrainTransitions > 9223372036854775807 {
|
|
violations = append(violations, FieldViolation{Field: "drain_transitions", Code: "maximum"})
|
|
}
|
|
if v.MediaDrops != 0 && v.MediaDrops < 0 {
|
|
violations = append(violations, FieldViolation{Field: "media_drops", Code: "minimum"})
|
|
}
|
|
if v.MediaDrops > 9223372036854775807 {
|
|
violations = append(violations, FieldViolation{Field: "media_drops", Code: "maximum"})
|
|
}
|
|
if v.MediaPackets != 0 && v.MediaPackets < 0 {
|
|
violations = append(violations, FieldViolation{Field: "media_packets", Code: "minimum"})
|
|
}
|
|
if v.MediaPackets > 9223372036854775807 {
|
|
violations = append(violations, FieldViolation{Field: "media_packets", Code: "maximum"})
|
|
}
|
|
if v.MediaBytes != 0 && v.MediaBytes < 0 {
|
|
violations = append(violations, FieldViolation{Field: "media_bytes", Code: "minimum"})
|
|
}
|
|
if v.MediaBytes > 9223372036854775807 {
|
|
violations = append(violations, FieldViolation{Field: "media_bytes", Code: "maximum"})
|
|
}
|
|
if v.QueueDelayMicros != 0 && v.QueueDelayMicros < 0 {
|
|
violations = append(violations, FieldViolation{Field: "queue_delay_micros", Code: "minimum"})
|
|
}
|
|
if v.QueueDelayMicros > 9223372036854775807 {
|
|
violations = append(violations, FieldViolation{Field: "queue_delay_micros", Code: "maximum"})
|
|
}
|
|
if v.ProcessingDelayMicros != 0 && v.ProcessingDelayMicros < 0 {
|
|
violations = append(violations, FieldViolation{Field: "processing_delay_micros", Code: "minimum"})
|
|
}
|
|
if v.ProcessingDelayMicros > 9223372036854775807 {
|
|
violations = append(violations, FieldViolation{Field: "processing_delay_micros", Code: "maximum"})
|
|
}
|
|
if v.ProcessingSamples != 0 && v.ProcessingSamples < 0 {
|
|
violations = append(violations, FieldViolation{Field: "processing_samples", Code: "minimum"})
|
|
}
|
|
if v.ProcessingSamples > 9223372036854775807 {
|
|
violations = append(violations, FieldViolation{Field: "processing_samples", Code: "maximum"})
|
|
}
|
|
if v.PacingDelayMicros != 0 && v.PacingDelayMicros < 0 {
|
|
violations = append(violations, FieldViolation{Field: "pacing_delay_micros", Code: "minimum"})
|
|
}
|
|
if v.PacingDelayMicros > 9223372036854775807 {
|
|
violations = append(violations, FieldViolation{Field: "pacing_delay_micros", Code: "maximum"})
|
|
}
|
|
if v.ProviderErrors != 0 && v.ProviderErrors < 0 {
|
|
violations = append(violations, FieldViolation{Field: "provider_errors", Code: "minimum"})
|
|
}
|
|
if v.ProviderErrors > 9223372036854775807 {
|
|
violations = append(violations, FieldViolation{Field: "provider_errors", Code: "maximum"})
|
|
}
|
|
if v.InputRejected != 0 && v.InputRejected < 0 {
|
|
violations = append(violations, FieldViolation{Field: "input_rejected", Code: "minimum"})
|
|
}
|
|
if v.InputRejected > 9223372036854775807 {
|
|
violations = append(violations, FieldViolation{Field: "input_rejected", Code: "maximum"})
|
|
}
|
|
if v.ControlRttMicros != 0 && v.ControlRttMicros < 0 {
|
|
violations = append(violations, FieldViolation{Field: "control_rtt_micros", Code: "minimum"})
|
|
}
|
|
if v.ControlRttMicros > 9223372036854775807 {
|
|
violations = append(violations, FieldViolation{Field: "control_rtt_micros", Code: "maximum"})
|
|
}
|
|
if v.ControlJitterMicros != 0 && v.ControlJitterMicros < 0 {
|
|
violations = append(violations, FieldViolation{Field: "control_jitter_micros", Code: "minimum"})
|
|
}
|
|
if v.ControlJitterMicros > 9223372036854775807 {
|
|
violations = append(violations, FieldViolation{Field: "control_jitter_micros", Code: "maximum"})
|
|
}
|
|
if v.ControlLossPpm != 0 && v.ControlLossPpm < 0 {
|
|
violations = append(violations, FieldViolation{Field: "control_loss_ppm", Code: "minimum"})
|
|
}
|
|
if v.ControlLossPpm > 1000000 {
|
|
violations = append(violations, FieldViolation{Field: "control_loss_ppm", Code: "maximum"})
|
|
}
|
|
if v.PendingReliable != 0 && v.PendingReliable < 0 {
|
|
violations = append(violations, FieldViolation{Field: "pending_reliable", Code: "minimum"})
|
|
}
|
|
if v.PendingReliable > 9223372036854775807 {
|
|
violations = append(violations, FieldViolation{Field: "pending_reliable", Code: "maximum"})
|
|
}
|
|
if v.ProviderState == "" {
|
|
violations = append(violations, FieldViolation{Field: "provider_state", Code: "required"})
|
|
}
|
|
if v.ProviderState != "" && !(v.ProviderState == "unknown" || v.ProviderState == "starting" || v.ProviderState == "ready" || v.ProviderState == "disconnected" || v.ProviderState == "terminating" || v.ProviderState == "terminated" || v.ProviderState == "cleanup_pending" || v.ProviderState == "failed") {
|
|
violations = append(violations, FieldViolation{Field: "provider_state", Code: "invalid_value"})
|
|
}
|
|
if len(violations) > 0 {
|
|
return ValidationError{Violations: violations}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func DecodeGatewayTelemetry(data []byte) (GatewayTelemetry, error) {
|
|
var value GatewayTelemetry
|
|
if len(data) > 1024*1024 {
|
|
return value, errors.New("protocol payload exceeds limit")
|
|
}
|
|
if err := rejectDuplicateJSONKeys(data); err != nil {
|
|
return value, err
|
|
}
|
|
var fields map[string]json.RawMessage
|
|
if err := json.Unmarshal(data, &fields); err != nil {
|
|
return value, err
|
|
}
|
|
if raw, ok := fields["admission_rejects"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "admission_rejects", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["admitted_sessions"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "admitted_sessions", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["control_jitter_micros"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "control_jitter_micros", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["control_loss_ppm"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "control_loss_ppm", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["control_rtt_micros"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "control_rtt_micros", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["drain_transitions"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "drain_transitions", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["input_rejected"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "input_rejected", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["media_bytes"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "media_bytes", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["media_drops"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "media_drops", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["media_packets"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "media_packets", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["pacing_delay_micros"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "pacing_delay_micros", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["pending_reliable"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "pending_reliable", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["processing_delay_micros"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "processing_delay_micros", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["processing_samples"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "processing_samples", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["provider_errors"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "provider_errors", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["provider_state"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "provider_state", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["queue_delay_micros"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "queue_delay_micros", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["reconnects"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "reconnects", 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 EncodeGatewayTelemetry(value GatewayTelemetry) ([]byte, error) {
|
|
if err := value.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(value)
|
|
}
|
|
|
|
func (v GrantReference) Validate() error {
|
|
var violations []FieldViolation
|
|
if v.OpaqueValue == "" {
|
|
violations = append(violations, FieldViolation{Field: "opaque_value", Code: "required"})
|
|
}
|
|
if len(v.OpaqueValue) < 43 && v.OpaqueValue != "" {
|
|
violations = append(violations, FieldViolation{Field: "opaque_value", Code: "min_length"})
|
|
}
|
|
if len(v.OpaqueValue) > 256 {
|
|
violations = append(violations, FieldViolation{Field: "opaque_value", Code: "max_length"})
|
|
}
|
|
if v.ExpiresAt == "" {
|
|
violations = append(violations, FieldViolation{Field: "expires_at", Code: "required"})
|
|
}
|
|
if len(v.ExpiresAt) > 64 {
|
|
violations = append(violations, FieldViolation{Field: "expires_at", Code: "max_length"})
|
|
}
|
|
if v.ExpiresAt != "" {
|
|
if parsed, err := time.Parse(time.RFC3339Nano, v.ExpiresAt); err != nil || parsed.UTC().Format(time.RFC3339Nano) != v.ExpiresAt {
|
|
violations = append(violations, FieldViolation{Field: "expires_at", Code: "invalid_time"})
|
|
}
|
|
}
|
|
if v.Audience == "" {
|
|
violations = append(violations, FieldViolation{Field: "audience", Code: "required"})
|
|
}
|
|
if len(v.Audience) < 1 && v.Audience != "" {
|
|
violations = append(violations, FieldViolation{Field: "audience", Code: "min_length"})
|
|
}
|
|
if len(v.Audience) > 128 {
|
|
violations = append(violations, FieldViolation{Field: "audience", Code: "max_length"})
|
|
}
|
|
if len(violations) > 0 {
|
|
return ValidationError{Violations: violations}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func DecodeGrantReference(data []byte) (GrantReference, error) {
|
|
var value GrantReference
|
|
if len(data) > 1024*1024 {
|
|
return value, errors.New("protocol payload exceeds limit")
|
|
}
|
|
if err := rejectDuplicateJSONKeys(data); err != nil {
|
|
return value, err
|
|
}
|
|
var fields map[string]json.RawMessage
|
|
if err := json.Unmarshal(data, &fields); err != nil {
|
|
return value, err
|
|
}
|
|
if raw, ok := fields["audience"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "audience", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["expires_at"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "expires_at", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["opaque_value"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "opaque_value", 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 EncodeGrantReference(value GrantReference) ([]byte, error) {
|
|
if err := value.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(value)
|
|
}
|
|
|
|
func (v LoginRequest) Validate() error {
|
|
var violations []FieldViolation
|
|
if v.Provider != "" && !(v.Provider == "ldap" || v.Provider == "local") {
|
|
violations = append(violations, FieldViolation{Field: "provider", Code: "invalid_value"})
|
|
}
|
|
if v.Username == "" {
|
|
violations = append(violations, FieldViolation{Field: "username", Code: "required"})
|
|
}
|
|
if len(v.Username) < 1 && v.Username != "" {
|
|
violations = append(violations, FieldViolation{Field: "username", Code: "min_length"})
|
|
}
|
|
if len(v.Username) > 256 {
|
|
violations = append(violations, FieldViolation{Field: "username", Code: "max_length"})
|
|
}
|
|
if v.Password == "" {
|
|
violations = append(violations, FieldViolation{Field: "password", Code: "required"})
|
|
}
|
|
if len(v.Password) < 1 && v.Password != "" {
|
|
violations = append(violations, FieldViolation{Field: "password", Code: "min_length"})
|
|
}
|
|
if len(v.Password) > 1024 {
|
|
violations = append(violations, FieldViolation{Field: "password", Code: "max_length"})
|
|
}
|
|
if len(violations) > 0 {
|
|
return ValidationError{Violations: violations}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func DecodeLoginRequest(data []byte) (LoginRequest, error) {
|
|
var value LoginRequest
|
|
if len(data) > 1024*1024 {
|
|
return value, errors.New("protocol payload exceeds limit")
|
|
}
|
|
if err := rejectDuplicateJSONKeys(data); err != nil {
|
|
return value, err
|
|
}
|
|
var fields map[string]json.RawMessage
|
|
if err := json.Unmarshal(data, &fields); err != nil {
|
|
return value, err
|
|
}
|
|
if raw, ok := fields["password"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "password", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["username"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "username", 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 EncodeLoginRequest(value LoginRequest) ([]byte, error) {
|
|
if err := value.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(value)
|
|
}
|
|
|
|
func (v ManifestBounds) Validate() error {
|
|
var violations []FieldViolation
|
|
if v.MinimumKbps == 0 {
|
|
violations = append(violations, FieldViolation{Field: "minimum_kbps", Code: "required"})
|
|
}
|
|
if v.MinimumKbps != 0 && v.MinimumKbps < 1 {
|
|
violations = append(violations, FieldViolation{Field: "minimum_kbps", Code: "minimum"})
|
|
}
|
|
if v.MinimumKbps > 100000000 {
|
|
violations = append(violations, FieldViolation{Field: "minimum_kbps", Code: "maximum"})
|
|
}
|
|
if v.TargetKbps == 0 {
|
|
violations = append(violations, FieldViolation{Field: "target_kbps", Code: "required"})
|
|
}
|
|
if v.TargetKbps != 0 && v.TargetKbps < 1 {
|
|
violations = append(violations, FieldViolation{Field: "target_kbps", Code: "minimum"})
|
|
}
|
|
if v.TargetKbps > 100000000 {
|
|
violations = append(violations, FieldViolation{Field: "target_kbps", Code: "maximum"})
|
|
}
|
|
if v.MaximumKbps == 0 {
|
|
violations = append(violations, FieldViolation{Field: "maximum_kbps", Code: "required"})
|
|
}
|
|
if v.MaximumKbps != 0 && v.MaximumKbps < 1 {
|
|
violations = append(violations, FieldViolation{Field: "maximum_kbps", Code: "minimum"})
|
|
}
|
|
if v.MaximumKbps > 100000000 {
|
|
violations = append(violations, FieldViolation{Field: "maximum_kbps", Code: "maximum"})
|
|
}
|
|
if v.MinimumKbps > v.TargetKbps || v.TargetKbps > v.MaximumKbps {
|
|
violations = append(violations, FieldViolation{Field: "bounds", Code: "invalid_order"})
|
|
}
|
|
if len(violations) > 0 {
|
|
return ValidationError{Violations: violations}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func DecodeManifestBounds(data []byte) (ManifestBounds, error) {
|
|
var value ManifestBounds
|
|
if len(data) > 1024*1024 {
|
|
return value, errors.New("protocol payload exceeds limit")
|
|
}
|
|
if err := rejectDuplicateJSONKeys(data); err != nil {
|
|
return value, err
|
|
}
|
|
var fields map[string]json.RawMessage
|
|
if err := json.Unmarshal(data, &fields); err != nil {
|
|
return value, err
|
|
}
|
|
if raw, ok := fields["maximum_kbps"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "maximum_kbps", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["minimum_kbps"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "minimum_kbps", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["target_kbps"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "target_kbps", 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 EncodeManifestBounds(value ManifestBounds) ([]byte, error) {
|
|
if err := value.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(value)
|
|
}
|
|
|
|
func (v ManifestGateway) Validate() error {
|
|
var violations []FieldViolation
|
|
if v.ID == "" {
|
|
violations = append(violations, FieldViolation{Field: "id", Code: "required"})
|
|
}
|
|
if len(v.ID) < 1 && v.ID != "" {
|
|
violations = append(violations, FieldViolation{Field: "id", Code: "min_length"})
|
|
}
|
|
if len(v.ID) > 128 {
|
|
violations = append(violations, FieldViolation{Field: "id", Code: "max_length"})
|
|
}
|
|
if v.Addresses == nil {
|
|
violations = append(violations, FieldViolation{Field: "addresses", Code: "required"})
|
|
}
|
|
if len(v.Addresses) < 1 {
|
|
violations = append(violations, FieldViolation{Field: "addresses", Code: "min_items"})
|
|
}
|
|
if len(v.Addresses) > 4 {
|
|
violations = append(violations, FieldViolation{Field: "addresses", Code: "max_items"})
|
|
}
|
|
for _, item := range v.Addresses {
|
|
if len(item) < 1 {
|
|
violations = append(violations, FieldViolation{Field: "addresses", Code: "min_item_length"})
|
|
}
|
|
}
|
|
for _, item := range v.Addresses {
|
|
if len(item) > 256 {
|
|
violations = append(violations, FieldViolation{Field: "addresses", Code: "max_item_length"})
|
|
}
|
|
}
|
|
if v.PublicIdentity == "" {
|
|
violations = append(violations, FieldViolation{Field: "public_identity", Code: "required"})
|
|
}
|
|
if len(v.PublicIdentity) < 1 && v.PublicIdentity != "" {
|
|
violations = append(violations, FieldViolation{Field: "public_identity", Code: "min_length"})
|
|
}
|
|
if len(v.PublicIdentity) > 256 {
|
|
violations = append(violations, FieldViolation{Field: "public_identity", Code: "max_length"})
|
|
}
|
|
if len(violations) > 0 {
|
|
return ValidationError{Violations: violations}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func DecodeManifestGateway(data []byte) (ManifestGateway, error) {
|
|
var value ManifestGateway
|
|
if len(data) > 1024*1024 {
|
|
return value, errors.New("protocol payload exceeds limit")
|
|
}
|
|
if err := rejectDuplicateJSONKeys(data); err != nil {
|
|
return value, err
|
|
}
|
|
var fields map[string]json.RawMessage
|
|
if err := json.Unmarshal(data, &fields); err != nil {
|
|
return value, err
|
|
}
|
|
if raw, ok := fields["addresses"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "addresses", Code: "required"}}}
|
|
}
|
|
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["public_identity"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "public_identity", 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 EncodeManifestGateway(value ManifestGateway) ([]byte, error) {
|
|
if err := value.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(value)
|
|
}
|
|
|
|
func (v ManifestProfile) Validate() error {
|
|
var violations []FieldViolation
|
|
if v.ID == "" {
|
|
violations = append(violations, FieldViolation{Field: "id", Code: "required"})
|
|
}
|
|
if len(v.ID) < 1 && v.ID != "" {
|
|
violations = append(violations, FieldViolation{Field: "id", Code: "min_length"})
|
|
}
|
|
if len(v.ID) > 128 {
|
|
violations = append(violations, FieldViolation{Field: "id", Code: "max_length"})
|
|
}
|
|
if reflect.DeepEqual(v.Bounds, ManifestBounds{}) {
|
|
violations = append(violations, FieldViolation{Field: "bounds", Code: "required"})
|
|
}
|
|
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}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func DecodeManifestProfile(data []byte) (ManifestProfile, error) {
|
|
var value ManifestProfile
|
|
if len(data) > 1024*1024 {
|
|
return value, errors.New("protocol payload exceeds limit")
|
|
}
|
|
if err := rejectDuplicateJSONKeys(data); err != nil {
|
|
return value, err
|
|
}
|
|
var fields map[string]json.RawMessage
|
|
if err := json.Unmarshal(data, &fields); err != nil {
|
|
return value, err
|
|
}
|
|
if raw, ok := fields["bounds"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "bounds", Code: "required"}}}
|
|
}
|
|
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 {
|
|
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 EncodeManifestProfile(value ManifestProfile) ([]byte, error) {
|
|
if err := value.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(value)
|
|
}
|
|
|
|
func (v ManifestTunnel) Validate() error {
|
|
var violations []FieldViolation
|
|
if v.Versions == nil {
|
|
violations = append(violations, FieldViolation{Field: "versions", Code: "required"})
|
|
}
|
|
if len(v.Versions) < 1 {
|
|
violations = append(violations, FieldViolation{Field: "versions", Code: "min_items"})
|
|
}
|
|
if len(v.Versions) > 4 {
|
|
violations = append(violations, FieldViolation{Field: "versions", Code: "max_items"})
|
|
}
|
|
for _, item := range v.Versions {
|
|
if len(item) < 1 {
|
|
violations = append(violations, FieldViolation{Field: "versions", Code: "min_item_length"})
|
|
}
|
|
}
|
|
for _, item := range v.Versions {
|
|
if len(item) > 64 {
|
|
violations = append(violations, FieldViolation{Field: "versions", Code: "max_item_length"})
|
|
}
|
|
}
|
|
if v.Features == nil {
|
|
violations = append(violations, FieldViolation{Field: "features", Code: "required"})
|
|
}
|
|
if len(v.Features) > 32 {
|
|
violations = append(violations, FieldViolation{Field: "features", Code: "max_items"})
|
|
}
|
|
for _, item := range v.Features {
|
|
if len(item) < 1 {
|
|
violations = append(violations, FieldViolation{Field: "features", Code: "min_item_length"})
|
|
}
|
|
}
|
|
for _, item := range v.Features {
|
|
if len(item) > 64 {
|
|
violations = append(violations, FieldViolation{Field: "features", Code: "max_item_length"})
|
|
}
|
|
}
|
|
if len(violations) > 0 {
|
|
return ValidationError{Violations: violations}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func DecodeManifestTunnel(data []byte) (ManifestTunnel, error) {
|
|
var value ManifestTunnel
|
|
if len(data) > 1024*1024 {
|
|
return value, errors.New("protocol payload exceeds limit")
|
|
}
|
|
if err := rejectDuplicateJSONKeys(data); err != nil {
|
|
return value, err
|
|
}
|
|
var fields map[string]json.RawMessage
|
|
if err := json.Unmarshal(data, &fields); err != nil {
|
|
return value, err
|
|
}
|
|
if raw, ok := fields["features"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "features", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["versions"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "versions", 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 EncodeManifestTunnel(value ManifestTunnel) ([]byte, error) {
|
|
if err := value.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(value)
|
|
}
|
|
|
|
func (v NativeAuthenticatedSession) Validate() error {
|
|
var violations []FieldViolation
|
|
if v.Username == "" {
|
|
violations = append(violations, FieldViolation{Field: "username", Code: "required"})
|
|
}
|
|
if len(v.Username) < 1 && v.Username != "" {
|
|
violations = append(violations, FieldViolation{Field: "username", Code: "min_length"})
|
|
}
|
|
if len(v.Username) > 256 {
|
|
violations = append(violations, FieldViolation{Field: "username", Code: "max_length"})
|
|
}
|
|
if v.Provider == "" {
|
|
violations = append(violations, FieldViolation{Field: "provider", Code: "required"})
|
|
}
|
|
if len(v.Provider) < 1 && v.Provider != "" {
|
|
violations = append(violations, FieldViolation{Field: "provider", Code: "min_length"})
|
|
}
|
|
if len(v.Provider) > 64 {
|
|
violations = append(violations, FieldViolation{Field: "provider", Code: "max_length"})
|
|
}
|
|
if v.Roles == nil {
|
|
violations = append(violations, FieldViolation{Field: "roles", Code: "required"})
|
|
}
|
|
if len(v.Roles) > 16 {
|
|
violations = append(violations, FieldViolation{Field: "roles", Code: "max_items"})
|
|
}
|
|
for _, item := range v.Roles {
|
|
if len(item) < 1 {
|
|
violations = append(violations, FieldViolation{Field: "roles", Code: "min_item_length"})
|
|
}
|
|
}
|
|
for _, item := range v.Roles {
|
|
if len(item) > 64 {
|
|
violations = append(violations, FieldViolation{Field: "roles", Code: "max_item_length"})
|
|
}
|
|
}
|
|
for _, item := range v.Roles {
|
|
if len(item) > 64 {
|
|
violations = append(violations, FieldViolation{Field: "roles", Code: "max_item_bytes"})
|
|
}
|
|
}
|
|
if v.Role == "" {
|
|
violations = append(violations, FieldViolation{Field: "role", Code: "required"})
|
|
}
|
|
if v.Role != "" && !(v.Role == "user" || v.Role == "admin") {
|
|
violations = append(violations, FieldViolation{Field: "role", Code: "invalid_value"})
|
|
}
|
|
if reflect.DeepEqual(v.NativeIdentity, NativeSessionIdentity{}) {
|
|
violations = append(violations, FieldViolation{Field: "native_identity", Code: "required"})
|
|
}
|
|
if err := v.NativeIdentity.Validate(); err != nil {
|
|
violations = append(violations, FieldViolation{Field: "native_identity", Code: "invalid_object"})
|
|
}
|
|
if len(violations) > 0 {
|
|
return ValidationError{Violations: violations}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func DecodeNativeAuthenticatedSession(data []byte) (NativeAuthenticatedSession, error) {
|
|
var value NativeAuthenticatedSession
|
|
if len(data) > 1024*1024 {
|
|
return value, errors.New("protocol payload exceeds limit")
|
|
}
|
|
if err := rejectDuplicateJSONKeys(data); err != nil {
|
|
return value, err
|
|
}
|
|
var fields map[string]json.RawMessage
|
|
if err := json.Unmarshal(data, &fields); err != nil {
|
|
return value, err
|
|
}
|
|
if raw, ok := fields["native_identity"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "native_identity", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["provider"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "provider", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["role"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "role", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["roles"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "roles", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["username"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "username", 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 EncodeNativeAuthenticatedSession(value NativeAuthenticatedSession) ([]byte, error) {
|
|
if err := value.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(value)
|
|
}
|
|
|
|
func (v NativeCredential) Validate() error {
|
|
var violations []FieldViolation
|
|
if len(v.DeviceID) > 128 {
|
|
violations = append(violations, FieldViolation{Field: "device_id", Code: "max_length"})
|
|
}
|
|
if v.FamilyID == "" {
|
|
violations = append(violations, FieldViolation{Field: "family_id", Code: "required"})
|
|
}
|
|
if len(v.FamilyID) < 1 && v.FamilyID != "" {
|
|
violations = append(violations, FieldViolation{Field: "family_id", Code: "min_length"})
|
|
}
|
|
if len(v.FamilyID) > 128 {
|
|
violations = append(violations, FieldViolation{Field: "family_id", Code: "max_length"})
|
|
}
|
|
if v.AccessToken == "" {
|
|
violations = append(violations, FieldViolation{Field: "access_token", Code: "required"})
|
|
}
|
|
if len(v.AccessToken) < 1 && v.AccessToken != "" {
|
|
violations = append(violations, FieldViolation{Field: "access_token", Code: "min_length"})
|
|
}
|
|
if len(v.AccessToken) > 256 {
|
|
violations = append(violations, FieldViolation{Field: "access_token", Code: "max_length"})
|
|
}
|
|
if v.RefreshToken == "" {
|
|
violations = append(violations, FieldViolation{Field: "refresh_token", Code: "required"})
|
|
}
|
|
if len(v.RefreshToken) < 1 && v.RefreshToken != "" {
|
|
violations = append(violations, FieldViolation{Field: "refresh_token", Code: "min_length"})
|
|
}
|
|
if len(v.RefreshToken) > 256 {
|
|
violations = append(violations, FieldViolation{Field: "refresh_token", Code: "max_length"})
|
|
}
|
|
if v.ExpiresAt == "" {
|
|
violations = append(violations, FieldViolation{Field: "expires_at", Code: "required"})
|
|
}
|
|
if len(v.ExpiresAt) > 64 {
|
|
violations = append(violations, FieldViolation{Field: "expires_at", Code: "max_length"})
|
|
}
|
|
if v.ExpiresAt != "" {
|
|
if parsed, err := time.Parse(time.RFC3339Nano, v.ExpiresAt); err != nil || parsed.UTC().Format(time.RFC3339Nano) != v.ExpiresAt {
|
|
violations = append(violations, FieldViolation{Field: "expires_at", Code: "invalid_time"})
|
|
}
|
|
}
|
|
if len(v.RefreshExpiresAt) > 64 {
|
|
violations = append(violations, FieldViolation{Field: "refresh_expires_at", Code: "max_length"})
|
|
}
|
|
if v.RefreshExpiresAt != "" {
|
|
if parsed, err := time.Parse(time.RFC3339Nano, v.RefreshExpiresAt); err != nil || parsed.UTC().Format(time.RFC3339Nano) != v.RefreshExpiresAt {
|
|
violations = append(violations, FieldViolation{Field: "refresh_expires_at", Code: "invalid_time"})
|
|
}
|
|
}
|
|
if len(violations) > 0 {
|
|
return ValidationError{Violations: violations}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func DecodeNativeCredential(data []byte) (NativeCredential, error) {
|
|
var value NativeCredential
|
|
if len(data) > 1024*1024 {
|
|
return value, errors.New("protocol payload exceeds limit")
|
|
}
|
|
if err := rejectDuplicateJSONKeys(data); err != nil {
|
|
return value, err
|
|
}
|
|
var fields map[string]json.RawMessage
|
|
if err := json.Unmarshal(data, &fields); err != nil {
|
|
return value, err
|
|
}
|
|
if raw, ok := fields["access_token"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "access_token", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["expires_at"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "expires_at", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["family_id"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "family_id", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["refresh_token"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "refresh_token", 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 EncodeNativeCredential(value NativeCredential) ([]byte, error) {
|
|
if err := value.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(value)
|
|
}
|
|
|
|
func (v NativeSessionIdentity) Validate() error {
|
|
var violations []FieldViolation
|
|
if v.ClientDeviceID == "" {
|
|
violations = append(violations, FieldViolation{Field: "client_device_id", Code: "required"})
|
|
}
|
|
if len(v.ClientDeviceID) < 1 && v.ClientDeviceID != "" {
|
|
violations = append(violations, FieldViolation{Field: "client_device_id", Code: "min_length"})
|
|
}
|
|
if len(v.ClientDeviceID) > 128 {
|
|
violations = append(violations, FieldViolation{Field: "client_device_id", Code: "max_length"})
|
|
}
|
|
if v.DeviceKeyID == "" {
|
|
violations = append(violations, FieldViolation{Field: "device_key_id", Code: "required"})
|
|
}
|
|
if len(v.DeviceKeyID) < 1 && v.DeviceKeyID != "" {
|
|
violations = append(violations, FieldViolation{Field: "device_key_id", Code: "min_length"})
|
|
}
|
|
if len(v.DeviceKeyID) > 128 {
|
|
violations = append(violations, FieldViolation{Field: "device_key_id", Code: "max_length"})
|
|
}
|
|
if len(violations) > 0 {
|
|
return ValidationError{Violations: violations}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func DecodeNativeSessionIdentity(data []byte) (NativeSessionIdentity, error) {
|
|
var value NativeSessionIdentity
|
|
if len(data) > 1024*1024 {
|
|
return value, errors.New("protocol payload exceeds limit")
|
|
}
|
|
if err := rejectDuplicateJSONKeys(data); err != nil {
|
|
return value, err
|
|
}
|
|
var fields map[string]json.RawMessage
|
|
if err := json.Unmarshal(data, &fields); err != nil {
|
|
return value, err
|
|
}
|
|
if raw, ok := fields["client_device_id"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "client_device_id", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["device_key_id"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "device_key_id", 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 EncodeNativeSessionIdentity(value NativeSessionIdentity) ([]byte, error) {
|
|
if err := value.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(value)
|
|
}
|
|
|
|
func (v NativeTunnelCredential) Validate() error {
|
|
var violations []FieldViolation
|
|
if v.ClientDeviceID == "" {
|
|
violations = append(violations, FieldViolation{Field: "client_device_id", Code: "required"})
|
|
}
|
|
if len(v.ClientDeviceID) < 1 && v.ClientDeviceID != "" {
|
|
violations = append(violations, FieldViolation{Field: "client_device_id", Code: "min_length"})
|
|
}
|
|
if len(v.ClientDeviceID) > 128 {
|
|
violations = append(violations, FieldViolation{Field: "client_device_id", Code: "max_length"})
|
|
}
|
|
if v.DeviceKeyID == "" {
|
|
violations = append(violations, FieldViolation{Field: "device_key_id", Code: "required"})
|
|
}
|
|
if len(v.DeviceKeyID) < 1 && v.DeviceKeyID != "" {
|
|
violations = append(violations, FieldViolation{Field: "device_key_id", Code: "min_length"})
|
|
}
|
|
if len(v.DeviceKeyID) > 128 {
|
|
violations = append(violations, FieldViolation{Field: "device_key_id", Code: "max_length"})
|
|
}
|
|
if v.CertificateChainPem == "" {
|
|
violations = append(violations, FieldViolation{Field: "certificate_chain_pem", Code: "required"})
|
|
}
|
|
if len(v.CertificateChainPem) < 1 && v.CertificateChainPem != "" {
|
|
violations = append(violations, FieldViolation{Field: "certificate_chain_pem", Code: "min_length"})
|
|
}
|
|
if len(v.CertificateChainPem) > 65536 {
|
|
violations = append(violations, FieldViolation{Field: "certificate_chain_pem", Code: "max_length"})
|
|
}
|
|
if v.TrustBundlePem == "" {
|
|
violations = append(violations, FieldViolation{Field: "trust_bundle_pem", Code: "required"})
|
|
}
|
|
if len(v.TrustBundlePem) < 1 && v.TrustBundlePem != "" {
|
|
violations = append(violations, FieldViolation{Field: "trust_bundle_pem", Code: "min_length"})
|
|
}
|
|
if len(v.TrustBundlePem) > 65536 {
|
|
violations = append(violations, FieldViolation{Field: "trust_bundle_pem", Code: "max_length"})
|
|
}
|
|
if v.ExpiresAt == "" {
|
|
violations = append(violations, FieldViolation{Field: "expires_at", Code: "required"})
|
|
}
|
|
if len(v.ExpiresAt) > 64 {
|
|
violations = append(violations, FieldViolation{Field: "expires_at", Code: "max_length"})
|
|
}
|
|
if v.ExpiresAt != "" {
|
|
if parsed, err := time.Parse(time.RFC3339Nano, v.ExpiresAt); err != nil || parsed.UTC().Format(time.RFC3339Nano) != v.ExpiresAt {
|
|
violations = append(violations, FieldViolation{Field: "expires_at", Code: "invalid_time"})
|
|
}
|
|
}
|
|
if len(violations) > 0 {
|
|
return ValidationError{Violations: violations}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func DecodeNativeTunnelCredential(data []byte) (NativeTunnelCredential, error) {
|
|
var value NativeTunnelCredential
|
|
if len(data) > 1024*1024 {
|
|
return value, errors.New("protocol payload exceeds limit")
|
|
}
|
|
if err := rejectDuplicateJSONKeys(data); err != nil {
|
|
return value, err
|
|
}
|
|
var fields map[string]json.RawMessage
|
|
if err := json.Unmarshal(data, &fields); err != nil {
|
|
return value, err
|
|
}
|
|
if raw, ok := fields["certificate_chain_pem"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "certificate_chain_pem", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["client_device_id"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "client_device_id", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["device_key_id"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "device_key_id", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["expires_at"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "expires_at", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["trust_bundle_pem"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "trust_bundle_pem", 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 EncodeNativeTunnelCredential(value NativeTunnelCredential) ([]byte, error) {
|
|
if err := value.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(value)
|
|
}
|
|
|
|
func (v PageInfo) Validate() error {
|
|
var violations []FieldViolation
|
|
if v.Limit == 0 {
|
|
violations = append(violations, FieldViolation{Field: "limit", Code: "required"})
|
|
}
|
|
if v.Limit != 0 && v.Limit < 1 {
|
|
violations = append(violations, FieldViolation{Field: "limit", Code: "minimum"})
|
|
}
|
|
if v.Limit > 100 {
|
|
violations = append(violations, FieldViolation{Field: "limit", Code: "maximum"})
|
|
}
|
|
if v.NextCursor == "" {
|
|
violations = append(violations, FieldViolation{Field: "next_cursor", Code: "required"})
|
|
}
|
|
if len(v.NextCursor) > 512 {
|
|
violations = append(violations, FieldViolation{Field: "next_cursor", Code: "max_length"})
|
|
}
|
|
if len(violations) > 0 {
|
|
return ValidationError{Violations: violations}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func DecodePageInfo(data []byte) (PageInfo, error) {
|
|
var value PageInfo
|
|
if len(data) > 1024*1024 {
|
|
return value, errors.New("protocol payload exceeds limit")
|
|
}
|
|
if err := rejectDuplicateJSONKeys(data); err != nil {
|
|
return value, err
|
|
}
|
|
var fields map[string]json.RawMessage
|
|
if err := json.Unmarshal(data, &fields); err != nil {
|
|
return value, err
|
|
}
|
|
if raw, ok := fields["limit"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "limit", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["next_cursor"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "next_cursor", 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 EncodePageInfo(value PageInfo) ([]byte, error) {
|
|
if err := value.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(value)
|
|
}
|
|
|
|
func (v ProviderSessionWork) Validate() error {
|
|
var violations []FieldViolation
|
|
if v.Version == "" {
|
|
violations = append(violations, FieldViolation{Field: "version", Code: "required"})
|
|
}
|
|
if v.Version != "1" && v.Version != "" {
|
|
violations = append(violations, FieldViolation{Field: "version", Code: "invalid_value"})
|
|
}
|
|
if v.SessionID == "" {
|
|
violations = append(violations, FieldViolation{Field: "session_id", Code: "required"})
|
|
}
|
|
if len(v.SessionID) < 1 && v.SessionID != "" {
|
|
violations = append(violations, FieldViolation{Field: "session_id", Code: "min_length"})
|
|
}
|
|
if len(v.SessionID) > 128 {
|
|
violations = append(violations, FieldViolation{Field: "session_id", Code: "max_length"})
|
|
}
|
|
if v.GatewayID == "" {
|
|
violations = append(violations, FieldViolation{Field: "gateway_id", Code: "required"})
|
|
}
|
|
if len(v.GatewayID) < 1 && v.GatewayID != "" {
|
|
violations = append(violations, FieldViolation{Field: "gateway_id", Code: "min_length"})
|
|
}
|
|
if len(v.GatewayID) > 128 {
|
|
violations = append(violations, FieldViolation{Field: "gateway_id", Code: "max_length"})
|
|
}
|
|
if v.ReconnectSequence != 0 && v.ReconnectSequence < 0 {
|
|
violations = append(violations, FieldViolation{Field: "reconnect_sequence", Code: "minimum"})
|
|
}
|
|
if v.ExpiresAt == "" {
|
|
violations = append(violations, FieldViolation{Field: "expires_at", Code: "required"})
|
|
}
|
|
if len(v.ExpiresAt) > 64 {
|
|
violations = append(violations, FieldViolation{Field: "expires_at", Code: "max_length"})
|
|
}
|
|
if v.ExpiresAt != "" {
|
|
if parsed, err := time.Parse(time.RFC3339Nano, v.ExpiresAt); err != nil || parsed.UTC().Format(time.RFC3339Nano) != v.ExpiresAt {
|
|
violations = append(violations, FieldViolation{Field: "expires_at", Code: "invalid_time"})
|
|
}
|
|
}
|
|
if v.ProviderProfile == "" {
|
|
violations = append(violations, FieldViolation{Field: "provider_profile", Code: "required"})
|
|
}
|
|
if v.ProviderProfile != "apollo" && v.ProviderProfile != "" {
|
|
violations = append(violations, FieldViolation{Field: "provider_profile", Code: "invalid_value"})
|
|
}
|
|
if v.ProviderIdentity == "" {
|
|
violations = append(violations, FieldViolation{Field: "provider_identity", Code: "required"})
|
|
}
|
|
if len(v.ProviderIdentity) < 1 && v.ProviderIdentity != "" {
|
|
violations = append(violations, FieldViolation{Field: "provider_identity", Code: "min_length"})
|
|
}
|
|
if len(v.ProviderIdentity) > 256 {
|
|
violations = append(violations, FieldViolation{Field: "provider_identity", Code: "max_length"})
|
|
}
|
|
if v.PolicyVersionID == "" {
|
|
violations = append(violations, FieldViolation{Field: "policy_version_id", Code: "required"})
|
|
}
|
|
if len(v.PolicyVersionID) < 1 && v.PolicyVersionID != "" {
|
|
violations = append(violations, FieldViolation{Field: "policy_version_id", Code: "min_length"})
|
|
}
|
|
if len(v.PolicyVersionID) > 128 {
|
|
violations = append(violations, FieldViolation{Field: "policy_version_id", Code: "max_length"})
|
|
}
|
|
if reflect.DeepEqual(v.StreamPolicy, ProviderStreamPolicy{}) {
|
|
violations = append(violations, FieldViolation{Field: "stream_policy", Code: "required"})
|
|
}
|
|
if err := v.StreamPolicy.Validate(); err != nil {
|
|
violations = append(violations, FieldViolation{Field: "stream_policy", Code: "invalid_object"})
|
|
}
|
|
if v.ApplicationID == "" {
|
|
violations = append(violations, FieldViolation{Field: "application_id", Code: "required"})
|
|
}
|
|
if len(v.ApplicationID) < 1 && v.ApplicationID != "" {
|
|
violations = append(violations, FieldViolation{Field: "application_id", Code: "min_length"})
|
|
}
|
|
if len(v.ApplicationID) > 128 {
|
|
violations = append(violations, FieldViolation{Field: "application_id", Code: "max_length"})
|
|
}
|
|
if v.ClientID == "" {
|
|
violations = append(violations, FieldViolation{Field: "client_id", Code: "required"})
|
|
}
|
|
if len(v.ClientID) < 1 && v.ClientID != "" {
|
|
violations = append(violations, FieldViolation{Field: "client_id", Code: "min_length"})
|
|
}
|
|
if len(v.ClientID) > 128 {
|
|
violations = append(violations, FieldViolation{Field: "client_id", Code: "max_length"})
|
|
}
|
|
if v.ManagementHost == "" {
|
|
violations = append(violations, FieldViolation{Field: "management_host", Code: "required"})
|
|
}
|
|
if len(v.ManagementHost) < 1 && v.ManagementHost != "" {
|
|
violations = append(violations, FieldViolation{Field: "management_host", Code: "min_length"})
|
|
}
|
|
if len(v.ManagementHost) > 256 {
|
|
violations = append(violations, FieldViolation{Field: "management_host", Code: "max_length"})
|
|
}
|
|
if v.ManagementPort == 0 {
|
|
violations = append(violations, FieldViolation{Field: "management_port", Code: "required"})
|
|
}
|
|
if v.ManagementPort != 0 && v.ManagementPort < 1 {
|
|
violations = append(violations, FieldViolation{Field: "management_port", Code: "minimum"})
|
|
}
|
|
if v.ManagementPort > 65535 {
|
|
violations = append(violations, FieldViolation{Field: "management_port", Code: "maximum"})
|
|
}
|
|
if v.StreamHost == "" {
|
|
violations = append(violations, FieldViolation{Field: "stream_host", Code: "required"})
|
|
}
|
|
if len(v.StreamHost) < 1 && v.StreamHost != "" {
|
|
violations = append(violations, FieldViolation{Field: "stream_host", Code: "min_length"})
|
|
}
|
|
if len(v.StreamHost) > 256 {
|
|
violations = append(violations, FieldViolation{Field: "stream_host", Code: "max_length"})
|
|
}
|
|
if v.StreamPort == 0 {
|
|
violations = append(violations, FieldViolation{Field: "stream_port", Code: "required"})
|
|
}
|
|
if v.StreamPort != 0 && v.StreamPort < 1 {
|
|
violations = append(violations, FieldViolation{Field: "stream_port", Code: "minimum"})
|
|
}
|
|
if v.StreamPort > 65535 {
|
|
violations = append(violations, FieldViolation{Field: "stream_port", Code: "maximum"})
|
|
}
|
|
if v.ClientCertificatePem == "" {
|
|
violations = append(violations, FieldViolation{Field: "client_certificate_pem", Code: "required"})
|
|
}
|
|
if len(v.ClientCertificatePem) < 1 && v.ClientCertificatePem != "" {
|
|
violations = append(violations, FieldViolation{Field: "client_certificate_pem", Code: "min_length"})
|
|
}
|
|
if len(v.ClientCertificatePem) > 32768 {
|
|
violations = append(violations, FieldViolation{Field: "client_certificate_pem", Code: "max_length"})
|
|
}
|
|
if v.ClientPrivateKeyPem == "" {
|
|
violations = append(violations, FieldViolation{Field: "client_private_key_pem", Code: "required"})
|
|
}
|
|
if len(v.ClientPrivateKeyPem) < 1 && v.ClientPrivateKeyPem != "" {
|
|
violations = append(violations, FieldViolation{Field: "client_private_key_pem", Code: "min_length"})
|
|
}
|
|
if len(v.ClientPrivateKeyPem) > 32768 {
|
|
violations = append(violations, FieldViolation{Field: "client_private_key_pem", Code: "max_length"})
|
|
}
|
|
if v.ServerCertificatePem == "" {
|
|
violations = append(violations, FieldViolation{Field: "server_certificate_pem", Code: "required"})
|
|
}
|
|
if len(v.ServerCertificatePem) < 1 && v.ServerCertificatePem != "" {
|
|
violations = append(violations, FieldViolation{Field: "server_certificate_pem", Code: "min_length"})
|
|
}
|
|
if len(v.ServerCertificatePem) > 32768 {
|
|
violations = append(violations, FieldViolation{Field: "server_certificate_pem", Code: "max_length"})
|
|
}
|
|
if reflect.DeepEqual(v.ClipboardPolicy, ClipboardPolicy{}) {
|
|
violations = append(violations, FieldViolation{Field: "clipboard_policy", Code: "required"})
|
|
}
|
|
if err := v.ClipboardPolicy.Validate(); err != nil {
|
|
violations = append(violations, FieldViolation{Field: "clipboard_policy", Code: "invalid_object"})
|
|
}
|
|
if len(violations) > 0 {
|
|
return ValidationError{Violations: violations}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func DecodeProviderSessionWork(data []byte) (ProviderSessionWork, error) {
|
|
var value ProviderSessionWork
|
|
if len(data) > 1024*1024 {
|
|
return value, errors.New("protocol payload exceeds limit")
|
|
}
|
|
if err := rejectDuplicateJSONKeys(data); err != nil {
|
|
return value, err
|
|
}
|
|
var fields map[string]json.RawMessage
|
|
if err := json.Unmarshal(data, &fields); err != nil {
|
|
return value, err
|
|
}
|
|
if raw, ok := fields["application_id"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "application_id", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["client_certificate_pem"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "client_certificate_pem", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["client_id"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "client_id", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["client_private_key_pem"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "client_private_key_pem", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["clipboard_policy"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "clipboard_policy", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["expires_at"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "expires_at", 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["management_host"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "management_host", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["management_port"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "management_port", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["policy_version_id"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "policy_version_id", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["provider_application_termination_allowed"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "provider_application_termination_allowed", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["provider_identity"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "provider_identity", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["provider_profile"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "provider_profile", 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["server_certificate_pem"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "server_certificate_pem", 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"}}}
|
|
}
|
|
if raw, ok := fields["stream_host"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "stream_host", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["stream_policy"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "stream_policy", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["stream_port"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "stream_port", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["version"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "version", 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 EncodeProviderSessionWork(value ProviderSessionWork) ([]byte, error) {
|
|
if err := value.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(value)
|
|
}
|
|
|
|
func (v ProviderState) Validate() error {
|
|
var violations []FieldViolation
|
|
if v.Version == "" {
|
|
violations = append(violations, FieldViolation{Field: "version", Code: "required"})
|
|
}
|
|
if v.Version != "1" && v.Version != "" {
|
|
violations = append(violations, FieldViolation{Field: "version", Code: "invalid_value"})
|
|
}
|
|
if v.SessionID == "" {
|
|
violations = append(violations, FieldViolation{Field: "session_id", Code: "required"})
|
|
}
|
|
if len(v.SessionID) < 1 && v.SessionID != "" {
|
|
violations = append(violations, FieldViolation{Field: "session_id", Code: "min_length"})
|
|
}
|
|
if len(v.SessionID) > 128 {
|
|
violations = append(violations, FieldViolation{Field: "session_id", Code: "max_length"})
|
|
}
|
|
if v.State == "" {
|
|
violations = append(violations, FieldViolation{Field: "state", Code: "required"})
|
|
}
|
|
if v.State != "" && !(v.State == "starting" || v.State == "ready" || v.State == "disconnected" || v.State == "terminating" || v.State == "terminated" || v.State == "cleanup_pending" || v.State == "failed") {
|
|
violations = append(violations, FieldViolation{Field: "state", Code: "invalid_value"})
|
|
}
|
|
if v.Channels == nil {
|
|
violations = append(violations, FieldViolation{Field: "channels", Code: "required"})
|
|
}
|
|
if len(v.Channels) > 8 {
|
|
violations = append(violations, FieldViolation{Field: "channels", Code: "max_items"})
|
|
}
|
|
for _, item := range v.Channels {
|
|
if len(item) < 1 {
|
|
violations = append(violations, FieldViolation{Field: "channels", Code: "min_item_length"})
|
|
}
|
|
}
|
|
for _, item := range v.Channels {
|
|
if len(item) > 64 {
|
|
violations = append(violations, FieldViolation{Field: "channels", Code: "max_item_length"})
|
|
}
|
|
}
|
|
if len(violations) > 0 {
|
|
return ValidationError{Violations: violations}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func DecodeProviderState(data []byte) (ProviderState, error) {
|
|
var value ProviderState
|
|
if len(data) > 1024*1024 {
|
|
return value, errors.New("protocol payload exceeds limit")
|
|
}
|
|
if err := rejectDuplicateJSONKeys(data); err != nil {
|
|
return value, err
|
|
}
|
|
var fields map[string]json.RawMessage
|
|
if err := json.Unmarshal(data, &fields); err != nil {
|
|
return value, err
|
|
}
|
|
if raw, ok := fields["channels"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "channels", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["cleanup_pending"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "cleanup_pending", 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"}}}
|
|
}
|
|
if raw, ok := fields["state"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "state", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["version"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "version", 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 EncodeProviderState(value ProviderState) ([]byte, error) {
|
|
if err := value.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(value)
|
|
}
|
|
|
|
func (v ProviderStreamPolicy) Validate() error {
|
|
var violations []FieldViolation
|
|
if reflect.DeepEqual(v.VideoProfile, VideoProfile{}) {
|
|
violations = append(violations, FieldViolation{Field: "video_profile", Code: "required"})
|
|
}
|
|
if err := v.VideoProfile.Validate(); err != nil {
|
|
violations = append(violations, FieldViolation{Field: "video_profile", Code: "invalid_object"})
|
|
}
|
|
if reflect.DeepEqual(v.AudioProfile, AudioProfile{}) {
|
|
violations = append(violations, FieldViolation{Field: "audio_profile", Code: "required"})
|
|
}
|
|
if err := v.AudioProfile.Validate(); err != nil {
|
|
violations = append(violations, FieldViolation{Field: "audio_profile", Code: "invalid_object"})
|
|
}
|
|
if reflect.DeepEqual(v.DisplayMode, DisplayMode{}) {
|
|
violations = append(violations, FieldViolation{Field: "display_mode", Code: "required"})
|
|
}
|
|
if err := v.DisplayMode.Validate(); err != nil {
|
|
violations = append(violations, FieldViolation{Field: "display_mode", Code: "invalid_object"})
|
|
}
|
|
if v.BitrateTargetKbps == 0 {
|
|
violations = append(violations, FieldViolation{Field: "bitrate_target_kbps", Code: "required"})
|
|
}
|
|
if v.BitrateTargetKbps != 0 && v.BitrateTargetKbps < 100 {
|
|
violations = append(violations, FieldViolation{Field: "bitrate_target_kbps", Code: "minimum"})
|
|
}
|
|
if v.BitrateTargetKbps > 1000000 {
|
|
violations = append(violations, FieldViolation{Field: "bitrate_target_kbps", Code: "maximum"})
|
|
}
|
|
if v.BitrateMaximumKbps == 0 {
|
|
violations = append(violations, FieldViolation{Field: "bitrate_maximum_kbps", Code: "required"})
|
|
}
|
|
if v.BitrateMaximumKbps != 0 && v.BitrateMaximumKbps < 100 {
|
|
violations = append(violations, FieldViolation{Field: "bitrate_maximum_kbps", Code: "minimum"})
|
|
}
|
|
if v.BitrateMaximumKbps > 1000000 {
|
|
violations = append(violations, FieldViolation{Field: "bitrate_maximum_kbps", Code: "maximum"})
|
|
}
|
|
if v.BitrateTargetKbps > v.BitrateMaximumKbps {
|
|
violations = append(violations, FieldViolation{Field: "bitrate_bounds", Code: "invalid_order"})
|
|
}
|
|
if len(violations) > 0 {
|
|
return ValidationError{Violations: violations}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func DecodeProviderStreamPolicy(data []byte) (ProviderStreamPolicy, error) {
|
|
var value ProviderStreamPolicy
|
|
if len(data) > 1024*1024 {
|
|
return value, errors.New("protocol payload exceeds limit")
|
|
}
|
|
if err := rejectDuplicateJSONKeys(data); err != nil {
|
|
return value, err
|
|
}
|
|
var fields map[string]json.RawMessage
|
|
if err := json.Unmarshal(data, &fields); err != nil {
|
|
return value, err
|
|
}
|
|
if raw, ok := fields["audio_profile"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "audio_profile", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["bitrate_maximum_kbps"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "bitrate_maximum_kbps", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["bitrate_target_kbps"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "bitrate_target_kbps", 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: "required"}}}
|
|
}
|
|
if raw, ok := fields["video_profile"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "video_profile", 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 EncodeProviderStreamPolicy(value ProviderStreamPolicy) ([]byte, error) {
|
|
if err := value.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(value)
|
|
}
|
|
|
|
func (v QualityChangeOperation) Validate() error {
|
|
var violations []FieldViolation
|
|
if v.OperationID == "" {
|
|
violations = append(violations, FieldViolation{Field: "operation_id", Code: "required"})
|
|
}
|
|
if len(v.OperationID) < 36 && v.OperationID != "" {
|
|
violations = append(violations, FieldViolation{Field: "operation_id", Code: "min_length"})
|
|
}
|
|
if len(v.OperationID) > 36 {
|
|
violations = append(violations, FieldViolation{Field: "operation_id", Code: "max_length"})
|
|
}
|
|
if v.OperationID != "" && !validCanonicalUUID(v.OperationID) {
|
|
violations = append(violations, FieldViolation{Field: "operation_id", Code: "invalid_uuid"})
|
|
}
|
|
if v.SessionID == "" {
|
|
violations = append(violations, FieldViolation{Field: "session_id", Code: "required"})
|
|
}
|
|
if len(v.SessionID) < 1 && v.SessionID != "" {
|
|
violations = append(violations, FieldViolation{Field: "session_id", Code: "min_length"})
|
|
}
|
|
if len(v.SessionID) > 128 {
|
|
violations = append(violations, FieldViolation{Field: "session_id", Code: "max_length"})
|
|
}
|
|
if v.Revision == 0 {
|
|
violations = append(violations, FieldViolation{Field: "revision", Code: "required"})
|
|
}
|
|
if v.Revision != 0 && v.Revision < 1 {
|
|
violations = append(violations, FieldViolation{Field: "revision", Code: "minimum"})
|
|
}
|
|
if v.State == "" {
|
|
violations = append(violations, FieldViolation{Field: "state", Code: "required"})
|
|
}
|
|
if v.State != "" && !(v.State == "pending" || v.State == "applying" || v.State == "applied" || v.State == "failed" || v.State == "timed_out") {
|
|
violations = append(violations, FieldViolation{Field: "state", Code: "invalid_value"})
|
|
}
|
|
if reflect.DeepEqual(v.RequestedBitratePreference, BitratePreference{}) {
|
|
violations = append(violations, FieldViolation{Field: "requested_bitrate_preference", Code: "required"})
|
|
}
|
|
if err := v.RequestedBitratePreference.Validate(); err != nil {
|
|
violations = append(violations, FieldViolation{Field: "requested_bitrate_preference", Code: "invalid_object"})
|
|
}
|
|
if v.EffectiveBitrateKbps == 0 {
|
|
violations = append(violations, FieldViolation{Field: "effective_bitrate_kbps", Code: "required"})
|
|
}
|
|
if v.EffectiveBitrateKbps != 0 && v.EffectiveBitrateKbps < 100 {
|
|
violations = append(violations, FieldViolation{Field: "effective_bitrate_kbps", Code: "minimum"})
|
|
}
|
|
if v.EffectiveBitrateKbps > 1000000 {
|
|
violations = append(violations, FieldViolation{Field: "effective_bitrate_kbps", Code: "maximum"})
|
|
}
|
|
if v.GoverningPolicyVersion == "" {
|
|
violations = append(violations, FieldViolation{Field: "governing_policy_version", Code: "required"})
|
|
}
|
|
if len(v.GoverningPolicyVersion) < 1 && v.GoverningPolicyVersion != "" {
|
|
violations = append(violations, FieldViolation{Field: "governing_policy_version", Code: "min_length"})
|
|
}
|
|
if len(v.GoverningPolicyVersion) > 128 {
|
|
violations = append(violations, FieldViolation{Field: "governing_policy_version", Code: "max_length"})
|
|
}
|
|
if v.SessionVersion == 0 {
|
|
violations = append(violations, FieldViolation{Field: "session_version", Code: "required"})
|
|
}
|
|
if v.SessionVersion != 0 && v.SessionVersion < 1 {
|
|
violations = append(violations, FieldViolation{Field: "session_version", Code: "minimum"})
|
|
}
|
|
if v.CreatedAt == "" {
|
|
violations = append(violations, FieldViolation{Field: "created_at", Code: "required"})
|
|
}
|
|
if len(v.CreatedAt) > 64 {
|
|
violations = append(violations, FieldViolation{Field: "created_at", Code: "max_length"})
|
|
}
|
|
if v.CreatedAt != "" {
|
|
if parsed, err := time.Parse(time.RFC3339Nano, v.CreatedAt); err != nil || parsed.UTC().Format(time.RFC3339Nano) != v.CreatedAt {
|
|
violations = append(violations, FieldViolation{Field: "created_at", Code: "invalid_time"})
|
|
}
|
|
}
|
|
if v.DeadlineAt == "" {
|
|
violations = append(violations, FieldViolation{Field: "deadline_at", Code: "required"})
|
|
}
|
|
if len(v.DeadlineAt) > 64 {
|
|
violations = append(violations, FieldViolation{Field: "deadline_at", Code: "max_length"})
|
|
}
|
|
if v.DeadlineAt != "" {
|
|
if parsed, err := time.Parse(time.RFC3339Nano, v.DeadlineAt); err != nil || parsed.UTC().Format(time.RFC3339Nano) != v.DeadlineAt {
|
|
violations = append(violations, FieldViolation{Field: "deadline_at", Code: "invalid_time"})
|
|
}
|
|
}
|
|
if v.UpdatedAt == "" {
|
|
violations = append(violations, FieldViolation{Field: "updated_at", Code: "required"})
|
|
}
|
|
if len(v.UpdatedAt) > 64 {
|
|
violations = append(violations, FieldViolation{Field: "updated_at", Code: "max_length"})
|
|
}
|
|
if v.UpdatedAt != "" {
|
|
if parsed, err := time.Parse(time.RFC3339Nano, v.UpdatedAt); err != nil || parsed.UTC().Format(time.RFC3339Nano) != v.UpdatedAt {
|
|
violations = append(violations, FieldViolation{Field: "updated_at", Code: "invalid_time"})
|
|
}
|
|
}
|
|
if len(v.FailureCode) < 1 && v.FailureCode != "" {
|
|
violations = append(violations, FieldViolation{Field: "failure_code", Code: "min_length"})
|
|
}
|
|
if len(v.FailureCode) > 128 {
|
|
violations = append(violations, FieldViolation{Field: "failure_code", Code: "max_length"})
|
|
}
|
|
if len(violations) > 0 {
|
|
return ValidationError{Violations: violations}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func DecodeQualityChangeOperation(data []byte) (QualityChangeOperation, error) {
|
|
var value QualityChangeOperation
|
|
if len(data) > 1024*1024 {
|
|
return value, errors.New("protocol payload exceeds limit")
|
|
}
|
|
if err := rejectDuplicateJSONKeys(data); err != nil {
|
|
return value, err
|
|
}
|
|
var fields map[string]json.RawMessage
|
|
if err := json.Unmarshal(data, &fields); err != nil {
|
|
return value, err
|
|
}
|
|
if raw, ok := fields["created_at"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "created_at", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["deadline_at"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "deadline_at", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["effective_bitrate_kbps"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "effective_bitrate_kbps", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["governing_policy_version"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "governing_policy_version", 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["requested_bitrate_preference"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "requested_bitrate_preference", 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"}}}
|
|
}
|
|
if raw, ok := fields["session_version"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "session_version", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["state"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "state", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["updated_at"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "updated_at", 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 EncodeQualityChangeOperation(value QualityChangeOperation) ([]byte, error) {
|
|
if err := value.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(value)
|
|
}
|
|
|
|
func (v QualityChangeRequest) Validate() error {
|
|
var violations []FieldViolation
|
|
if reflect.DeepEqual(v.BitratePreference, BitratePreference{}) {
|
|
violations = append(violations, FieldViolation{Field: "bitrate_preference", Code: "required"})
|
|
}
|
|
if err := v.BitratePreference.Validate(); err != nil {
|
|
violations = append(violations, FieldViolation{Field: "bitrate_preference", Code: "invalid_object"})
|
|
}
|
|
if v.ExpectedSessionVersion == 0 {
|
|
violations = append(violations, FieldViolation{Field: "expected_session_version", Code: "required"})
|
|
}
|
|
if v.ExpectedSessionVersion != 0 && v.ExpectedSessionVersion < 1 {
|
|
violations = append(violations, FieldViolation{Field: "expected_session_version", Code: "minimum"})
|
|
}
|
|
if v.ExpectedPolicyVersion == "" {
|
|
violations = append(violations, FieldViolation{Field: "expected_policy_version", Code: "required"})
|
|
}
|
|
if len(v.ExpectedPolicyVersion) < 1 && v.ExpectedPolicyVersion != "" {
|
|
violations = append(violations, FieldViolation{Field: "expected_policy_version", Code: "min_length"})
|
|
}
|
|
if len(v.ExpectedPolicyVersion) > 128 {
|
|
violations = append(violations, FieldViolation{Field: "expected_policy_version", Code: "max_length"})
|
|
}
|
|
if len(violations) > 0 {
|
|
return ValidationError{Violations: violations}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func DecodeQualityChangeRequest(data []byte) (QualityChangeRequest, error) {
|
|
var value QualityChangeRequest
|
|
if len(data) > 1024*1024 {
|
|
return value, errors.New("protocol payload exceeds limit")
|
|
}
|
|
if err := rejectDuplicateJSONKeys(data); err != nil {
|
|
return value, err
|
|
}
|
|
var fields map[string]json.RawMessage
|
|
if err := json.Unmarshal(data, &fields); err != nil {
|
|
return value, err
|
|
}
|
|
if raw, ok := fields["bitrate_preference"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "bitrate_preference", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["expected_policy_version"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "expected_policy_version", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["expected_session_version"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "expected_session_version", 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 EncodeQualityChangeRequest(value QualityChangeRequest) ([]byte, error) {
|
|
if err := value.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(value)
|
|
}
|
|
|
|
func (v ReauthGrant) Validate() error {
|
|
var violations []FieldViolation
|
|
if v.Token == "" {
|
|
violations = append(violations, FieldViolation{Field: "token", Code: "required"})
|
|
}
|
|
if len(v.Token) < 1 && v.Token != "" {
|
|
violations = append(violations, FieldViolation{Field: "token", Code: "min_length"})
|
|
}
|
|
if len(v.Token) > 256 {
|
|
violations = append(violations, FieldViolation{Field: "token", Code: "max_length"})
|
|
}
|
|
if v.Purpose == "" {
|
|
violations = append(violations, FieldViolation{Field: "purpose", Code: "required"})
|
|
}
|
|
if len(v.Purpose) < 1 && v.Purpose != "" {
|
|
violations = append(violations, FieldViolation{Field: "purpose", Code: "min_length"})
|
|
}
|
|
if len(v.Purpose) > 64 {
|
|
violations = append(violations, FieldViolation{Field: "purpose", Code: "max_length"})
|
|
}
|
|
if v.ExpiresAt == "" {
|
|
violations = append(violations, FieldViolation{Field: "expires_at", Code: "required"})
|
|
}
|
|
if len(v.ExpiresAt) > 64 {
|
|
violations = append(violations, FieldViolation{Field: "expires_at", Code: "max_length"})
|
|
}
|
|
if v.ExpiresAt != "" {
|
|
if parsed, err := time.Parse(time.RFC3339Nano, v.ExpiresAt); err != nil || parsed.UTC().Format(time.RFC3339Nano) != v.ExpiresAt {
|
|
violations = append(violations, FieldViolation{Field: "expires_at", Code: "invalid_time"})
|
|
}
|
|
}
|
|
if len(violations) > 0 {
|
|
return ValidationError{Violations: violations}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func DecodeReauthGrant(data []byte) (ReauthGrant, error) {
|
|
var value ReauthGrant
|
|
if len(data) > 1024*1024 {
|
|
return value, errors.New("protocol payload exceeds limit")
|
|
}
|
|
if err := rejectDuplicateJSONKeys(data); err != nil {
|
|
return value, err
|
|
}
|
|
var fields map[string]json.RawMessage
|
|
if err := json.Unmarshal(data, &fields); err != nil {
|
|
return value, err
|
|
}
|
|
if raw, ok := fields["expires_at"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "expires_at", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["purpose"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "purpose", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["token"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "token", 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 EncodeReauthGrant(value ReauthGrant) ([]byte, error) {
|
|
if err := value.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(value)
|
|
}
|
|
|
|
func (v ReauthRequest) Validate() error {
|
|
var violations []FieldViolation
|
|
if v.Password == "" {
|
|
violations = append(violations, FieldViolation{Field: "password", Code: "required"})
|
|
}
|
|
if len(v.Password) < 1 && v.Password != "" {
|
|
violations = append(violations, FieldViolation{Field: "password", Code: "min_length"})
|
|
}
|
|
if len(v.Password) > 1024 {
|
|
violations = append(violations, FieldViolation{Field: "password", Code: "max_length"})
|
|
}
|
|
if v.Purpose == "" {
|
|
violations = append(violations, FieldViolation{Field: "purpose", Code: "required"})
|
|
}
|
|
if v.Purpose != "" && !(v.Purpose == "identity_change" || v.Purpose == "key_change" || v.Purpose == "backup_enable" || v.Purpose == "external_database_tls_disabled" || v.Purpose == "assignment_change") {
|
|
violations = append(violations, FieldViolation{Field: "purpose", Code: "invalid_value"})
|
|
}
|
|
if len(violations) > 0 {
|
|
return ValidationError{Violations: violations}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func DecodeReauthRequest(data []byte) (ReauthRequest, error) {
|
|
var value ReauthRequest
|
|
if len(data) > 1024*1024 {
|
|
return value, errors.New("protocol payload exceeds limit")
|
|
}
|
|
if err := rejectDuplicateJSONKeys(data); err != nil {
|
|
return value, err
|
|
}
|
|
var fields map[string]json.RawMessage
|
|
if err := json.Unmarshal(data, &fields); err != nil {
|
|
return value, err
|
|
}
|
|
if raw, ok := fields["password"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "password", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["purpose"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "purpose", 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 EncodeReauthRequest(value ReauthRequest) ([]byte, error) {
|
|
if err := value.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(value)
|
|
}
|
|
|
|
func (v ReconnectRequest) Validate() error {
|
|
var violations []FieldViolation
|
|
if v.ClientDeviceID == "" {
|
|
violations = append(violations, FieldViolation{Field: "client_device_id", Code: "required"})
|
|
}
|
|
if len(v.ClientDeviceID) < 1 && v.ClientDeviceID != "" {
|
|
violations = append(violations, FieldViolation{Field: "client_device_id", Code: "min_length"})
|
|
}
|
|
if len(v.ClientDeviceID) > 128 {
|
|
violations = append(violations, FieldViolation{Field: "client_device_id", Code: "max_length"})
|
|
}
|
|
if v.DeviceKeyID == "" {
|
|
violations = append(violations, FieldViolation{Field: "device_key_id", Code: "required"})
|
|
}
|
|
if len(v.DeviceKeyID) < 1 && v.DeviceKeyID != "" {
|
|
violations = append(violations, FieldViolation{Field: "device_key_id", Code: "min_length"})
|
|
}
|
|
if len(v.DeviceKeyID) > 128 {
|
|
violations = append(violations, FieldViolation{Field: "device_key_id", Code: "max_length"})
|
|
}
|
|
if v.ExpectedVersion == 0 {
|
|
violations = append(violations, FieldViolation{Field: "expected_version", Code: "required"})
|
|
}
|
|
if v.ExpectedVersion != 0 && v.ExpectedVersion < 1 {
|
|
violations = append(violations, FieldViolation{Field: "expected_version", Code: "minimum"})
|
|
}
|
|
if len(violations) > 0 {
|
|
return ValidationError{Violations: violations}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func DecodeReconnectRequest(data []byte) (ReconnectRequest, error) {
|
|
var value ReconnectRequest
|
|
if len(data) > 1024*1024 {
|
|
return value, errors.New("protocol payload exceeds limit")
|
|
}
|
|
if err := rejectDuplicateJSONKeys(data); err != nil {
|
|
return value, err
|
|
}
|
|
var fields map[string]json.RawMessage
|
|
if err := json.Unmarshal(data, &fields); err != nil {
|
|
return value, err
|
|
}
|
|
if raw, ok := fields["client_device_id"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "client_device_id", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["device_key_id"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "device_key_id", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["display_relaunch_confirmed"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "display_relaunch_confirmed", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["expected_version"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "expected_version", 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 EncodeReconnectRequest(value ReconnectRequest) ([]byte, error) {
|
|
if err := value.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(value)
|
|
}
|
|
|
|
func (v RefreshRequest) Validate() error {
|
|
var violations []FieldViolation
|
|
if v.FamilyID == "" {
|
|
violations = append(violations, FieldViolation{Field: "family_id", Code: "required"})
|
|
}
|
|
if len(v.FamilyID) < 1 && v.FamilyID != "" {
|
|
violations = append(violations, FieldViolation{Field: "family_id", Code: "min_length"})
|
|
}
|
|
if len(v.FamilyID) > 128 {
|
|
violations = append(violations, FieldViolation{Field: "family_id", Code: "max_length"})
|
|
}
|
|
if v.RefreshToken == "" {
|
|
violations = append(violations, FieldViolation{Field: "refresh_token", Code: "required"})
|
|
}
|
|
if len(v.RefreshToken) < 1 && v.RefreshToken != "" {
|
|
violations = append(violations, FieldViolation{Field: "refresh_token", Code: "min_length"})
|
|
}
|
|
if len(v.RefreshToken) > 256 {
|
|
violations = append(violations, FieldViolation{Field: "refresh_token", Code: "max_length"})
|
|
}
|
|
if len(violations) > 0 {
|
|
return ValidationError{Violations: violations}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func DecodeRefreshRequest(data []byte) (RefreshRequest, error) {
|
|
var value RefreshRequest
|
|
if len(data) > 1024*1024 {
|
|
return value, errors.New("protocol payload exceeds limit")
|
|
}
|
|
if err := rejectDuplicateJSONKeys(data); err != nil {
|
|
return value, err
|
|
}
|
|
var fields map[string]json.RawMessage
|
|
if err := json.Unmarshal(data, &fields); err != nil {
|
|
return value, err
|
|
}
|
|
if raw, ok := fields["family_id"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "family_id", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["refresh_token"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "refresh_token", 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 EncodeRefreshRequest(value RefreshRequest) ([]byte, error) {
|
|
if err := value.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(value)
|
|
}
|
|
|
|
func (v Resource) Validate() error {
|
|
var violations []FieldViolation
|
|
if v.ID == "" {
|
|
violations = append(violations, FieldViolation{Field: "id", Code: "required"})
|
|
}
|
|
if len(v.ID) < 1 && v.ID != "" {
|
|
violations = append(violations, FieldViolation{Field: "id", Code: "min_length"})
|
|
}
|
|
if len(v.ID) > 128 {
|
|
violations = append(violations, FieldViolation{Field: "id", Code: "max_length"})
|
|
}
|
|
if v.Kind == "" {
|
|
violations = append(violations, FieldViolation{Field: "kind", Code: "required"})
|
|
}
|
|
if len(v.Kind) < 1 && v.Kind != "" {
|
|
violations = append(violations, FieldViolation{Field: "kind", Code: "min_length"})
|
|
}
|
|
if len(v.Kind) > 64 {
|
|
violations = append(violations, FieldViolation{Field: "kind", Code: "max_length"})
|
|
}
|
|
if v.Name == "" {
|
|
violations = append(violations, FieldViolation{Field: "name", Code: "required"})
|
|
}
|
|
if len(v.Name) < 1 && v.Name != "" {
|
|
violations = append(violations, FieldViolation{Field: "name", Code: "min_length"})
|
|
}
|
|
if len(v.Name) > 256 {
|
|
violations = append(violations, FieldViolation{Field: "name", Code: "max_length"})
|
|
}
|
|
if v.State == "" {
|
|
violations = append(violations, FieldViolation{Field: "state", Code: "required"})
|
|
}
|
|
if len(v.State) < 1 && v.State != "" {
|
|
violations = append(violations, FieldViolation{Field: "state", Code: "min_length"})
|
|
}
|
|
if len(v.State) > 64 {
|
|
violations = append(violations, FieldViolation{Field: "state", Code: "max_length"})
|
|
}
|
|
if len(v.AssignmentState) > 64 {
|
|
violations = append(violations, FieldViolation{Field: "assignment_state", Code: "max_length"})
|
|
}
|
|
if v.Version == 0 {
|
|
violations = append(violations, FieldViolation{Field: "version", Code: "required"})
|
|
}
|
|
if v.Version != 0 && v.Version < 1 {
|
|
violations = append(violations, FieldViolation{Field: "version", Code: "minimum"})
|
|
}
|
|
if v.Links == nil {
|
|
violations = append(violations, FieldViolation{Field: "links", Code: "required"})
|
|
}
|
|
if len(v.Links) > 16 {
|
|
violations = append(violations, FieldViolation{Field: "links", Code: "max_items"})
|
|
}
|
|
for index := range v.Links {
|
|
if err := v.Links[index].Validate(); err != nil {
|
|
violations = append(violations, FieldViolation{Field: fmt.Sprintf("links[%d]", index), Code: "invalid_item"})
|
|
}
|
|
}
|
|
if len(violations) > 0 {
|
|
return ValidationError{Violations: violations}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func DecodeResource(data []byte) (Resource, error) {
|
|
var value Resource
|
|
if len(data) > 1024*1024 {
|
|
return value, errors.New("protocol payload exceeds limit")
|
|
}
|
|
if err := rejectDuplicateJSONKeys(data); err != nil {
|
|
return value, err
|
|
}
|
|
var fields map[string]json.RawMessage
|
|
if err := json.Unmarshal(data, &fields); err != nil {
|
|
return value, err
|
|
}
|
|
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["kind"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "kind", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["links"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "links", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["name"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "name", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["state"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "state", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["version"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "version", 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 EncodeResource(value Resource) ([]byte, error) {
|
|
if err := value.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(value)
|
|
}
|
|
|
|
func (v ResourceLink) Validate() error {
|
|
var violations []FieldViolation
|
|
if v.Type == "" {
|
|
violations = append(violations, FieldViolation{Field: "type", Code: "required"})
|
|
}
|
|
if len(v.Type) < 1 && v.Type != "" {
|
|
violations = append(violations, FieldViolation{Field: "type", Code: "min_length"})
|
|
}
|
|
if len(v.Type) > 64 {
|
|
violations = append(violations, FieldViolation{Field: "type", Code: "max_length"})
|
|
}
|
|
if v.ID == "" {
|
|
violations = append(violations, FieldViolation{Field: "id", Code: "required"})
|
|
}
|
|
if len(v.ID) < 1 && v.ID != "" {
|
|
violations = append(violations, FieldViolation{Field: "id", Code: "min_length"})
|
|
}
|
|
if len(v.ID) > 128 {
|
|
violations = append(violations, FieldViolation{Field: "id", Code: "max_length"})
|
|
}
|
|
if v.Version == 0 {
|
|
violations = append(violations, FieldViolation{Field: "version", Code: "required"})
|
|
}
|
|
if v.Version != 0 && v.Version < 1 {
|
|
violations = append(violations, FieldViolation{Field: "version", Code: "minimum"})
|
|
}
|
|
if len(violations) > 0 {
|
|
return ValidationError{Violations: violations}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func DecodeResourceLink(data []byte) (ResourceLink, error) {
|
|
var value ResourceLink
|
|
if len(data) > 1024*1024 {
|
|
return value, errors.New("protocol payload exceeds limit")
|
|
}
|
|
if err := rejectDuplicateJSONKeys(data); err != nil {
|
|
return value, err
|
|
}
|
|
var fields map[string]json.RawMessage
|
|
if err := json.Unmarshal(data, &fields); err != nil {
|
|
return value, err
|
|
}
|
|
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["type"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "type", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["version"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "version", 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 EncodeResourceLink(value ResourceLink) ([]byte, error) {
|
|
if err := value.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(value)
|
|
}
|
|
|
|
func (v ResourceList) Validate() error {
|
|
var violations []FieldViolation
|
|
if v.AssignedDesktops == nil {
|
|
violations = append(violations, FieldViolation{Field: "assigned_desktops", Code: "required"})
|
|
}
|
|
if len(v.AssignedDesktops) > 100 {
|
|
violations = append(violations, FieldViolation{Field: "assigned_desktops", Code: "max_items"})
|
|
}
|
|
for index := range v.AssignedDesktops {
|
|
if err := v.AssignedDesktops[index].Validate(); err != nil {
|
|
violations = append(violations, FieldViolation{Field: fmt.Sprintf("assigned_desktops[%d]", index), Code: "invalid_item"})
|
|
}
|
|
}
|
|
if v.EntitledPools == nil {
|
|
violations = append(violations, FieldViolation{Field: "entitled_pools", Code: "required"})
|
|
}
|
|
if len(v.EntitledPools) > 100 {
|
|
violations = append(violations, FieldViolation{Field: "entitled_pools", Code: "max_items"})
|
|
}
|
|
for index := range v.EntitledPools {
|
|
if err := v.EntitledPools[index].Validate(); err != nil {
|
|
violations = append(violations, FieldViolation{Field: fmt.Sprintf("entitled_pools[%d]", index), Code: "invalid_item"})
|
|
}
|
|
}
|
|
if reflect.DeepEqual(v.Page, PageInfo{}) {
|
|
violations = append(violations, FieldViolation{Field: "page", Code: "required"})
|
|
}
|
|
if err := v.Page.Validate(); err != nil {
|
|
violations = append(violations, FieldViolation{Field: "page", Code: "invalid_object"})
|
|
}
|
|
if len(violations) > 0 {
|
|
return ValidationError{Violations: violations}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func DecodeResourceList(data []byte) (ResourceList, error) {
|
|
var value ResourceList
|
|
if len(data) > 1024*1024 {
|
|
return value, errors.New("protocol payload exceeds limit")
|
|
}
|
|
if err := rejectDuplicateJSONKeys(data); err != nil {
|
|
return value, err
|
|
}
|
|
var fields map[string]json.RawMessage
|
|
if err := json.Unmarshal(data, &fields); err != nil {
|
|
return value, err
|
|
}
|
|
if raw, ok := fields["assigned_desktops"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "assigned_desktops", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["entitled_pools"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "entitled_pools", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["page"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "page", 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 EncodeResourceList(value ResourceList) ([]byte, error) {
|
|
if err := value.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(value)
|
|
}
|
|
|
|
func (v SelectedSessionDescriptor) Validate() error {
|
|
var violations []FieldViolation
|
|
if reflect.DeepEqual(v.VideoProfile, VideoProfile{}) {
|
|
violations = append(violations, FieldViolation{Field: "video_profile", Code: "required"})
|
|
}
|
|
if err := v.VideoProfile.Validate(); err != nil {
|
|
violations = append(violations, FieldViolation{Field: "video_profile", Code: "invalid_object"})
|
|
}
|
|
if reflect.DeepEqual(v.AudioProfile, AudioProfile{}) {
|
|
violations = append(violations, FieldViolation{Field: "audio_profile", Code: "required"})
|
|
}
|
|
if err := v.AudioProfile.Validate(); err != nil {
|
|
violations = append(violations, FieldViolation{Field: "audio_profile", Code: "invalid_object"})
|
|
}
|
|
if reflect.DeepEqual(v.DisplayMode, DisplayMode{}) {
|
|
violations = append(violations, FieldViolation{Field: "display_mode", Code: "required"})
|
|
}
|
|
if err := v.DisplayMode.Validate(); err != nil {
|
|
violations = append(violations, FieldViolation{Field: "display_mode", Code: "invalid_object"})
|
|
}
|
|
if v.BitrateTargetKbps == 0 {
|
|
violations = append(violations, FieldViolation{Field: "bitrate_target_kbps", Code: "required"})
|
|
}
|
|
if v.BitrateTargetKbps != 0 && v.BitrateTargetKbps < 100 {
|
|
violations = append(violations, FieldViolation{Field: "bitrate_target_kbps", Code: "minimum"})
|
|
}
|
|
if v.BitrateTargetKbps > 1000000 {
|
|
violations = append(violations, FieldViolation{Field: "bitrate_target_kbps", Code: "maximum"})
|
|
}
|
|
if v.BitrateMaximumKbps == 0 {
|
|
violations = append(violations, FieldViolation{Field: "bitrate_maximum_kbps", Code: "required"})
|
|
}
|
|
if v.BitrateMaximumKbps != 0 && v.BitrateMaximumKbps < 100 {
|
|
violations = append(violations, FieldViolation{Field: "bitrate_maximum_kbps", Code: "minimum"})
|
|
}
|
|
if v.BitrateMaximumKbps > 1000000 {
|
|
violations = append(violations, FieldViolation{Field: "bitrate_maximum_kbps", Code: "maximum"})
|
|
}
|
|
if reflect.DeepEqual(v.Adjustment, SessionAdjustment{}) {
|
|
violations = append(violations, FieldViolation{Field: "adjustment", Code: "required"})
|
|
}
|
|
if err := v.Adjustment.Validate(); err != nil {
|
|
violations = append(violations, FieldViolation{Field: "adjustment", Code: "invalid_object"})
|
|
}
|
|
if v.MediaTimestampBasis == "" {
|
|
violations = append(violations, FieldViolation{Field: "media_timestamp_basis", Code: "required"})
|
|
}
|
|
if v.MediaTimestampBasis != "gateway-send-wall-clock-ms" && v.MediaTimestampBasis != "" {
|
|
violations = append(violations, FieldViolation{Field: "media_timestamp_basis", Code: "invalid_value"})
|
|
}
|
|
if v.BitrateTargetKbps > v.BitrateMaximumKbps {
|
|
violations = append(violations, FieldViolation{Field: "bitrate_bounds", Code: "invalid_order"})
|
|
}
|
|
if len(violations) > 0 {
|
|
return ValidationError{Violations: violations}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func DecodeSelectedSessionDescriptor(data []byte) (SelectedSessionDescriptor, error) {
|
|
var value SelectedSessionDescriptor
|
|
if len(data) > 1024*1024 {
|
|
return value, errors.New("protocol payload exceeds limit")
|
|
}
|
|
if err := rejectDuplicateJSONKeys(data); err != nil {
|
|
return value, err
|
|
}
|
|
var fields map[string]json.RawMessage
|
|
if err := json.Unmarshal(data, &fields); err != nil {
|
|
return value, err
|
|
}
|
|
if raw, ok := fields["adjustment"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "adjustment", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["audio_profile"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "audio_profile", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["bitrate_maximum_kbps"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "bitrate_maximum_kbps", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["bitrate_target_kbps"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "bitrate_target_kbps", 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: "required"}}}
|
|
}
|
|
if raw, ok := fields["media_timestamp_basis"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "media_timestamp_basis", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["video_profile"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "video_profile", 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 EncodeSelectedSessionDescriptor(value SelectedSessionDescriptor) ([]byte, error) {
|
|
if err := value.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(value)
|
|
}
|
|
|
|
func (v SessionAdjustment) Validate() error {
|
|
var violations []FieldViolation
|
|
if v.DisplayReason == "" {
|
|
violations = append(violations, FieldViolation{Field: "display_reason", Code: "required"})
|
|
}
|
|
if v.DisplayReason != "" && !(v.DisplayReason == "none" || v.DisplayReason == "policy_clamp" || v.DisplayReason == "direct_entitlement_override") {
|
|
violations = append(violations, FieldViolation{Field: "display_reason", Code: "invalid_value"})
|
|
}
|
|
if v.BitrateReason == "" {
|
|
violations = append(violations, FieldViolation{Field: "bitrate_reason", Code: "required"})
|
|
}
|
|
if v.BitrateReason != "" && !(v.BitrateReason == "none" || v.BitrateReason == "policy_default" || v.BitrateReason == "session_ceiling_clamp" || v.BitrateReason == "aggregate_capacity_clamp") {
|
|
violations = append(violations, FieldViolation{Field: "bitrate_reason", Code: "invalid_value"})
|
|
}
|
|
if len(violations) > 0 {
|
|
return ValidationError{Violations: violations}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func DecodeSessionAdjustment(data []byte) (SessionAdjustment, error) {
|
|
var value SessionAdjustment
|
|
if len(data) > 1024*1024 {
|
|
return value, errors.New("protocol payload exceeds limit")
|
|
}
|
|
if err := rejectDuplicateJSONKeys(data); err != nil {
|
|
return value, err
|
|
}
|
|
var fields map[string]json.RawMessage
|
|
if err := json.Unmarshal(data, &fields); err != nil {
|
|
return value, err
|
|
}
|
|
if raw, ok := fields["bitrate_reason"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "bitrate_reason", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["display_reason"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "display_reason", 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 EncodeSessionAdjustment(value SessionAdjustment) ([]byte, error) {
|
|
if err := value.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(value)
|
|
}
|
|
|
|
func (v SessionAuthority) Validate() error {
|
|
var violations []FieldViolation
|
|
if v.Version == "" {
|
|
violations = append(violations, FieldViolation{Field: "version", Code: "required"})
|
|
}
|
|
if v.Version != "1" && v.Version != "" {
|
|
violations = append(violations, FieldViolation{Field: "version", Code: "invalid_value"})
|
|
}
|
|
if v.SessionID == "" {
|
|
violations = append(violations, FieldViolation{Field: "session_id", Code: "required"})
|
|
}
|
|
if len(v.SessionID) < 1 && v.SessionID != "" {
|
|
violations = append(violations, FieldViolation{Field: "session_id", Code: "min_length"})
|
|
}
|
|
if len(v.SessionID) > 128 {
|
|
violations = append(violations, FieldViolation{Field: "session_id", Code: "max_length"})
|
|
}
|
|
if v.GatewayID == "" {
|
|
violations = append(violations, FieldViolation{Field: "gateway_id", Code: "required"})
|
|
}
|
|
if len(v.GatewayID) < 1 && v.GatewayID != "" {
|
|
violations = append(violations, FieldViolation{Field: "gateway_id", Code: "min_length"})
|
|
}
|
|
if len(v.GatewayID) > 128 {
|
|
violations = append(violations, FieldViolation{Field: "gateway_id", Code: "max_length"})
|
|
}
|
|
if v.Audience == "" {
|
|
violations = append(violations, FieldViolation{Field: "audience", Code: "required"})
|
|
}
|
|
if len(v.Audience) < 1 && v.Audience != "" {
|
|
violations = append(violations, FieldViolation{Field: "audience", Code: "min_length"})
|
|
}
|
|
if len(v.Audience) > 256 {
|
|
violations = append(violations, FieldViolation{Field: "audience", Code: "max_length"})
|
|
}
|
|
if v.ReconnectSequence != 0 && v.ReconnectSequence < 0 {
|
|
violations = append(violations, FieldViolation{Field: "reconnect_sequence", Code: "minimum"})
|
|
}
|
|
if v.ExpiresAt == "" {
|
|
violations = append(violations, FieldViolation{Field: "expires_at", Code: "required"})
|
|
}
|
|
if len(v.ExpiresAt) > 64 {
|
|
violations = append(violations, FieldViolation{Field: "expires_at", Code: "max_length"})
|
|
}
|
|
if v.ExpiresAt != "" {
|
|
if parsed, err := time.Parse(time.RFC3339Nano, v.ExpiresAt); err != nil || parsed.UTC().Format(time.RFC3339Nano) != v.ExpiresAt {
|
|
violations = append(violations, FieldViolation{Field: "expires_at", Code: "invalid_time"})
|
|
}
|
|
}
|
|
if reflect.DeepEqual(v.Capabilities, CapabilityProfile{}) {
|
|
violations = append(violations, FieldViolation{Field: "capabilities", Code: "required"})
|
|
}
|
|
if err := v.Capabilities.Validate(); err != nil {
|
|
violations = append(violations, FieldViolation{Field: "capabilities", Code: "invalid_object"})
|
|
}
|
|
if v.ProviderProfile == "" {
|
|
violations = append(violations, FieldViolation{Field: "provider_profile", Code: "required"})
|
|
}
|
|
if v.ProviderProfile != "" && !(v.ProviderProfile == "apollo") {
|
|
violations = append(violations, FieldViolation{Field: "provider_profile", Code: "invalid_value"})
|
|
}
|
|
if v.ProviderIdentity == "" {
|
|
violations = append(violations, FieldViolation{Field: "provider_identity", Code: "required"})
|
|
}
|
|
if len(v.ProviderIdentity) < 1 && v.ProviderIdentity != "" {
|
|
violations = append(violations, FieldViolation{Field: "provider_identity", Code: "min_length"})
|
|
}
|
|
if len(v.ProviderIdentity) > 256 {
|
|
violations = append(violations, FieldViolation{Field: "provider_identity", Code: "max_length"})
|
|
}
|
|
if reflect.DeepEqual(v.SelectedDescriptor, SelectedSessionDescriptor{}) {
|
|
violations = append(violations, FieldViolation{Field: "selected_descriptor", Code: "required"})
|
|
}
|
|
if err := v.SelectedDescriptor.Validate(); err != nil {
|
|
violations = append(violations, FieldViolation{Field: "selected_descriptor", Code: "invalid_object"})
|
|
}
|
|
if len(violations) > 0 {
|
|
return ValidationError{Violations: violations}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func DecodeSessionAuthority(data []byte) (SessionAuthority, error) {
|
|
var value SessionAuthority
|
|
if len(data) > 1024*1024 {
|
|
return value, errors.New("protocol payload exceeds limit")
|
|
}
|
|
if err := rejectDuplicateJSONKeys(data); err != nil {
|
|
return value, err
|
|
}
|
|
var fields map[string]json.RawMessage
|
|
if err := json.Unmarshal(data, &fields); err != nil {
|
|
return value, err
|
|
}
|
|
if raw, ok := fields["audience"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "audience", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["capabilities"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "capabilities", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["expires_at"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "expires_at", 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["provider_identity"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "provider_identity", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["provider_profile"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "provider_profile", 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["selected_descriptor"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "selected_descriptor", 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"}}}
|
|
}
|
|
if raw, ok := fields["version"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "version", 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 EncodeSessionAuthority(value SessionAuthority) ([]byte, error) {
|
|
if err := value.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(value)
|
|
}
|
|
|
|
func (v SessionQualityLimits) Validate() error {
|
|
var violations []FieldViolation
|
|
if v.PolicyVersionID == "" {
|
|
violations = append(violations, FieldViolation{Field: "policy_version_id", Code: "required"})
|
|
}
|
|
if len(v.PolicyVersionID) < 1 && v.PolicyVersionID != "" {
|
|
violations = append(violations, FieldViolation{Field: "policy_version_id", Code: "min_length"})
|
|
}
|
|
if len(v.PolicyVersionID) > 128 {
|
|
violations = append(violations, FieldViolation{Field: "policy_version_id", Code: "max_length"})
|
|
}
|
|
if reflect.DeepEqual(v.PolicyDisplayLimit, DisplayMode{}) {
|
|
violations = append(violations, FieldViolation{Field: "policy_display_limit", Code: "required"})
|
|
}
|
|
if err := v.PolicyDisplayLimit.Validate(); err != nil {
|
|
violations = append(violations, FieldViolation{Field: "policy_display_limit", Code: "invalid_object"})
|
|
}
|
|
if reflect.DeepEqual(v.SelectableDisplayMaximum, DisplayMode{}) {
|
|
violations = append(violations, FieldViolation{Field: "selectable_display_maximum", Code: "required"})
|
|
}
|
|
if err := v.SelectableDisplayMaximum.Validate(); err != nil {
|
|
violations = append(violations, FieldViolation{Field: "selectable_display_maximum", Code: "invalid_object"})
|
|
}
|
|
if v.BitrateMinimumKbps == 0 {
|
|
violations = append(violations, FieldViolation{Field: "bitrate_minimum_kbps", Code: "required"})
|
|
}
|
|
if v.BitrateMinimumKbps != 0 && v.BitrateMinimumKbps < 100 {
|
|
violations = append(violations, FieldViolation{Field: "bitrate_minimum_kbps", Code: "minimum"})
|
|
}
|
|
if v.BitrateMinimumKbps > 1000000 {
|
|
violations = append(violations, FieldViolation{Field: "bitrate_minimum_kbps", Code: "maximum"})
|
|
}
|
|
if v.BitrateTargetKbps == 0 {
|
|
violations = append(violations, FieldViolation{Field: "bitrate_target_kbps", Code: "required"})
|
|
}
|
|
if v.BitrateTargetKbps != 0 && v.BitrateTargetKbps < 100 {
|
|
violations = append(violations, FieldViolation{Field: "bitrate_target_kbps", Code: "minimum"})
|
|
}
|
|
if v.BitrateTargetKbps > 1000000 {
|
|
violations = append(violations, FieldViolation{Field: "bitrate_target_kbps", Code: "maximum"})
|
|
}
|
|
if v.BitrateMaximumKbps == 0 {
|
|
violations = append(violations, FieldViolation{Field: "bitrate_maximum_kbps", Code: "required"})
|
|
}
|
|
if v.BitrateMaximumKbps != 0 && v.BitrateMaximumKbps < 100 {
|
|
violations = append(violations, FieldViolation{Field: "bitrate_maximum_kbps", Code: "minimum"})
|
|
}
|
|
if v.BitrateMaximumKbps > 1000000 {
|
|
violations = append(violations, FieldViolation{Field: "bitrate_maximum_kbps", Code: "maximum"})
|
|
}
|
|
if v.BitrateMinimumKbps > v.BitrateTargetKbps || v.BitrateTargetKbps > v.BitrateMaximumKbps {
|
|
violations = append(violations, FieldViolation{Field: "bitrate_bounds", Code: "invalid_order"})
|
|
}
|
|
if len(violations) > 0 {
|
|
return ValidationError{Violations: violations}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func DecodeSessionQualityLimits(data []byte) (SessionQualityLimits, error) {
|
|
var value SessionQualityLimits
|
|
if len(data) > 1024*1024 {
|
|
return value, errors.New("protocol payload exceeds limit")
|
|
}
|
|
if err := rejectDuplicateJSONKeys(data); err != nil {
|
|
return value, err
|
|
}
|
|
var fields map[string]json.RawMessage
|
|
if err := json.Unmarshal(data, &fields); err != nil {
|
|
return value, err
|
|
}
|
|
if raw, ok := fields["bitrate_maximum_kbps"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "bitrate_maximum_kbps", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["bitrate_minimum_kbps"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "bitrate_minimum_kbps", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["bitrate_target_kbps"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "bitrate_target_kbps", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["display_limit_override"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "display_limit_override", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["policy_display_limit"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "policy_display_limit", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["policy_version_id"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "policy_version_id", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["selectable_display_maximum"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "selectable_display_maximum", 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 EncodeSessionQualityLimits(value SessionQualityLimits) ([]byte, error) {
|
|
if err := value.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(value)
|
|
}
|
|
|
|
func (v SessionRequest) Validate() error {
|
|
var violations []FieldViolation
|
|
if v.ClientDeviceID == "" {
|
|
violations = append(violations, FieldViolation{Field: "client_device_id", Code: "required"})
|
|
}
|
|
if len(v.ClientDeviceID) < 1 && v.ClientDeviceID != "" {
|
|
violations = append(violations, FieldViolation{Field: "client_device_id", Code: "min_length"})
|
|
}
|
|
if len(v.ClientDeviceID) > 128 {
|
|
violations = append(violations, FieldViolation{Field: "client_device_id", Code: "max_length"})
|
|
}
|
|
if v.DeviceKeyID == "" {
|
|
violations = append(violations, FieldViolation{Field: "device_key_id", Code: "required"})
|
|
}
|
|
if len(v.DeviceKeyID) < 1 && v.DeviceKeyID != "" {
|
|
violations = append(violations, FieldViolation{Field: "device_key_id", Code: "min_length"})
|
|
}
|
|
if len(v.DeviceKeyID) > 128 {
|
|
violations = append(violations, FieldViolation{Field: "device_key_id", Code: "max_length"})
|
|
}
|
|
if v.PoolID == "" {
|
|
violations = append(violations, FieldViolation{Field: "pool_id", Code: "required"})
|
|
}
|
|
if len(v.PoolID) < 1 && v.PoolID != "" {
|
|
violations = append(violations, FieldViolation{Field: "pool_id", Code: "min_length"})
|
|
}
|
|
if len(v.PoolID) > 128 {
|
|
violations = append(violations, FieldViolation{Field: "pool_id", Code: "max_length"})
|
|
}
|
|
if v.IdempotencyKey == "" {
|
|
violations = append(violations, FieldViolation{Field: "idempotency_key", Code: "required"})
|
|
}
|
|
if len(v.IdempotencyKey) < 1 && v.IdempotencyKey != "" {
|
|
violations = append(violations, FieldViolation{Field: "idempotency_key", Code: "min_length"})
|
|
}
|
|
if len(v.IdempotencyKey) > 256 {
|
|
violations = append(violations, FieldViolation{Field: "idempotency_key", Code: "max_length"})
|
|
}
|
|
if v.VideoProfiles == nil {
|
|
violations = append(violations, FieldViolation{Field: "video_profiles", Code: "required"})
|
|
}
|
|
if len(v.VideoProfiles) < 1 {
|
|
violations = append(violations, FieldViolation{Field: "video_profiles", Code: "min_items"})
|
|
}
|
|
if len(v.VideoProfiles) > 12 {
|
|
violations = append(violations, FieldViolation{Field: "video_profiles", Code: "max_items"})
|
|
}
|
|
for index, item := range v.VideoProfiles {
|
|
for prior := 0; prior < index; prior++ {
|
|
if reflect.DeepEqual(item, v.VideoProfiles[prior]) {
|
|
violations = append(violations, FieldViolation{Field: "video_profiles", Code: "duplicate_item"})
|
|
}
|
|
}
|
|
}
|
|
for index := range v.VideoProfiles {
|
|
if err := v.VideoProfiles[index].Validate(); err != nil {
|
|
violations = append(violations, FieldViolation{Field: fmt.Sprintf("video_profiles[%d]", index), Code: "invalid_item"})
|
|
}
|
|
}
|
|
if reflect.DeepEqual(v.BitratePreference, BitratePreference{}) {
|
|
violations = append(violations, FieldViolation{Field: "bitrate_preference", Code: "required"})
|
|
}
|
|
if err := v.BitratePreference.Validate(); err != nil {
|
|
violations = append(violations, FieldViolation{Field: "bitrate_preference", 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}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func DecodeSessionRequest(data []byte) (SessionRequest, error) {
|
|
var value SessionRequest
|
|
if len(data) > 1024*1024 {
|
|
return value, errors.New("protocol payload exceeds limit")
|
|
}
|
|
if err := rejectDuplicateJSONKeys(data); err != nil {
|
|
return value, err
|
|
}
|
|
var fields map[string]json.RawMessage
|
|
if err := json.Unmarshal(data, &fields); err != nil {
|
|
return value, err
|
|
}
|
|
if raw, ok := fields["bitrate_preference"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "bitrate_preference", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["client_device_id"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "client_device_id", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["device_key_id"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "device_key_id", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["idempotency_key"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "idempotency_key", Code: "required"}}}
|
|
}
|
|
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["video_profiles"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "video_profiles", 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 {
|
|
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 EncodeSessionRequest(value SessionRequest) ([]byte, error) {
|
|
if err := value.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(value)
|
|
}
|
|
|
|
func (v StableError) Validate() error {
|
|
var violations []FieldViolation
|
|
if v.Version == "" {
|
|
violations = append(violations, FieldViolation{Field: "version", Code: "required"})
|
|
}
|
|
if v.Version != "1" && v.Version != "" {
|
|
violations = append(violations, FieldViolation{Field: "version", Code: "invalid_value"})
|
|
}
|
|
if v.Code == "" {
|
|
violations = append(violations, FieldViolation{Field: "code", Code: "required"})
|
|
}
|
|
if len(v.Code) < 1 && v.Code != "" {
|
|
violations = append(violations, FieldViolation{Field: "code", Code: "min_length"})
|
|
}
|
|
if len(v.Code) > 128 {
|
|
violations = append(violations, FieldViolation{Field: "code", Code: "max_length"})
|
|
}
|
|
if v.Message == "" {
|
|
violations = append(violations, FieldViolation{Field: "message", Code: "required"})
|
|
}
|
|
if len(v.Message) < 1 && v.Message != "" {
|
|
violations = append(violations, FieldViolation{Field: "message", Code: "min_length"})
|
|
}
|
|
if len(v.Message) > 512 {
|
|
violations = append(violations, FieldViolation{Field: "message", Code: "max_length"})
|
|
}
|
|
if len(violations) > 0 {
|
|
return ValidationError{Violations: violations}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func DecodeStableError(data []byte) (StableError, error) {
|
|
var value StableError
|
|
if len(data) > 1024*1024 {
|
|
return value, errors.New("protocol payload exceeds limit")
|
|
}
|
|
if err := rejectDuplicateJSONKeys(data); err != nil {
|
|
return value, err
|
|
}
|
|
var fields map[string]json.RawMessage
|
|
if err := json.Unmarshal(data, &fields); err != nil {
|
|
return value, err
|
|
}
|
|
if raw, ok := fields["code"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "code", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["message"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "message", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["retryable"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "retryable", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["version"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "version", 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 EncodeStableError(value StableError) ([]byte, error) {
|
|
if err := value.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(value)
|
|
}
|
|
|
|
func (v StopOperation) Validate() error {
|
|
var violations []FieldViolation
|
|
if v.OperationID == "" {
|
|
violations = append(violations, FieldViolation{Field: "operation_id", Code: "required"})
|
|
}
|
|
if len(v.OperationID) < 36 && v.OperationID != "" {
|
|
violations = append(violations, FieldViolation{Field: "operation_id", Code: "min_length"})
|
|
}
|
|
if len(v.OperationID) > 36 {
|
|
violations = append(violations, FieldViolation{Field: "operation_id", Code: "max_length"})
|
|
}
|
|
if v.OperationID != "" && !validCanonicalUUID(v.OperationID) {
|
|
violations = append(violations, FieldViolation{Field: "operation_id", Code: "invalid_uuid"})
|
|
}
|
|
if v.SessionID == "" {
|
|
violations = append(violations, FieldViolation{Field: "session_id", Code: "required"})
|
|
}
|
|
if len(v.SessionID) < 1 && v.SessionID != "" {
|
|
violations = append(violations, FieldViolation{Field: "session_id", Code: "min_length"})
|
|
}
|
|
if len(v.SessionID) > 128 {
|
|
violations = append(violations, FieldViolation{Field: "session_id", Code: "max_length"})
|
|
}
|
|
if v.State == "" {
|
|
violations = append(violations, FieldViolation{Field: "state", Code: "required"})
|
|
}
|
|
if v.State != "" && !(v.State == "pending" || v.State == "attempting" || v.State == "applied" || v.State == "failed" || v.State == "termination_unconfirmed") {
|
|
violations = append(violations, FieldViolation{Field: "state", Code: "invalid_value"})
|
|
}
|
|
if v.SessionVersion == 0 {
|
|
violations = append(violations, FieldViolation{Field: "session_version", Code: "required"})
|
|
}
|
|
if v.SessionVersion != 0 && v.SessionVersion < 1 {
|
|
violations = append(violations, FieldViolation{Field: "session_version", Code: "minimum"})
|
|
}
|
|
if v.CreatedAt == "" {
|
|
violations = append(violations, FieldViolation{Field: "created_at", Code: "required"})
|
|
}
|
|
if len(v.CreatedAt) > 64 {
|
|
violations = append(violations, FieldViolation{Field: "created_at", Code: "max_length"})
|
|
}
|
|
if v.CreatedAt != "" {
|
|
if parsed, err := time.Parse(time.RFC3339Nano, v.CreatedAt); err != nil || parsed.UTC().Format(time.RFC3339Nano) != v.CreatedAt {
|
|
violations = append(violations, FieldViolation{Field: "created_at", Code: "invalid_time"})
|
|
}
|
|
}
|
|
if v.DeadlineAt == "" {
|
|
violations = append(violations, FieldViolation{Field: "deadline_at", Code: "required"})
|
|
}
|
|
if len(v.DeadlineAt) > 64 {
|
|
violations = append(violations, FieldViolation{Field: "deadline_at", Code: "max_length"})
|
|
}
|
|
if v.DeadlineAt != "" {
|
|
if parsed, err := time.Parse(time.RFC3339Nano, v.DeadlineAt); err != nil || parsed.UTC().Format(time.RFC3339Nano) != v.DeadlineAt {
|
|
violations = append(violations, FieldViolation{Field: "deadline_at", Code: "invalid_time"})
|
|
}
|
|
}
|
|
if v.UpdatedAt == "" {
|
|
violations = append(violations, FieldViolation{Field: "updated_at", Code: "required"})
|
|
}
|
|
if len(v.UpdatedAt) > 64 {
|
|
violations = append(violations, FieldViolation{Field: "updated_at", Code: "max_length"})
|
|
}
|
|
if v.UpdatedAt != "" {
|
|
if parsed, err := time.Parse(time.RFC3339Nano, v.UpdatedAt); err != nil || parsed.UTC().Format(time.RFC3339Nano) != v.UpdatedAt {
|
|
violations = append(violations, FieldViolation{Field: "updated_at", Code: "invalid_time"})
|
|
}
|
|
}
|
|
if len(v.FailureCode) < 1 && v.FailureCode != "" {
|
|
violations = append(violations, FieldViolation{Field: "failure_code", Code: "min_length"})
|
|
}
|
|
if len(v.FailureCode) > 128 {
|
|
violations = append(violations, FieldViolation{Field: "failure_code", Code: "max_length"})
|
|
}
|
|
if len(violations) > 0 {
|
|
return ValidationError{Violations: violations}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func DecodeStopOperation(data []byte) (StopOperation, error) {
|
|
var value StopOperation
|
|
if len(data) > 1024*1024 {
|
|
return value, errors.New("protocol payload exceeds limit")
|
|
}
|
|
if err := rejectDuplicateJSONKeys(data); err != nil {
|
|
return value, err
|
|
}
|
|
var fields map[string]json.RawMessage
|
|
if err := json.Unmarshal(data, &fields); err != nil {
|
|
return value, err
|
|
}
|
|
if raw, ok := fields["created_at"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "created_at", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["deadline_at"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "deadline_at", 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["session_id"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "session_id", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["session_version"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "session_version", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["state"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "state", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["updated_at"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "updated_at", 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 EncodeStopOperation(value StopOperation) ([]byte, error) {
|
|
if err := value.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(value)
|
|
}
|
|
|
|
func (v TunnelAdmissionRequest) Validate() error {
|
|
var violations []FieldViolation
|
|
if v.Version == "" {
|
|
violations = append(violations, FieldViolation{Field: "version", Code: "required"})
|
|
}
|
|
if v.Version != "1" && v.Version != "" {
|
|
violations = append(violations, FieldViolation{Field: "version", Code: "invalid_value"})
|
|
}
|
|
if v.SessionID == "" {
|
|
violations = append(violations, FieldViolation{Field: "session_id", Code: "required"})
|
|
}
|
|
if len(v.SessionID) < 1 && v.SessionID != "" {
|
|
violations = append(violations, FieldViolation{Field: "session_id", Code: "min_length"})
|
|
}
|
|
if len(v.SessionID) > 128 {
|
|
violations = append(violations, FieldViolation{Field: "session_id", Code: "max_length"})
|
|
}
|
|
if v.GatewayID == "" {
|
|
violations = append(violations, FieldViolation{Field: "gateway_id", Code: "required"})
|
|
}
|
|
if len(v.GatewayID) < 1 && v.GatewayID != "" {
|
|
violations = append(violations, FieldViolation{Field: "gateway_id", Code: "min_length"})
|
|
}
|
|
if len(v.GatewayID) > 128 {
|
|
violations = append(violations, FieldViolation{Field: "gateway_id", Code: "max_length"})
|
|
}
|
|
if v.Audience == "" {
|
|
violations = append(violations, FieldViolation{Field: "audience", Code: "required"})
|
|
}
|
|
if len(v.Audience) < 1 && v.Audience != "" {
|
|
violations = append(violations, FieldViolation{Field: "audience", Code: "min_length"})
|
|
}
|
|
if len(v.Audience) > 256 {
|
|
violations = append(violations, FieldViolation{Field: "audience", Code: "max_length"})
|
|
}
|
|
if v.Grant == "" {
|
|
violations = append(violations, FieldViolation{Field: "grant", Code: "required"})
|
|
}
|
|
if len(v.Grant) < 43 && v.Grant != "" {
|
|
violations = append(violations, FieldViolation{Field: "grant", Code: "min_length"})
|
|
}
|
|
if len(v.Grant) > 256 {
|
|
violations = append(violations, FieldViolation{Field: "grant", Code: "max_length"})
|
|
}
|
|
if v.ReconnectSequence != 0 && v.ReconnectSequence < 0 {
|
|
violations = append(violations, FieldViolation{Field: "reconnect_sequence", Code: "minimum"})
|
|
}
|
|
if v.ClientNonce == "" {
|
|
violations = append(violations, FieldViolation{Field: "client_nonce", Code: "required"})
|
|
}
|
|
if len(v.ClientNonce) < 16 && v.ClientNonce != "" {
|
|
violations = append(violations, FieldViolation{Field: "client_nonce", Code: "min_length"})
|
|
}
|
|
if len(v.ClientNonce) > 128 {
|
|
violations = append(violations, FieldViolation{Field: "client_nonce", Code: "max_length"})
|
|
}
|
|
if v.DeviceSignature == "" {
|
|
violations = append(violations, FieldViolation{Field: "device_signature", Code: "required"})
|
|
}
|
|
if len(v.DeviceSignature) < 86 && v.DeviceSignature != "" {
|
|
violations = append(violations, FieldViolation{Field: "device_signature", Code: "min_length"})
|
|
}
|
|
if len(v.DeviceSignature) > 86 {
|
|
violations = append(violations, FieldViolation{Field: "device_signature", Code: "max_length"})
|
|
}
|
|
if reflect.DeepEqual(v.Capabilities, CapabilityProfile{}) {
|
|
violations = append(violations, FieldViolation{Field: "capabilities", Code: "required"})
|
|
}
|
|
if err := v.Capabilities.Validate(); err != nil {
|
|
violations = append(violations, FieldViolation{Field: "capabilities", Code: "invalid_object"})
|
|
}
|
|
if len(violations) > 0 {
|
|
return ValidationError{Violations: violations}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func DecodeTunnelAdmissionRequest(data []byte) (TunnelAdmissionRequest, error) {
|
|
var value TunnelAdmissionRequest
|
|
if len(data) > 1024*1024 {
|
|
return value, errors.New("protocol payload exceeds limit")
|
|
}
|
|
if err := rejectDuplicateJSONKeys(data); err != nil {
|
|
return value, err
|
|
}
|
|
var fields map[string]json.RawMessage
|
|
if err := json.Unmarshal(data, &fields); err != nil {
|
|
return value, err
|
|
}
|
|
if raw, ok := fields["audience"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "audience", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["capabilities"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "capabilities", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["client_nonce"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "client_nonce", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["device_signature"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "device_signature", 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["grant"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "grant", 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["session_id"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "session_id", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["version"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "version", 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 EncodeTunnelAdmissionRequest(value TunnelAdmissionRequest) ([]byte, error) {
|
|
if err := value.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(value)
|
|
}
|
|
|
|
func (v VersionNegotiation) Validate() error {
|
|
var violations []FieldViolation
|
|
if v.SupportedVersions == nil {
|
|
violations = append(violations, FieldViolation{Field: "supported_versions", Code: "required"})
|
|
}
|
|
if len(v.SupportedVersions) < 1 {
|
|
violations = append(violations, FieldViolation{Field: "supported_versions", Code: "min_items"})
|
|
}
|
|
if len(v.SupportedVersions) > 3 {
|
|
violations = append(violations, FieldViolation{Field: "supported_versions", Code: "max_items"})
|
|
}
|
|
for _, item := range v.SupportedVersions {
|
|
if len(item) > 16 {
|
|
violations = append(violations, FieldViolation{Field: "supported_versions", Code: "max_item_length"})
|
|
}
|
|
}
|
|
if v.Features == nil {
|
|
violations = append(violations, FieldViolation{Field: "features", Code: "required"})
|
|
}
|
|
if len(v.Features) > 64 {
|
|
violations = append(violations, FieldViolation{Field: "features", Code: "max_items"})
|
|
}
|
|
for _, item := range v.Features {
|
|
if len(item) > 64 {
|
|
violations = append(violations, FieldViolation{Field: "features", Code: "max_item_length"})
|
|
}
|
|
}
|
|
if len(violations) > 0 {
|
|
return ValidationError{Violations: violations}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func DecodeVersionNegotiation(data []byte) (VersionNegotiation, error) {
|
|
var value VersionNegotiation
|
|
if len(data) > 1024*1024 {
|
|
return value, errors.New("protocol payload exceeds limit")
|
|
}
|
|
if err := rejectDuplicateJSONKeys(data); err != nil {
|
|
return value, err
|
|
}
|
|
var fields map[string]json.RawMessage
|
|
if err := json.Unmarshal(data, &fields); err != nil {
|
|
return value, err
|
|
}
|
|
if raw, ok := fields["features"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "features", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["supported_versions"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "supported_versions", 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 EncodeVersionNegotiation(value VersionNegotiation) ([]byte, error) {
|
|
if err := value.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(value)
|
|
}
|
|
|
|
func (v VideoProfile) Validate() error {
|
|
var violations []FieldViolation
|
|
if v.Codec == "" {
|
|
violations = append(violations, FieldViolation{Field: "codec", Code: "required"})
|
|
}
|
|
if v.Codec != "" && !(v.Codec == "h264" || v.Codec == "hevc" || v.Codec == "av1") {
|
|
violations = append(violations, FieldViolation{Field: "codec", Code: "invalid_value"})
|
|
}
|
|
if v.BitDepth == 0 {
|
|
violations = append(violations, FieldViolation{Field: "bit_depth", Code: "required"})
|
|
}
|
|
if v.BitDepth != 0 && v.BitDepth < 8 {
|
|
violations = append(violations, FieldViolation{Field: "bit_depth", Code: "minimum"})
|
|
}
|
|
if v.BitDepth > 8 {
|
|
violations = append(violations, FieldViolation{Field: "bit_depth", Code: "maximum"})
|
|
}
|
|
if v.ChromaSubsampling == "" {
|
|
violations = append(violations, FieldViolation{Field: "chroma_subsampling", Code: "required"})
|
|
}
|
|
if v.ChromaSubsampling != "4:2:0" && v.ChromaSubsampling != "" {
|
|
violations = append(violations, FieldViolation{Field: "chroma_subsampling", Code: "invalid_value"})
|
|
}
|
|
if v.ColorSpace == "" {
|
|
violations = append(violations, FieldViolation{Field: "color_space", Code: "required"})
|
|
}
|
|
if v.ColorSpace != "bt709-limited" && v.ColorSpace != "" {
|
|
violations = append(violations, FieldViolation{Field: "color_space", Code: "invalid_value"})
|
|
}
|
|
if v.TransferFunction == "" {
|
|
violations = append(violations, FieldViolation{Field: "transfer_function", Code: "required"})
|
|
}
|
|
if v.TransferFunction != "sdr" && v.TransferFunction != "" {
|
|
violations = append(violations, FieldViolation{Field: "transfer_function", Code: "invalid_value"})
|
|
}
|
|
if len(violations) > 0 {
|
|
return ValidationError{Violations: violations}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func DecodeVideoProfile(data []byte) (VideoProfile, error) {
|
|
var value VideoProfile
|
|
if len(data) > 1024*1024 {
|
|
return value, errors.New("protocol payload exceeds limit")
|
|
}
|
|
if err := rejectDuplicateJSONKeys(data); err != nil {
|
|
return value, err
|
|
}
|
|
var fields map[string]json.RawMessage
|
|
if err := json.Unmarshal(data, &fields); err != nil {
|
|
return value, err
|
|
}
|
|
if raw, ok := fields["bit_depth"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "bit_depth", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["chroma_subsampling"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "chroma_subsampling", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["codec"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "codec", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["color_space"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "color_space", Code: "required"}}}
|
|
}
|
|
if raw, ok := fields["transfer_function"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
|
return value, ValidationError{Violations: []FieldViolation{{Field: "transfer_function", 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 EncodeVideoProfile(value VideoProfile) ([]byte, error) {
|
|
if err := value.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
return json.Marshal(value)
|
|
}
|
|
|
|
func DeviceRegistrationProofTranscript(serverID, principalID, deviceID, challenge []byte, expiryUnixMilliseconds int64) ([]byte, error) {
|
|
for _, value := range []struct {
|
|
field string
|
|
bytes []byte
|
|
length int
|
|
}{{"server_id", serverID, 16}, {"principal_id", principalID, 16}, {"device_id", deviceID, 16}, {"challenge", challenge, 32}} {
|
|
if len(value.bytes) != value.length {
|
|
return nil, ValidationError{Violations: []FieldViolation{{Field: value.field, Code: "invalid_length"}}}
|
|
}
|
|
}
|
|
if expiryUnixMilliseconds < 0 {
|
|
return nil, ValidationError{Violations: []FieldViolation{{Field: "expiry_unix_milliseconds", Code: "minimum"}}}
|
|
}
|
|
transcript := make([]byte, 0, 112)
|
|
transcript = append(transcript, "versevdi-device-proof-v1"...)
|
|
transcript = append(transcript, serverID...)
|
|
transcript = append(transcript, principalID...)
|
|
transcript = append(transcript, deviceID...)
|
|
transcript = append(transcript, challenge...)
|
|
var expiry [8]byte
|
|
binary.BigEndian.PutUint64(expiry[:], uint64(expiryUnixMilliseconds))
|
|
return append(transcript, expiry[:]...), nil
|
|
}
|
|
|
|
var ErrNoCapabilityOverlap = errors.New("no capability overlap")
|
|
|
|
func IntersectCapabilityProfiles(profiles ...CapabilityProfile) (CapabilityProfile, error) {
|
|
if len(profiles) == 0 {
|
|
return CapabilityProfile{}, ErrNoCapabilityOverlap
|
|
}
|
|
selected := profiles[0]
|
|
if err := selected.Validate(); err != nil {
|
|
return CapabilityProfile{}, ErrNoCapabilityOverlap
|
|
}
|
|
commonVideo := append([]VideoProfile(nil), selected.VideoProfiles...)
|
|
commonAudio := append([]AudioProfile(nil), selected.AudioProfiles...)
|
|
for _, profile := range profiles[1:] {
|
|
if err := profile.Validate(); err != nil || profile.Transport != selected.Transport || profile.Framing != selected.Framing || profile.Media != selected.Media || profile.SourceRateControl != selected.SourceRateControl {
|
|
return CapabilityProfile{}, ErrNoCapabilityOverlap
|
|
}
|
|
nextVideo := commonVideo[:0]
|
|
for _, candidate := range commonVideo {
|
|
for _, offered := range profile.VideoProfiles {
|
|
if candidate == offered {
|
|
nextVideo = append(nextVideo, candidate)
|
|
break
|
|
}
|
|
}
|
|
}
|
|
commonVideo = nextVideo
|
|
nextAudio := commonAudio[:0]
|
|
for _, candidate := range commonAudio {
|
|
for _, offered := range profile.AudioProfiles {
|
|
if candidate == offered {
|
|
nextAudio = append(nextAudio, candidate)
|
|
break
|
|
}
|
|
}
|
|
}
|
|
commonAudio = nextAudio
|
|
if len(commonVideo) == 0 || len(commonAudio) == 0 {
|
|
return CapabilityProfile{}, ErrNoCapabilityOverlap
|
|
}
|
|
}
|
|
selected.VideoProfiles = commonVideo
|
|
selected.AudioProfiles = commonAudio
|
|
return selected, nil
|
|
}
|
|
|
|
func (v TunnelAdmissionRequest) DeviceAdmissionTranscript() []byte {
|
|
fields := []string{v.SessionID, v.GatewayID, v.Audience, v.Grant, fmt.Sprintf("%d", v.ReconnectSequence), v.ClientNonce, v.Capabilities.Transport, v.Capabilities.Framing, v.Capabilities.Media, v.Capabilities.SourceRateControl, fmt.Sprintf("%d", len(v.Capabilities.VideoProfiles)), fmt.Sprintf("%d", len(v.Capabilities.AudioProfiles))}
|
|
for _, profile := range v.Capabilities.VideoProfiles {
|
|
fields = append(fields, profile.Codec, fmt.Sprintf("%d", profile.BitDepth), profile.ChromaSubsampling, profile.ColorSpace, profile.TransferFunction)
|
|
}
|
|
for _, profile := range v.Capabilities.AudioProfiles {
|
|
fields = append(fields, profile.Codec, fmt.Sprintf("%d", profile.SampleRateHz), fmt.Sprintf("%d", profile.Channels), profile.ChannelLayout, fmt.Sprintf("%d", profile.PacketDurationMs))
|
|
}
|
|
var transcript strings.Builder
|
|
transcript.WriteString("versevdi/tunnel-admission/v1")
|
|
for _, field := range fields {
|
|
fmt.Fprintf(&transcript, "%d:%s", len(field), field)
|
|
}
|
|
return []byte(transcript.String())
|
|
}
|