Skip to content

Commit

Permalink
Convert TwinState to a []byte.
Browse files Browse the repository at this point in the history
At the MQTT layer publishing and receiving twin state is a []byte. This
eliminates the conversion of that data to map[string]interface{} at the
Client layer. I believe the original implementation expected twin data
to only be key/value pairs. This was not the case for me and an
interface{} type can not be indexed. Working with the byte array better
works in my use case.
  • Loading branch information
aka-mj committed Nov 3, 2022
1 parent 20c8c9a commit 3bba9e4
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 43 deletions.
12 changes: 8 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
module github.com/dangeroushobo/iothub

go 1.17
go 1.18

require (
github.com/Azure/go-amqp v0.17.0
github.com/eclipse/paho.mqtt.golang v1.3.5
github.com/eclipse/paho.mqtt.golang v1.4.2
github.com/tidwall/gjson v1.14.3
)

require (
github.com/gorilla/websocket v1.4.2 // indirect
golang.org/x/net v0.0.0-20211020060615-d418f374d309 // indirect
github.com/gorilla/websocket v1.5.0 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.0 // indirect
golang.org/x/net v0.1.0 // indirect
golang.org/x/sync v0.1.0 // indirect
)
20 changes: 15 additions & 5 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,38 @@ github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/eclipse/paho.mqtt.golang v1.3.5 h1:sWtmgNxYM9P2sP+xEItMozsR3w0cqZFlqnNN1bdl41Y=
github.com/eclipse/paho.mqtt.golang v1.3.5/go.mod h1:eTzb4gxwwyWpqBUHGQZ4ABAV7+Jgm1PklsYT/eo8Hcc=
github.com/eclipse/paho.mqtt.golang v1.4.2 h1:66wOzfUHSSI1zamx7jR6yMEI5EuHnT1G6rNA5PM12m4=
github.com/eclipse/paho.mqtt.golang v1.4.2/go.mod h1:JGt0RsEwEX+Xa/agj90YJ9d9DH2b7upDZMK9HRbFvCA=
github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
github.com/google/go-cmp v0.5.1 h1:JFrFEBb2xKufg6XkJsJr+WbKb4FQlURi5RUcBveYu9k=
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/tidwall/gjson v1.14.3 h1:9jvXn7olKEHU1S9vwoMGliaT8jq1vJ7IH/n9zD9Dnlw=
github.com/tidwall/gjson v1.14.3/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.0.0-20200425230154-ff2c4b7c35a0/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20211020060615-d418f374d309 h1:A0lJIi+hcTR6aajJH4YqKWwohY4aW9RO7oRMcdv+HKI=
golang.org/x/net v0.0.0-20211020060615-d418f374d309/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0=
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
38 changes: 16 additions & 22 deletions iotdevice/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -213,37 +213,35 @@ func (c *Client) UnregisterMethod(name string) {
}

// TwinState is both desired and reported twin device's state.
type TwinState map[string]interface{}
type TwinState []byte

// Version is state version.
func (s TwinState) Version() int {
v, _ := s["$version"].(float64)
return int(v)
func (s TwinState) Version() (int, int) {
var v struct {
Desired map[string]any `json:"desired"`
Reported map[string]any `json:"reported"`
}
json.Unmarshal(s, &v)
d, _ := v.Desired["$version"].(float64)
r, _ := v.Reported["$version"].(float64)
return int(d), int(r)
}

// String returns the string representation of the TwinState.
func (s TwinState) String() string {
b, _ := json.Marshal(s)
return string(b)
return string(s)
}

// RetrieveTwinState returns desired and reported twin device states.
func (c *Client) RetrieveTwinState(ctx context.Context) (desired, reported TwinState, err error) {
func (c *Client) RetrieveTwinState(ctx context.Context) (tw TwinState, err error) {
if err := c.checkConnection(ctx); err != nil {
return nil, nil, err
return TwinState{}, err
}
b, err := c.tr.RetrieveTwinProperties(ctx)
if err != nil {
return nil, nil, err
}
var v struct {
Desired TwinState `json:"desired"`
Reported TwinState `json:"reported"`
return TwinState{}, err
}
if err := json.Unmarshal(b, &v); err != nil {
return nil, nil, err
}
return v.Desired, v.Reported, nil
return TwinState(b), nil
}

// UpdateTwinState updates twin device's state and returns new version.
Expand All @@ -252,11 +250,7 @@ func (c *Client) UpdateTwinState(ctx context.Context, s TwinState) (int, error)
if err := c.checkConnection(ctx); err != nil {
return 0, err
}
b, err := json.Marshal(s)
if err != nil {
return 0, err
}
return c.tr.UpdateTwinProperties(ctx, b)
return c.tr.UpdateTwinProperties(ctx, []byte(s))
}

// SubscribeTwinUpdates registers fn as a desired state changes handler.
Expand Down
27 changes: 15 additions & 12 deletions tests/end2end_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import (
"testing"
"time"

"github.com/tidwall/gjson"

"github.com/dangeroushobo/iothub/iotdevice"
"github.com/dangeroushobo/iothub/iotdevice/transport"
"github.com/dangeroushobo/iothub/iotdevice/transport/mqtt"
Expand Down Expand Up @@ -307,22 +309,21 @@ func testCloudToDevice(t *testing.T, sc *iotservice.Client, dc *iotdevice.Client
func testUpdateTwin(t *testing.T, sc *iotservice.Client, dc *iotdevice.Client) {
// update state and keep track of version
s := fmt.Sprintf("%d", time.Now().UnixNano())
v, err := dc.UpdateTwinState(context.Background(), map[string]interface{}{
"ts": s,
})
v, err := dc.UpdateTwinState(context.Background(), []byte(fmt.Sprintf(`{"ts":%s}`, s)))
if err != nil {
t.Fatal(err)
}

_, r, err := dc.RetrieveTwinState(context.Background())
r, err := dc.RetrieveTwinState(context.Background())
if err != nil {
t.Fatal(err)
}
if v != r.Version() {
t.Errorf("update-twin version = %d, want %d", r.Version(), v)
_, reported := r.Version()
if v != reported {
t.Errorf("update-twin version = %d, want %d", reported, v)
}
if r["ts"] != s {
t.Errorf("update-twin parameter = %q, want %q", r["ts"], s)
if gjson.GetBytes(r, "ts").String() != s {
t.Errorf("update-twin parameter = %q, want %q", gjson.GetBytes(r, "reported.ts").String(), s)
}
}

Expand Down Expand Up @@ -354,11 +355,13 @@ func testSubscribeTwin(t *testing.T, sc *iotservice.Client, dc *iotdevice.Client

select {
case state := <-sub.C():
if state["$version"] != twin.Properties.Desired["$version"] {
t.Errorf("version = %d, want %d", state["$version"], twin.Properties.Desired["$version"])
desiredVersion, _ := state.Version()
if desiredVersion != twin.Properties.Desired["$version"] {
t.Errorf("version = %d, want %d", desiredVersion, twin.Properties.Desired["$version"])
}
if state["test-prop"] != twin.Properties.Desired["test-prop"] {
t.Errorf("test-prop = %q, want %q", state["test-prop"], twin.Properties.Desired["test-prop"])
prop := gjson.GetBytes(state, "desired.test-prop").String()
if prop != twin.Properties.Desired["test-prop"] {
t.Errorf("test-prop = %q, want %q", prop, twin.Properties.Desired["test-prop"])
}
case <-time.After(10 * time.Second):
t.Fatal("SubscribeTwinUpdates timed out")
Expand Down

0 comments on commit 3bba9e4

Please sign in to comment.