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	metaKeyJiraOperationId = "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}