Skip to content

Commit

Permalink
Refactor error management + user_resources call
Browse files Browse the repository at this point in the history
  • Loading branch information
mremond committed Jul 31, 2016
1 parent 5cf6616 commit 8f62eb0
Show file tree
Hide file tree
Showing 4 changed files with 150 additions and 21 deletions.
97 changes: 86 additions & 11 deletions api.go
Expand Up @@ -4,6 +4,7 @@ import (
"encoding/json"
"fmt"
"net/url"
"strings"
)

// Response is the common interface for all ejabberd API call results.
Expand Down Expand Up @@ -95,7 +96,7 @@ func (s statsRequest) parseResponse(body []byte) (Response, error) {
var resp Stats
err := json.Unmarshal(body, &resp)
if err != nil {
return Error{Code: 99, Message: "Cannot parse JSON response"}, err
return resp, APIError{Code: 99, Message: err.Error()}
}
resp.Name = s.Name
return resp, err
Expand Down Expand Up @@ -163,7 +164,7 @@ func (r registerRequest) parseResponse(body []byte) (Response, error) {
var resp Register
err := json.Unmarshal(body, &resp)
if err != nil {
return Error{Code: 99, Message: "Cannot parse JSON response"}, err
return resp, APIError{Code: 99, Message: err.Error()}
}
return resp, nil
}
Expand Down Expand Up @@ -229,7 +230,7 @@ func (o offlineCountRequest) parseResponse(body []byte) (Response, error) {
var resp OfflineCount
err := json.Unmarshal(body, &resp)
if err != nil {
return Error{Code: 99, Message: "Cannot parse JSON response"}, err
return resp, APIError{Code: 99, Message: err.Error()}
}
resp.Name = "offline_count"
resp.JID = o.JID
Expand All @@ -242,30 +243,104 @@ func (o OfflineCount) String() string {

//==============================================================================

// Error represents ejabberd error returned by the server as result of
// ejabberd API calls.
type Error struct {
// UserResources contains the result of the call to ejabberd
// user_resources API.
type UserResources struct {
JID string `json:"jid"`
Resources []string `json:"resources"`
}

// JSON represents UserResources as a JSON string, for further
// processing with other tools.
func (u UserResources) JSON() string {
body, _ := json.Marshal(u)
return string(body)
}

type userResourcesRequest struct {
JID string `json:"jid"`
}

func (u userResourcesRequest) params() (apiParams, error) {
var query url.Values
jid, err := parseJID(u.JID)
if err != nil {
return apiParams{}, err
}

type userResources struct {
User string `json:"user"`
Server string `json:"server"`
}

data := userResources{
User: jid.username,
Server: jid.domain,
}

body, err := json.Marshal(data)
if err != nil {
return apiParams{}, err
}

if err != nil {
return apiParams{}, err
}

return apiParams{
name: "user_resources",
version: 1,

method: "POST",
query: query,
body: body,
}, nil
}

func (u userResourcesRequest) parseResponse(body []byte) (Response, error) {
var resp UserResources

var data []string
err := json.Unmarshal(body, &data)
if err != nil {
return resp, APIError{Code: 99, Message: err.Error()}
}
resp.JID = u.JID
resp.Resources = data
return resp, nil
}

func (u UserResources) String() string {
resources := strings.Join(u.Resources, ",")
return fmt.Sprintf("%s", resources)
}

//==============================================================================

// APIError represents ejabberd error returned by the server as result
// of ejabberd API calls.
type APIError struct {
Status string `json:"status"`
Code int `json:"code"`
Message string `json:"message"`
}

func parseError(body []byte) (Error, error) {
var resp Error
func parseError(body []byte) (APIError, error) {
var resp APIError
err := json.Unmarshal(body, &resp)
if err != nil {
return Error{Code: 99, Message: "Cannot parse JSON response"}, err
return resp, APIError{Code: 99, Message: err.Error()}
}
return resp, nil
}

// JSON represents ejabberd error response as a JSON string, for further
// processing with other tools.
func (e Error) JSON() string {
func (e APIError) JSON() string {
body, _ := json.Marshal(e)
return string(body)
}

func (e Error) String() string {
func (e APIError) Error() string {
return fmt.Sprintf("Error %d: %s", e.Code, e.Message)
}
29 changes: 27 additions & 2 deletions client.go
Expand Up @@ -32,11 +32,15 @@ type Client struct {
func (c Client) call(req request) (Response, error) {
code, result, err := c.callRaw(req)
if err != nil {
return Error{Code: 99}, err
return APIError{Code: 99}, err
}

if code != 200 {
return parseError(result)
apiError, err := parseError(result)
if err != nil {
return nil, err
}
return nil, apiError
}

return req.parseResponse(result)
Expand All @@ -63,6 +67,7 @@ func (c Client) callRaw(req request) (int, []byte, error) {
if p.admin {
r.Header.Set("X-Admin", "true")
} else if needAdminForUser(req, c.Token.JID) {
fmt.Println("MREMOND Set admin")
r.Header.Set("X-Admin", "true")
}
r.Header.Set("Content-Type", "application/json")
Expand Down Expand Up @@ -218,6 +223,26 @@ func (c Client) GetOfflineCount(bareJID string) (OfflineCount, error) {

//==============================================================================

// UserResources returns the list of resources connected for a given
// user. It can be called as a user, if you try to read your own
// connected resources. It can also be called as an admin and in that
// case, you can read the connected resources for any any user on the
// server.
func (c Client) UserResources(bareJID string) (UserResources, error) {
command := userResourcesRequest{
JID: bareJID,
}

result, err := c.call(command)
if err != nil {
return UserResources{}, err
}
resp := result.(UserResources)
return resp, nil
}

//==============================================================================

// Prepare HTTP client settings with proper values, like default
// timeout.
func defaultHTTPClient(timeout time.Duration) *http.Client {
Expand Down
9 changes: 9 additions & 0 deletions client_test.go
@@ -1,6 +1,7 @@
package ejabberd_test

import (
"flag"
"fmt"
"net/http"
"net/http/httptest"
Expand Down Expand Up @@ -50,6 +51,12 @@ func ExampleClient_GetToken() {
}

func ExampleClient_Stats() {
if flag.Lookup("test.v") == nil {
fmt.Println("normal run")
} else {
fmt.Println("run under go test")
}

t := ejabberd.OAuthToken{AccessToken: "XjlJg0KF2wagT0A5dcYghePl8npsiEic"}
client := ejabberd.Client{BaseURL: "http://localhost:5281", Token: t}

Expand All @@ -58,4 +65,6 @@ func ExampleClient_Stats() {
} else {
fmt.Println(stats.Name, stats.Value)
}
// Output:
// 1
}
36 changes: 28 additions & 8 deletions cmd/ejabberd/main.go
Expand Up @@ -27,11 +27,15 @@ var (
stats = app.Command("stats", "Get ejabberd statistics.")
statsName = stats.Arg("name", "Name of stats to query.").Required().String()

// ========= admin =========
register = app.Command("register", "Create a new user.")
registerJID = register.Flag("jid", "JID of the user to create.").Short('j').Required().String()
registerPassword = register.Flag("password", "Password to set for created user.").Short('p').Required().String()

// ========= user =========
user = app.Command("user", "Operations to perform on users.")
userOperation = user.Arg("operation", "Operation").Required().Enum("register")
userJID = user.Flag("jid", "JID of the user to perform operation on.").Short('j').Required().String()
userPassword = user.Flag("password", "User password").Short('p').Required().String()
userOperation = user.Arg("operation", "Operation").Required().Enum("resources")
userJID = user.Flag("jid", "JID of the user to perform operation on.").Short('j').String()

// ========= offline =========
offline = app.Command("offline", "Operations to perform on offline store.")
Expand Down Expand Up @@ -68,6 +72,8 @@ func execute(command string) {

var resp ejabberd.Response
switch command {
case register.FullCommand():
resp = registerCommand(c, *registerJID, *registerPassword)
case stats.FullCommand():
resp = statsCommand(c)
case user.FullCommand():
Expand Down Expand Up @@ -106,6 +112,16 @@ func getToken() {

//==============================================================================

func registerCommand(c ejabberd.Client, j, p string) ejabberd.Response {
resp, err := c.RegisterUser(j, p)
if err != nil {
kingpin.Fatalf("user registration error for %s: %s", j, err)
}
return resp
}

//==============================================================================

func statsCommand(c ejabberd.Client) ejabberd.Response {
resp, err := c.Stats(*statsName)
if err != nil {
Expand All @@ -119,16 +135,20 @@ func statsCommand(c ejabberd.Client) ejabberd.Response {
func userCommand(c ejabberd.Client, op string) ejabberd.Response {
var resp ejabberd.Response
switch op {
case "register":
resp = registerCommand(c, *userJID, *userPassword)
case "resources":
resp = resourcesCommand(c, *userJID)
}
return resp
}

func registerCommand(c ejabberd.Client, j, p string) ejabberd.Response {
resp, err := c.RegisterUser(j, p)
func resourcesCommand(c ejabberd.Client, jid string) ejabberd.Response {
if jid == "" {
jid = c.Token.JID
}

resp, err := c.UserResources(jid)
if err != nil {
kingpin.Fatalf("user register error for %s: %s", j, err)
kingpin.Fatalf("%s: %s", jid, err)
}
return resp
}
Expand Down

0 comments on commit 8f62eb0

Please sign in to comment.