1// Package jira contains the Jira bridge implementation
2package jira
3
4import (
5 "context"
6 "fmt"
7 "sort"
8 "time"
9
10 "github.com/MichaelMure/git-bug/bridge/core"
11 "github.com/MichaelMure/git-bug/bridge/core/auth"
12 "github.com/MichaelMure/git-bug/input"
13)
14
15const (
16 target = "jira"
17
18 metaKeyJiraId = "jira-id"
19 metaKeyJiraDerivedId = "jira-derived-id"
20 metaKeyJiraKey = "jira-key"
21 metaKeyJiraUser = "jira-user"
22 metaKeyJiraProject = "jira-project"
23 metaKeyJiraExportTime = "jira-export-time"
24 metaKeyJiraLogin = "jira-login"
25
26 confKeyBaseUrl = "base-url"
27 confKeyProject = "project"
28 confKeyDefaultLogin = "default-login"
29 confKeyCredentialType = "credentials-type" // "SESSION" or "TOKEN"
30 confKeyIDMap = "bug-id-map"
31 confKeyIDRevMap = "bug-id-revmap"
32 // the issue type when exporting a new bug. Default is Story (10001)
33 confKeyCreateDefaults = "create-issue-defaults"
34 // if set, the bridge fill this JIRA field with the `git-bug` id when exporting
35 confKeyCreateGitBug = "create-issue-gitbug-id"
36
37 defaultTimeout = 60 * time.Second
38)
39
40var _ core.BridgeImpl = &Jira{}
41
42// Jira Main object for the bridge
43type Jira struct{}
44
45// Target returns "jira"
46func (*Jira) Target() string {
47 return target
48}
49
50func (*Jira) LoginMetaKey() string {
51 return metaKeyJiraLogin
52}
53
54// NewImporter returns the jira importer
55func (*Jira) NewImporter() core.Importer {
56 return &jiraImporter{}
57}
58
59// NewExporter returns the jira exporter
60func (*Jira) NewExporter() core.Exporter {
61 return &jiraExporter{}
62}
63
64func buildClient(ctx context.Context, baseURL string, credType string, cred auth.Credential) (*Client, error) {
65 client := NewClient(ctx, baseURL)
66
67 var login, password string
68
69 switch cred := cred.(type) {
70 case *auth.LoginPassword:
71 login = cred.Login
72 password = cred.Password
73 case *auth.Login:
74 login = cred.Login
75 p, err := input.PromptPassword(fmt.Sprintf("Password for %s", login), "password", input.Required)
76 if err != nil {
77 return nil, err
78 }
79 password = p
80 }
81
82 err := client.Login(credType, login, password)
83 if err != nil {
84 return nil, err
85 }
86
87 return client, nil
88}
89
90// stringInSlice returns true if needle is found in haystack
91func stringInSlice(needle string, haystack []string) bool {
92 for _, match := range haystack {
93 if match == needle {
94 return true
95 }
96 }
97 return false
98}
99
100// Given two string slices, return three lists containing:
101// 1. elements found only in the first input list
102// 2. elements found only in the second input list
103// 3. elements found in both input lists
104func setSymmetricDifference(setA, setB []string) ([]string, []string, []string) {
105 sort.Strings(setA)
106 sort.Strings(setB)
107
108 maxLen := len(setA) + len(setB)
109 onlyA := make([]string, 0, maxLen)
110 onlyB := make([]string, 0, maxLen)
111 both := make([]string, 0, maxLen)
112
113 idxA := 0
114 idxB := 0
115
116 for idxA < len(setA) && idxB < len(setB) {
117 if setA[idxA] < setB[idxB] {
118 // In the first set, but not the second
119 onlyA = append(onlyA, setA[idxA])
120 idxA++
121 } else if setA[idxA] > setB[idxB] {
122 // In the second set, but not the first
123 onlyB = append(onlyB, setB[idxB])
124 idxB++
125 } else {
126 // In both
127 both = append(both, setA[idxA])
128 idxA++
129 idxB++
130 }
131 }
132
133 for ; idxA < len(setA); idxA++ {
134 // Leftovers in the first set, not the second
135 onlyA = append(onlyA, setA[idxA])
136 }
137
138 for ; idxB < len(setB); idxB++ {
139 // Leftovers in the second set, not the first
140 onlyB = append(onlyB, setB[idxB])
141 }
142
143 return onlyA, onlyB, both
144}