Skip to content

Commit

Permalink
Merge pull request #1072 from DoSomething/feature/broadcast-topic
Browse files Browse the repository at this point in the history
Finalizing askYesNo and autoReplyBroadcast template fields [pr]
  • Loading branch information
aaronschachter committed Aug 9, 2018
2 parents 84b33be + 8dc28ee commit 06d93ae
Show file tree
Hide file tree
Showing 14 changed files with 245 additions and 416 deletions.
15 changes: 15 additions & 0 deletions config/lib/helpers/contentfulEntry.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@
'use strict';

/**
* This maps the fields in our Contentful types into broadcast, topic, and defaultTopicTriggers.
*
* Content types with either a templates or postType property set are returned as topics.
* If the type contains a templates array, each array item should correspond to a single value text
* field defined on the content type, which is used as a bot reply template in the topic.
* If the type contains a postType string property instead, its an older content type and its
* templates are configured via config/lib/helpers/topic. Ideally we should consolidate, but it'd
* take a bit of refactoring as the topic helper config contains default values for certain text
* fields to use if the field values are blank.
*
* A broadcastable content type currently requires a text field named "text" and attachments field
* named "attachments" to define content for the outbound broadcast.
*
*/
module.exports = {
contentTypes: {
askYesNo: {
Expand Down
51 changes: 38 additions & 13 deletions documentation/endpoints/broadcasts.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,21 @@ Name | Type | Description
-----|------|------------
`id` | String | The Contentful entry id
`name` | String | Internal name used to reference the broadcast
`type` | String | The Contentful entry type, e.g. `autoReplyBroadcast`, `broadcast`
`type` | String | The Contentful entry type, e.g. `askYesNo`, `autoReplyBroadcast`, `broadcast`
`message` | Object | Contains the [outbound message content](https://github.com/DoSomething/gambit-conversations/blob/master/documentation/endpoints/messages.md) to send to user
`message.text` | String |
`message.attachments` | Array |
`message.template` | String |
`topic` | String | If set, updates the conversation topic to this string.*
`campaignId` | Number | If set, updates the conversation topic to the given campaign's topic.*
`message.topic` | Object | If an id property exists, its saved to the [conversation topic](https://github.com/DoSomething/gambit-campaigns/blob/master/documentation/endpoints/topics.md) when broadcast is sent
`templates` | Object | Defines replies for when this broadcast is saved as a [conversation topic](https://github.com/DoSomething/gambit-campaigns/blob/master/documentation/endpoints/topics.md) -- used in `askYesNo`, which will update the conversation topic again if user answers yes

Legacy fields (only used for type `broadcast`)

Name | Type | Description
-----|------|------------
`topic` | String | (Legacy) If set, updates the conversation topic to this string.*
`campaignId` | Number | (Legacy) If set, updates the conversation topic to the given campaign's topic.*

* Note: These fields will likely be deprecated by a `topicId` per https://www.pivotaltracker.com/story/show/157369418


## Retrieve broadcasts
Expand Down Expand Up @@ -64,18 +70,37 @@ curl http://localhost:5000/v1/broadcasts?skip=20
"topic": "survey_response"
},
{
"id": "4C2gkDV8oUAaewSMYeokEC",
"name": "VoterRegistration2018_Jul8_OhioSpecialHouseGeneralReminder",
"type": "broadcast",
"createdAt": "2018-07-06T18:28:51.478Z",
"updatedAt": "2018-07-06T18:31:55.968Z",
"id": "2X4r3fZrTGA2mGemowgiEI",
"name": "askYesNo test",
"type": "askYesNo",
"createdAt": "2018-08-06T23:34:56.395Z",
"updatedAt": "2018-08-08T22:20:14.822Z",
"message": {
"text": "It's Freddie! Guess what -- Ohio needs YOU. Voters have the power to make a huge difference in your state, so make sure you're registered to vote in Ohio's special house general election before tonight's deadline! It takes just 2 mins: https://vote.dosomething.org/?r=user:{{user.id}},campaignID:8017,campaignRunID:8022,source:sms,source_details:broadcastID_4C2gkDV8oUAaewSMYeokEC",
"text": "Join Pump it Up? \n\nYes No",
"attachments": [],
"template": "rivescript"
"template": "askYesNo",
"topic": {}
},
"templates": {
"saidYes": {
"text": "Great! Text START to submit a photo.",
"topic": {
"id": "4xXe9sQqmIeiWauSUu6kAY"
}
},
"saidNo": {
"text": "Ok, we'll check in with you later.",
"topic": {}
},
"invalidAskYesNoResponse": {
"text": "Sorry, I didn't get that - did you want to join for Pump It Up? Yes or No",
"topic": {}
},
"autoReply": {
"text": "Sorry, I didn't understand that. Text Q if you have a question.",
"topic": {}
}
},
"campaignId": null,
"topic": "survey_response"
},
...
},
Expand Down
281 changes: 44 additions & 237 deletions documentation/endpoints/topics.md

Large diffs are not rendered by default.

58 changes: 22 additions & 36 deletions lib/helpers/broadcast.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,19 +64,18 @@ function getById(broadcastId, resetCache = false) {
* @return {Promise}
*/
function parseBroadcastFromContentfulEntry(contentfulEntry) {
const contentType = contentful.getContentTypeFromContentfulEntry(contentfulEntry);
// A nice to have TODO is migrating all legacy broadcast entries into the respective new type, so
// we can remove check and the function it calls entirely.
const contentTypeConfigs = helpers.contentfulEntry.getContentTypeConfigs();
if (contentType === contentTypeConfigs.legacyBroadcast.type) {
return module.exports.parseLegacyBroadcastFromContentfulEntry(contentfulEntry);
}

const data = helpers.contentfulEntry.getSummaryFromContentfulEntry(contentfulEntry);
const message = module.exports
.parseBroadcastMessageFromContentfulEntryAndTemplateName(contentfulEntry, contentType);
const templates = helpers.contentfulEntry.getMessageTemplatesFromContentfulEntry(contentfulEntry);

// A nice to have TODO would be migrating all legacy broadcast entries into their new respective
// type to avoid this check, but it likely is not possible to change type of an existing entry.
if (helpers.contentfulEntry.isLegacyBroadcast(contentfulEntry)) {
return Promise.resolve(Object.assign(data, module.exports
.getLegacyBroadcastDataFromContentfulEntry(contentfulEntry)));
}
// Parse the outbound broadcast message to send.
const message = helpers.contentfulEntry
.getMessageTemplateFromContentfulEntryAndTemplateName(contentfulEntry, data.type);
// Parse topic templates if they exist.
const templates = helpers.contentfulEntry.getTopicTemplatesFromContentfulEntry(contentfulEntry);
return Promise.resolve(Object.assign(data, { message, templates }));
}

Expand All @@ -85,13 +84,14 @@ function parseBroadcastFromContentfulEntry(contentfulEntry) {
* deprecated by new content types per broadcast type.
*
* @param {Object} contentfulEntry
* @return {Promise}
* @return {Object}
*/
function parseLegacyBroadcastFromContentfulEntry(contentfulEntry) {
const data = helpers.contentfulEntry.getSummaryFromContentfulEntry(contentfulEntry);
data.message = {
text: contentfulEntry.fields.message,
attachments: contentful.getAttachmentsFromContentfulEntry(contentfulEntry),
function getLegacyBroadcastDataFromContentfulEntry(contentfulEntry) {
const data = {
message: {
text: contentfulEntry.fields.message,
attachments: contentful.getAttachmentsFromContentfulEntry(contentfulEntry),
},
};
// This content type was used to send all types of broadcasts and has since been deprecated.
const hardcodedTopic = contentfulEntry.fields.topic;
Expand All @@ -108,32 +108,18 @@ function parseLegacyBroadcastFromContentfulEntry(contentfulEntry) {
const campaignConfigEntry = contentfulEntry.fields.campaign;
data.campaignId = Number(contentful.getCampaignIdFromContentfulEntry(campaignConfigEntry));
data.topic = null;
// Ancient legacy broadcasts didn't require the template field, because at the time, we only
// supported askSignup broadcasts.
data.message.template = contentfulEntry.fields.template || 'askSignup';
}
return Promise.resolve(data);
}

/**
* @param {Object} contentfulEntry
* @return {Object}
*/
function parseBroadcastMessageFromContentfulEntryAndTemplateName(contentfulEntry, templateName) {
const contentType = contentful.getContentTypeFromContentfulEntry(contentfulEntry);
const broadcastMessageEntry = contentfulEntry.fields[contentType];

if (!broadcastMessageEntry) {
return null;
}
return helpers.contentfulEntry
.getMessageTemplateFromContentfulEntryAndTemplateName(broadcastMessageEntry, templateName);
return data;
}

module.exports = {
fetch,
fetchById,
getById,
getContentTypes,
getLegacyBroadcastDataFromContentfulEntry,
parseBroadcastFromContentfulEntry,
parseBroadcastMessageFromContentfulEntryAndTemplateName,
parseLegacyBroadcastFromContentfulEntry,
};
41 changes: 29 additions & 12 deletions lib/helpers/contentfulEntry.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ function getSummaryFromContentfulEntry(contentfulEntry) {
createdAt: contentfulEntry.sys.createdAt,
updatedAt: contentfulEntry.sys.updatedAt,
};
logger.debug('parseNameInfoFromContentfulEntry', { result });
logger.debug('getSummaryFromContentfulEntry', { result });
return result;
}

Expand All @@ -82,23 +82,31 @@ function getMessageTemplateFromContentfulEntryAndTemplateName(contentfulEntry, t
* @param {String}
* @return {Object}
*/
function getMessageTemplatesFromContentfulEntry(contentfulEntry) {
function getTopicTemplatesFromContentfulEntry(contentfulEntry) {
const contentType = contentful.getContentTypeFromContentfulEntry(contentfulEntry);
const fieldNames = config.contentTypes[contentType].templates;
const templateFieldNames = config.contentTypes[contentType].templates;
const result = {};

if (!contentfulEntry || !fieldNames) {
if (!contentfulEntry || !templateFieldNames) {
return result;
}

fieldNames.forEach((fieldName) => {
const messageEntry = contentfulEntry.fields[fieldName];
if (!messageEntry) {
return;
templateFieldNames.forEach((fieldName) => {
const text = contentfulEntry.fields[fieldName];
const topic = {};
// The saidYes template should change conversation topic to the saidYesTopic field.
if (fieldName === 'saidYes') {
const topicRef = contentfulEntry.fields.saidYesTopic;
if (topicRef) {
topic.id = contentful.getContentfulIdFromContentfulEntry(topicRef);
}
}
result[fieldName] = module.exports
.getMessageTemplateFromContentfulEntryAndTemplateName(messageEntry, fieldName);
result[fieldName] = {
text,
topic,
};
});

return result;
}

Expand All @@ -124,7 +132,15 @@ function isBroadcastable(contentfulEntry) {
* @return {Boolean}
*/
function isDefaultTopicTrigger(contentfulEntry) {
return contentful.isContentType(contentfulEntry, 'defaultTopicTrigger');
return contentful.isContentType(contentfulEntry, config.contentTypes.defaultTopicTrigger.type);
}

/**
* @param {Object} contentfulEntry
* @return {Boolean}
*/
function isLegacyBroadcast(contentfulEntry) {
return contentful.isContentType(contentfulEntry, config.contentTypes.legacyBroadcast.type);
}

/**
Expand All @@ -139,11 +155,12 @@ module.exports = {
getById,
getContentTypeConfigs,
getMessageTemplateFromContentfulEntryAndTemplateName,
getMessageTemplatesFromContentfulEntry,
getSummaryFromContentfulEntry,
getTopicTemplatesFromContentfulEntry,
isAutoReply,
isBroadcastable,
isDefaultTopicTrigger,
isLegacyBroadcast,
isMessage,
parseContentfulEntry,
};
3 changes: 2 additions & 1 deletion lib/helpers/topic.js
Original file line number Diff line number Diff line change
Expand Up @@ -163,8 +163,9 @@ function parseTemplateFromContentfulEntryAndTemplateName(contentfulEntry, templa
*/
function parseTemplatesFromContentfulEntryAndCampaign(contentfulEntry, campaign) {
if (helpers.contentfulEntry.isAutoReply(contentfulEntry)) {
return helpers.contentfulEntry.getMessageTemplatesFromContentfulEntry(contentfulEntry);
return helpers.contentfulEntry.getTopicTemplatesFromContentfulEntry(contentfulEntry);
}

const contentType = contentful.getContentTypeFromContentfulEntry(contentfulEntry);
const data = {};
const templateNames = Object.keys(config.templatesByContentType[contentType]);
Expand Down
Loading

0 comments on commit 06d93ae

Please sign in to comment.