- setup answer messageas a separate function;
- async git/cloud requests; - error handling middleware;
This commit is contained in:
parent
b8e3dd5392
commit
fa5ba4742a
|
|
@ -0,0 +1,11 @@
|
||||||
|
CLOUD_BASE_URL = 'http://82.151.222.22:7000'
|
||||||
|
CLOUD_USER = 'naudachu'
|
||||||
|
CLOUD_PASS = '123456'
|
||||||
|
|
||||||
|
GIT_BASE_URL = 'http://82.151.222.22:7001/api/v3'
|
||||||
|
GIT_TOKEN = '7bd9d60cf7b9e78a4f2f1aea4734fbfa1052e419'
|
||||||
|
|
||||||
|
YT_URL = 'https://marlerino.youtrack.cloud/api'
|
||||||
|
YT_TOKEN = 'perm:bmF1ZGFjaHU=.NTYtMQ==.4TVHQx65u4EKnCGjadeMB1NMAmSHSL'
|
||||||
|
|
||||||
|
TG_API = '6002875059:AAFp1ZR9Y68oaqSL6vTNQdhrVgcM_yHouCY'
|
||||||
80
cmd/main.go
80
cmd/main.go
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"ticket-creator/domain"
|
"ticket-creator/domain"
|
||||||
|
|
@ -18,7 +19,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
env(".env")
|
env(".env.dev")
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
ctx, cancel := signal.NotifyContext(ctx, os.Interrupt, os.Kill, syscall.SIGTERM)
|
ctx, cancel := signal.NotifyContext(ctx, os.Interrupt, os.Kill, syscall.SIGTERM)
|
||||||
|
|
@ -37,6 +38,24 @@ func env(envFilePath string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func answer(name string) string {
|
||||||
|
return tg.HTML.Text(
|
||||||
|
tg.HTML.Line(
|
||||||
|
"🤘 Ticket ",
|
||||||
|
tg.HTML.Link(name, fmt.Sprintf("https://marlerino.youtrack.cloud/issue/%s", name)),
|
||||||
|
"has been created!",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func errorAnswer(errorMsg string) string {
|
||||||
|
return tg.HTML.Text(
|
||||||
|
tg.HTML.Line(
|
||||||
|
tg.HTML.Italic(errorMsg),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
func run(ctx context.Context) error {
|
func run(ctx context.Context) error {
|
||||||
client := tg.New(os.Getenv("TG_API"))
|
client := tg.New(os.Getenv("TG_API"))
|
||||||
|
|
||||||
|
|
@ -47,16 +66,16 @@ func run(ctx context.Context) error {
|
||||||
if str == "" {
|
if str == "" {
|
||||||
return errors.New("empty command provided")
|
return errors.New("empty command provided")
|
||||||
}
|
}
|
||||||
issueKeyStr := workflow(str)
|
issueKeyStr, err := workflow(str)
|
||||||
|
if err != nil {
|
||||||
|
return mu.Answer(errorAnswer(err.Error())).ParseMode(tg.HTML).DoVoid(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
return mu.Answer(tg.HTML.Text(
|
return mu.Answer(answer(issueKeyStr)).ParseMode(tg.HTML).DoVoid(ctx)
|
||||||
tg.HTML.Line(
|
}, tgb.TextHasPrefix("/new")).
|
||||||
"🤘 Ticket ",
|
Message(func(ctx context.Context, mu *tgb.MessageUpdate) error {
|
||||||
tg.HTML.Link(issueKeyStr, fmt.Sprintf("https://marlerino.youtrack.cloud/issue/%s", issueKeyStr)),
|
return mu.Answer("pong").DoVoid(ctx)
|
||||||
"has been created!",
|
}, tgb.Command("ping"))
|
||||||
),
|
|
||||||
)).ParseMode(tg.HTML).DoVoid(ctx)
|
|
||||||
}, tgb.TextHasPrefix("/new"))
|
|
||||||
|
|
||||||
return tgb.NewPoller(
|
return tgb.NewPoller(
|
||||||
router,
|
router,
|
||||||
|
|
@ -64,17 +83,44 @@ func run(ctx context.Context) error {
|
||||||
).Run(ctx)
|
).Run(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func workflow(name string) string {
|
func workflow(name string) (string, error) {
|
||||||
yt := domain.NewYT(os.Getenv("YT_URL"), os.Getenv("YT_TOKEN"))
|
yt := domain.NewYT(os.Getenv("YT_URL"), os.Getenv("YT_TOKEN"))
|
||||||
projects := yt.GetProjects()
|
|
||||||
issue := yt.CreateIssue(projects[0].ID, name)
|
projects, err := yt.GetProjects()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
issue, err := yt.CreateIssue(projects[1].ID, name)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
if issue != nil {
|
if issue != nil {
|
||||||
git := createRepo(issue.Key, 0)
|
var (
|
||||||
gitBuild := createRepo(issue.Key+"-build", 1)
|
wg sync.WaitGroup
|
||||||
folder := createFolder(issue.Key + " - " + issue.Summary)
|
git, gitBuild, folder string
|
||||||
|
)
|
||||||
|
|
||||||
|
wg.Add(3)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
git = createRepo(issue.Key, 0)
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
gitBuild = createRepo(issue.Key+"-build", 1)
|
||||||
|
}()
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
folder = createFolder(issue.Key + " - " + issue.Summary)
|
||||||
|
}()
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
yt.UpdateIssue(issue, folder, git, gitBuild)
|
yt.UpdateIssue(issue, folder, git, gitBuild)
|
||||||
}
|
}
|
||||||
return issue.Key
|
return issue.Key, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func createRepo(name string, param uint) string {
|
func createRepo(name string, param uint) string {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
package domain
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/imroc/req/v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Client struct {
|
||||||
|
*req.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewClient() *Client {
|
||||||
|
return &Client{req.C().
|
||||||
|
OnAfterResponse(func(client *req.Client, resp *req.Response) error {
|
||||||
|
if resp.Err != nil {
|
||||||
|
if dump := resp.Dump(); dump != "" {
|
||||||
|
resp.Err = fmt.Errorf("%s\nraw content:\n%s", resp.Err.Error(), resp.Dump())
|
||||||
|
}
|
||||||
|
return nil // Skip the following logic if there is an underlying error.
|
||||||
|
}
|
||||||
|
|
||||||
|
if !resp.IsSuccessState() {
|
||||||
|
resp.Err = fmt.Errorf("bad response, raw content:\n%s", resp.Dump())
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,26 +1,24 @@
|
||||||
package domain
|
package domain
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/imroc/req/v3"
|
"github.com/imroc/req/v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
type cloud struct {
|
type cloud struct {
|
||||||
baseUrl string
|
*req.Client
|
||||||
client *req.Client
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCloud(base, user, pass string) *cloud {
|
func NewCloud(base, user, pass string) *cloud {
|
||||||
|
|
||||||
client := req.C().
|
client := NewClient().
|
||||||
SetTimeout(5*time.Second).
|
SetTimeout(5*time.Second).
|
||||||
SetCommonBasicAuth(user, pass).
|
SetCommonBasicAuth(user, pass).
|
||||||
SetBaseURL(base)
|
SetBaseURL(base)
|
||||||
|
|
||||||
return &cloud{
|
return &cloud{
|
||||||
baseUrl: base,
|
client,
|
||||||
client: client,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -42,17 +40,12 @@ func (c *cloud) CreateFolder(name string) (*Cloud, error) {
|
||||||
|
|
||||||
pathName := HOMEPATH + name
|
pathName := HOMEPATH + name
|
||||||
|
|
||||||
resp, err := c.client.R().
|
resp, err := c.R().
|
||||||
Send("MKCOL", pathName)
|
Send("MKCOL", pathName)
|
||||||
|
|
||||||
// Check if request failed or response status is not Ok;
|
if resp.IsSuccessState() {
|
||||||
if !resp.IsSuccessState() || err != nil {
|
cloud.FolderPath = c.BaseURL + PATH + name
|
||||||
log.Print("bad status:", resp.Status)
|
|
||||||
log.Print(resp.Dump())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if resp.StatusCode == 201 {
|
|
||||||
cloud.FolderPath = c.baseUrl + PATH + name
|
|
||||||
}
|
|
||||||
return &cloud, err
|
return &cloud, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,13 @@
|
||||||
package domain
|
package domain
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/imroc/req/v3"
|
"github.com/imroc/req/v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
type gitbucket struct {
|
type gitbucket struct {
|
||||||
client *req.Client
|
*req.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewGitBucket(base, token string) *gitbucket {
|
func NewGitBucket(base, token string) *gitbucket {
|
||||||
|
|
@ -19,12 +18,12 @@ func NewGitBucket(base, token string) *gitbucket {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
}
|
}
|
||||||
|
|
||||||
client := req.C().
|
client := NewClient().
|
||||||
SetTimeout(5 * time.Second).
|
SetTimeout(5 * time.Second).
|
||||||
SetCommonHeaders(headers).
|
SetCommonHeaders(headers).
|
||||||
SetBaseURL(base)
|
SetBaseURL(base)
|
||||||
return &gitbucket{
|
return &gitbucket{
|
||||||
client: client,
|
client,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -52,32 +51,29 @@ func (gb *gitbucket) NewRepo(name string) (*Repo, error) {
|
||||||
|
|
||||||
var git Repo
|
var git Repo
|
||||||
|
|
||||||
resp, err := gb.client.R().
|
_, err := gb.R().
|
||||||
SetBody(&payload).
|
SetBody(&payload).
|
||||||
SetSuccessResult(&git).
|
SetSuccessResult(&git).
|
||||||
Post("/user/repos")
|
Post("/user/repos")
|
||||||
|
|
||||||
// Check if request failed or response status is not Ok;
|
if err != nil {
|
||||||
if !resp.IsSuccessState() || err != nil {
|
return nil, err
|
||||||
log.Print("bad status:", resp.Status)
|
|
||||||
log.Print(resp.Dump())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type permissionRequest struct {
|
type permissionRequest struct {
|
||||||
perm string `json:"permission"`
|
Perm string `json:"permission"`
|
||||||
}
|
}
|
||||||
|
|
||||||
payloadPermission := permissionRequest{
|
payloadPermission := permissionRequest{
|
||||||
perm: "admin",
|
Perm: "admin",
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err = gb.client.R().
|
_, err = gb.R().
|
||||||
SetBody(&payloadPermission).
|
SetBody(&payloadPermission).
|
||||||
Post("/repos/naudachu/" + name + "/collaborators/apps")
|
Put("/repos/naudachu/" + name + "/collaborators/apps")
|
||||||
|
|
||||||
if !resp.IsSuccessState() || err != nil {
|
if err != nil {
|
||||||
log.Print("bad status:", resp.Status)
|
return nil, err
|
||||||
log.Print(resp.Dump())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return &git, err
|
return &git, err
|
||||||
|
|
|
||||||
51
domain/yt.go
51
domain/yt.go
|
|
@ -1,6 +1,7 @@
|
||||||
package domain
|
package domain
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
|
@ -8,7 +9,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type youtrack struct {
|
type youtrack struct {
|
||||||
client *req.Client
|
*req.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewYT(base, token string) *youtrack {
|
func NewYT(base, token string) *youtrack {
|
||||||
|
|
@ -17,14 +18,14 @@ func NewYT(base, token string) *youtrack {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
}
|
}
|
||||||
|
|
||||||
client := req.C().
|
client := NewClient().
|
||||||
SetTimeout(15 * time.Second).
|
SetTimeout(15 * time.Second).
|
||||||
SetCommonHeaders(headers).
|
SetCommonHeaders(headers).
|
||||||
SetBaseURL(base).
|
SetBaseURL(base).
|
||||||
SetCommonBearerAuthToken(token)
|
SetCommonBearerAuthToken(token)
|
||||||
|
|
||||||
return &youtrack{
|
return &youtrack{
|
||||||
client: client,
|
client,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -36,21 +37,22 @@ type Project struct {
|
||||||
|
|
||||||
// GetProjects
|
// GetProjects
|
||||||
// provides an array of existing projects;
|
// provides an array of existing projects;
|
||||||
func (yt *youtrack) GetProjects() []Project {
|
func (yt *youtrack) GetProjects() ([]Project, error) {
|
||||||
|
|
||||||
var projects []Project
|
var projects []Project
|
||||||
|
|
||||||
resp, err := yt.client.R().
|
_, err := yt.R().
|
||||||
EnableDump().
|
EnableDump().
|
||||||
SetQueryParam("fields", "id,name,shortName").
|
SetQueryParam("fields", "id,name,shortName").
|
||||||
SetSuccessResult(&projects).
|
SetSuccessResult(&projects).
|
||||||
Get("/admin/projects")
|
Get("/admin/projects")
|
||||||
|
|
||||||
if !resp.IsSuccessState() || err != nil {
|
// Check if the request failed;
|
||||||
log.Print("bad status:", resp.Status)
|
if err != nil {
|
||||||
log.Print(resp.Dump())
|
return nil, fmt.Errorf("some problem with YT request. error message: %v", err)
|
||||||
}
|
}
|
||||||
return projects
|
|
||||||
|
return projects, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type ProjectID struct {
|
type ProjectID struct {
|
||||||
|
|
@ -67,7 +69,7 @@ type IssueCreateRequest struct {
|
||||||
|
|
||||||
// CreateIssue
|
// CreateIssue
|
||||||
// example: newIssue := yt.CreateIssue("0-2", "Summary", "Description");
|
// example: newIssue := yt.CreateIssue("0-2", "Summary", "Description");
|
||||||
func (yt *youtrack) CreateIssue(projectID, name string) *IssueCreateRequest {
|
func (yt *youtrack) CreateIssue(projectID, name string) (*IssueCreateRequest, error) {
|
||||||
|
|
||||||
// Create an issue with the provided:, Project ID, Name, Description;
|
// Create an issue with the provided:, Project ID, Name, Description;
|
||||||
issue := IssueCreateRequest{
|
issue := IssueCreateRequest{
|
||||||
|
|
@ -79,19 +81,18 @@ func (yt *youtrack) CreateIssue(projectID, name string) *IssueCreateRequest {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Push issue to the YT;
|
// Push issue to the YT;
|
||||||
resp, err := yt.client.R().
|
_, err := yt.R().
|
||||||
SetQueryParam("fields", "idReadable,id").
|
SetQueryParam("fields", "idReadable,id").
|
||||||
SetBody(&issue).
|
SetBody(&issue).
|
||||||
SetSuccessResult(&issue).
|
SetSuccessResult(&issue).
|
||||||
Post("/issues")
|
Post("/issues")
|
||||||
|
|
||||||
// Check if request failed or response status is not Ok;
|
// Check if the request failed;
|
||||||
if !resp.IsSuccessState() || err != nil {
|
if err != nil {
|
||||||
log.Print("bad status:", resp.Status)
|
return nil, fmt.Errorf("some problem with YT request. error message: %v", err)
|
||||||
log.Print(resp.Dump())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return &issue
|
return &issue, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type IssueUpdateRequest struct {
|
type IssueUpdateRequest struct {
|
||||||
|
|
@ -109,7 +110,7 @@ type CustomField struct {
|
||||||
Value string `json:"value"`
|
Value string `json:"value"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (yt *youtrack) UpdateIssue(issue *IssueCreateRequest, folder, git, gitBuild string) *IssueUpdateRequest {
|
func (yt *youtrack) UpdateIssue(issue *IssueCreateRequest, folder, git, gitBuild string) (*IssueUpdateRequest, error) {
|
||||||
// Set Folder, Git, GitBuild to the Issue:
|
// Set Folder, Git, GitBuild to the Issue:
|
||||||
update := IssueUpdateRequest{
|
update := IssueUpdateRequest{
|
||||||
IssueCreateRequest: *issue,
|
IssueCreateRequest: *issue,
|
||||||
|
|
@ -133,15 +134,21 @@ func (yt *youtrack) UpdateIssue(issue *IssueCreateRequest, folder, git, gitBuild
|
||||||
}
|
}
|
||||||
|
|
||||||
// Push issue update to YT
|
// Push issue update to YT
|
||||||
resp, err := yt.client.R().
|
resp, err := yt.R().
|
||||||
SetBody(&update).
|
SetBody(&update).
|
||||||
SetSuccessResult(&issue).
|
SetSuccessResult(&issue).
|
||||||
Post("/issues/" + issue.Key)
|
Post("/issues/" + issue.Key)
|
||||||
|
|
||||||
if !resp.IsSuccessState() || err != nil {
|
// Check if the request failed;
|
||||||
log.Print("bad status:", resp.Status)
|
if err != nil {
|
||||||
log.Print(resp.Dump())
|
return nil, fmt.Errorf("some problem with YT request. error message: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &update
|
if !resp.IsSuccessState() {
|
||||||
|
log.Print("bad status:", resp.Status)
|
||||||
|
log.Print(resp.Dump())
|
||||||
|
return nil, fmt.Errorf("YouTrack responded with %d", resp.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &update, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue