Skip to content
Open
2 changes: 1 addition & 1 deletion cmd/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ func loginCmd() *cobra.Command {
Use: "login",
Short: "Login to the App Store",
RunE: func(cmd *cobra.Command, args []string) error {
interactive := cmd.Context().Value("interactive").(bool)
interactive, _ := cmd.Context().Value(interactiveKey).(bool)

if password == "" && !interactive {
return errors.New("password is required when not running in interactive mode; use the \"--password\" flag")
Expand Down
16 changes: 8 additions & 8 deletions cmd/download.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ func downloadCmd() *cobra.Command {
purchased := false

return retry.Do(func() error {
bag, err := dependencies.AppStore.Bag(appstore.BagInput{})
if err != nil {
return fmt.Errorf("failed to get bag: %w", err)
}

infoResult, err := dependencies.AppStore.AccountInfo()
if err != nil {
return err
Expand All @@ -43,15 +48,10 @@ func downloadCmd() *cobra.Command {
acc = infoResult.Account

if errors.Is(lastErr, appstore.ErrPasswordTokenExpired) {
bagOutput, err := dependencies.AppStore.Bag(appstore.BagInput{})
if err != nil {
return fmt.Errorf("failed to get bag: %w", err)
}

loginResult, err := dependencies.AppStore.Login(appstore.LoginInput{
Email: acc.Email,
Password: acc.Password,
Endpoint: bagOutput.AuthEndpoint,
Endpoint: bag.AuthEndpoint,
})
if err != nil {
return err
Expand Down Expand Up @@ -81,7 +81,7 @@ func downloadCmd() *cobra.Command {
Msg("purchase")
}

interactive, _ := cmd.Context().Value("interactive").(bool)
interactive, _ := cmd.Context().Value(interactiveKey).(bool)
var progress *progressbar.ProgressBar
if interactive {
progress = progressbar.NewOptions64(1,
Expand All @@ -101,7 +101,7 @@ func downloadCmd() *cobra.Command {
}

out, err := dependencies.AppStore.Download(appstore.DownloadInput{
Account: acc, App: app, OutputPath: outputPath, Progress: progress, ExternalVersionID: externalVersionID})
Account: acc, App: app, OutputPath: outputPath, Progress: progress, ExternalVersionID: externalVersionID, Endpoint: bag.DownloadEndpoint})
if err != nil {
return err
}
Expand Down
13 changes: 7 additions & 6 deletions cmd/get_version_metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ func getVersionMetadataCmd() *cobra.Command {
var acc appstore.Account

return retry.Do(func() error {
bag, err := dependencies.AppStore.Bag(appstore.BagInput{})
if err != nil {
return fmt.Errorf("failed to get bag: %w", err)
}

infoResult, err := dependencies.AppStore.AccountInfo()
if err != nil {
return err
Expand All @@ -38,15 +43,10 @@ func getVersionMetadataCmd() *cobra.Command {
acc = infoResult.Account

if errors.Is(lastErr, appstore.ErrPasswordTokenExpired) {
bagOutput, err := dependencies.AppStore.Bag(appstore.BagInput{})
if err != nil {
return fmt.Errorf("failed to get bag: %w", err)
}

loginResult, err := dependencies.AppStore.Login(appstore.LoginInput{
Email: acc.Email,
Password: acc.Password,
Endpoint: bagOutput.AuthEndpoint,
Endpoint: bag.AuthEndpoint,
})
if err != nil {
return err
Expand All @@ -69,6 +69,7 @@ func getVersionMetadataCmd() *cobra.Command {
Account: acc,
App: app,
VersionID: externalVersionID,
Endpoint: bag.DownloadEndpoint,
})
if err != nil {
return err
Expand Down
14 changes: 7 additions & 7 deletions cmd/list_versions.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ func ListVersionsCmd() *cobra.Command {
var acc appstore.Account

return retry.Do(func() error {
bag, err := dependencies.AppStore.Bag(appstore.BagInput{})
if err != nil {
return fmt.Errorf("failed to get bag: %w", err)
}

infoResult, err := dependencies.AppStore.AccountInfo()
if err != nil {
return err
Expand All @@ -37,15 +42,10 @@ func ListVersionsCmd() *cobra.Command {
acc = infoResult.Account

if errors.Is(lastErr, appstore.ErrPasswordTokenExpired) {
bagOutput, err := dependencies.AppStore.Bag(appstore.BagInput{})
if err != nil {
return fmt.Errorf("failed to get bag: %w", err)
}

loginResult, err := dependencies.AppStore.Login(appstore.LoginInput{
Email: acc.Email,
Password: acc.Password,
Endpoint: bagOutput.AuthEndpoint,
Endpoint: bag.AuthEndpoint,
})
if err != nil {
return err
Expand All @@ -64,7 +64,7 @@ func ListVersionsCmd() *cobra.Command {
app = lookupResult.App
}

out, err := dependencies.AppStore.ListVersions(appstore.ListVersionsInput{Account: acc, App: app})
out, err := dependencies.AppStore.ListVersions(appstore.ListVersionsInput{Account: acc, App: app, Endpoint: bag.DownloadEndpoint})
if err != nil {
return err
}
Expand Down
9 changes: 6 additions & 3 deletions pkg/appstore/appstore_bag.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import (
type BagInput struct{}

type BagOutput struct {
AuthEndpoint string
AuthEndpoint string
DownloadEndpoint string
}

func (t *appstore) Bag(input BagInput) (BagOutput, error) {
Expand All @@ -33,7 +34,8 @@ func (t *appstore) Bag(input BagInput) (BagOutput, error) {
}

return BagOutput{
AuthEndpoint: res.Data.URLBag.AuthEndpoint,
AuthEndpoint: res.Data.URLBag.AuthEndpoint,
DownloadEndpoint: res.Data.URLBag.DownloadEndpoint,
}, nil
}

Expand All @@ -42,7 +44,8 @@ type bagResult struct {
}

type urlBag struct {
AuthEndpoint string `plist:"authenticateAccount,omitempty"`
AuthEndpoint string `plist:"authenticateAccount,omitempty"`
DownloadEndpoint string `plist:"redownloadProduct,omitempty"`
}

func (*appstore) bagRequest(guid string) http.Request {
Expand Down
11 changes: 8 additions & 3 deletions pkg/appstore/appstore_bag_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,11 @@ var _ = Describe("AppStore (Bag)", func() {
})
})

When("request is successful with authenticateAccount in urlBag", func() {
const testAuthEndpoint = "https://example.com"
When("request is successful with endpoints in urlBag", func() {
const (
testAuthEndpoint = "https://example.com"
testDownloadEndpoint = "https://downloaddispatch.example.com/r/redownload"
)

BeforeEach(func() {
mockMachine.EXPECT().
Expand All @@ -105,7 +108,8 @@ var _ = Describe("AppStore (Bag)", func() {
StatusCode: gohttp.StatusOK,
Data: bagResult{
URLBag: urlBag{
AuthEndpoint: testAuthEndpoint,
AuthEndpoint: testAuthEndpoint,
DownloadEndpoint: testDownloadEndpoint,
},
},
}, nil)
Expand All @@ -115,6 +119,7 @@ var _ = Describe("AppStore (Bag)", func() {
out, err := as.Bag(BagInput{})
Expect(err).ToNot(HaveOccurred())
Expect(out.AuthEndpoint).To(Equal(testAuthEndpoint))
Expect(out.DownloadEndpoint).To(Equal(testDownloadEndpoint))
})
})

Expand Down
17 changes: 6 additions & 11 deletions pkg/appstore/appstore_download.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ type DownloadInput struct {
OutputPath string
Progress *progressbar.ProgressBar
ExternalVersionID string
Endpoint string
}

type DownloadOutput struct {
Expand All @@ -39,7 +40,7 @@ func (t *appstore) Download(input DownloadInput) (DownloadOutput, error) {

guid := strings.ReplaceAll(strings.ToUpper(macAddr), ":", "")

req := t.downloadRequest(input.Account, input.App, guid, input.ExternalVersionID)
req := t.downloadRequest(input.Endpoint, input.Account, input.App, guid, input.ExternalVersionID)

res, err := t.downloadClient.Send(req)
if err != nil {
Expand All @@ -48,8 +49,7 @@ func (t *appstore) Download(input DownloadInput) (DownloadOutput, error) {

if res.Data.FailureType == FailureTypePasswordTokenExpired ||
res.Data.FailureType == FailureTypeSignInRequired ||
res.Data.FailureType == FailureTypeDeviceVerificationFailed ||
res.Data.FailureType == FailureTypeLicenseAlreadyExists {
res.Data.FailureType == FailureTypeDeviceVerificationFailed {
return DownloadOutput{}, ErrPasswordTokenExpired
}

Expand Down Expand Up @@ -172,24 +172,19 @@ func (t *appstore) downloadFile(src, dst string, progress *progressbar.ProgressB
return nil
}

func (*appstore) downloadRequest(acc Account, app App, guid string, externalVersionID string) http.Request {
func (*appstore) downloadRequest(endpoint string, acc Account, app App, guid string, externalVersionID string) http.Request {
payload := map[string]interface{}{
"creditDisplay": "",
"guid": guid,
"salableAdamId": app.ID,
}

if externalVersionID != "" {
payload["externalVersionId"] = externalVersionID
}

podPrefix := ""
if acc.Pod != "" {
podPrefix = "p" + acc.Pod + "-"
payload["appExtVrsId"] = externalVersionID
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this payload parameter supposed to be changed?

}

return http.Request{
URL: fmt.Sprintf("https://%s%s%s?guid=%s", podPrefix, PrivateAppStoreAPIDomain, PrivateAppStoreAPIPathDownload, guid),
URL: fmt.Sprintf("%s?guid=%s", endpoint, guid),
Method: http.MethodPOST,
ResponseFormat: http.ResponseFormatXML,
Headers: map[string]string{
Expand Down
17 changes: 6 additions & 11 deletions pkg/appstore/appstore_download_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,10 +97,10 @@ var _ = Describe("AppStore (Download)", func() {
})
})

When("request uses a custom pod", func() {
When("request is sent", func() {
const (
testPod = "42"
testGUID = "001122334455"
testEndpoint = "https://downloaddispatch.example.com/r/redownload"
testGUID = "001122334455"
)

BeforeEach(func() {
Expand All @@ -111,18 +111,13 @@ var _ = Describe("AppStore (Download)", func() {
mockDownloadClient.EXPECT().
Send(gomock.Any()).
Do(func(req http.Request) {
expectedURL := "https://p" + testPod + "-" + PrivateAppStoreAPIDomain + PrivateAppStoreAPIPathDownload + "?guid=" + testGUID
Expect(req.URL).To(Equal(expectedURL))
Expect(req.URL).To(Equal(testEndpoint + "?guid=" + testGUID))
}).
Return(http.Result[downloadResult]{}, errors.New(""))
})

It("sends the download request to the pod-specific host", func() {
_, err := as.Download(DownloadInput{
Account: Account{
Pod: testPod,
},
})
It("sends the download request to the endpoint provided by the caller", func() {
_, err := as.Download(DownloadInput{Endpoint: testEndpoint})
Expect(err).To(HaveOccurred())
})
})
Expand Down
20 changes: 8 additions & 12 deletions pkg/appstore/appstore_get_version_metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ type GetVersionMetadataInput struct {
Account Account
App App
VersionID string
Endpoint string
}

type GetVersionMetadataOutput struct {
Expand All @@ -28,7 +29,7 @@ func (t *appstore) GetVersionMetadata(input GetVersionMetadataInput) (GetVersion

guid := strings.ReplaceAll(strings.ToUpper(macAddr), ":", "")

req := t.getVersionMetadataRequest(input.Account, input.App, guid, input.VersionID)
req := t.getVersionMetadataRequest(input.Endpoint, input.Account, input.App, guid, input.VersionID)
res, err := t.downloadClient.Send(req)

if err != nil {
Expand Down Expand Up @@ -68,21 +69,16 @@ func (t *appstore) GetVersionMetadata(input GetVersionMetadataInput) (GetVersion
return GetVersionMetadataOutput(metadata), nil
}

func (t *appstore) getVersionMetadataRequest(acc Account, app App, guid string, version string) http.Request {
func (t *appstore) getVersionMetadataRequest(endpoint string, acc Account, app App, guid string, version string) http.Request {
payload := map[string]interface{}{
"creditDisplay": "",
"guid": guid,
"salableAdamId": app.ID,
"externalVersionId": version,
}

podPrefix := ""
if acc.Pod != "" {
podPrefix = "p" + acc.Pod + "-"
"creditDisplay": "",
"guid": guid,
"salableAdamId": app.ID,
"appExtVrsId": version,
}

return http.Request{
URL: fmt.Sprintf("https://%s%s%s?guid=%s", podPrefix, PrivateAppStoreAPIDomain, PrivateAppStoreAPIPathDownload, guid),
URL: fmt.Sprintf("%s?guid=%s", endpoint, guid),
Method: http.MethodPOST,
ResponseFormat: http.ResponseFormatXML,
Headers: map[string]string{
Expand Down
17 changes: 6 additions & 11 deletions pkg/appstore/appstore_get_version_metadata_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -222,10 +222,10 @@ var _ = Describe("AppStore (GetVersionMetadata)", func() {
})
})

When("request uses a custom pod", func() {
When("request is sent", func() {
const (
testPod = "42"
testGUID = "001122334455"
testEndpoint = "https://downloaddispatch.example.com/r/redownload"
testGUID = "001122334455"
)

BeforeEach(func() {
Expand All @@ -236,18 +236,13 @@ var _ = Describe("AppStore (GetVersionMetadata)", func() {
mockDownloadClient.EXPECT().
Send(gomock.Any()).
Do(func(req http.Request) {
expectedURL := "https://p" + testPod + "-" + PrivateAppStoreAPIDomain + PrivateAppStoreAPIPathDownload + "?guid=" + testGUID
Expect(req.URL).To(Equal(expectedURL))
Expect(req.URL).To(Equal(testEndpoint + "?guid=" + testGUID))
}).
Return(http.Result[downloadResult]{}, errors.New("request error"))
})

It("sends the request to the pod-specific host", func() {
_, err := as.GetVersionMetadata(GetVersionMetadataInput{
Account: Account{
Pod: testPod,
},
})
It("sends the request to the endpoint provided by the caller", func() {
_, err := as.GetVersionMetadata(GetVersionMetadataInput{Endpoint: testEndpoint})
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("failed to send http request"))
})
Expand Down
Loading
Loading