Skip to content

Instantly share code, notes, and snippets.

@larzconwell
Created October 11, 2013 20:02
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save larzconwell/6941140 to your computer and use it in GitHub Desktop.
Save larzconwell/6941140 to your computer and use it in GitHub Desktop.
Moln Client example
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