// AddMetadata adds metadata to the announcement of the specified service func (s *RedisAnnouncementStore) AddMetadata(serviceName, serviceID string, metadata ...Metadata) error { key := fmt.Sprintf("%s:%s", serviceName, serviceID) metadataStrings := make([]string, 0, len(metadata)) for _, meta := range metadata { txt, err := meta.MarshalText() if err != nil { return err } metadataStrings = append(metadataStrings, string(txt)) switch meta := meta.(type) { case AppIDMetadata: existing, err := s.byAppID.Get(meta.AppID) switch { case errors.GetErrType(err) == errors.NotFound: if err := s.byAppID.Create(meta.AppID, key); err != nil { return err } case err != nil: return err case existing == key: continue default: go s.metadata.Remove(existing, string(txt)) if err := s.byAppID.Update(meta.AppID, key); err != nil { return err } } case AppEUIMetadata: existing, err := s.byAppEUI.Get(meta.AppEUI.String()) switch { case errors.GetErrType(err) == errors.NotFound: if err := s.byAppEUI.Create(meta.AppEUI.String(), key); err != nil { return err } case err != nil: return err case existing == key: continue default: go s.metadata.Remove(existing, string(txt)) if err := s.byAppEUI.Update(meta.AppEUI.String(), key); err != nil { return err } } } } err := s.metadata.Add(key, metadataStrings...) if err != nil { return err } return nil }
func (h *handlerManager) DeleteDevice(ctx context.Context, in *pb.DeviceIdentifier) (*empty.Empty, error) { if err := in.Validate(); err != nil { return nil, errors.Wrap(err, "Invalid Device Identifier") } ctx, claims, err := h.validateTTNAuthAppContext(ctx, in.AppId) if err != nil { return nil, err } if !claims.AppRight(in.AppId, rights.Devices) { return nil, errors.NewErrPermissionDenied(fmt.Sprintf(`No "devices" rights to application "%s"`, in.AppId)) } if _, err := h.handler.applications.Get(in.AppId); err != nil { return nil, errors.Wrap(err, "Application not registered to this Handler") } dev, err := h.handler.devices.Get(in.AppId, in.DevId) if err != nil { return nil, err } _, err = h.deviceManager.DeleteDevice(ctx, &pb_lorawan.DeviceIdentifier{AppEui: &dev.AppEUI, DevEui: &dev.DevEUI}) if err != nil && errors.GetErrType(errors.FromGRPCError(err)) != errors.NotFound { return nil, errors.Wrap(errors.FromGRPCError(err), "Broker did not delete device") } err = h.handler.devices.Delete(in.AppId, in.DevId) if err != nil { return nil, err } return &empty.Empty{}, nil }
// Set a new Announcement or update an existing one // The metadata of the announcement is ignored, as metadata should be managed with AddMetadata and RemoveMetadata func (s *RedisAnnouncementStore) Set(new *Announcement) error { key := fmt.Sprintf("%s:%s", new.ServiceName, new.ID) now := time.Now() new.UpdatedAt = now err := s.store.Update(key, *new) if errors.GetErrType(err) == errors.NotFound { new.CreatedAt = now err = s.store.Create(key, *new) } if err != nil { return err } return nil }
func (n *networkServerManager) SetDevice(ctx context.Context, in *pb_lorawan.Device) (*empty.Empty, error) { dev, err := n.getDevice(ctx, &pb_lorawan.DeviceIdentifier{AppEui: in.AppEui, DevEui: in.DevEui}) if err != nil && errors.GetErrType(err) != errors.NotFound { return nil, err } if err := in.Validate(); err != nil { return nil, errors.Wrap(err, "Invalid Device") } claims, err := n.networkServer.Component.ValidateTTNAuthContext(ctx) if err != nil { return nil, err } if !claims.AppRight(in.AppId, rights.AppSettings) { return nil, errors.NewErrPermissionDenied(fmt.Sprintf("No access to Application %s", dev.AppID)) } if dev == nil { dev = new(device.Device) } else { dev.StartUpdate() } dev.AppID = in.AppId dev.AppEUI = *in.AppEui dev.DevID = in.DevId dev.DevEUI = *in.DevEui dev.FCntUp = in.FCntUp dev.FCntDown = in.FCntDown dev.Options = device.Options{ DisableFCntCheck: in.DisableFCntCheck, Uses32BitFCnt: in.Uses32BitFCnt, ActivationConstraints: in.ActivationConstraints, } if in.NwkSKey != nil && in.DevAddr != nil { dev.DevAddr = *in.DevAddr dev.NwkSKey = *in.NwkSKey } err = n.networkServer.devices.Set(dev) if err != nil { return nil, err } return &empty.Empty{}, nil }
// Delete an Announcement and its metadata func (s *RedisAnnouncementStore) Delete(serviceName, serviceID string) error { metadata, err := s.GetMetadata(serviceName, serviceID) if err != nil && errors.GetErrType(err) != errors.NotFound { return err } if len(metadata) > 0 { s.RemoveMetadata(serviceName, serviceID, metadata...) } key := fmt.Sprintf("%s:%s", serviceName, serviceID) err = s.store.Delete(key) if err != nil { return err } return nil }
// GetMetadata returns the metadata of the specified service func (s *RedisAnnouncementStore) GetMetadata(serviceName, serviceID string) ([]Metadata, error) { var out []Metadata metadata, err := s.metadata.Get(fmt.Sprintf("%s:%s", serviceName, serviceID)) if errors.GetErrType(err) == errors.NotFound { return nil, nil } if err != nil { return nil, err } for _, meta := range metadata { if meta := MetadataFromString(meta); meta != nil { out = append(out, meta) } } return out, nil }
func (h *handlerManager) RegisterApplication(ctx context.Context, in *pb.ApplicationIdentifier) (*empty.Empty, error) { if err := in.Validate(); err != nil { return nil, errors.Wrap(err, "Invalid Application Identifier") } ctx, claims, err := h.validateTTNAuthAppContext(ctx, in.AppId) if err != nil { return nil, err } if !claims.AppRight(in.AppId, rights.AppSettings) { return nil, errors.NewErrPermissionDenied(`No "settings" rights to application`) } app, err := h.handler.applications.Get(in.AppId) if err != nil && errors.GetErrType(err) != errors.NotFound { return nil, err } if app != nil { return nil, errors.NewErrAlreadyExists("Application") } err = h.handler.applications.Set(&application.Application{ AppID: in.AppId, }) if err != nil { return nil, err } token, _ := api.TokenFromContext(ctx) err = h.handler.Discovery.AddAppID(in.AppId, token) if err != nil { h.handler.Ctx.WithField("AppID", in.AppId).WithError(err).Warn("Could not register Application with Discovery") } _, err = h.handler.ttnBrokerManager.RegisterApplicationHandler(ctx, &pb_broker.ApplicationHandlerRegistration{ AppId: in.AppId, HandlerId: h.handler.Identity.Id, }) if err != nil { h.handler.Ctx.WithField("AppID", in.AppId).WithError(err).Warn("Could not register Application with Broker") } return &empty.Empty{}, nil }
// ListForAddress lists all devices for a specific DevAddr func (s *RedisDeviceStore) ListForAddress(devAddr types.DevAddr) ([]*Device, error) { deviceKeys, err := s.devAddrIndex.Get(devAddr.String()) if errors.GetErrType(err) == errors.NotFound { return nil, nil } if err != nil { return nil, err } devicesI, err := s.store.GetAll(deviceKeys, nil) if err != nil { return nil, err } devices := make([]*Device, 0, len(devicesI)) for _, deviceI := range devicesI { if device, ok := deviceI.(Device); ok { devices = append(devices, &device) } } return devices, nil }
func (d *discovery) Announce(in *pb.Announcement) error { service, err := d.services.Get(in.ServiceName, in.Id) if err != nil && errors.GetErrType(err) != errors.NotFound { return err } if service == nil { service = new(announcement.Announcement) } service.StartUpdate() service.ID = in.Id service.ServiceName = in.ServiceName service.ServiceVersion = in.ServiceVersion service.Description = in.Description service.URL = in.Url service.Public = in.Public service.NetAddress = in.NetAddress service.PublicKey = in.PublicKey service.Certificate = in.Certificate service.APIAddress = in.ApiAddress return d.services.Set(service) }
func (h *handlerManager) GetDevice(ctx context.Context, in *pb.DeviceIdentifier) (*pb.Device, error) { if err := in.Validate(); err != nil { return nil, errors.Wrap(err, "Invalid Device Identifier") } ctx, claims, err := h.validateTTNAuthAppContext(ctx, in.AppId) if err != nil { return nil, err } if !claims.AppRight(in.AppId, rights.Devices) { return nil, errors.NewErrPermissionDenied(fmt.Sprintf(`No "devices" rights to application "%s"`, in.AppId)) } if _, err := h.handler.applications.Get(in.AppId); err != nil { return nil, errors.Wrap(err, "Application not registered to this Handler") } dev, err := h.handler.devices.Get(in.AppId, in.DevId) if err != nil { return nil, err } pbDev := &pb.Device{ AppId: dev.AppID, DevId: dev.DevID, Device: &pb.Device_LorawanDevice{LorawanDevice: &pb_lorawan.Device{ AppId: dev.AppID, AppEui: &dev.AppEUI, DevId: dev.DevID, DevEui: &dev.DevEUI, DevAddr: &dev.DevAddr, NwkSKey: &dev.NwkSKey, AppSKey: &dev.AppSKey, AppKey: &dev.AppKey, DisableFCntCheck: dev.Options.DisableFCntCheck, Uses32BitFCnt: dev.Options.Uses32BitFCnt, ActivationConstraints: dev.Options.ActivationConstraints, }}, } nsDev, err := h.deviceManager.GetDevice(ctx, &pb_lorawan.DeviceIdentifier{ AppEui: &dev.AppEUI, DevEui: &dev.DevEUI, }) if errors.GetErrType(errors.FromGRPCError(err)) == errors.NotFound { // Re-register the device in the Broker (NetworkServer) h.handler.Ctx.WithFields(log.Fields{ "AppID": dev.AppID, "DevID": dev.DevID, "AppEUI": dev.AppEUI, "DevEUI": dev.DevEUI, }).Warn("Re-registering missing device to Broker") nsDev = dev.GetLoRaWAN() _, err = h.deviceManager.SetDevice(ctx, nsDev) if err != nil { return nil, errors.Wrap(errors.FromGRPCError(err), "Could not re-register missing device to Broker") } } else if err != nil { return pbDev, errors.Wrap(errors.FromGRPCError(err), "Broker did not return device") } pbDev.GetLorawanDevice().FCntUp = nsDev.FCntUp pbDev.GetLorawanDevice().FCntDown = nsDev.FCntDown pbDev.GetLorawanDevice().LastSeen = nsDev.LastSeen return pbDev, nil }
func (h *handlerManager) SetDevice(ctx context.Context, in *pb.Device) (*empty.Empty, error) { if err := in.Validate(); err != nil { return nil, errors.Wrap(err, "Invalid Device") } ctx, claims, err := h.validateTTNAuthAppContext(ctx, in.AppId) if err != nil { return nil, err } if !claims.AppRight(in.AppId, rights.Devices) { return nil, errors.NewErrPermissionDenied(fmt.Sprintf(`No "devices" rights to application "%s"`, in.AppId)) } if _, err := h.handler.applications.Get(in.AppId); err != nil { return nil, errors.Wrap(err, "Application not registered to this Handler") } dev, err := h.handler.devices.Get(in.AppId, in.DevId) if err != nil && errors.GetErrType(err) != errors.NotFound { return nil, err } lorawan := in.GetLorawanDevice() if lorawan == nil { return nil, errors.NewErrInvalidArgument("Device", "No LoRaWAN Device") } if dev != nil { // When this is an update if dev.AppEUI != *lorawan.AppEui || dev.DevEUI != *lorawan.DevEui { // If the AppEUI or DevEUI is changed, we should remove the device from the NetworkServer and re-add it later _, err = h.deviceManager.DeleteDevice(ctx, &pb_lorawan.DeviceIdentifier{ AppEui: &dev.AppEUI, DevEui: &dev.DevEUI, }) if err != nil { return nil, errors.Wrap(errors.FromGRPCError(err), "Broker did not delete device") } } dev.StartUpdate() } else { // When this is a create existingDevices, err := h.handler.devices.ListForApp(in.AppId) if err != nil { return nil, err } for _, existingDevice := range existingDevices { if existingDevice.AppEUI == *lorawan.AppEui && existingDevice.DevEUI == *lorawan.DevEui { return nil, errors.NewErrAlreadyExists("Device with AppEUI and DevEUI") } } dev = new(device.Device) } dev.AppID = in.AppId dev.AppEUI = *lorawan.AppEui dev.DevID = in.DevId dev.DevEUI = *lorawan.DevEui dev.Options = device.Options{ DisableFCntCheck: lorawan.DisableFCntCheck, Uses32BitFCnt: lorawan.Uses32BitFCnt, ActivationConstraints: lorawan.ActivationConstraints, } if dev.Options.ActivationConstraints == "" { dev.Options.ActivationConstraints = "local" } if lorawan.DevAddr != nil { dev.DevAddr = *lorawan.DevAddr } if lorawan.NwkSKey != nil { dev.NwkSKey = *lorawan.NwkSKey } if lorawan.AppSKey != nil { dev.AppSKey = *lorawan.AppSKey } if lorawan.AppKey != nil { if dev.AppKey != *lorawan.AppKey { // When the AppKey of an existing device is changed dev.UsedAppNonces = []device.AppNonce{} dev.UsedDevNonces = []device.DevNonce{} } dev.AppKey = *lorawan.AppKey } // Update the device in the Broker (NetworkServer) nsUpdated := dev.GetLoRaWAN() nsUpdated.FCntUp = lorawan.FCntUp nsUpdated.FCntDown = lorawan.FCntDown _, err = h.deviceManager.SetDevice(ctx, nsUpdated) if err != nil { return nil, errors.Wrap(errors.FromGRPCError(err), "Broker did not set device") } err = h.handler.devices.Set(dev) if err != nil { return nil, err } return &empty.Empty{}, nil }
func TestRedisKVStore(t *testing.T) { a := New(t) c := getRedisClient() s := NewRedisKVStore(c, "test-redis-kv-store") a.So(s, ShouldNotBeNil) // Get non-existing { _, err := s.Get("test") a.So(err, ShouldNotBeNil) a.So(errors.GetErrType(err), ShouldEqual, errors.NotFound) } // Create New { defer func() { c.Del("test-redis-kv-store:test").Result() }() err := s.Create("test", "value") a.So(err, ShouldBeNil) exists, err := c.Exists("test-redis-kv-store:test").Result() a.So(err, ShouldBeNil) a.So(exists, ShouldBeTrue) } // Create Existing { err := s.Create("test", "value") a.So(err, ShouldNotBeNil) } // Get { res, err := s.Get("test") a.So(err, ShouldBeNil) a.So(res, ShouldEqual, "value") } for i := 1; i < 10; i++ { // Create Extra { name := fmt.Sprintf("test-%d", i) defer func() { c.Del("test-redis-kv-store:" + name).Result() }() s.Create(name, name) } } // GetAll { res, err := s.GetAll([]string{"test"}, nil) a.So(err, ShouldBeNil) a.So(res, ShouldHaveLength, 1) a.So(res["test"], ShouldEqual, "value") } // List { res, err := s.List("", nil) a.So(err, ShouldBeNil) a.So(res, ShouldHaveLength, 10) a.So(res["test"], ShouldEqual, "value") } // List With Options { res, _ := s.List("test-*", &ListOptions{Limit: 2}) a.So(res, ShouldHaveLength, 2) a.So(res["test-1"], ShouldEqual, "test-1") a.So(res["test-2"], ShouldEqual, "test-2") res, _ = s.List("test-*", &ListOptions{Limit: 20}) a.So(res, ShouldHaveLength, 9) res, _ = s.List("test-*", &ListOptions{Offset: 20}) a.So(res, ShouldHaveLength, 0) res, _ = s.List("test-*", &ListOptions{Limit: 2, Offset: 1}) a.So(res, ShouldHaveLength, 2) a.So(res["test-2"], ShouldEqual, "test-2") a.So(res["test-3"], ShouldEqual, "test-3") res, _ = s.List("test-*", &ListOptions{Limit: 20, Offset: 1}) a.So(res, ShouldHaveLength, 8) } // Update Non-Existing { err := s.Update("not-there", "value") a.So(err, ShouldNotBeNil) } // Update Existing { err := s.Update("test", "updated") a.So(err, ShouldBeNil) name, err := c.Get("test-redis-kv-store:test").Result() a.So(err, ShouldBeNil) a.So(name, ShouldEqual, "updated") } // Delete Non-Existing { err := s.Delete("not-there") a.So(err, ShouldNotBeNil) } // Delete Existing { err := s.Delete("test") a.So(err, ShouldBeNil) exists, err := c.Exists("test-redis-kv-store:test").Result() a.So(err, ShouldBeNil) a.So(exists, ShouldBeFalse) } }
func TestRedisMapStore(t *testing.T) { a := New(t) c := getRedisClient() s := NewRedisMapStore(c, "test-redis-map-store") a.So(s, ShouldNotBeNil) now := time.Now() notEmpty := map[string]string{"ab": "cd"} testRedisStructVal := testRedisStruct{ Name: "My Name", UpdatedAt: Time{now}, NotEmpty: ¬Empty, } s.SetBase(testRedisStructVal, "") // Get non-existing { res, err := s.Get("test") a.So(err, ShouldNotBeNil) a.So(errors.GetErrType(err), ShouldEqual, errors.NotFound) a.So(res, ShouldBeNil) } // Create New { defer func() { c.Del("test-redis-map-store:test").Result() }() err := s.Create("test", &testRedisStructVal) a.So(err, ShouldBeNil) exists, err := c.Exists("test-redis-map-store:test").Result() a.So(err, ShouldBeNil) a.So(exists, ShouldBeTrue) } // Create Existing { err := s.Create("test", testRedisStructVal) a.So(err, ShouldNotBeNil) } // Get { res, err := s.Get("test") a.So(err, ShouldBeNil) a.So(res, ShouldNotBeNil) a.So(res.(testRedisStruct).Name, ShouldEqual, "My Name") a.So(res.(testRedisStruct).UpdatedAt.Nanosecond(), ShouldEqual, now.Nanosecond()) } // GetFields { res, err := s.GetFields("test", "name") a.So(err, ShouldBeNil) a.So(res, ShouldNotBeNil) a.So(res.(testRedisStruct).Name, ShouldEqual, "My Name") } for i := 1; i < 10; i++ { // Create Extra { name := fmt.Sprintf("test-%d", i) defer func() { c.Del("test-redis-map-store:" + name).Result() }() s.Create(name, testRedisStruct{ Name: name, }) } } // GetAll { res, err := s.GetAll([]string{"test"}, nil) a.So(err, ShouldBeNil) a.So(res, ShouldHaveLength, 1) a.So(res[0].(testRedisStruct).Name, ShouldEqual, "My Name") } // List { res, err := s.List("", nil) a.So(err, ShouldBeNil) a.So(res, ShouldHaveLength, 10) a.So(res[0].(testRedisStruct).Name, ShouldEqual, "My Name") } // List With Options { res, _ := s.List("test-*", &ListOptions{Limit: 2}) a.So(res, ShouldHaveLength, 2) a.So(res[0].(testRedisStruct).Name, ShouldEqual, "test-1") a.So(res[1].(testRedisStruct).Name, ShouldEqual, "test-2") res, _ = s.List("test-*", &ListOptions{Limit: 20}) a.So(res, ShouldHaveLength, 9) res, _ = s.List("test-*", &ListOptions{Offset: 20}) a.So(res, ShouldHaveLength, 0) res, _ = s.List("test-*", &ListOptions{Limit: 2, Offset: 1}) a.So(res, ShouldHaveLength, 2) a.So(res[0].(testRedisStruct).Name, ShouldEqual, "test-2") a.So(res[1].(testRedisStruct).Name, ShouldEqual, "test-3") res, _ = s.List("test-*", &ListOptions{Limit: 20, Offset: 1}) a.So(res, ShouldHaveLength, 8) } // Update Non-Existing { err := s.Update("not-there", &testRedisStructVal) a.So(err, ShouldNotBeNil) } // Update Existing { err := s.Update("test", &testRedisStruct{ Name: "New Name", }, "Name") a.So(err, ShouldBeNil) name, err := c.HGet("test-redis-map-store:test", "name").Result() a.So(err, ShouldBeNil) a.So(name, ShouldEqual, "New Name") } // Delete Non-Existing { err := s.Delete("not-there") a.So(err, ShouldNotBeNil) } // Delete Existing { err := s.Delete("test") a.So(err, ShouldBeNil) exists, err := c.Exists("test-redis-map-store:test").Result() a.So(err, ShouldBeNil) a.So(exists, ShouldBeFalse) } }