From 6c07f434fc01d326737120b1e428338ce8d36f74 Mon Sep 17 00:00:00 2001 From: naudachu Date: Wed, 8 Nov 2023 15:49:09 +0500 Subject: [PATCH 1/3] - fix folder creation process; --- Dockerfile | 1 - discord/discord.go | 14 +- discord/handler/handle_folder.go | 100 +++++++++++ discord/handler/handle_ping.go | 30 ++++ discord/handler/handle_repo.go | 109 +++++++++++ discord/handler/handle_ticket.go | 78 ++++++++ discord/handler/handler.go | 289 +----------------------------- internal/controller/controller.go | 3 +- internal/helpers/error_message.go | 5 + internal/helpers/helpers.go | 2 +- internal/helpers/helpers_test.go | 2 +- internal/services/cloud.go | 62 +++++-- internal/services/git.go | 2 +- telegram/handler/handle_folder.go | 8 +- telegram/telegram.go | 1 + 15 files changed, 389 insertions(+), 317 deletions(-) create mode 100644 discord/handler/handle_folder.go create mode 100644 discord/handler/handle_ping.go create mode 100644 discord/handler/handle_repo.go create mode 100644 discord/handler/handle_ticket.go create mode 100644 internal/helpers/error_message.go diff --git a/Dockerfile b/Dockerfile index f69a1c9..9b75311 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,7 +7,6 @@ RUN apk add git RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go install -ldflags '-extldflags "-static"' -tags timetzdata ./cmd/main.go FROM scratch -# the test program: COPY --from=app-builder /go/bin/main /ticket-pimp COPY --from=app-builder /go/src/ticket-pimp/cmd/prod.env / # the tls certificates: diff --git a/discord/discord.go b/discord/discord.go index c2db60a..911569d 100644 --- a/discord/discord.go +++ b/discord/discord.go @@ -12,12 +12,12 @@ import ( "github.com/bwmarrin/discordgo" ) -func initBotWith(token string) (*discordgo.Session, error) { +func initBotWith(token string) *discordgo.Session { discord, err := discordgo.New("Bot " + token) if err != nil { - return nil, err + log.Fatalf("unable to create discord session: %v", err) } - return discord, nil + return discord } type DiscordOptions struct { @@ -28,10 +28,7 @@ type DiscordOptions struct { func Run(conf domain.Config, opts DiscordOptions) error { token := conf.Discord.Token - session, err := initBotWith(token) - if err != nil { - return err - } + session := initBotWith(token) router := handler.InitRouter(*opts.Controller) @@ -41,7 +38,6 @@ func Run(conf domain.Config, opts DiscordOptions) error { } session.AddHandler(func(s *discordgo.Session, i *discordgo.InteractionCreate) { - if h, ok := commandHandlers[i.ApplicationCommandData().Name]; ok { h(s, i) } @@ -73,7 +69,7 @@ func Run(conf domain.Config, opts DiscordOptions) error { log.Println("Removing commands...") for _, h := range cmds { - err = session.ApplicationCommandDelete(session.State.User.ID, "", h.ID) + err := session.ApplicationCommandDelete(session.State.User.ID, "", h.ID) if err != nil { log.Panicf("Cannot delete '%v' command: %v", h.Name, err) } diff --git a/discord/handler/handle_folder.go b/discord/handler/handle_folder.go new file mode 100644 index 0000000..fd69214 --- /dev/null +++ b/discord/handler/handle_folder.go @@ -0,0 +1,100 @@ +package handler + +import ( + "context" + "fmt" + "log" + "strconv" + + "github.com/bwmarrin/discordgo" +) + +func (h *router) CreateFolderHandler(nameMinLenght int) route { + const ( + nameOption string = "folder_name" + ) + return route{ + + Command: discordgo.ApplicationCommand{ + Name: "folder", + Description: "Command for cloud folder creation", + Options: []*discordgo.ApplicationCommandOption{ + { + Type: discordgo.ApplicationCommandOptionString, + Name: nameOption, + Description: "Type the folder's name", + Required: false, + MinLength: &nameMinLenght, + }, + }, + }, + + Handler: func(s *discordgo.Session, i *discordgo.InteractionCreate) { + initialResponse := discordgo.InteractionResponse{ + // Ignore type for now, they will be discussed in "responses" + Type: discordgo.InteractionResponseChannelMessageWithSource, + Data: &discordgo.InteractionResponseData{ + Flags: discordgo.MessageFlagsEphemeral, + Content: "..folder is going to be created", + Title: "πŸ“‚ Folder creation", + }, + } + + s.InteractionRespond(i.Interaction, &initialResponse) + + var result string = "unexpected result" + + options := i.ApplicationCommandData().Options + + optionMap := make(map[string]*discordgo.ApplicationCommandInteractionDataOption, len(options)) + for _, opt := range options { + optionMap[opt.Name] = opt + } + + var str string = "" + + dchan, err := s.Channel(i.ChannelID) + + if err != nil { + log.Printf("error while identifying channel: %v", err) + } + + if dchan.ParentID == strconv.Itoa(1150719794853716028) { + log.Println("yes, channel is from `Projects`") + project, err := h.controller.GetProjectByChannelID(context.TODO(), i.ChannelID) + if err != nil { + result = fmt.Sprintf("unable to retrieve project from db, error: %v", err) + } else { + switch { + case project == nil: + if option, ok := optionMap[nameOption]; ok { + str = option.StringValue() + } else { + str = "Π’Ρ‹, Π»ΠΈΠ±ΠΎ Π² ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π΅ Π΄ΠΈΡ€Π΅ΠΊΡ‚ΠΎΡ€ΠΈΡŽ создавай, Π»ΠΈΠ±ΠΎ имя напиши, Π±Π»Π΅Ρ‚!" + } + default: + str = project.ShortName + } + + resp := h.controller.ICloud.CreateFolder(str) + if resp.ErrMessage != nil { + result = fmt.Sprintf("Command executed w/ errors: ``` %v``` \n But check this link: %s\n", resp.ErrMessage, resp.Folder.PrivateURL) + } else { + result = fmt.Sprintf("πŸ“‚ Folder was created: %s", resp.Folder.PrivateURL) + } + } + + _, err = s.FollowupMessageCreate(i.Interaction, true, &discordgo.WebhookParams{ + Content: result, + }) + + if err != nil { + s.FollowupMessageCreate(i.Interaction, true, &discordgo.WebhookParams{ + Content: fmt.Sprintf("Something went wrong: %v", err), + }) + return + } + } + }, + } +} diff --git a/discord/handler/handle_ping.go b/discord/handler/handle_ping.go new file mode 100644 index 0000000..b44b88c --- /dev/null +++ b/discord/handler/handle_ping.go @@ -0,0 +1,30 @@ +package handler + +import ( + "log" + + "github.com/bwmarrin/discordgo" +) + +func (h *router) Ping() route { + return route{ + Command: discordgo.ApplicationCommand{ + Name: "ping", + Description: "pongs in a reply", + }, + Handler: func(s *discordgo.Session, i *discordgo.InteractionCreate) { + log.Println("ok, I'm here..") + + err := s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ + Type: discordgo.InteractionResponseChannelMessageWithSource, + Data: &discordgo.InteractionResponseData{ + Content: "`pong`", + Title: "Pong reply", + }, + }) + if err != nil { + log.Println(err) + } + }, + } +} diff --git a/discord/handler/handle_repo.go b/discord/handler/handle_repo.go new file mode 100644 index 0000000..a62d7fa --- /dev/null +++ b/discord/handler/handle_repo.go @@ -0,0 +1,109 @@ +package handler + +import ( + "context" + "fmt" + + "github.com/bwmarrin/discordgo" +) + +func (h *router) CreateRepoHandler(repoNameMinLength int) route { + const ( + projectRepo = "project_repo" + buildRepo = "build_repo" + ) + + return route{ + Command: discordgo.ApplicationCommand{ + Name: "repo", + Description: "Command for repository creation", + Options: []*discordgo.ApplicationCommandOption{ + { + Type: discordgo.ApplicationCommandOptionString, + Name: "repo_type", + Description: "The type of repo", + Required: true, + Choices: []*discordgo.ApplicationCommandOptionChoice{ + { + Name: "Unity project repo", + Value: projectRepo, + }, + { + Name: "XCode build repo", + Value: buildRepo, + }, + }, + }, + { + Type: discordgo.ApplicationCommandOptionString, + Name: "repo_name", + Description: "Type the repository's name", + Required: false, + MinLength: &repoNameMinLength, + }, + }, + }, + Handler: func(s *discordgo.Session, i *discordgo.InteractionCreate) { + var result string + // Access options in the order provided by the user. + options := i.ApplicationCommandData().Options + + // Or convert the slice into a map + optionMap := make(map[string]*discordgo.ApplicationCommandInteractionDataOption, len(options)) + for _, opt := range options { + optionMap[opt.Name] = opt + } + + var str string = "" + + project, err := h.controller.GetProjectByChannelID(context.TODO(), i.ChannelID) + if err != nil { + result = fmt.Sprintf("unable to retrieve project from db, error: %v", err) + } else { + var suffix string + if option, ok := optionMap["repo_type"]; ok { + switch option.Value { + case projectRepo: + suffix = "" + case buildRepo: + suffix = "-build" + } + } + + if project == nil { + if option, ok := optionMap["repo_name"]; ok { + str = option.StringValue() + } else { + str = "" + } + } else { + str = project.ShortName + } + + if str == "" { + result = "Π’Ρ‹, Π»ΠΈΠ±ΠΎ Π² ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π΅ Ρ€Π΅ΠΏΠΎ создавай, Π»ΠΈΠ±ΠΎ имя напиши, Π±Π»Π΅Ρ‚!" + } else { + str = str + suffix + + // var g *domain.Git + + g, err := h.controller.IGit.CreateRepo(str) + if err != nil { + result = fmt.Sprintf("error while repo creation: %v", err) + } else { + result = "πŸš€ " + g.HtmlUrl + " was created" + } + } + } + + resp := &discordgo.InteractionResponse{ + Type: discordgo.InteractionResponseChannelMessageWithSource, + Data: &discordgo.InteractionResponseData{ + Content: result, + }, + } + + s.InteractionRespond(i.Interaction, resp) + }, + } +} diff --git a/discord/handler/handle_ticket.go b/discord/handler/handle_ticket.go new file mode 100644 index 0000000..1d1d0f6 --- /dev/null +++ b/discord/handler/handle_ticket.go @@ -0,0 +1,78 @@ +package handler + +import ( + "context" + "fmt" + "log" + "ticket-pimp/internal/domain" + + "github.com/bwmarrin/discordgo" +) + +func (h *router) CreateTicketHandler(repoNameMinLength int) route { + return route{ + Command: discordgo.ApplicationCommand{ + Name: "project", + Description: "Create new development ticket", + Options: []*discordgo.ApplicationCommandOption{ + { + Type: discordgo.ApplicationCommandOptionString, + Name: "project_name", + Description: "Temporary project name", + Required: true, + MinLength: &repoNameMinLength, + }, + }, + }, + Handler: func(s *discordgo.Session, i *discordgo.InteractionCreate) { + var result string + // Access options in the order provided by the user. + options := i.ApplicationCommandData().Options + + // Or convert the slice into a map + optionMap := make(map[string]*discordgo.ApplicationCommandInteractionDataOption, len(options)) + for _, opt := range options { + optionMap[opt.Name] = opt + } + + if option, ok := optionMap["project_name"]; ok { + dchan, err := s.GuildChannelCreate(i.GuildID, option.StringValue(), discordgo.ChannelTypeGuildText) + if err != nil { + result = fmt.Sprintf("chan creation problem: %v\n", err) + } else { + p, err := h.controller.ProjectCreate(context.TODO(), domain.Project{ + ChannelID: dchan.ID, + }) + if err != nil { + result = fmt.Sprintf("unable to create project: %v\n", err) + } else { + edit := discordgo.ChannelEdit{ + Name: p.ShortName, + ParentID: "1150719794853716028", + } + + dchan, err = s.ChannelEdit(dchan.ID, &edit) + if err != nil { + result = fmt.Sprintf("channel created, but unable to edit: %v\n", err) + + } else { + _, err = s.ChannelMessageSend(dchan.ID, "Hello!") + if err != nil { + log.Printf("message send problem: %v\n", err) + } + result = dchan.ID + } + } + } + } + + s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ + // Ignore type for now, they will be discussed in "responses" + Type: discordgo.InteractionResponseChannelMessageWithSource, + Data: &discordgo.InteractionResponseData{ + Content: result, + }, + }) + }, + } +} diff --git a/discord/handler/handler.go b/discord/handler/handler.go index b954eee..b1421c8 100644 --- a/discord/handler/handler.go +++ b/discord/handler/handler.go @@ -1,11 +1,7 @@ package handler import ( - "context" - "fmt" - "log" "ticket-pimp/internal/controller" - "ticket-pimp/internal/domain" "github.com/bwmarrin/discordgo" ) @@ -21,10 +17,10 @@ func InitRouter(wc controller.WorkflowController) *router { var r router r.Routes = append( r.Routes, - // r.CreateRepoHandler(3), + r.CreateRepoHandler(3), r.CreateFolderHandler(3), r.Ping(), - // r.CreateTicketHandler(3), + r.CreateTicketHandler(3), ) r.controller = wc @@ -35,284 +31,3 @@ type route struct { Command discordgo.ApplicationCommand Handler func(s *discordgo.Session, i *discordgo.InteractionCreate) } - -func (h *router) Ping() route { - return route{ - Command: discordgo.ApplicationCommand{ - Name: "ping", - Description: "pongs in a reply", - }, - Handler: func(s *discordgo.Session, i *discordgo.InteractionCreate) { - log.Println("ok, I'm here..") - - s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ - Type: discordgo.InteractionResponseDeferredMessageUpdate, - Data: &discordgo.InteractionResponseData{ - Content: "`pong`", - Title: "Pong reply", - }, - }) - }, - } -} - -func (h *router) CreateFolderHandler(nameMinLenght int) route { - const ( - nameOption string = "folder_name" - ) - return route{ - Command: discordgo.ApplicationCommand{ - Name: "folder", - Description: "Command for cloud folder creation", - Options: []*discordgo.ApplicationCommandOption{ - { - Type: discordgo.ApplicationCommandOptionString, - Name: nameOption, - Description: "Type the folder's name", - Required: false, - MinLength: &nameMinLenght, - }, - }, - }, - Handler: func(s *discordgo.Session, i *discordgo.InteractionCreate) { - var result string - - resp := discordgo.InteractionResponse{ - - Type: discordgo.InteractionResponseUpdateMessage, - Data: &discordgo.InteractionResponseData{ - Content: "Folder is going to be created..", - }, - } - err := s.InteractionRespond(i.Interaction, &resp) - if err != nil { - log.Println(err) - return - } - - options := i.ApplicationCommandData().Options - - optionMap := make(map[string]*discordgo.ApplicationCommandInteractionDataOption, len(options)) - for _, opt := range options { - optionMap[opt.Name] = opt - } - - var str string = "" - - project, err := h.controller.GetProjectByChannelID(context.TODO(), i.ChannelID) - if err != nil { - result = fmt.Sprintf("unable to retrieve project from db, error: %v", err) - } else { - if project == nil { - if option, ok := optionMap[nameOption]; ok { - str = option.StringValue() - } else { - str = "" - } - } else { - str = project.ShortName - } - - if str == "" { - result = "Π’Ρ‹, Π»ΠΈΠ±ΠΎ Π² ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π΅ Π΄ΠΈΡ€Π΅ΠΊΡ‚ΠΎΡ€ΠΈΡŽ создавай, Π»ΠΈΠ±ΠΎ имя напиши, Π±Π»Π΅Ρ‚!" - } else { - - f, err := h.controller.ICloud.CreateFolder(str) - if err != nil { - result = fmt.Sprintf("error while cloud folder creation: %v", err) - } else { - result = fmt.Sprint(f.PrivateURL) - } - } - } - - // resp = discordgo.InteractionResponse{ - // // Ignore type for now, they will be discussed in "responses" - // Type: discordgo.InteractionResponseUpdateMessage, - // Data: &discordgo.InteractionResponseData{ - // Content: result, - // Title: "πŸ“‚ Folder was created", - // }, - // } - - webhookEdit := discordgo.WebhookEdit{ - Content: &result, - } - s.InteractionResponseEdit(i.Interaction, &webhookEdit) - - // discerr := s.InteractionRespond(i.Interaction, &resp) - // if discerr != nil { - // log.Println(discerr) - // } - }, - } -} - -func (h *router) CreateRepoHandler(repoNameMinLength int) route { - const ( - projectRepo = "project_repo" - buildRepo = "build_repo" - ) - - return route{ - Command: discordgo.ApplicationCommand{ - Name: "repo", - Description: "Command for repository creation", - Options: []*discordgo.ApplicationCommandOption{ - { - Type: discordgo.ApplicationCommandOptionString, - Name: "repo_type", - Description: "The type of repo", - Required: true, - Choices: []*discordgo.ApplicationCommandOptionChoice{ - { - Name: "Unity project repo", - Value: projectRepo, - }, - { - Name: "XCode build repo", - Value: buildRepo, - }, - }, - }, - { - Type: discordgo.ApplicationCommandOptionString, - Name: "repo_name", - Description: "Type the repository's name", - Required: false, - MinLength: &repoNameMinLength, - }, - }, - }, - Handler: func(s *discordgo.Session, i *discordgo.InteractionCreate) { - var result string - // Access options in the order provided by the user. - options := i.ApplicationCommandData().Options - - // Or convert the slice into a map - optionMap := make(map[string]*discordgo.ApplicationCommandInteractionDataOption, len(options)) - for _, opt := range options { - optionMap[opt.Name] = opt - } - - var str string = "" - - project, err := h.controller.GetProjectByChannelID(context.TODO(), i.ChannelID) - if err != nil { - result = fmt.Sprintf("unable to retrieve project from db, error: %v", err) - } else { - var suffix string - if option, ok := optionMap["repo_type"]; ok { - switch option.Value { - case projectRepo: - suffix = "" - case buildRepo: - suffix = "-build" - } - } - - if project == nil { - if option, ok := optionMap["repo_name"]; ok { - str = option.StringValue() - } else { - str = "" - } - } else { - str = project.ShortName - } - - if str == "" { - result = "Π’Ρ‹, Π»ΠΈΠ±ΠΎ Π² ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π΅ Ρ€Π΅ΠΏΠΎ создавай, Π»ΠΈΠ±ΠΎ имя напиши, Π±Π»Π΅Ρ‚!" - } else { - str = str + suffix - - // var g *domain.Git - - g, err := h.controller.IGit.CreateRepo(str) - if err != nil { - result = fmt.Sprintf("error while repo creation: %v", err) - } else { - result = "πŸš€ " + g.HtmlUrl + " was created" - } - } - } - - resp := &discordgo.InteractionResponse{ - Type: discordgo.InteractionResponseChannelMessageWithSource, - Data: &discordgo.InteractionResponseData{ - Content: result, - }, - } - - s.InteractionRespond(i.Interaction, resp) - }, - } -} - -func (h *router) CreateTicketHandler(repoNameMinLength int) route { - return route{ - Command: discordgo.ApplicationCommand{ - Name: "project", - Description: "Create new development ticket", - Options: []*discordgo.ApplicationCommandOption{ - { - Type: discordgo.ApplicationCommandOptionString, - Name: "project_name", - Description: "Temporary project name", - Required: true, - MinLength: &repoNameMinLength, - }, - }, - }, - Handler: func(s *discordgo.Session, i *discordgo.InteractionCreate) { - var result string - // Access options in the order provided by the user. - options := i.ApplicationCommandData().Options - - // Or convert the slice into a map - optionMap := make(map[string]*discordgo.ApplicationCommandInteractionDataOption, len(options)) - for _, opt := range options { - optionMap[opt.Name] = opt - } - - if option, ok := optionMap["project_name"]; ok { - dchan, err := s.GuildChannelCreate(i.GuildID, option.StringValue(), discordgo.ChannelTypeGuildText) - if err != nil { - result = fmt.Sprintf("chan creation problem: %v\n", err) - } else { - p, err := h.controller.ProjectCreate(context.TODO(), domain.Project{ - ChannelID: dchan.ID, - }) - if err != nil { - result = fmt.Sprintf("unable to create project: %v\n", err) - } else { - edit := discordgo.ChannelEdit{ - Name: p.ShortName, - ParentID: "1150719794853716028", - } - - dchan, err = s.ChannelEdit(dchan.ID, &edit) - if err != nil { - result = fmt.Sprintf("channel created, but unable to edit: %v\n", err) - - } else { - _, err = s.ChannelMessageSend(dchan.ID, "Hello!") - if err != nil { - log.Printf("message send problem: %v\n", err) - } - result = dchan.ID - } - } - } - } - - s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ - // Ignore type for now, they will be discussed in "responses" - Type: discordgo.InteractionResponseChannelMessageWithSource, - Data: &discordgo.InteractionResponseData{ - Content: result, - }, - }) - }, - } -} diff --git a/internal/controller/controller.go b/internal/controller/controller.go index 93a86d1..a804b29 100644 --- a/internal/controller/controller.go +++ b/internal/controller/controller.go @@ -61,7 +61,8 @@ func (wc *WorkflowController) FullProjectInit(name, key, id string) (string, err go func(ref **domain.Folder) { defer wg.Done() - *ref, _ = wc.ICloud.CreateFolder(appKey) + + *ref = wc.ICloud.CreateFolder(appKey).Folder }(&cloud) wg.Wait() diff --git a/internal/helpers/error_message.go b/internal/helpers/error_message.go new file mode 100644 index 0000000..970e15c --- /dev/null +++ b/internal/helpers/error_message.go @@ -0,0 +1,5 @@ +package helpers + +type ErrorMessage struct { + Message string `json:"message"` +} diff --git a/internal/helpers/helpers.go b/internal/helpers/helpers.go index 8a12bfd..9c600e0 100644 --- a/internal/helpers/helpers.go +++ b/internal/helpers/helpers.go @@ -7,7 +7,7 @@ import ( "strings" ) -func GitNaming(input string) string { +func ValidNaming(input string) string { // Remove leading and trailing whitespace input = strings.TrimSpace(input) diff --git a/internal/helpers/helpers_test.go b/internal/helpers/helpers_test.go index 3e877a4..53e8112 100644 --- a/internal/helpers/helpers_test.go +++ b/internal/helpers/helpers_test.go @@ -18,7 +18,7 @@ var tests = []test{ func TestGitNaming(t *testing.T) { for _, test := range tests { - if output := GitNaming(test.arg); output != test.expected { + if output := ValidNaming(test.arg); output != test.expected { t.Errorf("Output %q not equal to expected %q", output, test.expected) } } diff --git a/internal/services/cloud.go b/internal/services/cloud.go index 8c61a6f..2b3946a 100644 --- a/internal/services/cloud.go +++ b/internal/services/cloud.go @@ -2,7 +2,9 @@ package services import ( "fmt" + "log" "strconv" + "strings" "ticket-pimp/internal/domain" "ticket-pimp/internal/helpers" "time" @@ -14,7 +16,7 @@ type Cloud struct { } type ICloud interface { - CreateFolder(name string) (*domain.Folder, error) + CreateFolder(name string) Response } func NewCloud(conf domain.CloudConfig) *Cloud { @@ -32,40 +34,76 @@ func NewCloud(conf domain.CloudConfig) *Cloud { } } -func (c *Cloud) CreateFolder(name string) (*domain.Folder, error) { +type Response struct { + Folder *domain.Folder + ErrMessage error +} + +func (c *Cloud) CreateFolder(name string) Response { + var R Response + rootDir := c.Config.RootDir user := c.Config.User davPath := "/remote.php/dav/files/" parentPath := "/apps/files/?dir=" - name = helpers.GitNaming(name) + name = helpers.ValidNaming(name) - cloud := domain.Folder{ + R.Folder = &domain.Folder{ Title: name, PrivateURL: "", } + // cloud := domain.Folder{ + // Title: name, + // PrivateURL: "", + // } + requestPath := davPath + user + rootDir + name - cloud.PathTo = parentPath + rootDir + name + R.Folder.PathTo = parentPath + rootDir + name - resp, _ := c.R(). + var errMessage helpers.ErrorMessage + + resp, err := c.R(). + SetErrorResult(&errMessage). Send("MKCOL", requestPath) + if err != nil { // Error handling. + log.Println("error:", err) + + // Π₯Сровая ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠ° ошибки: + // error while cloud folder creation: bad response, raw content: + // + // + // Sabre\DAV\Exception\MethodNotAllowed + // The resource you tried to create already exists + // + if strings.Contains(err.Error(), "already exists") { + R.Folder.PrivateURL = c.BaseURL + R.Folder.PathTo + R.ErrMessage = err + // Try to set short URL to the d entity + if err := c.setPrivateURL(requestPath, R.Folder); err != nil { + R.ErrMessage = err + return R + } + return R + } + return R + } + if resp.IsSuccessState() { // Set stupid URL to the d entity - cloud.PrivateURL = c.BaseURL + cloud.PathTo + R.Folder.PrivateURL = c.BaseURL + R.Folder.PathTo // Try to set short URL to the d entity - if err := c.setPrivateURL(requestPath, &cloud); err != nil { - return &cloud, err + if err := c.setPrivateURL(requestPath, R.Folder); err != nil { + return R } - } else { - fmt.Println(resp.Status) } - return &cloud, nil + return R } func (c *Cloud) setPrivateURL(requestPath string, cloud *domain.Folder) error { diff --git a/internal/services/git.go b/internal/services/git.go index 01388d6..0f1c1a7 100644 --- a/internal/services/git.go +++ b/internal/services/git.go @@ -55,7 +55,7 @@ type gitCreateRequest struct { } func (gb *Git) newRepo(name string) (*domain.Git, error) { - name = helpers.GitNaming(name) + name = helpers.ValidNaming(name) payload := gitCreateRequest{ Name: name, diff --git a/telegram/handler/handle_folder.go b/telegram/handler/handle_folder.go index 460d7af..fa88377 100644 --- a/telegram/handler/handle_folder.go +++ b/telegram/handler/handle_folder.go @@ -17,10 +17,10 @@ func (h *Handler) NewFolderHandler(ctx context.Context, mu *tgb.MessageUpdate) e return errors.New("empty command provided") } - cloud, err := h.cloud.CreateFolder(str) + resp := h.cloud.CreateFolder(str) - if err != nil { - answer := errorAnswer(err.Error()) + if resp.ErrMessage != nil { + answer := errorAnswer(resp.ErrMessage.Error()) h.LogMessage(ctx, mu, answer) return mu.Answer(answer).ParseMode(tg.HTML).DoVoid(ctx) } @@ -28,7 +28,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.Title, cloud.PrivateURL), + tg.HTML.Link(resp.Folder.Title, resp.Folder.PrivateURL), "has been created!", ), ) diff --git a/telegram/telegram.go b/telegram/telegram.go index 521012d..83f6403 100644 --- a/telegram/telegram.go +++ b/telegram/telegram.go @@ -45,6 +45,7 @@ func Run(ctx context.Context, opts TelegramOptions) error { // Message(h.NewFolderHandler, tgb.TextHasPrefix("/folder")). Message(h.FarmTaskHandler, tgb.TextHasPrefix("/task")) + log.Print("Success init. Start poller.") return tgb.NewPoller( router, client, From 3a4b461cdad8f3470c8ced1025ba6e76eeb28a93 Mon Sep 17 00:00:00 2001 From: naudachu Date: Wed, 8 Nov 2023 19:18:12 +0500 Subject: [PATCH 2/3] - implement whole Folder's feature set --- cmd/main.go | 3 - discord/handler/handle_folder.go | 81 +++++++++++---------- internal/controller/control_folder.go | 100 ++++++++++++++++++++++++++ internal/controller/project.go | 11 +-- internal/domain/models.go | 4 ++ internal/services/cloud.go | 3 +- internal/storage/db/queries.sql.go | 30 ++++++++ internal/storage/sqlc/queries.sql | 6 ++ 8 files changed, 193 insertions(+), 45 deletions(-) create mode 100644 internal/controller/control_folder.go diff --git a/cmd/main.go b/cmd/main.go index f750f95..c764e4e 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -62,7 +62,6 @@ func run(conf domain.Config) { } }() - // go func() { opts := telegram.TelegramOptions{ // TicketsRepo: db, GitService: gitService, @@ -75,6 +74,4 @@ func run(conf domain.Config) { log.Fatalf("telegram bot cannot be runned: %v", err) defer os.Exit(1) } - // }() - } diff --git a/discord/handler/handle_folder.go b/discord/handler/handle_folder.go index fd69214..50ce21d 100644 --- a/discord/handler/handle_folder.go +++ b/discord/handler/handle_folder.go @@ -5,6 +5,7 @@ import ( "fmt" "log" "strconv" + "ticket-pimp/internal/controller" "github.com/bwmarrin/discordgo" ) @@ -30,20 +31,21 @@ func (h *router) CreateFolderHandler(nameMinLenght int) route { }, Handler: func(s *discordgo.Session, i *discordgo.InteractionCreate) { + // ΠœΠΎΠΌΠ΅Π½Ρ‚Π°Π»ΡŒΠ½Ρ‹ΠΉ ΠΎΡ‚Π²Π΅Ρ‚ для избСТания столкновСния с ΠΏΡ€ΠΎΡ‚ΡƒΡ…Π°Π½ΠΈΠ΅ΠΌ Ρ‚ΠΎΠΊΠ΅Π½Π° initialResponse := discordgo.InteractionResponse{ - // Ignore type for now, they will be discussed in "responses" Type: discordgo.InteractionResponseChannelMessageWithSource, Data: &discordgo.InteractionResponseData{ Flags: discordgo.MessageFlagsEphemeral, - Content: "..folder is going to be created", - Title: "πŸ“‚ Folder creation", + Content: "Cooking your query..", }, } s.InteractionRespond(i.Interaction, &initialResponse) + // ΠžΠΏΡ€Π΅Π΄Π΅Π»Π΅Π½ΠΈΠ΅ ΠΏΠ΅Ρ€Π΅ΠΌΠ΅Π½Π½ΠΎΠΉ для ΠΎΡ‚Π²Π΅Ρ‚Π° var result string = "unexpected result" + // ΠžΠΏΡ€Π΅Π΄Π΅Π»Π΅Π½ΠΈΠ΅ Π²Ρ‹Π±Ρ€Π°Π½Π½Ρ‹Ρ… Π²Π°Ρ€ΠΈΠ°Π½Ρ‚ΠΎΠ² ΠΎΡ‚Π²Π΅Ρ‚Π° options := i.ApplicationCommandData().Options optionMap := make(map[string]*discordgo.ApplicationCommandInteractionDataOption, len(options)) @@ -51,49 +53,54 @@ func (h *router) CreateFolderHandler(nameMinLenght int) route { optionMap[opt.Name] = opt } - var str string = "" - + name, insertedValueNotNil := optionMap[nameOption] dchan, err := s.Channel(i.ChannelID) + // Creating request: + var req controller.FolderRequest if err != nil { log.Printf("error while identifying channel: %v", err) + } else { + + if dchan.ParentID == strconv.Itoa(1150719794853716028) { + req.ChannelID = dchan.ID + if insertedValueNotNil { + req.InsertedName = name.StringValue() + } + + } else { + req.ChannelID = "" + if insertedValueNotNil { + req.InsertedName = name.StringValue() + } + } } - if dchan.ParentID == strconv.Itoa(1150719794853716028) { - log.Println("yes, channel is from `Projects`") - project, err := h.controller.GetProjectByChannelID(context.TODO(), i.ChannelID) - if err != nil { - result = fmt.Sprintf("unable to retrieve project from db, error: %v", err) - } else { - switch { - case project == nil: - if option, ok := optionMap[nameOption]; ok { - str = option.StringValue() - } else { - str = "Π’Ρ‹, Π»ΠΈΠ±ΠΎ Π² ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π΅ Π΄ΠΈΡ€Π΅ΠΊΡ‚ΠΎΡ€ΠΈΡŽ создавай, Π»ΠΈΠ±ΠΎ имя напиши, Π±Π»Π΅Ρ‚!" - } - default: - str = project.ShortName - } + // Making request: + resp := h.controller.CreateFolder(context.TODO(), req) + if resp.Project == nil { + result = "Надо Π½Π°ΠΏΠΈΡΠ°Ρ‚ΡŒ имя для ΠΏΠ°ΠΏΠΊΠΈ, ΠΈΠ»ΠΈ ΡΠΎΠ·Π΄Π°Ρ‚ΡŒ ΠΏΠ°ΠΏΠΊΡƒ ΠΈΠ· ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π°!" + } else { + result = fmt.Sprintf( + "## Project info:\nπŸ”‘ key: %s\nπŸ“‚ folder: %s\nπŸ‘Ύ project git: %s\nπŸš€ build git: %s\n\nErrors: %v", + resp.Project.ShortName, + resp.Project.Cloud, + resp.Project.ProjectGit, + resp.Project.BuildGit, + resp.Message, + ) + } - resp := h.controller.ICloud.CreateFolder(str) - if resp.ErrMessage != nil { - result = fmt.Sprintf("Command executed w/ errors: ``` %v``` \n But check this link: %s\n", resp.ErrMessage, resp.Folder.PrivateURL) - } else { - result = fmt.Sprintf("πŸ“‚ Folder was created: %s", resp.Folder.PrivateURL) - } - } + // Sending result: + _, err = s.FollowupMessageCreate(i.Interaction, true, &discordgo.WebhookParams{ + Content: result, + }) - _, err = s.FollowupMessageCreate(i.Interaction, true, &discordgo.WebhookParams{ - Content: result, + if err != nil { + s.FollowupMessageCreate(i.Interaction, true, &discordgo.WebhookParams{ + Content: fmt.Sprintf("Something went wrong: %v", err), }) - - if err != nil { - s.FollowupMessageCreate(i.Interaction, true, &discordgo.WebhookParams{ - Content: fmt.Sprintf("Something went wrong: %v", err), - }) - return - } + return } }, } diff --git a/internal/controller/control_folder.go b/internal/controller/control_folder.go new file mode 100644 index 0000000..a445ee6 --- /dev/null +++ b/internal/controller/control_folder.go @@ -0,0 +1,100 @@ +package controller + +import ( + "context" + "errors" + "fmt" + "log" + "ticket-pimp/internal/domain" + "ticket-pimp/internal/storage/db" + "time" + + "github.com/jackc/pgx/v5/pgtype" +) + +type FolderRequest struct { + ChannelID string + InsertedName string +} + +type ProjectResponse struct { + Project *domain.Project + Message error +} + +func (wc *WorkflowController) CreateFolder(ctx context.Context, req FolderRequest) *ProjectResponse { + + project, err := wc.GetProjectByChannelID(ctx, req.ChannelID) + if err != nil { + return &ProjectResponse{ + Project: nil, + Message: fmt.Errorf("unable to retrieve project from db: %v", err), + } + } + + var ( + name string + dbticket db.Ticket + result ProjectResponse + ) + + if project != nil { + switch { + case project.Cloud != "": + return &ProjectResponse{ + Project: project, + Message: nil, + } + case project.ShortName != "": + name = project.ShortName + case req.InsertedName != "": + name = req.InsertedName + } + response := wc.ICloud.CreateFolder(name) + + dbticket, err = wc.q.UpdateTicketFolder( + ctx, + db.UpdateTicketFolderParams{ + Folder: pgtype.Text{String: response.Folder.PrivateURL, Valid: true}, + UpdatedAt: pgtype.Timestamptz{Time: time.Now(), InfinityModifier: 0, Valid: true}, + Channelid: pgtype.Text{String: req.ChannelID, Valid: true}, + }) + if err != nil { + log.Printf("unable to scan row from db: %v", err) + return &ProjectResponse{ + Project: project, + Message: fmt.Errorf("unable to update project: %v", err), + } + } + + result = ProjectResponse{ + Project: &domain.Project{ + ID: string(dbticket.ID), + ShortName: dbticket.Key.String, + ChannelID: dbticket.Channelid.String, + ProjectGit: dbticket.ProjectGit.String, + BuildGit: dbticket.BuildGit.String, + Cloud: dbticket.Folder.String, + }, + Message: response.ErrMessage, + } + } else { + if req.InsertedName != "" { + response := wc.ICloud.CreateFolder(req.InsertedName) + result = ProjectResponse{ + Project: &domain.Project{ + Cloud: response.Folder.PrivateURL, + }, + Message: response.ErrMessage, + } + } else { + return &ProjectResponse{ + Project: nil, + Message: errors.New("ΠΏΠ΅Ρ€Π΅Π΄Π°Π½ΠΎ пустоС имя"), + } + } + } + + return &result + +} diff --git a/internal/controller/project.go b/internal/controller/project.go index ee68bfe..54094f3 100644 --- a/internal/controller/project.go +++ b/internal/controller/project.go @@ -74,10 +74,13 @@ func (wc *WorkflowController) GetProjectByChannelID(ctx context.Context, id stri return nil, err } else { proj = domain.Project{ - ID: string(dbTicket.ID), - ShortName: dbTicket.Key.String, - Name: dbTicket.Key.String, - ChannelID: dbTicket.Channelid.String, + ID: string(dbTicket.ID), + ShortName: dbTicket.Key.String, + Name: dbTicket.Key.String, + ChannelID: dbTicket.Channelid.String, + ProjectGit: dbTicket.ProjectGit.String, + BuildGit: dbTicket.BuildGit.String, + Cloud: dbTicket.Folder.String, } } return &proj, nil diff --git a/internal/domain/models.go b/internal/domain/models.go index 96f5a99..8dff715 100644 --- a/internal/domain/models.go +++ b/internal/domain/models.go @@ -87,6 +87,10 @@ type Project struct { ShortName string `json:"shortName"` Name string `json:"name"` ChannelID string `json:"channel_id"` + + ProjectGit string `json:"project_git"` + BuildGit string `json:"build_git"` + Cloud string `json:"cloud"` } type ProjectID struct { diff --git a/internal/services/cloud.go b/internal/services/cloud.go index 2b3946a..cd37732 100644 --- a/internal/services/cloud.go +++ b/internal/services/cloud.go @@ -1,6 +1,7 @@ package services import ( + "errors" "fmt" "log" "strconv" @@ -82,7 +83,7 @@ func (c *Cloud) CreateFolder(name string) Response { // if strings.Contains(err.Error(), "already exists") { R.Folder.PrivateURL = c.BaseURL + R.Folder.PathTo - R.ErrMessage = err + R.ErrMessage = errors.New("guess, that folder already exists") // Try to set short URL to the d entity if err := c.setPrivateURL(requestPath, R.Folder); err != nil { R.ErrMessage = err diff --git a/internal/storage/db/queries.sql.go b/internal/storage/db/queries.sql.go index 505ed37..d760019 100644 --- a/internal/storage/db/queries.sql.go +++ b/internal/storage/db/queries.sql.go @@ -215,3 +215,33 @@ func (q *Queries) UpdateTicketByID(ctx context.Context, arg UpdateTicketByIDPara ) return err } + +const updateTicketFolder = `-- name: UpdateTicketFolder :one +UPDATE tickets +SET folder = $1, updated_at = $2 +WHERE channelID = $3 +RETURNING id, key, channelid, project_git, build_git, folder, created_at, deleted_at, updated_at +` + +type UpdateTicketFolderParams struct { + Folder pgtype.Text + UpdatedAt pgtype.Timestamptz + Channelid pgtype.Text +} + +func (q *Queries) UpdateTicketFolder(ctx context.Context, arg UpdateTicketFolderParams) (Ticket, error) { + row := q.db.QueryRow(ctx, updateTicketFolder, arg.Folder, arg.UpdatedAt, arg.Channelid) + var i Ticket + err := row.Scan( + &i.ID, + &i.Key, + &i.Channelid, + &i.ProjectGit, + &i.BuildGit, + &i.Folder, + &i.CreatedAt, + &i.DeletedAt, + &i.UpdatedAt, + ) + return i, err +} diff --git a/internal/storage/sqlc/queries.sql b/internal/storage/sqlc/queries.sql index b9911aa..69c3280 100644 --- a/internal/storage/sqlc/queries.sql +++ b/internal/storage/sqlc/queries.sql @@ -15,6 +15,12 @@ INSERT INTO tickets ( ) RETURNING *; +-- name: UpdateTicketFolder :one +UPDATE tickets +SET folder = $1, updated_at = $2 +WHERE channelID = $3 +RETURNING *; + -- name: ListTickets :many SELECT * FROM tickets WHERE deleted_at IS NULL; From 092ba475a2175436b91ce69d79c6df39c35ae78e Mon Sep 17 00:00:00 2001 From: naudachu Date: Thu, 9 Nov 2023 16:24:55 +0500 Subject: [PATCH 3/3] - create repo with discord bot; --- discord/handler/handle_folder.go | 4 +- discord/handler/handle_git.go | 131 ++++++++++++++ discord/handler/handle_repo.go | 109 ------------ internal/controller/control_folder.go | 6 - internal/controller/control_git.go | 168 ++++++++++++++++++ .../{project.go => control_project.go} | 0 internal/controller/control_workflow.go | 84 +++++++++ internal/controller/controller.go | 78 +------- internal/controller/tickets_config.go | 22 --- internal/storage/db/queries.sql.go | 60 +++++++ internal/storage/sqlc/queries.sql | 12 ++ 11 files changed, 460 insertions(+), 214 deletions(-) create mode 100644 discord/handler/handle_git.go delete mode 100644 discord/handler/handle_repo.go create mode 100644 internal/controller/control_git.go rename internal/controller/{project.go => control_project.go} (100%) create mode 100644 internal/controller/control_workflow.go delete mode 100644 internal/controller/tickets_config.go diff --git a/discord/handler/handle_folder.go b/discord/handler/handle_folder.go index 50ce21d..0373596 100644 --- a/discord/handler/handle_folder.go +++ b/discord/handler/handle_folder.go @@ -53,11 +53,11 @@ func (h *router) CreateFolderHandler(nameMinLenght int) route { optionMap[opt.Name] = opt } + // Creating request: + var req controller.FolderRequest name, insertedValueNotNil := optionMap[nameOption] dchan, err := s.Channel(i.ChannelID) - // Creating request: - var req controller.FolderRequest if err != nil { log.Printf("error while identifying channel: %v", err) } else { diff --git a/discord/handler/handle_git.go b/discord/handler/handle_git.go new file mode 100644 index 0000000..5816b9f --- /dev/null +++ b/discord/handler/handle_git.go @@ -0,0 +1,131 @@ +package handler + +import ( + "context" + "fmt" + "log" + "strconv" + "ticket-pimp/internal/controller" + + "github.com/bwmarrin/discordgo" +) + +func (h *router) CreateRepoHandler(repoNameMinLength int) route { + const ( + repoType = "repo_type" + projectRepo = "project_repo" + buildRepo = "build_repo" + nameOption = "repo_name" + ) + + return route{ + Command: discordgo.ApplicationCommand{ + Name: "repo", + Description: "Command for repository creation", + Options: []*discordgo.ApplicationCommandOption{ + { + Type: discordgo.ApplicationCommandOptionString, + Name: repoType, + Description: "The type of repo", + Required: true, + Choices: []*discordgo.ApplicationCommandOptionChoice{ + { + Name: "Unity project repo", + Value: projectRepo, + }, + { + Name: "XCode build repo", + Value: buildRepo, + }, + }, + }, + { + Type: discordgo.ApplicationCommandOptionString, + Name: nameOption, + Description: "Type the repository's name", + Required: false, + MinLength: &repoNameMinLength, + }, + }, + }, + Handler: func(s *discordgo.Session, i *discordgo.InteractionCreate) { + // ΠœΠΎΠΌΠ΅Π½Ρ‚Π°Π»ΡŒΠ½Ρ‹ΠΉ ΠΎΡ‚Π²Π΅Ρ‚ для избСТания столкновСния с ΠΏΡ€ΠΎΡ‚ΡƒΡ…Π°Π½ΠΈΠ΅ΠΌ Ρ‚ΠΎΠΊΠ΅Π½Π° + initialResponse := discordgo.InteractionResponse{ + Type: discordgo.InteractionResponseChannelMessageWithSource, + Data: &discordgo.InteractionResponseData{ + Flags: discordgo.MessageFlagsEphemeral, + Content: "Cooking your query..", + }, + } + + s.InteractionRespond(i.Interaction, &initialResponse) + + // ΠžΠΏΡ€Π΅Π΄Π΅Π»Π΅Π½ΠΈΠ΅ ΠΏΠ΅Ρ€Π΅ΠΌΠ΅Π½Π½ΠΎΠΉ для ΠΎΡ‚Π²Π΅Ρ‚Π° + var result string = "unexpected result" + + // Access options in the order provided by the user. + options := i.ApplicationCommandData().Options + + // Or convert the slice into a map + optionMap := make(map[string]*discordgo.ApplicationCommandInteractionDataOption, len(options)) + for _, opt := range options { + optionMap[opt.Name] = opt + } + + // Creating request: + var req controller.GitRequest + name, insertedValueNotNil := optionMap[nameOption] + isBuild := optionMap[repoType] + switch isBuild.StringValue() { + case buildRepo: + req.IsBuildGit = true + case projectRepo: + req.IsBuildGit = false + } + dchan, err := s.Channel(i.ChannelID) + + if err != nil { + log.Printf("error while identifying channel: %v", err) + } else { + + if dchan.ParentID == strconv.Itoa(1150719794853716028) { + req.ChannelID = dchan.ID + if insertedValueNotNil { + req.InsertedName = name.StringValue() + } + } else { + req.ChannelID = "" + if insertedValueNotNil { + req.InsertedName = name.StringValue() + } + } + } + + // Making request: + resp := h.controller.CreateGit(context.TODO(), req) + if resp.Project == nil { + result = resp.Message.Error() + } else { + result = fmt.Sprintf( + "## Project info:\nπŸ”‘ key: %s\nπŸ“‚ folder: %s\nπŸ‘Ύ project git: %s\nπŸš€ build git: %s\n\nErrors: %v", + resp.Project.ShortName, + resp.Project.Cloud, + resp.Project.ProjectGit, + resp.Project.BuildGit, + resp.Message, + ) + } + // Sending result: + _, err = s.FollowupMessageCreate(i.Interaction, true, &discordgo.WebhookParams{ + Content: result, + }) + + if err != nil { + s.FollowupMessageCreate(i.Interaction, true, &discordgo.WebhookParams{ + Content: fmt.Sprintf("Something went wrong: %v", err), + }) + return + } + }, + } +} diff --git a/discord/handler/handle_repo.go b/discord/handler/handle_repo.go deleted file mode 100644 index a62d7fa..0000000 --- a/discord/handler/handle_repo.go +++ /dev/null @@ -1,109 +0,0 @@ -package handler - -import ( - "context" - "fmt" - - "github.com/bwmarrin/discordgo" -) - -func (h *router) CreateRepoHandler(repoNameMinLength int) route { - const ( - projectRepo = "project_repo" - buildRepo = "build_repo" - ) - - return route{ - Command: discordgo.ApplicationCommand{ - Name: "repo", - Description: "Command for repository creation", - Options: []*discordgo.ApplicationCommandOption{ - { - Type: discordgo.ApplicationCommandOptionString, - Name: "repo_type", - Description: "The type of repo", - Required: true, - Choices: []*discordgo.ApplicationCommandOptionChoice{ - { - Name: "Unity project repo", - Value: projectRepo, - }, - { - Name: "XCode build repo", - Value: buildRepo, - }, - }, - }, - { - Type: discordgo.ApplicationCommandOptionString, - Name: "repo_name", - Description: "Type the repository's name", - Required: false, - MinLength: &repoNameMinLength, - }, - }, - }, - Handler: func(s *discordgo.Session, i *discordgo.InteractionCreate) { - var result string - // Access options in the order provided by the user. - options := i.ApplicationCommandData().Options - - // Or convert the slice into a map - optionMap := make(map[string]*discordgo.ApplicationCommandInteractionDataOption, len(options)) - for _, opt := range options { - optionMap[opt.Name] = opt - } - - var str string = "" - - project, err := h.controller.GetProjectByChannelID(context.TODO(), i.ChannelID) - if err != nil { - result = fmt.Sprintf("unable to retrieve project from db, error: %v", err) - } else { - var suffix string - if option, ok := optionMap["repo_type"]; ok { - switch option.Value { - case projectRepo: - suffix = "" - case buildRepo: - suffix = "-build" - } - } - - if project == nil { - if option, ok := optionMap["repo_name"]; ok { - str = option.StringValue() - } else { - str = "" - } - } else { - str = project.ShortName - } - - if str == "" { - result = "Π’Ρ‹, Π»ΠΈΠ±ΠΎ Π² ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π΅ Ρ€Π΅ΠΏΠΎ создавай, Π»ΠΈΠ±ΠΎ имя напиши, Π±Π»Π΅Ρ‚!" - } else { - str = str + suffix - - // var g *domain.Git - - g, err := h.controller.IGit.CreateRepo(str) - if err != nil { - result = fmt.Sprintf("error while repo creation: %v", err) - } else { - result = "πŸš€ " + g.HtmlUrl + " was created" - } - } - } - - resp := &discordgo.InteractionResponse{ - Type: discordgo.InteractionResponseChannelMessageWithSource, - Data: &discordgo.InteractionResponseData{ - Content: result, - }, - } - - s.InteractionRespond(i.Interaction, resp) - }, - } -} diff --git a/internal/controller/control_folder.go b/internal/controller/control_folder.go index a445ee6..7a8f61b 100644 --- a/internal/controller/control_folder.go +++ b/internal/controller/control_folder.go @@ -17,11 +17,6 @@ type FolderRequest struct { InsertedName string } -type ProjectResponse struct { - Project *domain.Project - Message error -} - func (wc *WorkflowController) CreateFolder(ctx context.Context, req FolderRequest) *ProjectResponse { project, err := wc.GetProjectByChannelID(ctx, req.ChannelID) @@ -96,5 +91,4 @@ func (wc *WorkflowController) CreateFolder(ctx context.Context, req FolderReques } return &result - } diff --git a/internal/controller/control_git.go b/internal/controller/control_git.go new file mode 100644 index 0000000..c9e1f0d --- /dev/null +++ b/internal/controller/control_git.go @@ -0,0 +1,168 @@ +package controller + +import ( + "context" + "errors" + "fmt" + "log" + "ticket-pimp/internal/domain" + "ticket-pimp/internal/storage/db" + "time" + + "github.com/jackc/pgx/v5/pgtype" +) + +type GitRequest struct { + ChannelID string + InsertedName string + IsBuildGit bool +} + +func (wc *WorkflowController) createGitForExistingProject(ctx context.Context, req GitRequest, p *domain.Project) *ProjectResponse { + var ( + name string = "" + dbticket db.Ticket + ) + switch { + case p.ShortName != "": + name = p.ShortName + if req.IsBuildGit { + name += "-build" + } + case req.InsertedName != "": + name = req.InsertedName + if req.IsBuildGit { + name += "-build" + } + } + + // response := wc.ICloud.CreateFolder(name) + git, err := wc.IGit.CreateRepo(name) + if err != nil { + return &ProjectResponse{ + Project: p, + Message: fmt.Errorf("unable to create git w/ an error: %v", err), + } + } + + if req.IsBuildGit { + dbticket, err = wc.q.UpdateTicketBuildGit( + ctx, + db.UpdateTicketBuildGitParams{ + BuildGit: pgtype.Text{String: git.HtmlUrl, Valid: true}, + UpdatedAt: pgtype.Timestamptz{Time: time.Now(), InfinityModifier: 0, Valid: true}, + Channelid: pgtype.Text{String: req.ChannelID, Valid: true}, + }) + if err != nil { + log.Printf("unable to scan row from db: %v", err) + return &ProjectResponse{ + Project: p, + Message: fmt.Errorf("unable to update project: %v", err), + } + } + } else { + dbticket, err = wc.q.UpdateTicketProjectGit( + ctx, + db.UpdateTicketProjectGitParams{ + ProjectGit: pgtype.Text{String: git.HtmlUrl, Valid: true}, + UpdatedAt: pgtype.Timestamptz{Time: time.Now(), InfinityModifier: 0, Valid: true}, + Channelid: pgtype.Text{String: req.ChannelID, Valid: true}, + }, + ) + if err != nil { + log.Printf("unable to scan row from db: %v", err) + return &ProjectResponse{ + Project: p, + Message: fmt.Errorf("unable to update project: %v", err), + } + } + } + + return &ProjectResponse{ + Project: &domain.Project{ + ID: string(dbticket.ID), + ShortName: dbticket.Key.String, + ChannelID: dbticket.Channelid.String, + ProjectGit: dbticket.ProjectGit.String, + BuildGit: dbticket.BuildGit.String, + Cloud: dbticket.Folder.String, + }, + Message: err, + } +} + +func (wc *WorkflowController) CreateGit(ctx context.Context, req GitRequest) *ProjectResponse { + + // [ ] Валидация Π½Π° пустой ΠΊΠ°Π½Π°Π»? + p, err := wc.GetProjectByChannelID(ctx, req.ChannelID) + if err != nil { + return &ProjectResponse{ + Project: nil, + Message: fmt.Errorf("unable to retrieve project from db: %v", err), + } + } + + // var ( + // name string + // dbticket db.Ticket + // result ProjectResponse + // ) + + switch { + case p != nil && req.IsBuildGit: + if p.BuildGit != "" { + return &ProjectResponse{ + Project: p, + Message: errors.New("build git already exists"), + } + } else { + // [x] + return wc.createGitForExistingProject(ctx, req, p) + } + case p != nil && !req.IsBuildGit: + if p.ProjectGit != "" { + return &ProjectResponse{ + Project: p, + Message: errors.New("project git already exists"), + } + } else { + // [x] + return wc.createGitForExistingProject(ctx, req, p) + } + default: + if req.InsertedName != "" { + + if req.IsBuildGit { + req.InsertedName += "-build" + } + + git, err := wc.IGit.CreateRepo(req.InsertedName) + if err != nil || git == nil { + return &ProjectResponse{ + Project: nil, + Message: err, + } + } else { + if req.IsBuildGit { + return &ProjectResponse{ + Project: &domain.Project{ + BuildGit: git.HtmlUrl, + }, + Message: err, + } + } + return &ProjectResponse{ + Project: &domain.Project{ + ProjectGit: git.HtmlUrl, + }, + Message: err, + } + } + } else { + return &ProjectResponse{ + Project: nil, + Message: errors.New("ΠΏΠ΅Ρ€Π΅Π΄Π°Π½ΠΎ пустоС имя"), + } + } + } +} diff --git a/internal/controller/project.go b/internal/controller/control_project.go similarity index 100% rename from internal/controller/project.go rename to internal/controller/control_project.go diff --git a/internal/controller/control_workflow.go b/internal/controller/control_workflow.go new file mode 100644 index 0000000..c24fdbb --- /dev/null +++ b/internal/controller/control_workflow.go @@ -0,0 +1,84 @@ +package controller + +import ( + "context" + "fmt" + "log" + "strings" + "sync" + "ticket-pimp/internal/domain" + "ticket-pimp/internal/storage/db" + + "github.com/jackc/pgx/v5/pgtype" +) + +func (wc *WorkflowController) FullProjectInit(name, key, id string) (string, error) { + + appKey := fmt.Sprintf("%s-%s", key, id) + + var ( + git, gitBuild *domain.Git + cloud *domain.Folder + ) + + var wg sync.WaitGroup + wg.Add(3) + + go func(ref **domain.Git) { + defer wg.Done() + *ref, _ = wc.IGit.CreateRepo(appKey) + }(&git) + + go func(ref **domain.Git) { + defer wg.Done() + *ref, _ = wc.IGit.CreateRepo(appKey + "-build") + }(&gitBuild) + + go func(ref **domain.Folder) { + defer wg.Done() + + *ref = wc.ICloud.CreateFolder(appKey).Folder + }(&cloud) + + wg.Wait() + + var gitResult, gitBuildResult, cloudResult string + + if git == nil { + gitResult = "cannot create git" + } else { + gitResult = git.HtmlUrl + } + + if gitBuild == nil { + gitBuildResult = "cannot create git" + } else { + gitBuildResult = fmt.Sprintf("ssh://%s/%s.git", gitBuild.SshUrl, gitBuild.FullName) + } + + if cloud == nil { + cloudResult = "cannot create folder" + } else { + cloudResult = cloud.PrivateURL + } + ctx := context.TODO() + + insertedTicket, err := wc.q.CreateTicket(ctx, db.CreateTicketParams{ + Key: pgtype.Text{String: appKey, Valid: true}, + Channelid: pgtype.Text{}, + }) + if err != nil { + log.Fatal(err) + } + log.Print(insertedTicket) + + wc.ICoda.CreateApp(domain.CodaApplication{ + ID: appKey, + Summary: strings.TrimSpace(name), + Git: gitResult, + GitBuild: gitBuildResult, + Folder: cloudResult, + }) + + return appKey, nil +} diff --git a/internal/controller/controller.go b/internal/controller/controller.go index a804b29..c65d97d 100644 --- a/internal/controller/controller.go +++ b/internal/controller/controller.go @@ -1,16 +1,10 @@ package controller import ( - "context" - "fmt" - "log" - "strings" - "sync" "ticket-pimp/internal/domain" "ticket-pimp/internal/services" "ticket-pimp/internal/storage/db" - "github.com/jackc/pgx/v5/pgtype" "github.com/jackc/pgx/v5/pgxpool" ) @@ -37,73 +31,7 @@ func NewWorkflowController( } } -func (wc *WorkflowController) FullProjectInit(name, key, id string) (string, error) { - - appKey := fmt.Sprintf("%s-%s", key, id) - - var ( - git, gitBuild *domain.Git - cloud *domain.Folder - ) - - var wg sync.WaitGroup - wg.Add(3) - - go func(ref **domain.Git) { - defer wg.Done() - *ref, _ = wc.IGit.CreateRepo(appKey) - }(&git) - - go func(ref **domain.Git) { - defer wg.Done() - *ref, _ = wc.IGit.CreateRepo(appKey + "-build") - }(&gitBuild) - - go func(ref **domain.Folder) { - defer wg.Done() - - *ref = wc.ICloud.CreateFolder(appKey).Folder - }(&cloud) - - wg.Wait() - - var gitResult, gitBuildResult, cloudResult string - - if git == nil { - gitResult = "cannot create git" - } else { - gitResult = git.HtmlUrl - } - - if gitBuild == nil { - gitBuildResult = "cannot create git" - } else { - gitBuildResult = fmt.Sprintf("ssh://%s/%s.git", gitBuild.SshUrl, gitBuild.FullName) - } - - if cloud == nil { - cloudResult = "cannot create folder" - } else { - cloudResult = cloud.PrivateURL - } - ctx := context.TODO() - - insertedTicket, err := wc.q.CreateTicket(ctx, db.CreateTicketParams{ - Key: pgtype.Text{String: appKey, Valid: true}, - Channelid: pgtype.Text{}, - }) - if err != nil { - log.Fatal(err) - } - log.Print(insertedTicket) - - wc.ICoda.CreateApp(domain.CodaApplication{ - ID: appKey, - Summary: strings.TrimSpace(name), - Git: gitResult, - GitBuild: gitBuildResult, - Folder: cloudResult, - }) - - return appKey, nil +type ProjectResponse struct { + Project *domain.Project + Message error } diff --git a/internal/controller/tickets_config.go b/internal/controller/tickets_config.go deleted file mode 100644 index b4a5392..0000000 --- a/internal/controller/tickets_config.go +++ /dev/null @@ -1,22 +0,0 @@ -package controller - -import ( - "context" - "ticket-pimp/internal/domain" - db "ticket-pimp/internal/storage/db" -) - -type IConfigController interface { - Get(context.Context) (domain.ApplicationConfig, error) - Update(context.Context) (domain.ApplicationConfig, error) -} - -type AppConfig struct { - db *db.Queries -} - -func NewAppConfig(db *db.Queries) AppConfig { - return AppConfig{ - db: db, - } -} diff --git a/internal/storage/db/queries.sql.go b/internal/storage/db/queries.sql.go index d760019..a425106 100644 --- a/internal/storage/db/queries.sql.go +++ b/internal/storage/db/queries.sql.go @@ -195,6 +195,36 @@ func (q *Queries) SetNewConfig(ctx context.Context) (Appconfig, error) { return i, err } +const updateTicketBuildGit = `-- name: UpdateTicketBuildGit :one +UPDATE tickets +SET build_git = $1, updated_at = $2 +WHERE channelID = $3 +RETURNING id, key, channelid, project_git, build_git, folder, created_at, deleted_at, updated_at +` + +type UpdateTicketBuildGitParams struct { + BuildGit pgtype.Text + UpdatedAt pgtype.Timestamptz + Channelid pgtype.Text +} + +func (q *Queries) UpdateTicketBuildGit(ctx context.Context, arg UpdateTicketBuildGitParams) (Ticket, error) { + row := q.db.QueryRow(ctx, updateTicketBuildGit, arg.BuildGit, arg.UpdatedAt, arg.Channelid) + var i Ticket + err := row.Scan( + &i.ID, + &i.Key, + &i.Channelid, + &i.ProjectGit, + &i.BuildGit, + &i.Folder, + &i.CreatedAt, + &i.DeletedAt, + &i.UpdatedAt, + ) + return i, err +} + const updateTicketByID = `-- name: UpdateTicketByID :exec UPDATE tickets SET project_git = $1, build_git = $2, folder = $3 WHERE id = $4 ` @@ -245,3 +275,33 @@ func (q *Queries) UpdateTicketFolder(ctx context.Context, arg UpdateTicketFolder ) return i, err } + +const updateTicketProjectGit = `-- name: UpdateTicketProjectGit :one +UPDATE tickets +SET project_git = $1, updated_at = $2 +WHERE channelID = $3 +RETURNING id, key, channelid, project_git, build_git, folder, created_at, deleted_at, updated_at +` + +type UpdateTicketProjectGitParams struct { + ProjectGit pgtype.Text + UpdatedAt pgtype.Timestamptz + Channelid pgtype.Text +} + +func (q *Queries) UpdateTicketProjectGit(ctx context.Context, arg UpdateTicketProjectGitParams) (Ticket, error) { + row := q.db.QueryRow(ctx, updateTicketProjectGit, arg.ProjectGit, arg.UpdatedAt, arg.Channelid) + var i Ticket + err := row.Scan( + &i.ID, + &i.Key, + &i.Channelid, + &i.ProjectGit, + &i.BuildGit, + &i.Folder, + &i.CreatedAt, + &i.DeletedAt, + &i.UpdatedAt, + ) + return i, err +} diff --git a/internal/storage/sqlc/queries.sql b/internal/storage/sqlc/queries.sql index 69c3280..d5320ee 100644 --- a/internal/storage/sqlc/queries.sql +++ b/internal/storage/sqlc/queries.sql @@ -21,6 +21,18 @@ SET folder = $1, updated_at = $2 WHERE channelID = $3 RETURNING *; +-- name: UpdateTicketProjectGit :one +UPDATE tickets +SET project_git = $1, updated_at = $2 +WHERE channelID = $3 +RETURNING *; + +-- name: UpdateTicketBuildGit :one +UPDATE tickets +SET build_git = $1, updated_at = $2 +WHERE channelID = $3 +RETURNING *; + -- name: ListTickets :many SELECT * FROM tickets WHERE deleted_at IS NULL;