refactor
Build and Publish / BuildAndDeployArm64 (push) Failing after 6s
Build and Publish / BuildAndDeployAmd64 (push) Failing after 4s

This commit is contained in:
2026-06-29 12:34:58 +02:00
parent d711348863
commit 46be260fcc
35 changed files with 1765 additions and 198 deletions
+109
View File
@@ -0,0 +1,109 @@
package order
import (
"bytes"
"context"
"encoding/json"
"fmt"
"net/http"
"strings"
"time"
"git.k6n.net/mats/go-cart-actor/pkg/flow"
)
type fulfillmentWebhookParams struct {
URL string `json:"url"`
Method string `json:"method,omitempty"`
Headers map[string]string `json:"headers,omitempty"`
ContentType string `json:"contentType,omitempty"`
}
// RegisterFulfillmentWebhookHook registers the "fulfillment_webhook" flow hook.
// It posts the current order + latest fulfillment to an external integration,
// with templated URL/headers so flows can target dropship or ERP adapters
// without custom Go code. Params:
//
// {
// "url": "https://example.test/hooks/{{ .OrderID }}",
// "method": "POST",
// "headers": {
// "X-Integration": "dropship",
// "Authorization": "Bearer {{ index .Vars \"token\" }}"
// }
// }
func RegisterFulfillmentWebhookHook(reg *flow.Registry, app Applier, client *http.Client) {
if client == nil {
client = &http.Client{Timeout: 10 * time.Second}
}
reg.HookWithMeta("fulfillment_webhook", func(ctx context.Context, st *flow.State, info flow.HookInfo, params json.RawMessage) error {
var p fulfillmentWebhookParams
if err := json.Unmarshal(params, &p); err != nil {
return fmt.Errorf("fulfillment_webhook: bad params: %w", err)
}
if strings.TrimSpace(p.URL) == "" {
return fmt.Errorf("fulfillment_webhook: missing url")
}
method := strings.ToUpper(strings.TrimSpace(p.Method))
if method == "" {
method = http.MethodPost
}
if p.ContentType == "" {
p.ContentType = "application/json"
}
o, err := app.Get(ctx, st.ID)
if err != nil {
return err
}
data := buildFlowTemplateData(o, st, info)
if data.Fulfillment == nil {
return fmt.Errorf("fulfillment_webhook: order %s has no fulfillment yet", data.OrderID)
}
url, err := renderTextTemplate("fulfillment-webhook-url", p.URL, data)
if err != nil {
return fmt.Errorf("fulfillment_webhook: render url: %w", err)
}
body, err := json.Marshal(map[string]any{
"orderId": data.OrderID,
"step": data.Step,
"phase": data.Phase,
"error": data.Error,
"order": data.Order,
"fulfillment": data.Fulfillment,
"billingAddress": data.BillingAddress,
"shippingAddress": data.ShippingAddress,
"vars": data.Vars,
})
if err != nil {
return fmt.Errorf("fulfillment_webhook: encode body: %w", err)
}
req, err := http.NewRequestWithContext(ctx, method, url, bytes.NewReader(body))
if err != nil {
return fmt.Errorf("fulfillment_webhook: build request: %w", err)
}
req.Header.Set("Content-Type", p.ContentType)
for key, raw := range p.Headers {
value, err := renderTextTemplate("fulfillment-webhook-header-"+key, raw, data)
if err != nil {
return fmt.Errorf("fulfillment_webhook: render header %s: %w", key, err)
}
req.Header.Set(key, value)
}
resp, err := client.Do(req)
if err != nil {
return fmt.Errorf("fulfillment_webhook: post %s: %w", url, err)
}
defer resp.Body.Close()
if resp.StatusCode >= 300 {
return fmt.Errorf("fulfillment_webhook: %s returned %d", url, resp.StatusCode)
}
return nil
}, flow.CapabilityMeta{
Description: "POST the order and latest fulfillment to an external dropship or ERP endpoint.",
ExampleParams: json.RawMessage(`{"url":"https://dropship.example/hooks/{{ .OrderID }}","method":"POST","headers":{"X-Integration":"dropship {{ .Order.OrderReference }}","Authorization":"Bearer {{ index .Vars \"token\" }}"}}`),
})
}