Skip to content

Commit b002fc1

Browse files
committed
feat: enhance device management with new types and metadata handling
1 parent 1119660 commit b002fc1

File tree

15 files changed

+120
-88
lines changed

15 files changed

+120
-88
lines changed

internal/db/migrations/20250821113750_create_telemetry_table.sql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
CREATE TABLE IF NOT EXISTS telemetry (
44
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
55
device_id UUID NOT NULL REFERENCES devices(id) ON DELETE CASCADE,
6-
type VARCHAR(50) NOT NULL,
6+
telemetry_type VARCHAR(50) NOT NULL,
77
payload JSONB,
88
recorded_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
99
);

internal/db/telemetry_repository.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ func (r *TelemetryRepository) Create(ctx context.Context, t *telemetry.Telemetry
2929
}
3030

3131
query := `
32-
INSERT INTO telemetry (device_id, type, payload)
32+
INSERT INTO telemetry (device_id, telemetry_type, payload)
3333
VALUES ($1, $2, $3)
3434
RETURNING id, recorded_at
3535
`
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package device
2+
3+
import (
4+
"errors"
5+
"regexp"
6+
"strings"
7+
)
8+
9+
type DeviceType struct {
10+
value string
11+
}
12+
13+
var deviceTypeRegex = regexp.MustCompile(`^[a-zA-Z0-9_-]+$`)
14+
15+
func NewDeviceType(t string) (DeviceType, error) {
16+
t = strings.TrimSpace(t)
17+
18+
if len(t) == 0 {
19+
return DeviceType{}, errors.New("device type is required")
20+
}
21+
22+
if len(t) < 3 {
23+
return DeviceType{}, errors.New("device type must be at least 3 characters")
24+
}
25+
26+
if len(t) > 50 {
27+
return DeviceType{}, errors.New("device type must be at most 50 characters")
28+
}
29+
30+
if matches := deviceTypeRegex.MatchString(t); !matches {
31+
return DeviceType{}, errors.New("device type can only contain letters, numbers, _ and -")
32+
}
33+
34+
return DeviceType{value: t}, nil
35+
}
36+
37+
func (t DeviceType) String() string {
38+
return t.value
39+
}

internal/domain/device/errors.go

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,5 @@ package device
33
import "errors"
44

55
var (
6-
ErrDeviceNotFound = errors.New("device not found")
7-
ErrNameRequired = errors.New("device name is required")
8-
ErrNameTooShort = errors.New("device name must be at least 3 characters")
9-
ErrNameTooLong = errors.New("device name must be at most 50 characters")
10-
ErrNameInvalidChars = errors.New("device name may only contain letters, numbers, underscores, periods, or hyphens")
11-
ErrInvalidStatus = errors.New("device status must be either 'offline' or 'online'")
12-
ErrInvalidDeviceType = errors.New("device type is invalid")
13-
ErrInvalidMetadata = errors.New("device metadata is invalid")
6+
ErrDeviceNotFound = errors.New("device not found")
147
)

internal/domain/device/metadata.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package device
2+
3+
import "errors"
4+
5+
type Metadata map[string]any
6+
7+
func NewMetadata(raw any) (Metadata, error) {
8+
if raw == nil {
9+
return Metadata{}, errors.New("device metadata is required")
10+
}
11+
12+
p, ok := raw.(map[string]any)
13+
if !ok {
14+
return Metadata{}, errors.New("device metadata must be a valid JSON object")
15+
}
16+
17+
if len(p) == 0 {
18+
return Metadata{}, errors.New("device metadata cannot be empty")
19+
}
20+
21+
return Metadata(p), nil
22+
}

internal/domain/device/name.go

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package device
22

33
import (
4+
"errors"
45
"regexp"
56
"strings"
67
)
@@ -9,23 +10,24 @@ type Name struct {
910
value string
1011
}
1112

13+
var nameRegex = regexp.MustCompile(`^[a-zA-Z0-9 _.-]+$`)
14+
1215
func NewName(value string) (Name, error) {
1316
value = strings.TrimSpace(value)
1417

1518
if value == "" {
16-
return Name{}, ErrNameRequired
19+
return Name{}, errors.New("device name is required")
1720
}
1821

1922
if len(value) < 3 {
20-
return Name{}, ErrNameTooShort
23+
return Name{}, errors.New("device name must be at least 3 characters")
2124
}
2225
if len(value) > 50 {
23-
return Name{}, ErrNameTooLong
26+
return Name{}, errors.New("device name must be at most 50 characters")
2427
}
2528

26-
valid := regexp.MustCompile(`^[a-zA-Z0-9 _.-]+$`)
27-
if !valid.MatchString(value) {
28-
return Name{}, ErrNameInvalidChars
29+
if !nameRegex.MatchString(value) {
30+
return Name{}, errors.New("device name may only contain letters, numbers, underscores, periods, or hyphens")
2931
}
3032

3133
return Name{value: value}, nil

internal/domain/device/service.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ type Service struct {
1414
type UpdateDeviceInput struct {
1515
Name *Name
1616
DeviceType *DeviceType
17-
Metadata *map[string]any
17+
Metadata *Metadata
1818
}
1919

2020
func NewService(repo Repository) *Service {
@@ -29,7 +29,7 @@ func (s *Service) CreateDevice(
2929
name Name,
3030
status Status,
3131
deviceType DeviceType,
32-
metadata map[string]any,
32+
metadata Metadata,
3333
) (*Device, error) {
3434
dev := NewDevice(userId, name, status, deviceType, metadata)
3535

internal/domain/device/status.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package device
22

3+
import "errors"
4+
35
type Status string
46

57
const (
@@ -14,6 +16,6 @@ func NewStatus(value string) (Status, error) {
1416
case string(StatusOnline):
1517
return StatusOnline, nil
1618
default:
17-
return "", ErrInvalidStatus
19+
return "", errors.New("device status must be either 'offline' or 'online'")
1820
}
1921
}

internal/domain/device/type.go

Lines changed: 0 additions & 25 deletions
This file was deleted.

internal/domain/user/email.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package user
22

33
import (
4+
"errors"
45
"net/mail"
56
"strings"
67
)
@@ -12,14 +13,12 @@ type Email struct {
1213
func NewEmail(value string) (Email, error) {
1314
value = strings.TrimSpace(value)
1415

15-
// Required
1616
if value == "" {
17-
return Email{}, ErrEmailRequired
17+
return Email{}, errors.New("email is required")
1818
}
1919

20-
// Validate format
2120
if _, err := mail.ParseAddress(value); err != nil {
22-
return Email{}, ErrEmailInvalid
21+
return Email{}, errors.New("invalid email format")
2322
}
2423

2524
return Email{value: value}, nil

0 commit comments

Comments
 (0)