Marvin Preuss
d095180eb4
All checks were successful
continuous-integration/drone/push Build is passing
694 lines
25 KiB
Go
694 lines
25 KiB
Go
// Copyright 2020 Enrico Hoffmann
|
|
// Copyright 2021 Adam Chalkley
|
|
//
|
|
// https://github.com/atc0005/go-teams-notify
|
|
//
|
|
// Licensed under the MIT License. See LICENSE file in the project root for
|
|
// full license information.
|
|
|
|
package goteamsnotify
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
const (
|
|
// PotentialActionOpenURIType is the type that must be used for OpenUri
|
|
// potential action.
|
|
PotentialActionOpenURIType = "OpenUri"
|
|
|
|
// PotentialActionHTTPPostType is the type that must be used for HttpPOST
|
|
// potential action.
|
|
PotentialActionHTTPPostType = "HttpPOST"
|
|
|
|
// PotentialActionActionCardType is the type that must be used for
|
|
// ActionCard potential action.
|
|
PotentialActionActionCardType = "ActionCard"
|
|
|
|
// PotentialActionInvokeAddInCommandType is the type that must be used for
|
|
// InvokeAddInCommand potential action.
|
|
PotentialActionInvokeAddInCommandType = "InvokeAddInCommand"
|
|
|
|
// PotentialActionActionCardInputTextInputType is the type that must be
|
|
// used for ActionCard TextInput type.
|
|
PotentialActionActionCardInputTextInputType = "TextInput"
|
|
|
|
// PotentialActionActionCardInputDateInputType is the type that must be
|
|
// used for ActionCard DateInput type.
|
|
PotentialActionActionCardInputDateInputType = "DateInput"
|
|
|
|
// PotentialActionActionCardInputMultichoiceInput is the type that must be
|
|
// used for ActionCard MultichoiceInput type.
|
|
PotentialActionActionCardInputMultichoiceInput = "MultichoiceInput"
|
|
)
|
|
|
|
// PotentialActionMaxSupported is the maximum number of actions allowed in a
|
|
// MessageCardPotentialAction collection.
|
|
// https://docs.microsoft.com/en-us/outlook/actionable-messages/message-card-reference#actions
|
|
const PotentialActionMaxSupported = 4
|
|
|
|
// ErrPotentialActionsLimitReached indicates that the maximum supported number
|
|
// of potentialAction collection values has been reached for either a
|
|
// MessageCard or a MessageCardSection.
|
|
var ErrPotentialActionsLimitReached = errors.New("potential actions collection limit reached")
|
|
|
|
// MessageCardPotentialAction represents potential actions an user can do in a
|
|
// message card. See
|
|
// https://docs.microsoft.com/en-us/outlook/actionable-messages/message-card-reference#actions
|
|
// for more information.
|
|
type MessageCardPotentialAction struct {
|
|
// Type of the potential action. Can be OpenUri, HttpPOST, ActionCard or
|
|
// InvokeAddInCommand.
|
|
Type string `json:"@type"`
|
|
|
|
// Name property defines the text that will be displayed on screen for the
|
|
// action.
|
|
Name string `json:"name"`
|
|
|
|
// MessageCardPotentialActionOpenURI is a set of options for openUri
|
|
// potential action.
|
|
MessageCardPotentialActionOpenURI
|
|
|
|
// MessageCardPotentialActionHTTPPOST is a set of options for httpPOST
|
|
// potential action.
|
|
MessageCardPotentialActionHTTPPOST
|
|
|
|
// MessageCardPotentialActionActionCard is a set of options for actionCard
|
|
// potential action.
|
|
MessageCardPotentialActionActionCard
|
|
|
|
// MessageCardPotentialActionInvokeAddInCommand is a set of options for
|
|
// invokeAddInCommand potential action.
|
|
MessageCardPotentialActionInvokeAddInCommand
|
|
}
|
|
|
|
// MessageCardPotentialActionOpenURI represents a OpenUri potential action.
|
|
type MessageCardPotentialActionOpenURI struct {
|
|
// Targets is a collection of name/value pairs that defines one URI per
|
|
// target operating system. Only used for OpenUri action type.
|
|
Targets []MessageCardPotentialActionOpenURITarget `json:"targets,omitempty"`
|
|
}
|
|
|
|
// MessageCardPotentialActionHTTPPOST represents a HttpPOST potential action.
|
|
type MessageCardPotentialActionHTTPPOST struct {
|
|
// Target defines the URL endpoint of the service that implements the
|
|
// action. Only used for HttpPOST action type.
|
|
Target string `json:"target,omitempty"`
|
|
|
|
// Headers is a collection of MessageCardPotentialActionHeader objects
|
|
// representing a set of HTTP headers that will be emitted when sending
|
|
// the POST request to the target URL. Only used for HttpPOST action type.
|
|
Headers []MessageCardPotentialActionHTTPPOSTHeader `json:"headers,omitempty"`
|
|
|
|
// Body is the body of the POST request. Only used for HttpPOST action
|
|
// type.
|
|
Body string `json:"body,omitempty"`
|
|
|
|
// BodyContentType is optional and specifies the MIME type of the body in
|
|
// the POST request. Only used for HttpPOST action type.
|
|
BodyContentType string `json:"bodyContentType,omitempty"`
|
|
}
|
|
|
|
// MessageCardPotentialActionActionCard represents an actionCard potential
|
|
// action.
|
|
type MessageCardPotentialActionActionCard struct {
|
|
// Inputs is a collection of inputs an user can provide before processing
|
|
// the actions. Only used for ActionCard action type. Three types of
|
|
// inputs are available: TextInput, DateInput and MultichoiceInput
|
|
Inputs []MessageCardPotentialActionActionCardInput `json:"inputs,omitempty"`
|
|
|
|
// Actions are the available actions. Only used for ActionCard action
|
|
// type.
|
|
Actions []MessageCardPotentialActionActionCardAction `json:"actions,omitempty"`
|
|
}
|
|
|
|
// MessageCardPotentialActionActionCardAction is used for configuring ActionCard actions
|
|
type MessageCardPotentialActionActionCardAction struct {
|
|
// Type of the action. Can be OpenUri, HttpPOST, ActionCard or
|
|
// InvokeAddInCommand.
|
|
Type string `json:"@type"`
|
|
|
|
// Name property defines the text that will be displayed on screen for the
|
|
// action.
|
|
Name string `json:"name"`
|
|
|
|
// MessageCardPotentialActionOpenURI is used to specify a openUri action
|
|
// card's action.
|
|
MessageCardPotentialActionOpenURI
|
|
|
|
// MessageCardPotentialActionHTTPPOST is used to specify a httpPOST action
|
|
// card's action.
|
|
MessageCardPotentialActionHTTPPOST
|
|
}
|
|
|
|
// MessageCardPotentialActionInvokeAddInCommand represents an
|
|
// invokeAddInCommand potential action.
|
|
type MessageCardPotentialActionInvokeAddInCommand struct {
|
|
// AddInID specifies the add-in ID of the required add-in. Only used for
|
|
// InvokeAddInCommand action type.
|
|
AddInID string `json:"addInId,omitempty"`
|
|
|
|
// DesktopCommandID specifies the ID of the add-in command button that
|
|
// opens the required task pane. Only used for InvokeAddInCommand action
|
|
// type.
|
|
DesktopCommandID string `json:"desktopCommandId,omitempty"`
|
|
|
|
// InitializationContext is an optional field which provides developers a
|
|
// way to specify any valid JSON object. The value is serialized into a
|
|
// string and made available to the add-in when the action is executed.
|
|
// This allows the action to pass initialization data to the add-in. Only
|
|
// used for InvokeAddInCommand action type.
|
|
InitializationContext interface{} `json:"initializationContext,omitempty"`
|
|
}
|
|
|
|
// MessageCardPotentialActionOpenURITarget is used for OpenUri action type.
|
|
// It defines one URI per target operating system.
|
|
type MessageCardPotentialActionOpenURITarget struct {
|
|
// OS defines the operating system the target uri refers to. Supported
|
|
// operating system values are default, windows, iOS and android. The
|
|
// default operating system will in most cases simply open the URI in a
|
|
// web browser, regardless of the actual operating system.
|
|
OS string `json:"os,omitempty"`
|
|
|
|
// URI defines the URI being called.
|
|
URI string `json:"uri,omitempty"`
|
|
}
|
|
|
|
// MessageCardPotentialActionHTTPPOSTHeader defines a HTTP header used for HttpPOST action type.
|
|
type MessageCardPotentialActionHTTPPOSTHeader struct {
|
|
// Name is the header name.
|
|
Name string `json:"name,omitempty"`
|
|
|
|
// Value is the header value.
|
|
Value string `json:"value,omitempty"`
|
|
}
|
|
|
|
// MessageCardPotentialActionActionCardInput represents an ActionCard input.
|
|
type MessageCardPotentialActionActionCardInput struct {
|
|
// Type of the ActionCard input.
|
|
// Must be either TextInput, DateInput or MultichoiceInput
|
|
Type string `json:"@type"`
|
|
|
|
// ID uniquely identifies the input so it is possible to reference it in
|
|
// the URL or body of an HttpPOST action.
|
|
ID string `json:"id,omitempty"`
|
|
|
|
// Title defines a title for the input.
|
|
Title string `json:"title,omitempty"`
|
|
|
|
// Value defines the initial value of the input. For multi-choice inputs,
|
|
// value must be equal to the value property of one of the input's
|
|
// choices.
|
|
Value string `json:"value,omitempty"`
|
|
|
|
// MessageCardPotentialActionInputMultichoiceInput must be defined for
|
|
// MultichoiceInput input type.
|
|
MessageCardPotentialActionActionCardInputMultichoiceInput
|
|
|
|
// MessageCardPotentialActionInputTextInput must be defined for InputText
|
|
// input type.
|
|
MessageCardPotentialActionActionCardInputTextInput
|
|
|
|
// MessageCardPotentialActionInputDateInput must be defined for DateInput
|
|
// input type.
|
|
MessageCardPotentialActionActionCardInputDateInput
|
|
|
|
// IsRequired indicates whether users are required to type a value before
|
|
// they are able to take an action that would take the value of the input
|
|
// as a parameter.
|
|
IsRequired bool `json:"isRequired,omitempty"`
|
|
}
|
|
|
|
// MessageCardPotentialActionActionCardInputTextInput represents a TextInput
|
|
// input used for potential action.
|
|
type MessageCardPotentialActionActionCardInputTextInput struct {
|
|
// MaxLength indicates the maximum number of characters that can be
|
|
// entered.
|
|
MaxLength int `json:"maxLength,omitempty"`
|
|
|
|
// IsMultiline indicates whether the text input should accept multiple
|
|
// lines of text.
|
|
IsMultiline bool `json:"isMultiline,omitempty"`
|
|
}
|
|
|
|
// MessageCardPotentialActionActionCardInputMultichoiceInput represents a
|
|
// MultichoiceInput input used for potential action.
|
|
type MessageCardPotentialActionActionCardInputMultichoiceInput struct {
|
|
// Choices defines the values that can be selected for the multichoice
|
|
// input.
|
|
Choices []struct {
|
|
Display string `json:"display,omitempty"`
|
|
Value string `json:"value,omitempty"`
|
|
} `json:"choices,omitempty"`
|
|
|
|
// Style defines the style of the input. When IsMultiSelect is false,
|
|
// setting the style property to expanded will instruct the host
|
|
// application to try and display all choices on the screen, typically
|
|
// using a set of radio buttons.
|
|
Style string `json:"style,omitempty"`
|
|
|
|
// IsMultiSelect indicates whether or not the user can select more than
|
|
// one choice. The specified choices will be displayed as a list of
|
|
// checkboxes. Default value is false.
|
|
IsMultiSelect bool `json:"isMultiSelect,omitempty"`
|
|
}
|
|
|
|
// MessageCardPotentialActionActionCardInputDateInput represents a DateInput
|
|
// input used for potential action.
|
|
type MessageCardPotentialActionActionCardInputDateInput struct {
|
|
// IncludeTime indicates whether the date input should allow for the
|
|
// selection of a time in addition to the date.
|
|
IncludeTime bool `json:"includeTime,omitempty"`
|
|
}
|
|
|
|
// MessageCardSectionFact represents a section fact entry that is usually
|
|
// displayed in a two-column key/value format.
|
|
type MessageCardSectionFact struct {
|
|
|
|
// Name is the key for an associated value in a key/value pair
|
|
Name string `json:"name"`
|
|
|
|
// Value is the value for an associated key in a key/value pair
|
|
Value string `json:"value"`
|
|
}
|
|
|
|
// MessageCardSectionImage represents an image as used by the heroImage and
|
|
// images properties of a section.
|
|
type MessageCardSectionImage struct {
|
|
|
|
// Image is the URL to the image.
|
|
Image string `json:"image"`
|
|
|
|
// Title is a short description of the image. Typically, this description
|
|
// is displayed in a tooltip as the user hovers their mouse over the
|
|
// image.
|
|
Title string `json:"title"`
|
|
}
|
|
|
|
// MessageCardSection represents a section to include in a message card.
|
|
type MessageCardSection struct {
|
|
// Title is the title property of a section. This property is displayed
|
|
// in a font that stands out, while not as prominent as the card's title.
|
|
// It is meant to introduce the section and summarize its content,
|
|
// similarly to how the card's title property is meant to summarize the
|
|
// whole card.
|
|
Title string `json:"title,omitempty"`
|
|
|
|
// Text is the section's text property. This property is very similar to
|
|
// the text property of the card. It can be used for the same purpose.
|
|
Text string `json:"text,omitempty"`
|
|
|
|
// ActivityImage is a property used to display a picture associated with
|
|
// the subject of a message card. For example, this might be the portrait
|
|
// of a person who performed an activity that the message card is
|
|
// associated with.
|
|
ActivityImage string `json:"activityImage,omitempty"`
|
|
|
|
// ActivityTitle is a property used to summarize the activity associated
|
|
// with a message card.
|
|
ActivityTitle string `json:"activityTitle,omitempty"`
|
|
|
|
// ActivitySubtitle is a property used to show brief, but extended
|
|
// information about an activity associated with a message card. Examples
|
|
// include the date and time the associated activity was taken or the
|
|
// handle of a person associated with the activity.
|
|
ActivitySubtitle string `json:"activitySubtitle,omitempty"`
|
|
|
|
// ActivityText is a property used to provide details about the activity.
|
|
// For example, if the message card is used to deliver updates about a
|
|
// topic, then this property would be used to hold the bulk of the content
|
|
// for the update notification.
|
|
ActivityText string `json:"activityText,omitempty"`
|
|
|
|
// HeroImage is a property that allows for setting an image as the
|
|
// centerpiece of a message card. This property can also be used to add a
|
|
// banner to the message card.
|
|
// Note: heroImage is not currently supported by Microsoft Teams
|
|
// https://stackoverflow.com/a/45389789
|
|
// We use a pointer to this type in order to have the json package
|
|
// properly omit this field if not explicitly set.
|
|
// https://github.com/golang/go/issues/11939
|
|
// https://stackoverflow.com/questions/18088294/how-to-not-marshal-an-empty-struct-into-json-with-go
|
|
// https://stackoverflow.com/questions/33447334/golang-json-marshal-how-to-omit-empty-nested-struct
|
|
HeroImage *MessageCardSectionImage `json:"heroImage,omitempty"`
|
|
|
|
// Facts is a collection of MessageCardSectionFact values. A section entry
|
|
// usually is displayed in a two-column key/value format.
|
|
Facts []MessageCardSectionFact `json:"facts,omitempty"`
|
|
|
|
// Images is a property that allows for the inclusion of a photo gallery
|
|
// inside a section.
|
|
// We use a slice of pointers to this type in order to have the json
|
|
// package properly omit this field if not explicitly set.
|
|
// https://github.com/golang/go/issues/11939
|
|
// https://stackoverflow.com/questions/18088294/how-to-not-marshal-an-empty-struct-into-json-with-go
|
|
// https://stackoverflow.com/questions/33447334/golang-json-marshal-how-to-omit-empty-nested-struct
|
|
Images []*MessageCardSectionImage `json:"images,omitempty"`
|
|
|
|
// PotentialActions is a collection of actions for a MessageCardSection.
|
|
// This is separate from the actions collection for the MessageCard.
|
|
PotentialActions []*MessageCardPotentialAction `json:"potentialAction,omitempty"`
|
|
|
|
// Markdown represents a toggle to enable or disable Markdown formatting.
|
|
// By default, all text fields in a card and its sections can be formatted
|
|
// using basic Markdown.
|
|
Markdown bool `json:"markdown,omitempty"`
|
|
|
|
// StartGroup is the section's startGroup property. This property marks
|
|
// the start of a logical group of information. Typically, sections with
|
|
// startGroup set to true will be visually separated from previous card
|
|
// elements.
|
|
StartGroup bool `json:"startGroup,omitempty"`
|
|
}
|
|
|
|
// MessageCard represents a legacy actionable message card used via Office 365
|
|
// or Microsoft Teams connectors.
|
|
type MessageCard struct {
|
|
// Required; must be set to "MessageCard"
|
|
Type string `json:"@type"`
|
|
|
|
// Required; must be set to "https://schema.org/extensions"
|
|
Context string `json:"@context"`
|
|
|
|
// Summary is required if the card does not contain a text property,
|
|
// otherwise optional. The summary property is typically displayed in the
|
|
// list view in Outlook, as a way to quickly determine what the card is
|
|
// all about. Summary appears to only be used when there are sections defined
|
|
Summary string `json:"summary,omitempty"`
|
|
|
|
// Title is the title property of a card. is meant to be rendered in a
|
|
// prominent way, at the very top of the card. Use it to introduce the
|
|
// content of the card in such a way users will immediately know what to
|
|
// expect.
|
|
Title string `json:"title,omitempty"`
|
|
|
|
// Text is required if the card does not contain a summary property,
|
|
// otherwise optional. The text property is meant to be displayed in a
|
|
// normal font below the card's title. Use it to display content, such as
|
|
// the description of the entity being referenced, or an abstract of a
|
|
// news article.
|
|
Text string `json:"text,omitempty"`
|
|
|
|
// Specifies a custom brand color for the card. The color will be
|
|
// displayed in a non-obtrusive manner.
|
|
ThemeColor string `json:"themeColor,omitempty"`
|
|
|
|
// ValidateFunc is a validation function that validates a MessageCard
|
|
ValidateFunc func() error `json:"-"`
|
|
|
|
// Sections is a collection of sections to include in the card.
|
|
Sections []*MessageCardSection `json:"sections,omitempty"`
|
|
|
|
// PotentialActions is a collection of actions for a MessageCard.
|
|
PotentialActions []*MessageCardPotentialAction `json:"potentialAction,omitempty"`
|
|
}
|
|
|
|
// validatePotentialAction inspects the given *MessageCardPotentialAction
|
|
// and returns an error if a value is missing or not known.
|
|
func validatePotentialAction(pa *MessageCardPotentialAction) error {
|
|
if pa == nil {
|
|
return fmt.Errorf("nil MessageCardPotentialAction received")
|
|
}
|
|
|
|
switch pa.Type {
|
|
case PotentialActionOpenURIType,
|
|
PotentialActionHTTPPostType,
|
|
PotentialActionActionCardType,
|
|
PotentialActionInvokeAddInCommandType:
|
|
|
|
default:
|
|
return fmt.Errorf("unknown type %s for potential action %s", pa.Type, pa.Name)
|
|
}
|
|
|
|
if pa.Name == "" {
|
|
return fmt.Errorf("missing name value for MessageCardPotentialAction")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// addPotentialAction adds one or many MessageCardPotentialAction values to a
|
|
// PotentialActions collection.
|
|
func addPotentialAction(collection *[]*MessageCardPotentialAction, actions ...*MessageCardPotentialAction) error {
|
|
for _, a := range actions {
|
|
logger.Printf("addPotentialAction: MessageCardPotentialAction received: %+v\n", a)
|
|
|
|
if err := validatePotentialAction(a); err != nil {
|
|
logger.Printf("addPotentialAction: validation failed: %v", err)
|
|
|
|
return err
|
|
}
|
|
|
|
if len(*collection) > PotentialActionMaxSupported {
|
|
logger.Printf("addPotentialAction: failed to add potential action: %v", ErrPotentialActionsLimitReached.Error())
|
|
|
|
return fmt.Errorf("func addPotentialAction: failed to add potential action: %w", ErrPotentialActionsLimitReached)
|
|
}
|
|
|
|
*collection = append(*collection, a)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// AddSection adds one or many additional MessageCardSection values to a
|
|
// MessageCard. Validation is performed to reject invalid values with an error
|
|
// message.
|
|
func (mc *MessageCard) AddSection(section ...*MessageCardSection) error {
|
|
for _, s := range section {
|
|
logger.Printf("AddSection: MessageCardSection received: %+v\n", s)
|
|
|
|
// bail if a completely nil section provided
|
|
if s == nil {
|
|
return fmt.Errorf("func AddSection: nil MessageCardSection received")
|
|
}
|
|
|
|
// Perform validation of all MessageCardSection fields in an effort to
|
|
// avoid adding a MessageCardSection with zero value fields. This is
|
|
// done to avoid generating an empty sections JSON array since the
|
|
// Sections slice for the MessageCard type would technically not be at
|
|
// a zero value state. Due to this non-zero value state, the
|
|
// encoding/json package would end up including the Sections struct
|
|
// field in the output JSON.
|
|
// See also https://github.com/golang/go/issues/11939
|
|
switch {
|
|
// If any of these cases trigger, skip over the `default` case
|
|
// statement and add the section.
|
|
case s.Images != nil:
|
|
case s.Facts != nil:
|
|
case s.HeroImage != nil:
|
|
case s.StartGroup:
|
|
case s.Markdown:
|
|
case s.ActivityText != "":
|
|
case s.ActivitySubtitle != "":
|
|
case s.ActivityTitle != "":
|
|
case s.ActivityImage != "":
|
|
case s.Text != "":
|
|
case s.Title != "":
|
|
|
|
default:
|
|
logger.Println("AddSection: No cases matched, all fields assumed to be at zero-value, skipping section")
|
|
return fmt.Errorf("all fields found to be at zero-value, skipping section")
|
|
}
|
|
|
|
logger.Println("AddSection: section contains at least one non-zero value, adding section")
|
|
mc.Sections = append(mc.Sections, s)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// AddPotentialAction adds one or many MessageCardPotentialAction values to a
|
|
// PotentialActions collection on a MessageCard.
|
|
func (mc *MessageCard) AddPotentialAction(actions ...*MessageCardPotentialAction) error {
|
|
return addPotentialAction(&mc.PotentialActions, actions...)
|
|
}
|
|
|
|
// Validate validates a MessageCard calling ValidateFunc if defined,
|
|
// otherwise, a default validation occurs
|
|
func (mc *MessageCard) Validate() error {
|
|
if mc.ValidateFunc != nil {
|
|
return mc.ValidateFunc()
|
|
}
|
|
|
|
// Falling back to a default implementation
|
|
if (mc.Text == "") && (mc.Summary == "") {
|
|
// This scenario results in:
|
|
// 400 Bad Request
|
|
// Summary or Text is required.
|
|
return fmt.Errorf("invalid message card: summary or text field is required")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// AddFact adds one or many additional MessageCardSectionFact values to a
|
|
// MessageCardSection
|
|
func (mcs *MessageCardSection) AddFact(fact ...MessageCardSectionFact) error {
|
|
for _, f := range fact {
|
|
logger.Printf("AddFact: MessageCardSectionFact received: %+v\n", f)
|
|
|
|
if f.Name == "" {
|
|
return fmt.Errorf("empty Name field received for new fact: %+v", f)
|
|
}
|
|
|
|
if f.Value == "" {
|
|
return fmt.Errorf("empty Name field received for new fact: %+v", f)
|
|
}
|
|
}
|
|
|
|
logger.Println("AddFact: section fact contains at least one non-zero value, adding section fact")
|
|
mcs.Facts = append(mcs.Facts, fact...)
|
|
|
|
return nil
|
|
}
|
|
|
|
// AddFactFromKeyValue accepts a key and slice of values and converts them to
|
|
// MessageCardSectionFact values
|
|
func (mcs *MessageCardSection) AddFactFromKeyValue(key string, values ...string) error {
|
|
// validate arguments
|
|
|
|
if key == "" {
|
|
return errors.New("empty key received for new fact")
|
|
}
|
|
|
|
if len(values) < 1 {
|
|
return errors.New("no values received for new fact")
|
|
}
|
|
|
|
fact := MessageCardSectionFact{
|
|
Name: key,
|
|
Value: strings.Join(values, ", "),
|
|
}
|
|
// TODO: Explicitly define or use constructor?
|
|
// fact := NewMessageCardSectionFact()
|
|
// fact.Name = key
|
|
// fact.Value = strings.Join(values, ", ")
|
|
|
|
mcs.Facts = append(mcs.Facts, fact)
|
|
|
|
// if we made it this far then all should be well
|
|
return nil
|
|
}
|
|
|
|
// AddPotentialAction adds one or many MessageCardPotentialAction values to a
|
|
// PotentialActions collection on a MessageCardSection. This is separate from
|
|
// the actions collection for the MessageCard.
|
|
func (mcs *MessageCardSection) AddPotentialAction(actions ...*MessageCardPotentialAction) error {
|
|
return addPotentialAction(&mcs.PotentialActions, actions...)
|
|
}
|
|
|
|
// AddImage adds an image to a MessageCard section. These images are used to
|
|
// provide a photo gallery inside a MessageCard section.
|
|
func (mcs *MessageCardSection) AddImage(sectionImage ...MessageCardSectionImage) error {
|
|
for i := range sectionImage {
|
|
if sectionImage[i].Image == "" {
|
|
return fmt.Errorf("cannot add empty image URL")
|
|
}
|
|
|
|
if sectionImage[i].Title == "" {
|
|
return fmt.Errorf("cannot add empty image title")
|
|
}
|
|
|
|
mcs.Images = append(mcs.Images, §ionImage[i])
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// AddHeroImageStr adds a Hero Image to a MessageCard section using string
|
|
// arguments. This image is used as the centerpiece or banner of a message
|
|
// card.
|
|
func (mcs *MessageCardSection) AddHeroImageStr(imageURL string, imageTitle string) error {
|
|
if imageURL == "" {
|
|
return fmt.Errorf("cannot add empty hero image URL")
|
|
}
|
|
|
|
if imageTitle == "" {
|
|
return fmt.Errorf("cannot add empty hero image title")
|
|
}
|
|
|
|
heroImage := MessageCardSectionImage{
|
|
Image: imageURL,
|
|
Title: imageTitle,
|
|
}
|
|
// TODO: Explicitly define or use constructor?
|
|
// heroImage := NewMessageCardSectionImage()
|
|
// heroImage.Image = imageURL
|
|
// heroImage.Title = imageTitle
|
|
|
|
mcs.HeroImage = &heroImage
|
|
|
|
// our validation checks didn't find any problems
|
|
return nil
|
|
}
|
|
|
|
// AddHeroImage adds a Hero Image to a MessageCard section using a
|
|
// MessageCardSectionImage argument. This image is used as the centerpiece or
|
|
// banner of a message card.
|
|
func (mcs *MessageCardSection) AddHeroImage(heroImage MessageCardSectionImage) error {
|
|
if heroImage.Image == "" {
|
|
return fmt.Errorf("cannot add empty hero image URL")
|
|
}
|
|
|
|
if heroImage.Title == "" {
|
|
return fmt.Errorf("cannot add empty hero image title")
|
|
}
|
|
|
|
mcs.HeroImage = &heroImage
|
|
|
|
// our validation checks didn't find any problems
|
|
return nil
|
|
}
|
|
|
|
// NewMessageCard creates a new message card with fields required by the
|
|
// legacy message card format already predefined
|
|
func NewMessageCard() MessageCard {
|
|
// define expected values to meet Office 365 Connector card requirements
|
|
// https://docs.microsoft.com/en-us/outlook/actionable-messages/message-card-reference#card-fields
|
|
msgCard := MessageCard{
|
|
Type: "MessageCard",
|
|
Context: "https://schema.org/extensions",
|
|
}
|
|
|
|
return msgCard
|
|
}
|
|
|
|
// NewMessageCardSection creates an empty message card section
|
|
func NewMessageCardSection() *MessageCardSection {
|
|
msgCardSection := MessageCardSection{}
|
|
return &msgCardSection
|
|
}
|
|
|
|
// NewMessageCardSectionFact creates an empty message card section fact
|
|
func NewMessageCardSectionFact() MessageCardSectionFact {
|
|
msgCardSectionFact := MessageCardSectionFact{}
|
|
return msgCardSectionFact
|
|
}
|
|
|
|
// NewMessageCardSectionImage creates an empty image for use with message card
|
|
// section
|
|
func NewMessageCardSectionImage() MessageCardSectionImage {
|
|
msgCardSectionImage := MessageCardSectionImage{}
|
|
return msgCardSectionImage
|
|
}
|
|
|
|
// NewMessageCardPotentialAction creates a new MessageCardPotentialAction
|
|
// using the provided potential action type and name. The name values defines
|
|
// the text that will be displayed on screen for the action. An error is
|
|
// returned if invalid values are supplied.
|
|
func NewMessageCardPotentialAction(potentialActionType string, name string) (*MessageCardPotentialAction, error) {
|
|
pa := MessageCardPotentialAction{
|
|
Type: potentialActionType,
|
|
Name: name,
|
|
}
|
|
|
|
if err := validatePotentialAction(&pa); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &pa, nil
|
|
}
|