diff --git a/api/ttn/lorawan/v3/api.md b/api/ttn/lorawan/v3/api.md index 42f0fe8d84..cf7b87002c 100644 --- a/api/ttn/lorawan/v3/api.md +++ b/api/ttn/lorawan/v3/api.md @@ -331,9 +331,11 @@ - [Service `GatewayConfigurationService`](#ttn.lorawan.v3.GatewayConfigurationService) - [File `ttn/lorawan/v3/gateway_services.proto`](#ttn/lorawan/v3/gateway_services.proto) - [Message `AssertGatewayRightsRequest`](#ttn.lorawan.v3.AssertGatewayRightsRequest) + - [Message `BatchDeleteGatewaysRequest`](#ttn.lorawan.v3.BatchDeleteGatewaysRequest) - [Message `PullGatewayConfigurationRequest`](#ttn.lorawan.v3.PullGatewayConfigurationRequest) - [Service `GatewayAccess`](#ttn.lorawan.v3.GatewayAccess) - [Service `GatewayBatchAccess`](#ttn.lorawan.v3.GatewayBatchAccess) + - [Service `GatewayBatchRegistry`](#ttn.lorawan.v3.GatewayBatchRegistry) - [Service `GatewayConfigurator`](#ttn.lorawan.v3.GatewayConfigurator) - [Service `GatewayRegistry`](#ttn.lorawan.v3.GatewayRegistry) - [File `ttn/lorawan/v3/gatewayserver.proto`](#ttn/lorawan/v3/gatewayserver.proto) @@ -355,6 +357,7 @@ - [Message `EndDeviceVersionIdentifiers`](#ttn.lorawan.v3.EndDeviceVersionIdentifiers) - [Message `EntityIdentifiers`](#ttn.lorawan.v3.EntityIdentifiers) - [Message `GatewayIdentifiers`](#ttn.lorawan.v3.GatewayIdentifiers) + - [Message `GatewayIdentifiersList`](#ttn.lorawan.v3.GatewayIdentifiersList) - [Message `LoRaAllianceProfileIdentifiers`](#ttn.lorawan.v3.LoRaAllianceProfileIdentifiers) - [Message `NetworkIdentifiers`](#ttn.lorawan.v3.NetworkIdentifiers) - [Message `OrganizationIdentifiers`](#ttn.lorawan.v3.OrganizationIdentifiers) @@ -5016,6 +5019,18 @@ Identifies an end device model with version information. | `gateway_ids` |
`repeated.min_items`: `1`
`repeated.max_items`: `20`
| | `required` |`message.required`: `true`
| +### Message `BatchDeleteGatewaysRequest` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `gateway_ids` | [`GatewayIdentifiers`](#ttn.lorawan.v3.GatewayIdentifiers) | repeated | | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `gateway_ids` |`repeated.min_items`: `1`
`repeated.max_items`: `20`
| + ### Message `PullGatewayConfigurationRequest` | Field | Type | Label | Description | @@ -5070,6 +5085,21 @@ EXPERIMENTAL: This service is subject to change. | ----------- | ------ | ------- | ---- | | `AssertRights` | `GET` | `/api/v3/gateways/rights/batch` | | +### Service `GatewayBatchRegistry` + +The GatewayBatchRegistry service, exposed by the Identity Server, is used to manage +gateway registrations in batches. + +| Method Name | Request Type | Response Type | Description | +| ----------- | ------------ | ------------- | ------------| +| `Delete` | [`BatchDeleteGatewaysRequest`](#ttn.lorawan.v3.BatchDeleteGatewaysRequest) | [`.google.protobuf.Empty`](#google.protobuf.Empty) | Delete a batch of gateways. This operation is atomic; either all gateways are deleted or none. The caller must have delete rights on all requested gateways. | + +#### HTTP bindings + +| Method Name | Method | Pattern | Body | +| ----------- | ------ | ------- | ---- | +| `Delete` | `DELETE` | `/api/v3/gateways/batch` | | + ### Service `GatewayConfigurator` | Method Name | Request Type | Response Type | Description | @@ -5315,6 +5345,12 @@ EntityIdentifiers contains one of the possible entity identifiers. | `gateway_id` |`string.max_len`: `36`
`string.pattern`: `^[a-z0-9](?:[-]?[a-z0-9]){2,}$`
| | `eui` |`bytes.len`: `8`
| +### Message `GatewayIdentifiersList` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `gateway_ids` | [`GatewayIdentifiers`](#ttn.lorawan.v3.GatewayIdentifiers) | repeated | | + ### Message `LoRaAllianceProfileIdentifiers` | Field | Type | Label | Description | diff --git a/api/ttn/lorawan/v3/api.swagger.json b/api/ttn/lorawan/v3/api.swagger.json index 2f59837909..00d805b344 100644 --- a/api/ttn/lorawan/v3/api.swagger.json +++ b/api/ttn/lorawan/v3/api.swagger.json @@ -89,6 +89,9 @@ { "name": "GatewayBatchAccess" }, + { + "name": "GatewayBatchRegistry" + }, { "name": "GtwGs" }, @@ -8446,6 +8449,30 @@ ] } }, + "/gateways/batch": { + "delete": { + "summary": "Delete a batch of gateways.\nThis operation is atomic; either all gateways are deleted or none.\nThe caller must have delete rights on all requested gateways.", + "operationId": "GatewayBatchRegistry_Delete", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "type": "object", + "properties": {} + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "tags": [ + "GatewayBatchRegistry" + ] + } + }, "/gateways/rights/batch": { "get": { "summary": "Assert that the caller has the requested rights on all the requested gateways.\nThe check is successful if there are no errors.", diff --git a/api/ttn/lorawan/v3/gateway_services.proto b/api/ttn/lorawan/v3/gateway_services.proto index 0ec4098551..a4dba53e45 100644 --- a/api/ttn/lorawan/v3/gateway_services.proto +++ b/api/ttn/lorawan/v3/gateway_services.proto @@ -183,3 +183,21 @@ service GatewayBatchAccess { option (google.api.http) = {get: "/gateways/rights/batch"}; } } + +message BatchDeleteGatewaysRequest { + repeated ttn.lorawan.v3.GatewayIdentifiers gateway_ids = 1 [(validate.rules).repeated = { + min_items: 1, + max_items: 20, + }]; +} + +// The GatewayBatchRegistry service, exposed by the Identity Server, is used to manage +// gateway registrations in batches. +service GatewayBatchRegistry { + // Delete a batch of gateways. + // This operation is atomic; either all gateways are deleted or none. + // The caller must have delete rights on all requested gateways. + rpc Delete(BatchDeleteGatewaysRequest) returns (google.protobuf.Empty) { + option (google.api.http) = {delete: "/gateways/batch"}; + } +} diff --git a/api/ttn/lorawan/v3/identifiers.proto b/api/ttn/lorawan/v3/identifiers.proto index 45ac2625eb..4d32b24d20 100644 --- a/api/ttn/lorawan/v3/identifiers.proto +++ b/api/ttn/lorawan/v3/identifiers.proto @@ -293,3 +293,7 @@ message LoRaAllianceProfileIdentifiers { message EndDeviceIdentifiersList { repeated EndDeviceIdentifiers end_device_ids = 1; } + +message GatewayIdentifiersList { + repeated GatewayIdentifiers gateway_ids = 1; +} diff --git a/cmd/ttn-lw-cli/commands/gateways.go b/cmd/ttn-lw-cli/commands/gateways.go index 99e42edf5b..a9de050e1d 100644 --- a/cmd/ttn-lw-cli/commands/gateways.go +++ b/cmd/ttn-lw-cli/commands/gateways.go @@ -417,6 +417,45 @@ var ( return nil }, } + gatewaysBatchDeleteCommand = &cobra.Command{ + Use: "batch-delete [gateway-ids]", + Short: "Delete a batch of gateways (EXPERIMENTAL).", + RunE: func(cmd *cobra.Command, args []string) error { + var gatewayIDs []*ttnpb.GatewayIdentifiers + if inputDecoder != nil { + dec := struct { + GatewayIDs []string `json:"gateway_ids"` + }{} + err := inputDecoder.Decode(&dec) + if err != nil { + return err + } + for _, gtwID := range dec.GatewayIDs { + gatewayIDs = append(gatewayIDs, &ttnpb.GatewayIdentifiers{ + GatewayId: gtwID, + }) + } + } else if len(args) == 0 { + return errNoIDs.New() + } else { + for _, arg := range args { + gatewayIDs = append(gatewayIDs, &ttnpb.GatewayIdentifiers{ + GatewayId: arg, + }) + } + } + is, err := api.Dial(ctx, config.IdentityServerGRPCAddress) + if err != nil { + return err + } + _, err = ttnpb.NewGatewayBatchRegistryClient(is).Delete( + ctx, &ttnpb.BatchDeleteGatewaysRequest{ + GatewayIds: gatewayIDs, + }, + ) + return err + }, + } gatewaysRestoreCommand = &cobra.Command{ Use: "restore [gateway-id]", Short: "Restore a gateway", @@ -606,6 +645,7 @@ func init() { gatewaysPurgeCommand.Flags().AddFlagSet(gatewayIDFlags()) gatewaysPurgeCommand.Flags().AddFlagSet(forceFlags()) gatewaysCommand.AddCommand(gatewaysPurgeCommand) + gatewaysCommand.AddCommand(gatewaysBatchDeleteCommand) Root.AddCommand(gatewaysCommand) } diff --git a/config/messages.json b/config/messages.json index c7517d3898..8241a89b2a 100644 --- a/config/messages.json +++ b/config/messages.json @@ -9764,6 +9764,15 @@ "file": "gateway_access.go" } }, + "event:gateway.batch.delete": { + "translations": { + "en": "batch delete gateways" + }, + "description": { + "package": "pkg/identityserver", + "file": "gateway_registry.go" + } + }, "event:gateway.collaborator.delete": { "translations": { "en": "delete gateway collaborator" diff --git a/pkg/identityserver/bunstore/gateway_store.go b/pkg/identityserver/bunstore/gateway_store.go index 3cfe0d8490..78cd8f25bf 100644 --- a/pkg/identityserver/bunstore/gateway_store.go +++ b/pkg/identityserver/bunstore/gateway_store.go @@ -828,6 +828,41 @@ func (s *gatewayStore) DeleteGateway(ctx context.Context, id *ttnpb.GatewayIdent return nil } +func (s *gatewayStore) BatchDeleteGateways( + ctx context.Context, + ids []*ttnpb.GatewayIdentifiers, +) ([]*ttnpb.GatewayIdentifiers, error) { + ctx, span := tracer.StartFromContext(ctx, "BatchDeleteGateways", trace.WithAttributes( + attribute.Int("count", len(ids)), + )) + defer span.End() + + deleted := make([]*ttnpb.GatewayIdentifiers, 0, len(ids)) + for _, id := range ids { + model, err := s.getGatewayModelBy( + ctx, + s.selectWithID(ctx, id.GetGatewayId()), + store.FieldMask{"ids"}, + ) + if err != nil { + if errors.IsNotFound(err) { + continue + } + return nil, storeutil.WrapDriverError(err) + } + + _, err = s.DB.NewDelete(). + Model(model). + WherePK(). + Exec(ctx) + if err != nil { + return nil, storeutil.WrapDriverError(err) + } + deleted = append(deleted, id) + } + return deleted, nil +} + func (s *gatewayStore) RestoreGateway(ctx context.Context, id *ttnpb.GatewayIdentifiers) error { ctx, span := tracer.StartFromContext(ctx, "RestoreGateway", trace.WithAttributes( attribute.String("gateway_id", id.GetGatewayId()), diff --git a/pkg/identityserver/bunstore/store_test.go b/pkg/identityserver/bunstore/store_test.go index 783707f257..8e8ce96358 100644 --- a/pkg/identityserver/bunstore/store_test.go +++ b/pkg/identityserver/bunstore/store_test.go @@ -99,6 +99,7 @@ func TestGatewayStore(t *testing.T) { st := storetest.New(t, newTestStore) st.TestGatewayStoreCRUD(t) st.TestGatewayStorePagination(t) + st.TestGatewayBatchOperations(t) } func TestOrganizationStore(t *testing.T) { diff --git a/pkg/identityserver/gateway_registry.go b/pkg/identityserver/gateway_registry.go index a44889c174..41921fd836 100644 --- a/pkg/identityserver/gateway_registry.go +++ b/pkg/identityserver/gateway_registry.go @@ -62,6 +62,14 @@ var ( events.WithAuthFromContext(), events.WithClientInfoFromContext(), ) + evtBatchDeleteGateways = events.Define( + "gateway.batch.delete", "batch delete gateways", + events.WithVisibility(ttnpb.Right_RIGHT_GATEWAY_INFO), + events.WithDataType(&ttnpb.GatewayIdentifiersList{}), + events.WithAuthFromContext(), + events.WithClientInfoFromContext(), + events.WithPropagateToParent(), + ) ) var ( @@ -728,6 +736,46 @@ func (is *IdentityServer) purgeGateway(ctx context.Context, ids *ttnpb.GatewayId return ttnpb.Empty, nil } +func (is *IdentityServer) batchDeleteGateways( + ctx context.Context, + req *ttnpb.BatchDeleteGatewaysRequest, +) (*emptypb.Empty, error) { + if err := is.assertGatewayRights( + ctx, + req.GatewayIds, + &ttnpb.Rights{ + Rights: []ttnpb.Right{ttnpb.Right_RIGHT_GATEWAY_DELETE}, + }, + ); err != nil { + return nil, err + } + var ( + err error + deleted = make([]*ttnpb.GatewayIdentifiers, 0) + ) + err = is.store.Transact(ctx, func(ctx context.Context, st store.Store) error { + deleted, err = st.BatchDeleteGateways(ctx, req.GatewayIds) + if err != nil { + return err + } + return nil + }) + if err != nil { + return nil, err + } + if len(deleted) != 0 { + events.Publish( + evtBatchDeleteGateways.New( + ctx, + events.WithData( + &ttnpb.GatewayIdentifiersList{GatewayIds: deleted}, + ), + ), + ) + } + return ttnpb.Empty, nil +} + func validateClaimAuthenticationCode(authCode *ttnpb.GatewayClaimAuthenticationCode) error { if authCode.Secret == nil { return errClaimAuthenticationCode.New() @@ -781,3 +829,17 @@ func (gr *gatewayRegistry) Restore(ctx context.Context, req *ttnpb.GatewayIdenti func (gr *gatewayRegistry) Purge(ctx context.Context, req *ttnpb.GatewayIdentifiers) (*emptypb.Empty, error) { return gr.purgeGateway(ctx, req) } + +type gatewayBatchRegistry struct { + ttnpb.UnimplementedGatewayBatchRegistryServer + + *IdentityServer +} + +// Delete implements ttnpb.GatewayBatchRegistryServer. +func (gr *gatewayBatchRegistry) Delete( + ctx context.Context, + req *ttnpb.BatchDeleteGatewaysRequest, +) (*emptypb.Empty, error) { + return gr.batchDeleteGateways(ctx, req) +} diff --git a/pkg/identityserver/gateway_registry_test.go b/pkg/identityserver/gateway_registry_test.go index e546ea0fa6..d6461dba8a 100644 --- a/pkg/identityserver/gateway_registry_test.go +++ b/pkg/identityserver/gateway_registry_test.go @@ -27,6 +27,8 @@ import ( "google.golang.org/grpc/metadata" ) +const noOfGateways = 3 + func TestGatewaysPermissionDenied(t *testing.T) { p := &storetest.Population{} usr1 := p.NewUser() @@ -341,3 +343,93 @@ func TestGatewaysPagination(t *testing.T) { } }, withPrivateTestDatabase(p)) } + +func TestGatewayBatchOperations(t *testing.T) { + t.Parallel() + a, ctx := test.New(t) + p := &storetest.Population{} + usr1 := p.NewUser() + usr2 := p.NewUser() + gtwIDs := make([]*ttnpb.GatewayIdentifiers, 0, noOfGateways) + for i := 0; i < noOfGateways; i++ { + gtw := p.NewGateway(usr1.GetOrganizationOrUserIdentifiers()) + gtw.Attributes = map[string]string{ + "foo": "bar", + } + gtw.UpdateLocationFromStatus = true + gtwIDs = append(gtwIDs, gtw.GetIds()) + } + limitedKey, _ := p.NewAPIKey(usr1.GetEntityIdentifiers(), ttnpb.Right_RIGHT_USER_GATEWAYS_LIST) + limitedCreds := rpcCreds(limitedKey) + + fullKey, _ := p.NewAPIKey(usr1.GetEntityIdentifiers(), ttnpb.Right_RIGHT_GATEWAY_ALL) + fullCreds := rpcCreds(fullKey) + + usr2Key, _ := p.NewAPIKey(usr2.GetEntityIdentifiers(), ttnpb.Right_RIGHT_ALL) + usr2Creds := rpcCreds(usr2Key) + + testWithIdentityServer(t, func(is *IdentityServer, cc *grpc.ClientConn) { + reg := ttnpb.NewGatewayBatchRegistryClient(cc) + readReg := ttnpb.NewGatewayRegistryClient(cc) + + // Read after create. + gtws, err := readReg.List(ctx, &ttnpb.ListGatewaysRequest{ + Collaborator: usr1.GetOrganizationOrUserIdentifiers(), + }, limitedCreds) + a.So(err, should.BeNil) + a.So(len(gtws.Gateways), should.Equal, noOfGateways) + + // ClusterAuth. + _, err = reg.Delete(ctx, &ttnpb.BatchDeleteGatewaysRequest{ + GatewayIds: gtwIDs, + }, is.WithClusterAuth()) + a.So(errors.IsPermissionDenied(err), should.BeTrue) + + // Insufficient rights. + _, err = reg.Delete(ctx, &ttnpb.BatchDeleteGatewaysRequest{ + GatewayIds: gtwIDs, + }, limitedCreds) + a.So(errors.IsPermissionDenied(err), should.BeTrue) + + // User without rights on gateways. + _, err = reg.Delete(ctx, &ttnpb.BatchDeleteGatewaysRequest{ + GatewayIds: gtwIDs, + }, usr2Creds) + a.So(errors.IsPermissionDenied(err), should.BeTrue) + + // Unknown gateway ID. + _, err = reg.Delete(ctx, &ttnpb.BatchDeleteGatewaysRequest{ + GatewayIds: []*ttnpb.GatewayIdentifiers{ + { + GatewayId: "unknown", + }, + }, + }, fullCreds) + a.So(errors.IsPermissionDenied(err), should.BeTrue) + + // One unknown in batch. + _, err = reg.Delete(ctx, &ttnpb.BatchDeleteGatewaysRequest{ + GatewayIds: []*ttnpb.GatewayIdentifiers{ + { + GatewayId: "unknown", + }, + gtwIDs[0], + gtwIDs[1], + }, + }, fullCreds) + a.So(errors.IsPermissionDenied(err), should.BeTrue) + + // Valid Batch. + _, err = reg.Delete(ctx, &ttnpb.BatchDeleteGatewaysRequest{ + GatewayIds: gtwIDs, + }, fullCreds) + a.So(err, should.BeNil) + + // Read after delete. + gtws, err = readReg.List(ctx, &ttnpb.ListGatewaysRequest{ + Collaborator: usr1.GetOrganizationOrUserIdentifiers(), + }, limitedCreds) + a.So(err, should.BeNil) + a.So(len(gtws.Gateways), should.Equal, 0) + }, withPrivateTestDatabase(p)) +} diff --git a/pkg/identityserver/identityserver.go b/pkg/identityserver/identityserver.go index 5c559dfee4..896ed0d2a2 100644 --- a/pkg/identityserver/identityserver.go +++ b/pkg/identityserver/identityserver.go @@ -197,6 +197,7 @@ func New(c *component.Component, config *Config) (is *IdentityServer, err error) "/ttn.lorawan.v3.EndDeviceBatchRegistry", "/ttn.lorawan.v3.GatewayRegistry", "/ttn.lorawan.v3.GatewayAccess", + "/ttn.lorawan.v3.GatewayBatchRegistry", "/ttn.lorawan.v3.GatewayBatchAccess", "/ttn.lorawan.v3.OrganizationRegistry", "/ttn.lorawan.v3.OrganizationAccess", @@ -245,6 +246,7 @@ func (is *IdentityServer) RegisterServices(s *grpc.Server) { ttnpb.RegisterEndDeviceRegistryServer(s, &endDeviceRegistry{IdentityServer: is}) ttnpb.RegisterGatewayRegistryServer(s, &gatewayRegistry{IdentityServer: is}) ttnpb.RegisterGatewayAccessServer(s, &gatewayAccess{IdentityServer: is}) + ttnpb.RegisterGatewayBatchRegistryServer(s, &gatewayBatchRegistry{IdentityServer: is}) ttnpb.RegisterGatewayBatchAccessServer(s, &gatewayBatchAccess{IdentityServer: is}) ttnpb.RegisterOrganizationRegistryServer(s, &organizationRegistry{IdentityServer: is}) ttnpb.RegisterOrganizationAccessServer(s, &organizationAccess{IdentityServer: is}) @@ -271,7 +273,8 @@ func (is *IdentityServer) RegisterHandlers(s *runtime.ServeMux, conn *grpc.Clien ttnpb.RegisterEndDeviceRegistryHandler(is.Context(), s, conn) ttnpb.RegisterGatewayRegistryHandler(is.Context(), s, conn) ttnpb.RegisterGatewayAccessHandler(is.Context(), s, conn) - ttnpb.RegisterGatewayBatchAccessHandler(is.Context(), s, conn) // nolint:errcheck + ttnpb.RegisterGatewayBatchRegistryHandler(is.Context(), s, conn) // nolint:errcheck + ttnpb.RegisterGatewayBatchAccessHandler(is.Context(), s, conn) // nolint:errcheck ttnpb.RegisterOrganizationRegistryHandler(is.Context(), s, conn) ttnpb.RegisterOrganizationAccessHandler(is.Context(), s, conn) ttnpb.RegisterUserRegistryHandler(is.Context(), s, conn) diff --git a/pkg/identityserver/store/store_interfaces.go b/pkg/identityserver/store/store_interfaces.go index 6f7adba424..66196afd2e 100644 --- a/pkg/identityserver/store/store_interfaces.go +++ b/pkg/identityserver/store/store_interfaces.go @@ -113,6 +113,10 @@ type GatewayStore interface { DeleteGateway(ctx context.Context, id *ttnpb.GatewayIdentifiers) error RestoreGateway(ctx context.Context, id *ttnpb.GatewayIdentifiers) error PurgeGateway(ctx context.Context, id *ttnpb.GatewayIdentifiers) error + BatchDeleteGateways( + ctx context.Context, + ids []*ttnpb.GatewayIdentifiers, + ) ([]*ttnpb.GatewayIdentifiers, error) } // OrganizationStore interface for storing Organizations. diff --git a/pkg/identityserver/storetest/end_device_store.go b/pkg/identityserver/storetest/end_device_store.go index c2f4bc87ed..03e75176dc 100644 --- a/pkg/identityserver/storetest/end_device_store.go +++ b/pkg/identityserver/storetest/end_device_store.go @@ -733,7 +733,7 @@ func (st *StoreTest) TestEndDeviceCAC(t *T) { //nolint:revive }) } -// TestEndDeviceBatchOperations tests the EndDeviceBatchStore implementation. +// TestEndDeviceBatchOperations tests end device batch operations. func (st *StoreTest) TestEndDeviceBatchOperations(t *T) { // nolint:gocyclo a, ctx := test.New(t) @@ -806,13 +806,12 @@ func (st *StoreTest) TestEndDeviceBatchOperations(t *T) { // nolint:gocyclo // Batch Delete for _, tc := range []struct { // nolint:paralleltest - Name string - Context context.Context - BatchDeleteFunc func(context.Context, []*ttnpb.EndDeviceIdentifiers) ([]*ttnpb.EndDeviceIdentifiers, error) - ApplicationIDs *ttnpb.ApplicationIdentifiers - DeviceIDs []string - Response []*ttnpb.EndDeviceIdentifiers - ErrorAssertion func(*T, error) bool + Name string + Context context.Context + ApplicationIDs *ttnpb.ApplicationIdentifiers + DeviceIDs []string + Response []*ttnpb.EndDeviceIdentifiers + ErrorAssertion func(*T, error) bool }{ { Name: "Not Found", diff --git a/pkg/identityserver/storetest/gateway_store.go b/pkg/identityserver/storetest/gateway_store.go index 004adc1e34..24e51174b7 100644 --- a/pkg/identityserver/storetest/gateway_store.go +++ b/pkg/identityserver/storetest/gateway_store.go @@ -15,10 +15,12 @@ package storetest import ( + "context" "fmt" . "testing" "time" + "github.com/smarty/assertions" "go.thethings.network/lorawan-stack/v3/pkg/errors" "go.thethings.network/lorawan-stack/v3/pkg/identityserver/store" is "go.thethings.network/lorawan-stack/v3/pkg/identityserver/store" @@ -540,3 +542,144 @@ func (st *StoreTest) TestGatewayStorePagination(t *T) { } }) } + +// TestGatewayBatchOperations tests gateway batch operations. +func (st *StoreTest) TestGatewayBatchOperations(t *T) { // nolint:gocyclo + a, ctx := test.New(t) + + s, ok := st.PrepareDB(t).(interface { + Store + is.GatewayStore + }) + defer st.DestroyDB(t, false) + if !ok { + t.Skip("Store does not implement TestGatewayBatchOperations") + } + defer s.Close() + + for _, ctx := range []context.Context{ + ctx, + } { + gtw1, err := s.CreateGateway(ctx, &ttnpb.Gateway{ + Ids: &ttnpb.GatewayIdentifiers{ + GatewayId: "gtw1", + }, + }) + if !a.So(err, should.BeNil) { + t.FailNow() + } + gtw2, err := s.CreateGateway(ctx, &ttnpb.Gateway{ + Ids: &ttnpb.GatewayIdentifiers{ + GatewayId: "gtw2", + Eui: types.EUI64{1, 2, 3, 4, 5, 6, 7, 8}.Bytes(), + }, + }) + if !a.So(err, should.BeNil) { + t.FailNow() + } + gtw3, err := s.CreateGateway(ctx, &ttnpb.Gateway{ + Ids: &ttnpb.GatewayIdentifiers{ + GatewayId: "gtw3", + Eui: types.EUI64{1, 2, 3, 4, 5, 6, 7, 9}.Bytes(), + }, + }) + if !a.So(err, should.BeNil) { + t.FailNow() + } + + gtws, err := s.FindGateways( + ctx, + []*ttnpb.GatewayIdentifiers{ + {GatewayId: "gtw1"}, + {GatewayId: "gtw2"}, + {GatewayId: "gtw3"}, + }, + []string{"ids"}) + if !a.So(err, should.BeNil) { + t.FailNow() + } + a.So(gtws, should.HaveLength, 3) + for _, gtw := range gtws { + switch gtw.IDString() { + case gtw1.IDString(): + case gtw2.IDString(): + case gtw3.IDString(): + default: + t.Fatalf("Unexpected gateway ID: %s", gtw.IDString()) + } + } + + // Batch Delete + for _, tc := range []struct { // nolint:paralleltest + Name string + Context context.Context + Request []*ttnpb.GatewayIdentifiers + Response []*ttnpb.GatewayIdentifiers + ErrorAssertion func(error) bool + }{ + { + Name: "Invalid batch", + Context: ctx, + Request: []*ttnpb.GatewayIdentifiers{ + {GatewayId: "unknown"}, + }, + Response: []*ttnpb.GatewayIdentifiers{}, + }, + { + Name: "One not found", + Context: ctx, + Request: []*ttnpb.GatewayIdentifiers{ + {GatewayId: "unknown"}, + {GatewayId: "gtw3"}, + }, + Response: []*ttnpb.GatewayIdentifiers{ + {GatewayId: "gtw3"}, + }, + }, + { + Name: "Valid batch", + Context: ctx, + Request: []*ttnpb.GatewayIdentifiers{ + {GatewayId: "gtw1"}, + {GatewayId: "gtw2"}, + }, + Response: []*ttnpb.GatewayIdentifiers{ + {GatewayId: "gtw1"}, + {GatewayId: "gtw2"}, + }, + }, + } { + tc := tc + t.Run(tc.Name, func(t *T) { + a := assertions.New(t) + deleted, err := s.BatchDeleteGateways(tc.Context, tc.Request) + 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") + } + if tc.Response != nil { + a.So(deleted, should.Resemble, tc.Response) + } else { + a.So(deleted, should.BeNil) + } + }) + } + + // Check that the gateways are deleted. + gtws, err = s.FindGateways( + ctx, + []*ttnpb.GatewayIdentifiers{ + {GatewayId: "gtw1"}, + {GatewayId: "gtw2"}, + {GatewayId: "gtw3"}, + }, + []string{"ids"}) + if !a.So(err, should.BeNil) { + t.FailNow() + } + a.So(gtws, should.HaveLength, 0) + } +} diff --git a/pkg/ttnpb/gateway_services.pb.go b/pkg/ttnpb/gateway_services.pb.go index 8c43a8bf4f..fd0e54c717 100644 --- a/pkg/ttnpb/gateway_services.pb.go +++ b/pkg/ttnpb/gateway_services.pb.go @@ -148,6 +148,53 @@ func (x *AssertGatewayRightsRequest) GetRequired() *Rights { return nil } +type BatchDeleteGatewaysRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + GatewayIds []*GatewayIdentifiers `protobuf:"bytes,1,rep,name=gateway_ids,json=gatewayIds,proto3" json:"gateway_ids,omitempty"` +} + +func (x *BatchDeleteGatewaysRequest) Reset() { + *x = BatchDeleteGatewaysRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_ttn_lorawan_v3_gateway_services_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *BatchDeleteGatewaysRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BatchDeleteGatewaysRequest) ProtoMessage() {} + +func (x *BatchDeleteGatewaysRequest) ProtoReflect() protoreflect.Message { + mi := &file_ttn_lorawan_v3_gateway_services_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BatchDeleteGatewaysRequest.ProtoReflect.Descriptor instead. +func (*BatchDeleteGatewaysRequest) Descriptor() ([]byte, []int) { + return file_ttn_lorawan_v3_gateway_services_proto_rawDescGZIP(), []int{2} +} + +func (x *BatchDeleteGatewaysRequest) GetGatewayIds() []*GatewayIdentifiers { + if x != nil { + return x.GatewayIds + } + return nil +} + var File_ttn_lorawan_v3_gateway_services_proto protoreflect.FileDescriptor var file_ttn_lorawan_v3_gateway_services_proto_rawDesc = []byte{ @@ -188,180 +235,195 @@ var file_ttn_lorawan_v3_gateway_services_proto_rawDesc = []byte{ 0x0a, 0x08, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x52, 0x69, 0x67, 0x68, 0x74, 0x73, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, - 0x10, 0x01, 0x52, 0x08, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x32, 0xd9, 0x08, 0x0a, - 0x0f, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, - 0x12, 0xd3, 0x01, 0x0a, 0x06, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x12, 0x24, 0x2e, 0x74, 0x74, - 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x43, 0x72, 0x65, - 0x61, 0x74, 0x65, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x17, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, - 0x76, 0x33, 0x2e, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x22, 0x89, 0x01, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x82, 0x01, 0x3a, 0x01, 0x2a, 0x5a, 0x4c, 0x3a, 0x01, 0x2a, 0x22, 0x47, 0x2f, 0x6f, - 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x7b, 0x63, 0x6f, - 0x6c, 0x6c, 0x61, 0x62, 0x6f, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x6f, 0x72, 0x67, 0x61, 0x6e, - 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x73, 0x2e, 0x6f, 0x72, 0x67, 0x61, - 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x67, 0x61, 0x74, - 0x65, 0x77, 0x61, 0x79, 0x73, 0x22, 0x2f, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x73, 0x2f, 0x7b, 0x63, - 0x6f, 0x6c, 0x6c, 0x61, 0x62, 0x6f, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x75, 0x73, 0x65, 0x72, - 0x5f, 0x69, 0x64, 0x73, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x67, 0x61, - 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x12, 0x6d, 0x0a, 0x03, 0x47, 0x65, 0x74, 0x12, 0x21, 0x2e, - 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x47, - 0x65, 0x74, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x17, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, - 0x33, 0x2e, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x22, 0x2a, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x24, 0x12, 0x22, 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, 0x12, 0x6e, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x49, 0x64, 0x65, 0x6e, - 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x46, 0x6f, 0x72, 0x45, 0x55, 0x49, 0x12, 0x32, 0x2e, - 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x47, - 0x65, 0x74, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, - 0x69, 0x65, 0x72, 0x73, 0x46, 0x6f, 0x72, 0x45, 0x55, 0x49, 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, 0x12, 0xd8, 0x01, 0x0a, 0x04, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x23, - 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, - 0x4c, 0x69, 0x73, 0x74, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, - 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x22, 0x90, 0x01, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x89, 0x01, 0x5a, 0x31, 0x12, 0x2f, 0x2f, 0x75, 0x73, 0x65, 0x72, - 0x73, 0x2f, 0x7b, 0x63, 0x6f, 0x6c, 0x6c, 0x61, 0x62, 0x6f, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2e, - 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, - 0x7d, 0x2f, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x5a, 0x49, 0x12, 0x47, 0x2f, 0x6f, - 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x7b, 0x63, 0x6f, - 0x6c, 0x6c, 0x61, 0x62, 0x6f, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x6f, 0x72, 0x67, 0x61, 0x6e, - 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x73, 0x2e, 0x6f, 0x72, 0x67, 0x61, - 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x67, 0x61, 0x74, - 0x65, 0x77, 0x61, 0x79, 0x73, 0x12, 0x09, 0x2f, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, - 0x12, 0x76, 0x0a, 0x06, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x24, 0x2e, 0x74, 0x74, 0x6e, - 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x55, 0x70, 0x64, 0x61, + 0x10, 0x01, 0x52, 0x08, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x22, 0x6d, 0x0a, 0x1a, + 0x42, 0x61, 0x74, 0x63, 0x68, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x47, 0x61, 0x74, 0x65, 0x77, + 0x61, 0x79, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4f, 0x0a, 0x0b, 0x67, 0x61, + 0x74, 0x65, 0x77, 0x61, 0x79, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x22, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, + 0x2e, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, + 0x65, 0x72, 0x73, 0x42, 0x0a, 0xfa, 0x42, 0x07, 0x92, 0x01, 0x04, 0x08, 0x01, 0x10, 0x14, 0x52, + 0x0a, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x49, 0x64, 0x73, 0x32, 0xd9, 0x08, 0x0a, 0x0f, + 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x12, + 0xd3, 0x01, 0x0a, 0x06, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x12, 0x24, 0x2e, 0x74, 0x74, 0x6e, + 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, - 0x33, 0x2e, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x22, 0x2d, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x27, 0x3a, 0x01, 0x2a, 0x1a, 0x22, 0x2f, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x2f, - 0x7b, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x69, 0x64, 0x73, 0x2e, 0x67, 0x61, 0x74, - 0x65, 0x77, 0x61, 0x79, 0x5f, 0x69, 0x64, 0x7d, 0x12, 0x64, 0x0a, 0x06, 0x44, 0x65, 0x6c, 0x65, - 0x74, 0x65, 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, 0x1e, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x2a, 0x16, 0x2f, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, - 0x73, 0x2f, 0x7b, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x5f, 0x69, 0x64, 0x7d, 0x12, 0x6d, - 0x0a, 0x07, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 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, 0x26, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x22, 0x1e, 0x2f, - 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x2f, 0x7b, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, - 0x79, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x72, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x12, 0x69, 0x0a, - 0x05, 0x50, 0x75, 0x72, 0x67, 0x65, 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, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x2a, 0x1c, 0x2f, 0x67, 0x61, 0x74, - 0x65, 0x77, 0x61, 0x79, 0x73, 0x2f, 0x7b, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x5f, 0x69, - 0x64, 0x7d, 0x2f, 0x70, 0x75, 0x72, 0x67, 0x65, 0x32, 0xb3, 0x0a, 0x0a, 0x0d, 0x47, 0x61, 0x74, - 0x65, 0x77, 0x61, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x6f, 0x0a, 0x0a, 0x4c, 0x69, - 0x73, 0x74, 0x52, 0x69, 0x67, 0x68, 0x74, 0x73, 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, 0x74, - 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x52, 0x69, - 0x67, 0x68, 0x74, 0x73, 0x22, 0x25, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1f, 0x12, 0x1d, 0x2f, 0x67, - 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x2f, 0x7b, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, - 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x72, 0x69, 0x67, 0x68, 0x74, 0x73, 0x12, 0x8a, 0x01, 0x0a, 0x0c, - 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x50, 0x49, 0x4b, 0x65, 0x79, 0x12, 0x2a, 0x2e, 0x74, - 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x43, 0x72, - 0x65, 0x61, 0x74, 0x65, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x41, 0x50, 0x49, 0x4b, 0x65, - 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, - 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x41, 0x50, 0x49, 0x4b, 0x65, 0x79, - 0x22, 0x36, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x30, 0x3a, 0x01, 0x2a, 0x22, 0x2b, 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, 0x70, 0x69, 0x2d, 0x6b, 0x65, 0x79, 0x73, 0x12, 0x86, 0x01, 0x0a, 0x0b, 0x4c, 0x69, 0x73, - 0x74, 0x41, 0x50, 0x49, 0x4b, 0x65, 0x79, 0x73, 0x12, 0x29, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, - 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x47, 0x61, - 0x74, 0x65, 0x77, 0x61, 0x79, 0x41, 0x50, 0x49, 0x4b, 0x65, 0x79, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, - 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x41, 0x50, 0x49, 0x4b, 0x65, 0x79, 0x73, 0x22, 0x33, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x2d, 0x12, 0x2b, 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, 0x70, 0x69, 0x2d, 0x6b, 0x65, 0x79, - 0x73, 0x12, 0x8a, 0x01, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x41, 0x50, 0x49, 0x4b, 0x65, 0x79, 0x12, - 0x27, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, - 0x2e, 0x47, 0x65, 0x74, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x41, 0x50, 0x49, 0x4b, 0x65, - 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, - 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x41, 0x50, 0x49, 0x4b, 0x65, 0x79, - 0x22, 0x3c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x36, 0x12, 0x34, 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, 0x70, 0x69, - 0x2d, 0x6b, 0x65, 0x79, 0x73, 0x2f, 0x7b, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x7d, 0x12, 0x97, - 0x01, 0x0a, 0x0c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x50, 0x49, 0x4b, 0x65, 0x79, 0x12, - 0x2a, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, - 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x41, 0x50, - 0x49, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x74, 0x74, - 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x41, 0x50, 0x49, - 0x4b, 0x65, 0x79, 0x22, 0x43, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x3d, 0x3a, 0x01, 0x2a, 0x1a, 0x38, - 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, 0x70, 0x69, 0x2d, 0x6b, 0x65, 0x79, 0x73, 0x2f, 0x7b, 0x61, 0x70, 0x69, - 0x5f, 0x6b, 0x65, 0x79, 0x2e, 0x69, 0x64, 0x7d, 0x12, 0xbb, 0x02, 0x0a, 0x0f, 0x47, 0x65, 0x74, - 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x62, 0x6f, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x2d, 0x2e, 0x74, + 0x33, 0x2e, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x22, 0x89, 0x01, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x82, 0x01, 0x3a, 0x01, 0x2a, 0x5a, 0x4c, 0x3a, 0x01, 0x2a, 0x22, 0x47, 0x2f, 0x6f, 0x72, + 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x7b, 0x63, 0x6f, 0x6c, + 0x6c, 0x61, 0x62, 0x6f, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x6f, 0x72, 0x67, 0x61, 0x6e, 0x69, + 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x73, 0x2e, 0x6f, 0x72, 0x67, 0x61, 0x6e, + 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x67, 0x61, 0x74, 0x65, + 0x77, 0x61, 0x79, 0x73, 0x22, 0x2f, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x73, 0x2f, 0x7b, 0x63, 0x6f, + 0x6c, 0x6c, 0x61, 0x62, 0x6f, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x5f, + 0x69, 0x64, 0x73, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x67, 0x61, 0x74, + 0x65, 0x77, 0x61, 0x79, 0x73, 0x12, 0x6d, 0x0a, 0x03, 0x47, 0x65, 0x74, 0x12, 0x21, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x47, 0x65, - 0x74, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x62, 0x6f, 0x72, - 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x74, 0x74, - 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x47, 0x65, 0x74, - 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x62, 0x6f, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xcf, 0x01, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0xc8, 0x01, 0x5a, 0x56, - 0x12, 0x54, 0x2f, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x2f, 0x7b, 0x67, 0x61, 0x74, + 0x74, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x17, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, + 0x2e, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x22, 0x2a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x24, + 0x12, 0x22, 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, 0x63, 0x6f, 0x6c, 0x6c, 0x61, 0x62, 0x6f, 0x72, 0x61, 0x74, 0x6f, - 0x72, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x2f, 0x7b, 0x63, 0x6f, 0x6c, 0x6c, 0x61, 0x62, 0x6f, 0x72, - 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x2e, 0x75, 0x73, - 0x65, 0x72, 0x5f, 0x69, 0x64, 0x7d, 0x5a, 0x6e, 0x12, 0x6c, 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, 0x63, 0x6f, 0x6c, - 0x6c, 0x61, 0x62, 0x6f, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x6f, 0x72, 0x67, 0x61, 0x6e, 0x69, - 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x7b, 0x63, 0x6f, 0x6c, 0x6c, 0x61, 0x62, 0x6f, 0x72, - 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x6f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x5f, 0x69, 0x64, 0x73, 0x2e, 0x6f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x7d, 0x12, 0x95, 0x01, 0x0a, 0x0f, 0x53, 0x65, 0x74, 0x43, 0x6f, - 0x6c, 0x6c, 0x61, 0x62, 0x6f, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x2d, 0x2e, 0x74, 0x74, 0x6e, - 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x53, 0x65, 0x74, 0x47, - 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x62, 0x6f, 0x72, 0x61, 0x74, - 0x6f, 0x72, 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, 0x3b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x35, 0x3a, 0x01, 0x2a, 0x1a, 0x30, 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, 0x63, 0x6f, 0x6c, 0x6c, 0x61, 0x62, 0x6f, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x73, 0x12, 0x9d, - 0x01, 0x0a, 0x11, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x62, 0x6f, 0x72, 0x61, - 0x74, 0x6f, 0x72, 0x73, 0x12, 0x2f, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, - 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, - 0x79, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x62, 0x6f, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, - 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x62, 0x6f, 0x72, 0x61, - 0x74, 0x6f, 0x72, 0x73, 0x22, 0x38, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x32, 0x12, 0x30, 0x2f, 0x67, + 0x5f, 0x69, 0x64, 0x7d, 0x12, 0x6e, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x49, 0x64, 0x65, 0x6e, 0x74, + 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x46, 0x6f, 0x72, 0x45, 0x55, 0x49, 0x12, 0x32, 0x2e, 0x74, + 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x47, 0x65, + 0x74, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, + 0x65, 0x72, 0x73, 0x46, 0x6f, 0x72, 0x45, 0x55, 0x49, 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, 0x12, 0xd8, 0x01, 0x0a, 0x04, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x23, 0x2e, + 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4c, + 0x69, 0x73, 0x74, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, + 0x2e, 0x76, 0x33, 0x2e, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x22, 0x90, 0x01, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x89, 0x01, 0x5a, 0x31, 0x12, 0x2f, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x73, + 0x2f, 0x7b, 0x63, 0x6f, 0x6c, 0x6c, 0x61, 0x62, 0x6f, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x75, + 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x7d, + 0x2f, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x5a, 0x49, 0x12, 0x47, 0x2f, 0x6f, 0x72, + 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x7b, 0x63, 0x6f, 0x6c, + 0x6c, 0x61, 0x62, 0x6f, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x6f, 0x72, 0x67, 0x61, 0x6e, 0x69, + 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x73, 0x2e, 0x6f, 0x72, 0x67, 0x61, 0x6e, + 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x67, 0x61, 0x74, 0x65, + 0x77, 0x61, 0x79, 0x73, 0x12, 0x09, 0x2f, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x12, + 0x76, 0x0a, 0x06, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x24, 0x2e, 0x74, 0x74, 0x6e, 0x2e, + 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x17, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, + 0x2e, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x22, 0x2d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x27, + 0x3a, 0x01, 0x2a, 0x1a, 0x22, 0x2f, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x2f, 0x7b, + 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x69, 0x64, 0x73, 0x2e, 0x67, 0x61, 0x74, 0x65, + 0x77, 0x61, 0x79, 0x5f, 0x69, 0x64, 0x7d, 0x12, 0x64, 0x0a, 0x06, 0x44, 0x65, 0x6c, 0x65, 0x74, + 0x65, 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, 0x1e, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x2a, 0x16, 0x2f, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, + 0x2f, 0x7b, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x5f, 0x69, 0x64, 0x7d, 0x12, 0x6d, 0x0a, + 0x07, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 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, 0x26, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x22, 0x1e, 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, 0x63, 0x6f, 0x6c, 0x6c, 0x61, 0x62, 0x6f, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x73, 0x32, 0x76, - 0x0a, 0x13, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, - 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x5f, 0x0a, 0x11, 0x50, 0x75, 0x6c, 0x6c, 0x43, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2f, 0x2e, 0x74, 0x74, 0x6e, - 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x50, 0x75, 0x6c, 0x6c, - 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x74, 0x74, - 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x47, 0x61, 0x74, - 0x65, 0x77, 0x61, 0x79, 0x30, 0x01, 0x32, 0x88, 0x01, 0x0a, 0x12, 0x47, 0x61, 0x74, 0x65, 0x77, - 0x61, 0x79, 0x42, 0x61, 0x74, 0x63, 0x68, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x72, 0x0a, - 0x0c, 0x41, 0x73, 0x73, 0x65, 0x72, 0x74, 0x52, 0x69, 0x67, 0x68, 0x74, 0x73, 0x12, 0x2a, 0x2e, - 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x41, - 0x73, 0x73, 0x65, 0x72, 0x74, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x52, 0x69, 0x67, 0x68, - 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x72, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x12, 0x69, 0x0a, 0x05, + 0x50, 0x75, 0x72, 0x67, 0x65, 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, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x12, 0x16, 0x2f, 0x67, 0x61, 0x74, 0x65, - 0x77, 0x61, 0x79, 0x73, 0x2f, 0x72, 0x69, 0x67, 0x68, 0x74, 0x73, 0x2f, 0x62, 0x61, 0x74, 0x63, - 0x68, 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, + 0x79, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x2a, 0x1c, 0x2f, 0x67, 0x61, 0x74, 0x65, + 0x77, 0x61, 0x79, 0x73, 0x2f, 0x7b, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x5f, 0x69, 0x64, + 0x7d, 0x2f, 0x70, 0x75, 0x72, 0x67, 0x65, 0x32, 0xb3, 0x0a, 0x0a, 0x0d, 0x47, 0x61, 0x74, 0x65, + 0x77, 0x61, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x6f, 0x0a, 0x0a, 0x4c, 0x69, 0x73, + 0x74, 0x52, 0x69, 0x67, 0x68, 0x74, 0x73, 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, 0x74, 0x74, + 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x52, 0x69, 0x67, + 0x68, 0x74, 0x73, 0x22, 0x25, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1f, 0x12, 0x1d, 0x2f, 0x67, 0x61, + 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x2f, 0x7b, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x5f, + 0x69, 0x64, 0x7d, 0x2f, 0x72, 0x69, 0x67, 0x68, 0x74, 0x73, 0x12, 0x8a, 0x01, 0x0a, 0x0c, 0x43, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x50, 0x49, 0x4b, 0x65, 0x79, 0x12, 0x2a, 0x2e, 0x74, 0x74, + 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x43, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x41, 0x50, 0x49, 0x4b, 0x65, 0x79, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, + 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x41, 0x50, 0x49, 0x4b, 0x65, 0x79, 0x22, + 0x36, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x30, 0x3a, 0x01, 0x2a, 0x22, 0x2b, 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, + 0x70, 0x69, 0x2d, 0x6b, 0x65, 0x79, 0x73, 0x12, 0x86, 0x01, 0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74, + 0x41, 0x50, 0x49, 0x4b, 0x65, 0x79, 0x73, 0x12, 0x29, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, + 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x47, 0x61, 0x74, + 0x65, 0x77, 0x61, 0x79, 0x41, 0x50, 0x49, 0x4b, 0x65, 0x79, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, + 0x2e, 0x76, 0x33, 0x2e, 0x41, 0x50, 0x49, 0x4b, 0x65, 0x79, 0x73, 0x22, 0x33, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x2d, 0x12, 0x2b, 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, 0x70, 0x69, 0x2d, 0x6b, 0x65, 0x79, 0x73, + 0x12, 0x8a, 0x01, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x41, 0x50, 0x49, 0x4b, 0x65, 0x79, 0x12, 0x27, + 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, + 0x47, 0x65, 0x74, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x41, 0x50, 0x49, 0x4b, 0x65, 0x79, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, + 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x41, 0x50, 0x49, 0x4b, 0x65, 0x79, 0x22, + 0x3c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x36, 0x12, 0x34, 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, 0x70, 0x69, 0x2d, + 0x6b, 0x65, 0x79, 0x73, 0x2f, 0x7b, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x7d, 0x12, 0x97, 0x01, + 0x0a, 0x0c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x50, 0x49, 0x4b, 0x65, 0x79, 0x12, 0x2a, + 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, + 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x41, 0x50, 0x49, + 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x74, 0x74, 0x6e, + 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x41, 0x50, 0x49, 0x4b, + 0x65, 0x79, 0x22, 0x43, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x3d, 0x3a, 0x01, 0x2a, 0x1a, 0x38, 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, 0x70, 0x69, 0x2d, 0x6b, 0x65, 0x79, 0x73, 0x2f, 0x7b, 0x61, 0x70, 0x69, 0x5f, + 0x6b, 0x65, 0x79, 0x2e, 0x69, 0x64, 0x7d, 0x12, 0xbb, 0x02, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x43, + 0x6f, 0x6c, 0x6c, 0x61, 0x62, 0x6f, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x2d, 0x2e, 0x74, 0x74, + 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x47, 0x65, 0x74, + 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x62, 0x6f, 0x72, 0x61, + 0x74, 0x6f, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x74, 0x74, 0x6e, + 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x47, 0x65, 0x74, 0x43, + 0x6f, 0x6c, 0x6c, 0x61, 0x62, 0x6f, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0xcf, 0x01, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0xc8, 0x01, 0x5a, 0x56, 0x12, + 0x54, 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, 0x63, 0x6f, 0x6c, 0x6c, 0x61, 0x62, 0x6f, 0x72, 0x61, 0x74, 0x6f, 0x72, + 0x2f, 0x75, 0x73, 0x65, 0x72, 0x2f, 0x7b, 0x63, 0x6f, 0x6c, 0x6c, 0x61, 0x62, 0x6f, 0x72, 0x61, + 0x74, 0x6f, 0x72, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x2e, 0x75, 0x73, 0x65, + 0x72, 0x5f, 0x69, 0x64, 0x7d, 0x5a, 0x6e, 0x12, 0x6c, 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, 0x63, 0x6f, 0x6c, 0x6c, + 0x61, 0x62, 0x6f, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x6f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x7b, 0x63, 0x6f, 0x6c, 0x6c, 0x61, 0x62, 0x6f, 0x72, 0x61, + 0x74, 0x6f, 0x72, 0x2e, 0x6f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x5f, 0x69, 0x64, 0x73, 0x2e, 0x6f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x5f, 0x69, 0x64, 0x7d, 0x12, 0x95, 0x01, 0x0a, 0x0f, 0x53, 0x65, 0x74, 0x43, 0x6f, 0x6c, + 0x6c, 0x61, 0x62, 0x6f, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x2d, 0x2e, 0x74, 0x74, 0x6e, 0x2e, + 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x53, 0x65, 0x74, 0x47, 0x61, + 0x74, 0x65, 0x77, 0x61, 0x79, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x62, 0x6f, 0x72, 0x61, 0x74, 0x6f, + 0x72, 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, 0x3b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x35, 0x3a, 0x01, 0x2a, 0x1a, 0x30, 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, + 0x63, 0x6f, 0x6c, 0x6c, 0x61, 0x62, 0x6f, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x73, 0x12, 0x9d, 0x01, + 0x0a, 0x11, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x62, 0x6f, 0x72, 0x61, 0x74, + 0x6f, 0x72, 0x73, 0x12, 0x2f, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, + 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, + 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x62, 0x6f, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, + 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x62, 0x6f, 0x72, 0x61, 0x74, + 0x6f, 0x72, 0x73, 0x22, 0x38, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x32, 0x12, 0x30, 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, + 0x63, 0x6f, 0x6c, 0x6c, 0x61, 0x62, 0x6f, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x73, 0x32, 0x76, 0x0a, + 0x13, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, + 0x61, 0x74, 0x6f, 0x72, 0x12, 0x5f, 0x0a, 0x11, 0x50, 0x75, 0x6c, 0x6c, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2f, 0x2e, 0x74, 0x74, 0x6e, 0x2e, + 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x50, 0x75, 0x6c, 0x6c, 0x47, + 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x74, 0x74, 0x6e, + 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x47, 0x61, 0x74, 0x65, + 0x77, 0x61, 0x79, 0x30, 0x01, 0x32, 0x88, 0x01, 0x0a, 0x12, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, + 0x79, 0x42, 0x61, 0x74, 0x63, 0x68, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x72, 0x0a, 0x0c, + 0x41, 0x73, 0x73, 0x65, 0x72, 0x74, 0x52, 0x69, 0x67, 0x68, 0x74, 0x73, 0x12, 0x2a, 0x2e, 0x74, + 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x41, 0x73, + 0x73, 0x65, 0x72, 0x74, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x52, 0x69, 0x67, 0x68, 0x74, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, + 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x12, 0x16, 0x2f, 0x67, 0x61, 0x74, 0x65, 0x77, + 0x61, 0x79, 0x73, 0x2f, 0x72, 0x69, 0x67, 0x68, 0x74, 0x73, 0x2f, 0x62, 0x61, 0x74, 0x63, 0x68, + 0x32, 0x7d, 0x0a, 0x14, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x42, 0x61, 0x74, 0x63, 0x68, + 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x12, 0x65, 0x0a, 0x06, 0x44, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x12, 0x2a, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, + 0x2e, 0x76, 0x33, 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x47, + 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x17, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11, 0x2a, 0x0f, + 0x2f, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x2f, 0x62, 0x61, 0x74, 0x63, 0x68, 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 ( @@ -376,79 +438,83 @@ func file_ttn_lorawan_v3_gateway_services_proto_rawDescGZIP() []byte { return file_ttn_lorawan_v3_gateway_services_proto_rawDescData } -var file_ttn_lorawan_v3_gateway_services_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_ttn_lorawan_v3_gateway_services_proto_msgTypes = make([]protoimpl.MessageInfo, 3) var file_ttn_lorawan_v3_gateway_services_proto_goTypes = []interface{}{ (*PullGatewayConfigurationRequest)(nil), // 0: ttn.lorawan.v3.PullGatewayConfigurationRequest (*AssertGatewayRightsRequest)(nil), // 1: ttn.lorawan.v3.AssertGatewayRightsRequest - (*GatewayIdentifiers)(nil), // 2: ttn.lorawan.v3.GatewayIdentifiers - (*fieldmaskpb.FieldMask)(nil), // 3: google.protobuf.FieldMask - (*Rights)(nil), // 4: ttn.lorawan.v3.Rights - (*CreateGatewayRequest)(nil), // 5: ttn.lorawan.v3.CreateGatewayRequest - (*GetGatewayRequest)(nil), // 6: ttn.lorawan.v3.GetGatewayRequest - (*GetGatewayIdentifiersForEUIRequest)(nil), // 7: ttn.lorawan.v3.GetGatewayIdentifiersForEUIRequest - (*ListGatewaysRequest)(nil), // 8: ttn.lorawan.v3.ListGatewaysRequest - (*UpdateGatewayRequest)(nil), // 9: ttn.lorawan.v3.UpdateGatewayRequest - (*CreateGatewayAPIKeyRequest)(nil), // 10: ttn.lorawan.v3.CreateGatewayAPIKeyRequest - (*ListGatewayAPIKeysRequest)(nil), // 11: ttn.lorawan.v3.ListGatewayAPIKeysRequest - (*GetGatewayAPIKeyRequest)(nil), // 12: ttn.lorawan.v3.GetGatewayAPIKeyRequest - (*UpdateGatewayAPIKeyRequest)(nil), // 13: ttn.lorawan.v3.UpdateGatewayAPIKeyRequest - (*GetGatewayCollaboratorRequest)(nil), // 14: ttn.lorawan.v3.GetGatewayCollaboratorRequest - (*SetGatewayCollaboratorRequest)(nil), // 15: ttn.lorawan.v3.SetGatewayCollaboratorRequest - (*ListGatewayCollaboratorsRequest)(nil), // 16: ttn.lorawan.v3.ListGatewayCollaboratorsRequest - (*Gateway)(nil), // 17: ttn.lorawan.v3.Gateway - (*Gateways)(nil), // 18: ttn.lorawan.v3.Gateways - (*emptypb.Empty)(nil), // 19: google.protobuf.Empty - (*APIKey)(nil), // 20: ttn.lorawan.v3.APIKey - (*APIKeys)(nil), // 21: ttn.lorawan.v3.APIKeys - (*GetCollaboratorResponse)(nil), // 22: ttn.lorawan.v3.GetCollaboratorResponse - (*Collaborators)(nil), // 23: ttn.lorawan.v3.Collaborators + (*BatchDeleteGatewaysRequest)(nil), // 2: ttn.lorawan.v3.BatchDeleteGatewaysRequest + (*GatewayIdentifiers)(nil), // 3: ttn.lorawan.v3.GatewayIdentifiers + (*fieldmaskpb.FieldMask)(nil), // 4: google.protobuf.FieldMask + (*Rights)(nil), // 5: ttn.lorawan.v3.Rights + (*CreateGatewayRequest)(nil), // 6: ttn.lorawan.v3.CreateGatewayRequest + (*GetGatewayRequest)(nil), // 7: ttn.lorawan.v3.GetGatewayRequest + (*GetGatewayIdentifiersForEUIRequest)(nil), // 8: ttn.lorawan.v3.GetGatewayIdentifiersForEUIRequest + (*ListGatewaysRequest)(nil), // 9: ttn.lorawan.v3.ListGatewaysRequest + (*UpdateGatewayRequest)(nil), // 10: ttn.lorawan.v3.UpdateGatewayRequest + (*CreateGatewayAPIKeyRequest)(nil), // 11: ttn.lorawan.v3.CreateGatewayAPIKeyRequest + (*ListGatewayAPIKeysRequest)(nil), // 12: ttn.lorawan.v3.ListGatewayAPIKeysRequest + (*GetGatewayAPIKeyRequest)(nil), // 13: ttn.lorawan.v3.GetGatewayAPIKeyRequest + (*UpdateGatewayAPIKeyRequest)(nil), // 14: ttn.lorawan.v3.UpdateGatewayAPIKeyRequest + (*GetGatewayCollaboratorRequest)(nil), // 15: ttn.lorawan.v3.GetGatewayCollaboratorRequest + (*SetGatewayCollaboratorRequest)(nil), // 16: ttn.lorawan.v3.SetGatewayCollaboratorRequest + (*ListGatewayCollaboratorsRequest)(nil), // 17: ttn.lorawan.v3.ListGatewayCollaboratorsRequest + (*Gateway)(nil), // 18: ttn.lorawan.v3.Gateway + (*Gateways)(nil), // 19: ttn.lorawan.v3.Gateways + (*emptypb.Empty)(nil), // 20: google.protobuf.Empty + (*APIKey)(nil), // 21: ttn.lorawan.v3.APIKey + (*APIKeys)(nil), // 22: ttn.lorawan.v3.APIKeys + (*GetCollaboratorResponse)(nil), // 23: ttn.lorawan.v3.GetCollaboratorResponse + (*Collaborators)(nil), // 24: ttn.lorawan.v3.Collaborators } var file_ttn_lorawan_v3_gateway_services_proto_depIdxs = []int32{ - 2, // 0: ttn.lorawan.v3.PullGatewayConfigurationRequest.gateway_ids:type_name -> ttn.lorawan.v3.GatewayIdentifiers - 3, // 1: ttn.lorawan.v3.PullGatewayConfigurationRequest.field_mask:type_name -> google.protobuf.FieldMask - 2, // 2: ttn.lorawan.v3.AssertGatewayRightsRequest.gateway_ids:type_name -> ttn.lorawan.v3.GatewayIdentifiers - 4, // 3: ttn.lorawan.v3.AssertGatewayRightsRequest.required:type_name -> ttn.lorawan.v3.Rights - 5, // 4: ttn.lorawan.v3.GatewayRegistry.Create:input_type -> ttn.lorawan.v3.CreateGatewayRequest - 6, // 5: ttn.lorawan.v3.GatewayRegistry.Get:input_type -> ttn.lorawan.v3.GetGatewayRequest - 7, // 6: ttn.lorawan.v3.GatewayRegistry.GetIdentifiersForEUI:input_type -> ttn.lorawan.v3.GetGatewayIdentifiersForEUIRequest - 8, // 7: ttn.lorawan.v3.GatewayRegistry.List:input_type -> ttn.lorawan.v3.ListGatewaysRequest - 9, // 8: ttn.lorawan.v3.GatewayRegistry.Update:input_type -> ttn.lorawan.v3.UpdateGatewayRequest - 2, // 9: ttn.lorawan.v3.GatewayRegistry.Delete:input_type -> ttn.lorawan.v3.GatewayIdentifiers - 2, // 10: ttn.lorawan.v3.GatewayRegistry.Restore:input_type -> ttn.lorawan.v3.GatewayIdentifiers - 2, // 11: ttn.lorawan.v3.GatewayRegistry.Purge:input_type -> ttn.lorawan.v3.GatewayIdentifiers - 2, // 12: ttn.lorawan.v3.GatewayAccess.ListRights:input_type -> ttn.lorawan.v3.GatewayIdentifiers - 10, // 13: ttn.lorawan.v3.GatewayAccess.CreateAPIKey:input_type -> ttn.lorawan.v3.CreateGatewayAPIKeyRequest - 11, // 14: ttn.lorawan.v3.GatewayAccess.ListAPIKeys:input_type -> ttn.lorawan.v3.ListGatewayAPIKeysRequest - 12, // 15: ttn.lorawan.v3.GatewayAccess.GetAPIKey:input_type -> ttn.lorawan.v3.GetGatewayAPIKeyRequest - 13, // 16: ttn.lorawan.v3.GatewayAccess.UpdateAPIKey:input_type -> ttn.lorawan.v3.UpdateGatewayAPIKeyRequest - 14, // 17: ttn.lorawan.v3.GatewayAccess.GetCollaborator:input_type -> ttn.lorawan.v3.GetGatewayCollaboratorRequest - 15, // 18: ttn.lorawan.v3.GatewayAccess.SetCollaborator:input_type -> ttn.lorawan.v3.SetGatewayCollaboratorRequest - 16, // 19: ttn.lorawan.v3.GatewayAccess.ListCollaborators:input_type -> ttn.lorawan.v3.ListGatewayCollaboratorsRequest - 0, // 20: ttn.lorawan.v3.GatewayConfigurator.PullConfiguration:input_type -> ttn.lorawan.v3.PullGatewayConfigurationRequest - 1, // 21: ttn.lorawan.v3.GatewayBatchAccess.AssertRights:input_type -> ttn.lorawan.v3.AssertGatewayRightsRequest - 17, // 22: ttn.lorawan.v3.GatewayRegistry.Create:output_type -> ttn.lorawan.v3.Gateway - 17, // 23: ttn.lorawan.v3.GatewayRegistry.Get:output_type -> ttn.lorawan.v3.Gateway - 2, // 24: ttn.lorawan.v3.GatewayRegistry.GetIdentifiersForEUI:output_type -> ttn.lorawan.v3.GatewayIdentifiers - 18, // 25: ttn.lorawan.v3.GatewayRegistry.List:output_type -> ttn.lorawan.v3.Gateways - 17, // 26: ttn.lorawan.v3.GatewayRegistry.Update:output_type -> ttn.lorawan.v3.Gateway - 19, // 27: ttn.lorawan.v3.GatewayRegistry.Delete:output_type -> google.protobuf.Empty - 19, // 28: ttn.lorawan.v3.GatewayRegistry.Restore:output_type -> google.protobuf.Empty - 19, // 29: ttn.lorawan.v3.GatewayRegistry.Purge:output_type -> google.protobuf.Empty - 4, // 30: ttn.lorawan.v3.GatewayAccess.ListRights:output_type -> ttn.lorawan.v3.Rights - 20, // 31: ttn.lorawan.v3.GatewayAccess.CreateAPIKey:output_type -> ttn.lorawan.v3.APIKey - 21, // 32: ttn.lorawan.v3.GatewayAccess.ListAPIKeys:output_type -> ttn.lorawan.v3.APIKeys - 20, // 33: ttn.lorawan.v3.GatewayAccess.GetAPIKey:output_type -> ttn.lorawan.v3.APIKey - 20, // 34: ttn.lorawan.v3.GatewayAccess.UpdateAPIKey:output_type -> ttn.lorawan.v3.APIKey - 22, // 35: ttn.lorawan.v3.GatewayAccess.GetCollaborator:output_type -> ttn.lorawan.v3.GetCollaboratorResponse - 19, // 36: ttn.lorawan.v3.GatewayAccess.SetCollaborator:output_type -> google.protobuf.Empty - 23, // 37: ttn.lorawan.v3.GatewayAccess.ListCollaborators:output_type -> ttn.lorawan.v3.Collaborators - 17, // 38: ttn.lorawan.v3.GatewayConfigurator.PullConfiguration:output_type -> ttn.lorawan.v3.Gateway - 19, // 39: ttn.lorawan.v3.GatewayBatchAccess.AssertRights:output_type -> google.protobuf.Empty - 22, // [22:40] is the sub-list for method output_type - 4, // [4:22] is the sub-list for method input_type - 4, // [4:4] is the sub-list for extension type_name - 4, // [4:4] is the sub-list for extension extendee - 0, // [0:4] is the sub-list for field type_name + 3, // 0: ttn.lorawan.v3.PullGatewayConfigurationRequest.gateway_ids:type_name -> ttn.lorawan.v3.GatewayIdentifiers + 4, // 1: ttn.lorawan.v3.PullGatewayConfigurationRequest.field_mask:type_name -> google.protobuf.FieldMask + 3, // 2: ttn.lorawan.v3.AssertGatewayRightsRequest.gateway_ids:type_name -> ttn.lorawan.v3.GatewayIdentifiers + 5, // 3: ttn.lorawan.v3.AssertGatewayRightsRequest.required:type_name -> ttn.lorawan.v3.Rights + 3, // 4: ttn.lorawan.v3.BatchDeleteGatewaysRequest.gateway_ids:type_name -> ttn.lorawan.v3.GatewayIdentifiers + 6, // 5: ttn.lorawan.v3.GatewayRegistry.Create:input_type -> ttn.lorawan.v3.CreateGatewayRequest + 7, // 6: ttn.lorawan.v3.GatewayRegistry.Get:input_type -> ttn.lorawan.v3.GetGatewayRequest + 8, // 7: ttn.lorawan.v3.GatewayRegistry.GetIdentifiersForEUI:input_type -> ttn.lorawan.v3.GetGatewayIdentifiersForEUIRequest + 9, // 8: ttn.lorawan.v3.GatewayRegistry.List:input_type -> ttn.lorawan.v3.ListGatewaysRequest + 10, // 9: ttn.lorawan.v3.GatewayRegistry.Update:input_type -> ttn.lorawan.v3.UpdateGatewayRequest + 3, // 10: ttn.lorawan.v3.GatewayRegistry.Delete:input_type -> ttn.lorawan.v3.GatewayIdentifiers + 3, // 11: ttn.lorawan.v3.GatewayRegistry.Restore:input_type -> ttn.lorawan.v3.GatewayIdentifiers + 3, // 12: ttn.lorawan.v3.GatewayRegistry.Purge:input_type -> ttn.lorawan.v3.GatewayIdentifiers + 3, // 13: ttn.lorawan.v3.GatewayAccess.ListRights:input_type -> ttn.lorawan.v3.GatewayIdentifiers + 11, // 14: ttn.lorawan.v3.GatewayAccess.CreateAPIKey:input_type -> ttn.lorawan.v3.CreateGatewayAPIKeyRequest + 12, // 15: ttn.lorawan.v3.GatewayAccess.ListAPIKeys:input_type -> ttn.lorawan.v3.ListGatewayAPIKeysRequest + 13, // 16: ttn.lorawan.v3.GatewayAccess.GetAPIKey:input_type -> ttn.lorawan.v3.GetGatewayAPIKeyRequest + 14, // 17: ttn.lorawan.v3.GatewayAccess.UpdateAPIKey:input_type -> ttn.lorawan.v3.UpdateGatewayAPIKeyRequest + 15, // 18: ttn.lorawan.v3.GatewayAccess.GetCollaborator:input_type -> ttn.lorawan.v3.GetGatewayCollaboratorRequest + 16, // 19: ttn.lorawan.v3.GatewayAccess.SetCollaborator:input_type -> ttn.lorawan.v3.SetGatewayCollaboratorRequest + 17, // 20: ttn.lorawan.v3.GatewayAccess.ListCollaborators:input_type -> ttn.lorawan.v3.ListGatewayCollaboratorsRequest + 0, // 21: ttn.lorawan.v3.GatewayConfigurator.PullConfiguration:input_type -> ttn.lorawan.v3.PullGatewayConfigurationRequest + 1, // 22: ttn.lorawan.v3.GatewayBatchAccess.AssertRights:input_type -> ttn.lorawan.v3.AssertGatewayRightsRequest + 2, // 23: ttn.lorawan.v3.GatewayBatchRegistry.Delete:input_type -> ttn.lorawan.v3.BatchDeleteGatewaysRequest + 18, // 24: ttn.lorawan.v3.GatewayRegistry.Create:output_type -> ttn.lorawan.v3.Gateway + 18, // 25: ttn.lorawan.v3.GatewayRegistry.Get:output_type -> ttn.lorawan.v3.Gateway + 3, // 26: ttn.lorawan.v3.GatewayRegistry.GetIdentifiersForEUI:output_type -> ttn.lorawan.v3.GatewayIdentifiers + 19, // 27: ttn.lorawan.v3.GatewayRegistry.List:output_type -> ttn.lorawan.v3.Gateways + 18, // 28: ttn.lorawan.v3.GatewayRegistry.Update:output_type -> ttn.lorawan.v3.Gateway + 20, // 29: ttn.lorawan.v3.GatewayRegistry.Delete:output_type -> google.protobuf.Empty + 20, // 30: ttn.lorawan.v3.GatewayRegistry.Restore:output_type -> google.protobuf.Empty + 20, // 31: ttn.lorawan.v3.GatewayRegistry.Purge:output_type -> google.protobuf.Empty + 5, // 32: ttn.lorawan.v3.GatewayAccess.ListRights:output_type -> ttn.lorawan.v3.Rights + 21, // 33: ttn.lorawan.v3.GatewayAccess.CreateAPIKey:output_type -> ttn.lorawan.v3.APIKey + 22, // 34: ttn.lorawan.v3.GatewayAccess.ListAPIKeys:output_type -> ttn.lorawan.v3.APIKeys + 21, // 35: ttn.lorawan.v3.GatewayAccess.GetAPIKey:output_type -> ttn.lorawan.v3.APIKey + 21, // 36: ttn.lorawan.v3.GatewayAccess.UpdateAPIKey:output_type -> ttn.lorawan.v3.APIKey + 23, // 37: ttn.lorawan.v3.GatewayAccess.GetCollaborator:output_type -> ttn.lorawan.v3.GetCollaboratorResponse + 20, // 38: ttn.lorawan.v3.GatewayAccess.SetCollaborator:output_type -> google.protobuf.Empty + 24, // 39: ttn.lorawan.v3.GatewayAccess.ListCollaborators:output_type -> ttn.lorawan.v3.Collaborators + 18, // 40: ttn.lorawan.v3.GatewayConfigurator.PullConfiguration:output_type -> ttn.lorawan.v3.Gateway + 20, // 41: ttn.lorawan.v3.GatewayBatchAccess.AssertRights:output_type -> google.protobuf.Empty + 20, // 42: ttn.lorawan.v3.GatewayBatchRegistry.Delete:output_type -> google.protobuf.Empty + 24, // [24:43] is the sub-list for method output_type + 5, // [5:24] is the sub-list for method input_type + 5, // [5:5] is the sub-list for extension type_name + 5, // [5:5] is the sub-list for extension extendee + 0, // [0:5] is the sub-list for field type_name } func init() { file_ttn_lorawan_v3_gateway_services_proto_init() } @@ -484,6 +550,18 @@ func file_ttn_lorawan_v3_gateway_services_proto_init() { return nil } } + file_ttn_lorawan_v3_gateway_services_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*BatchDeleteGatewaysRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } type x struct{} out := protoimpl.TypeBuilder{ @@ -491,9 +569,9 @@ func file_ttn_lorawan_v3_gateway_services_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_ttn_lorawan_v3_gateway_services_proto_rawDesc, NumEnums: 0, - NumMessages: 2, + NumMessages: 3, NumExtensions: 0, - NumServices: 4, + NumServices: 5, }, GoTypes: file_ttn_lorawan_v3_gateway_services_proto_goTypes, DependencyIndexes: file_ttn_lorawan_v3_gateway_services_proto_depIdxs, diff --git a/pkg/ttnpb/gateway_services.pb.gw.go b/pkg/ttnpb/gateway_services.pb.gw.go index c0c5e511dd..b28c3dea73 100644 --- a/pkg/ttnpb/gateway_services.pb.gw.go +++ b/pkg/ttnpb/gateway_services.pb.gw.go @@ -1431,6 +1431,42 @@ func local_request_GatewayBatchAccess_AssertRights_0(ctx context.Context, marsha } +var ( + filter_GatewayBatchRegistry_Delete_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_GatewayBatchRegistry_Delete_0(ctx context.Context, marshaler runtime.Marshaler, client GatewayBatchRegistryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq BatchDeleteGatewaysRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_GatewayBatchRegistry_Delete_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.Delete(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_GatewayBatchRegistry_Delete_0(ctx context.Context, marshaler runtime.Marshaler, server GatewayBatchRegistryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq BatchDeleteGatewaysRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_GatewayBatchRegistry_Delete_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.Delete(ctx, &protoReq) + return msg, metadata, err + +} + // RegisterGatewayRegistryHandlerServer registers the http handlers for service GatewayRegistry to "mux". // UnaryRPC :call GatewayRegistryServer directly. // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. @@ -1958,6 +1994,40 @@ func RegisterGatewayBatchAccessHandlerServer(ctx context.Context, mux *runtime.S return nil } +// RegisterGatewayBatchRegistryHandlerServer registers the http handlers for service GatewayBatchRegistry to "mux". +// UnaryRPC :call GatewayBatchRegistryServer directly. +// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. +// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterGatewayBatchRegistryHandlerFromEndpoint instead. +func RegisterGatewayBatchRegistryHandlerServer(ctx context.Context, mux *runtime.ServeMux, server GatewayBatchRegistryServer) error { + + mux.Handle("DELETE", pattern_GatewayBatchRegistry_Delete_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + var err error + var annotatedContext context.Context + annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/ttn.lorawan.v3.GatewayBatchRegistry/Delete", runtime.WithHTTPPathPattern("/gateways/batch")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_GatewayBatchRegistry_Delete_0(annotatedContext, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + + forward_GatewayBatchRegistry_Delete_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + // RegisterGatewayRegistryHandlerFromEndpoint is same as RegisterGatewayRegistryHandler but // automatically dials to "endpoint" and closes the connection when "ctx" gets done. func RegisterGatewayRegistryHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { @@ -2612,3 +2682,74 @@ var ( var ( forward_GatewayBatchAccess_AssertRights_0 = runtime.ForwardResponseMessage ) + +// RegisterGatewayBatchRegistryHandlerFromEndpoint is same as RegisterGatewayBatchRegistryHandler but +// automatically dials to "endpoint" and closes the connection when "ctx" gets done. +func RegisterGatewayBatchRegistryHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { + conn, err := grpc.DialContext(ctx, endpoint, opts...) + if err != nil { + return err + } + defer func() { + if err != nil { + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + return + } + go func() { + <-ctx.Done() + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + }() + }() + + return RegisterGatewayBatchRegistryHandler(ctx, mux, conn) +} + +// RegisterGatewayBatchRegistryHandler registers the http handlers for service GatewayBatchRegistry to "mux". +// The handlers forward requests to the grpc endpoint over "conn". +func RegisterGatewayBatchRegistryHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { + return RegisterGatewayBatchRegistryHandlerClient(ctx, mux, NewGatewayBatchRegistryClient(conn)) +} + +// RegisterGatewayBatchRegistryHandlerClient registers the http handlers for service GatewayBatchRegistry +// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "GatewayBatchRegistryClient". +// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "GatewayBatchRegistryClient" +// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in +// "GatewayBatchRegistryClient" to call the correct interceptors. +func RegisterGatewayBatchRegistryHandlerClient(ctx context.Context, mux *runtime.ServeMux, client GatewayBatchRegistryClient) error { + + mux.Handle("DELETE", pattern_GatewayBatchRegistry_Delete_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + var err error + var annotatedContext context.Context + annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/ttn.lorawan.v3.GatewayBatchRegistry/Delete", runtime.WithHTTPPathPattern("/gateways/batch")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_GatewayBatchRegistry_Delete_0(annotatedContext, inboundMarshaler, client, req, pathParams) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + + forward_GatewayBatchRegistry_Delete_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +var ( + pattern_GatewayBatchRegistry_Delete_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"gateways", "batch"}, "")) +) + +var ( + forward_GatewayBatchRegistry_Delete_0 = runtime.ForwardResponseMessage +) diff --git a/pkg/ttnpb/gateway_services.pb.paths.fm.go b/pkg/ttnpb/gateway_services.pb.paths.fm.go index 3a994f2120..f1de1660d1 100644 --- a/pkg/ttnpb/gateway_services.pb.paths.fm.go +++ b/pkg/ttnpb/gateway_services.pb.paths.fm.go @@ -23,3 +23,10 @@ var AssertGatewayRightsRequestFieldPathsTopLevel = []string{ "gateway_ids", "required", } +var BatchDeleteGatewaysRequestFieldPathsNested = []string{ + "gateway_ids", +} + +var BatchDeleteGatewaysRequestFieldPathsTopLevel = []string{ + "gateway_ids", +} diff --git a/pkg/ttnpb/gateway_services.pb.setters.fm.go b/pkg/ttnpb/gateway_services.pb.setters.fm.go index c8e7bc2182..ec2dc2abb6 100644 --- a/pkg/ttnpb/gateway_services.pb.setters.fm.go +++ b/pkg/ttnpb/gateway_services.pb.setters.fm.go @@ -93,3 +93,23 @@ func (dst *AssertGatewayRightsRequest) SetFields(src *AssertGatewayRightsRequest } return nil } + +func (dst *BatchDeleteGatewaysRequest) SetFields(src *BatchDeleteGatewaysRequest, paths ...string) error { + for name, subs := range _processPaths(paths) { + switch name { + case "gateway_ids": + if len(subs) > 0 { + return fmt.Errorf("'gateway_ids' has no subfields, but %s were specified", subs) + } + if src != nil { + dst.GatewayIds = src.GatewayIds + } else { + dst.GatewayIds = nil + } + + default: + return fmt.Errorf("invalid field: '%s'", name) + } + } + return nil +} diff --git a/pkg/ttnpb/gateway_services.pb.validate.go b/pkg/ttnpb/gateway_services.pb.validate.go index 8061e73113..8d21dc3a21 100644 --- a/pkg/ttnpb/gateway_services.pb.validate.go +++ b/pkg/ttnpb/gateway_services.pb.validate.go @@ -262,3 +262,109 @@ var _ interface { Cause() error ErrorName() string } = AssertGatewayRightsRequestValidationError{} + +// ValidateFields checks the field values on BatchDeleteGatewaysRequest with +// the rules defined in the proto definition for this message. If any rules +// are violated, an error is returned. +func (m *BatchDeleteGatewaysRequest) ValidateFields(paths ...string) error { + if m == nil { + return nil + } + + if len(paths) == 0 { + paths = BatchDeleteGatewaysRequestFieldPathsNested + } + + for name, subs := range _processPaths(append(paths[:0:0], paths...)) { + _ = subs + switch name { + case "gateway_ids": + + if l := len(m.GetGatewayIds()); l < 1 || l > 20 { + return BatchDeleteGatewaysRequestValidationError{ + field: "gateway_ids", + reason: "value must contain between 1 and 20 items, inclusive", + } + } + + for idx, item := range m.GetGatewayIds() { + _, _ = idx, item + + if v, ok := interface{}(item).(interface{ ValidateFields(...string) error }); ok { + if err := v.ValidateFields(subs...); err != nil { + return BatchDeleteGatewaysRequestValidationError{ + field: fmt.Sprintf("gateway_ids[%v]", idx), + reason: "embedded message failed validation", + cause: err, + } + } + } + + } + + default: + return BatchDeleteGatewaysRequestValidationError{ + field: name, + reason: "invalid field path", + } + } + } + return nil +} + +// BatchDeleteGatewaysRequestValidationError is the validation error returned +// by BatchDeleteGatewaysRequest.ValidateFields if the designated constraints +// aren't met. +type BatchDeleteGatewaysRequestValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e BatchDeleteGatewaysRequestValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e BatchDeleteGatewaysRequestValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e BatchDeleteGatewaysRequestValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e BatchDeleteGatewaysRequestValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e BatchDeleteGatewaysRequestValidationError) ErrorName() string { + return "BatchDeleteGatewaysRequestValidationError" +} + +// Error satisfies the builtin error interface +func (e BatchDeleteGatewaysRequestValidationError) Error() string { + cause := "" + if e.cause != nil { + cause = fmt.Sprintf(" | caused by: %v", e.cause) + } + + key := "" + if e.key { + key = "key for " + } + + return fmt.Sprintf( + "invalid %sBatchDeleteGatewaysRequest.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = BatchDeleteGatewaysRequestValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = BatchDeleteGatewaysRequestValidationError{} diff --git a/pkg/ttnpb/gateway_services_grpc.pb.go b/pkg/ttnpb/gateway_services_grpc.pb.go index 2b7fc19a4e..49caa959bb 100644 --- a/pkg/ttnpb/gateway_services_grpc.pb.go +++ b/pkg/ttnpb/gateway_services_grpc.pb.go @@ -1009,3 +1009,99 @@ var GatewayBatchAccess_ServiceDesc = grpc.ServiceDesc{ Streams: []grpc.StreamDesc{}, Metadata: "ttn/lorawan/v3/gateway_services.proto", } + +const ( + GatewayBatchRegistry_Delete_FullMethodName = "/ttn.lorawan.v3.GatewayBatchRegistry/Delete" +) + +// GatewayBatchRegistryClient is the client API for GatewayBatchRegistry service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type GatewayBatchRegistryClient interface { + // Delete a batch of gateways. + // This operation is atomic; either all gateways are deleted or none. + // The caller must have delete rights on all requested gateways. + Delete(ctx context.Context, in *BatchDeleteGatewaysRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) +} + +type gatewayBatchRegistryClient struct { + cc grpc.ClientConnInterface +} + +func NewGatewayBatchRegistryClient(cc grpc.ClientConnInterface) GatewayBatchRegistryClient { + return &gatewayBatchRegistryClient{cc} +} + +func (c *gatewayBatchRegistryClient) Delete(ctx context.Context, in *BatchDeleteGatewaysRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { + out := new(emptypb.Empty) + err := c.cc.Invoke(ctx, GatewayBatchRegistry_Delete_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// GatewayBatchRegistryServer is the server API for GatewayBatchRegistry service. +// All implementations must embed UnimplementedGatewayBatchRegistryServer +// for forward compatibility +type GatewayBatchRegistryServer interface { + // Delete a batch of gateways. + // This operation is atomic; either all gateways are deleted or none. + // The caller must have delete rights on all requested gateways. + Delete(context.Context, *BatchDeleteGatewaysRequest) (*emptypb.Empty, error) + mustEmbedUnimplementedGatewayBatchRegistryServer() +} + +// UnimplementedGatewayBatchRegistryServer must be embedded to have forward compatible implementations. +type UnimplementedGatewayBatchRegistryServer struct { +} + +func (UnimplementedGatewayBatchRegistryServer) Delete(context.Context, *BatchDeleteGatewaysRequest) (*emptypb.Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method Delete not implemented") +} +func (UnimplementedGatewayBatchRegistryServer) mustEmbedUnimplementedGatewayBatchRegistryServer() {} + +// UnsafeGatewayBatchRegistryServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to GatewayBatchRegistryServer will +// result in compilation errors. +type UnsafeGatewayBatchRegistryServer interface { + mustEmbedUnimplementedGatewayBatchRegistryServer() +} + +func RegisterGatewayBatchRegistryServer(s grpc.ServiceRegistrar, srv GatewayBatchRegistryServer) { + s.RegisterService(&GatewayBatchRegistry_ServiceDesc, srv) +} + +func _GatewayBatchRegistry_Delete_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(BatchDeleteGatewaysRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GatewayBatchRegistryServer).Delete(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: GatewayBatchRegistry_Delete_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GatewayBatchRegistryServer).Delete(ctx, req.(*BatchDeleteGatewaysRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// GatewayBatchRegistry_ServiceDesc is the grpc.ServiceDesc for GatewayBatchRegistry service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var GatewayBatchRegistry_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "ttn.lorawan.v3.GatewayBatchRegistry", + HandlerType: (*GatewayBatchRegistryServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Delete", + Handler: _GatewayBatchRegistry_Delete_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "ttn/lorawan/v3/gateway_services.proto", +} diff --git a/pkg/ttnpb/gateway_services_json.pb.go b/pkg/ttnpb/gateway_services_json.pb.go index 548e8fbc72..4106bc54fe 100644 --- a/pkg/ttnpb/gateway_services_json.pb.go +++ b/pkg/ttnpb/gateway_services_json.pb.go @@ -151,3 +151,66 @@ func (x *AssertGatewayRightsRequest) UnmarshalProtoJSON(s *jsonplugin.UnmarshalS func (x *AssertGatewayRightsRequest) UnmarshalJSON(b []byte) error { return jsonplugin.DefaultUnmarshalerConfig.Unmarshal(b, x) } + +// MarshalProtoJSON marshals the BatchDeleteGatewaysRequest message to JSON. +func (x *BatchDeleteGatewaysRequest) MarshalProtoJSON(s *jsonplugin.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if len(x.GatewayIds) > 0 || s.HasField("gateway_ids") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("gateway_ids") + s.WriteArrayStart() + var wroteElement bool + for _, element := range x.GatewayIds { + s.WriteMoreIf(&wroteElement) + element.MarshalProtoJSON(s.WithField("gateway_ids")) + } + s.WriteArrayEnd() + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the BatchDeleteGatewaysRequest to JSON. +func (x *BatchDeleteGatewaysRequest) MarshalJSON() ([]byte, error) { + return jsonplugin.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the BatchDeleteGatewaysRequest message from JSON. +func (x *BatchDeleteGatewaysRequest) UnmarshalProtoJSON(s *jsonplugin.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.ReadAny() // ignore unknown field + case "gateway_ids", "gatewayIds": + s.AddField("gateway_ids") + if s.ReadNil() { + x.GatewayIds = nil + return + } + s.ReadArray(func() { + if s.ReadNil() { + x.GatewayIds = append(x.GatewayIds, nil) + return + } + v := &GatewayIdentifiers{} + v.UnmarshalProtoJSON(s.WithField("gateway_ids", false)) + if s.Err() != nil { + return + } + x.GatewayIds = append(x.GatewayIds, v) + }) + } + }) +} + +// UnmarshalJSON unmarshals the BatchDeleteGatewaysRequest from JSON. +func (x *BatchDeleteGatewaysRequest) UnmarshalJSON(b []byte) error { + return jsonplugin.DefaultUnmarshalerConfig.Unmarshal(b, x) +} diff --git a/pkg/ttnpb/identifiers.pb.go b/pkg/ttnpb/identifiers.pb.go index e4e75651a8..50edfa43e1 100644 --- a/pkg/ttnpb/identifiers.pb.go +++ b/pkg/ttnpb/identifiers.pb.go @@ -871,6 +871,53 @@ func (x *EndDeviceIdentifiersList) GetEndDeviceIds() []*EndDeviceIdentifiers { return nil } +type GatewayIdentifiersList struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + GatewayIds []*GatewayIdentifiers `protobuf:"bytes,1,rep,name=gateway_ids,json=gatewayIds,proto3" json:"gateway_ids,omitempty"` +} + +func (x *GatewayIdentifiersList) Reset() { + *x = GatewayIdentifiersList{} + if protoimpl.UnsafeEnabled { + mi := &file_ttn_lorawan_v3_identifiers_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GatewayIdentifiersList) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GatewayIdentifiersList) ProtoMessage() {} + +func (x *GatewayIdentifiersList) ProtoReflect() protoreflect.Message { + mi := &file_ttn_lorawan_v3_identifiers_proto_msgTypes[12] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GatewayIdentifiersList.ProtoReflect.Descriptor instead. +func (*GatewayIdentifiersList) Descriptor() ([]byte, []int) { + return file_ttn_lorawan_v3_identifiers_proto_rawDescGZIP(), []int{12} +} + +func (x *GatewayIdentifiersList) GetGatewayIds() []*GatewayIdentifiers { + if x != nil { + return x.GatewayIds + } + return nil +} + var File_ttn_lorawan_v3_identifiers_proto protoreflect.FileDescriptor var file_ttn_lorawan_v3_identifiers_proto_rawDesc = []byte{ @@ -1164,11 +1211,17 @@ var file_ttn_lorawan_v3_identifiers_proto_rawDesc = []byte{ 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x45, 0x6e, 0x64, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x52, 0x0c, 0x65, 0x6e, 0x64, 0x44, 0x65, - 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x73, 0x42, 0x31, 0x5a, 0x2f, 0x67, 0x6f, 0x2e, 0x74, 0x68, - 0x65, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x2e, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2f, - 0x6c, 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2d, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x2f, 0x76, 0x33, - 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x74, 0x6e, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x33, + 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x73, 0x22, 0x5d, 0x0a, 0x16, 0x47, 0x61, 0x74, 0x65, 0x77, + 0x61, 0x79, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x4c, 0x69, 0x73, + 0x74, 0x12, 0x43, 0x0a, 0x0b, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x5f, 0x69, 0x64, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x74, 0x74, 0x6e, 0x2e, 0x6c, 0x6f, 0x72, + 0x61, 0x77, 0x61, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x49, + 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x52, 0x0a, 0x67, 0x61, 0x74, 0x65, + 0x77, 0x61, 0x79, 0x49, 0x64, 0x73, 0x42, 0x31, 0x5a, 0x2f, 0x67, 0x6f, 0x2e, 0x74, 0x68, 0x65, + 0x74, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x2e, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2f, 0x6c, + 0x6f, 0x72, 0x61, 0x77, 0x61, 0x6e, 0x2d, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x2f, 0x76, 0x33, 0x2f, + 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x74, 0x6e, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x33, } var ( @@ -1183,7 +1236,7 @@ func file_ttn_lorawan_v3_identifiers_proto_rawDescGZIP() []byte { return file_ttn_lorawan_v3_identifiers_proto_rawDescData } -var file_ttn_lorawan_v3_identifiers_proto_msgTypes = make([]protoimpl.MessageInfo, 12) +var file_ttn_lorawan_v3_identifiers_proto_msgTypes = make([]protoimpl.MessageInfo, 13) var file_ttn_lorawan_v3_identifiers_proto_goTypes = []interface{}{ (*ApplicationIdentifiers)(nil), // 0: ttn.lorawan.v3.ApplicationIdentifiers (*ClientIdentifiers)(nil), // 1: ttn.lorawan.v3.ClientIdentifiers @@ -1197,6 +1250,7 @@ var file_ttn_lorawan_v3_identifiers_proto_goTypes = []interface{}{ (*NetworkIdentifiers)(nil), // 9: ttn.lorawan.v3.NetworkIdentifiers (*LoRaAllianceProfileIdentifiers)(nil), // 10: ttn.lorawan.v3.LoRaAllianceProfileIdentifiers (*EndDeviceIdentifiersList)(nil), // 11: ttn.lorawan.v3.EndDeviceIdentifiersList + (*GatewayIdentifiersList)(nil), // 12: ttn.lorawan.v3.GatewayIdentifiersList } var file_ttn_lorawan_v3_identifiers_proto_depIdxs = []int32{ 0, // 0: ttn.lorawan.v3.EndDeviceIdentifiers.application_ids:type_name -> ttn.lorawan.v3.ApplicationIdentifiers @@ -1209,11 +1263,12 @@ var file_ttn_lorawan_v3_identifiers_proto_depIdxs = []int32{ 4, // 7: ttn.lorawan.v3.EntityIdentifiers.organization_ids:type_name -> ttn.lorawan.v3.OrganizationIdentifiers 5, // 8: ttn.lorawan.v3.EntityIdentifiers.user_ids:type_name -> ttn.lorawan.v3.UserIdentifiers 2, // 9: ttn.lorawan.v3.EndDeviceIdentifiersList.end_device_ids:type_name -> ttn.lorawan.v3.EndDeviceIdentifiers - 10, // [10:10] is the sub-list for method output_type - 10, // [10:10] is the sub-list for method input_type - 10, // [10:10] is the sub-list for extension type_name - 10, // [10:10] is the sub-list for extension extendee - 0, // [0:10] is the sub-list for field type_name + 3, // 10: ttn.lorawan.v3.GatewayIdentifiersList.gateway_ids:type_name -> ttn.lorawan.v3.GatewayIdentifiers + 11, // [11:11] is the sub-list for method output_type + 11, // [11:11] is the sub-list for method input_type + 11, // [11:11] is the sub-list for extension type_name + 11, // [11:11] is the sub-list for extension extendee + 0, // [0:11] is the sub-list for field type_name } func init() { file_ttn_lorawan_v3_identifiers_proto_init() } @@ -1366,6 +1421,18 @@ func file_ttn_lorawan_v3_identifiers_proto_init() { return nil } } + file_ttn_lorawan_v3_identifiers_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GatewayIdentifiersList); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } file_ttn_lorawan_v3_identifiers_proto_msgTypes[6].OneofWrappers = []interface{}{ (*OrganizationOrUserIdentifiers_OrganizationIds)(nil), @@ -1385,7 +1452,7 @@ func file_ttn_lorawan_v3_identifiers_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_ttn_lorawan_v3_identifiers_proto_rawDesc, NumEnums: 0, - NumMessages: 12, + NumMessages: 13, NumExtensions: 0, NumServices: 0, }, diff --git a/pkg/ttnpb/identifiers.pb.paths.fm.go b/pkg/ttnpb/identifiers.pb.paths.fm.go index 56460f7a0b..347d9423b2 100644 --- a/pkg/ttnpb/identifiers.pb.paths.fm.go +++ b/pkg/ttnpb/identifiers.pb.paths.fm.go @@ -143,3 +143,10 @@ var EndDeviceIdentifiersListFieldPathsNested = []string{ var EndDeviceIdentifiersListFieldPathsTopLevel = []string{ "end_device_ids", } +var GatewayIdentifiersListFieldPathsNested = []string{ + "gateway_ids", +} + +var GatewayIdentifiersListFieldPathsTopLevel = []string{ + "gateway_ids", +} diff --git a/pkg/ttnpb/identifiers.pb.setters.fm.go b/pkg/ttnpb/identifiers.pb.setters.fm.go index 09c7c57deb..e3cf15bf7d 100644 --- a/pkg/ttnpb/identifiers.pb.setters.fm.go +++ b/pkg/ttnpb/identifiers.pb.setters.fm.go @@ -733,3 +733,23 @@ func (dst *EndDeviceIdentifiersList) SetFields(src *EndDeviceIdentifiersList, pa } return nil } + +func (dst *GatewayIdentifiersList) SetFields(src *GatewayIdentifiersList, paths ...string) error { + for name, subs := range _processPaths(paths) { + switch name { + case "gateway_ids": + if len(subs) > 0 { + return fmt.Errorf("'gateway_ids' has no subfields, but %s were specified", subs) + } + if src != nil { + dst.GatewayIds = src.GatewayIds + } else { + dst.GatewayIds = nil + } + + default: + return fmt.Errorf("invalid field: '%s'", name) + } + } + return nil +} diff --git a/pkg/ttnpb/identifiers.pb.validate.go b/pkg/ttnpb/identifiers.pb.validate.go index 11be43ec3b..14622c9cd2 100644 --- a/pkg/ttnpb/identifiers.pb.validate.go +++ b/pkg/ttnpb/identifiers.pb.validate.go @@ -1511,3 +1511,101 @@ var _ interface { Cause() error ErrorName() string } = EndDeviceIdentifiersListValidationError{} + +// ValidateFields checks the field values on GatewayIdentifiersList with the +// rules defined in the proto definition for this message. If any rules are +// violated, an error is returned. +func (m *GatewayIdentifiersList) ValidateFields(paths ...string) error { + if m == nil { + return nil + } + + if len(paths) == 0 { + paths = GatewayIdentifiersListFieldPathsNested + } + + for name, subs := range _processPaths(append(paths[:0:0], paths...)) { + _ = subs + switch name { + case "gateway_ids": + + for idx, item := range m.GetGatewayIds() { + _, _ = idx, item + + if v, ok := interface{}(item).(interface{ ValidateFields(...string) error }); ok { + if err := v.ValidateFields(subs...); err != nil { + return GatewayIdentifiersListValidationError{ + field: fmt.Sprintf("gateway_ids[%v]", idx), + reason: "embedded message failed validation", + cause: err, + } + } + } + + } + + default: + return GatewayIdentifiersListValidationError{ + field: name, + reason: "invalid field path", + } + } + } + return nil +} + +// GatewayIdentifiersListValidationError is the validation error returned by +// GatewayIdentifiersList.ValidateFields if the designated constraints aren't met. +type GatewayIdentifiersListValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e GatewayIdentifiersListValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e GatewayIdentifiersListValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e GatewayIdentifiersListValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e GatewayIdentifiersListValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e GatewayIdentifiersListValidationError) ErrorName() string { + return "GatewayIdentifiersListValidationError" +} + +// Error satisfies the builtin error interface +func (e GatewayIdentifiersListValidationError) Error() string { + cause := "" + if e.cause != nil { + cause = fmt.Sprintf(" | caused by: %v", e.cause) + } + + key := "" + if e.key { + key = "key for " + } + + return fmt.Sprintf( + "invalid %sGatewayIdentifiersList.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = GatewayIdentifiersListValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = GatewayIdentifiersListValidationError{} diff --git a/pkg/ttnpb/identifiers_json.pb.go b/pkg/ttnpb/identifiers_json.pb.go index 46cc0ebb24..424f6f79ca 100644 --- a/pkg/ttnpb/identifiers_json.pb.go +++ b/pkg/ttnpb/identifiers_json.pb.go @@ -420,3 +420,66 @@ func (x *EndDeviceIdentifiersList) UnmarshalProtoJSON(s *jsonplugin.UnmarshalSta func (x *EndDeviceIdentifiersList) UnmarshalJSON(b []byte) error { return jsonplugin.DefaultUnmarshalerConfig.Unmarshal(b, x) } + +// MarshalProtoJSON marshals the GatewayIdentifiersList message to JSON. +func (x *GatewayIdentifiersList) MarshalProtoJSON(s *jsonplugin.MarshalState) { + if x == nil { + s.WriteNil() + return + } + s.WriteObjectStart() + var wroteField bool + if len(x.GatewayIds) > 0 || s.HasField("gateway_ids") { + s.WriteMoreIf(&wroteField) + s.WriteObjectField("gateway_ids") + s.WriteArrayStart() + var wroteElement bool + for _, element := range x.GatewayIds { + s.WriteMoreIf(&wroteElement) + element.MarshalProtoJSON(s.WithField("gateway_ids")) + } + s.WriteArrayEnd() + } + s.WriteObjectEnd() +} + +// MarshalJSON marshals the GatewayIdentifiersList to JSON. +func (x *GatewayIdentifiersList) MarshalJSON() ([]byte, error) { + return jsonplugin.DefaultMarshalerConfig.Marshal(x) +} + +// UnmarshalProtoJSON unmarshals the GatewayIdentifiersList message from JSON. +func (x *GatewayIdentifiersList) UnmarshalProtoJSON(s *jsonplugin.UnmarshalState) { + if s.ReadNil() { + return + } + s.ReadObject(func(key string) { + switch key { + default: + s.ReadAny() // ignore unknown field + case "gateway_ids", "gatewayIds": + s.AddField("gateway_ids") + if s.ReadNil() { + x.GatewayIds = nil + return + } + s.ReadArray(func() { + if s.ReadNil() { + x.GatewayIds = append(x.GatewayIds, nil) + return + } + v := &GatewayIdentifiers{} + v.UnmarshalProtoJSON(s.WithField("gateway_ids", false)) + if s.Err() != nil { + return + } + x.GatewayIds = append(x.GatewayIds, v) + }) + } + }) +} + +// UnmarshalJSON unmarshals the GatewayIdentifiersList from JSON. +func (x *GatewayIdentifiersList) UnmarshalJSON(b []byte) error { + return jsonplugin.DefaultUnmarshalerConfig.Unmarshal(b, x) +} diff --git a/sdk/js/generated/api-definition.json b/sdk/js/generated/api-definition.json index 80e20387b8..aeccb77945 100644 --- a/sdk/js/generated/api-definition.json +++ b/sdk/js/generated/api-definition.json @@ -3199,6 +3199,18 @@ ] } }, + "GatewayBatchRegistry": { + "Delete": { + "file": "ttn/lorawan/v3/gateway_services.proto", + "http": [ + { + "method": "delete", + "pattern": "/gateways/batch", + "parameters": [] + } + ] + } + }, "GatewayConfigurator": { "PullConfiguration": { "file": "ttn/lorawan/v3/gateway_services.proto", diff --git a/sdk/js/generated/api.json b/sdk/js/generated/api.json index ec0b498db1..6319cdfad5 100644 --- a/sdk/js/generated/api.json +++ b/sdk/js/generated/api.json @@ -22601,6 +22601,42 @@ } ] }, + { + "name": "BatchDeleteGatewaysRequest", + "longName": "BatchDeleteGatewaysRequest", + "fullName": "ttn.lorawan.v3.BatchDeleteGatewaysRequest", + "description": "", + "hasExtensions": false, + "hasFields": true, + "hasOneofs": false, + "extensions": [], + "fields": [ + { + "name": "gateway_ids", + "description": "", + "label": "repeated", + "type": "GatewayIdentifiers", + "longType": "GatewayIdentifiers", + "fullType": "ttn.lorawan.v3.GatewayIdentifiers", + "ismap": false, + "isoneof": false, + "oneofdecl": "", + "defaultValue": "", + "options": { + "validate.rules": [ + { + "name": "repeated.min_items", + "value": 1 + }, + { + "name": "repeated.max_items", + "value": 20 + } + ] + } + } + ] + }, { "name": "PullGatewayConfigurationRequest", "longName": "PullGatewayConfigurationRequest", @@ -22864,6 +22900,36 @@ } ] }, + { + "name": "GatewayBatchRegistry", + "longName": "GatewayBatchRegistry", + "fullName": "ttn.lorawan.v3.GatewayBatchRegistry", + "description": "The GatewayBatchRegistry service, exposed by the Identity Server, is used to manage\ngateway registrations in batches.", + "methods": [ + { + "name": "Delete", + "description": "Delete a batch of gateways.\nThis operation is atomic; either all gateways are deleted or none.\nThe caller must have delete rights on all requested gateways.", + "requestType": "BatchDeleteGatewaysRequest", + "requestLongType": "BatchDeleteGatewaysRequest", + "requestFullType": "ttn.lorawan.v3.BatchDeleteGatewaysRequest", + "requestStreaming": false, + "responseType": "Empty", + "responseLongType": ".google.protobuf.Empty", + "responseFullType": "google.protobuf.Empty", + "responseStreaming": false, + "options": { + "google.api.http": { + "rules": [ + { + "method": "DELETE", + "pattern": "/gateways/batch" + } + ] + } + } + } + ] + }, { "name": "GatewayConfigurator", "longName": "GatewayConfigurator", @@ -23994,6 +24060,30 @@ } ] }, + { + "name": "GatewayIdentifiersList", + "longName": "GatewayIdentifiersList", + "fullName": "ttn.lorawan.v3.GatewayIdentifiersList", + "description": "", + "hasExtensions": false, + "hasFields": true, + "hasOneofs": false, + "extensions": [], + "fields": [ + { + "name": "gateway_ids", + "description": "", + "label": "repeated", + "type": "GatewayIdentifiers", + "longType": "GatewayIdentifiers", + "fullType": "ttn.lorawan.v3.GatewayIdentifiers", + "ismap": false, + "isoneof": false, + "oneofdecl": "", + "defaultValue": "" + } + ] + }, { "name": "LoRaAllianceProfileIdentifiers", "longName": "LoRaAllianceProfileIdentifiers",