- added .dev.env, added to .gitignore;

- helper function to work with github naming (regexp mustcompile);
- move telegram requests handlers into a separate file/module;
- remove unused YouTrack interface;
- review Git struct, review init function,
- create separate git instance for each request;
- using env vars into Cloud functions;
- create domain package with only Git struct description..;
- add Controller package with all current UseCases;
This commit is contained in:
naudachu 2023-06-12 20:42:23 +05:00
parent d9883b59c3
commit fd1c8fc894
9 changed files with 272 additions and 82 deletions

1
.gitignore vendored
View File

@ -1,2 +1,3 @@
.vscode/ .vscode/
.env .env
.dev.env

83
controller/controller.go Normal file
View File

@ -0,0 +1,83 @@
package controller
import (
"fmt"
"os"
"sync"
"ticket-creator/ext"
)
func Workflow(name string) (string, error) {
yt := ext.NewYT(os.Getenv("YT_URL"), os.Getenv("YT_TOKEN"))
projects, err := yt.GetProjects()
if err != nil {
return "", err
}
issue, err := yt.CreateIssue(projects[0].ID, name)
if err != nil {
return "", err
}
if issue != nil {
var (
git, gitBuild, folder string
)
var wg sync.WaitGroup
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)
}
return issue.Key, nil
}
func CreateRepo(name string, param uint) (string, error) {
gb := ext.NewGit(os.Getenv("GIT_BASE_URL"), os.Getenv("GIT_TOKEN"))
repo, err := gb.NewRepo(name)
gb.AppsAsCollaboratorTo(repo)
// Result string formatting:
if repo != nil {
switch param {
case 0:
return repo.HtmlUrl, err
case 1:
return fmt.Sprintf("ssh://%s/%s.git", repo.SshUrl, repo.FullName), err
default:
return repo.CloneUrl, err
}
}
return "", err
}
func CreateFolder(name string) string {
oc := ext.NewCloud(os.Getenv("CLOUD_BASE_URL"), os.Getenv("CLOUD_USER"), os.Getenv("CLOUD_PASS"))
cloud, _ := oc.CreateFolder(name)
if cloud != nil {
return cloud.FolderPath
}
return "no-folder"
}

11
domain/git.go Normal file
View File

@ -0,0 +1,11 @@
package domain
type Git struct {
Name string `json:"name"`
FullName string `json:"full_name"`
Private bool `json:"private"`
Url string `json:"url"`
CloneUrl string `json:"clone_url"`
HtmlUrl string `json:"Html_url"`
SshUrl string `json:"ssh_url"`
}

View File

