From b3df8b794098be44df59e5f6fdc38feced45f45a Mon Sep 17 00:00:00 2001 From: Jonathan Katz <44128041+jkatz01@users.noreply.github.com> Date: Tue, 28 Oct 2025 16:21:51 -0400 Subject: [PATCH] Fix software_package info not provided (#34888) **Related issue:** Resolves #34882 Also fix uploaded_time not being returned in metadata # Checklist for submitter ## Testing - [x] Added/updated automated tests - [ ] 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 --- server/datastore/mysql/in_house_apps.go | 1 + server/datastore/mysql/in_house_apps_test.go | 1 + .../service/integration_vpp_install_test.go | 20 +++++++++++++------ server/service/software_titles.go | 20 +++++++++++++++++++ 4 files changed, 36 insertions(+), 6 deletions(-) diff --git a/server/datastore/mysql/in_house_apps.go b/server/datastore/mysql/in_house_apps.go index a4dc85e08d..7e9b993250 100644 --- a/server/datastore/mysql/in_house_apps.go +++ b/server/datastore/mysql/in_house_apps.go @@ -183,6 +183,7 @@ SELECT iha.platform, iha.storage_id, iha.version, + iha.created_at AS uploaded_at, st.bundle_identifier AS bundle_identifier, COALESCE(st.name, '') AS software_title FROM diff --git a/server/datastore/mysql/in_house_apps_test.go b/server/datastore/mysql/in_house_apps_test.go index 4eca1110e0..67a3a0689e 100644 --- a/server/datastore/mysql/in_house_apps_test.go +++ b/server/datastore/mysql/in_house_apps_test.go @@ -99,6 +99,7 @@ func testInHouseAppsCrud(t *testing.T, ds *Datastore) { require.NoError(t, err) require.Equal(t, payload.Title, installer.SoftwareTitle) require.Equal(t, payload.Version, installer.Version) + require.WithinDuration(t, time.Now(), installer.UploadedAt, time.Minute) // Install on multiple users with pending, success, failure createInHouseAppInstallRequest(t, ds, host1.ID, installerID, titleID, user1) diff --git a/server/service/integration_vpp_install_test.go b/server/service/integration_vpp_install_test.go index fab56c8115..bfe74bd4cf 100644 --- a/server/service/integration_vpp_install_test.go +++ b/server/service/integration_vpp_install_test.go @@ -1292,13 +1292,12 @@ func (s *integrationMDMTestSuite) TestInHouseAppInstall() { return sqlx.GetContext(ctx, q, &titleID, "SELECT title_id FROM in_house_apps WHERE filename = 'ipa_test.ipa'") }) - // TODO: uncomment once this endpoint supports in house apps - // var resp listSoftwareTitlesResponse - // s.DoJSON("GET", "/api/latest/fleet/software/titles", listSoftwareTitlesRequest{}, http.StatusOK, &resp, "team_id", "0") + var resp listSoftwareTitlesResponse + s.DoJSON("GET", "/api/latest/fleet/software/titles", listSoftwareTitlesRequest{}, http.StatusOK, &resp, "team_id", "0") - // assert.Len(t, resp.SoftwareTitles, 1) - // assert.Equal(t, "ipa_test", resp.SoftwareTitles[0].Name) - // titleID := resp.SoftwareTitles[0].ID + assert.Len(t, resp.SoftwareTitles, 2) + assert.Equal(t, "ipa_test", resp.SoftwareTitles[0].Name) + titleID = resp.SoftwareTitles[0].ID // Attempt installation on non-scoped app, should fail var installResp installSoftwareResponse @@ -1418,4 +1417,13 @@ func (s *integrationMDMTestSuite) TestInHouseAppInstall() { return nil }) + // Get title and software package details + var st getSoftwareTitleResponse + s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/software/titles/%d", titleID), + nil, http.StatusOK, &st) + + require.Equal(t, "ipa_test", st.SoftwareTitle.Name) + require.Equal(t, "ipa_test.ipa", st.SoftwareTitle.SoftwarePackage.Name) + require.Equal(t, "ios", st.SoftwareTitle.SoftwarePackage.Platform) + require.WithinDuration(t, time.Now(), st.SoftwareTitle.SoftwarePackage.UploadedAt, time.Hour) } diff --git a/server/service/software_titles.go b/server/service/software_titles.go index da6c9b2472..6452707eec 100644 --- a/server/service/software_titles.go +++ b/server/service/software_titles.go @@ -216,6 +216,26 @@ func (svc *Service) SoftwareTitleByID(ctx context.Context, id uint, teamID *uint } software.AppStoreApp = meta } + + // add in house app data if needed + if software.InHouseAppCount > 0 { + meta, err := svc.ds.GetInHouseAppMetadataByTeamAndTitleID(ctx, teamID, id) + if err != nil && !fleet.IsNotFound(err) { + return nil, ctxerr.Wrap(ctx, err, "get in house app metadata") + } + if meta != nil { + summary, err := svc.ds.GetSummaryHostInHouseAppInstalls(ctx, teamID, meta.InstallerID) + if err != nil { + return nil, ctxerr.Wrap(ctx, err, "get in house app status summary") + } + meta.Status = &fleet.SoftwareInstallerStatusSummary{ + Installed: summary.Installed, + PendingInstall: summary.Pending, + FailedInstall: summary.Failed, + } + } + software.SoftwarePackage = meta + } } return software, nil