1
0
mirror of synced 2024-11-21 20:16:02 +03:00

improve migrations, models, minor fixes

This commit is contained in:
DmitryZagorulko 2018-05-31 16:18:52 +03:00
parent 3933319b0e
commit 5a7dce7e14
7 changed files with 166 additions and 165 deletions

View File

@ -1,26 +1,32 @@
alter table connection
drop constraint connection_client_id_key,
drop constraint connection_api_key_key,
drop constraint connection_api_url_key,
drop constraint connection_mg_url_key;
alter table connection
drop constraint connection_key,
alter column client_id drop not null,
alter column api_key drop not null,
alter column api_url drop not null;
alter column api_url drop not null,
alter column mg_url drop not null,
alter column mg_token drop not null,
alter column api_url type varchar(100),
alter column mg_url type varchar(100);
alter table bot
alter column connection_id type varchar(70),
add column client_id varchar(70);
update bot b
set client_id = c.client_id
from connection c
where b.connection_id = c.id;
alter table bot
drop column connection_id,
alter column channel drop not null,
alter column token drop not null,
drop constraint bot_token_key;
alter table bot
rename column connection_id to client_id;
drop constraint bot_key;
create table mapping
(
id serial not null
id serial not null
constraint mapping_pkey
primary key,
site_code text,
bot_id text
);
);

View File

@ -1,19 +1,27 @@
alter table connection
add constraint connection_client_id_key unique (api_key),
add constraint connection_api_key_key unique (api_url),
add constraint connection_api_url_key unique (mg_url),
add constraint connection_mg_url_key unique (mg_token);
alter table connection
add constraint connection_key unique (client_id, mg_token),
alter column client_id set not null,
alter column api_key set not null,
alter column api_url set not null;
alter column api_url set not null,
alter column mg_url set not null,
alter column mg_token set not null,
alter column api_url type varchar(255),
alter column mg_url type varchar(255);
alter table bot
alter column client_id type integer using client_id::integer,
add column connection_id integer;
update bot b
set connection_id = c.id
from connection c
where b.client_id = c.client_id;
alter table bot
drop column client_id,
alter column channel set not null,
alter column token set not null,
add constraint bot_token_key unique (token);
add constraint bot_key unique (channel, token);
alter table bot
rename column client_id to connection_id;
alter table bot add foreign key (connection_id) references connection on delete cascade;
drop table mapping;
drop table mapping;

View File

