feat(responses): implement responses and add a test

- implement rendering test for responses and requests
- redo the API a bit to make responses and requests consistent
This commit is contained in:
Brandon Philips 2013-04-01 09:53:53 -07:00
parent 5cb5b5c4e9
commit 8a80b4e359
3 changed files with 257 additions and 104 deletions

View file

@ -24,4 +24,3 @@ IsDelta="True"
</updatecheck> </updatecheck>
</app> </app>
</response> </response>

View file

@ -1,140 +1,250 @@
/* /*
Package that implements the Google omaha protocol. Package that implements the Google omaha protocol.
Omaha is a request/response protocol using XML. Requests are made by Omaha is a request/response protocol using XML. Requests are made by
clients and responses are given by the Omaha server. clients and responses are given by the Omaha server.
http://code.google.com/p/omaha/wiki/ServerProtocol http://code.google.com/p/omaha/wiki/ServerProtocol
The The
*/ */
package omaha package omaha
import "encoding/xml" import (
"encoding/xml"
)
type Request struct { type Request struct {
Os Os XMLName xml.Name `xml:"request"`
Apps []App `xml:"app"` Os Os `xml:"os"`
Protocol string `xml:"protocol,attr"` Apps []*App `xml:"app"`
Version string `xml:"version,attr,omitempty"` Protocol string `xml:"protocol,attr"`
IsMachine string `xml:"ismachine,attr,omitempty"` Version string `xml:"version,attr,omitempty"`
SessionId string `xml:"sessionid,attr,omitempty"` IsMachine string `xml:"ismachine,attr,omitempty"`
UserId string `xml:"userid,attr,omitempty"` SessionId string `xml:"sessionid,attr,omitempty"`
InstallSource string `xml:"installsource,attr,omitempty"` UserId string `xml:"userid,attr,omitempty"`
TestSource string `xml:"testsource,attr,omitempty"` InstallSource string `xml:"installsource,attr,omitempty"`
RequestId string `xml:"requestid,attr,omitempty"` TestSource string `xml:"testsource,attr,omitempty"`
UpdaterVersion string `xml:"updaterversion,attr,omitempty"` RequestId string `xml:"requestid,attr,omitempty"`
UpdaterVersion string `xml:"updaterversion,attr,omitempty"`
} }
func NewRequest(os *Os, app *App) *Request { func NewRequest(version string, platform string, sp string, arch string) *Request {
r := new(Request) r := new(Request)
r.Protocol = "3.0" r.Protocol = "3.0"
r.AddApp(app) r.Os.Version = version
r.Os = *os r.Os.Platform = platform
r.Os.Sp = sp
r.Os.Arch = arch
return r return r
} }
func (r *Request) AddApp(a *App) { func (r *Request) AddApp(id string, version string) *App {
r.Apps = append(r.Apps, *a) a := NewApp(id)
}
// app element
type App struct {
XMLName xml.Name `xml:"app"`
UpdateCheck *UpdateCheck `xml:"updatecheck"`
Event *Event `xml:"event"`
Ping *Ping `xml:"ping"`
Id string `xml:"appid,attr,omitempty"`
Version string `xml:"version,attr,omitempty"`
NextVersion string `xml:"nextversion,attr,omitempty"`
Lang string `xml:"lang,attr,omitempty"`
Client string `xml:"client,attr,omitempty"`
InstallAge string `xml:"installage,attr,omitempty"`
FromTrack string `xml:"from_track,attr,omitempty"`
}
func NewApp(id string, version string) *App {
a := new(App)
a.Id = id
a.Version = version a.Version = version
r.Apps = append(r.Apps, a)
return a return a
} }
type UpdateCheck struct { /* Response
XMLName xml.Name `xml:"updatecheck"` */
TargetVersionPrefix string `xml:"targetversionprefix,attr,omitempty"` type Response struct {
XMLName xml.Name `xml:"response"`
DayStart DayStart `xml:"daystart"`
Apps []*App `xml:"app"`
Protocol string `xml:"protocol,attr"`
Server string `xml:"server,attr"`
} }
func (a *App) AddUpdateCheck() { func NewResponse(server string) *Response {
r := &Response{Server: server, Protocol: "3.0"}
r.DayStart.ElapsedSeconds = "0"
return r
}
type DayStart struct {
ElapsedSeconds string `xml:"elapsed_seconds,attr"`
}
func (r *Response) AddApp(id string) *App {
a := NewApp(id)
r.Apps = append(r.Apps, a)
return a
}
type App struct {
XMLName xml.Name `xml:"app"`
Ping *Ping `xml:"ping"`
UpdateCheck *UpdateCheck `xml:"updatecheck"`
Urls *Urls `xml:"urls"`
Manifest *Manifest `xml:"manifest"`
Event *Event `xml:"event"`
Id string `xml:"appid,attr,omitempty"`
Version string `xml:"version,attr,omitempty"`
NextVersion string `xml:"nextversion,attr,omitempty"`
Lang string `xml:"lang,attr,omitempty"`
Client string `xml:"client,attr,omitempty"`
InstallAge string `xml:"installage,attr,omitempty"`
FromTrack string `xml:"from_track,attr,omitempty"`
Status string `xml:"status,attr,omitempty"`
}
func NewApp(id string) *App {
a := &App{Id: id}
return a
}
func (a *App) AddUpdateCheck() *UpdateCheck {
a.UpdateCheck = new(UpdateCheck) a.UpdateCheck = new(UpdateCheck)
return a.UpdateCheck
}
func (a *App) AddPing() *Ping {
a.Ping = new(Ping)
return a.Ping
}
func (a *App) AddUrl(codebase string) *Url {
if a.Urls == nil {
a.Urls = new(Urls)
}
u := new(Url)
u.CodeBase = codebase
a.Urls.Urls = append(a.Urls.Urls, *u)
return u
}
func (a *App) AddManifest(version string) *Manifest {
a.Manifest = &Manifest{Version: version}
return a.Manifest
}
type UpdateCheck struct {
XMLName xml.Name `xml:"updatecheck"`
TargetVersionPrefix string `xml:"targetversionprefix,attr,omitempty"`
Status string `xml:"status,attr,omitempty"`
} }
type Ping struct { type Ping struct {
XMLName xml.Name `xml:"ping"` XMLName xml.Name `xml:"ping"`
LastReportDays string `xml:"r,attr,omitempty"` LastReportDays string `xml:"r,attr,omitempty"`
Status string `xml:"status,attr,omitempty"`
} }
type Os struct { type Os struct {
XMLName xml.Name `xml:"os"` XMLName xml.Name `xml:"os"`
Platform string `xml:"platform,attr,omitempty"` Platform string `xml:"platform,attr,omitempty"`
Version string `xml:"version,attr,omitempty"` Version string `xml:"version,attr,omitempty"`
Sp string `xml:"sp,attr,omitempty"` Sp string `xml:"sp,attr,omitempty"`
Arch string `xml:"arch,attr,omitempty"` Arch string `xml:"arch,attr,omitempty"`
} }
func NewOs(platform string, version string, sp string, arch string) *Os { func NewOs(platform string, version string, sp string, arch string) *Os {
o := new(Os) o := new(Os)
o.Platform = platform
o.Version = version o.Version = version
o.Platform = platform
o.Sp = sp o.Sp = sp
o.Arch = arch o.Arch = arch
return o return o
} }
func (a *App) AddPing() {
}
type Event struct { type Event struct {
XMLName xml.Name `xml:"event"` XMLName xml.Name `xml:"event"`
Type string `xml:"eventtype,attr,omitempty"` Type string `xml:"eventtype,attr,omitempty"`
Result string `xml:"eventresult,attr,omitempty"` Result string `xml:"eventresult,attr,omitempty"`
PreviousVersion string `xml:"previousversion,attr,omitempty"` PreviousVersion string `xml:"previousversion,attr,omitempty"`
} }
var EventTypes = map[int] string { type Urls struct {
0: "unknown", XMLName xml.Name `xml:"urls"`
1: "download complete", Urls []Url `xml:"url"`
2: "install complete", }
3: "update complete",
4: "uninstall", type Url struct {
5: "download started", XMLName xml.Name `xml:"url"`
6: "install started", CodeBase string `xml:"codebase,attr"`
9: "new application install started", }
10: "setup started",
11: "setup finished", type Manifest struct {
12: "update application started", XMLName xml.Name `xml:"manifest"`
13: "update download started", Packages Packages `xml:"packages"`
14: "update download finished", Actions Actions `xml:"actions"`
15: "update installer started", Version string `xml:"version,attr"`
16: "setup update begin", }
17: "setup update complete",
20: "register product complete", type Packages struct {
30: "OEM install first check", XMLName xml.Name `xml:"packages"`
40: "app-specific command started", Packages []Package `xml:"package"`
41: "app-specific command ended", }
type Package struct {
XMLName xml.Name `xml:"package"`
Hash string `xml:"hash,attr"`
Name string `xml:"name,attr"`
Size string `xml:"size,attr"`
Required bool `xml:"required,attr"`
}
func (m *Manifest) AddPackage(hash string, name string, size string, required bool) *Package {
p := &Package{Hash: hash, Name: name, Size: size, Required: required}
m.Packages.Packages = append(m.Packages.Packages, *p)
return p
}
type Actions struct {
XMLName xml.Name `xml:"actions"`
Actions []Action `xml:"action"`
}
type Action struct {
XMLName xml.Name `xml:"action"`
Event string `xml:"event,attr"`
ChromeOSVersion string `xml:"ChromeOSVersion,attr"`
sha256 string `xml:"sha256,attr"`
NeedsAdmin bool `xml:"needsadmin,attr"`
IsDelta bool `xml:"IsDelta,attr"`
}
func (m *Manifest) AddAction(event string) *Action {
a := &Action{Event: event}
m.Actions.Actions = append(m.Actions.Actions, *a)
return a
}
var EventTypes = map[int]string{
0: "unknown",
1: "download complete",
2: "install complete",
3: "update complete",
4: "uninstall",
5: "download started",
6: "install started",
9: "new application install started",
10: "setup started",
11: "setup finished",
12: "update application started",
13: "update download started",
14: "update download finished",
15: "update installer started",
16: "setup update begin",
17: "setup update complete",
20: "register product complete",
30: "OEM install first check",
40: "app-specific command started",
41: "app-specific command ended",
100: "setup failure", 100: "setup failure",
102: "COM server failure", 102: "COM server failure",
103: "setup update failure", 103: "setup update failure",
} }
var EventResults = map[int] string { var EventResults = map[int]string{
0: "error", 0: "error",
1: "success", 1: "success",
2: "success reboot", 2: "success reboot",
3: "success restart browser", 3: "success restart browser",
4: "cancelled", 4: "cancelled",
5: "error installer MSI", 5: "error installer MSI",
6: "error installer other", 6: "error installer other",
7: "noupdate", 7: "noupdate",
8: "error installer system", 8: "error installer system",
9: "update deferred", 9: "update deferred",
10: "handoff error", 10: "handoff error",
} }

