Files
sendico/api/payments/storage/model/gateway_affinity.go
2026-02-18 20:38:08 +01:00

145 lines
3.4 KiB
Go

package model
import "strings"
// SelectGatewayByPreference picks a gateway candidate using persisted affinity hints.
// Matching order:
// 1) gateway ID + instance ID
// 2) invoke URI
// 3) gateway ID
// 4) instance ID
// 5) first candidate fallback
func SelectGatewayByPreference(
candidates []*GatewayInstanceDescriptor,
gatewayID string,
instanceID string,
invokeURI string,
) (*GatewayInstanceDescriptor, string) {
if len(candidates) == 0 {
return nil, ""
}
gatewayID = strings.TrimSpace(gatewayID)
instanceID = strings.TrimSpace(instanceID)
invokeURI = strings.TrimSpace(invokeURI)
if gatewayID != "" && instanceID != "" {
for _, entry := range candidates {
if entry == nil {
continue
}
if strings.EqualFold(strings.TrimSpace(entry.ID), gatewayID) &&
strings.EqualFold(strings.TrimSpace(entry.InstanceID), instanceID) {
return entry, "exact"
}
}
}
if invokeURI != "" {
for _, entry := range candidates {
if entry == nil {
continue
}
if strings.EqualFold(strings.TrimSpace(entry.InvokeURI), invokeURI) {
return entry, "invoke_uri"
}
}
}
if gatewayID != "" {
for _, entry := range candidates {
if entry == nil {
continue
}
if strings.EqualFold(strings.TrimSpace(entry.ID), gatewayID) {
return entry, "gateway_id"
}
}
}
if instanceID != "" {
for _, entry := range candidates {
if entry == nil {
continue
}
if strings.EqualFold(strings.TrimSpace(entry.InstanceID), instanceID) {
return entry, "instance_id"
}
}
}
for _, entry := range candidates {
if entry != nil {
return entry, "rail_fallback"
}
}
return nil, ""
}
// GatewayDescriptorIdentityKey returns a stable dedupe key for gateway entries.
// The key is composed from logical gateway ID + instance identity; invoke URI is
// used as a fallback identity when instance ID is missing.
func GatewayDescriptorIdentityKey(entry *GatewayInstanceDescriptor) string {
if entry == nil {
return ""
}
return GatewayIdentityKey(entry.ID, entry.InstanceID, entry.InvokeURI)
}
// GatewayIdentityKey composes a stable identity key from gateway affinity fields.
func GatewayIdentityKey(gatewayID string, instanceID string, invokeURI string) string {
id := strings.ToLower(strings.TrimSpace(gatewayID))
if id == "" {
return ""
}
instance := strings.ToLower(strings.TrimSpace(instanceID))
if instance == "" {
instance = strings.ToLower(strings.TrimSpace(invokeURI))
}
if instance == "" {
return id
}
return id + "|" + instance
}
// LessGatewayDescriptor orders gateway descriptors deterministically.
func LessGatewayDescriptor(left *GatewayInstanceDescriptor, right *GatewayInstanceDescriptor) bool {
if left == nil {
return right != nil
}
if right == nil {
return false
}
if cmp := compareFolded(left.ID, right.ID); cmp != 0 {
return cmp < 0
}
if cmp := compareFolded(left.InstanceID, right.InstanceID); cmp != 0 {
return cmp < 0
}
if cmp := compareFolded(left.InvokeURI, right.InvokeURI); cmp != 0 {
return cmp < 0
}
if cmp := compareFolded(string(left.Rail), string(right.Rail)); cmp != 0 {
return cmp < 0
}
if cmp := compareFolded(left.Network, right.Network); cmp != 0 {
return cmp < 0
}
return compareFolded(left.Version, right.Version) < 0
}
func compareFolded(left string, right string) int {
l := strings.ToLower(strings.TrimSpace(left))
r := strings.ToLower(strings.TrimSpace(right))
switch {
case l < r:
return -1
case l > r:
return 1
default:
return 0
}
}