diff --git a/go.mod b/go.mod index a29351b..d512db2 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.12 require ( github.com/Masterminds/sprig/v3 v3.2.2 github.com/PuerkitoBio/goquery v1.8.0 + github.com/gfleury/go-bitbucket-v1 v0.0.0-20230830121038-6e30c5760c87 github.com/gorilla/mux v1.8.0 github.com/mattermost/mattermost-plugin-api v0.0.22 github.com/mattermost/mattermost-server/v6 v6.5.2 diff --git a/go.sum b/go.sum index 2aa338f..5a2f2f8 100644 --- a/go.sum +++ b/go.sum @@ -476,6 +476,10 @@ github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYis github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc= github.com/getsentry/sentry-go v0.11.0/go.mod h1:KBQIxiZAetw62Cj8Ri964vAEWVdgfaUCn30Q3bCvANo= github.com/getsentry/sentry-go v0.12.0/go.mod h1:NSap0JBYWzHND8oMbyi0+XZhUalc1TBdRL1M71JZW2c= +github.com/gfleury/go-bitbucket-v1 v0.0.0-20230830121038-6e30c5760c87 h1:aix/N0cwFcyaYksDTa96rcila54zTQ6Nk6yENtgO7yM= +github.com/gfleury/go-bitbucket-v1 v0.0.0-20230830121038-6e30c5760c87/go.mod h1:6saoZ1uJyRD/w4t5Djj8mik5W9cLjSKvba6ZaryV+98= +github.com/gfleury/go-bitbucket-v1/test/bb-mock-server v0.0.0-20230825095122-9bc1711434ab h1:BeG9dDWckFi/p5Gvqq3wTEDXsUV4G6bdvjEHMOT2B8E= +github.com/gfleury/go-bitbucket-v1/test/bb-mock-server v0.0.0-20230825095122-9bc1711434ab/go.mod h1:VssB0kb1cETNaFFC/0mHVCj+7i5TS2xraYq+tl9JLwE= github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gigawattio/window v0.0.0-20180317192513-0f5467e35573/go.mod h1:eBvb3i++NHDH4Ugo9qCvMw8t0mTSctaEa5blJbWcNxs= @@ -1036,6 +1040,7 @@ github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:F github.com/mitchellh/mapstructure v0.0.0-20180220230111-00c29f56e238/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.4.3 h1:OVowDSCllw/YjdLkam3/sm7wEtOy59d8ndGgCcyj8cs= github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A= github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY= @@ -1449,8 +1454,9 @@ github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1 github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.4/go.mod h1:rmuwmfZ0+bvzB24eSC//bk1R1Zp3hM0OXYv/G2LIilg= -github.com/yuin/goldmark v1.4.5 h1:4OEQwtW2uLXjEdgnGM3Vg652Pq37X7NOIRzFWb3BzIc= github.com/yuin/goldmark v1.4.5/go.mod h1:rmuwmfZ0+bvzB24eSC//bk1R1Zp3hM0OXYv/G2LIilg= +github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs= github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA= github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg= @@ -1582,6 +1588,7 @@ golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1659,8 +1666,10 @@ golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd h1:O7DYs+zxREGLKzKoMQrtrEacpb0ZVXA5rIwylE2Xchk= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/oauth2 v0.0.0-20180227000427-d7d64896b5ff/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -1697,6 +1706,7 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180224232135-f6cff0780e54/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1822,11 +1832,15 @@ golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220207234003-57398862261d h1:Bm7BNOQt2Qv7ZqysjeLjgCBanX+88Z/OtdvsrEv1Djc= golang.org/x/sys v0.0.0-20220207234003-57398862261d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1835,8 +1849,9 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1925,6 +1940,7 @@ golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/server/api.go b/server/api.go index 86df01a..08f1494 100644 --- a/server/api.go +++ b/server/api.go @@ -15,6 +15,7 @@ import ( "golang.org/x/oauth2" "github.com/mattermost/mattermost-server/v6/model" + "github.com/mattermost/mattermost-server/v6/plugin" ) const ( @@ -154,7 +155,7 @@ func checkPluginRequest(next http.HandlerFunc) http.HandlerFunc { } } -func (p *Plugin) ServeHTTP(w http.ResponseWriter, r *http.Request) { +func (p *Plugin) ServeHTTP(_ *plugin.Context, w http.ResponseWriter, r *http.Request) { config := p.getConfiguration() if err := config.IsValid(); err != nil { diff --git a/server/api_test.go b/server/api_test.go index 45b0985..5959923 100644 --- a/server/api_test.go +++ b/server/api_test.go @@ -72,7 +72,7 @@ func TestPlugin_ServeHTTP(t *testing.T) { req := test.httpTest.CreateHTTPRequest(test.request) req.Header.Add("Mattermost-User-ID", test.userID) rr := httptest.NewRecorder() - p.ServeHTTP(rr, req) + p.ServeHTTP(&plugin.Context{}, rr, req) test.httpTest.CompareHTTPResponse(rr, test.expectedResponse) }) } @@ -122,7 +122,7 @@ func TestGetToken(t *testing.T) { req := test.httpTest.CreateHTTPRequest(test.request) rr := httptest.NewRecorder() - p.ServeHTTP(rr, req) + p.ServeHTTP(test.context, rr, req) test.httpTest.CompareHTTPResponse(rr, test.expectedResponse) }) diff --git a/server/bitbucketclient/client.go b/server/bitbucketclient/client.go new file mode 100644 index 0000000..f420322 --- /dev/null +++ b/server/bitbucketclient/client.go @@ -0,0 +1,9 @@ +package bitbucketclient + +type Client interface { + GetMe() (*BitbucketUser, error) +} + +type BitbucketClient struct { + ClientConfiguration +} diff --git a/server/bitbucketclient/client_server.go b/server/bitbucketclient/client_server.go new file mode 100644 index 0000000..5411502 --- /dev/null +++ b/server/bitbucketclient/client_server.go @@ -0,0 +1,96 @@ +package bitbucketclient + +import ( + "encoding/json" + "fmt" + "io" + "net/http" + + "github.com/pkg/errors" +) + +// TODO: This will be changed to create the main structs when modularized +type Link struct { + Href string `json:"href"` +} + +type BitbucketUser struct { + AccountID int `json:"id"` + Username string `json:"name"` + Links struct { + Self []Link `json:"self"` + } `json:"links"` +} + +type BitbucketServerClient struct { + BitbucketClient +} + +func newServerClient(config ClientConfiguration) Client { + return &BitbucketServerClient{ + BitbucketClient: BitbucketClient{ + ClientConfiguration: ClientConfiguration{ + SelfHostedURL: config.SelfHostedURL, + SelfHostedAPIURL: config.SelfHostedAPIURL, + APIClient: config.APIClient, + OAuthClient: config.OAuthClient, + LogError: config.LogError, + }, + }, + } +} + +func (c *BitbucketServerClient) getWhoAmI() (string, error) { + requestURL := fmt.Sprintf("%s/plugins/servlet/applinks/whoami", c.SelfHostedURL) + + req, err := http.NewRequest("GET", requestURL, nil) + if err != nil { + return "", errors.Wrap(err, "unable to create request for getting whoami identity") + } + + resp, err := c.OAuthClient.Do(req) + if err != nil { + return "", errors.Wrap(err, "failed to make the request for getting whoami identity") + } + defer resp.Body.Close() + + if resp.StatusCode != 200 { + return "", errors.Errorf("who am i returned non-200 status code: %d", resp.StatusCode) + } + + user, err := io.ReadAll(resp.Body) + if err != nil { + return "", errors.Wrap(err, "failed to make the request for getting whoami identity") + } + + return string(user), nil +} + +func (c *BitbucketServerClient) GetMe() (*BitbucketUser, error) { + username, err := c.getWhoAmI() + if err != nil { + c.LogError("failed to get whoami identity", "error", err.Error()) + return nil, err + } + + resp, err := c.APIClient.DefaultApi.GetUser(username) + if err != nil { + c.LogError("failed to get user from bitbucket server", "error", err.Error()) + return nil, err + } + + jsonData, err := json.Marshal(resp.Values) + if err != nil { + c.LogError("failed to marshaling user from bitbucket server", "error", err.Error()) + return nil, err + } + + var user BitbucketUser + err = json.Unmarshal(jsonData, &user) + if err != nil { + c.LogError("failed to parse user from bitbucket server", "error", err.Error()) + return nil, err + } + + return &user, nil +} diff --git a/server/bitbucketclient/client_server_test.go b/server/bitbucketclient/client_server_test.go new file mode 100644 index 0000000..75b188a --- /dev/null +++ b/server/bitbucketclient/client_server_test.go @@ -0,0 +1,52 @@ +package bitbucketclient + +import ( + "net/http" + "net/http/httptest" + "testing" + + bitbucketv1 "github.com/gfleury/go-bitbucket-v1" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" +) + +type MockAPIClient struct { + mock.Mock +} + +func (m *MockAPIClient) GetUser(_ string) (*bitbucketv1.APIResponse, error) { + args := m.Called() + return args.Get(0).(*bitbucketv1.APIResponse), args.Error(1) +} + +func TestGetWhoAmI(t *testing.T) { + t.Run("successfully get who am i from oauth", func(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + _, err := w.Write([]byte("testuser")) + if err != nil { + t.Fatalf("Failed to write response: %v", err) + } + })) + defer ts.Close() + + client := newServerClient(ClientConfiguration{SelfHostedURL: ts.URL, OAuthClient: ts.Client()}).(*BitbucketServerClient) + + username, err := client.getWhoAmI() + + assert.Nil(t, err) + assert.Equal(t, "testuser", username) + }) + + t.Run("return error when the status code is different than 200", func(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusBadRequest) + })) + defer ts.Close() + + client := newServerClient(ClientConfiguration{SelfHostedURL: ts.URL, OAuthClient: ts.Client()}).(*BitbucketServerClient) + + _, err := client.getWhoAmI() + assert.Error(t, err) + }) +} diff --git a/server/bitbucketclient/factory.go b/server/bitbucketclient/factory.go new file mode 100644 index 0000000..336da5d --- /dev/null +++ b/server/bitbucketclient/factory.go @@ -0,0 +1,24 @@ +package bitbucketclient + +import ( + "fmt" + "net/http" + + bitbucketv1 "github.com/gfleury/go-bitbucket-v1" +) + +type ClientConfiguration struct { + SelfHostedURL string + SelfHostedAPIURL string + APIClient *bitbucketv1.APIClient + OAuthClient *http.Client + + LogError func(msg string, keyValuePairs ...interface{}) +} + +func GetBitbucketClient(clientType string, config ClientConfiguration) (Client, error) { + if clientType == "server" { + return newServerClient(config), nil + } + return nil, fmt.Errorf("wrong client passed") +}