Skip to content

Commit 4ab96e7

Browse files
authored
feat: add devices validation (#144)
1 parent 3f4e9a9 commit 4ab96e7

File tree

14 files changed

+267
-36
lines changed

14 files changed

+267
-36
lines changed

.github/workflows/ci.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ jobs:
1717
- name: Set up Go
1818
uses: actions/setup-go@v3
1919
with:
20-
go-version: "1.18"
20+
go-version: "1.19"
2121

2222
- uses: actions/checkout@v2
2323

@@ -27,4 +27,4 @@ jobs:
2727
STREAM_SECRET: ${{ secrets.STREAM_SECRET }}
2828
run: |
2929
go test -coverprofile cover.out -v -race ./...
30-
go tool cover -func=cover.out
30+
go tool cover -func=cover.out

.github/workflows/lint.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ jobs:
2222
- name: Setup Go
2323
uses: actions/setup-go@v3
2424
with:
25-
go-version: '1.18'
25+
go-version: '1.19'
2626

2727
- name: Tidy
2828
run: go mod tidy -v && git diff --no-patch --exit-code || { git status; echo 'Unchecked diff, did you forget go mod tidy again?' ; false ; };

.github/workflows/release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ jobs:
3535
- name: Set up Go
3636
uses: actions/setup-go@v3
3737
with:
38-
go-version: '1.18'
38+
go-version: '1.19'
3939

4040
- run: |
4141
git tag "${{ env.VERSION }}"

.github/workflows/reviewdog.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ jobs:
2020
- name: Setup Go
2121
uses: actions/setup-go@v3
2222
with:
23-
go-version: "1.18"
23+
go-version: "1.19"
2424

2525
- name: Install golangci-lint
2626
run:
@@ -30,4 +30,4 @@ jobs:
3030
env:
3131
REVIEWDOG_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }}
3232
run:
33-
$(go env GOPATH)/bin/golangci-lint run --out-format line-number | reviewdog -f=golangci-lint -name=golangci-lint -reporter=github-pr-review
33+
$(go env GOPATH)/bin/golangci-lint run --out-format line-number | reviewdog -f=golangci-lint -name=golangci-lint -reporter=github-pr-review

.golangci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
run:
2-
go: '1.18'
2+
go: '1.19'
33
deadline: 210s
44
timeout: 10m
55
skip-dirs:

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
NAME = stream-cli
22

3-
GOLANGCI_VERSION = 1.45.0
3+
GOLANGCI_VERSION = 1.49.0
44
GOLANGCI = .bin/golangci/$(GOLANGCI_VERSION)/golangci-lint
55
$(GOLANGCI):
66
@curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(dir $(GOLANGCI)) v$(GOLANGCI_VERSION)

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module github.com/GetStream/stream-cli
22

3-
go 1.18
3+
go 1.19
44

55
require (
66
github.com/AlecAivazis/survey/v2 v2.3.4

pkg/cmd/chat/imports/validator/index.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@ type index struct {
6767

6868
// reactions
6969
reactionPKs map[Bits256]struct{}
70+
71+
// devices
72+
devicePKs map[Bits256]struct{}
7073
}
7174

7275
func newIndex(roles map[string]*streamchat.Role, channelTypes map[string]*streamchat.ChannelType) *index {
@@ -84,6 +87,7 @@ func newIndex(roles map[string]*streamchat.Role, channelTypes map[string]*stream
8487
messagePKsWithReaction: make(map[Bits256]struct{}),
8588
messagePKsReplies: make(map[Bits256]struct{}),
8689
reactionPKs: make(map[Bits256]struct{}),
90+
devicePKs: make(map[Bits256]struct{}),
8791
}
8892
}
8993

@@ -94,6 +98,7 @@ func (i *index) stats() map[string]int {
9498
"members": len(i.memberPKs),
9599
"messages": len(i.messagePKs),
96100
"reactions": len(i.reactionPKs),
101+
"devices": len(i.devicePKs),
97102
}
98103
}
99104

