diff --git a/go.mod b/go.mod index 6f72f81..d71e334 100644 --- a/go.mod +++ b/go.mod @@ -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 ) diff --git a/go.sum b/go.sum index 2e7b560..6fbf892 100644 --- a/go.sum +++ b/go.sum @@ -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= diff --git a/iotdevice/client.go b/iotdevice/client.go index 8eb180e..05116f5 100644 --- a/iotdevice/client.go +++ b/iotdevice/client.go @@ -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. @@ -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. diff --git a/tests/end2end_test.go b/tests/end2end_test.go index 8a830b5..e82332d 100644 --- a/tests/end2end_test.go +++ b/tests/end2end_test.go @@ -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" @@ -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) } } @@ -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")