qnabot-on-aws icon indicating copy to clipboard operation
qnabot-on-aws copied to clipboard

Feature Request : Content Designer | ResponseCard titles optional rather than required?

Open pdkn opened this issue 4 years ago • 3 comments

Given that aws-lex-web-ui doesn't require responseCard titles, is there a reason they are required in the Content Designer? Reason I ask is being able to have a single message bubble with a list of button options is handy. (Appreciate that config.ui.shouldDisplayResponseCardTitle could be set to false, but being a global setting doesn't give so much granular control). Thoughts?

pdkn avatar Jan 28 '20 14:01 pdkn

The Lex runtime in past versions required title as property of a GenericAttachment. Returning a ResponseCard without title caused a runtime error. Hence the Content Designer required this attribute when defining a ResponseCard for an answer. Current Lex documentation indicates title is optional. It is worth a test changing title to be an optional field. The file templates/master/elasticsearch/schema/qna.js defines the values required when storing response cards as part of an answer. This also drives the validation performed by the content designer ui. Change the description field on line 67 so that 'Required - max length of 80...' becomes 'Optional - max length of 80 ...' and then remove title from the array of required fields on line 109. Rebuild deploying to a new QnABot stack. I've not found a way to change an existing index in elasticsearch to incorporate updates to the definition. If Lex Web Ui api now treats title as optional within a returned ResponseCard/GenericAttachment I think this might work.

bobpskier avatar Jan 29 '20 03:01 bobpskier

Thanks @bobpskier After trying that, I've hit the following issues:

  • stack update doesn't work, so currently have to make a new stack. Assume this is due to ElasticSearch Indexes/mappings not updated? (manageable but be good to find a solution)
  • cards with buttons only (no title/image), are not displayed due to card.send = false. I'm not sure where the 'send' prop is being set? However I've got it working by changing lambda/fulfillment/lib/middleware/lex.js assemble(). Note the addition of _.pickBy() predicate function so that title with an empty strings don't get removed
(v, k) => {
   if (k === "title") return true
   return v != null && v !== ""
})

Also note the removal of the responseCard check && (_.get(response.card,"imageUrl","").trim() who's effect I'm not entirely sure about?

exports.assemble=function(request,response){
    // Check standard buttons prop
    var filteredButtons = _.get(response.card,"buttons",[])

    // If card title is empty, check results object for buttons (as they are not passed up to card object)
    if (filteredButtons.length === 0) {
        filteredButtons = _.get(response, "result.r.buttons", [])
    }

    // Remove buttons that don't have text & value properties
    for (var i = filteredButtons.length - 1; i >= 0; --i){
        if (!(filteredButtons[i].text && filteredButtons[i].value)){
            filteredButtons.splice(i,1)
        }
    }

    // If card title is empty but buttons are available, card.send needs changing from false to true
    if (filteredButtons.length > 0) {
        response.card.send = true
    }
    
    var out={
        sessionAttributes:_.get(response,'session',{}),
        dialogAction:_.pickBy({
            type:"Close",
            fulfillmentState:"Fulfilled",
            message:{
                contentType:response.type,
                content:response.message
            },
            responseCard:isCard(response.card) ? {
                version:"1",
                contentType:"application/vnd.amazonaws.card.generic",
                genericAttachments:[_.pickBy({
                    title:_.get(response,"card.title","Image"),
                    subTitle:_.get(response.card,'subTitle'),
                    imageUrl:response.card.imageUrl,
                    buttons: _.has(filteredButtons, [0]) ? filteredButtons : null
                }, (v, k) => {
                    if (k === "title") return true
                    return v != null && v !== ""
                })]
            } : null
        })
    }
...

So it now seems to be working but bit more complicated than would have liked. thoughts?

pdkn avatar Jan 31 '20 15:01 pdkn

This has been simplified by amending lambda/proxy-es/lib/query.js get_answer(req, res) from var card=_.get(res,"result.r.title") ? res.result.r : null ... to

var card = _.get(res,"result.r", {})
const cleanedCard = _.pickBy(card , (v, k) => { return v != null && v !== "" })
if(!_.isEmpty(cleanedCard)){
    res.card.send=true
    res.card.title=_.get(card,'title')
    res.card.subTitle=_.get(card,'subTitle')
    res.card.imageUrl=_.get(card,'imageUrl')
    res.card.buttons=_.get(card,'buttons')
}

This should give a card if any property (title, subtitle, imageUrl, buttons) has a truthy value

pdkn avatar Feb 24 '20 18:02 pdkn