@@ -302,3 +307,23 @@ func (i *index) addReaction(messageID, reactionType, userID string) error {
302307

303308
return nil
304309
}
310+
311+
func getDevicePK(deviceID string) Bits256 {
312+
return hashValues(deviceID)
313+
}
314+
315+
func (i *index) deviceExist(deviceID string) bool {
316+
pk := getDevicePK(deviceID)
317+
_, ok := i.devicePKs[pk]
318+
return ok
319+
}
320+
321+
func (i *index) addDevice(deviceID string) error {
322+
if i.deviceExist(deviceID) {
323+
return fmt.Errorf("duplicate device id:%s", deviceID)
324+
}
325+
326+
pk := getDevicePK(deviceID)
327+
i.devicePKs[pk] = struct{}{}
328+
return nil
329+
}

pkg/cmd/chat/imports/validator/items.go

Lines changed: 90 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import (
99
"strings"
1010
"time"
1111
"unicode/utf8"
12+
13+
streamchat "github.com/GetStream/stream-chat-go/v5"
1214
)
1315

1416
var (
@@ -35,6 +37,8 @@ func newItem(rawItem *rawItem) (Item, error) {
3537
return newMessageItem(rawItem.Item)
3638
case "reaction":
3739
return newReactionItem(rawItem.Item)
40+
case "device":
41+
return newDeviceItem(rawItem.Item)
3842
default:
3943
return nil, fmt.Errorf("invalid item type %q", rawItem.Type)
4044
}
@@ -88,14 +92,20 @@ func newUserItem(itemBody json.RawMessage) (*userItem, error) {
8892
}
8993

9094
type userItem struct {
91-
ID string `json:"id"`
92-
Role string `json:"role"`
93-
Invisible bool `json:"invisible"`
94-
CreatedAt time.Time `json:"created_at"`
95-
UpdatedAt time.Time `json:"updated_at"`
96-
DeletedAt time.Time `json:"deleted_at"`
97-
Teams []string `json:"teams"`
98-
Custom extraFields
95+
ID string `json:"id"`
96+
Role string `json:"role"`
97+
Invisible bool `json:"invisible"`
98+
CreatedAt time.Time `json:"created_at"`
99+
UpdatedAt time.Time `json:"updated_at"`
100+
DeletedAt time.Time `json:"deleted_at"`
101+
Teams []string `json:"teams"`
102+
PushNotifications pushNotification `json:"push_notifications"`
103+
Custom extraFields
104+
}
105+
106+
type pushNotification struct {
107+
Disabled bool `json:"disabled"`
108+
DisabledUntil *time.Time `json:"disabled_until"`
99109
}
100110

101111
func (u *userItem) validateFields() error {
@@ -130,6 +140,78 @@ func (u *userItem) validateReferences(idx *index) error {
130140
return nil
131141
}
132142

143+
type deviceItem struct {
144+
ID string `json:"id"`
145+
UserID string `json:"user_id"`
146+
CreatedAt time.Time `json:"created_at"`
147+
Disabled bool `json:"disabled"`
148+
DisabledReason string `json:"disabled_reason"`
149+
PushProviderType string `json:"push_provider_type"`
150+
PushProviderName string `json:"push_provider_name"`
151+
}
152+
153+
func newDeviceItem(itemBody json.RawMessage) (*deviceItem, error) {
154+
var device deviceItem
155+
if err := unmarshalItem(itemBody, &device); err != nil {
156+
return nil, err
157+
}
158+
return &device, nil
159+
}
160+
161+
var pushProviders = []string{
162+
streamchat.PushProviderFirebase,
163+
streamchat.PushProviderHuawei,
164+
streamchat.PushProviderAPNS,
165+
streamchat.PushProviderXiaomi,
166+
}
167+
168+
func (d *deviceItem) validateFields() error {
169+
if d.ID == "" {
170+
return errors.New("device.id required")
171+
}
172+
if len(d.ID) > 255 {
173+
return errors.New("device.id max length exceeded (255)")
174+
}
175+
176+
if d.UserID == "" {
177+
return errors.New("device.user_id required")
178+
}
179+
if len(d.UserID) > 255 {
180+
return errors.New("device.user_id max length exceeded (255)")
181+
}
182+
183+
if d.CreatedAt.IsZero() {
184+
return errors.New("device.created_at required")
185+
}
186+
187+
if d.PushProviderType == "" {
188+
return errors.New("device.push_provider_type required")
189+
}
190+
var found bool
191+
for _, p := range pushProviders {
192+
if d.PushProviderType == p {
193+
found = true
194+
break
195+
}
196+
}
197+
if !found {
198+
return fmt.Errorf("device.push_provider_type invalid, available options are: %s", strings.Join(pushProviders, ","))
199+
}
200+
201+
return nil
202+
}
203+
204+
func (d *deviceItem) index(i *index) error {
205+
return i.addDevice(d.ID)
206+
}
207+
208+
func (d *deviceItem) validateReferences(i *index) error {
209+
if d.UserID != "" && !i.userExist(d.UserID) {
210+
return fmt.Errorf("device.user_id %q doesn't exist", d.UserID)
211+
}
212+
return nil
213+
}
214+
133215
var channelReservedFields = []string{
134216
"last_message_at", "cid", "created_by_pk", "members", "config", "app_pk", "pk",
135217
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
[
2+
{
3+
"type": "user",
4+
"item": {
5+
"id": "userA",
6+
"role": "user"
7+
}
8+
},
9+
{
10+
"type": "device",
11+
"item": {
12+
"id": "id-too-long-id-too-long-id-too-long-id-too-long-id-too-long-id-too-long-id-too-long-id-too-long-id-too-long-id-too-long-id-too-long-id-too-long-id-too-long-id-too-long-id-too-long-id-too-long-id-too-long-id-too-long-id-too-long-id-too-long-id-too-long-id-too-long",
13+
"user_id": "userA",
14+
"created_at": "2022-01-01T01:01:01Z",
15+
"disabled": false,
16+
"push_provider_type": "firebase"
17+
}
18+
},
19+
{
20+
"type": "device",
21+
"item": {
22+
"id": "123",
23+
"user_id": "userB",
24+
"created_at": "2022-01-01T01:01:01Z",
25+
"disabled": false,
26+
"push_provider_type": "firebase"
27+
}
28+
},
29+
{
30+
"type": "device",
31+
"item": {
32+
"user_id": "userA",
33+
"created_at": "2022-01-01T01:01:01Z",
34+
"disabled": false,
35+
"push_provider_type": "firebase"
36+
}
37+
},
38+
{
39+
"type": "device",
40+
"item": {
41+
"id": "no user ID",
42+
"created_at": "2022-01-01T01:01:01Z",
43+
"disabled": false,
44+
"push_provider_type": "firebase"
45+
}
46+
},
47+
{
48+
"type": "device",
49+
"item": {
50+
"id": "123",
51+
"user_id": "userA",
52+
"created_at": "2022-01-01T01:01:01Z",
53+
"disabled": false,
54+
"push_provider_type": "unknown_provider"
55+
}
56+
},
57+
{
58+
"type": "device",
59+
"item": {
60+
"id": "123",
61+
"user_id": "userA",
62+
"disabled": false,
63+
"push_provider_type": "unknown_provider"
64+
}
65+
},
66+
{
67+
"type": "device",
68+
"item": {
69+
"id": "duplicate",
70+
"user_id": "userA",
71+
"created_at": "2022-01-01T01:01:01Z",
72+
"disabled": false,
73+
"push_provider_type": "apn"
74+
}
75+
},
76+
{
77+
"type": "device",
78+
"item": {
79+
"id": "duplicate",
80+
"user_id": "userA",
81+
"created_at": "2022-01-01T01:01:01Z",
82+
"disabled": false,
83+
"push_provider_type": "xiaomi"
84+
}
85+
}
86+
]

0 commit comments

Comments
 (0)