mirror of
https://github.com/elyby/chrly.git
synced 2024-12-22 21:19:55 +05:30
Removed amqp worker command implementation
Removed Accounts Ely.by api implementation
This commit is contained in:
parent
f5f8fbc65e
commit
855302ec60
20
Gopkg.lock
generated
20
Gopkg.lock
generated
@ -7,12 +7,6 @@
|
|||||||
revision = "f6df55f235c24f236d11dbcf665249a59ac2021f"
|
revision = "f6df55f235c24f236d11dbcf665249a59ac2021f"
|
||||||
version = "1.1"
|
version = "1.1"
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
name = "github.com/assembla/cony"
|
|
||||||
packages = ["."]
|
|
||||||
revision = "dd62697b0adb9adfda8589520cb85f4cbc2361f1"
|
|
||||||
version = "v0.3.2"
|
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
name = "github.com/certifi/gocertifi"
|
name = "github.com/certifi/gocertifi"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
@ -169,12 +163,6 @@
|
|||||||
revision = "25b30aa063fc18e48662b86996252eabdcf2f0c7"
|
revision = "25b30aa063fc18e48662b86996252eabdcf2f0c7"
|
||||||
version = "v1.0.0"
|
version = "v1.0.0"
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
branch = "master"
|
|
||||||
name = "github.com/streadway/amqp"
|
|
||||||
packages = ["."]
|
|
||||||
revision = "2cbfe40c9341ad63ba23e53013b3ddc7989d801c"
|
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
name = "github.com/stretchr/testify"
|
name = "github.com/stretchr/testify"
|
||||||
packages = ["assert"]
|
packages = ["assert"]
|
||||||
@ -206,12 +194,6 @@
|
|||||||
packages = ["internal/gen","internal/triegen","internal/ucd","transform","unicode/cldr","unicode/norm"]
|
packages = ["internal/gen","internal/triegen","internal/ucd","transform","unicode/cldr","unicode/norm"]
|
||||||
revision = "bd91bbf73e9a4a801adbfb97133c992678533126"
|
revision = "bd91bbf73e9a4a801adbfb97133c992678533126"
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
name = "gopkg.in/h2non/gock.v1"
|
|
||||||
packages = ["."]
|
|
||||||
revision = "84d599244901620fb3eb96473eb9e50619f69b47"
|
|
||||||
version = "v1.0.6"
|
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "v2"
|
branch = "v2"
|
||||||
name = "gopkg.in/yaml.v2"
|
name = "gopkg.in/yaml.v2"
|
||||||
@ -221,6 +203,6 @@
|
|||||||
[solve-meta]
|
[solve-meta]
|
||||||
analyzer-name = "dep"
|
analyzer-name = "dep"
|
||||||
analyzer-version = 1
|
analyzer-version = 1
|
||||||
inputs-digest = "b7c6dd9fffc543dc24b5832c7767632e4c066189be7c40868ba5612f5f45dc64"
|
inputs-digest = "b85cbbca8b4283a0977ee92789c9beee468f2d355da5dfa28a4176934548f6f3"
|
||||||
solver-name = "gps-cdcl"
|
solver-name = "gps-cdcl"
|
||||||
solver-version = 1
|
solver-version = 1
|
||||||
|
@ -19,10 +19,6 @@ ignored = ["elyby/minecraft-skinsystem"]
|
|||||||
[[constraint]]
|
[[constraint]]
|
||||||
name = "github.com/getsentry/raven-go"
|
name = "github.com/getsentry/raven-go"
|
||||||
|
|
||||||
[[constraint]]
|
|
||||||
name = "github.com/assembla/cony"
|
|
||||||
version = "^0.3.2"
|
|
||||||
|
|
||||||
[[constraint]]
|
[[constraint]]
|
||||||
name = "github.com/SermoDigital/jose"
|
name = "github.com/SermoDigital/jose"
|
||||||
version = "~1.1.0"
|
version = "~1.1.0"
|
||||||
|
@ -1,166 +0,0 @@
|
|||||||
package accounts
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"path"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Config struct {
|
|
||||||
Addr string
|
|
||||||
Id string
|
|
||||||
Secret string
|
|
||||||
Scopes []string
|
|
||||||
|
|
||||||
Client *http.Client
|
|
||||||
}
|
|
||||||
|
|
||||||
type Token struct {
|
|
||||||
AccessToken string `json:"access_token"`
|
|
||||||
TokenType string `json:"token_type"`
|
|
||||||
ExpiresIn int `json:"expires_in"`
|
|
||||||
config *Config
|
|
||||||
}
|
|
||||||
|
|
||||||
func (config *Config) GetToken() (*Token, error) {
|
|
||||||
form := url.Values{}
|
|
||||||
form.Add("client_id", config.Id)
|
|
||||||
form.Add("client_secret", config.Secret)
|
|
||||||
form.Add("grant_type", "client_credentials")
|
|
||||||
form.Add("scope", strings.Join(config.Scopes, ","))
|
|
||||||
|
|
||||||
response, err := config.getHttpClient().Post(config.getTokenUrl(), "application/x-www-form-urlencoded", strings.NewReader(form.Encode()))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer response.Body.Close()
|
|
||||||
|
|
||||||
var result *Token
|
|
||||||
responseError := handleResponse(response)
|
|
||||||
if responseError != nil {
|
|
||||||
return nil, responseError
|
|
||||||
}
|
|
||||||
|
|
||||||
body, _ := ioutil.ReadAll(response.Body)
|
|
||||||
unmarshalError := json.Unmarshal(body, &result)
|
|
||||||
if unmarshalError != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
result.config = config
|
|
||||||
|
|
||||||
return result, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (config *Config) getTokenUrl() string {
|
|
||||||
return concatenateHostAndPath(config.Addr, "/api/oauth2/v1/token")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (config *Config) getHttpClient() *http.Client {
|
|
||||||
if config.Client == nil {
|
|
||||||
config.Client = &http.Client{}
|
|
||||||
}
|
|
||||||
|
|
||||||
return config.Client
|
|
||||||
}
|
|
||||||
|
|
||||||
type AccountInfoResponse struct {
|
|
||||||
Id int `json:"id"`
|
|
||||||
Uuid string `json:"uuid"`
|
|
||||||
Username string `json:"username"`
|
|
||||||
Email string `json:"email"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (token *Token) AccountInfo(attribute string, value string) (*AccountInfoResponse, error) {
|
|
||||||
request := token.newRequest("GET", token.accountInfoUrl(), nil)
|
|
||||||
|
|
||||||
query := request.URL.Query()
|
|
||||||
query.Add(attribute, value)
|
|
||||||
request.URL.RawQuery = query.Encode()
|
|
||||||
|
|
||||||
response, err := token.config.Client.Do(request)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer response.Body.Close()
|
|
||||||
|
|
||||||
var info *AccountInfoResponse
|
|
||||||
|
|
||||||
responseError := handleResponse(response)
|
|
||||||
if responseError != nil {
|
|
||||||
return nil, responseError
|
|
||||||
}
|
|
||||||
|
|
||||||
body, _ := ioutil.ReadAll(response.Body)
|
|
||||||
json.Unmarshal(body, &info)
|
|
||||||
|
|
||||||
return info, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (token *Token) accountInfoUrl() string {
|
|
||||||
return concatenateHostAndPath(token.config.Addr, "/api/internal/accounts/info")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (token *Token) newRequest(method string, urlStr string, body io.Reader) *http.Request {
|
|
||||||
request, err := http.NewRequest(method, urlStr, body)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
request.Header.Add("Authorization", "Bearer " + token.AccessToken)
|
|
||||||
|
|
||||||
return request
|
|
||||||
}
|
|
||||||
|
|
||||||
func concatenateHostAndPath(host string, pathToJoin string) string {
|
|
||||||
u, _ := url.Parse(host)
|
|
||||||
u.Path = path.Join(u.Path, pathToJoin)
|
|
||||||
|
|
||||||
return u.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
type UnauthorizedResponse struct {}
|
|
||||||
|
|
||||||
func (err UnauthorizedResponse) Error() string {
|
|
||||||
return "Unauthorized response"
|
|
||||||
}
|
|
||||||
|
|
||||||
type ForbiddenResponse struct {}
|
|
||||||
|
|
||||||
func (err ForbiddenResponse) Error() string {
|
|
||||||
return "Forbidden response"
|
|
||||||
}
|
|
||||||
|
|
||||||
type NotFoundResponse struct {}
|
|
||||||
|
|
||||||
func (err NotFoundResponse) Error() string {
|
|
||||||
return "Not found"
|
|
||||||
}
|
|
||||||
|
|
||||||
type NotSuccessResponse struct {
|
|
||||||
StatusCode int
|
|
||||||
}
|
|
||||||
|
|
||||||
func (err NotSuccessResponse) Error() string {
|
|
||||||
return fmt.Sprintf("Response code is \"%d\"", err.StatusCode)
|
|
||||||
}
|
|
||||||
|
|
||||||
func handleResponse(response *http.Response) error {
|
|
||||||
switch status := response.StatusCode; status {
|
|
||||||
case 200:
|
|
||||||
return nil
|
|
||||||
case 401:
|
|
||||||
return &UnauthorizedResponse{}
|
|
||||||
case 403:
|
|
||||||
return &ForbiddenResponse{}
|
|
||||||
case 404:
|
|
||||||
return &NotFoundResponse{}
|
|
||||||
default:
|
|
||||||
return &NotSuccessResponse{status}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,98 +0,0 @@
|
|||||||
package accounts
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
testify "github.com/stretchr/testify/assert"
|
|
||||||
"gopkg.in/h2non/gock.v1"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestConfig_GetToken(t *testing.T) {
|
|
||||||
assert := testify.New(t)
|
|
||||||
|
|
||||||
defer gock.Off()
|
|
||||||
gock.New("https://account.ely.by").
|
|
||||||
Post("/api/oauth2/v1/token").
|
|
||||||
Body(strings.NewReader("client_id=mock-id&client_secret=mock-secret&grant_type=client_credentials&scope=scope1%2Cscope2")).
|
|
||||||
Reply(200).
|
|
||||||
JSON(map[string]interface{}{
|
|
||||||
"access_token": "mocked-token",
|
|
||||||
"token_type": "Bearer",
|
|
||||||
"expires_in": 86400,
|
|
||||||
})
|
|
||||||
|
|
||||||
client := &http.Client{}
|
|
||||||
gock.InterceptClient(client)
|
|
||||||
|
|
||||||
config := &Config{
|
|
||||||
Addr: "https://account.ely.by",
|
|
||||||
Id: "mock-id",
|
|
||||||
Secret: "mock-secret",
|
|
||||||
Scopes: []string{"scope1", "scope2"},
|
|
||||||
Client: client,
|
|
||||||
}
|
|
||||||
|
|
||||||
result, err := config.GetToken()
|
|
||||||
if assert.NoError(err) {
|
|
||||||
assert.Equal("mocked-token", result.AccessToken)
|
|
||||||
assert.Equal("Bearer", result.TokenType)
|
|
||||||
assert.Equal(86400, result.ExpiresIn)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestToken_AccountInfo(t *testing.T) {
|
|
||||||
assert := testify.New(t)
|
|
||||||
|
|
||||||
defer gock.Off()
|
|
||||||
// To test valid behavior
|
|
||||||
gock.New("https://account.ely.by").
|
|
||||||
Get("/api/internal/accounts/info").
|
|
||||||
MatchParam("id", "1").
|
|
||||||
MatchHeader("Authorization", "Bearer mock-token").
|
|
||||||
Reply(200).
|
|
||||||
JSON(map[string]interface{}{
|
|
||||||
"id": 1,
|
|
||||||
"uuid": "0f657aa8-bfbe-415d-b700-5750090d3af3",
|
|
||||||
"username": "dummy",
|
|
||||||
"email": "dummy@ely.by",
|
|
||||||
})
|
|
||||||
|
|
||||||
// To test behavior on invalid or expired token
|
|
||||||
gock.New("https://account.ely.by").
|
|
||||||
Get("/api/internal/accounts/info").
|
|
||||||
MatchParam("id", "1").
|
|
||||||
MatchHeader("Authorization", "Bearer mock-token").
|
|
||||||
Reply(401).
|
|
||||||
JSON(map[string]interface{}{
|
|
||||||
"name": "Unauthorized",
|
|
||||||
"message": "Incorrect token",
|
|
||||||
"code": 0,
|
|
||||||
"status": 401,
|
|
||||||
})
|
|
||||||
|
|
||||||
client := &http.Client{}
|
|
||||||
gock.InterceptClient(client)
|
|
||||||
|
|
||||||
token := &Token{
|
|
||||||
AccessToken: "mock-token",
|
|
||||||
config: &Config{
|
|
||||||
Addr: "https://account.ely.by",
|
|
||||||
Client: client,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
result, err := token.AccountInfo("id", "1")
|
|
||||||
if assert.NoError(err) {
|
|
||||||
assert.Equal(1, result.Id)
|
|
||||||
assert.Equal("0f657aa8-bfbe-415d-b700-5750090d3af3", result.Uuid)
|
|
||||||
assert.Equal("dummy", result.Username)
|
|
||||||
assert.Equal("dummy@ely.by", result.Email)
|
|
||||||
}
|
|
||||||
|
|
||||||
result2, err2 := token.AccountInfo("id", "1")
|
|
||||||
assert.Nil(result2)
|
|
||||||
assert.Error(err2)
|
|
||||||
assert.IsType(&UnauthorizedResponse{}, err2)
|
|
||||||
}
|
|
@ -1,56 +0,0 @@
|
|||||||
package accounts
|
|
||||||
|
|
||||||
type AutoRefresh struct {
|
|
||||||
token *Token
|
|
||||||
config *Config
|
|
||||||
repeatsCount int
|
|
||||||
}
|
|
||||||
|
|
||||||
const repeatsLimit = 3
|
|
||||||
|
|
||||||
func (config *Config) GetTokenWithAutoRefresh() *AutoRefresh {
|
|
||||||
return &AutoRefresh{
|
|
||||||
config: config,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (refresher *AutoRefresh) AccountInfo(attribute string, value string) (*AccountInfoResponse, error) {
|
|
||||||
defer refresher.resetRepeatsCount()
|
|
||||||
|
|
||||||
apiToken, err := refresher.getToken()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
result, err := apiToken.AccountInfo(attribute, value)
|
|
||||||
if err != nil {
|
|
||||||
_, isTokenExpire := err.(*UnauthorizedResponse)
|
|
||||||
if !isTokenExpire || refresher.repeatsCount >= repeatsLimit - 1 {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
refresher.repeatsCount++
|
|
||||||
refresher.token = nil
|
|
||||||
|
|
||||||
return refresher.AccountInfo(attribute, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
return result, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (refresher *AutoRefresh) getToken() (*Token, error) {
|
|
||||||
if refresher.token == nil {
|
|
||||||
newToken, err := refresher.config.GetToken()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
refresher.token = newToken
|
|
||||||
}
|
|
||||||
|
|
||||||
return refresher.token, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (refresher *AutoRefresh) resetRepeatsCount() {
|
|
||||||
refresher.repeatsCount = 0
|
|
||||||
}
|
|
@ -1,242 +0,0 @@
|
|||||||
package accounts
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
testify "github.com/stretchr/testify/assert"
|
|
||||||
"gopkg.in/h2non/gock.v1"
|
|
||||||
)
|
|
||||||
|
|
||||||
var config = &Config{
|
|
||||||
Addr: "https://account.ely.by",
|
|
||||||
Id: "mock-id",
|
|
||||||
Secret: "mock-secret",
|
|
||||||
Scopes: []string{"scope1", "scope2"},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestConfig_GetTokenWithAutoRefresh(t *testing.T) {
|
|
||||||
assert := testify.New(t)
|
|
||||||
|
|
||||||
testConfig := &Config{}
|
|
||||||
*testConfig = *config
|
|
||||||
|
|
||||||
result := testConfig.GetTokenWithAutoRefresh()
|
|
||||||
assert.Equal(testConfig, result.config)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAutoRefresh_AccountInfo(t *testing.T) {
|
|
||||||
assert := testify.New(t)
|
|
||||||
|
|
||||||
defer gock.Off()
|
|
||||||
gock.New("https://account.ely.by").
|
|
||||||
Post("/api/oauth2/v1/token").
|
|
||||||
Body(strings.NewReader("client_id=mock-id&client_secret=mock-secret&grant_type=client_credentials&scope=scope1%2Cscope2")).
|
|
||||||
Reply(200).
|
|
||||||
JSON(map[string]interface{}{
|
|
||||||
"access_token": "mocked-token",
|
|
||||||
"token_type": "Bearer",
|
|
||||||
"expires_in": 86400,
|
|
||||||
})
|
|
||||||
|
|
||||||
gock.New("https://account.ely.by").
|
|
||||||
Get("/api/internal/accounts/info").
|
|
||||||
Times(2).
|
|
||||||
MatchParam("id", "1").
|
|
||||||
MatchHeader("Authorization", "Bearer mocked-token").
|
|
||||||
Reply(200).
|
|
||||||
JSON(map[string]interface{}{
|
|
||||||
"id": 1,
|
|
||||||
"uuid": "0f657aa8-bfbe-415d-b700-5750090d3af3",
|
|
||||||
"username": "dummy",
|
|
||||||
"email": "dummy@ely.by",
|
|
||||||
})
|
|
||||||
|
|
||||||
client := &http.Client{}
|
|
||||||
gock.InterceptClient(client)
|
|
||||||
|
|
||||||
testConfig := &Config{}
|
|
||||||
*testConfig = *config
|
|
||||||
testConfig.Client = client
|
|
||||||
|
|
||||||
autoRefresher := testConfig.GetTokenWithAutoRefresh()
|
|
||||||
result, err := autoRefresher.AccountInfo("id", "1")
|
|
||||||
if assert.NoError(err) {
|
|
||||||
assert.Equal(1, result.Id)
|
|
||||||
assert.Equal("0f657aa8-bfbe-415d-b700-5750090d3af3", result.Uuid)
|
|
||||||
assert.Equal("dummy", result.Username)
|
|
||||||
assert.Equal("dummy@ely.by", result.Email)
|
|
||||||
}
|
|
||||||
|
|
||||||
result2, err2 := autoRefresher.AccountInfo("id", "1")
|
|
||||||
if assert.NoError(err2) {
|
|
||||||
assert.Equal(result, result2, "Results should still be same without token refreshing")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAutoRefresh_AccountInfo2(t *testing.T) {
|
|
||||||
assert := testify.New(t)
|
|
||||||
|
|
||||||
defer gock.Off()
|
|
||||||
gock.New("https://account.ely.by").
|
|
||||||
Post("/api/oauth2/v1/token").
|
|
||||||
Body(strings.NewReader("client_id=mock-id&client_secret=mock-secret&grant_type=client_credentials&scope=scope1%2Cscope2")).
|
|
||||||
Reply(200).
|
|
||||||
JSON(map[string]interface{}{
|
|
||||||
"access_token": "mocked-token-1",
|
|
||||||
"token_type": "Bearer",
|
|
||||||
"expires_in": 86400,
|
|
||||||
})
|
|
||||||
|
|
||||||
gock.New("https://account.ely.by").
|
|
||||||
Get("/api/internal/accounts/info").
|
|
||||||
MatchParam("id", "1").
|
|
||||||
MatchHeader("Authorization", "Bearer mocked-token-1").
|
|
||||||
Reply(200).
|
|
||||||
JSON(map[string]interface{}{
|
|
||||||
"id": 1,
|
|
||||||
"uuid": "0f657aa8-bfbe-415d-b700-5750090d3af3",
|
|
||||||
"username": "dummy",
|
|
||||||
"email": "dummy@ely.by",
|
|
||||||
})
|
|
||||||
|
|
||||||
gock.New("https://account.ely.by").
|
|
||||||
Get("/api/internal/accounts/info").
|
|
||||||
MatchParam("id", "1").
|
|
||||||
MatchHeader("Authorization", "Bearer mocked-token-1").
|
|
||||||
Reply(401).
|
|
||||||
JSON(map[string]interface{}{
|
|
||||||
"name": "Unauthorized",
|
|
||||||
"message": "Incorrect token",
|
|
||||||
"code": 0,
|
|
||||||
"status": 401,
|
|
||||||
})
|
|
||||||
|
|
||||||
gock.New("https://account.ely.by").
|
|
||||||
Post("/api/oauth2/v1/token").
|
|
||||||
Body(strings.NewReader("client_id=mock-id&client_secret=mock-secret&grant_type=client_credentials&scope=scope1%2Cscope2")).
|
|
||||||
Reply(200).
|
|
||||||
JSON(map[string]interface{}{
|
|
||||||
"access_token": "mocked-token-2",
|
|
||||||
"token_type": "Bearer",
|
|
||||||
"expires_in": 86400,
|
|
||||||
})
|
|
||||||
|
|
||||||
gock.New("https://account.ely.by").
|
|
||||||
Get("/api/internal/accounts/info").
|
|
||||||
MatchParam("id", "1").
|
|
||||||
MatchHeader("Authorization", "Bearer mocked-token-2").
|
|
||||||
Reply(200).
|
|
||||||
JSON(map[string]interface{}{
|
|
||||||
"id": 1,
|
|
||||||
"uuid": "0f657aa8-bfbe-415d-b700-5750090d3af3",
|
|
||||||
"username": "dummy",
|
|
||||||
"email": "dummy@ely.by",
|
|
||||||
})
|
|
||||||
|
|
||||||
client := &http.Client{}
|
|
||||||
gock.InterceptClient(client)
|
|
||||||
|
|
||||||
testConfig := &Config{}
|
|
||||||
*testConfig = *config
|
|
||||||
testConfig.Client = client
|
|
||||||
|
|
||||||
autoRefresher := testConfig.GetTokenWithAutoRefresh()
|
|
||||||
result, err := autoRefresher.AccountInfo("id", "1")
|
|
||||||
if assert.NoError(err) {
|
|
||||||
assert.Equal(1, result.Id)
|
|
||||||
assert.Equal("0f657aa8-bfbe-415d-b700-5750090d3af3", result.Uuid)
|
|
||||||
assert.Equal("dummy", result.Username)
|
|
||||||
assert.Equal("dummy@ely.by", result.Email)
|
|
||||||
}
|
|
||||||
|
|
||||||
result2, err2 := autoRefresher.AccountInfo("id", "1")
|
|
||||||
if assert.NoError(err2) {
|
|
||||||
assert.Equal(result, result2, "Results should still be same with refreshed token")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAutoRefresh_AccountInfo3(t *testing.T) {
|
|
||||||
assert := testify.New(t)
|
|
||||||
|
|
||||||
defer gock.Off()
|
|
||||||
gock.New("https://account.ely.by").
|
|
||||||
Post("/api/oauth2/v1/token").
|
|
||||||
Body(strings.NewReader("client_id=mock-id&client_secret=mock-secret&grant_type=client_credentials&scope=scope1%2Cscope2")).
|
|
||||||
Reply(200).
|
|
||||||
JSON(map[string]interface{}{
|
|
||||||
"access_token": "mocked-token-1",
|
|
||||||
"token_type": "Bearer",
|
|
||||||
"expires_in": 86400,
|
|
||||||
})
|
|
||||||
|
|
||||||
gock.New("https://account.ely.by").
|
|
||||||
Get("/api/internal/accounts/info").
|
|
||||||
MatchParam("id", "1").
|
|
||||||
MatchHeader("Authorization", "Bearer mocked-token-1").
|
|
||||||
Reply(404).
|
|
||||||
JSON(map[string]interface{}{
|
|
||||||
"name": "Not Found",
|
|
||||||
"message": "Page not found.",
|
|
||||||
"code": 0,
|
|
||||||
"status": 404,
|
|
||||||
})
|
|
||||||
|
|
||||||
client := &http.Client{}
|
|
||||||
gock.InterceptClient(client)
|
|
||||||
|
|
||||||
testConfig := &Config{}
|
|
||||||
*testConfig = *config
|
|
||||||
testConfig.Client = client
|
|
||||||
|
|
||||||
autoRefresher := testConfig.GetTokenWithAutoRefresh()
|
|
||||||
result, err := autoRefresher.AccountInfo("id", "1")
|
|
||||||
assert.Nil(result)
|
|
||||||
assert.Error(err)
|
|
||||||
assert.IsType(&NotFoundResponse{}, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAutoRefresh_AccountInfo4(t *testing.T) {
|
|
||||||
assert := testify.New(t)
|
|
||||||
|
|
||||||
defer gock.Off()
|
|
||||||
gock.New("https://account.ely.by").
|
|
||||||
Post("/api/oauth2/v1/token").
|
|
||||||
Times(3).
|
|
||||||
Body(strings.NewReader("client_id=mock-id&client_secret=mock-secret&grant_type=client_credentials&scope=scope1%2Cscope2")).
|
|
||||||
Reply(200).
|
|
||||||
JSON(map[string]interface{}{
|
|
||||||
"access_token": "mocked-token-1",
|
|
||||||
"token_type": "Bearer",
|
|
||||||
"expires_in": 86400,
|
|
||||||
})
|
|
||||||
|
|
||||||
gock.New("https://account.ely.by").
|
|
||||||
Get("/api/internal/accounts/info").
|
|
||||||
Times(3).
|
|
||||||
MatchParam("id", "1").
|
|
||||||
MatchHeader("Authorization", "Bearer mocked-token-1").
|
|
||||||
Reply(401).
|
|
||||||
JSON(map[string]interface{}{
|
|
||||||
"name": "Unauthorized",
|
|
||||||
"message": "Incorrect token",
|
|
||||||
"code": 0,
|
|
||||||
"status": 401,
|
|
||||||
})
|
|
||||||
|
|
||||||
client := &http.Client{}
|
|
||||||
gock.InterceptClient(client)
|
|
||||||
|
|
||||||
testConfig := &Config{}
|
|
||||||
*testConfig = *config
|
|
||||||
testConfig.Client = client
|
|
||||||
|
|
||||||
autoRefresher := testConfig.GetTokenWithAutoRefresh()
|
|
||||||
result, err := autoRefresher.AccountInfo("id", "1")
|
|
||||||
assert.Nil(result)
|
|
||||||
assert.Error(err)
|
|
||||||
if !assert.IsType(&UnauthorizedResponse{}, err) {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,11 +1,8 @@
|
|||||||
package bootstrap
|
package bootstrap
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"net/url"
|
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/assembla/cony"
|
|
||||||
"github.com/getsentry/raven-go"
|
"github.com/getsentry/raven-go"
|
||||||
"github.com/mono83/slf/rays"
|
"github.com/mono83/slf/rays"
|
||||||
"github.com/mono83/slf/recievers/sentry"
|
"github.com/mono83/slf/recievers/sentry"
|
||||||
@ -74,17 +71,3 @@ type RabbitMQConfig struct {
|
|||||||
Vhost string
|
Vhost string
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateRabbitMQClient(config *RabbitMQConfig) *cony.Client {
|
|
||||||
addr := fmt.Sprintf(
|
|
||||||
"amqp://%s:%s@%s:%d/%s",
|
|
||||||
config.Username,
|
|
||||||
config.Password,
|
|
||||||
config.Host,
|
|
||||||
config.Port,
|
|
||||||
url.PathEscape(config.Vhost),
|
|
||||||
)
|
|
||||||
|
|
||||||
client := cony.NewClient(cony.URL(addr), cony.Backoff(cony.DefaultBackoff))
|
|
||||||
|
|
||||||
return client
|
|
||||||
}
|
|
||||||
|
@ -1,67 +0,0 @@
|
|||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"github.com/spf13/viper"
|
|
||||||
|
|
||||||
"elyby/minecraft-skinsystem/api/accounts"
|
|
||||||
"elyby/minecraft-skinsystem/bootstrap"
|
|
||||||
"elyby/minecraft-skinsystem/db"
|
|
||||||
"elyby/minecraft-skinsystem/worker"
|
|
||||||
)
|
|
||||||
|
|
||||||
var amqpWorkerCmd = &cobra.Command{
|
|
||||||
Use: "amqp-worker",
|
|
||||||
Short: "Launches a worker which listens to events and processes them",
|
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
|
||||||
logger, err := bootstrap.CreateLogger(viper.GetString("statsd.addr"), viper.GetString("sentry.dsn"))
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(fmt.Printf("Cannot initialize logger: %v", err))
|
|
||||||
}
|
|
||||||
logger.Info("Logger successfully initialized")
|
|
||||||
|
|
||||||
storageFactory := db.StorageFactory{Config: viper.GetViper()}
|
|
||||||
|
|
||||||
logger.Info("Initializing skins repository")
|
|
||||||
skinsRepo, err := storageFactory.CreateFactory("redis").CreateSkinsRepository()
|
|
||||||
if err != nil {
|
|
||||||
logger.Emergency(fmt.Sprintf("Error on creating skins repo: %+v", err))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
logger.Info("Skins repository successfully initialized")
|
|
||||||
|
|
||||||
logger.Info("Creating AMQP client")
|
|
||||||
amqpClient := bootstrap.CreateRabbitMQClient(&bootstrap.RabbitMQConfig{
|
|
||||||
Host: viper.GetString("amqp.host"),
|
|
||||||
Port: viper.GetInt("amqp.port"),
|
|
||||||
Username: viper.GetString("amqp.username"),
|
|
||||||
Password: viper.GetString("amqp.password"),
|
|
||||||
Vhost: viper.GetString("amqp.vhost"),
|
|
||||||
})
|
|
||||||
|
|
||||||
accountsApi := (&accounts.Config{
|
|
||||||
Addr: viper.GetString("api.accounts.host"),
|
|
||||||
Id: viper.GetString("api.accounts.id"),
|
|
||||||
Secret: viper.GetString("api.accounts.secret"),
|
|
||||||
Scopes: viper.GetStringSlice("api.accounts.scopes"),
|
|
||||||
}).GetTokenWithAutoRefresh()
|
|
||||||
|
|
||||||
services := &worker.Services{
|
|
||||||
Logger: logger,
|
|
||||||
AmqpClient: amqpClient,
|
|
||||||
SkinsRepo: skinsRepo,
|
|
||||||
AccountsAPI: accountsApi,
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := services.Run(); err != nil {
|
|
||||||
logger.Error(fmt.Sprintf("Cannot initialize worker: %+v", err))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
RootCmd.AddCommand(amqpWorkerCmd)
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
package interfaces
|
|
||||||
|
|
||||||
import (
|
|
||||||
"elyby/minecraft-skinsystem/api/accounts"
|
|
||||||
)
|
|
||||||
|
|
||||||
type AccountsAPI interface {
|
|
||||||
AccountInfo(attribute string, value string) (*accounts.AccountInfoResponse, error)
|
|
||||||
}
|
|
@ -1,46 +0,0 @@
|
|||||||
// Code generated by MockGen. DO NOT EDIT.
|
|
||||||
// Source: interfaces/api.go
|
|
||||||
|
|
||||||
package mock_interfaces
|
|
||||||
|
|
||||||
import (
|
|
||||||
accounts "elyby/minecraft-skinsystem/api/accounts"
|
|
||||||
gomock "github.com/golang/mock/gomock"
|
|
||||||
reflect "reflect"
|
|
||||||
)
|
|
||||||
|
|
||||||
// MockAccountsAPI is a mock of AccountsAPI interface
|
|
||||||
type MockAccountsAPI struct {
|
|
||||||
ctrl *gomock.Controller
|
|
||||||
recorder *MockAccountsAPIMockRecorder
|
|
||||||
}
|
|
||||||
|
|
||||||
// MockAccountsAPIMockRecorder is the mock recorder for MockAccountsAPI
|
|
||||||
type MockAccountsAPIMockRecorder struct {
|
|
||||||
mock *MockAccountsAPI
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewMockAccountsAPI creates a new mock instance
|
|
||||||
func NewMockAccountsAPI(ctrl *gomock.Controller) *MockAccountsAPI {
|
|
||||||
mock := &MockAccountsAPI{ctrl: ctrl}
|
|
||||||
mock.recorder = &MockAccountsAPIMockRecorder{mock}
|
|
||||||
return mock
|
|
||||||
}
|
|
||||||
|
|
||||||
// EXPECT returns an object that allows the caller to indicate expected use
|
|
||||||
func (_m *MockAccountsAPI) EXPECT() *MockAccountsAPIMockRecorder {
|
|
||||||
return _m.recorder
|
|
||||||
}
|
|
||||||
|
|
||||||
// AccountInfo mocks base method
|
|
||||||
func (_m *MockAccountsAPI) AccountInfo(attribute string, value string) (*accounts.AccountInfoResponse, error) {
|
|
||||||
ret := _m.ctrl.Call(_m, "AccountInfo", attribute, value)
|
|
||||||
ret0, _ := ret[0].(*accounts.AccountInfoResponse)
|
|
||||||
ret1, _ := ret[1].(error)
|
|
||||||
return ret0, ret1
|
|
||||||
}
|
|
||||||
|
|
||||||
// AccountInfo indicates an expected call of AccountInfo
|
|
||||||
func (_mr *MockAccountsAPIMockRecorder) AccountInfo(arg0, arg1 interface{}) *gomock.Call {
|
|
||||||
return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "AccountInfo", reflect.TypeOf((*MockAccountsAPI)(nil).AccountInfo), arg0, arg1)
|
|
||||||
}
|
|
@ -1,5 +1,4 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
mockgen -source=interfaces/repositories.go -destination=interfaces/mock_interfaces/mock_interfaces.go
|
mockgen -source=interfaces/repositories.go -destination=interfaces/mock_interfaces/mock_interfaces.go
|
||||||
mockgen -source=interfaces/api.go -destination=interfaces/mock_interfaces/mock_api.go
|
|
||||||
mockgen -source=interfaces/auth.go -destination=interfaces/mock_interfaces/mock_auth.go
|
mockgen -source=interfaces/auth.go -destination=interfaces/mock_interfaces/mock_auth.go
|
||||||
|
@ -1,186 +0,0 @@
|
|||||||
package worker
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/golang/mock/gomock"
|
|
||||||
testify "github.com/stretchr/testify/assert"
|
|
||||||
|
|
||||||
"elyby/minecraft-skinsystem/api/accounts"
|
|
||||||
"elyby/minecraft-skinsystem/db"
|
|
||||||
"elyby/minecraft-skinsystem/interfaces/mock_interfaces"
|
|
||||||
"elyby/minecraft-skinsystem/interfaces/mock_wd"
|
|
||||||
"elyby/minecraft-skinsystem/model"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestServices_HandleChangeUsername(t *testing.T) {
|
|
||||||
assert := testify.New(t)
|
|
||||||
|
|
||||||
ctrl := gomock.NewController(t)
|
|
||||||
defer ctrl.Finish()
|
|
||||||
|
|
||||||
services, skinRepo, _, wd := setupMocks(ctrl)
|
|
||||||
|
|
||||||
resultModel := createSourceModel()
|
|
||||||
resultModel.Username = "new_username"
|
|
||||||
|
|
||||||
// Запись о скине существует, никаких осложнений
|
|
||||||
skinRepo.EXPECT().FindByUserId(1).Return(createSourceModel(), nil)
|
|
||||||
skinRepo.EXPECT().Save(resultModel)
|
|
||||||
wd.EXPECT().IncCounter("worker.change_username", int64(1))
|
|
||||||
|
|
||||||
assert.True(services.HandleChangeUsername(&UsernameChanged{
|
|
||||||
AccountId: 1,
|
|
||||||
OldUsername: "mock_user",
|
|
||||||
NewUsername: "new_username",
|
|
||||||
}))
|
|
||||||
|
|
||||||
// Событие с пустым ником, т.е это регистрация, так что нужно создать запись о скине
|
|
||||||
skinRepo.EXPECT().FindByUserId(1).Times(0)
|
|
||||||
skinRepo.EXPECT().Save(&model.Skin{UserId: 1, Username: "new_mock"})
|
|
||||||
wd.EXPECT().IncCounter("worker.change_username", int64(1))
|
|
||||||
wd.EXPECT().IncCounter("worker.change_username_empty_old_username", int64(1))
|
|
||||||
|
|
||||||
assert.True(services.HandleChangeUsername(&UsernameChanged{
|
|
||||||
AccountId: 1,
|
|
||||||
OldUsername: "",
|
|
||||||
NewUsername: "new_mock",
|
|
||||||
}))
|
|
||||||
|
|
||||||
// В базе системы скинов нет записи об указанном пользователе, так что её нужно восстановить
|
|
||||||
skinRepo.EXPECT().FindByUserId(1).Return(nil, &db.SkinNotFoundError{})
|
|
||||||
skinRepo.EXPECT().Save(&model.Skin{UserId: 1, Username: "new_mock2"})
|
|
||||||
wd.EXPECT().IncCounter("worker.change_username", int64(1))
|
|
||||||
wd.EXPECT().IncCounter("worker.change_username_id_not_found", int64(1))
|
|
||||||
wd.EXPECT().Info("Cannot find user id :accountId. Trying to search.", gomock.Any())
|
|
||||||
|
|
||||||
assert.True(services.HandleChangeUsername(&UsernameChanged{
|
|
||||||
AccountId: 1,
|
|
||||||
OldUsername: "mock_user",
|
|
||||||
NewUsername: "new_mock2",
|
|
||||||
}))
|
|
||||||
|
|
||||||
// Репозиторий вернул неожиданную ошибку
|
|
||||||
skinRepo.EXPECT().FindByUserId(1).Return(nil, errors.New("mock error"))
|
|
||||||
skinRepo.EXPECT().Save(&model.Skin{UserId: 1, Username: "new_mock2"})
|
|
||||||
wd.EXPECT().IncCounter("worker.change_username", int64(1))
|
|
||||||
wd.EXPECT().IncCounter("worker.change_username_id_not_found", int64(1))
|
|
||||||
wd.EXPECT().Info("Cannot find user id :accountId. Trying to search.", gomock.Any())
|
|
||||||
wd.EXPECT().Error("Unknown error when requesting a skin from the repository: :err", gomock.Any())
|
|
||||||
|
|
||||||
assert.True(services.HandleChangeUsername(&UsernameChanged{
|
|
||||||
AccountId: 1,
|
|
||||||
OldUsername: "mock_user",
|
|
||||||
NewUsername: "new_mock2",
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestServices_HandleSkinChanged(t *testing.T) {
|
|
||||||
assert := testify.New(t)
|
|
||||||
|
|
||||||
ctrl := gomock.NewController(t)
|
|
||||||
defer ctrl.Finish()
|
|
||||||
|
|
||||||
services, skinRepo, accountsAPI, wd := setupMocks(ctrl)
|
|
||||||
|
|
||||||
event := &SkinChanged{
|
|
||||||
AccountId: 1,
|
|
||||||
Uuid: "cdb907ce-84f4-4c38-801d-1e287dca2623",
|
|
||||||
SkinId: 2,
|
|
||||||
Hash: "f76caa016e07267a05b7daf9ebc7419c",
|
|
||||||
Is1_8: true,
|
|
||||||
IsSlim: false,
|
|
||||||
Url: "http://ely.by/minecraft/skins/69c6740d2993e5d6f6a7fc92420efc29.png",
|
|
||||||
MojangTextures: "new mocked textures base64",
|
|
||||||
MojangSignature: "new mocked signature",
|
|
||||||
}
|
|
||||||
|
|
||||||
resultModel := createSourceModel()
|
|
||||||
resultModel.SkinId = event.SkinId
|
|
||||||
resultModel.Hash = event.Hash
|
|
||||||
resultModel.Is1_8 = event.Is1_8
|
|
||||||
resultModel.IsSlim = event.IsSlim
|
|
||||||
resultModel.Url = event.Url
|
|
||||||
resultModel.MojangTextures = event.MojangTextures
|
|
||||||
resultModel.MojangSignature = event.MojangSignature
|
|
||||||
|
|
||||||
// Запись о скине существует, никаких осложнений
|
|
||||||
skinRepo.EXPECT().FindByUserId(1).Return(createSourceModel(), nil)
|
|
||||||
skinRepo.EXPECT().Save(resultModel)
|
|
||||||
wd.EXPECT().IncCounter("worker.skin_changed", int64(1))
|
|
||||||
|
|
||||||
assert.True(services.HandleSkinChanged(event))
|
|
||||||
|
|
||||||
// Записи о скине не существует, она должна быть восстановлена
|
|
||||||
skinRepo.EXPECT().FindByUserId(1).Return(nil, &db.SkinNotFoundError{"mock_user"})
|
|
||||||
skinRepo.EXPECT().Save(resultModel)
|
|
||||||
accountsAPI.EXPECT().AccountInfo("id", "1").Return(&accounts.AccountInfoResponse{
|
|
||||||
Id: 1,
|
|
||||||
Username: "mock_user",
|
|
||||||
Uuid: "cdb907ce-84f4-4c38-801d-1e287dca2623",
|
|
||||||
Email: "mock-user@ely.by",
|
|
||||||
}, nil)
|
|
||||||
wd.EXPECT().IncCounter("worker.skin_changed", int64(1))
|
|
||||||
wd.EXPECT().IncCounter("worker.skin_changed_id_not_found", int64(1))
|
|
||||||
wd.EXPECT().IncCounter("worker.skin_changed_id_restored", int64(1))
|
|
||||||
wd.EXPECT().Info("Cannot find user id :accountId. Trying to search.", gomock.Any())
|
|
||||||
wd.EXPECT().Info("User info successfully restored.")
|
|
||||||
|
|
||||||
assert.True(services.HandleSkinChanged(event))
|
|
||||||
|
|
||||||
// Записи о скине не существует, и Ely.by Accounts internal API не знает о таком пользователе
|
|
||||||
skinRepo.EXPECT().FindByUserId(1).Return(nil, &db.SkinNotFoundError{"mock_user"})
|
|
||||||
accountsAPI.EXPECT().AccountInfo("id", "1").Return(nil, &accounts.NotFoundResponse{})
|
|
||||||
wd.EXPECT().IncCounter("worker.skin_changed", int64(1))
|
|
||||||
wd.EXPECT().IncCounter("worker.skin_changed_id_not_found", int64(1))
|
|
||||||
wd.EXPECT().IncCounter("worker.skin_changed_id_not_restored", int64(1))
|
|
||||||
wd.EXPECT().Info("Cannot find user id :accountId. Trying to search.", gomock.Any())
|
|
||||||
wd.EXPECT().Error("Cannot restore user info for :accountId: :err", gomock.Any(), gomock.Any())
|
|
||||||
|
|
||||||
assert.True(services.HandleSkinChanged(event))
|
|
||||||
|
|
||||||
// Репозиторий скинов вернул неизвестную ошибку, и Ely.by Accounts internal API не знает о таком пользователе
|
|
||||||
skinRepo.EXPECT().FindByUserId(1).Return(nil, errors.New("mocked error"))
|
|
||||||
accountsAPI.EXPECT().AccountInfo("id", "1").Return(nil, &accounts.NotFoundResponse{})
|
|
||||||
wd.EXPECT().IncCounter("worker.skin_changed", int64(1))
|
|
||||||
wd.EXPECT().IncCounter("worker.skin_changed_id_not_found", int64(1))
|
|
||||||
wd.EXPECT().IncCounter("worker.skin_changed_id_not_restored", int64(1))
|
|
||||||
wd.EXPECT().Error("Unknown error when requesting a skin from the repository: :err", gomock.Any())
|
|
||||||
wd.EXPECT().Info("Cannot find user id :accountId. Trying to search.", gomock.Any())
|
|
||||||
wd.EXPECT().Error("Cannot restore user info for :accountId: :err", gomock.Any(), gomock.Any())
|
|
||||||
|
|
||||||
assert.True(services.HandleSkinChanged(event))
|
|
||||||
}
|
|
||||||
|
|
||||||
func createSourceModel() *model.Skin {
|
|
||||||
return &model.Skin{
|
|
||||||
UserId: 1,
|
|
||||||
Uuid: "cdb907ce-84f4-4c38-801d-1e287dca2623",
|
|
||||||
Username: "mock_user",
|
|
||||||
SkinId: 1,
|
|
||||||
Url: "http://ely.by/minecraft/skins/3a345c701f473ac08c8c5b8ecb58ecf3.png",
|
|
||||||
Is1_8: false,
|
|
||||||
IsSlim: false,
|
|
||||||
Hash: "3a345c701f473ac08c8c5b8ecb58ecf3",
|
|
||||||
MojangTextures: "mocked textures base64",
|
|
||||||
MojangSignature: "mocked signature",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func setupMocks(ctrl *gomock.Controller) (
|
|
||||||
*Services,
|
|
||||||
*mock_interfaces.MockSkinsRepository,
|
|
||||||
*mock_interfaces.MockAccountsAPI,
|
|
||||||
*mock_wd.MockWatchdog,
|
|
||||||
) {
|
|
||||||
skinsRepo := mock_interfaces.NewMockSkinsRepository(ctrl)
|
|
||||||
accountApi := mock_interfaces.NewMockAccountsAPI(ctrl)
|
|
||||||
wd := mock_wd.NewMockWatchdog(ctrl)
|
|
||||||
|
|
||||||
return &Services{
|
|
||||||
SkinsRepo: skinsRepo,
|
|
||||||
AccountsAPI: accountApi,
|
|
||||||
Logger: wd,
|
|
||||||
}, skinsRepo, accountApi, wd
|
|
||||||
}
|
|
219
worker/worker.go
219
worker/worker.go
@ -1,219 +0,0 @@
|
|||||||
package worker
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"github.com/assembla/cony"
|
|
||||||
"github.com/mono83/slf/wd"
|
|
||||||
"github.com/streadway/amqp"
|
|
||||||
|
|
||||||
"elyby/minecraft-skinsystem/db"
|
|
||||||
"elyby/minecraft-skinsystem/interfaces"
|
|
||||||
"elyby/minecraft-skinsystem/model"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Services struct {
|
|
||||||
AmqpClient *cony.Client
|
|
||||||
SkinsRepo interfaces.SkinsRepository
|
|
||||||
AccountsAPI interfaces.AccountsAPI
|
|
||||||
Logger wd.Watchdog
|
|
||||||
}
|
|
||||||
|
|
||||||
type UsernameChanged struct {
|
|
||||||
AccountId int `json:"accountId"`
|
|
||||||
OldUsername string `json:"oldUsername"`
|
|
||||||
NewUsername string `json:"newUsername"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type SkinChanged struct {
|
|
||||||
AccountId int `json:"userId"`
|
|
||||||
Uuid string `json:"uuid"`
|
|
||||||
SkinId int `json:"skinId"`
|
|
||||||
Hash string `json:"hash"`
|
|
||||||
Is1_8 bool `json:"is1_8"`
|
|
||||||
IsSlim bool `json:"isSlim"`
|
|
||||||
Url string `json:"url"`
|
|
||||||
MojangTextures string `json:"mojangTextures"`
|
|
||||||
MojangSignature string `json:"mojangSignature"`
|
|
||||||
}
|
|
||||||
|
|
||||||
const exchangeName string = "events"
|
|
||||||
const queueName string = "skinsystem-accounts-events"
|
|
||||||
|
|
||||||
func (service *Services) Run() error {
|
|
||||||
clientErrs, consumerErrs, deliveryChannel := setupClient(service.AmqpClient)
|
|
||||||
shouldReturnError := true
|
|
||||||
|
|
||||||
for service.AmqpClient.Loop() {
|
|
||||||
select {
|
|
||||||
case msg := <-deliveryChannel:
|
|
||||||
shouldReturnError = false
|
|
||||||
service.HandleDelivery(&msg)
|
|
||||||
case err := <-consumerErrs:
|
|
||||||
if shouldReturnError {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
service.Logger.Error("Consume error: :err", wd.ErrParam(err))
|
|
||||||
case err := <-clientErrs:
|
|
||||||
if shouldReturnError {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
service.Logger.Error("Client error: :err", wd.ErrParam(err))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (service *Services) HandleDelivery(delivery *amqp.Delivery) {
|
|
||||||
service.Logger.Debug("Incoming message with routing key " + delivery.RoutingKey)
|
|
||||||
var result bool = true
|
|
||||||
switch delivery.RoutingKey {
|
|
||||||
case "accounts.username-changed":
|
|
||||||
var event *UsernameChanged
|
|
||||||
json.Unmarshal(delivery.Body, &event)
|
|
||||||
result = service.HandleChangeUsername(event)
|
|
||||||
case "accounts.skin-changed":
|
|
||||||
var event *SkinChanged
|
|
||||||
json.Unmarshal(delivery.Body, &event)
|
|
||||||
result = service.HandleSkinChanged(event)
|
|
||||||
default:
|
|
||||||
service.Logger.Info("Unknown delivery with routing key " + delivery.RoutingKey)
|
|
||||||
delivery.Ack(false)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if result {
|
|
||||||
delivery.Ack(false)
|
|
||||||
} else {
|
|
||||||
delivery.Reject(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (service *Services) HandleChangeUsername(event *UsernameChanged) bool {
|
|
||||||
service.Logger.IncCounter("worker.change_username", 1)
|
|
||||||
if event.OldUsername == "" {
|
|
||||||
service.Logger.IncCounter("worker.change_username_empty_old_username", 1)
|
|
||||||
record := &model.Skin{
|
|
||||||
UserId: event.AccountId,
|
|
||||||
Username: event.NewUsername,
|
|
||||||
}
|
|
||||||
|
|
||||||
service.SkinsRepo.Save(record)
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
record, err := service.SkinsRepo.FindByUserId(event.AccountId)
|
|
||||||
if err != nil {
|
|
||||||
service.Logger.Info("Cannot find user id :accountId. Trying to search.", wd.IntParam("accountId", event.AccountId))
|
|
||||||
if _, isSkinNotFound := err.(*db.SkinNotFoundError); !isSkinNotFound {
|
|
||||||
service.Logger.Error("Unknown error when requesting a skin from the repository: :err", wd.ErrParam(err))
|
|
||||||
}
|
|
||||||
|
|
||||||
service.Logger.IncCounter("worker.change_username_id_not_found", 1)
|
|
||||||
record = &model.Skin{
|
|
||||||
UserId: event.AccountId,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
record.Username = event.NewUsername
|
|
||||||
service.SkinsRepo.Save(record)
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: возможно стоит добавить проверку на совпадение id аккаунтов
|
|
||||||
func (service *Services) HandleSkinChanged(event *SkinChanged) bool {
|
|
||||||
service.Logger.IncCounter("worker.skin_changed", 1)
|
|
||||||
var record *model.Skin
|
|
||||||
record, err := service.SkinsRepo.FindByUserId(event.AccountId)
|
|
||||||
if err != nil {
|
|
||||||
if _, isSkinNotFound := err.(*db.SkinNotFoundError); !isSkinNotFound {
|
|
||||||
service.Logger.Error("Unknown error when requesting a skin from the repository: :err", wd.ErrParam(err))
|
|
||||||
}
|
|
||||||
|
|
||||||
service.Logger.IncCounter("worker.skin_changed_id_not_found", 1)
|
|
||||||
service.Logger.Info("Cannot find user id :accountId. Trying to search.", wd.IntParam("accountId", event.AccountId))
|
|
||||||
response, err := service.AccountsAPI.AccountInfo("id", strconv.Itoa(event.AccountId))
|
|
||||||
if err != nil {
|
|
||||||
service.Logger.IncCounter("worker.skin_changed_id_not_restored", 1)
|
|
||||||
service.Logger.Error(
|
|
||||||
"Cannot restore user info for :accountId: :err",
|
|
||||||
wd.IntParam("accountId", event.AccountId),
|
|
||||||
wd.ErrParam(err),
|
|
||||||
)
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
service.Logger.IncCounter("worker.skin_changed_id_restored", 1)
|
|
||||||
service.Logger.Info("User info successfully restored.")
|
|
||||||
|
|
||||||
record = &model.Skin{
|
|
||||||
UserId: response.Id,
|
|
||||||
Username: response.Username,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
record.Uuid = event.Uuid
|
|
||||||
record.SkinId = event.SkinId
|
|
||||||
record.Hash = event.Hash
|
|
||||||
record.Is1_8 = event.Is1_8
|
|
||||||
record.IsSlim = event.IsSlim
|
|
||||||
record.Url = event.Url
|
|
||||||
record.MojangTextures = event.MojangTextures
|
|
||||||
record.MojangSignature = event.MojangSignature
|
|
||||||
|
|
||||||
service.SkinsRepo.Save(record)
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func setupClient(client *cony.Client) (<-chan error, <-chan error, <-chan amqp.Delivery ) {
|
|
||||||
exchange := cony.Exchange{
|
|
||||||
Name: exchangeName,
|
|
||||||
Kind: "topic",
|
|
||||||
Durable: true,
|
|
||||||
AutoDelete: false,
|
|
||||||
}
|
|
||||||
|
|
||||||
queue := &cony.Queue{
|
|
||||||
Name: queueName,
|
|
||||||
Durable: true,
|
|
||||||
AutoDelete: false,
|
|
||||||
Exclusive: false,
|
|
||||||
}
|
|
||||||
|
|
||||||
usernameEventBinding := cony.Binding{
|
|
||||||
Exchange: exchange,
|
|
||||||
Queue: queue,
|
|
||||||
Key: "accounts.username-changed",
|
|
||||||
}
|
|
||||||
|
|
||||||
skinEventBinding := cony.Binding{
|
|
||||||
Exchange: exchange,
|
|
||||||
Queue: queue,
|
|
||||||
Key: "accounts.skin-changed",
|
|
||||||
}
|
|
||||||
|
|
||||||
declarations := []cony.Declaration{
|
|
||||||
cony.DeclareExchange(exchange),
|
|
||||||
cony.DeclareQueue(queue),
|
|
||||||
cony.DeclareBinding(usernameEventBinding),
|
|
||||||
cony.DeclareBinding(skinEventBinding),
|
|
||||||
}
|
|
||||||
|
|
||||||
client.Declare(declarations)
|
|
||||||
|
|
||||||
consumer := cony.NewConsumer(queue,
|
|
||||||
cony.Qos(10),
|
|
||||||
cony.AutoTag(),
|
|
||||||
)
|
|
||||||
client.Consume(consumer)
|
|
||||||
|
|
||||||
return client.Errors(), consumer.Errors(), consumer.Deliveries()
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user