@ -1,23 +1,18 @@
package ext package ext
import ( import (
"os"
"time" "time"
"github.com/imroc/req/v3"
) )
type cloud struct { func NewCloud(base, user, pass string) *Client {
*req.Client
}
func NewCloud(base, user, pass string) *cloud {
client := NewClient(). client := NewClient().
SetTimeout(5*time.Second). SetTimeout(5*time.Second).
SetCommonBasicAuth(user, pass). SetCommonBasicAuth(user, pass).
SetBaseURL(base) SetBaseURL(base)
return &cloud{ return &Client{
client, client,
} }
} }
@ -27,24 +22,20 @@ type Cloud struct {
FolderPath string FolderPath string
} }
func (c *cloud) CreateFolder(name string) (*Cloud, error) { func (c *Client) CreateFolder(name string) (*Cloud, error) {
const (
HOMEPATH = "/remote.php/dav/files/naudachu/%23mobiledev/"
PATH = "/apps/files/?dir=/%23mobiledev/"
)
cloud := Cloud{ cloud := Cloud{
FolderName: name, FolderName: name,
FolderPath: "", FolderPath: "",
} }
pathName := HOMEPATH + name pathName := os.Getenv("HOMEPATH") + name
resp, err := c.R(). resp, err := c.R().
Send("MKCOL", pathName) Send("MKCOL", pathName)
if resp.IsSuccessState() { if resp.IsSuccessState() {
cloud.FolderPath = c.BaseURL + PATH + name cloud.FolderPath = c.BaseURL + os.Getenv("FOLDER_PATH") + name
} }
return &cloud, err return &cloud, err

View File

@ -2,18 +2,18 @@ package ext
import ( import (
"log" "log"
"regexp" "os"
"strings" "ticket-creator/domain"
"ticket-creator/helpers"
"time" "time"
"github.com/imroc/req/v3"
) )
type gitbucket struct { type Git struct {
*req.Client *Client
*domain.Git
} }
func NewGitBucket(base, token string) *gitbucket { func NewGit(base, token string) *Git {
headers := map[string]string{ headers := map[string]string{
"Accept": "application/vnd.github+json", "Accept": "application/vnd.github+json",
"Authorization": "Token " + token, "Authorization": "Token " + token,
@ -25,80 +25,65 @@ func NewGitBucket(base, token string) *gitbucket {
SetTimeout(5 * time.Second). SetTimeout(5 * time.Second).
SetCommonHeaders(headers). SetCommonHeaders(headers).
SetBaseURL(base) SetBaseURL(base)
return &gitbucket{
client, return &Git{
Client: &Client{client},
Git: &domain.Git{
Name: "",
FullName: "",
Private: true,
Url: "",
CloneUrl: "",
HtmlUrl: "",
SshUrl: "",
},
} }
} }
type Repo struct {
Name string `json:"name"`
FullName string `json:"full_name"`
Private bool `json:"private"`
Url string `json:"url"`
CloneUrl string `json:"clone_url"`
HtmlUrl string `json:"Html_url"`
SshUrl string `json:"ssh_url"`
}
func gitHubLikeNaming(input string) string {
// Remove leading and trailing whitespace
input = strings.TrimSpace(input)
// Replace non-Latin letters with spaces
reg := regexp.MustCompile("[^a-zA-Z]+")
input = reg.ReplaceAllString(input, " ")
// Split into words and capitalize first letter of each
words := strings.Fields(input)
for i, word := range words {
words[i] = strings.ToLower(word)
}
// Join words and return
return strings.Join(words, "-")
}
func (gb *gitbucket) NewRepo(name string) (*Repo, error) {
name = gitHubLikeNaming(name)
type request struct { type request struct {
Name string `json:"name"` Name string `json:"name"`
Private bool `json:"private"` Private bool `json:"private"`
} }
payload := request{ type permissionRequest struct {
Name: name, Perm string `json:"permission"`
Private: false,
} }
var git Repo func (gb *Git) NewRepo(name string) (*domain.Git, error) {
name = helpers.GitNaming(name)
payload := request{
Name: name,
Private: true,
}
var git domain.Git
resp, err := gb.R(). resp, err := gb.R().
SetBody(&payload). SetBody(&payload).
SetSuccessResult(&git). SetSuccessResult(&git).
Post("/user/repos") Post("/user/repos")
//Post("/orgs/apps/repos")
if err != nil { if err != nil {
log.Print(resp) log.Print(resp)
return nil, err
}
type permissionRequest struct {
Perm string `json:"permission"`
}
payloadPermission := permissionRequest{
Perm: "admin",
}
resp, err = gb.R().
SetBody(&payloadPermission).
Put("/repos/naudachu/" + name + "/collaborators/apps")
if err != nil {
log.Print(resp)
return nil, err
} }
return &git, err return &git, err
} }
func (gb *Client) AppsAsCollaboratorTo(git *domain.Git) (*domain.Git, error) {
payloadPermission := permissionRequest{
Perm: "admin",
}
resp, err := gb.R().
SetBody(&payloadPermission).
Put("/repos/" + os.Getenv("GIT_USER") + "/" + git.Name + "/collaborators/apps")
if err != nil {
log.Print(resp)
}
return git, err
}

View File

@ -8,12 +8,6 @@ import (
"github.com/imroc/req/v3" "github.com/imroc/req/v3"
) )
type IYouTrack interface {
GetProjects() ([]Project, error)
CreateIssue(projectID, name string) (*IssueCreateRequest, error)
UpdateIssue(issue *IssueCreateRequest, folder, git, gitBuild string) (*IssueUpdateRequest, error)
}
type youtrack struct { type youtrack struct {
*req.Client *req.Client
} }

78
handler/handler.go Normal file
View File

@ -0,0 +1,78 @@
package handler
import (
"context"
"errors"
"fmt"
"strings"
"ticket-creator/controller"
"github.com/mr-linch/go-tg"
"github.com/mr-linch/go-tg/tgb"
)
func errorAnswer(errorMsg string) string {
return tg.HTML.Text(
tg.HTML.Line(
tg.HTML.Italic(errorMsg),
),
)
}
func NewTicketHandler(ctx context.Context, mu *tgb.MessageUpdate) error {
str := strings.Replace(mu.Text, "/new", "", 1)
if str == "" {
return errors.New("empty command provided")
}
issueKeyStr, err := controller.Workflow(str)
if err != nil {
return mu.Answer(errorAnswer(err.Error())).ParseMode(tg.HTML).DoVoid(ctx)
}
return mu.Answer(newTicketAnswer(issueKeyStr)).ParseMode(tg.HTML).DoVoid(ctx)
}
func newTicketAnswer(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 NewRepoHandler(ctx context.Context, mu *tgb.MessageUpdate) error {
str := strings.Replace(mu.Text, "/repo", "", 1)
if str == "" {
return errors.New("empty command provided")
}
repoStr, err := controller.CreateRepo(str, 0)
if err != nil {
return mu.Answer(errorAnswer(err.Error())).ParseMode(tg.HTML).DoVoid(ctx)
}
return mu.Answer(newRepoAnswer(repoStr)).ParseMode(tg.HTML).DoVoid(ctx)
}
func newRepoAnswer(name string) string {
return tg.HTML.Text(
tg.HTML.Line(
"Repo ",
name,
"has been created!",
),
)
}
func PingHandler(ctx context.Context, mu *tgb.MessageUpdate) error {
return mu.Answer("pong").DoVoid(ctx)
}

24
helpers/helpers.go Normal file
View File

@ -0,0 +1,24 @@
package helpers
import (
"regexp"
"strings"
)
func GitNaming(input string) string {
// Remove leading and trailing whitespace
input = strings.TrimSpace(input)
// Replace non-Latin letters with spaces
reg := regexp.MustCompile("[^a-zA-Z0-9]+")
input = strings.TrimSpace(reg.ReplaceAllString(input, " "))
// Split into words
words := strings.Fields(input)
for i, word := range words {
words[i] = strings.ToLower(word)
}
// Join words and return
return strings.Join(words, "-")
}

23
helpers/helpers_test.go Normal file
View File

@ -0,0 +1,23 @@
package helpers
import "testing"
type test struct {
arg, expected string
}
var tests = []test{
{" App-21", "app-21"},
{"App-21-build", "app-21-build"},
{" hello - biatch", "hello-biatch"},
{" `~!@#$%^&*()=+ abc `~!@#$%^&*()=+ 2-22 ", "abc-2-22"},
}
func TestGitNaming(t *testing.T) {
for _, test := range tests {
if output := GitNaming(test.arg); output != test.expected {
t.Errorf("Output %q not equal to expected %q", output, test.expected)
}
}
}