package sqlite import ( "context" "fmt" "strings" ) type Host struct { ID int64 HostID string BaseURL string HostVersion string CapabilityProbeJSON string AuthType string AuthToken string } type HostsRepo struct { db execQuerier } func newHostsRepo(db execQuerier) *HostsRepo { return &HostsRepo{db: db} } func (r *HostsRepo) GetByID(ctx context.Context, id int64) (Host, error) { if id <= 0 { return Host{}, fmt.Errorf("id is required") } var host Host if err := r.db.QueryRowContext(ctx, `SELECT id, host_id, base_url, host_version, capability_probe_json, auth_type, auth_token FROM hosts WHERE id = ?`, id).Scan(&host.ID, &host.HostID, &host.BaseURL, &host.HostVersion, &host.CapabilityProbeJSON, &host.AuthType, &host.AuthToken); err != nil { return Host{}, err } return host, nil } func (r *HostsRepo) GetByHostID(ctx context.Context, hostID string) (Host, error) { hostID = strings.TrimSpace(hostID) if hostID == "" { return Host{}, fmt.Errorf("host_id is required") } var host Host if err := r.db.QueryRowContext(ctx, `SELECT id, host_id, base_url, host_version, capability_probe_json, auth_type, auth_token FROM hosts WHERE host_id = ?`, hostID).Scan(&host.ID, &host.HostID, &host.BaseURL, &host.HostVersion, &host.CapabilityProbeJSON, &host.AuthType, &host.AuthToken); err != nil { return Host{}, err } return host, nil } func (r *HostsRepo) GetByBaseURL(ctx context.Context, baseURL string) (Host, error) { baseURL = strings.TrimSpace(baseURL) if baseURL == "" { return Host{}, fmt.Errorf("base_url is required") } var host Host if err := r.db.QueryRowContext(ctx, `SELECT id, host_id, base_url, host_version, capability_probe_json, auth_type, auth_token FROM hosts WHERE base_url = ?`, baseURL).Scan(&host.ID, &host.HostID, &host.BaseURL, &host.HostVersion, &host.CapabilityProbeJSON, &host.AuthType, &host.AuthToken); err != nil { return Host{}, err } return host, nil } func (r *HostsRepo) Create(ctx context.Context, host Host) (int64, error) { hostID := strings.TrimSpace(host.HostID) baseURL := strings.TrimSpace(host.BaseURL) hostVersion := strings.TrimSpace(host.HostVersion) capabilityProbeJSON := strings.TrimSpace(host.CapabilityProbeJSON) authType := firstNonEmptyTrimmed(host.AuthType, "apikey") authToken := strings.TrimSpace(host.AuthToken) switch { case hostID == "": return 0, fmt.Errorf("host_id is required") case baseURL == "": return 0, fmt.Errorf("base_url is required") case hostVersion == "": return 0, fmt.Errorf("host_version is required") case capabilityProbeJSON == "": capabilityProbeJSON = "{}" } result, err := r.db.ExecContext( ctx, `INSERT INTO hosts (host_id, base_url, host_version, capability_probe_json, auth_type, auth_token) VALUES (?, ?, ?, ?, ?, ?)`, hostID, baseURL, hostVersion, capabilityProbeJSON, authType, authToken, ) if err != nil { return 0, fmt.Errorf("insert host %q: %w", hostID, err) } id, err := result.LastInsertId() if err != nil { return 0, fmt.Errorf("read inserted host id for %q: %w", hostID, err) } return id, nil } func (r *HostsRepo) UpdateConnectionByHostID(ctx context.Context, hostID, baseURL, hostVersion, capabilityProbeJSON, authType, authToken string) error { hostID = strings.TrimSpace(hostID) baseURL = strings.TrimSpace(baseURL) hostVersion = strings.TrimSpace(hostVersion) capabilityProbeJSON = strings.TrimSpace(capabilityProbeJSON) authType = firstNonEmptyTrimmed(authType, "apikey") authToken = strings.TrimSpace(authToken) if hostID == "" { return fmt.Errorf("host_id is required") } if baseURL == "" { return fmt.Errorf("base_url is required") } if hostVersion == "" { return fmt.Errorf("host_version is required") } if capabilityProbeJSON == "" { capabilityProbeJSON = "{}" } result, err := r.db.ExecContext(ctx, `UPDATE hosts SET base_url = ?, host_version = ?, capability_probe_json = ?, auth_type = ?, auth_token = ? WHERE host_id = ?`, baseURL, hostVersion, capabilityProbeJSON, authType, authToken, hostID) if err != nil { return fmt.Errorf("update host %q connection: %w", hostID, err) } rows, err := result.RowsAffected() if err != nil { return err } if rows == 0 { return fmt.Errorf("host %q not found", hostID) } return nil } func (r *HostsRepo) ListAll(ctx context.Context) ([]Host, error) { rows, err := r.db.QueryContext(ctx, `SELECT id, host_id, base_url, host_version, capability_probe_json, auth_type, auth_token FROM hosts ORDER BY id`) if err != nil { return nil, fmt.Errorf("list hosts: %w", err) } defer rows.Close() var hosts []Host for rows.Next() { var host Host if err := rows.Scan(&host.ID, &host.HostID, &host.BaseURL, &host.HostVersion, &host.CapabilityProbeJSON, &host.AuthType, &host.AuthToken); err != nil { return nil, fmt.Errorf("scan host: %w", err) } hosts = append(hosts, host) } if err := rows.Err(); err != nil { return nil, fmt.Errorf("iterate hosts: %w", err) } return hosts, nil } func (r *HostsRepo) DeleteByHostID(ctx context.Context, hostID string) error { hostID = strings.TrimSpace(hostID) if hostID == "" { return fmt.Errorf("host_id is required") } result, err := r.db.ExecContext(ctx, `DELETE FROM hosts WHERE host_id = ?`, hostID) if err != nil { return fmt.Errorf("delete host %q: %w", hostID, err) } rows, err := result.RowsAffected() if err != nil { return err } if rows == 0 { return fmt.Errorf("host %q not found", hostID) } return nil } func (r *HostsRepo) UpdateProbeByHostID(ctx context.Context, hostID, hostVersion, capabilityProbeJSON string) error { hostID = strings.TrimSpace(hostID) hostVersion = strings.TrimSpace(hostVersion) capabilityProbeJSON = strings.TrimSpace(capabilityProbeJSON) if hostID == "" { return fmt.Errorf("host_id is required") } if hostVersion == "" { return fmt.Errorf("host_version is required") } if capabilityProbeJSON == "" { capabilityProbeJSON = "{}" } result, err := r.db.ExecContext(ctx, `UPDATE hosts SET host_version = ?, capability_probe_json = ? WHERE host_id = ?`, hostVersion, capabilityProbeJSON, hostID) if err != nil { return fmt.Errorf("update host %q probe: %w", hostID, err) } rows, err := result.RowsAffected() if err != nil { return err } if rows == 0 { return fmt.Errorf("host %q not found", hostID) } return nil } func firstNonEmptyTrimmed(values ...string) string { for _, value := range values { if trimmed := strings.TrimSpace(value); trimmed != "" { return trimmed } } return "" }