diff --git a/CHANGELOG.md b/CHANGELOG.md
index ede205ffa5..b567384622 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -15,8 +15,13 @@ For details about compatibility between different releases, see the **Commitment
### Deprecated
+- Experimental RPCs `AuthorizeGateway` and `UnauthorizeGateway` of the Gateway Claiming Server (GCLS).
+- `CUPSRedirection` field of `ClaimGatewayRequest`.
+
### Removed
+- `authorize` and `unauthorize` sub-commands of the gateway `claim` command.
+
### Fixed
### Security
diff --git a/api/ttn/lorawan/v3/api.md b/api/ttn/lorawan/v3/api.md
index 2dac035f83..76a3ee07db 100644
--- a/api/ttn/lorawan/v3/api.md
+++ b/api/ttn/lorawan/v3/api.md
@@ -2924,6 +2924,8 @@ in a future version of The Things Stack.
### Message `CUPSRedirection`
+DEPRECATED: This message is deprecated and will be removed in a future version of The Things Stack.
+
| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| `target_cups_uri` | [`string`](#string) | | CUPS URI for LoRa Basics Station CUPS redirection. |
@@ -2996,7 +2998,7 @@ in a future version of The Things Stack.
| `collaborator` | [`OrganizationOrUserIdentifiers`](#ttn.lorawan.v3.OrganizationOrUserIdentifiers) | | Collaborator to grant all rights on the target gateway. |
| `target_gateway_id` | [`string`](#string) | | Gateway ID for the target gateway. This must be a unique value. If this is not set, the target ID for the target gateway will be set to ``. |
| `target_gateway_server_address` | [`string`](#string) | | Target Gateway Server Address for the target gateway. |
-| `cups_redirection` | [`CUPSRedirection`](#ttn.lorawan.v3.CUPSRedirection) | | Parameters to set CUPS redirection for the gateway. |
+| `cups_redirection` | [`CUPSRedirection`](#ttn.lorawan.v3.CUPSRedirection) | | Parameters to set CUPS redirection for the gateway. DEPRECATED: This field is deprecated and will be removed in a future version of The Things Stack. |
| `target_frequency_plan_id` | [`string`](#string) | | Frequency plan ID of the target gateway. TODO: Remove this field (https://github.com/TheThingsIndustries/lorawan-stack/issues/4024) DEPRECATED: Use target_frequency_plan_ids instead. |
| `target_frequency_plan_ids` | [`string`](#string) | repeated | Frequency plan IDs of the target gateway. |
@@ -3164,18 +3166,20 @@ The GatewayClaimingServer service support claiming and managing gateway claims.
| Method Name | Request Type | Response Type | Description |
| ----------- | ------------ | ------------- | ------------|
| `Claim` | [`ClaimGatewayRequest`](#ttn.lorawan.v3.ClaimGatewayRequest) | [`GatewayIdentifiers`](#ttn.lorawan.v3.GatewayIdentifiers) | Claims a gateway by claim authentication code or QR code and transfers the gateway to the target user. |
-| `AuthorizeGateway` | [`AuthorizeGatewayRequest`](#ttn.lorawan.v3.AuthorizeGatewayRequest) | [`.google.protobuf.Empty`](#google.protobuf.Empty) | AuthorizeGateway allows a gateway to be claimed. |
-| `UnauthorizeGateway` | [`GatewayIdentifiers`](#ttn.lorawan.v3.GatewayIdentifiers) | [`.google.protobuf.Empty`](#google.protobuf.Empty) | UnauthorizeGateway prevents a gateway from being claimed. |
+| `Unclaim` | [`GatewayIdentifiers`](#ttn.lorawan.v3.GatewayIdentifiers) | [`.google.protobuf.Empty`](#google.protobuf.Empty) | Unclaims the gateway. EUI provided in the request are ignored and the end device is looked up by the gateway ID. |
| `GetInfoByGatewayEUI` | [`GetInfoByGatewayEUIRequest`](#ttn.lorawan.v3.GetInfoByGatewayEUIRequest) | [`GetInfoByGatewayEUIResponse`](#ttn.lorawan.v3.GetInfoByGatewayEUIResponse) | Return whether claiming is available for a given gateway EUI. |
+| `AuthorizeGateway` | [`AuthorizeGatewayRequest`](#ttn.lorawan.v3.AuthorizeGatewayRequest) | [`.google.protobuf.Empty`](#google.protobuf.Empty) | AuthorizeGateway allows a gateway to be claimed. DEPRECATED: Authorizing gateways for claiming is no longer supported and will be removed in a future version of The Things Stack. |
+| `UnauthorizeGateway` | [`GatewayIdentifiers`](#ttn.lorawan.v3.GatewayIdentifiers) | [`.google.protobuf.Empty`](#google.protobuf.Empty) | UnauthorizeGateway prevents a gateway from being claimed. DEPRECATED: Unauthorizing (locking) gateways for claiming is no longer supported and will be removed in a future version of The Things Stack. |
#### HTTP bindings
| Method Name | Method | Pattern | Body |
| ----------- | ------ | ------- | ---- |
| `Claim` | `POST` | `/api/v3/gcls/claim` | `*` |
+| `Unclaim` | `DELETE` | `/api/v3/gcls/claim/{gateway_id}` | |
+| `GetInfoByGatewayEUI` | `POST` | `/api/v3/gcls/claim/info` | `*` |
| `AuthorizeGateway` | `POST` | `/api/v3/gcls/gateways/{gateway_ids.gateway_id}/authorize` | `*` |
| `UnauthorizeGateway` | `DELETE` | `/api/v3/gcls/gateways/{gateway_id}/authorize` | |
-| `GetInfoByGatewayEUI` | `POST` | `/api/v3/gcls/claim/info` | `*` |
## File `ttn/lorawan/v3/devicerepository.proto`
diff --git a/api/ttn/lorawan/v3/api.swagger.json b/api/ttn/lorawan/v3/api.swagger.json
index 7251068181..ff81c19713 100644
--- a/api/ttn/lorawan/v3/api.swagger.json
+++ b/api/ttn/lorawan/v3/api.swagger.json
@@ -7862,9 +7862,49 @@
]
}
},
+ "/gcls/claim/{gateway_id}": {
+ "delete": {
+ "summary": "Unclaims the gateway.\nEUI provided in the request are ignored and the end device is looked up by the gateway ID.",
+ "operationId": "GatewayClaimingServer_Unclaim",
+ "responses": {
+ "200": {
+ "description": "A successful response.",
+ "schema": {
+ "type": "object",
+ "properties": {}
+ }
+ },
+ "default": {
+ "description": "An unexpected error response.",
+ "schema": {
+ "$ref": "#/definitions/googlerpcStatus"
+ }
+ }
+ },
+ "parameters": [
+ {
+ "name": "gateway_id",
+ "in": "path",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "name": "eui",
+ "description": "Secondary identifier, which can only be used in specific requests.",
+ "in": "query",
+ "required": false,
+ "type": "string",
+ "format": "string"
+ }
+ ],
+ "tags": [
+ "GatewayClaimingServer"
+ ]
+ }
+ },
"/gcls/gateways/{gateway_ids.gateway_id}/authorize": {
"post": {
- "summary": "AuthorizeGateway allows a gateway to be claimed.",
+ "summary": "AuthorizeGateway allows a gateway to be claimed.\nDEPRECATED: Authorizing gateways for claiming is no longer supported and will be removed in a future version of The\nThings Stack.",
"operationId": "GatewayClaimingServer_AuthorizeGateway",
"responses": {
"200": {
@@ -7904,7 +7944,7 @@
},
"/gcls/gateways/{gateway_id}/authorize": {
"delete": {
- "summary": "UnauthorizeGateway prevents a gateway from being claimed.",
+ "summary": "UnauthorizeGateway prevents a gateway from being claimed.\nDEPRECATED: Unauthorizing (locking) gateways for claiming is no longer supported and will be removed in a future\nversion of The Things Stack.",
"operationId": "GatewayClaimingServer_UnauthorizeGateway",
"responses": {
"200": {
@@ -20451,7 +20491,8 @@
"type": "string",
"description": "The Device Claiming Server will fill this field with a The Things Stack API Key."
}
- }
+ },
+ "description": "DEPRECATED: This message is deprecated and will be removed in a future version of The Things Stack."
},
"v3ClaimEndDeviceRequest": {
"type": "object",
@@ -20520,7 +20561,7 @@
},
"cups_redirection": {
"$ref": "#/definitions/v3CUPSRedirection",
- "description": "Parameters to set CUPS redirection for the gateway."
+ "description": "Parameters to set CUPS redirection for the gateway.\nDEPRECATED: This field is deprecated and will be removed in a future version of The Things Stack."
},
"target_frequency_plan_id": {
"type": "string",
diff --git a/api/ttn/lorawan/v3/deviceclaimingserver.proto b/api/ttn/lorawan/v3/deviceclaimingserver.proto
index 46e8833a1b..e57a207541 100644
--- a/api/ttn/lorawan/v3/deviceclaimingserver.proto
+++ b/api/ttn/lorawan/v3/deviceclaimingserver.proto
@@ -286,7 +286,9 @@ service EndDeviceBatchClaimingServer {
}
}
+// DEPRECATED: This message is deprecated and will be removed in a future version of The Things Stack.
message CUPSRedirection {
+ option deprecated = true;
// CUPS URI for LoRa Basics Station CUPS redirection.
string target_cups_uri = 1 [(validate.rules).string = {
uri: true,
@@ -356,7 +358,8 @@ message ClaimGatewayRequest {
string target_gateway_server_address = 5 [(validate.rules).string.pattern = "^(?:(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]*[a-zA-Z0-9])\\.)*(?:[A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\\-]*[A-Za-z0-9])(?::[0-9]{1,5})?$|^$"];
// Parameters to set CUPS redirection for the gateway.
- CUPSRedirection cups_redirection = 6;
+ // DEPRECATED: This field is deprecated and will be removed in a future version of The Things Stack.
+ CUPSRedirection cups_redirection = 6 [deprecated = true];
// Frequency plan ID of the target gateway.
// TODO: Remove this field (https://github.com/TheThingsIndustries/lorawan-stack/issues/4024)
@@ -429,8 +432,25 @@ service GatewayClaimingServer {
};
}
+ // Unclaims the gateway.
+ // EUI provided in the request are ignored and the end device is looked up by the gateway ID.
+ rpc Unclaim(GatewayIdentifiers) returns (google.protobuf.Empty) {
+ option (google.api.http) = {delete: "/gcls/claim/{gateway_id}"};
+ }
+
+ // Return whether claiming is available for a given gateway EUI.
+ rpc GetInfoByGatewayEUI(GetInfoByGatewayEUIRequest) returns (GetInfoByGatewayEUIResponse) {
+ option (google.api.http) = {
+ post: "/gcls/claim/info",
+ body: "*"
+ };
+ }
+
// AuthorizeGateway allows a gateway to be claimed.
+ // DEPRECATED: Authorizing gateways for claiming is no longer supported and will be removed in a future version of The
+ // Things Stack.
rpc AuthorizeGateway(AuthorizeGatewayRequest) returns (google.protobuf.Empty) {
+ option deprecated = true;
option (google.api.http) = {
post: "/gcls/gateways/{gateway_ids.gateway_id}/authorize",
body: "*"
@@ -438,15 +458,10 @@ service GatewayClaimingServer {
}
// UnauthorizeGateway prevents a gateway from being claimed.
+ // DEPRECATED: Unauthorizing (locking) gateways for claiming is no longer supported and will be removed in a future
+ // version of The Things Stack.
rpc UnauthorizeGateway(GatewayIdentifiers) returns (google.protobuf.Empty) {
+ option deprecated = true;
option (google.api.http) = {delete: "/gcls/gateways/{gateway_id}/authorize"};
}
-
- // Return whether claiming is available for a given gateway EUI.
- rpc GetInfoByGatewayEUI(GetInfoByGatewayEUIRequest) returns (GetInfoByGatewayEUIResponse) {
- option (google.api.http) = {
- post: "/gcls/claim/info",
- body: "*"
- };
- }
}
diff --git a/cmd/ttn-lw-cli/commands/gateways_claim.go b/cmd/ttn-lw-cli/commands/gateways_claim.go
index a44a01da95..982e910cc7 100644
--- a/cmd/ttn-lw-cli/commands/gateways_claim.go
+++ b/cmd/ttn-lw-cli/commands/gateways_claim.go
@@ -15,16 +15,14 @@
package commands
import (
- "encoding/pem"
"os"
"github.com/spf13/cobra"
"go.thethings.network/lorawan-stack/v3/cmd/internal/io"
"go.thethings.network/lorawan-stack/v3/cmd/ttn-lw-cli/internal/api"
+ "go.thethings.network/lorawan-stack/v3/cmd/ttn-lw-cli/internal/util"
"go.thethings.network/lorawan-stack/v3/pkg/errors"
- "go.thethings.network/lorawan-stack/v3/pkg/rpcmetadata"
"go.thethings.network/lorawan-stack/v3/pkg/ttnpb"
- "google.golang.org/grpc"
)
var (
@@ -32,11 +30,10 @@ var (
errInvalidTargetCUPSTrust = errors.DefineInvalidArgument("invalid_target_cups_trust", "invalid target CUPS trust")
)
-var (
- gatewayClaimCommand = &cobra.Command{
- Use: "claim [gateway-eui]",
- Short: "Claim a gateway (EXPERIMENTAL)",
- Long: `Claim an gateway (EXPERIMENTAL)
+var gatewayClaimCommand = &cobra.Command{
+ Use: "claim [gateway-eui]",
+ Short: "Claim a gateway (EXPERIMENTAL)",
+ Long: `Claim an gateway (EXPERIMENTAL)
The claiming procedure transfers ownership of gateways using the Device
Claiming Server.
@@ -53,169 +50,71 @@ Gateway Server address and a frequency plan ID.
For LoRa Basic Station gateways, the Target CUPS URI must be specified.
A PEM encoded CUPS trust may be included in the claim request.
`,
- RunE: func(cmd *cobra.Command, args []string) error {
- gtwIDs, err := getGatewayEUI(cmd.Flags(), args, true)
- if err != nil {
- return err
- }
- collaborator := getCollaborator(cmd.Flags())
- if collaborator == nil {
- return errNoCollaborator.New()
- }
- dcs, err := api.Dial(ctx, config.DeviceClaimingServerGRPCAddress)
- if err != nil {
- return err
- }
- authenticationCode, _ := cmd.Flags().GetString("authentication-code")
- targetGatewayServerAddress, _ := cmd.Flags().GetString("target-gateway-server-address")
- targetGatewayID, _ := cmd.Flags().GetString("target-gateway-id")
- targetCUPSURI, _ := cmd.Flags().GetString("target-cups-uri")
- targetCUPSTrustLocalFile, _ := cmd.Flags().GetString("target-cups-trust-local-file")
- targetFrequencyPlanId, _ := cmd.Flags().GetString("target-frequency-plan-id")
-
- var targetCUPSTrust []byte
- if targetCUPSTrustLocalFile != "" {
- raw, err := getDataBytes("target-cups-trust", cmd.Flags())
- if err != nil {
- return err
- }
- block, _ := pem.Decode(raw)
- if block == nil || block.Type != "CERTIFICATE" {
- return errInvalidTargetCUPSTrust.New()
- }
- targetCUPSTrust = block.Bytes
- }
- req := &ttnpb.ClaimGatewayRequest{
- SourceGateway: &ttnpb.ClaimGatewayRequest_AuthenticatedIdentifiers_{
- AuthenticatedIdentifiers: &ttnpb.ClaimGatewayRequest_AuthenticatedIdentifiers{
- GatewayEui: gtwIDs.Eui,
- AuthenticationCode: []byte(authenticationCode),
- },
+ RunE: func(cmd *cobra.Command, args []string) error {
+ gtwIDs, err := getGatewayEUI(cmd.Flags(), args, true)
+ if err != nil {
+ return err
+ }
+ collaborator := getCollaborator(cmd.Flags())
+ if collaborator == nil {
+ return errNoCollaborator.New()
+ }
+ dcs, err := api.Dial(ctx, config.DeviceClaimingServerGRPCAddress)
+ if err != nil {
+ return err
+ }
+ authenticationCode, _ := cmd.Flags().GetString("authentication-code")
+ targetGatewayServerAddress, _ := cmd.Flags().GetString("target-gateway-server-address")
+ targetGatewayID, _ := cmd.Flags().GetString("target-gateway-id")
+ targetFrequencyPlanID, _ := cmd.Flags().GetString("target-frequency-plan-id")
+ targetFrequencyPlanIDs, _ := cmd.Flags().GetStringSlice("target-frequency-plan-ids")
+
+ if len(targetFrequencyPlanIDs) == 0 && targetFrequencyPlanID != "" {
+ targetFrequencyPlanIDs = []string{targetFrequencyPlanID}
+ }
+ req := &ttnpb.ClaimGatewayRequest{
+ SourceGateway: &ttnpb.ClaimGatewayRequest_AuthenticatedIdentifiers_{
+ AuthenticatedIdentifiers: &ttnpb.ClaimGatewayRequest_AuthenticatedIdentifiers{
+ GatewayEui: gtwIDs.Eui,
+ AuthenticationCode: []byte(authenticationCode),
},
- Collaborator: collaborator,
- TargetGatewayServerAddress: targetGatewayServerAddress,
- TargetGatewayId: targetGatewayID,
- TargetFrequencyPlanId: targetFrequencyPlanId,
- }
- if targetCUPSURI != "" {
- req.CupsRedirection = &ttnpb.CUPSRedirection{
- TargetCupsTrust: targetCUPSTrust,
- TargetCupsUri: targetCUPSURI,
- }
- }
- ids, err := ttnpb.NewGatewayClaimingServerClient(dcs).Claim(ctx, req)
- if err != nil {
- return err
- }
-
- return io.Write(os.Stdout, config.OutputFormat, ids)
- },
- }
- gatewayClaimAuthorize = &cobra.Command{
- Use: "authorize [gateway-id]",
- Short: "Authorize an gateway for claiming (EXPERIMENTAL)",
- Long: `Authorize an gateway for claiming (EXPERIMENTAL)
-
-The given API key must have the right to
-- read gateway information
-- read secrets
-- delete the gateway.
-If no API key is provided, a new one will be created.`,
- RunE: func(cmd *cobra.Command, args []string) error {
- gtwID, err := getGatewayID(cmd.Flags(), args, true)
- if err != nil {
- return errNoGatewayID.New()
- }
-
- expiryDate, err := getAPIKeyExpiry(cmd.Flags())
- if err != nil {
- return err
- }
-
- requiredRights := []ttnpb.Right{
- ttnpb.Right_RIGHT_GATEWAY_READ_SECRETS,
- ttnpb.Right_RIGHT_GATEWAY_DELETE,
- ttnpb.Right_RIGHT_GATEWAY_INFO,
- }
-
- is, err := api.Dial(ctx, config.IdentityServerGRPCAddress)
- if err != nil {
- return err
- }
-
- key, _ := cmd.Flags().GetString("api-key")
- if key != "" {
- retrievedRights, err := ttnpb.NewGatewayAccessClient(is).ListRights(ctx, gtwID, grpc.PerRPCCredentials(rpcmetadata.MD{
- AuthType: "Bearer",
- AuthValue: key,
- }))
- if err != nil {
- return err
- }
- if !retrievedRights.IncludesAll(requiredRights...) {
- return errInsufficientSourceGatewayRights.New()
- }
- } else {
- logger.Info("No API Key provided. Creating one")
- res, err := ttnpb.NewGatewayAccessClient(is).CreateAPIKey(ctx, &ttnpb.CreateGatewayAPIKeyRequest{
- GatewayIds: gtwID,
- Name: "Gateway Claim Authorization Key", // This field can only have 50 chars.
- Rights: requiredRights,
- ExpiresAt: ttnpb.ProtoTime(expiryDate),
- })
- if err != nil {
- return err
- }
-
- logger.Infof("Created API Key with ID: %s", res.Id)
- key = res.Key
- }
-
- dcs, err := api.Dial(ctx, config.DeviceClaimingServerGRPCAddress)
- if err != nil {
- return err
- }
- _, err = ttnpb.NewGatewayClaimingServerClient(dcs).AuthorizeGateway(ctx, &ttnpb.AuthorizeGatewayRequest{
- GatewayIds: gtwID,
- ApiKey: key,
- })
+ },
+ Collaborator: collaborator,
+ TargetGatewayServerAddress: targetGatewayServerAddress,
+ TargetGatewayId: targetGatewayID,
+ TargetFrequencyPlanId: targetFrequencyPlanID,
+ TargetFrequencyPlanIds: targetFrequencyPlanIDs,
+ }
+ ids, err := ttnpb.NewGatewayClaimingServerClient(dcs).Claim(ctx, req)
+ if err != nil {
return err
- },
- }
- gatewayClaimUnauthorize = &cobra.Command{
- Use: "unauthorize [gateway-id]",
- Short: "Unauthorize an gateway for claiming (EXPERIMENTAL)",
- RunE: func(cmd *cobra.Command, args []string) error {
- gtwID, err := getGatewayID(cmd.Flags(), args, true)
- if err != nil {
- return errNoGatewayID.New()
- }
-
- dcs, err := api.Dial(ctx, config.DeviceClaimingServerGRPCAddress)
- if err != nil {
- return err
- }
-
- logger.Warn("Make sure to delete the API Key used for authorizing claiming as this is not done automatically")
+ }
- _, err = ttnpb.NewGatewayClaimingServerClient(dcs).UnauthorizeGateway(ctx, gtwID)
- return err
- },
- }
-)
+ return io.Write(os.Stdout, config.OutputFormat, ids)
+ },
+}
func init() {
- gatewayClaimAuthorize.Flags().String("api-key", "", "")
- gatewayClaimAuthorize.Flags().AddFlagSet(apiKeyExpiryFlag)
- gatewayClaimCommand.AddCommand(gatewayClaimAuthorize)
gatewayClaimCommand.Flags().AddFlagSet(collaboratorFlags())
- gatewayClaimCommand.AddCommand(gatewayClaimUnauthorize)
gatewayClaimCommand.PersistentFlags().AddFlagSet(gatewayIDFlags())
gatewayClaimCommand.Flags().String("authentication-code", "", "(hex)")
gatewayClaimCommand.Flags().String("target-cups-uri", "", "")
gatewayClaimCommand.Flags().String("target-frequency-plan-id", "", "")
+ gatewayClaimCommand.Flags().String("target-frequency-plan-ids", "", "")
gatewayClaimCommand.Flags().AddFlagSet(dataFlags("target-cups-trust", "(optional) Target CUPS trust in PEM format"))
gatewayClaimCommand.Flags().String("target-gateway-server-address", "", "")
gatewayClaimCommand.Flags().String("target-gateway-id", "", "gateway ID for the claimed gateway")
gatewaysCommand.AddCommand(gatewayClaimCommand)
+
+ // Deprecate unsupported flags.
+ util.DeprecateFlagWithoutForwarding(
+ gatewayClaimCommand.Flags(),
+ "target-cups-uri",
+ "this functionality is no longer supported",
+ )
+ util.DeprecateFlagWithoutForwarding(
+ gatewayClaimCommand.Flags(),
+ "target-cups-trust-local-file",
+ "this functionality is no longer supported",
+ )
}
diff --git a/config/messages.json b/config/messages.json
index 5b49a831df..0fa82d27f7 100644
--- a/config/messages.json
+++ b/config/messages.json
@@ -3923,6 +3923,33 @@
"file": "ttjs.go"
}
},
+ "error:pkg/deviceclaimingserver/gateways/ttgc:not_implemented": {
+ "translations": {
+ "en": "not implemented"
+ },
+ "description": {
+ "package": "pkg/deviceclaimingserver/gateways/ttgc",
+ "file": "ttgc.go"
+ }
+ },
+ "error:pkg/deviceclaimingserver/gateways:invalid_upstream": {
+ "translations": {
+ "en": "upstream `{name}` is invalid"
+ },
+ "description": {
+ "package": "pkg/deviceclaimingserver/gateways",
+ "file": "gateways.go"
+ }
+ },
+ "error:pkg/deviceclaimingserver:claim gateway": {
+ "translations": {
+ "en": "claim gateway"
+ },
+ "description": {
+ "package": "pkg/deviceclaimingserver",
+ "file": "grpc_gateways.go"
+ }
+ },
"error:pkg/deviceclaimingserver:claiming_not_supported": {
"translations": {
"en": "claiming not supported for JoinEUI `{eui}`"
@@ -3932,6 +3959,15 @@
"file": "grpc_end_devices.go"
}
},
+ "error:pkg/deviceclaimingserver:create_gateway": {
+ "translations": {
+ "en": "create gateway"
+ },
+ "description": {
+ "package": "pkg/deviceclaimingserver",
+ "file": "grpc_gateways.go"
+ }
+ },
"error:pkg/deviceclaimingserver:device_not_found": {
"translations": {
"en": "device not found"
@@ -3941,13 +3977,31 @@
"file": "grpc_end_devices.go"
}
},
- "error:pkg/deviceclaimingserver:method_unavailable": {
+ "error:pkg/deviceclaimingserver:gateway_already_exists": {
"translations": {
- "en": "method unavailable"
+ "en": "gateway with EUI `{eui}` already exists"
},
"description": {
"package": "pkg/deviceclaimingserver",
- "file": "grpc_end_devices.go"
+ "file": "grpc_gateways.go"
+ }
+ },
+ "error:pkg/deviceclaimingserver:gateway_claiming_not_supported": {
+ "translations": {
+ "en": "claiming not supported for gateway with EUI `{eui}`"
+ },
+ "description": {
+ "package": "pkg/deviceclaimingserver",
+ "file": "grpc_gateways.go"
+ }
+ },
+ "error:pkg/deviceclaimingserver:gateway_claiming_with_qrcodes_not_implemented": {
+ "translations": {
+ "en": "gateway claiming with QR codes not implemented"
+ },
+ "description": {
+ "package": "pkg/deviceclaimingserver",
+ "file": "grpc_gateways.go"
}
},
"error:pkg/deviceclaimingserver:no_devices_found": {
@@ -3959,6 +4013,15 @@
"file": "grpc_end_devices.go"
}
},
+ "error:pkg/deviceclaimingserver:no_eui": {
+ "translations": {
+ "en": "no EUI found for gateway"
+ },
+ "description": {
+ "package": "pkg/deviceclaimingserver",
+ "file": "grpc_gateways.go"
+ }
+ },
"error:pkg/deviceclaimingserver:no_euis": {
"translations": {
"en": "DevEUI/JoinEUI not set for device"
@@ -3968,6 +4031,15 @@
"file": "grpc_end_devices.go"
}
},
+ "error:pkg/deviceclaimingserver:no_gateway_server_address": {
+ "translations": {
+ "en": "no gateway server address set for gateway"
+ },
+ "description": {
+ "package": "pkg/deviceclaimingserver",
+ "file": "grpc_gateways.go"
+ }
+ },
"error:pkg/deviceclaimingserver:no_join_eui": {
"translations": {
"en": "extract JoinEUI from request"
@@ -10151,6 +10223,96 @@
"file": "client_registry.go"
}
},
+ "event:dcs.end_device.claim.abort": {
+ "translations": {
+ "en": "abort end device claim"
+ },
+ "description": {
+ "package": "pkg/deviceclaimingserver/observability",
+ "file": "observability.go"
+ }
+ },
+ "event:dcs.end_device.claim.fail": {
+ "translations": {
+ "en": "claim end device failure"
+ },
+ "description": {
+ "package": "pkg/deviceclaimingserver/observability",
+ "file": "observability.go"
+ }
+ },
+ "event:dcs.end_device.claim.success": {
+ "translations": {
+ "en": "claim end device successful"
+ },
+ "description": {
+ "package": "pkg/deviceclaimingserver/observability",
+ "file": "observability.go"
+ }
+ },
+ "event:dcs.end_device.unclaim.fail": {
+ "translations": {
+ "en": "unclaim end device failure"
+ },
+ "description": {
+ "package": "pkg/deviceclaimingserver/observability",
+ "file": "observability.go"
+ }
+ },
+ "event:dcs.end_device.unclaim.success": {
+ "translations": {
+ "en": "unclaim end device successful"
+ },
+ "description": {
+ "package": "pkg/deviceclaimingserver/observability",
+ "file": "observability.go"
+ }
+ },
+ "event:dcs.gateway.claim.abort": {
+ "translations": {
+ "en": "abort gateway claim"
+ },
+ "description": {
+ "package": "pkg/deviceclaimingserver/observability",
+ "file": "observability.go"
+ }
+ },
+ "event:dcs.gateway.claim.fail": {
+ "translations": {
+ "en": "claim gateway failure"
+ },
+ "description": {
+ "package": "pkg/deviceclaimingserver/observability",
+ "file": "observability.go"
+ }
+ },
+ "event:dcs.gateway.claim.success": {
+ "translations": {
+ "en": "claim gateway successful"
+ },
+ "description": {
+ "package": "pkg/deviceclaimingserver/observability",
+ "file": "observability.go"
+ }
+ },
+ "event:dcs.gateway.unclaim.fail": {
+ "translations": {
+ "en": "unclaim gateway failure"
+ },
+ "description": {
+ "package": "pkg/deviceclaimingserver/observability",
+ "file": "observability.go"
+ }
+ },
+ "event:dcs.gateway.unclaim.success": {
+ "translations": {
+ "en": "unclaim gateway successful"
+ },
+ "description": {
+ "package": "pkg/deviceclaimingserver/observability",
+ "file": "observability.go"
+ }
+ },
"event:end_device.batch.delete": {
"translations": {
"en": "batch delete end devices"
diff --git a/pkg/deviceclaimingserver/config.go b/pkg/deviceclaimingserver/config.go
index a48c14705f..78611dc6d7 100644
--- a/pkg/deviceclaimingserver/config.go
+++ b/pkg/deviceclaimingserver/config.go
@@ -16,9 +16,11 @@ package deviceclaimingserver
import (
"go.thethings.network/lorawan-stack/v3/pkg/deviceclaimingserver/enddevices"
+ "go.thethings.network/lorawan-stack/v3/pkg/deviceclaimingserver/gateways"
)
// Config is the configuration for the Device Claiming Server.
type Config struct {
EndDeviceClaimingServerConfig enddevices.Config `name:"edcs"`
+ GatewayClaimingServerConfig gateways.Config `name:"gcls"`
}
diff --git a/pkg/deviceclaimingserver/deviceclaimingserver.go b/pkg/deviceclaimingserver/deviceclaimingserver.go
index 782b572942..e56087de99 100644
--- a/pkg/deviceclaimingserver/deviceclaimingserver.go
+++ b/pkg/deviceclaimingserver/deviceclaimingserver.go
@@ -21,6 +21,8 @@ import (
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
"go.thethings.network/lorawan-stack/v3/pkg/component"
"go.thethings.network/lorawan-stack/v3/pkg/deviceclaimingserver/enddevices"
+ "go.thethings.network/lorawan-stack/v3/pkg/deviceclaimingserver/gateways"
+ gtwregistry "go.thethings.network/lorawan-stack/v3/pkg/deviceclaimingserver/registry/gateways"
"go.thethings.network/lorawan-stack/v3/pkg/log"
"go.thethings.network/lorawan-stack/v3/pkg/ttnpb"
"google.golang.org/grpc"
@@ -35,8 +37,6 @@ type DeviceClaimingServer struct {
endDeviceClaimingUpstream *enddevices.Upstream
- gatewayClaimingServerUpstream ttnpb.GatewayClaimingServerServer
-
grpc struct {
endDeviceClaimingServer *endDeviceClaimingServer
endDeviceBatchClaimingServer *endDeviceBatchClaimingServer
@@ -64,6 +64,7 @@ func New(c *component.Component, conf *Config, opts ...Option) (*DeviceClaimingS
}
dcs.endDeviceClaimingUpstream = upstream
}
+
dcs.grpc.endDeviceClaimingServer = &endDeviceClaimingServer{
DCS: dcs,
}
@@ -72,9 +73,18 @@ func New(c *component.Component, conf *Config, opts ...Option) (*DeviceClaimingS
DCS: dcs,
}
- dcs.gatewayClaimingServerUpstream = noopGCLS{}
- dcs.grpc.gatewayClaimingServer = &gatewayClaimingServer{
- DCS: dcs,
+ if dcs.grpc.gatewayClaimingServer == nil {
+ upstream, err := gateways.NewUpstream(ctx, conf.GatewayClaimingServerConfig)
+ if err != nil {
+ return nil, err
+ }
+ dcs.grpc.gatewayClaimingServer = &gatewayClaimingServer{
+ upstream: upstream,
+ registry: gtwregistry.Registry{
+ Cluster: c,
+ },
+ peerAccess: c,
+ }
}
c.RegisterGRPC(dcs)
@@ -91,6 +101,21 @@ func WithEndDeviceClaimingUpstream(upstream *enddevices.Upstream) Option {
}
}
+// WithGatewayClaimingServer configures the gateway claiming server.
+func WithGatewayClaimingServer(
+ upstream *gateways.Upstream,
+ registry gtwregistry.GatewayRegistry,
+ access peerAccess,
+) Option {
+ return func(dcs *DeviceClaimingServer) {
+ dcs.grpc.gatewayClaimingServer = &gatewayClaimingServer{
+ upstream: upstream,
+ registry: registry,
+ peerAccess: access,
+ }
+ }
+}
+
// Context returns the context of the Device Claiming Server.
func (dcs *DeviceClaimingServer) Context() context.Context {
return dcs.ctx
diff --git a/pkg/deviceclaimingserver/gateways/gateways.go b/pkg/deviceclaimingserver/gateways/gateways.go
new file mode 100644
index 0000000000..ec3b18f73d
--- /dev/null
+++ b/pkg/deviceclaimingserver/gateways/gateways.go
@@ -0,0 +1,156 @@
+// 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 gateways provides functions to claim gateways.
+package gateways
+
+import (
+ "context"
+ "strings"
+
+ "go.thethings.network/lorawan-stack/v3/pkg/deviceclaimingserver/gateways/ttgc"
+ dcstypes "go.thethings.network/lorawan-stack/v3/pkg/deviceclaimingserver/types"
+ "go.thethings.network/lorawan-stack/v3/pkg/errors"
+ "go.thethings.network/lorawan-stack/v3/pkg/types"
+)
+
+// Config is the configuration for the Gateway Claiming Server.
+type Config struct {
+ CreateOnNotFound bool `name:"create-on-not-found" description:"DEPRECATED"` // nolint:lll
+ DefaultGatewayServerAddress string `name:"default-gateway-server-address" description:"The default Gateway Server Address"` // nolint:lll
+ Upstreams map[string][]string `name:"upstreams" description:"Map of upstream type and the supported Gateway EUI ranges"` // nolint:lll
+ TTGC ttgc.Config `name:"ttgc"`
+}
+
+var errInvalidUpstream = errors.DefineInvalidArgument("invalid_upstream", "upstream `{name}` is invalid")
+
+// ParseGatewayEUIRanges parses the configured upstream map and returns map of ranges.
+func ParseGatewayEUIRanges(config map[string][]string) (map[string][]dcstypes.EUI64Range, error) {
+ res := make(map[string][]dcstypes.EUI64Range, len(config))
+ for host, ranges := range config {
+ res[host] = make([]dcstypes.EUI64Range, 0, len(ranges))
+ for _, val := range ranges {
+ var r dcstypes.EUI64Range
+ switch {
+ case strings.Contains(val, "/"):
+ var prefix types.EUI64Prefix
+ if err := prefix.UnmarshalText([]byte(val)); err != nil {
+ return nil, errInvalidUpstream.WithAttributes("name", host).WithCause(err)
+ }
+ r = dcstypes.RangeFromEUI64Prefix(prefix)
+ case strings.Contains(val, "-"):
+ parts := strings.Split(val, "-")
+ if len(parts) != 2 {
+ return nil, errInvalidUpstream.WithAttributes("name", host)
+ }
+ var start, end types.EUI64
+ if err := start.UnmarshalText([]byte(parts[0])); err != nil {
+ return nil, errInvalidUpstream.WithAttributes("name", host).WithCause(err)
+ }
+ if err := end.UnmarshalText([]byte(parts[1])); err != nil {
+ return nil, errInvalidUpstream.WithAttributes("name", host).WithCause(err)
+ }
+ r = dcstypes.RangeFromEUI64Range(start, end)
+ default:
+ return nil, errInvalidUpstream.WithAttributes("name", host)
+ }
+ res[host] = append(res[host], r)
+ }
+ }
+ return res, nil
+}
+
+// Claimer provides methods for claiming Gateways.
+type Claimer interface {
+ // Claim claims a gateway.
+ Claim(ctx context.Context, eui types.EUI64, ownerToken string, clusterAddress string) error
+ // Unclaim unclaims a gateway.
+ Unclaim(context.Context, types.EUI64, string) error
+}
+
+// rangeClaimer supports claiming a range of EUIs.
+type rangeClaimer struct {
+ ranges []dcstypes.EUI64Range
+ Claimer
+}
+
+// Upstream is a gateway claiming upstream.
+type Upstream struct {
+ claimers map[string]rangeClaimer
+}
+
+// NewUpstream returns a new upstream based on the provided configuration.
+func NewUpstream(
+ ctx context.Context,
+ conf Config,
+ opts ...Option,
+) (*Upstream, error) {
+ upstream := &Upstream{
+ claimers: make(map[string]rangeClaimer),
+ }
+ for _, opt := range opts {
+ opt(upstream)
+ }
+
+ hosts, err := ParseGatewayEUIRanges(conf.Upstreams)
+ if err != nil {
+ return nil, err
+ }
+ // Setup upstream table.
+ for name, ranges := range hosts {
+ if len(ranges) == 0 || name == "" {
+ continue
+ }
+ var claimer Claimer
+ switch name {
+ case "ttgc":
+ claimer, err = conf.TTGC.NewClient(ctx)
+ if err != nil {
+ return nil, err
+ }
+ default:
+ return nil, errInvalidUpstream.WithAttributes("name", name)
+ }
+ upstream.claimers[name] = rangeClaimer{
+ Claimer: claimer,
+ ranges: ranges,
+ }
+ }
+ return upstream, nil
+}
+
+// Option configures Upstream.
+type Option func(*Upstream)
+
+// WithClaimer adds a claimer to Upstream.
+func WithClaimer(name string, ranges []dcstypes.EUI64Range, claimer Claimer) Option {
+ return func(upstream *Upstream) {
+ upstream.claimers[name] = rangeClaimer{
+ Claimer: claimer,
+ ranges: ranges,
+ }
+ }
+}
+
+// Claimer returns the Claimer for the given Gateway EUI.
+func (upstream *Upstream) Claimer(gatewayEUI types.EUI64) Claimer {
+ for _, claimer := range upstream.claimers {
+ for _, r := range claimer.ranges {
+ if r.Contains(gatewayEUI) {
+ return claimer.Claimer
+ }
+ }
+ }
+ return nil
+}
diff --git a/pkg/deviceclaimingserver/gateways/gateways_test.go b/pkg/deviceclaimingserver/gateways/gateways_test.go
new file mode 100644
index 0000000000..6ee5192f2a
--- /dev/null
+++ b/pkg/deviceclaimingserver/gateways/gateways_test.go
@@ -0,0 +1,120 @@
+// 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 gateways_test
+
+import (
+ "testing"
+
+ "go.thethings.network/lorawan-stack/v3/pkg/deviceclaimingserver/gateways"
+ "go.thethings.network/lorawan-stack/v3/pkg/deviceclaimingserver/gateways/ttgc"
+ dcstypes "go.thethings.network/lorawan-stack/v3/pkg/deviceclaimingserver/types"
+ "go.thethings.network/lorawan-stack/v3/pkg/errors"
+ "go.thethings.network/lorawan-stack/v3/pkg/types"
+ "go.thethings.network/lorawan-stack/v3/pkg/util/test"
+ "go.thethings.network/lorawan-stack/v3/pkg/util/test/assertions/should"
+)
+
+func TestUpstream(t *testing.T) {
+ t.Parallel()
+
+ a, ctx := test.New(t)
+
+ // Invalid ranges.
+ ranges := map[string][]string{"ttgc": {"&S(FU*)"}}
+ euiPrefixes, err := gateways.ParseGatewayEUIRanges(ranges)
+ a.So(err, should.NotBeNil)
+ a.So(euiPrefixes, should.BeEmpty)
+
+ ranges = map[string][]string{"ttgc": {"58A0CBFFFE800000"}}
+ euiPrefixes, err = gateways.ParseGatewayEUIRanges(ranges)
+ a.So(err, should.NotBeNil)
+ a.So(euiPrefixes, should.BeEmpty)
+
+ ranges = map[string][]string{"ttgc": {"58A0CBFFFE800000/123456"}}
+ euiPrefixes, err = gateways.ParseGatewayEUIRanges(ranges)
+ a.So(err, should.NotBeNil)
+ a.So(euiPrefixes, should.BeEmpty)
+
+ ranges = map[string][]string{"ttgc": {"58A0CBFFFE800000-58A0CBFFFE800000-58A0CBFFFE800000"}}
+ euiPrefixes, err = gateways.ParseGatewayEUIRanges(ranges)
+ a.So(err, should.NotBeNil)
+ a.So(euiPrefixes, should.BeEmpty)
+
+ ranges = map[string][]string{"ttgc": {"001616FFFEWXUSD-001616FFFETGENDE"}}
+ euiPrefixes, err = gateways.ParseGatewayEUIRanges(ranges)
+ a.So(err, should.NotBeNil)
+ a.So(euiPrefixes, should.BeEmpty)
+
+ ranges = map[string][]string{"ttgc": {"001616FFFE42DFAD-001616FFFETGENDE"}}
+ euiPrefixes, err = gateways.ParseGatewayEUIRanges(ranges)
+ a.So(err, should.NotBeNil)
+ a.So(euiPrefixes, should.BeEmpty)
+
+ // Valid Configuration
+ ranges = map[string][]string{
+ "ttgc": {
+ "58A0CBFFFE800000/48",
+ "001616FFFE42DFAD-001616FFFE42E395",
+ },
+ }
+ euiPrefixes, err = gateways.ParseGatewayEUIRanges(ranges)
+ a.So(err, should.BeNil)
+ a.So(euiPrefixes, should.Resemble, map[string][]dcstypes.EUI64Range{
+ "ttgc": {
+ dcstypes.RangeFromEUI64Prefix(types.EUI64Prefix{
+ EUI64: types.EUI64{0x58, 0xa0, 0xcb, 0xff, 0xfe, 0x80, 0x00, 0x00},
+ Length: 48,
+ }),
+ dcstypes.RangeFromEUI64Range(
+ types.EUI64{0x00, 0x16, 0x16, 0xff, 0xfe, 0x42, 0xdf, 0xad},
+ types.EUI64{0x00, 0x16, 0x16, 0xff, 0xfe, 0x42, 0xe3, 0x95},
+ ),
+ },
+ })
+
+ // Invalid configurations
+ config := gateways.Config{
+ Upstreams: map[string][]string{"ttgc": {"&S(FU*)"}},
+ TTGC: ttgc.Config{},
+ }
+ upstream, err := gateways.NewUpstream(ctx, config)
+ a.So(errors.IsInvalidArgument(err), should.BeTrue)
+ a.So(upstream, should.BeNil)
+
+ config = gateways.Config{
+ Upstreams: map[string][]string{"unsupported": {"58A0CBFFFE800000/48"}},
+ TTGC: ttgc.Config{},
+ }
+ upstream, err = gateways.NewUpstream(ctx, config)
+ a.So(errors.IsInvalidArgument(err), should.BeTrue)
+ a.So(upstream, should.BeNil)
+
+ // Valid Configuration
+ config = gateways.Config{
+ Upstreams: map[string][]string{"ttgc": {"58A0CBFFFE800000/48"}},
+ TTGC: ttgc.Config{},
+ }
+ upstream, err = gateways.NewUpstream(ctx, config)
+ a.So(err, should.BeNil)
+ a.So(upstream, should.NotBeNil)
+
+ // Invalid EUI
+ claimer := upstream.Claimer(types.EUI64{0x58, 0xa0, 0xcb, 0xff, 0xfe, 0x81, 0x00, 0x00})
+ a.So(claimer, should.BeNil)
+
+ // Valid EUI
+ claimer = upstream.Claimer(types.EUI64{0x58, 0xa0, 0xcb, 0xff, 0xfe, 0x80, 0x00, 0x1B})
+ a.So(claimer, should.NotBeNil)
+}
diff --git a/pkg/deviceclaimingserver/gateways/ttgc/ttgc.go b/pkg/deviceclaimingserver/gateways/ttgc/ttgc.go
new file mode 100644
index 0000000000..efcc204cd1
--- /dev/null
+++ b/pkg/deviceclaimingserver/gateways/ttgc/ttgc.go
@@ -0,0 +1,50 @@
+// 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 ttgc provides functions to use The Things Gateway Controller.
+package ttgc
+
+import (
+ "context"
+
+ "go.thethings.network/lorawan-stack/v3/pkg/errors"
+ "go.thethings.network/lorawan-stack/v3/pkg/types"
+)
+
+// Config is the configuration for the client.
+type Config struct{}
+
+// TTGC is the client for The Things Gateway Controller.
+type TTGC struct {
+ config Config
+}
+
+// NewClient returns a new TTGC client.
+func (c Config) NewClient(context.Context) (*TTGC, error) {
+ return &TTGC{
+ config: c,
+ }, nil
+}
+
+var errUnimplemented = errors.DefineUnimplemented("not_implemented", "not implemented")
+
+// Claim implements gateways.GatewayClaimer.
+func (TTGC) Claim(context.Context, types.EUI64, string, string) error {
+ return errUnimplemented.New()
+}
+
+// Unclaim implements gateways.GatewayClaimer.
+func (TTGC) Unclaim(context.Context, types.EUI64, string) error {
+ return errUnimplemented.New()
+}
diff --git a/pkg/deviceclaimingserver/grpc_end_devices.go b/pkg/deviceclaimingserver/grpc_end_devices.go
index a1fac529f8..68f6ce21d3 100644
--- a/pkg/deviceclaimingserver/grpc_end_devices.go
+++ b/pkg/deviceclaimingserver/grpc_end_devices.go
@@ -36,7 +36,6 @@ var (
"DevEUI/JoinEUI not set for device",
)
errDeviceNotFound = errors.DefineNotFound("device_not_found", "device not found")
- errMethodUnavailable = errors.DefineUnimplemented("method_unavailable", "method unavailable")
errClaimingNotSupported = errors.DefineAborted(
"claiming_not_supported",
"claiming not supported for JoinEUI `{eui}`",
@@ -215,10 +214,8 @@ func (dcs *DeviceClaimingServer) getEndDevices(
if err != nil {
return nil, err
}
- client, err := ttnpb.NewEndDeviceBatchRegistryClient(conn), nil
- if err != nil {
- return nil, err
- }
+ client := ttnpb.NewEndDeviceBatchRegistryClient(conn)
+
callOpt, err := rpcmetadata.WithForwardedAuth(ctx, dcs.AllowInsecureForCredentials())
if err != nil {
return nil, err
diff --git a/pkg/deviceclaimingserver/grpc_end_devices_test.go b/pkg/deviceclaimingserver/grpc_end_devices_test.go
index 6b8a10dbb8..87c211d4f0 100644
--- a/pkg/deviceclaimingserver/grpc_end_devices_test.go
+++ b/pkg/deviceclaimingserver/grpc_end_devices_test.go
@@ -25,7 +25,7 @@ import (
"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/deviceclaimingserver"
+ "go.thethings.network/lorawan-stack/v3/pkg/deviceclaimingserver"
"go.thethings.network/lorawan-stack/v3/pkg/deviceclaimingserver/enddevices"
claimerrors "go.thethings.network/lorawan-stack/v3/pkg/deviceclaimingserver/enddevices/errors"
"go.thethings.network/lorawan-stack/v3/pkg/errors"
@@ -91,10 +91,10 @@ func TestEndDeviceClaimingServer(t *testing.T) {
ctx,
c,
enddevices.Config{},
- enddevices.WithClaimer("test", &MockClaimer{
+ enddevices.WithClaimer("test", &MockEndDeviceClaimer{
JoinEUIs: []types.EUI64{registeredJoinEUI},
ClaimFunc: func(
- ctx context.Context, joinEUI, devEUI types.EUI64, claimAuthenticationCode string,
+ _ context.Context, joinEUI, devEUI types.EUI64, claimAuthenticationCode string,
) error {
a.So(joinEUI, should.Equal, registeredJoinEUI)
a.So(devEUI, should.Resemble, registeredDevEUI)
@@ -104,7 +104,11 @@ func TestEndDeviceClaimingServer(t *testing.T) {
}),
)
a.So(err, should.BeNil)
- dcs, err := New(c, &Config{}, WithEndDeviceClaimingUpstream(mockUpstream))
+ dcs, err := deviceclaimingserver.New(
+ c,
+ &deviceclaimingserver.Config{},
+ deviceclaimingserver.WithEndDeviceClaimingUpstream(mockUpstream),
+ )
test.Must(dcs, err)
componenttest.StartComponent(t, c)
@@ -398,7 +402,7 @@ func TestBatchOperations(t *testing.T) { // nolint:all
})
registeredJoinEUI2 := types.EUI64{0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E}
- mockClaimer := &MockClaimer{
+ mockEndDeviceClaimer := &MockEndDeviceClaimer{
JoinEUIs: []types.EUI64{
registeredJoinEUI,
registeredJoinEUI2,
@@ -408,10 +412,14 @@ func TestBatchOperations(t *testing.T) { // nolint:all
ctx,
c,
enddevices.Config{},
- enddevices.WithClaimer("test", mockClaimer),
+ enddevices.WithClaimer("test", mockEndDeviceClaimer),
)
a.So(err, should.BeNil)
- dcs, err := New(c, &Config{}, WithEndDeviceClaimingUpstream(mockUpstream))
+ dcs, err := deviceclaimingserver.New(
+ c,
+ &deviceclaimingserver.Config{},
+ deviceclaimingserver.WithEndDeviceClaimingUpstream(mockUpstream),
+ )
test.Must(dcs, err)
componenttest.StartComponent(t, c)
@@ -582,8 +590,8 @@ func TestBatchOperations(t *testing.T) { // nolint:all
},
CallOpts: authorizedCallOpt,
BatchUnclaimFunc: func(
- ctx context.Context,
- ids []*ttnpb.EndDeviceIdentifiers,
+ _ context.Context,
+ _ []*ttnpb.EndDeviceIdentifiers,
) error {
return errors.DefineCanceled("batch level error", "batch level error")
},
@@ -600,7 +608,7 @@ func TestBatchOperations(t *testing.T) { // nolint:all
},
CallOpts: authorizedCallOpt,
BatchUnclaimFunc: func(
- ctx context.Context,
+ _ context.Context,
ids []*ttnpb.EndDeviceIdentifiers,
) error {
a.So(ids, should.HaveLength, 2)
@@ -637,7 +645,7 @@ func TestBatchOperations(t *testing.T) { // nolint:all
},
CallOpts: authorizedCallOpt,
BatchUnclaimFunc: func(
- ctx context.Context,
+ _ context.Context,
ids []*ttnpb.EndDeviceIdentifiers,
) error {
a.So(ids, should.HaveLength, 2)
@@ -673,7 +681,7 @@ func TestBatchOperations(t *testing.T) { // nolint:all
},
CallOpts: authorizedCallOpt,
BatchUnclaimFunc: func(
- ctx context.Context,
+ _ context.Context,
ids []*ttnpb.EndDeviceIdentifiers,
) error {
a.So(ids, should.HaveLength, 2)
@@ -711,7 +719,7 @@ func TestBatchOperations(t *testing.T) { // nolint:all
},
CallOpts: authorizedCallOpt,
BatchUnclaimFunc: func(
- ctx context.Context,
+ _ context.Context,
ids []*ttnpb.EndDeviceIdentifiers,
) error {
a.So(ids, should.HaveLength, 2)
@@ -753,7 +761,7 @@ func TestBatchOperations(t *testing.T) { // nolint:all
},
CallOpts: authorizedCallOpt,
BatchUnclaimFunc: func(
- ctx context.Context,
+ _ context.Context,
ids []*ttnpb.EndDeviceIdentifiers,
) error {
a.So(ids, should.HaveLength, 2)
@@ -784,7 +792,7 @@ func TestBatchOperations(t *testing.T) { // nolint:all
} {
tc := tc
t.Run(tc.Name, func(t *testing.T) {
- mockClaimer.BatchUnclaimFunc = tc.BatchUnclaimFunc
+ mockEndDeviceClaimer.BatchUnclaimFunc = tc.BatchUnclaimFunc
ret, err := edcsClient.Unclaim(ctx, tc.Req, tc.CallOpts)
if err != nil {
if tc.ErrorAssertion == nil || !a.So(tc.ErrorAssertion(err), should.BeTrue) {
diff --git a/pkg/deviceclaimingserver/grpc_gateways.go b/pkg/deviceclaimingserver/grpc_gateways.go
index 7ecae5c0ae..1db18606b4 100644
--- a/pkg/deviceclaimingserver/grpc_gateways.go
+++ b/pkg/deviceclaimingserver/grpc_gateways.go
@@ -16,67 +16,205 @@ package deviceclaimingserver
import (
"context"
+ "fmt"
+ "go.thethings.network/lorawan-stack/v3/pkg/deviceclaimingserver/gateways"
+ "go.thethings.network/lorawan-stack/v3/pkg/deviceclaimingserver/observability"
+ gtwregistry "go.thethings.network/lorawan-stack/v3/pkg/deviceclaimingserver/registry/gateways"
+ "go.thethings.network/lorawan-stack/v3/pkg/errors"
+ "go.thethings.network/lorawan-stack/v3/pkg/log"
+ "go.thethings.network/lorawan-stack/v3/pkg/rpcmetadata"
"go.thethings.network/lorawan-stack/v3/pkg/ttnpb"
+ "go.thethings.network/lorawan-stack/v3/pkg/types"
"google.golang.org/protobuf/types/known/emptypb"
)
-// noopGCLS is a no-op GCLS.
-type noopGCLS struct {
- ttnpb.UnimplementedGatewayClaimingServerServer
-}
-
-// Claim implements GatewayClaimingServer.
-func (noopGCLS) Claim(
- _ context.Context,
- _ *ttnpb.ClaimGatewayRequest,
-) (ids *ttnpb.GatewayIdentifiers, retErr error) {
- return nil, errMethodUnavailable.New()
-}
-
-// AuthorizeGateway implements GatewayClaimingServer.
-func (noopGCLS) AuthorizeGateway(
- _ context.Context,
- _ *ttnpb.AuthorizeGatewayRequest,
-) (*emptypb.Empty, error) {
- return nil, errMethodUnavailable.New()
-}
-
-// UnauthorizeGateway implements GatewayClaimingServer.
-func (noopGCLS) UnauthorizeGateway(
- _ context.Context,
- _ *ttnpb.GatewayIdentifiers,
-) (*emptypb.Empty, error) {
- return nil, errMethodUnavailable.New()
+type peerAccess interface {
+ AllowInsecureForCredentials() bool
}
// gatewayClaimingServer is the front facing entity for gRPC requests.
type gatewayClaimingServer struct {
ttnpb.UnimplementedGatewayClaimingServerServer
- DCS *DeviceClaimingServer
+ peerAccess
+
+ upstream *gateways.Upstream
+ registry gtwregistry.GatewayRegistry
}
+var (
+ errGatewayClaimingWithQRCode = errors.DefineUnimplemented(
+ "gateway_claiming_with_qrcodes_not_implemented",
+ "gateway claiming with QR codes not implemented",
+ )
+ errGatewayAlreadyExists = errors.DefineAlreadyExists(
+ "gateway_already_exists",
+ "gateway with EUI `{eui}` already exists",
+ )
+ errGatewayClaimingNotSupported = errors.DefineAborted(
+ "gateway_claiming_not_supported",
+ "claiming not supported for gateway with EUI `{eui}`",
+ )
+ errClaim = errors.DefineAborted(
+ "claim gateway",
+ "claim gateway",
+ )
+ errCreateGateway = errors.DefineAborted(
+ "create_gateway",
+ "create gateway",
+ )
+ errNoEUI = errors.DefineInvalidArgument(
+ "no_eui",
+ "no EUI found for gateway",
+ )
+ errNoGatewayServerAddress = errors.DefineInvalidArgument(
+ "no_gateway_server_address",
+ "no gateway server address set for gateway",
+ )
+)
+
// Claim implements GatewayClaimingServer.
-func (gcls gatewayClaimingServer) Claim(
+func (gcls *gatewayClaimingServer) Claim(
ctx context.Context,
req *ttnpb.ClaimGatewayRequest,
) (ids *ttnpb.GatewayIdentifiers, retErr error) {
- return gcls.DCS.gatewayClaimingServerUpstream.Claim(ctx, req)
+ logger := log.FromContext(ctx)
+
+ // Extract the EUI and the owner token (claim authentication code) from the request.
+ var (
+ authCode []byte
+ gatewayEUI types.EUI64
+ )
+ switch claim := req.SourceGateway.(type) {
+ case *ttnpb.ClaimGatewayRequest_AuthenticatedIdentifiers_:
+ authIDs := claim.AuthenticatedIdentifiers
+ gatewayEUI, authCode = types.MustEUI64(authIDs.GatewayEui).OrZero(), authIDs.AuthenticationCode
+ case *ttnpb.ClaimGatewayRequest_QrCode:
+ return nil, errGatewayClaimingWithQRCode.New()
+ default:
+ panic(fmt.Sprintf("proto: unexpected type %T", claim))
+ }
+ logger = logger.WithFields(log.Fields(
+ "gateway_eui", gatewayEUI,
+ ))
+ ids = &ttnpb.GatewayIdentifiers{
+ Eui: gatewayEUI.Bytes(),
+ GatewayId: req.TargetGatewayId,
+ }
+
+ // Check if the gateway already exists.
+ _, err := gcls.registry.GetIdentifiersForEUI(ctx, gatewayEUI)
+ if err == nil {
+ return nil, errGatewayAlreadyExists.WithAttributes("eui", gatewayEUI)
+ } else if !errors.IsNotFound(err) {
+ return nil, err
+ }
+
+ // Support clients that only set a single frequency plan.
+ if len(req.TargetFrequencyPlanIds) == 0 && req.TargetFrequencyPlanId != "" { // nolint:staticcheck
+ req.TargetFrequencyPlanIds = []string{req.TargetFrequencyPlanId} // nolint:staticcheck
+ }
+
+ // Check if the gateway is configured for claiming.
+ claimer := gcls.upstream.Claimer(gatewayEUI)
+ if claimer == nil {
+ return nil, errGatewayClaimingNotSupported.WithAttributes("eui", gatewayEUI)
+ }
+
+ // Claim the gateway on the upstream.
+ if err := claimer.Claim(ctx, gatewayEUI, string(authCode), req.TargetGatewayServerAddress); err != nil {
+ observability.RegisterFailClaim(ctx, ids.GetEntityIdentifiers(), err)
+ return nil, errClaim.WithCause(err)
+ }
+
+ // Unclaim if creation fails.
+ defer func(ids *ttnpb.GatewayIdentifiers) {
+ if retErr != nil {
+ observability.RegisterAbortClaim(ctx, ids.GetEntityIdentifiers(), retErr)
+ if err := claimer.Unclaim(ctx, gatewayEUI, string(authCode)); err != nil {
+ logger.WithError(err).Warn("Failed to unclaim gateway")
+ }
+ return
+ }
+ observability.RegisterSuccessClaim(ctx, ids.GetEntityIdentifiers())
+ }(ids)
+
+ // Create the gateway in the IS.
+ gateway := &ttnpb.Gateway{
+ Ids: ids,
+ GatewayServerAddress: req.TargetGatewayServerAddress,
+ EnforceDutyCycle: true,
+ RequireAuthenticatedConnection: true,
+ FrequencyPlanIds: req.TargetFrequencyPlanIds,
+ }
+
+ _, err = gcls.registry.Create(ctx, &ttnpb.CreateGatewayRequest{
+ Gateway: gateway,
+ Collaborator: req.GetCollaborator(),
+ })
+ if err != nil {
+ return nil, errCreateGateway.WithCause(err)
+ }
+
+ return ids, nil
}
-// AuthorizeGateway implements GatewayClaimingServer.
-func (gcls gatewayClaimingServer) AuthorizeGateway(
- ctx context.Context,
- req *ttnpb.AuthorizeGatewayRequest,
-) (*emptypb.Empty, error) {
- return gcls.DCS.gatewayClaimingServerUpstream.AuthorizeGateway(ctx, req)
+// GetInfoByGatewayEUI implements GatewayClaimingServer.
+func (gcls gatewayClaimingServer) GetInfoByGatewayEUI(
+ ctx context.Context, in *ttnpb.GetInfoByGatewayEUIRequest,
+) (*ttnpb.GetInfoByGatewayEUIResponse, error) {
+ // Check that there's any auth token on the request context.
+ _, err := rpcmetadata.WithForwardedAuth(ctx, gcls.AllowInsecureForCredentials())
+ if err != nil {
+ return nil, err
+ }
+ eui := types.MustEUI64(in.Eui).OrZero()
+
+ return &ttnpb.GetInfoByGatewayEUIResponse{
+ Eui: in.Eui,
+ SupportsClaiming: gcls.upstream.Claimer(eui) != nil,
+ }, nil
}
-// UnauthorizeGateway implements GatewayClaimingServer.
-func (gcls gatewayClaimingServer) UnauthorizeGateway(
- ctx context.Context,
- gtwIDs *ttnpb.GatewayIdentifiers,
-) (*emptypb.Empty, error) {
- return gcls.DCS.gatewayClaimingServerUpstream.UnauthorizeGateway(ctx, gtwIDs)
+// Unclaim implements GatewayClaimingServer.
+func (gcls gatewayClaimingServer) Unclaim(ctx context.Context, req *ttnpb.GatewayIdentifiers) (*emptypb.Empty, error) {
+ // Check for the necessary rights.
+ if err := gcls.registry.AssertGatewayRights(
+ ctx,
+ &ttnpb.GatewayIdentifiers{
+ GatewayId: req.GatewayId,
+ },
+ ttnpb.Right_RIGHT_GATEWAY_INFO,
+ ttnpb.Right_RIGHT_GATEWAY_DELETE,
+ ); err != nil {
+ return nil, err
+ }
+
+ // Get the gateway.
+ gtw, err := gcls.registry.Get(ctx, &ttnpb.GetGatewayRequest{
+ GatewayIds: req,
+ })
+ if err != nil {
+ return nil, err
+ }
+ gatewayEUI := types.MustEUI64(gtw.Ids.Eui).OrZero()
+ if gatewayEUI.IsZero() {
+ return nil, errNoEUI.New()
+ }
+ if gtw.GatewayServerAddress == "" {
+ return nil, errNoGatewayServerAddress.New()
+ }
+ claimer := gcls.upstream.Claimer(gatewayEUI)
+ if claimer == nil {
+ return nil, errGatewayClaimingNotSupported.WithAttributes("eui", gatewayEUI)
+ }
+
+ if err := claimer.Unclaim(ctx, gatewayEUI, gtw.GatewayServerAddress); err != nil {
+ observability.RegisterFailUnclaim(ctx, gtw.GetEntityIdentifiers(), err)
+ return nil, err
+ }
+ observability.RegisterSuccessUnclaim(ctx, gtw.GetEntityIdentifiers())
+
+ return ttnpb.Empty, nil
}
diff --git a/pkg/deviceclaimingserver/grpc_gateways_test.go b/pkg/deviceclaimingserver/grpc_gateways_test.go
index 6701e5903d..2f19ea485e 100644
--- a/pkg/deviceclaimingserver/grpc_gateways_test.go
+++ b/pkg/deviceclaimingserver/grpc_gateways_test.go
@@ -23,7 +23,9 @@ import (
"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/deviceclaimingserver"
+ "go.thethings.network/lorawan-stack/v3/pkg/deviceclaimingserver"
+ "go.thethings.network/lorawan-stack/v3/pkg/deviceclaimingserver/gateways"
+ dcstypes "go.thethings.network/lorawan-stack/v3/pkg/deviceclaimingserver/types"
"go.thethings.network/lorawan-stack/v3/pkg/errors"
"go.thethings.network/lorawan-stack/v3/pkg/log"
"go.thethings.network/lorawan-stack/v3/pkg/rpcmetadata"
@@ -39,14 +41,14 @@ var (
userID = ttnpb.UserIdentifiers{
UserId: "test-user",
}
- authorizedCallOpt = grpc.PerRPCCredentials(rpcmetadata.MD{
+ authorizedMD = rpcmetadata.MD{
AuthType: "Bearer",
AuthValue: "foo",
- })
+ }
+ authorizedCallOpt = grpc.PerRPCCredentials(authorizedMD)
)
-func TestGatewayClaimingServer(t *testing.T) {
- t.Parallel()
+func TestGatewayClaimingServer(t *testing.T) { // nolint:paralleltest
a := assertions.New(t)
ctx := log.NewContext(test.Context(), test.GetLogger(t))
ctx, cancelCtx := context.WithCancel(ctx)
@@ -54,6 +56,13 @@ func TestGatewayClaimingServer(t *testing.T) {
cancelCtx()
})
+ supportedEUI := types.EUI64{0x58, 0xa0, 0xcb, 0xff, 0xfe, 0x80, 0x00, 0x01}
+
+ unAuthorizedCallOpt := grpc.PerRPCCredentials(rpcmetadata.MD{
+ AuthType: "Bearer",
+ AuthValue: "invalid-key",
+ })
+
c := componenttest.NewComponent(t, &component.Config{
ServiceBase: config.ServiceBase{
GRPC: config.GRPC{
@@ -61,7 +70,47 @@ func TestGatewayClaimingServer(t *testing.T) {
},
},
})
- test.Must(New(c, &Config{}))
+
+ mockGatewayclaimer := &MockGatewayClaimer{}
+ mockUpstream, err := gateways.NewUpstream(
+ ctx,
+ gateways.Config{},
+ gateways.WithClaimer(
+ "mock",
+ []dcstypes.EUI64Range{
+ dcstypes.RangeFromEUI64Prefix(types.EUI64Prefix{
+ EUI64: types.EUI64{0x58, 0xa0, 0xcb, 0xff, 0xfe, 0x80, 0x00, 0x00},
+ Length: 48,
+ }),
+ },
+ mockGatewayclaimer,
+ ),
+ )
+ if err != nil {
+ t.FailNow()
+ }
+
+ existingEUI := types.EUI64{0x58, 0xa0, 0xcb, 0xff, 0xfe, 0x80, 0x00, 0xFF}
+ mockGatewayRegistry := &mockGatewayRegistry{
+ authorizedMD: authorizedMD,
+ gateways: []*ttnpb.Gateway{
+ {
+ Ids: &ttnpb.GatewayIdentifiers{
+ GatewayId: "test-gateway",
+ Eui: existingEUI.Bytes(),
+ },
+ },
+ },
+ }
+
+ test.Must(deviceclaimingserver.New(c,
+ &deviceclaimingserver.Config{},
+ deviceclaimingserver.WithGatewayClaimingServer(
+ mockUpstream,
+ mockGatewayRegistry,
+ c,
+ ),
+ ))
componenttest.StartComponent(t, c)
t.Cleanup(func() {
c.Close()
@@ -73,77 +122,379 @@ func TestGatewayClaimingServer(t *testing.T) {
mustHavePeer(ctx, c, ttnpb.ClusterRole_DEVICE_CLAIMING_SERVER)
gclsClient := ttnpb.NewGatewayClaimingServerClient(c.LoopbackConn())
- // Test API Validation here. Functionality is tested in the implementations.
+ // Check that AuthorizeGateway and UnauthorizeGateway are not implemented.
+ _, err = gclsClient.AuthorizeGateway(ctx, &ttnpb.AuthorizeGatewayRequest{ // nolint:staticcheck
+ GatewayIds: &ttnpb.GatewayIdentifiers{
+ GatewayId: "test-gateway",
+ },
+ ApiKey: "foo",
+ }, authorizedCallOpt)
+ a.So(errors.IsUnimplemented(err), should.BeTrue)
+
+ _, err = gclsClient.UnauthorizeGateway(ctx, &ttnpb.GatewayIdentifiers{ // nolint:staticcheck
+ GatewayId: "test-gateway",
+ }, authorizedCallOpt)
+ a.So(errors.IsUnimplemented(err), should.BeTrue)
+
+ // Test GetInfoByGatewayEUI
+ _, err = gclsClient.GetInfoByGatewayEUI(
+ ctx,
+ &ttnpb.GetInfoByGatewayEUIRequest{
+ Eui: types.EUI64{0x58, 0xa0, 0xcb, 0xff, 0xfe, 0x80, 0x00, 0x00}.Bytes(),
+ },
+ )
+ a.So(errors.IsUnauthenticated(err), should.BeTrue)
+
+ unsupportedEUI := types.EUI64{0x58, 0xa0, 0xcb, 0xff, 0xfe, 0x90, 0x00, 0x00}
+ resp, err := gclsClient.GetInfoByGatewayEUI(
+ ctx,
+ &ttnpb.GetInfoByGatewayEUIRequest{
+ Eui: unsupportedEUI.Bytes(),
+ },
+ authorizedCallOpt,
+ )
+ a.So(err, should.BeNil)
+ a.So(resp.Eui, should.Resemble, unsupportedEUI.Bytes())
+ a.So(resp.SupportsClaiming, should.BeFalse)
+
+ resp, err = gclsClient.GetInfoByGatewayEUI(
+ ctx,
+ &ttnpb.GetInfoByGatewayEUIRequest{
+ Eui: supportedEUI.Bytes(),
+ },
+ authorizedCallOpt,
+ )
+ a.So(err, should.BeNil)
+ a.So(resp.Eui, should.Resemble, supportedEUI.Bytes())
+ a.So(resp.SupportsClaiming, should.BeTrue)
+
+ // Test claiming
for _, tc := range []struct {
Name string
- Req any
- ErrorAssertion func(err error) bool
+ Req *ttnpb.ClaimGatewayRequest
+ CallOpt grpc.CallOption
+ ClaimFunc func(context.Context, types.EUI64, string, string) error
+ CreateFunc func(context.Context, *ttnpb.CreateGatewayRequest) (*ttnpb.Gateway, error)
+ UnclaimFunc func(context.Context, types.EUI64, string) error
+ ErrorAssertion func(error) bool
}{
{
- Name: "Authorize/NilIDs",
- Req: &ttnpb.AuthorizeGatewayRequest{
- GatewayIds: nil,
- ApiKey: "test",
+ Name: "Claim/EmptyRequest",
+ Req: &ttnpb.ClaimGatewayRequest{
+ Collaborator: userID.GetOrganizationOrUserIdentifiers(),
},
+ CallOpt: authorizedCallOpt,
ErrorAssertion: errors.IsInvalidArgument,
},
{
- Name: "Unauthorize/EmptyIDs",
- Req: &ttnpb.GatewayIdentifiers{},
+ Name: "Claim/NilCollaborator",
+ Req: &ttnpb.ClaimGatewayRequest{
+ Collaborator: nil,
+ SourceGateway: &ttnpb.ClaimGatewayRequest_AuthenticatedIdentifiers_{
+ AuthenticatedIdentifiers: &ttnpb.ClaimGatewayRequest_AuthenticatedIdentifiers{
+ GatewayEui: supportedEUI.Bytes(),
+ AuthenticationCode: claimAuthCode,
+ },
+ },
+ TargetGatewayId: "test-gateway",
+ TargetGatewayServerAddress: "things.example.com",
+ },
+ CallOpt: authorizedCallOpt,
ErrorAssertion: errors.IsInvalidArgument,
},
{
- Name: "Claim/EmptyRequest",
+ Name: "Claim/InvalidGatewayID",
Req: &ttnpb.ClaimGatewayRequest{
Collaborator: userID.GetOrganizationOrUserIdentifiers(),
+ SourceGateway: &ttnpb.ClaimGatewayRequest_AuthenticatedIdentifiers_{
+ AuthenticatedIdentifiers: &ttnpb.ClaimGatewayRequest_AuthenticatedIdentifiers{
+ GatewayEui: supportedEUI.Bytes(),
+ AuthenticationCode: claimAuthCode,
+ },
+ },
+ TargetGatewayId: "&-gateway",
+ TargetGatewayServerAddress: "things.example.com",
},
+ CallOpt: authorizedCallOpt,
ErrorAssertion: errors.IsInvalidArgument,
},
{
- Name: "Claim/NilCollaborator",
+ Name: "Claim/GatewayEUIAlreadyExists",
Req: &ttnpb.ClaimGatewayRequest{
- Collaborator: nil,
+ Collaborator: userID.GetOrganizationOrUserIdentifiers(),
SourceGateway: &ttnpb.ClaimGatewayRequest_AuthenticatedIdentifiers_{
AuthenticatedIdentifiers: &ttnpb.ClaimGatewayRequest_AuthenticatedIdentifiers{
- GatewayEui: types.EUI64{0x58, 0xA0, 0xCB, 0xFF, 0xFE, 0x80, 0x00, 0x20}.Bytes(),
+ GatewayEui: existingEUI.Bytes(),
AuthenticationCode: claimAuthCode,
},
},
- TargetGatewayId: "my-new-gateway",
- TargetGatewayServerAddress: "target-tenant.things.example.com",
+ TargetGatewayId: "test-gateway",
+ TargetGatewayServerAddress: "things.example.com",
},
- ErrorAssertion: errors.IsInvalidArgument,
+ CallOpt: authorizedCallOpt,
+ ErrorAssertion: errors.IsAlreadyExists,
},
{
- Name: "Claim/NilCollaborator",
+ Name: "Claim/EUINotRegisteredForClaiming",
Req: &ttnpb.ClaimGatewayRequest{
- Collaborator: nil,
+ Collaborator: userID.GetOrganizationOrUserIdentifiers(),
+ SourceGateway: &ttnpb.ClaimGatewayRequest_AuthenticatedIdentifiers_{
+ AuthenticatedIdentifiers: &ttnpb.ClaimGatewayRequest_AuthenticatedIdentifiers{
+ GatewayEui: unsupportedEUI.Bytes(),
+ AuthenticationCode: claimAuthCode,
+ },
+ },
+ TargetGatewayId: "test-gateway",
+ TargetGatewayServerAddress: "things.example.com",
+ },
+ CallOpt: authorizedCallOpt,
+ ErrorAssertion: errors.IsAborted,
+ },
+ {
+ Name: "Claim/ClaimFailed",
+ Req: &ttnpb.ClaimGatewayRequest{
+ Collaborator: userID.GetOrganizationOrUserIdentifiers(),
SourceGateway: &ttnpb.ClaimGatewayRequest_AuthenticatedIdentifiers_{
AuthenticatedIdentifiers: &ttnpb.ClaimGatewayRequest_AuthenticatedIdentifiers{
- GatewayEui: types.EUI64{0x58, 0xA0, 0xCB, 0xFF, 0xFE, 0x80, 0x00, 0x20}.Bytes(),
+ GatewayEui: supportedEUI.Bytes(),
AuthenticationCode: claimAuthCode,
},
},
- TargetGatewayId: "my-new-gateway",
- TargetGatewayServerAddress: "target-tenant.things.example.com",
+ TargetGatewayId: "test-gateway",
+ TargetGatewayServerAddress: "things.example.com",
},
+ CallOpt: authorizedCallOpt,
+ ClaimFunc: func(_ context.Context, _ types.EUI64, _, _ string) error {
+ return errClaim.New()
+ },
+ ErrorAssertion: errors.IsAborted,
+ },
+ {
+ Name: "Claim/CreateFailed",
+ Req: &ttnpb.ClaimGatewayRequest{
+ Collaborator: userID.GetOrganizationOrUserIdentifiers(),
+ SourceGateway: &ttnpb.ClaimGatewayRequest_AuthenticatedIdentifiers_{
+ AuthenticatedIdentifiers: &ttnpb.ClaimGatewayRequest_AuthenticatedIdentifiers{
+ GatewayEui: supportedEUI.Bytes(),
+ AuthenticationCode: claimAuthCode,
+ },
+ },
+ TargetGatewayId: "test-gateway",
+ TargetGatewayServerAddress: "things.example.com",
+ },
+ CallOpt: authorizedCallOpt,
+ ClaimFunc: func(context.Context, types.EUI64, string, string) error {
+ return nil
+ },
+ CreateFunc: func(context.Context, *ttnpb.CreateGatewayRequest) (*ttnpb.Gateway, error) {
+ return nil, errCreate.New()
+ },
+ UnclaimFunc: func(_ context.Context, eui types.EUI64, _ string) error {
+ if eui.Equal(supportedEUI) {
+ return nil
+ }
+ return errUnclaim.New()
+ },
+ ErrorAssertion: errors.IsAborted,
+ },
+ {
+ Name: "Claim/CreateFailedWithUnclaimFailed",
+ Req: &ttnpb.ClaimGatewayRequest{
+ Collaborator: userID.GetOrganizationOrUserIdentifiers(),
+ SourceGateway: &ttnpb.ClaimGatewayRequest_AuthenticatedIdentifiers_{
+ AuthenticatedIdentifiers: &ttnpb.ClaimGatewayRequest_AuthenticatedIdentifiers{
+ GatewayEui: supportedEUI.Bytes(),
+ AuthenticationCode: claimAuthCode,
+ },
+ },
+ TargetGatewayId: "test-gateway",
+ TargetGatewayServerAddress: "things.example.com",
+ },
+ CallOpt: authorizedCallOpt,
+ ClaimFunc: func(context.Context, types.EUI64, string, string) error {
+ return nil
+ },
+ CreateFunc: func(context.Context, *ttnpb.CreateGatewayRequest) (*ttnpb.Gateway, error) {
+ return nil, errCreate.New()
+ },
+ UnclaimFunc: func(context.Context, types.EUI64, string) error {
+ return errUnclaim.New()
+ },
+ ErrorAssertion: errors.IsAborted,
+ },
+ {
+ Name: "Claim/SuccessfullyClaimedAndCreated",
+ Req: &ttnpb.ClaimGatewayRequest{
+ Collaborator: userID.GetOrganizationOrUserIdentifiers(),
+ SourceGateway: &ttnpb.ClaimGatewayRequest_AuthenticatedIdentifiers_{
+ AuthenticatedIdentifiers: &ttnpb.ClaimGatewayRequest_AuthenticatedIdentifiers{
+ GatewayEui: supportedEUI.Bytes(),
+ AuthenticationCode: claimAuthCode,
+ },
+ },
+ TargetGatewayId: "test-gateway",
+ TargetGatewayServerAddress: "things.example.com",
+ },
+ ClaimFunc: func(context.Context, types.EUI64, string, string) error {
+ return nil
+ },
+ CreateFunc: func(_ context.Context, in *ttnpb.CreateGatewayRequest) (*ttnpb.Gateway, error) {
+ return in.Gateway, nil
+ },
+ CallOpt: authorizedCallOpt,
+ },
+ } {
+ tc := tc
+ t.Run(tc.Name, func(t *testing.T) {
+ if tc.ClaimFunc != nil {
+ mockGatewayclaimer.ClaimFunc = tc.ClaimFunc
+ }
+ if tc.UnclaimFunc != nil {
+ mockGatewayclaimer.UnclaimFunc = tc.UnclaimFunc
+ }
+ if tc.CreateFunc != nil {
+ mockGatewayRegistry.createFunc = tc.CreateFunc
+ }
+
+ _, err := gclsClient.Claim(ctx, tc.Req, tc.CallOpt)
+ if err != nil {
+ if tc.ErrorAssertion == nil || !a.So(tc.ErrorAssertion(err), should.BeTrue) {
+ t.Fatalf("Unexpected error: %v", err)
+ }
+ } else if tc.ErrorAssertion != nil {
+ t.Fatalf("Expected error")
+ }
+ })
+ }
+
+ // Test unclaiming.
+ for _, tc := range []struct { //nolint:paralleltest
+ Name string
+ Req *ttnpb.GatewayIdentifiers
+ CallOpt grpc.CallOption
+ GetFunc func(context.Context, *ttnpb.GetGatewayRequest) (*ttnpb.Gateway, error)
+ UnclaimFunc func(context.Context, types.EUI64, string) error
+ ErrorAssertion func(error) bool
+ }{
+ {
+ Name: "Unclaim/EmptyRequest",
+ Req: &ttnpb.GatewayIdentifiers{},
+ CallOpt: authorizedCallOpt,
+ ErrorAssertion: errors.IsInvalidArgument,
+ },
+ {
+ Name: "Unclaim/NoGatewayRights",
+ Req: &ttnpb.GatewayIdentifiers{
+ GatewayId: "test-gateway",
+ },
+ CallOpt: unAuthorizedCallOpt,
+ ErrorAssertion: errors.IsUnauthenticated,
+ },
+ {
+ Name: "Unclaim/InvalidGatewayID",
+ Req: &ttnpb.GatewayIdentifiers{
+ GatewayId: "test-gateway*W(&$@#)",
+ },
+ CallOpt: authorizedCallOpt,
+ ErrorAssertion: errors.IsInvalidArgument,
+ },
+ {
+ Name: "Unclaim/NoGatewayEUI",
+ Req: &ttnpb.GatewayIdentifiers{
+ GatewayId: "test-gateway",
+ },
+ GetFunc: func(context.Context, *ttnpb.GetGatewayRequest) (*ttnpb.Gateway, error) {
+ return &ttnpb.Gateway{
+ Ids: &ttnpb.GatewayIdentifiers{
+ GatewayId: "test-gateway",
+ },
+ }, nil
+ },
+ CallOpt: authorizedCallOpt,
ErrorAssertion: errors.IsInvalidArgument,
},
+ {
+ Name: "Unclaim/NoGatewayServerAddress",
+ Req: &ttnpb.GatewayIdentifiers{
+ GatewayId: "invalid-gateway-server-address",
+ },
+ GetFunc: func(context.Context, *ttnpb.GetGatewayRequest) (*ttnpb.Gateway, error) {
+ return &ttnpb.Gateway{
+ Ids: &ttnpb.GatewayIdentifiers{
+ GatewayId: "test-gateway",
+ Eui: supportedEUI.Bytes(),
+ },
+ }, nil
+ },
+ CallOpt: authorizedCallOpt,
+ ErrorAssertion: errors.IsInvalidArgument,
+ },
+ {
+ Name: "Unclaim/EUINotRegisteredForClaiming",
+ Req: &ttnpb.GatewayIdentifiers{
+ GatewayId: "unsupported-eui",
+ },
+ GetFunc: func(context.Context, *ttnpb.GetGatewayRequest) (*ttnpb.Gateway, error) {
+ return &ttnpb.Gateway{
+ Ids: &ttnpb.GatewayIdentifiers{
+ GatewayId: "test-gateway",
+ Eui: unsupportedEUI.Bytes(),
+ },
+ GatewayServerAddress: "test.example.com",
+ }, nil
+ },
+ CallOpt: authorizedCallOpt,
+ ErrorAssertion: errors.IsAborted,
+ },
+ {
+ Name: "Unclaim/Failed",
+ Req: &ttnpb.GatewayIdentifiers{
+ GatewayId: "test-gateway",
+ },
+ GetFunc: func(context.Context, *ttnpb.GetGatewayRequest) (*ttnpb.Gateway, error) {
+ return &ttnpb.Gateway{
+ Ids: &ttnpb.GatewayIdentifiers{
+ GatewayId: "test-gateway",
+ Eui: supportedEUI.Bytes(),
+ },
+ GatewayServerAddress: "test.example.com",
+ }, nil
+ },
+ UnclaimFunc: func(context.Context, types.EUI64, string) error {
+ return errUnclaim.New()
+ },
+ CallOpt: authorizedCallOpt,
+ ErrorAssertion: errors.IsAborted,
+ },
+ {
+ Name: "Unclaim/Success",
+ Req: &ttnpb.GatewayIdentifiers{
+ GatewayId: "test-gateway",
+ },
+ GetFunc: func(context.Context, *ttnpb.GetGatewayRequest) (*ttnpb.Gateway, error) {
+ return &ttnpb.Gateway{
+ Ids: &ttnpb.GatewayIdentifiers{
+ GatewayId: "test-gateway",
+ Eui: supportedEUI.Bytes(),
+ },
+ GatewayServerAddress: "test.example.com",
+ }, nil
+ },
+ UnclaimFunc: func(context.Context, types.EUI64, string) error {
+ return nil
+ },
+ CallOpt: authorizedCallOpt,
+ },
} {
tc := tc
t.Run(tc.Name, func(t *testing.T) {
- t.Parallel()
- var err error
- switch req := tc.Req.(type) {
- case *ttnpb.AuthorizeGatewayRequest:
- _, err = gclsClient.AuthorizeGateway(ctx, req, authorizedCallOpt)
- case *ttnpb.GatewayIdentifiers:
- _, err = gclsClient.UnauthorizeGateway(ctx, req, authorizedCallOpt)
- case *ttnpb.ClaimGatewayRequest:
- _, err = gclsClient.Claim(ctx, req, authorizedCallOpt)
- default:
- panic("invalid request type")
+ if tc.UnclaimFunc != nil {
+ mockGatewayclaimer.UnclaimFunc = tc.UnclaimFunc
+ }
+ if tc.GetFunc != nil {
+ mockGatewayRegistry.getFunc = tc.GetFunc
}
+ _, err := gclsClient.Unclaim(ctx, tc.Req, tc.CallOpt)
if err != nil {
if tc.ErrorAssertion == nil || !a.So(tc.ErrorAssertion(err), should.BeTrue) {
t.Fatalf("Unexpected error: %v", err)
diff --git a/pkg/deviceclaimingserver/observability/observability.go b/pkg/deviceclaimingserver/observability/observability.go
new file mode 100644
index 0000000000..eec831364e
--- /dev/null
+++ b/pkg/deviceclaimingserver/observability/observability.go
@@ -0,0 +1,264 @@
+// 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 observability provides events and metrics for device claiming.
+package observability
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/prometheus/client_golang/prometheus"
+ "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/metrics"
+ "go.thethings.network/lorawan-stack/v3/pkg/ttnpb"
+)
+
+var (
+ evtClaimEndDeviceSuccess = events.Define(
+ "dcs.end_device.claim.success", "claim end device successful",
+ events.WithVisibility(ttnpb.Right_RIGHT_APPLICATION_DEVICES_READ),
+ )
+ evtClaimEndDeviceAbort = events.Define(
+ "dcs.end_device.claim.abort", "abort end device claim",
+ events.WithVisibility(ttnpb.Right_RIGHT_APPLICATION_DEVICES_READ),
+ )
+ evtClaimEndDeviceFail = events.Define(
+ "dcs.end_device.claim.fail", "claim end device failure",
+ events.WithVisibility(
+ ttnpb.Right_RIGHT_APPLICATION_DEVICES_READ,
+ ttnpb.Right_RIGHT_APPLICATION_DEVICES_READ_KEYS,
+ ),
+ )
+ evtClaimGatewaySuccess = events.Define(
+ "dcs.gateway.claim.success", "claim gateway successful",
+ events.WithVisibility(ttnpb.Right_RIGHT_GATEWAY_STATUS_READ),
+ )
+ evtClaimGatewayAbort = events.Define(
+ "dcs.gateway.claim.abort", "abort gateway claim",
+ events.WithVisibility(ttnpb.Right_RIGHT_GATEWAY_STATUS_READ),
+ )
+ evtClaimGatewayFail = events.Define(
+ "dcs.gateway.claim.fail", "claim gateway failure",
+ events.WithVisibility(
+ ttnpb.Right_RIGHT_GATEWAY_STATUS_READ,
+ ),
+ )
+
+ evtUnclaimEndDeviceSuccess = events.Define(
+ "dcs.end_device.unclaim.success", "unclaim end device successful",
+ events.WithVisibility(ttnpb.Right_RIGHT_APPLICATION_DEVICES_READ),
+ )
+ evtUnclaimEndDeviceFail = events.Define(
+ "dcs.end_device.unclaim.fail", "unclaim end device failure",
+ events.WithVisibility(
+ ttnpb.Right_RIGHT_APPLICATION_DEVICES_READ,
+ ttnpb.Right_RIGHT_APPLICATION_DEVICES_READ_KEYS,
+ ),
+ )
+ evtUnclaimGatewaySuccess = events.Define(
+ "dcs.gateway.unclaim.success", "unclaim gateway successful",
+ events.WithVisibility(ttnpb.Right_RIGHT_GATEWAY_STATUS_READ),
+ )
+ evtUnclaimGatewayFail = events.Define(
+ "dcs.gateway.unclaim.fail", "unclaim gateway failure",
+ events.WithVisibility(ttnpb.Right_RIGHT_GATEWAY_STATUS_READ),
+ )
+)
+
+const (
+ subsystem = "dcs"
+ unknown = "unknown"
+ entityType = "entity_type"
+ id = "id"
+)
+
+var dcsMetrics = &claimMetrics{
+ claimSucceeded: metrics.NewContextualCounterVec(
+ prometheus.CounterOpts{
+ Subsystem: subsystem,
+ Name: "claim_success_total",
+ Help: "Total number of successfully claimed entities",
+ },
+ []string{entityType, id},
+ ),
+ claimAborted: metrics.NewContextualCounterVec(
+ prometheus.CounterOpts{
+ Subsystem: subsystem,
+ Name: "claim_aborted_total",
+ Help: "Total number of claim entity abortions",
+ },
+ []string{entityType, id, "error"},
+ ),
+ claimFailed: metrics.NewContextualCounterVec(
+ prometheus.CounterOpts{
+ Subsystem: subsystem,
+ Name: "claim_failed_total",
+ Help: "Total number of claim entity failures",
+ },
+ []string{entityType, id, "error"},
+ ),
+
+ unclaimSucceeded: metrics.NewContextualCounterVec(
+ prometheus.CounterOpts{
+ Subsystem: subsystem,
+ Name: "unclaim_success_total",
+ Help: "Total number of successfully unclaimed entities",
+ },
+ []string{entityType, id},
+ ),
+ unclaimFailed: metrics.NewContextualCounterVec(
+ prometheus.CounterOpts{
+ Subsystem: subsystem,
+ Name: "unclaim_failed_total",
+ Help: "Total number of unclaim entity failures",
+ },
+ []string{entityType, id, "error"},
+ ),
+}
+
+func init() {
+ metrics.MustRegister(dcsMetrics)
+}
+
+type claimMetrics struct {
+ claimSucceeded *metrics.ContextualCounterVec
+ claimAborted *metrics.ContextualCounterVec
+ claimFailed *metrics.ContextualCounterVec
+
+ unclaimSucceeded *metrics.ContextualCounterVec
+ unclaimFailed *metrics.ContextualCounterVec
+}
+
+func (m claimMetrics) Describe(ch chan<- *prometheus.Desc) {
+ m.claimSucceeded.Describe(ch)
+ m.claimAborted.Describe(ch)
+ m.claimFailed.Describe(ch)
+
+ m.unclaimSucceeded.Describe(ch)
+ m.unclaimFailed.Describe(ch)
+}
+
+func (m claimMetrics) Collect(ch chan<- prometheus.Metric) {
+ m.claimSucceeded.Collect(ch)
+ m.claimAborted.Collect(ch)
+ m.claimFailed.Collect(ch)
+
+ m.unclaimSucceeded.Collect(ch)
+ m.unclaimFailed.Collect(ch)
+}
+
+// RegisterSuccessClaim registers a successful claim.
+func RegisterSuccessClaim(ctx context.Context, entityIDs *ttnpb.EntityIdentifiers) {
+ var id, entityType string
+ switch ids := entityIDs.Ids.(type) {
+ case *ttnpb.EntityIdentifiers_DeviceIds:
+ id = ids.DeviceIds.GetApplicationIds().GetApplicationId()
+ entityType = store.EntityEndDevice
+ events.Publish(evtClaimEndDeviceSuccess.NewWithIdentifiersAndData(ctx, entityIDs, nil))
+ case *ttnpb.EntityIdentifiers_GatewayIds:
+ id = ids.GatewayIds.GatewayId
+ entityType = store.EntityGateway
+ events.Publish(evtClaimGatewaySuccess.NewWithIdentifiersAndData(ctx, entityIDs, nil))
+ default:
+ panic(fmt.Sprintf("proto: unexpected type %T", entityIDs.Ids))
+ }
+ dcsMetrics.claimSucceeded.WithLabelValues(ctx, entityType, id).Inc()
+}
+
+// RegisterAbortClaim registers an aborted claim.
+func RegisterAbortClaim(ctx context.Context, entityIDs *ttnpb.EntityIdentifiers, err error) {
+ var id, entityType string
+ switch ids := entityIDs.Ids.(type) {
+ case *ttnpb.EntityIdentifiers_DeviceIds:
+ id = ids.DeviceIds.GetApplicationIds().GetApplicationId()
+ entityType = store.EntityEndDevice
+ events.Publish(evtClaimEndDeviceAbort.NewWithIdentifiersAndData(ctx, entityIDs, err))
+ case *ttnpb.EntityIdentifiers_GatewayIds:
+ id = ids.GatewayIds.GatewayId
+ entityType = store.EntityGateway
+ events.Publish(evtClaimGatewayAbort.NewWithIdentifiersAndData(ctx, entityIDs, err))
+ default:
+ panic(fmt.Sprintf("proto: unexpected type %T", entityIDs.Ids))
+ }
+ if ttnErr, ok := errors.From(err); ok {
+ dcsMetrics.claimAborted.WithLabelValues(ctx, entityType, id, ttnErr.FullName()).Inc()
+ } else {
+ dcsMetrics.claimAborted.WithLabelValues(ctx, entityType, id, unknown).Inc()
+ }
+}
+
+// RegisterFailClaim registers an failed claim.
+func RegisterFailClaim(ctx context.Context, entityIDs *ttnpb.EntityIdentifiers, err error) {
+ var id, entityType string
+ switch ids := entityIDs.Ids.(type) {
+ case *ttnpb.EntityIdentifiers_DeviceIds:
+ id = ids.DeviceIds.GetApplicationIds().GetApplicationId()
+ entityType = "end_device"
+ events.Publish(evtClaimEndDeviceFail.NewWithIdentifiersAndData(ctx, entityIDs, err))
+ case *ttnpb.EntityIdentifiers_GatewayIds:
+ id = ids.GatewayIds.GatewayId
+ entityType = "gateway"
+ events.Publish(evtClaimGatewayFail.NewWithIdentifiersAndData(ctx, entityIDs, err))
+ default:
+ panic(fmt.Sprintf("proto: unexpected type %T", entityIDs.Ids))
+ }
+ if ttnErr, ok := errors.From(err); ok {
+ dcsMetrics.claimFailed.WithLabelValues(ctx, entityType, id, ttnErr.FullName()).Inc()
+ } else {
+ dcsMetrics.claimFailed.WithLabelValues(ctx, entityType, id, unknown).Inc()
+ }
+}
+
+// RegisterSuccessUnclaim registers a successful unclaim.
+func RegisterSuccessUnclaim(ctx context.Context, entityIDs *ttnpb.EntityIdentifiers) {
+ var id, entityType string
+ switch ids := entityIDs.Ids.(type) {
+ case *ttnpb.EntityIdentifiers_DeviceIds:
+ id = ids.DeviceIds.GetApplicationIds().GetApplicationId()
+ entityType = "end_device"
+ events.Publish(evtUnclaimEndDeviceSuccess.NewWithIdentifiersAndData(ctx, entityIDs, nil))
+ case *ttnpb.EntityIdentifiers_GatewayIds:
+ id = ids.GatewayIds.GatewayId
+ entityType = "gateway"
+ events.Publish(evtUnclaimGatewaySuccess.NewWithIdentifiersAndData(ctx, entityIDs, nil))
+ default:
+ panic(fmt.Sprintf("proto: unexpected type %T", entityIDs.Ids))
+ }
+ dcsMetrics.unclaimSucceeded.WithLabelValues(ctx, entityType, id).Inc()
+}
+
+// RegisterFailUnclaim registers an failed unclaim.
+func RegisterFailUnclaim(ctx context.Context, entityIDs *ttnpb.EntityIdentifiers, err error) {
+ var id, entityType string
+ switch ids := entityIDs.Ids.(type) {
+ case *ttnpb.EntityIdentifiers_DeviceIds:
+ id = ids.DeviceIds.GetApplicationIds().GetApplicationId()
+ entityType = "end_device"
+ events.Publish(evtUnclaimEndDeviceFail.NewWithIdentifiersAndData(ctx, entityIDs, err))
+ case *ttnpb.EntityIdentifiers_GatewayIds:
+ id = ids.GatewayIds.GatewayId
+ entityType = "gateway"
+ events.Publish(evtUnclaimGatewayFail.NewWithIdentifiersAndData(ctx, entityIDs, err))
+ default:
+ panic(fmt.Sprintf("proto: unexpected type %T", entityIDs.Ids))
+ }
+ if ttnErr, ok := errors.From(err); ok {
+ dcsMetrics.unclaimFailed.WithLabelValues(ctx, entityType, id, ttnErr.FullName()).Inc()
+ } else {
+ dcsMetrics.unclaimFailed.WithLabelValues(ctx, entityType, id, unknown).Inc()
+ }
+}
diff --git a/pkg/deviceclaimingserver/registry/gateways/gateways.go b/pkg/deviceclaimingserver/registry/gateways/gateways.go
new file mode 100644
index 0000000000..419bb44409
--- /dev/null
+++ b/pkg/deviceclaimingserver/registry/gateways/gateways.go
@@ -0,0 +1,131 @@
+// 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 gateways provide gateway registry functions.
+package gateways
+
+import (
+ "context"
+
+ "go.thethings.network/lorawan-stack/v3/pkg/auth/rights"
+ "go.thethings.network/lorawan-stack/v3/pkg/cluster"
+ "go.thethings.network/lorawan-stack/v3/pkg/rpcmetadata"
+ "go.thethings.network/lorawan-stack/v3/pkg/ttnpb"
+ "go.thethings.network/lorawan-stack/v3/pkg/types"
+ "google.golang.org/grpc"
+ "google.golang.org/protobuf/types/known/emptypb"
+)
+
+// Cluster provides cluster operations.
+type Cluster interface {
+ GetPeerConn(ctx context.Context, role ttnpb.ClusterRole, ids cluster.EntityIdentifiers) (*grpc.ClientConn, error)
+ AllowInsecureForCredentials() bool
+ WithClusterAuth() grpc.CallOption
+}
+
+// GatewayRegistry abstracts the entity registry's gateway functions.
+type GatewayRegistry interface {
+ // AssertGatewayRights checks whether the gateway authentication
+ // (provided in the context) contains the required rights.
+ AssertGatewayRights(ctx context.Context, ids *ttnpb.GatewayIdentifiers, required ...ttnpb.Right) error
+ // GetIdentifiersForEUI returns the gateway identifiers for the EUI.
+ GetIdentifiersForEUI(ctx context.Context, eui types.EUI64) (*ttnpb.GatewayIdentifiers, error)
+ // Create creates a gateway.
+ Create(ctx context.Context, in *ttnpb.CreateGatewayRequest) (*ttnpb.Gateway, error)
+ // Delete the gateway. This may not release the gateway ID for reuse, but it does release the EUI.
+ Delete(ctx context.Context, in *ttnpb.GatewayIdentifiers) (*emptypb.Empty, error)
+ // Get the gateway. This may not release the gateway ID for reuse, but it does release the EUI.
+ Get(ctx context.Context, req *ttnpb.GetGatewayRequest) (*ttnpb.Gateway, error)
+}
+
+// Registry implements GatewayRegistry.
+type Registry struct {
+ Cluster
+}
+
+func (reg Registry) newEntityRegistryClient(ctx context.Context) (ttnpb.GatewayRegistryClient, error) {
+ cc, err := reg.GetPeerConn(ctx, ttnpb.ClusterRole_ENTITY_REGISTRY, nil)
+ if err != nil {
+ return nil, err
+ }
+ return ttnpb.NewGatewayRegistryClient(cc), nil
+}
+
+// callOptFromContext returns a gRPC call option from the provided context.
+func (reg Registry) callOptFromContext(ctx context.Context) (grpc.CallOption, error) {
+ return rpcmetadata.WithForwardedAuth(ctx, reg.AllowInsecureForCredentials())
+}
+
+// GetIdentifiersForEUI implements GatewayRegistry.
+func (reg Registry) GetIdentifiersForEUI(
+ ctx context.Context,
+ gatewayEUI types.EUI64,
+) (*ttnpb.GatewayIdentifiers, error) {
+ // Check if the gateway is registered.
+ gatewayRegistry, err := reg.newEntityRegistryClient(ctx)
+ if err != nil {
+ return nil, err
+ }
+ return gatewayRegistry.GetIdentifiersForEUI(ctx, &ttnpb.GetGatewayIdentifiersForEUIRequest{
+ Eui: gatewayEUI.Bytes(),
+ }, reg.WithClusterAuth())
+}
+
+// AssertGatewayRights implements GatewayRegistry.
+func (Registry) AssertGatewayRights(
+ ctx context.Context,
+ ids *ttnpb.GatewayIdentifiers,
+ required ...ttnpb.Right,
+) error {
+ return rights.RequireGateway(ctx, ids, required...)
+}
+
+// Create implements GatewayRegistry.
+func (reg Registry) Create(ctx context.Context, req *ttnpb.CreateGatewayRequest) (*ttnpb.Gateway, error) {
+ callOpt, err := reg.callOptFromContext(ctx)
+ if err != nil {
+ return nil, err
+ }
+ gatewayRegistry, err := reg.newEntityRegistryClient(ctx)
+ if err != nil {
+ return nil, err
+ }
+ return gatewayRegistry.Create(ctx, req, callOpt)
+}
+
+// Delete implements GatewayRegistry.
+func (reg Registry) Delete(ctx context.Context, req *ttnpb.GatewayIdentifiers) (*emptypb.Empty, error) {
+ callOpt, err := reg.callOptFromContext(ctx)
+ if err != nil {
+ return nil, err
+ }
+ gatewayRegistry, err := reg.newEntityRegistryClient(ctx)
+ if err != nil {
+ return nil, err
+ }
+ return gatewayRegistry.Delete(ctx, req, callOpt)
+}
+
+// Get implements GatewayRegistry.
+func (reg Registry) Get(ctx context.Context, req *ttnpb.GetGatewayRequest) (*ttnpb.Gateway, error) {
+ callOpt, err := reg.callOptFromContext(ctx)
+ if err != nil {
+ return nil, err
+ }
+ gatewayRegistry, err := reg.newEntityRegistryClient(ctx)
+ if err != nil {
+ return nil, err
+ }
+ return gatewayRegistry.Get(ctx, req, callOpt)
+}
diff --git a/pkg/deviceclaimingserver/types/types.go b/pkg/deviceclaimingserver/types/types.go
new file mode 100644
index 0000000000..2e17c59454
--- /dev/null
+++ b/pkg/deviceclaimingserver/types/types.go
@@ -0,0 +1,58 @@
+// 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 types provides types for the Device Claiming Server.
+package types
+
+import "go.thethings.network/lorawan-stack/v3/pkg/types"
+
+// EUI64Range is a range of EUI64s.
+type EUI64Range interface {
+ // Contains returns true if the EUI64 is in the range.
+ Contains(types.EUI64) bool
+}
+
+type eui64PrefixRange types.EUI64Prefix
+
+var _ EUI64Range = eui64PrefixRange{}
+
+// Contains implements EUI64Range.
+func (r eui64PrefixRange) Contains(eui types.EUI64) bool {
+ return eui.HasPrefix(types.EUI64Prefix(r))
+}
+
+// RangeFromEUI64Prefix returns a range that contains all EUI64s with the given prefix.
+func RangeFromEUI64Prefix(prefix types.EUI64Prefix) EUI64Range {
+ return eui64PrefixRange(prefix)
+}
+
+type eui64Range struct {
+ start, end uint64
+}
+
+var _ EUI64Range = eui64Range{}
+
+// Contains implements EUI64Range.
+func (r eui64Range) Contains(eui types.EUI64) bool {
+ n := eui.MarshalNumber()
+ return n >= r.start && n <= r.end
+}
+
+// RangeFromEUI64Range returns a range that contains all EUI64s between start and end.
+func RangeFromEUI64Range(start, end types.EUI64) EUI64Range {
+ return eui64Range{
+ start: start.MarshalNumber(),
+ end: end.MarshalNumber(),
+ }
+}
diff --git a/pkg/deviceclaimingserver/util_test.go b/pkg/deviceclaimingserver/util_test.go
index 57f94f1779..db7f875088 100644
--- a/pkg/deviceclaimingserver/util_test.go
+++ b/pkg/deviceclaimingserver/util_test.go
@@ -17,12 +17,15 @@ package deviceclaimingserver_test
import (
"context"
+ "go.thethings.network/lorawan-stack/v3/pkg/errors"
+ "go.thethings.network/lorawan-stack/v3/pkg/rpcmetadata"
"go.thethings.network/lorawan-stack/v3/pkg/ttnpb"
"go.thethings.network/lorawan-stack/v3/pkg/types"
+ "google.golang.org/protobuf/types/known/emptypb"
)
-// MockClaimer is a mock Claimer.
-type MockClaimer struct {
+// MockEndDeviceClaimer is a mock Claimer.
+type MockEndDeviceClaimer struct {
JoinEUIs []types.EUI64
ClaimFunc func(context.Context, types.EUI64, types.EUI64, string) error
@@ -33,7 +36,7 @@ type MockClaimer struct {
}
// SupportsJoinEUI returns whether the Join Server supports this JoinEUI.
-func (m MockClaimer) SupportsJoinEUI(joinEUI types.EUI64) bool {
+func (m MockEndDeviceClaimer) SupportsJoinEUI(joinEUI types.EUI64) bool {
for _, eui := range m.JoinEUIs {
if eui.Equal(joinEUI) {
return true
@@ -43,14 +46,14 @@ func (m MockClaimer) SupportsJoinEUI(joinEUI types.EUI64) bool {
}
// Claim claims an End Device.
-func (m MockClaimer) Claim(
+func (m MockEndDeviceClaimer) Claim(
ctx context.Context, joinEUI, devEUI types.EUI64, claimAuthenticationCode string,
) error {
return m.ClaimFunc(ctx, joinEUI, devEUI, claimAuthenticationCode)
}
// GetClaimStatus returns the claim status an End Device.
-func (MockClaimer) GetClaimStatus(_ context.Context,
+func (MockEndDeviceClaimer) GetClaimStatus(_ context.Context,
ids *ttnpb.EndDeviceIdentifiers,
) (*ttnpb.GetClaimStatusResponse, error) {
return &ttnpb.GetClaimStatusResponse{
@@ -59,16 +62,97 @@ func (MockClaimer) GetClaimStatus(_ context.Context,
}
// Unclaim releases the claim on an End Device.
-func (MockClaimer) Unclaim(_ context.Context,
+func (MockEndDeviceClaimer) Unclaim(_ context.Context,
_ *ttnpb.EndDeviceIdentifiers,
) (err error) {
return nil
}
// Unclaim releases the claim on an End Device.
-func (m MockClaimer) BatchUnclaim(
+func (m MockEndDeviceClaimer) BatchUnclaim(
ctx context.Context,
ids []*ttnpb.EndDeviceIdentifiers,
) error {
return m.BatchUnclaimFunc(ctx, ids)
}
+
+// MockGatewayClaimer is a mock gateway Claimer.
+type MockGatewayClaimer struct {
+ EUIs []types.EUI64
+
+ ClaimFunc func(context.Context, types.EUI64, string, string) error
+ UnclaimFunc func(context.Context, types.EUI64, string) error
+}
+
+// Claim implements gateways.Claimer.
+func (claimer MockGatewayClaimer) Claim(
+ ctx context.Context,
+ eui types.EUI64,
+ ownerToken string,
+ clusterAddress string,
+) error {
+ return claimer.ClaimFunc(ctx, eui, ownerToken, clusterAddress)
+}
+
+// Unclaim implements gateways.Claimer.
+func (claimer MockGatewayClaimer) Unclaim(ctx context.Context, eui types.EUI64, clusterAddress string) error {
+ return claimer.UnclaimFunc(ctx, eui, clusterAddress)
+}
+
+type mockGatewayRegistry struct {
+ gateways []*ttnpb.Gateway
+ authorizedMD rpcmetadata.MD
+
+ createFunc func(ctx context.Context, in *ttnpb.CreateGatewayRequest) (*ttnpb.Gateway, error)
+ deleteFunc func(ctx context.Context, in *ttnpb.GatewayIdentifiers) (*emptypb.Empty, error)
+ getFunc func(ctx context.Context, req *ttnpb.GetGatewayRequest) (*ttnpb.Gateway, error)
+}
+
+var (
+ errInvalidCredentials = errors.DefineUnauthenticated("invalid_credentials", "invalid credentials")
+ errGatewayNotFound = errors.DefineNotFound("gateway_not_found", "gateway not found")
+ errClaim = errors.DefineAborted("claim", "claim")
+ errCreate = errors.DefineAborted("create_gateway", "create gateway")
+ errUnclaim = errors.DefineAborted("unclaim", "unclaim gateway")
+)
+
+// AssertGatewayRights implements GatewayRegistry.
+func (mock mockGatewayRegistry) AssertGatewayRights(
+ ctx context.Context,
+ _ *ttnpb.GatewayIdentifiers,
+ _ ...ttnpb.Right,
+) error {
+ md := rpcmetadata.FromIncomingContext(ctx)
+ if md.AuthType == mock.authorizedMD.AuthType && md.AuthValue == mock.authorizedMD.AuthValue {
+ return nil
+ }
+ return errInvalidCredentials.New()
+}
+
+// GetIdentifiersForEUI implements GatewayRegistry.
+func (mock mockGatewayRegistry) GetIdentifiersForEUI(
+ _ context.Context,
+ eui types.EUI64,
+) (*ttnpb.GatewayIdentifiers, error) {
+ for _, gateway := range mock.gateways {
+ if types.MustEUI64(gateway.GetIds().Eui).Equal(eui) {
+ return gateway.Ids, nil
+ }
+ }
+ return nil, errGatewayNotFound.New()
+}
+
+// Create implements GatewayRegistry.
+func (mock mockGatewayRegistry) Create(ctx context.Context, in *ttnpb.CreateGatewayRequest) (*ttnpb.Gateway, error) {
+ return mock.createFunc(ctx, in)
+}
+
+// Delete implements GatewayRegistry.
+func (mock mockGatewayRegistry) Delete(ctx context.Context, in *ttnpb.GatewayIdentifiers) (*emptypb.Empty, error) {
+ return mock.deleteFunc(ctx, in)
+}
+
+// Get implements GatewayRegistry.
+func (mock mockGatewayRegistry) Get(ctx context.Context, in *ttnpb.GetGatewayRequest) (*ttnpb.Gateway, error) {
+ return mock.getFunc(ctx, in)
+}
diff --git a/pkg/ttnpb/deviceclaimingserver.pb.go b/pkg/ttnpb/deviceclaimingserver.pb.go
index bc2f605688..c1d7f7552c 100644
--- a/pkg/ttnpb/deviceclaimingserver.pb.go
+++ b/pkg/ttnpb/deviceclaimingserver.pb.go
@@ -577,6 +577,9 @@ func (x *BatchUnclaimEndDevicesResponse) GetFailed() map[string]*ErrorDetails {
return nil
}
+// DEPRECATED: This message is deprecated and will be removed in a future version of The Things Stack.
+//
+// Deprecated: Marked as deprecated in ttn/lorawan/v3/deviceclaimingserver.proto.
type CUPSRedirection struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@@ -705,6 +708,9 @@ type ClaimGatewayRequest struct {
// Target Gateway Server Address for the target gateway.
TargetGatewayServerAddress string `protobuf:"bytes,5,opt,name=target_gateway_server_address,json=targetGatewayServerAddress,proto3" json:"target_gateway_server_address,omitempty"`
// Parameters to set CUPS redirection for the gateway.
+ // DEPRECATED: This field is deprecated and will be removed in a future version of The Things Stack.
+ //
+ // Deprecated: Marked as deprecated in ttn/lorawan/v3/deviceclaimingserver.proto.
CupsRedirection *CUPSRedirection `protobuf:"bytes,6,opt,name=cups_redirection,json=cupsRedirection,proto3" json:"cups_redirection,omitempty"`
// Frequency plan ID of the target gateway.
// TODO: Remove this field (https://github.com/TheThingsIndustries/lorawan-stack/issues/4024)
@@ -790,6 +796,7 @@ func (x *ClaimGatewayRequest) GetTargetGatewayServerAddress() string {
return ""
}
+// Deprecated: Marked as deprecated in ttn/lorawan/v3/deviceclaimingserver.proto.
func (x *ClaimGatewayRequest) GetCupsRedirection() *CUPSRedirection {
if x != nil {
return x.CupsRedirection
@@ -1438,7 +1445,7 @@ var file_ttn_lorawan_v3_deviceclaimingserver_proto_rawDesc = []byte{
0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f,
0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x44, 0x65,
0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01,
- 0x22, 0x89, 0x03, 0x0a, 0x0f, 0x43, 0x55, 0x50, 0x53, 0x52, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63,
+ 0x22, 0x8d, 0x03, 0x0a, 0x0f, 0x43, 0x55, 0x50, 0x53, 0x52, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63,
0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3b, 0x0a, 0x0f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x63,
0x75, 0x70, 0x73, 0x5f, 0x75, 0x72, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x13, 0xfa,
0x42, 0x10, 0x72, 0x0e, 0x18, 0x80, 0x02, 0x32, 0x06, 0x5e, 0x68, 0x74, 0x74, 0x70, 0x73, 0x88,
@@ -1461,247 +1468,255 @@ var file_ttn_lorawan_v3_deviceclaimingserver_proto_rawDesc = []byte{
0x65, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x7a, 0x03,
0x18, 0x80, 0x40, 0x52, 0x04, 0x63, 0x65, 0x72, 0x74, 0x12, 0x1a, 0x0a, 0x03, 0x6b, 0x65, 0x79,
0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x7a, 0x03, 0x18, 0x80, 0x40,
- 0x52, 0x03, 0x6b, 0x65, 0x79, 0x42, 0x15, 0x0a, 0x13, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79,
- 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x22, 0xe8, 0x08, 0x0a,
- 0x13, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x52, 0x65, 0x71,
- 0x75, 0x65, 0x73, 0x74, 0x12, 0x7b, 0x0a, 0x19, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69,
- 0x63, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72,
- 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3c, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f,
- 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x47, 0x61,
- 0x74, 0x65, 0x77, 0x61, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x41, 0x75, 0x74,
+ 0x52, 0x03, 0x6b, 0x65, 0x79, 0x3a, 0x02, 0x18, 0x01, 0x42, 0x15, 0x0a, 0x13, 0x67, 0x61, 0x74,
+ 0x65, 0x77, 0x61, 0x79, 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73,
+ 0x22, 0xec, 0x08, 0x0a, 0x13, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61,
+ 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x7b, 0x0a, 0x19, 0x61, 0x75, 0x74, 0x68,
+ 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69,
+ 0x66, 0x69, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3c, 0x2e, 0x74, 0x74,
+ 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x43, 0x6c, 0x61,
+ 0x69, 0x6d, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
+ 0x2e, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x64, 0x49, 0x64,
+ 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x48, 0x00, 0x52, 0x18, 0x61, 0x75, 0x74,
0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x64, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69,
- 0x66, 0x69, 0x65, 0x72, 0x73, 0x48, 0x00, 0x52, 0x18, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74,
- 0x69, 0x63, 0x61, 0x74, 0x65, 0x64, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72,
- 0x73, 0x12, 0x25, 0x0a, 0x07, 0x71, 0x72, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01,
- 0x28, 0x0c, 0x42, 0x0a, 0xfa, 0x42, 0x07, 0x7a, 0x05, 0x10, 0x00, 0x18, 0x80, 0x08, 0x48, 0x00,
- 0x52, 0x06, 0x71, 0x72, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x5b, 0x0a, 0x0c, 0x63, 0x6f, 0x6c, 0x6c,
- 0x61, 0x62, 0x6f, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d,
- 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e,
- 0x4f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4f, 0x72, 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, 0x0c, 0x63, 0x6f, 0x6c, 0x6c, 0x61, 0x62, 0x6f,
- 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x56, 0x0a, 0x11, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f,
- 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x04, 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, 0x0f, 0x74, 0x61,
- 0x72, 0x67, 0x65, 0x74, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x49, 0x64, 0x12, 0xd2, 0x01,
- 0x0a, 0x1d, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79,
- 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18,
- 0x05, 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, 0x1a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x47, 0x61,
- 0x74, 0x65, 0x77, 0x61, 0x79, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x41, 0x64, 0x64, 0x72, 0x65,
- 0x73, 0x73, 0x12, 0x4a, 0x0a, 0x10, 0x63, 0x75, 0x70, 0x73, 0x5f, 0x72, 0x65, 0x64, 0x69, 0x72,
- 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x74,
- 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x43, 0x55,
- 0x50, 0x53, 0x52, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0f, 0x63,
- 0x75, 0x70, 0x73, 0x52, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x42,
- 0x0a, 0x18, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x66, 0x72, 0x65, 0x71, 0x75, 0x65, 0x6e,
- 0x63, 0x79, 0x5f, 0x70, 0x6c, 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09,
- 0x42, 0x09, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x18, 0x40, 0x18, 0x01, 0x52, 0x15, 0x74, 0x61, 0x72,
- 0x67, 0x65, 0x74, 0x46, 0x72, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x6c, 0x61, 0x6e,
- 0x49, 0x64, 0x12, 0x4b, 0x0a, 0x19, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x66, 0x72, 0x65,
- 0x71, 0x75, 0x65, 0x6e, 0x63, 0x79, 0x5f, 0x70, 0x6c, 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x73, 0x18,
- 0x08, 0x20, 0x03, 0x28, 0x09, 0x42, 0x10, 0xfa, 0x42, 0x0d, 0x92, 0x01, 0x0a, 0x08, 0x00, 0x10,
- 0x08, 0x22, 0x04, 0x72, 0x02, 0x18, 0x40, 0x52, 0x16, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x46,
- 0x72, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x6c, 0x61, 0x6e, 0x49, 0x64, 0x73, 0x1a,
- 0xae, 0x02, 0x0a, 0x18, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65,
- 0x64, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x12, 0xd6, 0x01, 0x0a,
- 0x0b, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 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, 0x55, 0x6e, 0x6d, 0x61, 0x72, 0x73,
- 0x68, 0x61, 0x6c, 0x38, 0x42, 0x79, 0x74, 0x65, 0x73, 0x52, 0x0a, 0x67, 0x61, 0x74, 0x65, 0x77,
- 0x61, 0x79, 0x45, 0x75, 0x69, 0x12, 0x39, 0x0a, 0x13, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74,
- 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01,
- 0x28, 0x0c, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x7a, 0x03, 0x18, 0x80, 0x10, 0x52, 0x12, 0x61, 0x75,
- 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x64, 0x65,
- 0x42, 0x15, 0x0a, 0x0e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x67, 0x61, 0x74, 0x65, 0x77,
- 0x61, 0x79, 0x12, 0x03, 0xf8, 0x42, 0x01, 0x22, 0x8a, 0x01, 0x0a, 0x17, 0x41, 0x75, 0x74, 0x68,
- 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x52, 0x65, 0x71, 0x75,
- 0x65, 0x73, 0x74, 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, 0x20, 0x0a, 0x07, 0x61, 0x70, 0x69, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20,
- 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x06, 0x61, 0x70,
- 0x69, 0x4b, 0x65, 0x79, 0x22, 0xe6, 0x01, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f,
- 0x42, 0x79, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x45, 0x55, 0x49, 0x52, 0x65, 0x71, 0x75,
- 0x65, 0x73, 0x74, 0x12, 0xc7, 0x01, 0x0a, 0x03, 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, 0x55, 0x6e, 0x6d, 0x61, 0x72, 0x73, 0x68,
- 0x61, 0x6c, 0x38, 0x42, 0x79, 0x74, 0x65, 0x73, 0x52, 0x03, 0x65, 0x75, 0x69, 0x22, 0x94, 0x02,
- 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x42, 0x79, 0x47, 0x61, 0x74, 0x65, 0x77,
- 0x61, 0x79, 0x45, 0x55, 0x49, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0xc7, 0x01,
- 0x0a, 0x03, 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,
+ 0x66, 0x69, 0x65, 0x72, 0x73, 0x12, 0x25, 0x0a, 0x07, 0x71, 0x72, 0x5f, 0x63, 0x6f, 0x64, 0x65,
+ 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x0a, 0xfa, 0x42, 0x07, 0x7a, 0x05, 0x10, 0x00, 0x18,
+ 0x80, 0x08, 0x48, 0x00, 0x52, 0x06, 0x71, 0x72, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x5b, 0x0a, 0x0c,
+ 0x63, 0x6f, 0x6c, 0x6c, 0x61, 0x62, 0x6f, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01,
+ 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e,
+ 0x2e, 0x76, 0x33, 0x2e, 0x4f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+ 0x4f, 0x72, 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, 0x0c, 0x63, 0x6f, 0x6c,
+ 0x6c, 0x61, 0x62, 0x6f, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x56, 0x0a, 0x11, 0x74, 0x61, 0x72,
+ 0x67, 0x65, 0x74, 0x5f, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x04,
+ 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, 0x0f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x49,
+ 0x64, 0x12, 0xd2, 0x01, 0x0a, 0x1d, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x67, 0x61, 0x74,
+ 0x65, 0x77, 0x61, 0x79, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x61, 0x64, 0x64, 0x72,
+ 0x65, 0x73, 0x73, 0x18, 0x05, 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, 0x1a, 0x74, 0x61, 0x72, 0x67,
+ 0x65, 0x74, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x41,
+ 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x4e, 0x0a, 0x10, 0x63, 0x75, 0x70, 0x73, 0x5f, 0x72,
+ 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b,
+ 0x32, 0x1f, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76,
+ 0x33, 0x2e, 0x43, 0x55, 0x50, 0x53, 0x52, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f,
+ 0x6e, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0f, 0x63, 0x75, 0x70, 0x73, 0x52, 0x65, 0x64, 0x69, 0x72,
+ 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x42, 0x0a, 0x18, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74,
+ 0x5f, 0x66, 0x72, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x79, 0x5f, 0x70, 0x6c, 0x61, 0x6e, 0x5f,
+ 0x69, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x42, 0x09, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x18,
+ 0x40, 0x18, 0x01, 0x52, 0x15, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x46, 0x72, 0x65, 0x71, 0x75,
+ 0x65, 0x6e, 0x63, 0x79, 0x50, 0x6c, 0x61, 0x6e, 0x49, 0x64, 0x12, 0x4b, 0x0a, 0x19, 0x74, 0x61,
+ 0x72, 0x67, 0x65, 0x74, 0x5f, 0x66, 0x72, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x79, 0x5f, 0x70,
+ 0x6c, 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x09, 0x42, 0x10, 0xfa,
+ 0x42, 0x0d, 0x92, 0x01, 0x0a, 0x08, 0x00, 0x10, 0x08, 0x22, 0x04, 0x72, 0x02, 0x18, 0x40, 0x52,
+ 0x16, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x46, 0x72, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x79,
+ 0x50, 0x6c, 0x61, 0x6e, 0x49, 0x64, 0x73, 0x1a, 0xae, 0x02, 0x0a, 0x18, 0x41, 0x75, 0x74, 0x68,
+ 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x64, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66,
+ 0x69, 0x65, 0x72, 0x73, 0x12, 0xd6, 0x01, 0x0a, 0x0b, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79,
+ 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, 0x38, 0x42, 0x79, 0x74,
- 0x65, 0x73, 0x52, 0x03, 0x65, 0x75, 0x69, 0x12, 0x2b, 0x0a, 0x11, 0x73, 0x75, 0x70, 0x70, 0x6f,
- 0x72, 0x74, 0x73, 0x5f, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x18, 0x02, 0x20, 0x01,
- 0x28, 0x08, 0x52, 0x10, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x43, 0x6c, 0x61, 0x69,
- 0x6d, 0x69, 0x6e, 0x67, 0x32, 0xc2, 0x07, 0x0a, 0x17, 0x45, 0x6e, 0x64, 0x44, 0x65, 0x76, 0x69,
- 0x63, 0x65, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72,
- 0x12, 0x6c, 0x0a, 0x05, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x12, 0x25, 0x2e, 0x74, 0x74, 0x6e, 0x2e,
- 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x43, 0x6c, 0x61, 0x69, 0x6d,
- 0x45, 0x6e, 0x64, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
- 0x1a, 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, 0x22, 0x16, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x10, 0x3a, 0x01,
- 0x2a, 0x22, 0x0b, 0x2f, 0x65, 0x64, 0x63, 0x73, 0x2f, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x12, 0x91,
- 0x01, 0x0a, 0x07, 0x55, 0x6e, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x12, 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,
+ 0x73, 0x2e, 0x55, 0x6e, 0x6d, 0x61, 0x72, 0x73, 0x68, 0x61, 0x6c, 0x38, 0x42, 0x79, 0x74, 0x65,
+ 0x73, 0x52, 0x0a, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x45, 0x75, 0x69, 0x12, 0x39, 0x0a,
+ 0x13, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f,
+ 0x63, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x7a,
+ 0x03, 0x18, 0x80, 0x10, 0x52, 0x12, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61,
+ 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x64, 0x65, 0x42, 0x15, 0x0a, 0x0e, 0x73, 0x6f, 0x75, 0x72,
+ 0x63, 0x65, 0x5f, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x12, 0x03, 0xf8, 0x42, 0x01, 0x22,
+ 0x8a, 0x01, 0x0a, 0x17, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x47, 0x61, 0x74,
+ 0x65, 0x77, 0x61, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 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, 0x20, 0x0a, 0x07, 0x61, 0x70,
+ 0x69, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04,
+ 0x72, 0x02, 0x10, 0x01, 0x52, 0x06, 0x61, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x22, 0xe6, 0x01, 0x0a,
+ 0x1a, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x42, 0x79, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61,
+ 0x79, 0x45, 0x55, 0x49, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0xc7, 0x01, 0x0a, 0x03,
+ 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, 0x55, 0x6e, 0x6d, 0x61, 0x72, 0x73, 0x68, 0x61, 0x6c, 0x38, 0x42, 0x79, 0x74, 0x65, 0x73,
+ 0x52, 0x03, 0x65, 0x75, 0x69, 0x22, 0x94, 0x02, 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66,
+ 0x6f, 0x42, 0x79, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x45, 0x55, 0x49, 0x52, 0x65, 0x73,
+ 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0xc7, 0x01, 0x0a, 0x03, 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, 0x55, 0x6e, 0x6d, 0x61, 0x72,
+ 0x73, 0x68, 0x61, 0x6c, 0x38, 0x42, 0x79, 0x74, 0x65, 0x73, 0x52, 0x03, 0x65, 0x75, 0x69, 0x12,
+ 0x2b, 0x0a, 0x11, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x5f, 0x63, 0x6c, 0x61, 0x69,
+ 0x6d, 0x69, 0x6e, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x73, 0x75, 0x70, 0x70,
+ 0x6f, 0x72, 0x74, 0x73, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x32, 0xc2, 0x07, 0x0a,
+ 0x17, 0x45, 0x6e, 0x64, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x69,
+ 0x6e, 0x67, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x6c, 0x0a, 0x05, 0x43, 0x6c, 0x61, 0x69,
+ 0x6d, 0x12, 0x25, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e,
+ 0x76, 0x33, 0x2e, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x45, 0x6e, 0x64, 0x44, 0x65, 0x76, 0x69, 0x63,
+ 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 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, 0x22, 0x16,
+ 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x10, 0x3a, 0x01, 0x2a, 0x22, 0x0b, 0x2f, 0x65, 0x64, 0x63, 0x73,
+ 0x2f, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x12, 0x91, 0x01, 0x0a, 0x07, 0x55, 0x6e, 0x63, 0x6c, 0x61,
+ 0x69, 0x6d, 0x12, 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, 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, 0x48, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x42, 0x2a, 0x40, 0x2f, 0x65, 0x64, 0x63, 0x73, 0x2f,
+ 0x63, 0x6c, 0x61, 0x69, 0x6d, 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, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, 0x7b,
+ 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x12, 0x82, 0x01, 0x0a, 0x10, 0x47,
+ 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x42, 0x79, 0x4a, 0x6f, 0x69, 0x6e, 0x45, 0x55, 0x49, 0x12,
+ 0x27, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33,
+ 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x42, 0x79, 0x4a, 0x6f, 0x69, 0x6e, 0x45, 0x55,
+ 0x49, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c,
+ 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66,
+ 0x6f, 0x42, 0x79, 0x4a, 0x6f, 0x69, 0x6e, 0x45, 0x55, 0x49, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
+ 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x3a, 0x01, 0x2a, 0x22, 0x10, 0x2f,
+ 0x65, 0x64, 0x63, 0x73, 0x2f, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x2f, 0x69, 0x6e, 0x66, 0x6f, 0x12,
+ 0xa8, 0x01, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x53, 0x74, 0x61, 0x74,
+ 0x75, 0x73, 0x12, 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, 0x1a, 0x26, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c,
+ 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x61,
+ 0x69, 0x6d, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
+ 0x22, 0x48, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x42, 0x12, 0x40, 0x2f, 0x65, 0x64, 0x63, 0x73, 0x2f,
+ 0x63, 0x6c, 0x61, 0x69, 0x6d, 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, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, 0x7b,
+ 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x12, 0xa5, 0x01, 0x0a, 0x14, 0x41,
+ 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x12, 0x2b, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61,
+ 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x41, 0x70,
+ 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 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, 0x48, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x42,
- 0x2a, 0x40, 0x2f, 0x65, 0x64, 0x63, 0x73, 0x2f, 0x63, 0x6c, 0x61, 0x69, 0x6d, 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, 0x64,
- 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69,
- 0x64, 0x7d, 0x12, 0x82, 0x01, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x42, 0x79,
- 0x4a, 0x6f, 0x69, 0x6e, 0x45, 0x55, 0x49, 0x12, 0x27, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f,
+ 0x3a, 0x01, 0x2a, 0x22, 0x3d, 0x2f, 0x65, 0x64, 0x63, 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, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69,
+ 0x7a, 0x65, 0x12, 0x8f, 0x01, 0x0a, 0x16, 0x55, 0x6e, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69,
+ 0x7a, 0x65, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 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, 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, 0x35, 0x82,
+ 0xd3, 0xe4, 0x93, 0x02, 0x2f, 0x2a, 0x2d, 0x2f, 0x65, 0x64, 0x63, 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, 0x7d, 0x2f, 0x61, 0x75, 0x74, 0x68, 0x6f,
+ 0x72, 0x69, 0x7a, 0x65, 0x1a, 0x3b, 0x92, 0x41, 0x38, 0x12, 0x36, 0x43, 0x6c, 0x61, 0x69, 0x6d,
+ 0x20, 0x61, 0x6e, 0x64, 0x20, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x20, 0x65, 0x6e, 0x64, 0x20,
+ 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x20, 0x6f, 0x6e, 0x20, 0x65, 0x78, 0x74, 0x65, 0x72,
+ 0x6e, 0x61, 0x6c, 0x20, 0x4a, 0x6f, 0x69, 0x6e, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73,
+ 0x2e, 0x32, 0xa3, 0x03, 0x0a, 0x1c, 0x45, 0x6e, 0x64, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x42,
+ 0x61, 0x74, 0x63, 0x68, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x72, 0x76,
+ 0x65, 0x72, 0x12, 0xac, 0x01, 0x0a, 0x07, 0x55, 0x6e, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x12, 0x2d,
+ 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e,
+ 0x42, 0x61, 0x74, 0x63, 0x68, 0x55, 0x6e, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x45, 0x6e, 0x64, 0x44,
+ 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e,
+ 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x42,
+ 0x61, 0x74, 0x63, 0x68, 0x55, 0x6e, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x45, 0x6e, 0x64, 0x44, 0x65,
+ 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x42, 0x82,
+ 0xd3, 0xe4, 0x93, 0x02, 0x3c, 0x2a, 0x3a, 0x2f, 0x65, 0x64, 0x63, 0x73, 0x2f, 0x63, 0x6c, 0x61,
+ 0x69, 0x6d, 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, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, 0x62, 0x61, 0x74, 0x63,
+ 0x68, 0x12, 0x8b, 0x01, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x42, 0x79, 0x4a,
+ 0x6f, 0x69, 0x6e, 0x45, 0x55, 0x49, 0x73, 0x12, 0x28, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f,
0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f,
- 0x42, 0x79, 0x4a, 0x6f, 0x69, 0x6e, 0x45, 0x55, 0x49, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
- 0x1a, 0x28, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76,
- 0x33, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x42, 0x79, 0x4a, 0x6f, 0x69, 0x6e, 0x45,
- 0x55, 0x49, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93,
- 0x02, 0x15, 0x3a, 0x01, 0x2a, 0x22, 0x10, 0x2f, 0x65, 0x64, 0x63, 0x73, 0x2f, 0x63, 0x6c, 0x61,
- 0x69, 0x6d, 0x2f, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0xa8, 0x01, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x43,
- 0x6c, 0x61, 0x69, 0x6d, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 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,
- 0x1a, 0x26, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76,
- 0x33, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73,
- 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x48, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x42,
- 0x12, 0x40, 0x2f, 0x65, 0x64, 0x63, 0x73, 0x2f, 0x63, 0x6c, 0x61, 0x69, 0x6d, 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, 0x64,
- 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, 0x7b, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69,
- 0x64, 0x7d, 0x12, 0xa5, 0x01, 0x0a, 0x14, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65,
- 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2b, 0x2e, 0x74, 0x74,
- 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x41, 0x75, 0x74,
- 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f,
- 0x6e, 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, 0x48, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x42, 0x3a, 0x01, 0x2a, 0x22, 0x3d, 0x2f, 0x65, 0x64,
- 0x63, 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, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x12, 0x8f, 0x01, 0x0a, 0x16, 0x55,
- 0x6e, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63,
- 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 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, 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, 0x35, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2f, 0x2a, 0x2d, 0x2f,
- 0x65, 0x64, 0x63, 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, 0x7d, 0x2f, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x1a, 0x3b, 0x92, 0x41,
- 0x38, 0x12, 0x36, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x6d, 0x61, 0x6e,
- 0x61, 0x67, 0x65, 0x20, 0x65, 0x6e, 0x64, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x20,
- 0x6f, 0x6e, 0x20, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x4a, 0x6f, 0x69, 0x6e,
- 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2e, 0x32, 0xa3, 0x03, 0x0a, 0x1c, 0x45, 0x6e,
- 0x64, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x42, 0x61, 0x74, 0x63, 0x68, 0x43, 0x6c, 0x61, 0x69,
- 0x6d, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0xac, 0x01, 0x0a, 0x07, 0x55,
- 0x6e, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x12, 0x2d, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72,
- 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x55, 0x6e, 0x63,
- 0x6c, 0x61, 0x69, 0x6d, 0x45, 0x6e, 0x64, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65,
- 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61,
- 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x55, 0x6e, 0x63, 0x6c,
- 0x61, 0x69, 0x6d, 0x45, 0x6e, 0x64, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73,
- 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x42, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x3c, 0x2a, 0x3a, 0x2f,
- 0x65, 0x64, 0x63, 0x73, 0x2f, 0x63, 0x6c, 0x61, 0x69, 0x6d, 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, 0x64, 0x65, 0x76, 0x69,
- 0x63, 0x65, 0x73, 0x2f, 0x62, 0x61, 0x74, 0x63, 0x68, 0x12, 0x8b, 0x01, 0x0a, 0x11, 0x47, 0x65,
- 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x42, 0x79, 0x4a, 0x6f, 0x69, 0x6e, 0x45, 0x55, 0x49, 0x73, 0x12,
- 0x28, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33,
- 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x42, 0x79, 0x4a, 0x6f, 0x69, 0x6e, 0x45, 0x55,
- 0x49, 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, 0x47, 0x65, 0x74, 0x49, 0x6e,
- 0x66, 0x6f, 0x42, 0x79, 0x4a, 0x6f, 0x69, 0x6e, 0x45, 0x55, 0x49, 0x73, 0x52, 0x65, 0x73, 0x70,
- 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x21, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1b, 0x3a, 0x01, 0x2a, 0x22,
- 0x16, 0x2f, 0x65, 0x64, 0x63, 0x73, 0x2f, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x2f, 0x69, 0x6e, 0x66,
- 0x6f, 0x2f, 0x62, 0x61, 0x74, 0x63, 0x68, 0x1a, 0x46, 0x92, 0x41, 0x43, 0x12, 0x41, 0x43, 0x6c,
- 0x61, 0x69, 0x6d, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x20, 0x62,
- 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x65, 0x6e, 0x64, 0x20, 0x64, 0x65,
- 0x76, 0x69, 0x63, 0x65, 0x73, 0x20, 0x6f, 0x6e, 0x20, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61,
- 0x6c, 0x20, 0x4a, 0x6f, 0x69, 0x6e, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2e, 0x32,
- 0xcc, 0x04, 0x0a, 0x15, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x43, 0x6c, 0x61, 0x69, 0x6d,
- 0x69, 0x6e, 0x67, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x68, 0x0a, 0x05, 0x43, 0x6c, 0x61,
- 0x69, 0x6d, 0x12, 0x23, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e,
- 0x2e, 0x76, 0x33, 0x2e, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79,
- 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 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, 0x22, 0x16, 0x82, 0xd3, 0xe4,
- 0x93, 0x02, 0x10, 0x3a, 0x01, 0x2a, 0x22, 0x0b, 0x2f, 0x67, 0x63, 0x6c, 0x73, 0x2f, 0x63, 0x6c,
- 0x61, 0x69, 0x6d, 0x12, 0x91, 0x01, 0x0a, 0x10, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a,
- 0x65, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x12, 0x27, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c,
- 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72,
- 0x69, 0x7a, 0x65, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 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, 0x3c, 0x82, 0xd3, 0xe4, 0x93, 0x02,
- 0x36, 0x3a, 0x01, 0x2a, 0x22, 0x31, 0x2f, 0x67, 0x63, 0x6c, 0x73, 0x2f, 0x67, 0x61, 0x74, 0x65,
- 0x77, 0x61, 0x79, 0x73, 0x2f, 0x7b, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x5f, 0x69, 0x64,
- 0x73, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x61, 0x75,
- 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x12, 0x7f, 0x0a, 0x12, 0x55, 0x6e, 0x61, 0x75, 0x74,
- 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x12, 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, 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, 0x2d, 0x82, 0xd3, 0xe4, 0x93, 0x02,
- 0x27, 0x2a, 0x25, 0x2f, 0x67, 0x63, 0x6c, 0x73, 0x2f, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79,
- 0x73, 0x2f, 0x7b, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x61,
- 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x12, 0x8b, 0x01, 0x0a, 0x13, 0x47, 0x65, 0x74,
- 0x49, 0x6e, 0x66, 0x6f, 0x42, 0x79, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x45, 0x55, 0x49,
- 0x12, 0x2a, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76,
- 0x33, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x42, 0x79, 0x47, 0x61, 0x74, 0x65, 0x77,
- 0x61, 0x79, 0x45, 0x55, 0x49, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x74,
+ 0x42, 0x79, 0x4a, 0x6f, 0x69, 0x6e, 0x45, 0x55, 0x49, 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, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x42, 0x79, 0x4a, 0x6f, 0x69, 0x6e,
+ 0x45, 0x55, 0x49, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x21, 0x82, 0xd3,
+ 0xe4, 0x93, 0x02, 0x1b, 0x3a, 0x01, 0x2a, 0x22, 0x16, 0x2f, 0x65, 0x64, 0x63, 0x73, 0x2f, 0x63,
+ 0x6c, 0x61, 0x69, 0x6d, 0x2f, 0x69, 0x6e, 0x66, 0x6f, 0x2f, 0x62, 0x61, 0x74, 0x63, 0x68, 0x1a,
+ 0x46, 0x92, 0x41, 0x43, 0x12, 0x41, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x20, 0x61, 0x6e, 0x64, 0x20,
+ 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x20, 0x62, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x20, 0x6f,
+ 0x66, 0x20, 0x65, 0x6e, 0x64, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x20, 0x6f, 0x6e,
+ 0x20, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x4a, 0x6f, 0x69, 0x6e, 0x20, 0x53,
+ 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2e, 0x32, 0xbc, 0x05, 0x0a, 0x15, 0x47, 0x61, 0x74, 0x65,
+ 0x77, 0x61, 0x79, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x72, 0x76, 0x65,
+ 0x72, 0x12, 0x68, 0x0a, 0x05, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x12, 0x23, 0x2e, 0x74, 0x74, 0x6e,
+ 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x43, 0x6c, 0x61, 0x69,
+ 0x6d, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
+ 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, 0x22, 0x16, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x10, 0x3a, 0x01, 0x2a, 0x22, 0x0b,
+ 0x2f, 0x67, 0x63, 0x6c, 0x73, 0x2f, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x12, 0x67, 0x0a, 0x07, 0x55,
+ 0x6e, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x12, 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, 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, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x2a, 0x18, 0x2f, 0x67, 0x63, 0x6c,
+ 0x73, 0x2f, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x2f, 0x7b, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79,
+ 0x5f, 0x69, 0x64, 0x7d, 0x12, 0x8b, 0x01, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f,
+ 0x42, 0x79, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x45, 0x55, 0x49, 0x12, 0x2a, 0x2e, 0x74,
0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x47, 0x65,
0x74, 0x49, 0x6e, 0x66, 0x6f, 0x42, 0x79, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x45, 0x55,
- 0x49, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02,
- 0x15, 0x3a, 0x01, 0x2a, 0x22, 0x10, 0x2f, 0x67, 0x63, 0x6c, 0x73, 0x2f, 0x63, 0x6c, 0x61, 0x69,
- 0x6d, 0x2f, 0x69, 0x6e, 0x66, 0x6f, 0x1a, 0x26, 0x92, 0x41, 0x23, 0x12, 0x21, 0x43, 0x6c, 0x61,
- 0x69, 0x6d, 0x20, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20,
- 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x20, 0x63, 0x6c, 0x61, 0x69, 0x6d, 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,
+ 0x49, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c,
+ 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66,
+ 0x6f, 0x42, 0x79, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x45, 0x55, 0x49, 0x52, 0x65, 0x73,
+ 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x3a, 0x01, 0x2a,
+ 0x22, 0x10, 0x2f, 0x67, 0x63, 0x6c, 0x73, 0x2f, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x2f, 0x69, 0x6e,
+ 0x66, 0x6f, 0x12, 0x94, 0x01, 0x0a, 0x10, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65,
+ 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x12, 0x27, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f,
+ 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69,
+ 0x7a, 0x65, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 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, 0x3f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x36,
+ 0x3a, 0x01, 0x2a, 0x22, 0x31, 0x2f, 0x67, 0x63, 0x6c, 0x73, 0x2f, 0x67, 0x61, 0x74, 0x65, 0x77,
+ 0x61, 0x79, 0x73, 0x2f, 0x7b, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x5f, 0x69, 0x64, 0x73,
+ 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x61, 0x75, 0x74,
+ 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x88, 0x02, 0x01, 0x12, 0x82, 0x01, 0x0a, 0x12, 0x55, 0x6e,
+ 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79,
+ 0x12, 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, 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, 0x30, 0x82, 0xd3,
+ 0xe4, 0x93, 0x02, 0x27, 0x2a, 0x25, 0x2f, 0x67, 0x63, 0x6c, 0x73, 0x2f, 0x67, 0x61, 0x74, 0x65,
+ 0x77, 0x61, 0x79, 0x73, 0x2f, 0x7b, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x5f, 0x69, 0x64,
+ 0x7d, 0x2f, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x88, 0x02, 0x01, 0x1a, 0x26,
+ 0x92, 0x41, 0x23, 0x12, 0x21, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x20, 0x67, 0x61, 0x74, 0x65, 0x77,
+ 0x61, 0x79, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x20, 0x63,
+ 0x6c, 0x61, 0x69, 0x6d, 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 (
@@ -1772,23 +1787,25 @@ var file_ttn_lorawan_v3_deviceclaimingserver_proto_depIdxs = []int32{
7, // 23: ttn.lorawan.v3.EndDeviceBatchClaimingServer.Unclaim:input_type -> ttn.lorawan.v3.BatchUnclaimEndDevicesRequest
4, // 24: ttn.lorawan.v3.EndDeviceBatchClaimingServer.GetInfoByJoinEUIs:input_type -> ttn.lorawan.v3.GetInfoByJoinEUIsRequest
10, // 25: ttn.lorawan.v3.GatewayClaimingServer.Claim:input_type -> ttn.lorawan.v3.ClaimGatewayRequest
- 11, // 26: ttn.lorawan.v3.GatewayClaimingServer.AuthorizeGateway:input_type -> ttn.lorawan.v3.AuthorizeGatewayRequest
- 22, // 27: ttn.lorawan.v3.GatewayClaimingServer.UnauthorizeGateway:input_type -> ttn.lorawan.v3.GatewayIdentifiers
- 12, // 28: ttn.lorawan.v3.GatewayClaimingServer.GetInfoByGatewayEUI:input_type -> ttn.lorawan.v3.GetInfoByGatewayEUIRequest
- 20, // 29: ttn.lorawan.v3.EndDeviceClaimingServer.Claim:output_type -> ttn.lorawan.v3.EndDeviceIdentifiers
- 25, // 30: ttn.lorawan.v3.EndDeviceClaimingServer.Unclaim:output_type -> google.protobuf.Empty
- 3, // 31: ttn.lorawan.v3.EndDeviceClaimingServer.GetInfoByJoinEUI:output_type -> ttn.lorawan.v3.GetInfoByJoinEUIResponse
- 6, // 32: ttn.lorawan.v3.EndDeviceClaimingServer.GetClaimStatus:output_type -> ttn.lorawan.v3.GetClaimStatusResponse
- 25, // 33: ttn.lorawan.v3.EndDeviceClaimingServer.AuthorizeApplication:output_type -> google.protobuf.Empty
- 25, // 34: ttn.lorawan.v3.EndDeviceClaimingServer.UnauthorizeApplication:output_type -> google.protobuf.Empty
- 8, // 35: ttn.lorawan.v3.EndDeviceBatchClaimingServer.Unclaim:output_type -> ttn.lorawan.v3.BatchUnclaimEndDevicesResponse
- 5, // 36: ttn.lorawan.v3.EndDeviceBatchClaimingServer.GetInfoByJoinEUIs:output_type -> ttn.lorawan.v3.GetInfoByJoinEUIsResponse
- 22, // 37: ttn.lorawan.v3.GatewayClaimingServer.Claim:output_type -> ttn.lorawan.v3.GatewayIdentifiers
- 25, // 38: ttn.lorawan.v3.GatewayClaimingServer.AuthorizeGateway:output_type -> google.protobuf.Empty
- 25, // 39: ttn.lorawan.v3.GatewayClaimingServer.UnauthorizeGateway:output_type -> google.protobuf.Empty
+ 22, // 26: ttn.lorawan.v3.GatewayClaimingServer.Unclaim:input_type -> ttn.lorawan.v3.GatewayIdentifiers
+ 12, // 27: ttn.lorawan.v3.GatewayClaimingServer.GetInfoByGatewayEUI:input_type -> ttn.lorawan.v3.GetInfoByGatewayEUIRequest
+ 11, // 28: ttn.lorawan.v3.GatewayClaimingServer.AuthorizeGateway:input_type -> ttn.lorawan.v3.AuthorizeGatewayRequest
+ 22, // 29: ttn.lorawan.v3.GatewayClaimingServer.UnauthorizeGateway:input_type -> ttn.lorawan.v3.GatewayIdentifiers
+ 20, // 30: ttn.lorawan.v3.EndDeviceClaimingServer.Claim:output_type -> ttn.lorawan.v3.EndDeviceIdentifiers
+ 25, // 31: ttn.lorawan.v3.EndDeviceClaimingServer.Unclaim:output_type -> google.protobuf.Empty
+ 3, // 32: ttn.lorawan.v3.EndDeviceClaimingServer.GetInfoByJoinEUI:output_type -> ttn.lorawan.v3.GetInfoByJoinEUIResponse
+ 6, // 33: ttn.lorawan.v3.EndDeviceClaimingServer.GetClaimStatus:output_type -> ttn.lorawan.v3.GetClaimStatusResponse
+ 25, // 34: ttn.lorawan.v3.EndDeviceClaimingServer.AuthorizeApplication:output_type -> google.protobuf.Empty
+ 25, // 35: ttn.lorawan.v3.EndDeviceClaimingServer.UnauthorizeApplication:output_type -> google.protobuf.Empty
+ 8, // 36: ttn.lorawan.v3.EndDeviceBatchClaimingServer.Unclaim:output_type -> ttn.lorawan.v3.BatchUnclaimEndDevicesResponse
+ 5, // 37: ttn.lorawan.v3.EndDeviceBatchClaimingServer.GetInfoByJoinEUIs:output_type -> ttn.lorawan.v3.GetInfoByJoinEUIsResponse
+ 22, // 38: ttn.lorawan.v3.GatewayClaimingServer.Claim:output_type -> ttn.lorawan.v3.GatewayIdentifiers
+ 25, // 39: ttn.lorawan.v3.GatewayClaimingServer.Unclaim:output_type -> google.protobuf.Empty
13, // 40: ttn.lorawan.v3.GatewayClaimingServer.GetInfoByGatewayEUI:output_type -> ttn.lorawan.v3.GetInfoByGatewayEUIResponse
- 29, // [29:41] is the sub-list for method output_type
- 17, // [17:29] is the sub-list for method input_type
+ 25, // 41: ttn.lorawan.v3.GatewayClaimingServer.AuthorizeGateway:output_type -> google.protobuf.Empty
+ 25, // 42: ttn.lorawan.v3.GatewayClaimingServer.UnauthorizeGateway:output_type -> google.protobuf.Empty
+ 30, // [30:43] is the sub-list for method output_type
+ 17, // [17:30] is the sub-list for method input_type
17, // [17:17] is the sub-list for extension type_name
17, // [17:17] is the sub-list for extension extendee
0, // [0:17] is the sub-list for field type_name
diff --git a/pkg/ttnpb/deviceclaimingserver.pb.gw.go b/pkg/ttnpb/deviceclaimingserver.pb.gw.go
index ef61197718..de96966474 100644
--- a/pkg/ttnpb/deviceclaimingserver.pb.gw.go
+++ b/pkg/ttnpb/deviceclaimingserver.pb.gw.go
@@ -497,6 +497,102 @@ func local_request_GatewayClaimingServer_Claim_0(ctx context.Context, marshaler
}
+var (
+ filter_GatewayClaimingServer_Unclaim_0 = &utilities.DoubleArray{Encoding: map[string]int{"gateway_id": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}}
+)
+
+func request_GatewayClaimingServer_Unclaim_0(ctx context.Context, marshaler runtime.Marshaler, client GatewayClaimingServerClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+ var protoReq GatewayIdentifiers
+ var metadata runtime.ServerMetadata
+
+ var (
+ val string
+ ok bool
+ err error
+ _ = err
+ )
+
+ val, ok = pathParams["gateway_id"]
+ if !ok {
+ return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "gateway_id")
+ }
+
+ protoReq.GatewayId, err = runtime.String(val)
+ if err != nil {
+ return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "gateway_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_GatewayClaimingServer_Unclaim_0); err != nil {
+ return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+ }
+
+ msg, err := client.Unclaim(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
+ return msg, metadata, err
+
+}
+
+func local_request_GatewayClaimingServer_Unclaim_0(ctx context.Context, marshaler runtime.Marshaler, server GatewayClaimingServerServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+ var protoReq GatewayIdentifiers
+ var metadata runtime.ServerMetadata
+
+ var (
+ val string
+ ok bool
+ err error
+ _ = err
+ )
+
+ val, ok = pathParams["gateway_id"]
+ if !ok {
+ return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "gateway_id")
+ }
+
+ protoReq.GatewayId, err = runtime.String(val)
+ if err != nil {
+ return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "gateway_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_GatewayClaimingServer_Unclaim_0); err != nil {
+ return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+ }
+
+ msg, err := server.Unclaim(ctx, &protoReq)
+ return msg, metadata, err
+
+}
+
+func request_GatewayClaimingServer_GetInfoByGatewayEUI_0(ctx context.Context, marshaler runtime.Marshaler, client GatewayClaimingServerClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+ var protoReq GetInfoByGatewayEUIRequest
+ 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)
+ }
+
+ msg, err := client.GetInfoByGatewayEUI(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
+ return msg, metadata, err
+
+}
+
+func local_request_GatewayClaimingServer_GetInfoByGatewayEUI_0(ctx context.Context, marshaler runtime.Marshaler, server GatewayClaimingServerServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+ var protoReq GetInfoByGatewayEUIRequest
+ 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)
+ }
+
+ msg, err := server.GetInfoByGatewayEUI(ctx, &protoReq)
+ return msg, metadata, err
+
+}
+
func request_GatewayClaimingServer_AuthorizeGateway_0(ctx context.Context, marshaler runtime.Marshaler, client GatewayClaimingServerClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq AuthorizeGatewayRequest
var metadata runtime.ServerMetadata
@@ -627,32 +723,6 @@ func local_request_GatewayClaimingServer_UnauthorizeGateway_0(ctx context.Contex
}
-func request_GatewayClaimingServer_GetInfoByGatewayEUI_0(ctx context.Context, marshaler runtime.Marshaler, client GatewayClaimingServerClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
- var protoReq GetInfoByGatewayEUIRequest
- 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)
- }
-
- msg, err := client.GetInfoByGatewayEUI(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
- return msg, metadata, err
-
-}
-
-func local_request_GatewayClaimingServer_GetInfoByGatewayEUI_0(ctx context.Context, marshaler runtime.Marshaler, server GatewayClaimingServerServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
- var protoReq GetInfoByGatewayEUIRequest
- 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)
- }
-
- msg, err := server.GetInfoByGatewayEUI(ctx, &protoReq)
- return msg, metadata, err
-
-}
-
// RegisterEndDeviceClaimingServerHandlerServer registers the http handlers for service EndDeviceClaimingServer to "mux".
// UnaryRPC :call EndDeviceClaimingServerServer directly.
// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906.
@@ -902,7 +972,7 @@ func RegisterGatewayClaimingServerHandlerServer(ctx context.Context, mux *runtim
})
- mux.Handle("POST", pattern_GatewayClaimingServer_AuthorizeGateway_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+ mux.Handle("DELETE", pattern_GatewayClaimingServer_Unclaim_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
@@ -910,12 +980,12 @@ func RegisterGatewayClaimingServerHandlerServer(ctx context.Context, mux *runtim
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error
var annotatedContext context.Context
- annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/ttn.lorawan.v3.GatewayClaimingServer/AuthorizeGateway", runtime.WithHTTPPathPattern("/gcls/gateways/{gateway_ids.gateway_id}/authorize"))
+ annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/ttn.lorawan.v3.GatewayClaimingServer/Unclaim", runtime.WithHTTPPathPattern("/gcls/claim/{gateway_id}"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
- resp, md, err := local_request_GatewayClaimingServer_AuthorizeGateway_0(annotatedContext, inboundMarshaler, server, req, pathParams)
+ resp, md, err := local_request_GatewayClaimingServer_Unclaim_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 {
@@ -923,11 +993,11 @@ func RegisterGatewayClaimingServerHandlerServer(ctx context.Context, mux *runtim
return
}
- forward_GatewayClaimingServer_AuthorizeGateway_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+ forward_GatewayClaimingServer_Unclaim_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
- mux.Handle("DELETE", pattern_GatewayClaimingServer_UnauthorizeGateway_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+ mux.Handle("POST", pattern_GatewayClaimingServer_GetInfoByGatewayEUI_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
@@ -935,12 +1005,12 @@ func RegisterGatewayClaimingServerHandlerServer(ctx context.Context, mux *runtim
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error
var annotatedContext context.Context
- annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/ttn.lorawan.v3.GatewayClaimingServer/UnauthorizeGateway", runtime.WithHTTPPathPattern("/gcls/gateways/{gateway_id}/authorize"))
+ annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/ttn.lorawan.v3.GatewayClaimingServer/GetInfoByGatewayEUI", runtime.WithHTTPPathPattern("/gcls/claim/info"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
- resp, md, err := local_request_GatewayClaimingServer_UnauthorizeGateway_0(annotatedContext, inboundMarshaler, server, req, pathParams)
+ resp, md, err := local_request_GatewayClaimingServer_GetInfoByGatewayEUI_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 {
@@ -948,11 +1018,11 @@ func RegisterGatewayClaimingServerHandlerServer(ctx context.Context, mux *runtim
return
}
- forward_GatewayClaimingServer_UnauthorizeGateway_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+ forward_GatewayClaimingServer_GetInfoByGatewayEUI_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
- mux.Handle("POST", pattern_GatewayClaimingServer_GetInfoByGatewayEUI_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+ mux.Handle("POST", pattern_GatewayClaimingServer_AuthorizeGateway_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
@@ -960,12 +1030,12 @@ func RegisterGatewayClaimingServerHandlerServer(ctx context.Context, mux *runtim
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error
var annotatedContext context.Context
- annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/ttn.lorawan.v3.GatewayClaimingServer/GetInfoByGatewayEUI", runtime.WithHTTPPathPattern("/gcls/claim/info"))
+ annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/ttn.lorawan.v3.GatewayClaimingServer/AuthorizeGateway", runtime.WithHTTPPathPattern("/gcls/gateways/{gateway_ids.gateway_id}/authorize"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
- resp, md, err := local_request_GatewayClaimingServer_GetInfoByGatewayEUI_0(annotatedContext, inboundMarshaler, server, req, pathParams)
+ resp, md, err := local_request_GatewayClaimingServer_AuthorizeGateway_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 {
@@ -973,7 +1043,32 @@ func RegisterGatewayClaimingServerHandlerServer(ctx context.Context, mux *runtim
return
}
- forward_GatewayClaimingServer_GetInfoByGatewayEUI_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+ forward_GatewayClaimingServer_AuthorizeGateway_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+ })
+
+ mux.Handle("DELETE", pattern_GatewayClaimingServer_UnauthorizeGateway_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.GatewayClaimingServer/UnauthorizeGateway", runtime.WithHTTPPathPattern("/gcls/gateways/{gateway_id}/authorize"))
+ if err != nil {
+ runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+ return
+ }
+ resp, md, err := local_request_GatewayClaimingServer_UnauthorizeGateway_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_GatewayClaimingServer_UnauthorizeGateway_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
@@ -1338,69 +1433,91 @@ func RegisterGatewayClaimingServerHandlerClient(ctx context.Context, mux *runtim
})
- mux.Handle("POST", pattern_GatewayClaimingServer_AuthorizeGateway_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+ mux.Handle("DELETE", pattern_GatewayClaimingServer_Unclaim_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.GatewayClaimingServer/AuthorizeGateway", runtime.WithHTTPPathPattern("/gcls/gateways/{gateway_ids.gateway_id}/authorize"))
+ annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/ttn.lorawan.v3.GatewayClaimingServer/Unclaim", runtime.WithHTTPPathPattern("/gcls/claim/{gateway_id}"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
- resp, md, err := request_GatewayClaimingServer_AuthorizeGateway_0(annotatedContext, inboundMarshaler, client, req, pathParams)
+ resp, md, err := request_GatewayClaimingServer_Unclaim_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
- forward_GatewayClaimingServer_AuthorizeGateway_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+ forward_GatewayClaimingServer_Unclaim_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
- mux.Handle("DELETE", pattern_GatewayClaimingServer_UnauthorizeGateway_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+ mux.Handle("POST", pattern_GatewayClaimingServer_GetInfoByGatewayEUI_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.GatewayClaimingServer/UnauthorizeGateway", runtime.WithHTTPPathPattern("/gcls/gateways/{gateway_id}/authorize"))
+ annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/ttn.lorawan.v3.GatewayClaimingServer/GetInfoByGatewayEUI", runtime.WithHTTPPathPattern("/gcls/claim/info"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
- resp, md, err := request_GatewayClaimingServer_UnauthorizeGateway_0(annotatedContext, inboundMarshaler, client, req, pathParams)
+ resp, md, err := request_GatewayClaimingServer_GetInfoByGatewayEUI_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
- forward_GatewayClaimingServer_UnauthorizeGateway_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+ forward_GatewayClaimingServer_GetInfoByGatewayEUI_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
- mux.Handle("POST", pattern_GatewayClaimingServer_GetInfoByGatewayEUI_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+ mux.Handle("POST", pattern_GatewayClaimingServer_AuthorizeGateway_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.GatewayClaimingServer/GetInfoByGatewayEUI", runtime.WithHTTPPathPattern("/gcls/claim/info"))
+ annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/ttn.lorawan.v3.GatewayClaimingServer/AuthorizeGateway", runtime.WithHTTPPathPattern("/gcls/gateways/{gateway_ids.gateway_id}/authorize"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
- resp, md, err := request_GatewayClaimingServer_GetInfoByGatewayEUI_0(annotatedContext, inboundMarshaler, client, req, pathParams)
+ resp, md, err := request_GatewayClaimingServer_AuthorizeGateway_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
- forward_GatewayClaimingServer_GetInfoByGatewayEUI_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+ forward_GatewayClaimingServer_AuthorizeGateway_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+ })
+
+ mux.Handle("DELETE", pattern_GatewayClaimingServer_UnauthorizeGateway_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.GatewayClaimingServer/UnauthorizeGateway", runtime.WithHTTPPathPattern("/gcls/gateways/{gateway_id}/authorize"))
+ if err != nil {
+ runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+ return
+ }
+ resp, md, err := request_GatewayClaimingServer_UnauthorizeGateway_0(annotatedContext, inboundMarshaler, client, req, pathParams)
+ annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
+ if err != nil {
+ runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
+ return
+ }
+
+ forward_GatewayClaimingServer_UnauthorizeGateway_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
@@ -1410,19 +1527,23 @@ func RegisterGatewayClaimingServerHandlerClient(ctx context.Context, mux *runtim
var (
pattern_GatewayClaimingServer_Claim_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"gcls", "claim"}, ""))
+ pattern_GatewayClaimingServer_Unclaim_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"gcls", "claim", "gateway_id"}, ""))
+
+ pattern_GatewayClaimingServer_GetInfoByGatewayEUI_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"gcls", "claim", "info"}, ""))
+
pattern_GatewayClaimingServer_AuthorizeGateway_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2, 2, 3}, []string{"gcls", "gateways", "gateway_ids.gateway_id", "authorize"}, ""))
pattern_GatewayClaimingServer_UnauthorizeGateway_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2, 2, 3}, []string{"gcls", "gateways", "gateway_id", "authorize"}, ""))
-
- pattern_GatewayClaimingServer_GetInfoByGatewayEUI_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"gcls", "claim", "info"}, ""))
)
var (
forward_GatewayClaimingServer_Claim_0 = runtime.ForwardResponseMessage
+ forward_GatewayClaimingServer_Unclaim_0 = runtime.ForwardResponseMessage
+
+ forward_GatewayClaimingServer_GetInfoByGatewayEUI_0 = runtime.ForwardResponseMessage
+
forward_GatewayClaimingServer_AuthorizeGateway_0 = runtime.ForwardResponseMessage
forward_GatewayClaimingServer_UnauthorizeGateway_0 = runtime.ForwardResponseMessage
-
- forward_GatewayClaimingServer_GetInfoByGatewayEUI_0 = runtime.ForwardResponseMessage
)
diff --git a/pkg/ttnpb/deviceclaimingserver_grpc.pb.go b/pkg/ttnpb/deviceclaimingserver_grpc.pb.go
index 90f8f37d85..145a5f750b 100644
--- a/pkg/ttnpb/deviceclaimingserver_grpc.pb.go
+++ b/pkg/ttnpb/deviceclaimingserver_grpc.pb.go
@@ -479,9 +479,10 @@ var EndDeviceBatchClaimingServer_ServiceDesc = grpc.ServiceDesc{
const (
GatewayClaimingServer_Claim_FullMethodName = "/ttn.lorawan.v3.GatewayClaimingServer/Claim"
+ GatewayClaimingServer_Unclaim_FullMethodName = "/ttn.lorawan.v3.GatewayClaimingServer/Unclaim"
+ GatewayClaimingServer_GetInfoByGatewayEUI_FullMethodName = "/ttn.lorawan.v3.GatewayClaimingServer/GetInfoByGatewayEUI"
GatewayClaimingServer_AuthorizeGateway_FullMethodName = "/ttn.lorawan.v3.GatewayClaimingServer/AuthorizeGateway"
GatewayClaimingServer_UnauthorizeGateway_FullMethodName = "/ttn.lorawan.v3.GatewayClaimingServer/UnauthorizeGateway"
- GatewayClaimingServer_GetInfoByGatewayEUI_FullMethodName = "/ttn.lorawan.v3.GatewayClaimingServer/GetInfoByGatewayEUI"
)
// GatewayClaimingServerClient is the client API for GatewayClaimingServer service.
@@ -490,12 +491,21 @@ const (
type GatewayClaimingServerClient interface {
// Claims a gateway by claim authentication code or QR code and transfers the gateway to the target user.
Claim(ctx context.Context, in *ClaimGatewayRequest, opts ...grpc.CallOption) (*GatewayIdentifiers, error)
+ // Unclaims the gateway.
+ // EUI provided in the request are ignored and the end device is looked up by the gateway ID.
+ Unclaim(ctx context.Context, in *GatewayIdentifiers, opts ...grpc.CallOption) (*emptypb.Empty, error)
+ // Return whether claiming is available for a given gateway EUI.
+ GetInfoByGatewayEUI(ctx context.Context, in *GetInfoByGatewayEUIRequest, opts ...grpc.CallOption) (*GetInfoByGatewayEUIResponse, error)
+ // Deprecated: Do not use.
// AuthorizeGateway allows a gateway to be claimed.
+ // DEPRECATED: Authorizing gateways for claiming is no longer supported and will be removed in a future version of The
+ // Things Stack.
AuthorizeGateway(ctx context.Context, in *AuthorizeGatewayRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
+ // Deprecated: Do not use.
// UnauthorizeGateway prevents a gateway from being claimed.
+ // DEPRECATED: Unauthorizing (locking) gateways for claiming is no longer supported and will be removed in a future
+ // version of The Things Stack.
UnauthorizeGateway(ctx context.Context, in *GatewayIdentifiers, opts ...grpc.CallOption) (*emptypb.Empty, error)
- // Return whether claiming is available for a given gateway EUI.
- GetInfoByGatewayEUI(ctx context.Context, in *GetInfoByGatewayEUIRequest, opts ...grpc.CallOption) (*GetInfoByGatewayEUIResponse, error)
}
type gatewayClaimingServerClient struct {
@@ -515,27 +525,38 @@ func (c *gatewayClaimingServerClient) Claim(ctx context.Context, in *ClaimGatewa
return out, nil
}
-func (c *gatewayClaimingServerClient) AuthorizeGateway(ctx context.Context, in *AuthorizeGatewayRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+func (c *gatewayClaimingServerClient) Unclaim(ctx context.Context, in *GatewayIdentifiers, opts ...grpc.CallOption) (*emptypb.Empty, error) {
out := new(emptypb.Empty)
- err := c.cc.Invoke(ctx, GatewayClaimingServer_AuthorizeGateway_FullMethodName, in, out, opts...)
+ err := c.cc.Invoke(ctx, GatewayClaimingServer_Unclaim_FullMethodName, in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
-func (c *gatewayClaimingServerClient) UnauthorizeGateway(ctx context.Context, in *GatewayIdentifiers, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+func (c *gatewayClaimingServerClient) GetInfoByGatewayEUI(ctx context.Context, in *GetInfoByGatewayEUIRequest, opts ...grpc.CallOption) (*GetInfoByGatewayEUIResponse, error) {
+ out := new(GetInfoByGatewayEUIResponse)
+ err := c.cc.Invoke(ctx, GatewayClaimingServer_GetInfoByGatewayEUI_FullMethodName, in, out, opts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+// Deprecated: Do not use.
+func (c *gatewayClaimingServerClient) AuthorizeGateway(ctx context.Context, in *AuthorizeGatewayRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
out := new(emptypb.Empty)
- err := c.cc.Invoke(ctx, GatewayClaimingServer_UnauthorizeGateway_FullMethodName, in, out, opts...)
+ err := c.cc.Invoke(ctx, GatewayClaimingServer_AuthorizeGateway_FullMethodName, in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
-func (c *gatewayClaimingServerClient) GetInfoByGatewayEUI(ctx context.Context, in *GetInfoByGatewayEUIRequest, opts ...grpc.CallOption) (*GetInfoByGatewayEUIResponse, error) {
- out := new(GetInfoByGatewayEUIResponse)
- err := c.cc.Invoke(ctx, GatewayClaimingServer_GetInfoByGatewayEUI_FullMethodName, in, out, opts...)
+// Deprecated: Do not use.
+func (c *gatewayClaimingServerClient) UnauthorizeGateway(ctx context.Context, in *GatewayIdentifiers, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+ out := new(emptypb.Empty)
+ err := c.cc.Invoke(ctx, GatewayClaimingServer_UnauthorizeGateway_FullMethodName, in, out, opts...)
if err != nil {
return nil, err
}
@@ -548,12 +569,21 @@ func (c *gatewayClaimingServerClient) GetInfoByGatewayEUI(ctx context.Context, i
type GatewayClaimingServerServer interface {
// Claims a gateway by claim authentication code or QR code and transfers the gateway to the target user.
Claim(context.Context, *ClaimGatewayRequest) (*GatewayIdentifiers, error)
+ // Unclaims the gateway.
+ // EUI provided in the request are ignored and the end device is looked up by the gateway ID.
+ Unclaim(context.Context, *GatewayIdentifiers) (*emptypb.Empty, error)
+ // Return whether claiming is available for a given gateway EUI.
+ GetInfoByGatewayEUI(context.Context, *GetInfoByGatewayEUIRequest) (*GetInfoByGatewayEUIResponse, error)
+ // Deprecated: Do not use.
// AuthorizeGateway allows a gateway to be claimed.
+ // DEPRECATED: Authorizing gateways for claiming is no longer supported and will be removed in a future version of The
+ // Things Stack.
AuthorizeGateway(context.Context, *AuthorizeGatewayRequest) (*emptypb.Empty, error)
+ // Deprecated: Do not use.
// UnauthorizeGateway prevents a gateway from being claimed.
+ // DEPRECATED: Unauthorizing (locking) gateways for claiming is no longer supported and will be removed in a future
+ // version of The Things Stack.
UnauthorizeGateway(context.Context, *GatewayIdentifiers) (*emptypb.Empty, error)
- // Return whether claiming is available for a given gateway EUI.
- GetInfoByGatewayEUI(context.Context, *GetInfoByGatewayEUIRequest) (*GetInfoByGatewayEUIResponse, error)
mustEmbedUnimplementedGatewayClaimingServerServer()
}
@@ -564,15 +594,18 @@ type UnimplementedGatewayClaimingServerServer struct {
func (UnimplementedGatewayClaimingServerServer) Claim(context.Context, *ClaimGatewayRequest) (*GatewayIdentifiers, error) {
return nil, status.Errorf(codes.Unimplemented, "method Claim not implemented")
}
+func (UnimplementedGatewayClaimingServerServer) Unclaim(context.Context, *GatewayIdentifiers) (*emptypb.Empty, error) {
+ return nil, status.Errorf(codes.Unimplemented, "method Unclaim not implemented")
+}
+func (UnimplementedGatewayClaimingServerServer) GetInfoByGatewayEUI(context.Context, *GetInfoByGatewayEUIRequest) (*GetInfoByGatewayEUIResponse, error) {
+ return nil, status.Errorf(codes.Unimplemented, "method GetInfoByGatewayEUI not implemented")
+}
func (UnimplementedGatewayClaimingServerServer) AuthorizeGateway(context.Context, *AuthorizeGatewayRequest) (*emptypb.Empty, error) {
return nil, status.Errorf(codes.Unimplemented, "method AuthorizeGateway not implemented")
}
func (UnimplementedGatewayClaimingServerServer) UnauthorizeGateway(context.Context, *GatewayIdentifiers) (*emptypb.Empty, error) {
return nil, status.Errorf(codes.Unimplemented, "method UnauthorizeGateway not implemented")
}
-func (UnimplementedGatewayClaimingServerServer) GetInfoByGatewayEUI(context.Context, *GetInfoByGatewayEUIRequest) (*GetInfoByGatewayEUIResponse, error) {
- return nil, status.Errorf(codes.Unimplemented, "method GetInfoByGatewayEUI not implemented")
-}
func (UnimplementedGatewayClaimingServerServer) mustEmbedUnimplementedGatewayClaimingServerServer() {}
// UnsafeGatewayClaimingServerServer may be embedded to opt out of forward compatibility for this service.
@@ -604,56 +637,74 @@ func _GatewayClaimingServer_Claim_Handler(srv interface{}, ctx context.Context,
return interceptor(ctx, in, info, handler)
}
-func _GatewayClaimingServer_AuthorizeGateway_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
- in := new(AuthorizeGatewayRequest)
+func _GatewayClaimingServer_Unclaim_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(GatewayIdentifiers)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
- return srv.(GatewayClaimingServerServer).AuthorizeGateway(ctx, in)
+ return srv.(GatewayClaimingServerServer).Unclaim(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
- FullMethod: GatewayClaimingServer_AuthorizeGateway_FullMethodName,
+ FullMethod: GatewayClaimingServer_Unclaim_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
- return srv.(GatewayClaimingServerServer).AuthorizeGateway(ctx, req.(*AuthorizeGatewayRequest))
+ return srv.(GatewayClaimingServerServer).Unclaim(ctx, req.(*GatewayIdentifiers))
}
return interceptor(ctx, in, info, handler)
}
-func _GatewayClaimingServer_UnauthorizeGateway_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
- in := new(GatewayIdentifiers)
+func _GatewayClaimingServer_GetInfoByGatewayEUI_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(GetInfoByGatewayEUIRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
- return srv.(GatewayClaimingServerServer).UnauthorizeGateway(ctx, in)
+ return srv.(GatewayClaimingServerServer).GetInfoByGatewayEUI(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
- FullMethod: GatewayClaimingServer_UnauthorizeGateway_FullMethodName,
+ FullMethod: GatewayClaimingServer_GetInfoByGatewayEUI_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
- return srv.(GatewayClaimingServerServer).UnauthorizeGateway(ctx, req.(*GatewayIdentifiers))
+ return srv.(GatewayClaimingServerServer).GetInfoByGatewayEUI(ctx, req.(*GetInfoByGatewayEUIRequest))
}
return interceptor(ctx, in, info, handler)
}
-func _GatewayClaimingServer_GetInfoByGatewayEUI_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
- in := new(GetInfoByGatewayEUIRequest)
+func _GatewayClaimingServer_AuthorizeGateway_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(AuthorizeGatewayRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
- return srv.(GatewayClaimingServerServer).GetInfoByGatewayEUI(ctx, in)
+ return srv.(GatewayClaimingServerServer).AuthorizeGateway(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
- FullMethod: GatewayClaimingServer_GetInfoByGatewayEUI_FullMethodName,
+ FullMethod: GatewayClaimingServer_AuthorizeGateway_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
- return srv.(GatewayClaimingServerServer).GetInfoByGatewayEUI(ctx, req.(*GetInfoByGatewayEUIRequest))
+ return srv.(GatewayClaimingServerServer).AuthorizeGateway(ctx, req.(*AuthorizeGatewayRequest))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+func _GatewayClaimingServer_UnauthorizeGateway_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(GatewayIdentifiers)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(GatewayClaimingServerServer).UnauthorizeGateway(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: GatewayClaimingServer_UnauthorizeGateway_FullMethodName,
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(GatewayClaimingServerServer).UnauthorizeGateway(ctx, req.(*GatewayIdentifiers))
}
return interceptor(ctx, in, info, handler)
}
@@ -669,6 +720,14 @@ var GatewayClaimingServer_ServiceDesc = grpc.ServiceDesc{
MethodName: "Claim",
Handler: _GatewayClaimingServer_Claim_Handler,
},
+ {
+ MethodName: "Unclaim",
+ Handler: _GatewayClaimingServer_Unclaim_Handler,
+ },
+ {
+ MethodName: "GetInfoByGatewayEUI",
+ Handler: _GatewayClaimingServer_GetInfoByGatewayEUI_Handler,
+ },
{
MethodName: "AuthorizeGateway",
Handler: _GatewayClaimingServer_AuthorizeGateway_Handler,
@@ -677,10 +736,6 @@ var GatewayClaimingServer_ServiceDesc = grpc.ServiceDesc{
MethodName: "UnauthorizeGateway",
Handler: _GatewayClaimingServer_UnauthorizeGateway_Handler,
},
- {
- MethodName: "GetInfoByGatewayEUI",
- Handler: _GatewayClaimingServer_GetInfoByGatewayEUI_Handler,
- },
},
Streams: []grpc.StreamDesc{},
Metadata: "ttn/lorawan/v3/deviceclaimingserver.proto",
diff --git a/pkg/webui/locales/ja.json b/pkg/webui/locales/ja.json
index fe17949bc5..5864dc5c8e 100644
--- a/pkg/webui/locales/ja.json
+++ b/pkg/webui/locales/ja.json
@@ -1944,11 +1944,19 @@
"error:pkg/deviceclaimingserver/enddevices/ttjsv2:internal_error": "内部エラー",
"error:pkg/deviceclaimingserver/enddevices/ttjsv2:unclaim_device": "EUI `{dev_eui}`のデバイスの主張を取り消す",
"error:pkg/deviceclaimingserver/enddevices/ttjsv2:unclaim_devices": "デバイスの主張を取り消す",
+ "error:pkg/deviceclaimingserver/gateways/ttgc:not_implemented": "",
+ "error:pkg/deviceclaimingserver/gateways:invalid_upstream": "",
+ "error:pkg/deviceclaimingserver:claim gateway": "",
"error:pkg/deviceclaimingserver:claiming_not_supported": "JoinEUI `{eui}` に対してクレームはサポートされていません",
+ "error:pkg/deviceclaimingserver:create_gateway": "",
"error:pkg/deviceclaimingserver:device_not_found": "デバイスが見つかりません",
- "error:pkg/deviceclaimingserver:method_unavailable": "メソッドが利用できません",
+ "error:pkg/deviceclaimingserver:gateway_already_exists": "",
+ "error:pkg/deviceclaimingserver:gateway_claiming_not_supported": "",
+ "error:pkg/deviceclaimingserver:gateway_claiming_with_qrcodes_not_implemented": "",
"error:pkg/deviceclaimingserver:no_devices_found": "デバイスレジストリにバッチ内のデバイスが見つかりません",
+ "error:pkg/deviceclaimingserver:no_eui": "",
"error:pkg/deviceclaimingserver:no_euis": "デバイスにDevEUI/JoinEUIが設定されていません",
+ "error:pkg/deviceclaimingserver:no_gateway_server_address": "",
"error:pkg/deviceclaimingserver:no_join_eui": "リクエストからJoinEUIを抽出",
"error:pkg/deviceclaimingserver:parse_qr_code": "QRコードの解析に失敗しました",
"error:pkg/deviceclaimingserver:qr_code_data": "無効なQRコードデータ",
@@ -2636,6 +2644,16 @@
"event:client.purge": "クライアントをパージする",
"event:client.restore": "OAuthクライアントを復元する",
"event:client.update": "OAuthクライアントの更新",
+ "event:dcs.end_device.claim.abort": "",
+ "event:dcs.end_device.claim.fail": "",
+ "event:dcs.end_device.claim.success": "",
+ "event:dcs.end_device.unclaim.fail": "",
+ "event:dcs.end_device.unclaim.success": "",
+ "event:dcs.gateway.claim.abort": "",
+ "event:dcs.gateway.claim.fail": "",
+ "event:dcs.gateway.claim.success": "",
+ "event:dcs.gateway.unclaim.fail": "",
+ "event:dcs.gateway.unclaim.success": "",
"event:end_device.batch.delete": "エンドデバイスを一括削除する",
"event:end_device.create": "エンドデバイスの作成",
"event:end_device.delete": "エンドデバイスの削除",
diff --git a/sdk/js/generated/api-definition.json b/sdk/js/generated/api-definition.json
index ee96d3416d..1377e6e514 100644
--- a/sdk/js/generated/api-definition.json
+++ b/sdk/js/generated/api-definition.json
@@ -2280,6 +2280,29 @@
}
]
},
+ "Unclaim": {
+ "file": "ttn/lorawan/v3/deviceclaimingserver.proto",
+ "http": [
+ {
+ "method": "delete",
+ "pattern": "/gcls/claim/{gateway_id}",
+ "parameters": [
+ "gateway_id"
+ ]
+ }
+ ]
+ },
+ "GetInfoByGatewayEUI": {
+ "file": "ttn/lorawan/v3/deviceclaimingserver.proto",
+ "http": [
+ {
+ "method": "post",
+ "pattern": "/gcls/claim/info",
+ "body": "*",
+ "parameters": []
+ }
+ ]
+ },
"AuthorizeGateway": {
"file": "ttn/lorawan/v3/deviceclaimingserver.proto",
"http": [
@@ -2304,17 +2327,6 @@
]
}
]
- },
- "GetInfoByGatewayEUI": {
- "file": "ttn/lorawan/v3/deviceclaimingserver.proto",
- "http": [
- {
- "method": "post",
- "pattern": "/gcls/claim/info",
- "body": "*",
- "parameters": []
- }
- ]
}
},
"DeviceRepository": {
diff --git a/sdk/js/generated/api.json b/sdk/js/generated/api.json
index 5b47e34eb0..1292adf71f 100644
--- a/sdk/js/generated/api.json
+++ b/sdk/js/generated/api.json
@@ -10886,7 +10886,7 @@
"name": "CUPSRedirection",
"longName": "CUPSRedirection",
"fullName": "ttn.lorawan.v3.CUPSRedirection",
- "description": "",
+ "description": "DEPRECATED: This message is deprecated and will be removed in a future version of The Things Stack.",
"hasExtensions": false,
"hasFields": true,
"hasOneofs": true,
@@ -10984,7 +10984,10 @@
]
}
}
- ]
+ ],
+ "options": {
+ "deprecated": true
+ }
},
{
"name": "ClientTLS",
@@ -11314,7 +11317,7 @@
},
{
"name": "cups_redirection",
- "description": "Parameters to set CUPS redirection for the gateway.",
+ "description": "Parameters to set CUPS redirection for the gateway.\nDEPRECATED: This field is deprecated and will be removed in a future version of The Things Stack.",
"label": "",
"type": "CUPSRedirection",
"longType": "CUPSRedirection",
@@ -11322,7 +11325,10 @@
"ismap": false,
"isoneof": false,
"oneofdecl": "",
- "defaultValue": ""
+ "defaultValue": "",
+ "options": {
+ "deprecated": true
+ }
},
{
"name": "target_frequency_plan_id",
@@ -11983,22 +11989,44 @@
}
},
{
- "name": "AuthorizeGateway",
- "description": "AuthorizeGateway allows a gateway to be claimed.",
- "requestType": "AuthorizeGatewayRequest",
- "requestLongType": "AuthorizeGatewayRequest",
- "requestFullType": "ttn.lorawan.v3.AuthorizeGatewayRequest",
+ "name": "Unclaim",
+ "description": "Unclaims the gateway.\nEUI provided in the request are ignored and the end device is looked up by the gateway ID.",
+ "requestType": "GatewayIdentifiers",
+ "requestLongType": "GatewayIdentifiers",
+ "requestFullType": "ttn.lorawan.v3.GatewayIdentifiers",
"requestStreaming": false,
"responseType": "Empty",
"responseLongType": ".google.protobuf.Empty",
"responseFullType": "google.protobuf.Empty",
"responseStreaming": false,
+ "options": {
+ "google.api.http": {
+ "rules": [
+ {
+ "method": "DELETE",
+ "pattern": "/gcls/claim/{gateway_id}"
+ }
+ ]
+ }
+ }
+ },
+ {
+ "name": "GetInfoByGatewayEUI",
+ "description": "Return whether claiming is available for a given gateway EUI.",
+ "requestType": "GetInfoByGatewayEUIRequest",
+ "requestLongType": "GetInfoByGatewayEUIRequest",
+ "requestFullType": "ttn.lorawan.v3.GetInfoByGatewayEUIRequest",
+ "requestStreaming": false,
+ "responseType": "GetInfoByGatewayEUIResponse",
+ "responseLongType": "GetInfoByGatewayEUIResponse",
+ "responseFullType": "ttn.lorawan.v3.GetInfoByGatewayEUIResponse",
+ "responseStreaming": false,
"options": {
"google.api.http": {
"rules": [
{
"method": "POST",
- "pattern": "/gcls/gateways/{gateway_ids.gateway_id}/authorize",
+ "pattern": "/gcls/claim/info",
"body": "*"
}
]
@@ -12006,45 +12034,47 @@
}
},
{
- "name": "UnauthorizeGateway",
- "description": "UnauthorizeGateway prevents a gateway from being claimed.",
- "requestType": "GatewayIdentifiers",
- "requestLongType": "GatewayIdentifiers",
- "requestFullType": "ttn.lorawan.v3.GatewayIdentifiers",
+ "name": "AuthorizeGateway",
+ "description": "AuthorizeGateway allows a gateway to be claimed.\nDEPRECATED: Authorizing gateways for claiming is no longer supported and will be removed in a future version of The\nThings Stack.",
+ "requestType": "AuthorizeGatewayRequest",
+ "requestLongType": "AuthorizeGatewayRequest",
+ "requestFullType": "ttn.lorawan.v3.AuthorizeGatewayRequest",
"requestStreaming": false,
"responseType": "Empty",
"responseLongType": ".google.protobuf.Empty",
"responseFullType": "google.protobuf.Empty",
"responseStreaming": false,
"options": {
+ "deprecated": true,
"google.api.http": {
"rules": [
{
- "method": "DELETE",
- "pattern": "/gcls/gateways/{gateway_id}/authorize"
+ "method": "POST",
+ "pattern": "/gcls/gateways/{gateway_ids.gateway_id}/authorize",
+ "body": "*"
}
]
}
}
},
{
- "name": "GetInfoByGatewayEUI",
- "description": "Return whether claiming is available for a given gateway EUI.",
- "requestType": "GetInfoByGatewayEUIRequest",
- "requestLongType": "GetInfoByGatewayEUIRequest",
- "requestFullType": "ttn.lorawan.v3.GetInfoByGatewayEUIRequest",
+ "name": "UnauthorizeGateway",
+ "description": "UnauthorizeGateway prevents a gateway from being claimed.\nDEPRECATED: Unauthorizing (locking) gateways for claiming is no longer supported and will be removed in a future\nversion of The Things Stack.",
+ "requestType": "GatewayIdentifiers",
+ "requestLongType": "GatewayIdentifiers",
+ "requestFullType": "ttn.lorawan.v3.GatewayIdentifiers",
"requestStreaming": false,
- "responseType": "GetInfoByGatewayEUIResponse",
- "responseLongType": "GetInfoByGatewayEUIResponse",
- "responseFullType": "ttn.lorawan.v3.GetInfoByGatewayEUIResponse",
+ "responseType": "Empty",
+ "responseLongType": ".google.protobuf.Empty",
+ "responseFullType": "google.protobuf.Empty",
"responseStreaming": false,
"options": {
+ "deprecated": true,
"google.api.http": {
"rules": [
{
- "method": "POST",
- "pattern": "/gcls/claim/info",
- "body": "*"
+ "method": "DELETE",
+ "pattern": "/gcls/gateways/{gateway_id}/authorize"
}
]
}