Files
silo-server/internal/autoscan/provider.go
QuickandGitHub f9bb94a299 [codex] Fix autoscan plugin bindings and poll status (#75)
* fix(autoscan): bind sources by plugin id

* fix(autoscan): skip overlapping source polls
2026-06-07 17:18:55 -04:00

84 lines
3.1 KiB
Go

package autoscan
import (
"context"
pluginv1 "github.com/Silo-Server/silo-plugin-sdk/pkg/pluginproto/silo/plugin/v1"
)
// ScanSourceProvider yields changed paths for one source. The engine calls
// PollChanges; production wraps the plugins.Service scan_source resolver.
type ScanSourceProvider interface {
PollChanges(ctx context.Context, pluginID, capabilityID, marker string, conn ResolvedConnection, sourceConfig map[string]string) (changes []Change, nextMarker string, err error)
}
// PollChangesClient is the slice of *pluginhost.ScanSourceClient used here. It
// is exported so wiring in the api package can declare an adapter whose
// ScanSourceClient returns it (Go interface method signatures must match
// exactly across packages, and the unexported form could not be named there).
type PollChangesClient interface {
PollChanges(ctx context.Context, req *pluginv1.PollChangesRequest) (*pluginv1.PollChangesResponse, error)
}
// ScanSourceResolver yields a per-(plugin, capability) scan-source client.
// Exported for the same cross-package adapter reason as PollChangesClient.
type ScanSourceResolver interface {
ScanSourceClient(ctx context.Context, pluginID, capabilityID string) (PollChangesClient, error)
}
type pluginProvider struct{ resolver ScanSourceResolver }
// NewPluginProvider builds the production scan-source provider over the plugins
// resolver.
func NewPluginProvider(resolver ScanSourceResolver) ScanSourceProvider {
return &pluginProvider{resolver: resolver}
}
func (p *pluginProvider) PollChanges(ctx context.Context, pluginID, capabilityID, marker string, conn ResolvedConnection, sourceConfig map[string]string) ([]Change, string, error) {
client, err := p.resolver.ScanSourceClient(ctx, pluginID, capabilityID)
if err != nil {
return nil, "", err
}
resp, err := client.PollChanges(ctx, &pluginv1.PollChangesRequest{
CapabilityId: capabilityID,
Marker: marker,
Connection: &pluginv1.ResolvedConnection{BaseUrl: conn.BaseURL, ApiKey: conn.APIKey},
SourceConfig: sourceConfig,
})
if err != nil {
return nil, "", err
}
if structured := resp.GetChanges(); len(structured) > 0 {
changes := make([]Change, 0, len(structured))
for _, change := range structured {
if change == nil {
continue
}
changes = append(changes, Change{
SourcePath: change.GetSourcePath(),
Scope: scanSourceScope(change.GetScope()),
})
}
return changes, resp.GetNextMarker(), nil
}
// Legacy plugins return only source_paths. Treat them as auto/file-like
// paths so the existing parent-directory collapse behavior is preserved.
changes := make([]Change, 0, len(resp.GetSourcePaths()))
for _, path := range resp.GetSourcePaths() {
changes = append(changes, Change{SourcePath: path, Scope: ChangeScopeAuto})
}
return changes, resp.GetNextMarker(), nil
}
func scanSourceScope(scope pluginv1.ScanSourceChangeScope) ChangeScope {
switch scope {
case pluginv1.ScanSourceChangeScope_SCAN_SOURCE_CHANGE_SCOPE_FILE:
return ChangeScopeFile
case pluginv1.ScanSourceChangeScope_SCAN_SOURCE_CHANGE_SCOPE_SUBTREE:
return ChangeScopeSubtree
default:
return ChangeScopeAuto
}
}