Revise FMA list endpoint to match Windows FMA spec (#27180)
For #26652. No changes file as that'll come in another PR. Will stack additional PRs on top of this one (for ingestion changes etc.) to get this merged more quickly. # Checklist for submitter If some of the following don't apply, delete the relevant line. <!-- Note that API documentation changes are now addressed by the product design team. --> - [x] Input data is properly validated, `SELECT *` is avoided, SQL injection is prevented (using placeholders for values in statements) - [x] If paths of existing endpoints are modified without backwards compatibility, checked the frontend/CLI for any necessary changes - [x] Added/updated automated tests - [x] A detailed QA plan exists on the associated ticket (if it isn't there, work with the product group's QA engineer to add it) - [x] Manual QA for all new/changed functionality
This commit is contained in:
@@ -5,6 +5,7 @@ import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/fleetdm/fleet/v4/server/contexts/ctxerr"
|
||||
"github.com/fleetdm/fleet/v4/server/fleet"
|
||||
@@ -68,28 +69,53 @@ ON DUPLICATE KEY UPDATE
|
||||
return app, nil
|
||||
}
|
||||
|
||||
func (ds *Datastore) GetMaintainedAppByID(ctx context.Context, appID uint) (*fleet.MaintainedApp, error) {
|
||||
const stmt = `
|
||||
SELECT
|
||||
fla.id,
|
||||
fla.name,
|
||||
fla.token,
|
||||
fla.version,
|
||||
fla.platform,
|
||||
fla.installer_url,
|
||||
fla.sha256,
|
||||
fla.bundle_identifier,
|
||||
sc1.contents AS install_script,
|
||||
sc2.contents AS uninstall_script
|
||||
FROM fleet_library_apps fla
|
||||
const teamFMATitlesJoin = `
|
||||
team_titles.id software_title_id FROM fleet_library_apps fla
|
||||
LEFT JOIN (
|
||||
SELECT DISTINCT st.id, st.bundle_identifier, st.name
|
||||
FROM software_titles st
|
||||
LEFT JOIN
|
||||
software_installers si
|
||||
ON si.title_id = st.id AND si.global_or_team_id = ?
|
||||
AND si.platform IN ('darwin','windows')
|
||||
LEFT JOIN
|
||||
vpp_apps va
|
||||
ON va.title_id = st.id
|
||||
AND va.platform = 'darwin'
|
||||
LEFT JOIN
|
||||
vpp_apps_teams vat
|
||||
ON vat.adam_id = va.adam_id
|
||||
AND vat.platform = va.platform
|
||||
AND vat.global_or_team_id = ?
|
||||
WHERE si.id IS NOT NULL OR vat.id IS NOT NULL
|
||||
) team_titles ON (
|
||||
team_titles.bundle_identifier != '' AND team_titles.bundle_identifier = fla.bundle_identifier
|
||||
) OR (
|
||||
team_titles.bundle_identifier = '' AND team_titles.name = fla.name
|
||||
)`
|
||||
|
||||
func (ds *Datastore) GetMaintainedAppByID(ctx context.Context, appID uint, teamID *uint) (*fleet.MaintainedApp, error) {
|
||||
stmt := `SELECT fla.id, fla.name, fla.token, fla.version, fla.platform, fla.installer_url, fla.sha256, fla.bundle_identifier,
|
||||
sc1.contents AS install_script, sc2.contents AS uninstall_script, `
|
||||
var args []any
|
||||
|
||||
if teamID != nil {
|
||||
stmt += teamFMATitlesJoin
|
||||
args = []any{teamID, teamID}
|
||||
} else {
|
||||
stmt += `NULL software_title_id FROM fleet_library_apps fla`
|
||||
}
|
||||
|
||||
stmt += `
|
||||
JOIN script_contents sc1 ON sc1.id = fla.install_script_content_id
|
||||
JOIN script_contents sc2 ON sc2.id = fla.uninstall_script_content_id
|
||||
WHERE
|
||||
fla.id = ?
|
||||
`
|
||||
args = append(args, appID)
|
||||
|
||||
var app fleet.MaintainedApp
|
||||
if err := sqlx.GetContext(ctx, ds.reader(ctx), &app, stmt, appID); err != nil {
|
||||
if err := sqlx.GetContext(ctx, ds.reader(ctx), &app, stmt, args...); err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, ctxerr.Wrap(ctx, notFound("MaintainedApp"), "no matching maintained app found")
|
||||
}
|
||||
@@ -100,36 +126,30 @@ WHERE
|
||||
return &app, nil
|
||||
}
|
||||
|
||||
// NoMaintainedAppsInDatabase is the error type for no Fleet Maintained Apps in the database
|
||||
type NoMaintainedAppsInDatabase struct {
|
||||
fleet.ErrorWithUUID
|
||||
}
|
||||
|
||||
// Error implements the error interface.
|
||||
func (e *NoMaintainedAppsInDatabase) Error() string {
|
||||
return `Fleet was unable to ingest the maintained apps list. Run fleetctl trigger name=maintained_apps to try repopulating the apps list.`
|
||||
}
|
||||
|
||||
// StatusCode implements the go-kit http StatusCoder interface.
|
||||
func (e *NoMaintainedAppsInDatabase) StatusCode() int {
|
||||
return http.StatusNotFound
|
||||
}
|
||||
|
||||
func (ds *Datastore) ListAvailableFleetMaintainedApps(ctx context.Context, teamID *uint, opt fleet.ListOptions) ([]fleet.MaintainedApp, *fleet.PaginationMetadata, error) {
|
||||
stmt := `SELECT fla.id, fla.name, fla.version, fla.platform, fla.updated_at FROM fleet_library_apps fla `
|
||||
stmt := `SELECT fla.id, fla.name, fla.platform, `
|
||||
var args []any
|
||||
|
||||
if teamID != nil {
|
||||
stmt += `WHERE NOT EXISTS (
|
||||
SELECT
|
||||
1
|
||||
FROM
|
||||
software_titles st
|
||||
LEFT JOIN
|
||||
software_installers si
|
||||
ON si.title_id = st.id
|
||||
LEFT JOIN
|
||||
vpp_apps va
|
||||
ON va.title_id = st.id
|
||||
LEFT JOIN
|
||||
vpp_apps_teams vat
|
||||
ON vat.adam_id = va.adam_id
|
||||
WHERE
|
||||
st.bundle_identifier = fla.bundle_identifier
|
||||
AND (
|
||||
(si.platform = fla.platform AND si.global_or_team_id = ?)
|
||||
OR
|
||||
(va.platform = fla.platform AND vat.global_or_team_id = ?)
|
||||
)
|
||||
)`
|
||||
stmt += teamFMATitlesJoin + ` WHERE TRUE`
|
||||
args = []any{teamID, teamID}
|
||||
} else {
|
||||
stmt += `WHERE TRUE`
|
||||
stmt += `NULL software_title_id FROM fleet_library_apps fla`
|
||||
}
|
||||
|
||||
if match := opt.MatchQuery; match != "" {
|
||||
@@ -138,16 +158,27 @@ func (ds *Datastore) ListAvailableFleetMaintainedApps(ctx context.Context, teamI
|
||||
args = append(args, match)
|
||||
}
|
||||
|
||||
// perform a second query to grab the counts. Build the count statement before
|
||||
// perform a second query to grab the filtered count. Build the count statement before
|
||||
// adding the pagination constraints to the stmt but after including the
|
||||
// MatchQuery option sql.
|
||||
dbReader := ds.reader(ctx)
|
||||
getAppsCountStmt := fmt.Sprintf(`SELECT COUNT(DISTINCT s.id) FROM (%s) AS s`, stmt)
|
||||
var counts int
|
||||
if err := sqlx.GetContext(ctx, dbReader, &counts, getAppsCountStmt, args...); err != nil {
|
||||
var filteredCount int
|
||||
if err := sqlx.GetContext(ctx, dbReader, &filteredCount, getAppsCountStmt, args...); err != nil {
|
||||
return nil, nil, ctxerr.Wrap(ctx, err, "get fleet maintained apps count")
|
||||
}
|
||||
|
||||
if filteredCount == 0 { // check if we have nothing in the full apps list, in which case provide an error back
|
||||
var totalCount int
|
||||
if err := sqlx.GetContext(ctx, dbReader, &totalCount, `SELECT COUNT(id) FROM fleet_library_apps`); err != nil {
|
||||
return nil, nil, ctxerr.Wrap(ctx, err, "get fleet maintained apps total count")
|
||||
}
|
||||
|
||||
if totalCount == 0 {
|
||||
return nil, nil, &NoMaintainedAppsInDatabase{}
|
||||
}
|
||||
}
|
||||
|
||||
stmtPaged, args := appendListOptionsWithCursorToSQL(stmt, args, &opt)
|
||||
|
||||
var avail []fleet.MaintainedApp
|
||||
@@ -155,8 +186,8 @@ func (ds *Datastore) ListAvailableFleetMaintainedApps(ctx context.Context, teamI
|
||||
return nil, nil, ctxerr.Wrap(ctx, err, "selecting available fleet managed apps")
|
||||
}
|
||||
|
||||
meta := &fleet.PaginationMetadata{HasPreviousResults: opt.Page > 0, TotalResults: uint(counts)} //nolint:gosec // dismiss G115
|
||||
if len(avail) > int(opt.PerPage) { //nolint:gosec // dismiss G115
|
||||
meta := &fleet.PaginationMetadata{HasPreviousResults: opt.Page > 0, TotalResults: uint(filteredCount)} //nolint:gosec // dismiss G115
|
||||
if len(avail) > int(opt.PerPage) { //nolint:gosec // dismiss G115
|
||||
meta.HasNextResults = true
|
||||
avail = avail[:len(avail)-1]
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
|
||||
"github.com/fleetdm/fleet/v4/server/fleet"
|
||||
"github.com/fleetdm/fleet/v4/server/mdm/maintainedapps"
|
||||
"github.com/fleetdm/fleet/v4/server/ptr"
|
||||
"github.com/fleetdm/fleet/v4/server/test"
|
||||
"github.com/go-kit/kit/log"
|
||||
"github.com/jmoiron/sqlx"
|
||||
@@ -22,8 +23,7 @@ func TestMaintainedApps(t *testing.T) {
|
||||
}{
|
||||
{"UpsertMaintainedApps", testUpsertMaintainedApps},
|
||||
{"IngestWithBrew", testIngestWithBrew},
|
||||
{"ListAvailableApps", testListAvailableApps},
|
||||
{"GetMaintainedAppByID", testGetMaintainedAppByID},
|
||||
{"ListAndGetAvailableApps", testListAndGetAvailableApps},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
@@ -58,7 +58,7 @@ func testUpsertMaintainedApps(t *testing.T, ds *Datastore) {
|
||||
Token: "figma",
|
||||
InstallerURL: "https://desktop.figma.com/mac-arm/Figma-999.9.9.zip",
|
||||
Version: "999.9.9",
|
||||
Platform: fleet.MacOSPlatform,
|
||||
Platform: "darwin",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -89,7 +89,7 @@ func testIngestWithBrew(t *testing.T, ds *Datastore) {
|
||||
require.ElementsMatch(t, expectedTokens, actualTokens)
|
||||
}
|
||||
|
||||
func testListAvailableApps(t *testing.T, ds *Datastore) {
|
||||
func testListAndGetAvailableApps(t *testing.T, ds *Datastore) {
|
||||
ctx := context.Background()
|
||||
|
||||
user := test.NewUser(t, ds, "Zaphod Beeblebrox", "zaphod@example.com", true)
|
||||
@@ -102,7 +102,7 @@ func testListAvailableApps(t *testing.T, ds *Datastore) {
|
||||
Name: "Maintained1",
|
||||
Token: "maintained1",
|
||||
Version: "1.0.0",
|
||||
Platform: fleet.MacOSPlatform,
|
||||
Platform: "darwin",
|
||||
InstallerURL: "http://example.com/main1",
|
||||
SHA256: "DEADBEEF",
|
||||
BundleIdentifier: "fleet.maintained1",
|
||||
@@ -115,7 +115,7 @@ func testListAvailableApps(t *testing.T, ds *Datastore) {
|
||||
Name: "Maintained2",
|
||||
Token: "maintained2",
|
||||
Version: "1.0.0",
|
||||
Platform: fleet.MacOSPlatform,
|
||||
Platform: "darwin",
|
||||
InstallerURL: "http://example.com/main1",
|
||||
SHA256: "DEADBEEF",
|
||||
BundleIdentifier: "fleet.maintained2",
|
||||
@@ -127,7 +127,7 @@ func testListAvailableApps(t *testing.T, ds *Datastore) {
|
||||
Name: "Maintained3",
|
||||
Token: "maintained3",
|
||||
Version: "1.0.0",
|
||||
Platform: fleet.MacOSPlatform,
|
||||
Platform: "darwin",
|
||||
InstallerURL: "http://example.com/main1",
|
||||
SHA256: "DEADBEEF",
|
||||
BundleIdentifier: "fleet.maintained3",
|
||||
@@ -136,43 +136,37 @@ func testListAvailableApps(t *testing.T, ds *Datastore) {
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
gotApp, err := ds.GetMaintainedAppByID(ctx, maintained1.ID, nil)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, maintained1, gotApp)
|
||||
|
||||
gotApp, err = ds.GetMaintainedAppByID(ctx, maintained1.ID, &team1.ID)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, maintained1, gotApp)
|
||||
|
||||
expectedApps := []fleet.MaintainedApp{
|
||||
{
|
||||
ID: maintained1.ID,
|
||||
Name: maintained1.Name,
|
||||
Version: maintained1.Version,
|
||||
Platform: maintained1.Platform,
|
||||
},
|
||||
{
|
||||
ID: maintained2.ID,
|
||||
Name: maintained2.Name,
|
||||
Version: maintained2.Version,
|
||||
Platform: maintained2.Platform,
|
||||
},
|
||||
{
|
||||
ID: maintained3.ID,
|
||||
Name: maintained3.Name,
|
||||
Version: maintained3.Version,
|
||||
Platform: maintained3.Platform,
|
||||
},
|
||||
}
|
||||
|
||||
// We use this assertion for UpdatedAt because we only concerned with
|
||||
// its presence, not its value. We will set it to nil after asserting
|
||||
// to make the expected vs actual comparison easier.
|
||||
assertUpdatedAt := func(apps []fleet.MaintainedApp) {
|
||||
for i, app := range apps {
|
||||
require.NotNil(t, app.UpdatedAt)
|
||||
apps[i].UpdatedAt = nil
|
||||
}
|
||||
}
|
||||
|
||||
// Testing pagination
|
||||
apps, meta, err := ds.ListAvailableFleetMaintainedApps(ctx, &team1.ID, fleet.ListOptions{IncludeMetadata: true})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, apps, 3)
|
||||
require.EqualValues(t, meta.TotalResults, 3)
|
||||
assertUpdatedAt(apps)
|
||||
require.Equal(t, expectedApps, apps)
|
||||
require.False(t, meta.HasNextResults)
|
||||
|
||||
@@ -180,7 +174,6 @@ func testListAvailableApps(t *testing.T, ds *Datastore) {
|
||||
require.NoError(t, err)
|
||||
require.Len(t, apps, 1)
|
||||
require.EqualValues(t, meta.TotalResults, 3)
|
||||
assertUpdatedAt(apps)
|
||||
require.Equal(t, expectedApps[:1], apps)
|
||||
require.True(t, meta.HasNextResults)
|
||||
|
||||
@@ -188,7 +181,6 @@ func testListAvailableApps(t *testing.T, ds *Datastore) {
|
||||
require.NoError(t, err)
|
||||
require.Len(t, apps, 1)
|
||||
require.EqualValues(t, meta.TotalResults, 3)
|
||||
assertUpdatedAt(apps)
|
||||
require.Equal(t, expectedApps[1:2], apps)
|
||||
require.True(t, meta.HasNextResults)
|
||||
require.True(t, meta.HasPreviousResults)
|
||||
@@ -197,13 +189,12 @@ func testListAvailableApps(t *testing.T, ds *Datastore) {
|
||||
require.NoError(t, err)
|
||||
require.Len(t, apps, 1)
|
||||
require.EqualValues(t, meta.TotalResults, 3)
|
||||
assertUpdatedAt(apps)
|
||||
require.Equal(t, expectedApps[2:3], apps)
|
||||
require.False(t, meta.HasNextResults)
|
||||
require.True(t, meta.HasPreviousResults)
|
||||
|
||||
//
|
||||
// Test excluding results for existing apps (installers)
|
||||
// Test including software title ID for existing apps (installers)
|
||||
|
||||
/// Irrelevant package
|
||||
_, _, err = ds.MatchOrCreateSoftwareInstaller(ctx, &fleet.UploadSoftwareInstallerPayload{
|
||||
@@ -222,7 +213,6 @@ func testListAvailableApps(t *testing.T, ds *Datastore) {
|
||||
require.NoError(t, err)
|
||||
require.Len(t, apps, 3)
|
||||
require.EqualValues(t, meta.TotalResults, 3)
|
||||
assertUpdatedAt(apps)
|
||||
require.Equal(t, expectedApps, apps)
|
||||
|
||||
/// Correct package on a different team
|
||||
@@ -242,11 +232,10 @@ func testListAvailableApps(t *testing.T, ds *Datastore) {
|
||||
require.NoError(t, err)
|
||||
require.Len(t, apps, 3)
|
||||
require.EqualValues(t, meta.TotalResults, 3)
|
||||
assertUpdatedAt(apps)
|
||||
require.Equal(t, expectedApps, apps)
|
||||
|
||||
/// Correct package on the right team with the wrong platform
|
||||
_, _, err = ds.MatchOrCreateSoftwareInstaller(ctx, &fleet.UploadSoftwareInstallerPayload{
|
||||
_, titleID, err := ds.MatchOrCreateSoftwareInstaller(ctx, &fleet.UploadSoftwareInstallerPayload{
|
||||
Title: "Maintained1",
|
||||
TeamID: &team1.ID,
|
||||
InstallScript: "nothing",
|
||||
@@ -262,9 +251,12 @@ func testListAvailableApps(t *testing.T, ds *Datastore) {
|
||||
require.NoError(t, err)
|
||||
require.Len(t, apps, 3)
|
||||
require.EqualValues(t, meta.TotalResults, 3)
|
||||
assertUpdatedAt(apps)
|
||||
require.Equal(t, expectedApps, apps)
|
||||
|
||||
gotApp, err = ds.GetMaintainedAppByID(ctx, maintained1.ID, &team1.ID)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, maintained1, gotApp)
|
||||
|
||||
/// Correct team and platform
|
||||
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
|
||||
_, err := q.ExecContext(ctx, "UPDATE software_installers SET platform = ? WHERE platform = ?", fleet.MacOSPlatform, fleet.IOSPlatform)
|
||||
@@ -273,13 +265,22 @@ func testListAvailableApps(t *testing.T, ds *Datastore) {
|
||||
|
||||
apps, meta, err = ds.ListAvailableFleetMaintainedApps(ctx, &team1.ID, fleet.ListOptions{IncludeMetadata: true})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, apps, 2)
|
||||
require.EqualValues(t, meta.TotalResults, 2)
|
||||
assertUpdatedAt(apps)
|
||||
require.Equal(t, expectedApps[1:], apps)
|
||||
require.Len(t, apps, 3)
|
||||
require.EqualValues(t, meta.TotalResults, 3)
|
||||
expectedApps[0].TitleID = ptr.Uint(titleID)
|
||||
require.Equal(t, expectedApps, apps)
|
||||
|
||||
gotApp, err = ds.GetMaintainedAppByID(ctx, maintained1.ID, ptr.Uint(0))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, maintained1, gotApp)
|
||||
|
||||
gotApp, err = ds.GetMaintainedAppByID(ctx, maintained1.ID, &team1.ID)
|
||||
require.NoError(t, err)
|
||||
maintained1.TitleID = ptr.Uint(titleID)
|
||||
require.Equal(t, maintained1, gotApp)
|
||||
|
||||
//
|
||||
// Test excluding results for existing apps (VPP)
|
||||
// Test including software title ID for existing apps (VPP)
|
||||
|
||||
test.CreateInsertGlobalVPPToken(t, ds)
|
||||
|
||||
@@ -299,10 +300,9 @@ func testListAvailableApps(t *testing.T, ds *Datastore) {
|
||||
|
||||
apps, meta, err = ds.ListAvailableFleetMaintainedApps(ctx, &team1.ID, fleet.ListOptions{IncludeMetadata: true})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, apps, 2)
|
||||
require.EqualValues(t, meta.TotalResults, 2)
|
||||
assertUpdatedAt(apps)
|
||||
require.Equal(t, expectedApps[1:], apps)
|
||||
require.Len(t, apps, 3)
|
||||
require.EqualValues(t, meta.TotalResults, 3)
|
||||
require.Equal(t, expectedApps, apps)
|
||||
|
||||
// right vpp app, wrong team
|
||||
vppMaintained2 := &fleet.VPPApp{
|
||||
@@ -315,26 +315,14 @@ func testListAvailableApps(t *testing.T, ds *Datastore) {
|
||||
},
|
||||
BundleIdentifier: "fleet.maintained2",
|
||||
}
|
||||
_, err = ds.InsertVPPAppWithTeam(ctx, vppMaintained2, &team2.ID)
|
||||
vppApp, err := ds.InsertVPPAppWithTeam(ctx, vppMaintained2, &team2.ID)
|
||||
require.NoError(t, err)
|
||||
|
||||
apps, meta, err = ds.ListAvailableFleetMaintainedApps(ctx, &team1.ID, fleet.ListOptions{IncludeMetadata: true})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, apps, 2)
|
||||
require.EqualValues(t, meta.TotalResults, 2)
|
||||
assertUpdatedAt(apps)
|
||||
require.Equal(t, expectedApps[1:], apps)
|
||||
|
||||
// right vpp app, right team
|
||||
_, err = ds.InsertVPPAppWithTeam(ctx, vppMaintained2, &team1.ID)
|
||||
require.NoError(t, err)
|
||||
|
||||
apps, meta, err = ds.ListAvailableFleetMaintainedApps(ctx, &team1.ID, fleet.ListOptions{IncludeMetadata: true})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, apps, 1)
|
||||
require.EqualValues(t, meta.TotalResults, 1)
|
||||
assertUpdatedAt(apps)
|
||||
require.Equal(t, expectedApps[2:], apps)
|
||||
require.Len(t, apps, 3)
|
||||
require.EqualValues(t, meta.TotalResults, 3)
|
||||
require.Equal(t, expectedApps, apps)
|
||||
|
||||
// right app, right team, wrong platform
|
||||
vppMaintained3 := &fleet.VPPApp{
|
||||
@@ -353,38 +341,46 @@ func testListAvailableApps(t *testing.T, ds *Datastore) {
|
||||
|
||||
apps, meta, err = ds.ListAvailableFleetMaintainedApps(ctx, &team1.ID, fleet.ListOptions{IncludeMetadata: true})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, apps, 1)
|
||||
require.EqualValues(t, meta.TotalResults, 1)
|
||||
assertUpdatedAt(apps)
|
||||
require.Equal(t, expectedApps[2:], apps)
|
||||
require.Len(t, apps, 3)
|
||||
require.EqualValues(t, meta.TotalResults, 3)
|
||||
require.Equal(t, expectedApps, apps)
|
||||
|
||||
// viewing with no team selected shouldn't exclude any results
|
||||
gotApp, err = ds.GetMaintainedAppByID(ctx, maintained3.ID, &team1.ID)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, maintained3, gotApp)
|
||||
|
||||
// right vpp app, right team
|
||||
_, err = ds.InsertVPPAppWithTeam(ctx, vppMaintained2, &team1.ID)
|
||||
require.NoError(t, err)
|
||||
|
||||
apps, meta, err = ds.ListAvailableFleetMaintainedApps(ctx, &team1.ID, fleet.ListOptions{IncludeMetadata: true})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, apps, 3)
|
||||
require.EqualValues(t, meta.TotalResults, 3)
|
||||
expectedApps[1].TitleID = ptr.Uint(vppApp.TitleID)
|
||||
require.Equal(t, expectedApps, apps)
|
||||
|
||||
gotApp, err = ds.GetMaintainedAppByID(ctx, maintained2.ID, &team1.ID)
|
||||
require.NoError(t, err)
|
||||
maintained2.TitleID = ptr.Uint(vppApp.TitleID)
|
||||
require.Equal(t, maintained2, gotApp)
|
||||
|
||||
// viewing with no team selected shouldn't include any title IDs
|
||||
apps, meta, err = ds.ListAvailableFleetMaintainedApps(ctx, nil, fleet.ListOptions{IncludeMetadata: true})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, apps, 3)
|
||||
require.EqualValues(t, meta.TotalResults, 3)
|
||||
assertUpdatedAt(apps)
|
||||
expectedApps[0].TitleID = nil
|
||||
expectedApps[1].TitleID = nil
|
||||
require.Equal(t, expectedApps, apps)
|
||||
}
|
||||
|
||||
func testGetMaintainedAppByID(t *testing.T, ds *Datastore) {
|
||||
ctx := context.Background()
|
||||
|
||||
expApp, err := ds.UpsertMaintainedApp(ctx, &fleet.MaintainedApp{
|
||||
Name: "foo",
|
||||
Token: "token",
|
||||
Version: "1.0.0",
|
||||
Platform: "darwin",
|
||||
InstallerURL: "https://example.com/foo.zip",
|
||||
SHA256: "sha",
|
||||
BundleIdentifier: "bundle",
|
||||
InstallScript: "install",
|
||||
UninstallScript: "uninstall",
|
||||
})
|
||||
gotApp, err = ds.GetMaintainedAppByID(ctx, maintained1.ID, nil)
|
||||
require.NoError(t, err)
|
||||
maintained1.TitleID = nil
|
||||
require.Equal(t, maintained1, gotApp)
|
||||
|
||||
gotApp, err := ds.GetMaintainedAppByID(ctx, expApp.ID)
|
||||
gotApp, err = ds.GetMaintainedAppByID(ctx, maintained3.ID, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, expApp, gotApp)
|
||||
maintained3.TitleID = nil
|
||||
require.Equal(t, maintained3, gotApp)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user