- add batch-scoped reconcile_runs persistence and queries - route batch detail and reconcile writes through batch_id/host_id - refresh production boards with host-scope acceptance artifacts - include latest real-host acceptance evidence for self_service and subscription
207 lines
4.5 KiB
Go
207 lines
4.5 KiB
Go
package sub2api
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
func decodeFlexibleID(raw json.RawMessage) (string, error) {
|
|
if len(raw) == 0 || bytes.Equal(raw, []byte("null")) {
|
|
return "", nil
|
|
}
|
|
|
|
var asString string
|
|
if err := json.Unmarshal(raw, &asString); err == nil {
|
|
return asString, nil
|
|
}
|
|
|
|
decoder := json.NewDecoder(bytes.NewReader(raw))
|
|
decoder.UseNumber()
|
|
var asNumber json.Number
|
|
if err := decoder.Decode(&asNumber); err == nil {
|
|
return asNumber.String(), nil
|
|
}
|
|
|
|
return "", fmt.Errorf("unsupported id payload: %s", string(raw))
|
|
}
|
|
|
|
func flexibleIDValue(raw string) any {
|
|
trimmed := strings.TrimSpace(raw)
|
|
if trimmed == "" {
|
|
return ""
|
|
}
|
|
if id, err := strconv.ParseInt(trimmed, 10, 64); err == nil {
|
|
return id
|
|
}
|
|
return trimmed
|
|
}
|
|
|
|
func flexibleIDSliceValues(raw []string) []any {
|
|
values := make([]any, 0, len(raw))
|
|
for _, item := range raw {
|
|
values = append(values, flexibleIDValue(item))
|
|
}
|
|
return values
|
|
}
|
|
|
|
func (r CreateChannelRequest) MarshalJSON() ([]byte, error) {
|
|
return json.Marshal(struct {
|
|
Name string `json:"name"`
|
|
GroupIDs []any `json:"group_ids"`
|
|
}{
|
|
Name: r.Name,
|
|
GroupIDs: flexibleIDSliceValues(r.GroupIDs),
|
|
})
|
|
}
|
|
|
|
func (r CreatePlanRequest) MarshalJSON() ([]byte, error) {
|
|
return json.Marshal(struct {
|
|
GroupID any `json:"group_id"`
|
|
Name string `json:"name"`
|
|
Price float64 `json:"price"`
|
|
ValidityDays int `json:"validity_days"`
|
|
ValidityUnit string `json:"validity_unit"`
|
|
}{
|
|
GroupID: flexibleIDValue(r.GroupID),
|
|
Name: r.Name,
|
|
Price: r.Price,
|
|
ValidityDays: r.ValidityDays,
|
|
ValidityUnit: r.ValidityUnit,
|
|
})
|
|
}
|
|
|
|
func (r CreateAccountRequest) MarshalJSON() ([]byte, error) {
|
|
return json.Marshal(struct {
|
|
Name string `json:"name"`
|
|
Platform string `json:"platform"`
|
|
Type string `json:"type"`
|
|
Credentials map[string]any `json:"credentials"`
|
|
GroupIDs []any `json:"group_ids"`
|
|
}{
|
|
Name: r.Name,
|
|
Platform: r.Platform,
|
|
Type: r.Type,
|
|
Credentials: r.Credentials,
|
|
GroupIDs: flexibleIDSliceValues(r.GroupIDs),
|
|
})
|
|
}
|
|
|
|
func (r AssignSubscriptionRequest) MarshalJSON() ([]byte, error) {
|
|
return json.Marshal(struct {
|
|
UserID any `json:"user_id"`
|
|
GroupID any `json:"group_id"`
|
|
DurationDays int `json:"validity_days,omitempty"`
|
|
}{
|
|
UserID: flexibleIDValue(r.UserID),
|
|
GroupID: flexibleIDValue(r.GroupID),
|
|
DurationDays: r.DurationDays,
|
|
})
|
|
}
|
|
|
|
func (r *NamedResource) UnmarshalJSON(data []byte) error {
|
|
var aux struct {
|
|
ID json.RawMessage `json:"id"`
|
|
Name string `json:"name"`
|
|
}
|
|
if err := json.Unmarshal(data, &aux); err != nil {
|
|
return err
|
|
}
|
|
id, err := decodeFlexibleID(aux.ID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
r.ID = id
|
|
r.Name = aux.Name
|
|
return nil
|
|
}
|
|
|
|
func (r *GroupRef) UnmarshalJSON(data []byte) error {
|
|
var aux struct {
|
|
ID json.RawMessage `json:"id"`
|
|
Name string `json:"name"`
|
|
}
|
|
if err := json.Unmarshal(data, &aux); err != nil {
|
|
return err
|
|
}
|
|
id, err := decodeFlexibleID(aux.ID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
r.ID = id
|
|
r.Name = aux.Name
|
|
return nil
|
|
}
|
|
|
|
func (r *ChannelRef) UnmarshalJSON(data []byte) error {
|
|
var aux struct {
|
|
ID json.RawMessage `json:"id"`
|
|
Name string `json:"name"`
|
|
}
|
|
if err := json.Unmarshal(data, &aux); err != nil {
|
|
return err
|
|
}
|
|
id, err := decodeFlexibleID(aux.ID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
r.ID = id
|
|
r.Name = aux.Name
|
|
return nil
|
|
}
|
|
|
|
func (r *PlanRef) UnmarshalJSON(data []byte) error {
|
|
var aux struct {
|
|
ID json.RawMessage `json:"id"`
|
|
Name string `json:"name"`
|
|
}
|
|
if err := json.Unmarshal(data, &aux); err != nil {
|
|
return err
|
|
}
|
|
id, err := decodeFlexibleID(aux.ID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
r.ID = id
|
|
r.Name = aux.Name
|
|
return nil
|
|
}
|
|
|
|
func (r *AccountRef) UnmarshalJSON(data []byte) error {
|
|
var aux struct {
|
|
ID json.RawMessage `json:"id"`
|
|
Name string `json:"name,omitempty"`
|
|
Platform string `json:"platform,omitempty"`
|
|
Type string `json:"type,omitempty"`
|
|
}
|
|
if err := json.Unmarshal(data, &aux); err != nil {
|
|
return err
|
|
}
|
|
id, err := decodeFlexibleID(aux.ID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
r.ID = id
|
|
r.Name = aux.Name
|
|
r.Platform = aux.Platform
|
|
r.Type = aux.Type
|
|
return nil
|
|
}
|
|
|
|
func (r *SubscriptionRef) UnmarshalJSON(data []byte) error {
|
|
var aux struct {
|
|
ID json.RawMessage `json:"id"`
|
|
}
|
|
if err := json.Unmarshal(data, &aux); err != nil {
|
|
return err
|
|
}
|
|
id, err := decodeFlexibleID(aux.ID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
r.ID = id
|
|
return nil
|
|
}
|