diff --git a/.github/workflows/changelog-cutoff.yml b/.github/workflows/changelog-cutoff.yml index ba4a24aa49..e34d4583f8 100644 --- a/.github/workflows/changelog-cutoff.yml +++ b/.github/workflows/changelog-cutoff.yml @@ -14,7 +14,7 @@ env: jobs: cutoff_changelog: name: Cutoff changelog - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 timeout-minutes: 30 steps: - name: Check branch diff --git a/.github/workflows/console-e2e.yml b/.github/workflows/console-e2e.yml index 314fd5a998..53326d700f 100644 --- a/.github/workflows/console-e2e.yml +++ b/.github/workflows/console-e2e.yml @@ -29,7 +29,7 @@ env: jobs: determine-if-required: name: Determine if run is required - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 outputs: needs-to-run: ${{ steps.check-result.outputs.passed != 'true' }} hash: ${{ steps.get-hash.outputs.hash }} @@ -57,7 +57,7 @@ jobs: run: test -f .cache/passed && echo "passed=$(cat .cache/passed)" >> $GITHUB_OUTPUT preparation: name: Run preparations - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 needs: determine-if-required if: needs.determine-if-required.outputs.needs-to-run == 'true' timeout-minutes: 30 @@ -113,7 +113,7 @@ jobs: path: build.zip end-to-end: name: Main frontend end-to-end tests (Chrome) - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 env: NODE_ENV: development CYPRESS_MACHINE_NUMBER: ${{ matrix.machines }} @@ -213,8 +213,8 @@ jobs: name: cypress-failed-test-spec path: .cache/.failed-specs-*.txt cross-browser-smoke: - name: Cross-browser smoke tests (Firefox 115.7.0 ESR) - runs-on: ubuntu-22.04 + name: Cross-browser smoke tests (Firefox 129.0) + runs-on: ubuntu-24.04 env: NODE_ENV: development needs: [determine-if-required, preparation] @@ -240,7 +240,7 @@ jobs: run: unzip -o build.zip - name: Install FF 115.7.0 ESR run: | - wget --no-verbose -O /tmp/firefox.tar.bz2 https://download-installer.cdn.mozilla.net/pub/firefox/releases/115.7.0esr/linux-x86_64/en-US/firefox-115.7.0esr.tar.bz2 + wget --no-verbose -O /tmp/firefox.tar.bz2 https://download-installer.cdn.mozilla.net/pub/firefox/releases/129.0/linux-x86_64/en-US/firefox-129.0.tar.bz2 tar -C /opt -xjf /tmp/firefox.tar.bz2 rm /tmp/firefox.tar.bz2 sudo ln -fs /opt/firefox/firefox /usr/bin/firefox @@ -272,7 +272,7 @@ jobs: path: cypress/screenshots cache-result: name: Write result cache - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 needs: [preparation, end-to-end, cross-browser-smoke, determine-if-required] timeout-minutes: 5 steps: diff --git a/.github/workflows/create-release-branch.yml b/.github/workflows/create-release-branch.yml index 269b378e54..287cd7187f 100644 --- a/.github/workflows/create-release-branch.yml +++ b/.github/workflows/create-release-branch.yml @@ -17,7 +17,7 @@ env: jobs: create_release: name: Create release branch - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 timeout-minutes: 30 steps: - name: Check branch diff --git a/.github/workflows/general.yml b/.github/workflows/general.yml index 5374f4b906..c73f83430f 100644 --- a/.github/workflows/general.yml +++ b/.github/workflows/general.yml @@ -5,7 +5,7 @@ on: pull_request jobs: quality: name: Code Quality - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 timeout-minutes: 5 steps: - name: Check out code diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 55141a9056..0f3826811a 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -3,22 +3,22 @@ name: Go Code on: pull_request: paths: - - ".github/workflows/go.yml" - - "*.go" - - ".revive.toml" - - "cmd/**" - - "go.mod" - - "go.sum" - - "Makefile" - - "pkg/**" - - "!pkg/webui/**" - - "pkg/webui/**.go" - - "tools/**" + - '.github/workflows/go.yml' + - '*.go' + - '.revive.toml' + - 'cmd/**' + - 'go.mod' + - 'go.sum' + - 'Makefile' + - 'pkg/**' + - '!pkg/webui/**' + - 'pkg/webui/**.go' + - 'tools/**' jobs: quality: name: Code Quality - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 timeout-minutes: 15 steps: - name: Check out code @@ -42,13 +42,13 @@ jobs: - name: Lint code uses: golangci/golangci-lint-action@v6 with: - version: v1.57.2 + version: v1.62.2 only-new-issues: true args: --timeout=10m ./pkg/... ./cmd/... test: name: Tests - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 permissions: contents: read id-token: write @@ -56,7 +56,7 @@ jobs: postgres: image: postgres ports: - - "5432/tcp" + - '5432/tcp' env: POSTGRES_USER: root POSTGRES_PASSWORD: root @@ -64,7 +64,7 @@ jobs: redis: image: redis:7 ports: - - "6379/tcp" + - '6379/tcp' timeout-minutes: 15 steps: - name: Create ttn_lorawan_is_test DB @@ -85,11 +85,11 @@ jobs: if: "${{ env.AWS_REGION != '' }}" uses: aws-actions/configure-aws-credentials@v4 with: - aws-region: "${{ secrets.AWS_REGION }}" - role-to-assume: "arn:aws:iam::${{ secrets.AWS_USER_ID }}:role/${{ secrets.AWS_ROLE_NAME }}" - role-session-name: "${{ secrets.AWS_ROLE_NAME }}-${{ github.job }}-${{ github.run_id }}" + aws-region: '${{ secrets.AWS_REGION }}' + role-to-assume: 'arn:aws:iam::${{ secrets.AWS_USER_ID }}:role/${{ secrets.AWS_ROLE_NAME }}' + role-session-name: '${{ secrets.AWS_ROLE_NAME }}-${{ github.job }}-${{ github.run_id }}' env: - AWS_REGION: "${{ secrets.AWS_REGION }}" + AWS_REGION: '${{ secrets.AWS_REGION }}' - name: Check out code uses: actions/checkout@v4 - name: Install Go and Dependencies @@ -101,10 +101,10 @@ jobs: - name: Test code env: SQL_DB_ADDRESS: localhost:${{ job.services.postgres.ports['5432'] }} - SQL_DB_AUTH: "root:root" + SQL_DB_AUTH: 'root:root' REDIS_ADDRESS: localhost:${{ job.services.redis.ports['6379'] }} - TEST_REDIS: "1" - TEST_SLOWDOWN: "8" + TEST_REDIS: '1' + TEST_SLOWDOWN: '8' GCP_CREDENTIALS: ${{ secrets.GCP_TEST_CREDENTIALS }} TEST_BUCKET: ttn-lorawan-test run: tools/bin/mage go:test diff --git a/.github/workflows/issue-triage.yml b/.github/workflows/issue-triage.yml index b93e51d26f..221124375f 100644 --- a/.github/workflows/issue-triage.yml +++ b/.github/workflows/issue-triage.yml @@ -6,7 +6,7 @@ on: jobs: triage: name: Triage New Issues - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 timeout-minutes: 5 steps: - name: Add "needs/triage" label diff --git a/.github/workflows/js.yml b/.github/workflows/js.yml index 7f4b0f809a..8e2dcf6c1e 100644 --- a/.github/workflows/js.yml +++ b/.github/workflows/js.yml @@ -18,7 +18,7 @@ on: jobs: quality: name: Code Quality - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 timeout-minutes: 15 steps: - name: Check out code @@ -46,7 +46,7 @@ jobs: test: name: Tests - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 timeout-minutes: 15 steps: - name: Check out code diff --git a/.github/workflows/mergeability.yml b/.github/workflows/mergeability.yml index d629e203f1..b7e92f83c4 100644 --- a/.github/workflows/mergeability.yml +++ b/.github/workflows/mergeability.yml @@ -11,7 +11,7 @@ jobs: check_mergeability: name: Check Mergeability if: ${{ github.repository == 'TheThingsNetwork/lorawan-stack' }} - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 env: MERGE_CONFLICTS_IGNORE: | CHANGELOG.md diff --git a/.github/workflows/pr-labels.yml b/.github/workflows/pr-labels.yml index d4231a1ce5..f4fda5ce65 100644 --- a/.github/workflows/pr-labels.yml +++ b/.github/workflows/pr-labels.yml @@ -3,7 +3,7 @@ on: pull_request jobs: triage: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 timeout-minutes: 5 steps: - uses: actions/labeler@v5 diff --git a/.github/workflows/proto.yml b/.github/workflows/proto.yml index 967fc26d6e..d6ed483b5e 100644 --- a/.github/workflows/proto.yml +++ b/.github/workflows/proto.yml @@ -10,7 +10,7 @@ on: jobs: protos: name: Generate protos - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 timeout-minutes: 5 steps: - name: Check out code diff --git a/.github/workflows/release-rc.yml b/.github/workflows/release-rc.yml index 81d6eac155..ff989c87e6 100644 --- a/.github/workflows/release-rc.yml +++ b/.github/workflows/release-rc.yml @@ -13,7 +13,7 @@ env: jobs: release: name: Release-candidate release - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 timeout-minutes: 30 steps: - name: Check branch diff --git a/.github/workflows/release-snapshot.yml b/.github/workflows/release-snapshot.yml index 50cca37a91..67c8daa3a2 100644 --- a/.github/workflows/release-snapshot.yml +++ b/.github/workflows/release-snapshot.yml @@ -12,7 +12,7 @@ env: jobs: release: name: Snapshot release - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 timeout-minutes: 30 steps: - name: Check out code diff --git a/.github/workflows/release-tag.yml b/.github/workflows/release-tag.yml index 22ed36caf9..faee48f60b 100644 --- a/.github/workflows/release-tag.yml +++ b/.github/workflows/release-tag.yml @@ -8,7 +8,7 @@ env: jobs: release: name: Tag release - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 permissions: contents: write id-token: write diff --git a/.golangci.yml b/.golangci.yml index 91ec68d207..cbd0fc46f8 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -17,8 +17,8 @@ linters: # Enabled manually: - asciicheck # Finds non-ASCII identifiers. - bodyclose # Finds HTTP response bodies that are not closed. + - copyloopvar # Finds for loop variables that are overwritten. - errorlint # Finds problems with error wrapping. - - exportloopref # Finds pointers to range variables. - goconst # Finds repeated strings that could be replaced by a const. - gocyclo # Finds funcs with high cyclomatic complexity. - godot # Finds comments that do not end with a period. diff --git a/CHANGELOG.md b/CHANGELOG.md index 826401117d..e2662cf7bf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,8 +19,30 @@ For details about compatibility between different releases, see the **Commitment ### Fixed +- Enforce default page limit on AS and NS List RPCs if a value is not provided in the request. +- Swapped field order in `RelayNotifyNewEndDeviceReq` MAC command. + ### Security +## [3.33.0] - unreleased + +### Added + +- Support user email notification preferences. + - This requires an Identity Server database migration (`ttn-lw-stack is-db migrate`). +- Support for managing MAC settings profiles. + - This feature is experimental and subject to change. +- Support pausing webhook in the edit webhook view in console +- Add more specific rights for user operations. + - The rights added were for listing and creating rights. + - Admin validation associated with these operations remains. +- Add purge rights for application, organizations, Oauth clients, gateways and user operations. + +### Fixed + +- Fix OAuth client authorizations delete request in the Console. +- Consecutive disconnect/reconnect messages in the Live Data view in the Console. + ## [3.32.3] - 2024-12-04 ## [3.32.2] - 2024-11-18 @@ -2898,7 +2920,8 @@ For details about compatibility between different releases, see the **Commitment NOTE: These links should respect backports. See https://github.com/TheThingsNetwork/lorawan-stack/pull/1444/files#r333379706. --> -[unreleased]: https://github.com/TheThingsNetwork/lorawan-stack/compare/v3.32.3...v3.32 +[unreleased]: https://github.com/TheThingsNetwork/lorawan-stack/compare/v3.33.0...v3.33 +[3.33.0]: https://github.com/TheThingsNetwork/lorawan-stack/compare/v3.32.2...v3.33.0 [3.32.3]: https://github.com/TheThingsNetwork/lorawan-stack/compare/v3.32.2...v3.32.3 [3.32.2]: https://github.com/TheThingsNetwork/lorawan-stack/compare/v3.32.1...v3.32.2 [3.32.1]: https://github.com/TheThingsNetwork/lorawan-stack/compare/v3.32.0...v3.32.1 diff --git a/Dockerfile b/Dockerfile index 0540e8cc13..3687cf7c90 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,7 +12,7 @@ RUN rm -rf /srv/ttn-lorawan/lorawan-frequency-plans/.git COPY data/lorawan-webhook-templates /srv/ttn-lorawan/lorawan-webhook-templates RUN rm -rf /srv/ttn-lorawan/lorawan-webhook-templates/.git -FROM alpine:3.19 +FROM alpine:3.21 RUN addgroup -g 886 thethings && adduser -u 886 -S -G thethings thethings diff --git a/api/buf.yaml b/api/buf.yaml index 18f76aea15..25115565cf 100644 --- a/api/buf.yaml +++ b/api/buf.yaml @@ -31,6 +31,7 @@ lint: - ttn/lorawan/v3/messages.proto - ttn/lorawan/v3/metadata.proto - ttn/lorawan/v3/rights.proto + - ttn/lorawan/v3/notification_service.proto ENUM_VALUE_UPPER_SNAKE_CASE: - ttn/lorawan/v3/rights.proto ENUM_ZERO_VALUE_SUFFIX: @@ -100,6 +101,7 @@ lint: - ttn/lorawan/v3/identityserver.proto - ttn/lorawan/v3/joinserver.proto - ttn/lorawan/v3/networkserver.proto + - ttn/lorawan/v3/networkserver_mac_settings_profile.proto - ttn/lorawan/v3/notification_service.proto - ttn/lorawan/v3/oauth_services.proto - ttn/lorawan/v3/organization_services.proto @@ -126,6 +128,7 @@ lint: - ttn/lorawan/v3/identityserver.proto - ttn/lorawan/v3/joinserver.proto - ttn/lorawan/v3/networkserver.proto + - ttn/lorawan/v3/networkserver_mac_settings_profile.proto - ttn/lorawan/v3/notification_service.proto - ttn/lorawan/v3/oauth_services.proto - ttn/lorawan/v3/organization_services.proto @@ -153,6 +156,7 @@ lint: - ttn/lorawan/v3/identityserver.proto - ttn/lorawan/v3/joinserver.proto - ttn/lorawan/v3/networkserver.proto + - ttn/lorawan/v3/networkserver_mac_settings_profile.proto - ttn/lorawan/v3/oauth_services.proto - ttn/lorawan/v3/organization_services.proto - ttn/lorawan/v3/packetbrokeragent.proto diff --git a/api/ttn/lorawan/v3/api.md b/api/ttn/lorawan/v3/api.md index 6c096d3d5a..196bc6e43a 100644 --- a/api/ttn/lorawan/v3/api.md +++ b/api/ttn/lorawan/v3/api.md @@ -267,6 +267,7 @@ - [Message `MACParameters`](#ttn.lorawan.v3.MACParameters) - [Message `MACParameters.Channel`](#ttn.lorawan.v3.MACParameters.Channel) - [Message `MACSettings`](#ttn.lorawan.v3.MACSettings) + - [Message `MACSettingsProfile`](#ttn.lorawan.v3.MACSettingsProfile) - [Message `MACState`](#ttn.lorawan.v3.MACState) - [Message `MACState.DataRateRange`](#ttn.lorawan.v3.MACState.DataRateRange) - [Message `MACState.DataRateRanges`](#ttn.lorawan.v3.MACState.DataRateRanges) @@ -417,6 +418,7 @@ - [Message `GatewayIdentifiers`](#ttn.lorawan.v3.GatewayIdentifiers) - [Message `GatewayIdentifiersList`](#ttn.lorawan.v3.GatewayIdentifiersList) - [Message `LoRaAllianceProfileIdentifiers`](#ttn.lorawan.v3.LoRaAllianceProfileIdentifiers) + - [Message `MACSettingsProfileIdentifiers`](#ttn.lorawan.v3.MACSettingsProfileIdentifiers) - [Message `NetworkIdentifiers`](#ttn.lorawan.v3.NetworkIdentifiers) - [Message `OrganizationIdentifiers`](#ttn.lorawan.v3.OrganizationIdentifiers) - [Message `OrganizationOrUserIdentifiers`](#ttn.lorawan.v3.OrganizationOrUserIdentifiers) @@ -637,6 +639,18 @@ - [Service `Ns`](#ttn.lorawan.v3.Ns) - [Service `NsEndDeviceBatchRegistry`](#ttn.lorawan.v3.NsEndDeviceBatchRegistry) - [Service `NsEndDeviceRegistry`](#ttn.lorawan.v3.NsEndDeviceRegistry) +- [File `ttn/lorawan/v3/networkserver_mac_settings_profile.proto`](#ttn/lorawan/v3/networkserver_mac_settings_profile.proto) + - [Message `CreateMACSettingsProfileRequest`](#ttn.lorawan.v3.CreateMACSettingsProfileRequest) + - [Message `CreateMACSettingsProfileResponse`](#ttn.lorawan.v3.CreateMACSettingsProfileResponse) + - [Message `DeleteMACSettingsProfileRequest`](#ttn.lorawan.v3.DeleteMACSettingsProfileRequest) + - [Message `DeleteMACSettingsProfileResponse`](#ttn.lorawan.v3.DeleteMACSettingsProfileResponse) + - [Message `GetMACSettingsProfileRequest`](#ttn.lorawan.v3.GetMACSettingsProfileRequest) + - [Message `GetMACSettingsProfileResponse`](#ttn.lorawan.v3.GetMACSettingsProfileResponse) + - [Message `ListMACSettingsProfilesRequest`](#ttn.lorawan.v3.ListMACSettingsProfilesRequest) + - [Message `ListMACSettingsProfilesResponse`](#ttn.lorawan.v3.ListMACSettingsProfilesResponse) + - [Message `UpdateMACSettingsProfileRequest`](#ttn.lorawan.v3.UpdateMACSettingsProfileRequest) + - [Message `UpdateMACSettingsProfileResponse`](#ttn.lorawan.v3.UpdateMACSettingsProfileResponse) + - [Service `NsMACSettingsProfileRegistry`](#ttn.lorawan.v3.NsMACSettingsProfileRegistry) - [File `ttn/lorawan/v3/networkserver_relay.proto`](#ttn/lorawan/v3/networkserver_relay.proto) - [Message `CreateRelayRequest`](#ttn.lorawan.v3.CreateRelayRequest) - [Message `CreateRelayResponse`](#ttn.lorawan.v3.CreateRelayResponse) @@ -667,6 +681,7 @@ - [Message `UpdateNotificationStatusRequest`](#ttn.lorawan.v3.UpdateNotificationStatusRequest) - [Enum `NotificationReceiver`](#ttn.lorawan.v3.NotificationReceiver) - [Enum `NotificationStatus`](#ttn.lorawan.v3.NotificationStatus) + - [Enum `NotificationType`](#ttn.lorawan.v3.NotificationType) - [Service `NotificationService`](#ttn.lorawan.v3.NotificationService) - [File `ttn/lorawan/v3/oauth.proto`](#ttn/lorawan/v3/oauth.proto) - [Message `ListOAuthAccessTokensRequest`](#ttn.lorawan.v3.ListOAuthAccessTokensRequest) @@ -798,6 +813,7 @@ - [Message `DeleteInvitationRequest`](#ttn.lorawan.v3.DeleteInvitationRequest) - [Message `DeleteUserAPIKeyRequest`](#ttn.lorawan.v3.DeleteUserAPIKeyRequest) - [Message `DeleteUserBookmarkRequest`](#ttn.lorawan.v3.DeleteUserBookmarkRequest) + - [Message `EmailNotificationPreferences`](#ttn.lorawan.v3.EmailNotificationPreferences) - [Message `GetUserAPIKeyRequest`](#ttn.lorawan.v3.GetUserAPIKeyRequest) - [Message `GetUserRequest`](#ttn.lorawan.v3.GetUserRequest) - [Message `Invitation`](#ttn.lorawan.v3.Invitation) @@ -819,12 +835,14 @@ - [Message `UserConsolePreferences`](#ttn.lorawan.v3.UserConsolePreferences) - [Message `UserConsolePreferences.DashboardLayouts`](#ttn.lorawan.v3.UserConsolePreferences.DashboardLayouts) - [Message `UserConsolePreferences.SortBy`](#ttn.lorawan.v3.UserConsolePreferences.SortBy) + - [Message `UserConsolePreferences.Tutorials`](#ttn.lorawan.v3.UserConsolePreferences.Tutorials) - [Message `UserSession`](#ttn.lorawan.v3.UserSession) - [Message `UserSessionIdentifiers`](#ttn.lorawan.v3.UserSessionIdentifiers) - [Message `UserSessions`](#ttn.lorawan.v3.UserSessions) - [Message `Users`](#ttn.lorawan.v3.Users) - [Enum `ConsoleTheme`](#ttn.lorawan.v3.ConsoleTheme) - [Enum `DashboardLayout`](#ttn.lorawan.v3.DashboardLayout) + - [Enum `Tutorial`](#ttn.lorawan.v3.Tutorial) - [File `ttn/lorawan/v3/user_services.proto`](#ttn/lorawan/v3/user_services.proto) - [Service `UserAccess`](#ttn.lorawan.v3.UserAccess) - [Service `UserBookmarkRegistry`](#ttn.lorawan.v3.UserBookmarkRegistry) @@ -4334,6 +4352,20 @@ This is used internally by the Network Server. | ----- | ----------- | | `factory_preset_frequencies` |

`repeated.max_items`: `96`

| +### Message `MACSettingsProfile` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `ids` | [`MACSettingsProfileIdentifiers`](#ttn.lorawan.v3.MACSettingsProfileIdentifiers) | | Profile identifiers. | +| `mac_settings` | [`MACSettings`](#ttn.lorawan.v3.MACSettings) | | MAC settings. | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `ids` |

`message.required`: `true`

| +| `mac_settings` |

`message.required`: `true`

| + ### Message `MACState` MACState represents the state of MAC layer of the device. @@ -6342,6 +6374,20 @@ EntityIdentifiers contains one of the possible entity identifiers. | `vendor_id` | [`uint32`](#uint32) | | VendorID managed by the LoRa Alliance, as defined in TR005. | | `vendor_profile_id` | [`uint32`](#uint32) | | ID of the LoRaWAN end device profile assigned by the vendor. | +### Message `MACSettingsProfileIdentifiers` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `application_ids` | [`ApplicationIdentifiers`](#ttn.lorawan.v3.ApplicationIdentifiers) | | Application IDs. | +| `profile_id` | [`string`](#string) | | Profile ID. | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `application_ids` |

`message.required`: `true`

| +| `profile_id` |

`string.max_len`: `36`

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

| + ### Message `NetworkIdentifiers` Identifies a Network Server. @@ -9261,6 +9307,145 @@ The NsEndDeviceRegistry service allows clients to manage their end devices on th | `ResetFactoryDefaults` | `PATCH` | `/api/v3/ns/applications/{end_device_ids.application_ids.application_id}/devices/{end_device_ids.device_id}` | `*` | | `Delete` | `DELETE` | `/api/v3/ns/applications/{application_ids.application_id}/devices/{device_id}` | | +## File `ttn/lorawan/v3/networkserver_mac_settings_profile.proto` + +### Message `CreateMACSettingsProfileRequest` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `mac_settings_profile` | [`MACSettingsProfile`](#ttn.lorawan.v3.MACSettingsProfile) | | The MAC settings profile to create. | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `mac_settings_profile` |

`message.required`: `true`

| + +### Message `CreateMACSettingsProfileResponse` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `mac_settings_profile` | [`MACSettingsProfile`](#ttn.lorawan.v3.MACSettingsProfile) | | The MAC settings profile. | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `mac_settings_profile` |

`message.required`: `true`

| + +### Message `DeleteMACSettingsProfileRequest` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `mac_settings_profile_ids` | [`MACSettingsProfileIdentifiers`](#ttn.lorawan.v3.MACSettingsProfileIdentifiers) | | The identifiers of the MAC settings profile. | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `mac_settings_profile_ids` |

`message.required`: `true`

| + +### Message `DeleteMACSettingsProfileResponse` + +### Message `GetMACSettingsProfileRequest` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `mac_settings_profile_ids` | [`MACSettingsProfileIdentifiers`](#ttn.lorawan.v3.MACSettingsProfileIdentifiers) | | The identifiers of the MAC settings profile. | +| `field_mask` | [`google.protobuf.FieldMask`](#google.protobuf.FieldMask) | | The names of the fields that should be returned. | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `mac_settings_profile_ids` |

`message.required`: `true`

| + +### Message `GetMACSettingsProfileResponse` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `mac_settings_profile` | [`MACSettingsProfile`](#ttn.lorawan.v3.MACSettingsProfile) | | The MAC settings profile. | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `mac_settings_profile` |

`message.required`: `true`

| + +### Message `ListMACSettingsProfilesRequest` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `application_ids` | [`ApplicationIdentifiers`](#ttn.lorawan.v3.ApplicationIdentifiers) | | The identifiers of the application. | +| `field_mask` | [`google.protobuf.FieldMask`](#google.protobuf.FieldMask) | | The names of the fields that should be returned. | +| `order` | [`string`](#string) | | Order the results by this field path (must be present in the field mask). Default ordering is by ID. Prepend with a minus (-) to reverse the order. | +| `limit` | [`uint32`](#uint32) | | Limit the number of results per page. | +| `page` | [`uint32`](#uint32) | | Page number for pagination. 0 is interpreted as 1. | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `application_ids` |

`message.required`: `true`

| +| `order` |

`string.in`: `[ ids.profile_id -ids.profile_id]`

| +| `limit` |

`uint32.lte`: `1000`

| + +### Message `ListMACSettingsProfilesResponse` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `mac_settings_profiles` | [`MACSettingsProfile`](#ttn.lorawan.v3.MACSettingsProfile) | repeated | The MAC settings profiles. | + +### Message `UpdateMACSettingsProfileRequest` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `mac_settings_profile_ids` | [`MACSettingsProfileIdentifiers`](#ttn.lorawan.v3.MACSettingsProfileIdentifiers) | | The identifiers of the MAC settings profile. | +| `mac_settings_profile` | [`MACSettingsProfile`](#ttn.lorawan.v3.MACSettingsProfile) | | The MAC settings profile to update. | +| `field_mask` | [`google.protobuf.FieldMask`](#google.protobuf.FieldMask) | | The names of the fields that should be updated. | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `mac_settings_profile_ids` |

`message.required`: `true`

| +| `mac_settings_profile` |

`message.required`: `true`

| + +### Message `UpdateMACSettingsProfileResponse` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `mac_settings_profile` | [`MACSettingsProfile`](#ttn.lorawan.v3.MACSettingsProfile) | | The MAC settings profile. | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `mac_settings_profile` |

`message.required`: `true`

| + +### Service `NsMACSettingsProfileRegistry` + +The NsMACSettingsProfileRegistry service allows clients to manage MAC settings profiles on the Network Server. +EXPERIMENTAL: This service is subject to change. + +| Method Name | Request Type | Response Type | Description | +| ----------- | ------------ | ------------- | ------------| +| `Create` | [`CreateMACSettingsProfileRequest`](#ttn.lorawan.v3.CreateMACSettingsProfileRequest) | [`CreateMACSettingsProfileResponse`](#ttn.lorawan.v3.CreateMACSettingsProfileResponse) | Create creates a new MAC settings profile. | +| `Get` | [`GetMACSettingsProfileRequest`](#ttn.lorawan.v3.GetMACSettingsProfileRequest) | [`GetMACSettingsProfileResponse`](#ttn.lorawan.v3.GetMACSettingsProfileResponse) | Get returns the MAC settings profile that matches the given identifiers. | +| `Update` | [`UpdateMACSettingsProfileRequest`](#ttn.lorawan.v3.UpdateMACSettingsProfileRequest) | [`UpdateMACSettingsProfileResponse`](#ttn.lorawan.v3.UpdateMACSettingsProfileResponse) | Update updates the MAC settings profile that matches the given identifiers. | +| `Delete` | [`DeleteMACSettingsProfileRequest`](#ttn.lorawan.v3.DeleteMACSettingsProfileRequest) | [`DeleteMACSettingsProfileResponse`](#ttn.lorawan.v3.DeleteMACSettingsProfileResponse) | Delete deletes the MAC settings profile that matches the given identifiers. | +| `List` | [`ListMACSettingsProfilesRequest`](#ttn.lorawan.v3.ListMACSettingsProfilesRequest) | [`ListMACSettingsProfilesResponse`](#ttn.lorawan.v3.ListMACSettingsProfilesResponse) | List lists the MAC settings profiles. | + +#### HTTP bindings + +| Method Name | Method | Pattern | Body | +| ----------- | ------ | ------- | ---- | +| `Create` | `POST` | `/api/v3/ns/applications/{mac_settings_profile.ids.application_ids.application_id}/mac_settings_profiles` | `*` | +| `Get` | `GET` | `/api/v3/ns/applications/{mac_settings_profile_ids.application_ids.application_id}/mac_settings_profiles/{mac_settings_profile_ids.profile_id}` | | +| `Update` | `PUT` | `/api/v3/ns/applications/{mac_settings_profile_ids.application_ids.application_id}/mac_settings_profiles/{mac_settings_profile_ids.profile_id}` | `*` | +| `Delete` | `DELETE` | `/api/v3/ns/applications/{mac_settings_profile_ids.application_ids.application_id}/mac_settings_profiles/{mac_settings_profile_ids.profile_id}` | | +| `List` | `GET` | `/api/v3/ns/applications/{application_ids.application_id}/mac_settings_profiles` | | + ## File `ttn/lorawan/v3/networkserver_relay.proto` ### Message `CreateRelayRequest` @@ -9486,7 +9671,7 @@ The NsRelayConfigurationService provides configuration management capabilities f | Field | Validations | | ----- | ----------- | | `entity_ids` |

`message.required`: `true`

| -| `notification_type` |

`string.min_len`: `1`

`string.max_len`: `100`

| +| `notification_type` |

`string.in`: `[unknown api_key_created api_key_changed client_requested collaborator_changed entity_state_changed invitation login_token password_changed temporary_password user_requested validate]`

| | `receivers` |

`repeated.min_items`: `1`

`repeated.unique`: `true`

`repeated.items.enum.defined_only`: `true`

| ### Message `CreateNotificationResponse` @@ -9539,7 +9724,7 @@ The NsRelayConfigurationService provides configuration management capabilities f | `id` | [`string`](#string) | | The immutable ID of the notification. Generated by the server. | | `created_at` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | The time when the notification was triggered. | | `entity_ids` | [`EntityIdentifiers`](#ttn.lorawan.v3.EntityIdentifiers) | | The entity this notification is about. | -| `notification_type` | [`string`](#string) | | The type of this notification. | +| `notification_type` | [`string`](#string) | | The type of this notification. TODO: Replace with type NotificationType in v4 https://github.com/TheThingsNetwork/lorawan-stack/issues/7384. | | `data` | [`google.protobuf.Any`](#google.protobuf.Any) | | The data related to the notification. | | `sender_ids` | [`UserIdentifiers`](#ttn.lorawan.v3.UserIdentifiers) | | If the notification was triggered by a user action, this contains the identifiers of the user that triggered the notification. | | `receivers` | [`NotificationReceiver`](#ttn.lorawan.v3.NotificationReceiver) | repeated | Relation of the notification receiver to the entity. | @@ -9547,6 +9732,12 @@ The NsRelayConfigurationService provides configuration management capabilities f | `status` | [`NotificationStatus`](#ttn.lorawan.v3.NotificationStatus) | | The status of the notification. | | `status_updated_at` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | The time when the notification status was updated. | +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `notification_type` |

`string.in`: `[unknown api_key_created api_key_changed client_requested collaborator_changed entity_state_changed invitation login_token password_changed temporary_password user_requested validate]`

| + ### Message `UpdateNotificationStatusRequest` | Field | Type | Label | Description | @@ -9580,6 +9771,23 @@ The NsRelayConfigurationService provides configuration management capabilities f | `NOTIFICATION_STATUS_SEEN` | 1 | | | `NOTIFICATION_STATUS_ARCHIVED` | 2 | | +### Enum `NotificationType` + +| Name | Number | Description | +| ---- | ------ | ----------- | +| `UNKNOWN` | 0 | | +| `API_KEY_CREATED` | 1 | | +| `API_KEY_CHANGED` | 2 | | +| `CLIENT_REQUESTED` | 3 | | +| `COLLABORATOR_CHANGED` | 4 | | +| `ENTITY_STATE_CHANGED` | 5 | | +| `INVITATION` | 6 | | +| `LOGIN_TOKEN` | 7 | | +| `PASSWORD_CHANGED` | 8 | | +| `TEMPORARY_PASSWORD` | 9 | | +| `USER_REQUESTED` | 10 | | +| `VALIDATE` | 11 | | + ### Service `NotificationService` The NotificationService is used to send notifications. @@ -10766,8 +10974,11 @@ Right is the enum that defines all the different rights to do something in the n | `right_invalid` | 0 | | | `RIGHT_USER_INFO` | 1 | The right to view user information. | | `RIGHT_USER_SETTINGS_BASIC` | 2 | The right to edit basic user settings. | +| `RIGHT_USER_LIST` | 87 | The right to list users accounts. | +| `RIGHT_USER_CREATE` | 88 | The right to create an user account. | | `RIGHT_USER_SETTINGS_API_KEYS` | 3 | The right to view and edit user API keys. | | `RIGHT_USER_DELETE` | 4 | The right to delete user account. | +| `RIGHT_USER_PURGE` | 66 | The right to delete user account. | | `RIGHT_USER_AUTHORIZED_CLIENTS` | 5 | The right to view and edit authorized OAuth clients of the user. | | `RIGHT_USER_APPLICATIONS_LIST` | 6 | The right to list applications the user is a collaborator of. | | `RIGHT_USER_APPLICATIONS_CREATE` | 7 | The right to create an application under the user account. | @@ -10785,6 +10996,7 @@ Right is the enum that defines all the different rights to do something in the n | `RIGHT_APPLICATION_SETTINGS_COLLABORATORS` | 18 | The right to view and edit application collaborators. | | `RIGHT_APPLICATION_SETTINGS_PACKAGES` | 56 | The right to view and edit application packages and associations. | | `RIGHT_APPLICATION_DELETE` | 19 | The right to delete application. | +| `RIGHT_APPLICATION_PURGE` | 64 | The right to purge application. | | `RIGHT_APPLICATION_DEVICES_READ` | 20 | The right to view devices in application. | | `RIGHT_APPLICATION_DEVICES_WRITE` | 21 | The right to create devices in application. | | `RIGHT_APPLICATION_DEVICES_READ_KEYS` | 22 | The right to view device keys in application. Note that keys may not be stored in a way that supports viewing them. | @@ -10799,11 +11011,13 @@ Right is the enum that defines all the different rights to do something in the n | `RIGHT_CLIENT_SETTINGS_BASIC` | 61 | The right to edit basic client settings. | | `RIGHT_CLIENT_SETTINGS_COLLABORATORS` | 62 | The right to view and edit client collaborators. | | `RIGHT_CLIENT_DELETE` | 63 | The right to delete a client. | +| `RIGHT_CLIENT_PURGE` | 68 | The right to purge a client. | | `RIGHT_GATEWAY_INFO` | 30 | The right to view gateway information. | | `RIGHT_GATEWAY_SETTINGS_BASIC` | 31 | The right to edit basic gateway settings. | | `RIGHT_GATEWAY_SETTINGS_API_KEYS` | 32 | The right to view and edit gateway API keys. | | `RIGHT_GATEWAY_SETTINGS_COLLABORATORS` | 33 | The right to view and edit gateway collaborators. | | `RIGHT_GATEWAY_DELETE` | 34 | The right to delete gateway. | +| `RIGHT_GATEWAY_PURGE` | 67 | The right to purge gateway. | | `RIGHT_GATEWAY_TRAFFIC_READ` | 35 | The right to read gateway traffic. | | `RIGHT_GATEWAY_TRAFFIC_DOWN_WRITE` | 36 | The right to write downlink gateway traffic. | | `RIGHT_GATEWAY_LINK` | 37 | The right to link as Gateway to a Gateway Server for traffic exchange, i.e. write uplink and read downlink (API keys only) This right is typically only given to a gateway. This right implies RIGHT_GATEWAY_INFO. | @@ -10817,6 +11031,7 @@ Right is the enum that defines all the different rights to do something in the n | `RIGHT_ORGANIZATION_SETTINGS_API_KEYS` | 43 | The right to view and edit organization API keys. | | `RIGHT_ORGANIZATION_SETTINGS_MEMBERS` | 44 | The right to view and edit organization members. | | `RIGHT_ORGANIZATION_DELETE` | 45 | The right to delete organization. | +| `RIGHT_ORGANIZATION_PURGE` | 65 | The right to purge organization. | | `RIGHT_ORGANIZATION_APPLICATIONS_LIST` | 46 | The right to list the applications the organization is a collaborator of. | | `RIGHT_ORGANIZATION_APPLICATIONS_CREATE` | 47 | The right to create an application under the organization. | | `RIGHT_ORGANIZATION_GATEWAYS_LIST` | 48 | The right to list the gateways the organization is a collaborator of. | @@ -11346,6 +11561,20 @@ Secret contains a secret value. It also contains the ID of the Encryption key us | `user_ids` |

`message.required`: `true`

| | `entity_ids` |

`message.required`: `true`

| +### Message `EmailNotificationPreferences` + +EmailNotificationPreferences is the message that defines the types of notifications for which the user wants to receive an email. + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `types` | [`NotificationType`](#ttn.lorawan.v3.NotificationType) | repeated | | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `types` |

`repeated.unique`: `true`

`repeated.items.enum.defined_only`: `true`

| + ### Message `GetUserAPIKeyRequest` | Field | Type | Label | Description | @@ -11581,6 +11810,8 @@ User is the message that defines a user on the network. | `temporary_password_expires_at` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | | | `profile_picture` | [`Picture`](#ttn.lorawan.v3.Picture) | | A profile picture for the user. This information is public and can be seen by any authenticated user in the network. | | `console_preferences` | [`UserConsolePreferences`](#ttn.lorawan.v3.UserConsolePreferences) | | Console preferences contains the user's preferences regarding the behavior of the Console. | +| `email_notification_preferences` | [`EmailNotificationPreferences`](#ttn.lorawan.v3.EmailNotificationPreferences) | | | +| `universal_rights` | [`Right`](#ttn.lorawan.v3.Right) | repeated | Universal rights are capable of restricting the user's interactions with the API, be it an admin or not. | #### Field Rules @@ -11634,6 +11865,7 @@ UserConsolePreferences is the message that defines the user preferences for the | `console_theme` | [`ConsoleTheme`](#ttn.lorawan.v3.ConsoleTheme) | | | | `dashboard_layouts` | [`UserConsolePreferences.DashboardLayouts`](#ttn.lorawan.v3.UserConsolePreferences.DashboardLayouts) | | | | `sort_by` | [`UserConsolePreferences.SortBy`](#ttn.lorawan.v3.UserConsolePreferences.SortBy) | | | +| `tutorials` | [`UserConsolePreferences.Tutorials`](#ttn.lorawan.v3.UserConsolePreferences.Tutorials) | | | ### Message `UserConsolePreferences.DashboardLayouts` @@ -11674,6 +11906,18 @@ SortBy defines the field to which the Console will sort the display of entities. | `organization` |

`string.in`: `[ organization_id -organization_id name -name created_at -created_at]`

| | `user` |

`string.in`: `[ user_id -user_id name -name primary_email_address -primary_email_address state -state admin -admin created_at -created_at]`

| +### Message `UserConsolePreferences.Tutorials` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `seen` | [`Tutorial`](#ttn.lorawan.v3.Tutorial) | repeated | | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `seen` |

`repeated.unique`: `true`

`repeated.items.enum.defined_only`: `true`

| + ### Message `UserSession` | Field | Type | Label | Description | @@ -11738,6 +11982,15 @@ DashboardLayout is a set of possible layout values to be used in the Console. | `DASHBOARD_LAYOUT_LIST` | 1 | | | `DASHBOARD_LAYOUT_GRID` | 2 | | +### Enum `Tutorial` + +Tutorial is a set of possible tutorials in the Console. + +| Name | Number | Description | +| ---- | ------ | ----------- | +| `TUTORIAL_UNKNOWN` | 0 | | +| `TUTORIAL_LIVE_DATA_SPLIT_VIEW` | 1 | | + ## File `ttn/lorawan/v3/user_services.proto` ### Service `UserAccess` diff --git a/api/ttn/lorawan/v3/api.swagger.json b/api/ttn/lorawan/v3/api.swagger.json index acc89de363..d9eb432aee 100644 --- a/api/ttn/lorawan/v3/api.swagger.json +++ b/api/ttn/lorawan/v3/api.swagger.json @@ -209,6 +209,10 @@ "name": "NsEndDeviceBatchRegistry", "description": "Manage batches of end devices on The Things Stack Network Server." }, + { + "name": "NsMACSettingsProfileRegistry", + "description": "Manage MAC settings profiles on The Things Stack Network Server." + }, { "name": "NsRelayConfigurationService", "description": "Manage configuration of LoRaWAN® relay devices." @@ -6907,7 +6911,7 @@ "parameters": [ { "name": "required.rights", - "description": " - RIGHT_USER_INFO: The right to view user information.\n - RIGHT_USER_SETTINGS_BASIC: The right to edit basic user settings.\n - RIGHT_USER_SETTINGS_API_KEYS: The right to view and edit user API keys.\n - RIGHT_USER_DELETE: The right to delete user account.\n - RIGHT_USER_AUTHORIZED_CLIENTS: The right to view and edit authorized OAuth clients of the user.\n - RIGHT_USER_APPLICATIONS_LIST: The right to list applications the user is a collaborator of.\n - RIGHT_USER_APPLICATIONS_CREATE: The right to create an application under the user account.\n - RIGHT_USER_GATEWAYS_LIST: The right to list gateways the user is a collaborator of.\n - RIGHT_USER_GATEWAYS_CREATE: The right to create a gateway under the account of the user.\n - RIGHT_USER_CLIENTS_LIST: The right to list OAuth clients the user is a collaborator of.\n - RIGHT_USER_CLIENTS_CREATE: The right to create an OAuth client under the account of the user.\n - RIGHT_USER_ORGANIZATIONS_LIST: The right to list organizations the user is a member of.\n - RIGHT_USER_ORGANIZATIONS_CREATE: The right to create an organization under the user account.\n - RIGHT_USER_NOTIFICATIONS_READ: The right to read notifications sent to the user.\n - RIGHT_USER_ALL: The pseudo-right for all (current and future) user rights.\n - RIGHT_APPLICATION_INFO: The right to view application information.\n - RIGHT_APPLICATION_SETTINGS_BASIC: The right to edit basic application settings.\n - RIGHT_APPLICATION_SETTINGS_API_KEYS: The right to view and edit application API keys.\n - RIGHT_APPLICATION_SETTINGS_COLLABORATORS: The right to view and edit application collaborators.\n - RIGHT_APPLICATION_SETTINGS_PACKAGES: The right to view and edit application packages and associations.\n - RIGHT_APPLICATION_DELETE: The right to delete application.\n - RIGHT_APPLICATION_DEVICES_READ: The right to view devices in application.\n - RIGHT_APPLICATION_DEVICES_WRITE: The right to create devices in application.\n - RIGHT_APPLICATION_DEVICES_READ_KEYS: The right to view device keys in application.\nNote that keys may not be stored in a way that supports viewing them.\n - RIGHT_APPLICATION_DEVICES_WRITE_KEYS: The right to edit device keys in application.\n - RIGHT_APPLICATION_TRAFFIC_READ: The right to read application traffic (uplink and downlink).\n - RIGHT_APPLICATION_TRAFFIC_UP_WRITE: The right to write uplink application traffic.\n - RIGHT_APPLICATION_TRAFFIC_DOWN_WRITE: The right to write downlink application traffic.\n - RIGHT_APPLICATION_LINK: The right to link as Application to a Network Server for traffic exchange,\ni.e. read uplink and write downlink (API keys only).\nThis right is typically only given to an Application Server.\nThis right implies RIGHT_APPLICATION_INFO, RIGHT_APPLICATION_TRAFFIC_READ,\nand RIGHT_APPLICATION_TRAFFIC_DOWN_WRITE.\n - RIGHT_APPLICATION_ALL: The pseudo-right for all (current and future) application rights.\n - RIGHT_CLIENT_ALL: The pseudo-right for all (current and future) OAuth client rights.\n - RIGHT_CLIENT_INFO: The right to read client information.\n - RIGHT_CLIENT_SETTINGS_BASIC: The right to edit basic client settings.\n - RIGHT_CLIENT_SETTINGS_COLLABORATORS: The right to view and edit client collaborators.\n - RIGHT_CLIENT_DELETE: The right to delete a client.\n - RIGHT_GATEWAY_INFO: The right to view gateway information.\n - RIGHT_GATEWAY_SETTINGS_BASIC: The right to edit basic gateway settings.\n - RIGHT_GATEWAY_SETTINGS_API_KEYS: The right to view and edit gateway API keys.\n - RIGHT_GATEWAY_SETTINGS_COLLABORATORS: The right to view and edit gateway collaborators.\n - RIGHT_GATEWAY_DELETE: The right to delete gateway.\n - RIGHT_GATEWAY_TRAFFIC_READ: The right to read gateway traffic.\n - RIGHT_GATEWAY_TRAFFIC_DOWN_WRITE: The right to write downlink gateway traffic.\n - RIGHT_GATEWAY_LINK: The right to link as Gateway to a Gateway Server for traffic exchange,\ni.e. write uplink and read downlink (API keys only)\nThis right is typically only given to a gateway.\nThis right implies RIGHT_GATEWAY_INFO.\n - RIGHT_GATEWAY_STATUS_READ: The right to view gateway status.\n - RIGHT_GATEWAY_LOCATION_READ: The right to view view gateway location.\n - RIGHT_GATEWAY_WRITE_SECRETS: The right to store secrets associated with this gateway.\n - RIGHT_GATEWAY_READ_SECRETS: The right to retrieve secrets associated with this gateway.\n - RIGHT_GATEWAY_ALL: The pseudo-right for all (current and future) gateway rights.\n - RIGHT_ORGANIZATION_INFO: The right to view organization information.\n - RIGHT_ORGANIZATION_SETTINGS_BASIC: The right to edit basic organization settings.\n - RIGHT_ORGANIZATION_SETTINGS_API_KEYS: The right to view and edit organization API keys.\n - RIGHT_ORGANIZATION_SETTINGS_MEMBERS: The right to view and edit organization members.\n - RIGHT_ORGANIZATION_DELETE: The right to delete organization.\n - RIGHT_ORGANIZATION_APPLICATIONS_LIST: The right to list the applications the organization is a collaborator of.\n - RIGHT_ORGANIZATION_APPLICATIONS_CREATE: The right to create an application under the organization.\n - RIGHT_ORGANIZATION_GATEWAYS_LIST: The right to list the gateways the organization is a collaborator of.\n - RIGHT_ORGANIZATION_GATEWAYS_CREATE: The right to create a gateway under the organization.\n - RIGHT_ORGANIZATION_CLIENTS_LIST: The right to list the OAuth clients the organization is a collaborator of.\n - RIGHT_ORGANIZATION_CLIENTS_CREATE: The right to create an OAuth client under the organization.\n - RIGHT_ORGANIZATION_ADD_AS_COLLABORATOR: The right to add the organization as a collaborator on an existing entity.\n - RIGHT_ORGANIZATION_ALL: The pseudo-right for all (current and future) organization rights.\n - RIGHT_SEND_INVITES: The right to send invites to new users.\nNote that this is not prefixed with \"USER_\"; it is not a right on the user entity.\n - RIGHT_ALL: The pseudo-right for all (current and future) possible rights.", + "description": " - RIGHT_USER_INFO: The right to view user information.\n - RIGHT_USER_SETTINGS_BASIC: The right to edit basic user settings.\n - RIGHT_USER_LIST: The right to list users accounts.\n - RIGHT_USER_CREATE: The right to create an user account.\n - RIGHT_USER_SETTINGS_API_KEYS: The right to view and edit user API keys.\n - RIGHT_USER_DELETE: The right to delete user account.\n - RIGHT_USER_PURGE: The right to delete user account.\n - RIGHT_USER_AUTHORIZED_CLIENTS: The right to view and edit authorized OAuth clients of the user.\n - RIGHT_USER_APPLICATIONS_LIST: The right to list applications the user is a collaborator of.\n - RIGHT_USER_APPLICATIONS_CREATE: The right to create an application under the user account.\n - RIGHT_USER_GATEWAYS_LIST: The right to list gateways the user is a collaborator of.\n - RIGHT_USER_GATEWAYS_CREATE: The right to create a gateway under the account of the user.\n - RIGHT_USER_CLIENTS_LIST: The right to list OAuth clients the user is a collaborator of.\n - RIGHT_USER_CLIENTS_CREATE: The right to create an OAuth client under the account of the user.\n - RIGHT_USER_ORGANIZATIONS_LIST: The right to list organizations the user is a member of.\n - RIGHT_USER_ORGANIZATIONS_CREATE: The right to create an organization under the user account.\n - RIGHT_USER_NOTIFICATIONS_READ: The right to read notifications sent to the user.\n - RIGHT_USER_ALL: The pseudo-right for all (current and future) user rights.\n - RIGHT_APPLICATION_INFO: The right to view application information.\n - RIGHT_APPLICATION_SETTINGS_BASIC: The right to edit basic application settings.\n - RIGHT_APPLICATION_SETTINGS_API_KEYS: The right to view and edit application API keys.\n - RIGHT_APPLICATION_SETTINGS_COLLABORATORS: The right to view and edit application collaborators.\n - RIGHT_APPLICATION_SETTINGS_PACKAGES: The right to view and edit application packages and associations.\n - RIGHT_APPLICATION_DELETE: The right to delete application.\n - RIGHT_APPLICATION_PURGE: The right to purge application.\n - RIGHT_APPLICATION_DEVICES_READ: The right to view devices in application.\n - RIGHT_APPLICATION_DEVICES_WRITE: The right to create devices in application.\n - RIGHT_APPLICATION_DEVICES_READ_KEYS: The right to view device keys in application.\nNote that keys may not be stored in a way that supports viewing them.\n - RIGHT_APPLICATION_DEVICES_WRITE_KEYS: The right to edit device keys in application.\n - RIGHT_APPLICATION_TRAFFIC_READ: The right to read application traffic (uplink and downlink).\n - RIGHT_APPLICATION_TRAFFIC_UP_WRITE: The right to write uplink application traffic.\n - RIGHT_APPLICATION_TRAFFIC_DOWN_WRITE: The right to write downlink application traffic.\n - RIGHT_APPLICATION_LINK: The right to link as Application to a Network Server for traffic exchange,\ni.e. read uplink and write downlink (API keys only).\nThis right is typically only given to an Application Server.\nThis right implies RIGHT_APPLICATION_INFO, RIGHT_APPLICATION_TRAFFIC_READ,\nand RIGHT_APPLICATION_TRAFFIC_DOWN_WRITE.\n - RIGHT_APPLICATION_ALL: The pseudo-right for all (current and future) application rights.\n - RIGHT_CLIENT_ALL: The pseudo-right for all (current and future) OAuth client rights.\n - RIGHT_CLIENT_INFO: The right to read client information.\n - RIGHT_CLIENT_SETTINGS_BASIC: The right to edit basic client settings.\n - RIGHT_CLIENT_SETTINGS_COLLABORATORS: The right to view and edit client collaborators.\n - RIGHT_CLIENT_DELETE: The right to delete a client.\n - RIGHT_CLIENT_PURGE: The right to purge a client.\n - RIGHT_GATEWAY_INFO: The right to view gateway information.\n - RIGHT_GATEWAY_SETTINGS_BASIC: The right to edit basic gateway settings.\n - RIGHT_GATEWAY_SETTINGS_API_KEYS: The right to view and edit gateway API keys.\n - RIGHT_GATEWAY_SETTINGS_COLLABORATORS: The right to view and edit gateway collaborators.\n - RIGHT_GATEWAY_DELETE: The right to delete gateway.\n - RIGHT_GATEWAY_PURGE: The right to purge gateway.\n - RIGHT_GATEWAY_TRAFFIC_READ: The right to read gateway traffic.\n - RIGHT_GATEWAY_TRAFFIC_DOWN_WRITE: The right to write downlink gateway traffic.\n - RIGHT_GATEWAY_LINK: The right to link as Gateway to a Gateway Server for traffic exchange,\ni.e. write uplink and read downlink (API keys only)\nThis right is typically only given to a gateway.\nThis right implies RIGHT_GATEWAY_INFO.\n - RIGHT_GATEWAY_STATUS_READ: The right to view gateway status.\n - RIGHT_GATEWAY_LOCATION_READ: The right to view view gateway location.\n - RIGHT_GATEWAY_WRITE_SECRETS: The right to store secrets associated with this gateway.\n - RIGHT_GATEWAY_READ_SECRETS: The right to retrieve secrets associated with this gateway.\n - RIGHT_GATEWAY_ALL: The pseudo-right for all (current and future) gateway rights.\n - RIGHT_ORGANIZATION_INFO: The right to view organization information.\n - RIGHT_ORGANIZATION_SETTINGS_BASIC: The right to edit basic organization settings.\n - RIGHT_ORGANIZATION_SETTINGS_API_KEYS: The right to view and edit organization API keys.\n - RIGHT_ORGANIZATION_SETTINGS_MEMBERS: The right to view and edit organization members.\n - RIGHT_ORGANIZATION_DELETE: The right to delete organization.\n - RIGHT_ORGANIZATION_PURGE: The right to purge organization.\n - RIGHT_ORGANIZATION_APPLICATIONS_LIST: The right to list the applications the organization is a collaborator of.\n - RIGHT_ORGANIZATION_APPLICATIONS_CREATE: The right to create an application under the organization.\n - RIGHT_ORGANIZATION_GATEWAYS_LIST: The right to list the gateways the organization is a collaborator of.\n - RIGHT_ORGANIZATION_GATEWAYS_CREATE: The right to create a gateway under the organization.\n - RIGHT_ORGANIZATION_CLIENTS_LIST: The right to list the OAuth clients the organization is a collaborator of.\n - RIGHT_ORGANIZATION_CLIENTS_CREATE: The right to create an OAuth client under the organization.\n - RIGHT_ORGANIZATION_ADD_AS_COLLABORATOR: The right to add the organization as a collaborator on an existing entity.\n - RIGHT_ORGANIZATION_ALL: The pseudo-right for all (current and future) organization rights.\n - RIGHT_SEND_INVITES: The right to send invites to new users.\nNote that this is not prefixed with \"USER_\"; it is not a right on the user entity.\n - RIGHT_ALL: The pseudo-right for all (current and future) possible rights.", "in": "query", "required": false, "type": "array", @@ -6917,8 +6921,11 @@ "right_invalid", "RIGHT_USER_INFO", "RIGHT_USER_SETTINGS_BASIC", + "RIGHT_USER_LIST", + "RIGHT_USER_CREATE", "RIGHT_USER_SETTINGS_API_KEYS", "RIGHT_USER_DELETE", + "RIGHT_USER_PURGE", "RIGHT_USER_AUTHORIZED_CLIENTS", "RIGHT_USER_APPLICATIONS_LIST", "RIGHT_USER_APPLICATIONS_CREATE", @@ -6936,6 +6943,7 @@ "RIGHT_APPLICATION_SETTINGS_COLLABORATORS", "RIGHT_APPLICATION_SETTINGS_PACKAGES", "RIGHT_APPLICATION_DELETE", + "RIGHT_APPLICATION_PURGE", "RIGHT_APPLICATION_DEVICES_READ", "RIGHT_APPLICATION_DEVICES_WRITE", "RIGHT_APPLICATION_DEVICES_READ_KEYS", @@ -6950,11 +6958,13 @@ "RIGHT_CLIENT_SETTINGS_BASIC", "RIGHT_CLIENT_SETTINGS_COLLABORATORS", "RIGHT_CLIENT_DELETE", + "RIGHT_CLIENT_PURGE", "RIGHT_GATEWAY_INFO", "RIGHT_GATEWAY_SETTINGS_BASIC", "RIGHT_GATEWAY_SETTINGS_API_KEYS", "RIGHT_GATEWAY_SETTINGS_COLLABORATORS", "RIGHT_GATEWAY_DELETE", + "RIGHT_GATEWAY_PURGE", "RIGHT_GATEWAY_TRAFFIC_READ", "RIGHT_GATEWAY_TRAFFIC_DOWN_WRITE", "RIGHT_GATEWAY_LINK", @@ -6968,6 +6978,7 @@ "RIGHT_ORGANIZATION_SETTINGS_API_KEYS", "RIGHT_ORGANIZATION_SETTINGS_MEMBERS", "RIGHT_ORGANIZATION_DELETE", + "RIGHT_ORGANIZATION_PURGE", "RIGHT_ORGANIZATION_APPLICATIONS_LIST", "RIGHT_ORGANIZATION_APPLICATIONS_CREATE", "RIGHT_ORGANIZATION_GATEWAYS_LIST", @@ -10217,6 +10228,67 @@ ] } }, + "/ns/applications/{application_ids.application_id}/mac_settings_profiles": { + "get": { + "summary": "List lists the MAC settings profiles.", + "operationId": "NsMACSettingsProfileRegistry_List", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v3ListMACSettingsProfilesResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "application_ids.application_id", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "field_mask", + "description": "The names of the fields that should be returned.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "order", + "description": "Order the results by this field path (must be present in the field mask).\nDefault ordering is by ID. Prepend with a minus (-) to reverse the order.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "limit", + "description": "Limit the number of results per page.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + }, + { + "name": "page", + "description": "Page number for pagination. 0 is interpreted as 1.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + } + ], + "tags": [ + "NsMACSettingsProfileRegistry" + ] + } + }, "/ns/applications/{end_device.ids.application_ids.application_id}/devices": { "post": { "summary": "Set creates or updates the device.", @@ -10926,6 +10998,170 @@ ] } }, + "/ns/applications/{mac_settings_profile.ids.application_ids.application_id}/mac_settings_profiles": { + "post": { + "summary": "Create creates a new MAC settings profile.", + "operationId": "NsMACSettingsProfileRegistry_Create", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v3CreateMACSettingsProfileResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "mac_settings_profile.ids.application_ids.application_id", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/v3NsMACSettingsProfileRegistryCreateBody" + } + } + ], + "tags": [ + "NsMACSettingsProfileRegistry" + ] + } + }, + "/ns/applications/{mac_settings_profile_ids.application_ids.application_id}/mac_settings_profiles/{mac_settings_profile_ids.profile_id}": { + "get": { + "summary": "Get returns the MAC settings profile that matches the given identifiers.", + "operationId": "NsMACSettingsProfileRegistry_Get", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v3GetMACSettingsProfileResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "mac_settings_profile_ids.application_ids.application_id", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "mac_settings_profile_ids.profile_id", + "description": "Profile ID.", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "field_mask", + "description": "The names of the fields that should be returned.", + "in": "query", + "required": false, + "type": "string" + } + ], + "tags": [ + "NsMACSettingsProfileRegistry" + ] + }, + "delete": { + "summary": "Delete deletes the MAC settings profile that matches the given identifiers.", + "operationId": "NsMACSettingsProfileRegistry_Delete", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v3DeleteMACSettingsProfileResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "mac_settings_profile_ids.application_ids.application_id", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "mac_settings_profile_ids.profile_id", + "description": "Profile ID.", + "in": "path", + "required": true, + "type": "string" + } + ], + "tags": [ + "NsMACSettingsProfileRegistry" + ] + }, + "put": { + "summary": "Update updates the MAC settings profile that matches the given identifiers.", + "operationId": "NsMACSettingsProfileRegistry_Update", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v3UpdateMACSettingsProfileResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "mac_settings_profile_ids.application_ids.application_id", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "mac_settings_profile_ids.profile_id", + "description": "Profile ID.", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/v3NsMACSettingsProfileRegistryUpdateBody" + } + } + ], + "tags": [ + "NsMACSettingsProfileRegistry" + ] + } + }, "/ns/default_mac_settings/{frequency_plan_id}/{lorawan_phy_version}": { "get": { "summary": "GetDefaultMACSettings retrieves the default MAC settings for a frequency plan.", @@ -19010,6 +19246,17 @@ }, "description": "SortBy defines the field to which the Console will sort the display of entities." }, + "UserConsolePreferencesTutorials": { + "type": "object", + "properties": { + "seen": { + "type": "array", + "items": { + "$ref": "#/definitions/v3Tutorial" + } + } + } + }, "UserRegistrationAdminApproval": { "type": "object", "properties": { @@ -22416,6 +22663,15 @@ } } }, + "v3CreateMACSettingsProfileResponse": { + "type": "object", + "properties": { + "mac_settings_profile": { + "$ref": "#/definitions/v3MACSettingsProfile", + "description": "The MAC settings profile." + } + } + }, "v3CreateNotificationResponse": { "type": "object", "properties": { @@ -22617,6 +22873,9 @@ } } }, + "v3DeleteMACSettingsProfileResponse": { + "type": "object" + }, "v3DeleteRelayResponse": { "type": "object" }, @@ -22692,6 +22951,18 @@ "default": "DOWNLINK_PATH_CONSTRAINT_NONE", "description": " - DOWNLINK_PATH_CONSTRAINT_NONE: Indicates that the gateway can be selected for downlink without constraints by the Network Server.\n - DOWNLINK_PATH_CONSTRAINT_PREFER_OTHER: Indicates that the gateway can be selected for downlink only if no other or better gateway can be selected.\n - DOWNLINK_PATH_CONSTRAINT_NEVER: Indicates that this gateway will never be selected for downlink, even if that results in no available downlink path." }, + "v3EmailNotificationPreferences": { + "type": "object", + "properties": { + "types": { + "type": "array", + "items": { + "$ref": "#/definitions/v3NotificationType" + } + } + }, + "description": "EmailNotificationPreferences is the message that defines the types of notifications for which the user wants to receive an email." + }, "v3EmailValidation": { "type": "object", "properties": { @@ -24965,6 +25236,15 @@ } } }, + "v3GetMACSettingsProfileResponse": { + "type": "object", + "properties": { + "mac_settings_profile": { + "$ref": "#/definitions/v3MACSettingsProfile", + "description": "The MAC settings profile." + } + } + }, "v3GetNetIDResponse": { "type": "object", "properties": { @@ -25627,6 +25907,19 @@ }, "description": "Filter gateways by fields." }, + "v3ListMACSettingsProfilesResponse": { + "type": "object", + "properties": { + "mac_settings_profiles": { + "type": "array", + "items": { + "type": "object", + "$ref": "#/definitions/v3MACSettingsProfile" + }, + "description": "The MAC settings profiles." + } + } + }, "v3ListNotificationsResponse": { "type": "object", "properties": { @@ -26162,6 +26455,32 @@ } } }, + "v3MACSettingsProfile": { + "type": "object", + "properties": { + "ids": { + "$ref": "#/definitions/v3MACSettingsProfileIdentifiers", + "description": "Profile identifiers." + }, + "mac_settings": { + "$ref": "#/definitions/v3MACSettings", + "description": "MAC settings." + } + } + }, + "v3MACSettingsProfileIdentifiers": { + "type": "object", + "properties": { + "application_ids": { + "$ref": "#/definitions/v3ApplicationIdentifiers", + "description": "Application IDs." + }, + "profile_id": { + "type": "string", + "description": "Profile ID." + } + } + }, "v3MACState": { "type": "object", "properties": { @@ -27087,7 +27406,7 @@ }, "notification_type": { "type": "string", - "description": "The type of this notification." + "description": "The type of this notification.\nTODO: Replace with type NotificationType in v4 https://github.com/TheThingsNetwork/lorawan-stack/issues/7384." }, "data": { "$ref": "#/definitions/protobufAny", @@ -27139,6 +27458,24 @@ ], "default": "NOTIFICATION_STATUS_UNSEEN" }, + "v3NotificationType": { + "type": "string", + "enum": [ + "UNKNOWN", + "API_KEY_CREATED", + "API_KEY_CHANGED", + "CLIENT_REQUESTED", + "COLLABORATOR_CHANGED", + "ENTITY_STATE_CHANGED", + "INVITATION", + "LOGIN_TOKEN", + "PASSWORD_CHANGED", + "TEMPORARY_PASSWORD", + "USER_REQUESTED", + "VALIDATE" + ], + "default": "UNKNOWN" + }, "v3NsEndDeviceRegistrySetBody": { "type": "object", "properties": { @@ -27416,6 +27753,63 @@ } } }, + "v3NsMACSettingsProfileRegistryCreateBody": { + "type": "object", + "properties": { + "mac_settings_profile": { + "type": "object", + "properties": { + "ids": { + "type": "object", + "properties": { + "application_ids": { + "type": "object", + "description": "Application IDs.", + "title": "Application IDs." + }, + "profile_id": { + "type": "string", + "description": "Profile ID." + } + }, + "description": "Profile identifiers.", + "title": "Profile identifiers." + }, + "mac_settings": { + "$ref": "#/definitions/v3MACSettings", + "description": "MAC settings." + } + }, + "description": "The MAC settings profile to create.", + "title": "The MAC settings profile to create." + } + } + }, + "v3NsMACSettingsProfileRegistryUpdateBody": { + "type": "object", + "properties": { + "mac_settings_profile_ids": { + "type": "object", + "properties": { + "application_ids": { + "type": "object", + "description": "Application IDs.", + "title": "Application IDs." + } + }, + "description": "The identifiers of the MAC settings profile.", + "title": "The identifiers of the MAC settings profile." + }, + "mac_settings_profile": { + "$ref": "#/definitions/v3MACSettingsProfile", + "description": "The MAC settings profile to update." + }, + "field_mask": { + "type": "string", + "description": "The names of the fields that should be updated." + } + } + }, "v3NwkSKeysResponse": { "type": "object", "properties": { @@ -28575,8 +28969,11 @@ "right_invalid", "RIGHT_USER_INFO", "RIGHT_USER_SETTINGS_BASIC", + "RIGHT_USER_LIST", + "RIGHT_USER_CREATE", "RIGHT_USER_SETTINGS_API_KEYS", "RIGHT_USER_DELETE", + "RIGHT_USER_PURGE", "RIGHT_USER_AUTHORIZED_CLIENTS", "RIGHT_USER_APPLICATIONS_LIST", "RIGHT_USER_APPLICATIONS_CREATE", @@ -28594,6 +28991,7 @@ "RIGHT_APPLICATION_SETTINGS_COLLABORATORS", "RIGHT_APPLICATION_SETTINGS_PACKAGES", "RIGHT_APPLICATION_DELETE", + "RIGHT_APPLICATION_PURGE", "RIGHT_APPLICATION_DEVICES_READ", "RIGHT_APPLICATION_DEVICES_WRITE", "RIGHT_APPLICATION_DEVICES_READ_KEYS", @@ -28608,11 +29006,13 @@ "RIGHT_CLIENT_SETTINGS_BASIC", "RIGHT_CLIENT_SETTINGS_COLLABORATORS", "RIGHT_CLIENT_DELETE", + "RIGHT_CLIENT_PURGE", "RIGHT_GATEWAY_INFO", "RIGHT_GATEWAY_SETTINGS_BASIC", "RIGHT_GATEWAY_SETTINGS_API_KEYS", "RIGHT_GATEWAY_SETTINGS_COLLABORATORS", "RIGHT_GATEWAY_DELETE", + "RIGHT_GATEWAY_PURGE", "RIGHT_GATEWAY_TRAFFIC_READ", "RIGHT_GATEWAY_TRAFFIC_DOWN_WRITE", "RIGHT_GATEWAY_LINK", @@ -28626,6 +29026,7 @@ "RIGHT_ORGANIZATION_SETTINGS_API_KEYS", "RIGHT_ORGANIZATION_SETTINGS_MEMBERS", "RIGHT_ORGANIZATION_DELETE", + "RIGHT_ORGANIZATION_PURGE", "RIGHT_ORGANIZATION_APPLICATIONS_LIST", "RIGHT_ORGANIZATION_APPLICATIONS_CREATE", "RIGHT_ORGANIZATION_GATEWAYS_LIST", @@ -28638,7 +29039,7 @@ "RIGHT_ALL" ], "default": "right_invalid", - "description": "Right is the enum that defines all the different rights to do something in the network.\n\n - RIGHT_USER_INFO: The right to view user information.\n - RIGHT_USER_SETTINGS_BASIC: The right to edit basic user settings.\n - RIGHT_USER_SETTINGS_API_KEYS: The right to view and edit user API keys.\n - RIGHT_USER_DELETE: The right to delete user account.\n - RIGHT_USER_AUTHORIZED_CLIENTS: The right to view and edit authorized OAuth clients of the user.\n - RIGHT_USER_APPLICATIONS_LIST: The right to list applications the user is a collaborator of.\n - RIGHT_USER_APPLICATIONS_CREATE: The right to create an application under the user account.\n - RIGHT_USER_GATEWAYS_LIST: The right to list gateways the user is a collaborator of.\n - RIGHT_USER_GATEWAYS_CREATE: The right to create a gateway under the account of the user.\n - RIGHT_USER_CLIENTS_LIST: The right to list OAuth clients the user is a collaborator of.\n - RIGHT_USER_CLIENTS_CREATE: The right to create an OAuth client under the account of the user.\n - RIGHT_USER_ORGANIZATIONS_LIST: The right to list organizations the user is a member of.\n - RIGHT_USER_ORGANIZATIONS_CREATE: The right to create an organization under the user account.\n - RIGHT_USER_NOTIFICATIONS_READ: The right to read notifications sent to the user.\n - RIGHT_USER_ALL: The pseudo-right for all (current and future) user rights.\n - RIGHT_APPLICATION_INFO: The right to view application information.\n - RIGHT_APPLICATION_SETTINGS_BASIC: The right to edit basic application settings.\n - RIGHT_APPLICATION_SETTINGS_API_KEYS: The right to view and edit application API keys.\n - RIGHT_APPLICATION_SETTINGS_COLLABORATORS: The right to view and edit application collaborators.\n - RIGHT_APPLICATION_SETTINGS_PACKAGES: The right to view and edit application packages and associations.\n - RIGHT_APPLICATION_DELETE: The right to delete application.\n - RIGHT_APPLICATION_DEVICES_READ: The right to view devices in application.\n - RIGHT_APPLICATION_DEVICES_WRITE: The right to create devices in application.\n - RIGHT_APPLICATION_DEVICES_READ_KEYS: The right to view device keys in application.\nNote that keys may not be stored in a way that supports viewing them.\n - RIGHT_APPLICATION_DEVICES_WRITE_KEYS: The right to edit device keys in application.\n - RIGHT_APPLICATION_TRAFFIC_READ: The right to read application traffic (uplink and downlink).\n - RIGHT_APPLICATION_TRAFFIC_UP_WRITE: The right to write uplink application traffic.\n - RIGHT_APPLICATION_TRAFFIC_DOWN_WRITE: The right to write downlink application traffic.\n - RIGHT_APPLICATION_LINK: The right to link as Application to a Network Server for traffic exchange,\ni.e. read uplink and write downlink (API keys only).\nThis right is typically only given to an Application Server.\nThis right implies RIGHT_APPLICATION_INFO, RIGHT_APPLICATION_TRAFFIC_READ,\nand RIGHT_APPLICATION_TRAFFIC_DOWN_WRITE.\n - RIGHT_APPLICATION_ALL: The pseudo-right for all (current and future) application rights.\n - RIGHT_CLIENT_ALL: The pseudo-right for all (current and future) OAuth client rights.\n - RIGHT_CLIENT_INFO: The right to read client information.\n - RIGHT_CLIENT_SETTINGS_BASIC: The right to edit basic client settings.\n - RIGHT_CLIENT_SETTINGS_COLLABORATORS: The right to view and edit client collaborators.\n - RIGHT_CLIENT_DELETE: The right to delete a client.\n - RIGHT_GATEWAY_INFO: The right to view gateway information.\n - RIGHT_GATEWAY_SETTINGS_BASIC: The right to edit basic gateway settings.\n - RIGHT_GATEWAY_SETTINGS_API_KEYS: The right to view and edit gateway API keys.\n - RIGHT_GATEWAY_SETTINGS_COLLABORATORS: The right to view and edit gateway collaborators.\n - RIGHT_GATEWAY_DELETE: The right to delete gateway.\n - RIGHT_GATEWAY_TRAFFIC_READ: The right to read gateway traffic.\n - RIGHT_GATEWAY_TRAFFIC_DOWN_WRITE: The right to write downlink gateway traffic.\n - RIGHT_GATEWAY_LINK: The right to link as Gateway to a Gateway Server for traffic exchange,\ni.e. write uplink and read downlink (API keys only)\nThis right is typically only given to a gateway.\nThis right implies RIGHT_GATEWAY_INFO.\n - RIGHT_GATEWAY_STATUS_READ: The right to view gateway status.\n - RIGHT_GATEWAY_LOCATION_READ: The right to view view gateway location.\n - RIGHT_GATEWAY_WRITE_SECRETS: The right to store secrets associated with this gateway.\n - RIGHT_GATEWAY_READ_SECRETS: The right to retrieve secrets associated with this gateway.\n - RIGHT_GATEWAY_ALL: The pseudo-right for all (current and future) gateway rights.\n - RIGHT_ORGANIZATION_INFO: The right to view organization information.\n - RIGHT_ORGANIZATION_SETTINGS_BASIC: The right to edit basic organization settings.\n - RIGHT_ORGANIZATION_SETTINGS_API_KEYS: The right to view and edit organization API keys.\n - RIGHT_ORGANIZATION_SETTINGS_MEMBERS: The right to view and edit organization members.\n - RIGHT_ORGANIZATION_DELETE: The right to delete organization.\n - RIGHT_ORGANIZATION_APPLICATIONS_LIST: The right to list the applications the organization is a collaborator of.\n - RIGHT_ORGANIZATION_APPLICATIONS_CREATE: The right to create an application under the organization.\n - RIGHT_ORGANIZATION_GATEWAYS_LIST: The right to list the gateways the organization is a collaborator of.\n - RIGHT_ORGANIZATION_GATEWAYS_CREATE: The right to create a gateway under the organization.\n - RIGHT_ORGANIZATION_CLIENTS_LIST: The right to list the OAuth clients the organization is a collaborator of.\n - RIGHT_ORGANIZATION_CLIENTS_CREATE: The right to create an OAuth client under the organization.\n - RIGHT_ORGANIZATION_ADD_AS_COLLABORATOR: The right to add the organization as a collaborator on an existing entity.\n - RIGHT_ORGANIZATION_ALL: The pseudo-right for all (current and future) organization rights.\n - RIGHT_SEND_INVITES: The right to send invites to new users.\nNote that this is not prefixed with \"USER_\"; it is not a right on the user entity.\n - RIGHT_ALL: The pseudo-right for all (current and future) possible rights." + "description": "Right is the enum that defines all the different rights to do something in the network.\n\n - RIGHT_USER_INFO: The right to view user information.\n - RIGHT_USER_SETTINGS_BASIC: The right to edit basic user settings.\n - RIGHT_USER_LIST: The right to list users accounts.\n - RIGHT_USER_CREATE: The right to create an user account.\n - RIGHT_USER_SETTINGS_API_KEYS: The right to view and edit user API keys.\n - RIGHT_USER_DELETE: The right to delete user account.\n - RIGHT_USER_PURGE: The right to delete user account.\n - RIGHT_USER_AUTHORIZED_CLIENTS: The right to view and edit authorized OAuth clients of the user.\n - RIGHT_USER_APPLICATIONS_LIST: The right to list applications the user is a collaborator of.\n - RIGHT_USER_APPLICATIONS_CREATE: The right to create an application under the user account.\n - RIGHT_USER_GATEWAYS_LIST: The right to list gateways the user is a collaborator of.\n - RIGHT_USER_GATEWAYS_CREATE: The right to create a gateway under the account of the user.\n - RIGHT_USER_CLIENTS_LIST: The right to list OAuth clients the user is a collaborator of.\n - RIGHT_USER_CLIENTS_CREATE: The right to create an OAuth client under the account of the user.\n - RIGHT_USER_ORGANIZATIONS_LIST: The right to list organizations the user is a member of.\n - RIGHT_USER_ORGANIZATIONS_CREATE: The right to create an organization under the user account.\n - RIGHT_USER_NOTIFICATIONS_READ: The right to read notifications sent to the user.\n - RIGHT_USER_ALL: The pseudo-right for all (current and future) user rights.\n - RIGHT_APPLICATION_INFO: The right to view application information.\n - RIGHT_APPLICATION_SETTINGS_BASIC: The right to edit basic application settings.\n - RIGHT_APPLICATION_SETTINGS_API_KEYS: The right to view and edit application API keys.\n - RIGHT_APPLICATION_SETTINGS_COLLABORATORS: The right to view and edit application collaborators.\n - RIGHT_APPLICATION_SETTINGS_PACKAGES: The right to view and edit application packages and associations.\n - RIGHT_APPLICATION_DELETE: The right to delete application.\n - RIGHT_APPLICATION_PURGE: The right to purge application.\n - RIGHT_APPLICATION_DEVICES_READ: The right to view devices in application.\n - RIGHT_APPLICATION_DEVICES_WRITE: The right to create devices in application.\n - RIGHT_APPLICATION_DEVICES_READ_KEYS: The right to view device keys in application.\nNote that keys may not be stored in a way that supports viewing them.\n - RIGHT_APPLICATION_DEVICES_WRITE_KEYS: The right to edit device keys in application.\n - RIGHT_APPLICATION_TRAFFIC_READ: The right to read application traffic (uplink and downlink).\n - RIGHT_APPLICATION_TRAFFIC_UP_WRITE: The right to write uplink application traffic.\n - RIGHT_APPLICATION_TRAFFIC_DOWN_WRITE: The right to write downlink application traffic.\n - RIGHT_APPLICATION_LINK: The right to link as Application to a Network Server for traffic exchange,\ni.e. read uplink and write downlink (API keys only).\nThis right is typically only given to an Application Server.\nThis right implies RIGHT_APPLICATION_INFO, RIGHT_APPLICATION_TRAFFIC_READ,\nand RIGHT_APPLICATION_TRAFFIC_DOWN_WRITE.\n - RIGHT_APPLICATION_ALL: The pseudo-right for all (current and future) application rights.\n - RIGHT_CLIENT_ALL: The pseudo-right for all (current and future) OAuth client rights.\n - RIGHT_CLIENT_INFO: The right to read client information.\n - RIGHT_CLIENT_SETTINGS_BASIC: The right to edit basic client settings.\n - RIGHT_CLIENT_SETTINGS_COLLABORATORS: The right to view and edit client collaborators.\n - RIGHT_CLIENT_DELETE: The right to delete a client.\n - RIGHT_CLIENT_PURGE: The right to purge a client.\n - RIGHT_GATEWAY_INFO: The right to view gateway information.\n - RIGHT_GATEWAY_SETTINGS_BASIC: The right to edit basic gateway settings.\n - RIGHT_GATEWAY_SETTINGS_API_KEYS: The right to view and edit gateway API keys.\n - RIGHT_GATEWAY_SETTINGS_COLLABORATORS: The right to view and edit gateway collaborators.\n - RIGHT_GATEWAY_DELETE: The right to delete gateway.\n - RIGHT_GATEWAY_PURGE: The right to purge gateway.\n - RIGHT_GATEWAY_TRAFFIC_READ: The right to read gateway traffic.\n - RIGHT_GATEWAY_TRAFFIC_DOWN_WRITE: The right to write downlink gateway traffic.\n - RIGHT_GATEWAY_LINK: The right to link as Gateway to a Gateway Server for traffic exchange,\ni.e. write uplink and read downlink (API keys only)\nThis right is typically only given to a gateway.\nThis right implies RIGHT_GATEWAY_INFO.\n - RIGHT_GATEWAY_STATUS_READ: The right to view gateway status.\n - RIGHT_GATEWAY_LOCATION_READ: The right to view view gateway location.\n - RIGHT_GATEWAY_WRITE_SECRETS: The right to store secrets associated with this gateway.\n - RIGHT_GATEWAY_READ_SECRETS: The right to retrieve secrets associated with this gateway.\n - RIGHT_GATEWAY_ALL: The pseudo-right for all (current and future) gateway rights.\n - RIGHT_ORGANIZATION_INFO: The right to view organization information.\n - RIGHT_ORGANIZATION_SETTINGS_BASIC: The right to edit basic organization settings.\n - RIGHT_ORGANIZATION_SETTINGS_API_KEYS: The right to view and edit organization API keys.\n - RIGHT_ORGANIZATION_SETTINGS_MEMBERS: The right to view and edit organization members.\n - RIGHT_ORGANIZATION_DELETE: The right to delete organization.\n - RIGHT_ORGANIZATION_PURGE: The right to purge organization.\n - RIGHT_ORGANIZATION_APPLICATIONS_LIST: The right to list the applications the organization is a collaborator of.\n - RIGHT_ORGANIZATION_APPLICATIONS_CREATE: The right to create an application under the organization.\n - RIGHT_ORGANIZATION_GATEWAYS_LIST: The right to list the gateways the organization is a collaborator of.\n - RIGHT_ORGANIZATION_GATEWAYS_CREATE: The right to create a gateway under the organization.\n - RIGHT_ORGANIZATION_CLIENTS_LIST: The right to list the OAuth clients the organization is a collaborator of.\n - RIGHT_ORGANIZATION_CLIENTS_CREATE: The right to create an OAuth client under the organization.\n - RIGHT_ORGANIZATION_ADD_AS_COLLABORATOR: The right to add the organization as a collaborator on an existing entity.\n - RIGHT_ORGANIZATION_ALL: The pseudo-right for all (current and future) organization rights.\n - RIGHT_SEND_INVITES: The right to send invites to new users.\nNote that this is not prefixed with \"USER_\"; it is not a right on the user entity.\n - RIGHT_ALL: The pseudo-right for all (current and future) possible rights." }, "v3Rights": { "type": "object", @@ -29036,6 +29437,15 @@ } } }, + "v3Tutorial": { + "type": "string", + "enum": [ + "TUTORIAL_UNKNOWN", + "TUTORIAL_LIVE_DATA_SPLIT_VIEW" + ], + "default": "TUTORIAL_UNKNOWN", + "description": "Tutorial is a set of possible tutorials in the Console." + }, "v3TxAcknowledgment": { "type": "object", "properties": { @@ -29124,6 +29534,15 @@ ], "default": "LOWEST" }, + "v3UpdateMACSettingsProfileResponse": { + "type": "object", + "properties": { + "mac_settings_profile": { + "$ref": "#/definitions/v3MACSettingsProfile", + "description": "The MAC settings profile." + } + } + }, "v3UpdatePacketBrokerGatewayResponse": { "type": "object", "properties": { @@ -29247,6 +29666,16 @@ "console_preferences": { "$ref": "#/definitions/v3UserConsolePreferences", "description": "Console preferences contains the user's preferences regarding the behavior of the Console." + }, + "email_notification_preferences": { + "$ref": "#/definitions/v3EmailNotificationPreferences" + }, + "universal_rights": { + "type": "array", + "items": { + "$ref": "#/definitions/v3Right" + }, + "description": "Universal rights are capable of restricting the user's interactions with the API, be it an admin or not." } }, "description": "User is the message that defines a user on the network." @@ -29383,6 +29812,9 @@ }, "sort_by": { "$ref": "#/definitions/UserConsolePreferencesSortBy" + }, + "tutorials": { + "$ref": "#/definitions/UserConsolePreferencesTutorials" } }, "description": "UserConsolePreferences is the message that defines the user preferences for the Console." @@ -29506,6 +29938,16 @@ "console_preferences": { "$ref": "#/definitions/v3UserConsolePreferences", "description": "Console preferences contains the user's preferences regarding the behavior of the Console." + }, + "email_notification_preferences": { + "$ref": "#/definitions/v3EmailNotificationPreferences" + }, + "universal_rights": { + "type": "array", + "items": { + "$ref": "#/definitions/v3Right" + }, + "description": "Universal rights are capable of restricting the user's interactions with the API, be it an admin or not." } }, "description": "User is the message that defines a user on the network." diff --git a/api/ttn/lorawan/v3/end_device.proto b/api/ttn/lorawan/v3/end_device.proto index 017a5b3a71..143f2c7887 100644 --- a/api/ttn/lorawan/v3/end_device.proto +++ b/api/ttn/lorawan/v3/end_device.proto @@ -690,6 +690,23 @@ message MACSettings { RelaySettings desired_relay = 37; } +message MACSettingsProfile { + option (thethings.flags.message) = { + select: true, + set: true + }; + // Profile identifiers. + MACSettingsProfileIdentifiers ids = 1 [ + (validate.rules).message.required = true, + (thethings.flags.field) = { + select: false, + hidden: true + } + ]; + // MAC settings. + MACSettings mac_settings = 2 [(validate.rules).message.required = true]; +} + // MACState represents the state of MAC layer of the device. // MACState is reset on each join for OTAA or ResetInd for ABP devices. // This is used internally by the Network Server. diff --git a/api/ttn/lorawan/v3/identifiers.proto b/api/ttn/lorawan/v3/identifiers.proto index 0e17fc72a3..e3cf242a07 100644 --- a/api/ttn/lorawan/v3/identifiers.proto +++ b/api/ttn/lorawan/v3/identifiers.proto @@ -297,3 +297,17 @@ message EndDeviceIdentifiersList { message GatewayIdentifiersList { repeated GatewayIdentifiers gateway_ids = 1; } + +message MACSettingsProfileIdentifiers { + option (thethings.flags.message) = { + select: true, + set: true + }; + // Application IDs. + ApplicationIdentifiers application_ids = 1 [(validate.rules).message.required = true]; + // Profile ID. + string profile_id = 2 [(validate.rules).string = { + pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$", + max_len: 36 + }]; +} diff --git a/api/ttn/lorawan/v3/networkserver_mac_settings_profile.proto b/api/ttn/lorawan/v3/networkserver_mac_settings_profile.proto new file mode 100644 index 0000000000..30c27f517f --- /dev/null +++ b/api/ttn/lorawan/v3/networkserver_mac_settings_profile.proto @@ -0,0 +1,136 @@ +// Copyright © 2024 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. + +syntax = "proto3"; + +package ttn.lorawan.v3; + +import "google/api/annotations.proto"; +import "google/protobuf/field_mask.proto"; +import "protoc-gen-openapiv2/options/annotations.proto"; +import "thethings/flags/annotations.proto"; +import "ttn/lorawan/v3/end_device.proto"; +import "ttn/lorawan/v3/identifiers.proto"; +import "validate/validate.proto"; + +option go_package = "go.thethings.network/lorawan-stack/v3/pkg/ttnpb"; + +message CreateMACSettingsProfileRequest { + // The MAC settings profile to create. + MACSettingsProfile mac_settings_profile = 1 [(validate.rules).message.required = true]; +} + +message CreateMACSettingsProfileResponse { + // The MAC settings profile. + MACSettingsProfile mac_settings_profile = 1 [(validate.rules).message.required = true]; +} + +message GetMACSettingsProfileRequest { + // The identifiers of the MAC settings profile. + MACSettingsProfileIdentifiers mac_settings_profile_ids = 1 [(validate.rules).message.required = true]; + // The names of the fields that should be returned. + google.protobuf.FieldMask field_mask = 2; +} + +message GetMACSettingsProfileResponse { + // The MAC settings profile. + MACSettingsProfile mac_settings_profile = 1 [(validate.rules).message.required = true]; +} + +message UpdateMACSettingsProfileRequest { + // The identifiers of the MAC settings profile. + MACSettingsProfileIdentifiers mac_settings_profile_ids = 1 [(validate.rules).message.required = true]; + // The MAC settings profile to update. + MACSettingsProfile mac_settings_profile = 2 [(validate.rules).message.required = true]; + // The names of the fields that should be updated. + google.protobuf.FieldMask field_mask = 3; +} + +message UpdateMACSettingsProfileResponse { + // The MAC settings profile. + MACSettingsProfile mac_settings_profile = 1 [(validate.rules).message.required = true]; +} + +message DeleteMACSettingsProfileRequest { + // The identifiers of the MAC settings profile. + MACSettingsProfileIdentifiers mac_settings_profile_ids = 1 [(validate.rules).message.required = true]; +} + +message DeleteMACSettingsProfileResponse {} + +message ListMACSettingsProfilesRequest { + option (thethings.flags.message) = { + select: false, + set: true + }; + // The identifiers of the application. + ApplicationIdentifiers application_ids = 1 [(validate.rules).message.required = true]; + // The names of the fields that should be returned. + google.protobuf.FieldMask field_mask = 2; + // Order the results by this field path (must be present in the field mask). + // Default ordering is by ID. Prepend with a minus (-) to reverse the order. + string order = 3 [(validate.rules).string = { + in: [ + "", + "ids.profile_id", + "-ids.profile_id" + ] + }]; + // Limit the number of results per page. + uint32 limit = 4 [(validate.rules).uint32.lte = 1000]; + // Page number for pagination. 0 is interpreted as 1. + uint32 page = 5; +} + +message ListMACSettingsProfilesResponse { + // The MAC settings profiles. + repeated MACSettingsProfile mac_settings_profiles = 1; +} + +// The NsMACSettingsProfileRegistry service allows clients to manage MAC settings profiles on the Network Server. +// EXPERIMENTAL: This service is subject to change. +service NsMACSettingsProfileRegistry { + option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_tag) = {description: "Manage MAC settings profiles on The Things Stack Network Server."}; + + // Create creates a new MAC settings profile. + rpc Create(CreateMACSettingsProfileRequest) returns (CreateMACSettingsProfileResponse) { + option (google.api.http) = { + post: "/ns/applications/{mac_settings_profile.ids.application_ids.application_id}/mac_settings_profiles" + body: "*" + }; + } + + // Get returns the MAC settings profile that matches the given identifiers. + rpc Get(GetMACSettingsProfileRequest) returns (GetMACSettingsProfileResponse) { + option (google.api.http) = {get: "/ns/applications/{mac_settings_profile_ids.application_ids.application_id}/mac_settings_profiles/{mac_settings_profile_ids.profile_id}"}; + } + + // Update updates the MAC settings profile that matches the given identifiers. + rpc Update(UpdateMACSettingsProfileRequest) returns (UpdateMACSettingsProfileResponse) { + option (google.api.http) = { + put: "/ns/applications/{mac_settings_profile_ids.application_ids.application_id}/mac_settings_profiles/{mac_settings_profile_ids.profile_id}" + body: "*" + }; + } + + // Delete deletes the MAC settings profile that matches the given identifiers. + rpc Delete(DeleteMACSettingsProfileRequest) returns (DeleteMACSettingsProfileResponse) { + option (google.api.http) = {delete: "/ns/applications/{mac_settings_profile_ids.application_ids.application_id}/mac_settings_profiles/{mac_settings_profile_ids.profile_id}"}; + } + + // List lists the MAC settings profiles. + rpc List(ListMACSettingsProfilesRequest) returns (ListMACSettingsProfilesResponse) { + option (google.api.http) = {get: "/ns/applications/{application_ids.application_id}/mac_settings_profiles"}; + } +} diff --git a/api/ttn/lorawan/v3/notification_service.proto b/api/ttn/lorawan/v3/notification_service.proto index b3f5601730..f405a8f46a 100644 --- a/api/ttn/lorawan/v3/notification_service.proto +++ b/api/ttn/lorawan/v3/notification_service.proto @@ -40,7 +40,23 @@ message Notification { EntityIdentifiers entity_ids = 3; // The type of this notification. - string notification_type = 4; + // TODO: Replace with type NotificationType in v4 https://github.com/TheThingsNetwork/lorawan-stack/issues/7384. + string notification_type = 4 [(validate.rules).string = { + in: [ + "unknown", + "api_key_created", + "api_key_changed", + "client_requested", + "collaborator_changed", + "entity_state_changed", + "invitation", + "login_token", + "password_changed", + "temporary_password", + "user_requested", + "validate" + ] + }]; // The data related to the notification. google.protobuf.Any data = 5; @@ -57,7 +73,7 @@ message Notification { repeated NotificationReceiver receivers = 8; // Whether an email was sent for the notification. - bool email = 9; + bool email = 9 [deprecated = true]; // The status of the notification. NotificationStatus status = 10; @@ -66,6 +82,23 @@ message Notification { google.protobuf.Timestamp status_updated_at = 11; } +enum NotificationType { + option (thethings.json.enum) = {marshal_as_string: true}; + + UNKNOWN = 0; + API_KEY_CREATED = 1; + API_KEY_CHANGED = 2; + CLIENT_REQUESTED = 3; + COLLABORATOR_CHANGED = 4; + ENTITY_STATE_CHANGED = 5; + INVITATION = 6; + LOGIN_TOKEN = 7; + PASSWORD_CHANGED = 8; + TEMPORARY_PASSWORD = 9; + USER_REQUESTED = 10; + VALIDATE = 11; +} + enum NotificationReceiver { option (thethings.json.enum) = { marshal_as_string: true, @@ -104,8 +137,20 @@ message CreateNotificationRequest { // The type of this notification. string notification_type = 2 [(validate.rules).string = { - min_len: 1, - max_len: 100, + in: [ + "unknown", + "api_key_created", + "api_key_changed", + "client_requested", + "collaborator_changed", + "entity_state_changed", + "invitation", + "login_token", + "password_changed", + "temporary_password", + "user_requested", + "validate" + ] }]; // The data related to the notification. @@ -124,7 +169,7 @@ message CreateNotificationRequest { }]; // Whether an email should be sent for the notification. - bool email = 6; + bool email = 6 [deprecated = true]; } message CreateNotificationResponse { diff --git a/api/ttn/lorawan/v3/rights.proto b/api/ttn/lorawan/v3/rights.proto index 6a3096c48f..2c0036adec 100644 --- a/api/ttn/lorawan/v3/rights.proto +++ b/api/ttn/lorawan/v3/rights.proto @@ -37,10 +37,16 @@ enum Right { RIGHT_USER_INFO = 1; // The right to edit basic user settings. RIGHT_USER_SETTINGS_BASIC = 2; + // The right to list users accounts. + RIGHT_USER_LIST = 87; + // The right to create an user account. + RIGHT_USER_CREATE = 88; // The right to view and edit user API keys. RIGHT_USER_SETTINGS_API_KEYS = 3; // The right to delete user account. RIGHT_USER_DELETE = 4; + // The right to delete user account. + RIGHT_USER_PURGE = 66; // The right to view and edit authorized OAuth clients of the user. RIGHT_USER_AUTHORIZED_CLIENTS = 5; // The right to list applications the user is a collaborator of. @@ -76,6 +82,8 @@ enum Right { RIGHT_APPLICATION_SETTINGS_PACKAGES = 56; // The right to delete application. RIGHT_APPLICATION_DELETE = 19; + // The right to purge application. + RIGHT_APPLICATION_PURGE = 64; // The right to view devices in application. RIGHT_APPLICATION_DEVICES_READ = 20; // The right to create devices in application. @@ -110,6 +118,8 @@ enum Right { RIGHT_CLIENT_SETTINGS_COLLABORATORS = 62; // The right to delete a client. RIGHT_CLIENT_DELETE = 63; + // The right to purge a client. + RIGHT_CLIENT_PURGE = 68; // The right to view gateway information. RIGHT_GATEWAY_INFO = 30; @@ -121,6 +131,8 @@ enum Right { RIGHT_GATEWAY_SETTINGS_COLLABORATORS = 33; // The right to delete gateway. RIGHT_GATEWAY_DELETE = 34; + // The right to purge gateway. + RIGHT_GATEWAY_PURGE = 67; // The right to read gateway traffic. RIGHT_GATEWAY_TRAFFIC_READ = 35; // The right to write downlink gateway traffic. @@ -151,6 +163,8 @@ enum Right { RIGHT_ORGANIZATION_SETTINGS_MEMBERS = 44; // The right to delete organization. RIGHT_ORGANIZATION_DELETE = 45; + // The right to purge organization. + RIGHT_ORGANIZATION_PURGE = 65; // The right to list the applications the organization is a collaborator of. RIGHT_ORGANIZATION_APPLICATIONS_LIST = 46; // The right to create an application under the organization. @@ -172,10 +186,29 @@ enum Right { // Note that this is not prefixed with "USER_"; it is not a right on the user entity. RIGHT_SEND_INVITES = 54; + reserved 69; + reserved 70; + reserved 71; + reserved 72; + reserved 73; + reserved 74; + reserved 75; + reserved 76; + reserved 77; + reserved 78; + reserved 79; + reserved 80; + reserved 81; + reserved 82; + reserved 83; + reserved 84; + reserved 85; + reserved 86; + // The pseudo-right for all (current and future) possible rights. RIGHT_ALL = 55; - // Next value: 64 + // Next value: 89 } message Rights { diff --git a/api/ttn/lorawan/v3/user.proto b/api/ttn/lorawan/v3/user.proto index 650386fcbc..7663cb151a 100644 --- a/api/ttn/lorawan/v3/user.proto +++ b/api/ttn/lorawan/v3/user.proto @@ -23,6 +23,7 @@ import "thethings/json/annotations.proto"; import "ttn/lorawan/v3/contact_info.proto"; import "ttn/lorawan/v3/enums.proto"; import "ttn/lorawan/v3/identifiers.proto"; +import "ttn/lorawan/v3/notification_service.proto"; import "ttn/lorawan/v3/picture.proto"; import "ttn/lorawan/v3/rights.proto"; import "validate/validate.proto"; @@ -43,6 +44,14 @@ enum ConsoleTheme { CONSOLE_THEME_DARK = 2; } +// Tutorial is a set of possible tutorials in the Console. +enum Tutorial { + option (thethings.json.enum) = {marshal_as_string: true}; + + TUTORIAL_UNKNOWN = 0; + TUTORIAL_LIVE_DATA_SPLIT_VIEW = 1; +} + // DashboardLayout is a set of possible layout values to be used in the Console. enum DashboardLayout { option (thethings.json.enum) = { @@ -54,6 +63,16 @@ enum DashboardLayout { DASHBOARD_LAYOUT_GRID = 2; } +// EmailNotificationPreferences is the message that defines the types of notifications for which the user wants to receive an email. +message EmailNotificationPreferences { + repeated NotificationType types = 1 [(validate.rules).repeated = { + unique: true, + items: { + enum: {defined_only: true} + } + }]; +} + // UserConsolePreferences is the message that defines the user preferences for the Console. message UserConsolePreferences { option (thethings.flags.message) = { @@ -171,6 +190,16 @@ message UserConsolePreferences { }]; } SortBy sort_by = 3; + + message Tutorials { + repeated Tutorial seen = 1 [(validate.rules).repeated = { + unique: true, + items: { + enum: {defined_only: true} + } + }]; + } + Tutorials tutorials = 4; } // User is the message that defines a user on the network. @@ -288,7 +317,12 @@ message User { // Console preferences contains the user's preferences regarding the behavior of the Console. UserConsolePreferences console_preferences = 25; - // next: 26 + EmailNotificationPreferences email_notification_preferences = 26; + + // Universal rights are capable of restricting the user's interactions with the API, be it an admin or not. + repeated Right universal_rights = 27; + + // next: 28 } message Users { diff --git a/cmd/ttn-lw-cli/commands/root.go b/cmd/ttn-lw-cli/commands/root.go index 861430638a..7af165876e 100644 --- a/cmd/ttn-lw-cli/commands/root.go +++ b/cmd/ttn-lw-cli/commands/root.go @@ -233,6 +233,9 @@ func preRun(tasks ...func() error) func(cmd *cobra.Command, args []string) error } rootCAs.AppendCertsFromPEM(pemBytes) http.DefaultTransport.(*http.Transport).TLSClientConfig.RootCAs = rootCAs + if err = api.AddCA(pemBytes); err != nil { + return err + } } // OAuth diff --git a/cmd/ttn-lw-cli/internal/api/grpc.go b/cmd/ttn-lw-cli/internal/api/grpc.go index ad28e3e4f5..d59f700cc3 100644 --- a/cmd/ttn-lw-cli/internal/api/grpc.go +++ b/cmd/ttn-lw-cli/internal/api/grpc.go @@ -17,6 +17,7 @@ package api import ( "context" "crypto/tls" + "crypto/x509" "sync" "time" @@ -134,6 +135,24 @@ var ( conns = make(map[string]*grpc.ClientConn) ) +// AddCA adds the CA certificate file. +func AddCA(pemBytes []byte) (err error) { + if tlsConfig == nil { + tlsConfig = &tls.Config{ + MinVersion: tls.VersionTLS12, + } + } + rootCAs := tlsConfig.RootCAs + if rootCAs == nil { + if rootCAs, err = x509.SystemCertPool(); err != nil { + rootCAs = x509.NewCertPool() + } + } + rootCAs.AppendCertsFromPEM(pemBytes) + tlsConfig.RootCAs = rootCAs + return nil +} + // Dial dials a gRPC connection to the target. func Dial(ctx context.Context, target string) (*grpc.ClientConn, error) { connMu.Lock() diff --git a/cmd/ttn-lw-stack/commands/start.go b/cmd/ttn-lw-stack/commands/start.go index c2988bdbff..ec3eaf7b09 100644 --- a/cmd/ttn-lw-stack/commands/start.go +++ b/cmd/ttn-lw-stack/commands/start.go @@ -79,6 +79,16 @@ func NewNetworkServerDownlinkTaskRedis(conf *Config) *redis.Client { return redis.New(conf.Redis.WithNamespace("ns", "tasks")) } +// NewNetworkServerMACSettingsProfileRegistryRedis instantiates a new redis client +// with the Network Server MAC Settings Profile Registry namespace. +func NewNetworkServerMACSettingsProfileRegistryRedis(conf *Config) *redis.Client { + redis.SetPaginationDefaults(redis.PaginationDefaults{ + DefaultLimit: conf.NS.Pagination.DefaultLimit, + }) + + return redis.New(conf.Redis.WithNamespace("ns", "mac-settings-profiles")) +} + // NewIdentityServerTelemetryTaskRedis instantiates a new redis client // with the Identity Server Telemetry Task namespace. func NewIdentityServerTelemetryTaskRedis(conf *Config) *redis.Client { @@ -91,6 +101,36 @@ func NewApplicationServerDeviceRegistryRedis(conf *Config) *redis.Client { return NewComponentDeviceRegistryRedis(conf, "as") } +// NewApplicationServerPubSubRegistryRedis instantiates a new redis client +// with the Application Server PubSub Registry namespace. +func NewApplicationServerPubSubRegistryRedis(conf *Config) *redis.Client { + redis.SetPaginationDefaults(redis.PaginationDefaults{ + DefaultLimit: conf.AS.Pagination.DefaultLimit, + }) + + return redis.New(config.Redis.WithNamespace("as", "io", "pubsub")) +} + +// NewApplicationServerPackagesRegistryRedis instantiates a new redis client +// with the Application Server Packages Registry namespace. +func NewApplicationServerPackagesRegistryRedis(conf *Config) *redis.Client { + redis.SetPaginationDefaults(redis.PaginationDefaults{ + DefaultLimit: conf.AS.Pagination.DefaultLimit, + }) + + return redis.New(config.Redis.WithNamespace("as", "io", "applicationpackages")) +} + +// NewApplicationServerWebhookRegistryRedis instantiates a new redis client +// with the Application Server Webhook Registry namespace. +func NewApplicationServerWebhookRegistryRedis(conf *Config) *redis.Client { + redis.SetPaginationDefaults(redis.PaginationDefaults{ + DefaultLimit: conf.AS.Pagination.DefaultLimit, + }) + + return redis.New(config.Redis.WithNamespace("as", "io", "webhooks")) +} + // NewJoinServerDeviceRegistryRedis instantiates a new redis client // with the Join Server Device Registry namespace. func NewJoinServerDeviceRegistryRedis(conf *Config) *redis.Client { @@ -342,6 +382,14 @@ var startCommand = &cobra.Command{ config.NS.ScheduledDownlinkMatcher = &nsredis.ScheduledDownlinkMatcher{ Redis: redis.New(config.Cache.Redis.WithNamespace("ns", "scheduled-downlinks")), } + macSettingsProfiles := &nsredis.MACSettingsProfileRegistry{ + Redis: NewNetworkServerMACSettingsProfileRegistryRedis(config), + LockTTL: defaultLockTTL, + } + if err := macSettingsProfiles.Init(ctx); err != nil { + return shared.ErrInitializeNetworkServer.WithCause(err) + } + config.NS.MACSettingsProfileRegistry = macSettingsProfiles ns, err := networkserver.New(c, &config.NS) if err != nil { return shared.ErrInitializeNetworkServer.WithCause(err) @@ -371,7 +419,7 @@ var startCommand = &cobra.Command{ Redis: redis.New(config.Cache.Redis.WithNamespace("as", "traffic")), } pubsubRegistry := &asiopsredis.PubSubRegistry{ - Redis: redis.New(config.Redis.WithNamespace("as", "io", "pubsub")), + Redis: NewApplicationServerPubSubRegistryRedis(config), LockTTL: defaultLockTTL, } if err := pubsubRegistry.Init(ctx); err != nil { @@ -380,7 +428,7 @@ var startCommand = &cobra.Command{ config.AS.PubSub.Registry = pubsubRegistry applicationPackagesRegistry, err := asioapredis.NewApplicationPackagesRegistry( ctx, - redis.New(config.Redis.WithNamespace("as", "io", "applicationpackages")), + NewApplicationServerPackagesRegistryRedis(config), defaultLockTTL, ) if err != nil { @@ -389,7 +437,7 @@ var startCommand = &cobra.Command{ config.AS.Packages.Registry = applicationPackagesRegistry if config.AS.Webhooks.Target != "" { webhookRegistry := &asiowebredis.WebhookRegistry{ - Redis: redis.New(config.Redis.WithNamespace("as", "io", "webhooks")), + Redis: NewApplicationServerWebhookRegistryRedis(config), LockTTL: defaultLockTTL, } if err := webhookRegistry.Init(ctx); err != nil { diff --git a/config/messages.json b/config/messages.json index 6c8e4924d3..a474d02dbf 100644 --- a/config/messages.json +++ b/config/messages.json @@ -593,6 +593,15 @@ "file": "i18n.go" } }, + "enum:RIGHT_APPLICATION_PURGE": { + "translations": { + "en": "purge application" + }, + "description": { + "package": "pkg/ttnpb", + "file": "i18n.go" + } + }, "enum:RIGHT_APPLICATION_SETTINGS_API_KEYS": { "translations": { "en": "view and edit application API keys" @@ -683,6 +692,15 @@ "file": "i18n.go" } }, + "enum:RIGHT_CLIENT_PURGE": { + "translations": { + "en": "purge OAuth client" + }, + "description": { + "package": "pkg/ttnpb", + "file": "i18n.go" + } + }, "enum:RIGHT_CLIENT_SETTINGS_BASIC": { "translations": { "en": "edit OAuth client basic settings" @@ -746,6 +764,15 @@ "file": "i18n.go" } }, + "enum:RIGHT_GATEWAY_PURGE": { + "translations": { + "en": "purge gateway" + }, + "description": { + "package": "pkg/ttnpb", + "file": "i18n.go" + } + }, "enum:RIGHT_GATEWAY_READ_SECRETS": { "translations": { "en": "retrieve secrets associated with a gateway" @@ -908,6 +935,15 @@ "file": "i18n.go" } }, + "enum:RIGHT_ORGANIZATION_PURGE": { + "translations": { + "en": "purge organization" + }, + "description": { + "package": "pkg/ttnpb", + "file": "i18n.go" + } + }, "enum:RIGHT_ORGANIZATION_SETTINGS_API_KEYS": { "translations": { "en": "view and edit organization API keys" @@ -998,6 +1034,15 @@ "file": "i18n.go" } }, + "enum:RIGHT_USER_CREATE": { + "translations": { + "en": "create user account" + }, + "description": { + "package": "pkg/ttnpb", + "file": "i18n.go" + } + }, "enum:RIGHT_USER_DELETE": { "translations": { "en": "delete user account" @@ -1034,6 +1079,15 @@ "file": "i18n.go" } }, + "enum:RIGHT_USER_LIST": { + "translations": { + "en": "list user accounts" + }, + "description": { + "package": "pkg/ttnpb", + "file": "i18n.go" + } + }, "enum:RIGHT_USER_NOTIFICATIONS_READ": { "translations": { "en": "read user notifications" @@ -1061,6 +1115,15 @@ "file": "i18n.go" } }, + "enum:RIGHT_USER_PURGE": { + "translations": { + "en": "purge user account" + }, + "description": { + "package": "pkg/ttnpb", + "file": "i18n.go" + } + }, "enum:RIGHT_USER_SETTINGS_API_KEYS": { "translations": { "en": "view and edit user API keys" @@ -3230,6 +3293,15 @@ "file": "require.go" } }, + "error:pkg/auth/rights:insufficient_rights": { + "translations": { + "en": "insufficient rights for {entity_type} `{uid}`" + }, + "description": { + "package": "pkg/auth/rights", + "file": "require.go" + } + }, "error:pkg/auth/rights:insufficient_universal_rights": { "translations": { "en": "insufficient universal rights" @@ -4760,6 +4832,15 @@ "file": "pattern.go" } }, + "error:pkg/events:marshal_data": { + "translations": { + "en": "marshal data" + }, + "description": { + "package": "pkg/events", + "file": "events.go" + } + }, "error:pkg/events:no_matching_events": { "translations": { "en": "no matching events for regexp `{regexp}`" @@ -4949,6 +5030,15 @@ "file": "grpc.go" } }, + "error:pkg/gatewayconfigurationserver/managed:managed_gateway_configuration_server_not_enabled": { + "translations": { + "en": "managed gateway configuration server is not enabled" + }, + "description": { + "package": "pkg/gatewayconfigurationserver/managed", + "file": "grpc_disabled.go" + } + }, "error:pkg/gatewayconfigurationserver/managed:no_profile_name": { "translations": { "en": "no profile name set" @@ -8198,6 +8288,24 @@ "file": "errors.go" } }, + "error:pkg/networkserver:mac_settings_profile_already_exists": { + "translations": { + "en": "MAC settings profile already exists" + }, + "description": { + "package": "pkg/networkserver", + "file": "grpc_mac_settings_profile.go" + } + }, + "error:pkg/networkserver:mac_settings_profile_not_found": { + "translations": { + "en": "MAC settings profile not found" + }, + "description": { + "package": "pkg/networkserver", + "file": "grpc_mac_settings_profile.go" + } + }, "error:pkg/networkserver:no_downlink": { "translations": { "en": "no downlink to send" diff --git a/cypress/e2e/console/gateways/edit.spec.js b/cypress/e2e/console/gateways/edit.spec.js index e740ba249f..eff267504d 100644 --- a/cypress/e2e/console/gateways/edit.spec.js +++ b/cypress/e2e/console/gateways/edit.spec.js @@ -116,25 +116,17 @@ describe('Gateway general settings', () => { .should('be.visible') .and('have.attr', 'value', gateway.attributes.key) }) - cy.findByLabelText('Automatic updates').should('exist').and('have.attr', 'value', 'false') - cy.findDescriptionByLabelText('LoRa Basics Station LNS Authentication Key') - .should( - 'contain', - 'The Authentication Key for Lora Basics Station LNS connections. This field is ignored for other gateways.', - ) - .and('be.visible') - cy.findDescriptionByLabelText('Automatic updates') - .should('contain', 'Gateway can be updated automatically') - .and('be.visible') + cy.findDescriptionByLabelText('LoRa Basics Station LNS Authentication Key').should( + 'contain', + 'The Authentication Key for Lora Basics Station LNS connections. This field is ignored for other gateways.', + ) cy.findByLabelText('Channel') .should('be.visible') .and('have.attr', 'placeholder') .and('eq', 'Stable') - cy.findDescriptionByLabelText('Channel') - .should('contain', 'Channel for gateway automatic updates') - .and('be.visible') cy.findByRole('button', { name: 'Save changes' }).should('be.visible') cy.findByRole('button', { name: /Delete gateway/ }).should('be.visible') + cy.get('button[type="submit').scrollIntoView() cy.findByRole('heading', { name: 'LoRaWAN options' }).should('be.visible') cy.findByText('Frequency plan').should('not.exist') cy.findByRole('button', { name: 'Expand' }).click() @@ -177,7 +169,6 @@ describe('Gateway general settings', () => { cy.findByLabelText('Gateway location').check() cy.findByPlaceholderText('key').type('-changed') cy.findByPlaceholderText('value').type('-changed') - cy.findByLabelText('Automatic updates').check() cy.findByLabelText('Channel').type('test') cy.findByLabelText('Packet Broker').check() @@ -198,7 +189,6 @@ describe('Gateway general settings', () => { cy.findByLabelText('Gateway location').should('have.attr', 'value', 'true') cy.findByPlaceholderText('key').should('have.value', 'key-changed') cy.findByPlaceholderText('value').should('have.value', 'value-changed') - cy.findByLabelText('Automatic updates').should('have.attr', 'value', 'true') cy.findByLabelText('Channel').should('have.value', 'test') cy.findByLabelText('Packet Broker').should('have.attr', 'value', 'true') diff --git a/cypress/e2e/console/integrations/webhooks/edit.spec.js b/cypress/e2e/console/integrations/webhooks/edit.spec.js index ee203d919a..798cad2e23 100644 --- a/cypress/e2e/console/integrations/webhooks/edit.spec.js +++ b/cypress/e2e/console/integrations/webhooks/edit.spec.js @@ -180,6 +180,26 @@ describe('Application Webhook', () => { .and('eq', '1234QUERTY!') }) + it('succeeds pausing and activating webhook', () => { + cy.findByRole('button', { name: /Pause/ }).click() + + cy.findByTestId('modal-window') + .should('be.visible') + .within(() => { + cy.findByText('Pause webhook?', { selector: 'h1' }).should('be.visible') + cy.findByRole('button', { name: /Pause webhook/ }).click() + }) + cy.findByTestId('toast-notification-success').findByText('Webhook paused').should('be.visible') + + cy.findAllByRole('button', { name: /Activate/ }).should('have.length', 2) + cy.findByTestId('notification') + .should('exist') + .findByRole('button', { name: /Activate/ }) + .click() + + cy.findByTestId('toast-notification-success').findByText('Webhook active').should('be.visible') + }) + it('succeeds deleting webhook', () => { cy.findByRole('button', { name: /Delete Webhook/ }).click() diff --git a/go.mod b/go.mod index 87f715ad33..2f61931621 100644 --- a/go.mod +++ b/go.mod @@ -1,8 +1,6 @@ module go.thethings.network/lorawan-stack/v3 -go 1.22 - -toolchain go1.22.5 +go 1.23.3 // See https://github.com/mitchellh/mapstructure/pull/278 replace github.com/mitchellh/mapstructure => github.com/TheThingsIndustries/mapstructure v0.0.0-20230413130846-941bcd1deec3 @@ -20,13 +18,13 @@ require ( github.com/bluele/gcache v0.0.2 github.com/coder/websocket v1.8.12 github.com/disintegration/imaging v1.6.2 - github.com/dop251/goja v0.0.0-20231027120936-b396bb4c349d + github.com/dop251/goja v0.0.0-20241024094426-79f3a7efcdbd github.com/dustin/go-humanize v1.0.1 github.com/eclipse/paho.mqtt.golang v1.5.0 github.com/emersion/go-smtp v0.21.3 github.com/envoyproxy/protoc-gen-validate v1.1.0 github.com/felixge/httpsnoop v1.0.4 - github.com/getsentry/sentry-go v0.29.1 + github.com/getsentry/sentry-go v0.30.0 github.com/golang/gddo v0.0.0-20210115222349-20d68f94ee1f github.com/google/go-cmp v0.6.0 github.com/google/uuid v1.6.0 @@ -40,11 +38,11 @@ require ( github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 - github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 + github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0 github.com/hellofresh/health-go/v5 v5.5.3 github.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef github.com/iancoleman/strcase v0.3.0 - github.com/jackc/pgerrcode v0.0.0-20220416144525-469b46aa5efa + github.com/jackc/pgerrcode v0.0.0-20240316143900-6e2875d9b438 github.com/jackc/pgx/v5 v5.7.1 github.com/jacobsa/crypto v0.0.0-20190317225127-9f44e2d11115 github.com/jarcoal/httpmock v1.3.1 @@ -73,127 +71,138 @@ require ( github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.19.0 github.com/throttled/throttled/v2 v2.12.0 - github.com/uptrace/bun v1.2.5 - github.com/uptrace/bun/dialect/pgdialect v1.2.5 - github.com/uptrace/bun/driver/pgdriver v1.2.5 + github.com/uptrace/bun v1.2.6 + github.com/uptrace/bun/dialect/pgdialect v1.2.6 + github.com/uptrace/bun/driver/pgdriver v1.2.6 github.com/vmihailenco/msgpack/v5 v5.4.1 - go.opentelemetry.io/contrib/instrumentation/github.com/gorilla/mux/otelmux v0.56.0 - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.56.0 - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 - go.opentelemetry.io/otel v1.31.0 - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.31.0 - go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.31.0 - go.opentelemetry.io/otel/metric v1.31.0 - go.opentelemetry.io/otel/sdk v1.31.0 - go.opentelemetry.io/otel/trace v1.31.0 + go.opentelemetry.io/contrib/instrumentation/github.com/gorilla/mux/otelmux v0.57.0 + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.57.0 + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.57.0 + go.opentelemetry.io/otel v1.32.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.32.0 + go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.32.0 + go.opentelemetry.io/otel/metric v1.32.0 + go.opentelemetry.io/otel/sdk v1.32.0 + go.opentelemetry.io/otel/trace v1.32.0 go.packetbroker.org/api/iam v1.8.2 go.packetbroker.org/api/iam/v2 v2.9.1 go.packetbroker.org/api/mapping/v2 v2.3.2 go.packetbroker.org/api/routing v1.9.2 go.packetbroker.org/api/v3 v3.17.1 go.thethings.industries/pkg/api/gen/tti/gateway v0.0.0-20241023114011-31c9c9f86834 - go.thethings.industries/pkg/ca v0.0.0-20240809123127-21a24c0e47df + go.thethings.industries/pkg/ca v0.0.0-20241023114011-31c9c9f86834 go.thethings.network/lorawan-application-payload v0.0.0-20220125153912-1198ff1e403e go.thethings.network/lorawan-stack-legacy/v2 v2.1.0 go.uber.org/automaxprocs v1.6.0 go.uber.org/zap v1.27.0 gocloud.dev v0.40.0 gocloud.dev/pubsub/natspubsub v0.40.0 - golang.org/x/crypto v0.28.0 - golang.org/x/exp v0.0.0-20240119083558-1b970713d09a - golang.org/x/net v0.30.0 - golang.org/x/oauth2 v0.23.0 - golang.org/x/sync v0.8.0 - google.golang.org/genproto v0.0.0-20240812133136-8ffd90a71988 - google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9 - google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9 - google.golang.org/grpc v1.67.1 - google.golang.org/protobuf v1.35.1 + golang.org/x/crypto v0.30.0 + golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f + golang.org/x/net v0.32.0 + golang.org/x/oauth2 v0.24.0 + golang.org/x/sync v0.10.0 + google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 + google.golang.org/genproto/googleapis/api v0.0.0-20241118233622-e639e219e697 + google.golang.org/genproto/googleapis/rpc v0.0.0-20241118233622-e639e219e697 + google.golang.org/grpc v1.68.1 + google.golang.org/protobuf v1.35.2 gopkg.in/mail.v2 v2.3.1 gopkg.in/square/go-jose.v2 v2.6.0 gopkg.in/yaml.v2 v2.4.0 ) require ( - buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.34.2-20240508200655-46a4cf4ba109.2 // indirect - cloud.google.com/go v0.115.0 // indirect - cloud.google.com/go/auth v0.8.1 // indirect - cloud.google.com/go/auth/oauth2adapt v0.2.4 // indirect - cloud.google.com/go/compute/metadata v0.5.0 // indirect - cloud.google.com/go/iam v1.1.13 // indirect - cloud.google.com/go/pubsub v1.41.0 // indirect - cloud.google.com/go/storage v1.43.0 // indirect + buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.35.2-20240920164238-5a7b106cbb87.1 // indirect + cel.dev/expr v0.18.0 // indirect + cloud.google.com/go v0.116.0 // indirect + cloud.google.com/go/auth v0.11.0 // indirect + cloud.google.com/go/auth/oauth2adapt v0.2.6 // indirect + cloud.google.com/go/compute/metadata v0.5.2 // indirect + cloud.google.com/go/iam v1.2.2 // indirect + cloud.google.com/go/monitoring v1.21.2 // indirect + cloud.google.com/go/pubsub v1.45.1 // indirect + cloud.google.com/go/storage v1.47.0 // indirect dario.cat/mergo v1.0.1 // indirect - github.com/Azure/azure-sdk-for-go/sdk/azcore v1.14.0 // indirect - github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/azcore v1.16.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect - github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.3.2 // indirect + github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.5.0 // indirect github.com/Azure/go-autorest v14.2.0+incompatible // indirect github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect - github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 // indirect + github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2 // indirect + github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0 // indirect + github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.49.0 // indirect + github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.49.0 // indirect github.com/Masterminds/goutils v1.1.1 // indirect - github.com/Masterminds/semver/v3 v3.3.0 // indirect - github.com/RoaringBitmap/roaring v0.4.23 // indirect - github.com/aws/aws-sdk-go-v2 v1.30.3 // indirect - github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3 // indirect - github.com/aws/aws-sdk-go-v2/config v1.27.27 // indirect - github.com/aws/aws-sdk-go-v2/credentials v1.17.27 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11 // indirect - github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.10 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15 // indirect - github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 // indirect - github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.15 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.17 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.15 // indirect - github.com/aws/aws-sdk-go-v2/service/s3 v1.58.3 // indirect - github.com/aws/aws-sdk-go-v2/service/sns v1.31.3 // indirect - github.com/aws/aws-sdk-go-v2/service/sqs v1.34.3 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.22.4 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 // indirect - github.com/aws/smithy-go v1.20.3 // indirect + github.com/Masterminds/semver/v3 v3.3.1 // indirect + github.com/RoaringBitmap/roaring v1.9.4 // indirect + github.com/aws/aws-sdk-go-v2 v1.32.5 // indirect + github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7 // indirect + github.com/aws/aws-sdk-go-v2/config v1.28.5 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.17.46 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.20 // indirect + github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.41 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.24 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.24 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 // indirect + github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.24 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.5 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.5 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.5 // indirect + github.com/aws/aws-sdk-go-v2/service/s3 v1.69.0 // indirect + github.com/aws/aws-sdk-go-v2/service/sns v1.33.6 // indirect + github.com/aws/aws-sdk-go-v2/service/sqs v1.37.1 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.24.6 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.5 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.33.1 // indirect + github.com/aws/smithy-go v1.22.1 // indirect + github.com/bahlo/generic-list-go v0.2.0 // indirect github.com/beorn7/perks v1.0.1 // indirect + github.com/bits-and-blooms/bitset v1.17.0 // indirect github.com/blevesearch/go-porterstemmer v1.0.3 // indirect - github.com/blevesearch/mmap-go v1.0.2 // indirect - github.com/blevesearch/segment v0.9.0 // indirect + github.com/blevesearch/mmap-go v1.0.4 // indirect + github.com/blevesearch/segment v0.9.1 // indirect github.com/blevesearch/snowballstem v0.9.0 // indirect github.com/blevesearch/zap/v11 v11.0.14 // indirect github.com/blevesearch/zap/v12 v12.0.14 // indirect github.com/blevesearch/zap/v13 v13.0.6 // indirect github.com/blevesearch/zap/v14 v14.0.5 // indirect github.com/blevesearch/zap/v15 v15.0.3 // indirect + github.com/buger/jsonparser v1.1.1 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect + github.com/census-instrumentation/opencensus-proto v0.4.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/cilium/ebpf v0.9.1 // indirect - github.com/containerd/cgroups/v3 v3.0.1 // indirect + github.com/cilium/ebpf v0.16.0 // indirect + github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78 // indirect + github.com/containerd/cgroups/v3 v3.0.4 // indirect + github.com/containerd/log v0.1.0 // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect github.com/couchbase/vellum v1.0.2 // indirect - github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect - github.com/dlclark/regexp2 v1.7.0 // indirect - github.com/docker/go-units v0.4.0 // indirect + github.com/dlclark/regexp2 v1.11.4 // indirect + github.com/docker/go-units v0.5.0 // indirect github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21 // indirect - github.com/fsnotify/fsnotify v1.7.0 // indirect - github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2 // indirect + github.com/envoyproxy/go-control-plane v0.13.1 // indirect + github.com/fsnotify/fsnotify v1.8.0 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect - github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect - github.com/godbus/dbus/v5 v5.0.4 // indirect + github.com/go-sourcemap/sourcemap v2.1.4+incompatible // indirect + github.com/godbus/dbus/v5 v5.1.0 // indirect github.com/golang-jwt/jwt/v5 v5.2.1 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/golang/snappy v0.0.4 // indirect - github.com/google/pprof v0.0.0-20230207041349-798e818bf904 // indirect + github.com/google/pprof v0.0.0-20241122213907-cbe949e5a41b // indirect github.com/google/s2a-go v0.1.8 // indirect github.com/google/wire v0.6.0 // indirect - github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect - github.com/googleapis/gax-go/v2 v2.13.0 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect + github.com/googleapis/gax-go/v2 v2.14.0 // indirect github.com/gotnospirit/makeplural v0.0.0-20180622080156-a5f48d94d976 // indirect github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 // indirect - github.com/hashicorp/golang-lru v0.5.4 // indirect + github.com/hashicorp/golang-lru v1.0.2 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/huandu/xstrings v1.5.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect @@ -209,10 +218,12 @@ require ( github.com/kr/text v0.2.0 // indirect github.com/kylelemons/godebug v1.1.0 // indirect github.com/magiconair/properties v1.8.7 // indirect - github.com/mattn/go-runewidth v0.0.15 // indirect + github.com/mailru/easyjson v0.7.7 // indirect + github.com/mattn/go-runewidth v0.0.16 // indirect github.com/minio/highwayhash v1.0.3 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect + github.com/moby/sys/userns v0.1.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/mschoch/smat v0.2.0 // indirect @@ -221,19 +232,19 @@ require ( github.com/nats-io/nkeys v0.4.7 // indirect github.com/nats-io/nuid v1.0.1 // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect - github.com/opencontainers/runtime-spec v1.0.2 // indirect + github.com/opencontainers/runtime-spec v1.2.0 // indirect github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect - github.com/pborman/uuid v1.2.0 // indirect - github.com/pelletier/go-toml/v2 v2.2.2 // indirect - github.com/philhofer/fwd v1.0.0 // indirect + github.com/pborman/uuid v1.2.1 // indirect + github.com/pelletier/go-toml/v2 v2.2.3 // indirect + github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect github.com/prometheus/client_model v0.6.1 // indirect - github.com/prometheus/common v0.55.0 // indirect + github.com/prometheus/common v0.60.1 // indirect github.com/prometheus/procfs v0.15.1 // indirect github.com/puzpuzpuz/xsync/v3 v3.4.0 // indirect - github.com/rivo/uniseg v0.4.4 // indirect + github.com/rivo/uniseg v0.4.7 // indirect github.com/rogpeppe/go-internal v1.13.1 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect - github.com/sagikazarmark/locafero v0.4.0 // indirect + github.com/sagikazarmark/locafero v0.6.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect github.com/sendgrid/rest v2.6.9+incompatible // indirect github.com/shopspring/decimal v1.4.0 // indirect @@ -243,22 +254,25 @@ require ( github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect github.com/steveyen/gtreap v0.1.0 // indirect github.com/subosito/gotenv v1.6.0 // indirect - github.com/tinylib/msgp v1.1.0 // indirect github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect - github.com/willf/bitset v1.1.10 // indirect - go.etcd.io/bbolt v1.3.8 // indirect + github.com/willf/bitset v1.1.11 // indirect + github.com/wk8/go-ordered-map/v2 v2.1.9-0.20240816141633-0a40785b4f41 // indirect + go.etcd.io/bbolt v1.3.11 // indirect go.opencensus.io v0.24.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.31.0 // indirect + go.opentelemetry.io/contrib/detectors/gcp v1.32.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.32.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.32.0 // indirect go.opentelemetry.io/proto/otlp v1.3.1 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/image v0.14.0 // indirect - golang.org/x/sys v0.26.0 // indirect - golang.org/x/term v0.25.0 // indirect - golang.org/x/text v0.19.0 // indirect - golang.org/x/time v0.7.0 // indirect - golang.org/x/xerrors v0.0.0-20240716161551-93cc26a95ae9 // indirect - google.golang.org/api v0.191.0 // indirect + golang.org/x/image v0.22.0 // indirect + golang.org/x/sys v0.28.0 // indirect + golang.org/x/term v0.27.0 // indirect + golang.org/x/text v0.21.0 // indirect + golang.org/x/time v0.8.0 // indirect + golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect + google.golang.org/api v0.209.0 // indirect + google.golang.org/grpc/stats/opentelemetry v0.0.0-20241028142157-ada6787961b3 // indirect gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index e7e3d26513..5188ae82f4 100644 --- a/go.sum +++ b/go.sum @@ -1,53 +1,74 @@ -buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.34.2-20240508200655-46a4cf4ba109.2 h1:cFrEG/pJch6t62+jqndcPXeTNkYcztS4tBRgNkR+drw= -buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.34.2-20240508200655-46a4cf4ba109.2/go.mod h1:ylS4c28ACSI59oJrOdW4pHS4n0Hw4TgSPHn8rpHl4Yw= +buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.35.2-20240920164238-5a7b106cbb87.1 h1:7QIeAuTdLp173vC/9JojRMDFcpmqtoYrxPmvdHAOynw= +buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.35.2-20240920164238-5a7b106cbb87.1/go.mod h1:mnHCFccv4HwuIAOHNGdiIc5ZYbBCvbTWZcodLN5wITI= +cel.dev/expr v0.18.0 h1:CJ6drgk+Hf96lkLikr4rFf19WrU0BOWEihyZnI2TAzo= +cel.dev/expr v0.18.0/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw= cloud.google.com/go v0.16.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.115.0 h1:CnFSK6Xo3lDYRoBKEcAtia6VSC837/ZkJuRduSFnr14= -cloud.google.com/go v0.115.0/go.mod h1:8jIM5vVgoAEoiVxQ/O4BFTfHqulPZgs/ufEzMcFMdWU= -cloud.google.com/go/auth v0.8.1 h1:QZW9FjC5lZzN864p13YxvAtGUlQ+KgRL+8Sg45Z6vxo= -cloud.google.com/go/auth v0.8.1/go.mod h1:qGVp/Y3kDRSDZ5gFD/XPUfYQ9xW1iI7q8RIRoCyBbJc= -cloud.google.com/go/auth/oauth2adapt v0.2.4 h1:0GWE/FUsXhf6C+jAkWgYm7X9tK8cuEIfy19DBn6B6bY= -cloud.google.com/go/auth/oauth2adapt v0.2.4/go.mod h1:jC/jOpwFP6JBxhB3P5Rr0a9HLMC/Pe3eaL4NmdvqPtc= -cloud.google.com/go/compute/metadata v0.5.0 h1:Zr0eK8JbFv6+Wi4ilXAR8FJ3wyNdpxHKJNPos6LTZOY= -cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykWvVCfu7hQbF+9CWoiY= -cloud.google.com/go/iam v1.1.13 h1:7zWBXG9ERbMLrzQBRhFliAV+kjcRToDTgQT3CTwYyv4= -cloud.google.com/go/iam v1.1.13/go.mod h1:K8mY0uSXwEXS30KrnVb+j54LB/ntfZu1dr+4zFMNbus= -cloud.google.com/go/longrunning v0.5.12 h1:5LqSIdERr71CqfUsFlJdBpOkBH8FBCFD7P1nTWy3TYE= -cloud.google.com/go/longrunning v0.5.12/go.mod h1:S5hMV8CDJ6r50t2ubVJSKQVv5u0rmik5//KgLO3k4lU= -cloud.google.com/go/pubsub v1.41.0 h1:ZPaM/CvTO6T+1tQOs/jJ4OEMpjtel0PTLV7j1JK+ZrI= -cloud.google.com/go/pubsub v1.41.0/go.mod h1:g+YzC6w/3N91tzG66e2BZtp7WrpBBMXVa3Y9zVoOGpk= -cloud.google.com/go/storage v1.43.0 h1:CcxnSohZwizt4LCzQHWvBf1/kvtHUn7gk9QERXPyXFs= -cloud.google.com/go/storage v1.43.0/go.mod h1:ajvxEa7WmZS1PxvKRq4bq0tFT3vMd502JwstCcYv0Q0= +cloud.google.com/go v0.116.0 h1:B3fRrSDkLRt5qSHWe40ERJvhvnQwdZiHu0bJOpldweE= +cloud.google.com/go v0.116.0/go.mod h1:cEPSRWPzZEswwdr9BxE6ChEn01dWlTaF05LiC2Xs70U= +cloud.google.com/go/auth v0.11.0 h1:Ic5SZz2lsvbYcWT5dfjNWgw6tTlGi2Wc8hyQSC9BstA= +cloud.google.com/go/auth v0.11.0/go.mod h1:xxA5AqpDrvS+Gkmo9RqrGGRh6WSNKKOXhY3zNOr38tI= +cloud.google.com/go/auth/oauth2adapt v0.2.6 h1:V6a6XDu2lTwPZWOawrAa9HUK+DB2zfJyTuciBG5hFkU= +cloud.google.com/go/auth/oauth2adapt v0.2.6/go.mod h1:AlmsELtlEBnaNTL7jCj8VQFLy6mbZv0s4Q7NGBeQ5E8= +cloud.google.com/go/compute/metadata v0.5.2 h1:UxK4uu/Tn+I3p2dYWTfiX4wva7aYlKixAHn3fyqngqo= +cloud.google.com/go/compute/metadata v0.5.2/go.mod h1:C66sj2AluDcIqakBq/M8lw8/ybHgOZqin2obFxa/E5k= +cloud.google.com/go/iam v1.2.2 h1:ozUSofHUGf/F4tCNy/mu9tHLTaxZFLOUiKzjcgWHGIA= +cloud.google.com/go/iam v1.2.2/go.mod h1:0Ys8ccaZHdI1dEUilwzqng/6ps2YB6vRsjIe00/+6JY= +cloud.google.com/go/logging v1.12.0 h1:ex1igYcGFd4S/RZWOCU51StlIEuey5bjqwH9ZYjHibk= +cloud.google.com/go/logging v1.12.0/go.mod h1:wwYBt5HlYP1InnrtYI0wtwttpVU1rifnMT7RejksUAM= +cloud.google.com/go/longrunning v0.6.2 h1:xjDfh1pQcWPEvnfjZmwjKQEcHnpz6lHjfy7Fo0MK+hc= +cloud.google.com/go/longrunning v0.6.2/go.mod h1:k/vIs83RN4bE3YCswdXC5PFfWVILjm3hpEUlSko4PiI= +cloud.google.com/go/monitoring v1.21.2 h1:FChwVtClH19E7pJ+e0xUhJPGksctZNVOk2UhMmblmdU= +cloud.google.com/go/monitoring v1.21.2/go.mod h1:hS3pXvaG8KgWTSz+dAdyzPrGUYmi2Q+WFX8g2hqVEZU= +cloud.google.com/go/pubsub v1.45.1 h1:ZC/UzYcrmK12THWn1P72z+Pnp2vu/zCZRXyhAfP1hJY= +cloud.google.com/go/pubsub v1.45.1/go.mod h1:3bn7fTmzZFwaUjllitv1WlsNMkqBgGUb3UdMhI54eCc= +cloud.google.com/go/storage v1.47.0 h1:ajqgt30fnOMmLfWfu1PWcb+V9Dxz6n+9WKjdNg5R4HM= +cloud.google.com/go/storage v1.47.0/go.mod h1:Ks0vP374w0PW6jOUameJbapbQKXqkjGd/OJRp2fb9IQ= +cloud.google.com/go/trace v1.11.2 h1:4ZmaBdL8Ng/ajrgKqY5jfvzqMXbrDcBsUGXOT9aqTtI= +cloud.google.com/go/trace v1.11.2/go.mod h1:bn7OwXd4pd5rFuAnTrzBuoZ4ax2XQeG3qNgYmfCy0Io= dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.14.0 h1:nyQWyZvwGTvunIMxi1Y9uXkcyr+I7TeNrr/foo4Kpk8= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.14.0/go.mod h1:l38EPgmsp71HHLq9j7De57JcKOWPyhrsW1Awm1JS6K0= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0 h1:tfLQ34V6F7tVSwoTf/4lH5sE0o6eCJuNDTmH09nDpbc= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0/go.mod h1:9kIvujWAA58nmPmWB1m23fyWic1kYZMxD9CxaWn4Qpg= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.16.0 h1:JZg6HRh6W6U4OLl6lk7BZ7BLisIzM9dG1R50zUk9C/M= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.16.0/go.mod h1:YL1xnZ6QejvQHWJrX/AvhFl4WW4rqHVoKspWNVwFk0M= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.0 h1:B/dfvscEQtew9dVuoxqxrUKKv8Ih2f55PydknDamU+g= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.0/go.mod h1:fiPSssYvltE08HJchL04dOy+RD4hgrjph0cwGGMntdI= +github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.0 h1:+m0M/LFxN43KvULkDNfdXOgrjtg6UYJPFBJyuEcRCAw= +github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.0/go.mod h1:PwOyop78lveYMRs6oCxjiVyBdyCgIYH6XHIVZO9/SFQ= github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 h1:ywEEhmNahHBihViHepv3xPBn1663uRv2t2q/ESv9seY= github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0/go.mod h1:iZDifYGJTIgIIkYRNWPENUnqx6bJ2xnSDFI2tjwZNuY= -github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.5.0 h1:AifHbc4mg0x9zW52WOpKbsHaDKuRhlI7TVl47thgQ70= -github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.5.0/go.mod h1:T5RfihdXtBDxt1Ch2wobif3TvzTdumDy29kahv6AV9A= -github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.3.2 h1:YUUxeiOWgdAQE3pXt2H7QXzZs0q8UBjgRbl56qo8GYM= -github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.3.2/go.mod h1:dmXQgZuiSubAecswZE+Sm8jkvEa7kQgTPVRvwL/nd0E= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.6.0 h1:PiSrjRPpkQNjrM8H0WwKMnZUdu1RGMtd/LdGKUrOo+c= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.6.0/go.mod h1:oDrbWx4ewMylP7xHivfgixbfGBT6APAwsSoHRKotnIc= +github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.5.0 h1:mlmW46Q0B79I+Aj4azKC6xDMFN9a9SyZWESlGWYXbFs= +github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.5.0/go.mod h1:PXe2h+LKcWTX9afWdZoHyODqR4fBa5boUM/8uJfZ0Jo= github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/Azure/go-autorest/autorest/to v0.4.0 h1:oXVqrxakqqV1UZdSazDOPOLvOIz+XA683u8EctwboHk= github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE= -github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 h1:XHOnouVk1mxXfQidrMEnLlPk9UMeRtyBTnEFtxkV0kU= -github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= +github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJTmL004Abzc5wDB5VtZG2PJk5ndYDgVacGqfirKxjM= +github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE= +github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2 h1:kYRSnvJju5gYVyhkij+RTJ/VR6QIUaCfWeaFm2ycsjQ= +github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0 h1:3c8yed4lgqTt+oTQ+JNMDo+F4xprBf+O/il4ZC0nRLw= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0/go.mod h1:obipzmGjfSjam60XLwGfqUkJsfiheAl+TUjG+4yzyPM= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.49.0 h1:o90wcURuxekmXrtxmYWTyNla0+ZEHhud6DI1ZTxd1vI= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.49.0/go.mod h1:6fTWu4m3jocfUZLYF5KsZC1TUfRvEjs7lM4crme/irw= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.49.0 h1:jJKWl98inONJAr/IZrdFQUWcwUO95DLY1XMD1ZIut+g= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.49.0/go.mod h1:l2fIqmwB+FKSfvn3bAD/0i+AXAxhIZjTK2svT/mgUXs= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.49.0 h1:GYUJLfvd++4DMuMhCFLgLXvFwofIxh/qOwoGuS/LTew= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.49.0/go.mod h1:wRbFgBQUVm1YXrvWKofAEmq9HNJTDphbAaJSSX01KUI= github.com/KimMachineGun/automemlimit v0.6.1 h1:ILa9j1onAAMadBsyyUJv5cack8Y1WT26yLj/V+ulKp8= github.com/KimMachineGun/automemlimit v0.6.1/go.mod h1:T7xYht7B8r6AG/AqFcUdc7fzd2bIdBKmepfP2S1svPY= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= -github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0= -github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= +github.com/Masterminds/semver/v3 v3.3.1 h1:QtNSWtVZ3nBfk8mAOu/B6v7FMJ+NHTIgUPi7rj+4nv4= +github.com/Masterminds/semver/v3 v3.3.1/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs= github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0= -github.com/RoaringBitmap/roaring v0.4.23 h1:gpyfd12QohbqhFO4NVDUdoPOCXsyahYRQhINmlHxKeo= github.com/RoaringBitmap/roaring v0.4.23/go.mod h1:D0gp8kJQgE1A4LQ5wFLggQEyvDi06Mq5mKs52e1TwOo= +github.com/RoaringBitmap/roaring v1.9.4 h1:yhEIoH4YezLYT04s1nHehNO64EKFTop/wBhxv2QzDdQ= +github.com/RoaringBitmap/roaring v1.9.4/go.mod h1:6AXUsoIEzDTFFQCe1RbGA6uFONMhvejWj5rqITANK90= github.com/TheThingsIndustries/mapstructure v0.0.0-20230413130846-941bcd1deec3 h1:8fBjDLymZqKpajlAFg98PmzEHhnPIphnkOCTiZyTnF8= github.com/TheThingsIndustries/mapstructure v0.0.0-20230413130846-941bcd1deec3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/TheThingsIndustries/mystique v0.0.0-20221125120501-80ab21781b6d h1:ZlvidrSPuNA6BWhj/K4/3UJwvckLbqAqn6PNyvurA34= @@ -61,51 +82,56 @@ github.com/TheThingsNetwork/go-cayenne-lib v1.2.0/go.mod h1:HTTus7UIBhXKvLIeNGyb github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU= github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= -github.com/aws/aws-sdk-go-v2 v1.30.3 h1:jUeBtG0Ih+ZIFH0F4UkmL9w3cSpaMv9tYYDbzILP8dY= -github.com/aws/aws-sdk-go-v2 v1.30.3/go.mod h1:nIQjQVp5sfpQcTc9mPSr1B0PaWK5ByX9MOoDadSN4lc= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3 h1:tW1/Rkad38LA15X4UQtjXZXNKsCgkshC3EbmcUmghTg= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3/go.mod h1:UbnqO+zjqk3uIt9yCACHJ9IVNhyhOCnYk8yA19SAWrM= -github.com/aws/aws-sdk-go-v2/config v1.27.27 h1:HdqgGt1OAP0HkEDDShEl0oSYa9ZZBSOmKpdpsDMdO90= -github.com/aws/aws-sdk-go-v2/config v1.27.27/go.mod h1:MVYamCg76dFNINkZFu4n4RjDixhVr51HLj4ErWzrVwg= -github.com/aws/aws-sdk-go-v2/credentials v1.17.27 h1:2raNba6gr2IfA0eqqiP2XiQ0UVOpGPgDSi0I9iAP+UI= -github.com/aws/aws-sdk-go-v2/credentials v1.17.27/go.mod h1:gniiwbGahQByxan6YjQUMcW4Aov6bLC3m+evgcoN4r4= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11 h1:KreluoV8FZDEtI6Co2xuNk/UqI9iwMrOx/87PBNIKqw= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11/go.mod h1:SeSUYBLsMYFoRvHE0Tjvn7kbxaUhl75CJi1sbfhMxkU= -github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.10 h1:zeN9UtUlA6FTx0vFSayxSX32HDw73Yb6Hh2izDSFxXY= -github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.10/go.mod h1:3HKuexPDcwLWPaqpW2UR/9n8N/u/3CKcGAzSs8p8u8g= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15 h1:SoNJ4RlFEQEbtDcCEt+QG56MY4fm4W8rYirAmq+/DdU= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15/go.mod h1:U9ke74k1n2bf+RIgoX1SXFed1HLs51OgUSs+Ph0KJP8= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15 h1:C6WHdGnTDIYETAm5iErQUiVNsclNx9qbJVPIt03B6bI= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15/go.mod h1:ZQLZqhcu+JhSrA9/NXRm8SkDvsycE+JkV3WGY41e+IM= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 h1:hT8rVHwugYE2lEfdFE0QWVo81lF7jMrYJVDWI+f+VxU= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0/go.mod h1:8tu/lYfQfFe6IGnaOdrpVgEL2IrrDOf6/m9RQum4NkY= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.15 h1:Z5r7SycxmSllHYmaAZPpmN8GviDrSGhMS6bldqtXZPw= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.15/go.mod h1:CetW7bDE00QoGEmPUoZuRog07SGVAUVW6LFpNP0YfIg= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3 h1:dT3MqvGhSoaIhRseqw2I0yH81l7wiR2vjs57O51EAm8= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3/go.mod h1:GlAeCkHwugxdHaueRr4nhPuY+WW+gR8UjlcqzPr1SPI= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.17 h1:YPYe6ZmvUfDDDELqEKtAd6bo8zxhkm+XEFEzQisqUIE= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.17/go.mod h1:oBtcnYua/CgzCWYN7NZ5j7PotFDaFSUjCYVTtfyn7vw= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17 h1:HGErhhrxZlQ044RiM+WdoZxp0p+EGM62y3L6pwA4olE= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17/go.mod h1:RkZEx4l0EHYDJpWppMJ3nD9wZJAa8/0lq9aVC+r2UII= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.15 h1:246A4lSTXWJw/rmlQI+TT2OcqeDMKBdyjEQrafMaQdA= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.15/go.mod h1:haVfg3761/WF7YPuJOER2MP0k4UAXyHaLclKXB6usDg= -github.com/aws/aws-sdk-go-v2/service/s3 v1.58.3 h1:hT8ZAZRIfqBqHbzKTII+CIiY8G2oC9OpLedkZ51DWl8= -github.com/aws/aws-sdk-go-v2/service/s3 v1.58.3/go.mod h1:Lcxzg5rojyVPU/0eFwLtcyTaek/6Mtic5B1gJo7e/zE= -github.com/aws/aws-sdk-go-v2/service/sns v1.31.3 h1:eSTEdxkfle2G98FE+Xl3db/XAXXVTJPNQo9K/Ar8oAI= -github.com/aws/aws-sdk-go-v2/service/sns v1.31.3/go.mod h1:1dn0delSO3J69THuty5iwP0US2Glt0mx2qBBlI13pvw= -github.com/aws/aws-sdk-go-v2/service/sqs v1.34.3 h1:Vjqy5BZCOIsn4Pj8xzyqgGmsSqzz7y/WXbN3RgOoVrc= -github.com/aws/aws-sdk-go-v2/service/sqs v1.34.3/go.mod h1:L0enV3GCRd5iG9B64W35C4/hwsCB00Ib+DKVGTadKHI= -github.com/aws/aws-sdk-go-v2/service/sso v1.22.4 h1:BXx0ZIxvrJdSgSvKTZ+yRBeSqqgPM89VPlulEcl37tM= -github.com/aws/aws-sdk-go-v2/service/sso v1.22.4/go.mod h1:ooyCOXjvJEsUw7x+ZDHeISPMhtwI3ZCB7ggFMcFfWLU= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4 h1:yiwVzJW2ZxZTurVbYWA7QOrAaCYQR72t0wrSBfoesUE= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4/go.mod h1:0oxfLkpz3rQ/CHlx5hB7H69YUpFiI1tql6Q6Ne+1bCw= -github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 h1:ZsDKRLXGWHk8WdtyYMoGNO7bTudrvuKpDKgMVRlepGE= -github.com/aws/aws-sdk-go-v2/service/sts v1.30.3/go.mod h1:zwySh8fpFyXp9yOr/KVzxOl8SRqgf/IDw5aUt9UKFcQ= -github.com/aws/smithy-go v1.20.3 h1:ryHwveWzPV5BIof6fyDvor6V3iUL7nTfiTKXHiW05nE= -github.com/aws/smithy-go v1.20.3/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E= +github.com/aws/aws-sdk-go-v2 v1.32.5 h1:U8vdWJuY7ruAkzaOdD7guwJjD06YSKmnKCJs7s3IkIo= +github.com/aws/aws-sdk-go-v2 v1.32.5/go.mod h1:P5WJBrYqqbWVaOxgH0X/FYYD47/nooaPOZPlQdmiN2U= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7 h1:lL7IfaFzngfx0ZwUGOZdsFFnQ5uLvR0hWqqhyE7Q9M8= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7/go.mod h1:QraP0UcVlQJsmHfioCrveWOC1nbiWUl3ej08h4mXWoc= +github.com/aws/aws-sdk-go-v2/config v1.28.5 h1:Za41twdCXbuyyWv9LndXxZZv3QhTG1DinqlFsSuvtI0= +github.com/aws/aws-sdk-go-v2/config v1.28.5/go.mod h1:4VsPbHP8JdcdUDmbTVgNL/8w9SqOkM5jyY8ljIxLO3o= +github.com/aws/aws-sdk-go-v2/credentials v1.17.46 h1:AU7RcriIo2lXjUfHFnFKYsLCwgbz1E7Mm95ieIRDNUg= +github.com/aws/aws-sdk-go-v2/credentials v1.17.46/go.mod h1:1FmYyLGL08KQXQ6mcTlifyFXfJVCNJTVGuQP4m0d/UA= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.20 h1:sDSXIrlsFSFJtWKLQS4PUWRvrT580rrnuLydJrCQ/yA= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.20/go.mod h1:WZ/c+w0ofps+/OUqMwWgnfrgzZH1DZO1RIkktICsqnY= +github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.41 h1:hqcxMc2g/MwwnRMod9n6Bd+t+9Nf7d5qRg7RaXKPd6o= +github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.41/go.mod h1:d1eH0VrttvPmrCraU68LOyNdu26zFxQFjrVSb5vdhog= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.24 h1:4usbeaes3yJnCFC7kfeyhkdkPtoRYPa/hTmCqMpKpLI= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.24/go.mod h1:5CI1JemjVwde8m2WG3cz23qHKPOxbpkq0HaoreEgLIY= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.24 h1:N1zsICrQglfzaBnrfM0Ys00860C+QFwu6u/5+LomP+o= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.24/go.mod h1:dCn9HbJ8+K31i8IQ8EWmWj0EiIk0+vKiHNMxTTYveAg= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 h1:VaRN3TlFdd6KxX1x3ILT5ynH6HvKgqdiXoTxAF4HQcQ= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.24 h1:JX70yGKLj25+lMC5Yyh8wBtvB01GDilyRuJvXJ4piD0= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.24/go.mod h1:+Ln60j9SUTD0LEwnhEB0Xhg61DHqplBrbZpLgyjoEHg= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 h1:iXtILhvDxB6kPvEXgsDhGaZCSC6LQET5ZHSdJozeI0Y= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1/go.mod h1:9nu0fVANtYiAePIBh2/pFUSwtJ402hLnp854CNoDOeE= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.5 h1:gvZOjQKPxFXy1ft3QnEyXmT+IqneM9QAUWlM3r0mfqw= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.5/go.mod h1:DLWnfvIcm9IET/mmjdxeXbBKmTCm0ZB8p1za9BVteM8= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.5 h1:wtpJ4zcwrSbwhECWQoI/g6WM9zqCcSpHDJIWSbMLOu4= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.5/go.mod h1:qu/W9HXQbbQ4+1+JcZp0ZNPV31ym537ZJN+fiS7Ti8E= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.5 h1:P1doBzv5VEg1ONxnJss1Kh5ZG/ewoIE4MQtKKc6Crgg= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.5/go.mod h1:NOP+euMW7W3Ukt28tAxPuoWao4rhhqJD3QEBk7oCg7w= +github.com/aws/aws-sdk-go-v2/service/s3 v1.69.0 h1:Q2ax8S21clKOnHhhr933xm3JxdJebql+R7aNo7p7GBQ= +github.com/aws/aws-sdk-go-v2/service/s3 v1.69.0/go.mod h1:ralv4XawHjEMaHOWnTFushl0WRqim/gQWesAMF6hTow= +github.com/aws/aws-sdk-go-v2/service/sns v1.33.6 h1:lEUtRHICiXsd7VRwRjXaY7MApT2X4Ue0Mrwe6XbyBro= +github.com/aws/aws-sdk-go-v2/service/sns v1.33.6/go.mod h1:SODr0Lu3lFdT0SGsGX1TzFTapwveBrT5wztVoYtppm8= +github.com/aws/aws-sdk-go-v2/service/sqs v1.37.1 h1:39WvSrVq9DD6UHkD+fx5x19P5KpRQfNdtgReDVNbelc= +github.com/aws/aws-sdk-go-v2/service/sqs v1.37.1/go.mod h1:3gwPzC9LER/BTQdQZ3r6dUktb1rSjABF1D3Sr6nS7VU= +github.com/aws/aws-sdk-go-v2/service/sso v1.24.6 h1:3zu537oLmsPfDMyjnUS2g+F2vITgy5pB74tHI+JBNoM= +github.com/aws/aws-sdk-go-v2/service/sso v1.24.6/go.mod h1:WJSZH2ZvepM6t6jwu4w/Z45Eoi75lPN7DcydSRtJg6Y= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.5 h1:K0OQAsDywb0ltlFrZm0JHPY3yZp/S9OaoLU33S7vPS8= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.5/go.mod h1:ORITg+fyuMoeiQFiVGoqB3OydVTLkClw/ljbblMq6Cc= +github.com/aws/aws-sdk-go-v2/service/sts v1.33.1 h1:6SZUVRQNvExYlMLbHdlKB48x0fLbc2iVROyaNEwBHbU= +github.com/aws/aws-sdk-go-v2/service/sts v1.33.1/go.mod h1:GqWyYCwLXnlUB1lOAXQyNSPqPLQJvmo8J0DWBzp9mtg= +github.com/aws/smithy-go v1.22.1 h1:/HPHZQ0g7f4eUeK6HKglFz8uwVfZKgoI25rb/J+dnro= +github.com/aws/smithy-go v1.22.1/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= +github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk= +github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bits-and-blooms/bitset v1.12.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= +github.com/bits-and-blooms/bitset v1.17.0 h1:1X2TS7aHz1ELcC0yU1y2stUs/0ig5oMU6STFZGrhvHI= +github.com/bits-and-blooms/bitset v1.17.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/blevesearch/bleve v1.0.14 h1:Q8r+fHTt35jtGXJUM0ULwM3Tzg+MRfyai4ZkWDy2xO4= @@ -115,10 +141,12 @@ github.com/blevesearch/blevex v1.0.0/go.mod h1:2rNVqoG2BZI8t1/P1awgTKnGlx5MP9Zbt github.com/blevesearch/cld2 v0.0.0-20200327141045-8b5f551d37f5/go.mod h1:PN0QNTLs9+j1bKy3d/GB/59wsNBFC4sWLWG3k69lWbc= github.com/blevesearch/go-porterstemmer v1.0.3 h1:GtmsqID0aZdCSNiY8SkuPJ12pD4jI+DdXTAn4YRcHCo= github.com/blevesearch/go-porterstemmer v1.0.3/go.mod h1:angGc5Ht+k2xhJdZi511LtmxuEf0OVpvUUNrwmM1P7M= -github.com/blevesearch/mmap-go v1.0.2 h1:JtMHb+FgQCTTYIhtMvimw15dJwu1Y5lrZDMOFXVWPk0= github.com/blevesearch/mmap-go v1.0.2/go.mod h1:ol2qBqYaOUsGdm7aRMRrYGgPvnwLe6Y+7LMvAB5IbSA= -github.com/blevesearch/segment v0.9.0 h1:5lG7yBCx98or7gK2cHMKPukPZ/31Kag7nONpoBt22Ac= +github.com/blevesearch/mmap-go v1.0.4 h1:OVhDhT5B/M1HNPpYPBKIEJaD0F3Si+CrEKULGCDPWmc= +github.com/blevesearch/mmap-go v1.0.4/go.mod h1:EWmEAOmdAS9z/pi/+Toxu99DnsbhG1TIxUoRmJw/pSs= github.com/blevesearch/segment v0.9.0/go.mod h1:9PfHYUdQCgHktBgvtUOF4x+pc4/l8rdH0u5spnW85UQ= +github.com/blevesearch/segment v0.9.1 h1:+dThDy+Lvgj5JMxhmOVlgFfkUtZV2kw49xax4+jTfSU= +github.com/blevesearch/segment v0.9.1/go.mod h1:zN21iLm7+GnBHWTao9I+Au/7MBiL8pPFtJBJTsk6kQw= github.com/blevesearch/snowballstem v0.9.0 h1:lMQ189YspGP6sXvZQ4WZ+MLawfV8wOmPoD/iWeNXm8s= github.com/blevesearch/snowballstem v0.9.0/go.mod h1:PivSj3JMc8WuaFkTSRDW2SlrulNWPl4ABg1tC/hlgLs= github.com/blevesearch/zap/v11 v11.0.14 h1:IrDAvtlzDylh6H2QCmS0OGcN9Hpf6mISJlfKjcwJs7k= @@ -140,24 +168,29 @@ github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdb github.com/bsm/gomega v1.26.0/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= +github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs= +github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/census-instrumentation/opencensus-proto v0.4.1 h1:iKLQ0xPNFxR/2hzXZMrBo8f1j86j5WHzznCCQxV/b8g= +github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/chzyer/logex v1.2.0/go.mod h1:9+9sk7u7pGNWYMkh0hdiL++6OeibzJccyQU4p4MedaY= -github.com/chzyer/readline v1.5.0/go.mod h1:x22KAscuvRqlLoK9CsoYsmxoXZMMFVyOl86cAH8qUic= -github.com/chzyer/test v0.0.0-20210722231415-061457976a23/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/cilium/ebpf v0.9.1 h1:64sn2K3UKw8NbP/blsixRpF3nXuyhz/VjRlRzvlBRu4= -github.com/cilium/ebpf v0.9.1/go.mod h1:+OhNOIXx/Fnu1IE8bJz2dzOA+VSfyTfdNUVdlQnxUFY= +github.com/cilium/ebpf v0.16.0 h1:+BiEnHL6Z7lXnlGUsXQPPAE7+kenAd4ES8MQ5min0Ok= +github.com/cilium/ebpf v0.16.0/go.mod h1:L7u2Blt2jMM/vLAVgjxluxtBKlz3/GWjB0dMOEngfwE= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78 h1:QVw89YDxXxEe+l8gU8ETbOasdwEV+avkR75ZzsVV9WI= +github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= github.com/coder/websocket v1.8.12 h1:5bUXkEPPIbewrnkU8LTCLVaxi4N4J8ahufH2vlo4NAo= github.com/coder/websocket v1.8.12/go.mod h1:LNVeNrXQZfe5qhS9ALED3uA+l5pPqvwXg3CKoDBB2gs= -github.com/containerd/cgroups/v3 v3.0.1 h1:4hfGvu8rfGIwVIDd+nLzn/B9ZXx4BcCjzt5ToenJRaE= -github.com/containerd/cgroups/v3 v3.0.1/go.mod h1:/vtwk1VXrtoa5AaZLkypuOJgA/6DyPMZHJPGQNtlHnw= +github.com/containerd/cgroups/v3 v3.0.4 h1:2fs7l3P0Qxb1nKWuJNFiwhp2CqiKzho71DQkDrHJIo4= +github.com/containerd/cgroups/v3 v3.0.4/go.mod h1:SA5DLYnXO8pTGYiAHXz94qvLQTKfVM5GEVisn4jpins= +github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= +github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= @@ -168,8 +201,9 @@ github.com/couchbase/moss v0.1.0/go.mod h1:9MaHIaRuy9pvLPUJxB8sh8OrLfyDczECVL37g github.com/couchbase/vellum v1.0.2 h1:BrbP0NKiyDdndMPec8Jjhy0U47CZ0Lgx3xUC2r9rZqw= github.com/couchbase/vellum v1.0.2/go.mod h1:FcwrEivFpNi24R3jLOs3n+fs5RnuQnQqCLBJ1uAg1W4= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= -github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc= +github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cznic/b v0.0.0-20181122101859-a26611c4d92d h1:SwD98825d6bdB+pEuTxWOXiSjBrHdOl/UVp75eI7JT8= github.com/cznic/b v0.0.0-20181122101859-a26611c4d92d/go.mod h1:URriBxXwVq5ijiJ12C7iIZqlA69nTlI+LgI6/pwftG8= @@ -184,16 +218,12 @@ github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/r github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c= github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4= -github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= -github.com/dlclark/regexp2 v1.7.0 h1:7lJfhqlPssTb1WQx4yvTHN0uElPEv52sbaECrAQxjAo= -github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= -github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= -github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/dop251/goja v0.0.0-20211022113120-dc8c55024d06/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk= -github.com/dop251/goja v0.0.0-20231027120936-b396bb4c349d h1:wi6jN5LVt/ljaBG4ue79Ekzb12QfJ52L9Q98tl8SWhw= -github.com/dop251/goja v0.0.0-20231027120936-b396bb4c349d/go.mod h1:QMWlm50DNe14hD7t24KEqZuUdC9sOTy8W6XbCU1mlw4= -github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA7c8pLvoGndExHudxTDKZ84Pyvv+90pbBjbTz0Y= -github.com/dop251/goja_nodejs v0.0.0-20211022123610-8dd9abb0616d/go.mod h1:DngW8aVqWbuLRMHItjPUyqdj+HWPvnQe8V8y1nDpIbM= +github.com/dlclark/regexp2 v1.11.4 h1:rPYF9/LECdNymJufQKmri9gV604RvvABwgOA8un7yAo= +github.com/dlclark/regexp2 v1.11.4/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/dop251/goja v0.0.0-20241024094426-79f3a7efcdbd h1:QMSNEh9uQkDjyPwu/J541GgSH+4hw+0skJDIj9HJ3mE= +github.com/dop251/goja v0.0.0-20241024094426-79f3a7efcdbd/go.mod h1:MxLav0peU43GgvwVgNbLAj1s/bSGboKkhuULvq/7hx4= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/eclipse/paho.mqtt.golang v1.5.0 h1:EH+bUVJNgttidWFkLLVKaQPGmkTUfQQqjOsyvMGvD6o= @@ -205,6 +235,8 @@ github.com/emersion/go-smtp v0.21.3/go.mod h1:qm27SGYgoIPRot6ubfQ/GpiPy/g3PaZAVR github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.13.1 h1:vPfJZCkob6yTMEgS+0TwfTUfbHjfy/6vOJ8hUWX/uXE= +github.com/envoyproxy/go-control-plane v0.13.1/go.mod h1:X45hY0mufo6Fd0KW3rqsGvQMw58jvjymeCzBU3mWyHw= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v1.1.0 h1:tntQDh69XqOCOZsDz0lVJQez/2L6Uu2PdjCQwWCJ3bM= github.com/envoyproxy/protoc-gen-validate v1.1.0/go.mod h1:sXRDRVmzEbkM7CVcM06s9shE/m23dg3wzjl0UWqJ2q4= @@ -218,14 +250,12 @@ github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7z github.com/fsnotify/fsnotify v1.4.3-0.20170329110642-4da3e2cfbabc/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= -github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= +github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/garyburd/redigo v1.1.1-0.20170914051019-70e1b1943d4f/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= -github.com/getsentry/sentry-go v0.29.1 h1:DyZuChN8Hz3ARxGVV8ePaNXh1dQ7d76AiB117xcREwA= -github.com/getsentry/sentry-go v0.29.1/go.mod h1:x3AtIzN01d6SiWkderzaH28Tm0lgkafpJ5Bm3li39O0= -github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2 h1:Ujru1hufTHVb++eG6OuNDKMxZnGIvF6o/u8q/8h2+I4= +github.com/getsentry/sentry-go v0.30.0 h1:lWUwDnY7sKHaVIoZ9wYqRHJ5iEmoc0pqcRqFkosKzBo= +github.com/getsentry/sentry-go v0.30.0/go.mod h1:WU9B9/1/sHDqeV8T+3VwwbjeR5MSXs/6aqG3mqZrezA= github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE= -github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31 h1:gclg6gY70GLy3PbkQ1AERPfmLMMagS60DKF78eWwLn8= github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= @@ -236,14 +266,17 @@ github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI= +github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow= github.com/go-redis/redis v6.15.8+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= github.com/go-redis/redis/v8 v8.4.2/go.mod h1:A1tbYoHSa1fXwN+//ljcCYYJeLmVrwL9hbQN45Jdy0M= -github.com/go-sourcemap/sourcemap v2.1.3+incompatible h1:W1iEw64niKVGogNgBN3ePyLFfuisuzeidWPMPWmECqU= -github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= +github.com/go-sourcemap/sourcemap v2.1.4+incompatible h1:a+iTbH5auLKxaNwQFg0B+TCYl6lbukKPc7b5x0n1s6Q= +github.com/go-sourcemap/sourcemap v2.1.4+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= github.com/go-stack/stack v1.6.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/godbus/dbus/v5 v5.0.4 h1:9349emZab16e7zQvpmsbtjc18ykshndd8y2PG3sgJbA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= +github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= @@ -295,8 +328,8 @@ github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian/v3 v3.3.3 h1:DIhPTQrbPkgs2yJYdXU/eNACCG5DVQjySNRNlflZ9Fc= github.com/google/martian/v3 v3.3.3/go.mod h1:iEPrYcgCF7jA9OtScMFQyAlZZ4YXTKEtJ1E6RWzmBA0= -github.com/google/pprof v0.0.0-20230207041349-798e818bf904 h1:4/hN5RUoecvl+RmJRE2YxKWtnnQls6rQjjW5oV7qg2U= -github.com/google/pprof v0.0.0-20230207041349-798e818bf904/go.mod h1:uglQLonpP8qtYCYyzA+8c/9qtqgA3qsXGYqCPKARAFg= +github.com/google/pprof v0.0.0-20241122213907-cbe949e5a41b h1:SXO0REt4iu865upYCk8aKBBJQ4BqoE0ReP23ClMu60s= +github.com/google/pprof v0.0.0-20241122213907-cbe949e5a41b/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= github.com/google/s2a-go v0.1.8 h1:zZDs9gcbt9ZPLV0ndSyQk6Kacx2g/X+SKYovpnz3SMM= github.com/google/s2a-go v0.1.8/go.mod h1:6iNWHTpQ+nfNRN5E00MSdfDwVesa8hhS32PhPO8deJA= github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= @@ -306,12 +339,11 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/wire v0.6.0 h1:HBkoIh4BdSxoyo9PveV8giw7ZsaBOvzWKfcg/6MrVwI= github.com/google/wire v0.6.0/go.mod h1:F4QhpQ9EDIdJ1Mbop/NZBRB+5yrR6qg3BnctaoUk6NA= -github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs= -github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= +github.com/googleapis/enterprise-certificate-proxy v0.3.4 h1:XYIDZApgAnrN1c855gTgghdIA6Stxb52D5RnLI1SLyw= +github.com/googleapis/enterprise-certificate-proxy v0.3.4/go.mod h1:YKe7cfqYXjKGpGvmSg28/fFvhNzinZQm8DGnaburhGA= github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= -github.com/googleapis/gax-go/v2 v2.13.0 h1:yitjD5f7jQHhyDsnhKEBU52NdvvdSeGzlAnDPT0hH1s= -github.com/googleapis/gax-go/v2 v2.13.0/go.mod h1:Z/fvTZXF8/uw7Xu5GuslPw+bplx6SS338j1Is2S+B7A= -github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99 h1:twflg0XRTjwKpxb/jFExr4HGq6on2dEOmnL6FV+fgPw= +github.com/googleapis/gax-go/v2 v2.14.0 h1:f+jMrjBPl+DL9nI4IQzLUxMq7XrAqFYB7hBPqMNIe8o= +github.com/googleapis/gax-go/v2 v2.14.0/go.mod h1:lhBCnjdLrWRaPvLWhmc8IS24m9mr07qSYnHncrgo+zk= github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/csrf v1.7.2 h1:oTUjx0vyf2T+wkrx09Trsev1TE+/EbDAeHtSTbtC2eI= github.com/gorilla/csrf v1.7.2/go.mod h1:F1Fj3KG23WYHE6gozCmBAezKookxbIvUJT+121wTuLk= @@ -338,10 +370,11 @@ github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 h1:qnpS github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1/go.mod h1:lXGCsh6c22WGtjr+qGHj1otzZpV/1kwTMAqkwZsnWRU= github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 h1:pRhl55Yx1eC7BZ1N+BBWwnKaMyD8uC+34TLdndZMAKk= github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0/go.mod h1:XKMd7iuf/RGPSMJ/U4HP0zS2Z9Fh8Ps9a+6X26m/tmI= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 h1:asbCHRVmodnJTuQ3qamDwqVOIjwqUPTYmYuemVOx+Ys= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0/go.mod h1:ggCgvZ2r7uOoQjOyu2Y1NhHmEPPzzuhWgcza5M1Ji1I= -github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0 h1:TmHmbvxPmaegwhDubVz0lICL0J5Ka2vwTzhoePEXsGE= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0/go.mod h1:qztMSjm835F2bXf+5HKAPIS5qsmQDqZna/PgVt4rWtI= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c= +github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v0.0.0-20170914154624-68e816d1c783/go.mod h1:oZtUIOe8dh44I2q6ScRibXws4Ajl+d+nod3AaR9vL5w= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= @@ -354,14 +387,13 @@ github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/iancoleman/strcase v0.3.0 h1:nTXanmYxhfFAMjZL34Ov6gkzEsSJZ5DbhxWjvSASxEI= github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= -github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= github.com/ikawaha/kagome.ipadic v1.1.2/go.mod h1:DPSBbU0czaJhAb/5uKQZHMc9MTVRpDugJfX+HddPHHg= github.com/inconshreveable/log15 v0.0.0-20170622235902-74a0988b5f80/go.mod h1:cOaXtrgN4ScfRrD9Bre7U1thNq5RtJ8ZoP4iXVGRj6o= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/jackc/pgerrcode v0.0.0-20220416144525-469b46aa5efa h1:s+4MhCQ6YrzisK6hFJUX53drDT4UsSW3DEhKn0ifuHw= -github.com/jackc/pgerrcode v0.0.0-20220416144525-469b46aa5efa/go.mod h1:a/s9Lp5W7n/DD0VrVoyJ00FbP2ytTPDVOivvn2bMlds= +github.com/jackc/pgerrcode v0.0.0-20240316143900-6e2875d9b438 h1:Dj0L5fhJ9F82ZJyVOmBx6msDp/kfd1t9GRfny/mfJA0= +github.com/jackc/pgerrcode v0.0.0-20240316143900-6e2875d9b438/go.mod h1:a/s9Lp5W7n/DD0VrVoyJ00FbP2ytTPDVOivvn2bMlds= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= @@ -392,12 +424,18 @@ github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGw github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jmhodges/levigo v1.0.0 h1:q5EC36kV79HWeTBWsod3mG11EgStG3qArTKcvlksN1U= github.com/jmhodges/levigo v1.0.0/go.mod h1:Q6Qx+uH3RAqyK4rFQroq9RL7mdkABMcfhEI+nNuzMJQ= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA= +github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= +github.com/jsimonetti/rtnetlink/v2 v2.0.1 h1:xda7qaHDSVOsADNouv7ukSuicKZO7GgVUCXxpaIEIlM= +github.com/jsimonetti/rtnetlink/v2 v2.0.1/go.mod h1:7MoNYNbb3UaDHtF8udiJo/RH6VsTKP1pqKLUTVCvToE= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jtacoma/uritemplates v1.0.0 h1:xwx5sBF7pPAb0Uj8lDC1Q/aBPpOFyQza7OC705ZlLCo= github.com/jtacoma/uritemplates v1.0.0/go.mod h1:IhIICdE9OcvgUnGwTtJxgBQ+VrTrti5PcbLVSJianO8= -github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/keybase/go-keychain v0.0.0-20231219164618-57a3676c3af6 h1:IsMZxCuZqKuao2vNdfD82fjjgPLfyHLpR41Z88viRWs= +github.com/keybase/go-keychain v0.0.0-20231219164618-57a3676c3af6/go.mod h1:3VeWNIJaW+O5xpRQbPp0Ybqu1vJd/pm7s2F473HRrkw= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= @@ -406,8 +444,6 @@ github.com/kljensen/snowball v0.6.0/go.mod h1:27N7E8fVU5H68RlUmnWwZCfxgt4POBJfEN github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -422,13 +458,19 @@ github.com/magiconair/properties v1.7.4-0.20170902060319-8d7837e64d3c/go.mod h1: github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-colorable v0.0.10-0.20170816031813-ad5389df28cd/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-isatty v0.0.2/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= -github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= +github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/maxatome/go-testdeep v1.12.0 h1:Ql7Go8Tg0C1D/uMMX59LAoYK7LffeJQ6X2T04nTH68g= github.com/maxatome/go-testdeep v1.12.0/go.mod h1:lPZc/HAcJMP92l7yI6TRz1aZN5URwUBUAfUNvrclaNM= +github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g= +github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw= +github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U= +github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA= github.com/mileusna/useragent v1.3.5 h1:SJM5NzBmh/hO+4LGeATKpaEX9+b4vcGg2qXGLiNGDws= github.com/mileusna/useragent v1.3.5/go.mod h1:3d8TOmwL/5I8pJjyVDteHtgDGcefrFUX4ccGOMKNYYc= github.com/minio/highwayhash v1.0.3 h1:kbnuUMoHYyVl7szWjSxJnxw11k2U709jqFPPmIUyD6Q= @@ -438,6 +480,8 @@ github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HK github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= +github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -473,8 +517,8 @@ github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1Cpa github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc= -github.com/opencontainers/runtime-spec v1.0.2 h1:UfAcuLBJB9Coz72x1hgl8O5RVzTdNiaglX6v2DM6FI0= -github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-spec v1.2.0 h1:z97+pHb3uELt/yiAWD691HNHQIF07bE7dzrbT927iTk= +github.com/opencontainers/runtime-spec v1.2.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/openshift/build-machinery-go v0.0.0-20200917070002-f171684f77ab/go.mod h1:b1BuldmJlbA/xYtdZvKi+7j5YGB44qJUJDZ9zwiNCfE= github.com/openshift/osin v1.0.2-0.20220317075346-0f4d38c6e53f h1:4da9vH8eDlJo58703cADj3FlsdnFRgsnfuwj/4lYXfY= github.com/openshift/osin v1.0.2-0.20220317075346-0f4d38c6e53f/go.mod h1:DoYehsADYGKlXTIvqyZVnopfJbWgT6UsQYf8ETt1vjw= @@ -483,13 +527,13 @@ github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFSt github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0= github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y= github.com/pborman/getopt v0.0.0-20170112200414-7148bc3a4c30/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o= -github.com/pborman/uuid v1.2.0 h1:J7Q5mO4ysT1dv8hyrUGHb9+ooztCXu1D8MY8DZYsu3g= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= +github.com/pborman/uuid v1.2.1 h1:+ZZIw58t/ozdjRaXh/3awHfmWRbzYxJoAdNJxe/3pvw= +github.com/pborman/uuid v1.2.1/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.0.1-0.20170904195809-1d6b12b7cb29/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= -github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= -github.com/philhofer/fwd v1.0.0 h1:UbZqGr5Y38ApvM/V/jEljVxwocdweyH+vmYvRPBnbqQ= +github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= +github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= @@ -499,6 +543,8 @@ github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsK github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -509,8 +555,8 @@ github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/j github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= -github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc= -github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8= +github.com/prometheus/common v0.60.1 h1:FUas6GcOw66yB/73KC+BOZoFJmbo/1pojoILArPAaSc= +github.com/prometheus/common v0.60.1/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw= github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= github.com/puzpuzpuz/xsync/v3 v3.4.0 h1:DuVBAdXuGFHv8adVXjWWZ63pJq+NRXOWVXlKDBZ+mJ4= @@ -521,17 +567,16 @@ github.com/redis/go-redis/v9 v9.7.0 h1:HhLSs+B6O021gwzl+locl0zEDnyNkxMtf/Z3NNBMa github.com/redis/go-redis/v9 v9.7.0/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= -github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= -github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= +github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= -github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= +github.com/sagikazarmark/locafero v0.6.0 h1:ON7AQg37yzcRPU69mt7gwhFEBwxI6P9T4Qu3N51bwOk= +github.com/sagikazarmark/locafero v0.6.0/go.mod h1:77OmuIc6VTraTXKXIs/uvUxKGUXjE1GbemJYHqdNjX0= github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= github.com/sendgrid/rest v2.6.9+incompatible h1:1EyIcsNdn9KIisLW50MKwmSRSK+ekueiEMJ7NEoxJo0= @@ -590,7 +635,6 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= @@ -602,55 +646,63 @@ github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c h1:g+WoO5jjkqGAzH github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c/go.mod h1:ahpPrc7HpcfEWDQRZEmnXMzHY03mLDYMCxeDzy46i+8= github.com/throttled/throttled/v2 v2.12.0 h1:IezKE1uHlYC/0Al05oZV6Ar+uN/znw3cy9J8banxhEY= github.com/throttled/throttled/v2 v2.12.0/go.mod h1:+EAvrG2hZAQTx8oMpBu8fq6Xmm+d1P2luKK7fIY1Esc= -github.com/tinylib/msgp v1.1.0 h1:9fQd+ICuRIu/ue4vxJZu6/LzxN0HwMds2nq/0cFvxHU= github.com/tinylib/msgp v1.1.0/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc h1:9lRDQMhESg+zvGYmW5DyG0UqvY96Bu5QYsTLvCHdrgo= github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc/go.mod h1:bciPuU6GHm1iF1pBvUfxfsH0Wmnc2VbpgvbI9ZWuIRs= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= -github.com/uptrace/bun v1.2.5 h1:gSprL5xiBCp+tzcZHgENzJpXnmQwRM/A6s4HnBF85mc= -github.com/uptrace/bun v1.2.5/go.mod h1:vkQMS4NNs4VNZv92y53uBSHXRqYyJp4bGhMHgaNCQpY= -github.com/uptrace/bun/dialect/pgdialect v1.2.5 h1:dWLUxpjTdglzfBks2x+U2WIi+nRVjuh7Z3DLYVFswJk= -github.com/uptrace/bun/dialect/pgdialect v1.2.5/go.mod h1:stwnlE8/6x8cuQ2aXcZqwDK/d+6jxgO3iQewflJT6C4= -github.com/uptrace/bun/driver/pgdriver v1.2.5 h1:+0Ofdg/tW7DsIXdTizYWapSex6Csh9VdBg6/bbAZWJw= -github.com/uptrace/bun/driver/pgdriver v1.2.5/go.mod h1:RsYV08Z72glum3swBhag7IBl1D+eztjWmodfcOZFHJ0= +github.com/uptrace/bun v1.2.6 h1:lyGBQAhNiClchb97HA2cBnDeRxwTRLhSIgiFPXVisV8= +github.com/uptrace/bun v1.2.6/go.mod h1:xMgnVFf+/5xsrFBU34HjDJmzZnXbVuNEt/Ih56I8qBU= +github.com/uptrace/bun/dialect/pgdialect v1.2.6 h1:iNd1YLx619K+sZK+dRcWPzluurXYK1QwIkp9FEfNB/8= +github.com/uptrace/bun/dialect/pgdialect v1.2.6/go.mod h1:OL7d3qZLxKYP8kxNhMg3IheN1pDR3UScGjoUP+ivxJQ= +github.com/uptrace/bun/driver/pgdriver v1.2.6 h1:dD7ckqIhVDayfYTwMKZ0dJM9AZfNJNBu5Cg/8g0EMOk= +github.com/uptrace/bun/driver/pgdriver v1.2.6/go.mod h1:ChrVrMZlRzPQHTP4QCm/p1FfQqgnYXWlES0GS9qjWEY= github.com/vitorsalgado/mocha/v2 v2.0.2 h1:wb1QCRzVkp8uhRcUYmb9jJfbMj/qbiqcDyD8rD+Ldfw= github.com/vitorsalgado/mocha/v2 v2.0.2/go.mod h1:l7jRVm7KTL4VAxxazH99UVo+KzwztjrYpFTksTmL1DE= github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8= github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok= github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= -github.com/willf/bitset v1.1.10 h1:NotGKqX0KwQ72NUzqrjZq5ipPNDQex9lo3WpaS8L2sc= github.com/willf/bitset v1.1.10/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= +github.com/willf/bitset v1.1.11 h1:N7Z7E9UvjW+sGsEl7k/SJrvY2reP1A07MrGuCjIOjRE= +github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI= +github.com/wk8/go-ordered-map/v2 v2.1.9-0.20240816141633-0a40785b4f41 h1:rnB8ZLMeAr3VcqjfRkAm27qb8y6zFKNfuHvy1Gfe7KI= +github.com/wk8/go-ordered-map/v2 v2.1.9-0.20240816141633-0a40785b4f41/go.mod h1:DbzwytT4g/odXquuOCqroKvtxxldI4nb3nuesHF/Exo= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= -go.etcd.io/bbolt v1.3.8 h1:xs88BrvEv273UsB79e0hcVrlUWmS0a8upikMFhSyAtA= -go.etcd.io/bbolt v1.3.8/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= +go.etcd.io/bbolt v1.3.11 h1:yGEzV1wPz2yVCLsD8ZAiGHhHVlczyC9d1rP43/VCRJ0= +go.etcd.io/bbolt v1.3.11/go.mod h1:dksAq7YMXoljX0xu6VF5DMZGbhYYoLUalEiSySYAS4I= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= -go.opentelemetry.io/contrib/instrumentation/github.com/gorilla/mux/otelmux v0.56.0 h1:k5inBHeCb4SXSmzkZGNX5oJj2RGg0y8LyLNHKR4hlb8= -go.opentelemetry.io/contrib/instrumentation/github.com/gorilla/mux/otelmux v0.56.0/go.mod h1:Q3hUOabe0Dekk+iwIJZDB3AzB/TVaECQ03Es8OV+vZ0= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.56.0 h1:yMkBS9yViCc7U7yeLzJPM2XizlfdVvBRSmsQDWu6qc0= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.56.0/go.mod h1:n8MR6/liuGB5EmTETUBeU5ZgqMOlqKRxUaqPQBOANZ8= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 h1:UP6IpuHFkUgOQL9FFQFrZ+5LiwhhYRbi7VZSIx6Nj5s= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0/go.mod h1:qxuZLtbq5QDtdeSHsS7bcf6EH6uO6jUAgk764zd3rhM= +go.opentelemetry.io/contrib/detectors/gcp v1.32.0 h1:P78qWqkLSShicHmAzfECaTgvslqHxblNE9j62Ws1NK8= +go.opentelemetry.io/contrib/detectors/gcp v1.32.0/go.mod h1:TVqo0Sda4Cv8gCIixd7LuLwW4EylumVWfhjZJjDD4DU= +go.opentelemetry.io/contrib/instrumentation/github.com/gorilla/mux/otelmux v0.57.0 h1:ydMxn2B3ZKzDXmjgE/tBtq7RsArxmikZUlRWComOPFs= +go.opentelemetry.io/contrib/instrumentation/github.com/gorilla/mux/otelmux v0.57.0/go.mod h1:rD9Z+09JseOeFdSJUrtnA2hO4XBY3lf1Tj0tPqf+LEM= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.57.0 h1:qtFISDHKolvIxzSs0gIaiPUPR0Cucb0F2coHC7ZLdps= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.57.0/go.mod h1:Y+Pop1Q6hCOnETWTW4NROK/q1hv50hM7yDaUTjG8lp8= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.57.0 h1:DheMAlT6POBP+gh8RUH19EOTnQIor5QE0uSRPtzCpSw= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.57.0/go.mod h1:wZcGmeVO9nzP67aYSLDqXNWK87EZWhi7JWj1v7ZXf94= go.opentelemetry.io/otel v0.14.0/go.mod h1:vH5xEuwy7Rts0GNtsCW3HYQoZDY+OmBJ6t1bFGGlxgw= -go.opentelemetry.io/otel v1.31.0 h1:NsJcKPIW0D0H3NgzPDHmo0WW6SptzPdqg/L1zsIm2hY= -go.opentelemetry.io/otel v1.31.0/go.mod h1:O0C14Yl9FgkjqcCZAsE053C13OaddMYr/hz6clDkEJE= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.31.0 h1:K0XaT3DwHAcV4nKLzcQvwAgSyisUghWoY20I7huthMk= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.31.0/go.mod h1:B5Ki776z/MBnVha1Nzwp5arlzBbE3+1jk+pGmaP5HME= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.31.0 h1:FFeLy03iVTXP6ffeN2iXrxfGsZGCjVx0/4KlizjyBwU= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.31.0/go.mod h1:TMu73/k1CP8nBUpDLc71Wj/Kf7ZS9FK5b53VapRsP9o= -go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.31.0 h1:UGZ1QwZWY67Z6BmckTU+9Rxn04m2bD3gD6Mk0OIOCPk= -go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.31.0/go.mod h1:fcwWuDuaObkkChiDlhEpSq9+X1C0omv+s5mBtToAQ64= -go.opentelemetry.io/otel/metric v1.31.0 h1:FSErL0ATQAmYHUIzSezZibnyVlft1ybhy4ozRPcF2fE= -go.opentelemetry.io/otel/metric v1.31.0/go.mod h1:C3dEloVbLuYoX41KpmAhOqNriGbA+qqH6PQ5E5mUfnY= -go.opentelemetry.io/otel/sdk v1.31.0 h1:xLY3abVHYZ5HSfOg3l2E5LUj2Cwva5Y7yGxnSW9H5Gk= -go.opentelemetry.io/otel/sdk v1.31.0/go.mod h1:TfRbMdhvxIIr/B2N2LQW2S5v9m3gOQ/08KsbbO5BPT0= -go.opentelemetry.io/otel/trace v1.31.0 h1:ffjsj1aRouKewfr85U2aGagJ46+MvodynlQ1HYdmJys= -go.opentelemetry.io/otel/trace v1.31.0/go.mod h1:TXZkRk7SM2ZQLtR6eoAWQFIHPvzQ06FJAsO1tJg480A= +go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U= +go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.32.0 h1:IJFEoHiytixx8cMiVAO+GmHR6Frwu+u5Ur8njpFO6Ac= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.32.0/go.mod h1:3rHrKNtLIoS0oZwkY2vxi+oJcwFRWdtUyRII+so45p8= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.32.0 h1:9kV11HXBHZAvuPUZxmMWrH8hZn/6UnHX4K0mu36vNsU= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.32.0/go.mod h1:JyA0FHXe22E1NeNiHmVp7kFHglnexDQ7uRWDiiJ1hKQ= +go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.29.0 h1:WDdP9acbMYjbKIyJUhTvtzj601sVJOqgWdUxSdR/Ysc= +go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.29.0/go.mod h1:BLbf7zbNIONBLPwvFnwNHGj4zge8uTCM/UPIVW1Mq2I= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.32.0 h1:cC2yDI3IQd0Udsux7Qmq8ToKAx1XCilTQECZ0KDZyTw= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.32.0/go.mod h1:2PD5Ex6z8CFzDbTdOlwyNIUywRr1DN0ospafJM1wJ+s= +go.opentelemetry.io/otel/metric v1.32.0 h1:xV2umtmNcThh2/a/aCP+h64Xx5wsj8qqnkYZktzNa0M= +go.opentelemetry.io/otel/metric v1.32.0/go.mod h1:jH7CIbbK6SH2V2wE16W05BHCtIDzauciCRLoc/SyMv8= +go.opentelemetry.io/otel/sdk v1.32.0 h1:RNxepc9vK59A8XsgZQouW8ue8Gkb4jpWtJm9ge5lEG4= +go.opentelemetry.io/otel/sdk v1.32.0/go.mod h1:LqgegDBjKMmb2GC6/PrTnteJG39I8/vJCAP9LlJXEjU= +go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU= +go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ= +go.opentelemetry.io/otel/trace v1.32.0 h1:WIC9mYrXf8TmY/EXuULKc8hR17vE+Hjv2cssQDe03fM= +go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8= go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= go.packetbroker.org/api/iam v1.8.2 h1:XQ0ViBP5+6PikRNLPZi0u+bi8STdQ0Xb9poLyiXwj8k= @@ -665,8 +717,8 @@ go.packetbroker.org/api/v3 v3.17.1 h1:LcyFPUGqVubGWMvQ16tZlQIKd+noGx7urzEYhSLiEQ go.packetbroker.org/api/v3 v3.17.1/go.mod h1:6bVbdWAYLnvZ5kgXxA7GBQvZTN7vxI0DoF1Di1NoAT4= go.thethings.industries/pkg/api/gen/tti/gateway v0.0.0-20241023114011-31c9c9f86834 h1:ANhGlIwofZn/HfA6bNkAD5Oh6n3wCcEjXjhT1LmpMv0= go.thethings.industries/pkg/api/gen/tti/gateway v0.0.0-20241023114011-31c9c9f86834/go.mod h1:2+WsMwIunNLh22oauBzGL56JazE3UY34W1fstqEbacw= -go.thethings.industries/pkg/ca v0.0.0-20240809123127-21a24c0e47df h1:IMSctPllwEtJLyM/1AWgBiN1NPwBKqEVkCiK0I/MNY4= -go.thethings.industries/pkg/ca v0.0.0-20240809123127-21a24c0e47df/go.mod h1:89OU623VYKW9i3W4CZgIGFmtgb/jsN8JV2PAuCsj+7w= +go.thethings.industries/pkg/ca v0.0.0-20241023114011-31c9c9f86834 h1:I7oylW49wLHcUZOrxk9CmCzKAk0wyBCMIvxeiYUWXt4= +go.thethings.industries/pkg/ca v0.0.0-20241023114011-31c9c9f86834/go.mod h1:89OU623VYKW9i3W4CZgIGFmtgb/jsN8JV2PAuCsj+7w= go.thethings.network/lorawan-application-payload v0.0.0-20220125153912-1198ff1e403e h1:TWGQ3lh7gI2W5hnb6qPdpoAa0d7s/XPwvgf2VVCMJaY= go.thethings.network/lorawan-application-payload v0.0.0-20220125153912-1198ff1e403e/go.mod h1:/smENFuESiJCyWRsHRLBjbZGMN889BORQXIPP6NlpZE= go.thethings.network/lorawan-stack-legacy/v2 v2.1.0 h1:xP1fElQiH8/6GWXg6JG8UqRD0uluhmicIRS4e/BzcAw= @@ -694,14 +746,14 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= -golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= -golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= +golang.org/x/crypto v0.30.0 h1:RwoQn3GkWiMkzlX562cLB7OxWvjH1L8xutO2WoJcRoY= +golang.org/x/crypto v0.30.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20240119083558-1b970713d09a h1:Q8/wZp0KX97QFTc2ywcOE0YRjZPVIx+MXInMzdvQqcA= -golang.org/x/exp v0.0.0-20240119083558-1b970713d09a/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08= +golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f h1:XdNn9LlyWAhLVp6P/i8QYBW+hlyhrhei9uErw2B5GJo= +golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f/go.mod h1:D5SMRVC3C2/4+F/DB1wZsLRnSNimn2Sp/NPsCrsv8ak= golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.14.0 h1:tNgSxAFe3jC4uYqvZdTr84SZoM1KfwdC9SKIFrLjFn4= -golang.org/x/image v0.14.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE= +golang.org/x/image v0.22.0 h1:UtK5yLUzilVrkjMAZAZ34DXGpASN8i8pj8g+O+yd10g= +golang.org/x/image v0.22.0/go.mod h1:9hPFhljd4zZ1GNSIZJ49sqbp45GKK9t6w+iXvGqZUz4= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= @@ -732,13 +784,13 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= -golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= -golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= +golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI= +golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs= golang.org/x/oauth2 v0.0.0-20170912212905-13449ad91cb2/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= -golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE= +golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20170517211232-f52d1811a629/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -750,8 +802,8 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= -golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -770,7 +822,6 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -780,31 +831,30 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= -golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= -golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24= -golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M= +golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= +golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= -golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/time v0.0.0-20170424234030-8be79e1e0910/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ= -golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg= +golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -822,11 +872,11 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20240716161551-93cc26a95ae9 h1:LLhsEBxRTBLuKlQxFBYUOU8xyFgXv6cOTp2HASDlsDk= -golang.org/x/xerrors v0.0.0-20240716161551-93cc26a95ae9/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= +golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhSt0ABwskkZKjD3bXGnZGpNY= +golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= google.golang.org/api v0.0.0-20170921000349-586095a6e407/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= -google.golang.org/api v0.191.0 h1:cJcF09Z+4HAB2t5qTQM1ZtfL/PemsLFkcFG67qq2afk= -google.golang.org/api v0.191.0/go.mod h1:tD5dsFGxFza0hnQveGfVk9QQYKcfp+VzgRqyXFxE0+E= +google.golang.org/api v0.209.0 h1:Ja2OXNlyRlWCWu8o+GgI4yUn/wz9h/5ZfFbKz+dQX+w= +google.golang.org/api v0.209.0/go.mod h1:I53S168Yr/PNDNMi5yPnDc0/LGRZO6o7PoEbl/HY3CM= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= @@ -835,12 +885,12 @@ google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoA google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20240812133136-8ffd90a71988 h1:CT2Thj5AuPV9phrYMtzX11k+XkzMGfRAet42PmoTATM= -google.golang.org/genproto v0.0.0-20240812133136-8ffd90a71988/go.mod h1:7uvplUBj4RjHAxIZ//98LzOvrQ04JBkaixRmCMI29hc= -google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9 h1:T6rh4haD3GVYsgEfWExoCZA2o2FmbNyKpTuAxbEFPTg= -google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9/go.mod h1:wp2WsuBYj6j8wUdo3ToZsdxxixbvQNAHqVJrTgi5E5M= -google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9 h1:QCqS/PdaHTSWGvupk2F/ehwHtGc0/GYkT+3GAcR1CCc= -google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI= +google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 h1:ToEetK57OidYuqD4Q5w+vfEnPvPpuTwedCNVohYJfNk= +google.golang.org/genproto v0.0.0-20241118233622-e639e219e697/go.mod h1:JJrvXBWRZaFMxBufik1a4RpFw4HhgVtBBWQeQgUj2cc= +google.golang.org/genproto/googleapis/api v0.0.0-20241118233622-e639e219e697 h1:pgr/4QbFyktUv9CtQ/Fq4gzEE6/Xs7iCXbktaGzLHbQ= +google.golang.org/genproto/googleapis/api v0.0.0-20241118233622-e639e219e697/go.mod h1:+D9ySVjN8nY8YCVjc5O7PZDIdZporIDY3KaGfJunh88= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241118233622-e639e219e697 h1:LWZqQOEjDyONlF1H6afSWpAL/znlREo2tHfLoe+8LMA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241118233622-e639e219e697/go.mod h1:5uTbfoYQed2U9p3KIj2/Zzm02PYhndfdmML0qC3q3FU= google.golang.org/grpc v1.2.1-0.20170921194603-d4b75ebd4f9f/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= @@ -848,8 +898,10 @@ google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQ google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E= -google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= +google.golang.org/grpc v1.68.1 h1:oI5oTa11+ng8r8XMMN7jAOmWfPZWbYpCFaMUTACxkM0= +google.golang.org/grpc v1.68.1/go.mod h1:+q1XYFJjShcqn0QZHvCyeR4CXPA+llXIeUIfIe00waw= +google.golang.org/grpc/stats/opentelemetry v0.0.0-20241028142157-ada6787961b3 h1:hUfOButuEtpc0UvYiaYRbNwxVYr0mQQOWq6X8beJ9Gc= +google.golang.org/grpc/stats/opentelemetry v0.0.0-20241028142157-ada6787961b3/go.mod h1:jzYlkSMbKypzuu6xoAEijsNVo9ZeDF1u/zCfFgsx7jg= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -859,8 +911,8 @@ google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= -google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io= +google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk= gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -868,7 +920,6 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= diff --git a/package.json b/package.json index 1b160f257f..d2672688dd 100644 --- a/package.json +++ b/package.json @@ -6,28 +6,28 @@ "repository": "https://github.com/TheThingsNetwork/lorawan-stack.git", "license": "Apache-2.0", "devDependencies": { - "@babel/cli": "^7.25.6", - "@babel/core": "^7.25.2", - "@babel/eslint-parser": "^7.25.1", + "@babel/cli": "^7.25.9", + "@babel/core": "^7.26.0", + "@babel/eslint-parser": "^7.25.9", "@babel/plugin-proposal-class-properties": "^7.18.6", - "@babel/plugin-proposal-decorators": "^7.24.7", + "@babel/plugin-proposal-decorators": "^7.25.9", "@babel/plugin-syntax-dynamic-import": "^7.8.3", - "@babel/plugin-transform-runtime": "^7.25.4", - "@babel/plugin-transform-spread": "^7.24.7", - "@babel/plugin-transform-strict-mode": "^7.24.7", - "@babel/preset-env": "^7.25.4", - "@babel/preset-react": "^7.24.7", - "@babel/register": "^7.24.6", - "@babel/runtime-corejs2": "^7.25.6", - "@inquirer/prompts": "^5.4.0", + "@babel/plugin-transform-runtime": "^7.25.9", + "@babel/plugin-transform-spread": "^7.25.9", + "@babel/plugin-transform-strict-mode": "^7.25.9", + "@babel/preset-env": "^7.26.0", + "@babel/preset-react": "^7.25.9", + "@babel/register": "^7.25.9", + "@babel/runtime-corejs2": "^7.26.0", + "@inquirer/prompts": "^7.1.0", "@pmmmwh/react-refresh-webpack-plugin": "^0.5.15", - "@storybook/addon-designs": "8.0.3", - "@storybook/addon-essentials": "8.3.4", - "@storybook/addon-mdx-gfm": "8.3.4", - "@storybook/addon-actions": "8.3.4", - "@storybook/cli": "8.3.4", - "@storybook/react": "8.3.4", - "@storybook/react-webpack5": "8.3.4", + "@storybook/addon-designs": "8.0.4", + "@storybook/addon-essentials": "8.4.5", + "@storybook/addon-mdx-gfm": "8.4.5", + "@storybook/addon-actions": "8.4.5", + "@storybook/cli": "8.4.5", + "@storybook/react": "8.4.5", + "@storybook/react-webpack5": "8.4.5", "@testing-library/cypress": "^10.0.2", "add-asset-html-webpack-plugin": "^6.0.0", "babel-jest": "^29.7.0", @@ -38,7 +38,7 @@ "clean-webpack-plugin": "^4.0.0", "copy-webpack-plugin": "^12.0.2", "css-loader": "^6.10.0", - "cypress": "13.15.0", + "cypress": "13.16.0", "cypress-browser-permissions": "^1.1.0", "cypress-file-upload": "^5.0.8", "cypress-log-to-output": "^1.1.2", @@ -47,54 +47,54 @@ "eslint-config-ttn": "git+https://github.com/TheThingsNetwork/eslint-config-ttn.git#v1.4.0", "eslint-import-resolver-webpack": "^0.13.9", "eslint-plugin-babel": "^5.3.1", - "eslint-plugin-cypress": "^3.5.0", - "eslint-plugin-import": "^2.30.0", - "eslint-plugin-jest": "^28.8.3", - "eslint-plugin-jsdoc": "^50.3.0", + "eslint-plugin-cypress": "^3.6.0", + "eslint-plugin-import": "^2.31.0", + "eslint-plugin-jest": "^28.9.0", + "eslint-plugin-jsdoc": "^50.5.0", "eslint-plugin-prefer-arrow": "^1.2.3", "eslint-plugin-prettier": "5.2.1", - "eslint-plugin-react": "^7.37.0", + "eslint-plugin-react": "^7.37.2", "file-loader": "^6.2.0", - "html-webpack-plugin": "^5.6.0", - "http-proxy-middleware": "^3.0.2", + "html-webpack-plugin": "^5.6.3", + "http-proxy-middleware": "^3.0.3", "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", "js-yaml": "^4.0.0", "json": "^11.0.0", "messageformat-parser": "^4.1.3", - "mini-css-extract-plugin": "^2.9.1", + "mini-css-extract-plugin": "^2.9.2", "mjml": "^4.15.3", "mkdirp": "^3.0.1", "nib": "^1.2.0", - "pg": "^8.13.0", + "pg": "^8.13.1", "prettier": "3.3.3", "react-refresh": "^0.14.2", - "stylelint": "^16.9.0", + "stylelint": "^16.10.0", "stylelint-stylus": "^1.0.0", "storybook": "^8.1.8", - "stylus": "^0.63.0", + "stylus": "^0.64.0", "stylus-loader": "^8.1.1", "wait-on": "^8.0.1", - "webpack": "^5.95.0", + "webpack": "^5.96.1", "webpack-cli": "^5.1.4", "webpack-dev-server": "^5.1.0", "webpack-shell-plugin": "https://github.com/cdeutsch/webpack-shell-plugin.git#bee537d", "yargs": "^17.7.2" }, "dependencies": { - "@tabler/icons-react": "^3.19.0", - "@formatjs/intl-datetimeformat": "^6.12.5", - "@formatjs/intl-displaynames": "^6.6.8", - "@formatjs/intl-listformat": "^7.5.7", - "@formatjs/intl-locale": "^4.0.0", - "@formatjs/intl-numberformat": "^8.10.3", - "@formatjs/intl-pluralrules": "^5.2.14", - "@formatjs/intl-relativetimeformat": "^11.2.14", - "@reduxjs/toolkit": "^2.2.7", + "@tabler/icons-react": "^3.22.0", + "@formatjs/intl-datetimeformat": "^6.16.5", + "@formatjs/intl-displaynames": "^6.8.5", + "@formatjs/intl-listformat": "^7.7.5", + "@formatjs/intl-locale": "^4.2.5", + "@formatjs/intl-numberformat": "^8.14.5", + "@formatjs/intl-pluralrules": "^5.3.5", + "@formatjs/intl-relativetimeformat": "^11.4.5", + "@reduxjs/toolkit": "^2.3.0", "@sentry/integrations": "^7.114.0", - "@sentry/react": "^8.32.0", + "@sentry/react": "^8.40.0", "@tippyjs/react": "^4.2.6", - "apexcharts": "^3.54.0", + "apexcharts": "^3.54.1", "autobind-decorator": "^2.4.0", "axios": "^1.5.1", "brace": "^0.11.1", @@ -110,21 +110,21 @@ "lodash": "^4.17.21", "md5": "^2.3.0", "prop-types": "^15.8.1", - "query-string": "^9.1.0", + "query-string": "^9.1.1", "react": "^17.0.1", "react-ace": "^6.6.0", - "react-apexcharts": "^1.4.1", + "react-apexcharts": "^1.6.0", "react-display-name": "^0.2.5", "react-dom": "^17.0.1", "react-focus-lock": "^2.13.2", "react-helmet": "^6.1.0", - "react-intl": "^6.7.0", + "react-intl": "^7.0.1", "react-leaflet": "^4.2.1", "react-paginate": "^8.2.0", "react-redux": "^8.1.2", "react-remove-scroll": "^2.6.0", "react-router-dom": "^6.14.1", - "react-select": "^5.8.1", + "react-select": "^5.8.3", "react-string-replace": "^1.1.1", "react-switch": "^7.0.0", "react-text-mask": "^5.5.0", diff --git a/pkg/applicationserver/config.go b/pkg/applicationserver/config.go index 97f3b3e0db..54b46ca378 100644 --- a/pkg/applicationserver/config.go +++ b/pkg/applicationserver/config.go @@ -105,6 +105,11 @@ type DownlinksConfig struct { ConfirmationConfig ConfirmationConfig `name:"confirmation" description:"Configuration for confirmed downlink"` } +// PaginationConfig represents the configuration for pagination. +type PaginationConfig struct { + DefaultLimit int64 `name:"default-limit" description:"Default limit for pagination"` +} + // 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)"` @@ -123,6 +128,7 @@ type Config struct { 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"` Downlinks DownlinksConfig `name:"downlinks" description:"Downlink configuration"` + Pagination PaginationConfig `name:"pagination" description:"Pagination configuration"` } func (c Config) toProto() *ttnpb.AsConfiguration { diff --git a/pkg/applicationserver/io/packages/grpc_test.go b/pkg/applicationserver/io/packages/grpc_test.go index cb0cc6a158..5069cd7fdb 100644 --- a/pkg/applicationserver/io/packages/grpc_test.go +++ b/pkg/applicationserver/io/packages/grpc_test.go @@ -30,6 +30,7 @@ import ( "go.thethings.network/lorawan-stack/v3/pkg/config" "go.thethings.network/lorawan-stack/v3/pkg/errors" mockis "go.thethings.network/lorawan-stack/v3/pkg/identityserver/mock" + ttnredis "go.thethings.network/lorawan-stack/v3/pkg/redis" "go.thethings.network/lorawan-stack/v3/pkg/rpcmetadata" "go.thethings.network/lorawan-stack/v3/pkg/ttnpb" "go.thethings.network/lorawan-stack/v3/pkg/unique" @@ -371,6 +372,8 @@ func TestAssociations(t *testing.T) { t.Run("Pagination", func(t *testing.T) { a := assertions.New(t) + ttnredis.SetPaginationDefaults(ttnredis.PaginationDefaults{DefaultLimit: 10}) + for i := 1; i < 21; i++ { association := &ttnpb.ApplicationPackageAssociation{ Ids: &ttnpb.ApplicationPackageAssociationIdentifiers{ @@ -424,8 +427,8 @@ func TestAssociations(t *testing.T) { limit: 0, page: 0, portLow: 1, - portHigh: 20, - length: 20, + portHigh: 10, + length: 10, }, } { t.Run(fmt.Sprintf("limit:%v_page:%v", tc.limit, tc.page), diff --git a/pkg/applicationserver/io/pubsub/redis/registry.go b/pkg/applicationserver/io/pubsub/redis/registry.go index ef718fbf4a..01134e6c0f 100644 --- a/pkg/applicationserver/io/pubsub/redis/registry.go +++ b/pkg/applicationserver/io/pubsub/redis/registry.go @@ -16,6 +16,7 @@ package redis import ( "context" + "runtime/trace" "time" "github.com/redis/go-redis/v9" @@ -132,19 +133,54 @@ func (r PubSubRegistry) Range(ctx context.Context, paths []string, f func(contex func (r PubSubRegistry) List(ctx context.Context, ids *ttnpb.ApplicationIdentifiers, paths []string) ([]*ttnpb.ApplicationPubSub, error) { var pbs []*ttnpb.ApplicationPubSub appUID := unique.ID(ctx, ids) - err := ttnredis.FindProtos(ctx, r.Redis, r.appKey(appUID), r.makeUIDKeyFunc(appUID)).Range(func() (proto.Message, func() (bool, error)) { - pb := &ttnpb.ApplicationPubSub{} - return pb, func() (bool, error) { - pb, err := applyPubSubFieldMask(nil, pb, appendImplicitPubSubGetPaths(paths...)...) + uidKey := r.appKey(appUID) + + opts := []ttnredis.FindProtosOption{} + limit, offset := ttnredis.PaginationLimitAndOffsetFromContext(ctx) + if limit != 0 { + opts = append(opts, + ttnredis.FindProtosSorted(true), + ttnredis.FindProtosWithOffsetAndCount(offset, limit), + ) + } + + rangeProtos := func(c redis.Cmdable) error { + return ttnredis.FindProtos(ctx, c, uidKey, r.makeUIDKeyFunc(appUID), opts...).Range( + func() (proto.Message, func() (bool, error)) { + pb := &ttnpb.ApplicationPubSub{} + return pb, func() (bool, error) { + pb, err := applyPubSubFieldMask(nil, pb, appendImplicitPubSubGetPaths(paths...)...) + if err != nil { + return false, err + } + pbs = append(pbs, pb) + return true, nil + } + }) + } + + defer trace.StartRegion(ctx, "list pubsub by application id").End() + + var err error + if limit != 0 { + var lockerID string + lockerID, err = ttnredis.GenerateLockerID() + if err != nil { + return nil, err + } + err = ttnredis.LockedWatch(ctx, r.Redis, uidKey, lockerID, r.LockTTL, func(tx *redis.Tx) (err error) { + total, err := tx.SCard(ctx, uidKey).Result() if err != nil { - return false, err + return err } - pbs = append(pbs, pb) - return true, nil - } - }) + ttnredis.SetPaginationTotal(ctx, total) + return rangeProtos(tx) + }) + } else { + err = rangeProtos(r.Redis) + } if err != nil { - return nil, err + return nil, ttnredis.ConvertError(err) } return pbs, nil } @@ -283,3 +319,13 @@ func (r PubSubRegistry) Set(ctx context.Context, ids *ttnpb.ApplicationPubSubIde } return pb, nil } + +// WithPagination returns a new context with pagination parameters. +func (PubSubRegistry) WithPagination( + ctx context.Context, + limit uint32, + page uint32, + total *int64, +) context.Context { + return ttnredis.NewContextWithPagination(ctx, int64(limit), int64(page), total) +} diff --git a/pkg/applicationserver/io/pubsub/redis/registry_test.go b/pkg/applicationserver/io/pubsub/redis/registry_test.go new file mode 100644 index 0000000000..68e829e8a4 --- /dev/null +++ b/pkg/applicationserver/io/pubsub/redis/registry_test.go @@ -0,0 +1,281 @@ +// Copyright © 2024 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 implements a Redis-backed PubSub registry. +package redis + +import ( + "fmt" + "testing" + + "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/util/test" + "go.thethings.network/lorawan-stack/v3/pkg/util/test/assertions/should" +) + +var Timeout = 10 * test.Delay + +func TestPubSubRegistry(t *testing.T) { + t.Parallel() + a, ctx := test.New(t) + cl, flush := test.NewRedis(ctx, "redis_test") + t.Cleanup(func() { + flush() + cl.Close() + }) + + ids := &ttnpb.ApplicationPubSubIdentifiers{ + ApplicationIds: &ttnpb.ApplicationIdentifiers{ + ApplicationId: "myapp-00", + }, + PubSubId: "pubsub-00", + } + ids1 := &ttnpb.ApplicationPubSubIdentifiers{ + ApplicationIds: &ttnpb.ApplicationIdentifiers{ + ApplicationId: "myapp-01", + }, + PubSubId: "pubsub-01", + } + ids2 := &ttnpb.ApplicationPubSubIdentifiers{ + ApplicationIds: &ttnpb.ApplicationIdentifiers{ + ApplicationId: "myapp-02", + }, + PubSubId: "pubsub-02", + } + provider := &ttnpb.ApplicationPubSub_Nats{ + Nats: &ttnpb.ApplicationPubSub_NATSProvider{ + ServerUrl: "nats://localhost", + }, + } + + registry := &PubSubRegistry{ + Redis: cl, + LockTTL: test.Delay << 10, + } + if err := registry.Init(ctx); !a.So(err, should.BeNil) { + t.FailNow() + } + + paths := []string{"ids", "provider", "format", "base_topic"} + format := "json" + format2 := "xml" + baseTopic := "app1.ps1" + baseTopic2 := "app1.ps2" + + createPubSubFunc := func(ps *ttnpb.ApplicationPubSub, + ) (*ttnpb.ApplicationPubSub, []string, error) { // nolint: unparam + a.So(ps, should.BeNil) + return &ttnpb.ApplicationPubSub{ + Ids: ids1, + Provider: provider, + Format: format, + }, paths, nil + } + updatePubSubFunc := func(ps *ttnpb.ApplicationPubSub, + ) (*ttnpb.ApplicationPubSub, []string, error) { // nolint: unparam + a.So(ps, should.NotBeNil) + return &ttnpb.ApplicationPubSub{ + Ids: ids1, + Provider: provider, + Format: format, + BaseTopic: baseTopic, + }, paths, nil + } + updateFieldMaskPubSubFunc := func(ps *ttnpb.ApplicationPubSub, + ) (*ttnpb.ApplicationPubSub, []string, error) { // nolint: unparam + a.So(ps, should.NotBeNil) + return &ttnpb.ApplicationPubSub{ + Ids: ids1, + Provider: provider, + Format: format2, + BaseTopic: baseTopic2, + }, []string{"ids", "base_topic"}, nil + } + deletePubSubFunc := func(ps *ttnpb.ApplicationPubSub, + ) (*ttnpb.ApplicationPubSub, []string, error) { // nolint: unparam + a.So(ps, should.NotBeNil) + return nil, nil, nil + } + listPubSubFunc := func(ps *ttnpb.ApplicationPubSub, + ) (*ttnpb.ApplicationPubSub, []string, error) { // nolint: unparam + a.So(ps, should.BeNil) + return &ttnpb.ApplicationPubSub{ + Ids: ids2, + Provider: provider, + Format: format, + }, paths, nil + } + + t.Run("GetNonExisting", func(t *testing.T) { + t.Parallel() + a, ctx := test.New(t) + pubsub, err := registry.Get(ctx, ids, []string{"ids"}) + a.So(pubsub, should.BeNil) + a.So(errors.IsNotFound(err), should.BeTrue) + }) + + t.Run("CreateReadUpdateDelete", func(t *testing.T) { + t.Parallel() + a, ctx := test.New(t) + pubsub, err := registry.Set(ctx, ids1, paths, createPubSubFunc) + a.So(err, should.BeNil) + a.So(pubsub, should.NotBeNil) + a.So(pubsub.Ids, should.Resemble, ids1) + a.So(pubsub.Format, should.NotBeNil) + a.So(pubsub.Format, should.Equal, format) + + retrieved, err := registry.Get(ctx, ids1, paths) + a.So(err, should.BeNil) + a.So(retrieved, should.NotBeNil) + a.So(retrieved.Ids, should.Resemble, ids1) + a.So(retrieved.Format, should.NotBeNil) + a.So(retrieved.Format, should.Equal, format) + + updated, err := registry.Set(ctx, ids1, paths, updatePubSubFunc) + a.So(err, should.BeNil) + a.So(updated, should.NotBeNil) + a.So(updated.Ids, should.Resemble, ids1) + a.So(updated.Format, should.NotBeNil) + a.So(updated.Format, should.Equal, format) + a.So(updated.BaseTopic, should.NotBeNil) + a.So(updated.BaseTopic, should.Equal, baseTopic) + + updated2, err := registry.Set(ctx, ids1, paths, updateFieldMaskPubSubFunc) + a.So(err, should.BeNil) + a.So(updated2, should.NotBeNil) + a.So(updated2.Ids, should.Resemble, ids1) + a.So(updated2.Format, should.NotBeNil) + a.So(updated2.Format, should.Equal, format) + a.So(updated2.BaseTopic, should.NotBeNil) + a.So(updated2.BaseTopic, should.Equal, baseTopic2) + + deleted, err := registry.Set(ctx, ids1, []string{"ids"}, deletePubSubFunc) + a.So(err, should.BeNil) + a.So(deleted, should.BeNil) + }) + + t.Run("List", func(t *testing.T) { + t.Parallel() + a, ctx := test.New(t) + pubsub, err := registry.Set(ctx, ids2, paths, listPubSubFunc) + a.So(err, should.BeNil) + a.So(pubsub, should.NotBeNil) + a.So(pubsub.Ids, should.Resemble, ids2) + a.So(pubsub.Format, should.NotBeNil) + a.So(pubsub.Format, should.Equal, format) + + pubsubs, err := registry.List(ctx, ids2.ApplicationIds, paths) + a.So(err, should.BeNil) + a.So(pubsubs, should.HaveLength, 1) + a.So(pubsubs[0], should.NotBeNil) + a.So(pubsubs[0].Ids, should.Resemble, ids2) + a.So(pubsubs[0].Format, should.NotBeNil) + a.So(pubsubs[0].Format, should.Equal, format) + }) + + t.Run("Pagination", func(t *testing.T) { + t.Parallel() + a, ctx := test.New(t) + + ttnredis.SetPaginationDefaults(ttnredis.PaginationDefaults{DefaultLimit: 10}) + + for i := 1; i < 21; i++ { + ids3 := &ttnpb.ApplicationPubSubIdentifiers{ + ApplicationIds: &ttnpb.ApplicationIdentifiers{ + ApplicationId: "myapp-pagination", + }, + PubSubId: fmt.Sprintf("pubsub-%02d", i), + } + + pubsub, err := registry.Set( + ctx, + ids3, + paths, + func(ps *ttnpb.ApplicationPubSub) (*ttnpb.ApplicationPubSub, []string, error) { + a.So(ps, should.BeNil) + return &ttnpb.ApplicationPubSub{ + Ids: ids3, + Provider: provider, + Format: format, + }, paths, nil + }, + ) + a.So(err, should.BeNil) + a.So(pubsub, should.NotBeNil) + } + + for _, tc := range []struct { + limit uint32 + page uint32 + idLow string + idHigh string + length int + }{ + { + limit: 10, + page: 0, + idLow: "pubsub-01", + idHigh: "pubsub-10", + length: 10, + }, + { + limit: 10, + page: 1, + idLow: "pubsub-01", + idHigh: "pubsub-10", + length: 10, + }, + { + limit: 10, + page: 2, + idLow: "pubsub-11", + idHigh: "pubsub-20", + length: 10, + }, + { + limit: 10, + page: 3, + length: 0, + }, + { + limit: 0, + page: 0, + idLow: "pubsub-01", + idHigh: "pubsub-10", + length: 10, + }, + } { + t.Run(fmt.Sprintf("limit:%v_page:%v", tc.limit, tc.page), + func(t *testing.T) { + t.Parallel() + var total int64 + paginationCtx := registry.WithPagination(ctx, tc.limit, tc.page, &total) + + pubsubs, err := registry.List(paginationCtx, &ttnpb.ApplicationIdentifiers{ + ApplicationId: "myapp-pagination", + }, + paths, + ) + a.So(err, should.BeNil) + a.So(pubsubs, should.HaveLength, tc.length) + a.So(total, should.Equal, 20) + for _, pubsub := range pubsubs { + a.So(pubsub.Ids.PubSubId, should.BeBetweenOrEqual, tc.idLow, tc.idHigh) + } + }) + } + }) +} diff --git a/pkg/applicationserver/io/pubsub/registry.go b/pkg/applicationserver/io/pubsub/registry.go index b4a2a5b5c6..21a25b7af5 100644 --- a/pkg/applicationserver/io/pubsub/registry.go +++ b/pkg/applicationserver/io/pubsub/registry.go @@ -30,4 +30,6 @@ type Registry interface { List(ctx context.Context, ids *ttnpb.ApplicationIdentifiers, paths []string) ([]*ttnpb.ApplicationPubSub, error) // Set creates, updates or deletes the pub/sub integration by its identifiers. Set(ctx context.Context, ids *ttnpb.ApplicationPubSubIdentifiers, paths []string, f func(*ttnpb.ApplicationPubSub) (*ttnpb.ApplicationPubSub, []string, error)) (*ttnpb.ApplicationPubSub, error) + // WithPagination returns a new context with pagination parameters. + WithPagination(ctx context.Context, limit uint32, page uint32, total *int64) context.Context } diff --git a/pkg/applicationserver/io/web/redis/registry.go b/pkg/applicationserver/io/web/redis/registry.go index 3ecd739818..937ed2b01a 100644 --- a/pkg/applicationserver/io/web/redis/registry.go +++ b/pkg/applicationserver/io/web/redis/registry.go @@ -17,6 +17,7 @@ package redis import ( "context" "regexp" + "runtime/trace" "strings" "time" @@ -99,19 +100,55 @@ func (r WebhookRegistry) Get(ctx context.Context, ids *ttnpb.ApplicationWebhookI func (r WebhookRegistry) List(ctx context.Context, ids *ttnpb.ApplicationIdentifiers, paths []string) ([]*ttnpb.ApplicationWebhook, error) { var pbs []*ttnpb.ApplicationWebhook appUID := unique.ID(ctx, ids) - err := ttnredis.FindProtos(ctx, r.Redis, r.appKey(appUID), r.makeIDKeyFunc(appUID)).Range(func() (proto.Message, func() (bool, error)) { - pb := &ttnpb.ApplicationWebhook{} - return pb, func() (bool, error) { - pb, err := applyWebhookFieldMask(nil, pb, appendImplicitWebhookGetPaths(paths...)...) + uidKey := r.appKey(appUID) + + opts := []ttnredis.FindProtosOption{} + limit, offset := ttnredis.PaginationLimitAndOffsetFromContext(ctx) + if limit != 0 { + opts = append(opts, + ttnredis.FindProtosSorted(true), + ttnredis.FindProtosWithOffsetAndCount(offset, limit), + ) + } + + rangeProtos := func(c redis.Cmdable) error { + return ttnredis.FindProtos(ctx, c, uidKey, r.makeIDKeyFunc(appUID), opts...).Range( + func() (proto.Message, func() (bool, error)) { + pb := &ttnpb.ApplicationWebhook{} + return pb, func() (bool, error) { + pb, err := applyWebhookFieldMask(nil, pb, appendImplicitWebhookGetPaths(paths...)...) + if err != nil { + return false, err + } + pbs = append(pbs, pb) + return true, nil + } + }) + } + + defer trace.StartRegion(ctx, "list webhooks by application id").End() + + var err error + if limit != 0 { + var lockerID string + lockerID, err = ttnredis.GenerateLockerID() + if err != nil { + return nil, err + } + err = ttnredis.LockedWatch(ctx, r.Redis, uidKey, lockerID, r.LockTTL, func(tx *redis.Tx) (err error) { + total, err := tx.SCard(ctx, uidKey).Result() if err != nil { - return false, err + return err } - pbs = append(pbs, pb) - return true, nil - } - }) + ttnredis.SetPaginationTotal(ctx, total) + return rangeProtos(tx) + }) + } else { + err = rangeProtos(r.Redis) + } + if err != nil { - return nil, err + return nil, ttnredis.ConvertError(err) } return pbs, nil } @@ -267,3 +304,13 @@ func (r WebhookRegistry) Range(ctx context.Context, paths []string, f func(conte return true, nil }) } + +// WithPagination returns a new context with pagination parameters. +func (WebhookRegistry) WithPagination( + ctx context.Context, + limit uint32, + page uint32, + total *int64, +) context.Context { + return ttnredis.NewContextWithPagination(ctx, int64(limit), int64(page), total) +} diff --git a/pkg/applicationserver/io/web/redis/registry_test.go b/pkg/applicationserver/io/web/redis/registry_test.go new file mode 100644 index 0000000000..696a141e33 --- /dev/null +++ b/pkg/applicationserver/io/web/redis/registry_test.go @@ -0,0 +1,274 @@ +// Copyright © 2024 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 implements a Redis-backed Webhook registry. +package redis + +import ( + "fmt" + "testing" + + "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/util/test" + "go.thethings.network/lorawan-stack/v3/pkg/util/test/assertions/should" +) + +var Timeout = 10 * test.Delay + +func TestWebhookRegistry(t *testing.T) { + t.Parallel() + a, ctx := test.New(t) + cl, flush := test.NewRedis(ctx, "redis_test") + t.Cleanup(func() { + flush() + cl.Close() + }) + + ids := &ttnpb.ApplicationWebhookIdentifiers{ + ApplicationIds: &ttnpb.ApplicationIdentifiers{ + ApplicationId: "myapp-00", + }, + WebhookId: "webhook-00", + } + ids1 := &ttnpb.ApplicationWebhookIdentifiers{ + ApplicationIds: &ttnpb.ApplicationIdentifiers{ + ApplicationId: "myapp-01", + }, + WebhookId: "webhook-01", + } + ids2 := &ttnpb.ApplicationWebhookIdentifiers{ + ApplicationIds: &ttnpb.ApplicationIdentifiers{ + ApplicationId: "myapp-02", + }, + WebhookId: "webhook-02", + } + + registry := &WebhookRegistry{ + Redis: cl, + LockTTL: test.Delay << 10, + } + if err := registry.Init(ctx); !a.So(err, should.BeNil) { + t.FailNow() + } + + paths := []string{"ids", "base_url", "format"} + format := "json" + format2 := "xml" + baseURL := "http://localhost/test1" + baseURL2 := "http://localhost/test2" + + createWebhookFunc := func(ps *ttnpb.ApplicationWebhook, + ) (*ttnpb.ApplicationWebhook, []string, error) { // nolint: unparam + a.So(ps, should.BeNil) + return &ttnpb.ApplicationWebhook{ + Ids: ids1, + Format: format, + BaseUrl: baseURL, + }, paths, nil + } + updateWebhookFunc := func(ps *ttnpb.ApplicationWebhook, + ) (*ttnpb.ApplicationWebhook, []string, error) { // nolint: unparam + a.So(ps, should.NotBeNil) + return &ttnpb.ApplicationWebhook{ + Ids: ids1, + Format: format, + BaseUrl: baseURL, + }, paths, nil + } + updateFieldMaskWebhookFunc := func(ps *ttnpb.ApplicationWebhook, + ) (*ttnpb.ApplicationWebhook, []string, error) { // nolint: unparam + a.So(ps, should.NotBeNil) + return &ttnpb.ApplicationWebhook{ + Ids: ids1, + Format: format2, + BaseUrl: baseURL2, + }, []string{"ids", "base_url"}, nil + } + deleteWebhookFunc := func(ps *ttnpb.ApplicationWebhook, + ) (*ttnpb.ApplicationWebhook, []string, error) { // nolint: unparam + a.So(ps, should.NotBeNil) + return nil, nil, nil + } + listWebhookFunc := func(ps *ttnpb.ApplicationWebhook, + ) (*ttnpb.ApplicationWebhook, []string, error) { // nolint: unparam + a.So(ps, should.BeNil) + return &ttnpb.ApplicationWebhook{ + Ids: ids2, + Format: format, + BaseUrl: baseURL, + }, paths, nil + } + + t.Run("GetNonExisting", func(t *testing.T) { + t.Parallel() + a, ctx := test.New(t) + webhook, err := registry.Get(ctx, ids, []string{"ids"}) + a.So(webhook, should.BeNil) + a.So(errors.IsNotFound(err), should.BeTrue) + }) + + t.Run("CreateReadUpdateDelete", func(t *testing.T) { + t.Parallel() + a, ctx := test.New(t) + webhook, err := registry.Set(ctx, ids1, paths, createWebhookFunc) + a.So(err, should.BeNil) + a.So(webhook, should.NotBeNil) + a.So(webhook.Ids, should.Resemble, ids1) + a.So(webhook.Format, should.NotBeNil) + a.So(webhook.Format, should.Equal, format) + + retrieved, err := registry.Get(ctx, ids1, paths) + a.So(err, should.BeNil) + a.So(retrieved, should.NotBeNil) + a.So(retrieved.Ids, should.Resemble, ids1) + a.So(retrieved.Format, should.NotBeNil) + a.So(retrieved.Format, should.Equal, format) + + updated, err := registry.Set(ctx, ids1, paths, updateWebhookFunc) + a.So(err, should.BeNil) + a.So(updated, should.NotBeNil) + a.So(updated.Ids, should.Resemble, ids1) + a.So(updated.Format, should.NotBeNil) + a.So(updated.Format, should.Equal, format) + a.So(updated.BaseUrl, should.NotBeNil) + a.So(updated.BaseUrl, should.Equal, baseURL) + + updated2, err := registry.Set(ctx, ids1, paths, updateFieldMaskWebhookFunc) + a.So(err, should.BeNil) + a.So(updated2, should.NotBeNil) + a.So(updated2.Ids, should.Resemble, ids1) + a.So(updated2.Format, should.NotBeNil) + a.So(updated2.Format, should.Equal, format) + a.So(updated2.BaseUrl, should.NotBeNil) + a.So(updated2.BaseUrl, should.Equal, baseURL2) + + deleted, err := registry.Set(ctx, ids1, []string{"ids"}, deleteWebhookFunc) + a.So(err, should.BeNil) + a.So(deleted, should.BeNil) + }) + + t.Run("List", func(t *testing.T) { + t.Parallel() + a, ctx := test.New(t) + webhook, err := registry.Set(ctx, ids2, paths, listWebhookFunc) + a.So(err, should.BeNil) + a.So(webhook, should.NotBeNil) + a.So(webhook.Ids, should.Resemble, ids2) + a.So(webhook.Format, should.NotBeNil) + a.So(webhook.Format, should.Equal, format) + + webhooks, err := registry.List(ctx, ids2.ApplicationIds, paths) + a.So(err, should.BeNil) + a.So(webhooks, should.HaveLength, 1) + a.So(webhooks[0], should.NotBeNil) + a.So(webhooks[0].Ids, should.Resemble, ids2) + a.So(webhooks[0].Format, should.NotBeNil) + a.So(webhooks[0].Format, should.Equal, format) + }) + + t.Run("Pagination", func(t *testing.T) { + t.Parallel() + a, ctx := test.New(t) + + ttnredis.SetPaginationDefaults(ttnredis.PaginationDefaults{DefaultLimit: 10}) + + for i := 1; i < 21; i++ { + ids3 := &ttnpb.ApplicationWebhookIdentifiers{ + ApplicationIds: &ttnpb.ApplicationIdentifiers{ + ApplicationId: "myapp-pagination", + }, + WebhookId: fmt.Sprintf("webhook-%02d", i), + } + + webhook, err := registry.Set( + ctx, + ids3, + paths, + func(ps *ttnpb.ApplicationWebhook) (*ttnpb.ApplicationWebhook, []string, error) { + a.So(ps, should.BeNil) + return &ttnpb.ApplicationWebhook{ + Ids: ids3, + Format: format, + BaseUrl: baseURL, + }, paths, nil + }, + ) + a.So(err, should.BeNil) + a.So(webhook, should.NotBeNil) + } + + for _, tc := range []struct { + limit uint32 + page uint32 + idLow string + idHigh string + length int + }{ + { + limit: 10, + page: 0, + idLow: "webhook-01", + idHigh: "webhook-10", + length: 10, + }, + { + limit: 10, + page: 1, + idLow: "webhook-01", + idHigh: "webhook-10", + length: 10, + }, + { + limit: 10, + page: 2, + idLow: "webhook-11", + idHigh: "webhook-20", + length: 10, + }, + { + limit: 10, + page: 3, + length: 0, + }, + { + limit: 0, + page: 0, + idLow: "webhook-01", + idHigh: "webhook-10", + length: 10, + }, + } { + t.Run(fmt.Sprintf("limit:%v_page:%v", tc.limit, tc.page), + func(t *testing.T) { + t.Parallel() + var total int64 + paginationCtx := registry.WithPagination(ctx, tc.limit, tc.page, &total) + + webhooks, err := registry.List(paginationCtx, &ttnpb.ApplicationIdentifiers{ + ApplicationId: "myapp-pagination", + }, + paths, + ) + a.So(err, should.BeNil) + a.So(webhooks, should.HaveLength, tc.length) + a.So(total, should.Equal, 20) + for _, webhook := range webhooks { + a.So(webhook.Ids.WebhookId, should.BeBetweenOrEqual, tc.idLow, tc.idHigh) + } + }) + } + }) +} diff --git a/pkg/applicationserver/io/web/registry.go b/pkg/applicationserver/io/web/registry.go index df7f43a3f1..7e1823cce9 100644 --- a/pkg/applicationserver/io/web/registry.go +++ b/pkg/applicationserver/io/web/registry.go @@ -30,4 +30,6 @@ type WebhookRegistry interface { Set(ctx context.Context, ids *ttnpb.ApplicationWebhookIdentifiers, paths []string, f func(*ttnpb.ApplicationWebhook) (*ttnpb.ApplicationWebhook, []string, error)) (*ttnpb.ApplicationWebhook, error) // Range ranges over the webhooks and calls the callback function, until false is returned. Range(ctx context.Context, paths []string, f func(context.Context, *ttnpb.ApplicationIdentifiers, *ttnpb.ApplicationWebhook) bool) error + // WithPagination returns a new context with pagination parameters. + WithPagination(ctx context.Context, limit uint32, page uint32, total *int64) context.Context } diff --git a/pkg/auth/rights/cache_test.go b/pkg/auth/rights/cache_test.go index 804999d666..009ee85259 100644 --- a/pkg/auth/rights/cache_test.go +++ b/pkg/auth/rights/cache_test.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package rights +package rights_test import ( "context" @@ -20,6 +20,7 @@ import ( "time" "github.com/smarty/assertions" + "go.thethings.network/lorawan-stack/v3/pkg/auth/rights" "go.thethings.network/lorawan-stack/v3/pkg/errors" "go.thethings.network/lorawan-stack/v3/pkg/util/test" "go.thethings.network/lorawan-stack/v3/pkg/util/test/assertions/should" @@ -32,9 +33,9 @@ func timeTravel(d time.Duration) { } func TestCache(t *testing.T) { - now = func() time.Time { + rights.OverrideNowForTest(func() time.Time { return currentTime - } + }) a := assertions.New(t) @@ -42,7 +43,10 @@ func TestCache(t *testing.T) { mockFetcher := &mockFetcher{} - c := NewInMemoryCache(mockFetcher, 5*time.Minute, time.Minute).(*inMemoryCache) + c, ok := rights.NewInMemoryCache(mockFetcher, 5*time.Minute, time.Minute).(*rights.InMemoryCache) + if !ok { + t.Fatal("Failed to create in-memory cache") + } mockFetcher.authInfoError = mockErr mockFetcher.applicationError = mockErr @@ -114,10 +118,10 @@ func TestCache(t *testing.T) { timeTravel(time.Hour) - c.maybeCleanup() + c.MaybeCleanupTest() - a.So(c.authInfo, should.BeEmpty) - a.So(c.applicationRights, should.BeEmpty) - a.So(c.gatewayRights, should.BeEmpty) - a.So(c.organizationRights, should.BeEmpty) + a.So(c.AuthInfoTest(), should.BeEmpty) + a.So(c.ApplicationRightsTest(), should.BeEmpty) + a.So(c.GatewayRightsTest(), should.BeEmpty) + a.So(c.OrganizationRightsTest(), should.BeEmpty) } diff --git a/pkg/auth/rights/fetcher_test.go b/pkg/auth/rights/fetcher_test.go index 53fd6503ea..c9852c6fad 100644 --- a/pkg/auth/rights/fetcher_test.go +++ b/pkg/auth/rights/fetcher_test.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package rights +package rights_test import ( "context" @@ -21,6 +21,7 @@ import ( "testing" "github.com/smarty/assertions" + "go.thethings.network/lorawan-stack/v3/pkg/auth/rights" "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" @@ -30,7 +31,7 @@ import ( "google.golang.org/protobuf/types/known/emptypb" ) -func fetchEntityRights(ctx context.Context, id string, f EntityFetcher) (res struct { +func fetchEntityRights(ctx context.Context, id string, f rights.EntityFetcher) (res struct { // nolint:unparam AppRights *ttnpb.Rights AppErr error CliRights *ttnpb.Rights @@ -69,7 +70,7 @@ func fetchEntityRights(ctx context.Context, id string, f EntityFetcher) (res str return res } -func fetchAuthInfo(ctx context.Context, f AuthInfoFetcher) (*ttnpb.AuthInfoResponse, error) { +func fetchAuthInfo(ctx context.Context, f rights.AuthInfoFetcher) (*ttnpb.AuthInfoResponse, error) { return f.AuthInfo(ctx) } @@ -177,7 +178,7 @@ func TestEntityFetcherFunc(t *testing.T) { err error } fetcher.err = errors.New("test err") - f := EntityFetcherFunc(func(ctx context.Context, ids *ttnpb.EntityIdentifiers) (*ttnpb.Rights, error) { + f := rights.EntityFetcherFunc(func(ctx context.Context, ids *ttnpb.EntityIdentifiers) (*ttnpb.Rights, error) { fetcher.mu.Lock() defer fetcher.mu.Unlock() fetcher.ctx = append(fetcher.ctx, ctx) @@ -211,7 +212,7 @@ func TestAuthInfoFetcherFunc(t *testing.T) { err error } fetcher.err = errors.New("test err") - f := AuthInfoFetcherFunc(func(ctx context.Context) (*ttnpb.AuthInfoResponse, error) { + f := rights.AuthInfoFetcherFunc(func(ctx context.Context) (*ttnpb.AuthInfoResponse, error) { fetcher.mu.Lock() defer fetcher.mu.Unlock() fetcher.ctx = append(fetcher.ctx, ctx) @@ -252,7 +253,7 @@ func TestAccessFetcher(t *testing.T) { } // Identity Server not available, return Unavailable error. - unavailableFetcher := NewAccessFetcher(func(context.Context) *grpc.ClientConn { + unavailableFetcher := rights.NewAccessFetcher(func(context.Context) *grpc.ClientConn { return nil }, false) _, unavailableAuthInfoErr := fetchAuthInfo(test.Context(), unavailableFetcher) @@ -264,7 +265,7 @@ func TestAccessFetcher(t *testing.T) { a.So(errors.IsUnavailable(unavailableEntityRes.OrgErr), should.BeTrue) a.So(errors.IsUnavailable(unavailableEntityRes.UsrErr), should.BeTrue) - onlySecureFetcher := NewAccessFetcher(func(context.Context) *grpc.ClientConn { + onlySecureFetcher := rights.NewAccessFetcher(func(context.Context) *grpc.ClientConn { return cc }, false) @@ -292,7 +293,7 @@ func TestAccessFetcher(t *testing.T) { a.So(errors.IsUnauthenticated(authEntityRes.OrgErr), should.BeTrue) a.So(errors.IsUnauthenticated(authEntityRes.UsrErr), should.BeTrue) - alsoInsecureFetcher := NewAccessFetcher(func(context.Context) *grpc.ClientConn { + alsoInsecureFetcher := rights.NewAccessFetcher(func(context.Context) *grpc.ClientConn { return cc }, true) diff --git a/pkg/auth/rights/internal_test.go b/pkg/auth/rights/internal_test.go new file mode 100644 index 0000000000..2b764b3244 --- /dev/null +++ b/pkg/auth/rights/internal_test.go @@ -0,0 +1,45 @@ +// Copyright © 2024 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 rights + +import "time" + +var FromContext = fromContext + +func OverrideNowForTest(f func() time.Time) { + now = f +} + +func (r *inMemoryCache) MaybeCleanupTest() { + r.maybeCleanup() +} + +func (r *inMemoryCache) AuthInfoTest() map[cachedReq]*cachedAuthInfoRes { + return r.authInfo +} + +func (r *inMemoryCache) ApplicationRightsTest() map[cachedReq]*cachedRes { + return r.applicationRights +} + +func (r *inMemoryCache) GatewayRightsTest() map[cachedReq]*cachedRes { + return r.gatewayRights +} + +func (r *inMemoryCache) OrganizationRightsTest() map[cachedReq]*cachedRes { + return r.organizationRights +} + +type InMemoryCache = inMemoryCache diff --git a/pkg/auth/rights/mock_test.go b/pkg/auth/rights/mock_test.go index 33e00ba6be..37234e245d 100644 --- a/pkg/auth/rights/mock_test.go +++ b/pkg/auth/rights/mock_test.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package rights +package rights_test import ( "context" diff --git a/pkg/auth/rights/require.go b/pkg/auth/rights/require.go index 067f9089ae..331512e284 100644 --- a/pkg/auth/rights/require.go +++ b/pkg/auth/rights/require.go @@ -82,6 +82,11 @@ var ( "insufficient rights for user `{uid}`", "missing", ) + ErrInsufficientRights = errors.DefinePermissionDenied( + "insufficient_rights", + "insufficient rights for {entity_type} `{uid}`", + "missing", + ) ) func rightsNames(rights ...ttnpb.Right) []string { diff --git a/pkg/auth/rights/require_test.go b/pkg/auth/rights/require_test.go index 3fa812732f..32af8a03a4 100644 --- a/pkg/auth/rights/require_test.go +++ b/pkg/auth/rights/require_test.go @@ -12,13 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License. -package rights +package rights_test import ( "context" "sync" "testing" + "go.thethings.network/lorawan-stack/v3/pkg/auth/rights" "go.thethings.network/lorawan-stack/v3/pkg/errors" "go.thethings.network/lorawan-stack/v3/pkg/ttnpb" "go.thethings.network/lorawan-stack/v3/pkg/unique" @@ -34,16 +35,20 @@ func requireAuthInfo(ctx context.Context) (res struct { }, ) { var wg sync.WaitGroup + var universalErr, isAdminErr error wg.Add(2) go func() { - res.UniversalErr = RequireUniversal(ctx, ttnpb.Right_RIGHT_SEND_INVITES) + universalErr = rights.RequireUniversal(ctx, ttnpb.Right_RIGHT_SEND_INVITES) wg.Done() }() go func() { - res.IsAdminErr = RequireIsAdmin(ctx) + isAdminErr = rights.RequireIsAdmin(ctx) wg.Done() }() wg.Wait() + + res.UniversalErr = universalErr + res.IsAdminErr = isAdminErr return res } @@ -56,38 +61,53 @@ func requireRights(ctx context.Context, id string) (res struct { }, ) { var wg sync.WaitGroup + var ( + appErr error + cliErr error + gtwErr error + orgErr error + usrErr error + ) + wg.Add(5) go func() { - res.AppErr = RequireApplication(ctx, &ttnpb.ApplicationIdentifiers{ + appErr = rights.RequireApplication(ctx, &ttnpb.ApplicationIdentifiers{ ApplicationId: id, }, ttnpb.Right_RIGHT_APPLICATION_INFO) wg.Done() }() go func() { - res.CliErr = RequireClient(ctx, &ttnpb.ClientIdentifiers{ + cliErr = rights.RequireClient(ctx, &ttnpb.ClientIdentifiers{ ClientId: id, }, ttnpb.Right_RIGHT_CLIENT_INFO) wg.Done() }() go func() { - res.GtwErr = RequireGateway(ctx, &ttnpb.GatewayIdentifiers{ + gtwErr = rights.RequireGateway(ctx, &ttnpb.GatewayIdentifiers{ GatewayId: id, }, ttnpb.Right_RIGHT_GATEWAY_INFO) wg.Done() }() go func() { - res.OrgErr = RequireOrganization(ctx, &ttnpb.OrganizationIdentifiers{ + orgErr = rights.RequireOrganization(ctx, &ttnpb.OrganizationIdentifiers{ OrganizationId: id, }, ttnpb.Right_RIGHT_ORGANIZATION_INFO) wg.Done() }() go func() { - res.UsrErr = RequireUser(ctx, &ttnpb.UserIdentifiers{ + usrErr = rights.RequireUser(ctx, &ttnpb.UserIdentifiers{ UserId: id, }, ttnpb.Right_RIGHT_USER_INFO) wg.Done() }() wg.Wait() + + res.AppErr = appErr + res.CliErr = cliErr + res.GtwErr = gtwErr + res.OrgErr = orgErr + res.UsrErr = usrErr + return res } @@ -96,59 +116,59 @@ func TestRequire(t *testing.T) { a, ctx := test.New(t) a.So(func() { - _ = RequireUniversal(ctx, ttnpb.Right_RIGHT_SEND_INVITES) + _ = rights.RequireUniversal(ctx, ttnpb.Right_RIGHT_SEND_INVITES) }, should.Panic) a.So(func() { - _ = RequireIsAdmin(ctx) + _ = rights.RequireIsAdmin(ctx) }, should.Panic) a.So(func() { - _ = RequireApplication(ctx, &ttnpb.ApplicationIdentifiers{}, ttnpb.Right_RIGHT_APPLICATION_INFO) + _ = rights.RequireApplication(ctx, &ttnpb.ApplicationIdentifiers{}, ttnpb.Right_RIGHT_APPLICATION_INFO) }, should.Panic) a.So(func() { - _ = RequireClient(ctx, &ttnpb.ClientIdentifiers{}, ttnpb.Right_RIGHT_CLIENT_INFO) + _ = rights.RequireClient(ctx, &ttnpb.ClientIdentifiers{}, ttnpb.Right_RIGHT_CLIENT_INFO) }, should.Panic) a.So(func() { - _ = RequireGateway(ctx, &ttnpb.GatewayIdentifiers{}, ttnpb.Right_RIGHT_GATEWAY_INFO) + _ = rights.RequireGateway(ctx, &ttnpb.GatewayIdentifiers{}, ttnpb.Right_RIGHT_GATEWAY_INFO) }, should.Panic) a.So(func() { - _ = RequireOrganization(ctx, &ttnpb.OrganizationIdentifiers{}, ttnpb.Right_RIGHT_ORGANIZATION_INFO) + _ = rights.RequireOrganization(ctx, &ttnpb.OrganizationIdentifiers{}, ttnpb.Right_RIGHT_ORGANIZATION_INFO) }, should.Panic) a.So(func() { - _ = RequireUser(ctx, &ttnpb.UserIdentifiers{}, ttnpb.Right_RIGHT_USER_INFO) + _ = rights.RequireUser(ctx, &ttnpb.UserIdentifiers{}, ttnpb.Right_RIGHT_USER_INFO) }, should.Panic) var ( fooCtx = ctx fooID = "foo" ) - fooCtx = NewContext(fooCtx, &Rights{ - ApplicationRights: *NewMap(map[string]*ttnpb.Rights{ + fooCtx = rights.NewContext(fooCtx, &rights.Rights{ + ApplicationRights: *rights.NewMap(map[string]*ttnpb.Rights{ unique.ID(fooCtx, &ttnpb.ApplicationIdentifiers{ ApplicationId: fooID, }): ttnpb.RightsFrom(ttnpb.Right_RIGHT_APPLICATION_INFO), }), - ClientRights: *NewMap(map[string]*ttnpb.Rights{ + ClientRights: *rights.NewMap(map[string]*ttnpb.Rights{ unique.ID(fooCtx, &ttnpb.ClientIdentifiers{ ClientId: fooID, }): ttnpb.RightsFrom(ttnpb.Right_RIGHT_CLIENT_INFO), }), - GatewayRights: *NewMap(map[string]*ttnpb.Rights{ + GatewayRights: *rights.NewMap(map[string]*ttnpb.Rights{ unique.ID(fooCtx, &ttnpb.GatewayIdentifiers{ GatewayId: fooID, }): ttnpb.RightsFrom(ttnpb.Right_RIGHT_GATEWAY_INFO), }), - OrganizationRights: *NewMap(map[string]*ttnpb.Rights{ + OrganizationRights: *rights.NewMap(map[string]*ttnpb.Rights{ unique.ID(fooCtx, &ttnpb.OrganizationIdentifiers{ OrganizationId: fooID, }): ttnpb.RightsFrom(ttnpb.Right_RIGHT_ORGANIZATION_INFO), }), - UserRights: *NewMap(map[string]*ttnpb.Rights{ + UserRights: *rights.NewMap(map[string]*ttnpb.Rights{ unique.ID(fooCtx, &ttnpb.UserIdentifiers{ UserId: fooID, }): ttnpb.RightsFrom(ttnpb.Right_RIGHT_USER_INFO), }), }) - fooCtx = NewContextWithAuthInfo(fooCtx, &ttnpb.AuthInfoResponse{ + fooCtx = rights.NewContextWithAuthInfo(fooCtx, &ttnpb.AuthInfoResponse{ UniversalRights: ttnpb.RightsFrom(ttnpb.Right_RIGHT_SEND_INVITES), IsAdmin: true, }) @@ -164,7 +184,7 @@ func TestRequire(t *testing.T) { a.So(fooEntityRes.UsrErr, should.BeNil) mockErr := errors.New("mock") - errFetchCtx := NewContextWithFetcher(test.Context(), &mockFetcher{ + errFetchCtx := rights.NewContextWithFetcher(test.Context(), &mockFetcher{ authInfoError: mockErr, applicationError: mockErr, clientError: mockErr, @@ -183,7 +203,7 @@ func TestRequire(t *testing.T) { a.So(errFetchEntityRes.UsrErr, should.Resemble, mockErr) errPermissionDenied := status.New(codes.PermissionDenied, "permission denied").Err() - permissionDeniedFetchCtx := NewContextWithFetcher(test.Context(), &mockFetcher{ + permissionDeniedFetchCtx := rights.NewContextWithFetcher(test.Context(), &mockFetcher{ authInfoError: errPermissionDenied, applicationError: errPermissionDenied, clientError: errPermissionDenied, diff --git a/pkg/auth/rights/rights_test.go b/pkg/auth/rights/rights_test.go index 6736baaf89..286ac0f5fe 100644 --- a/pkg/auth/rights/rights_test.go +++ b/pkg/auth/rights/rights_test.go @@ -12,12 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -package rights +package rights_test import ( "testing" "github.com/smarty/assertions" + "go.thethings.network/lorawan-stack/v3/pkg/auth/rights" "go.thethings.network/lorawan-stack/v3/pkg/ttnpb" "go.thethings.network/lorawan-stack/v3/pkg/unique" "go.thethings.network/lorawan-stack/v3/pkg/util/test" @@ -28,60 +29,60 @@ func TestContext(t *testing.T) { a := assertions.New(t) ctx := test.Context() - rights, ok := fromContext(ctx) + r, ok := rights.FromContext(ctx) a.So(ok, should.BeFalse) - a.So(rights, should.Resemble, &Rights{}) + a.So(r, should.Resemble, &rights.Rights{}) - fooRights := &Rights{ - ApplicationRights: *NewMap(map[string]*ttnpb.Rights{ + fooRights := &rights.Rights{ + ApplicationRights: *rights.NewMap(map[string]*ttnpb.Rights{ unique.ID(ctx, &ttnpb.ApplicationIdentifiers{ ApplicationId: "foo-app", }): ttnpb.RightsFrom(ttnpb.Right_RIGHT_APPLICATION_INFO), }), - ClientRights: *NewMap(map[string]*ttnpb.Rights{ + ClientRights: *rights.NewMap(map[string]*ttnpb.Rights{ unique.ID(ctx, &ttnpb.ClientIdentifiers{ ClientId: "foo-cli", }): ttnpb.RightsFrom(ttnpb.Right_RIGHT_CLIENT_INFO), }), - GatewayRights: *NewMap(map[string]*ttnpb.Rights{ + GatewayRights: *rights.NewMap(map[string]*ttnpb.Rights{ unique.ID(ctx, &ttnpb.GatewayIdentifiers{ GatewayId: "foo-gtw", }): ttnpb.RightsFrom(ttnpb.Right_RIGHT_GATEWAY_INFO), }), - OrganizationRights: *NewMap(map[string]*ttnpb.Rights{ + OrganizationRights: *rights.NewMap(map[string]*ttnpb.Rights{ unique.ID(ctx, &ttnpb.OrganizationIdentifiers{ OrganizationId: "foo-org", }): ttnpb.RightsFrom(ttnpb.Right_RIGHT_ORGANIZATION_INFO), }), - UserRights: *NewMap(map[string]*ttnpb.Rights{ + UserRights: *rights.NewMap(map[string]*ttnpb.Rights{ unique.ID(ctx, &ttnpb.UserIdentifiers{ UserId: "foo-usr", }): ttnpb.RightsFrom(ttnpb.Right_RIGHT_USER_INFO), }), } - ctx = NewContext(ctx, fooRights) + ctx = rights.NewContext(ctx, fooRights) - rights, ok = fromContext(ctx) + r, ok = rights.FromContext(ctx) a.So(ok, should.BeTrue) - a.So(rights, should.Resemble, fooRights) - a.So(rights.IncludesApplicationRights( + a.So(r, should.Resemble, fooRights) + a.So(r.IncludesApplicationRights( unique.ID(ctx, &ttnpb.ApplicationIdentifiers{ApplicationId: "foo-app"}), ttnpb.Right_RIGHT_APPLICATION_INFO, ), should.BeTrue) - a.So(rights.IncludesClientRights( + a.So(r.IncludesClientRights( unique.ID(ctx, &ttnpb.ClientIdentifiers{ClientId: "foo-cli"}), ttnpb.Right_RIGHT_CLIENT_INFO, ), should.BeTrue) - a.So(rights.IncludesGatewayRights( + a.So(r.IncludesGatewayRights( unique.ID(ctx, &ttnpb.GatewayIdentifiers{GatewayId: "foo-gtw"}), ttnpb.Right_RIGHT_GATEWAY_INFO, ), should.BeTrue) - a.So(rights.IncludesOrganizationRights( + a.So(r.IncludesOrganizationRights( unique.ID(ctx, &ttnpb.OrganizationIdentifiers{OrganizationId: "foo-org"}), ttnpb.Right_RIGHT_ORGANIZATION_INFO, ), should.BeTrue) - a.So(rights.IncludesUserRights( + a.So(r.IncludesUserRights( unique.ID(ctx, &ttnpb.UserIdentifiers{UserId: "foo-usr"}), ttnpb.Right_RIGHT_USER_INFO, ), should.BeTrue) @@ -91,10 +92,10 @@ func TestMap(t *testing.T) { t.Parallel() a := assertions.New(t) - expectedMap := func(rights map[string]*ttnpb.Rights) *Map { - m := &Map{} - for k, v := range rights { - m.syncMap.Store(k, v) + expectedMap := func(r map[string]*ttnpb.Rights) *rights.Map { + m := &rights.Map{} + for k, v := range r { + m.SetRights(k, v) } return m } @@ -107,19 +108,19 @@ func TestMap(t *testing.T) { for _, tc := range []struct { Name string Rights map[string]*ttnpb.Rights - NewMap func(*testing.T, map[string]*ttnpb.Rights) *Map - Assertion func(*testing.T, map[string]*ttnpb.Rights, *Map) bool + NewMap func(*testing.T, map[string]*ttnpb.Rights) *rights.Map + Assertion func(*testing.T, map[string]*ttnpb.Rights, *rights.Map) bool }{ { Name: "`NewMap` with nil Rights", Rights: nil, - NewMap: func(t *testing.T, r map[string]*ttnpb.Rights) *Map { + NewMap: func(t *testing.T, r map[string]*ttnpb.Rights) *rights.Map { t.Helper() - return NewMap(r) + return rights.NewMap(r) }, - Assertion: func(t *testing.T, _ map[string]*ttnpb.Rights, m *Map) bool { + Assertion: func(t *testing.T, _ map[string]*ttnpb.Rights, m *rights.Map) bool { t.Helper() - return a.So(m, should.Resemble, &Map{}) + return a.So(m, should.Resemble, &rights.Map{}) }, }, @@ -129,11 +130,11 @@ func TestMap(t *testing.T) { fooID: ttnpb.RightsFrom(ttnpb.Right_RIGHT_APPLICATION_INFO), barID: ttnpb.RightsFrom(ttnpb.Right_RIGHT_APPLICATION_INFO), }, - NewMap: func(t *testing.T, r map[string]*ttnpb.Rights) *Map { + NewMap: func(t *testing.T, r map[string]*ttnpb.Rights) *rights.Map { t.Helper() - return NewMap(r) + return rights.NewMap(r) }, - Assertion: func(t *testing.T, r map[string]*ttnpb.Rights, m *Map) bool { + Assertion: func(t *testing.T, r map[string]*ttnpb.Rights, m *rights.Map) bool { t.Helper() for k, v := range r { got, ok := m.GetRights(k) @@ -154,11 +155,11 @@ func TestMap(t *testing.T) { fooID: ttnpb.RightsFrom(ttnpb.Right_RIGHT_APPLICATION_INFO), barID: ttnpb.RightsFrom(ttnpb.Right_RIGHT_APPLICATION_DEVICES_READ), }, - NewMap: func(t *testing.T, r map[string]*ttnpb.Rights) *Map { + NewMap: func(t *testing.T, r map[string]*ttnpb.Rights) *rights.Map { t.Helper() return expectedMap(r) }, - Assertion: func(t *testing.T, r map[string]*ttnpb.Rights, m *Map) bool { + Assertion: func(t *testing.T, r map[string]*ttnpb.Rights, m *rights.Map) bool { t.Helper() for k, v := range r { got, ok := m.GetRights(k) @@ -179,15 +180,15 @@ func TestMap(t *testing.T) { fooID: ttnpb.RightsFrom(ttnpb.Right_RIGHT_APPLICATION_INFO), barID: ttnpb.RightsFrom(ttnpb.Right_RIGHT_APPLICATION_DEVICES_READ), }, - NewMap: func(t *testing.T, r map[string]*ttnpb.Rights) *Map { + NewMap: func(t *testing.T, r map[string]*ttnpb.Rights) *rights.Map { t.Helper() - m := &Map{} + m := &rights.Map{} for k, v := range r { m.SetRights(k, v) } return m }, - Assertion: func(t *testing.T, r map[string]*ttnpb.Rights, m *Map) bool { + Assertion: func(t *testing.T, r map[string]*ttnpb.Rights, m *rights.Map) bool { t.Helper() for k, v := range r { got, ok := m.GetRights(k) @@ -207,11 +208,11 @@ func TestMap(t *testing.T) { Rights: map[string]*ttnpb.Rights{ fooID: ttnpb.RightsFrom(ttnpb.Right_RIGHT_APPLICATION_INFO), }, - NewMap: func(t *testing.T, r map[string]*ttnpb.Rights) *Map { + NewMap: func(t *testing.T, r map[string]*ttnpb.Rights) *rights.Map { t.Helper() return expectedMap(r) }, - Assertion: func(t *testing.T, _ map[string]*ttnpb.Rights, m *Map) bool { + Assertion: func(t *testing.T, _ map[string]*ttnpb.Rights, m *rights.Map) bool { t.Helper() got, ok := m.GetRights(wrongID) return a.So(got, should.BeNil) && a.So(ok, should.BeFalse) diff --git a/pkg/crypto/cryptoutil/keyservice_cache_test.go b/pkg/crypto/cryptoutil/keyservice_cache_test.go index 40d849e433..6848dbe458 100644 --- a/pkg/crypto/cryptoutil/keyservice_cache_test.go +++ b/pkg/crypto/cryptoutil/keyservice_cache_test.go @@ -77,7 +77,7 @@ func TestCacheUsed(t *testing.T) { m := &mockKeyService{} ctx := test.Context() - ck := NewCacheKeyService(m, test.Delay, 1) + ck := NewCacheKeyService(m, test.Delay<<4, 1) // Cache is empty, expect a miss m.results.key = []byte{0x01, 0x02, 0x03} diff --git a/pkg/email/dir/dir_test.go b/pkg/email/dir/dir_test.go index 4a7708d533..d633cd4926 100644 --- a/pkg/email/dir/dir_test.go +++ b/pkg/email/dir/dir_test.go @@ -20,6 +20,7 @@ import ( "testing" "go.thethings.network/lorawan-stack/v3/pkg/email" + "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" ) @@ -40,7 +41,7 @@ func TestMailDir(t *testing.T) { a.So(err, should.BeNil) err = mailer.Send(&email.Message{ - TemplateName: "irrelevant", + TemplateName: ttnpb.GetNotificationTypeString(ttnpb.NotificationType_UNKNOWN), RecipientName: "John Doe", RecipientAddress: "john.doe@example.com", Subject: "Email Subject", diff --git a/pkg/email/email_test.go b/pkg/email/email_test.go index 2af1fea382..15a98a5769 100644 --- a/pkg/email/email_test.go +++ b/pkg/email/email_test.go @@ -40,7 +40,7 @@ func TestEmail(t *testing.T) { registry := email.NewTemplateRegistry() welcomeEmailTemplate, err := email.NewTemplateFS( - os.DirFS("testdata"), "welcome", + os.DirFS("testdata"), ttnpb.GetNotificationTypeString(ttnpb.NotificationType_UNKNOWN), email.FSTemplate{ SubjectTemplate: "Welcome to {{ .Network.Name }}", HTMLTemplateBaseFile: "base.html", @@ -53,9 +53,8 @@ func TestEmail(t *testing.T) { a.So(err, should.BeNil) registry.RegisterTemplate(welcomeEmailTemplate) - - a.So(registry.RegisteredTemplates(), should.Contain, "welcome") - returnedTemplate := registry.GetTemplate(ctx, "welcome") + a.So(registry.RegisteredTemplates(), should.Contain, "unknown") + returnedTemplate := registry.GetTemplate(ctx, ttnpb.GetNotificationTypeString(ttnpb.NotificationType_UNKNOWN)) for i, template := range []*email.Template{welcomeEmailTemplate, returnedTemplate} { template := template diff --git a/pkg/email/sendgrid/sendgrid_test.go b/pkg/email/sendgrid/sendgrid_test.go index a5a344bd6b..33be1b1028 100644 --- a/pkg/email/sendgrid/sendgrid_test.go +++ b/pkg/email/sendgrid/sendgrid_test.go @@ -21,6 +21,7 @@ import ( "github.com/smarty/assertions" "go.thethings.network/lorawan-stack/v3/pkg/email" "go.thethings.network/lorawan-stack/v3/pkg/log" + "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" ) @@ -46,7 +47,7 @@ func TestSendGrid(t *testing.T) { a.So(err, should.BeNil) err = sg.Send(&email.Message{ - TemplateName: "test", + TemplateName: ttnpb.GetNotificationTypeString(ttnpb.NotificationType_UNKNOWN), RecipientName: "John Doe", RecipientAddress: "john.doe@example.com", Subject: "Testing SendGrid", diff --git a/pkg/email/smtp/smtp_test.go b/pkg/email/smtp/smtp_test.go index 1764fec15b..f0d579d53f 100644 --- a/pkg/email/smtp/smtp_test.go +++ b/pkg/email/smtp/smtp_test.go @@ -22,6 +22,7 @@ import ( "github.com/smarty/assertions" "go.thethings.network/lorawan-stack/v3/pkg/email" "go.thethings.network/lorawan-stack/v3/pkg/log" + "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" ) @@ -64,7 +65,7 @@ func TestSMTP(t *testing.T) { a.So(err, should.BeNil) mail := &email.Message{ - TemplateName: "test", + TemplateName: ttnpb.GetNotificationTypeString(ttnpb.NotificationType_UNKNOWN), RecipientName: "John Doe", RecipientAddress: "john.doe@example.com", Subject: "Testing SMTP", diff --git a/pkg/email/template.go b/pkg/email/template.go index 9e961eaac5..a324b0c8a8 100644 --- a/pkg/email/template.go +++ b/pkg/email/template.go @@ -43,7 +43,7 @@ func newContextWithTemplateRegistry(parent context.Context, reg TemplateRegistry // TemplateRegistry keeps track of email templates. type TemplateRegistry interface { - RegisteredTemplates() []string + RegisteredTemplates() []*ttnpb.NotificationType GetTemplate(ctx context.Context, name string) *Template } diff --git a/pkg/email/templates/api_key_changed.go b/pkg/email/templates/api_key_changed.go index ad738b8769..31a8b2d9e0 100644 --- a/pkg/email/templates/api_key_changed.go +++ b/pkg/email/templates/api_key_changed.go @@ -24,7 +24,7 @@ import ( func init() { tmpl, err := email.NewTemplateFS( - fsys, "api_key_changed", + fsys, ttnpb.GetNotificationTypeString(ttnpb.NotificationType_API_KEY_CHANGED), email.FSTemplate{ SubjectTemplate: "An API key of your {{ .Notification.EntityIds.EntityType }} has been changed", HTMLTemplateBaseFile: "base.html.tmpl", @@ -36,10 +36,11 @@ func init() { panic(err) } email.RegisterTemplate(tmpl) - email.RegisterNotification("api_key_changed", &email.NotificationBuilder{ - EmailTemplateName: "api_key_changed", - DataBuilder: newAPIKeyChangedData, - }) + email.RegisterNotification( + ttnpb.GetNotificationTypeString(ttnpb.NotificationType_API_KEY_CHANGED), &email.NotificationBuilder{ + EmailTemplateName: ttnpb.GetNotificationTypeString(ttnpb.NotificationType_API_KEY_CHANGED), + DataBuilder: newAPIKeyChangedData, + }) } func newAPIKeyChangedData(_ context.Context, data email.NotificationTemplateData) (email.NotificationTemplateData, error) { diff --git a/pkg/email/templates/api_key_created.go b/pkg/email/templates/api_key_created.go index 2abb6bc725..9b89578c04 100644 --- a/pkg/email/templates/api_key_created.go +++ b/pkg/email/templates/api_key_created.go @@ -24,7 +24,7 @@ import ( func init() { tmpl, err := email.NewTemplateFS( - fsys, "api_key_created", + fsys, ttnpb.GetNotificationTypeString(ttnpb.NotificationType_API_KEY_CREATED), email.FSTemplate{ SubjectTemplate: "A new API key has been created for your {{ .Notification.EntityIds.EntityType }}", HTMLTemplateBaseFile: "base.html.tmpl", @@ -36,10 +36,11 @@ func init() { panic(err) } email.RegisterTemplate(tmpl) - email.RegisterNotification("api_key_created", &email.NotificationBuilder{ - EmailTemplateName: "api_key_created", - DataBuilder: newAPIKeyCreatedData, - }) + email.RegisterNotification( + ttnpb.GetNotificationTypeString(ttnpb.NotificationType_API_KEY_CREATED), &email.NotificationBuilder{ + EmailTemplateName: ttnpb.GetNotificationTypeString(ttnpb.NotificationType_API_KEY_CREATED), + DataBuilder: newAPIKeyCreatedData, + }) } func newAPIKeyCreatedData(_ context.Context, data email.NotificationTemplateData) (email.NotificationTemplateData, error) { diff --git a/pkg/email/templates/client_requested.go b/pkg/email/templates/client_requested.go index 2781352936..bf0a16699d 100644 --- a/pkg/email/templates/client_requested.go +++ b/pkg/email/templates/client_requested.go @@ -23,7 +23,7 @@ import ( func init() { tmpl, err := email.NewTemplateFS( - fsys, "client_requested", + fsys, ttnpb.GetNotificationTypeString(ttnpb.NotificationType_CLIENT_REQUESTED), email.FSTemplate{ SubjectTemplate: "A new OAuth client has been requested", HTMLTemplateBaseFile: "base.html.tmpl", @@ -35,10 +35,11 @@ func init() { panic(err) } email.RegisterTemplate(tmpl) - email.RegisterNotification("client_requested", &email.NotificationBuilder{ - EmailTemplateName: "client_requested", - DataBuilder: newClientRequestedData, - }) + email.RegisterNotification( + ttnpb.GetNotificationTypeString(ttnpb.NotificationType_CLIENT_REQUESTED), &email.NotificationBuilder{ + EmailTemplateName: ttnpb.GetNotificationTypeString(ttnpb.NotificationType_CLIENT_REQUESTED), + DataBuilder: newClientRequestedData, + }) } func newClientRequestedData(_ context.Context, data email.NotificationTemplateData) (email.NotificationTemplateData, error) { diff --git a/pkg/email/templates/collaborator_changed.go b/pkg/email/templates/collaborator_changed.go index f01ba44e1d..4fea57ad3c 100644 --- a/pkg/email/templates/collaborator_changed.go +++ b/pkg/email/templates/collaborator_changed.go @@ -24,7 +24,7 @@ import ( func init() { tmpl, err := email.NewTemplateFS( - fsys, "collaborator_changed", + fsys, ttnpb.GetNotificationTypeString(ttnpb.NotificationType_COLLABORATOR_CHANGED), email.FSTemplate{ SubjectTemplate: "A collaborator of your {{ .Notification.EntityIds.EntityType }} has been changed", HTMLTemplateBaseFile: "base.html.tmpl", @@ -36,10 +36,11 @@ func init() { panic(err) } email.RegisterTemplate(tmpl) - email.RegisterNotification("collaborator_changed", &email.NotificationBuilder{ - EmailTemplateName: "collaborator_changed", - DataBuilder: newCollaboratorChangedData, - }) + email.RegisterNotification( + ttnpb.GetNotificationTypeString(ttnpb.NotificationType_COLLABORATOR_CHANGED), &email.NotificationBuilder{ + EmailTemplateName: ttnpb.GetNotificationTypeString(ttnpb.NotificationType_COLLABORATOR_CHANGED), + DataBuilder: newCollaboratorChangedData, + }) } func newCollaboratorChangedData(_ context.Context, data email.NotificationTemplateData) (email.NotificationTemplateData, error) { diff --git a/pkg/email/templates/entity_state_changed.go b/pkg/email/templates/entity_state_changed.go index cd801f872b..0a37fcda77 100644 --- a/pkg/email/templates/entity_state_changed.go +++ b/pkg/email/templates/entity_state_changed.go @@ -23,7 +23,7 @@ import ( func init() { tmpl, err := email.NewTemplateFS( - fsys, "entity_state_changed", + fsys, ttnpb.GetNotificationTypeString(ttnpb.NotificationType_ENTITY_STATE_CHANGED), email.FSTemplate{ SubjectTemplate: "The state of your {{ .Notification.EntityIds.EntityType }} has been changed", HTMLTemplateBaseFile: "base.html.tmpl", @@ -35,10 +35,11 @@ func init() { panic(err) } email.RegisterTemplate(tmpl) - email.RegisterNotification("entity_state_changed", &email.NotificationBuilder{ - EmailTemplateName: "entity_state_changed", - DataBuilder: newEntityStateChangedData, - }) + email.RegisterNotification( + ttnpb.GetNotificationTypeString(ttnpb.NotificationType_ENTITY_STATE_CHANGED), &email.NotificationBuilder{ + EmailTemplateName: ttnpb.GetNotificationTypeString(ttnpb.NotificationType_ENTITY_STATE_CHANGED), + DataBuilder: newEntityStateChangedData, + }) } func newEntityStateChangedData(_ context.Context, data email.NotificationTemplateData) (email.NotificationTemplateData, error) { diff --git a/pkg/email/templates/invitation.go b/pkg/email/templates/invitation.go index 5dc024f712..969af04c8f 100644 --- a/pkg/email/templates/invitation.go +++ b/pkg/email/templates/invitation.go @@ -23,7 +23,7 @@ import ( func init() { tmpl, err := email.NewTemplateFS( - fsys, "invitation", + fsys, ttnpb.GetNotificationTypeString(ttnpb.NotificationType_INVITATION), email.FSTemplate{ SubjectTemplate: "You have been invited to join {{ .Network.Name }}", HTMLTemplateBaseFile: "base.html.tmpl", diff --git a/pkg/email/templates/login_token.go b/pkg/email/templates/login_token.go index 40616468c3..791e96b160 100644 --- a/pkg/email/templates/login_token.go +++ b/pkg/email/templates/login_token.go @@ -18,11 +18,12 @@ import ( "time" "go.thethings.network/lorawan-stack/v3/pkg/email" + "go.thethings.network/lorawan-stack/v3/pkg/ttnpb" ) func init() { tmpl, err := email.NewTemplateFS( - fsys, "login_token", + fsys, ttnpb.GetNotificationTypeString(ttnpb.NotificationType_LOGIN_TOKEN), email.FSTemplate{ SubjectTemplate: "Your login token for {{ .Network.Name }}", HTMLTemplateBaseFile: "base.html.tmpl", diff --git a/pkg/email/templates/password_changed.go b/pkg/email/templates/password_changed.go index 844d9b769c..92062c4052 100644 --- a/pkg/email/templates/password_changed.go +++ b/pkg/email/templates/password_changed.go @@ -18,11 +18,12 @@ import ( "context" "go.thethings.network/lorawan-stack/v3/pkg/email" + "go.thethings.network/lorawan-stack/v3/pkg/ttnpb" ) func init() { tmpl, err := email.NewTemplateFS( - fsys, "password_changed", + fsys, ttnpb.GetNotificationTypeString(ttnpb.NotificationType_PASSWORD_CHANGED), email.FSTemplate{ SubjectTemplate: "Your password on {{ .Network.Name }} has been changed", HTMLTemplateBaseFile: "base.html.tmpl", @@ -34,10 +35,11 @@ func init() { panic(err) } email.RegisterTemplate(tmpl) - email.RegisterNotification("password_changed", &email.NotificationBuilder{ - EmailTemplateName: "password_changed", - DataBuilder: newPasswordChangedData, - }) + email.RegisterNotification( + ttnpb.GetNotificationTypeString(ttnpb.NotificationType_PASSWORD_CHANGED), &email.NotificationBuilder{ + EmailTemplateName: ttnpb.GetNotificationTypeString(ttnpb.NotificationType_PASSWORD_CHANGED), + DataBuilder: newPasswordChangedData, + }) } func newPasswordChangedData(_ context.Context, data email.NotificationTemplateData) (email.NotificationTemplateData, error) { diff --git a/pkg/email/templates/templates_test.go b/pkg/email/templates/templates_test.go index f1577df4fa..72e3231b6f 100644 --- a/pkg/email/templates/templates_test.go +++ b/pkg/email/templates/templates_test.go @@ -66,7 +66,7 @@ func TestEmailTemplates(t *testing.T) { TemplateData email.TemplateData }{ { - TemplateName: "invitation", + TemplateName: ttnpb.GetNotificationTypeString(ttnpb.NotificationType_INVITATION), TemplateData: &InvitationData{ TemplateData: testTemplateData, SenderIds: usrIDs, @@ -76,7 +76,7 @@ func TestEmailTemplates(t *testing.T) { }, { - TemplateName: "login_token", + TemplateName: ttnpb.GetNotificationTypeString(ttnpb.NotificationType_LOGIN_TOKEN), TemplateData: &LoginTokenData{ TemplateData: testTemplateData, LoginToken: "TOKEN", @@ -85,7 +85,7 @@ func TestEmailTemplates(t *testing.T) { }, { - TemplateName: "temporary_password", + TemplateName: ttnpb.GetNotificationTypeString(ttnpb.NotificationType_TEMPORARY_PASSWORD), TemplateData: &TemporaryPasswordData{ TemplateData: testTemplateData, TemporaryPassword: "TEMPORARY", @@ -94,7 +94,7 @@ func TestEmailTemplates(t *testing.T) { }, { - TemplateName: "validate", + TemplateName: ttnpb.GetNotificationTypeString(ttnpb.NotificationType_VALIDATE), TemplateData: &ValidateData{ TemplateData: testTemplateData, EntityIdentifiers: usrIDs.GetEntityIdentifiers(), @@ -142,7 +142,7 @@ func TestNotificationEmailTemplates(t *testing.T) { for _, notification := range []*ttnpb.Notification{ { EntityIds: appIDs.GetEntityIdentifiers(), - NotificationType: "api_key_changed", + NotificationType: ttnpb.GetNotificationTypeString(ttnpb.NotificationType_API_KEY_CHANGED), Data: ttnpb.MustMarshalAny(&ttnpb.APIKey{ Id: "TEST", Name: "API Key Name", @@ -164,7 +164,7 @@ func TestNotificationEmailTemplates(t *testing.T) { { Id: "with_sender_ids", EntityIds: appIDs.GetEntityIdentifiers(), - NotificationType: "api_key_created", + NotificationType: ttnpb.GetNotificationTypeString(ttnpb.NotificationType_API_KEY_CREATED), Data: ttnpb.MustMarshalAny(&ttnpb.APIKey{ Id: "TEST", Name: "API Key Name", @@ -186,7 +186,7 @@ func TestNotificationEmailTemplates(t *testing.T) { { Id: "with_api_key_name", EntityIds: cliIDs.GetEntityIdentifiers(), - NotificationType: "client_requested", + NotificationType: ttnpb.GetNotificationTypeString(ttnpb.NotificationType_CLIENT_REQUESTED), Data: ttnpb.MustMarshalAny(&ttnpb.CreateClientEmailMessage{ ApiKey: &ttnpb.APIKey{ Name: "My API key Name", @@ -227,7 +227,7 @@ func TestNotificationEmailTemplates(t *testing.T) { { Id: "with_api_key_id", EntityIds: cliIDs.GetEntityIdentifiers(), - NotificationType: "client_requested", + NotificationType: ttnpb.GetNotificationTypeString(ttnpb.NotificationType_CLIENT_REQUESTED), Data: ttnpb.MustMarshalAny(&ttnpb.CreateClientEmailMessage{ ApiKey: &ttnpb.APIKey{ Id: "My API key ID", @@ -266,7 +266,7 @@ func TestNotificationEmailTemplates(t *testing.T) { }, { EntityIds: cliIDs.GetEntityIdentifiers(), - NotificationType: "client_requested", + NotificationType: ttnpb.GetNotificationTypeString(ttnpb.NotificationType_CLIENT_REQUESTED), Data: ttnpb.MustMarshalAny(&ttnpb.CreateClientEmailMessage{ CreateClientRequest: &ttnpb.CreateClientRequest{ Client: &ttnpb.Client{ @@ -303,7 +303,7 @@ func TestNotificationEmailTemplates(t *testing.T) { { EntityIds: appIDs.GetEntityIdentifiers(), - NotificationType: "collaborator_changed", + NotificationType: ttnpb.GetNotificationTypeString(ttnpb.NotificationType_COLLABORATOR_CHANGED), Data: ttnpb.MustMarshalAny(&ttnpb.Collaborator{ Ids: usrIDs.GetOrganizationOrUserIdentifiers(), Rights: []ttnpb.Right{ @@ -318,7 +318,7 @@ func TestNotificationEmailTemplates(t *testing.T) { { EntityIds: cliIDs.GetEntityIdentifiers(), - NotificationType: "entity_state_changed", + NotificationType: ttnpb.GetNotificationTypeString(ttnpb.NotificationType_ENTITY_STATE_CHANGED), Data: ttnpb.MustMarshalAny(&ttnpb.EntityStateChangedNotification{ State: ttnpb.State_STATE_FLAGGED, StateDescription: "This OAuch client has been sending a large number of invalid requests.", @@ -328,12 +328,12 @@ func TestNotificationEmailTemplates(t *testing.T) { { EntityIds: usrIDs.GetEntityIdentifiers(), - NotificationType: "password_changed", + NotificationType: ttnpb.GetNotificationTypeString(ttnpb.NotificationType_PASSWORD_CHANGED), }, { EntityIds: usrIDs.GetEntityIdentifiers(), - NotificationType: "user_requested", + NotificationType: ttnpb.GetNotificationTypeString(ttnpb.NotificationType_USER_REQUESTED), Data: ttnpb.MustMarshalAny(&ttnpb.CreateUserRequest{ User: &ttnpb.User{ Ids: usrIDs, @@ -349,7 +349,6 @@ func TestNotificationEmailTemplates(t *testing.T) { } { notification := ttnpb.Clone(notification) notification.CreatedAt = now - notification.Email = true t.Run(notification.NotificationType, func(t *testing.T) { t.Parallel() a, ctx := test.New(t) diff --git a/pkg/email/templates/temporary_password.go b/pkg/email/templates/temporary_password.go index 1234153132..d5648cd544 100644 --- a/pkg/email/templates/temporary_password.go +++ b/pkg/email/templates/temporary_password.go @@ -18,11 +18,12 @@ import ( "time" "go.thethings.network/lorawan-stack/v3/pkg/email" + "go.thethings.network/lorawan-stack/v3/pkg/ttnpb" ) func init() { tmpl, err := email.NewTemplateFS( - fsys, "temporary_password", + fsys, ttnpb.GetNotificationTypeString(ttnpb.NotificationType_TEMPORARY_PASSWORD), email.FSTemplate{ SubjectTemplate: "Your temporary password for {{ .Network.Name }}", HTMLTemplateBaseFile: "base.html.tmpl", diff --git a/pkg/email/templates/user_requested.go b/pkg/email/templates/user_requested.go index 72c5e83b99..f46f05e14a 100644 --- a/pkg/email/templates/user_requested.go +++ b/pkg/email/templates/user_requested.go @@ -23,7 +23,7 @@ import ( func init() { tmpl, err := email.NewTemplateFS( - fsys, "user_requested", + fsys, ttnpb.GetNotificationTypeString(ttnpb.NotificationType_USER_REQUESTED), email.FSTemplate{ SubjectTemplate: "A new user has requested to join {{ .Network.Name }}", HTMLTemplateBaseFile: "base.html.tmpl", @@ -35,10 +35,11 @@ func init() { panic(err) } email.RegisterTemplate(tmpl) - email.RegisterNotification("user_requested", &email.NotificationBuilder{ - EmailTemplateName: "user_requested", - DataBuilder: newUserRequestedData, - }) + email.RegisterNotification( + ttnpb.GetNotificationTypeString(ttnpb.NotificationType_USER_REQUESTED), &email.NotificationBuilder{ + EmailTemplateName: ttnpb.GetNotificationTypeString(ttnpb.NotificationType_USER_REQUESTED), + DataBuilder: newUserRequestedData, + }) } func newUserRequestedData(_ context.Context, data email.NotificationTemplateData) (email.NotificationTemplateData, error) { diff --git a/pkg/email/templates/validate.go b/pkg/email/templates/validate.go index b39fce3897..e33f570f21 100644 --- a/pkg/email/templates/validate.go +++ b/pkg/email/templates/validate.go @@ -23,7 +23,7 @@ import ( func init() { tmpl, err := email.NewTemplateFS( - fsys, "validate", + fsys, ttnpb.GetNotificationTypeString(ttnpb.NotificationType_VALIDATE), email.FSTemplate{ SubjectTemplate: "Please confirm your email address for {{ .Network.Name }}", HTMLTemplateBaseFile: "base.html.tmpl", diff --git a/pkg/encoding/lorawan/mac.go b/pkg/encoding/lorawan/mac.go index ece8e44004..6338a32470 100644 --- a/pkg/encoding/lorawan/mac.go +++ b/pkg/encoding/lorawan/mac.go @@ -1430,6 +1430,12 @@ var DefaultMACCommands = MACCommandSpec{ UplinkLength: 6, AppendUplink: func(phy band.Band, b []byte, cmd *ttnpb.MACCommand) ([]byte, error) { pld := cmd.GetRelayNotifyNewEndDeviceReq() + if n := len(pld.DevAddr); n != 4 { + return nil, errExpectedLengthEncodedEqual("DevAddr", 4)(n) + } + devAddr := make([]byte, 4) + copyReverse(devAddr, pld.DevAddr) + b = append(b, devAddr...) var powerLevel uint16 if pld.Snr < -20 || pld.Snr > 11 { return nil, errExpectedBetween("SNR", -20, 11)(pld.Snr) @@ -1440,12 +1446,6 @@ var DefaultMACCommands = MACCommandSpec{ } powerLevel |= uint16(-pld.Rssi-15) & 0x7f << 5 b = byteutil.AppendUint16(b, powerLevel, 2) - if n := len(pld.DevAddr); n != 4 { - return nil, errExpectedLengthEncodedEqual("DevAddr", 4)(n) - } - devAddr := make([]byte, 4) - copyReverse(devAddr, pld.DevAddr) - b = append(b, devAddr...) return b, nil }, UnmarshalUplink: newMACUnmarshaler( @@ -1457,11 +1457,11 @@ var DefaultMACCommands = MACCommandSpec{ cmd.Payload = &ttnpb.MACCommand_RelayNotifyNewEndDeviceReq_{ RelayNotifyNewEndDeviceReq: req, } - powerLevel := byteutil.ParseUint16(b[0:2]) + req.DevAddr = make([]byte, 4) + copyReverse(req.DevAddr, b[0:4]) + powerLevel := byteutil.ParseUint16(b[4:6]) req.Snr = int32(powerLevel&0x1f) - 20 req.Rssi = -int32(powerLevel>>5&0x7f) - 15 - req.DevAddr = make([]byte, 4) - copyReverse(req.DevAddr, b[2:6]) return nil }, ), diff --git a/pkg/encoding/lorawan/mac_test.go b/pkg/encoding/lorawan/mac_test.go index 189fd73fc9..c5e4682e28 100644 --- a/pkg/encoding/lorawan/mac_test.go +++ b/pkg/encoding/lorawan/mac_test.go @@ -597,7 +597,7 @@ func TestLoRaWANEncodingMAC(t *testing.T) { Snr: 6, Rssi: -64, }, - []byte{0x46, 0x3a, 0x06, 0x44, 0x43, 0x42, 0x41}, + []byte{0x46, 0x44, 0x43, 0x42, 0x41, 0x3a, 0x06}, true, }, } { diff --git a/pkg/events/events.go b/pkg/events/events.go index 45cc8be130..b1799bddaf 100644 --- a/pkg/events/events.go +++ b/pkg/events/events.go @@ -25,7 +25,9 @@ import ( "strings" "time" + "github.com/getsentry/sentry-go" "go.thethings.network/lorawan-stack/v3/pkg/errors" + sentryerrors "go.thethings.network/lorawan-stack/v3/pkg/errors/sentry" "go.thethings.network/lorawan-stack/v3/pkg/goproto" "go.thethings.network/lorawan-stack/v3/pkg/jsonpb" "go.thethings.network/lorawan-stack/v3/pkg/ttnpb" @@ -176,7 +178,27 @@ func New(ctx context.Context, name, description string, opts ...Option) Event { return (&definition{name: name, description: description}).New(ctx, opts...) } +var errMarshalData = errors.Define("marshal_data", "marshal data") + func marshalData(data any) (anyPB *anypb.Any, err error) { + // TODO: https://github.com/TheThingsIndustries/lorawan-stack-support/issues/1163. + // Remove this after the issue is fixed. + defer func() { + if p := recover(); p != nil { + if pErr, ok := p.(error); ok { + err = errMarshalData.WithCause(pErr) + } else { + err = errMarshalData.WithAttributes("panic", p) + } + event := sentryerrors.NewEvent(err) + sentry.CaptureEvent(event) + } + }() + + return mustMarshalData(data) +} + +func mustMarshalData(data any) (anyPB *anypb.Any, err error) { if protoMessage, ok := data.(proto.Message); ok { anyPB, err = anypb.New(protoMessage) if err != nil { diff --git a/pkg/gatewayconfigurationserver/grpc_test.go b/pkg/gatewayconfigurationserver/grpc_test.go new file mode 100644 index 0000000000..2a9a7afe29 --- /dev/null +++ b/pkg/gatewayconfigurationserver/grpc_test.go @@ -0,0 +1,88 @@ +// Copyright © 2024 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 gatewayconfigurationserver_test + +import ( + "testing" + + "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" + "go.thethings.network/lorawan-stack/v3/pkg/errors" + "go.thethings.network/lorawan-stack/v3/pkg/gatewayconfigurationserver" + "go.thethings.network/lorawan-stack/v3/pkg/ttgc" + "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 TestNoopManagedGCSServer(t *testing.T) { + t.Parallel() + + a, ctx := test.New(t) + + c := componenttest.NewComponent(t, &component.Config{ + ServiceBase: config.ServiceBase{ + GRPC: config.GRPC{ + Listen: ":0", + AllowInsecureForCredentials: true, + }, + TTGC: ttgc.Config{Enabled: false}, + }, + }) + + _, err := gatewayconfigurationserver.New(c, &gatewayconfigurationserver.Config{}) + if !a.So(err, should.BeNil) { + t.FailNow() + } + + componenttest.StartComponent(t, c) + defer c.Close() + + mustHavePeer(ctx, c, ttnpb.ClusterRole_GATEWAY_CONFIGURATION_SERVER) + + client := ttnpb.NewManagedGatewayConfigurationServiceClient(c.LoopbackConn()) + a.So(client, should.NotBeNil) + + managedGateway, err := client.Get(ctx, &ttnpb.GetGatewayRequest{ + GatewayIds: &ttnpb.GatewayIdentifiers{GatewayId: "gtw-id"}, + }) + a.So(managedGateway, should.BeNil) + a.So(errors.IsNotFound(err), should.BeTrue) + + managedGateway, err = client.Update(ctx, &ttnpb.UpdateManagedGatewayRequest{ + Gateway: &ttnpb.ManagedGateway{ + Ids: &ttnpb.GatewayIdentifiers{ + GatewayId: "gtw-id", + }, + }, + }) + a.So(managedGateway, should.BeNil) + a.So(errors.IsNotFound(err), should.BeTrue) + + managedGatewayWiFiAccessPoints, err := client.ScanWiFiAccessPoints(ctx, &ttnpb.GatewayIdentifiers{ + GatewayId: "gtw-id", + }) + a.So(managedGatewayWiFiAccessPoints, should.BeNil) + a.So(errors.IsNotFound(err), should.BeTrue) + + streamEventsClient, err := client.StreamEvents(ctx, &ttnpb.GatewayIdentifiers{GatewayId: "gtw-id"}) + if !a.So(err, should.BeNil) { + t.FailNow() + } + + err = streamEventsClient.RecvMsg(nil) + a.So(errors.IsNotFound(err), should.BeTrue) +} diff --git a/pkg/gatewayconfigurationserver/managed/grpc_disabled.go b/pkg/gatewayconfigurationserver/managed/grpc_disabled.go new file mode 100644 index 0000000000..c946fbe165 --- /dev/null +++ b/pkg/gatewayconfigurationserver/managed/grpc_disabled.go @@ -0,0 +1,158 @@ +// Copyright © 2024 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 managed + +import ( + "context" + + "go.thethings.network/lorawan-stack/v3/pkg/errors" + "go.thethings.network/lorawan-stack/v3/pkg/ttnpb" + "google.golang.org/protobuf/types/known/emptypb" +) + +var errManagedGatewayConfigurationServerNotEnabled = errors.DefineNotFound( + "managed_gateway_configuration_server_not_enabled", "managed gateway configuration server is not enabled", +) + +type noopManagedGCSServer struct { + Component + ttnpb.UnsafeManagedGatewayConfigurationServiceServer +} + +var _ ttnpb.ManagedGatewayConfigurationServiceServer = (*noopManagedGCSServer)(nil) + +// Get implements ttnpb.ManagedGatewayConfigurationServiceServer. +func (*noopManagedGCSServer) Get( + _ context.Context, + _ *ttnpb.GetGatewayRequest, +) (*ttnpb.ManagedGateway, error) { + return nil, errManagedGatewayConfigurationServerNotEnabled.New() +} + +// ScanWiFiAccessPoints implements ttnpb.ManagedGatewayConfigurationServiceServer. +func (*noopManagedGCSServer) ScanWiFiAccessPoints( + _ context.Context, + _ *ttnpb.GatewayIdentifiers, +) (*ttnpb.ManagedGatewayWiFiAccessPoints, error) { + return nil, errManagedGatewayConfigurationServerNotEnabled.New() +} + +// StreamEvents implements ttnpb.ManagedGatewayConfigurationServiceServer. +func (*noopManagedGCSServer) StreamEvents( + _ *ttnpb.GatewayIdentifiers, + _ ttnpb.ManagedGatewayConfigurationService_StreamEventsServer, +) error { + return errManagedGatewayConfigurationServerNotEnabled.New() +} + +// Update implements ttnpb.ManagedGatewayConfigurationServiceServer. +func (*noopManagedGCSServer) Update( + _ context.Context, + _ *ttnpb.UpdateManagedGatewayRequest, +) (*ttnpb.ManagedGateway, error) { + return nil, errManagedGatewayConfigurationServerNotEnabled.New() +} + +type noopManagedGatewayWiFiProfileServer struct { + ttnpb.UnsafeManagedGatewayWiFiProfileConfigurationServiceServer +} + +var _ ttnpb.ManagedGatewayWiFiProfileConfigurationServiceServer = (*noopManagedGatewayWiFiProfileServer)(nil) + +// Create implements ttnpb.ManagedGatewayWiFiProfileConfigurationServiceServer. +func (*noopManagedGatewayWiFiProfileServer) Create( + _ context.Context, + _ *ttnpb.CreateManagedGatewayWiFiProfileRequest, +) (*ttnpb.ManagedGatewayWiFiProfile, error) { + return nil, errManagedGatewayConfigurationServerNotEnabled.New() +} + +// Delete implements ttnpb.ManagedGatewayWiFiProfileConfigurationServiceServer. +func (*noopManagedGatewayWiFiProfileServer) Delete( + _ context.Context, + _ *ttnpb.DeleteManagedGatewayWiFiProfileRequest, +) (*emptypb.Empty, error) { + return nil, errManagedGatewayConfigurationServerNotEnabled.New() +} + +// Get implements ttnpb.ManagedGatewayWiFiProfileConfigurationServiceServer. +func (*noopManagedGatewayWiFiProfileServer) Get( + _ context.Context, + _ *ttnpb.GetManagedGatewayWiFiProfileRequest, +) (*ttnpb.ManagedGatewayWiFiProfile, error) { + return nil, errManagedGatewayConfigurationServerNotEnabled.New() +} + +// List implements ttnpb.ManagedGatewayWiFiProfileConfigurationServiceServer. +func (*noopManagedGatewayWiFiProfileServer) List( + _ context.Context, + _ *ttnpb.ListManagedGatewayWiFiProfilesRequest, +) (*ttnpb.ManagedGatewayWiFiProfiles, error) { + return nil, errManagedGatewayConfigurationServerNotEnabled.New() +} + +// Update implements ttnpb.ManagedGatewayWiFiProfileConfigurationServiceServer. +func (*noopManagedGatewayWiFiProfileServer) Update( + _ context.Context, + _ *ttnpb.UpdateManagedGatewayWiFiProfileRequest, +) (*ttnpb.ManagedGatewayWiFiProfile, error) { + return nil, errManagedGatewayConfigurationServerNotEnabled.New() +} + +type noopManagedGatewayEthernetProfileServer struct { + ttnpb.UnsafeManagedGatewayEthernetProfileConfigurationServiceServer +} + +var _ ttnpb.ManagedGatewayEthernetProfileConfigurationServiceServer = (*noopManagedGatewayEthernetProfileServer)(nil) //nolint:lll + +// Create implements ttnpb.ManagedGatewayEthernetProfileConfigurationServiceServer. +func (*noopManagedGatewayEthernetProfileServer) Create( + _ context.Context, + _ *ttnpb.CreateManagedGatewayEthernetProfileRequest, +) (*ttnpb.ManagedGatewayEthernetProfile, error) { + return nil, errManagedGatewayConfigurationServerNotEnabled.New() +} + +// Delete implements ttnpb.ManagedGatewayEthernetProfileConfigurationServiceServer. +func (*noopManagedGatewayEthernetProfileServer) Delete( + _ context.Context, + _ *ttnpb.DeleteManagedGatewayEthernetProfileRequest, +) (*emptypb.Empty, error) { + return nil, errManagedGatewayConfigurationServerNotEnabled.New() +} + +// Get implements ttnpb.ManagedGatewayEthernetProfileConfigurationServiceServer. +func (*noopManagedGatewayEthernetProfileServer) Get( + _ context.Context, + _ *ttnpb.GetManagedGatewayEthernetProfileRequest, +) (*ttnpb.ManagedGatewayEthernetProfile, error) { + return nil, errManagedGatewayConfigurationServerNotEnabled.New() +} + +// List implements ttnpb.ManagedGatewayEthernetProfileConfigurationServiceServer. +func (*noopManagedGatewayEthernetProfileServer) List( + _ context.Context, + _ *ttnpb.ListManagedGatewayEthernetProfilesRequest, +) (*ttnpb.ManagedGatewayEthernetProfiles, error) { + return nil, errManagedGatewayConfigurationServerNotEnabled.New() +} + +// Update implements ttnpb.ManagedGatewayEthernetProfileConfigurationServiceServer. +func (*noopManagedGatewayEthernetProfileServer) Update( + _ context.Context, + _ *ttnpb.UpdateManagedGatewayEthernetProfileRequest, +) (*ttnpb.ManagedGatewayEthernetProfile, error) { + return nil, errManagedGatewayConfigurationServerNotEnabled.New() +} diff --git a/pkg/gatewayconfigurationserver/managed/managed.go b/pkg/gatewayconfigurationserver/managed/managed.go index 5934cfaf33..7b13d88f9c 100644 --- a/pkg/gatewayconfigurationserver/managed/managed.go +++ b/pkg/gatewayconfigurationserver/managed/managed.go @@ -52,23 +52,27 @@ type Server struct { // New returns a new Server. func New(ctx context.Context, c Component, conf ttgc.Config) (*Server, error) { - client, err := ttgc.NewClient(ctx, c, conf) - if err != nil { - return nil, err - } - srv := &Server{ - Component: c, - } - srv.grpc.server = &managedGCSServer{ - Component: c, - client: client, - gatewayEUIs: conf.GatewayEUIs, - } - srv.grpc.wifiProfiles = &managedGatewayWiFiProfileServer{ - client: client, - } - srv.grpc.ethernetProfiles = &managedGatewayEthernetProfileServer{ - client: client, + srv := &Server{Component: c} + srv.grpc.server = &noopManagedGCSServer{} + srv.grpc.wifiProfiles = &noopManagedGatewayWiFiProfileServer{} + srv.grpc.ethernetProfiles = &noopManagedGatewayEthernetProfileServer{} + + if conf.Enabled { + client, err := ttgc.NewClient(ctx, c, conf) + if err != nil { + return nil, err + } + srv.grpc.server = &managedGCSServer{ + Component: c, + client: client, + gatewayEUIs: conf.GatewayEUIs, + } + srv.grpc.wifiProfiles = &managedGatewayWiFiProfileServer{ + client: client, + } + srv.grpc.ethernetProfiles = &managedGatewayEthernetProfileServer{ + client: client, + } } c.GRPCServer().RegisterUnaryHook( diff --git a/pkg/gatewayconfigurationserver/server.go b/pkg/gatewayconfigurationserver/server.go index f56b80d0a5..20737455a5 100644 --- a/pkg/gatewayconfigurationserver/server.go +++ b/pkg/gatewayconfigurationserver/server.go @@ -74,13 +74,12 @@ func New(c *component.Component, conf *Config) (*Server, error) { c.GRPC.RegisterUnaryHook("/ttn.lorawan.v3.GatewayConfigurationService", rpclog.NamespaceHook, rpclog.UnaryNamespaceHook("gatewayconfigurationserver")) //nolint:lll c.GRPC.RegisterUnaryHook("/ttn.lorawan.v3.GatewayConfigurationService", cluster.HookName, c.ClusterAuthUnaryHook()) - if ttgcConf := c.GetBaseConfig(c.Context()).TTGC; ttgcConf.Enabled { - var err error - gcs.managedServer, err = managed.New(c.Context(), c, ttgcConf) - if err != nil { - return nil, err - } + ttgcConf := c.GetBaseConfig(c.Context()).TTGC + managedServer, err := managed.New(c.Context(), c, ttgcConf) + if err != nil { + return nil, err } + gcs.managedServer = managedServer c.RegisterGRPC(gcs) c.RegisterWeb(gcs) diff --git a/pkg/gatewayserver/gatewayserver.go b/pkg/gatewayserver/gatewayserver.go index ccba5b181a..93fba9912a 100644 --- a/pkg/gatewayserver/gatewayserver.go +++ b/pkg/gatewayserver/gatewayserver.go @@ -1101,7 +1101,7 @@ func (gs *GatewayServer) updateConnStats(ctx context.Context, conn connectionEnt Protocol: conn.Connection.Frontend().Protocol(), GatewayRemoteAddress: conn.Connection.GatewayRemoteAddress(), } - registerGatewayConnectionStats(ctx, ids, stats) + registerGatewayConnectionStats(ctx, ids, ttnpb.Clone(stats)) if gs.statsRegistry != nil { if err := gs.statsRegistry.Set( decoupledCtx, @@ -1121,7 +1121,7 @@ func (gs *GatewayServer) updateConnStats(ctx context.Context, conn connectionEnt ConnectedAt: nil, DisconnectedAt: timestamppb.Now(), } - registerGatewayConnectionStats(decoupledCtx, ids, stats) + registerGatewayConnectionStats(decoupledCtx, ids, ttnpb.Clone(stats)) if gs.statsRegistry == nil { return } diff --git a/pkg/identityserver/application_access.go b/pkg/identityserver/application_access.go index 1ad3ea2a44..c61ed9fb40 100644 --- a/pkg/identityserver/application_access.go +++ b/pkg/identityserver/application_access.go @@ -104,12 +104,11 @@ func (is *IdentityServer) createApplicationAPIKey( events.Publish(evtCreateApplicationAPIKey.NewWithIdentifiersAndData(ctx, req.GetApplicationIds(), key)) go is.notifyInternal(ctx, &ttnpb.CreateNotificationRequest{ EntityIds: req.GetApplicationIds().GetEntityIdentifiers(), - NotificationType: "api_key_created", + NotificationType: ttnpb.GetNotificationTypeString(ttnpb.NotificationType_API_KEY_CREATED), Data: ttnpb.MustMarshalAny(key), Receivers: []ttnpb.NotificationReceiver{ ttnpb.NotificationReceiver_NOTIFICATION_RECEIVER_ADMINISTRATIVE_CONTACT, }, - Email: true, }) key.Key = token @@ -226,12 +225,11 @@ func (is *IdentityServer) updateApplicationAPIKey( events.Publish(evtUpdateApplicationAPIKey.NewWithIdentifiersAndData(ctx, req.GetApplicationIds(), key)) go is.notifyInternal(ctx, &ttnpb.CreateNotificationRequest{ EntityIds: req.GetApplicationIds().GetEntityIdentifiers(), - NotificationType: "api_key_changed", + NotificationType: ttnpb.GetNotificationTypeString(ttnpb.NotificationType_API_KEY_CHANGED), Data: ttnpb.MustMarshalAny(key), Receivers: []ttnpb.NotificationReceiver{ ttnpb.NotificationReceiver_NOTIFICATION_RECEIVER_ADMINISTRATIVE_CONTACT, }, - Email: true, }) return key, nil @@ -370,12 +368,11 @@ func (is *IdentityServer) setApplicationCollaborator( )) go is.notifyInternal(ctx, &ttnpb.CreateNotificationRequest{ EntityIds: req.GetApplicationIds().GetEntityIdentifiers(), - NotificationType: "collaborator_changed", + NotificationType: ttnpb.GetNotificationTypeString(ttnpb.NotificationType_COLLABORATOR_CHANGED), Data: ttnpb.MustMarshalAny(req.GetCollaborator()), Receivers: []ttnpb.NotificationReceiver{ ttnpb.NotificationReceiver_NOTIFICATION_RECEIVER_ADMINISTRATIVE_CONTACT, }, - Email: false, }) } else { events.Publish(evtDeleteApplicationCollaborator.New( diff --git a/pkg/identityserver/application_access_test.go b/pkg/identityserver/application_access_test.go index e5f24407a1..e1aab795fc 100644 --- a/pkg/identityserver/application_access_test.go +++ b/pkg/identityserver/application_access_test.go @@ -26,6 +26,79 @@ import ( "google.golang.org/grpc" ) +func TestApplicationAPIKeysPermissions(t *testing.T) { + p := &storetest.Population{} + + readOnlyAdmin := p.NewUser() + readOnlyAdmin.Admin = true + readOnlyAdminKey, _ := p.NewAPIKey(readOnlyAdmin.GetEntityIdentifiers(), ttnpb.AllReadAdminRights.GetRights()...) + readOnlyAdminKeyCreds := rpcCreds(readOnlyAdminKey) + + usr1 := p.NewUser() + app1 := p.NewApplication(usr1.GetOrganizationOrUserIdentifiers()) + appKey, _ := p.NewAPIKey(app1.GetEntityIdentifiers(), + ttnpb.Right_RIGHT_APPLICATION_INFO, + ttnpb.Right_RIGHT_APPLICATION_LINK, + ) + appCreds := rpcCreds(appKey) + + t.Parallel() + a, ctx := test.New(t) + + testWithIdentityServer(t, func(is *IdentityServer, cc *grpc.ClientConn) { + is.config.AdminRights.All = true + reg := ttnpb.NewApplicationAccessClient(cc) + + t.Run("API Key CRUD with different invalid credentials", func(t *testing.T) { // nolint:paralleltest + for _, opts := range [][]grpc.CallOption{nil, {appCreds}, {readOnlyAdminKeyCreds}} { + created, err := reg.CreateAPIKey(ctx, &ttnpb.CreateApplicationAPIKeyRequest{ + ApplicationIds: app1.GetIds(), + Name: "api-key-name", + Rights: []ttnpb.Right{ttnpb.Right_RIGHT_APPLICATION_INFO}, + }, opts...) + if a.So(err, should.NotBeNil) && a.So(errors.IsPermissionDenied(err), should.BeTrue) { + a.So(created, should.BeNil) + } + + list, err := reg.ListAPIKeys(ctx, &ttnpb.ListApplicationAPIKeysRequest{ + ApplicationIds: app1.GetIds(), + }, opts...) + if a.So(err, should.NotBeNil) && a.So(errors.IsPermissionDenied(err), should.BeTrue) { + a.So(list, should.BeNil) + } + + got, err := reg.GetAPIKey(ctx, &ttnpb.GetApplicationAPIKeyRequest{ + ApplicationIds: app1.GetIds(), + KeyId: appKey.GetId(), + }, opts...) + if a.So(err, should.NotBeNil) && a.So(errors.IsPermissionDenied(err), should.BeTrue) { + a.So(got, should.BeNil) + } + + updated, err := reg.UpdateAPIKey(ctx, &ttnpb.UpdateApplicationAPIKeyRequest{ + ApplicationIds: app1.GetIds(), + ApiKey: &ttnpb.APIKey{ + Id: appKey.GetId(), + Name: "api-key-name-updated", + }, + FieldMask: ttnpb.FieldMask("name"), + }, opts...) + if a.So(err, should.NotBeNil) && a.So(errors.IsPermissionDenied(err), should.BeTrue) { + a.So(updated, should.BeNil) + } + + _, err = reg.DeleteAPIKey(ctx, &ttnpb.DeleteApplicationAPIKeyRequest{ + ApplicationIds: app1.GetIds(), + KeyId: created.GetId(), + }, opts...) + if !a.So(errors.IsPermissionDenied(err), should.BeTrue) { + t.FailNow() + } + } + }) + }, withPrivateTestDatabase(p)) +} + func TestApplicationAPIKeys(t *testing.T) { // nolint:gocyclo p := &storetest.Population{} @@ -50,7 +123,6 @@ func TestApplicationAPIKeys(t *testing.T) { // nolint:gocyclo ttnpb.Right_RIGHT_APPLICATION_INFO, ttnpb.Right_RIGHT_APPLICATION_LINK, ) - appCreds := rpcCreds(appKey) t.Parallel() a, ctx := test.New(t) @@ -147,53 +219,6 @@ func TestApplicationAPIKeys(t *testing.T) { // nolint:gocyclo }) } - // API Key CRUD with different invalid credentials. - for _, opts := range [][]grpc.CallOption{nil, {appCreds}} { - created, err := reg.CreateAPIKey(ctx, &ttnpb.CreateApplicationAPIKeyRequest{ - ApplicationIds: app1.GetIds(), - Name: "api-key-name", - Rights: []ttnpb.Right{ttnpb.Right_RIGHT_APPLICATION_INFO}, - }, opts...) - if a.So(err, should.NotBeNil) && a.So(errors.IsPermissionDenied(err), should.BeTrue) { - a.So(created, should.BeNil) - } - - list, err := reg.ListAPIKeys(ctx, &ttnpb.ListApplicationAPIKeysRequest{ - ApplicationIds: app1.GetIds(), - }, opts...) - if a.So(err, should.NotBeNil) && a.So(errors.IsPermissionDenied(err), should.BeTrue) { - a.So(list, should.BeNil) - } - - got, err := reg.GetAPIKey(ctx, &ttnpb.GetApplicationAPIKeyRequest{ - ApplicationIds: app1.GetIds(), - KeyId: appKey.GetId(), - }, opts...) - if a.So(err, should.NotBeNil) && a.So(errors.IsPermissionDenied(err), should.BeTrue) { - a.So(got, should.BeNil) - } - - updated, err := reg.UpdateAPIKey(ctx, &ttnpb.UpdateApplicationAPIKeyRequest{ - ApplicationIds: app1.GetIds(), - ApiKey: &ttnpb.APIKey{ - Id: appKey.GetId(), - Name: "api-key-name-updated", - }, - FieldMask: ttnpb.FieldMask("name"), - }, opts...) - if a.So(err, should.NotBeNil) && a.So(errors.IsPermissionDenied(err), should.BeTrue) { - a.So(updated, should.BeNil) - } - - _, err = reg.DeleteAPIKey(ctx, &ttnpb.DeleteApplicationAPIKeyRequest{ - ApplicationIds: app1.GetIds(), - KeyId: created.GetId(), - }, opts...) - if !a.So(errors.IsPermissionDenied(err), should.BeTrue) { - t.FailNow() - } - } - // API Key CRUD with different valid credentials. for _, opts := range [][]grpc.CallOption{{adminCreds}, {usr1Creds}, {limitedCreds}} { created, err := reg.CreateAPIKey(ctx, &ttnpb.CreateApplicationAPIKeyRequest{ @@ -287,6 +312,72 @@ func TestApplicationAPIKeys(t *testing.T) { // nolint:gocyclo }, withPrivateTestDatabase(p)) } +func TestApplicationCollaboratorsPermissions(t *testing.T) { // nolint:gocyclo + p := &storetest.Population{} + + readOnlyAdmin := p.NewUser() + readOnlyAdmin.Admin = true + readOnlyAdminKey, _ := p.NewAPIKey(readOnlyAdmin.GetEntityIdentifiers(), ttnpb.AllReadAdminRights.GetRights()...) + readOnlyAdminKeyCreds := rpcCreds(readOnlyAdminKey) + + usr1 := p.NewUser() + app1 := p.NewApplication(usr1.GetOrganizationOrUserIdentifiers()) + appKey, _ := p.NewAPIKey(app1.GetEntityIdentifiers(), + ttnpb.Right_RIGHT_APPLICATION_INFO, + ttnpb.Right_RIGHT_APPLICATION_LINK, + ) + appCreds := rpcCreds(appKey) + + usr2 := p.NewUser() + p.NewMembership( + usr2.GetOrganizationOrUserIdentifiers(), + app1.GetEntityIdentifiers(), + ttnpb.Right_RIGHT_APPLICATION_INFO, + ttnpb.Right_RIGHT_APPLICATION_LINK, + ) + + t.Parallel() + a, ctx := test.New(t) + + testWithIdentityServer(t, func(is *IdentityServer, cc *grpc.ClientConn) { + is.config.AdminRights.All = true + + reg := ttnpb.NewApplicationAccessClient(cc) + + // Collaborator CRUD with different invalid credentials. + for _, opts := range [][]grpc.CallOption{nil, {appCreds}, {readOnlyAdminKeyCreds}} { + _, err := reg.SetCollaborator(ctx, &ttnpb.SetApplicationCollaboratorRequest{ + ApplicationIds: app1.GetIds(), + Collaborator: &ttnpb.Collaborator{ + Ids: usr2.GetOrganizationOrUserIdentifiers(), + Rights: []ttnpb.Right{ + ttnpb.Right_RIGHT_APPLICATION_INFO, + }, + }, + }, opts...) + if a.So(err, should.NotBeNil) { + a.So(errors.IsPermissionDenied(err), should.BeTrue) + } + + got, err := reg.GetCollaborator(ctx, &ttnpb.GetApplicationCollaboratorRequest{ + ApplicationIds: app1.GetIds(), + Collaborator: usr2.GetOrganizationOrUserIdentifiers(), + }, opts...) + if a.So(err, should.NotBeNil) && a.So(errors.IsPermissionDenied(err), should.BeTrue) { + a.So(got, should.BeNil) + } + } + + // ListCollaborators without credentials. + list, err := reg.ListCollaborators(ctx, &ttnpb.ListApplicationCollaboratorsRequest{ + ApplicationIds: app1.GetIds(), + }) + if a.So(err, should.NotBeNil) && a.So(errors.IsUnauthenticated(err), should.BeTrue) { + a.So(list, should.BeNil) + } + }, withPrivateTestDatabase(p)) +} + func TestApplicationCollaborators(t *testing.T) { // nolint:gocyclo p := &storetest.Population{} @@ -308,12 +399,6 @@ func TestApplicationCollaborators(t *testing.T) { // nolint:gocyclo ) limitedCreds := rpcCreds(limitedKey) - appKey, _ := p.NewAPIKey(app1.GetEntityIdentifiers(), - ttnpb.Right_RIGHT_APPLICATION_INFO, - ttnpb.Right_RIGHT_APPLICATION_LINK, - ) - appCreds := rpcCreds(appKey) - usr2 := p.NewUser() p.NewMembership( usr2.GetOrganizationOrUserIdentifiers(), @@ -385,30 +470,6 @@ func TestApplicationCollaborators(t *testing.T) { // nolint:gocyclo }, limitedCreds) a.So(err, should.BeNil) - // Collaborator CRUD with different invalid credentials. - for _, opts := range [][]grpc.CallOption{nil, {appCreds}} { - _, err := reg.SetCollaborator(ctx, &ttnpb.SetApplicationCollaboratorRequest{ - ApplicationIds: app1.GetIds(), - Collaborator: &ttnpb.Collaborator{ - Ids: usr2.GetOrganizationOrUserIdentifiers(), - Rights: []ttnpb.Right{ - ttnpb.Right_RIGHT_APPLICATION_INFO, - }, - }, - }, opts...) - if a.So(err, should.NotBeNil) { - a.So(errors.IsPermissionDenied(err), should.BeTrue) - } - - got, err := reg.GetCollaborator(ctx, &ttnpb.GetApplicationCollaboratorRequest{ - ApplicationIds: app1.GetIds(), - Collaborator: usr2.GetOrganizationOrUserIdentifiers(), - }, opts...) - if a.So(err, should.NotBeNil) && a.So(errors.IsPermissionDenied(err), should.BeTrue) { - a.So(got, should.BeNil) - } - } - // ListCollaborators without credentials. list, err := reg.ListCollaborators(ctx, &ttnpb.ListApplicationCollaboratorsRequest{ ApplicationIds: app1.GetIds(), diff --git a/pkg/identityserver/application_registry.go b/pkg/identityserver/application_registry.go index 605810f675..e2c587c54b 100644 --- a/pkg/identityserver/application_registry.go +++ b/pkg/identityserver/application_registry.go @@ -422,6 +422,11 @@ func (is *IdentityServer) purgeApplication( if !is.IsAdmin(ctx) { return nil, errAdminsPurgeApplications.New() } + if err := rights.RequireApplication( + store.WithSoftDeleted(ctx, false), ids, ttnpb.Right_RIGHT_APPLICATION_PURGE, + ); err != nil { + return nil, err + } err := is.store.Transact(ctx, func(ctx context.Context, st store.Store) error { total, err := st.CountEndDevices(ctx, ids) if err != nil { diff --git a/pkg/identityserver/application_registry_test.go b/pkg/identityserver/application_registry_test.go index 3fc372ecf8..bf32a907fa 100644 --- a/pkg/identityserver/application_registry_test.go +++ b/pkg/identityserver/application_registry_test.go @@ -26,66 +26,114 @@ import ( "google.golang.org/grpc/metadata" ) -func TestApplicationsPermissionDenied(t *testing.T) { +func TestApplicationsPermissions(t *testing.T) { p := &storetest.Population{} usr1 := p.NewUser() app1 := p.NewApplication(usr1.GetOrganizationOrUserIdentifiers()) + readAdminUsr := p.NewUser() + readAdminUsr.Admin = true + readAdminUsrKey, _ := p.NewAPIKey(readAdminUsr.GetEntityIdentifiers(), ttnpb.AllReadAdminRights.GetRights()...) + readAdminUsrCreds := rpcCreds(readAdminUsrKey) + t.Parallel() a, ctx := test.New(t) testWithIdentityServer(t, func(_ *IdentityServer, cc *grpc.ClientConn) { reg := ttnpb.NewApplicationRegistryClient(cc) - _, err := reg.Create(ctx, &ttnpb.CreateApplicationRequest{ - Application: &ttnpb.Application{ - Ids: &ttnpb.ApplicationIdentifiers{ApplicationId: "foo-app"}, - }, - Collaborator: usr1.GetOrganizationOrUserIdentifiers(), - }) - if a.So(err, should.NotBeNil) { - a.So(errors.IsPermissionDenied(err), should.BeTrue) - } + t.Run("Permission denied", func(t *testing.T) { // nolint:paralleltest + reg := ttnpb.NewApplicationRegistryClient(cc) - _, err = reg.Get(ctx, &ttnpb.GetApplicationRequest{ - ApplicationIds: app1.GetIds(), - FieldMask: ttnpb.FieldMask("name"), - }) - if a.So(err, should.NotBeNil) { - a.So(errors.IsUnauthenticated(err), should.BeTrue) - } + _, err := reg.Create(ctx, &ttnpb.CreateApplicationRequest{ + Application: &ttnpb.Application{ + Ids: &ttnpb.ApplicationIdentifiers{ApplicationId: "foo-app"}, + }, + Collaborator: usr1.GetOrganizationOrUserIdentifiers(), + }) + if a.So(err, should.NotBeNil) { + a.So(errors.IsPermissionDenied(err), should.BeTrue) + } - listRes, err := reg.List(ctx, &ttnpb.ListApplicationsRequest{ - FieldMask: ttnpb.FieldMask("name"), - }) - a.So(err, should.BeNil) - if a.So(listRes, should.NotBeNil) { - a.So(listRes.Applications, should.BeEmpty) - } + _, err = reg.Get(ctx, &ttnpb.GetApplicationRequest{ + ApplicationIds: app1.GetIds(), + FieldMask: ttnpb.FieldMask("name"), + }) + if a.So(err, should.NotBeNil) { + a.So(errors.IsUnauthenticated(err), should.BeTrue) + } - _, err = reg.List(ctx, &ttnpb.ListApplicationsRequest{ - Collaborator: usr1.GetOrganizationOrUserIdentifiers(), - FieldMask: ttnpb.FieldMask("name"), + listRes, err := reg.List(ctx, &ttnpb.ListApplicationsRequest{ + FieldMask: ttnpb.FieldMask("name"), + }) + a.So(err, should.BeNil) + if a.So(listRes, should.NotBeNil) { + a.So(listRes.Applications, should.BeEmpty) + } + + _, err = reg.List(ctx, &ttnpb.ListApplicationsRequest{ + Collaborator: usr1.GetOrganizationOrUserIdentifiers(), + FieldMask: ttnpb.FieldMask("name"), + }) + if a.So(err, should.NotBeNil) { + a.So(errors.IsPermissionDenied(err), should.BeTrue) + } + + _, err = reg.Update(ctx, &ttnpb.UpdateApplicationRequest{ + Application: &ttnpb.Application{ + Ids: app1.GetIds(), + Name: "Updated Name", + }, + FieldMask: ttnpb.FieldMask("name"), + }) + if a.So(err, should.NotBeNil) { + a.So(errors.IsPermissionDenied(err), should.BeTrue) + } + + _, err = reg.Delete(ctx, app1.GetIds()) + if a.So(err, should.NotBeNil) { + a.So(errors.IsPermissionDenied(err), should.BeTrue) + } }) - if a.So(err, should.NotBeNil) { + + t.Run("Admin read-only", func(t *testing.T) { // nolint:paralleltest + _, err := reg.Create(ctx, &ttnpb.CreateApplicationRequest{ + Application: &ttnpb.Application{ + Ids: &ttnpb.ApplicationIdentifiers{ApplicationId: "foo-app"}, + }, + Collaborator: usr1.GetOrganizationOrUserIdentifiers(), + }, readAdminUsrCreds) a.So(errors.IsPermissionDenied(err), should.BeTrue) - } - _, err = reg.Update(ctx, &ttnpb.UpdateApplicationRequest{ - Application: &ttnpb.Application{ - Ids: app1.GetIds(), - Name: "Updated Name", - }, - FieldMask: ttnpb.FieldMask("name"), - }) - if a.So(err, should.NotBeNil) { + _, err = reg.Get(ctx, &ttnpb.GetApplicationRequest{ + ApplicationIds: app1.GetIds(), + FieldMask: ttnpb.FieldMask("name"), + }, readAdminUsrCreds) + a.So(errors.IsPermissionDenied(err), should.BeFalse) + + _, err = reg.List(ctx, &ttnpb.ListApplicationsRequest{ + FieldMask: ttnpb.FieldMask("name"), + }, readAdminUsrCreds) + a.So(errors.IsPermissionDenied(err), should.BeFalse) + + _, err = reg.List(ctx, &ttnpb.ListApplicationsRequest{ + Collaborator: usr1.GetOrganizationOrUserIdentifiers(), + FieldMask: ttnpb.FieldMask("name"), + }, readAdminUsrCreds) + a.So(errors.IsPermissionDenied(err), should.BeFalse) + + _, err = reg.Update(ctx, &ttnpb.UpdateApplicationRequest{ + Application: &ttnpb.Application{ + Ids: app1.GetIds(), + Name: "Updated Name", + }, + FieldMask: ttnpb.FieldMask("name"), + }, readAdminUsrCreds) a.So(errors.IsPermissionDenied(err), should.BeTrue) - } - _, err = reg.Delete(ctx, app1.GetIds()) - if a.So(err, should.NotBeNil) { + _, err = reg.Delete(ctx, app1.GetIds(), readAdminUsrCreds) a.So(errors.IsPermissionDenied(err), should.BeTrue) - } + }) }, withPrivateTestDatabase(p)) } diff --git a/pkg/identityserver/bunstore/notification_store.go b/pkg/identityserver/bunstore/notification_store.go index 61bff862fb..274950692f 100644 --- a/pkg/identityserver/bunstore/notification_store.go +++ b/pkg/identityserver/bunstore/notification_store.go @@ -90,7 +90,6 @@ func notificationToPB(m *Notification, r *NotificationReceiver) (*ttnpb.Notifica EntityIds: getEntityIdentifiers(m.EntityType, m.EntityUID), NotificationType: m.NotificationType, Receivers: convertIntSlice[int, ttnpb.NotificationReceiver](m.Receivers), - Email: m.Email, } if len(m.Data) > 0 { anyPB := &anypb.Any{} @@ -144,7 +143,6 @@ func (s *notificationStore) CreateNotification( EntityUID: pb.GetEntityIds().IDString(), NotificationType: pb.NotificationType, Receivers: convertIntSlice[ttnpb.NotificationReceiver, int](pb.Receivers), - Email: pb.Email, } if pb.Data != nil { diff --git a/pkg/identityserver/bunstore/user_store.go b/pkg/identityserver/bunstore/user_store.go index b5144e9077..0bc40108a4 100644 --- a/pkg/identityserver/bunstore/user_store.go +++ b/pkg/identityserver/bunstore/user_store.go @@ -65,8 +65,9 @@ type User struct { ProfilePictureID *string `bun:"profile_picture_id"` ProfilePicture *Picture `bun:"rel:belongs-to,join:profile_picture_id=id"` - ConsolePreferences json.RawMessage `bun:"console_preferences"` - UniversalRights []int `bun:"universal_rights,array,nullzero"` + ConsolePreferences json.RawMessage `bun:"console_preferences"` + UniversalRights []int `bun:"universal_rights,array,nullzero"` + EmailNotificationPreferences []int `bun:"email_notification_preferences,array,nullzero"` } // BeforeAppendModel is a hook that modifies the model on SELECT and UPDATE queries. @@ -107,6 +108,10 @@ func userToPB(m *User, fieldMask ...string) (*ttnpb.User, error) { TemporaryPasswordExpiresAt: ttnpb.ProtoTime(m.TemporaryPasswordExpiresAt), ConsolePreferences: &ttnpb.UserConsolePreferences{}, + EmailNotificationPreferences: &ttnpb.EmailNotificationPreferences{ + Types: convertIntSlice[int, ttnpb.NotificationType](m.EmailNotificationPreferences), + }, + UniversalRights: convertIntSlice[int, ttnpb.Right](m.UniversalRights), } if len(m.Attributes) > 0 { @@ -181,6 +186,8 @@ func (s *userStore) CreateUser(ctx context.Context, pb *ttnpb.User) (*ttnpb.User TemporaryPassword: pb.TemporaryPassword, TemporaryPasswordCreatedAt: cleanTimePtr(ttnpb.StdTime(pb.TemporaryPasswordCreatedAt)), TemporaryPasswordExpiresAt: cleanTimePtr(ttnpb.StdTime(pb.TemporaryPasswordExpiresAt)), + EmailNotificationPreferences: convertIntSlice[ttnpb.NotificationType, int](pb.EmailNotificationPreferences.GetTypes()), // nolint:lll + UniversalRights: convertIntSlice[ttnpb.Right, int](pb.UniversalRights), } if pb.ProfilePicture != nil { @@ -234,7 +241,6 @@ func (s *userStore) CreateUser(ctx context.Context, pb *ttnpb.User) (*ttnpb.User if err != nil { return nil, storeutil.WrapDriverError(err) } - if len(pb.Attributes) > 0 { userModel.Attributes, err = s.replaceAttributes( ctx, nil, pb.Attributes, "user", userModel.ID, @@ -283,6 +289,10 @@ func (*userStore) selectWithFields(q *bun.SelectQuery, fieldMask store.FieldMask "console_preferences.dashboard_layouts", "console_preferences.sort_by": columns = append(columns, "console_preferences") + case "email_notification_preferences", "email_notification_preferences.types": + columns = append(columns, "email_notification_preferences") + case "universal_rights": + columns = append(columns, "universal_rights") case "attributes": q = q.Relation("Attributes") case "administrative_contact": @@ -597,8 +607,15 @@ func (s *userStore) updateUserModel( //nolint:gocyclo case "console_preferences.sort_by": updateConsolePreferences = true consolePreferences.SortBy = pb.ConsolePreferences.GetSortBy() + case "console_preferences.tutorials": + updateConsolePreferences = true + consolePreferences.Tutorials = pb.ConsolePreferences.GetTutorials() case "universal_rights": + model.UniversalRights = convertIntSlice[ttnpb.Right, int](pb.UniversalRights) columns = append(columns, "universal_rights") + case "email_notification_preferences", "email_notification_preferences.types": + model.EmailNotificationPreferences = convertIntSlice[ttnpb.NotificationType, int](pb.EmailNotificationPreferences.Types) // nolint:lll + columns = append(columns, "email_notification_preferences") } } diff --git a/pkg/identityserver/client_access.go b/pkg/identityserver/client_access.go index ad9770cb45..aa56f3df62 100644 --- a/pkg/identityserver/client_access.go +++ b/pkg/identityserver/client_access.go @@ -173,12 +173,11 @@ func (is *IdentityServer) setClientCollaborator( ) go is.notifyInternal(ctx, &ttnpb.CreateNotificationRequest{ EntityIds: req.GetClientIds().GetEntityIdentifiers(), - NotificationType: "collaborator_changed", + NotificationType: ttnpb.GetNotificationTypeString(ttnpb.NotificationType_COLLABORATOR_CHANGED), Data: ttnpb.MustMarshalAny(req.GetCollaborator()), Receivers: []ttnpb.NotificationReceiver{ ttnpb.NotificationReceiver_NOTIFICATION_RECEIVER_ADMINISTRATIVE_CONTACT, }, - Email: false, }) } else { events.Publish(evtDeleteClientCollaborator.New( diff --git a/pkg/identityserver/client_access_test.go b/pkg/identityserver/client_access_test.go index 624750d5b6..d28ff62812 100644 --- a/pkg/identityserver/client_access_test.go +++ b/pkg/identityserver/client_access_test.go @@ -26,6 +26,95 @@ import ( "google.golang.org/grpc" ) +func TestClientCollaboratorsPermissions(t *testing.T) { // nolint:gocyclo + p := &storetest.Population{} + + usr1 := p.NewUser() + + readAdmin := p.NewUser() + readAdmin.Admin = true + readAdminKey, _ := p.NewAPIKey(readAdmin.GetEntityIdentifiers(), ttnpb.AllReadAdminRights.GetRights()...) + readAdminCreds := rpcCreds(readAdminKey) + + cli1 := p.NewClient(usr1.GetOrganizationOrUserIdentifiers()) + cliKey, _ := p.NewAPIKey(cli1.GetEntityIdentifiers(), + ttnpb.Right_RIGHT_GATEWAY_INFO, + ttnpb.Right_RIGHT_GATEWAY_LINK, + ) + cliCreds := rpcCreds(cliKey) + + p.NewMembership( + usr1.GetOrganizationOrUserIdentifiers(), + cli1.GetEntityIdentifiers(), + ttnpb.Right_RIGHT_CLIENT_SETTINGS_COLLABORATORS, + ) + + t.Parallel() + a, ctx := test.New(t) + + testWithIdentityServer(t, func(is *IdentityServer, cc *grpc.ClientConn) { + is.config.AdminRights.All = true + + reg := ttnpb.NewClientAccessClient(cc) + + t.Run("Invalid credentials", func(t *testing.T) { // nolint:paralleltest + for _, opts := range [][]grpc.CallOption{nil, {cliCreds}} { + _, err := reg.GetCollaborator(ctx, &ttnpb.GetClientCollaboratorRequest{ + ClientIds: cli1.GetIds(), + Collaborator: usr1.GetOrganizationOrUserIdentifiers(), + }) + a.So(errors.IsPermissionDenied(err), should.BeTrue) + + _, err = reg.ListCollaborators(ctx, &ttnpb.ListClientCollaboratorsRequest{ + ClientIds: cli1.GetIds(), + }) + a.So(errors.IsUnauthenticated(err), should.BeTrue) + + _, err = reg.SetCollaborator(ctx, &ttnpb.SetClientCollaboratorRequest{ + ClientIds: cli1.GetIds(), + Collaborator: &ttnpb.Collaborator{ + Ids: usr1.GetOrganizationOrUserIdentifiers(), + Rights: []ttnpb.Right{ttnpb.Right_RIGHT_GATEWAY_INFO}, + }, + }, opts...) + a.So(errors.IsPermissionDenied(err), should.BeTrue) + } + }) + + t.Run("Admin read-only", func(t *testing.T) { // nolint:paralleltest + _, err := reg.GetCollaborator(ctx, &ttnpb.GetClientCollaboratorRequest{ + ClientIds: cli1.GetIds(), + Collaborator: usr1.GetOrganizationOrUserIdentifiers(), + }, readAdminCreds) + a.So(errors.IsPermissionDenied(err), should.BeTrue) + + // ListCollaborators without credentials. + _, err = reg.ListCollaborators(ctx, &ttnpb.ListClientCollaboratorsRequest{ + ClientIds: cli1.GetIds(), + }, readAdminCreds) + a.So(errors.IsPermissionDenied(err), should.BeFalse) + + _, err = reg.SetCollaborator(ctx, &ttnpb.SetClientCollaboratorRequest{ + ClientIds: cli1.GetIds(), + Collaborator: &ttnpb.Collaborator{ + Ids: usr1.GetOrganizationOrUserIdentifiers(), + Rights: []ttnpb.Right{ttnpb.Right_RIGHT_GATEWAY_INFO}, + }, + }, readAdminCreds) + if a.So(err, should.NotBeNil) { + a.So(errors.IsPermissionDenied(err), should.BeTrue) + } + }) + + t.Run("cluster auth", func(t *testing.T) { // nolint:paralleltest + rights, err := reg.ListRights(ctx, cli1.GetIds(), is.WithClusterAuth()) + if a.So(err, should.BeNil) && a.So(rights, should.NotBeNil) { + a.So(ttnpb.AllClusterRights.Intersect(ttnpb.AllClientRights).Sub(rights).Rights, should.BeEmpty) + } + }) + }, withPrivateTestDatabase(p)) +} + func TestClientCollaborators(t *testing.T) { // nolint:gocyclo p := &storetest.Population{} @@ -39,11 +128,6 @@ func TestClientCollaborators(t *testing.T) { // nolint:gocyclo usr1Creds := rpcCreds(usr1Key) cli1 := p.NewClient(usr1.GetOrganizationOrUserIdentifiers()) - cliKey, _ := p.NewAPIKey(cli1.GetEntityIdentifiers(), - ttnpb.Right_RIGHT_GATEWAY_INFO, - ttnpb.Right_RIGHT_GATEWAY_LINK, - ) - cliCreds := rpcCreds(cliKey) usr2 := p.NewUser() p.NewMembership( @@ -72,59 +156,6 @@ func TestClientCollaborators(t *testing.T) { // nolint:gocyclo } a.So(got, should.BeNil) - // SetCollaborator without credentials. - _, err = reg.SetCollaborator(ctx, &ttnpb.SetClientCollaboratorRequest{ - ClientIds: cli1.GetIds(), - Collaborator: &ttnpb.Collaborator{ - Ids: usr2.GetOrganizationOrUserIdentifiers(), - Rights: []ttnpb.Right{ - ttnpb.Right_RIGHT_CLIENT_SETTINGS_COLLABORATORS, - }, - }, - }) - if a.So(err, should.NotBeNil) { - a.So(errors.IsPermissionDenied(err), should.BeTrue) - } - - // GetCollaborator without credentials. - got, err = reg.GetCollaborator(ctx, &ttnpb.GetClientCollaboratorRequest{ - ClientIds: cli1.GetIds(), - Collaborator: usr2.GetOrganizationOrUserIdentifiers(), - }) - if a.So(err, should.NotBeNil) && a.So(errors.IsPermissionDenied(err), should.BeTrue) { - a.So(got, should.BeNil) - } - - // ListCollaborators without credentials. - list, err := reg.ListCollaborators(ctx, &ttnpb.ListClientCollaboratorsRequest{ - ClientIds: cli1.GetIds(), - }) - if a.So(err, should.NotBeNil) && a.So(errors.IsUnauthenticated(err), should.BeTrue) { - a.So(list, should.BeNil) - } - - // Collaborator CRUD with different invalid credentials. - for _, opts := range [][]grpc.CallOption{nil, {cliCreds}} { - _, err := reg.SetCollaborator(ctx, &ttnpb.SetClientCollaboratorRequest{ - ClientIds: cli1.GetIds(), - Collaborator: &ttnpb.Collaborator{ - Ids: usr2.GetOrganizationOrUserIdentifiers(), - Rights: []ttnpb.Right{ttnpb.Right_RIGHT_GATEWAY_INFO}, - }, - }, opts...) - if a.So(err, should.NotBeNil) { - a.So(errors.IsPermissionDenied(err), should.BeTrue) - } - - got, err := reg.GetCollaborator(ctx, &ttnpb.GetClientCollaboratorRequest{ - ClientIds: cli1.GetIds(), - Collaborator: usr2.GetOrganizationOrUserIdentifiers(), - }, opts...) - if a.So(err, should.NotBeNil) && a.So(errors.IsPermissionDenied(err), should.BeTrue) { - a.So(got, should.BeNil) - } - } - // Collaborator CRUD with different valid credentials. for _, opts := range [][]grpc.CallOption{{adminCreds}, {usr1Creds}} { // Adds `usr3` as a collaborator of `cli1` client. @@ -138,7 +169,7 @@ func TestClientCollaborators(t *testing.T) { // nolint:gocyclo a.So(err, should.BeNil) // Lists collaborators of the `cli1` client. - list, err = reg.ListCollaborators(ctx, &ttnpb.ListClientCollaboratorsRequest{ + list, err := reg.ListCollaborators(ctx, &ttnpb.ListClientCollaboratorsRequest{ ClientIds: cli1.GetIds(), }, opts...) if a.So(err, should.BeNil) && a.So(list, should.NotBeNil) && a.So(list.Collaborators, should.HaveLength, 3) { @@ -230,23 +261,6 @@ func TestClientCollaborators(t *testing.T) { // nolint:gocyclo }, withPrivateTestDatabase(p)) } -func TestClientAccessClusterAuth(t *testing.T) { - p := &storetest.Population{} - cli1 := p.NewClient(nil) - - t.Parallel() - a, ctx := test.New(t) - - testWithIdentityServer(t, func(is *IdentityServer, cc *grpc.ClientConn) { - reg := ttnpb.NewClientAccessClient(cc) - - rights, err := reg.ListRights(ctx, cli1.GetIds(), is.WithClusterAuth()) - if a.So(err, should.BeNil) && a.So(rights, should.NotBeNil) { - a.So(ttnpb.AllClusterRights.Intersect(ttnpb.AllClientRights).Sub(rights).Rights, should.BeEmpty) - } - }, withPrivateTestDatabase(p)) -} - func TestClientContactRestrictions(t *testing.T) { p := &storetest.Population{} diff --git a/pkg/identityserver/client_registry.go b/pkg/identityserver/client_registry.go index c3a3920361..639dabb27a 100644 --- a/pkg/identityserver/client_registry.go +++ b/pkg/identityserver/client_registry.go @@ -153,7 +153,7 @@ func (is *IdentityServer) createClient( //nolint:gocyclo if !createdByAdmin && cli.State == ttnpb.State_STATE_REQUESTED { go is.notifyAdminsInternal(ctx, &ttnpb.CreateNotificationRequest{ EntityIds: req.GetClient().GetIds().GetEntityIdentifiers(), - NotificationType: "client_requested", + NotificationType: ttnpb.GetNotificationTypeString(ttnpb.NotificationType_CLIENT_REQUESTED), Data: ttnpb.MustMarshalAny( &ttnpb.CreateClientEmailMessage{ CreateClientRequest: req, @@ -163,7 +163,6 @@ func (is *IdentityServer) createClient( //nolint:gocyclo Receivers: []ttnpb.NotificationReceiver{ ttnpb.NotificationReceiver_NOTIFICATION_RECEIVER_ADMINISTRATIVE_CONTACT, }, - Email: true, }) } @@ -368,7 +367,7 @@ func (is *IdentityServer) updateClient( if ttnpb.HasAnyField(req.FieldMask.GetPaths(), "state") { go is.notifyInternal(ctx, &ttnpb.CreateNotificationRequest{ EntityIds: cli.GetIds().GetEntityIdentifiers(), - NotificationType: "entity_state_changed", + NotificationType: ttnpb.GetNotificationTypeString(ttnpb.NotificationType_ENTITY_STATE_CHANGED), Data: ttnpb.MustMarshalAny(&ttnpb.EntityStateChangedNotification{ State: cli.State, StateDescription: cli.StateDescription, @@ -376,7 +375,6 @@ func (is *IdentityServer) updateClient( Receivers: []ttnpb.NotificationReceiver{ ttnpb.NotificationReceiver_NOTIFICATION_RECEIVER_ADMINISTRATIVE_CONTACT, }, - Email: true, }) } return cli, nil @@ -431,6 +429,11 @@ func (is *IdentityServer) purgeClient(ctx context.Context, ids *ttnpb.ClientIden if !is.IsAdmin(ctx) { return nil, errAdminsPurgeClients.New() } + if err := rights.RequireClient( + store.WithSoftDeleted(ctx, false), ids, ttnpb.Right_RIGHT_CLIENT_PURGE, + ); err != nil { + return nil, err + } err := is.store.Transact(ctx, func(ctx context.Context, st store.Store) error { // delete related authorizations before purging the client err := st.DeleteClientAuthorizations(ctx, ids) diff --git a/pkg/identityserver/client_registry_test.go b/pkg/identityserver/client_registry_test.go index 5622363832..95121286ae 100644 --- a/pkg/identityserver/client_registry_test.go +++ b/pkg/identityserver/client_registry_test.go @@ -26,8 +26,14 @@ import ( "google.golang.org/grpc/metadata" ) -func TestClientsPermissionDenied(t *testing.T) { +func TestClientsPermissions(t *testing.T) { p := &storetest.Population{} + + readOnlyAdmin := p.NewUser() + readOnlyAdmin.Admin = true + readOnlyAdminKey, _ := p.NewAPIKey(readOnlyAdmin.GetEntityIdentifiers(), ttnpb.AllReadAdminRights.GetRights()...) + readOnlyAdminKeyCreds := rpcCreds(readOnlyAdminKey) + usr1 := p.NewUser() cli1 := p.NewClient(usr1.GetOrganizationOrUserIdentifiers()) @@ -37,55 +43,82 @@ func TestClientsPermissionDenied(t *testing.T) { testWithIdentityServer(t, func(_ *IdentityServer, cc *grpc.ClientConn) { reg := ttnpb.NewClientRegistryClient(cc) - _, err := reg.Create(ctx, &ttnpb.CreateClientRequest{ - Client: &ttnpb.Client{ - Ids: &ttnpb.ClientIdentifiers{ClientId: "foo-cli"}, - }, - Collaborator: usr1.GetOrganizationOrUserIdentifiers(), - }) - if a.So(err, should.NotBeNil) { + t.Run("Invalid credentials", func(t *testing.T) { // nolint:paralleltest + _, err := reg.Create(ctx, &ttnpb.CreateClientRequest{ + Client: &ttnpb.Client{ + Ids: &ttnpb.ClientIdentifiers{ClientId: "foo-cli"}, + }, + Collaborator: usr1.GetOrganizationOrUserIdentifiers(), + }) a.So(errors.IsPermissionDenied(err), should.BeTrue) - } - _, err = reg.Get(ctx, &ttnpb.GetClientRequest{ - ClientIds: cli1.GetIds(), - FieldMask: ttnpb.FieldMask("name"), - }) - if a.So(err, should.NotBeNil) { + _, err = reg.Get(ctx, &ttnpb.GetClientRequest{ + ClientIds: cli1.GetIds(), + FieldMask: ttnpb.FieldMask("name"), + }) a.So(errors.IsUnauthenticated(err), should.BeTrue) - } - listRes, err := reg.List(ctx, &ttnpb.ListClientsRequest{ - FieldMask: ttnpb.FieldMask("name"), - }) - a.So(err, should.BeNil) - if a.So(listRes, should.NotBeNil) { - a.So(listRes.Clients, should.BeEmpty) - } + listRes, err := reg.List(ctx, &ttnpb.ListClientsRequest{ + FieldMask: ttnpb.FieldMask("name"), + }) + a.So(err, should.BeNil) + if a.So(listRes, should.NotBeNil) { + a.So(listRes.Clients, should.BeEmpty) + } - _, err = reg.List(ctx, &ttnpb.ListClientsRequest{ - Collaborator: usr1.GetOrganizationOrUserIdentifiers(), - FieldMask: ttnpb.FieldMask("name"), - }) - if a.So(err, should.NotBeNil) { + _, err = reg.List(ctx, &ttnpb.ListClientsRequest{ + Collaborator: usr1.GetOrganizationOrUserIdentifiers(), + FieldMask: ttnpb.FieldMask("name"), + }) a.So(errors.IsPermissionDenied(err), should.BeTrue) - } - _, err = reg.Update(ctx, &ttnpb.UpdateClientRequest{ - Client: &ttnpb.Client{ - Ids: cli1.GetIds(), - Name: "Updated Name", - }, - FieldMask: ttnpb.FieldMask("name"), + _, err = reg.Update(ctx, &ttnpb.UpdateClientRequest{ + Client: &ttnpb.Client{ + Ids: cli1.GetIds(), + Name: "Updated Name", + }, + FieldMask: ttnpb.FieldMask("name"), + }) + a.So(errors.IsPermissionDenied(err), should.BeTrue) + + _, err = reg.Delete(ctx, cli1.GetIds()) + a.So(errors.IsPermissionDenied(err), should.BeTrue) }) - if a.So(err, should.NotBeNil) { + + t.Run("Admin read-only", func(t *testing.T) { // nolint:paralleltest + _, err := reg.Create(ctx, &ttnpb.CreateClientRequest{ + Client: &ttnpb.Client{ + Ids: &ttnpb.ClientIdentifiers{ClientId: "foo-cli"}, + }, + Collaborator: usr1.GetOrganizationOrUserIdentifiers(), + }, readOnlyAdminKeyCreds) a.So(errors.IsPermissionDenied(err), should.BeTrue) - } - _, err = reg.Delete(ctx, cli1.GetIds()) - if a.So(err, should.NotBeNil) { + _, err = reg.Get(ctx, &ttnpb.GetClientRequest{ + ClientIds: cli1.GetIds(), + FieldMask: ttnpb.FieldMask("name"), + }, readOnlyAdminKeyCreds) + // NOTE: This will always be nil because there are no client fields which are not public. + // Meaning that even without the proper rights the response is `nil`. + a.So(err, should.BeNil) + + _, err = reg.List(ctx, &ttnpb.ListClientsRequest{ + FieldMask: ttnpb.FieldMask("name"), + }, readOnlyAdminKeyCreds) + a.So(errors.IsPermissionDenied(err), should.BeFalse) + + _, err = reg.Update(ctx, &ttnpb.UpdateClientRequest{ + Client: &ttnpb.Client{ + Ids: cli1.GetIds(), + Name: "Updated Name", + }, + FieldMask: ttnpb.FieldMask("name"), + }, readOnlyAdminKeyCreds) a.So(errors.IsPermissionDenied(err), should.BeTrue) - } + + _, err = reg.Delete(ctx, cli1.GetIds(), readOnlyAdminKeyCreds) + a.So(errors.IsPermissionDenied(err), should.BeTrue) + }) }, withPrivateTestDatabase(p)) } diff --git a/pkg/identityserver/contact_info_registry.go b/pkg/identityserver/contact_info_registry.go index 78600be273..1d9a474e45 100644 --- a/pkg/identityserver/contact_info_registry.go +++ b/pkg/identityserver/contact_info_registry.go @@ -261,7 +261,7 @@ func (is *IdentityServer) requestContactInfoValidation( } go is.SendTemplateEmailToUsers( // nolint:errcheck is.FromRequestContext(ctx), - "validate", + ttnpb.GetNotificationTypeString(ttnpb.NotificationType_VALIDATE), func(_ context.Context, data email.TemplateData) (email.TemplateData, error) { validateData.TemplateData = data return validateData, nil diff --git a/pkg/identityserver/email.go b/pkg/identityserver/email.go index 8fe94fa487..3a6efbe1ff 100644 --- a/pkg/identityserver/email.go +++ b/pkg/identityserver/email.go @@ -63,7 +63,12 @@ func (is *IdentityServer) SendEmail(ctx context.Context, message *email.Message) } // SendTemplateEmailToUsers sends an email to users. -func (is *IdentityServer) SendTemplateEmailToUsers(ctx context.Context, templateName string, dataBuilder email.TemplateDataBuilder, receivers ...*ttnpb.User) error { +func (is *IdentityServer) SendTemplateEmailToUsers( + ctx context.Context, + templateName string, + dataBuilder email.TemplateDataBuilder, + receivers ...*ttnpb.User, +) error { networkConfig := is.configFromContext(ctx).Email.Network emailTemplate := email.GetTemplate(ctx, templateName) @@ -97,6 +102,18 @@ func (is *IdentityServer) SendNotificationEmailToUsers(ctx context.Context, noti var wg errgroup.Group for _, receiver := range receivers { receiver := receiver // shadow range variable. + + // Skips over the possible `support` user. + // This user can only be created via the API endpoints defined in the tenant access service. + if receiver.Ids.IDString() == ttnpb.SupportUserID { + continue + } + + // Skips over non approved users. + if receiver.State != ttnpb.State_STATE_APPROVED { + continue + } + wg.Go(func() error { templateData, err := emailNotification.DataBuilder( ctx, @@ -118,7 +135,12 @@ func (is *IdentityServer) SendNotificationEmailToUsers(ctx context.Context, noti var emailUserFields = store.FieldMask{"ids", "name", "primary_email_address"} // SendTemplateEmailToUserIDs looks up the users and sends them an email. -func (is *IdentityServer) SendTemplateEmailToUserIDs(ctx context.Context, templateName string, dataBuilder email.TemplateDataBuilder, receiverIDs ...*ttnpb.UserIdentifiers) error { +func (is *IdentityServer) SendTemplateEmailToUserIDs( + ctx context.Context, + templateName string, + dataBuilder email.TemplateDataBuilder, + receiverIDs ...*ttnpb.UserIdentifiers, +) error { var receivers []*ttnpb.User err := is.store.Transact(ctx, func(ctx context.Context, st store.Store) (err error) { receivers, err = st.FindUsers(ctx, receiverIDs, emailUserFields) @@ -130,7 +152,7 @@ func (is *IdentityServer) SendTemplateEmailToUserIDs(ctx context.Context, templa return is.SendTemplateEmailToUsers(ctx, templateName, dataBuilder, receivers...) } -var notificationEmailUserFields = store.FieldMask{"ids", "name", "primary_email_address", "admin"} +var notificationEmailUserFields = store.FieldMask{"ids", "name", "primary_email_address", "admin", "state"} // SendNotificationEmailToUserIDs looks up the users and sends them a notification email. func (is *IdentityServer) SendNotificationEmailToUserIDs(ctx context.Context, notification *ttnpb.Notification, receiverIDs ...*ttnpb.UserIdentifiers) error { diff --git a/pkg/identityserver/email_notification_preferences_test.go b/pkg/identityserver/email_notification_preferences_test.go new file mode 100644 index 0000000000..4eda4bfa0b --- /dev/null +++ b/pkg/identityserver/email_notification_preferences_test.go @@ -0,0 +1,150 @@ +// Copyright © 2024 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 identityserver + +import ( + "os" + "testing" + "time" + + "go.thethings.network/lorawan-stack/v3/pkg/identityserver/storetest" + "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" + "google.golang.org/grpc" + "google.golang.org/protobuf/types/known/timestamppb" +) + +func TestEmailNotificationPreferences(t *testing.T) { + p := &storetest.Population{} + + admin := p.NewUser() + admin.Admin = true + adminKey, _ := p.NewAPIKey(admin.GetEntityIdentifiers(), ttnpb.Right_RIGHT_ALL) + adminCreds := rpcCreds(adminKey) + admin.EmailNotificationPreferences = &ttnpb.EmailNotificationPreferences{ + Types: []ttnpb.NotificationType{ + ttnpb.NotificationType_API_KEY_CREATED, + }, + } + + usr1 := p.NewUser() + usr1.EmailNotificationPreferences = &ttnpb.EmailNotificationPreferences{ + Types: []ttnpb.NotificationType{ + ttnpb.NotificationType_API_KEY_CREATED, + }, + } + + app1 := p.NewApplication(admin.GetOrganizationOrUserIdentifiers()) + p.NewMembership( + usr1.GetOrganizationOrUserIdentifiers(), + app1.GetEntityIdentifiers(), + ttnpb.Right_RIGHT_APPLICATION_SETTINGS_BASIC, + ttnpb.Right_RIGHT_APPLICATION_SETTINGS_API_KEYS, + ttnpb.Right_RIGHT_APPLICATION_LINK, + ) + appKey, _ := p.NewAPIKey(app1.GetEntityIdentifiers(), + ttnpb.Right_RIGHT_APPLICATION_INFO, + ttnpb.Right_RIGHT_APPLICATION_LINK, + ttnpb.Right_RIGHT_APPLICATION_SETTINGS_API_KEYS, + ) + + now := timestamppb.Now() + usrIDs := &ttnpb.UserIdentifiers{ + UserId: "foo-usr", + } + + t.Parallel() + a, ctx := test.New(t) + + testWithIdentityServer(t, func(is *IdentityServer, cc *grpc.ClientConn) { + is.config.AdminRights.All = true + is.config.Email.Provider = "dir" + tempDir := t.TempDir() + is.config.Email.Dir = tempDir + + reg := ttnpb.NewApplicationAccessClient(cc) + userReg := ttnpb.NewUserRegistryClient(cc) + + // Test user not receiving email notification because this + // notification type is not in the list of preferences. + updated, err := reg.UpdateAPIKey(ctx, &ttnpb.UpdateApplicationAPIKeyRequest{ + ApplicationIds: app1.GetIds(), + ApiKey: &ttnpb.APIKey{ + Id: appKey.GetId(), + Rights: []ttnpb.Right{ + ttnpb.Right_RIGHT_APPLICATION_SETTINGS_BASIC, + ttnpb.Right_RIGHT_APPLICATION_SETTINGS_API_KEYS, + ttnpb.Right_RIGHT_APPLICATION_LINK, + }, + }, + FieldMask: ttnpb.FieldMask("rights"), + }, adminCreds) + if a.So(err, should.BeNil) && a.So(updated, should.NotBeNil) { + a.So(updated.Rights, should.Resemble, []ttnpb.Right{ + ttnpb.Right_RIGHT_APPLICATION_SETTINGS_BASIC, + ttnpb.Right_RIGHT_APPLICATION_SETTINGS_API_KEYS, + ttnpb.Right_RIGHT_APPLICATION_LINK, + }) + } + + time.Sleep(test.Delay * 1000) + + entries, err := os.ReadDir(tempDir) + a.So(err, should.BeNil) + a.So(entries, should.HaveLength, 0) + + // Test admin receiving email notification in spite of the list of preferences. + updatedUser, err := userReg.Create(ctx, &ttnpb.CreateUserRequest{ + User: &ttnpb.User{ + Ids: usrIDs, + Password: "test password", + CreatedAt: now, + UpdatedAt: now, + Name: "Foo User", + Description: "Foo User Description", + PrimaryEmailAddress: "foo@example.com", + State: ttnpb.State_STATE_REQUESTED, + }, + }, adminCreds) + if a.So(err, should.BeNil) && a.So(updatedUser, should.NotBeNil) { + a.So(updatedUser.State, should.Equal, ttnpb.State_STATE_REQUESTED) + } + + time.Sleep(test.Delay * 1000) + + entries, err = os.ReadDir(tempDir) + a.So(err, should.BeNil) + // Creating a user send two email, one for the admin to approve and another for the user to confirm the email. + a.So(entries, should.HaveLength, 2) + + // Test user receiving email notification because this notification type is in the list of preferences. + created, err := reg.CreateAPIKey(ctx, &ttnpb.CreateApplicationAPIKeyRequest{ + ApplicationIds: app1.GetIds(), + Name: "api-key-name", + Rights: []ttnpb.Right{ttnpb.Right_RIGHT_APPLICATION_INFO}, + }, adminCreds) + if a.So(err, should.BeNil) && a.So(created, should.NotBeNil) { + a.So(created.Name, should.Equal, "api-key-name") + a.So(created.Rights, should.Resemble, []ttnpb.Right{ttnpb.Right_RIGHT_APPLICATION_INFO}) + } + + time.Sleep(test.Delay * 1000) + + entries, err = os.ReadDir(tempDir) + a.So(err, should.BeNil) + a.So(entries, should.HaveLength, 3) + }, withPrivateTestDatabase(p)) +} diff --git a/pkg/identityserver/email_validation_registry.go b/pkg/identityserver/email_validation_registry.go index 6f6f171c10..3883a28b9f 100644 --- a/pkg/identityserver/email_validation_registry.go +++ b/pkg/identityserver/email_validation_registry.go @@ -140,7 +140,7 @@ func (is *IdentityServer) requestEmailValidation( )).Info("Sending validation email") go is.SendTemplateEmailToUsers( // nolint:errcheck is.Component.FromRequestContext(ctx), - "validate", + ttnpb.GetNotificationTypeString(ttnpb.NotificationType_VALIDATE), func(_ context.Context, data email.TemplateData) (email.TemplateData, error) { validateData.TemplateData = data return validateData, nil diff --git a/pkg/identityserver/email_validation_registry_test.go b/pkg/identityserver/email_validation_registry_test.go index f5a0ce4ffd..40a277b13f 100644 --- a/pkg/identityserver/email_validation_registry_test.go +++ b/pkg/identityserver/email_validation_registry_test.go @@ -29,6 +29,11 @@ import ( func TestEmailValidation(t *testing.T) { p := &storetest.Population{} + readOnlyAdmin := p.NewUser() + readOnlyAdmin.Admin = true + readOnlyAdminKey, _ := p.NewAPIKey(readOnlyAdmin.GetEntityIdentifiers(), ttnpb.AllReadAdminRights.GetRights()...) + readOnlyAdminKeyCreds := rpcCreds(readOnlyAdminKey) + usr1 := p.NewUser() usr1.PrimaryEmailAddress = "usr1@email.com" usr1Key, _ := p.NewAPIKey(usr1.GetEntityIdentifiers(), ttnpb.Right_RIGHT_ALL) @@ -58,6 +63,11 @@ func TestEmailValidation(t *testing.T) { a.So(err, should.NotBeNil) a.So(errors.IsPermissionDenied(err), should.BeTrue) + // Only reader rights. + _, err = reg.RequestValidation(ctx, usr1.Ids, readOnlyAdminKeyCreds) + a.So(err, should.NotBeNil) + a.So(errors.IsPermissionDenied(err), should.BeTrue) + // Proper Request. validation, err := reg.RequestValidation(ctx, usr1.Ids, usr1Creds) a.So(err, should.BeNil) diff --git a/pkg/identityserver/end_device_registry_test.go b/pkg/identityserver/end_device_registry_test.go index c01de47556..77de955958 100644 --- a/pkg/identityserver/end_device_registry_test.go +++ b/pkg/identityserver/end_device_registry_test.go @@ -30,61 +30,110 @@ import ( const noOfDevices = 3 -func TestEndDevicesPermissionDenied(t *testing.T) { +func TestEndDevicesPermissions(t *testing.T) { p := &storetest.Population{} usr1 := p.NewUser() app1 := p.NewApplication(usr1.GetOrganizationOrUserIdentifiers()) dev1 := p.NewEndDevice(app1.GetIds()) + readOnlyAdmin := p.NewUser() + readOnlyAdmin.Admin = true + readOnlyAdminKey, _ := p.NewAPIKey(readOnlyAdmin.GetEntityIdentifiers(), ttnpb.AllReadAdminRights.GetRights()...) + readOnlyAdminKeyCreds := rpcCreds(readOnlyAdminKey) + t.Parallel() a, ctx := test.New(t) testWithIdentityServer(t, func(_ *IdentityServer, cc *grpc.ClientConn) { reg := ttnpb.NewEndDeviceRegistryClient(cc) - _, err := reg.Create(ctx, &ttnpb.CreateEndDeviceRequest{ - EndDevice: &ttnpb.EndDevice{ - Ids: &ttnpb.EndDeviceIdentifiers{ - ApplicationIds: app1.GetIds(), - DeviceId: "foo-dev", + t.Run("Invalid credentials", func(t *testing.T) { // nolint:paralleltest + _, err := reg.Create(ctx, &ttnpb.CreateEndDeviceRequest{ + EndDevice: &ttnpb.EndDevice{ + Ids: &ttnpb.EndDeviceIdentifiers{ + ApplicationIds: app1.GetIds(), + DeviceId: "foo-dev", + }, }, - }, - }) - if a.So(err, should.NotBeNil) { - a.So(errors.IsPermissionDenied(err), should.BeTrue) - } + }) + if a.So(err, should.NotBeNil) { + a.So(errors.IsPermissionDenied(err), should.BeTrue) + } - _, err = reg.Get(ctx, &ttnpb.GetEndDeviceRequest{ - EndDeviceIds: dev1.GetIds(), - FieldMask: ttnpb.FieldMask("name"), - }) - if a.So(err, should.NotBeNil) { - a.So(errors.IsPermissionDenied(err), should.BeTrue) - } + _, err = reg.Get(ctx, &ttnpb.GetEndDeviceRequest{ + EndDeviceIds: dev1.GetIds(), + FieldMask: ttnpb.FieldMask("name"), + }) + if a.So(err, should.NotBeNil) { + a.So(errors.IsPermissionDenied(err), should.BeTrue) + } - _, err = reg.List(ctx, &ttnpb.ListEndDevicesRequest{ - ApplicationIds: app1.GetIds(), - FieldMask: ttnpb.FieldMask("name"), - }) - if a.So(err, should.NotBeNil) { - a.So(errors.IsPermissionDenied(err), should.BeTrue) - } + _, err = reg.List(ctx, &ttnpb.ListEndDevicesRequest{ + ApplicationIds: app1.GetIds(), + FieldMask: ttnpb.FieldMask("name"), + }) + if a.So(err, should.NotBeNil) { + a.So(errors.IsPermissionDenied(err), should.BeTrue) + } - _, err = reg.Update(ctx, &ttnpb.UpdateEndDeviceRequest{ - EndDevice: &ttnpb.EndDevice{ - Ids: dev1.GetIds(), - Name: "Updated Name", - }, - FieldMask: ttnpb.FieldMask("name"), + _, err = reg.Update(ctx, &ttnpb.UpdateEndDeviceRequest{ + EndDevice: &ttnpb.EndDevice{ + Ids: dev1.GetIds(), + Name: "Updated Name", + }, + FieldMask: ttnpb.FieldMask("name"), + }) + if a.So(err, should.NotBeNil) { + a.So(errors.IsPermissionDenied(err), should.BeTrue) + } + + _, err = reg.Delete(ctx, dev1.GetIds()) + if a.So(err, should.NotBeNil) { + a.So(errors.IsPermissionDenied(err), should.BeTrue) + } }) - if a.So(err, should.NotBeNil) { - a.So(errors.IsPermissionDenied(err), should.BeTrue) - } - _, err = reg.Delete(ctx, dev1.GetIds()) - if a.So(err, should.NotBeNil) { - a.So(errors.IsPermissionDenied(err), should.BeTrue) - } + t.Run("Admin read-only", func(t *testing.T) { // nolint:paralleltest + _, err := reg.Create(ctx, &ttnpb.CreateEndDeviceRequest{ + EndDevice: &ttnpb.EndDevice{ + Ids: &ttnpb.EndDeviceIdentifiers{ + ApplicationIds: app1.GetIds(), + DeviceId: "foo-dev", + }, + }, + }, readOnlyAdminKeyCreds) + if a.So(err, should.NotBeNil) { + a.So(errors.IsPermissionDenied(err), should.BeTrue) + } + + _, err = reg.Get(ctx, &ttnpb.GetEndDeviceRequest{ + EndDeviceIds: dev1.GetIds(), + FieldMask: ttnpb.FieldMask("name"), + }, readOnlyAdminKeyCreds) + a.So(errors.IsPermissionDenied(err), should.BeFalse) + + _, err = reg.List(ctx, &ttnpb.ListEndDevicesRequest{ + ApplicationIds: app1.GetIds(), + FieldMask: ttnpb.FieldMask("name"), + }, readOnlyAdminKeyCreds) + a.So(errors.IsPermissionDenied(err), should.BeFalse) + + _, err = reg.Update(ctx, &ttnpb.UpdateEndDeviceRequest{ + EndDevice: &ttnpb.EndDevice{ + Ids: dev1.GetIds(), + Name: "Updated Name", + }, + FieldMask: ttnpb.FieldMask("name"), + }, readOnlyAdminKeyCreds) + if a.So(err, should.NotBeNil) { + a.So(errors.IsPermissionDenied(err), should.BeTrue) + } + + _, err = reg.Delete(ctx, dev1.GetIds()) + if a.So(err, should.NotBeNil) { + a.So(errors.IsPermissionDenied(err), should.BeTrue) + } + }) }, withPrivateTestDatabase(p)) } @@ -219,7 +268,7 @@ func TestEndDevicesPagination(t *testing.T) { }, withPrivateTestDatabase(p)) } -func TestEndDevicesBatchOperations(t *testing.T) { +func TestEndDevicesBatchOperationsPermissions(t *testing.T) { t.Parallel() a, ctx := test.New(t) p := &storetest.Population{} @@ -246,6 +295,11 @@ func TestEndDevicesBatchOperations(t *testing.T) { writeKey, _ := p.NewAPIKey(usr1.GetEntityIdentifiers(), ttnpb.Right_RIGHT_APPLICATION_DEVICES_WRITE) writeCreds := rpcCreds(writeKey) + readOnlyAdmin := p.NewUser() + readOnlyAdmin.Admin = true + readOnlyAdminKey, _ := p.NewAPIKey(readOnlyAdmin.GetEntityIdentifiers(), ttnpb.AllReadAdminRights.GetRights()...) + readOnlyAdminKeyCreds := rpcCreds(readOnlyAdminKey) + testWithIdentityServer(t, func(is *IdentityServer, cc *grpc.ClientConn) { reg := ttnpb.NewEndDeviceBatchRegistryClient(cc) @@ -276,8 +330,50 @@ func TestEndDevicesBatchOperations(t *testing.T) { }, writeCreds) a.So(errors.IsPermissionDenied(err), should.BeTrue) + t.Run("Admin read-only", func(t *testing.T) { // nolint:paralleltest + devs, err := reg.Get(ctx, &ttnpb.BatchGetEndDevicesRequest{ + ApplicationIds: app1.GetIds(), + DeviceIds: devIDs, + }, readOnlyAdminKeyCreds) + a.So(err, should.BeNil) + a.So(devs, should.NotBeNil) + a.So(devs.GetEndDevices(), should.HaveLength, noOfDevices) + }) + }, withPrivateTestDatabase(p)) +} + +func TestEndDevicesBatchOperations(t *testing.T) { + t.Parallel() + a, ctx := test.New(t) + p := &storetest.Population{} + usr1 := p.NewUser() + app1 := p.NewApplication(usr1.GetOrganizationOrUserIdentifiers()) + devIDs := make([]string, 0, noOfDevices) + for i := 0; i < noOfDevices; i++ { + dev := p.NewEndDevice(app1.GetIds()) + dev.Attributes = map[string]string{ + "foo": "bar", + } + dev.Locations = map[string]*ttnpb.Location{ + "foo": { + Latitude: 1, + Longitude: 2, + Altitude: 3, + }, + } + devIDs = append(devIDs, dev.GetIds().DeviceId) + } + readKey, _ := p.NewAPIKey(usr1.GetEntityIdentifiers(), ttnpb.Right_RIGHT_APPLICATION_DEVICES_READ) + readCreds := rpcCreds(readKey) + + writeKey, _ := p.NewAPIKey(usr1.GetEntityIdentifiers(), ttnpb.Right_RIGHT_APPLICATION_DEVICES_WRITE) + writeCreds := rpcCreds(writeKey) + + testWithIdentityServer(t, func(is *IdentityServer, cc *grpc.ClientConn) { + reg := ttnpb.NewEndDeviceBatchRegistryClient(cc) + // Unknown device ignored. - _, err = reg.Get(ctx, &ttnpb.BatchGetEndDevicesRequest{ + _, err := reg.Get(ctx, &ttnpb.BatchGetEndDevicesRequest{ ApplicationIds: app1.GetIds(), DeviceIds: []string{ "unknown", diff --git a/pkg/identityserver/entity_access.go b/pkg/identityserver/entity_access.go index 95d16f761c..98f203926e 100644 --- a/pkg/identityserver/entity_access.go +++ b/pkg/identityserver/entity_access.go @@ -122,7 +122,13 @@ func (is *IdentityServer) authInfo(ctx context.Context) (info *ttnpb.AuthInfoRes var fetch func(ctx context.Context, st store.Store) error res := &ttnpb.AuthInfoResponse{} - userFieldMask := []string{"admin", "state", "state_description", "primary_email_address_validated_at"} + userFieldMask := []string{ + "admin", + "state", + "state_description", + "primary_email_address_validated_at", + "universal_rights", + } clientFieldMask := []string{"state", "state_description"} var user *ttnpb.User var userRights *ttnpb.Rights @@ -303,6 +309,10 @@ func (is *IdentityServer) authInfo(ctx context.Context) (info *ttnpb.AuthInfoRes } else { res.UniversalRights = ttnpb.AllAdminRights.Implied().Intersect(userRights) } + + if len(user.UniversalRights) > 0 { + res.UniversalRights = res.UniversalRights.Intersect(ttnpb.RightsFrom(user.UniversalRights...)) + } } case ttnpb.State_STATE_REJECTED: // Go to profile page, delete account. diff --git a/pkg/identityserver/entity_access_test.go b/pkg/identityserver/entity_access_test.go index d6173444a4..2dfe01a05e 100644 --- a/pkg/identityserver/entity_access_test.go +++ b/pkg/identityserver/entity_access_test.go @@ -54,6 +54,12 @@ func TestEntityAccess(t *testing.T) { adminUsrKey, _ := p.NewAPIKey(adminUsr.GetEntityIdentifiers(), ttnpb.Right_RIGHT_ALL) adminUsrCreds := rpcCreds(adminUsrKey) + readOnlyAdmin := p.NewUser() + readOnlyAdmin.Admin = true + readOnlyAdmin.UniversalRights = ttnpb.AllReadAdminRights.GetRights() + readOnlyAdminKey, _ := p.NewAPIKey(readOnlyAdmin.GetEntityIdentifiers(), ttnpb.Right_RIGHT_ALL) + readOnlyAdminKeyCreds := rpcCreds(readOnlyAdminKey) + expiredKey, storedKey := p.NewAPIKey(adminUsr.GetEntityIdentifiers(), ttnpb.Right_RIGHT_ALL) storedKey.ExpiresAt = timestamppb.New(time.Now().Add(-10 * time.Minute)) expiredCreds := rpcCreds(expiredKey) @@ -72,7 +78,14 @@ func TestEntityAccess(t *testing.T) { a.So(md.Get("warning"), should.Contain, "Restricted rights while account pending") if a.So(authInfo, should.NotBeNil) && a.So(authInfo.GetApiKey(), should.NotBeNil) { rights := ttnpb.RightsFrom(authInfo.GetApiKey().GetApiKey().GetRights()...) - a.So(rights.IncludesAll(ttnpb.Right_RIGHT_USER_INFO, ttnpb.Right_RIGHT_USER_SETTINGS_BASIC, ttnpb.Right_RIGHT_USER_DELETE), should.BeTrue) + a.So( + rights.IncludesAll( + ttnpb.Right_RIGHT_USER_INFO, + ttnpb.Right_RIGHT_USER_SETTINGS_BASIC, + ttnpb.Right_RIGHT_USER_DELETE, + ), + should.BeTrue, + ) } } }) @@ -117,6 +130,25 @@ func TestEntityAccess(t *testing.T) { } }) + t.Run("Read-only Admin User", func(t *testing.T) { + a, ctx := test.New(t) + var md metadata.MD + authInfo, err := cli.AuthInfo(ctx, ttnpb.Empty, readOnlyAdminKeyCreds, grpc.Header(&md)) + if a.So(err, should.BeNil) && a.So(authInfo, should.NotBeNil) { + a.So(authInfo.GetIsAdmin(), should.BeTrue) + a.So(authInfo.GetUniversalRights().GetRights(), should.NotBeEmpty) + a.So( + authInfo.GetUniversalRights().IncludesAll(ttnpb.AllReadAdminRights.GetRights()...), + should.BeTrue, + ) + a.So( + authInfo.GetUniversalRights().IncludesAll(ttnpb.Right_RIGHT_USER_APPLICATIONS_CREATE), + should.BeFalse, + ) + + } + }) + t.Run("Cluster Peer", func(t *testing.T) { a, ctx := test.New(t) var md metadata.MD diff --git a/pkg/identityserver/gateway_access.go b/pkg/identityserver/gateway_access.go index 7bf451a8c9..0efbc87893 100644 --- a/pkg/identityserver/gateway_access.go +++ b/pkg/identityserver/gateway_access.go @@ -101,12 +101,11 @@ func (is *IdentityServer) createGatewayAPIKey( events.Publish(evtCreateGatewayAPIKey.NewWithIdentifiersAndData(ctx, req.GetGatewayIds(), key)) go is.notifyInternal(ctx, &ttnpb.CreateNotificationRequest{ EntityIds: req.GetGatewayIds().GetEntityIdentifiers(), - NotificationType: "api_key_created", + NotificationType: ttnpb.GetNotificationTypeString(ttnpb.NotificationType_API_KEY_CREATED), Data: ttnpb.MustMarshalAny(key), Receivers: []ttnpb.NotificationReceiver{ ttnpb.NotificationReceiver_NOTIFICATION_RECEIVER_ADMINISTRATIVE_CONTACT, }, - Email: true, }) key.Key = token @@ -219,12 +218,11 @@ func (is *IdentityServer) updateGatewayAPIKey( events.Publish(evtUpdateGatewayAPIKey.NewWithIdentifiersAndData(ctx, req.GetGatewayIds(), key)) go is.notifyInternal(ctx, &ttnpb.CreateNotificationRequest{ EntityIds: req.GetGatewayIds().GetEntityIdentifiers(), - NotificationType: "api_key_changed", + NotificationType: ttnpb.GetNotificationTypeString(ttnpb.NotificationType_API_KEY_CHANGED), Data: ttnpb.MustMarshalAny(key), Receivers: []ttnpb.NotificationReceiver{ ttnpb.NotificationReceiver_NOTIFICATION_RECEIVER_ADMINISTRATIVE_CONTACT, }, - Email: true, }) return key, nil @@ -360,12 +358,11 @@ func (is *IdentityServer) setGatewayCollaborator( )) go is.notifyInternal(ctx, &ttnpb.CreateNotificationRequest{ EntityIds: req.GetGatewayIds().GetEntityIdentifiers(), - NotificationType: "collaborator_changed", + NotificationType: ttnpb.GetNotificationTypeString(ttnpb.NotificationType_COLLABORATOR_CHANGED), Data: ttnpb.MustMarshalAny(req.GetCollaborator()), Receivers: []ttnpb.NotificationReceiver{ ttnpb.NotificationReceiver_NOTIFICATION_RECEIVER_ADMINISTRATIVE_CONTACT, }, - Email: false, }) } else { events.Publish(evtDeleteGatewayCollaborator.New( diff --git a/pkg/identityserver/gateway_access_test.go b/pkg/identityserver/gateway_access_test.go index 75a265b9b0..ab7bf70478 100644 --- a/pkg/identityserver/gateway_access_test.go +++ b/pkg/identityserver/gateway_access_test.go @@ -26,6 +26,127 @@ import ( "google.golang.org/grpc" ) +func TestGatewayAPIKeysPermissions(t *testing.T) { // nolint:gocyclo + p := &storetest.Population{} + + readOnlyAdmin := p.NewUser() + readOnlyAdmin.Admin = true + readOnlyAdminKey, _ := p.NewAPIKey(readOnlyAdmin.GetEntityIdentifiers(), ttnpb.AllReadAdminRights.GetRights()...) + readOnlyAdminKeyCreds := rpcCreds(readOnlyAdminKey) + + usr1 := p.NewUser() + gtw1 := p.NewGateway(usr1.GetOrganizationOrUserIdentifiers()) + + gtwKey, _ := p.NewAPIKey(gtw1.GetEntityIdentifiers(), + ttnpb.Right_RIGHT_GATEWAY_INFO, + ttnpb.Right_RIGHT_GATEWAY_LINK, + ) + gtwCreds := rpcCreds(gtwKey) + + t.Parallel() + a, ctx := test.New(t) + + testWithIdentityServer(t, func(is *IdentityServer, cc *grpc.ClientConn) { + is.config.AdminRights.All = true + + reg := ttnpb.NewGatewayAccessClient(cc) + + t.Run("Invalid credentials", func(t *testing.T) { // nolint:paralleltest + for _, opts := range [][]grpc.CallOption{nil, {gtwCreds}} { + created, err := reg.CreateAPIKey(ctx, &ttnpb.CreateGatewayAPIKeyRequest{ + GatewayIds: gtw1.GetIds(), + Name: "api-key-name", + Rights: []ttnpb.Right{ttnpb.Right_RIGHT_GATEWAY_INFO}, + }, opts...) + if a.So(err, should.NotBeNil) && a.So(errors.IsPermissionDenied(err), should.BeTrue) { + a.So(created, should.BeNil) + } + + list, err := reg.ListAPIKeys(ctx, &ttnpb.ListGatewayAPIKeysRequest{ + GatewayIds: gtw1.GetIds(), + }, opts...) + if a.So(err, should.NotBeNil) && a.So(errors.IsPermissionDenied(err), should.BeTrue) { + a.So(list, should.BeNil) + } + + got, err := reg.GetAPIKey(ctx, &ttnpb.GetGatewayAPIKeyRequest{ + GatewayIds: gtw1.GetIds(), + KeyId: gtwKey.GetId(), + }, opts...) + if a.So(err, should.NotBeNil) && a.So(errors.IsPermissionDenied(err), should.BeTrue) { + a.So(got, should.BeNil) + } + + updated, err := reg.UpdateAPIKey(ctx, &ttnpb.UpdateGatewayAPIKeyRequest{ + GatewayIds: gtw1.GetIds(), + ApiKey: &ttnpb.APIKey{ + Id: gtwKey.GetId(), + Name: "api-key-name-updated", + }, + FieldMask: ttnpb.FieldMask("name"), + }, opts...) + if a.So(err, should.NotBeNil) && a.So(errors.IsPermissionDenied(err), should.BeTrue) { + a.So(updated, should.BeNil) + } + + _, err = reg.DeleteAPIKey(ctx, &ttnpb.DeleteGatewayAPIKeyRequest{ + GatewayIds: gtw1.GetIds(), + KeyId: created.GetId(), + }, opts...) + if !a.So(errors.IsPermissionDenied(err), should.BeTrue) { + t.FailNow() + } + } + }) + + t.Run("Admin read-only", func(t *testing.T) { // nolint:paralleltest + _, err := reg.CreateAPIKey(ctx, &ttnpb.CreateGatewayAPIKeyRequest{ + GatewayIds: gtw1.GetIds(), + Name: "api-key-name", + Rights: []ttnpb.Right{ttnpb.Right_RIGHT_GATEWAY_INFO}, + }, readOnlyAdminKeyCreds) + if a.So(err, should.NotBeNil) { + a.So(errors.IsPermissionDenied(err), should.BeTrue) + } + + _, err = reg.ListAPIKeys(ctx, &ttnpb.ListGatewayAPIKeysRequest{ + GatewayIds: gtw1.GetIds(), + }, readOnlyAdminKeyCreds) + if a.So(err, should.NotBeNil) { + a.So(errors.IsPermissionDenied(err), should.BeTrue) + } + + _, err = reg.GetAPIKey(ctx, &ttnpb.GetGatewayAPIKeyRequest{ + GatewayIds: gtw1.GetIds(), + KeyId: gtwKey.GetId(), + }, readOnlyAdminKeyCreds) + if a.So(err, should.NotBeNil) { + a.So(errors.IsPermissionDenied(err), should.BeTrue) + } + + _, err = reg.UpdateAPIKey(ctx, &ttnpb.UpdateGatewayAPIKeyRequest{ + GatewayIds: gtw1.GetIds(), + ApiKey: &ttnpb.APIKey{ + Id: gtwKey.GetId(), + Name: "api-key-name-updated", + }, + FieldMask: ttnpb.FieldMask("name"), + }, readOnlyAdminKeyCreds) + if a.So(err, should.NotBeNil) { + a.So(errors.IsPermissionDenied(err), should.BeTrue) + } + + _, err = reg.DeleteAPIKey(ctx, &ttnpb.DeleteGatewayAPIKeyRequest{ + GatewayIds: gtw1.GetIds(), + KeyId: "unknown-key", + }, readOnlyAdminKeyCreds) + if a.So(err, should.NotBeNil) { + a.So(errors.IsPermissionDenied(err), should.BeTrue) + } + }) + }, withPrivateTestDatabase(p)) +} + func TestGatewayAPIKeys(t *testing.T) { // nolint:gocyclo p := &storetest.Population{} @@ -50,7 +171,6 @@ func TestGatewayAPIKeys(t *testing.T) { // nolint:gocyclo ttnpb.Right_RIGHT_GATEWAY_INFO, ttnpb.Right_RIGHT_GATEWAY_LINK, ) - gtwCreds := rpcCreds(gtwKey) t.Parallel() a, ctx := test.New(t) @@ -147,53 +267,6 @@ func TestGatewayAPIKeys(t *testing.T) { // nolint:gocyclo }) } - // API Key CRUD with different invalid credentials. - for _, opts := range [][]grpc.CallOption{nil, {gtwCreds}} { - created, err := reg.CreateAPIKey(ctx, &ttnpb.CreateGatewayAPIKeyRequest{ - GatewayIds: gtw1.GetIds(), - Name: "api-key-name", - Rights: []ttnpb.Right{ttnpb.Right_RIGHT_GATEWAY_INFO}, - }, opts...) - if a.So(err, should.NotBeNil) && a.So(errors.IsPermissionDenied(err), should.BeTrue) { - a.So(created, should.BeNil) - } - - list, err := reg.ListAPIKeys(ctx, &ttnpb.ListGatewayAPIKeysRequest{ - GatewayIds: gtw1.GetIds(), - }, opts...) - if a.So(err, should.NotBeNil) && a.So(errors.IsPermissionDenied(err), should.BeTrue) { - a.So(list, should.BeNil) - } - - got, err := reg.GetAPIKey(ctx, &ttnpb.GetGatewayAPIKeyRequest{ - GatewayIds: gtw1.GetIds(), - KeyId: gtwKey.GetId(), - }, opts...) - if a.So(err, should.NotBeNil) && a.So(errors.IsPermissionDenied(err), should.BeTrue) { - a.So(got, should.BeNil) - } - - updated, err := reg.UpdateAPIKey(ctx, &ttnpb.UpdateGatewayAPIKeyRequest{ - GatewayIds: gtw1.GetIds(), - ApiKey: &ttnpb.APIKey{ - Id: gtwKey.GetId(), - Name: "api-key-name-updated", - }, - FieldMask: ttnpb.FieldMask("name"), - }, opts...) - if a.So(err, should.NotBeNil) && a.So(errors.IsPermissionDenied(err), should.BeTrue) { - a.So(updated, should.BeNil) - } - - _, err = reg.DeleteAPIKey(ctx, &ttnpb.DeleteGatewayAPIKeyRequest{ - GatewayIds: gtw1.GetIds(), - KeyId: created.GetId(), - }, opts...) - if !a.So(errors.IsPermissionDenied(err), should.BeTrue) { - t.FailNow() - } - } - // API Key CRUD with different valid credentials. for _, opts := range [][]grpc.CallOption{{adminCreds}, {usr1Creds}, {limitedCreds}} { created, err := reg.CreateAPIKey(ctx, &ttnpb.CreateGatewayAPIKeyRequest{ @@ -287,6 +360,99 @@ func TestGatewayAPIKeys(t *testing.T) { // nolint:gocyclo }, withPrivateTestDatabase(p)) } +func TestGatewayCollaboratorsPermissions(t *testing.T) { // nolint:gocyclo + p := &storetest.Population{} + + usr1 := p.NewUser() + gtw1 := p.NewGateway(usr1.GetOrganizationOrUserIdentifiers()) + + gtwKey, _ := p.NewAPIKey(gtw1.GetEntityIdentifiers(), + ttnpb.Right_RIGHT_GATEWAY_INFO, + ttnpb.Right_RIGHT_GATEWAY_LINK, + ) + gtwCreds := rpcCreds(gtwKey) + + readOnlyAdmin := p.NewUser() + readOnlyAdmin.Admin = true + readOnlyAdminKey, _ := p.NewAPIKey(readOnlyAdmin.GetEntityIdentifiers(), ttnpb.AllReadAdminRights.GetRights()...) + readOnlyAdminKeyCreds := rpcCreds(readOnlyAdminKey) + + usr2 := p.NewUser() + p.NewMembership( + usr2.GetOrganizationOrUserIdentifiers(), + gtw1.GetEntityIdentifiers(), + ttnpb.Right_RIGHT_GATEWAY_INFO, + ttnpb.Right_RIGHT_GATEWAY_LINK, + ) + + t.Parallel() + a, ctx := test.New(t) + + testWithIdentityServer(t, func(is *IdentityServer, cc *grpc.ClientConn) { + is.config.AdminRights.All = true + + reg := ttnpb.NewGatewayAccessClient(cc) + + t.Run("Permission denied", func(t *testing.T) { // nolint:paralleltest + for _, opts := range [][]grpc.CallOption{nil, {gtwCreds}} { + _, err := reg.SetCollaborator(ctx, &ttnpb.SetGatewayCollaboratorRequest{ + GatewayIds: gtw1.GetIds(), + Collaborator: &ttnpb.Collaborator{ + Ids: usr2.GetOrganizationOrUserIdentifiers(), + Rights: []ttnpb.Right{ttnpb.Right_RIGHT_GATEWAY_INFO}, + }, + }, opts...) + if a.So(err, should.NotBeNil) { + a.So(errors.IsPermissionDenied(err), should.BeTrue) + } + + got, err := reg.GetCollaborator(ctx, &ttnpb.GetGatewayCollaboratorRequest{ + GatewayIds: gtw1.GetIds(), + Collaborator: usr2.GetOrganizationOrUserIdentifiers(), + }, opts...) + if a.So(err, should.NotBeNil) && a.So(errors.IsPermissionDenied(err), should.BeTrue) { + a.So(got, should.BeNil) + } + + // ListCollaborators without credentials. + list, err := reg.ListCollaborators(ctx, &ttnpb.ListGatewayCollaboratorsRequest{ + GatewayIds: gtw1.GetIds(), + }) + if a.So(err, should.NotBeNil) && a.So(errors.IsUnauthenticated(err), should.BeTrue) { + a.So(list, should.BeNil) + } + } + }) + + t.Run("Admin read-only", func(t *testing.T) { // nolint:paralleltest + _, err := reg.SetCollaborator(ctx, &ttnpb.SetGatewayCollaboratorRequest{ + GatewayIds: gtw1.GetIds(), + Collaborator: &ttnpb.Collaborator{ + Ids: usr2.GetOrganizationOrUserIdentifiers(), + Rights: []ttnpb.Right{ttnpb.Right_RIGHT_GATEWAY_INFO}, + }, + }, readOnlyAdminKeyCreds) + if a.So(err, should.NotBeNil) { + a.So(errors.IsPermissionDenied(err), should.BeTrue) + } + + _, err = reg.GetCollaborator(ctx, &ttnpb.GetGatewayCollaboratorRequest{ + GatewayIds: gtw1.GetIds(), + Collaborator: usr2.GetOrganizationOrUserIdentifiers(), + }, readOnlyAdminKeyCreds) + if a.So(err, should.NotBeNil) { + a.So(errors.IsPermissionDenied(err), should.BeTrue) + } + + // ListCollaborators without credentials. + _, err = reg.ListCollaborators(ctx, &ttnpb.ListGatewayCollaboratorsRequest{ + GatewayIds: gtw1.GetIds(), + }, readOnlyAdminKeyCreds) + a.So(errors.IsPermissionDenied(err), should.BeFalse) + }) + }, withPrivateTestDatabase(p)) +} + func TestGatewayCollaborators(t *testing.T) { // nolint:gocyclo p := &storetest.Population{} diff --git a/pkg/identityserver/gateway_registry.go b/pkg/identityserver/gateway_registry.go index 4d624cf3b3..acb6ce3fef 100644 --- a/pkg/identityserver/gateway_registry.go +++ b/pkg/identityserver/gateway_registry.go @@ -727,6 +727,11 @@ func (is *IdentityServer) purgeGateway(ctx context.Context, ids *ttnpb.GatewayId if !is.IsAdmin(ctx) { return nil, errAdminsPurgeGateways.New() } + if err := rights.RequireGateway( + store.WithSoftDeleted(ctx, false), ids, ttnpb.Right_RIGHT_GATEWAY_PURGE, + ); err != nil { + return nil, err + } err := is.store.Transact(ctx, func(ctx context.Context, st store.Store) error { // delete related API keys before purging the gateway err := st.DeleteEntityAPIKeys(ctx, ids.GetEntityIdentifiers()) diff --git a/pkg/identityserver/gateway_registry_test.go b/pkg/identityserver/gateway_registry_test.go index 6d768985f4..6b1b158951 100644 --- a/pkg/identityserver/gateway_registry_test.go +++ b/pkg/identityserver/gateway_registry_test.go @@ -31,66 +31,118 @@ import ( const noOfGateways = 3 -func TestGatewaysPermissionDenied(t *testing.T) { +func TestGatewaysPermissions(t *testing.T) { p := &storetest.Population{} usr1 := p.NewUser() gtw1 := p.NewGateway(usr1.GetOrganizationOrUserIdentifiers()) + readOnlyAdmin := p.NewUser() + readOnlyAdmin.Admin = true + readOnlyAdminKey, _ := p.NewAPIKey(readOnlyAdmin.GetEntityIdentifiers(), ttnpb.AllReadAdminRights.GetRights()...) + readOnlyAdminKeyCreds := rpcCreds(readOnlyAdminKey) + t.Parallel() a, ctx := test.New(t) testWithIdentityServer(t, func(_ *IdentityServer, cc *grpc.ClientConn) { reg := ttnpb.NewGatewayRegistryClient(cc) - _, err := reg.Create(ctx, &ttnpb.CreateGatewayRequest{ - Gateway: &ttnpb.Gateway{ - Ids: &ttnpb.GatewayIdentifiers{GatewayId: "foo-gtw"}, - }, - Collaborator: usr1.GetOrganizationOrUserIdentifiers(), - }) - if a.So(err, should.NotBeNil) { - a.So(errors.IsPermissionDenied(err), should.BeTrue) - } + t.Run("Invalid credentials", func(t *testing.T) { // nolint:paralleltest + _, err := reg.Create(ctx, &ttnpb.CreateGatewayRequest{ + Gateway: &ttnpb.Gateway{ + Ids: &ttnpb.GatewayIdentifiers{GatewayId: "foo-gtw"}, + }, + Collaborator: usr1.GetOrganizationOrUserIdentifiers(), + }) + if a.So(err, should.NotBeNil) { + a.So(errors.IsPermissionDenied(err), should.BeTrue) + } - _, err = reg.Get(ctx, &ttnpb.GetGatewayRequest{ - GatewayIds: gtw1.GetIds(), - FieldMask: ttnpb.FieldMask("name"), - }) - if a.So(err, should.NotBeNil) { - a.So(errors.IsUnauthenticated(err), should.BeTrue) - } + _, err = reg.Get(ctx, &ttnpb.GetGatewayRequest{ + GatewayIds: gtw1.GetIds(), + FieldMask: ttnpb.FieldMask("name"), + }) + if a.So(err, should.NotBeNil) { + a.So(errors.IsUnauthenticated(err), should.BeTrue) + } - listRes, err := reg.List(ctx, &ttnpb.ListGatewaysRequest{ - FieldMask: ttnpb.FieldMask("name"), - }) - a.So(err, should.BeNil) - if a.So(listRes, should.NotBeNil) { - a.So(listRes.Gateways, should.BeEmpty) - } + listRes, err := reg.List(ctx, &ttnpb.ListGatewaysRequest{ + FieldMask: ttnpb.FieldMask("name"), + }) + a.So(err, should.BeNil) + if a.So(listRes, should.NotBeNil) { + a.So(listRes.Gateways, should.BeEmpty) + } - _, err = reg.List(ctx, &ttnpb.ListGatewaysRequest{ - Collaborator: usr1.GetOrganizationOrUserIdentifiers(), - FieldMask: ttnpb.FieldMask("name"), - }) - if a.So(err, should.NotBeNil) { - a.So(errors.IsPermissionDenied(err), should.BeTrue) - } + _, err = reg.List(ctx, &ttnpb.ListGatewaysRequest{ + Collaborator: usr1.GetOrganizationOrUserIdentifiers(), + FieldMask: ttnpb.FieldMask("name"), + }) + if a.So(err, should.NotBeNil) { + a.So(errors.IsPermissionDenied(err), should.BeTrue) + } - _, err = reg.Update(ctx, &ttnpb.UpdateGatewayRequest{ - Gateway: &ttnpb.Gateway{ - Ids: gtw1.GetIds(), - Name: "Updated Name", - }, - FieldMask: ttnpb.FieldMask("name"), + _, err = reg.Update(ctx, &ttnpb.UpdateGatewayRequest{ + Gateway: &ttnpb.Gateway{ + Ids: gtw1.GetIds(), + Name: "Updated Name", + }, + FieldMask: ttnpb.FieldMask("name"), + }) + if a.So(err, should.NotBeNil) { + a.So(errors.IsPermissionDenied(err), should.BeTrue) + } + + _, err = reg.Delete(ctx, gtw1.GetIds()) + if a.So(err, should.NotBeNil) { + a.So(errors.IsPermissionDenied(err), should.BeTrue) + } }) - if a.So(err, should.NotBeNil) { - a.So(errors.IsPermissionDenied(err), should.BeTrue) - } - _, err = reg.Delete(ctx, gtw1.GetIds()) - if a.So(err, should.NotBeNil) { - a.So(errors.IsPermissionDenied(err), should.BeTrue) - } + t.Run("Admin read-only", func(t *testing.T) { // nolint:paralleltest + _, err := reg.Create(ctx, &ttnpb.CreateGatewayRequest{ + Gateway: &ttnpb.Gateway{ + Ids: &ttnpb.GatewayIdentifiers{GatewayId: "foo-gtw"}, + }, + Collaborator: usr1.GetOrganizationOrUserIdentifiers(), + }, readOnlyAdminKeyCreds) + if a.So(err, should.NotBeNil) { + a.So(errors.IsPermissionDenied(err), should.BeTrue) + } + + _, err = reg.Get(ctx, &ttnpb.GetGatewayRequest{ + GatewayIds: gtw1.GetIds(), + FieldMask: ttnpb.FieldMask("name"), + }, readOnlyAdminKeyCreds) + a.So(errors.IsPermissionDenied(err), should.BeFalse) + + _, err = reg.List(ctx, &ttnpb.ListGatewaysRequest{ + FieldMask: ttnpb.FieldMask("name"), + }, readOnlyAdminKeyCreds) + a.So(errors.IsPermissionDenied(err), should.BeFalse) + + _, err = reg.List(ctx, &ttnpb.ListGatewaysRequest{ + Collaborator: usr1.GetOrganizationOrUserIdentifiers(), + FieldMask: ttnpb.FieldMask("name"), + }, readOnlyAdminKeyCreds) + a.So(errors.IsPermissionDenied(err), should.BeFalse) + + _, err = reg.Update(ctx, &ttnpb.UpdateGatewayRequest{ + Gateway: &ttnpb.Gateway{ + Ids: gtw1.GetIds(), + Name: "Updated Name", + }, + FieldMask: ttnpb.FieldMask("name"), + }, readOnlyAdminKeyCreds) + if a.So(err, should.NotBeNil) { + a.So(errors.IsPermissionDenied(err), should.BeTrue) + } + + _, err = reg.Delete(ctx, gtw1.GetIds(), readOnlyAdminKeyCreds) + if a.So(err, should.NotBeNil) { + a.So(errors.IsPermissionDenied(err), should.BeTrue) + } + }) }, withPrivateTestDatabase(p)) } @@ -396,6 +448,11 @@ func TestGatewayBatchOperations(t *testing.T) { usr2Key, _ := p.NewAPIKey(usr2.GetEntityIdentifiers(), ttnpb.Right_RIGHT_ALL) usr2Creds := rpcCreds(usr2Key) + readOnlyAdmin := p.NewUser() + readOnlyAdmin.Admin = true + readOnlyAdminKey, _ := p.NewAPIKey(readOnlyAdmin.GetEntityIdentifiers(), ttnpb.AllReadAdminRights.GetRights()...) + readOnlyAdminKeyCreds := rpcCreds(readOnlyAdminKey) + testWithIdentityServer(t, func(is *IdentityServer, cc *grpc.ClientConn) { reg := ttnpb.NewGatewayBatchRegistryClient(cc) readReg := ttnpb.NewGatewayRegistryClient(cc) @@ -419,6 +476,12 @@ func TestGatewayBatchOperations(t *testing.T) { }, limitedCreds) a.So(errors.IsPermissionDenied(err), should.BeTrue) + // Read-only admin rights. + _, err = reg.Delete(ctx, &ttnpb.BatchDeleteGatewaysRequest{ + GatewayIds: gtwIDs, + }, readOnlyAdminKeyCreds) + a.So(errors.IsPermissionDenied(err), should.BeTrue) + // User without rights on gateways. _, err = reg.Delete(ctx, &ttnpb.BatchDeleteGatewaysRequest{ GatewayIds: gtwIDs, diff --git a/pkg/identityserver/invitation_registry.go b/pkg/identityserver/invitation_registry.go index 0662344d2a..caf57f8c10 100644 --- a/pkg/identityserver/invitation_registry.go +++ b/pkg/identityserver/invitation_registry.go @@ -68,14 +68,17 @@ func (is *IdentityServer) sendInvitation(ctx context.Context, in *ttnpb.SendInvi return nil, err } events.Publish(evtCreateInvitation.NewWithIdentifiersAndData(ctx, nil, invitation)) - go is.SendTemplateEmailToUsers(is.FromRequestContext(ctx), "invitation", func(ctx context.Context, data email.TemplateData) (email.TemplateData, error) { - return &templates.InvitationData{ - TemplateData: data, - SenderIds: authInfo.GetEntityIdentifiers().GetUserIds(), - InvitationToken: invitation.Token, - TTL: ttl, - }, nil - }, &ttnpb.User{PrimaryEmailAddress: in.Email}) + go is.SendTemplateEmailToUsers( // nolint:errcheck + is.FromRequestContext(ctx), + ttnpb.GetNotificationTypeString(ttnpb.NotificationType_INVITATION), + func(_ context.Context, data email.TemplateData) (email.TemplateData, error) { + return &templates.InvitationData{ + TemplateData: data, + SenderIds: authInfo.GetEntityIdentifiers().GetUserIds(), + InvitationToken: invitation.Token, + TTL: ttl, + }, nil + }, &ttnpb.User{PrimaryEmailAddress: in.Email}) return invitation, nil } diff --git a/pkg/identityserver/notification_registry.go b/pkg/identityserver/notification_registry.go index d67c57f768..93227118c0 100644 --- a/pkg/identityserver/notification_registry.go +++ b/pkg/identityserver/notification_registry.go @@ -16,6 +16,7 @@ package identityserver import ( "context" + "fmt" clusterauth "go.thethings.network/lorawan-stack/v3/pkg/auth/cluster" "go.thethings.network/lorawan-stack/v3/pkg/auth/rights" @@ -23,7 +24,6 @@ import ( "go.thethings.network/lorawan-stack/v3/pkg/errors" "go.thethings.network/lorawan-stack/v3/pkg/events" "go.thethings.network/lorawan-stack/v3/pkg/identityserver/store" - "go.thethings.network/lorawan-stack/v3/pkg/log" "go.thethings.network/lorawan-stack/v3/pkg/ttnpb" "go.thethings.network/lorawan-stack/v3/pkg/unique" "google.golang.org/protobuf/types/known/emptypb" @@ -55,6 +55,23 @@ func receiversContains(receivers []ttnpb.NotificationReceiver, search ttnpb.Noti return false } +func filterAllowedEmailReceivers( + emailReceiverUsers []*ttnpb.User, notificationType string, +) []*ttnpb.UserIdentifiers { + var emailReceiverIDs []*ttnpb.UserIdentifiers + + // Collect IDs of users that have email notifications enabled for that notification type. + for _, user := range emailReceiverUsers { + userNotificationPreferences := user.GetEmailNotificationPreferences().GetTypes() + for _, allowedType := range userNotificationPreferences { + if notificationType == ttnpb.GetNotificationTypeString(allowedType) { + emailReceiverIDs = append(emailReceiverIDs, user.GetIds()) + } + } + } + return emailReceiverIDs +} + func uniqueOrganizationOrUserIdentifiers(ctx context.Context, ids []*ttnpb.OrganizationOrUserIdentifiers) []*ttnpb.OrganizationOrUserIdentifiers { out := make([]*ttnpb.OrganizationOrUserIdentifiers, 0, len(ids)) seen := make(map[string]struct{}, len(ids)) @@ -256,33 +273,47 @@ func (is *IdentityServer) createNotification(ctx context.Context, req *ttnpb.Cre return nil, err } - if req.Email && email.GetNotification(ctx, req.GetNotificationType()) == nil { - log.FromContext(ctx).WithField("notification_type", req.GetNotificationType()).Warn("email template for notification not registered") - req.Email = false - } - receiverUserIDs, err := is.lookupNotificationReceivers(ctx, req) if err != nil { return nil, err } - notification, err := is.storeNotification(ctx, req, receiverUserIDs...) if err != nil { return nil, err } - if req.Email { - if err := is.SendNotificationEmailToUserIDs(ctx, notification, receiverUserIDs...); err != nil { - return nil, err - } - } - evs := make([]events.Event, 0, len(receiverUserIDs)) for _, ids := range receiverUserIDs { evs = append(evs, evtNotificationCreate.NewWithIdentifiersAndData(ctx, ids, notification)) } events.Publish(evs...) + // Filter email receivers + var emailReceiverIDs []*ttnpb.UserIdentifiers + err = is.store.Transact(ctx, func(ctx context.Context, st store.Store) error { + // Get the email notification preferences of the receiver users. + emailReceiverUsers, err := st.FindUsers(ctx, receiverUserIDs, []string{"email_notification_preferences"}) + if err != nil { + return err + } + // Filter only the users that have email notifications enabled for the notification type. + emailReceiverIDs = filterAllowedEmailReceivers(emailReceiverUsers, req.NotificationType) + return nil + }) + if err != nil { + return nil, err + } + + if len(emailReceiverIDs) > 0 && email.GetNotification(ctx, req.GetNotificationType()) == nil { + panic(fmt.Errorf("invalid email template for notification: %s", req.GetNotificationType())) + } + + if len(emailReceiverIDs) > 0 { + if err := is.SendNotificationEmailToUserIDs(ctx, notification, emailReceiverIDs...); err != nil { + return nil, err + } + } + return &ttnpb.CreateNotificationResponse{ Id: notification.Id, }, nil @@ -300,11 +331,9 @@ func (is *IdentityServer) notifyAdminsInternal(ctx context.Context, req *ttnpb.C } } - if req.Email && email.GetNotification(ctx, req.GetNotificationType()) == nil { - log.FromContext(ctx).WithField("notification_type", req.GetNotificationType()).Warn("email template for notification not registered") - req.Email = false + if email.GetNotification(ctx, req.GetNotificationType()) == nil { + panic(fmt.Errorf("invalid email template for notification: %s", req.GetNotificationType())) } - var receivers []*ttnpb.User err := is.store.Transact(ctx, func(ctx context.Context, st store.Store) (err error) { receivers, err = st.ListAdmins(ctx, notificationEmailUserFields) @@ -316,6 +345,16 @@ func (is *IdentityServer) notifyAdminsInternal(ctx context.Context, req *ttnpb.C receiverUserIDs := make([]*ttnpb.UserIdentifiers, len(receivers)) for i, receiver := range receivers { + // Skips over the possible `support` user. + // This user can only be created via the API endpoints defined in the tenant access service. + if receiver.Ids.IDString() == ttnpb.SupportUserID { + continue + } + + // Skips over non approved administrators. + if receiver.State != ttnpb.State_STATE_APPROVED { + continue + } receiverUserIDs[i] = receiver.Ids } @@ -324,10 +363,9 @@ func (is *IdentityServer) notifyAdminsInternal(ctx context.Context, req *ttnpb.C return err } - if req.Email { - if err := is.SendNotificationEmailToUsers(ctx, notification, receivers...); err != nil { - return err - } + err = is.SendNotificationEmailToUsers(ctx, notification, receivers...) + if err != nil { + return err } return nil diff --git a/pkg/identityserver/notification_registry_test.go b/pkg/identityserver/notification_registry_test.go index f0ce0bc75a..4dc519dbae 100644 --- a/pkg/identityserver/notification_registry_test.go +++ b/pkg/identityserver/notification_registry_test.go @@ -56,7 +56,7 @@ func TestNotificationRegistry(t *testing.T) { for _, entityIds := range entityIDs { res, err := svc.Create(ctx, &ttnpb.CreateNotificationRequest{ EntityIds: entityIds, - NotificationType: "test_notification", + NotificationType: ttnpb.GetNotificationTypeString(ttnpb.NotificationType_UNKNOWN), Receivers: []ttnpb.NotificationReceiver{ ttnpb.NotificationReceiver_NOTIFICATION_RECEIVER_COLLABORATOR, ttnpb.NotificationReceiver_NOTIFICATION_RECEIVER_ADMINISTRATIVE_CONTACT, @@ -159,7 +159,7 @@ func TestNotificationRegistryWithOrganizationFanout(t *testing.T) { entityID := entityID res, err := svc.Create(ctx, &ttnpb.CreateNotificationRequest{ EntityIds: entityID, - NotificationType: "test_notification", + NotificationType: ttnpb.GetNotificationTypeString(ttnpb.NotificationType_UNKNOWN), Receivers: []ttnpb.NotificationReceiver{ ttnpb.NotificationReceiver_NOTIFICATION_RECEIVER_ADMINISTRATIVE_CONTACT, ttnpb.NotificationReceiver_NOTIFICATION_RECEIVER_TECHNICAL_CONTACT, diff --git a/pkg/identityserver/organization_access.go b/pkg/identityserver/organization_access.go index d3f76e362b..8b92aa75f7 100644 --- a/pkg/identityserver/organization_access.go +++ b/pkg/identityserver/organization_access.go @@ -108,12 +108,11 @@ func (is *IdentityServer) createOrganizationAPIKey( events.Publish(evtCreateOrganizationAPIKey.NewWithIdentifiersAndData(ctx, req.GetOrganizationIds(), key)) go is.notifyInternal(ctx, &ttnpb.CreateNotificationRequest{ EntityIds: req.GetOrganizationIds().GetEntityIdentifiers(), - NotificationType: "api_key_created", + NotificationType: ttnpb.GetNotificationTypeString(ttnpb.NotificationType_API_KEY_CREATED), Data: ttnpb.MustMarshalAny(key), Receivers: []ttnpb.NotificationReceiver{ ttnpb.NotificationReceiver_NOTIFICATION_RECEIVER_ADMINISTRATIVE_CONTACT, }, - Email: true, }) key.Key = token @@ -230,12 +229,11 @@ func (is *IdentityServer) updateOrganizationAPIKey( events.Publish(evtUpdateOrganizationAPIKey.NewWithIdentifiersAndData(ctx, req.GetOrganizationIds(), key)) go is.notifyInternal(ctx, &ttnpb.CreateNotificationRequest{ EntityIds: req.GetOrganizationIds().GetEntityIdentifiers(), - NotificationType: "api_key_changed", + NotificationType: ttnpb.GetNotificationTypeString(ttnpb.NotificationType_API_KEY_CHANGED), Data: ttnpb.MustMarshalAny(key), Receivers: []ttnpb.NotificationReceiver{ ttnpb.NotificationReceiver_NOTIFICATION_RECEIVER_ADMINISTRATIVE_CONTACT, }, - Email: true, }) return key, nil @@ -374,12 +372,11 @@ func (is *IdentityServer) setOrganizationCollaborator( //nolint:gocyclo )) go is.notifyInternal(ctx, &ttnpb.CreateNotificationRequest{ EntityIds: req.GetOrganizationIds().GetEntityIdentifiers(), - NotificationType: "collaborator_changed", + NotificationType: ttnpb.GetNotificationTypeString(ttnpb.NotificationType_COLLABORATOR_CHANGED), Data: ttnpb.MustMarshalAny(req.GetCollaborator()), Receivers: []ttnpb.NotificationReceiver{ ttnpb.NotificationReceiver_NOTIFICATION_RECEIVER_ADMINISTRATIVE_CONTACT, }, - Email: false, }) } else { events.Publish(evtDeleteOrganizationCollaborator.New( diff --git a/pkg/identityserver/organization_access_test.go b/pkg/identityserver/organization_access_test.go index b11c511d0d..6d36868fa0 100644 --- a/pkg/identityserver/organization_access_test.go +++ b/pkg/identityserver/organization_access_test.go @@ -26,6 +26,80 @@ import ( "google.golang.org/grpc" ) +func TestOrganizationAPIKeysPermissions(t *testing.T) { // nolint:gocyclo + p := &storetest.Population{} + + readOnlyAdmin := p.NewUser() + readOnlyAdmin.Admin = true + readOnlyAdminKey, _ := p.NewAPIKey(readOnlyAdmin.GetEntityIdentifiers(), ttnpb.AllReadAdminRights.GetRights()...) + readOnlyAdminKeyCreds := rpcCreds(readOnlyAdminKey) + + usr1 := p.NewUser() + org1 := p.NewOrganization(usr1.GetOrganizationOrUserIdentifiers()) + orgKey, _ := p.NewAPIKey(org1.GetEntityIdentifiers(), + ttnpb.Right_RIGHT_ORGANIZATION_INFO, + ttnpb.Right_RIGHT_ORGANIZATION_APPLICATIONS_LIST, + ) + orgCreds := rpcCreds(orgKey) + + t.Parallel() + a, ctx := test.New(t) + + testWithIdentityServer(t, func(is *IdentityServer, cc *grpc.ClientConn) { + is.config.AdminRights.All = true + + reg := ttnpb.NewOrganizationAccessClient(cc) + + t.Run("Invalid credentials", func(t *testing.T) { // nolint:paralleltest + for _, opts := range [][]grpc.CallOption{nil, {orgCreds}, {readOnlyAdminKeyCreds}} { + created, err := reg.CreateAPIKey(ctx, &ttnpb.CreateOrganizationAPIKeyRequest{ + OrganizationIds: org1.GetIds(), + Name: "api-key-name", + Rights: []ttnpb.Right{ttnpb.Right_RIGHT_ORGANIZATION_INFO}, + }, opts...) + if a.So(err, should.NotBeNil) && a.So(errors.IsPermissionDenied(err), should.BeTrue) { + a.So(created, should.BeNil) + } + + list, err := reg.ListAPIKeys(ctx, &ttnpb.ListOrganizationAPIKeysRequest{ + OrganizationIds: org1.GetIds(), + }, opts...) + if a.So(err, should.NotBeNil) && a.So(errors.IsPermissionDenied(err), should.BeTrue) { + a.So(list, should.BeNil) + } + + got, err := reg.GetAPIKey(ctx, &ttnpb.GetOrganizationAPIKeyRequest{ + OrganizationIds: org1.GetIds(), + KeyId: orgKey.GetId(), + }, opts...) + if a.So(err, should.NotBeNil) && a.So(errors.IsPermissionDenied(err), should.BeTrue) { + a.So(got, should.BeNil) + } + + updated, err := reg.UpdateAPIKey(ctx, &ttnpb.UpdateOrganizationAPIKeyRequest{ + OrganizationIds: org1.GetIds(), + ApiKey: &ttnpb.APIKey{ + Id: orgKey.GetId(), + Name: "api-key-name-updated", + }, + FieldMask: ttnpb.FieldMask("name"), + }, opts...) + if a.So(err, should.NotBeNil) && a.So(errors.IsPermissionDenied(err), should.BeTrue) { + a.So(updated, should.BeNil) + } + + _, err = reg.DeleteAPIKey(ctx, &ttnpb.DeleteOrganizationAPIKeyRequest{ + OrganizationIds: org1.GetIds(), + KeyId: created.GetId(), + }, opts...) + if !a.So(errors.IsPermissionDenied(err), should.BeTrue) { + t.FailNow() + } + } + }) + }, withPrivateTestDatabase(p)) +} + func TestOrganizationAPIKeys(t *testing.T) { // nolint:gocyclo p := &storetest.Population{} @@ -50,7 +124,6 @@ func TestOrganizationAPIKeys(t *testing.T) { // nolint:gocyclo ttnpb.Right_RIGHT_ORGANIZATION_INFO, ttnpb.Right_RIGHT_ORGANIZATION_APPLICATIONS_LIST, ) - orgCreds := rpcCreds(orgKey) t.Parallel() a, ctx := test.New(t) @@ -147,53 +220,6 @@ func TestOrganizationAPIKeys(t *testing.T) { // nolint:gocyclo }) } - // API Key CRUD with different invalid credentials. - for _, opts := range [][]grpc.CallOption{nil, {orgCreds}} { - created, err := reg.CreateAPIKey(ctx, &ttnpb.CreateOrganizationAPIKeyRequest{ - OrganizationIds: org1.GetIds(), - Name: "api-key-name", - Rights: []ttnpb.Right{ttnpb.Right_RIGHT_ORGANIZATION_INFO}, - }, opts...) - if a.So(err, should.NotBeNil) && a.So(errors.IsPermissionDenied(err), should.BeTrue) { - a.So(created, should.BeNil) - } - - list, err := reg.ListAPIKeys(ctx, &ttnpb.ListOrganizationAPIKeysRequest{ - OrganizationIds: org1.GetIds(), - }, opts...) - if a.So(err, should.NotBeNil) && a.So(errors.IsPermissionDenied(err), should.BeTrue) { - a.So(list, should.BeNil) - } - - got, err := reg.GetAPIKey(ctx, &ttnpb.GetOrganizationAPIKeyRequest{ - OrganizationIds: org1.GetIds(), - KeyId: orgKey.GetId(), - }, opts...) - if a.So(err, should.NotBeNil) && a.So(errors.IsPermissionDenied(err), should.BeTrue) { - a.So(got, should.BeNil) - } - - updated, err := reg.UpdateAPIKey(ctx, &ttnpb.UpdateOrganizationAPIKeyRequest{ - OrganizationIds: org1.GetIds(), - ApiKey: &ttnpb.APIKey{ - Id: orgKey.GetId(), - Name: "api-key-name-updated", - }, - FieldMask: ttnpb.FieldMask("name"), - }, opts...) - if a.So(err, should.NotBeNil) && a.So(errors.IsPermissionDenied(err), should.BeTrue) { - a.So(updated, should.BeNil) - } - - _, err = reg.DeleteAPIKey(ctx, &ttnpb.DeleteOrganizationAPIKeyRequest{ - OrganizationIds: org1.GetIds(), - KeyId: created.GetId(), - }, opts...) - if !a.So(errors.IsPermissionDenied(err), should.BeTrue) { - t.FailNow() - } - } - // API Key CRUD with different valid credentials. for _, opts := range [][]grpc.CallOption{{adminCreds}, {usr1Creds}, {limitedCreds}} { created, err := reg.CreateAPIKey(ctx, &ttnpb.CreateOrganizationAPIKeyRequest{ @@ -287,6 +313,92 @@ func TestOrganizationAPIKeys(t *testing.T) { // nolint:gocyclo }, withPrivateTestDatabase(p)) } +func TestOrganizationCollaboratorsPermissions(t *testing.T) { // nolint:gocyclo + p := &storetest.Population{} + + admin := p.NewUser() + admin.Admin = true + + readOnlyAdmin := p.NewUser() + readOnlyAdmin.Admin = true + readOnlyAdminKey, _ := p.NewAPIKey(readOnlyAdmin.GetEntityIdentifiers(), ttnpb.AllReadAdminRights.GetRights()...) + readOnlyAdminKeyCreds := rpcCreds(readOnlyAdminKey) + + usr1 := p.NewUser() + org1 := p.NewOrganization(usr1.GetOrganizationOrUserIdentifiers()) + + orgKey, _ := p.NewAPIKey(org1.GetEntityIdentifiers(), + ttnpb.Right_RIGHT_ORGANIZATION_INFO, + ttnpb.Right_RIGHT_ORGANIZATION_APPLICATIONS_LIST, + ) + orgCreds := rpcCreds(orgKey) + + usr2 := p.NewUser() + p.NewMembership( + usr2.GetOrganizationOrUserIdentifiers(), + org1.GetEntityIdentifiers(), + ttnpb.Right_RIGHT_ORGANIZATION_INFO, + ttnpb.Right_RIGHT_ORGANIZATION_APPLICATIONS_LIST, + ) + + t.Parallel() + a, ctx := test.New(t) + + testWithIdentityServer(t, func(is *IdentityServer, cc *grpc.ClientConn) { + is.config.AdminRights.All = true + + reg := ttnpb.NewOrganizationAccessClient(cc) + + t.Run("Invalid credentials", func(t *testing.T) { // nolint:paralleltest + for _, opts := range [][]grpc.CallOption{nil, {orgCreds}, {readOnlyAdminKeyCreds}} { + _, err := reg.SetCollaborator(ctx, &ttnpb.SetOrganizationCollaboratorRequest{ + OrganizationIds: org1.GetIds(), + Collaborator: &ttnpb.Collaborator{ + Ids: usr2.GetOrganizationOrUserIdentifiers(), + Rights: []ttnpb.Right{ + ttnpb.Right_RIGHT_ORGANIZATION_INFO, + }, + }, + }, opts...) + if a.So(err, should.NotBeNil) { + a.So(errors.IsPermissionDenied(err), should.BeTrue) + } + + got, err := reg.GetCollaborator(ctx, &ttnpb.GetOrganizationCollaboratorRequest{ + OrganizationIds: org1.GetIds(), + Collaborator: usr2.GetOrganizationOrUserIdentifiers(), + }, opts...) + if a.So(err, should.NotBeNil) && a.So(errors.IsPermissionDenied(err), should.BeTrue) { + a.So(got, should.BeNil) + } + + _, err = reg.DeleteCollaborator(ctx, &ttnpb.DeleteOrganizationCollaboratorRequest{ + OrganizationIds: org1.GetIds(), + CollaboratorIds: usr2.GetOrganizationOrUserIdentifiers(), + }, opts...) + if a.So(err, should.NotBeNil) { + a.So(errors.IsPermissionDenied(err), should.BeTrue) + } + } + + // ListCollaborators without credentials. + list, err := reg.ListCollaborators(ctx, &ttnpb.ListOrganizationCollaboratorsRequest{ + OrganizationIds: org1.GetIds(), + }) + if a.So(err, should.NotBeNil) && a.So(errors.IsUnauthenticated(err), should.BeTrue) { + a.So(list, should.BeNil) + } + + // ListCollaborators with read-only credentials. Returns only safe fields. + list, err = reg.ListCollaborators(ctx, &ttnpb.ListOrganizationCollaboratorsRequest{ + OrganizationIds: org1.GetIds(), + }, readOnlyAdminKeyCreds) + a.So(err, should.BeNil) + a.So(list, should.NotBeNil) + }) + }, withPrivateTestDatabase(p)) +} + func TestOrganizationCollaborators(t *testing.T) { // nolint:gocyclo p := &storetest.Population{} @@ -308,12 +420,6 @@ func TestOrganizationCollaborators(t *testing.T) { // nolint:gocyclo ) limitedCreds := rpcCreds(limitedKey) - orgKey, _ := p.NewAPIKey(org1.GetEntityIdentifiers(), - ttnpb.Right_RIGHT_ORGANIZATION_INFO, - ttnpb.Right_RIGHT_ORGANIZATION_APPLICATIONS_LIST, - ) - orgCreds := rpcCreds(orgKey) - usr2 := p.NewUser() p.NewMembership( usr2.GetOrganizationOrUserIdentifiers(), @@ -383,30 +489,6 @@ func TestOrganizationCollaborators(t *testing.T) { // nolint:gocyclo }, limitedCreds) a.So(err, should.BeNil) - // Collaborator CRUD with different invalid credentials. - for _, opts := range [][]grpc.CallOption{nil, {orgCreds}} { - _, err := reg.SetCollaborator(ctx, &ttnpb.SetOrganizationCollaboratorRequest{ - OrganizationIds: org1.GetIds(), - Collaborator: &ttnpb.Collaborator{ - Ids: usr2.GetOrganizationOrUserIdentifiers(), - Rights: []ttnpb.Right{ - ttnpb.Right_RIGHT_ORGANIZATION_INFO, - }, - }, - }, opts...) - if a.So(err, should.NotBeNil) { - a.So(errors.IsPermissionDenied(err), should.BeTrue) - } - - got, err := reg.GetCollaborator(ctx, &ttnpb.GetOrganizationCollaboratorRequest{ - OrganizationIds: org1.GetIds(), - Collaborator: usr2.GetOrganizationOrUserIdentifiers(), - }, opts...) - if a.So(err, should.NotBeNil) && a.So(errors.IsPermissionDenied(err), should.BeTrue) { - a.So(got, should.BeNil) - } - } - // ListCollaborators without credentials. list, err := reg.ListCollaborators(ctx, &ttnpb.ListOrganizationCollaboratorsRequest{ OrganizationIds: org1.GetIds(), diff --git a/pkg/identityserver/organization_registry.go b/pkg/identityserver/organization_registry.go index 6387e0a08a..47b6fb6cca 100644 --- a/pkg/identityserver/organization_registry.go +++ b/pkg/identityserver/organization_registry.go @@ -378,6 +378,11 @@ func (is *IdentityServer) purgeOrganization( if !is.IsAdmin(ctx) { return nil, errAdminsPurgeOrganizations.New() } + if err := rights.RequireOrganization( + store.WithSoftDeleted(ctx, false), ids, ttnpb.Right_RIGHT_ORGANIZATION_DELETE, + ); err != nil { + return nil, err + } err := is.store.Transact(ctx, func(ctx context.Context, st store.Store) error { // Delete related API keys before purging the organization. err := st.DeleteEntityAPIKeys(ctx, ids.GetEntityIdentifiers()) diff --git a/pkg/identityserver/organization_registry_test.go b/pkg/identityserver/organization_registry_test.go index 6f5e3e1fa4..39f0ab3132 100644 --- a/pkg/identityserver/organization_registry_test.go +++ b/pkg/identityserver/organization_registry_test.go @@ -62,67 +62,122 @@ func TestOrganizationsNestedError(t *testing.T) { }, withPrivateTestDatabase(p)) } -func TestOrganizationsPermissionDenied(t *testing.T) { +func TestOrganizationsPermissions(t *testing.T) { p := &storetest.Population{} usr1 := p.NewUser() org1 := p.NewOrganization(usr1.GetOrganizationOrUserIdentifiers()) + readOnlyAdmin := p.NewUser() + readOnlyAdmin.Admin = true + readOnlyAdminKey, _ := p.NewAPIKey(readOnlyAdmin.GetEntityIdentifiers(), ttnpb.AllReadAdminRights.GetRights()...) + readOnlyAdminKeyCreds := rpcCreds(readOnlyAdminKey) + t.Parallel() a, ctx := test.New(t) testWithIdentityServer(t, func(_ *IdentityServer, cc *grpc.ClientConn) { reg := ttnpb.NewOrganizationRegistryClient(cc) - _, err := reg.Create(ctx, &ttnpb.CreateOrganizationRequest{ - Organization: &ttnpb.Organization{ - Ids: &ttnpb.OrganizationIdentifiers{OrganizationId: "foo-org"}, - }, - Collaborator: usr1.GetOrganizationOrUserIdentifiers(), - }) - if a.So(err, should.NotBeNil) { - a.So(errors.IsPermissionDenied(err), should.BeTrue) - } + t.Run("Invalid credentials", func(t *testing.T) { // nolint:paralleltest + _, err := reg.Create(ctx, &ttnpb.CreateOrganizationRequest{ + Organization: &ttnpb.Organization{ + Ids: &ttnpb.OrganizationIdentifiers{OrganizationId: "foo-org"}, + }, + Collaborator: usr1.GetOrganizationOrUserIdentifiers(), + }) + if a.So(err, should.NotBeNil) { + a.So(errors.IsPermissionDenied(err), should.BeTrue) + } - _, err = reg.Get(ctx, &ttnpb.GetOrganizationRequest{ - OrganizationIds: org1.GetIds(), - FieldMask: ttnpb.FieldMask("name"), - }) - if a.So(err, should.NotBeNil) { - a.So(errors.IsUnauthenticated(err), should.BeTrue) - } + _, err = reg.Get(ctx, &ttnpb.GetOrganizationRequest{ + OrganizationIds: org1.GetIds(), + FieldMask: ttnpb.FieldMask("name"), + }) + if a.So(err, should.NotBeNil) { + a.So(errors.IsUnauthenticated(err), should.BeTrue) + } - listRes, err := reg.List(ctx, &ttnpb.ListOrganizationsRequest{ - FieldMask: ttnpb.FieldMask("name"), - }) - a.So(err, should.BeNil) - if a.So(listRes, should.NotBeNil) { - a.So(listRes.Organizations, should.BeEmpty) - } + listRes, err := reg.List(ctx, &ttnpb.ListOrganizationsRequest{ + FieldMask: ttnpb.FieldMask("name"), + }) + a.So(err, should.BeNil) + if a.So(listRes, should.NotBeNil) { + a.So(listRes.Organizations, should.BeEmpty) + } - _, err = reg.List(ctx, &ttnpb.ListOrganizationsRequest{ - Collaborator: usr1.GetOrganizationOrUserIdentifiers(), - FieldMask: ttnpb.FieldMask("name"), - }) - if a.So(err, should.NotBeNil) { - a.So(errors.IsPermissionDenied(err), should.BeTrue) - } + _, err = reg.List(ctx, &ttnpb.ListOrganizationsRequest{ + Collaborator: usr1.GetOrganizationOrUserIdentifiers(), + FieldMask: ttnpb.FieldMask("name"), + }) + if a.So(err, should.NotBeNil) { + a.So(errors.IsPermissionDenied(err), should.BeTrue) + } - _, err = reg.Update(ctx, &ttnpb.UpdateOrganizationRequest{ - Organization: &ttnpb.Organization{ - Ids: org1.GetIds(), - Name: "Updated Name", - }, - FieldMask: ttnpb.FieldMask("name"), + _, err = reg.Update(ctx, &ttnpb.UpdateOrganizationRequest{ + Organization: &ttnpb.Organization{ + Ids: org1.GetIds(), + Name: "Updated Name", + }, + FieldMask: ttnpb.FieldMask("name"), + }) + if a.So(err, should.NotBeNil) { + a.So(errors.IsPermissionDenied(err), should.BeTrue) + } + + _, err = reg.Delete(ctx, org1.GetIds()) + if a.So(err, should.NotBeNil) { + a.So(errors.IsPermissionDenied(err), should.BeTrue) + } }) - if a.So(err, should.NotBeNil) { - a.So(errors.IsPermissionDenied(err), should.BeTrue) - } - _, err = reg.Delete(ctx, org1.GetIds()) - if a.So(err, should.NotBeNil) { - a.So(errors.IsPermissionDenied(err), should.BeTrue) - } - }) + t.Run("Admin read-only", func(t *testing.T) { // nolint:paralleltest + _, err := reg.Create(ctx, &ttnpb.CreateOrganizationRequest{ + Organization: &ttnpb.Organization{ + Ids: &ttnpb.OrganizationIdentifiers{OrganizationId: "foo-org"}, + }, + Collaborator: usr1.GetOrganizationOrUserIdentifiers(), + }, readOnlyAdminKeyCreds) + if a.So(err, should.NotBeNil) { + a.So(errors.IsPermissionDenied(err), should.BeTrue) + } + + _, err = reg.Get(ctx, &ttnpb.GetOrganizationRequest{ + OrganizationIds: org1.GetIds(), + FieldMask: ttnpb.FieldMask("name"), + }, readOnlyAdminKeyCreds) + a.So(errors.IsPermissionDenied(err), should.BeFalse) + + listRes, err := reg.List(ctx, &ttnpb.ListOrganizationsRequest{ + FieldMask: ttnpb.FieldMask("name"), + }, readOnlyAdminKeyCreds) + a.So(err, should.BeNil) + if a.So(listRes, should.NotBeNil) { + a.So(listRes.Organizations, should.BeEmpty) + } + + _, err = reg.List(ctx, &ttnpb.ListOrganizationsRequest{ + Collaborator: usr1.GetOrganizationOrUserIdentifiers(), + FieldMask: ttnpb.FieldMask("name"), + }, readOnlyAdminKeyCreds) + a.So(errors.IsPermissionDenied(err), should.BeFalse) + + _, err = reg.Update(ctx, &ttnpb.UpdateOrganizationRequest{ + Organization: &ttnpb.Organization{ + Ids: org1.GetIds(), + Name: "Updated Name", + }, + FieldMask: ttnpb.FieldMask("name"), + }, readOnlyAdminKeyCreds) + if a.So(err, should.NotBeNil) { + a.So(errors.IsPermissionDenied(err), should.BeTrue) + } + + _, err = reg.Delete(ctx, org1.GetIds(), readOnlyAdminKeyCreds) + if a.So(err, should.NotBeNil) { + a.So(errors.IsPermissionDenied(err), should.BeTrue) + } + }) + }, withPrivateTestDatabase(p)) } func TestOrganizationsCRUD(t *testing.T) { diff --git a/pkg/identityserver/rights.go b/pkg/identityserver/rights.go index b021745dfd..7e0fe62bf1 100644 --- a/pkg/identityserver/rights.go +++ b/pkg/identityserver/rights.go @@ -38,7 +38,9 @@ func allPotentialRights(eIDs *ttnpb.EntityIdentifiers, rights *ttnpb.Rights) *tt return nil } -func (is *IdentityServer) getRights(ctx context.Context, entityID *ttnpb.EntityIdentifiers) (entityRights, universalRights *ttnpb.Rights, err error) { +func (is *IdentityServer) getRights( + ctx context.Context, entityID *ttnpb.EntityIdentifiers, +) (entityRights, universalRights *ttnpb.Rights, err error) { authInfo, err := is.authInfo(ctx) if err != nil { return nil, nil, err diff --git a/pkg/identityserver/store/migrations/20241001000000_email_notification_preferences.tx.down.sql b/pkg/identityserver/store/migrations/20241001000000_email_notification_preferences.tx.down.sql new file mode 100644 index 0000000000..b03f5f9d04 --- /dev/null +++ b/pkg/identityserver/store/migrations/20241001000000_email_notification_preferences.tx.down.sql @@ -0,0 +1,15 @@ +ALTER TABLE users DROP COLUMN IF EXISTS email_notification_preferences CASCADE; + +--bun:split +CREATE OR REPLACE VIEW user_accounts AS +SELECT + acc.id AS account_id, + acc.created_at AS account_created_at, + acc.updated_at AS account_updated_at, + acc.deleted_at AS account_deleted_at, + acc.uid AS account_uid, + usr.* +FROM + accounts acc + JOIN users usr ON usr.id = acc.account_id + AND acc.account_type = 'user'; diff --git a/pkg/identityserver/store/migrations/20241001000000_email_notification_preferences.tx.up.sql b/pkg/identityserver/store/migrations/20241001000000_email_notification_preferences.tx.up.sql new file mode 100644 index 0000000000..5fb2c66b79 --- /dev/null +++ b/pkg/identityserver/store/migrations/20241001000000_email_notification_preferences.tx.up.sql @@ -0,0 +1,16 @@ +ALTER TABLE users +ADD COLUMN IF NOT EXISTS email_notification_preferences INTEGER []; + +--bun:split +CREATE OR REPLACE VIEW user_accounts AS +SELECT + acc.id AS account_id, + acc.created_at AS account_created_at, + acc.updated_at AS account_updated_at, + acc.deleted_at AS account_deleted_at, + acc.uid AS account_uid, + usr.* +FROM + accounts acc + JOIN users usr ON usr.id = acc.account_id + AND acc.account_type = 'user'; diff --git a/pkg/identityserver/storetest/entity_search.go b/pkg/identityserver/storetest/entity_search.go index e44501a30e..2e2394e888 100644 --- a/pkg/identityserver/storetest/entity_search.go +++ b/pkg/identityserver/storetest/entity_search.go @@ -630,30 +630,34 @@ func (st *StoreTest) TestEntitySearch(t *T) { } func (st *StoreTest) TestEntitySearchPagination(t *T) { + store.SetPaginationDefaults(store.PaginationDefaults{ + DefaultLimit: 7, + }) + var users []*ttnpb.User - for i := 0; i < 7; i++ { + for i := 0; i < 9; i++ { users = append(users, st.population.NewUser()) } var applications []*ttnpb.Application - for i := 0; i < 7; i++ { + for i := 0; i < 9; i++ { applications = append(applications, st.population.NewApplication(users[0].GetOrganizationOrUserIdentifiers())) } var clients []*ttnpb.Client - for i := 0; i < 7; i++ { + for i := 0; i < 9; i++ { clients = append(clients, st.population.NewClient(users[0].GetOrganizationOrUserIdentifiers())) } var gateways []*ttnpb.Gateway - for i := 0; i < 7; i++ { + for i := 0; i < 9; i++ { gateways = append(gateways, st.population.NewGateway(users[0].GetOrganizationOrUserIdentifiers())) } var organizations []*ttnpb.Organization - for i := 0; i < 7; i++ { + for i := 0; i < 9; i++ { organizations = append(organizations, st.population.NewOrganization(users[0].GetOrganizationOrUserIdentifiers())) } var endDevices []*ttnpb.EndDevice - for i := 0; i < 7; i++ { + for i := 0; i < 9; i++ { endDevices = append(endDevices, st.population.NewEndDevice(applications[0].GetIds())) } @@ -676,17 +680,30 @@ func (st *StoreTest) TestEntitySearchPagination(t *T) { got, err := s.SearchApplications(paginateCtx, nil, &ttnpb.SearchApplicationsRequest{}) if a.So(err, should.BeNil) && a.So(got, should.NotBeNil) { - if page == 4 { - a.So(got, should.HaveLength, 1) - } else { - a.So(got, should.HaveLength, 2) - } + a.So(got, should.HaveLength, 2) for i, e := range got { a.So(e, should.Resemble, applications[i+2*int(page-1)].Ids) } } - a.So(total, should.Equal, 7) + a.So(total, should.Equal, 9) + } + }) + + t.Run("Applications_PageLimit", func(t *T) { + a, ctx := test.New(t) + + var total uint64 + paginateCtx := store.WithPagination(ctx, 0, 0, &total) + got, err := s.SearchApplications(paginateCtx, nil, &ttnpb.SearchApplicationsRequest{}) + if a.So(err, should.BeNil) && a.So(got, should.NotBeNil) { + a.So(got, should.HaveLength, 7) + } + + paginateCtx = store.WithPagination(ctx, 0, 2, &total) + got, err = s.SearchApplications(paginateCtx, nil, &ttnpb.SearchApplicationsRequest{}) + if a.So(err, should.BeNil) && a.So(got, should.NotBeNil) { + a.So(got, should.HaveLength, 2) } }) @@ -699,17 +716,30 @@ func (st *StoreTest) TestEntitySearchPagination(t *T) { got, err := s.SearchClients(paginateCtx, nil, &ttnpb.SearchClientsRequest{}) if a.So(err, should.BeNil) && a.So(got, should.NotBeNil) { - if page == 4 { - a.So(got, should.HaveLength, 1) - } else { - a.So(got, should.HaveLength, 2) - } + a.So(got, should.HaveLength, 2) for i, e := range got { a.So(e, should.Resemble, clients[i+2*int(page-1)].Ids) } } - a.So(total, should.Equal, 7) + a.So(total, should.Equal, 9) + } + }) + + t.Run("Clients_PageLimit", func(t *T) { + a, ctx := test.New(t) + + var total uint64 + paginateCtx := store.WithPagination(ctx, 0, 0, &total) + got, err := s.SearchClients(paginateCtx, nil, &ttnpb.SearchClientsRequest{}) + if a.So(err, should.BeNil) && a.So(got, should.NotBeNil) { + a.So(got, should.HaveLength, 7) + } + + paginateCtx = store.WithPagination(ctx, 0, 2, &total) + got, err = s.SearchClients(paginateCtx, nil, &ttnpb.SearchClientsRequest{}) + if a.So(err, should.BeNil) && a.So(got, should.NotBeNil) { + a.So(got, should.HaveLength, 2) } }) @@ -724,17 +754,34 @@ func (st *StoreTest) TestEntitySearchPagination(t *T) { ApplicationIds: applications[0].GetIds(), }) if a.So(err, should.BeNil) && a.So(got, should.NotBeNil) { - if page == 4 { - a.So(got, should.HaveLength, 1) - } else { - a.So(got, should.HaveLength, 2) - } + a.So(got, should.HaveLength, 2) for i, e := range got { a.So(e, should.Resemble, endDevices[i+2*int(page-1)].Ids) } } - a.So(total, should.Equal, 7) + a.So(total, should.Equal, 9) + } + }) + + t.Run("EndDevices_PageLimit", func(t *T) { + a, ctx := test.New(t) + + var total uint64 + paginateCtx := store.WithPagination(ctx, 0, 0, &total) + got, err := s.SearchEndDevices(paginateCtx, &ttnpb.SearchEndDevicesRequest{ + ApplicationIds: applications[0].GetIds(), + }) + if a.So(err, should.BeNil) && a.So(got, should.NotBeNil) { + a.So(got, should.HaveLength, 7) + } + + paginateCtx = store.WithPagination(ctx, 0, 2, &total) + got, err = s.SearchEndDevices(paginateCtx, &ttnpb.SearchEndDevicesRequest{ + ApplicationIds: applications[0].GetIds(), + }) + if a.So(err, should.BeNil) && a.So(got, should.NotBeNil) { + a.So(got, should.HaveLength, 2) } }) @@ -747,17 +794,30 @@ func (st *StoreTest) TestEntitySearchPagination(t *T) { got, err := s.SearchGateways(paginateCtx, nil, &ttnpb.SearchGatewaysRequest{}) if a.So(err, should.BeNil) && a.So(got, should.NotBeNil) { - if page == 4 { - a.So(got, should.HaveLength, 1) - } else { - a.So(got, should.HaveLength, 2) - } + a.So(got, should.HaveLength, 2) for i, e := range got { a.So(e, should.Resemble, gateways[i+2*int(page-1)].Ids) } } - a.So(total, should.Equal, 7) + a.So(total, should.Equal, 9) + } + }) + + t.Run("Gateways_PageLimit", func(t *T) { + a, ctx := test.New(t) + + var total uint64 + paginateCtx := store.WithPagination(ctx, 0, 0, &total) + got, err := s.SearchGateways(paginateCtx, nil, &ttnpb.SearchGatewaysRequest{}) + if a.So(err, should.BeNil) && a.So(got, should.NotBeNil) { + a.So(got, should.HaveLength, 7) + } + + paginateCtx = store.WithPagination(ctx, 0, 2, &total) + got, err = s.SearchGateways(paginateCtx, nil, &ttnpb.SearchGatewaysRequest{}) + if a.So(err, should.BeNil) && a.So(got, should.NotBeNil) { + a.So(got, should.HaveLength, 2) } }) @@ -770,17 +830,30 @@ func (st *StoreTest) TestEntitySearchPagination(t *T) { got, err := s.SearchOrganizations(paginateCtx, nil, &ttnpb.SearchOrganizationsRequest{}) if a.So(err, should.BeNil) && a.So(got, should.NotBeNil) { - if page == 4 { - a.So(got, should.HaveLength, 1) - } else { - a.So(got, should.HaveLength, 2) - } + a.So(got, should.HaveLength, 2) for i, e := range got { a.So(e, should.Resemble, organizations[i+2*int(page-1)].Ids) } } - a.So(total, should.Equal, 7) + a.So(total, should.Equal, 9) + } + }) + + t.Run("Organizations_PageLimit", func(t *T) { + a, ctx := test.New(t) + + var total uint64 + paginateCtx := store.WithPagination(ctx, 0, 0, &total) + got, err := s.SearchOrganizations(paginateCtx, nil, &ttnpb.SearchOrganizationsRequest{}) + if a.So(err, should.BeNil) && a.So(got, should.NotBeNil) { + a.So(got, should.HaveLength, 7) + } + + paginateCtx = store.WithPagination(ctx, 0, 2, &total) + got, err = s.SearchOrganizations(paginateCtx, nil, &ttnpb.SearchOrganizationsRequest{}) + if a.So(err, should.BeNil) && a.So(got, should.NotBeNil) { + a.So(got, should.HaveLength, 2) } }) @@ -793,17 +866,30 @@ func (st *StoreTest) TestEntitySearchPagination(t *T) { got, err := s.SearchUsers(paginateCtx, &ttnpb.SearchUsersRequest{}) if a.So(err, should.BeNil) && a.So(got, should.NotBeNil) { - if page == 4 { - a.So(got, should.HaveLength, 1) - } else { - a.So(got, should.HaveLength, 2) - } + a.So(got, should.HaveLength, 2) for i, e := range got { a.So(e, should.Resemble, users[i+2*int(page-1)].Ids) } } - a.So(total, should.Equal, 7) + a.So(total, should.Equal, 9) + } + }) + + t.Run("Users_PageLimit", func(t *T) { + a, ctx := test.New(t) + + var total uint64 + paginateCtx := store.WithPagination(ctx, 0, 0, &total) + got, err := s.SearchUsers(paginateCtx, &ttnpb.SearchUsersRequest{}) + if a.So(err, should.BeNil) && a.So(got, should.NotBeNil) { + a.So(got, should.HaveLength, 7) + } + + paginateCtx = store.WithPagination(ctx, 0, 2, &total) + got, err = s.SearchUsers(paginateCtx, &ttnpb.SearchUsersRequest{}) + if a.So(err, should.BeNil) && a.So(got, should.NotBeNil) { + a.So(got, should.HaveLength, 2) } }) } diff --git a/pkg/identityserver/storetest/notification_store.go b/pkg/identityserver/storetest/notification_store.go index 787caff67d..197080d508 100644 --- a/pkg/identityserver/storetest/notification_store.go +++ b/pkg/identityserver/storetest/notification_store.go @@ -76,21 +76,19 @@ func (st *StoreTest) TestNotificationStore(t *T) { } { created, err := s.CreateNotification(ctx, &ttnpb.Notification{ EntityIds: ids.GetEntityIdentifiers(), - NotificationType: "test_notification", + NotificationType: ttnpb.GetNotificationTypeString(ttnpb.NotificationType_UNKNOWN), Data: notificationData, SenderIds: usr1.GetIds(), Receivers: []ttnpb.NotificationReceiver{ttnpb.NotificationReceiver_NOTIFICATION_RECEIVER_COLLABORATOR}, - Email: true, }, []*ttnpb.UserIdentifiers{usr1.GetIds(), usr2.GetIds()}) if a.So(err, should.BeNil) && a.So(created, should.NotBeNil) { a.So(created.Id, should.NotBeBlank) a.So(created.EntityIds, should.Resemble, ids.GetEntityIdentifiers()) - a.So(created.NotificationType, should.Equal, "test_notification") + a.So(created.NotificationType, should.Equal, "unknown") a.So(created.Data, should.Resemble, notificationData) a.So(created.SenderIds, should.Resemble, usr1.GetIds()) a.So(created.Receivers, should.Resemble, []ttnpb.NotificationReceiver{ttnpb.NotificationReceiver_NOTIFICATION_RECEIVER_COLLABORATOR}) - a.So(created.Email, should.BeTrue) a.So(created.Status, should.Resemble, ttnpb.NotificationStatus_NOTIFICATION_STATUS_UNSEEN) a.So(*ttnpb.StdTime(created.CreatedAt), should.HappenWithin, 5*time.Second, start) a.So(*ttnpb.StdTime(created.StatusUpdatedAt), should.HappenWithin, 5*time.Second, start) diff --git a/pkg/identityserver/storetest/user_store.go b/pkg/identityserver/storetest/user_store.go index 529f97881d..ecf3d3c639 100644 --- a/pkg/identityserver/storetest/user_store.go +++ b/pkg/identityserver/storetest/user_store.go @@ -75,6 +75,12 @@ func (st *StoreTest) TestUserStoreCRUD(t *T) { ConsolePreferences: &ttnpb.UserConsolePreferences{ ConsoleTheme: ttnpb.ConsoleTheme_CONSOLE_THEME_LIGHT, }, + EmailNotificationPreferences: &ttnpb.EmailNotificationPreferences{ + Types: []ttnpb.NotificationType{ + ttnpb.NotificationType_API_KEY_CREATED, + }, + }, + UniversalRights: []ttnpb.Right{ttnpb.Right_RIGHT_ALL}, }) if a.So(err, should.BeNil) && a.So(created, should.NotBeNil) { @@ -99,8 +105,14 @@ func (st *StoreTest) TestUserStoreCRUD(t *T) { should.Resemble, &ttnpb.UserConsolePreferences{ConsoleTheme: ttnpb.ConsoleTheme_CONSOLE_THEME_LIGHT}, ) + a.So(created.UniversalRights, should.Resemble, []ttnpb.Right{ttnpb.Right_RIGHT_ALL}) a.So(*ttnpb.StdTime(created.CreatedAt), should.HappenWithin, 5*time.Second, start) a.So(*ttnpb.StdTime(created.UpdatedAt), should.HappenWithin, 5*time.Second, start) + a.So(created.EmailNotificationPreferences, should.Resemble, &ttnpb.EmailNotificationPreferences{ + Types: []ttnpb.NotificationType{ + ttnpb.NotificationType_API_KEY_CREATED, + }, + }) } }) @@ -221,7 +233,16 @@ func (st *StoreTest) TestUserStoreCRUD(t *T) { DashboardLayouts: &ttnpb.UserConsolePreferences_DashboardLayouts{ ApiKey: ttnpb.DashboardLayout_DASHBOARD_LAYOUT_LIST, }, + Tutorials: &ttnpb.UserConsolePreferences_Tutorials{ + Seen: []ttnpb.Tutorial{ttnpb.Tutorial_TUTORIAL_LIVE_DATA_SPLIT_VIEW}, + }, }, + EmailNotificationPreferences: &ttnpb.EmailNotificationPreferences{ + Types: []ttnpb.NotificationType{ + ttnpb.NotificationType_API_KEY_CREATED, + }, + }, + UniversalRights: []ttnpb.Right{ttnpb.Right_RIGHT_USER_INFO}, }, mask) if a.So(err, should.BeNil) && a.So(updated, should.NotBeNil) { a.So(updated.GetIds().GetUserId(), should.Equal, "foo") @@ -248,8 +269,17 @@ func (st *StoreTest) TestUserStoreCRUD(t *T) { DashboardLayouts: &ttnpb.UserConsolePreferences_DashboardLayouts{ ApiKey: ttnpb.DashboardLayout_DASHBOARD_LAYOUT_LIST, }, + Tutorials: &ttnpb.UserConsolePreferences_Tutorials{ + Seen: []ttnpb.Tutorial{ttnpb.Tutorial_TUTORIAL_LIVE_DATA_SPLIT_VIEW}, + }, }, ) + a.So(updated.EmailNotificationPreferences, should.Resemble, &ttnpb.EmailNotificationPreferences{ + Types: []ttnpb.NotificationType{ + ttnpb.NotificationType_API_KEY_CREATED, + }, + }) + a.So(updated.UniversalRights, should.Resemble, []ttnpb.Right{ttnpb.Right_RIGHT_USER_INFO}) a.So(*ttnpb.StdTime(updated.CreatedAt), should.Equal, *ttnpb.StdTime(created.CreatedAt)) a.So(*ttnpb.StdTime(updated.UpdatedAt), should.HappenWithin, 5*time.Second, start) } diff --git a/pkg/identityserver/user_access.go b/pkg/identityserver/user_access.go index b3e22c385d..de585af182 100644 --- a/pkg/identityserver/user_access.go +++ b/pkg/identityserver/user_access.go @@ -86,12 +86,11 @@ func (is *IdentityServer) createUserAPIKey( events.Publish(evtCreateUserAPIKey.NewWithIdentifiersAndData(ctx, req.GetUserIds(), key)) go is.notifyInternal(ctx, &ttnpb.CreateNotificationRequest{ EntityIds: req.GetUserIds().GetEntityIdentifiers(), - NotificationType: "api_key_created", + NotificationType: ttnpb.GetNotificationTypeString(ttnpb.NotificationType_API_KEY_CREATED), Data: ttnpb.MustMarshalAny(key), Receivers: []ttnpb.NotificationReceiver{ ttnpb.NotificationReceiver_NOTIFICATION_RECEIVER_ADMINISTRATIVE_CONTACT, }, - Email: true, }) key.Key = token @@ -203,12 +202,11 @@ func (is *IdentityServer) updateUserAPIKey( events.Publish(evtUpdateUserAPIKey.NewWithIdentifiersAndData(ctx, req.GetUserIds(), key)) go is.notifyInternal(ctx, &ttnpb.CreateNotificationRequest{ EntityIds: req.GetUserIds().GetEntityIdentifiers(), - NotificationType: "api_key_changed", + NotificationType: ttnpb.GetNotificationTypeString(ttnpb.NotificationType_API_KEY_CHANGED), Data: ttnpb.MustMarshalAny(key), Receivers: []ttnpb.NotificationReceiver{ ttnpb.NotificationReceiver_NOTIFICATION_RECEIVER_ADMINISTRATIVE_CONTACT, }, - Email: true, }) return key, nil @@ -249,49 +247,45 @@ func (is *IdentityServer) createLoginToken( return nil, errLoginTokensDisabled.New() } - var canCreateMoreTokens bool - err := is.store.Transact(ctx, func(ctx context.Context, st store.Store) error { + // Admin callers can skip sending emails. + canSkipEmail := is.IsAdmin(ctx) + var canReturnToken bool + + token, err := auth.GenerateKey(ctx) + if err != nil { + return nil, err + } + expiresAt := time.Now().Add(loginTokenConfig.TokenTTL) + + err = is.store.Transact(ctx, func(ctx context.Context, st store.Store) error { activeTokens, err := st.FindActiveLoginTokens(ctx, req.GetUserIds()) if err != nil { return err } - canCreateMoreTokens = len(activeTokens) < maxActiveLoginTokens - return nil - }) - if err != nil { - return nil, err - } - if !canCreateMoreTokens { - return nil, errLoginTokensStillValid.New() - } + if len(activeTokens) >= maxActiveLoginTokens { + return errLoginTokensStillValid.New() + } - var canSkipEmail, canReturnToken bool - if is.IsAdmin(ctx) { - canSkipEmail = true // Admin callers can skip sending emails. - err := is.store.Transact(ctx, func(ctx context.Context, st store.Store) error { + if is.IsAdmin(ctx) { usr, err := st.GetUser(ctx, req.GetUserIds(), []string{"admin"}) - if !usr.Admin { - canReturnToken = true // Admin callers can get login tokens for non-admin users. + if err != nil { + return err } - return err - }) - if err != nil { - return nil, err + + // Admin callers can get login tokens for non-admin users. + canReturnToken = !usr.GetAdmin() } - } - token, err := auth.GenerateKey(ctx) - if err != nil { - return nil, err - } - expiresAt := time.Now().Add(loginTokenConfig.TokenTTL) - err = is.store.Transact(ctx, func(ctx context.Context, st store.Store) error { - _, err := st.CreateLoginToken(ctx, &ttnpb.LoginToken{ + _, err = st.CreateLoginToken(ctx, &ttnpb.LoginToken{ UserIds: req.GetUserIds(), ExpiresAt: timestamppb.New(expiresAt), Token: token, }) - return err + if err != nil { + return err + } + + return nil }) if err != nil { return nil, err @@ -300,7 +294,7 @@ func (is *IdentityServer) createLoginToken( if !canSkipEmail || !req.SkipEmail { go is.SendTemplateEmailToUserIDs( is.FromRequestContext(ctx), - "login_token", + ttnpb.GetNotificationTypeString(ttnpb.NotificationType_LOGIN_TOKEN), func(ctx context.Context, data email.TemplateData) (email.TemplateData, error) { return &templates.LoginTokenData{ TemplateData: data, diff --git a/pkg/identityserver/user_access_test.go b/pkg/identityserver/user_access_test.go index bb09beaa12..8cd95ce333 100644 --- a/pkg/identityserver/user_access_test.go +++ b/pkg/identityserver/user_access_test.go @@ -26,6 +26,77 @@ import ( "google.golang.org/grpc" ) +func TestUserAPIKeysPermissions(t *testing.T) { // nolint:gocyclo + p := &storetest.Population{} + + readOnlyAdmin := p.NewUser() + readOnlyAdmin.Admin = true + readOnlyAdminKey, _ := p.NewAPIKey(readOnlyAdmin.GetEntityIdentifiers(), ttnpb.AllReadAdminRights.GetRights()...) + readOnlyAdminKeyCreds := rpcCreds(readOnlyAdminKey) + + usr1 := p.NewUser() + usr1Key, _ := p.NewAPIKey(usr1.GetEntityIdentifiers(), + ttnpb.Right_RIGHT_USER_INFO, + ttnpb.Right_RIGHT_USER_APPLICATIONS_LIST, + ) + usr1Creds := rpcCreds(usr1Key) + + t.Parallel() + a, ctx := test.New(t) + + testWithIdentityServer(t, func(is *IdentityServer, cc *grpc.ClientConn) { + is.config.AdminRights.All = true + + reg := ttnpb.NewUserAccessClient(cc) + + for _, opts := range [][]grpc.CallOption{nil, {usr1Creds}, {readOnlyAdminKeyCreds}} { + created, err := reg.CreateAPIKey(ctx, &ttnpb.CreateUserAPIKeyRequest{ + UserIds: usr1.GetIds(), + Name: "api-key-name", + Rights: []ttnpb.Right{ttnpb.Right_RIGHT_USER_INFO}, + }, opts...) + if a.So(err, should.NotBeNil) && a.So(errors.IsPermissionDenied(err), should.BeTrue) { + a.So(created, should.BeNil) + } + + list, err := reg.ListAPIKeys(ctx, &ttnpb.ListUserAPIKeysRequest{ + UserIds: usr1.GetIds(), + }, opts...) + if a.So(err, should.NotBeNil) && a.So(errors.IsPermissionDenied(err), should.BeTrue) { + a.So(list, should.BeNil) + } + + got, err := reg.GetAPIKey(ctx, &ttnpb.GetUserAPIKeyRequest{ + UserIds: usr1.GetIds(), + KeyId: usr1Key.GetId(), + }, opts...) + if a.So(err, should.NotBeNil) && a.So(errors.IsPermissionDenied(err), should.BeTrue) { + a.So(got, should.BeNil) + } + + updated, err := reg.UpdateAPIKey(ctx, &ttnpb.UpdateUserAPIKeyRequest{ + UserIds: usr1.GetIds(), + ApiKey: &ttnpb.APIKey{ + Id: usr1Key.GetId(), + Name: "api-key-name-updated", + }, + FieldMask: ttnpb.FieldMask("name"), + }, opts...) + if a.So(err, should.NotBeNil) && a.So(errors.IsPermissionDenied(err), should.BeTrue) { + a.So(updated, should.BeNil) + } + + _, err = reg.DeleteAPIKey(ctx, &ttnpb.DeleteUserAPIKeyRequest{ + UserIds: usr1.GetIds(), + KeyId: usr1Key.GetId(), + }, opts...) + if !a.So(errors.IsPermissionDenied(err), should.BeTrue) { + t.FailNow() + } + } + }, withPrivateTestDatabase(p)) +} + func TestUserAPIKeys(t *testing.T) { // nolint:gocyclo p := &storetest.Population{} @@ -39,7 +110,6 @@ func TestUserAPIKeys(t *testing.T) { // nolint:gocyclo ttnpb.Right_RIGHT_USER_INFO, ttnpb.Right_RIGHT_USER_APPLICATIONS_LIST, ) - usr1Creds := rpcCreds(usr1Key) limitedKey, _ := p.NewAPIKey(usr1.GetEntityIdentifiers(), ttnpb.Right_RIGHT_USER_INFO, ttnpb.Right_RIGHT_USER_SETTINGS_BASIC, @@ -142,53 +212,6 @@ func TestUserAPIKeys(t *testing.T) { // nolint:gocyclo }) } - // API Key CRUD with different invalid credentials. - for _, opts := range [][]grpc.CallOption{nil, {usr1Creds}} { - created, err := reg.CreateAPIKey(ctx, &ttnpb.CreateUserAPIKeyRequest{ - UserIds: usr1.GetIds(), - Name: "api-key-name", - Rights: []ttnpb.Right{ttnpb.Right_RIGHT_USER_INFO}, - }, opts...) - if a.So(err, should.NotBeNil) && a.So(errors.IsPermissionDenied(err), should.BeTrue) { - a.So(created, should.BeNil) - } - - list, err := reg.ListAPIKeys(ctx, &ttnpb.ListUserAPIKeysRequest{ - UserIds: usr1.GetIds(), - }, opts...) - if a.So(err, should.NotBeNil) && a.So(errors.IsPermissionDenied(err), should.BeTrue) { - a.So(list, should.BeNil) - } - - got, err := reg.GetAPIKey(ctx, &ttnpb.GetUserAPIKeyRequest{ - UserIds: usr1.GetIds(), - KeyId: usr1Key.GetId(), - }, opts...) - if a.So(err, should.NotBeNil) && a.So(errors.IsPermissionDenied(err), should.BeTrue) { - a.So(got, should.BeNil) - } - - updated, err := reg.UpdateAPIKey(ctx, &ttnpb.UpdateUserAPIKeyRequest{ - UserIds: usr1.GetIds(), - ApiKey: &ttnpb.APIKey{ - Id: usr1Key.GetId(), - Name: "api-key-name-updated", - }, - FieldMask: ttnpb.FieldMask("name"), - }, opts...) - if a.So(err, should.NotBeNil) && a.So(errors.IsPermissionDenied(err), should.BeTrue) { - a.So(updated, should.BeNil) - } - - _, err = reg.DeleteAPIKey(ctx, &ttnpb.DeleteUserAPIKeyRequest{ - UserIds: usr1.GetIds(), - KeyId: usr1Key.GetId(), - }, opts...) - if !a.So(errors.IsPermissionDenied(err), should.BeTrue) { - t.FailNow() - } - } - // API Key CRUD with different valid credentials. for _, opts := range [][]grpc.CallOption{{adminCreds}, {limitedCreds}} { created, err := reg.CreateAPIKey(ctx, &ttnpb.CreateUserAPIKeyRequest{ diff --git a/pkg/identityserver/user_bookmark_registry_test.go b/pkg/identityserver/user_bookmark_registry_test.go index 0ae64b59cc..d5768fad76 100644 --- a/pkg/identityserver/user_bookmark_registry_test.go +++ b/pkg/identityserver/user_bookmark_registry_test.go @@ -26,9 +26,7 @@ import ( "google.golang.org/grpc" ) -func TestUsersBookmarksOperations(t *testing.T) { - t.Parallel() - +func TestUsersBookmarksPermissions(t *testing.T) { p := &storetest.Population{} usr1 := p.NewUser() @@ -40,24 +38,114 @@ func TestUsersBookmarksOperations(t *testing.T) { app1 := p.NewApplication(usr1.GetOrganizationOrUserIdentifiers()) app2 := p.NewApplication(usr1.GetOrganizationOrUserIdentifiers()) - key, _ := p.NewAPIKey(usr1.GetEntityIdentifiers(), ttnpb.Right_RIGHT_ALL) - creds := rpcCreds(key) - keyWithoutRights, _ := p.NewAPIKey(usr1.GetEntityIdentifiers()) credsWithoutRights := rpcCreds(keyWithoutRights) + readOnlyAdmin := p.NewUser() + readOnlyAdmin.Admin = true + readOnlyAdminKey, _ := p.NewAPIKey(readOnlyAdmin.GetEntityIdentifiers(), ttnpb.AllReadAdminRights.GetRights()...) + readOnlyAdminKeyCreds := rpcCreds(readOnlyAdminKey) + + t.Parallel() + a, ctx := test.New(t) + testWithIdentityServer(t, func(_ *IdentityServer, cc *grpc.ClientConn) { reg := ttnpb.NewUserBookmarkRegistryClient(cc) - t.Run("Create/WithoutRights", func(t *testing.T) { // nolint:paralleltest - a, ctx := test.New(t) - got, err := reg.Create(ctx, &ttnpb.CreateUserBookmarkRequest{ + t.Run("Invalid credentials", func(t *testing.T) { // nolint:paralleltest + for _, opts := range [][]grpc.CallOption{nil, {credsWithoutRights}} { + _, err := reg.Create(ctx, &ttnpb.CreateUserBookmarkRequest{ + UserIds: usr1.Ids, + EntityIds: app1.GetEntityIdentifiers(), + }, opts...) + if a.So(err, should.NotBeNil) { + a.So(errors.IsPermissionDenied(err), should.BeTrue) + } + + _, err = reg.List(ctx, &ttnpb.ListUserBookmarksRequest{ + UserIds: usr1.Ids, + }, opts...) + if a.So(err, should.NotBeNil) { + a.So(errors.IsPermissionDenied(err), should.BeTrue) + } + + _, err = reg.Delete(ctx, &ttnpb.DeleteUserBookmarkRequest{ + UserIds: usr1.Ids, + EntityIds: app1.GetEntityIdentifiers(), + }, opts...) + if a.So(err, should.NotBeNil) { + a.So(errors.IsPermissionDenied(err), should.BeTrue) + } + + _, err = reg.BatchDelete(ctx, &ttnpb.BatchDeleteUserBookmarksRequest{ + UserIds: usr1.Ids, + EntityIds: []*ttnpb.EntityIdentifiers{ + app1.GetEntityIdentifiers(), + app2.GetEntityIdentifiers(), + }, + }, opts...) + if a.So(err, should.NotBeNil) { + a.So(errors.IsPermissionDenied(err), should.BeTrue) + } + } + }) + + t.Run("Admin read-only", func(t *testing.T) { // nolint:paralleltest + _, err := reg.Create(ctx, &ttnpb.CreateUserBookmarkRequest{ UserIds: usr1.Ids, EntityIds: app1.GetEntityIdentifiers(), - }, credsWithoutRights) - a.So(got, should.BeNil) - a.So(errors.IsPermissionDenied(err), should.BeTrue) + }, readOnlyAdminKeyCreds) + if a.So(err, should.NotBeNil) { + a.So(errors.IsPermissionDenied(err), should.BeTrue) + } + + _, err = reg.List(ctx, &ttnpb.ListUserBookmarksRequest{ + UserIds: usr1.Ids, + }, readOnlyAdminKeyCreds) + a.So(errors.IsPermissionDenied(err), should.BeFalse) + + _, err = reg.Delete(ctx, &ttnpb.DeleteUserBookmarkRequest{ + UserIds: usr1.Ids, + EntityIds: app1.GetEntityIdentifiers(), + }, readOnlyAdminKeyCreds) + if a.So(err, should.NotBeNil) { + a.So(errors.IsPermissionDenied(err), should.BeTrue) + } + + _, err = reg.BatchDelete(ctx, &ttnpb.BatchDeleteUserBookmarksRequest{ + UserIds: usr1.Ids, + EntityIds: []*ttnpb.EntityIdentifiers{ + app1.GetEntityIdentifiers(), + app2.GetEntityIdentifiers(), + }, + }, readOnlyAdminKeyCreds) + if a.So(err, should.NotBeNil) { + a.So(errors.IsPermissionDenied(err), should.BeTrue) + } }) + }, withPrivateTestDatabase(p)) +} + +func TestUsersBookmarksOperations(t *testing.T) { + t.Parallel() + + p := &storetest.Population{} + + usr1 := p.NewUser() + usr1.Password = "OldPassword" + usr1.PrimaryEmailAddress = "user-1@email.com" + validatedAtTime := time.Now().Truncate(time.Millisecond) + usr1.PrimaryEmailAddressValidatedAt = ttnpb.ProtoTime(&validatedAtTime) + + app1 := p.NewApplication(usr1.GetOrganizationOrUserIdentifiers()) + app2 := p.NewApplication(usr1.GetOrganizationOrUserIdentifiers()) + + key, _ := p.NewAPIKey(usr1.GetEntityIdentifiers(), ttnpb.Right_RIGHT_ALL) + creds := rpcCreds(key) + + testWithIdentityServer(t, func(_ *IdentityServer, cc *grpc.ClientConn) { + reg := ttnpb.NewUserBookmarkRegistryClient(cc) + t.Run("Create", func(t *testing.T) { // nolint:paralleltest a, ctx := test.New(t) got, err := reg.Create(ctx, &ttnpb.CreateUserBookmarkRequest{ @@ -99,14 +187,6 @@ func TestUsersBookmarksOperations(t *testing.T) { a.So(got, should.Resemble, &ttnpb.UserBookmark{UserIds: usr1.Ids, EntityIds: app2.GetEntityIdentifiers()}) }) - t.Run("FindBookmarks/WithoutRights", func(t *testing.T) { // nolint:paralleltest - a, ctx := test.New(t) - got, err := reg.List(ctx, &ttnpb.ListUserBookmarksRequest{ - UserIds: usr1.Ids, - }, credsWithoutRights) - a.So(got, should.BeNil) - a.So(errors.IsPermissionDenied(err), should.BeTrue) - }) t.Run("FindBookmarks/HasEntityType", func(t *testing.T) { // nolint:paralleltest a, ctx := test.New(t) @@ -162,14 +242,6 @@ func TestUsersBookmarksOperations(t *testing.T) { } }) - t.Run("Delete/WithoutRights", func(t *testing.T) { // nolint:paralleltest - a, ctx := test.New(t) - _, err := reg.Delete(ctx, &ttnpb.DeleteUserBookmarkRequest{ - UserIds: usr1.Ids, - EntityIds: app1.GetEntityIdentifiers(), - }, credsWithoutRights) - a.So(errors.IsPermissionDenied(err), should.BeTrue) - }) t.Run("Delete", func(t *testing.T) { // nolint:paralleltest a, ctx := test.New(t) _, err := reg.Delete(ctx, &ttnpb.DeleteUserBookmarkRequest{ @@ -192,17 +264,6 @@ func TestUsersBookmarksOperations(t *testing.T) { } }) - t.Run("BatchDelete/WithoutRights", func(t *testing.T) { // nolint:paralleltest - a, ctx := test.New(t) - _, err := reg.BatchDelete(ctx, &ttnpb.BatchDeleteUserBookmarksRequest{ - UserIds: usr1.Ids, - EntityIds: []*ttnpb.EntityIdentifiers{ - app1.GetEntityIdentifiers(), - app2.GetEntityIdentifiers(), - }, - }, credsWithoutRights) - a.So(errors.IsPermissionDenied(err), should.BeTrue) - }) t.Run("BatchDelete", func(t *testing.T) { // nolint:paralleltest a, ctx := test.New(t) _, err := reg.BatchDelete(ctx, &ttnpb.BatchDeleteUserBookmarksRequest{ diff --git a/pkg/identityserver/user_registry.go b/pkg/identityserver/user_registry.go index 3864b7d705..12838a0aff 100644 --- a/pkg/identityserver/user_registry.go +++ b/pkg/identityserver/user_registry.go @@ -179,6 +179,20 @@ func (is *IdentityServer) createUser(ctx context.Context, req *ttnpb.CreateUserR createdByAdmin := is.IsAdmin(ctx) config := is.configFromContext(ctx) + if createdByAdmin { + authInfo, err := is.authInfo(ctx) + if err != nil { + return nil, err + } + if !ttnpb.RightsFrom(authInfo.GetRights()...).IncludesAll(ttnpb.Right_RIGHT_USER_CREATE) { + return nil, rights.ErrInsufficientRights.WithAttributes( + "uid", req.IDString(), + "entity_type", "user", + "missing", ttnpb.Right_RIGHT_ALL.String(), + ) + } + } + if err = blocklist.Check(ctx, req.User.GetIds().GetUserId()); err != nil { return nil, err } @@ -259,12 +273,11 @@ func (is *IdentityServer) createUser(ctx context.Context, req *ttnpb.CreateUserR if usr.State == ttnpb.State_STATE_REQUESTED { go is.notifyAdminsInternal(ctx, &ttnpb.CreateNotificationRequest{ EntityIds: req.GetUser().GetIds().GetEntityIdentifiers(), - NotificationType: "user_requested", + NotificationType: ttnpb.GetNotificationTypeString(ttnpb.NotificationType_USER_REQUESTED), Data: ttnpb.MustMarshalAny(req), Receivers: []ttnpb.NotificationReceiver{ ttnpb.NotificationReceiver_NOTIFICATION_RECEIVER_ADMINISTRATIVE_CONTACT, }, - Email: true, }) } @@ -339,6 +352,18 @@ func (is *IdentityServer) listUsers(ctx context.Context, req *ttnpb.ListUsersReq if err = is.RequireAdmin(ctx); err != nil { return nil, err } + + authInfo, err := is.authInfo(ctx) + if err != nil { + return nil, err + } + if !ttnpb.RightsFrom(authInfo.GetRights()...).IncludesAll(ttnpb.Right_RIGHT_USER_LIST) { + return nil, rights.ErrInsufficientRights.WithAttributes( + "entity_type", "user", + "missing", ttnpb.Right_RIGHT_ALL.String(), + ) + } + contactInfoInPath := ttnpb.HasAnyField(req.FieldMask.GetPaths(), "contact_info") if contactInfoInPath { req.FieldMask.Paths = ttnpb.ExcludeFields(req.FieldMask.Paths, "contact_info") @@ -510,13 +535,12 @@ func (is *IdentityServer) updateUser(ctx context.Context, req *ttnpb.UpdateUserR if ttnpb.HasAnyField(req.FieldMask.GetPaths(), "state") { go is.notifyInternal(ctx, &ttnpb.CreateNotificationRequest{ EntityIds: usr.GetIds().GetEntityIdentifiers(), - NotificationType: "entity_state_changed", + NotificationType: ttnpb.GetNotificationTypeString(ttnpb.NotificationType_ENTITY_STATE_CHANGED), Data: ttnpb.MustMarshalAny(&ttnpb.EntityStateChangedNotification{ State: usr.State, StateDescription: usr.StateDescription, }), Receivers: []ttnpb.NotificationReceiver{ttnpb.NotificationReceiver_NOTIFICATION_RECEIVER_COLLABORATOR}, - Email: true, }) } @@ -639,8 +663,7 @@ func (is *IdentityServer) updateUserPassword(ctx context.Context, req *ttnpb.Upd events.Publish(evtUpdateUser.NewWithIdentifiersAndData(ctx, req.GetUserIds(), updateMask)) go is.notifyInternal(ctx, &ttnpb.CreateNotificationRequest{ EntityIds: req.GetUserIds().GetEntityIdentifiers(), - NotificationType: "password_changed", - Email: true, + NotificationType: ttnpb.GetNotificationTypeString(ttnpb.NotificationType_PASSWORD_CHANGED), Receivers: []ttnpb.NotificationReceiver{ttnpb.NotificationReceiver_NOTIFICATION_RECEIVER_COLLABORATOR}, }) return ttnpb.Empty, nil @@ -682,13 +705,15 @@ func (is *IdentityServer) createTemporaryPassword(ctx context.Context, req *ttnp "temporary_password", temporaryPassword, )).Info("Created temporary password") events.Publish(evtUpdateUser.NewWithIdentifiersAndData(ctx, req.GetUserIds(), updateTemporaryPasswordFieldMask)) - go is.SendTemplateEmailToUserIDs(is.FromRequestContext(ctx), "temporary_password", func(ctx context.Context, data email.TemplateData) (email.TemplateData, error) { - return &templates.TemporaryPasswordData{ - TemplateData: data, - TemporaryPassword: temporaryPassword, - TTL: ttl, - }, nil - }, req.GetUserIds()) + go is.SendTemplateEmailToUserIDs( // nolint:errcheck + is.FromRequestContext(ctx), ttnpb.GetNotificationTypeString(ttnpb.NotificationType_TEMPORARY_PASSWORD), + func(_ context.Context, data email.TemplateData) (email.TemplateData, error) { + return &templates.TemporaryPasswordData{ + TemplateData: data, + TemporaryPassword: temporaryPassword, + TTL: ttl, + }, nil + }, req.GetUserIds()) return ttnpb.Empty, nil } @@ -752,6 +777,11 @@ func (is *IdentityServer) purgeUser(ctx context.Context, ids *ttnpb.UserIdentifi if !is.IsAdmin(ctx) { return nil, errAdminsPurgeUsers.New() } + if err := rights.RequireUser( + store.WithSoftDeleted(ctx, false), ids, ttnpb.Right_RIGHT_USER_PURGE, + ); err != nil { + return nil, err + } err := is.store.Transact(ctx, func(ctx context.Context, st store.Store) error { // Delete related API keys before purging the user. err := st.DeleteEntityAPIKeys(ctx, ids.GetEntityIdentifiers()) diff --git a/pkg/identityserver/user_registry_test.go b/pkg/identityserver/user_registry_test.go index 54daf10905..2994ba1726 100644 --- a/pkg/identityserver/user_registry_test.go +++ b/pkg/identityserver/user_registry_test.go @@ -239,6 +239,66 @@ func TestUserUpdateInvalidPassword(t *testing.T) { }, withPrivateTestDatabase(p)) } +func TestUsersCRUDReadOnlyAdmin(t *testing.T) { + p := &storetest.Population{} + + readOnlyAdmin := p.NewUser() + readOnlyAdmin.Admin = true + readOnlyAdminKey, _ := p.NewAPIKey(readOnlyAdmin.GetEntityIdentifiers(), ttnpb.AllReadAdminRights.GetRights()...) + readOnlyAdminCreds := rpcCreds(readOnlyAdminKey) + + usr1 := p.NewUser() + usr1.Attributes = map[string]string{"foo": "bar"} + + t.Parallel() + a, ctx := test.New(t) + + testWithIdentityServer(t, func(_ *IdentityServer, cc *grpc.ClientConn) { + reg := ttnpb.NewUserRegistryClient(cc) + + // Fetch only public safe fields. + got, err := reg.Get(ctx, &ttnpb.GetUserRequest{ + UserIds: usr1.GetIds(), + FieldMask: ttnpb.FieldMask("ids"), + }, readOnlyAdminCreds) + if a.So(err, should.BeNil) { + a.So(got.GetIds(), should.Resemble, usr1.GetIds()) + a.So(got.Attributes, should.BeEmpty) + } + + // Fetch non-public fields. + got, err = reg.Get(ctx, &ttnpb.GetUserRequest{ + UserIds: usr1.GetIds(), + FieldMask: ttnpb.FieldMask("attributes"), + }, readOnlyAdminCreds) + if a.So(err, should.BeNil) { + a.So(got.GetIds(), should.Resemble, usr1.GetIds()) + a.So(got.GetAttributes(), should.Resemble, usr1.Attributes) + } + + _, err = reg.Update(ctx, &ttnpb.UpdateUserRequest{ + User: &ttnpb.User{ + Ids: usr1.GetIds(), + Name: "Updated Name", + }, + FieldMask: ttnpb.FieldMask("name"), + }, readOnlyAdminCreds) + if a.So(err, should.NotBeNil) { + a.So(errors.IsPermissionDenied(err), should.BeTrue) + } + + _, err = reg.Delete(ctx, usr1.GetIds(), readOnlyAdminCreds) + if a.So(err, should.NotBeNil) { + a.So(errors.IsPermissionDenied(err), should.BeTrue) + } + + _, err = reg.Purge(ctx, usr1.GetIds(), readOnlyAdminCreds) + if a.So(err, should.NotBeNil) { + a.So(errors.IsPermissionDenied(err), should.BeTrue) + } + }, withPrivateTestDatabase(p)) +} + func TestUsersCRUD(t *testing.T) { t.Parallel() @@ -424,12 +484,18 @@ func TestUsersCRUD(t *testing.T) { User: ttnpb.DashboardLayout_DASHBOARD_LAYOUT_GRID, Overview: ttnpb.DashboardLayout_DASHBOARD_LAYOUT_GRID, }, + Tutorials: &ttnpb.UserConsolePreferences_Tutorials{ + Seen: []ttnpb.Tutorial{ + ttnpb.Tutorial_TUTORIAL_LIVE_DATA_SPLIT_VIEW, + }, + }, }, }, FieldMask: ttnpb.FieldMask( "console_preferences.console_theme", "console_preferences.dashboard_layouts", "console_preferences.sort_by", + "console_preferences.tutorials", ), }, creds) if a.So(err, should.BeNil) { @@ -457,6 +523,15 @@ func TestUsersCRUD(t *testing.T) { Overview: ttnpb.DashboardLayout_DASHBOARD_LAYOUT_GRID, }, ) + a.So( + got.ConsolePreferences.GetTutorials(), + should.Resemble, + &ttnpb.UserConsolePreferences_Tutorials{ + Seen: []ttnpb.Tutorial{ + ttnpb.Tutorial_TUTORIAL_LIVE_DATA_SPLIT_VIEW, + }, + }, + ) } }) }) diff --git a/pkg/identityserver/user_session_registry_test.go b/pkg/identityserver/user_session_registry_test.go index 0f86ed0378..f0cd771bfb 100644 --- a/pkg/identityserver/user_session_registry_test.go +++ b/pkg/identityserver/user_session_registry_test.go @@ -34,11 +34,18 @@ func TestUserSessionsRegistry(t *testing.T) { p := &storetest.Population{} usr1 := p.NewUser() + key, _ := p.NewAPIKey(usr1.GetEntityIdentifiers(), ttnpb.Right_RIGHT_ALL) creds := rpcCreds(key) + keyWithoutRights, _ := p.NewAPIKey(usr1.GetEntityIdentifiers()) credsWithoutRights := rpcCreds(keyWithoutRights) + readOnlyAdmin := p.NewUser() + readOnlyAdmin.Admin = true + readOnlyAdminKey, _ := p.NewAPIKey(readOnlyAdmin.GetEntityIdentifiers(), ttnpb.AllReadAdminRights.GetRights()...) + readOnlyAdminKeyCreds := rpcCreds(readOnlyAdminKey) + a, ctx := test.New(t) randomUUID := uuid.NewString() @@ -46,19 +53,22 @@ func TestUserSessionsRegistry(t *testing.T) { testWithIdentityServer(t, func(is *IdentityServer, cc *grpc.ClientConn) { reg := ttnpb.NewUserSessionRegistryClient(cc) - _, err := reg.List(ctx, &ttnpb.ListUserSessionsRequest{ - UserIds: usr1.GetIds(), - }, credsWithoutRights) - if a.So(err, should.NotBeNil) { - a.So(errors.IsPermissionDenied(err), should.BeTrue) - } + // Invalid credentials + for _, opts := range [][]grpc.CallOption{nil, {credsWithoutRights}, {readOnlyAdminKeyCreds}} { + _, err := reg.List(ctx, &ttnpb.ListUserSessionsRequest{ + UserIds: usr1.GetIds(), + }, opts...) + if a.So(err, should.NotBeNil) { + a.So(errors.IsPermissionDenied(err), should.BeTrue) + } - _, err = reg.Delete(ctx, &ttnpb.UserSessionIdentifiers{ - UserIds: usr1.GetIds(), - SessionId: randomUUID, - }, credsWithoutRights) - if a.So(err, should.NotBeNil) { - a.So(errors.IsPermissionDenied(err), should.BeTrue) + _, err = reg.Delete(ctx, &ttnpb.UserSessionIdentifiers{ + UserIds: usr1.GetIds(), + SessionId: randomUUID, + }, opts...) + if a.So(err, should.NotBeNil) { + a.So(errors.IsPermissionDenied(err), should.BeTrue) + } } sessions, err := reg.List(ctx, &ttnpb.ListUserSessionsRequest{ diff --git a/pkg/networkserver/config.go b/pkg/networkserver/config.go index a7bf239c05..9dfa2b0062 100644 --- a/pkg/networkserver/config.go +++ b/pkg/networkserver/config.go @@ -131,23 +131,30 @@ type InteropConfig struct { ID *types.EUI64 `name:"id" description:"NSID of this Network Server (EUI)"` } +// PaginationConfig represents the configuration for pagination. +type PaginationConfig struct { + DefaultLimit int64 `name:"default-limit" description:"Default limit for pagination"` +} + // Config represents the NetworkServer configuration. type Config struct { - ApplicationUplinkQueue ApplicationUplinkQueueConfig `name:"application-uplink-queue"` - Devices DeviceRegistry `name:"-"` - DownlinkTaskQueue DownlinkTaskQueueConfig `name:"downlink-task-queue"` - UplinkDeduplicator UplinkDeduplicator `name:"-"` - ScheduledDownlinkMatcher ScheduledDownlinkMatcher `name:"-"` - NetID types.NetID `name:"net-id" description:"NetID of this Network Server"` - ClusterID string `name:"cluster-id" description:"Cluster ID of this Network Server"` - DevAddrPrefixes []types.DevAddrPrefix `name:"dev-addr-prefixes" description:"Device address prefixes of this Network Server"` - DeduplicationWindow time.Duration `name:"deduplication-window" description:"Time window during which, duplicate messages are collected for metadata"` - CooldownWindow time.Duration `name:"cooldown-window" description:"Time window starting right after deduplication window, during which, duplicate messages are discarded"` - DownlinkPriorities DownlinkPriorityConfig `name:"downlink-priorities" description:"Downlink message priorities"` - DefaultMACSettings MACSettingConfig `name:"default-mac-settings" description:"Default MAC settings to fallback to if not specified by device, band or frequency plan"` - 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"` - DownlinkQueueCapacity int `name:"downlink-queue-capacity" description:"Maximum downlink queue size per-session"` + ApplicationUplinkQueue ApplicationUplinkQueueConfig `name:"application-uplink-queue"` + Devices DeviceRegistry `name:"-"` + DownlinkTaskQueue DownlinkTaskQueueConfig `name:"downlink-task-queue"` + UplinkDeduplicator UplinkDeduplicator `name:"-"` + ScheduledDownlinkMatcher ScheduledDownlinkMatcher `name:"-"` + NetID types.NetID `name:"net-id" description:"NetID of this Network Server"` // nolint: lll + ClusterID string `name:"cluster-id" description:"Cluster ID of this Network Server"` // nolint: lll + DevAddrPrefixes []types.DevAddrPrefix `name:"dev-addr-prefixes" description:"Device address prefixes of this Network Server"` // nolint: lll + DeduplicationWindow time.Duration `name:"deduplication-window" description:"Time window during which, duplicate messages are collected for metadata"` // nolint: lll + CooldownWindow time.Duration `name:"cooldown-window" description:"Time window starting right after deduplication window, during which, duplicate messages are discarded"` // nolint: lll + DownlinkPriorities DownlinkPriorityConfig `name:"downlink-priorities" description:"Downlink message priorities"` // nolint: lll + DefaultMACSettings MACSettingConfig `name:"default-mac-settings" description:"Default MAC settings to fallback to if not specified by device, band or frequency plan"` // nolint: lll + Interop InteropConfig `name:"interop" description:"Interop client configuration"` // nolint: lll + DeviceKEKLabel string `name:"device-kek-label" description:"Label of KEK used to encrypt device keys at rest"` // nolint: lll + DownlinkQueueCapacity int `name:"downlink-queue-capacity" description:"Maximum downlink queue size per-session"` // nolint: lll + MACSettingsProfileRegistry MACSettingsProfileRegistry `name:"-"` + Pagination PaginationConfig `name:"pagination" description:"Pagination configuration"` } // DefaultConfig is the default Network Server configuration. diff --git a/pkg/networkserver/grpc_mac_settings_profile.go b/pkg/networkserver/grpc_mac_settings_profile.go new file mode 100644 index 0000000000..c91fb4aad4 --- /dev/null +++ b/pkg/networkserver/grpc_mac_settings_profile.go @@ -0,0 +1,182 @@ +// Copyright © 2024 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 networkserver implements the LoRaWAN Network Server. +package networkserver + +import ( + "context" + "strconv" + + "go.thethings.network/lorawan-stack/v3/pkg/auth/rights" + "go.thethings.network/lorawan-stack/v3/pkg/errors" + "go.thethings.network/lorawan-stack/v3/pkg/ttnpb" + "google.golang.org/grpc" + "google.golang.org/grpc/metadata" +) + +var ( + errMACSettingsProfileAlreadyExists = errors.DefineAlreadyExists("mac_settings_profile_already_exists", "MAC settings profile already exists") // nolint: lll + errMACSettingsProfileNotFound = errors.DefineNotFound("mac_settings_profile_not_found", "MAC settings profile not found") // nolint: lll +) + +func setTotalHeader(ctx context.Context, total int64) { + grpc.SetHeader(ctx, metadata.Pairs("x-total-count", strconv.FormatInt(total, 10))) // nolint: errcheck +} + +// NsMACSettingsProfileRegistry implements the MAC settings profile registry grpc service. +type NsMACSettingsProfileRegistry struct { + ttnpb.UnimplementedNsMACSettingsProfileRegistryServer + + registry MACSettingsProfileRegistry +} + +// Create creates a new MAC settings profile. +func (m *NsMACSettingsProfileRegistry) Create(ctx context.Context, req *ttnpb.CreateMACSettingsProfileRequest, +) (*ttnpb.CreateMACSettingsProfileResponse, error) { + if err := rights.RequireApplication( + ctx, req.MacSettingsProfile.Ids.ApplicationIds, ttnpb.Right_RIGHT_APPLICATION_DEVICES_WRITE, + ); err != nil { + return nil, err + } + paths := []string{"ids", "mac_settings"} + profile, err := m.registry.Set( + ctx, + req.MacSettingsProfile.Ids, + paths, + func(_ context.Context, profile *ttnpb.MACSettingsProfile) (*ttnpb.MACSettingsProfile, []string, error) { + if profile != nil { + return nil, nil, errMACSettingsProfileAlreadyExists.New() + } + return req.MacSettingsProfile, paths, nil + }) + if err != nil { + logRegistryRPCError(ctx, err, "Failed to create MAC settings profile") + return nil, err + } + + return &ttnpb.CreateMACSettingsProfileResponse{ + MacSettingsProfile: profile, + }, nil +} + +// Get returns the MAC settings profile that matches the given identifiers. +func (m *NsMACSettingsProfileRegistry) Get(ctx context.Context, req *ttnpb.GetMACSettingsProfileRequest, +) (*ttnpb.GetMACSettingsProfileResponse, error) { + if err := rights.RequireApplication( + ctx, req.MacSettingsProfileIds.ApplicationIds, ttnpb.Right_RIGHT_APPLICATION_DEVICES_READ, + ); err != nil { + return nil, err + } + paths := []string{"ids", "mac_settings"} + if req.FieldMask != nil { + paths = req.FieldMask.GetPaths() + } + profile, err := m.registry.Get(ctx, req.MacSettingsProfileIds, paths) + if err != nil { + logRegistryRPCError(ctx, err, "Failed to get MAC settings profile") + return nil, err + } + + return &ttnpb.GetMACSettingsProfileResponse{ + MacSettingsProfile: profile, + }, nil +} + +// Update updates the MAC settings profile that matches the given identifiers. +func (m *NsMACSettingsProfileRegistry) Update(ctx context.Context, req *ttnpb.UpdateMACSettingsProfileRequest, +) (*ttnpb.UpdateMACSettingsProfileResponse, error) { + if err := rights.RequireApplication( + ctx, req.MacSettingsProfileIds.ApplicationIds, ttnpb.Right_RIGHT_APPLICATION_DEVICES_WRITE, + ); err != nil { + return nil, err + } + paths := []string{"ids", "mac_settings"} + if req.FieldMask != nil { + paths = req.FieldMask.GetPaths() + } + profile, err := m.registry.Set( + ctx, + req.MacSettingsProfile.Ids, + paths, + func(_ context.Context, profile *ttnpb.MACSettingsProfile) (*ttnpb.MACSettingsProfile, []string, error) { + if profile == nil { + return nil, nil, errMACSettingsProfileNotFound.New() + } + return req.MacSettingsProfile, paths, nil + }) + if err != nil { + logRegistryRPCError(ctx, err, "Failed to create MAC settings profile") + return nil, err + } + + return &ttnpb.UpdateMACSettingsProfileResponse{ + MacSettingsProfile: profile, + }, nil +} + +// Delete deletes the MAC settings profile that matches the given identifiers. +func (m *NsMACSettingsProfileRegistry) Delete(ctx context.Context, req *ttnpb.DeleteMACSettingsProfileRequest, +) (*ttnpb.DeleteMACSettingsProfileResponse, error) { + if err := rights.RequireApplication( + ctx, req.MacSettingsProfileIds.ApplicationIds, ttnpb.Right_RIGHT_APPLICATION_DEVICES_WRITE, + ); err != nil { + return nil, err + } + paths := []string{"ids", "mac_settings"} + _, err := m.registry.Set( + ctx, + req.MacSettingsProfileIds, + paths, + func(_ context.Context, profile *ttnpb.MACSettingsProfile) (*ttnpb.MACSettingsProfile, []string, error) { + if profile == nil { + return nil, nil, errMACSettingsProfileNotFound.New() + } + return nil, nil, nil + }) + if err != nil { + logRegistryRPCError(ctx, err, "Failed to delete MAC settings profile") + return nil, err + } + + return &ttnpb.DeleteMACSettingsProfileResponse{}, nil +} + +// List lists the MAC settings profiles. +func (m *NsMACSettingsProfileRegistry) List(ctx context.Context, req *ttnpb.ListMACSettingsProfilesRequest, +) (*ttnpb.ListMACSettingsProfilesResponse, error) { + if err := rights.RequireApplication( + ctx, req.ApplicationIds, ttnpb.Right_RIGHT_APPLICATION_DEVICES_READ, + ); err != nil { + return nil, err + } + paths := []string{"ids", "mac_settings"} + if req.FieldMask != nil { + paths = req.FieldMask.GetPaths() + } + + var total int64 + ctx = m.registry.WithPagination(ctx, req.Limit, req.Page, &total) + + profiles, err := m.registry.List(ctx, req.ApplicationIds, paths) + if err != nil { + logRegistryRPCError(ctx, err, "Failed to list MAC settings profiles") + return nil, err + } + + setTotalHeader(ctx, total) + return &ttnpb.ListMACSettingsProfilesResponse{ + MacSettingsProfiles: profiles, + }, nil +} diff --git a/pkg/networkserver/grpc_mac_settings_profile_test.go b/pkg/networkserver/grpc_mac_settings_profile_test.go new file mode 100644 index 0000000000..340b115353 --- /dev/null +++ b/pkg/networkserver/grpc_mac_settings_profile_test.go @@ -0,0 +1,1235 @@ +// Copyright © 2024 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 networkserver implements the LoRaWAN Network Server. +package networkserver_test + +import ( + "context" + "sync/atomic" + "testing" + + "github.com/smarty/assertions" + "go.thethings.network/lorawan-stack/v3/pkg/auth/rights" + "go.thethings.network/lorawan-stack/v3/pkg/errors" + . "go.thethings.network/lorawan-stack/v3/pkg/networkserver" // nolint: revive + "go.thethings.network/lorawan-stack/v3/pkg/ttnpb" + "go.thethings.network/lorawan-stack/v3/pkg/unique" + "go.thethings.network/lorawan-stack/v3/pkg/util/test" + "go.thethings.network/lorawan-stack/v3/pkg/util/test/assertions/should" +) + +var errNotFound = errors.DefineNotFound("not_found", "not found") + +func TestMACSettingsProfileRegistryGet(t *testing.T) { + t.Parallel() + nilProfileAssertion := func(t *testing.T, profile *ttnpb.GetMACSettingsProfileResponse) bool { + t.Helper() + return assertions.New(t).So(profile, should.BeNil) + } + nilErrorAssertion := func(t *testing.T, err error) bool { + t.Helper() + return assertions.New(t).So(err, should.BeNil) + } + permissionDeniedErrorAssertion := func(t *testing.T, err error) bool { + t.Helper() + return assertions.New(t).So(errors.IsPermissionDenied(err), should.BeTrue) + } + notFoundErrorAssertion := func(t *testing.T, err error) bool { + t.Helper() + return assertions.New(t).So(errors.IsNotFound(err), should.BeTrue) + } + + registeredProfileIDs := &ttnpb.MACSettingsProfileIdentifiers{ + ApplicationIds: &ttnpb.ApplicationIdentifiers{ApplicationId: "test-app-id"}, + ProfileId: "test-profile-id", + } + + for _, tc := range []struct { + Name string + ContextFunc func(context.Context) context.Context + GetFunc func(context.Context, *ttnpb.MACSettingsProfileIdentifiers, []string) (*ttnpb.MACSettingsProfile, error) // nolint: lll + ProfileRequest *ttnpb.GetMACSettingsProfileRequest + ProfileAssertion func(*testing.T, *ttnpb.GetMACSettingsProfileResponse) bool + ErrorAssertion func(*testing.T, error) bool + GetCalls uint64 + }{ + { + Name: "Permission denied", + ContextFunc: func(ctx context.Context) context.Context { + return rights.NewContext(ctx, &rights.Rights{ + ApplicationRights: *rights.NewMap(map[string]*ttnpb.Rights{ + unique.ID(test.Context(), &ttnpb.ApplicationIdentifiers{ApplicationId: "test-app-id"}): nil, + }), + }) + }, + GetFunc: func( + ctx context.Context, + _ *ttnpb.MACSettingsProfileIdentifiers, + _ []string, + ) (*ttnpb.MACSettingsProfile, error) { + err := errors.New("GetFunc must not be called") + test.MustTFromContext(ctx).Error(err) + return nil, err + }, + ProfileRequest: &ttnpb.GetMACSettingsProfileRequest{ + MacSettingsProfileIds: registeredProfileIDs, + FieldMask: ttnpb.FieldMask("mac_settings"), + }, + ProfileAssertion: nilProfileAssertion, + ErrorAssertion: permissionDeniedErrorAssertion, + GetCalls: 0, + }, + { + Name: "Invalid application ID", + ContextFunc: func(ctx context.Context) context.Context { + return rights.NewContext(ctx, &rights.Rights{ + ApplicationRights: *rights.NewMap(map[string]*ttnpb.Rights{ + unique.ID(test.Context(), &ttnpb.ApplicationIdentifiers{ + ApplicationId: "invalid-application", + }): ttnpb.RightsFrom( + ttnpb.Right_RIGHT_APPLICATION_DEVICES_READ, + ), + }), + }) + }, + GetFunc: func( + ctx context.Context, + _ *ttnpb.MACSettingsProfileIdentifiers, + _ []string, + ) (*ttnpb.MACSettingsProfile, error) { + err := errors.New("GetFunc must not be called") + test.MustTFromContext(ctx).Error(err) + return nil, err + }, + ProfileRequest: &ttnpb.GetMACSettingsProfileRequest{ + MacSettingsProfileIds: registeredProfileIDs, + FieldMask: ttnpb.FieldMask("mac_settings"), + }, + ProfileAssertion: nilProfileAssertion, + ErrorAssertion: permissionDeniedErrorAssertion, + GetCalls: 0, + }, + { + Name: "Not found", + ContextFunc: func(ctx context.Context) context.Context { + return rights.NewContext(ctx, &rights.Rights{ + ApplicationRights: *rights.NewMap(map[string]*ttnpb.Rights{ + unique.ID(test.Context(), &ttnpb.ApplicationIdentifiers{ + ApplicationId: "test-app-id", + }): ttnpb.RightsFrom( + ttnpb.Right_RIGHT_APPLICATION_DEVICES_READ, + ), + }), + }) + }, + GetFunc: func( + ctx context.Context, + ids *ttnpb.MACSettingsProfileIdentifiers, + paths []string, + ) (*ttnpb.MACSettingsProfile, error) { + a := assertions.New(test.MustTFromContext(ctx)) + a.So(ids, should.Resemble, ids) + a.So(paths, should.HaveSameElementsDeep, []string{ + "mac_settings", + }) + return nil, errNotFound.New() + }, + ProfileRequest: &ttnpb.GetMACSettingsProfileRequest{ + MacSettingsProfileIds: registeredProfileIDs, + FieldMask: ttnpb.FieldMask("mac_settings"), + }, + ProfileAssertion: nilProfileAssertion, + ErrorAssertion: notFoundErrorAssertion, + GetCalls: 1, + }, + { + Name: "Found", + ContextFunc: func(ctx context.Context) context.Context { + return rights.NewContext(ctx, &rights.Rights{ + ApplicationRights: *rights.NewMap(map[string]*ttnpb.Rights{ + unique.ID(test.Context(), &ttnpb.ApplicationIdentifiers{ + ApplicationId: "test-app-id", + }): ttnpb.RightsFrom( + ttnpb.Right_RIGHT_APPLICATION_DEVICES_READ, + ), + }), + }) + }, + GetFunc: func( + ctx context.Context, + ids *ttnpb.MACSettingsProfileIdentifiers, + paths []string, + ) (*ttnpb.MACSettingsProfile, error) { + a := assertions.New(test.MustTFromContext(ctx)) + a.So(ids, should.Resemble, ids) + a.So(paths, should.HaveSameElementsDeep, []string{ + "ids", + "mac_settings", + }) + return ttnpb.Clone(&ttnpb.MACSettingsProfile{ + Ids: ids, + MacSettings: &ttnpb.MACSettings{ + ResetsFCnt: &ttnpb.BoolValue{Value: true}, + }, + }), nil + }, + ProfileRequest: &ttnpb.GetMACSettingsProfileRequest{ + MacSettingsProfileIds: registeredProfileIDs, + FieldMask: ttnpb.FieldMask("ids", "mac_settings"), + }, + ProfileAssertion: func(t *testing.T, profile *ttnpb.GetMACSettingsProfileResponse) bool { + t.Helper() + return assertions.New(t).So(profile.MacSettingsProfile, should.Resemble, &ttnpb.MACSettingsProfile{ + Ids: &ttnpb.MACSettingsProfileIdentifiers{ + ApplicationIds: &ttnpb.ApplicationIdentifiers{ + ApplicationId: "test-app-id", + }, + ProfileId: "test-profile-id", + }, + MacSettings: &ttnpb.MACSettings{ + ResetsFCnt: &ttnpb.BoolValue{Value: true}, + }, + }) + }, + ErrorAssertion: nilErrorAssertion, + GetCalls: 1, + }, + } { + tc := tc + test.RunSubtest(t, test.SubtestConfig{ + Name: tc.Name, + Parallel: true, + Func: func(ctx context.Context, t *testing.T, a *assertions.Assertion) { + t.Helper() + var getCalls uint64 + + ns, ctx, _, stop := StartTest( + ctx, + TestConfig{ + NetworkServer: Config{ + MACSettingsProfileRegistry: &MockMACSettingsProfileRegistry{ + GetFunc: func( + ctx context.Context, + ids *ttnpb.MACSettingsProfileIdentifiers, + paths []string, + ) (*ttnpb.MACSettingsProfile, error) { + atomic.AddUint64(&getCalls, 1) + return tc.GetFunc(ctx, ids, paths) + }, + }, + }, + TaskStarter: StartTaskExclude( + DownlinkProcessTaskName, + DownlinkDispatchTaskName, + ), + }, + ) + defer stop() + + ns.AddContextFiller(tc.ContextFunc) + ns.AddContextFiller(func(ctx context.Context) context.Context { + return test.ContextWithTB(ctx, t) + }) + + req := ttnpb.Clone(tc.ProfileRequest) + + profile, err := ttnpb.NewNsMACSettingsProfileRegistryClient(ns.LoopbackConn()).Get(ctx, req) + if a.So(tc.ErrorAssertion(t, err), should.BeTrue) { + a.So(tc.ProfileAssertion(t, profile), should.BeTrue) + } + a.So(req, should.Resemble, tc.ProfileRequest) + a.So(getCalls, should.Equal, tc.GetCalls) + }, + }) + } +} + +func TestMACSettingsProfileRegistryCreate(t *testing.T) { + t.Parallel() + nilProfileAssertion := func(t *testing.T, profile *ttnpb.CreateMACSettingsProfileResponse) bool { + t.Helper() + return assertions.New(t).So(profile, should.BeNil) + } + nilErrorAssertion := func(t *testing.T, err error) bool { + t.Helper() + return assertions.New(t).So(err, should.BeNil) + } + permissionDeniedErrorAssertion := func(t *testing.T, err error) bool { + t.Helper() + return assertions.New(t).So(errors.IsPermissionDenied(err), should.BeTrue) + } + alreadyExistsErrorAssertion := func(t *testing.T, err error) bool { + t.Helper() + return assertions.New(t).So(errors.IsAlreadyExists(err), should.BeTrue) + } + + registeredProfile := &ttnpb.MACSettingsProfile{ + Ids: &ttnpb.MACSettingsProfileIdentifiers{ + ApplicationIds: &ttnpb.ApplicationIdentifiers{ApplicationId: "test-app-id"}, + ProfileId: "test-profile-id", + }, + MacSettings: &ttnpb.MACSettings{ + ResetsFCnt: &ttnpb.BoolValue{Value: true}, + }, + } + + for _, tc := range []struct { + Name string + ContextFunc func(context.Context) context.Context + SetFunc func(ctx context.Context, ids *ttnpb.MACSettingsProfileIdentifiers, paths []string, f func(context.Context, *ttnpb.MACSettingsProfile) (*ttnpb.MACSettingsProfile, []string, error)) (*ttnpb.MACSettingsProfile, error) // nolint: lll + ProfileRequest *ttnpb.CreateMACSettingsProfileRequest + ProfileAssertion func(*testing.T, *ttnpb.CreateMACSettingsProfileResponse) bool + ErrorAssertion func(*testing.T, error) bool + SetCalls uint64 + }{ + { + Name: "Permission denied", + ContextFunc: func(ctx context.Context) context.Context { + return rights.NewContext(ctx, &rights.Rights{ + ApplicationRights: *rights.NewMap(map[string]*ttnpb.Rights{ + unique.ID(test.Context(), &ttnpb.ApplicationIdentifiers{ + ApplicationId: "test-app-id", + }): nil, + }), + }) + }, + SetFunc: func( + ctx context.Context, + _ *ttnpb.MACSettingsProfileIdentifiers, + _ []string, + _ func(context.Context, *ttnpb.MACSettingsProfile) (*ttnpb.MACSettingsProfile, []string, error), + ) (*ttnpb.MACSettingsProfile, error) { + err := errors.New("SetFunc must not be called") + test.MustTFromContext(ctx).Error(err) + return nil, err + }, + ProfileRequest: &ttnpb.CreateMACSettingsProfileRequest{ + MacSettingsProfile: registeredProfile, + }, + ProfileAssertion: nilProfileAssertion, + ErrorAssertion: permissionDeniedErrorAssertion, + SetCalls: 0, + }, + { + Name: "Invalid application ID", + ContextFunc: func(ctx context.Context) context.Context { + return rights.NewContext(ctx, &rights.Rights{ + ApplicationRights: *rights.NewMap(map[string]*ttnpb.Rights{ + unique.ID(test.Context(), &ttnpb.ApplicationIdentifiers{ + ApplicationId: "invalid-application", + }): ttnpb.RightsFrom( + ttnpb.Right_RIGHT_APPLICATION_DEVICES_WRITE, + ), + }), + }) + }, + SetFunc: func( + ctx context.Context, + _ *ttnpb.MACSettingsProfileIdentifiers, + _ []string, + _ func(context.Context, *ttnpb.MACSettingsProfile) (*ttnpb.MACSettingsProfile, []string, error), + ) (*ttnpb.MACSettingsProfile, error) { + err := errors.New("SetFunc must not be called") + test.MustTFromContext(ctx).Error(err) + return nil, err + }, + ProfileRequest: &ttnpb.CreateMACSettingsProfileRequest{ + MacSettingsProfile: registeredProfile, + }, + ProfileAssertion: nilProfileAssertion, + ErrorAssertion: permissionDeniedErrorAssertion, + SetCalls: 0, + }, + { + Name: "Create", + ContextFunc: func(ctx context.Context) context.Context { + return rights.NewContext(ctx, &rights.Rights{ + ApplicationRights: *rights.NewMap(map[string]*ttnpb.Rights{ + unique.ID(test.Context(), &ttnpb.ApplicationIdentifiers{ + ApplicationId: "test-app-id", + }): ttnpb.RightsFrom( + ttnpb.Right_RIGHT_APPLICATION_DEVICES_WRITE, + ), + }), + }) + }, + SetFunc: func( + ctx context.Context, + ids *ttnpb.MACSettingsProfileIdentifiers, + paths []string, + f func(context.Context, *ttnpb.MACSettingsProfile) (*ttnpb.MACSettingsProfile, []string, error), + ) (*ttnpb.MACSettingsProfile, error) { + a := assertions.New(test.MustTFromContext(ctx)) + a.So(ids, should.Resemble, ids) + a.So(paths, should.HaveSameElementsDeep, []string{ + "ids", + "mac_settings", + }) + profile, sets, err := f(ctx, nil) + a.So(sets, should.HaveSameElementsDeep, paths) + a.So(profile, should.Resemble, registeredProfile) + return profile, err + }, + ProfileRequest: &ttnpb.CreateMACSettingsProfileRequest{ + MacSettingsProfile: registeredProfile, + }, + ProfileAssertion: func(t *testing.T, profile *ttnpb.CreateMACSettingsProfileResponse) bool { + t.Helper() + return assertions.New(t).So(profile.MacSettingsProfile, should.Resemble, registeredProfile) + }, + ErrorAssertion: nilErrorAssertion, + SetCalls: 1, + }, + { + Name: "Create already exists", + ContextFunc: func(ctx context.Context) context.Context { + return rights.NewContext(ctx, &rights.Rights{ + ApplicationRights: *rights.NewMap(map[string]*ttnpb.Rights{ + unique.ID(test.Context(), &ttnpb.ApplicationIdentifiers{ + ApplicationId: "test-app-id", + }): ttnpb.RightsFrom( + ttnpb.Right_RIGHT_APPLICATION_DEVICES_WRITE, + ), + }), + }) + }, + SetFunc: func( + ctx context.Context, + ids *ttnpb.MACSettingsProfileIdentifiers, + paths []string, + f func(context.Context, *ttnpb.MACSettingsProfile) (*ttnpb.MACSettingsProfile, []string, error), + ) (*ttnpb.MACSettingsProfile, error) { + a := assertions.New(test.MustTFromContext(ctx)) + a.So(ids, should.Resemble, ids) + a.So(paths, should.HaveSameElementsDeep, []string{ + "ids", + "mac_settings", + }) + profile, sets, err := f(ctx, ttnpb.Clone(registeredProfile)) + a.So(sets, should.HaveSameElementsDeep, []string{}) + a.So(profile, should.BeNil) + return profile, err + }, + ProfileRequest: &ttnpb.CreateMACSettingsProfileRequest{ + MacSettingsProfile: registeredProfile, + }, + ProfileAssertion: nilProfileAssertion, + ErrorAssertion: alreadyExistsErrorAssertion, + SetCalls: 1, + }, + } { + tc := tc + test.RunSubtest(t, test.SubtestConfig{ + Name: tc.Name, + Parallel: true, + Func: func(ctx context.Context, t *testing.T, a *assertions.Assertion) { + t.Helper() + var setCalls uint64 + + ns, ctx, _, stop := StartTest( + ctx, + TestConfig{ + NetworkServer: Config{ + MACSettingsProfileRegistry: &MockMACSettingsProfileRegistry{ + SetFunc: func( + ctx context.Context, + ids *ttnpb.MACSettingsProfileIdentifiers, + paths []string, + f func(context.Context, *ttnpb.MACSettingsProfile) (*ttnpb.MACSettingsProfile, []string, error), + ) (*ttnpb.MACSettingsProfile, error) { + atomic.AddUint64(&setCalls, 1) + return tc.SetFunc(ctx, ids, paths, f) + }, + }, + }, + TaskStarter: StartTaskExclude( + DownlinkProcessTaskName, + DownlinkDispatchTaskName, + ), + }, + ) + defer stop() + + ns.AddContextFiller(tc.ContextFunc) + ns.AddContextFiller(func(ctx context.Context) context.Context { + return test.ContextWithTB(ctx, t) + }) + + req := ttnpb.Clone(tc.ProfileRequest) + + profile, err := ttnpb.NewNsMACSettingsProfileRegistryClient(ns.LoopbackConn()).Create(ctx, req) + if a.So(tc.ErrorAssertion(t, err), should.BeTrue) { + a.So(tc.ProfileAssertion(t, profile), should.BeTrue) + } + a.So(req, should.Resemble, tc.ProfileRequest) + a.So(setCalls, should.Equal, tc.SetCalls) + }, + }) + } +} + +func TestMACSettingsProfileRegistryUpdate(t *testing.T) { + t.Parallel() + nilProfileAssertion := func(t *testing.T, profile *ttnpb.UpdateMACSettingsProfileResponse) bool { + t.Helper() + return assertions.New(t).So(profile, should.BeNil) + } + nilErrorAssertion := func(t *testing.T, err error) bool { + t.Helper() + return assertions.New(t).So(err, should.BeNil) + } + permissionDeniedErrorAssertion := func(t *testing.T, err error) bool { + t.Helper() + return assertions.New(t).So(errors.IsPermissionDenied(err), should.BeTrue) + } + notFoundErrorAssertion := func(t *testing.T, err error) bool { + t.Helper() + return assertions.New(t).So(errors.IsNotFound(err), should.BeTrue) + } + + registeredProfileIDs := &ttnpb.MACSettingsProfileIdentifiers{ + ApplicationIds: &ttnpb.ApplicationIdentifiers{ApplicationId: "test-app-id"}, + ProfileId: "test-profile-id", + } + + registeredProfile := &ttnpb.MACSettingsProfile{ + Ids: &ttnpb.MACSettingsProfileIdentifiers{ + ApplicationIds: &ttnpb.ApplicationIdentifiers{ApplicationId: "test-app-id"}, + ProfileId: "test-profile-id", + }, + MacSettings: &ttnpb.MACSettings{ + ResetsFCnt: &ttnpb.BoolValue{Value: true}, + }, + } + + for _, tc := range []struct { + Name string + ContextFunc func(context.Context) context.Context + SetFunc func(ctx context.Context, ids *ttnpb.MACSettingsProfileIdentifiers, paths []string, f func(context.Context, *ttnpb.MACSettingsProfile) (*ttnpb.MACSettingsProfile, []string, error)) (*ttnpb.MACSettingsProfile, error) // nolint: lll + ProfileRequest *ttnpb.UpdateMACSettingsProfileRequest + ProfileAssertion func(*testing.T, *ttnpb.UpdateMACSettingsProfileResponse) bool + ErrorAssertion func(*testing.T, error) bool + SetCalls uint64 + }{ + { + Name: "Permission denied", + ContextFunc: func(ctx context.Context) context.Context { + return rights.NewContext(ctx, &rights.Rights{ + ApplicationRights: *rights.NewMap(map[string]*ttnpb.Rights{ + unique.ID(test.Context(), &ttnpb.ApplicationIdentifiers{ + ApplicationId: "test-app-id", + }): nil, + }), + }) + }, + SetFunc: func( + ctx context.Context, + _ *ttnpb.MACSettingsProfileIdentifiers, + _ []string, + _ func(context.Context, *ttnpb.MACSettingsProfile) (*ttnpb.MACSettingsProfile, []string, error), + ) (*ttnpb.MACSettingsProfile, error) { + err := errors.New("SetFunc must not be called") + test.MustTFromContext(ctx).Error(err) + return nil, err + }, + ProfileRequest: &ttnpb.UpdateMACSettingsProfileRequest{ + MacSettingsProfileIds: registeredProfileIDs, + MacSettingsProfile: registeredProfile, + FieldMask: ttnpb.FieldMask("mac_settings"), + }, + ProfileAssertion: nilProfileAssertion, + ErrorAssertion: permissionDeniedErrorAssertion, + SetCalls: 0, + }, + { + Name: "Invalid application ID", + ContextFunc: func(ctx context.Context) context.Context { + return rights.NewContext(ctx, &rights.Rights{ + ApplicationRights: *rights.NewMap(map[string]*ttnpb.Rights{ + unique.ID(test.Context(), &ttnpb.ApplicationIdentifiers{ + ApplicationId: "invalid-application", + }): ttnpb.RightsFrom( + ttnpb.Right_RIGHT_APPLICATION_DEVICES_WRITE, + ), + }), + }) + }, + SetFunc: func( + ctx context.Context, + _ *ttnpb.MACSettingsProfileIdentifiers, + _ []string, + _ func(context.Context, *ttnpb.MACSettingsProfile) (*ttnpb.MACSettingsProfile, []string, error), + ) (*ttnpb.MACSettingsProfile, error) { + err := errors.New("SetFunc must not be called") + test.MustTFromContext(ctx).Error(err) + return nil, err + }, + ProfileRequest: &ttnpb.UpdateMACSettingsProfileRequest{ + MacSettingsProfileIds: registeredProfileIDs, + MacSettingsProfile: registeredProfile, + FieldMask: ttnpb.FieldMask("mac_settings"), + }, + ProfileAssertion: nilProfileAssertion, + ErrorAssertion: permissionDeniedErrorAssertion, + SetCalls: 0, + }, + { + Name: "Update", + ContextFunc: func(ctx context.Context) context.Context { + return rights.NewContext(ctx, &rights.Rights{ + ApplicationRights: *rights.NewMap(map[string]*ttnpb.Rights{ + unique.ID(test.Context(), &ttnpb.ApplicationIdentifiers{ + ApplicationId: "test-app-id", + }): ttnpb.RightsFrom( + ttnpb.Right_RIGHT_APPLICATION_DEVICES_WRITE, + ), + }), + }) + }, + SetFunc: func( + ctx context.Context, + ids *ttnpb.MACSettingsProfileIdentifiers, + paths []string, + f func(context.Context, *ttnpb.MACSettingsProfile) (*ttnpb.MACSettingsProfile, []string, error), + ) (*ttnpb.MACSettingsProfile, error) { + a := assertions.New(test.MustTFromContext(ctx)) + a.So(ids, should.Resemble, ids) + a.So(paths, should.HaveSameElementsDeep, []string{ + "mac_settings", + }) + profile, sets, err := f(ctx, ttnpb.Clone(registeredProfile)) + a.So(sets, should.HaveSameElementsDeep, paths) + a.So(profile, should.Resemble, registeredProfile) + return profile, err + }, + ProfileRequest: &ttnpb.UpdateMACSettingsProfileRequest{ + MacSettingsProfileIds: registeredProfileIDs, + MacSettingsProfile: registeredProfile, + FieldMask: ttnpb.FieldMask("mac_settings"), + }, + ProfileAssertion: func(t *testing.T, profile *ttnpb.UpdateMACSettingsProfileResponse) bool { + t.Helper() + return assertions.New(t).So(profile.MacSettingsProfile, should.Resemble, registeredProfile) + }, + ErrorAssertion: nilErrorAssertion, + SetCalls: 1, + }, + { + Name: "Update not found", + ContextFunc: func(ctx context.Context) context.Context { + return rights.NewContext(ctx, &rights.Rights{ + ApplicationRights: *rights.NewMap(map[string]*ttnpb.Rights{ + unique.ID(test.Context(), &ttnpb.ApplicationIdentifiers{ + ApplicationId: "test-app-id", + }): ttnpb.RightsFrom( + ttnpb.Right_RIGHT_APPLICATION_DEVICES_WRITE, + ), + }), + }) + }, + SetFunc: func( + ctx context.Context, + ids *ttnpb.MACSettingsProfileIdentifiers, + paths []string, + f func(context.Context, *ttnpb.MACSettingsProfile) (*ttnpb.MACSettingsProfile, []string, error), + ) (*ttnpb.MACSettingsProfile, error) { + a := assertions.New(test.MustTFromContext(ctx)) + a.So(ids, should.Resemble, ids) + a.So(paths, should.HaveSameElementsDeep, []string{ + "mac_settings", + }) + profile, sets, err := f(ctx, nil) + a.So(sets, should.HaveSameElementsDeep, []string{}) + a.So(profile, should.BeNil) + return profile, err + }, + ProfileRequest: &ttnpb.UpdateMACSettingsProfileRequest{ + MacSettingsProfileIds: registeredProfileIDs, + MacSettingsProfile: registeredProfile, + FieldMask: ttnpb.FieldMask("mac_settings"), + }, + ProfileAssertion: nilProfileAssertion, + ErrorAssertion: notFoundErrorAssertion, + SetCalls: 1, + }, + } { + tc := tc + test.RunSubtest(t, test.SubtestConfig{ + Name: tc.Name, + Parallel: true, + Func: func(ctx context.Context, t *testing.T, a *assertions.Assertion) { + t.Helper() + var setCalls uint64 + + ns, ctx, _, stop := StartTest( + ctx, + TestConfig{ + NetworkServer: Config{ + MACSettingsProfileRegistry: &MockMACSettingsProfileRegistry{ + SetFunc: func( + ctx context.Context, + ids *ttnpb.MACSettingsProfileIdentifiers, + paths []string, + f func(context.Context, *ttnpb.MACSettingsProfile) (*ttnpb.MACSettingsProfile, []string, error), + ) (*ttnpb.MACSettingsProfile, error) { + atomic.AddUint64(&setCalls, 1) + return tc.SetFunc(ctx, ids, paths, f) + }, + }, + }, + TaskStarter: StartTaskExclude( + DownlinkProcessTaskName, + DownlinkDispatchTaskName, + ), + }, + ) + defer stop() + + ns.AddContextFiller(tc.ContextFunc) + ns.AddContextFiller(func(ctx context.Context) context.Context { + return test.ContextWithTB(ctx, t) + }) + + req := ttnpb.Clone(tc.ProfileRequest) + + profile, err := ttnpb.NewNsMACSettingsProfileRegistryClient(ns.LoopbackConn()).Update(ctx, req) + if a.So(tc.ErrorAssertion(t, err), should.BeTrue) { + a.So(tc.ProfileAssertion(t, profile), should.BeTrue) + } + a.So(req, should.Resemble, tc.ProfileRequest) + a.So(setCalls, should.Equal, tc.SetCalls) + }, + }) + } +} + +func TestMACSettingsProfileRegistryDelete(t *testing.T) { + t.Parallel() + nilProfileAssertion := func(t *testing.T, profile *ttnpb.DeleteMACSettingsProfileResponse) bool { + t.Helper() + return assertions.New(t).So(profile, should.BeNil) + } + nilErrorAssertion := func(t *testing.T, err error) bool { + t.Helper() + return assertions.New(t).So(err, should.BeNil) + } + permissionDeniedErrorAssertion := func(t *testing.T, err error) bool { + t.Helper() + return assertions.New(t).So(errors.IsPermissionDenied(err), should.BeTrue) + } + notFoundErrorAssertion := func(t *testing.T, err error) bool { + t.Helper() + return assertions.New(t).So(errors.IsNotFound(err), should.BeTrue) + } + emptyProfileAssertion := func(t *testing.T, profile *ttnpb.DeleteMACSettingsProfileResponse) bool { + t.Helper() + return assertions.New(t).So(profile, should.Resemble, &ttnpb.DeleteMACSettingsProfileResponse{}) + } + + registeredProfileIDs := &ttnpb.MACSettingsProfileIdentifiers{ + ApplicationIds: &ttnpb.ApplicationIdentifiers{ApplicationId: "test-app-id"}, + ProfileId: "test-profile-id", + } + + registeredProfile := &ttnpb.MACSettingsProfile{ + Ids: &ttnpb.MACSettingsProfileIdentifiers{ + ApplicationIds: &ttnpb.ApplicationIdentifiers{ApplicationId: "test-app-id"}, + ProfileId: "test-profile-id", + }, + MacSettings: &ttnpb.MACSettings{ + ResetsFCnt: &ttnpb.BoolValue{Value: true}, + }, + } + + for _, tc := range []struct { + Name string + ContextFunc func(context.Context) context.Context + SetFunc func(ctx context.Context, ids *ttnpb.MACSettingsProfileIdentifiers, paths []string, f func(context.Context, *ttnpb.MACSettingsProfile) (*ttnpb.MACSettingsProfile, []string, error)) (*ttnpb.MACSettingsProfile, error) // nolint: lll + ProfileRequest *ttnpb.DeleteMACSettingsProfileRequest + ProfileAssertion func(*testing.T, *ttnpb.DeleteMACSettingsProfileResponse) bool + ErrorAssertion func(*testing.T, error) bool + SetCalls uint64 + }{ + { + Name: "Permission denied", + ContextFunc: func(ctx context.Context) context.Context { + return rights.NewContext(ctx, &rights.Rights{ + ApplicationRights: *rights.NewMap(map[string]*ttnpb.Rights{ + unique.ID(test.Context(), &ttnpb.ApplicationIdentifiers{ + ApplicationId: "test-app-id", + }): nil, + }), + }) + }, + SetFunc: func( + ctx context.Context, + _ *ttnpb.MACSettingsProfileIdentifiers, + _ []string, + _ func(context.Context, *ttnpb.MACSettingsProfile) (*ttnpb.MACSettingsProfile, []string, error), + ) (*ttnpb.MACSettingsProfile, error) { + err := errors.New("SetFunc must not be called") + test.MustTFromContext(ctx).Error(err) + return nil, err + }, + ProfileRequest: &ttnpb.DeleteMACSettingsProfileRequest{ + MacSettingsProfileIds: registeredProfileIDs, + }, + ProfileAssertion: nilProfileAssertion, + ErrorAssertion: permissionDeniedErrorAssertion, + SetCalls: 0, + }, + { + Name: "Invalid application ID", + ContextFunc: func(ctx context.Context) context.Context { + return rights.NewContext(ctx, &rights.Rights{ + ApplicationRights: *rights.NewMap(map[string]*ttnpb.Rights{ + unique.ID(test.Context(), &ttnpb.ApplicationIdentifiers{ + ApplicationId: "invalid-application", + }): ttnpb.RightsFrom( + ttnpb.Right_RIGHT_APPLICATION_DEVICES_WRITE, + ), + }), + }) + }, + SetFunc: func( + ctx context.Context, + _ *ttnpb.MACSettingsProfileIdentifiers, + _ []string, + _ func(context.Context, *ttnpb.MACSettingsProfile) (*ttnpb.MACSettingsProfile, []string, error), + ) (*ttnpb.MACSettingsProfile, error) { + err := errors.New("SetFunc must not be called") + test.MustTFromContext(ctx).Error(err) + return nil, err + }, + ProfileRequest: &ttnpb.DeleteMACSettingsProfileRequest{ + MacSettingsProfileIds: registeredProfileIDs, + }, + ProfileAssertion: nilProfileAssertion, + ErrorAssertion: permissionDeniedErrorAssertion, + SetCalls: 0, + }, + { + Name: "Delete", + ContextFunc: func(ctx context.Context) context.Context { + return rights.NewContext(ctx, &rights.Rights{ + ApplicationRights: *rights.NewMap(map[string]*ttnpb.Rights{ + unique.ID(test.Context(), &ttnpb.ApplicationIdentifiers{ + ApplicationId: "test-app-id", + }): ttnpb.RightsFrom( + ttnpb.Right_RIGHT_APPLICATION_DEVICES_WRITE, + ), + }), + }) + }, + SetFunc: func( + ctx context.Context, + ids *ttnpb.MACSettingsProfileIdentifiers, + paths []string, + f func(context.Context, *ttnpb.MACSettingsProfile) (*ttnpb.MACSettingsProfile, []string, error), + ) (*ttnpb.MACSettingsProfile, error) { + a := assertions.New(test.MustTFromContext(ctx)) + a.So(ids, should.Resemble, ids) + a.So(paths, should.HaveSameElementsDeep, []string{ + "ids", + "mac_settings", + }) + profile, sets, err := f(ctx, ttnpb.Clone(registeredProfile)) + a.So(sets, should.BeNil) + a.So(profile, should.BeNil) + return profile, err + }, + ProfileRequest: &ttnpb.DeleteMACSettingsProfileRequest{ + MacSettingsProfileIds: registeredProfileIDs, + }, + ProfileAssertion: emptyProfileAssertion, + ErrorAssertion: nilErrorAssertion, + SetCalls: 1, + }, + { + Name: "Delete not found", + ContextFunc: func(ctx context.Context) context.Context { + return rights.NewContext(ctx, &rights.Rights{ + ApplicationRights: *rights.NewMap(map[string]*ttnpb.Rights{ + unique.ID(test.Context(), &ttnpb.ApplicationIdentifiers{ + ApplicationId: "test-app-id", + }): ttnpb.RightsFrom( + ttnpb.Right_RIGHT_APPLICATION_DEVICES_WRITE, + ), + }), + }) + }, + SetFunc: func( + ctx context.Context, + ids *ttnpb.MACSettingsProfileIdentifiers, + paths []string, + f func(context.Context, *ttnpb.MACSettingsProfile) (*ttnpb.MACSettingsProfile, []string, error), + ) (*ttnpb.MACSettingsProfile, error) { + a := assertions.New(test.MustTFromContext(ctx)) + a.So(ids, should.Resemble, ids) + a.So(paths, should.HaveSameElementsDeep, []string{ + "ids", + "mac_settings", + }) + profile, sets, err := f(ctx, nil) + a.So(sets, should.HaveSameElementsDeep, []string{}) + a.So(profile, should.BeNil) + return profile, err + }, + ProfileRequest: &ttnpb.DeleteMACSettingsProfileRequest{ + MacSettingsProfileIds: registeredProfileIDs, + }, + ProfileAssertion: nilProfileAssertion, + ErrorAssertion: notFoundErrorAssertion, + SetCalls: 1, + }, + } { + tc := tc + test.RunSubtest(t, test.SubtestConfig{ + Name: tc.Name, + Parallel: true, + Func: func(ctx context.Context, t *testing.T, a *assertions.Assertion) { + t.Helper() + var setCalls uint64 + + ns, ctx, _, stop := StartTest( + ctx, + TestConfig{ + NetworkServer: Config{ + MACSettingsProfileRegistry: &MockMACSettingsProfileRegistry{ + SetFunc: func( + ctx context.Context, + ids *ttnpb.MACSettingsProfileIdentifiers, + paths []string, + f func(context.Context, *ttnpb.MACSettingsProfile) (*ttnpb.MACSettingsProfile, []string, error), + ) (*ttnpb.MACSettingsProfile, error) { + atomic.AddUint64(&setCalls, 1) + return tc.SetFunc(ctx, ids, paths, f) + }, + }, + }, + TaskStarter: StartTaskExclude( + DownlinkProcessTaskName, + DownlinkDispatchTaskName, + ), + }, + ) + defer stop() + + ns.AddContextFiller(tc.ContextFunc) + ns.AddContextFiller(func(ctx context.Context) context.Context { + return test.ContextWithTB(ctx, t) + }) + + req := ttnpb.Clone(tc.ProfileRequest) + + profile, err := ttnpb.NewNsMACSettingsProfileRegistryClient(ns.LoopbackConn()).Delete(ctx, req) + if a.So(tc.ErrorAssertion(t, err), should.BeTrue) { + a.So(tc.ProfileAssertion(t, profile), should.BeTrue) + } + a.So(req, should.Resemble, tc.ProfileRequest) + a.So(setCalls, should.Equal, tc.SetCalls) + }, + }) + } +} + +func TestMACSettingsProfileRegistryList(t *testing.T) { + t.Parallel() + nilProfileAssertion := func(t *testing.T, profile *ttnpb.ListMACSettingsProfilesResponse) bool { + t.Helper() + return assertions.New(t).So(profile, should.BeNil) + } + nilErrorAssertion := func(t *testing.T, err error) bool { + t.Helper() + return assertions.New(t).So(err, should.BeNil) + } + permissionDeniedErrorAssertion := func(t *testing.T, err error) bool { + t.Helper() + return assertions.New(t).So(errors.IsPermissionDenied(err), should.BeTrue) + } + notFoundErrorAssertion := func(t *testing.T, err error) bool { + t.Helper() + return assertions.New(t).So(errors.IsNotFound(err), should.BeTrue) + } + + registeredProfileIDs := &ttnpb.MACSettingsProfileIdentifiers{ + ApplicationIds: &ttnpb.ApplicationIdentifiers{ApplicationId: "test-app-id"}, + ProfileId: "test-profile-id", + } + + for _, tc := range []struct { + Name string + ContextFunc func(context.Context) context.Context + ListFunc func(context.Context, *ttnpb.ApplicationIdentifiers, []string) ([]*ttnpb.MACSettingsProfile, error) // nolint: lll + PaginationFunc func(context.Context, uint32, uint32, *int64) context.Context + ProfileRequest *ttnpb.ListMACSettingsProfilesRequest + ProfileAssertion func(*testing.T, *ttnpb.ListMACSettingsProfilesResponse) bool + ErrorAssertion func(*testing.T, error) bool + ListCalls uint64 + PaginationCalls uint64 + }{ + { + Name: "Permission denied", + ContextFunc: func(ctx context.Context) context.Context { + return rights.NewContext(ctx, &rights.Rights{ + ApplicationRights: *rights.NewMap(map[string]*ttnpb.Rights{ + unique.ID(test.Context(), &ttnpb.ApplicationIdentifiers{ApplicationId: "test-app-id"}): nil, + }), + }) + }, + ListFunc: func( + ctx context.Context, + _ *ttnpb.ApplicationIdentifiers, + _ []string, + ) ([]*ttnpb.MACSettingsProfile, error) { + err := errors.New("ListFunc must not be called") + test.MustTFromContext(ctx).Error(err) + return nil, err + }, + PaginationFunc: func( + ctx context.Context, + _ uint32, + _ uint32, + _ *int64, + ) context.Context { + err := errors.New("PaginationFunc must not be called") + test.MustTFromContext(ctx).Error(err) + return ctx + }, + ProfileRequest: &ttnpb.ListMACSettingsProfilesRequest{ + ApplicationIds: registeredProfileIDs.ApplicationIds, + FieldMask: ttnpb.FieldMask("mac_settings"), + }, + ProfileAssertion: nilProfileAssertion, + ErrorAssertion: permissionDeniedErrorAssertion, + ListCalls: 0, + PaginationCalls: 0, + }, + { + Name: "Invalid application ID", + ContextFunc: func(ctx context.Context) context.Context { + return rights.NewContext(ctx, &rights.Rights{ + ApplicationRights: *rights.NewMap(map[string]*ttnpb.Rights{ + unique.ID(test.Context(), &ttnpb.ApplicationIdentifiers{ + ApplicationId: "invalid-application", + }): ttnpb.RightsFrom( + ttnpb.Right_RIGHT_APPLICATION_DEVICES_READ, + ), + }), + }) + }, + ListFunc: func( + ctx context.Context, + _ *ttnpb.ApplicationIdentifiers, + _ []string, + ) ([]*ttnpb.MACSettingsProfile, error) { + err := errors.New("ListFunc must not be called") + test.MustTFromContext(ctx).Error(err) + return nil, err + }, + PaginationFunc: func( + ctx context.Context, + _ uint32, + _ uint32, + _ *int64, + ) context.Context { + err := errors.New("PaginationFunc must not be called") + test.MustTFromContext(ctx).Error(err) + return ctx + }, + ProfileRequest: &ttnpb.ListMACSettingsProfilesRequest{ + ApplicationIds: registeredProfileIDs.ApplicationIds, + FieldMask: ttnpb.FieldMask("mac_settings"), + }, + ProfileAssertion: nilProfileAssertion, + ErrorAssertion: permissionDeniedErrorAssertion, + ListCalls: 0, + PaginationCalls: 0, + }, + { + Name: "Not found", + ContextFunc: func(ctx context.Context) context.Context { + return rights.NewContext(ctx, &rights.Rights{ + ApplicationRights: *rights.NewMap(map[string]*ttnpb.Rights{ + unique.ID(test.Context(), &ttnpb.ApplicationIdentifiers{ + ApplicationId: "test-app-id", + }): ttnpb.RightsFrom( + ttnpb.Right_RIGHT_APPLICATION_DEVICES_READ, + ), + }), + }) + }, + ListFunc: func( + ctx context.Context, + ids *ttnpb.ApplicationIdentifiers, + paths []string, + ) ([]*ttnpb.MACSettingsProfile, error) { + a := assertions.New(test.MustTFromContext(ctx)) + a.So(ids, should.Resemble, ids) + a.So(paths, should.HaveSameElementsDeep, []string{ + "mac_settings", + }) + return nil, errNotFound.New() + }, + PaginationFunc: func( + ctx context.Context, + limit uint32, + page uint32, + total *int64, + ) context.Context { + a := assertions.New(test.MustTFromContext(ctx)) + a.So(limit, should.Equal, 0) + a.So(page, should.Equal, 0) + a.So(total, should.NotBeNil) + return ctx + }, + ProfileRequest: &ttnpb.ListMACSettingsProfilesRequest{ + ApplicationIds: registeredProfileIDs.ApplicationIds, + FieldMask: ttnpb.FieldMask("mac_settings"), + }, + ProfileAssertion: nilProfileAssertion, + ErrorAssertion: notFoundErrorAssertion, + ListCalls: 1, + PaginationCalls: 1, + }, + { + Name: "Found", + ContextFunc: func(ctx context.Context) context.Context { + return rights.NewContext(ctx, &rights.Rights{ + ApplicationRights: *rights.NewMap(map[string]*ttnpb.Rights{ + unique.ID(test.Context(), &ttnpb.ApplicationIdentifiers{ + ApplicationId: "test-app-id", + }): ttnpb.RightsFrom( + ttnpb.Right_RIGHT_APPLICATION_DEVICES_READ, + ), + }), + }) + }, + ListFunc: func( + ctx context.Context, + ids *ttnpb.ApplicationIdentifiers, + paths []string, + ) ([]*ttnpb.MACSettingsProfile, error) { + a := assertions.New(test.MustTFromContext(ctx)) + a.So(ids, should.Resemble, ids) + a.So(paths, should.HaveSameElementsDeep, []string{ + "ids", + "mac_settings", + }) + return []*ttnpb.MACSettingsProfile{ttnpb.Clone(&ttnpb.MACSettingsProfile{ + Ids: registeredProfileIDs, + MacSettings: &ttnpb.MACSettings{ + ResetsFCnt: &ttnpb.BoolValue{Value: true}, + }, + })}, nil + }, + PaginationFunc: func( + ctx context.Context, + limit uint32, + page uint32, + total *int64, + ) context.Context { + a := assertions.New(test.MustTFromContext(ctx)) + a.So(limit, should.Equal, 2) + a.So(page, should.Equal, 1) + a.So(total, should.NotBeNil) + return ctx + }, + ProfileRequest: &ttnpb.ListMACSettingsProfilesRequest{ + ApplicationIds: registeredProfileIDs.ApplicationIds, + FieldMask: ttnpb.FieldMask("ids", "mac_settings"), + Limit: 2, + Page: 1, + }, + ProfileAssertion: func(t *testing.T, profile *ttnpb.ListMACSettingsProfilesResponse) bool { + t.Helper() + a := assertions.New(t) + a.So(profile, should.NotBeNil) + a.So(profile.MacSettingsProfiles, should.HaveLength, 1) + return a.So(profile.MacSettingsProfiles, should.Resemble, []*ttnpb.MACSettingsProfile{{ + Ids: &ttnpb.MACSettingsProfileIdentifiers{ + ApplicationIds: &ttnpb.ApplicationIdentifiers{ + ApplicationId: "test-app-id", + }, + ProfileId: "test-profile-id", + }, + MacSettings: &ttnpb.MACSettings{ + ResetsFCnt: &ttnpb.BoolValue{Value: true}, + }, + }}) + }, + ErrorAssertion: nilErrorAssertion, + ListCalls: 1, + PaginationCalls: 1, + }, + } { + tc := tc + test.RunSubtest(t, test.SubtestConfig{ + Name: tc.Name, + Parallel: true, + Func: func(ctx context.Context, t *testing.T, a *assertions.Assertion) { + t.Helper() + var listCalls, paginationCalls uint64 + + ns, ctx, _, stop := StartTest( + ctx, + TestConfig{ + NetworkServer: Config{ + MACSettingsProfileRegistry: &MockMACSettingsProfileRegistry{ + ListFunc: func( + ctx context.Context, + ids *ttnpb.ApplicationIdentifiers, + paths []string, + ) ([]*ttnpb.MACSettingsProfile, error) { + atomic.AddUint64(&listCalls, 1) + return tc.ListFunc(ctx, ids, paths) + }, + WithPaginationFunc: func( + ctx context.Context, + limit uint32, + page uint32, + total *int64, + ) context.Context { + atomic.AddUint64(&paginationCalls, 1) + return tc.PaginationFunc(ctx, limit, page, total) + }, + }, + }, + TaskStarter: StartTaskExclude( + DownlinkProcessTaskName, + DownlinkDispatchTaskName, + ), + }, + ) + defer stop() + + ns.AddContextFiller(tc.ContextFunc) + ns.AddContextFiller(func(ctx context.Context) context.Context { + return test.ContextWithTB(ctx, t) + }) + + req := ttnpb.Clone(tc.ProfileRequest) + + profile, err := ttnpb.NewNsMACSettingsProfileRegistryClient(ns.LoopbackConn()).List(ctx, req) + if a.So(tc.ErrorAssertion(t, err), should.BeTrue) { + a.So(tc.ProfileAssertion(t, profile), should.BeTrue) + } + a.So(req, should.Resemble, tc.ProfileRequest) + a.So(listCalls, should.Equal, tc.ListCalls) + a.So(paginationCalls, should.Equal, tc.PaginationCalls) + }, + }) + } +} diff --git a/pkg/networkserver/internal/test/shared/redis.go b/pkg/networkserver/internal/test/shared/redis.go index 5ffccf960a..e0158954ab 100644 --- a/pkg/networkserver/internal/test/shared/redis.go +++ b/pkg/networkserver/internal/test/shared/redis.go @@ -105,3 +105,21 @@ func NewRedisScheduledDownlinkMatcher(ctx context.Context) (ScheduledDownlinkMat cl.Close() } } + +// NewRedisMACSettingsProfileRegistry initialize a new test Redis for MAC Settings Profile Registry. +func NewRedisMACSettingsProfileRegistry(ctx context.Context) (MACSettingsProfileRegistry, func()) { + tb := test.MustTBFromContext(ctx) + cl, flush := test.NewRedis(ctx, append(redisNamespace[:], "mac-settings-profile")...) + reg := &redis.MACSettingsProfileRegistry{ + Redis: cl, + LockTTL: test.Delay << 10, + } + if err := reg.Init(ctx); err != nil { + tb.Fatalf("Failed to initialize Redis MAC Settings Profile Registry: %s", test.FormatError(err)) + } + return reg, + func() { + flush() + cl.Close() + } +} diff --git a/pkg/networkserver/networkserver.go b/pkg/networkserver/networkserver.go index 0df37cf2ff..178d37d4e1 100644 --- a/pkg/networkserver/networkserver.go +++ b/pkg/networkserver/networkserver.go @@ -161,6 +161,7 @@ type NetworkServer struct { batchDevices ttnpb.NsEndDeviceBatchRegistryServer relayConfiguration ttnpb.NsRelayConfigurationServiceServer + macSettingsProfile ttnpb.NsMACSettingsProfileRegistryServer netID netIDFunc nsID nsIDFunc @@ -238,6 +239,8 @@ func New(c *component.Component, conf *Config, opts ...Option) (*NetworkServer, return nil, errInvalidConfiguration.WithCause(errors.New("Downlink queue capacity must be greater than or equal to 0")) case conf.DownlinkQueueCapacity > maxInt/2: return nil, errInvalidConfiguration.WithCause(errors.New(fmt.Sprintf("Downlink queue capacity must be below %d", maxInt/2))) + case conf.MACSettingsProfileRegistry == nil: + panic(errInvalidConfiguration.WithCause(errors.New("MACSettingsProfileRegistry is not specified"))) } devAddrPrefixes := conf.DevAddrPrefixes @@ -288,6 +291,7 @@ func New(c *component.Component, conf *Config, opts ...Option) (*NetworkServer, devices: wrapEndDeviceRegistryWithReplacedFields(conf.Devices, replacedEndDeviceFields...), batchDevices: &nsEndDeviceBatchRegistry{devices: conf.Devices}, relayConfiguration: &nsRelayConfigurationService{devices: conf.Devices, frequencyPlans: c.FrequencyPlansStore}, + macSettingsProfile: &NsMACSettingsProfileRegistry{registry: conf.MACSettingsProfileRegistry}, downlinkTasks: conf.DownlinkTaskQueue.Queue, downlinkPriorities: downlinkPriorities, defaultMACSettings: defaultMACSettings, @@ -328,6 +332,7 @@ func New(c *component.Component, conf *Config, opts ...Option) (*NetworkServer, "/ttn.lorawan.v3.NsEndDeviceBatchRegistry", "/ttn.lorawan.v3.Ns", "/ttn.lorawan.v3.RelayConfigurationService", + "/ttn.lorawan.v3.NsMACSettingsProfileRegistry", } { c.GRPC.RegisterUnaryHook(filter, hook.name, hook.middleware) } @@ -398,6 +403,7 @@ func (ns *NetworkServer) RegisterServices(s *grpc.Server) { ttnpb.RegisterNsEndDeviceBatchRegistryServer(s, ns.batchDevices) ttnpb.RegisterNsServer(s, ns) ttnpb.RegisterNsRelayConfigurationServiceServer(s, ns.relayConfiguration) + ttnpb.RegisterNsMACSettingsProfileRegistryServer(s, ns.macSettingsProfile) } // RegisterHandlers registers gRPC handlers. @@ -405,7 +411,8 @@ func (ns *NetworkServer) RegisterHandlers(s *runtime.ServeMux, conn *grpc.Client ttnpb.RegisterNsEndDeviceRegistryHandler(ns.Context(), s, conn) ttnpb.RegisterNsEndDeviceBatchRegistryHandler(ns.Context(), s, conn) // nolint:errcheck ttnpb.RegisterNsHandler(ns.Context(), s, conn) - ttnpb.RegisterNsRelayConfigurationServiceHandler(ns.Context(), s, conn) // nolint:errcheck + ttnpb.RegisterNsRelayConfigurationServiceHandler(ns.Context(), s, conn) // nolint:errcheck + ttnpb.RegisterNsMACSettingsProfileRegistryHandler(ns.Context(), s, conn) // nolint:errcheck } // Roles returns the roles that the Network Server fulfills. diff --git a/pkg/networkserver/networkserver_util_internal_test.go b/pkg/networkserver/networkserver_util_internal_test.go index dd7f1dc7cc..fa7d0a78e3 100644 --- a/pkg/networkserver/networkserver_util_internal_test.go +++ b/pkg/networkserver/networkserver_util_internal_test.go @@ -103,11 +103,12 @@ var ( EvtScheduleJoinAcceptSuccess = evtScheduleJoinAcceptSuccess EvtUpdateEndDevice = evtUpdateEndDevice - NewDeviceRegistry func(context.Context) (DeviceRegistry, func()) - NewApplicationUplinkQueue func(context.Context) (ApplicationUplinkQueue, func()) - NewDownlinkTaskQueue func(context.Context) (DownlinkTaskQueue, func()) - NewUplinkDeduplicator func(context.Context) (UplinkDeduplicator, func()) - NewScheduledDownlinkMatcher func(context.Context) (ScheduledDownlinkMatcher, func()) + NewDeviceRegistry func(context.Context) (DeviceRegistry, func()) + NewApplicationUplinkQueue func(context.Context) (ApplicationUplinkQueue, func()) + NewDownlinkTaskQueue func(context.Context) (DownlinkTaskQueue, func()) + NewUplinkDeduplicator func(context.Context) (UplinkDeduplicator, func()) + NewScheduledDownlinkMatcher func(context.Context) (ScheduledDownlinkMatcher, func()) + NewMACSettingsProfileRegistry func(context.Context) (MACSettingsProfileRegistry, func()) ) type DownlinkPath = downlinkPath @@ -2103,6 +2104,13 @@ func StartTest(ctx context.Context, conf TestConfig) (*NetworkServer, context.Co } conf.NetworkServer.ScheduledDownlinkMatcher = v } + if conf.NetworkServer.MACSettingsProfileRegistry == nil { + v, closeFn := NewMACSettingsProfileRegistry(ctx) + if closeFn != nil { + closeFuncs = append(closeFuncs, closeFn) + } + conf.NetworkServer.MACSettingsProfileRegistry = v + } ns := test.Must(New( componenttest.NewComponent(tb, &conf.Component, cmpOpts...), @@ -2588,3 +2596,74 @@ func (m MockDeviceRegistry) BatchDelete( } return m.BatchDeleteFunc(ctx, appIDs, deviceIDs) } + +type MockMACSettingsProfileRegistry struct { + GetFunc func( + ctx context.Context, + ids *ttnpb.MACSettingsProfileIdentifiers, + paths []string, + ) (*ttnpb.MACSettingsProfile, error) + SetFunc func( + ctx context.Context, + ids *ttnpb.MACSettingsProfileIdentifiers, + paths []string, + f func(context.Context, *ttnpb.MACSettingsProfile) (*ttnpb.MACSettingsProfile, []string, error), + ) (*ttnpb.MACSettingsProfile, error) + ListFunc func( + ctx context.Context, + ids *ttnpb.ApplicationIdentifiers, + paths []string, + ) ([]*ttnpb.MACSettingsProfile, error) + WithPaginationFunc func( + ctx context.Context, + limit uint32, + page uint32, + total *int64, + ) context.Context +} + +func (m MockMACSettingsProfileRegistry) Get( + ctx context.Context, + ids *ttnpb.MACSettingsProfileIdentifiers, + paths []string, +) (*ttnpb.MACSettingsProfile, error) { + if m.GetFunc == nil { + panic("GetFunc not set") + } + return m.GetFunc(ctx, ids, paths) +} + +func (m MockMACSettingsProfileRegistry) Set( + ctx context.Context, + ids *ttnpb.MACSettingsProfileIdentifiers, + paths []string, + f func(context.Context, *ttnpb.MACSettingsProfile) (*ttnpb.MACSettingsProfile, []string, error), +) (*ttnpb.MACSettingsProfile, error) { + if m.SetFunc == nil { + panic("SetFunc not set") + } + return m.SetFunc(ctx, ids, paths, f) +} + +func (m MockMACSettingsProfileRegistry) List( + ctx context.Context, + ids *ttnpb.ApplicationIdentifiers, + paths []string, +) ([]*ttnpb.MACSettingsProfile, error) { + if m.ListFunc == nil { + panic("ListFunc not set") + } + return m.ListFunc(ctx, ids, paths) +} + +func (m MockMACSettingsProfileRegistry) WithPagination( + ctx context.Context, + limit uint32, + page uint32, + total *int64, +) context.Context { + if m.WithPaginationFunc == nil { + panic("WithPaginationFunc not set") + } + return m.WithPaginationFunc(ctx, limit, page, total) +} diff --git a/pkg/networkserver/networkserver_util_test.go b/pkg/networkserver/networkserver_util_test.go index 210019656f..ae2d14c307 100644 --- a/pkg/networkserver/networkserver_util_test.go +++ b/pkg/networkserver/networkserver_util_test.go @@ -25,4 +25,5 @@ func init() { NewDownlinkTaskQueue = NewRedisDownlinkTaskQueue NewUplinkDeduplicator = NewRedisUplinkDeduplicator NewScheduledDownlinkMatcher = NewRedisScheduledDownlinkMatcher + NewMACSettingsProfileRegistry = NewRedisMACSettingsProfileRegistry } diff --git a/pkg/networkserver/redis/mac_settings_profile.go b/pkg/networkserver/redis/mac_settings_profile.go new file mode 100644 index 0000000000..3907999052 --- /dev/null +++ b/pkg/networkserver/redis/mac_settings_profile.go @@ -0,0 +1,296 @@ +// Copyright © 2024 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 implements a Redis-backed MAC settings profile registry. +package redis + +import ( + "context" + "runtime/trace" + + "github.com/redis/go-redis/v9" + "go.thethings.network/lorawan-stack/v3/pkg/errors" + "go.thethings.network/lorawan-stack/v3/pkg/networkserver/internal/time" + 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" + "google.golang.org/protobuf/proto" +) + +// MACSettingsProfileRegistry is an implementation of networkserver.MACSettingsProfileRegistry. +type MACSettingsProfileRegistry struct { + Redis *ttnredis.Client + LockTTL time.Duration +} + +func applyMACSettingsProfileFieldMask(dst, src *ttnpb.MACSettingsProfile, paths ...string, +) (*ttnpb.MACSettingsProfile, error) { + if dst == nil { + dst = &ttnpb.MACSettingsProfile{} + } + return dst, dst.SetFields(src, paths...) +} + +func (r *MACSettingsProfileRegistry) appKey(uid string) string { + return r.Redis.Key("uid", uid) +} + +func (r *MACSettingsProfileRegistry) profileKey(appUID string, id string) string { + return r.Redis.Key("uid", appUID, id) +} + +func (r *MACSettingsProfileRegistry) makeProfileKeyFunc(appUID string) func(string) string { + return func(id string) string { + return r.profileKey(appUID, id) + } +} + +// Init initializes the MAC settings profile registry. +func (r *MACSettingsProfileRegistry) Init(ctx context.Context) error { + return ttnredis.InitMutex(ctx, r.Redis) +} + +// Get gets the MAC settings profile by identifiers. +func (r *MACSettingsProfileRegistry) Get( + ctx context.Context, + ids *ttnpb.MACSettingsProfileIdentifiers, + paths []string, +) (*ttnpb.MACSettingsProfile, error) { + defer trace.StartRegion(ctx, "get mac settings profile").End() + + if err := ids.ValidateContext(ctx); err != nil { + return nil, err + } + + pb := &ttnpb.MACSettingsProfile{} + appUID := unique.ID(ctx, ids.ApplicationIds) + if err := ttnredis.GetProto(ctx, r.Redis, r.profileKey(appUID, ids.ProfileId)).ScanProto(pb); err != nil { + return nil, err + } + pb, err := applyMACSettingsProfileFieldMask(nil, pb, paths...) + if err != nil { + return nil, err + } + return pb, nil +} + +// Set sets the MAC settings profile by identifiers. +func (r *MACSettingsProfileRegistry) Set( //nolint:gocyclo + ctx context.Context, + ids *ttnpb.MACSettingsProfileIdentifiers, + paths []string, + f func(context.Context, *ttnpb.MACSettingsProfile) (*ttnpb.MACSettingsProfile, []string, error), +) (*ttnpb.MACSettingsProfile, error) { + defer trace.StartRegion(ctx, "set mac settings profile").End() + + if err := ids.ValidateContext(ctx); err != nil { + return nil, err + } + + appUID := unique.ID(ctx, ids.ApplicationIds) + pk := r.profileKey(appUID, ids.ProfileId) + + lockerID, err := ttnredis.GenerateLockerID() + if err != nil { + return nil, err + } + + var pb *ttnpb.MACSettingsProfile + err = ttnredis.LockedWatch(ctx, r.Redis, pk, lockerID, r.LockTTL, func(tx *redis.Tx) error { + cmd := ttnredis.GetProto(ctx, tx, pk) + stored := &ttnpb.MACSettingsProfile{} + if err := cmd.ScanProto(stored); errors.IsNotFound(err) { + stored = nil + } else if err != nil { + return err + } + + var err error + if stored != nil { + pb = &ttnpb.MACSettingsProfile{} + if err := cmd.ScanProto(pb); err != nil { + return err + } + pb, err = applyMACSettingsProfileFieldMask(nil, pb, paths...) + if err != nil { + return err + } + } + + var sets []string + pb, sets, err = f(ctx, pb) + if err != nil { + return err + } + if stored == nil && pb == nil { + return nil + } + if pb != nil && len(sets) == 0 { + pb, err = applyMACSettingsProfileFieldMask(nil, stored, paths...) + return err + } + + var pipelined func(redis.Pipeliner) error + if pb == nil && len(sets) == 0 { + pipelined = func(p redis.Pipeliner) error { + p.Del(ctx, pk) + p.SRem(ctx, r.appKey(appUID), stored.Ids.ProfileId) + return nil + } + } else { + if pb == nil { + pb = &ttnpb.MACSettingsProfile{} + } + + if pb.Ids.ApplicationIds.ApplicationId != ids.ApplicationIds.ApplicationId || pb.Ids.ProfileId != ids.ProfileId { + return errInvalidIdentifiers.New() + } + updated := &ttnpb.MACSettingsProfile{} + if stored == nil { + if err := ttnpb.RequireFields(sets, + "ids.application_ids", + "ids.profile_id", + ); err != nil { + return errInvalidFieldmask.WithCause(err) + } + updated, err = applyMACSettingsProfileFieldMask(updated, pb, sets...) + if err != nil { + return err + } + if updated.Ids.ApplicationIds.ApplicationId != ids.ApplicationIds.ApplicationId || + updated.Ids.ProfileId != ids.ProfileId { + return errInvalidIdentifiers.New() + } + } else { + if ttnpb.HasAnyField(sets, "ids.application_ids.application_id") && + pb.Ids.ApplicationIds.ApplicationId != stored.Ids.ApplicationIds.ApplicationId { + return errReadOnlyField.WithAttributes("field", "ids.application_ids.application_id") + } + if ttnpb.HasAnyField(sets, "ids.profile_id") && pb.Ids.ProfileId != stored.Ids.ProfileId { + return errReadOnlyField.WithAttributes("field", "ids.profile_id") + } + if err := cmd.ScanProto(updated); err != nil { + return err + } + updated, err = applyMACSettingsProfileFieldMask(updated, pb, sets...) + if err != nil { + return err + } + } + if err := updated.ValidateFields(); err != nil { + return err + } + pipelined = func(p redis.Pipeliner) error { + if _, err := ttnredis.SetProto(ctx, p, pk, updated, 0); err != nil { + return err + } + p.SAdd(ctx, r.appKey(appUID), updated.Ids.ProfileId) + return nil + } + pb, err = applyMACSettingsProfileFieldMask(nil, updated, paths...) + if err != nil { + return err + } + } + _, err = tx.TxPipelined(ctx, pipelined) + if err != nil { + return err + } + return nil + }) + if err != nil { + return nil, ttnredis.ConvertError(err) + } + + return pb, nil +} + +// List lists MAC settings profiles by application identifiers. +func (r *MACSettingsProfileRegistry) List( + ctx context.Context, + ids *ttnpb.ApplicationIdentifiers, + paths []string, +) ([]*ttnpb.MACSettingsProfile, error) { + defer trace.StartRegion(ctx, "list mac settings profile").End() + + if err := ids.ValidateContext(ctx); err != nil { + return nil, err + } + + var pbs []*ttnpb.MACSettingsProfile + appUID := unique.ID(ctx, ids) + uidKey := r.appKey(appUID) + + opts := []ttnredis.FindProtosOption{} + limit, offset := ttnredis.PaginationLimitAndOffsetFromContext(ctx) + if limit != 0 { + opts = append(opts, + ttnredis.FindProtosSorted(true), + ttnredis.FindProtosWithOffsetAndCount(offset, limit), + ) + } + + rangeProtos := func(c redis.Cmdable) error { + return ttnredis.FindProtos( + ctx, + c, + uidKey, + r.makeProfileKeyFunc(appUID), + opts..., + ).Range(func() (proto.Message, func() (bool, error)) { + pb := &ttnpb.MACSettingsProfile{} + return pb, func() (bool, error) { + pb, err := applyMACSettingsProfileFieldMask(nil, pb, paths...) + if err != nil { + return false, err + } + pbs = append(pbs, pb) + return true, nil + } + }) + } + + var err error + if limit != 0 { + var lockerID string + lockerID, err = ttnredis.GenerateLockerID() + if err != nil { + return nil, err + } + err = ttnredis.LockedWatch(ctx, r.Redis, uidKey, lockerID, r.LockTTL, func(tx *redis.Tx) (err error) { + total, err := tx.SCard(ctx, uidKey).Result() + if err != nil { + return err + } + ttnredis.SetPaginationTotal(ctx, total) + return rangeProtos(tx) + }) + } else { + err = rangeProtos(r.Redis) + } + if err != nil { + return nil, ttnredis.ConvertError(err) + } + return pbs, nil +} + +// WithPagination returns a new context with pagination parameters. +func (*MACSettingsProfileRegistry) WithPagination( + ctx context.Context, + limit uint32, + page uint32, + total *int64, +) context.Context { + return ttnredis.NewContextWithPagination(ctx, int64(limit), int64(page), total) +} diff --git a/pkg/networkserver/redis/mac_settings_profile_test.go b/pkg/networkserver/redis/mac_settings_profile_test.go new file mode 100644 index 0000000000..9e5696d0c0 --- /dev/null +++ b/pkg/networkserver/redis/mac_settings_profile_test.go @@ -0,0 +1,288 @@ +// Copyright © 2024 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 implements a Redis-backed MAC settings profile registry. +package redis + +import ( + "context" + "fmt" + "testing" + + "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/util/test" + "go.thethings.network/lorawan-stack/v3/pkg/util/test/assertions/should" +) + +var Timeout = 10 * test.Delay + +func TestMACSettingsProfileRegistry(t *testing.T) { + t.Parallel() + a, ctx := test.New(t) + cl, flush := test.NewRedis(ctx, "redis_test") + t.Cleanup(func() { + flush() + cl.Close() + }) + + ids := &ttnpb.MACSettingsProfileIdentifiers{ + ApplicationIds: &ttnpb.ApplicationIdentifiers{ + ApplicationId: "myapp-00", + }, + ProfileId: "prof-00", + } + ids1 := &ttnpb.MACSettingsProfileIdentifiers{ + ApplicationIds: &ttnpb.ApplicationIdentifiers{ + ApplicationId: "myapp-01", + }, + ProfileId: "prof-01", + } + ids2 := &ttnpb.MACSettingsProfileIdentifiers{ + ApplicationIds: &ttnpb.ApplicationIdentifiers{ + ApplicationId: "myapp-02", + }, + ProfileId: "prof-02", + } + + frequencies := []uint64{868100000, 868300000, 868500000} + frequencies2 := []uint64{868100000, 868300000, 868500000, 868700000} + + registry := &MACSettingsProfileRegistry{ + Redis: cl, + LockTTL: test.Delay << 10, + } + if err := registry.Init(ctx); !a.So(err, should.BeNil) { + t.FailNow() + } + + createProfileFunc := func(_ context.Context, pb *ttnpb.MACSettingsProfile, + ) (*ttnpb.MACSettingsProfile, []string, error) { // nolint: unparam + a.So(pb, should.BeNil) + return &ttnpb.MACSettingsProfile{ + Ids: ids1, + MacSettings: &ttnpb.MACSettings{ + ResetsFCnt: &ttnpb.BoolValue{Value: true}, + }, + }, []string{"ids", "mac_settings"}, nil + } + + updateProfileFunc := func(_ context.Context, pb *ttnpb.MACSettingsProfile, + ) (*ttnpb.MACSettingsProfile, []string, error) { // nolint: unparam + a.So(pb, should.NotBeNil) + return &ttnpb.MACSettingsProfile{ + Ids: ids1, + MacSettings: &ttnpb.MACSettings{ + ResetsFCnt: &ttnpb.BoolValue{Value: false}, + FactoryPresetFrequencies: frequencies, + }, + }, []string{"ids", "mac_settings"}, nil + } + + updateFieldMaskProfileFunc := func(_ context.Context, pb *ttnpb.MACSettingsProfile, + ) (*ttnpb.MACSettingsProfile, []string, error) { // nolint: unparam + a.So(pb, should.NotBeNil) + return &ttnpb.MACSettingsProfile{ + Ids: ids1, + MacSettings: &ttnpb.MACSettings{ + ResetsFCnt: &ttnpb.BoolValue{Value: true}, + FactoryPresetFrequencies: frequencies2, + }, + }, []string{"ids", "mac_settings.factory_preset_frequencies"}, nil + } + + deleteProfileFunc := func(_ context.Context, pb *ttnpb.MACSettingsProfile, + ) (*ttnpb.MACSettingsProfile, []string, error) { // nolint: unparam + a.So(pb, should.NotBeNil) + return nil, nil, nil + } + + listProfileFunc := func(_ context.Context, pb *ttnpb.MACSettingsProfile, + ) (*ttnpb.MACSettingsProfile, []string, error) { // nolint: unparam + a.So(pb, should.BeNil) + return &ttnpb.MACSettingsProfile{ + Ids: ids2, + MacSettings: &ttnpb.MACSettings{ + ResetsFCnt: &ttnpb.BoolValue{Value: true}, + }, + }, []string{"ids", "mac_settings"}, nil + } + + t.Run("GetNonExisting", func(t *testing.T) { + t.Parallel() + a, ctx := test.New(t) + profile, err := registry.Get(ctx, ids, []string{"ids"}) + a.So(profile, should.BeNil) + a.So(errors.IsNotFound(err), should.BeTrue) + }) + + t.Run("CreateReadUpdateDelete", func(t *testing.T) { + t.Parallel() + a, ctx := test.New(t) + profile, err := registry.Set(ctx, ids1, []string{"ids", "mac_settings"}, createProfileFunc) + a.So(err, should.BeNil) + a.So(profile, should.NotBeNil) + a.So(profile.Ids, should.Resemble, ids1) + a.So(profile.MacSettings, should.NotBeNil) + a.So(profile.MacSettings.ResetsFCnt, should.NotBeNil) + a.So(profile.MacSettings.ResetsFCnt.Value, should.BeTrue) + + retrieved, err := registry.Get(ctx, ids1, []string{"ids", "mac_settings"}) + a.So(err, should.BeNil) + a.So(retrieved, should.NotBeNil) + a.So(retrieved.Ids, should.Resemble, ids1) + a.So(retrieved.MacSettings, should.NotBeNil) + a.So(retrieved.MacSettings.ResetsFCnt, should.NotBeNil) + a.So(retrieved.MacSettings.ResetsFCnt.Value, should.BeTrue) + + updated, err := registry.Set(ctx, ids1, []string{"ids", "mac_settings"}, updateProfileFunc) + a.So(err, should.BeNil) + a.So(updated, should.NotBeNil) + a.So(updated.Ids, should.Resemble, ids1) + a.So(updated.MacSettings, should.NotBeNil) + a.So(updated.MacSettings.ResetsFCnt, should.NotBeNil) + a.So(updated.MacSettings.ResetsFCnt.Value, should.BeFalse) + a.So(updated.MacSettings.FactoryPresetFrequencies, should.Resemble, frequencies) + + updated2, err := registry.Set(ctx, ids1, []string{"ids", "mac_settings"}, updateFieldMaskProfileFunc) + a.So(err, should.BeNil) + a.So(updated2, should.NotBeNil) + a.So(updated2.Ids, should.Resemble, ids1) + a.So(updated2.MacSettings, should.NotBeNil) + a.So(updated2.MacSettings.ResetsFCnt, should.NotBeNil) + a.So(updated2.MacSettings.ResetsFCnt.Value, should.BeFalse) + a.So(updated2.MacSettings.FactoryPresetFrequencies, should.Resemble, frequencies2) + + deleted, err := registry.Set(ctx, ids1, []string{"ids", "mac_settings"}, deleteProfileFunc) + a.So(err, should.BeNil) + a.So(deleted, should.BeNil) + }) + + t.Run("List", func(t *testing.T) { + t.Parallel() + a, ctx := test.New(t) + profile, err := registry.Set(ctx, ids2, []string{"ids", "mac_settings"}, listProfileFunc) + a.So(err, should.BeNil) + a.So(profile, should.NotBeNil) + a.So(profile.Ids, should.Resemble, ids2) + a.So(profile.MacSettings, should.NotBeNil) + a.So(profile.MacSettings.ResetsFCnt, should.NotBeNil) + a.So(profile.MacSettings.ResetsFCnt.Value, should.BeTrue) + + profiles, err := registry.List(ctx, ids2.ApplicationIds, []string{"ids", "mac_settings"}) + a.So(err, should.BeNil) + a.So(profiles, should.HaveLength, 1) + a.So(profiles[0], should.NotBeNil) + a.So(profiles[0].Ids, should.Resemble, ids2) + a.So(profiles[0].MacSettings, should.NotBeNil) + a.So(profiles[0].MacSettings.ResetsFCnt, should.NotBeNil) + a.So(profiles[0].MacSettings.ResetsFCnt.Value, should.BeTrue) + }) + + t.Run("Pagination", func(t *testing.T) { + t.Parallel() + a, ctx := test.New(t) + + ttnredis.SetPaginationDefaults(ttnredis.PaginationDefaults{DefaultLimit: 10}) + + for i := 1; i < 21; i++ { + ids3 := &ttnpb.MACSettingsProfileIdentifiers{ + ApplicationIds: &ttnpb.ApplicationIdentifiers{ + ApplicationId: "myapp-pagination", + }, + ProfileId: fmt.Sprintf("listprof-%02d", i), + } + + profile, err := registry.Set( + ctx, + ids3, + []string{"ids", "mac_settings"}, + func(_ context.Context, pb *ttnpb.MACSettingsProfile, + ) (*ttnpb.MACSettingsProfile, []string, error) { // nolint: unparam + a.So(pb, should.BeNil) + return &ttnpb.MACSettingsProfile{ + Ids: ids3, + MacSettings: &ttnpb.MACSettings{ + ResetsFCnt: &ttnpb.BoolValue{Value: true}, + }, + }, []string{"ids", "mac_settings"}, nil + }, + ) + a.So(err, should.BeNil) + a.So(profile, should.NotBeNil) + } + + for _, tc := range []struct { + limit uint32 + page uint32 + idLow string + idHigh string + length int + }{ + { + limit: 10, + page: 0, + idLow: "listprof-01", + idHigh: "listprof-10", + length: 10, + }, + { + limit: 10, + page: 1, + idLow: "listprof-01", + idHigh: "listprof-10", + length: 10, + }, + { + limit: 10, + page: 2, + idLow: "listprof-11", + idHigh: "listprof-20", + length: 10, + }, + { + limit: 10, + page: 3, + length: 0, + }, + { + limit: 0, + page: 0, + idLow: "listprof-01", + idHigh: "listprof-10", + length: 10, + }, + } { + t.Run(fmt.Sprintf("limit:%v_page:%v", tc.limit, tc.page), + func(t *testing.T) { + t.Parallel() + var total int64 + paginationCtx := registry.WithPagination(ctx, tc.limit, tc.page, &total) + + profiles, err := registry.List(paginationCtx, &ttnpb.ApplicationIdentifiers{ + ApplicationId: "myapp-pagination", + }, + []string{"ids", "mac_settings"}, + ) + a.So(err, should.BeNil) + a.So(profiles, should.HaveLength, tc.length) + a.So(total, should.Equal, 20) + for _, profile := range profiles { + a.So(profile.Ids.ProfileId, should.BeBetweenOrEqual, tc.idLow, tc.idHigh) + } + }) + } + }) +} diff --git a/pkg/networkserver/registry.go b/pkg/networkserver/registry.go index e76eedfc77..0f10b25734 100644 --- a/pkg/networkserver/registry.go +++ b/pkg/networkserver/registry.go @@ -299,3 +299,11 @@ type ScheduledDownlinkMatcher interface { // successful, for example if a long time has passed since the downlink was scheduled. Match(ctx context.Context, ack *ttnpb.TxAcknowledgment) (*ttnpb.DownlinkMessage, error) } + +// MACSettingsProfileRegistry is a registry, containing MAC settings profiles. +type MACSettingsProfileRegistry interface { + Get(ctx context.Context, ids *ttnpb.MACSettingsProfileIdentifiers, paths []string) (*ttnpb.MACSettingsProfile, error) // nolint: lll + Set(ctx context.Context, ids *ttnpb.MACSettingsProfileIdentifiers, paths []string, f func(context.Context, *ttnpb.MACSettingsProfile) (*ttnpb.MACSettingsProfile, []string, error)) (*ttnpb.MACSettingsProfile, error) // nolint: lll + List(ctx context.Context, ids *ttnpb.ApplicationIdentifiers, paths []string) ([]*ttnpb.MACSettingsProfile, error) // nolint: lll + WithPagination(ctx context.Context, limit uint32, page uint32, total *int64) context.Context +} diff --git a/pkg/redis/pagination.go b/pkg/redis/pagination.go index b1c447b443..73e5e9ef8c 100644 --- a/pkg/redis/pagination.go +++ b/pkg/redis/pagination.go @@ -18,6 +18,18 @@ import ( "context" ) +// PaginationDefaults sets default values for paginations options within the Redis store. +type PaginationDefaults struct { + DefaultLimit int64 +} + +var paginationDefaults = PaginationDefaults{} + +// SetPaginationDefaults should only be called at the initialization of the server. +func SetPaginationDefaults(d PaginationDefaults) { + paginationDefaults = d +} + type paginationOptionsKeyType struct{} var paginationOptionsKey paginationOptionsKeyType @@ -33,6 +45,9 @@ func NewContextWithPagination(ctx context.Context, limit, page int64, total *int if page == 0 { page = 1 } + if limit == 0 { + limit = paginationDefaults.DefaultLimit + } return context.WithValue(ctx, paginationOptionsKey, paginationOptions{ limit: limit, offset: (page - 1) * limit, diff --git a/pkg/redis/pagination_test.go b/pkg/redis/pagination_test.go index 537a8dfb06..ba54fe8e25 100644 --- a/pkg/redis/pagination_test.go +++ b/pkg/redis/pagination_test.go @@ -84,4 +84,16 @@ func TestPagination(t *testing.T) { redis.SetPaginationTotal(ctx, total) a.So(totalCount, should.Equal, total) }) + + t.Run("SetPaginationDefaults", func(t *testing.T) { + t.Parallel() + redis.SetPaginationDefaults(redis.PaginationDefaults{DefaultLimit: 20}) + + ctx := test.Context() + ctx = redis.NewContextWithPagination(ctx, 0, 0, nil) + + limit, _ := redis.PaginationLimitAndOffsetFromContext(ctx) + + a.So(limit, should.Equal, uint64(20)) + }) } diff --git a/pkg/ttnpb/end_device.pb.go b/pkg/ttnpb/end_device.pb.go index d894eeee24..eb1f90a831 100644 --- a/pkg/ttnpb/end_device.pb.go +++ b/pkg/ttnpb/end_device.pb.go @@ -1924,6 +1924,63 @@ func (x *MACSettings) GetDesiredRelay() *RelaySettings { return nil } +type MACSettingsProfile struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Profile identifiers. + Ids *MACSettingsProfileIdentifiers `protobuf:"bytes,1,opt,name=ids,proto3" json:"ids,omitempty"` + // MAC settings. + MacSettings *MACSettings `protobuf:"bytes,2,opt,name=mac_settings,json=macSettings,proto3" json:"mac_settings,omitempty"` +} + +func (x *MACSettingsProfile) Reset() { + *x = MACSettingsProfile{} + if protoimpl.UnsafeEnabled { + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *MACSettingsProfile) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MACSettingsProfile) ProtoMessage() {} + +func (x *MACSettingsProfile) ProtoReflect() protoreflect.Message { + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[14] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use MACSettingsProfile.ProtoReflect.Descriptor instead. +func (*MACSettingsProfile) Descriptor() ([]byte, []int) { + return file_ttn_lorawan_v3_end_device_proto_rawDescGZIP(), []int{14} +} + +func (x *MACSettingsProfile) GetIds() *MACSettingsProfileIdentifiers { + if x != nil { + return x.Ids + } + return nil +} + +func (x *MACSettingsProfile) GetMacSettings() *MACSettings { + if x != nil { + return x.MacSettings + } + return nil +} + // MACState represents the state of MAC layer of the device. // MACState is reset on each join for OTAA or ResetInd for ABP devices. // This is used internally by the Network Server. @@ -2003,7 +2060,7 @@ type MACState struct { func (x *MACState) Reset() { *x = MACState{} if protoimpl.UnsafeEnabled { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[14] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2016,7 +2073,7 @@ func (x *MACState) String() string { func (*MACState) ProtoMessage() {} func (x *MACState) ProtoReflect() protoreflect.Message { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[14] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[15] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2029,7 +2086,7 @@ func (x *MACState) ProtoReflect() protoreflect.Message { // Deprecated: Use MACState.ProtoReflect.Descriptor instead. func (*MACState) Descriptor() ([]byte, []int) { - return file_ttn_lorawan_v3_end_device_proto_rawDescGZIP(), []int{14} + return file_ttn_lorawan_v3_end_device_proto_rawDescGZIP(), []int{15} } func (x *MACState) GetCurrentParameters() *MACParameters { @@ -2214,7 +2271,7 @@ type EndDeviceAuthenticationCode struct { func (x *EndDeviceAuthenticationCode) Reset() { *x = EndDeviceAuthenticationCode{} if protoimpl.UnsafeEnabled { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[15] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2227,7 +2284,7 @@ func (x *EndDeviceAuthenticationCode) String() string { func (*EndDeviceAuthenticationCode) ProtoMessage() {} func (x *EndDeviceAuthenticationCode) ProtoReflect() protoreflect.Message { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[15] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[16] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2240,7 +2297,7 @@ func (x *EndDeviceAuthenticationCode) ProtoReflect() protoreflect.Message { // Deprecated: Use EndDeviceAuthenticationCode.ProtoReflect.Descriptor instead. func (*EndDeviceAuthenticationCode) Descriptor() ([]byte, []int) { - return file_ttn_lorawan_v3_end_device_proto_rawDescGZIP(), []int{15} + return file_ttn_lorawan_v3_end_device_proto_rawDescGZIP(), []int{16} } func (x *EndDeviceAuthenticationCode) GetValue() string { @@ -2433,7 +2490,7 @@ type EndDevice struct { func (x *EndDevice) Reset() { *x = EndDevice{} if protoimpl.UnsafeEnabled { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[16] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2446,7 +2503,7 @@ func (x *EndDevice) String() string { func (*EndDevice) ProtoMessage() {} func (x *EndDevice) ProtoReflect() protoreflect.Message { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[16] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[17] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2459,7 +2516,7 @@ func (x *EndDevice) ProtoReflect() protoreflect.Message { // Deprecated: Use EndDevice.ProtoReflect.Descriptor instead. func (*EndDevice) Descriptor() ([]byte, []int) { - return file_ttn_lorawan_v3_end_device_proto_rawDescGZIP(), []int{16} + return file_ttn_lorawan_v3_end_device_proto_rawDescGZIP(), []int{17} } func (x *EndDevice) GetIds() *EndDeviceIdentifiers { @@ -2844,7 +2901,7 @@ type EndDevices struct { func (x *EndDevices) Reset() { *x = EndDevices{} if protoimpl.UnsafeEnabled { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[17] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2857,7 +2914,7 @@ func (x *EndDevices) String() string { func (*EndDevices) ProtoMessage() {} func (x *EndDevices) ProtoReflect() protoreflect.Message { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[17] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[18] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2870,7 +2927,7 @@ func (x *EndDevices) ProtoReflect() protoreflect.Message { // Deprecated: Use EndDevices.ProtoReflect.Descriptor instead. func (*EndDevices) Descriptor() ([]byte, []int) { - return file_ttn_lorawan_v3_end_device_proto_rawDescGZIP(), []int{17} + return file_ttn_lorawan_v3_end_device_proto_rawDescGZIP(), []int{18} } func (x *EndDevices) GetEndDevices() []*EndDevice { @@ -2894,7 +2951,7 @@ type DevAddrPrefix struct { func (x *DevAddrPrefix) Reset() { *x = DevAddrPrefix{} if protoimpl.UnsafeEnabled { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[18] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2907,7 +2964,7 @@ func (x *DevAddrPrefix) String() string { func (*DevAddrPrefix) ProtoMessage() {} func (x *DevAddrPrefix) ProtoReflect() protoreflect.Message { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[18] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[19] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2920,7 +2977,7 @@ func (x *DevAddrPrefix) ProtoReflect() protoreflect.Message { // Deprecated: Use DevAddrPrefix.ProtoReflect.Descriptor instead. func (*DevAddrPrefix) Descriptor() ([]byte, []int) { - return file_ttn_lorawan_v3_end_device_proto_rawDescGZIP(), []int{18} + return file_ttn_lorawan_v3_end_device_proto_rawDescGZIP(), []int{19} } func (x *DevAddrPrefix) GetDevAddr() []byte { @@ -2948,7 +3005,7 @@ type CreateEndDeviceRequest struct { func (x *CreateEndDeviceRequest) Reset() { *x = CreateEndDeviceRequest{} if protoimpl.UnsafeEnabled { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[19] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[20] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2961,7 +3018,7 @@ func (x *CreateEndDeviceRequest) String() string { func (*CreateEndDeviceRequest) ProtoMessage() {} func (x *CreateEndDeviceRequest) ProtoReflect() protoreflect.Message { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[19] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[20] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2974,7 +3031,7 @@ func (x *CreateEndDeviceRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CreateEndDeviceRequest.ProtoReflect.Descriptor instead. func (*CreateEndDeviceRequest) Descriptor() ([]byte, []int) { - return file_ttn_lorawan_v3_end_device_proto_rawDescGZIP(), []int{19} + return file_ttn_lorawan_v3_end_device_proto_rawDescGZIP(), []int{20} } func (x *CreateEndDeviceRequest) GetEndDevice() *EndDevice { @@ -2998,7 +3055,7 @@ type UpdateEndDeviceRequest struct { func (x *UpdateEndDeviceRequest) Reset() { *x = UpdateEndDeviceRequest{} if protoimpl.UnsafeEnabled { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[20] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[21] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3011,7 +3068,7 @@ func (x *UpdateEndDeviceRequest) String() string { func (*UpdateEndDeviceRequest) ProtoMessage() {} func (x *UpdateEndDeviceRequest) ProtoReflect() protoreflect.Message { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[20] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[21] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3024,7 +3081,7 @@ func (x *UpdateEndDeviceRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use UpdateEndDeviceRequest.ProtoReflect.Descriptor instead. func (*UpdateEndDeviceRequest) Descriptor() ([]byte, []int) { - return file_ttn_lorawan_v3_end_device_proto_rawDescGZIP(), []int{20} + return file_ttn_lorawan_v3_end_device_proto_rawDescGZIP(), []int{21} } func (x *UpdateEndDeviceRequest) GetEndDevice() *EndDevice { @@ -3053,7 +3110,7 @@ type BatchUpdateEndDeviceLastSeenRequest struct { func (x *BatchUpdateEndDeviceLastSeenRequest) Reset() { *x = BatchUpdateEndDeviceLastSeenRequest{} if protoimpl.UnsafeEnabled { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[21] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[22] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3066,7 +3123,7 @@ func (x *BatchUpdateEndDeviceLastSeenRequest) String() string { func (*BatchUpdateEndDeviceLastSeenRequest) ProtoMessage() {} func (x *BatchUpdateEndDeviceLastSeenRequest) ProtoReflect() protoreflect.Message { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[21] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[22] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3079,7 +3136,7 @@ func (x *BatchUpdateEndDeviceLastSeenRequest) ProtoReflect() protoreflect.Messag // Deprecated: Use BatchUpdateEndDeviceLastSeenRequest.ProtoReflect.Descriptor instead. func (*BatchUpdateEndDeviceLastSeenRequest) Descriptor() ([]byte, []int) { - return file_ttn_lorawan_v3_end_device_proto_rawDescGZIP(), []int{21} + return file_ttn_lorawan_v3_end_device_proto_rawDescGZIP(), []int{22} } func (x *BatchUpdateEndDeviceLastSeenRequest) GetUpdates() []*BatchUpdateEndDeviceLastSeenRequest_EndDeviceLastSeenUpdate { @@ -3103,7 +3160,7 @@ type GetEndDeviceRequest struct { func (x *GetEndDeviceRequest) Reset() { *x = GetEndDeviceRequest{} if protoimpl.UnsafeEnabled { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[22] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[23] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3116,7 +3173,7 @@ func (x *GetEndDeviceRequest) String() string { func (*GetEndDeviceRequest) ProtoMessage() {} func (x *GetEndDeviceRequest) ProtoReflect() protoreflect.Message { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[22] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[23] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3129,7 +3186,7 @@ func (x *GetEndDeviceRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetEndDeviceRequest.ProtoReflect.Descriptor instead. func (*GetEndDeviceRequest) Descriptor() ([]byte, []int) { - return file_ttn_lorawan_v3_end_device_proto_rawDescGZIP(), []int{22} + return file_ttn_lorawan_v3_end_device_proto_rawDescGZIP(), []int{23} } func (x *GetEndDeviceRequest) GetEndDeviceIds() *EndDeviceIdentifiers { @@ -3158,7 +3215,7 @@ type GetEndDeviceIdentifiersForEUIsRequest struct { func (x *GetEndDeviceIdentifiersForEUIsRequest) Reset() { *x = GetEndDeviceIdentifiersForEUIsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[23] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[24] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3171,7 +3228,7 @@ func (x *GetEndDeviceIdentifiersForEUIsRequest) String() string { func (*GetEndDeviceIdentifiersForEUIsRequest) ProtoMessage() {} func (x *GetEndDeviceIdentifiersForEUIsRequest) ProtoReflect() protoreflect.Message { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[23] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[24] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3184,7 +3241,7 @@ func (x *GetEndDeviceIdentifiersForEUIsRequest) ProtoReflect() protoreflect.Mess // Deprecated: Use GetEndDeviceIdentifiersForEUIsRequest.ProtoReflect.Descriptor instead. func (*GetEndDeviceIdentifiersForEUIsRequest) Descriptor() ([]byte, []int) { - return file_ttn_lorawan_v3_end_device_proto_rawDescGZIP(), []int{23} + return file_ttn_lorawan_v3_end_device_proto_rawDescGZIP(), []int{24} } func (x *GetEndDeviceIdentifiersForEUIsRequest) GetJoinEui() []byte { @@ -3223,7 +3280,7 @@ type ListEndDevicesRequest struct { func (x *ListEndDevicesRequest) Reset() { *x = ListEndDevicesRequest{} if protoimpl.UnsafeEnabled { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[24] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[25] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3236,7 +3293,7 @@ func (x *ListEndDevicesRequest) String() string { func (*ListEndDevicesRequest) ProtoMessage() {} func (x *ListEndDevicesRequest) ProtoReflect() protoreflect.Message { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[24] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[25] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3249,7 +3306,7 @@ func (x *ListEndDevicesRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListEndDevicesRequest.ProtoReflect.Descriptor instead. func (*ListEndDevicesRequest) Descriptor() ([]byte, []int) { - return file_ttn_lorawan_v3_end_device_proto_rawDescGZIP(), []int{24} + return file_ttn_lorawan_v3_end_device_proto_rawDescGZIP(), []int{25} } func (x *ListEndDevicesRequest) GetApplicationIds() *ApplicationIdentifiers { @@ -3308,7 +3365,7 @@ type SetEndDeviceRequest struct { func (x *SetEndDeviceRequest) Reset() { *x = SetEndDeviceRequest{} if protoimpl.UnsafeEnabled { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[25] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[26] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3321,7 +3378,7 @@ func (x *SetEndDeviceRequest) String() string { func (*SetEndDeviceRequest) ProtoMessage() {} func (x *SetEndDeviceRequest) ProtoReflect() protoreflect.Message { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[25] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[26] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3334,7 +3391,7 @@ func (x *SetEndDeviceRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use SetEndDeviceRequest.ProtoReflect.Descriptor instead. func (*SetEndDeviceRequest) Descriptor() ([]byte, []int) { - return file_ttn_lorawan_v3_end_device_proto_rawDescGZIP(), []int{25} + return file_ttn_lorawan_v3_end_device_proto_rawDescGZIP(), []int{26} } func (x *SetEndDeviceRequest) GetEndDevice() *EndDevice { @@ -3365,7 +3422,7 @@ type ResetAndGetEndDeviceRequest struct { func (x *ResetAndGetEndDeviceRequest) Reset() { *x = ResetAndGetEndDeviceRequest{} if protoimpl.UnsafeEnabled { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[26] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[27] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3378,7 +3435,7 @@ func (x *ResetAndGetEndDeviceRequest) String() string { func (*ResetAndGetEndDeviceRequest) ProtoMessage() {} func (x *ResetAndGetEndDeviceRequest) ProtoReflect() protoreflect.Message { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[26] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[27] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3391,7 +3448,7 @@ func (x *ResetAndGetEndDeviceRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ResetAndGetEndDeviceRequest.ProtoReflect.Descriptor instead. func (*ResetAndGetEndDeviceRequest) Descriptor() ([]byte, []int) { - return file_ttn_lorawan_v3_end_device_proto_rawDescGZIP(), []int{26} + return file_ttn_lorawan_v3_end_device_proto_rawDescGZIP(), []int{27} } func (x *ResetAndGetEndDeviceRequest) GetEndDeviceIds() *EndDeviceIdentifiers { @@ -3421,7 +3478,7 @@ type EndDeviceTemplate struct { func (x *EndDeviceTemplate) Reset() { *x = EndDeviceTemplate{} if protoimpl.UnsafeEnabled { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[27] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[28] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3434,7 +3491,7 @@ func (x *EndDeviceTemplate) String() string { func (*EndDeviceTemplate) ProtoMessage() {} func (x *EndDeviceTemplate) ProtoReflect() protoreflect.Message { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[27] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[28] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3447,7 +3504,7 @@ func (x *EndDeviceTemplate) ProtoReflect() protoreflect.Message { // Deprecated: Use EndDeviceTemplate.ProtoReflect.Descriptor instead. func (*EndDeviceTemplate) Descriptor() ([]byte, []int) { - return file_ttn_lorawan_v3_end_device_proto_rawDescGZIP(), []int{27} + return file_ttn_lorawan_v3_end_device_proto_rawDescGZIP(), []int{28} } func (x *EndDeviceTemplate) GetEndDevice() *EndDevice { @@ -3484,7 +3541,7 @@ type EndDeviceTemplateFormat struct { func (x *EndDeviceTemplateFormat) Reset() { *x = EndDeviceTemplateFormat{} if protoimpl.UnsafeEnabled { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[28] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[29] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3497,7 +3554,7 @@ func (x *EndDeviceTemplateFormat) String() string { func (*EndDeviceTemplateFormat) ProtoMessage() {} func (x *EndDeviceTemplateFormat) ProtoReflect() protoreflect.Message { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[28] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[29] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3510,7 +3567,7 @@ func (x *EndDeviceTemplateFormat) ProtoReflect() protoreflect.Message { // Deprecated: Use EndDeviceTemplateFormat.ProtoReflect.Descriptor instead. func (*EndDeviceTemplateFormat) Descriptor() ([]byte, []int) { - return file_ttn_lorawan_v3_end_device_proto_rawDescGZIP(), []int{28} + return file_ttn_lorawan_v3_end_device_proto_rawDescGZIP(), []int{29} } func (x *EndDeviceTemplateFormat) GetName() string { @@ -3545,7 +3602,7 @@ type EndDeviceTemplateFormats struct { func (x *EndDeviceTemplateFormats) Reset() { *x = EndDeviceTemplateFormats{} if protoimpl.UnsafeEnabled { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[29] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[30] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3558,7 +3615,7 @@ func (x *EndDeviceTemplateFormats) String() string { func (*EndDeviceTemplateFormats) ProtoMessage() {} func (x *EndDeviceTemplateFormats) ProtoReflect() protoreflect.Message { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[29] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[30] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3571,7 +3628,7 @@ func (x *EndDeviceTemplateFormats) ProtoReflect() protoreflect.Message { // Deprecated: Use EndDeviceTemplateFormats.ProtoReflect.Descriptor instead. func (*EndDeviceTemplateFormats) Descriptor() ([]byte, []int) { - return file_ttn_lorawan_v3_end_device_proto_rawDescGZIP(), []int{29} + return file_ttn_lorawan_v3_end_device_proto_rawDescGZIP(), []int{30} } func (x *EndDeviceTemplateFormats) GetFormats() map[string]*EndDeviceTemplateFormat { @@ -3597,7 +3654,7 @@ type ConvertEndDeviceTemplateRequest struct { func (x *ConvertEndDeviceTemplateRequest) Reset() { *x = ConvertEndDeviceTemplateRequest{} if protoimpl.UnsafeEnabled { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[30] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[31] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3610,7 +3667,7 @@ func (x *ConvertEndDeviceTemplateRequest) String() string { func (*ConvertEndDeviceTemplateRequest) ProtoMessage() {} func (x *ConvertEndDeviceTemplateRequest) ProtoReflect() protoreflect.Message { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[30] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[31] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3623,7 +3680,7 @@ func (x *ConvertEndDeviceTemplateRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ConvertEndDeviceTemplateRequest.ProtoReflect.Descriptor instead. func (*ConvertEndDeviceTemplateRequest) Descriptor() ([]byte, []int) { - return file_ttn_lorawan_v3_end_device_proto_rawDescGZIP(), []int{30} + return file_ttn_lorawan_v3_end_device_proto_rawDescGZIP(), []int{31} } func (x *ConvertEndDeviceTemplateRequest) GetFormatId() string { @@ -3659,7 +3716,7 @@ type BatchDeleteEndDevicesRequest struct { func (x *BatchDeleteEndDevicesRequest) Reset() { *x = BatchDeleteEndDevicesRequest{} if protoimpl.UnsafeEnabled { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[31] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[32] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3672,7 +3729,7 @@ func (x *BatchDeleteEndDevicesRequest) String() string { func (*BatchDeleteEndDevicesRequest) ProtoMessage() {} func (x *BatchDeleteEndDevicesRequest) ProtoReflect() protoreflect.Message { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[31] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[32] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3685,7 +3742,7 @@ func (x *BatchDeleteEndDevicesRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use BatchDeleteEndDevicesRequest.ProtoReflect.Descriptor instead. func (*BatchDeleteEndDevicesRequest) Descriptor() ([]byte, []int) { - return file_ttn_lorawan_v3_end_device_proto_rawDescGZIP(), []int{31} + return file_ttn_lorawan_v3_end_device_proto_rawDescGZIP(), []int{32} } func (x *BatchDeleteEndDevicesRequest) GetApplicationIds() *ApplicationIdentifiers { @@ -3718,7 +3775,7 @@ type BatchGetEndDevicesRequest struct { func (x *BatchGetEndDevicesRequest) Reset() { *x = BatchGetEndDevicesRequest{} if protoimpl.UnsafeEnabled { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[32] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[33] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3731,7 +3788,7 @@ func (x *BatchGetEndDevicesRequest) String() string { func (*BatchGetEndDevicesRequest) ProtoMessage() {} func (x *BatchGetEndDevicesRequest) ProtoReflect() protoreflect.Message { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[32] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[33] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3744,7 +3801,7 @@ func (x *BatchGetEndDevicesRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use BatchGetEndDevicesRequest.ProtoReflect.Descriptor instead. func (*BatchGetEndDevicesRequest) Descriptor() ([]byte, []int) { - return file_ttn_lorawan_v3_end_device_proto_rawDescGZIP(), []int{32} + return file_ttn_lorawan_v3_end_device_proto_rawDescGZIP(), []int{33} } func (x *BatchGetEndDevicesRequest) GetApplicationIds() *ApplicationIdentifiers { @@ -3788,7 +3845,7 @@ type MACParameters_Channel struct { func (x *MACParameters_Channel) Reset() { *x = MACParameters_Channel{} if protoimpl.UnsafeEnabled { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[33] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[34] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3801,7 +3858,7 @@ func (x *MACParameters_Channel) String() string { func (*MACParameters_Channel) ProtoMessage() {} func (x *MACParameters_Channel) ProtoReflect() protoreflect.Message { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[33] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[34] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3869,7 +3926,7 @@ type ADRSettings_StaticMode struct { func (x *ADRSettings_StaticMode) Reset() { *x = ADRSettings_StaticMode{} if protoimpl.UnsafeEnabled { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[34] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[35] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3882,7 +3939,7 @@ func (x *ADRSettings_StaticMode) String() string { func (*ADRSettings_StaticMode) ProtoMessage() {} func (x *ADRSettings_StaticMode) ProtoReflect() protoreflect.Message { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[34] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[35] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3954,7 +4011,7 @@ type ADRSettings_DynamicMode struct { func (x *ADRSettings_DynamicMode) Reset() { *x = ADRSettings_DynamicMode{} if protoimpl.UnsafeEnabled { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[35] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[36] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3967,7 +4024,7 @@ func (x *ADRSettings_DynamicMode) String() string { func (*ADRSettings_DynamicMode) ProtoMessage() {} func (x *ADRSettings_DynamicMode) ProtoReflect() protoreflect.Message { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[35] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[36] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4057,7 +4114,7 @@ type ADRSettings_DisabledMode struct { func (x *ADRSettings_DisabledMode) Reset() { *x = ADRSettings_DisabledMode{} if protoimpl.UnsafeEnabled { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[36] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[37] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4070,7 +4127,7 @@ func (x *ADRSettings_DisabledMode) String() string { func (*ADRSettings_DisabledMode) ProtoMessage() {} func (x *ADRSettings_DisabledMode) ProtoReflect() protoreflect.Message { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[36] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[37] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4101,7 +4158,7 @@ type ADRSettings_DynamicMode_ChannelSteeringSettings struct { func (x *ADRSettings_DynamicMode_ChannelSteeringSettings) Reset() { *x = ADRSettings_DynamicMode_ChannelSteeringSettings{} if protoimpl.UnsafeEnabled { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[37] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[38] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4114,7 +4171,7 @@ func (x *ADRSettings_DynamicMode_ChannelSteeringSettings) String() string { func (*ADRSettings_DynamicMode_ChannelSteeringSettings) ProtoMessage() {} func (x *ADRSettings_DynamicMode_ChannelSteeringSettings) ProtoReflect() protoreflect.Message { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[37] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[38] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4186,7 +4243,7 @@ type ADRSettings_DynamicMode_PerDataRateIndexOverride struct { func (x *ADRSettings_DynamicMode_PerDataRateIndexOverride) Reset() { *x = ADRSettings_DynamicMode_PerDataRateIndexOverride{} if protoimpl.UnsafeEnabled { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[38] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[39] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4199,7 +4256,7 @@ func (x *ADRSettings_DynamicMode_PerDataRateIndexOverride) String() string { func (*ADRSettings_DynamicMode_PerDataRateIndexOverride) ProtoMessage() {} func (x *ADRSettings_DynamicMode_PerDataRateIndexOverride) ProtoReflect() protoreflect.Message { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[38] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[39] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4256,7 +4313,7 @@ type ADRSettings_DynamicMode_Overrides struct { func (x *ADRSettings_DynamicMode_Overrides) Reset() { *x = ADRSettings_DynamicMode_Overrides{} if protoimpl.UnsafeEnabled { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[39] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[40] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4269,7 +4326,7 @@ func (x *ADRSettings_DynamicMode_Overrides) String() string { func (*ADRSettings_DynamicMode_Overrides) ProtoMessage() {} func (x *ADRSettings_DynamicMode_Overrides) ProtoReflect() protoreflect.Message { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[39] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[40] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4409,7 +4466,7 @@ type ADRSettings_DynamicMode_ChannelSteeringSettings_LoRaNarrowMode struct { func (x *ADRSettings_DynamicMode_ChannelSteeringSettings_LoRaNarrowMode) Reset() { *x = ADRSettings_DynamicMode_ChannelSteeringSettings_LoRaNarrowMode{} if protoimpl.UnsafeEnabled { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[40] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[41] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4422,7 +4479,7 @@ func (x *ADRSettings_DynamicMode_ChannelSteeringSettings_LoRaNarrowMode) String( func (*ADRSettings_DynamicMode_ChannelSteeringSettings_LoRaNarrowMode) ProtoMessage() {} func (x *ADRSettings_DynamicMode_ChannelSteeringSettings_LoRaNarrowMode) ProtoReflect() protoreflect.Message { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[40] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[41] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4449,7 +4506,7 @@ type ADRSettings_DynamicMode_ChannelSteeringSettings_DisabledMode struct { func (x *ADRSettings_DynamicMode_ChannelSteeringSettings_DisabledMode) Reset() { *x = ADRSettings_DynamicMode_ChannelSteeringSettings_DisabledMode{} if protoimpl.UnsafeEnabled { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[41] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[42] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4462,7 +4519,7 @@ func (x *ADRSettings_DynamicMode_ChannelSteeringSettings_DisabledMode) String() func (*ADRSettings_DynamicMode_ChannelSteeringSettings_DisabledMode) ProtoMessage() {} func (x *ADRSettings_DynamicMode_ChannelSteeringSettings_DisabledMode) ProtoReflect() protoreflect.Message { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[41] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[42] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4491,7 +4548,7 @@ type MACState_JoinRequest struct { func (x *MACState_JoinRequest) Reset() { *x = MACState_JoinRequest{} if protoimpl.UnsafeEnabled { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[42] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[43] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4504,7 +4561,7 @@ func (x *MACState_JoinRequest) String() string { func (*MACState_JoinRequest) ProtoMessage() {} func (x *MACState_JoinRequest) ProtoReflect() protoreflect.Message { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[42] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[43] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4517,7 +4574,7 @@ func (x *MACState_JoinRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use MACState_JoinRequest.ProtoReflect.Descriptor instead. func (*MACState_JoinRequest) Descriptor() ([]byte, []int) { - return file_ttn_lorawan_v3_end_device_proto_rawDescGZIP(), []int{14, 0} + return file_ttn_lorawan_v3_end_device_proto_rawDescGZIP(), []int{15, 0} } func (x *MACState_JoinRequest) GetDownlinkSettings() *DLSettings { @@ -4559,7 +4616,7 @@ type MACState_JoinAccept struct { func (x *MACState_JoinAccept) Reset() { *x = MACState_JoinAccept{} if protoimpl.UnsafeEnabled { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[43] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[44] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4572,7 +4629,7 @@ func (x *MACState_JoinAccept) String() string { func (*MACState_JoinAccept) ProtoMessage() {} func (x *MACState_JoinAccept) ProtoReflect() protoreflect.Message { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[43] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[44] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4585,7 +4642,7 @@ func (x *MACState_JoinAccept) ProtoReflect() protoreflect.Message { // Deprecated: Use MACState_JoinAccept.ProtoReflect.Descriptor instead. func (*MACState_JoinAccept) Descriptor() ([]byte, []int) { - return file_ttn_lorawan_v3_end_device_proto_rawDescGZIP(), []int{14, 1} + return file_ttn_lorawan_v3_end_device_proto_rawDescGZIP(), []int{15, 1} } func (x *MACState_JoinAccept) GetPayload() []byte { @@ -4648,7 +4705,7 @@ type MACState_UplinkMessage struct { func (x *MACState_UplinkMessage) Reset() { *x = MACState_UplinkMessage{} if protoimpl.UnsafeEnabled { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[44] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[45] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4661,7 +4718,7 @@ func (x *MACState_UplinkMessage) String() string { func (*MACState_UplinkMessage) ProtoMessage() {} func (x *MACState_UplinkMessage) ProtoReflect() protoreflect.Message { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[44] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[45] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4674,7 +4731,7 @@ func (x *MACState_UplinkMessage) ProtoReflect() protoreflect.Message { // Deprecated: Use MACState_UplinkMessage.ProtoReflect.Descriptor instead. func (*MACState_UplinkMessage) Descriptor() ([]byte, []int) { - return file_ttn_lorawan_v3_end_device_proto_rawDescGZIP(), []int{14, 2} + return file_ttn_lorawan_v3_end_device_proto_rawDescGZIP(), []int{15, 2} } func (x *MACState_UplinkMessage) GetPayload() *Message { @@ -4733,7 +4790,7 @@ type MACState_DownlinkMessage struct { func (x *MACState_DownlinkMessage) Reset() { *x = MACState_DownlinkMessage{} if protoimpl.UnsafeEnabled { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[45] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[46] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4746,7 +4803,7 @@ func (x *MACState_DownlinkMessage) String() string { func (*MACState_DownlinkMessage) ProtoMessage() {} func (x *MACState_DownlinkMessage) ProtoReflect() protoreflect.Message { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[45] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[46] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4759,7 +4816,7 @@ func (x *MACState_DownlinkMessage) ProtoReflect() protoreflect.Message { // Deprecated: Use MACState_DownlinkMessage.ProtoReflect.Descriptor instead. func (*MACState_DownlinkMessage) Descriptor() ([]byte, []int) { - return file_ttn_lorawan_v3_end_device_proto_rawDescGZIP(), []int{14, 3} + return file_ttn_lorawan_v3_end_device_proto_rawDescGZIP(), []int{15, 3} } func (x *MACState_DownlinkMessage) GetPayload() *MACState_DownlinkMessage_Message { @@ -4788,7 +4845,7 @@ type MACState_DataRateRange struct { func (x *MACState_DataRateRange) Reset() { *x = MACState_DataRateRange{} if protoimpl.UnsafeEnabled { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[46] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[47] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4801,7 +4858,7 @@ func (x *MACState_DataRateRange) String() string { func (*MACState_DataRateRange) ProtoMessage() {} func (x *MACState_DataRateRange) ProtoReflect() protoreflect.Message { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[46] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[47] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4814,7 +4871,7 @@ func (x *MACState_DataRateRange) ProtoReflect() protoreflect.Message { // Deprecated: Use MACState_DataRateRange.ProtoReflect.Descriptor instead. func (*MACState_DataRateRange) Descriptor() ([]byte, []int) { - return file_ttn_lorawan_v3_end_device_proto_rawDescGZIP(), []int{14, 4} + return file_ttn_lorawan_v3_end_device_proto_rawDescGZIP(), []int{15, 4} } func (x *MACState_DataRateRange) GetMinDataRateIndex() DataRateIndex { @@ -4842,7 +4899,7 @@ type MACState_DataRateRanges struct { func (x *MACState_DataRateRanges) Reset() { *x = MACState_DataRateRanges{} if protoimpl.UnsafeEnabled { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[47] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[48] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4855,7 +4912,7 @@ func (x *MACState_DataRateRanges) String() string { func (*MACState_DataRateRanges) ProtoMessage() {} func (x *MACState_DataRateRanges) ProtoReflect() protoreflect.Message { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[47] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[48] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4868,7 +4925,7 @@ func (x *MACState_DataRateRanges) ProtoReflect() protoreflect.Message { // Deprecated: Use MACState_DataRateRanges.ProtoReflect.Descriptor instead. func (*MACState_DataRateRanges) Descriptor() ([]byte, []int) { - return file_ttn_lorawan_v3_end_device_proto_rawDescGZIP(), []int{14, 5} + return file_ttn_lorawan_v3_end_device_proto_rawDescGZIP(), []int{15, 5} } func (x *MACState_DataRateRanges) GetRanges() []*MACState_DataRateRange { @@ -4889,7 +4946,7 @@ type MACState_UplinkMessage_TxSettings struct { func (x *MACState_UplinkMessage_TxSettings) Reset() { *x = MACState_UplinkMessage_TxSettings{} if protoimpl.UnsafeEnabled { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[49] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[50] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4902,7 +4959,7 @@ func (x *MACState_UplinkMessage_TxSettings) String() string { func (*MACState_UplinkMessage_TxSettings) ProtoMessage() {} func (x *MACState_UplinkMessage_TxSettings) ProtoReflect() protoreflect.Message { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[49] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[50] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4915,7 +4972,7 @@ func (x *MACState_UplinkMessage_TxSettings) ProtoReflect() protoreflect.Message // Deprecated: Use MACState_UplinkMessage_TxSettings.ProtoReflect.Descriptor instead. func (*MACState_UplinkMessage_TxSettings) Descriptor() ([]byte, []int) { - return file_ttn_lorawan_v3_end_device_proto_rawDescGZIP(), []int{14, 2, 0} + return file_ttn_lorawan_v3_end_device_proto_rawDescGZIP(), []int{15, 2, 0} } func (x *MACState_UplinkMessage_TxSettings) GetDataRate() *DataRate { @@ -4942,7 +4999,7 @@ type MACState_UplinkMessage_RxMetadata struct { func (x *MACState_UplinkMessage_RxMetadata) Reset() { *x = MACState_UplinkMessage_RxMetadata{} if protoimpl.UnsafeEnabled { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[50] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[51] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4955,7 +5012,7 @@ func (x *MACState_UplinkMessage_RxMetadata) String() string { func (*MACState_UplinkMessage_RxMetadata) ProtoMessage() {} func (x *MACState_UplinkMessage_RxMetadata) ProtoReflect() protoreflect.Message { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[50] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[51] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4968,7 +5025,7 @@ func (x *MACState_UplinkMessage_RxMetadata) ProtoReflect() protoreflect.Message // Deprecated: Use MACState_UplinkMessage_RxMetadata.ProtoReflect.Descriptor instead. func (*MACState_UplinkMessage_RxMetadata) Descriptor() ([]byte, []int) { - return file_ttn_lorawan_v3_end_device_proto_rawDescGZIP(), []int{14, 2, 1} + return file_ttn_lorawan_v3_end_device_proto_rawDescGZIP(), []int{15, 2, 1} } func (x *MACState_UplinkMessage_RxMetadata) GetGatewayIds() *GatewayIdentifiers { @@ -5029,7 +5086,7 @@ type MACState_UplinkMessage_RxMetadata_PacketBrokerMetadata struct { func (x *MACState_UplinkMessage_RxMetadata_PacketBrokerMetadata) Reset() { *x = MACState_UplinkMessage_RxMetadata_PacketBrokerMetadata{} if protoimpl.UnsafeEnabled { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[51] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[52] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5042,7 +5099,7 @@ func (x *MACState_UplinkMessage_RxMetadata_PacketBrokerMetadata) String() string func (*MACState_UplinkMessage_RxMetadata_PacketBrokerMetadata) ProtoMessage() {} func (x *MACState_UplinkMessage_RxMetadata_PacketBrokerMetadata) ProtoReflect() protoreflect.Message { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[51] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[52] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5055,7 +5112,7 @@ func (x *MACState_UplinkMessage_RxMetadata_PacketBrokerMetadata) ProtoReflect() // Deprecated: Use MACState_UplinkMessage_RxMetadata_PacketBrokerMetadata.ProtoReflect.Descriptor instead. func (*MACState_UplinkMessage_RxMetadata_PacketBrokerMetadata) Descriptor() ([]byte, []int) { - return file_ttn_lorawan_v3_end_device_proto_rawDescGZIP(), []int{14, 2, 1, 0} + return file_ttn_lorawan_v3_end_device_proto_rawDescGZIP(), []int{15, 2, 1, 0} } type MACState_UplinkMessage_RxMetadata_RelayMetadata struct { @@ -5067,7 +5124,7 @@ type MACState_UplinkMessage_RxMetadata_RelayMetadata struct { func (x *MACState_UplinkMessage_RxMetadata_RelayMetadata) Reset() { *x = MACState_UplinkMessage_RxMetadata_RelayMetadata{} if protoimpl.UnsafeEnabled { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[52] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[53] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5080,7 +5137,7 @@ func (x *MACState_UplinkMessage_RxMetadata_RelayMetadata) String() string { func (*MACState_UplinkMessage_RxMetadata_RelayMetadata) ProtoMessage() {} func (x *MACState_UplinkMessage_RxMetadata_RelayMetadata) ProtoReflect() protoreflect.Message { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[52] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[53] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5093,7 +5150,7 @@ func (x *MACState_UplinkMessage_RxMetadata_RelayMetadata) ProtoReflect() protore // Deprecated: Use MACState_UplinkMessage_RxMetadata_RelayMetadata.ProtoReflect.Descriptor instead. func (*MACState_UplinkMessage_RxMetadata_RelayMetadata) Descriptor() ([]byte, []int) { - return file_ttn_lorawan_v3_end_device_proto_rawDescGZIP(), []int{14, 2, 1, 1} + return file_ttn_lorawan_v3_end_device_proto_rawDescGZIP(), []int{15, 2, 1, 1} } type MACState_DownlinkMessage_Message struct { @@ -5108,7 +5165,7 @@ type MACState_DownlinkMessage_Message struct { func (x *MACState_DownlinkMessage_Message) Reset() { *x = MACState_DownlinkMessage_Message{} if protoimpl.UnsafeEnabled { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[53] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[54] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5121,7 +5178,7 @@ func (x *MACState_DownlinkMessage_Message) String() string { func (*MACState_DownlinkMessage_Message) ProtoMessage() {} func (x *MACState_DownlinkMessage_Message) ProtoReflect() protoreflect.Message { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[53] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[54] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5134,7 +5191,7 @@ func (x *MACState_DownlinkMessage_Message) ProtoReflect() protoreflect.Message { // Deprecated: Use MACState_DownlinkMessage_Message.ProtoReflect.Descriptor instead. func (*MACState_DownlinkMessage_Message) Descriptor() ([]byte, []int) { - return file_ttn_lorawan_v3_end_device_proto_rawDescGZIP(), []int{14, 3, 0} + return file_ttn_lorawan_v3_end_device_proto_rawDescGZIP(), []int{15, 3, 0} } func (x *MACState_DownlinkMessage_Message) GetMHdr() *MACState_DownlinkMessage_Message_MHDR { @@ -5162,7 +5219,7 @@ type MACState_DownlinkMessage_Message_MHDR struct { func (x *MACState_DownlinkMessage_Message_MHDR) Reset() { *x = MACState_DownlinkMessage_Message_MHDR{} if protoimpl.UnsafeEnabled { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[54] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[55] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5175,7 +5232,7 @@ func (x *MACState_DownlinkMessage_Message_MHDR) String() string { func (*MACState_DownlinkMessage_Message_MHDR) ProtoMessage() {} func (x *MACState_DownlinkMessage_Message_MHDR) ProtoReflect() protoreflect.Message { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[54] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[55] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5188,7 +5245,7 @@ func (x *MACState_DownlinkMessage_Message_MHDR) ProtoReflect() protoreflect.Mess // Deprecated: Use MACState_DownlinkMessage_Message_MHDR.ProtoReflect.Descriptor instead. func (*MACState_DownlinkMessage_Message_MHDR) Descriptor() ([]byte, []int) { - return file_ttn_lorawan_v3_end_device_proto_rawDescGZIP(), []int{14, 3, 0, 0} + return file_ttn_lorawan_v3_end_device_proto_rawDescGZIP(), []int{15, 3, 0, 0} } func (x *MACState_DownlinkMessage_Message_MHDR) GetMType() MType { @@ -5210,7 +5267,7 @@ type MACState_DownlinkMessage_Message_MACPayload struct { func (x *MACState_DownlinkMessage_Message_MACPayload) Reset() { *x = MACState_DownlinkMessage_Message_MACPayload{} if protoimpl.UnsafeEnabled { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[55] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[56] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5223,7 +5280,7 @@ func (x *MACState_DownlinkMessage_Message_MACPayload) String() string { func (*MACState_DownlinkMessage_Message_MACPayload) ProtoMessage() {} func (x *MACState_DownlinkMessage_Message_MACPayload) ProtoReflect() protoreflect.Message { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[55] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[56] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5236,7 +5293,7 @@ func (x *MACState_DownlinkMessage_Message_MACPayload) ProtoReflect() protoreflec // Deprecated: Use MACState_DownlinkMessage_Message_MACPayload.ProtoReflect.Descriptor instead. func (*MACState_DownlinkMessage_Message_MACPayload) Descriptor() ([]byte, []int) { - return file_ttn_lorawan_v3_end_device_proto_rawDescGZIP(), []int{14, 3, 0, 1} + return file_ttn_lorawan_v3_end_device_proto_rawDescGZIP(), []int{15, 3, 0, 1} } func (x *MACState_DownlinkMessage_Message_MACPayload) GetFPort() uint32 { @@ -5265,7 +5322,7 @@ type BatchUpdateEndDeviceLastSeenRequest_EndDeviceLastSeenUpdate struct { func (x *BatchUpdateEndDeviceLastSeenRequest_EndDeviceLastSeenUpdate) Reset() { *x = BatchUpdateEndDeviceLastSeenRequest_EndDeviceLastSeenUpdate{} if protoimpl.UnsafeEnabled { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[58] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[59] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5278,7 +5335,7 @@ func (x *BatchUpdateEndDeviceLastSeenRequest_EndDeviceLastSeenUpdate) String() s func (*BatchUpdateEndDeviceLastSeenRequest_EndDeviceLastSeenUpdate) ProtoMessage() {} func (x *BatchUpdateEndDeviceLastSeenRequest_EndDeviceLastSeenUpdate) ProtoReflect() protoreflect.Message { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[58] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[59] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5291,7 +5348,7 @@ func (x *BatchUpdateEndDeviceLastSeenRequest_EndDeviceLastSeenUpdate) ProtoRefle // Deprecated: Use BatchUpdateEndDeviceLastSeenRequest_EndDeviceLastSeenUpdate.ProtoReflect.Descriptor instead. func (*BatchUpdateEndDeviceLastSeenRequest_EndDeviceLastSeenUpdate) Descriptor() ([]byte, []int) { - return file_ttn_lorawan_v3_end_device_proto_rawDescGZIP(), []int{21, 0} + return file_ttn_lorawan_v3_end_device_proto_rawDescGZIP(), []int{22, 0} } func (x *BatchUpdateEndDeviceLastSeenRequest_EndDeviceLastSeenUpdate) GetIds() *EndDeviceIdentifiers { @@ -5322,7 +5379,7 @@ type ListEndDevicesRequest_Filter struct { func (x *ListEndDevicesRequest_Filter) Reset() { *x = ListEndDevicesRequest_Filter{} if protoimpl.UnsafeEnabled { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[59] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[60] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5335,7 +5392,7 @@ func (x *ListEndDevicesRequest_Filter) String() string { func (*ListEndDevicesRequest_Filter) ProtoMessage() {} func (x *ListEndDevicesRequest_Filter) ProtoReflect() protoreflect.Message { - mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[59] + mi := &file_ttn_lorawan_v3_end_device_proto_msgTypes[60] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5348,7 +5405,7 @@ func (x *ListEndDevicesRequest_Filter) ProtoReflect() protoreflect.Message { // Deprecated: Use ListEndDevicesRequest_Filter.ProtoReflect.Descriptor instead. func (*ListEndDevicesRequest_Filter) Descriptor() ([]byte, []int) { - return file_ttn_lorawan_v3_end_device_proto_rawDescGZIP(), []int{24, 0} + return file_ttn_lorawan_v3_end_device_proto_rawDescGZIP(), []int{25, 0} } func (m *ListEndDevicesRequest_Filter) GetField() isListEndDevicesRequest_Filter_Field { @@ -6218,693 +6275,718 @@ var file_ttn_lorawan_v3_end_device_proto_rawDesc = []byte{ 0x0b, 0x32, 0x1d, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x0c, 0x64, 0x65, 0x73, 0x69, 0x72, 0x65, 0x64, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x3a, 0x08, - 0xf2, 0xaa, 0x19, 0x04, 0x08, 0x01, 0x10, 0x01, 0x22, 0xef, 0x29, 0x0a, 0x08, 0x4d, 0x41, 0x43, - 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x56, 0x0a, 0x12, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, - 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1d, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, - 0x76, 0x33, 0x2e, 0x4d, 0x41, 0x43, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, - 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x11, 0x63, 0x75, 0x72, 0x72, - 0x65, 0x6e, 0x74, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, 0x56, 0x0a, - 0x12, 0x64, 0x65, 0x73, 0x69, 0x72, 0x65, 0x64, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, - 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x74, 0x74, 0x6e, 0x2e, - 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4d, 0x41, 0x43, 0x50, 0x61, - 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, - 0x10, 0x01, 0x52, 0x11, 0x64, 0x65, 0x73, 0x69, 0x72, 0x65, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, - 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, 0x42, 0x0a, 0x0c, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, - 0x63, 0x6c, 0x61, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x74, 0x74, - 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x43, 0x6c, 0x61, - 0x73, 0x73, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x82, 0x01, 0x02, 0x10, 0x01, 0x52, 0x0b, 0x64, 0x65, - 0x76, 0x69, 0x63, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x12, 0x4d, 0x0a, 0x0f, 0x6c, 0x6f, 0x72, - 0x61, 0x77, 0x61, 0x6e, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, - 0x2e, 0x76, 0x33, 0x2e, 0x4d, 0x41, 0x43, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x42, 0x08, - 0xfa, 0x42, 0x05, 0x82, 0x01, 0x02, 0x10, 0x01, 0x52, 0x0e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, - 0x6e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x57, 0x0a, 0x1a, 0x6c, 0x61, 0x73, 0x74, - 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, 0x5f, 0x64, 0x6f, 0x77, 0x6e, 0x6c, - 0x69, 0x6e, 0x6b, 0x5f, 0x61, 0x74, 0x18, 0x05, 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, 0x17, 0x6c, 0x61, 0x73, 0x74, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x41, - 0x74, 0x12, 0x35, 0x0a, 0x18, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x64, 0x65, 0x76, 0x5f, 0x73, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x5f, 0x66, 0x5f, 0x63, 0x6e, 0x74, 0x5f, 0x75, 0x70, 0x18, 0x06, 0x20, - 0x01, 0x28, 0x0d, 0x52, 0x13, 0x6c, 0x61, 0x73, 0x74, 0x44, 0x65, 0x76, 0x53, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x46, 0x43, 0x6e, 0x74, 0x55, 0x70, 0x12, 0x57, 0x0a, 0x15, 0x70, 0x69, 0x6e, 0x67, - 0x5f, 0x73, 0x6c, 0x6f, 0x74, 0x5f, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x69, 0x63, 0x69, 0x74, - 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, - 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x53, 0x6c, 0x6f, - 0x74, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x13, 0x70, 0x69, - 0x6e, 0x67, 0x53, 0x6c, 0x6f, 0x74, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x69, 0x63, 0x69, 0x74, - 0x79, 0x12, 0x65, 0x0a, 0x1c, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x61, 0x70, 0x70, - 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, - 0x6b, 0x18, 0x08, 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, 0x52, 0x1a, 0x70, 0x65, - 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x12, 0x45, 0x0a, 0x10, 0x71, 0x75, 0x65, 0x75, - 0x65, 0x64, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x18, 0x09, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, - 0x2e, 0x76, 0x33, 0x2e, 0x4d, 0x41, 0x43, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x52, 0x0f, - 0x71, 0x75, 0x65, 0x75, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x12, - 0x45, 0x0a, 0x10, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x74, 0x74, 0x6e, 0x2e, - 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4d, 0x41, 0x43, 0x43, 0x6f, - 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x52, 0x0f, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x12, 0x51, 0x0a, 0x12, 0x71, 0x75, 0x65, 0x75, 0x65, 0x64, - 0x5f, 0x6a, 0x6f, 0x69, 0x6e, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x18, 0x0b, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, - 0x2e, 0x76, 0x33, 0x2e, 0x4d, 0x41, 0x43, 0x53, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x4a, 0x6f, 0x69, - 0x6e, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x52, 0x10, 0x71, 0x75, 0x65, 0x75, 0x65, 0x64, 0x4a, - 0x6f, 0x69, 0x6e, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x12, 0x56, 0x0a, 0x14, 0x70, 0x65, 0x6e, - 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x6a, 0x6f, 0x69, 0x6e, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, + 0xf2, 0xaa, 0x19, 0x04, 0x08, 0x01, 0x10, 0x01, 0x22, 0xbb, 0x01, 0x0a, 0x12, 0x4d, 0x41, 0x43, + 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x12, + 0x51, 0x0a, 0x03, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x74, + 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4d, 0x41, + 0x43, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, + 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x42, 0x10, 0xfa, 0x42, 0x05, + 0x8a, 0x01, 0x02, 0x10, 0x01, 0xf2, 0xaa, 0x19, 0x04, 0x08, 0x00, 0x28, 0x01, 0x52, 0x03, 0x69, + 0x64, 0x73, 0x12, 0x48, 0x0a, 0x0c, 0x6d, 0x61, 0x63, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, + 0x67, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, + 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4d, 0x41, 0x43, 0x53, 0x65, 0x74, + 0x74, 0x69, 0x6e, 0x67, 0x73, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, + 0x0b, 0x6d, 0x61, 0x63, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x3a, 0x08, 0xf2, 0xaa, + 0x19, 0x04, 0x08, 0x01, 0x10, 0x01, 0x22, 0xef, 0x29, 0x0a, 0x08, 0x4d, 0x41, 0x43, 0x53, 0x74, + 0x61, 0x74, 0x65, 0x12, 0x56, 0x0a, 0x12, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x70, + 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1d, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, + 0x2e, 0x4d, 0x41, 0x43, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x42, 0x08, + 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x11, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, + 0x74, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, 0x56, 0x0a, 0x12, 0x64, + 0x65, 0x73, 0x69, 0x72, 0x65, 0x64, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, + 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, + 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4d, 0x41, 0x43, 0x50, 0x61, 0x72, 0x61, + 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, + 0x52, 0x11, 0x64, 0x65, 0x73, 0x69, 0x72, 0x65, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, + 0x65, 0x72, 0x73, 0x12, 0x42, 0x0a, 0x0c, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x63, 0x6c, + 0x61, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x74, 0x74, 0x6e, 0x2e, + 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x43, 0x6c, 0x61, 0x73, 0x73, + 0x42, 0x08, 0xfa, 0x42, 0x05, 0x82, 0x01, 0x02, 0x10, 0x01, 0x52, 0x0b, 0x64, 0x65, 0x76, 0x69, + 0x63, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x12, 0x4d, 0x0a, 0x0f, 0x6c, 0x6f, 0x72, 0x61, 0x77, + 0x61, 0x6e, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, + 0x32, 0x1a, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, + 0x33, 0x2e, 0x4d, 0x41, 0x43, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x42, 0x08, 0xfa, 0x42, + 0x05, 0x82, 0x01, 0x02, 0x10, 0x01, 0x52, 0x0e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x56, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x57, 0x0a, 0x1a, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x63, + 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, 0x5f, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, + 0x6b, 0x5f, 0x61, 0x74, 0x18, 0x05, 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, 0x17, 0x6c, 0x61, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x72, 0x6d, 0x65, 0x64, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x41, 0x74, 0x12, + 0x35, 0x0a, 0x18, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x64, 0x65, 0x76, 0x5f, 0x73, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x5f, 0x66, 0x5f, 0x63, 0x6e, 0x74, 0x5f, 0x75, 0x70, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x13, 0x6c, 0x61, 0x73, 0x74, 0x44, 0x65, 0x76, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x46, 0x43, 0x6e, 0x74, 0x55, 0x70, 0x12, 0x57, 0x0a, 0x15, 0x70, 0x69, 0x6e, 0x67, 0x5f, 0x73, + 0x6c, 0x6f, 0x74, 0x5f, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x69, 0x63, 0x69, 0x74, 0x79, 0x18, + 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, + 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x53, 0x6c, 0x6f, 0x74, 0x50, + 0x65, 0x72, 0x69, 0x6f, 0x64, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x13, 0x70, 0x69, 0x6e, 0x67, + 0x53, 0x6c, 0x6f, 0x74, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x69, 0x63, 0x69, 0x74, 0x79, 0x12, + 0x65, 0x0a, 0x1c, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x61, 0x70, 0x70, 0x6c, 0x69, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x18, + 0x08, 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, 0x52, 0x1a, 0x70, 0x65, 0x6e, 0x64, + 0x69, 0x6e, 0x67, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x6f, + 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x12, 0x45, 0x0a, 0x10, 0x71, 0x75, 0x65, 0x75, 0x65, 0x64, + 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x1a, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, + 0x33, 0x2e, 0x4d, 0x41, 0x43, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x52, 0x0f, 0x71, 0x75, + 0x65, 0x75, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x12, 0x45, 0x0a, + 0x10, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, + 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4d, 0x41, 0x43, 0x43, 0x6f, 0x6d, 0x6d, + 0x61, 0x6e, 0x64, 0x52, 0x0f, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x73, 0x12, 0x51, 0x0a, 0x12, 0x71, 0x75, 0x65, 0x75, 0x65, 0x64, 0x5f, 0x6a, + 0x6f, 0x69, 0x6e, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x23, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, + 0x33, 0x2e, 0x4d, 0x41, 0x43, 0x53, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x4a, 0x6f, 0x69, 0x6e, 0x41, + 0x63, 0x63, 0x65, 0x70, 0x74, 0x52, 0x10, 0x71, 0x75, 0x65, 0x75, 0x65, 0x64, 0x4a, 0x6f, 0x69, + 0x6e, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x12, 0x56, 0x0a, 0x14, 0x70, 0x65, 0x6e, 0x64, 0x69, + 0x6e, 0x67, 0x5f, 0x6a, 0x6f, 0x69, 0x6e, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, + 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, + 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4d, 0x41, 0x43, 0x53, 0x74, 0x61, 0x74, 0x65, 0x2e, + 0x4a, 0x6f, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x12, 0x70, 0x65, 0x6e, + 0x64, 0x69, 0x6e, 0x67, 0x4a, 0x6f, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x30, 0x0a, 0x14, 0x72, 0x78, 0x5f, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x5f, 0x61, 0x76, + 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x72, + 0x78, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, + 0x65, 0x12, 0x4d, 0x0a, 0x0e, 0x72, 0x65, 0x63, 0x65, 0x6e, 0x74, 0x5f, 0x75, 0x70, 0x6c, 0x69, + 0x6e, 0x6b, 0x73, 0x18, 0x0e, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x74, 0x74, 0x6e, 0x2e, + 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4d, 0x41, 0x43, 0x53, 0x74, + 0x61, 0x74, 0x65, 0x2e, 0x55, 0x70, 0x6c, 0x69, 0x6e, 0x6b, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x52, 0x0d, 0x72, 0x65, 0x63, 0x65, 0x6e, 0x74, 0x55, 0x70, 0x6c, 0x69, 0x6e, 0x6b, 0x73, + 0x12, 0x53, 0x0a, 0x10, 0x72, 0x65, 0x63, 0x65, 0x6e, 0x74, 0x5f, 0x64, 0x6f, 0x77, 0x6e, 0x6c, + 0x69, 0x6e, 0x6b, 0x73, 0x18, 0x0f, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x74, 0x74, 0x6e, + 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4d, 0x41, 0x43, 0x53, + 0x74, 0x61, 0x74, 0x65, 0x2e, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x4d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x52, 0x0f, 0x72, 0x65, 0x63, 0x65, 0x6e, 0x74, 0x44, 0x6f, 0x77, 0x6e, + 0x6c, 0x69, 0x6e, 0x6b, 0x73, 0x12, 0x66, 0x0a, 0x22, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x6e, 0x65, + 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x5f, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x74, 0x65, 0x64, 0x5f, + 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x5f, 0x61, 0x74, 0x18, 0x10, 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, 0x1e, 0x6c, + 0x61, 0x73, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, + 0x74, 0x65, 0x64, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x41, 0x74, 0x12, 0x72, 0x0a, + 0x1e, 0x72, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x64, 0x72, 0x5f, 0x64, 0x61, + 0x74, 0x61, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x73, 0x18, + 0x11, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x1d, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, + 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x52, 0x61, 0x74, 0x65, 0x49, + 0x6e, 0x64, 0x65, 0x78, 0x42, 0x0f, 0xfa, 0x42, 0x0c, 0x92, 0x01, 0x09, 0x10, 0x0f, 0x22, 0x05, + 0x82, 0x01, 0x02, 0x10, 0x01, 0x52, 0x1a, 0x72, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x65, 0x64, 0x41, + 0x64, 0x72, 0x44, 0x61, 0x74, 0x61, 0x52, 0x61, 0x74, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x65, + 0x73, 0x12, 0x50, 0x0a, 0x1d, 0x72, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x64, + 0x72, 0x5f, 0x74, 0x78, 0x5f, 0x70, 0x6f, 0x77, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, + 0x65, 0x73, 0x18, 0x12, 0x20, 0x03, 0x28, 0x0d, 0x42, 0x0e, 0xfa, 0x42, 0x0b, 0x92, 0x01, 0x08, + 0x10, 0x0f, 0x22, 0x04, 0x2a, 0x02, 0x18, 0x0f, 0x52, 0x19, 0x72, 0x65, 0x6a, 0x65, 0x63, 0x74, + 0x65, 0x64, 0x41, 0x64, 0x72, 0x54, 0x78, 0x50, 0x6f, 0x77, 0x65, 0x72, 0x49, 0x6e, 0x64, 0x65, + 0x78, 0x65, 0x73, 0x12, 0x41, 0x0a, 0x14, 0x72, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, + 0x66, 0x72, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x18, 0x13, 0x20, 0x03, 0x28, + 0x04, 0x42, 0x0e, 0xfa, 0x42, 0x0b, 0x92, 0x01, 0x08, 0x22, 0x06, 0x32, 0x04, 0x28, 0xa0, 0x8d, + 0x06, 0x52, 0x13, 0x72, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x65, 0x64, 0x46, 0x72, 0x65, 0x71, 0x75, + 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x12, 0x44, 0x0a, 0x10, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x64, + 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x5f, 0x61, 0x74, 0x18, 0x14, 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, 0x0e, 0x6c, 0x61, + 0x73, 0x74, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x41, 0x74, 0x12, 0x6f, 0x0a, 0x19, + 0x72, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x72, 0x61, + 0x74, 0x65, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x18, 0x15, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x34, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, + 0x2e, 0x4d, 0x41, 0x43, 0x53, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x52, 0x65, 0x6a, 0x65, 0x63, 0x74, + 0x65, 0x64, 0x44, 0x61, 0x74, 0x61, 0x52, 0x61, 0x74, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x73, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x16, 0x72, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x65, 0x64, 0x44, + 0x61, 0x74, 0x61, 0x52, 0x61, 0x74, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x35, 0x0a, + 0x18, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x61, 0x64, 0x72, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, + 0x5f, 0x66, 0x5f, 0x63, 0x6e, 0x74, 0x5f, 0x75, 0x70, 0x18, 0x16, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x13, 0x6c, 0x61, 0x73, 0x74, 0x41, 0x64, 0x72, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x46, 0x43, + 0x6e, 0x74, 0x55, 0x70, 0x12, 0x69, 0x0a, 0x1e, 0x72, 0x65, 0x63, 0x65, 0x6e, 0x74, 0x5f, 0x6d, + 0x61, 0x63, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x5f, 0x69, 0x64, 0x65, 0x6e, 0x74, + 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x18, 0x17, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x24, 0x2e, 0x74, + 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4d, 0x41, + 0x43, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, + 0x65, 0x72, 0x52, 0x1b, 0x72, 0x65, 0x63, 0x65, 0x6e, 0x74, 0x4d, 0x61, 0x63, 0x43, 0x6f, 0x6d, + 0x6d, 0x61, 0x6e, 0x64, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x12, + 0x5d, 0x0a, 0x16, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x79, + 0x5f, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x18, 0x18, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x27, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, + 0x2e, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x44, 0x6f, 0x77, + 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x52, 0x65, 0x71, 0x52, 0x14, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, + 0x67, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x1a, 0x83, + 0x02, 0x0a, 0x0b, 0x4a, 0x6f, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x51, + 0x0a, 0x11, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, + 0x6e, 0x67, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x74, 0x74, 0x6e, 0x2e, + 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x44, 0x4c, 0x53, 0x65, 0x74, + 0x74, 0x69, 0x6e, 0x67, 0x73, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, + 0x10, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, + 0x73, 0x12, 0x3c, 0x0a, 0x08, 0x72, 0x78, 0x5f, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x18, 0x07, 0x20, + 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, + 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x52, 0x78, 0x44, 0x65, 0x6c, 0x61, 0x79, 0x42, 0x08, 0xfa, 0x42, + 0x05, 0x82, 0x01, 0x02, 0x10, 0x01, 0x52, 0x07, 0x72, 0x78, 0x44, 0x65, 0x6c, 0x61, 0x79, 0x12, + 0x2f, 0x0a, 0x07, 0x63, 0x66, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x16, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, + 0x33, 0x2e, 0x43, 0x46, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x06, 0x63, 0x66, 0x4c, 0x69, 0x73, 0x74, + 0x3a, 0x08, 0xf2, 0xaa, 0x19, 0x04, 0x08, 0x01, 0x10, 0x01, 0x4a, 0x04, 0x08, 0x01, 0x10, 0x02, + 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x4a, 0x04, 0x08, 0x04, + 0x10, 0x05, 0x4a, 0x04, 0x08, 0x05, 0x10, 0x06, 0x4a, 0x04, 0x08, 0x09, 0x10, 0x0a, 0x4a, 0x04, + 0x08, 0x0a, 0x10, 0x0b, 0x1a, 0xd1, 0x07, 0x0a, 0x0a, 0x4a, 0x6f, 0x69, 0x6e, 0x41, 0x63, 0x63, + 0x65, 0x70, 0x74, 0x12, 0x23, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0c, 0x42, 0x09, 0xfa, 0x42, 0x06, 0x7a, 0x04, 0x10, 0x11, 0x18, 0x21, 0x52, + 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x48, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x74, 0x74, 0x6e, 0x2e, + 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4d, 0x41, 0x43, 0x53, 0x74, + 0x61, 0x74, 0x65, 0x2e, 0x4a, 0x6f, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x42, + 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x39, 0x0a, 0x04, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1b, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, + 0x33, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x73, 0x42, 0x08, 0xfa, + 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x04, 0x6b, 0x65, 0x79, 0x73, 0x12, 0x35, 0x0a, + 0x0f, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x73, + 0x18, 0x04, 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, 0xed, 0x02, 0x0a, 0x08, 0x64, 0x65, 0x76, 0x5f, 0x61, 0x64, 0x64, + 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x42, 0xd1, 0x02, 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, 0xf2, 0xaa, 0x19, 0xa0, 0x01, 0x1a, 0x4e, 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, 0x63, 0x6d, 0x64, 0x2f, 0x74, 0x74, 0x6e, 0x2d, 0x6c, 0x77, 0x2d, 0x63, 0x6c, + 0x69, 0x2f, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x2e, 0x4e, 0x65, + 0x77, 0x34, 0x42, 0x79, 0x74, 0x65, 0x73, 0x46, 0x6c, 0x61, 0x67, 0x22, 0x4e, 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, 0x63, 0x6d, 0x64, 0x2f, 0x74, 0x74, 0x6e, 0x2d, 0x6c, 0x77, 0x2d, 0x63, 0x6c, + 0x69, 0x2f, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x2e, 0x47, 0x65, + 0x74, 0x45, 0x78, 0x61, 0x63, 0x74, 0x42, 0x79, 0x74, 0x65, 0x73, 0x52, 0x07, 0x64, 0x65, 0x76, + 0x41, 0x64, 0x64, 0x72, 0x12, 0xe7, 0x02, 0x0a, 0x06, 0x6e, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x0c, 0x42, 0xcf, 0x02, 0x92, 0x41, 0x17, 0x4a, 0x08, 0x22, 0x30, 0x30, + 0x30, 0x30, 0x31, 0x33, 0x22, 0x9a, 0x02, 0x01, 0x07, 0xa2, 0x02, 0x06, 0x73, 0x74, 0x72, 0x69, + 0x6e, 0x67, 0xfa, 0x42, 0x06, 0x7a, 0x04, 0x68, 0x03, 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, 0x33, 0x42, 0x79, 0x74, + 0x65, 0x73, 0xf2, 0xaa, 0x19, 0xa0, 0x01, 0x1a, 0x4e, 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, 0x63, + 0x6d, 0x64, 0x2f, 0x74, 0x74, 0x6e, 0x2d, 0x6c, 0x77, 0x2d, 0x63, 0x6c, 0x69, 0x2f, 0x63, 0x75, + 0x73, 0x74, 0x6f, 0x6d, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x2e, 0x4e, 0x65, 0x77, 0x33, 0x42, 0x79, + 0x74, 0x65, 0x73, 0x46, 0x6c, 0x61, 0x67, 0x22, 0x4e, 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, 0x63, + 0x6d, 0x64, 0x2f, 0x74, 0x74, 0x6e, 0x2d, 0x6c, 0x77, 0x2d, 0x63, 0x6c, 0x69, 0x2f, 0x63, 0x75, + 0x73, 0x74, 0x6f, 0x6d, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x61, + 0x63, 0x74, 0x42, 0x79, 0x74, 0x65, 0x73, 0x52, 0x05, 0x6e, 0x65, 0x74, 0x49, 0x64, 0x3a, 0x08, + 0xf2, 0xaa, 0x19, 0x04, 0x08, 0x01, 0x10, 0x01, 0x1a, 0xf3, 0x08, 0x0a, 0x0d, 0x55, 0x70, 0x6c, + 0x69, 0x6e, 0x6b, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x3b, 0x0a, 0x07, 0x70, 0x61, + 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x74, + 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x07, + 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x57, 0x0a, 0x08, 0x73, 0x65, 0x74, 0x74, 0x69, + 0x6e, 0x67, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x74, 0x74, 0x6e, 0x2e, + 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4d, 0x41, 0x43, 0x53, 0x74, + 0x61, 0x74, 0x65, 0x2e, 0x55, 0x70, 0x6c, 0x69, 0x6e, 0x6b, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 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, 0x52, 0x0a, 0x0b, 0x72, 0x78, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, + 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, + 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4d, 0x41, 0x43, 0x53, 0x74, 0x61, 0x74, 0x65, 0x2e, + 0x55, 0x70, 0x6c, 0x69, 0x6e, 0x6b, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x52, 0x78, + 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x0a, 0x72, 0x78, 0x4d, 0x65, 0x74, 0x61, + 0x64, 0x61, 0x74, 0x61, 0x12, 0x3b, 0x0a, 0x0b, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, + 0x5f, 0x61, 0x74, 0x18, 0x06, 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, 0x35, 0x0a, 0x0f, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x5f, 0x69, 0x64, 0x73, 0x18, 0x07, 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, 0x3a, 0x0a, 0x14, 0x64, 0x65, 0x76, 0x69, + 0x63, 0x65, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, + 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x2a, 0x03, 0x18, 0xff, 0x01, + 0x52, 0x12, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x49, + 0x6e, 0x64, 0x65, 0x78, 0x1a, 0x53, 0x0a, 0x0a, 0x54, 0x78, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, + 0x67, 0x73, 0x12, 0x3f, 0x0a, 0x09, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, + 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x52, 0x61, 0x74, 0x65, 0x42, + 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x08, 0x64, 0x61, 0x74, 0x61, 0x52, + 0x61, 0x74, 0x65, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x0a, 0x1a, 0xda, 0x04, 0x0a, 0x0a, 0x52, 0x78, + 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x4d, 0x0a, 0x0b, 0x67, 0x61, 0x74, 0x65, + 0x77, 0x61, 0x79, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, + 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x47, + 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, + 0x73, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x0a, 0x67, 0x61, 0x74, + 0x65, 0x77, 0x61, 0x79, 0x49, 0x64, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x68, 0x61, 0x6e, 0x6e, + 0x65, 0x6c, 0x5f, 0x72, 0x73, 0x73, 0x69, 0x18, 0x09, 0x20, 0x01, 0x28, 0x02, 0x52, 0x0b, 0x63, + 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x73, 0x73, 0x69, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x6e, + 0x72, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x02, 0x52, 0x03, 0x73, 0x6e, 0x72, 0x12, 0x6a, 0x0a, 0x18, + 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x5f, 0x63, 0x6f, + 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x74, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x26, + 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, + 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x50, 0x61, 0x74, 0x68, 0x43, 0x6f, 0x6e, 0x73, + 0x74, 0x72, 0x61, 0x69, 0x6e, 0x74, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x82, 0x01, 0x02, 0x10, 0x01, + 0x52, 0x16, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x50, 0x61, 0x74, 0x68, 0x43, 0x6f, + 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x75, 0x70, 0x6c, 0x69, + 0x6e, 0x6b, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, + 0x75, 0x70, 0x6c, 0x69, 0x6e, 0x6b, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x6b, 0x0a, 0x0d, 0x70, + 0x61, 0x63, 0x6b, 0x65, 0x74, 0x5f, 0x62, 0x72, 0x6f, 0x6b, 0x65, 0x72, 0x18, 0x12, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x46, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, + 0x2e, 0x76, 0x33, 0x2e, 0x4d, 0x41, 0x43, 0x53, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x55, 0x70, 0x6c, + 0x69, 0x6e, 0x6b, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x52, 0x78, 0x4d, 0x65, 0x74, + 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x50, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x42, 0x72, 0x6f, 0x6b, + 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x0c, 0x70, 0x61, 0x63, 0x6b, + 0x65, 0x74, 0x42, 0x72, 0x6f, 0x6b, 0x65, 0x72, 0x12, 0x55, 0x0a, 0x05, 0x72, 0x65, 0x6c, 0x61, + 0x79, 0x18, 0x17, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3f, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4d, 0x41, 0x43, 0x53, 0x74, 0x61, 0x74, - 0x65, 0x2e, 0x4a, 0x6f, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x12, 0x70, - 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x4a, 0x6f, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x30, 0x0a, 0x14, 0x72, 0x78, 0x5f, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x5f, - 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x12, 0x72, 0x78, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, - 0x62, 0x6c, 0x65, 0x12, 0x4d, 0x0a, 0x0e, 0x72, 0x65, 0x63, 0x65, 0x6e, 0x74, 0x5f, 0x75, 0x70, - 0x6c, 0x69, 0x6e, 0x6b, 0x73, 0x18, 0x0e, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x74, 0x74, - 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4d, 0x41, 0x43, - 0x53, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x55, 0x70, 0x6c, 0x69, 0x6e, 0x6b, 0x4d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x52, 0x0d, 0x72, 0x65, 0x63, 0x65, 0x6e, 0x74, 0x55, 0x70, 0x6c, 0x69, 0x6e, - 0x6b, 0x73, 0x12, 0x53, 0x0a, 0x10, 0x72, 0x65, 0x63, 0x65, 0x6e, 0x74, 0x5f, 0x64, 0x6f, 0x77, - 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x73, 0x18, 0x0f, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x74, + 0x65, 0x2e, 0x55, 0x70, 0x6c, 0x69, 0x6e, 0x6b, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, + 0x52, 0x78, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x52, 0x65, 0x6c, 0x61, 0x79, + 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x05, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x1a, + 0x1c, 0x0a, 0x14, 0x50, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x42, 0x72, 0x6f, 0x6b, 0x65, 0x72, 0x4d, + 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x4a, 0x04, 0x08, 0x01, 0x10, 0x0b, 0x1a, 0x15, 0x0a, + 0x0d, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x4a, 0x04, + 0x08, 0x01, 0x10, 0x04, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x09, 0x4a, 0x04, 0x08, 0x0a, 0x10, 0x0b, + 0x4a, 0x04, 0x08, 0x0c, 0x10, 0x0d, 0x4a, 0x04, 0x08, 0x0d, 0x10, 0x0e, 0x4a, 0x04, 0x08, 0x10, + 0x10, 0x11, 0x4a, 0x04, 0x08, 0x11, 0x10, 0x12, 0x4a, 0x04, 0x08, 0x13, 0x10, 0x14, 0x4a, 0x04, + 0x08, 0x14, 0x10, 0x15, 0x4a, 0x04, 0x08, 0x15, 0x10, 0x16, 0x4a, 0x04, 0x08, 0x16, 0x10, 0x17, + 0x4a, 0x04, 0x08, 0x63, 0x10, 0x64, 0x4a, 0x04, 0x08, 0x01, 0x10, 0x02, 0x4a, 0x04, 0x08, 0x03, + 0x10, 0x04, 0x4a, 0x04, 0x08, 0x08, 0x10, 0x09, 0x4a, 0x04, 0x08, 0x0a, 0x10, 0x0b, 0x1a, 0x97, + 0x04, 0x0a, 0x0f, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x4d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x12, 0x4a, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, + 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4d, 0x41, 0x43, 0x53, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x44, 0x6f, + 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x4d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x35, + 0x0a, 0x0f, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, + 0x73, 0x18, 0x06, 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, 0x1a, 0xee, 0x02, 0x0a, 0x07, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x12, 0x54, 0x0a, 0x05, 0x6d, 0x5f, 0x68, 0x64, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x35, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, + 0x33, 0x2e, 0x4d, 0x41, 0x43, 0x53, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x44, 0x6f, 0x77, 0x6e, 0x6c, + 0x69, 0x6e, 0x6b, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x2e, 0x4d, 0x48, 0x44, 0x52, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, + 0x01, 0x52, 0x04, 0x6d, 0x48, 0x64, 0x72, 0x12, 0x5c, 0x0a, 0x0b, 0x6d, 0x61, 0x63, 0x5f, 0x70, + 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3b, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4d, 0x41, 0x43, 0x53, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x4d, - 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x0f, 0x72, 0x65, 0x63, 0x65, 0x6e, 0x74, 0x44, 0x6f, - 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x73, 0x12, 0x66, 0x0a, 0x22, 0x6c, 0x61, 0x73, 0x74, 0x5f, - 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x5f, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x74, 0x65, - 0x64, 0x5f, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x5f, 0x61, 0x74, 0x18, 0x10, 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, - 0x1e, 0x6c, 0x61, 0x73, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x6e, 0x69, 0x74, - 0x69, 0x61, 0x74, 0x65, 0x64, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x41, 0x74, 0x12, - 0x72, 0x0a, 0x1e, 0x72, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x64, 0x72, 0x5f, - 0x64, 0x61, 0x74, 0x61, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x65, - 0x73, 0x18, 0x11, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x1d, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x4d, + 0x41, 0x43, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x0a, 0x6d, 0x61, 0x63, 0x50, 0x61, + 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x1a, 0x44, 0x0a, 0x04, 0x4d, 0x48, 0x44, 0x52, 0x12, 0x36, 0x0a, + 0x06, 0x6d, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, + 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4d, + 0x54, 0x79, 0x70, 0x65, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x82, 0x01, 0x02, 0x10, 0x01, 0x52, 0x05, + 0x6d, 0x54, 0x79, 0x70, 0x65, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x1a, 0x5d, 0x0a, 0x0a, 0x4d, + 0x41, 0x43, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x1f, 0x0a, 0x06, 0x66, 0x5f, 0x70, + 0x6f, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x2a, 0x03, + 0x18, 0xff, 0x01, 0x52, 0x05, 0x66, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x1c, 0x0a, 0x0a, 0x66, 0x75, + 0x6c, 0x6c, 0x5f, 0x66, 0x5f, 0x63, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, + 0x66, 0x75, 0x6c, 0x6c, 0x46, 0x43, 0x6e, 0x74, 0x4a, 0x04, 0x08, 0x01, 0x10, 0x02, 0x4a, 0x04, + 0x08, 0x03, 0x10, 0x04, 0x4a, 0x04, 0x08, 0x04, 0x10, 0x05, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, + 0x4a, 0x04, 0x08, 0x04, 0x10, 0x08, 0x4a, 0x04, 0x08, 0x01, 0x10, 0x02, 0x4a, 0x04, 0x08, 0x03, + 0x10, 0x06, 0x4a, 0x04, 0x08, 0x07, 0x10, 0x08, 0x1a, 0xc9, 0x01, 0x0a, 0x0d, 0x44, 0x61, 0x74, + 0x61, 0x52, 0x61, 0x74, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x56, 0x0a, 0x13, 0x6d, 0x69, + 0x6e, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x5f, 0x69, 0x6e, 0x64, 0x65, + 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1d, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x52, 0x61, 0x74, - 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x42, 0x0f, 0xfa, 0x42, 0x0c, 0x92, 0x01, 0x09, 0x10, 0x0f, - 0x22, 0x05, 0x82, 0x01, 0x02, 0x10, 0x01, 0x52, 0x1a, 0x72, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x65, - 0x64, 0x41, 0x64, 0x72, 0x44, 0x61, 0x74, 0x61, 0x52, 0x61, 0x74, 0x65, 0x49, 0x6e, 0x64, 0x65, - 0x78, 0x65, 0x73, 0x12, 0x50, 0x0a, 0x1d, 0x72, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, - 0x61, 0x64, 0x72, 0x5f, 0x74, 0x78, 0x5f, 0x70, 0x6f, 0x77, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, - 0x65, 0x78, 0x65, 0x73, 0x18, 0x12, 0x20, 0x03, 0x28, 0x0d, 0x42, 0x0e, 0xfa, 0x42, 0x0b, 0x92, - 0x01, 0x08, 0x10, 0x0f, 0x22, 0x04, 0x2a, 0x02, 0x18, 0x0f, 0x52, 0x19, 0x72, 0x65, 0x6a, 0x65, - 0x63, 0x74, 0x65, 0x64, 0x41, 0x64, 0x72, 0x54, 0x78, 0x50, 0x6f, 0x77, 0x65, 0x72, 0x49, 0x6e, - 0x64, 0x65, 0x78, 0x65, 0x73, 0x12, 0x41, 0x0a, 0x14, 0x72, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x65, - 0x64, 0x5f, 0x66, 0x72, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x18, 0x13, 0x20, - 0x03, 0x28, 0x04, 0x42, 0x0e, 0xfa, 0x42, 0x0b, 0x92, 0x01, 0x08, 0x22, 0x06, 0x32, 0x04, 0x28, - 0xa0, 0x8d, 0x06, 0x52, 0x13, 0x72, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x65, 0x64, 0x46, 0x72, 0x65, - 0x71, 0x75, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x12, 0x44, 0x0a, 0x10, 0x6c, 0x61, 0x73, 0x74, - 0x5f, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x5f, 0x61, 0x74, 0x18, 0x14, 0x20, 0x01, + 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x82, 0x01, 0x02, 0x10, 0x01, + 0x52, 0x10, 0x6d, 0x69, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x52, 0x61, 0x74, 0x65, 0x49, 0x6e, 0x64, + 0x65, 0x78, 0x12, 0x56, 0x0a, 0x13, 0x6d, 0x61, 0x78, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x72, + 0x61, 0x74, 0x65, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, + 0x1d, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, + 0x2e, 0x44, 0x61, 0x74, 0x61, 0x52, 0x61, 0x74, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x42, 0x08, + 0xfa, 0x42, 0x05, 0x82, 0x01, 0x02, 0x10, 0x01, 0x52, 0x10, 0x6d, 0x61, 0x78, 0x44, 0x61, 0x74, + 0x61, 0x52, 0x61, 0x74, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x3a, 0x08, 0xf2, 0xaa, 0x19, 0x04, + 0x08, 0x01, 0x10, 0x01, 0x1a, 0x64, 0x0a, 0x0e, 0x44, 0x61, 0x74, 0x61, 0x52, 0x61, 0x74, 0x65, + 0x52, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x48, 0x0a, 0x06, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, + 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4d, 0x41, 0x43, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x2e, 0x44, 0x61, 0x74, 0x61, 0x52, 0x61, 0x74, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x42, 0x08, + 0xfa, 0x42, 0x05, 0x92, 0x01, 0x02, 0x08, 0x01, 0x52, 0x06, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x73, + 0x3a, 0x08, 0xf2, 0xaa, 0x19, 0x04, 0x08, 0x01, 0x10, 0x01, 0x1a, 0x72, 0x0a, 0x1b, 0x52, 0x65, + 0x6a, 0x65, 0x63, 0x74, 0x65, 0x64, 0x44, 0x61, 0x74, 0x61, 0x52, 0x61, 0x74, 0x65, 0x52, 0x61, + 0x6e, 0x67, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x3d, 0x0a, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x74, 0x74, 0x6e, + 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4d, 0x41, 0x43, 0x53, + 0x74, 0x61, 0x74, 0x65, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x52, 0x61, 0x74, 0x65, 0x52, 0x61, 0x6e, + 0x67, 0x65, 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x3a, 0x08, + 0xf2, 0xaa, 0x19, 0x04, 0x08, 0x01, 0x10, 0x01, 0x22, 0xcb, 0x01, 0x0a, 0x1b, 0x45, 0x6e, 0x64, + 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x30, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x1a, 0xfa, 0x42, 0x17, 0x72, 0x15, 0x32, 0x13, + 0x5e, 0x5b, 0x61, 0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x30, 0x2d, 0x39, 0x5d, 0x7b, 0x31, 0x2c, 0x33, + 0x32, 0x7d, 0x24, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x39, 0x0a, 0x0a, 0x76, 0x61, + 0x6c, 0x69, 0x64, 0x5f, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x02, 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, 0x09, 0x76, 0x61, 0x6c, 0x69, + 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x12, 0x35, 0x0a, 0x08, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x5f, 0x74, + 0x6f, 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, 0x07, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x54, 0x6f, 0x3a, 0x08, 0xf2, 0xaa, + 0x19, 0x04, 0x08, 0x01, 0x10, 0x01, 0x22, 0xd7, 0x21, 0x0a, 0x09, 0x45, 0x6e, 0x64, 0x44, 0x65, + 0x76, 0x69, 0x63, 0x65, 0x12, 0x48, 0x0a, 0x03, 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, 0x10, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, + 0x01, 0xf2, 0xaa, 0x19, 0x04, 0x08, 0x00, 0x28, 0x01, 0x52, 0x03, 0x69, 0x64, 0x73, 0x12, 0x43, + 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x02, 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, 0x0e, - 0x6c, 0x61, 0x73, 0x74, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x41, 0x74, 0x12, 0x6f, - 0x0a, 0x19, 0x72, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x5f, - 0x72, 0x61, 0x74, 0x65, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x18, 0x15, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x34, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, - 0x76, 0x33, 0x2e, 0x4d, 0x41, 0x43, 0x53, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x52, 0x65, 0x6a, 0x65, - 0x63, 0x74, 0x65, 0x64, 0x44, 0x61, 0x74, 0x61, 0x52, 0x61, 0x74, 0x65, 0x52, 0x61, 0x6e, 0x67, - 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x16, 0x72, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x65, - 0x64, 0x44, 0x61, 0x74, 0x61, 0x52, 0x61, 0x74, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, - 0x35, 0x0a, 0x18, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x61, 0x64, 0x72, 0x5f, 0x63, 0x68, 0x61, 0x6e, - 0x67, 0x65, 0x5f, 0x66, 0x5f, 0x63, 0x6e, 0x74, 0x5f, 0x75, 0x70, 0x18, 0x16, 0x20, 0x01, 0x28, - 0x0d, 0x52, 0x13, 0x6c, 0x61, 0x73, 0x74, 0x41, 0x64, 0x72, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, - 0x46, 0x43, 0x6e, 0x74, 0x55, 0x70, 0x12, 0x69, 0x0a, 0x1e, 0x72, 0x65, 0x63, 0x65, 0x6e, 0x74, - 0x5f, 0x6d, 0x61, 0x63, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x5f, 0x69, 0x64, 0x65, - 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x18, 0x17, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x24, - 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, - 0x4d, 0x41, 0x43, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, - 0x66, 0x69, 0x65, 0x72, 0x52, 0x1b, 0x72, 0x65, 0x63, 0x65, 0x6e, 0x74, 0x4d, 0x61, 0x63, 0x43, - 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, - 0x73, 0x12, 0x5d, 0x0a, 0x16, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x72, 0x65, 0x6c, - 0x61, 0x79, 0x5f, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x18, 0x18, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x27, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, - 0x76, 0x33, 0x2e, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x44, - 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x52, 0x65, 0x71, 0x52, 0x14, 0x70, 0x65, 0x6e, 0x64, - 0x69, 0x6e, 0x67, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, - 0x1a, 0x83, 0x02, 0x0a, 0x0b, 0x4a, 0x6f, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x51, 0x0a, 0x11, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x5f, 0x73, 0x65, 0x74, - 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x74, 0x74, - 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x44, 0x4c, 0x53, - 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, - 0x01, 0x52, 0x10, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x53, 0x65, 0x74, 0x74, 0x69, - 0x6e, 0x67, 0x73, 0x12, 0x3c, 0x0a, 0x08, 0x72, 0x78, 0x5f, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x18, - 0x07, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, - 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x52, 0x78, 0x44, 0x65, 0x6c, 0x61, 0x79, 0x42, 0x08, - 0xfa, 0x42, 0x05, 0x82, 0x01, 0x02, 0x10, 0x01, 0x52, 0x07, 0x72, 0x78, 0x44, 0x65, 0x6c, 0x61, - 0x79, 0x12, 0x2f, 0x0a, 0x07, 0x63, 0x66, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x08, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, - 0x2e, 0x76, 0x33, 0x2e, 0x43, 0x46, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x06, 0x63, 0x66, 0x4c, 0x69, - 0x73, 0x74, 0x3a, 0x08, 0xf2, 0xaa, 0x19, 0x04, 0x08, 0x01, 0x10, 0x01, 0x4a, 0x04, 0x08, 0x01, - 0x10, 0x02, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x4a, 0x04, - 0x08, 0x04, 0x10, 0x05, 0x4a, 0x04, 0x08, 0x05, 0x10, 0x06, 0x4a, 0x04, 0x08, 0x09, 0x10, 0x0a, - 0x4a, 0x04, 0x08, 0x0a, 0x10, 0x0b, 0x1a, 0xd1, 0x07, 0x0a, 0x0a, 0x4a, 0x6f, 0x69, 0x6e, 0x41, - 0x63, 0x63, 0x65, 0x70, 0x74, 0x12, 0x23, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x09, 0xfa, 0x42, 0x06, 0x7a, 0x04, 0x10, 0x11, 0x18, - 0x21, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x48, 0x0a, 0x07, 0x72, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x74, 0x74, - 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4d, 0x41, 0x43, - 0x53, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x4a, 0x6f, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x07, 0x72, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x39, 0x0a, 0x04, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, - 0x2e, 0x76, 0x33, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x73, 0x42, - 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x04, 0x6b, 0x65, 0x79, 0x73, 0x12, - 0x35, 0x0a, 0x0f, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, - 0x64, 0x73, 0x18, 0x04, 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, 0xed, 0x02, 0x0a, 0x08, 0x64, 0x65, 0x76, 0x5f, 0x61, - 0x64, 0x64, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x42, 0xd1, 0x02, 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, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x42, 0x08, + 0xf2, 0xaa, 0x19, 0x04, 0x08, 0x00, 0x10, 0x00, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, + 0x64, 0x41, 0x74, 0x12, 0x43, 0x0a, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 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, 0x42, 0x08, 0xf2, 0xaa, 0x19, 0x04, 0x08, 0x00, 0x10, 0x00, 0x52, 0x09, 0x75, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x1b, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x18, 0x32, 0x52, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2a, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x72, + 0x03, 0x18, 0xd0, 0x0f, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, + 0x6e, 0x12, 0x80, 0x01, 0x0a, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, + 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 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, 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, 0x12, 0x4c, 0x0a, 0x0b, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x5f, + 0x69, 0x64, 0x73, 0x18, 0x07, 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, 0x35, 0x0a, 0x12, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x70, 0x72, + 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, + 0xfa, 0x42, 0x04, 0x72, 0x02, 0x18, 0x40, 0x52, 0x10, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x49, 0x64, 0x12, 0xc5, 0x01, 0x0a, 0x16, 0x6e, 0x65, + 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x61, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x42, 0x8e, 0x01, 0xfa, 0x42, 0x8a, + 0x01, 0x72, 0x87, 0x01, 0x32, 0x84, 0x01, 0x5e, 0x28, 0x3f, 0x3a, 0x28, 0x3f, 0x3a, 0x5b, 0x61, + 0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x30, 0x2d, 0x39, 0x5d, 0x7c, 0x5b, 0x61, 0x2d, 0x7a, 0x41, 0x2d, + 0x5a, 0x30, 0x2d, 0x39, 0x5d, 0x5b, 0x61, 0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x30, 0x2d, 0x39, 0x5c, + 0x2d, 0x5d, 0x2a, 0x5b, 0x61, 0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x30, 0x2d, 0x39, 0x5d, 0x29, 0x5c, + 0x2e, 0x29, 0x2a, 0x28, 0x3f, 0x3a, 0x5b, 0x41, 0x2d, 0x5a, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, + 0x5d, 0x7c, 0x5b, 0x41, 0x2d, 0x5a, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x5b, 0x41, 0x2d, + 0x5a, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5c, 0x2d, 0x5d, 0x2a, 0x5b, 0x41, 0x2d, 0x5a, 0x61, + 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x29, 0x28, 0x3f, 0x3a, 0x3a, 0x5b, 0x30, 0x2d, 0x39, 0x5d, + 0x7b, 0x31, 0x2c, 0x35, 0x7d, 0x29, 0x3f, 0x24, 0x7c, 0x5e, 0x24, 0x52, 0x14, 0x6e, 0x65, 0x74, + 0x77, 0x6f, 0x72, 0x6b, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, + 0x73, 0x12, 0x41, 0x0a, 0x18, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x5f, 0x73, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x5f, 0x6b, 0x65, 0x6b, 0x5f, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x18, 0x2f, 0x20, + 0x01, 0x28, 0x09, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x72, 0x03, 0x18, 0x80, 0x10, 0x52, 0x15, 0x6e, + 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4b, 0x65, 0x6b, 0x4c, + 0x61, 0x62, 0x65, 0x6c, 0x12, 0xcd, 0x01, 0x0a, 0x1a, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x61, 0x64, 0x64, 0x72, + 0x65, 0x73, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x42, 0x8e, 0x01, 0xfa, 0x42, 0x8a, 0x01, + 0x72, 0x87, 0x01, 0x32, 0x84, 0x01, 0x5e, 0x28, 0x3f, 0x3a, 0x28, 0x3f, 0x3a, 0x5b, 0x61, 0x2d, + 0x7a, 0x41, 0x2d, 0x5a, 0x30, 0x2d, 0x39, 0x5d, 0x7c, 0x5b, 0x61, 0x2d, 0x7a, 0x41, 0x2d, 0x5a, + 0x30, 0x2d, 0x39, 0x5d, 0x5b, 0x61, 0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x30, 0x2d, 0x39, 0x5c, 0x2d, + 0x5d, 0x2a, 0x5b, 0x61, 0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x30, 0x2d, 0x39, 0x5d, 0x29, 0x5c, 0x2e, + 0x29, 0x2a, 0x28, 0x3f, 0x3a, 0x5b, 0x41, 0x2d, 0x5a, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, + 0x7c, 0x5b, 0x41, 0x2d, 0x5a, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x5b, 0x41, 0x2d, 0x5a, + 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5c, 0x2d, 0x5d, 0x2a, 0x5b, 0x41, 0x2d, 0x5a, 0x61, 0x2d, + 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x29, 0x28, 0x3f, 0x3a, 0x3a, 0x5b, 0x30, 0x2d, 0x39, 0x5d, 0x7b, + 0x31, 0x2c, 0x35, 0x7d, 0x29, 0x3f, 0x24, 0x7c, 0x5e, 0x24, 0x52, 0x18, 0x61, 0x70, 0x70, 0x6c, + 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x41, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x12, 0x49, 0x0a, 0x1c, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x6b, 0x65, 0x6b, 0x5f, 0x6c, + 0x61, 0x62, 0x65, 0x6c, 0x18, 0x30, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x72, + 0x03, 0x18, 0x80, 0x10, 0x52, 0x19, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4b, 0x65, 0x6b, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x12, + 0x3b, 0x0a, 0x15, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x73, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x31, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, + 0xfa, 0x42, 0x04, 0x72, 0x02, 0x18, 0x64, 0x52, 0x13, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x49, 0x64, 0x12, 0xbf, 0x01, 0x0a, + 0x13, 0x6a, 0x6f, 0x69, 0x6e, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x61, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x42, 0x8e, 0x01, 0xfa, 0x42, 0x8a, + 0x01, 0x72, 0x87, 0x01, 0x32, 0x84, 0x01, 0x5e, 0x28, 0x3f, 0x3a, 0x28, 0x3f, 0x3a, 0x5b, 0x61, + 0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x30, 0x2d, 0x39, 0x5d, 0x7c, 0x5b, 0x61, 0x2d, 0x7a, 0x41, 0x2d, + 0x5a, 0x30, 0x2d, 0x39, 0x5d, 0x5b, 0x61, 0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x30, 0x2d, 0x39, 0x5c, + 0x2d, 0x5d, 0x2a, 0x5b, 0x61, 0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x30, 0x2d, 0x39, 0x5d, 0x29, 0x5c, + 0x2e, 0x29, 0x2a, 0x28, 0x3f, 0x3a, 0x5b, 0x41, 0x2d, 0x5a, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, + 0x5d, 0x7c, 0x5b, 0x41, 0x2d, 0x5a, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x5b, 0x41, 0x2d, + 0x5a, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5c, 0x2d, 0x5d, 0x2a, 0x5b, 0x41, 0x2d, 0x5a, 0x61, + 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x29, 0x28, 0x3f, 0x3a, 0x3a, 0x5b, 0x30, 0x2d, 0x39, 0x5d, + 0x7b, 0x31, 0x2c, 0x35, 0x7d, 0x29, 0x3f, 0x24, 0x7c, 0x5e, 0x24, 0x52, 0x11, 0x6a, 0x6f, 0x69, + 0x6e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x74, + 0x0a, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x0c, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x28, 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, 0x2e, 0x4c, 0x6f, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x42, 0x2c, 0xfa, 0x42, 0x29, + 0x9a, 0x01, 0x26, 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, 0x52, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x31, 0x0a, 0x07, 0x70, 0x69, 0x63, 0x74, 0x75, 0x72, 0x65, 0x18, + 0x32, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, + 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x50, 0x69, 0x63, 0x74, 0x75, 0x72, 0x65, 0x52, 0x07, + 0x70, 0x69, 0x63, 0x74, 0x75, 0x72, 0x65, 0x12, 0x28, 0x0a, 0x10, 0x73, 0x75, 0x70, 0x70, 0x6f, + 0x72, 0x74, 0x73, 0x5f, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x5f, 0x62, 0x18, 0x0d, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x0e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x43, 0x6c, 0x61, 0x73, 0x73, + 0x42, 0x12, 0x28, 0x0a, 0x10, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x5f, 0x63, 0x6c, + 0x61, 0x73, 0x73, 0x5f, 0x63, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x73, 0x75, 0x70, + 0x70, 0x6f, 0x72, 0x74, 0x73, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x43, 0x12, 0x4d, 0x0a, 0x0f, 0x6c, + 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x0f, + 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, + 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4d, 0x41, 0x43, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, + 0x42, 0x08, 0xfa, 0x42, 0x05, 0x82, 0x01, 0x02, 0x10, 0x01, 0x52, 0x0e, 0x6c, 0x6f, 0x72, 0x61, + 0x77, 0x61, 0x6e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x54, 0x0a, 0x13, 0x6c, 0x6f, + 0x72, 0x61, 0x77, 0x61, 0x6e, 0x5f, 0x70, 0x68, 0x79, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, + 0x6e, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, + 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x50, 0x48, 0x59, 0x56, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x82, 0x01, 0x02, 0x10, 0x01, 0x52, 0x11, 0x6c, + 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x50, 0x68, 0x79, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, + 0x12, 0x33, 0x0a, 0x11, 0x66, 0x72, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x79, 0x5f, 0x70, 0x6c, + 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x11, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, + 0x72, 0x02, 0x18, 0x40, 0x52, 0x0f, 0x66, 0x72, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x79, 0x50, + 0x6c, 0x61, 0x6e, 0x49, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x6d, 0x69, 0x6e, 0x5f, 0x66, 0x72, 0x65, + 0x71, 0x75, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x12, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x6d, 0x69, + 0x6e, 0x46, 0x72, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x23, 0x0a, 0x0d, 0x6d, 0x61, + 0x78, 0x5f, 0x66, 0x72, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x13, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x0c, 0x6d, 0x61, 0x78, 0x46, 0x72, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x79, 0x12, + 0x23, 0x0a, 0x0d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x5f, 0x6a, 0x6f, 0x69, 0x6e, + 0x18, 0x14, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x73, + 0x4a, 0x6f, 0x69, 0x6e, 0x12, 0x2c, 0x0a, 0x12, 0x72, 0x65, 0x73, 0x65, 0x74, 0x73, 0x5f, 0x6a, + 0x6f, 0x69, 0x6e, 0x5f, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x73, 0x18, 0x15, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x10, 0x72, 0x65, 0x73, 0x65, 0x74, 0x73, 0x4a, 0x6f, 0x69, 0x6e, 0x4e, 0x6f, 0x6e, 0x63, + 0x65, 0x73, 0x12, 0x35, 0x0a, 0x09, 0x72, 0x6f, 0x6f, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x18, + 0x16, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, + 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x52, 0x6f, 0x6f, 0x74, 0x4b, 0x65, 0x79, 0x73, 0x52, + 0x08, 0x72, 0x6f, 0x6f, 0x74, 0x4b, 0x65, 0x79, 0x73, 0x12, 0xe7, 0x02, 0x0a, 0x06, 0x6e, 0x65, + 0x74, 0x5f, 0x69, 0x64, 0x18, 0x17, 0x20, 0x01, 0x28, 0x0c, 0x42, 0xcf, 0x02, 0x92, 0x41, 0x17, + 0x4a, 0x08, 0x22, 0x30, 0x30, 0x30, 0x30, 0x31, 0x33, 0x22, 0x9a, 0x02, 0x01, 0x07, 0xa2, 0x02, + 0x06, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0xfa, 0x42, 0x06, 0x7a, 0x04, 0x68, 0x03, 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, 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, 0xf2, 0xaa, 0x19, 0xa0, 0x01, 0x1a, 0x4e, 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, 0x63, 0x6d, 0x64, 0x2f, 0x74, 0x74, 0x6e, 0x2d, 0x6c, 0x77, 0x2d, - 0x63, 0x6c, 0x69, 0x2f, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x2e, - 0x4e, 0x65, 0x77, 0x34, 0x42, 0x79, 0x74, 0x65, 0x73, 0x46, 0x6c, 0x61, 0x67, 0x22, 0x4e, 0x67, + 0x67, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x55, 0x6e, 0x6d, 0x61, 0x72, 0x73, 0x68, 0x61, + 0x6c, 0x33, 0x42, 0x79, 0x74, 0x65, 0x73, 0xf2, 0xaa, 0x19, 0xa0, 0x01, 0x1a, 0x4e, 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, 0x63, 0x6d, 0x64, 0x2f, 0x74, 0x74, 0x6e, 0x2d, 0x6c, 0x77, 0x2d, 0x63, + 0x6c, 0x69, 0x2f, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x2e, 0x4e, + 0x65, 0x77, 0x33, 0x42, 0x79, 0x74, 0x65, 0x73, 0x46, 0x6c, 0x61, 0x67, 0x22, 0x4e, 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, 0x63, 0x6d, 0x64, 0x2f, 0x74, 0x74, 0x6e, 0x2d, 0x6c, 0x77, 0x2d, 0x63, + 0x6c, 0x69, 0x2f, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x2e, 0x47, + 0x65, 0x74, 0x45, 0x78, 0x61, 0x63, 0x74, 0x42, 0x79, 0x74, 0x65, 0x73, 0x52, 0x05, 0x6e, 0x65, + 0x74, 0x49, 0x64, 0x12, 0x3e, 0x0a, 0x0c, 0x6d, 0x61, 0x63, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, + 0x6e, 0x67, 0x73, 0x18, 0x18, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x74, 0x74, 0x6e, 0x2e, + 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4d, 0x41, 0x43, 0x53, 0x65, + 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x0b, 0x6d, 0x61, 0x63, 0x53, 0x65, 0x74, 0x74, 0x69, + 0x6e, 0x67, 0x73, 0x12, 0x35, 0x0a, 0x09, 0x6d, 0x61, 0x63, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, + 0x18, 0x19, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, + 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4d, 0x41, 0x43, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x52, 0x08, 0x6d, 0x61, 0x63, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x44, 0x0a, 0x11, 0x70, 0x65, + 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x6d, 0x61, 0x63, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, + 0x2c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, + 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4d, 0x41, 0x43, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, + 0x0f, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x4d, 0x61, 0x63, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x12, 0x31, 0x0a, 0x07, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x1a, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, + 0x76, 0x33, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x73, 0x65, 0x73, 0x73, + 0x69, 0x6f, 0x6e, 0x12, 0x40, 0x0a, 0x0f, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x73, + 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x1b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, + 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x53, 0x65, + 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x0e, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x53, 0x65, + 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x24, 0x0a, 0x0e, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x64, 0x65, + 0x76, 0x5f, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x18, 0x1c, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x6c, + 0x61, 0x73, 0x74, 0x44, 0x65, 0x76, 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x12, 0x26, 0x0a, 0x0f, 0x75, + 0x73, 0x65, 0x64, 0x5f, 0x64, 0x65, 0x76, 0x5f, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x73, 0x18, 0x1d, + 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0d, 0x75, 0x73, 0x65, 0x64, 0x44, 0x65, 0x76, 0x4e, 0x6f, 0x6e, + 0x63, 0x65, 0x73, 0x12, 0x26, 0x0a, 0x0f, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x6a, 0x6f, 0x69, 0x6e, + 0x5f, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x6c, 0x61, + 0x73, 0x74, 0x4a, 0x6f, 0x69, 0x6e, 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x12, 0x25, 0x0a, 0x0f, 0x6c, + 0x61, 0x73, 0x74, 0x5f, 0x72, 0x6a, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x30, 0x18, 0x1f, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x6c, 0x61, 0x73, 0x74, 0x52, 0x6a, 0x43, 0x6f, 0x75, 0x6e, + 0x74, 0x30, 0x12, 0x25, 0x0a, 0x0f, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x72, 0x6a, 0x5f, 0x63, 0x6f, + 0x75, 0x6e, 0x74, 0x5f, 0x31, 0x18, 0x20, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x6c, 0x61, 0x73, + 0x74, 0x52, 0x6a, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x31, 0x12, 0x58, 0x0a, 0x1b, 0x6c, 0x61, 0x73, + 0x74, 0x5f, 0x64, 0x65, 0x76, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, 0x72, 0x65, 0x63, + 0x65, 0x69, 0x76, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x21, 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, 0x17, 0x6c, 0x61, 0x73, 0x74, + 0x44, 0x65, 0x76, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, + 0x64, 0x41, 0x74, 0x12, 0x4b, 0x0a, 0x0b, 0x70, 0x6f, 0x77, 0x65, 0x72, 0x5f, 0x73, 0x74, 0x61, + 0x74, 0x65, 0x18, 0x22, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, + 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x50, 0x6f, 0x77, 0x65, 0x72, 0x53, + 0x74, 0x61, 0x74, 0x65, 0x42, 0x0e, 0xfa, 0x42, 0x05, 0x82, 0x01, 0x02, 0x10, 0x01, 0xf2, 0xaa, + 0x19, 0x02, 0x10, 0x00, 0x52, 0x0a, 0x70, 0x6f, 0x77, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x12, 0x5b, 0x0a, 0x12, 0x62, 0x61, 0x74, 0x74, 0x65, 0x72, 0x79, 0x5f, 0x70, 0x65, 0x72, 0x63, + 0x65, 0x6e, 0x74, 0x61, 0x67, 0x65, 0x18, 0x23, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, + 0x6c, 0x6f, 0x61, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x0f, 0xfa, 0x42, 0x0c, 0x0a, 0x0a, + 0x1d, 0x00, 0x00, 0x80, 0x3f, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x52, 0x11, 0x62, 0x61, 0x74, 0x74, + 0x65, 0x72, 0x79, 0x50, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x61, 0x67, 0x65, 0x12, 0x27, 0x0a, + 0x0f, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x5f, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, + 0x18, 0x24, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, + 0x4d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x12, 0x65, 0x0a, 0x1c, 0x71, 0x75, 0x65, 0x75, 0x65, 0x64, + 0x5f, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x64, 0x6f, 0x77, + 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x73, 0x18, 0x28, 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, 0x1a, 0x71, 0x75, 0x65, 0x75, 0x65, 0x64, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x73, 0x12, 0x48, 0x0a, + 0x0a, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x74, 0x65, 0x72, 0x73, 0x18, 0x29, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x28, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, + 0x76, 0x33, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, + 0x64, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x74, 0x65, 0x72, 0x73, 0x52, 0x0a, 0x66, 0x6f, 0x72, + 0x6d, 0x61, 0x74, 0x74, 0x65, 0x72, 0x73, 0x12, 0x51, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x76, 0x69, + 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x2a, 0x20, 0x01, 0x28, 0x09, 0x42, + 0x2a, 0xfa, 0x42, 0x27, 0x72, 0x25, 0x18, 0x24, 0x32, 0x21, 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, 0x7c, 0x5e, 0x24, 0x52, 0x0d, 0x70, 0x72, 0x6f, + 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x49, 0x64, 0x12, 0x44, 0x0a, 0x11, 0x70, 0x72, + 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x69, 0x6e, 0x67, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, + 0x2b, 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, 0x10, + 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x69, 0x6e, 0x67, 0x44, 0x61, 0x74, 0x61, + 0x12, 0x1c, 0x0a, 0x09, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x63, 0x61, 0x73, 0x74, 0x18, 0x2d, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x09, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x63, 0x61, 0x73, 0x74, 0x12, 0x67, + 0x0a, 0x19, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x2e, 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, 0x41, 0x75, 0x74, 0x68, + 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x64, 0x65, 0x52, 0x17, + 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x2e, 0x0a, 0x13, 0x73, 0x6b, 0x69, 0x70, 0x5f, + 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x18, 0x33, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x73, 0x6b, 0x69, 0x70, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, + 0x64, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x12, 0x5b, 0x0a, 0x1c, 0x73, 0x6b, 0x69, 0x70, 0x5f, + 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x5f, 0x6f, + 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x18, 0x34, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, + 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x19, 0x73, 0x6b, 0x69, 0x70, 0x50, + 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x4f, 0x76, 0x65, 0x72, + 0x72, 0x69, 0x64, 0x65, 0x12, 0x3d, 0x0a, 0x0c, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, + 0x64, 0x5f, 0x61, 0x74, 0x18, 0x35, 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, 0x0b, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, + 0x64, 0x41, 0x74, 0x12, 0x3c, 0x0a, 0x0c, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x65, 0x6e, + 0x5f, 0x61, 0x74, 0x18, 0x36, 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, 0x6c, 0x61, 0x73, 0x74, 0x53, 0x65, 0x65, 0x6e, 0x41, + 0x74, 0x12, 0x4f, 0x0a, 0x0d, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x5f, 0x6e, 0x75, 0x6d, 0x62, + 0x65, 0x72, 0x18, 0x37, 0x20, 0x01, 0x28, 0x09, 0x42, 0x2a, 0xfa, 0x42, 0x27, 0x72, 0x25, 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, 0xd0, 0x01, 0x01, 0x52, 0x0c, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x4e, 0x75, 0x6d, 0x62, + 0x65, 0x72, 0x12, 0x69, 0x0a, 0x19, 0x6c, 0x6f, 0x72, 0x61, 0x5f, 0x61, 0x6c, 0x6c, 0x69, 0x61, + 0x6e, 0x63, 0x65, 0x5f, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x73, 0x18, + 0x38, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, + 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4c, 0x6f, 0x52, 0x61, 0x41, 0x6c, 0x6c, 0x69, 0x61, + 0x6e, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, + 0x66, 0x69, 0x65, 0x72, 0x73, 0x52, 0x16, 0x6c, 0x6f, 0x72, 0x61, 0x41, 0x6c, 0x6c, 0x69, 0x61, + 0x6e, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x49, 0x64, 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, 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, 0x4a, 0x04, + 0x08, 0x25, 0x10, 0x26, 0x4a, 0x04, 0x08, 0x26, 0x10, 0x27, 0x4a, 0x04, 0x08, 0x27, 0x10, 0x28, + 0x22, 0x48, 0x0a, 0x0a, 0x45, 0x6e, 0x64, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x12, 0x3a, + 0x0a, 0x0b, 0x65, 0x6e, 0x64, 0x5f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x19, 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, 0x52, 0x0a, + 0x65, 0x6e, 0x64, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x22, 0xf2, 0x01, 0x0a, 0x0d, 0x44, + 0x65, 0x76, 0x41, 0x64, 0x64, 0x72, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 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, 0x63, 0x6d, 0x64, 0x2f, 0x74, 0x74, 0x6e, 0x2d, 0x6c, 0x77, 0x2d, - 0x63, 0x6c, 0x69, 0x2f, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x2e, - 0x47, 0x65, 0x74, 0x45, 0x78, 0x61, 0x63, 0x74, 0x42, 0x79, 0x74, 0x65, 0x73, 0x52, 0x07, 0x64, - 0x65, 0x76, 0x41, 0x64, 0x64, 0x72, 0x12, 0xe7, 0x02, 0x0a, 0x06, 0x6e, 0x65, 0x74, 0x5f, 0x69, - 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x42, 0xcf, 0x02, 0x92, 0x41, 0x17, 0x4a, 0x08, 0x22, - 0x30, 0x30, 0x30, 0x30, 0x31, 0x33, 0x22, 0x9a, 0x02, 0x01, 0x07, 0xa2, 0x02, 0x06, 0x73, 0x74, - 0x72, 0x69, 0x6e, 0x67, 0xfa, 0x42, 0x06, 0x7a, 0x04, 0x68, 0x03, 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, 0x33, 0x42, - 0x79, 0x74, 0x65, 0x73, 0xf2, 0xaa, 0x19, 0xa0, 0x01, 0x1a, 0x4e, 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, 0x63, 0x6d, 0x64, 0x2f, 0x74, 0x74, 0x6e, 0x2d, 0x6c, 0x77, 0x2d, 0x63, 0x6c, 0x69, 0x2f, - 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x2e, 0x4e, 0x65, 0x77, 0x33, - 0x42, 0x79, 0x74, 0x65, 0x73, 0x46, 0x6c, 0x61, 0x67, 0x22, 0x4e, 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, 0x63, 0x6d, 0x64, 0x2f, 0x74, 0x74, 0x6e, 0x2d, 0x6c, 0x77, 0x2d, 0x63, 0x6c, 0x69, 0x2f, - 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x45, - 0x78, 0x61, 0x63, 0x74, 0x42, 0x79, 0x74, 0x65, 0x73, 0x52, 0x05, 0x6e, 0x65, 0x74, 0x49, 0x64, - 0x3a, 0x08, 0xf2, 0xaa, 0x19, 0x04, 0x08, 0x01, 0x10, 0x01, 0x1a, 0xf3, 0x08, 0x0a, 0x0d, 0x55, - 0x70, 0x6c, 0x69, 0x6e, 0x6b, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x3b, 0x0a, 0x07, - 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, - 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4d, - 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, - 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x57, 0x0a, 0x08, 0x73, 0x65, 0x74, - 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x74, 0x74, - 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4d, 0x41, 0x43, - 0x53, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x55, 0x70, 0x6c, 0x69, 0x6e, 0x6b, 0x4d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 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, 0x52, 0x0a, 0x0b, 0x72, 0x78, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, - 0x61, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, - 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4d, 0x41, 0x43, 0x53, 0x74, 0x61, 0x74, - 0x65, 0x2e, 0x55, 0x70, 0x6c, 0x69, 0x6e, 0x6b, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, - 0x52, 0x78, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x0a, 0x72, 0x78, 0x4d, 0x65, - 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x3b, 0x0a, 0x0b, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, - 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x06, 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, 0x35, 0x0a, 0x0f, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x07, 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, 0x3a, 0x0a, 0x14, 0x64, 0x65, - 0x76, 0x69, 0x63, 0x65, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x69, 0x6e, 0x64, - 0x65, 0x78, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x2a, 0x03, 0x18, - 0xff, 0x01, 0x52, 0x12, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, - 0x6c, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x1a, 0x53, 0x0a, 0x0a, 0x54, 0x78, 0x53, 0x65, 0x74, 0x74, - 0x69, 0x6e, 0x67, 0x73, 0x12, 0x3f, 0x0a, 0x09, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x72, 0x61, 0x74, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, - 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x52, 0x61, 0x74, - 0x65, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x08, 0x64, 0x61, 0x74, - 0x61, 0x52, 0x61, 0x74, 0x65, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x0a, 0x1a, 0xda, 0x04, 0x0a, 0x0a, - 0x52, 0x78, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x4d, 0x0a, 0x0b, 0x67, 0x61, - 0x74, 0x65, 0x77, 0x61, 0x79, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x22, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, - 0x2e, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, - 0x65, 0x72, 0x73, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x0a, 0x67, - 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x49, 0x64, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x68, 0x61, - 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x72, 0x73, 0x73, 0x69, 0x18, 0x09, 0x20, 0x01, 0x28, 0x02, 0x52, - 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x73, 0x73, 0x69, 0x12, 0x10, 0x0a, 0x03, - 0x73, 0x6e, 0x72, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x02, 0x52, 0x03, 0x73, 0x6e, 0x72, 0x12, 0x6a, - 0x0a, 0x18, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x5f, - 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x74, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0e, - 0x32, 0x26, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, - 0x33, 0x2e, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x50, 0x61, 0x74, 0x68, 0x43, 0x6f, - 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x74, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x82, 0x01, 0x02, - 0x10, 0x01, 0x52, 0x16, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x50, 0x61, 0x74, 0x68, - 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x75, 0x70, - 0x6c, 0x69, 0x6e, 0x6b, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x0b, 0x75, 0x70, 0x6c, 0x69, 0x6e, 0x6b, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x6b, 0x0a, - 0x0d, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x5f, 0x62, 0x72, 0x6f, 0x6b, 0x65, 0x72, 0x18, 0x12, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x46, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, - 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4d, 0x41, 0x43, 0x53, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x55, - 0x70, 0x6c, 0x69, 0x6e, 0x6b, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x52, 0x78, 0x4d, - 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x50, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x42, 0x72, - 0x6f, 0x6b, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x0c, 0x70, 0x61, - 0x63, 0x6b, 0x65, 0x74, 0x42, 0x72, 0x6f, 0x6b, 0x65, 0x72, 0x12, 0x55, 0x0a, 0x05, 0x72, 0x65, - 0x6c, 0x61, 0x79, 0x18, 0x17, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3f, 0x2e, 0x74, 0x74, 0x6e, 0x2e, - 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4d, 0x41, 0x43, 0x53, 0x74, - 0x61, 0x74, 0x65, 0x2e, 0x55, 0x70, 0x6c, 0x69, 0x6e, 0x6b, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x2e, 0x52, 0x78, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x52, 0x65, 0x6c, - 0x61, 0x79, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x05, 0x72, 0x65, 0x6c, 0x61, - 0x79, 0x1a, 0x1c, 0x0a, 0x14, 0x50, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x42, 0x72, 0x6f, 0x6b, 0x65, - 0x72, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x4a, 0x04, 0x08, 0x01, 0x10, 0x0b, 0x1a, - 0x15, 0x0a, 0x0d, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, - 0x4a, 0x04, 0x08, 0x01, 0x10, 0x04, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x09, 0x4a, 0x04, 0x08, 0x0a, - 0x10, 0x0b, 0x4a, 0x04, 0x08, 0x0c, 0x10, 0x0d, 0x4a, 0x04, 0x08, 0x0d, 0x10, 0x0e, 0x4a, 0x04, - 0x08, 0x10, 0x10, 0x11, 0x4a, 0x04, 0x08, 0x11, 0x10, 0x12, 0x4a, 0x04, 0x08, 0x13, 0x10, 0x14, - 0x4a, 0x04, 0x08, 0x14, 0x10, 0x15, 0x4a, 0x04, 0x08, 0x15, 0x10, 0x16, 0x4a, 0x04, 0x08, 0x16, - 0x10, 0x17, 0x4a, 0x04, 0x08, 0x63, 0x10, 0x64, 0x4a, 0x04, 0x08, 0x01, 0x10, 0x02, 0x4a, 0x04, - 0x08, 0x03, 0x10, 0x04, 0x4a, 0x04, 0x08, 0x08, 0x10, 0x09, 0x4a, 0x04, 0x08, 0x0a, 0x10, 0x0b, - 0x1a, 0x97, 0x04, 0x0a, 0x0f, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x4d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x12, 0x4a, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, - 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4d, 0x41, 0x43, 0x53, 0x74, 0x61, 0x74, 0x65, 0x2e, - 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, - 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, - 0x12, 0x35, 0x0a, 0x0f, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, - 0x69, 0x64, 0x73, 0x18, 0x06, 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, 0x1a, 0xee, 0x02, 0x0a, 0x07, 0x4d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x12, 0x54, 0x0a, 0x05, 0x6d, 0x5f, 0x68, 0x64, 0x72, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, - 0x2e, 0x76, 0x33, 0x2e, 0x4d, 0x41, 0x43, 0x53, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x44, 0x6f, 0x77, - 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x4d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x2e, 0x4d, 0x48, 0x44, 0x52, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, - 0x02, 0x10, 0x01, 0x52, 0x04, 0x6d, 0x48, 0x64, 0x72, 0x12, 0x5c, 0x0a, 0x0b, 0x6d, 0x61, 0x63, - 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3b, - 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, - 0x4d, 0x41, 0x43, 0x53, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, - 0x6b, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x2e, 0x4d, 0x41, 0x43, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x0a, 0x6d, 0x61, 0x63, - 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x1a, 0x44, 0x0a, 0x04, 0x4d, 0x48, 0x44, 0x52, 0x12, - 0x36, 0x0a, 0x06, 0x6d, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, - 0x15, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, - 0x2e, 0x4d, 0x54, 0x79, 0x70, 0x65, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x82, 0x01, 0x02, 0x10, 0x01, - 0x52, 0x05, 0x6d, 0x54, 0x79, 0x70, 0x65, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x1a, 0x5d, 0x0a, - 0x0a, 0x4d, 0x41, 0x43, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x1f, 0x0a, 0x06, 0x66, - 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x42, 0x08, 0xfa, 0x42, 0x05, - 0x2a, 0x03, 0x18, 0xff, 0x01, 0x52, 0x05, 0x66, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x1c, 0x0a, 0x0a, - 0x66, 0x75, 0x6c, 0x6c, 0x5f, 0x66, 0x5f, 0x63, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, - 0x52, 0x08, 0x66, 0x75, 0x6c, 0x6c, 0x46, 0x43, 0x6e, 0x74, 0x4a, 0x04, 0x08, 0x01, 0x10, 0x02, - 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x4a, 0x04, 0x08, 0x04, 0x10, 0x05, 0x4a, 0x04, 0x08, 0x02, - 0x10, 0x03, 0x4a, 0x04, 0x08, 0x04, 0x10, 0x08, 0x4a, 0x04, 0x08, 0x01, 0x10, 0x02, 0x4a, 0x04, - 0x08, 0x03, 0x10, 0x06, 0x4a, 0x04, 0x08, 0x07, 0x10, 0x08, 0x1a, 0xc9, 0x01, 0x0a, 0x0d, 0x44, - 0x61, 0x74, 0x61, 0x52, 0x61, 0x74, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x56, 0x0a, 0x13, - 0x6d, 0x69, 0x6e, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x5f, 0x69, 0x6e, - 0x64, 0x65, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1d, 0x2e, 0x74, 0x74, 0x6e, 0x2e, - 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x52, - 0x61, 0x74, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x82, 0x01, 0x02, - 0x10, 0x01, 0x52, 0x10, 0x6d, 0x69, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x52, 0x61, 0x74, 0x65, 0x49, - 0x6e, 0x64, 0x65, 0x78, 0x12, 0x56, 0x0a, 0x13, 0x6d, 0x61, 0x78, 0x5f, 0x64, 0x61, 0x74, 0x61, - 0x5f, 0x72, 0x61, 0x74, 0x65, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0e, 0x32, 0x1d, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, - 0x76, 0x33, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x52, 0x61, 0x74, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, - 0x42, 0x08, 0xfa, 0x42, 0x05, 0x82, 0x01, 0x02, 0x10, 0x01, 0x52, 0x10, 0x6d, 0x61, 0x78, 0x44, - 0x61, 0x74, 0x61, 0x52, 0x61, 0x74, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x3a, 0x08, 0xf2, 0xaa, - 0x19, 0x04, 0x08, 0x01, 0x10, 0x01, 0x1a, 0x64, 0x0a, 0x0e, 0x44, 0x61, 0x74, 0x61, 0x52, 0x61, - 0x74, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x48, 0x0a, 0x06, 0x72, 0x61, 0x6e, 0x67, - 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, - 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4d, 0x41, 0x43, 0x53, 0x74, 0x61, - 0x74, 0x65, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x52, 0x61, 0x74, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, - 0x42, 0x08, 0xfa, 0x42, 0x05, 0x92, 0x01, 0x02, 0x08, 0x01, 0x52, 0x06, 0x72, 0x61, 0x6e, 0x67, - 0x65, 0x73, 0x3a, 0x08, 0xf2, 0xaa, 0x19, 0x04, 0x08, 0x01, 0x10, 0x01, 0x1a, 0x72, 0x0a, 0x1b, - 0x52, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x65, 0x64, 0x44, 0x61, 0x74, 0x61, 0x52, 0x61, 0x74, 0x65, - 0x52, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, - 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x3d, 0x0a, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x74, - 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4d, 0x41, - 0x43, 0x53, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x52, 0x61, 0x74, 0x65, 0x52, - 0x61, 0x6e, 0x67, 0x65, 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, - 0x3a, 0x08, 0xf2, 0xaa, 0x19, 0x04, 0x08, 0x01, 0x10, 0x01, 0x22, 0xcb, 0x01, 0x0a, 0x1b, 0x45, - 0x6e, 0x64, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, - 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x30, 0x0a, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x1a, 0xfa, 0x42, 0x17, 0x72, 0x15, - 0x32, 0x13, 0x5e, 0x5b, 0x61, 0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x30, 0x2d, 0x39, 0x5d, 0x7b, 0x31, - 0x2c, 0x33, 0x32, 0x7d, 0x24, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x39, 0x0a, 0x0a, - 0x76, 0x61, 0x6c, 0x69, 0x64, 0x5f, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 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, 0x16, 0x0a, 0x06, 0x6c, 0x65, 0x6e, 0x67, 0x74, + 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x22, + 0x5c, 0x0a, 0x16, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x64, 0x44, 0x65, 0x76, 0x69, + 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x42, 0x0a, 0x0a, 0x65, 0x6e, 0x64, + 0x5f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 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, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, + 0x10, 0x01, 0x52, 0x09, 0x65, 0x6e, 0x64, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x22, 0x97, 0x01, + 0x0a, 0x16, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x64, 0x44, 0x65, 0x76, 0x69, 0x63, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x42, 0x0a, 0x0a, 0x65, 0x6e, 0x64, 0x5f, + 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 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, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, + 0x01, 0x52, 0x09, 0x65, 0x6e, 0x64, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x12, 0x39, 0x0a, 0x0a, + 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x02, 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, 0x09, 0x76, 0x61, - 0x6c, 0x69, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x12, 0x35, 0x0a, 0x08, 0x76, 0x61, 0x6c, 0x69, 0x64, - 0x5f, 0x74, 0x6f, 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, 0x07, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x54, 0x6f, 0x3a, 0x08, - 0xf2, 0xaa, 0x19, 0x04, 0x08, 0x01, 0x10, 0x01, 0x22, 0xd7, 0x21, 0x0a, 0x09, 0x45, 0x6e, 0x64, - 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x12, 0x48, 0x0a, 0x03, 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, 0x10, 0xfa, 0x42, 0x05, 0x8a, 0x01, - 0x02, 0x10, 0x01, 0xf2, 0xaa, 0x19, 0x04, 0x08, 0x00, 0x28, 0x01, 0x52, 0x03, 0x69, 0x64, 0x73, - 0x12, 0x43, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x02, + 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4d, 0x61, 0x73, 0x6b, 0x52, 0x09, 0x66, 0x69, + 0x65, 0x6c, 0x64, 0x4d, 0x61, 0x73, 0x6b, 0x22, 0xa8, 0x02, 0x0a, 0x23, 0x42, 0x61, 0x74, 0x63, + 0x68, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x64, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, + 0x4c, 0x61, 0x73, 0x74, 0x53, 0x65, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x65, 0x0a, 0x07, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x4b, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, + 0x33, 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x64, + 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x4c, 0x61, 0x73, 0x74, 0x53, 0x65, 0x65, 0x6e, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x45, 0x6e, 0x64, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x4c, + 0x61, 0x73, 0x74, 0x53, 0x65, 0x65, 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x07, 0x75, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x1a, 0x99, 0x01, 0x0a, 0x17, 0x45, 0x6e, 0x64, 0x44, 0x65, + 0x76, 0x69, 0x63, 0x65, 0x4c, 0x61, 0x73, 0x74, 0x53, 0x65, 0x65, 0x6e, 0x55, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x12, 0x40, 0x0a, 0x03, 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, + 0x03, 0x69, 0x64, 0x73, 0x12, 0x3c, 0x0a, 0x0c, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x65, + 0x6e, 0x5f, 0x61, 0x74, 0x18, 0x02, 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, 0x6c, 0x61, 0x73, 0x74, 0x53, 0x65, 0x65, 0x6e, + 0x41, 0x74, 0x22, 0xa6, 0x01, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x45, 0x6e, 0x64, 0x44, 0x65, 0x76, + 0x69, 0x63, 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, 0x39, 0x0a, 0x0a, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x02, 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, 0xf2, 0xaa, 0x19, 0x04, 0x08, 0x00, 0x10, 0x00, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, - 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x43, 0x0a, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 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, 0x42, 0x08, 0xf2, 0xaa, 0x19, 0x04, 0x08, 0x00, 0x10, 0x00, 0x52, - 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x1b, 0x0a, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x18, - 0x32, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2a, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, - 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xfa, 0x42, - 0x05, 0x72, 0x03, 0x18, 0xd0, 0x0f, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x69, 0x6f, 0x6e, 0x12, 0x80, 0x01, 0x0a, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, - 0x65, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 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, 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, 0x12, 0x4c, 0x0a, 0x0b, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, - 0x6e, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x07, 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, 0x35, 0x0a, 0x12, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, - 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, - 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x18, 0x40, 0x52, 0x10, 0x73, 0x65, 0x72, 0x76, 0x69, - 0x63, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x49, 0x64, 0x12, 0xc5, 0x01, 0x0a, 0x16, - 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x61, - 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x42, 0x8e, 0x01, 0xfa, - 0x42, 0x8a, 0x01, 0x72, 0x87, 0x01, 0x32, 0x84, 0x01, 0x5e, 0x28, 0x3f, 0x3a, 0x28, 0x3f, 0x3a, - 0x5b, 0x61, 0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x30, 0x2d, 0x39, 0x5d, 0x7c, 0x5b, 0x61, 0x2d, 0x7a, - 0x41, 0x2d, 0x5a, 0x30, 0x2d, 0x39, 0x5d, 0x5b, 0x61, 0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x30, 0x2d, - 0x39, 0x5c, 0x2d, 0x5d, 0x2a, 0x5b, 0x61, 0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x30, 0x2d, 0x39, 0x5d, - 0x29, 0x5c, 0x2e, 0x29, 0x2a, 0x28, 0x3f, 0x3a, 0x5b, 0x41, 0x2d, 0x5a, 0x61, 0x2d, 0x7a, 0x30, - 0x2d, 0x39, 0x5d, 0x7c, 0x5b, 0x41, 0x2d, 0x5a, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x5b, - 0x41, 0x2d, 0x5a, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5c, 0x2d, 0x5d, 0x2a, 0x5b, 0x41, 0x2d, - 0x5a, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x29, 0x28, 0x3f, 0x3a, 0x3a, 0x5b, 0x30, 0x2d, - 0x39, 0x5d, 0x7b, 0x31, 0x2c, 0x35, 0x7d, 0x29, 0x3f, 0x24, 0x7c, 0x5e, 0x24, 0x52, 0x14, 0x6e, - 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x41, 0x64, 0x64, 0x72, - 0x65, 0x73, 0x73, 0x12, 0x41, 0x0a, 0x18, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x5f, 0x73, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x6b, 0x65, 0x6b, 0x5f, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x18, - 0x2f, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x72, 0x03, 0x18, 0x80, 0x10, 0x52, - 0x15, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4b, 0x65, - 0x6b, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x12, 0xcd, 0x01, 0x0a, 0x1a, 0x61, 0x70, 0x70, 0x6c, 0x69, - 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x61, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x42, 0x8e, 0x01, 0xfa, 0x42, - 0x8a, 0x01, 0x72, 0x87, 0x01, 0x32, 0x84, 0x01, 0x5e, 0x28, 0x3f, 0x3a, 0x28, 0x3f, 0x3a, 0x5b, - 0x61, 0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x30, 0x2d, 0x39, 0x5d, 0x7c, 0x5b, 0x61, 0x2d, 0x7a, 0x41, - 0x2d, 0x5a, 0x30, 0x2d, 0x39, 0x5d, 0x5b, 0x61, 0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x30, 0x2d, 0x39, - 0x5c, 0x2d, 0x5d, 0x2a, 0x5b, 0x61, 0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x30, 0x2d, 0x39, 0x5d, 0x29, - 0x5c, 0x2e, 0x29, 0x2a, 0x28, 0x3f, 0x3a, 0x5b, 0x41, 0x2d, 0x5a, 0x61, 0x2d, 0x7a, 0x30, 0x2d, - 0x39, 0x5d, 0x7c, 0x5b, 0x41, 0x2d, 0x5a, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x5b, 0x41, - 0x2d, 0x5a, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5c, 0x2d, 0x5d, 0x2a, 0x5b, 0x41, 0x2d, 0x5a, - 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x29, 0x28, 0x3f, 0x3a, 0x3a, 0x5b, 0x30, 0x2d, 0x39, - 0x5d, 0x7b, 0x31, 0x2c, 0x35, 0x7d, 0x29, 0x3f, 0x24, 0x7c, 0x5e, 0x24, 0x52, 0x18, 0x61, 0x70, - 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x41, - 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x49, 0x0a, 0x1c, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x6b, 0x65, 0x6b, - 0x5f, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x18, 0x30, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xfa, 0x42, - 0x05, 0x72, 0x03, 0x18, 0x80, 0x10, 0x52, 0x19, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4b, 0x65, 0x6b, 0x4c, 0x61, 0x62, 0x65, - 0x6c, 0x12, 0x3b, 0x0a, 0x15, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x31, 0x20, 0x01, 0x28, 0x09, - 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x18, 0x64, 0x52, 0x13, 0x61, 0x70, 0x70, 0x6c, 0x69, - 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x49, 0x64, 0x12, 0xbf, - 0x01, 0x0a, 0x13, 0x6a, 0x6f, 0x69, 0x6e, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x61, - 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x42, 0x8e, 0x01, 0xfa, - 0x42, 0x8a, 0x01, 0x72, 0x87, 0x01, 0x32, 0x84, 0x01, 0x5e, 0x28, 0x3f, 0x3a, 0x28, 0x3f, 0x3a, - 0x5b, 0x61, 0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x30, 0x2d, 0x39, 0x5d, 0x7c, 0x5b, 0x61, 0x2d, 0x7a, - 0x41, 0x2d, 0x5a, 0x30, 0x2d, 0x39, 0x5d, 0x5b, 0x61, 0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x30, 0x2d, - 0x39, 0x5c, 0x2d, 0x5d, 0x2a, 0x5b, 0x61, 0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x30, 0x2d, 0x39, 0x5d, - 0x29, 0x5c, 0x2e, 0x29, 0x2a, 0x28, 0x3f, 0x3a, 0x5b, 0x41, 0x2d, 0x5a, 0x61, 0x2d, 0x7a, 0x30, - 0x2d, 0x39, 0x5d, 0x7c, 0x5b, 0x41, 0x2d, 0x5a, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x5b, - 0x41, 0x2d, 0x5a, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5c, 0x2d, 0x5d, 0x2a, 0x5b, 0x41, 0x2d, - 0x5a, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x29, 0x28, 0x3f, 0x3a, 0x3a, 0x5b, 0x30, 0x2d, - 0x39, 0x5d, 0x7b, 0x31, 0x2c, 0x35, 0x7d, 0x29, 0x3f, 0x24, 0x7c, 0x5e, 0x24, 0x52, 0x11, 0x6a, - 0x6f, 0x69, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, - 0x12, 0x74, 0x0a, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x0c, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x28, 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, 0x2e, 0x4c, - 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x42, 0x2c, 0xfa, - 0x42, 0x29, 0x9a, 0x01, 0x26, 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, 0x52, 0x09, 0x6c, 0x6f, 0x63, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x31, 0x0a, 0x07, 0x70, 0x69, 0x63, 0x74, 0x75, 0x72, - 0x65, 0x18, 0x32, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, - 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x50, 0x69, 0x63, 0x74, 0x75, 0x72, 0x65, - 0x52, 0x07, 0x70, 0x69, 0x63, 0x74, 0x75, 0x72, 0x65, 0x12, 0x28, 0x0a, 0x10, 0x73, 0x75, 0x70, - 0x70, 0x6f, 0x72, 0x74, 0x73, 0x5f, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x5f, 0x62, 0x18, 0x0d, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x0e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x43, 0x6c, 0x61, - 0x73, 0x73, 0x42, 0x12, 0x28, 0x0a, 0x10, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x5f, - 0x63, 0x6c, 0x61, 0x73, 0x73, 0x5f, 0x63, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x73, - 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x43, 0x12, 0x4d, 0x0a, - 0x0f, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, - 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, - 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4d, 0x41, 0x43, 0x56, 0x65, 0x72, 0x73, 0x69, - 0x6f, 0x6e, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x82, 0x01, 0x02, 0x10, 0x01, 0x52, 0x0e, 0x6c, 0x6f, - 0x72, 0x61, 0x77, 0x61, 0x6e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x54, 0x0a, 0x13, - 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x5f, 0x70, 0x68, 0x79, 0x5f, 0x76, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x74, 0x74, 0x6e, 0x2e, - 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x50, 0x48, 0x59, 0x56, 0x65, - 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x82, 0x01, 0x02, 0x10, 0x01, 0x52, - 0x11, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x50, 0x68, 0x79, 0x56, 0x65, 0x72, 0x73, 0x69, - 0x6f, 0x6e, 0x12, 0x33, 0x0a, 0x11, 0x66, 0x72, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x79, 0x5f, - 0x70, 0x6c, 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x11, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, - 0x42, 0x04, 0x72, 0x02, 0x18, 0x40, 0x52, 0x0f, 0x66, 0x72, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, - 0x79, 0x50, 0x6c, 0x61, 0x6e, 0x49, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x6d, 0x69, 0x6e, 0x5f, 0x66, - 0x72, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x12, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, - 0x6d, 0x69, 0x6e, 0x46, 0x72, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x23, 0x0a, 0x0d, - 0x6d, 0x61, 0x78, 0x5f, 0x66, 0x72, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x13, 0x20, - 0x01, 0x28, 0x04, 0x52, 0x0c, 0x6d, 0x61, 0x78, 0x46, 0x72, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, - 0x79, 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x5f, 0x6a, 0x6f, - 0x69, 0x6e, 0x18, 0x14, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, - 0x74, 0x73, 0x4a, 0x6f, 0x69, 0x6e, 0x12, 0x2c, 0x0a, 0x12, 0x72, 0x65, 0x73, 0x65, 0x74, 0x73, - 0x5f, 0x6a, 0x6f, 0x69, 0x6e, 0x5f, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x73, 0x18, 0x15, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x10, 0x72, 0x65, 0x73, 0x65, 0x74, 0x73, 0x4a, 0x6f, 0x69, 0x6e, 0x4e, 0x6f, - 0x6e, 0x63, 0x65, 0x73, 0x12, 0x35, 0x0a, 0x09, 0x72, 0x6f, 0x6f, 0x74, 0x5f, 0x6b, 0x65, 0x79, - 0x73, 0x18, 0x16, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, - 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x52, 0x6f, 0x6f, 0x74, 0x4b, 0x65, 0x79, - 0x73, 0x52, 0x08, 0x72, 0x6f, 0x6f, 0x74, 0x4b, 0x65, 0x79, 0x73, 0x12, 0xe7, 0x02, 0x0a, 0x06, - 0x6e, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x17, 0x20, 0x01, 0x28, 0x0c, 0x42, 0xcf, 0x02, 0x92, - 0x41, 0x17, 0x4a, 0x08, 0x22, 0x30, 0x30, 0x30, 0x30, 0x31, 0x33, 0x22, 0x9a, 0x02, 0x01, 0x07, - 0xa2, 0x02, 0x06, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0xfa, 0x42, 0x06, 0x7a, 0x04, 0x68, 0x03, - 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, 0x33, 0x42, 0x79, 0x74, 0x65, 0x73, 0xf2, 0xaa, 0x19, 0xa0, 0x01, 0x1a, 0x4e, - 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, 0x63, 0x6d, 0x64, 0x2f, 0x74, 0x74, 0x6e, 0x2d, 0x6c, 0x77, - 0x2d, 0x63, 0x6c, 0x69, 0x2f, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x66, 0x6c, 0x61, 0x67, 0x73, - 0x2e, 0x4e, 0x65, 0x77, 0x33, 0x42, 0x79, 0x74, 0x65, 0x73, 0x46, 0x6c, 0x61, 0x67, 0x22, 0x4e, - 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, 0x63, 0x6d, 0x64, 0x2f, 0x74, 0x74, 0x6e, 0x2d, 0x6c, 0x77, - 0x2d, 0x63, 0x6c, 0x69, 0x2f, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x66, 0x6c, 0x61, 0x67, 0x73, - 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x61, 0x63, 0x74, 0x42, 0x79, 0x74, 0x65, 0x73, 0x52, 0x05, - 0x6e, 0x65, 0x74, 0x49, 0x64, 0x12, 0x3e, 0x0a, 0x0c, 0x6d, 0x61, 0x63, 0x5f, 0x73, 0x65, 0x74, - 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x18, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x74, 0x74, - 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4d, 0x41, 0x43, - 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x0b, 0x6d, 0x61, 0x63, 0x53, 0x65, 0x74, - 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x35, 0x0a, 0x09, 0x6d, 0x61, 0x63, 0x5f, 0x73, 0x74, 0x61, - 0x74, 0x65, 0x18, 0x19, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, - 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4d, 0x41, 0x43, 0x53, 0x74, 0x61, - 0x74, 0x65, 0x52, 0x08, 0x6d, 0x61, 0x63, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x44, 0x0a, 0x11, - 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x6d, 0x61, 0x63, 0x5f, 0x73, 0x74, 0x61, 0x74, - 0x65, 0x18, 0x2c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, - 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4d, 0x41, 0x43, 0x53, 0x74, 0x61, 0x74, - 0x65, 0x52, 0x0f, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x4d, 0x61, 0x63, 0x53, 0x74, 0x61, - 0x74, 0x65, 0x12, 0x31, 0x0a, 0x07, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x1a, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, - 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x73, 0x65, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x40, 0x0a, 0x0f, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, - 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x1b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, - 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, - 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x0e, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, - 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x24, 0x0a, 0x0e, 0x6c, 0x61, 0x73, 0x74, 0x5f, - 0x64, 0x65, 0x76, 0x5f, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x18, 0x1c, 0x20, 0x01, 0x28, 0x0d, 0x52, - 0x0c, 0x6c, 0x61, 0x73, 0x74, 0x44, 0x65, 0x76, 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x12, 0x26, 0x0a, - 0x0f, 0x75, 0x73, 0x65, 0x64, 0x5f, 0x64, 0x65, 0x76, 0x5f, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x73, - 0x18, 0x1d, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0d, 0x75, 0x73, 0x65, 0x64, 0x44, 0x65, 0x76, 0x4e, - 0x6f, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x26, 0x0a, 0x0f, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x6a, 0x6f, - 0x69, 0x6e, 0x5f, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, - 0x6c, 0x61, 0x73, 0x74, 0x4a, 0x6f, 0x69, 0x6e, 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x12, 0x25, 0x0a, - 0x0f, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x72, 0x6a, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x30, - 0x18, 0x1f, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x6c, 0x61, 0x73, 0x74, 0x52, 0x6a, 0x43, 0x6f, - 0x75, 0x6e, 0x74, 0x30, 0x12, 0x25, 0x0a, 0x0f, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x72, 0x6a, 0x5f, - 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x31, 0x18, 0x20, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x6c, - 0x61, 0x73, 0x74, 0x52, 0x6a, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x31, 0x12, 0x58, 0x0a, 0x1b, 0x6c, - 0x61, 0x73, 0x74, 0x5f, 0x64, 0x65, 0x76, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, 0x72, - 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x21, 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, 0x17, 0x6c, 0x61, - 0x73, 0x74, 0x44, 0x65, 0x76, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x63, 0x65, 0x69, - 0x76, 0x65, 0x64, 0x41, 0x74, 0x12, 0x4b, 0x0a, 0x0b, 0x70, 0x6f, 0x77, 0x65, 0x72, 0x5f, 0x73, - 0x74, 0x61, 0x74, 0x65, 0x18, 0x22, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x74, 0x74, 0x6e, - 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x50, 0x6f, 0x77, 0x65, - 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x42, 0x0e, 0xfa, 0x42, 0x05, 0x82, 0x01, 0x02, 0x10, 0x01, - 0xf2, 0xaa, 0x19, 0x02, 0x10, 0x00, 0x52, 0x0a, 0x70, 0x6f, 0x77, 0x65, 0x72, 0x53, 0x74, 0x61, - 0x74, 0x65, 0x12, 0x5b, 0x0a, 0x12, 0x62, 0x61, 0x74, 0x74, 0x65, 0x72, 0x79, 0x5f, 0x70, 0x65, - 0x72, 0x63, 0x65, 0x6e, 0x74, 0x61, 0x67, 0x65, 0x18, 0x23, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, - 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, - 0x2e, 0x46, 0x6c, 0x6f, 0x61, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x0f, 0xfa, 0x42, 0x0c, - 0x0a, 0x0a, 0x1d, 0x00, 0x00, 0x80, 0x3f, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x52, 0x11, 0x62, 0x61, - 0x74, 0x74, 0x65, 0x72, 0x79, 0x50, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x61, 0x67, 0x65, 0x12, - 0x27, 0x0a, 0x0f, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x5f, 0x6d, 0x61, 0x72, 0x67, - 0x69, 0x6e, 0x18, 0x24, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x69, - 0x6e, 0x6b, 0x4d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x12, 0x65, 0x0a, 0x1c, 0x71, 0x75, 0x65, 0x75, - 0x65, 0x64, 0x5f, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x64, - 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x73, 0x18, 0x28, 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, 0x1a, 0x71, 0x75, 0x65, 0x75, 0x65, 0x64, 0x41, 0x70, 0x70, 0x6c, 0x69, - 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x73, 0x12, - 0x48, 0x0a, 0x0a, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x74, 0x65, 0x72, 0x73, 0x18, 0x29, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, - 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x50, 0x61, 0x79, 0x6c, - 0x6f, 0x61, 0x64, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x74, 0x65, 0x72, 0x73, 0x52, 0x0a, 0x66, - 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x74, 0x65, 0x72, 0x73, 0x12, 0x51, 0x0a, 0x0e, 0x70, 0x72, 0x6f, - 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x2a, 0x20, 0x01, 0x28, - 0x09, 0x42, 0x2a, 0xfa, 0x42, 0x27, 0x72, 0x25, 0x18, 0x24, 0x32, 0x21, 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, 0x7c, 0x5e, 0x24, 0x52, 0x0d, 0x70, - 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x49, 0x64, 0x12, 0x44, 0x0a, 0x11, - 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x69, 0x6e, 0x67, 0x5f, 0x64, 0x61, 0x74, - 0x61, 0x18, 0x2b, 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, 0x10, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x69, 0x6e, 0x67, 0x44, 0x61, - 0x74, 0x61, 0x12, 0x1c, 0x0a, 0x09, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x63, 0x61, 0x73, 0x74, 0x18, - 0x2d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x63, 0x61, 0x73, 0x74, - 0x12, 0x67, 0x0a, 0x19, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, - 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x2e, 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, 0x41, 0x75, - 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x64, 0x65, - 0x52, 0x17, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x2e, 0x0a, 0x13, 0x73, 0x6b, 0x69, - 0x70, 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, - 0x18, 0x33, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x73, 0x6b, 0x69, 0x70, 0x50, 0x61, 0x79, 0x6c, - 0x6f, 0x61, 0x64, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x12, 0x5b, 0x0a, 0x1c, 0x73, 0x6b, 0x69, - 0x70, 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, - 0x5f, 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x18, 0x34, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, - 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x19, 0x73, 0x6b, 0x69, - 0x70, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x4f, 0x76, - 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x12, 0x3d, 0x0a, 0x0c, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, - 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x35, 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, 0x0b, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, - 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x3c, 0x0a, 0x0c, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x65, - 0x65, 0x6e, 0x5f, 0x61, 0x74, 0x18, 0x36, 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, 0x6c, 0x61, 0x73, 0x74, 0x53, 0x65, 0x65, - 0x6e, 0x41, 0x74, 0x12, 0x4f, 0x0a, 0x0d, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x5f, 0x6e, 0x75, - 0x6d, 0x62, 0x65, 0x72, 0x18, 0x37, 0x20, 0x01, 0x28, 0x09, 0x42, 0x2a, 0xfa, 0x42, 0x27, 0x72, - 0x25, 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, 0xd0, 0x01, 0x01, 0x52, 0x0c, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x4e, 0x75, - 0x6d, 0x62, 0x65, 0x72, 0x12, 0x69, 0x0a, 0x19, 0x6c, 0x6f, 0x72, 0x61, 0x5f, 0x61, 0x6c, 0x6c, - 0x69, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x69, 0x64, - 0x73, 0x18, 0x38, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, - 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4c, 0x6f, 0x52, 0x61, 0x41, 0x6c, 0x6c, - 0x69, 0x61, 0x6e, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x49, 0x64, 0x65, 0x6e, - 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x52, 0x16, 0x6c, 0x6f, 0x72, 0x61, 0x41, 0x6c, 0x6c, - 0x69, 0x61, 0x6e, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x49, 0x64, 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, 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, - 0x4a, 0x04, 0x08, 0x25, 0x10, 0x26, 0x4a, 0x04, 0x08, 0x26, 0x10, 0x27, 0x4a, 0x04, 0x08, 0x27, - 0x10, 0x28, 0x22, 0x48, 0x0a, 0x0a, 0x45, 0x6e, 0x64, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, - 0x12, 0x3a, 0x0a, 0x0b, 0x65, 0x6e, 0x64, 0x5f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x18, - 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 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, - 0x52, 0x0a, 0x65, 0x6e, 0x64, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x22, 0xf2, 0x01, 0x0a, - 0x0d, 0x44, 0x65, 0x76, 0x41, 0x64, 0x64, 0x72, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 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, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4d, 0x61, 0x73, 0x6b, + 0x52, 0x09, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x4d, 0x61, 0x73, 0x6b, 0x22, 0xcb, 0x03, 0x0a, 0x25, + 0x47, 0x65, 0x74, 0x45, 0x6e, 0x64, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x65, 0x6e, + 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x46, 0x6f, 0x72, 0x45, 0x55, 0x49, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0xd0, 0x01, 0x0a, 0x08, 0x6a, 0x6f, 0x69, 0x6e, 0x5f, 0x65, + 0x75, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0xb4, 0x01, 0x92, 0x41, 0x21, 0x4a, 0x12, + 0x22, 0x37, 0x30, 0x42, 0x33, 0x44, 0x35, 0x37, 0x45, 0x44, 0x30, 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, 0x08, 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, - 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, 0x16, 0x0a, 0x06, 0x6c, 0x65, 0x6e, - 0x67, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x6c, 0x65, 0x6e, 0x67, 0x74, - 0x68, 0x22, 0x5c, 0x0a, 0x16, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x64, 0x44, 0x65, - 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x42, 0x0a, 0x0a, 0x65, - 0x6e, 0x64, 0x5f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x19, 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, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, - 0x01, 0x02, 0x10, 0x01, 0x52, 0x09, 0x65, 0x6e, 0x64, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x22, - 0x97, 0x01, 0x0a, 0x16, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x64, 0x44, 0x65, 0x76, - 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x42, 0x0a, 0x0a, 0x65, 0x6e, - 0x64, 0x5f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, - 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, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, - 0x02, 0x10, 0x01, 0x52, 0x09, 0x65, 0x6e, 0x64, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x12, 0x39, - 0x0a, 0x0a, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4d, 0x61, 0x73, 0x6b, 0x52, 0x09, - 0x66, 0x69, 0x65, 0x6c, 0x64, 0x4d, 0x61, 0x73, 0x6b, 0x22, 0xa8, 0x02, 0x0a, 0x23, 0x42, 0x61, - 0x74, 0x63, 0x68, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x64, 0x44, 0x65, 0x76, 0x69, - 0x63, 0x65, 0x4c, 0x61, 0x73, 0x74, 0x53, 0x65, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x65, 0x0a, 0x07, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x4b, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, - 0x2e, 0x76, 0x33, 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x45, - 0x6e, 0x64, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x4c, 0x61, 0x73, 0x74, 0x53, 0x65, 0x65, 0x6e, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x45, 0x6e, 0x64, 0x44, 0x65, 0x76, 0x69, 0x63, - 0x65, 0x4c, 0x61, 0x73, 0x74, 0x53, 0x65, 0x65, 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, - 0x07, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x1a, 0x99, 0x01, 0x0a, 0x17, 0x45, 0x6e, 0x64, - 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x4c, 0x61, 0x73, 0x74, 0x53, 0x65, 0x65, 0x6e, 0x55, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x12, 0x40, 0x0a, 0x03, 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, 0x03, 0x69, 0x64, 0x73, 0x12, 0x3c, 0x0a, 0x0c, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, - 0x65, 0x65, 0x6e, 0x5f, 0x61, 0x74, 0x18, 0x02, 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, 0x6c, 0x61, 0x73, 0x74, 0x53, 0x65, - 0x65, 0x6e, 0x41, 0x74, 0x22, 0xa6, 0x01, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x45, 0x6e, 0x64, 0x44, - 0x65, 0x76, 0x69, 0x63, 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, 0x39, 0x0a, 0x0a, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x6d, 0x61, 0x73, 0x6b, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4d, 0x61, - 0x73, 0x6b, 0x52, 0x09, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x4d, 0x61, 0x73, 0x6b, 0x22, 0xcb, 0x03, - 0x0a, 0x25, 0x47, 0x65, 0x74, 0x45, 0x6e, 0x64, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, - 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x46, 0x6f, 0x72, 0x45, 0x55, 0x49, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0xd0, 0x01, 0x0a, 0x08, 0x6a, 0x6f, 0x69, 0x6e, - 0x5f, 0x65, 0x75, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0xb4, 0x01, 0x92, 0x41, 0x21, + 0x55, 0x6e, 0x6d, 0x61, 0x72, 0x73, 0x68, 0x61, 0x6c, 0x38, 0x42, 0x79, 0x74, 0x65, 0x73, 0x52, + 0x07, 0x6a, 0x6f, 0x69, 0x6e, 0x45, 0x75, 0x69, 0x12, 0xce, 0x01, 0x0a, 0x07, 0x64, 0x65, 0x76, + 0x5f, 0x65, 0x75, 0x69, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x42, 0xb4, 0x01, 0x92, 0x41, 0x21, 0x4a, 0x12, 0x22, 0x37, 0x30, 0x42, 0x33, 0x44, 0x35, 0x37, 0x45, 0x44, 0x30, 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, 0x08, 0x70, 0x01, 0xea, 0xaa, 0x19, 0x82, 0x01, 0x0a, @@ -6916,171 +6998,158 @@ var file_ttn_lorawan_v3_end_device_proto_rawDesc = []byte{ 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, 0x38, 0x42, 0x79, 0x74, 0x65, - 0x73, 0x52, 0x07, 0x6a, 0x6f, 0x69, 0x6e, 0x45, 0x75, 0x69, 0x12, 0xce, 0x01, 0x0a, 0x07, 0x64, - 0x65, 0x76, 0x5f, 0x65, 0x75, 0x69, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x42, 0xb4, 0x01, 0x92, - 0x41, 0x21, 0x4a, 0x12, 0x22, 0x37, 0x30, 0x42, 0x33, 0x44, 0x35, 0x37, 0x45, 0x44, 0x30, 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, 0x08, 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, 0x38, 0x42, 0x79, - 0x74, 0x65, 0x73, 0x52, 0x06, 0x64, 0x65, 0x76, 0x45, 0x75, 0x69, 0x22, 0xbf, 0x04, 0x0a, 0x15, - 0x4c, 0x69, 0x73, 0x74, 0x45, 0x6e, 0x64, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4f, 0x0a, 0x0f, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 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, 0x49, 0x64, 0x65, 0x6e, 0x74, - 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x52, 0x0e, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x73, 0x12, 0x39, 0x0a, 0x0a, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, - 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, - 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, - 0x6c, 0x64, 0x4d, 0x61, 0x73, 0x6b, 0x52, 0x09, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x4d, 0x61, 0x73, - 0x6b, 0x12, 0xbd, 0x01, 0x0a, 0x05, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x09, 0x42, 0xa6, 0x01, 0xfa, 0x42, 0xa2, 0x01, 0x72, 0x9f, 0x01, 0x52, 0x00, 0x52, 0x09, 0x64, - 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x52, 0x0a, 0x2d, 0x64, 0x65, 0x76, 0x69, 0x63, - 0x65, 0x5f, 0x69, 0x64, 0x52, 0x08, 0x6a, 0x6f, 0x69, 0x6e, 0x5f, 0x65, 0x75, 0x69, 0x52, 0x09, - 0x2d, 0x6a, 0x6f, 0x69, 0x6e, 0x5f, 0x65, 0x75, 0x69, 0x52, 0x07, 0x64, 0x65, 0x76, 0x5f, 0x65, - 0x75, 0x69, 0x52, 0x08, 0x2d, 0x64, 0x65, 0x76, 0x5f, 0x65, 0x75, 0x69, 0x52, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x52, 0x05, 0x2d, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, - 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0c, 0x2d, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, - 0x52, 0x0b, 0x2d, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x52, 0x0c, 0x6c, - 0x61, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x65, 0x6e, 0x5f, 0x61, 0x74, 0x52, 0x0d, 0x2d, 0x6c, 0x61, - 0x73, 0x74, 0x5f, 0x73, 0x65, 0x65, 0x6e, 0x5f, 0x61, 0x74, 0x52, 0x05, 0x6f, 0x72, 0x64, 0x65, - 0x72, 0x12, 0x1e, 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, - 0x42, 0x08, 0xfa, 0x42, 0x05, 0x2a, 0x03, 0x18, 0xe8, 0x07, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, - 0x74, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x67, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, - 0x04, 0x70, 0x61, 0x67, 0x65, 0x12, 0x46, 0x0a, 0x07, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, - 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, - 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x45, 0x6e, 0x64, 0x44, - 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x69, - 0x6c, 0x74, 0x65, 0x72, 0x52, 0x07, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x1a, 0x5e, 0x0a, - 0x06, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x4b, 0x0a, 0x0d, 0x75, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x64, 0x5f, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x18, 0x01, 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, 0x38, 0x01, 0x48, 0x00, 0x52, 0x0c, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x53, - 0x69, 0x6e, 0x63, 0x65, 0x42, 0x07, 0x0a, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x22, 0x94, 0x01, - 0x0a, 0x13, 0x53, 0x65, 0x74, 0x45, 0x6e, 0x64, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x42, 0x0a, 0x0a, 0x65, 0x6e, 0x64, 0x5f, 0x64, 0x65, 0x76, - 0x69, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 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, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x09, - 0x65, 0x6e, 0x64, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x12, 0x39, 0x0a, 0x0a, 0x66, 0x69, 0x65, - 0x6c, 0x64, 0x5f, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, - 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, - 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4d, 0x61, 0x73, 0x6b, 0x52, 0x09, 0x66, 0x69, 0x65, 0x6c, 0x64, - 0x4d, 0x61, 0x73, 0x6b, 0x22, 0xae, 0x01, 0x0a, 0x1b, 0x52, 0x65, 0x73, 0x65, 0x74, 0x41, 0x6e, - 0x64, 0x47, 0x65, 0x74, 0x45, 0x6e, 0x64, 0x44, 0x65, 0x76, 0x69, 0x63, 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, 0x39, 0x0a, 0x0a, 0x66, 0x69, - 0x65, 0x6c, 0x64, 0x5f, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, - 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, - 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4d, 0x61, 0x73, 0x6b, 0x52, 0x09, 0x66, 0x69, 0x65, 0x6c, - 0x64, 0x4d, 0x61, 0x73, 0x6b, 0x22, 0xbc, 0x01, 0x0a, 0x11, 0x45, 0x6e, 0x64, 0x44, 0x65, 0x76, - 0x69, 0x63, 0x65, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x12, 0x42, 0x0a, 0x0a, 0x65, - 0x6e, 0x64, 0x5f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x19, 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, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, - 0x01, 0x02, 0x10, 0x01, 0x52, 0x09, 0x65, 0x6e, 0x64, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x12, - 0x39, 0x0a, 0x0a, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4d, 0x61, 0x73, 0x6b, 0x52, - 0x09, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x4d, 0x61, 0x73, 0x6b, 0x12, 0x28, 0x0a, 0x0b, 0x6d, 0x61, - 0x70, 0x70, 0x69, 0x6e, 0x67, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, - 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x18, 0x64, 0x52, 0x0a, 0x6d, 0x61, 0x70, 0x70, 0x69, 0x6e, - 0x67, 0x4b, 0x65, 0x79, 0x22, 0xb8, 0x01, 0x0a, 0x17, 0x45, 0x6e, 0x64, 0x44, 0x65, 0x76, 0x69, - 0x63, 0x65, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, - 0x12, 0x1b, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, - 0xfa, 0x42, 0x04, 0x72, 0x02, 0x18, 0x64, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2a, 0x0a, - 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x72, 0x03, 0x18, 0xc8, 0x01, 0x52, 0x0b, 0x64, 0x65, - 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x54, 0x0a, 0x0f, 0x66, 0x69, 0x6c, - 0x65, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x03, - 0x28, 0x09, 0x42, 0x2b, 0xfa, 0x42, 0x28, 0x92, 0x01, 0x25, 0x10, 0x64, 0x18, 0x01, 0x22, 0x1f, - 0x72, 0x1d, 0x32, 0x1b, 0x5e, 0x28, 0x3f, 0x3a, 0x5c, 0x2e, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, - 0x39, 0x5d, 0x7b, 0x31, 0x2c, 0x31, 0x36, 0x7d, 0x29, 0x7b, 0x31, 0x2c, 0x32, 0x7d, 0x24, 0x52, - 0x0e, 0x66, 0x69, 0x6c, 0x65, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x22, - 0xfe, 0x01, 0x0a, 0x18, 0x45, 0x6e, 0x64, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x54, 0x65, 0x6d, - 0x70, 0x6c, 0x61, 0x74, 0x65, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x73, 0x12, 0x7d, 0x0a, 0x07, - 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x35, 0x2e, + 0x73, 0x52, 0x06, 0x64, 0x65, 0x76, 0x45, 0x75, 0x69, 0x22, 0xbf, 0x04, 0x0a, 0x15, 0x4c, 0x69, + 0x73, 0x74, 0x45, 0x6e, 0x64, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x4f, 0x0a, 0x0f, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 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, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, + 0x69, 0x65, 0x72, 0x73, 0x52, 0x0e, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x49, 0x64, 0x73, 0x12, 0x39, 0x0a, 0x0a, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x6d, 0x61, + 0x73, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, + 0x4d, 0x61, 0x73, 0x6b, 0x52, 0x09, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x4d, 0x61, 0x73, 0x6b, 0x12, + 0xbd, 0x01, 0x0a, 0x05, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, + 0xa6, 0x01, 0xfa, 0x42, 0xa2, 0x01, 0x72, 0x9f, 0x01, 0x52, 0x00, 0x52, 0x09, 0x64, 0x65, 0x76, + 0x69, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x52, 0x0a, 0x2d, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, + 0x69, 0x64, 0x52, 0x08, 0x6a, 0x6f, 0x69, 0x6e, 0x5f, 0x65, 0x75, 0x69, 0x52, 0x09, 0x2d, 0x6a, + 0x6f, 0x69, 0x6e, 0x5f, 0x65, 0x75, 0x69, 0x52, 0x07, 0x64, 0x65, 0x76, 0x5f, 0x65, 0x75, 0x69, + 0x52, 0x08, 0x2d, 0x64, 0x65, 0x76, 0x5f, 0x65, 0x75, 0x69, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x52, 0x05, 0x2d, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0c, 0x2d, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x52, 0x0b, + 0x2d, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x52, 0x0c, 0x6c, 0x61, 0x73, + 0x74, 0x5f, 0x73, 0x65, 0x65, 0x6e, 0x5f, 0x61, 0x74, 0x52, 0x0d, 0x2d, 0x6c, 0x61, 0x73, 0x74, + 0x5f, 0x73, 0x65, 0x65, 0x6e, 0x5f, 0x61, 0x74, 0x52, 0x05, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x12, + 0x1e, 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x42, 0x08, + 0xfa, 0x42, 0x05, 0x2a, 0x03, 0x18, 0xe8, 0x07, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x12, + 0x12, 0x0a, 0x04, 0x70, 0x61, 0x67, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x70, + 0x61, 0x67, 0x65, 0x12, 0x46, 0x0a, 0x07, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x18, 0x06, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, + 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x45, 0x6e, 0x64, 0x44, 0x65, 0x76, + 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x69, 0x6c, 0x74, + 0x65, 0x72, 0x52, 0x07, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x1a, 0x5e, 0x0a, 0x06, 0x46, + 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x4b, 0x0a, 0x0d, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, + 0x5f, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x18, 0x01, 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, + 0x38, 0x01, 0x48, 0x00, 0x52, 0x0c, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x53, 0x69, 0x6e, + 0x63, 0x65, 0x42, 0x07, 0x0a, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x22, 0x94, 0x01, 0x0a, 0x13, + 0x53, 0x65, 0x74, 0x45, 0x6e, 0x64, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x42, 0x0a, 0x0a, 0x65, 0x6e, 0x64, 0x5f, 0x64, 0x65, 0x76, 0x69, 0x63, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 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, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x09, 0x65, 0x6e, + 0x64, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x12, 0x39, 0x0a, 0x0a, 0x66, 0x69, 0x65, 0x6c, 0x64, + 0x5f, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, + 0x65, 0x6c, 0x64, 0x4d, 0x61, 0x73, 0x6b, 0x52, 0x09, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x4d, 0x61, + 0x73, 0x6b, 0x22, 0xae, 0x01, 0x0a, 0x1b, 0x52, 0x65, 0x73, 0x65, 0x74, 0x41, 0x6e, 0x64, 0x47, + 0x65, 0x74, 0x45, 0x6e, 0x64, 0x44, 0x65, 0x76, 0x69, 0x63, 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, 0x39, 0x0a, 0x0a, 0x66, 0x69, 0x65, 0x6c, + 0x64, 0x5f, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, + 0x69, 0x65, 0x6c, 0x64, 0x4d, 0x61, 0x73, 0x6b, 0x52, 0x09, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x4d, + 0x61, 0x73, 0x6b, 0x22, 0xbc, 0x01, 0x0a, 0x11, 0x45, 0x6e, 0x64, 0x44, 0x65, 0x76, 0x69, 0x63, + 0x65, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x12, 0x42, 0x0a, 0x0a, 0x65, 0x6e, 0x64, + 0x5f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 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, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, - 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x73, 0x2e, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x73, 0x45, - 0x6e, 0x74, 0x72, 0x79, 0x42, 0x2c, 0xfa, 0x42, 0x29, 0x9a, 0x01, 0x26, 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, 0x52, 0x07, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x73, 0x1a, 0x63, 0x0a, 0x0c, 0x46, - 0x6f, 0x72, 0x6d, 0x61, 0x74, 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, 0x3d, 0x0a, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x74, + 0x6e, 0x64, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, + 0x10, 0x01, 0x52, 0x09, 0x65, 0x6e, 0x64, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x12, 0x39, 0x0a, + 0x0a, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4d, 0x61, 0x73, 0x6b, 0x52, 0x09, 0x66, + 0x69, 0x65, 0x6c, 0x64, 0x4d, 0x61, 0x73, 0x6b, 0x12, 0x28, 0x0a, 0x0b, 0x6d, 0x61, 0x70, 0x70, + 0x69, 0x6e, 0x67, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, + 0x42, 0x04, 0x72, 0x02, 0x18, 0x64, 0x52, 0x0a, 0x6d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x4b, + 0x65, 0x79, 0x22, 0xb8, 0x01, 0x0a, 0x17, 0x45, 0x6e, 0x64, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, + 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x1b, + 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, + 0x04, 0x72, 0x02, 0x18, 0x64, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2a, 0x0a, 0x0b, 0x64, + 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x42, 0x08, 0xfa, 0x42, 0x05, 0x72, 0x03, 0x18, 0xc8, 0x01, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x54, 0x0a, 0x0f, 0x66, 0x69, 0x6c, 0x65, 0x5f, + 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, + 0x42, 0x2b, 0xfa, 0x42, 0x28, 0x92, 0x01, 0x25, 0x10, 0x64, 0x18, 0x01, 0x22, 0x1f, 0x72, 0x1d, + 0x32, 0x1b, 0x5e, 0x28, 0x3f, 0x3a, 0x5c, 0x2e, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, + 0x7b, 0x31, 0x2c, 0x31, 0x36, 0x7d, 0x29, 0x7b, 0x31, 0x2c, 0x32, 0x7d, 0x24, 0x52, 0x0e, 0x66, + 0x69, 0x6c, 0x65, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0xfe, 0x01, + 0x0a, 0x18, 0x45, 0x6e, 0x64, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x54, 0x65, 0x6d, 0x70, 0x6c, + 0x61, 0x74, 0x65, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x73, 0x12, 0x7d, 0x0a, 0x07, 0x66, 0x6f, + 0x72, 0x6d, 0x61, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x35, 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, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x46, 0x6f, + 0x72, 0x6d, 0x61, 0x74, 0x73, 0x2e, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x73, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x42, 0x2c, 0xfa, 0x42, 0x29, 0x9a, 0x01, 0x26, 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, + 0x52, 0x07, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x73, 0x1a, 0x63, 0x0a, 0x0c, 0x46, 0x6f, 0x72, + 0x6d, 0x61, 0x74, 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, 0x3d, 0x0a, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 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, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x46, 0x6f, 0x72, + 0x6d, 0x61, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xdd, + 0x01, 0x0a, 0x1f, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x45, 0x6e, 0x64, 0x44, 0x65, 0x76, + 0x69, 0x63, 0x65, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x44, 0x0a, 0x09, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x69, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x27, 0xfa, 0x42, 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, 0x52, 0x08, + 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x60, 0x0a, 0x16, + 0x65, 0x6e, 0x64, 0x5f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, + 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x03, 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, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x46, - 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, - 0x22, 0xdd, 0x01, 0x0a, 0x1f, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x45, 0x6e, 0x64, 0x44, - 0x65, 0x76, 0x69, 0x63, 0x65, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x44, 0x0a, 0x09, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x69, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x27, 0xfa, 0x42, 0x24, 0x72, 0x22, 0x18, 0x24, + 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, 0x13, 0x65, 0x6e, 0x64, 0x44, 0x65, + 0x76, 0x69, 0x63, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x73, 0x22, 0xca, + 0x01, 0x0a, 0x1c, 0x42, 0x61, 0x74, 0x63, 0x68, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x45, 0x6e, + 0x64, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x59, 0x0a, 0x0f, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, + 0x64, 0x73, 0x18, 0x01, 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, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, + 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x0e, 0x61, 0x70, 0x70, 0x6c, + 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x73, 0x12, 0x4f, 0x0a, 0x0a, 0x64, 0x65, + 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x42, 0x30, + 0xfa, 0x42, 0x2d, 0x92, 0x01, 0x2a, 0x08, 0x01, 0x10, 0x14, 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, - 0x52, 0x08, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, - 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x60, - 0x0a, 0x16, 0x65, 0x6e, 0x64, 0x5f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x76, 0x65, 0x72, - 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x03, 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, 0x13, 0x65, 0x6e, 0x64, - 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x73, - 0x22, 0xca, 0x01, 0x0a, 0x1c, 0x42, 0x61, 0x74, 0x63, 0x68, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, - 0x45, 0x6e, 0x64, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x59, 0x0a, 0x0f, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 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, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, - 0x72, 0x73, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x0e, 0x61, 0x70, - 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x73, 0x12, 0x4f, 0x0a, 0x0a, - 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, - 0x42, 0x30, 0xfa, 0x42, 0x2d, 0x92, 0x01, 0x2a, 0x08, 0x01, 0x10, 0x14, 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, 0x52, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x73, 0x22, 0xa8, 0x02, - 0x0a, 0x19, 0x42, 0x61, 0x74, 0x63, 0x68, 0x47, 0x65, 0x74, 0x45, 0x6e, 0x64, 0x44, 0x65, 0x76, - 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x59, 0x0a, 0x0f, 0x61, - 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, - 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, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x42, 0x08, 0xfa, 0x42, - 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x0e, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x73, 0x12, 0x4f, 0x0a, 0x0a, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, - 0x5f, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x42, 0x30, 0xfa, 0x42, 0x2d, 0x92, - 0x01, 0x2a, 0x08, 0x01, 0x10, 0x14, 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, 0x52, 0x09, 0x64, 0x65, - 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x73, 0x12, 0x39, 0x0a, 0x0a, 0x66, 0x69, 0x65, 0x6c, 0x64, - 0x5f, 0x6d, 0x61, 0x73, 0x6b, 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, 0x46, 0x69, - 0x65, 0x6c, 0x64, 0x4d, 0x61, 0x73, 0x6b, 0x52, 0x09, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x4d, 0x61, - 0x73, 0x6b, 0x4a, 0x04, 0x08, 0x04, 0x10, 0x05, 0x4a, 0x04, 0x08, 0x05, 0x10, 0x06, 0x4a, 0x04, - 0x08, 0x06, 0x10, 0x07, 0x52, 0x05, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x05, 0x6c, 0x69, 0x6d, - 0x69, 0x74, 0x52, 0x04, 0x70, 0x61, 0x67, 0x65, 0x2a, 0x55, 0x0a, 0x0a, 0x50, 0x6f, 0x77, 0x65, - 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x11, 0x0a, 0x0d, 0x50, 0x4f, 0x57, 0x45, 0x52, 0x5f, - 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x11, 0x0a, 0x0d, 0x50, 0x4f, 0x57, - 0x45, 0x52, 0x5f, 0x42, 0x41, 0x54, 0x54, 0x45, 0x52, 0x59, 0x10, 0x01, 0x12, 0x12, 0x0a, 0x0e, - 0x50, 0x4f, 0x57, 0x45, 0x52, 0x5f, 0x45, 0x58, 0x54, 0x45, 0x52, 0x4e, 0x41, 0x4c, 0x10, 0x02, - 0x1a, 0x0d, 0xea, 0xaa, 0x19, 0x09, 0x18, 0x01, 0x2a, 0x05, 0x50, 0x4f, 0x57, 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, + 0x52, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x73, 0x22, 0xa8, 0x02, 0x0a, 0x19, + 0x42, 0x61, 0x74, 0x63, 0x68, 0x47, 0x65, 0x74, 0x45, 0x6e, 0x64, 0x44, 0x65, 0x76, 0x69, 0x63, + 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x59, 0x0a, 0x0f, 0x61, 0x70, 0x70, + 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 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, 0x49, + 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, + 0x01, 0x02, 0x10, 0x01, 0x52, 0x0e, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x49, 0x64, 0x73, 0x12, 0x4f, 0x0a, 0x0a, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, + 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x42, 0x30, 0xfa, 0x42, 0x2d, 0x92, 0x01, 0x2a, + 0x08, 0x01, 0x10, 0x14, 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, 0x52, 0x09, 0x64, 0x65, 0x76, 0x69, + 0x63, 0x65, 0x49, 0x64, 0x73, 0x12, 0x39, 0x0a, 0x0a, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x6d, + 0x61, 0x73, 0x6b, 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, 0x46, 0x69, 0x65, 0x6c, + 0x64, 0x4d, 0x61, 0x73, 0x6b, 0x52, 0x09, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x4d, 0x61, 0x73, 0x6b, + 0x4a, 0x04, 0x08, 0x04, 0x10, 0x05, 0x4a, 0x04, 0x08, 0x05, 0x10, 0x06, 0x4a, 0x04, 0x08, 0x06, + 0x10, 0x07, 0x52, 0x05, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, + 0x52, 0x04, 0x70, 0x61, 0x67, 0x65, 0x2a, 0x55, 0x0a, 0x0a, 0x50, 0x6f, 0x77, 0x65, 0x72, 0x53, + 0x74, 0x61, 0x74, 0x65, 0x12, 0x11, 0x0a, 0x0d, 0x50, 0x4f, 0x57, 0x45, 0x52, 0x5f, 0x55, 0x4e, + 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x11, 0x0a, 0x0d, 0x50, 0x4f, 0x57, 0x45, 0x52, + 0x5f, 0x42, 0x41, 0x54, 0x54, 0x45, 0x52, 0x59, 0x10, 0x01, 0x12, 0x12, 0x0a, 0x0e, 0x50, 0x4f, + 0x57, 0x45, 0x52, 0x5f, 0x45, 0x58, 0x54, 0x45, 0x52, 0x4e, 0x41, 0x4c, 0x10, 0x02, 0x1a, 0x0d, + 0xea, 0xaa, 0x19, 0x09, 0x18, 0x01, 0x2a, 0x05, 0x50, 0x4f, 0x57, 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 ( @@ -7096,7 +7165,7 @@ func file_ttn_lorawan_v3_end_device_proto_rawDescGZIP() []byte { } var file_ttn_lorawan_v3_end_device_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_ttn_lorawan_v3_end_device_proto_msgTypes = make([]protoimpl.MessageInfo, 61) +var file_ttn_lorawan_v3_end_device_proto_msgTypes = make([]protoimpl.MessageInfo, 62) var file_ttn_lorawan_v3_end_device_proto_goTypes = []interface{}{ (PowerState)(0), // 0: ttn.lorawan.v3.PowerState (*Session)(nil), // 1: ttn.lorawan.v3.Session @@ -7113,330 +7182,334 @@ var file_ttn_lorawan_v3_end_device_proto_goTypes = []interface{}{ (*EndDeviceVersion)(nil), // 12: ttn.lorawan.v3.EndDeviceVersion (*ADRSettings)(nil), // 13: ttn.lorawan.v3.ADRSettings (*MACSettings)(nil), // 14: ttn.lorawan.v3.MACSettings - (*MACState)(nil), // 15: ttn.lorawan.v3.MACState - (*EndDeviceAuthenticationCode)(nil), // 16: ttn.lorawan.v3.EndDeviceAuthenticationCode - (*EndDevice)(nil), // 17: ttn.lorawan.v3.EndDevice - (*EndDevices)(nil), // 18: ttn.lorawan.v3.EndDevices - (*DevAddrPrefix)(nil), // 19: ttn.lorawan.v3.DevAddrPrefix - (*CreateEndDeviceRequest)(nil), // 20: ttn.lorawan.v3.CreateEndDeviceRequest - (*UpdateEndDeviceRequest)(nil), // 21: ttn.lorawan.v3.UpdateEndDeviceRequest - (*BatchUpdateEndDeviceLastSeenRequest)(nil), // 22: ttn.lorawan.v3.BatchUpdateEndDeviceLastSeenRequest - (*GetEndDeviceRequest)(nil), // 23: ttn.lorawan.v3.GetEndDeviceRequest - (*GetEndDeviceIdentifiersForEUIsRequest)(nil), // 24: ttn.lorawan.v3.GetEndDeviceIdentifiersForEUIsRequest - (*ListEndDevicesRequest)(nil), // 25: ttn.lorawan.v3.ListEndDevicesRequest - (*SetEndDeviceRequest)(nil), // 26: ttn.lorawan.v3.SetEndDeviceRequest - (*ResetAndGetEndDeviceRequest)(nil), // 27: ttn.lorawan.v3.ResetAndGetEndDeviceRequest - (*EndDeviceTemplate)(nil), // 28: ttn.lorawan.v3.EndDeviceTemplate - (*EndDeviceTemplateFormat)(nil), // 29: ttn.lorawan.v3.EndDeviceTemplateFormat - (*EndDeviceTemplateFormats)(nil), // 30: ttn.lorawan.v3.EndDeviceTemplateFormats - (*ConvertEndDeviceTemplateRequest)(nil), // 31: ttn.lorawan.v3.ConvertEndDeviceTemplateRequest - (*BatchDeleteEndDevicesRequest)(nil), // 32: ttn.lorawan.v3.BatchDeleteEndDevicesRequest - (*BatchGetEndDevicesRequest)(nil), // 33: ttn.lorawan.v3.BatchGetEndDevicesRequest - (*MACParameters_Channel)(nil), // 34: ttn.lorawan.v3.MACParameters.Channel - (*ADRSettings_StaticMode)(nil), // 35: ttn.lorawan.v3.ADRSettings.StaticMode - (*ADRSettings_DynamicMode)(nil), // 36: ttn.lorawan.v3.ADRSettings.DynamicMode - (*ADRSettings_DisabledMode)(nil), // 37: ttn.lorawan.v3.ADRSettings.DisabledMode - (*ADRSettings_DynamicMode_ChannelSteeringSettings)(nil), // 38: ttn.lorawan.v3.ADRSettings.DynamicMode.ChannelSteeringSettings - (*ADRSettings_DynamicMode_PerDataRateIndexOverride)(nil), // 39: ttn.lorawan.v3.ADRSettings.DynamicMode.PerDataRateIndexOverride - (*ADRSettings_DynamicMode_Overrides)(nil), // 40: ttn.lorawan.v3.ADRSettings.DynamicMode.Overrides - (*ADRSettings_DynamicMode_ChannelSteeringSettings_LoRaNarrowMode)(nil), // 41: ttn.lorawan.v3.ADRSettings.DynamicMode.ChannelSteeringSettings.LoRaNarrowMode - (*ADRSettings_DynamicMode_ChannelSteeringSettings_DisabledMode)(nil), // 42: ttn.lorawan.v3.ADRSettings.DynamicMode.ChannelSteeringSettings.DisabledMode - (*MACState_JoinRequest)(nil), // 43: ttn.lorawan.v3.MACState.JoinRequest - (*MACState_JoinAccept)(nil), // 44: ttn.lorawan.v3.MACState.JoinAccept - (*MACState_UplinkMessage)(nil), // 45: ttn.lorawan.v3.MACState.UplinkMessage - (*MACState_DownlinkMessage)(nil), // 46: ttn.lorawan.v3.MACState.DownlinkMessage - (*MACState_DataRateRange)(nil), // 47: ttn.lorawan.v3.MACState.DataRateRange - (*MACState_DataRateRanges)(nil), // 48: ttn.lorawan.v3.MACState.DataRateRanges - nil, // 49: ttn.lorawan.v3.MACState.RejectedDataRateRangesEntry - (*MACState_UplinkMessage_TxSettings)(nil), // 50: ttn.lorawan.v3.MACState.UplinkMessage.TxSettings - (*MACState_UplinkMessage_RxMetadata)(nil), // 51: ttn.lorawan.v3.MACState.UplinkMessage.RxMetadata - (*MACState_UplinkMessage_RxMetadata_PacketBrokerMetadata)(nil), // 52: ttn.lorawan.v3.MACState.UplinkMessage.RxMetadata.PacketBrokerMetadata - (*MACState_UplinkMessage_RxMetadata_RelayMetadata)(nil), // 53: ttn.lorawan.v3.MACState.UplinkMessage.RxMetadata.RelayMetadata - (*MACState_DownlinkMessage_Message)(nil), // 54: ttn.lorawan.v3.MACState.DownlinkMessage.Message - (*MACState_DownlinkMessage_Message_MHDR)(nil), // 55: ttn.lorawan.v3.MACState.DownlinkMessage.Message.MHDR - (*MACState_DownlinkMessage_Message_MACPayload)(nil), // 56: ttn.lorawan.v3.MACState.DownlinkMessage.Message.MACPayload - nil, // 57: ttn.lorawan.v3.EndDevice.AttributesEntry - nil, // 58: ttn.lorawan.v3.EndDevice.LocationsEntry - (*BatchUpdateEndDeviceLastSeenRequest_EndDeviceLastSeenUpdate)(nil), // 59: ttn.lorawan.v3.BatchUpdateEndDeviceLastSeenRequest.EndDeviceLastSeenUpdate - (*ListEndDevicesRequest_Filter)(nil), // 60: ttn.lorawan.v3.ListEndDevicesRequest.Filter - nil, // 61: ttn.lorawan.v3.EndDeviceTemplateFormats.FormatsEntry - (*SessionKeys)(nil), // 62: ttn.lorawan.v3.SessionKeys - (*timestamppb.Timestamp)(nil), // 63: google.protobuf.Timestamp - (*ApplicationDownlink)(nil), // 64: ttn.lorawan.v3.ApplicationDownlink - (RelayResetLimitCounter)(0), // 65: ttn.lorawan.v3.RelayResetLimitCounter - (*RelayForwardLimits)(nil), // 66: ttn.lorawan.v3.RelayForwardLimits - (*RelayUplinkForwardLimits)(nil), // 67: ttn.lorawan.v3.RelayUplinkForwardLimits - (*RelaySecondChannel)(nil), // 68: ttn.lorawan.v3.RelaySecondChannel - (RelayCADPeriodicity)(0), // 69: ttn.lorawan.v3.RelayCADPeriodicity - (*RelayEndDeviceAlwaysMode)(nil), // 70: ttn.lorawan.v3.RelayEndDeviceAlwaysMode - (*RelayEndDeviceDynamicMode)(nil), // 71: ttn.lorawan.v3.RelayEndDeviceDynamicMode - (*RelayEndDeviceControlledMode)(nil), // 72: ttn.lorawan.v3.RelayEndDeviceControlledMode - (*wrapperspb.UInt32Value)(nil), // 73: google.protobuf.UInt32Value - (DataRateIndex)(0), // 74: ttn.lorawan.v3.DataRateIndex - (RxDelay)(0), // 75: ttn.lorawan.v3.RxDelay - (DataRateOffset)(0), // 76: ttn.lorawan.v3.DataRateOffset - (AggregatedDutyCycle)(0), // 77: ttn.lorawan.v3.AggregatedDutyCycle - (RejoinTimeExponent)(0), // 78: ttn.lorawan.v3.RejoinTimeExponent - (RejoinCountExponent)(0), // 79: ttn.lorawan.v3.RejoinCountExponent - (*ADRAckLimitExponentValue)(nil), // 80: ttn.lorawan.v3.ADRAckLimitExponentValue - (*ADRAckDelayExponentValue)(nil), // 81: ttn.lorawan.v3.ADRAckDelayExponentValue - (*DataRateIndexValue)(nil), // 82: ttn.lorawan.v3.DataRateIndexValue - (*EndDeviceVersionIdentifiers)(nil), // 83: ttn.lorawan.v3.EndDeviceVersionIdentifiers - (MACVersion)(0), // 84: ttn.lorawan.v3.MACVersion - (PHYVersion)(0), // 85: ttn.lorawan.v3.PHYVersion - (*MessagePayloadFormatters)(nil), // 86: ttn.lorawan.v3.MessagePayloadFormatters - (*durationpb.Duration)(nil), // 87: google.protobuf.Duration - (*PingSlotPeriodValue)(nil), // 88: ttn.lorawan.v3.PingSlotPeriodValue - (*ZeroableFrequencyValue)(nil), // 89: ttn.lorawan.v3.ZeroableFrequencyValue - (*RxDelayValue)(nil), // 90: ttn.lorawan.v3.RxDelayValue - (*DataRateOffsetValue)(nil), // 91: ttn.lorawan.v3.DataRateOffsetValue - (*FrequencyValue)(nil), // 92: ttn.lorawan.v3.FrequencyValue - (*AggregatedDutyCycleValue)(nil), // 93: ttn.lorawan.v3.AggregatedDutyCycleValue - (*wrapperspb.FloatValue)(nil), // 94: google.protobuf.FloatValue - (*DeviceEIRPValue)(nil), // 95: ttn.lorawan.v3.DeviceEIRPValue - (Class)(0), // 96: ttn.lorawan.v3.Class - (*MACCommand)(nil), // 97: ttn.lorawan.v3.MACCommand - (MACCommandIdentifier)(0), // 98: ttn.lorawan.v3.MACCommandIdentifier - (*RelayForwardDownlinkReq)(nil), // 99: ttn.lorawan.v3.RelayForwardDownlinkReq - (*EndDeviceIdentifiers)(nil), // 100: ttn.lorawan.v3.EndDeviceIdentifiers - (*Picture)(nil), // 101: ttn.lorawan.v3.Picture - (*RootKeys)(nil), // 102: ttn.lorawan.v3.RootKeys - (*structpb.Struct)(nil), // 103: google.protobuf.Struct - (*wrapperspb.BoolValue)(nil), // 104: google.protobuf.BoolValue - (*LoRaAllianceProfileIdentifiers)(nil), // 105: ttn.lorawan.v3.LoRaAllianceProfileIdentifiers - (*fieldmaskpb.FieldMask)(nil), // 106: google.protobuf.FieldMask - (*ApplicationIdentifiers)(nil), // 107: ttn.lorawan.v3.ApplicationIdentifiers - (*DLSettings)(nil), // 108: ttn.lorawan.v3.DLSettings - (*CFList)(nil), // 109: ttn.lorawan.v3.CFList - (*Message)(nil), // 110: ttn.lorawan.v3.Message - (*DataRate)(nil), // 111: ttn.lorawan.v3.DataRate - (*GatewayIdentifiers)(nil), // 112: ttn.lorawan.v3.GatewayIdentifiers - (DownlinkPathConstraint)(0), // 113: ttn.lorawan.v3.DownlinkPathConstraint - (MType)(0), // 114: ttn.lorawan.v3.MType - (*Location)(nil), // 115: ttn.lorawan.v3.Location + (*MACSettingsProfile)(nil), // 15: ttn.lorawan.v3.MACSettingsProfile + (*MACState)(nil), // 16: ttn.lorawan.v3.MACState + (*EndDeviceAuthenticationCode)(nil), // 17: ttn.lorawan.v3.EndDeviceAuthenticationCode + (*EndDevice)(nil), // 18: ttn.lorawan.v3.EndDevice + (*EndDevices)(nil), // 19: ttn.lorawan.v3.EndDevices + (*DevAddrPrefix)(nil), // 20: ttn.lorawan.v3.DevAddrPrefix + (*CreateEndDeviceRequest)(nil), // 21: ttn.lorawan.v3.CreateEndDeviceRequest + (*UpdateEndDeviceRequest)(nil), // 22: ttn.lorawan.v3.UpdateEndDeviceRequest + (*BatchUpdateEndDeviceLastSeenRequest)(nil), // 23: ttn.lorawan.v3.BatchUpdateEndDeviceLastSeenRequest + (*GetEndDeviceRequest)(nil), // 24: ttn.lorawan.v3.GetEndDeviceRequest + (*GetEndDeviceIdentifiersForEUIsRequest)(nil), // 25: ttn.lorawan.v3.GetEndDeviceIdentifiersForEUIsRequest + (*ListEndDevicesRequest)(nil), // 26: ttn.lorawan.v3.ListEndDevicesRequest + (*SetEndDeviceRequest)(nil), // 27: ttn.lorawan.v3.SetEndDeviceRequest + (*ResetAndGetEndDeviceRequest)(nil), // 28: ttn.lorawan.v3.ResetAndGetEndDeviceRequest + (*EndDeviceTemplate)(nil), // 29: ttn.lorawan.v3.EndDeviceTemplate + (*EndDeviceTemplateFormat)(nil), // 30: ttn.lorawan.v3.EndDeviceTemplateFormat + (*EndDeviceTemplateFormats)(nil), // 31: ttn.lorawan.v3.EndDeviceTemplateFormats + (*ConvertEndDeviceTemplateRequest)(nil), // 32: ttn.lorawan.v3.ConvertEndDeviceTemplateRequest + (*BatchDeleteEndDevicesRequest)(nil), // 33: ttn.lorawan.v3.BatchDeleteEndDevicesRequest + (*BatchGetEndDevicesRequest)(nil), // 34: ttn.lorawan.v3.BatchGetEndDevicesRequest + (*MACParameters_Channel)(nil), // 35: ttn.lorawan.v3.MACParameters.Channel + (*ADRSettings_StaticMode)(nil), // 36: ttn.lorawan.v3.ADRSettings.StaticMode + (*ADRSettings_DynamicMode)(nil), // 37: ttn.lorawan.v3.ADRSettings.DynamicMode + (*ADRSettings_DisabledMode)(nil), // 38: ttn.lorawan.v3.ADRSettings.DisabledMode + (*ADRSettings_DynamicMode_ChannelSteeringSettings)(nil), // 39: ttn.lorawan.v3.ADRSettings.DynamicMode.ChannelSteeringSettings + (*ADRSettings_DynamicMode_PerDataRateIndexOverride)(nil), // 40: ttn.lorawan.v3.ADRSettings.DynamicMode.PerDataRateIndexOverride + (*ADRSettings_DynamicMode_Overrides)(nil), // 41: ttn.lorawan.v3.ADRSettings.DynamicMode.Overrides + (*ADRSettings_DynamicMode_ChannelSteeringSettings_LoRaNarrowMode)(nil), // 42: ttn.lorawan.v3.ADRSettings.DynamicMode.ChannelSteeringSettings.LoRaNarrowMode + (*ADRSettings_DynamicMode_ChannelSteeringSettings_DisabledMode)(nil), // 43: ttn.lorawan.v3.ADRSettings.DynamicMode.ChannelSteeringSettings.DisabledMode + (*MACState_JoinRequest)(nil), // 44: ttn.lorawan.v3.MACState.JoinRequest + (*MACState_JoinAccept)(nil), // 45: ttn.lorawan.v3.MACState.JoinAccept + (*MACState_UplinkMessage)(nil), // 46: ttn.lorawan.v3.MACState.UplinkMessage + (*MACState_DownlinkMessage)(nil), // 47: ttn.lorawan.v3.MACState.DownlinkMessage + (*MACState_DataRateRange)(nil), // 48: ttn.lorawan.v3.MACState.DataRateRange + (*MACState_DataRateRanges)(nil), // 49: ttn.lorawan.v3.MACState.DataRateRanges + nil, // 50: ttn.lorawan.v3.MACState.RejectedDataRateRangesEntry + (*MACState_UplinkMessage_TxSettings)(nil), // 51: ttn.lorawan.v3.MACState.UplinkMessage.TxSettings + (*MACState_UplinkMessage_RxMetadata)(nil), // 52: ttn.lorawan.v3.MACState.UplinkMessage.RxMetadata + (*MACState_UplinkMessage_RxMetadata_PacketBrokerMetadata)(nil), // 53: ttn.lorawan.v3.MACState.UplinkMessage.RxMetadata.PacketBrokerMetadata + (*MACState_UplinkMessage_RxMetadata_RelayMetadata)(nil), // 54: ttn.lorawan.v3.MACState.UplinkMessage.RxMetadata.RelayMetadata + (*MACState_DownlinkMessage_Message)(nil), // 55: ttn.lorawan.v3.MACState.DownlinkMessage.Message + (*MACState_DownlinkMessage_Message_MHDR)(nil), // 56: ttn.lorawan.v3.MACState.DownlinkMessage.Message.MHDR + (*MACState_DownlinkMessage_Message_MACPayload)(nil), // 57: ttn.lorawan.v3.MACState.DownlinkMessage.Message.MACPayload + nil, // 58: ttn.lorawan.v3.EndDevice.AttributesEntry + nil, // 59: ttn.lorawan.v3.EndDevice.LocationsEntry + (*BatchUpdateEndDeviceLastSeenRequest_EndDeviceLastSeenUpdate)(nil), // 60: ttn.lorawan.v3.BatchUpdateEndDeviceLastSeenRequest.EndDeviceLastSeenUpdate + (*ListEndDevicesRequest_Filter)(nil), // 61: ttn.lorawan.v3.ListEndDevicesRequest.Filter + nil, // 62: ttn.lorawan.v3.EndDeviceTemplateFormats.FormatsEntry + (*SessionKeys)(nil), // 63: ttn.lorawan.v3.SessionKeys + (*timestamppb.Timestamp)(nil), // 64: google.protobuf.Timestamp + (*ApplicationDownlink)(nil), // 65: ttn.lorawan.v3.ApplicationDownlink + (RelayResetLimitCounter)(0), // 66: ttn.lorawan.v3.RelayResetLimitCounter + (*RelayForwardLimits)(nil), // 67: ttn.lorawan.v3.RelayForwardLimits + (*RelayUplinkForwardLimits)(nil), // 68: ttn.lorawan.v3.RelayUplinkForwardLimits + (*RelaySecondChannel)(nil), // 69: ttn.lorawan.v3.RelaySecondChannel + (RelayCADPeriodicity)(0), // 70: ttn.lorawan.v3.RelayCADPeriodicity + (*RelayEndDeviceAlwaysMode)(nil), // 71: ttn.lorawan.v3.RelayEndDeviceAlwaysMode + (*RelayEndDeviceDynamicMode)(nil), // 72: ttn.lorawan.v3.RelayEndDeviceDynamicMode + (*RelayEndDeviceControlledMode)(nil), // 73: ttn.lorawan.v3.RelayEndDeviceControlledMode + (*wrapperspb.UInt32Value)(nil), // 74: google.protobuf.UInt32Value + (DataRateIndex)(0), // 75: ttn.lorawan.v3.DataRateIndex + (RxDelay)(0), // 76: ttn.lorawan.v3.RxDelay + (DataRateOffset)(0), // 77: ttn.lorawan.v3.DataRateOffset + (AggregatedDutyCycle)(0), // 78: ttn.lorawan.v3.AggregatedDutyCycle + (RejoinTimeExponent)(0), // 79: ttn.lorawan.v3.RejoinTimeExponent + (RejoinCountExponent)(0), // 80: ttn.lorawan.v3.RejoinCountExponent + (*ADRAckLimitExponentValue)(nil), // 81: ttn.lorawan.v3.ADRAckLimitExponentValue + (*ADRAckDelayExponentValue)(nil), // 82: ttn.lorawan.v3.ADRAckDelayExponentValue + (*DataRateIndexValue)(nil), // 83: ttn.lorawan.v3.DataRateIndexValue + (*EndDeviceVersionIdentifiers)(nil), // 84: ttn.lorawan.v3.EndDeviceVersionIdentifiers + (MACVersion)(0), // 85: ttn.lorawan.v3.MACVersion + (PHYVersion)(0), // 86: ttn.lorawan.v3.PHYVersion + (*MessagePayloadFormatters)(nil), // 87: ttn.lorawan.v3.MessagePayloadFormatters + (*durationpb.Duration)(nil), // 88: google.protobuf.Duration + (*PingSlotPeriodValue)(nil), // 89: ttn.lorawan.v3.PingSlotPeriodValue + (*ZeroableFrequencyValue)(nil), // 90: ttn.lorawan.v3.ZeroableFrequencyValue + (*RxDelayValue)(nil), // 91: ttn.lorawan.v3.RxDelayValue + (*DataRateOffsetValue)(nil), // 92: ttn.lorawan.v3.DataRateOffsetValue + (*FrequencyValue)(nil), // 93: ttn.lorawan.v3.FrequencyValue + (*AggregatedDutyCycleValue)(nil), // 94: ttn.lorawan.v3.AggregatedDutyCycleValue + (*wrapperspb.FloatValue)(nil), // 95: google.protobuf.FloatValue + (*DeviceEIRPValue)(nil), // 96: ttn.lorawan.v3.DeviceEIRPValue + (*MACSettingsProfileIdentifiers)(nil), // 97: ttn.lorawan.v3.MACSettingsProfileIdentifiers + (Class)(0), // 98: ttn.lorawan.v3.Class + (*MACCommand)(nil), // 99: ttn.lorawan.v3.MACCommand + (MACCommandIdentifier)(0), // 100: ttn.lorawan.v3.MACCommandIdentifier + (*RelayForwardDownlinkReq)(nil), // 101: ttn.lorawan.v3.RelayForwardDownlinkReq + (*EndDeviceIdentifiers)(nil), // 102: ttn.lorawan.v3.EndDeviceIdentifiers + (*Picture)(nil), // 103: ttn.lorawan.v3.Picture + (*RootKeys)(nil), // 104: ttn.lorawan.v3.RootKeys + (*structpb.Struct)(nil), // 105: google.protobuf.Struct + (*wrapperspb.BoolValue)(nil), // 106: google.protobuf.BoolValue + (*LoRaAllianceProfileIdentifiers)(nil), // 107: ttn.lorawan.v3.LoRaAllianceProfileIdentifiers + (*fieldmaskpb.FieldMask)(nil), // 108: google.protobuf.FieldMask + (*ApplicationIdentifiers)(nil), // 109: ttn.lorawan.v3.ApplicationIdentifiers + (*DLSettings)(nil), // 110: ttn.lorawan.v3.DLSettings + (*CFList)(nil), // 111: ttn.lorawan.v3.CFList + (*Message)(nil), // 112: ttn.lorawan.v3.Message + (*DataRate)(nil), // 113: ttn.lorawan.v3.DataRate + (*GatewayIdentifiers)(nil), // 114: ttn.lorawan.v3.GatewayIdentifiers + (DownlinkPathConstraint)(0), // 115: ttn.lorawan.v3.DownlinkPathConstraint + (MType)(0), // 116: ttn.lorawan.v3.MType + (*Location)(nil), // 117: ttn.lorawan.v3.Location } var file_ttn_lorawan_v3_end_device_proto_depIdxs = []int32{ - 62, // 0: ttn.lorawan.v3.Session.keys:type_name -> ttn.lorawan.v3.SessionKeys - 63, // 1: ttn.lorawan.v3.Session.started_at:type_name -> google.protobuf.Timestamp - 64, // 2: ttn.lorawan.v3.Session.queued_application_downlinks:type_name -> ttn.lorawan.v3.ApplicationDownlink - 65, // 3: ttn.lorawan.v3.ServingRelayForwardingLimits.reset_behavior:type_name -> ttn.lorawan.v3.RelayResetLimitCounter - 66, // 4: ttn.lorawan.v3.ServingRelayForwardingLimits.join_requests:type_name -> ttn.lorawan.v3.RelayForwardLimits - 66, // 5: ttn.lorawan.v3.ServingRelayForwardingLimits.notifications:type_name -> ttn.lorawan.v3.RelayForwardLimits - 66, // 6: ttn.lorawan.v3.ServingRelayForwardingLimits.uplink_messages:type_name -> ttn.lorawan.v3.RelayForwardLimits - 66, // 7: ttn.lorawan.v3.ServingRelayForwardingLimits.overall:type_name -> ttn.lorawan.v3.RelayForwardLimits - 67, // 8: ttn.lorawan.v3.RelayUplinkForwardingRule.limits:type_name -> ttn.lorawan.v3.RelayUplinkForwardLimits - 68, // 9: ttn.lorawan.v3.ServingRelayParameters.second_channel:type_name -> ttn.lorawan.v3.RelaySecondChannel - 69, // 10: ttn.lorawan.v3.ServingRelayParameters.cad_periodicity:type_name -> ttn.lorawan.v3.RelayCADPeriodicity + 63, // 0: ttn.lorawan.v3.Session.keys:type_name -> ttn.lorawan.v3.SessionKeys + 64, // 1: ttn.lorawan.v3.Session.started_at:type_name -> google.protobuf.Timestamp + 65, // 2: ttn.lorawan.v3.Session.queued_application_downlinks:type_name -> ttn.lorawan.v3.ApplicationDownlink + 66, // 3: ttn.lorawan.v3.ServingRelayForwardingLimits.reset_behavior:type_name -> ttn.lorawan.v3.RelayResetLimitCounter + 67, // 4: ttn.lorawan.v3.ServingRelayForwardingLimits.join_requests:type_name -> ttn.lorawan.v3.RelayForwardLimits + 67, // 5: ttn.lorawan.v3.ServingRelayForwardingLimits.notifications:type_name -> ttn.lorawan.v3.RelayForwardLimits + 67, // 6: ttn.lorawan.v3.ServingRelayForwardingLimits.uplink_messages:type_name -> ttn.lorawan.v3.RelayForwardLimits + 67, // 7: ttn.lorawan.v3.ServingRelayForwardingLimits.overall:type_name -> ttn.lorawan.v3.RelayForwardLimits + 68, // 8: ttn.lorawan.v3.RelayUplinkForwardingRule.limits:type_name -> ttn.lorawan.v3.RelayUplinkForwardLimits + 69, // 9: ttn.lorawan.v3.ServingRelayParameters.second_channel:type_name -> ttn.lorawan.v3.RelaySecondChannel + 70, // 10: ttn.lorawan.v3.ServingRelayParameters.cad_periodicity:type_name -> ttn.lorawan.v3.RelayCADPeriodicity 4, // 11: ttn.lorawan.v3.ServingRelayParameters.uplink_forwarding_rules:type_name -> ttn.lorawan.v3.RelayUplinkForwardingRule 3, // 12: ttn.lorawan.v3.ServingRelayParameters.limits:type_name -> ttn.lorawan.v3.ServingRelayForwardingLimits - 70, // 13: ttn.lorawan.v3.ServedRelayParameters.always:type_name -> ttn.lorawan.v3.RelayEndDeviceAlwaysMode - 71, // 14: ttn.lorawan.v3.ServedRelayParameters.dynamic:type_name -> ttn.lorawan.v3.RelayEndDeviceDynamicMode - 72, // 15: ttn.lorawan.v3.ServedRelayParameters.end_device_controlled:type_name -> ttn.lorawan.v3.RelayEndDeviceControlledMode - 68, // 16: ttn.lorawan.v3.ServedRelayParameters.second_channel:type_name -> ttn.lorawan.v3.RelaySecondChannel + 71, // 13: ttn.lorawan.v3.ServedRelayParameters.always:type_name -> ttn.lorawan.v3.RelayEndDeviceAlwaysMode + 72, // 14: ttn.lorawan.v3.ServedRelayParameters.dynamic:type_name -> ttn.lorawan.v3.RelayEndDeviceDynamicMode + 73, // 15: ttn.lorawan.v3.ServedRelayParameters.end_device_controlled:type_name -> ttn.lorawan.v3.RelayEndDeviceControlledMode + 69, // 16: ttn.lorawan.v3.ServedRelayParameters.second_channel:type_name -> ttn.lorawan.v3.RelaySecondChannel 5, // 17: ttn.lorawan.v3.RelayParameters.serving:type_name -> ttn.lorawan.v3.ServingRelayParameters 6, // 18: ttn.lorawan.v3.RelayParameters.served:type_name -> ttn.lorawan.v3.ServedRelayParameters - 68, // 19: ttn.lorawan.v3.ServingRelaySettings.second_channel:type_name -> ttn.lorawan.v3.RelaySecondChannel - 73, // 20: ttn.lorawan.v3.ServingRelaySettings.default_channel_index:type_name -> google.protobuf.UInt32Value - 69, // 21: ttn.lorawan.v3.ServingRelaySettings.cad_periodicity:type_name -> ttn.lorawan.v3.RelayCADPeriodicity + 69, // 19: ttn.lorawan.v3.ServingRelaySettings.second_channel:type_name -> ttn.lorawan.v3.RelaySecondChannel + 74, // 20: ttn.lorawan.v3.ServingRelaySettings.default_channel_index:type_name -> google.protobuf.UInt32Value + 70, // 21: ttn.lorawan.v3.ServingRelaySettings.cad_periodicity:type_name -> ttn.lorawan.v3.RelayCADPeriodicity 4, // 22: ttn.lorawan.v3.ServingRelaySettings.uplink_forwarding_rules:type_name -> ttn.lorawan.v3.RelayUplinkForwardingRule 3, // 23: ttn.lorawan.v3.ServingRelaySettings.limits:type_name -> ttn.lorawan.v3.ServingRelayForwardingLimits - 70, // 24: ttn.lorawan.v3.ServedRelaySettings.always:type_name -> ttn.lorawan.v3.RelayEndDeviceAlwaysMode - 71, // 25: ttn.lorawan.v3.ServedRelaySettings.dynamic:type_name -> ttn.lorawan.v3.RelayEndDeviceDynamicMode - 72, // 26: ttn.lorawan.v3.ServedRelaySettings.end_device_controlled:type_name -> ttn.lorawan.v3.RelayEndDeviceControlledMode - 73, // 27: ttn.lorawan.v3.ServedRelaySettings.backoff:type_name -> google.protobuf.UInt32Value - 68, // 28: ttn.lorawan.v3.ServedRelaySettings.second_channel:type_name -> ttn.lorawan.v3.RelaySecondChannel + 71, // 24: ttn.lorawan.v3.ServedRelaySettings.always:type_name -> ttn.lorawan.v3.RelayEndDeviceAlwaysMode + 72, // 25: ttn.lorawan.v3.ServedRelaySettings.dynamic:type_name -> ttn.lorawan.v3.RelayEndDeviceDynamicMode + 73, // 26: ttn.lorawan.v3.ServedRelaySettings.end_device_controlled:type_name -> ttn.lorawan.v3.RelayEndDeviceControlledMode + 74, // 27: ttn.lorawan.v3.ServedRelaySettings.backoff:type_name -> google.protobuf.UInt32Value + 69, // 28: ttn.lorawan.v3.ServedRelaySettings.second_channel:type_name -> ttn.lorawan.v3.RelaySecondChannel 8, // 29: ttn.lorawan.v3.RelaySettings.serving:type_name -> ttn.lorawan.v3.ServingRelaySettings 9, // 30: ttn.lorawan.v3.RelaySettings.served:type_name -> ttn.lorawan.v3.ServedRelaySettings - 74, // 31: ttn.lorawan.v3.MACParameters.adr_data_rate_index:type_name -> ttn.lorawan.v3.DataRateIndex - 75, // 32: ttn.lorawan.v3.MACParameters.rx1_delay:type_name -> ttn.lorawan.v3.RxDelay - 76, // 33: ttn.lorawan.v3.MACParameters.rx1_data_rate_offset:type_name -> ttn.lorawan.v3.DataRateOffset - 74, // 34: ttn.lorawan.v3.MACParameters.rx2_data_rate_index:type_name -> ttn.lorawan.v3.DataRateIndex - 77, // 35: ttn.lorawan.v3.MACParameters.max_duty_cycle:type_name -> ttn.lorawan.v3.AggregatedDutyCycle - 78, // 36: ttn.lorawan.v3.MACParameters.rejoin_time_periodicity:type_name -> ttn.lorawan.v3.RejoinTimeExponent - 79, // 37: ttn.lorawan.v3.MACParameters.rejoin_count_periodicity:type_name -> ttn.lorawan.v3.RejoinCountExponent - 74, // 38: ttn.lorawan.v3.MACParameters.ping_slot_data_rate_index:type_name -> ttn.lorawan.v3.DataRateIndex - 34, // 39: ttn.lorawan.v3.MACParameters.channels:type_name -> ttn.lorawan.v3.MACParameters.Channel + 75, // 31: ttn.lorawan.v3.MACParameters.adr_data_rate_index:type_name -> ttn.lorawan.v3.DataRateIndex + 76, // 32: ttn.lorawan.v3.MACParameters.rx1_delay:type_name -> ttn.lorawan.v3.RxDelay + 77, // 33: ttn.lorawan.v3.MACParameters.rx1_data_rate_offset:type_name -> ttn.lorawan.v3.DataRateOffset + 75, // 34: ttn.lorawan.v3.MACParameters.rx2_data_rate_index:type_name -> ttn.lorawan.v3.DataRateIndex + 78, // 35: ttn.lorawan.v3.MACParameters.max_duty_cycle:type_name -> ttn.lorawan.v3.AggregatedDutyCycle + 79, // 36: ttn.lorawan.v3.MACParameters.rejoin_time_periodicity:type_name -> ttn.lorawan.v3.RejoinTimeExponent + 80, // 37: ttn.lorawan.v3.MACParameters.rejoin_count_periodicity:type_name -> ttn.lorawan.v3.RejoinCountExponent + 75, // 38: ttn.lorawan.v3.MACParameters.ping_slot_data_rate_index:type_name -> ttn.lorawan.v3.DataRateIndex + 35, // 39: ttn.lorawan.v3.MACParameters.channels:type_name -> ttn.lorawan.v3.MACParameters.Channel 2, // 40: ttn.lorawan.v3.MACParameters.uplink_dwell_time:type_name -> ttn.lorawan.v3.BoolValue 2, // 41: ttn.lorawan.v3.MACParameters.downlink_dwell_time:type_name -> ttn.lorawan.v3.BoolValue - 80, // 42: ttn.lorawan.v3.MACParameters.adr_ack_limit_exponent:type_name -> ttn.lorawan.v3.ADRAckLimitExponentValue - 81, // 43: ttn.lorawan.v3.MACParameters.adr_ack_delay_exponent:type_name -> ttn.lorawan.v3.ADRAckDelayExponentValue - 82, // 44: ttn.lorawan.v3.MACParameters.ping_slot_data_rate_index_value:type_name -> ttn.lorawan.v3.DataRateIndexValue + 81, // 42: ttn.lorawan.v3.MACParameters.adr_ack_limit_exponent:type_name -> ttn.lorawan.v3.ADRAckLimitExponentValue + 82, // 43: ttn.lorawan.v3.MACParameters.adr_ack_delay_exponent:type_name -> ttn.lorawan.v3.ADRAckDelayExponentValue + 83, // 44: ttn.lorawan.v3.MACParameters.ping_slot_data_rate_index_value:type_name -> ttn.lorawan.v3.DataRateIndexValue 7, // 45: ttn.lorawan.v3.MACParameters.relay:type_name -> ttn.lorawan.v3.RelayParameters - 83, // 46: ttn.lorawan.v3.EndDeviceVersion.ids:type_name -> ttn.lorawan.v3.EndDeviceVersionIdentifiers - 84, // 47: ttn.lorawan.v3.EndDeviceVersion.lorawan_version:type_name -> ttn.lorawan.v3.MACVersion - 85, // 48: ttn.lorawan.v3.EndDeviceVersion.lorawan_phy_version:type_name -> ttn.lorawan.v3.PHYVersion + 84, // 46: ttn.lorawan.v3.EndDeviceVersion.ids:type_name -> ttn.lorawan.v3.EndDeviceVersionIdentifiers + 85, // 47: ttn.lorawan.v3.EndDeviceVersion.lorawan_version:type_name -> ttn.lorawan.v3.MACVersion + 86, // 48: ttn.lorawan.v3.EndDeviceVersion.lorawan_phy_version:type_name -> ttn.lorawan.v3.PHYVersion 14, // 49: ttn.lorawan.v3.EndDeviceVersion.default_mac_settings:type_name -> ttn.lorawan.v3.MACSettings - 86, // 50: ttn.lorawan.v3.EndDeviceVersion.default_formatters:type_name -> ttn.lorawan.v3.MessagePayloadFormatters - 35, // 51: ttn.lorawan.v3.ADRSettings.static:type_name -> ttn.lorawan.v3.ADRSettings.StaticMode - 36, // 52: ttn.lorawan.v3.ADRSettings.dynamic:type_name -> ttn.lorawan.v3.ADRSettings.DynamicMode - 37, // 53: ttn.lorawan.v3.ADRSettings.disabled:type_name -> ttn.lorawan.v3.ADRSettings.DisabledMode - 87, // 54: ttn.lorawan.v3.MACSettings.class_b_timeout:type_name -> google.protobuf.Duration - 88, // 55: ttn.lorawan.v3.MACSettings.ping_slot_periodicity:type_name -> ttn.lorawan.v3.PingSlotPeriodValue - 82, // 56: ttn.lorawan.v3.MACSettings.ping_slot_data_rate_index:type_name -> ttn.lorawan.v3.DataRateIndexValue - 89, // 57: ttn.lorawan.v3.MACSettings.ping_slot_frequency:type_name -> ttn.lorawan.v3.ZeroableFrequencyValue - 89, // 58: ttn.lorawan.v3.MACSettings.beacon_frequency:type_name -> ttn.lorawan.v3.ZeroableFrequencyValue - 87, // 59: ttn.lorawan.v3.MACSettings.class_c_timeout:type_name -> google.protobuf.Duration - 90, // 60: ttn.lorawan.v3.MACSettings.rx1_delay:type_name -> ttn.lorawan.v3.RxDelayValue - 91, // 61: ttn.lorawan.v3.MACSettings.rx1_data_rate_offset:type_name -> ttn.lorawan.v3.DataRateOffsetValue - 82, // 62: ttn.lorawan.v3.MACSettings.rx2_data_rate_index:type_name -> ttn.lorawan.v3.DataRateIndexValue - 92, // 63: ttn.lorawan.v3.MACSettings.rx2_frequency:type_name -> ttn.lorawan.v3.FrequencyValue - 93, // 64: ttn.lorawan.v3.MACSettings.max_duty_cycle:type_name -> ttn.lorawan.v3.AggregatedDutyCycleValue + 87, // 50: ttn.lorawan.v3.EndDeviceVersion.default_formatters:type_name -> ttn.lorawan.v3.MessagePayloadFormatters + 36, // 51: ttn.lorawan.v3.ADRSettings.static:type_name -> ttn.lorawan.v3.ADRSettings.StaticMode + 37, // 52: ttn.lorawan.v3.ADRSettings.dynamic:type_name -> ttn.lorawan.v3.ADRSettings.DynamicMode + 38, // 53: ttn.lorawan.v3.ADRSettings.disabled:type_name -> ttn.lorawan.v3.ADRSettings.DisabledMode + 88, // 54: ttn.lorawan.v3.MACSettings.class_b_timeout:type_name -> google.protobuf.Duration + 89, // 55: ttn.lorawan.v3.MACSettings.ping_slot_periodicity:type_name -> ttn.lorawan.v3.PingSlotPeriodValue + 83, // 56: ttn.lorawan.v3.MACSettings.ping_slot_data_rate_index:type_name -> ttn.lorawan.v3.DataRateIndexValue + 90, // 57: ttn.lorawan.v3.MACSettings.ping_slot_frequency:type_name -> ttn.lorawan.v3.ZeroableFrequencyValue + 90, // 58: ttn.lorawan.v3.MACSettings.beacon_frequency:type_name -> ttn.lorawan.v3.ZeroableFrequencyValue + 88, // 59: ttn.lorawan.v3.MACSettings.class_c_timeout:type_name -> google.protobuf.Duration + 91, // 60: ttn.lorawan.v3.MACSettings.rx1_delay:type_name -> ttn.lorawan.v3.RxDelayValue + 92, // 61: ttn.lorawan.v3.MACSettings.rx1_data_rate_offset:type_name -> ttn.lorawan.v3.DataRateOffsetValue + 83, // 62: ttn.lorawan.v3.MACSettings.rx2_data_rate_index:type_name -> ttn.lorawan.v3.DataRateIndexValue + 93, // 63: ttn.lorawan.v3.MACSettings.rx2_frequency:type_name -> ttn.lorawan.v3.FrequencyValue + 94, // 64: ttn.lorawan.v3.MACSettings.max_duty_cycle:type_name -> ttn.lorawan.v3.AggregatedDutyCycleValue 2, // 65: ttn.lorawan.v3.MACSettings.supports_32_bit_f_cnt:type_name -> ttn.lorawan.v3.BoolValue 2, // 66: ttn.lorawan.v3.MACSettings.use_adr:type_name -> ttn.lorawan.v3.BoolValue - 94, // 67: ttn.lorawan.v3.MACSettings.adr_margin:type_name -> google.protobuf.FloatValue + 95, // 67: ttn.lorawan.v3.MACSettings.adr_margin:type_name -> google.protobuf.FloatValue 2, // 68: ttn.lorawan.v3.MACSettings.resets_f_cnt:type_name -> ttn.lorawan.v3.BoolValue - 87, // 69: ttn.lorawan.v3.MACSettings.status_time_periodicity:type_name -> google.protobuf.Duration - 73, // 70: ttn.lorawan.v3.MACSettings.status_count_periodicity:type_name -> google.protobuf.UInt32Value - 90, // 71: ttn.lorawan.v3.MACSettings.desired_rx1_delay:type_name -> ttn.lorawan.v3.RxDelayValue - 91, // 72: ttn.lorawan.v3.MACSettings.desired_rx1_data_rate_offset:type_name -> ttn.lorawan.v3.DataRateOffsetValue - 82, // 73: ttn.lorawan.v3.MACSettings.desired_rx2_data_rate_index:type_name -> ttn.lorawan.v3.DataRateIndexValue - 92, // 74: ttn.lorawan.v3.MACSettings.desired_rx2_frequency:type_name -> ttn.lorawan.v3.FrequencyValue - 93, // 75: ttn.lorawan.v3.MACSettings.desired_max_duty_cycle:type_name -> ttn.lorawan.v3.AggregatedDutyCycleValue - 80, // 76: ttn.lorawan.v3.MACSettings.desired_adr_ack_limit_exponent:type_name -> ttn.lorawan.v3.ADRAckLimitExponentValue - 81, // 77: ttn.lorawan.v3.MACSettings.desired_adr_ack_delay_exponent:type_name -> ttn.lorawan.v3.ADRAckDelayExponentValue - 82, // 78: ttn.lorawan.v3.MACSettings.desired_ping_slot_data_rate_index:type_name -> ttn.lorawan.v3.DataRateIndexValue - 89, // 79: ttn.lorawan.v3.MACSettings.desired_ping_slot_frequency:type_name -> ttn.lorawan.v3.ZeroableFrequencyValue - 89, // 80: ttn.lorawan.v3.MACSettings.desired_beacon_frequency:type_name -> ttn.lorawan.v3.ZeroableFrequencyValue - 95, // 81: ttn.lorawan.v3.MACSettings.desired_max_eirp:type_name -> ttn.lorawan.v3.DeviceEIRPValue - 87, // 82: ttn.lorawan.v3.MACSettings.class_b_c_downlink_interval:type_name -> google.protobuf.Duration + 88, // 69: ttn.lorawan.v3.MACSettings.status_time_periodicity:type_name -> google.protobuf.Duration + 74, // 70: ttn.lorawan.v3.MACSettings.status_count_periodicity:type_name -> google.protobuf.UInt32Value + 91, // 71: ttn.lorawan.v3.MACSettings.desired_rx1_delay:type_name -> ttn.lorawan.v3.RxDelayValue + 92, // 72: ttn.lorawan.v3.MACSettings.desired_rx1_data_rate_offset:type_name -> ttn.lorawan.v3.DataRateOffsetValue + 83, // 73: ttn.lorawan.v3.MACSettings.desired_rx2_data_rate_index:type_name -> ttn.lorawan.v3.DataRateIndexValue + 93, // 74: ttn.lorawan.v3.MACSettings.desired_rx2_frequency:type_name -> ttn.lorawan.v3.FrequencyValue + 94, // 75: ttn.lorawan.v3.MACSettings.desired_max_duty_cycle:type_name -> ttn.lorawan.v3.AggregatedDutyCycleValue + 81, // 76: ttn.lorawan.v3.MACSettings.desired_adr_ack_limit_exponent:type_name -> ttn.lorawan.v3.ADRAckLimitExponentValue + 82, // 77: ttn.lorawan.v3.MACSettings.desired_adr_ack_delay_exponent:type_name -> ttn.lorawan.v3.ADRAckDelayExponentValue + 83, // 78: ttn.lorawan.v3.MACSettings.desired_ping_slot_data_rate_index:type_name -> ttn.lorawan.v3.DataRateIndexValue + 90, // 79: ttn.lorawan.v3.MACSettings.desired_ping_slot_frequency:type_name -> ttn.lorawan.v3.ZeroableFrequencyValue + 90, // 80: ttn.lorawan.v3.MACSettings.desired_beacon_frequency:type_name -> ttn.lorawan.v3.ZeroableFrequencyValue + 96, // 81: ttn.lorawan.v3.MACSettings.desired_max_eirp:type_name -> ttn.lorawan.v3.DeviceEIRPValue + 88, // 82: ttn.lorawan.v3.MACSettings.class_b_c_downlink_interval:type_name -> google.protobuf.Duration 2, // 83: ttn.lorawan.v3.MACSettings.uplink_dwell_time:type_name -> ttn.lorawan.v3.BoolValue 2, // 84: ttn.lorawan.v3.MACSettings.downlink_dwell_time:type_name -> ttn.lorawan.v3.BoolValue 13, // 85: ttn.lorawan.v3.MACSettings.adr:type_name -> ttn.lorawan.v3.ADRSettings 2, // 86: ttn.lorawan.v3.MACSettings.schedule_downlinks:type_name -> ttn.lorawan.v3.BoolValue 10, // 87: ttn.lorawan.v3.MACSettings.relay:type_name -> ttn.lorawan.v3.RelaySettings 10, // 88: ttn.lorawan.v3.MACSettings.desired_relay:type_name -> ttn.lorawan.v3.RelaySettings - 11, // 89: ttn.lorawan.v3.MACState.current_parameters:type_name -> ttn.lorawan.v3.MACParameters - 11, // 90: ttn.lorawan.v3.MACState.desired_parameters:type_name -> ttn.lorawan.v3.MACParameters - 96, // 91: ttn.lorawan.v3.MACState.device_class:type_name -> ttn.lorawan.v3.Class - 84, // 92: ttn.lorawan.v3.MACState.lorawan_version:type_name -> ttn.lorawan.v3.MACVersion - 63, // 93: ttn.lorawan.v3.MACState.last_confirmed_downlink_at:type_name -> google.protobuf.Timestamp - 88, // 94: ttn.lorawan.v3.MACState.ping_slot_periodicity:type_name -> ttn.lorawan.v3.PingSlotPeriodValue - 64, // 95: ttn.lorawan.v3.MACState.pending_application_downlink:type_name -> ttn.lorawan.v3.ApplicationDownlink - 97, // 96: ttn.lorawan.v3.MACState.queued_responses:type_name -> ttn.lorawan.v3.MACCommand - 97, // 97: ttn.lorawan.v3.MACState.pending_requests:type_name -> ttn.lorawan.v3.MACCommand - 44, // 98: ttn.lorawan.v3.MACState.queued_join_accept:type_name -> ttn.lorawan.v3.MACState.JoinAccept - 43, // 99: ttn.lorawan.v3.MACState.pending_join_request:type_name -> ttn.lorawan.v3.MACState.JoinRequest - 45, // 100: ttn.lorawan.v3.MACState.recent_uplinks:type_name -> ttn.lorawan.v3.MACState.UplinkMessage - 46, // 101: ttn.lorawan.v3.MACState.recent_downlinks:type_name -> ttn.lorawan.v3.MACState.DownlinkMessage - 63, // 102: ttn.lorawan.v3.MACState.last_network_initiated_downlink_at:type_name -> google.protobuf.Timestamp - 74, // 103: ttn.lorawan.v3.MACState.rejected_adr_data_rate_indexes:type_name -> ttn.lorawan.v3.DataRateIndex - 63, // 104: ttn.lorawan.v3.MACState.last_downlink_at:type_name -> google.protobuf.Timestamp - 49, // 105: ttn.lorawan.v3.MACState.rejected_data_rate_ranges:type_name -> ttn.lorawan.v3.MACState.RejectedDataRateRangesEntry - 98, // 106: ttn.lorawan.v3.MACState.recent_mac_command_identifiers:type_name -> ttn.lorawan.v3.MACCommandIdentifier - 99, // 107: ttn.lorawan.v3.MACState.pending_relay_downlink:type_name -> ttn.lorawan.v3.RelayForwardDownlinkReq - 63, // 108: ttn.lorawan.v3.EndDeviceAuthenticationCode.valid_from:type_name -> google.protobuf.Timestamp - 63, // 109: ttn.lorawan.v3.EndDeviceAuthenticationCode.valid_to:type_name -> google.protobuf.Timestamp - 100, // 110: ttn.lorawan.v3.EndDevice.ids:type_name -> ttn.lorawan.v3.EndDeviceIdentifiers - 63, // 111: ttn.lorawan.v3.EndDevice.created_at:type_name -> google.protobuf.Timestamp - 63, // 112: ttn.lorawan.v3.EndDevice.updated_at:type_name -> google.protobuf.Timestamp - 57, // 113: ttn.lorawan.v3.EndDevice.attributes:type_name -> ttn.lorawan.v3.EndDevice.AttributesEntry - 83, // 114: ttn.lorawan.v3.EndDevice.version_ids:type_name -> ttn.lorawan.v3.EndDeviceVersionIdentifiers - 58, // 115: ttn.lorawan.v3.EndDevice.locations:type_name -> ttn.lorawan.v3.EndDevice.LocationsEntry - 101, // 116: ttn.lorawan.v3.EndDevice.picture:type_name -> ttn.lorawan.v3.Picture - 84, // 117: ttn.lorawan.v3.EndDevice.lorawan_version:type_name -> ttn.lorawan.v3.MACVersion - 85, // 118: ttn.lorawan.v3.EndDevice.lorawan_phy_version:type_name -> ttn.lorawan.v3.PHYVersion - 102, // 119: ttn.lorawan.v3.EndDevice.root_keys:type_name -> ttn.lorawan.v3.RootKeys - 14, // 120: ttn.lorawan.v3.EndDevice.mac_settings:type_name -> ttn.lorawan.v3.MACSettings - 15, // 121: ttn.lorawan.v3.EndDevice.mac_state:type_name -> ttn.lorawan.v3.MACState - 15, // 122: ttn.lorawan.v3.EndDevice.pending_mac_state:type_name -> ttn.lorawan.v3.MACState - 1, // 123: ttn.lorawan.v3.EndDevice.session:type_name -> ttn.lorawan.v3.Session - 1, // 124: ttn.lorawan.v3.EndDevice.pending_session:type_name -> ttn.lorawan.v3.Session - 63, // 125: ttn.lorawan.v3.EndDevice.last_dev_status_received_at:type_name -> google.protobuf.Timestamp - 0, // 126: ttn.lorawan.v3.EndDevice.power_state:type_name -> ttn.lorawan.v3.PowerState - 94, // 127: ttn.lorawan.v3.EndDevice.battery_percentage:type_name -> google.protobuf.FloatValue - 64, // 128: ttn.lorawan.v3.EndDevice.queued_application_downlinks:type_name -> ttn.lorawan.v3.ApplicationDownlink - 86, // 129: ttn.lorawan.v3.EndDevice.formatters:type_name -> ttn.lorawan.v3.MessagePayloadFormatters - 103, // 130: ttn.lorawan.v3.EndDevice.provisioning_data:type_name -> google.protobuf.Struct - 16, // 131: ttn.lorawan.v3.EndDevice.claim_authentication_code:type_name -> ttn.lorawan.v3.EndDeviceAuthenticationCode - 104, // 132: ttn.lorawan.v3.EndDevice.skip_payload_crypto_override:type_name -> google.protobuf.BoolValue - 63, // 133: ttn.lorawan.v3.EndDevice.activated_at:type_name -> google.protobuf.Timestamp - 63, // 134: ttn.lorawan.v3.EndDevice.last_seen_at:type_name -> google.protobuf.Timestamp - 105, // 135: ttn.lorawan.v3.EndDevice.lora_alliance_profile_ids:type_name -> ttn.lorawan.v3.LoRaAllianceProfileIdentifiers - 17, // 136: ttn.lorawan.v3.EndDevices.end_devices:type_name -> ttn.lorawan.v3.EndDevice - 17, // 137: ttn.lorawan.v3.CreateEndDeviceRequest.end_device:type_name -> ttn.lorawan.v3.EndDevice - 17, // 138: ttn.lorawan.v3.UpdateEndDeviceRequest.end_device:type_name -> ttn.lorawan.v3.EndDevice - 106, // 139: ttn.lorawan.v3.UpdateEndDeviceRequest.field_mask:type_name -> google.protobuf.FieldMask - 59, // 140: ttn.lorawan.v3.BatchUpdateEndDeviceLastSeenRequest.updates:type_name -> ttn.lorawan.v3.BatchUpdateEndDeviceLastSeenRequest.EndDeviceLastSeenUpdate - 100, // 141: ttn.lorawan.v3.GetEndDeviceRequest.end_device_ids:type_name -> ttn.lorawan.v3.EndDeviceIdentifiers - 106, // 142: ttn.lorawan.v3.GetEndDeviceRequest.field_mask:type_name -> google.protobuf.FieldMask - 107, // 143: ttn.lorawan.v3.ListEndDevicesRequest.application_ids:type_name -> ttn.lorawan.v3.ApplicationIdentifiers - 106, // 144: ttn.lorawan.v3.ListEndDevicesRequest.field_mask:type_name -> google.protobuf.FieldMask - 60, // 145: ttn.lorawan.v3.ListEndDevicesRequest.filters:type_name -> ttn.lorawan.v3.ListEndDevicesRequest.Filter - 17, // 146: ttn.lorawan.v3.SetEndDeviceRequest.end_device:type_name -> ttn.lorawan.v3.EndDevice - 106, // 147: ttn.lorawan.v3.SetEndDeviceRequest.field_mask:type_name -> google.protobuf.FieldMask - 100, // 148: ttn.lorawan.v3.ResetAndGetEndDeviceRequest.end_device_ids:type_name -> ttn.lorawan.v3.EndDeviceIdentifiers - 106, // 149: ttn.lorawan.v3.ResetAndGetEndDeviceRequest.field_mask:type_name -> google.protobuf.FieldMask - 17, // 150: ttn.lorawan.v3.EndDeviceTemplate.end_device:type_name -> ttn.lorawan.v3.EndDevice - 106, // 151: ttn.lorawan.v3.EndDeviceTemplate.field_mask:type_name -> google.protobuf.FieldMask - 61, // 152: ttn.lorawan.v3.EndDeviceTemplateFormats.formats:type_name -> ttn.lorawan.v3.EndDeviceTemplateFormats.FormatsEntry - 83, // 153: ttn.lorawan.v3.ConvertEndDeviceTemplateRequest.end_device_version_ids:type_name -> ttn.lorawan.v3.EndDeviceVersionIdentifiers - 107, // 154: ttn.lorawan.v3.BatchDeleteEndDevicesRequest.application_ids:type_name -> ttn.lorawan.v3.ApplicationIdentifiers - 107, // 155: ttn.lorawan.v3.BatchGetEndDevicesRequest.application_ids:type_name -> ttn.lorawan.v3.ApplicationIdentifiers - 106, // 156: ttn.lorawan.v3.BatchGetEndDevicesRequest.field_mask:type_name -> google.protobuf.FieldMask - 74, // 157: ttn.lorawan.v3.MACParameters.Channel.min_data_rate_index:type_name -> ttn.lorawan.v3.DataRateIndex - 74, // 158: ttn.lorawan.v3.MACParameters.Channel.max_data_rate_index:type_name -> ttn.lorawan.v3.DataRateIndex - 74, // 159: ttn.lorawan.v3.ADRSettings.StaticMode.data_rate_index:type_name -> ttn.lorawan.v3.DataRateIndex - 94, // 160: ttn.lorawan.v3.ADRSettings.DynamicMode.margin:type_name -> google.protobuf.FloatValue - 82, // 161: ttn.lorawan.v3.ADRSettings.DynamicMode.min_data_rate_index:type_name -> ttn.lorawan.v3.DataRateIndexValue - 82, // 162: ttn.lorawan.v3.ADRSettings.DynamicMode.max_data_rate_index:type_name -> ttn.lorawan.v3.DataRateIndexValue - 73, // 163: ttn.lorawan.v3.ADRSettings.DynamicMode.min_tx_power_index:type_name -> google.protobuf.UInt32Value - 73, // 164: ttn.lorawan.v3.ADRSettings.DynamicMode.max_tx_power_index:type_name -> google.protobuf.UInt32Value - 73, // 165: ttn.lorawan.v3.ADRSettings.DynamicMode.min_nb_trans:type_name -> google.protobuf.UInt32Value - 73, // 166: ttn.lorawan.v3.ADRSettings.DynamicMode.max_nb_trans:type_name -> google.protobuf.UInt32Value - 38, // 167: ttn.lorawan.v3.ADRSettings.DynamicMode.channel_steering:type_name -> ttn.lorawan.v3.ADRSettings.DynamicMode.ChannelSteeringSettings - 40, // 168: ttn.lorawan.v3.ADRSettings.DynamicMode.overrides:type_name -> ttn.lorawan.v3.ADRSettings.DynamicMode.Overrides - 41, // 169: ttn.lorawan.v3.ADRSettings.DynamicMode.ChannelSteeringSettings.lora_narrow:type_name -> ttn.lorawan.v3.ADRSettings.DynamicMode.ChannelSteeringSettings.LoRaNarrowMode - 42, // 170: ttn.lorawan.v3.ADRSettings.DynamicMode.ChannelSteeringSettings.disabled:type_name -> ttn.lorawan.v3.ADRSettings.DynamicMode.ChannelSteeringSettings.DisabledMode - 73, // 171: ttn.lorawan.v3.ADRSettings.DynamicMode.PerDataRateIndexOverride.min_nb_trans:type_name -> google.protobuf.UInt32Value - 73, // 172: ttn.lorawan.v3.ADRSettings.DynamicMode.PerDataRateIndexOverride.max_nb_trans:type_name -> google.protobuf.UInt32Value - 39, // 173: ttn.lorawan.v3.ADRSettings.DynamicMode.Overrides.data_rate_0:type_name -> ttn.lorawan.v3.ADRSettings.DynamicMode.PerDataRateIndexOverride - 39, // 174: ttn.lorawan.v3.ADRSettings.DynamicMode.Overrides.data_rate_1:type_name -> ttn.lorawan.v3.ADRSettings.DynamicMode.PerDataRateIndexOverride - 39, // 175: ttn.lorawan.v3.ADRSettings.DynamicMode.Overrides.data_rate_2:type_name -> ttn.lorawan.v3.ADRSettings.DynamicMode.PerDataRateIndexOverride - 39, // 176: ttn.lorawan.v3.ADRSettings.DynamicMode.Overrides.data_rate_3:type_name -> ttn.lorawan.v3.ADRSettings.DynamicMode.PerDataRateIndexOverride - 39, // 177: ttn.lorawan.v3.ADRSettings.DynamicMode.Overrides.data_rate_4:type_name -> ttn.lorawan.v3.ADRSettings.DynamicMode.PerDataRateIndexOverride - 39, // 178: ttn.lorawan.v3.ADRSettings.DynamicMode.Overrides.data_rate_5:type_name -> ttn.lorawan.v3.ADRSettings.DynamicMode.PerDataRateIndexOverride - 39, // 179: ttn.lorawan.v3.ADRSettings.DynamicMode.Overrides.data_rate_6:type_name -> ttn.lorawan.v3.ADRSettings.DynamicMode.PerDataRateIndexOverride - 39, // 180: ttn.lorawan.v3.ADRSettings.DynamicMode.Overrides.data_rate_7:type_name -> ttn.lorawan.v3.ADRSettings.DynamicMode.PerDataRateIndexOverride - 39, // 181: ttn.lorawan.v3.ADRSettings.DynamicMode.Overrides.data_rate_8:type_name -> ttn.lorawan.v3.ADRSettings.DynamicMode.PerDataRateIndexOverride - 39, // 182: ttn.lorawan.v3.ADRSettings.DynamicMode.Overrides.data_rate_9:type_name -> ttn.lorawan.v3.ADRSettings.DynamicMode.PerDataRateIndexOverride - 39, // 183: ttn.lorawan.v3.ADRSettings.DynamicMode.Overrides.data_rate_10:type_name -> ttn.lorawan.v3.ADRSettings.DynamicMode.PerDataRateIndexOverride - 39, // 184: ttn.lorawan.v3.ADRSettings.DynamicMode.Overrides.data_rate_11:type_name -> ttn.lorawan.v3.ADRSettings.DynamicMode.PerDataRateIndexOverride - 39, // 185: ttn.lorawan.v3.ADRSettings.DynamicMode.Overrides.data_rate_12:type_name -> ttn.lorawan.v3.ADRSettings.DynamicMode.PerDataRateIndexOverride - 39, // 186: ttn.lorawan.v3.ADRSettings.DynamicMode.Overrides.data_rate_13:type_name -> ttn.lorawan.v3.ADRSettings.DynamicMode.PerDataRateIndexOverride - 39, // 187: ttn.lorawan.v3.ADRSettings.DynamicMode.Overrides.data_rate_14:type_name -> ttn.lorawan.v3.ADRSettings.DynamicMode.PerDataRateIndexOverride - 39, // 188: ttn.lorawan.v3.ADRSettings.DynamicMode.Overrides.data_rate_15:type_name -> ttn.lorawan.v3.ADRSettings.DynamicMode.PerDataRateIndexOverride - 108, // 189: ttn.lorawan.v3.MACState.JoinRequest.downlink_settings:type_name -> ttn.lorawan.v3.DLSettings - 75, // 190: ttn.lorawan.v3.MACState.JoinRequest.rx_delay:type_name -> ttn.lorawan.v3.RxDelay - 109, // 191: ttn.lorawan.v3.MACState.JoinRequest.cf_list:type_name -> ttn.lorawan.v3.CFList - 43, // 192: ttn.lorawan.v3.MACState.JoinAccept.request:type_name -> ttn.lorawan.v3.MACState.JoinRequest - 62, // 193: ttn.lorawan.v3.MACState.JoinAccept.keys:type_name -> ttn.lorawan.v3.SessionKeys - 110, // 194: ttn.lorawan.v3.MACState.UplinkMessage.payload:type_name -> ttn.lorawan.v3.Message - 50, // 195: ttn.lorawan.v3.MACState.UplinkMessage.settings:type_name -> ttn.lorawan.v3.MACState.UplinkMessage.TxSettings - 51, // 196: ttn.lorawan.v3.MACState.UplinkMessage.rx_metadata:type_name -> ttn.lorawan.v3.MACState.UplinkMessage.RxMetadata - 63, // 197: ttn.lorawan.v3.MACState.UplinkMessage.received_at:type_name -> google.protobuf.Timestamp - 54, // 198: ttn.lorawan.v3.MACState.DownlinkMessage.payload:type_name -> ttn.lorawan.v3.MACState.DownlinkMessage.Message - 74, // 199: ttn.lorawan.v3.MACState.DataRateRange.min_data_rate_index:type_name -> ttn.lorawan.v3.DataRateIndex - 74, // 200: ttn.lorawan.v3.MACState.DataRateRange.max_data_rate_index:type_name -> ttn.lorawan.v3.DataRateIndex - 47, // 201: ttn.lorawan.v3.MACState.DataRateRanges.ranges:type_name -> ttn.lorawan.v3.MACState.DataRateRange - 48, // 202: ttn.lorawan.v3.MACState.RejectedDataRateRangesEntry.value:type_name -> ttn.lorawan.v3.MACState.DataRateRanges - 111, // 203: ttn.lorawan.v3.MACState.UplinkMessage.TxSettings.data_rate:type_name -> ttn.lorawan.v3.DataRate - 112, // 204: ttn.lorawan.v3.MACState.UplinkMessage.RxMetadata.gateway_ids:type_name -> ttn.lorawan.v3.GatewayIdentifiers - 113, // 205: ttn.lorawan.v3.MACState.UplinkMessage.RxMetadata.downlink_path_constraint:type_name -> ttn.lorawan.v3.DownlinkPathConstraint - 52, // 206: ttn.lorawan.v3.MACState.UplinkMessage.RxMetadata.packet_broker:type_name -> ttn.lorawan.v3.MACState.UplinkMessage.RxMetadata.PacketBrokerMetadata - 53, // 207: ttn.lorawan.v3.MACState.UplinkMessage.RxMetadata.relay:type_name -> ttn.lorawan.v3.MACState.UplinkMessage.RxMetadata.RelayMetadata - 55, // 208: ttn.lorawan.v3.MACState.DownlinkMessage.Message.m_hdr:type_name -> ttn.lorawan.v3.MACState.DownlinkMessage.Message.MHDR - 56, // 209: ttn.lorawan.v3.MACState.DownlinkMessage.Message.mac_payload:type_name -> ttn.lorawan.v3.MACState.DownlinkMessage.Message.MACPayload - 114, // 210: ttn.lorawan.v3.MACState.DownlinkMessage.Message.MHDR.m_type:type_name -> ttn.lorawan.v3.MType - 115, // 211: ttn.lorawan.v3.EndDevice.LocationsEntry.value:type_name -> ttn.lorawan.v3.Location - 100, // 212: ttn.lorawan.v3.BatchUpdateEndDeviceLastSeenRequest.EndDeviceLastSeenUpdate.ids:type_name -> ttn.lorawan.v3.EndDeviceIdentifiers - 63, // 213: ttn.lorawan.v3.BatchUpdateEndDeviceLastSeenRequest.EndDeviceLastSeenUpdate.last_seen_at:type_name -> google.protobuf.Timestamp - 63, // 214: ttn.lorawan.v3.ListEndDevicesRequest.Filter.updated_since:type_name -> google.protobuf.Timestamp - 29, // 215: ttn.lorawan.v3.EndDeviceTemplateFormats.FormatsEntry.value:type_name -> ttn.lorawan.v3.EndDeviceTemplateFormat - 216, // [216:216] is the sub-list for method output_type - 216, // [216:216] is the sub-list for method input_type - 216, // [216:216] is the sub-list for extension type_name - 216, // [216:216] is the sub-list for extension extendee - 0, // [0:216] is the sub-list for field type_name + 97, // 89: ttn.lorawan.v3.MACSettingsProfile.ids:type_name -> ttn.lorawan.v3.MACSettingsProfileIdentifiers + 14, // 90: ttn.lorawan.v3.MACSettingsProfile.mac_settings:type_name -> ttn.lorawan.v3.MACSettings + 11, // 91: ttn.lorawan.v3.MACState.current_parameters:type_name -> ttn.lorawan.v3.MACParameters + 11, // 92: ttn.lorawan.v3.MACState.desired_parameters:type_name -> ttn.lorawan.v3.MACParameters + 98, // 93: ttn.lorawan.v3.MACState.device_class:type_name -> ttn.lorawan.v3.Class + 85, // 94: ttn.lorawan.v3.MACState.lorawan_version:type_name -> ttn.lorawan.v3.MACVersion + 64, // 95: ttn.lorawan.v3.MACState.last_confirmed_downlink_at:type_name -> google.protobuf.Timestamp + 89, // 96: ttn.lorawan.v3.MACState.ping_slot_periodicity:type_name -> ttn.lorawan.v3.PingSlotPeriodValue + 65, // 97: ttn.lorawan.v3.MACState.pending_application_downlink:type_name -> ttn.lorawan.v3.ApplicationDownlink + 99, // 98: ttn.lorawan.v3.MACState.queued_responses:type_name -> ttn.lorawan.v3.MACCommand + 99, // 99: ttn.lorawan.v3.MACState.pending_requests:type_name -> ttn.lorawan.v3.MACCommand + 45, // 100: ttn.lorawan.v3.MACState.queued_join_accept:type_name -> ttn.lorawan.v3.MACState.JoinAccept + 44, // 101: ttn.lorawan.v3.MACState.pending_join_request:type_name -> ttn.lorawan.v3.MACState.JoinRequest + 46, // 102: ttn.lorawan.v3.MACState.recent_uplinks:type_name -> ttn.lorawan.v3.MACState.UplinkMessage + 47, // 103: ttn.lorawan.v3.MACState.recent_downlinks:type_name -> ttn.lorawan.v3.MACState.DownlinkMessage + 64, // 104: ttn.lorawan.v3.MACState.last_network_initiated_downlink_at:type_name -> google.protobuf.Timestamp + 75, // 105: ttn.lorawan.v3.MACState.rejected_adr_data_rate_indexes:type_name -> ttn.lorawan.v3.DataRateIndex + 64, // 106: ttn.lorawan.v3.MACState.last_downlink_at:type_name -> google.protobuf.Timestamp + 50, // 107: ttn.lorawan.v3.MACState.rejected_data_rate_ranges:type_name -> ttn.lorawan.v3.MACState.RejectedDataRateRangesEntry + 100, // 108: ttn.lorawan.v3.MACState.recent_mac_command_identifiers:type_name -> ttn.lorawan.v3.MACCommandIdentifier + 101, // 109: ttn.lorawan.v3.MACState.pending_relay_downlink:type_name -> ttn.lorawan.v3.RelayForwardDownlinkReq + 64, // 110: ttn.lorawan.v3.EndDeviceAuthenticationCode.valid_from:type_name -> google.protobuf.Timestamp + 64, // 111: ttn.lorawan.v3.EndDeviceAuthenticationCode.valid_to:type_name -> google.protobuf.Timestamp + 102, // 112: ttn.lorawan.v3.EndDevice.ids:type_name -> ttn.lorawan.v3.EndDeviceIdentifiers + 64, // 113: ttn.lorawan.v3.EndDevice.created_at:type_name -> google.protobuf.Timestamp + 64, // 114: ttn.lorawan.v3.EndDevice.updated_at:type_name -> google.protobuf.Timestamp + 58, // 115: ttn.lorawan.v3.EndDevice.attributes:type_name -> ttn.lorawan.v3.EndDevice.AttributesEntry + 84, // 116: ttn.lorawan.v3.EndDevice.version_ids:type_name -> ttn.lorawan.v3.EndDeviceVersionIdentifiers + 59, // 117: ttn.lorawan.v3.EndDevice.locations:type_name -> ttn.lorawan.v3.EndDevice.LocationsEntry + 103, // 118: ttn.lorawan.v3.EndDevice.picture:type_name -> ttn.lorawan.v3.Picture + 85, // 119: ttn.lorawan.v3.EndDevice.lorawan_version:type_name -> ttn.lorawan.v3.MACVersion + 86, // 120: ttn.lorawan.v3.EndDevice.lorawan_phy_version:type_name -> ttn.lorawan.v3.PHYVersion + 104, // 121: ttn.lorawan.v3.EndDevice.root_keys:type_name -> ttn.lorawan.v3.RootKeys + 14, // 122: ttn.lorawan.v3.EndDevice.mac_settings:type_name -> ttn.lorawan.v3.MACSettings + 16, // 123: ttn.lorawan.v3.EndDevice.mac_state:type_name -> ttn.lorawan.v3.MACState + 16, // 124: ttn.lorawan.v3.EndDevice.pending_mac_state:type_name -> ttn.lorawan.v3.MACState + 1, // 125: ttn.lorawan.v3.EndDevice.session:type_name -> ttn.lorawan.v3.Session + 1, // 126: ttn.lorawan.v3.EndDevice.pending_session:type_name -> ttn.lorawan.v3.Session + 64, // 127: ttn.lorawan.v3.EndDevice.last_dev_status_received_at:type_name -> google.protobuf.Timestamp + 0, // 128: ttn.lorawan.v3.EndDevice.power_state:type_name -> ttn.lorawan.v3.PowerState + 95, // 129: ttn.lorawan.v3.EndDevice.battery_percentage:type_name -> google.protobuf.FloatValue + 65, // 130: ttn.lorawan.v3.EndDevice.queued_application_downlinks:type_name -> ttn.lorawan.v3.ApplicationDownlink + 87, // 131: ttn.lorawan.v3.EndDevice.formatters:type_name -> ttn.lorawan.v3.MessagePayloadFormatters + 105, // 132: ttn.lorawan.v3.EndDevice.provisioning_data:type_name -> google.protobuf.Struct + 17, // 133: ttn.lorawan.v3.EndDevice.claim_authentication_code:type_name -> ttn.lorawan.v3.EndDeviceAuthenticationCode + 106, // 134: ttn.lorawan.v3.EndDevice.skip_payload_crypto_override:type_name -> google.protobuf.BoolValue + 64, // 135: ttn.lorawan.v3.EndDevice.activated_at:type_name -> google.protobuf.Timestamp + 64, // 136: ttn.lorawan.v3.EndDevice.last_seen_at:type_name -> google.protobuf.Timestamp + 107, // 137: ttn.lorawan.v3.EndDevice.lora_alliance_profile_ids:type_name -> ttn.lorawan.v3.LoRaAllianceProfileIdentifiers + 18, // 138: ttn.lorawan.v3.EndDevices.end_devices:type_name -> ttn.lorawan.v3.EndDevice + 18, // 139: ttn.lorawan.v3.CreateEndDeviceRequest.end_device:type_name -> ttn.lorawan.v3.EndDevice + 18, // 140: ttn.lorawan.v3.UpdateEndDeviceRequest.end_device:type_name -> ttn.lorawan.v3.EndDevice + 108, // 141: ttn.lorawan.v3.UpdateEndDeviceRequest.field_mask:type_name -> google.protobuf.FieldMask + 60, // 142: ttn.lorawan.v3.BatchUpdateEndDeviceLastSeenRequest.updates:type_name -> ttn.lorawan.v3.BatchUpdateEndDeviceLastSeenRequest.EndDeviceLastSeenUpdate + 102, // 143: ttn.lorawan.v3.GetEndDeviceRequest.end_device_ids:type_name -> ttn.lorawan.v3.EndDeviceIdentifiers + 108, // 144: ttn.lorawan.v3.GetEndDeviceRequest.field_mask:type_name -> google.protobuf.FieldMask + 109, // 145: ttn.lorawan.v3.ListEndDevicesRequest.application_ids:type_name -> ttn.lorawan.v3.ApplicationIdentifiers + 108, // 146: ttn.lorawan.v3.ListEndDevicesRequest.field_mask:type_name -> google.protobuf.FieldMask + 61, // 147: ttn.lorawan.v3.ListEndDevicesRequest.filters:type_name -> ttn.lorawan.v3.ListEndDevicesRequest.Filter + 18, // 148: ttn.lorawan.v3.SetEndDeviceRequest.end_device:type_name -> ttn.lorawan.v3.EndDevice + 108, // 149: ttn.lorawan.v3.SetEndDeviceRequest.field_mask:type_name -> google.protobuf.FieldMask + 102, // 150: ttn.lorawan.v3.ResetAndGetEndDeviceRequest.end_device_ids:type_name -> ttn.lorawan.v3.EndDeviceIdentifiers + 108, // 151: ttn.lorawan.v3.ResetAndGetEndDeviceRequest.field_mask:type_name -> google.protobuf.FieldMask + 18, // 152: ttn.lorawan.v3.EndDeviceTemplate.end_device:type_name -> ttn.lorawan.v3.EndDevice + 108, // 153: ttn.lorawan.v3.EndDeviceTemplate.field_mask:type_name -> google.protobuf.FieldMask + 62, // 154: ttn.lorawan.v3.EndDeviceTemplateFormats.formats:type_name -> ttn.lorawan.v3.EndDeviceTemplateFormats.FormatsEntry + 84, // 155: ttn.lorawan.v3.ConvertEndDeviceTemplateRequest.end_device_version_ids:type_name -> ttn.lorawan.v3.EndDeviceVersionIdentifiers + 109, // 156: ttn.lorawan.v3.BatchDeleteEndDevicesRequest.application_ids:type_name -> ttn.lorawan.v3.ApplicationIdentifiers + 109, // 157: ttn.lorawan.v3.BatchGetEndDevicesRequest.application_ids:type_name -> ttn.lorawan.v3.ApplicationIdentifiers + 108, // 158: ttn.lorawan.v3.BatchGetEndDevicesRequest.field_mask:type_name -> google.protobuf.FieldMask + 75, // 159: ttn.lorawan.v3.MACParameters.Channel.min_data_rate_index:type_name -> ttn.lorawan.v3.DataRateIndex + 75, // 160: ttn.lorawan.v3.MACParameters.Channel.max_data_rate_index:type_name -> ttn.lorawan.v3.DataRateIndex + 75, // 161: ttn.lorawan.v3.ADRSettings.StaticMode.data_rate_index:type_name -> ttn.lorawan.v3.DataRateIndex + 95, // 162: ttn.lorawan.v3.ADRSettings.DynamicMode.margin:type_name -> google.protobuf.FloatValue + 83, // 163: ttn.lorawan.v3.ADRSettings.DynamicMode.min_data_rate_index:type_name -> ttn.lorawan.v3.DataRateIndexValue + 83, // 164: ttn.lorawan.v3.ADRSettings.DynamicMode.max_data_rate_index:type_name -> ttn.lorawan.v3.DataRateIndexValue + 74, // 165: ttn.lorawan.v3.ADRSettings.DynamicMode.min_tx_power_index:type_name -> google.protobuf.UInt32Value + 74, // 166: ttn.lorawan.v3.ADRSettings.DynamicMode.max_tx_power_index:type_name -> google.protobuf.UInt32Value + 74, // 167: ttn.lorawan.v3.ADRSettings.DynamicMode.min_nb_trans:type_name -> google.protobuf.UInt32Value + 74, // 168: ttn.lorawan.v3.ADRSettings.DynamicMode.max_nb_trans:type_name -> google.protobuf.UInt32Value + 39, // 169: ttn.lorawan.v3.ADRSettings.DynamicMode.channel_steering:type_name -> ttn.lorawan.v3.ADRSettings.DynamicMode.ChannelSteeringSettings + 41, // 170: ttn.lorawan.v3.ADRSettings.DynamicMode.overrides:type_name -> ttn.lorawan.v3.ADRSettings.DynamicMode.Overrides + 42, // 171: ttn.lorawan.v3.ADRSettings.DynamicMode.ChannelSteeringSettings.lora_narrow:type_name -> ttn.lorawan.v3.ADRSettings.DynamicMode.ChannelSteeringSettings.LoRaNarrowMode + 43, // 172: ttn.lorawan.v3.ADRSettings.DynamicMode.ChannelSteeringSettings.disabled:type_name -> ttn.lorawan.v3.ADRSettings.DynamicMode.ChannelSteeringSettings.DisabledMode + 74, // 173: ttn.lorawan.v3.ADRSettings.DynamicMode.PerDataRateIndexOverride.min_nb_trans:type_name -> google.protobuf.UInt32Value + 74, // 174: ttn.lorawan.v3.ADRSettings.DynamicMode.PerDataRateIndexOverride.max_nb_trans:type_name -> google.protobuf.UInt32Value + 40, // 175: ttn.lorawan.v3.ADRSettings.DynamicMode.Overrides.data_rate_0:type_name -> ttn.lorawan.v3.ADRSettings.DynamicMode.PerDataRateIndexOverride + 40, // 176: ttn.lorawan.v3.ADRSettings.DynamicMode.Overrides.data_rate_1:type_name -> ttn.lorawan.v3.ADRSettings.DynamicMode.PerDataRateIndexOverride + 40, // 177: ttn.lorawan.v3.ADRSettings.DynamicMode.Overrides.data_rate_2:type_name -> ttn.lorawan.v3.ADRSettings.DynamicMode.PerDataRateIndexOverride + 40, // 178: ttn.lorawan.v3.ADRSettings.DynamicMode.Overrides.data_rate_3:type_name -> ttn.lorawan.v3.ADRSettings.DynamicMode.PerDataRateIndexOverride + 40, // 179: ttn.lorawan.v3.ADRSettings.DynamicMode.Overrides.data_rate_4:type_name -> ttn.lorawan.v3.ADRSettings.DynamicMode.PerDataRateIndexOverride + 40, // 180: ttn.lorawan.v3.ADRSettings.DynamicMode.Overrides.data_rate_5:type_name -> ttn.lorawan.v3.ADRSettings.DynamicMode.PerDataRateIndexOverride + 40, // 181: ttn.lorawan.v3.ADRSettings.DynamicMode.Overrides.data_rate_6:type_name -> ttn.lorawan.v3.ADRSettings.DynamicMode.PerDataRateIndexOverride + 40, // 182: ttn.lorawan.v3.ADRSettings.DynamicMode.Overrides.data_rate_7:type_name -> ttn.lorawan.v3.ADRSettings.DynamicMode.PerDataRateIndexOverride + 40, // 183: ttn.lorawan.v3.ADRSettings.DynamicMode.Overrides.data_rate_8:type_name -> ttn.lorawan.v3.ADRSettings.DynamicMode.PerDataRateIndexOverride + 40, // 184: ttn.lorawan.v3.ADRSettings.DynamicMode.Overrides.data_rate_9:type_name -> ttn.lorawan.v3.ADRSettings.DynamicMode.PerDataRateIndexOverride + 40, // 185: ttn.lorawan.v3.ADRSettings.DynamicMode.Overrides.data_rate_10:type_name -> ttn.lorawan.v3.ADRSettings.DynamicMode.PerDataRateIndexOverride + 40, // 186: ttn.lorawan.v3.ADRSettings.DynamicMode.Overrides.data_rate_11:type_name -> ttn.lorawan.v3.ADRSettings.DynamicMode.PerDataRateIndexOverride + 40, // 187: ttn.lorawan.v3.ADRSettings.DynamicMode.Overrides.data_rate_12:type_name -> ttn.lorawan.v3.ADRSettings.DynamicMode.PerDataRateIndexOverride + 40, // 188: ttn.lorawan.v3.ADRSettings.DynamicMode.Overrides.data_rate_13:type_name -> ttn.lorawan.v3.ADRSettings.DynamicMode.PerDataRateIndexOverride + 40, // 189: ttn.lorawan.v3.ADRSettings.DynamicMode.Overrides.data_rate_14:type_name -> ttn.lorawan.v3.ADRSettings.DynamicMode.PerDataRateIndexOverride + 40, // 190: ttn.lorawan.v3.ADRSettings.DynamicMode.Overrides.data_rate_15:type_name -> ttn.lorawan.v3.ADRSettings.DynamicMode.PerDataRateIndexOverride + 110, // 191: ttn.lorawan.v3.MACState.JoinRequest.downlink_settings:type_name -> ttn.lorawan.v3.DLSettings + 76, // 192: ttn.lorawan.v3.MACState.JoinRequest.rx_delay:type_name -> ttn.lorawan.v3.RxDelay + 111, // 193: ttn.lorawan.v3.MACState.JoinRequest.cf_list:type_name -> ttn.lorawan.v3.CFList + 44, // 194: ttn.lorawan.v3.MACState.JoinAccept.request:type_name -> ttn.lorawan.v3.MACState.JoinRequest + 63, // 195: ttn.lorawan.v3.MACState.JoinAccept.keys:type_name -> ttn.lorawan.v3.SessionKeys + 112, // 196: ttn.lorawan.v3.MACState.UplinkMessage.payload:type_name -> ttn.lorawan.v3.Message + 51, // 197: ttn.lorawan.v3.MACState.UplinkMessage.settings:type_name -> ttn.lorawan.v3.MACState.UplinkMessage.TxSettings + 52, // 198: ttn.lorawan.v3.MACState.UplinkMessage.rx_metadata:type_name -> ttn.lorawan.v3.MACState.UplinkMessage.RxMetadata + 64, // 199: ttn.lorawan.v3.MACState.UplinkMessage.received_at:type_name -> google.protobuf.Timestamp + 55, // 200: ttn.lorawan.v3.MACState.DownlinkMessage.payload:type_name -> ttn.lorawan.v3.MACState.DownlinkMessage.Message + 75, // 201: ttn.lorawan.v3.MACState.DataRateRange.min_data_rate_index:type_name -> ttn.lorawan.v3.DataRateIndex + 75, // 202: ttn.lorawan.v3.MACState.DataRateRange.max_data_rate_index:type_name -> ttn.lorawan.v3.DataRateIndex + 48, // 203: ttn.lorawan.v3.MACState.DataRateRanges.ranges:type_name -> ttn.lorawan.v3.MACState.DataRateRange + 49, // 204: ttn.lorawan.v3.MACState.RejectedDataRateRangesEntry.value:type_name -> ttn.lorawan.v3.MACState.DataRateRanges + 113, // 205: ttn.lorawan.v3.MACState.UplinkMessage.TxSettings.data_rate:type_name -> ttn.lorawan.v3.DataRate + 114, // 206: ttn.lorawan.v3.MACState.UplinkMessage.RxMetadata.gateway_ids:type_name -> ttn.lorawan.v3.GatewayIdentifiers + 115, // 207: ttn.lorawan.v3.MACState.UplinkMessage.RxMetadata.downlink_path_constraint:type_name -> ttn.lorawan.v3.DownlinkPathConstraint + 53, // 208: ttn.lorawan.v3.MACState.UplinkMessage.RxMetadata.packet_broker:type_name -> ttn.lorawan.v3.MACState.UplinkMessage.RxMetadata.PacketBrokerMetadata + 54, // 209: ttn.lorawan.v3.MACState.UplinkMessage.RxMetadata.relay:type_name -> ttn.lorawan.v3.MACState.UplinkMessage.RxMetadata.RelayMetadata + 56, // 210: ttn.lorawan.v3.MACState.DownlinkMessage.Message.m_hdr:type_name -> ttn.lorawan.v3.MACState.DownlinkMessage.Message.MHDR + 57, // 211: ttn.lorawan.v3.MACState.DownlinkMessage.Message.mac_payload:type_name -> ttn.lorawan.v3.MACState.DownlinkMessage.Message.MACPayload + 116, // 212: ttn.lorawan.v3.MACState.DownlinkMessage.Message.MHDR.m_type:type_name -> ttn.lorawan.v3.MType + 117, // 213: ttn.lorawan.v3.EndDevice.LocationsEntry.value:type_name -> ttn.lorawan.v3.Location + 102, // 214: ttn.lorawan.v3.BatchUpdateEndDeviceLastSeenRequest.EndDeviceLastSeenUpdate.ids:type_name -> ttn.lorawan.v3.EndDeviceIdentifiers + 64, // 215: ttn.lorawan.v3.BatchUpdateEndDeviceLastSeenRequest.EndDeviceLastSeenUpdate.last_seen_at:type_name -> google.protobuf.Timestamp + 64, // 216: ttn.lorawan.v3.ListEndDevicesRequest.Filter.updated_since:type_name -> google.protobuf.Timestamp + 30, // 217: ttn.lorawan.v3.EndDeviceTemplateFormats.FormatsEntry.value:type_name -> ttn.lorawan.v3.EndDeviceTemplateFormat + 218, // [218:218] is the sub-list for method output_type + 218, // [218:218] is the sub-list for method input_type + 218, // [218:218] is the sub-list for extension type_name + 218, // [218:218] is the sub-list for extension extendee + 0, // [0:218] is the sub-list for field type_name } func init() { file_ttn_lorawan_v3_end_device_proto_init() } @@ -7621,7 +7694,7 @@ func file_ttn_lorawan_v3_end_device_proto_init() { } } file_ttn_lorawan_v3_end_device_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*MACState); i { + switch v := v.(*MACSettingsProfile); i { case 0: return &v.state case 1: @@ -7633,7 +7706,7 @@ func file_ttn_lorawan_v3_end_device_proto_init() { } } file_ttn_lorawan_v3_end_device_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*EndDeviceAuthenticationCode); i { + switch v := v.(*MACState); i { case 0: return &v.state case 1: @@ -7645,7 +7718,7 @@ func file_ttn_lorawan_v3_end_device_proto_init() { } } file_ttn_lorawan_v3_end_device_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*EndDevice); i { + switch v := v.(*EndDeviceAuthenticationCode); i { case 0: return &v.state case 1: @@ -7657,7 +7730,7 @@ func file_ttn_lorawan_v3_end_device_proto_init() { } } file_ttn_lorawan_v3_end_device_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*EndDevices); i { + switch v := v.(*EndDevice); i { case 0: return &v.state case 1: @@ -7669,7 +7742,7 @@ func file_ttn_lorawan_v3_end_device_proto_init() { } } file_ttn_lorawan_v3_end_device_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DevAddrPrefix); i { + switch v := v.(*EndDevices); i { case 0: return &v.state case 1: @@ -7681,7 +7754,7 @@ func file_ttn_lorawan_v3_end_device_proto_init() { } } file_ttn_lorawan_v3_end_device_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CreateEndDeviceRequest); i { + switch v := v.(*DevAddrPrefix); i { case 0: return &v.state case 1: @@ -7693,7 +7766,7 @@ func file_ttn_lorawan_v3_end_device_proto_init() { } } file_ttn_lorawan_v3_end_device_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*UpdateEndDeviceRequest); i { + switch v := v.(*CreateEndDeviceRequest); i { case 0: return &v.state case 1: @@ -7705,7 +7778,7 @@ func file_ttn_lorawan_v3_end_device_proto_init() { } } file_ttn_lorawan_v3_end_device_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*BatchUpdateEndDeviceLastSeenRequest); i { + switch v := v.(*UpdateEndDeviceRequest); i { case 0: return &v.state case 1: @@ -7717,7 +7790,7 @@ func file_ttn_lorawan_v3_end_device_proto_init() { } } file_ttn_lorawan_v3_end_device_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetEndDeviceRequest); i { + switch v := v.(*BatchUpdateEndDeviceLastSeenRequest); i { case 0: return &v.state case 1: @@ -7729,7 +7802,7 @@ func file_ttn_lorawan_v3_end_device_proto_init() { } } file_ttn_lorawan_v3_end_device_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetEndDeviceIdentifiersForEUIsRequest); i { + switch v := v.(*GetEndDeviceRequest); i { case 0: return &v.state case 1: @@ -7741,7 +7814,7 @@ func file_ttn_lorawan_v3_end_device_proto_init() { } } file_ttn_lorawan_v3_end_device_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListEndDevicesRequest); i { + switch v := v.(*GetEndDeviceIdentifiersForEUIsRequest); i { case 0: return &v.state case 1: @@ -7753,7 +7826,7 @@ func file_ttn_lorawan_v3_end_device_proto_init() { } } file_ttn_lorawan_v3_end_device_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SetEndDeviceRequest); i { + switch v := v.(*ListEndDevicesRequest); i { case 0: return &v.state case 1: @@ -7765,7 +7838,7 @@ func file_ttn_lorawan_v3_end_device_proto_init() { } } file_ttn_lorawan_v3_end_device_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ResetAndGetEndDeviceRequest); i { + switch v := v.(*SetEndDeviceRequest); i { case 0: return &v.state case 1: @@ -7777,7 +7850,7 @@ func file_ttn_lorawan_v3_end_device_proto_init() { } } file_ttn_lorawan_v3_end_device_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*EndDeviceTemplate); i { + switch v := v.(*ResetAndGetEndDeviceRequest); i { case 0: return &v.state case 1: @@ -7789,7 +7862,7 @@ func file_ttn_lorawan_v3_end_device_proto_init() { } } file_ttn_lorawan_v3_end_device_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*EndDeviceTemplateFormat); i { + switch v := v.(*EndDeviceTemplate); i { case 0: return &v.state case 1: @@ -7801,7 +7874,7 @@ func file_ttn_lorawan_v3_end_device_proto_init() { } } file_ttn_lorawan_v3_end_device_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*EndDeviceTemplateFormats); i { + switch v := v.(*EndDeviceTemplateFormat); i { case 0: return &v.state case 1: @@ -7813,7 +7886,7 @@ func file_ttn_lorawan_v3_end_device_proto_init() { } } file_ttn_lorawan_v3_end_device_proto_msgTypes[30].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ConvertEndDeviceTemplateRequest); i { + switch v := v.(*EndDeviceTemplateFormats); i { case 0: return &v.state case 1: @@ -7825,7 +7898,7 @@ func file_ttn_lorawan_v3_end_device_proto_init() { } } file_ttn_lorawan_v3_end_device_proto_msgTypes[31].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*BatchDeleteEndDevicesRequest); i { + switch v := v.(*ConvertEndDeviceTemplateRequest); i { case 0: return &v.state case 1: @@ -7837,7 +7910,7 @@ func file_ttn_lorawan_v3_end_device_proto_init() { } } file_ttn_lorawan_v3_end_device_proto_msgTypes[32].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*BatchGetEndDevicesRequest); i { + switch v := v.(*BatchDeleteEndDevicesRequest); i { case 0: return &v.state case 1: @@ -7849,7 +7922,7 @@ func file_ttn_lorawan_v3_end_device_proto_init() { } } file_ttn_lorawan_v3_end_device_proto_msgTypes[33].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*MACParameters_Channel); i { + switch v := v.(*BatchGetEndDevicesRequest); i { case 0: return &v.state case 1: @@ -7861,7 +7934,7 @@ func file_ttn_lorawan_v3_end_device_proto_init() { } } file_ttn_lorawan_v3_end_device_proto_msgTypes[34].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ADRSettings_StaticMode); i { + switch v := v.(*MACParameters_Channel); i { case 0: return &v.state case 1: @@ -7873,7 +7946,7 @@ func file_ttn_lorawan_v3_end_device_proto_init() { } } file_ttn_lorawan_v3_end_device_proto_msgTypes[35].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ADRSettings_DynamicMode); i { + switch v := v.(*ADRSettings_StaticMode); i { case 0: return &v.state case 1: @@ -7885,7 +7958,7 @@ func file_ttn_lorawan_v3_end_device_proto_init() { } } file_ttn_lorawan_v3_end_device_proto_msgTypes[36].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ADRSettings_DisabledMode); i { + switch v := v.(*ADRSettings_DynamicMode); i { case 0: return &v.state case 1: @@ -7897,7 +7970,7 @@ func file_ttn_lorawan_v3_end_device_proto_init() { } } file_ttn_lorawan_v3_end_device_proto_msgTypes[37].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ADRSettings_DynamicMode_ChannelSteeringSettings); i { + switch v := v.(*ADRSettings_DisabledMode); i { case 0: return &v.state case 1: @@ -7909,7 +7982,7 @@ func file_ttn_lorawan_v3_end_device_proto_init() { } } file_ttn_lorawan_v3_end_device_proto_msgTypes[38].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ADRSettings_DynamicMode_PerDataRateIndexOverride); i { + switch v := v.(*ADRSettings_DynamicMode_ChannelSteeringSettings); i { case 0: return &v.state case 1: @@ -7921,7 +7994,7 @@ func file_ttn_lorawan_v3_end_device_proto_init() { } } file_ttn_lorawan_v3_end_device_proto_msgTypes[39].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ADRSettings_DynamicMode_Overrides); i { + switch v := v.(*ADRSettings_DynamicMode_PerDataRateIndexOverride); i { case 0: return &v.state case 1: @@ -7933,7 +8006,7 @@ func file_ttn_lorawan_v3_end_device_proto_init() { } } file_ttn_lorawan_v3_end_device_proto_msgTypes[40].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ADRSettings_DynamicMode_ChannelSteeringSettings_LoRaNarrowMode); i { + switch v := v.(*ADRSettings_DynamicMode_Overrides); i { case 0: return &v.state case 1: @@ -7945,7 +8018,7 @@ func file_ttn_lorawan_v3_end_device_proto_init() { } } file_ttn_lorawan_v3_end_device_proto_msgTypes[41].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ADRSettings_DynamicMode_ChannelSteeringSettings_DisabledMode); i { + switch v := v.(*ADRSettings_DynamicMode_ChannelSteeringSettings_LoRaNarrowMode); i { case 0: return &v.state case 1: @@ -7957,7 +8030,7 @@ func file_ttn_lorawan_v3_end_device_proto_init() { } } file_ttn_lorawan_v3_end_device_proto_msgTypes[42].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*MACState_JoinRequest); i { + switch v := v.(*ADRSettings_DynamicMode_ChannelSteeringSettings_DisabledMode); i { case 0: return &v.state case 1: @@ -7969,7 +8042,7 @@ func file_ttn_lorawan_v3_end_device_proto_init() { } } file_ttn_lorawan_v3_end_device_proto_msgTypes[43].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*MACState_JoinAccept); i { + switch v := v.(*MACState_JoinRequest); i { case 0: return &v.state case 1: @@ -7981,7 +8054,7 @@ func file_ttn_lorawan_v3_end_device_proto_init() { } } file_ttn_lorawan_v3_end_device_proto_msgTypes[44].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*MACState_UplinkMessage); i { + switch v := v.(*MACState_JoinAccept); i { case 0: return &v.state case 1: @@ -7993,7 +8066,7 @@ func file_ttn_lorawan_v3_end_device_proto_init() { } } file_ttn_lorawan_v3_end_device_proto_msgTypes[45].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*MACState_DownlinkMessage); i { + switch v := v.(*MACState_UplinkMessage); i { case 0: return &v.state case 1: @@ -8005,7 +8078,7 @@ func file_ttn_lorawan_v3_end_device_proto_init() { } } file_ttn_lorawan_v3_end_device_proto_msgTypes[46].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*MACState_DataRateRange); i { + switch v := v.(*MACState_DownlinkMessage); i { case 0: return &v.state case 1: @@ -8017,6 +8090,18 @@ func file_ttn_lorawan_v3_end_device_proto_init() { } } file_ttn_lorawan_v3_end_device_proto_msgTypes[47].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*MACState_DataRateRange); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_ttn_lorawan_v3_end_device_proto_msgTypes[48].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*MACState_DataRateRanges); i { case 0: return &v.state @@ -8028,7 +8113,7 @@ func file_ttn_lorawan_v3_end_device_proto_init() { return nil } } - file_ttn_lorawan_v3_end_device_proto_msgTypes[49].Exporter = func(v interface{}, i int) interface{} { + file_ttn_lorawan_v3_end_device_proto_msgTypes[50].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*MACState_UplinkMessage_TxSettings); i { case 0: return &v.state @@ -8040,7 +8125,7 @@ func file_ttn_lorawan_v3_end_device_proto_init() { return nil } } - file_ttn_lorawan_v3_end_device_proto_msgTypes[50].Exporter = func(v interface{}, i int) interface{} { + file_ttn_lorawan_v3_end_device_proto_msgTypes[51].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*MACState_UplinkMessage_RxMetadata); i { case 0: return &v.state @@ -8052,7 +8137,7 @@ func file_ttn_lorawan_v3_end_device_proto_init() { return nil } } - file_ttn_lorawan_v3_end_device_proto_msgTypes[51].Exporter = func(v interface{}, i int) interface{} { + file_ttn_lorawan_v3_end_device_proto_msgTypes[52].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*MACState_UplinkMessage_RxMetadata_PacketBrokerMetadata); i { case 0: return &v.state @@ -8064,7 +8149,7 @@ func file_ttn_lorawan_v3_end_device_proto_init() { return nil } } - file_ttn_lorawan_v3_end_device_proto_msgTypes[52].Exporter = func(v interface{}, i int) interface{} { + file_ttn_lorawan_v3_end_device_proto_msgTypes[53].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*MACState_UplinkMessage_RxMetadata_RelayMetadata); i { case 0: return &v.state @@ -8076,7 +8161,7 @@ func file_ttn_lorawan_v3_end_device_proto_init() { return nil } } - file_ttn_lorawan_v3_end_device_proto_msgTypes[53].Exporter = func(v interface{}, i int) interface{} { + file_ttn_lorawan_v3_end_device_proto_msgTypes[54].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*MACState_DownlinkMessage_Message); i { case 0: return &v.state @@ -8088,7 +8173,7 @@ func file_ttn_lorawan_v3_end_device_proto_init() { return nil } } - file_ttn_lorawan_v3_end_device_proto_msgTypes[54].Exporter = func(v interface{}, i int) interface{} { + file_ttn_lorawan_v3_end_device_proto_msgTypes[55].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*MACState_DownlinkMessage_Message_MHDR); i { case 0: return &v.state @@ -8100,7 +8185,7 @@ func file_ttn_lorawan_v3_end_device_proto_init() { return nil } } - file_ttn_lorawan_v3_end_device_proto_msgTypes[55].Exporter = func(v interface{}, i int) interface{} { + file_ttn_lorawan_v3_end_device_proto_msgTypes[56].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*MACState_DownlinkMessage_Message_MACPayload); i { case 0: return &v.state @@ -8112,7 +8197,7 @@ func file_ttn_lorawan_v3_end_device_proto_init() { return nil } } - file_ttn_lorawan_v3_end_device_proto_msgTypes[58].Exporter = func(v interface{}, i int) interface{} { + file_ttn_lorawan_v3_end_device_proto_msgTypes[59].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*BatchUpdateEndDeviceLastSeenRequest_EndDeviceLastSeenUpdate); i { case 0: return &v.state @@ -8124,7 +8209,7 @@ func file_ttn_lorawan_v3_end_device_proto_init() { return nil } } - file_ttn_lorawan_v3_end_device_proto_msgTypes[59].Exporter = func(v interface{}, i int) interface{} { + file_ttn_lorawan_v3_end_device_proto_msgTypes[60].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ListEndDevicesRequest_Filter); i { case 0: return &v.state @@ -8160,11 +8245,11 @@ func file_ttn_lorawan_v3_end_device_proto_init() { (*ADRSettings_Dynamic)(nil), (*ADRSettings_Disabled)(nil), } - file_ttn_lorawan_v3_end_device_proto_msgTypes[37].OneofWrappers = []interface{}{ + file_ttn_lorawan_v3_end_device_proto_msgTypes[38].OneofWrappers = []interface{}{ (*ADRSettings_DynamicMode_ChannelSteeringSettings_LoraNarrow)(nil), (*ADRSettings_DynamicMode_ChannelSteeringSettings_Disabled)(nil), } - file_ttn_lorawan_v3_end_device_proto_msgTypes[59].OneofWrappers = []interface{}{ + file_ttn_lorawan_v3_end_device_proto_msgTypes[60].OneofWrappers = []interface{}{ (*ListEndDevicesRequest_Filter_UpdatedSince)(nil), } type x struct{} @@ -8173,7 +8258,7 @@ func file_ttn_lorawan_v3_end_device_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_ttn_lorawan_v3_end_device_proto_rawDesc, NumEnums: 1, - NumMessages: 61, + NumMessages: 62, NumExtensions: 0, NumServices: 0, }, diff --git a/pkg/ttnpb/end_device.pb.paths.fm.go b/pkg/ttnpb/end_device.pb.paths.fm.go index 27cf9f46e4..089bfe8399 100644 --- a/pkg/ttnpb/end_device.pb.paths.fm.go +++ b/pkg/ttnpb/end_device.pb.paths.fm.go @@ -918,6 +918,219 @@ var MACSettingsFieldPathsTopLevel = []string{ "uplink_dwell_time", "use_adr", } +var MACSettingsProfileFieldPathsNested = []string{ + "ids", + "ids.application_ids", + "ids.application_ids.application_id", + "ids.profile_id", + "mac_settings", + "mac_settings.adr", + "mac_settings.adr.mode", + "mac_settings.adr.mode.disabled", + "mac_settings.adr.mode.dynamic", + "mac_settings.adr.mode.dynamic.channel_steering", + "mac_settings.adr.mode.dynamic.channel_steering.mode", + "mac_settings.adr.mode.dynamic.channel_steering.mode.disabled", + "mac_settings.adr.mode.dynamic.channel_steering.mode.lora_narrow", + "mac_settings.adr.mode.dynamic.margin", + "mac_settings.adr.mode.dynamic.max_data_rate_index", + "mac_settings.adr.mode.dynamic.max_data_rate_index.value", + "mac_settings.adr.mode.dynamic.max_nb_trans", + "mac_settings.adr.mode.dynamic.max_tx_power_index", + "mac_settings.adr.mode.dynamic.min_data_rate_index", + "mac_settings.adr.mode.dynamic.min_data_rate_index.value", + "mac_settings.adr.mode.dynamic.min_nb_trans", + "mac_settings.adr.mode.dynamic.min_tx_power_index", + "mac_settings.adr.mode.dynamic.overrides", + "mac_settings.adr.mode.dynamic.overrides.data_rate_0", + "mac_settings.adr.mode.dynamic.overrides.data_rate_0.max_nb_trans", + "mac_settings.adr.mode.dynamic.overrides.data_rate_0.min_nb_trans", + "mac_settings.adr.mode.dynamic.overrides.data_rate_1", + "mac_settings.adr.mode.dynamic.overrides.data_rate_1.max_nb_trans", + "mac_settings.adr.mode.dynamic.overrides.data_rate_1.min_nb_trans", + "mac_settings.adr.mode.dynamic.overrides.data_rate_10", + "mac_settings.adr.mode.dynamic.overrides.data_rate_10.max_nb_trans", + "mac_settings.adr.mode.dynamic.overrides.data_rate_10.min_nb_trans", + "mac_settings.adr.mode.dynamic.overrides.data_rate_11", + "mac_settings.adr.mode.dynamic.overrides.data_rate_11.max_nb_trans", + "mac_settings.adr.mode.dynamic.overrides.data_rate_11.min_nb_trans", + "mac_settings.adr.mode.dynamic.overrides.data_rate_12", + "mac_settings.adr.mode.dynamic.overrides.data_rate_12.max_nb_trans", + "mac_settings.adr.mode.dynamic.overrides.data_rate_12.min_nb_trans", + "mac_settings.adr.mode.dynamic.overrides.data_rate_13", + "mac_settings.adr.mode.dynamic.overrides.data_rate_13.max_nb_trans", + "mac_settings.adr.mode.dynamic.overrides.data_rate_13.min_nb_trans", + "mac_settings.adr.mode.dynamic.overrides.data_rate_14", + "mac_settings.adr.mode.dynamic.overrides.data_rate_14.max_nb_trans", + "mac_settings.adr.mode.dynamic.overrides.data_rate_14.min_nb_trans", + "mac_settings.adr.mode.dynamic.overrides.data_rate_15", + "mac_settings.adr.mode.dynamic.overrides.data_rate_15.max_nb_trans", + "mac_settings.adr.mode.dynamic.overrides.data_rate_15.min_nb_trans", + "mac_settings.adr.mode.dynamic.overrides.data_rate_2", + "mac_settings.adr.mode.dynamic.overrides.data_rate_2.max_nb_trans", + "mac_settings.adr.mode.dynamic.overrides.data_rate_2.min_nb_trans", + "mac_settings.adr.mode.dynamic.overrides.data_rate_3", + "mac_settings.adr.mode.dynamic.overrides.data_rate_3.max_nb_trans", + "mac_settings.adr.mode.dynamic.overrides.data_rate_3.min_nb_trans", + "mac_settings.adr.mode.dynamic.overrides.data_rate_4", + "mac_settings.adr.mode.dynamic.overrides.data_rate_4.max_nb_trans", + "mac_settings.adr.mode.dynamic.overrides.data_rate_4.min_nb_trans", + "mac_settings.adr.mode.dynamic.overrides.data_rate_5", + "mac_settings.adr.mode.dynamic.overrides.data_rate_5.max_nb_trans", + "mac_settings.adr.mode.dynamic.overrides.data_rate_5.min_nb_trans", + "mac_settings.adr.mode.dynamic.overrides.data_rate_6", + "mac_settings.adr.mode.dynamic.overrides.data_rate_6.max_nb_trans", + "mac_settings.adr.mode.dynamic.overrides.data_rate_6.min_nb_trans", + "mac_settings.adr.mode.dynamic.overrides.data_rate_7", + "mac_settings.adr.mode.dynamic.overrides.data_rate_7.max_nb_trans", + "mac_settings.adr.mode.dynamic.overrides.data_rate_7.min_nb_trans", + "mac_settings.adr.mode.dynamic.overrides.data_rate_8", + "mac_settings.adr.mode.dynamic.overrides.data_rate_8.max_nb_trans", + "mac_settings.adr.mode.dynamic.overrides.data_rate_8.min_nb_trans", + "mac_settings.adr.mode.dynamic.overrides.data_rate_9", + "mac_settings.adr.mode.dynamic.overrides.data_rate_9.max_nb_trans", + "mac_settings.adr.mode.dynamic.overrides.data_rate_9.min_nb_trans", + "mac_settings.adr.mode.static", + "mac_settings.adr.mode.static.data_rate_index", + "mac_settings.adr.mode.static.nb_trans", + "mac_settings.adr.mode.static.tx_power_index", + "mac_settings.adr_margin", + "mac_settings.beacon_frequency", + "mac_settings.beacon_frequency.value", + "mac_settings.class_b_c_downlink_interval", + "mac_settings.class_b_timeout", + "mac_settings.class_c_timeout", + "mac_settings.desired_adr_ack_delay_exponent", + "mac_settings.desired_adr_ack_delay_exponent.value", + "mac_settings.desired_adr_ack_limit_exponent", + "mac_settings.desired_adr_ack_limit_exponent.value", + "mac_settings.desired_beacon_frequency", + "mac_settings.desired_beacon_frequency.value", + "mac_settings.desired_max_duty_cycle", + "mac_settings.desired_max_duty_cycle.value", + "mac_settings.desired_max_eirp", + "mac_settings.desired_max_eirp.value", + "mac_settings.desired_ping_slot_data_rate_index", + "mac_settings.desired_ping_slot_data_rate_index.value", + "mac_settings.desired_ping_slot_frequency", + "mac_settings.desired_ping_slot_frequency.value", + "mac_settings.desired_relay", + "mac_settings.desired_relay.mode", + "mac_settings.desired_relay.mode.served", + "mac_settings.desired_relay.mode.served.backoff", + "mac_settings.desired_relay.mode.served.mode", + "mac_settings.desired_relay.mode.served.mode.always", + "mac_settings.desired_relay.mode.served.mode.dynamic", + "mac_settings.desired_relay.mode.served.mode.dynamic.smart_enable_level", + "mac_settings.desired_relay.mode.served.mode.end_device_controlled", + "mac_settings.desired_relay.mode.served.second_channel", + "mac_settings.desired_relay.mode.served.second_channel.ack_offset", + "mac_settings.desired_relay.mode.served.second_channel.data_rate_index", + "mac_settings.desired_relay.mode.served.second_channel.frequency", + "mac_settings.desired_relay.mode.served.serving_device_id", + "mac_settings.desired_relay.mode.serving", + "mac_settings.desired_relay.mode.serving.cad_periodicity", + "mac_settings.desired_relay.mode.serving.default_channel_index", + "mac_settings.desired_relay.mode.serving.limits", + "mac_settings.desired_relay.mode.serving.limits.join_requests", + "mac_settings.desired_relay.mode.serving.limits.join_requests.bucket_size", + "mac_settings.desired_relay.mode.serving.limits.join_requests.reload_rate", + "mac_settings.desired_relay.mode.serving.limits.notifications", + "mac_settings.desired_relay.mode.serving.limits.notifications.bucket_size", + "mac_settings.desired_relay.mode.serving.limits.notifications.reload_rate", + "mac_settings.desired_relay.mode.serving.limits.overall", + "mac_settings.desired_relay.mode.serving.limits.overall.bucket_size", + "mac_settings.desired_relay.mode.serving.limits.overall.reload_rate", + "mac_settings.desired_relay.mode.serving.limits.reset_behavior", + "mac_settings.desired_relay.mode.serving.limits.uplink_messages", + "mac_settings.desired_relay.mode.serving.limits.uplink_messages.bucket_size", + "mac_settings.desired_relay.mode.serving.limits.uplink_messages.reload_rate", + "mac_settings.desired_relay.mode.serving.second_channel", + "mac_settings.desired_relay.mode.serving.second_channel.ack_offset", + "mac_settings.desired_relay.mode.serving.second_channel.data_rate_index", + "mac_settings.desired_relay.mode.serving.second_channel.frequency", + "mac_settings.desired_relay.mode.serving.uplink_forwarding_rules", + "mac_settings.desired_rx1_data_rate_offset", + "mac_settings.desired_rx1_data_rate_offset.value", + "mac_settings.desired_rx1_delay", + "mac_settings.desired_rx1_delay.value", + "mac_settings.desired_rx2_data_rate_index", + "mac_settings.desired_rx2_data_rate_index.value", + "mac_settings.desired_rx2_frequency", + "mac_settings.desired_rx2_frequency.value", + "mac_settings.downlink_dwell_time", + "mac_settings.downlink_dwell_time.value", + "mac_settings.factory_preset_frequencies", + "mac_settings.max_duty_cycle", + "mac_settings.max_duty_cycle.value", + "mac_settings.ping_slot_data_rate_index", + "mac_settings.ping_slot_data_rate_index.value", + "mac_settings.ping_slot_frequency", + "mac_settings.ping_slot_frequency.value", + "mac_settings.ping_slot_periodicity", + "mac_settings.ping_slot_periodicity.value", + "mac_settings.relay", + "mac_settings.relay.mode", + "mac_settings.relay.mode.served", + "mac_settings.relay.mode.served.backoff", + "mac_settings.relay.mode.served.mode", + "mac_settings.relay.mode.served.mode.always", + "mac_settings.relay.mode.served.mode.dynamic", + "mac_settings.relay.mode.served.mode.dynamic.smart_enable_level", + "mac_settings.relay.mode.served.mode.end_device_controlled", + "mac_settings.relay.mode.served.second_channel", + "mac_settings.relay.mode.served.second_channel.ack_offset", + "mac_settings.relay.mode.served.second_channel.data_rate_index", + "mac_settings.relay.mode.served.second_channel.frequency", + "mac_settings.relay.mode.served.serving_device_id", + "mac_settings.relay.mode.serving", + "mac_settings.relay.mode.serving.cad_periodicity", + "mac_settings.relay.mode.serving.default_channel_index", + "mac_settings.relay.mode.serving.limits", + "mac_settings.relay.mode.serving.limits.join_requests", + "mac_settings.relay.mode.serving.limits.join_requests.bucket_size", + "mac_settings.relay.mode.serving.limits.join_requests.reload_rate", + "mac_settings.relay.mode.serving.limits.notifications", + "mac_settings.relay.mode.serving.limits.notifications.bucket_size", + "mac_settings.relay.mode.serving.limits.notifications.reload_rate", + "mac_settings.relay.mode.serving.limits.overall", + "mac_settings.relay.mode.serving.limits.overall.bucket_size", + "mac_settings.relay.mode.serving.limits.overall.reload_rate", + "mac_settings.relay.mode.serving.limits.reset_behavior", + "mac_settings.relay.mode.serving.limits.uplink_messages", + "mac_settings.relay.mode.serving.limits.uplink_messages.bucket_size", + "mac_settings.relay.mode.serving.limits.uplink_messages.reload_rate", + "mac_settings.relay.mode.serving.second_channel", + "mac_settings.relay.mode.serving.second_channel.ack_offset", + "mac_settings.relay.mode.serving.second_channel.data_rate_index", + "mac_settings.relay.mode.serving.second_channel.frequency", + "mac_settings.relay.mode.serving.uplink_forwarding_rules", + "mac_settings.resets_f_cnt", + "mac_settings.resets_f_cnt.value", + "mac_settings.rx1_data_rate_offset", + "mac_settings.rx1_data_rate_offset.value", + "mac_settings.rx1_delay", + "mac_settings.rx1_delay.value", + "mac_settings.rx2_data_rate_index", + "mac_settings.rx2_data_rate_index.value", + "mac_settings.rx2_frequency", + "mac_settings.rx2_frequency.value", + "mac_settings.schedule_downlinks", + "mac_settings.schedule_downlinks.value", + "mac_settings.status_count_periodicity", + "mac_settings.status_time_periodicity", + "mac_settings.supports_32_bit_f_cnt", + "mac_settings.supports_32_bit_f_cnt.value", + "mac_settings.uplink_dwell_time", + "mac_settings.uplink_dwell_time.value", + "mac_settings.use_adr", + "mac_settings.use_adr.value", +} + +var MACSettingsProfileFieldPathsTopLevel = []string{ + "ids", + "mac_settings", +} var MACStateFieldPathsNested = []string{ "current_parameters", "current_parameters.adr_ack_delay", diff --git a/pkg/ttnpb/end_device.pb.setters.fm.go b/pkg/ttnpb/end_device.pb.setters.fm.go index 29b4bb7911..fa1c564259 100644 --- a/pkg/ttnpb/end_device.pb.setters.fm.go +++ b/pkg/ttnpb/end_device.pb.setters.fm.go @@ -2511,6 +2511,67 @@ func (dst *MACSettings) SetFields(src *MACSettings, paths ...string) error { return nil } +func (dst *MACSettingsProfile) SetFields(src *MACSettingsProfile, paths ...string) error { + for name, subs := range _processPaths(paths) { + switch name { + case "ids": + if len(subs) > 0 { + var newDst, newSrc *MACSettingsProfileIdentifiers + if (src == nil || src.Ids == nil) && dst.Ids == nil { + continue + } + if src != nil { + newSrc = src.Ids + } + if dst.Ids != nil { + newDst = dst.Ids + } else { + newDst = &MACSettingsProfileIdentifiers{} + dst.Ids = newDst + } + if err := newDst.SetFields(newSrc, subs...); err != nil { + return err + } + } else { + if src != nil { + dst.Ids = src.Ids + } else { + dst.Ids = nil + } + } + case "mac_settings": + if len(subs) > 0 { + var newDst, newSrc *MACSettings + if (src == nil || src.MacSettings == nil) && dst.MacSettings == nil { + continue + } + if src != nil { + newSrc = src.MacSettings + } + if dst.MacSettings != nil { + newDst = dst.MacSettings + } else { + newDst = &MACSettings{} + dst.MacSettings = newDst + } + if err := newDst.SetFields(newSrc, subs...); err != nil { + return err + } + } else { + if src != nil { + dst.MacSettings = src.MacSettings + } else { + dst.MacSettings = nil + } + } + + default: + return fmt.Errorf("invalid field: '%s'", name) + } + } + return nil +} + func (dst *MACState) SetFields(src *MACState, paths ...string) error { for name, subs := range _processPaths(paths) { switch name { diff --git a/pkg/ttnpb/end_device.pb.validate.go b/pkg/ttnpb/end_device.pb.validate.go index 9b1035f954..235b9e8160 100644 --- a/pkg/ttnpb/end_device.pb.validate.go +++ b/pkg/ttnpb/end_device.pb.validate.go @@ -2555,6 +2555,125 @@ var _ interface { ErrorName() string } = MACSettingsValidationError{} +// ValidateFields checks the field values on MACSettingsProfile with the rules +// defined in the proto definition for this message. If any rules are +// violated, an error is returned. +func (m *MACSettingsProfile) ValidateFields(paths ...string) error { + if m == nil { + return nil + } + + if len(paths) == 0 { + paths = MACSettingsProfileFieldPathsNested + } + + for name, subs := range _processPaths(append(paths[:0:0], paths...)) { + _ = subs + switch name { + case "ids": + + if m.GetIds() == nil { + return MACSettingsProfileValidationError{ + field: "ids", + reason: "value is required", + } + } + + if v, ok := interface{}(m.GetIds()).(interface{ ValidateFields(...string) error }); ok { + if err := v.ValidateFields(subs...); err != nil { + return MACSettingsProfileValidationError{ + field: "ids", + reason: "embedded message failed validation", + cause: err, + } + } + } + + case "mac_settings": + + if m.GetMacSettings() == nil { + return MACSettingsProfileValidationError{ + field: "mac_settings", + reason: "value is required", + } + } + + if v, ok := interface{}(m.GetMacSettings()).(interface{ ValidateFields(...string) error }); ok { + if err := v.ValidateFields(subs...); err != nil { + return MACSettingsProfileValidationError{ + field: "mac_settings", + reason: "embedded message failed validation", + cause: err, + } + } + } + + default: + return MACSettingsProfileValidationError{ + field: name, + reason: "invalid field path", + } + } + } + return nil +} + +// MACSettingsProfileValidationError is the validation error returned by +// MACSettingsProfile.ValidateFields if the designated constraints aren't met. +type MACSettingsProfileValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e MACSettingsProfileValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e MACSettingsProfileValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e MACSettingsProfileValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e MACSettingsProfileValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e MACSettingsProfileValidationError) ErrorName() string { + return "MACSettingsProfileValidationError" +} + +// Error satisfies the builtin error interface +func (e MACSettingsProfileValidationError) Error() string { + cause := "" + if e.cause != nil { + cause = fmt.Sprintf(" | caused by: %v", e.cause) + } + + key := "" + if e.key { + key = "key for " + } + + return fmt.Sprintf( + "invalid %sMACSettingsProfile.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = MACSettingsProfileValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = MACSettingsProfileValidationError{} + // ValidateFields checks the field values on MACState 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/end_device_flags.pb.go b/pkg/ttnpb/end_device_flags.pb.go index 49b02f3f19..d1e97d3b00 100644 --- a/pkg/ttnpb/end_device_flags.pb.go +++ b/pkg/ttnpb/end_device_flags.pb.go @@ -3239,6 +3239,58 @@ func (m *MACSettings) SetFromFlags(flags *pflag.FlagSet, prefix string) (paths [ return paths, nil } +// AddSelectFlagsForMACSettingsProfile adds flags to select fields in MACSettingsProfile. +func AddSelectFlagsForMACSettingsProfile(flags *pflag.FlagSet, prefix string, hidden bool) { + flags.AddFlag(flagsplugin.NewBoolFlag(flagsplugin.Prefix("mac-settings", prefix), flagsplugin.SelectDesc(flagsplugin.Prefix("mac-settings", prefix), true), flagsplugin.WithHidden(hidden))) + AddSelectFlagsForMACSettings(flags, flagsplugin.Prefix("mac-settings", prefix), hidden) +} + +// SelectFromFlags outputs the fieldmask paths forMACSettingsProfile message from select flags. +func PathsFromSelectFlagsForMACSettingsProfile(flags *pflag.FlagSet, prefix string) (paths []string, err error) { + if val, selected, err := flagsplugin.GetBool(flags, flagsplugin.Prefix("mac_settings", prefix)); err != nil { + return nil, err + } else if selected && val { + paths = append(paths, flagsplugin.Prefix("mac_settings", prefix)) + } + if selectPaths, err := PathsFromSelectFlagsForMACSettings(flags, flagsplugin.Prefix("mac_settings", prefix)); err != nil { + return nil, err + } else { + paths = append(paths, selectPaths...) + } + return paths, nil +} + +// AddSetFlagsForMACSettingsProfile adds flags to select fields in MACSettingsProfile. +func AddSetFlagsForMACSettingsProfile(flags *pflag.FlagSet, prefix string, hidden bool) { + AddSetFlagsForMACSettingsProfileIdentifiers(flags, flagsplugin.Prefix("ids", prefix), true) + AddSetFlagsForMACSettings(flags, flagsplugin.Prefix("mac-settings", prefix), hidden) +} + +// SetFromFlags sets the MACSettingsProfile message from flags. +func (m *MACSettingsProfile) SetFromFlags(flags *pflag.FlagSet, prefix string) (paths []string, err error) { + if changed := flagsplugin.IsAnyPrefixSet(flags, flagsplugin.Prefix("ids", prefix)); changed { + if m.Ids == nil { + m.Ids = &MACSettingsProfileIdentifiers{} + } + if setPaths, err := m.Ids.SetFromFlags(flags, flagsplugin.Prefix("ids", prefix)); err != nil { + return nil, err + } else { + paths = append(paths, setPaths...) + } + } + if changed := flagsplugin.IsAnyPrefixSet(flags, flagsplugin.Prefix("mac_settings", prefix)); changed { + if m.MacSettings == nil { + m.MacSettings = &MACSettings{} + } + if setPaths, err := m.MacSettings.SetFromFlags(flags, flagsplugin.Prefix("mac_settings", prefix)); err != nil { + return nil, err + } else { + paths = append(paths, setPaths...) + } + } + return paths, nil +} + // AddSelectFlagsForMACState_JoinRequest adds flags to select fields in MACState_JoinRequest. func AddSelectFlagsForMACState_JoinRequest(flags *pflag.FlagSet, prefix string, hidden bool) { flags.AddFlag(flagsplugin.NewBoolFlag(flagsplugin.Prefix("downlink-settings", prefix), flagsplugin.SelectDesc(flagsplugin.Prefix("downlink-settings", prefix), true), flagsplugin.WithHidden(hidden))) diff --git a/pkg/ttnpb/end_device_json.pb.go b/pkg/ttnpb/end_device_json.pb.go index d53cae9986..ea6ed100b3 100644 --- a/pkg/ttnpb/end_device_json.pb.go +++ b/pkg/ttnpb/end_device_json.pb.go @@ -2339,6 +2339,68 @@ func (x *MACSettings) UnmarshalJSON(b []byte) error { return jsonplugin.DefaultUnmarshalerConfig.Unmarshal(b, x) } +// MarshalProtoJSON marshals the MACSettingsProfile message to JSON. +func (x *MACSettingsProfile) MarshalProtoJSON(s *jsonplugin.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.Ids != nil || s.HasField("ids") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("ids") + // NOTE: MACSettingsProfileIdentifiers does not seem to implement MarshalProtoJSON. + golang.MarshalMessage(s, x.Ids) + } + if x.MacSettings != nil || s.HasField("mac_settings") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("mac_settings") + x.MacSettings.MarshalProtoJSON(s.WithField("mac_settings")) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the MACSettingsProfile to JSON. +func (x *MACSettingsProfile) MarshalJSON() ([]byte, error) { + return jsonplugin.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the MACSettingsProfile message from JSON. +func (x *MACSettingsProfile) UnmarshalProtoJSON(s *jsonplugin.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.ReadAny() // ignore unknown field + case "ids": + s.AddField("ids") + if s.ReadNil() { + x.Ids = nil + return + } + // NOTE: MACSettingsProfileIdentifiers does not seem to implement UnmarshalProtoJSON. + var v MACSettingsProfileIdentifiers + golang.UnmarshalMessage(s, &v) + x.Ids = &v + case "mac_settings", "macSettings": + if s.ReadNil() { + x.MacSettings = nil + return + } + x.MacSettings = &MACSettings{} + x.MacSettings.UnmarshalProtoJSON(s.WithField("mac_settings", true)) + } + }) +} + +// UnmarshalJSON unmarshals the MACSettingsProfile from JSON. +func (x *MACSettingsProfile) UnmarshalJSON(b []byte) error { + return jsonplugin.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + // MarshalProtoJSON marshals the MACState_JoinRequest message to JSON. func (x *MACState_JoinRequest) MarshalProtoJSON(s *jsonplugin.MarshalState) { if x == nil { diff --git a/pkg/ttnpb/field_mask_validation.go b/pkg/ttnpb/field_mask_validation.go index f453e4feb5..d07e53d09a 100644 --- a/pkg/ttnpb/field_mask_validation.go +++ b/pkg/ttnpb/field_mask_validation.go @@ -1599,6 +1599,21 @@ var RPCFieldMaskPaths = map[string]RPCFieldMaskPathValue{ Allowed: nsEndDeviceReadFieldPaths[:], }, + // MAC settings profiles: + "/ttn.lorawan.v3.NsMACSettingsProfileRegistry/Get": { + All: MACSettingsProfileFieldPathsNested, + Allowed: MACSettingsProfileFieldPathsNested, + }, + "/ttn.lorawan.v3.NsMACSettingsProfileRegistry/List": { + All: MACSettingsProfileFieldPathsNested, + Allowed: MACSettingsProfileFieldPathsNested, + }, + "/ttn.lorawan.v3.NsMACSettingsProfileRegistry/Update": { + All: MACSettingsProfileFieldPathsNested, + Allowed: MACSettingsProfileFieldPathsNested, + Set: true, + }, + // Relays: "/ttn.lorawan.v3.NsRelayConfigurationService/GetRelay": { All: RelaySettingsFieldPathsNested, diff --git a/pkg/ttnpb/i18n.go b/pkg/ttnpb/i18n.go index 6c2b8d0296..63761a8156 100644 --- a/pkg/ttnpb/i18n.go +++ b/pkg/ttnpb/i18n.go @@ -113,8 +113,11 @@ func init() { defineEnum(Right_RIGHT_USER_INFO, "view user information") defineEnum(Right_RIGHT_USER_SETTINGS_BASIC, "edit basic user settings") + defineEnum(Right_RIGHT_USER_LIST, "list user accounts") + defineEnum(Right_RIGHT_USER_CREATE, "create user account") defineEnum(Right_RIGHT_USER_SETTINGS_API_KEYS, "view and edit user API keys") defineEnum(Right_RIGHT_USER_DELETE, "delete user account") + defineEnum(Right_RIGHT_USER_PURGE, "purge user account") defineEnum(Right_RIGHT_USER_AUTHORIZED_CLIENTS, "view and edit authorized OAuth clients of the user") defineEnum(Right_RIGHT_USER_APPLICATIONS_LIST, "list applications the user is a collaborator of") defineEnum(Right_RIGHT_USER_APPLICATIONS_CREATE, "create an application under the user account") @@ -133,6 +136,7 @@ func init() { defineEnum(Right_RIGHT_APPLICATION_SETTINGS_COLLABORATORS, "view and edit application collaborators") defineEnum(Right_RIGHT_APPLICATION_SETTINGS_PACKAGES, "view and edit application packages and associations") defineEnum(Right_RIGHT_APPLICATION_DELETE, "delete application") + defineEnum(Right_RIGHT_APPLICATION_PURGE, "purge application") defineEnum(Right_RIGHT_APPLICATION_DEVICES_READ, "view devices in application") defineEnum(Right_RIGHT_APPLICATION_DEVICES_WRITE, "create devices in application") defineEnum(Right_RIGHT_APPLICATION_DEVICES_READ_KEYS, "view device keys in application") @@ -148,12 +152,14 @@ func init() { defineEnum(Right_RIGHT_CLIENT_SETTINGS_BASIC, "edit OAuth client basic settings") defineEnum(Right_RIGHT_CLIENT_SETTINGS_COLLABORATORS, "view and edit OAuth client collaborators") defineEnum(Right_RIGHT_CLIENT_DELETE, "delete OAuth client") + defineEnum(Right_RIGHT_CLIENT_PURGE, "purge OAuth client") defineEnum(Right_RIGHT_GATEWAY_INFO, "view gateway information") defineEnum(Right_RIGHT_GATEWAY_SETTINGS_BASIC, "edit basic gateway settings") defineEnum(Right_RIGHT_GATEWAY_SETTINGS_API_KEYS, "view and edit gateway API keys") defineEnum(Right_RIGHT_GATEWAY_SETTINGS_COLLABORATORS, "view and edit gateway collaborators") defineEnum(Right_RIGHT_GATEWAY_DELETE, "delete gateway") + defineEnum(Right_RIGHT_GATEWAY_PURGE, "purge gateway") defineEnum(Right_RIGHT_GATEWAY_TRAFFIC_READ, "read gateway traffic") defineEnum(Right_RIGHT_GATEWAY_TRAFFIC_DOWN_WRITE, "write downlink gateway traffic") defineEnum(Right_RIGHT_GATEWAY_LINK, "link as Gateway to a Gateway Server for traffic exchange, i.e. write uplink and read downlink") @@ -168,6 +174,7 @@ func init() { defineEnum(Right_RIGHT_ORGANIZATION_SETTINGS_API_KEYS, "view and edit organization API keys") defineEnum(Right_RIGHT_ORGANIZATION_SETTINGS_MEMBERS, "view and edit organization members") defineEnum(Right_RIGHT_ORGANIZATION_DELETE, "delete organization") + defineEnum(Right_RIGHT_ORGANIZATION_PURGE, "purge organization") defineEnum(Right_RIGHT_ORGANIZATION_APPLICATIONS_LIST, "list the applications the organization is a collaborator of") defineEnum(Right_RIGHT_ORGANIZATION_APPLICATIONS_CREATE, "create an application under the organization") defineEnum(Right_RIGHT_ORGANIZATION_GATEWAYS_LIST, "list the gateways the organization is a collaborator of") diff --git a/pkg/ttnpb/identifiers.go b/pkg/ttnpb/identifiers.go index 2dfe772935..0ccf66743c 100644 --- a/pkg/ttnpb/identifiers.go +++ b/pkg/ttnpb/identifiers.go @@ -99,6 +99,14 @@ func (ids *UserIdentifiers) IsZero() bool { return ids.GetUserId() == "" && ids.GetEmail() == "" } +// IsZero returns true if all identifiers have zero-values. +func (ids *MACSettingsProfileIdentifiers) IsZero() bool { + if ids == nil { + return true + } + return ids.ProfileId == "" && ids.ApplicationIds == nil +} + // GetOrganizationOrUserIdentifiers returns the OrganizationIdentifiers as *OrganizationOrUserIdentifiers. func (ids *OrganizationIdentifiers) GetOrganizationOrUserIdentifiers() *OrganizationOrUserIdentifiers { if ids == nil { @@ -152,3 +160,11 @@ func (ids *UserIdentifiers) ValidateContext(context.Context) error { } return nil } + +// ValidateContext wraps the generated validator with (optionally context-based) custom checks. +func (ids *MACSettingsProfileIdentifiers) ValidateContext(context.Context) error { + if err := ids.ValidateFields(); err != nil { + return errIdentifiers.WithCause(err) + } + return nil +} diff --git a/pkg/ttnpb/identifiers.pb.go b/pkg/ttnpb/identifiers.pb.go index fef2ec68b5..5bac238c19 100644 --- a/pkg/ttnpb/identifiers.pb.go +++ b/pkg/ttnpb/identifiers.pb.go @@ -918,6 +918,63 @@ func (x *GatewayIdentifiersList) GetGatewayIds() []*GatewayIdentifiers { return nil } +type MACSettingsProfileIdentifiers struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Application IDs. + ApplicationIds *ApplicationIdentifiers `protobuf:"bytes,1,opt,name=application_ids,json=applicationIds,proto3" json:"application_ids,omitempty"` + // Profile ID. + ProfileId string `protobuf:"bytes,2,opt,name=profile_id,json=profileId,proto3" json:"profile_id,omitempty"` +} + +func (x *MACSettingsProfileIdentifiers) Reset() { + *x = MACSettingsProfileIdentifiers{} + if protoimpl.UnsafeEnabled { + mi := &file_ttn_lorawan_v3_identifiers_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *MACSettingsProfileIdentifiers) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MACSettingsProfileIdentifiers) ProtoMessage() {} + +func (x *MACSettingsProfileIdentifiers) ProtoReflect() protoreflect.Message { + mi := &file_ttn_lorawan_v3_identifiers_proto_msgTypes[13] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use MACSettingsProfileIdentifiers.ProtoReflect.Descriptor instead. +func (*MACSettingsProfileIdentifiers) Descriptor() ([]byte, []int) { + return file_ttn_lorawan_v3_identifiers_proto_rawDescGZIP(), []int{13} +} + +func (x *MACSettingsProfileIdentifiers) GetApplicationIds() *ApplicationIdentifiers { + if x != nil { + return x.ApplicationIds + } + return nil +} + +func (x *MACSettingsProfileIdentifiers) GetProfileId() string { + if x != nil { + return x.ProfileId + } + return "" +} + var File_ttn_lorawan_v3_identifiers_proto protoreflect.FileDescriptor var file_ttn_lorawan_v3_identifiers_proto_rawDesc = []byte{ @@ -1217,11 +1274,23 @@ var file_ttn_lorawan_v3_identifiers_proto_rawDesc = []byte{ 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x52, 0x0a, 0x67, 0x61, 0x74, 0x65, - 0x77, 0x61, 0x79, 0x49, 0x64, 0x73, 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, + 0x77, 0x61, 0x79, 0x49, 0x64, 0x73, 0x22, 0xcc, 0x01, 0x0a, 0x1d, 0x4d, 0x41, 0x43, 0x53, 0x65, + 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x49, 0x64, 0x65, + 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x12, 0x59, 0x0a, 0x0f, 0x61, 0x70, 0x70, 0x6c, + 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 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, 0x49, 0x64, + 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, + 0x02, 0x10, 0x01, 0x52, 0x0e, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x49, 0x64, 0x73, 0x12, 0x46, 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x69, + 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x27, 0xfa, 0x42, 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, + 0x52, 0x09, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x49, 0x64, 0x3a, 0x08, 0xf2, 0xaa, 0x19, + 0x04, 0x08, 0x01, 0x10, 0x01, 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 ( @@ -1236,7 +1305,7 @@ func file_ttn_lorawan_v3_identifiers_proto_rawDescGZIP() []byte { return file_ttn_lorawan_v3_identifiers_proto_rawDescData } -var file_ttn_lorawan_v3_identifiers_proto_msgTypes = make([]protoimpl.MessageInfo, 13) +var file_ttn_lorawan_v3_identifiers_proto_msgTypes = make([]protoimpl.MessageInfo, 14) var file_ttn_lorawan_v3_identifiers_proto_goTypes = []interface{}{ (*ApplicationIdentifiers)(nil), // 0: ttn.lorawan.v3.ApplicationIdentifiers (*ClientIdentifiers)(nil), // 1: ttn.lorawan.v3.ClientIdentifiers @@ -1251,6 +1320,7 @@ var file_ttn_lorawan_v3_identifiers_proto_goTypes = []interface{}{ (*LoRaAllianceProfileIdentifiers)(nil), // 10: ttn.lorawan.v3.LoRaAllianceProfileIdentifiers (*EndDeviceIdentifiersList)(nil), // 11: ttn.lorawan.v3.EndDeviceIdentifiersList (*GatewayIdentifiersList)(nil), // 12: ttn.lorawan.v3.GatewayIdentifiersList + (*MACSettingsProfileIdentifiers)(nil), // 13: ttn.lorawan.v3.MACSettingsProfileIdentifiers } var file_ttn_lorawan_v3_identifiers_proto_depIdxs = []int32{ 0, // 0: ttn.lorawan.v3.EndDeviceIdentifiers.application_ids:type_name -> ttn.lorawan.v3.ApplicationIdentifiers @@ -1264,11 +1334,12 @@ var file_ttn_lorawan_v3_identifiers_proto_depIdxs = []int32{ 5, // 8: ttn.lorawan.v3.EntityIdentifiers.user_ids:type_name -> ttn.lorawan.v3.UserIdentifiers 2, // 9: ttn.lorawan.v3.EndDeviceIdentifiersList.end_device_ids:type_name -> ttn.lorawan.v3.EndDeviceIdentifiers 3, // 10: ttn.lorawan.v3.GatewayIdentifiersList.gateway_ids:type_name -> ttn.lorawan.v3.GatewayIdentifiers - 11, // [11:11] is the sub-list for method output_type - 11, // [11:11] is the sub-list for method input_type - 11, // [11:11] is the sub-list for extension type_name - 11, // [11:11] is the sub-list for extension extendee - 0, // [0:11] is the sub-list for field type_name + 0, // 11: ttn.lorawan.v3.MACSettingsProfileIdentifiers.application_ids:type_name -> ttn.lorawan.v3.ApplicationIdentifiers + 12, // [12:12] is the sub-list for method output_type + 12, // [12:12] is the sub-list for method input_type + 12, // [12:12] is the sub-list for extension type_name + 12, // [12:12] is the sub-list for extension extendee + 0, // [0:12] is the sub-list for field type_name } func init() { file_ttn_lorawan_v3_identifiers_proto_init() } @@ -1433,6 +1504,18 @@ func file_ttn_lorawan_v3_identifiers_proto_init() { return nil } } + file_ttn_lorawan_v3_identifiers_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*MACSettingsProfileIdentifiers); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } file_ttn_lorawan_v3_identifiers_proto_msgTypes[6].OneofWrappers = []interface{}{ (*OrganizationOrUserIdentifiers_OrganizationIds)(nil), @@ -1452,7 +1535,7 @@ func file_ttn_lorawan_v3_identifiers_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_ttn_lorawan_v3_identifiers_proto_rawDesc, NumEnums: 0, - NumMessages: 13, + NumMessages: 14, NumExtensions: 0, NumServices: 0, }, diff --git a/pkg/ttnpb/identifiers.pb.paths.fm.go b/pkg/ttnpb/identifiers.pb.paths.fm.go index 347d9423b2..306ae473aa 100644 --- a/pkg/ttnpb/identifiers.pb.paths.fm.go +++ b/pkg/ttnpb/identifiers.pb.paths.fm.go @@ -150,3 +150,13 @@ var GatewayIdentifiersListFieldPathsNested = []string{ var GatewayIdentifiersListFieldPathsTopLevel = []string{ "gateway_ids", } +var MACSettingsProfileIdentifiersFieldPathsNested = []string{ + "application_ids", + "application_ids.application_id", + "profile_id", +} + +var MACSettingsProfileIdentifiersFieldPathsTopLevel = []string{ + "application_ids", + "profile_id", +} diff --git a/pkg/ttnpb/identifiers.pb.setters.fm.go b/pkg/ttnpb/identifiers.pb.setters.fm.go index e3cf15bf7d..c7994ec6b4 100644 --- a/pkg/ttnpb/identifiers.pb.setters.fm.go +++ b/pkg/ttnpb/identifiers.pb.setters.fm.go @@ -753,3 +753,49 @@ func (dst *GatewayIdentifiersList) SetFields(src *GatewayIdentifiersList, paths } return nil } + +func (dst *MACSettingsProfileIdentifiers) SetFields(src *MACSettingsProfileIdentifiers, paths ...string) error { + for name, subs := range _processPaths(paths) { + switch name { + case "application_ids": + if len(subs) > 0 { + var newDst, newSrc *ApplicationIdentifiers + if (src == nil || src.ApplicationIds == nil) && dst.ApplicationIds == nil { + continue + } + if src != nil { + newSrc = src.ApplicationIds + } + if dst.ApplicationIds != nil { + newDst = dst.ApplicationIds + } else { + newDst = &ApplicationIdentifiers{} + dst.ApplicationIds = newDst + } + if err := newDst.SetFields(newSrc, subs...); err != nil { + return err + } + } else { + if src != nil { + dst.ApplicationIds = src.ApplicationIds + } else { + dst.ApplicationIds = nil + } + } + case "profile_id": + if len(subs) > 0 { + return fmt.Errorf("'profile_id' has no subfields, but %s were specified", subs) + } + if src != nil { + dst.ProfileId = src.ProfileId + } else { + var zero string + dst.ProfileId = zero + } + + default: + return fmt.Errorf("invalid field: '%s'", name) + } + } + return nil +} diff --git a/pkg/ttnpb/identifiers.pb.validate.go b/pkg/ttnpb/identifiers.pb.validate.go index 14622c9cd2..d4cfbde9f1 100644 --- a/pkg/ttnpb/identifiers.pb.validate.go +++ b/pkg/ttnpb/identifiers.pb.validate.go @@ -1609,3 +1609,122 @@ var _ interface { Cause() error ErrorName() string } = GatewayIdentifiersListValidationError{} + +// ValidateFields checks the field values on MACSettingsProfileIdentifiers with +// the rules defined in the proto definition for this message. If any rules +// are violated, an error is returned. +func (m *MACSettingsProfileIdentifiers) ValidateFields(paths ...string) error { + if m == nil { + return nil + } + + if len(paths) == 0 { + paths = MACSettingsProfileIdentifiersFieldPathsNested + } + + for name, subs := range _processPaths(append(paths[:0:0], paths...)) { + _ = subs + switch name { + case "application_ids": + + if m.GetApplicationIds() == nil { + return MACSettingsProfileIdentifiersValidationError{ + field: "application_ids", + reason: "value is required", + } + } + + if v, ok := interface{}(m.GetApplicationIds()).(interface{ ValidateFields(...string) error }); ok { + if err := v.ValidateFields(subs...); err != nil { + return MACSettingsProfileIdentifiersValidationError{ + field: "application_ids", + reason: "embedded message failed validation", + cause: err, + } + } + } + + case "profile_id": + + if utf8.RuneCountInString(m.GetProfileId()) > 36 { + return MACSettingsProfileIdentifiersValidationError{ + field: "profile_id", + reason: "value length must be at most 36 runes", + } + } + + if !_MACSettingsProfileIdentifiers_ProfileId_Pattern.MatchString(m.GetProfileId()) { + return MACSettingsProfileIdentifiersValidationError{ + field: "profile_id", + reason: "value does not match regex pattern \"^[a-z0-9](?:[-]?[a-z0-9]){2,}$\"", + } + } + + default: + return MACSettingsProfileIdentifiersValidationError{ + field: name, + reason: "invalid field path", + } + } + } + return nil +} + +// MACSettingsProfileIdentifiersValidationError is the validation error +// returned by MACSettingsProfileIdentifiers.ValidateFields if the designated +// constraints aren't met. +type MACSettingsProfileIdentifiersValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e MACSettingsProfileIdentifiersValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e MACSettingsProfileIdentifiersValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e MACSettingsProfileIdentifiersValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e MACSettingsProfileIdentifiersValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e MACSettingsProfileIdentifiersValidationError) ErrorName() string { + return "MACSettingsProfileIdentifiersValidationError" +} + +// Error satisfies the builtin error interface +func (e MACSettingsProfileIdentifiersValidationError) Error() string { + cause := "" + if e.cause != nil { + cause = fmt.Sprintf(" | caused by: %v", e.cause) + } + + key := "" + if e.key { + key = "key for " + } + + return fmt.Sprintf( + "invalid %sMACSettingsProfileIdentifiers.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = MACSettingsProfileIdentifiersValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = MACSettingsProfileIdentifiersValidationError{} + +var _MACSettingsProfileIdentifiers_ProfileId_Pattern = regexp.MustCompile("^[a-z0-9](?:[-]?[a-z0-9]){2,}$") diff --git a/pkg/ttnpb/identifiers_flags.pb.go b/pkg/ttnpb/identifiers_flags.pb.go index 5f765f6184..b1edd1a884 100644 --- a/pkg/ttnpb/identifiers_flags.pb.go +++ b/pkg/ttnpb/identifiers_flags.pb.go @@ -532,3 +532,57 @@ func (m *NetworkIdentifiers) SetFromFlags(flags *pflag.FlagSet, prefix string) ( } return paths, nil } + +// AddSelectFlagsForMACSettingsProfileIdentifiers adds flags to select fields in MACSettingsProfileIdentifiers. +func AddSelectFlagsForMACSettingsProfileIdentifiers(flags *pflag.FlagSet, prefix string, hidden bool) { + flags.AddFlag(flagsplugin.NewBoolFlag(flagsplugin.Prefix("application-ids", prefix), flagsplugin.SelectDesc(flagsplugin.Prefix("application-ids", prefix), true), flagsplugin.WithHidden(hidden))) + AddSelectFlagsForApplicationIdentifiers(flags, flagsplugin.Prefix("application-ids", prefix), hidden) + flags.AddFlag(flagsplugin.NewBoolFlag(flagsplugin.Prefix("profile-id", prefix), flagsplugin.SelectDesc(flagsplugin.Prefix("profile-id", prefix), false), flagsplugin.WithHidden(hidden))) +} + +// SelectFromFlags outputs the fieldmask paths forMACSettingsProfileIdentifiers message from select flags. +func PathsFromSelectFlagsForMACSettingsProfileIdentifiers(flags *pflag.FlagSet, prefix string) (paths []string, err error) { + if val, selected, err := flagsplugin.GetBool(flags, flagsplugin.Prefix("application_ids", prefix)); err != nil { + return nil, err + } else if selected && val { + paths = append(paths, flagsplugin.Prefix("application_ids", prefix)) + } + if selectPaths, err := PathsFromSelectFlagsForApplicationIdentifiers(flags, flagsplugin.Prefix("application_ids", prefix)); err != nil { + return nil, err + } else { + paths = append(paths, selectPaths...) + } + if val, selected, err := flagsplugin.GetBool(flags, flagsplugin.Prefix("profile_id", prefix)); err != nil { + return nil, err + } else if selected && val { + paths = append(paths, flagsplugin.Prefix("profile_id", prefix)) + } + return paths, nil +} + +// AddSetFlagsForMACSettingsProfileIdentifiers adds flags to select fields in MACSettingsProfileIdentifiers. +func AddSetFlagsForMACSettingsProfileIdentifiers(flags *pflag.FlagSet, prefix string, hidden bool) { + AddSetFlagsForApplicationIdentifiers(flags, flagsplugin.Prefix("application-ids", prefix), hidden) + flags.AddFlag(flagsplugin.NewStringFlag(flagsplugin.Prefix("profile-id", prefix), "", flagsplugin.WithHidden(hidden))) +} + +// SetFromFlags sets the MACSettingsProfileIdentifiers message from flags. +func (m *MACSettingsProfileIdentifiers) SetFromFlags(flags *pflag.FlagSet, prefix string) (paths []string, err error) { + if changed := flagsplugin.IsAnyPrefixSet(flags, flagsplugin.Prefix("application_ids", prefix)); changed { + if m.ApplicationIds == nil { + m.ApplicationIds = &ApplicationIdentifiers{} + } + if setPaths, err := m.ApplicationIds.SetFromFlags(flags, flagsplugin.Prefix("application_ids", prefix)); err != nil { + return nil, err + } else { + paths = append(paths, setPaths...) + } + } + if val, changed, err := flagsplugin.GetString(flags, flagsplugin.Prefix("profile_id", prefix)); err != nil { + return nil, err + } else if changed { + m.ProfileId = val + paths = append(paths, flagsplugin.Prefix("profile_id", prefix)) + } + return paths, nil +} diff --git a/pkg/ttnpb/identityserver.go b/pkg/ttnpb/identityserver.go index 0670ba9ea1..30ef03a434 100644 --- a/pkg/ttnpb/identityserver.go +++ b/pkg/ttnpb/identityserver.go @@ -14,6 +14,10 @@ package ttnpb +// SupportUserID is the userID used for creating the support user and for validation of operation in which this user +// should be present. +const SupportUserID = "support" + // GetEntityIdentifiers returns the EntityIdentifiers for the used access method. func (m *AuthInfoResponse) GetEntityIdentifiers() *EntityIdentifiers { if m == nil { @@ -37,17 +41,36 @@ func (m *AuthInfoResponse) GetRights() []Right { if m == nil { return nil } + + var rights []Right + var limitRights bool + switch accessMethod := m.GetAccessMethod().(type) { case *AuthInfoResponse_ApiKey: - return accessMethod.ApiKey.GetApiKey().GetRights() + if accessMethod.ApiKey.GetEntityIds().GetUserIds().GetUserId() == SupportUserID { + limitRights = true + } + rights = accessMethod.ApiKey.GetApiKey().GetRights() case *AuthInfoResponse_OauthAccessToken: - return accessMethod.OauthAccessToken.GetRights() + if accessMethod.OauthAccessToken.GetUserIds().GetUserId() == SupportUserID { + limitRights = true + } + rights = accessMethod.OauthAccessToken.GetRights() case *AuthInfoResponse_UserSession: - return RightsFrom(Right_RIGHT_ALL).Implied().GetRights() + if accessMethod.UserSession.GetUserIds().GetUserId() == SupportUserID { + limitRights = true + } + rights = RightsFrom(Right_RIGHT_ALL).Implied().GetRights() case *AuthInfoResponse_GatewayToken_: - return accessMethod.GatewayToken.GetRights() + rights = accessMethod.GatewayToken.GetRights() } - return nil + + universalRights := m.GetUniversalRights() + if universalRights != nil && limitRights { + return RightsFrom(rights...).Intersect(universalRights).GetRights() + } + + return rights } // GetOrganizationOrUserIdentifiers returns the OrganizationOrUserIdentifiers for the used access method. diff --git a/pkg/ttnpb/networkserver_mac_settings_profile.pb.go b/pkg/ttnpb/networkserver_mac_settings_profile.pb.go new file mode 100644 index 0000000000..03ed68b2f7 --- /dev/null +++ b/pkg/ttnpb/networkserver_mac_settings_profile.pb.go @@ -0,0 +1,995 @@ +// Copyright © 2024 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. + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.32.0 +// protoc v4.25.1 +// source: ttn/lorawan/v3/networkserver_mac_settings_profile.proto + +package ttnpb + +import ( + _ "github.com/TheThingsIndustries/protoc-gen-go-flags/annotations" + _ "github.com/envoyproxy/protoc-gen-validate/validate" + _ "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options" + _ "google.golang.org/genproto/googleapis/api/annotations" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + fieldmaskpb "google.golang.org/protobuf/types/known/fieldmaskpb" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type CreateMACSettingsProfileRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The MAC settings profile to create. + MacSettingsProfile *MACSettingsProfile `protobuf:"bytes,1,opt,name=mac_settings_profile,json=macSettingsProfile,proto3" json:"mac_settings_profile,omitempty"` +} + +func (x *CreateMACSettingsProfileRequest) Reset() { + *x = CreateMACSettingsProfileRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_ttn_lorawan_v3_networkserver_mac_settings_profile_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CreateMACSettingsProfileRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateMACSettingsProfileRequest) ProtoMessage() {} + +func (x *CreateMACSettingsProfileRequest) ProtoReflect() protoreflect.Message { + mi := &file_ttn_lorawan_v3_networkserver_mac_settings_profile_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CreateMACSettingsProfileRequest.ProtoReflect.Descriptor instead. +func (*CreateMACSettingsProfileRequest) Descriptor() ([]byte, []int) { + return file_ttn_lorawan_v3_networkserver_mac_settings_profile_proto_rawDescGZIP(), []int{0} +} + +func (x *CreateMACSettingsProfileRequest) GetMacSettingsProfile() *MACSettingsProfile { + if x != nil { + return x.MacSettingsProfile + } + return nil +} + +type CreateMACSettingsProfileResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The MAC settings profile. + MacSettingsProfile *MACSettingsProfile `protobuf:"bytes,1,opt,name=mac_settings_profile,json=macSettingsProfile,proto3" json:"mac_settings_profile,omitempty"` +} + +func (x *CreateMACSettingsProfileResponse) Reset() { + *x = CreateMACSettingsProfileResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_ttn_lorawan_v3_networkserver_mac_settings_profile_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CreateMACSettingsProfileResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateMACSettingsProfileResponse) ProtoMessage() {} + +func (x *CreateMACSettingsProfileResponse) ProtoReflect() protoreflect.Message { + mi := &file_ttn_lorawan_v3_networkserver_mac_settings_profile_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CreateMACSettingsProfileResponse.ProtoReflect.Descriptor instead. +func (*CreateMACSettingsProfileResponse) Descriptor() ([]byte, []int) { + return file_ttn_lorawan_v3_networkserver_mac_settings_profile_proto_rawDescGZIP(), []int{1} +} + +func (x *CreateMACSettingsProfileResponse) GetMacSettingsProfile() *MACSettingsProfile { + if x != nil { + return x.MacSettingsProfile + } + return nil +} + +type GetMACSettingsProfileRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The identifiers of the MAC settings profile. + MacSettingsProfileIds *MACSettingsProfileIdentifiers `protobuf:"bytes,1,opt,name=mac_settings_profile_ids,json=macSettingsProfileIds,proto3" json:"mac_settings_profile_ids,omitempty"` + // The names of the fields that should be returned. + FieldMask *fieldmaskpb.FieldMask `protobuf:"bytes,2,opt,name=field_mask,json=fieldMask,proto3" json:"field_mask,omitempty"` +} + +func (x *GetMACSettingsProfileRequest) Reset() { + *x = GetMACSettingsProfileRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_ttn_lorawan_v3_networkserver_mac_settings_profile_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetMACSettingsProfileRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetMACSettingsProfileRequest) ProtoMessage() {} + +func (x *GetMACSettingsProfileRequest) ProtoReflect() protoreflect.Message { + mi := &file_ttn_lorawan_v3_networkserver_mac_settings_profile_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetMACSettingsProfileRequest.ProtoReflect.Descriptor instead. +func (*GetMACSettingsProfileRequest) Descriptor() ([]byte, []int) { + return file_ttn_lorawan_v3_networkserver_mac_settings_profile_proto_rawDescGZIP(), []int{2} +} + +func (x *GetMACSettingsProfileRequest) GetMacSettingsProfileIds() *MACSettingsProfileIdentifiers { + if x != nil { + return x.MacSettingsProfileIds + } + return nil +} + +func (x *GetMACSettingsProfileRequest) GetFieldMask() *fieldmaskpb.FieldMask { + if x != nil { + return x.FieldMask + } + return nil +} + +type GetMACSettingsProfileResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The MAC settings profile. + MacSettingsProfile *MACSettingsProfile `protobuf:"bytes,1,opt,name=mac_settings_profile,json=macSettingsProfile,proto3" json:"mac_settings_profile,omitempty"` +} + +func (x *GetMACSettingsProfileResponse) Reset() { + *x = GetMACSettingsProfileResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_ttn_lorawan_v3_networkserver_mac_settings_profile_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetMACSettingsProfileResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetMACSettingsProfileResponse) ProtoMessage() {} + +func (x *GetMACSettingsProfileResponse) ProtoReflect() protoreflect.Message { + mi := &file_ttn_lorawan_v3_networkserver_mac_settings_profile_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetMACSettingsProfileResponse.ProtoReflect.Descriptor instead. +func (*GetMACSettingsProfileResponse) Descriptor() ([]byte, []int) { + return file_ttn_lorawan_v3_networkserver_mac_settings_profile_proto_rawDescGZIP(), []int{3} +} + +func (x *GetMACSettingsProfileResponse) GetMacSettingsProfile() *MACSettingsProfile { + if x != nil { + return x.MacSettingsProfile + } + return nil +} + +type UpdateMACSettingsProfileRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The identifiers of the MAC settings profile. + MacSettingsProfileIds *MACSettingsProfileIdentifiers `protobuf:"bytes,1,opt,name=mac_settings_profile_ids,json=macSettingsProfileIds,proto3" json:"mac_settings_profile_ids,omitempty"` + // The MAC settings profile to update. + MacSettingsProfile *MACSettingsProfile `protobuf:"bytes,2,opt,name=mac_settings_profile,json=macSettingsProfile,proto3" json:"mac_settings_profile,omitempty"` + // The names of the fields that should be updated. + FieldMask *fieldmaskpb.FieldMask `protobuf:"bytes,3,opt,name=field_mask,json=fieldMask,proto3" json:"field_mask,omitempty"` +} + +func (x *UpdateMACSettingsProfileRequest) Reset() { + *x = UpdateMACSettingsProfileRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_ttn_lorawan_v3_networkserver_mac_settings_profile_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UpdateMACSettingsProfileRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateMACSettingsProfileRequest) ProtoMessage() {} + +func (x *UpdateMACSettingsProfileRequest) ProtoReflect() protoreflect.Message { + mi := &file_ttn_lorawan_v3_networkserver_mac_settings_profile_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UpdateMACSettingsProfileRequest.ProtoReflect.Descriptor instead. +func (*UpdateMACSettingsProfileRequest) Descriptor() ([]byte, []int) { + return file_ttn_lorawan_v3_networkserver_mac_settings_profile_proto_rawDescGZIP(), []int{4} +} + +func (x *UpdateMACSettingsProfileRequest) GetMacSettingsProfileIds() *MACSettingsProfileIdentifiers { + if x != nil { + return x.MacSettingsProfileIds + } + return nil +} + +func (x *UpdateMACSettingsProfileRequest) GetMacSettingsProfile() *MACSettingsProfile { + if x != nil { + return x.MacSettingsProfile + } + return nil +} + +func (x *UpdateMACSettingsProfileRequest) GetFieldMask() *fieldmaskpb.FieldMask { + if x != nil { + return x.FieldMask + } + return nil +} + +type UpdateMACSettingsProfileResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The MAC settings profile. + MacSettingsProfile *MACSettingsProfile `protobuf:"bytes,1,opt,name=mac_settings_profile,json=macSettingsProfile,proto3" json:"mac_settings_profile,omitempty"` +} + +func (x *UpdateMACSettingsProfileResponse) Reset() { + *x = UpdateMACSettingsProfileResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_ttn_lorawan_v3_networkserver_mac_settings_profile_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UpdateMACSettingsProfileResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateMACSettingsProfileResponse) ProtoMessage() {} + +func (x *UpdateMACSettingsProfileResponse) ProtoReflect() protoreflect.Message { + mi := &file_ttn_lorawan_v3_networkserver_mac_settings_profile_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UpdateMACSettingsProfileResponse.ProtoReflect.Descriptor instead. +func (*UpdateMACSettingsProfileResponse) Descriptor() ([]byte, []int) { + return file_ttn_lorawan_v3_networkserver_mac_settings_profile_proto_rawDescGZIP(), []int{5} +} + +func (x *UpdateMACSettingsProfileResponse) GetMacSettingsProfile() *MACSettingsProfile { + if x != nil { + return x.MacSettingsProfile + } + return nil +} + +type DeleteMACSettingsProfileRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The identifiers of the MAC settings profile. + MacSettingsProfileIds *MACSettingsProfileIdentifiers `protobuf:"bytes,1,opt,name=mac_settings_profile_ids,json=macSettingsProfileIds,proto3" json:"mac_settings_profile_ids,omitempty"` +} + +func (x *DeleteMACSettingsProfileRequest) Reset() { + *x = DeleteMACSettingsProfileRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_ttn_lorawan_v3_networkserver_mac_settings_profile_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DeleteMACSettingsProfileRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteMACSettingsProfileRequest) ProtoMessage() {} + +func (x *DeleteMACSettingsProfileRequest) ProtoReflect() protoreflect.Message { + mi := &file_ttn_lorawan_v3_networkserver_mac_settings_profile_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DeleteMACSettingsProfileRequest.ProtoReflect.Descriptor instead. +func (*DeleteMACSettingsProfileRequest) Descriptor() ([]byte, []int) { + return file_ttn_lorawan_v3_networkserver_mac_settings_profile_proto_rawDescGZIP(), []int{6} +} + +func (x *DeleteMACSettingsProfileRequest) GetMacSettingsProfileIds() *MACSettingsProfileIdentifiers { + if x != nil { + return x.MacSettingsProfileIds + } + return nil +} + +type DeleteMACSettingsProfileResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *DeleteMACSettingsProfileResponse) Reset() { + *x = DeleteMACSettingsProfileResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_ttn_lorawan_v3_networkserver_mac_settings_profile_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DeleteMACSettingsProfileResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteMACSettingsProfileResponse) ProtoMessage() {} + +func (x *DeleteMACSettingsProfileResponse) ProtoReflect() protoreflect.Message { + mi := &file_ttn_lorawan_v3_networkserver_mac_settings_profile_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DeleteMACSettingsProfileResponse.ProtoReflect.Descriptor instead. +func (*DeleteMACSettingsProfileResponse) Descriptor() ([]byte, []int) { + return file_ttn_lorawan_v3_networkserver_mac_settings_profile_proto_rawDescGZIP(), []int{7} +} + +type ListMACSettingsProfilesRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The identifiers of the application. + ApplicationIds *ApplicationIdentifiers `protobuf:"bytes,1,opt,name=application_ids,json=applicationIds,proto3" json:"application_ids,omitempty"` + // The names of the fields that should be returned. + FieldMask *fieldmaskpb.FieldMask `protobuf:"bytes,2,opt,name=field_mask,json=fieldMask,proto3" json:"field_mask,omitempty"` + // Order the results by this field path (must be present in the field mask). + // Default ordering is by ID. Prepend with a minus (-) to reverse the order. + Order string `protobuf:"bytes,3,opt,name=order,proto3" json:"order,omitempty"` + // Limit the number of results per page. + Limit uint32 `protobuf:"varint,4,opt,name=limit,proto3" json:"limit,omitempty"` + // Page number for pagination. 0 is interpreted as 1. + Page uint32 `protobuf:"varint,5,opt,name=page,proto3" json:"page,omitempty"` +} + +func (x *ListMACSettingsProfilesRequest) Reset() { + *x = ListMACSettingsProfilesRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_ttn_lorawan_v3_networkserver_mac_settings_profile_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ListMACSettingsProfilesRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListMACSettingsProfilesRequest) ProtoMessage() {} + +func (x *ListMACSettingsProfilesRequest) ProtoReflect() protoreflect.Message { + mi := &file_ttn_lorawan_v3_networkserver_mac_settings_profile_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListMACSettingsProfilesRequest.ProtoReflect.Descriptor instead. +func (*ListMACSettingsProfilesRequest) Descriptor() ([]byte, []int) { + return file_ttn_lorawan_v3_networkserver_mac_settings_profile_proto_rawDescGZIP(), []int{8} +} + +func (x *ListMACSettingsProfilesRequest) GetApplicationIds() *ApplicationIdentifiers { + if x != nil { + return x.ApplicationIds + } + return nil +} + +func (x *ListMACSettingsProfilesRequest) GetFieldMask() *fieldmaskpb.FieldMask { + if x != nil { + return x.FieldMask + } + return nil +} + +func (x *ListMACSettingsProfilesRequest) GetOrder() string { + if x != nil { + return x.Order + } + return "" +} + +func (x *ListMACSettingsProfilesRequest) GetLimit() uint32 { + if x != nil { + return x.Limit + } + return 0 +} + +func (x *ListMACSettingsProfilesRequest) GetPage() uint32 { + if x != nil { + return x.Page + } + return 0 +} + +type ListMACSettingsProfilesResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The MAC settings profiles. + MacSettingsProfiles []*MACSettingsProfile `protobuf:"bytes,1,rep,name=mac_settings_profiles,json=macSettingsProfiles,proto3" json:"mac_settings_profiles,omitempty"` +} + +func (x *ListMACSettingsProfilesResponse) Reset() { + *x = ListMACSettingsProfilesResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_ttn_lorawan_v3_networkserver_mac_settings_profile_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ListMACSettingsProfilesResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListMACSettingsProfilesResponse) ProtoMessage() {} + +func (x *ListMACSettingsProfilesResponse) ProtoReflect() protoreflect.Message { + mi := &file_ttn_lorawan_v3_networkserver_mac_settings_profile_proto_msgTypes[9] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListMACSettingsProfilesResponse.ProtoReflect.Descriptor instead. +func (*ListMACSettingsProfilesResponse) Descriptor() ([]byte, []int) { + return file_ttn_lorawan_v3_networkserver_mac_settings_profile_proto_rawDescGZIP(), []int{9} +} + +func (x *ListMACSettingsProfilesResponse) GetMacSettingsProfiles() []*MACSettingsProfile { + if x != nil { + return x.MacSettingsProfiles + } + return nil +} + +var File_ttn_lorawan_v3_networkserver_mac_settings_profile_proto protoreflect.FileDescriptor + +var file_ttn_lorawan_v3_networkserver_mac_settings_profile_proto_rawDesc = []byte{ + 0x0a, 0x37, 0x74, 0x74, 0x6e, 0x2f, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2f, 0x76, 0x33, + 0x2f, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x6d, + 0x61, 0x63, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x66, + 0x69, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, + 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x20, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x6d, + 0x61, 0x73, 0x6b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x63, 0x2d, 0x67, 0x65, 0x6e, 0x2d, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x32, 0x2f, + 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x21, 0x74, 0x68, 0x65, 0x74, 0x68, + 0x69, 0x6e, 0x67, 0x73, 0x2f, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x74, 0x74, + 0x6e, 0x2f, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2f, 0x76, 0x33, 0x2f, 0x65, 0x6e, 0x64, + 0x5f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x20, 0x74, + 0x74, 0x6e, 0x2f, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2f, 0x76, 0x33, 0x2f, 0x69, 0x64, + 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, + 0x17, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, + 0x74, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x81, 0x01, 0x0a, 0x1f, 0x43, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x4d, 0x41, 0x43, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x50, 0x72, + 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x5e, 0x0a, 0x14, + 0x6d, 0x61, 0x63, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x5f, 0x70, 0x72, 0x6f, + 0x66, 0x69, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x74, 0x74, 0x6e, + 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4d, 0x41, 0x43, 0x53, + 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x42, 0x08, + 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x12, 0x6d, 0x61, 0x63, 0x53, 0x65, 0x74, + 0x74, 0x69, 0x6e, 0x67, 0x73, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x22, 0x82, 0x01, 0x0a, + 0x20, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4d, 0x41, 0x43, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, + 0x67, 0x73, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x5e, 0x0a, 0x14, 0x6d, 0x61, 0x63, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, + 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x22, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, + 0x2e, 0x4d, 0x41, 0x43, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x50, 0x72, 0x6f, 0x66, + 0x69, 0x6c, 0x65, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x12, 0x6d, + 0x61, 0x63, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, + 0x65, 0x22, 0xcb, 0x01, 0x0a, 0x1c, 0x47, 0x65, 0x74, 0x4d, 0x41, 0x43, 0x53, 0x65, 0x74, 0x74, + 0x69, 0x6e, 0x67, 0x73, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x70, 0x0a, 0x18, 0x6d, 0x61, 0x63, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, + 0x67, 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, + 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4d, 0x41, 0x43, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, + 0x73, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, + 0x65, 0x72, 0x73, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x15, 0x6d, + 0x61, 0x63, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, + 0x65, 0x49, 0x64, 0x73, 0x12, 0x39, 0x0a, 0x0a, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x6d, 0x61, + 0x73, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, + 0x4d, 0x61, 0x73, 0x6b, 0x52, 0x09, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x4d, 0x61, 0x73, 0x6b, 0x22, + 0x7f, 0x0a, 0x1d, 0x47, 0x65, 0x74, 0x4d, 0x41, 0x43, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, + 0x73, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x5e, 0x0a, 0x14, 0x6d, 0x61, 0x63, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, + 0x5f, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, + 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, + 0x4d, 0x41, 0x43, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x50, 0x72, 0x6f, 0x66, 0x69, + 0x6c, 0x65, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x12, 0x6d, 0x61, + 0x63, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, + 0x22, 0xae, 0x02, 0x0a, 0x1f, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x41, 0x43, 0x53, 0x65, + 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x70, 0x0a, 0x18, 0x6d, 0x61, 0x63, 0x5f, 0x73, 0x65, 0x74, 0x74, + 0x69, 0x6e, 0x67, 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x73, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, + 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4d, 0x41, 0x43, 0x53, 0x65, 0x74, 0x74, 0x69, + 0x6e, 0x67, 0x73, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, + 0x66, 0x69, 0x65, 0x72, 0x73, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, + 0x15, 0x6d, 0x61, 0x63, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x50, 0x72, 0x6f, 0x66, + 0x69, 0x6c, 0x65, 0x49, 0x64, 0x73, 0x12, 0x5e, 0x0a, 0x14, 0x6d, 0x61, 0x63, 0x5f, 0x73, 0x65, + 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, + 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4d, 0x41, 0x43, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, + 0x73, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, + 0x10, 0x01, 0x52, 0x12, 0x6d, 0x61, 0x63, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x50, + 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x12, 0x39, 0x0a, 0x0a, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, + 0x6d, 0x61, 0x73, 0x6b, 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, 0x46, 0x69, 0x65, + 0x6c, 0x64, 0x4d, 0x61, 0x73, 0x6b, 0x52, 0x09, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x4d, 0x61, 0x73, + 0x6b, 0x22, 0x82, 0x01, 0x0a, 0x20, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x41, 0x43, 0x53, + 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5e, 0x0a, 0x14, 0x6d, 0x61, 0x63, 0x5f, 0x73, 0x65, + 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, + 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4d, 0x41, 0x43, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, + 0x73, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, + 0x10, 0x01, 0x52, 0x12, 0x6d, 0x61, 0x63, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x50, + 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x22, 0x93, 0x01, 0x0a, 0x1f, 0x44, 0x65, 0x6c, 0x65, 0x74, + 0x65, 0x4d, 0x41, 0x43, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x50, 0x72, 0x6f, 0x66, + 0x69, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x70, 0x0a, 0x18, 0x6d, 0x61, + 0x63, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x66, 0x69, + 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x74, + 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4d, 0x41, + 0x43, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, + 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x42, 0x08, 0xfa, 0x42, 0x05, + 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x15, 0x6d, 0x61, 0x63, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, + 0x67, 0x73, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x49, 0x64, 0x73, 0x22, 0x22, 0x0a, 0x20, + 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x41, 0x43, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, + 0x73, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0xb4, 0x02, 0x0a, 0x1e, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x41, 0x43, 0x53, 0x65, 0x74, 0x74, + 0x69, 0x6e, 0x67, 0x73, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x59, 0x0a, 0x0f, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 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, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, + 0x69, 0x65, 0x72, 0x73, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x0e, + 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x73, 0x12, 0x39, + 0x0a, 0x0a, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4d, 0x61, 0x73, 0x6b, 0x52, 0x09, + 0x66, 0x69, 0x65, 0x6c, 0x64, 0x4d, 0x61, 0x73, 0x6b, 0x12, 0x3e, 0x0a, 0x05, 0x6f, 0x72, 0x64, + 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x28, 0xfa, 0x42, 0x25, 0x72, 0x23, 0x52, + 0x00, 0x52, 0x0e, 0x69, 0x64, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x69, + 0x64, 0x52, 0x0f, 0x2d, 0x69, 0x64, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x5f, + 0x69, 0x64, 0x52, 0x05, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x1e, 0x0a, 0x05, 0x6c, 0x69, 0x6d, + 0x69, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x2a, 0x03, 0x18, + 0xe8, 0x07, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x67, + 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x70, 0x61, 0x67, 0x65, 0x3a, 0x08, 0xf2, + 0xaa, 0x19, 0x04, 0x08, 0x00, 0x10, 0x01, 0x22, 0x79, 0x0a, 0x1f, 0x4c, 0x69, 0x73, 0x74, 0x4d, + 0x41, 0x43, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, + 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x56, 0x0a, 0x15, 0x6d, 0x61, + 0x63, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x66, 0x69, + 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x74, 0x74, 0x6e, 0x2e, + 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4d, 0x41, 0x43, 0x53, 0x65, + 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x52, 0x13, 0x6d, + 0x61, 0x63, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, + 0x65, 0x73, 0x32, 0xf8, 0x09, 0x0a, 0x1c, 0x4e, 0x73, 0x4d, 0x41, 0x43, 0x53, 0x65, 0x74, 0x74, + 0x69, 0x6e, 0x67, 0x73, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x67, 0x69, 0x73, + 0x74, 0x72, 0x79, 0x12, 0xd8, 0x01, 0x0a, 0x06, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x12, 0x2f, + 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, + 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4d, 0x41, 0x43, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, + 0x73, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x30, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, + 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4d, 0x41, 0x43, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, + 0x67, 0x73, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x6b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x65, 0x3a, 0x01, 0x2a, 0x22, 0x60, 0x2f, 0x6e, + 0x73, 0x2f, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x7b, + 0x6d, 0x61, 0x63, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x5f, 0x70, 0x72, 0x6f, + 0x66, 0x69, 0x6c, 0x65, 0x2e, 0x69, 0x64, 0x73, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x73, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x6d, 0x61, 0x63, 0x5f, 0x73, 0x65, 0x74, + 0x74, 0x69, 0x6e, 0x67, 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x12, 0xf5, + 0x01, 0x0a, 0x03, 0x47, 0x65, 0x74, 0x12, 0x2c, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, + 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x41, 0x43, 0x53, 0x65, + 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, + 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x41, 0x43, 0x53, 0x65, 0x74, 0x74, + 0x69, 0x6e, 0x67, 0x73, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x90, 0x01, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x89, 0x01, 0x12, 0x86, 0x01, + 0x2f, 0x6e, 0x73, 0x2f, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x2f, 0x7b, 0x6d, 0x61, 0x63, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x5f, 0x70, + 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x73, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x69, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x73, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x69, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x6d, 0x61, 0x63, 0x5f, 0x73, + 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, + 0x2f, 0x7b, 0x6d, 0x61, 0x63, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x5f, 0x70, + 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x66, 0x69, + 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x12, 0x81, 0x02, 0x0a, 0x06, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x12, 0x2f, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, + 0x76, 0x33, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x41, 0x43, 0x53, 0x65, 0x74, 0x74, + 0x69, 0x6e, 0x67, 0x73, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, + 0x2e, 0x76, 0x33, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x41, 0x43, 0x53, 0x65, 0x74, + 0x74, 0x69, 0x6e, 0x67, 0x73, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x93, 0x01, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x8c, 0x01, 0x3a, 0x01, + 0x2a, 0x1a, 0x86, 0x01, 0x2f, 0x6e, 0x73, 0x2f, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x7b, 0x6d, 0x61, 0x63, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, + 0x67, 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x73, 0x2e, 0x61, + 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x73, 0x2e, 0x61, + 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x6d, + 0x61, 0x63, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x66, + 0x69, 0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x6d, 0x61, 0x63, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, + 0x67, 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x73, 0x2e, 0x70, + 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x12, 0xfe, 0x01, 0x0a, 0x06, 0x44, + 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x2f, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, + 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x41, 0x43, + 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, + 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x41, + 0x43, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x90, 0x01, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x89, 0x01, 0x2a, 0x86, 0x01, 0x2f, 0x6e, 0x73, 0x2f, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x7b, 0x6d, 0x61, 0x63, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, + 0x6e, 0x67, 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x73, 0x2e, + 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x73, 0x2e, + 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x7d, 0x2f, + 0x6d, 0x61, 0x63, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x5f, 0x70, 0x72, 0x6f, + 0x66, 0x69, 0x6c, 0x65, 0x73, 0x2f, 0x7b, 0x6d, 0x61, 0x63, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, + 0x6e, 0x67, 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x73, 0x2e, + 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x12, 0xb8, 0x01, 0x0a, 0x04, + 0x4c, 0x69, 0x73, 0x74, 0x12, 0x2e, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, + 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x41, 0x43, 0x53, 0x65, 0x74, + 0x74, 0x69, 0x6e, 0x67, 0x73, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, + 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x41, 0x43, 0x53, 0x65, 0x74, + 0x74, 0x69, 0x6e, 0x67, 0x73, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x4f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x49, 0x12, 0x47, 0x2f, + 0x6e, 0x73, 0x2f, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, + 0x7b, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x73, + 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x7d, + 0x2f, 0x6d, 0x61, 0x63, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x5f, 0x70, 0x72, + 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x1a, 0x45, 0x92, 0x41, 0x42, 0x12, 0x40, 0x4d, 0x61, 0x6e, + 0x61, 0x67, 0x65, 0x20, 0x4d, 0x41, 0x43, 0x20, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, + 0x20, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x20, 0x6f, 0x6e, 0x20, 0x54, 0x68, 0x65, + 0x20, 0x54, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x20, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x20, 0x4e, 0x65, + 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 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 ( + file_ttn_lorawan_v3_networkserver_mac_settings_profile_proto_rawDescOnce sync.Once + file_ttn_lorawan_v3_networkserver_mac_settings_profile_proto_rawDescData = file_ttn_lorawan_v3_networkserver_mac_settings_profile_proto_rawDesc +) + +func file_ttn_lorawan_v3_networkserver_mac_settings_profile_proto_rawDescGZIP() []byte { + file_ttn_lorawan_v3_networkserver_mac_settings_profile_proto_rawDescOnce.Do(func() { + file_ttn_lorawan_v3_networkserver_mac_settings_profile_proto_rawDescData = protoimpl.X.CompressGZIP(file_ttn_lorawan_v3_networkserver_mac_settings_profile_proto_rawDescData) + }) + return file_ttn_lorawan_v3_networkserver_mac_settings_profile_proto_rawDescData +} + +var file_ttn_lorawan_v3_networkserver_mac_settings_profile_proto_msgTypes = make([]protoimpl.MessageInfo, 10) +var file_ttn_lorawan_v3_networkserver_mac_settings_profile_proto_goTypes = []interface{}{ + (*CreateMACSettingsProfileRequest)(nil), // 0: ttn.lorawan.v3.CreateMACSettingsProfileRequest + (*CreateMACSettingsProfileResponse)(nil), // 1: ttn.lorawan.v3.CreateMACSettingsProfileResponse + (*GetMACSettingsProfileRequest)(nil), // 2: ttn.lorawan.v3.GetMACSettingsProfileRequest + (*GetMACSettingsProfileResponse)(nil), // 3: ttn.lorawan.v3.GetMACSettingsProfileResponse + (*UpdateMACSettingsProfileRequest)(nil), // 4: ttn.lorawan.v3.UpdateMACSettingsProfileRequest + (*UpdateMACSettingsProfileResponse)(nil), // 5: ttn.lorawan.v3.UpdateMACSettingsProfileResponse + (*DeleteMACSettingsProfileRequest)(nil), // 6: ttn.lorawan.v3.DeleteMACSettingsProfileRequest + (*DeleteMACSettingsProfileResponse)(nil), // 7: ttn.lorawan.v3.DeleteMACSettingsProfileResponse + (*ListMACSettingsProfilesRequest)(nil), // 8: ttn.lorawan.v3.ListMACSettingsProfilesRequest + (*ListMACSettingsProfilesResponse)(nil), // 9: ttn.lorawan.v3.ListMACSettingsProfilesResponse + (*MACSettingsProfile)(nil), // 10: ttn.lorawan.v3.MACSettingsProfile + (*MACSettingsProfileIdentifiers)(nil), // 11: ttn.lorawan.v3.MACSettingsProfileIdentifiers + (*fieldmaskpb.FieldMask)(nil), // 12: google.protobuf.FieldMask + (*ApplicationIdentifiers)(nil), // 13: ttn.lorawan.v3.ApplicationIdentifiers +} +var file_ttn_lorawan_v3_networkserver_mac_settings_profile_proto_depIdxs = []int32{ + 10, // 0: ttn.lorawan.v3.CreateMACSettingsProfileRequest.mac_settings_profile:type_name -> ttn.lorawan.v3.MACSettingsProfile + 10, // 1: ttn.lorawan.v3.CreateMACSettingsProfileResponse.mac_settings_profile:type_name -> ttn.lorawan.v3.MACSettingsProfile + 11, // 2: ttn.lorawan.v3.GetMACSettingsProfileRequest.mac_settings_profile_ids:type_name -> ttn.lorawan.v3.MACSettingsProfileIdentifiers + 12, // 3: ttn.lorawan.v3.GetMACSettingsProfileRequest.field_mask:type_name -> google.protobuf.FieldMask + 10, // 4: ttn.lorawan.v3.GetMACSettingsProfileResponse.mac_settings_profile:type_name -> ttn.lorawan.v3.MACSettingsProfile + 11, // 5: ttn.lorawan.v3.UpdateMACSettingsProfileRequest.mac_settings_profile_ids:type_name -> ttn.lorawan.v3.MACSettingsProfileIdentifiers + 10, // 6: ttn.lorawan.v3.UpdateMACSettingsProfileRequest.mac_settings_profile:type_name -> ttn.lorawan.v3.MACSettingsProfile + 12, // 7: ttn.lorawan.v3.UpdateMACSettingsProfileRequest.field_mask:type_name -> google.protobuf.FieldMask + 10, // 8: ttn.lorawan.v3.UpdateMACSettingsProfileResponse.mac_settings_profile:type_name -> ttn.lorawan.v3.MACSettingsProfile + 11, // 9: ttn.lorawan.v3.DeleteMACSettingsProfileRequest.mac_settings_profile_ids:type_name -> ttn.lorawan.v3.MACSettingsProfileIdentifiers + 13, // 10: ttn.lorawan.v3.ListMACSettingsProfilesRequest.application_ids:type_name -> ttn.lorawan.v3.ApplicationIdentifiers + 12, // 11: ttn.lorawan.v3.ListMACSettingsProfilesRequest.field_mask:type_name -> google.protobuf.FieldMask + 10, // 12: ttn.lorawan.v3.ListMACSettingsProfilesResponse.mac_settings_profiles:type_name -> ttn.lorawan.v3.MACSettingsProfile + 0, // 13: ttn.lorawan.v3.NsMACSettingsProfileRegistry.Create:input_type -> ttn.lorawan.v3.CreateMACSettingsProfileRequest + 2, // 14: ttn.lorawan.v3.NsMACSettingsProfileRegistry.Get:input_type -> ttn.lorawan.v3.GetMACSettingsProfileRequest + 4, // 15: ttn.lorawan.v3.NsMACSettingsProfileRegistry.Update:input_type -> ttn.lorawan.v3.UpdateMACSettingsProfileRequest + 6, // 16: ttn.lorawan.v3.NsMACSettingsProfileRegistry.Delete:input_type -> ttn.lorawan.v3.DeleteMACSettingsProfileRequest + 8, // 17: ttn.lorawan.v3.NsMACSettingsProfileRegistry.List:input_type -> ttn.lorawan.v3.ListMACSettingsProfilesRequest + 1, // 18: ttn.lorawan.v3.NsMACSettingsProfileRegistry.Create:output_type -> ttn.lorawan.v3.CreateMACSettingsProfileResponse + 3, // 19: ttn.lorawan.v3.NsMACSettingsProfileRegistry.Get:output_type -> ttn.lorawan.v3.GetMACSettingsProfileResponse + 5, // 20: ttn.lorawan.v3.NsMACSettingsProfileRegistry.Update:output_type -> ttn.lorawan.v3.UpdateMACSettingsProfileResponse + 7, // 21: ttn.lorawan.v3.NsMACSettingsProfileRegistry.Delete:output_type -> ttn.lorawan.v3.DeleteMACSettingsProfileResponse + 9, // 22: ttn.lorawan.v3.NsMACSettingsProfileRegistry.List:output_type -> ttn.lorawan.v3.ListMACSettingsProfilesResponse + 18, // [18:23] is the sub-list for method output_type + 13, // [13:18] is the sub-list for method input_type + 13, // [13:13] is the sub-list for extension type_name + 13, // [13:13] is the sub-list for extension extendee + 0, // [0:13] is the sub-list for field type_name +} + +func init() { file_ttn_lorawan_v3_networkserver_mac_settings_profile_proto_init() } +func file_ttn_lorawan_v3_networkserver_mac_settings_profile_proto_init() { + if File_ttn_lorawan_v3_networkserver_mac_settings_profile_proto != nil { + return + } + file_ttn_lorawan_v3_end_device_proto_init() + file_ttn_lorawan_v3_identifiers_proto_init() + if !protoimpl.UnsafeEnabled { + file_ttn_lorawan_v3_networkserver_mac_settings_profile_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CreateMACSettingsProfileRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_ttn_lorawan_v3_networkserver_mac_settings_profile_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CreateMACSettingsProfileResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_ttn_lorawan_v3_networkserver_mac_settings_profile_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetMACSettingsProfileRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_ttn_lorawan_v3_networkserver_mac_settings_profile_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetMACSettingsProfileResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_ttn_lorawan_v3_networkserver_mac_settings_profile_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UpdateMACSettingsProfileRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_ttn_lorawan_v3_networkserver_mac_settings_profile_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UpdateMACSettingsProfileResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_ttn_lorawan_v3_networkserver_mac_settings_profile_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DeleteMACSettingsProfileRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_ttn_lorawan_v3_networkserver_mac_settings_profile_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DeleteMACSettingsProfileResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_ttn_lorawan_v3_networkserver_mac_settings_profile_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ListMACSettingsProfilesRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_ttn_lorawan_v3_networkserver_mac_settings_profile_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ListMACSettingsProfilesResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_ttn_lorawan_v3_networkserver_mac_settings_profile_proto_rawDesc, + NumEnums: 0, + NumMessages: 10, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_ttn_lorawan_v3_networkserver_mac_settings_profile_proto_goTypes, + DependencyIndexes: file_ttn_lorawan_v3_networkserver_mac_settings_profile_proto_depIdxs, + MessageInfos: file_ttn_lorawan_v3_networkserver_mac_settings_profile_proto_msgTypes, + }.Build() + File_ttn_lorawan_v3_networkserver_mac_settings_profile_proto = out.File + file_ttn_lorawan_v3_networkserver_mac_settings_profile_proto_rawDesc = nil + file_ttn_lorawan_v3_networkserver_mac_settings_profile_proto_goTypes = nil + file_ttn_lorawan_v3_networkserver_mac_settings_profile_proto_depIdxs = nil +} diff --git a/pkg/ttnpb/networkserver_mac_settings_profile.pb.gw.go b/pkg/ttnpb/networkserver_mac_settings_profile.pb.gw.go new file mode 100644 index 0000000000..9689da713f --- /dev/null +++ b/pkg/ttnpb/networkserver_mac_settings_profile.pb.gw.go @@ -0,0 +1,731 @@ +// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. +// source: ttn/lorawan/v3/networkserver_mac_settings_profile.proto + +/* +Package ttnpb is a reverse proxy. + +It translates gRPC into RESTful JSON APIs. +*/ +package ttnpb + +import ( + "context" + "io" + "net/http" + + "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" + "github.com/grpc-ecosystem/grpc-gateway/v2/utilities" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/grpclog" + "google.golang.org/grpc/metadata" + "google.golang.org/grpc/status" + "google.golang.org/protobuf/proto" +) + +// Suppress "imported and not used" errors +var _ codes.Code +var _ io.Reader +var _ status.Status +var _ = runtime.String +var _ = utilities.NewDoubleArray +var _ = metadata.Join + +func request_NsMACSettingsProfileRegistry_Create_0(ctx context.Context, marshaler runtime.Marshaler, client NsMACSettingsProfileRegistryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq CreateMACSettingsProfileRequest + var metadata runtime.ServerMetadata + + if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["mac_settings_profile.ids.application_ids.application_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "mac_settings_profile.ids.application_ids.application_id") + } + + err = runtime.PopulateFieldFromPath(&protoReq, "mac_settings_profile.ids.application_ids.application_id", val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "mac_settings_profile.ids.application_ids.application_id", err) + } + + msg, err := client.Create(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_NsMACSettingsProfileRegistry_Create_0(ctx context.Context, marshaler runtime.Marshaler, server NsMACSettingsProfileRegistryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq CreateMACSettingsProfileRequest + var metadata runtime.ServerMetadata + + if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["mac_settings_profile.ids.application_ids.application_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "mac_settings_profile.ids.application_ids.application_id") + } + + err = runtime.PopulateFieldFromPath(&protoReq, "mac_settings_profile.ids.application_ids.application_id", val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "mac_settings_profile.ids.application_ids.application_id", err) + } + + msg, err := server.Create(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_NsMACSettingsProfileRegistry_Get_0 = &utilities.DoubleArray{Encoding: map[string]int{"mac_settings_profile_ids": 0, "application_ids": 1, "application_id": 2, "profile_id": 3}, Base: []int{1, 1, 1, 1, 2, 0, 0}, Check: []int{0, 1, 2, 3, 2, 4, 5}} +) + +func request_NsMACSettingsProfileRegistry_Get_0(ctx context.Context, marshaler runtime.Marshaler, client NsMACSettingsProfileRegistryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq GetMACSettingsProfileRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["mac_settings_profile_ids.application_ids.application_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "mac_settings_profile_ids.application_ids.application_id") + } + + err = runtime.PopulateFieldFromPath(&protoReq, "mac_settings_profile_ids.application_ids.application_id", val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "mac_settings_profile_ids.application_ids.application_id", err) + } + + val, ok = pathParams["mac_settings_profile_ids.profile_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "mac_settings_profile_ids.profile_id") + } + + err = runtime.PopulateFieldFromPath(&protoReq, "mac_settings_profile_ids.profile_id", val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "mac_settings_profile_ids.profile_id", err) + } + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_NsMACSettingsProfileRegistry_Get_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.Get(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_NsMACSettingsProfileRegistry_Get_0(ctx context.Context, marshaler runtime.Marshaler, server NsMACSettingsProfileRegistryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq GetMACSettingsProfileRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["mac_settings_profile_ids.application_ids.application_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "mac_settings_profile_ids.application_ids.application_id") + } + + err = runtime.PopulateFieldFromPath(&protoReq, "mac_settings_profile_ids.application_ids.application_id", val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "mac_settings_profile_ids.application_ids.application_id", err) + } + + val, ok = pathParams["mac_settings_profile_ids.profile_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "mac_settings_profile_ids.profile_id") + } + + err = runtime.PopulateFieldFromPath(&protoReq, "mac_settings_profile_ids.profile_id", val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "mac_settings_profile_ids.profile_id", err) + } + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_NsMACSettingsProfileRegistry_Get_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.Get(ctx, &protoReq) + return msg, metadata, err + +} + +func request_NsMACSettingsProfileRegistry_Update_0(ctx context.Context, marshaler runtime.Marshaler, client NsMACSettingsProfileRegistryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq UpdateMACSettingsProfileRequest + var metadata runtime.ServerMetadata + + if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["mac_settings_profile_ids.application_ids.application_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "mac_settings_profile_ids.application_ids.application_id") + } + + err = runtime.PopulateFieldFromPath(&protoReq, "mac_settings_profile_ids.application_ids.application_id", val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "mac_settings_profile_ids.application_ids.application_id", err) + } + + val, ok = pathParams["mac_settings_profile_ids.profile_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "mac_settings_profile_ids.profile_id") + } + + err = runtime.PopulateFieldFromPath(&protoReq, "mac_settings_profile_ids.profile_id", val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "mac_settings_profile_ids.profile_id", err) + } + + msg, err := client.Update(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_NsMACSettingsProfileRegistry_Update_0(ctx context.Context, marshaler runtime.Marshaler, server NsMACSettingsProfileRegistryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq UpdateMACSettingsProfileRequest + var metadata runtime.ServerMetadata + + if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["mac_settings_profile_ids.application_ids.application_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "mac_settings_profile_ids.application_ids.application_id") + } + + err = runtime.PopulateFieldFromPath(&protoReq, "mac_settings_profile_ids.application_ids.application_id", val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "mac_settings_profile_ids.application_ids.application_id", err) + } + + val, ok = pathParams["mac_settings_profile_ids.profile_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "mac_settings_profile_ids.profile_id") + } + + err = runtime.PopulateFieldFromPath(&protoReq, "mac_settings_profile_ids.profile_id", val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "mac_settings_profile_ids.profile_id", err) + } + + msg, err := server.Update(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_NsMACSettingsProfileRegistry_Delete_0 = &utilities.DoubleArray{Encoding: map[string]int{"mac_settings_profile_ids": 0, "application_ids": 1, "application_id": 2, "profile_id": 3}, Base: []int{1, 1, 1, 1, 2, 0, 0}, Check: []int{0, 1, 2, 3, 2, 4, 5}} +) + +func request_NsMACSettingsProfileRegistry_Delete_0(ctx context.Context, marshaler runtime.Marshaler, client NsMACSettingsProfileRegistryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq DeleteMACSettingsProfileRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["mac_settings_profile_ids.application_ids.application_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "mac_settings_profile_ids.application_ids.application_id") + } + + err = runtime.PopulateFieldFromPath(&protoReq, "mac_settings_profile_ids.application_ids.application_id", val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "mac_settings_profile_ids.application_ids.application_id", err) + } + + val, ok = pathParams["mac_settings_profile_ids.profile_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "mac_settings_profile_ids.profile_id") + } + + err = runtime.PopulateFieldFromPath(&protoReq, "mac_settings_profile_ids.profile_id", val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "mac_settings_profile_ids.profile_id", err) + } + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_NsMACSettingsProfileRegistry_Delete_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.Delete(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_NsMACSettingsProfileRegistry_Delete_0(ctx context.Context, marshaler runtime.Marshaler, server NsMACSettingsProfileRegistryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq DeleteMACSettingsProfileRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["mac_settings_profile_ids.application_ids.application_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "mac_settings_profile_ids.application_ids.application_id") + } + + err = runtime.PopulateFieldFromPath(&protoReq, "mac_settings_profile_ids.application_ids.application_id", val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "mac_settings_profile_ids.application_ids.application_id", err) + } + + val, ok = pathParams["mac_settings_profile_ids.profile_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "mac_settings_profile_ids.profile_id") + } + + err = runtime.PopulateFieldFromPath(&protoReq, "mac_settings_profile_ids.profile_id", val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "mac_settings_profile_ids.profile_id", err) + } + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_NsMACSettingsProfileRegistry_Delete_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.Delete(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_NsMACSettingsProfileRegistry_List_0 = &utilities.DoubleArray{Encoding: map[string]int{"application_ids": 0, "application_id": 1}, Base: []int{1, 1, 1, 0}, Check: []int{0, 1, 2, 3}} +) + +func request_NsMACSettingsProfileRegistry_List_0(ctx context.Context, marshaler runtime.Marshaler, client NsMACSettingsProfileRegistryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ListMACSettingsProfilesRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["application_ids.application_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "application_ids.application_id") + } + + err = runtime.PopulateFieldFromPath(&protoReq, "application_ids.application_id", val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "application_ids.application_id", err) + } + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_NsMACSettingsProfileRegistry_List_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.List(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_NsMACSettingsProfileRegistry_List_0(ctx context.Context, marshaler runtime.Marshaler, server NsMACSettingsProfileRegistryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ListMACSettingsProfilesRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["application_ids.application_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "application_ids.application_id") + } + + err = runtime.PopulateFieldFromPath(&protoReq, "application_ids.application_id", val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "application_ids.application_id", err) + } + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_NsMACSettingsProfileRegistry_List_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.List(ctx, &protoReq) + return msg, metadata, err + +} + +// RegisterNsMACSettingsProfileRegistryHandlerServer registers the http handlers for service NsMACSettingsProfileRegistry to "mux". +// UnaryRPC :call NsMACSettingsProfileRegistryServer directly. +// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. +// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterNsMACSettingsProfileRegistryHandlerFromEndpoint instead. +func RegisterNsMACSettingsProfileRegistryHandlerServer(ctx context.Context, mux *runtime.ServeMux, server NsMACSettingsProfileRegistryServer) error { + + mux.Handle("POST", pattern_NsMACSettingsProfileRegistry_Create_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + var err error + var annotatedContext context.Context + annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/ttn.lorawan.v3.NsMACSettingsProfileRegistry/Create", runtime.WithHTTPPathPattern("/ns/applications/{mac_settings_profile.ids.application_ids.application_id}/mac_settings_profiles")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_NsMACSettingsProfileRegistry_Create_0(annotatedContext, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + + forward_NsMACSettingsProfileRegistry_Create_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_NsMACSettingsProfileRegistry_Get_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + var err error + var annotatedContext context.Context + annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/ttn.lorawan.v3.NsMACSettingsProfileRegistry/Get", runtime.WithHTTPPathPattern("/ns/applications/{mac_settings_profile_ids.application_ids.application_id}/mac_settings_profiles/{mac_settings_profile_ids.profile_id}")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_NsMACSettingsProfileRegistry_Get_0(annotatedContext, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + + forward_NsMACSettingsProfileRegistry_Get_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("PUT", pattern_NsMACSettingsProfileRegistry_Update_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + var err error + var annotatedContext context.Context + annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/ttn.lorawan.v3.NsMACSettingsProfileRegistry/Update", runtime.WithHTTPPathPattern("/ns/applications/{mac_settings_profile_ids.application_ids.application_id}/mac_settings_profiles/{mac_settings_profile_ids.profile_id}")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_NsMACSettingsProfileRegistry_Update_0(annotatedContext, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + + forward_NsMACSettingsProfileRegistry_Update_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("DELETE", pattern_NsMACSettingsProfileRegistry_Delete_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + var err error + var annotatedContext context.Context + annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/ttn.lorawan.v3.NsMACSettingsProfileRegistry/Delete", runtime.WithHTTPPathPattern("/ns/applications/{mac_settings_profile_ids.application_ids.application_id}/mac_settings_profiles/{mac_settings_profile_ids.profile_id}")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_NsMACSettingsProfileRegistry_Delete_0(annotatedContext, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + + forward_NsMACSettingsProfileRegistry_Delete_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_NsMACSettingsProfileRegistry_List_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + var err error + var annotatedContext context.Context + annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/ttn.lorawan.v3.NsMACSettingsProfileRegistry/List", runtime.WithHTTPPathPattern("/ns/applications/{application_ids.application_id}/mac_settings_profiles")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_NsMACSettingsProfileRegistry_List_0(annotatedContext, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + + forward_NsMACSettingsProfileRegistry_List_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +// RegisterNsMACSettingsProfileRegistryHandlerFromEndpoint is same as RegisterNsMACSettingsProfileRegistryHandler but +// automatically dials to "endpoint" and closes the connection when "ctx" gets done. +func RegisterNsMACSettingsProfileRegistryHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { + conn, err := grpc.DialContext(ctx, endpoint, opts...) + if err != nil { + return err + } + defer func() { + if err != nil { + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + return + } + go func() { + <-ctx.Done() + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + }() + }() + + return RegisterNsMACSettingsProfileRegistryHandler(ctx, mux, conn) +} + +// RegisterNsMACSettingsProfileRegistryHandler registers the http handlers for service NsMACSettingsProfileRegistry to "mux". +// The handlers forward requests to the grpc endpoint over "conn". +func RegisterNsMACSettingsProfileRegistryHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { + return RegisterNsMACSettingsProfileRegistryHandlerClient(ctx, mux, NewNsMACSettingsProfileRegistryClient(conn)) +} + +// RegisterNsMACSettingsProfileRegistryHandlerClient registers the http handlers for service NsMACSettingsProfileRegistry +// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "NsMACSettingsProfileRegistryClient". +// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "NsMACSettingsProfileRegistryClient" +// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in +// "NsMACSettingsProfileRegistryClient" to call the correct interceptors. +func RegisterNsMACSettingsProfileRegistryHandlerClient(ctx context.Context, mux *runtime.ServeMux, client NsMACSettingsProfileRegistryClient) error { + + mux.Handle("POST", pattern_NsMACSettingsProfileRegistry_Create_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + var err error + var annotatedContext context.Context + annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/ttn.lorawan.v3.NsMACSettingsProfileRegistry/Create", runtime.WithHTTPPathPattern("/ns/applications/{mac_settings_profile.ids.application_ids.application_id}/mac_settings_profiles")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_NsMACSettingsProfileRegistry_Create_0(annotatedContext, inboundMarshaler, client, req, pathParams) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + + forward_NsMACSettingsProfileRegistry_Create_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_NsMACSettingsProfileRegistry_Get_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + var err error + var annotatedContext context.Context + annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/ttn.lorawan.v3.NsMACSettingsProfileRegistry/Get", runtime.WithHTTPPathPattern("/ns/applications/{mac_settings_profile_ids.application_ids.application_id}/mac_settings_profiles/{mac_settings_profile_ids.profile_id}")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_NsMACSettingsProfileRegistry_Get_0(annotatedContext, inboundMarshaler, client, req, pathParams) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + + forward_NsMACSettingsProfileRegistry_Get_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("PUT", pattern_NsMACSettingsProfileRegistry_Update_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + var err error + var annotatedContext context.Context + annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/ttn.lorawan.v3.NsMACSettingsProfileRegistry/Update", runtime.WithHTTPPathPattern("/ns/applications/{mac_settings_profile_ids.application_ids.application_id}/mac_settings_profiles/{mac_settings_profile_ids.profile_id}")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_NsMACSettingsProfileRegistry_Update_0(annotatedContext, inboundMarshaler, client, req, pathParams) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + + forward_NsMACSettingsProfileRegistry_Update_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("DELETE", pattern_NsMACSettingsProfileRegistry_Delete_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + var err error + var annotatedContext context.Context + annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/ttn.lorawan.v3.NsMACSettingsProfileRegistry/Delete", runtime.WithHTTPPathPattern("/ns/applications/{mac_settings_profile_ids.application_ids.application_id}/mac_settings_profiles/{mac_settings_profile_ids.profile_id}")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_NsMACSettingsProfileRegistry_Delete_0(annotatedContext, inboundMarshaler, client, req, pathParams) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + + forward_NsMACSettingsProfileRegistry_Delete_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_NsMACSettingsProfileRegistry_List_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + var err error + var annotatedContext context.Context + annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/ttn.lorawan.v3.NsMACSettingsProfileRegistry/List", runtime.WithHTTPPathPattern("/ns/applications/{application_ids.application_id}/mac_settings_profiles")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_NsMACSettingsProfileRegistry_List_0(annotatedContext, inboundMarshaler, client, req, pathParams) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + + forward_NsMACSettingsProfileRegistry_List_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +var ( + pattern_NsMACSettingsProfileRegistry_Create_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2, 2, 3}, []string{"ns", "applications", "mac_settings_profile.ids.application_ids.application_id", "mac_settings_profiles"}, "")) + + pattern_NsMACSettingsProfileRegistry_Get_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"ns", "applications", "mac_settings_profile_ids.application_ids.application_id", "mac_settings_profiles", "mac_settings_profile_ids.profile_id"}, "")) + + pattern_NsMACSettingsProfileRegistry_Update_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"ns", "applications", "mac_settings_profile_ids.application_ids.application_id", "mac_settings_profiles", "mac_settings_profile_ids.profile_id"}, "")) + + pattern_NsMACSettingsProfileRegistry_Delete_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"ns", "applications", "mac_settings_profile_ids.application_ids.application_id", "mac_settings_profiles", "mac_settings_profile_ids.profile_id"}, "")) + + pattern_NsMACSettingsProfileRegistry_List_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2, 2, 3}, []string{"ns", "applications", "application_ids.application_id", "mac_settings_profiles"}, "")) +) + +var ( + forward_NsMACSettingsProfileRegistry_Create_0 = runtime.ForwardResponseMessage + + forward_NsMACSettingsProfileRegistry_Get_0 = runtime.ForwardResponseMessage + + forward_NsMACSettingsProfileRegistry_Update_0 = runtime.ForwardResponseMessage + + forward_NsMACSettingsProfileRegistry_Delete_0 = runtime.ForwardResponseMessage + + forward_NsMACSettingsProfileRegistry_List_0 = runtime.ForwardResponseMessage +) diff --git a/pkg/ttnpb/networkserver_mac_settings_profile.pb.paths.fm.go b/pkg/ttnpb/networkserver_mac_settings_profile.pb.paths.fm.go new file mode 100644 index 0000000000..d1a0674b9c --- /dev/null +++ b/pkg/ttnpb/networkserver_mac_settings_profile.pb.paths.fm.go @@ -0,0 +1,1123 @@ +// Code generated by protoc-gen-fieldmask. DO NOT EDIT. + +package ttnpb + +var CreateMACSettingsProfileRequestFieldPathsNested = []string{ + "mac_settings_profile", + "mac_settings_profile.ids", + "mac_settings_profile.ids.application_ids", + "mac_settings_profile.ids.application_ids.application_id", + "mac_settings_profile.ids.profile_id", + "mac_settings_profile.mac_settings", + "mac_settings_profile.mac_settings.adr", + "mac_settings_profile.mac_settings.adr.mode", + "mac_settings_profile.mac_settings.adr.mode.disabled", + "mac_settings_profile.mac_settings.adr.mode.dynamic", + "mac_settings_profile.mac_settings.adr.mode.dynamic.channel_steering", + "mac_settings_profile.mac_settings.adr.mode.dynamic.channel_steering.mode", + "mac_settings_profile.mac_settings.adr.mode.dynamic.channel_steering.mode.disabled", + "mac_settings_profile.mac_settings.adr.mode.dynamic.channel_steering.mode.lora_narrow", + "mac_settings_profile.mac_settings.adr.mode.dynamic.margin", + "mac_settings_profile.mac_settings.adr.mode.dynamic.max_data_rate_index", + "mac_settings_profile.mac_settings.adr.mode.dynamic.max_data_rate_index.value", + "mac_settings_profile.mac_settings.adr.mode.dynamic.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.max_tx_power_index", + "mac_settings_profile.mac_settings.adr.mode.dynamic.min_data_rate_index", + "mac_settings_profile.mac_settings.adr.mode.dynamic.min_data_rate_index.value", + "mac_settings_profile.mac_settings.adr.mode.dynamic.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.min_tx_power_index", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_0", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_0.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_0.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_1", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_1.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_1.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_10", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_10.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_10.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_11", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_11.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_11.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_12", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_12.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_12.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_13", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_13.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_13.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_14", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_14.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_14.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_15", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_15.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_15.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_2", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_2.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_2.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_3", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_3.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_3.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_4", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_4.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_4.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_5", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_5.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_5.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_6", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_6.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_6.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_7", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_7.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_7.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_8", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_8.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_8.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_9", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_9.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_9.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.static", + "mac_settings_profile.mac_settings.adr.mode.static.data_rate_index", + "mac_settings_profile.mac_settings.adr.mode.static.nb_trans", + "mac_settings_profile.mac_settings.adr.mode.static.tx_power_index", + "mac_settings_profile.mac_settings.adr_margin", + "mac_settings_profile.mac_settings.beacon_frequency", + "mac_settings_profile.mac_settings.beacon_frequency.value", + "mac_settings_profile.mac_settings.class_b_c_downlink_interval", + "mac_settings_profile.mac_settings.class_b_timeout", + "mac_settings_profile.mac_settings.class_c_timeout", + "mac_settings_profile.mac_settings.desired_adr_ack_delay_exponent", + "mac_settings_profile.mac_settings.desired_adr_ack_delay_exponent.value", + "mac_settings_profile.mac_settings.desired_adr_ack_limit_exponent", + "mac_settings_profile.mac_settings.desired_adr_ack_limit_exponent.value", + "mac_settings_profile.mac_settings.desired_beacon_frequency", + "mac_settings_profile.mac_settings.desired_beacon_frequency.value", + "mac_settings_profile.mac_settings.desired_max_duty_cycle", + "mac_settings_profile.mac_settings.desired_max_duty_cycle.value", + "mac_settings_profile.mac_settings.desired_max_eirp", + "mac_settings_profile.mac_settings.desired_max_eirp.value", + "mac_settings_profile.mac_settings.desired_ping_slot_data_rate_index", + "mac_settings_profile.mac_settings.desired_ping_slot_data_rate_index.value", + "mac_settings_profile.mac_settings.desired_ping_slot_frequency", + "mac_settings_profile.mac_settings.desired_ping_slot_frequency.value", + "mac_settings_profile.mac_settings.desired_relay", + "mac_settings_profile.mac_settings.desired_relay.mode", + "mac_settings_profile.mac_settings.desired_relay.mode.served", + "mac_settings_profile.mac_settings.desired_relay.mode.served.backoff", + "mac_settings_profile.mac_settings.desired_relay.mode.served.mode", + "mac_settings_profile.mac_settings.desired_relay.mode.served.mode.always", + "mac_settings_profile.mac_settings.desired_relay.mode.served.mode.dynamic", + "mac_settings_profile.mac_settings.desired_relay.mode.served.mode.dynamic.smart_enable_level", + "mac_settings_profile.mac_settings.desired_relay.mode.served.mode.end_device_controlled", + "mac_settings_profile.mac_settings.desired_relay.mode.served.second_channel", + "mac_settings_profile.mac_settings.desired_relay.mode.served.second_channel.ack_offset", + "mac_settings_profile.mac_settings.desired_relay.mode.served.second_channel.data_rate_index", + "mac_settings_profile.mac_settings.desired_relay.mode.served.second_channel.frequency", + "mac_settings_profile.mac_settings.desired_relay.mode.served.serving_device_id", + "mac_settings_profile.mac_settings.desired_relay.mode.serving", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.cad_periodicity", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.default_channel_index", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.limits", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.limits.join_requests", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.limits.join_requests.bucket_size", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.limits.join_requests.reload_rate", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.limits.notifications", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.limits.notifications.bucket_size", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.limits.notifications.reload_rate", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.limits.overall", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.limits.overall.bucket_size", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.limits.overall.reload_rate", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.limits.reset_behavior", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.limits.uplink_messages", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.limits.uplink_messages.bucket_size", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.limits.uplink_messages.reload_rate", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.second_channel", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.second_channel.ack_offset", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.second_channel.data_rate_index", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.second_channel.frequency", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.uplink_forwarding_rules", + "mac_settings_profile.mac_settings.desired_rx1_data_rate_offset", + "mac_settings_profile.mac_settings.desired_rx1_data_rate_offset.value", + "mac_settings_profile.mac_settings.desired_rx1_delay", + "mac_settings_profile.mac_settings.desired_rx1_delay.value", + "mac_settings_profile.mac_settings.desired_rx2_data_rate_index", + "mac_settings_profile.mac_settings.desired_rx2_data_rate_index.value", + "mac_settings_profile.mac_settings.desired_rx2_frequency", + "mac_settings_profile.mac_settings.desired_rx2_frequency.value", + "mac_settings_profile.mac_settings.downlink_dwell_time", + "mac_settings_profile.mac_settings.downlink_dwell_time.value", + "mac_settings_profile.mac_settings.factory_preset_frequencies", + "mac_settings_profile.mac_settings.max_duty_cycle", + "mac_settings_profile.mac_settings.max_duty_cycle.value", + "mac_settings_profile.mac_settings.ping_slot_data_rate_index", + "mac_settings_profile.mac_settings.ping_slot_data_rate_index.value", + "mac_settings_profile.mac_settings.ping_slot_frequency", + "mac_settings_profile.mac_settings.ping_slot_frequency.value", + "mac_settings_profile.mac_settings.ping_slot_periodicity", + "mac_settings_profile.mac_settings.ping_slot_periodicity.value", + "mac_settings_profile.mac_settings.relay", + "mac_settings_profile.mac_settings.relay.mode", + "mac_settings_profile.mac_settings.relay.mode.served", + "mac_settings_profile.mac_settings.relay.mode.served.backoff", + "mac_settings_profile.mac_settings.relay.mode.served.mode", + "mac_settings_profile.mac_settings.relay.mode.served.mode.always", + "mac_settings_profile.mac_settings.relay.mode.served.mode.dynamic", + "mac_settings_profile.mac_settings.relay.mode.served.mode.dynamic.smart_enable_level", + "mac_settings_profile.mac_settings.relay.mode.served.mode.end_device_controlled", + "mac_settings_profile.mac_settings.relay.mode.served.second_channel", + "mac_settings_profile.mac_settings.relay.mode.served.second_channel.ack_offset", + "mac_settings_profile.mac_settings.relay.mode.served.second_channel.data_rate_index", + "mac_settings_profile.mac_settings.relay.mode.served.second_channel.frequency", + "mac_settings_profile.mac_settings.relay.mode.served.serving_device_id", + "mac_settings_profile.mac_settings.relay.mode.serving", + "mac_settings_profile.mac_settings.relay.mode.serving.cad_periodicity", + "mac_settings_profile.mac_settings.relay.mode.serving.default_channel_index", + "mac_settings_profile.mac_settings.relay.mode.serving.limits", + "mac_settings_profile.mac_settings.relay.mode.serving.limits.join_requests", + "mac_settings_profile.mac_settings.relay.mode.serving.limits.join_requests.bucket_size", + "mac_settings_profile.mac_settings.relay.mode.serving.limits.join_requests.reload_rate", + "mac_settings_profile.mac_settings.relay.mode.serving.limits.notifications", + "mac_settings_profile.mac_settings.relay.mode.serving.limits.notifications.bucket_size", + "mac_settings_profile.mac_settings.relay.mode.serving.limits.notifications.reload_rate", + "mac_settings_profile.mac_settings.relay.mode.serving.limits.overall", + "mac_settings_profile.mac_settings.relay.mode.serving.limits.overall.bucket_size", + "mac_settings_profile.mac_settings.relay.mode.serving.limits.overall.reload_rate", + "mac_settings_profile.mac_settings.relay.mode.serving.limits.reset_behavior", + "mac_settings_profile.mac_settings.relay.mode.serving.limits.uplink_messages", + "mac_settings_profile.mac_settings.relay.mode.serving.limits.uplink_messages.bucket_size", + "mac_settings_profile.mac_settings.relay.mode.serving.limits.uplink_messages.reload_rate", + "mac_settings_profile.mac_settings.relay.mode.serving.second_channel", + "mac_settings_profile.mac_settings.relay.mode.serving.second_channel.ack_offset", + "mac_settings_profile.mac_settings.relay.mode.serving.second_channel.data_rate_index", + "mac_settings_profile.mac_settings.relay.mode.serving.second_channel.frequency", + "mac_settings_profile.mac_settings.relay.mode.serving.uplink_forwarding_rules", + "mac_settings_profile.mac_settings.resets_f_cnt", + "mac_settings_profile.mac_settings.resets_f_cnt.value", + "mac_settings_profile.mac_settings.rx1_data_rate_offset", + "mac_settings_profile.mac_settings.rx1_data_rate_offset.value", + "mac_settings_profile.mac_settings.rx1_delay", + "mac_settings_profile.mac_settings.rx1_delay.value", + "mac_settings_profile.mac_settings.rx2_data_rate_index", + "mac_settings_profile.mac_settings.rx2_data_rate_index.value", + "mac_settings_profile.mac_settings.rx2_frequency", + "mac_settings_profile.mac_settings.rx2_frequency.value", + "mac_settings_profile.mac_settings.schedule_downlinks", + "mac_settings_profile.mac_settings.schedule_downlinks.value", + "mac_settings_profile.mac_settings.status_count_periodicity", + "mac_settings_profile.mac_settings.status_time_periodicity", + "mac_settings_profile.mac_settings.supports_32_bit_f_cnt", + "mac_settings_profile.mac_settings.supports_32_bit_f_cnt.value", + "mac_settings_profile.mac_settings.uplink_dwell_time", + "mac_settings_profile.mac_settings.uplink_dwell_time.value", + "mac_settings_profile.mac_settings.use_adr", + "mac_settings_profile.mac_settings.use_adr.value", +} + +var CreateMACSettingsProfileRequestFieldPathsTopLevel = []string{ + "mac_settings_profile", +} +var CreateMACSettingsProfileResponseFieldPathsNested = []string{ + "mac_settings_profile", + "mac_settings_profile.ids", + "mac_settings_profile.ids.application_ids", + "mac_settings_profile.ids.application_ids.application_id", + "mac_settings_profile.ids.profile_id", + "mac_settings_profile.mac_settings", + "mac_settings_profile.mac_settings.adr", + "mac_settings_profile.mac_settings.adr.mode", + "mac_settings_profile.mac_settings.adr.mode.disabled", + "mac_settings_profile.mac_settings.adr.mode.dynamic", + "mac_settings_profile.mac_settings.adr.mode.dynamic.channel_steering", + "mac_settings_profile.mac_settings.adr.mode.dynamic.channel_steering.mode", + "mac_settings_profile.mac_settings.adr.mode.dynamic.channel_steering.mode.disabled", + "mac_settings_profile.mac_settings.adr.mode.dynamic.channel_steering.mode.lora_narrow", + "mac_settings_profile.mac_settings.adr.mode.dynamic.margin", + "mac_settings_profile.mac_settings.adr.mode.dynamic.max_data_rate_index", + "mac_settings_profile.mac_settings.adr.mode.dynamic.max_data_rate_index.value", + "mac_settings_profile.mac_settings.adr.mode.dynamic.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.max_tx_power_index", + "mac_settings_profile.mac_settings.adr.mode.dynamic.min_data_rate_index", + "mac_settings_profile.mac_settings.adr.mode.dynamic.min_data_rate_index.value", + "mac_settings_profile.mac_settings.adr.mode.dynamic.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.min_tx_power_index", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_0", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_0.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_0.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_1", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_1.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_1.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_10", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_10.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_10.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_11", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_11.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_11.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_12", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_12.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_12.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_13", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_13.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_13.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_14", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_14.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_14.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_15", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_15.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_15.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_2", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_2.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_2.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_3", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_3.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_3.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_4", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_4.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_4.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_5", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_5.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_5.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_6", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_6.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_6.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_7", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_7.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_7.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_8", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_8.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_8.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_9", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_9.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_9.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.static", + "mac_settings_profile.mac_settings.adr.mode.static.data_rate_index", + "mac_settings_profile.mac_settings.adr.mode.static.nb_trans", + "mac_settings_profile.mac_settings.adr.mode.static.tx_power_index", + "mac_settings_profile.mac_settings.adr_margin", + "mac_settings_profile.mac_settings.beacon_frequency", + "mac_settings_profile.mac_settings.beacon_frequency.value", + "mac_settings_profile.mac_settings.class_b_c_downlink_interval", + "mac_settings_profile.mac_settings.class_b_timeout", + "mac_settings_profile.mac_settings.class_c_timeout", + "mac_settings_profile.mac_settings.desired_adr_ack_delay_exponent", + "mac_settings_profile.mac_settings.desired_adr_ack_delay_exponent.value", + "mac_settings_profile.mac_settings.desired_adr_ack_limit_exponent", + "mac_settings_profile.mac_settings.desired_adr_ack_limit_exponent.value", + "mac_settings_profile.mac_settings.desired_beacon_frequency", + "mac_settings_profile.mac_settings.desired_beacon_frequency.value", + "mac_settings_profile.mac_settings.desired_max_duty_cycle", + "mac_settings_profile.mac_settings.desired_max_duty_cycle.value", + "mac_settings_profile.mac_settings.desired_max_eirp", + "mac_settings_profile.mac_settings.desired_max_eirp.value", + "mac_settings_profile.mac_settings.desired_ping_slot_data_rate_index", + "mac_settings_profile.mac_settings.desired_ping_slot_data_rate_index.value", + "mac_settings_profile.mac_settings.desired_ping_slot_frequency", + "mac_settings_profile.mac_settings.desired_ping_slot_frequency.value", + "mac_settings_profile.mac_settings.desired_relay", + "mac_settings_profile.mac_settings.desired_relay.mode", + "mac_settings_profile.mac_settings.desired_relay.mode.served", + "mac_settings_profile.mac_settings.desired_relay.mode.served.backoff", + "mac_settings_profile.mac_settings.desired_relay.mode.served.mode", + "mac_settings_profile.mac_settings.desired_relay.mode.served.mode.always", + "mac_settings_profile.mac_settings.desired_relay.mode.served.mode.dynamic", + "mac_settings_profile.mac_settings.desired_relay.mode.served.mode.dynamic.smart_enable_level", + "mac_settings_profile.mac_settings.desired_relay.mode.served.mode.end_device_controlled", + "mac_settings_profile.mac_settings.desired_relay.mode.served.second_channel", + "mac_settings_profile.mac_settings.desired_relay.mode.served.second_channel.ack_offset", + "mac_settings_profile.mac_settings.desired_relay.mode.served.second_channel.data_rate_index", + "mac_settings_profile.mac_settings.desired_relay.mode.served.second_channel.frequency", + "mac_settings_profile.mac_settings.desired_relay.mode.served.serving_device_id", + "mac_settings_profile.mac_settings.desired_relay.mode.serving", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.cad_periodicity", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.default_channel_index", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.limits", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.limits.join_requests", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.limits.join_requests.bucket_size", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.limits.join_requests.reload_rate", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.limits.notifications", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.limits.notifications.bucket_size", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.limits.notifications.reload_rate", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.limits.overall", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.limits.overall.bucket_size", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.limits.overall.reload_rate", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.limits.reset_behavior", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.limits.uplink_messages", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.limits.uplink_messages.bucket_size", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.limits.uplink_messages.reload_rate", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.second_channel", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.second_channel.ack_offset", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.second_channel.data_rate_index", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.second_channel.frequency", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.uplink_forwarding_rules", + "mac_settings_profile.mac_settings.desired_rx1_data_rate_offset", + "mac_settings_profile.mac_settings.desired_rx1_data_rate_offset.value", + "mac_settings_profile.mac_settings.desired_rx1_delay", + "mac_settings_profile.mac_settings.desired_rx1_delay.value", + "mac_settings_profile.mac_settings.desired_rx2_data_rate_index", + "mac_settings_profile.mac_settings.desired_rx2_data_rate_index.value", + "mac_settings_profile.mac_settings.desired_rx2_frequency", + "mac_settings_profile.mac_settings.desired_rx2_frequency.value", + "mac_settings_profile.mac_settings.downlink_dwell_time", + "mac_settings_profile.mac_settings.downlink_dwell_time.value", + "mac_settings_profile.mac_settings.factory_preset_frequencies", + "mac_settings_profile.mac_settings.max_duty_cycle", + "mac_settings_profile.mac_settings.max_duty_cycle.value", + "mac_settings_profile.mac_settings.ping_slot_data_rate_index", + "mac_settings_profile.mac_settings.ping_slot_data_rate_index.value", + "mac_settings_profile.mac_settings.ping_slot_frequency", + "mac_settings_profile.mac_settings.ping_slot_frequency.value", + "mac_settings_profile.mac_settings.ping_slot_periodicity", + "mac_settings_profile.mac_settings.ping_slot_periodicity.value", + "mac_settings_profile.mac_settings.relay", + "mac_settings_profile.mac_settings.relay.mode", + "mac_settings_profile.mac_settings.relay.mode.served", + "mac_settings_profile.mac_settings.relay.mode.served.backoff", + "mac_settings_profile.mac_settings.relay.mode.served.mode", + "mac_settings_profile.mac_settings.relay.mode.served.mode.always", + "mac_settings_profile.mac_settings.relay.mode.served.mode.dynamic", + "mac_settings_profile.mac_settings.relay.mode.served.mode.dynamic.smart_enable_level", + "mac_settings_profile.mac_settings.relay.mode.served.mode.end_device_controlled", + "mac_settings_profile.mac_settings.relay.mode.served.second_channel", + "mac_settings_profile.mac_settings.relay.mode.served.second_channel.ack_offset", + "mac_settings_profile.mac_settings.relay.mode.served.second_channel.data_rate_index", + "mac_settings_profile.mac_settings.relay.mode.served.second_channel.frequency", + "mac_settings_profile.mac_settings.relay.mode.served.serving_device_id", + "mac_settings_profile.mac_settings.relay.mode.serving", + "mac_settings_profile.mac_settings.relay.mode.serving.cad_periodicity", + "mac_settings_profile.mac_settings.relay.mode.serving.default_channel_index", + "mac_settings_profile.mac_settings.relay.mode.serving.limits", + "mac_settings_profile.mac_settings.relay.mode.serving.limits.join_requests", + "mac_settings_profile.mac_settings.relay.mode.serving.limits.join_requests.bucket_size", + "mac_settings_profile.mac_settings.relay.mode.serving.limits.join_requests.reload_rate", + "mac_settings_profile.mac_settings.relay.mode.serving.limits.notifications", + "mac_settings_profile.mac_settings.relay.mode.serving.limits.notifications.bucket_size", + "mac_settings_profile.mac_settings.relay.mode.serving.limits.notifications.reload_rate", + "mac_settings_profile.mac_settings.relay.mode.serving.limits.overall", + "mac_settings_profile.mac_settings.relay.mode.serving.limits.overall.bucket_size", + "mac_settings_profile.mac_settings.relay.mode.serving.limits.overall.reload_rate", + "mac_settings_profile.mac_settings.relay.mode.serving.limits.reset_behavior", + "mac_settings_profile.mac_settings.relay.mode.serving.limits.uplink_messages", + "mac_settings_profile.mac_settings.relay.mode.serving.limits.uplink_messages.bucket_size", + "mac_settings_profile.mac_settings.relay.mode.serving.limits.uplink_messages.reload_rate", + "mac_settings_profile.mac_settings.relay.mode.serving.second_channel", + "mac_settings_profile.mac_settings.relay.mode.serving.second_channel.ack_offset", + "mac_settings_profile.mac_settings.relay.mode.serving.second_channel.data_rate_index", + "mac_settings_profile.mac_settings.relay.mode.serving.second_channel.frequency", + "mac_settings_profile.mac_settings.relay.mode.serving.uplink_forwarding_rules", + "mac_settings_profile.mac_settings.resets_f_cnt", + "mac_settings_profile.mac_settings.resets_f_cnt.value", + "mac_settings_profile.mac_settings.rx1_data_rate_offset", + "mac_settings_profile.mac_settings.rx1_data_rate_offset.value", + "mac_settings_profile.mac_settings.rx1_delay", + "mac_settings_profile.mac_settings.rx1_delay.value", + "mac_settings_profile.mac_settings.rx2_data_rate_index", + "mac_settings_profile.mac_settings.rx2_data_rate_index.value", + "mac_settings_profile.mac_settings.rx2_frequency", + "mac_settings_profile.mac_settings.rx2_frequency.value", + "mac_settings_profile.mac_settings.schedule_downlinks", + "mac_settings_profile.mac_settings.schedule_downlinks.value", + "mac_settings_profile.mac_settings.status_count_periodicity", + "mac_settings_profile.mac_settings.status_time_periodicity", + "mac_settings_profile.mac_settings.supports_32_bit_f_cnt", + "mac_settings_profile.mac_settings.supports_32_bit_f_cnt.value", + "mac_settings_profile.mac_settings.uplink_dwell_time", + "mac_settings_profile.mac_settings.uplink_dwell_time.value", + "mac_settings_profile.mac_settings.use_adr", + "mac_settings_profile.mac_settings.use_adr.value", +} + +var CreateMACSettingsProfileResponseFieldPathsTopLevel = []string{ + "mac_settings_profile", +} +var GetMACSettingsProfileRequestFieldPathsNested = []string{ + "field_mask", + "mac_settings_profile_ids", + "mac_settings_profile_ids.application_ids", + "mac_settings_profile_ids.application_ids.application_id", + "mac_settings_profile_ids.profile_id", +} + +var GetMACSettingsProfileRequestFieldPathsTopLevel = []string{ + "field_mask", + "mac_settings_profile_ids", +} +var GetMACSettingsProfileResponseFieldPathsNested = []string{ + "mac_settings_profile", + "mac_settings_profile.ids", + "mac_settings_profile.ids.application_ids", + "mac_settings_profile.ids.application_ids.application_id", + "mac_settings_profile.ids.profile_id", + "mac_settings_profile.mac_settings", + "mac_settings_profile.mac_settings.adr", + "mac_settings_profile.mac_settings.adr.mode", + "mac_settings_profile.mac_settings.adr.mode.disabled", + "mac_settings_profile.mac_settings.adr.mode.dynamic", + "mac_settings_profile.mac_settings.adr.mode.dynamic.channel_steering", + "mac_settings_profile.mac_settings.adr.mode.dynamic.channel_steering.mode", + "mac_settings_profile.mac_settings.adr.mode.dynamic.channel_steering.mode.disabled", + "mac_settings_profile.mac_settings.adr.mode.dynamic.channel_steering.mode.lora_narrow", + "mac_settings_profile.mac_settings.adr.mode.dynamic.margin", + "mac_settings_profile.mac_settings.adr.mode.dynamic.max_data_rate_index", + "mac_settings_profile.mac_settings.adr.mode.dynamic.max_data_rate_index.value", + "mac_settings_profile.mac_settings.adr.mode.dynamic.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.max_tx_power_index", + "mac_settings_profile.mac_settings.adr.mode.dynamic.min_data_rate_index", + "mac_settings_profile.mac_settings.adr.mode.dynamic.min_data_rate_index.value", + "mac_settings_profile.mac_settings.adr.mode.dynamic.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.min_tx_power_index", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_0", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_0.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_0.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_1", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_1.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_1.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_10", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_10.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_10.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_11", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_11.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_11.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_12", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_12.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_12.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_13", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_13.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_13.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_14", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_14.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_14.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_15", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_15.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_15.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_2", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_2.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_2.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_3", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_3.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_3.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_4", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_4.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_4.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_5", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_5.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_5.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_6", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_6.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_6.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_7", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_7.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_7.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_8", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_8.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_8.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_9", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_9.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_9.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.static", + "mac_settings_profile.mac_settings.adr.mode.static.data_rate_index", + "mac_settings_profile.mac_settings.adr.mode.static.nb_trans", + "mac_settings_profile.mac_settings.adr.mode.static.tx_power_index", + "mac_settings_profile.mac_settings.adr_margin", + "mac_settings_profile.mac_settings.beacon_frequency", + "mac_settings_profile.mac_settings.beacon_frequency.value", + "mac_settings_profile.mac_settings.class_b_c_downlink_interval", + "mac_settings_profile.mac_settings.class_b_timeout", + "mac_settings_profile.mac_settings.class_c_timeout", + "mac_settings_profile.mac_settings.desired_adr_ack_delay_exponent", + "mac_settings_profile.mac_settings.desired_adr_ack_delay_exponent.value", + "mac_settings_profile.mac_settings.desired_adr_ack_limit_exponent", + "mac_settings_profile.mac_settings.desired_adr_ack_limit_exponent.value", + "mac_settings_profile.mac_settings.desired_beacon_frequency", + "mac_settings_profile.mac_settings.desired_beacon_frequency.value", + "mac_settings_profile.mac_settings.desired_max_duty_cycle", + "mac_settings_profile.mac_settings.desired_max_duty_cycle.value", + "mac_settings_profile.mac_settings.desired_max_eirp", + "mac_settings_profile.mac_settings.desired_max_eirp.value", + "mac_settings_profile.mac_settings.desired_ping_slot_data_rate_index", + "mac_settings_profile.mac_settings.desired_ping_slot_data_rate_index.value", + "mac_settings_profile.mac_settings.desired_ping_slot_frequency", + "mac_settings_profile.mac_settings.desired_ping_slot_frequency.value", + "mac_settings_profile.mac_settings.desired_relay", + "mac_settings_profile.mac_settings.desired_relay.mode", + "mac_settings_profile.mac_settings.desired_relay.mode.served", + "mac_settings_profile.mac_settings.desired_relay.mode.served.backoff", + "mac_settings_profile.mac_settings.desired_relay.mode.served.mode", + "mac_settings_profile.mac_settings.desired_relay.mode.served.mode.always", + "mac_settings_profile.mac_settings.desired_relay.mode.served.mode.dynamic", + "mac_settings_profile.mac_settings.desired_relay.mode.served.mode.dynamic.smart_enable_level", + "mac_settings_profile.mac_settings.desired_relay.mode.served.mode.end_device_controlled", + "mac_settings_profile.mac_settings.desired_relay.mode.served.second_channel", + "mac_settings_profile.mac_settings.desired_relay.mode.served.second_channel.ack_offset", + "mac_settings_profile.mac_settings.desired_relay.mode.served.second_channel.data_rate_index", + "mac_settings_profile.mac_settings.desired_relay.mode.served.second_channel.frequency", + "mac_settings_profile.mac_settings.desired_relay.mode.served.serving_device_id", + "mac_settings_profile.mac_settings.desired_relay.mode.serving", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.cad_periodicity", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.default_channel_index", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.limits", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.limits.join_requests", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.limits.join_requests.bucket_size", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.limits.join_requests.reload_rate", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.limits.notifications", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.limits.notifications.bucket_size", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.limits.notifications.reload_rate", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.limits.overall", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.limits.overall.bucket_size", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.limits.overall.reload_rate", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.limits.reset_behavior", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.limits.uplink_messages", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.limits.uplink_messages.bucket_size", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.limits.uplink_messages.reload_rate", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.second_channel", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.second_channel.ack_offset", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.second_channel.data_rate_index", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.second_channel.frequency", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.uplink_forwarding_rules", + "mac_settings_profile.mac_settings.desired_rx1_data_rate_offset", + "mac_settings_profile.mac_settings.desired_rx1_data_rate_offset.value", + "mac_settings_profile.mac_settings.desired_rx1_delay", + "mac_settings_profile.mac_settings.desired_rx1_delay.value", + "mac_settings_profile.mac_settings.desired_rx2_data_rate_index", + "mac_settings_profile.mac_settings.desired_rx2_data_rate_index.value", + "mac_settings_profile.mac_settings.desired_rx2_frequency", + "mac_settings_profile.mac_settings.desired_rx2_frequency.value", + "mac_settings_profile.mac_settings.downlink_dwell_time", + "mac_settings_profile.mac_settings.downlink_dwell_time.value", + "mac_settings_profile.mac_settings.factory_preset_frequencies", + "mac_settings_profile.mac_settings.max_duty_cycle", + "mac_settings_profile.mac_settings.max_duty_cycle.value", + "mac_settings_profile.mac_settings.ping_slot_data_rate_index", + "mac_settings_profile.mac_settings.ping_slot_data_rate_index.value", + "mac_settings_profile.mac_settings.ping_slot_frequency", + "mac_settings_profile.mac_settings.ping_slot_frequency.value", + "mac_settings_profile.mac_settings.ping_slot_periodicity", + "mac_settings_profile.mac_settings.ping_slot_periodicity.value", + "mac_settings_profile.mac_settings.relay", + "mac_settings_profile.mac_settings.relay.mode", + "mac_settings_profile.mac_settings.relay.mode.served", + "mac_settings_profile.mac_settings.relay.mode.served.backoff", + "mac_settings_profile.mac_settings.relay.mode.served.mode", + "mac_settings_profile.mac_settings.relay.mode.served.mode.always", + "mac_settings_profile.mac_settings.relay.mode.served.mode.dynamic", + "mac_settings_profile.mac_settings.relay.mode.served.mode.dynamic.smart_enable_level", + "mac_settings_profile.mac_settings.relay.mode.served.mode.end_device_controlled", + "mac_settings_profile.mac_settings.relay.mode.served.second_channel", + "mac_settings_profile.mac_settings.relay.mode.served.second_channel.ack_offset", + "mac_settings_profile.mac_settings.relay.mode.served.second_channel.data_rate_index", + "mac_settings_profile.mac_settings.relay.mode.served.second_channel.frequency", + "mac_settings_profile.mac_settings.relay.mode.served.serving_device_id", + "mac_settings_profile.mac_settings.relay.mode.serving", + "mac_settings_profile.mac_settings.relay.mode.serving.cad_periodicity", + "mac_settings_profile.mac_settings.relay.mode.serving.default_channel_index", + "mac_settings_profile.mac_settings.relay.mode.serving.limits", + "mac_settings_profile.mac_settings.relay.mode.serving.limits.join_requests", + "mac_settings_profile.mac_settings.relay.mode.serving.limits.join_requests.bucket_size", + "mac_settings_profile.mac_settings.relay.mode.serving.limits.join_requests.reload_rate", + "mac_settings_profile.mac_settings.relay.mode.serving.limits.notifications", + "mac_settings_profile.mac_settings.relay.mode.serving.limits.notifications.bucket_size", + "mac_settings_profile.mac_settings.relay.mode.serving.limits.notifications.reload_rate", + "mac_settings_profile.mac_settings.relay.mode.serving.limits.overall", + "mac_settings_profile.mac_settings.relay.mode.serving.limits.overall.bucket_size", + "mac_settings_profile.mac_settings.relay.mode.serving.limits.overall.reload_rate", + "mac_settings_profile.mac_settings.relay.mode.serving.limits.reset_behavior", + "mac_settings_profile.mac_settings.relay.mode.serving.limits.uplink_messages", + "mac_settings_profile.mac_settings.relay.mode.serving.limits.uplink_messages.bucket_size", + "mac_settings_profile.mac_settings.relay.mode.serving.limits.uplink_messages.reload_rate", + "mac_settings_profile.mac_settings.relay.mode.serving.second_channel", + "mac_settings_profile.mac_settings.relay.mode.serving.second_channel.ack_offset", + "mac_settings_profile.mac_settings.relay.mode.serving.second_channel.data_rate_index", + "mac_settings_profile.mac_settings.relay.mode.serving.second_channel.frequency", + "mac_settings_profile.mac_settings.relay.mode.serving.uplink_forwarding_rules", + "mac_settings_profile.mac_settings.resets_f_cnt", + "mac_settings_profile.mac_settings.resets_f_cnt.value", + "mac_settings_profile.mac_settings.rx1_data_rate_offset", + "mac_settings_profile.mac_settings.rx1_data_rate_offset.value", + "mac_settings_profile.mac_settings.rx1_delay", + "mac_settings_profile.mac_settings.rx1_delay.value", + "mac_settings_profile.mac_settings.rx2_data_rate_index", + "mac_settings_profile.mac_settings.rx2_data_rate_index.value", + "mac_settings_profile.mac_settings.rx2_frequency", + "mac_settings_profile.mac_settings.rx2_frequency.value", + "mac_settings_profile.mac_settings.schedule_downlinks", + "mac_settings_profile.mac_settings.schedule_downlinks.value", + "mac_settings_profile.mac_settings.status_count_periodicity", + "mac_settings_profile.mac_settings.status_time_periodicity", + "mac_settings_profile.mac_settings.supports_32_bit_f_cnt", + "mac_settings_profile.mac_settings.supports_32_bit_f_cnt.value", + "mac_settings_profile.mac_settings.uplink_dwell_time", + "mac_settings_profile.mac_settings.uplink_dwell_time.value", + "mac_settings_profile.mac_settings.use_adr", + "mac_settings_profile.mac_settings.use_adr.value", +} + +var GetMACSettingsProfileResponseFieldPathsTopLevel = []string{ + "mac_settings_profile", +} +var UpdateMACSettingsProfileRequestFieldPathsNested = []string{ + "field_mask", + "mac_settings_profile", + "mac_settings_profile.ids", + "mac_settings_profile.ids.application_ids", + "mac_settings_profile.ids.application_ids.application_id", + "mac_settings_profile.ids.profile_id", + "mac_settings_profile.mac_settings", + "mac_settings_profile.mac_settings.adr", + "mac_settings_profile.mac_settings.adr.mode", + "mac_settings_profile.mac_settings.adr.mode.disabled", + "mac_settings_profile.mac_settings.adr.mode.dynamic", + "mac_settings_profile.mac_settings.adr.mode.dynamic.channel_steering", + "mac_settings_profile.mac_settings.adr.mode.dynamic.channel_steering.mode", + "mac_settings_profile.mac_settings.adr.mode.dynamic.channel_steering.mode.disabled", + "mac_settings_profile.mac_settings.adr.mode.dynamic.channel_steering.mode.lora_narrow", + "mac_settings_profile.mac_settings.adr.mode.dynamic.margin", + "mac_settings_profile.mac_settings.adr.mode.dynamic.max_data_rate_index", + "mac_settings_profile.mac_settings.adr.mode.dynamic.max_data_rate_index.value", + "mac_settings_profile.mac_settings.adr.mode.dynamic.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.max_tx_power_index", + "mac_settings_profile.mac_settings.adr.mode.dynamic.min_data_rate_index", + "mac_settings_profile.mac_settings.adr.mode.dynamic.min_data_rate_index.value", + "mac_settings_profile.mac_settings.adr.mode.dynamic.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.min_tx_power_index", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_0", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_0.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_0.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_1", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_1.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_1.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_10", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_10.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_10.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_11", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_11.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_11.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_12", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_12.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_12.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_13", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_13.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_13.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_14", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_14.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_14.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_15", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_15.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_15.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_2", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_2.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_2.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_3", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_3.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_3.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_4", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_4.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_4.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_5", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_5.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_5.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_6", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_6.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_6.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_7", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_7.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_7.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_8", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_8.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_8.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_9", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_9.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_9.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.static", + "mac_settings_profile.mac_settings.adr.mode.static.data_rate_index", + "mac_settings_profile.mac_settings.adr.mode.static.nb_trans", + "mac_settings_profile.mac_settings.adr.mode.static.tx_power_index", + "mac_settings_profile.mac_settings.adr_margin", + "mac_settings_profile.mac_settings.beacon_frequency", + "mac_settings_profile.mac_settings.beacon_frequency.value", + "mac_settings_profile.mac_settings.class_b_c_downlink_interval", + "mac_settings_profile.mac_settings.class_b_timeout", + "mac_settings_profile.mac_settings.class_c_timeout", + "mac_settings_profile.mac_settings.desired_adr_ack_delay_exponent", + "mac_settings_profile.mac_settings.desired_adr_ack_delay_exponent.value", + "mac_settings_profile.mac_settings.desired_adr_ack_limit_exponent", + "mac_settings_profile.mac_settings.desired_adr_ack_limit_exponent.value", + "mac_settings_profile.mac_settings.desired_beacon_frequency", + "mac_settings_profile.mac_settings.desired_beacon_frequency.value", + "mac_settings_profile.mac_settings.desired_max_duty_cycle", + "mac_settings_profile.mac_settings.desired_max_duty_cycle.value", + "mac_settings_profile.mac_settings.desired_max_eirp", + "mac_settings_profile.mac_settings.desired_max_eirp.value", + "mac_settings_profile.mac_settings.desired_ping_slot_data_rate_index", + "mac_settings_profile.mac_settings.desired_ping_slot_data_rate_index.value", + "mac_settings_profile.mac_settings.desired_ping_slot_frequency", + "mac_settings_profile.mac_settings.desired_ping_slot_frequency.value", + "mac_settings_profile.mac_settings.desired_relay", + "mac_settings_profile.mac_settings.desired_relay.mode", + "mac_settings_profile.mac_settings.desired_relay.mode.served", + "mac_settings_profile.mac_settings.desired_relay.mode.served.backoff", + "mac_settings_profile.mac_settings.desired_relay.mode.served.mode", + "mac_settings_profile.mac_settings.desired_relay.mode.served.mode.always", + "mac_settings_profile.mac_settings.desired_relay.mode.served.mode.dynamic", + "mac_settings_profile.mac_settings.desired_relay.mode.served.mode.dynamic.smart_enable_level", + "mac_settings_profile.mac_settings.desired_relay.mode.served.mode.end_device_controlled", + "mac_settings_profile.mac_settings.desired_relay.mode.served.second_channel", + "mac_settings_profile.mac_settings.desired_relay.mode.served.second_channel.ack_offset", + "mac_settings_profile.mac_settings.desired_relay.mode.served.second_channel.data_rate_index", + "mac_settings_profile.mac_settings.desired_relay.mode.served.second_channel.frequency", + "mac_settings_profile.mac_settings.desired_relay.mode.served.serving_device_id", + "mac_settings_profile.mac_settings.desired_relay.mode.serving", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.cad_periodicity", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.default_channel_index", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.limits", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.limits.join_requests", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.limits.join_requests.bucket_size", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.limits.join_requests.reload_rate", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.limits.notifications", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.limits.notifications.bucket_size", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.limits.notifications.reload_rate", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.limits.overall", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.limits.overall.bucket_size", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.limits.overall.reload_rate", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.limits.reset_behavior", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.limits.uplink_messages", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.limits.uplink_messages.bucket_size", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.limits.uplink_messages.reload_rate", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.second_channel", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.second_channel.ack_offset", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.second_channel.data_rate_index", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.second_channel.frequency", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.uplink_forwarding_rules", + "mac_settings_profile.mac_settings.desired_rx1_data_rate_offset", + "mac_settings_profile.mac_settings.desired_rx1_data_rate_offset.value", + "mac_settings_profile.mac_settings.desired_rx1_delay", + "mac_settings_profile.mac_settings.desired_rx1_delay.value", + "mac_settings_profile.mac_settings.desired_rx2_data_rate_index", + "mac_settings_profile.mac_settings.desired_rx2_data_rate_index.value", + "mac_settings_profile.mac_settings.desired_rx2_frequency", + "mac_settings_profile.mac_settings.desired_rx2_frequency.value", + "mac_settings_profile.mac_settings.downlink_dwell_time", + "mac_settings_profile.mac_settings.downlink_dwell_time.value", + "mac_settings_profile.mac_settings.factory_preset_frequencies", + "mac_settings_profile.mac_settings.max_duty_cycle", + "mac_settings_profile.mac_settings.max_duty_cycle.value", + "mac_settings_profile.mac_settings.ping_slot_data_rate_index", + "mac_settings_profile.mac_settings.ping_slot_data_rate_index.value", + "mac_settings_profile.mac_settings.ping_slot_frequency", + "mac_settings_profile.mac_settings.ping_slot_frequency.value", + "mac_settings_profile.mac_settings.ping_slot_periodicity", + "mac_settings_profile.mac_settings.ping_slot_periodicity.value", + "mac_settings_profile.mac_settings.relay", + "mac_settings_profile.mac_settings.relay.mode", + "mac_settings_profile.mac_settings.relay.mode.served", + "mac_settings_profile.mac_settings.relay.mode.served.backoff", + "mac_settings_profile.mac_settings.relay.mode.served.mode", + "mac_settings_profile.mac_settings.relay.mode.served.mode.always", + "mac_settings_profile.mac_settings.relay.mode.served.mode.dynamic", + "mac_settings_profile.mac_settings.relay.mode.served.mode.dynamic.smart_enable_level", + "mac_settings_profile.mac_settings.relay.mode.served.mode.end_device_controlled", + "mac_settings_profile.mac_settings.relay.mode.served.second_channel", + "mac_settings_profile.mac_settings.relay.mode.served.second_channel.ack_offset", + "mac_settings_profile.mac_settings.relay.mode.served.second_channel.data_rate_index", + "mac_settings_profile.mac_settings.relay.mode.served.second_channel.frequency", + "mac_settings_profile.mac_settings.relay.mode.served.serving_device_id", + "mac_settings_profile.mac_settings.relay.mode.serving", + "mac_settings_profile.mac_settings.relay.mode.serving.cad_periodicity", + "mac_settings_profile.mac_settings.relay.mode.serving.default_channel_index", + "mac_settings_profile.mac_settings.relay.mode.serving.limits", + "mac_settings_profile.mac_settings.relay.mode.serving.limits.join_requests", + "mac_settings_profile.mac_settings.relay.mode.serving.limits.join_requests.bucket_size", + "mac_settings_profile.mac_settings.relay.mode.serving.limits.join_requests.reload_rate", + "mac_settings_profile.mac_settings.relay.mode.serving.limits.notifications", + "mac_settings_profile.mac_settings.relay.mode.serving.limits.notifications.bucket_size", + "mac_settings_profile.mac_settings.relay.mode.serving.limits.notifications.reload_rate", + "mac_settings_profile.mac_settings.relay.mode.serving.limits.overall", + "mac_settings_profile.mac_settings.relay.mode.serving.limits.overall.bucket_size", + "mac_settings_profile.mac_settings.relay.mode.serving.limits.overall.reload_rate", + "mac_settings_profile.mac_settings.relay.mode.serving.limits.reset_behavior", + "mac_settings_profile.mac_settings.relay.mode.serving.limits.uplink_messages", + "mac_settings_profile.mac_settings.relay.mode.serving.limits.uplink_messages.bucket_size", + "mac_settings_profile.mac_settings.relay.mode.serving.limits.uplink_messages.reload_rate", + "mac_settings_profile.mac_settings.relay.mode.serving.second_channel", + "mac_settings_profile.mac_settings.relay.mode.serving.second_channel.ack_offset", + "mac_settings_profile.mac_settings.relay.mode.serving.second_channel.data_rate_index", + "mac_settings_profile.mac_settings.relay.mode.serving.second_channel.frequency", + "mac_settings_profile.mac_settings.relay.mode.serving.uplink_forwarding_rules", + "mac_settings_profile.mac_settings.resets_f_cnt", + "mac_settings_profile.mac_settings.resets_f_cnt.value", + "mac_settings_profile.mac_settings.rx1_data_rate_offset", + "mac_settings_profile.mac_settings.rx1_data_rate_offset.value", + "mac_settings_profile.mac_settings.rx1_delay", + "mac_settings_profile.mac_settings.rx1_delay.value", + "mac_settings_profile.mac_settings.rx2_data_rate_index", + "mac_settings_profile.mac_settings.rx2_data_rate_index.value", + "mac_settings_profile.mac_settings.rx2_frequency", + "mac_settings_profile.mac_settings.rx2_frequency.value", + "mac_settings_profile.mac_settings.schedule_downlinks", + "mac_settings_profile.mac_settings.schedule_downlinks.value", + "mac_settings_profile.mac_settings.status_count_periodicity", + "mac_settings_profile.mac_settings.status_time_periodicity", + "mac_settings_profile.mac_settings.supports_32_bit_f_cnt", + "mac_settings_profile.mac_settings.supports_32_bit_f_cnt.value", + "mac_settings_profile.mac_settings.uplink_dwell_time", + "mac_settings_profile.mac_settings.uplink_dwell_time.value", + "mac_settings_profile.mac_settings.use_adr", + "mac_settings_profile.mac_settings.use_adr.value", + "mac_settings_profile_ids", + "mac_settings_profile_ids.application_ids", + "mac_settings_profile_ids.application_ids.application_id", + "mac_settings_profile_ids.profile_id", +} + +var UpdateMACSettingsProfileRequestFieldPathsTopLevel = []string{ + "field_mask", + "mac_settings_profile", + "mac_settings_profile_ids", +} +var UpdateMACSettingsProfileResponseFieldPathsNested = []string{ + "mac_settings_profile", + "mac_settings_profile.ids", + "mac_settings_profile.ids.application_ids", + "mac_settings_profile.ids.application_ids.application_id", + "mac_settings_profile.ids.profile_id", + "mac_settings_profile.mac_settings", + "mac_settings_profile.mac_settings.adr", + "mac_settings_profile.mac_settings.adr.mode", + "mac_settings_profile.mac_settings.adr.mode.disabled", + "mac_settings_profile.mac_settings.adr.mode.dynamic", + "mac_settings_profile.mac_settings.adr.mode.dynamic.channel_steering", + "mac_settings_profile.mac_settings.adr.mode.dynamic.channel_steering.mode", + "mac_settings_profile.mac_settings.adr.mode.dynamic.channel_steering.mode.disabled", + "mac_settings_profile.mac_settings.adr.mode.dynamic.channel_steering.mode.lora_narrow", + "mac_settings_profile.mac_settings.adr.mode.dynamic.margin", + "mac_settings_profile.mac_settings.adr.mode.dynamic.max_data_rate_index", + "mac_settings_profile.mac_settings.adr.mode.dynamic.max_data_rate_index.value", + "mac_settings_profile.mac_settings.adr.mode.dynamic.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.max_tx_power_index", + "mac_settings_profile.mac_settings.adr.mode.dynamic.min_data_rate_index", + "mac_settings_profile.mac_settings.adr.mode.dynamic.min_data_rate_index.value", + "mac_settings_profile.mac_settings.adr.mode.dynamic.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.min_tx_power_index", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_0", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_0.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_0.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_1", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_1.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_1.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_10", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_10.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_10.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_11", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_11.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_11.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_12", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_12.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_12.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_13", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_13.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_13.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_14", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_14.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_14.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_15", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_15.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_15.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_2", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_2.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_2.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_3", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_3.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_3.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_4", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_4.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_4.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_5", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_5.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_5.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_6", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_6.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_6.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_7", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_7.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_7.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_8", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_8.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_8.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_9", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_9.max_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.dynamic.overrides.data_rate_9.min_nb_trans", + "mac_settings_profile.mac_settings.adr.mode.static", + "mac_settings_profile.mac_settings.adr.mode.static.data_rate_index", + "mac_settings_profile.mac_settings.adr.mode.static.nb_trans", + "mac_settings_profile.mac_settings.adr.mode.static.tx_power_index", + "mac_settings_profile.mac_settings.adr_margin", + "mac_settings_profile.mac_settings.beacon_frequency", + "mac_settings_profile.mac_settings.beacon_frequency.value", + "mac_settings_profile.mac_settings.class_b_c_downlink_interval", + "mac_settings_profile.mac_settings.class_b_timeout", + "mac_settings_profile.mac_settings.class_c_timeout", + "mac_settings_profile.mac_settings.desired_adr_ack_delay_exponent", + "mac_settings_profile.mac_settings.desired_adr_ack_delay_exponent.value", + "mac_settings_profile.mac_settings.desired_adr_ack_limit_exponent", + "mac_settings_profile.mac_settings.desired_adr_ack_limit_exponent.value", + "mac_settings_profile.mac_settings.desired_beacon_frequency", + "mac_settings_profile.mac_settings.desired_beacon_frequency.value", + "mac_settings_profile.mac_settings.desired_max_duty_cycle", + "mac_settings_profile.mac_settings.desired_max_duty_cycle.value", + "mac_settings_profile.mac_settings.desired_max_eirp", + "mac_settings_profile.mac_settings.desired_max_eirp.value", + "mac_settings_profile.mac_settings.desired_ping_slot_data_rate_index", + "mac_settings_profile.mac_settings.desired_ping_slot_data_rate_index.value", + "mac_settings_profile.mac_settings.desired_ping_slot_frequency", + "mac_settings_profile.mac_settings.desired_ping_slot_frequency.value", + "mac_settings_profile.mac_settings.desired_relay", + "mac_settings_profile.mac_settings.desired_relay.mode", + "mac_settings_profile.mac_settings.desired_relay.mode.served", + "mac_settings_profile.mac_settings.desired_relay.mode.served.backoff", + "mac_settings_profile.mac_settings.desired_relay.mode.served.mode", + "mac_settings_profile.mac_settings.desired_relay.mode.served.mode.always", + "mac_settings_profile.mac_settings.desired_relay.mode.served.mode.dynamic", + "mac_settings_profile.mac_settings.desired_relay.mode.served.mode.dynamic.smart_enable_level", + "mac_settings_profile.mac_settings.desired_relay.mode.served.mode.end_device_controlled", + "mac_settings_profile.mac_settings.desired_relay.mode.served.second_channel", + "mac_settings_profile.mac_settings.desired_relay.mode.served.second_channel.ack_offset", + "mac_settings_profile.mac_settings.desired_relay.mode.served.second_channel.data_rate_index", + "mac_settings_profile.mac_settings.desired_relay.mode.served.second_channel.frequency", + "mac_settings_profile.mac_settings.desired_relay.mode.served.serving_device_id", + "mac_settings_profile.mac_settings.desired_relay.mode.serving", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.cad_periodicity", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.default_channel_index", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.limits", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.limits.join_requests", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.limits.join_requests.bucket_size", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.limits.join_requests.reload_rate", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.limits.notifications", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.limits.notifications.bucket_size", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.limits.notifications.reload_rate", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.limits.overall", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.limits.overall.bucket_size", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.limits.overall.reload_rate", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.limits.reset_behavior", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.limits.uplink_messages", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.limits.uplink_messages.bucket_size", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.limits.uplink_messages.reload_rate", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.second_channel", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.second_channel.ack_offset", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.second_channel.data_rate_index", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.second_channel.frequency", + "mac_settings_profile.mac_settings.desired_relay.mode.serving.uplink_forwarding_rules", + "mac_settings_profile.mac_settings.desired_rx1_data_rate_offset", + "mac_settings_profile.mac_settings.desired_rx1_data_rate_offset.value", + "mac_settings_profile.mac_settings.desired_rx1_delay", + "mac_settings_profile.mac_settings.desired_rx1_delay.value", + "mac_settings_profile.mac_settings.desired_rx2_data_rate_index", + "mac_settings_profile.mac_settings.desired_rx2_data_rate_index.value", + "mac_settings_profile.mac_settings.desired_rx2_frequency", + "mac_settings_profile.mac_settings.desired_rx2_frequency.value", + "mac_settings_profile.mac_settings.downlink_dwell_time", + "mac_settings_profile.mac_settings.downlink_dwell_time.value", + "mac_settings_profile.mac_settings.factory_preset_frequencies", + "mac_settings_profile.mac_settings.max_duty_cycle", + "mac_settings_profile.mac_settings.max_duty_cycle.value", + "mac_settings_profile.mac_settings.ping_slot_data_rate_index", + "mac_settings_profile.mac_settings.ping_slot_data_rate_index.value", + "mac_settings_profile.mac_settings.ping_slot_frequency", + "mac_settings_profile.mac_settings.ping_slot_frequency.value", + "mac_settings_profile.mac_settings.ping_slot_periodicity", + "mac_settings_profile.mac_settings.ping_slot_periodicity.value", + "mac_settings_profile.mac_settings.relay", + "mac_settings_profile.mac_settings.relay.mode", + "mac_settings_profile.mac_settings.relay.mode.served", + "mac_settings_profile.mac_settings.relay.mode.served.backoff", + "mac_settings_profile.mac_settings.relay.mode.served.mode", + "mac_settings_profile.mac_settings.relay.mode.served.mode.always", + "mac_settings_profile.mac_settings.relay.mode.served.mode.dynamic", + "mac_settings_profile.mac_settings.relay.mode.served.mode.dynamic.smart_enable_level", + "mac_settings_profile.mac_settings.relay.mode.served.mode.end_device_controlled", + "mac_settings_profile.mac_settings.relay.mode.served.second_channel", + "mac_settings_profile.mac_settings.relay.mode.served.second_channel.ack_offset", + "mac_settings_profile.mac_settings.relay.mode.served.second_channel.data_rate_index", + "mac_settings_profile.mac_settings.relay.mode.served.second_channel.frequency", + "mac_settings_profile.mac_settings.relay.mode.served.serving_device_id", + "mac_settings_profile.mac_settings.relay.mode.serving", + "mac_settings_profile.mac_settings.relay.mode.serving.cad_periodicity", + "mac_settings_profile.mac_settings.relay.mode.serving.default_channel_index", + "mac_settings_profile.mac_settings.relay.mode.serving.limits", + "mac_settings_profile.mac_settings.relay.mode.serving.limits.join_requests", + "mac_settings_profile.mac_settings.relay.mode.serving.limits.join_requests.bucket_size", + "mac_settings_profile.mac_settings.relay.mode.serving.limits.join_requests.reload_rate", + "mac_settings_profile.mac_settings.relay.mode.serving.limits.notifications", + "mac_settings_profile.mac_settings.relay.mode.serving.limits.notifications.bucket_size", + "mac_settings_profile.mac_settings.relay.mode.serving.limits.notifications.reload_rate", + "mac_settings_profile.mac_settings.relay.mode.serving.limits.overall", + "mac_settings_profile.mac_settings.relay.mode.serving.limits.overall.bucket_size", + "mac_settings_profile.mac_settings.relay.mode.serving.limits.overall.reload_rate", + "mac_settings_profile.mac_settings.relay.mode.serving.limits.reset_behavior", + "mac_settings_profile.mac_settings.relay.mode.serving.limits.uplink_messages", + "mac_settings_profile.mac_settings.relay.mode.serving.limits.uplink_messages.bucket_size", + "mac_settings_profile.mac_settings.relay.mode.serving.limits.uplink_messages.reload_rate", + "mac_settings_profile.mac_settings.relay.mode.serving.second_channel", + "mac_settings_profile.mac_settings.relay.mode.serving.second_channel.ack_offset", + "mac_settings_profile.mac_settings.relay.mode.serving.second_channel.data_rate_index", + "mac_settings_profile.mac_settings.relay.mode.serving.second_channel.frequency", + "mac_settings_profile.mac_settings.relay.mode.serving.uplink_forwarding_rules", + "mac_settings_profile.mac_settings.resets_f_cnt", + "mac_settings_profile.mac_settings.resets_f_cnt.value", + "mac_settings_profile.mac_settings.rx1_data_rate_offset", + "mac_settings_profile.mac_settings.rx1_data_rate_offset.value", + "mac_settings_profile.mac_settings.rx1_delay", + "mac_settings_profile.mac_settings.rx1_delay.value", + "mac_settings_profile.mac_settings.rx2_data_rate_index", + "mac_settings_profile.mac_settings.rx2_data_rate_index.value", + "mac_settings_profile.mac_settings.rx2_frequency", + "mac_settings_profile.mac_settings.rx2_frequency.value", + "mac_settings_profile.mac_settings.schedule_downlinks", + "mac_settings_profile.mac_settings.schedule_downlinks.value", + "mac_settings_profile.mac_settings.status_count_periodicity", + "mac_settings_profile.mac_settings.status_time_periodicity", + "mac_settings_profile.mac_settings.supports_32_bit_f_cnt", + "mac_settings_profile.mac_settings.supports_32_bit_f_cnt.value", + "mac_settings_profile.mac_settings.uplink_dwell_time", + "mac_settings_profile.mac_settings.uplink_dwell_time.value", + "mac_settings_profile.mac_settings.use_adr", + "mac_settings_profile.mac_settings.use_adr.value", +} + +var UpdateMACSettingsProfileResponseFieldPathsTopLevel = []string{ + "mac_settings_profile", +} +var DeleteMACSettingsProfileRequestFieldPathsNested = []string{ + "mac_settings_profile_ids", + "mac_settings_profile_ids.application_ids", + "mac_settings_profile_ids.application_ids.application_id", + "mac_settings_profile_ids.profile_id", +} + +var DeleteMACSettingsProfileRequestFieldPathsTopLevel = []string{ + "mac_settings_profile_ids", +} +var DeleteMACSettingsProfileResponseFieldPathsNested []string +var DeleteMACSettingsProfileResponseFieldPathsTopLevel []string +var ListMACSettingsProfilesRequestFieldPathsNested = []string{ + "application_ids", + "application_ids.application_id", + "field_mask", + "limit", + "order", + "page", +} + +var ListMACSettingsProfilesRequestFieldPathsTopLevel = []string{ + "application_ids", + "field_mask", + "limit", + "order", + "page", +} +var ListMACSettingsProfilesResponseFieldPathsNested = []string{ + "mac_settings_profiles", +} + +var ListMACSettingsProfilesResponseFieldPathsTopLevel = []string{ + "mac_settings_profiles", +} diff --git a/pkg/ttnpb/networkserver_mac_settings_profile.pb.setters.fm.go b/pkg/ttnpb/networkserver_mac_settings_profile.pb.setters.fm.go new file mode 100644 index 0000000000..34c20f13a0 --- /dev/null +++ b/pkg/ttnpb/networkserver_mac_settings_profile.pb.setters.fm.go @@ -0,0 +1,402 @@ +// Code generated by protoc-gen-fieldmask. DO NOT EDIT. + +package ttnpb + +import fmt "fmt" + +func (dst *CreateMACSettingsProfileRequest) SetFields(src *CreateMACSettingsProfileRequest, paths ...string) error { + for name, subs := range _processPaths(paths) { + switch name { + case "mac_settings_profile": + if len(subs) > 0 { + var newDst, newSrc *MACSettingsProfile + if (src == nil || src.MacSettingsProfile == nil) && dst.MacSettingsProfile == nil { + continue + } + if src != nil { + newSrc = src.MacSettingsProfile + } + if dst.MacSettingsProfile != nil { + newDst = dst.MacSettingsProfile + } else { + newDst = &MACSettingsProfile{} + dst.MacSettingsProfile = newDst + } + if err := newDst.SetFields(newSrc, subs...); err != nil { + return err + } + } else { + if src != nil { + dst.MacSettingsProfile = src.MacSettingsProfile + } else { + dst.MacSettingsProfile = nil + } + } + + default: + return fmt.Errorf("invalid field: '%s'", name) + } + } + return nil +} + +func (dst *CreateMACSettingsProfileResponse) SetFields(src *CreateMACSettingsProfileResponse, paths ...string) error { + for name, subs := range _processPaths(paths) { + switch name { + case "mac_settings_profile": + if len(subs) > 0 { + var newDst, newSrc *MACSettingsProfile + if (src == nil || src.MacSettingsProfile == nil) && dst.MacSettingsProfile == nil { + continue + } + if src != nil { + newSrc = src.MacSettingsProfile + } + if dst.MacSettingsProfile != nil { + newDst = dst.MacSettingsProfile + } else { + newDst = &MACSettingsProfile{} + dst.MacSettingsProfile = newDst + } + if err := newDst.SetFields(newSrc, subs...); err != nil { + return err + } + } else { + if src != nil { + dst.MacSettingsProfile = src.MacSettingsProfile + } else { + dst.MacSettingsProfile = nil + } + } + + default: + return fmt.Errorf("invalid field: '%s'", name) + } + } + return nil +} + +func (dst *GetMACSettingsProfileRequest) SetFields(src *GetMACSettingsProfileRequest, paths ...string) error { + for name, subs := range _processPaths(paths) { + switch name { + case "mac_settings_profile_ids": + if len(subs) > 0 { + var newDst, newSrc *MACSettingsProfileIdentifiers + if (src == nil || src.MacSettingsProfileIds == nil) && dst.MacSettingsProfileIds == nil { + continue + } + if src != nil { + newSrc = src.MacSettingsProfileIds + } + if dst.MacSettingsProfileIds != nil { + newDst = dst.MacSettingsProfileIds + } else { + newDst = &MACSettingsProfileIdentifiers{} + dst.MacSettingsProfileIds = newDst + } + if err := newDst.SetFields(newSrc, subs...); err != nil { + return err + } + } else { + if src != nil { + dst.MacSettingsProfileIds = src.MacSettingsProfileIds + } else { + dst.MacSettingsProfileIds = nil + } + } + case "field_mask": + if len(subs) > 0 { + return fmt.Errorf("'field_mask' has no subfields, but %s were specified", subs) + } + if src != nil { + dst.FieldMask = src.FieldMask + } else { + dst.FieldMask = nil + } + + default: + return fmt.Errorf("invalid field: '%s'", name) + } + } + return nil +} + +func (dst *GetMACSettingsProfileResponse) SetFields(src *GetMACSettingsProfileResponse, paths ...string) error { + for name, subs := range _processPaths(paths) { + switch name { + case "mac_settings_profile": + if len(subs) > 0 { + var newDst, newSrc *MACSettingsProfile + if (src == nil || src.MacSettingsProfile == nil) && dst.MacSettingsProfile == nil { + continue + } + if src != nil { + newSrc = src.MacSettingsProfile + } + if dst.MacSettingsProfile != nil { + newDst = dst.MacSettingsProfile + } else { + newDst = &MACSettingsProfile{} + dst.MacSettingsProfile = newDst + } + if err := newDst.SetFields(newSrc, subs...); err != nil { + return err + } + } else { + if src != nil { + dst.MacSettingsProfile = src.MacSettingsProfile + } else { + dst.MacSettingsProfile = nil + } + } + + default: + return fmt.Errorf("invalid field: '%s'", name) + } + } + return nil +} + +func (dst *UpdateMACSettingsProfileRequest) SetFields(src *UpdateMACSettingsProfileRequest, paths ...string) error { + for name, subs := range _processPaths(paths) { + switch name { + case "mac_settings_profile_ids": + if len(subs) > 0 { + var newDst, newSrc *MACSettingsProfileIdentifiers + if (src == nil || src.MacSettingsProfileIds == nil) && dst.MacSettingsProfileIds == nil { + continue + } + if src != nil { + newSrc = src.MacSettingsProfileIds + } + if dst.MacSettingsProfileIds != nil { + newDst = dst.MacSettingsProfileIds + } else { + newDst = &MACSettingsProfileIdentifiers{} + dst.MacSettingsProfileIds = newDst + } + if err := newDst.SetFields(newSrc, subs...); err != nil { + return err + } + } else { + if src != nil { + dst.MacSettingsProfileIds = src.MacSettingsProfileIds + } else { + dst.MacSettingsProfileIds = nil + } + } + case "mac_settings_profile": + if len(subs) > 0 { + var newDst, newSrc *MACSettingsProfile + if (src == nil || src.MacSettingsProfile == nil) && dst.MacSettingsProfile == nil { + continue + } + if src != nil { + newSrc = src.MacSettingsProfile + } + if dst.MacSettingsProfile != nil { + newDst = dst.MacSettingsProfile + } else { + newDst = &MACSettingsProfile{} + dst.MacSettingsProfile = newDst + } + if err := newDst.SetFields(newSrc, subs...); err != nil { + return err + } + } else { + if src != nil { + dst.MacSettingsProfile = src.MacSettingsProfile + } else { + dst.MacSettingsProfile = nil + } + } + case "field_mask": + if len(subs) > 0 { + return fmt.Errorf("'field_mask' has no subfields, but %s were specified", subs) + } + if src != nil { + dst.FieldMask = src.FieldMask + } else { + dst.FieldMask = nil + } + + default: + return fmt.Errorf("invalid field: '%s'", name) + } + } + return nil +} + +func (dst *UpdateMACSettingsProfileResponse) SetFields(src *UpdateMACSettingsProfileResponse, paths ...string) error { + for name, subs := range _processPaths(paths) { + switch name { + case "mac_settings_profile": + if len(subs) > 0 { + var newDst, newSrc *MACSettingsProfile + if (src == nil || src.MacSettingsProfile == nil) && dst.MacSettingsProfile == nil { + continue + } + if src != nil { + newSrc = src.MacSettingsProfile + } + if dst.MacSettingsProfile != nil { + newDst = dst.MacSettingsProfile + } else { + newDst = &MACSettingsProfile{} + dst.MacSettingsProfile = newDst + } + if err := newDst.SetFields(newSrc, subs...); err != nil { + return err + } + } else { + if src != nil { + dst.MacSettingsProfile = src.MacSettingsProfile + } else { + dst.MacSettingsProfile = nil + } + } + + default: + return fmt.Errorf("invalid field: '%s'", name) + } + } + return nil +} + +func (dst *DeleteMACSettingsProfileRequest) SetFields(src *DeleteMACSettingsProfileRequest, paths ...string) error { + for name, subs := range _processPaths(paths) { + switch name { + case "mac_settings_profile_ids": + if len(subs) > 0 { + var newDst, newSrc *MACSettingsProfileIdentifiers + if (src == nil || src.MacSettingsProfileIds == nil) && dst.MacSettingsProfileIds == nil { + continue + } + if src != nil { + newSrc = src.MacSettingsProfileIds + } + if dst.MacSettingsProfileIds != nil { + newDst = dst.MacSettingsProfileIds + } else { + newDst = &MACSettingsProfileIdentifiers{} + dst.MacSettingsProfileIds = newDst + } + if err := newDst.SetFields(newSrc, subs...); err != nil { + return err + } + } else { + if src != nil { + dst.MacSettingsProfileIds = src.MacSettingsProfileIds + } else { + dst.MacSettingsProfileIds = nil + } + } + + default: + return fmt.Errorf("invalid field: '%s'", name) + } + } + return nil +} + +func (dst *DeleteMACSettingsProfileResponse) SetFields(src *DeleteMACSettingsProfileResponse, paths ...string) error { + if len(paths) != 0 { + return fmt.Errorf("message DeleteMACSettingsProfileResponse has no fields, but paths %s were specified", paths) + } + return nil +} + +func (dst *ListMACSettingsProfilesRequest) SetFields(src *ListMACSettingsProfilesRequest, paths ...string) error { + for name, subs := range _processPaths(paths) { + switch name { + case "application_ids": + if len(subs) > 0 { + var newDst, newSrc *ApplicationIdentifiers + if (src == nil || src.ApplicationIds == nil) && dst.ApplicationIds == nil { + continue + } + if src != nil { + newSrc = src.ApplicationIds + } + if dst.ApplicationIds != nil { + newDst = dst.ApplicationIds + } else { + newDst = &ApplicationIdentifiers{} + dst.ApplicationIds = newDst + } + if err := newDst.SetFields(newSrc, subs...); err != nil { + return err + } + } else { + if src != nil { + dst.ApplicationIds = src.ApplicationIds + } else { + dst.ApplicationIds = nil + } + } + case "field_mask": + if len(subs) > 0 { + return fmt.Errorf("'field_mask' has no subfields, but %s were specified", subs) + } + if src != nil { + dst.FieldMask = src.FieldMask + } else { + dst.FieldMask = nil + } + case "order": + if len(subs) > 0 { + return fmt.Errorf("'order' has no subfields, but %s were specified", subs) + } + if src != nil { + dst.Order = src.Order + } else { + var zero string + dst.Order = zero + } + case "limit": + if len(subs) > 0 { + return fmt.Errorf("'limit' has no subfields, but %s were specified", subs) + } + if src != nil { + dst.Limit = src.Limit + } else { + var zero uint32 + dst.Limit = zero + } + case "page": + if len(subs) > 0 { + return fmt.Errorf("'page' has no subfields, but %s were specified", subs) + } + if src != nil { + dst.Page = src.Page + } else { + var zero uint32 + dst.Page = zero + } + + default: + return fmt.Errorf("invalid field: '%s'", name) + } + } + return nil +} + +func (dst *ListMACSettingsProfilesResponse) SetFields(src *ListMACSettingsProfilesResponse, paths ...string) error { + for name, subs := range _processPaths(paths) { + switch name { + case "mac_settings_profiles": + if len(subs) > 0 { + return fmt.Errorf("'mac_settings_profiles' has no subfields, but %s were specified", subs) + } + if src != nil { + dst.MacSettingsProfiles = src.MacSettingsProfiles + } else { + dst.MacSettingsProfiles = nil + } + + default: + return fmt.Errorf("invalid field: '%s'", name) + } + } + return nil +} diff --git a/pkg/ttnpb/networkserver_mac_settings_profile.pb.validate.go b/pkg/ttnpb/networkserver_mac_settings_profile.pb.validate.go new file mode 100644 index 0000000000..46a9af69af --- /dev/null +++ b/pkg/ttnpb/networkserver_mac_settings_profile.pb.validate.go @@ -0,0 +1,1088 @@ +// Code generated by protoc-gen-fieldmask. DO NOT EDIT. + +package ttnpb + +import ( + "bytes" + "errors" + "fmt" + "net" + "net/mail" + "net/url" + "regexp" + "strings" + "time" + "unicode/utf8" + + "google.golang.org/protobuf/types/known/anypb" +) + +// ensure the imports are used +var ( + _ = bytes.MinRead + _ = errors.New("") + _ = fmt.Print + _ = utf8.UTFMax + _ = (*regexp.Regexp)(nil) + _ = (*strings.Reader)(nil) + _ = net.IPv4len + _ = time.Duration(0) + _ = (*url.URL)(nil) + _ = (*mail.Address)(nil) + _ = anypb.Any{} +) + +// ValidateFields checks the field values on CreateMACSettingsProfileRequest +// with the rules defined in the proto definition for this message. If any +// rules are violated, an error is returned. +func (m *CreateMACSettingsProfileRequest) ValidateFields(paths ...string) error { + if m == nil { + return nil + } + + if len(paths) == 0 { + paths = CreateMACSettingsProfileRequestFieldPathsNested + } + + for name, subs := range _processPaths(append(paths[:0:0], paths...)) { + _ = subs + switch name { + case "mac_settings_profile": + + if m.GetMacSettingsProfile() == nil { + return CreateMACSettingsProfileRequestValidationError{ + field: "mac_settings_profile", + reason: "value is required", + } + } + + if v, ok := interface{}(m.GetMacSettingsProfile()).(interface{ ValidateFields(...string) error }); ok { + if err := v.ValidateFields(subs...); err != nil { + return CreateMACSettingsProfileRequestValidationError{ + field: "mac_settings_profile", + reason: "embedded message failed validation", + cause: err, + } + } + } + + default: + return CreateMACSettingsProfileRequestValidationError{ + field: name, + reason: "invalid field path", + } + } + } + return nil +} + +// CreateMACSettingsProfileRequestValidationError is the validation error +// returned by CreateMACSettingsProfileRequest.ValidateFields if the +// designated constraints aren't met. +type CreateMACSettingsProfileRequestValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e CreateMACSettingsProfileRequestValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e CreateMACSettingsProfileRequestValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e CreateMACSettingsProfileRequestValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e CreateMACSettingsProfileRequestValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e CreateMACSettingsProfileRequestValidationError) ErrorName() string { + return "CreateMACSettingsProfileRequestValidationError" +} + +// Error satisfies the builtin error interface +func (e CreateMACSettingsProfileRequestValidationError) Error() string { + cause := "" + if e.cause != nil { + cause = fmt.Sprintf(" | caused by: %v", e.cause) + } + + key := "" + if e.key { + key = "key for " + } + + return fmt.Sprintf( + "invalid %sCreateMACSettingsProfileRequest.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = CreateMACSettingsProfileRequestValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = CreateMACSettingsProfileRequestValidationError{} + +// ValidateFields checks the field values on CreateMACSettingsProfileResponse +// with the rules defined in the proto definition for this message. If any +// rules are violated, an error is returned. +func (m *CreateMACSettingsProfileResponse) ValidateFields(paths ...string) error { + if m == nil { + return nil + } + + if len(paths) == 0 { + paths = CreateMACSettingsProfileResponseFieldPathsNested + } + + for name, subs := range _processPaths(append(paths[:0:0], paths...)) { + _ = subs + switch name { + case "mac_settings_profile": + + if m.GetMacSettingsProfile() == nil { + return CreateMACSettingsProfileResponseValidationError{ + field: "mac_settings_profile", + reason: "value is required", + } + } + + if v, ok := interface{}(m.GetMacSettingsProfile()).(interface{ ValidateFields(...string) error }); ok { + if err := v.ValidateFields(subs...); err != nil { + return CreateMACSettingsProfileResponseValidationError{ + field: "mac_settings_profile", + reason: "embedded message failed validation", + cause: err, + } + } + } + + default: + return CreateMACSettingsProfileResponseValidationError{ + field: name, + reason: "invalid field path", + } + } + } + return nil +} + +// CreateMACSettingsProfileResponseValidationError is the validation error +// returned by CreateMACSettingsProfileResponse.ValidateFields if the +// designated constraints aren't met. +type CreateMACSettingsProfileResponseValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e CreateMACSettingsProfileResponseValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e CreateMACSettingsProfileResponseValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e CreateMACSettingsProfileResponseValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e CreateMACSettingsProfileResponseValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e CreateMACSettingsProfileResponseValidationError) ErrorName() string { + return "CreateMACSettingsProfileResponseValidationError" +} + +// Error satisfies the builtin error interface +func (e CreateMACSettingsProfileResponseValidationError) Error() string { + cause := "" + if e.cause != nil { + cause = fmt.Sprintf(" | caused by: %v", e.cause) + } + + key := "" + if e.key { + key = "key for " + } + + return fmt.Sprintf( + "invalid %sCreateMACSettingsProfileResponse.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = CreateMACSettingsProfileResponseValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = CreateMACSettingsProfileResponseValidationError{} + +// ValidateFields checks the field values on GetMACSettingsProfileRequest with +// the rules defined in the proto definition for this message. If any rules +// are violated, an error is returned. +func (m *GetMACSettingsProfileRequest) ValidateFields(paths ...string) error { + if m == nil { + return nil + } + + if len(paths) == 0 { + paths = GetMACSettingsProfileRequestFieldPathsNested + } + + for name, subs := range _processPaths(append(paths[:0:0], paths...)) { + _ = subs + switch name { + case "mac_settings_profile_ids": + + if m.GetMacSettingsProfileIds() == nil { + return GetMACSettingsProfileRequestValidationError{ + field: "mac_settings_profile_ids", + reason: "value is required", + } + } + + if v, ok := interface{}(m.GetMacSettingsProfileIds()).(interface{ ValidateFields(...string) error }); ok { + if err := v.ValidateFields(subs...); err != nil { + return GetMACSettingsProfileRequestValidationError{ + field: "mac_settings_profile_ids", + reason: "embedded message failed validation", + cause: err, + } + } + } + + case "field_mask": + + if v, ok := interface{}(m.GetFieldMask()).(interface{ ValidateFields(...string) error }); ok { + if err := v.ValidateFields(subs...); err != nil { + return GetMACSettingsProfileRequestValidationError{ + field: "field_mask", + reason: "embedded message failed validation", + cause: err, + } + } + } + + default: + return GetMACSettingsProfileRequestValidationError{ + field: name, + reason: "invalid field path", + } + } + } + return nil +} + +// GetMACSettingsProfileRequestValidationError is the validation error returned +// by GetMACSettingsProfileRequest.ValidateFields if the designated +// constraints aren't met. +type GetMACSettingsProfileRequestValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e GetMACSettingsProfileRequestValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e GetMACSettingsProfileRequestValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e GetMACSettingsProfileRequestValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e GetMACSettingsProfileRequestValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e GetMACSettingsProfileRequestValidationError) ErrorName() string { + return "GetMACSettingsProfileRequestValidationError" +} + +// Error satisfies the builtin error interface +func (e GetMACSettingsProfileRequestValidationError) Error() string { + cause := "" + if e.cause != nil { + cause = fmt.Sprintf(" | caused by: %v", e.cause) + } + + key := "" + if e.key { + key = "key for " + } + + return fmt.Sprintf( + "invalid %sGetMACSettingsProfileRequest.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = GetMACSettingsProfileRequestValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = GetMACSettingsProfileRequestValidationError{} + +// ValidateFields checks the field values on GetMACSettingsProfileResponse with +// the rules defined in the proto definition for this message. If any rules +// are violated, an error is returned. +func (m *GetMACSettingsProfileResponse) ValidateFields(paths ...string) error { + if m == nil { + return nil + } + + if len(paths) == 0 { + paths = GetMACSettingsProfileResponseFieldPathsNested + } + + for name, subs := range _processPaths(append(paths[:0:0], paths...)) { + _ = subs + switch name { + case "mac_settings_profile": + + if m.GetMacSettingsProfile() == nil { + return GetMACSettingsProfileResponseValidationError{ + field: "mac_settings_profile", + reason: "value is required", + } + } + + if v, ok := interface{}(m.GetMacSettingsProfile()).(interface{ ValidateFields(...string) error }); ok { + if err := v.ValidateFields(subs...); err != nil { + return GetMACSettingsProfileResponseValidationError{ + field: "mac_settings_profile", + reason: "embedded message failed validation", + cause: err, + } + } + } + + default: + return GetMACSettingsProfileResponseValidationError{ + field: name, + reason: "invalid field path", + } + } + } + return nil +} + +// GetMACSettingsProfileResponseValidationError is the validation error +// returned by GetMACSettingsProfileResponse.ValidateFields if the designated +// constraints aren't met. +type GetMACSettingsProfileResponseValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e GetMACSettingsProfileResponseValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e GetMACSettingsProfileResponseValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e GetMACSettingsProfileResponseValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e GetMACSettingsProfileResponseValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e GetMACSettingsProfileResponseValidationError) ErrorName() string { + return "GetMACSettingsProfileResponseValidationError" +} + +// Error satisfies the builtin error interface +func (e GetMACSettingsProfileResponseValidationError) Error() string { + cause := "" + if e.cause != nil { + cause = fmt.Sprintf(" | caused by: %v", e.cause) + } + + key := "" + if e.key { + key = "key for " + } + + return fmt.Sprintf( + "invalid %sGetMACSettingsProfileResponse.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = GetMACSettingsProfileResponseValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = GetMACSettingsProfileResponseValidationError{} + +// ValidateFields checks the field values on UpdateMACSettingsProfileRequest +// with the rules defined in the proto definition for this message. If any +// rules are violated, an error is returned. +func (m *UpdateMACSettingsProfileRequest) ValidateFields(paths ...string) error { + if m == nil { + return nil + } + + if len(paths) == 0 { + paths = UpdateMACSettingsProfileRequestFieldPathsNested + } + + for name, subs := range _processPaths(append(paths[:0:0], paths...)) { + _ = subs + switch name { + case "mac_settings_profile_ids": + + if m.GetMacSettingsProfileIds() == nil { + return UpdateMACSettingsProfileRequestValidationError{ + field: "mac_settings_profile_ids", + reason: "value is required", + } + } + + if v, ok := interface{}(m.GetMacSettingsProfileIds()).(interface{ ValidateFields(...string) error }); ok { + if err := v.ValidateFields(subs...); err != nil { + return UpdateMACSettingsProfileRequestValidationError{ + field: "mac_settings_profile_ids", + reason: "embedded message failed validation", + cause: err, + } + } + } + + case "mac_settings_profile": + + if m.GetMacSettingsProfile() == nil { + return UpdateMACSettingsProfileRequestValidationError{ + field: "mac_settings_profile", + reason: "value is required", + } + } + + if v, ok := interface{}(m.GetMacSettingsProfile()).(interface{ ValidateFields(...string) error }); ok { + if err := v.ValidateFields(subs...); err != nil { + return UpdateMACSettingsProfileRequestValidationError{ + field: "mac_settings_profile", + reason: "embedded message failed validation", + cause: err, + } + } + } + + case "field_mask": + + if v, ok := interface{}(m.GetFieldMask()).(interface{ ValidateFields(...string) error }); ok { + if err := v.ValidateFields(subs...); err != nil { + return UpdateMACSettingsProfileRequestValidationError{ + field: "field_mask", + reason: "embedded message failed validation", + cause: err, + } + } + } + + default: + return UpdateMACSettingsProfileRequestValidationError{ + field: name, + reason: "invalid field path", + } + } + } + return nil +} + +// UpdateMACSettingsProfileRequestValidationError is the validation error +// returned by UpdateMACSettingsProfileRequest.ValidateFields if the +// designated constraints aren't met. +type UpdateMACSettingsProfileRequestValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e UpdateMACSettingsProfileRequestValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e UpdateMACSettingsProfileRequestValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e UpdateMACSettingsProfileRequestValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e UpdateMACSettingsProfileRequestValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e UpdateMACSettingsProfileRequestValidationError) ErrorName() string { + return "UpdateMACSettingsProfileRequestValidationError" +} + +// Error satisfies the builtin error interface +func (e UpdateMACSettingsProfileRequestValidationError) Error() string { + cause := "" + if e.cause != nil { + cause = fmt.Sprintf(" | caused by: %v", e.cause) + } + + key := "" + if e.key { + key = "key for " + } + + return fmt.Sprintf( + "invalid %sUpdateMACSettingsProfileRequest.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = UpdateMACSettingsProfileRequestValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = UpdateMACSettingsProfileRequestValidationError{} + +// ValidateFields checks the field values on UpdateMACSettingsProfileResponse +// with the rules defined in the proto definition for this message. If any +// rules are violated, an error is returned. +func (m *UpdateMACSettingsProfileResponse) ValidateFields(paths ...string) error { + if m == nil { + return nil + } + + if len(paths) == 0 { + paths = UpdateMACSettingsProfileResponseFieldPathsNested + } + + for name, subs := range _processPaths(append(paths[:0:0], paths...)) { + _ = subs + switch name { + case "mac_settings_profile": + + if m.GetMacSettingsProfile() == nil { + return UpdateMACSettingsProfileResponseValidationError{ + field: "mac_settings_profile", + reason: "value is required", + } + } + + if v, ok := interface{}(m.GetMacSettingsProfile()).(interface{ ValidateFields(...string) error }); ok { + if err := v.ValidateFields(subs...); err != nil { + return UpdateMACSettingsProfileResponseValidationError{ + field: "mac_settings_profile", + reason: "embedded message failed validation", + cause: err, + } + } + } + + default: + return UpdateMACSettingsProfileResponseValidationError{ + field: name, + reason: "invalid field path", + } + } + } + return nil +} + +// UpdateMACSettingsProfileResponseValidationError is the validation error +// returned by UpdateMACSettingsProfileResponse.ValidateFields if the +// designated constraints aren't met. +type UpdateMACSettingsProfileResponseValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e UpdateMACSettingsProfileResponseValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e UpdateMACSettingsProfileResponseValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e UpdateMACSettingsProfileResponseValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e UpdateMACSettingsProfileResponseValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e UpdateMACSettingsProfileResponseValidationError) ErrorName() string { + return "UpdateMACSettingsProfileResponseValidationError" +} + +// Error satisfies the builtin error interface +func (e UpdateMACSettingsProfileResponseValidationError) Error() string { + cause := "" + if e.cause != nil { + cause = fmt.Sprintf(" | caused by: %v", e.cause) + } + + key := "" + if e.key { + key = "key for " + } + + return fmt.Sprintf( + "invalid %sUpdateMACSettingsProfileResponse.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = UpdateMACSettingsProfileResponseValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = UpdateMACSettingsProfileResponseValidationError{} + +// ValidateFields checks the field values on DeleteMACSettingsProfileRequest +// with the rules defined in the proto definition for this message. If any +// rules are violated, an error is returned. +func (m *DeleteMACSettingsProfileRequest) ValidateFields(paths ...string) error { + if m == nil { + return nil + } + + if len(paths) == 0 { + paths = DeleteMACSettingsProfileRequestFieldPathsNested + } + + for name, subs := range _processPaths(append(paths[:0:0], paths...)) { + _ = subs + switch name { + case "mac_settings_profile_ids": + + if m.GetMacSettingsProfileIds() == nil { + return DeleteMACSettingsProfileRequestValidationError{ + field: "mac_settings_profile_ids", + reason: "value is required", + } + } + + if v, ok := interface{}(m.GetMacSettingsProfileIds()).(interface{ ValidateFields(...string) error }); ok { + if err := v.ValidateFields(subs...); err != nil { + return DeleteMACSettingsProfileRequestValidationError{ + field: "mac_settings_profile_ids", + reason: "embedded message failed validation", + cause: err, + } + } + } + + default: + return DeleteMACSettingsProfileRequestValidationError{ + field: name, + reason: "invalid field path", + } + } + } + return nil +} + +// DeleteMACSettingsProfileRequestValidationError is the validation error +// returned by DeleteMACSettingsProfileRequest.ValidateFields if the +// designated constraints aren't met. +type DeleteMACSettingsProfileRequestValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e DeleteMACSettingsProfileRequestValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e DeleteMACSettingsProfileRequestValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e DeleteMACSettingsProfileRequestValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e DeleteMACSettingsProfileRequestValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e DeleteMACSettingsProfileRequestValidationError) ErrorName() string { + return "DeleteMACSettingsProfileRequestValidationError" +} + +// Error satisfies the builtin error interface +func (e DeleteMACSettingsProfileRequestValidationError) Error() string { + cause := "" + if e.cause != nil { + cause = fmt.Sprintf(" | caused by: %v", e.cause) + } + + key := "" + if e.key { + key = "key for " + } + + return fmt.Sprintf( + "invalid %sDeleteMACSettingsProfileRequest.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = DeleteMACSettingsProfileRequestValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = DeleteMACSettingsProfileRequestValidationError{} + +// ValidateFields checks the field values on DeleteMACSettingsProfileResponse +// with the rules defined in the proto definition for this message. If any +// rules are violated, an error is returned. +func (m *DeleteMACSettingsProfileResponse) ValidateFields(paths ...string) error { + if len(paths) > 0 { + return fmt.Errorf("message DeleteMACSettingsProfileResponse has no fields, but paths %s were specified", paths) + } + return nil +} + +// DeleteMACSettingsProfileResponseValidationError is the validation error +// returned by DeleteMACSettingsProfileResponse.ValidateFields if the +// designated constraints aren't met. +type DeleteMACSettingsProfileResponseValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e DeleteMACSettingsProfileResponseValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e DeleteMACSettingsProfileResponseValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e DeleteMACSettingsProfileResponseValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e DeleteMACSettingsProfileResponseValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e DeleteMACSettingsProfileResponseValidationError) ErrorName() string { + return "DeleteMACSettingsProfileResponseValidationError" +} + +// Error satisfies the builtin error interface +func (e DeleteMACSettingsProfileResponseValidationError) Error() string { + cause := "" + if e.cause != nil { + cause = fmt.Sprintf(" | caused by: %v", e.cause) + } + + key := "" + if e.key { + key = "key for " + } + + return fmt.Sprintf( + "invalid %sDeleteMACSettingsProfileResponse.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = DeleteMACSettingsProfileResponseValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = DeleteMACSettingsProfileResponseValidationError{} + +// ValidateFields checks the field values on ListMACSettingsProfilesRequest +// with the rules defined in the proto definition for this message. If any +// rules are violated, an error is returned. +func (m *ListMACSettingsProfilesRequest) ValidateFields(paths ...string) error { + if m == nil { + return nil + } + + if len(paths) == 0 { + paths = ListMACSettingsProfilesRequestFieldPathsNested + } + + for name, subs := range _processPaths(append(paths[:0:0], paths...)) { + _ = subs + switch name { + case "application_ids": + + if m.GetApplicationIds() == nil { + return ListMACSettingsProfilesRequestValidationError{ + field: "application_ids", + reason: "value is required", + } + } + + if v, ok := interface{}(m.GetApplicationIds()).(interface{ ValidateFields(...string) error }); ok { + if err := v.ValidateFields(subs...); err != nil { + return ListMACSettingsProfilesRequestValidationError{ + field: "application_ids", + reason: "embedded message failed validation", + cause: err, + } + } + } + + case "field_mask": + + if v, ok := interface{}(m.GetFieldMask()).(interface{ ValidateFields(...string) error }); ok { + if err := v.ValidateFields(subs...); err != nil { + return ListMACSettingsProfilesRequestValidationError{ + field: "field_mask", + reason: "embedded message failed validation", + cause: err, + } + } + } + + case "order": + + if _, ok := _ListMACSettingsProfilesRequest_Order_InLookup[m.GetOrder()]; !ok { + return ListMACSettingsProfilesRequestValidationError{ + field: "order", + reason: "value must be in list [ ids.profile_id -ids.profile_id]", + } + } + + case "limit": + + if m.GetLimit() > 1000 { + return ListMACSettingsProfilesRequestValidationError{ + field: "limit", + reason: "value must be less than or equal to 1000", + } + } + + case "page": + // no validation rules for Page + default: + return ListMACSettingsProfilesRequestValidationError{ + field: name, + reason: "invalid field path", + } + } + } + return nil +} + +// ListMACSettingsProfilesRequestValidationError is the validation error +// returned by ListMACSettingsProfilesRequest.ValidateFields if the designated +// constraints aren't met. +type ListMACSettingsProfilesRequestValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e ListMACSettingsProfilesRequestValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e ListMACSettingsProfilesRequestValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e ListMACSettingsProfilesRequestValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e ListMACSettingsProfilesRequestValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e ListMACSettingsProfilesRequestValidationError) ErrorName() string { + return "ListMACSettingsProfilesRequestValidationError" +} + +// Error satisfies the builtin error interface +func (e ListMACSettingsProfilesRequestValidationError) Error() string { + cause := "" + if e.cause != nil { + cause = fmt.Sprintf(" | caused by: %v", e.cause) + } + + key := "" + if e.key { + key = "key for " + } + + return fmt.Sprintf( + "invalid %sListMACSettingsProfilesRequest.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = ListMACSettingsProfilesRequestValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = ListMACSettingsProfilesRequestValidationError{} + +var _ListMACSettingsProfilesRequest_Order_InLookup = map[string]struct{}{ + "": {}, + "ids.profile_id": {}, + "-ids.profile_id": {}, +} + +// ValidateFields checks the field values on ListMACSettingsProfilesResponse +// with the rules defined in the proto definition for this message. If any +// rules are violated, an error is returned. +func (m *ListMACSettingsProfilesResponse) ValidateFields(paths ...string) error { + if m == nil { + return nil + } + + if len(paths) == 0 { + paths = ListMACSettingsProfilesResponseFieldPathsNested + } + + for name, subs := range _processPaths(append(paths[:0:0], paths...)) { + _ = subs + switch name { + case "mac_settings_profiles": + + for idx, item := range m.GetMacSettingsProfiles() { + _, _ = idx, item + + if v, ok := interface{}(item).(interface{ ValidateFields(...string) error }); ok { + if err := v.ValidateFields(subs...); err != nil { + return ListMACSettingsProfilesResponseValidationError{ + field: fmt.Sprintf("mac_settings_profiles[%v]", idx), + reason: "embedded message failed validation", + cause: err, + } + } + } + + } + + default: + return ListMACSettingsProfilesResponseValidationError{ + field: name, + reason: "invalid field path", + } + } + } + return nil +} + +// ListMACSettingsProfilesResponseValidationError is the validation error +// returned by ListMACSettingsProfilesResponse.ValidateFields if the +// designated constraints aren't met. +type ListMACSettingsProfilesResponseValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e ListMACSettingsProfilesResponseValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e ListMACSettingsProfilesResponseValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e ListMACSettingsProfilesResponseValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e ListMACSettingsProfilesResponseValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e ListMACSettingsProfilesResponseValidationError) ErrorName() string { + return "ListMACSettingsProfilesResponseValidationError" +} + +// Error satisfies the builtin error interface +func (e ListMACSettingsProfilesResponseValidationError) Error() string { + cause := "" + if e.cause != nil { + cause = fmt.Sprintf(" | caused by: %v", e.cause) + } + + key := "" + if e.key { + key = "key for " + } + + return fmt.Sprintf( + "invalid %sListMACSettingsProfilesResponse.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = ListMACSettingsProfilesResponseValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = ListMACSettingsProfilesResponseValidationError{} diff --git a/pkg/ttnpb/networkserver_mac_settings_profile_flags.pb.go b/pkg/ttnpb/networkserver_mac_settings_profile_flags.pb.go new file mode 100644 index 0000000000..b3efd94008 --- /dev/null +++ b/pkg/ttnpb/networkserver_mac_settings_profile_flags.pb.go @@ -0,0 +1,61 @@ +// Code generated by protoc-gen-go-flags. DO NOT EDIT. +// versions: +// - protoc-gen-go-flags v1.2.0 +// - protoc v4.23.4 +// source: ttn/lorawan/v3/networkserver_mac_settings_profile.proto + +package ttnpb + +import ( + flagsplugin "github.com/TheThingsIndustries/protoc-gen-go-flags/flagsplugin" + golang "github.com/TheThingsIndustries/protoc-gen-go-flags/golang" + pflag "github.com/spf13/pflag" +) + +// AddSetFlagsForListMACSettingsProfilesRequest adds flags to select fields in ListMACSettingsProfilesRequest. +func AddSetFlagsForListMACSettingsProfilesRequest(flags *pflag.FlagSet, prefix string, hidden bool) { + AddSetFlagsForApplicationIdentifiers(flags, flagsplugin.Prefix("application-ids", prefix), hidden) + flags.AddFlag(flagsplugin.NewStringSliceFlag(flagsplugin.Prefix("field-mask", prefix), "", flagsplugin.WithHidden(hidden))) + flags.AddFlag(flagsplugin.NewStringFlag(flagsplugin.Prefix("order", prefix), "", flagsplugin.WithHidden(hidden))) + flags.AddFlag(flagsplugin.NewUint32Flag(flagsplugin.Prefix("limit", prefix), "", flagsplugin.WithHidden(hidden))) + flags.AddFlag(flagsplugin.NewUint32Flag(flagsplugin.Prefix("page", prefix), "", flagsplugin.WithHidden(hidden))) +} + +// SetFromFlags sets the ListMACSettingsProfilesRequest message from flags. +func (m *ListMACSettingsProfilesRequest) SetFromFlags(flags *pflag.FlagSet, prefix string) (paths []string, err error) { + if changed := flagsplugin.IsAnyPrefixSet(flags, flagsplugin.Prefix("application_ids", prefix)); changed { + if m.ApplicationIds == nil { + m.ApplicationIds = &ApplicationIdentifiers{} + } + if setPaths, err := m.ApplicationIds.SetFromFlags(flags, flagsplugin.Prefix("application_ids", prefix)); err != nil { + return nil, err + } else { + paths = append(paths, setPaths...) + } + } + if val, changed, err := flagsplugin.GetStringSlice(flags, flagsplugin.Prefix("field_mask", prefix)); err != nil { + return nil, err + } else if changed { + m.FieldMask = golang.SetFieldMask(val) + paths = append(paths, flagsplugin.Prefix("field_mask", prefix)) + } + if val, changed, err := flagsplugin.GetString(flags, flagsplugin.Prefix("order", prefix)); err != nil { + return nil, err + } else if changed { + m.Order = val + paths = append(paths, flagsplugin.Prefix("order", prefix)) + } + if val, changed, err := flagsplugin.GetUint32(flags, flagsplugin.Prefix("limit", prefix)); err != nil { + return nil, err + } else if changed { + m.Limit = val + paths = append(paths, flagsplugin.Prefix("limit", prefix)) + } + if val, changed, err := flagsplugin.GetUint32(flags, flagsplugin.Prefix("page", prefix)); err != nil { + return nil, err + } else if changed { + m.Page = val + paths = append(paths, flagsplugin.Prefix("page", prefix)) + } + return paths, nil +} diff --git a/pkg/ttnpb/networkserver_mac_settings_profile_grpc.pb.go b/pkg/ttnpb/networkserver_mac_settings_profile_grpc.pb.go new file mode 100644 index 0000000000..e9c21d7329 --- /dev/null +++ b/pkg/ttnpb/networkserver_mac_settings_profile_grpc.pb.go @@ -0,0 +1,282 @@ +// Copyright © 2024 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. + +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.3.0 +// - protoc v4.25.1 +// source: ttn/lorawan/v3/networkserver_mac_settings_profile.proto + +package ttnpb + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +const ( + NsMACSettingsProfileRegistry_Create_FullMethodName = "/ttn.lorawan.v3.NsMACSettingsProfileRegistry/Create" + NsMACSettingsProfileRegistry_Get_FullMethodName = "/ttn.lorawan.v3.NsMACSettingsProfileRegistry/Get" + NsMACSettingsProfileRegistry_Update_FullMethodName = "/ttn.lorawan.v3.NsMACSettingsProfileRegistry/Update" + NsMACSettingsProfileRegistry_Delete_FullMethodName = "/ttn.lorawan.v3.NsMACSettingsProfileRegistry/Delete" + NsMACSettingsProfileRegistry_List_FullMethodName = "/ttn.lorawan.v3.NsMACSettingsProfileRegistry/List" +) + +// NsMACSettingsProfileRegistryClient is the client API for NsMACSettingsProfileRegistry service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type NsMACSettingsProfileRegistryClient interface { + // Create creates a new MAC settings profile. + Create(ctx context.Context, in *CreateMACSettingsProfileRequest, opts ...grpc.CallOption) (*CreateMACSettingsProfileResponse, error) + // Get returns the MAC settings profile that matches the given identifiers. + Get(ctx context.Context, in *GetMACSettingsProfileRequest, opts ...grpc.CallOption) (*GetMACSettingsProfileResponse, error) + // Update updates the MAC settings profile that matches the given identifiers. + Update(ctx context.Context, in *UpdateMACSettingsProfileRequest, opts ...grpc.CallOption) (*UpdateMACSettingsProfileResponse, error) + // Delete deletes the MAC settings profile that matches the given identifiers. + Delete(ctx context.Context, in *DeleteMACSettingsProfileRequest, opts ...grpc.CallOption) (*DeleteMACSettingsProfileResponse, error) + // List lists the MAC settings profiles. + List(ctx context.Context, in *ListMACSettingsProfilesRequest, opts ...grpc.CallOption) (*ListMACSettingsProfilesResponse, error) +} + +type nsMACSettingsProfileRegistryClient struct { + cc grpc.ClientConnInterface +} + +func NewNsMACSettingsProfileRegistryClient(cc grpc.ClientConnInterface) NsMACSettingsProfileRegistryClient { + return &nsMACSettingsProfileRegistryClient{cc} +} + +func (c *nsMACSettingsProfileRegistryClient) Create(ctx context.Context, in *CreateMACSettingsProfileRequest, opts ...grpc.CallOption) (*CreateMACSettingsProfileResponse, error) { + out := new(CreateMACSettingsProfileResponse) + err := c.cc.Invoke(ctx, NsMACSettingsProfileRegistry_Create_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *nsMACSettingsProfileRegistryClient) Get(ctx context.Context, in *GetMACSettingsProfileRequest, opts ...grpc.CallOption) (*GetMACSettingsProfileResponse, error) { + out := new(GetMACSettingsProfileResponse) + err := c.cc.Invoke(ctx, NsMACSettingsProfileRegistry_Get_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *nsMACSettingsProfileRegistryClient) Update(ctx context.Context, in *UpdateMACSettingsProfileRequest, opts ...grpc.CallOption) (*UpdateMACSettingsProfileResponse, error) { + out := new(UpdateMACSettingsProfileResponse) + err := c.cc.Invoke(ctx, NsMACSettingsProfileRegistry_Update_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *nsMACSettingsProfileRegistryClient) Delete(ctx context.Context, in *DeleteMACSettingsProfileRequest, opts ...grpc.CallOption) (*DeleteMACSettingsProfileResponse, error) { + out := new(DeleteMACSettingsProfileResponse) + err := c.cc.Invoke(ctx, NsMACSettingsProfileRegistry_Delete_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *nsMACSettingsProfileRegistryClient) List(ctx context.Context, in *ListMACSettingsProfilesRequest, opts ...grpc.CallOption) (*ListMACSettingsProfilesResponse, error) { + out := new(ListMACSettingsProfilesResponse) + err := c.cc.Invoke(ctx, NsMACSettingsProfileRegistry_List_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// NsMACSettingsProfileRegistryServer is the server API for NsMACSettingsProfileRegistry service. +// All implementations must embed UnimplementedNsMACSettingsProfileRegistryServer +// for forward compatibility +type NsMACSettingsProfileRegistryServer interface { + // Create creates a new MAC settings profile. + Create(context.Context, *CreateMACSettingsProfileRequest) (*CreateMACSettingsProfileResponse, error) + // Get returns the MAC settings profile that matches the given identifiers. + Get(context.Context, *GetMACSettingsProfileRequest) (*GetMACSettingsProfileResponse, error) + // Update updates the MAC settings profile that matches the given identifiers. + Update(context.Context, *UpdateMACSettingsProfileRequest) (*UpdateMACSettingsProfileResponse, error) + // Delete deletes the MAC settings profile that matches the given identifiers. + Delete(context.Context, *DeleteMACSettingsProfileRequest) (*DeleteMACSettingsProfileResponse, error) + // List lists the MAC settings profiles. + List(context.Context, *ListMACSettingsProfilesRequest) (*ListMACSettingsProfilesResponse, error) + mustEmbedUnimplementedNsMACSettingsProfileRegistryServer() +} + +// UnimplementedNsMACSettingsProfileRegistryServer must be embedded to have forward compatible implementations. +type UnimplementedNsMACSettingsProfileRegistryServer struct { +} + +func (UnimplementedNsMACSettingsProfileRegistryServer) Create(context.Context, *CreateMACSettingsProfileRequest) (*CreateMACSettingsProfileResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Create not implemented") +} +func (UnimplementedNsMACSettingsProfileRegistryServer) Get(context.Context, *GetMACSettingsProfileRequest) (*GetMACSettingsProfileResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Get not implemented") +} +func (UnimplementedNsMACSettingsProfileRegistryServer) Update(context.Context, *UpdateMACSettingsProfileRequest) (*UpdateMACSettingsProfileResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Update not implemented") +} +func (UnimplementedNsMACSettingsProfileRegistryServer) Delete(context.Context, *DeleteMACSettingsProfileRequest) (*DeleteMACSettingsProfileResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Delete not implemented") +} +func (UnimplementedNsMACSettingsProfileRegistryServer) List(context.Context, *ListMACSettingsProfilesRequest) (*ListMACSettingsProfilesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method List not implemented") +} +func (UnimplementedNsMACSettingsProfileRegistryServer) mustEmbedUnimplementedNsMACSettingsProfileRegistryServer() { +} + +// UnsafeNsMACSettingsProfileRegistryServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to NsMACSettingsProfileRegistryServer will +// result in compilation errors. +type UnsafeNsMACSettingsProfileRegistryServer interface { + mustEmbedUnimplementedNsMACSettingsProfileRegistryServer() +} + +func RegisterNsMACSettingsProfileRegistryServer(s grpc.ServiceRegistrar, srv NsMACSettingsProfileRegistryServer) { + s.RegisterService(&NsMACSettingsProfileRegistry_ServiceDesc, srv) +} + +func _NsMACSettingsProfileRegistry_Create_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CreateMACSettingsProfileRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(NsMACSettingsProfileRegistryServer).Create(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: NsMACSettingsProfileRegistry_Create_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(NsMACSettingsProfileRegistryServer).Create(ctx, req.(*CreateMACSettingsProfileRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _NsMACSettingsProfileRegistry_Get_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetMACSettingsProfileRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(NsMACSettingsProfileRegistryServer).Get(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: NsMACSettingsProfileRegistry_Get_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(NsMACSettingsProfileRegistryServer).Get(ctx, req.(*GetMACSettingsProfileRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _NsMACSettingsProfileRegistry_Update_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UpdateMACSettingsProfileRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(NsMACSettingsProfileRegistryServer).Update(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: NsMACSettingsProfileRegistry_Update_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(NsMACSettingsProfileRegistryServer).Update(ctx, req.(*UpdateMACSettingsProfileRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _NsMACSettingsProfileRegistry_Delete_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DeleteMACSettingsProfileRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(NsMACSettingsProfileRegistryServer).Delete(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: NsMACSettingsProfileRegistry_Delete_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(NsMACSettingsProfileRegistryServer).Delete(ctx, req.(*DeleteMACSettingsProfileRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _NsMACSettingsProfileRegistry_List_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListMACSettingsProfilesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(NsMACSettingsProfileRegistryServer).List(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: NsMACSettingsProfileRegistry_List_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(NsMACSettingsProfileRegistryServer).List(ctx, req.(*ListMACSettingsProfilesRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// NsMACSettingsProfileRegistry_ServiceDesc is the grpc.ServiceDesc for NsMACSettingsProfileRegistry service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var NsMACSettingsProfileRegistry_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "ttn.lorawan.v3.NsMACSettingsProfileRegistry", + HandlerType: (*NsMACSettingsProfileRegistryServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Create", + Handler: _NsMACSettingsProfileRegistry_Create_Handler, + }, + { + MethodName: "Get", + Handler: _NsMACSettingsProfileRegistry_Get_Handler, + }, + { + MethodName: "Update", + Handler: _NsMACSettingsProfileRegistry_Update_Handler, + }, + { + MethodName: "Delete", + Handler: _NsMACSettingsProfileRegistry_Delete_Handler, + }, + { + MethodName: "List", + Handler: _NsMACSettingsProfileRegistry_List_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "ttn/lorawan/v3/networkserver_mac_settings_profile.proto", +} diff --git a/pkg/ttnpb/networkserver_mac_settings_profile_json.pb.go b/pkg/ttnpb/networkserver_mac_settings_profile_json.pb.go new file mode 100644 index 0000000000..bf7335dce5 --- /dev/null +++ b/pkg/ttnpb/networkserver_mac_settings_profile_json.pb.go @@ -0,0 +1,505 @@ +// Code generated by protoc-gen-go-json. DO NOT EDIT. +// versions: +// - protoc-gen-go-json v1.6.0 +// - protoc v4.23.4 +// source: ttn/lorawan/v3/networkserver_mac_settings_profile.proto + +package ttnpb + +import ( + golang "github.com/TheThingsIndustries/protoc-gen-go-json/golang" + jsonplugin "github.com/TheThingsIndustries/protoc-gen-go-json/jsonplugin" +) + +// MarshalProtoJSON marshals the CreateMACSettingsProfileRequest message to JSON. +func (x *CreateMACSettingsProfileRequest) MarshalProtoJSON(s *jsonplugin.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.MacSettingsProfile != nil || s.HasField("mac_settings_profile") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("mac_settings_profile") + x.MacSettingsProfile.MarshalProtoJSON(s.WithField("mac_settings_profile")) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the CreateMACSettingsProfileRequest to JSON. +func (x *CreateMACSettingsProfileRequest) MarshalJSON() ([]byte, error) { + return jsonplugin.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the CreateMACSettingsProfileRequest message from JSON. +func (x *CreateMACSettingsProfileRequest) UnmarshalProtoJSON(s *jsonplugin.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.ReadAny() // ignore unknown field + case "mac_settings_profile", "macSettingsProfile": + if s.ReadNil() { + x.MacSettingsProfile = nil + return + } + x.MacSettingsProfile = &MACSettingsProfile{} + x.MacSettingsProfile.UnmarshalProtoJSON(s.WithField("mac_settings_profile", true)) + } + }) +} + +// UnmarshalJSON unmarshals the CreateMACSettingsProfileRequest from JSON. +func (x *CreateMACSettingsProfileRequest) UnmarshalJSON(b []byte) error { + return jsonplugin.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the CreateMACSettingsProfileResponse message to JSON. +func (x *CreateMACSettingsProfileResponse) MarshalProtoJSON(s *jsonplugin.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.MacSettingsProfile != nil || s.HasField("mac_settings_profile") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("mac_settings_profile") + x.MacSettingsProfile.MarshalProtoJSON(s.WithField("mac_settings_profile")) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the CreateMACSettingsProfileResponse to JSON. +func (x *CreateMACSettingsProfileResponse) MarshalJSON() ([]byte, error) { + return jsonplugin.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the CreateMACSettingsProfileResponse message from JSON. +func (x *CreateMACSettingsProfileResponse) UnmarshalProtoJSON(s *jsonplugin.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.ReadAny() // ignore unknown field + case "mac_settings_profile", "macSettingsProfile": + if s.ReadNil() { + x.MacSettingsProfile = nil + return + } + x.MacSettingsProfile = &MACSettingsProfile{} + x.MacSettingsProfile.UnmarshalProtoJSON(s.WithField("mac_settings_profile", true)) + } + }) +} + +// UnmarshalJSON unmarshals the CreateMACSettingsProfileResponse from JSON. +func (x *CreateMACSettingsProfileResponse) UnmarshalJSON(b []byte) error { + return jsonplugin.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the GetMACSettingsProfileRequest message to JSON. +func (x *GetMACSettingsProfileRequest) MarshalProtoJSON(s *jsonplugin.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.MacSettingsProfileIds != nil || s.HasField("mac_settings_profile_ids") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("mac_settings_profile_ids") + // NOTE: MACSettingsProfileIdentifiers does not seem to implement MarshalProtoJSON. + golang.MarshalMessage(s, x.MacSettingsProfileIds) + } + if x.FieldMask != nil || s.HasField("field_mask") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("field_mask") + if x.FieldMask == nil { + s.WriteNil() + } else { + golang.MarshalLegacyFieldMask(s, x.FieldMask) + } + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the GetMACSettingsProfileRequest to JSON. +func (x *GetMACSettingsProfileRequest) MarshalJSON() ([]byte, error) { + return jsonplugin.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the GetMACSettingsProfileRequest message from JSON. +func (x *GetMACSettingsProfileRequest) UnmarshalProtoJSON(s *jsonplugin.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.ReadAny() // ignore unknown field + case "mac_settings_profile_ids", "macSettingsProfileIds": + s.AddField("mac_settings_profile_ids") + if s.ReadNil() { + x.MacSettingsProfileIds = nil + return + } + // NOTE: MACSettingsProfileIdentifiers does not seem to implement UnmarshalProtoJSON. + var v MACSettingsProfileIdentifiers + golang.UnmarshalMessage(s, &v) + x.MacSettingsProfileIds = &v + case "field_mask", "fieldMask": + s.AddField("field_mask") + if s.ReadNil() { + x.FieldMask = nil + return + } + v := golang.UnmarshalFieldMask(s) + if s.Err() != nil { + return + } + x.FieldMask = v + } + }) +} + +// UnmarshalJSON unmarshals the GetMACSettingsProfileRequest from JSON. +func (x *GetMACSettingsProfileRequest) UnmarshalJSON(b []byte) error { + return jsonplugin.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the GetMACSettingsProfileResponse message to JSON. +func (x *GetMACSettingsProfileResponse) MarshalProtoJSON(s *jsonplugin.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.MacSettingsProfile != nil || s.HasField("mac_settings_profile") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("mac_settings_profile") + x.MacSettingsProfile.MarshalProtoJSON(s.WithField("mac_settings_profile")) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the GetMACSettingsProfileResponse to JSON. +func (x *GetMACSettingsProfileResponse) MarshalJSON() ([]byte, error) { + return jsonplugin.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the GetMACSettingsProfileResponse message from JSON. +func (x *GetMACSettingsProfileResponse) UnmarshalProtoJSON(s *jsonplugin.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.ReadAny() // ignore unknown field + case "mac_settings_profile", "macSettingsProfile": + if s.ReadNil() { + x.MacSettingsProfile = nil + return + } + x.MacSettingsProfile = &MACSettingsProfile{} + x.MacSettingsProfile.UnmarshalProtoJSON(s.WithField("mac_settings_profile", true)) + } + }) +} + +// UnmarshalJSON unmarshals the GetMACSettingsProfileResponse from JSON. +func (x *GetMACSettingsProfileResponse) UnmarshalJSON(b []byte) error { + return jsonplugin.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the UpdateMACSettingsProfileRequest message to JSON. +func (x *UpdateMACSettingsProfileRequest) MarshalProtoJSON(s *jsonplugin.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.MacSettingsProfileIds != nil || s.HasField("mac_settings_profile_ids") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("mac_settings_profile_ids") + // NOTE: MACSettingsProfileIdentifiers does not seem to implement MarshalProtoJSON. + golang.MarshalMessage(s, x.MacSettingsProfileIds) + } + if x.MacSettingsProfile != nil || s.HasField("mac_settings_profile") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("mac_settings_profile") + x.MacSettingsProfile.MarshalProtoJSON(s.WithField("mac_settings_profile")) + } + if x.FieldMask != nil || s.HasField("field_mask") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("field_mask") + if x.FieldMask == nil { + s.WriteNil() + } else { + golang.MarshalLegacyFieldMask(s, x.FieldMask) + } + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the UpdateMACSettingsProfileRequest to JSON. +func (x *UpdateMACSettingsProfileRequest) MarshalJSON() ([]byte, error) { + return jsonplugin.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the UpdateMACSettingsProfileRequest message from JSON. +func (x *UpdateMACSettingsProfileRequest) UnmarshalProtoJSON(s *jsonplugin.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.ReadAny() // ignore unknown field + case "mac_settings_profile_ids", "macSettingsProfileIds": + s.AddField("mac_settings_profile_ids") + if s.ReadNil() { + x.MacSettingsProfileIds = nil + return + } + // NOTE: MACSettingsProfileIdentifiers does not seem to implement UnmarshalProtoJSON. + var v MACSettingsProfileIdentifiers + golang.UnmarshalMessage(s, &v) + x.MacSettingsProfileIds = &v + case "mac_settings_profile", "macSettingsProfile": + if s.ReadNil() { + x.MacSettingsProfile = nil + return + } + x.MacSettingsProfile = &MACSettingsProfile{} + x.MacSettingsProfile.UnmarshalProtoJSON(s.WithField("mac_settings_profile", true)) + case "field_mask", "fieldMask": + s.AddField("field_mask") + if s.ReadNil() { + x.FieldMask = nil + return + } + v := golang.UnmarshalFieldMask(s) + if s.Err() != nil { + return + } + x.FieldMask = v + } + }) +} + +// UnmarshalJSON unmarshals the UpdateMACSettingsProfileRequest from JSON. +func (x *UpdateMACSettingsProfileRequest) UnmarshalJSON(b []byte) error { + return jsonplugin.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the UpdateMACSettingsProfileResponse message to JSON. +func (x *UpdateMACSettingsProfileResponse) MarshalProtoJSON(s *jsonplugin.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.MacSettingsProfile != nil || s.HasField("mac_settings_profile") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("mac_settings_profile") + x.MacSettingsProfile.MarshalProtoJSON(s.WithField("mac_settings_profile")) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the UpdateMACSettingsProfileResponse to JSON. +func (x *UpdateMACSettingsProfileResponse) MarshalJSON() ([]byte, error) { + return jsonplugin.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the UpdateMACSettingsProfileResponse message from JSON. +func (x *UpdateMACSettingsProfileResponse) UnmarshalProtoJSON(s *jsonplugin.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.ReadAny() // ignore unknown field + case "mac_settings_profile", "macSettingsProfile": + if s.ReadNil() { + x.MacSettingsProfile = nil + return + } + x.MacSettingsProfile = &MACSettingsProfile{} + x.MacSettingsProfile.UnmarshalProtoJSON(s.WithField("mac_settings_profile", true)) + } + }) +} + +// UnmarshalJSON unmarshals the UpdateMACSettingsProfileResponse from JSON. +func (x *UpdateMACSettingsProfileResponse) UnmarshalJSON(b []byte) error { + return jsonplugin.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the ListMACSettingsProfilesRequest message to JSON. +func (x *ListMACSettingsProfilesRequest) MarshalProtoJSON(s *jsonplugin.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if x.ApplicationIds != nil || s.HasField("application_ids") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("application_ids") + // NOTE: ApplicationIdentifiers does not seem to implement MarshalProtoJSON. + golang.MarshalMessage(s, x.ApplicationIds) + } + if x.FieldMask != nil || s.HasField("field_mask") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("field_mask") + if x.FieldMask == nil { + s.WriteNil() + } else { + golang.MarshalLegacyFieldMask(s, x.FieldMask) + } + } + if x.Order != "" || s.HasField("order") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("order") + s.WriteString(x.Order) + } + if x.Limit != 0 || s.HasField("limit") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("limit") + s.WriteUint32(x.Limit) + } + if x.Page != 0 || s.HasField("page") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("page") + s.WriteUint32(x.Page) + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the ListMACSettingsProfilesRequest to JSON. +func (x *ListMACSettingsProfilesRequest) MarshalJSON() ([]byte, error) { + return jsonplugin.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the ListMACSettingsProfilesRequest message from JSON. +func (x *ListMACSettingsProfilesRequest) UnmarshalProtoJSON(s *jsonplugin.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.ReadAny() // ignore unknown field + case "application_ids", "applicationIds": + s.AddField("application_ids") + if s.ReadNil() { + x.ApplicationIds = nil + return + } + // NOTE: ApplicationIdentifiers does not seem to implement UnmarshalProtoJSON. + var v ApplicationIdentifiers + golang.UnmarshalMessage(s, &v) + x.ApplicationIds = &v + case "field_mask", "fieldMask": + s.AddField("field_mask") + if s.ReadNil() { + x.FieldMask = nil + return + } + v := golang.UnmarshalFieldMask(s) + if s.Err() != nil { + return + } + x.FieldMask = v + case "order": + s.AddField("order") + x.Order = s.ReadString() + case "limit": + s.AddField("limit") + x.Limit = s.ReadUint32() + case "page": + s.AddField("page") + x.Page = s.ReadUint32() + } + }) +} + +// UnmarshalJSON unmarshals the ListMACSettingsProfilesRequest from JSON. +func (x *ListMACSettingsProfilesRequest) UnmarshalJSON(b []byte) error { + return jsonplugin.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + +// MarshalProtoJSON marshals the ListMACSettingsProfilesResponse message to JSON. +func (x *ListMACSettingsProfilesResponse) MarshalProtoJSON(s *jsonplugin.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if len(x.MacSettingsProfiles) > 0 || s.HasField("mac_settings_profiles") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("mac_settings_profiles") + s.WriteArrayStart() + var wroteElement bool + for _, element := range x.MacSettingsProfiles { + s.WriteMoreIf(&wroteElement) + element.MarshalProtoJSON(s.WithField("mac_settings_profiles")) + } + s.WriteArrayEnd() + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the ListMACSettingsProfilesResponse to JSON. +func (x *ListMACSettingsProfilesResponse) MarshalJSON() ([]byte, error) { + return jsonplugin.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the ListMACSettingsProfilesResponse message from JSON. +func (x *ListMACSettingsProfilesResponse) UnmarshalProtoJSON(s *jsonplugin.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.ReadAny() // ignore unknown field + case "mac_settings_profiles", "macSettingsProfiles": + s.AddField("mac_settings_profiles") + if s.ReadNil() { + x.MacSettingsProfiles = nil + return + } + s.ReadArray(func() { + if s.ReadNil() { + x.MacSettingsProfiles = append(x.MacSettingsProfiles, nil) + return + } + v := &MACSettingsProfile{} + v.UnmarshalProtoJSON(s.WithField("mac_settings_profiles", false)) + if s.Err() != nil { + return + } + x.MacSettingsProfiles = append(x.MacSettingsProfiles, v) + }) + } + }) +} + +// UnmarshalJSON unmarshals the ListMACSettingsProfilesResponse from JSON. +func (x *ListMACSettingsProfilesResponse) UnmarshalJSON(b []byte) error { + return jsonplugin.DefaultUnmarshalerConfig.Unmarshal(b, x) +} diff --git a/pkg/ttnpb/notification_service.go b/pkg/ttnpb/notification_service.go new file mode 100644 index 0000000000..dbceb281ed --- /dev/null +++ b/pkg/ttnpb/notification_service.go @@ -0,0 +1,23 @@ +// Copyright © 2024 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 ttnpb + +import "strings" + +// GetNotificationTypeString converts the NotificationType enum into lowercase string. +// TODO: Use the enum directly everywhere in v4 https://github.com/TheThingsNetwork/lorawan-stack/issues/7384. +func GetNotificationTypeString(t NotificationType) string { + return strings.ToLower(t.String()) +} diff --git a/pkg/ttnpb/notification_service.pb.go b/pkg/ttnpb/notification_service.pb.go index 771330db5c..60ee5c980d 100644 --- a/pkg/ttnpb/notification_service.pb.go +++ b/pkg/ttnpb/notification_service.pb.go @@ -42,6 +42,82 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) +type NotificationType int32 + +const ( + NotificationType_UNKNOWN NotificationType = 0 + NotificationType_API_KEY_CREATED NotificationType = 1 + NotificationType_API_KEY_CHANGED NotificationType = 2 + NotificationType_CLIENT_REQUESTED NotificationType = 3 + NotificationType_COLLABORATOR_CHANGED NotificationType = 4 + NotificationType_ENTITY_STATE_CHANGED NotificationType = 5 + NotificationType_INVITATION NotificationType = 6 + NotificationType_LOGIN_TOKEN NotificationType = 7 + NotificationType_PASSWORD_CHANGED NotificationType = 8 + NotificationType_TEMPORARY_PASSWORD NotificationType = 9 + NotificationType_USER_REQUESTED NotificationType = 10 + NotificationType_VALIDATE NotificationType = 11 +) + +// Enum value maps for NotificationType. +var ( + NotificationType_name = map[int32]string{ + 0: "UNKNOWN", + 1: "API_KEY_CREATED", + 2: "API_KEY_CHANGED", + 3: "CLIENT_REQUESTED", + 4: "COLLABORATOR_CHANGED", + 5: "ENTITY_STATE_CHANGED", + 6: "INVITATION", + 7: "LOGIN_TOKEN", + 8: "PASSWORD_CHANGED", + 9: "TEMPORARY_PASSWORD", + 10: "USER_REQUESTED", + 11: "VALIDATE", + } + NotificationType_value = map[string]int32{ + "UNKNOWN": 0, + "API_KEY_CREATED": 1, + "API_KEY_CHANGED": 2, + "CLIENT_REQUESTED": 3, + "COLLABORATOR_CHANGED": 4, + "ENTITY_STATE_CHANGED": 5, + "INVITATION": 6, + "LOGIN_TOKEN": 7, + "PASSWORD_CHANGED": 8, + "TEMPORARY_PASSWORD": 9, + "USER_REQUESTED": 10, + "VALIDATE": 11, + } +) + +func (x NotificationType) Enum() *NotificationType { + p := new(NotificationType) + *p = x + return p +} + +func (x NotificationType) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (NotificationType) Descriptor() protoreflect.EnumDescriptor { + return file_ttn_lorawan_v3_notification_service_proto_enumTypes[0].Descriptor() +} + +func (NotificationType) Type() protoreflect.EnumType { + return &file_ttn_lorawan_v3_notification_service_proto_enumTypes[0] +} + +func (x NotificationType) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use NotificationType.Descriptor instead. +func (NotificationType) EnumDescriptor() ([]byte, []int) { + return file_ttn_lorawan_v3_notification_service_proto_rawDescGZIP(), []int{0} +} + type NotificationReceiver int32 const ( @@ -84,11 +160,11 @@ func (x NotificationReceiver) String() string { } func (NotificationReceiver) Descriptor() protoreflect.EnumDescriptor { - return file_ttn_lorawan_v3_notification_service_proto_enumTypes[0].Descriptor() + return file_ttn_lorawan_v3_notification_service_proto_enumTypes[1].Descriptor() } func (NotificationReceiver) Type() protoreflect.EnumType { - return &file_ttn_lorawan_v3_notification_service_proto_enumTypes[0] + return &file_ttn_lorawan_v3_notification_service_proto_enumTypes[1] } func (x NotificationReceiver) Number() protoreflect.EnumNumber { @@ -97,7 +173,7 @@ func (x NotificationReceiver) Number() protoreflect.EnumNumber { // Deprecated: Use NotificationReceiver.Descriptor instead. func (NotificationReceiver) EnumDescriptor() ([]byte, []int) { - return file_ttn_lorawan_v3_notification_service_proto_rawDescGZIP(), []int{0} + return file_ttn_lorawan_v3_notification_service_proto_rawDescGZIP(), []int{1} } type NotificationStatus int32 @@ -133,11 +209,11 @@ func (x NotificationStatus) String() string { } func (NotificationStatus) Descriptor() protoreflect.EnumDescriptor { - return file_ttn_lorawan_v3_notification_service_proto_enumTypes[1].Descriptor() + return file_ttn_lorawan_v3_notification_service_proto_enumTypes[2].Descriptor() } func (NotificationStatus) Type() protoreflect.EnumType { - return &file_ttn_lorawan_v3_notification_service_proto_enumTypes[1] + return &file_ttn_lorawan_v3_notification_service_proto_enumTypes[2] } func (x NotificationStatus) Number() protoreflect.EnumNumber { @@ -146,7 +222,7 @@ func (x NotificationStatus) Number() protoreflect.EnumNumber { // Deprecated: Use NotificationStatus.Descriptor instead. func (NotificationStatus) EnumDescriptor() ([]byte, []int) { - return file_ttn_lorawan_v3_notification_service_proto_rawDescGZIP(), []int{1} + return file_ttn_lorawan_v3_notification_service_proto_rawDescGZIP(), []int{2} } type Notification struct { @@ -161,6 +237,7 @@ type Notification struct { // The entity this notification is about. EntityIds *EntityIdentifiers `protobuf:"bytes,3,opt,name=entity_ids,json=entityIds,proto3" json:"entity_ids,omitempty"` // The type of this notification. + // TODO: Replace with type NotificationType in v4 https://github.com/TheThingsNetwork/lorawan-stack/issues/7384. NotificationType string `protobuf:"bytes,4,opt,name=notification_type,json=notificationType,proto3" json:"notification_type,omitempty"` // The data related to the notification. Data *anypb.Any `protobuf:"bytes,5,opt,name=data,proto3" json:"data,omitempty"` @@ -169,6 +246,8 @@ type Notification struct { // Relation of the notification receiver to the entity. Receivers []NotificationReceiver `protobuf:"varint,8,rep,packed,name=receivers,proto3,enum=ttn.lorawan.v3.NotificationReceiver" json:"receivers,omitempty"` // Whether an email was sent for the notification. + // + // Deprecated: Marked as deprecated in ttn/lorawan/v3/notification_service.proto. Email bool `protobuf:"varint,9,opt,name=email,proto3" json:"email,omitempty"` // The status of the notification. Status NotificationStatus `protobuf:"varint,10,opt,name=status,proto3,enum=ttn.lorawan.v3.NotificationStatus" json:"status,omitempty"` @@ -257,6 +336,7 @@ func (x *Notification) GetReceivers() []NotificationReceiver { return nil } +// Deprecated: Marked as deprecated in ttn/lorawan/v3/notification_service.proto. func (x *Notification) GetEmail() bool { if x != nil { return x.Email @@ -294,6 +374,8 @@ type CreateNotificationRequest struct { // Receivers of the notification. Receivers []NotificationReceiver `protobuf:"varint,5,rep,packed,name=receivers,proto3,enum=ttn.lorawan.v3.NotificationReceiver" json:"receivers,omitempty"` // Whether an email should be sent for the notification. + // + // Deprecated: Marked as deprecated in ttn/lorawan/v3/notification_service.proto. Email bool `protobuf:"varint,6,opt,name=email,proto3" json:"email,omitempty"` } @@ -364,6 +446,7 @@ func (x *CreateNotificationRequest) GetReceivers() []NotificationReceiver { return nil } +// Deprecated: Marked as deprecated in ttn/lorawan/v3/notification_service.proto. func (x *CreateNotificationRequest) GetEmail() bool { if x != nil { return x.Email @@ -688,8 +771,8 @@ var file_ttn_lorawan_v3_notification_service_proto_rawDesc = []byte{ 0x6f, 0x74, 0x6f, 0x1a, 0x20, 0x74, 0x74, 0x6e, 0x2f, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2f, 0x76, 0x33, 0x2f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x17, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2f, - 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xaa, - 0x04, 0x0a, 0x0c, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xfb, + 0x05, 0x0a, 0x0c, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, @@ -698,159 +781,201 @@ var file_ttn_lorawan_v3_notification_service_proto_rawDesc = []byte{ 0x74, 0x69, 0x74, 0x79, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, - 0x73, 0x52, 0x09, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x49, 0x64, 0x73, 0x12, 0x2b, 0x0a, 0x11, - 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x79, 0x70, - 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x28, 0x0a, 0x04, 0x64, 0x61, 0x74, - 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x04, 0x64, - 0x61, 0x74, 0x61, 0x12, 0x3e, 0x0a, 0x0a, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x5f, 0x69, 0x64, - 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, - 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x49, 0x64, 0x65, - 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x52, 0x09, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, - 0x49, 0x64, 0x73, 0x12, 0x42, 0x0a, 0x09, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x73, - 0x18, 0x08, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x24, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, - 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x52, 0x09, 0x72, 0x65, - 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, - 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x3a, 0x0a, - 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x22, 0x2e, - 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4e, - 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x46, 0x0a, 0x11, 0x73, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x0b, - 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, 0x0f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, - 0x74, 0x4a, 0x04, 0x08, 0x07, 0x10, 0x08, 0x52, 0x12, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x70, - 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x69, 0x64, 0x73, 0x22, 0xf6, 0x02, 0x0a, 0x19, - 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4a, 0x0a, 0x0a, 0x65, 0x6e, 0x74, - 0x69, 0x74, 0x79, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, - 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x45, - 0x6e, 0x74, 0x69, 0x74, 0x79, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, - 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x09, 0x65, 0x6e, 0x74, 0x69, - 0x74, 0x79, 0x49, 0x64, 0x73, 0x12, 0x36, 0x0a, 0x11, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x42, 0x09, 0xfa, 0x42, 0x06, 0x72, 0x04, 0x10, 0x01, 0x18, 0x64, 0x52, 0x10, 0x6e, 0x6f, 0x74, - 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x28, 0x0a, - 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, - 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e, - 0x79, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x3e, 0x0a, 0x0a, 0x73, 0x65, 0x6e, 0x64, 0x65, - 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x74, 0x74, - 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x55, 0x73, 0x65, - 0x72, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x52, 0x09, 0x73, 0x65, - 0x6e, 0x64, 0x65, 0x72, 0x49, 0x64, 0x73, 0x12, 0x55, 0x0a, 0x09, 0x72, 0x65, 0x63, 0x65, 0x69, - 0x76, 0x65, 0x72, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x24, 0x2e, 0x74, 0x74, 0x6e, - 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4e, 0x6f, 0x74, 0x69, - 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, - 0x42, 0x11, 0xfa, 0x42, 0x0e, 0x92, 0x01, 0x0b, 0x08, 0x01, 0x18, 0x01, 0x22, 0x05, 0x82, 0x01, - 0x02, 0x10, 0x01, 0x52, 0x09, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x73, 0x12, 0x14, - 0x0a, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x65, - 0x6d, 0x61, 0x69, 0x6c, 0x22, 0x2c, 0x0a, 0x1a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4e, 0x6f, - 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, - 0x69, 0x64, 0x22, 0xf3, 0x01, 0x0a, 0x18, 0x4c, 0x69, 0x73, 0x74, 0x4e, 0x6f, 0x74, 0x69, 0x66, - 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x4c, 0x0a, 0x0c, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, - 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x49, 0x64, 0x65, 0x6e, 0x74, - 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, - 0x52, 0x0b, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x49, 0x64, 0x73, 0x12, 0x4b, 0x0a, - 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x22, 0x2e, + 0x73, 0x52, 0x09, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x49, 0x64, 0x73, 0x12, 0xf7, 0x01, 0x0a, + 0x11, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x79, + 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x42, 0xc9, 0x01, 0xfa, 0x42, 0xc5, 0x01, 0x72, + 0xc2, 0x01, 0x52, 0x07, 0x75, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x52, 0x0f, 0x61, 0x70, 0x69, + 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x52, 0x0f, 0x61, 0x70, + 0x69, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x52, 0x10, 0x63, + 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, 0x52, + 0x14, 0x63, 0x6f, 0x6c, 0x6c, 0x61, 0x62, 0x6f, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x63, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x64, 0x52, 0x14, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, 0x73, 0x74, + 0x61, 0x74, 0x65, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x52, 0x0a, 0x69, 0x6e, 0x76, + 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x5f, 0x74, + 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x10, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x5f, 0x63, + 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x52, 0x12, 0x74, 0x65, 0x6d, 0x70, 0x6f, 0x72, 0x61, 0x72, + 0x79, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x0e, 0x75, 0x73, 0x65, 0x72, + 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, 0x52, 0x08, 0x76, 0x61, 0x6c, 0x69, + 0x64, 0x61, 0x74, 0x65, 0x52, 0x10, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x28, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, + 0x12, 0x3e, 0x0a, 0x0a, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, + 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, + 0x66, 0x69, 0x65, 0x72, 0x73, 0x52, 0x09, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x49, 0x64, 0x73, + 0x12, 0x42, 0x0a, 0x09, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x73, 0x18, 0x08, 0x20, + 0x03, 0x28, 0x0e, 0x32, 0x24, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, + 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x52, 0x09, 0x72, 0x65, 0x63, 0x65, 0x69, + 0x76, 0x65, 0x72, 0x73, 0x12, 0x18, 0x0a, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x09, 0x20, + 0x01, 0x28, 0x08, 0x42, 0x02, 0x18, 0x01, 0x52, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x3a, + 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x22, + 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, + 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x46, 0x0a, 0x11, 0x73, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, + 0x0b, 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, 0x0f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, + 0x41, 0x74, 0x4a, 0x04, 0x08, 0x07, 0x10, 0x08, 0x52, 0x12, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, + 0x70, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x69, 0x64, 0x73, 0x22, 0xbc, 0x04, 0x0a, + 0x19, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4a, 0x0a, 0x0a, 0x65, 0x6e, + 0x74, 0x69, 0x74, 0x79, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, + 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, + 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, + 0x73, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x09, 0x65, 0x6e, 0x74, + 0x69, 0x74, 0x79, 0x49, 0x64, 0x73, 0x12, 0xf7, 0x01, 0x0a, 0x11, 0x6e, 0x6f, 0x74, 0x69, 0x66, + 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x42, 0xc9, 0x01, 0xfa, 0x42, 0xc5, 0x01, 0x72, 0xc2, 0x01, 0x52, 0x07, 0x75, 0x6e, + 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x52, 0x0f, 0x61, 0x70, 0x69, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x63, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x52, 0x0f, 0x61, 0x70, 0x69, 0x5f, 0x6b, 0x65, 0x79, 0x5f, + 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x52, 0x10, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, + 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, 0x52, 0x14, 0x63, 0x6f, 0x6c, 0x6c, 0x61, + 0x62, 0x6f, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x52, + 0x14, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x63, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x64, 0x52, 0x0a, 0x69, 0x6e, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x52, 0x0b, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x10, + 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, + 0x52, 0x12, 0x74, 0x65, 0x6d, 0x70, 0x6f, 0x72, 0x61, 0x72, 0x79, 0x5f, 0x70, 0x61, 0x73, 0x73, + 0x77, 0x6f, 0x72, 0x64, 0x52, 0x0e, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x65, 0x64, 0x52, 0x08, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x52, 0x10, + 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, + 0x12, 0x28, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x3e, 0x0a, 0x0a, 0x73, 0x65, + 0x6e, 0x64, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, + 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, + 0x55, 0x73, 0x65, 0x72, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x52, + 0x09, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x49, 0x64, 0x73, 0x12, 0x55, 0x0a, 0x09, 0x72, 0x65, + 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x24, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4e, - 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x42, 0x0f, 0xfa, 0x42, 0x0c, 0x92, 0x01, 0x09, 0x18, 0x01, 0x22, 0x05, 0x82, 0x01, 0x02, - 0x10, 0x01, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1e, 0x0a, 0x05, 0x6c, 0x69, - 0x6d, 0x69, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x2a, 0x03, - 0x18, 0xe8, 0x07, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, - 0x67, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x70, 0x61, 0x67, 0x65, 0x3a, 0x08, - 0xf2, 0xaa, 0x19, 0x04, 0x08, 0x00, 0x10, 0x01, 0x22, 0x5f, 0x0a, 0x19, 0x4c, 0x69, 0x73, 0x74, - 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x42, 0x0a, 0x0d, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x74, - 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4e, 0x6f, - 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0d, 0x6e, 0x6f, 0x74, 0x69, - 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0xe7, 0x01, 0x0a, 0x1f, 0x55, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4c, 0x0a, - 0x0c, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, - 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, - 0x69, 0x65, 0x72, 0x73, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x0b, - 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x49, 0x64, 0x73, 0x12, 0x26, 0x0a, 0x03, 0x69, - 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x42, 0x14, 0xfa, 0x42, 0x11, 0x92, 0x01, 0x0e, - 0x08, 0x01, 0x10, 0xe8, 0x07, 0x18, 0x01, 0x22, 0x05, 0x72, 0x03, 0x98, 0x01, 0x24, 0x52, 0x03, - 0x69, 0x64, 0x73, 0x12, 0x44, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x0e, 0x32, 0x22, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, + 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x63, 0x65, 0x69, + 0x76, 0x65, 0x72, 0x42, 0x11, 0xfa, 0x42, 0x0e, 0x92, 0x01, 0x0b, 0x08, 0x01, 0x18, 0x01, 0x22, + 0x05, 0x82, 0x01, 0x02, 0x10, 0x01, 0x52, 0x09, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, + 0x73, 0x12, 0x18, 0x0a, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, + 0x42, 0x02, 0x18, 0x01, 0x52, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x22, 0x2c, 0x0a, 0x1a, 0x43, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0xf3, 0x01, 0x0a, 0x18, 0x4c, 0x69, + 0x73, 0x74, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4c, 0x0a, 0x0c, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, + 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x74, + 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x55, 0x73, + 0x65, 0x72, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x42, 0x08, 0xfa, + 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x0b, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, + 0x72, 0x49, 0x64, 0x73, 0x12, 0x4b, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, + 0x20, 0x03, 0x28, 0x0e, 0x32, 0x22, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, + 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x42, 0x0f, 0xfa, 0x42, 0x0c, 0x92, 0x01, 0x09, + 0x18, 0x01, 0x22, 0x05, 0x82, 0x01, 0x02, 0x10, 0x01, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x12, 0x1e, 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, + 0x42, 0x08, 0xfa, 0x42, 0x05, 0x2a, 0x03, 0x18, 0xe8, 0x07, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, + 0x74, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x67, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x04, 0x70, 0x61, 0x67, 0x65, 0x3a, 0x08, 0xf2, 0xaa, 0x19, 0x04, 0x08, 0x00, 0x10, 0x01, 0x22, + 0x5f, 0x0a, 0x19, 0x4c, 0x69, 0x73, 0x74, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x42, 0x0a, 0x0d, + 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x82, 0x01, 0x02, 0x10, - 0x01, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x3a, 0x08, 0xf2, 0xaa, 0x19, 0x04, 0x08, - 0x00, 0x10, 0x01, 0x22, 0x8e, 0x01, 0x0a, 0x1e, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x53, 0x74, - 0x61, 0x74, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, - 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x35, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, - 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x42, 0x08, 0xfa, 0x42, - 0x05, 0x82, 0x01, 0x02, 0x10, 0x01, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x35, 0x0a, - 0x11, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, - 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x72, 0x03, 0x18, - 0x80, 0x01, 0x52, 0x10, 0x73, 0x74, 0x61, 0x74, 0x65, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x69, 0x6f, 0x6e, 0x2a, 0xdf, 0x01, 0x0a, 0x14, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x12, 0x21, 0x0a, - 0x1d, 0x4e, 0x4f, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x52, 0x45, - 0x43, 0x45, 0x49, 0x56, 0x45, 0x52, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, - 0x12, 0x26, 0x0a, 0x22, 0x4e, 0x4f, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, - 0x5f, 0x52, 0x45, 0x43, 0x45, 0x49, 0x56, 0x45, 0x52, 0x5f, 0x43, 0x4f, 0x4c, 0x4c, 0x41, 0x42, - 0x4f, 0x52, 0x41, 0x54, 0x4f, 0x52, 0x10, 0x01, 0x12, 0x30, 0x0a, 0x2c, 0x4e, 0x4f, 0x54, 0x49, - 0x46, 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x52, 0x45, 0x43, 0x45, 0x49, 0x56, 0x45, - 0x52, 0x5f, 0x41, 0x44, 0x4d, 0x49, 0x4e, 0x49, 0x53, 0x54, 0x52, 0x41, 0x54, 0x49, 0x56, 0x45, - 0x5f, 0x43, 0x4f, 0x4e, 0x54, 0x41, 0x43, 0x54, 0x10, 0x03, 0x12, 0x2b, 0x0a, 0x27, 0x4e, 0x4f, - 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x52, 0x45, 0x43, 0x45, 0x49, - 0x56, 0x45, 0x52, 0x5f, 0x54, 0x45, 0x43, 0x48, 0x4e, 0x49, 0x43, 0x41, 0x4c, 0x5f, 0x43, 0x4f, - 0x4e, 0x54, 0x41, 0x43, 0x54, 0x10, 0x04, 0x1a, 0x1d, 0xea, 0xaa, 0x19, 0x19, 0x18, 0x01, 0x2a, - 0x15, 0x4e, 0x4f, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x52, 0x45, - 0x43, 0x45, 0x49, 0x56, 0x45, 0x52, 0x2a, 0x91, 0x01, 0x0a, 0x12, 0x4e, 0x6f, 0x74, 0x69, 0x66, - 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1e, 0x0a, - 0x1a, 0x4e, 0x4f, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x54, - 0x41, 0x54, 0x55, 0x53, 0x5f, 0x55, 0x4e, 0x53, 0x45, 0x45, 0x4e, 0x10, 0x00, 0x12, 0x1c, 0x0a, - 0x18, 0x4e, 0x4f, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x54, - 0x41, 0x54, 0x55, 0x53, 0x5f, 0x53, 0x45, 0x45, 0x4e, 0x10, 0x01, 0x12, 0x20, 0x0a, 0x1c, 0x4e, - 0x4f, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x54, 0x41, 0x54, - 0x55, 0x53, 0x5f, 0x41, 0x52, 0x43, 0x48, 0x49, 0x56, 0x45, 0x44, 0x10, 0x02, 0x1a, 0x1b, 0xea, - 0xaa, 0x19, 0x17, 0x18, 0x01, 0x2a, 0x13, 0x4e, 0x4f, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, - 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x32, 0xc2, 0x03, 0x0a, 0x13, 0x4e, - 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, - 0x63, 0x65, 0x12, 0x5f, 0x0a, 0x06, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x12, 0x29, 0x2e, 0x74, - 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x43, 0x72, - 0x65, 0x61, 0x74, 0x65, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, - 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4e, - 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x92, 0x01, 0x0a, 0x04, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x28, 0x2e, 0x74, + 0x6e, 0x52, 0x0d, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x22, 0xe7, 0x01, 0x0a, 0x1f, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4e, 0x6f, 0x74, 0x69, 0x66, + 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x4c, 0x0a, 0x0c, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, + 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x74, 0x74, 0x6e, + 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x55, 0x73, 0x65, 0x72, + 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x42, 0x08, 0xfa, 0x42, 0x05, + 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x0b, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x49, + 0x64, 0x73, 0x12, 0x26, 0x0a, 0x03, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x42, + 0x14, 0xfa, 0x42, 0x11, 0x92, 0x01, 0x0e, 0x08, 0x01, 0x10, 0xe8, 0x07, 0x18, 0x01, 0x22, 0x05, + 0x72, 0x03, 0x98, 0x01, 0x24, 0x52, 0x03, 0x69, 0x64, 0x73, 0x12, 0x44, 0x0a, 0x06, 0x73, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x22, 0x2e, 0x74, 0x74, 0x6e, + 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4e, 0x6f, 0x74, 0x69, + 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x42, 0x08, + 0xfa, 0x42, 0x05, 0x82, 0x01, 0x02, 0x10, 0x01, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x3a, 0x08, 0xf2, 0xaa, 0x19, 0x04, 0x08, 0x00, 0x10, 0x01, 0x22, 0x8e, 0x01, 0x0a, 0x1e, 0x45, + 0x6e, 0x74, 0x69, 0x74, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, + 0x64, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x35, 0x0a, + 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x74, + 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x53, 0x74, + 0x61, 0x74, 0x65, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x82, 0x01, 0x02, 0x10, 0x01, 0x52, 0x05, 0x73, + 0x74, 0x61, 0x74, 0x65, 0x12, 0x35, 0x0a, 0x11, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x64, 0x65, + 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, + 0x08, 0xfa, 0x42, 0x05, 0x72, 0x03, 0x18, 0x80, 0x01, 0x52, 0x10, 0x73, 0x74, 0x61, 0x74, 0x65, + 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x2a, 0x8c, 0x02, 0x0a, 0x10, + 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, + 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x13, 0x0a, + 0x0f, 0x41, 0x50, 0x49, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x44, + 0x10, 0x01, 0x12, 0x13, 0x0a, 0x0f, 0x41, 0x50, 0x49, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x43, 0x48, + 0x41, 0x4e, 0x47, 0x45, 0x44, 0x10, 0x02, 0x12, 0x14, 0x0a, 0x10, 0x43, 0x4c, 0x49, 0x45, 0x4e, + 0x54, 0x5f, 0x52, 0x45, 0x51, 0x55, 0x45, 0x53, 0x54, 0x45, 0x44, 0x10, 0x03, 0x12, 0x18, 0x0a, + 0x14, 0x43, 0x4f, 0x4c, 0x4c, 0x41, 0x42, 0x4f, 0x52, 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x43, 0x48, + 0x41, 0x4e, 0x47, 0x45, 0x44, 0x10, 0x04, 0x12, 0x18, 0x0a, 0x14, 0x45, 0x4e, 0x54, 0x49, 0x54, + 0x59, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x43, 0x48, 0x41, 0x4e, 0x47, 0x45, 0x44, 0x10, + 0x05, 0x12, 0x0e, 0x0a, 0x0a, 0x49, 0x4e, 0x56, 0x49, 0x54, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x10, + 0x06, 0x12, 0x0f, 0x0a, 0x0b, 0x4c, 0x4f, 0x47, 0x49, 0x4e, 0x5f, 0x54, 0x4f, 0x4b, 0x45, 0x4e, + 0x10, 0x07, 0x12, 0x14, 0x0a, 0x10, 0x50, 0x41, 0x53, 0x53, 0x57, 0x4f, 0x52, 0x44, 0x5f, 0x43, + 0x48, 0x41, 0x4e, 0x47, 0x45, 0x44, 0x10, 0x08, 0x12, 0x16, 0x0a, 0x12, 0x54, 0x45, 0x4d, 0x50, + 0x4f, 0x52, 0x41, 0x52, 0x59, 0x5f, 0x50, 0x41, 0x53, 0x53, 0x57, 0x4f, 0x52, 0x44, 0x10, 0x09, + 0x12, 0x12, 0x0a, 0x0e, 0x55, 0x53, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x51, 0x55, 0x45, 0x53, 0x54, + 0x45, 0x44, 0x10, 0x0a, 0x12, 0x0c, 0x0a, 0x08, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x41, 0x54, 0x45, + 0x10, 0x0b, 0x1a, 0x06, 0xea, 0xaa, 0x19, 0x02, 0x18, 0x01, 0x2a, 0xdf, 0x01, 0x0a, 0x14, 0x4e, + 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x63, 0x65, 0x69, + 0x76, 0x65, 0x72, 0x12, 0x21, 0x0a, 0x1d, 0x4e, 0x4f, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, + 0x49, 0x4f, 0x4e, 0x5f, 0x52, 0x45, 0x43, 0x45, 0x49, 0x56, 0x45, 0x52, 0x5f, 0x55, 0x4e, 0x4b, + 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x26, 0x0a, 0x22, 0x4e, 0x4f, 0x54, 0x49, 0x46, 0x49, + 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x52, 0x45, 0x43, 0x45, 0x49, 0x56, 0x45, 0x52, 0x5f, + 0x43, 0x4f, 0x4c, 0x4c, 0x41, 0x42, 0x4f, 0x52, 0x41, 0x54, 0x4f, 0x52, 0x10, 0x01, 0x12, 0x30, + 0x0a, 0x2c, 0x4e, 0x4f, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x52, + 0x45, 0x43, 0x45, 0x49, 0x56, 0x45, 0x52, 0x5f, 0x41, 0x44, 0x4d, 0x49, 0x4e, 0x49, 0x53, 0x54, + 0x52, 0x41, 0x54, 0x49, 0x56, 0x45, 0x5f, 0x43, 0x4f, 0x4e, 0x54, 0x41, 0x43, 0x54, 0x10, 0x03, + 0x12, 0x2b, 0x0a, 0x27, 0x4e, 0x4f, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, + 0x5f, 0x52, 0x45, 0x43, 0x45, 0x49, 0x56, 0x45, 0x52, 0x5f, 0x54, 0x45, 0x43, 0x48, 0x4e, 0x49, + 0x43, 0x41, 0x4c, 0x5f, 0x43, 0x4f, 0x4e, 0x54, 0x41, 0x43, 0x54, 0x10, 0x04, 0x1a, 0x1d, 0xea, + 0xaa, 0x19, 0x19, 0x18, 0x01, 0x2a, 0x15, 0x4e, 0x4f, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, + 0x49, 0x4f, 0x4e, 0x5f, 0x52, 0x45, 0x43, 0x45, 0x49, 0x56, 0x45, 0x52, 0x2a, 0x91, 0x01, 0x0a, + 0x12, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x12, 0x1e, 0x0a, 0x1a, 0x4e, 0x4f, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, + 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x55, 0x4e, 0x53, 0x45, 0x45, + 0x4e, 0x10, 0x00, 0x12, 0x1c, 0x0a, 0x18, 0x4e, 0x4f, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, + 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x53, 0x45, 0x45, 0x4e, 0x10, + 0x01, 0x12, 0x20, 0x0a, 0x1c, 0x4e, 0x4f, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, + 0x4e, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x41, 0x52, 0x43, 0x48, 0x49, 0x56, 0x45, + 0x44, 0x10, 0x02, 0x1a, 0x1b, 0xea, 0xaa, 0x19, 0x17, 0x18, 0x01, 0x2a, 0x13, 0x4e, 0x4f, 0x54, + 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, + 0x32, 0xc2, 0x03, 0x0a, 0x13, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x5f, 0x0a, 0x06, 0x43, 0x72, 0x65, 0x61, + 0x74, 0x65, 0x12, 0x29, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, + 0x2e, 0x76, 0x33, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, + 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x43, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x92, 0x01, 0x0a, 0x04, 0x4c, 0x69, + 0x73, 0x74, 0x12, 0x28, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, + 0x2e, 0x76, 0x33, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, - 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4e, 0x6f, 0x74, 0x69, - 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x35, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2f, 0x5a, 0x2d, 0x12, 0x2b, 0x2f, 0x75, 0x73, - 0x65, 0x72, 0x73, 0x2f, 0x7b, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x5f, 0x69, 0x64, - 0x73, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x6e, 0x6f, 0x74, 0x69, 0x66, - 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x8f, 0x01, 0x0a, 0x0c, 0x55, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x2f, 0x2e, 0x74, 0x74, 0x6e, 0x2e, - 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, - 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, - 0x74, 0x79, 0x22, 0x36, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x30, 0x3a, 0x01, 0x2a, 0x32, 0x2b, 0x2f, - 0x75, 0x73, 0x65, 0x72, 0x73, 0x2f, 0x7b, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x5f, - 0x69, 0x64, 0x73, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x6e, 0x6f, 0x74, - 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x23, 0x92, 0x41, 0x20, 0x12, - 0x1e, 0x52, 0x65, 0x61, 0x64, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, - 0x20, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 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, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x35, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2f, 0x5a, + 0x2d, 0x12, 0x2b, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x73, 0x2f, 0x7b, 0x72, 0x65, 0x63, 0x65, 0x69, + 0x76, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x7d, + 0x2f, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x8f, + 0x01, 0x0a, 0x0c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, + 0x2f, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, + 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x36, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x30, + 0x3a, 0x01, 0x2a, 0x32, 0x2b, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x73, 0x2f, 0x7b, 0x72, 0x65, 0x63, + 0x65, 0x69, 0x76, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, + 0x64, 0x7d, 0x2f, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x1a, 0x23, 0x92, 0x41, 0x20, 0x12, 0x1e, 0x52, 0x65, 0x61, 0x64, 0x20, 0x61, 0x6e, 0x64, 0x20, + 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x20, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x2e, 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 ( @@ -865,49 +990,50 @@ func file_ttn_lorawan_v3_notification_service_proto_rawDescGZIP() []byte { return file_ttn_lorawan_v3_notification_service_proto_rawDescData } -var file_ttn_lorawan_v3_notification_service_proto_enumTypes = make([]protoimpl.EnumInfo, 2) +var file_ttn_lorawan_v3_notification_service_proto_enumTypes = make([]protoimpl.EnumInfo, 3) var file_ttn_lorawan_v3_notification_service_proto_msgTypes = make([]protoimpl.MessageInfo, 7) var file_ttn_lorawan_v3_notification_service_proto_goTypes = []interface{}{ - (NotificationReceiver)(0), // 0: ttn.lorawan.v3.NotificationReceiver - (NotificationStatus)(0), // 1: ttn.lorawan.v3.NotificationStatus - (*Notification)(nil), // 2: ttn.lorawan.v3.Notification - (*CreateNotificationRequest)(nil), // 3: ttn.lorawan.v3.CreateNotificationRequest - (*CreateNotificationResponse)(nil), // 4: ttn.lorawan.v3.CreateNotificationResponse - (*ListNotificationsRequest)(nil), // 5: ttn.lorawan.v3.ListNotificationsRequest - (*ListNotificationsResponse)(nil), // 6: ttn.lorawan.v3.ListNotificationsResponse - (*UpdateNotificationStatusRequest)(nil), // 7: ttn.lorawan.v3.UpdateNotificationStatusRequest - (*EntityStateChangedNotification)(nil), // 8: ttn.lorawan.v3.EntityStateChangedNotification - (*timestamppb.Timestamp)(nil), // 9: google.protobuf.Timestamp - (*EntityIdentifiers)(nil), // 10: ttn.lorawan.v3.EntityIdentifiers - (*anypb.Any)(nil), // 11: google.protobuf.Any - (*UserIdentifiers)(nil), // 12: ttn.lorawan.v3.UserIdentifiers - (State)(0), // 13: ttn.lorawan.v3.State - (*emptypb.Empty)(nil), // 14: google.protobuf.Empty + (NotificationType)(0), // 0: ttn.lorawan.v3.NotificationType + (NotificationReceiver)(0), // 1: ttn.lorawan.v3.NotificationReceiver + (NotificationStatus)(0), // 2: ttn.lorawan.v3.NotificationStatus + (*Notification)(nil), // 3: ttn.lorawan.v3.Notification + (*CreateNotificationRequest)(nil), // 4: ttn.lorawan.v3.CreateNotificationRequest + (*CreateNotificationResponse)(nil), // 5: ttn.lorawan.v3.CreateNotificationResponse + (*ListNotificationsRequest)(nil), // 6: ttn.lorawan.v3.ListNotificationsRequest + (*ListNotificationsResponse)(nil), // 7: ttn.lorawan.v3.ListNotificationsResponse + (*UpdateNotificationStatusRequest)(nil), // 8: ttn.lorawan.v3.UpdateNotificationStatusRequest + (*EntityStateChangedNotification)(nil), // 9: ttn.lorawan.v3.EntityStateChangedNotification + (*timestamppb.Timestamp)(nil), // 10: google.protobuf.Timestamp + (*EntityIdentifiers)(nil), // 11: ttn.lorawan.v3.EntityIdentifiers + (*anypb.Any)(nil), // 12: google.protobuf.Any + (*UserIdentifiers)(nil), // 13: ttn.lorawan.v3.UserIdentifiers + (State)(0), // 14: ttn.lorawan.v3.State + (*emptypb.Empty)(nil), // 15: google.protobuf.Empty } var file_ttn_lorawan_v3_notification_service_proto_depIdxs = []int32{ - 9, // 0: ttn.lorawan.v3.Notification.created_at:type_name -> google.protobuf.Timestamp - 10, // 1: ttn.lorawan.v3.Notification.entity_ids:type_name -> ttn.lorawan.v3.EntityIdentifiers - 11, // 2: ttn.lorawan.v3.Notification.data:type_name -> google.protobuf.Any - 12, // 3: ttn.lorawan.v3.Notification.sender_ids:type_name -> ttn.lorawan.v3.UserIdentifiers - 0, // 4: ttn.lorawan.v3.Notification.receivers:type_name -> ttn.lorawan.v3.NotificationReceiver - 1, // 5: ttn.lorawan.v3.Notification.status:type_name -> ttn.lorawan.v3.NotificationStatus - 9, // 6: ttn.lorawan.v3.Notification.status_updated_at:type_name -> google.protobuf.Timestamp - 10, // 7: ttn.lorawan.v3.CreateNotificationRequest.entity_ids:type_name -> ttn.lorawan.v3.EntityIdentifiers - 11, // 8: ttn.lorawan.v3.CreateNotificationRequest.data:type_name -> google.protobuf.Any - 12, // 9: ttn.lorawan.v3.CreateNotificationRequest.sender_ids:type_name -> ttn.lorawan.v3.UserIdentifiers - 0, // 10: ttn.lorawan.v3.CreateNotificationRequest.receivers:type_name -> ttn.lorawan.v3.NotificationReceiver - 12, // 11: ttn.lorawan.v3.ListNotificationsRequest.receiver_ids:type_name -> ttn.lorawan.v3.UserIdentifiers - 1, // 12: ttn.lorawan.v3.ListNotificationsRequest.status:type_name -> ttn.lorawan.v3.NotificationStatus - 2, // 13: ttn.lorawan.v3.ListNotificationsResponse.notifications:type_name -> ttn.lorawan.v3.Notification - 12, // 14: ttn.lorawan.v3.UpdateNotificationStatusRequest.receiver_ids:type_name -> ttn.lorawan.v3.UserIdentifiers - 1, // 15: ttn.lorawan.v3.UpdateNotificationStatusRequest.status:type_name -> ttn.lorawan.v3.NotificationStatus - 13, // 16: ttn.lorawan.v3.EntityStateChangedNotification.state:type_name -> ttn.lorawan.v3.State - 3, // 17: ttn.lorawan.v3.NotificationService.Create:input_type -> ttn.lorawan.v3.CreateNotificationRequest - 5, // 18: ttn.lorawan.v3.NotificationService.List:input_type -> ttn.lorawan.v3.ListNotificationsRequest - 7, // 19: ttn.lorawan.v3.NotificationService.UpdateStatus:input_type -> ttn.lorawan.v3.UpdateNotificationStatusRequest - 4, // 20: ttn.lorawan.v3.NotificationService.Create:output_type -> ttn.lorawan.v3.CreateNotificationResponse - 6, // 21: ttn.lorawan.v3.NotificationService.List:output_type -> ttn.lorawan.v3.ListNotificationsResponse - 14, // 22: ttn.lorawan.v3.NotificationService.UpdateStatus:output_type -> google.protobuf.Empty + 10, // 0: ttn.lorawan.v3.Notification.created_at:type_name -> google.protobuf.Timestamp + 11, // 1: ttn.lorawan.v3.Notification.entity_ids:type_name -> ttn.lorawan.v3.EntityIdentifiers + 12, // 2: ttn.lorawan.v3.Notification.data:type_name -> google.protobuf.Any + 13, // 3: ttn.lorawan.v3.Notification.sender_ids:type_name -> ttn.lorawan.v3.UserIdentifiers + 1, // 4: ttn.lorawan.v3.Notification.receivers:type_name -> ttn.lorawan.v3.NotificationReceiver + 2, // 5: ttn.lorawan.v3.Notification.status:type_name -> ttn.lorawan.v3.NotificationStatus + 10, // 6: ttn.lorawan.v3.Notification.status_updated_at:type_name -> google.protobuf.Timestamp + 11, // 7: ttn.lorawan.v3.CreateNotificationRequest.entity_ids:type_name -> ttn.lorawan.v3.EntityIdentifiers + 12, // 8: ttn.lorawan.v3.CreateNotificationRequest.data:type_name -> google.protobuf.Any + 13, // 9: ttn.lorawan.v3.CreateNotificationRequest.sender_ids:type_name -> ttn.lorawan.v3.UserIdentifiers + 1, // 10: ttn.lorawan.v3.CreateNotificationRequest.receivers:type_name -> ttn.lorawan.v3.NotificationReceiver + 13, // 11: ttn.lorawan.v3.ListNotificationsRequest.receiver_ids:type_name -> ttn.lorawan.v3.UserIdentifiers + 2, // 12: ttn.lorawan.v3.ListNotificationsRequest.status:type_name -> ttn.lorawan.v3.NotificationStatus + 3, // 13: ttn.lorawan.v3.ListNotificationsResponse.notifications:type_name -> ttn.lorawan.v3.Notification + 13, // 14: ttn.lorawan.v3.UpdateNotificationStatusRequest.receiver_ids:type_name -> ttn.lorawan.v3.UserIdentifiers + 2, // 15: ttn.lorawan.v3.UpdateNotificationStatusRequest.status:type_name -> ttn.lorawan.v3.NotificationStatus + 14, // 16: ttn.lorawan.v3.EntityStateChangedNotification.state:type_name -> ttn.lorawan.v3.State + 4, // 17: ttn.lorawan.v3.NotificationService.Create:input_type -> ttn.lorawan.v3.CreateNotificationRequest + 6, // 18: ttn.lorawan.v3.NotificationService.List:input_type -> ttn.lorawan.v3.ListNotificationsRequest + 8, // 19: ttn.lorawan.v3.NotificationService.UpdateStatus:input_type -> ttn.lorawan.v3.UpdateNotificationStatusRequest + 5, // 20: ttn.lorawan.v3.NotificationService.Create:output_type -> ttn.lorawan.v3.CreateNotificationResponse + 7, // 21: ttn.lorawan.v3.NotificationService.List:output_type -> ttn.lorawan.v3.ListNotificationsResponse + 15, // 22: ttn.lorawan.v3.NotificationService.UpdateStatus:output_type -> google.protobuf.Empty 20, // [20:23] is the sub-list for method output_type 17, // [17:20] is the sub-list for method input_type 17, // [17:17] is the sub-list for extension type_name @@ -1013,7 +1139,7 @@ func file_ttn_lorawan_v3_notification_service_proto_init() { File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_ttn_lorawan_v3_notification_service_proto_rawDesc, - NumEnums: 2, + NumEnums: 3, NumMessages: 7, NumExtensions: 0, NumServices: 1, diff --git a/pkg/ttnpb/notification_service.pb.validate.go b/pkg/ttnpb/notification_service.pb.validate.go index 948de60a99..7bdd8b6c06 100644 --- a/pkg/ttnpb/notification_service.pb.validate.go +++ b/pkg/ttnpb/notification_service.pb.validate.go @@ -74,7 +74,14 @@ func (m *Notification) ValidateFields(paths ...string) error { } case "notification_type": - // no validation rules for NotificationType + + if _, ok := _Notification_NotificationType_InLookup[m.GetNotificationType()]; !ok { + return NotificationValidationError{ + field: "notification_type", + reason: "value must be in list [unknown api_key_created api_key_changed client_requested collaborator_changed entity_state_changed invitation login_token password_changed temporary_password user_requested validate]", + } + } + case "data": if v, ok := interface{}(m.GetData()).(interface{ ValidateFields(...string) error }); ok { @@ -181,6 +188,21 @@ var _ interface { ErrorName() string } = NotificationValidationError{} +var _Notification_NotificationType_InLookup = map[string]struct{}{ + "unknown": {}, + "api_key_created": {}, + "api_key_changed": {}, + "client_requested": {}, + "collaborator_changed": {}, + "entity_state_changed": {}, + "invitation": {}, + "login_token": {}, + "password_changed": {}, + "temporary_password": {}, + "user_requested": {}, + "validate": {}, +} + // ValidateFields checks the field values on CreateNotificationRequest with the // rules defined in the proto definition for this message. If any rules are // violated, an error is returned. @@ -217,10 +239,10 @@ func (m *CreateNotificationRequest) ValidateFields(paths ...string) error { case "notification_type": - if l := utf8.RuneCountInString(m.GetNotificationType()); l < 1 || l > 100 { + if _, ok := _CreateNotificationRequest_NotificationType_InLookup[m.GetNotificationType()]; !ok { return CreateNotificationRequestValidationError{ field: "notification_type", - reason: "value length must be between 1 and 100 runes, inclusive", + reason: "value must be in list [unknown api_key_created api_key_changed client_requested collaborator_changed entity_state_changed invitation login_token password_changed temporary_password user_requested validate]", } } @@ -349,6 +371,21 @@ var _ interface { ErrorName() string } = CreateNotificationRequestValidationError{} +var _CreateNotificationRequest_NotificationType_InLookup = map[string]struct{}{ + "unknown": {}, + "api_key_created": {}, + "api_key_changed": {}, + "client_requested": {}, + "collaborator_changed": {}, + "entity_state_changed": {}, + "invitation": {}, + "login_token": {}, + "password_changed": {}, + "temporary_password": {}, + "user_requested": {}, + "validate": {}, +} + // ValidateFields checks the field values on CreateNotificationResponse 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/notification_service_json.pb.go b/pkg/ttnpb/notification_service_json.pb.go index 561dc7b7c6..4fd2bc31b1 100644 --- a/pkg/ttnpb/notification_service_json.pb.go +++ b/pkg/ttnpb/notification_service_json.pb.go @@ -11,6 +11,46 @@ import ( jsonplugin "github.com/TheThingsIndustries/protoc-gen-go-json/jsonplugin" ) +// MarshalProtoJSON marshals the NotificationType to JSON. +func (x NotificationType) MarshalProtoJSON(s *jsonplugin.MarshalState) { + s.WriteEnumString(int32(x), NotificationType_name) +} + +// MarshalText marshals the NotificationType to text. +func (x NotificationType) MarshalText() ([]byte, error) { + return []byte(jsonplugin.GetEnumString(int32(x), NotificationType_name)), nil +} + +// MarshalJSON marshals the NotificationType to JSON. +func (x NotificationType) MarshalJSON() ([]byte, error) { + return jsonplugin.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the NotificationType from JSON. +func (x *NotificationType) UnmarshalProtoJSON(s *jsonplugin.UnmarshalState) { + v := s.ReadEnum(NotificationType_value) + if err := s.Err(); err != nil { + s.SetErrorf("could not read NotificationType enum: %v", err) + return + } + *x = NotificationType(v) +} + +// UnmarshalText unmarshals the NotificationType from text. +func (x *NotificationType) UnmarshalText(b []byte) error { + i, err := jsonplugin.ParseEnumString(string(b), NotificationType_value) + if err != nil { + return err + } + *x = NotificationType(i) + return nil +} + +// UnmarshalJSON unmarshals the NotificationType from JSON. +func (x *NotificationType) UnmarshalJSON(b []byte) error { + return jsonplugin.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + // MarshalProtoJSON marshals the NotificationReceiver to JSON. func (x NotificationReceiver) MarshalProtoJSON(s *jsonplugin.MarshalState) { s.WriteEnumString(int32(x), NotificationReceiver_name) diff --git a/pkg/ttnpb/rights.go b/pkg/ttnpb/rights.go index b77fa4aca5..6827b6b980 100644 --- a/pkg/ttnpb/rights.go +++ b/pkg/ttnpb/rights.go @@ -28,6 +28,7 @@ var ( AllGatewayRights = &Rights{} AllOrganizationRights = &Rights{} AllClusterRights = &Rights{} + AllReadAdminRights = &Rights{} AllAdminRights = &Rights{} AllRights = &Rights{} ) @@ -55,6 +56,12 @@ func init() { if strings.HasSuffix(k, "_READ") || strings.HasSuffix(k, "_INFO") { AllClusterRights.Rights = append(AllClusterRights.Rights, Right(v)) } + if strings.HasSuffix(k, "_READ") || + strings.HasSuffix(k, "_INFO") || + strings.HasSuffix(k, "_LIST") || + strings.HasSuffix(k, "_GET") { + AllReadAdminRights.Rights = append(AllReadAdminRights.Rights, Right(v)) + } if !strings.HasSuffix(k, "_KEYS") && !strings.HasSuffix(k, "_ALL") { AllAdminRights.Rights = append(AllAdminRights.Rights, Right(v)) } diff --git a/pkg/ttnpb/rights.pb.go b/pkg/ttnpb/rights.pb.go index 9583a7ef45..7af2a2a0de 100644 --- a/pkg/ttnpb/rights.pb.go +++ b/pkg/ttnpb/rights.pb.go @@ -47,10 +47,16 @@ const ( Right_RIGHT_USER_INFO Right = 1 // The right to edit basic user settings. Right_RIGHT_USER_SETTINGS_BASIC Right = 2 + // The right to list users accounts. + Right_RIGHT_USER_LIST Right = 87 + // The right to create an user account. + Right_RIGHT_USER_CREATE Right = 88 // The right to view and edit user API keys. Right_RIGHT_USER_SETTINGS_API_KEYS Right = 3 // The right to delete user account. Right_RIGHT_USER_DELETE Right = 4 + // The right to delete user account. + Right_RIGHT_USER_PURGE Right = 66 // The right to view and edit authorized OAuth clients of the user. Right_RIGHT_USER_AUTHORIZED_CLIENTS Right = 5 // The right to list applications the user is a collaborator of. @@ -85,6 +91,8 @@ const ( Right_RIGHT_APPLICATION_SETTINGS_PACKAGES Right = 56 // The right to delete application. Right_RIGHT_APPLICATION_DELETE Right = 19 + // The right to purge application. + Right_RIGHT_APPLICATION_PURGE Right = 64 // The right to view devices in application. Right_RIGHT_APPLICATION_DEVICES_READ Right = 20 // The right to create devices in application. @@ -118,6 +126,8 @@ const ( Right_RIGHT_CLIENT_SETTINGS_COLLABORATORS Right = 62 // The right to delete a client. Right_RIGHT_CLIENT_DELETE Right = 63 + // The right to purge a client. + Right_RIGHT_CLIENT_PURGE Right = 68 // The right to view gateway information. Right_RIGHT_GATEWAY_INFO Right = 30 // The right to edit basic gateway settings. @@ -128,6 +138,8 @@ const ( Right_RIGHT_GATEWAY_SETTINGS_COLLABORATORS Right = 33 // The right to delete gateway. Right_RIGHT_GATEWAY_DELETE Right = 34 + // The right to purge gateway. + Right_RIGHT_GATEWAY_PURGE Right = 67 // The right to read gateway traffic. Right_RIGHT_GATEWAY_TRAFFIC_READ Right = 35 // The right to write downlink gateway traffic. @@ -157,6 +169,8 @@ const ( Right_RIGHT_ORGANIZATION_SETTINGS_MEMBERS Right = 44 // The right to delete organization. Right_RIGHT_ORGANIZATION_DELETE Right = 45 + // The right to purge organization. + Right_RIGHT_ORGANIZATION_PURGE Right = 65 // The right to list the applications the organization is a collaborator of. Right_RIGHT_ORGANIZATION_APPLICATIONS_LIST Right = 46 // The right to create an application under the organization. @@ -186,8 +200,11 @@ var ( 0: "right_invalid", 1: "RIGHT_USER_INFO", 2: "RIGHT_USER_SETTINGS_BASIC", + 87: "RIGHT_USER_LIST", + 88: "RIGHT_USER_CREATE", 3: "RIGHT_USER_SETTINGS_API_KEYS", 4: "RIGHT_USER_DELETE", + 66: "RIGHT_USER_PURGE", 5: "RIGHT_USER_AUTHORIZED_CLIENTS", 6: "RIGHT_USER_APPLICATIONS_LIST", 7: "RIGHT_USER_APPLICATIONS_CREATE", @@ -205,6 +222,7 @@ var ( 18: "RIGHT_APPLICATION_SETTINGS_COLLABORATORS", 56: "RIGHT_APPLICATION_SETTINGS_PACKAGES", 19: "RIGHT_APPLICATION_DELETE", + 64: "RIGHT_APPLICATION_PURGE", 20: "RIGHT_APPLICATION_DEVICES_READ", 21: "RIGHT_APPLICATION_DEVICES_WRITE", 22: "RIGHT_APPLICATION_DEVICES_READ_KEYS", @@ -219,11 +237,13 @@ var ( 61: "RIGHT_CLIENT_SETTINGS_BASIC", 62: "RIGHT_CLIENT_SETTINGS_COLLABORATORS", 63: "RIGHT_CLIENT_DELETE", + 68: "RIGHT_CLIENT_PURGE", 30: "RIGHT_GATEWAY_INFO", 31: "RIGHT_GATEWAY_SETTINGS_BASIC", 32: "RIGHT_GATEWAY_SETTINGS_API_KEYS", 33: "RIGHT_GATEWAY_SETTINGS_COLLABORATORS", 34: "RIGHT_GATEWAY_DELETE", + 67: "RIGHT_GATEWAY_PURGE", 35: "RIGHT_GATEWAY_TRAFFIC_READ", 36: "RIGHT_GATEWAY_TRAFFIC_DOWN_WRITE", 37: "RIGHT_GATEWAY_LINK", @@ -237,6 +257,7 @@ var ( 43: "RIGHT_ORGANIZATION_SETTINGS_API_KEYS", 44: "RIGHT_ORGANIZATION_SETTINGS_MEMBERS", 45: "RIGHT_ORGANIZATION_DELETE", + 65: "RIGHT_ORGANIZATION_PURGE", 46: "RIGHT_ORGANIZATION_APPLICATIONS_LIST", 47: "RIGHT_ORGANIZATION_APPLICATIONS_CREATE", 48: "RIGHT_ORGANIZATION_GATEWAYS_LIST", @@ -252,8 +273,11 @@ var ( "right_invalid": 0, "RIGHT_USER_INFO": 1, "RIGHT_USER_SETTINGS_BASIC": 2, + "RIGHT_USER_LIST": 87, + "RIGHT_USER_CREATE": 88, "RIGHT_USER_SETTINGS_API_KEYS": 3, "RIGHT_USER_DELETE": 4, + "RIGHT_USER_PURGE": 66, "RIGHT_USER_AUTHORIZED_CLIENTS": 5, "RIGHT_USER_APPLICATIONS_LIST": 6, "RIGHT_USER_APPLICATIONS_CREATE": 7, @@ -271,6 +295,7 @@ var ( "RIGHT_APPLICATION_SETTINGS_COLLABORATORS": 18, "RIGHT_APPLICATION_SETTINGS_PACKAGES": 56, "RIGHT_APPLICATION_DELETE": 19, + "RIGHT_APPLICATION_PURGE": 64, "RIGHT_APPLICATION_DEVICES_READ": 20, "RIGHT_APPLICATION_DEVICES_WRITE": 21, "RIGHT_APPLICATION_DEVICES_READ_KEYS": 22, @@ -285,11 +310,13 @@ var ( "RIGHT_CLIENT_SETTINGS_BASIC": 61, "RIGHT_CLIENT_SETTINGS_COLLABORATORS": 62, "RIGHT_CLIENT_DELETE": 63, + "RIGHT_CLIENT_PURGE": 68, "RIGHT_GATEWAY_INFO": 30, "RIGHT_GATEWAY_SETTINGS_BASIC": 31, "RIGHT_GATEWAY_SETTINGS_API_KEYS": 32, "RIGHT_GATEWAY_SETTINGS_COLLABORATORS": 33, "RIGHT_GATEWAY_DELETE": 34, + "RIGHT_GATEWAY_PURGE": 67, "RIGHT_GATEWAY_TRAFFIC_READ": 35, "RIGHT_GATEWAY_TRAFFIC_DOWN_WRITE": 36, "RIGHT_GATEWAY_LINK": 37, @@ -303,6 +330,7 @@ var ( "RIGHT_ORGANIZATION_SETTINGS_API_KEYS": 43, "RIGHT_ORGANIZATION_SETTINGS_MEMBERS": 44, "RIGHT_ORGANIZATION_DELETE": 45, + "RIGHT_ORGANIZATION_PURGE": 65, "RIGHT_ORGANIZATION_APPLICATIONS_LIST": 46, "RIGHT_ORGANIZATION_APPLICATIONS_CREATE": 47, "RIGHT_ORGANIZATION_GATEWAYS_LIST": 48, @@ -766,92 +794,101 @@ var file_ttn_lorawan_v3_rights_proto_rawDesc = []byte{ 0x61, 0x62, 0x6f, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x62, 0x6f, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x0d, 0x63, - 0x6f, 0x6c, 0x6c, 0x61, 0x62, 0x6f, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x73, 0x2a, 0xdf, 0x10, 0x0a, + 0x6f, 0x6c, 0x6c, 0x61, 0x62, 0x6f, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x73, 0x2a, 0xf9, 0x12, 0x0a, 0x05, 0x52, 0x69, 0x67, 0x68, 0x74, 0x12, 0x11, 0x0a, 0x0d, 0x72, 0x69, 0x67, 0x68, 0x74, 0x5f, 0x69, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x10, 0x00, 0x12, 0x13, 0x0a, 0x0f, 0x52, 0x49, 0x47, 0x48, 0x54, 0x5f, 0x55, 0x53, 0x45, 0x52, 0x5f, 0x49, 0x4e, 0x46, 0x4f, 0x10, 0x01, 0x12, 0x1d, 0x0a, 0x19, 0x52, 0x49, 0x47, 0x48, 0x54, 0x5f, 0x55, 0x53, 0x45, 0x52, 0x5f, 0x53, 0x45, 0x54, - 0x54, 0x49, 0x4e, 0x47, 0x53, 0x5f, 0x42, 0x41, 0x53, 0x49, 0x43, 0x10, 0x02, 0x12, 0x20, 0x0a, - 0x1c, 0x52, 0x49, 0x47, 0x48, 0x54, 0x5f, 0x55, 0x53, 0x45, 0x52, 0x5f, 0x53, 0x45, 0x54, 0x54, - 0x49, 0x4e, 0x47, 0x53, 0x5f, 0x41, 0x50, 0x49, 0x5f, 0x4b, 0x45, 0x59, 0x53, 0x10, 0x03, 0x12, - 0x15, 0x0a, 0x11, 0x52, 0x49, 0x47, 0x48, 0x54, 0x5f, 0x55, 0x53, 0x45, 0x52, 0x5f, 0x44, 0x45, - 0x4c, 0x45, 0x54, 0x45, 0x10, 0x04, 0x12, 0x21, 0x0a, 0x1d, 0x52, 0x49, 0x47, 0x48, 0x54, 0x5f, - 0x55, 0x53, 0x45, 0x52, 0x5f, 0x41, 0x55, 0x54, 0x48, 0x4f, 0x52, 0x49, 0x5a, 0x45, 0x44, 0x5f, - 0x43, 0x4c, 0x49, 0x45, 0x4e, 0x54, 0x53, 0x10, 0x05, 0x12, 0x20, 0x0a, 0x1c, 0x52, 0x49, 0x47, - 0x48, 0x54, 0x5f, 0x55, 0x53, 0x45, 0x52, 0x5f, 0x41, 0x50, 0x50, 0x4c, 0x49, 0x43, 0x41, 0x54, - 0x49, 0x4f, 0x4e, 0x53, 0x5f, 0x4c, 0x49, 0x53, 0x54, 0x10, 0x06, 0x12, 0x22, 0x0a, 0x1e, 0x52, + 0x54, 0x49, 0x4e, 0x47, 0x53, 0x5f, 0x42, 0x41, 0x53, 0x49, 0x43, 0x10, 0x02, 0x12, 0x13, 0x0a, + 0x0f, 0x52, 0x49, 0x47, 0x48, 0x54, 0x5f, 0x55, 0x53, 0x45, 0x52, 0x5f, 0x4c, 0x49, 0x53, 0x54, + 0x10, 0x57, 0x12, 0x15, 0x0a, 0x11, 0x52, 0x49, 0x47, 0x48, 0x54, 0x5f, 0x55, 0x53, 0x45, 0x52, + 0x5f, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x10, 0x58, 0x12, 0x20, 0x0a, 0x1c, 0x52, 0x49, 0x47, + 0x48, 0x54, 0x5f, 0x55, 0x53, 0x45, 0x52, 0x5f, 0x53, 0x45, 0x54, 0x54, 0x49, 0x4e, 0x47, 0x53, + 0x5f, 0x41, 0x50, 0x49, 0x5f, 0x4b, 0x45, 0x59, 0x53, 0x10, 0x03, 0x12, 0x15, 0x0a, 0x11, 0x52, + 0x49, 0x47, 0x48, 0x54, 0x5f, 0x55, 0x53, 0x45, 0x52, 0x5f, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, + 0x10, 0x04, 0x12, 0x14, 0x0a, 0x10, 0x52, 0x49, 0x47, 0x48, 0x54, 0x5f, 0x55, 0x53, 0x45, 0x52, + 0x5f, 0x50, 0x55, 0x52, 0x47, 0x45, 0x10, 0x42, 0x12, 0x21, 0x0a, 0x1d, 0x52, 0x49, 0x47, 0x48, + 0x54, 0x5f, 0x55, 0x53, 0x45, 0x52, 0x5f, 0x41, 0x55, 0x54, 0x48, 0x4f, 0x52, 0x49, 0x5a, 0x45, + 0x44, 0x5f, 0x43, 0x4c, 0x49, 0x45, 0x4e, 0x54, 0x53, 0x10, 0x05, 0x12, 0x20, 0x0a, 0x1c, 0x52, 0x49, 0x47, 0x48, 0x54, 0x5f, 0x55, 0x53, 0x45, 0x52, 0x5f, 0x41, 0x50, 0x50, 0x4c, 0x49, 0x43, - 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x53, 0x5f, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x10, 0x07, 0x12, - 0x1c, 0x0a, 0x18, 0x52, 0x49, 0x47, 0x48, 0x54, 0x5f, 0x55, 0x53, 0x45, 0x52, 0x5f, 0x47, 0x41, - 0x54, 0x45, 0x57, 0x41, 0x59, 0x53, 0x5f, 0x4c, 0x49, 0x53, 0x54, 0x10, 0x08, 0x12, 0x1e, 0x0a, - 0x1a, 0x52, 0x49, 0x47, 0x48, 0x54, 0x5f, 0x55, 0x53, 0x45, 0x52, 0x5f, 0x47, 0x41, 0x54, 0x45, - 0x57, 0x41, 0x59, 0x53, 0x5f, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x10, 0x09, 0x12, 0x1b, 0x0a, - 0x17, 0x52, 0x49, 0x47, 0x48, 0x54, 0x5f, 0x55, 0x53, 0x45, 0x52, 0x5f, 0x43, 0x4c, 0x49, 0x45, - 0x4e, 0x54, 0x53, 0x5f, 0x4c, 0x49, 0x53, 0x54, 0x10, 0x0a, 0x12, 0x1d, 0x0a, 0x19, 0x52, 0x49, - 0x47, 0x48, 0x54, 0x5f, 0x55, 0x53, 0x45, 0x52, 0x5f, 0x43, 0x4c, 0x49, 0x45, 0x4e, 0x54, 0x53, - 0x5f, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x10, 0x0b, 0x12, 0x21, 0x0a, 0x1d, 0x52, 0x49, 0x47, - 0x48, 0x54, 0x5f, 0x55, 0x53, 0x45, 0x52, 0x5f, 0x4f, 0x52, 0x47, 0x41, 0x4e, 0x49, 0x5a, 0x41, - 0x54, 0x49, 0x4f, 0x4e, 0x53, 0x5f, 0x4c, 0x49, 0x53, 0x54, 0x10, 0x0c, 0x12, 0x23, 0x0a, 0x1f, - 0x52, 0x49, 0x47, 0x48, 0x54, 0x5f, 0x55, 0x53, 0x45, 0x52, 0x5f, 0x4f, 0x52, 0x47, 0x41, 0x4e, - 0x49, 0x5a, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x53, 0x5f, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x10, - 0x0d, 0x12, 0x21, 0x0a, 0x1d, 0x52, 0x49, 0x47, 0x48, 0x54, 0x5f, 0x55, 0x53, 0x45, 0x52, 0x5f, - 0x4e, 0x4f, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x53, 0x5f, 0x52, 0x45, - 0x41, 0x44, 0x10, 0x3b, 0x12, 0x12, 0x0a, 0x0e, 0x52, 0x49, 0x47, 0x48, 0x54, 0x5f, 0x55, 0x53, - 0x45, 0x52, 0x5f, 0x41, 0x4c, 0x4c, 0x10, 0x0e, 0x12, 0x1a, 0x0a, 0x16, 0x52, 0x49, 0x47, 0x48, - 0x54, 0x5f, 0x41, 0x50, 0x50, 0x4c, 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x49, 0x4e, - 0x46, 0x4f, 0x10, 0x0f, 0x12, 0x24, 0x0a, 0x20, 0x52, 0x49, 0x47, 0x48, 0x54, 0x5f, 0x41, 0x50, - 0x50, 0x4c, 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x45, 0x54, 0x54, 0x49, 0x4e, - 0x47, 0x53, 0x5f, 0x42, 0x41, 0x53, 0x49, 0x43, 0x10, 0x10, 0x12, 0x27, 0x0a, 0x23, 0x52, 0x49, + 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x53, 0x5f, 0x4c, 0x49, 0x53, 0x54, 0x10, 0x06, 0x12, 0x22, 0x0a, + 0x1e, 0x52, 0x49, 0x47, 0x48, 0x54, 0x5f, 0x55, 0x53, 0x45, 0x52, 0x5f, 0x41, 0x50, 0x50, 0x4c, + 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x53, 0x5f, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x10, + 0x07, 0x12, 0x1c, 0x0a, 0x18, 0x52, 0x49, 0x47, 0x48, 0x54, 0x5f, 0x55, 0x53, 0x45, 0x52, 0x5f, + 0x47, 0x41, 0x54, 0x45, 0x57, 0x41, 0x59, 0x53, 0x5f, 0x4c, 0x49, 0x53, 0x54, 0x10, 0x08, 0x12, + 0x1e, 0x0a, 0x1a, 0x52, 0x49, 0x47, 0x48, 0x54, 0x5f, 0x55, 0x53, 0x45, 0x52, 0x5f, 0x47, 0x41, + 0x54, 0x45, 0x57, 0x41, 0x59, 0x53, 0x5f, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x10, 0x09, 0x12, + 0x1b, 0x0a, 0x17, 0x52, 0x49, 0x47, 0x48, 0x54, 0x5f, 0x55, 0x53, 0x45, 0x52, 0x5f, 0x43, 0x4c, + 0x49, 0x45, 0x4e, 0x54, 0x53, 0x5f, 0x4c, 0x49, 0x53, 0x54, 0x10, 0x0a, 0x12, 0x1d, 0x0a, 0x19, + 0x52, 0x49, 0x47, 0x48, 0x54, 0x5f, 0x55, 0x53, 0x45, 0x52, 0x5f, 0x43, 0x4c, 0x49, 0x45, 0x4e, + 0x54, 0x53, 0x5f, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x10, 0x0b, 0x12, 0x21, 0x0a, 0x1d, 0x52, + 0x49, 0x47, 0x48, 0x54, 0x5f, 0x55, 0x53, 0x45, 0x52, 0x5f, 0x4f, 0x52, 0x47, 0x41, 0x4e, 0x49, + 0x5a, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x53, 0x5f, 0x4c, 0x49, 0x53, 0x54, 0x10, 0x0c, 0x12, 0x23, + 0x0a, 0x1f, 0x52, 0x49, 0x47, 0x48, 0x54, 0x5f, 0x55, 0x53, 0x45, 0x52, 0x5f, 0x4f, 0x52, 0x47, + 0x41, 0x4e, 0x49, 0x5a, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x53, 0x5f, 0x43, 0x52, 0x45, 0x41, 0x54, + 0x45, 0x10, 0x0d, 0x12, 0x21, 0x0a, 0x1d, 0x52, 0x49, 0x47, 0x48, 0x54, 0x5f, 0x55, 0x53, 0x45, + 0x52, 0x5f, 0x4e, 0x4f, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x53, 0x5f, + 0x52, 0x45, 0x41, 0x44, 0x10, 0x3b, 0x12, 0x12, 0x0a, 0x0e, 0x52, 0x49, 0x47, 0x48, 0x54, 0x5f, + 0x55, 0x53, 0x45, 0x52, 0x5f, 0x41, 0x4c, 0x4c, 0x10, 0x0e, 0x12, 0x1a, 0x0a, 0x16, 0x52, 0x49, 0x47, 0x48, 0x54, 0x5f, 0x41, 0x50, 0x50, 0x4c, 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, - 0x53, 0x45, 0x54, 0x54, 0x49, 0x4e, 0x47, 0x53, 0x5f, 0x41, 0x50, 0x49, 0x5f, 0x4b, 0x45, 0x59, - 0x53, 0x10, 0x11, 0x12, 0x2c, 0x0a, 0x28, 0x52, 0x49, 0x47, 0x48, 0x54, 0x5f, 0x41, 0x50, 0x50, + 0x49, 0x4e, 0x46, 0x4f, 0x10, 0x0f, 0x12, 0x24, 0x0a, 0x20, 0x52, 0x49, 0x47, 0x48, 0x54, 0x5f, + 0x41, 0x50, 0x50, 0x4c, 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x45, 0x54, 0x54, + 0x49, 0x4e, 0x47, 0x53, 0x5f, 0x42, 0x41, 0x53, 0x49, 0x43, 0x10, 0x10, 0x12, 0x27, 0x0a, 0x23, + 0x52, 0x49, 0x47, 0x48, 0x54, 0x5f, 0x41, 0x50, 0x50, 0x4c, 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, + 0x4e, 0x5f, 0x53, 0x45, 0x54, 0x54, 0x49, 0x4e, 0x47, 0x53, 0x5f, 0x41, 0x50, 0x49, 0x5f, 0x4b, + 0x45, 0x59, 0x53, 0x10, 0x11, 0x12, 0x2c, 0x0a, 0x28, 0x52, 0x49, 0x47, 0x48, 0x54, 0x5f, 0x41, + 0x50, 0x50, 0x4c, 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x45, 0x54, 0x54, 0x49, + 0x4e, 0x47, 0x53, 0x5f, 0x43, 0x4f, 0x4c, 0x4c, 0x41, 0x42, 0x4f, 0x52, 0x41, 0x54, 0x4f, 0x52, + 0x53, 0x10, 0x12, 0x12, 0x27, 0x0a, 0x23, 0x52, 0x49, 0x47, 0x48, 0x54, 0x5f, 0x41, 0x50, 0x50, 0x4c, 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x45, 0x54, 0x54, 0x49, 0x4e, 0x47, - 0x53, 0x5f, 0x43, 0x4f, 0x4c, 0x4c, 0x41, 0x42, 0x4f, 0x52, 0x41, 0x54, 0x4f, 0x52, 0x53, 0x10, - 0x12, 0x12, 0x27, 0x0a, 0x23, 0x52, 0x49, 0x47, 0x48, 0x54, 0x5f, 0x41, 0x50, 0x50, 0x4c, 0x49, - 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x45, 0x54, 0x54, 0x49, 0x4e, 0x47, 0x53, 0x5f, - 0x50, 0x41, 0x43, 0x4b, 0x41, 0x47, 0x45, 0x53, 0x10, 0x38, 0x12, 0x1c, 0x0a, 0x18, 0x52, 0x49, - 0x47, 0x48, 0x54, 0x5f, 0x41, 0x50, 0x50, 0x4c, 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, - 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, 0x10, 0x13, 0x12, 0x22, 0x0a, 0x1e, 0x52, 0x49, 0x47, 0x48, - 0x54, 0x5f, 0x41, 0x50, 0x50, 0x4c, 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x44, 0x45, - 0x56, 0x49, 0x43, 0x45, 0x53, 0x5f, 0x52, 0x45, 0x41, 0x44, 0x10, 0x14, 0x12, 0x23, 0x0a, 0x1f, + 0x53, 0x5f, 0x50, 0x41, 0x43, 0x4b, 0x41, 0x47, 0x45, 0x53, 0x10, 0x38, 0x12, 0x1c, 0x0a, 0x18, 0x52, 0x49, 0x47, 0x48, 0x54, 0x5f, 0x41, 0x50, 0x50, 0x4c, 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, - 0x4e, 0x5f, 0x44, 0x45, 0x56, 0x49, 0x43, 0x45, 0x53, 0x5f, 0x57, 0x52, 0x49, 0x54, 0x45, 0x10, - 0x15, 0x12, 0x27, 0x0a, 0x23, 0x52, 0x49, 0x47, 0x48, 0x54, 0x5f, 0x41, 0x50, 0x50, 0x4c, 0x49, - 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x44, 0x45, 0x56, 0x49, 0x43, 0x45, 0x53, 0x5f, 0x52, - 0x45, 0x41, 0x44, 0x5f, 0x4b, 0x45, 0x59, 0x53, 0x10, 0x16, 0x12, 0x28, 0x0a, 0x24, 0x52, 0x49, + 0x4e, 0x5f, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, 0x10, 0x13, 0x12, 0x1b, 0x0a, 0x17, 0x52, 0x49, 0x47, 0x48, 0x54, 0x5f, 0x41, 0x50, 0x50, 0x4c, 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, - 0x44, 0x45, 0x56, 0x49, 0x43, 0x45, 0x53, 0x5f, 0x57, 0x52, 0x49, 0x54, 0x45, 0x5f, 0x4b, 0x45, - 0x59, 0x53, 0x10, 0x17, 0x12, 0x22, 0x0a, 0x1e, 0x52, 0x49, 0x47, 0x48, 0x54, 0x5f, 0x41, 0x50, - 0x50, 0x4c, 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x54, 0x52, 0x41, 0x46, 0x46, 0x49, - 0x43, 0x5f, 0x52, 0x45, 0x41, 0x44, 0x10, 0x18, 0x12, 0x26, 0x0a, 0x22, 0x52, 0x49, 0x47, 0x48, - 0x54, 0x5f, 0x41, 0x50, 0x50, 0x4c, 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x54, 0x52, - 0x41, 0x46, 0x46, 0x49, 0x43, 0x5f, 0x55, 0x50, 0x5f, 0x57, 0x52, 0x49, 0x54, 0x45, 0x10, 0x19, - 0x12, 0x28, 0x0a, 0x24, 0x52, 0x49, 0x47, 0x48, 0x54, 0x5f, 0x41, 0x50, 0x50, 0x4c, 0x49, 0x43, - 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x54, 0x52, 0x41, 0x46, 0x46, 0x49, 0x43, 0x5f, 0x44, 0x4f, - 0x57, 0x4e, 0x5f, 0x57, 0x52, 0x49, 0x54, 0x45, 0x10, 0x1a, 0x12, 0x1a, 0x0a, 0x16, 0x52, 0x49, - 0x47, 0x48, 0x54, 0x5f, 0x41, 0x50, 0x50, 0x4c, 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, - 0x4c, 0x49, 0x4e, 0x4b, 0x10, 0x1b, 0x12, 0x19, 0x0a, 0x15, 0x52, 0x49, 0x47, 0x48, 0x54, 0x5f, - 0x41, 0x50, 0x50, 0x4c, 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x41, 0x4c, 0x4c, 0x10, - 0x1c, 0x12, 0x14, 0x0a, 0x10, 0x52, 0x49, 0x47, 0x48, 0x54, 0x5f, 0x43, 0x4c, 0x49, 0x45, 0x4e, - 0x54, 0x5f, 0x41, 0x4c, 0x4c, 0x10, 0x1d, 0x12, 0x15, 0x0a, 0x11, 0x52, 0x49, 0x47, 0x48, 0x54, - 0x5f, 0x43, 0x4c, 0x49, 0x45, 0x4e, 0x54, 0x5f, 0x49, 0x4e, 0x46, 0x4f, 0x10, 0x3c, 0x12, 0x1f, - 0x0a, 0x1b, 0x52, 0x49, 0x47, 0x48, 0x54, 0x5f, 0x43, 0x4c, 0x49, 0x45, 0x4e, 0x54, 0x5f, 0x53, - 0x45, 0x54, 0x54, 0x49, 0x4e, 0x47, 0x53, 0x5f, 0x42, 0x41, 0x53, 0x49, 0x43, 0x10, 0x3d, 0x12, - 0x27, 0x0a, 0x23, 0x52, 0x49, 0x47, 0x48, 0x54, 0x5f, 0x43, 0x4c, 0x49, 0x45, 0x4e, 0x54, 0x5f, - 0x53, 0x45, 0x54, 0x54, 0x49, 0x4e, 0x47, 0x53, 0x5f, 0x43, 0x4f, 0x4c, 0x4c, 0x41, 0x42, 0x4f, - 0x52, 0x41, 0x54, 0x4f, 0x52, 0x53, 0x10, 0x3e, 0x12, 0x17, 0x0a, 0x13, 0x52, 0x49, 0x47, 0x48, - 0x54, 0x5f, 0x43, 0x4c, 0x49, 0x45, 0x4e, 0x54, 0x5f, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, 0x10, - 0x3f, 0x12, 0x16, 0x0a, 0x12, 0x52, 0x49, 0x47, 0x48, 0x54, 0x5f, 0x47, 0x41, 0x54, 0x45, 0x57, - 0x41, 0x59, 0x5f, 0x49, 0x4e, 0x46, 0x4f, 0x10, 0x1e, 0x12, 0x20, 0x0a, 0x1c, 0x52, 0x49, 0x47, - 0x48, 0x54, 0x5f, 0x47, 0x41, 0x54, 0x45, 0x57, 0x41, 0x59, 0x5f, 0x53, 0x45, 0x54, 0x54, 0x49, - 0x4e, 0x47, 0x53, 0x5f, 0x42, 0x41, 0x53, 0x49, 0x43, 0x10, 0x1f, 0x12, 0x23, 0x0a, 0x1f, 0x52, - 0x49, 0x47, 0x48, 0x54, 0x5f, 0x47, 0x41, 0x54, 0x45, 0x57, 0x41, 0x59, 0x5f, 0x53, 0x45, 0x54, - 0x54, 0x49, 0x4e, 0x47, 0x53, 0x5f, 0x41, 0x50, 0x49, 0x5f, 0x4b, 0x45, 0x59, 0x53, 0x10, 0x20, - 0x12, 0x28, 0x0a, 0x24, 0x52, 0x49, 0x47, 0x48, 0x54, 0x5f, 0x47, 0x41, 0x54, 0x45, 0x57, 0x41, - 0x59, 0x5f, 0x53, 0x45, 0x54, 0x54, 0x49, 0x4e, 0x47, 0x53, 0x5f, 0x43, 0x4f, 0x4c, 0x4c, 0x41, - 0x42, 0x4f, 0x52, 0x41, 0x54, 0x4f, 0x52, 0x53, 0x10, 0x21, 0x12, 0x18, 0x0a, 0x14, 0x52, 0x49, - 0x47, 0x48, 0x54, 0x5f, 0x47, 0x41, 0x54, 0x45, 0x57, 0x41, 0x59, 0x5f, 0x44, 0x45, 0x4c, 0x45, - 0x54, 0x45, 0x10, 0x22, 0x12, 0x1e, 0x0a, 0x1a, 0x52, 0x49, 0x47, 0x48, 0x54, 0x5f, 0x47, 0x41, + 0x50, 0x55, 0x52, 0x47, 0x45, 0x10, 0x40, 0x12, 0x22, 0x0a, 0x1e, 0x52, 0x49, 0x47, 0x48, 0x54, + 0x5f, 0x41, 0x50, 0x50, 0x4c, 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x44, 0x45, 0x56, + 0x49, 0x43, 0x45, 0x53, 0x5f, 0x52, 0x45, 0x41, 0x44, 0x10, 0x14, 0x12, 0x23, 0x0a, 0x1f, 0x52, + 0x49, 0x47, 0x48, 0x54, 0x5f, 0x41, 0x50, 0x50, 0x4c, 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, + 0x5f, 0x44, 0x45, 0x56, 0x49, 0x43, 0x45, 0x53, 0x5f, 0x57, 0x52, 0x49, 0x54, 0x45, 0x10, 0x15, + 0x12, 0x27, 0x0a, 0x23, 0x52, 0x49, 0x47, 0x48, 0x54, 0x5f, 0x41, 0x50, 0x50, 0x4c, 0x49, 0x43, + 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x44, 0x45, 0x56, 0x49, 0x43, 0x45, 0x53, 0x5f, 0x52, 0x45, + 0x41, 0x44, 0x5f, 0x4b, 0x45, 0x59, 0x53, 0x10, 0x16, 0x12, 0x28, 0x0a, 0x24, 0x52, 0x49, 0x47, + 0x48, 0x54, 0x5f, 0x41, 0x50, 0x50, 0x4c, 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x44, + 0x45, 0x56, 0x49, 0x43, 0x45, 0x53, 0x5f, 0x57, 0x52, 0x49, 0x54, 0x45, 0x5f, 0x4b, 0x45, 0x59, + 0x53, 0x10, 0x17, 0x12, 0x22, 0x0a, 0x1e, 0x52, 0x49, 0x47, 0x48, 0x54, 0x5f, 0x41, 0x50, 0x50, + 0x4c, 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x54, 0x52, 0x41, 0x46, 0x46, 0x49, 0x43, + 0x5f, 0x52, 0x45, 0x41, 0x44, 0x10, 0x18, 0x12, 0x26, 0x0a, 0x22, 0x52, 0x49, 0x47, 0x48, 0x54, + 0x5f, 0x41, 0x50, 0x50, 0x4c, 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x54, 0x52, 0x41, + 0x46, 0x46, 0x49, 0x43, 0x5f, 0x55, 0x50, 0x5f, 0x57, 0x52, 0x49, 0x54, 0x45, 0x10, 0x19, 0x12, + 0x28, 0x0a, 0x24, 0x52, 0x49, 0x47, 0x48, 0x54, 0x5f, 0x41, 0x50, 0x50, 0x4c, 0x49, 0x43, 0x41, + 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x54, 0x52, 0x41, 0x46, 0x46, 0x49, 0x43, 0x5f, 0x44, 0x4f, 0x57, + 0x4e, 0x5f, 0x57, 0x52, 0x49, 0x54, 0x45, 0x10, 0x1a, 0x12, 0x1a, 0x0a, 0x16, 0x52, 0x49, 0x47, + 0x48, 0x54, 0x5f, 0x41, 0x50, 0x50, 0x4c, 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x4c, + 0x49, 0x4e, 0x4b, 0x10, 0x1b, 0x12, 0x19, 0x0a, 0x15, 0x52, 0x49, 0x47, 0x48, 0x54, 0x5f, 0x41, + 0x50, 0x50, 0x4c, 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x41, 0x4c, 0x4c, 0x10, 0x1c, + 0x12, 0x14, 0x0a, 0x10, 0x52, 0x49, 0x47, 0x48, 0x54, 0x5f, 0x43, 0x4c, 0x49, 0x45, 0x4e, 0x54, + 0x5f, 0x41, 0x4c, 0x4c, 0x10, 0x1d, 0x12, 0x15, 0x0a, 0x11, 0x52, 0x49, 0x47, 0x48, 0x54, 0x5f, + 0x43, 0x4c, 0x49, 0x45, 0x4e, 0x54, 0x5f, 0x49, 0x4e, 0x46, 0x4f, 0x10, 0x3c, 0x12, 0x1f, 0x0a, + 0x1b, 0x52, 0x49, 0x47, 0x48, 0x54, 0x5f, 0x43, 0x4c, 0x49, 0x45, 0x4e, 0x54, 0x5f, 0x53, 0x45, + 0x54, 0x54, 0x49, 0x4e, 0x47, 0x53, 0x5f, 0x42, 0x41, 0x53, 0x49, 0x43, 0x10, 0x3d, 0x12, 0x27, + 0x0a, 0x23, 0x52, 0x49, 0x47, 0x48, 0x54, 0x5f, 0x43, 0x4c, 0x49, 0x45, 0x4e, 0x54, 0x5f, 0x53, + 0x45, 0x54, 0x54, 0x49, 0x4e, 0x47, 0x53, 0x5f, 0x43, 0x4f, 0x4c, 0x4c, 0x41, 0x42, 0x4f, 0x52, + 0x41, 0x54, 0x4f, 0x52, 0x53, 0x10, 0x3e, 0x12, 0x17, 0x0a, 0x13, 0x52, 0x49, 0x47, 0x48, 0x54, + 0x5f, 0x43, 0x4c, 0x49, 0x45, 0x4e, 0x54, 0x5f, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, 0x10, 0x3f, + 0x12, 0x16, 0x0a, 0x12, 0x52, 0x49, 0x47, 0x48, 0x54, 0x5f, 0x43, 0x4c, 0x49, 0x45, 0x4e, 0x54, + 0x5f, 0x50, 0x55, 0x52, 0x47, 0x45, 0x10, 0x44, 0x12, 0x16, 0x0a, 0x12, 0x52, 0x49, 0x47, 0x48, + 0x54, 0x5f, 0x47, 0x41, 0x54, 0x45, 0x57, 0x41, 0x59, 0x5f, 0x49, 0x4e, 0x46, 0x4f, 0x10, 0x1e, + 0x12, 0x20, 0x0a, 0x1c, 0x52, 0x49, 0x47, 0x48, 0x54, 0x5f, 0x47, 0x41, 0x54, 0x45, 0x57, 0x41, + 0x59, 0x5f, 0x53, 0x45, 0x54, 0x54, 0x49, 0x4e, 0x47, 0x53, 0x5f, 0x42, 0x41, 0x53, 0x49, 0x43, + 0x10, 0x1f, 0x12, 0x23, 0x0a, 0x1f, 0x52, 0x49, 0x47, 0x48, 0x54, 0x5f, 0x47, 0x41, 0x54, 0x45, + 0x57, 0x41, 0x59, 0x5f, 0x53, 0x45, 0x54, 0x54, 0x49, 0x4e, 0x47, 0x53, 0x5f, 0x41, 0x50, 0x49, + 0x5f, 0x4b, 0x45, 0x59, 0x53, 0x10, 0x20, 0x12, 0x28, 0x0a, 0x24, 0x52, 0x49, 0x47, 0x48, 0x54, + 0x5f, 0x47, 0x41, 0x54, 0x45, 0x57, 0x41, 0x59, 0x5f, 0x53, 0x45, 0x54, 0x54, 0x49, 0x4e, 0x47, + 0x53, 0x5f, 0x43, 0x4f, 0x4c, 0x4c, 0x41, 0x42, 0x4f, 0x52, 0x41, 0x54, 0x4f, 0x52, 0x53, 0x10, + 0x21, 0x12, 0x18, 0x0a, 0x14, 0x52, 0x49, 0x47, 0x48, 0x54, 0x5f, 0x47, 0x41, 0x54, 0x45, 0x57, + 0x41, 0x59, 0x5f, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, 0x10, 0x22, 0x12, 0x17, 0x0a, 0x13, 0x52, + 0x49, 0x47, 0x48, 0x54, 0x5f, 0x47, 0x41, 0x54, 0x45, 0x57, 0x41, 0x59, 0x5f, 0x50, 0x55, 0x52, + 0x47, 0x45, 0x10, 0x43, 0x12, 0x1e, 0x0a, 0x1a, 0x52, 0x49, 0x47, 0x48, 0x54, 0x5f, 0x47, 0x41, 0x54, 0x45, 0x57, 0x41, 0x59, 0x5f, 0x54, 0x52, 0x41, 0x46, 0x46, 0x49, 0x43, 0x5f, 0x52, 0x45, 0x41, 0x44, 0x10, 0x23, 0x12, 0x24, 0x0a, 0x20, 0x52, 0x49, 0x47, 0x48, 0x54, 0x5f, 0x47, 0x41, 0x54, 0x45, 0x57, 0x41, 0x59, 0x5f, 0x54, 0x52, 0x41, 0x46, 0x46, 0x49, 0x43, 0x5f, 0x44, 0x4f, @@ -878,33 +915,42 @@ var file_ttn_lorawan_v3_rights_proto_rawDesc = []byte{ 0x45, 0x54, 0x54, 0x49, 0x4e, 0x47, 0x53, 0x5f, 0x4d, 0x45, 0x4d, 0x42, 0x45, 0x52, 0x53, 0x10, 0x2c, 0x12, 0x1d, 0x0a, 0x19, 0x52, 0x49, 0x47, 0x48, 0x54, 0x5f, 0x4f, 0x52, 0x47, 0x41, 0x4e, 0x49, 0x5a, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, 0x10, 0x2d, - 0x12, 0x28, 0x0a, 0x24, 0x52, 0x49, 0x47, 0x48, 0x54, 0x5f, 0x4f, 0x52, 0x47, 0x41, 0x4e, 0x49, - 0x5a, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x41, 0x50, 0x50, 0x4c, 0x49, 0x43, 0x41, 0x54, 0x49, - 0x4f, 0x4e, 0x53, 0x5f, 0x4c, 0x49, 0x53, 0x54, 0x10, 0x2e, 0x12, 0x2a, 0x0a, 0x26, 0x52, 0x49, + 0x12, 0x1c, 0x0a, 0x18, 0x52, 0x49, 0x47, 0x48, 0x54, 0x5f, 0x4f, 0x52, 0x47, 0x41, 0x4e, 0x49, + 0x5a, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x50, 0x55, 0x52, 0x47, 0x45, 0x10, 0x41, 0x12, 0x28, + 0x0a, 0x24, 0x52, 0x49, 0x47, 0x48, 0x54, 0x5f, 0x4f, 0x52, 0x47, 0x41, 0x4e, 0x49, 0x5a, 0x41, + 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x41, 0x50, 0x50, 0x4c, 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, + 0x53, 0x5f, 0x4c, 0x49, 0x53, 0x54, 0x10, 0x2e, 0x12, 0x2a, 0x0a, 0x26, 0x52, 0x49, 0x47, 0x48, + 0x54, 0x5f, 0x4f, 0x52, 0x47, 0x41, 0x4e, 0x49, 0x5a, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x41, + 0x50, 0x50, 0x4c, 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x53, 0x5f, 0x43, 0x52, 0x45, 0x41, + 0x54, 0x45, 0x10, 0x2f, 0x12, 0x24, 0x0a, 0x20, 0x52, 0x49, 0x47, 0x48, 0x54, 0x5f, 0x4f, 0x52, + 0x47, 0x41, 0x4e, 0x49, 0x5a, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x47, 0x41, 0x54, 0x45, 0x57, + 0x41, 0x59, 0x53, 0x5f, 0x4c, 0x49, 0x53, 0x54, 0x10, 0x30, 0x12, 0x26, 0x0a, 0x22, 0x52, 0x49, + 0x47, 0x48, 0x54, 0x5f, 0x4f, 0x52, 0x47, 0x41, 0x4e, 0x49, 0x5a, 0x41, 0x54, 0x49, 0x4f, 0x4e, + 0x5f, 0x47, 0x41, 0x54, 0x45, 0x57, 0x41, 0x59, 0x53, 0x5f, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, + 0x10, 0x31, 0x12, 0x23, 0x0a, 0x1f, 0x52, 0x49, 0x47, 0x48, 0x54, 0x5f, 0x4f, 0x52, 0x47, 0x41, + 0x4e, 0x49, 0x5a, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x43, 0x4c, 0x49, 0x45, 0x4e, 0x54, 0x53, + 0x5f, 0x4c, 0x49, 0x53, 0x54, 0x10, 0x32, 0x12, 0x25, 0x0a, 0x21, 0x52, 0x49, 0x47, 0x48, 0x54, + 0x5f, 0x4f, 0x52, 0x47, 0x41, 0x4e, 0x49, 0x5a, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x43, 0x4c, + 0x49, 0x45, 0x4e, 0x54, 0x53, 0x5f, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x10, 0x33, 0x12, 0x2a, + 0x0a, 0x26, 0x52, 0x49, 0x47, 0x48, 0x54, 0x5f, 0x4f, 0x52, 0x47, 0x41, 0x4e, 0x49, 0x5a, 0x41, + 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x41, 0x44, 0x44, 0x5f, 0x41, 0x53, 0x5f, 0x43, 0x4f, 0x4c, 0x4c, + 0x41, 0x42, 0x4f, 0x52, 0x41, 0x54, 0x4f, 0x52, 0x10, 0x34, 0x12, 0x1a, 0x0a, 0x16, 0x52, 0x49, 0x47, 0x48, 0x54, 0x5f, 0x4f, 0x52, 0x47, 0x41, 0x4e, 0x49, 0x5a, 0x41, 0x54, 0x49, 0x4f, 0x4e, - 0x5f, 0x41, 0x50, 0x50, 0x4c, 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x53, 0x5f, 0x43, 0x52, - 0x45, 0x41, 0x54, 0x45, 0x10, 0x2f, 0x12, 0x24, 0x0a, 0x20, 0x52, 0x49, 0x47, 0x48, 0x54, 0x5f, - 0x4f, 0x52, 0x47, 0x41, 0x4e, 0x49, 0x5a, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x47, 0x41, 0x54, - 0x45, 0x57, 0x41, 0x59, 0x53, 0x5f, 0x4c, 0x49, 0x53, 0x54, 0x10, 0x30, 0x12, 0x26, 0x0a, 0x22, - 0x52, 0x49, 0x47, 0x48, 0x54, 0x5f, 0x4f, 0x52, 0x47, 0x41, 0x4e, 0x49, 0x5a, 0x41, 0x54, 0x49, - 0x4f, 0x4e, 0x5f, 0x47, 0x41, 0x54, 0x45, 0x57, 0x41, 0x59, 0x53, 0x5f, 0x43, 0x52, 0x45, 0x41, - 0x54, 0x45, 0x10, 0x31, 0x12, 0x23, 0x0a, 0x1f, 0x52, 0x49, 0x47, 0x48, 0x54, 0x5f, 0x4f, 0x52, - 0x47, 0x41, 0x4e, 0x49, 0x5a, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x43, 0x4c, 0x49, 0x45, 0x4e, - 0x54, 0x53, 0x5f, 0x4c, 0x49, 0x53, 0x54, 0x10, 0x32, 0x12, 0x25, 0x0a, 0x21, 0x52, 0x49, 0x47, - 0x48, 0x54, 0x5f, 0x4f, 0x52, 0x47, 0x41, 0x4e, 0x49, 0x5a, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, - 0x43, 0x4c, 0x49, 0x45, 0x4e, 0x54, 0x53, 0x5f, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x10, 0x33, - 0x12, 0x2a, 0x0a, 0x26, 0x52, 0x49, 0x47, 0x48, 0x54, 0x5f, 0x4f, 0x52, 0x47, 0x41, 0x4e, 0x49, - 0x5a, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x41, 0x44, 0x44, 0x5f, 0x41, 0x53, 0x5f, 0x43, 0x4f, - 0x4c, 0x4c, 0x41, 0x42, 0x4f, 0x52, 0x41, 0x54, 0x4f, 0x52, 0x10, 0x34, 0x12, 0x1a, 0x0a, 0x16, - 0x52, 0x49, 0x47, 0x48, 0x54, 0x5f, 0x4f, 0x52, 0x47, 0x41, 0x4e, 0x49, 0x5a, 0x41, 0x54, 0x49, - 0x4f, 0x4e, 0x5f, 0x41, 0x4c, 0x4c, 0x10, 0x35, 0x12, 0x16, 0x0a, 0x12, 0x52, 0x49, 0x47, 0x48, - 0x54, 0x5f, 0x53, 0x45, 0x4e, 0x44, 0x5f, 0x49, 0x4e, 0x56, 0x49, 0x54, 0x45, 0x53, 0x10, 0x36, - 0x12, 0x0d, 0x0a, 0x09, 0x52, 0x49, 0x47, 0x48, 0x54, 0x5f, 0x41, 0x4c, 0x4c, 0x10, 0x37, 0x1a, - 0x0d, 0xea, 0xaa, 0x19, 0x09, 0x18, 0x01, 0x2a, 0x05, 0x52, 0x49, 0x47, 0x48, 0x54, 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, + 0x5f, 0x41, 0x4c, 0x4c, 0x10, 0x35, 0x12, 0x16, 0x0a, 0x12, 0x52, 0x49, 0x47, 0x48, 0x54, 0x5f, + 0x53, 0x45, 0x4e, 0x44, 0x5f, 0x49, 0x4e, 0x56, 0x49, 0x54, 0x45, 0x53, 0x10, 0x36, 0x12, 0x0d, + 0x0a, 0x09, 0x52, 0x49, 0x47, 0x48, 0x54, 0x5f, 0x41, 0x4c, 0x4c, 0x10, 0x37, 0x1a, 0x0d, 0xea, + 0xaa, 0x19, 0x09, 0x18, 0x01, 0x2a, 0x05, 0x52, 0x49, 0x47, 0x48, 0x54, 0x22, 0x04, 0x08, 0x45, + 0x10, 0x45, 0x22, 0x04, 0x08, 0x46, 0x10, 0x46, 0x22, 0x04, 0x08, 0x47, 0x10, 0x47, 0x22, 0x04, + 0x08, 0x48, 0x10, 0x48, 0x22, 0x04, 0x08, 0x49, 0x10, 0x49, 0x22, 0x04, 0x08, 0x4a, 0x10, 0x4a, + 0x22, 0x04, 0x08, 0x4b, 0x10, 0x4b, 0x22, 0x04, 0x08, 0x4c, 0x10, 0x4c, 0x22, 0x04, 0x08, 0x4d, + 0x10, 0x4d, 0x22, 0x04, 0x08, 0x4e, 0x10, 0x4e, 0x22, 0x04, 0x08, 0x4f, 0x10, 0x4f, 0x22, 0x04, + 0x08, 0x50, 0x10, 0x50, 0x22, 0x04, 0x08, 0x51, 0x10, 0x51, 0x22, 0x04, 0x08, 0x52, 0x10, 0x52, + 0x22, 0x04, 0x08, 0x53, 0x10, 0x53, 0x22, 0x04, 0x08, 0x54, 0x10, 0x54, 0x22, 0x04, 0x08, 0x55, + 0x10, 0x55, 0x22, 0x04, 0x08, 0x56, 0x10, 0x56, 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 ( diff --git a/pkg/ttnpb/rights_json.pb.go b/pkg/ttnpb/rights_json.pb.go index 1a1f1277af..f4fbb3f88d 100644 --- a/pkg/ttnpb/rights_json.pb.go +++ b/pkg/ttnpb/rights_json.pb.go @@ -30,8 +30,11 @@ func (x Right) MarshalJSON() ([]byte, error) { var Right_customvalue = map[string]int32{ "USER_INFO": 1, "USER_SETTINGS_BASIC": 2, + "USER_LIST": 87, + "USER_CREATE": 88, "USER_SETTINGS_API_KEYS": 3, "USER_DELETE": 4, + "USER_PURGE": 66, "USER_AUTHORIZED_CLIENTS": 5, "USER_APPLICATIONS_LIST": 6, "USER_APPLICATIONS_CREATE": 7, @@ -49,6 +52,7 @@ var Right_customvalue = map[string]int32{ "APPLICATION_SETTINGS_COLLABORATORS": 18, "APPLICATION_SETTINGS_PACKAGES": 56, "APPLICATION_DELETE": 19, + "APPLICATION_PURGE": 64, "APPLICATION_DEVICES_READ": 20, "APPLICATION_DEVICES_WRITE": 21, "APPLICATION_DEVICES_READ_KEYS": 22, @@ -63,11 +67,13 @@ var Right_customvalue = map[string]int32{ "CLIENT_SETTINGS_BASIC": 61, "CLIENT_SETTINGS_COLLABORATORS": 62, "CLIENT_DELETE": 63, + "CLIENT_PURGE": 68, "GATEWAY_INFO": 30, "GATEWAY_SETTINGS_BASIC": 31, "GATEWAY_SETTINGS_API_KEYS": 32, "GATEWAY_SETTINGS_COLLABORATORS": 33, "GATEWAY_DELETE": 34, + "GATEWAY_PURGE": 67, "GATEWAY_TRAFFIC_READ": 35, "GATEWAY_TRAFFIC_DOWN_WRITE": 36, "GATEWAY_LINK": 37, @@ -81,6 +87,7 @@ var Right_customvalue = map[string]int32{ "ORGANIZATION_SETTINGS_API_KEYS": 43, "ORGANIZATION_SETTINGS_MEMBERS": 44, "ORGANIZATION_DELETE": 45, + "ORGANIZATION_PURGE": 65, "ORGANIZATION_APPLICATIONS_LIST": 46, "ORGANIZATION_APPLICATIONS_CREATE": 47, "ORGANIZATION_GATEWAYS_LIST": 48, diff --git a/pkg/ttnpb/testdata/ttnpb_encoding_golden.md b/pkg/ttnpb/testdata/ttnpb_encoding_golden.md index 4bb15def24..35fae9bf03 100644 --- a/pkg/ttnpb/testdata/ttnpb_encoding_golden.md +++ b/pkg/ttnpb/testdata/ttnpb_encoding_golden.md @@ -702,6 +702,7 @@ JSON | ttnpb.Right | RIGHT_APPLICATION_DEVICES_WRITE | "RIGHT_APPLICATION_DEVICE JSON | ttnpb.Right | RIGHT_APPLICATION_DEVICES_WRITE_KEYS | "RIGHT_APPLICATION_DEVICES_WRITE_KEYS" JSON | ttnpb.Right | RIGHT_APPLICATION_INFO | "RIGHT_APPLICATION_INFO" JSON | ttnpb.Right | RIGHT_APPLICATION_LINK | "RIGHT_APPLICATION_LINK" +JSON | ttnpb.Right | RIGHT_APPLICATION_PURGE | "RIGHT_APPLICATION_PURGE" JSON | ttnpb.Right | RIGHT_APPLICATION_SETTINGS_API_KEYS | "RIGHT_APPLICATION_SETTINGS_API_KEYS" JSON | ttnpb.Right | RIGHT_APPLICATION_SETTINGS_BASIC | "RIGHT_APPLICATION_SETTINGS_BASIC" JSON | ttnpb.Right | RIGHT_APPLICATION_SETTINGS_COLLABORATORS | "RIGHT_APPLICATION_SETTINGS_COLLABORATORS" @@ -712,6 +713,7 @@ JSON | ttnpb.Right | RIGHT_APPLICATION_TRAFFIC_UP_WRITE | "RIGHT_APPLICATION_TRA JSON | ttnpb.Right | RIGHT_CLIENT_ALL | "RIGHT_CLIENT_ALL" JSON | ttnpb.Right | RIGHT_CLIENT_DELETE | "RIGHT_CLIENT_DELETE" JSON | ttnpb.Right | RIGHT_CLIENT_INFO | "RIGHT_CLIENT_INFO" +JSON | ttnpb.Right | RIGHT_CLIENT_PURGE | "RIGHT_CLIENT_PURGE" JSON | ttnpb.Right | RIGHT_CLIENT_SETTINGS_BASIC | "RIGHT_CLIENT_SETTINGS_BASIC" JSON | ttnpb.Right | RIGHT_CLIENT_SETTINGS_COLLABORATORS | "RIGHT_CLIENT_SETTINGS_COLLABORATORS" JSON | ttnpb.Right | RIGHT_GATEWAY_ALL | "RIGHT_GATEWAY_ALL" @@ -719,6 +721,7 @@ JSON | ttnpb.Right | RIGHT_GATEWAY_DELETE | "RIGHT_GATEWAY_DELETE" JSON | ttnpb.Right | RIGHT_GATEWAY_INFO | "RIGHT_GATEWAY_INFO" JSON | ttnpb.Right | RIGHT_GATEWAY_LINK | "RIGHT_GATEWAY_LINK" JSON | ttnpb.Right | RIGHT_GATEWAY_LOCATION_READ | "RIGHT_GATEWAY_LOCATION_READ" +JSON | ttnpb.Right | RIGHT_GATEWAY_PURGE | "RIGHT_GATEWAY_PURGE" JSON | ttnpb.Right | RIGHT_GATEWAY_READ_SECRETS | "RIGHT_GATEWAY_READ_SECRETS" JSON | ttnpb.Right | RIGHT_GATEWAY_SETTINGS_API_KEYS | "RIGHT_GATEWAY_SETTINGS_API_KEYS" JSON | ttnpb.Right | RIGHT_GATEWAY_SETTINGS_BASIC | "RIGHT_GATEWAY_SETTINGS_BASIC" @@ -737,6 +740,7 @@ JSON | ttnpb.Right | RIGHT_ORGANIZATION_DELETE | "RIGHT_ORGANIZATION_DELETE" JSON | ttnpb.Right | RIGHT_ORGANIZATION_GATEWAYS_CREATE | "RIGHT_ORGANIZATION_GATEWAYS_CREATE" JSON | ttnpb.Right | RIGHT_ORGANIZATION_GATEWAYS_LIST | "RIGHT_ORGANIZATION_GATEWAYS_LIST" JSON | ttnpb.Right | RIGHT_ORGANIZATION_INFO | "RIGHT_ORGANIZATION_INFO" +JSON | ttnpb.Right | RIGHT_ORGANIZATION_PURGE | "RIGHT_ORGANIZATION_PURGE" JSON | ttnpb.Right | RIGHT_ORGANIZATION_SETTINGS_API_KEYS | "RIGHT_ORGANIZATION_SETTINGS_API_KEYS" JSON | ttnpb.Right | RIGHT_ORGANIZATION_SETTINGS_BASIC | "RIGHT_ORGANIZATION_SETTINGS_BASIC" JSON | ttnpb.Right | RIGHT_ORGANIZATION_SETTINGS_MEMBERS | "RIGHT_ORGANIZATION_SETTINGS_MEMBERS" @@ -747,13 +751,16 @@ JSON | ttnpb.Right | RIGHT_USER_APPLICATIONS_LIST | "RIGHT_USER_APPLICATIONS_LIS JSON | ttnpb.Right | RIGHT_USER_AUTHORIZED_CLIENTS | "RIGHT_USER_AUTHORIZED_CLIENTS" JSON | ttnpb.Right | RIGHT_USER_CLIENTS_CREATE | "RIGHT_USER_CLIENTS_CREATE" JSON | ttnpb.Right | RIGHT_USER_CLIENTS_LIST | "RIGHT_USER_CLIENTS_LIST" +JSON | ttnpb.Right | RIGHT_USER_CREATE | "RIGHT_USER_CREATE" JSON | ttnpb.Right | RIGHT_USER_DELETE | "RIGHT_USER_DELETE" JSON | ttnpb.Right | RIGHT_USER_GATEWAYS_CREATE | "RIGHT_USER_GATEWAYS_CREATE" JSON | ttnpb.Right | RIGHT_USER_GATEWAYS_LIST | "RIGHT_USER_GATEWAYS_LIST" JSON | ttnpb.Right | RIGHT_USER_INFO | "RIGHT_USER_INFO" +JSON | ttnpb.Right | RIGHT_USER_LIST | "RIGHT_USER_LIST" JSON | ttnpb.Right | RIGHT_USER_NOTIFICATIONS_READ | "RIGHT_USER_NOTIFICATIONS_READ" JSON | ttnpb.Right | RIGHT_USER_ORGANIZATIONS_CREATE | "RIGHT_USER_ORGANIZATIONS_CREATE" JSON | ttnpb.Right | RIGHT_USER_ORGANIZATIONS_LIST | "RIGHT_USER_ORGANIZATIONS_LIST" +JSON | ttnpb.Right | RIGHT_USER_PURGE | "RIGHT_USER_PURGE" JSON | ttnpb.Right | RIGHT_USER_SETTINGS_API_KEYS | "RIGHT_USER_SETTINGS_API_KEYS" JSON | ttnpb.Right | RIGHT_USER_SETTINGS_BASIC | "RIGHT_USER_SETTINGS_BASIC" JSON | ttnpb.Right | right_invalid | "right_invalid" @@ -1151,6 +1158,7 @@ ProtoJSON | ttnpb.Right | RIGHT_APPLICATION_DEVICES_WRITE | "RIGHT_APPLICATION_D ProtoJSON | ttnpb.Right | RIGHT_APPLICATION_DEVICES_WRITE_KEYS | "RIGHT_APPLICATION_DEVICES_WRITE_KEYS" ProtoJSON | ttnpb.Right | RIGHT_APPLICATION_INFO | "RIGHT_APPLICATION_INFO" ProtoJSON | ttnpb.Right | RIGHT_APPLICATION_LINK | "RIGHT_APPLICATION_LINK" +ProtoJSON | ttnpb.Right | RIGHT_APPLICATION_PURGE | "RIGHT_APPLICATION_PURGE" ProtoJSON | ttnpb.Right | RIGHT_APPLICATION_SETTINGS_API_KEYS | "RIGHT_APPLICATION_SETTINGS_API_KEYS" ProtoJSON | ttnpb.Right | RIGHT_APPLICATION_SETTINGS_BASIC | "RIGHT_APPLICATION_SETTINGS_BASIC" ProtoJSON | ttnpb.Right | RIGHT_APPLICATION_SETTINGS_COLLABORATORS | "RIGHT_APPLICATION_SETTINGS_COLLABORATORS" @@ -1161,6 +1169,7 @@ ProtoJSON | ttnpb.Right | RIGHT_APPLICATION_TRAFFIC_UP_WRITE | "RIGHT_APPLICATIO ProtoJSON | ttnpb.Right | RIGHT_CLIENT_ALL | "RIGHT_CLIENT_ALL" ProtoJSON | ttnpb.Right | RIGHT_CLIENT_DELETE | "RIGHT_CLIENT_DELETE" ProtoJSON | ttnpb.Right | RIGHT_CLIENT_INFO | "RIGHT_CLIENT_INFO" +ProtoJSON | ttnpb.Right | RIGHT_CLIENT_PURGE | "RIGHT_CLIENT_PURGE" ProtoJSON | ttnpb.Right | RIGHT_CLIENT_SETTINGS_BASIC | "RIGHT_CLIENT_SETTINGS_BASIC" ProtoJSON | ttnpb.Right | RIGHT_CLIENT_SETTINGS_COLLABORATORS | "RIGHT_CLIENT_SETTINGS_COLLABORATORS" ProtoJSON | ttnpb.Right | RIGHT_GATEWAY_ALL | "RIGHT_GATEWAY_ALL" @@ -1168,6 +1177,7 @@ ProtoJSON | ttnpb.Right | RIGHT_GATEWAY_DELETE | "RIGHT_GATEWAY_DELETE" ProtoJSON | ttnpb.Right | RIGHT_GATEWAY_INFO | "RIGHT_GATEWAY_INFO" ProtoJSON | ttnpb.Right | RIGHT_GATEWAY_LINK | "RIGHT_GATEWAY_LINK" ProtoJSON | ttnpb.Right | RIGHT_GATEWAY_LOCATION_READ | "RIGHT_GATEWAY_LOCATION_READ" +ProtoJSON | ttnpb.Right | RIGHT_GATEWAY_PURGE | "RIGHT_GATEWAY_PURGE" ProtoJSON | ttnpb.Right | RIGHT_GATEWAY_READ_SECRETS | "RIGHT_GATEWAY_READ_SECRETS" ProtoJSON | ttnpb.Right | RIGHT_GATEWAY_SETTINGS_API_KEYS | "RIGHT_GATEWAY_SETTINGS_API_KEYS" ProtoJSON | ttnpb.Right | RIGHT_GATEWAY_SETTINGS_BASIC | "RIGHT_GATEWAY_SETTINGS_BASIC" @@ -1186,6 +1196,7 @@ ProtoJSON | ttnpb.Right | RIGHT_ORGANIZATION_DELETE | "RIGHT_ORGANIZATION_DELETE ProtoJSON | ttnpb.Right | RIGHT_ORGANIZATION_GATEWAYS_CREATE | "RIGHT_ORGANIZATION_GATEWAYS_CREATE" ProtoJSON | ttnpb.Right | RIGHT_ORGANIZATION_GATEWAYS_LIST | "RIGHT_ORGANIZATION_GATEWAYS_LIST" ProtoJSON | ttnpb.Right | RIGHT_ORGANIZATION_INFO | "RIGHT_ORGANIZATION_INFO" +ProtoJSON | ttnpb.Right | RIGHT_ORGANIZATION_PURGE | "RIGHT_ORGANIZATION_PURGE" ProtoJSON | ttnpb.Right | RIGHT_ORGANIZATION_SETTINGS_API_KEYS | "RIGHT_ORGANIZATION_SETTINGS_API_KEYS" ProtoJSON | ttnpb.Right | RIGHT_ORGANIZATION_SETTINGS_BASIC | "RIGHT_ORGANIZATION_SETTINGS_BASIC" ProtoJSON | ttnpb.Right | RIGHT_ORGANIZATION_SETTINGS_MEMBERS | "RIGHT_ORGANIZATION_SETTINGS_MEMBERS" @@ -1196,13 +1207,16 @@ ProtoJSON | ttnpb.Right | RIGHT_USER_APPLICATIONS_LIST | "RIGHT_USER_APPLICATION ProtoJSON | ttnpb.Right | RIGHT_USER_AUTHORIZED_CLIENTS | "RIGHT_USER_AUTHORIZED_CLIENTS" ProtoJSON | ttnpb.Right | RIGHT_USER_CLIENTS_CREATE | "RIGHT_USER_CLIENTS_CREATE" ProtoJSON | ttnpb.Right | RIGHT_USER_CLIENTS_LIST | "RIGHT_USER_CLIENTS_LIST" +ProtoJSON | ttnpb.Right | RIGHT_USER_CREATE | "RIGHT_USER_CREATE" ProtoJSON | ttnpb.Right | RIGHT_USER_DELETE | "RIGHT_USER_DELETE" ProtoJSON | ttnpb.Right | RIGHT_USER_GATEWAYS_CREATE | "RIGHT_USER_GATEWAYS_CREATE" ProtoJSON | ttnpb.Right | RIGHT_USER_GATEWAYS_LIST | "RIGHT_USER_GATEWAYS_LIST" ProtoJSON | ttnpb.Right | RIGHT_USER_INFO | "RIGHT_USER_INFO" +ProtoJSON | ttnpb.Right | RIGHT_USER_LIST | "RIGHT_USER_LIST" ProtoJSON | ttnpb.Right | RIGHT_USER_NOTIFICATIONS_READ | "RIGHT_USER_NOTIFICATIONS_READ" ProtoJSON | ttnpb.Right | RIGHT_USER_ORGANIZATIONS_CREATE | "RIGHT_USER_ORGANIZATIONS_CREATE" ProtoJSON | ttnpb.Right | RIGHT_USER_ORGANIZATIONS_LIST | "RIGHT_USER_ORGANIZATIONS_LIST" +ProtoJSON | ttnpb.Right | RIGHT_USER_PURGE | "RIGHT_USER_PURGE" ProtoJSON | ttnpb.Right | RIGHT_USER_SETTINGS_API_KEYS | "RIGHT_USER_SETTINGS_API_KEYS" ProtoJSON | ttnpb.Right | RIGHT_USER_SETTINGS_BASIC | "RIGHT_USER_SETTINGS_BASIC" ProtoJSON | ttnpb.Right | right_invalid | "right_invalid" @@ -1600,6 +1614,7 @@ Text | ttnpb.Right | RIGHT_APPLICATION_DEVICES_WRITE | RIGHT_APPLICATION_DEVICES Text | ttnpb.Right | RIGHT_APPLICATION_DEVICES_WRITE_KEYS | RIGHT_APPLICATION_DEVICES_WRITE_KEYS Text | ttnpb.Right | RIGHT_APPLICATION_INFO | RIGHT_APPLICATION_INFO Text | ttnpb.Right | RIGHT_APPLICATION_LINK | RIGHT_APPLICATION_LINK +Text | ttnpb.Right | RIGHT_APPLICATION_PURGE | RIGHT_APPLICATION_PURGE Text | ttnpb.Right | RIGHT_APPLICATION_SETTINGS_API_KEYS | RIGHT_APPLICATION_SETTINGS_API_KEYS Text | ttnpb.Right | RIGHT_APPLICATION_SETTINGS_BASIC | RIGHT_APPLICATION_SETTINGS_BASIC Text | ttnpb.Right | RIGHT_APPLICATION_SETTINGS_COLLABORATORS | RIGHT_APPLICATION_SETTINGS_COLLABORATORS @@ -1610,6 +1625,7 @@ Text | ttnpb.Right | RIGHT_APPLICATION_TRAFFIC_UP_WRITE | RIGHT_APPLICATION_TRAF Text | ttnpb.Right | RIGHT_CLIENT_ALL | RIGHT_CLIENT_ALL Text | ttnpb.Right | RIGHT_CLIENT_DELETE | RIGHT_CLIENT_DELETE Text | ttnpb.Right | RIGHT_CLIENT_INFO | RIGHT_CLIENT_INFO +Text | ttnpb.Right | RIGHT_CLIENT_PURGE | RIGHT_CLIENT_PURGE Text | ttnpb.Right | RIGHT_CLIENT_SETTINGS_BASIC | RIGHT_CLIENT_SETTINGS_BASIC Text | ttnpb.Right | RIGHT_CLIENT_SETTINGS_COLLABORATORS | RIGHT_CLIENT_SETTINGS_COLLABORATORS Text | ttnpb.Right | RIGHT_GATEWAY_ALL | RIGHT_GATEWAY_ALL @@ -1617,6 +1633,7 @@ Text | ttnpb.Right | RIGHT_GATEWAY_DELETE | RIGHT_GATEWAY_DELETE Text | ttnpb.Right | RIGHT_GATEWAY_INFO | RIGHT_GATEWAY_INFO Text | ttnpb.Right | RIGHT_GATEWAY_LINK | RIGHT_GATEWAY_LINK Text | ttnpb.Right | RIGHT_GATEWAY_LOCATION_READ | RIGHT_GATEWAY_LOCATION_READ +Text | ttnpb.Right | RIGHT_GATEWAY_PURGE | RIGHT_GATEWAY_PURGE Text | ttnpb.Right | RIGHT_GATEWAY_READ_SECRETS | RIGHT_GATEWAY_READ_SECRETS Text | ttnpb.Right | RIGHT_GATEWAY_SETTINGS_API_KEYS | RIGHT_GATEWAY_SETTINGS_API_KEYS Text | ttnpb.Right | RIGHT_GATEWAY_SETTINGS_BASIC | RIGHT_GATEWAY_SETTINGS_BASIC @@ -1635,6 +1652,7 @@ Text | ttnpb.Right | RIGHT_ORGANIZATION_DELETE | RIGHT_ORGANIZATION_DELETE Text | ttnpb.Right | RIGHT_ORGANIZATION_GATEWAYS_CREATE | RIGHT_ORGANIZATION_GATEWAYS_CREATE Text | ttnpb.Right | RIGHT_ORGANIZATION_GATEWAYS_LIST | RIGHT_ORGANIZATION_GATEWAYS_LIST Text | ttnpb.Right | RIGHT_ORGANIZATION_INFO | RIGHT_ORGANIZATION_INFO +Text | ttnpb.Right | RIGHT_ORGANIZATION_PURGE | RIGHT_ORGANIZATION_PURGE Text | ttnpb.Right | RIGHT_ORGANIZATION_SETTINGS_API_KEYS | RIGHT_ORGANIZATION_SETTINGS_API_KEYS Text | ttnpb.Right | RIGHT_ORGANIZATION_SETTINGS_BASIC | RIGHT_ORGANIZATION_SETTINGS_BASIC Text | ttnpb.Right | RIGHT_ORGANIZATION_SETTINGS_MEMBERS | RIGHT_ORGANIZATION_SETTINGS_MEMBERS @@ -1645,13 +1663,16 @@ Text | ttnpb.Right | RIGHT_USER_APPLICATIONS_LIST | RIGHT_USER_APPLICATIONS_LIST Text | ttnpb.Right | RIGHT_USER_AUTHORIZED_CLIENTS | RIGHT_USER_AUTHORIZED_CLIENTS Text | ttnpb.Right | RIGHT_USER_CLIENTS_CREATE | RIGHT_USER_CLIENTS_CREATE Text | ttnpb.Right | RIGHT_USER_CLIENTS_LIST | RIGHT_USER_CLIENTS_LIST +Text | ttnpb.Right | RIGHT_USER_CREATE | RIGHT_USER_CREATE Text | ttnpb.Right | RIGHT_USER_DELETE | RIGHT_USER_DELETE Text | ttnpb.Right | RIGHT_USER_GATEWAYS_CREATE | RIGHT_USER_GATEWAYS_CREATE Text | ttnpb.Right | RIGHT_USER_GATEWAYS_LIST | RIGHT_USER_GATEWAYS_LIST Text | ttnpb.Right | RIGHT_USER_INFO | RIGHT_USER_INFO +Text | ttnpb.Right | RIGHT_USER_LIST | RIGHT_USER_LIST Text | ttnpb.Right | RIGHT_USER_NOTIFICATIONS_READ | RIGHT_USER_NOTIFICATIONS_READ Text | ttnpb.Right | RIGHT_USER_ORGANIZATIONS_CREATE | RIGHT_USER_ORGANIZATIONS_CREATE Text | ttnpb.Right | RIGHT_USER_ORGANIZATIONS_LIST | RIGHT_USER_ORGANIZATIONS_LIST +Text | ttnpb.Right | RIGHT_USER_PURGE | RIGHT_USER_PURGE Text | ttnpb.Right | RIGHT_USER_SETTINGS_API_KEYS | RIGHT_USER_SETTINGS_API_KEYS Text | ttnpb.Right | RIGHT_USER_SETTINGS_BASIC | RIGHT_USER_SETTINGS_BASIC Text | ttnpb.Right | right_invalid | right_invalid diff --git a/pkg/ttnpb/user.pb.go b/pkg/ttnpb/user.pb.go index 7b1e8013e4..7c665595e2 100644 --- a/pkg/ttnpb/user.pb.go +++ b/pkg/ttnpb/user.pb.go @@ -92,6 +92,53 @@ func (ConsoleTheme) EnumDescriptor() ([]byte, []int) { return file_ttn_lorawan_v3_user_proto_rawDescGZIP(), []int{0} } +// Tutorial is a set of possible tutorials in the Console. +type Tutorial int32 + +const ( + Tutorial_TUTORIAL_UNKNOWN Tutorial = 0 + Tutorial_TUTORIAL_LIVE_DATA_SPLIT_VIEW Tutorial = 1 +) + +// Enum value maps for Tutorial. +var ( + Tutorial_name = map[int32]string{ + 0: "TUTORIAL_UNKNOWN", + 1: "TUTORIAL_LIVE_DATA_SPLIT_VIEW", + } + Tutorial_value = map[string]int32{ + "TUTORIAL_UNKNOWN": 0, + "TUTORIAL_LIVE_DATA_SPLIT_VIEW": 1, + } +) + +func (x Tutorial) Enum() *Tutorial { + p := new(Tutorial) + *p = x + return p +} + +func (x Tutorial) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (Tutorial) Descriptor() protoreflect.EnumDescriptor { + return file_ttn_lorawan_v3_user_proto_enumTypes[1].Descriptor() +} + +func (Tutorial) Type() protoreflect.EnumType { + return &file_ttn_lorawan_v3_user_proto_enumTypes[1] +} + +func (x Tutorial) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use Tutorial.Descriptor instead. +func (Tutorial) EnumDescriptor() ([]byte, []int) { + return file_ttn_lorawan_v3_user_proto_rawDescGZIP(), []int{1} +} + // DashboardLayout is a set of possible layout values to be used in the Console. type DashboardLayout int32 @@ -126,11 +173,11 @@ func (x DashboardLayout) String() string { } func (DashboardLayout) Descriptor() protoreflect.EnumDescriptor { - return file_ttn_lorawan_v3_user_proto_enumTypes[1].Descriptor() + return file_ttn_lorawan_v3_user_proto_enumTypes[2].Descriptor() } func (DashboardLayout) Type() protoreflect.EnumType { - return &file_ttn_lorawan_v3_user_proto_enumTypes[1] + return &file_ttn_lorawan_v3_user_proto_enumTypes[2] } func (x DashboardLayout) Number() protoreflect.EnumNumber { @@ -139,7 +186,55 @@ func (x DashboardLayout) Number() protoreflect.EnumNumber { // Deprecated: Use DashboardLayout.Descriptor instead. func (DashboardLayout) EnumDescriptor() ([]byte, []int) { - return file_ttn_lorawan_v3_user_proto_rawDescGZIP(), []int{1} + return file_ttn_lorawan_v3_user_proto_rawDescGZIP(), []int{2} +} + +// EmailNotificationPreferences is the message that defines the types of notifications for which the user wants to receive an email. +type EmailNotificationPreferences struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Types []NotificationType `protobuf:"varint,1,rep,packed,name=types,proto3,enum=ttn.lorawan.v3.NotificationType" json:"types,omitempty"` +} + +func (x *EmailNotificationPreferences) Reset() { + *x = EmailNotificationPreferences{} + if protoimpl.UnsafeEnabled { + mi := &file_ttn_lorawan_v3_user_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *EmailNotificationPreferences) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*EmailNotificationPreferences) ProtoMessage() {} + +func (x *EmailNotificationPreferences) ProtoReflect() protoreflect.Message { + mi := &file_ttn_lorawan_v3_user_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use EmailNotificationPreferences.ProtoReflect.Descriptor instead. +func (*EmailNotificationPreferences) Descriptor() ([]byte, []int) { + return file_ttn_lorawan_v3_user_proto_rawDescGZIP(), []int{0} +} + +func (x *EmailNotificationPreferences) GetTypes() []NotificationType { + if x != nil { + return x.Types + } + return nil } // UserConsolePreferences is the message that defines the user preferences for the Console. @@ -151,12 +246,13 @@ type UserConsolePreferences struct { ConsoleTheme ConsoleTheme `protobuf:"varint,1,opt,name=console_theme,json=consoleTheme,proto3,enum=ttn.lorawan.v3.ConsoleTheme" json:"console_theme,omitempty"` DashboardLayouts *UserConsolePreferences_DashboardLayouts `protobuf:"bytes,2,opt,name=dashboard_layouts,json=dashboardLayouts,proto3" json:"dashboard_layouts,omitempty"` SortBy *UserConsolePreferences_SortBy `protobuf:"bytes,3,opt,name=sort_by,json=sortBy,proto3" json:"sort_by,omitempty"` + Tutorials *UserConsolePreferences_Tutorials `protobuf:"bytes,4,opt,name=tutorials,proto3" json:"tutorials,omitempty"` } func (x *UserConsolePreferences) Reset() { *x = UserConsolePreferences{} if protoimpl.UnsafeEnabled { - mi := &file_ttn_lorawan_v3_user_proto_msgTypes[0] + mi := &file_ttn_lorawan_v3_user_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -169,7 +265,7 @@ func (x *UserConsolePreferences) String() string { func (*UserConsolePreferences) ProtoMessage() {} func (x *UserConsolePreferences) ProtoReflect() protoreflect.Message { - mi := &file_ttn_lorawan_v3_user_proto_msgTypes[0] + mi := &file_ttn_lorawan_v3_user_proto_msgTypes[1] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -182,7 +278,7 @@ func (x *UserConsolePreferences) ProtoReflect() protoreflect.Message { // Deprecated: Use UserConsolePreferences.ProtoReflect.Descriptor instead. func (*UserConsolePreferences) Descriptor() ([]byte, []int) { - return file_ttn_lorawan_v3_user_proto_rawDescGZIP(), []int{0} + return file_ttn_lorawan_v3_user_proto_rawDescGZIP(), []int{1} } func (x *UserConsolePreferences) GetConsoleTheme() ConsoleTheme { @@ -206,6 +302,13 @@ func (x *UserConsolePreferences) GetSortBy() *UserConsolePreferences_SortBy { return nil } +func (x *UserConsolePreferences) GetTutorials() *UserConsolePreferences_Tutorials { + if x != nil { + return x.Tutorials + } + return nil +} + // User is the message that defines a user on the network. type User struct { state protoimpl.MessageState @@ -264,13 +367,16 @@ type User struct { // This information is public and can be seen by any authenticated user in the network. ProfilePicture *Picture `protobuf:"bytes,18,opt,name=profile_picture,json=profilePicture,proto3" json:"profile_picture,omitempty"` // Console preferences contains the user's preferences regarding the behavior of the Console. - ConsolePreferences *UserConsolePreferences `protobuf:"bytes,25,opt,name=console_preferences,json=consolePreferences,proto3" json:"console_preferences,omitempty"` + ConsolePreferences *UserConsolePreferences `protobuf:"bytes,25,opt,name=console_preferences,json=consolePreferences,proto3" json:"console_preferences,omitempty"` + EmailNotificationPreferences *EmailNotificationPreferences `protobuf:"bytes,26,opt,name=email_notification_preferences,json=emailNotificationPreferences,proto3" json:"email_notification_preferences,omitempty"` + // Universal rights are capable of restricting the user's interactions with the API, be it an admin or not. + UniversalRights []Right `protobuf:"varint,27,rep,packed,name=universal_rights,json=universalRights,proto3,enum=ttn.lorawan.v3.Right" json:"universal_rights,omitempty"` } func (x *User) Reset() { *x = User{} if protoimpl.UnsafeEnabled { - mi := &file_ttn_lorawan_v3_user_proto_msgTypes[1] + mi := &file_ttn_lorawan_v3_user_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -283,7 +389,7 @@ func (x *User) String() string { func (*User) ProtoMessage() {} func (x *User) ProtoReflect() protoreflect.Message { - mi := &file_ttn_lorawan_v3_user_proto_msgTypes[1] + mi := &file_ttn_lorawan_v3_user_proto_msgTypes[2] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -296,7 +402,7 @@ func (x *User) ProtoReflect() protoreflect.Message { // Deprecated: Use User.ProtoReflect.Descriptor instead. func (*User) Descriptor() ([]byte, []int) { - return file_ttn_lorawan_v3_user_proto_rawDescGZIP(), []int{1} + return file_ttn_lorawan_v3_user_proto_rawDescGZIP(), []int{2} } func (x *User) GetIds() *UserIdentifiers { @@ -447,6 +553,20 @@ func (x *User) GetConsolePreferences() *UserConsolePreferences { return nil } +func (x *User) GetEmailNotificationPreferences() *EmailNotificationPreferences { + if x != nil { + return x.EmailNotificationPreferences + } + return nil +} + +func (x *User) GetUniversalRights() []Right { + if x != nil { + return x.UniversalRights + } + return nil +} + type Users struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -458,7 +578,7 @@ type Users struct { func (x *Users) Reset() { *x = Users{} if protoimpl.UnsafeEnabled { - mi := &file_ttn_lorawan_v3_user_proto_msgTypes[2] + mi := &file_ttn_lorawan_v3_user_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -471,7 +591,7 @@ func (x *Users) String() string { func (*Users) ProtoMessage() {} func (x *Users) ProtoReflect() protoreflect.Message { - mi := &file_ttn_lorawan_v3_user_proto_msgTypes[2] + mi := &file_ttn_lorawan_v3_user_proto_msgTypes[3] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -484,7 +604,7 @@ func (x *Users) ProtoReflect() protoreflect.Message { // Deprecated: Use Users.ProtoReflect.Descriptor instead. func (*Users) Descriptor() ([]byte, []int) { - return file_ttn_lorawan_v3_user_proto_rawDescGZIP(), []int{2} + return file_ttn_lorawan_v3_user_proto_rawDescGZIP(), []int{3} } func (x *Users) GetUsers() []*User { @@ -507,7 +627,7 @@ type GetUserRequest struct { func (x *GetUserRequest) Reset() { *x = GetUserRequest{} if protoimpl.UnsafeEnabled { - mi := &file_ttn_lorawan_v3_user_proto_msgTypes[3] + mi := &file_ttn_lorawan_v3_user_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -520,7 +640,7 @@ func (x *GetUserRequest) String() string { func (*GetUserRequest) ProtoMessage() {} func (x *GetUserRequest) ProtoReflect() protoreflect.Message { - mi := &file_ttn_lorawan_v3_user_proto_msgTypes[3] + mi := &file_ttn_lorawan_v3_user_proto_msgTypes[4] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -533,7 +653,7 @@ func (x *GetUserRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetUserRequest.ProtoReflect.Descriptor instead. func (*GetUserRequest) Descriptor() ([]byte, []int) { - return file_ttn_lorawan_v3_user_proto_rawDescGZIP(), []int{3} + return file_ttn_lorawan_v3_user_proto_rawDescGZIP(), []int{4} } func (x *GetUserRequest) GetUserIds() *UserIdentifiers { @@ -571,7 +691,7 @@ type ListUsersRequest struct { func (x *ListUsersRequest) Reset() { *x = ListUsersRequest{} if protoimpl.UnsafeEnabled { - mi := &file_ttn_lorawan_v3_user_proto_msgTypes[4] + mi := &file_ttn_lorawan_v3_user_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -584,7 +704,7 @@ func (x *ListUsersRequest) String() string { func (*ListUsersRequest) ProtoMessage() {} func (x *ListUsersRequest) ProtoReflect() protoreflect.Message { - mi := &file_ttn_lorawan_v3_user_proto_msgTypes[4] + mi := &file_ttn_lorawan_v3_user_proto_msgTypes[5] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -597,7 +717,7 @@ func (x *ListUsersRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListUsersRequest.ProtoReflect.Descriptor instead. func (*ListUsersRequest) Descriptor() ([]byte, []int) { - return file_ttn_lorawan_v3_user_proto_rawDescGZIP(), []int{4} + return file_ttn_lorawan_v3_user_proto_rawDescGZIP(), []int{5} } func (x *ListUsersRequest) GetFieldMask() *fieldmaskpb.FieldMask { @@ -648,7 +768,7 @@ type CreateUserRequest struct { func (x *CreateUserRequest) Reset() { *x = CreateUserRequest{} if protoimpl.UnsafeEnabled { - mi := &file_ttn_lorawan_v3_user_proto_msgTypes[5] + mi := &file_ttn_lorawan_v3_user_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -661,7 +781,7 @@ func (x *CreateUserRequest) String() string { func (*CreateUserRequest) ProtoMessage() {} func (x *CreateUserRequest) ProtoReflect() protoreflect.Message { - mi := &file_ttn_lorawan_v3_user_proto_msgTypes[5] + mi := &file_ttn_lorawan_v3_user_proto_msgTypes[6] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -674,7 +794,7 @@ func (x *CreateUserRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CreateUserRequest.ProtoReflect.Descriptor instead. func (*CreateUserRequest) Descriptor() ([]byte, []int) { - return file_ttn_lorawan_v3_user_proto_rawDescGZIP(), []int{5} + return file_ttn_lorawan_v3_user_proto_rawDescGZIP(), []int{6} } func (x *CreateUserRequest) GetUser() *User { @@ -704,7 +824,7 @@ type UpdateUserRequest struct { func (x *UpdateUserRequest) Reset() { *x = UpdateUserRequest{} if protoimpl.UnsafeEnabled { - mi := &file_ttn_lorawan_v3_user_proto_msgTypes[6] + mi := &file_ttn_lorawan_v3_user_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -717,7 +837,7 @@ func (x *UpdateUserRequest) String() string { func (*UpdateUserRequest) ProtoMessage() {} func (x *UpdateUserRequest) ProtoReflect() protoreflect.Message { - mi := &file_ttn_lorawan_v3_user_proto_msgTypes[6] + mi := &file_ttn_lorawan_v3_user_proto_msgTypes[7] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -730,7 +850,7 @@ func (x *UpdateUserRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use UpdateUserRequest.ProtoReflect.Descriptor instead. func (*UpdateUserRequest) Descriptor() ([]byte, []int) { - return file_ttn_lorawan_v3_user_proto_rawDescGZIP(), []int{6} + return file_ttn_lorawan_v3_user_proto_rawDescGZIP(), []int{7} } func (x *UpdateUserRequest) GetUser() *User { @@ -758,7 +878,7 @@ type CreateTemporaryPasswordRequest struct { func (x *CreateTemporaryPasswordRequest) Reset() { *x = CreateTemporaryPasswordRequest{} if protoimpl.UnsafeEnabled { - mi := &file_ttn_lorawan_v3_user_proto_msgTypes[7] + mi := &file_ttn_lorawan_v3_user_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -771,7 +891,7 @@ func (x *CreateTemporaryPasswordRequest) String() string { func (*CreateTemporaryPasswordRequest) ProtoMessage() {} func (x *CreateTemporaryPasswordRequest) ProtoReflect() protoreflect.Message { - mi := &file_ttn_lorawan_v3_user_proto_msgTypes[7] + mi := &file_ttn_lorawan_v3_user_proto_msgTypes[8] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -784,7 +904,7 @@ func (x *CreateTemporaryPasswordRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CreateTemporaryPasswordRequest.ProtoReflect.Descriptor instead. func (*CreateTemporaryPasswordRequest) Descriptor() ([]byte, []int) { - return file_ttn_lorawan_v3_user_proto_rawDescGZIP(), []int{7} + return file_ttn_lorawan_v3_user_proto_rawDescGZIP(), []int{8} } func (x *CreateTemporaryPasswordRequest) GetUserIds() *UserIdentifiers { @@ -809,7 +929,7 @@ type UpdateUserPasswordRequest struct { func (x *UpdateUserPasswordRequest) Reset() { *x = UpdateUserPasswordRequest{} if protoimpl.UnsafeEnabled { - mi := &file_ttn_lorawan_v3_user_proto_msgTypes[8] + mi := &file_ttn_lorawan_v3_user_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -822,7 +942,7 @@ func (x *UpdateUserPasswordRequest) String() string { func (*UpdateUserPasswordRequest) ProtoMessage() {} func (x *UpdateUserPasswordRequest) ProtoReflect() protoreflect.Message { - mi := &file_ttn_lorawan_v3_user_proto_msgTypes[8] + mi := &file_ttn_lorawan_v3_user_proto_msgTypes[9] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -835,7 +955,7 @@ func (x *UpdateUserPasswordRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use UpdateUserPasswordRequest.ProtoReflect.Descriptor instead. func (*UpdateUserPasswordRequest) Descriptor() ([]byte, []int) { - return file_ttn_lorawan_v3_user_proto_rawDescGZIP(), []int{8} + return file_ttn_lorawan_v3_user_proto_rawDescGZIP(), []int{9} } func (x *UpdateUserPasswordRequest) GetUserIds() *UserIdentifiers { @@ -884,7 +1004,7 @@ type ListUserAPIKeysRequest struct { func (x *ListUserAPIKeysRequest) Reset() { *x = ListUserAPIKeysRequest{} if protoimpl.UnsafeEnabled { - mi := &file_ttn_lorawan_v3_user_proto_msgTypes[9] + mi := &file_ttn_lorawan_v3_user_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -897,7 +1017,7 @@ func (x *ListUserAPIKeysRequest) String() string { func (*ListUserAPIKeysRequest) ProtoMessage() {} func (x *ListUserAPIKeysRequest) ProtoReflect() protoreflect.Message { - mi := &file_ttn_lorawan_v3_user_proto_msgTypes[9] + mi := &file_ttn_lorawan_v3_user_proto_msgTypes[10] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -910,7 +1030,7 @@ func (x *ListUserAPIKeysRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListUserAPIKeysRequest.ProtoReflect.Descriptor instead. func (*ListUserAPIKeysRequest) Descriptor() ([]byte, []int) { - return file_ttn_lorawan_v3_user_proto_rawDescGZIP(), []int{9} + return file_ttn_lorawan_v3_user_proto_rawDescGZIP(), []int{10} } func (x *ListUserAPIKeysRequest) GetUserIds() *UserIdentifiers { @@ -954,7 +1074,7 @@ type GetUserAPIKeyRequest struct { func (x *GetUserAPIKeyRequest) Reset() { *x = GetUserAPIKeyRequest{} if protoimpl.UnsafeEnabled { - mi := &file_ttn_lorawan_v3_user_proto_msgTypes[10] + mi := &file_ttn_lorawan_v3_user_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -967,7 +1087,7 @@ func (x *GetUserAPIKeyRequest) String() string { func (*GetUserAPIKeyRequest) ProtoMessage() {} func (x *GetUserAPIKeyRequest) ProtoReflect() protoreflect.Message { - mi := &file_ttn_lorawan_v3_user_proto_msgTypes[10] + mi := &file_ttn_lorawan_v3_user_proto_msgTypes[11] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -980,7 +1100,7 @@ func (x *GetUserAPIKeyRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetUserAPIKeyRequest.ProtoReflect.Descriptor instead. func (*GetUserAPIKeyRequest) Descriptor() ([]byte, []int) { - return file_ttn_lorawan_v3_user_proto_rawDescGZIP(), []int{10} + return file_ttn_lorawan_v3_user_proto_rawDescGZIP(), []int{11} } func (x *GetUserAPIKeyRequest) GetUserIds() *UserIdentifiers { @@ -1011,7 +1131,7 @@ type CreateUserAPIKeyRequest struct { func (x *CreateUserAPIKeyRequest) Reset() { *x = CreateUserAPIKeyRequest{} if protoimpl.UnsafeEnabled { - mi := &file_ttn_lorawan_v3_user_proto_msgTypes[11] + mi := &file_ttn_lorawan_v3_user_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1024,7 +1144,7 @@ func (x *CreateUserAPIKeyRequest) String() string { func (*CreateUserAPIKeyRequest) ProtoMessage() {} func (x *CreateUserAPIKeyRequest) ProtoReflect() protoreflect.Message { - mi := &file_ttn_lorawan_v3_user_proto_msgTypes[11] + mi := &file_ttn_lorawan_v3_user_proto_msgTypes[12] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1037,7 +1157,7 @@ func (x *CreateUserAPIKeyRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CreateUserAPIKeyRequest.ProtoReflect.Descriptor instead. func (*CreateUserAPIKeyRequest) Descriptor() ([]byte, []int) { - return file_ttn_lorawan_v3_user_proto_rawDescGZIP(), []int{11} + return file_ttn_lorawan_v3_user_proto_rawDescGZIP(), []int{12} } func (x *CreateUserAPIKeyRequest) GetUserIds() *UserIdentifiers { @@ -1082,7 +1202,7 @@ type UpdateUserAPIKeyRequest struct { func (x *UpdateUserAPIKeyRequest) Reset() { *x = UpdateUserAPIKeyRequest{} if protoimpl.UnsafeEnabled { - mi := &file_ttn_lorawan_v3_user_proto_msgTypes[12] + mi := &file_ttn_lorawan_v3_user_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1095,7 +1215,7 @@ func (x *UpdateUserAPIKeyRequest) String() string { func (*UpdateUserAPIKeyRequest) ProtoMessage() {} func (x *UpdateUserAPIKeyRequest) ProtoReflect() protoreflect.Message { - mi := &file_ttn_lorawan_v3_user_proto_msgTypes[12] + mi := &file_ttn_lorawan_v3_user_proto_msgTypes[13] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1108,7 +1228,7 @@ func (x *UpdateUserAPIKeyRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use UpdateUserAPIKeyRequest.ProtoReflect.Descriptor instead. func (*UpdateUserAPIKeyRequest) Descriptor() ([]byte, []int) { - return file_ttn_lorawan_v3_user_proto_rawDescGZIP(), []int{12} + return file_ttn_lorawan_v3_user_proto_rawDescGZIP(), []int{13} } func (x *UpdateUserAPIKeyRequest) GetUserIds() *UserIdentifiers { @@ -1144,7 +1264,7 @@ type DeleteUserAPIKeyRequest struct { func (x *DeleteUserAPIKeyRequest) Reset() { *x = DeleteUserAPIKeyRequest{} if protoimpl.UnsafeEnabled { - mi := &file_ttn_lorawan_v3_user_proto_msgTypes[13] + mi := &file_ttn_lorawan_v3_user_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1157,7 +1277,7 @@ func (x *DeleteUserAPIKeyRequest) String() string { func (*DeleteUserAPIKeyRequest) ProtoMessage() {} func (x *DeleteUserAPIKeyRequest) ProtoReflect() protoreflect.Message { - mi := &file_ttn_lorawan_v3_user_proto_msgTypes[13] + mi := &file_ttn_lorawan_v3_user_proto_msgTypes[14] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1170,7 +1290,7 @@ func (x *DeleteUserAPIKeyRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteUserAPIKeyRequest.ProtoReflect.Descriptor instead. func (*DeleteUserAPIKeyRequest) Descriptor() ([]byte, []int) { - return file_ttn_lorawan_v3_user_proto_rawDescGZIP(), []int{13} + return file_ttn_lorawan_v3_user_proto_rawDescGZIP(), []int{14} } func (x *DeleteUserAPIKeyRequest) GetUserIds() *UserIdentifiers { @@ -1204,7 +1324,7 @@ type Invitation struct { func (x *Invitation) Reset() { *x = Invitation{} if protoimpl.UnsafeEnabled { - mi := &file_ttn_lorawan_v3_user_proto_msgTypes[14] + mi := &file_ttn_lorawan_v3_user_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1217,7 +1337,7 @@ func (x *Invitation) String() string { func (*Invitation) ProtoMessage() {} func (x *Invitation) ProtoReflect() protoreflect.Message { - mi := &file_ttn_lorawan_v3_user_proto_msgTypes[14] + mi := &file_ttn_lorawan_v3_user_proto_msgTypes[15] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1230,7 +1350,7 @@ func (x *Invitation) ProtoReflect() protoreflect.Message { // Deprecated: Use Invitation.ProtoReflect.Descriptor instead. func (*Invitation) Descriptor() ([]byte, []int) { - return file_ttn_lorawan_v3_user_proto_rawDescGZIP(), []int{14} + return file_ttn_lorawan_v3_user_proto_rawDescGZIP(), []int{15} } func (x *Invitation) GetEmail() string { @@ -1296,7 +1416,7 @@ type ListInvitationsRequest struct { func (x *ListInvitationsRequest) Reset() { *x = ListInvitationsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_ttn_lorawan_v3_user_proto_msgTypes[15] + mi := &file_ttn_lorawan_v3_user_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1309,7 +1429,7 @@ func (x *ListInvitationsRequest) String() string { func (*ListInvitationsRequest) ProtoMessage() {} func (x *ListInvitationsRequest) ProtoReflect() protoreflect.Message { - mi := &file_ttn_lorawan_v3_user_proto_msgTypes[15] + mi := &file_ttn_lorawan_v3_user_proto_msgTypes[16] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1322,7 +1442,7 @@ func (x *ListInvitationsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListInvitationsRequest.ProtoReflect.Descriptor instead. func (*ListInvitationsRequest) Descriptor() ([]byte, []int) { - return file_ttn_lorawan_v3_user_proto_rawDescGZIP(), []int{15} + return file_ttn_lorawan_v3_user_proto_rawDescGZIP(), []int{16} } func (x *ListInvitationsRequest) GetLimit() uint32 { @@ -1350,7 +1470,7 @@ type Invitations struct { func (x *Invitations) Reset() { *x = Invitations{} if protoimpl.UnsafeEnabled { - mi := &file_ttn_lorawan_v3_user_proto_msgTypes[16] + mi := &file_ttn_lorawan_v3_user_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1363,7 +1483,7 @@ func (x *Invitations) String() string { func (*Invitations) ProtoMessage() {} func (x *Invitations) ProtoReflect() protoreflect.Message { - mi := &file_ttn_lorawan_v3_user_proto_msgTypes[16] + mi := &file_ttn_lorawan_v3_user_proto_msgTypes[17] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1376,7 +1496,7 @@ func (x *Invitations) ProtoReflect() protoreflect.Message { // Deprecated: Use Invitations.ProtoReflect.Descriptor instead. func (*Invitations) Descriptor() ([]byte, []int) { - return file_ttn_lorawan_v3_user_proto_rawDescGZIP(), []int{16} + return file_ttn_lorawan_v3_user_proto_rawDescGZIP(), []int{17} } func (x *Invitations) GetInvitations() []*Invitation { @@ -1397,7 +1517,7 @@ type SendInvitationRequest struct { func (x *SendInvitationRequest) Reset() { *x = SendInvitationRequest{} if protoimpl.UnsafeEnabled { - mi := &file_ttn_lorawan_v3_user_proto_msgTypes[17] + mi := &file_ttn_lorawan_v3_user_proto_msgTypes[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1410,7 +1530,7 @@ func (x *SendInvitationRequest) String() string { func (*SendInvitationRequest) ProtoMessage() {} func (x *SendInvitationRequest) ProtoReflect() protoreflect.Message { - mi := &file_ttn_lorawan_v3_user_proto_msgTypes[17] + mi := &file_ttn_lorawan_v3_user_proto_msgTypes[18] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1423,7 +1543,7 @@ func (x *SendInvitationRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use SendInvitationRequest.ProtoReflect.Descriptor instead. func (*SendInvitationRequest) Descriptor() ([]byte, []int) { - return file_ttn_lorawan_v3_user_proto_rawDescGZIP(), []int{17} + return file_ttn_lorawan_v3_user_proto_rawDescGZIP(), []int{18} } func (x *SendInvitationRequest) GetEmail() string { @@ -1444,7 +1564,7 @@ type DeleteInvitationRequest struct { func (x *DeleteInvitationRequest) Reset() { *x = DeleteInvitationRequest{} if protoimpl.UnsafeEnabled { - mi := &file_ttn_lorawan_v3_user_proto_msgTypes[18] + mi := &file_ttn_lorawan_v3_user_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1457,7 +1577,7 @@ func (x *DeleteInvitationRequest) String() string { func (*DeleteInvitationRequest) ProtoMessage() {} func (x *DeleteInvitationRequest) ProtoReflect() protoreflect.Message { - mi := &file_ttn_lorawan_v3_user_proto_msgTypes[18] + mi := &file_ttn_lorawan_v3_user_proto_msgTypes[19] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1470,7 +1590,7 @@ func (x *DeleteInvitationRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteInvitationRequest.ProtoReflect.Descriptor instead. func (*DeleteInvitationRequest) Descriptor() ([]byte, []int) { - return file_ttn_lorawan_v3_user_proto_rawDescGZIP(), []int{18} + return file_ttn_lorawan_v3_user_proto_rawDescGZIP(), []int{19} } func (x *DeleteInvitationRequest) GetEmail() string { @@ -1492,7 +1612,7 @@ type UserSessionIdentifiers struct { func (x *UserSessionIdentifiers) Reset() { *x = UserSessionIdentifiers{} if protoimpl.UnsafeEnabled { - mi := &file_ttn_lorawan_v3_user_proto_msgTypes[19] + mi := &file_ttn_lorawan_v3_user_proto_msgTypes[20] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1505,7 +1625,7 @@ func (x *UserSessionIdentifiers) String() string { func (*UserSessionIdentifiers) ProtoMessage() {} func (x *UserSessionIdentifiers) ProtoReflect() protoreflect.Message { - mi := &file_ttn_lorawan_v3_user_proto_msgTypes[19] + mi := &file_ttn_lorawan_v3_user_proto_msgTypes[20] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1518,7 +1638,7 @@ func (x *UserSessionIdentifiers) ProtoReflect() protoreflect.Message { // Deprecated: Use UserSessionIdentifiers.ProtoReflect.Descriptor instead. func (*UserSessionIdentifiers) Descriptor() ([]byte, []int) { - return file_ttn_lorawan_v3_user_proto_rawDescGZIP(), []int{19} + return file_ttn_lorawan_v3_user_proto_rawDescGZIP(), []int{20} } func (x *UserSessionIdentifiers) GetUserIds() *UserIdentifiers { @@ -1552,7 +1672,7 @@ type UserSession struct { func (x *UserSession) Reset() { *x = UserSession{} if protoimpl.UnsafeEnabled { - mi := &file_ttn_lorawan_v3_user_proto_msgTypes[20] + mi := &file_ttn_lorawan_v3_user_proto_msgTypes[21] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1565,7 +1685,7 @@ func (x *UserSession) String() string { func (*UserSession) ProtoMessage() {} func (x *UserSession) ProtoReflect() protoreflect.Message { - mi := &file_ttn_lorawan_v3_user_proto_msgTypes[20] + mi := &file_ttn_lorawan_v3_user_proto_msgTypes[21] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1578,7 +1698,7 @@ func (x *UserSession) ProtoReflect() protoreflect.Message { // Deprecated: Use UserSession.ProtoReflect.Descriptor instead. func (*UserSession) Descriptor() ([]byte, []int) { - return file_ttn_lorawan_v3_user_proto_rawDescGZIP(), []int{20} + return file_ttn_lorawan_v3_user_proto_rawDescGZIP(), []int{21} } func (x *UserSession) GetUserIds() *UserIdentifiers { @@ -1634,7 +1754,7 @@ type UserSessions struct { func (x *UserSessions) Reset() { *x = UserSessions{} if protoimpl.UnsafeEnabled { - mi := &file_ttn_lorawan_v3_user_proto_msgTypes[21] + mi := &file_ttn_lorawan_v3_user_proto_msgTypes[22] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1647,7 +1767,7 @@ func (x *UserSessions) String() string { func (*UserSessions) ProtoMessage() {} func (x *UserSessions) ProtoReflect() protoreflect.Message { - mi := &file_ttn_lorawan_v3_user_proto_msgTypes[21] + mi := &file_ttn_lorawan_v3_user_proto_msgTypes[22] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1660,7 +1780,7 @@ func (x *UserSessions) ProtoReflect() protoreflect.Message { // Deprecated: Use UserSessions.ProtoReflect.Descriptor instead. func (*UserSessions) Descriptor() ([]byte, []int) { - return file_ttn_lorawan_v3_user_proto_rawDescGZIP(), []int{21} + return file_ttn_lorawan_v3_user_proto_rawDescGZIP(), []int{22} } func (x *UserSessions) GetSessions() []*UserSession { @@ -1688,7 +1808,7 @@ type ListUserSessionsRequest struct { func (x *ListUserSessionsRequest) Reset() { *x = ListUserSessionsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_ttn_lorawan_v3_user_proto_msgTypes[22] + mi := &file_ttn_lorawan_v3_user_proto_msgTypes[23] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1701,7 +1821,7 @@ func (x *ListUserSessionsRequest) String() string { func (*ListUserSessionsRequest) ProtoMessage() {} func (x *ListUserSessionsRequest) ProtoReflect() protoreflect.Message { - mi := &file_ttn_lorawan_v3_user_proto_msgTypes[22] + mi := &file_ttn_lorawan_v3_user_proto_msgTypes[23] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1714,7 +1834,7 @@ func (x *ListUserSessionsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListUserSessionsRequest.ProtoReflect.Descriptor instead. func (*ListUserSessionsRequest) Descriptor() ([]byte, []int) { - return file_ttn_lorawan_v3_user_proto_rawDescGZIP(), []int{22} + return file_ttn_lorawan_v3_user_proto_rawDescGZIP(), []int{23} } func (x *ListUserSessionsRequest) GetUserIds() *UserIdentifiers { @@ -1761,7 +1881,7 @@ type LoginToken struct { func (x *LoginToken) Reset() { *x = LoginToken{} if protoimpl.UnsafeEnabled { - mi := &file_ttn_lorawan_v3_user_proto_msgTypes[23] + mi := &file_ttn_lorawan_v3_user_proto_msgTypes[24] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1774,7 +1894,7 @@ func (x *LoginToken) String() string { func (*LoginToken) ProtoMessage() {} func (x *LoginToken) ProtoReflect() protoreflect.Message { - mi := &file_ttn_lorawan_v3_user_proto_msgTypes[23] + mi := &file_ttn_lorawan_v3_user_proto_msgTypes[24] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1787,7 +1907,7 @@ func (x *LoginToken) ProtoReflect() protoreflect.Message { // Deprecated: Use LoginToken.ProtoReflect.Descriptor instead. func (*LoginToken) Descriptor() ([]byte, []int) { - return file_ttn_lorawan_v3_user_proto_rawDescGZIP(), []int{23} + return file_ttn_lorawan_v3_user_proto_rawDescGZIP(), []int{24} } func (x *LoginToken) GetUserIds() *UserIdentifiers { @@ -1846,7 +1966,7 @@ type CreateLoginTokenRequest struct { func (x *CreateLoginTokenRequest) Reset() { *x = CreateLoginTokenRequest{} if protoimpl.UnsafeEnabled { - mi := &file_ttn_lorawan_v3_user_proto_msgTypes[24] + mi := &file_ttn_lorawan_v3_user_proto_msgTypes[25] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1859,7 +1979,7 @@ func (x *CreateLoginTokenRequest) String() string { func (*CreateLoginTokenRequest) ProtoMessage() {} func (x *CreateLoginTokenRequest) ProtoReflect() protoreflect.Message { - mi := &file_ttn_lorawan_v3_user_proto_msgTypes[24] + mi := &file_ttn_lorawan_v3_user_proto_msgTypes[25] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1872,7 +1992,7 @@ func (x *CreateLoginTokenRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CreateLoginTokenRequest.ProtoReflect.Descriptor instead. func (*CreateLoginTokenRequest) Descriptor() ([]byte, []int) { - return file_ttn_lorawan_v3_user_proto_rawDescGZIP(), []int{24} + return file_ttn_lorawan_v3_user_proto_rawDescGZIP(), []int{25} } func (x *CreateLoginTokenRequest) GetUserIds() *UserIdentifiers { @@ -1902,7 +2022,7 @@ type CreateLoginTokenResponse struct { func (x *CreateLoginTokenResponse) Reset() { *x = CreateLoginTokenResponse{} if protoimpl.UnsafeEnabled { - mi := &file_ttn_lorawan_v3_user_proto_msgTypes[25] + mi := &file_ttn_lorawan_v3_user_proto_msgTypes[26] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1915,7 +2035,7 @@ func (x *CreateLoginTokenResponse) String() string { func (*CreateLoginTokenResponse) ProtoMessage() {} func (x *CreateLoginTokenResponse) ProtoReflect() protoreflect.Message { - mi := &file_ttn_lorawan_v3_user_proto_msgTypes[25] + mi := &file_ttn_lorawan_v3_user_proto_msgTypes[26] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1928,7 +2048,7 @@ func (x *CreateLoginTokenResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use CreateLoginTokenResponse.ProtoReflect.Descriptor instead. func (*CreateLoginTokenResponse) Descriptor() ([]byte, []int) { - return file_ttn_lorawan_v3_user_proto_rawDescGZIP(), []int{25} + return file_ttn_lorawan_v3_user_proto_rawDescGZIP(), []int{26} } func (x *CreateLoginTokenResponse) GetToken() string { @@ -1951,7 +2071,7 @@ type UserBookmark struct { func (x *UserBookmark) Reset() { *x = UserBookmark{} if protoimpl.UnsafeEnabled { - mi := &file_ttn_lorawan_v3_user_proto_msgTypes[26] + mi := &file_ttn_lorawan_v3_user_proto_msgTypes[27] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1964,7 +2084,7 @@ func (x *UserBookmark) String() string { func (*UserBookmark) ProtoMessage() {} func (x *UserBookmark) ProtoReflect() protoreflect.Message { - mi := &file_ttn_lorawan_v3_user_proto_msgTypes[26] + mi := &file_ttn_lorawan_v3_user_proto_msgTypes[27] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1977,7 +2097,7 @@ func (x *UserBookmark) ProtoReflect() protoreflect.Message { // Deprecated: Use UserBookmark.ProtoReflect.Descriptor instead. func (*UserBookmark) Descriptor() ([]byte, []int) { - return file_ttn_lorawan_v3_user_proto_rawDescGZIP(), []int{26} + return file_ttn_lorawan_v3_user_proto_rawDescGZIP(), []int{27} } func (x *UserBookmark) GetUserIds() *UserIdentifiers { @@ -2012,7 +2132,7 @@ type UserBookmarks struct { func (x *UserBookmarks) Reset() { *x = UserBookmarks{} if protoimpl.UnsafeEnabled { - mi := &file_ttn_lorawan_v3_user_proto_msgTypes[27] + mi := &file_ttn_lorawan_v3_user_proto_msgTypes[28] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2025,7 +2145,7 @@ func (x *UserBookmarks) String() string { func (*UserBookmarks) ProtoMessage() {} func (x *UserBookmarks) ProtoReflect() protoreflect.Message { - mi := &file_ttn_lorawan_v3_user_proto_msgTypes[27] + mi := &file_ttn_lorawan_v3_user_proto_msgTypes[28] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2038,7 +2158,7 @@ func (x *UserBookmarks) ProtoReflect() protoreflect.Message { // Deprecated: Use UserBookmarks.ProtoReflect.Descriptor instead. func (*UserBookmarks) Descriptor() ([]byte, []int) { - return file_ttn_lorawan_v3_user_proto_rawDescGZIP(), []int{27} + return file_ttn_lorawan_v3_user_proto_rawDescGZIP(), []int{28} } func (x *UserBookmarks) GetBookmarks() []*UserBookmark { @@ -2060,7 +2180,7 @@ type CreateUserBookmarkRequest struct { func (x *CreateUserBookmarkRequest) Reset() { *x = CreateUserBookmarkRequest{} if protoimpl.UnsafeEnabled { - mi := &file_ttn_lorawan_v3_user_proto_msgTypes[28] + mi := &file_ttn_lorawan_v3_user_proto_msgTypes[29] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2073,7 +2193,7 @@ func (x *CreateUserBookmarkRequest) String() string { func (*CreateUserBookmarkRequest) ProtoMessage() {} func (x *CreateUserBookmarkRequest) ProtoReflect() protoreflect.Message { - mi := &file_ttn_lorawan_v3_user_proto_msgTypes[28] + mi := &file_ttn_lorawan_v3_user_proto_msgTypes[29] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2086,7 +2206,7 @@ func (x *CreateUserBookmarkRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CreateUserBookmarkRequest.ProtoReflect.Descriptor instead. func (*CreateUserBookmarkRequest) Descriptor() ([]byte, []int) { - return file_ttn_lorawan_v3_user_proto_rawDescGZIP(), []int{28} + return file_ttn_lorawan_v3_user_proto_rawDescGZIP(), []int{29} } func (x *CreateUserBookmarkRequest) GetUserIds() *UserIdentifiers { @@ -2126,7 +2246,7 @@ type ListUserBookmarksRequest struct { func (x *ListUserBookmarksRequest) Reset() { *x = ListUserBookmarksRequest{} if protoimpl.UnsafeEnabled { - mi := &file_ttn_lorawan_v3_user_proto_msgTypes[29] + mi := &file_ttn_lorawan_v3_user_proto_msgTypes[30] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2139,7 +2259,7 @@ func (x *ListUserBookmarksRequest) String() string { func (*ListUserBookmarksRequest) ProtoMessage() {} func (x *ListUserBookmarksRequest) ProtoReflect() protoreflect.Message { - mi := &file_ttn_lorawan_v3_user_proto_msgTypes[29] + mi := &file_ttn_lorawan_v3_user_proto_msgTypes[30] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2152,7 +2272,7 @@ func (x *ListUserBookmarksRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListUserBookmarksRequest.ProtoReflect.Descriptor instead. func (*ListUserBookmarksRequest) Descriptor() ([]byte, []int) { - return file_ttn_lorawan_v3_user_proto_rawDescGZIP(), []int{29} + return file_ttn_lorawan_v3_user_proto_rawDescGZIP(), []int{30} } func (x *ListUserBookmarksRequest) GetUserIds() *UserIdentifiers { @@ -2209,7 +2329,7 @@ type DeleteUserBookmarkRequest struct { func (x *DeleteUserBookmarkRequest) Reset() { *x = DeleteUserBookmarkRequest{} if protoimpl.UnsafeEnabled { - mi := &file_ttn_lorawan_v3_user_proto_msgTypes[30] + mi := &file_ttn_lorawan_v3_user_proto_msgTypes[31] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2222,7 +2342,7 @@ func (x *DeleteUserBookmarkRequest) String() string { func (*DeleteUserBookmarkRequest) ProtoMessage() {} func (x *DeleteUserBookmarkRequest) ProtoReflect() protoreflect.Message { - mi := &file_ttn_lorawan_v3_user_proto_msgTypes[30] + mi := &file_ttn_lorawan_v3_user_proto_msgTypes[31] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2235,7 +2355,7 @@ func (x *DeleteUserBookmarkRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteUserBookmarkRequest.ProtoReflect.Descriptor instead. func (*DeleteUserBookmarkRequest) Descriptor() ([]byte, []int) { - return file_ttn_lorawan_v3_user_proto_rawDescGZIP(), []int{30} + return file_ttn_lorawan_v3_user_proto_rawDescGZIP(), []int{31} } func (x *DeleteUserBookmarkRequest) GetUserIds() *UserIdentifiers { @@ -2264,7 +2384,7 @@ type BatchDeleteUserBookmarksRequest struct { func (x *BatchDeleteUserBookmarksRequest) Reset() { *x = BatchDeleteUserBookmarksRequest{} if protoimpl.UnsafeEnabled { - mi := &file_ttn_lorawan_v3_user_proto_msgTypes[31] + mi := &file_ttn_lorawan_v3_user_proto_msgTypes[32] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2277,7 +2397,7 @@ func (x *BatchDeleteUserBookmarksRequest) String() string { func (*BatchDeleteUserBookmarksRequest) ProtoMessage() {} func (x *BatchDeleteUserBookmarksRequest) ProtoReflect() protoreflect.Message { - mi := &file_ttn_lorawan_v3_user_proto_msgTypes[31] + mi := &file_ttn_lorawan_v3_user_proto_msgTypes[32] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2290,7 +2410,7 @@ func (x *BatchDeleteUserBookmarksRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use BatchDeleteUserBookmarksRequest.ProtoReflect.Descriptor instead. func (*BatchDeleteUserBookmarksRequest) Descriptor() ([]byte, []int) { - return file_ttn_lorawan_v3_user_proto_rawDescGZIP(), []int{31} + return file_ttn_lorawan_v3_user_proto_rawDescGZIP(), []int{32} } func (x *BatchDeleteUserBookmarksRequest) GetUserIds() *UserIdentifiers { @@ -2325,7 +2445,7 @@ type UserConsolePreferences_DashboardLayouts struct { func (x *UserConsolePreferences_DashboardLayouts) Reset() { *x = UserConsolePreferences_DashboardLayouts{} if protoimpl.UnsafeEnabled { - mi := &file_ttn_lorawan_v3_user_proto_msgTypes[32] + mi := &file_ttn_lorawan_v3_user_proto_msgTypes[33] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2338,7 +2458,7 @@ func (x *UserConsolePreferences_DashboardLayouts) String() string { func (*UserConsolePreferences_DashboardLayouts) ProtoMessage() {} func (x *UserConsolePreferences_DashboardLayouts) ProtoReflect() protoreflect.Message { - mi := &file_ttn_lorawan_v3_user_proto_msgTypes[32] + mi := &file_ttn_lorawan_v3_user_proto_msgTypes[33] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2351,7 +2471,7 @@ func (x *UserConsolePreferences_DashboardLayouts) ProtoReflect() protoreflect.Me // Deprecated: Use UserConsolePreferences_DashboardLayouts.ProtoReflect.Descriptor instead. func (*UserConsolePreferences_DashboardLayouts) Descriptor() ([]byte, []int) { - return file_ttn_lorawan_v3_user_proto_rawDescGZIP(), []int{0, 0} + return file_ttn_lorawan_v3_user_proto_rawDescGZIP(), []int{1, 0} } func (x *UserConsolePreferences_DashboardLayouts) GetApiKey() DashboardLayout { @@ -2428,7 +2548,7 @@ type UserConsolePreferences_SortBy struct { func (x *UserConsolePreferences_SortBy) Reset() { *x = UserConsolePreferences_SortBy{} if protoimpl.UnsafeEnabled { - mi := &file_ttn_lorawan_v3_user_proto_msgTypes[33] + mi := &file_ttn_lorawan_v3_user_proto_msgTypes[34] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2441,7 +2561,7 @@ func (x *UserConsolePreferences_SortBy) String() string { func (*UserConsolePreferences_SortBy) ProtoMessage() {} func (x *UserConsolePreferences_SortBy) ProtoReflect() protoreflect.Message { - mi := &file_ttn_lorawan_v3_user_proto_msgTypes[33] + mi := &file_ttn_lorawan_v3_user_proto_msgTypes[34] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2454,7 +2574,7 @@ func (x *UserConsolePreferences_SortBy) ProtoReflect() protoreflect.Message { // Deprecated: Use UserConsolePreferences_SortBy.ProtoReflect.Descriptor instead. func (*UserConsolePreferences_SortBy) Descriptor() ([]byte, []int) { - return file_ttn_lorawan_v3_user_proto_rawDescGZIP(), []int{0, 1} + return file_ttn_lorawan_v3_user_proto_rawDescGZIP(), []int{1, 1} } func (x *UserConsolePreferences_SortBy) GetApiKey() string { @@ -2506,6 +2626,53 @@ func (x *UserConsolePreferences_SortBy) GetUser() string { return "" } +type UserConsolePreferences_Tutorials struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Seen []Tutorial `protobuf:"varint,1,rep,packed,name=seen,proto3,enum=ttn.lorawan.v3.Tutorial" json:"seen,omitempty"` +} + +func (x *UserConsolePreferences_Tutorials) Reset() { + *x = UserConsolePreferences_Tutorials{} + if protoimpl.UnsafeEnabled { + mi := &file_ttn_lorawan_v3_user_proto_msgTypes[35] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UserConsolePreferences_Tutorials) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UserConsolePreferences_Tutorials) ProtoMessage() {} + +func (x *UserConsolePreferences_Tutorials) ProtoReflect() protoreflect.Message { + mi := &file_ttn_lorawan_v3_user_proto_msgTypes[35] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UserConsolePreferences_Tutorials.ProtoReflect.Descriptor instead. +func (*UserConsolePreferences_Tutorials) Descriptor() ([]byte, []int) { + return file_ttn_lorawan_v3_user_proto_rawDescGZIP(), []int{1, 2} +} + +func (x *UserConsolePreferences_Tutorials) GetSeen() []Tutorial { + if x != nil { + return x.Seen + } + return nil +} + var File_ttn_lorawan_v3_user_proto protoreflect.FileDescriptor var file_ttn_lorawan_v3_user_proto_rawDesc = []byte{ @@ -2526,120 +2693,139 @@ var file_ttn_lorawan_v3_user_proto_rawDesc = []byte{ 0x77, 0x61, 0x6e, 0x2f, 0x76, 0x33, 0x2f, 0x65, 0x6e, 0x75, 0x6d, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x20, 0x74, 0x74, 0x6e, 0x2f, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2f, 0x76, 0x33, 0x2f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x74, 0x74, 0x6e, 0x2f, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, - 0x6e, 0x2f, 0x76, 0x33, 0x2f, 0x70, 0x69, 0x63, 0x74, 0x75, 0x72, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x1a, 0x1b, 0x74, 0x74, 0x6e, 0x2f, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2f, - 0x76, 0x33, 0x2f, 0x72, 0x69, 0x67, 0x68, 0x74, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, - 0x17, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, - 0x74, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xbd, 0x0d, 0x0a, 0x16, 0x55, 0x73, 0x65, - 0x72, 0x43, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x65, 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, - 0x63, 0x65, 0x73, 0x12, 0x41, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x65, 0x5f, 0x74, - 0x68, 0x65, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1c, 0x2e, 0x74, 0x74, 0x6e, - 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x43, 0x6f, 0x6e, 0x73, - 0x6f, 0x6c, 0x65, 0x54, 0x68, 0x65, 0x6d, 0x65, 0x52, 0x0c, 0x63, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, - 0x65, 0x54, 0x68, 0x65, 0x6d, 0x65, 0x12, 0x64, 0x0a, 0x11, 0x64, 0x61, 0x73, 0x68, 0x62, 0x6f, - 0x61, 0x72, 0x64, 0x5f, 0x6c, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x37, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, + 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x29, 0x74, 0x74, 0x6e, 0x2f, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, + 0x6e, 0x2f, 0x76, 0x33, 0x2f, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, + 0x1c, 0x74, 0x74, 0x6e, 0x2f, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2f, 0x76, 0x33, 0x2f, + 0x70, 0x69, 0x63, 0x74, 0x75, 0x72, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1b, 0x74, + 0x74, 0x6e, 0x2f, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2f, 0x76, 0x33, 0x2f, 0x72, 0x69, + 0x67, 0x68, 0x74, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x17, 0x76, 0x61, 0x6c, 0x69, + 0x64, 0x61, 0x74, 0x65, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x22, 0x67, 0x0a, 0x1c, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x4e, 0x6f, 0x74, 0x69, + 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, + 0x63, 0x65, 0x73, 0x12, 0x47, 0x0a, 0x05, 0x74, 0x79, 0x70, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, + 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, + 0x2e, 0x76, 0x33, 0x2e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x54, 0x79, 0x70, 0x65, 0x42, 0x0f, 0xfa, 0x42, 0x0c, 0x92, 0x01, 0x09, 0x18, 0x01, 0x22, 0x05, + 0x82, 0x01, 0x02, 0x10, 0x01, 0x52, 0x05, 0x74, 0x79, 0x70, 0x65, 0x73, 0x22, 0xd9, 0x0e, 0x0a, + 0x16, 0x55, 0x73, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x65, 0x50, 0x72, 0x65, 0x66, + 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x41, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x73, 0x6f, + 0x6c, 0x65, 0x5f, 0x74, 0x68, 0x65, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1c, + 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, + 0x43, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x65, 0x54, 0x68, 0x65, 0x6d, 0x65, 0x52, 0x0c, 0x63, 0x6f, + 0x6e, 0x73, 0x6f, 0x6c, 0x65, 0x54, 0x68, 0x65, 0x6d, 0x65, 0x12, 0x64, 0x0a, 0x11, 0x64, 0x61, + 0x73, 0x68, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x5f, 0x6c, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x73, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x37, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, + 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x73, 0x6f, + 0x6c, 0x65, 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x2e, 0x44, 0x61, + 0x73, 0x68, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x4c, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x73, 0x52, 0x10, + 0x64, 0x61, 0x73, 0x68, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x4c, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x73, + 0x12, 0x46, 0x0a, 0x07, 0x73, 0x6f, 0x72, 0x74, 0x5f, 0x62, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x2d, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x65, 0x50, 0x72, - 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x2e, 0x44, 0x61, 0x73, 0x68, 0x62, 0x6f, - 0x61, 0x72, 0x64, 0x4c, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x73, 0x52, 0x10, 0x64, 0x61, 0x73, 0x68, - 0x62, 0x6f, 0x61, 0x72, 0x64, 0x4c, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x73, 0x12, 0x46, 0x0a, 0x07, - 0x73, 0x6f, 0x72, 0x74, 0x5f, 0x62, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, - 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x55, - 0x73, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x65, 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, - 0x65, 0x6e, 0x63, 0x65, 0x73, 0x2e, 0x53, 0x6f, 0x72, 0x74, 0x42, 0x79, 0x52, 0x06, 0x73, 0x6f, - 0x72, 0x74, 0x42, 0x79, 0x1a, 0x86, 0x04, 0x0a, 0x10, 0x44, 0x61, 0x73, 0x68, 0x62, 0x6f, 0x61, - 0x72, 0x64, 0x4c, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x73, 0x12, 0x38, 0x0a, 0x07, 0x61, 0x70, 0x69, - 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1f, 0x2e, 0x74, 0x74, 0x6e, - 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x44, 0x61, 0x73, 0x68, - 0x62, 0x6f, 0x61, 0x72, 0x64, 0x4c, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x52, 0x06, 0x61, 0x70, 0x69, - 0x4b, 0x65, 0x79, 0x12, 0x41, 0x0a, 0x0b, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1f, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, - 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x44, 0x61, 0x73, 0x68, 0x62, 0x6f, - 0x61, 0x72, 0x64, 0x4c, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x52, 0x0b, 0x61, 0x70, 0x70, 0x6c, 0x69, - 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x43, 0x0a, 0x0c, 0x63, 0x6f, 0x6c, 0x6c, 0x61, 0x62, - 0x6f, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1f, 0x2e, 0x74, - 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x44, 0x61, - 0x73, 0x68, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x4c, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x52, 0x0c, 0x63, - 0x6f, 0x6c, 0x6c, 0x61, 0x62, 0x6f, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x3e, 0x0a, 0x0a, 0x65, - 0x6e, 0x64, 0x5f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, - 0x1f, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, - 0x2e, 0x44, 0x61, 0x73, 0x68, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x4c, 0x61, 0x79, 0x6f, 0x75, 0x74, - 0x52, 0x09, 0x65, 0x6e, 0x64, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x12, 0x39, 0x0a, 0x07, 0x67, - 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1f, 0x2e, 0x74, - 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x44, 0x61, - 0x73, 0x68, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x4c, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x52, 0x07, 0x67, - 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x12, 0x43, 0x0a, 0x0c, 0x6f, 0x72, 0x67, 0x61, 0x6e, 0x69, - 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1f, 0x2e, 0x74, + 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x2e, 0x53, 0x6f, 0x72, 0x74, 0x42, 0x79, + 0x52, 0x06, 0x73, 0x6f, 0x72, 0x74, 0x42, 0x79, 0x12, 0x4e, 0x0a, 0x09, 0x74, 0x75, 0x74, 0x6f, + 0x72, 0x69, 0x61, 0x6c, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x74, 0x74, + 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x55, 0x73, 0x65, + 0x72, 0x43, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x65, 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, + 0x63, 0x65, 0x73, 0x2e, 0x54, 0x75, 0x74, 0x6f, 0x72, 0x69, 0x61, 0x6c, 0x73, 0x52, 0x09, 0x74, + 0x75, 0x74, 0x6f, 0x72, 0x69, 0x61, 0x6c, 0x73, 0x1a, 0x86, 0x04, 0x0a, 0x10, 0x44, 0x61, 0x73, + 0x68, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x4c, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x73, 0x12, 0x38, 0x0a, + 0x07, 0x61, 0x70, 0x69, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1f, + 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, + 0x44, 0x61, 0x73, 0x68, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x4c, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x52, + 0x06, 0x61, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x12, 0x41, 0x0a, 0x0b, 0x61, 0x70, 0x70, 0x6c, 0x69, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1f, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x44, 0x61, - 0x73, 0x68, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x4c, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x52, 0x0c, 0x6f, - 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3b, 0x0a, 0x08, 0x6f, - 0x76, 0x65, 0x72, 0x76, 0x69, 0x65, 0x77, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1f, 0x2e, - 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x44, - 0x61, 0x73, 0x68, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x4c, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x52, 0x08, - 0x6f, 0x76, 0x65, 0x72, 0x76, 0x69, 0x65, 0x77, 0x12, 0x33, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, - 0x18, 0x08, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1f, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, - 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x44, 0x61, 0x73, 0x68, 0x62, 0x6f, 0x61, 0x72, - 0x64, 0x4c, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x1a, 0x9e, 0x07, - 0x0a, 0x06, 0x53, 0x6f, 0x72, 0x74, 0x42, 0x79, 0x12, 0x78, 0x0a, 0x07, 0x61, 0x70, 0x69, 0x5f, - 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x5f, 0xfa, 0x42, 0x5c, 0x72, 0x5a, - 0x52, 0x00, 0x52, 0x0a, 0x61, 0x70, 0x69, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x52, 0x0b, - 0x2d, 0x61, 0x70, 0x69, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x52, 0x04, 0x6e, 0x61, 0x6d, - 0x65, 0x52, 0x05, 0x2d, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, - 0x64, 0x5f, 0x61, 0x74, 0x52, 0x0b, 0x2d, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, - 0x74, 0x52, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x5f, 0x61, 0x74, 0x52, 0x0b, 0x2d, - 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x5f, 0x61, 0x74, 0x52, 0x06, 0x61, 0x70, 0x69, 0x4b, - 0x65, 0x79, 0x12, 0x70, 0x0a, 0x0b, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x4e, 0xfa, 0x42, 0x4b, 0x72, 0x49, 0x52, 0x00, - 0x52, 0x0e, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, - 0x52, 0x0f, 0x2d, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, - 0x64, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x05, 0x2d, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x0a, - 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x52, 0x0b, 0x2d, 0x63, 0x72, 0x65, - 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x52, 0x0b, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x45, 0x0a, 0x0c, 0x63, 0x6f, 0x6c, 0x6c, 0x61, 0x62, 0x6f, 0x72, - 0x61, 0x74, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x21, 0xfa, 0x42, 0x1e, 0x72, - 0x1c, 0x52, 0x00, 0x52, 0x02, 0x69, 0x64, 0x52, 0x03, 0x2d, 0x69, 0x64, 0x52, 0x06, 0x72, 0x69, - 0x67, 0x68, 0x74, 0x73, 0x52, 0x07, 0x2d, 0x72, 0x69, 0x67, 0x68, 0x74, 0x73, 0x52, 0x0c, 0x63, - 0x6f, 0x6c, 0x6c, 0x61, 0x62, 0x6f, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0xc6, 0x01, 0x0a, 0x0a, - 0x65, 0x6e, 0x64, 0x5f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, - 0x42, 0xa6, 0x01, 0xfa, 0x42, 0xa2, 0x01, 0x72, 0x9f, 0x01, 0x52, 0x00, 0x52, 0x09, 0x64, 0x65, - 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x52, 0x0a, 0x2d, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, - 0x5f, 0x69, 0x64, 0x52, 0x08, 0x6a, 0x6f, 0x69, 0x6e, 0x5f, 0x65, 0x75, 0x69, 0x52, 0x09, 0x2d, - 0x6a, 0x6f, 0x69, 0x6e, 0x5f, 0x65, 0x75, 0x69, 0x52, 0x07, 0x64, 0x65, 0x76, 0x5f, 0x65, 0x75, - 0x69, 0x52, 0x08, 0x2d, 0x64, 0x65, 0x76, 0x5f, 0x65, 0x75, 0x69, 0x52, 0x04, 0x6e, 0x61, 0x6d, - 0x65, 0x52, 0x05, 0x2d, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0c, 0x2d, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x52, - 0x0b, 0x2d, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x52, 0x0c, 0x6c, 0x61, - 0x73, 0x74, 0x5f, 0x73, 0x65, 0x65, 0x6e, 0x5f, 0x61, 0x74, 0x52, 0x0d, 0x2d, 0x6c, 0x61, 0x73, - 0x74, 0x5f, 0x73, 0x65, 0x65, 0x6e, 0x5f, 0x61, 0x74, 0x52, 0x09, 0x65, 0x6e, 0x64, 0x44, 0x65, - 0x76, 0x69, 0x63, 0x65, 0x12, 0x7b, 0x0a, 0x07, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x09, 0x42, 0x61, 0xfa, 0x42, 0x5e, 0x72, 0x5c, 0x52, 0x00, 0x52, 0x0a, - 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x5f, 0x69, 0x64, 0x52, 0x0b, 0x2d, 0x67, 0x61, 0x74, - 0x65, 0x77, 0x61, 0x79, 0x5f, 0x69, 0x64, 0x52, 0x0b, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, - 0x5f, 0x65, 0x75, 0x69, 0x52, 0x0c, 0x2d, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x5f, 0x65, - 0x75, 0x69, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x05, 0x2d, 0x6e, 0x61, 0x6d, 0x65, 0x52, - 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x52, 0x0b, 0x2d, 0x63, 0x72, - 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x52, 0x07, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, - 0x79, 0x12, 0x74, 0x0a, 0x0c, 0x6f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x42, 0x50, 0xfa, 0x42, 0x4d, 0x72, 0x4b, 0x52, 0x00, - 0x52, 0x0f, 0x6f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, - 0x64, 0x52, 0x10, 0x2d, 0x6f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x5f, 0x69, 0x64, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x05, 0x2d, 0x6e, 0x61, 0x6d, 0x65, - 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x52, 0x0b, 0x2d, 0x63, - 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x52, 0x0c, 0x6f, 0x72, 0x67, 0x61, 0x6e, - 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0xa4, 0x01, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, - 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x42, 0x8f, 0x01, 0xfa, 0x42, 0x8b, 0x01, 0x72, 0x88, 0x01, - 0x52, 0x00, 0x52, 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x52, 0x08, 0x2d, 0x75, 0x73, - 0x65, 0x72, 0x5f, 0x69, 0x64, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x05, 0x2d, 0x6e, 0x61, - 0x6d, 0x65, 0x52, 0x15, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x5f, 0x65, 0x6d, 0x61, 0x69, - 0x6c, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x16, 0x2d, 0x70, 0x72, 0x69, 0x6d, - 0x61, 0x72, 0x79, 0x5f, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, - 0x73, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x52, 0x06, 0x2d, 0x73, 0x74, 0x61, 0x74, 0x65, - 0x52, 0x05, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x52, 0x06, 0x2d, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x52, - 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x52, 0x0b, 0x2d, 0x63, 0x72, - 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x3a, 0x08, - 0xf2, 0xaa, 0x19, 0x04, 0x08, 0x01, 0x10, 0x01, 0x22, 0xd4, 0x0c, 0x0a, 0x04, 0x55, 0x73, 0x65, + 0x73, 0x68, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x4c, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x52, 0x0b, 0x61, + 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x43, 0x0a, 0x0c, 0x63, 0x6f, + 0x6c, 0x6c, 0x61, 0x62, 0x6f, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, + 0x32, 0x1f, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, + 0x33, 0x2e, 0x44, 0x61, 0x73, 0x68, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x4c, 0x61, 0x79, 0x6f, 0x75, + 0x74, 0x52, 0x0c, 0x63, 0x6f, 0x6c, 0x6c, 0x61, 0x62, 0x6f, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, + 0x3e, 0x0a, 0x0a, 0x65, 0x6e, 0x64, 0x5f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x0e, 0x32, 0x1f, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, + 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x44, 0x61, 0x73, 0x68, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x4c, 0x61, + 0x79, 0x6f, 0x75, 0x74, 0x52, 0x09, 0x65, 0x6e, 0x64, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x12, + 0x39, 0x0a, 0x07, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, + 0x32, 0x1f, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, + 0x33, 0x2e, 0x44, 0x61, 0x73, 0x68, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x4c, 0x61, 0x79, 0x6f, 0x75, + 0x74, 0x52, 0x07, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x12, 0x43, 0x0a, 0x0c, 0x6f, 0x72, + 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, + 0x32, 0x1f, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, + 0x33, 0x2e, 0x44, 0x61, 0x73, 0x68, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x4c, 0x61, 0x79, 0x6f, 0x75, + 0x74, 0x52, 0x0c, 0x6f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x3b, 0x0a, 0x08, 0x6f, 0x76, 0x65, 0x72, 0x76, 0x69, 0x65, 0x77, 0x18, 0x07, 0x20, 0x01, 0x28, + 0x0e, 0x32, 0x1f, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, + 0x76, 0x33, 0x2e, 0x44, 0x61, 0x73, 0x68, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x4c, 0x61, 0x79, 0x6f, + 0x75, 0x74, 0x52, 0x08, 0x6f, 0x76, 0x65, 0x72, 0x76, 0x69, 0x65, 0x77, 0x12, 0x33, 0x0a, 0x04, + 0x75, 0x73, 0x65, 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1f, 0x2e, 0x74, 0x74, 0x6e, + 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x44, 0x61, 0x73, 0x68, + 0x62, 0x6f, 0x61, 0x72, 0x64, 0x4c, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x52, 0x04, 0x75, 0x73, 0x65, + 0x72, 0x1a, 0x9e, 0x07, 0x0a, 0x06, 0x53, 0x6f, 0x72, 0x74, 0x42, 0x79, 0x12, 0x78, 0x0a, 0x07, + 0x61, 0x70, 0x69, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x5f, 0xfa, + 0x42, 0x5c, 0x72, 0x5a, 0x52, 0x00, 0x52, 0x0a, 0x61, 0x70, 0x69, 0x5f, 0x6b, 0x65, 0x79, 0x5f, + 0x69, 0x64, 0x52, 0x0b, 0x2d, 0x61, 0x70, 0x69, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x52, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x05, 0x2d, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x0a, 0x63, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x52, 0x0b, 0x2d, 0x63, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x64, 0x5f, 0x61, 0x74, 0x52, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x5f, 0x61, + 0x74, 0x52, 0x0b, 0x2d, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x5f, 0x61, 0x74, 0x52, 0x06, + 0x61, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x12, 0x70, 0x0a, 0x0b, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x4e, 0xfa, 0x42, 0x4b, + 0x72, 0x49, 0x52, 0x00, 0x52, 0x0e, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x5f, 0x69, 0x64, 0x52, 0x0f, 0x2d, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x05, 0x2d, 0x6e, 0x61, + 0x6d, 0x65, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x52, 0x0b, + 0x2d, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x52, 0x0b, 0x61, 0x70, 0x70, + 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x45, 0x0a, 0x0c, 0x63, 0x6f, 0x6c, 0x6c, + 0x61, 0x62, 0x6f, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x21, + 0xfa, 0x42, 0x1e, 0x72, 0x1c, 0x52, 0x00, 0x52, 0x02, 0x69, 0x64, 0x52, 0x03, 0x2d, 0x69, 0x64, + 0x52, 0x06, 0x72, 0x69, 0x67, 0x68, 0x74, 0x73, 0x52, 0x07, 0x2d, 0x72, 0x69, 0x67, 0x68, 0x74, + 0x73, 0x52, 0x0c, 0x63, 0x6f, 0x6c, 0x6c, 0x61, 0x62, 0x6f, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, + 0xc6, 0x01, 0x0a, 0x0a, 0x65, 0x6e, 0x64, 0x5f, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x09, 0x42, 0xa6, 0x01, 0xfa, 0x42, 0xa2, 0x01, 0x72, 0x9f, 0x01, 0x52, 0x00, + 0x52, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x52, 0x0a, 0x2d, 0x64, 0x65, + 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x52, 0x08, 0x6a, 0x6f, 0x69, 0x6e, 0x5f, 0x65, 0x75, + 0x69, 0x52, 0x09, 0x2d, 0x6a, 0x6f, 0x69, 0x6e, 0x5f, 0x65, 0x75, 0x69, 0x52, 0x07, 0x64, 0x65, + 0x76, 0x5f, 0x65, 0x75, 0x69, 0x52, 0x08, 0x2d, 0x64, 0x65, 0x76, 0x5f, 0x65, 0x75, 0x69, 0x52, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x05, 0x2d, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x0b, 0x64, 0x65, + 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0c, 0x2d, 0x64, 0x65, 0x73, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, + 0x5f, 0x61, 0x74, 0x52, 0x0b, 0x2d, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, + 0x52, 0x0c, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x65, 0x6e, 0x5f, 0x61, 0x74, 0x52, 0x0d, + 0x2d, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x65, 0x6e, 0x5f, 0x61, 0x74, 0x52, 0x09, 0x65, + 0x6e, 0x64, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x12, 0x7b, 0x0a, 0x07, 0x67, 0x61, 0x74, 0x65, + 0x77, 0x61, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x42, 0x61, 0xfa, 0x42, 0x5e, 0x72, 0x5c, + 0x52, 0x00, 0x52, 0x0a, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x5f, 0x69, 0x64, 0x52, 0x0b, + 0x2d, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x5f, 0x69, 0x64, 0x52, 0x0b, 0x67, 0x61, 0x74, + 0x65, 0x77, 0x61, 0x79, 0x5f, 0x65, 0x75, 0x69, 0x52, 0x0c, 0x2d, 0x67, 0x61, 0x74, 0x65, 0x77, + 0x61, 0x79, 0x5f, 0x65, 0x75, 0x69, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x05, 0x2d, 0x6e, + 0x61, 0x6d, 0x65, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x52, + 0x0b, 0x2d, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x52, 0x07, 0x67, 0x61, + 0x74, 0x65, 0x77, 0x61, 0x79, 0x12, 0x74, 0x0a, 0x0c, 0x6f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x42, 0x50, 0xfa, 0x42, 0x4d, + 0x72, 0x4b, 0x52, 0x00, 0x52, 0x0f, 0x6f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x52, 0x10, 0x2d, 0x6f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x05, 0x2d, + 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, + 0x52, 0x0b, 0x2d, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x52, 0x0c, 0x6f, + 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0xa4, 0x01, 0x0a, 0x04, + 0x75, 0x73, 0x65, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x42, 0x8f, 0x01, 0xfa, 0x42, 0x8b, + 0x01, 0x72, 0x88, 0x01, 0x52, 0x00, 0x52, 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x52, + 0x08, 0x2d, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x52, + 0x05, 0x2d, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x15, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x5f, + 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x16, 0x2d, + 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x5f, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x5f, 0x61, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x52, 0x06, 0x2d, 0x73, + 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x52, 0x06, 0x2d, 0x61, 0x64, + 0x6d, 0x69, 0x6e, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x52, + 0x0b, 0x2d, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x52, 0x04, 0x75, 0x73, + 0x65, 0x72, 0x1a, 0x4a, 0x0a, 0x09, 0x54, 0x75, 0x74, 0x6f, 0x72, 0x69, 0x61, 0x6c, 0x73, 0x12, + 0x3d, 0x0a, 0x04, 0x73, 0x65, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x18, 0x2e, + 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x54, + 0x75, 0x74, 0x6f, 0x72, 0x69, 0x61, 0x6c, 0x42, 0x0f, 0xfa, 0x42, 0x0c, 0x92, 0x01, 0x09, 0x18, + 0x01, 0x22, 0x05, 0x82, 0x01, 0x02, 0x10, 0x01, 0x52, 0x04, 0x73, 0x65, 0x65, 0x6e, 0x3a, 0x08, + 0xf2, 0xaa, 0x19, 0x04, 0x08, 0x01, 0x10, 0x01, 0x22, 0x8a, 0x0e, 0x0a, 0x04, 0x55, 0x73, 0x65, 0x72, 0x12, 0x43, 0x0a, 0x03, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x42, @@ -2730,363 +2916,379 @@ var file_ttn_lorawan_v3_user_proto_rawDesc = []byte{ 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x65, 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x12, 0x63, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x65, 0x50, - 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 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, 0x01, 0x4a, 0x04, 0x08, 0x15, 0x10, 0x16, 0x4a, 0x04, 0x08, 0x16, 0x10, 0x17, 0x4a, - 0x04, 0x08, 0x17, 0x10, 0x18, 0x4a, 0x04, 0x08, 0x18, 0x10, 0x19, 0x52, 0x11, 0x61, 0x70, 0x70, - 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x52, 0x0c, - 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x52, 0x0d, 0x67, 0x61, - 0x74, 0x65, 0x77, 0x61, 0x79, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x52, 0x12, 0x6f, 0x72, 0x67, - 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x22, - 0x33, 0x0a, 0x05, 0x55, 0x73, 0x65, 0x72, 0x73, 0x12, 0x2a, 0x0a, 0x05, 0x75, 0x73, 0x65, 0x72, - 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, - 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x05, 0x75, - 0x73, 0x65, 0x72, 0x73, 0x22, 0x91, 0x01, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x44, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x5f, - 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x74, 0x74, 0x6e, 0x2e, - 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x49, - 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, - 0x01, 0x02, 0x10, 0x01, 0x52, 0x07, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x73, 0x12, 0x39, 0x0a, - 0x0a, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4d, 0x61, 0x73, 0x6b, 0x52, 0x09, 0x66, - 0x69, 0x65, 0x6c, 0x64, 0x4d, 0x61, 0x73, 0x6b, 0x22, 0xce, 0x02, 0x0a, 0x10, 0x4c, 0x69, 0x73, - 0x74, 0x55, 0x73, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x39, 0x0a, - 0x0a, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4d, 0x61, 0x73, 0x6b, 0x52, 0x09, 0x66, - 0x69, 0x65, 0x6c, 0x64, 0x4d, 0x61, 0x73, 0x6b, 0x12, 0xa6, 0x01, 0x0a, 0x05, 0x6f, 0x72, 0x64, - 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x8f, 0x01, 0xfa, 0x42, 0x8b, 0x01, 0x72, - 0x88, 0x01, 0x52, 0x00, 0x52, 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x52, 0x08, 0x2d, - 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x05, 0x2d, - 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x15, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x5f, 0x65, 0x6d, - 0x61, 0x69, 0x6c, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x16, 0x2d, 0x70, 0x72, - 0x69, 0x6d, 0x61, 0x72, 0x79, 0x5f, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x5f, 0x61, 0x64, 0x64, 0x72, - 0x65, 0x73, 0x73, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x52, 0x06, 0x2d, 0x73, 0x74, 0x61, - 0x74, 0x65, 0x52, 0x05, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x52, 0x06, 0x2d, 0x61, 0x64, 0x6d, 0x69, - 0x6e, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x52, 0x0b, 0x2d, - 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x52, 0x05, 0x6f, 0x72, 0x64, 0x65, - 0x72, 0x12, 0x1e, 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, - 0x42, 0x08, 0xfa, 0x42, 0x05, 0x2a, 0x03, 0x18, 0xe8, 0x07, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, - 0x74, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x67, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, - 0x04, 0x70, 0x61, 0x67, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x3a, - 0x08, 0xf2, 0xaa, 0x19, 0x04, 0x08, 0x01, 0x10, 0x01, 0x22, 0x72, 0x0a, 0x11, 0x43, 0x72, 0x65, - 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x32, - 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x74, - 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x55, 0x73, - 0x65, 0x72, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x04, 0x75, 0x73, - 0x65, 0x72, 0x12, 0x29, 0x0a, 0x10, 0x69, 0x6e, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x69, 0x6e, - 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x82, 0x01, - 0x0a, 0x11, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x32, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x14, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, - 0x76, 0x33, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, - 0x01, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x12, 0x39, 0x0a, 0x0a, 0x66, 0x69, 0x65, 0x6c, 0x64, - 0x5f, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, - 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, - 0x65, 0x6c, 0x64, 0x4d, 0x61, 0x73, 0x6b, 0x52, 0x09, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x4d, 0x61, - 0x73, 0x6b, 0x22, 0x66, 0x0a, 0x1e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x65, 0x6d, 0x70, - 0x6f, 0x72, 0x61, 0x72, 0x79, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x44, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, - 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x49, 0x64, 0x65, 0x6e, - 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, - 0x01, 0x52, 0x07, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x73, 0x22, 0xc5, 0x01, 0x0a, 0x19, 0x55, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, - 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x44, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, - 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x74, 0x74, 0x6e, - 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x55, 0x73, 0x65, 0x72, - 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x42, 0x08, 0xfa, 0x42, 0x05, - 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x07, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x73, 0x12, 0x1a, - 0x0a, 0x03, 0x6e, 0x65, 0x77, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xfa, 0x42, 0x05, - 0x72, 0x03, 0x18, 0xe8, 0x07, 0x52, 0x03, 0x6e, 0x65, 0x77, 0x12, 0x1a, 0x0a, 0x03, 0x6f, 0x6c, - 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x72, 0x03, 0x18, 0xe8, - 0x07, 0x52, 0x03, 0x6f, 0x6c, 0x64, 0x12, 0x2a, 0x0a, 0x11, 0x72, 0x65, 0x76, 0x6f, 0x6b, 0x65, - 0x5f, 0x61, 0x6c, 0x6c, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x0f, 0x72, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x41, 0x6c, 0x6c, 0x41, 0x63, 0x63, 0x65, - 0x73, 0x73, 0x22, 0x93, 0x02, 0x0a, 0x16, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x73, 0x65, 0x72, 0x41, - 0x50, 0x49, 0x4b, 0x65, 0x79, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x44, 0x0a, + 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x72, 0x0a, 0x1e, 0x65, 0x6d, + 0x61, 0x69, 0x6c, 0x5f, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x5f, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x18, 0x1a, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, + 0x2e, 0x76, 0x33, 0x2e, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, + 0x52, 0x1c, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x40, + 0x0a, 0x10, 0x75, 0x6e, 0x69, 0x76, 0x65, 0x72, 0x73, 0x61, 0x6c, 0x5f, 0x72, 0x69, 0x67, 0x68, + 0x74, 0x73, 0x18, 0x1b, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, + 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x52, 0x69, 0x67, 0x68, 0x74, 0x52, + 0x0f, 0x75, 0x6e, 0x69, 0x76, 0x65, 0x72, 0x73, 0x61, 0x6c, 0x52, 0x69, 0x67, 0x68, 0x74, 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, 0x01, 0x4a, 0x04, 0x08, 0x15, 0x10, 0x16, 0x4a, + 0x04, 0x08, 0x16, 0x10, 0x17, 0x4a, 0x04, 0x08, 0x17, 0x10, 0x18, 0x4a, 0x04, 0x08, 0x18, 0x10, + 0x19, 0x52, 0x11, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6c, + 0x69, 0x6d, 0x69, 0x74, 0x52, 0x0c, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x6c, 0x69, 0x6d, + 0x69, 0x74, 0x52, 0x0d, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x5f, 0x6c, 0x69, 0x6d, 0x69, + 0x74, 0x52, 0x12, 0x6f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, + 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x22, 0x33, 0x0a, 0x05, 0x55, 0x73, 0x65, 0x72, 0x73, 0x12, 0x2a, + 0x0a, 0x05, 0x75, 0x73, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, + 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x55, + 0x73, 0x65, 0x72, 0x52, 0x05, 0x75, 0x73, 0x65, 0x72, 0x73, 0x22, 0x91, 0x01, 0x0a, 0x0e, 0x47, + 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x44, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x07, 0x75, 0x73, 0x65, 0x72, - 0x49, 0x64, 0x73, 0x12, 0x75, 0x0a, 0x05, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x09, 0x42, 0x5f, 0xfa, 0x42, 0x5c, 0x72, 0x5a, 0x52, 0x00, 0x52, 0x0a, 0x61, 0x70, 0x69, - 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x52, 0x0b, 0x2d, 0x61, 0x70, 0x69, 0x5f, 0x6b, 0x65, - 0x79, 0x5f, 0x69, 0x64, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x05, 0x2d, 0x6e, 0x61, 0x6d, - 0x65, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x52, 0x0b, 0x2d, - 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x52, 0x0a, 0x65, 0x78, 0x70, 0x69, - 0x72, 0x65, 0x73, 0x5f, 0x61, 0x74, 0x52, 0x0b, 0x2d, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, - 0x5f, 0x61, 0x74, 0x52, 0x05, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x1e, 0x0a, 0x05, 0x6c, 0x69, - 0x6d, 0x69, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x2a, 0x03, - 0x18, 0xe8, 0x07, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, - 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x70, 0x61, 0x67, 0x65, 0x3a, 0x08, - 0xf2, 0xaa, 0x19, 0x04, 0x08, 0x00, 0x10, 0x01, 0x22, 0x73, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x55, - 0x73, 0x65, 0x72, 0x41, 0x50, 0x49, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x44, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, - 0x2e, 0x76, 0x33, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, - 0x65, 0x72, 0x73, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x07, 0x75, - 0x73, 0x65, 0x72, 0x49, 0x64, 0x73, 0x12, 0x15, 0x0a, 0x06, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6b, 0x65, 0x79, 0x49, 0x64, 0x22, 0x83, 0x02, - 0x0a, 0x17, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x41, 0x50, 0x49, 0x4b, - 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x44, 0x0a, 0x08, 0x75, 0x73, 0x65, - 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x74, 0x74, - 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x55, 0x73, 0x65, - 0x72, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x42, 0x08, 0xfa, 0x42, - 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x07, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x73, 0x12, - 0x1b, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, - 0x42, 0x04, 0x72, 0x02, 0x18, 0x32, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x40, 0x0a, 0x06, - 0x72, 0x69, 0x67, 0x68, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x74, - 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x52, 0x69, - 0x67, 0x68, 0x74, 0x42, 0x11, 0xfa, 0x42, 0x0e, 0x92, 0x01, 0x0b, 0x08, 0x01, 0x18, 0x01, 0x22, - 0x05, 0x82, 0x01, 0x02, 0x10, 0x01, 0x52, 0x06, 0x72, 0x69, 0x67, 0x68, 0x74, 0x73, 0x12, 0x43, - 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x5f, 0x61, 0x74, 0x18, 0x04, 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, 0x40, 0x01, 0x52, 0x09, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, - 0x73, 0x41, 0x74, 0x22, 0xd5, 0x01, 0x0a, 0x17, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x55, 0x73, - 0x65, 0x72, 0x41, 0x50, 0x49, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x44, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1f, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, - 0x76, 0x33, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, - 0x72, 0x73, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x07, 0x75, 0x73, - 0x65, 0x72, 0x49, 0x64, 0x73, 0x12, 0x39, 0x0a, 0x07, 0x61, 0x70, 0x69, 0x5f, 0x6b, 0x65, 0x79, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, - 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x41, 0x50, 0x49, 0x4b, 0x65, 0x79, 0x42, 0x08, - 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x06, 0x61, 0x70, 0x69, 0x4b, 0x65, 0x79, - 0x12, 0x39, 0x0a, 0x0a, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x6d, 0x61, 0x73, 0x6b, 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, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4d, 0x61, 0x73, 0x6b, - 0x52, 0x09, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x4d, 0x61, 0x73, 0x6b, 0x22, 0x76, 0x0a, 0x17, 0x44, - 0x65, 0x6c, 0x65, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x41, 0x50, 0x49, 0x4b, 0x65, 0x79, 0x52, + 0x49, 0x64, 0x73, 0x12, 0x39, 0x0a, 0x0a, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x6d, 0x61, 0x73, + 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4d, + 0x61, 0x73, 0x6b, 0x52, 0x09, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x4d, 0x61, 0x73, 0x6b, 0x22, 0xce, + 0x02, 0x0a, 0x10, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x73, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x6d, 0x61, 0x73, + 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4d, + 0x61, 0x73, 0x6b, 0x52, 0x09, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x4d, 0x61, 0x73, 0x6b, 0x12, 0xa6, + 0x01, 0x0a, 0x05, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x8f, + 0x01, 0xfa, 0x42, 0x8b, 0x01, 0x72, 0x88, 0x01, 0x52, 0x00, 0x52, 0x07, 0x75, 0x73, 0x65, 0x72, + 0x5f, 0x69, 0x64, 0x52, 0x08, 0x2d, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x52, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x52, 0x05, 0x2d, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x15, 0x70, 0x72, 0x69, 0x6d, + 0x61, 0x72, 0x79, 0x5f, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, + 0x73, 0x52, 0x16, 0x2d, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x5f, 0x65, 0x6d, 0x61, 0x69, + 0x6c, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, + 0x52, 0x06, 0x2d, 0x73, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x52, + 0x06, 0x2d, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, + 0x5f, 0x61, 0x74, 0x52, 0x0b, 0x2d, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, + 0x52, 0x05, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x1e, 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x2a, 0x03, 0x18, 0xe8, 0x07, + 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x67, 0x65, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x70, 0x61, 0x67, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x64, + 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x64, 0x65, + 0x6c, 0x65, 0x74, 0x65, 0x64, 0x3a, 0x08, 0xf2, 0xaa, 0x19, 0x04, 0x08, 0x01, 0x10, 0x01, 0x22, + 0x72, 0x0a, 0x11, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x32, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, + 0x2e, 0x76, 0x33, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, + 0x10, 0x01, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x12, 0x29, 0x0a, 0x10, 0x69, 0x6e, 0x76, 0x69, + 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0f, 0x69, 0x6e, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x6f, + 0x6b, 0x65, 0x6e, 0x22, 0x82, 0x01, 0x0a, 0x11, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x55, 0x73, + 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x32, 0x0a, 0x04, 0x75, 0x73, 0x65, + 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, + 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x42, 0x08, 0xfa, + 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x12, 0x39, 0x0a, + 0x0a, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4d, 0x61, 0x73, 0x6b, 0x52, 0x09, 0x66, + 0x69, 0x65, 0x6c, 0x64, 0x4d, 0x61, 0x73, 0x6b, 0x22, 0x66, 0x0a, 0x1e, 0x43, 0x72, 0x65, 0x61, + 0x74, 0x65, 0x54, 0x65, 0x6d, 0x70, 0x6f, 0x72, 0x61, 0x72, 0x79, 0x50, 0x61, 0x73, 0x73, 0x77, + 0x6f, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x44, 0x0a, 0x08, 0x75, 0x73, + 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x74, + 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x55, 0x73, + 0x65, 0x72, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x42, 0x08, 0xfa, + 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x07, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x73, + 0x22, 0xc5, 0x01, 0x0a, 0x19, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x50, + 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x44, + 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1f, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, + 0x33, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, + 0x73, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x07, 0x75, 0x73, 0x65, + 0x72, 0x49, 0x64, 0x73, 0x12, 0x1a, 0x0a, 0x03, 0x6e, 0x65, 0x77, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x72, 0x03, 0x18, 0xe8, 0x07, 0x52, 0x03, 0x6e, 0x65, 0x77, + 0x12, 0x1a, 0x0a, 0x03, 0x6f, 0x6c, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xfa, + 0x42, 0x05, 0x72, 0x03, 0x18, 0xe8, 0x07, 0x52, 0x03, 0x6f, 0x6c, 0x64, 0x12, 0x2a, 0x0a, 0x11, + 0x72, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x5f, 0x61, 0x6c, 0x6c, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, + 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x72, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x41, + 0x6c, 0x6c, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x22, 0x93, 0x02, 0x0a, 0x16, 0x4c, 0x69, 0x73, + 0x74, 0x55, 0x73, 0x65, 0x72, 0x41, 0x50, 0x49, 0x4b, 0x65, 0x79, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x44, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, + 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x49, 0x64, 0x65, 0x6e, 0x74, + 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, + 0x52, 0x07, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x73, 0x12, 0x75, 0x0a, 0x05, 0x6f, 0x72, 0x64, + 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x42, 0x5f, 0xfa, 0x42, 0x5c, 0x72, 0x5a, 0x52, + 0x00, 0x52, 0x0a, 0x61, 0x70, 0x69, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x52, 0x0b, 0x2d, + 0x61, 0x70, 0x69, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x52, 0x05, 0x2d, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, + 0x5f, 0x61, 0x74, 0x52, 0x0b, 0x2d, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, + 0x52, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x5f, 0x61, 0x74, 0x52, 0x0b, 0x2d, 0x65, + 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x5f, 0x61, 0x74, 0x52, 0x05, 0x6f, 0x72, 0x64, 0x65, 0x72, + 0x12, 0x1e, 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x42, + 0x08, 0xfa, 0x42, 0x05, 0x2a, 0x03, 0x18, 0xe8, 0x07, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, + 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, + 0x70, 0x61, 0x67, 0x65, 0x3a, 0x08, 0xf2, 0xaa, 0x19, 0x04, 0x08, 0x00, 0x10, 0x01, 0x22, 0x73, + 0x0a, 0x14, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x41, 0x50, 0x49, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x44, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x07, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x73, 0x12, 0x15, 0x0a, 0x06, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6b, 0x65, - 0x79, 0x49, 0x64, 0x22, 0xf1, 0x02, 0x0a, 0x0a, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x12, 0x1d, 0x0a, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x60, 0x01, 0x52, 0x05, 0x65, 0x6d, 0x61, 0x69, - 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x39, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, - 0x65, 0x73, 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, 0x09, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, - 0x41, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, - 0x18, 0x04, 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, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x39, 0x0a, - 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x05, 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, 0x09, 0x75, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x3b, 0x0a, 0x0b, 0x61, 0x63, 0x63, 0x65, - 0x70, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x06, 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, 0x61, 0x63, 0x63, 0x65, 0x70, - 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x40, 0x0a, 0x0b, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, - 0x64, 0x5f, 0x62, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x74, 0x74, 0x6e, - 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x55, 0x73, 0x65, 0x72, - 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x52, 0x0a, 0x61, 0x63, 0x63, - 0x65, 0x70, 0x74, 0x65, 0x64, 0x42, 0x79, 0x22, 0x4c, 0x0a, 0x16, 0x4c, 0x69, 0x73, 0x74, 0x49, - 0x6e, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x1e, 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, - 0x42, 0x08, 0xfa, 0x42, 0x05, 0x2a, 0x03, 0x18, 0xe8, 0x07, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, - 0x74, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, - 0x04, 0x70, 0x61, 0x67, 0x65, 0x22, 0x4b, 0x0a, 0x0b, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x3c, 0x0a, 0x0b, 0x69, 0x6e, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x74, 0x74, 0x6e, 0x2e, - 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x49, 0x6e, 0x76, 0x69, 0x74, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x69, 0x6e, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x22, 0x36, 0x0a, 0x15, 0x53, 0x65, 0x6e, 0x64, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x05, 0x65, - 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, - 0x02, 0x60, 0x01, 0x52, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x22, 0x38, 0x0a, 0x17, 0x44, 0x65, - 0x6c, 0x65, 0x74, 0x65, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x60, 0x01, 0x52, 0x05, 0x65, - 0x6d, 0x61, 0x69, 0x6c, 0x22, 0x86, 0x01, 0x0a, 0x16, 0x55, 0x73, 0x65, 0x72, 0x53, 0x65, 0x73, - 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x12, + 0x79, 0x49, 0x64, 0x22, 0x83, 0x02, 0x0a, 0x17, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x73, + 0x65, 0x72, 0x41, 0x50, 0x49, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x44, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x07, 0x75, 0x73, - 0x65, 0x72, 0x49, 0x64, 0x73, 0x12, 0x26, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, - 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, - 0x18, 0x40, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x22, 0xd3, 0x02, - 0x0a, 0x0b, 0x55, 0x73, 0x65, 0x72, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x44, 0x0a, - 0x08, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1f, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, - 0x2e, 0x55, 0x73, 0x65, 0x72, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, - 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x07, 0x75, 0x73, 0x65, 0x72, - 0x49, 0x64, 0x73, 0x12, 0x26, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, - 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x18, 0x40, - 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x39, 0x0a, 0x0a, 0x63, - 0x72, 0x65, 0x61, 0x74, 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, 0x09, 0x63, 0x72, 0x65, - 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, - 0x64, 0x5f, 0x61, 0x74, 0x18, 0x04, 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, 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, - 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x5f, 0x61, 0x74, 0x18, - 0x05, 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, 0x09, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x41, 0x74, 0x12, 0x25, 0x0a, 0x0e, - 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x06, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x63, - 0x72, 0x65, 0x74, 0x22, 0x47, 0x0a, 0x0c, 0x55, 0x73, 0x65, 0x72, 0x53, 0x65, 0x73, 0x73, 0x69, - 0x6f, 0x6e, 0x73, 0x12, 0x37, 0x0a, 0x08, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, - 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, - 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x53, 0x65, 0x73, 0x73, 0x69, - 0x6f, 0x6e, 0x52, 0x08, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0xcb, 0x01, 0x0a, - 0x17, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x73, 0x65, 0x72, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x44, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, - 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x74, 0x74, 0x6e, - 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x55, 0x73, 0x65, 0x72, - 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x42, 0x08, 0xfa, 0x42, 0x05, - 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x07, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x73, 0x12, 0x36, - 0x0a, 0x05, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x20, 0xfa, - 0x42, 0x1d, 0x72, 0x1b, 0x52, 0x00, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, - 0x61, 0x74, 0x52, 0x0b, 0x2d, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x52, - 0x05, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x1e, 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x0d, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x2a, 0x03, 0x18, 0xe8, 0x07, 0x52, - 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x67, 0x65, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x70, 0x61, 0x67, 0x65, 0x22, 0xad, 0x02, 0x0a, 0x0a, 0x4c, - 0x6f, 0x67, 0x69, 0x6e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x44, 0x0a, 0x08, 0x75, 0x73, 0x65, - 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x74, 0x74, - 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x55, 0x73, 0x65, - 0x72, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x42, 0x08, 0xfa, 0x42, - 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x07, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x73, 0x12, - 0x39, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x02, 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, - 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x75, 0x70, - 0x64, 0x61, 0x74, 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, 0x09, 0x75, 0x70, 0x64, 0x61, - 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, - 0x5f, 0x61, 0x74, 0x18, 0x04, 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, 0x09, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x41, 0x74, - 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x64, 0x18, 0x06, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x75, 0x73, 0x65, 0x64, 0x22, 0x7e, 0x0a, 0x17, 0x43, 0x72, - 0x65, 0x61, 0x74, 0x65, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, + 0x65, 0x72, 0x49, 0x64, 0x73, 0x12, 0x1b, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x18, 0x32, 0x52, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x12, 0x40, 0x0a, 0x06, 0x72, 0x69, 0x67, 0x68, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, + 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, + 0x2e, 0x76, 0x33, 0x2e, 0x52, 0x69, 0x67, 0x68, 0x74, 0x42, 0x11, 0xfa, 0x42, 0x0e, 0x92, 0x01, + 0x0b, 0x08, 0x01, 0x18, 0x01, 0x22, 0x05, 0x82, 0x01, 0x02, 0x10, 0x01, 0x52, 0x06, 0x72, 0x69, + 0x67, 0x68, 0x74, 0x73, 0x12, 0x43, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x5f, + 0x61, 0x74, 0x18, 0x04, 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, 0x40, 0x01, 0x52, 0x09, + 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x41, 0x74, 0x22, 0xd5, 0x01, 0x0a, 0x17, 0x55, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x41, 0x50, 0x49, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x44, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, - 0x10, 0x01, 0x52, 0x07, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x73, - 0x6b, 0x69, 0x70, 0x5f, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x09, 0x73, 0x6b, 0x69, 0x70, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x22, 0x30, 0x0a, 0x18, 0x43, 0x72, - 0x65, 0x61, 0x74, 0x65, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0xdb, 0x01, 0x0a, - 0x0c, 0x55, 0x73, 0x65, 0x72, 0x42, 0x6f, 0x6f, 0x6b, 0x6d, 0x61, 0x72, 0x6b, 0x12, 0x44, 0x0a, - 0x08, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1f, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, - 0x2e, 0x55, 0x73, 0x65, 0x72, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, - 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x07, 0x75, 0x73, 0x65, 0x72, - 0x49, 0x64, 0x73, 0x12, 0x4a, 0x0a, 0x0a, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, 0x69, 0x64, - 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, - 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x49, - 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, - 0x01, 0x02, 0x10, 0x01, 0x52, 0x09, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x49, 0x64, 0x73, 0x12, - 0x39, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x03, 0x20, + 0x10, 0x01, 0x52, 0x07, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x73, 0x12, 0x39, 0x0a, 0x07, 0x61, + 0x70, 0x69, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x74, + 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x41, 0x50, + 0x49, 0x4b, 0x65, 0x79, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x06, + 0x61, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x12, 0x39, 0x0a, 0x0a, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, + 0x6d, 0x61, 0x73, 0x6b, 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, 0x46, 0x69, 0x65, + 0x6c, 0x64, 0x4d, 0x61, 0x73, 0x6b, 0x52, 0x09, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x4d, 0x61, 0x73, + 0x6b, 0x22, 0x76, 0x0a, 0x17, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x41, + 0x50, 0x49, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x44, 0x0a, 0x08, + 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, + 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, + 0x55, 0x73, 0x65, 0x72, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x42, + 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x07, 0x75, 0x73, 0x65, 0x72, 0x49, + 0x64, 0x73, 0x12, 0x15, 0x0a, 0x06, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x05, 0x6b, 0x65, 0x79, 0x49, 0x64, 0x22, 0xf1, 0x02, 0x0a, 0x0a, 0x49, 0x6e, + 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1d, 0x0a, 0x05, 0x65, 0x6d, 0x61, 0x69, + 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x60, 0x01, + 0x52, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x39, 0x0a, + 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 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, 0x09, 0x65, + 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x41, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, + 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x04, 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, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, + 0x64, 0x41, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, + 0x74, 0x18, 0x05, 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, 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x3b, + 0x0a, 0x0b, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x06, 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, - 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x22, 0x4b, 0x0a, 0x0d, 0x55, 0x73, - 0x65, 0x72, 0x42, 0x6f, 0x6f, 0x6b, 0x6d, 0x61, 0x72, 0x6b, 0x73, 0x12, 0x3a, 0x0a, 0x09, 0x62, - 0x6f, 0x6f, 0x6b, 0x6d, 0x61, 0x72, 0x6b, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, - 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, - 0x55, 0x73, 0x65, 0x72, 0x42, 0x6f, 0x6f, 0x6b, 0x6d, 0x61, 0x72, 0x6b, 0x52, 0x09, 0x62, 0x6f, - 0x6f, 0x6b, 0x6d, 0x61, 0x72, 0x6b, 0x73, 0x22, 0xad, 0x01, 0x0a, 0x19, 0x43, 0x72, 0x65, 0x61, - 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x42, 0x6f, 0x6f, 0x6b, 0x6d, 0x61, 0x72, 0x6b, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x44, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, + 0x0a, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x40, 0x0a, 0x0b, 0x61, + 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x5f, 0x62, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1f, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, + 0x33, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, + 0x73, 0x52, 0x0a, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x42, 0x79, 0x22, 0x4c, 0x0a, + 0x16, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1e, 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x2a, 0x03, 0x18, 0xe8, 0x07, + 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x67, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x70, 0x61, 0x67, 0x65, 0x22, 0x4b, 0x0a, 0x0b, 0x49, + 0x6e, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x3c, 0x0a, 0x0b, 0x69, 0x6e, + 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x1a, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, + 0x2e, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x69, 0x6e, 0x76, + 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x36, 0x0a, 0x15, 0x53, 0x65, 0x6e, 0x64, + 0x49, 0x6e, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x1d, 0x0a, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x60, 0x01, 0x52, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, + 0x22, 0x38, 0x0a, 0x17, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x05, 0x65, + 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, + 0x02, 0x60, 0x01, 0x52, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x22, 0x86, 0x01, 0x0a, 0x16, 0x55, + 0x73, 0x65, 0x72, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, + 0x66, 0x69, 0x65, 0x72, 0x73, 0x12, 0x44, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, - 0x10, 0x01, 0x52, 0x07, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x73, 0x12, 0x4a, 0x0a, 0x0a, 0x65, - 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x21, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, - 0x2e, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, - 0x72, 0x73, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x09, 0x65, 0x6e, - 0x74, 0x69, 0x74, 0x79, 0x49, 0x64, 0x73, 0x22, 0xa4, 0x03, 0x0a, 0x18, 0x4c, 0x69, 0x73, 0x74, - 0x55, 0x73, 0x65, 0x72, 0x42, 0x6f, 0x6f, 0x6b, 0x6d, 0x61, 0x72, 0x6b, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x44, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, - 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x49, 0x64, 0x65, 0x6e, - 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, - 0x01, 0x52, 0x07, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x73, 0x12, 0x1e, 0x0a, 0x05, 0x6c, 0x69, - 0x6d, 0x69, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x2a, 0x03, - 0x18, 0xe8, 0x07, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, - 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x70, 0x61, 0x67, 0x65, 0x12, 0x7b, - 0x0a, 0x05, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x42, 0x65, 0xfa, - 0x42, 0x62, 0x72, 0x60, 0x52, 0x00, 0x52, 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x52, - 0x08, 0x2d, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x52, 0x0b, 0x65, 0x6e, 0x74, 0x69, 0x74, - 0x79, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x52, 0x0c, 0x2d, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, - 0x74, 0x79, 0x70, 0x65, 0x52, 0x09, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, 0x69, 0x64, 0x52, - 0x0a, 0x2d, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, 0x69, 0x64, 0x52, 0x0a, 0x63, 0x72, 0x65, - 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x52, 0x0b, 0x2d, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, - 0x64, 0x5f, 0x61, 0x74, 0x52, 0x05, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x64, - 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x64, 0x65, - 0x6c, 0x65, 0x74, 0x65, 0x64, 0x12, 0x6d, 0x0a, 0x0c, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, - 0x74, 0x79, 0x70, 0x65, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x42, 0x4a, 0xfa, 0x42, 0x47, - 0x92, 0x01, 0x44, 0x18, 0x01, 0x22, 0x40, 0x72, 0x3e, 0x52, 0x0b, 0x61, 0x70, 0x70, 0x6c, 0x69, - 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x0a, - 0x65, 0x6e, 0x64, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x52, 0x07, 0x67, 0x61, 0x74, 0x65, - 0x77, 0x61, 0x79, 0x52, 0x0c, 0x6f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x52, 0x0b, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x54, - 0x79, 0x70, 0x65, 0x73, 0x3a, 0x08, 0xf2, 0xaa, 0x19, 0x04, 0x08, 0x00, 0x10, 0x01, 0x22, 0xad, - 0x01, 0x0a, 0x19, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x42, 0x6f, 0x6f, - 0x6b, 0x6d, 0x61, 0x72, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x44, 0x0a, 0x08, - 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, - 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, - 0x55, 0x73, 0x65, 0x72, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x42, - 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x07, 0x75, 0x73, 0x65, 0x72, 0x49, - 0x64, 0x73, 0x12, 0x4a, 0x0a, 0x0a, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, 0x69, 0x64, 0x73, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, - 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x49, 0x64, - 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, - 0x02, 0x10, 0x01, 0x52, 0x09, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x49, 0x64, 0x73, 0x22, 0xb5, - 0x01, 0x0a, 0x1f, 0x42, 0x61, 0x74, 0x63, 0x68, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x55, 0x73, - 0x65, 0x72, 0x42, 0x6f, 0x6f, 0x6b, 0x6d, 0x61, 0x72, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x10, 0x01, 0x52, 0x07, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x73, 0x12, 0x26, 0x0a, 0x0a, 0x73, + 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, + 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x18, 0x40, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x49, 0x64, 0x22, 0xd3, 0x02, 0x0a, 0x0b, 0x55, 0x73, 0x65, 0x72, 0x53, 0x65, 0x73, 0x73, + 0x69, 0x6f, 0x6e, 0x12, 0x44, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, + 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x49, 0x64, 0x65, 0x6e, 0x74, + 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, + 0x52, 0x07, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x73, 0x12, 0x26, 0x0a, 0x0a, 0x73, 0x65, 0x73, + 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, + 0x42, 0x04, 0x72, 0x02, 0x18, 0x40, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, + 0x64, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 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, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x39, 0x0a, 0x0a, + 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x04, 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, 0x09, 0x75, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, + 0x65, 0x73, 0x5f, 0x61, 0x74, 0x18, 0x05, 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, 0x09, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, + 0x41, 0x74, 0x12, 0x25, 0x0a, 0x0e, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x65, + 0x63, 0x72, 0x65, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x73, 0x65, 0x73, 0x73, + 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x22, 0x47, 0x0a, 0x0c, 0x55, 0x73, 0x65, + 0x72, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x37, 0x0a, 0x08, 0x73, 0x65, 0x73, + 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x74, 0x74, + 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x55, 0x73, 0x65, + 0x72, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x73, 0x22, 0xcb, 0x01, 0x0a, 0x17, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x73, 0x65, 0x72, 0x53, + 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x44, + 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1f, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, + 0x33, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, + 0x73, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x07, 0x75, 0x73, 0x65, + 0x72, 0x49, 0x64, 0x73, 0x12, 0x36, 0x0a, 0x05, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x42, 0x20, 0xfa, 0x42, 0x1d, 0x72, 0x1b, 0x52, 0x00, 0x52, 0x0a, 0x63, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x52, 0x0b, 0x2d, 0x63, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x64, 0x5f, 0x61, 0x74, 0x52, 0x05, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x1e, 0x0a, 0x05, + 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x42, 0x08, 0xfa, 0x42, 0x05, + 0x2a, 0x03, 0x18, 0xe8, 0x07, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x12, 0x0a, 0x04, + 0x70, 0x61, 0x67, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x70, 0x61, 0x67, 0x65, + 0x22, 0xad, 0x02, 0x0a, 0x0a, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, + 0x44, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1f, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, + 0x76, 0x33, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, + 0x72, 0x73, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x07, 0x75, 0x73, + 0x65, 0x72, 0x49, 0x64, 0x73, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, + 0x5f, 0x61, 0x74, 0x18, 0x02, 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, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, + 0x12, 0x39, 0x0a, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 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, 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x65, + 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x5f, 0x61, 0x74, 0x18, 0x04, 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, 0x09, 0x65, 0x78, 0x70, + 0x69, 0x72, 0x65, 0x73, 0x41, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x12, 0x0a, 0x04, + 0x75, 0x73, 0x65, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x75, 0x73, 0x65, 0x64, + 0x22, 0x7e, 0x0a, 0x17, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x54, + 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x44, 0x0a, 0x08, 0x75, + 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, + 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x55, + 0x73, 0x65, 0x72, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x42, 0x08, + 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x07, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, + 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x6b, 0x69, 0x70, 0x5f, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x73, 0x6b, 0x69, 0x70, 0x45, 0x6d, 0x61, 0x69, 0x6c, + 0x22, 0x30, 0x0a, 0x18, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x54, + 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, + 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x6b, + 0x65, 0x6e, 0x22, 0xdb, 0x01, 0x0a, 0x0c, 0x55, 0x73, 0x65, 0x72, 0x42, 0x6f, 0x6f, 0x6b, 0x6d, + 0x61, 0x72, 0x6b, 0x12, 0x44, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, + 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x49, 0x64, 0x65, 0x6e, 0x74, + 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, + 0x52, 0x07, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x73, 0x12, 0x4a, 0x0a, 0x0a, 0x65, 0x6e, 0x74, + 0x69, 0x74, 0x79, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, + 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x45, + 0x6e, 0x74, 0x69, 0x74, 0x79, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, + 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x09, 0x65, 0x6e, 0x74, 0x69, + 0x74, 0x79, 0x49, 0x64, 0x73, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 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, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, + 0x22, 0x4b, 0x0a, 0x0d, 0x55, 0x73, 0x65, 0x72, 0x42, 0x6f, 0x6f, 0x6b, 0x6d, 0x61, 0x72, 0x6b, + 0x73, 0x12, 0x3a, 0x0a, 0x09, 0x62, 0x6f, 0x6f, 0x6b, 0x6d, 0x61, 0x72, 0x6b, 0x73, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, + 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x42, 0x6f, 0x6f, 0x6b, 0x6d, 0x61, + 0x72, 0x6b, 0x52, 0x09, 0x62, 0x6f, 0x6f, 0x6b, 0x6d, 0x61, 0x72, 0x6b, 0x73, 0x22, 0xad, 0x01, + 0x0a, 0x19, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x42, 0x6f, 0x6f, 0x6b, + 0x6d, 0x61, 0x72, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x44, 0x0a, 0x08, 0x75, + 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, + 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x55, + 0x73, 0x65, 0x72, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x42, 0x08, + 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x07, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, + 0x73, 0x12, 0x4a, 0x0a, 0x0a, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, 0x69, 0x64, 0x73, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, + 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x49, 0x64, 0x65, + 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, + 0x10, 0x01, 0x52, 0x09, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x49, 0x64, 0x73, 0x22, 0xa4, 0x03, + 0x0a, 0x18, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x73, 0x65, 0x72, 0x42, 0x6f, 0x6f, 0x6b, 0x6d, 0x61, + 0x72, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x44, 0x0a, 0x08, 0x75, 0x73, + 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x74, + 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x55, 0x73, + 0x65, 0x72, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x42, 0x08, 0xfa, + 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x07, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x73, + 0x12, 0x1e, 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x42, + 0x08, 0xfa, 0x42, 0x05, 0x2a, 0x03, 0x18, 0xe8, 0x07, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, + 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, + 0x70, 0x61, 0x67, 0x65, 0x12, 0x7b, 0x0a, 0x05, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x09, 0x42, 0x65, 0xfa, 0x42, 0x62, 0x72, 0x60, 0x52, 0x00, 0x52, 0x07, 0x75, 0x73, + 0x65, 0x72, 0x5f, 0x69, 0x64, 0x52, 0x08, 0x2d, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x52, + 0x0b, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x52, 0x0c, 0x2d, 0x65, + 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x52, 0x09, 0x65, 0x6e, 0x74, 0x69, + 0x74, 0x79, 0x5f, 0x69, 0x64, 0x52, 0x0a, 0x2d, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, 0x69, + 0x64, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x52, 0x0b, 0x2d, + 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x52, 0x05, 0x6f, 0x72, 0x64, 0x65, + 0x72, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x07, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x12, 0x6d, 0x0a, 0x0c, 0x65, + 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, + 0x09, 0x42, 0x4a, 0xfa, 0x42, 0x47, 0x92, 0x01, 0x44, 0x18, 0x01, 0x22, 0x40, 0x72, 0x3e, 0x52, + 0x0b, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x63, 0x6c, + 0x69, 0x65, 0x6e, 0x74, 0x52, 0x0a, 0x65, 0x6e, 0x64, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, + 0x52, 0x07, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x52, 0x0c, 0x6f, 0x72, 0x67, 0x61, 0x6e, + 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x52, 0x0b, 0x65, + 0x6e, 0x74, 0x69, 0x74, 0x79, 0x54, 0x79, 0x70, 0x65, 0x73, 0x3a, 0x08, 0xf2, 0xaa, 0x19, 0x04, + 0x08, 0x00, 0x10, 0x01, 0x22, 0xad, 0x01, 0x0a, 0x19, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x55, + 0x73, 0x65, 0x72, 0x42, 0x6f, 0x6f, 0x6b, 0x6d, 0x61, 0x72, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x44, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, - 0x07, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x73, 0x12, 0x4c, 0x0a, 0x0a, 0x65, 0x6e, 0x74, 0x69, - 0x74, 0x79, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x74, + 0x07, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x73, 0x12, 0x4a, 0x0a, 0x0a, 0x65, 0x6e, 0x74, 0x69, + 0x74, 0x79, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x42, - 0x0a, 0xfa, 0x42, 0x07, 0x92, 0x01, 0x04, 0x08, 0x01, 0x10, 0x14, 0x52, 0x09, 0x65, 0x6e, 0x74, - 0x69, 0x74, 0x79, 0x49, 0x64, 0x73, 0x2a, 0x70, 0x0a, 0x0c, 0x43, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, - 0x65, 0x54, 0x68, 0x65, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x14, 0x43, 0x4f, 0x4e, 0x53, 0x4f, 0x4c, - 0x45, 0x5f, 0x54, 0x48, 0x45, 0x4d, 0x45, 0x5f, 0x53, 0x59, 0x53, 0x54, 0x45, 0x4d, 0x10, 0x00, - 0x12, 0x17, 0x0a, 0x13, 0x43, 0x4f, 0x4e, 0x53, 0x4f, 0x4c, 0x45, 0x5f, 0x54, 0x48, 0x45, 0x4d, - 0x45, 0x5f, 0x4c, 0x49, 0x47, 0x48, 0x54, 0x10, 0x01, 0x12, 0x16, 0x0a, 0x12, 0x43, 0x4f, 0x4e, - 0x53, 0x4f, 0x4c, 0x45, 0x5f, 0x54, 0x48, 0x45, 0x4d, 0x45, 0x5f, 0x44, 0x41, 0x52, 0x4b, 0x10, - 0x02, 0x1a, 0x15, 0xea, 0xaa, 0x19, 0x11, 0x18, 0x01, 0x2a, 0x0d, 0x43, 0x4f, 0x4e, 0x53, 0x4f, - 0x4c, 0x45, 0x5f, 0x54, 0x48, 0x45, 0x4d, 0x45, 0x2a, 0x7d, 0x0a, 0x0f, 0x44, 0x61, 0x73, 0x68, - 0x62, 0x6f, 0x61, 0x72, 0x64, 0x4c, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x12, 0x1a, 0x0a, 0x16, 0x44, - 0x41, 0x53, 0x48, 0x42, 0x4f, 0x41, 0x52, 0x44, 0x5f, 0x4c, 0x41, 0x59, 0x4f, 0x55, 0x54, 0x5f, - 0x54, 0x41, 0x42, 0x4c, 0x45, 0x10, 0x00, 0x12, 0x19, 0x0a, 0x15, 0x44, 0x41, 0x53, 0x48, 0x42, - 0x4f, 0x41, 0x52, 0x44, 0x5f, 0x4c, 0x41, 0x59, 0x4f, 0x55, 0x54, 0x5f, 0x4c, 0x49, 0x53, 0x54, - 0x10, 0x01, 0x12, 0x19, 0x0a, 0x15, 0x44, 0x41, 0x53, 0x48, 0x42, 0x4f, 0x41, 0x52, 0x44, 0x5f, - 0x4c, 0x41, 0x59, 0x4f, 0x55, 0x54, 0x5f, 0x47, 0x52, 0x49, 0x44, 0x10, 0x02, 0x1a, 0x18, 0xea, - 0xaa, 0x19, 0x14, 0x18, 0x01, 0x2a, 0x10, 0x44, 0x41, 0x53, 0x48, 0x42, 0x4f, 0x41, 0x52, 0x44, - 0x5f, 0x4c, 0x41, 0x59, 0x4f, 0x55, 0x54, 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, + 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x09, 0x65, 0x6e, 0x74, 0x69, 0x74, + 0x79, 0x49, 0x64, 0x73, 0x22, 0xb5, 0x01, 0x0a, 0x1f, 0x42, 0x61, 0x74, 0x63, 0x68, 0x44, 0x65, + 0x6c, 0x65, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x42, 0x6f, 0x6f, 0x6b, 0x6d, 0x61, 0x72, 0x6b, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x44, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, + 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x74, 0x74, 0x6e, + 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x55, 0x73, 0x65, 0x72, + 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x42, 0x08, 0xfa, 0x42, 0x05, + 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x07, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x73, 0x12, 0x4c, + 0x0a, 0x0a, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, + 0x2e, 0x76, 0x33, 0x2e, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, + 0x66, 0x69, 0x65, 0x72, 0x73, 0x42, 0x0a, 0xfa, 0x42, 0x07, 0x92, 0x01, 0x04, 0x08, 0x01, 0x10, + 0x14, 0x52, 0x09, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x49, 0x64, 0x73, 0x2a, 0x70, 0x0a, 0x0c, + 0x43, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x65, 0x54, 0x68, 0x65, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x14, + 0x43, 0x4f, 0x4e, 0x53, 0x4f, 0x4c, 0x45, 0x5f, 0x54, 0x48, 0x45, 0x4d, 0x45, 0x5f, 0x53, 0x59, + 0x53, 0x54, 0x45, 0x4d, 0x10, 0x00, 0x12, 0x17, 0x0a, 0x13, 0x43, 0x4f, 0x4e, 0x53, 0x4f, 0x4c, + 0x45, 0x5f, 0x54, 0x48, 0x45, 0x4d, 0x45, 0x5f, 0x4c, 0x49, 0x47, 0x48, 0x54, 0x10, 0x01, 0x12, + 0x16, 0x0a, 0x12, 0x43, 0x4f, 0x4e, 0x53, 0x4f, 0x4c, 0x45, 0x5f, 0x54, 0x48, 0x45, 0x4d, 0x45, + 0x5f, 0x44, 0x41, 0x52, 0x4b, 0x10, 0x02, 0x1a, 0x15, 0xea, 0xaa, 0x19, 0x11, 0x18, 0x01, 0x2a, + 0x0d, 0x43, 0x4f, 0x4e, 0x53, 0x4f, 0x4c, 0x45, 0x5f, 0x54, 0x48, 0x45, 0x4d, 0x45, 0x2a, 0x4b, + 0x0a, 0x08, 0x54, 0x75, 0x74, 0x6f, 0x72, 0x69, 0x61, 0x6c, 0x12, 0x14, 0x0a, 0x10, 0x54, 0x55, + 0x54, 0x4f, 0x52, 0x49, 0x41, 0x4c, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, + 0x12, 0x21, 0x0a, 0x1d, 0x54, 0x55, 0x54, 0x4f, 0x52, 0x49, 0x41, 0x4c, 0x5f, 0x4c, 0x49, 0x56, + 0x45, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x5f, 0x53, 0x50, 0x4c, 0x49, 0x54, 0x5f, 0x56, 0x49, 0x45, + 0x57, 0x10, 0x01, 0x1a, 0x06, 0xea, 0xaa, 0x19, 0x02, 0x18, 0x01, 0x2a, 0x7d, 0x0a, 0x0f, 0x44, + 0x61, 0x73, 0x68, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x4c, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x12, 0x1a, + 0x0a, 0x16, 0x44, 0x41, 0x53, 0x48, 0x42, 0x4f, 0x41, 0x52, 0x44, 0x5f, 0x4c, 0x41, 0x59, 0x4f, + 0x55, 0x54, 0x5f, 0x54, 0x41, 0x42, 0x4c, 0x45, 0x10, 0x00, 0x12, 0x19, 0x0a, 0x15, 0x44, 0x41, + 0x53, 0x48, 0x42, 0x4f, 0x41, 0x52, 0x44, 0x5f, 0x4c, 0x41, 0x59, 0x4f, 0x55, 0x54, 0x5f, 0x4c, + 0x49, 0x53, 0x54, 0x10, 0x01, 0x12, 0x19, 0x0a, 0x15, 0x44, 0x41, 0x53, 0x48, 0x42, 0x4f, 0x41, + 0x52, 0x44, 0x5f, 0x4c, 0x41, 0x59, 0x4f, 0x55, 0x54, 0x5f, 0x47, 0x52, 0x49, 0x44, 0x10, 0x02, + 0x1a, 0x18, 0xea, 0xaa, 0x19, 0x14, 0x18, 0x01, 0x2a, 0x10, 0x44, 0x41, 0x53, 0x48, 0x42, 0x4f, + 0x41, 0x52, 0x44, 0x5f, 0x4c, 0x41, 0x59, 0x4f, 0x55, 0x54, 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 ( @@ -3101,133 +3303,142 @@ func file_ttn_lorawan_v3_user_proto_rawDescGZIP() []byte { return file_ttn_lorawan_v3_user_proto_rawDescData } -var file_ttn_lorawan_v3_user_proto_enumTypes = make([]protoimpl.EnumInfo, 2) -var file_ttn_lorawan_v3_user_proto_msgTypes = make([]protoimpl.MessageInfo, 35) +var file_ttn_lorawan_v3_user_proto_enumTypes = make([]protoimpl.EnumInfo, 3) +var file_ttn_lorawan_v3_user_proto_msgTypes = make([]protoimpl.MessageInfo, 37) var file_ttn_lorawan_v3_user_proto_goTypes = []interface{}{ (ConsoleTheme)(0), // 0: ttn.lorawan.v3.ConsoleTheme - (DashboardLayout)(0), // 1: ttn.lorawan.v3.DashboardLayout - (*UserConsolePreferences)(nil), // 2: ttn.lorawan.v3.UserConsolePreferences - (*User)(nil), // 3: ttn.lorawan.v3.User - (*Users)(nil), // 4: ttn.lorawan.v3.Users - (*GetUserRequest)(nil), // 5: ttn.lorawan.v3.GetUserRequest - (*ListUsersRequest)(nil), // 6: ttn.lorawan.v3.ListUsersRequest - (*CreateUserRequest)(nil), // 7: ttn.lorawan.v3.CreateUserRequest - (*UpdateUserRequest)(nil), // 8: ttn.lorawan.v3.UpdateUserRequest - (*CreateTemporaryPasswordRequest)(nil), // 9: ttn.lorawan.v3.CreateTemporaryPasswordRequest - (*UpdateUserPasswordRequest)(nil), // 10: ttn.lorawan.v3.UpdateUserPasswordRequest - (*ListUserAPIKeysRequest)(nil), // 11: ttn.lorawan.v3.ListUserAPIKeysRequest - (*GetUserAPIKeyRequest)(nil), // 12: ttn.lorawan.v3.GetUserAPIKeyRequest - (*CreateUserAPIKeyRequest)(nil), // 13: ttn.lorawan.v3.CreateUserAPIKeyRequest - (*UpdateUserAPIKeyRequest)(nil), // 14: ttn.lorawan.v3.UpdateUserAPIKeyRequest - (*DeleteUserAPIKeyRequest)(nil), // 15: ttn.lorawan.v3.DeleteUserAPIKeyRequest - (*Invitation)(nil), // 16: ttn.lorawan.v3.Invitation - (*ListInvitationsRequest)(nil), // 17: ttn.lorawan.v3.ListInvitationsRequest - (*Invitations)(nil), // 18: ttn.lorawan.v3.Invitations - (*SendInvitationRequest)(nil), // 19: ttn.lorawan.v3.SendInvitationRequest - (*DeleteInvitationRequest)(nil), // 20: ttn.lorawan.v3.DeleteInvitationRequest - (*UserSessionIdentifiers)(nil), // 21: ttn.lorawan.v3.UserSessionIdentifiers - (*UserSession)(nil), // 22: ttn.lorawan.v3.UserSession - (*UserSessions)(nil), // 23: ttn.lorawan.v3.UserSessions - (*ListUserSessionsRequest)(nil), // 24: ttn.lorawan.v3.ListUserSessionsRequest - (*LoginToken)(nil), // 25: ttn.lorawan.v3.LoginToken - (*CreateLoginTokenRequest)(nil), // 26: ttn.lorawan.v3.CreateLoginTokenRequest - (*CreateLoginTokenResponse)(nil), // 27: ttn.lorawan.v3.CreateLoginTokenResponse - (*UserBookmark)(nil), // 28: ttn.lorawan.v3.UserBookmark - (*UserBookmarks)(nil), // 29: ttn.lorawan.v3.UserBookmarks - (*CreateUserBookmarkRequest)(nil), // 30: ttn.lorawan.v3.CreateUserBookmarkRequest - (*ListUserBookmarksRequest)(nil), // 31: ttn.lorawan.v3.ListUserBookmarksRequest - (*DeleteUserBookmarkRequest)(nil), // 32: ttn.lorawan.v3.DeleteUserBookmarkRequest - (*BatchDeleteUserBookmarksRequest)(nil), // 33: ttn.lorawan.v3.BatchDeleteUserBookmarksRequest - (*UserConsolePreferences_DashboardLayouts)(nil), // 34: ttn.lorawan.v3.UserConsolePreferences.DashboardLayouts - (*UserConsolePreferences_SortBy)(nil), // 35: ttn.lorawan.v3.UserConsolePreferences.SortBy - nil, // 36: ttn.lorawan.v3.User.AttributesEntry - (*UserIdentifiers)(nil), // 37: ttn.lorawan.v3.UserIdentifiers - (*timestamppb.Timestamp)(nil), // 38: google.protobuf.Timestamp - (*ContactInfo)(nil), // 39: ttn.lorawan.v3.ContactInfo - (State)(0), // 40: ttn.lorawan.v3.State - (*Picture)(nil), // 41: ttn.lorawan.v3.Picture - (*fieldmaskpb.FieldMask)(nil), // 42: google.protobuf.FieldMask - (Right)(0), // 43: ttn.lorawan.v3.Right - (*APIKey)(nil), // 44: ttn.lorawan.v3.APIKey - (*EntityIdentifiers)(nil), // 45: ttn.lorawan.v3.EntityIdentifiers + (Tutorial)(0), // 1: ttn.lorawan.v3.Tutorial + (DashboardLayout)(0), // 2: ttn.lorawan.v3.DashboardLayout + (*EmailNotificationPreferences)(nil), // 3: ttn.lorawan.v3.EmailNotificationPreferences + (*UserConsolePreferences)(nil), // 4: ttn.lorawan.v3.UserConsolePreferences + (*User)(nil), // 5: ttn.lorawan.v3.User + (*Users)(nil), // 6: ttn.lorawan.v3.Users + (*GetUserRequest)(nil), // 7: ttn.lorawan.v3.GetUserRequest + (*ListUsersRequest)(nil), // 8: ttn.lorawan.v3.ListUsersRequest + (*CreateUserRequest)(nil), // 9: ttn.lorawan.v3.CreateUserRequest + (*UpdateUserRequest)(nil), // 10: ttn.lorawan.v3.UpdateUserRequest + (*CreateTemporaryPasswordRequest)(nil), // 11: ttn.lorawan.v3.CreateTemporaryPasswordRequest + (*UpdateUserPasswordRequest)(nil), // 12: ttn.lorawan.v3.UpdateUserPasswordRequest + (*ListUserAPIKeysRequest)(nil), // 13: ttn.lorawan.v3.ListUserAPIKeysRequest + (*GetUserAPIKeyRequest)(nil), // 14: ttn.lorawan.v3.GetUserAPIKeyRequest + (*CreateUserAPIKeyRequest)(nil), // 15: ttn.lorawan.v3.CreateUserAPIKeyRequest + (*UpdateUserAPIKeyRequest)(nil), // 16: ttn.lorawan.v3.UpdateUserAPIKeyRequest + (*DeleteUserAPIKeyRequest)(nil), // 17: ttn.lorawan.v3.DeleteUserAPIKeyRequest + (*Invitation)(nil), // 18: ttn.lorawan.v3.Invitation + (*ListInvitationsRequest)(nil), // 19: ttn.lorawan.v3.ListInvitationsRequest + (*Invitations)(nil), // 20: ttn.lorawan.v3.Invitations + (*SendInvitationRequest)(nil), // 21: ttn.lorawan.v3.SendInvitationRequest + (*DeleteInvitationRequest)(nil), // 22: ttn.lorawan.v3.DeleteInvitationRequest + (*UserSessionIdentifiers)(nil), // 23: ttn.lorawan.v3.UserSessionIdentifiers + (*UserSession)(nil), // 24: ttn.lorawan.v3.UserSession + (*UserSessions)(nil), // 25: ttn.lorawan.v3.UserSessions + (*ListUserSessionsRequest)(nil), // 26: ttn.lorawan.v3.ListUserSessionsRequest + (*LoginToken)(nil), // 27: ttn.lorawan.v3.LoginToken + (*CreateLoginTokenRequest)(nil), // 28: ttn.lorawan.v3.CreateLoginTokenRequest + (*CreateLoginTokenResponse)(nil), // 29: ttn.lorawan.v3.CreateLoginTokenResponse + (*UserBookmark)(nil), // 30: ttn.lorawan.v3.UserBookmark + (*UserBookmarks)(nil), // 31: ttn.lorawan.v3.UserBookmarks + (*CreateUserBookmarkRequest)(nil), // 32: ttn.lorawan.v3.CreateUserBookmarkRequest + (*ListUserBookmarksRequest)(nil), // 33: ttn.lorawan.v3.ListUserBookmarksRequest + (*DeleteUserBookmarkRequest)(nil), // 34: ttn.lorawan.v3.DeleteUserBookmarkRequest + (*BatchDeleteUserBookmarksRequest)(nil), // 35: ttn.lorawan.v3.BatchDeleteUserBookmarksRequest + (*UserConsolePreferences_DashboardLayouts)(nil), // 36: ttn.lorawan.v3.UserConsolePreferences.DashboardLayouts + (*UserConsolePreferences_SortBy)(nil), // 37: ttn.lorawan.v3.UserConsolePreferences.SortBy + (*UserConsolePreferences_Tutorials)(nil), // 38: ttn.lorawan.v3.UserConsolePreferences.Tutorials + nil, // 39: ttn.lorawan.v3.User.AttributesEntry + (NotificationType)(0), // 40: ttn.lorawan.v3.NotificationType + (*UserIdentifiers)(nil), // 41: ttn.lorawan.v3.UserIdentifiers + (*timestamppb.Timestamp)(nil), // 42: google.protobuf.Timestamp + (*ContactInfo)(nil), // 43: ttn.lorawan.v3.ContactInfo + (State)(0), // 44: ttn.lorawan.v3.State + (*Picture)(nil), // 45: ttn.lorawan.v3.Picture + (Right)(0), // 46: ttn.lorawan.v3.Right + (*fieldmaskpb.FieldMask)(nil), // 47: google.protobuf.FieldMask + (*APIKey)(nil), // 48: ttn.lorawan.v3.APIKey + (*EntityIdentifiers)(nil), // 49: ttn.lorawan.v3.EntityIdentifiers } var file_ttn_lorawan_v3_user_proto_depIdxs = []int32{ - 0, // 0: ttn.lorawan.v3.UserConsolePreferences.console_theme:type_name -> ttn.lorawan.v3.ConsoleTheme - 34, // 1: ttn.lorawan.v3.UserConsolePreferences.dashboard_layouts:type_name -> ttn.lorawan.v3.UserConsolePreferences.DashboardLayouts - 35, // 2: ttn.lorawan.v3.UserConsolePreferences.sort_by:type_name -> ttn.lorawan.v3.UserConsolePreferences.SortBy - 37, // 3: ttn.lorawan.v3.User.ids:type_name -> ttn.lorawan.v3.UserIdentifiers - 38, // 4: ttn.lorawan.v3.User.created_at:type_name -> google.protobuf.Timestamp - 38, // 5: ttn.lorawan.v3.User.updated_at:type_name -> google.protobuf.Timestamp - 38, // 6: ttn.lorawan.v3.User.deleted_at:type_name -> google.protobuf.Timestamp - 36, // 7: ttn.lorawan.v3.User.attributes:type_name -> ttn.lorawan.v3.User.AttributesEntry - 39, // 8: ttn.lorawan.v3.User.contact_info:type_name -> ttn.lorawan.v3.ContactInfo - 38, // 9: ttn.lorawan.v3.User.primary_email_address_validated_at:type_name -> google.protobuf.Timestamp - 38, // 10: ttn.lorawan.v3.User.password_updated_at:type_name -> google.protobuf.Timestamp - 40, // 11: ttn.lorawan.v3.User.state:type_name -> ttn.lorawan.v3.State - 38, // 12: ttn.lorawan.v3.User.temporary_password_created_at:type_name -> google.protobuf.Timestamp - 38, // 13: ttn.lorawan.v3.User.temporary_password_expires_at:type_name -> google.protobuf.Timestamp - 41, // 14: ttn.lorawan.v3.User.profile_picture:type_name -> ttn.lorawan.v3.Picture - 2, // 15: ttn.lorawan.v3.User.console_preferences:type_name -> ttn.lorawan.v3.UserConsolePreferences - 3, // 16: ttn.lorawan.v3.Users.users:type_name -> ttn.lorawan.v3.User - 37, // 17: ttn.lorawan.v3.GetUserRequest.user_ids:type_name -> ttn.lorawan.v3.UserIdentifiers - 42, // 18: ttn.lorawan.v3.GetUserRequest.field_mask:type_name -> google.protobuf.FieldMask - 42, // 19: ttn.lorawan.v3.ListUsersRequest.field_mask:type_name -> google.protobuf.FieldMask - 3, // 20: ttn.lorawan.v3.CreateUserRequest.user:type_name -> ttn.lorawan.v3.User - 3, // 21: ttn.lorawan.v3.UpdateUserRequest.user:type_name -> ttn.lorawan.v3.User - 42, // 22: ttn.lorawan.v3.UpdateUserRequest.field_mask:type_name -> google.protobuf.FieldMask - 37, // 23: ttn.lorawan.v3.CreateTemporaryPasswordRequest.user_ids:type_name -> ttn.lorawan.v3.UserIdentifiers - 37, // 24: ttn.lorawan.v3.UpdateUserPasswordRequest.user_ids:type_name -> ttn.lorawan.v3.UserIdentifiers - 37, // 25: ttn.lorawan.v3.ListUserAPIKeysRequest.user_ids:type_name -> ttn.lorawan.v3.UserIdentifiers - 37, // 26: ttn.lorawan.v3.GetUserAPIKeyRequest.user_ids:type_name -> ttn.lorawan.v3.UserIdentifiers - 37, // 27: ttn.lorawan.v3.CreateUserAPIKeyRequest.user_ids:type_name -> ttn.lorawan.v3.UserIdentifiers - 43, // 28: ttn.lorawan.v3.CreateUserAPIKeyRequest.rights:type_name -> ttn.lorawan.v3.Right - 38, // 29: ttn.lorawan.v3.CreateUserAPIKeyRequest.expires_at:type_name -> google.protobuf.Timestamp - 37, // 30: ttn.lorawan.v3.UpdateUserAPIKeyRequest.user_ids:type_name -> ttn.lorawan.v3.UserIdentifiers - 44, // 31: ttn.lorawan.v3.UpdateUserAPIKeyRequest.api_key:type_name -> ttn.lorawan.v3.APIKey - 42, // 32: ttn.lorawan.v3.UpdateUserAPIKeyRequest.field_mask:type_name -> google.protobuf.FieldMask - 37, // 33: ttn.lorawan.v3.DeleteUserAPIKeyRequest.user_ids:type_name -> ttn.lorawan.v3.UserIdentifiers - 38, // 34: ttn.lorawan.v3.Invitation.expires_at:type_name -> google.protobuf.Timestamp - 38, // 35: ttn.lorawan.v3.Invitation.created_at:type_name -> google.protobuf.Timestamp - 38, // 36: ttn.lorawan.v3.Invitation.updated_at:type_name -> google.protobuf.Timestamp - 38, // 37: ttn.lorawan.v3.Invitation.accepted_at:type_name -> google.protobuf.Timestamp - 37, // 38: ttn.lorawan.v3.Invitation.accepted_by:type_name -> ttn.lorawan.v3.UserIdentifiers - 16, // 39: ttn.lorawan.v3.Invitations.invitations:type_name -> ttn.lorawan.v3.Invitation - 37, // 40: ttn.lorawan.v3.UserSessionIdentifiers.user_ids:type_name -> ttn.lorawan.v3.UserIdentifiers - 37, // 41: ttn.lorawan.v3.UserSession.user_ids:type_name -> ttn.lorawan.v3.UserIdentifiers - 38, // 42: ttn.lorawan.v3.UserSession.created_at:type_name -> google.protobuf.Timestamp - 38, // 43: ttn.lorawan.v3.UserSession.updated_at:type_name -> google.protobuf.Timestamp - 38, // 44: ttn.lorawan.v3.UserSession.expires_at:type_name -> google.protobuf.Timestamp - 22, // 45: ttn.lorawan.v3.UserSessions.sessions:type_name -> ttn.lorawan.v3.UserSession - 37, // 46: ttn.lorawan.v3.ListUserSessionsRequest.user_ids:type_name -> ttn.lorawan.v3.UserIdentifiers - 37, // 47: ttn.lorawan.v3.LoginToken.user_ids:type_name -> ttn.lorawan.v3.UserIdentifiers - 38, // 48: ttn.lorawan.v3.LoginToken.created_at:type_name -> google.protobuf.Timestamp - 38, // 49: ttn.lorawan.v3.LoginToken.updated_at:type_name -> google.protobuf.Timestamp - 38, // 50: ttn.lorawan.v3.LoginToken.expires_at:type_name -> google.protobuf.Timestamp - 37, // 51: ttn.lorawan.v3.CreateLoginTokenRequest.user_ids:type_name -> ttn.lorawan.v3.UserIdentifiers - 37, // 52: ttn.lorawan.v3.UserBookmark.user_ids:type_name -> ttn.lorawan.v3.UserIdentifiers - 45, // 53: ttn.lorawan.v3.UserBookmark.entity_ids:type_name -> ttn.lorawan.v3.EntityIdentifiers - 38, // 54: ttn.lorawan.v3.UserBookmark.created_at:type_name -> google.protobuf.Timestamp - 28, // 55: ttn.lorawan.v3.UserBookmarks.bookmarks:type_name -> ttn.lorawan.v3.UserBookmark - 37, // 56: ttn.lorawan.v3.CreateUserBookmarkRequest.user_ids:type_name -> ttn.lorawan.v3.UserIdentifiers - 45, // 57: ttn.lorawan.v3.CreateUserBookmarkRequest.entity_ids:type_name -> ttn.lorawan.v3.EntityIdentifiers - 37, // 58: ttn.lorawan.v3.ListUserBookmarksRequest.user_ids:type_name -> ttn.lorawan.v3.UserIdentifiers - 37, // 59: ttn.lorawan.v3.DeleteUserBookmarkRequest.user_ids:type_name -> ttn.lorawan.v3.UserIdentifiers - 45, // 60: ttn.lorawan.v3.DeleteUserBookmarkRequest.entity_ids:type_name -> ttn.lorawan.v3.EntityIdentifiers - 37, // 61: ttn.lorawan.v3.BatchDeleteUserBookmarksRequest.user_ids:type_name -> ttn.lorawan.v3.UserIdentifiers - 45, // 62: ttn.lorawan.v3.BatchDeleteUserBookmarksRequest.entity_ids:type_name -> ttn.lorawan.v3.EntityIdentifiers - 1, // 63: ttn.lorawan.v3.UserConsolePreferences.DashboardLayouts.api_key:type_name -> ttn.lorawan.v3.DashboardLayout - 1, // 64: ttn.lorawan.v3.UserConsolePreferences.DashboardLayouts.application:type_name -> ttn.lorawan.v3.DashboardLayout - 1, // 65: ttn.lorawan.v3.UserConsolePreferences.DashboardLayouts.collaborator:type_name -> ttn.lorawan.v3.DashboardLayout - 1, // 66: ttn.lorawan.v3.UserConsolePreferences.DashboardLayouts.end_device:type_name -> ttn.lorawan.v3.DashboardLayout - 1, // 67: ttn.lorawan.v3.UserConsolePreferences.DashboardLayouts.gateway:type_name -> ttn.lorawan.v3.DashboardLayout - 1, // 68: ttn.lorawan.v3.UserConsolePreferences.DashboardLayouts.organization:type_name -> ttn.lorawan.v3.DashboardLayout - 1, // 69: ttn.lorawan.v3.UserConsolePreferences.DashboardLayouts.overview:type_name -> ttn.lorawan.v3.DashboardLayout - 1, // 70: ttn.lorawan.v3.UserConsolePreferences.DashboardLayouts.user:type_name -> ttn.lorawan.v3.DashboardLayout - 71, // [71:71] is the sub-list for method output_type - 71, // [71:71] is the sub-list for method input_type - 71, // [71:71] is the sub-list for extension type_name - 71, // [71:71] is the sub-list for extension extendee - 0, // [0:71] is the sub-list for field type_name + 40, // 0: ttn.lorawan.v3.EmailNotificationPreferences.types:type_name -> ttn.lorawan.v3.NotificationType + 0, // 1: ttn.lorawan.v3.UserConsolePreferences.console_theme:type_name -> ttn.lorawan.v3.ConsoleTheme + 36, // 2: ttn.lorawan.v3.UserConsolePreferences.dashboard_layouts:type_name -> ttn.lorawan.v3.UserConsolePreferences.DashboardLayouts + 37, // 3: ttn.lorawan.v3.UserConsolePreferences.sort_by:type_name -> ttn.lorawan.v3.UserConsolePreferences.SortBy + 38, // 4: ttn.lorawan.v3.UserConsolePreferences.tutorials:type_name -> ttn.lorawan.v3.UserConsolePreferences.Tutorials + 41, // 5: ttn.lorawan.v3.User.ids:type_name -> ttn.lorawan.v3.UserIdentifiers + 42, // 6: ttn.lorawan.v3.User.created_at:type_name -> google.protobuf.Timestamp + 42, // 7: ttn.lorawan.v3.User.updated_at:type_name -> google.protobuf.Timestamp + 42, // 8: ttn.lorawan.v3.User.deleted_at:type_name -> google.protobuf.Timestamp + 39, // 9: ttn.lorawan.v3.User.attributes:type_name -> ttn.lorawan.v3.User.AttributesEntry + 43, // 10: ttn.lorawan.v3.User.contact_info:type_name -> ttn.lorawan.v3.ContactInfo + 42, // 11: ttn.lorawan.v3.User.primary_email_address_validated_at:type_name -> google.protobuf.Timestamp + 42, // 12: ttn.lorawan.v3.User.password_updated_at:type_name -> google.protobuf.Timestamp + 44, // 13: ttn.lorawan.v3.User.state:type_name -> ttn.lorawan.v3.State + 42, // 14: ttn.lorawan.v3.User.temporary_password_created_at:type_name -> google.protobuf.Timestamp + 42, // 15: ttn.lorawan.v3.User.temporary_password_expires_at:type_name -> google.protobuf.Timestamp + 45, // 16: ttn.lorawan.v3.User.profile_picture:type_name -> ttn.lorawan.v3.Picture + 4, // 17: ttn.lorawan.v3.User.console_preferences:type_name -> ttn.lorawan.v3.UserConsolePreferences + 3, // 18: ttn.lorawan.v3.User.email_notification_preferences:type_name -> ttn.lorawan.v3.EmailNotificationPreferences + 46, // 19: ttn.lorawan.v3.User.universal_rights:type_name -> ttn.lorawan.v3.Right + 5, // 20: ttn.lorawan.v3.Users.users:type_name -> ttn.lorawan.v3.User + 41, // 21: ttn.lorawan.v3.GetUserRequest.user_ids:type_name -> ttn.lorawan.v3.UserIdentifiers + 47, // 22: ttn.lorawan.v3.GetUserRequest.field_mask:type_name -> google.protobuf.FieldMask + 47, // 23: ttn.lorawan.v3.ListUsersRequest.field_mask:type_name -> google.protobuf.FieldMask + 5, // 24: ttn.lorawan.v3.CreateUserRequest.user:type_name -> ttn.lorawan.v3.User + 5, // 25: ttn.lorawan.v3.UpdateUserRequest.user:type_name -> ttn.lorawan.v3.User + 47, // 26: ttn.lorawan.v3.UpdateUserRequest.field_mask:type_name -> google.protobuf.FieldMask + 41, // 27: ttn.lorawan.v3.CreateTemporaryPasswordRequest.user_ids:type_name -> ttn.lorawan.v3.UserIdentifiers + 41, // 28: ttn.lorawan.v3.UpdateUserPasswordRequest.user_ids:type_name -> ttn.lorawan.v3.UserIdentifiers + 41, // 29: ttn.lorawan.v3.ListUserAPIKeysRequest.user_ids:type_name -> ttn.lorawan.v3.UserIdentifiers + 41, // 30: ttn.lorawan.v3.GetUserAPIKeyRequest.user_ids:type_name -> ttn.lorawan.v3.UserIdentifiers + 41, // 31: ttn.lorawan.v3.CreateUserAPIKeyRequest.user_ids:type_name -> ttn.lorawan.v3.UserIdentifiers + 46, // 32: ttn.lorawan.v3.CreateUserAPIKeyRequest.rights:type_name -> ttn.lorawan.v3.Right + 42, // 33: ttn.lorawan.v3.CreateUserAPIKeyRequest.expires_at:type_name -> google.protobuf.Timestamp + 41, // 34: ttn.lorawan.v3.UpdateUserAPIKeyRequest.user_ids:type_name -> ttn.lorawan.v3.UserIdentifiers + 48, // 35: ttn.lorawan.v3.UpdateUserAPIKeyRequest.api_key:type_name -> ttn.lorawan.v3.APIKey + 47, // 36: ttn.lorawan.v3.UpdateUserAPIKeyRequest.field_mask:type_name -> google.protobuf.FieldMask + 41, // 37: ttn.lorawan.v3.DeleteUserAPIKeyRequest.user_ids:type_name -> ttn.lorawan.v3.UserIdentifiers + 42, // 38: ttn.lorawan.v3.Invitation.expires_at:type_name -> google.protobuf.Timestamp + 42, // 39: ttn.lorawan.v3.Invitation.created_at:type_name -> google.protobuf.Timestamp + 42, // 40: ttn.lorawan.v3.Invitation.updated_at:type_name -> google.protobuf.Timestamp + 42, // 41: ttn.lorawan.v3.Invitation.accepted_at:type_name -> google.protobuf.Timestamp + 41, // 42: ttn.lorawan.v3.Invitation.accepted_by:type_name -> ttn.lorawan.v3.UserIdentifiers + 18, // 43: ttn.lorawan.v3.Invitations.invitations:type_name -> ttn.lorawan.v3.Invitation + 41, // 44: ttn.lorawan.v3.UserSessionIdentifiers.user_ids:type_name -> ttn.lorawan.v3.UserIdentifiers + 41, // 45: ttn.lorawan.v3.UserSession.user_ids:type_name -> ttn.lorawan.v3.UserIdentifiers + 42, // 46: ttn.lorawan.v3.UserSession.created_at:type_name -> google.protobuf.Timestamp + 42, // 47: ttn.lorawan.v3.UserSession.updated_at:type_name -> google.protobuf.Timestamp + 42, // 48: ttn.lorawan.v3.UserSession.expires_at:type_name -> google.protobuf.Timestamp + 24, // 49: ttn.lorawan.v3.UserSessions.sessions:type_name -> ttn.lorawan.v3.UserSession + 41, // 50: ttn.lorawan.v3.ListUserSessionsRequest.user_ids:type_name -> ttn.lorawan.v3.UserIdentifiers + 41, // 51: ttn.lorawan.v3.LoginToken.user_ids:type_name -> ttn.lorawan.v3.UserIdentifiers + 42, // 52: ttn.lorawan.v3.LoginToken.created_at:type_name -> google.protobuf.Timestamp + 42, // 53: ttn.lorawan.v3.LoginToken.updated_at:type_name -> google.protobuf.Timestamp + 42, // 54: ttn.lorawan.v3.LoginToken.expires_at:type_name -> google.protobuf.Timestamp + 41, // 55: ttn.lorawan.v3.CreateLoginTokenRequest.user_ids:type_name -> ttn.lorawan.v3.UserIdentifiers + 41, // 56: ttn.lorawan.v3.UserBookmark.user_ids:type_name -> ttn.lorawan.v3.UserIdentifiers + 49, // 57: ttn.lorawan.v3.UserBookmark.entity_ids:type_name -> ttn.lorawan.v3.EntityIdentifiers + 42, // 58: ttn.lorawan.v3.UserBookmark.created_at:type_name -> google.protobuf.Timestamp + 30, // 59: ttn.lorawan.v3.UserBookmarks.bookmarks:type_name -> ttn.lorawan.v3.UserBookmark + 41, // 60: ttn.lorawan.v3.CreateUserBookmarkRequest.user_ids:type_name -> ttn.lorawan.v3.UserIdentifiers + 49, // 61: ttn.lorawan.v3.CreateUserBookmarkRequest.entity_ids:type_name -> ttn.lorawan.v3.EntityIdentifiers + 41, // 62: ttn.lorawan.v3.ListUserBookmarksRequest.user_ids:type_name -> ttn.lorawan.v3.UserIdentifiers + 41, // 63: ttn.lorawan.v3.DeleteUserBookmarkRequest.user_ids:type_name -> ttn.lorawan.v3.UserIdentifiers + 49, // 64: ttn.lorawan.v3.DeleteUserBookmarkRequest.entity_ids:type_name -> ttn.lorawan.v3.EntityIdentifiers + 41, // 65: ttn.lorawan.v3.BatchDeleteUserBookmarksRequest.user_ids:type_name -> ttn.lorawan.v3.UserIdentifiers + 49, // 66: ttn.lorawan.v3.BatchDeleteUserBookmarksRequest.entity_ids:type_name -> ttn.lorawan.v3.EntityIdentifiers + 2, // 67: ttn.lorawan.v3.UserConsolePreferences.DashboardLayouts.api_key:type_name -> ttn.lorawan.v3.DashboardLayout + 2, // 68: ttn.lorawan.v3.UserConsolePreferences.DashboardLayouts.application:type_name -> ttn.lorawan.v3.DashboardLayout + 2, // 69: ttn.lorawan.v3.UserConsolePreferences.DashboardLayouts.collaborator:type_name -> ttn.lorawan.v3.DashboardLayout + 2, // 70: ttn.lorawan.v3.UserConsolePreferences.DashboardLayouts.end_device:type_name -> ttn.lorawan.v3.DashboardLayout + 2, // 71: ttn.lorawan.v3.UserConsolePreferences.DashboardLayouts.gateway:type_name -> ttn.lorawan.v3.DashboardLayout + 2, // 72: ttn.lorawan.v3.UserConsolePreferences.DashboardLayouts.organization:type_name -> ttn.lorawan.v3.DashboardLayout + 2, // 73: ttn.lorawan.v3.UserConsolePreferences.DashboardLayouts.overview:type_name -> ttn.lorawan.v3.DashboardLayout + 2, // 74: ttn.lorawan.v3.UserConsolePreferences.DashboardLayouts.user:type_name -> ttn.lorawan.v3.DashboardLayout + 1, // 75: ttn.lorawan.v3.UserConsolePreferences.Tutorials.seen:type_name -> ttn.lorawan.v3.Tutorial + 76, // [76:76] is the sub-list for method output_type + 76, // [76:76] is the sub-list for method input_type + 76, // [76:76] is the sub-list for extension type_name + 76, // [76:76] is the sub-list for extension extendee + 0, // [0:76] is the sub-list for field type_name } func init() { file_ttn_lorawan_v3_user_proto_init() } @@ -3238,11 +3449,12 @@ func file_ttn_lorawan_v3_user_proto_init() { file_ttn_lorawan_v3_contact_info_proto_init() file_ttn_lorawan_v3_enums_proto_init() file_ttn_lorawan_v3_identifiers_proto_init() + file_ttn_lorawan_v3_notification_service_proto_init() file_ttn_lorawan_v3_picture_proto_init() file_ttn_lorawan_v3_rights_proto_init() if !protoimpl.UnsafeEnabled { file_ttn_lorawan_v3_user_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*UserConsolePreferences); i { + switch v := v.(*EmailNotificationPreferences); i { case 0: return &v.state case 1: @@ -3254,7 +3466,7 @@ func file_ttn_lorawan_v3_user_proto_init() { } } file_ttn_lorawan_v3_user_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*User); i { + switch v := v.(*UserConsolePreferences); i { case 0: return &v.state case 1: @@ -3266,7 +3478,7 @@ func file_ttn_lorawan_v3_user_proto_init() { } } file_ttn_lorawan_v3_user_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Users); i { + switch v := v.(*User); i { case 0: return &v.state case 1: @@ -3278,7 +3490,7 @@ func file_ttn_lorawan_v3_user_proto_init() { } } file_ttn_lorawan_v3_user_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetUserRequest); i { + switch v := v.(*Users); i { case 0: return &v.state case 1: @@ -3290,7 +3502,7 @@ func file_ttn_lorawan_v3_user_proto_init() { } } file_ttn_lorawan_v3_user_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListUsersRequest); i { + switch v := v.(*GetUserRequest); i { case 0: return &v.state case 1: @@ -3302,7 +3514,7 @@ func file_ttn_lorawan_v3_user_proto_init() { } } file_ttn_lorawan_v3_user_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CreateUserRequest); i { + switch v := v.(*ListUsersRequest); i { case 0: return &v.state case 1: @@ -3314,7 +3526,7 @@ func file_ttn_lorawan_v3_user_proto_init() { } } file_ttn_lorawan_v3_user_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*UpdateUserRequest); i { + switch v := v.(*CreateUserRequest); i { case 0: return &v.state case 1: @@ -3326,7 +3538,7 @@ func file_ttn_lorawan_v3_user_proto_init() { } } file_ttn_lorawan_v3_user_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CreateTemporaryPasswordRequest); i { + switch v := v.(*UpdateUserRequest); i { case 0: return &v.state case 1: @@ -3338,7 +3550,7 @@ func file_ttn_lorawan_v3_user_proto_init() { } } file_ttn_lorawan_v3_user_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*UpdateUserPasswordRequest); i { + switch v := v.(*CreateTemporaryPasswordRequest); i { case 0: return &v.state case 1: @@ -3350,7 +3562,7 @@ func file_ttn_lorawan_v3_user_proto_init() { } } file_ttn_lorawan_v3_user_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListUserAPIKeysRequest); i { + switch v := v.(*UpdateUserPasswordRequest); i { case 0: return &v.state case 1: @@ -3362,7 +3574,7 @@ func file_ttn_lorawan_v3_user_proto_init() { } } file_ttn_lorawan_v3_user_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetUserAPIKeyRequest); i { + switch v := v.(*ListUserAPIKeysRequest); i { case 0: return &v.state case 1: @@ -3374,7 +3586,7 @@ func file_ttn_lorawan_v3_user_proto_init() { } } file_ttn_lorawan_v3_user_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CreateUserAPIKeyRequest); i { + switch v := v.(*GetUserAPIKeyRequest); i { case 0: return &v.state case 1: @@ -3386,7 +3598,7 @@ func file_ttn_lorawan_v3_user_proto_init() { } } file_ttn_lorawan_v3_user_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*UpdateUserAPIKeyRequest); i { + switch v := v.(*CreateUserAPIKeyRequest); i { case 0: return &v.state case 1: @@ -3398,7 +3610,7 @@ func file_ttn_lorawan_v3_user_proto_init() { } } file_ttn_lorawan_v3_user_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DeleteUserAPIKeyRequest); i { + switch v := v.(*UpdateUserAPIKeyRequest); i { case 0: return &v.state case 1: @@ -3410,7 +3622,7 @@ func file_ttn_lorawan_v3_user_proto_init() { } } file_ttn_lorawan_v3_user_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Invitation); i { + switch v := v.(*DeleteUserAPIKeyRequest); i { case 0: return &v.state case 1: @@ -3422,7 +3634,7 @@ func file_ttn_lorawan_v3_user_proto_init() { } } file_ttn_lorawan_v3_user_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListInvitationsRequest); i { + switch v := v.(*Invitation); i { case 0: return &v.state case 1: @@ -3434,7 +3646,7 @@ func file_ttn_lorawan_v3_user_proto_init() { } } file_ttn_lorawan_v3_user_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Invitations); i { + switch v := v.(*ListInvitationsRequest); i { case 0: return &v.state case 1: @@ -3446,7 +3658,7 @@ func file_ttn_lorawan_v3_user_proto_init() { } } file_ttn_lorawan_v3_user_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SendInvitationRequest); i { + switch v := v.(*Invitations); i { case 0: return &v.state case 1: @@ -3458,7 +3670,7 @@ func file_ttn_lorawan_v3_user_proto_init() { } } file_ttn_lorawan_v3_user_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DeleteInvitationRequest); i { + switch v := v.(*SendInvitationRequest); i { case 0: return &v.state case 1: @@ -3470,7 +3682,7 @@ func file_ttn_lorawan_v3_user_proto_init() { } } file_ttn_lorawan_v3_user_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*UserSessionIdentifiers); i { + switch v := v.(*DeleteInvitationRequest); i { case 0: return &v.state case 1: @@ -3482,7 +3694,7 @@ func file_ttn_lorawan_v3_user_proto_init() { } } file_ttn_lorawan_v3_user_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*UserSession); i { + switch v := v.(*UserSessionIdentifiers); i { case 0: return &v.state case 1: @@ -3494,7 +3706,7 @@ func file_ttn_lorawan_v3_user_proto_init() { } } file_ttn_lorawan_v3_user_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*UserSessions); i { + switch v := v.(*UserSession); i { case 0: return &v.state case 1: @@ -3506,7 +3718,7 @@ func file_ttn_lorawan_v3_user_proto_init() { } } file_ttn_lorawan_v3_user_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListUserSessionsRequest); i { + switch v := v.(*UserSessions); i { case 0: return &v.state case 1: @@ -3518,7 +3730,7 @@ func file_ttn_lorawan_v3_user_proto_init() { } } file_ttn_lorawan_v3_user_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*LoginToken); i { + switch v := v.(*ListUserSessionsRequest); i { case 0: return &v.state case 1: @@ -3530,7 +3742,7 @@ func file_ttn_lorawan_v3_user_proto_init() { } } file_ttn_lorawan_v3_user_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CreateLoginTokenRequest); i { + switch v := v.(*LoginToken); i { case 0: return &v.state case 1: @@ -3542,7 +3754,7 @@ func file_ttn_lorawan_v3_user_proto_init() { } } file_ttn_lorawan_v3_user_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CreateLoginTokenResponse); i { + switch v := v.(*CreateLoginTokenRequest); i { case 0: return &v.state case 1: @@ -3554,7 +3766,7 @@ func file_ttn_lorawan_v3_user_proto_init() { } } file_ttn_lorawan_v3_user_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*UserBookmark); i { + switch v := v.(*CreateLoginTokenResponse); i { case 0: return &v.state case 1: @@ -3566,7 +3778,7 @@ func file_ttn_lorawan_v3_user_proto_init() { } } file_ttn_lorawan_v3_user_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*UserBookmarks); i { + switch v := v.(*UserBookmark); i { case 0: return &v.state case 1: @@ -3578,7 +3790,7 @@ func file_ttn_lorawan_v3_user_proto_init() { } } file_ttn_lorawan_v3_user_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CreateUserBookmarkRequest); i { + switch v := v.(*UserBookmarks); i { case 0: return &v.state case 1: @@ -3590,7 +3802,7 @@ func file_ttn_lorawan_v3_user_proto_init() { } } file_ttn_lorawan_v3_user_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListUserBookmarksRequest); i { + switch v := v.(*CreateUserBookmarkRequest); i { case 0: return &v.state case 1: @@ -3602,7 +3814,7 @@ func file_ttn_lorawan_v3_user_proto_init() { } } file_ttn_lorawan_v3_user_proto_msgTypes[30].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DeleteUserBookmarkRequest); i { + switch v := v.(*ListUserBookmarksRequest); i { case 0: return &v.state case 1: @@ -3614,7 +3826,7 @@ func file_ttn_lorawan_v3_user_proto_init() { } } file_ttn_lorawan_v3_user_proto_msgTypes[31].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*BatchDeleteUserBookmarksRequest); i { + switch v := v.(*DeleteUserBookmarkRequest); i { case 0: return &v.state case 1: @@ -3626,7 +3838,7 @@ func file_ttn_lorawan_v3_user_proto_init() { } } file_ttn_lorawan_v3_user_proto_msgTypes[32].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*UserConsolePreferences_DashboardLayouts); i { + switch v := v.(*BatchDeleteUserBookmarksRequest); i { case 0: return &v.state case 1: @@ -3638,6 +3850,18 @@ func file_ttn_lorawan_v3_user_proto_init() { } } file_ttn_lorawan_v3_user_proto_msgTypes[33].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UserConsolePreferences_DashboardLayouts); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_ttn_lorawan_v3_user_proto_msgTypes[34].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*UserConsolePreferences_SortBy); i { case 0: return &v.state @@ -3649,14 +3873,26 @@ func file_ttn_lorawan_v3_user_proto_init() { return nil } } + file_ttn_lorawan_v3_user_proto_msgTypes[35].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UserConsolePreferences_Tutorials); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_ttn_lorawan_v3_user_proto_rawDesc, - NumEnums: 2, - NumMessages: 35, + NumEnums: 3, + NumMessages: 37, NumExtensions: 0, NumServices: 0, }, diff --git a/pkg/ttnpb/user.pb.paths.fm.go b/pkg/ttnpb/user.pb.paths.fm.go index 0d89b5d779..a0b62f317a 100644 --- a/pkg/ttnpb/user.pb.paths.fm.go +++ b/pkg/ttnpb/user.pb.paths.fm.go @@ -2,6 +2,13 @@ package ttnpb +var EmailNotificationPreferencesFieldPathsNested = []string{ + "types", +} + +var EmailNotificationPreferencesFieldPathsTopLevel = []string{ + "types", +} var UserConsolePreferencesFieldPathsNested = []string{ "console_theme", "dashboard_layouts", @@ -21,12 +28,15 @@ var UserConsolePreferencesFieldPathsNested = []string{ "sort_by.gateway", "sort_by.organization", "sort_by.user", + "tutorials", + "tutorials.seen", } var UserConsolePreferencesFieldPathsTopLevel = []string{ "console_theme", "dashboard_layouts", "sort_by", + "tutorials", } var UserFieldPathsNested = []string{ "admin", @@ -50,10 +60,14 @@ var UserFieldPathsNested = []string{ "console_preferences.sort_by.gateway", "console_preferences.sort_by.organization", "console_preferences.sort_by.user", + "console_preferences.tutorials", + "console_preferences.tutorials.seen", "contact_info", "created_at", "deleted_at", "description", + "email_notification_preferences", + "email_notification_preferences.types", "ids", "ids.email", "ids.user_id", @@ -73,6 +87,7 @@ var UserFieldPathsNested = []string{ "temporary_password", "temporary_password_created_at", "temporary_password_expires_at", + "universal_rights", "updated_at", } @@ -84,6 +99,7 @@ var UserFieldPathsTopLevel = []string{ "created_at", "deleted_at", "description", + "email_notification_preferences", "ids", "name", "password", @@ -97,6 +113,7 @@ var UserFieldPathsTopLevel = []string{ "temporary_password", "temporary_password_created_at", "temporary_password_expires_at", + "universal_rights", "updated_at", } var UsersFieldPathsNested = []string{ @@ -156,10 +173,14 @@ var CreateUserRequestFieldPathsNested = []string{ "user.console_preferences.sort_by.gateway", "user.console_preferences.sort_by.organization", "user.console_preferences.sort_by.user", + "user.console_preferences.tutorials", + "user.console_preferences.tutorials.seen", "user.contact_info", "user.created_at", "user.deleted_at", "user.description", + "user.email_notification_preferences", + "user.email_notification_preferences.types", "user.ids", "user.ids.email", "user.ids.user_id", @@ -179,6 +200,7 @@ var CreateUserRequestFieldPathsNested = []string{ "user.temporary_password", "user.temporary_password_created_at", "user.temporary_password_expires_at", + "user.universal_rights", "user.updated_at", } @@ -210,10 +232,14 @@ var UpdateUserRequestFieldPathsNested = []string{ "user.console_preferences.sort_by.gateway", "user.console_preferences.sort_by.organization", "user.console_preferences.sort_by.user", + "user.console_preferences.tutorials", + "user.console_preferences.tutorials.seen", "user.contact_info", "user.created_at", "user.deleted_at", "user.description", + "user.email_notification_preferences", + "user.email_notification_preferences.types", "user.ids", "user.ids.email", "user.ids.user_id", @@ -233,6 +259,7 @@ var UpdateUserRequestFieldPathsNested = []string{ "user.temporary_password", "user.temporary_password_created_at", "user.temporary_password_expires_at", + "user.universal_rights", "user.updated_at", } @@ -648,3 +675,10 @@ var UserConsolePreferences_SortByFieldPathsTopLevel = []string{ "organization", "user", } +var UserConsolePreferences_TutorialsFieldPathsNested = []string{ + "seen", +} + +var UserConsolePreferences_TutorialsFieldPathsTopLevel = []string{ + "seen", +} diff --git a/pkg/ttnpb/user.pb.setters.fm.go b/pkg/ttnpb/user.pb.setters.fm.go index 92172ffea4..a367421b77 100644 --- a/pkg/ttnpb/user.pb.setters.fm.go +++ b/pkg/ttnpb/user.pb.setters.fm.go @@ -4,6 +4,26 @@ package ttnpb import fmt "fmt" +func (dst *EmailNotificationPreferences) SetFields(src *EmailNotificationPreferences, paths ...string) error { + for name, subs := range _processPaths(paths) { + switch name { + case "types": + if len(subs) > 0 { + return fmt.Errorf("'types' has no subfields, but %s were specified", subs) + } + if src != nil { + dst.Types = src.Types + } else { + dst.Types = nil + } + + default: + return fmt.Errorf("invalid field: '%s'", name) + } + } + return nil +} + func (dst *UserConsolePreferences) SetFields(src *UserConsolePreferences, paths ...string) error { for name, subs := range _processPaths(paths) { switch name { @@ -66,6 +86,31 @@ func (dst *UserConsolePreferences) SetFields(src *UserConsolePreferences, paths dst.SortBy = nil } } + case "tutorials": + if len(subs) > 0 { + var newDst, newSrc *UserConsolePreferences_Tutorials + if (src == nil || src.Tutorials == nil) && dst.Tutorials == nil { + continue + } + if src != nil { + newSrc = src.Tutorials + } + if dst.Tutorials != nil { + newDst = dst.Tutorials + } else { + newDst = &UserConsolePreferences_Tutorials{} + dst.Tutorials = newDst + } + if err := newDst.SetFields(newSrc, subs...); err != nil { + return err + } + } else { + if src != nil { + dst.Tutorials = src.Tutorials + } else { + dst.Tutorials = nil + } + } default: return fmt.Errorf("invalid field: '%s'", name) @@ -322,6 +367,40 @@ func (dst *User) SetFields(src *User, paths ...string) error { dst.ConsolePreferences = nil } } + case "email_notification_preferences": + if len(subs) > 0 { + var newDst, newSrc *EmailNotificationPreferences + if (src == nil || src.EmailNotificationPreferences == nil) && dst.EmailNotificationPreferences == nil { + continue + } + if src != nil { + newSrc = src.EmailNotificationPreferences + } + if dst.EmailNotificationPreferences != nil { + newDst = dst.EmailNotificationPreferences + } else { + newDst = &EmailNotificationPreferences{} + dst.EmailNotificationPreferences = newDst + } + if err := newDst.SetFields(newSrc, subs...); err != nil { + return err + } + } else { + if src != nil { + dst.EmailNotificationPreferences = src.EmailNotificationPreferences + } else { + dst.EmailNotificationPreferences = nil + } + } + case "universal_rights": + if len(subs) > 0 { + return fmt.Errorf("'universal_rights' has no subfields, but %s were specified", subs) + } + if src != nil { + dst.UniversalRights = src.UniversalRights + } else { + dst.UniversalRights = nil + } default: return fmt.Errorf("invalid field: '%s'", name) @@ -1995,3 +2074,23 @@ func (dst *UserConsolePreferences_SortBy) SetFields(src *UserConsolePreferences_ } return nil } + +func (dst *UserConsolePreferences_Tutorials) SetFields(src *UserConsolePreferences_Tutorials, paths ...string) error { + for name, subs := range _processPaths(paths) { + switch name { + case "seen": + if len(subs) > 0 { + return fmt.Errorf("'seen' has no subfields, but %s were specified", subs) + } + if src != nil { + dst.Seen = src.Seen + } else { + dst.Seen = nil + } + + default: + return fmt.Errorf("invalid field: '%s'", name) + } + } + return nil +} diff --git a/pkg/ttnpb/user.pb.validate.go b/pkg/ttnpb/user.pb.validate.go index 4e02d4d74d..7d7ca8d3e1 100644 --- a/pkg/ttnpb/user.pb.validate.go +++ b/pkg/ttnpb/user.pb.validate.go @@ -32,6 +32,113 @@ var ( _ = anypb.Any{} ) +// ValidateFields checks the field values on EmailNotificationPreferences with +// the rules defined in the proto definition for this message. If any rules +// are violated, an error is returned. +func (m *EmailNotificationPreferences) ValidateFields(paths ...string) error { + if m == nil { + return nil + } + + if len(paths) == 0 { + paths = EmailNotificationPreferencesFieldPathsNested + } + + for name, subs := range _processPaths(append(paths[:0:0], paths...)) { + _ = subs + switch name { + case "types": + + _EmailNotificationPreferences_Types_Unique := make(map[NotificationType]struct{}, len(m.GetTypes())) + + for idx, item := range m.GetTypes() { + _, _ = idx, item + + if _, exists := _EmailNotificationPreferences_Types_Unique[item]; exists { + return EmailNotificationPreferencesValidationError{ + field: fmt.Sprintf("types[%v]", idx), + reason: "repeated value must contain unique items", + } + } else { + _EmailNotificationPreferences_Types_Unique[item] = struct{}{} + } + + if _, ok := NotificationType_name[int32(item)]; !ok { + return EmailNotificationPreferencesValidationError{ + field: fmt.Sprintf("types[%v]", idx), + reason: "value must be one of the defined enum values", + } + } + + } + + default: + return EmailNotificationPreferencesValidationError{ + field: name, + reason: "invalid field path", + } + } + } + return nil +} + +// EmailNotificationPreferencesValidationError is the validation error returned +// by EmailNotificationPreferences.ValidateFields if the designated +// constraints aren't met. +type EmailNotificationPreferencesValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e EmailNotificationPreferencesValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e EmailNotificationPreferencesValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e EmailNotificationPreferencesValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e EmailNotificationPreferencesValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e EmailNotificationPreferencesValidationError) ErrorName() string { + return "EmailNotificationPreferencesValidationError" +} + +// Error satisfies the builtin error interface +func (e EmailNotificationPreferencesValidationError) Error() string { + cause := "" + if e.cause != nil { + cause = fmt.Sprintf(" | caused by: %v", e.cause) + } + + key := "" + if e.key { + key = "key for " + } + + return fmt.Sprintf( + "invalid %sEmailNotificationPreferences.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = EmailNotificationPreferencesValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = EmailNotificationPreferencesValidationError{} + // ValidateFields checks the field values on UserConsolePreferences with the // rules defined in the proto definition for this message. If any rules are // violated, an error is returned. @@ -73,6 +180,18 @@ func (m *UserConsolePreferences) ValidateFields(paths ...string) error { } } + case "tutorials": + + if v, ok := interface{}(m.GetTutorials()).(interface{ ValidateFields(...string) error }); ok { + if err := v.ValidateFields(subs...); err != nil { + return UserConsolePreferencesValidationError{ + field: "tutorials", + reason: "embedded message failed validation", + cause: err, + } + } + } + default: return UserConsolePreferencesValidationError{ field: name, @@ -407,6 +526,20 @@ func (m *User) ValidateFields(paths ...string) error { } } + case "email_notification_preferences": + + if v, ok := interface{}(m.GetEmailNotificationPreferences()).(interface{ ValidateFields(...string) error }); ok { + if err := v.ValidateFields(subs...); err != nil { + return UserValidationError{ + field: "email_notification_preferences", + reason: "embedded message failed validation", + cause: err, + } + } + } + + case "universal_rights": + default: return UserValidationError{ field: name, @@ -4483,3 +4616,110 @@ var _UserConsolePreferences_SortBy_User_InLookup = map[string]struct{}{ "created_at": {}, "-created_at": {}, } + +// ValidateFields checks the field values on UserConsolePreferences_Tutorials +// with the rules defined in the proto definition for this message. If any +// rules are violated, an error is returned. +func (m *UserConsolePreferences_Tutorials) ValidateFields(paths ...string) error { + if m == nil { + return nil + } + + if len(paths) == 0 { + paths = UserConsolePreferences_TutorialsFieldPathsNested + } + + for name, subs := range _processPaths(append(paths[:0:0], paths...)) { + _ = subs + switch name { + case "seen": + + _UserConsolePreferences_Tutorials_Seen_Unique := make(map[Tutorial]struct{}, len(m.GetSeen())) + + for idx, item := range m.GetSeen() { + _, _ = idx, item + + if _, exists := _UserConsolePreferences_Tutorials_Seen_Unique[item]; exists { + return UserConsolePreferences_TutorialsValidationError{ + field: fmt.Sprintf("seen[%v]", idx), + reason: "repeated value must contain unique items", + } + } else { + _UserConsolePreferences_Tutorials_Seen_Unique[item] = struct{}{} + } + + if _, ok := Tutorial_name[int32(item)]; !ok { + return UserConsolePreferences_TutorialsValidationError{ + field: fmt.Sprintf("seen[%v]", idx), + reason: "value must be one of the defined enum values", + } + } + + } + + default: + return UserConsolePreferences_TutorialsValidationError{ + field: name, + reason: "invalid field path", + } + } + } + return nil +} + +// UserConsolePreferences_TutorialsValidationError is the validation error +// returned by UserConsolePreferences_Tutorials.ValidateFields if the +// designated constraints aren't met. +type UserConsolePreferences_TutorialsValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e UserConsolePreferences_TutorialsValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e UserConsolePreferences_TutorialsValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e UserConsolePreferences_TutorialsValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e UserConsolePreferences_TutorialsValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e UserConsolePreferences_TutorialsValidationError) ErrorName() string { + return "UserConsolePreferences_TutorialsValidationError" +} + +// Error satisfies the builtin error interface +func (e UserConsolePreferences_TutorialsValidationError) Error() string { + cause := "" + if e.cause != nil { + cause = fmt.Sprintf(" | caused by: %v", e.cause) + } + + key := "" + if e.key { + key = "key for " + } + + return fmt.Sprintf( + "invalid %sUserConsolePreferences_Tutorials.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = UserConsolePreferences_TutorialsValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = UserConsolePreferences_TutorialsValidationError{} diff --git a/pkg/ttnpb/user_flags.pb.go b/pkg/ttnpb/user_flags.pb.go index c1f35bb517..dde3aa9ce5 100644 --- a/pkg/ttnpb/user_flags.pb.go +++ b/pkg/ttnpb/user_flags.pb.go @@ -19,6 +19,8 @@ func AddSelectFlagsForUserConsolePreferences(flags *pflag.FlagSet, prefix string // NOTE: dashboard_layouts (UserConsolePreferences_DashboardLayouts) does not seem to have select flags. flags.AddFlag(flagsplugin.NewBoolFlag(flagsplugin.Prefix("sort-by", prefix), flagsplugin.SelectDesc(flagsplugin.Prefix("sort-by", prefix), true), flagsplugin.WithHidden(hidden))) // NOTE: sort_by (UserConsolePreferences_SortBy) does not seem to have select flags. + flags.AddFlag(flagsplugin.NewBoolFlag(flagsplugin.Prefix("tutorials", prefix), flagsplugin.SelectDesc(flagsplugin.Prefix("tutorials", prefix), true), flagsplugin.WithHidden(hidden))) + // NOTE: tutorials (UserConsolePreferences_Tutorials) does not seem to have select flags. } // SelectFromFlags outputs the fieldmask paths forUserConsolePreferences message from select flags. @@ -40,6 +42,12 @@ func PathsFromSelectFlagsForUserConsolePreferences(flags *pflag.FlagSet, prefix paths = append(paths, flagsplugin.Prefix("sort_by", prefix)) } // NOTE: sort_by (UserConsolePreferences_SortBy) does not seem to have select flags. + if val, selected, err := flagsplugin.GetBool(flags, flagsplugin.Prefix("tutorials", prefix)); err != nil { + return nil, err + } else if selected && val { + paths = append(paths, flagsplugin.Prefix("tutorials", prefix)) + } + // NOTE: tutorials (UserConsolePreferences_Tutorials) does not seem to have select flags. return paths, nil } @@ -48,6 +56,7 @@ func AddSetFlagsForUserConsolePreferences(flags *pflag.FlagSet, prefix string, h flags.AddFlag(flagsplugin.NewStringFlag(flagsplugin.Prefix("console-theme", prefix), flagsplugin.EnumValueDesc(ConsoleTheme_value), flagsplugin.WithHidden(hidden))) // FIXME: Skipping DashboardLayouts because it does not seem to implement AddSetFlags. // FIXME: Skipping SortBy because it does not seem to implement AddSetFlags. + // FIXME: Skipping Tutorials because it does not seem to implement AddSetFlags. } // SetFromFlags sets the UserConsolePreferences message from flags. @@ -64,6 +73,7 @@ func (m *UserConsolePreferences) SetFromFlags(flags *pflag.FlagSet, prefix strin } // FIXME: Skipping DashboardLayouts because it does not seem to implement AddSetFlags. // FIXME: Skipping SortBy because it does not seem to implement AddSetFlags. + // FIXME: Skipping Tutorials because it does not seem to implement AddSetFlags. return paths, nil } @@ -89,6 +99,9 @@ func AddSelectFlagsForUser(flags *pflag.FlagSet, prefix string, hidden bool) { // NOTE: profile_picture (Picture) does not seem to have select flags. flags.AddFlag(flagsplugin.NewBoolFlag(flagsplugin.Prefix("console-preferences", prefix), flagsplugin.SelectDesc(flagsplugin.Prefix("console-preferences", prefix), true), flagsplugin.WithHidden(hidden))) AddSelectFlagsForUserConsolePreferences(flags, flagsplugin.Prefix("console-preferences", prefix), hidden) + flags.AddFlag(flagsplugin.NewBoolFlag(flagsplugin.Prefix("email-notification-preferences", prefix), flagsplugin.SelectDesc(flagsplugin.Prefix("email-notification-preferences", prefix), true), flagsplugin.WithHidden(hidden))) + // NOTE: email_notification_preferences (EmailNotificationPreferences) does not seem to have select flags. + flags.AddFlag(flagsplugin.NewBoolFlag(flagsplugin.Prefix("universal-rights", prefix), flagsplugin.SelectDesc(flagsplugin.Prefix("universal-rights", prefix), false), flagsplugin.WithHidden(hidden))) } // SelectFromFlags outputs the fieldmask paths forUser message from select flags. @@ -189,6 +202,17 @@ func PathsFromSelectFlagsForUser(flags *pflag.FlagSet, prefix string) (paths []s } else { paths = append(paths, selectPaths...) } + if val, selected, err := flagsplugin.GetBool(flags, flagsplugin.Prefix("email_notification_preferences", prefix)); err != nil { + return nil, err + } else if selected && val { + paths = append(paths, flagsplugin.Prefix("email_notification_preferences", prefix)) + } + // NOTE: email_notification_preferences (EmailNotificationPreferences) does not seem to have select flags. + if val, selected, err := flagsplugin.GetBool(flags, flagsplugin.Prefix("universal_rights", prefix)); err != nil { + return nil, err + } else if selected && val { + paths = append(paths, flagsplugin.Prefix("universal_rights", prefix)) + } return paths, nil } @@ -209,6 +233,8 @@ func AddSetFlagsForUser(flags *pflag.FlagSet, prefix string, hidden bool) { flags.AddFlag(flagsplugin.NewStringFlag(flagsplugin.Prefix("temporary-password", prefix), "", flagsplugin.WithHidden(hidden))) // FIXME: Skipping ProfilePicture because it does not seem to implement AddSetFlags. AddSetFlagsForUserConsolePreferences(flags, flagsplugin.Prefix("console-preferences", prefix), hidden) + // FIXME: Skipping EmailNotificationPreferences because it does not seem to implement AddSetFlags. + flags.AddFlag(flagsplugin.NewStringSliceFlag(flagsplugin.Prefix("universal-rights", prefix), flagsplugin.EnumValueDesc(Right_value), flagsplugin.WithHidden(hidden))) } // SetFromFlags sets the User message from flags. @@ -305,6 +331,20 @@ func (m *User) SetFromFlags(flags *pflag.FlagSet, prefix string) (paths []string paths = append(paths, setPaths...) } } + // FIXME: Skipping EmailNotificationPreferences because it does not seem to implement AddSetFlags. + if val, changed, err := flagsplugin.GetStringSlice(flags, flagsplugin.Prefix("universal_rights", prefix)); err != nil { + return nil, err + } else if changed { + m.UniversalRights = make([]Right, len(val)) + for i, v := range val { + enumValue, err := flagsplugin.SetEnumString(v, Right_value) + if err != nil { + return nil, err + } + m.UniversalRights[i] = Right(enumValue) + } + paths = append(paths, flagsplugin.Prefix("universal_rights", prefix)) + } return paths, nil } diff --git a/pkg/ttnpb/user_json.pb.go b/pkg/ttnpb/user_json.pb.go index 7bbe2a28b3..eca5eac75c 100644 --- a/pkg/ttnpb/user_json.pb.go +++ b/pkg/ttnpb/user_json.pb.go @@ -58,6 +58,46 @@ func (x *ConsoleTheme) UnmarshalJSON(b []byte) error { return jsonplugin.DefaultUnmarshalerConfig.Unmarshal(b, x) } +// MarshalProtoJSON marshals the Tutorial to JSON. +func (x Tutorial) MarshalProtoJSON(s *jsonplugin.MarshalState) { + s.WriteEnumString(int32(x), Tutorial_name) +} + +// MarshalText marshals the Tutorial to text. +func (x Tutorial) MarshalText() ([]byte, error) { + return []byte(jsonplugin.GetEnumString(int32(x), Tutorial_name)), nil +} + +// MarshalJSON marshals the Tutorial to JSON. +func (x Tutorial) MarshalJSON() ([]byte, error) { + return jsonplugin.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the Tutorial from JSON. +func (x *Tutorial) UnmarshalProtoJSON(s *jsonplugin.UnmarshalState) { + v := s.ReadEnum(Tutorial_value) + if err := s.Err(); err != nil { + s.SetErrorf("could not read Tutorial enum: %v", err) + return + } + *x = Tutorial(v) +} + +// UnmarshalText unmarshals the Tutorial from text. +func (x *Tutorial) UnmarshalText(b []byte) error { + i, err := jsonplugin.ParseEnumString(string(b), Tutorial_value) + if err != nil { + return err + } + *x = Tutorial(i) + return nil +} + +// UnmarshalJSON unmarshals the Tutorial from JSON. +func (x *Tutorial) UnmarshalJSON(b []byte) error { + return jsonplugin.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + // MarshalProtoJSON marshals the DashboardLayout to JSON. func (x DashboardLayout) MarshalProtoJSON(s *jsonplugin.MarshalState) { s.WriteEnumString(int32(x), DashboardLayout_name) @@ -105,6 +145,62 @@ func (x *DashboardLayout) UnmarshalJSON(b []byte) error { return jsonplugin.DefaultUnmarshalerConfig.Unmarshal(b, x) } +// MarshalProtoJSON marshals the EmailNotificationPreferences message to JSON. +func (x *EmailNotificationPreferences) MarshalProtoJSON(s *jsonplugin.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if len(x.Types) > 0 || s.HasField("types") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("types") + s.WriteArrayStart() + var wroteElement bool + for _, element := range x.Types { + s.WriteMoreIf(&wroteElement) + element.MarshalProtoJSON(s) + } + s.WriteArrayEnd() + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the EmailNotificationPreferences to JSON. +func (x *EmailNotificationPreferences) MarshalJSON() ([]byte, error) { + return jsonplugin.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the EmailNotificationPreferences message from JSON. +func (x *EmailNotificationPreferences) UnmarshalProtoJSON(s *jsonplugin.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.ReadAny() // ignore unknown field + case "types": + s.AddField("types") + if s.ReadNil() { + x.Types = nil + return + } + s.ReadArray(func() { + var v NotificationType + v.UnmarshalProtoJSON(s) + x.Types = append(x.Types, v) + }) + } + }) +} + +// UnmarshalJSON unmarshals the EmailNotificationPreferences from JSON. +func (x *EmailNotificationPreferences) UnmarshalJSON(b []byte) error { + return jsonplugin.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + // MarshalProtoJSON marshals the UserConsolePreferences_DashboardLayouts message to JSON. func (x *UserConsolePreferences_DashboardLayouts) MarshalProtoJSON(s *jsonplugin.MarshalState) { if x == nil { @@ -203,6 +299,62 @@ func (x *UserConsolePreferences_DashboardLayouts) UnmarshalJSON(b []byte) error return jsonplugin.DefaultUnmarshalerConfig.Unmarshal(b, x) } +// MarshalProtoJSON marshals the UserConsolePreferences_Tutorials message to JSON. +func (x *UserConsolePreferences_Tutorials) MarshalProtoJSON(s *jsonplugin.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if len(x.Seen) > 0 || s.HasField("seen") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("seen") + s.WriteArrayStart() + var wroteElement bool + for _, element := range x.Seen { + s.WriteMoreIf(&wroteElement) + element.MarshalProtoJSON(s) + } + s.WriteArrayEnd() + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the UserConsolePreferences_Tutorials to JSON. +func (x *UserConsolePreferences_Tutorials) MarshalJSON() ([]byte, error) { + return jsonplugin.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the UserConsolePreferences_Tutorials message from JSON. +func (x *UserConsolePreferences_Tutorials) UnmarshalProtoJSON(s *jsonplugin.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.ReadAny() // ignore unknown field + case "seen": + s.AddField("seen") + if s.ReadNil() { + x.Seen = nil + return + } + s.ReadArray(func() { + var v Tutorial + v.UnmarshalProtoJSON(s) + x.Seen = append(x.Seen, v) + }) + } + }) +} + +// UnmarshalJSON unmarshals the UserConsolePreferences_Tutorials from JSON. +func (x *UserConsolePreferences_Tutorials) UnmarshalJSON(b []byte) error { + return jsonplugin.DefaultUnmarshalerConfig.Unmarshal(b, x) +} + // MarshalProtoJSON marshals the UserConsolePreferences message to JSON. func (x *UserConsolePreferences) MarshalProtoJSON(s *jsonplugin.MarshalState) { if x == nil { @@ -227,6 +379,11 @@ func (x *UserConsolePreferences) MarshalProtoJSON(s *jsonplugin.MarshalState) { // NOTE: UserConsolePreferences_SortBy does not seem to implement MarshalProtoJSON. golang.MarshalMessage(s, x.SortBy) } + if x.Tutorials != nil || s.HasField("tutorials") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("tutorials") + x.Tutorials.MarshalProtoJSON(s.WithField("tutorials")) + } s.WriteObjectEnd() } @@ -264,6 +421,13 @@ func (x *UserConsolePreferences) UnmarshalProtoJSON(s *jsonplugin.UnmarshalState var v UserConsolePreferences_SortBy golang.UnmarshalMessage(s, &v) x.SortBy = &v + case "tutorials": + if s.ReadNil() { + x.Tutorials = nil + return + } + x.Tutorials = &UserConsolePreferences_Tutorials{} + x.Tutorials.UnmarshalProtoJSON(s.WithField("tutorials", true)) } }) } @@ -429,6 +593,22 @@ func (x *User) MarshalProtoJSON(s *jsonplugin.MarshalState) { s.WriteObjectField("console_preferences") x.ConsolePreferences.MarshalProtoJSON(s.WithField("console_preferences")) } + if x.EmailNotificationPreferences != nil || s.HasField("email_notification_preferences") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("email_notification_preferences") + x.EmailNotificationPreferences.MarshalProtoJSON(s.WithField("email_notification_preferences")) + } + if len(x.UniversalRights) > 0 || s.HasField("universal_rights") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("universal_rights") + s.WriteArrayStart() + var wroteElement bool + for _, element := range x.UniversalRights { + s.WriteMoreIf(&wroteElement) + element.MarshalProtoJSON(s) + } + s.WriteArrayEnd() + } s.WriteObjectEnd() } @@ -605,6 +785,24 @@ func (x *User) UnmarshalProtoJSON(s *jsonplugin.UnmarshalState) { } x.ConsolePreferences = &UserConsolePreferences{} x.ConsolePreferences.UnmarshalProtoJSON(s.WithField("console_preferences", true)) + case "email_notification_preferences", "emailNotificationPreferences": + if s.ReadNil() { + x.EmailNotificationPreferences = nil + return + } + x.EmailNotificationPreferences = &EmailNotificationPreferences{} + x.EmailNotificationPreferences.UnmarshalProtoJSON(s.WithField("email_notification_preferences", true)) + case "universal_rights", "universalRights": + s.AddField("universal_rights") + if s.ReadNil() { + x.UniversalRights = nil + return + } + s.ReadArray(func() { + var v Right + v.UnmarshalProtoJSON(s) + x.UniversalRights = append(x.UniversalRights, v) + }) } }) } diff --git a/pkg/webui/components/button/button.styl b/pkg/webui/components/button/button.styl index 969620f0ee..f5057163d9 100644 --- a/pkg/webui/components/button/button.styl +++ b/pkg/webui/components/button/button.styl @@ -19,10 +19,10 @@ // where the button is used as a container. reset-button() - &.primary, &.secondary, &.naked + &.primary, &.secondary, &.tertiary, &.naked position: relative display: inline-flex - transition: 80ms background ease-in-out, 80ms color ease-in-out, 80ms border-color ease-in-out, 80ms box-shadow ease-in-out + transition: .1s background ease-in-out, .1s color ease-in-out, .1s border-color ease-in-out, .1s box-shadow ease-in-out border-radius: $br.m outline: 0 cursor: pointer @@ -31,6 +31,7 @@ gap: $cs.xxs height: $default-input-height text-decoration: none + font-weight: $fw.bold padding: 0 $cs.m white-space: nowrap box-sizing: border-box @@ -72,7 +73,6 @@ &.primary:not(.naked) color: var(--c-text-neutral-min) - background-color: var(--c-text-brand-normal) &:disabled @@ -123,19 +123,17 @@ &.secondary color: var(--c-text-neutral-heavy) background-color: var(--c-bg-neutral-min) - box-shadow: 0px 2px 2px 0px rgba(0 0 0 4.5%), inset 0px 0px 0px 1px var(--c-bg-neutral-min) - gradient-border(var(--c-bg-neutral-min), var(--c-gradient-neutral-light-01), var(--c-gradient-neutral-light-02)) - border: 1px solid transparent + border: solid 1px var(--c-border-neutral-light) &:not(:disabled) &:hover - gradient-border(var(--c-bg-neutral-extralight), var(--c-gradient-neutral-light-01), var(--c-gradient-neutral-light-02)) + background-color: var(--c-bg-neutral-light) +focus-visible() - gradient-border(var(--c-bg-neutral-extralight), var(--c-gradient-neutral-light-01), var(--c-gradient-neutral-light-02)) + gradient-border(var(--c-bg-neutral-light), var(--c-gradient-neutral-light-01), var(--c-gradient-neutral-light-02)) &:active - gradient-border(var(--c-bg-neutral-extralight), var(--c-gradient-neutral-light-01), var(--c-gradient-neutral-light-02)) + gradient-border(var(--c-bg-neutral-light), var(--c-gradient-neutral-light-01), var(--c-gradient-neutral-light-02)) box-shadow: none &.warning @@ -182,6 +180,58 @@ width: 1.2rem height: 1.2rem + &.tertiary + color: var(--c-text-brand-normal) + background-color: var(--c-bg-brand-light) + + &:disabled + color: var(--c-text-neutral-light) + background-color: var(--c-border-neutral-light) + + &:not(:disabled) + &:hover + background-color: var(--c-bg-brand-semilight) + + +focus-visible() + background-color: var(--c-bg-brand-light) + + &:active + background-color: var(--c-text-brand-light) + + &.warning + color: var(--c-text-warning-normal) + background-color: var(--c-bg-warning-light) + + &:not(:disabled) + &:hover + background-color: var(--c-bg-warning-semilight) + + +focus-visible() + background-color: var(--c-bg-warning-semilight) + + &:active + background-color: var(--c-bg-warning-ligth) + + &:disabled + background-color: var(--c-bg-warning-normal-disabled) + + &.danger + color: var(--c-text-danger-normal) + background-color: var(--c-bg-error-light) + + &:not(:disabled) + &:hover + background-color: var(--c-bg-error-semilight) + + +focus-visible() + background-color: var(--c-bg-error-semilight) + + &:active + background-color: var(--c-bg-error-normal-active) + + &:disabled + background-color: var(--c-bg-error-normal-disabled) + &.naked:not(.primary) color: var(--c-text-neutral-light) diff --git a/pkg/webui/components/button/index.js b/pkg/webui/components/button/index.js index 8ec86b9ec4..f3bf060874 100644 --- a/pkg/webui/components/button/index.js +++ b/pkg/webui/components/button/index.js @@ -38,6 +38,7 @@ const assembleClassnames = ({ warning, primary, secondary, + tertiary, naked, icon, small, @@ -54,6 +55,7 @@ const assembleClassnames = ({ [style.warning]: warning, [style.primary]: primary, [style.secondary]: secondary, + [style.tertiary]: tertiary, [style.naked]: naked, [style.busy]: busy, [style.small]: small, diff --git a/pkg/webui/components/checkbox/checkbox.js b/pkg/webui/components/checkbox/checkbox.js index a17a0d097c..634976ea28 100644 --- a/pkg/webui/components/checkbox/checkbox.js +++ b/pkg/webui/components/checkbox/checkbox.js @@ -104,22 +104,24 @@ const Checkbox = props => { return ( ) diff --git a/pkg/webui/components/code-editor/index.js b/pkg/webui/components/code-editor/index.js index 5358476ad6..ceda846a2f 100644 --- a/pkg/webui/components/code-editor/index.js +++ b/pkg/webui/components/code-editor/index.js @@ -126,6 +126,7 @@ const CodeEditor = ({ editorProps={{ $blockScrolling: Infinity }} commands={commands} ref={editorRef ? combineRefs([aceRef, editorRef]) : aceRef} + wrapEnabled /> ) diff --git a/pkg/webui/components/dropdown/index.js b/pkg/webui/components/dropdown/index.js index 57ed30216a..80990aa47c 100644 --- a/pkg/webui/components/dropdown/index.js +++ b/pkg/webui/components/dropdown/index.js @@ -124,7 +124,7 @@ const DropdownItem = ({ }) => { const ref = useRef() const iconElement = icon && - const ItemElement = action ? ( + const itemElement = action ? ( ) : external ? ( - {Boolean(iconElement) ? iconElement : null} + {iconElement} ) : ( - {Boolean(iconElement) ? iconElement : null} + {iconElement} ) @@ -152,7 +152,7 @@ const DropdownItem = ({ @@ -164,7 +164,7 @@ const DropdownItem = ({ return (
  • - {submenu || ItemElement} + {submenu || itemElement}
  • ) } diff --git a/pkg/webui/components/form/section/index.js b/pkg/webui/components/form/section/index.js index 879aabe88d..08e5b7d97f 100644 --- a/pkg/webui/components/form/section/index.js +++ b/pkg/webui/components/form/section/index.js @@ -16,7 +16,7 @@ import React from 'react' import classnames from 'classnames' import { defineMessages, useIntl } from 'react-intl' -import Icon from '@ttn-lw/components/icon' +import Icon, { IconChevronDown, IconChevronUp } from '@ttn-lw/components/icon' import Message from '@ttn-lw/lib/components/message' @@ -65,7 +65,7 @@ const FormCollapseSection = props => { disabled={disabled} > - +
    {children} diff --git a/pkg/webui/components/input/index.js b/pkg/webui/components/input/index.js index 1ce77644a7..eb7742f7de 100644 --- a/pkg/webui/components/input/index.js +++ b/pkg/webui/components/input/index.js @@ -16,7 +16,7 @@ import React, { useCallback, useImperativeHandle, useRef, useState } from 'react import { defineMessages, useIntl } from 'react-intl' import classnames from 'classnames' -import Icon from '@ttn-lw/components/icon' +import Icon, { IconEye, IconEyeOff } from '@ttn-lw/components/icon' import Spinner from '@ttn-lw/components/spinner' import Button from '@ttn-lw/components/button' import Tooltip from '@ttn-lw/components/tooltip' @@ -234,7 +234,7 @@ const Input = React.forwardRef((props, ref) => { small >
    diff --git a/pkg/webui/console/components/notifications/utils.js b/pkg/webui/console/components/notifications/utils.js index 0c896a4101..b7c1229f60 100644 --- a/pkg/webui/console/components/notifications/utils.js +++ b/pkg/webui/console/components/notifications/utils.js @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +import { ingestError } from '@ttn-lw/lib/errors/utils' + import notificationMap from './constants' export const getNotification = notificationType => notificationMap[notificationType] @@ -25,5 +27,16 @@ const idToEntityMap = { client_ids: 'client', } +export const validateNotification = (notificationType, ingestedBy) => { + if (!(notificationType in notificationMap)) { + ingestError(new Error(`Notification type "${notificationType}" does not exist`), { + ingestedBy, + }) + return false + } + + return true +} + export const getEntity = entity_ids => (entity_ids && idToEntityMap[Object.keys(entity_ids)[0]]) || 'entity' diff --git a/pkg/webui/console/components/routing-policy/sheet.js b/pkg/webui/console/components/routing-policy/sheet.js index 35c57922f1..510a309b2f 100644 --- a/pkg/webui/console/components/routing-policy/sheet.js +++ b/pkg/webui/console/components/routing-policy/sheet.js @@ -15,7 +15,7 @@ import React from 'react' import classnames from 'classnames' -import Icon from '@ttn-lw/components/icon' +import Icon, { IconCheck, IconX } from '@ttn-lw/components/icon' import Tooltip from '@ttn-lw/components/tooltip' import Message from '@ttn-lw/lib/components/message' @@ -29,12 +29,12 @@ import style from './routing-policy-sheet.styl' const RoutingPolicy = ({ enabled, message, positiveMessage, negativeMessage }) => ( }> - + diff --git a/pkg/webui/console/components/total-end-devices-upseller-panel/index.js b/pkg/webui/console/components/total-end-devices-upseller-panel/index.js index 22de5fcc67..109e147a36 100644 --- a/pkg/webui/console/components/total-end-devices-upseller-panel/index.js +++ b/pkg/webui/console/components/total-end-devices-upseller-panel/index.js @@ -13,7 +13,6 @@ // limitations under the License. import React from 'react' -import classnames from 'classnames' import { IconBolt, IconDevice } from '@ttn-lw/components/icon' import Panel from '@ttn-lw/components/panel' @@ -48,15 +47,11 @@ const TotalEndDevicesUpsellerPanel = () => { content={sharedMessages.quicklyTroubleshoot} /> diff --git a/pkg/webui/console/components/webhook-form/index.js b/pkg/webui/console/components/webhook-form/index.js index af338fc871..d1c57fb52e 100644 --- a/pkg/webui/console/components/webhook-form/index.js +++ b/pkg/webui/console/components/webhook-form/index.js @@ -18,7 +18,7 @@ import { uniq } from 'lodash' import tts from '@console/api/tts' -import { IconTrash, IconRefresh } from '@ttn-lw/components/icon' +import { IconTrash, IconRefresh, IconPlayerPlay, IconPlayerPause } from '@ttn-lw/components/icon' import Form from '@ttn-lw/components/form' import Input from '@ttn-lw/components/input' import SubmitBar from '@ttn-lw/components/submit-bar' @@ -81,6 +81,12 @@ const m = defineMessages({ 'This webhook has been deactivated due to several unsuccessful forwarding attempts. It will be automatically reactivated after {webhookRetryInterval}. If you wish to reactivate right away, you can use the button below.', pendingInfo: 'This webhook is currently pending until attempting its first regular request attempt. Note that webhooks can be restricted if they encounter too many request failures.', + pausedInfo: + 'This webhook is currently paused and no messages are forwarded to the configured end point.', + pauseWebhook: 'Pause webhook', + pauseWebhookQuestion: 'Pause webhook?', + pauseWebhookDescription: + 'When a webhook is paused, messages will not be forwarded to the configured end point and will be dropped.', messagePathValidateTooLong: 'Enabled message path must be at most 64 characters', basicAuthCheckbox: 'Use basic access authentication (basic auth)', requestBasicAuth: 'Request authentication', @@ -94,6 +100,8 @@ const m = defineMessages({ filterEventData: 'Filter event data', fieldMaskPlaceholder: 'Select a filter path', filtersAdd: 'Add filter path', + pause: 'Pause', + activate: 'Activate', }) // We can use the allowed field masks of the `ApplicationUpStorage` API as @@ -249,6 +257,7 @@ const WebhookForm = props => { onDeleteFailure, onReactivate, onReactivateSuccess, + onPause, } = props const form = useRef(null) @@ -280,6 +289,8 @@ const WebhookForm = props => { const mayReactivate = update && hasUnhealthyWebhookConfig && healthStatus?.unhealthy const isPending = update && healthStatusEnabled && !healthStatus + const isPaused = initialWebhookValue?.paused + const handleReplaceModalDecision = useCallback(mayReplace => { if (mayReplace) { modalResolve.current() @@ -373,32 +384,52 @@ const WebhookForm = props => {
    )} - {mayReactivate && ( + {isPaused ? ( - ), - }} + info + content={m.pausedInfo} children={