@ -6,10 +6,10 @@ import "time"
type Connection struct {
ID int `gorm:"primary_key"`
ClientID string `gorm:"client_id type:varchar(70);not null;unique" json:"clientId,omitempty"`
APIKEY string `gorm:"api_key type:varchar(100);not null;unique" json:"api_key,omitempty"`
APIURL string `gorm:"api_url type:varchar(100);not null;unique" json:"api_url,omitempty"`
MGURL string `gorm:"mg_url type:varchar(100);unique" json:"mg_url,omitempty"`
MGToken string `gorm:"mg_token type:varchar(100)" json:"mg_token,omitempty"`
APIKEY string `gorm:"api_key type:varchar(100);not null" json:"api_key,omitempty"`
APIURL string `gorm:"api_url type:varchar(255);not null" json:"api_url,omitempty"`
MGURL string `gorm:"mg_url type:varchar(255);not null;" json:"mg_url,omitempty"`
MGToken string `gorm:"mg_token type:varchar(100);not null;unique" json:"mg_token,omitempty"`
CreatedAt time.Time
UpdatedAt time.Time
Active bool `json:"active,omitempty"`
@ -20,7 +20,7 @@ type Connection struct {
type Bot struct {
ID int `gorm:"primary_key"`
ConnectionID int `gorm:"connection_id" json:"connectionId,omitempty"`
Channel uint64 `json:"channel,omitempty"`
Channel uint64 `gorm:"channel;not null;unique" json:"channel,omitempty"`
Token string `gorm:"token type:varchar(100);not null;unique" json:"token,omitempty"`
Name string `gorm:"name type:varchar(40)" json:"name,omitempty"`
CreatedAt time.Time

View File

@ -1,5 +1,7 @@
package main
import "github.com/jinzhu/gorm"
func getConnection(uid string) *Connection {
var connection Connection
orm.DB.First(&connection, "client_id = ?", uid)
@ -30,11 +32,18 @@ func (c *Connection) createBot(b Bot) error {
return orm.DB.Model(c).Association("Bots").Append(&b).Error
}
func getBotByToken(token string) *Bot {
var bot Bot
orm.DB.First(&bot, "token = ?", token)
func getConnectionByBotToken(token string) (*Connection, error) {
var c Connection
err := orm.DB.Where("active = ?", true).
Preload("Bots", "token = ?", token).
First(&c).Error
if gorm.IsRecordNotFoundError(err) {
return &c, nil
} else {
return &c, err
}
return &bot
return &c, nil
}
func getBotByChannel(ch uint64) *Bot {
@ -44,10 +53,6 @@ func getBotByChannel(ch uint64) *Bot {
return &bot
}
func (b *Bot) createBot() error {
return orm.DB.Create(b).Error
}
func (b *Bot) setBotActivity() error {
return orm.DB.Model(b).Where("token = ?", b.Token).Update("Active", !b.Active).Error
}
@ -59,9 +64,14 @@ func getBotChannelByToken(token string) uint64 {
return b.Channel
}
func (b *Bots) getBotsByClientID(uid string) error {
var c Connection
return orm.DB.First(&c, "client_id = ?", uid).Association("Bots").Find(b).Error
func (c Connection) getBotsByClientID() Bots {
var b Bots
err := orm.DB.Model(c).Association("Bots").Find(&b).Error
if err != nil {
logger.Error(err)
}
return b
}
func getConnectionById(id int) *Connection {

View File

@ -133,15 +133,15 @@ func addBotHandler(w http.ResponseWriter, r *http.Request) {
return
}
c := getConnectionById(b.ConnectionID)
if c.MGURL == "" || c.MGToken == "" {
http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "not_found_account"}), http.StatusBadRequest)
logger.Error(b.Token, "MGURL or MGToken is empty")
cb, err := getConnectionByBotToken(b.Token)
if err != nil {
raven.CaptureErrorAndWait(err, nil)
http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_adding_bot"}), http.StatusInternalServerError)
logger.Error(err.Error())
return
}
cl := getBotByToken(b.Token)
if cl.ID != 0 {
if len(cb.Bots) != 0 {
http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "bot_already_created"}), http.StatusBadRequest)
return
}
@ -180,22 +180,22 @@ func addBotHandler(w http.ResponseWriter, r *http.Request) {
},
}
var client = v1.New(c.MGURL, c.MGToken)
var client = v1.New(cb.MGURL, cb.MGToken)
data, status, err := client.ActivateTransportChannel(ch)
if status != http.StatusCreated {
http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_activating_channel"}), http.StatusBadRequest)
logger.Error(c.APIURL, status, err.Error(), data)
logger.Error(cb.APIURL, status, err.Error(), data)
return
}
b.Channel = data.ChannelID
b.Active = true
err = c.createBot(b)
err = cb.createBot(b)
if err != nil {
raven.CaptureErrorAndWait(err, nil)
http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_adding_bot"}), http.StatusInternalServerError)
logger.Error(c.APIURL, err.Error())
logger.Error(cb.APIURL, err.Error())
return
}
@ -203,7 +203,7 @@ func addBotHandler(w http.ResponseWriter, r *http.Request) {
if err != nil {
raven.CaptureErrorAndWait(err, nil)
http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_adding_bot"}), http.StatusInternalServerError)
logger.Error(c.APIURL, err.Error())
logger.Error(cb.APIURL, err.Error())
return
}
@ -286,13 +286,7 @@ func settingsHandler(w http.ResponseWriter, r *http.Request, uid string) {
return
}
bots := Bots{}
err := bots.getBotsByClientID(uid)
if err != nil {
raven.CaptureErrorAndWait(err, nil)
http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_save"}), http.StatusInternalServerError)
return
}
bots := p.getBotsByClientID()
res := struct {
Conn *Connection
@ -368,6 +362,7 @@ func createHandler(w http.ResponseWriter, r *http.Request) {
if err != nil {
raven.CaptureErrorAndWait(err, nil)
http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_save"}), http.StatusInternalServerError)
logger.Error(err.Error())
return
}
@ -377,6 +372,7 @@ func createHandler(w http.ResponseWriter, r *http.Request) {
if err != nil {
raven.CaptureErrorAndWait(err, nil)
http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_save"}), http.StatusInternalServerError)
logger.Error(c.APIURL, err.Error())
return
}
@ -453,6 +449,7 @@ func createHandler(w http.ResponseWriter, r *http.Request) {
if err != nil {
raven.CaptureErrorAndWait(err, nil)
http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_creating_connection"}), http.StatusInternalServerError)
logger.Error(c.APIURL, err.Error())
return
}
@ -468,6 +465,7 @@ func createHandler(w http.ResponseWriter, r *http.Request) {
if err != nil {
raven.CaptureErrorAndWait(err, nil)
http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_creating_connection"}), http.StatusInternalServerError)
logger.Error(c.APIURL, err.Error())
return
}

View File

@ -1,13 +1,20 @@
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/http/httptest"
"net/url"
"sort"
"strings"
"testing"
"github.com/h2non/gock"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gopkg.in/yaml.v2"
)
func init() {
@ -26,7 +33,9 @@ func init() {
}
c.createConnection()
orm.DB.Where("token = 123123:Qwerty").Delete(Bot{})
}
func TestRouting_connectHandler(t *testing.T) {
req, err := http.NewRequest("GET", "/", nil)
if err != nil {
@ -37,11 +46,8 @@ func TestRouting_connectHandler(t *testing.T) {
handler := http.HandlerFunc(connectHandler)
handler.ServeHTTP(rr, req)
if rr.Code != http.StatusOK {
t.Errorf("handler returned wrong status code: got %v want %v",
rr.Code, http.StatusOK)
}
assert.Equal(t, http.StatusOK, rr.Code,
fmt.Sprintf("handler returned wrong status code: got %v want %v", rr.Code, http.StatusOK))
}
func TestRouting_addBotHandler(t *testing.T) {
@ -78,15 +84,25 @@ func TestRouting_addBotHandler(t *testing.T) {
if err != nil {
t.Fatal(err)
}
rr := httptest.NewRecorder()
handler := http.HandlerFunc(addBotHandler)
handler.ServeHTTP(rr, req)
require.Equal(t, http.StatusCreated, rr.Code,
fmt.Sprintf("handler returned wrong status code: got %v want %v", rr.Code, http.StatusCreated))
if rr.Code != http.StatusCreated {
t.Errorf("handler returned wrong status code: got %v want %v",
rr.Code, http.StatusCreated)
bytes, err := ioutil.ReadAll(rr.Body)
if err != nil {
t.Fatal(err)
}
var res map[string]interface{}
err = json.Unmarshal(bytes, &res)
if err != nil {
t.Fatal(err)
}
assert.Equal(t, "123123:Qwerty", res["token"])
}
func TestRouting_activityBotHandler(t *testing.T) {
@ -109,10 +125,8 @@ func TestRouting_activityBotHandler(t *testing.T) {
handler := http.HandlerFunc(activityBotHandler)
handler.ServeHTTP(rr, req)
if rr.Code != http.StatusOK {
t.Errorf("handler returned wrong status code: got %v want %v",
rr.Code, http.StatusOK)
}
assert.Equal(t, http.StatusOK, rr.Code,
fmt.Sprintf("handler returned wrong status code: got %v want %v", rr.Code, http.StatusOK))
}
func TestRouting_settingsHandler(t *testing.T) {
@ -125,10 +139,8 @@ func TestRouting_settingsHandler(t *testing.T) {
handler := http.HandlerFunc(makeHandler(settingsHandler))
handler.ServeHTTP(rr, req)
if rr.Code != http.StatusOK {
t.Errorf("handler returned wrong status code: got %v want %v",
rr.Code, http.StatusOK)
}
assert.Equal(t, http.StatusOK, rr.Code,
fmt.Sprintf("handler returned wrong status code: got %v want %v", rr.Code, http.StatusOK))
}
func TestRouting_saveHandler(t *testing.T) {
@ -153,80 +165,10 @@ func TestRouting_saveHandler(t *testing.T) {
handler := http.HandlerFunc(saveHandler)
handler.ServeHTTP(rr, req)
if rr.Code != http.StatusOK {
t.Errorf("handler returned wrong status code: got %v want %v",
rr.Code, http.StatusOK)
}
assert.Equal(t, http.StatusOK, rr.Code,
fmt.Sprintf("handler returned wrong status code: got %v want %v", rr.Code, http.StatusOK))
}
//
//func TestRouting_createHandler(t *testing.T) {
// defer gock.Off()
//
// gock.New("https://test2.retailcrm.ru").
// Get("/api/credentials").
// Reply(200).
// BodyString(`{"success": true}`)
//
// integrationModule := v5.IntegrationModule{
// Code: transport,
// IntegrationCode: transport,
// Active: true,
// Name: "Telegram",
// ClientID: "123",
// Logo: fmt.Sprintf(
// "https://%s/web/telegram_logo.svg",
// config.HTTPServer.Host,
// ),
// BaseURL: fmt.Sprintf(
// "https://%s",
// config.HTTPServer.Host,
// ),
// AccountURL: fmt.Sprintf(
// "https://%s/settings/%s",
// config.HTTPServer.Host,
// "123",
// ),
// Actions: map[string]string{"activity": "/actions/activity"},
// Integrations: &v5.Integrations{
// MgTransport: &v5.MgTransport{
// WebhookUrl: fmt.Sprintf(
// "https://%s/webhook",
// config.HTTPServer.Host,
// ),
// },
// },
// }
//
// updateJSON, _ := json.Marshal(&integrationModule)
// p := url.Values{"integrationModule": {string(updateJSON[:])}}
//
// gock.New("https://test2.retailcrm.ru").
// Post(fmt.Sprintf("/api/v5/integration-modules/%s/edit", integrationModule.Code)).
// MatchType("url").
// BodyString(p.Encode()).
// MatchHeader("X-API-KEY", "test").
// Reply(201).
// BodyString(`{"success": true, "info": {"baseUrl": "http://test.te", "token": "test"}}`)
//
// req, err := http.NewRequest("POST", "/create/",
// strings.NewReader(
// `{"api_url": "https://test2.retailcrm.ru","api_key": "test"}`,
// ))
// if err != nil {
// t.Fatal(err)
// }
//
// rr := httptest.NewRecorder()
// handler := http.HandlerFunc(createHandler)
// handler.ServeHTTP(rr, req)
//
// if rr.Code != http.StatusFound {
// t.Errorf("handler returned wrong status code: got %v want %v",
// rr.Code, http.StatusFound)
// }
//}
func TestRouting_activityHandler(t *testing.T) {
req, err := http.NewRequest("POST", "/actions/activity",
strings.NewReader(
@ -240,8 +182,41 @@ func TestRouting_activityHandler(t *testing.T) {
handler := http.HandlerFunc(activityHandler)
handler.ServeHTTP(rr, req)
if rr.Code != http.StatusOK {
t.Errorf("handler returned wrong status code: got %v want %v",
rr.Code, http.StatusOK)
}
assert.Equal(t, http.StatusOK, rr.Code,
fmt.Sprintf("handler returned wrong status code: got %v want %v", rr.Code, http.StatusOK))
}
func TestRouting_TranslateLoader(t *testing.T) {
type m map[string]string
te := [][]string{}
dt := "translate"
files, err := ioutil.ReadDir(dt)
if err != nil {
t.Fatal(err)
}
for _, f := range files {
ms := m{}
if !f.IsDir() {
res, err := ioutil.ReadFile(fmt.Sprintf("%s/%s", dt, f.Name()))
if err != nil {
t.Fatal(err)
}
err = yaml.Unmarshal(res, &ms)
if err != nil {
t.Fatal(err)
}
keys := []string{}
for kms := range ms {
keys = append(keys, kms)
}
sort.Strings(keys)
te = append(te, keys)
}
}
}

View File

@ -1,10 +1,9 @@
package main
import (
"net/http"
"encoding/json"
"io/ioutil"
"net/http"
"strconv"
"time"
@ -33,26 +32,26 @@ func GetBotName(bot *tgbotapi.BotAPI) string {
}
func telegramWebhookHandler(w http.ResponseWriter, r *http.Request, token string) {
b := getBotByToken(token)
if b.ID == 0 {
c, err := getConnectionByBotToken(token)
if err != nil {
raven.CaptureErrorAndWait(err, nil)
logger.Error(token, err.Error())
w.WriteHeader(http.StatusInternalServerError)
return
}
if len(c.Bots) == 0 {
logger.Error(token, "missing")
w.WriteHeader(http.StatusBadRequest)
return
}
if !b.Active {
if !c.Bots[0].Active {
logger.Error(token, "deactivated")
w.WriteHeader(http.StatusBadRequest)
return
}
c := getConnectionById(b.ConnectionID)
if c.MGURL == "" || c.MGToken == "" {
logger.Error(token, "MGURL or MGToken is empty")
w.WriteHeader(http.StatusBadRequest)
return
}
var update tgbotapi.Update
bytes, err := ioutil.ReadAll(r.Body)
@ -90,11 +89,12 @@ func telegramWebhookHandler(w http.ResponseWriter, r *http.Request, token string
Lastname: update.Message.From.LastName,
Language: update.Message.From.LanguageCode,
},
Channel: b.Channel,
Channel: c.Bots[0].Channel,
}
data, st, err := client.Messages(snd)
if err != nil {
raven.CaptureErrorAndWait(err, nil)
logger.Error(token, err.Error(), st, data)
w.WriteHeader(http.StatusInternalServerError)
return
@ -110,11 +110,12 @@ func telegramWebhookHandler(w http.ResponseWriter, r *http.Request, token string
Text: update.EditedMessage.Text,
},
},
Channel: b.Channel,
Channel: c.Bots[0].Channel,
}
data, st, err := client.UpdateMessages(snd)
if err != nil {
raven.CaptureErrorAndWait(err, nil)
logger.Error(token, err.Error(), st, data)
w.WriteHeader(http.StatusInternalServerError)
return
@ -163,6 +164,7 @@ func mgWebhookHandler(w http.ResponseWriter, r *http.Request) {
if msg.Type == "message_sent" {
msg, err := bot.Send(tgbotapi.NewMessage(msg.Data.ExternalChatID, msg.Data.Content))
if err != nil {
raven.CaptureErrorAndWait(err, nil)
logger.Error(err)
w.WriteHeader(http.StatusBadRequest)
return
@ -176,6 +178,7 @@ func mgWebhookHandler(w http.ResponseWriter, r *http.Request) {
if msg.Type == "message_updated" {
msg, err := bot.Send(tgbotapi.NewEditMessageText(msg.Data.ExternalChatID, uid, msg.Data.Content))
if err != nil {
raven.CaptureErrorAndWait(err, nil)
logger.Error(err)
w.WriteHeader(http.StatusBadRequest)
return
@ -189,6 +192,7 @@ func mgWebhookHandler(w http.ResponseWriter, r *http.Request) {
if msg.Type == "message_deleted" {
msg, err := bot.Send(tgbotapi.NewDeleteMessage(msg.Data.ExternalChatID, uid))
if err != nil {
raven.CaptureErrorAndWait(err, nil)
logger.Error(err)
w.WriteHeader(http.StatusBadRequest)
return