fleet/server/acl/activityacl/fleet_adapter.go
Victor Lyuboslavsky 61f635dd44
Activity bounded context: Complete read operations (#38555)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #38534

moved `/api/_version_/fleet/hosts/{id:[0-9]+}/activities` endpoint and
`MarkActivitiesAsStreamed` to activity bounded context

# Checklist for submitter

- [x] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.

## 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

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Added new endpoint to retrieve host-specific past activities with
pagination metadata.
  
* **Refactor**
* Refactored activity service architecture and authorization layer to
improve data provider integration and activity streaming capabilities.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-02-09 15:29:12 -06:00

96 lines
2.5 KiB
Go

// Package activityacl provides the anti-corruption layer between the activity
// bounded context and legacy Fleet code.
//
// This package is the ONLY place that imports both activity types and fleet types.
// It translates between them, allowing the activity context to remain decoupled
// from legacy code.
package activityacl
import (
"context"
"github.com/fleetdm/fleet/v4/server/activity"
"github.com/fleetdm/fleet/v4/server/fleet"
)
// FleetServiceAdapter provides access to Fleet service methods
// for data that the activity bounded context doesn't own.
type FleetServiceAdapter struct {
svc fleet.LookupService
}
// NewFleetServiceAdapter creates a new adapter for the Fleet service.
func NewFleetServiceAdapter(svc fleet.LookupService) *FleetServiceAdapter {
return &FleetServiceAdapter{svc: svc}
}
// Ensure FleetServiceAdapter implements activity.UserProvider and activity.HostProvider
var (
_ activity.UserProvider = (*FleetServiceAdapter)(nil)
_ activity.HostProvider = (*FleetServiceAdapter)(nil)
)
// UsersByIDs fetches users by their IDs from the Fleet service.
func (a *FleetServiceAdapter) UsersByIDs(ctx context.Context, ids []uint) ([]*activity.User, error) {
if len(ids) == 0 {
return nil, nil
}
// Fetch only the requested users by their IDs
users, err := a.svc.UsersByIDs(ctx, ids)
if err != nil {
return nil, err
}
// Convert to activity.User
result := make([]*activity.User, 0, len(users))
for _, u := range users {
result = append(result, convertUser(u))
}
return result, nil
}
// FindUserIDs searches for users by name/email prefix and returns their IDs.
func (a *FleetServiceAdapter) FindUserIDs(ctx context.Context, query string) ([]uint, error) {
if query == "" {
return nil, nil
}
// Search users via Fleet service with the query
users, err := a.svc.ListUsers(ctx, fleet.UserListOptions{
ListOptions: fleet.ListOptions{
MatchQuery: query,
},
})
if err != nil {
return nil, err
}
ids := make([]uint, 0, len(users))
for _, u := range users {
ids = append(ids, u.ID)
}
return ids, nil
}
// GetHostLite fetches minimal host information for authorization.
func (a *FleetServiceAdapter) GetHostLite(ctx context.Context, hostID uint) (*activity.Host, error) {
host, err := a.svc.GetHostLite(ctx, hostID)
if err != nil {
return nil, err
}
return &activity.Host{
ID: host.ID,
TeamID: host.TeamID,
}, nil
}
func convertUser(u *fleet.UserSummary) *activity.User {
return &activity.User{
ID: u.ID,
Name: u.Name,
Email: u.Email,
Gravatar: u.GravatarURL,
APIOnly: u.APIOnly,
}
}