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	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}