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