Stop various JSON field from being sent with null values #4260

Merged
merged 3 commits into from Nov 23, 2017
View
@@ -20,6 +20,7 @@
package client
import (
+ "encoding/json"
"errors"
"fmt"
"net/url"
@@ -35,37 +36,52 @@ type Snap struct {
Title string `json:"title,omitempty"`
Summary string `json:"summary"`
Description string `json:"description"`
- DownloadSize int64 `json:"download-size"`
- Icon string `json:"icon"`
- InstalledSize int64 `json:"installed-size"`
- InstallDate time.Time `json:"install-date"`
+ DownloadSize int64 `json:"download-size,omitempty"`
+ Icon string `json:"icon,omitempty"`
+ InstalledSize int64 `json:"installed-size,omitempty"`
+ InstallDate time.Time `json:"install-date,omitempty"`
Name string `json:"name"`
Developer string `json:"developer"`
Status string `json:"status"`
Type string `json:"type"`
Version string `json:"version"`
Channel string `json:"channel"`
- TrackingChannel string `json:"tracking-channel"`
- IgnoreValidation bool `json:"ignore-validation"`
+ TrackingChannel string `json:"tracking-channel,omitempty"`
+ IgnoreValidation bool `json:"ignore-validation,omitempty"`
Revision snap.Revision `json:"revision"`
Confinement string `json:"confinement"`
- Private bool `json:"private"`
- DevMode bool `json:"devmode"`
- JailMode bool `json:"jailmode"`
- TryMode bool `json:"trymode"`
- Apps []AppInfo `json:"apps"`
- Broken string `json:"broken"`
+ Private bool `json:"private,omitempty"`
+ DevMode bool `json:"devmode,omitempty"`
+ JailMode bool `json:"jailmode,omitempty"`
+ TryMode bool `json:"trymode,omitempty"`
+ Apps []AppInfo `json:"apps,omitempty"`
+ Broken string `json:"broken,omitempty"`
Contact string `json:"contact"`
License string `json:"license,omitempty"`
- Prices map[string]float64 `json:"prices"`
- Screenshots []Screenshot `json:"screenshots"`
+ Prices map[string]float64 `json:"prices,omitempty"`
+ Screenshots []Screenshot `json:"screenshots,omitempty"`
// The flattended channel map with $track/$risk
- Channels map[string]*snap.ChannelSnapInfo `json:"channels"`
+ Channels map[string]*snap.ChannelSnapInfo `json:"channels,omitempty"`
// The ordered list of tracks that contains channels
- Tracks []string
+ Tracks []string `json:"tracks,omitempty"`
+}
+
+func (s *Snap) MarshalJSON() ([]byte, error) {
+ type auxSnap Snap // use auxiliary type so that Go does not call Snap.MarshalJSON()
+ // separate type just for marshalling
+ m := struct {
+ auxSnap
+ InstallDate *time.Time `json:"install-date,omitempty"`
+ }{
+ auxSnap: auxSnap(*s),
+ }
+ if !s.InstallDate.IsZero() {
+ m.InstallDate = &s.InstallDate
+ }
+ return json.Marshal(&m)
}
type Screenshot struct {
View
@@ -1419,7 +1419,7 @@ func (s *apiSuite) TestFind(c *check.C) {
c.Assert(snaps, check.HasLen, 1)
c.Assert(snaps[0]["name"], check.Equals, "store")
c.Check(snaps[0]["prices"], check.IsNil)
- c.Check(snaps[0]["screenshots"], check.HasLen, 0)
+ c.Check(snaps[0]["screenshots"], check.IsNil)
@bboozzoo

bboozzoo Nov 21, 2017

Contributor

That's interesting. I would suspect the checker to behave the same for var foo []string and foo := []string{}. Both have len(..) == 0.

@zyga

zyga Nov 21, 2017

Contributor

Yes, I was thinking the same thing.

c.Check(snaps[0]["channels"], check.IsNil)
c.Check(rsp.SuggestedCurrency, check.Equals, "EUR")
View
@@ -20,6 +20,7 @@
package daemon
import (
+ "encoding/json"
"fmt"
"io/ioutil"
"net/http"
@@ -97,3 +98,12 @@ func (s *responseSuite) TestFileResponseSetsContentDisposition(c *check.C) {
c.Check(hdr.Get("Content-Disposition"), check.Equals,
fmt.Sprintf("attachment; filename=%s", filename))
}
+
+// Due to how the protocol was defined the result must be sent, even if it is
+// null. Older clients rely on this.
+func (s *responseSuite) TestRespJSONWithNullResult(c *check.C) {
+ rj := &respJSON{Result: nil}
+ data, err := json.Marshal(rj)
+ c.Assert(err, check.IsNil)
+ c.Check(string(data), check.Equals, `{"type":"","status-code":0,"status":"","result":null}`)
+}