From 4ce4828ba097ed55a2ce85ef208ee62b733b51b7 Mon Sep 17 00:00:00 2001 From: Jahziel Villasana-Espinoza Date: Tue, 9 Dec 2025 10:42:04 -0500 Subject: [PATCH] fix app store apps display names in gitops (#36913) **Related issue:** Resolves #36700 # Checklist for submitter If some of the following don't apply, delete the relevant line. - [x] Input data is properly validated, `SELECT *` is avoided, SQL injection is prevented (using placeholders for values in statements) ## Testing - [x] Added/updated automated tests - [x] Where appropriate, [automated tests simulate multiple hosts and test for host isolation](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/reference/patterns-backend.md#unit-testing) (updates to one hosts's records do not affect another) - [x] QA'd all new/changed functionality manually For unreleased bug fixes in a release candidate, one of: - [x] Confirmed that the fix is not expected to adversely impact load test results ## New Fleet configuration settings - [x] Verified that the setting is exported via `fleetctl generate-gitops` - [x] Verified that the setting is cleared on the server if it is not supplied in a YAML file (or that it is documented as being optional) --- cmd/fleetctl/fleetctl/gitops_test.go | 14 ++--- .../fleetctl/testing_utils/testing_utils.go | 2 +- .../integrationtest/gitops/software_test.go | 8 +-- ee/server/service/vpp.go | 16 ++++- server/datastore/mysql/vpp.go | 15 ++++- server/datastore/mysql/vpp_test.go | 20 +++---- server/fleet/datastore.go | 2 +- server/mock/datastore_mock.go | 6 +- server/service/integration_mdm_test.go | 58 ++++++++++++++++++- 9 files changed, 110 insertions(+), 31 deletions(-) diff --git a/cmd/fleetctl/fleetctl/gitops_test.go b/cmd/fleetctl/fleetctl/gitops_test.go index c9f23515c0..dcf5e6e37d 100644 --- a/cmd/fleetctl/fleetctl/gitops_test.go +++ b/cmd/fleetctl/fleetctl/gitops_test.go @@ -350,7 +350,7 @@ func TestGitOpsBasicGlobalPremium(t *testing.T) { ) (teamPolicies []*fleet.Policy, inheritedPolicies []*fleet.Policy, err error) { return nil, nil, nil } - ds.SetTeamVPPAppsFunc = func(ctx context.Context, teamID *uint, adamIDs []fleet.VPPAppTeam) error { + ds.SetTeamVPPAppsFunc = func(ctx context.Context, teamID *uint, adamIDs []fleet.VPPAppTeam, _ map[string]uint) error { return nil } @@ -575,7 +575,7 @@ func TestGitOpsBasicTeam(t *testing.T) { const secret = "TestSecret" - ds.SetTeamVPPAppsFunc = func(ctx context.Context, teamID *uint, adamIDs []fleet.VPPAppTeam) error { + ds.SetTeamVPPAppsFunc = func(ctx context.Context, teamID *uint, adamIDs []fleet.VPPAppTeam, _ map[string]uint) error { return nil } ds.BatchInsertVPPAppsFunc = func(ctx context.Context, apps []*fleet.VPPApp) error { @@ -1325,7 +1325,7 @@ func TestGitOpsFullTeam(t *testing.T) { ds.GetSoftwareInstallersFunc = func(ctx context.Context, tmID uint) ([]fleet.SoftwarePackageResponse, error) { return nil, nil } - ds.SetTeamVPPAppsFunc = func(ctx context.Context, teamID *uint, adamIDs []fleet.VPPAppTeam) error { + ds.SetTeamVPPAppsFunc = func(ctx context.Context, teamID *uint, adamIDs []fleet.VPPAppTeam, _ map[string]uint) error { return nil } ds.BatchInsertVPPAppsFunc = func(ctx context.Context, apps []*fleet.VPPApp) error { @@ -1529,7 +1529,7 @@ func TestGitOpsBasicGlobalAndTeam(t *testing.T) { return nil } - ds.SetTeamVPPAppsFunc = func(ctx context.Context, teamID *uint, adamIDs []fleet.VPPAppTeam) error { + ds.SetTeamVPPAppsFunc = func(ctx context.Context, teamID *uint, adamIDs []fleet.VPPAppTeam, _ map[string]uint) error { return nil } ds.BatchInsertVPPAppsFunc = func(ctx context.Context, apps []*fleet.VPPApp) error { @@ -1922,7 +1922,7 @@ func TestGitOpsBasicGlobalAndNoTeam(t *testing.T) { savedAppConfig = config return nil } - ds.SetTeamVPPAppsFunc = func(ctx context.Context, teamID *uint, adamIDs []fleet.VPPAppTeam) error { + ds.SetTeamVPPAppsFunc = func(ctx context.Context, teamID *uint, adamIDs []fleet.VPPAppTeam, _ map[string]uint) error { return nil } ds.BatchInsertVPPAppsFunc = func(ctx context.Context, apps []*fleet.VPPApp) error { @@ -2466,7 +2466,7 @@ func TestGitOpsFullGlobalAndTeam(t *testing.T) { }, nil } - ds.SetTeamVPPAppsFunc = func(ctx context.Context, teamID *uint, adamIDs []fleet.VPPAppTeam) error { + ds.SetTeamVPPAppsFunc = func(ctx context.Context, teamID *uint, adamIDs []fleet.VPPAppTeam, _ map[string]uint) error { return nil } ds.BatchInsertVPPAppsFunc = func(ctx context.Context, apps []*fleet.VPPApp) error { @@ -2676,7 +2676,7 @@ func TestGitOpsCustomSettings(t *testing.T) { } return ret, nil } - ds.SetTeamVPPAppsFunc = func(ctx context.Context, teamID *uint, adamIDs []fleet.VPPAppTeam) error { + ds.SetTeamVPPAppsFunc = func(ctx context.Context, teamID *uint, adamIDs []fleet.VPPAppTeam, _ map[string]uint) error { return nil } ds.BatchInsertVPPAppsFunc = func(ctx context.Context, apps []*fleet.VPPApp) error { diff --git a/cmd/fleetctl/fleetctl/testing_utils/testing_utils.go b/cmd/fleetctl/fleetctl/testing_utils/testing_utils.go index bca613b43d..9d6a0489e3 100644 --- a/cmd/fleetctl/fleetctl/testing_utils/testing_utils.go +++ b/cmd/fleetctl/fleetctl/testing_utils/testing_utils.go @@ -270,7 +270,7 @@ func SetupFullGitOpsPremiumServer(t *testing.T) (*mock.Store, **fleet.AppConfig, savedAppConfig = &appConfigCopy return nil } - ds.SetTeamVPPAppsFunc = func(ctx context.Context, teamID *uint, adamIDs []fleet.VPPAppTeam) error { + ds.SetTeamVPPAppsFunc = func(ctx context.Context, teamID *uint, adamIDs []fleet.VPPAppTeam, _ map[string]uint) error { return nil } ds.BatchInsertVPPAppsFunc = func(ctx context.Context, apps []*fleet.VPPApp) error { diff --git a/cmd/fleetctl/integrationtest/gitops/software_test.go b/cmd/fleetctl/integrationtest/gitops/software_test.go index bdfb5c0ffe..a20eb419d9 100644 --- a/cmd/fleetctl/integrationtest/gitops/software_test.go +++ b/cmd/fleetctl/integrationtest/gitops/software_test.go @@ -86,7 +86,7 @@ func TestGitOpsTeamSoftwareInstallers(t *testing.T) { token, err := test.CreateVPPTokenEncoded(tokExpire, "fleet", "ca") require.NoError(t, err) - ds.SetTeamVPPAppsFunc = func(ctx context.Context, teamID *uint, adamIDs []fleet.VPPAppTeam) error { + ds.SetTeamVPPAppsFunc = func(ctx context.Context, teamID *uint, adamIDs []fleet.VPPAppTeam, _ map[string]uint) error { return nil } ds.GetVPPAppsFunc = func(ctx context.Context, teamID *uint) ([]fleet.VPPAppResponse, error) { @@ -248,7 +248,7 @@ func TestGitOpsNoTeamVPPPolicies(t *testing.T) { token, err := test.CreateVPPTokenEncoded(tokExpire, "fleet", "ca") require.NoError(t, err) - ds.SetTeamVPPAppsFunc = func(ctx context.Context, teamID *uint, adamIDs []fleet.VPPAppTeam) error { + ds.SetTeamVPPAppsFunc = func(ctx context.Context, teamID *uint, adamIDs []fleet.VPPAppTeam, _ map[string]uint) error { return nil } ds.BatchInsertVPPAppsFunc = func(ctx context.Context, apps []*fleet.VPPApp) error { @@ -368,7 +368,7 @@ func TestGitOpsNoTeamSoftwareInstallers(t *testing.T) { token, err := test.CreateVPPTokenEncoded(tokExpire, "fleet", "ca") require.NoError(t, err) - ds.SetTeamVPPAppsFunc = func(ctx context.Context, teamID *uint, adamIDs []fleet.VPPAppTeam) error { + ds.SetTeamVPPAppsFunc = func(ctx context.Context, teamID *uint, adamIDs []fleet.VPPAppTeam, _ map[string]uint) error { return nil } ds.BatchInsertVPPAppsFunc = func(ctx context.Context, apps []*fleet.VPPApp) error { @@ -501,7 +501,7 @@ func TestGitOpsTeamVPPApps(t *testing.T) { token, err := test.CreateVPPTokenEncoded(c.tokenExpiration, "fleet", "ca") require.NoError(t, err) - ds.SetTeamVPPAppsFunc = func(ctx context.Context, teamID *uint, adamIDs []fleet.VPPAppTeam) error { + ds.SetTeamVPPAppsFunc = func(ctx context.Context, teamID *uint, adamIDs []fleet.VPPAppTeam, _ map[string]uint) error { return nil } ds.BatchInsertVPPAppsFunc = func(ctx context.Context, apps []*fleet.VPPApp) error { diff --git a/ee/server/service/vpp.go b/ee/server/service/vpp.go index f25786a4e0..de341671a8 100644 --- a/ee/server/service/vpp.go +++ b/ee/server/service/vpp.go @@ -90,6 +90,7 @@ func (svc *Service) BatchAssociateVPPApps(ctx context.Context, teamName string, LabelsExcludeAny: payload.LabelsExcludeAny, LabelsIncludeAny: payload.LabelsIncludeAny, Categories: payload.Categories, + DisplayName: payload.DisplayName, }, { AppStoreID: payload.AppStoreID, SelfService: payload.SelfService, @@ -98,6 +99,7 @@ func (svc *Service) BatchAssociateVPPApps(ctx context.Context, teamName string, LabelsExcludeAny: payload.LabelsExcludeAny, LabelsIncludeAny: payload.LabelsIncludeAny, Categories: payload.Categories, + DisplayName: payload.DisplayName, }, { AppStoreID: payload.AppStoreID, SelfService: payload.SelfService, @@ -106,6 +108,7 @@ func (svc *Service) BatchAssociateVPPApps(ctx context.Context, teamName string, LabelsExcludeAny: payload.LabelsExcludeAny, LabelsIncludeAny: payload.LabelsIncludeAny, Categories: payload.Categories, + DisplayName: payload.DisplayName, }}...) } else { payloadsWithPlatform = append(payloadsWithPlatform, fleet.VPPBatchPayloadWithPlatform{ @@ -116,6 +119,7 @@ func (svc *Service) BatchAssociateVPPApps(ctx context.Context, teamName string, LabelsExcludeAny: payload.LabelsExcludeAny, LabelsIncludeAny: payload.LabelsIncludeAny, Categories: payload.Categories, + DisplayName: payload.DisplayName, }) } @@ -169,6 +173,7 @@ func (svc *Service) BatchAssociateVPPApps(ctx context.Context, teamName string, InstallDuringSetup: payload.InstallDuringSetup, ValidatedLabels: validatedLabels, CategoryIDs: catIDs, + DisplayName: ptr.String(payload.DisplayName), } switch payload.Platform { case fleet.AndroidPlatform: @@ -268,6 +273,12 @@ func (svc *Service) BatchAssociateVPPApps(ctx context.Context, teamName string, return nil, ctxerr.Wrap(ctx, err, "inserting vpp app metadata") } } + + appStoreIDToTitleID := make(map[string]uint, len(appStoreApps)) + for _, a := range appStoreApps { + appStoreIDToTitleID[a.AdamID] = a.TitleID + } + // Filter out the apps with invalid platforms if len(appStoreApps) != len(allPlatformApps) { allPlatformApps = make([]fleet.VPPAppTeam, 0, len(appStoreApps)) @@ -276,7 +287,7 @@ func (svc *Service) BatchAssociateVPPApps(ctx context.Context, teamName string, } } - if err := svc.ds.SetTeamVPPApps(ctx, teamID, allPlatformApps); err != nil { + if err := svc.ds.SetTeamVPPApps(ctx, teamID, allPlatformApps, appStoreIDToTitleID); err != nil { if errors.Is(err, sql.ErrNoRows) { return nil, fleet.NewUserMessageError(ctxerr.Wrap(ctx, err, "no vpp token to set team vpp assets"), http.StatusUnprocessableEntity) } @@ -671,6 +682,7 @@ func getVPPAppsMetadata(ctx context.Context, ids []fleet.VPPAppTeam) ([]*fleet.V AppTeamID: id.AppTeamID, Categories: id.Categories, CategoryIDs: id.CategoryIDs, + DisplayName: id.DisplayName, } } else { adamIDMap[id.AdamID][id.Platform] = fleet.VPPAppTeam{ @@ -680,6 +692,7 @@ func getVPPAppsMetadata(ctx context.Context, ids []fleet.VPPAppTeam) ([]*fleet.V AppTeamID: id.AppTeamID, Categories: id.Categories, CategoryIDs: id.CategoryIDs, + DisplayName: id.DisplayName, } } } @@ -709,6 +722,7 @@ func getVPPAppsMetadata(ctx context.Context, ids []fleet.VPPAppTeam) ([]*fleet.V AppTeamID: props.AppTeamID, Categories: props.Categories, CategoryIDs: props.CategoryIDs, + DisplayName: props.DisplayName, }, BundleIdentifier: metadata.BundleID, IconURL: metadata.ArtworkURL, diff --git a/server/datastore/mysql/vpp.go b/server/datastore/mysql/vpp.go index 1e68b726de..2488662dc4 100644 --- a/server/datastore/mysql/vpp.go +++ b/server/datastore/mysql/vpp.go @@ -389,7 +389,7 @@ func (ds *Datastore) getVPPAppTeamCategoryIDs(ctx context.Context, vppAppTeamID return ids, nil } -func (ds *Datastore) SetTeamVPPApps(ctx context.Context, teamID *uint, incomingApps []fleet.VPPAppTeam) error { +func (ds *Datastore) SetTeamVPPApps(ctx context.Context, teamID *uint, incomingApps []fleet.VPPAppTeam, appStoreAppIDsToTitleIDs map[string]uint) error { existingApps, err := ds.GetAssignedVPPApps(ctx, teamID) if err != nil { return ctxerr.Wrap(ctx, err, "SetTeamVPPApps getting list of existing apps") @@ -433,7 +433,7 @@ func (ds *Datastore) SetTeamVPPApps(ctx context.Context, teamID *uint, incomingA // upsert it if it does not exist or labels or SelfService or InstallDuringSetup flags are changed existingApp, isExistingApp := existingApps[incomingApp.VPPAppID] incomingApp.AppTeamID = existingApp.AppTeamID - var labelsChanged, categoriesChanged bool + var labelsChanged, categoriesChanged, displayNameChanged bool if isExistingApp { existingLabels, err := ds.getExistingLabels(ctx, incomingApp.AppTeamID) if err != nil { @@ -449,6 +449,10 @@ func (ds *Datastore) SetTeamVPPApps(ctx context.Context, teamID *uint, incomingA categoriesChanged = !slices.Equal(existingCatIDs, incomingApp.CategoryIDs) + existingDisplayName := ptr.ValOrZero(existingApp.DisplayName) + incomingDisplayName := ptr.ValOrZero(incomingApp.DisplayName) + displayNameChanged = existingDisplayName != incomingDisplayName + } // Get the hosts that are NOT in label scope currently (before the update happens) @@ -465,6 +469,7 @@ func (ds *Datastore) SetTeamVPPApps(ctx context.Context, teamID *uint, incomingA existingApp.SelfService != incomingApp.SelfService || labelsChanged || categoriesChanged || + displayNameChanged || incomingApp.InstallDuringSetup != nil && existingApp.InstallDuringSetup != nil && *incomingApp.InstallDuringSetup != *existingApp.InstallDuringSetup { @@ -530,6 +535,12 @@ func (ds *Datastore) SetTeamVPPApps(ctx context.Context, teamID *uint, incomingA } } + if toAdd.DisplayName != nil { + if err := updateSoftwareTitleDisplayName(ctx, tx, teamID, appStoreAppIDsToTitleIDs[toAdd.AdamID], *toAdd.DisplayName); err != nil { + return ctxerr.Wrap(ctx, err, "setting software title display name for vpp app") + } + } + if hostsNotInScope, ok := appsWithChangedLabels[toAdd.AppTeamID]; ok { hostsInScope, err := ds.GetIncludedHostIDMapForVPPAppTx(ctx, tx, toAdd.AppTeamID) if err != nil { diff --git a/server/datastore/mysql/vpp_test.go b/server/datastore/mysql/vpp_test.go index 182741ed1a..c062770cbb 100644 --- a/server/datastore/mysql/vpp_test.go +++ b/server/datastore/mysql/vpp_test.go @@ -777,7 +777,7 @@ func testSetTeamVPPApps(t *testing.T, ds *Datastore) { err = ds.SetTeamVPPApps(ctx, &team.ID, []fleet.VPPAppTeam{ {VPPAppID: app1.VPPAppID, InstallDuringSetup: ptr.Bool(true)}, {VPPAppID: app2.VPPAppID, SelfService: true}, - }) + }, map[string]uint{}) require.NoError(t, err) // create policies using two of the apps @@ -818,7 +818,7 @@ func testSetTeamVPPApps(t *testing.T, ds *Datastore) { {VPPAppID: app1.VPPAppID, InstallDuringSetup: ptr.Bool(true)}, {VPPAppID: app2.VPPAppID}, {VPPAppID: app3.VPPAppID}, - }) + }, nil) require.NoError(t, err) policy1, err = ds.Policy(ctx, policy1.ID) @@ -839,7 +839,7 @@ func testSetTeamVPPApps(t *testing.T, ds *Datastore) { {VPPAppID: app1.VPPAppID, InstallDuringSetup: ptr.Bool(true)}, {VPPAppID: app2.VPPAppID, SelfService: true}, {VPPAppID: app4.VPPAppID}, - }) + }, nil) require.NoError(t, err) policy1, err = ds.Policy(ctx, policy1.ID) @@ -859,7 +859,7 @@ func testSetTeamVPPApps(t *testing.T, ds *Datastore) { err = ds.SetTeamVPPApps(ctx, &team.ID, []fleet.VPPAppTeam{ {VPPAppID: app2.VPPAppID, SelfService: true}, {VPPAppID: app4.VPPAppID}, - }) + }, nil) require.Error(t, err) require.ErrorIs(t, err, errDeleteInstallerInstalledDuringSetup) @@ -868,14 +868,14 @@ func testSetTeamVPPApps(t *testing.T, ds *Datastore) { {VPPAppID: app1.VPPAppID, InstallDuringSetup: ptr.Bool(false)}, {VPPAppID: app2.VPPAppID, SelfService: true}, {VPPAppID: app4.VPPAppID}, - }) + }, nil) require.NoError(t, err) // Remove app1 now works err = ds.SetTeamVPPApps(ctx, &team.ID, []fleet.VPPAppTeam{ {VPPAppID: app2.VPPAppID, SelfService: true}, {VPPAppID: app4.VPPAppID}, - }) + }, nil) require.NoError(t, err) policy1, err = ds.Policy(ctx, policy1.ID) @@ -887,7 +887,7 @@ func testSetTeamVPPApps(t *testing.T, ds *Datastore) { require.Equal(t, app2Meta.VPPAppsTeamsID, *policy2.VPPAppsTeamsID) // Remove all apps - err = ds.SetTeamVPPApps(ctx, &team.ID, []fleet.VPPAppTeam{}) + err = ds.SetTeamVPPApps(ctx, &team.ID, []fleet.VPPAppTeam{}, nil) require.NoError(t, err) assigned, err = ds.GetAssignedVPPApps(ctx, &team.ID) @@ -1806,7 +1806,7 @@ func testVPPTokenTeamAssignment(t *testing.T, ds *Datastore) { err = ds.SetTeamVPPApps(ctx, &team1.ID, []fleet.VPPAppTeam{ {VPPAppID: app1.VPPAppID, InstallDuringSetup: ptr.Bool(true)}, {VPPAppID: app2.VPPAppID, SelfService: true}, - }) + }, nil) require.NoError(t, err) assigned, err = ds.GetAssignedVPPApps(ctx, &team1.ID) @@ -1913,7 +1913,7 @@ func testSetTeamVPPAppsWithLabels(t *testing.T, ds *Datastore) { err = ds.SetTeamVPPApps(ctx, &team.ID, []fleet.VPPAppTeam{ app1.VPPAppTeam, app2.VPPAppTeam, - }) + }, nil) require.NoError(t, err) assigned, err = ds.GetAssignedVPPApps(ctx, &team.ID) @@ -1972,7 +1972,7 @@ func testSetTeamVPPAppsWithLabels(t *testing.T, ds *Datastore) { err = ds.SetTeamVPPApps(ctx, &team.ID, []fleet.VPPAppTeam{ app1.VPPAppTeam, app2.VPPAppTeam, - }) + }, nil) require.NoError(t, err) assigned, err = ds.GetAssignedVPPApps(ctx, &team.ID) diff --git a/server/fleet/datastore.go b/server/fleet/datastore.go index fa7480e19c..8ef8d02f31 100644 --- a/server/fleet/datastore.go +++ b/server/fleet/datastore.go @@ -2127,7 +2127,7 @@ type Datastore interface { BatchInsertVPPApps(ctx context.Context, apps []*VPPApp) error GetAssignedVPPApps(ctx context.Context, teamID *uint) (map[VPPAppID]VPPAppTeam, error) GetVPPApps(ctx context.Context, teamID *uint) ([]VPPAppResponse, error) - SetTeamVPPApps(ctx context.Context, teamID *uint, appIDs []VPPAppTeam) error + SetTeamVPPApps(ctx context.Context, teamID *uint, appIDs []VPPAppTeam, appStoreAppIDsToTitleIDs map[string]uint) error InsertVPPAppWithTeam(ctx context.Context, app *VPPApp, teamID *uint) (*VPPApp, error) GetVPPAppsToInstallDuringSetupExperience(ctx context.Context, teamID *uint, platform string) ([]string, error) diff --git a/server/mock/datastore_mock.go b/server/mock/datastore_mock.go index 0c3e6124d0..7bc293d70a 100644 --- a/server/mock/datastore_mock.go +++ b/server/mock/datastore_mock.go @@ -1363,7 +1363,7 @@ type GetAssignedVPPAppsFunc func(ctx context.Context, teamID *uint) (map[fleet.V type GetVPPAppsFunc func(ctx context.Context, teamID *uint) ([]fleet.VPPAppResponse, error) -type SetTeamVPPAppsFunc func(ctx context.Context, teamID *uint, appIDs []fleet.VPPAppTeam) error +type SetTeamVPPAppsFunc func(ctx context.Context, teamID *uint, appIDs []fleet.VPPAppTeam, appStoreAppIDsToTitleIDs map[string]uint) error type InsertVPPAppWithTeamFunc func(ctx context.Context, app *fleet.VPPApp, teamID *uint) (*fleet.VPPApp, error) @@ -8822,11 +8822,11 @@ func (s *DataStore) GetVPPApps(ctx context.Context, teamID *uint) ([]fleet.VPPAp return s.GetVPPAppsFunc(ctx, teamID) } -func (s *DataStore) SetTeamVPPApps(ctx context.Context, teamID *uint, appIDs []fleet.VPPAppTeam) error { +func (s *DataStore) SetTeamVPPApps(ctx context.Context, teamID *uint, appIDs []fleet.VPPAppTeam, appStoreAppIDsToTitleIDs map[string]uint) error { s.mu.Lock() s.SetTeamVPPAppsFuncInvoked = true s.mu.Unlock() - return s.SetTeamVPPAppsFunc(ctx, teamID, appIDs) + return s.SetTeamVPPAppsFunc(ctx, teamID, appIDs, appStoreAppIDsToTitleIDs) } func (s *DataStore) InsertVPPAppWithTeam(ctx context.Context, app *fleet.VPPApp, teamID *uint) (*fleet.VPPApp, error) { diff --git a/server/service/integration_mdm_test.go b/server/service/integration_mdm_test.go index 15d7dad163..3d9f1eb12c 100644 --- a/server/service/integration_mdm_test.go +++ b/server/service/integration_mdm_test.go @@ -11722,8 +11722,8 @@ func (s *integrationMDMTestSuite) TestBatchAssociateAppStoreApps() { // Remove an app s.DoJSON("POST", batchURL, batchAssociateAppStoreAppsRequest{Apps: []fleet.VPPBatchPayload{ - {AppStoreID: s.appleVPPConfigSrvConfig.Assets[0].AdamID}, - {AppStoreID: driveAppID, Platform: fleet.AndroidPlatform}, + {AppStoreID: s.appleVPPConfigSrvConfig.Assets[0].AdamID, DisplayName: "VPPAppUpdatedName"}, + {AppStoreID: driveAppID, Platform: fleet.AndroidPlatform, DisplayName: "DriveUpdatedName"}, }}, http.StatusOK, &batchAssociateResponse, "team_name", tmGood.Name) require.Len(t, batchAssociateResponse.Apps, 2) @@ -11742,6 +11742,60 @@ func (s *integrationMDMTestSuite) TestBatchAssociateAppStoreApps() { s.runWorker() s.Assert().True(s.androidAPIClient.EnterprisesPoliciesPatchFuncInvoked) checkJobs([]string{driveAppID}) + + var listSwTitles listSoftwareTitlesResponse + s.DoJSON("GET", "/api/latest/fleet/software/titles", nil, http.StatusOK, &listSwTitles, "team_id", fmt.Sprint(tmGood.ID)) + + s.Assert().Len(listSwTitles.SoftwareTitles, 2) + for _, sw := range listSwTitles.SoftwareTitles { + switch sw.AppStoreApp.AppStoreID { + case driveAppID: + s.Assert().Equal("DriveUpdatedName", sw.DisplayName) + case s.appleVPPConfigSrvConfig.Assets[0].AdamID: + s.Assert().Equal("VPPAppUpdatedName", sw.DisplayName) + } + } + + // change display names + setDisplayNames := func(androidAppName, vppAppName string) { + s.DoJSON("POST", batchURL, batchAssociateAppStoreAppsRequest{Apps: []fleet.VPPBatchPayload{ + {AppStoreID: s.appleVPPConfigSrvConfig.Assets[0].AdamID, DisplayName: vppAppName}, + {AppStoreID: driveAppID, Platform: fleet.AndroidPlatform, DisplayName: androidAppName}, + }}, http.StatusOK, &batchAssociateResponse, "team_name", tmGood.Name) + require.Len(t, batchAssociateResponse.Apps, 2) + + assoc, err = s.ds.GetAssignedVPPApps(ctx, &tmGood.ID) + require.NoError(t, err) + require.Len(t, assoc, 2) + + for _, a := range []fleet.VPPAppID{ + {AdamID: s.appleVPPConfigSrvConfig.Assets[0].AdamID, Platform: fleet.MacOSPlatform}, + {AdamID: driveAppID, Platform: fleet.AndroidPlatform}, + } { + assert.Contains(t, assoc, a) + } + + time.Sleep(time.Second) + s.runWorker() + s.Assert().True(s.androidAPIClient.EnterprisesPoliciesPatchFuncInvoked) + checkJobs([]string{driveAppID}) + + s.DoJSON("GET", "/api/latest/fleet/software/titles", nil, http.StatusOK, &listSwTitles, "team_id", fmt.Sprint(tmGood.ID)) + + s.Assert().Len(listSwTitles.SoftwareTitles, 2) + for _, sw := range listSwTitles.SoftwareTitles { + switch sw.AppStoreApp.AppStoreID { + case driveAppID: + s.Assert().Equal(androidAppName, sw.DisplayName) + case s.appleVPPConfigSrvConfig.Assets[0].AdamID: + s.Assert().Equal(vppAppName, sw.DisplayName) + } + } + } + + setDisplayNames("VPPAppUpdatedName2", "DriveUpdatedName2") + setDisplayNames("", "") + } func (s *integrationMDMTestSuite) TestInvalidCommandUUID() {