Skip to content

Commit e6b7808

Browse files
lpusoktothszabiofalvai
authored
[STEP-2061] Enterprise api (#246)
* Added enterprise API client * Added test * Make baseURL fully overrideable * Use the new API flag * Update input override * Update appstoreconnect.go * Update URL creation * Added codesigning integration test * Add enterprise flag * Use the new v2 devportalservice. Moved appleauth.Credentials to devportalservice.Credentials. * Use new devportalservice.NewBitriseClient with new params * Added codesinging e2e tests using dev step * Disable Xcode managed signing for enterpries as not working yet * lint * Change workdir back * lint * yml formatting * Speed up spaceships tests by removing sleep in tests * Fix dev step path * Convert e2e test step to scrip to work around go workspace issues due to CLI copying step dir * step path lookup fix * E2E test step path fixed * Removed e2e from legacy WF * Deduplicate legacy WF * Fix step ref * Lint * Revert E2E test changes for now --------- Co-authored-by: Szabolcs Toth <54896607+tothszabi@users.noreply.github.com> Co-authored-by: Olivér Falvai <ofalvai@gmail.com>
1 parent a05d0b4 commit e6b7808

27 files changed

+211
-113
lines changed

autocodesign/autocodesign.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@ import (
1111

1212
"github.com/bitrise-io/go-utils/log"
1313
"github.com/bitrise-io/go-xcode/certificateutil"
14-
"github.com/bitrise-io/go-xcode/devportalservice"
1514
"github.com/bitrise-io/go-xcode/profileutil"
1615
"github.com/bitrise-io/go-xcode/v2/autocodesign/devportalclient/appstoreconnect"
16+
"github.com/bitrise-io/go-xcode/v2/devportalservice"
1717
"github.com/bitrise-io/go-xcode/xcodeproject/serialized"
1818
)
1919

autocodesign/devices.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ import (
55
"fmt"
66

77
"github.com/bitrise-io/go-utils/log"
8-
"github.com/bitrise-io/go-xcode/devportalservice"
98
"github.com/bitrise-io/go-xcode/v2/autocodesign/devportalclient/appstoreconnect"
9+
"github.com/bitrise-io/go-xcode/v2/devportalservice"
1010
)
1111

1212
// EnsureTestDevices fetches devices from Apple, and register missing devices.

autocodesign/devices_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ import (
44
"fmt"
55
"testing"
66

7-
"github.com/bitrise-io/go-xcode/devportalservice"
87
"github.com/bitrise-io/go-xcode/v2/autocodesign/devportalclient/appstoreconnect"
8+
"github.com/bitrise-io/go-xcode/v2/devportalservice"
99
"github.com/stretchr/testify/assert"
1010
"github.com/stretchr/testify/require"
1111
)

autocodesign/devportalclient/appstoreconnect/appstoreconnect.go

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"net/http"
1313
"net/url"
1414
"reflect"
15+
"strings"
1516
"time"
1617

1718
"github.com/bitrise-io/go-utils/httputil"
@@ -23,7 +24,12 @@ import (
2324
)
2425

2526
const (
26-
baseURL = "https://api.appstoreconnect.apple.com/"
27+
clientBaseURL = "https://api.appstoreconnect.apple.com/"
28+
tokenAudience = "appstoreconnect-v1"
29+
30+
clientBaseEnterpiseURL = "https://api.enterprise.developer.apple.com/"
31+
tokenEnterpriseAudience = "apple-developer-enterprise-v1"
32+
2733
apiVersion = "v1"
2834
)
2935

@@ -52,6 +58,7 @@ type Client struct {
5258
keyID string
5359
issuerID string
5460
privateKeyContent []byte
61+
audience string
5562

5663
token *jwt.Token
5764
signedToken string
@@ -83,8 +90,15 @@ func NewRetryableHTTPClient() *http.Client {
8390
}
8491

8592
// NewClient creates a new client
86-
func NewClient(httpClient HTTPClient, keyID, issuerID string, privateKey []byte) *Client {
87-
baseURL, err := url.Parse(baseURL)
93+
func NewClient(httpClient HTTPClient, keyID, issuerID string, privateKey []byte, isEnterpise bool) *Client {
94+
targetURL := clientBaseURL
95+
targetAudience := tokenAudience
96+
if isEnterpise {
97+
targetURL = clientBaseEnterpiseURL
98+
targetAudience = tokenEnterpriseAudience
99+
}
100+
101+
baseURL, err := url.Parse(targetURL)
88102
if err != nil {
89103
panic("invalid api base url: " + err.Error())
90104
}
@@ -93,6 +107,7 @@ func NewClient(httpClient HTTPClient, keyID, issuerID string, privateKey []byte)
93107
keyID: keyID,
94108
issuerID: issuerID,
95109
privateKeyContent: privateKey,
110+
audience: targetAudience,
96111

97112
client: httpClient,
98113
BaseURL: baseURL,
@@ -126,17 +141,29 @@ func (c *Client) ensureSignedToken() (string, error) {
126141
log.Debugf("Generating JWT token")
127142
}
128143

129-
c.token = createToken(c.keyID, c.issuerID)
144+
c.token = createToken(c.keyID, c.issuerID, c.audience)
130145
var err error
131146
if c.signedToken, err = signToken(c.token, c.privateKeyContent); err != nil {
132147
return "", err
133148
}
134149
return c.signedToken, nil
135150
}
136151

152+
// NewRequestWithRelationshipURL ...
153+
func (c *Client) NewRequestWithRelationshipURL(method, endpoint string, body interface{}) (*http.Request, error) {
154+
endpoint = strings.TrimPrefix(endpoint, c.BaseURL.String()+apiVersion+"/")
155+
156+
return c.NewRequest(method, endpoint, body)
157+
}
158+
137159
// NewRequest creates a new http.Request
138160
func (c *Client) NewRequest(method, endpoint string, body interface{}) (*http.Request, error) {
139161
endpoint = apiVersion + "/" + endpoint
162+
163+
return c.newRequest(method, endpoint, body)
164+
}
165+
166+
func (c *Client) newRequest(method, endpoint string, body interface{}) (*http.Request, error) {
140167
u, err := c.BaseURL.Parse(endpoint)
141168
if err != nil {
142169
return nil, fmt.Errorf("parsing endpoint failed: %v", err)
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Package appstoreconnect implements a client for the App Store Connect API.
2+
//
3+
// It contains type definitions, authentication and API calls, without business logic built on those API calls.
4+
package appstoreconnect
5+
6+
import (
7+
"net/url"
8+
"testing"
9+
10+
"github.com/stretchr/testify/require"
11+
)
12+
13+
func TestNewClient(t *testing.T) {
14+
got := NewClient(NewRetryableHTTPClient(), "keyID", "issuerID", []byte{}, false)
15+
16+
require.Equal(t, "appstoreconnect-v1", got.audience)
17+
18+
wantURL, err := url.Parse("https://api.appstoreconnect.apple.com/")
19+
require.NoError(t, err)
20+
require.Equal(t, wantURL, got.BaseURL)
21+
}
22+
23+
func TestNewEnterpriseClient(t *testing.T) {
24+
got := NewClient(NewRetryableHTTPClient(), "keyID", "issuerID", []byte{}, true)
25+
26+
require.Equal(t, "apple-developer-enterprise-v1", got.audience)
27+
28+
wantURL, err := url.Parse("https://api.enterprise.developer.apple.com/")
29+
require.NoError(t, err)
30+
require.Equal(t, wantURL, got.BaseURL)
31+
}

autocodesign/devportalclient/appstoreconnect/bundleids.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package appstoreconnect
22

33
import (
44
"net/http"
5-
"strings"
65
)
76

87
// BundleIDsEndpoint ...
@@ -125,8 +124,7 @@ func (s ProvisioningService) CreateBundleID(body BundleIDCreateRequest) (*Bundle
125124

126125
// BundleID ...
127126
func (s ProvisioningService) BundleID(relationshipLink string) (*BundleIDResponse, error) {
128-
endpoint := strings.TrimPrefix(relationshipLink, baseURL+apiVersion)
129-
req, err := s.client.NewRequest(http.MethodGet, endpoint, nil)
127+
req, err := s.client.NewRequestWithRelationshipURL(http.MethodGet, relationshipLink, nil)
130128
if err != nil {
131129
return nil, err
132130
}

autocodesign/devportalclient/appstoreconnect/capabilities.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package appstoreconnect
22

33
import (
44
"net/http"
5-
"strings"
65
)
76

87
// BundleIDCapabilitiesEndpoint ...
@@ -262,8 +261,7 @@ func (s ProvisioningService) UpdateCapability(id string, body BundleIDCapability
262261

263262
// Capabilities ...
264263
func (s ProvisioningService) Capabilities(relationshipLink string) (*BundleIDCapabilitiesResponse, error) {
265-
endpoint := strings.TrimPrefix(relationshipLink, baseURL+apiVersion)
266-
req, err := s.client.NewRequest(http.MethodGet, endpoint, nil)
264+
req, err := s.client.NewRequestWithRelationshipURL(http.MethodGet, relationshipLink, nil)
267265
if err != nil {
268266
return nil, err
269267
}

autocodesign/devportalclient/appstoreconnect/certificates.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package appstoreconnect
33
import (
44
"fmt"
55
"net/http"
6-
"strings"
76
)
87

98
// CertificatesEndpoint ...
@@ -108,8 +107,7 @@ func (s ProvisioningService) Certificates(relationshipLink string, opt *PagingOp
108107
return nil, err
109108
}
110109

111-
endpoint := strings.TrimPrefix(u, baseURL+apiVersion)
112-
req, err := s.client.NewRequest(http.MethodGet, endpoint, nil)
110+
req, err := s.client.NewRequestWithRelationshipURL(http.MethodGet, u, nil)
113111
if err != nil {
114112
return nil, err
115113
}

autocodesign/devportalclient/appstoreconnect/devices.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package appstoreconnect
22

33
import (
44
"net/http"
5-
"strings"
65
)
76

87
// DevicesEndpoint ...
@@ -145,8 +144,7 @@ func (s ProvisioningService) Devices(relationshipLink string, opt *PagingOptions
145144
return nil, err
146145
}
147146

148-
endpoint := strings.TrimPrefix(u, baseURL+apiVersion)
149-
req, err := s.client.NewRequest(http.MethodGet, endpoint, nil)
147+
req, err := s.client.NewRequestWithRelationshipURL(http.MethodGet, u, nil)
150148
if err != nil {
151149
return nil, err
152150
}

autocodesign/devportalclient/appstoreconnect/jwt.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,11 @@ func signToken(token *jwt.Token, privateKeyContent []byte) (string, error) {
3131
}
3232

3333
// createToken creates a jwt.Token for the Apple API
34-
func createToken(keyID string, issuerID string) *jwt.Token {
34+
func createToken(keyID string, issuerID string, audience string) *jwt.Token {
3535
payload := claims{
3636
IssuerID: issuerID,
3737
Expiration: time.Now().Add(jwtDuration).Unix(),
38-
Audience: "appstoreconnect-v1",
38+
Audience: audience,
3939
}
4040

4141
// registers headers: alg = ES256 and typ = JWT

autocodesign/devportalclient/appstoreconnect/profiles.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package appstoreconnect
22

33
import (
44
"net/http"
5-
"strings"
65

76
"github.com/bitrise-io/go-xcode/v2/autocodesign/devportalclient/time"
87
"github.com/bitrise-io/go-xcode/xcodeproject/serialized"
@@ -286,8 +285,7 @@ func (s ProvisioningService) Profiles(relationshipLink string, opt *PagingOption
286285
return nil, err
287286
}
288287

289-
endpoint := strings.TrimPrefix(u, baseURL+apiVersion)
290-
req, err := s.client.NewRequest(http.MethodGet, endpoint, nil)
288+
req, err := s.client.NewRequestWithRelationshipURL(http.MethodGet, u, nil)
291289
if err != nil {
292290
return nil, err
293291
}

autocodesign/devportalclient/appstoreconnectclient/devices.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ import (
55
"fmt"
66
"net/http"
77

8-
"github.com/bitrise-io/go-xcode/devportalservice"
98
"github.com/bitrise-io/go-xcode/v2/autocodesign/devportalclient/appstoreconnect"
9+
"github.com/bitrise-io/go-xcode/v2/devportalservice"
1010
)
1111

1212
// DeviceClient ...

autocodesign/devportalclient/appstoreconnectclient/devices_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ import (
55
"net/http"
66
"testing"
77

8-
"github.com/bitrise-io/go-xcode/devportalservice"
98
"github.com/bitrise-io/go-xcode/v2/autocodesign/devportalclient/appstoreconnect"
9+
"github.com/bitrise-io/go-xcode/v2/devportalservice"
1010
"github.com/stretchr/testify/mock"
1111
"github.com/stretchr/testify/require"
1212
)
@@ -39,7 +39,7 @@ func TestDeviceClient_RegisterDevice_WhenInvaludUUID(t *testing.T) {
3939
},
4040
})
4141

42-
client := appstoreconnect.NewClient(&mockClient, "keyID", "issueID", []byte("privateKey"))
42+
client := appstoreconnect.NewClient(&mockClient, "keyID", "issueID", []byte("privateKey"), false)
4343
deviceClient := NewDeviceClient(client)
4444

4545
got, err := deviceClient.RegisterDevice(devportalservice.TestDevice{

autocodesign/devportalclient/appstoreconnectclient/profiles_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ func TestEnsureProfile_ExpiredProfile(t *testing.T) {
8888
On("PostProfilesSuccess", mock.AnythingOfType("*http.Request")).
8989
Return(newResponse(t, http.StatusOK, map[string]interface{}{}), nil)
9090

91-
client := appstoreconnect.NewClient(mockClient, "keyID", "issueID", []byte("privateKey"))
91+
client := appstoreconnect.NewClient(mockClient, "keyID", "issueID", []byte("privateKey"), false)
9292
profileClient := NewProfileClient(client)
9393
bundleID := appstoreconnect.BundleID{
9494
Attributes: appstoreconnect.BundleIDAttributes{Identifier: "io.bitrise.testapp"},
@@ -133,9 +133,9 @@ func (c *MockClient) Do(req *http.Request) (*http.Response, error) {
133133
}
134134
// After deleting the expired profile, creating a new one succeed
135135
return c.PostProfilesSuccess(req)
136-
case req.URL.Path == "/v1//bundleID/capabilities" && req.Method == "GET":
136+
case req.URL.Path == "/v1/bundleID/capabilities" && req.Method == "GET":
137137
return c.GetBundleIDCapabilities(req)
138-
case req.URL.Path == "/v1//bundleID/profiles" && req.Method == "GET":
138+
case req.URL.Path == "/v1/bundleID/profiles" && req.Method == "GET":
139139
return c.GetBundleIDProfiles(req)
140140
case req.URL.Path == "/v1/profiles/1" && req.Method == "DELETE":
141141
return c.DeleteProfiles(req)

autocodesign/devportalclient/devportalclient.go

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,13 @@ import (
99
"github.com/bitrise-io/go-utils/retry"
1010
"github.com/bitrise-io/go-utils/v2/command"
1111
"github.com/bitrise-io/go-utils/v2/env"
12+
"github.com/bitrise-io/go-utils/v2/fileutil"
1213
"github.com/bitrise-io/go-utils/v2/log"
13-
"github.com/bitrise-io/go-xcode/appleauth"
14-
"github.com/bitrise-io/go-xcode/devportalservice"
1514
"github.com/bitrise-io/go-xcode/v2/autocodesign"
1615
"github.com/bitrise-io/go-xcode/v2/autocodesign/devportalclient/appstoreconnect"
1716
"github.com/bitrise-io/go-xcode/v2/autocodesign/devportalclient/appstoreconnectclient"
1817
"github.com/bitrise-io/go-xcode/v2/autocodesign/devportalclient/spaceship"
18+
"github.com/bitrise-io/go-xcode/v2/devportalservice"
1919
)
2020

2121
const (
@@ -27,21 +27,23 @@ Read more: https://devcenter.bitrise.io/getting-started/configuring-bitrise-step
2727

2828
// Factory ...
2929
type Factory struct {
30-
logger log.Logger
30+
logger log.Logger
31+
filemanager fileutil.FileManager
3132
}
3233

3334
// NewFactory ...
34-
func NewFactory(logger log.Logger) Factory {
35+
func NewFactory(logger log.Logger, filemanager fileutil.FileManager) Factory {
3536
return Factory{
36-
logger: logger,
37+
logger: logger,
38+
filemanager: filemanager,
3739
}
3840
}
3941

4042
// CreateBitriseConnection ...
4143
func (f Factory) CreateBitriseConnection(buildURL, buildAPIToken string) (*devportalservice.AppleDeveloperConnection, error) {
4244
f.logger.Println()
4345
f.logger.Infof("Fetching Apple Service connection")
44-
connectionProvider := devportalservice.NewBitriseClient(retry.NewHTTPClient().StandardClient(), buildURL, buildAPIToken)
46+
connectionProvider := devportalservice.NewBitriseClient(f.logger, f.filemanager, retry.NewHTTPClient().StandardClient(), buildURL, buildAPIToken)
4547
conn, err := connectionProvider.GetAppleDeveloperConnection()
4648
if err != nil {
4749
if networkErr, ok := err.(devportalservice.NetworkError); ok && networkErr.Status == http.StatusUnauthorized {
@@ -68,13 +70,13 @@ func (f Factory) CreateBitriseConnection(buildURL, buildAPIToken string) (*devpo
6870
}
6971

7072
// Create ...
71-
func (f Factory) Create(credentials appleauth.Credentials, teamID string) (autocodesign.DevPortalClient, error) {
73+
func (f Factory) Create(credentials devportalservice.Credentials, teamID string) (autocodesign.DevPortalClient, error) {
7274
f.logger.Println()
7375
f.logger.Infof("Initializing Developer Portal client")
7476
var devportalClient autocodesign.DevPortalClient
7577
if credentials.APIKey != nil {
7678
httpClient := appstoreconnect.NewRetryableHTTPClient()
77-
client := appstoreconnect.NewClient(httpClient, credentials.APIKey.KeyID, credentials.APIKey.IssuerID, []byte(credentials.APIKey.PrivateKey))
79+
client := appstoreconnect.NewClient(httpClient, credentials.APIKey.KeyID, credentials.APIKey.IssuerID, []byte(credentials.APIKey.PrivateKey), credentials.APIKey.EnterpriseAccount)
7880
client.EnableDebugLogs = false // Turn off client debug logs including HTTP call debug logs
7981
devportalClient = appstoreconnectclient.NewAPIDevPortalClient(client)
8082
f.logger.Debugf("App Store Connect API client created with base URL: %s", client.BaseURL)

autocodesign/devportalclient/spaceship/devices.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ import (
77
"strings"
88

99
"github.com/bitrise-io/go-utils/log"
10-
"github.com/bitrise-io/go-xcode/devportalservice"
1110
"github.com/bitrise-io/go-xcode/v2/autocodesign/devportalclient/appstoreconnect"
11+
"github.com/bitrise-io/go-xcode/v2/devportalservice"
1212
)
1313

1414
// DeviceClient ...

0 commit comments

Comments
 (0)