Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 17 additions & 4 deletions pkg/services/teams-workflows.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,10 @@ func (n *TeamsWorkflowsNotification) GetTemplater(name string, f texttemplate.Fu
type TeamsWorkflowsOptions struct {
RecipientUrls map[string]string `json:"recipientUrls"`
InsecureSkipVerify bool `json:"insecureSkipVerify"`
// RawCardPayload sends the AdaptiveCard JSON directly as the POST body,
// skipping the "type":"message" + "attachments" envelope. Use this for
// webhook endpoints that expect the card at the root level.
RawCardPayload bool `json:"rawCardPayload"`
httputil.TransportOptions
}

Expand Down Expand Up @@ -223,7 +227,7 @@ func (s teamsWorkflowsService) Send(notification Notification, dest Destination)
}

// Generate message payload
message, err := teamsWorkflowsNotificationToReader(notification)
message, err := teamsWorkflowsNotificationToReader(notification, s.opts.RawCardPayload)
if err != nil {
return fmt.Errorf("failed to generate message payload for teams-workflows: %w", err)
}
Expand Down Expand Up @@ -484,17 +488,21 @@ func buildAdaptiveCard(data *notificationData) *adaptiveCard {
return card
}

func teamsWorkflowsNotificationToReader(n Notification) ([]byte, error) {
func teamsWorkflowsNotificationToReader(n Notification, rawCardPayload bool) ([]byte, error) {
// Check if a custom AdaptiveCard template is provided
if n.TeamsWorkflows != nil && n.TeamsWorkflows.AdaptiveCard != "" {
// Use the custom AdaptiveCard template directly, wrapped in message envelope.
// Use the custom AdaptiveCard template directly.
// Keep it as raw JSON so custom cards are not lossy-converted through limited structs.
cardBytes := bytes.TrimSpace([]byte(n.TeamsWorkflows.AdaptiveCard))
var raw json.RawMessage
if err := json.Unmarshal(cardBytes, &raw); err != nil {
return nil, fmt.Errorf("teams-workflows adaptiveCard unmarshalling error %w", err)
}

if rawCardPayload {
return raw, nil
}

payload := adaptiveMessage{
Type: "message",
Attachments: []adaptiveAttachment{
Expand All @@ -513,12 +521,17 @@ func teamsWorkflowsNotificationToReader(n Notification) ([]byte, error) {
return nil, err
}

// Build AdaptiveCard and wrap it in the message envelope
// Build AdaptiveCard
card := buildAdaptiveCard(data)
cardBytes, err := json.Marshal(card)
if err != nil {
return nil, fmt.Errorf("teams-workflows adaptive card marshalling error %w", err)
}

if rawCardPayload {
return cardBytes, nil
}

payload := adaptiveMessage{
Type: "message",
Attachments: []adaptiveAttachment{
Expand Down
70 changes: 70 additions & 0 deletions pkg/services/teams-workflows_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -938,3 +938,73 @@ func TestTeamsWorkflows_ActionNonOpenUri(t *testing.T) {
// Non-OpenUri actions should not be converted
assert.Empty(t, card.Actions)
}

func TestTeamsWorkflows_RawCardPayload_BuiltIn(t *testing.T) {
var receivedBody []byte
server := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
data, err := io.ReadAll(request.Body)
require.NoError(t, err)
receivedBody = data
writer.WriteHeader(http.StatusOK)
}))
defer server.Close()

webhookURL := server.URL + "/powerautomate/webhook"

service := NewTeamsWorkflowsService(TeamsWorkflowsOptions{
RecipientUrls: map[string]string{
"test": webhookURL,
},
RawCardPayload: true,
})

notification := Notification{
TeamsWorkflows: &TeamsWorkflowsNotification{
Title: "Test Title",
Text: "Test message body",
},
}

err := service.Send(notification, Destination{Recipient: "test", Service: "teams-workflows"})
require.NoError(t, err)

var root map[string]any
require.NoError(t, json.Unmarshal(receivedBody, &root))
assert.Equal(t, "AdaptiveCard", root["type"])
assert.NotContains(t, root, "attachments")
}

func TestTeamsWorkflows_RawCardPayload_CustomCard(t *testing.T) {
var receivedBody []byte
server := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
data, err := io.ReadAll(request.Body)
require.NoError(t, err)
receivedBody = data
writer.WriteHeader(http.StatusOK)
}))
defer server.Close()

webhookURL := server.URL + "/powerautomate/webhook"

service := NewTeamsWorkflowsService(TeamsWorkflowsOptions{
RecipientUrls: map[string]string{
"test": webhookURL,
},
RawCardPayload: true,
})

notification := Notification{
TeamsWorkflows: &TeamsWorkflowsNotification{
AdaptiveCard: `{"type":"AdaptiveCard","version":"1.4","body":[{"type":"TextBlock","text":"hello"}]}`,
},
}

err := service.Send(notification, Destination{Recipient: "test", Service: "teams-workflows"})
require.NoError(t, err)

var root map[string]any
require.NoError(t, json.Unmarshal(receivedBody, &root))
assert.Equal(t, "AdaptiveCard", root["type"])
assert.Equal(t, "1.4", root["version"])
assert.NotContains(t, root, "attachments")
}
Loading