Implement APIv4 infrastructure (#5191)
* Implement APIv4 infrastructure * Update parameter requirement functions per feedback
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
3e2f879b77
Коммит
c01d9ad6cf
@@ -40,7 +40,8 @@ const (
|
||||
|
||||
API_URL_SUFFIX_V1 = "/api/v1"
|
||||
API_URL_SUFFIX_V3 = "/api/v3"
|
||||
API_URL_SUFFIX = API_URL_SUFFIX_V3
|
||||
API_URL_SUFFIX_V4 = "/api/v4"
|
||||
API_URL_SUFFIX = API_URL_SUFFIX_V4
|
||||
)
|
||||
|
||||
type Result struct {
|
||||
@@ -71,7 +72,7 @@ type Client struct {
|
||||
// NewClient constructs a new client with convienence methods for talking to
|
||||
// the server.
|
||||
func NewClient(url string) *Client {
|
||||
return &Client{url, url + API_URL_SUFFIX, &http.Client{}, "", "", "", "", "", ""}
|
||||
return &Client{url, url + API_URL_SUFFIX_V3, &http.Client{}, "", "", "", "", "", ""}
|
||||
}
|
||||
|
||||
func closeBody(r *http.Response) {
|
||||
|
||||
222
model/client4.go
Обычный файл
222
model/client4.go
Обычный файл
@@ -0,0 +1,222 @@
|
||||
// Copyright (c) 2017 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Response struct {
|
||||
StatusCode int
|
||||
Error *AppError
|
||||
RequestId string
|
||||
Etag string
|
||||
ServerVersion string
|
||||
}
|
||||
|
||||
type Client4 struct {
|
||||
Url string // The location of the server, for example "http://localhost:8065"
|
||||
ApiUrl string // The api location of the server, for example "http://localhost:8065/api/v4"
|
||||
HttpClient *http.Client // The http client
|
||||
AuthToken string
|
||||
AuthType string
|
||||
}
|
||||
|
||||
func NewAPIv4Client(url string) *Client4 {
|
||||
return &Client4{url, url + API_URL_SUFFIX, &http.Client{}, "", ""}
|
||||
}
|
||||
|
||||
func BuildResponse(r *http.Response) *Response {
|
||||
return &Response{
|
||||
StatusCode: r.StatusCode,
|
||||
RequestId: r.Header.Get(HEADER_REQUEST_ID),
|
||||
Etag: r.Header.Get(HEADER_ETAG_SERVER),
|
||||
ServerVersion: r.Header.Get(HEADER_VERSION_ID),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client4) SetOAuthToken(token string) {
|
||||
c.AuthToken = token
|
||||
c.AuthType = HEADER_TOKEN
|
||||
}
|
||||
|
||||
func (c *Client4) ClearOAuthToken() {
|
||||
c.AuthToken = ""
|
||||
c.AuthType = HEADER_BEARER
|
||||
}
|
||||
|
||||
func (c *Client4) GetUsersRoute() string {
|
||||
return fmt.Sprintf("/users")
|
||||
}
|
||||
|
||||
func (c *Client4) GetUserRoute(userId string) string {
|
||||
return fmt.Sprintf(c.GetUsersRoute()+"/%v", userId)
|
||||
}
|
||||
|
||||
func (c *Client4) GetTeamsRoute() string {
|
||||
return fmt.Sprintf("/teams")
|
||||
}
|
||||
|
||||
func (c *Client4) GetTeamRoute(teamId string) string {
|
||||
return fmt.Sprintf(c.GetTeamsRoute()+"/%v", teamId)
|
||||
}
|
||||
|
||||
func (c *Client4) DoApiGet(url string, etag string) (*http.Response, *AppError) {
|
||||
return c.DoApiRequest(http.MethodGet, url, "", etag)
|
||||
}
|
||||
|
||||
func (c *Client4) DoApiPost(url string, data string) (*http.Response, *AppError) {
|
||||
return c.DoApiRequest(http.MethodPost, url, data, "")
|
||||
}
|
||||
|
||||
func (c *Client4) DoApiPut(url string, data string) (*http.Response, *AppError) {
|
||||
return c.DoApiRequest(http.MethodPut, url, data, "")
|
||||
}
|
||||
|
||||
func (c *Client4) DoApiDelete(url string, data string) (*http.Response, *AppError) {
|
||||
return c.DoApiRequest(http.MethodDelete, url, "", "")
|
||||
}
|
||||
|
||||
func (c *Client4) DoApiRequest(method, url, data, etag string) (*http.Response, *AppError) {
|
||||
rq, _ := http.NewRequest(method, c.ApiUrl+url, strings.NewReader(data))
|
||||
rq.Close = true
|
||||
|
||||
if len(etag) > 0 {
|
||||
rq.Header.Set(HEADER_ETAG_CLIENT, etag)
|
||||
}
|
||||
|
||||
if len(c.AuthToken) > 0 {
|
||||
rq.Header.Set(HEADER_AUTH, c.AuthType+" "+c.AuthToken)
|
||||
}
|
||||
|
||||
if rp, err := c.HttpClient.Do(rq); err != nil {
|
||||
return nil, NewLocAppError(url, "model.client.connecting.app_error", nil, err.Error())
|
||||
} else if rp.StatusCode == 304 {
|
||||
return rp, nil
|
||||
} else if rp.StatusCode >= 300 {
|
||||
defer closeBody(rp)
|
||||
return rp, AppErrorFromJson(rp.Body)
|
||||
} else {
|
||||
return rp, nil
|
||||
}
|
||||
}
|
||||
|
||||
// CheckStatusOK is a convenience function for checking the standard OK response
|
||||
// from the web service.
|
||||
func CheckStatusOK(r *http.Response) bool {
|
||||
m := MapFromJson(r.Body)
|
||||
defer closeBody(r)
|
||||
|
||||
if m != nil && m[STATUS] == STATUS_OK {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Authentication Section
|
||||
|
||||
// LoginById authenticates a user by user id and password.
|
||||
func (c *Client4) LoginById(id string, password string) (*User, *Response) {
|
||||
m := make(map[string]string)
|
||||
m["id"] = id
|
||||
m["password"] = password
|
||||
return c.login(m)
|
||||
}
|
||||
|
||||
// Login authenticates a user by login id, which can be username, email or some sort
|
||||
// of SSO identifier based on server configuration, and a password.
|
||||
func (c *Client4) Login(loginId string, password string) (*User, *Response) {
|
||||
m := make(map[string]string)
|
||||
m["login_id"] = loginId
|
||||
m["password"] = password
|
||||
return c.login(m)
|
||||
}
|
||||
|
||||
// LoginByLdap authenticates a user by LDAP id and password.
|
||||
func (c *Client4) LoginByLdap(loginId string, password string) (*User, *Response) {
|
||||
m := make(map[string]string)
|
||||
m["login_id"] = loginId
|
||||
m["password"] = password
|
||||
m["ldap_only"] = "true"
|
||||
return c.login(m)
|
||||
}
|
||||
|
||||
// LoginWithDevice authenticates a user by login id (username, email or some sort
|
||||
// of SSO identifier based on configuration), password and attaches a device id to
|
||||
// the session.
|
||||
func (c *Client4) LoginWithDevice(loginId string, password string, deviceId string) (*User, *Response) {
|
||||
m := make(map[string]string)
|
||||
m["login_id"] = loginId
|
||||
m["password"] = password
|
||||
m["device_id"] = deviceId
|
||||
return c.login(m)
|
||||
}
|
||||
|
||||
func (c *Client4) login(m map[string]string) (*User, *Response) {
|
||||
if r, err := c.DoApiPost("/users/login", MapToJson(m)); err != nil {
|
||||
return nil, &Response{StatusCode: r.StatusCode, Error: err}
|
||||
} else {
|
||||
c.AuthToken = r.Header.Get(HEADER_TOKEN)
|
||||
c.AuthType = HEADER_BEARER
|
||||
defer closeBody(r)
|
||||
return UserFromJson(r.Body), BuildResponse(r)
|
||||
}
|
||||
}
|
||||
|
||||
// Logout terminates the current user's session.
|
||||
func (c *Client4) Logout() (bool, *Response) {
|
||||
if r, err := c.DoApiPost("/users/logout", ""); err != nil {
|
||||
return false, &Response{StatusCode: r.StatusCode, Error: err}
|
||||
} else {
|
||||
c.AuthToken = ""
|
||||
c.AuthType = HEADER_BEARER
|
||||
|
||||
defer closeBody(r)
|
||||
return CheckStatusOK(r), BuildResponse(r)
|
||||
}
|
||||
}
|
||||
|
||||
// User Section
|
||||
|
||||
// CreateUser creates a user in the system based on the provided user struct.
|
||||
func (c *Client4) CreateUser(user *User) (*User, *Response) {
|
||||
if r, err := c.DoApiPost(c.GetUsersRoute(), user.ToJson()); err != nil {
|
||||
return nil, &Response{StatusCode: r.StatusCode, Error: err}
|
||||
} else {
|
||||
defer closeBody(r)
|
||||
return UserFromJson(r.Body), BuildResponse(r)
|
||||
}
|
||||
}
|
||||
|
||||
// GetUser returns a user based on the provided user id string.
|
||||
func (c *Client4) GetUser(userId, etag string) (*User, *Response) {
|
||||
if r, err := c.DoApiGet(c.GetUserRoute(userId), etag); err != nil {
|
||||
return nil, &Response{StatusCode: r.StatusCode, Error: err}
|
||||
} else {
|
||||
defer closeBody(r)
|
||||
return UserFromJson(r.Body), BuildResponse(r)
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateUser updates a user in the system based on the provided user struct.
|
||||
func (c *Client4) UpdateUser(user *User) (*User, *Response) {
|
||||
if r, err := c.DoApiPut(c.GetUserRoute(user.Id), user.ToJson()); err != nil {
|
||||
return nil, &Response{StatusCode: r.StatusCode, Error: err}
|
||||
} else {
|
||||
defer closeBody(r)
|
||||
return UserFromJson(r.Body), BuildResponse(r)
|
||||
}
|
||||
}
|
||||
|
||||
// Team Section
|
||||
// to be filled in..
|
||||
|
||||
// Channel Section
|
||||
// to be filled in..
|
||||
|
||||
// Post Section
|
||||
// to be filled in..
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
@@ -56,51 +57,51 @@ type User struct {
|
||||
func (u *User) IsValid() *AppError {
|
||||
|
||||
if len(u.Id) != 26 {
|
||||
return NewLocAppError("User.IsValid", "model.user.is_valid.id.app_error", nil, "")
|
||||
return NewAppError("User.IsValid", "model.user.is_valid.id.app_error", nil, "", http.StatusBadRequest)
|
||||
}
|
||||
|
||||
if u.CreateAt == 0 {
|
||||
return NewLocAppError("User.IsValid", "model.user.is_valid.create_at.app_error", nil, "user_id="+u.Id)
|
||||
return NewAppError("User.IsValid", "model.user.is_valid.create_at.app_error", nil, "user_id="+u.Id, http.StatusBadRequest)
|
||||
}
|
||||
|
||||
if u.UpdateAt == 0 {
|
||||
return NewLocAppError("User.IsValid", "model.user.is_valid.update_at.app_error", nil, "user_id="+u.Id)
|
||||
return NewAppError("User.IsValid", "model.user.is_valid.update_at.app_error", nil, "user_id="+u.Id, http.StatusBadRequest)
|
||||
}
|
||||
|
||||
if !IsValidUsername(u.Username) {
|
||||
return NewLocAppError("User.IsValid", "model.user.is_valid.username.app_error", nil, "user_id="+u.Id)
|
||||
return NewAppError("User.IsValid", "model.user.is_valid.username.app_error", nil, "user_id="+u.Id, http.StatusBadRequest)
|
||||
}
|
||||
|
||||
if len(u.Email) > 128 || len(u.Email) == 0 {
|
||||
return NewLocAppError("User.IsValid", "model.user.is_valid.email.app_error", nil, "user_id="+u.Id)
|
||||
return NewAppError("User.IsValid", "model.user.is_valid.email.app_error", nil, "user_id="+u.Id, http.StatusBadRequest)
|
||||
}
|
||||
|
||||
if utf8.RuneCountInString(u.Nickname) > 64 {
|
||||
return NewLocAppError("User.IsValid", "model.user.is_valid.nickname.app_error", nil, "user_id="+u.Id)
|
||||
return NewAppError("User.IsValid", "model.user.is_valid.nickname.app_error", nil, "user_id="+u.Id, http.StatusBadRequest)
|
||||
}
|
||||
|
||||
if utf8.RuneCountInString(u.Position) > 35 {
|
||||
return NewLocAppError("User.IsValid", "model.user.is_valid.position.app_error", nil, "user_id="+u.Id)
|
||||
return NewAppError("User.IsValid", "model.user.is_valid.position.app_error", nil, "user_id="+u.Id, http.StatusBadRequest)
|
||||
}
|
||||
|
||||
if utf8.RuneCountInString(u.FirstName) > 64 {
|
||||
return NewLocAppError("User.IsValid", "model.user.is_valid.first_name.app_error", nil, "user_id="+u.Id)
|
||||
return NewAppError("User.IsValid", "model.user.is_valid.first_name.app_error", nil, "user_id="+u.Id, http.StatusBadRequest)
|
||||
}
|
||||
|
||||
if utf8.RuneCountInString(u.LastName) > 64 {
|
||||
return NewLocAppError("User.IsValid", "model.user.is_valid.last_name.app_error", nil, "user_id="+u.Id)
|
||||
return NewAppError("User.IsValid", "model.user.is_valid.last_name.app_error", nil, "user_id="+u.Id, http.StatusBadRequest)
|
||||
}
|
||||
|
||||
if u.AuthData != nil && len(*u.AuthData) > 128 {
|
||||
return NewLocAppError("User.IsValid", "model.user.is_valid.auth_data.app_error", nil, "user_id="+u.Id)
|
||||
return NewAppError("User.IsValid", "model.user.is_valid.auth_data.app_error", nil, "user_id="+u.Id, http.StatusBadRequest)
|
||||
}
|
||||
|
||||
if u.AuthData != nil && len(*u.AuthData) > 0 && len(u.AuthService) == 0 {
|
||||
return NewLocAppError("User.IsValid", "model.user.is_valid.auth_data_type.app_error", nil, "user_id="+u.Id)
|
||||
return NewAppError("User.IsValid", "model.user.is_valid.auth_data_type.app_error", nil, "user_id="+u.Id, http.StatusBadRequest)
|
||||
}
|
||||
|
||||
if len(u.Password) > 0 && u.AuthData != nil && len(*u.AuthData) > 0 {
|
||||
return NewLocAppError("User.IsValid", "model.user.is_valid.auth_data_pwd.app_error", nil, "user_id="+u.Id)
|
||||
return NewAppError("User.IsValid", "model.user.is_valid.auth_data_pwd.app_error", nil, "user_id="+u.Id, http.StatusBadRequest)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -93,6 +93,18 @@ func AppErrorFromJson(data io.Reader) *AppError {
|
||||
}
|
||||
}
|
||||
|
||||
func NewAppError(where string, id string, params map[string]interface{}, details string, status int) *AppError {
|
||||
ap := &AppError{}
|
||||
ap.Id = id
|
||||
ap.params = params
|
||||
ap.Message = id
|
||||
ap.Where = where
|
||||
ap.DetailedError = details
|
||||
ap.StatusCode = status
|
||||
ap.IsOAuth = false
|
||||
return ap
|
||||
}
|
||||
|
||||
func NewLocAppError(where string, id string, params map[string]interface{}, details string) *AppError {
|
||||
ap := &AppError{}
|
||||
ap.Id = id
|
||||
|
||||
@@ -26,14 +26,14 @@ type WebSocketClient struct {
|
||||
// NewWebSocketClient constructs a new WebSocket client with convienence
|
||||
// methods for talking to the server.
|
||||
func NewWebSocketClient(url, authToken string) (*WebSocketClient, *AppError) {
|
||||
conn, _, err := websocket.DefaultDialer.Dial(url+API_URL_SUFFIX+"/users/websocket", nil)
|
||||
conn, _, err := websocket.DefaultDialer.Dial(url+API_URL_SUFFIX_V3+"/users/websocket", nil)
|
||||
if err != nil {
|
||||
return nil, NewLocAppError("NewWebSocketClient", "model.websocket_client.connect_fail.app_error", nil, err.Error())
|
||||
}
|
||||
|
||||
client := &WebSocketClient{
|
||||
url,
|
||||
url + API_URL_SUFFIX,
|
||||
url + API_URL_SUFFIX_V3,
|
||||
conn,
|
||||
authToken,
|
||||
1,
|
||||
|
||||
Ссылка в новой задаче
Block a user