jira.go

  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}