View file

@ -1,11 +1,11 @@
package omaha package omaha
import ( import (
"testing"
"fmt"
"encoding/xml" "encoding/xml"
"fmt"
"io/ioutil" "io/ioutil"
"os" "os"
"testing"
) )
func TestOmahaRequestUpdateCheck(t *testing.T) { func TestOmahaRequestUpdateCheck(t *testing.T) {
@ -45,14 +45,58 @@ func TestOmahaRequestUpdateCheck(t *testing.T) {
} }
} }
func ExampleOmaha_NewResponse() {
response := NewResponse("unit-test")
app := response.AddApp("{52F1B9BC-D31A-4D86-9276-CBC256AADF9A}")
app.Status = "ok"
p := app.AddPing()
p.Status = "ok"
u := app.AddUpdateCheck()
u.Status = "ok"
app.AddUrl("http://localhost/updates")
m := app.AddManifest("9999.0.0")
m.AddPackage("+LXvjiaPkeYDLHoNKlf9qbJwvnk=", "update.gz", "67546213", true)
a := m.AddAction("postinstall")
a.ChromeOSVersion = "9999.0.0"
a.sha256 = "0VAlQW3RE99SGtSB5R4m08antAHO8XDoBMKDyxQT/Mg="
a.NeedsAdmin = false
a.IsDelta = true
if raw, err := xml.MarshalIndent(response, "", " "); err != nil {
fmt.Println(err)
return
} else {
fmt.Printf("%s%s\n", xml.Header, raw)
}
// Output:
// <?xml version="1.0" encoding="UTF-8"?>
//
// <response protocol="3.0" server="unit-test">
// <daystart elapsed_seconds="0"></daystart>
// <app appid="{52F1B9BC-D31A-4D86-9276-CBC256AADF9A}" status="ok">
// <ping status="ok"></ping>
// <updatecheck status="ok"></updatecheck>
// <urls>
// <url codebase="http://localhost/updates"></url>
// </urls>
// <manifest version="9999.0.0">
// <packages>
// <package hash="+LXvjiaPkeYDLHoNKlf9qbJwvnk=" name="update.gz" size="67546213" required="true"></package>
// </packages>
// <actions>
// <action event="postinstall" ChromeOSVersion="" needsadmin="false" IsDelta="false"></action>
// </actions>
// </manifest>
// </app>
// </response>
}
func ExampleOmaha_NewRequest() { func ExampleOmaha_NewRequest() {
os := NewOs("linux", "3.0", "", "x64") request := NewRequest("Indy", "Chrome OS", "ForcedUpdate_x86_64", "")
app := request.AddApp("{27BD862E-8AE8-4886-A055-F7F1A6460627}", "1.0.0.0")
app := NewApp("{27BD862E-8AE8-4886-A055-F7F1A6460627}", "1.0.0.0")
app.AddUpdateCheck() app.AddUpdateCheck()
request := NewRequest(os, app)
if raw, err := xml.MarshalIndent(request, "", " "); err != nil { if raw, err := xml.MarshalIndent(request, "", " "); err != nil {
fmt.Println(err) fmt.Println(err)
return return
@ -63,10 +107,10 @@ func ExampleOmaha_NewRequest() {
// Output: // Output:
// <?xml version="1.0" encoding="UTF-8"?> // <?xml version="1.0" encoding="UTF-8"?>
// //
// <Request protocol="3.0"> // <request protocol="3.0">
// <os platform="linux" version="3.0" arch="x64"></os> // <os platform="Chrome OS" version="Indy" sp="ForcedUpdate_x86_64"></os>
// <app appid="{27BD862E-8AE8-4886-A055-F7F1A6460627}" version="1.0.0.0"> // <app appid="{27BD862E-8AE8-4886-A055-F7F1A6460627}" version="1.0.0.0">
// <updatecheck></updatecheck> // <updatecheck></updatecheck>
// </app> // </app>
// </Request> // </request>
} }