Created
October 11, 2013 20:02
-
-
Save larzconwell/6941140 to your computer and use it in GitHub Desktop.
Moln Client example
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"encoding/json" | |
"errors" | |
"flag" | |
"fmt" | |
"net/http" | |
"net/url" | |
"os" | |
"strconv" | |
"strings" | |
) | |
var commands = make(map[string]*Command) | |
const usage = `client [command] [args] | |
Commands: | |
help account tasks` | |
// Account represents the account stored on the FS. | |
type Account struct { | |
Name string `json:"name"` | |
Server string `json:"server"` | |
Token string `json:"token"` | |
} | |
// Activity is a single activity | |
type Activity struct { | |
Time string | |
Message string | |
} | |
func (activity *Activity) String() string { | |
return activity.Time + " " + activity.Message | |
} | |
// Task is a single task | |
type Task struct { | |
ID int | |
Message string | |
Complete bool | |
} | |
func (task *Task) String() string { | |
complete := "×" | |
if task.Complete { | |
complete = "✓" | |
} | |
return complete + " " + strconv.Itoa(task.ID) + " " + task.Message | |
} | |
// LoadAccount decodes the clientrc file. | |
func LoadAccount() (*Account, error) { | |
file, err := os.Open(".clientrc") | |
if err != nil { | |
return nil, err | |
} | |
defer file.Close() | |
account := new(Account) | |
decoder := json.NewDecoder(file) | |
err = decoder.Decode(account) | |
if err != nil { | |
return nil, err | |
} | |
return account, nil | |
} | |
// ErrorResponse is a non 200 response from the server. | |
type ErrorResponse struct { | |
Err string `json:"error"` | |
Errs []string `json:"errors"` | |
} | |
func (err *ErrorResponse) Error() string { | |
if err.Errs != nil { | |
msg := "" | |
for _, e := range err.Errs { | |
if msg == "" { | |
msg = e | |
} else { | |
msg += ", " + e | |
} | |
} | |
return msg | |
} | |
return err.Err | |
} | |
// Command is a single command with help message and execution. | |
type Command struct { | |
Help string | |
Run func(args ...string) error | |
} | |
// Delete sends a delete request to the path authenticating with the account. | |
func Delete(account *Account, path string) (*http.Response, error) { | |
req, err := http.NewRequest("DELETE", account.Server+path, nil) | |
if err != nil { | |
return nil, err | |
} | |
return request(account, req) | |
} | |
// Get sends a get request to the path authenticating with the account. | |
func Get(account *Account, path string) (*http.Response, error) { | |
req, err := http.NewRequest("GET", account.Server+path, nil) | |
if err != nil { | |
return nil, err | |
} | |
return request(account, req) | |
} | |
// SendForm posts the given form to the path with authorization from the account. | |
func SendForm(account *Account, method, path string, form url.Values) (*http.Response, error) { | |
req, err := http.NewRequest(method, account.Server+path, strings.NewReader(form.Encode())) | |
if err != nil { | |
return nil, err | |
} | |
req.Header.Set("Content-Type", "application/x-www-form-urlencoded") | |
return request(account, req) | |
} | |
// request handles requesting and managing responses. | |
func request(account *Account, req *http.Request) (*http.Response, error) { | |
req.Header.Set("Authorization", "token "+account.Token) | |
res, err := http.DefaultClient.Do(req) | |
if err != nil { | |
return nil, err | |
} | |
if res.StatusCode != 200 { | |
e := new(ErrorResponse) | |
decoder := json.NewDecoder(res.Body) | |
err = decoder.Decode(e) | |
res.Body.Close() | |
if err != nil { | |
return nil, err | |
} | |
return nil, e | |
} | |
return res, nil | |
} | |
/* | |
Commands | |
*/ | |
const helpHelp = `client help [command] | |
View help for the given command` | |
func help(args ...string) error { | |
if len(args) <= 0 { | |
fmt.Fprintln(os.Stderr, helpHelp) | |
return nil | |
} | |
cmd, ok := commands[args[0]] | |
if !ok { | |
return errors.New("Command " + args[0] + " not found") | |
} | |
fmt.Println(cmd.Help) | |
return nil | |
} | |
const accountHelp = `client account [command] [args] | |
Manage your account | |
Commands: | |
create <server> <name> <password> <device> Create a new account | |
update [password] Update your account | |
activity View your accounts activity | |
delete Delete your account` | |
func account(args ...string) error { | |
if len(args) <= 0 { | |
fmt.Fprintln(os.Stderr, accountHelp) | |
return nil | |
} | |
switch args[0] { | |
case "create": | |
if len(args[1:]) < 4 { | |
return errors.New("Not all arguments given") | |
} | |
res, err := SendForm(&Account{Server: args[1]}, "POST", "/user", url.Values{ | |
"name": {args[2]}, | |
"password": {args[3]}, | |
"device": {args[4]}, | |
}) | |
if err != nil { | |
return err | |
} | |
defer res.Body.Close() | |
body := make(map[string]map[string]string) | |
decoder := json.NewDecoder(res.Body) | |
err = decoder.Decode(&body) | |
if err != nil { | |
return err | |
} | |
account := &Account{args[2], args[1], body["device"]["token"]} | |
file, err := os.Create(".clientrc") | |
if err != nil { | |
return err | |
} | |
defer file.Close() | |
encoder := json.NewEncoder(file) | |
err = encoder.Encode(account) | |
if err != nil { | |
return err | |
} | |
fmt.Println("Created account for", args[2]) | |
break | |
case "update": | |
account, err := LoadAccount() | |
if err != nil { | |
return err | |
} | |
form := make(url.Values) | |
if len(args) > 1 { | |
form["password"] = []string{args[1]} | |
} | |
res, err := SendForm(account, "PUT", "/user", form) | |
if err != nil { | |
return err | |
} | |
defer res.Body.Close() | |
fmt.Println("Updated account for", account.Name) | |
break | |
case "activity": | |
account, err := LoadAccount() | |
if err != nil { | |
return err | |
} | |
res, err := Get(account, "/activities") | |
if err != nil { | |
return err | |
} | |
defer res.Body.Close() | |
activities := make([]*Activity, 0) | |
decoder := json.NewDecoder(res.Body) | |
err = decoder.Decode(&activities) | |
if err != nil { | |
return err | |
} | |
for _, activity := range activities { | |
fmt.Println(activity) | |
} | |
break | |
case "delete": | |
account, err := LoadAccount() | |
if err != nil { | |
return err | |
} | |
res, err := Delete(account, "/user") | |
if err != nil { | |
return err | |
} | |
defer res.Body.Close() | |
err = os.Remove(".clientrc") | |
if err != nil { | |
return err | |
} | |
fmt.Println("Deleted account for", account.Name) | |
break | |
default: | |
fmt.Println(accountHelp) | |
return nil | |
} | |
return nil | |
} | |
const tasksHelp = `client tasks [command] [args] | |
List and manage tasks, to get a single task just give an id | |
Commands: | |
create <message> Create a new task | |
toggle <id> Toggle a task complete | |
delete <id> Delete a task` | |
func tasks(args ...string) error { | |
account, err := LoadAccount() | |
if err != nil { | |
return err | |
} | |
if len(args) <= 0 { | |
res, err := Get(account, "/tasks") | |
if err != nil { | |
return err | |
} | |
defer res.Body.Close() | |
tasks := make([]*Task, 0) | |
decoder := json.NewDecoder(res.Body) | |
err = decoder.Decode(&tasks) | |
if err != nil { | |
return err | |
} | |
for _, task := range tasks { | |
fmt.Println(task) | |
} | |
return nil | |
} | |
switch args[0] { | |
case "create": | |
if len(args) <= 1 { | |
fmt.Println(tasksHelp) | |
return nil | |
} | |
res, err := SendForm(account, "POST", "/tasks", url.Values{"message": {args[1]}}) | |
if err != nil { | |
return err | |
} | |
defer res.Body.Close() | |
task := new(Task) | |
decoder := json.NewDecoder(res.Body) | |
err = decoder.Decode(task) | |
if err != nil { | |
return err | |
} | |
fmt.Println(task) | |
break | |
case "toggle": | |
if len(args) <= 1 { | |
fmt.Println(tasksHelp) | |
return nil | |
} | |
res, err := Get(account, "/tasks/"+args[1]) | |
if err != nil { | |
return err | |
} | |
defer res.Body.Close() | |
task := new(Task) | |
decoder := json.NewDecoder(res.Body) | |
err = decoder.Decode(task) | |
if err != nil { | |
return err | |
} | |
form := make(url.Values) | |
if task.Complete { | |
form["complete"] = []string{"false"} | |
task.Complete = false | |
} else { | |
form["complete"] = []string{"true"} | |
task.Complete = true | |
} | |
res, err = SendForm(account, "PUT", "/tasks/"+args[1], form) | |
if err != nil { | |
return err | |
} | |
defer res.Body.Close() | |
fmt.Println(task) | |
break | |
case "delete": | |
if len(args) <= 1 { | |
fmt.Println(tasksHelp) | |
return nil | |
} | |
res, err := Delete(account, "/tasks/"+args[1]) | |
if err != nil { | |
return err | |
} | |
defer res.Body.Close() | |
fmt.Println("Deleted task", args[1]) | |
break | |
default: | |
_, err := strconv.Atoi(args[0]) | |
if err != nil { | |
fmt.Println(tasksHelp) | |
return nil | |
} | |
res, err := Get(account, "/tasks/"+args[0]) | |
if err != nil { | |
return err | |
} | |
defer res.Body.Close() | |
task := new(Task) | |
decoder := json.NewDecoder(res.Body) | |
err = decoder.Decode(task) | |
if err != nil { | |
return err | |
} | |
fmt.Println(task) | |
} | |
return nil | |
} | |
func init() { | |
commands["help"] = &Command{helpHelp, help} | |
commands["account"] = &Command{accountHelp, account} | |
commands["tasks"] = &Command{tasksHelp, tasks} | |
} | |
func main() { | |
flag.Parse() | |
args := flag.Args() | |
if len(args) <= 0 { | |
fmt.Fprintln(os.Stderr, usage) | |
os.Exit(1) | |
} | |
cmd, ok := commands[args[0]] | |
if !ok { | |
fmt.Fprintln(os.Stderr, "Command", args[0], "not found") | |
os.Exit(1) | |
} | |
err := cmd.Run(args[1:]...) | |
if err != nil { | |
fmt.Fprintln(os.Stderr, err) | |
os.Exit(1) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment