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