diff --git a/.golangci.yml b/.golangci.yml index cbd0fc46f8..445e35f88e 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,7 +1,3 @@ -run: - skip-dirs: - - node_modules - linters: disable-all: true enable: @@ -113,3 +109,6 @@ issues: - linters: - paralleltest text: 'does not use range value in test Run' + exclude-dirs: + - node_modules + diff --git a/CHANGELOG.md b/CHANGELOG.md index 6655089a04..819f6757eb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,11 +14,21 @@ For details about compatibility between different releases, see the **Commitment - Add recvTime field to the decodeUplink input in payload formatters - Add the latest battery percentage of the end device in the `ApplicationUplink` message. - Add live data split view tutorial to the Console. +- Add end device attributes to ApplicationUp messages. + - Add the locations, version_ids, network_ids fields to the following ApplicationUp messages: + - ApplicationJoinAccept + - ApplicationDownlink + - ApplicationDownlinkFailed + - ApplicationInvalidatedDownlinks + - ApplicationServiceData + - Add Timeout and Cache fields in the EndDeviceMetadataStorageConfig of the AS. ### Changed ### Deprecated +- Deprecate the Location field (and its subfields) in the EndDeviceMetadataStorageConfig of AS. + ### Removed ### Fixed diff --git a/api/ttn/lorawan/v3/api.md b/api/ttn/lorawan/v3/api.md index 1ee06e0ed6..568fcc96e0 100644 --- a/api/ttn/lorawan/v3/api.md +++ b/api/ttn/lorawan/v3/api.md @@ -596,19 +596,31 @@ - [Enum `TxSchedulePriority`](#ttn.lorawan.v3.TxSchedulePriority) - [File `ttn/lorawan/v3/messages.proto`](#ttn/lorawan/v3/messages.proto) - [Message `ApplicationDownlink`](#ttn.lorawan.v3.ApplicationDownlink) + - [Message `ApplicationDownlink.AttributesEntry`](#ttn.lorawan.v3.ApplicationDownlink.AttributesEntry) - [Message `ApplicationDownlink.ClassBC`](#ttn.lorawan.v3.ApplicationDownlink.ClassBC) - [Message `ApplicationDownlink.ConfirmedRetry`](#ttn.lorawan.v3.ApplicationDownlink.ConfirmedRetry) + - [Message `ApplicationDownlink.LocationsEntry`](#ttn.lorawan.v3.ApplicationDownlink.LocationsEntry) - [Message `ApplicationDownlinkFailed`](#ttn.lorawan.v3.ApplicationDownlinkFailed) + - [Message `ApplicationDownlinkFailed.AttributesEntry`](#ttn.lorawan.v3.ApplicationDownlinkFailed.AttributesEntry) + - [Message `ApplicationDownlinkFailed.LocationsEntry`](#ttn.lorawan.v3.ApplicationDownlinkFailed.LocationsEntry) - [Message `ApplicationDownlinks`](#ttn.lorawan.v3.ApplicationDownlinks) - [Message `ApplicationInvalidatedDownlinks`](#ttn.lorawan.v3.ApplicationInvalidatedDownlinks) + - [Message `ApplicationInvalidatedDownlinks.AttributesEntry`](#ttn.lorawan.v3.ApplicationInvalidatedDownlinks.AttributesEntry) + - [Message `ApplicationInvalidatedDownlinks.LocationsEntry`](#ttn.lorawan.v3.ApplicationInvalidatedDownlinks.LocationsEntry) - [Message `ApplicationJoinAccept`](#ttn.lorawan.v3.ApplicationJoinAccept) + - [Message `ApplicationJoinAccept.AttributesEntry`](#ttn.lorawan.v3.ApplicationJoinAccept.AttributesEntry) + - [Message `ApplicationJoinAccept.LocationsEntry`](#ttn.lorawan.v3.ApplicationJoinAccept.LocationsEntry) - [Message `ApplicationLocation`](#ttn.lorawan.v3.ApplicationLocation) - [Message `ApplicationLocation.AttributesEntry`](#ttn.lorawan.v3.ApplicationLocation.AttributesEntry) - [Message `ApplicationServiceData`](#ttn.lorawan.v3.ApplicationServiceData) + - [Message `ApplicationServiceData.AttributesEntry`](#ttn.lorawan.v3.ApplicationServiceData.AttributesEntry) + - [Message `ApplicationServiceData.LocationsEntry`](#ttn.lorawan.v3.ApplicationServiceData.LocationsEntry) - [Message `ApplicationUp`](#ttn.lorawan.v3.ApplicationUp) - [Message `ApplicationUplink`](#ttn.lorawan.v3.ApplicationUplink) + - [Message `ApplicationUplink.AttributesEntry`](#ttn.lorawan.v3.ApplicationUplink.AttributesEntry) - [Message `ApplicationUplink.LocationsEntry`](#ttn.lorawan.v3.ApplicationUplink.LocationsEntry) - [Message `ApplicationUplinkNormalized`](#ttn.lorawan.v3.ApplicationUplinkNormalized) + - [Message `ApplicationUplinkNormalized.AttributesEntry`](#ttn.lorawan.v3.ApplicationUplinkNormalized.AttributesEntry) - [Message `ApplicationUplinkNormalized.LocationsEntry`](#ttn.lorawan.v3.ApplicationUplinkNormalized.LocationsEntry) - [Message `DownlinkMessage`](#ttn.lorawan.v3.DownlinkMessage) - [Message `DownlinkQueueOperationErrorDetails`](#ttn.lorawan.v3.DownlinkQueueOperationErrorDetails) @@ -8633,6 +8645,10 @@ Transmission settings for downlink. | `priority` | [`TxSchedulePriority`](#ttn.lorawan.v3.TxSchedulePriority) | | Priority for scheduling the downlink message. | | `correlation_ids` | [`string`](#string) | repeated | | | `confirmed_retry` | [`ApplicationDownlink.ConfirmedRetry`](#ttn.lorawan.v3.ApplicationDownlink.ConfirmedRetry) | | | +| `locations` | [`ApplicationDownlink.LocationsEntry`](#ttn.lorawan.v3.ApplicationDownlink.LocationsEntry) | repeated | End device location metadata, set by the Application Server while handling the message. | +| `version_ids` | [`EndDeviceVersionIdentifiers`](#ttn.lorawan.v3.EndDeviceVersionIdentifiers) | | End device version identifiers, set by the Application Server while handling the message. | +| `network_ids` | [`NetworkIdentifiers`](#ttn.lorawan.v3.NetworkIdentifiers) | | Network identifiers, set by the Network Server that handles the message. | +| `attributes` | [`ApplicationDownlink.AttributesEntry`](#ttn.lorawan.v3.ApplicationDownlink.AttributesEntry) | repeated | Attributes for devices, set by the Application Server while handling the message. | #### Field Rules @@ -8643,6 +8659,14 @@ Transmission settings for downlink. | `frm_payload` |

`bytes.max_len`: `255`

| | `priority` |

`enum.defined_only`: `true`

| | `correlation_ids` |

`repeated.items.string.max_len`: `100`

| +| `attributes` |

`map.max_pairs`: `10`

`map.keys.string.max_len`: `36`

`map.keys.string.pattern`: `^[a-z0-9](?:[-]?[a-z0-9]){2,}$`

`map.values.string.max_len`: `200`

| + +### Message `ApplicationDownlink.AttributesEntry` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `key` | [`string`](#string) | | | +| `value` | [`string`](#string) | | | ### Message `ApplicationDownlink.ClassBC` @@ -8664,12 +8688,23 @@ Transmission settings for downlink. | ----- | ----------- | | `max_attempts` |

`uint32.lte`: `100`

`uint32.gt`: `0`

| +### Message `ApplicationDownlink.LocationsEntry` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `key` | [`string`](#string) | | | +| `value` | [`Location`](#ttn.lorawan.v3.Location) | | | + ### Message `ApplicationDownlinkFailed` | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | `downlink` | [`ApplicationDownlink`](#ttn.lorawan.v3.ApplicationDownlink) | | | | `error` | [`ErrorDetails`](#ttn.lorawan.v3.ErrorDetails) | | | +| `locations` | [`ApplicationDownlinkFailed.LocationsEntry`](#ttn.lorawan.v3.ApplicationDownlinkFailed.LocationsEntry) | repeated | End device location metadata, set by the Application Server while handling the message. | +| `version_ids` | [`EndDeviceVersionIdentifiers`](#ttn.lorawan.v3.EndDeviceVersionIdentifiers) | | End device version identifiers, set by the Application Server while handling the message. | +| `network_ids` | [`NetworkIdentifiers`](#ttn.lorawan.v3.NetworkIdentifiers) | | Network identifiers, set by the Network Server that handles the message. | +| `attributes` | [`ApplicationDownlinkFailed.AttributesEntry`](#ttn.lorawan.v3.ApplicationDownlinkFailed.AttributesEntry) | repeated | Attributes for devices, set by the Application Server while handling the message. | #### Field Rules @@ -8677,6 +8712,21 @@ Transmission settings for downlink. | ----- | ----------- | | `downlink` |

`message.required`: `true`

| | `error` |

`message.required`: `true`

| +| `attributes` |

`map.max_pairs`: `10`

`map.keys.string.max_len`: `36`

`map.keys.string.pattern`: `^[a-z0-9](?:[-]?[a-z0-9]){2,}$`

`map.values.string.max_len`: `200`

| + +### Message `ApplicationDownlinkFailed.AttributesEntry` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `key` | [`string`](#string) | | | +| `value` | [`string`](#string) | | | + +### Message `ApplicationDownlinkFailed.LocationsEntry` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `key` | [`string`](#string) | | | +| `value` | [`Location`](#ttn.lorawan.v3.Location) | | | ### Message `ApplicationDownlinks` @@ -8691,12 +8741,31 @@ Transmission settings for downlink. | `downlinks` | [`ApplicationDownlink`](#ttn.lorawan.v3.ApplicationDownlink) | repeated | | | `last_f_cnt_down` | [`uint32`](#uint32) | | | | `session_key_id` | [`bytes`](#bytes) | | | +| `locations` | [`ApplicationInvalidatedDownlinks.LocationsEntry`](#ttn.lorawan.v3.ApplicationInvalidatedDownlinks.LocationsEntry) | repeated | End device location metadata, set by the Application Server while handling the message. | +| `version_ids` | [`EndDeviceVersionIdentifiers`](#ttn.lorawan.v3.EndDeviceVersionIdentifiers) | | End device version identifiers, set by the Application Server while handling the message. | +| `network_ids` | [`NetworkIdentifiers`](#ttn.lorawan.v3.NetworkIdentifiers) | | Network identifiers, set by the Network Server that handles the message. | +| `attributes` | [`ApplicationInvalidatedDownlinks.AttributesEntry`](#ttn.lorawan.v3.ApplicationInvalidatedDownlinks.AttributesEntry) | repeated | Attributes for devices, set by the Application Server while handling the message. | #### Field Rules | Field | Validations | | ----- | ----------- | | `session_key_id` |

`bytes.max_len`: `2048`

| +| `attributes` |

`map.max_pairs`: `10`

`map.keys.string.max_len`: `36`

`map.keys.string.pattern`: `^[a-z0-9](?:[-]?[a-z0-9]){2,}$`

`map.values.string.max_len`: `200`

| + +### Message `ApplicationInvalidatedDownlinks.AttributesEntry` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `key` | [`string`](#string) | | | +| `value` | [`string`](#string) | | | + +### Message `ApplicationInvalidatedDownlinks.LocationsEntry` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `key` | [`string`](#string) | | | +| `value` | [`Location`](#ttn.lorawan.v3.Location) | | | ### Message `ApplicationJoinAccept` @@ -8707,6 +8776,10 @@ Transmission settings for downlink. | `invalidated_downlinks` | [`ApplicationDownlink`](#ttn.lorawan.v3.ApplicationDownlink) | repeated | Downlink messages in the queue that got invalidated because of the session change. | | `pending_session` | [`bool`](#bool) | | Indicates whether the security context refers to the pending session, i.e. when this join-accept is an answer to a rejoin-request. | | `received_at` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | Server time when the Network Server received the message. | +| `locations` | [`ApplicationJoinAccept.LocationsEntry`](#ttn.lorawan.v3.ApplicationJoinAccept.LocationsEntry) | repeated | End device location metadata, set by the Application Server while handling the message. | +| `version_ids` | [`EndDeviceVersionIdentifiers`](#ttn.lorawan.v3.EndDeviceVersionIdentifiers) | | End device version identifiers, set by the Application Server while handling the message. | +| `network_ids` | [`NetworkIdentifiers`](#ttn.lorawan.v3.NetworkIdentifiers) | | Network identifiers, set by the Network Server that handles the message. | +| `attributes` | [`ApplicationJoinAccept.AttributesEntry`](#ttn.lorawan.v3.ApplicationJoinAccept.AttributesEntry) | repeated | Attributes for devices, set by the Application Server while handling the message. | #### Field Rules @@ -8714,6 +8787,21 @@ Transmission settings for downlink. | ----- | ----------- | | `session_key_id` |

`bytes.max_len`: `2048`

| | `received_at` |

`timestamp.required`: `true`

| +| `attributes` |

`map.max_pairs`: `10`

`map.keys.string.max_len`: `36`

`map.keys.string.pattern`: `^[a-z0-9](?:[-]?[a-z0-9]){2,}$`

`map.values.string.max_len`: `200`

| + +### Message `ApplicationJoinAccept.AttributesEntry` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `key` | [`string`](#string) | | | +| `value` | [`string`](#string) | | | + +### Message `ApplicationJoinAccept.LocationsEntry` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `key` | [`string`](#string) | | | +| `value` | [`Location`](#ttn.lorawan.v3.Location) | | | ### Message `ApplicationLocation` @@ -8743,6 +8831,30 @@ Transmission settings for downlink. | ----- | ---- | ----- | ----------- | | `service` | [`string`](#string) | | | | `data` | [`google.protobuf.Struct`](#google.protobuf.Struct) | | | +| `locations` | [`ApplicationServiceData.LocationsEntry`](#ttn.lorawan.v3.ApplicationServiceData.LocationsEntry) | repeated | End device location metadata, set by the Application Server while handling the message. | +| `version_ids` | [`EndDeviceVersionIdentifiers`](#ttn.lorawan.v3.EndDeviceVersionIdentifiers) | | End device version identifiers, set by the Application Server while handling the message. | +| `network_ids` | [`NetworkIdentifiers`](#ttn.lorawan.v3.NetworkIdentifiers) | | Network identifiers, set by the Network Server that handles the message. | +| `attributes` | [`ApplicationServiceData.AttributesEntry`](#ttn.lorawan.v3.ApplicationServiceData.AttributesEntry) | repeated | Attributes for devices, set by the Application Server while handling the message. | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `attributes` |

`map.max_pairs`: `10`

`map.keys.string.max_len`: `36`

`map.keys.string.pattern`: `^[a-z0-9](?:[-]?[a-z0-9]){2,}$`

`map.values.string.max_len`: `200`

| + +### Message `ApplicationServiceData.AttributesEntry` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `key` | [`string`](#string) | | | +| `value` | [`string`](#string) | | | + +### Message `ApplicationServiceData.LocationsEntry` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `key` | [`string`](#string) | | | +| `value` | [`Location`](#ttn.lorawan.v3.Location) | | | ### Message `ApplicationUp` @@ -8797,6 +8909,7 @@ Application uplink message. | `version_ids` | [`EndDeviceVersionIdentifiers`](#ttn.lorawan.v3.EndDeviceVersionIdentifiers) | | End device version identifiers, set by the Application Server while handling the message. | | `network_ids` | [`NetworkIdentifiers`](#ttn.lorawan.v3.NetworkIdentifiers) | | Network identifiers, set by the Network Server that handles the message. | | `last_battery_percentage` | [`LastBatteryPercentage`](#ttn.lorawan.v3.LastBatteryPercentage) | | Last battery percentage of the end device. Received via the DevStatus MAC command at last_dev_status_received_at or earlier. Set by the Network Server while handling the message. | +| `attributes` | [`ApplicationUplink.AttributesEntry`](#ttn.lorawan.v3.ApplicationUplink.AttributesEntry) | repeated | Attributes for devices, set by the Application Server while handling the message. | #### Field Rules @@ -8805,6 +8918,14 @@ Application uplink message. | `session_key_id` |

`bytes.max_len`: `2048`

| | `f_port` |

`uint32.lte`: `255`

`uint32.not_in`: `[224]`

| | `settings` |

`message.required`: `true`

| +| `attributes` |

`map.max_pairs`: `10`

`map.keys.string.max_len`: `36`

`map.keys.string.pattern`: `^[a-z0-9](?:[-]?[a-z0-9]){2,}$`

`map.values.string.max_len`: `200`

| + +### Message `ApplicationUplink.AttributesEntry` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `key` | [`string`](#string) | | | +| `value` | [`string`](#string) | | | ### Message `ApplicationUplink.LocationsEntry` @@ -8831,6 +8952,7 @@ Application uplink message. | `locations` | [`ApplicationUplinkNormalized.LocationsEntry`](#ttn.lorawan.v3.ApplicationUplinkNormalized.LocationsEntry) | repeated | End device location metadata, set by the Application Server while handling the message. | | `version_ids` | [`EndDeviceVersionIdentifiers`](#ttn.lorawan.v3.EndDeviceVersionIdentifiers) | | End device version identifiers, set by the Application Server while handling the message. | | `network_ids` | [`NetworkIdentifiers`](#ttn.lorawan.v3.NetworkIdentifiers) | | Network identifiers, set by the Network Server that handles the message. | +| `attributes` | [`ApplicationUplinkNormalized.AttributesEntry`](#ttn.lorawan.v3.ApplicationUplinkNormalized.AttributesEntry) | repeated | Attributes for devices, set by the Application Server while handling the message. | #### Field Rules @@ -8841,6 +8963,14 @@ Application uplink message. | `normalized_payload` |

`message.required`: `true`

| | `settings` |

`message.required`: `true`

| | `received_at` |

`timestamp.required`: `true`

| +| `attributes` |

`map.max_pairs`: `10`

`map.keys.string.max_len`: `36`

`map.keys.string.pattern`: `^[a-z0-9](?:[-]?[a-z0-9]){2,}$`

`map.values.string.max_len`: `200`

| + +### Message `ApplicationUplinkNormalized.AttributesEntry` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `key` | [`string`](#string) | | | +| `value` | [`string`](#string) | | | ### Message `ApplicationUplinkNormalized.LocationsEntry` diff --git a/api/ttn/lorawan/v3/api.swagger.json b/api/ttn/lorawan/v3/api.swagger.json index 57c7d5b5ce..626f161912 100644 --- a/api/ttn/lorawan/v3/api.swagger.json +++ b/api/ttn/lorawan/v3/api.swagger.json @@ -20326,6 +20326,28 @@ }, "confirmed_retry": { "$ref": "#/definitions/ApplicationDownlinkConfirmedRetry" + }, + "locations": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/lorawanv3Location" + }, + "description": "End device location metadata, set by the Application Server while handling the message." + }, + "version_ids": { + "$ref": "#/definitions/v3EndDeviceVersionIdentifiers", + "description": "End device version identifiers, set by the Application Server while handling the message." + }, + "network_ids": { + "$ref": "#/definitions/v3NetworkIdentifiers", + "description": "Network identifiers, set by the Network Server that handles the message." + }, + "attributes": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "Attributes for devices, set by the Application Server while handling the message." } } }, @@ -20337,6 +20359,28 @@ }, "error": { "$ref": "#/definitions/v3ErrorDetails" + }, + "locations": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/lorawanv3Location" + }, + "description": "End device location metadata, set by the Application Server while handling the message." + }, + "version_ids": { + "$ref": "#/definitions/v3EndDeviceVersionIdentifiers", + "description": "End device version identifiers, set by the Application Server while handling the message." + }, + "network_ids": { + "$ref": "#/definitions/v3NetworkIdentifiers", + "description": "Network identifiers, set by the Network Server that handles the message." + }, + "attributes": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "Attributes for devices, set by the Application Server while handling the message." } } }, @@ -20377,6 +20421,28 @@ "session_key_id": { "type": "string", "format": "byte" + }, + "locations": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/lorawanv3Location" + }, + "description": "End device location metadata, set by the Application Server while handling the message." + }, + "version_ids": { + "$ref": "#/definitions/v3EndDeviceVersionIdentifiers", + "description": "End device version identifiers, set by the Application Server while handling the message." + }, + "network_ids": { + "$ref": "#/definitions/v3NetworkIdentifiers", + "description": "Network identifiers, set by the Network Server that handles the message." + }, + "attributes": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "Attributes for devices, set by the Application Server while handling the message." } } }, @@ -20408,6 +20474,28 @@ "type": "string", "format": "date-time", "description": "Server time when the Network Server received the message." + }, + "locations": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/lorawanv3Location" + }, + "description": "End device location metadata, set by the Application Server while handling the message." + }, + "version_ids": { + "$ref": "#/definitions/v3EndDeviceVersionIdentifiers", + "description": "End device version identifiers, set by the Application Server while handling the message." + }, + "network_ids": { + "$ref": "#/definitions/v3NetworkIdentifiers", + "description": "Network identifiers, set by the Network Server that handles the message." + }, + "attributes": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "Attributes for devices, set by the Application Server while handling the message." } } }, @@ -20907,6 +20995,28 @@ }, "data": { "type": "object" + }, + "locations": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/lorawanv3Location" + }, + "description": "End device location metadata, set by the Application Server while handling the message." + }, + "version_ids": { + "$ref": "#/definitions/v3EndDeviceVersionIdentifiers", + "description": "End device version identifiers, set by the Application Server while handling the message." + }, + "network_ids": { + "$ref": "#/definitions/v3NetworkIdentifiers", + "description": "Network identifiers, set by the Network Server that handles the message." + }, + "attributes": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "Attributes for devices, set by the Application Server while handling the message." } } }, @@ -21072,6 +21182,13 @@ "last_battery_percentage": { "$ref": "#/definitions/v3LastBatteryPercentage", "description": "Last battery percentage of the end device.\nReceived via the DevStatus MAC command at last_dev_status_received_at or earlier.\nSet by the Network Server while handling the message." + }, + "attributes": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "Attributes for devices, set by the Application Server while handling the message." } } }, @@ -21148,6 +21265,13 @@ "network_ids": { "$ref": "#/definitions/v3NetworkIdentifiers", "description": "Network identifiers, set by the Network Server that handles the message." + }, + "attributes": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "Attributes for devices, set by the Application Server while handling the message." } } }, diff --git a/api/ttn/lorawan/v3/messages.proto b/api/ttn/lorawan/v3/messages.proto index 84b69891d3..0d09cff9ee 100644 --- a/api/ttn/lorawan/v3/messages.proto +++ b/api/ttn/lorawan/v3/messages.proto @@ -277,7 +277,21 @@ message ApplicationUplink { // Set by the Network Server while handling the message. LastBatteryPercentage last_battery_percentage = 20; - // next: 21 + // Attributes for devices, set by the Application Server while handling the message. + map attributes = 21 [(validate.rules).map = { + max_pairs: 10, + keys: { + string: { + pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$", + max_len: 36 + } + }, + values: { + string: {max_len: 200} + } + }]; + + // next: 22 } message ApplicationUplinkNormalized { @@ -330,7 +344,21 @@ message ApplicationUplinkNormalized { // Network identifiers, set by the Network Server that handles the message. NetworkIdentifiers network_ids = 14; - // next: 15 + // Attributes for devices, set by the Application Server while handling the message. + map attributes = 15 [(validate.rules).map = { + max_pairs: 10, + keys: { + string: { + pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$", + max_len: 36 + } + }, + values: { + string: {max_len: 200} + } + }]; + + // next: 16 } message ApplicationLocation { @@ -370,6 +398,31 @@ message ApplicationJoinAccept { bool pending_session = 4; // Server time when the Network Server received the message. google.protobuf.Timestamp received_at = 8 [(validate.rules).timestamp.required = true]; + + // End device location metadata, set by the Application Server while handling the message. + map locations = 9; + + // End device version identifiers, set by the Application Server while handling the message. + EndDeviceVersionIdentifiers version_ids = 10; + + // Network identifiers, set by the Network Server that handles the message. + NetworkIdentifiers network_ids = 11; + + // Attributes for devices, set by the Application Server while handling the message. + map attributes = 12 [(validate.rules).map = { + max_pairs: 10, + keys: { + string: { + pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$", + max_len: 36 + } + }, + values: { + string: {max_len: 200} + } + }]; + + // next: 13 } message ApplicationDownlink { @@ -456,7 +509,30 @@ message ApplicationDownlink { ConfirmedRetry confirmed_retry = 11; - // next: 12 + // End device location metadata, set by the Application Server while handling the message. + map locations = 12; + + // End device version identifiers, set by the Application Server while handling the message. + EndDeviceVersionIdentifiers version_ids = 13; + + // Network identifiers, set by the Network Server that handles the message. + NetworkIdentifiers network_ids = 14; + + // Attributes for devices, set by the Application Server while handling the message. + map attributes = 15 [(validate.rules).map = { + max_pairs: 10, + keys: { + string: { + pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$", + max_len: 36 + } + }, + values: { + string: {max_len: 200} + } + }]; + + // next: 16 } message ApplicationDownlinks { @@ -470,6 +546,31 @@ message ApplicationDownlinkFailed { }; ApplicationDownlink downlink = 1 [(validate.rules).message.required = true]; ErrorDetails error = 2 [(validate.rules).message.required = true]; + + // End device location metadata, set by the Application Server while handling the message. + map locations = 3; + + // End device version identifiers, set by the Application Server while handling the message. + EndDeviceVersionIdentifiers version_ids = 4; + + // Network identifiers, set by the Network Server that handles the message. + NetworkIdentifiers network_ids = 5; + + // Attributes for devices, set by the Application Server while handling the message. + map attributes = 6 [(validate.rules).map = { + max_pairs: 10, + keys: { + string: { + pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$", + max_len: 36 + } + }, + values: { + string: {max_len: 200} + } + }]; + + // next: 7 } message ApplicationInvalidatedDownlinks { @@ -480,6 +581,31 @@ message ApplicationInvalidatedDownlinks { repeated ApplicationDownlink downlinks = 1; uint32 last_f_cnt_down = 2; bytes session_key_id = 3 [(validate.rules).bytes.max_len = 2048]; + + // End device location metadata, set by the Application Server while handling the message. + map locations = 4; + + // End device version identifiers, set by the Application Server while handling the message. + EndDeviceVersionIdentifiers version_ids = 5; + + // Network identifiers, set by the Network Server that handles the message. + NetworkIdentifiers network_ids = 6; + + // Attributes for devices, set by the Application Server while handling the message. + map attributes = 7 [(validate.rules).map = { + max_pairs: 10, + keys: { + string: { + pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$", + max_len: 36 + } + }, + values: { + string: {max_len: 200} + } + }]; + + // next: 8 } message DownlinkQueueOperationErrorDetails { @@ -527,6 +653,31 @@ message ApplicationServiceData { }; string service = 1; google.protobuf.Struct data = 2; + + // End device location metadata, set by the Application Server while handling the message. + map locations = 3; + + // End device version identifiers, set by the Application Server while handling the message. + EndDeviceVersionIdentifiers version_ids = 4; + + // Network identifiers, set by the Network Server that handles the message. + NetworkIdentifiers network_ids = 5; + + // Attributes for devices, set by the Application Server while handling the message. + map attributes = 6 [(validate.rules).map = { + max_pairs: 10, + keys: { + string: { + pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$", + max_len: 36 + } + }, + values: { + string: {max_len: 200} + } + }]; + + // next: 7 } // Application uplink message. diff --git a/cmd/internal/shared/applicationserver/config.go b/cmd/internal/shared/applicationserver/config.go index 0a680a3819..b420608610 100644 --- a/cmd/internal/shared/applicationserver/config.go +++ b/cmd/internal/shared/applicationserver/config.go @@ -49,15 +49,7 @@ var DefaultApplicationServerConfig = applicationserver.Config{ Downlinks: web.DownlinksConfig{PublicAddress: shared.DefaultPublicURL + "/api/v3"}, }, EndDeviceMetadataStorage: applicationserver.EndDeviceMetadataStorageConfig{ - Location: applicationserver.EndDeviceLocationStorageConfig{ - Timeout: 5 * time.Second, - Cache: applicationserver.EndDeviceLocationStorageCacheConfig{ - Enable: true, - MinRefreshInterval: 15 * time.Minute, - MaxRefreshInterval: 4 * time.Hour, - TTL: 14 * 24 * time.Hour, - }, - }, + Timeout: 5 * time.Second, }, Distribution: applicationserver.DistributionConfig{ Timeout: time.Minute, diff --git a/cmd/ttn-lw-stack/commands/start.go b/cmd/ttn-lw-stack/commands/start.go index ec3eaf7b09..8a8820fd31 100644 --- a/cmd/ttn-lw-stack/commands/start.go +++ b/cmd/ttn-lw-stack/commands/start.go @@ -27,7 +27,6 @@ import ( asioapredis "go.thethings.network/lorawan-stack/v3/pkg/applicationserver/io/packages/redis" asiopsredis "go.thethings.network/lorawan-stack/v3/pkg/applicationserver/io/pubsub/redis" asiowebredis "go.thethings.network/lorawan-stack/v3/pkg/applicationserver/io/web/redis" - asmetaredis "go.thethings.network/lorawan-stack/v3/pkg/applicationserver/metadata/redis" asredis "go.thethings.network/lorawan-stack/v3/pkg/applicationserver/redis" "go.thethings.network/lorawan-stack/v3/pkg/component" "go.thethings.network/lorawan-stack/v3/pkg/console" @@ -445,21 +444,11 @@ var startCommand = &cobra.Command{ } config.AS.Webhooks.Registry = webhookRegistry } - if cache := &config.AS.EndDeviceMetadataStorage.Location.Cache; cache.Enable { - switch config.Cache.Service { - case "redis": - cache.Cache = &asmetaredis.EndDeviceLocationCache{ - Redis: redis.New(config.Cache.Redis.WithNamespace("as", "metadata", "locations")), - } - default: - cache.Enable = false - } - } - locationRegistry, err := config.AS.EndDeviceMetadataStorage.Location.NewRegistry(ctx, c) + endDeviceRegistry, err := config.AS.EndDeviceMetadataStorage.NewRegistry(ctx, c) if err != nil { return shared.ErrInitializeApplicationServer.WithCause(err) } - config.AS.EndDeviceMetadataStorage.Location.Registry = locationRegistry + config.AS.EndDeviceMetadataStorage.Registry = endDeviceRegistry as, err := applicationserver.New(c, &config.AS) if err != nil { return shared.ErrInitializeApplicationServer.WithCause(err) diff --git a/config/messages.json b/config/messages.json index a474d02dbf..5109030ed5 100644 --- a/config/messages.json +++ b/config/messages.json @@ -2861,13 +2861,13 @@ "file": "io.go" } }, - "error:pkg/applicationserver/metadata/redis:cache_miss": { + "error:pkg/applicationserver/metadata:field_mask_path_not_supported": { "translations": { - "en": "cache miss" + "en": "field mask path `{path}` is not supported" }, "description": { - "package": "pkg/applicationserver/metadata/redis", - "file": "location_cache.go" + "package": "pkg/applicationserver/metadata", + "file": "metadata.go" } }, "error:pkg/applicationserver/redis:application_uid": { @@ -2978,15 +2978,6 @@ "file": "config.go" } }, - "error:pkg/applicationserver:invalid_ttl": { - "translations": { - "en": "invalid TTL `{ttl}`" - }, - "description": { - "package": "pkg/applicationserver", - "file": "config.go" - } - }, "error:pkg/applicationserver:join_server_unavailable": { "translations": { "en": "Join Server unavailable for JoinEUI `{join_eui}`" diff --git a/pkg/applicationserver/applicationserver.go b/pkg/applicationserver/applicationserver.go index 1559c1736f..48aeeb306c 100644 --- a/pkg/applicationserver/applicationserver.go +++ b/pkg/applicationserver/applicationserver.go @@ -81,7 +81,7 @@ type ApplicationServer struct { linkRegistry LinkRegistry deviceRegistry DeviceRegistry - locationRegistry metadata.EndDeviceLocationRegistry + endDeviceRegistry metadata.EndDeviceRegistry formatters messageprocessors.MapPayloadProcessor webhooks ioweb.Webhooks webhookTemplates ioweb.TemplateStore @@ -156,14 +156,14 @@ func New(c *component.Component, conf *Config) (as *ApplicationServer, err error } as = &ApplicationServer{ - Component: c, - ctx: ctx, - config: conf, - linkRegistry: conf.Links, - deviceRegistry: wrapEndDeviceRegistryWithReplacedFields(conf.Devices, replacedEndDeviceFields...), - appPkgRegistry: conf.Packages.Registry, - locationRegistry: conf.EndDeviceMetadataStorage.Location.Registry, - formatters: make(messageprocessors.MapPayloadProcessor), + Component: c, + ctx: ctx, + config: conf, + linkRegistry: conf.Links, + deviceRegistry: wrapEndDeviceRegistryWithReplacedFields(conf.Devices, replacedEndDeviceFields...), + appPkgRegistry: conf.Packages.Registry, + endDeviceRegistry: conf.EndDeviceMetadataStorage.Registry, + formatters: make(messageprocessors.MapPayloadProcessor), clusterDistributor: distribution.NewPubSubDistributor( ctx, c, @@ -1049,6 +1049,14 @@ func (as *ApplicationServer) handleJoinAccept( return err } + if entity, err := as.endDeviceRegistry.Get(ctx, ids, []string{"attributes"}); err != nil { + log.FromContext(ctx).WithError(err).Warn( + "Failed to retrieve end device attributes on join-accept", + ) + } else { + joinAccept.Attributes = entity.Attributes + } + // Publish last seen event. if err := as.deviceLastSeenPool.Publish(ctx, lastSeenAtInfo{ ids: ids, @@ -1159,6 +1167,7 @@ func (as *ApplicationServer) publishNormalizedUplink(ctx context.Context, info u Locations: info.uplink.Locations, VersionIds: info.uplink.VersionIds, NetworkIds: info.uplink.NetworkIds, + Attributes: info.uplink.Attributes, }, }, Simulated: info.simulated, @@ -1208,6 +1217,15 @@ func (as *ApplicationServer) handleUplink(ctx context.Context, info uplinkInfo) return err } + if entity, err := as.endDeviceRegistry.Get(ctx, info.ids, []string{"attributes", "locations"}); err != nil { + log.FromContext(ctx).WithError(err).Warn( + "Failed to retrieve the end device attributes and locations on uplink", + ) + } else { + info.uplink.Attributes = entity.Attributes + info.uplink.Locations = entity.Locations + } + if !as.skipPayloadCrypto(ctx, info.link, dev, dev.Session) { if err := as.decryptAndDecodeUplink(ctx, dev, info.uplink, info.link.DefaultFormatters); err != nil { return err @@ -1227,11 +1245,6 @@ func (as *ApplicationServer) handleUplink(ctx context.Context, info uplinkInfo) } // Set location in message and publish location solved if the payload contains location information. - if locations, err := as.locationRegistry.Get(ctx, info.ids); err != nil { - log.FromContext(ctx).WithError(err).Warn("Failed to retrieve end device locations") - } else { - info.uplink.Locations = locations - } loc := as.locationFromPayload(info.uplink) if loc != nil { if info.uplink.Locations == nil { @@ -1285,10 +1298,14 @@ func (as *ApplicationServer) handleSimulatedUplink(ctx context.Context, info upl return err } - if locations, err := as.locationRegistry.Get(ctx, info.ids); err != nil { - log.FromContext(ctx).WithError(err).Warn("Failed to retrieve end device locations") + if entity, err := as.endDeviceRegistry.Get(ctx, info.ids, []string{"attributes", "locations"}); err != nil { + log.FromContext(ctx).WithError(err).Warn( + "Failed to retrieve the end device from entity registry on simulated uplink", + ) } else { - info.uplink.Locations = locations + info.uplink.Attributes = entity.Attributes + info.uplink.Locations = entity.Locations + } if err := as.decodeUplink(ctx, dev, info.uplink, info.link.DefaultFormatters); err != nil { @@ -1370,7 +1387,19 @@ func (as *ApplicationServer) handleDownlinkQueueInvalidated( return dev, mask, nil }, ) - return pass, err + if err != nil { + return pass, err + } + + if entity, err := as.endDeviceRegistry.Get(ctx, ids, []string{"attributes"}); err != nil { + log.FromContext(ctx).WithError(err).Warn( + "Failed to retrieve end device attributes on downlink queue invalidated", + ) + } else { + invalid.Attributes = entity.Attributes + } + + return pass, nil } func (as *ApplicationServer) handleDownlinkNack( @@ -1455,17 +1484,47 @@ func (as *ApplicationServer) handleDownlinkNack( return dev, mask, nil }, ) - return err + if err != nil { + return err + } + + if _, err = as.endDeviceRegistry.Set(ctx, ids, []string{"attributes"}, + func(entity *ttnpb.EndDevice) (*ttnpb.EndDevice, []string, error) { + if entity == nil { + return nil, nil, errDeviceNotFound.WithAttributes("device_uid", unique.ID(ctx, ids)) + } + msg.Attributes = entity.Attributes + return entity, []string{""}, nil + }, + ); err != nil { + log.FromContext(ctx).WithError(err).Warn( + "Failed to retrieve end device attributes on downlink nack", + ) + } + + return nil } -// handleLocationSolved saves the provided *ttnpb.ApplicationLocation in the Entity Registry as part of the device locations. -// Locations provided by other services will be maintained. +// handleLocationSolved saves the provided *ttnpb.ApplicationLocation in the Entity Registry as part of the device +// locations. Locations provided by other services will be maintained. func (as *ApplicationServer) handleLocationSolved(ctx context.Context, ids *ttnpb.EndDeviceIdentifiers, msg *ttnpb.ApplicationLocation, link *ttnpb.ApplicationLink) error { defer trace.StartRegion(ctx, "handle location solved").End() - if _, err := as.locationRegistry.Merge(ctx, ids, map[string]*ttnpb.Location{ - msg.Service: msg.Location, - }); err != nil { + if _, err := as.endDeviceRegistry.Set(ctx, ids, []string{"locations"}, + func(stored *ttnpb.EndDevice) (*ttnpb.EndDevice, []string, error) { + if stored == nil { + return nil, nil, errDeviceNotFound.WithAttributes("device_uid", unique.ID(ctx, ids)) + } + + if len(stored.Locations) == 0 { + stored.Locations = make(map[string]*ttnpb.Location) + } + + stored.Locations[msg.Service] = msg.Location + + return stored, []string{"locations"}, nil + }, + ); err != nil { log.FromContext(ctx).WithError(err).Warn("Failed to merge end device locations") } return nil @@ -1486,6 +1545,15 @@ func (as *ApplicationServer) decryptDownlinkMessage(ctx context.Context, ids *tt if err != nil { return err } + + if entity, err := as.endDeviceRegistry.Get(ctx, ids, []string{"attributes"}); err != nil { + log.FromContext(ctx).WithError(err).Warn( + "Failed to retrieve end device attributes on downlink message", + ) + } else { + msg.Attributes = entity.Attributes + } + var session *ttnpb.Session switch { case dev.Session != nil && bytes.Equal(dev.Session.Keys.SessionKeyId, msg.SessionKeyId): diff --git a/pkg/applicationserver/applicationserver_test.go b/pkg/applicationserver/applicationserver_test.go index 0c37b39110..ce5999de30 100644 --- a/pkg/applicationserver/applicationserver_test.go +++ b/pkg/applicationserver/applicationserver_test.go @@ -303,9 +303,7 @@ func TestApplicationServer(t *testing.T) { }, }, EndDeviceMetadataStorage: applicationserver.EndDeviceMetadataStorageConfig{ - Location: applicationserver.EndDeviceLocationStorageConfig{ - Registry: metadata.NewNoopEndDeviceLocationRegistry(), - }, + Registry: metadata.NewNoopEndDeviceRegistry(), }, Downlinks: applicationserver.DownlinksConfig{ ConfirmationConfig: applicationserver.ConfirmationConfig{ @@ -2414,9 +2412,7 @@ func TestSkipPayloadCrypto(t *testing.T) { }, }, EndDeviceMetadataStorage: applicationserver.EndDeviceMetadataStorageConfig{ - Location: applicationserver.EndDeviceLocationStorageConfig{ - Registry: metadata.NewNoopEndDeviceLocationRegistry(), - }, + Registry: metadata.NewNoopEndDeviceRegistry(), }, Downlinks: applicationserver.DownlinksConfig{ ConfirmationConfig: applicationserver.ConfirmationConfig{ @@ -2916,9 +2912,7 @@ func TestLocationFromPayload(t *testing.T) { }, }, EndDeviceMetadataStorage: applicationserver.EndDeviceMetadataStorageConfig{ - Location: applicationserver.EndDeviceLocationStorageConfig{ - Registry: metadata.NewClusterEndDeviceLocationRegistry(c, (1<<4)*Timeout), - }, + Registry: metadata.NewClusterEndDeviceRegistry(c, (1<<4)*Timeout), }, Downlinks: applicationserver.DownlinksConfig{ ConfirmationConfig: applicationserver.ConfirmationConfig{ @@ -3101,9 +3095,7 @@ func TestUplinkNormalized(t *testing.T) { }, }, EndDeviceMetadataStorage: applicationserver.EndDeviceMetadataStorageConfig{ - Location: applicationserver.EndDeviceLocationStorageConfig{ - Registry: metadata.NewClusterEndDeviceLocationRegistry(c, (1<<4)*Timeout), - }, + Registry: metadata.NewClusterEndDeviceRegistry(c, (1<<4)*Timeout), }, Downlinks: applicationserver.DownlinksConfig{ ConfirmationConfig: applicationserver.ConfirmationConfig{ diff --git a/pkg/applicationserver/config.go b/pkg/applicationserver/config.go index 54b46ca378..d9b69272fa 100644 --- a/pkg/applicationserver/config.go +++ b/pkg/applicationserver/config.go @@ -49,9 +49,9 @@ type InteropConfig struct { // EndDeviceFetcherConfig represents configuration for the end device fetcher in Application Server. type EndDeviceFetcherConfig struct { - Timeout time.Duration `name:"timeout" description:"Timeout of the end device retrival operation"` - Cache EndDeviceFetcherCacheConfig `name:"cache" description:"Cache configuration options for the end device fetcher"` - CircuitBreaker EndDeviceFetcherCircuitBreakerConfig `name:"circuit-breaker" description:"Circuit breaker options for the end device fetcher"` + Timeout time.Duration `name:"timeout" description:"Timeout of the end device retrival operation"` // nolint:lll + Cache EndDeviceFetcherCacheConfig `name:"cache" description:"Cache configuration options for the end device fetcher"` // nolint:lll + CircuitBreaker EndDeviceFetcherCircuitBreakerConfig `name:"circuit-breaker" description:"Circuit breaker options for the end device fetcher"` // nolint:lll } // EndDeviceFetcherCacheConfig represents configuration for device information caching in Application Server. @@ -65,33 +65,37 @@ type EndDeviceFetcherCacheConfig struct { type EndDeviceFetcherCircuitBreakerConfig struct { Enable bool `name:"enable" description:"Enable circuit breaker behavior on burst errors"` Timeout time.Duration `name:"timeout" description:"Timeout after which the circuit breaker closes"` - Threshold int `name:"threshold" description:"Number of failed fetching attempts after which the circuit breaker opens"` + Threshold int `name:"threshold" description:"Number of failed fetching attempts after which the circuit breaker opens"` // nolint:lll } // EndDeviceMetadataStorageConfig represents the configuration of end device metadata operations. type EndDeviceMetadataStorageConfig struct { - Location EndDeviceLocationStorageConfig `name:"location"` + // DEPRECATED: use the EndDeviceRegistry for location storage. + Location EndDeviceLocationStorageConfig `name:"location" description:"DEPRECATED setting."` + + Registry metadata.EndDeviceRegistry `name:"-"` + Timeout time.Duration `name:"timeout" description:"Timeout of the entity registry retrieval operation"` // nolint:lll } // EndDeviceLocationStorageConfig represents the configuration of end device locations storage. +// DEPRECATED: use the metadata.EndDeviceRegistry for location storage. type EndDeviceLocationStorageConfig struct { - Registry metadata.EndDeviceLocationRegistry `name:"-"` - Timeout time.Duration `name:"timeout" description:"Timeout of the end device retrival operation"` - Cache EndDeviceLocationStorageCacheConfig `name:"cache"` + Timeout time.Duration `name:"timeout" description:"Timeout of the end device retrieval operation. DEPRECATED: use the end device metadata storage directly instead."` // nolint:lll + Cache EndDeviceLocationStorageCacheConfig `name:"cache" description:"DEPRECATED: use the end device metadata storage directly instead."` // nolint:lll } // EndDeviceLocationStorageCacheConfig represents the configuration of end device location registry caching. +// DEPRECATED: use the metadata.EndDeviceRegistryCache and the locations field of end devices for caching. type EndDeviceLocationStorageCacheConfig struct { - Cache metadata.EndDeviceLocationCache `name:"-"` - Enable bool `name:"enable" description:"Enable caching of end device locations"` - MinRefreshInterval time.Duration `name:"min-refresh-interval" description:"Minimum time interval between two asynchronous refreshes"` - MaxRefreshInterval time.Duration `name:"max-refresh-interval" description:"Maximum time interval between two asynchronous refreshes"` - TTL time.Duration `name:"eviction-ttl" description:"Time to live of cached locations"` + Enable bool `name:"enable" description:"Enable caching of end device locations. DEPRECATED: use the end device metadata storage directly instead."` // nolint:lll + MinRefreshInterval time.Duration `name:"min-refresh-interval" description:"Minimum time interval between two asynchronous refreshes. DEPRECATED: use the end device metadata storage directly instead."` // nolint:lll + MaxRefreshInterval time.Duration `name:"max-refresh-interval" description:"Maximum time interval between two asynchronous refreshes. DEPRECATED: use the end device metadata storage directly instead."` // nolint:lll + TTL time.Duration `name:"eviction-ttl" description:"Time to live of cached locations. DEPRECATED: use the end device metadata storage directly instead."` // nolint:lll } // FormattersConfig represents the configuration for payload formatters. type FormattersConfig struct { - MaxParameterLength int `name:"max-parameter-length" description:"Maximum allowed size for length of formatter parameters (payload formatter scripts)"` + MaxParameterLength int `name:"max-parameter-length" description:"Maximum allowed size for length of formatter parameters (payload formatter scripts)"` // nolint:lll } // ConfirmationConfig represents the configuration for confirmed downlink. @@ -112,21 +116,21 @@ type PaginationConfig struct { // Config represents the ApplicationServer configuration. type Config struct { - LinkMode string `name:"link-mode" description:"Deprecated - mode to link applications to their Network Server (all, explicit)"` + LinkMode string `name:"link-mode" description:"Deprecated - mode to link applications to their Network Server (all, explicit)"` // nolint:lll Devices DeviceRegistry `name:"-"` Links LinkRegistry `name:"-"` UplinkStorage UplinkStorageConfig `name:"uplink-storage" description:"Application uplinks storage configuration"` Formatters FormattersConfig `name:"formatters" description:"Payload formatters configuration"` Distribution DistributionConfig `name:"distribution" description:"Distribution configuration"` - EndDeviceFetcher EndDeviceFetcherConfig `name:"fetcher" description:"Deprecated - End Device fetcher configuration"` - EndDeviceMetadataStorage EndDeviceMetadataStorageConfig `name:"end-device-metadata-storage" description:"End device metadata storage configuration"` + EndDeviceFetcher EndDeviceFetcherConfig `name:"fetcher" description:"Deprecated - End Device fetcher configuration"` // nolint:lll + EndDeviceMetadataStorage EndDeviceMetadataStorageConfig `name:"end-device-metadata-storage" description:"End device metadata storage configuration"` // nolint:lll MQTT config.MQTT `name:"mqtt" description:"MQTT configuration"` Webhooks WebhooksConfig `name:"webhooks" description:"Webhooks configuration"` PubSub PubSubConfig `name:"pubsub" description:"Pub/sub messaging configuration"` Packages ApplicationPackagesConfig `name:"packages" description:"Application packages configuration"` Interop InteropConfig `name:"interop" description:"Interop client configuration"` - DeviceKEKLabel string `name:"device-kek-label" description:"Label of KEK used to encrypt device keys at rest"` - DeviceLastSeen LastSeenConfig `name:"device-last-seen" description:"End Device last seen batch update configuration"` + DeviceKEKLabel string `name:"device-kek-label" description:"Label of KEK used to encrypt device keys at rest"` // nolint:lll + DeviceLastSeen LastSeenConfig `name:"device-last-seen" description:"End Device last seen batch update configuration"` // nolint:lll Downlinks DownlinksConfig `name:"downlinks" description:"Downlink configuration"` Pagination PaginationConfig `name:"pagination" description:"Pagination configuration"` } @@ -153,10 +157,10 @@ type UplinkStorageConfig struct { type WebhooksConfig struct { Registry web.WebhookRegistry `name:"-"` Target string `name:"target" description:"Target of the integration (direct)"` - Timeout time.Duration `name:"timeout" description:"Wait timeout of the target to process the request"` + Timeout time.Duration `name:"timeout" description:"Wait timeout of the target to process the request"` // nolint:lll QueueSize int `name:"queue-size" description:"Number of requests to queue"` Workers int `name:"workers" description:"Number of workers to process requests"` - UnhealthyAttemptsThreshold int `name:"unhealthy-attempts-threshold" description:"Number of failed webhook attempts before the webhook is disabled"` + UnhealthyAttemptsThreshold int `name:"unhealthy-attempts-threshold" description:"Number of failed webhook attempts before the webhook is disabled"` // nolint:lll UnhealthyRetryInterval time.Duration `name:"unhealthy-retry-interval" description:"Time interval after which disabled webhooks may execute again"` Templates web.TemplatesConfig `name:"templates" description:"The store of the webhook templates"` Downlinks web.DownlinksConfig `name:"downlink" description:"The downlink queue operations configuration"` @@ -292,7 +296,10 @@ func (c PubSubConfig) NewPubSub(comp *component.Component, server io.Server) (*p // NewApplicationPackages returns a new applications packages frontend based on the configuration. // If the registry is nil, it returns nil. -func (c ApplicationPackagesConfig) NewApplicationPackages(ctx context.Context, server io.Server) (packages.Server, error) { +func (c ApplicationPackagesConfig) NewApplicationPackages( + ctx context.Context, + server io.Server, +) (packages.Server, error) { if c.Registry == nil { return nil, nil } @@ -310,27 +317,19 @@ func (c ApplicationPackagesConfig) NewApplicationPackages(ctx context.Context, s return packages.New(ctx, server, c.Registry, handlers, c.Workers, c.Timeout) } -var ( - errInvalidTimeout = errors.DefineInvalidArgument("invalid_timeout", "invalid timeout `{timeout}`") - errInvalidTTL = errors.DefineInvalidArgument("invalid_ttl", "invalid TTL `{ttl}`") -) +var errInvalidTimeout = errors.DefineInvalidArgument("invalid_timeout", "invalid timeout `{timeout}`") -// NewRegistry returns a new end device location registry based on the configuration. -func (c EndDeviceLocationStorageConfig) NewRegistry(ctx context.Context, comp *component.Component) (metadata.EndDeviceLocationRegistry, error) { +// NewRegistry returns a new end device attributes registry based on the configuration. +func (c EndDeviceMetadataStorageConfig) NewRegistry( + _ context.Context, + comp *component.Component, +) (metadata.EndDeviceRegistry, error) { if c.Timeout <= 0 { return nil, errInvalidTimeout.WithAttributes("timeout", c.Timeout) } - registry := metadata.NewClusterEndDeviceLocationRegistry(comp, c.Timeout) - registry = metadata.NewMetricsEndDeviceLocationRegistry(registry) - if c.Cache.Enable { - for _, ttl := range []time.Duration{c.Cache.MinRefreshInterval, c.Cache.MaxRefreshInterval, c.Cache.TTL} { - if ttl <= 0 { - return nil, errInvalidTTL.WithAttributes("ttl", ttl) - } - } - cache := metadata.NewMetricsEndDeviceLocationCache(c.Cache.Cache) - registry = metadata.NewCachedEndDeviceLocationRegistry(ctx, comp, registry, cache, c.Cache.MinRefreshInterval, c.Cache.MaxRefreshInterval, c.Cache.TTL) - } + registry := metadata.NewClusterEndDeviceRegistry(comp, c.Timeout) + registry = metadata.NewMetricsEndDeviceRegistry(registry) + return registry, nil } diff --git a/pkg/applicationserver/metadata/cluster.go b/pkg/applicationserver/metadata/cluster.go new file mode 100644 index 0000000000..63eee88360 --- /dev/null +++ b/pkg/applicationserver/metadata/cluster.go @@ -0,0 +1,29 @@ +// Copyright © 2025 The Things Network Foundation, The Things Industries B.V. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package metadata + +import ( + "context" + + "go.thethings.network/lorawan-stack/v3/pkg/cluster" + "go.thethings.network/lorawan-stack/v3/pkg/ttnpb" + "google.golang.org/grpc" +) + +// ClusterPeerAccess provides access to cluster peers. +type ClusterPeerAccess interface { + GetPeerConn(ctx context.Context, role ttnpb.ClusterRole, ids cluster.EntityIdentifiers) (*grpc.ClientConn, error) + WithClusterAuth() grpc.CallOption +} diff --git a/pkg/applicationserver/metadata/end_device_registry.go b/pkg/applicationserver/metadata/end_device_registry.go new file mode 100644 index 0000000000..d133c811b4 --- /dev/null +++ b/pkg/applicationserver/metadata/end_device_registry.go @@ -0,0 +1,215 @@ +// Copyright © 2025 The Things Network Foundation, The Things Industries B.V. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package metadata + +import ( + "context" + "time" + + "go.thethings.network/lorawan-stack/v3/pkg/ttnpb" +) + +// allowedFieldMaskPaths defines the allowed field mask paths that can be accessed for end devices from this package. +// Calls to the entity registry require an IS roundtrip call which can be cross-continental. This must be done for a +// high volume of end devices, so we want to limit the amount of data that is being transferred. +var allowedFieldMaskPaths = []string{ + "attributes", + "locations", +} + +// EndDeviceRegistry interface for the identity server. +type EndDeviceRegistry interface { + // Get returns an end device from the entity registry by its identifiers. + Get(ctx context.Context, ids *ttnpb.EndDeviceIdentifiers, paths []string) (*ttnpb.EndDevice, error) + // Set creates, updates or deletes an end device from the entity registry by its identifiers. + Set( + ctx context.Context, + ids *ttnpb.EndDeviceIdentifiers, + paths []string, + f func(*ttnpb.EndDevice) (*ttnpb.EndDevice, []string, error), + ) (*ttnpb.EndDevice, error) +} + +type noopEndDeviceRegistry struct{} + +// Get implements EndDeviceRegistry. +func (noopEndDeviceRegistry) Get( + _ context.Context, + _ *ttnpb.EndDeviceIdentifiers, + _ []string, +) (*ttnpb.EndDevice, error) { + return &ttnpb.EndDevice{}, nil // nolint: nilnil +} + +// Set implements EndDeviceRegistry. +func (noopEndDeviceRegistry) Set( + _ context.Context, + _ *ttnpb.EndDeviceIdentifiers, + _ []string, + f func(*ttnpb.EndDevice) (*ttnpb.EndDevice, []string, error), +) (*ttnpb.EndDevice, error) { + if f == nil { + return &ttnpb.EndDevice{}, nil // nolint: nilnil + } + + endDevice, _, err := f(&ttnpb.EndDevice{}) + return endDevice, err +} + +// NewNoopEndDeviceRegistry returns a noop EndDeviceRegistry. +func NewNoopEndDeviceRegistry() EndDeviceRegistry { + return noopEndDeviceRegistry{} +} + +type metricsEndDeviceRegistry struct { + inner EndDeviceRegistry +} + +// Get implements EndDeviceRegistry. +func (m *metricsEndDeviceRegistry) Get( + ctx context.Context, + ids *ttnpb.EndDeviceIdentifiers, + paths []string, +) (*ttnpb.EndDevice, error) { + registerMetadataRegistryRetrieval(ctx, endDeviceLabel) + return m.inner.Get(ctx, ids, paths) +} + +// Set implements EndDeviceRegistry. +func (m *metricsEndDeviceRegistry) Set( + ctx context.Context, + ids *ttnpb.EndDeviceIdentifiers, + paths []string, + f func(*ttnpb.EndDevice) (*ttnpb.EndDevice, []string, error), +) (*ttnpb.EndDevice, error) { + registerMetadataRegistryUpdate(ctx, endDeviceLabel) + return m.inner.Set(ctx, ids, paths, f) +} + +// NewMetricsEndDeviceRegistry returns an EndDeviceRegistry that collects metrics. +func NewMetricsEndDeviceRegistry(inner EndDeviceRegistry) EndDeviceRegistry { + return &metricsEndDeviceRegistry{ + inner: inner, + } +} + +type clusterEndDeviceRegistry struct { + ClusterPeerAccess + timeout time.Duration +} + +// Get implements EndDeviceRegistry. +func (c clusterEndDeviceRegistry) Get( + ctx context.Context, + ids *ttnpb.EndDeviceIdentifiers, + paths []string, +) (*ttnpb.EndDevice, error) { + paths, err := processEndDeviceFieldMaskPaths(paths) + if err != nil { + return nil, err + } + + cc, err := c.GetPeerConn(ctx, ttnpb.ClusterRole_ENTITY_REGISTRY, nil) + if err != nil { + return nil, err + } + + cl := ttnpb.NewEndDeviceRegistryClient(cc) + ctx, cancel := context.WithTimeout(ctx, c.timeout) + defer cancel() + + dev, err := cl.Get(ctx, &ttnpb.GetEndDeviceRequest{ + EndDeviceIds: ids, + FieldMask: ttnpb.FieldMask(paths...), + }, c.WithClusterAuth()) + if err != nil { + return nil, err + } + + return dev, nil +} + +// Set implements EndDeviceRegistry. +func (c clusterEndDeviceRegistry) Set( + ctx context.Context, + ids *ttnpb.EndDeviceIdentifiers, + paths []string, + f func(*ttnpb.EndDevice) (*ttnpb.EndDevice, []string, error), +) (*ttnpb.EndDevice, error) { + paths, err := processEndDeviceFieldMaskPaths(paths) + if err != nil { + return nil, err + } + + cc, err := c.GetPeerConn(ctx, ttnpb.ClusterRole_ENTITY_REGISTRY, nil) + if err != nil { + return nil, err + } + + cl := ttnpb.NewEndDeviceRegistryClient(cc) + ctx, cancel := context.WithTimeout(ctx, c.timeout) + defer cancel() + + dev, err := cl.Get(ctx, &ttnpb.GetEndDeviceRequest{ + EndDeviceIds: ids, + FieldMask: ttnpb.FieldMask(paths...), + }, c.WithClusterAuth()) + if err != nil { + return nil, err + } + + dev, paths, err = f(dev) + if err != nil || dev == nil { + return nil, err + } + dev, err = cl.Update(ctx, &ttnpb.UpdateEndDeviceRequest{ + EndDevice: dev, + FieldMask: ttnpb.FieldMask(paths...), + }, c.WithClusterAuth()) + if err != nil { + return nil, err + } + + return dev, nil +} + +// NewClusterEndDeviceRegistry returns an EndDeviceRegistry connected to the entity registry of the Identity Server. +func NewClusterEndDeviceRegistry(cluster ClusterPeerAccess, timeout time.Duration) EndDeviceRegistry { + return &clusterEndDeviceRegistry{ + ClusterPeerAccess: cluster, + timeout: timeout, + } +} + +func processEndDeviceFieldMaskPaths(paths []string) ([]string, error) { + if len(paths) == 0 { + return allowedFieldMaskPaths, nil + } + + if err := validateEndDevicePaths(paths); err != nil { + return nil, err + } + + return paths, nil +} + +func validateEndDevicePaths(paths []string) error { + allowedFieldMaskSet := ttnpb.FieldMaskPathsSet(allowedFieldMaskPaths) + if ok, firstNotAllowedPath := ttnpb.FieldMaskPathsSetContainsAll(allowedFieldMaskSet, paths...); !ok { + return errFieldMaskPathNotSupported.WithAttributes("path", firstNotAllowedPath) + } + + return nil +} diff --git a/pkg/applicationserver/metadata/end_device_registry_test.go b/pkg/applicationserver/metadata/end_device_registry_test.go new file mode 100644 index 0000000000..9aac154a89 --- /dev/null +++ b/pkg/applicationserver/metadata/end_device_registry_test.go @@ -0,0 +1,210 @@ +// Copyright © 2025 The Things Network Foundation, The Things Industries B.V. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package metadata_test + +import ( + "testing" + "time" + + "go.thethings.network/lorawan-stack/v3/pkg/errors" + + "go.thethings.network/lorawan-stack/v3/pkg/applicationserver/metadata" + "go.thethings.network/lorawan-stack/v3/pkg/cluster" + "go.thethings.network/lorawan-stack/v3/pkg/component" + componenttest "go.thethings.network/lorawan-stack/v3/pkg/component/test" + "go.thethings.network/lorawan-stack/v3/pkg/config" + mockis "go.thethings.network/lorawan-stack/v3/pkg/identityserver/mock" + "go.thethings.network/lorawan-stack/v3/pkg/ttnpb" + "go.thethings.network/lorawan-stack/v3/pkg/util/test" + "go.thethings.network/lorawan-stack/v3/pkg/util/test/assertions/should" +) + +var ( + originalLocations = map[string]*ttnpb.Location{ + "baz": { + Altitude: 12, + Latitude: 23, + }, + } + locationsPatch = map[string]*ttnpb.Location{ + "bzz": { + Altitude: 23, + Latitude: 34, + }, + } + + originalAttributes = map[string]string{ + "attr1": "val1", + "attr2": "val2", + } + + attributesPatch = map[string]string{ + "attr3": "val3", + "attr4": "val4", + } +) + +func TestClusterEndDeviceRegistry(t *testing.T) { // nolint:gocyclo + registeredEndDeviceIDs := &ttnpb.EndDeviceIdentifiers{ + ApplicationIds: &ttnpb.ApplicationIdentifiers{ + ApplicationId: "foo", + }, + DeviceId: "bar", + } + + t.Parallel() + + a, ctx := test.New(t) + is, isAddr, closeIS := mockis.New(ctx) + defer closeIS() + + registeredEndDevice := ttnpb.EndDevice{ + Ids: registeredEndDeviceIDs, + Locations: originalLocations, + Attributes: originalAttributes, + } + is.EndDeviceRegistry().Add(ctx, ®isteredEndDevice) + + c := componenttest.NewComponent(t, &component.Config{ + ServiceBase: config.ServiceBase{ + Cluster: cluster.Config{ + IdentityServer: isAddr, + }, + }, + }) + componenttest.StartComponent(t, c) + defer c.Close() + mustHavePeer(ctx, c, ttnpb.ClusterRole_ENTITY_REGISTRY) + + registry := metadata.NewClusterEndDeviceRegistry(c, 10*time.Second) + + _, err := registry.Get(ctx, registeredEndDeviceIDs, []string{ + "network_server_address", "application_server_address", "join_server_address", + }) + a.So(errors.IsInvalidArgument(err), should.BeTrue) + + dev, err := registry.Get(ctx, registeredEndDeviceIDs, []string{"attributes", "locations"}) + if a.So(err, should.BeNil) { + a.So(dev, should.NotBeNil) + + a.So(dev.Locations, should.NotBeNil) + a.So(len(dev.Locations), should.Equal, len(registeredEndDevice.Locations)) + for k, v := range dev.Locations { + a.So(registeredEndDevice.Locations[k], should.Resemble, v) + } + for k, v := range originalLocations { + a.So(dev.Locations[k], should.Resemble, v) + } + + a.So(dev.Attributes, should.NotBeNil) + a.So(len(dev.Attributes), should.Equal, len(registeredEndDevice.Attributes)) + for k, v := range dev.Attributes { + a.So(registeredEndDevice.Attributes[k], should.Equal, v) + } + for k, v := range originalAttributes { + a.So(dev.Attributes[k], should.Equal, v) + } + } + + _, err = registry.Set(ctx, registeredEndDeviceIDs, []string{ + "network_server_address", "application_server_address", "join_server_address", + }, func(_ *ttnpb.EndDevice) (*ttnpb.EndDevice, []string, error) { + return nil, nil, nil // nolint: nilnil + }) + a.So(errors.IsInvalidArgument(err), should.BeTrue) + + // Update location and attributes. + dev, err = registry.Set(ctx, registeredEndDeviceIDs, []string{"locations", "attributes"}, + func(stored *ttnpb.EndDevice) (*ttnpb.EndDevice, []string, error) { + if stored == nil { + return nil, nil, errors.New("not found") + } + + if len(stored.Locations) == 0 { + stored.Locations = make(map[string]*ttnpb.Location, len(locationsPatch)) + } + + for k, l := range locationsPatch { + stored.Locations[k] = l + } + + if len(stored.Attributes) == 0 { + stored.Attributes = make(map[string]string, len(attributesPatch)) + } + + for k, v := range attributesPatch { + stored.Attributes[k] = v + } + + return stored, []string{"locations", "attributes"}, nil + }, + ) + if a.So(err, should.BeNil) { + a.So(dev, should.NotBeNil) + + a.So(dev.Locations, should.NotBeNil) + a.So(len(dev.Locations), should.Equal, len(registeredEndDevice.Locations)) + for k, v := range dev.Locations { + a.So(registeredEndDevice.Locations[k], should.Resemble, v) + } + for k, v := range originalLocations { + a.So(dev.Locations[k], should.Resemble, v) + } + for k, v := range locationsPatch { + a.So(dev.Locations[k], should.Resemble, v) + } + + a.So(dev.Attributes, should.NotBeNil) + a.So(len(dev.Attributes), should.Equal, len(registeredEndDevice.Attributes)) + for k, v := range dev.Attributes { + a.So(registeredEndDevice.Attributes[k], should.Equal, v) + } + for k, v := range originalAttributes { + a.So(dev.Attributes[k], should.Equal, v) + } + for k, v := range attributesPatch { + a.So(dev.Attributes[k], should.Equal, v) + } + } + + dev, err = registry.Get(ctx, registeredEndDeviceIDs, []string{"attributes", "locations"}) + if a.So(err, should.BeNil) { + a.So(dev, should.NotBeNil) + + a.So(dev.Locations, should.NotBeNil) + a.So(len(dev.Locations), should.Equal, len(registeredEndDevice.Locations)) + for k, v := range dev.Locations { + a.So(registeredEndDevice.Locations[k], should.Resemble, v) + } + for k, v := range originalLocations { + a.So(dev.Locations[k], should.Resemble, v) + } + for k, v := range locationsPatch { + a.So(dev.Locations[k], should.Resemble, v) + } + + a.So(dev.Attributes, should.NotBeNil) + a.So(len(dev.Attributes), should.Equal, len(registeredEndDevice.Attributes)) + for k, v := range dev.Attributes { + a.So(registeredEndDevice.Attributes[k], should.Equal, v) + } + for k, v := range originalAttributes { + a.So(dev.Attributes[k], should.Equal, v) + } + for k, v := range attributesPatch { + a.So(dev.Attributes[k], should.Equal, v) + } + } +} diff --git a/pkg/applicationserver/metadata/location_cache.go b/pkg/applicationserver/metadata/location_cache.go deleted file mode 100644 index 6ad1a3de1a..0000000000 --- a/pkg/applicationserver/metadata/location_cache.go +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright © 2021 The Things Network Foundation, The Things Industries B.V. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package metadata - -import ( - "context" - "time" - - "go.thethings.network/lorawan-stack/v3/pkg/ttnpb" -) - -// EndDeviceLocationCache is a cache for end device locations. -type EndDeviceLocationCache interface { - // Get retrieves the end device locations and the remaining TTL for the entry. - Get(ctx context.Context, ids *ttnpb.EndDeviceIdentifiers) (map[string]*ttnpb.Location, *time.Time, error) - // Set sets the end device locations. - Set(ctx context.Context, ids *ttnpb.EndDeviceIdentifiers, update map[string]*ttnpb.Location, ttl time.Duration) error - // Delete removes the locations from the cache. - Delete(ctx context.Context, ids *ttnpb.EndDeviceIdentifiers) error -} - -type metricsEndDeviceLocationCache struct { - inner EndDeviceLocationCache -} - -// Get implements EndDeviceLocationCache. -func (c *metricsEndDeviceLocationCache) Get(ctx context.Context, ids *ttnpb.EndDeviceIdentifiers) (map[string]*ttnpb.Location, *time.Time, error) { - m, storedAt, err := c.inner.Get(ctx, ids) - if storedAt == nil { - registerMetadataCacheMiss(ctx, locationLabel) - } else { - registerMetadataCacheHit(ctx, locationLabel) - } - return m, storedAt, err -} - -// Set implements EndDeviceLocationCache. -func (c *metricsEndDeviceLocationCache) Set(ctx context.Context, ids *ttnpb.EndDeviceIdentifiers, update map[string]*ttnpb.Location, ttl time.Duration) error { - return c.inner.Set(ctx, ids, update, ttl) -} - -// Delete implements EndDeviceLocationCache. -func (c *metricsEndDeviceLocationCache) Delete(ctx context.Context, ids *ttnpb.EndDeviceIdentifiers) error { - return c.inner.Delete(ctx, ids) -} - -// NewMetricsEndDeviceLocationCache constructs an EndDeviceLocationCache that collects metrics. -func NewMetricsEndDeviceLocationCache(inner EndDeviceLocationCache) EndDeviceLocationCache { - return &metricsEndDeviceLocationCache{ - inner: inner, - } -} diff --git a/pkg/applicationserver/metadata/location_registry.go b/pkg/applicationserver/metadata/location_registry.go deleted file mode 100644 index b1c1a5fcef..0000000000 --- a/pkg/applicationserver/metadata/location_registry.go +++ /dev/null @@ -1,242 +0,0 @@ -// Copyright © 2021 The Things Network Foundation, The Things Industries B.V. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package metadata - -import ( - "context" - "time" - - "go.thethings.network/lorawan-stack/v3/pkg/cluster" - "go.thethings.network/lorawan-stack/v3/pkg/errors" - "go.thethings.network/lorawan-stack/v3/pkg/log" - "go.thethings.network/lorawan-stack/v3/pkg/random" - "go.thethings.network/lorawan-stack/v3/pkg/ttnpb" - "go.thethings.network/lorawan-stack/v3/pkg/workerpool" - "google.golang.org/grpc" -) - -// EndDeviceLocationRegistry is a registry for end device locations. -type EndDeviceLocationRegistry interface { - // Get retrieves the end device locations. - Get(ctx context.Context, ids *ttnpb.EndDeviceIdentifiers) (map[string]*ttnpb.Location, error) - // Merge merges the end device locations. - Merge(ctx context.Context, ids *ttnpb.EndDeviceIdentifiers, update map[string]*ttnpb.Location) (map[string]*ttnpb.Location, error) -} - -type noopEndDeviceLocationRegistry struct{} - -// Get implements EndDeviceLocationRegistry. -func (noopEndDeviceLocationRegistry) Get(ctx context.Context, ids *ttnpb.EndDeviceIdentifiers) (map[string]*ttnpb.Location, error) { - return nil, nil -} - -// Merge implements EndDeviceLocationRegistry. -func (noopEndDeviceLocationRegistry) Merge(ctx context.Context, ids *ttnpb.EndDeviceIdentifiers, update map[string]*ttnpb.Location) (map[string]*ttnpb.Location, error) { - return update, nil -} - -// NewNoopEndDeviceLocationRegistry returns a noop EndDeviceLocationRegistry. -func NewNoopEndDeviceLocationRegistry() EndDeviceLocationRegistry { - return noopEndDeviceLocationRegistry{} -} - -type metricsEndDeviceLocationRegistry struct { - inner EndDeviceLocationRegistry -} - -// Get implements EndDeviceLocationRegistry. -func (m *metricsEndDeviceLocationRegistry) Get(ctx context.Context, ids *ttnpb.EndDeviceIdentifiers) (map[string]*ttnpb.Location, error) { - registerMetadataRegistryRetrieval(ctx, locationLabel) - return m.inner.Get(ctx, ids) -} - -// Merge implements EndDeviceLocationRegistry. -func (m *metricsEndDeviceLocationRegistry) Merge(ctx context.Context, ids *ttnpb.EndDeviceIdentifiers, update map[string]*ttnpb.Location) (map[string]*ttnpb.Location, error) { - registerMetadataRegistryUpdate(ctx, locationLabel) - return m.inner.Merge(ctx, ids, update) -} - -// NewMetricsEndDeviceLocationRegistry returns an EndDeviceLocationRegistry that collects metrics. -func NewMetricsEndDeviceLocationRegistry(inner EndDeviceLocationRegistry) EndDeviceLocationRegistry { - return &metricsEndDeviceLocationRegistry{ - inner: inner, - } -} - -// ClusterPeerAccess provides access to cluster peers. -type ClusterPeerAccess interface { - GetPeerConn(ctx context.Context, role ttnpb.ClusterRole, ids cluster.EntityIdentifiers) (*grpc.ClientConn, error) - WithClusterAuth() grpc.CallOption -} - -var endDeviceLocationFieldMask = ttnpb.FieldMask("locations") - -type clusterEndDeviceLocationRegistry struct { - ClusterPeerAccess - timeout time.Duration -} - -// Get implements EndDeviceLocationRegistry. -func (c clusterEndDeviceLocationRegistry) Get(ctx context.Context, ids *ttnpb.EndDeviceIdentifiers) (map[string]*ttnpb.Location, error) { - cc, err := c.GetPeerConn(ctx, ttnpb.ClusterRole_ENTITY_REGISTRY, nil) - if err != nil { - return nil, err - } - cl := ttnpb.NewEndDeviceRegistryClient(cc) - ctx, cancel := context.WithTimeout(ctx, c.timeout) - defer cancel() - dev, err := cl.Get(ctx, &ttnpb.GetEndDeviceRequest{ - EndDeviceIds: ids, - FieldMask: endDeviceLocationFieldMask, - }, c.WithClusterAuth()) - if err != nil { - return nil, err - } - return dev.Locations, nil -} - -// Merge implements EndDeviceLocationRegistry. -func (c clusterEndDeviceLocationRegistry) Merge(ctx context.Context, ids *ttnpb.EndDeviceIdentifiers, update map[string]*ttnpb.Location) (map[string]*ttnpb.Location, error) { - cc, err := c.GetPeerConn(ctx, ttnpb.ClusterRole_ENTITY_REGISTRY, nil) - if err != nil { - return nil, err - } - cl := ttnpb.NewEndDeviceRegistryClient(cc) - ctx, cancel := context.WithTimeout(ctx, c.timeout) - defer cancel() - dev, err := cl.Get(ctx, &ttnpb.GetEndDeviceRequest{ - EndDeviceIds: ids, - FieldMask: endDeviceLocationFieldMask, - }, c.WithClusterAuth()) - if err != nil { - return nil, err - } - if len(update) == 0 { - return dev.Locations, nil - } - if dev.Locations == nil { - dev.Locations = make(map[string]*ttnpb.Location, len(update)) - } - for k, l := range update { - dev.Locations[k] = l - } - _, err = cl.Update(ctx, &ttnpb.UpdateEndDeviceRequest{ - EndDevice: &ttnpb.EndDevice{ - Ids: ids, - Locations: dev.Locations, - }, - FieldMask: endDeviceLocationFieldMask, - }, c.WithClusterAuth()) - if err != nil { - return nil, err - } - return dev.Locations, nil -} - -// NewClusterEndDeviceLocationRegistry returns an EndDeviceLocationRegistry connected to the Entity Registry. -func NewClusterEndDeviceLocationRegistry(cluster ClusterPeerAccess, timeout time.Duration) EndDeviceLocationRegistry { - return &clusterEndDeviceLocationRegistry{ - ClusterPeerAccess: cluster, - timeout: timeout, - } -} - -type cachedEndDeviceLocationRegistry struct { - registry EndDeviceLocationRegistry - cache EndDeviceLocationCache - - minRefreshInterval time.Duration - maxRefreshInterval time.Duration - ttl time.Duration - - replicationPool workerpool.WorkerPool[*ttnpb.EndDeviceIdentifiers] -} - -// Get implements EndDeviceLocationRegistry. -func (c *cachedEndDeviceLocationRegistry) Get(ctx context.Context, ids *ttnpb.EndDeviceIdentifiers) (map[string]*ttnpb.Location, error) { - locations, storedAt, err := c.cache.Get(ctx, ids) - switch { - case err != nil && !errors.IsNotFound(err): - return nil, err - case err != nil && errors.IsNotFound(err): - locations = nil - case err == nil: - age := time.Since(*storedAt) - if age <= c.minRefreshInterval { - // If the object is younger than the minimum refresh interval, just return the cached value. - return locations, nil - } - if remaining := c.maxRefreshInterval - age; remaining > 0 { - // If the objects age is between the minimum and maximum refresh interval, check if we should asynchronously - // refresh the cache. - window := c.maxRefreshInterval - c.minRefreshInterval - threshold := time.Duration(random.Int63n(int64(window))) - // remaining is the remaining window of the refresh interval in the (0, window) interval. - // threshold is a uniformly distributed duration in the [0, window) interval. - if remaining >= threshold { - return locations, nil - } - } - } - if err := c.replicationPool.Publish(ctx, ids); err != nil { - log.FromContext(ctx).WithError(err).Warn("Failed to publish end device locations replication request") - } - return locations, nil -} - -// Merge implements EndDeviceLocationRegistry. -func (c *cachedEndDeviceLocationRegistry) Merge(ctx context.Context, ids *ttnpb.EndDeviceIdentifiers, update map[string]*ttnpb.Location) (map[string]*ttnpb.Location, error) { - locations, err := c.registry.Merge(ctx, ids, update) - if err != nil { - return nil, err - } - if err := c.cache.Set(ctx, ids, locations, c.ttl); err != nil { - return nil, err - } - return locations, nil -} - -// NewCachedEndDeviceLocationRegistry returns an EndDeviceLocationRegistry that caches the responses of the provided EndDeviceLocationRegistry in the provided -// EndDeviceLocationCache. On cache miss, the registry will retrieve and cache the locations asynchronously. -// Items whose TTL is within the soft TTL window have a chance to trigger an asynchronous cache synchronization event on location retrieval. -// The probability of a synchronization event increases linearly between the soft TTL (0%) and the hard TTL (100%). -func NewCachedEndDeviceLocationRegistry(ctx context.Context, c workerpool.Component, registry EndDeviceLocationRegistry, cache EndDeviceLocationCache, minRefreshInterval, maxRefreshInterval, ttl time.Duration) EndDeviceLocationRegistry { - st := &cachedEndDeviceLocationRegistry{ - registry: registry, - cache: cache, - - minRefreshInterval: minRefreshInterval, - maxRefreshInterval: maxRefreshInterval, - ttl: ttl, - - replicationPool: workerpool.NewWorkerPool(workerpool.Config[*ttnpb.EndDeviceIdentifiers]{ - Component: c, - Context: ctx, - Name: "replicate_end_device_locations", - Handler: func(ctx context.Context, ids *ttnpb.EndDeviceIdentifiers) { - locations, err := registry.Get(ctx, ids) - if err != nil { - log.FromContext(ctx).WithError(err).Warn("Failed to retrieve end device locations") - return - } - if err := cache.Set(ctx, ids, locations, ttl); err != nil { - log.FromContext(ctx).WithError(err).Warn("Failed to cache end device locations") - return - } - }, - }), - } - return st -} diff --git a/pkg/applicationserver/metadata/location_registry_test.go b/pkg/applicationserver/metadata/location_registry_test.go deleted file mode 100644 index f366942a04..0000000000 --- a/pkg/applicationserver/metadata/location_registry_test.go +++ /dev/null @@ -1,274 +0,0 @@ -// Copyright © 2021 The Things Network Foundation, The Things Industries B.V. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package metadata_test - -import ( - "context" - "testing" - "time" - - "go.thethings.network/lorawan-stack/v3/pkg/applicationserver/metadata" - "go.thethings.network/lorawan-stack/v3/pkg/applicationserver/metadata/redis" - "go.thethings.network/lorawan-stack/v3/pkg/cluster" - "go.thethings.network/lorawan-stack/v3/pkg/component" - componenttest "go.thethings.network/lorawan-stack/v3/pkg/component/test" - "go.thethings.network/lorawan-stack/v3/pkg/config" - mockis "go.thethings.network/lorawan-stack/v3/pkg/identityserver/mock" - "go.thethings.network/lorawan-stack/v3/pkg/ttnpb" - "go.thethings.network/lorawan-stack/v3/pkg/util/test" - "go.thethings.network/lorawan-stack/v3/pkg/util/test/assertions/should" -) - -func mustHavePeer(ctx context.Context, c *component.Component, role ttnpb.ClusterRole) { - for i := 0; i < 20; i++ { - time.Sleep(20 * time.Millisecond) - if _, err := c.GetPeer(ctx, role, nil); err == nil { - return - } - } - panic("could not connect to peer") -} - -var ( - registeredEndDeviceIDs = &ttnpb.EndDeviceIdentifiers{ - ApplicationIds: &ttnpb.ApplicationIdentifiers{ - ApplicationId: "foo", - }, - DeviceId: "bar", - } - originalLocations = map[string]*ttnpb.Location{ - "baz": { - Altitude: 12, - Latitude: 23, - }, - } - locationsPatch = map[string]*ttnpb.Location{ - "bzz": { - Altitude: 23, - Latitude: 34, - }, - } - Timeout = (1 << 7) * test.Delay -) - -func TestClusterEndDeviceLocationRegistry(t *testing.T) { - a, ctx := test.New(t) - is, isAddr, closeIS := mockis.New(ctx) - defer closeIS() - - registeredEndDevice := ttnpb.EndDevice{ - Ids: registeredEndDeviceIDs, - Locations: originalLocations, - } - is.EndDeviceRegistry().Add(ctx, ®isteredEndDevice) - - c := componenttest.NewComponent(t, &component.Config{ - ServiceBase: config.ServiceBase{ - Cluster: cluster.Config{ - IdentityServer: isAddr, - }, - }, - }) - componenttest.StartComponent(t, c) - defer c.Close() - mustHavePeer(ctx, c, ttnpb.ClusterRole_ENTITY_REGISTRY) - - registry := metadata.NewClusterEndDeviceLocationRegistry(c, 10*time.Second) - - locations, err := registry.Get(ctx, registeredEndDeviceIDs) - if a.So(err, should.BeNil) { - a.So(locations, should.NotBeNil) - a.So(len(locations), should.Equal, len(registeredEndDevice.Locations)) - for k, v := range locations { - a.So(registeredEndDevice.Locations[k], should.Resemble, v) - } - for k, v := range originalLocations { - a.So(locations[k], should.Resemble, v) - } - } - - locations, err = registry.Merge(ctx, registeredEndDeviceIDs, locationsPatch) - if a.So(err, should.BeNil) { - a.So(locations, should.NotBeNil) - a.So(len(locations), should.Equal, len(registeredEndDevice.Locations)) - for k, v := range locations { - a.So(registeredEndDevice.Locations[k], should.Resemble, v) - } - for k, v := range originalLocations { - a.So(locations[k], should.Resemble, v) - } - for k, v := range locationsPatch { - a.So(locations[k], should.Resemble, v) - } - } - - locations, err = registry.Get(ctx, registeredEndDeviceIDs) - if a.So(err, should.BeNil) { - a.So(locations, should.NotBeNil) - a.So(len(locations), should.Equal, len(registeredEndDevice.Locations)) - for k, v := range locations { - a.So(registeredEndDevice.Locations[k], should.Resemble, v) - } - for k, v := range originalLocations { - a.So(locations[k], should.Resemble, v) - } - for k, v := range locationsPatch { - a.So(locations[k], should.Resemble, v) - } - } -} - -func TestCachedEndDeviceLocationRegistry(t *testing.T) { - a, ctx := test.New(t) - is, isAddr, closeIS := mockis.New(ctx) - defer closeIS() - - registeredEndDevice := ttnpb.EndDevice{ - Ids: registeredEndDeviceIDs, - Locations: originalLocations, - } - is.EndDeviceRegistry().Add(ctx, ®isteredEndDevice) - - c := componenttest.NewComponent(t, &component.Config{ - ServiceBase: config.ServiceBase{ - Cluster: cluster.Config{ - IdentityServer: isAddr, - }, - }, - }) - componenttest.StartComponent(t, c) - defer c.Close() - mustHavePeer(ctx, c, ttnpb.ClusterRole_ENTITY_REGISTRY) - - registry := metadata.NewClusterEndDeviceLocationRegistry(c, 4*Timeout) - cl, flush := test.NewRedis(ctx, "metadata_redis_test") - defer flush() - cache := &redis.EndDeviceLocationCache{ - Redis: cl, - } - registry = metadata.NewCachedEndDeviceLocationRegistry( - ctx, c, registry, cache, 4*Timeout, 8*Timeout, 16*Timeout, - ) - - locations, err := registry.Get(ctx, registeredEndDeviceIDs) - a.So(err, should.BeNil) - a.So(locations, should.HaveLength, 0) - - // Wait for the cache to be populated asynchronously. - time.Sleep(Timeout) - - locations, err = registry.Get(ctx, registeredEndDeviceIDs) - if a.So(err, should.BeNil) { - a.So(locations, should.NotBeNil) - a.So(len(locations), should.Equal, len(originalLocations)) - for k, v := range originalLocations { - a.So(locations[k], should.Resemble, v) - } - } - - locations, err = registry.Merge(ctx, registeredEndDeviceIDs, locationsPatch) - if a.So(err, should.BeNil) { - a.So(locations, should.NotBeNil) - a.So(len(locations), should.Equal, len(registeredEndDevice.Locations)) - for k, v := range locations { - a.So(registeredEndDevice.Locations[k], should.Resemble, v) - } - for k, v := range originalLocations { - a.So(locations[k], should.Resemble, v) - } - for k, v := range locationsPatch { - a.So(locations[k], should.Resemble, v) - } - } - - locations, err = registry.Get(ctx, registeredEndDeviceIDs) - if a.So(err, should.BeNil) { - a.So(locations, should.NotBeNil) - a.So(len(locations), should.Equal, len(registeredEndDevice.Locations)) - for k, v := range locations { - a.So(registeredEndDevice.Locations[k], should.Resemble, v) - } - for k, v := range originalLocations { - a.So(locations[k], should.Resemble, v) - } - for k, v := range locationsPatch { - a.So(locations[k], should.Resemble, v) - } - } - - // Wait for the entry to be evicted. - time.Sleep(20 * Timeout) - - // There is no cached location anymore, and we have triggered an asynchronous refresh. - locations, err = registry.Get(ctx, registeredEndDeviceIDs) - a.So(err, should.BeNil) - a.So(locations, should.HaveLength, 0) - - time.Sleep(Timeout) - - locations, err = registry.Get(ctx, registeredEndDeviceIDs) - if a.So(err, should.BeNil) { - a.So(locations, should.NotBeNil) - a.So(len(locations), should.Equal, len(registeredEndDevice.Locations)) - for k, v := range locations { - a.So(registeredEndDevice.Locations[k], should.Resemble, v) - } - for k, v := range originalLocations { - a.So(locations[k], should.Resemble, v) - } - for k, v := range locationsPatch { - a.So(locations[k], should.Resemble, v) - } - } - - // Simulate a network partition. - closeIS() - time.Sleep(Timeout) - - // Do a read that will trigger an asynchronous cache refresh. - locations, err = registry.Get(ctx, registeredEndDeviceIDs) - if a.So(err, should.BeNil) { - a.So(locations, should.NotBeNil) - a.So(len(locations), should.Equal, len(registeredEndDevice.Locations)) - for k, v := range locations { - a.So(registeredEndDevice.Locations[k], should.Resemble, v) - } - for k, v := range originalLocations { - a.So(locations[k], should.Resemble, v) - } - for k, v := range locationsPatch { - a.So(locations[k], should.Resemble, v) - } - } - - // Wait for the partition to be detected asynchronously. - time.Sleep(Timeout) - - // We now serve stale data. - locations, err = registry.Get(ctx, registeredEndDeviceIDs) - if a.So(err, should.BeNil) { - a.So(locations, should.NotBeNil) - a.So(len(locations), should.Equal, len(registeredEndDevice.Locations)) - for k, v := range locations { - a.So(registeredEndDevice.Locations[k], should.Resemble, v) - } - for k, v := range originalLocations { - a.So(locations[k], should.Resemble, v) - } - for k, v := range locationsPatch { - a.So(locations[k], should.Resemble, v) - } - } -} diff --git a/pkg/applicationserver/metadata/metadata.go b/pkg/applicationserver/metadata/metadata.go new file mode 100644 index 0000000000..fb442095c6 --- /dev/null +++ b/pkg/applicationserver/metadata/metadata.go @@ -0,0 +1,21 @@ +// Copyright © 2025 The Things Network Foundation, The Things Industries B.V. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package metadata contains the metadata registry clients. +package metadata + +import "go.thethings.network/lorawan-stack/v3/pkg/errors" + +var errFieldMaskPathNotSupported = errors.DefineInvalidArgument( + "field_mask_path_not_supported", "field mask path `{path}` is not supported") diff --git a/pkg/applicationserver/metadata/metadata_util_test.go b/pkg/applicationserver/metadata/metadata_util_test.go new file mode 100644 index 0000000000..56407cc45b --- /dev/null +++ b/pkg/applicationserver/metadata/metadata_util_test.go @@ -0,0 +1,33 @@ +// Copyright © 2025 The Things Network Foundation, The Things Industries B.V. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package metadata_test + +import ( + "context" + "time" + + "go.thethings.network/lorawan-stack/v3/pkg/component" + "go.thethings.network/lorawan-stack/v3/pkg/ttnpb" +) + +func mustHavePeer(ctx context.Context, c *component.Component, role ttnpb.ClusterRole) { // nolint: unparam + for i := 0; i < 20; i++ { + time.Sleep(20 * time.Millisecond) + if _, err := c.GetPeer(ctx, role, nil); err == nil { + return + } + } + panic("could not connect to peer") +} diff --git a/pkg/applicationserver/metadata/observability.go b/pkg/applicationserver/metadata/observability.go index 64ff2f6bc3..75a3c93408 100644 --- a/pkg/applicationserver/metadata/observability.go +++ b/pkg/applicationserver/metadata/observability.go @@ -22,28 +22,13 @@ import ( ) const ( - subsystem = "as_metadata" - metadataLabel = "metadata" - locationLabel = "location" + subsystem = "as_metadata" + metadataLabel = "metadata" + locationLabel = "location" + endDeviceLabel = "end_device" ) var metaMetrics = &metadataMetrics{ - cacheHits: metrics.NewCounterVec( - prometheus.CounterOpts{ - Subsystem: subsystem, - Name: "cache_hits_total", - Help: "Total number of metadata cache hits", - }, - []string{metadataLabel}, - ), - cacheMisses: metrics.NewCounterVec( - prometheus.CounterOpts{ - Subsystem: subsystem, - Name: "cache_misses_total", - Help: "Total number of metadata cache misses", - }, - []string{metadataLabel}, - ), registryRetrievals: metrics.NewCounterVec( prometheus.CounterOpts{ Subsystem: subsystem, @@ -67,40 +52,24 @@ func init() { } type metadataMetrics struct { - cacheHits *prometheus.CounterVec - cacheMisses *prometheus.CounterVec registryRetrievals *prometheus.CounterVec registryUpdates *prometheus.CounterVec } func (m metadataMetrics) Describe(ch chan<- *prometheus.Desc) { - m.cacheHits.Describe(ch) - m.cacheMisses.Describe(ch) m.registryRetrievals.Describe(ch) m.registryUpdates.Describe(ch) } func (m metadataMetrics) Collect(ch chan<- prometheus.Metric) { - m.cacheHits.Collect(ch) - m.cacheMisses.Collect(ch) m.registryRetrievals.Collect(ch) m.registryUpdates.Collect(ch) } -func registerMetadataCacheHit(ctx context.Context, metadata string) { - metaMetrics.cacheHits.WithLabelValues(metadata).Inc() - metaMetrics.cacheMisses.WithLabelValues(metadata) -} - -func registerMetadataCacheMiss(ctx context.Context, metadata string) { - metaMetrics.cacheHits.WithLabelValues(metadata) - metaMetrics.cacheMisses.WithLabelValues(metadata).Inc() -} - -func registerMetadataRegistryRetrieval(ctx context.Context, metadata string) { +func registerMetadataRegistryRetrieval(_ context.Context, metadata string) { metaMetrics.registryRetrievals.WithLabelValues(metadata).Inc() } -func registerMetadataRegistryUpdate(ctx context.Context, metadata string) { +func registerMetadataRegistryUpdate(_ context.Context, metadata string) { metaMetrics.registryUpdates.WithLabelValues(metadata).Inc() } diff --git a/pkg/applicationserver/metadata/redis/end_device_cache.go b/pkg/applicationserver/metadata/redis/end_device_cache.go new file mode 100644 index 0000000000..f9e48a1de7 --- /dev/null +++ b/pkg/applicationserver/metadata/redis/end_device_cache.go @@ -0,0 +1,116 @@ +// Copyright © 2025 The Things Network Foundation, The Things Industries B.V. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package redis + +import ( + "context" + "fmt" + "strconv" + "time" + + "github.com/redis/go-redis/v9" + ttnredis "go.thethings.network/lorawan-stack/v3/pkg/redis" + "go.thethings.network/lorawan-stack/v3/pkg/ttnpb" + "go.thethings.network/lorawan-stack/v3/pkg/unique" +) + +// EndDeviceCache is a Redis end device cache. +type EndDeviceCache struct { + Redis *ttnredis.Client +} + +func (r *EndDeviceCache) uidKey(uid string) string { + return r.Redis.Key("uid", uid) +} + +// Get returns the end device by the identifiers. +func (r *EndDeviceCache) Get( + ctx context.Context, + ids *ttnpb.EndDeviceIdentifiers, +) (*ttnpb.EndDevice, *time.Time, error) { + uidKey := r.uidKey(unique.ID(ctx, ids)) + m, err := r.Redis.HGetAll(ctx, uidKey).Result() + if err != nil { + return nil, nil, ttnredis.ConvertError(err) + } + if len(m) == 0 { + return nil, nil, errCacheMiss.New() + } + + var storedAt time.Time + if s, ok := m[storedAtMarker]; ok { + n, err := strconv.ParseInt(s, 10, 64) + if err != nil { + return nil, nil, err + } + storedAt = time.Unix(0, n) + } + if s, ok := m[errorMarker]; ok { + details := &ttnpb.ErrorDetails{} + if err := ttnredis.UnmarshalProto(s, details); err != nil { + return nil, nil, err + } + return nil, &storedAt, ttnpb.ErrorDetailsFromProto(details) + } + if marshalledDevice, ok := m[endDeviceMarker]; ok { + dev := new(ttnpb.EndDevice) + if err := ttnredis.UnmarshalProto(marshalledDevice, dev); err != nil { + return nil, nil, err + } + + return dev, &storedAt, nil + } + + return nil, nil, errCacheEntryMalformed +} + +// Set updates the end device by its identifiers. +func (r *EndDeviceCache) Set( + ctx context.Context, + ids *ttnpb.EndDeviceIdentifiers, + dev *ttnpb.EndDevice, + ttl time.Duration, +) error { + marshalledEndDevice, err := ttnredis.MarshalProto(dev) + if err != nil { + return err + } + pairs := []string{ + storedAtMarker, + fmt.Sprintf("%v", time.Now().UnixNano()), + endDeviceMarker, + marshalledEndDevice, + } + + uidKey := r.uidKey(unique.ID(ctx, ids)) + if _, err := r.Redis.Pipelined(ctx, func(p redis.Pipeliner) error { + p.Del(ctx, uidKey) + p.HSet(ctx, uidKey, pairs) + p.PExpire(ctx, uidKey, ttl) + return nil + }); err != nil { + return ttnredis.ConvertError(err) + } + return nil +} + +// Delete removes the end device by the identifiers. +func (r *EndDeviceCache) Delete(ctx context.Context, ids *ttnpb.EndDeviceIdentifiers) error { + uidKey := r.uidKey(unique.ID(ctx, ids)) + if err := r.Redis.Del(ctx, uidKey).Err(); err != nil { + return ttnredis.ConvertError(err) + } + return nil +} diff --git a/pkg/applicationserver/metadata/redis/end_device_cache_test.go b/pkg/applicationserver/metadata/redis/end_device_cache_test.go new file mode 100644 index 0000000000..5aed3ad45a --- /dev/null +++ b/pkg/applicationserver/metadata/redis/end_device_cache_test.go @@ -0,0 +1,107 @@ +// Copyright © 2025 The Things Network Foundation, The Things Industries B.V. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package redis_test + +import ( + "testing" + "time" + + "go.thethings.network/lorawan-stack/v3/pkg/applicationserver/metadata/redis" + "go.thethings.network/lorawan-stack/v3/pkg/errors" + "go.thethings.network/lorawan-stack/v3/pkg/ttnpb" + "go.thethings.network/lorawan-stack/v3/pkg/util/test" + "go.thethings.network/lorawan-stack/v3/pkg/util/test/assertions/should" +) + +var ( + attributesA = map[string]string{ + "attr1": "val1", + "attr2": "val2", + } + attributesB = map[string]string{ + "attr3": "val3", + } +) + +func TestEndDeviceCache(t *testing.T) { + t.Parallel() + + endDevice := &ttnpb.EndDevice{ + Ids: registeredEndDeviceIDs, + Attributes: attributesA, + } + + a, ctx := test.New(t) + cl, flush := test.NewRedis(ctx, "metadata_redis_test") + defer flush() + cache := redis.EndDeviceCache{Redis: cl} + + _, _, err := cache.Get(ctx, registeredEndDeviceIDs) + a.So(err, should.NotBeNil) + a.So(errors.IsNotFound(err), should.BeTrue) + + storeTime := time.Now() + err = cache.Set(ctx, registeredEndDeviceIDs, endDevice, Timeout) + a.So(err, should.BeNil) + + dev, storedAt, err := cache.Get(ctx, endDevice.Ids) + if a.So(err, should.BeNil) { + if a.So(storedAt, should.NotBeNil) { + a.So(*storedAt, should.HappenOnOrAfter, storeTime) + } + a.So(dev, should.Resemble, endDevice) + } + + endDevice.Attributes = attributesB + + storeTime = time.Now() + err = cache.Set(ctx, registeredEndDeviceIDs, endDevice, Timeout) + a.So(err, should.BeNil) + + dev, storedAt, err = cache.Get(ctx, registeredEndDeviceIDs) + if a.So(err, should.BeNil) { + if a.So(storedAt, should.NotBeNil) { + a.So(*storedAt, should.HappenOnOrAfter, storeTime) + } + a.So(dev, should.Resemble, endDevice) + } + + err = cache.Delete(ctx, registeredEndDeviceIDs) + a.So(err, should.BeNil) + + _, _, err = cache.Get(ctx, registeredEndDeviceIDs) + a.So(err, should.NotBeNil) + a.So(errors.IsNotFound(err), should.BeTrue) + + endDevice.Attributes = attributesA + + storeTime = time.Now() + err = cache.Set(ctx, registeredEndDeviceIDs, endDevice, Timeout) + a.So(err, should.BeNil) + + dev, storedAt, err = cache.Get(ctx, registeredEndDeviceIDs) + if a.So(err, should.BeNil) { + if a.So(storedAt, should.NotBeNil) { + a.So(*storedAt, should.HappenOnOrAfter, storeTime) + } + a.So(dev, should.Resemble, endDevice) + } + + time.Sleep(2 * Timeout) + + _, _, err = cache.Get(ctx, registeredEndDeviceIDs) + a.So(err, should.NotBeNil) + a.So(errors.IsNotFound(err), should.BeTrue) +} diff --git a/pkg/applicationserver/metadata/redis/location_cache.go b/pkg/applicationserver/metadata/redis/location_cache.go index c8221e109e..d27d6f6469 100644 --- a/pkg/applicationserver/metadata/redis/location_cache.go +++ b/pkg/applicationserver/metadata/redis/location_cache.go @@ -21,7 +21,6 @@ import ( "time" "github.com/redis/go-redis/v9" - "go.thethings.network/lorawan-stack/v3/pkg/errors" ttnredis "go.thethings.network/lorawan-stack/v3/pkg/redis" "go.thethings.network/lorawan-stack/v3/pkg/ttnpb" "go.thethings.network/lorawan-stack/v3/pkg/unique" @@ -36,17 +35,11 @@ func (r *EndDeviceLocationCache) uidKey(uid string) string { return r.Redis.Key("uid", uid) } -const ( - // storedAtMarker is used to store the timestamp of the last Set operation. - storedAtMarker = "_stored_at" - // errorMarker is used to store errors. - errorMarker = "_error" -) - -var errCacheMiss = errors.DefineNotFound("cache_miss", "cache miss") - // Get returns the locations by the end device identifiers. -func (r *EndDeviceLocationCache) Get(ctx context.Context, ids *ttnpb.EndDeviceIdentifiers) (map[string]*ttnpb.Location, *time.Time, error) { +func (r *EndDeviceLocationCache) Get( + ctx context.Context, + ids *ttnpb.EndDeviceIdentifiers, +) (map[string]*ttnpb.Location, *time.Time, error) { uidKey := r.uidKey(unique.ID(ctx, ids)) m, err := r.Redis.HGetAll(ctx, uidKey).Result() if err != nil { @@ -86,7 +79,12 @@ func (r *EndDeviceLocationCache) Get(ctx context.Context, ids *ttnpb.EndDeviceId } // Set updates the locations by the end device identifiers. -func (r *EndDeviceLocationCache) Set(ctx context.Context, ids *ttnpb.EndDeviceIdentifiers, update map[string]*ttnpb.Location, ttl time.Duration) error { +func (r *EndDeviceLocationCache) Set( + ctx context.Context, + ids *ttnpb.EndDeviceIdentifiers, + update map[string]*ttnpb.Location, + ttl time.Duration, +) error { pairs := append(make([]string, 0, 2*len(update)+2), storedAtMarker, fmt.Sprintf("%v", time.Now().UnixNano())) for k, v := range update { s, err := ttnredis.MarshalProto(v) diff --git a/pkg/applicationserver/metadata/redis/location_cache_test.go b/pkg/applicationserver/metadata/redis/location_cache_test.go index eee514b062..706e036da6 100644 --- a/pkg/applicationserver/metadata/redis/location_cache_test.go +++ b/pkg/applicationserver/metadata/redis/location_cache_test.go @@ -26,13 +26,6 @@ import ( ) var ( - registeredEndDeviceIDs = &ttnpb.EndDeviceIdentifiers{ - ApplicationIds: &ttnpb.ApplicationIdentifiers{ - ApplicationId: "foo", - }, - DeviceId: "bar", - } - locationA = map[string]*ttnpb.Location{ "foo": { Latitude: 123, @@ -48,13 +41,11 @@ var ( Latitude: 567, }, } - - errUnavailable = errors.DefineUnavailable("unavailable", "unavailable") - - Timeout = (1 << 8) * test.Delay ) func TestLocationCache(t *testing.T) { + t.Parallel() + a, ctx := test.New(t) cl, flush := test.NewRedis(ctx, "metadata_redis_test") defer flush() diff --git a/pkg/applicationserver/metadata/redis/redis.go b/pkg/applicationserver/metadata/redis/redis.go new file mode 100644 index 0000000000..b997c79759 --- /dev/null +++ b/pkg/applicationserver/metadata/redis/redis.go @@ -0,0 +1,37 @@ +// Copyright © 2025 The Things Network Foundation, The Things Industries B.V. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package redis provides a Redis-based cache for end device metadata. +package redis + +import ( + "go.thethings.network/lorawan-stack/v3/pkg/errors" +) + +const ( + // nolint: godot + // endDeviceMarker is used to store an end device. + endDeviceMarker = "_end_device" + // nolint: godot + // errorMarker is used to store errors. + errorMarker = "_error" + // nolint: godot + // storedAtMarker is used to store the timestamp of the last Set operation. + storedAtMarker = "_stored_at" +) + +var ( + errCacheEntryMalformed = errors.DefineCorruption("cache_entry_malformed", "cache entry malformed") + errCacheMiss = errors.DefineNotFound("cache_miss", "cache miss") +) diff --git a/pkg/applicationserver/metadata/redis/redis_util_test.go b/pkg/applicationserver/metadata/redis/redis_util_test.go new file mode 100644 index 0000000000..5991749652 --- /dev/null +++ b/pkg/applicationserver/metadata/redis/redis_util_test.go @@ -0,0 +1,31 @@ +// Copyright © 2025 The Things Network Foundation, The Things Industries B.V. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package redis_test + +import ( + "go.thethings.network/lorawan-stack/v3/pkg/ttnpb" + "go.thethings.network/lorawan-stack/v3/pkg/util/test" +) + +var ( + registeredEndDeviceIDs = &ttnpb.EndDeviceIdentifiers{ + ApplicationIds: &ttnpb.ApplicationIdentifiers{ + ApplicationId: "foo", + }, + DeviceId: "bar", + } + + Timeout = (1 << 8) * test.Delay +) diff --git a/pkg/networkserver/grpc_deviceregistry.go b/pkg/networkserver/grpc_deviceregistry.go index 756ca39e4c..d050c43a3f 100644 --- a/pkg/networkserver/grpc_deviceregistry.go +++ b/pkg/networkserver/grpc_deviceregistry.go @@ -3021,6 +3021,13 @@ func (ns *NetworkServer) Set(ctx context.Context, req *ttnpb.SetEndDeviceRequest "mac_state.pending_application_downlink.f_cnt", "mac_state.pending_application_downlink.f_port", "mac_state.pending_application_downlink.frm_payload", + "mac_state.pending_application_downlink.network_ids", + "mac_state.pending_application_downlink.network_ids.cluster_address", + "mac_state.pending_application_downlink.network_ids.cluster_id", + "mac_state.pending_application_downlink.network_ids.net_id", + "mac_state.pending_application_downlink.network_ids.ns_id", + "mac_state.pending_application_downlink.network_ids.tenant_address", + "mac_state.pending_application_downlink.network_ids.tenant_id", "mac_state.pending_application_downlink.priority", "mac_state.pending_application_downlink.session_key_id", "mac_state.pending_relay_downlink.raw_payload", diff --git a/pkg/ttnpb/applicationserver.pb.paths.fm.go b/pkg/ttnpb/applicationserver.pb.paths.fm.go index f3b7f770d2..9d5a558dea 100644 --- a/pkg/ttnpb/applicationserver.pb.paths.fm.go +++ b/pkg/ttnpb/applicationserver.pb.paths.fm.go @@ -99,6 +99,7 @@ var NsAsHandleUplinkRequestFieldPathsTopLevel = []string{ } var EncodeDownlinkRequestFieldPathsNested = []string{ "downlink", + "downlink.attributes", "downlink.class_b_c", "downlink.class_b_c.absolute_time", "downlink.class_b_c.gateways", @@ -112,8 +113,22 @@ var EncodeDownlinkRequestFieldPathsNested = []string{ "downlink.f_cnt", "downlink.f_port", "downlink.frm_payload", + "downlink.locations", + "downlink.network_ids", + "downlink.network_ids.cluster_address", + "downlink.network_ids.cluster_id", + "downlink.network_ids.net_id", + "downlink.network_ids.ns_id", + "downlink.network_ids.tenant_address", + "downlink.network_ids.tenant_id", "downlink.priority", "downlink.session_key_id", + "downlink.version_ids", + "downlink.version_ids.band_id", + "downlink.version_ids.brand_id", + "downlink.version_ids.firmware_version", + "downlink.version_ids.hardware_version", + "downlink.version_ids.model_id", "end_device_ids", "end_device_ids.application_ids", "end_device_ids.application_ids.application_id", @@ -140,6 +155,7 @@ var EncodeDownlinkRequestFieldPathsTopLevel = []string{ } var EncodeDownlinkResponseFieldPathsNested = []string{ "downlink", + "downlink.attributes", "downlink.class_b_c", "downlink.class_b_c.absolute_time", "downlink.class_b_c.gateways", @@ -153,8 +169,22 @@ var EncodeDownlinkResponseFieldPathsNested = []string{ "downlink.f_cnt", "downlink.f_port", "downlink.frm_payload", + "downlink.locations", + "downlink.network_ids", + "downlink.network_ids.cluster_address", + "downlink.network_ids.cluster_id", + "downlink.network_ids.net_id", + "downlink.network_ids.ns_id", + "downlink.network_ids.tenant_address", + "downlink.network_ids.tenant_id", "downlink.priority", "downlink.session_key_id", + "downlink.version_ids", + "downlink.version_ids.band_id", + "downlink.version_ids.brand_id", + "downlink.version_ids.firmware_version", + "downlink.version_ids.hardware_version", + "downlink.version_ids.model_id", } var EncodeDownlinkResponseFieldPathsTopLevel = []string{ @@ -175,6 +205,7 @@ var DecodeUplinkRequestFieldPathsNested = []string{ "uplink.app_s_key.encrypted_key", "uplink.app_s_key.kek_label", "uplink.app_s_key.key", + "uplink.attributes", "uplink.confirmed", "uplink.consumed_airtime", "uplink.decoded_payload", @@ -250,6 +281,7 @@ var DecodeUplinkResponseFieldPathsNested = []string{ "uplink.app_s_key.encrypted_key", "uplink.app_s_key.kek_label", "uplink.app_s_key.key", + "uplink.attributes", "uplink.confirmed", "uplink.consumed_airtime", "uplink.decoded_payload", @@ -311,6 +343,7 @@ var DecodeUplinkResponseFieldPathsTopLevel = []string{ } var DecodeDownlinkRequestFieldPathsNested = []string{ "downlink", + "downlink.attributes", "downlink.class_b_c", "downlink.class_b_c.absolute_time", "downlink.class_b_c.gateways", @@ -324,8 +357,22 @@ var DecodeDownlinkRequestFieldPathsNested = []string{ "downlink.f_cnt", "downlink.f_port", "downlink.frm_payload", + "downlink.locations", + "downlink.network_ids", + "downlink.network_ids.cluster_address", + "downlink.network_ids.cluster_id", + "downlink.network_ids.net_id", + "downlink.network_ids.ns_id", + "downlink.network_ids.tenant_address", + "downlink.network_ids.tenant_id", "downlink.priority", "downlink.session_key_id", + "downlink.version_ids", + "downlink.version_ids.band_id", + "downlink.version_ids.brand_id", + "downlink.version_ids.firmware_version", + "downlink.version_ids.hardware_version", + "downlink.version_ids.model_id", "end_device_ids", "end_device_ids.application_ids", "end_device_ids.application_ids.application_id", @@ -352,6 +399,7 @@ var DecodeDownlinkRequestFieldPathsTopLevel = []string{ } var DecodeDownlinkResponseFieldPathsNested = []string{ "downlink", + "downlink.attributes", "downlink.class_b_c", "downlink.class_b_c.absolute_time", "downlink.class_b_c.gateways", @@ -365,8 +413,22 @@ var DecodeDownlinkResponseFieldPathsNested = []string{ "downlink.f_cnt", "downlink.f_port", "downlink.frm_payload", + "downlink.locations", + "downlink.network_ids", + "downlink.network_ids.cluster_address", + "downlink.network_ids.cluster_id", + "downlink.network_ids.net_id", + "downlink.network_ids.ns_id", + "downlink.network_ids.tenant_address", + "downlink.network_ids.tenant_id", "downlink.priority", "downlink.session_key_id", + "downlink.version_ids", + "downlink.version_ids.band_id", + "downlink.version_ids.brand_id", + "downlink.version_ids.firmware_version", + "downlink.version_ids.hardware_version", + "downlink.version_ids.model_id", } var DecodeDownlinkResponseFieldPathsTopLevel = []string{ diff --git a/pkg/ttnpb/end_device.go b/pkg/ttnpb/end_device.go index 41985da6de..1e79c4d18c 100644 --- a/pkg/ttnpb/end_device.go +++ b/pkg/ttnpb/end_device.go @@ -1829,6 +1829,8 @@ func (v *MACState) FieldIsZero(p string) bool { return v.LorawanVersion == 0 case "pending_application_downlink": return v.PendingApplicationDownlink == nil + case "pending_application_downlink.attributes": + return v.PendingApplicationDownlink.FieldIsZero("attributes") case "pending_application_downlink.class_b_c": return v.PendingApplicationDownlink.FieldIsZero("class_b_c") case "pending_application_downlink.class_b_c.absolute_time": @@ -1855,10 +1857,38 @@ func (v *MACState) FieldIsZero(p string) bool { return v.PendingApplicationDownlink.FieldIsZero("f_port") case "pending_application_downlink.frm_payload": return v.PendingApplicationDownlink.FieldIsZero("frm_payload") + case "pending_application_downlink.locations": + return v.PendingApplicationDownlink.FieldIsZero("locations") + case "pending_application_downlink.network_ids": + return v.PendingApplicationDownlink.FieldIsZero("network_ids") + case "pending_application_downlink.network_ids.cluster_address": + return v.PendingApplicationDownlink.FieldIsZero("network_ids.cluster_address") + case "pending_application_downlink.network_ids.cluster_id": + return v.PendingApplicationDownlink.FieldIsZero("network_ids.cluster_id") + case "pending_application_downlink.network_ids.net_id": + return v.PendingApplicationDownlink.FieldIsZero("network_ids.net_id") + case "pending_application_downlink.network_ids.ns_id": + return v.PendingApplicationDownlink.FieldIsZero("network_ids.ns_id") + case "pending_application_downlink.network_ids.tenant_address": + return v.PendingApplicationDownlink.FieldIsZero("network_ids.tenant_address") + case "pending_application_downlink.network_ids.tenant_id": + return v.PendingApplicationDownlink.FieldIsZero("network_ids.tenant_id") case "pending_application_downlink.priority": return v.PendingApplicationDownlink.FieldIsZero("priority") case "pending_application_downlink.session_key_id": return v.PendingApplicationDownlink.FieldIsZero("session_key_id") + case "pending_application_downlink.version_ids": + return v.PendingApplicationDownlink.FieldIsZero("version_ids") + case "pending_application_downlink.version_ids.band_id": + return v.PendingApplicationDownlink.FieldIsZero("version_ids.band_id") + case "pending_application_downlink.version_ids.brand_id": + return v.PendingApplicationDownlink.FieldIsZero("version_ids.brand_id") + case "pending_application_downlink.version_ids.firmware_version": + return v.PendingApplicationDownlink.FieldIsZero("version_ids.firmware_version") + case "pending_application_downlink.version_ids.hardware_version": + return v.PendingApplicationDownlink.FieldIsZero("version_ids.hardware_version") + case "pending_application_downlink.version_ids.model_id": + return v.PendingApplicationDownlink.FieldIsZero("version_ids.model_id") case "pending_join_request": return v.PendingJoinRequest == nil case "pending_join_request.cf_list": @@ -2998,3 +3028,25 @@ func (d *EndDevice) UpdateTimestamps(src *EndDevice) { // EndDeviceFieldPathsNestedWithoutWrappers is the set of EndDevice nested paths without the wrapper paths. var EndDeviceFieldPathsNestedWithoutWrappers = FieldsWithoutWrappers(EndDeviceFieldPathsNested) + +// FieldIsZero returns whether path p is zero. +func (v *NetworkIdentifiers) FieldIsZero(p string) bool { + if v == nil { + return true + } + switch p { + case "cluster_address": + return v.ClusterAddress == "" + case "cluster_id": + return v.ClusterId == "" + case "net_id": + return len(v.NetId) == 0 + case "ns_id": + return len(v.NsId) == 0 + case "tenant_address": + return v.TenantAddress == "" + case "tenant_id": // nolint:goconst + return v.TenantId == "" + } + panic(fmt.Sprintf("unknown path '%s'", p)) +} diff --git a/pkg/ttnpb/end_device.pb.paths.fm.go b/pkg/ttnpb/end_device.pb.paths.fm.go index 089bfe8399..07447f5e95 100644 --- a/pkg/ttnpb/end_device.pb.paths.fm.go +++ b/pkg/ttnpb/end_device.pb.paths.fm.go @@ -1268,6 +1268,7 @@ var MACStateFieldPathsNested = []string{ "last_network_initiated_downlink_at", "lorawan_version", "pending_application_downlink", + "pending_application_downlink.attributes", "pending_application_downlink.class_b_c", "pending_application_downlink.class_b_c.absolute_time", "pending_application_downlink.class_b_c.gateways", @@ -1281,8 +1282,22 @@ var MACStateFieldPathsNested = []string{ "pending_application_downlink.f_cnt", "pending_application_downlink.f_port", "pending_application_downlink.frm_payload", + "pending_application_downlink.locations", + "pending_application_downlink.network_ids", + "pending_application_downlink.network_ids.cluster_address", + "pending_application_downlink.network_ids.cluster_id", + "pending_application_downlink.network_ids.net_id", + "pending_application_downlink.network_ids.ns_id", + "pending_application_downlink.network_ids.tenant_address", + "pending_application_downlink.network_ids.tenant_id", "pending_application_downlink.priority", "pending_application_downlink.session_key_id", + "pending_application_downlink.version_ids", + "pending_application_downlink.version_ids.band_id", + "pending_application_downlink.version_ids.brand_id", + "pending_application_downlink.version_ids.firmware_version", + "pending_application_downlink.version_ids.hardware_version", + "pending_application_downlink.version_ids.model_id", "pending_join_request", "pending_join_request.cf_list", "pending_join_request.cf_list.ch_masks", @@ -1758,6 +1773,7 @@ var EndDeviceFieldPathsNested = []string{ "mac_state.last_network_initiated_downlink_at", "mac_state.lorawan_version", "mac_state.pending_application_downlink", + "mac_state.pending_application_downlink.attributes", "mac_state.pending_application_downlink.class_b_c", "mac_state.pending_application_downlink.class_b_c.absolute_time", "mac_state.pending_application_downlink.class_b_c.gateways", @@ -1771,8 +1787,22 @@ var EndDeviceFieldPathsNested = []string{ "mac_state.pending_application_downlink.f_cnt", "mac_state.pending_application_downlink.f_port", "mac_state.pending_application_downlink.frm_payload", + "mac_state.pending_application_downlink.locations", + "mac_state.pending_application_downlink.network_ids", + "mac_state.pending_application_downlink.network_ids.cluster_address", + "mac_state.pending_application_downlink.network_ids.cluster_id", + "mac_state.pending_application_downlink.network_ids.net_id", + "mac_state.pending_application_downlink.network_ids.ns_id", + "mac_state.pending_application_downlink.network_ids.tenant_address", + "mac_state.pending_application_downlink.network_ids.tenant_id", "mac_state.pending_application_downlink.priority", "mac_state.pending_application_downlink.session_key_id", + "mac_state.pending_application_downlink.version_ids", + "mac_state.pending_application_downlink.version_ids.band_id", + "mac_state.pending_application_downlink.version_ids.brand_id", + "mac_state.pending_application_downlink.version_ids.firmware_version", + "mac_state.pending_application_downlink.version_ids.hardware_version", + "mac_state.pending_application_downlink.version_ids.model_id", "mac_state.pending_join_request", "mac_state.pending_join_request.cf_list", "mac_state.pending_join_request.cf_list.ch_masks", @@ -1974,6 +2004,7 @@ var EndDeviceFieldPathsNested = []string{ "pending_mac_state.last_network_initiated_downlink_at", "pending_mac_state.lorawan_version", "pending_mac_state.pending_application_downlink", + "pending_mac_state.pending_application_downlink.attributes", "pending_mac_state.pending_application_downlink.class_b_c", "pending_mac_state.pending_application_downlink.class_b_c.absolute_time", "pending_mac_state.pending_application_downlink.class_b_c.gateways", @@ -1987,8 +2018,22 @@ var EndDeviceFieldPathsNested = []string{ "pending_mac_state.pending_application_downlink.f_cnt", "pending_mac_state.pending_application_downlink.f_port", "pending_mac_state.pending_application_downlink.frm_payload", + "pending_mac_state.pending_application_downlink.locations", + "pending_mac_state.pending_application_downlink.network_ids", + "pending_mac_state.pending_application_downlink.network_ids.cluster_address", + "pending_mac_state.pending_application_downlink.network_ids.cluster_id", + "pending_mac_state.pending_application_downlink.network_ids.net_id", + "pending_mac_state.pending_application_downlink.network_ids.ns_id", + "pending_mac_state.pending_application_downlink.network_ids.tenant_address", + "pending_mac_state.pending_application_downlink.network_ids.tenant_id", "pending_mac_state.pending_application_downlink.priority", "pending_mac_state.pending_application_downlink.session_key_id", + "pending_mac_state.pending_application_downlink.version_ids", + "pending_mac_state.pending_application_downlink.version_ids.band_id", + "pending_mac_state.pending_application_downlink.version_ids.brand_id", + "pending_mac_state.pending_application_downlink.version_ids.firmware_version", + "pending_mac_state.pending_application_downlink.version_ids.hardware_version", + "pending_mac_state.pending_application_downlink.version_ids.model_id", "pending_mac_state.pending_join_request", "pending_mac_state.pending_join_request.cf_list", "pending_mac_state.pending_join_request.cf_list.ch_masks", @@ -2586,6 +2631,7 @@ var CreateEndDeviceRequestFieldPathsNested = []string{ "end_device.mac_state.last_network_initiated_downlink_at", "end_device.mac_state.lorawan_version", "end_device.mac_state.pending_application_downlink", + "end_device.mac_state.pending_application_downlink.attributes", "end_device.mac_state.pending_application_downlink.class_b_c", "end_device.mac_state.pending_application_downlink.class_b_c.absolute_time", "end_device.mac_state.pending_application_downlink.class_b_c.gateways", @@ -2599,8 +2645,22 @@ var CreateEndDeviceRequestFieldPathsNested = []string{ "end_device.mac_state.pending_application_downlink.f_cnt", "end_device.mac_state.pending_application_downlink.f_port", "end_device.mac_state.pending_application_downlink.frm_payload", + "end_device.mac_state.pending_application_downlink.locations", + "end_device.mac_state.pending_application_downlink.network_ids", + "end_device.mac_state.pending_application_downlink.network_ids.cluster_address", + "end_device.mac_state.pending_application_downlink.network_ids.cluster_id", + "end_device.mac_state.pending_application_downlink.network_ids.net_id", + "end_device.mac_state.pending_application_downlink.network_ids.ns_id", + "end_device.mac_state.pending_application_downlink.network_ids.tenant_address", + "end_device.mac_state.pending_application_downlink.network_ids.tenant_id", "end_device.mac_state.pending_application_downlink.priority", "end_device.mac_state.pending_application_downlink.session_key_id", + "end_device.mac_state.pending_application_downlink.version_ids", + "end_device.mac_state.pending_application_downlink.version_ids.band_id", + "end_device.mac_state.pending_application_downlink.version_ids.brand_id", + "end_device.mac_state.pending_application_downlink.version_ids.firmware_version", + "end_device.mac_state.pending_application_downlink.version_ids.hardware_version", + "end_device.mac_state.pending_application_downlink.version_ids.model_id", "end_device.mac_state.pending_join_request", "end_device.mac_state.pending_join_request.cf_list", "end_device.mac_state.pending_join_request.cf_list.ch_masks", @@ -2802,6 +2862,7 @@ var CreateEndDeviceRequestFieldPathsNested = []string{ "end_device.pending_mac_state.last_network_initiated_downlink_at", "end_device.pending_mac_state.lorawan_version", "end_device.pending_mac_state.pending_application_downlink", + "end_device.pending_mac_state.pending_application_downlink.attributes", "end_device.pending_mac_state.pending_application_downlink.class_b_c", "end_device.pending_mac_state.pending_application_downlink.class_b_c.absolute_time", "end_device.pending_mac_state.pending_application_downlink.class_b_c.gateways", @@ -2815,8 +2876,22 @@ var CreateEndDeviceRequestFieldPathsNested = []string{ "end_device.pending_mac_state.pending_application_downlink.f_cnt", "end_device.pending_mac_state.pending_application_downlink.f_port", "end_device.pending_mac_state.pending_application_downlink.frm_payload", + "end_device.pending_mac_state.pending_application_downlink.locations", + "end_device.pending_mac_state.pending_application_downlink.network_ids", + "end_device.pending_mac_state.pending_application_downlink.network_ids.cluster_address", + "end_device.pending_mac_state.pending_application_downlink.network_ids.cluster_id", + "end_device.pending_mac_state.pending_application_downlink.network_ids.net_id", + "end_device.pending_mac_state.pending_application_downlink.network_ids.ns_id", + "end_device.pending_mac_state.pending_application_downlink.network_ids.tenant_address", + "end_device.pending_mac_state.pending_application_downlink.network_ids.tenant_id", "end_device.pending_mac_state.pending_application_downlink.priority", "end_device.pending_mac_state.pending_application_downlink.session_key_id", + "end_device.pending_mac_state.pending_application_downlink.version_ids", + "end_device.pending_mac_state.pending_application_downlink.version_ids.band_id", + "end_device.pending_mac_state.pending_application_downlink.version_ids.brand_id", + "end_device.pending_mac_state.pending_application_downlink.version_ids.firmware_version", + "end_device.pending_mac_state.pending_application_downlink.version_ids.hardware_version", + "end_device.pending_mac_state.pending_application_downlink.version_ids.model_id", "end_device.pending_mac_state.pending_join_request", "end_device.pending_mac_state.pending_join_request.cf_list", "end_device.pending_mac_state.pending_join_request.cf_list.ch_masks", @@ -3346,6 +3421,7 @@ var UpdateEndDeviceRequestFieldPathsNested = []string{ "end_device.mac_state.last_network_initiated_downlink_at", "end_device.mac_state.lorawan_version", "end_device.mac_state.pending_application_downlink", + "end_device.mac_state.pending_application_downlink.attributes", "end_device.mac_state.pending_application_downlink.class_b_c", "end_device.mac_state.pending_application_downlink.class_b_c.absolute_time", "end_device.mac_state.pending_application_downlink.class_b_c.gateways", @@ -3359,8 +3435,22 @@ var UpdateEndDeviceRequestFieldPathsNested = []string{ "end_device.mac_state.pending_application_downlink.f_cnt", "end_device.mac_state.pending_application_downlink.f_port", "end_device.mac_state.pending_application_downlink.frm_payload", + "end_device.mac_state.pending_application_downlink.locations", + "end_device.mac_state.pending_application_downlink.network_ids", + "end_device.mac_state.pending_application_downlink.network_ids.cluster_address", + "end_device.mac_state.pending_application_downlink.network_ids.cluster_id", + "end_device.mac_state.pending_application_downlink.network_ids.net_id", + "end_device.mac_state.pending_application_downlink.network_ids.ns_id", + "end_device.mac_state.pending_application_downlink.network_ids.tenant_address", + "end_device.mac_state.pending_application_downlink.network_ids.tenant_id", "end_device.mac_state.pending_application_downlink.priority", "end_device.mac_state.pending_application_downlink.session_key_id", + "end_device.mac_state.pending_application_downlink.version_ids", + "end_device.mac_state.pending_application_downlink.version_ids.band_id", + "end_device.mac_state.pending_application_downlink.version_ids.brand_id", + "end_device.mac_state.pending_application_downlink.version_ids.firmware_version", + "end_device.mac_state.pending_application_downlink.version_ids.hardware_version", + "end_device.mac_state.pending_application_downlink.version_ids.model_id", "end_device.mac_state.pending_join_request", "end_device.mac_state.pending_join_request.cf_list", "end_device.mac_state.pending_join_request.cf_list.ch_masks", @@ -3562,6 +3652,7 @@ var UpdateEndDeviceRequestFieldPathsNested = []string{ "end_device.pending_mac_state.last_network_initiated_downlink_at", "end_device.pending_mac_state.lorawan_version", "end_device.pending_mac_state.pending_application_downlink", + "end_device.pending_mac_state.pending_application_downlink.attributes", "end_device.pending_mac_state.pending_application_downlink.class_b_c", "end_device.pending_mac_state.pending_application_downlink.class_b_c.absolute_time", "end_device.pending_mac_state.pending_application_downlink.class_b_c.gateways", @@ -3575,8 +3666,22 @@ var UpdateEndDeviceRequestFieldPathsNested = []string{ "end_device.pending_mac_state.pending_application_downlink.f_cnt", "end_device.pending_mac_state.pending_application_downlink.f_port", "end_device.pending_mac_state.pending_application_downlink.frm_payload", + "end_device.pending_mac_state.pending_application_downlink.locations", + "end_device.pending_mac_state.pending_application_downlink.network_ids", + "end_device.pending_mac_state.pending_application_downlink.network_ids.cluster_address", + "end_device.pending_mac_state.pending_application_downlink.network_ids.cluster_id", + "end_device.pending_mac_state.pending_application_downlink.network_ids.net_id", + "end_device.pending_mac_state.pending_application_downlink.network_ids.ns_id", + "end_device.pending_mac_state.pending_application_downlink.network_ids.tenant_address", + "end_device.pending_mac_state.pending_application_downlink.network_ids.tenant_id", "end_device.pending_mac_state.pending_application_downlink.priority", "end_device.pending_mac_state.pending_application_downlink.session_key_id", + "end_device.pending_mac_state.pending_application_downlink.version_ids", + "end_device.pending_mac_state.pending_application_downlink.version_ids.band_id", + "end_device.pending_mac_state.pending_application_downlink.version_ids.brand_id", + "end_device.pending_mac_state.pending_application_downlink.version_ids.firmware_version", + "end_device.pending_mac_state.pending_application_downlink.version_ids.hardware_version", + "end_device.pending_mac_state.pending_application_downlink.version_ids.model_id", "end_device.pending_mac_state.pending_join_request", "end_device.pending_mac_state.pending_join_request.cf_list", "end_device.pending_mac_state.pending_join_request.cf_list.ch_masks", @@ -4157,6 +4262,7 @@ var SetEndDeviceRequestFieldPathsNested = []string{ "end_device.mac_state.last_network_initiated_downlink_at", "end_device.mac_state.lorawan_version", "end_device.mac_state.pending_application_downlink", + "end_device.mac_state.pending_application_downlink.attributes", "end_device.mac_state.pending_application_downlink.class_b_c", "end_device.mac_state.pending_application_downlink.class_b_c.absolute_time", "end_device.mac_state.pending_application_downlink.class_b_c.gateways", @@ -4170,8 +4276,22 @@ var SetEndDeviceRequestFieldPathsNested = []string{ "end_device.mac_state.pending_application_downlink.f_cnt", "end_device.mac_state.pending_application_downlink.f_port", "end_device.mac_state.pending_application_downlink.frm_payload", + "end_device.mac_state.pending_application_downlink.locations", + "end_device.mac_state.pending_application_downlink.network_ids", + "end_device.mac_state.pending_application_downlink.network_ids.cluster_address", + "end_device.mac_state.pending_application_downlink.network_ids.cluster_id", + "end_device.mac_state.pending_application_downlink.network_ids.net_id", + "end_device.mac_state.pending_application_downlink.network_ids.ns_id", + "end_device.mac_state.pending_application_downlink.network_ids.tenant_address", + "end_device.mac_state.pending_application_downlink.network_ids.tenant_id", "end_device.mac_state.pending_application_downlink.priority", "end_device.mac_state.pending_application_downlink.session_key_id", + "end_device.mac_state.pending_application_downlink.version_ids", + "end_device.mac_state.pending_application_downlink.version_ids.band_id", + "end_device.mac_state.pending_application_downlink.version_ids.brand_id", + "end_device.mac_state.pending_application_downlink.version_ids.firmware_version", + "end_device.mac_state.pending_application_downlink.version_ids.hardware_version", + "end_device.mac_state.pending_application_downlink.version_ids.model_id", "end_device.mac_state.pending_join_request", "end_device.mac_state.pending_join_request.cf_list", "end_device.mac_state.pending_join_request.cf_list.ch_masks", @@ -4373,6 +4493,7 @@ var SetEndDeviceRequestFieldPathsNested = []string{ "end_device.pending_mac_state.last_network_initiated_downlink_at", "end_device.pending_mac_state.lorawan_version", "end_device.pending_mac_state.pending_application_downlink", + "end_device.pending_mac_state.pending_application_downlink.attributes", "end_device.pending_mac_state.pending_application_downlink.class_b_c", "end_device.pending_mac_state.pending_application_downlink.class_b_c.absolute_time", "end_device.pending_mac_state.pending_application_downlink.class_b_c.gateways", @@ -4386,8 +4507,22 @@ var SetEndDeviceRequestFieldPathsNested = []string{ "end_device.pending_mac_state.pending_application_downlink.f_cnt", "end_device.pending_mac_state.pending_application_downlink.f_port", "end_device.pending_mac_state.pending_application_downlink.frm_payload", + "end_device.pending_mac_state.pending_application_downlink.locations", + "end_device.pending_mac_state.pending_application_downlink.network_ids", + "end_device.pending_mac_state.pending_application_downlink.network_ids.cluster_address", + "end_device.pending_mac_state.pending_application_downlink.network_ids.cluster_id", + "end_device.pending_mac_state.pending_application_downlink.network_ids.net_id", + "end_device.pending_mac_state.pending_application_downlink.network_ids.ns_id", + "end_device.pending_mac_state.pending_application_downlink.network_ids.tenant_address", + "end_device.pending_mac_state.pending_application_downlink.network_ids.tenant_id", "end_device.pending_mac_state.pending_application_downlink.priority", "end_device.pending_mac_state.pending_application_downlink.session_key_id", + "end_device.pending_mac_state.pending_application_downlink.version_ids", + "end_device.pending_mac_state.pending_application_downlink.version_ids.band_id", + "end_device.pending_mac_state.pending_application_downlink.version_ids.brand_id", + "end_device.pending_mac_state.pending_application_downlink.version_ids.firmware_version", + "end_device.pending_mac_state.pending_application_downlink.version_ids.hardware_version", + "end_device.pending_mac_state.pending_application_downlink.version_ids.model_id", "end_device.pending_mac_state.pending_join_request", "end_device.pending_mac_state.pending_join_request.cf_list", "end_device.pending_mac_state.pending_join_request.cf_list.ch_masks", @@ -4934,6 +5069,7 @@ var EndDeviceTemplateFieldPathsNested = []string{ "end_device.mac_state.last_network_initiated_downlink_at", "end_device.mac_state.lorawan_version", "end_device.mac_state.pending_application_downlink", + "end_device.mac_state.pending_application_downlink.attributes", "end_device.mac_state.pending_application_downlink.class_b_c", "end_device.mac_state.pending_application_downlink.class_b_c.absolute_time", "end_device.mac_state.pending_application_downlink.class_b_c.gateways", @@ -4947,8 +5083,22 @@ var EndDeviceTemplateFieldPathsNested = []string{ "end_device.mac_state.pending_application_downlink.f_cnt", "end_device.mac_state.pending_application_downlink.f_port", "end_device.mac_state.pending_application_downlink.frm_payload", + "end_device.mac_state.pending_application_downlink.locations", + "end_device.mac_state.pending_application_downlink.network_ids", + "end_device.mac_state.pending_application_downlink.network_ids.cluster_address", + "end_device.mac_state.pending_application_downlink.network_ids.cluster_id", + "end_device.mac_state.pending_application_downlink.network_ids.net_id", + "end_device.mac_state.pending_application_downlink.network_ids.ns_id", + "end_device.mac_state.pending_application_downlink.network_ids.tenant_address", + "end_device.mac_state.pending_application_downlink.network_ids.tenant_id", "end_device.mac_state.pending_application_downlink.priority", "end_device.mac_state.pending_application_downlink.session_key_id", + "end_device.mac_state.pending_application_downlink.version_ids", + "end_device.mac_state.pending_application_downlink.version_ids.band_id", + "end_device.mac_state.pending_application_downlink.version_ids.brand_id", + "end_device.mac_state.pending_application_downlink.version_ids.firmware_version", + "end_device.mac_state.pending_application_downlink.version_ids.hardware_version", + "end_device.mac_state.pending_application_downlink.version_ids.model_id", "end_device.mac_state.pending_join_request", "end_device.mac_state.pending_join_request.cf_list", "end_device.mac_state.pending_join_request.cf_list.ch_masks", @@ -5150,6 +5300,7 @@ var EndDeviceTemplateFieldPathsNested = []string{ "end_device.pending_mac_state.last_network_initiated_downlink_at", "end_device.pending_mac_state.lorawan_version", "end_device.pending_mac_state.pending_application_downlink", + "end_device.pending_mac_state.pending_application_downlink.attributes", "end_device.pending_mac_state.pending_application_downlink.class_b_c", "end_device.pending_mac_state.pending_application_downlink.class_b_c.absolute_time", "end_device.pending_mac_state.pending_application_downlink.class_b_c.gateways", @@ -5163,8 +5314,22 @@ var EndDeviceTemplateFieldPathsNested = []string{ "end_device.pending_mac_state.pending_application_downlink.f_cnt", "end_device.pending_mac_state.pending_application_downlink.f_port", "end_device.pending_mac_state.pending_application_downlink.frm_payload", + "end_device.pending_mac_state.pending_application_downlink.locations", + "end_device.pending_mac_state.pending_application_downlink.network_ids", + "end_device.pending_mac_state.pending_application_downlink.network_ids.cluster_address", + "end_device.pending_mac_state.pending_application_downlink.network_ids.cluster_id", + "end_device.pending_mac_state.pending_application_downlink.network_ids.net_id", + "end_device.pending_mac_state.pending_application_downlink.network_ids.ns_id", + "end_device.pending_mac_state.pending_application_downlink.network_ids.tenant_address", + "end_device.pending_mac_state.pending_application_downlink.network_ids.tenant_id", "end_device.pending_mac_state.pending_application_downlink.priority", "end_device.pending_mac_state.pending_application_downlink.session_key_id", + "end_device.pending_mac_state.pending_application_downlink.version_ids", + "end_device.pending_mac_state.pending_application_downlink.version_ids.band_id", + "end_device.pending_mac_state.pending_application_downlink.version_ids.brand_id", + "end_device.pending_mac_state.pending_application_downlink.version_ids.firmware_version", + "end_device.pending_mac_state.pending_application_downlink.version_ids.hardware_version", + "end_device.pending_mac_state.pending_application_downlink.version_ids.model_id", "end_device.pending_mac_state.pending_join_request", "end_device.pending_mac_state.pending_join_request.cf_list", "end_device.pending_mac_state.pending_join_request.cf_list.ch_masks", diff --git a/pkg/ttnpb/field_mask_validation.go b/pkg/ttnpb/field_mask_validation.go index d07e53d09a..42e9acdebc 100644 --- a/pkg/ttnpb/field_mask_validation.go +++ b/pkg/ttnpb/field_mask_validation.go @@ -439,6 +439,7 @@ var nsEndDeviceReadFieldPaths = [...]string{ "mac_state.last_network_initiated_downlink_at", "mac_state.lorawan_version", "mac_state.pending_application_downlink", + "mac_state.pending_application_downlink.attributes", "mac_state.pending_application_downlink.class_b_c", "mac_state.pending_application_downlink.class_b_c.absolute_time", "mac_state.pending_application_downlink.class_b_c.gateways", @@ -447,8 +448,22 @@ var nsEndDeviceReadFieldPaths = [...]string{ "mac_state.pending_application_downlink.f_cnt", "mac_state.pending_application_downlink.f_port", "mac_state.pending_application_downlink.frm_payload", + "mac_state.pending_application_downlink.locations", + "mac_state.pending_application_downlink.network_ids", + "mac_state.pending_application_downlink.network_ids.cluster_address", + "mac_state.pending_application_downlink.network_ids.cluster_id", + "mac_state.pending_application_downlink.network_ids.net_id", + "mac_state.pending_application_downlink.network_ids.ns_id", + "mac_state.pending_application_downlink.network_ids.tenant_address", + "mac_state.pending_application_downlink.network_ids.tenant_id", "mac_state.pending_application_downlink.priority", "mac_state.pending_application_downlink.session_key_id", + "mac_state.pending_application_downlink.version_ids", + "mac_state.pending_application_downlink.version_ids.band_id", + "mac_state.pending_application_downlink.version_ids.brand_id", + "mac_state.pending_application_downlink.version_ids.firmware_version", + "mac_state.pending_application_downlink.version_ids.hardware_version", + "mac_state.pending_application_downlink.version_ids.model_id", "mac_state.pending_relay_downlink", "mac_state.pending_relay_downlink.raw_payload", "mac_state.pending_requests", @@ -1358,6 +1373,13 @@ var RPCFieldMaskPaths = map[string]RPCFieldMaskPathValue{ "mac_state.pending_application_downlink.f_cnt", "mac_state.pending_application_downlink.f_port", "mac_state.pending_application_downlink.frm_payload", + "mac_state.pending_application_downlink.network_ids", + "mac_state.pending_application_downlink.network_ids.cluster_address", + "mac_state.pending_application_downlink.network_ids.cluster_id", + "mac_state.pending_application_downlink.network_ids.net_id", + "mac_state.pending_application_downlink.network_ids.ns_id", + "mac_state.pending_application_downlink.network_ids.tenant_address", + "mac_state.pending_application_downlink.network_ids.tenant_id", "mac_state.pending_application_downlink.priority", "mac_state.pending_application_downlink.session_key_id", "mac_state.pending_relay_downlink", diff --git a/pkg/ttnpb/messages.go b/pkg/ttnpb/messages.go index d7cfc2a436..18e06b66b5 100644 --- a/pkg/ttnpb/messages.go +++ b/pkg/ttnpb/messages.go @@ -71,6 +71,8 @@ func (v *ApplicationDownlink) FieldIsZero(p string) bool { return true } switch p { + case "attributes": + return v.Attributes == nil case "class_b_c": return v.ClassBC == nil case "class_b_c.absolute_time": @@ -79,6 +81,12 @@ func (v *ApplicationDownlink) FieldIsZero(p string) bool { return v.ClassBC.FieldIsZero("gateways") case "confirmed": return !v.Confirmed + case "confirmed_retry": + return v.ConfirmedRetry == nil + case "confirmed_retry.attempt": + return v.ConfirmedRetry.FieldIsZero("attempt") + case "confirmed_retry.max_attempts": + return v.ConfirmedRetry.FieldIsZero("max_attempts") case "correlation_ids": return v.CorrelationIds == nil case "decoded_payload": @@ -91,16 +99,38 @@ func (v *ApplicationDownlink) FieldIsZero(p string) bool { return v.FPort == 0 case "frm_payload": return v.FrmPayload == nil + case "locations": + return v.Locations == nil + case "network_ids": // nolint: goconst + return v.NetworkIds == nil + case "network_ids.cluster_address": + return v.NetworkIds.FieldIsZero("cluster_address") + case "network_ids.cluster_id": + return v.NetworkIds.FieldIsZero("cluster_id") + case "network_ids.net_id": + return v.NetworkIds.FieldIsZero("net_id") + case "network_ids.ns_id": + return v.NetworkIds.FieldIsZero("ns_id") + case "network_ids.tenant_address": + return v.NetworkIds.FieldIsZero("tenant_address") + case "network_ids.tenant_id": + return v.NetworkIds.FieldIsZero("tenant_id") case "priority": return v.Priority == 0 case "session_key_id": return v.SessionKeyId == nil - case "confirmed_retry": - return v.ConfirmedRetry == nil - case "confirmed_retry.attempt": - return v.ConfirmedRetry.FieldIsZero("attempt") - case "confirmed_retry.max_attempts": - return v.ConfirmedRetry.FieldIsZero("max_attempts") + case "version_ids": + return v.VersionIds == nil + case "version_ids.band_id": + return v.VersionIds.FieldIsZero("band_id") + case "version_ids.brand_id": + return v.VersionIds.FieldIsZero("brand_id") + case "version_ids.firmware_version": + return v.VersionIds.FieldIsZero("firmware_version") + case "version_ids.hardware_version": + return v.VersionIds.FieldIsZero("hardware_version") + case "version_ids.model_id": + return v.VersionIds.FieldIsZero("model_id") } panic(fmt.Sprintf("unknown path '%s'", p)) } diff --git a/pkg/ttnpb/messages.pb.go b/pkg/ttnpb/messages.pb.go index 0d65cc1b9f..22323c0fb5 100644 --- a/pkg/ttnpb/messages.pb.go +++ b/pkg/ttnpb/messages.pb.go @@ -715,6 +715,8 @@ type ApplicationUplink struct { // Received via the DevStatus MAC command at last_dev_status_received_at or earlier. // Set by the Network Server while handling the message. LastBatteryPercentage *LastBatteryPercentage `protobuf:"bytes,20,opt,name=last_battery_percentage,json=lastBatteryPercentage,proto3" json:"last_battery_percentage,omitempty"` + // Attributes for devices, set by the Application Server while handling the message. + Attributes map[string]string `protobuf:"bytes,21,rep,name=attributes,proto3" json:"attributes,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` } func (x *ApplicationUplink) Reset() { @@ -889,6 +891,13 @@ func (x *ApplicationUplink) GetLastBatteryPercentage() *LastBatteryPercentage { return nil } +func (x *ApplicationUplink) GetAttributes() map[string]string { + if x != nil { + return x.Attributes + } + return nil +} + type ApplicationUplinkNormalized struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -924,6 +933,8 @@ type ApplicationUplinkNormalized struct { VersionIds *EndDeviceVersionIdentifiers `protobuf:"bytes,13,opt,name=version_ids,json=versionIds,proto3" json:"version_ids,omitempty"` // Network identifiers, set by the Network Server that handles the message. NetworkIds *NetworkIdentifiers `protobuf:"bytes,14,opt,name=network_ids,json=networkIds,proto3" json:"network_ids,omitempty"` + // Attributes for devices, set by the Application Server while handling the message. + Attributes map[string]string `protobuf:"bytes,15,rep,name=attributes,proto3" json:"attributes,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` } func (x *ApplicationUplinkNormalized) Reset() { @@ -1056,6 +1067,13 @@ func (x *ApplicationUplinkNormalized) GetNetworkIds() *NetworkIdentifiers { return nil } +func (x *ApplicationUplinkNormalized) GetAttributes() map[string]string { + if x != nil { + return x.Attributes + } + return nil +} + type ApplicationLocation struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1135,6 +1153,14 @@ type ApplicationJoinAccept struct { PendingSession bool `protobuf:"varint,4,opt,name=pending_session,json=pendingSession,proto3" json:"pending_session,omitempty"` // Server time when the Network Server received the message. ReceivedAt *timestamppb.Timestamp `protobuf:"bytes,8,opt,name=received_at,json=receivedAt,proto3" json:"received_at,omitempty"` + // End device location metadata, set by the Application Server while handling the message. + Locations map[string]*Location `protobuf:"bytes,9,rep,name=locations,proto3" json:"locations,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + // End device version identifiers, set by the Application Server while handling the message. + VersionIds *EndDeviceVersionIdentifiers `protobuf:"bytes,10,opt,name=version_ids,json=versionIds,proto3" json:"version_ids,omitempty"` + // Network identifiers, set by the Network Server that handles the message. + NetworkIds *NetworkIdentifiers `protobuf:"bytes,11,opt,name=network_ids,json=networkIds,proto3" json:"network_ids,omitempty"` + // Attributes for devices, set by the Application Server while handling the message. + Attributes map[string]string `protobuf:"bytes,12,rep,name=attributes,proto3" json:"attributes,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` } func (x *ApplicationJoinAccept) Reset() { @@ -1204,6 +1230,34 @@ func (x *ApplicationJoinAccept) GetReceivedAt() *timestamppb.Timestamp { return nil } +func (x *ApplicationJoinAccept) GetLocations() map[string]*Location { + if x != nil { + return x.Locations + } + return nil +} + +func (x *ApplicationJoinAccept) GetVersionIds() *EndDeviceVersionIdentifiers { + if x != nil { + return x.VersionIds + } + return nil +} + +func (x *ApplicationJoinAccept) GetNetworkIds() *NetworkIdentifiers { + if x != nil { + return x.NetworkIds + } + return nil +} + +func (x *ApplicationJoinAccept) GetAttributes() map[string]string { + if x != nil { + return x.Attributes + } + return nil +} + type ApplicationDownlink struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1233,6 +1287,14 @@ type ApplicationDownlink struct { Priority TxSchedulePriority `protobuf:"varint,8,opt,name=priority,proto3,enum=ttn.lorawan.v3.TxSchedulePriority" json:"priority,omitempty"` CorrelationIds []string `protobuf:"bytes,9,rep,name=correlation_ids,json=correlationIds,proto3" json:"correlation_ids,omitempty"` ConfirmedRetry *ApplicationDownlink_ConfirmedRetry `protobuf:"bytes,11,opt,name=confirmed_retry,json=confirmedRetry,proto3" json:"confirmed_retry,omitempty"` + // End device location metadata, set by the Application Server while handling the message. + Locations map[string]*Location `protobuf:"bytes,12,rep,name=locations,proto3" json:"locations,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + // End device version identifiers, set by the Application Server while handling the message. + VersionIds *EndDeviceVersionIdentifiers `protobuf:"bytes,13,opt,name=version_ids,json=versionIds,proto3" json:"version_ids,omitempty"` + // Network identifiers, set by the Network Server that handles the message. + NetworkIds *NetworkIdentifiers `protobuf:"bytes,14,opt,name=network_ids,json=networkIds,proto3" json:"network_ids,omitempty"` + // Attributes for devices, set by the Application Server while handling the message. + Attributes map[string]string `protobuf:"bytes,15,rep,name=attributes,proto3" json:"attributes,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` } func (x *ApplicationDownlink) Reset() { @@ -1344,6 +1406,34 @@ func (x *ApplicationDownlink) GetConfirmedRetry() *ApplicationDownlink_Confirmed return nil } +func (x *ApplicationDownlink) GetLocations() map[string]*Location { + if x != nil { + return x.Locations + } + return nil +} + +func (x *ApplicationDownlink) GetVersionIds() *EndDeviceVersionIdentifiers { + if x != nil { + return x.VersionIds + } + return nil +} + +func (x *ApplicationDownlink) GetNetworkIds() *NetworkIdentifiers { + if x != nil { + return x.NetworkIds + } + return nil +} + +func (x *ApplicationDownlink) GetAttributes() map[string]string { + if x != nil { + return x.Attributes + } + return nil +} + type ApplicationDownlinks struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1398,6 +1488,14 @@ type ApplicationDownlinkFailed struct { Downlink *ApplicationDownlink `protobuf:"bytes,1,opt,name=downlink,proto3" json:"downlink,omitempty"` Error *ErrorDetails `protobuf:"bytes,2,opt,name=error,proto3" json:"error,omitempty"` + // End device location metadata, set by the Application Server while handling the message. + Locations map[string]*Location `protobuf:"bytes,3,rep,name=locations,proto3" json:"locations,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + // End device version identifiers, set by the Application Server while handling the message. + VersionIds *EndDeviceVersionIdentifiers `protobuf:"bytes,4,opt,name=version_ids,json=versionIds,proto3" json:"version_ids,omitempty"` + // Network identifiers, set by the Network Server that handles the message. + NetworkIds *NetworkIdentifiers `protobuf:"bytes,5,opt,name=network_ids,json=networkIds,proto3" json:"network_ids,omitempty"` + // Attributes for devices, set by the Application Server while handling the message. + Attributes map[string]string `protobuf:"bytes,6,rep,name=attributes,proto3" json:"attributes,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` } func (x *ApplicationDownlinkFailed) Reset() { @@ -1446,6 +1544,34 @@ func (x *ApplicationDownlinkFailed) GetError() *ErrorDetails { return nil } +func (x *ApplicationDownlinkFailed) GetLocations() map[string]*Location { + if x != nil { + return x.Locations + } + return nil +} + +func (x *ApplicationDownlinkFailed) GetVersionIds() *EndDeviceVersionIdentifiers { + if x != nil { + return x.VersionIds + } + return nil +} + +func (x *ApplicationDownlinkFailed) GetNetworkIds() *NetworkIdentifiers { + if x != nil { + return x.NetworkIds + } + return nil +} + +func (x *ApplicationDownlinkFailed) GetAttributes() map[string]string { + if x != nil { + return x.Attributes + } + return nil +} + type ApplicationInvalidatedDownlinks struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1454,6 +1580,14 @@ type ApplicationInvalidatedDownlinks struct { Downlinks []*ApplicationDownlink `protobuf:"bytes,1,rep,name=downlinks,proto3" json:"downlinks,omitempty"` LastFCntDown uint32 `protobuf:"varint,2,opt,name=last_f_cnt_down,json=lastFCntDown,proto3" json:"last_f_cnt_down,omitempty"` SessionKeyId []byte `protobuf:"bytes,3,opt,name=session_key_id,json=sessionKeyId,proto3" json:"session_key_id,omitempty"` + // End device location metadata, set by the Application Server while handling the message. + Locations map[string]*Location `protobuf:"bytes,4,rep,name=locations,proto3" json:"locations,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + // End device version identifiers, set by the Application Server while handling the message. + VersionIds *EndDeviceVersionIdentifiers `protobuf:"bytes,5,opt,name=version_ids,json=versionIds,proto3" json:"version_ids,omitempty"` + // Network identifiers, set by the Network Server that handles the message. + NetworkIds *NetworkIdentifiers `protobuf:"bytes,6,opt,name=network_ids,json=networkIds,proto3" json:"network_ids,omitempty"` + // Attributes for devices, set by the Application Server while handling the message. + Attributes map[string]string `protobuf:"bytes,7,rep,name=attributes,proto3" json:"attributes,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` } func (x *ApplicationInvalidatedDownlinks) Reset() { @@ -1509,6 +1643,34 @@ func (x *ApplicationInvalidatedDownlinks) GetSessionKeyId() []byte { return nil } +func (x *ApplicationInvalidatedDownlinks) GetLocations() map[string]*Location { + if x != nil { + return x.Locations + } + return nil +} + +func (x *ApplicationInvalidatedDownlinks) GetVersionIds() *EndDeviceVersionIdentifiers { + if x != nil { + return x.VersionIds + } + return nil +} + +func (x *ApplicationInvalidatedDownlinks) GetNetworkIds() *NetworkIdentifiers { + if x != nil { + return x.NetworkIds + } + return nil +} + +func (x *ApplicationInvalidatedDownlinks) GetAttributes() map[string]string { + if x != nil { + return x.Attributes + } + return nil +} + type DownlinkQueueOperationErrorDetails struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1603,6 +1765,14 @@ type ApplicationServiceData struct { Service string `protobuf:"bytes,1,opt,name=service,proto3" json:"service,omitempty"` Data *structpb.Struct `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"` + // End device location metadata, set by the Application Server while handling the message. + Locations map[string]*Location `protobuf:"bytes,3,rep,name=locations,proto3" json:"locations,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + // End device version identifiers, set by the Application Server while handling the message. + VersionIds *EndDeviceVersionIdentifiers `protobuf:"bytes,4,opt,name=version_ids,json=versionIds,proto3" json:"version_ids,omitempty"` + // Network identifiers, set by the Network Server that handles the message. + NetworkIds *NetworkIdentifiers `protobuf:"bytes,5,opt,name=network_ids,json=networkIds,proto3" json:"network_ids,omitempty"` + // Attributes for devices, set by the Application Server while handling the message. + Attributes map[string]string `protobuf:"bytes,6,rep,name=attributes,proto3" json:"attributes,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` } func (x *ApplicationServiceData) Reset() { @@ -1651,6 +1821,34 @@ func (x *ApplicationServiceData) GetData() *structpb.Struct { return nil } +func (x *ApplicationServiceData) GetLocations() map[string]*Location { + if x != nil { + return x.Locations + } + return nil +} + +func (x *ApplicationServiceData) GetVersionIds() *EndDeviceVersionIdentifiers { + if x != nil { + return x.VersionIds + } + return nil +} + +func (x *ApplicationServiceData) GetNetworkIds() *NetworkIdentifiers { + if x != nil { + return x.NetworkIds + } + return nil +} + +func (x *ApplicationServiceData) GetAttributes() map[string]string { + if x != nil { + return x.Attributes + } + return nil +} + // Application uplink message. type ApplicationUp struct { state protoimpl.MessageState @@ -2044,7 +2242,7 @@ type ApplicationDownlink_ClassBC struct { func (x *ApplicationDownlink_ClassBC) Reset() { *x = ApplicationDownlink_ClassBC{} if protoimpl.UnsafeEnabled { - mi := &file_ttn_lorawan_v3_messages_proto_msgTypes[22] + mi := &file_ttn_lorawan_v3_messages_proto_msgTypes[26] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2057,7 +2255,7 @@ func (x *ApplicationDownlink_ClassBC) String() string { func (*ApplicationDownlink_ClassBC) ProtoMessage() {} func (x *ApplicationDownlink_ClassBC) ProtoReflect() protoreflect.Message { - mi := &file_ttn_lorawan_v3_messages_proto_msgTypes[22] + mi := &file_ttn_lorawan_v3_messages_proto_msgTypes[26] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2102,7 +2300,7 @@ type ApplicationDownlink_ConfirmedRetry struct { func (x *ApplicationDownlink_ConfirmedRetry) Reset() { *x = ApplicationDownlink_ConfirmedRetry{} if protoimpl.UnsafeEnabled { - mi := &file_ttn_lorawan_v3_messages_proto_msgTypes[23] + mi := &file_ttn_lorawan_v3_messages_proto_msgTypes[27] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2115,7 +2313,7 @@ func (x *ApplicationDownlink_ConfirmedRetry) String() string { func (*ApplicationDownlink_ConfirmedRetry) ProtoMessage() {} func (x *ApplicationDownlink_ConfirmedRetry) ProtoReflect() protoreflect.Message { - mi := &file_ttn_lorawan_v3_messages_proto_msgTypes[23] + mi := &file_ttn_lorawan_v3_messages_proto_msgTypes[27] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2289,7 +2487,7 @@ var file_ttn_lorawan_v3_messages_proto_rawDesc = []byte{ 0x12, 0x3b, 0x0a, 0x0b, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, - 0x70, 0x52, 0x0a, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x41, 0x74, 0x22, 0xf4, 0x09, + 0x70, 0x52, 0x0a, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x41, 0x74, 0x22, 0xbe, 0x0b, 0x0a, 0x11, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x70, 0x6c, 0x69, 0x6e, 0x6b, 0x12, 0x2e, 0x0a, 0x0e, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x08, 0xfa, 0x42, 0x05, @@ -2363,389 +2561,581 @@ var file_ttn_lorawan_v3_messages_proto_rawDesc = []byte{ 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4c, 0x61, 0x73, 0x74, 0x42, 0x61, 0x74, 0x74, 0x65, 0x72, 0x79, 0x50, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x61, 0x67, 0x65, 0x52, 0x15, 0x6c, 0x61, 0x73, 0x74, 0x42, 0x61, 0x74, 0x74, 0x65, 0x72, 0x79, 0x50, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x61, - 0x67, 0x65, 0x1a, 0x56, 0x0a, 0x0e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, - 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2e, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, - 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x3a, 0x08, 0xf2, 0xaa, 0x19, 0x04, - 0x08, 0x01, 0x10, 0x01, 0x22, 0xb4, 0x07, 0x0a, 0x1b, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x70, 0x6c, 0x69, 0x6e, 0x6b, 0x4e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, - 0x69, 0x7a, 0x65, 0x64, 0x12, 0x2e, 0x0a, 0x0e, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, - 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x08, 0xfa, 0x42, - 0x05, 0x7a, 0x03, 0x18, 0x80, 0x10, 0x52, 0x0c, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4b, - 0x65, 0x79, 0x49, 0x64, 0x12, 0x24, 0x0a, 0x06, 0x66, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0d, 0x42, 0x0d, 0xfa, 0x42, 0x0a, 0x2a, 0x08, 0x18, 0xff, 0x01, 0x28, 0x01, - 0x38, 0xe0, 0x01, 0x52, 0x05, 0x66, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x13, 0x0a, 0x05, 0x66, 0x5f, - 0x63, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x66, 0x43, 0x6e, 0x74, 0x12, - 0x1f, 0x0a, 0x0b, 0x66, 0x72, 0x6d, 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x66, 0x72, 0x6d, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, - 0x12, 0x50, 0x0a, 0x12, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x70, - 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, - 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, - 0x74, 0x72, 0x75, 0x63, 0x74, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, - 0x11, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x50, 0x61, 0x79, 0x6c, 0x6f, - 0x61, 0x64, 0x12, 0x3e, 0x0a, 0x1b, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, - 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x77, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, - 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x19, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x69, - 0x7a, 0x65, 0x64, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x57, 0x61, 0x72, 0x6e, 0x69, 0x6e, - 0x67, 0x73, 0x12, 0x3b, 0x0a, 0x0b, 0x72, 0x78, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, - 0x61, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, - 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x52, 0x78, 0x4d, 0x65, 0x74, 0x61, 0x64, - 0x61, 0x74, 0x61, 0x52, 0x0a, 0x72, 0x78, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, - 0x40, 0x0a, 0x08, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, + 0x67, 0x65, 0x12, 0x88, 0x01, 0x0a, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, + 0x73, 0x18, 0x15, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, + 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x70, 0x6c, 0x69, 0x6e, 0x6b, 0x2e, 0x41, 0x74, 0x74, 0x72, 0x69, + 0x62, 0x75, 0x74, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x42, 0x35, 0xfa, 0x42, 0x32, 0x9a, + 0x01, 0x2f, 0x10, 0x0a, 0x22, 0x24, 0x72, 0x22, 0x18, 0x24, 0x32, 0x1e, 0x5e, 0x5b, 0x61, 0x2d, + 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x28, 0x3f, 0x3a, 0x5b, 0x2d, 0x5d, 0x3f, 0x5b, 0x61, 0x2d, 0x7a, + 0x30, 0x2d, 0x39, 0x5d, 0x29, 0x7b, 0x32, 0x2c, 0x7d, 0x24, 0x2a, 0x05, 0x72, 0x03, 0x18, 0xc8, + 0x01, 0x52, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x1a, 0x56, 0x0a, + 0x0e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, + 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, + 0x79, 0x12, 0x2e, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x18, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, + 0x33, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x3d, 0x0a, 0x0f, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, + 0x74, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x3a, 0x02, 0x38, 0x01, 0x3a, 0x08, 0xf2, 0xaa, 0x19, 0x04, 0x08, 0x01, 0x10, 0x01, 0x22, 0x88, + 0x09, 0x0a, 0x1b, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x70, + 0x6c, 0x69, 0x6e, 0x6b, 0x4e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x12, 0x2e, + 0x0a, 0x0e, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x7a, 0x03, 0x18, 0x80, 0x10, + 0x52, 0x0c, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x12, 0x24, + 0x0a, 0x06, 0x66, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x42, 0x0d, + 0xfa, 0x42, 0x0a, 0x2a, 0x08, 0x18, 0xff, 0x01, 0x28, 0x01, 0x38, 0xe0, 0x01, 0x52, 0x05, 0x66, + 0x50, 0x6f, 0x72, 0x74, 0x12, 0x13, 0x0a, 0x05, 0x66, 0x5f, 0x63, 0x6e, 0x74, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x04, 0x66, 0x43, 0x6e, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x66, 0x72, 0x6d, + 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, + 0x66, 0x72, 0x6d, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x50, 0x0a, 0x12, 0x6e, 0x6f, + 0x72, 0x6d, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x42, + 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x11, 0x6e, 0x6f, 0x72, 0x6d, 0x61, + 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x3e, 0x0a, 0x1b, + 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, + 0x61, 0x64, 0x5f, 0x77, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, + 0x09, 0x52, 0x19, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x50, 0x61, 0x79, + 0x6c, 0x6f, 0x61, 0x64, 0x57, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x3b, 0x0a, 0x0b, + 0x72, 0x78, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, - 0x76, 0x33, 0x2e, 0x54, 0x78, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x42, 0x08, 0xfa, - 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x08, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, - 0x73, 0x12, 0x45, 0x0a, 0x0b, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x5f, 0x61, 0x74, - 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, - 0x6d, 0x70, 0x42, 0x08, 0xfa, 0x42, 0x05, 0xb2, 0x01, 0x02, 0x08, 0x01, 0x52, 0x0a, 0x72, 0x65, - 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x41, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x66, - 0x69, 0x72, 0x6d, 0x65, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x63, 0x6f, 0x6e, - 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, 0x12, 0x44, 0x0a, 0x10, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6d, - 0x65, 0x64, 0x5f, 0x61, 0x69, 0x72, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0f, 0x63, 0x6f, 0x6e, - 0x73, 0x75, 0x6d, 0x65, 0x64, 0x41, 0x69, 0x72, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x58, 0x0a, 0x09, - 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x3a, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, - 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x70, 0x6c, 0x69, - 0x6e, 0x6b, 0x4e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x2e, 0x4c, 0x6f, 0x63, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x09, 0x6c, 0x6f, 0x63, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x4c, 0x0a, 0x0b, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, - 0x6e, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x74, 0x74, - 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x45, 0x6e, 0x64, - 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x65, - 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x52, 0x0a, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, - 0x6e, 0x49, 0x64, 0x73, 0x12, 0x43, 0x0a, 0x0b, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x5f, - 0x69, 0x64, 0x73, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x74, 0x74, 0x6e, 0x2e, - 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, - 0x72, 0x6b, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x52, 0x0a, 0x6e, - 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x64, 0x73, 0x1a, 0x56, 0x0a, 0x0e, 0x4c, 0x6f, 0x63, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, - 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2e, 0x0a, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x74, - 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4c, 0x6f, - 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, - 0x01, 0x3a, 0x08, 0xf2, 0xaa, 0x19, 0x04, 0x08, 0x01, 0x10, 0x00, 0x22, 0xc5, 0x02, 0x0a, 0x13, - 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x6f, 0x63, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x3e, 0x0a, - 0x08, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x18, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, - 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, - 0x02, 0x10, 0x01, 0x52, 0x08, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x8a, 0x01, - 0x0a, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, - 0x2e, 0x76, 0x33, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4c, - 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, - 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x42, 0x35, 0xfa, 0x42, 0x32, 0x9a, 0x01, 0x2f, 0x10, - 0x0a, 0x22, 0x24, 0x72, 0x22, 0x18, 0x24, 0x32, 0x1e, 0x5e, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, - 0x39, 0x5d, 0x28, 0x3f, 0x3a, 0x5b, 0x2d, 0x5d, 0x3f, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, - 0x5d, 0x29, 0x7b, 0x32, 0x2c, 0x7d, 0x24, 0x2a, 0x05, 0x72, 0x03, 0x18, 0xc8, 0x01, 0x52, 0x0a, - 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x1a, 0x3d, 0x0a, 0x0f, 0x41, 0x74, - 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, - 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, - 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x3a, 0x08, 0xf2, 0xaa, 0x19, 0x04, 0x08, - 0x01, 0x10, 0x00, 0x22, 0xd4, 0x02, 0x0a, 0x15, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x4a, 0x6f, 0x69, 0x6e, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x12, 0x2e, 0x0a, - 0x0e, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x7a, 0x03, 0x18, 0x80, 0x10, 0x52, - 0x0c, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x12, 0x37, 0x0a, - 0x09, 0x61, 0x70, 0x70, 0x5f, 0x73, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x1b, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, - 0x33, 0x2e, 0x4b, 0x65, 0x79, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x52, 0x07, 0x61, - 0x70, 0x70, 0x53, 0x4b, 0x65, 0x79, 0x12, 0x58, 0x0a, 0x15, 0x69, 0x6e, 0x76, 0x61, 0x6c, 0x69, - 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x73, 0x18, - 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, - 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x52, 0x14, 0x69, 0x6e, 0x76, 0x61, - 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x64, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x73, - 0x12, 0x27, 0x0a, 0x0f, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x73, 0x65, 0x73, 0x73, - 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x70, 0x65, 0x6e, 0x64, 0x69, - 0x6e, 0x67, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x45, 0x0a, 0x0b, 0x72, 0x65, 0x63, - 0x65, 0x69, 0x76, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, - 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, - 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x42, 0x08, 0xfa, 0x42, 0x05, 0xb2, - 0x01, 0x02, 0x08, 0x01, 0x52, 0x0a, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x41, 0x74, - 0x3a, 0x08, 0xf2, 0xaa, 0x19, 0x04, 0x08, 0x01, 0x10, 0x00, 0x22, 0xd5, 0x09, 0x0a, 0x13, 0x41, - 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69, - 0x6e, 0x6b, 0x12, 0xcd, 0x01, 0x0a, 0x0e, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x6b, - 0x65, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0xa6, 0x01, 0xfa, 0x42, - 0x05, 0x7a, 0x03, 0x18, 0x80, 0x10, 0xf2, 0xaa, 0x19, 0x99, 0x01, 0x1a, 0x4e, 0x67, 0x69, 0x74, - 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x54, 0x68, 0x65, 0x54, 0x68, 0x69, 0x6e, 0x67, - 0x73, 0x49, 0x6e, 0x64, 0x75, 0x73, 0x74, 0x72, 0x69, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x63, 0x2d, 0x67, 0x65, 0x6e, 0x2d, 0x67, 0x6f, 0x2d, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x2f, - 0x66, 0x6c, 0x61, 0x67, 0x73, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x4e, 0x65, 0x77, 0x48, - 0x65, 0x78, 0x42, 0x79, 0x74, 0x65, 0x73, 0x46, 0x6c, 0x61, 0x67, 0x22, 0x47, 0x67, 0x69, 0x74, - 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x54, 0x68, 0x65, 0x54, 0x68, 0x69, 0x6e, 0x67, - 0x73, 0x49, 0x6e, 0x64, 0x75, 0x73, 0x74, 0x72, 0x69, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x63, 0x2d, 0x67, 0x65, 0x6e, 0x2d, 0x67, 0x6f, 0x2d, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x2f, - 0x66, 0x6c, 0x61, 0x67, 0x73, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x42, - 0x79, 0x74, 0x65, 0x73, 0x52, 0x0c, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, - 0x49, 0x64, 0x12, 0x22, 0x0a, 0x06, 0x66, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0d, 0x42, 0x0b, 0xfa, 0x42, 0x08, 0x2a, 0x06, 0x18, 0xff, 0x01, 0x38, 0xe0, 0x01, 0x52, - 0x05, 0x66, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x13, 0x0a, 0x05, 0x66, 0x5f, 0x63, 0x6e, 0x74, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x66, 0x43, 0x6e, 0x74, 0x12, 0xc8, 0x01, 0x0a, 0x0b, - 0x66, 0x72, 0x6d, 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x0c, 0x42, 0xa6, 0x01, 0xfa, 0x42, 0x05, 0x7a, 0x03, 0x18, 0xff, 0x01, 0xf2, 0xaa, 0x19, 0x99, - 0x01, 0x1a, 0x4e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x54, 0x68, - 0x65, 0x54, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x49, 0x6e, 0x64, 0x75, 0x73, 0x74, 0x72, 0x69, 0x65, - 0x73, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x2d, 0x67, 0x65, 0x6e, 0x2d, 0x67, 0x6f, 0x2d, - 0x66, 0x6c, 0x61, 0x67, 0x73, 0x2f, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x70, 0x6c, 0x75, 0x67, 0x69, - 0x6e, 0x2e, 0x4e, 0x65, 0x77, 0x48, 0x65, 0x78, 0x42, 0x79, 0x74, 0x65, 0x73, 0x46, 0x6c, 0x61, - 0x67, 0x22, 0x47, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x54, 0x68, - 0x65, 0x54, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x49, 0x6e, 0x64, 0x75, 0x73, 0x74, 0x72, 0x69, 0x65, - 0x73, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x2d, 0x67, 0x65, 0x6e, 0x2d, 0x67, 0x6f, 0x2d, - 0x66, 0x6c, 0x61, 0x67, 0x73, 0x2f, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x70, 0x6c, 0x75, 0x67, 0x69, - 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x79, 0x74, 0x65, 0x73, 0x52, 0x0a, 0x66, 0x72, 0x6d, 0x50, - 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x40, 0x0a, 0x0f, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, - 0x64, 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, - 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x0e, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, - 0x64, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x38, 0x0a, 0x18, 0x64, 0x65, 0x63, 0x6f, - 0x64, 0x65, 0x64, 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x77, 0x61, 0x72, 0x6e, - 0x69, 0x6e, 0x67, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x09, 0x52, 0x16, 0x64, 0x65, 0x63, 0x6f, - 0x64, 0x65, 0x64, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x57, 0x61, 0x72, 0x6e, 0x69, 0x6e, - 0x67, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, 0x18, - 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, - 0x12, 0x47, 0x0a, 0x09, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x5f, 0x62, 0x5f, 0x63, 0x18, 0x07, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, - 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x2e, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x42, 0x43, - 0x52, 0x07, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x42, 0x43, 0x12, 0x48, 0x0a, 0x08, 0x70, 0x72, 0x69, - 0x6f, 0x72, 0x69, 0x74, 0x79, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x22, 0x2e, 0x74, 0x74, + 0x76, 0x33, 0x2e, 0x52, 0x78, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x0a, 0x72, + 0x78, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x40, 0x0a, 0x08, 0x73, 0x65, 0x74, + 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x54, 0x78, 0x53, - 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x50, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x42, - 0x08, 0xfa, 0x42, 0x05, 0x82, 0x01, 0x02, 0x10, 0x01, 0x52, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, - 0x69, 0x74, 0x79, 0x12, 0x35, 0x0a, 0x0f, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x09, 0x42, 0x0c, 0xfa, 0x42, - 0x09, 0x92, 0x01, 0x06, 0x22, 0x04, 0x72, 0x02, 0x18, 0x64, 0x52, 0x0e, 0x63, 0x6f, 0x72, 0x72, - 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x73, 0x12, 0x5b, 0x0a, 0x0f, 0x63, 0x6f, - 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x74, 0x72, 0x79, 0x18, 0x0b, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, - 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, - 0x65, 0x64, 0x52, 0x65, 0x74, 0x72, 0x79, 0x52, 0x0e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, - 0x65, 0x64, 0x52, 0x65, 0x74, 0x72, 0x79, 0x1a, 0x9b, 0x01, 0x0a, 0x07, 0x43, 0x6c, 0x61, 0x73, - 0x73, 0x42, 0x43, 0x12, 0x45, 0x0a, 0x08, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x18, - 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, - 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x42, 0x43, 0x47, 0x61, - 0x74, 0x65, 0x77, 0x61, 0x79, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, - 0x52, 0x08, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x12, 0x3f, 0x0a, 0x0d, 0x61, 0x62, - 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0c, 0x61, - 0x62, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x3a, 0x08, 0xf2, 0xaa, 0x19, - 0x04, 0x08, 0x01, 0x10, 0x01, 0x1a, 0x80, 0x01, 0x0a, 0x0e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x72, - 0x6d, 0x65, 0x64, 0x52, 0x65, 0x74, 0x72, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x74, 0x74, 0x65, - 0x6d, 0x70, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x61, 0x74, 0x74, 0x65, 0x6d, - 0x70, 0x74, 0x12, 0x4a, 0x0a, 0x0c, 0x6d, 0x61, 0x78, 0x5f, 0x61, 0x74, 0x74, 0x65, 0x6d, 0x70, - 0x74, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, - 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x49, 0x6e, 0x74, 0x33, - 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x09, 0xfa, 0x42, 0x06, 0x2a, 0x04, 0x18, 0x64, 0x20, - 0x00, 0x52, 0x0b, 0x6d, 0x61, 0x78, 0x41, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x73, 0x3a, 0x08, - 0xf2, 0xaa, 0x19, 0x04, 0x08, 0x01, 0x10, 0x01, 0x3a, 0x08, 0xf2, 0xaa, 0x19, 0x04, 0x08, 0x01, - 0x10, 0x01, 0x22, 0x59, 0x0a, 0x14, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x73, 0x12, 0x41, 0x0a, 0x09, 0x64, 0x6f, - 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, - 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x41, - 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69, - 0x6e, 0x6b, 0x52, 0x09, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x73, 0x22, 0xae, 0x01, - 0x0a, 0x19, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x6f, 0x77, - 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x12, 0x49, 0x0a, 0x08, 0x64, - 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, - 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x41, - 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69, - 0x6e, 0x6b, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x08, 0x64, 0x6f, - 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x12, 0x3c, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, - 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x44, 0x65, 0x74, 0x61, - 0x69, 0x6c, 0x73, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x05, 0x65, - 0x72, 0x72, 0x6f, 0x72, 0x3a, 0x08, 0xf2, 0xaa, 0x19, 0x04, 0x08, 0x01, 0x10, 0x00, 0x22, 0xc5, - 0x01, 0x0a, 0x1f, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, - 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x64, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, - 0x6b, 0x73, 0x12, 0x41, 0x0a, 0x09, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x73, 0x18, - 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, - 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x52, 0x09, 0x64, 0x6f, 0x77, 0x6e, - 0x6c, 0x69, 0x6e, 0x6b, 0x73, 0x12, 0x25, 0x0a, 0x0f, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x66, 0x5f, - 0x63, 0x6e, 0x74, 0x5f, 0x64, 0x6f, 0x77, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, - 0x6c, 0x61, 0x73, 0x74, 0x46, 0x43, 0x6e, 0x74, 0x44, 0x6f, 0x77, 0x6e, 0x12, 0x2e, 0x0a, 0x0e, - 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x0c, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x7a, 0x03, 0x18, 0x80, 0x10, 0x52, 0x0c, - 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x3a, 0x08, 0xf2, 0xaa, - 0x19, 0x04, 0x08, 0x01, 0x10, 0x00, 0x22, 0x91, 0x05, 0x0a, 0x22, 0x44, 0x6f, 0x77, 0x6e, 0x6c, - 0x69, 0x6e, 0x6b, 0x51, 0x75, 0x65, 0x75, 0x65, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0xc8, 0x01, - 0x0a, 0x08, 0x64, 0x65, 0x76, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, - 0x42, 0xac, 0x01, 0x92, 0x41, 0x19, 0x4a, 0x0a, 0x22, 0x32, 0x36, 0x30, 0x30, 0x41, 0x42, 0x43, - 0x44, 0x22, 0x9a, 0x02, 0x01, 0x07, 0xa2, 0x02, 0x06, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0xfa, - 0x42, 0x06, 0x7a, 0x04, 0x68, 0x04, 0x70, 0x01, 0xea, 0xaa, 0x19, 0x82, 0x01, 0x0a, 0x3f, 0x67, - 0x6f, 0x2e, 0x74, 0x68, 0x65, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x2e, 0x6e, 0x65, 0x74, 0x77, - 0x6f, 0x72, 0x6b, 0x2f, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2d, 0x73, 0x74, 0x61, 0x63, - 0x6b, 0x2f, 0x76, 0x33, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x4d, - 0x61, 0x72, 0x73, 0x68, 0x61, 0x6c, 0x48, 0x45, 0x58, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x3f, - 0x67, 0x6f, 0x2e, 0x74, 0x68, 0x65, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x2e, 0x6e, 0x65, 0x74, - 0x77, 0x6f, 0x72, 0x6b, 0x2f, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2d, 0x73, 0x74, 0x61, - 0x63, 0x6b, 0x2f, 0x76, 0x33, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, - 0x55, 0x6e, 0x6d, 0x61, 0x72, 0x73, 0x68, 0x61, 0x6c, 0x34, 0x42, 0x79, 0x74, 0x65, 0x73, 0x52, - 0x07, 0x64, 0x65, 0x76, 0x41, 0x64, 0x64, 0x72, 0x12, 0x2e, 0x0a, 0x0e, 0x73, 0x65, 0x73, 0x73, - 0x69, 0x6f, 0x6e, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, - 0x42, 0x08, 0xfa, 0x42, 0x05, 0x7a, 0x03, 0x18, 0x80, 0x10, 0x52, 0x0c, 0x73, 0x65, 0x73, 0x73, - 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x12, 0x23, 0x0a, 0x0e, 0x6d, 0x69, 0x6e, 0x5f, - 0x66, 0x5f, 0x63, 0x6e, 0x74, 0x5f, 0x64, 0x6f, 0x77, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, - 0x52, 0x0b, 0x6d, 0x69, 0x6e, 0x46, 0x43, 0x6e, 0x74, 0x44, 0x6f, 0x77, 0x6e, 0x12, 0xd7, 0x01, - 0x0a, 0x10, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x64, 0x65, 0x76, 0x5f, 0x61, 0x64, - 0x64, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x42, 0xac, 0x01, 0x92, 0x41, 0x19, 0x4a, 0x0a, - 0x22, 0x32, 0x36, 0x30, 0x30, 0x41, 0x42, 0x43, 0x44, 0x22, 0x9a, 0x02, 0x01, 0x07, 0xa2, 0x02, - 0x06, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0xfa, 0x42, 0x06, 0x7a, 0x04, 0x68, 0x04, 0x70, 0x01, - 0xea, 0xaa, 0x19, 0x82, 0x01, 0x0a, 0x3f, 0x67, 0x6f, 0x2e, 0x74, 0x68, 0x65, 0x74, 0x68, 0x69, - 0x6e, 0x67, 0x73, 0x2e, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2f, 0x6c, 0x6f, 0x72, 0x61, - 0x77, 0x61, 0x6e, 0x2d, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x2f, 0x76, 0x33, 0x2f, 0x70, 0x6b, 0x67, - 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x4d, 0x61, 0x72, 0x73, 0x68, 0x61, 0x6c, 0x48, 0x45, - 0x58, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x3f, 0x67, 0x6f, 0x2e, 0x74, 0x68, 0x65, 0x74, 0x68, - 0x69, 0x6e, 0x67, 0x73, 0x2e, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2f, 0x6c, 0x6f, 0x72, - 0x61, 0x77, 0x61, 0x6e, 0x2d, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x2f, 0x76, 0x33, 0x2f, 0x70, 0x6b, - 0x67, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x55, 0x6e, 0x6d, 0x61, 0x72, 0x73, 0x68, 0x61, - 0x6c, 0x34, 0x42, 0x79, 0x74, 0x65, 0x73, 0x52, 0x0e, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, - 0x44, 0x65, 0x76, 0x41, 0x64, 0x64, 0x72, 0x12, 0x3d, 0x0a, 0x16, 0x70, 0x65, 0x6e, 0x64, 0x69, - 0x6e, 0x67, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x69, - 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x7a, 0x03, 0x18, 0x80, - 0x10, 0x52, 0x13, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, - 0x6e, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x12, 0x32, 0x0a, 0x16, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, - 0x67, 0x5f, 0x6d, 0x69, 0x6e, 0x5f, 0x66, 0x5f, 0x63, 0x6e, 0x74, 0x5f, 0x64, 0x6f, 0x77, 0x6e, - 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x12, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x4d, - 0x69, 0x6e, 0x46, 0x43, 0x6e, 0x74, 0x44, 0x6f, 0x77, 0x6e, 0x22, 0x69, 0x0a, 0x16, 0x41, 0x70, - 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x44, 0x61, 0x74, 0x61, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x2b, - 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, - 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, - 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x3a, 0x08, 0xf2, 0xaa, 0x19, - 0x04, 0x08, 0x01, 0x10, 0x00, 0x22, 0x94, 0x09, 0x0a, 0x0d, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x70, 0x12, 0x54, 0x0a, 0x0e, 0x65, 0x6e, 0x64, 0x5f, 0x64, - 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x24, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, - 0x2e, 0x45, 0x6e, 0x64, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, - 0x66, 0x69, 0x65, 0x72, 0x73, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, - 0x0c, 0x65, 0x6e, 0x64, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x73, 0x12, 0x35, 0x0a, - 0x0f, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x73, - 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x42, 0x0c, 0xfa, 0x42, 0x09, 0x92, 0x01, 0x06, 0x22, 0x04, - 0x72, 0x02, 0x18, 0x64, 0x52, 0x0e, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x49, 0x64, 0x73, 0x12, 0x3b, 0x0a, 0x0b, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, - 0x5f, 0x61, 0x74, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, - 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x41, - 0x74, 0x12, 0x4a, 0x0a, 0x0e, 0x75, 0x70, 0x6c, 0x69, 0x6e, 0x6b, 0x5f, 0x6d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x74, 0x74, 0x6e, 0x2e, + 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, + 0x01, 0x52, 0x08, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x45, 0x0a, 0x0b, 0x72, + 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x42, 0x08, 0xfa, 0x42, + 0x05, 0xb2, 0x01, 0x02, 0x08, 0x01, 0x52, 0x0a, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, + 0x41, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, 0x18, + 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, + 0x12, 0x44, 0x0a, 0x10, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x64, 0x5f, 0x61, 0x69, 0x72, + 0x74, 0x69, 0x6d, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0f, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x64, 0x41, + 0x69, 0x72, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x58, 0x0a, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3a, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x69, - 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x70, 0x6c, 0x69, 0x6e, 0x6b, 0x48, 0x00, 0x52, 0x0d, - 0x75, 0x70, 0x6c, 0x69, 0x6e, 0x6b, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x5a, 0x0a, - 0x11, 0x75, 0x70, 0x6c, 0x69, 0x6e, 0x6b, 0x5f, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x69, 0x7a, - 0x65, 0x64, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x70, 0x6c, 0x69, 0x6e, 0x6b, 0x4e, 0x6f, 0x72, 0x6d, + 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x12, 0x4c, 0x0a, 0x0b, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x73, 0x18, + 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, + 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x45, 0x6e, 0x64, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, + 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, + 0x72, 0x73, 0x52, 0x0a, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x73, 0x12, 0x43, + 0x0a, 0x0b, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x0e, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, + 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x64, 0x65, 0x6e, + 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x52, 0x0a, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, + 0x49, 0x64, 0x73, 0x12, 0x92, 0x01, 0x0a, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, + 0x65, 0x73, 0x18, 0x0f, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3b, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x70, 0x6c, 0x69, 0x6e, 0x6b, 0x4e, 0x6f, 0x72, 0x6d, 0x61, - 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x48, 0x00, 0x52, 0x10, 0x75, 0x70, 0x6c, 0x69, 0x6e, 0x6b, 0x4e, - 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x12, 0x48, 0x0a, 0x0b, 0x6a, 0x6f, 0x69, - 0x6e, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, + 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x2e, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x42, 0x35, 0xfa, 0x42, 0x32, 0x9a, 0x01, 0x2f, 0x10, 0x0a, 0x22, + 0x24, 0x72, 0x22, 0x18, 0x24, 0x32, 0x1e, 0x5e, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, + 0x28, 0x3f, 0x3a, 0x5b, 0x2d, 0x5d, 0x3f, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x29, + 0x7b, 0x32, 0x2c, 0x7d, 0x24, 0x2a, 0x05, 0x72, 0x03, 0x18, 0xc8, 0x01, 0x52, 0x0a, 0x61, 0x74, + 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x1a, 0x56, 0x0a, 0x0e, 0x4c, 0x6f, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, + 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2e, 0x0a, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x74, 0x74, + 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4c, 0x6f, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, + 0x1a, 0x3d, 0x0a, 0x0f, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x3a, + 0x08, 0xf2, 0xaa, 0x19, 0x04, 0x08, 0x01, 0x10, 0x00, 0x22, 0xc5, 0x02, 0x0a, 0x13, 0x41, 0x70, + 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x3e, 0x0a, 0x08, 0x6c, + 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, + 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4c, + 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, + 0x01, 0x52, 0x08, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x8a, 0x01, 0x0a, 0x0a, + 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x33, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, + 0x33, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x6f, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x42, 0x35, 0xfa, 0x42, 0x32, 0x9a, 0x01, 0x2f, 0x10, 0x0a, 0x22, + 0x24, 0x72, 0x22, 0x18, 0x24, 0x32, 0x1e, 0x5e, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, + 0x28, 0x3f, 0x3a, 0x5b, 0x2d, 0x5d, 0x3f, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x29, + 0x7b, 0x32, 0x2c, 0x7d, 0x24, 0x2a, 0x05, 0x72, 0x03, 0x18, 0xc8, 0x01, 0x52, 0x0a, 0x61, 0x74, + 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x1a, 0x3d, 0x0a, 0x0f, 0x41, 0x74, 0x74, 0x72, + 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, + 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x3a, 0x08, 0xf2, 0xaa, 0x19, 0x04, 0x08, 0x01, 0x10, + 0x00, 0x22, 0xe1, 0x06, 0x0a, 0x15, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x4a, 0x6f, 0x69, 0x6e, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x12, 0x2e, 0x0a, 0x0e, 0x73, + 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0c, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x7a, 0x03, 0x18, 0x80, 0x10, 0x52, 0x0c, 0x73, + 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x12, 0x37, 0x0a, 0x09, 0x61, + 0x70, 0x70, 0x5f, 0x73, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, - 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4a, 0x6f, 0x69, 0x6e, 0x41, - 0x63, 0x63, 0x65, 0x70, 0x74, 0x48, 0x00, 0x52, 0x0a, 0x6a, 0x6f, 0x69, 0x6e, 0x41, 0x63, 0x63, - 0x65, 0x70, 0x74, 0x12, 0x48, 0x0a, 0x0c, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x5f, - 0x61, 0x63, 0x6b, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x74, 0x74, 0x6e, 0x2e, - 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x69, - 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x48, 0x00, - 0x52, 0x0b, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x41, 0x63, 0x6b, 0x12, 0x4a, 0x0a, - 0x0d, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x5f, 0x6e, 0x61, 0x63, 0x6b, 0x18, 0x06, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, - 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x48, 0x00, 0x52, 0x0c, 0x64, 0x6f, 0x77, - 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x4e, 0x61, 0x63, 0x6b, 0x12, 0x4a, 0x0a, 0x0d, 0x64, 0x6f, 0x77, - 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x5f, 0x73, 0x65, 0x6e, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x23, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, - 0x33, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x6f, 0x77, - 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x48, 0x00, 0x52, 0x0c, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, - 0x6b, 0x53, 0x65, 0x6e, 0x74, 0x12, 0x54, 0x0a, 0x0f, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, - 0x6b, 0x5f, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, + 0x4b, 0x65, 0x79, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x52, 0x07, 0x61, 0x70, 0x70, + 0x53, 0x4b, 0x65, 0x79, 0x12, 0x58, 0x0a, 0x15, 0x69, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, + 0x74, 0x65, 0x64, 0x5f, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x73, 0x18, 0x03, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, + 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x52, 0x14, 0x69, 0x6e, 0x76, 0x61, 0x6c, 0x69, + 0x64, 0x61, 0x74, 0x65, 0x64, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x73, 0x12, 0x27, + 0x0a, 0x0f, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, + 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x45, 0x0a, 0x0b, 0x72, 0x65, 0x63, 0x65, 0x69, + 0x76, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x42, 0x08, 0xfa, 0x42, 0x05, 0xb2, 0x01, 0x02, + 0x08, 0x01, 0x52, 0x0a, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x41, 0x74, 0x12, 0x52, + 0x0a, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x34, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, + 0x76, 0x33, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4a, 0x6f, + 0x69, 0x6e, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x12, 0x4c, 0x0a, 0x0b, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, + 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, + 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x45, 0x6e, 0x64, 0x44, 0x65, 0x76, 0x69, + 0x63, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, + 0x69, 0x65, 0x72, 0x73, 0x52, 0x0a, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x73, + 0x12, 0x43, 0x0a, 0x0b, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x5f, 0x69, 0x64, 0x73, 0x18, + 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, + 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x64, + 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x52, 0x0a, 0x6e, 0x65, 0x74, 0x77, 0x6f, + 0x72, 0x6b, 0x49, 0x64, 0x73, 0x12, 0x8c, 0x01, 0x0a, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, + 0x75, 0x74, 0x65, 0x73, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x74, 0x74, 0x6e, + 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x41, 0x70, 0x70, 0x6c, + 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4a, 0x6f, 0x69, 0x6e, 0x41, 0x63, 0x63, 0x65, 0x70, + 0x74, 0x2e, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x42, 0x35, 0xfa, 0x42, 0x32, 0x9a, 0x01, 0x2f, 0x10, 0x0a, 0x22, 0x24, 0x72, 0x22, 0x18, + 0x24, 0x32, 0x1e, 0x5e, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x28, 0x3f, 0x3a, 0x5b, + 0x2d, 0x5d, 0x3f, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x29, 0x7b, 0x32, 0x2c, 0x7d, + 0x24, 0x2a, 0x05, 0x72, 0x03, 0x18, 0xc8, 0x01, 0x52, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, + 0x75, 0x74, 0x65, 0x73, 0x1a, 0x56, 0x0a, 0x0e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2e, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, + 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x3d, 0x0a, 0x0f, + 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, + 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, + 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x3a, 0x08, 0xf2, 0xaa, 0x19, + 0x04, 0x08, 0x01, 0x10, 0x00, 0x22, 0xde, 0x0d, 0x0a, 0x13, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x12, 0xcd, 0x01, + 0x0a, 0x0e, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0xa6, 0x01, 0xfa, 0x42, 0x05, 0x7a, 0x03, 0x18, 0x80, + 0x10, 0xf2, 0xaa, 0x19, 0x99, 0x01, 0x1a, 0x4e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x54, 0x68, 0x65, 0x54, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x49, 0x6e, 0x64, 0x75, + 0x73, 0x74, 0x72, 0x69, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x2d, 0x67, 0x65, + 0x6e, 0x2d, 0x67, 0x6f, 0x2d, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x2f, 0x66, 0x6c, 0x61, 0x67, 0x73, + 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x4e, 0x65, 0x77, 0x48, 0x65, 0x78, 0x42, 0x79, 0x74, + 0x65, 0x73, 0x46, 0x6c, 0x61, 0x67, 0x22, 0x47, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x54, 0x68, 0x65, 0x54, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x49, 0x6e, 0x64, 0x75, + 0x73, 0x74, 0x72, 0x69, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x2d, 0x67, 0x65, + 0x6e, 0x2d, 0x67, 0x6f, 0x2d, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x2f, 0x66, 0x6c, 0x61, 0x67, 0x73, + 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x79, 0x74, 0x65, 0x73, 0x52, + 0x0c, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x12, 0x22, 0x0a, + 0x06, 0x66, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x42, 0x0b, 0xfa, + 0x42, 0x08, 0x2a, 0x06, 0x18, 0xff, 0x01, 0x38, 0xe0, 0x01, 0x52, 0x05, 0x66, 0x50, 0x6f, 0x72, + 0x74, 0x12, 0x13, 0x0a, 0x05, 0x66, 0x5f, 0x63, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x04, 0x66, 0x43, 0x6e, 0x74, 0x12, 0xc8, 0x01, 0x0a, 0x0b, 0x66, 0x72, 0x6d, 0x5f, 0x70, + 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x42, 0xa6, 0x01, 0xfa, + 0x42, 0x05, 0x7a, 0x03, 0x18, 0xff, 0x01, 0xf2, 0xaa, 0x19, 0x99, 0x01, 0x1a, 0x4e, 0x67, 0x69, + 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x54, 0x68, 0x65, 0x54, 0x68, 0x69, 0x6e, + 0x67, 0x73, 0x49, 0x6e, 0x64, 0x75, 0x73, 0x74, 0x72, 0x69, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x63, 0x2d, 0x67, 0x65, 0x6e, 0x2d, 0x67, 0x6f, 0x2d, 0x66, 0x6c, 0x61, 0x67, 0x73, + 0x2f, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x4e, 0x65, 0x77, + 0x48, 0x65, 0x78, 0x42, 0x79, 0x74, 0x65, 0x73, 0x46, 0x6c, 0x61, 0x67, 0x22, 0x47, 0x67, 0x69, + 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x54, 0x68, 0x65, 0x54, 0x68, 0x69, 0x6e, + 0x67, 0x73, 0x49, 0x6e, 0x64, 0x75, 0x73, 0x74, 0x72, 0x69, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x63, 0x2d, 0x67, 0x65, 0x6e, 0x2d, 0x67, 0x6f, 0x2d, 0x66, 0x6c, 0x61, 0x67, 0x73, + 0x2f, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x47, 0x65, 0x74, + 0x42, 0x79, 0x74, 0x65, 0x73, 0x52, 0x0a, 0x66, 0x72, 0x6d, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, + 0x64, 0x12, 0x40, 0x0a, 0x0f, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x5f, 0x70, 0x61, 0x79, + 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, + 0x75, 0x63, 0x74, 0x52, 0x0e, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x50, 0x61, 0x79, 0x6c, + 0x6f, 0x61, 0x64, 0x12, 0x38, 0x0a, 0x18, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x5f, 0x70, + 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x77, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x73, 0x18, + 0x0a, 0x20, 0x03, 0x28, 0x09, 0x52, 0x16, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x50, 0x61, + 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x57, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x1c, 0x0a, + 0x09, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, 0x12, 0x47, 0x0a, 0x09, 0x63, + 0x6c, 0x61, 0x73, 0x73, 0x5f, 0x62, 0x5f, 0x63, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, + 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, + 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x6f, 0x77, 0x6e, 0x6c, + 0x69, 0x6e, 0x6b, 0x2e, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x42, 0x43, 0x52, 0x07, 0x63, 0x6c, 0x61, + 0x73, 0x73, 0x42, 0x43, 0x12, 0x48, 0x0a, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, + 0x18, 0x08, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x22, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, + 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x54, 0x78, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, + 0x6c, 0x65, 0x50, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x82, + 0x01, 0x02, 0x10, 0x01, 0x52, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x12, 0x35, + 0x0a, 0x0f, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, + 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x09, 0x42, 0x0c, 0xfa, 0x42, 0x09, 0x92, 0x01, 0x06, 0x22, + 0x04, 0x72, 0x02, 0x18, 0x64, 0x52, 0x0e, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x49, 0x64, 0x73, 0x12, 0x5b, 0x0a, 0x0f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, + 0x65, 0x64, 0x5f, 0x72, 0x65, 0x74, 0x72, 0x79, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x6f, 0x77, 0x6e, 0x6c, - 0x69, 0x6e, 0x6b, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x48, 0x00, 0x52, 0x0e, 0x64, 0x6f, 0x77, - 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x12, 0x4e, 0x0a, 0x0f, 0x64, - 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x5f, 0x71, 0x75, 0x65, 0x75, 0x65, 0x64, 0x18, 0x09, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, - 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x48, 0x00, 0x52, 0x0e, 0x64, 0x6f, 0x77, - 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x51, 0x75, 0x65, 0x75, 0x65, 0x64, 0x12, 0x6f, 0x0a, 0x1a, 0x64, - 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x5f, 0x71, 0x75, 0x65, 0x75, 0x65, 0x5f, 0x69, 0x6e, - 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x2f, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, - 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x76, 0x61, - 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x64, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x73, - 0x48, 0x00, 0x52, 0x18, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x51, 0x75, 0x65, 0x75, - 0x65, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x64, 0x12, 0x4e, 0x0a, 0x0f, - 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x64, 0x18, - 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, + 0x69, 0x6e, 0x6b, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, 0x52, 0x65, 0x74, + 0x72, 0x79, 0x52, 0x0e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, 0x52, 0x65, 0x74, + 0x72, 0x79, 0x12, 0x50, 0x0a, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, + 0x0c, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, 0x0e, 0x6c, 0x6f, - 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x6f, 0x6c, 0x76, 0x65, 0x64, 0x12, 0x4b, 0x0a, 0x0c, - 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x0d, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, - 0x2e, 0x76, 0x33, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x48, 0x00, 0x52, 0x0b, 0x73, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x6d, - 0x75, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x73, 0x69, - 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x3a, 0x08, 0xf2, 0xaa, 0x19, 0x04, 0x08, 0x01, 0x10, - 0x00, 0x42, 0x09, 0x0a, 0x02, 0x75, 0x70, 0x12, 0x03, 0xf8, 0x42, 0x01, 0x22, 0xcc, 0x02, 0x0a, - 0x18, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x46, - 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x74, 0x65, 0x72, 0x73, 0x12, 0x4d, 0x0a, 0x0c, 0x75, 0x70, 0x5f, - 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x74, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, - 0x20, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, - 0x2e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x74, 0x65, - 0x72, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x82, 0x01, 0x02, 0x10, 0x01, 0x52, 0x0b, 0x75, 0x70, 0x46, - 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x74, 0x65, 0x72, 0x12, 0x3f, 0x0a, 0x16, 0x75, 0x70, 0x5f, 0x66, + 0x6f, 0x6e, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x4c, 0x0a, 0x0b, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x5f, + 0x69, 0x64, 0x73, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x74, 0x74, 0x6e, 0x2e, + 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x45, 0x6e, 0x64, 0x44, 0x65, + 0x76, 0x69, 0x63, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x65, 0x6e, 0x74, + 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x52, 0x0a, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x49, + 0x64, 0x73, 0x12, 0x43, 0x0a, 0x0b, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x5f, 0x69, 0x64, + 0x73, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, + 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, + 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x52, 0x0a, 0x6e, 0x65, 0x74, + 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x64, 0x73, 0x12, 0x8a, 0x01, 0x0a, 0x0a, 0x61, 0x74, 0x74, 0x72, + 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x0f, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x74, + 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x41, 0x70, + 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, + 0x6b, 0x2e, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x42, 0x35, 0xfa, 0x42, 0x32, 0x9a, 0x01, 0x2f, 0x10, 0x0a, 0x22, 0x24, 0x72, 0x22, 0x18, + 0x24, 0x32, 0x1e, 0x5e, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x28, 0x3f, 0x3a, 0x5b, + 0x2d, 0x5d, 0x3f, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x29, 0x7b, 0x32, 0x2c, 0x7d, + 0x24, 0x2a, 0x05, 0x72, 0x03, 0x18, 0xc8, 0x01, 0x52, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, + 0x75, 0x74, 0x65, 0x73, 0x1a, 0x9b, 0x01, 0x0a, 0x07, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x42, 0x43, + 0x12, 0x45, 0x0a, 0x08, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x18, 0x07, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, + 0x2e, 0x76, 0x33, 0x2e, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x42, 0x43, 0x47, 0x61, 0x74, 0x65, 0x77, + 0x61, 0x79, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x52, 0x08, 0x67, + 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x12, 0x3f, 0x0a, 0x0d, 0x61, 0x62, 0x73, 0x6f, 0x6c, + 0x75, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0c, 0x61, 0x62, 0x73, 0x6f, + 0x6c, 0x75, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x3a, 0x08, 0xf2, 0xaa, 0x19, 0x04, 0x08, 0x01, + 0x10, 0x01, 0x1a, 0x80, 0x01, 0x0a, 0x0e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, + 0x52, 0x65, 0x74, 0x72, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x61, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x12, + 0x4a, 0x0a, 0x0c, 0x6d, 0x61, 0x78, 0x5f, 0x61, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x73, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x56, 0x61, + 0x6c, 0x75, 0x65, 0x42, 0x09, 0xfa, 0x42, 0x06, 0x2a, 0x04, 0x18, 0x64, 0x20, 0x00, 0x52, 0x0b, + 0x6d, 0x61, 0x78, 0x41, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x73, 0x3a, 0x08, 0xf2, 0xaa, 0x19, + 0x04, 0x08, 0x01, 0x10, 0x01, 0x1a, 0x56, 0x0a, 0x0e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2e, 0x0a, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, + 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x3d, 0x0a, + 0x0f, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, + 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x3a, 0x08, 0xf2, 0xaa, + 0x19, 0x04, 0x08, 0x01, 0x10, 0x01, 0x22, 0x59, 0x0a, 0x14, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x73, 0x12, 0x41, + 0x0a, 0x09, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x23, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, + 0x76, 0x33, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x6f, + 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x52, 0x09, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, + 0x73, 0x22, 0xc3, 0x05, 0x0a, 0x19, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x12, + 0x49, 0x0a, 0x08, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x23, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, + 0x76, 0x33, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x6f, + 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, + 0x52, 0x08, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x12, 0x3c, 0x0a, 0x05, 0x65, 0x72, + 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x74, 0x74, 0x6e, 0x2e, + 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, + 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, + 0x01, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x56, 0x0a, 0x09, 0x6c, 0x6f, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x38, 0x2e, 0x74, 0x74, + 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x41, 0x70, 0x70, + 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, + 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x12, 0x4c, 0x0a, 0x0b, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x73, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, + 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x45, 0x6e, 0x64, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, + 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, + 0x72, 0x73, 0x52, 0x0a, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x73, 0x12, 0x43, + 0x0a, 0x0b, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, + 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x64, 0x65, 0x6e, + 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x52, 0x0a, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, + 0x49, 0x64, 0x73, 0x12, 0x90, 0x01, 0x0a, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, + 0x65, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x39, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, + 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x46, 0x61, 0x69, + 0x6c, 0x65, 0x64, 0x2e, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x42, 0x35, 0xfa, 0x42, 0x32, 0x9a, 0x01, 0x2f, 0x10, 0x0a, 0x22, 0x24, 0x72, + 0x22, 0x18, 0x24, 0x32, 0x1e, 0x5e, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x28, 0x3f, + 0x3a, 0x5b, 0x2d, 0x5d, 0x3f, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x29, 0x7b, 0x32, + 0x2c, 0x7d, 0x24, 0x2a, 0x05, 0x72, 0x03, 0x18, 0xc8, 0x01, 0x52, 0x0a, 0x61, 0x74, 0x74, 0x72, + 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x1a, 0x56, 0x0a, 0x0e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2e, 0x0a, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x74, 0x74, 0x6e, 0x2e, + 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x3d, + 0x0a, 0x0f, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x3a, 0x08, 0xf2, + 0xaa, 0x19, 0x04, 0x08, 0x01, 0x10, 0x00, 0x22, 0xe6, 0x05, 0x0a, 0x1f, 0x41, 0x70, 0x70, 0x6c, + 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, + 0x65, 0x64, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x73, 0x12, 0x41, 0x0a, 0x09, 0x64, + 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, + 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, + 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x6f, 0x77, 0x6e, 0x6c, + 0x69, 0x6e, 0x6b, 0x52, 0x09, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x73, 0x12, 0x25, + 0x0a, 0x0f, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x66, 0x5f, 0x63, 0x6e, 0x74, 0x5f, 0x64, 0x6f, 0x77, + 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x6c, 0x61, 0x73, 0x74, 0x46, 0x43, 0x6e, + 0x74, 0x44, 0x6f, 0x77, 0x6e, 0x12, 0x2e, 0x0a, 0x0e, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, + 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x08, 0xfa, + 0x42, 0x05, 0x7a, 0x03, 0x18, 0x80, 0x10, 0x52, 0x0c, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, + 0x4b, 0x65, 0x79, 0x49, 0x64, 0x12, 0x5c, 0x0a, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3e, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, + 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x64, + 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x73, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x12, 0x4c, 0x0a, 0x0b, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, + 0x64, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, + 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x45, 0x6e, 0x64, 0x44, 0x65, 0x76, + 0x69, 0x63, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, + 0x66, 0x69, 0x65, 0x72, 0x73, 0x52, 0x0a, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, + 0x73, 0x12, 0x43, 0x0a, 0x0b, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x5f, 0x69, 0x64, 0x73, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, + 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, + 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x52, 0x0a, 0x6e, 0x65, 0x74, 0x77, + 0x6f, 0x72, 0x6b, 0x49, 0x64, 0x73, 0x12, 0x96, 0x01, 0x0a, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, + 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3f, 0x2e, 0x74, 0x74, + 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x41, 0x70, 0x70, + 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, + 0x74, 0x65, 0x64, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x73, 0x2e, 0x41, 0x74, 0x74, + 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x42, 0x35, 0xfa, 0x42, + 0x32, 0x9a, 0x01, 0x2f, 0x10, 0x0a, 0x22, 0x24, 0x72, 0x22, 0x18, 0x24, 0x32, 0x1e, 0x5e, 0x5b, + 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x28, 0x3f, 0x3a, 0x5b, 0x2d, 0x5d, 0x3f, 0x5b, 0x61, + 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x29, 0x7b, 0x32, 0x2c, 0x7d, 0x24, 0x2a, 0x05, 0x72, 0x03, + 0x18, 0xc8, 0x01, 0x52, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x1a, + 0x56, 0x0a, 0x0e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x6b, 0x65, 0x79, 0x12, 0x2e, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, + 0x2e, 0x76, 0x33, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x3d, 0x0a, 0x0f, 0x41, 0x74, 0x74, 0x72, 0x69, + 0x62, 0x75, 0x74, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, + 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x3a, 0x08, 0xf2, 0xaa, 0x19, 0x04, 0x08, 0x01, 0x10, 0x00, + 0x22, 0x91, 0x05, 0x0a, 0x22, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x51, 0x75, 0x65, + 0x75, 0x65, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x72, 0x72, 0x6f, 0x72, + 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0xc8, 0x01, 0x0a, 0x08, 0x64, 0x65, 0x76, 0x5f, + 0x61, 0x64, 0x64, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0xac, 0x01, 0x92, 0x41, 0x19, + 0x4a, 0x0a, 0x22, 0x32, 0x36, 0x30, 0x30, 0x41, 0x42, 0x43, 0x44, 0x22, 0x9a, 0x02, 0x01, 0x07, + 0xa2, 0x02, 0x06, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0xfa, 0x42, 0x06, 0x7a, 0x04, 0x68, 0x04, + 0x70, 0x01, 0xea, 0xaa, 0x19, 0x82, 0x01, 0x0a, 0x3f, 0x67, 0x6f, 0x2e, 0x74, 0x68, 0x65, 0x74, + 0x68, 0x69, 0x6e, 0x67, 0x73, 0x2e, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2f, 0x6c, 0x6f, + 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2d, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x2f, 0x76, 0x33, 0x2f, 0x70, + 0x6b, 0x67, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x4d, 0x61, 0x72, 0x73, 0x68, 0x61, 0x6c, + 0x48, 0x45, 0x58, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x3f, 0x67, 0x6f, 0x2e, 0x74, 0x68, 0x65, + 0x74, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x2e, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2f, 0x6c, + 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2d, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x2f, 0x76, 0x33, 0x2f, + 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x55, 0x6e, 0x6d, 0x61, 0x72, 0x73, + 0x68, 0x61, 0x6c, 0x34, 0x42, 0x79, 0x74, 0x65, 0x73, 0x52, 0x07, 0x64, 0x65, 0x76, 0x41, 0x64, + 0x64, 0x72, 0x12, 0x2e, 0x0a, 0x0e, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x6b, 0x65, + 0x79, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x7a, + 0x03, 0x18, 0x80, 0x10, 0x52, 0x0c, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, + 0x49, 0x64, 0x12, 0x23, 0x0a, 0x0e, 0x6d, 0x69, 0x6e, 0x5f, 0x66, 0x5f, 0x63, 0x6e, 0x74, 0x5f, + 0x64, 0x6f, 0x77, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x6d, 0x69, 0x6e, 0x46, + 0x43, 0x6e, 0x74, 0x44, 0x6f, 0x77, 0x6e, 0x12, 0xd7, 0x01, 0x0a, 0x10, 0x70, 0x65, 0x6e, 0x64, + 0x69, 0x6e, 0x67, 0x5f, 0x64, 0x65, 0x76, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x0c, 0x42, 0xac, 0x01, 0x92, 0x41, 0x19, 0x4a, 0x0a, 0x22, 0x32, 0x36, 0x30, 0x30, 0x41, + 0x42, 0x43, 0x44, 0x22, 0x9a, 0x02, 0x01, 0x07, 0xa2, 0x02, 0x06, 0x73, 0x74, 0x72, 0x69, 0x6e, + 0x67, 0xfa, 0x42, 0x06, 0x7a, 0x04, 0x68, 0x04, 0x70, 0x01, 0xea, 0xaa, 0x19, 0x82, 0x01, 0x0a, + 0x3f, 0x67, 0x6f, 0x2e, 0x74, 0x68, 0x65, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x2e, 0x6e, 0x65, + 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2f, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2d, 0x73, 0x74, + 0x61, 0x63, 0x6b, 0x2f, 0x76, 0x33, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, + 0x2e, 0x4d, 0x61, 0x72, 0x73, 0x68, 0x61, 0x6c, 0x48, 0x45, 0x58, 0x42, 0x79, 0x74, 0x65, 0x73, + 0x12, 0x3f, 0x67, 0x6f, 0x2e, 0x74, 0x68, 0x65, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x2e, 0x6e, + 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2f, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2d, 0x73, + 0x74, 0x61, 0x63, 0x6b, 0x2f, 0x76, 0x33, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x79, 0x70, 0x65, + 0x73, 0x2e, 0x55, 0x6e, 0x6d, 0x61, 0x72, 0x73, 0x68, 0x61, 0x6c, 0x34, 0x42, 0x79, 0x74, 0x65, + 0x73, 0x52, 0x0e, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x44, 0x65, 0x76, 0x41, 0x64, 0x64, + 0x72, 0x12, 0x3d, 0x0a, 0x16, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x73, 0x65, 0x73, + 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x0c, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x7a, 0x03, 0x18, 0x80, 0x10, 0x52, 0x13, 0x70, 0x65, 0x6e, + 0x64, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x49, 0x64, + 0x12, 0x32, 0x0a, 0x16, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x6d, 0x69, 0x6e, 0x5f, + 0x66, 0x5f, 0x63, 0x6e, 0x74, 0x5f, 0x64, 0x6f, 0x77, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x12, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x4d, 0x69, 0x6e, 0x46, 0x43, 0x6e, 0x74, + 0x44, 0x6f, 0x77, 0x6e, 0x22, 0xf8, 0x04, 0x0a, 0x16, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x12, + 0x18, 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x2b, 0x0a, 0x04, 0x64, 0x61, 0x74, + 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, + 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x53, 0x0a, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x74, 0x74, 0x6e, 0x2e, + 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x69, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x61, 0x74, + 0x61, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x52, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x4c, 0x0a, 0x0b, 0x76, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x2b, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, + 0x33, 0x2e, 0x45, 0x6e, 0x64, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, + 0x6f, 0x6e, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x52, 0x0a, 0x76, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x73, 0x12, 0x43, 0x0a, 0x0b, 0x6e, 0x65, 0x74, + 0x77, 0x6f, 0x72, 0x6b, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, + 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, + 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, + 0x72, 0x73, 0x52, 0x0a, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x64, 0x73, 0x12, 0x8d, + 0x01, 0x0a, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x06, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x36, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, + 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x2e, 0x41, 0x74, 0x74, 0x72, + 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x42, 0x35, 0xfa, 0x42, 0x32, + 0x9a, 0x01, 0x2f, 0x10, 0x0a, 0x22, 0x24, 0x72, 0x22, 0x18, 0x24, 0x32, 0x1e, 0x5e, 0x5b, 0x61, + 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x28, 0x3f, 0x3a, 0x5b, 0x2d, 0x5d, 0x3f, 0x5b, 0x61, 0x2d, + 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x29, 0x7b, 0x32, 0x2c, 0x7d, 0x24, 0x2a, 0x05, 0x72, 0x03, 0x18, + 0xc8, 0x01, 0x52, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x1a, 0x56, + 0x0a, 0x0e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, + 0x65, 0x79, 0x12, 0x2e, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x18, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, + 0x76, 0x33, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x3d, 0x0a, 0x0f, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, + 0x75, 0x74, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x3a, 0x02, 0x38, 0x01, 0x3a, 0x08, 0xf2, 0xaa, 0x19, 0x04, 0x08, 0x01, 0x10, 0x00, 0x22, + 0x94, 0x09, 0x0a, 0x0d, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x55, + 0x70, 0x12, 0x54, 0x0a, 0x0e, 0x65, 0x6e, 0x64, 0x5f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, + 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x74, 0x74, 0x6e, 0x2e, + 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x45, 0x6e, 0x64, 0x44, 0x65, + 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x42, + 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x0c, 0x65, 0x6e, 0x64, 0x44, 0x65, + 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x73, 0x12, 0x35, 0x0a, 0x0f, 0x63, 0x6f, 0x72, 0x72, 0x65, + 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, + 0x42, 0x0c, 0xfa, 0x42, 0x09, 0x92, 0x01, 0x06, 0x22, 0x04, 0x72, 0x02, 0x18, 0x64, 0x52, 0x0e, + 0x63, 0x6f, 0x72, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x73, 0x12, 0x3b, + 0x0a, 0x0b, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x0c, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, + 0x0a, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x41, 0x74, 0x12, 0x4a, 0x0a, 0x0e, 0x75, + 0x70, 0x6c, 0x69, 0x6e, 0x6b, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, + 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x55, 0x70, 0x6c, 0x69, 0x6e, 0x6b, 0x48, 0x00, 0x52, 0x0d, 0x75, 0x70, 0x6c, 0x69, 0x6e, 0x6b, + 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x5a, 0x0a, 0x11, 0x75, 0x70, 0x6c, 0x69, 0x6e, + 0x6b, 0x5f, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x18, 0x0f, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, + 0x2e, 0x76, 0x33, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x55, + 0x70, 0x6c, 0x69, 0x6e, 0x6b, 0x4e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x48, + 0x00, 0x52, 0x10, 0x75, 0x70, 0x6c, 0x69, 0x6e, 0x6b, 0x4e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x69, + 0x7a, 0x65, 0x64, 0x12, 0x48, 0x0a, 0x0b, 0x6a, 0x6f, 0x69, 0x6e, 0x5f, 0x61, 0x63, 0x63, 0x65, + 0x70, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, + 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4a, 0x6f, 0x69, 0x6e, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x48, + 0x00, 0x52, 0x0a, 0x6a, 0x6f, 0x69, 0x6e, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x12, 0x48, 0x0a, + 0x0c, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x5f, 0x61, 0x63, 0x6b, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, + 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x48, 0x00, 0x52, 0x0b, 0x64, 0x6f, 0x77, 0x6e, + 0x6c, 0x69, 0x6e, 0x6b, 0x41, 0x63, 0x6b, 0x12, 0x4a, 0x0a, 0x0d, 0x64, 0x6f, 0x77, 0x6e, 0x6c, + 0x69, 0x6e, 0x6b, 0x5f, 0x6e, 0x61, 0x63, 0x6b, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, + 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, + 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x6f, 0x77, 0x6e, 0x6c, + 0x69, 0x6e, 0x6b, 0x48, 0x00, 0x52, 0x0c, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x4e, + 0x61, 0x63, 0x6b, 0x12, 0x4a, 0x0a, 0x0d, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x5f, + 0x73, 0x65, 0x6e, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x74, 0x74, 0x6e, + 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x41, 0x70, 0x70, 0x6c, + 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x48, + 0x00, 0x52, 0x0c, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x53, 0x65, 0x6e, 0x74, 0x12, + 0x54, 0x0a, 0x0f, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x5f, 0x66, 0x61, 0x69, 0x6c, + 0x65, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, + 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x46, 0x61, 0x69, + 0x6c, 0x65, 0x64, 0x48, 0x00, 0x52, 0x0e, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x46, + 0x61, 0x69, 0x6c, 0x65, 0x64, 0x12, 0x4e, 0x0a, 0x0f, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, + 0x6b, 0x5f, 0x71, 0x75, 0x65, 0x75, 0x65, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, + 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, + 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x6f, 0x77, 0x6e, 0x6c, + 0x69, 0x6e, 0x6b, 0x48, 0x00, 0x52, 0x0e, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x51, + 0x75, 0x65, 0x75, 0x65, 0x64, 0x12, 0x6f, 0x0a, 0x1a, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, + 0x6b, 0x5f, 0x71, 0x75, 0x65, 0x75, 0x65, 0x5f, 0x69, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, + 0x74, 0x65, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x74, 0x74, 0x6e, 0x2e, + 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x69, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, + 0x64, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x73, 0x48, 0x00, 0x52, 0x18, 0x64, 0x6f, + 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x51, 0x75, 0x65, 0x75, 0x65, 0x49, 0x6e, 0x76, 0x61, 0x6c, + 0x69, 0x64, 0x61, 0x74, 0x65, 0x64, 0x12, 0x4e, 0x0a, 0x0f, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x5f, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x64, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x23, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, + 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x6f, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, 0x0e, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x53, 0x6f, 0x6c, 0x76, 0x65, 0x64, 0x12, 0x4b, 0x0a, 0x0c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x74, + 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x41, 0x70, + 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x44, 0x61, 0x74, 0x61, 0x48, 0x00, 0x52, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, + 0x61, 0x74, 0x61, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x64, + 0x18, 0x0e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x73, 0x69, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, + 0x64, 0x3a, 0x08, 0xf2, 0xaa, 0x19, 0x04, 0x08, 0x01, 0x10, 0x00, 0x42, 0x09, 0x0a, 0x02, 0x75, + 0x70, 0x12, 0x03, 0xf8, 0x42, 0x01, 0x22, 0xcc, 0x02, 0x0a, 0x18, 0x4d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x74, + 0x65, 0x72, 0x73, 0x12, 0x4d, 0x0a, 0x0c, 0x75, 0x70, 0x5f, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, + 0x74, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x74, 0x74, 0x6e, 0x2e, + 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x50, 0x61, 0x79, 0x6c, 0x6f, + 0x61, 0x64, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x74, 0x65, 0x72, 0x42, 0x08, 0xfa, 0x42, 0x05, + 0x82, 0x01, 0x02, 0x10, 0x01, 0x52, 0x0b, 0x75, 0x70, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x74, + 0x65, 0x72, 0x12, 0x3f, 0x0a, 0x16, 0x75, 0x70, 0x5f, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x74, + 0x65, 0x72, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x42, 0x09, 0xfa, 0x42, 0x06, 0x72, 0x04, 0x18, 0x80, 0xc0, 0x02, 0x52, 0x14, 0x75, + 0x70, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x74, 0x65, 0x72, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, + 0x74, 0x65, 0x72, 0x12, 0x51, 0x0a, 0x0e, 0x64, 0x6f, 0x77, 0x6e, 0x5f, 0x66, 0x6f, 0x72, 0x6d, + 0x61, 0x74, 0x74, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x74, 0x74, + 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x50, 0x61, 0x79, + 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x74, 0x65, 0x72, 0x42, 0x08, 0xfa, + 0x42, 0x05, 0x82, 0x01, 0x02, 0x10, 0x01, 0x52, 0x0d, 0x64, 0x6f, 0x77, 0x6e, 0x46, 0x6f, 0x72, + 0x6d, 0x61, 0x74, 0x74, 0x65, 0x72, 0x12, 0x43, 0x0a, 0x18, 0x64, 0x6f, 0x77, 0x6e, 0x5f, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x74, 0x65, 0x72, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, - 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x09, 0xfa, 0x42, 0x06, 0x72, 0x04, 0x18, - 0x80, 0xc0, 0x02, 0x52, 0x14, 0x75, 0x70, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x74, 0x65, 0x72, - 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x12, 0x51, 0x0a, 0x0e, 0x64, 0x6f, 0x77, - 0x6e, 0x5f, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x74, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x0e, 0x32, 0x20, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, - 0x76, 0x33, 0x2e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, - 0x74, 0x65, 0x72, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x82, 0x01, 0x02, 0x10, 0x01, 0x52, 0x0d, 0x64, - 0x6f, 0x77, 0x6e, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x74, 0x65, 0x72, 0x12, 0x43, 0x0a, 0x18, - 0x64, 0x6f, 0x77, 0x6e, 0x5f, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x74, 0x65, 0x72, 0x5f, 0x70, - 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x42, 0x09, - 0xfa, 0x42, 0x06, 0x72, 0x04, 0x18, 0x80, 0xc0, 0x02, 0x52, 0x16, 0x64, 0x6f, 0x77, 0x6e, 0x46, - 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x74, 0x65, 0x72, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, - 0x72, 0x3a, 0x08, 0xf2, 0xaa, 0x19, 0x04, 0x08, 0x01, 0x10, 0x01, 0x22, 0xbb, 0x01, 0x0a, 0x14, - 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x51, 0x75, 0x65, 0x75, 0x65, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x54, 0x0a, 0x0e, 0x65, 0x6e, 0x64, 0x5f, 0x64, 0x65, 0x76, 0x69, - 0x63, 0x65, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x74, - 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x45, 0x6e, - 0x64, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, - 0x72, 0x73, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x0c, 0x65, 0x6e, - 0x64, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x73, 0x12, 0x4d, 0x0a, 0x09, 0x64, 0x6f, - 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, - 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x41, - 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69, - 0x6e, 0x6b, 0x42, 0x0a, 0xfa, 0x42, 0x07, 0x92, 0x01, 0x04, 0x10, 0xa0, 0x8d, 0x06, 0x52, 0x09, - 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x73, 0x2a, 0xa3, 0x01, 0x0a, 0x10, 0x50, 0x61, - 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x74, 0x65, 0x72, 0x12, 0x12, - 0x0a, 0x0e, 0x46, 0x4f, 0x52, 0x4d, 0x41, 0x54, 0x54, 0x45, 0x52, 0x5f, 0x4e, 0x4f, 0x4e, 0x45, - 0x10, 0x00, 0x12, 0x18, 0x0a, 0x14, 0x46, 0x4f, 0x52, 0x4d, 0x41, 0x54, 0x54, 0x45, 0x52, 0x5f, - 0x52, 0x45, 0x50, 0x4f, 0x53, 0x49, 0x54, 0x4f, 0x52, 0x59, 0x10, 0x01, 0x12, 0x1a, 0x0a, 0x16, - 0x46, 0x4f, 0x52, 0x4d, 0x41, 0x54, 0x54, 0x45, 0x52, 0x5f, 0x47, 0x52, 0x50, 0x43, 0x5f, 0x53, - 0x45, 0x52, 0x56, 0x49, 0x43, 0x45, 0x10, 0x02, 0x12, 0x18, 0x0a, 0x14, 0x46, 0x4f, 0x52, 0x4d, - 0x41, 0x54, 0x54, 0x45, 0x52, 0x5f, 0x4a, 0x41, 0x56, 0x41, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, - 0x10, 0x03, 0x12, 0x18, 0x0a, 0x14, 0x46, 0x4f, 0x52, 0x4d, 0x41, 0x54, 0x54, 0x45, 0x52, 0x5f, - 0x43, 0x41, 0x59, 0x45, 0x4e, 0x4e, 0x45, 0x4c, 0x50, 0x50, 0x10, 0x04, 0x1a, 0x11, 0xea, 0xaa, - 0x19, 0x0d, 0x18, 0x01, 0x2a, 0x09, 0x46, 0x4f, 0x52, 0x4d, 0x41, 0x54, 0x54, 0x45, 0x52, 0x42, - 0x31, 0x5a, 0x2f, 0x67, 0x6f, 0x2e, 0x74, 0x68, 0x65, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x2e, - 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2f, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2d, - 0x73, 0x74, 0x61, 0x63, 0x6b, 0x2f, 0x76, 0x33, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x74, 0x6e, - 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x42, 0x09, 0xfa, 0x42, 0x06, 0x72, 0x04, 0x18, + 0x80, 0xc0, 0x02, 0x52, 0x16, 0x64, 0x6f, 0x77, 0x6e, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x74, + 0x65, 0x72, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x3a, 0x08, 0xf2, 0xaa, 0x19, + 0x04, 0x08, 0x01, 0x10, 0x01, 0x22, 0xbb, 0x01, 0x0a, 0x14, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69, + 0x6e, 0x6b, 0x51, 0x75, 0x65, 0x75, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x54, + 0x0a, 0x0e, 0x65, 0x6e, 0x64, 0x5f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x73, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, + 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x45, 0x6e, 0x64, 0x44, 0x65, 0x76, 0x69, 0x63, + 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x42, 0x08, 0xfa, 0x42, + 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x0c, 0x65, 0x6e, 0x64, 0x44, 0x65, 0x76, 0x69, 0x63, + 0x65, 0x49, 0x64, 0x73, 0x12, 0x4d, 0x0a, 0x09, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, + 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, + 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x42, 0x0a, 0xfa, 0x42, + 0x07, 0x92, 0x01, 0x04, 0x10, 0xa0, 0x8d, 0x06, 0x52, 0x09, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x69, + 0x6e, 0x6b, 0x73, 0x2a, 0xa3, 0x01, 0x0a, 0x10, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x46, + 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x74, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x0e, 0x46, 0x4f, 0x52, 0x4d, + 0x41, 0x54, 0x54, 0x45, 0x52, 0x5f, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x00, 0x12, 0x18, 0x0a, 0x14, + 0x46, 0x4f, 0x52, 0x4d, 0x41, 0x54, 0x54, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x50, 0x4f, 0x53, 0x49, + 0x54, 0x4f, 0x52, 0x59, 0x10, 0x01, 0x12, 0x1a, 0x0a, 0x16, 0x46, 0x4f, 0x52, 0x4d, 0x41, 0x54, + 0x54, 0x45, 0x52, 0x5f, 0x47, 0x52, 0x50, 0x43, 0x5f, 0x53, 0x45, 0x52, 0x56, 0x49, 0x43, 0x45, + 0x10, 0x02, 0x12, 0x18, 0x0a, 0x14, 0x46, 0x4f, 0x52, 0x4d, 0x41, 0x54, 0x54, 0x45, 0x52, 0x5f, + 0x4a, 0x41, 0x56, 0x41, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x10, 0x03, 0x12, 0x18, 0x0a, 0x14, + 0x46, 0x4f, 0x52, 0x4d, 0x41, 0x54, 0x54, 0x45, 0x52, 0x5f, 0x43, 0x41, 0x59, 0x45, 0x4e, 0x4e, + 0x45, 0x4c, 0x50, 0x50, 0x10, 0x04, 0x1a, 0x11, 0xea, 0xaa, 0x19, 0x0d, 0x18, 0x01, 0x2a, 0x09, + 0x46, 0x4f, 0x52, 0x4d, 0x41, 0x54, 0x54, 0x45, 0x52, 0x42, 0x31, 0x5a, 0x2f, 0x67, 0x6f, 0x2e, + 0x74, 0x68, 0x65, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x2e, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, + 0x6b, 0x2f, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2d, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x2f, + 0x76, 0x33, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x74, 0x6e, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -2761,7 +3151,7 @@ func file_ttn_lorawan_v3_messages_proto_rawDescGZIP() []byte { } var file_ttn_lorawan_v3_messages_proto_enumTypes = make([]protoimpl.EnumInfo, 2) -var file_ttn_lorawan_v3_messages_proto_msgTypes = make([]protoimpl.MessageInfo, 24) +var file_ttn_lorawan_v3_messages_proto_msgTypes = make([]protoimpl.MessageInfo, 36) var file_ttn_lorawan_v3_messages_proto_goTypes = []interface{}{ (PayloadFormatter)(0), // 0: ttn.lorawan.v3.PayloadFormatter (TxAcknowledgment_Result)(0), // 1: ttn.lorawan.v3.TxAcknowledgment.Result @@ -2785,108 +3175,147 @@ var file_ttn_lorawan_v3_messages_proto_goTypes = []interface{}{ (*MessagePayloadFormatters)(nil), // 19: ttn.lorawan.v3.MessagePayloadFormatters (*DownlinkQueueRequest)(nil), // 20: ttn.lorawan.v3.DownlinkQueueRequest nil, // 21: ttn.lorawan.v3.ApplicationUplink.LocationsEntry - nil, // 22: ttn.lorawan.v3.ApplicationUplinkNormalized.LocationsEntry - nil, // 23: ttn.lorawan.v3.ApplicationLocation.AttributesEntry - (*ApplicationDownlink_ClassBC)(nil), // 24: ttn.lorawan.v3.ApplicationDownlink.ClassBC - (*ApplicationDownlink_ConfirmedRetry)(nil), // 25: ttn.lorawan.v3.ApplicationDownlink.ConfirmedRetry - (*Message)(nil), // 26: ttn.lorawan.v3.Message - (*TxSettings)(nil), // 27: ttn.lorawan.v3.TxSettings - (*RxMetadata)(nil), // 28: ttn.lorawan.v3.RxMetadata - (*timestamppb.Timestamp)(nil), // 29: google.protobuf.Timestamp - (*durationpb.Duration)(nil), // 30: google.protobuf.Duration - (*wrapperspb.BoolValue)(nil), // 31: google.protobuf.BoolValue - (*EndDeviceIdentifiers)(nil), // 32: ttn.lorawan.v3.EndDeviceIdentifiers - (*TxRequest)(nil), // 33: ttn.lorawan.v3.TxRequest - (*GatewayIdentifiers)(nil), // 34: ttn.lorawan.v3.GatewayIdentifiers - (*wrapperspb.FloatValue)(nil), // 35: google.protobuf.FloatValue - (*structpb.Struct)(nil), // 36: google.protobuf.Struct - (*KeyEnvelope)(nil), // 37: ttn.lorawan.v3.KeyEnvelope - (*EndDeviceVersionIdentifiers)(nil), // 38: ttn.lorawan.v3.EndDeviceVersionIdentifiers - (*NetworkIdentifiers)(nil), // 39: ttn.lorawan.v3.NetworkIdentifiers - (*Location)(nil), // 40: ttn.lorawan.v3.Location - (TxSchedulePriority)(0), // 41: ttn.lorawan.v3.TxSchedulePriority - (*ErrorDetails)(nil), // 42: ttn.lorawan.v3.ErrorDetails - (*ClassBCGatewayIdentifiers)(nil), // 43: ttn.lorawan.v3.ClassBCGatewayIdentifiers - (*wrapperspb.UInt32Value)(nil), // 44: google.protobuf.UInt32Value + nil, // 22: ttn.lorawan.v3.ApplicationUplink.AttributesEntry + nil, // 23: ttn.lorawan.v3.ApplicationUplinkNormalized.LocationsEntry + nil, // 24: ttn.lorawan.v3.ApplicationUplinkNormalized.AttributesEntry + nil, // 25: ttn.lorawan.v3.ApplicationLocation.AttributesEntry + nil, // 26: ttn.lorawan.v3.ApplicationJoinAccept.LocationsEntry + nil, // 27: ttn.lorawan.v3.ApplicationJoinAccept.AttributesEntry + (*ApplicationDownlink_ClassBC)(nil), // 28: ttn.lorawan.v3.ApplicationDownlink.ClassBC + (*ApplicationDownlink_ConfirmedRetry)(nil), // 29: ttn.lorawan.v3.ApplicationDownlink.ConfirmedRetry + nil, // 30: ttn.lorawan.v3.ApplicationDownlink.LocationsEntry + nil, // 31: ttn.lorawan.v3.ApplicationDownlink.AttributesEntry + nil, // 32: ttn.lorawan.v3.ApplicationDownlinkFailed.LocationsEntry + nil, // 33: ttn.lorawan.v3.ApplicationDownlinkFailed.AttributesEntry + nil, // 34: ttn.lorawan.v3.ApplicationInvalidatedDownlinks.LocationsEntry + nil, // 35: ttn.lorawan.v3.ApplicationInvalidatedDownlinks.AttributesEntry + nil, // 36: ttn.lorawan.v3.ApplicationServiceData.LocationsEntry + nil, // 37: ttn.lorawan.v3.ApplicationServiceData.AttributesEntry + (*Message)(nil), // 38: ttn.lorawan.v3.Message + (*TxSettings)(nil), // 39: ttn.lorawan.v3.TxSettings + (*RxMetadata)(nil), // 40: ttn.lorawan.v3.RxMetadata + (*timestamppb.Timestamp)(nil), // 41: google.protobuf.Timestamp + (*durationpb.Duration)(nil), // 42: google.protobuf.Duration + (*wrapperspb.BoolValue)(nil), // 43: google.protobuf.BoolValue + (*EndDeviceIdentifiers)(nil), // 44: ttn.lorawan.v3.EndDeviceIdentifiers + (*TxRequest)(nil), // 45: ttn.lorawan.v3.TxRequest + (*GatewayIdentifiers)(nil), // 46: ttn.lorawan.v3.GatewayIdentifiers + (*wrapperspb.FloatValue)(nil), // 47: google.protobuf.FloatValue + (*structpb.Struct)(nil), // 48: google.protobuf.Struct + (*KeyEnvelope)(nil), // 49: ttn.lorawan.v3.KeyEnvelope + (*EndDeviceVersionIdentifiers)(nil), // 50: ttn.lorawan.v3.EndDeviceVersionIdentifiers + (*NetworkIdentifiers)(nil), // 51: ttn.lorawan.v3.NetworkIdentifiers + (*Location)(nil), // 52: ttn.lorawan.v3.Location + (TxSchedulePriority)(0), // 53: ttn.lorawan.v3.TxSchedulePriority + (*ErrorDetails)(nil), // 54: ttn.lorawan.v3.ErrorDetails + (*ClassBCGatewayIdentifiers)(nil), // 55: ttn.lorawan.v3.ClassBCGatewayIdentifiers + (*wrapperspb.UInt32Value)(nil), // 56: google.protobuf.UInt32Value } var file_ttn_lorawan_v3_messages_proto_depIdxs = []int32{ - 26, // 0: ttn.lorawan.v3.UplinkMessage.payload:type_name -> ttn.lorawan.v3.Message - 27, // 1: ttn.lorawan.v3.UplinkMessage.settings:type_name -> ttn.lorawan.v3.TxSettings - 28, // 2: ttn.lorawan.v3.UplinkMessage.rx_metadata:type_name -> ttn.lorawan.v3.RxMetadata - 29, // 3: ttn.lorawan.v3.UplinkMessage.received_at:type_name -> google.protobuf.Timestamp - 30, // 4: ttn.lorawan.v3.UplinkMessage.consumed_airtime:type_name -> google.protobuf.Duration - 31, // 5: ttn.lorawan.v3.UplinkMessage.crc_status:type_name -> google.protobuf.BoolValue - 26, // 6: ttn.lorawan.v3.DownlinkMessage.payload:type_name -> ttn.lorawan.v3.Message - 32, // 7: ttn.lorawan.v3.DownlinkMessage.end_device_ids:type_name -> ttn.lorawan.v3.EndDeviceIdentifiers - 33, // 8: ttn.lorawan.v3.DownlinkMessage.request:type_name -> ttn.lorawan.v3.TxRequest - 27, // 9: ttn.lorawan.v3.DownlinkMessage.scheduled:type_name -> ttn.lorawan.v3.TxSettings + 38, // 0: ttn.lorawan.v3.UplinkMessage.payload:type_name -> ttn.lorawan.v3.Message + 39, // 1: ttn.lorawan.v3.UplinkMessage.settings:type_name -> ttn.lorawan.v3.TxSettings + 40, // 2: ttn.lorawan.v3.UplinkMessage.rx_metadata:type_name -> ttn.lorawan.v3.RxMetadata + 41, // 3: ttn.lorawan.v3.UplinkMessage.received_at:type_name -> google.protobuf.Timestamp + 42, // 4: ttn.lorawan.v3.UplinkMessage.consumed_airtime:type_name -> google.protobuf.Duration + 43, // 5: ttn.lorawan.v3.UplinkMessage.crc_status:type_name -> google.protobuf.BoolValue + 38, // 6: ttn.lorawan.v3.DownlinkMessage.payload:type_name -> ttn.lorawan.v3.Message + 44, // 7: ttn.lorawan.v3.DownlinkMessage.end_device_ids:type_name -> ttn.lorawan.v3.EndDeviceIdentifiers + 45, // 8: ttn.lorawan.v3.DownlinkMessage.request:type_name -> ttn.lorawan.v3.TxRequest + 39, // 9: ttn.lorawan.v3.DownlinkMessage.scheduled:type_name -> ttn.lorawan.v3.TxSettings 1, // 10: ttn.lorawan.v3.TxAcknowledgment.result:type_name -> ttn.lorawan.v3.TxAcknowledgment.Result 3, // 11: ttn.lorawan.v3.TxAcknowledgment.downlink_message:type_name -> ttn.lorawan.v3.DownlinkMessage - 34, // 12: ttn.lorawan.v3.GatewayTxAcknowledgment.gateway_ids:type_name -> ttn.lorawan.v3.GatewayIdentifiers + 46, // 12: ttn.lorawan.v3.GatewayTxAcknowledgment.gateway_ids:type_name -> ttn.lorawan.v3.GatewayIdentifiers 4, // 13: ttn.lorawan.v3.GatewayTxAcknowledgment.tx_ack:type_name -> ttn.lorawan.v3.TxAcknowledgment 2, // 14: ttn.lorawan.v3.GatewayUplinkMessage.message:type_name -> ttn.lorawan.v3.UplinkMessage - 35, // 15: ttn.lorawan.v3.LastBatteryPercentage.value:type_name -> google.protobuf.FloatValue - 29, // 16: ttn.lorawan.v3.LastBatteryPercentage.received_at:type_name -> google.protobuf.Timestamp - 36, // 17: ttn.lorawan.v3.ApplicationUplink.decoded_payload:type_name -> google.protobuf.Struct - 36, // 18: ttn.lorawan.v3.ApplicationUplink.normalized_payload:type_name -> google.protobuf.Struct - 28, // 19: ttn.lorawan.v3.ApplicationUplink.rx_metadata:type_name -> ttn.lorawan.v3.RxMetadata - 27, // 20: ttn.lorawan.v3.ApplicationUplink.settings:type_name -> ttn.lorawan.v3.TxSettings - 29, // 21: ttn.lorawan.v3.ApplicationUplink.received_at:type_name -> google.protobuf.Timestamp - 37, // 22: ttn.lorawan.v3.ApplicationUplink.app_s_key:type_name -> ttn.lorawan.v3.KeyEnvelope - 30, // 23: ttn.lorawan.v3.ApplicationUplink.consumed_airtime:type_name -> google.protobuf.Duration + 47, // 15: ttn.lorawan.v3.LastBatteryPercentage.value:type_name -> google.protobuf.FloatValue + 41, // 16: ttn.lorawan.v3.LastBatteryPercentage.received_at:type_name -> google.protobuf.Timestamp + 48, // 17: ttn.lorawan.v3.ApplicationUplink.decoded_payload:type_name -> google.protobuf.Struct + 48, // 18: ttn.lorawan.v3.ApplicationUplink.normalized_payload:type_name -> google.protobuf.Struct + 40, // 19: ttn.lorawan.v3.ApplicationUplink.rx_metadata:type_name -> ttn.lorawan.v3.RxMetadata + 39, // 20: ttn.lorawan.v3.ApplicationUplink.settings:type_name -> ttn.lorawan.v3.TxSettings + 41, // 21: ttn.lorawan.v3.ApplicationUplink.received_at:type_name -> google.protobuf.Timestamp + 49, // 22: ttn.lorawan.v3.ApplicationUplink.app_s_key:type_name -> ttn.lorawan.v3.KeyEnvelope + 42, // 23: ttn.lorawan.v3.ApplicationUplink.consumed_airtime:type_name -> google.protobuf.Duration 21, // 24: ttn.lorawan.v3.ApplicationUplink.locations:type_name -> ttn.lorawan.v3.ApplicationUplink.LocationsEntry - 38, // 25: ttn.lorawan.v3.ApplicationUplink.version_ids:type_name -> ttn.lorawan.v3.EndDeviceVersionIdentifiers - 39, // 26: ttn.lorawan.v3.ApplicationUplink.network_ids:type_name -> ttn.lorawan.v3.NetworkIdentifiers + 50, // 25: ttn.lorawan.v3.ApplicationUplink.version_ids:type_name -> ttn.lorawan.v3.EndDeviceVersionIdentifiers + 51, // 26: ttn.lorawan.v3.ApplicationUplink.network_ids:type_name -> ttn.lorawan.v3.NetworkIdentifiers 7, // 27: ttn.lorawan.v3.ApplicationUplink.last_battery_percentage:type_name -> ttn.lorawan.v3.LastBatteryPercentage - 36, // 28: ttn.lorawan.v3.ApplicationUplinkNormalized.normalized_payload:type_name -> google.protobuf.Struct - 28, // 29: ttn.lorawan.v3.ApplicationUplinkNormalized.rx_metadata:type_name -> ttn.lorawan.v3.RxMetadata - 27, // 30: ttn.lorawan.v3.ApplicationUplinkNormalized.settings:type_name -> ttn.lorawan.v3.TxSettings - 29, // 31: ttn.lorawan.v3.ApplicationUplinkNormalized.received_at:type_name -> google.protobuf.Timestamp - 30, // 32: ttn.lorawan.v3.ApplicationUplinkNormalized.consumed_airtime:type_name -> google.protobuf.Duration - 22, // 33: ttn.lorawan.v3.ApplicationUplinkNormalized.locations:type_name -> ttn.lorawan.v3.ApplicationUplinkNormalized.LocationsEntry - 38, // 34: ttn.lorawan.v3.ApplicationUplinkNormalized.version_ids:type_name -> ttn.lorawan.v3.EndDeviceVersionIdentifiers - 39, // 35: ttn.lorawan.v3.ApplicationUplinkNormalized.network_ids:type_name -> ttn.lorawan.v3.NetworkIdentifiers - 40, // 36: ttn.lorawan.v3.ApplicationLocation.location:type_name -> ttn.lorawan.v3.Location - 23, // 37: ttn.lorawan.v3.ApplicationLocation.attributes:type_name -> ttn.lorawan.v3.ApplicationLocation.AttributesEntry - 37, // 38: ttn.lorawan.v3.ApplicationJoinAccept.app_s_key:type_name -> ttn.lorawan.v3.KeyEnvelope - 12, // 39: ttn.lorawan.v3.ApplicationJoinAccept.invalidated_downlinks:type_name -> ttn.lorawan.v3.ApplicationDownlink - 29, // 40: ttn.lorawan.v3.ApplicationJoinAccept.received_at:type_name -> google.protobuf.Timestamp - 36, // 41: ttn.lorawan.v3.ApplicationDownlink.decoded_payload:type_name -> google.protobuf.Struct - 24, // 42: ttn.lorawan.v3.ApplicationDownlink.class_b_c:type_name -> ttn.lorawan.v3.ApplicationDownlink.ClassBC - 41, // 43: ttn.lorawan.v3.ApplicationDownlink.priority:type_name -> ttn.lorawan.v3.TxSchedulePriority - 25, // 44: ttn.lorawan.v3.ApplicationDownlink.confirmed_retry:type_name -> ttn.lorawan.v3.ApplicationDownlink.ConfirmedRetry - 12, // 45: ttn.lorawan.v3.ApplicationDownlinks.downlinks:type_name -> ttn.lorawan.v3.ApplicationDownlink - 12, // 46: ttn.lorawan.v3.ApplicationDownlinkFailed.downlink:type_name -> ttn.lorawan.v3.ApplicationDownlink - 42, // 47: ttn.lorawan.v3.ApplicationDownlinkFailed.error:type_name -> ttn.lorawan.v3.ErrorDetails - 12, // 48: ttn.lorawan.v3.ApplicationInvalidatedDownlinks.downlinks:type_name -> ttn.lorawan.v3.ApplicationDownlink - 36, // 49: ttn.lorawan.v3.ApplicationServiceData.data:type_name -> google.protobuf.Struct - 32, // 50: ttn.lorawan.v3.ApplicationUp.end_device_ids:type_name -> ttn.lorawan.v3.EndDeviceIdentifiers - 29, // 51: ttn.lorawan.v3.ApplicationUp.received_at:type_name -> google.protobuf.Timestamp - 8, // 52: ttn.lorawan.v3.ApplicationUp.uplink_message:type_name -> ttn.lorawan.v3.ApplicationUplink - 9, // 53: ttn.lorawan.v3.ApplicationUp.uplink_normalized:type_name -> ttn.lorawan.v3.ApplicationUplinkNormalized - 11, // 54: ttn.lorawan.v3.ApplicationUp.join_accept:type_name -> ttn.lorawan.v3.ApplicationJoinAccept - 12, // 55: ttn.lorawan.v3.ApplicationUp.downlink_ack:type_name -> ttn.lorawan.v3.ApplicationDownlink - 12, // 56: ttn.lorawan.v3.ApplicationUp.downlink_nack:type_name -> ttn.lorawan.v3.ApplicationDownlink - 12, // 57: ttn.lorawan.v3.ApplicationUp.downlink_sent:type_name -> ttn.lorawan.v3.ApplicationDownlink - 14, // 58: ttn.lorawan.v3.ApplicationUp.downlink_failed:type_name -> ttn.lorawan.v3.ApplicationDownlinkFailed - 12, // 59: ttn.lorawan.v3.ApplicationUp.downlink_queued:type_name -> ttn.lorawan.v3.ApplicationDownlink - 15, // 60: ttn.lorawan.v3.ApplicationUp.downlink_queue_invalidated:type_name -> ttn.lorawan.v3.ApplicationInvalidatedDownlinks - 10, // 61: ttn.lorawan.v3.ApplicationUp.location_solved:type_name -> ttn.lorawan.v3.ApplicationLocation - 17, // 62: ttn.lorawan.v3.ApplicationUp.service_data:type_name -> ttn.lorawan.v3.ApplicationServiceData - 0, // 63: ttn.lorawan.v3.MessagePayloadFormatters.up_formatter:type_name -> ttn.lorawan.v3.PayloadFormatter - 0, // 64: ttn.lorawan.v3.MessagePayloadFormatters.down_formatter:type_name -> ttn.lorawan.v3.PayloadFormatter - 32, // 65: ttn.lorawan.v3.DownlinkQueueRequest.end_device_ids:type_name -> ttn.lorawan.v3.EndDeviceIdentifiers - 12, // 66: ttn.lorawan.v3.DownlinkQueueRequest.downlinks:type_name -> ttn.lorawan.v3.ApplicationDownlink - 40, // 67: ttn.lorawan.v3.ApplicationUplink.LocationsEntry.value:type_name -> ttn.lorawan.v3.Location - 40, // 68: ttn.lorawan.v3.ApplicationUplinkNormalized.LocationsEntry.value:type_name -> ttn.lorawan.v3.Location - 43, // 69: ttn.lorawan.v3.ApplicationDownlink.ClassBC.gateways:type_name -> ttn.lorawan.v3.ClassBCGatewayIdentifiers - 29, // 70: ttn.lorawan.v3.ApplicationDownlink.ClassBC.absolute_time:type_name -> google.protobuf.Timestamp - 44, // 71: ttn.lorawan.v3.ApplicationDownlink.ConfirmedRetry.max_attempts:type_name -> google.protobuf.UInt32Value - 72, // [72:72] is the sub-list for method output_type - 72, // [72:72] is the sub-list for method input_type - 72, // [72:72] is the sub-list for extension type_name - 72, // [72:72] is the sub-list for extension extendee - 0, // [0:72] is the sub-list for field type_name + 22, // 28: ttn.lorawan.v3.ApplicationUplink.attributes:type_name -> ttn.lorawan.v3.ApplicationUplink.AttributesEntry + 48, // 29: ttn.lorawan.v3.ApplicationUplinkNormalized.normalized_payload:type_name -> google.protobuf.Struct + 40, // 30: ttn.lorawan.v3.ApplicationUplinkNormalized.rx_metadata:type_name -> ttn.lorawan.v3.RxMetadata + 39, // 31: ttn.lorawan.v3.ApplicationUplinkNormalized.settings:type_name -> ttn.lorawan.v3.TxSettings + 41, // 32: ttn.lorawan.v3.ApplicationUplinkNormalized.received_at:type_name -> google.protobuf.Timestamp + 42, // 33: ttn.lorawan.v3.ApplicationUplinkNormalized.consumed_airtime:type_name -> google.protobuf.Duration + 23, // 34: ttn.lorawan.v3.ApplicationUplinkNormalized.locations:type_name -> ttn.lorawan.v3.ApplicationUplinkNormalized.LocationsEntry + 50, // 35: ttn.lorawan.v3.ApplicationUplinkNormalized.version_ids:type_name -> ttn.lorawan.v3.EndDeviceVersionIdentifiers + 51, // 36: ttn.lorawan.v3.ApplicationUplinkNormalized.network_ids:type_name -> ttn.lorawan.v3.NetworkIdentifiers + 24, // 37: ttn.lorawan.v3.ApplicationUplinkNormalized.attributes:type_name -> ttn.lorawan.v3.ApplicationUplinkNormalized.AttributesEntry + 52, // 38: ttn.lorawan.v3.ApplicationLocation.location:type_name -> ttn.lorawan.v3.Location + 25, // 39: ttn.lorawan.v3.ApplicationLocation.attributes:type_name -> ttn.lorawan.v3.ApplicationLocation.AttributesEntry + 49, // 40: ttn.lorawan.v3.ApplicationJoinAccept.app_s_key:type_name -> ttn.lorawan.v3.KeyEnvelope + 12, // 41: ttn.lorawan.v3.ApplicationJoinAccept.invalidated_downlinks:type_name -> ttn.lorawan.v3.ApplicationDownlink + 41, // 42: ttn.lorawan.v3.ApplicationJoinAccept.received_at:type_name -> google.protobuf.Timestamp + 26, // 43: ttn.lorawan.v3.ApplicationJoinAccept.locations:type_name -> ttn.lorawan.v3.ApplicationJoinAccept.LocationsEntry + 50, // 44: ttn.lorawan.v3.ApplicationJoinAccept.version_ids:type_name -> ttn.lorawan.v3.EndDeviceVersionIdentifiers + 51, // 45: ttn.lorawan.v3.ApplicationJoinAccept.network_ids:type_name -> ttn.lorawan.v3.NetworkIdentifiers + 27, // 46: ttn.lorawan.v3.ApplicationJoinAccept.attributes:type_name -> ttn.lorawan.v3.ApplicationJoinAccept.AttributesEntry + 48, // 47: ttn.lorawan.v3.ApplicationDownlink.decoded_payload:type_name -> google.protobuf.Struct + 28, // 48: ttn.lorawan.v3.ApplicationDownlink.class_b_c:type_name -> ttn.lorawan.v3.ApplicationDownlink.ClassBC + 53, // 49: ttn.lorawan.v3.ApplicationDownlink.priority:type_name -> ttn.lorawan.v3.TxSchedulePriority + 29, // 50: ttn.lorawan.v3.ApplicationDownlink.confirmed_retry:type_name -> ttn.lorawan.v3.ApplicationDownlink.ConfirmedRetry + 30, // 51: ttn.lorawan.v3.ApplicationDownlink.locations:type_name -> ttn.lorawan.v3.ApplicationDownlink.LocationsEntry + 50, // 52: ttn.lorawan.v3.ApplicationDownlink.version_ids:type_name -> ttn.lorawan.v3.EndDeviceVersionIdentifiers + 51, // 53: ttn.lorawan.v3.ApplicationDownlink.network_ids:type_name -> ttn.lorawan.v3.NetworkIdentifiers + 31, // 54: ttn.lorawan.v3.ApplicationDownlink.attributes:type_name -> ttn.lorawan.v3.ApplicationDownlink.AttributesEntry + 12, // 55: ttn.lorawan.v3.ApplicationDownlinks.downlinks:type_name -> ttn.lorawan.v3.ApplicationDownlink + 12, // 56: ttn.lorawan.v3.ApplicationDownlinkFailed.downlink:type_name -> ttn.lorawan.v3.ApplicationDownlink + 54, // 57: ttn.lorawan.v3.ApplicationDownlinkFailed.error:type_name -> ttn.lorawan.v3.ErrorDetails + 32, // 58: ttn.lorawan.v3.ApplicationDownlinkFailed.locations:type_name -> ttn.lorawan.v3.ApplicationDownlinkFailed.LocationsEntry + 50, // 59: ttn.lorawan.v3.ApplicationDownlinkFailed.version_ids:type_name -> ttn.lorawan.v3.EndDeviceVersionIdentifiers + 51, // 60: ttn.lorawan.v3.ApplicationDownlinkFailed.network_ids:type_name -> ttn.lorawan.v3.NetworkIdentifiers + 33, // 61: ttn.lorawan.v3.ApplicationDownlinkFailed.attributes:type_name -> ttn.lorawan.v3.ApplicationDownlinkFailed.AttributesEntry + 12, // 62: ttn.lorawan.v3.ApplicationInvalidatedDownlinks.downlinks:type_name -> ttn.lorawan.v3.ApplicationDownlink + 34, // 63: ttn.lorawan.v3.ApplicationInvalidatedDownlinks.locations:type_name -> ttn.lorawan.v3.ApplicationInvalidatedDownlinks.LocationsEntry + 50, // 64: ttn.lorawan.v3.ApplicationInvalidatedDownlinks.version_ids:type_name -> ttn.lorawan.v3.EndDeviceVersionIdentifiers + 51, // 65: ttn.lorawan.v3.ApplicationInvalidatedDownlinks.network_ids:type_name -> ttn.lorawan.v3.NetworkIdentifiers + 35, // 66: ttn.lorawan.v3.ApplicationInvalidatedDownlinks.attributes:type_name -> ttn.lorawan.v3.ApplicationInvalidatedDownlinks.AttributesEntry + 48, // 67: ttn.lorawan.v3.ApplicationServiceData.data:type_name -> google.protobuf.Struct + 36, // 68: ttn.lorawan.v3.ApplicationServiceData.locations:type_name -> ttn.lorawan.v3.ApplicationServiceData.LocationsEntry + 50, // 69: ttn.lorawan.v3.ApplicationServiceData.version_ids:type_name -> ttn.lorawan.v3.EndDeviceVersionIdentifiers + 51, // 70: ttn.lorawan.v3.ApplicationServiceData.network_ids:type_name -> ttn.lorawan.v3.NetworkIdentifiers + 37, // 71: ttn.lorawan.v3.ApplicationServiceData.attributes:type_name -> ttn.lorawan.v3.ApplicationServiceData.AttributesEntry + 44, // 72: ttn.lorawan.v3.ApplicationUp.end_device_ids:type_name -> ttn.lorawan.v3.EndDeviceIdentifiers + 41, // 73: ttn.lorawan.v3.ApplicationUp.received_at:type_name -> google.protobuf.Timestamp + 8, // 74: ttn.lorawan.v3.ApplicationUp.uplink_message:type_name -> ttn.lorawan.v3.ApplicationUplink + 9, // 75: ttn.lorawan.v3.ApplicationUp.uplink_normalized:type_name -> ttn.lorawan.v3.ApplicationUplinkNormalized + 11, // 76: ttn.lorawan.v3.ApplicationUp.join_accept:type_name -> ttn.lorawan.v3.ApplicationJoinAccept + 12, // 77: ttn.lorawan.v3.ApplicationUp.downlink_ack:type_name -> ttn.lorawan.v3.ApplicationDownlink + 12, // 78: ttn.lorawan.v3.ApplicationUp.downlink_nack:type_name -> ttn.lorawan.v3.ApplicationDownlink + 12, // 79: ttn.lorawan.v3.ApplicationUp.downlink_sent:type_name -> ttn.lorawan.v3.ApplicationDownlink + 14, // 80: ttn.lorawan.v3.ApplicationUp.downlink_failed:type_name -> ttn.lorawan.v3.ApplicationDownlinkFailed + 12, // 81: ttn.lorawan.v3.ApplicationUp.downlink_queued:type_name -> ttn.lorawan.v3.ApplicationDownlink + 15, // 82: ttn.lorawan.v3.ApplicationUp.downlink_queue_invalidated:type_name -> ttn.lorawan.v3.ApplicationInvalidatedDownlinks + 10, // 83: ttn.lorawan.v3.ApplicationUp.location_solved:type_name -> ttn.lorawan.v3.ApplicationLocation + 17, // 84: ttn.lorawan.v3.ApplicationUp.service_data:type_name -> ttn.lorawan.v3.ApplicationServiceData + 0, // 85: ttn.lorawan.v3.MessagePayloadFormatters.up_formatter:type_name -> ttn.lorawan.v3.PayloadFormatter + 0, // 86: ttn.lorawan.v3.MessagePayloadFormatters.down_formatter:type_name -> ttn.lorawan.v3.PayloadFormatter + 44, // 87: ttn.lorawan.v3.DownlinkQueueRequest.end_device_ids:type_name -> ttn.lorawan.v3.EndDeviceIdentifiers + 12, // 88: ttn.lorawan.v3.DownlinkQueueRequest.downlinks:type_name -> ttn.lorawan.v3.ApplicationDownlink + 52, // 89: ttn.lorawan.v3.ApplicationUplink.LocationsEntry.value:type_name -> ttn.lorawan.v3.Location + 52, // 90: ttn.lorawan.v3.ApplicationUplinkNormalized.LocationsEntry.value:type_name -> ttn.lorawan.v3.Location + 52, // 91: ttn.lorawan.v3.ApplicationJoinAccept.LocationsEntry.value:type_name -> ttn.lorawan.v3.Location + 55, // 92: ttn.lorawan.v3.ApplicationDownlink.ClassBC.gateways:type_name -> ttn.lorawan.v3.ClassBCGatewayIdentifiers + 41, // 93: ttn.lorawan.v3.ApplicationDownlink.ClassBC.absolute_time:type_name -> google.protobuf.Timestamp + 56, // 94: ttn.lorawan.v3.ApplicationDownlink.ConfirmedRetry.max_attempts:type_name -> google.protobuf.UInt32Value + 52, // 95: ttn.lorawan.v3.ApplicationDownlink.LocationsEntry.value:type_name -> ttn.lorawan.v3.Location + 52, // 96: ttn.lorawan.v3.ApplicationDownlinkFailed.LocationsEntry.value:type_name -> ttn.lorawan.v3.Location + 52, // 97: ttn.lorawan.v3.ApplicationInvalidatedDownlinks.LocationsEntry.value:type_name -> ttn.lorawan.v3.Location + 52, // 98: ttn.lorawan.v3.ApplicationServiceData.LocationsEntry.value:type_name -> ttn.lorawan.v3.Location + 99, // [99:99] is the sub-list for method output_type + 99, // [99:99] is the sub-list for method input_type + 99, // [99:99] is the sub-list for extension type_name + 99, // [99:99] is the sub-list for extension extendee + 0, // [0:99] is the sub-list for field type_name } func init() { file_ttn_lorawan_v3_messages_proto_init() } @@ -3128,7 +3557,7 @@ func file_ttn_lorawan_v3_messages_proto_init() { return nil } } - file_ttn_lorawan_v3_messages_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} { + file_ttn_lorawan_v3_messages_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ApplicationDownlink_ClassBC); i { case 0: return &v.state @@ -3140,7 +3569,7 @@ func file_ttn_lorawan_v3_messages_proto_init() { return nil } } - file_ttn_lorawan_v3_messages_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} { + file_ttn_lorawan_v3_messages_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ApplicationDownlink_ConfirmedRetry); i { case 0: return &v.state @@ -3176,7 +3605,7 @@ func file_ttn_lorawan_v3_messages_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_ttn_lorawan_v3_messages_proto_rawDesc, NumEnums: 2, - NumMessages: 24, + NumMessages: 36, NumExtensions: 0, NumServices: 0, }, diff --git a/pkg/ttnpb/messages.pb.paths.fm.go b/pkg/ttnpb/messages.pb.paths.fm.go index 0dfa9d1d44..81094e2700 100644 --- a/pkg/ttnpb/messages.pb.paths.fm.go +++ b/pkg/ttnpb/messages.pb.paths.fm.go @@ -561,6 +561,7 @@ var ApplicationUplinkFieldPathsNested = []string{ "app_s_key.encrypted_key", "app_s_key.kek_label", "app_s_key.key", + "attributes", "confirmed", "consumed_airtime", "decoded_payload", @@ -619,6 +620,7 @@ var ApplicationUplinkFieldPathsNested = []string{ var ApplicationUplinkFieldPathsTopLevel = []string{ "app_s_key", + "attributes", "confirmed", "consumed_airtime", "decoded_payload", @@ -640,6 +642,7 @@ var ApplicationUplinkFieldPathsTopLevel = []string{ "version_ids", } var ApplicationUplinkNormalizedFieldPathsNested = []string{ + "attributes", "confirmed", "consumed_airtime", "f_cnt", @@ -689,6 +692,7 @@ var ApplicationUplinkNormalizedFieldPathsNested = []string{ } var ApplicationUplinkNormalizedFieldPathsTopLevel = []string{ + "attributes", "confirmed", "consumed_airtime", "f_cnt", @@ -725,20 +729,40 @@ var ApplicationJoinAcceptFieldPathsNested = []string{ "app_s_key.encrypted_key", "app_s_key.kek_label", "app_s_key.key", + "attributes", "invalidated_downlinks", + "locations", + "network_ids", + "network_ids.cluster_address", + "network_ids.cluster_id", + "network_ids.net_id", + "network_ids.ns_id", + "network_ids.tenant_address", + "network_ids.tenant_id", "pending_session", "received_at", "session_key_id", + "version_ids", + "version_ids.band_id", + "version_ids.brand_id", + "version_ids.firmware_version", + "version_ids.hardware_version", + "version_ids.model_id", } var ApplicationJoinAcceptFieldPathsTopLevel = []string{ "app_s_key", + "attributes", "invalidated_downlinks", + "locations", + "network_ids", "pending_session", "received_at", "session_key_id", + "version_ids", } var ApplicationDownlinkFieldPathsNested = []string{ + "attributes", "class_b_c", "class_b_c.absolute_time", "class_b_c.gateways", @@ -752,11 +776,26 @@ var ApplicationDownlinkFieldPathsNested = []string{ "f_cnt", "f_port", "frm_payload", + "locations", + "network_ids", + "network_ids.cluster_address", + "network_ids.cluster_id", + "network_ids.net_id", + "network_ids.ns_id", + "network_ids.tenant_address", + "network_ids.tenant_id", "priority", "session_key_id", + "version_ids", + "version_ids.band_id", + "version_ids.brand_id", + "version_ids.firmware_version", + "version_ids.hardware_version", + "version_ids.model_id", } var ApplicationDownlinkFieldPathsTopLevel = []string{ + "attributes", "class_b_c", "confirmed", "confirmed_retry", @@ -766,8 +805,11 @@ var ApplicationDownlinkFieldPathsTopLevel = []string{ "f_cnt", "f_port", "frm_payload", + "locations", + "network_ids", "priority", "session_key_id", + "version_ids", } var ApplicationDownlinksFieldPathsNested = []string{ "downlinks", @@ -777,7 +819,9 @@ var ApplicationDownlinksFieldPathsTopLevel = []string{ "downlinks", } var ApplicationDownlinkFailedFieldPathsNested = []string{ + "attributes", "downlink", + "downlink.attributes", "downlink.class_b_c", "downlink.class_b_c.absolute_time", "downlink.class_b_c.gateways", @@ -791,8 +835,22 @@ var ApplicationDownlinkFailedFieldPathsNested = []string{ "downlink.f_cnt", "downlink.f_port", "downlink.frm_payload", + "downlink.locations", + "downlink.network_ids", + "downlink.network_ids.cluster_address", + "downlink.network_ids.cluster_id", + "downlink.network_ids.net_id", + "downlink.network_ids.ns_id", + "downlink.network_ids.tenant_address", + "downlink.network_ids.tenant_id", "downlink.priority", "downlink.session_key_id", + "downlink.version_ids", + "downlink.version_ids.band_id", + "downlink.version_ids.brand_id", + "downlink.version_ids.firmware_version", + "downlink.version_ids.hardware_version", + "downlink.version_ids.model_id", "error", "error.attributes", "error.cause", @@ -807,22 +865,59 @@ var ApplicationDownlinkFailedFieldPathsNested = []string{ "error.message_format", "error.name", "error.namespace", + "locations", + "network_ids", + "network_ids.cluster_address", + "network_ids.cluster_id", + "network_ids.net_id", + "network_ids.ns_id", + "network_ids.tenant_address", + "network_ids.tenant_id", + "version_ids", + "version_ids.band_id", + "version_ids.brand_id", + "version_ids.firmware_version", + "version_ids.hardware_version", + "version_ids.model_id", } var ApplicationDownlinkFailedFieldPathsTopLevel = []string{ + "attributes", "downlink", "error", + "locations", + "network_ids", + "version_ids", } var ApplicationInvalidatedDownlinksFieldPathsNested = []string{ + "attributes", "downlinks", "last_f_cnt_down", + "locations", + "network_ids", + "network_ids.cluster_address", + "network_ids.cluster_id", + "network_ids.net_id", + "network_ids.ns_id", + "network_ids.tenant_address", + "network_ids.tenant_id", "session_key_id", + "version_ids", + "version_ids.band_id", + "version_ids.brand_id", + "version_ids.firmware_version", + "version_ids.hardware_version", + "version_ids.model_id", } var ApplicationInvalidatedDownlinksFieldPathsTopLevel = []string{ + "attributes", "downlinks", "last_f_cnt_down", + "locations", + "network_ids", "session_key_id", + "version_ids", } var DownlinkQueueOperationErrorDetailsFieldPathsNested = []string{ "dev_addr", @@ -842,13 +937,32 @@ var DownlinkQueueOperationErrorDetailsFieldPathsTopLevel = []string{ "session_key_id", } var ApplicationServiceDataFieldPathsNested = []string{ + "attributes", "data", + "locations", + "network_ids", + "network_ids.cluster_address", + "network_ids.cluster_id", + "network_ids.net_id", + "network_ids.ns_id", + "network_ids.tenant_address", + "network_ids.tenant_id", "service", + "version_ids", + "version_ids.band_id", + "version_ids.brand_id", + "version_ids.firmware_version", + "version_ids.hardware_version", + "version_ids.model_id", } var ApplicationServiceDataFieldPathsTopLevel = []string{ + "attributes", "data", + "locations", + "network_ids", "service", + "version_ids", } var ApplicationUpFieldPathsNested = []string{ "correlation_ids", @@ -863,6 +977,7 @@ var ApplicationUpFieldPathsNested = []string{ "simulated", "up", "up.downlink_ack", + "up.downlink_ack.attributes", "up.downlink_ack.class_b_c", "up.downlink_ack.class_b_c.absolute_time", "up.downlink_ack.class_b_c.gateways", @@ -876,10 +991,26 @@ var ApplicationUpFieldPathsNested = []string{ "up.downlink_ack.f_cnt", "up.downlink_ack.f_port", "up.downlink_ack.frm_payload", + "up.downlink_ack.locations", + "up.downlink_ack.network_ids", + "up.downlink_ack.network_ids.cluster_address", + "up.downlink_ack.network_ids.cluster_id", + "up.downlink_ack.network_ids.net_id", + "up.downlink_ack.network_ids.ns_id", + "up.downlink_ack.network_ids.tenant_address", + "up.downlink_ack.network_ids.tenant_id", "up.downlink_ack.priority", "up.downlink_ack.session_key_id", + "up.downlink_ack.version_ids", + "up.downlink_ack.version_ids.band_id", + "up.downlink_ack.version_ids.brand_id", + "up.downlink_ack.version_ids.firmware_version", + "up.downlink_ack.version_ids.hardware_version", + "up.downlink_ack.version_ids.model_id", "up.downlink_failed", + "up.downlink_failed.attributes", "up.downlink_failed.downlink", + "up.downlink_failed.downlink.attributes", "up.downlink_failed.downlink.class_b_c", "up.downlink_failed.downlink.class_b_c.absolute_time", "up.downlink_failed.downlink.class_b_c.gateways", @@ -893,8 +1024,22 @@ var ApplicationUpFieldPathsNested = []string{ "up.downlink_failed.downlink.f_cnt", "up.downlink_failed.downlink.f_port", "up.downlink_failed.downlink.frm_payload", + "up.downlink_failed.downlink.locations", + "up.downlink_failed.downlink.network_ids", + "up.downlink_failed.downlink.network_ids.cluster_address", + "up.downlink_failed.downlink.network_ids.cluster_id", + "up.downlink_failed.downlink.network_ids.net_id", + "up.downlink_failed.downlink.network_ids.ns_id", + "up.downlink_failed.downlink.network_ids.tenant_address", + "up.downlink_failed.downlink.network_ids.tenant_id", "up.downlink_failed.downlink.priority", "up.downlink_failed.downlink.session_key_id", + "up.downlink_failed.downlink.version_ids", + "up.downlink_failed.downlink.version_ids.band_id", + "up.downlink_failed.downlink.version_ids.brand_id", + "up.downlink_failed.downlink.version_ids.firmware_version", + "up.downlink_failed.downlink.version_ids.hardware_version", + "up.downlink_failed.downlink.version_ids.model_id", "up.downlink_failed.error", "up.downlink_failed.error.attributes", "up.downlink_failed.error.cause", @@ -909,7 +1054,22 @@ var ApplicationUpFieldPathsNested = []string{ "up.downlink_failed.error.message_format", "up.downlink_failed.error.name", "up.downlink_failed.error.namespace", + "up.downlink_failed.locations", + "up.downlink_failed.network_ids", + "up.downlink_failed.network_ids.cluster_address", + "up.downlink_failed.network_ids.cluster_id", + "up.downlink_failed.network_ids.net_id", + "up.downlink_failed.network_ids.ns_id", + "up.downlink_failed.network_ids.tenant_address", + "up.downlink_failed.network_ids.tenant_id", + "up.downlink_failed.version_ids", + "up.downlink_failed.version_ids.band_id", + "up.downlink_failed.version_ids.brand_id", + "up.downlink_failed.version_ids.firmware_version", + "up.downlink_failed.version_ids.hardware_version", + "up.downlink_failed.version_ids.model_id", "up.downlink_nack", + "up.downlink_nack.attributes", "up.downlink_nack.class_b_c", "up.downlink_nack.class_b_c.absolute_time", "up.downlink_nack.class_b_c.gateways", @@ -923,13 +1083,43 @@ var ApplicationUpFieldPathsNested = []string{ "up.downlink_nack.f_cnt", "up.downlink_nack.f_port", "up.downlink_nack.frm_payload", + "up.downlink_nack.locations", + "up.downlink_nack.network_ids", + "up.downlink_nack.network_ids.cluster_address", + "up.downlink_nack.network_ids.cluster_id", + "up.downlink_nack.network_ids.net_id", + "up.downlink_nack.network_ids.ns_id", + "up.downlink_nack.network_ids.tenant_address", + "up.downlink_nack.network_ids.tenant_id", "up.downlink_nack.priority", "up.downlink_nack.session_key_id", + "up.downlink_nack.version_ids", + "up.downlink_nack.version_ids.band_id", + "up.downlink_nack.version_ids.brand_id", + "up.downlink_nack.version_ids.firmware_version", + "up.downlink_nack.version_ids.hardware_version", + "up.downlink_nack.version_ids.model_id", "up.downlink_queue_invalidated", + "up.downlink_queue_invalidated.attributes", "up.downlink_queue_invalidated.downlinks", "up.downlink_queue_invalidated.last_f_cnt_down", + "up.downlink_queue_invalidated.locations", + "up.downlink_queue_invalidated.network_ids", + "up.downlink_queue_invalidated.network_ids.cluster_address", + "up.downlink_queue_invalidated.network_ids.cluster_id", + "up.downlink_queue_invalidated.network_ids.net_id", + "up.downlink_queue_invalidated.network_ids.ns_id", + "up.downlink_queue_invalidated.network_ids.tenant_address", + "up.downlink_queue_invalidated.network_ids.tenant_id", "up.downlink_queue_invalidated.session_key_id", + "up.downlink_queue_invalidated.version_ids", + "up.downlink_queue_invalidated.version_ids.band_id", + "up.downlink_queue_invalidated.version_ids.brand_id", + "up.downlink_queue_invalidated.version_ids.firmware_version", + "up.downlink_queue_invalidated.version_ids.hardware_version", + "up.downlink_queue_invalidated.version_ids.model_id", "up.downlink_queued", + "up.downlink_queued.attributes", "up.downlink_queued.class_b_c", "up.downlink_queued.class_b_c.absolute_time", "up.downlink_queued.class_b_c.gateways", @@ -943,9 +1133,24 @@ var ApplicationUpFieldPathsNested = []string{ "up.downlink_queued.f_cnt", "up.downlink_queued.f_port", "up.downlink_queued.frm_payload", + "up.downlink_queued.locations", + "up.downlink_queued.network_ids", + "up.downlink_queued.network_ids.cluster_address", + "up.downlink_queued.network_ids.cluster_id", + "up.downlink_queued.network_ids.net_id", + "up.downlink_queued.network_ids.ns_id", + "up.downlink_queued.network_ids.tenant_address", + "up.downlink_queued.network_ids.tenant_id", "up.downlink_queued.priority", "up.downlink_queued.session_key_id", + "up.downlink_queued.version_ids", + "up.downlink_queued.version_ids.band_id", + "up.downlink_queued.version_ids.brand_id", + "up.downlink_queued.version_ids.firmware_version", + "up.downlink_queued.version_ids.hardware_version", + "up.downlink_queued.version_ids.model_id", "up.downlink_sent", + "up.downlink_sent.attributes", "up.downlink_sent.class_b_c", "up.downlink_sent.class_b_c.absolute_time", "up.downlink_sent.class_b_c.gateways", @@ -959,17 +1164,46 @@ var ApplicationUpFieldPathsNested = []string{ "up.downlink_sent.f_cnt", "up.downlink_sent.f_port", "up.downlink_sent.frm_payload", + "up.downlink_sent.locations", + "up.downlink_sent.network_ids", + "up.downlink_sent.network_ids.cluster_address", + "up.downlink_sent.network_ids.cluster_id", + "up.downlink_sent.network_ids.net_id", + "up.downlink_sent.network_ids.ns_id", + "up.downlink_sent.network_ids.tenant_address", + "up.downlink_sent.network_ids.tenant_id", "up.downlink_sent.priority", "up.downlink_sent.session_key_id", + "up.downlink_sent.version_ids", + "up.downlink_sent.version_ids.band_id", + "up.downlink_sent.version_ids.brand_id", + "up.downlink_sent.version_ids.firmware_version", + "up.downlink_sent.version_ids.hardware_version", + "up.downlink_sent.version_ids.model_id", "up.join_accept", "up.join_accept.app_s_key", "up.join_accept.app_s_key.encrypted_key", "up.join_accept.app_s_key.kek_label", "up.join_accept.app_s_key.key", + "up.join_accept.attributes", "up.join_accept.invalidated_downlinks", + "up.join_accept.locations", + "up.join_accept.network_ids", + "up.join_accept.network_ids.cluster_address", + "up.join_accept.network_ids.cluster_id", + "up.join_accept.network_ids.net_id", + "up.join_accept.network_ids.ns_id", + "up.join_accept.network_ids.tenant_address", + "up.join_accept.network_ids.tenant_id", "up.join_accept.pending_session", "up.join_accept.received_at", "up.join_accept.session_key_id", + "up.join_accept.version_ids", + "up.join_accept.version_ids.band_id", + "up.join_accept.version_ids.brand_id", + "up.join_accept.version_ids.firmware_version", + "up.join_accept.version_ids.hardware_version", + "up.join_accept.version_ids.model_id", "up.location_solved", "up.location_solved.attributes", "up.location_solved.location", @@ -980,13 +1214,29 @@ var ApplicationUpFieldPathsNested = []string{ "up.location_solved.location.source", "up.location_solved.service", "up.service_data", + "up.service_data.attributes", "up.service_data.data", + "up.service_data.locations", + "up.service_data.network_ids", + "up.service_data.network_ids.cluster_address", + "up.service_data.network_ids.cluster_id", + "up.service_data.network_ids.net_id", + "up.service_data.network_ids.ns_id", + "up.service_data.network_ids.tenant_address", + "up.service_data.network_ids.tenant_id", "up.service_data.service", + "up.service_data.version_ids", + "up.service_data.version_ids.band_id", + "up.service_data.version_ids.brand_id", + "up.service_data.version_ids.firmware_version", + "up.service_data.version_ids.hardware_version", + "up.service_data.version_ids.model_id", "up.uplink_message", "up.uplink_message.app_s_key", "up.uplink_message.app_s_key.encrypted_key", "up.uplink_message.app_s_key.kek_label", "up.uplink_message.app_s_key.key", + "up.uplink_message.attributes", "up.uplink_message.confirmed", "up.uplink_message.consumed_airtime", "up.uplink_message.decoded_payload", @@ -1042,6 +1292,7 @@ var ApplicationUpFieldPathsNested = []string{ "up.uplink_message.version_ids.hardware_version", "up.uplink_message.version_ids.model_id", "up.uplink_normalized", + "up.uplink_normalized.attributes", "up.uplink_normalized.confirmed", "up.uplink_normalized.consumed_airtime", "up.uplink_normalized.f_cnt", diff --git a/pkg/ttnpb/messages.pb.setters.fm.go b/pkg/ttnpb/messages.pb.setters.fm.go index 8cfbcff174..ad7dc055f6 100644 --- a/pkg/ttnpb/messages.pb.setters.fm.go +++ b/pkg/ttnpb/messages.pb.setters.fm.go @@ -778,6 +778,15 @@ func (dst *ApplicationUplink) SetFields(src *ApplicationUplink, paths ...string) dst.LastBatteryPercentage = nil } } + case "attributes": + if len(subs) > 0 { + return fmt.Errorf("'attributes' has no subfields, but %s were specified", subs) + } + if src != nil { + dst.Attributes = src.Attributes + } else { + dst.Attributes = nil + } default: return fmt.Errorf("invalid field: '%s'", name) @@ -966,6 +975,15 @@ func (dst *ApplicationUplinkNormalized) SetFields(src *ApplicationUplinkNormaliz dst.NetworkIds = nil } } + case "attributes": + if len(subs) > 0 { + return fmt.Errorf("'attributes' has no subfields, but %s were specified", subs) + } + if src != nil { + dst.Attributes = src.Attributes + } else { + dst.Attributes = nil + } default: return fmt.Errorf("invalid field: '%s'", name) @@ -1094,6 +1112,74 @@ func (dst *ApplicationJoinAccept) SetFields(src *ApplicationJoinAccept, paths .. } else { dst.ReceivedAt = nil } + case "locations": + if len(subs) > 0 { + return fmt.Errorf("'locations' has no subfields, but %s were specified", subs) + } + if src != nil { + dst.Locations = src.Locations + } else { + dst.Locations = nil + } + case "version_ids": + if len(subs) > 0 { + var newDst, newSrc *EndDeviceVersionIdentifiers + if (src == nil || src.VersionIds == nil) && dst.VersionIds == nil { + continue + } + if src != nil { + newSrc = src.VersionIds + } + if dst.VersionIds != nil { + newDst = dst.VersionIds + } else { + newDst = &EndDeviceVersionIdentifiers{} + dst.VersionIds = newDst + } + if err := newDst.SetFields(newSrc, subs...); err != nil { + return err + } + } else { + if src != nil { + dst.VersionIds = src.VersionIds + } else { + dst.VersionIds = nil + } + } + case "network_ids": + if len(subs) > 0 { + var newDst, newSrc *NetworkIdentifiers + if (src == nil || src.NetworkIds == nil) && dst.NetworkIds == nil { + continue + } + if src != nil { + newSrc = src.NetworkIds + } + if dst.NetworkIds != nil { + newDst = dst.NetworkIds + } else { + newDst = &NetworkIdentifiers{} + dst.NetworkIds = newDst + } + if err := newDst.SetFields(newSrc, subs...); err != nil { + return err + } + } else { + if src != nil { + dst.NetworkIds = src.NetworkIds + } else { + dst.NetworkIds = nil + } + } + case "attributes": + if len(subs) > 0 { + return fmt.Errorf("'attributes' has no subfields, but %s were specified", subs) + } + if src != nil { + dst.Attributes = src.Attributes + } else { + dst.Attributes = nil + } default: return fmt.Errorf("invalid field: '%s'", name) @@ -1239,6 +1325,74 @@ func (dst *ApplicationDownlink) SetFields(src *ApplicationDownlink, paths ...str dst.ConfirmedRetry = nil } } + case "locations": + if len(subs) > 0 { + return fmt.Errorf("'locations' has no subfields, but %s were specified", subs) + } + if src != nil { + dst.Locations = src.Locations + } else { + dst.Locations = nil + } + case "version_ids": + if len(subs) > 0 { + var newDst, newSrc *EndDeviceVersionIdentifiers + if (src == nil || src.VersionIds == nil) && dst.VersionIds == nil { + continue + } + if src != nil { + newSrc = src.VersionIds + } + if dst.VersionIds != nil { + newDst = dst.VersionIds + } else { + newDst = &EndDeviceVersionIdentifiers{} + dst.VersionIds = newDst + } + if err := newDst.SetFields(newSrc, subs...); err != nil { + return err + } + } else { + if src != nil { + dst.VersionIds = src.VersionIds + } else { + dst.VersionIds = nil + } + } + case "network_ids": + if len(subs) > 0 { + var newDst, newSrc *NetworkIdentifiers + if (src == nil || src.NetworkIds == nil) && dst.NetworkIds == nil { + continue + } + if src != nil { + newSrc = src.NetworkIds + } + if dst.NetworkIds != nil { + newDst = dst.NetworkIds + } else { + newDst = &NetworkIdentifiers{} + dst.NetworkIds = newDst + } + if err := newDst.SetFields(newSrc, subs...); err != nil { + return err + } + } else { + if src != nil { + dst.NetworkIds = src.NetworkIds + } else { + dst.NetworkIds = nil + } + } + case "attributes": + if len(subs) > 0 { + return fmt.Errorf("'attributes' has no subfields, but %s were specified", subs) + } + if src != nil { + dst.Attributes = src.Attributes + } else { + dst.Attributes = nil + } default: return fmt.Errorf("invalid field: '%s'", name) @@ -1320,6 +1474,74 @@ func (dst *ApplicationDownlinkFailed) SetFields(src *ApplicationDownlinkFailed, dst.Error = nil } } + case "locations": + if len(subs) > 0 { + return fmt.Errorf("'locations' has no subfields, but %s were specified", subs) + } + if src != nil { + dst.Locations = src.Locations + } else { + dst.Locations = nil + } + case "version_ids": + if len(subs) > 0 { + var newDst, newSrc *EndDeviceVersionIdentifiers + if (src == nil || src.VersionIds == nil) && dst.VersionIds == nil { + continue + } + if src != nil { + newSrc = src.VersionIds + } + if dst.VersionIds != nil { + newDst = dst.VersionIds + } else { + newDst = &EndDeviceVersionIdentifiers{} + dst.VersionIds = newDst + } + if err := newDst.SetFields(newSrc, subs...); err != nil { + return err + } + } else { + if src != nil { + dst.VersionIds = src.VersionIds + } else { + dst.VersionIds = nil + } + } + case "network_ids": + if len(subs) > 0 { + var newDst, newSrc *NetworkIdentifiers + if (src == nil || src.NetworkIds == nil) && dst.NetworkIds == nil { + continue + } + if src != nil { + newSrc = src.NetworkIds + } + if dst.NetworkIds != nil { + newDst = dst.NetworkIds + } else { + newDst = &NetworkIdentifiers{} + dst.NetworkIds = newDst + } + if err := newDst.SetFields(newSrc, subs...); err != nil { + return err + } + } else { + if src != nil { + dst.NetworkIds = src.NetworkIds + } else { + dst.NetworkIds = nil + } + } + case "attributes": + if len(subs) > 0 { + return fmt.Errorf("'attributes' has no subfields, but %s were specified", subs) + } + if src != nil { + dst.Attributes = src.Attributes + } else { + dst.Attributes = nil + } default: return fmt.Errorf("invalid field: '%s'", name) @@ -1359,6 +1581,74 @@ func (dst *ApplicationInvalidatedDownlinks) SetFields(src *ApplicationInvalidate } else { dst.SessionKeyId = nil } + case "locations": + if len(subs) > 0 { + return fmt.Errorf("'locations' has no subfields, but %s were specified", subs) + } + if src != nil { + dst.Locations = src.Locations + } else { + dst.Locations = nil + } + case "version_ids": + if len(subs) > 0 { + var newDst, newSrc *EndDeviceVersionIdentifiers + if (src == nil || src.VersionIds == nil) && dst.VersionIds == nil { + continue + } + if src != nil { + newSrc = src.VersionIds + } + if dst.VersionIds != nil { + newDst = dst.VersionIds + } else { + newDst = &EndDeviceVersionIdentifiers{} + dst.VersionIds = newDst + } + if err := newDst.SetFields(newSrc, subs...); err != nil { + return err + } + } else { + if src != nil { + dst.VersionIds = src.VersionIds + } else { + dst.VersionIds = nil + } + } + case "network_ids": + if len(subs) > 0 { + var newDst, newSrc *NetworkIdentifiers + if (src == nil || src.NetworkIds == nil) && dst.NetworkIds == nil { + continue + } + if src != nil { + newSrc = src.NetworkIds + } + if dst.NetworkIds != nil { + newDst = dst.NetworkIds + } else { + newDst = &NetworkIdentifiers{} + dst.NetworkIds = newDst + } + if err := newDst.SetFields(newSrc, subs...); err != nil { + return err + } + } else { + if src != nil { + dst.NetworkIds = src.NetworkIds + } else { + dst.NetworkIds = nil + } + } + case "attributes": + if len(subs) > 0 { + return fmt.Errorf("'attributes' has no subfields, but %s were specified", subs) + } + if src != nil { + dst.Attributes = src.Attributes + } else { + dst.Attributes = nil + } default: return fmt.Errorf("invalid field: '%s'", name) @@ -1456,6 +1746,74 @@ func (dst *ApplicationServiceData) SetFields(src *ApplicationServiceData, paths } else { dst.Data = nil } + case "locations": + if len(subs) > 0 { + return fmt.Errorf("'locations' has no subfields, but %s were specified", subs) + } + if src != nil { + dst.Locations = src.Locations + } else { + dst.Locations = nil + } + case "version_ids": + if len(subs) > 0 { + var newDst, newSrc *EndDeviceVersionIdentifiers + if (src == nil || src.VersionIds == nil) && dst.VersionIds == nil { + continue + } + if src != nil { + newSrc = src.VersionIds + } + if dst.VersionIds != nil { + newDst = dst.VersionIds + } else { + newDst = &EndDeviceVersionIdentifiers{} + dst.VersionIds = newDst + } + if err := newDst.SetFields(newSrc, subs...); err != nil { + return err + } + } else { + if src != nil { + dst.VersionIds = src.VersionIds + } else { + dst.VersionIds = nil + } + } + case "network_ids": + if len(subs) > 0 { + var newDst, newSrc *NetworkIdentifiers + if (src == nil || src.NetworkIds == nil) && dst.NetworkIds == nil { + continue + } + if src != nil { + newSrc = src.NetworkIds + } + if dst.NetworkIds != nil { + newDst = dst.NetworkIds + } else { + newDst = &NetworkIdentifiers{} + dst.NetworkIds = newDst + } + if err := newDst.SetFields(newSrc, subs...); err != nil { + return err + } + } else { + if src != nil { + dst.NetworkIds = src.NetworkIds + } else { + dst.NetworkIds = nil + } + } + case "attributes": + if len(subs) > 0 { + return fmt.Errorf("'attributes' has no subfields, but %s were specified", subs) + } + if src != nil { + dst.Attributes = src.Attributes + } else { + dst.Attributes = nil + } default: return fmt.Errorf("invalid field: '%s'", name) diff --git a/pkg/ttnpb/messages.pb.validate.go b/pkg/ttnpb/messages.pb.validate.go index e12c1ee3fd..6d13b82dd1 100644 --- a/pkg/ttnpb/messages.pb.validate.go +++ b/pkg/ttnpb/messages.pb.validate.go @@ -1036,6 +1036,41 @@ func (m *ApplicationUplink) ValidateFields(paths ...string) error { } } + case "attributes": + + if len(m.GetAttributes()) > 10 { + return ApplicationUplinkValidationError{ + field: "attributes", + reason: "value must contain no more than 10 pair(s)", + } + } + + for key, val := range m.GetAttributes() { + _ = val + + if utf8.RuneCountInString(key) > 36 { + return ApplicationUplinkValidationError{ + field: fmt.Sprintf("attributes[%v]", key), + reason: "value length must be at most 36 runes", + } + } + + if !_ApplicationUplink_Attributes_Pattern.MatchString(key) { + return ApplicationUplinkValidationError{ + field: fmt.Sprintf("attributes[%v]", key), + reason: "value does not match regex pattern \"^[a-z0-9](?:[-]?[a-z0-9]){2,}$\"", + } + } + + if utf8.RuneCountInString(val) > 200 { + return ApplicationUplinkValidationError{ + field: fmt.Sprintf("attributes[%v]", key), + reason: "value length must be at most 200 runes", + } + } + + } + default: return ApplicationUplinkValidationError{ field: name, @@ -1106,6 +1141,8 @@ var _ApplicationUplink_FPort_NotInLookup = map[uint32]struct{}{ 224: {}, } +var _ApplicationUplink_Attributes_Pattern = regexp.MustCompile("^[a-z0-9](?:[-]?[a-z0-9]){2,}$") + // ValidateFields checks the field values on ApplicationUplinkNormalized with // the rules defined in the proto definition for this message. If any rules // are violated, an error is returned. @@ -1273,6 +1310,41 @@ func (m *ApplicationUplinkNormalized) ValidateFields(paths ...string) error { } } + case "attributes": + + if len(m.GetAttributes()) > 10 { + return ApplicationUplinkNormalizedValidationError{ + field: "attributes", + reason: "value must contain no more than 10 pair(s)", + } + } + + for key, val := range m.GetAttributes() { + _ = val + + if utf8.RuneCountInString(key) > 36 { + return ApplicationUplinkNormalizedValidationError{ + field: fmt.Sprintf("attributes[%v]", key), + reason: "value length must be at most 36 runes", + } + } + + if !_ApplicationUplinkNormalized_Attributes_Pattern.MatchString(key) { + return ApplicationUplinkNormalizedValidationError{ + field: fmt.Sprintf("attributes[%v]", key), + reason: "value does not match regex pattern \"^[a-z0-9](?:[-]?[a-z0-9]){2,}$\"", + } + } + + if utf8.RuneCountInString(val) > 200 { + return ApplicationUplinkNormalizedValidationError{ + field: fmt.Sprintf("attributes[%v]", key), + reason: "value length must be at most 200 runes", + } + } + + } + default: return ApplicationUplinkNormalizedValidationError{ field: name, @@ -1344,6 +1416,8 @@ var _ApplicationUplinkNormalized_FPort_NotInLookup = map[uint32]struct{}{ 224: {}, } +var _ApplicationUplinkNormalized_Attributes_Pattern = regexp.MustCompile("^[a-z0-9](?:[-]?[a-z0-9]){2,}$") + // ValidateFields checks the field values on ApplicationLocation with the rules // defined in the proto definition for this message. If any rules are // violated, an error is returned. @@ -1547,6 +1621,84 @@ func (m *ApplicationJoinAccept) ValidateFields(paths ...string) error { } } + case "locations": + + for key, val := range m.GetLocations() { + _ = val + + // no validation rules for Locations[key] + + if v, ok := interface{}(val).(interface{ ValidateFields(...string) error }); ok { + if err := v.ValidateFields(subs...); err != nil { + return ApplicationJoinAcceptValidationError{ + field: fmt.Sprintf("locations[%v]", key), + reason: "embedded message failed validation", + cause: err, + } + } + } + + } + + case "version_ids": + + if v, ok := interface{}(m.GetVersionIds()).(interface{ ValidateFields(...string) error }); ok { + if err := v.ValidateFields(subs...); err != nil { + return ApplicationJoinAcceptValidationError{ + field: "version_ids", + reason: "embedded message failed validation", + cause: err, + } + } + } + + case "network_ids": + + if v, ok := interface{}(m.GetNetworkIds()).(interface{ ValidateFields(...string) error }); ok { + if err := v.ValidateFields(subs...); err != nil { + return ApplicationJoinAcceptValidationError{ + field: "network_ids", + reason: "embedded message failed validation", + cause: err, + } + } + } + + case "attributes": + + if len(m.GetAttributes()) > 10 { + return ApplicationJoinAcceptValidationError{ + field: "attributes", + reason: "value must contain no more than 10 pair(s)", + } + } + + for key, val := range m.GetAttributes() { + _ = val + + if utf8.RuneCountInString(key) > 36 { + return ApplicationJoinAcceptValidationError{ + field: fmt.Sprintf("attributes[%v]", key), + reason: "value length must be at most 36 runes", + } + } + + if !_ApplicationJoinAccept_Attributes_Pattern.MatchString(key) { + return ApplicationJoinAcceptValidationError{ + field: fmt.Sprintf("attributes[%v]", key), + reason: "value does not match regex pattern \"^[a-z0-9](?:[-]?[a-z0-9]){2,}$\"", + } + } + + if utf8.RuneCountInString(val) > 200 { + return ApplicationJoinAcceptValidationError{ + field: fmt.Sprintf("attributes[%v]", key), + reason: "value length must be at most 200 runes", + } + } + + } + default: return ApplicationJoinAcceptValidationError{ field: name, @@ -1613,6 +1765,8 @@ var _ interface { ErrorName() string } = ApplicationJoinAcceptValidationError{} +var _ApplicationJoinAccept_Attributes_Pattern = regexp.MustCompile("^[a-z0-9](?:[-]?[a-z0-9]){2,}$") + // ValidateFields checks the field values on ApplicationDownlink with the rules // defined in the proto definition for this message. If any rules are // violated, an error is returned. @@ -1727,6 +1881,84 @@ func (m *ApplicationDownlink) ValidateFields(paths ...string) error { } } + case "locations": + + for key, val := range m.GetLocations() { + _ = val + + // no validation rules for Locations[key] + + if v, ok := interface{}(val).(interface{ ValidateFields(...string) error }); ok { + if err := v.ValidateFields(subs...); err != nil { + return ApplicationDownlinkValidationError{ + field: fmt.Sprintf("locations[%v]", key), + reason: "embedded message failed validation", + cause: err, + } + } + } + + } + + case "version_ids": + + if v, ok := interface{}(m.GetVersionIds()).(interface{ ValidateFields(...string) error }); ok { + if err := v.ValidateFields(subs...); err != nil { + return ApplicationDownlinkValidationError{ + field: "version_ids", + reason: "embedded message failed validation", + cause: err, + } + } + } + + case "network_ids": + + if v, ok := interface{}(m.GetNetworkIds()).(interface{ ValidateFields(...string) error }); ok { + if err := v.ValidateFields(subs...); err != nil { + return ApplicationDownlinkValidationError{ + field: "network_ids", + reason: "embedded message failed validation", + cause: err, + } + } + } + + case "attributes": + + if len(m.GetAttributes()) > 10 { + return ApplicationDownlinkValidationError{ + field: "attributes", + reason: "value must contain no more than 10 pair(s)", + } + } + + for key, val := range m.GetAttributes() { + _ = val + + if utf8.RuneCountInString(key) > 36 { + return ApplicationDownlinkValidationError{ + field: fmt.Sprintf("attributes[%v]", key), + reason: "value length must be at most 36 runes", + } + } + + if !_ApplicationDownlink_Attributes_Pattern.MatchString(key) { + return ApplicationDownlinkValidationError{ + field: fmt.Sprintf("attributes[%v]", key), + reason: "value does not match regex pattern \"^[a-z0-9](?:[-]?[a-z0-9]){2,}$\"", + } + } + + if utf8.RuneCountInString(val) > 200 { + return ApplicationDownlinkValidationError{ + field: fmt.Sprintf("attributes[%v]", key), + reason: "value length must be at most 200 runes", + } + } + + } + default: return ApplicationDownlinkValidationError{ field: name, @@ -1797,6 +2029,8 @@ var _ApplicationDownlink_FPort_NotInLookup = map[uint32]struct{}{ 224: {}, } +var _ApplicationDownlink_Attributes_Pattern = regexp.MustCompile("^[a-z0-9](?:[-]?[a-z0-9]){2,}$") + // ValidateFields checks the field values on ApplicationDownlinks with the // rules defined in the proto definition for this message. If any rules are // violated, an error is returned. @@ -1948,6 +2182,84 @@ func (m *ApplicationDownlinkFailed) ValidateFields(paths ...string) error { } } + case "locations": + + for key, val := range m.GetLocations() { + _ = val + + // no validation rules for Locations[key] + + if v, ok := interface{}(val).(interface{ ValidateFields(...string) error }); ok { + if err := v.ValidateFields(subs...); err != nil { + return ApplicationDownlinkFailedValidationError{ + field: fmt.Sprintf("locations[%v]", key), + reason: "embedded message failed validation", + cause: err, + } + } + } + + } + + case "version_ids": + + if v, ok := interface{}(m.GetVersionIds()).(interface{ ValidateFields(...string) error }); ok { + if err := v.ValidateFields(subs...); err != nil { + return ApplicationDownlinkFailedValidationError{ + field: "version_ids", + reason: "embedded message failed validation", + cause: err, + } + } + } + + case "network_ids": + + if v, ok := interface{}(m.GetNetworkIds()).(interface{ ValidateFields(...string) error }); ok { + if err := v.ValidateFields(subs...); err != nil { + return ApplicationDownlinkFailedValidationError{ + field: "network_ids", + reason: "embedded message failed validation", + cause: err, + } + } + } + + case "attributes": + + if len(m.GetAttributes()) > 10 { + return ApplicationDownlinkFailedValidationError{ + field: "attributes", + reason: "value must contain no more than 10 pair(s)", + } + } + + for key, val := range m.GetAttributes() { + _ = val + + if utf8.RuneCountInString(key) > 36 { + return ApplicationDownlinkFailedValidationError{ + field: fmt.Sprintf("attributes[%v]", key), + reason: "value length must be at most 36 runes", + } + } + + if !_ApplicationDownlinkFailed_Attributes_Pattern.MatchString(key) { + return ApplicationDownlinkFailedValidationError{ + field: fmt.Sprintf("attributes[%v]", key), + reason: "value does not match regex pattern \"^[a-z0-9](?:[-]?[a-z0-9]){2,}$\"", + } + } + + if utf8.RuneCountInString(val) > 200 { + return ApplicationDownlinkFailedValidationError{ + field: fmt.Sprintf("attributes[%v]", key), + reason: "value length must be at most 200 runes", + } + } + + } + default: return ApplicationDownlinkFailedValidationError{ field: name, @@ -2015,6 +2327,8 @@ var _ interface { ErrorName() string } = ApplicationDownlinkFailedValidationError{} +var _ApplicationDownlinkFailed_Attributes_Pattern = regexp.MustCompile("^[a-z0-9](?:[-]?[a-z0-9]){2,}$") + // ValidateFields checks the field values on ApplicationInvalidatedDownlinks // with the rules defined in the proto definition for this message. If any // rules are violated, an error is returned. @@ -2058,6 +2372,84 @@ func (m *ApplicationInvalidatedDownlinks) ValidateFields(paths ...string) error } } + case "locations": + + for key, val := range m.GetLocations() { + _ = val + + // no validation rules for Locations[key] + + if v, ok := interface{}(val).(interface{ ValidateFields(...string) error }); ok { + if err := v.ValidateFields(subs...); err != nil { + return ApplicationInvalidatedDownlinksValidationError{ + field: fmt.Sprintf("locations[%v]", key), + reason: "embedded message failed validation", + cause: err, + } + } + } + + } + + case "version_ids": + + if v, ok := interface{}(m.GetVersionIds()).(interface{ ValidateFields(...string) error }); ok { + if err := v.ValidateFields(subs...); err != nil { + return ApplicationInvalidatedDownlinksValidationError{ + field: "version_ids", + reason: "embedded message failed validation", + cause: err, + } + } + } + + case "network_ids": + + if v, ok := interface{}(m.GetNetworkIds()).(interface{ ValidateFields(...string) error }); ok { + if err := v.ValidateFields(subs...); err != nil { + return ApplicationInvalidatedDownlinksValidationError{ + field: "network_ids", + reason: "embedded message failed validation", + cause: err, + } + } + } + + case "attributes": + + if len(m.GetAttributes()) > 10 { + return ApplicationInvalidatedDownlinksValidationError{ + field: "attributes", + reason: "value must contain no more than 10 pair(s)", + } + } + + for key, val := range m.GetAttributes() { + _ = val + + if utf8.RuneCountInString(key) > 36 { + return ApplicationInvalidatedDownlinksValidationError{ + field: fmt.Sprintf("attributes[%v]", key), + reason: "value length must be at most 36 runes", + } + } + + if !_ApplicationInvalidatedDownlinks_Attributes_Pattern.MatchString(key) { + return ApplicationInvalidatedDownlinksValidationError{ + field: fmt.Sprintf("attributes[%v]", key), + reason: "value does not match regex pattern \"^[a-z0-9](?:[-]?[a-z0-9]){2,}$\"", + } + } + + if utf8.RuneCountInString(val) > 200 { + return ApplicationInvalidatedDownlinksValidationError{ + field: fmt.Sprintf("attributes[%v]", key), + reason: "value length must be at most 200 runes", + } + } + + } + default: return ApplicationInvalidatedDownlinksValidationError{ field: name, @@ -2125,6 +2517,8 @@ var _ interface { ErrorName() string } = ApplicationInvalidatedDownlinksValidationError{} +var _ApplicationInvalidatedDownlinks_Attributes_Pattern = regexp.MustCompile("^[a-z0-9](?:[-]?[a-z0-9]){2,}$") + // ValidateFields checks the field values on DownlinkQueueOperationErrorDetails // with the rules defined in the proto definition for this message. If any // rules are violated, an error is returned. @@ -2284,6 +2678,84 @@ func (m *ApplicationServiceData) ValidateFields(paths ...string) error { } } + case "locations": + + for key, val := range m.GetLocations() { + _ = val + + // no validation rules for Locations[key] + + if v, ok := interface{}(val).(interface{ ValidateFields(...string) error }); ok { + if err := v.ValidateFields(subs...); err != nil { + return ApplicationServiceDataValidationError{ + field: fmt.Sprintf("locations[%v]", key), + reason: "embedded message failed validation", + cause: err, + } + } + } + + } + + case "version_ids": + + if v, ok := interface{}(m.GetVersionIds()).(interface{ ValidateFields(...string) error }); ok { + if err := v.ValidateFields(subs...); err != nil { + return ApplicationServiceDataValidationError{ + field: "version_ids", + reason: "embedded message failed validation", + cause: err, + } + } + } + + case "network_ids": + + if v, ok := interface{}(m.GetNetworkIds()).(interface{ ValidateFields(...string) error }); ok { + if err := v.ValidateFields(subs...); err != nil { + return ApplicationServiceDataValidationError{ + field: "network_ids", + reason: "embedded message failed validation", + cause: err, + } + } + } + + case "attributes": + + if len(m.GetAttributes()) > 10 { + return ApplicationServiceDataValidationError{ + field: "attributes", + reason: "value must contain no more than 10 pair(s)", + } + } + + for key, val := range m.GetAttributes() { + _ = val + + if utf8.RuneCountInString(key) > 36 { + return ApplicationServiceDataValidationError{ + field: fmt.Sprintf("attributes[%v]", key), + reason: "value length must be at most 36 runes", + } + } + + if !_ApplicationServiceData_Attributes_Pattern.MatchString(key) { + return ApplicationServiceDataValidationError{ + field: fmt.Sprintf("attributes[%v]", key), + reason: "value does not match regex pattern \"^[a-z0-9](?:[-]?[a-z0-9]){2,}$\"", + } + } + + if utf8.RuneCountInString(val) > 200 { + return ApplicationServiceDataValidationError{ + field: fmt.Sprintf("attributes[%v]", key), + reason: "value length must be at most 200 runes", + } + } + + } + default: return ApplicationServiceDataValidationError{ field: name, @@ -2350,6 +2822,8 @@ var _ interface { ErrorName() string } = ApplicationServiceDataValidationError{} +var _ApplicationServiceData_Attributes_Pattern = regexp.MustCompile("^[a-z0-9](?:[-]?[a-z0-9]){2,}$") + // ValidateFields checks the field values on ApplicationUp with the rules // defined in the proto definition for this message. If any rules are // violated, an error is returned. diff --git a/pkg/ttnpb/messages_flags.pb.go b/pkg/ttnpb/messages_flags.pb.go index ead7ad98e1..80f01568ec 100644 --- a/pkg/ttnpb/messages_flags.pb.go +++ b/pkg/ttnpb/messages_flags.pb.go @@ -40,6 +40,7 @@ func AddSelectFlagsForApplicationUplink(flags *pflag.FlagSet, prefix string, hid AddSelectFlagsForNetworkIdentifiers(flags, flagsplugin.Prefix("network-ids", prefix), hidden) flags.AddFlag(flagsplugin.NewBoolFlag(flagsplugin.Prefix("last-battery-percentage", prefix), flagsplugin.SelectDesc(flagsplugin.Prefix("last-battery-percentage", prefix), true), flagsplugin.WithHidden(hidden))) // NOTE: last_battery_percentage (LastBatteryPercentage) does not seem to have select flags. + flags.AddFlag(flagsplugin.NewBoolFlag(flagsplugin.Prefix("attributes", prefix), flagsplugin.SelectDesc(flagsplugin.Prefix("attributes", prefix), false), flagsplugin.WithHidden(hidden))) } // SelectFromFlags outputs the fieldmask paths forApplicationUplink message from select flags. @@ -165,6 +166,11 @@ func PathsFromSelectFlagsForApplicationUplink(flags *pflag.FlagSet, prefix strin paths = append(paths, flagsplugin.Prefix("last_battery_percentage", prefix)) } // NOTE: last_battery_percentage (LastBatteryPercentage) does not seem to have select flags. + if val, selected, err := flagsplugin.GetBool(flags, flagsplugin.Prefix("attributes", prefix)); err != nil { + return nil, err + } else if selected && val { + paths = append(paths, flagsplugin.Prefix("attributes", prefix)) + } return paths, nil } @@ -190,6 +196,7 @@ func AddSetFlagsForApplicationUplink(flags *pflag.FlagSet, prefix string, hidden AddSetFlagsForEndDeviceVersionIdentifiers(flags, flagsplugin.Prefix("version-ids", prefix), hidden) AddSetFlagsForNetworkIdentifiers(flags, flagsplugin.Prefix("network-ids", prefix), hidden) // FIXME: Skipping LastBatteryPercentage because it does not seem to implement AddSetFlags. + flags.AddFlag(flagsplugin.NewStringStringMapFlag(flagsplugin.Prefix("attributes", prefix), "", flagsplugin.WithHidden(hidden))) } // SetFromFlags sets the ApplicationUplink message from flags. @@ -305,6 +312,12 @@ func (m *ApplicationUplink) SetFromFlags(flags *pflag.FlagSet, prefix string) (p } } // FIXME: Skipping LastBatteryPercentage because it does not seem to implement AddSetFlags. + if val, changed, err := flagsplugin.GetStringStringMap(flags, flagsplugin.Prefix("attributes", prefix)); err != nil { + return nil, err + } else if changed { + m.Attributes = val + paths = append(paths, flagsplugin.Prefix("attributes", prefix)) + } return paths, nil } @@ -327,6 +340,7 @@ func AddSelectFlagsForApplicationUplinkNormalized(flags *pflag.FlagSet, prefix s AddSelectFlagsForEndDeviceVersionIdentifiers(flags, flagsplugin.Prefix("version-ids", prefix), hidden) flags.AddFlag(flagsplugin.NewBoolFlag(flagsplugin.Prefix("network-ids", prefix), flagsplugin.SelectDesc(flagsplugin.Prefix("network-ids", prefix), true), flagsplugin.WithHidden(hidden))) AddSelectFlagsForNetworkIdentifiers(flags, flagsplugin.Prefix("network-ids", prefix), hidden) + flags.AddFlag(flagsplugin.NewBoolFlag(flagsplugin.Prefix("attributes", prefix), flagsplugin.SelectDesc(flagsplugin.Prefix("attributes", prefix), false), flagsplugin.WithHidden(hidden))) } // SelectFromFlags outputs the fieldmask paths forApplicationUplinkNormalized message from select flags. @@ -416,6 +430,11 @@ func PathsFromSelectFlagsForApplicationUplinkNormalized(flags *pflag.FlagSet, pr } else { paths = append(paths, selectPaths...) } + if val, selected, err := flagsplugin.GetBool(flags, flagsplugin.Prefix("attributes", prefix)); err != nil { + return nil, err + } else if selected && val { + paths = append(paths, flagsplugin.Prefix("attributes", prefix)) + } return paths, nil } @@ -460,6 +479,12 @@ func AddSelectFlagsForApplicationJoinAccept(flags *pflag.FlagSet, prefix string, flags.AddFlag(flagsplugin.NewBoolFlag(flagsplugin.Prefix("invalidated-downlinks", prefix), flagsplugin.SelectDesc(flagsplugin.Prefix("invalidated-downlinks", prefix), false), flagsplugin.WithHidden(hidden))) flags.AddFlag(flagsplugin.NewBoolFlag(flagsplugin.Prefix("pending-session", prefix), flagsplugin.SelectDesc(flagsplugin.Prefix("pending-session", prefix), false), flagsplugin.WithHidden(hidden))) flags.AddFlag(flagsplugin.NewBoolFlag(flagsplugin.Prefix("received-at", prefix), flagsplugin.SelectDesc(flagsplugin.Prefix("received-at", prefix), false), flagsplugin.WithHidden(hidden))) + flags.AddFlag(flagsplugin.NewBoolFlag(flagsplugin.Prefix("locations", prefix), flagsplugin.SelectDesc(flagsplugin.Prefix("locations", prefix), false), flagsplugin.WithHidden(hidden))) + flags.AddFlag(flagsplugin.NewBoolFlag(flagsplugin.Prefix("version-ids", prefix), flagsplugin.SelectDesc(flagsplugin.Prefix("version-ids", prefix), true), flagsplugin.WithHidden(hidden))) + AddSelectFlagsForEndDeviceVersionIdentifiers(flags, flagsplugin.Prefix("version-ids", prefix), hidden) + flags.AddFlag(flagsplugin.NewBoolFlag(flagsplugin.Prefix("network-ids", prefix), flagsplugin.SelectDesc(flagsplugin.Prefix("network-ids", prefix), true), flagsplugin.WithHidden(hidden))) + AddSelectFlagsForNetworkIdentifiers(flags, flagsplugin.Prefix("network-ids", prefix), hidden) + flags.AddFlag(flagsplugin.NewBoolFlag(flagsplugin.Prefix("attributes", prefix), flagsplugin.SelectDesc(flagsplugin.Prefix("attributes", prefix), false), flagsplugin.WithHidden(hidden))) } // SelectFromFlags outputs the fieldmask paths forApplicationJoinAccept message from select flags. @@ -494,6 +519,36 @@ func PathsFromSelectFlagsForApplicationJoinAccept(flags *pflag.FlagSet, prefix s } else if selected && val { paths = append(paths, flagsplugin.Prefix("received_at", prefix)) } + if val, selected, err := flagsplugin.GetBool(flags, flagsplugin.Prefix("locations", prefix)); err != nil { + return nil, err + } else if selected && val { + paths = append(paths, flagsplugin.Prefix("locations", prefix)) + } + if val, selected, err := flagsplugin.GetBool(flags, flagsplugin.Prefix("version_ids", prefix)); err != nil { + return nil, err + } else if selected && val { + paths = append(paths, flagsplugin.Prefix("version_ids", prefix)) + } + if selectPaths, err := PathsFromSelectFlagsForEndDeviceVersionIdentifiers(flags, flagsplugin.Prefix("version_ids", prefix)); err != nil { + return nil, err + } else { + paths = append(paths, selectPaths...) + } + if val, selected, err := flagsplugin.GetBool(flags, flagsplugin.Prefix("network_ids", prefix)); err != nil { + return nil, err + } else if selected && val { + paths = append(paths, flagsplugin.Prefix("network_ids", prefix)) + } + if selectPaths, err := PathsFromSelectFlagsForNetworkIdentifiers(flags, flagsplugin.Prefix("network_ids", prefix)); err != nil { + return nil, err + } else { + paths = append(paths, selectPaths...) + } + if val, selected, err := flagsplugin.GetBool(flags, flagsplugin.Prefix("attributes", prefix)); err != nil { + return nil, err + } else if selected && val { + paths = append(paths, flagsplugin.Prefix("attributes", prefix)) + } return paths, nil } @@ -595,6 +650,12 @@ func AddSelectFlagsForApplicationDownlink(flags *pflag.FlagSet, prefix string, h flags.AddFlag(flagsplugin.NewBoolFlag(flagsplugin.Prefix("correlation-ids", prefix), flagsplugin.SelectDesc(flagsplugin.Prefix("correlation-ids", prefix), false), flagsplugin.WithHidden(hidden))) flags.AddFlag(flagsplugin.NewBoolFlag(flagsplugin.Prefix("confirmed-retry", prefix), flagsplugin.SelectDesc(flagsplugin.Prefix("confirmed-retry", prefix), true), flagsplugin.WithHidden(hidden))) AddSelectFlagsForApplicationDownlink_ConfirmedRetry(flags, flagsplugin.Prefix("confirmed-retry", prefix), hidden) + flags.AddFlag(flagsplugin.NewBoolFlag(flagsplugin.Prefix("locations", prefix), flagsplugin.SelectDesc(flagsplugin.Prefix("locations", prefix), false), flagsplugin.WithHidden(hidden))) + flags.AddFlag(flagsplugin.NewBoolFlag(flagsplugin.Prefix("version-ids", prefix), flagsplugin.SelectDesc(flagsplugin.Prefix("version-ids", prefix), true), flagsplugin.WithHidden(hidden))) + AddSelectFlagsForEndDeviceVersionIdentifiers(flags, flagsplugin.Prefix("version-ids", prefix), hidden) + flags.AddFlag(flagsplugin.NewBoolFlag(flagsplugin.Prefix("network-ids", prefix), flagsplugin.SelectDesc(flagsplugin.Prefix("network-ids", prefix), true), flagsplugin.WithHidden(hidden))) + AddSelectFlagsForNetworkIdentifiers(flags, flagsplugin.Prefix("network-ids", prefix), hidden) + flags.AddFlag(flagsplugin.NewBoolFlag(flagsplugin.Prefix("attributes", prefix), flagsplugin.SelectDesc(flagsplugin.Prefix("attributes", prefix), false), flagsplugin.WithHidden(hidden))) } // SelectFromFlags outputs the fieldmask paths forApplicationDownlink message from select flags. @@ -664,6 +725,36 @@ func PathsFromSelectFlagsForApplicationDownlink(flags *pflag.FlagSet, prefix str } else { paths = append(paths, selectPaths...) } + if val, selected, err := flagsplugin.GetBool(flags, flagsplugin.Prefix("locations", prefix)); err != nil { + return nil, err + } else if selected && val { + paths = append(paths, flagsplugin.Prefix("locations", prefix)) + } + if val, selected, err := flagsplugin.GetBool(flags, flagsplugin.Prefix("version_ids", prefix)); err != nil { + return nil, err + } else if selected && val { + paths = append(paths, flagsplugin.Prefix("version_ids", prefix)) + } + if selectPaths, err := PathsFromSelectFlagsForEndDeviceVersionIdentifiers(flags, flagsplugin.Prefix("version_ids", prefix)); err != nil { + return nil, err + } else { + paths = append(paths, selectPaths...) + } + if val, selected, err := flagsplugin.GetBool(flags, flagsplugin.Prefix("network_ids", prefix)); err != nil { + return nil, err + } else if selected && val { + paths = append(paths, flagsplugin.Prefix("network_ids", prefix)) + } + if selectPaths, err := PathsFromSelectFlagsForNetworkIdentifiers(flags, flagsplugin.Prefix("network_ids", prefix)); err != nil { + return nil, err + } else { + paths = append(paths, selectPaths...) + } + if val, selected, err := flagsplugin.GetBool(flags, flagsplugin.Prefix("attributes", prefix)); err != nil { + return nil, err + } else if selected && val { + paths = append(paths, flagsplugin.Prefix("attributes", prefix)) + } return paths, nil } @@ -680,6 +771,10 @@ func AddSetFlagsForApplicationDownlink(flags *pflag.FlagSet, prefix string, hidd flags.AddFlag(flagsplugin.NewStringFlag(flagsplugin.Prefix("priority", prefix), flagsplugin.EnumValueDesc(TxSchedulePriority_value), flagsplugin.WithHidden(hidden))) flags.AddFlag(flagsplugin.NewStringSliceFlag(flagsplugin.Prefix("correlation-ids", prefix), "", flagsplugin.WithHidden(hidden))) AddSetFlagsForApplicationDownlink_ConfirmedRetry(flags, flagsplugin.Prefix("confirmed-retry", prefix), hidden) + // FIXME: Skipping Locations because maps with message value types are currently not supported. + AddSetFlagsForEndDeviceVersionIdentifiers(flags, flagsplugin.Prefix("version-ids", prefix), hidden) + AddSetFlagsForNetworkIdentifiers(flags, flagsplugin.Prefix("network-ids", prefix), hidden) + flags.AddFlag(flagsplugin.NewStringStringMapFlag(flagsplugin.Prefix("attributes", prefix), "", flagsplugin.WithHidden(hidden))) } // SetFromFlags sets the ApplicationDownlink message from flags. @@ -757,6 +852,33 @@ func (m *ApplicationDownlink) SetFromFlags(flags *pflag.FlagSet, prefix string) paths = append(paths, setPaths...) } } + // FIXME: Skipping Locations because maps with message value types are currently not supported. + if changed := flagsplugin.IsAnyPrefixSet(flags, flagsplugin.Prefix("version_ids", prefix)); changed { + if m.VersionIds == nil { + m.VersionIds = &EndDeviceVersionIdentifiers{} + } + if setPaths, err := m.VersionIds.SetFromFlags(flags, flagsplugin.Prefix("version_ids", prefix)); err != nil { + return nil, err + } else { + paths = append(paths, setPaths...) + } + } + if changed := flagsplugin.IsAnyPrefixSet(flags, flagsplugin.Prefix("network_ids", prefix)); changed { + if m.NetworkIds == nil { + m.NetworkIds = &NetworkIdentifiers{} + } + if setPaths, err := m.NetworkIds.SetFromFlags(flags, flagsplugin.Prefix("network_ids", prefix)); err != nil { + return nil, err + } else { + paths = append(paths, setPaths...) + } + } + if val, changed, err := flagsplugin.GetStringStringMap(flags, flagsplugin.Prefix("attributes", prefix)); err != nil { + return nil, err + } else if changed { + m.Attributes = val + paths = append(paths, flagsplugin.Prefix("attributes", prefix)) + } return paths, nil } @@ -766,6 +888,12 @@ func AddSelectFlagsForApplicationDownlinkFailed(flags *pflag.FlagSet, prefix str AddSelectFlagsForApplicationDownlink(flags, flagsplugin.Prefix("downlink", prefix), hidden) flags.AddFlag(flagsplugin.NewBoolFlag(flagsplugin.Prefix("error", prefix), flagsplugin.SelectDesc(flagsplugin.Prefix("error", prefix), true), flagsplugin.WithHidden(hidden))) // NOTE: error (ErrorDetails) does not seem to have select flags. + flags.AddFlag(flagsplugin.NewBoolFlag(flagsplugin.Prefix("locations", prefix), flagsplugin.SelectDesc(flagsplugin.Prefix("locations", prefix), false), flagsplugin.WithHidden(hidden))) + flags.AddFlag(flagsplugin.NewBoolFlag(flagsplugin.Prefix("version-ids", prefix), flagsplugin.SelectDesc(flagsplugin.Prefix("version-ids", prefix), true), flagsplugin.WithHidden(hidden))) + AddSelectFlagsForEndDeviceVersionIdentifiers(flags, flagsplugin.Prefix("version-ids", prefix), hidden) + flags.AddFlag(flagsplugin.NewBoolFlag(flagsplugin.Prefix("network-ids", prefix), flagsplugin.SelectDesc(flagsplugin.Prefix("network-ids", prefix), true), flagsplugin.WithHidden(hidden))) + AddSelectFlagsForNetworkIdentifiers(flags, flagsplugin.Prefix("network-ids", prefix), hidden) + flags.AddFlag(flagsplugin.NewBoolFlag(flagsplugin.Prefix("attributes", prefix), flagsplugin.SelectDesc(flagsplugin.Prefix("attributes", prefix), false), flagsplugin.WithHidden(hidden))) } // SelectFromFlags outputs the fieldmask paths forApplicationDownlinkFailed message from select flags. @@ -786,6 +914,36 @@ func PathsFromSelectFlagsForApplicationDownlinkFailed(flags *pflag.FlagSet, pref paths = append(paths, flagsplugin.Prefix("error", prefix)) } // NOTE: error (ErrorDetails) does not seem to have select flags. + if val, selected, err := flagsplugin.GetBool(flags, flagsplugin.Prefix("locations", prefix)); err != nil { + return nil, err + } else if selected && val { + paths = append(paths, flagsplugin.Prefix("locations", prefix)) + } + if val, selected, err := flagsplugin.GetBool(flags, flagsplugin.Prefix("version_ids", prefix)); err != nil { + return nil, err + } else if selected && val { + paths = append(paths, flagsplugin.Prefix("version_ids", prefix)) + } + if selectPaths, err := PathsFromSelectFlagsForEndDeviceVersionIdentifiers(flags, flagsplugin.Prefix("version_ids", prefix)); err != nil { + return nil, err + } else { + paths = append(paths, selectPaths...) + } + if val, selected, err := flagsplugin.GetBool(flags, flagsplugin.Prefix("network_ids", prefix)); err != nil { + return nil, err + } else if selected && val { + paths = append(paths, flagsplugin.Prefix("network_ids", prefix)) + } + if selectPaths, err := PathsFromSelectFlagsForNetworkIdentifiers(flags, flagsplugin.Prefix("network_ids", prefix)); err != nil { + return nil, err + } else { + paths = append(paths, selectPaths...) + } + if val, selected, err := flagsplugin.GetBool(flags, flagsplugin.Prefix("attributes", prefix)); err != nil { + return nil, err + } else if selected && val { + paths = append(paths, flagsplugin.Prefix("attributes", prefix)) + } return paths, nil } @@ -794,6 +952,12 @@ func AddSelectFlagsForApplicationInvalidatedDownlinks(flags *pflag.FlagSet, pref flags.AddFlag(flagsplugin.NewBoolFlag(flagsplugin.Prefix("downlinks", prefix), flagsplugin.SelectDesc(flagsplugin.Prefix("downlinks", prefix), false), flagsplugin.WithHidden(hidden))) flags.AddFlag(flagsplugin.NewBoolFlag(flagsplugin.Prefix("last-f-cnt-down", prefix), flagsplugin.SelectDesc(flagsplugin.Prefix("last-f-cnt-down", prefix), false), flagsplugin.WithHidden(hidden))) flags.AddFlag(flagsplugin.NewBoolFlag(flagsplugin.Prefix("session-key-id", prefix), flagsplugin.SelectDesc(flagsplugin.Prefix("session-key-id", prefix), false), flagsplugin.WithHidden(hidden))) + flags.AddFlag(flagsplugin.NewBoolFlag(flagsplugin.Prefix("locations", prefix), flagsplugin.SelectDesc(flagsplugin.Prefix("locations", prefix), false), flagsplugin.WithHidden(hidden))) + flags.AddFlag(flagsplugin.NewBoolFlag(flagsplugin.Prefix("version-ids", prefix), flagsplugin.SelectDesc(flagsplugin.Prefix("version-ids", prefix), true), flagsplugin.WithHidden(hidden))) + AddSelectFlagsForEndDeviceVersionIdentifiers(flags, flagsplugin.Prefix("version-ids", prefix), hidden) + flags.AddFlag(flagsplugin.NewBoolFlag(flagsplugin.Prefix("network-ids", prefix), flagsplugin.SelectDesc(flagsplugin.Prefix("network-ids", prefix), true), flagsplugin.WithHidden(hidden))) + AddSelectFlagsForNetworkIdentifiers(flags, flagsplugin.Prefix("network-ids", prefix), hidden) + flags.AddFlag(flagsplugin.NewBoolFlag(flagsplugin.Prefix("attributes", prefix), flagsplugin.SelectDesc(flagsplugin.Prefix("attributes", prefix), false), flagsplugin.WithHidden(hidden))) } // SelectFromFlags outputs the fieldmask paths forApplicationInvalidatedDownlinks message from select flags. @@ -813,6 +977,36 @@ func PathsFromSelectFlagsForApplicationInvalidatedDownlinks(flags *pflag.FlagSet } else if selected && val { paths = append(paths, flagsplugin.Prefix("session_key_id", prefix)) } + if val, selected, err := flagsplugin.GetBool(flags, flagsplugin.Prefix("locations", prefix)); err != nil { + return nil, err + } else if selected && val { + paths = append(paths, flagsplugin.Prefix("locations", prefix)) + } + if val, selected, err := flagsplugin.GetBool(flags, flagsplugin.Prefix("version_ids", prefix)); err != nil { + return nil, err + } else if selected && val { + paths = append(paths, flagsplugin.Prefix("version_ids", prefix)) + } + if selectPaths, err := PathsFromSelectFlagsForEndDeviceVersionIdentifiers(flags, flagsplugin.Prefix("version_ids", prefix)); err != nil { + return nil, err + } else { + paths = append(paths, selectPaths...) + } + if val, selected, err := flagsplugin.GetBool(flags, flagsplugin.Prefix("network_ids", prefix)); err != nil { + return nil, err + } else if selected && val { + paths = append(paths, flagsplugin.Prefix("network_ids", prefix)) + } + if selectPaths, err := PathsFromSelectFlagsForNetworkIdentifiers(flags, flagsplugin.Prefix("network_ids", prefix)); err != nil { + return nil, err + } else { + paths = append(paths, selectPaths...) + } + if val, selected, err := flagsplugin.GetBool(flags, flagsplugin.Prefix("attributes", prefix)); err != nil { + return nil, err + } else if selected && val { + paths = append(paths, flagsplugin.Prefix("attributes", prefix)) + } return paths, nil } @@ -820,6 +1014,12 @@ func PathsFromSelectFlagsForApplicationInvalidatedDownlinks(flags *pflag.FlagSet func AddSelectFlagsForApplicationServiceData(flags *pflag.FlagSet, prefix string, hidden bool) { flags.AddFlag(flagsplugin.NewBoolFlag(flagsplugin.Prefix("service", prefix), flagsplugin.SelectDesc(flagsplugin.Prefix("service", prefix), false), flagsplugin.WithHidden(hidden))) flags.AddFlag(flagsplugin.NewBoolFlag(flagsplugin.Prefix("data", prefix), flagsplugin.SelectDesc(flagsplugin.Prefix("data", prefix), false), flagsplugin.WithHidden(hidden))) + flags.AddFlag(flagsplugin.NewBoolFlag(flagsplugin.Prefix("locations", prefix), flagsplugin.SelectDesc(flagsplugin.Prefix("locations", prefix), false), flagsplugin.WithHidden(hidden))) + flags.AddFlag(flagsplugin.NewBoolFlag(flagsplugin.Prefix("version-ids", prefix), flagsplugin.SelectDesc(flagsplugin.Prefix("version-ids", prefix), true), flagsplugin.WithHidden(hidden))) + AddSelectFlagsForEndDeviceVersionIdentifiers(flags, flagsplugin.Prefix("version-ids", prefix), hidden) + flags.AddFlag(flagsplugin.NewBoolFlag(flagsplugin.Prefix("network-ids", prefix), flagsplugin.SelectDesc(flagsplugin.Prefix("network-ids", prefix), true), flagsplugin.WithHidden(hidden))) + AddSelectFlagsForNetworkIdentifiers(flags, flagsplugin.Prefix("network-ids", prefix), hidden) + flags.AddFlag(flagsplugin.NewBoolFlag(flagsplugin.Prefix("attributes", prefix), flagsplugin.SelectDesc(flagsplugin.Prefix("attributes", prefix), false), flagsplugin.WithHidden(hidden))) } // SelectFromFlags outputs the fieldmask paths forApplicationServiceData message from select flags. @@ -834,6 +1034,36 @@ func PathsFromSelectFlagsForApplicationServiceData(flags *pflag.FlagSet, prefix } else if selected && val { paths = append(paths, flagsplugin.Prefix("data", prefix)) } + if val, selected, err := flagsplugin.GetBool(flags, flagsplugin.Prefix("locations", prefix)); err != nil { + return nil, err + } else if selected && val { + paths = append(paths, flagsplugin.Prefix("locations", prefix)) + } + if val, selected, err := flagsplugin.GetBool(flags, flagsplugin.Prefix("version_ids", prefix)); err != nil { + return nil, err + } else if selected && val { + paths = append(paths, flagsplugin.Prefix("version_ids", prefix)) + } + if selectPaths, err := PathsFromSelectFlagsForEndDeviceVersionIdentifiers(flags, flagsplugin.Prefix("version_ids", prefix)); err != nil { + return nil, err + } else { + paths = append(paths, selectPaths...) + } + if val, selected, err := flagsplugin.GetBool(flags, flagsplugin.Prefix("network_ids", prefix)); err != nil { + return nil, err + } else if selected && val { + paths = append(paths, flagsplugin.Prefix("network_ids", prefix)) + } + if selectPaths, err := PathsFromSelectFlagsForNetworkIdentifiers(flags, flagsplugin.Prefix("network_ids", prefix)); err != nil { + return nil, err + } else { + paths = append(paths, selectPaths...) + } + if val, selected, err := flagsplugin.GetBool(flags, flagsplugin.Prefix("attributes", prefix)); err != nil { + return nil, err + } else if selected && val { + paths = append(paths, flagsplugin.Prefix("attributes", prefix)) + } return paths, nil } diff --git a/pkg/ttnpb/messages_json.pb.go b/pkg/ttnpb/messages_json.pb.go index d73f3c4f35..8fdbb15b89 100644 --- a/pkg/ttnpb/messages_json.pb.go +++ b/pkg/ttnpb/messages_json.pb.go @@ -725,6 +725,18 @@ func (x *ApplicationUplink) MarshalProtoJSON(s *jsonplugin.MarshalState) { // NOTE: LastBatteryPercentage does not seem to implement MarshalProtoJSON. golang.MarshalMessage(s, x.LastBatteryPercentage) } + if x.Attributes != nil || s.HasField("attributes") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("attributes") + s.WriteObjectStart() + var wroteElement bool + for k, v := range x.Attributes { + s.WriteMoreIf(&wroteElement) + s.WriteObjectStringField(k) + s.WriteString(v) + } + s.WriteObjectEnd() + } s.WriteObjectEnd() } @@ -897,6 +909,16 @@ func (x *ApplicationUplink) UnmarshalProtoJSON(s *jsonplugin.UnmarshalState) { var v LastBatteryPercentage golang.UnmarshalMessage(s, &v) x.LastBatteryPercentage = &v + case "attributes": + s.AddField("attributes") + if s.ReadNil() { + x.Attributes = nil + return + } + x.Attributes = make(map[string]string) + s.ReadStringMap(func(key string) { + x.Attributes[key] = s.ReadString() + }) } }) } @@ -1011,6 +1033,18 @@ func (x *ApplicationUplinkNormalized) MarshalProtoJSON(s *jsonplugin.MarshalStat s.WriteObjectField("network_ids") x.NetworkIds.MarshalProtoJSON(s.WithField("network_ids")) } + if x.Attributes != nil || s.HasField("attributes") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("attributes") + s.WriteObjectStart() + var wroteElement bool + for k, v := range x.Attributes { + s.WriteMoreIf(&wroteElement) + s.WriteObjectStringField(k) + s.WriteString(v) + } + s.WriteObjectEnd() + } s.WriteObjectEnd() } @@ -1140,6 +1174,16 @@ func (x *ApplicationUplinkNormalized) UnmarshalProtoJSON(s *jsonplugin.Unmarshal } x.NetworkIds = &NetworkIdentifiers{} x.NetworkIds.UnmarshalProtoJSON(s.WithField("network_ids", true)) + case "attributes": + s.AddField("attributes") + if s.ReadNil() { + x.Attributes = nil + return + } + x.Attributes = make(map[string]string) + s.ReadStringMap(func(key string) { + x.Attributes[key] = s.ReadString() + }) } }) } @@ -1268,6 +1312,41 @@ func (x *ApplicationJoinAccept) MarshalProtoJSON(s *jsonplugin.MarshalState) { golang.MarshalTimestamp(s, x.ReceivedAt) } } + if x.Locations != nil || s.HasField("locations") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("locations") + s.WriteObjectStart() + var wroteElement bool + for k, v := range x.Locations { + s.WriteMoreIf(&wroteElement) + s.WriteObjectStringField(k) + v.MarshalProtoJSON(s.WithField("locations")) + } + s.WriteObjectEnd() + } + if x.VersionIds != nil || s.HasField("version_ids") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("version_ids") + // NOTE: EndDeviceVersionIdentifiers does not seem to implement MarshalProtoJSON. + golang.MarshalMessage(s, x.VersionIds) + } + if x.NetworkIds != nil || s.HasField("network_ids") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("network_ids") + x.NetworkIds.MarshalProtoJSON(s.WithField("network_ids")) + } + if x.Attributes != nil || s.HasField("attributes") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("attributes") + s.WriteObjectStart() + var wroteElement bool + for k, v := range x.Attributes { + s.WriteMoreIf(&wroteElement) + s.WriteObjectStringField(k) + s.WriteString(v) + } + s.WriteObjectEnd() + } s.WriteObjectEnd() } @@ -1327,6 +1406,45 @@ func (x *ApplicationJoinAccept) UnmarshalProtoJSON(s *jsonplugin.UnmarshalState) return } x.ReceivedAt = v + case "locations": + s.AddField("locations") + if s.ReadNil() { + x.Locations = nil + return + } + x.Locations = make(map[string]*Location) + s.ReadStringMap(func(key string) { + var v Location + v.UnmarshalProtoJSON(s) + x.Locations[key] = &v + }) + case "version_ids", "versionIds": + s.AddField("version_ids") + if s.ReadNil() { + x.VersionIds = nil + return + } + // NOTE: EndDeviceVersionIdentifiers does not seem to implement UnmarshalProtoJSON. + var v EndDeviceVersionIdentifiers + golang.UnmarshalMessage(s, &v) + x.VersionIds = &v + case "network_ids", "networkIds": + if s.ReadNil() { + x.NetworkIds = nil + return + } + x.NetworkIds = &NetworkIdentifiers{} + x.NetworkIds.UnmarshalProtoJSON(s.WithField("network_ids", true)) + case "attributes": + s.AddField("attributes") + if s.ReadNil() { + x.Attributes = nil + return + } + x.Attributes = make(map[string]string) + s.ReadStringMap(func(key string) { + x.Attributes[key] = s.ReadString() + }) } }) } @@ -1487,6 +1605,41 @@ func (x *ApplicationDownlink) MarshalProtoJSON(s *jsonplugin.MarshalState) { // NOTE: ApplicationDownlink_ConfirmedRetry does not seem to implement MarshalProtoJSON. golang.MarshalMessage(s, x.ConfirmedRetry) } + if x.Locations != nil || s.HasField("locations") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("locations") + s.WriteObjectStart() + var wroteElement bool + for k, v := range x.Locations { + s.WriteMoreIf(&wroteElement) + s.WriteObjectStringField(k) + v.MarshalProtoJSON(s.WithField("locations")) + } + s.WriteObjectEnd() + } + if x.VersionIds != nil || s.HasField("version_ids") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("version_ids") + // NOTE: EndDeviceVersionIdentifiers does not seem to implement MarshalProtoJSON. + golang.MarshalMessage(s, x.VersionIds) + } + if x.NetworkIds != nil || s.HasField("network_ids") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("network_ids") + x.NetworkIds.MarshalProtoJSON(s.WithField("network_ids")) + } + if x.Attributes != nil || s.HasField("attributes") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("attributes") + s.WriteObjectStart() + var wroteElement bool + for k, v := range x.Attributes { + s.WriteMoreIf(&wroteElement) + s.WriteObjectStringField(k) + s.WriteString(v) + } + s.WriteObjectEnd() + } s.WriteObjectEnd() } @@ -1564,6 +1717,45 @@ func (x *ApplicationDownlink) UnmarshalProtoJSON(s *jsonplugin.UnmarshalState) { var v ApplicationDownlink_ConfirmedRetry golang.UnmarshalMessage(s, &v) x.ConfirmedRetry = &v + case "locations": + s.AddField("locations") + if s.ReadNil() { + x.Locations = nil + return + } + x.Locations = make(map[string]*Location) + s.ReadStringMap(func(key string) { + var v Location + v.UnmarshalProtoJSON(s) + x.Locations[key] = &v + }) + case "version_ids", "versionIds": + s.AddField("version_ids") + if s.ReadNil() { + x.VersionIds = nil + return + } + // NOTE: EndDeviceVersionIdentifiers does not seem to implement UnmarshalProtoJSON. + var v EndDeviceVersionIdentifiers + golang.UnmarshalMessage(s, &v) + x.VersionIds = &v + case "network_ids", "networkIds": + if s.ReadNil() { + x.NetworkIds = nil + return + } + x.NetworkIds = &NetworkIdentifiers{} + x.NetworkIds.UnmarshalProtoJSON(s.WithField("network_ids", true)) + case "attributes": + s.AddField("attributes") + if s.ReadNil() { + x.Attributes = nil + return + } + x.Attributes = make(map[string]string) + s.ReadStringMap(func(key string) { + x.Attributes[key] = s.ReadString() + }) } }) } @@ -1655,6 +1847,41 @@ func (x *ApplicationDownlinkFailed) MarshalProtoJSON(s *jsonplugin.MarshalState) // NOTE: ErrorDetails does not seem to implement MarshalProtoJSON. golang.MarshalMessage(s, x.Error) } + if x.Locations != nil || s.HasField("locations") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("locations") + s.WriteObjectStart() + var wroteElement bool + for k, v := range x.Locations { + s.WriteMoreIf(&wroteElement) + s.WriteObjectStringField(k) + v.MarshalProtoJSON(s.WithField("locations")) + } + s.WriteObjectEnd() + } + if x.VersionIds != nil || s.HasField("version_ids") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("version_ids") + // NOTE: EndDeviceVersionIdentifiers does not seem to implement MarshalProtoJSON. + golang.MarshalMessage(s, x.VersionIds) + } + if x.NetworkIds != nil || s.HasField("network_ids") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("network_ids") + x.NetworkIds.MarshalProtoJSON(s.WithField("network_ids")) + } + if x.Attributes != nil || s.HasField("attributes") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("attributes") + s.WriteObjectStart() + var wroteElement bool + for k, v := range x.Attributes { + s.WriteMoreIf(&wroteElement) + s.WriteObjectStringField(k) + s.WriteString(v) + } + s.WriteObjectEnd() + } s.WriteObjectEnd() } @@ -1689,6 +1916,45 @@ func (x *ApplicationDownlinkFailed) UnmarshalProtoJSON(s *jsonplugin.UnmarshalSt var v ErrorDetails golang.UnmarshalMessage(s, &v) x.Error = &v + case "locations": + s.AddField("locations") + if s.ReadNil() { + x.Locations = nil + return + } + x.Locations = make(map[string]*Location) + s.ReadStringMap(func(key string) { + var v Location + v.UnmarshalProtoJSON(s) + x.Locations[key] = &v + }) + case "version_ids", "versionIds": + s.AddField("version_ids") + if s.ReadNil() { + x.VersionIds = nil + return + } + // NOTE: EndDeviceVersionIdentifiers does not seem to implement UnmarshalProtoJSON. + var v EndDeviceVersionIdentifiers + golang.UnmarshalMessage(s, &v) + x.VersionIds = &v + case "network_ids", "networkIds": + if s.ReadNil() { + x.NetworkIds = nil + return + } + x.NetworkIds = &NetworkIdentifiers{} + x.NetworkIds.UnmarshalProtoJSON(s.WithField("network_ids", true)) + case "attributes": + s.AddField("attributes") + if s.ReadNil() { + x.Attributes = nil + return + } + x.Attributes = make(map[string]string) + s.ReadStringMap(func(key string) { + x.Attributes[key] = s.ReadString() + }) } }) } @@ -1727,6 +1993,41 @@ func (x *ApplicationInvalidatedDownlinks) MarshalProtoJSON(s *jsonplugin.Marshal s.WriteObjectField("session_key_id") s.WriteBytes(x.SessionKeyId) } + if x.Locations != nil || s.HasField("locations") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("locations") + s.WriteObjectStart() + var wroteElement bool + for k, v := range x.Locations { + s.WriteMoreIf(&wroteElement) + s.WriteObjectStringField(k) + v.MarshalProtoJSON(s.WithField("locations")) + } + s.WriteObjectEnd() + } + if x.VersionIds != nil || s.HasField("version_ids") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("version_ids") + // NOTE: EndDeviceVersionIdentifiers does not seem to implement MarshalProtoJSON. + golang.MarshalMessage(s, x.VersionIds) + } + if x.NetworkIds != nil || s.HasField("network_ids") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("network_ids") + x.NetworkIds.MarshalProtoJSON(s.WithField("network_ids")) + } + if x.Attributes != nil || s.HasField("attributes") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("attributes") + s.WriteObjectStart() + var wroteElement bool + for k, v := range x.Attributes { + s.WriteMoreIf(&wroteElement) + s.WriteObjectStringField(k) + s.WriteString(v) + } + s.WriteObjectEnd() + } s.WriteObjectEnd() } @@ -1768,6 +2069,45 @@ func (x *ApplicationInvalidatedDownlinks) UnmarshalProtoJSON(s *jsonplugin.Unmar case "session_key_id", "sessionKeyId": s.AddField("session_key_id") x.SessionKeyId = s.ReadBytes() + case "locations": + s.AddField("locations") + if s.ReadNil() { + x.Locations = nil + return + } + x.Locations = make(map[string]*Location) + s.ReadStringMap(func(key string) { + var v Location + v.UnmarshalProtoJSON(s) + x.Locations[key] = &v + }) + case "version_ids", "versionIds": + s.AddField("version_ids") + if s.ReadNil() { + x.VersionIds = nil + return + } + // NOTE: EndDeviceVersionIdentifiers does not seem to implement UnmarshalProtoJSON. + var v EndDeviceVersionIdentifiers + golang.UnmarshalMessage(s, &v) + x.VersionIds = &v + case "network_ids", "networkIds": + if s.ReadNil() { + x.NetworkIds = nil + return + } + x.NetworkIds = &NetworkIdentifiers{} + x.NetworkIds.UnmarshalProtoJSON(s.WithField("network_ids", true)) + case "attributes": + s.AddField("attributes") + if s.ReadNil() { + x.Attributes = nil + return + } + x.Attributes = make(map[string]string) + s.ReadStringMap(func(key string) { + x.Attributes[key] = s.ReadString() + }) } }) } @@ -1859,6 +2199,142 @@ func (x *DownlinkQueueOperationErrorDetails) UnmarshalJSON(b []byte) error { return jsonplugin.DefaultUnmarshalerConfig.Unmarshal(b, x) } +// MarshalProtoJSON marshals the ApplicationServiceData message to JSON. +func (x *ApplicationServiceData) MarshalProtoJSON(s *jsonplugin.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Service != "" || s.HasField("service") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("service") + s.WriteString(x.Service) + } + if x.Data != nil || s.HasField("data") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("data") + if x.Data == nil { + s.WriteNil() + } else { + golang.MarshalStruct(s, x.Data) + } + } + if x.Locations != nil || s.HasField("locations") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("locations") + s.WriteObjectStart() + var wroteElement bool + for k, v := range x.Locations { + s.WriteMoreIf(&wroteElement) + s.WriteObjectStringField(k) + v.MarshalProtoJSON(s.WithField("locations")) + } + s.WriteObjectEnd() + } + if x.VersionIds != nil || s.HasField("version_ids") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("version_ids") + // NOTE: EndDeviceVersionIdentifiers does not seem to implement MarshalProtoJSON. + golang.MarshalMessage(s, x.VersionIds) + } + if x.NetworkIds != nil || s.HasField("network_ids") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("network_ids") + x.NetworkIds.MarshalProtoJSON(s.WithField("network_ids")) + } + if x.Attributes != nil || s.HasField("attributes") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("attributes") + s.WriteObjectStart() + var wroteElement bool + for k, v := range x.Attributes { + s.WriteMoreIf(&wroteElement) + s.WriteObjectStringField(k) + s.WriteString(v) + } + s.WriteObjectEnd() + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the ApplicationServiceData to JSON. +func (x *ApplicationServiceData) MarshalJSON() ([]byte, error) { + return jsonplugin.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the ApplicationServiceData message from JSON. +func (x *ApplicationServiceData) UnmarshalProtoJSON(s *jsonplugin.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.ReadAny() // ignore unknown field + case "service": + s.AddField("service") + x.Service = s.ReadString() + case "data": + s.AddField("data") + if s.ReadNil() { + x.Data = nil + return + } + v := golang.UnmarshalStruct(s) + if s.Err() != nil { + return + } + x.Data = v + case "locations": + s.AddField("locations") + if s.ReadNil() { + x.Locations = nil + return + } + x.Locations = make(map[string]*Location) + s.ReadStringMap(func(key string) { + var v Location + v.UnmarshalProtoJSON(s) + x.Locations[key] = &v + }) + case "version_ids", "versionIds": + s.AddField("version_ids") + if s.ReadNil() { + x.VersionIds = nil + return + } + // NOTE: EndDeviceVersionIdentifiers does not seem to implement UnmarshalProtoJSON. + var v EndDeviceVersionIdentifiers + golang.UnmarshalMessage(s, &v) + x.VersionIds = &v + case "network_ids", "networkIds": + if s.ReadNil() { + x.NetworkIds = nil + return + } + x.NetworkIds = &NetworkIdentifiers{} + x.NetworkIds.UnmarshalProtoJSON(s.WithField("network_ids", true)) + case "attributes": + s.AddField("attributes") + if s.ReadNil() { + x.Attributes = nil + return + } + x.Attributes = make(map[string]string) + s.ReadStringMap(func(key string) { + x.Attributes[key] = s.ReadString() + }) + } + }) +} + +// UnmarshalJSON unmarshals the ApplicationServiceData from JSON. +func (x *ApplicationServiceData) UnmarshalJSON(b []byte) error { + return jsonplugin.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + // MarshalProtoJSON marshals the ApplicationUp message to JSON. func (x *ApplicationUp) MarshalProtoJSON(s *jsonplugin.MarshalState) { if x == nil { @@ -1931,8 +2407,7 @@ func (x *ApplicationUp) MarshalProtoJSON(s *jsonplugin.MarshalState) { case *ApplicationUp_ServiceData: s.WriteMoreIf(&wroteField) s.WriteObjectField("service_data") - // NOTE: ApplicationServiceData does not seem to implement MarshalProtoJSON. - golang.MarshalMessage(s, ov.ServiceData) + ov.ServiceData.MarshalProtoJSON(s.WithField("service_data")) } } if x.Simulated || s.HasField("simulated") { @@ -2073,17 +2548,14 @@ func (x *ApplicationUp) UnmarshalProtoJSON(s *jsonplugin.UnmarshalState) { ov.LocationSolved = &ApplicationLocation{} ov.LocationSolved.UnmarshalProtoJSON(s.WithField("location_solved", true)) case "service_data", "serviceData": - s.AddField("service_data") ov := &ApplicationUp_ServiceData{} x.Up = ov if s.ReadNil() { ov.ServiceData = nil return } - // NOTE: ApplicationServiceData does not seem to implement UnmarshalProtoJSON. - var v ApplicationServiceData - golang.UnmarshalMessage(s, &v) - ov.ServiceData = &v + ov.ServiceData = &ApplicationServiceData{} + ov.ServiceData.UnmarshalProtoJSON(s.WithField("service_data", true)) case "simulated": s.AddField("simulated") x.Simulated = s.ReadBool() diff --git a/pkg/ttnpb/qrcodegenerator.pb.paths.fm.go b/pkg/ttnpb/qrcodegenerator.pb.paths.fm.go index 0c6cc9e100..f921d78b07 100644 --- a/pkg/ttnpb/qrcodegenerator.pb.paths.fm.go +++ b/pkg/ttnpb/qrcodegenerator.pb.paths.fm.go @@ -407,6 +407,7 @@ var GenerateEndDeviceQRCodeRequestFieldPathsNested = []string{ "end_device.mac_state.last_network_initiated_downlink_at", "end_device.mac_state.lorawan_version", "end_device.mac_state.pending_application_downlink", + "end_device.mac_state.pending_application_downlink.attributes", "end_device.mac_state.pending_application_downlink.class_b_c", "end_device.mac_state.pending_application_downlink.class_b_c.absolute_time", "end_device.mac_state.pending_application_downlink.class_b_c.gateways", @@ -420,8 +421,22 @@ var GenerateEndDeviceQRCodeRequestFieldPathsNested = []string{ "end_device.mac_state.pending_application_downlink.f_cnt", "end_device.mac_state.pending_application_downlink.f_port", "end_device.mac_state.pending_application_downlink.frm_payload", + "end_device.mac_state.pending_application_downlink.locations", + "end_device.mac_state.pending_application_downlink.network_ids", + "end_device.mac_state.pending_application_downlink.network_ids.cluster_address", + "end_device.mac_state.pending_application_downlink.network_ids.cluster_id", + "end_device.mac_state.pending_application_downlink.network_ids.net_id", + "end_device.mac_state.pending_application_downlink.network_ids.ns_id", + "end_device.mac_state.pending_application_downlink.network_ids.tenant_address", + "end_device.mac_state.pending_application_downlink.network_ids.tenant_id", "end_device.mac_state.pending_application_downlink.priority", "end_device.mac_state.pending_application_downlink.session_key_id", + "end_device.mac_state.pending_application_downlink.version_ids", + "end_device.mac_state.pending_application_downlink.version_ids.band_id", + "end_device.mac_state.pending_application_downlink.version_ids.brand_id", + "end_device.mac_state.pending_application_downlink.version_ids.firmware_version", + "end_device.mac_state.pending_application_downlink.version_ids.hardware_version", + "end_device.mac_state.pending_application_downlink.version_ids.model_id", "end_device.mac_state.pending_join_request", "end_device.mac_state.pending_join_request.cf_list", "end_device.mac_state.pending_join_request.cf_list.ch_masks", @@ -623,6 +638,7 @@ var GenerateEndDeviceQRCodeRequestFieldPathsNested = []string{ "end_device.pending_mac_state.last_network_initiated_downlink_at", "end_device.pending_mac_state.lorawan_version", "end_device.pending_mac_state.pending_application_downlink", + "end_device.pending_mac_state.pending_application_downlink.attributes", "end_device.pending_mac_state.pending_application_downlink.class_b_c", "end_device.pending_mac_state.pending_application_downlink.class_b_c.absolute_time", "end_device.pending_mac_state.pending_application_downlink.class_b_c.gateways", @@ -636,8 +652,22 @@ var GenerateEndDeviceQRCodeRequestFieldPathsNested = []string{ "end_device.pending_mac_state.pending_application_downlink.f_cnt", "end_device.pending_mac_state.pending_application_downlink.f_port", "end_device.pending_mac_state.pending_application_downlink.frm_payload", + "end_device.pending_mac_state.pending_application_downlink.locations", + "end_device.pending_mac_state.pending_application_downlink.network_ids", + "end_device.pending_mac_state.pending_application_downlink.network_ids.cluster_address", + "end_device.pending_mac_state.pending_application_downlink.network_ids.cluster_id", + "end_device.pending_mac_state.pending_application_downlink.network_ids.net_id", + "end_device.pending_mac_state.pending_application_downlink.network_ids.ns_id", + "end_device.pending_mac_state.pending_application_downlink.network_ids.tenant_address", + "end_device.pending_mac_state.pending_application_downlink.network_ids.tenant_id", "end_device.pending_mac_state.pending_application_downlink.priority", "end_device.pending_mac_state.pending_application_downlink.session_key_id", + "end_device.pending_mac_state.pending_application_downlink.version_ids", + "end_device.pending_mac_state.pending_application_downlink.version_ids.band_id", + "end_device.pending_mac_state.pending_application_downlink.version_ids.brand_id", + "end_device.pending_mac_state.pending_application_downlink.version_ids.firmware_version", + "end_device.pending_mac_state.pending_application_downlink.version_ids.hardware_version", + "end_device.pending_mac_state.pending_application_downlink.version_ids.model_id", "end_device.pending_mac_state.pending_join_request", "end_device.pending_mac_state.pending_join_request.cf_list", "end_device.pending_mac_state.pending_join_request.cf_list.ch_masks", @@ -1195,6 +1225,7 @@ var ParseEndDeviceQRCodeResponseFieldPathsNested = []string{ "end_device_template.end_device.mac_state.last_network_initiated_downlink_at", "end_device_template.end_device.mac_state.lorawan_version", "end_device_template.end_device.mac_state.pending_application_downlink", + "end_device_template.end_device.mac_state.pending_application_downlink.attributes", "end_device_template.end_device.mac_state.pending_application_downlink.class_b_c", "end_device_template.end_device.mac_state.pending_application_downlink.class_b_c.absolute_time", "end_device_template.end_device.mac_state.pending_application_downlink.class_b_c.gateways", @@ -1208,8 +1239,22 @@ var ParseEndDeviceQRCodeResponseFieldPathsNested = []string{ "end_device_template.end_device.mac_state.pending_application_downlink.f_cnt", "end_device_template.end_device.mac_state.pending_application_downlink.f_port", "end_device_template.end_device.mac_state.pending_application_downlink.frm_payload", + "end_device_template.end_device.mac_state.pending_application_downlink.locations", + "end_device_template.end_device.mac_state.pending_application_downlink.network_ids", + "end_device_template.end_device.mac_state.pending_application_downlink.network_ids.cluster_address", + "end_device_template.end_device.mac_state.pending_application_downlink.network_ids.cluster_id", + "end_device_template.end_device.mac_state.pending_application_downlink.network_ids.net_id", + "end_device_template.end_device.mac_state.pending_application_downlink.network_ids.ns_id", + "end_device_template.end_device.mac_state.pending_application_downlink.network_ids.tenant_address", + "end_device_template.end_device.mac_state.pending_application_downlink.network_ids.tenant_id", "end_device_template.end_device.mac_state.pending_application_downlink.priority", "end_device_template.end_device.mac_state.pending_application_downlink.session_key_id", + "end_device_template.end_device.mac_state.pending_application_downlink.version_ids", + "end_device_template.end_device.mac_state.pending_application_downlink.version_ids.band_id", + "end_device_template.end_device.mac_state.pending_application_downlink.version_ids.brand_id", + "end_device_template.end_device.mac_state.pending_application_downlink.version_ids.firmware_version", + "end_device_template.end_device.mac_state.pending_application_downlink.version_ids.hardware_version", + "end_device_template.end_device.mac_state.pending_application_downlink.version_ids.model_id", "end_device_template.end_device.mac_state.pending_join_request", "end_device_template.end_device.mac_state.pending_join_request.cf_list", "end_device_template.end_device.mac_state.pending_join_request.cf_list.ch_masks", @@ -1411,6 +1456,7 @@ var ParseEndDeviceQRCodeResponseFieldPathsNested = []string{ "end_device_template.end_device.pending_mac_state.last_network_initiated_downlink_at", "end_device_template.end_device.pending_mac_state.lorawan_version", "end_device_template.end_device.pending_mac_state.pending_application_downlink", + "end_device_template.end_device.pending_mac_state.pending_application_downlink.attributes", "end_device_template.end_device.pending_mac_state.pending_application_downlink.class_b_c", "end_device_template.end_device.pending_mac_state.pending_application_downlink.class_b_c.absolute_time", "end_device_template.end_device.pending_mac_state.pending_application_downlink.class_b_c.gateways", @@ -1424,8 +1470,22 @@ var ParseEndDeviceQRCodeResponseFieldPathsNested = []string{ "end_device_template.end_device.pending_mac_state.pending_application_downlink.f_cnt", "end_device_template.end_device.pending_mac_state.pending_application_downlink.f_port", "end_device_template.end_device.pending_mac_state.pending_application_downlink.frm_payload", + "end_device_template.end_device.pending_mac_state.pending_application_downlink.locations", + "end_device_template.end_device.pending_mac_state.pending_application_downlink.network_ids", + "end_device_template.end_device.pending_mac_state.pending_application_downlink.network_ids.cluster_address", + "end_device_template.end_device.pending_mac_state.pending_application_downlink.network_ids.cluster_id", + "end_device_template.end_device.pending_mac_state.pending_application_downlink.network_ids.net_id", + "end_device_template.end_device.pending_mac_state.pending_application_downlink.network_ids.ns_id", + "end_device_template.end_device.pending_mac_state.pending_application_downlink.network_ids.tenant_address", + "end_device_template.end_device.pending_mac_state.pending_application_downlink.network_ids.tenant_id", "end_device_template.end_device.pending_mac_state.pending_application_downlink.priority", "end_device_template.end_device.pending_mac_state.pending_application_downlink.session_key_id", + "end_device_template.end_device.pending_mac_state.pending_application_downlink.version_ids", + "end_device_template.end_device.pending_mac_state.pending_application_downlink.version_ids.band_id", + "end_device_template.end_device.pending_mac_state.pending_application_downlink.version_ids.brand_id", + "end_device_template.end_device.pending_mac_state.pending_application_downlink.version_ids.firmware_version", + "end_device_template.end_device.pending_mac_state.pending_application_downlink.version_ids.hardware_version", + "end_device_template.end_device.pending_mac_state.pending_application_downlink.version_ids.model_id", "end_device_template.end_device.pending_mac_state.pending_join_request", "end_device_template.end_device.pending_mac_state.pending_join_request.cf_list", "end_device_template.end_device.pending_mac_state.pending_join_request.cf_list.ch_masks", diff --git a/pkg/webui/locales/ja.json b/pkg/webui/locales/ja.json index 512ee5a683..efe3f93b0a 100644 --- a/pkg/webui/locales/ja.json +++ b/pkg/webui/locales/ja.json @@ -2143,7 +2143,7 @@ "error:pkg/applicationserver/io/web:validate_body": "本文を検証する", "error:pkg/applicationserver/io/web:webhook_not_found": "Webhookが見つかりません", "error:pkg/applicationserver/io:buffer_full": "バッファがいっぱいです", - "error:pkg/applicationserver/metadata/redis:cache_miss": "キャッシュミス", + "error:pkg/applicationserver/metadata:field_mask_path_not_supported": "", "error:pkg/applicationserver/redis:application_uid": "無効なアプリケーションUID `{application_uid}`", "error:pkg/applicationserver/redis:invalid_fieldmask": "無効なフィールドマスク", "error:pkg/applicationserver/redis:invalid_identifiers": "無効な識別子", @@ -2156,7 +2156,6 @@ "error:pkg/applicationserver:field_value": "無効なフィールド `{field}` の値", "error:pkg/applicationserver:formatter_script_too_large": "フォーマッタースクリプトのサイズが許可された最大サイズを超えています", "error:pkg/applicationserver:invalid_timeout": "無効なタイムアウト`{timeout}`", - "error:pkg/applicationserver:invalid_ttl": "無効なTTL `{ttl}`", "error:pkg/applicationserver:join_server_unavailable": "JoinEUI `{join_eui}` ではジョインサーバが利用できません", "error:pkg/applicationserver:linking_not_implemented": "リンキングが実装されていません", "error:pkg/applicationserver:listen_frontend": "アドレス `{address}` でフロントエンドリスナ `{protocol}` を起動失敗", diff --git a/sdk/js/generated/api-definition.json b/sdk/js/generated/api-definition.json index 2e234ff95c..4de765ea3b 100644 --- a/sdk/js/generated/api-definition.json +++ b/sdk/js/generated/api-definition.json @@ -745,6 +745,7 @@ "simulated", "up", "up.downlink_ack", + "up.downlink_ack.attributes", "up.downlink_ack.class_b_c", "up.downlink_ack.class_b_c.absolute_time", "up.downlink_ack.class_b_c.gateways", @@ -758,10 +759,26 @@ "up.downlink_ack.f_cnt", "up.downlink_ack.f_port", "up.downlink_ack.frm_payload", + "up.downlink_ack.locations", + "up.downlink_ack.network_ids", + "up.downlink_ack.network_ids.cluster_address", + "up.downlink_ack.network_ids.cluster_id", + "up.downlink_ack.network_ids.net_id", + "up.downlink_ack.network_ids.ns_id", + "up.downlink_ack.network_ids.tenant_address", + "up.downlink_ack.network_ids.tenant_id", "up.downlink_ack.priority", "up.downlink_ack.session_key_id", + "up.downlink_ack.version_ids", + "up.downlink_ack.version_ids.band_id", + "up.downlink_ack.version_ids.brand_id", + "up.downlink_ack.version_ids.firmware_version", + "up.downlink_ack.version_ids.hardware_version", + "up.downlink_ack.version_ids.model_id", "up.downlink_failed", + "up.downlink_failed.attributes", "up.downlink_failed.downlink", + "up.downlink_failed.downlink.attributes", "up.downlink_failed.downlink.class_b_c", "up.downlink_failed.downlink.class_b_c.absolute_time", "up.downlink_failed.downlink.class_b_c.gateways", @@ -775,8 +792,22 @@ "up.downlink_failed.downlink.f_cnt", "up.downlink_failed.downlink.f_port", "up.downlink_failed.downlink.frm_payload", + "up.downlink_failed.downlink.locations", + "up.downlink_failed.downlink.network_ids", + "up.downlink_failed.downlink.network_ids.cluster_address", + "up.downlink_failed.downlink.network_ids.cluster_id", + "up.downlink_failed.downlink.network_ids.net_id", + "up.downlink_failed.downlink.network_ids.ns_id", + "up.downlink_failed.downlink.network_ids.tenant_address", + "up.downlink_failed.downlink.network_ids.tenant_id", "up.downlink_failed.downlink.priority", "up.downlink_failed.downlink.session_key_id", + "up.downlink_failed.downlink.version_ids", + "up.downlink_failed.downlink.version_ids.band_id", + "up.downlink_failed.downlink.version_ids.brand_id", + "up.downlink_failed.downlink.version_ids.firmware_version", + "up.downlink_failed.downlink.version_ids.hardware_version", + "up.downlink_failed.downlink.version_ids.model_id", "up.downlink_failed.error", "up.downlink_failed.error.attributes", "up.downlink_failed.error.cause", @@ -791,7 +822,22 @@ "up.downlink_failed.error.message_format", "up.downlink_failed.error.name", "up.downlink_failed.error.namespace", + "up.downlink_failed.locations", + "up.downlink_failed.network_ids", + "up.downlink_failed.network_ids.cluster_address", + "up.downlink_failed.network_ids.cluster_id", + "up.downlink_failed.network_ids.net_id", + "up.downlink_failed.network_ids.ns_id", + "up.downlink_failed.network_ids.tenant_address", + "up.downlink_failed.network_ids.tenant_id", + "up.downlink_failed.version_ids", + "up.downlink_failed.version_ids.band_id", + "up.downlink_failed.version_ids.brand_id", + "up.downlink_failed.version_ids.firmware_version", + "up.downlink_failed.version_ids.hardware_version", + "up.downlink_failed.version_ids.model_id", "up.downlink_nack", + "up.downlink_nack.attributes", "up.downlink_nack.class_b_c", "up.downlink_nack.class_b_c.absolute_time", "up.downlink_nack.class_b_c.gateways", @@ -805,13 +851,43 @@ "up.downlink_nack.f_cnt", "up.downlink_nack.f_port", "up.downlink_nack.frm_payload", + "up.downlink_nack.locations", + "up.downlink_nack.network_ids", + "up.downlink_nack.network_ids.cluster_address", + "up.downlink_nack.network_ids.cluster_id", + "up.downlink_nack.network_ids.net_id", + "up.downlink_nack.network_ids.ns_id", + "up.downlink_nack.network_ids.tenant_address", + "up.downlink_nack.network_ids.tenant_id", "up.downlink_nack.priority", "up.downlink_nack.session_key_id", + "up.downlink_nack.version_ids", + "up.downlink_nack.version_ids.band_id", + "up.downlink_nack.version_ids.brand_id", + "up.downlink_nack.version_ids.firmware_version", + "up.downlink_nack.version_ids.hardware_version", + "up.downlink_nack.version_ids.model_id", "up.downlink_queue_invalidated", + "up.downlink_queue_invalidated.attributes", "up.downlink_queue_invalidated.downlinks", "up.downlink_queue_invalidated.last_f_cnt_down", + "up.downlink_queue_invalidated.locations", + "up.downlink_queue_invalidated.network_ids", + "up.downlink_queue_invalidated.network_ids.cluster_address", + "up.downlink_queue_invalidated.network_ids.cluster_id", + "up.downlink_queue_invalidated.network_ids.net_id", + "up.downlink_queue_invalidated.network_ids.ns_id", + "up.downlink_queue_invalidated.network_ids.tenant_address", + "up.downlink_queue_invalidated.network_ids.tenant_id", "up.downlink_queue_invalidated.session_key_id", + "up.downlink_queue_invalidated.version_ids", + "up.downlink_queue_invalidated.version_ids.band_id", + "up.downlink_queue_invalidated.version_ids.brand_id", + "up.downlink_queue_invalidated.version_ids.firmware_version", + "up.downlink_queue_invalidated.version_ids.hardware_version", + "up.downlink_queue_invalidated.version_ids.model_id", "up.downlink_queued", + "up.downlink_queued.attributes", "up.downlink_queued.class_b_c", "up.downlink_queued.class_b_c.absolute_time", "up.downlink_queued.class_b_c.gateways", @@ -825,9 +901,24 @@ "up.downlink_queued.f_cnt", "up.downlink_queued.f_port", "up.downlink_queued.frm_payload", + "up.downlink_queued.locations", + "up.downlink_queued.network_ids", + "up.downlink_queued.network_ids.cluster_address", + "up.downlink_queued.network_ids.cluster_id", + "up.downlink_queued.network_ids.net_id", + "up.downlink_queued.network_ids.ns_id", + "up.downlink_queued.network_ids.tenant_address", + "up.downlink_queued.network_ids.tenant_id", "up.downlink_queued.priority", "up.downlink_queued.session_key_id", + "up.downlink_queued.version_ids", + "up.downlink_queued.version_ids.band_id", + "up.downlink_queued.version_ids.brand_id", + "up.downlink_queued.version_ids.firmware_version", + "up.downlink_queued.version_ids.hardware_version", + "up.downlink_queued.version_ids.model_id", "up.downlink_sent", + "up.downlink_sent.attributes", "up.downlink_sent.class_b_c", "up.downlink_sent.class_b_c.absolute_time", "up.downlink_sent.class_b_c.gateways", @@ -841,17 +932,46 @@ "up.downlink_sent.f_cnt", "up.downlink_sent.f_port", "up.downlink_sent.frm_payload", + "up.downlink_sent.locations", + "up.downlink_sent.network_ids", + "up.downlink_sent.network_ids.cluster_address", + "up.downlink_sent.network_ids.cluster_id", + "up.downlink_sent.network_ids.net_id", + "up.downlink_sent.network_ids.ns_id", + "up.downlink_sent.network_ids.tenant_address", + "up.downlink_sent.network_ids.tenant_id", "up.downlink_sent.priority", "up.downlink_sent.session_key_id", + "up.downlink_sent.version_ids", + "up.downlink_sent.version_ids.band_id", + "up.downlink_sent.version_ids.brand_id", + "up.downlink_sent.version_ids.firmware_version", + "up.downlink_sent.version_ids.hardware_version", + "up.downlink_sent.version_ids.model_id", "up.join_accept", "up.join_accept.app_s_key", "up.join_accept.app_s_key.encrypted_key", "up.join_accept.app_s_key.kek_label", "up.join_accept.app_s_key.key", + "up.join_accept.attributes", "up.join_accept.invalidated_downlinks", + "up.join_accept.locations", + "up.join_accept.network_ids", + "up.join_accept.network_ids.cluster_address", + "up.join_accept.network_ids.cluster_id", + "up.join_accept.network_ids.net_id", + "up.join_accept.network_ids.ns_id", + "up.join_accept.network_ids.tenant_address", + "up.join_accept.network_ids.tenant_id", "up.join_accept.pending_session", "up.join_accept.received_at", "up.join_accept.session_key_id", + "up.join_accept.version_ids", + "up.join_accept.version_ids.band_id", + "up.join_accept.version_ids.brand_id", + "up.join_accept.version_ids.firmware_version", + "up.join_accept.version_ids.hardware_version", + "up.join_accept.version_ids.model_id", "up.location_solved", "up.location_solved.attributes", "up.location_solved.location", @@ -862,13 +982,29 @@ "up.location_solved.location.source", "up.location_solved.service", "up.service_data", + "up.service_data.attributes", "up.service_data.data", + "up.service_data.locations", + "up.service_data.network_ids", + "up.service_data.network_ids.cluster_address", + "up.service_data.network_ids.cluster_id", + "up.service_data.network_ids.net_id", + "up.service_data.network_ids.ns_id", + "up.service_data.network_ids.tenant_address", + "up.service_data.network_ids.tenant_id", "up.service_data.service", + "up.service_data.version_ids", + "up.service_data.version_ids.band_id", + "up.service_data.version_ids.brand_id", + "up.service_data.version_ids.firmware_version", + "up.service_data.version_ids.hardware_version", + "up.service_data.version_ids.model_id", "up.uplink_message", "up.uplink_message.app_s_key", "up.uplink_message.app_s_key.encrypted_key", "up.uplink_message.app_s_key.kek_label", "up.uplink_message.app_s_key.key", + "up.uplink_message.attributes", "up.uplink_message.confirmed", "up.uplink_message.consumed_airtime", "up.uplink_message.decoded_payload", @@ -924,6 +1060,7 @@ "up.uplink_message.version_ids.hardware_version", "up.uplink_message.version_ids.model_id", "up.uplink_normalized", + "up.uplink_normalized.attributes", "up.uplink_normalized.confirmed", "up.uplink_normalized.consumed_airtime", "up.uplink_normalized.f_cnt", @@ -4754,6 +4891,7 @@ "mac_state.last_network_initiated_downlink_at", "mac_state.lorawan_version", "mac_state.pending_application_downlink", + "mac_state.pending_application_downlink.attributes", "mac_state.pending_application_downlink.class_b_c", "mac_state.pending_application_downlink.class_b_c.absolute_time", "mac_state.pending_application_downlink.class_b_c.gateways", @@ -4762,8 +4900,22 @@ "mac_state.pending_application_downlink.f_cnt", "mac_state.pending_application_downlink.f_port", "mac_state.pending_application_downlink.frm_payload", + "mac_state.pending_application_downlink.locations", + "mac_state.pending_application_downlink.network_ids", + "mac_state.pending_application_downlink.network_ids.cluster_address", + "mac_state.pending_application_downlink.network_ids.cluster_id", + "mac_state.pending_application_downlink.network_ids.net_id", + "mac_state.pending_application_downlink.network_ids.ns_id", + "mac_state.pending_application_downlink.network_ids.tenant_address", + "mac_state.pending_application_downlink.network_ids.tenant_id", "mac_state.pending_application_downlink.priority", "mac_state.pending_application_downlink.session_key_id", + "mac_state.pending_application_downlink.version_ids", + "mac_state.pending_application_downlink.version_ids.band_id", + "mac_state.pending_application_downlink.version_ids.brand_id", + "mac_state.pending_application_downlink.version_ids.firmware_version", + "mac_state.pending_application_downlink.version_ids.hardware_version", + "mac_state.pending_application_downlink.version_ids.model_id", "mac_state.pending_relay_downlink", "mac_state.pending_relay_downlink.raw_payload", "mac_state.pending_requests", @@ -5370,6 +5522,13 @@ "mac_state.pending_application_downlink.f_cnt", "mac_state.pending_application_downlink.f_port", "mac_state.pending_application_downlink.frm_payload", + "mac_state.pending_application_downlink.network_ids", + "mac_state.pending_application_downlink.network_ids.cluster_address", + "mac_state.pending_application_downlink.network_ids.cluster_id", + "mac_state.pending_application_downlink.network_ids.net_id", + "mac_state.pending_application_downlink.network_ids.ns_id", + "mac_state.pending_application_downlink.network_ids.tenant_address", + "mac_state.pending_application_downlink.network_ids.tenant_id", "mac_state.pending_application_downlink.priority", "mac_state.pending_application_downlink.session_key_id", "mac_state.pending_relay_downlink", @@ -5966,6 +6125,7 @@ "mac_state.last_network_initiated_downlink_at", "mac_state.lorawan_version", "mac_state.pending_application_downlink", + "mac_state.pending_application_downlink.attributes", "mac_state.pending_application_downlink.class_b_c", "mac_state.pending_application_downlink.class_b_c.absolute_time", "mac_state.pending_application_downlink.class_b_c.gateways", @@ -5974,8 +6134,22 @@ "mac_state.pending_application_downlink.f_cnt", "mac_state.pending_application_downlink.f_port", "mac_state.pending_application_downlink.frm_payload", + "mac_state.pending_application_downlink.locations", + "mac_state.pending_application_downlink.network_ids", + "mac_state.pending_application_downlink.network_ids.cluster_address", + "mac_state.pending_application_downlink.network_ids.cluster_id", + "mac_state.pending_application_downlink.network_ids.net_id", + "mac_state.pending_application_downlink.network_ids.ns_id", + "mac_state.pending_application_downlink.network_ids.tenant_address", + "mac_state.pending_application_downlink.network_ids.tenant_id", "mac_state.pending_application_downlink.priority", "mac_state.pending_application_downlink.session_key_id", + "mac_state.pending_application_downlink.version_ids", + "mac_state.pending_application_downlink.version_ids.band_id", + "mac_state.pending_application_downlink.version_ids.brand_id", + "mac_state.pending_application_downlink.version_ids.firmware_version", + "mac_state.pending_application_downlink.version_ids.hardware_version", + "mac_state.pending_application_downlink.version_ids.model_id", "mac_state.pending_relay_downlink", "mac_state.pending_relay_downlink.raw_payload", "mac_state.pending_requests", diff --git a/sdk/js/generated/api.json b/sdk/js/generated/api.json index f953f176c2..5c4c2aae19 100644 --- a/sdk/js/generated/api.json +++ b/sdk/js/generated/api.json @@ -38661,6 +38661,110 @@ "isoneof": false, "oneofdecl": "", "defaultValue": "" + }, + { + "name": "locations", + "description": "End device location metadata, set by the Application Server while handling the message.", + "label": "repeated", + "type": "LocationsEntry", + "longType": "ApplicationDownlink.LocationsEntry", + "fullType": "ttn.lorawan.v3.ApplicationDownlink.LocationsEntry", + "ismap": true, + "isoneof": false, + "oneofdecl": "", + "defaultValue": "" + }, + { + "name": "version_ids", + "description": "End device version identifiers, set by the Application Server while handling the message.", + "label": "", + "type": "EndDeviceVersionIdentifiers", + "longType": "EndDeviceVersionIdentifiers", + "fullType": "ttn.lorawan.v3.EndDeviceVersionIdentifiers", + "ismap": false, + "isoneof": false, + "oneofdecl": "", + "defaultValue": "" + }, + { + "name": "network_ids", + "description": "Network identifiers, set by the Network Server that handles the message.", + "label": "", + "type": "NetworkIdentifiers", + "longType": "NetworkIdentifiers", + "fullType": "ttn.lorawan.v3.NetworkIdentifiers", + "ismap": false, + "isoneof": false, + "oneofdecl": "", + "defaultValue": "" + }, + { + "name": "attributes", + "description": "Attributes for devices, set by the Application Server while handling the message.", + "label": "repeated", + "type": "AttributesEntry", + "longType": "ApplicationDownlink.AttributesEntry", + "fullType": "ttn.lorawan.v3.ApplicationDownlink.AttributesEntry", + "ismap": true, + "isoneof": false, + "oneofdecl": "", + "defaultValue": "", + "options": { + "validate.rules": [ + { + "name": "map.max_pairs", + "value": 10 + }, + { + "name": "map.keys.string.max_len", + "value": 36 + }, + { + "name": "map.keys.string.pattern", + "value": "^[a-z0-9](?:[-]?[a-z0-9]){2,}$" + }, + { + "name": "map.values.string.max_len", + "value": 200 + } + ] + } + } + ] + }, + { + "name": "AttributesEntry", + "longName": "ApplicationDownlink.AttributesEntry", + "fullName": "ttn.lorawan.v3.ApplicationDownlink.AttributesEntry", + "description": "", + "hasExtensions": false, + "hasFields": true, + "hasOneofs": false, + "extensions": [], + "fields": [ + { + "name": "key", + "description": "", + "label": "", + "type": "string", + "longType": "string", + "fullType": "string", + "ismap": false, + "isoneof": false, + "oneofdecl": "", + "defaultValue": "" + }, + { + "name": "value", + "description": "", + "label": "", + "type": "string", + "longType": "string", + "fullType": "string", + "ismap": false, + "isoneof": false, + "oneofdecl": "", + "defaultValue": "" } ] }, @@ -38748,6 +38852,42 @@ } ] }, + { + "name": "LocationsEntry", + "longName": "ApplicationDownlink.LocationsEntry", + "fullName": "ttn.lorawan.v3.ApplicationDownlink.LocationsEntry", + "description": "", + "hasExtensions": false, + "hasFields": true, + "hasOneofs": false, + "extensions": [], + "fields": [ + { + "name": "key", + "description": "", + "label": "", + "type": "string", + "longType": "string", + "fullType": "string", + "ismap": false, + "isoneof": false, + "oneofdecl": "", + "defaultValue": "" + }, + { + "name": "value", + "description": "", + "label": "", + "type": "Location", + "longType": "Location", + "fullType": "ttn.lorawan.v3.Location", + "ismap": false, + "isoneof": false, + "oneofdecl": "", + "defaultValue": "" + } + ] + }, { "name": "ApplicationDownlinkFailed", "longName": "ApplicationDownlinkFailed", @@ -38797,83 +38937,71 @@ } ] } - } - ] - }, - { - "name": "ApplicationDownlinks", - "longName": "ApplicationDownlinks", - "fullName": "ttn.lorawan.v3.ApplicationDownlinks", - "description": "", - "hasExtensions": false, - "hasFields": true, - "hasOneofs": false, - "extensions": [], - "fields": [ + }, { - "name": "downlinks", - "description": "", + "name": "locations", + "description": "End device location metadata, set by the Application Server while handling the message.", "label": "repeated", - "type": "ApplicationDownlink", - "longType": "ApplicationDownlink", - "fullType": "ttn.lorawan.v3.ApplicationDownlink", - "ismap": false, + "type": "LocationsEntry", + "longType": "ApplicationDownlinkFailed.LocationsEntry", + "fullType": "ttn.lorawan.v3.ApplicationDownlinkFailed.LocationsEntry", + "ismap": true, "isoneof": false, "oneofdecl": "", "defaultValue": "" - } - ] - }, - { - "name": "ApplicationInvalidatedDownlinks", - "longName": "ApplicationInvalidatedDownlinks", - "fullName": "ttn.lorawan.v3.ApplicationInvalidatedDownlinks", - "description": "", - "hasExtensions": false, - "hasFields": true, - "hasOneofs": false, - "extensions": [], - "fields": [ + }, { - "name": "downlinks", - "description": "", - "label": "repeated", - "type": "ApplicationDownlink", - "longType": "ApplicationDownlink", - "fullType": "ttn.lorawan.v3.ApplicationDownlink", + "name": "version_ids", + "description": "End device version identifiers, set by the Application Server while handling the message.", + "label": "", + "type": "EndDeviceVersionIdentifiers", + "longType": "EndDeviceVersionIdentifiers", + "fullType": "ttn.lorawan.v3.EndDeviceVersionIdentifiers", "ismap": false, "isoneof": false, "oneofdecl": "", "defaultValue": "" }, { - "name": "last_f_cnt_down", - "description": "", + "name": "network_ids", + "description": "Network identifiers, set by the Network Server that handles the message.", "label": "", - "type": "uint32", - "longType": "uint32", - "fullType": "uint32", + "type": "NetworkIdentifiers", + "longType": "NetworkIdentifiers", + "fullType": "ttn.lorawan.v3.NetworkIdentifiers", "ismap": false, "isoneof": false, "oneofdecl": "", "defaultValue": "" }, { - "name": "session_key_id", - "description": "", - "label": "", - "type": "bytes", - "longType": "bytes", - "fullType": "bytes", - "ismap": false, + "name": "attributes", + "description": "Attributes for devices, set by the Application Server while handling the message.", + "label": "repeated", + "type": "AttributesEntry", + "longType": "ApplicationDownlinkFailed.AttributesEntry", + "fullType": "ttn.lorawan.v3.ApplicationDownlinkFailed.AttributesEntry", + "ismap": true, "isoneof": false, "oneofdecl": "", "defaultValue": "", "options": { "validate.rules": [ { - "name": "bytes.max_len", - "value": 2048 + "name": "map.max_pairs", + "value": 10 + }, + { + "name": "map.keys.string.max_len", + "value": 36 + }, + { + "name": "map.keys.string.pattern", + "value": "^[a-z0-9](?:[-]?[a-z0-9]){2,}$" + }, + { + "name": "map.values.string.max_len", + "value": 200 } ] } @@ -38881,9 +39009,9 @@ ] }, { - "name": "ApplicationJoinAccept", - "longName": "ApplicationJoinAccept", - "fullName": "ttn.lorawan.v3.ApplicationJoinAccept", + "name": "AttributesEntry", + "longName": "ApplicationDownlinkFailed.AttributesEntry", + "fullName": "ttn.lorawan.v3.ApplicationDownlinkFailed.AttributesEntry", "description": "", "hasExtensions": false, "hasFields": true, @@ -38891,87 +39019,631 @@ "extensions": [], "fields": [ { - "name": "session_key_id", - "description": "Join Server issued identifier for the session keys negotiated in this join.", + "name": "key", + "description": "", "label": "", - "type": "bytes", - "longType": "bytes", - "fullType": "bytes", + "type": "string", + "longType": "string", + "fullType": "string", "ismap": false, "isoneof": false, "oneofdecl": "", - "defaultValue": "", - "options": { - "validate.rules": [ - { - "name": "bytes.max_len", - "value": 2048 - } - ] - } + "defaultValue": "" }, { - "name": "app_s_key", - "description": "Encrypted Application Session Key (if Join Server sent it to Network Server).", + "name": "value", + "description": "", + "label": "", + "type": "string", + "longType": "string", + "fullType": "string", + "ismap": false, + "isoneof": false, + "oneofdecl": "", + "defaultValue": "" + } + ] + }, + { + "name": "LocationsEntry", + "longName": "ApplicationDownlinkFailed.LocationsEntry", + "fullName": "ttn.lorawan.v3.ApplicationDownlinkFailed.LocationsEntry", + "description": "", + "hasExtensions": false, + "hasFields": true, + "hasOneofs": false, + "extensions": [], + "fields": [ + { + "name": "key", + "description": "", + "label": "", + "type": "string", + "longType": "string", + "fullType": "string", + "ismap": false, + "isoneof": false, + "oneofdecl": "", + "defaultValue": "" + }, + { + "name": "value", + "description": "", + "label": "", + "type": "Location", + "longType": "Location", + "fullType": "ttn.lorawan.v3.Location", + "ismap": false, + "isoneof": false, + "oneofdecl": "", + "defaultValue": "" + } + ] + }, + { + "name": "ApplicationDownlinks", + "longName": "ApplicationDownlinks", + "fullName": "ttn.lorawan.v3.ApplicationDownlinks", + "description": "", + "hasExtensions": false, + "hasFields": true, + "hasOneofs": false, + "extensions": [], + "fields": [ + { + "name": "downlinks", + "description": "", + "label": "repeated", + "type": "ApplicationDownlink", + "longType": "ApplicationDownlink", + "fullType": "ttn.lorawan.v3.ApplicationDownlink", + "ismap": false, + "isoneof": false, + "oneofdecl": "", + "defaultValue": "" + } + ] + }, + { + "name": "ApplicationInvalidatedDownlinks", + "longName": "ApplicationInvalidatedDownlinks", + "fullName": "ttn.lorawan.v3.ApplicationInvalidatedDownlinks", + "description": "", + "hasExtensions": false, + "hasFields": true, + "hasOneofs": false, + "extensions": [], + "fields": [ + { + "name": "downlinks", + "description": "", + "label": "repeated", + "type": "ApplicationDownlink", + "longType": "ApplicationDownlink", + "fullType": "ttn.lorawan.v3.ApplicationDownlink", + "ismap": false, + "isoneof": false, + "oneofdecl": "", + "defaultValue": "" + }, + { + "name": "last_f_cnt_down", + "description": "", + "label": "", + "type": "uint32", + "longType": "uint32", + "fullType": "uint32", + "ismap": false, + "isoneof": false, + "oneofdecl": "", + "defaultValue": "" + }, + { + "name": "session_key_id", + "description": "", + "label": "", + "type": "bytes", + "longType": "bytes", + "fullType": "bytes", + "ismap": false, + "isoneof": false, + "oneofdecl": "", + "defaultValue": "", + "options": { + "validate.rules": [ + { + "name": "bytes.max_len", + "value": 2048 + } + ] + } + }, + { + "name": "locations", + "description": "End device location metadata, set by the Application Server while handling the message.", + "label": "repeated", + "type": "LocationsEntry", + "longType": "ApplicationInvalidatedDownlinks.LocationsEntry", + "fullType": "ttn.lorawan.v3.ApplicationInvalidatedDownlinks.LocationsEntry", + "ismap": true, + "isoneof": false, + "oneofdecl": "", + "defaultValue": "" + }, + { + "name": "version_ids", + "description": "End device version identifiers, set by the Application Server while handling the message.", + "label": "", + "type": "EndDeviceVersionIdentifiers", + "longType": "EndDeviceVersionIdentifiers", + "fullType": "ttn.lorawan.v3.EndDeviceVersionIdentifiers", + "ismap": false, + "isoneof": false, + "oneofdecl": "", + "defaultValue": "" + }, + { + "name": "network_ids", + "description": "Network identifiers, set by the Network Server that handles the message.", + "label": "", + "type": "NetworkIdentifiers", + "longType": "NetworkIdentifiers", + "fullType": "ttn.lorawan.v3.NetworkIdentifiers", + "ismap": false, + "isoneof": false, + "oneofdecl": "", + "defaultValue": "" + }, + { + "name": "attributes", + "description": "Attributes for devices, set by the Application Server while handling the message.", + "label": "repeated", + "type": "AttributesEntry", + "longType": "ApplicationInvalidatedDownlinks.AttributesEntry", + "fullType": "ttn.lorawan.v3.ApplicationInvalidatedDownlinks.AttributesEntry", + "ismap": true, + "isoneof": false, + "oneofdecl": "", + "defaultValue": "", + "options": { + "validate.rules": [ + { + "name": "map.max_pairs", + "value": 10 + }, + { + "name": "map.keys.string.max_len", + "value": 36 + }, + { + "name": "map.keys.string.pattern", + "value": "^[a-z0-9](?:[-]?[a-z0-9]){2,}$" + }, + { + "name": "map.values.string.max_len", + "value": 200 + } + ] + } + } + ] + }, + { + "name": "AttributesEntry", + "longName": "ApplicationInvalidatedDownlinks.AttributesEntry", + "fullName": "ttn.lorawan.v3.ApplicationInvalidatedDownlinks.AttributesEntry", + "description": "", + "hasExtensions": false, + "hasFields": true, + "hasOneofs": false, + "extensions": [], + "fields": [ + { + "name": "key", + "description": "", + "label": "", + "type": "string", + "longType": "string", + "fullType": "string", + "ismap": false, + "isoneof": false, + "oneofdecl": "", + "defaultValue": "" + }, + { + "name": "value", + "description": "", + "label": "", + "type": "string", + "longType": "string", + "fullType": "string", + "ismap": false, + "isoneof": false, + "oneofdecl": "", + "defaultValue": "" + } + ] + }, + { + "name": "LocationsEntry", + "longName": "ApplicationInvalidatedDownlinks.LocationsEntry", + "fullName": "ttn.lorawan.v3.ApplicationInvalidatedDownlinks.LocationsEntry", + "description": "", + "hasExtensions": false, + "hasFields": true, + "hasOneofs": false, + "extensions": [], + "fields": [ + { + "name": "key", + "description": "", + "label": "", + "type": "string", + "longType": "string", + "fullType": "string", + "ismap": false, + "isoneof": false, + "oneofdecl": "", + "defaultValue": "" + }, + { + "name": "value", + "description": "", + "label": "", + "type": "Location", + "longType": "Location", + "fullType": "ttn.lorawan.v3.Location", + "ismap": false, + "isoneof": false, + "oneofdecl": "", + "defaultValue": "" + } + ] + }, + { + "name": "ApplicationJoinAccept", + "longName": "ApplicationJoinAccept", + "fullName": "ttn.lorawan.v3.ApplicationJoinAccept", + "description": "", + "hasExtensions": false, + "hasFields": true, + "hasOneofs": false, + "extensions": [], + "fields": [ + { + "name": "session_key_id", + "description": "Join Server issued identifier for the session keys negotiated in this join.", + "label": "", + "type": "bytes", + "longType": "bytes", + "fullType": "bytes", + "ismap": false, + "isoneof": false, + "oneofdecl": "", + "defaultValue": "", + "options": { + "validate.rules": [ + { + "name": "bytes.max_len", + "value": 2048 + } + ] + } + }, + { + "name": "app_s_key", + "description": "Encrypted Application Session Key (if Join Server sent it to Network Server).", + "label": "", + "type": "KeyEnvelope", + "longType": "KeyEnvelope", + "fullType": "ttn.lorawan.v3.KeyEnvelope", + "ismap": false, + "isoneof": false, + "oneofdecl": "", + "defaultValue": "" + }, + { + "name": "invalidated_downlinks", + "description": "Downlink messages in the queue that got invalidated because of the session change.", + "label": "repeated", + "type": "ApplicationDownlink", + "longType": "ApplicationDownlink", + "fullType": "ttn.lorawan.v3.ApplicationDownlink", + "ismap": false, + "isoneof": false, + "oneofdecl": "", + "defaultValue": "" + }, + { + "name": "pending_session", + "description": "Indicates whether the security context refers to the pending session, i.e. when this join-accept is an answer to a\nrejoin-request.", + "label": "", + "type": "bool", + "longType": "bool", + "fullType": "bool", + "ismap": false, + "isoneof": false, + "oneofdecl": "", + "defaultValue": "" + }, + { + "name": "received_at", + "description": "Server time when the Network Server received the message.", + "label": "", + "type": "Timestamp", + "longType": "google.protobuf.Timestamp", + "fullType": "google.protobuf.Timestamp", + "ismap": false, + "isoneof": false, + "oneofdecl": "", + "defaultValue": "", + "options": { + "validate.rules": [ + { + "name": "timestamp.required", + "value": true + } + ] + } + }, + { + "name": "locations", + "description": "End device location metadata, set by the Application Server while handling the message.", + "label": "repeated", + "type": "LocationsEntry", + "longType": "ApplicationJoinAccept.LocationsEntry", + "fullType": "ttn.lorawan.v3.ApplicationJoinAccept.LocationsEntry", + "ismap": true, + "isoneof": false, + "oneofdecl": "", + "defaultValue": "" + }, + { + "name": "version_ids", + "description": "End device version identifiers, set by the Application Server while handling the message.", + "label": "", + "type": "EndDeviceVersionIdentifiers", + "longType": "EndDeviceVersionIdentifiers", + "fullType": "ttn.lorawan.v3.EndDeviceVersionIdentifiers", + "ismap": false, + "isoneof": false, + "oneofdecl": "", + "defaultValue": "" + }, + { + "name": "network_ids", + "description": "Network identifiers, set by the Network Server that handles the message.", + "label": "", + "type": "NetworkIdentifiers", + "longType": "NetworkIdentifiers", + "fullType": "ttn.lorawan.v3.NetworkIdentifiers", + "ismap": false, + "isoneof": false, + "oneofdecl": "", + "defaultValue": "" + }, + { + "name": "attributes", + "description": "Attributes for devices, set by the Application Server while handling the message.", + "label": "repeated", + "type": "AttributesEntry", + "longType": "ApplicationJoinAccept.AttributesEntry", + "fullType": "ttn.lorawan.v3.ApplicationJoinAccept.AttributesEntry", + "ismap": true, + "isoneof": false, + "oneofdecl": "", + "defaultValue": "", + "options": { + "validate.rules": [ + { + "name": "map.max_pairs", + "value": 10 + }, + { + "name": "map.keys.string.max_len", + "value": 36 + }, + { + "name": "map.keys.string.pattern", + "value": "^[a-z0-9](?:[-]?[a-z0-9]){2,}$" + }, + { + "name": "map.values.string.max_len", + "value": 200 + } + ] + } + } + ] + }, + { + "name": "AttributesEntry", + "longName": "ApplicationJoinAccept.AttributesEntry", + "fullName": "ttn.lorawan.v3.ApplicationJoinAccept.AttributesEntry", + "description": "", + "hasExtensions": false, + "hasFields": true, + "hasOneofs": false, + "extensions": [], + "fields": [ + { + "name": "key", + "description": "", + "label": "", + "type": "string", + "longType": "string", + "fullType": "string", + "ismap": false, + "isoneof": false, + "oneofdecl": "", + "defaultValue": "" + }, + { + "name": "value", + "description": "", + "label": "", + "type": "string", + "longType": "string", + "fullType": "string", + "ismap": false, + "isoneof": false, + "oneofdecl": "", + "defaultValue": "" + } + ] + }, + { + "name": "LocationsEntry", + "longName": "ApplicationJoinAccept.LocationsEntry", + "fullName": "ttn.lorawan.v3.ApplicationJoinAccept.LocationsEntry", + "description": "", + "hasExtensions": false, + "hasFields": true, + "hasOneofs": false, + "extensions": [], + "fields": [ + { + "name": "key", + "description": "", + "label": "", + "type": "string", + "longType": "string", + "fullType": "string", + "ismap": false, + "isoneof": false, + "oneofdecl": "", + "defaultValue": "" + }, + { + "name": "value", + "description": "", + "label": "", + "type": "Location", + "longType": "Location", + "fullType": "ttn.lorawan.v3.Location", + "ismap": false, + "isoneof": false, + "oneofdecl": "", + "defaultValue": "" + } + ] + }, + { + "name": "ApplicationLocation", + "longName": "ApplicationLocation", + "fullName": "ttn.lorawan.v3.ApplicationLocation", + "description": "", + "hasExtensions": false, + "hasFields": true, + "hasOneofs": false, + "extensions": [], + "fields": [ + { + "name": "service", + "description": "", + "label": "", + "type": "string", + "longType": "string", + "fullType": "string", + "ismap": false, + "isoneof": false, + "oneofdecl": "", + "defaultValue": "" + }, + { + "name": "location", + "description": "", + "label": "", + "type": "Location", + "longType": "Location", + "fullType": "ttn.lorawan.v3.Location", + "ismap": false, + "isoneof": false, + "oneofdecl": "", + "defaultValue": "", + "options": { + "validate.rules": [ + { + "name": "message.required", + "value": true + } + ] + } + }, + { + "name": "attributes", + "description": "", + "label": "repeated", + "type": "AttributesEntry", + "longType": "ApplicationLocation.AttributesEntry", + "fullType": "ttn.lorawan.v3.ApplicationLocation.AttributesEntry", + "ismap": true, + "isoneof": false, + "oneofdecl": "", + "defaultValue": "", + "options": { + "validate.rules": [ + { + "name": "map.max_pairs", + "value": 10 + }, + { + "name": "map.keys.string.max_len", + "value": 36 + }, + { + "name": "map.keys.string.pattern", + "value": "^[a-z0-9](?:[-]?[a-z0-9]){2,}$" + }, + { + "name": "map.values.string.max_len", + "value": 200 + } + ] + } + } + ] + }, + { + "name": "AttributesEntry", + "longName": "ApplicationLocation.AttributesEntry", + "fullName": "ttn.lorawan.v3.ApplicationLocation.AttributesEntry", + "description": "", + "hasExtensions": false, + "hasFields": true, + "hasOneofs": false, + "extensions": [], + "fields": [ + { + "name": "key", + "description": "", "label": "", - "type": "KeyEnvelope", - "longType": "KeyEnvelope", - "fullType": "ttn.lorawan.v3.KeyEnvelope", - "ismap": false, - "isoneof": false, - "oneofdecl": "", - "defaultValue": "" - }, - { - "name": "invalidated_downlinks", - "description": "Downlink messages in the queue that got invalidated because of the session change.", - "label": "repeated", - "type": "ApplicationDownlink", - "longType": "ApplicationDownlink", - "fullType": "ttn.lorawan.v3.ApplicationDownlink", + "type": "string", + "longType": "string", + "fullType": "string", "ismap": false, "isoneof": false, "oneofdecl": "", "defaultValue": "" }, { - "name": "pending_session", - "description": "Indicates whether the security context refers to the pending session, i.e. when this join-accept is an answer to a\nrejoin-request.", + "name": "value", + "description": "", "label": "", - "type": "bool", - "longType": "bool", - "fullType": "bool", + "type": "string", + "longType": "string", + "fullType": "string", "ismap": false, "isoneof": false, "oneofdecl": "", "defaultValue": "" - }, - { - "name": "received_at", - "description": "Server time when the Network Server received the message.", - "label": "", - "type": "Timestamp", - "longType": "google.protobuf.Timestamp", - "fullType": "google.protobuf.Timestamp", - "ismap": false, - "isoneof": false, - "oneofdecl": "", - "defaultValue": "", - "options": { - "validate.rules": [ - { - "name": "timestamp.required", - "value": true - } - ] - } } ] }, { - "name": "ApplicationLocation", - "longName": "ApplicationLocation", - "fullName": "ttn.lorawan.v3.ApplicationLocation", + "name": "ApplicationServiceData", + "longName": "ApplicationServiceData", + "fullName": "ttn.lorawan.v3.ApplicationServiceData", "description": "", "hasExtensions": false, "hasFields": true, @@ -38991,32 +39663,60 @@ "defaultValue": "" }, { - "name": "location", + "name": "data", "description": "", "label": "", - "type": "Location", - "longType": "Location", - "fullType": "ttn.lorawan.v3.Location", + "type": "Struct", + "longType": "google.protobuf.Struct", + "fullType": "google.protobuf.Struct", "ismap": false, "isoneof": false, "oneofdecl": "", - "defaultValue": "", - "options": { - "validate.rules": [ - { - "name": "message.required", - "value": true - } - ] - } + "defaultValue": "" + }, + { + "name": "locations", + "description": "End device location metadata, set by the Application Server while handling the message.", + "label": "repeated", + "type": "LocationsEntry", + "longType": "ApplicationServiceData.LocationsEntry", + "fullType": "ttn.lorawan.v3.ApplicationServiceData.LocationsEntry", + "ismap": true, + "isoneof": false, + "oneofdecl": "", + "defaultValue": "" + }, + { + "name": "version_ids", + "description": "End device version identifiers, set by the Application Server while handling the message.", + "label": "", + "type": "EndDeviceVersionIdentifiers", + "longType": "EndDeviceVersionIdentifiers", + "fullType": "ttn.lorawan.v3.EndDeviceVersionIdentifiers", + "ismap": false, + "isoneof": false, + "oneofdecl": "", + "defaultValue": "" + }, + { + "name": "network_ids", + "description": "Network identifiers, set by the Network Server that handles the message.", + "label": "", + "type": "NetworkIdentifiers", + "longType": "NetworkIdentifiers", + "fullType": "ttn.lorawan.v3.NetworkIdentifiers", + "ismap": false, + "isoneof": false, + "oneofdecl": "", + "defaultValue": "" }, { "name": "attributes", - "description": "", + "description": "Attributes for devices, set by the Application Server while handling the message.", "label": "repeated", "type": "AttributesEntry", - "longType": "ApplicationLocation.AttributesEntry", - "fullType": "ttn.lorawan.v3.ApplicationLocation.AttributesEntry", + "longType": "ApplicationServiceData.AttributesEntry", + "fullType": "ttn.lorawan.v3.ApplicationServiceData.AttributesEntry", "ismap": true, "isoneof": false, "oneofdecl": "", @@ -39046,8 +39746,8 @@ }, { "name": "AttributesEntry", - "longName": "ApplicationLocation.AttributesEntry", - "fullName": "ttn.lorawan.v3.ApplicationLocation.AttributesEntry", + "longName": "ApplicationServiceData.AttributesEntry", + "fullName": "ttn.lorawan.v3.ApplicationServiceData.AttributesEntry", "description": "", "hasExtensions": false, "hasFields": true, @@ -39081,9 +39781,9 @@ ] }, { - "name": "ApplicationServiceData", - "longName": "ApplicationServiceData", - "fullName": "ttn.lorawan.v3.ApplicationServiceData", + "name": "LocationsEntry", + "longName": "ApplicationServiceData.LocationsEntry", + "fullName": "ttn.lorawan.v3.ApplicationServiceData.LocationsEntry", "description": "", "hasExtensions": false, "hasFields": true, @@ -39091,7 +39791,7 @@ "extensions": [], "fields": [ { - "name": "service", + "name": "key", "description": "", "label": "", "type": "string", @@ -39103,12 +39803,12 @@ "defaultValue": "" }, { - "name": "data", + "name": "value", "description": "", "label": "", - "type": "Struct", - "longType": "google.protobuf.Struct", - "fullType": "google.protobuf.Struct", + "type": "Location", + "longType": "Location", + "fullType": "ttn.lorawan.v3.Location", "ismap": false, "isoneof": false, "oneofdecl": "", @@ -39603,6 +40303,74 @@ "isoneof": false, "oneofdecl": "", "defaultValue": "" + }, + { + "name": "attributes", + "description": "Attributes for devices, set by the Application Server while handling the message.", + "label": "repeated", + "type": "AttributesEntry", + "longType": "ApplicationUplink.AttributesEntry", + "fullType": "ttn.lorawan.v3.ApplicationUplink.AttributesEntry", + "ismap": true, + "isoneof": false, + "oneofdecl": "", + "defaultValue": "", + "options": { + "validate.rules": [ + { + "name": "map.max_pairs", + "value": 10 + }, + { + "name": "map.keys.string.max_len", + "value": 36 + }, + { + "name": "map.keys.string.pattern", + "value": "^[a-z0-9](?:[-]?[a-z0-9]){2,}$" + }, + { + "name": "map.values.string.max_len", + "value": 200 + } + ] + } + } + ] + }, + { + "name": "AttributesEntry", + "longName": "ApplicationUplink.AttributesEntry", + "fullName": "ttn.lorawan.v3.ApplicationUplink.AttributesEntry", + "description": "", + "hasExtensions": false, + "hasFields": true, + "hasOneofs": false, + "extensions": [], + "fields": [ + { + "name": "key", + "description": "", + "label": "", + "type": "string", + "longType": "string", + "fullType": "string", + "ismap": false, + "isoneof": false, + "oneofdecl": "", + "defaultValue": "" + }, + { + "name": "value", + "description": "", + "label": "", + "type": "string", + "longType": "string", + "fullType": "string", + "ismap": false, + "isoneof": false, + "oneofdecl": "", + "defaultValue": "" } ] }, @@ -39869,6 +40637,74 @@ "isoneof": false, "oneofdecl": "", "defaultValue": "" + }, + { + "name": "attributes", + "description": "Attributes for devices, set by the Application Server while handling the message.", + "label": "repeated", + "type": "AttributesEntry", + "longType": "ApplicationUplinkNormalized.AttributesEntry", + "fullType": "ttn.lorawan.v3.ApplicationUplinkNormalized.AttributesEntry", + "ismap": true, + "isoneof": false, + "oneofdecl": "", + "defaultValue": "", + "options": { + "validate.rules": [ + { + "name": "map.max_pairs", + "value": 10 + }, + { + "name": "map.keys.string.max_len", + "value": 36 + }, + { + "name": "map.keys.string.pattern", + "value": "^[a-z0-9](?:[-]?[a-z0-9]){2,}$" + }, + { + "name": "map.values.string.max_len", + "value": 200 + } + ] + } + } + ] + }, + { + "name": "AttributesEntry", + "longName": "ApplicationUplinkNormalized.AttributesEntry", + "fullName": "ttn.lorawan.v3.ApplicationUplinkNormalized.AttributesEntry", + "description": "", + "hasExtensions": false, + "hasFields": true, + "hasOneofs": false, + "extensions": [], + "fields": [ + { + "name": "key", + "description": "", + "label": "", + "type": "string", + "longType": "string", + "fullType": "string", + "ismap": false, + "isoneof": false, + "oneofdecl": "", + "defaultValue": "" + }, + { + "name": "value", + "description": "", + "label": "", + "type": "string", + "longType": "string", + "fullType": "string", + "ismap": false, + "isoneof": false, + "oneofdecl": "", + "defaultValue": "" } ] }, diff --git a/sdk/js/generated/device-entity-map.json b/sdk/js/generated/device-entity-map.json index 0d65c728ec..62da12da47 100644 --- a/sdk/js/generated/device-entity-map.json +++ b/sdk/js/generated/device-entity-map.json @@ -2091,6 +2091,10 @@ "ns", "ns" ], + "attributes": [ + "ns", + "read_only" + ], "class_b_c": { "_root": [ "ns", @@ -2125,6 +2129,40 @@ "ns", "ns" ], + "locations": [ + "ns", + "read_only" + ], + "network_ids": { + "_root": [ + "ns", + "ns" + ], + "cluster_address": [ + "ns", + "ns" + ], + "cluster_id": [ + "ns", + "ns" + ], + "net_id": [ + "ns", + "ns" + ], + "ns_id": [ + "ns", + "ns" + ], + "tenant_address": [ + "ns", + "ns" + ], + "tenant_id": [ + "ns", + "ns" + ] + }, "priority": [ "ns", "ns" @@ -2132,7 +2170,33 @@ "session_key_id": [ "ns", "ns" - ] + ], + "version_ids": { + "_root": [ + "ns", + "read_only" + ], + "band_id": [ + "ns", + "read_only" + ], + "brand_id": [ + "ns", + "read_only" + ], + "firmware_version": [ + "ns", + "read_only" + ], + "hardware_version": [ + "ns", + "read_only" + ], + "model_id": [ + "ns", + "read_only" + ] + } }, "pending_relay_downlink": { "_root": [