From ad1beb66dddfd14124d361218e8a4991490db6cb Mon Sep 17 00:00:00 2001 From: naudachu Date: Wed, 14 Jun 2023 22:21:58 +0500 Subject: [PATCH 01/11] - clean architecture implemeted! --- cmd/main.go | 20 ++++++-- controller/controller.go | 64 +++++++++++++++++------- ext/cloud.go | 30 +++++++++--- ext/git.go | 8 ++- ext/yt.go | 6 +++ handler/handler.go | 102 ++++++++++++++++++++++++++------------- 6 files changed, 166 insertions(+), 64 deletions(-) diff --git a/cmd/main.go b/cmd/main.go index a063e51..a62db91 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -16,7 +16,7 @@ import ( func main() { log.Print("started") - env(".env") + env(".dev.env") ctx := context.Background() ctx, cancel := signal.NotifyContext(ctx, os.Interrupt, os.Kill, syscall.SIGTERM) @@ -36,12 +36,24 @@ func env(envFilePath string) { } func run(ctx context.Context) error { + client := tg.New(os.Getenv("TG_API")) + h := handler.NewHandler( + os.Getenv("GIT_BASE_URL"), + os.Getenv("GIT_TOKEN"), + os.Getenv("CLOUD_BASE_URL"), + os.Getenv("CLOUD_USER"), + os.Getenv("CLOUD_PASS"), + os.Getenv("YT_URL"), + os.Getenv("YT_TOKEN"), + ) + router := tgb.NewRouter(). - Message(handler.NewTicketHandler, tgb.TextHasPrefix("/new")). - Message(handler.PingHandler, tgb.Command("ping")). - Message(handler.NewRepoHandler, tgb.TextHasPrefix("/repo")) + Message(h.NewTicketHandler, tgb.TextHasPrefix("/new")). + Message(h.PingHandler, tgb.Command("ping")). + Message(h.NewRepoHandler, tgb.TextHasPrefix("/repo")). + Message(h.NewFolderHandler, tgb.TextHasPrefix("/folder")) return tgb.NewPoller( router, diff --git a/controller/controller.go b/controller/controller.go index aabf8bd..0e66f3f 100644 --- a/controller/controller.go +++ b/controller/controller.go @@ -2,13 +2,40 @@ package controller import ( "fmt" - "os" "sync" "ticket-pimp/ext" ) -func Workflow(name string) (string, error) { - yt := ext.NewYT(os.Getenv("YT_URL"), os.Getenv("YT_TOKEN")) +type WorkflowController struct { + iGit ext.IGit + iCloud ext.ICloud + iYouTrack ext.IYouTrack +} + +type IWorkflowController interface { + Workflow(name string) (string, error) + CreateRepo(name string, param uint) (string, error) + CreateFolder(name string) (string, string, error) +} + +func NewWorkflowController( + gitBaseURL, + gitToken, + cloudBaseURL, + cloudAuthUser, + cloudAuthPass, + ytBaseURL, + ytToken string, +) *WorkflowController { + return &WorkflowController{ + iGit: ext.NewGit(gitBaseURL, gitToken), + iCloud: ext.NewCloud(cloudBaseURL, cloudAuthUser, cloudAuthPass), + iYouTrack: ext.NewYT(ytBaseURL, ytToken), + } +} + +func (wc *WorkflowController) Workflow(name string) (string, error) { + yt := wc.iYouTrack projects, err := yt.GetProjects() @@ -16,7 +43,7 @@ func Workflow(name string) (string, error) { return "", err } - issue, err := yt.CreateIssue(projects[0].ID, name) + issue, err := yt.CreateIssue(projects[1].ID, name) if err != nil { return "", err @@ -32,17 +59,17 @@ func Workflow(name string) (string, error) { go func() { defer wg.Done() - git, _ = CreateRepo(issue.Key, 0) + git, _ = wc.CreateRepo(issue.Key, 0) }() go func() { defer wg.Done() - gitBuild, _ = CreateRepo(issue.Key+"-build", 1) + gitBuild, _ = wc.CreateRepo(issue.Key+"-build", 1) }() go func() { defer wg.Done() - folder = CreateFolder(issue.Key + " - " + issue.Summary) + _, folder, _ = wc.CreateFolder(issue.Key + " - " + issue.Summary) }() wg.Wait() @@ -52,10 +79,12 @@ func Workflow(name string) (string, error) { 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) +func (wc *WorkflowController) CreateRepo(name string, param uint) (string, error) { + //Create git repository with iGit interface; + repo, err := wc.iGit.NewRepo(name) + + //Set 'apps' as collaborator to created repository; + wc.iGit.AppsAsCollaboratorTo(repo) // Result string formatting: if repo != nil { @@ -72,12 +101,13 @@ func CreateRepo(name string, param uint) (string, error) { return "", err } -func CreateFolder(name string) string { - oc := ext.NewCloud(os.Getenv("CLOUD_BASE_URL"), os.Getenv("CLOUD_USER"), os.Getenv("CLOUD_PASS")) +func (wc *WorkflowController) CreateFolder(name string) (string, string, error) { - cloud, _ := oc.CreateFolder(name) - if cloud != nil { - return cloud.FolderPath + //Create ownCloud folder w/ iCloud interface; + cloud, err := wc.iCloud.CreateFolder(name) + if cloud == nil { + return "", "", err } - return "no-folder" + + return cloud.FolderName, cloud.FolderPath, err } diff --git a/ext/cloud.go b/ext/cloud.go index 54d4add..834d817 100644 --- a/ext/cloud.go +++ b/ext/cloud.go @@ -2,27 +2,40 @@ package ext import ( "os" + "ticket-pimp/helpers" "time" ) -func NewCloud(base, user, pass string) *Client { +type Cloud struct { + //[ ] out in separate domain struct + FolderName string + FolderPath string + *Client +} + +type ICloud interface { + CreateFolder(name string) (*Cloud, error) +} + +func NewCloud(base, user, pass string) *Cloud { client := NewClient(). SetTimeout(5*time.Second). SetCommonBasicAuth(user, pass). SetBaseURL(base) - return &Client{ - client, + return &Cloud{ + FolderName: "", + FolderPath: "", + Client: &Client{ + client, + }, } } -type Cloud struct { - FolderName string - FolderPath string -} +func (c *Cloud) CreateFolder(name string) (*Cloud, error) { -func (c *Client) CreateFolder(name string) (*Cloud, error) { + name = helpers.GitNaming(name) cloud := Cloud{ FolderName: name, @@ -36,6 +49,7 @@ func (c *Client) CreateFolder(name string) (*Cloud, error) { if resp.IsSuccessState() { cloud.FolderPath = c.BaseURL + os.Getenv("FOLDER_PATH") + name + } return &cloud, err diff --git a/ext/git.go b/ext/git.go index f6bc9cf..a798ada 100644 --- a/ext/git.go +++ b/ext/git.go @@ -13,6 +13,11 @@ type Git struct { *domain.Git } +type IGit interface { + NewRepo(string) (*domain.Git, error) + AppsAsCollaboratorTo(*domain.Git) (*domain.Git, error) +} + func NewGit(base, token string) *Git { headers := map[string]string{ "Accept": "application/vnd.github+json", @@ -63,7 +68,6 @@ func (gb *Git) NewRepo(name string) (*domain.Git, error) { SetBody(&payload). SetSuccessResult(&git). Post("/user/repos") - //Post("/orgs/apps/repos") if err != nil { log.Print(resp) @@ -72,7 +76,7 @@ func (gb *Git) NewRepo(name string) (*domain.Git, error) { return &git, err } -func (gb *Client) AppsAsCollaboratorTo(git *domain.Git) (*domain.Git, error) { +func (gb *Git) AppsAsCollaboratorTo(git *domain.Git) (*domain.Git, error) { payloadPermission := permissionRequest{ Perm: "admin", } diff --git a/ext/yt.go b/ext/yt.go index 92e89c3..7f54fb9 100644 --- a/ext/yt.go +++ b/ext/yt.go @@ -12,6 +12,12 @@ type youtrack struct { *req.Client } +type IYouTrack interface { + GetProjects() ([]Project, error) + CreateIssue(projectID, name string) (*IssueCreateRequest, error) + UpdateIssue(issue *IssueCreateRequest, folder, git, gitBuild string) (*IssueUpdateRequest, error) +} + func NewYT(base, token string) *youtrack { headers := map[string]string{ "Accept": "application/json", diff --git a/handler/handler.go b/handler/handler.go index ef86364..f134246 100644 --- a/handler/handler.go +++ b/handler/handler.go @@ -11,6 +11,73 @@ import ( "github.com/mr-linch/go-tg/tgb" ) +type Handler struct { + workflow controller.IWorkflowController +} + +func NewHandler(gitBaseURL, gitToken, cloudBaseURL, cloudAuthUser, cloudAuthPass, ytBaseURL, ytToken string) *Handler { + return &Handler{ + workflow: controller.NewWorkflowController(gitBaseURL, gitToken, cloudBaseURL, cloudAuthUser, cloudAuthPass, ytBaseURL, ytToken), + } +} + +func (h *Handler) PingHandler(ctx context.Context, mu *tgb.MessageUpdate) error { + return mu.Answer("pong").DoVoid(ctx) +} + +func newRepoAnswer(name string) string { + return tg.HTML.Text( + tg.HTML.Line( + "Repo ", + name, + "has been created!", + ), + ) +} + +func (h *Handler) 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 := h.workflow.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 newFolderAnswer(name string) string { + return tg.HTML.Text( + tg.HTML.Line( + "Folder ", + name, + "has been created!", + ), + ) +} +func (h *Handler) NewFolderHandler(ctx context.Context, mu *tgb.MessageUpdate) error { + + str := strings.Replace(mu.Text, "/folder", "", 1) + + if str == "" { + return errors.New("empty command provided") + } + + nameStr, _, err := h.workflow.CreateFolder(str) + + if err != nil { + return mu.Answer(errorAnswer(err.Error())).ParseMode(tg.HTML).DoVoid(ctx) + } + + return mu.Answer(newFolderAnswer(nameStr)).ParseMode(tg.HTML).DoVoid(ctx) +} + func errorAnswer(errorMsg string) string { return tg.HTML.Text( tg.HTML.Line( @@ -19,7 +86,7 @@ func errorAnswer(errorMsg string) string { ) } -func NewTicketHandler(ctx context.Context, mu *tgb.MessageUpdate) error { +func (h *Handler) NewTicketHandler(ctx context.Context, mu *tgb.MessageUpdate) error { str := strings.Replace(mu.Text, "/new", "", 1) @@ -27,7 +94,7 @@ func NewTicketHandler(ctx context.Context, mu *tgb.MessageUpdate) error { return errors.New("empty command provided") } - issueKeyStr, err := controller.Workflow(str) + issueKeyStr, err := h.workflow.Workflow(str) if err != nil { return mu.Answer(errorAnswer(err.Error())).ParseMode(tg.HTML).DoVoid(ctx) @@ -45,34 +112,3 @@ func newTicketAnswer(name string) string { ), ) } - -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) -} From db69ef238ea3de133c3e175fbd54324aaf346cd0 Mon Sep 17 00:00:00 2001 From: naudachu Date: Wed, 21 Jun 2023 14:07:26 +0500 Subject: [PATCH 02/11] just...no message --- cmd/fileid.xml | 5 +++ controller/controller.go | 24 +++++++----- domain/cloud.go | 8 ++++ domain/youtrack.go | 32 ++++++++++++++++ ext/cloud.go | 83 ++++++++++++++++++++++++++++++++++------ ext/git.go | 12 +----- ext/yt.go | 57 +++++++-------------------- go.mod | 1 + go.sum | 2 + handler/handler.go | 32 ++++++++++------ helpers/helpers_test.go | 4 +- helpers/xml_test.go | 59 ++++++++++++++++++++++++++++ 12 files changed, 231 insertions(+), 88 deletions(-) create mode 100644 cmd/fileid.xml create mode 100644 domain/cloud.go create mode 100644 domain/youtrack.go create mode 100644 helpers/xml_test.go diff --git a/cmd/fileid.xml b/cmd/fileid.xml new file mode 100644 index 0000000..c0478a2 --- /dev/null +++ b/cmd/fileid.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/controller/controller.go b/controller/controller.go index 0e66f3f..0faba9d 100644 --- a/controller/controller.go +++ b/controller/controller.go @@ -3,6 +3,7 @@ package controller import ( "fmt" "sync" + d "ticket-pimp/domain" "ticket-pimp/ext" ) @@ -15,7 +16,7 @@ type WorkflowController struct { type IWorkflowController interface { Workflow(name string) (string, error) CreateRepo(name string, param uint) (string, error) - CreateFolder(name string) (string, string, error) + CreateFolder(name string) (*d.Cloud, error) } func NewWorkflowController( @@ -51,7 +52,8 @@ func (wc *WorkflowController) Workflow(name string) (string, error) { if issue != nil { var ( - git, gitBuild, folder string + git, gitBuild string + cloud *d.Cloud ) var wg sync.WaitGroup @@ -67,14 +69,14 @@ func (wc *WorkflowController) Workflow(name string) (string, error) { gitBuild, _ = wc.CreateRepo(issue.Key+"-build", 1) }() - go func() { + go func(ref **d.Cloud) { defer wg.Done() - _, folder, _ = wc.CreateFolder(issue.Key + " - " + issue.Summary) - }() + *ref, _ = wc.CreateFolder(issue.Key + " - " + issue.Summary) + }(&cloud) wg.Wait() - yt.UpdateIssue(issue, folder, git, gitBuild) + yt.UpdateIssue(issue, cloud.FolderURL, git, gitBuild) } return issue.Key, nil } @@ -101,13 +103,17 @@ func (wc *WorkflowController) CreateRepo(name string, param uint) (string, error return "", err } -func (wc *WorkflowController) CreateFolder(name string) (string, string, error) { +func (wc *WorkflowController) CreateFolder(name string) (*d.Cloud, error) { //Create ownCloud folder w/ iCloud interface; cloud, err := wc.iCloud.CreateFolder(name) if cloud == nil { - return "", "", err + return cloud, err } - return cloud.FolderName, cloud.FolderPath, err + /* [ ] Experimental call: + wc.iCloud.ShareToExternals(cloud) + */ + + return cloud, err } diff --git a/domain/cloud.go b/domain/cloud.go new file mode 100644 index 0000000..729b48f --- /dev/null +++ b/domain/cloud.go @@ -0,0 +1,8 @@ +package domain + +type Cloud struct { + FolderName string // "k" + FolderPath string // "/temp/k" + FolderURL string // "http://82.151.222.22:7000/apps/files/?dir=/temp/k" + PublicURL string +} diff --git a/domain/youtrack.go b/domain/youtrack.go new file mode 100644 index 0000000..6f47f2a --- /dev/null +++ b/domain/youtrack.go @@ -0,0 +1,32 @@ +package domain + +type Project struct { + ID string `json:"id"` + ShortName string `json:"shortName"` + Name string `json:"name"` +} + +type ProjectID struct { + ID string `json:"id"` +} + +type IssueCreateRequest struct { + ProjectID ProjectID `json:"project"` + Key string `json:"idReadable"` + ID string `json:"id"` + Summary string `json:"summary"` + Description string `json:"description"` +} + +// [ ] try `,omitempty` to remove extra struct; + +type IssueUpdateRequest struct { + IssueCreateRequest + CustomFields []CustomField `json:"customFields"` +} + +type CustomField struct { + Name string `json:"name"` + Type string `json:"$type"` + Value string `json:"value"` +} diff --git a/ext/cloud.go b/ext/cloud.go index 834d817..b93fa11 100644 --- a/ext/cloud.go +++ b/ext/cloud.go @@ -1,20 +1,23 @@ package ext import ( + "encoding/xml" + "fmt" + "io/ioutil" + "log" "os" + "ticket-pimp/domain" "ticket-pimp/helpers" "time" ) type Cloud struct { - //[ ] out in separate domain struct - FolderName string - FolderPath string *Client } type ICloud interface { - CreateFolder(name string) (*Cloud, error) + CreateFolder(name string) (*domain.Cloud, error) + ShareToExternals(cloud *domain.Cloud) (*domain.Cloud, error) } func NewCloud(base, user, pass string) *Cloud { @@ -25,32 +28,88 @@ func NewCloud(base, user, pass string) *Cloud { SetBaseURL(base) return &Cloud{ - FolderName: "", - FolderPath: "", Client: &Client{ client, }, } } -func (c *Cloud) CreateFolder(name string) (*Cloud, error) { +func (c *Cloud) CreateFolder(name string) (*domain.Cloud, error) { + rootDir := os.Getenv("ROOTDIR") name = helpers.GitNaming(name) - cloud := Cloud{ + cloud := domain.Cloud{ FolderName: name, - FolderPath: "", + FolderURL: "", } - pathName := os.Getenv("HOMEPATH") + name + requestPath := os.Getenv("HOMEPATH") + rootDir + name + cloud.FolderPath = os.Getenv("FOLDER_PATH") + rootDir + name resp, err := c.R(). - Send("MKCOL", pathName) + Send("MKCOL", requestPath) if resp.IsSuccessState() { - cloud.FolderPath = c.BaseURL + os.Getenv("FOLDER_PATH") + name + + cloud.FolderURL = c.BaseURL + cloud.FolderPath + + /* + type ResponseObj struct { + Multistatus struct { + Response struct { + Href struct { + Propstat struct { + Prop struct { + FileID int `json:"oc:fileid"` + } `json:"d:prop"` + } `json:"d:propstat"` + } `json:"d:href"` + } `json:"d:response"` + } `json:"d:multistatus"` + }*/ + + type ResponseObj struct { + XMLName xml.Name `xml:"d:multistatus"` + Multistatus struct { + XMLName xml.Name `xml:"d:multistatus"` + Response struct { + Href struct { + Propstat struct { + Prop struct { + FileID string `xml:"oc:fileid"` + } `xml:"d:prop"` + } `xml:"d:propstat"` + } `xml:"d:href"` + } `xml:"d:response"` + } `xml:"d:multistatus"` + } + + xmlFile, err := ioutil.ReadFile("./fileid.xml") + + if err != nil { + fmt.Println(err) + return nil, err // fix this return; + } + + var id ResponseObj + + resp, _ := c.R(). + SetBody(xmlFile). + Send("PROPFIND", os.Getenv("HOMEPATH")+os.Getenv("ROOTDIR")+cloud.FolderName) + + xmlEncodingErr := resp.UnmarshalXml(&id) + if xmlEncodingErr != nil { + log.Print(err) + } + + log.Print(resp) } return &cloud, err } + +func (c *Cloud) ShareToExternals(cloud *domain.Cloud) (*domain.Cloud, error) { + return nil, nil +} diff --git a/ext/git.go b/ext/git.go index a798ada..1fadb40 100644 --- a/ext/git.go +++ b/ext/git.go @@ -10,7 +10,6 @@ import ( type Git struct { *Client - *domain.Git } type IGit interface { @@ -33,15 +32,6 @@ func NewGit(base, token string) *Git { return &Git{ Client: &Client{client}, - Git: &domain.Git{ - Name: "", - FullName: "", - Private: true, - Url: "", - CloneUrl: "", - HtmlUrl: "", - SshUrl: "", - }, } } @@ -63,6 +53,7 @@ func (gb *Git) NewRepo(name string) (*domain.Git, error) { } var git domain.Git + git.Private = true resp, err := gb.R(). SetBody(&payload). @@ -77,6 +68,7 @@ func (gb *Git) NewRepo(name string) (*domain.Git, error) { } func (gb *Git) AppsAsCollaboratorTo(git *domain.Git) (*domain.Git, error) { + payloadPermission := permissionRequest{ Perm: "admin", } diff --git a/ext/yt.go b/ext/yt.go index 7f54fb9..0f4af58 100644 --- a/ext/yt.go +++ b/ext/yt.go @@ -5,6 +5,8 @@ import ( "log" "time" + d "ticket-pimp/domain" + "github.com/imroc/req/v3" ) @@ -13,9 +15,9 @@ type youtrack struct { } type IYouTrack interface { - GetProjects() ([]Project, error) - CreateIssue(projectID, name string) (*IssueCreateRequest, error) - UpdateIssue(issue *IssueCreateRequest, folder, git, gitBuild string) (*IssueUpdateRequest, error) + GetProjects() ([]d.Project, error) + CreateIssue(projectID, name string) (*d.IssueCreateRequest, error) + UpdateIssue(issue *d.IssueCreateRequest, folder, git, gitBuild string) (*d.IssueUpdateRequest, error) } func NewYT(base, token string) *youtrack { @@ -35,17 +37,11 @@ func NewYT(base, token string) *youtrack { } } -type Project struct { - ID string `json:"id"` - ShortName string `json:"shortName"` - Name string `json:"name"` -} - // GetProjects // provides an array of existing projects; -func (yt *youtrack) GetProjects() ([]Project, error) { +func (yt *youtrack) GetProjects() ([]d.Project, error) { - var projects []Project + var projects []d.Project _, err := yt.R(). EnableDump(). @@ -61,25 +57,13 @@ func (yt *youtrack) GetProjects() ([]Project, error) { return projects, nil } -type ProjectID struct { - ID string `json:"id"` -} - -type IssueCreateRequest struct { - ProjectID ProjectID `json:"project"` - Key string `json:"idReadable"` - ID string `json:"id"` - Summary string `json:"summary"` - Description string `json:"description"` -} - // CreateIssue // example: newIssue := yt.CreateIssue("0-2", "Summary", "Description"); -func (yt *youtrack) CreateIssue(projectID, name string) (*IssueCreateRequest, error) { +func (yt *youtrack) CreateIssue(projectID, name string) (*d.IssueCreateRequest, error) { // Create an issue with the provided:, Project ID, Name, Description; - issue := IssueCreateRequest{ - ProjectID: ProjectID{ + issue := d.IssueCreateRequest{ + ProjectID: d.ProjectID{ ID: projectID, //"id":"0-2" }, Summary: name, @@ -101,26 +85,11 @@ func (yt *youtrack) CreateIssue(projectID, name string) (*IssueCreateRequest, er return &issue, nil } -type IssueUpdateRequest struct { - IssueCreateRequest - CustomFields []CustomField `json:"customFields"` -} - -type CustomFields struct { - List []CustomField `json:"customFields"` -} - -type CustomField struct { - Name string `json:"name"` - Type string `json:"$type"` - Value string `json:"value"` -} - -func (yt *youtrack) UpdateIssue(issue *IssueCreateRequest, folder, git, gitBuild string) (*IssueUpdateRequest, error) { +func (yt *youtrack) UpdateIssue(issue *d.IssueCreateRequest, folder, git, gitBuild string) (*d.IssueUpdateRequest, error) { // Set Folder, Git, GitBuild to the Issue: - update := IssueUpdateRequest{ + update := d.IssueUpdateRequest{ IssueCreateRequest: *issue, - CustomFields: []CustomField{ + CustomFields: []d.CustomField{ { Name: "Директория графики", Type: "SimpleIssueCustomField", diff --git a/go.mod b/go.mod index 8b44822..9a3ff88 100644 --- a/go.mod +++ b/go.mod @@ -19,6 +19,7 @@ require ( github.com/quic-go/qtls-go1-19 v0.3.2 // indirect github.com/quic-go/qtls-go1-20 v0.2.2 // indirect github.com/quic-go/quic-go v0.35.1 // indirect + github.com/studio-b12/gowebdav v0.0.0-20230203202212-3282f94193f2 // indirect github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce // indirect golang.org/x/crypto v0.9.0 // indirect golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 // indirect diff --git a/go.sum b/go.sum index 9db009a..8242e11 100644 --- a/go.sum +++ b/go.sum @@ -38,6 +38,8 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= +github.com/studio-b12/gowebdav v0.0.0-20230203202212-3282f94193f2 h1:VsBj3UD2xyAOu7kJw6O/2jjG2UXLFoBzihqDU9Ofg9M= +github.com/studio-b12/gowebdav v0.0.0-20230203202212-3282f94193f2/go.mod h1:bHA7t77X/QFExdeAnDzK6vKM34kEZAcE1OX4MfiwjkE= github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce h1:fb190+cK2Xz/dvi9Hv8eCYJYvIGUTN2/KLq1pT6CjEc= github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce/go.mod h1:o8v6yHRoik09Xen7gje4m9ERNah1d1PPsVq1VEx9vE4= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= diff --git a/handler/handler.go b/handler/handler.go index f134246..32e8975 100644 --- a/handler/handler.go +++ b/handler/handler.go @@ -17,7 +17,14 @@ type Handler struct { func NewHandler(gitBaseURL, gitToken, cloudBaseURL, cloudAuthUser, cloudAuthPass, ytBaseURL, ytToken string) *Handler { return &Handler{ - workflow: controller.NewWorkflowController(gitBaseURL, gitToken, cloudBaseURL, cloudAuthUser, cloudAuthPass, ytBaseURL, ytToken), + workflow: controller.NewWorkflowController( + gitBaseURL, + gitToken, + cloudBaseURL, + cloudAuthUser, + cloudAuthPass, + ytBaseURL, + ytToken), } } @@ -52,15 +59,6 @@ func (h *Handler) NewRepoHandler(ctx context.Context, mu *tgb.MessageUpdate) err return mu.Answer(newRepoAnswer(repoStr)).ParseMode(tg.HTML).DoVoid(ctx) } -func newFolderAnswer(name string) string { - return tg.HTML.Text( - tg.HTML.Line( - "Folder ", - name, - "has been created!", - ), - ) -} func (h *Handler) NewFolderHandler(ctx context.Context, mu *tgb.MessageUpdate) error { str := strings.Replace(mu.Text, "/folder", "", 1) @@ -69,13 +67,23 @@ func (h *Handler) NewFolderHandler(ctx context.Context, mu *tgb.MessageUpdate) e return errors.New("empty command provided") } - nameStr, _, err := h.workflow.CreateFolder(str) + cloud, err := h.workflow.CreateFolder(str) if err != nil { return mu.Answer(errorAnswer(err.Error())).ParseMode(tg.HTML).DoVoid(ctx) } - return mu.Answer(newFolderAnswer(nameStr)).ParseMode(tg.HTML).DoVoid(ctx) + answer := tg.HTML.Text( + tg.HTML.Line( + "✨ Shiny folder", + tg.HTML.Link(cloud.FolderName, cloud.FolderURL), + "has been created!", + ), + ) + + return mu.Answer(answer). + ParseMode(tg.HTML). + DoVoid(ctx) } func errorAnswer(errorMsg string) string { diff --git a/helpers/helpers_test.go b/helpers/helpers_test.go index bf22d06..3e877a4 100644 --- a/helpers/helpers_test.go +++ b/helpers/helpers_test.go @@ -1,6 +1,8 @@ package helpers -import "testing" +import ( + "testing" +) type test struct { arg, expected string diff --git a/helpers/xml_test.go b/helpers/xml_test.go new file mode 100644 index 0000000..7efcd37 --- /dev/null +++ b/helpers/xml_test.go @@ -0,0 +1,59 @@ +package helpers + +import ( + "encoding/xml" + "fmt" + "log" + "testing" +) + +/* + + + + + /remote.php/dav/files/naudachu/temp/id/ + + + 33225 + + HTTP/1.1 200 OK + + + + +*/ + +type MultistatusObj struct { + XMLName xml.Name `xml:"multistatus"` + Multistatus struct { + ResponseObj + } +} + +type ResponseObj struct { + XMLName xml.Name `xml:"response"` + Response struct { + Content string `xml:",chardata"` + } +} + +const ( + EXAMPLE = "\n/remote.php/dav/files/naudachu/temp/id/33225HTTP/1.1 200 OK\n" +) + +func GetFileID(str string) string { + + var multi MultistatusObj + err := xml.Unmarshal([]byte(str), &multi) + if err != nil { + fmt.Print(err) + } + return multi.Multistatus.Response.Content +} + +func TestGetFileID(t *testing.T) { + str := GetFileID(EXAMPLE) + log.Print(str) + +} From 8b84480e741fe95d0d20ee75b6e84deb9837a8e9 Mon Sep 17 00:00:00 2001 From: naudachu Date: Wed, 21 Jun 2023 17:58:51 +0500 Subject: [PATCH 03/11] TestGetFileID is working, but FileID cannot be unmarshaled --- helpers/xml_test.go | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/helpers/xml_test.go b/helpers/xml_test.go index 7efcd37..183ef83 100644 --- a/helpers/xml_test.go +++ b/helpers/xml_test.go @@ -23,18 +23,26 @@ import ( */ +/* +type MultistatusObj struct { + XMLName xml.Name `xml:"multistatus"` + Multistatus struct { + XMLName xml.Name `xml:"response"` + Other string `xml:",innerxml"` + } +}*/ type MultistatusObj struct { XMLName xml.Name `xml:"multistatus"` Multistatus struct { - ResponseObj - } -} - -type ResponseObj struct { - XMLName xml.Name `xml:"response"` - Response struct { - Content string `xml:",chardata"` + XMLName xml.Name `xml:"response"` + Propstat struct { + XMLName xml.Name `xml:"propstat"` + Prop struct { + XMLName xml.Name `xml:"prop"` + Other string `xml:",innerxml"` + } + } } } @@ -49,7 +57,7 @@ func GetFileID(str string) string { if err != nil { fmt.Print(err) } - return multi.Multistatus.Response.Content + return multi.Multistatus.Propstat.Prop.Other } func TestGetFileID(t *testing.T) { From a19263e97e8a1c7d34d87a0ee3ddb608e4cf7108 Mon Sep 17 00:00:00 2001 From: naudachu Date: Wed, 21 Jun 2023 18:55:25 +0500 Subject: [PATCH 04/11] - Folder PrivateURL fixed; --- controller/controller.go | 2 +- domain/cloud.go | 8 ++--- ext/cloud.go | 78 ++++++++++++++++++++-------------------- ext/xml_test.go | 18 ++++++++++ helpers/xml_test.go | 67 ---------------------------------- 5 files changed, 63 insertions(+), 110 deletions(-) create mode 100644 ext/xml_test.go delete mode 100644 helpers/xml_test.go diff --git a/controller/controller.go b/controller/controller.go index 0faba9d..1e3df58 100644 --- a/controller/controller.go +++ b/controller/controller.go @@ -76,7 +76,7 @@ func (wc *WorkflowController) Workflow(name string) (string, error) { wg.Wait() - yt.UpdateIssue(issue, cloud.FolderURL, git, gitBuild) + yt.UpdateIssue(issue, cloud.PrivateURL, git, gitBuild) } return issue.Key, nil } diff --git a/domain/cloud.go b/domain/cloud.go index 729b48f..1fd744b 100644 --- a/domain/cloud.go +++ b/domain/cloud.go @@ -1,8 +1,8 @@ package domain type Cloud struct { - FolderName string // "k" - FolderPath string // "/temp/k" - FolderURL string // "http://82.151.222.22:7000/apps/files/?dir=/temp/k" - PublicURL string + FolderName string // k + FolderPath string // /temp/k + FolderURL string // http://82.151.222.22:7000/apps/files/?dir=/temp/k + PrivateURL string // http://82.151.222.22:7000/f/00000 } diff --git a/ext/cloud.go b/ext/cloud.go index b93fa11..312d844 100644 --- a/ext/cloud.go +++ b/ext/cloud.go @@ -6,6 +6,7 @@ import ( "io/ioutil" "log" "os" + "strconv" "ticket-pimp/domain" "ticket-pimp/helpers" "time" @@ -34,6 +35,23 @@ func NewCloud(base, user, pass string) *Cloud { } } +type MultistatusObj struct { + XMLName xml.Name `xml:"multistatus"` + Multistatus struct { + XMLName xml.Name `xml:"response"` + Propstat struct { + XMLName xml.Name `xml:"propstat"` + Prop struct { + XMLName xml.Name `xml:"prop"` + FileID struct { + XMLName xml.Name `xml:"fileid"` + ID string `xml:",chardata"` + } + } + } + } +} + func (c *Cloud) CreateFolder(name string) (*domain.Cloud, error) { rootDir := os.Getenv("ROOTDIR") @@ -54,37 +72,6 @@ func (c *Cloud) CreateFolder(name string) (*domain.Cloud, error) { cloud.FolderURL = c.BaseURL + cloud.FolderPath - /* - type ResponseObj struct { - Multistatus struct { - Response struct { - Href struct { - Propstat struct { - Prop struct { - FileID int `json:"oc:fileid"` - } `json:"d:prop"` - } `json:"d:propstat"` - } `json:"d:href"` - } `json:"d:response"` - } `json:"d:multistatus"` - }*/ - - type ResponseObj struct { - XMLName xml.Name `xml:"d:multistatus"` - Multistatus struct { - XMLName xml.Name `xml:"d:multistatus"` - Response struct { - Href struct { - Propstat struct { - Prop struct { - FileID string `xml:"oc:fileid"` - } `xml:"d:prop"` - } `xml:"d:propstat"` - } `xml:"d:href"` - } `xml:"d:response"` - } `xml:"d:multistatus"` - } - xmlFile, err := ioutil.ReadFile("./fileid.xml") if err != nil { @@ -92,24 +79,39 @@ func (c *Cloud) CreateFolder(name string) (*domain.Cloud, error) { return nil, err // fix this return; } - var id ResponseObj - resp, _ := c.R(). SetBody(xmlFile). Send("PROPFIND", os.Getenv("HOMEPATH")+os.Getenv("ROOTDIR")+cloud.FolderName) - xmlEncodingErr := resp.UnmarshalXml(&id) - if xmlEncodingErr != nil { - log.Print(err) + id, err := getFileIDFromRespBody(resp.Bytes()) + + if err != nil { + log.Print(err) // [ ] Если тут проблема - надо пытаться засетать полную ссылку } - log.Print(resp) - + cloud.PrivateURL = os.Getenv("CLOUD_BASE_URL") + "/f/" + strconv.Itoa(id) } return &cloud, err } +func getFileIDFromRespBody(str []byte) (int, error) { + + var multi MultistatusObj + + err := xml.Unmarshal(str, &multi) + if err != nil { + return 0, fmt.Errorf("XML Unmarshal error: %v", err) + } + + id, err := strconv.Atoi(multi.Multistatus.Propstat.Prop.FileID.ID) + if err != nil { + return 0, fmt.Errorf("FileID str to int convertion error: %v", err) + } + + return id, nil +} + func (c *Cloud) ShareToExternals(cloud *domain.Cloud) (*domain.Cloud, error) { return nil, nil } diff --git a/ext/xml_test.go b/ext/xml_test.go new file mode 100644 index 0000000..ba59d5c --- /dev/null +++ b/ext/xml_test.go @@ -0,0 +1,18 @@ +package ext + +import ( + "log" + "testing" +) + +const ( + EXAMPLE = "\n/remote.php/dav/files/naudachu/temp/id/33225HTTP/1.1 200 OK\n" +) + +// [ ] todo normal test... +func TestGetFileID(t *testing.T) { + + str, err := getFileIDFromRespBody([]byte(EXAMPLE)) + log.Print(str, err) + +} diff --git a/helpers/xml_test.go b/helpers/xml_test.go deleted file mode 100644 index 183ef83..0000000 --- a/helpers/xml_test.go +++ /dev/null @@ -1,67 +0,0 @@ -package helpers - -import ( - "encoding/xml" - "fmt" - "log" - "testing" -) - -/* - - - - - /remote.php/dav/files/naudachu/temp/id/ - - - 33225 - - HTTP/1.1 200 OK - - - - -*/ -/* -type MultistatusObj struct { - XMLName xml.Name `xml:"multistatus"` - Multistatus struct { - XMLName xml.Name `xml:"response"` - Other string `xml:",innerxml"` - } -}*/ - -type MultistatusObj struct { - XMLName xml.Name `xml:"multistatus"` - Multistatus struct { - XMLName xml.Name `xml:"response"` - Propstat struct { - XMLName xml.Name `xml:"propstat"` - Prop struct { - XMLName xml.Name `xml:"prop"` - Other string `xml:",innerxml"` - } - } - } -} - -const ( - EXAMPLE = "\n/remote.php/dav/files/naudachu/temp/id/33225HTTP/1.1 200 OK\n" -) - -func GetFileID(str string) string { - - var multi MultistatusObj - err := xml.Unmarshal([]byte(str), &multi) - if err != nil { - fmt.Print(err) - } - return multi.Multistatus.Propstat.Prop.Other -} - -func TestGetFileID(t *testing.T) { - str := GetFileID(EXAMPLE) - log.Print(str) - -} From 2b6b7b5d4a1cf554786b20e81e9ce046c5072420 Mon Sep 17 00:00:00 2001 From: naudachu Date: Wed, 21 Jun 2023 21:01:48 +0500 Subject: [PATCH 05/11] - rename domain entities into controller --- controller/controller.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/controller/controller.go b/controller/controller.go index 1e3df58..6f3c197 100644 --- a/controller/controller.go +++ b/controller/controller.go @@ -16,7 +16,7 @@ type WorkflowController struct { type IWorkflowController interface { Workflow(name string) (string, error) CreateRepo(name string, param uint) (string, error) - CreateFolder(name string) (*d.Cloud, error) + CreateFolder(name string) (*d.Folder, error) } func NewWorkflowController( @@ -53,7 +53,7 @@ func (wc *WorkflowController) Workflow(name string) (string, error) { if issue != nil { var ( git, gitBuild string - cloud *d.Cloud + cloud *d.Folder ) var wg sync.WaitGroup @@ -69,7 +69,7 @@ func (wc *WorkflowController) Workflow(name string) (string, error) { gitBuild, _ = wc.CreateRepo(issue.Key+"-build", 1) }() - go func(ref **d.Cloud) { + go func(ref **d.Folder) { defer wg.Done() *ref, _ = wc.CreateFolder(issue.Key + " - " + issue.Summary) }(&cloud) @@ -103,12 +103,12 @@ func (wc *WorkflowController) CreateRepo(name string, param uint) (string, error return "", err } -func (wc *WorkflowController) CreateFolder(name string) (*d.Cloud, error) { +func (wc *WorkflowController) CreateFolder(name string) (*d.Folder, error) { //Create ownCloud folder w/ iCloud interface; cloud, err := wc.iCloud.CreateFolder(name) if cloud == nil { - return cloud, err + return nil, err } /* [ ] Experimental call: From 3b155acf9e864755c801930288db6a6fa0f19ef2 Mon Sep 17 00:00:00 2001 From: naudachu Date: Wed, 21 Jun 2023 21:02:03 +0500 Subject: [PATCH 06/11] - domain Cloud into domain --- domain/cloud.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/domain/cloud.go b/domain/cloud.go index 1fd744b..84c66b2 100644 --- a/domain/cloud.go +++ b/domain/cloud.go @@ -1,8 +1,7 @@ package domain -type Cloud struct { - FolderName string // k - FolderPath string // /temp/k - FolderURL string // http://82.151.222.22:7000/apps/files/?dir=/temp/k - PrivateURL string // http://82.151.222.22:7000/f/00000 +type Folder struct { + Title string // k + PathTo string // /temp/k + PrivateURL string // http://domain/apps/files/?dir=/temp/k OR http://domain/f/3333 } From 228ca14f3362b9ecd1096d4999f35ebd1400d188 Mon Sep 17 00:00:00 2001 From: naudachu Date: Wed, 21 Jun 2023 21:02:36 +0500 Subject: [PATCH 07/11] - moved XML helper function into helpers package; --- ext/cloud.go | 100 ++++++++++++++++++++++----------------------------- 1 file changed, 42 insertions(+), 58 deletions(-) diff --git a/ext/cloud.go b/ext/cloud.go index 312d844..f420ac7 100644 --- a/ext/cloud.go +++ b/ext/cloud.go @@ -1,13 +1,9 @@ package ext import ( - "encoding/xml" - "fmt" - "io/ioutil" - "log" "os" "strconv" - "ticket-pimp/domain" + d "ticket-pimp/domain" "ticket-pimp/helpers" "time" ) @@ -17,8 +13,8 @@ type Cloud struct { } type ICloud interface { - CreateFolder(name string) (*domain.Cloud, error) - ShareToExternals(cloud *domain.Cloud) (*domain.Cloud, error) + CreateFolder(name string) (*d.Folder, error) + ShareToExternals(cloud *d.Folder) (*d.Folder, error) } func NewCloud(base, user, pass string) *Cloud { @@ -35,83 +31,71 @@ func NewCloud(base, user, pass string) *Cloud { } } -type MultistatusObj struct { - XMLName xml.Name `xml:"multistatus"` - Multistatus struct { - XMLName xml.Name `xml:"response"` - Propstat struct { - XMLName xml.Name `xml:"propstat"` - Prop struct { - XMLName xml.Name `xml:"prop"` - FileID struct { - XMLName xml.Name `xml:"fileid"` - ID string `xml:",chardata"` - } - } - } - } -} - -func (c *Cloud) CreateFolder(name string) (*domain.Cloud, error) { +func (c *Cloud) CreateFolder(name string) (*d.Folder, error) { rootDir := os.Getenv("ROOTDIR") + user := os.Getenv("CLOUD_USER") + + davPath := "/remote.php/dav/files/" + parentPath := "/apps/files/?dir=" name = helpers.GitNaming(name) - cloud := domain.Cloud{ - FolderName: name, - FolderURL: "", + cloud := d.Folder{ + Title: name, + PrivateURL: "", } - requestPath := os.Getenv("HOMEPATH") + rootDir + name - cloud.FolderPath = os.Getenv("FOLDER_PATH") + rootDir + name + requestPath := davPath + user + rootDir + name + + cloud.PathTo = parentPath + rootDir + name resp, err := c.R(). Send("MKCOL", requestPath) if resp.IsSuccessState() { + // Set stupid URL to the d entity + cloud.PrivateURL = c.BaseURL + cloud.PathTo - cloud.FolderURL = c.BaseURL + cloud.FolderPath - - xmlFile, err := ioutil.ReadFile("./fileid.xml") - - if err != nil { - fmt.Println(err) - return nil, err // fix this return; + // Try to set short URL to the d entity + if err = c.setPrivateURL(requestPath, &cloud); err != nil { + return &cloud, nil } - - resp, _ := c.R(). - SetBody(xmlFile). - Send("PROPFIND", os.Getenv("HOMEPATH")+os.Getenv("ROOTDIR")+cloud.FolderName) - - id, err := getFileIDFromRespBody(resp.Bytes()) - - if err != nil { - log.Print(err) // [ ] Если тут проблема - надо пытаться засетать полную ссылку - } - - cloud.PrivateURL = os.Getenv("CLOUD_BASE_URL") + "/f/" + strconv.Itoa(id) } return &cloud, err } -func getFileIDFromRespBody(str []byte) (int, error) { +func (c *Cloud) setPrivateURL(requestPath string, cloud *d.Folder) error { - var multi MultistatusObj + payload := []byte(``) + + // Deprecated: Read XML file + /* + xmlFile, err := ioutil.ReadFile("./fileid.xml") // moved into this method as a string.. + + if err != nil { + return fmt.Errorf("request xml file error: %v", err) + } + */ + + resp, err := c.R(). + SetBody(payload). + Send("PROPFIND", requestPath) - err := xml.Unmarshal(str, &multi) if err != nil { - return 0, fmt.Errorf("XML Unmarshal error: %v", err) + return err } - id, err := strconv.Atoi(multi.Multistatus.Propstat.Prop.FileID.ID) - if err != nil { - return 0, fmt.Errorf("FileID str to int convertion error: %v", err) + id := helpers.GetFileIDFromRespBody(resp.Bytes()) + + if id != 0 { + cloud.PrivateURL = c.BaseURL + "/f/" + strconv.Itoa(id) + return nil } - return id, nil + return err } -func (c *Cloud) ShareToExternals(cloud *domain.Cloud) (*domain.Cloud, error) { +func (c *Cloud) ShareToExternals(cloud *d.Folder) (*d.Folder, error) { return nil, nil } From 7221fa4aadb82912abce5eeb181361f7d7d0b4eb Mon Sep 17 00:00:00 2001 From: naudachu Date: Wed, 21 Jun 2023 21:03:08 +0500 Subject: [PATCH 08/11] - deprecate read xml file for set up webdav payload --- cmd/fileid.xml | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 cmd/fileid.xml diff --git a/cmd/fileid.xml b/cmd/fileid.xml deleted file mode 100644 index c0478a2..0000000 --- a/cmd/fileid.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file From 9a670fdc617a433df4d9f6d440d4b7258d97d5d2 Mon Sep 17 00:00:00 2001 From: naudachu Date: Wed, 21 Jun 2023 21:03:48 +0500 Subject: [PATCH 09/11] - simple test for helper function --- helpers/helpers.go | 36 ++++++++++++++++++++++++++++++++++++ {ext => helpers}/xml_test.go | 9 ++++----- 2 files changed, 40 insertions(+), 5 deletions(-) rename {ext => helpers}/xml_test.go (74%) diff --git a/helpers/helpers.go b/helpers/helpers.go index 93c17ba..8a12bfd 100644 --- a/helpers/helpers.go +++ b/helpers/helpers.go @@ -1,7 +1,9 @@ package helpers import ( + "encoding/xml" "regexp" + "strconv" "strings" ) @@ -22,3 +24,37 @@ func GitNaming(input string) string { // Join words and return return strings.Join(words, "-") } + +type MultistatusObj struct { + XMLName xml.Name `xml:"multistatus"` + Multistatus struct { + XMLName xml.Name `xml:"response"` + Propstat struct { + XMLName xml.Name `xml:"propstat"` + Prop struct { + XMLName xml.Name `xml:"prop"` + FileID struct { + XMLName xml.Name `xml:"fileid"` + ID string `xml:",chardata"` + } + } + } + } +} + +func GetFileIDFromRespBody(str []byte) int { + + var multi MultistatusObj + + err := xml.Unmarshal(str, &multi) + if err != nil { + return 0 + } + + id, err := strconv.Atoi(multi.Multistatus.Propstat.Prop.FileID.ID) + if err != nil { + return 0 + } + + return id +} diff --git a/ext/xml_test.go b/helpers/xml_test.go similarity index 74% rename from ext/xml_test.go rename to helpers/xml_test.go index ba59d5c..30fccee 100644 --- a/ext/xml_test.go +++ b/helpers/xml_test.go @@ -1,7 +1,6 @@ -package ext +package helpers import ( - "log" "testing" ) @@ -12,7 +11,7 @@ const ( // [ ] todo normal test... func TestGetFileID(t *testing.T) { - str, err := getFileIDFromRespBody([]byte(EXAMPLE)) - log.Print(str, err) - + if output := GetFileIDFromRespBody([]byte(EXAMPLE)); output != 33225 { + t.Errorf("Output %q not equal to expected %q", output, 33225) + } } From 3130994e7de11c7799d8f1d8308bfbc28162b94c Mon Sep 17 00:00:00 2001 From: naudachu Date: Wed, 21 Jun 2023 21:04:04 +0500 Subject: [PATCH 10/11] - rename domain entities into handler; --- handler/handler.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/handler/handler.go b/handler/handler.go index 32e8975..d333fe9 100644 --- a/handler/handler.go +++ b/handler/handler.go @@ -76,7 +76,7 @@ func (h *Handler) NewFolderHandler(ctx context.Context, mu *tgb.MessageUpdate) e answer := tg.HTML.Text( tg.HTML.Line( "✨ Shiny folder", - tg.HTML.Link(cloud.FolderName, cloud.FolderURL), + tg.HTML.Link(cloud.Title, cloud.PrivateURL), "has been created!", ), ) From 6be164682c2589e1bde3bde0069c0dcccc679072 Mon Sep 17 00:00:00 2001 From: naudachu Date: Sun, 25 Jun 2023 19:12:53 +0500 Subject: [PATCH 11/11] - get git entity from externals; - rework error handling; --- controller/controller.go | 58 +++++++++++++++++++--------------------- domain/git.go | 12 ++++----- ext/cloud.go | 24 +++++++++-------- ext/git.go | 26 ++++++++++-------- ext/yt.go | 18 ++++++------- handler/handler.go | 30 ++++++++++++++++++--- 6 files changed, 97 insertions(+), 71 deletions(-) diff --git a/controller/controller.go b/controller/controller.go index 6f3c197..9a080ef 100644 --- a/controller/controller.go +++ b/controller/controller.go @@ -13,12 +13,6 @@ type WorkflowController struct { iYouTrack ext.IYouTrack } -type IWorkflowController interface { - Workflow(name string) (string, error) - CreateRepo(name string, param uint) (string, error) - CreateFolder(name string) (*d.Folder, error) -} - func NewWorkflowController( gitBaseURL, gitToken, @@ -35,6 +29,12 @@ func NewWorkflowController( } } +type IWorkflowController interface { + Workflow(name string) (string, error) + CreateRepo(name string) (*d.Git, error) + CreateFolder(name string) (*d.Folder, error) +} + func (wc *WorkflowController) Workflow(name string) (string, error) { yt := wc.iYouTrack @@ -52,22 +52,22 @@ func (wc *WorkflowController) Workflow(name string) (string, error) { if issue != nil { var ( - git, gitBuild string + git, gitBuild *d.Git cloud *d.Folder ) var wg sync.WaitGroup wg.Add(3) - go func() { + go func(ref **d.Git) { defer wg.Done() - git, _ = wc.CreateRepo(issue.Key, 0) - }() + *ref, _ = wc.CreateRepo(issue.Key) + }(&git) - go func() { + go func(ref **d.Git) { defer wg.Done() - gitBuild, _ = wc.CreateRepo(issue.Key+"-build", 1) - }() + *ref, _ = wc.CreateRepo(issue.Key + "-build") + }(&gitBuild) go func(ref **d.Folder) { defer wg.Done() @@ -76,31 +76,29 @@ func (wc *WorkflowController) Workflow(name string) (string, error) { wg.Wait() - yt.UpdateIssue(issue, cloud.PrivateURL, git, gitBuild) + yt.UpdateIssue( + issue, + cloud.PrivateURL, + git.HtmlUrl, + fmt.Sprintf("ssh://%s/%s.git", gitBuild.SshUrl, gitBuild.FullName)) } return issue.Key, nil } -func (wc *WorkflowController) CreateRepo(name string, param uint) (string, error) { +func (wc *WorkflowController) CreateRepo(name string) (*d.Git, error) { //Create git repository with iGit interface; repo, err := wc.iGit.NewRepo(name) - - //Set 'apps' as collaborator to created repository; - wc.iGit.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 - } + if err != nil { + return nil, err } - return "", err + //Set 'apps' as collaborator to created repository; + _, err = wc.iGit.AppsAsCollaboratorTo(repo) + if err != nil { + return nil, err + } + + return repo, nil } func (wc *WorkflowController) CreateFolder(name string) (*d.Folder, error) { diff --git a/domain/git.go b/domain/git.go index 31882a3..083805b 100644 --- a/domain/git.go +++ b/domain/git.go @@ -1,11 +1,11 @@ package domain type Git struct { - Name string `json:"name"` - FullName string `json:"full_name"` + Name string `json:"name"` // "poop" + FullName string `json:"full_name"` // "developer/poop" Private bool `json:"private"` - Url string `json:"url"` - CloneUrl string `json:"clone_url"` - HtmlUrl string `json:"Html_url"` - SshUrl string `json:"ssh_url"` + Url string `json:"url"` // "http://localhost:8081/api/v3/repos/developer/poop" + CloneUrl string `json:"clone_url"` // "http://localhost:8081/git/developer/poop.git" + HtmlUrl string `json:"Html_url"` // "http://localhost:8081/developer/poop" + SshUrl string `json:"ssh_url"` // ?! } diff --git a/ext/cloud.go b/ext/cloud.go index f420ac7..3d111ff 100644 --- a/ext/cloud.go +++ b/ext/cloud.go @@ -1,6 +1,7 @@ package ext import ( + "fmt" "os" "strconv" d "ticket-pimp/domain" @@ -49,7 +50,7 @@ func (c *Cloud) CreateFolder(name string) (*d.Folder, error) { cloud.PathTo = parentPath + rootDir + name - resp, err := c.R(). + resp, _ := c.R(). Send("MKCOL", requestPath) if resp.IsSuccessState() { @@ -57,12 +58,12 @@ func (c *Cloud) CreateFolder(name string) (*d.Folder, error) { cloud.PrivateURL = c.BaseURL + cloud.PathTo // Try to set short URL to the d entity - if err = c.setPrivateURL(requestPath, &cloud); err != nil { - return &cloud, nil + if err := c.setPrivateURL(requestPath, &cloud); err != nil { + return &cloud, err } } - return &cloud, err + return &cloud, nil } func (c *Cloud) setPrivateURL(requestPath string, cloud *d.Folder) error { @@ -78,22 +79,23 @@ func (c *Cloud) setPrivateURL(requestPath string, cloud *d.Folder) error { } */ - resp, err := c.R(). + resp, _ := c.R(). SetBody(payload). Send("PROPFIND", requestPath) - if err != nil { - return err + if resp.Err != nil { + return resp.Err } id := helpers.GetFileIDFromRespBody(resp.Bytes()) - if id != 0 { - cloud.PrivateURL = c.BaseURL + "/f/" + strconv.Itoa(id) - return nil + if id == 0 { + return fmt.Errorf("unable to get fileid") } - return err + cloud.PrivateURL = c.BaseURL + "/f/" + strconv.Itoa(id) + + return nil } func (c *Cloud) ShareToExternals(cloud *d.Folder) (*d.Folder, error) { diff --git a/ext/git.go b/ext/git.go index 1fadb40..7b47657 100644 --- a/ext/git.go +++ b/ext/git.go @@ -55,31 +55,35 @@ func (gb *Git) NewRepo(name string) (*domain.Git, error) { var git domain.Git git.Private = true - resp, err := gb.R(). + resp, _ := gb.R(). SetBody(&payload). SetSuccessResult(&git). Post("/user/repos") - if err != nil { - log.Print(resp) + if resp.Err != nil { + log.Print(resp.Err) + return nil, resp.Err } - return &git, err + return &git, nil } func (gb *Git) AppsAsCollaboratorTo(git *domain.Git) (*domain.Git, error) { - payloadPermission := permissionRequest{ + payload := permissionRequest{ Perm: "admin", } - resp, err := gb.R(). - SetBody(&payloadPermission). - Put("/repos/" + os.Getenv("GIT_USER") + "/" + git.Name + "/collaborators/apps") + respURL := "/repos/" + os.Getenv("GIT_USER") + "/" + git.Name + "/collaborators/apps" - if err != nil { - log.Print(resp) + resp, _ := gb.R(). + SetBody(&payload). + Put(respURL) + + if resp.Err != nil { + log.Print(resp.Err) + return nil, resp.Err } - return git, err + return git, nil } diff --git a/ext/yt.go b/ext/yt.go index 0f4af58..2d23b2f 100644 --- a/ext/yt.go +++ b/ext/yt.go @@ -43,15 +43,15 @@ func (yt *youtrack) GetProjects() ([]d.Project, error) { var projects []d.Project - _, err := yt.R(). + resp, _ := yt.R(). EnableDump(). SetQueryParam("fields", "id,name,shortName"). SetSuccessResult(&projects). Get("/admin/projects") // Check if the request failed; - if err != nil { - return nil, fmt.Errorf("some problem with YT request. error message: %v", err) + if resp.Err != nil { + return nil, fmt.Errorf("some problem with YT request. error message: %v", resp.Err) } return projects, nil @@ -71,15 +71,15 @@ func (yt *youtrack) CreateIssue(projectID, name string) (*d.IssueCreateRequest, } // Push issue to the YT; - _, err := yt.R(). + resp, _ := yt.R(). SetQueryParam("fields", "idReadable,id"). SetBody(&issue). SetSuccessResult(&issue). Post("/issues") // Check if the request failed; - if err != nil { - return nil, fmt.Errorf("some problem with YT request. error message: %v", err) + if resp.Err != nil { + return nil, fmt.Errorf("some problem with YT request. error message: %v", resp.Err) } return &issue, nil @@ -109,14 +109,14 @@ func (yt *youtrack) UpdateIssue(issue *d.IssueCreateRequest, folder, git, gitBui } // Push issue update to YT - resp, err := yt.R(). + resp, _ := yt.R(). SetBody(&update). SetSuccessResult(&issue). Post("/issues/" + issue.Key) // Check if the request failed; - if err != nil { - return nil, fmt.Errorf("some problem with YT request. error message: %v", err) + if resp.Err != nil { + return nil, fmt.Errorf("some problem with YT request. error message: %v", resp.Err) } if !resp.IsSuccessState() { diff --git a/handler/handler.go b/handler/handler.go index d333fe9..efd6528 100644 --- a/handler/handler.go +++ b/handler/handler.go @@ -6,6 +6,7 @@ import ( "fmt" "strings" "ticket-pimp/controller" + d "ticket-pimp/domain" "github.com/mr-linch/go-tg" "github.com/mr-linch/go-tg/tgb" @@ -32,11 +33,29 @@ func (h *Handler) PingHandler(ctx context.Context, mu *tgb.MessageUpdate) error return mu.Answer("pong").DoVoid(ctx) } -func newRepoAnswer(name string) string { +type git struct { + name string + url string + + git string + ssh string +} + +func newGit(d *d.Git) *git { + return &git{ + name: d.Name, + url: d.HtmlUrl, + git: d.CloneUrl, + ssh: fmt.Sprintf("ssh://%s/%s.git", d.SshUrl, d.FullName), + } +} + +// FYI: Telegram doesn't renders this hyperlink, if the url is localhost 🤷‍♂️ +func (g *git) prepareAnswer() string { return tg.HTML.Text( tg.HTML.Line( "Repo ", - name, + tg.HTML.Link(g.name, g.url), "has been created!", ), ) @@ -50,13 +69,16 @@ func (h *Handler) NewRepoHandler(ctx context.Context, mu *tgb.MessageUpdate) err return errors.New("empty command provided") } - repoStr, err := h.workflow.CreateRepo(str, 0) + var g *d.Git + g, err := h.workflow.CreateRepo(str) if err != nil { return mu.Answer(errorAnswer(err.Error())).ParseMode(tg.HTML).DoVoid(ctx) } - return mu.Answer(newRepoAnswer(repoStr)).ParseMode(tg.HTML).DoVoid(ctx) + resp := newGit(g).prepareAnswer() + + return mu.Answer(resp).ParseMode(tg.HTML).DoVoid(ctx) } func (h *Handler) NewFolderHandler(ctx context.Context, mu *tgb.MessageUpdate) error {