From 4e098d44951dd406b524d3ea3af435d731dac2af Mon Sep 17 00:00:00 2001 From: dylan Date: Tue, 14 Jan 2020 10:43:33 +0000 Subject: [PATCH 01/18] MAS-129 Add MAD fields without branding --- lambda/MAS/Controllers/MailController.cs | 2 +- lambda/MAS/Services/MailService.cs | 29 ++++++++++++++++-------- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/lambda/MAS/Controllers/MailController.cs b/lambda/MAS/Controllers/MailController.cs index d9ea13a4..5521597e 100644 --- a/lambda/MAS/Controllers/MailController.cs +++ b/lambda/MAS/Controllers/MailController.cs @@ -25,7 +25,7 @@ public async Task PutMailAsync() { var items = await _contentService.GetItemsAsync(); - var body = _mailService.CreateEmailBody(items); + var body = _mailService.CreateDailyEmailBody(items); var subject = "MAS Email"; var previewText = "This MAS email was created " + DateTime.Now.ToShortDateString(); diff --git a/lambda/MAS/Services/MailService.cs b/lambda/MAS/Services/MailService.cs index aa7ab767..30b9d4e3 100644 --- a/lambda/MAS/Services/MailService.cs +++ b/lambda/MAS/Services/MailService.cs @@ -8,13 +8,14 @@ using System.Collections.Generic; using System.Text; using System.Threading.Tasks; +using System.Linq; namespace MAS.Services { public interface IMailService { Task CreateAndSendCampaignAsync(string subject, string previewText, string body); - string CreateEmailBody(IEnumerable item); + string CreateDailyEmailBody(IEnumerable item); } public class MailService: IMailService @@ -73,18 +74,28 @@ public async Task CreateAndSendCampaignAsync(string subject, string prev } - public string CreateEmailBody(IEnumerable items) + public string CreateDailyEmailBody(IEnumerable items) { + var groupedItems = items.GroupBy(x => x.EvidenceType).ToList(); + var body = new StringBuilder(); - foreach (var item in items) + foreach (var group in groupedItems) { - body.Append(item.Source.Title); - body.Append("
"); - body.Append(item.Title); - body.Append("
"); - body.Append(item.ShortSummary); - body.Append("


"); + var speciality = group.Single().EvidenceType.Title; + body.Append("" + speciality + ""); + + foreach (var item in group) + { + body.Append("
"); + body.Append(item.Source.Title); + body.Append("
"); + body.Append(item.Title); + body.Append("
"); + body.Append(item.ShortSummary); + body.Append("
"); + body.Append("SPS Comment"); + } } return body.ToString(); From 0a12a63f6390428969cdb9332833a547dbe3110b Mon Sep 17 00:00:00 2001 From: dylan Date: Tue, 14 Jan 2020 11:28:04 +0000 Subject: [PATCH 02/18] MAS-129 Add Specialities to MAD --- cms/models/Item.js | 7 ++-- cms/routes/api/items.js | 10 +++--- lambda/MAS.Tests/Feeds/multiple-items.json | 39 ++++++++++++++++++++-- lambda/MAS/Models/Item.cs | 3 ++ lambda/MAS/Models/Speciality.cs | 14 ++++++++ lambda/MAS/Services/MailService.cs | 15 ++++++--- 6 files changed, 74 insertions(+), 14 deletions(-) create mode 100644 lambda/MAS/Models/Speciality.cs diff --git a/cms/models/Item.js b/cms/models/Item.js index 3b63fbc0..ef77262f 100644 --- a/cms/models/Item.js +++ b/cms/models/Item.js @@ -95,7 +95,7 @@ Item.add({ staticPath: { type: Types.Text, watch: "title", - value: function() { + value: function () { return ( process.env.STATIC_SITE_PATH + (this.slug || utils.slug(this.title)) + @@ -107,7 +107,7 @@ Item.add({ } }); -Item.schema.pre("validate", function(next) { +Item.schema.pre("validate", function (next) { if (this.isInitial) { this.isInitial = false; next(); @@ -152,7 +152,7 @@ const createWeeklyIfNeeded = async () => { }; // Post save hook to trigger a lambda with the document details -Item.schema.post("save", async function(doc, next) { +Item.schema.post("save", async function (doc, next) { await createWeeklyIfNeeded(); logger.info("Post save, sending request...", doc); @@ -165,6 +165,7 @@ Item.schema.post("save", async function(doc, next) { .model.findById(doc._id) .populate("source") .populate("evidenceType") + .populate("speciality") .exec(); } catch (err) { logger.error("An error occurred finding item: ", err.message); diff --git a/cms/routes/api/items.js b/cms/routes/api/items.js index 589cbf66..45c5da92 100644 --- a/cms/routes/api/items.js +++ b/cms/routes/api/items.js @@ -4,12 +4,13 @@ const keystone = require("keystone"), const log4js = require("log4js"), logger = log4js.getLogger(); -exports.single = function(req, res, next) { +exports.single = function (req, res, next) { Items.model .findById(req.params.itemId) .populate("source") .populate("evidenceType") - .exec(function(err, item) { + .populate("speciality") + .exec(function (err, item) { if (err) { logger.error(`Error getting item with id ${req.params.itemId}`, err); return res.error(err, true); @@ -28,13 +29,14 @@ exports.single = function(req, res, next) { /** * List Items */ -exports.list = function(req, res) { +exports.list = function (req, res) { // TODO: Pagination Items.model .find() .populate("source") .populate("evidenceType") - .exec(function(err, items) { + .populate("speciality") + .exec(function (err, items) { if (err) { logger.error(`Failed to get list of items`, err); return res.error(err, true); diff --git a/lambda/MAS.Tests/Feeds/multiple-items.json b/lambda/MAS.Tests/Feeds/multiple-items.json index c68a3ab5..7cbcfefb 100644 --- a/lambda/MAS.Tests/Feeds/multiple-items.json +++ b/lambda/MAS.Tests/Feeds/multiple-items.json @@ -46,7 +46,18 @@ "publicationDate": "2019-11-23T13:48:36.000Z", "createdDate": "2019-10-23T10:38:43.000Z", "specialities": [ - "5daf1a5c8a34d478fd05b5d4" + { + "_id": "5e0e4331200a585f718e1ee5", + "key": "be1c2e2f-745e-4a82-b5aa-d4cef4d31a1b", + "title": "Anaesthesia and pain", + "__v": 0 + }, + { + "_id": "5e0e4331200a58dead8e1ee8", + "key": "53fc67a4-46d8-4171-9447-7fcf216c8749", + "title": "Complementary and alternative therapies", + "__v": 0 + } ], "evidenceType": { "_id": "5df7abf383138898ee1f67ef", @@ -75,7 +86,18 @@ "publicationDate": "2019-10-23T00:00:00.000Z", "createdDate": "2019-10-23T10:56:40.000Z", "specialities": [ - "5daf1a5c8a34d4d6e505b5b9" + { + "_id": "5e0e4331200a585f718e1ee5", + "key": "be1c2e2f-745e-4a82-b5aa-d4cef4d31a1b", + "title": "Anaesthesia and pain", + "__v": 0 + }, + { + "_id": "5e0e4331200a58dead8e1ee8", + "key": "53fc67a4-46d8-4171-9447-7fcf216c8749", + "title": "Complementary and alternative therapies", + "__v": 0 + } ], "evidenceType": { "_id": "5df7abf383138898ee1f67ef", @@ -104,7 +126,18 @@ "publicationDate": "2019-10-28T00:00:00.000Z", "createdDate": "2019-10-28T11:07:11.000Z", "specialities": [ - "5daf1a5c8a34d485cb05b5ba" + { + "_id": "5e0e4331200a585f718e1ee5", + "key": "be1c2e2f-745e-4a82-b5aa-d4cef4d31a1b", + "title": "Anaesthesia and pain", + "__v": 0 + }, + { + "_id": "5e0e4331200a58dead8e1ee8", + "key": "53fc67a4-46d8-4171-9447-7fcf216c8749", + "title": "Complementary and alternative therapies", + "__v": 0 + } ], "evidenceType": { "_id": "5df7abf383138898ee1f67ef", diff --git a/lambda/MAS/Models/Item.cs b/lambda/MAS/Models/Item.cs index 4dfc11a6..203355c6 100644 --- a/lambda/MAS/Models/Item.cs +++ b/lambda/MAS/Models/Item.cs @@ -1,4 +1,5 @@ using Newtonsoft.Json; +using System.Collections.Generic; using System.ComponentModel.DataAnnotations; namespace MAS.Models @@ -13,6 +14,8 @@ public class Item public string Title { get; set; } [Required, JsonRequired] public Source Source { get; set; } + [JsonProperty("speciality")] + public List Speciality { get; set; } [Required, JsonRequired] public EvidenceType EvidenceType { get; set; } public string ShortSummary { get; set; } diff --git a/lambda/MAS/Models/Speciality.cs b/lambda/MAS/Models/Speciality.cs new file mode 100644 index 00000000..800ea34e --- /dev/null +++ b/lambda/MAS/Models/Speciality.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace MAS.Models +{ + public class Speciality + { + public string Title { get; set; } + + public string Key { get; set; } + } +} \ No newline at end of file diff --git a/lambda/MAS/Services/MailService.cs b/lambda/MAS/Services/MailService.cs index 30b9d4e3..42452e67 100644 --- a/lambda/MAS/Services/MailService.cs +++ b/lambda/MAS/Services/MailService.cs @@ -76,26 +76,33 @@ public async Task CreateAndSendCampaignAsync(string subject, string prev public string CreateDailyEmailBody(IEnumerable items) { - var groupedItems = items.GroupBy(x => x.EvidenceType).ToList(); + var groupedItems = items.GroupBy(x => x.EvidenceType.Title).ToList(); var body = new StringBuilder(); foreach (var group in groupedItems) { - var speciality = group.Single().EvidenceType.Title; - body.Append("" + speciality + ""); + var evidenceType = group.Key; + + body.Append("
"); + body.Append("" + evidenceType + ""); foreach (var item in group) { + body.Append("
"); + body.Append(item.Title); body.Append("
"); body.Append(item.Source.Title); body.Append("
"); - body.Append(item.Title); + body.Append(String.Join(" | ", item.Speciality.Select(x => x.Title))); body.Append("
"); body.Append(item.ShortSummary); body.Append("
"); body.Append("SPS Comment"); + body.Append("
"); } + + body.Append("
"); } return body.ToString(); From 697face1ba14257775afe550c7cc0ab2dc91cff3 Mon Sep 17 00:00:00 2001 From: dylan Date: Tue, 14 Jan 2020 12:45:18 +0000 Subject: [PATCH 03/18] MAS-129 Add tests --- lambda/MAS.Tests/Infrastructure/TestBase.cs | 8 +- ...TwoItemsDifferentEvidenceType.approved.txt | 1 + ...thTwoItemsSharingEvidenceType.approved.txt | 1 + ...ests.CanCreateSingleItemEmail.approved.txt | 1 + ...nySpecialitiesRenderCorrectly.approved.txt | 1 + lambda/MAS.Tests/UnitTests/DailyEmailTests.cs | 142 ++++++++++++++++++ lambda/MAS/Services/ContentService.cs | 4 +- 7 files changed, 155 insertions(+), 3 deletions(-) create mode 100644 lambda/MAS.Tests/UnitTests/DailyEmailTests.CanCreateEmailWithTwoItemsDifferentEvidenceType.approved.txt create mode 100644 lambda/MAS.Tests/UnitTests/DailyEmailTests.CanCreateEmailWithTwoItemsSharingEvidenceType.approved.txt create mode 100644 lambda/MAS.Tests/UnitTests/DailyEmailTests.CanCreateSingleItemEmail.approved.txt create mode 100644 lambda/MAS.Tests/UnitTests/DailyEmailTests.ItemsWithManySpecialitiesRenderCorrectly.approved.txt create mode 100644 lambda/MAS.Tests/UnitTests/DailyEmailTests.cs diff --git a/lambda/MAS.Tests/Infrastructure/TestBase.cs b/lambda/MAS.Tests/Infrastructure/TestBase.cs index e2264ec9..25e43ecd 100644 --- a/lambda/MAS.Tests/Infrastructure/TestBase.cs +++ b/lambda/MAS.Tests/Infrastructure/TestBase.cs @@ -2,11 +2,12 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.TestHost; using Microsoft.Extensions.Configuration; +using System; using System.Net.Http; namespace MAS.Tests.Infrastructure { - public class TestBase + public class TestBase : IDisposable { protected readonly TestServer _server; protected readonly HttpClient _client; @@ -30,5 +31,10 @@ public TestBase() _server = new TestServer(builder); _client = _server.CreateClient(); } + + public virtual void Dispose() + { + // Do "global" teardown here; Called after every test method. + } } } diff --git a/lambda/MAS.Tests/UnitTests/DailyEmailTests.CanCreateEmailWithTwoItemsDifferentEvidenceType.approved.txt b/lambda/MAS.Tests/UnitTests/DailyEmailTests.CanCreateEmailWithTwoItemsDifferentEvidenceType.approved.txt new file mode 100644 index 00000000..3539d7b3 --- /dev/null +++ b/lambda/MAS.Tests/UnitTests/DailyEmailTests.CanCreateEmailWithTwoItemsDifferentEvidenceType.approved.txt @@ -0,0 +1 @@ +
Some evidence type
Some Title
Some source
Some speciality
Some short summary
SPS Comment
Some evidence type 2
Some Title
Some source
Some speciality 2
Some short summary
SPS Comment
\ No newline at end of file diff --git a/lambda/MAS.Tests/UnitTests/DailyEmailTests.CanCreateEmailWithTwoItemsSharingEvidenceType.approved.txt b/lambda/MAS.Tests/UnitTests/DailyEmailTests.CanCreateEmailWithTwoItemsSharingEvidenceType.approved.txt new file mode 100644 index 00000000..49775cd6 --- /dev/null +++ b/lambda/MAS.Tests/UnitTests/DailyEmailTests.CanCreateEmailWithTwoItemsSharingEvidenceType.approved.txt @@ -0,0 +1 @@ +
Some evidence type
Some Title
Some source
Some speciality
Some short summary
SPS Comment
Some Title
Some source
Some speciality
Some short summary
SPS Comment
\ No newline at end of file diff --git a/lambda/MAS.Tests/UnitTests/DailyEmailTests.CanCreateSingleItemEmail.approved.txt b/lambda/MAS.Tests/UnitTests/DailyEmailTests.CanCreateSingleItemEmail.approved.txt new file mode 100644 index 00000000..92233622 --- /dev/null +++ b/lambda/MAS.Tests/UnitTests/DailyEmailTests.CanCreateSingleItemEmail.approved.txt @@ -0,0 +1 @@ +
Some evidence type
Some Title
Some source
Some speciality
Some short summary
SPS Comment
\ No newline at end of file diff --git a/lambda/MAS.Tests/UnitTests/DailyEmailTests.ItemsWithManySpecialitiesRenderCorrectly.approved.txt b/lambda/MAS.Tests/UnitTests/DailyEmailTests.ItemsWithManySpecialitiesRenderCorrectly.approved.txt new file mode 100644 index 00000000..e978e75c --- /dev/null +++ b/lambda/MAS.Tests/UnitTests/DailyEmailTests.ItemsWithManySpecialitiesRenderCorrectly.approved.txt @@ -0,0 +1 @@ +
Some evidence type
Some Title
Some source
Some speciality | Another speciality
Some short summary
SPS Comment
\ No newline at end of file diff --git a/lambda/MAS.Tests/UnitTests/DailyEmailTests.cs b/lambda/MAS.Tests/UnitTests/DailyEmailTests.cs new file mode 100644 index 00000000..63d7fcb0 --- /dev/null +++ b/lambda/MAS.Tests/UnitTests/DailyEmailTests.cs @@ -0,0 +1,142 @@ +using MailChimp.Net.Core; +using MailChimp.Net.Interfaces; +using MailChimp.Net.Models; +using MAS.Models; +using MAS.Services; +using MAS.Tests.Infrastructure; +using Microsoft.Extensions.Logging; +using Moq; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Xunit; +using Source = MAS.Models.Source; + +namespace MAS.Tests.UnitTests +{ + public class DailyEmailTests : TestBase + { + + Mock> MockLogger; + Mock MockMailChimpManager; + MailService MailService; + + public DailyEmailTests() + { + //Arrange + MockLogger = new Mock>(); + + MockMailChimpManager = new Mock(); + MockMailChimpManager.Setup(x => x.Campaigns.AddAsync(It.IsAny())).ReturnsAsync(new Campaign() { Id = "1234" }); + MockMailChimpManager.Setup(x => x.Content.AddOrUpdateAsync(It.IsAny(), It.IsAny())); + MockMailChimpManager.Setup(x => x.Campaigns.SendAsync(It.IsAny())); + + MailService = new MailService(MockMailChimpManager.Object, MockLogger.Object); + } + + public override void Dispose() + { + + } + + Item exampleItem = new Item() + { + Id = "123", + Title = "Some Title", + Slug = "abc", + ShortSummary = "Some short summary", + ResourceLinks = "", + Comment = "", + Speciality = new List + { + new Speciality() + { + Key = "1a", + Title = "Some speciality", + + } + }, + EvidenceType = new EvidenceType() + { + Key = "1b", + Title = "Some evidence type" + }, + Source = new Source() + { + Id = "1c", + Title = "Some source" + } + }; + Item exampleItem2 = new Item() + { + Id = "123", + Title = "Some Title", + Slug = "abc", + ShortSummary = "Some short summary", + ResourceLinks = "", + Comment = "", + Speciality = new List + { + new Speciality() + { + Key = "1a", + Title = "Some speciality 2", + + } + }, + EvidenceType = new EvidenceType() + { + Key = "1c", + Title = "Some evidence type 2" + }, + Source = new Source() + { + Id = "1c", + Title = "Some source" + } + }; + + [Fact] + public void CanCreateSingleItemEmail() + { + var items = new List { exampleItem }; + var actualHtml = this.MailService.CreateDailyEmailBody(items); + + actualHtml.ShouldMatchApproved(); + + } + + [Fact] + public void CanCreateEmailWithTwoItemsSharingEvidenceType() + { + var items = new List { exampleItem, exampleItem }; + var actualHtml = this.MailService.CreateDailyEmailBody(items); + + actualHtml.ShouldMatchApproved(); + + } + + [Fact] + public void CanCreateEmailWithTwoItemsDifferentEvidenceType() + { + var items = new List { exampleItem, exampleItem2 }; + var actualHtml = this.MailService.CreateDailyEmailBody(items); + + actualHtml.ShouldMatchApproved(); + + } + + [Fact] + public void ItemsWithManySpecialitiesRenderCorrectly() + { + exampleItem.Speciality.Add(new Speciality() { Key = "abcd", Title = "Another speciality" }); + var items = new List { exampleItem }; + var actualHtml = this.MailService.CreateDailyEmailBody(items); + + actualHtml.ShouldMatchApproved(); + + } + + } +} diff --git a/lambda/MAS/Services/ContentService.cs b/lambda/MAS/Services/ContentService.cs index 028f802c..2c10abab 100644 --- a/lambda/MAS/Services/ContentService.cs +++ b/lambda/MAS/Services/ContentService.cs @@ -30,8 +30,8 @@ public async Task> GetItemsAsync() try { var jsonStr = await client.DownloadStringTaskAsync(new Uri(AppSettings.CMSConfig.URI)); - var json = JsonConvert.DeserializeObject(jsonStr); - return json; + var item = JsonConvert.DeserializeObject(jsonStr); + return item; } catch(Exception e) { From be26ac1b1175de8db80266286bd2dbddd36717f5 Mon Sep 17 00:00:00 2001 From: dylan Date: Mon, 13 Jan 2020 16:40:58 +0000 Subject: [PATCH 04/18] MAS-121 Add broader title to evidence type in item api Use JSONLD file to look up the key for the evidence type when we're exporting items via the single API endpoint --- cms/evidence-types.jsonld | 371 +++++++++++++++++++++++++++++++++++++ cms/models/EvidenceType.js | 40 +++- cms/routes/api/items.js | 22 ++- 3 files changed, 426 insertions(+), 7 deletions(-) create mode 100644 cms/evidence-types.jsonld diff --git a/cms/evidence-types.jsonld b/cms/evidence-types.jsonld new file mode 100644 index 00000000..9c9320b2 --- /dev/null +++ b/cms/evidence-types.jsonld @@ -0,0 +1,371 @@ +{ + "@graph" : [ { + "@id" : "mas_evidence_types:Audit%20reports", + "@type" : "http://www.w3.org/2004/02/skos/core#Concept", + "broader" : "mas_evidence_types:Other%20evidence", + "prefLabel" : { + "@language" : "en", + "@value" : "Audit reports" + } + }, { + "@id" : "mas_evidence_types:CONCEPT-Evidence_type", + "@type" : "http://www.w3.org/2004/02/skos/core#Concept", + "prefLabel" : { + "@language" : "en", + "@value" : "Evidence type" + } + }, { + "@id" : "mas_evidence_types:CONCEPT-Guidance_and_advice", + "@type" : "http://www.w3.org/2004/02/skos/core#Concept", + "broader" : "mas_evidence_types:CONCEPT-Evidence_type", + "prefLabel" : { + "@language" : "en", + "@value" : "Guidance and advice" + } + }, { + "@id" : "mas_evidence_types:Care%20pathways", + "@type" : "http://www.w3.org/2004/02/skos/core#Concept", + "broader" : "mas_evidence_types:Other%20evidence", + "prefLabel" : { + "@language" : "en", + "@value" : "Care pathways" + } + }, { + "@id" : "mas_evidence_types:Commissioning%20guides", + "@type" : "http://www.w3.org/2004/02/skos/core#Concept", + "broader" : "mas_evidence_types:CONCEPT-Guidance_and_advice", + "prefLabel" : { + "@language" : "en", + "@value" : "Commissioning guides" + } + }, { + "@id" : "mas_evidence_types:Drug%20best%20practice%20guidance", + "@type" : "http://www.w3.org/2004/02/skos/core#Concept", + "broader" : "mas_evidence_types:CONCEPT-Guidance_and_advice", + "prefLabel" : { + "@language" : "en", + "@value" : "Drug best practice guidance" + } + }, { + "@id" : "mas_evidence_types:Drug%20costs", + "@type" : "http://www.w3.org/2004/02/skos/core#Concept", + "broader" : "mas_evidence_types:Other%20evidence", + "prefLabel" : { + "@language" : "en", + "@value" : "Drug costs" + } + }, { + "@id" : "mas_evidence_types:Drug%20horizon%20scanning", + "@type" : "http://www.w3.org/2004/02/skos/core#Concept", + "broader" : "mas_evidence_types:Other%20evidence", + "prefLabel" : { + "@language" : "en", + "@value" : "Drug horizon scanning" + } + }, { + "@id" : "mas_evidence_types:Drug%20prescribing", + "@type" : "http://www.w3.org/2004/02/skos/core#Concept", + "broader" : "mas_evidence_types:CONCEPT-Guidance_and_advice", + "prefLabel" : { + "@language" : "en", + "@value" : "Drug prescribing" + } + }, { + "@id" : "mas_evidence_types:Drug%20regulatory%20and%20marketing", + "@type" : "http://www.w3.org/2004/02/skos/core#Concept", + "broader" : "mas_evidence_types:Other%20evidence", + "prefLabel" : { + "@language" : "en", + "@value" : "Drug regulatory and marketing" + } + }, { + "@id" : "mas_evidence_types:Drug%2Fmedicines%20management", + "@type" : "http://www.w3.org/2004/02/skos/core#Concept", + "broader" : "mas_evidence_types:Other%20evidence", + "prefLabel" : { + "@language" : "en", + "@value" : "Drug/medicines management" + } + }, { + "@id" : "mas_evidence_types:Effective%20practice%20examples", + "@type" : "http://www.w3.org/2004/02/skos/core#Concept", + "broader" : "mas_evidence_types:Other%20evidence", + "prefLabel" : { + "@language" : "en", + "@value" : "Effective practice examples" + } + }, { + "@id" : "mas_evidence_types:Evidence%20summaries", + "@type" : "http://www.w3.org/2004/02/skos/core#Concept", + "broader" : "mas_evidence_types:Evidence%20summary", + "prefLabel" : { + "@language" : "en", + "@value" : "Evidence summaries" + } + }, { + "@id" : "mas_evidence_types:Evidence%20summary", + "@type" : "http://www.w3.org/2004/02/skos/core#Concept", + "broader" : "mas_evidence_types:CONCEPT-Evidence_type", + "prefLabel" : { + "@language" : "en", + "@value" : "Evidence summary" + } + }, { + "@id" : "mas_evidence_types:Evidence%20updates", + "@type" : "http://www.w3.org/2004/02/skos/core#Concept", + "broader" : "mas_evidence_types:Evidence%20summary", + "prefLabel" : { + "@language" : "en", + "@value" : "Evidence updates" + } + }, { + "@id" : "mas_evidence_types:Evidence-based%20management%20reports", + "@type" : "http://www.w3.org/2004/02/skos/core#Concept", + "broader" : "mas_evidence_types:Other%20evidence", + "prefLabel" : { + "@language" : "en", + "@value" : "Evidence-based management reports" + } + }, { + "@id" : "mas_evidence_types:Eyes%20on%20evidence%20commentaries", + "@type" : "http://www.w3.org/2004/02/skos/core#Concept", + "broader" : "mas_evidence_types:Evidence%20summary", + "prefLabel" : { + "@language" : "en", + "@value" : "Eyes on evidence commentaries" + } + }, { + "@id" : "mas_evidence_types:Guidance", + "@type" : "http://www.w3.org/2004/02/skos/core#Concept", + "broader" : "mas_evidence_types:CONCEPT-Guidance_and_advice", + "prefLabel" : { + "@language" : "en", + "@value" : "Guidance" + } + }, { + "@id" : "mas_evidence_types:Health%20technology%20assessments", + "@type" : "http://www.w3.org/2004/02/skos/core#Concept", + "broader" : "mas_evidence_types:Systematic%20review", + "prefLabel" : { + "@language" : "en", + "@value" : "Health technology assessments" + } + }, { + "@id" : "mas_evidence_types:Implementation%20support%20tools", + "@type" : "http://www.w3.org/2004/02/skos/core#Concept", + "broader" : "mas_evidence_types:Other%20evidence", + "prefLabel" : { + "@language" : "en", + "@value" : "Implementation support tools" + } + }, { + "@id" : "mas_evidence_types:Learning%20materials", + "@type" : "http://www.w3.org/2004/02/skos/core#Concept", + "broader" : "mas_evidence_types:Other%20evidence", + "prefLabel" : { + "@language" : "en", + "@value" : "Learning materials" + } + }, { + "@id" : "mas_evidence_types:MAS_evidence_type", + "@type" : "http://www.w3.org/2004/02/skos/core#ConceptScheme", + "hasTopConcept" : "mas_evidence_types:CONCEPT-Evidence_type", + "prefLabel" : { + "@language" : "en", + "@value" : "MAS evidence type" + } + }, { + "@id" : "mas_evidence_types:Media%20and%20commentaries", + "@type" : "http://www.w3.org/2004/02/skos/core#Concept", + "broader" : "mas_evidence_types:CONCEPT-Evidence_type", + "prefLabel" : { + "@language" : "en", + "@value" : "Media and commentaries" + } + }, { + "@id" : "mas_evidence_types:Medicines%20Q%20%26%20A", + "@type" : "http://www.w3.org/2004/02/skos/core#Concept", + "broader" : "mas_evidence_types:Evidence%20summary", + "prefLabel" : { + "@language" : "en", + "@value" : "Medicines Q & A" + } + }, { + "@id" : "mas_evidence_types:Medicines%20evidence%20commentaries", + "@type" : "http://www.w3.org/2004/02/skos/core#Concept", + "broader" : "mas_evidence_types:Evidence%20summary", + "prefLabel" : { + "@language" : "en", + "@value" : "Medicines evidence commentaries" + } + }, { + "@id" : "mas_evidence_types:Ongoing%20or%20unpublished%20research", + "@type" : "http://www.w3.org/2004/02/skos/core#Concept", + "broader" : "mas_evidence_types:Primary%20research", + "prefLabel" : { + "@language" : "en", + "@value" : "Ongoing or unpublished research" + } + }, { + "@id" : "mas_evidence_types:Other%20economic%20evaluations", + "@type" : "http://www.w3.org/2004/02/skos/core#Concept", + "broader" : "mas_evidence_types:Other%20evidence", + "prefLabel" : { + "@language" : "en", + "@value" : "Other economic evaluations" + } + }, { + "@id" : "mas_evidence_types:Other%20evidence", + "@type" : "http://www.w3.org/2004/02/skos/core#Concept", + "broader" : "mas_evidence_types:CONCEPT-Evidence_type", + "prefLabel" : { + "@language" : "en", + "@value" : "Other evidence" + } + }, { + "@id" : "mas_evidence_types:Other%20primary%20research", + "@type" : "http://www.w3.org/2004/02/skos/core#Concept", + "broader" : "mas_evidence_types:Primary%20research", + "prefLabel" : { + "@language" : "en", + "@value" : "Other primary research" + } + }, { + "@id" : "mas_evidence_types:Patient%20decision%20aids", + "@type" : "http://www.w3.org/2004/02/skos/core#Concept", + "broader" : "mas_evidence_types:Other%20evidence", + "prefLabel" : { + "@language" : "en", + "@value" : "Patient decision aids" + } + }, { + "@id" : "mas_evidence_types:Patient%20information", + "@type" : "http://www.w3.org/2004/02/skos/core#Concept", + "broader" : "mas_evidence_types:Other%20evidence", + "prefLabel" : { + "@language" : "en", + "@value" : "Patient information" + } + }, { + "@id" : "mas_evidence_types:Policy", + "@type" : "http://www.w3.org/2004/02/skos/core#Concept", + "broader" : "mas_evidence_types:CONCEPT-Evidence_type", + "prefLabel" : { + "@language" : "en", + "@value" : "Policy" + } + }, { + "@id" : "mas_evidence_types:Population%20intelligence", + "@type" : "http://www.w3.org/2004/02/skos/core#Concept", + "broader" : "mas_evidence_types:Other%20evidence", + "prefLabel" : { + "@language" : "en", + "@value" : "Population intelligence" + } + }, { + "@id" : "mas_evidence_types:Population%20needs%20assessment", + "@type" : "http://www.w3.org/2004/02/skos/core#Concept", + "broader" : "mas_evidence_types:Other%20evidence", + "prefLabel" : { + "@language" : "en", + "@value" : "Population needs assessment" + } + }, { + "@id" : "mas_evidence_types:Primary%20research", + "@type" : "http://www.w3.org/2004/02/skos/core#Concept", + "broader" : "mas_evidence_types:CONCEPT-Evidence_type", + "prefLabel" : { + "@language" : "en", + "@value" : "Primary research" + } + }, { + "@id" : "mas_evidence_types:Quality%20measures", + "@type" : "http://www.w3.org/2004/02/skos/core#Concept", + "broader" : "mas_evidence_types:Other%20evidence", + "prefLabel" : { + "@language" : "en", + "@value" : "Quality measures" + } + }, { + "@id" : "mas_evidence_types:Randomised%20controlled%20trials", + "@type" : "http://www.w3.org/2004/02/skos/core#Concept", + "broader" : "mas_evidence_types:Primary%20research", + "prefLabel" : { + "@language" : "en", + "@value" : "Randomised controlled trials" + } + }, { + "@id" : "mas_evidence_types:Safety%20alerts", + "@type" : "http://www.w3.org/2004/02/skos/core#Concept", + "broader" : "mas_evidence_types:CONCEPT-Evidence_type", + "prefLabel" : { + "@language" : "en", + "@value" : "Safety alerts" + } + }, { + "@id" : "mas_evidence_types:Systematic%20review", + "@type" : "http://www.w3.org/2004/02/skos/core#Concept", + "broader" : "mas_evidence_types:CONCEPT-Evidence_type", + "prefLabel" : { + "@language" : "en", + "@value" : "Systematic review" + } + }, { + "@id" : "mas_evidence_types:Systematic%20reviews", + "@type" : "http://www.w3.org/2004/02/skos/core#Concept", + "broader" : "mas_evidence_types:Systematic%20review", + "prefLabel" : { + "@language" : "en", + "@value" : "Systematic reviews" + } + }, { + "@id" : "urn:x-evn-master:mas_evidence_types", + "@type" : "owl:Ontology", + "status" : "metadata:UnderDevelopmentStatus", + "defaultNamespace" : "http://nice.org.uk/MAS/evidence_type/", + "newInstancesUserCannotModifyURI" : "0", + "comment" : "A list of evidence types includes in the Medicines Awareness Service", + "label" : "MAS evidence types", + "imports" : [ "http://topbraid.org/skos.shapes", "http://topbraid.org/imported" ] + } ], + "@context" : { + "prefLabel" : { + "@id" : "http://www.w3.org/2004/02/skos/core#prefLabel" + }, + "broader" : { + "@id" : "http://www.w3.org/2004/02/skos/core#broader", + "@type" : "@id" + }, + "imports" : { + "@id" : "http://www.w3.org/2002/07/owl#imports", + "@type" : "@id" + }, + "status" : { + "@id" : "http://topbraid.org/metadata#status", + "@type" : "@id" + }, + "label" : { + "@id" : "http://www.w3.org/2000/01/rdf-schema#label" + }, + "newInstancesUserCannotModifyURI" : { + "@id" : "http://topbraid.org/teamwork#newInstancesUserCannotModifyURI", + "@type" : "http://www.w3.org/2001/XMLSchema#boolean" + }, + "comment" : { + "@id" : "http://www.w3.org/2000/01/rdf-schema#comment" + }, + "defaultNamespace" : { + "@id" : "http://topbraid.org/swa#defaultNamespace" + }, + "hasTopConcept" : { + "@id" : "http://www.w3.org/2004/02/skos/core#hasTopConcept", + "@type" : "@id" + }, + "metadata" : "http://topbraid.org/metadata#", + "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + "owl" : "http://www.w3.org/2002/07/owl#", + "teamwork" : "http://topbraid.org/teamwork#", + "mas_evidence_types" : "http://nice.org.uk/MAS/evidence_type/", + "rdfs" : "http://www.w3.org/2000/01/rdf-schema#" + } +} diff --git a/cms/models/EvidenceType.js b/cms/models/EvidenceType.js index d67e5084..80db1abf 100644 --- a/cms/models/EvidenceType.js +++ b/cms/models/EvidenceType.js @@ -1,4 +1,22 @@ -const keystone = require("keystone"); +const keystone = require("keystone"), + fs = require("fs"), + path = require("path"); + +// Load the JSONLD for evidence types on application load +// so we don't have to hit the file system again and again +let evidenceTypesJsonLd; +fs.readFile( + path.resolve(__dirname, "../evidence-types.jsonld"), + "UTF-8", + function(err, contents) { + if (err) { + loggger.error(err); + throw err; + } + + evidenceTypesJsonLd = JSON.parse(contents); + } +); const Types = keystone.Field.Types; @@ -12,11 +30,7 @@ const EvidenceType = new keystone.List("EvidenceType", { EvidenceType.add({ title: { type: String, required: true }, key: { type: String, required: true }, - oldEPiServerId: { - label: "Old EPiServer ID", - type: Types.Number, - required: false - } + oldEPiServerId: { label: "Old EPiServer ID", type: Types.Number, required: false }, }); EvidenceType.relationship({ @@ -25,6 +39,20 @@ EvidenceType.relationship({ refPath: "evidenceType" }); +EvidenceType.schema.virtual("broaderTitle").get(function() { + const key = this.key, + evidenceTypes = evidenceTypesJsonLd["@graph"], + concept = evidenceTypes.find(e => e["@id"] === key); + + // Assume only 2 levels + const broaderConcept = evidenceTypes.find(e => e["@id"] === concept.broader); + + if(broaderConcept.broader) + return broaderConcept.prefLabel["@value"]; + else + return concept.prefLabel["@value"]; +}); + EvidenceType.defaultColumns = "title, oldEPiServerId, key"; EvidenceType.register(); diff --git a/cms/routes/api/items.js b/cms/routes/api/items.js index 45c5da92..1d5f7c1a 100644 --- a/cms/routes/api/items.js +++ b/cms/routes/api/items.js @@ -1,4 +1,5 @@ const keystone = require("keystone"), + _ = require("lodash"), Items = keystone.list("Item"); const log4js = require("log4js"), @@ -22,7 +23,26 @@ exports.single = function (req, res, next) { return res.notfound("Item not found", notFoundMsg, true); } - res.json(item); + const obj = _.pick(item, [ + "_id", + "updatedAt", + "createdAt", + "slug", + "shortSummary", + "source._id", + "source.title", + "url", + "title", + "comment", + "publicationDate", + "resourceLinks", + "speciality", + "evidenceType._id", + "evidenceType.title", + "evidenceType.key", + "evidenceType.broaderTitle"]); + + return res.json(obj); }); }; From 1b4a38832ae49155884d01ab810039a6df465b33 Mon Sep 17 00:00:00 2001 From: dylan Date: Tue, 14 Jan 2020 16:58:17 +0000 Subject: [PATCH 05/18] MAS-129 Fix test and pluralise Item.Speciality --- cms/models/Item.js | 2 +- cms/routes/api/items.js | 4 ++-- lambda/MAS.Tests/Feeds/multiple-items.json | 13 ++++++++++++- lambda/MAS.Tests/UnitTests/DailyEmailTests.cs | 6 +++--- lambda/MAS/Models/Item.cs | 4 ++-- lambda/MAS/Services/MailService.cs | 2 +- 6 files changed, 21 insertions(+), 10 deletions(-) diff --git a/cms/models/Item.js b/cms/models/Item.js index ef77262f..7476907d 100644 --- a/cms/models/Item.js +++ b/cms/models/Item.js @@ -165,7 +165,7 @@ Item.schema.post("save", async function (doc, next) { .model.findById(doc._id) .populate("source") .populate("evidenceType") - .populate("speciality") + .populate("specialities") .exec(); } catch (err) { logger.error("An error occurred finding item: ", err.message); diff --git a/cms/routes/api/items.js b/cms/routes/api/items.js index 1d5f7c1a..62f9543e 100644 --- a/cms/routes/api/items.js +++ b/cms/routes/api/items.js @@ -10,7 +10,7 @@ exports.single = function (req, res, next) { .findById(req.params.itemId) .populate("source") .populate("evidenceType") - .populate("speciality") + .populate("specialities") .exec(function (err, item) { if (err) { logger.error(`Error getting item with id ${req.params.itemId}`, err); @@ -55,7 +55,7 @@ exports.list = function (req, res) { .find() .populate("source") .populate("evidenceType") - .populate("speciality") + .populate("specialities") .exec(function (err, items) { if (err) { logger.error(`Failed to get list of items`, err); diff --git a/lambda/MAS.Tests/Feeds/multiple-items.json b/lambda/MAS.Tests/Feeds/multiple-items.json index 7cbcfefb..ef0788d5 100644 --- a/lambda/MAS.Tests/Feeds/multiple-items.json +++ b/lambda/MAS.Tests/Feeds/multiple-items.json @@ -17,7 +17,18 @@ "publicationDate": "2019-11-25T13:48:36.000Z", "createdDate": "2019-10-22T15:05:05.927Z", "specialities": [ - "5daf1a5c8a34d485cb05b5ba" + { + "_id": "5e0e4331200a585f718e1ee5", + "key": "be1c2e2f-745e-4a82-b5aa-d4cef4d31a1b", + "title": "Anaesthesia and pain", + "__v": 0 + }, + { + "_id": "5e0e4331200a58dead8e1ee8", + "key": "53fc67a4-46d8-4171-9447-7fcf216c8749", + "title": "Complementary and alternative therapies", + "__v": 0 + } ], "evidenceType": { "_id": "5df7abf383138898ee1f67ef", diff --git a/lambda/MAS.Tests/UnitTests/DailyEmailTests.cs b/lambda/MAS.Tests/UnitTests/DailyEmailTests.cs index 63d7fcb0..a5348edf 100644 --- a/lambda/MAS.Tests/UnitTests/DailyEmailTests.cs +++ b/lambda/MAS.Tests/UnitTests/DailyEmailTests.cs @@ -48,7 +48,7 @@ public override void Dispose() ShortSummary = "Some short summary", ResourceLinks = "", Comment = "", - Speciality = new List + Specialities = new List { new Speciality() { @@ -76,7 +76,7 @@ public override void Dispose() ShortSummary = "Some short summary", ResourceLinks = "", Comment = "", - Speciality = new List + Specialities = new List { new Speciality() { @@ -130,7 +130,7 @@ public void CanCreateEmailWithTwoItemsDifferentEvidenceType() [Fact] public void ItemsWithManySpecialitiesRenderCorrectly() { - exampleItem.Speciality.Add(new Speciality() { Key = "abcd", Title = "Another speciality" }); + exampleItem.Specialities.Add(new Speciality() { Key = "abcd", Title = "Another speciality" }); var items = new List { exampleItem }; var actualHtml = this.MailService.CreateDailyEmailBody(items); diff --git a/lambda/MAS/Models/Item.cs b/lambda/MAS/Models/Item.cs index 203355c6..69ecbe69 100644 --- a/lambda/MAS/Models/Item.cs +++ b/lambda/MAS/Models/Item.cs @@ -14,8 +14,8 @@ public class Item public string Title { get; set; } [Required, JsonRequired] public Source Source { get; set; } - [JsonProperty("speciality")] - public List Speciality { get; set; } + [JsonProperty("specialities")] + public List Specialities { get; set; } [Required, JsonRequired] public EvidenceType EvidenceType { get; set; } public string ShortSummary { get; set; } diff --git a/lambda/MAS/Services/MailService.cs b/lambda/MAS/Services/MailService.cs index 42452e67..3e174889 100644 --- a/lambda/MAS/Services/MailService.cs +++ b/lambda/MAS/Services/MailService.cs @@ -94,7 +94,7 @@ public string CreateDailyEmailBody(IEnumerable items) body.Append("
"); body.Append(item.Source.Title); body.Append("
"); - body.Append(String.Join(" | ", item.Speciality.Select(x => x.Title))); + body.Append(String.Join(" | ", item.Specialities.Select(x => x.Title))); body.Append("
"); body.Append(item.ShortSummary); body.Append("
"); From fb9a9cc2dfe61ed4b12b7103b7f411222bd3e354 Mon Sep 17 00:00:00 2001 From: dylan Date: Wed, 15 Jan 2020 14:59:12 +0000 Subject: [PATCH 06/18] MAS-121 Clean up and add mailcontroller tests --- .../IntergrationTests/DailyEmailTests.cs | 154 ++++++++++++++++++ .../Controllers/MailControllerTests.cs | 63 +++++++ ...TwoItemsDifferentEvidenceType.approved.txt | 1 - ...thTwoItemsSharingEvidenceType.approved.txt | 1 - ...ests.CanCreateSingleItemEmail.approved.txt | 1 - ...nySpecialitiesRenderCorrectly.approved.txt | 1 - lambda/MAS.Tests/UnitTests/DailyEmailTests.cs | 142 ---------------- .../{ => Services}/ContentServiceTests.cs | 0 .../{ => Services}/MailServiceTests.cs | 4 +- .../{ => Services}/ViewRendererTests.cs | 0 lambda/MAS/Controllers/MailController.cs | 25 +-- lambda/MAS/Services/ContentService.cs | 22 +++ lambda/MAS/Services/MailService.cs | 42 +---- lambda/MAS/Views/DailyEmail.cshtml | 36 ++++ lambda/MAS/Views/Shared/_EmailLayout.cshtml | 1 + 15 files changed, 295 insertions(+), 198 deletions(-) create mode 100644 lambda/MAS.Tests/IntergrationTests/DailyEmailTests.cs create mode 100644 lambda/MAS.Tests/UnitTests/Controllers/MailControllerTests.cs delete mode 100644 lambda/MAS.Tests/UnitTests/DailyEmailTests.CanCreateEmailWithTwoItemsDifferentEvidenceType.approved.txt delete mode 100644 lambda/MAS.Tests/UnitTests/DailyEmailTests.CanCreateEmailWithTwoItemsSharingEvidenceType.approved.txt delete mode 100644 lambda/MAS.Tests/UnitTests/DailyEmailTests.CanCreateSingleItemEmail.approved.txt delete mode 100644 lambda/MAS.Tests/UnitTests/DailyEmailTests.ItemsWithManySpecialitiesRenderCorrectly.approved.txt delete mode 100644 lambda/MAS.Tests/UnitTests/DailyEmailTests.cs rename lambda/MAS.Tests/UnitTests/{ => Services}/ContentServiceTests.cs (100%) rename lambda/MAS.Tests/UnitTests/{ => Services}/MailServiceTests.cs (92%) rename lambda/MAS.Tests/UnitTests/{ => Services}/ViewRendererTests.cs (100%) create mode 100644 lambda/MAS/Views/DailyEmail.cshtml create mode 100644 lambda/MAS/Views/Shared/_EmailLayout.cshtml diff --git a/lambda/MAS.Tests/IntergrationTests/DailyEmailTests.cs b/lambda/MAS.Tests/IntergrationTests/DailyEmailTests.cs new file mode 100644 index 00000000..e3545ac5 --- /dev/null +++ b/lambda/MAS.Tests/IntergrationTests/DailyEmailTests.cs @@ -0,0 +1,154 @@ +using MailChimp.Net.Core; +using MailChimp.Net.Interfaces; +using MailChimp.Net.Models; +using MAS.Models; +using MAS.Services; +using MAS.Tests.Infrastructure; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Controllers; +using Microsoft.AspNetCore.Mvc.ViewFeatures; +using Microsoft.AspNetCore.Routing; +using Microsoft.Extensions.Logging; +using Moq; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using System.Web.Mvc; +using Xunit; +using Source = MAS.Models.Source; + +namespace MAS.Tests.IntergrationTests +{ + public class FakeController : Microsoft.AspNetCore.Mvc.Controller + { + + } + + public class DailyEmailTests : TestBase + { + IMailService MailService; + + public DailyEmailTests(IMailService mailService) + { + + MailService = mailService; + } + + public override void Dispose() + { + + } + + Item exampleItem = new Item() + { + Id = "123", + Title = "Some Title", + Slug = "abc", + ShortSummary = "Some short summary", + ResourceLinks = "", + Comment = "", + Specialities = new List + { + new Speciality() + { + Key = "1a", + Title = "Some speciality", + + } + }, + EvidenceType = new EvidenceType() + { + Key = "1b", + Title = "Some evidence type" + }, + Source = new Source() + { + Id = "1c", + Title = "Some source" + } + }; + Item exampleItem2 = new Item() + { + Id = "123", + Title = "Some Title", + Slug = "abc", + ShortSummary = "Some short summary", + ResourceLinks = "", + Comment = "", + Specialities = new List + { + new Speciality() + { + Key = "1a", + Title = "Some speciality 2", + + } + }, + EvidenceType = new EvidenceType() + { + Key = "1c", + Title = "Some evidence type 2" + }, + Source = new Source() + { + Id = "1c", + Title = "Some source" + } + }; + + //[Fact] + //public void CanCreateSingleItemEmail() + //{ + + // var items = new List { exampleItem }; + // var actualHtml = this.MailService.CreateDailyEmailBody(items, mockController.Object); + + // actualHtml.ShouldMatchApproved(); + + //} + + //[Fact] + //public void CanCreateEmailWithTwoItemsSharingEvidenceType() + //{ + // var items = new List { exampleItem, exampleItem }; + // var actualHtml = this.MailService.CreateDailyEmailBody(items, new FakeController()); + + // actualHtml.ShouldMatchApproved(); + + //} + + //[Fact] + //public void CanCreateEmailWithTwoItemsDifferentEvidenceType() + //{ + // var items = new List { exampleItem, exampleItem2 }; + // var actualHtml = ""; + // try + // { + // actualHtml = this.MailService.CreateDailyEmailBody(items, new FakeController()); + // } + // catch(Exception e) + // { + // var p = e.Message; + // } + + + // actualHtml.ShouldMatchApproved(); + + //} + + //[Fact] + //public void ItemsWithManySpecialitiesRenderCorrectly() + //{ + // exampleItem.Specialities.Add(new Speciality() { Key = "abcd", Title = "Another speciality" }); + // var items = new List { exampleItem }; + // var actualHtml = this.MailService.CreateDailyEmailBody(items, new FakeController()); + + // actualHtml.ShouldMatchApproved(); + + //} + + } +} diff --git a/lambda/MAS.Tests/UnitTests/Controllers/MailControllerTests.cs b/lambda/MAS.Tests/UnitTests/Controllers/MailControllerTests.cs new file mode 100644 index 00000000..d7b4f480 --- /dev/null +++ b/lambda/MAS.Tests/UnitTests/Controllers/MailControllerTests.cs @@ -0,0 +1,63 @@ +using MAS.Controllers; +using MAS.Models; +using MAS.Services; +using Microsoft.Extensions.Logging; +using Moq; +using Shouldly; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Xunit; + +namespace MAS.Tests.UnitTests.Controllers +{ + public class MailControllerTests + { + [Fact] + public async void GetDailyItemsForToday() + { + + var mockContentService = new Mock(); + var mailService = new MailController(Mock.Of(), mockContentService.Object, Mock.Of(), Mock.Of>()); + + //Act + await mailService.PutMailAsync(); + + //Assert + mockContentService.Verify(mock => mock.GetDailyItemsAsync(null), Times.Once()); + } + + [Fact] + public async void GetDailyItemsForSpecificDate() + { + DateTime date = new DateTime(2020, 1, 15); + var mockContentService = new Mock(); + var mailService = new MailController(Mock.Of(), mockContentService.Object, Mock.Of(), Mock.Of>()); + + //Act + await mailService.PutMailAsync(date); + + //Assert + mockContentService.Verify(mock => mock.GetDailyItemsAsync(date), Times.Once()); + } + + [Fact] + public async void RendersDailyViewWithContentItems() + { + var items = new List() { }.AsEnumerable(); + var mockContentService = new Mock(); + mockContentService.Setup(x => x.GetDailyItemsAsync(null)).ReturnsAsync(items); + + var mockViewRenderer = new Mock(); + + var mailController = new MailController(Mock.Of(), mockContentService.Object, mockViewRenderer.Object, Mock.Of>()); + + //Act + await mailController.PutMailAsync(); + + //Assert + mockViewRenderer.Verify(mock => mock.RenderViewAsync(mailController, "~/Views/MAD.cshtml", items, false), Times.Once()); + } + } +} diff --git a/lambda/MAS.Tests/UnitTests/DailyEmailTests.CanCreateEmailWithTwoItemsDifferentEvidenceType.approved.txt b/lambda/MAS.Tests/UnitTests/DailyEmailTests.CanCreateEmailWithTwoItemsDifferentEvidenceType.approved.txt deleted file mode 100644 index 3539d7b3..00000000 --- a/lambda/MAS.Tests/UnitTests/DailyEmailTests.CanCreateEmailWithTwoItemsDifferentEvidenceType.approved.txt +++ /dev/null @@ -1 +0,0 @@ -
Some evidence type
Some Title
Some source
Some speciality
Some short summary
SPS Comment
Some evidence type 2
Some Title
Some source
Some speciality 2
Some short summary
SPS Comment
\ No newline at end of file diff --git a/lambda/MAS.Tests/UnitTests/DailyEmailTests.CanCreateEmailWithTwoItemsSharingEvidenceType.approved.txt b/lambda/MAS.Tests/UnitTests/DailyEmailTests.CanCreateEmailWithTwoItemsSharingEvidenceType.approved.txt deleted file mode 100644 index 49775cd6..00000000 --- a/lambda/MAS.Tests/UnitTests/DailyEmailTests.CanCreateEmailWithTwoItemsSharingEvidenceType.approved.txt +++ /dev/null @@ -1 +0,0 @@ -
Some evidence type
Some Title
Some source
Some speciality
Some short summary
SPS Comment
Some Title
Some source
Some speciality
Some short summary
SPS Comment
\ No newline at end of file diff --git a/lambda/MAS.Tests/UnitTests/DailyEmailTests.CanCreateSingleItemEmail.approved.txt b/lambda/MAS.Tests/UnitTests/DailyEmailTests.CanCreateSingleItemEmail.approved.txt deleted file mode 100644 index 92233622..00000000 --- a/lambda/MAS.Tests/UnitTests/DailyEmailTests.CanCreateSingleItemEmail.approved.txt +++ /dev/null @@ -1 +0,0 @@ -
Some evidence type
Some Title
Some source
Some speciality
Some short summary
SPS Comment
\ No newline at end of file diff --git a/lambda/MAS.Tests/UnitTests/DailyEmailTests.ItemsWithManySpecialitiesRenderCorrectly.approved.txt b/lambda/MAS.Tests/UnitTests/DailyEmailTests.ItemsWithManySpecialitiesRenderCorrectly.approved.txt deleted file mode 100644 index e978e75c..00000000 --- a/lambda/MAS.Tests/UnitTests/DailyEmailTests.ItemsWithManySpecialitiesRenderCorrectly.approved.txt +++ /dev/null @@ -1 +0,0 @@ -
Some evidence type
Some Title
Some source
Some speciality | Another speciality
Some short summary
SPS Comment
\ No newline at end of file diff --git a/lambda/MAS.Tests/UnitTests/DailyEmailTests.cs b/lambda/MAS.Tests/UnitTests/DailyEmailTests.cs deleted file mode 100644 index a5348edf..00000000 --- a/lambda/MAS.Tests/UnitTests/DailyEmailTests.cs +++ /dev/null @@ -1,142 +0,0 @@ -using MailChimp.Net.Core; -using MailChimp.Net.Interfaces; -using MailChimp.Net.Models; -using MAS.Models; -using MAS.Services; -using MAS.Tests.Infrastructure; -using Microsoft.Extensions.Logging; -using Moq; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Xunit; -using Source = MAS.Models.Source; - -namespace MAS.Tests.UnitTests -{ - public class DailyEmailTests : TestBase - { - - Mock> MockLogger; - Mock MockMailChimpManager; - MailService MailService; - - public DailyEmailTests() - { - //Arrange - MockLogger = new Mock>(); - - MockMailChimpManager = new Mock(); - MockMailChimpManager.Setup(x => x.Campaigns.AddAsync(It.IsAny())).ReturnsAsync(new Campaign() { Id = "1234" }); - MockMailChimpManager.Setup(x => x.Content.AddOrUpdateAsync(It.IsAny(), It.IsAny())); - MockMailChimpManager.Setup(x => x.Campaigns.SendAsync(It.IsAny())); - - MailService = new MailService(MockMailChimpManager.Object, MockLogger.Object); - } - - public override void Dispose() - { - - } - - Item exampleItem = new Item() - { - Id = "123", - Title = "Some Title", - Slug = "abc", - ShortSummary = "Some short summary", - ResourceLinks = "", - Comment = "", - Specialities = new List - { - new Speciality() - { - Key = "1a", - Title = "Some speciality", - - } - }, - EvidenceType = new EvidenceType() - { - Key = "1b", - Title = "Some evidence type" - }, - Source = new Source() - { - Id = "1c", - Title = "Some source" - } - }; - Item exampleItem2 = new Item() - { - Id = "123", - Title = "Some Title", - Slug = "abc", - ShortSummary = "Some short summary", - ResourceLinks = "", - Comment = "", - Specialities = new List - { - new Speciality() - { - Key = "1a", - Title = "Some speciality 2", - - } - }, - EvidenceType = new EvidenceType() - { - Key = "1c", - Title = "Some evidence type 2" - }, - Source = new Source() - { - Id = "1c", - Title = "Some source" - } - }; - - [Fact] - public void CanCreateSingleItemEmail() - { - var items = new List { exampleItem }; - var actualHtml = this.MailService.CreateDailyEmailBody(items); - - actualHtml.ShouldMatchApproved(); - - } - - [Fact] - public void CanCreateEmailWithTwoItemsSharingEvidenceType() - { - var items = new List { exampleItem, exampleItem }; - var actualHtml = this.MailService.CreateDailyEmailBody(items); - - actualHtml.ShouldMatchApproved(); - - } - - [Fact] - public void CanCreateEmailWithTwoItemsDifferentEvidenceType() - { - var items = new List { exampleItem, exampleItem2 }; - var actualHtml = this.MailService.CreateDailyEmailBody(items); - - actualHtml.ShouldMatchApproved(); - - } - - [Fact] - public void ItemsWithManySpecialitiesRenderCorrectly() - { - exampleItem.Specialities.Add(new Speciality() { Key = "abcd", Title = "Another speciality" }); - var items = new List { exampleItem }; - var actualHtml = this.MailService.CreateDailyEmailBody(items); - - actualHtml.ShouldMatchApproved(); - - } - - } -} diff --git a/lambda/MAS.Tests/UnitTests/ContentServiceTests.cs b/lambda/MAS.Tests/UnitTests/Services/ContentServiceTests.cs similarity index 100% rename from lambda/MAS.Tests/UnitTests/ContentServiceTests.cs rename to lambda/MAS.Tests/UnitTests/Services/ContentServiceTests.cs diff --git a/lambda/MAS.Tests/UnitTests/MailServiceTests.cs b/lambda/MAS.Tests/UnitTests/Services/MailServiceTests.cs similarity index 92% rename from lambda/MAS.Tests/UnitTests/MailServiceTests.cs rename to lambda/MAS.Tests/UnitTests/Services/MailServiceTests.cs index d2674883..0403f3be 100644 --- a/lambda/MAS.Tests/UnitTests/MailServiceTests.cs +++ b/lambda/MAS.Tests/UnitTests/Services/MailServiceTests.cs @@ -29,7 +29,7 @@ public void CreateCampaignAndSendToMailChimp() var mailService = new MailService(mockMailChimpManager.Object, mockLogger.Object); //Act - var response = mailService.CreateAndSendCampaignAsync("Test Subject", "Preview Text", "Body Text"); + var response = mailService.CreateAndSendDailyAsync("Test Subject", "Preview Text", "Body Text"); //Assert response.Exception.ShouldBe(null); @@ -51,7 +51,7 @@ public void ErrorInSendingCampaignShouldThrowError() var mailService = new MailService(mockMailChimpManager.Object, mockLogger.Object); //Act + Assert - Should.Throw(() => mailService.CreateAndSendCampaignAsync("Test Subject", "Preview Text", "Body Text")); + Should.Throw(() => mailService.CreateAndSendDailyAsync("Test Subject", "Preview Text", "Body Text")); } } } diff --git a/lambda/MAS.Tests/UnitTests/ViewRendererTests.cs b/lambda/MAS.Tests/UnitTests/Services/ViewRendererTests.cs similarity index 100% rename from lambda/MAS.Tests/UnitTests/ViewRendererTests.cs rename to lambda/MAS.Tests/UnitTests/Services/ViewRendererTests.cs diff --git a/lambda/MAS/Controllers/MailController.cs b/lambda/MAS/Controllers/MailController.cs index 5521597e..1f0172a3 100644 --- a/lambda/MAS/Controllers/MailController.cs +++ b/lambda/MAS/Controllers/MailController.cs @@ -1,9 +1,9 @@ -using System; -using System.Net; -using System.Net.Http; -using System.Threading.Tasks; -using MAS.Services; +using MAS.Services; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using System; +using System.Linq; +using System.Threading.Tasks; namespace MAS.Controllers { @@ -12,30 +12,35 @@ public class MailController : ControllerBase { private readonly IMailService _mailService; private readonly IContentService _contentService; + private readonly IViewRenderer _viewRenderer; + private readonly ILogger _logger; - public MailController(IMailService mailService, IContentService contentService) + public MailController(IMailService mailService, IContentService contentService, IViewRenderer viewRenderer, ILogger logger) { _mailService = mailService; _contentService = contentService; + _viewRenderer = viewRenderer; + _logger = logger; } //PUT api/mail/daily [HttpPut("daily")] - public async Task PutMailAsync() + public async Task PutMailAsync(DateTime? date = null) { - var items = await _contentService.GetItemsAsync(); + var items = await _contentService.GetDailyItemsAsync(date); - var body = _mailService.CreateDailyEmailBody(items); + var body = await _viewRenderer.RenderViewAsync(this, "~/Views/MAD.cshtml", items.ToList()); var subject = "MAS Email"; var previewText = "This MAS email was created " + DateTime.Now.ToShortDateString(); try { - var campaignId = await _mailService.CreateAndSendCampaignAsync(subject, previewText, body); + var campaignId = await _mailService.CreateAndSendDailyAsync(subject, previewText, body); return Content(campaignId); } catch (Exception e) { + _logger.LogError(e, $"Failed to send daily email - exception: {e.Message}"); return StatusCode(500, new ProblemDetails { Status = 500, Title = e.Message, Detail = e.InnerException?.Message }); } } diff --git a/lambda/MAS/Services/ContentService.cs b/lambda/MAS/Services/ContentService.cs index 2c10abab..2de37dbe 100644 --- a/lambda/MAS/Services/ContentService.cs +++ b/lambda/MAS/Services/ContentService.cs @@ -12,6 +12,7 @@ namespace MAS.Services public interface IContentService { Task> GetItemsAsync(); + Task> GetDailyItemsAsync(DateTime? date = null); } public class ContentService : IContentService @@ -40,5 +41,26 @@ public async Task> GetItemsAsync() } } } + + public async Task> GetDailyItemsAsync(DateTime? date = null) + { + date = date ?? DateTime.Today; + + using (WebClient client = new WebClient()) + { + try + { + var jsonStr = await client.DownloadStringTaskAsync(new Uri(AppSettings.CMSConfig.URI)); //TODO Use date + var item = JsonConvert.DeserializeObject(jsonStr); + return item; + } + catch (Exception e) + { + _logger.LogError($"Failed to get items from CMS - exception: {e.Message}"); + throw new Exception($"Failed to get items from CMS - exception: {e.Message}"); + } + } + } + } } diff --git a/lambda/MAS/Services/MailService.cs b/lambda/MAS/Services/MailService.cs index 3e174889..cd05b3a0 100644 --- a/lambda/MAS/Services/MailService.cs +++ b/lambda/MAS/Services/MailService.cs @@ -3,19 +3,15 @@ using MailChimp.Net.Models; using MAS.Configuration; using Microsoft.Extensions.Logging; -using MAS.Models; using System; using System.Collections.Generic; -using System.Text; using System.Threading.Tasks; -using System.Linq; namespace MAS.Services { public interface IMailService { - Task CreateAndSendCampaignAsync(string subject, string previewText, string body); - string CreateDailyEmailBody(IEnumerable item); + Task CreateAndSendDailyAsync(string subject, string previewText, string body); } public class MailService: IMailService @@ -29,7 +25,7 @@ public MailService(IMailChimpManager mailChimpManager, ILogger logg _logger = logger; } - public async Task CreateAndSendCampaignAsync(string subject, string previewText, string body) + public async Task CreateAndSendDailyAsync(string subject, string previewText, string body) { try { @@ -73,39 +69,5 @@ public async Task CreateAndSendCampaignAsync(string subject, string prev } } - - public string CreateDailyEmailBody(IEnumerable items) - { - var groupedItems = items.GroupBy(x => x.EvidenceType.Title).ToList(); - - var body = new StringBuilder(); - - foreach (var group in groupedItems) - { - var evidenceType = group.Key; - - body.Append("
"); - body.Append("" + evidenceType + ""); - - foreach (var item in group) - { - body.Append("
"); - body.Append(item.Title); - body.Append("
"); - body.Append(item.Source.Title); - body.Append("
"); - body.Append(String.Join(" | ", item.Specialities.Select(x => x.Title))); - body.Append("
"); - body.Append(item.ShortSummary); - body.Append("
"); - body.Append("SPS Comment"); - body.Append("
"); - } - - body.Append("
"); - } - - return body.ToString(); - } } } diff --git a/lambda/MAS/Views/DailyEmail.cshtml b/lambda/MAS/Views/DailyEmail.cshtml new file mode 100644 index 00000000..58765bd9 --- /dev/null +++ b/lambda/MAS/Views/DailyEmail.cshtml @@ -0,0 +1,36 @@ +@using MAS.Models; +@model List +@{ + ViewData["Title"] = "DailyEmail"; + Layout = "~/Views/Shared/_EmailLayout.cshtml"; + + var groupedItems = Model.GroupBy(x => x.EvidenceType.Title).ToList(); +} + +

DailyEmail

+ + + @foreach (var group in groupedItems) + { + var evidenceType = group.Key; + +
+ @evidenceType + + @foreach (var item in group) + { +
+ @item.Title +
+ @item.Source.Title +
+ @String.Join(" | ", item.Specialities.Select(x => x.Title)) +
+ @item.ShortSummary +
+ SPS Comment +
+ } +
+ } + diff --git a/lambda/MAS/Views/Shared/_EmailLayout.cshtml b/lambda/MAS/Views/Shared/_EmailLayout.cshtml new file mode 100644 index 00000000..5f282702 --- /dev/null +++ b/lambda/MAS/Views/Shared/_EmailLayout.cshtml @@ -0,0 +1 @@ + \ No newline at end of file From dda5c61844f89946c6a7881c386c1230bf65c5d5 Mon Sep 17 00:00:00 2001 From: dylan Date: Wed, 15 Jan 2020 16:34:32 +0000 Subject: [PATCH 07/18] Working on mailservice integration tests --- .../Infrastructure/TestAppSettings.cs | 4 +- lambda/MAS.Tests/Infrastructure/TestBase.cs | 38 ++++-- .../IntergrationTests/DailyEmailTests.cs | 119 ++++++++++-------- lambda/MAS.Tests/MAS.Tests.csproj | 11 -- .../UnitTests/Services/ContentServiceTests.cs | 4 +- lambda/MAS.Tests/appsettings.Development.json | 5 - lambda/MAS.Tests/appsettings.json | 35 ------ lambda/MAS/Configuration/CMSConfig.cs | 4 +- lambda/MAS/Services/ContentService.cs | 9 +- lambda/MAS/appsettings.json | 6 +- 10 files changed, 114 insertions(+), 121 deletions(-) delete mode 100644 lambda/MAS.Tests/appsettings.Development.json delete mode 100644 lambda/MAS.Tests/appsettings.json diff --git a/lambda/MAS.Tests/Infrastructure/TestAppSettings.cs b/lambda/MAS.Tests/Infrastructure/TestAppSettings.cs index 20f00c4c..ef2d8fae 100644 --- a/lambda/MAS.Tests/Infrastructure/TestAppSettings.cs +++ b/lambda/MAS.Tests/Infrastructure/TestAppSettings.cs @@ -10,7 +10,7 @@ public static CMSConfig GetInvalidURI() { return new CMSConfig() { - URI = new Uri("file://" + Directory.GetCurrentDirectory() + "/Feeds/nonexistanturl").ToString() + BaseUrl = new Uri("file://" + Directory.GetCurrentDirectory() + "/Feeds/nonexistanturl").ToString() }; } @@ -18,7 +18,7 @@ public static CMSConfig GetMultipleItemsFeed() { return new CMSConfig() { - URI = new Uri("file://" + Directory.GetCurrentDirectory() + "/Feeds/multiple-items.json").ToString() + BaseUrl = new Uri("file://" + Directory.GetCurrentDirectory() + "/Feeds/multiple-items.json").ToString() }; } } diff --git a/lambda/MAS.Tests/Infrastructure/TestBase.cs b/lambda/MAS.Tests/Infrastructure/TestBase.cs index 25e43ecd..b952e0d4 100644 --- a/lambda/MAS.Tests/Infrastructure/TestBase.cs +++ b/lambda/MAS.Tests/Infrastructure/TestBase.cs @@ -1,40 +1,60 @@ using MAS.Configuration; +using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.TestHost; using Microsoft.Extensions.Configuration; using System; using System.Net.Http; +using Microsoft.Extensions.DependencyInjection; namespace MAS.Tests.Infrastructure { public class TestBase : IDisposable { + protected readonly IWebHostBuilder _builder; protected readonly TestServer _server; protected readonly HttpClient _client; - protected readonly IConfigurationRoot _config; + //protected readonly IConfigurationRoot _config; public TestBase() { - _config = new ConfigurationBuilder() - .AddJsonFile("appsettings.json") - .AddUserSecrets("adafe3d8-65fb-49fd-885e-03341a36dc88") - .Build(); + //_config = new ConfigurationBuilder() + // .AddJsonFile("appsettings.json") + // //.AddUserSecrets("adafe3d8-65fb-49fd-885e-03341a36dc88") + // .Build(); - var builder = new WebHostBuilder() + //var builder = new WebHostBuilder() + _builder = WebHost.CreateDefaultBuilder(new string[0]) .UseContentRoot("../../../../MAS") .ConfigureServices(services => { - AppSettings.Configure(services, _config); + //AppSettings.Configure(services, _config); }) .UseEnvironment("Production") .UseStartup(typeof(Startup)); - _server = new TestServer(builder); - _client = _server.CreateClient(); + + _server = new TestServer(_builder); + _client = _server.CreateClient(); + //_client.BaseAddress = new Uri("http://localhost:5000"); + } + + protected void WithImplementation() + where TService : class where TImplementation : class, TService + { + _builder.ConfigureServices(serviceCollection => serviceCollection.AddTransient()); + } + + protected void WithImplementation(TService implementation) + where TService : class + { + _builder.ConfigureServices(serviceCollection => serviceCollection.AddTransient(serviceProvier => implementation)); } public virtual void Dispose() { // Do "global" teardown here; Called after every test method. + _client.Dispose(); + _server.Dispose(); } } } diff --git a/lambda/MAS.Tests/IntergrationTests/DailyEmailTests.cs b/lambda/MAS.Tests/IntergrationTests/DailyEmailTests.cs index e3545ac5..271d0f9a 100644 --- a/lambda/MAS.Tests/IntergrationTests/DailyEmailTests.cs +++ b/lambda/MAS.Tests/IntergrationTests/DailyEmailTests.cs @@ -22,24 +22,17 @@ namespace MAS.Tests.IntergrationTests { - public class FakeController : Microsoft.AspNetCore.Mvc.Controller - { + //public class FakeMailChimpManager : IMailChimpManager + //{ - } + //} public class DailyEmailTests : TestBase { - IMailService MailService; - - public DailyEmailTests(IMailService mailService) - { - MailService = mailService; - } - public override void Dispose() + public DailyEmailTests() { - } Item exampleItem = new Item() @@ -99,56 +92,82 @@ public override void Dispose() } }; - //[Fact] - //public void CanCreateSingleItemEmail() - //{ + [Fact] + public async void SomeTest() + { + var fakeMailService = new Mock(); + + string bodyHtml = string.Empty; + fakeMailService.Setup(s => s.CreateAndSendDailyAsync(It.IsAny(), It.IsAny(), It.IsAny())) + .Callback((subject, previewText, body) => bodyHtml = body) + .ReturnsAsync("1234"); + + WithImplementation(fakeMailService); + + await _client.PutAsync("/api/mail/daily", null); + + bodyHtml.ShouldMatchApproved(); + } + - // var items = new List { exampleItem }; - // var actualHtml = this.MailService.CreateDailyEmailBody(items, mockController.Object); - // actualHtml.ShouldMatchApproved(); - //} - //[Fact] - //public void CanCreateEmailWithTwoItemsSharingEvidenceType() - //{ - // var items = new List { exampleItem, exampleItem }; - // var actualHtml = this.MailService.CreateDailyEmailBody(items, new FakeController()); - // actualHtml.ShouldMatchApproved(); - //} - //[Fact] - //public void CanCreateEmailWithTwoItemsDifferentEvidenceType() - //{ - // var items = new List { exampleItem, exampleItem2 }; - // var actualHtml = ""; - // try - // { - // actualHtml = this.MailService.CreateDailyEmailBody(items, new FakeController()); - // } - // catch(Exception e) - // { - // var p = e.Message; - // } - - // actualHtml.ShouldMatchApproved(); - //} + //[Fact] + //public void CanCreateSingleItemEmail() + //{ - //[Fact] - //public void ItemsWithManySpecialitiesRenderCorrectly() - //{ - // exampleItem.Specialities.Add(new Speciality() { Key = "abcd", Title = "Another speciality" }); - // var items = new List { exampleItem }; - // var actualHtml = this.MailService.CreateDailyEmailBody(items, new FakeController()); + // var items = new List { exampleItem }; + // var actualHtml = this.MailService.CreateDailyEmailBody(items, mockController.Object); - // actualHtml.ShouldMatchApproved(); + // actualHtml.ShouldMatchApproved(); - //} + //} - } + //[Fact] + //public void CanCreateEmailWithTwoItemsSharingEvidenceType() + //{ + // var items = new List { exampleItem, exampleItem }; + // var actualHtml = this.MailService.CreateDailyEmailBody(items, new FakeController()); + + // actualHtml.ShouldMatchApproved(); + + //} + + //[Fact] + //public void CanCreateEmailWithTwoItemsDifferentEvidenceType() + //{ + // var items = new List { exampleItem, exampleItem2 }; + // var actualHtml = ""; + // try + // { + // actualHtml = this.MailService.CreateDailyEmailBody(items, new FakeController()); + // } + // catch(Exception e) + // { + // var p = e.Message; + // } + + + // actualHtml.ShouldMatchApproved(); + + //} + + //[Fact] + //public void ItemsWithManySpecialitiesRenderCorrectly() + //{ + // exampleItem.Specialities.Add(new Speciality() { Key = "abcd", Title = "Another speciality" }); + // var items = new List { exampleItem }; + // var actualHtml = this.MailService.CreateDailyEmailBody(items, new FakeController()); + + // actualHtml.ShouldMatchApproved(); + + //} + + } } diff --git a/lambda/MAS.Tests/MAS.Tests.csproj b/lambda/MAS.Tests/MAS.Tests.csproj index c15eb412..2a80da57 100644 --- a/lambda/MAS.Tests/MAS.Tests.csproj +++ b/lambda/MAS.Tests/MAS.Tests.csproj @@ -10,21 +10,10 @@ - - - - - PreserveNewest - - - PreserveNewest - PreserveNewest - - Always diff --git a/lambda/MAS.Tests/UnitTests/Services/ContentServiceTests.cs b/lambda/MAS.Tests/UnitTests/Services/ContentServiceTests.cs index 4845c4ae..d63be9e9 100644 --- a/lambda/MAS.Tests/UnitTests/Services/ContentServiceTests.cs +++ b/lambda/MAS.Tests/UnitTests/Services/ContentServiceTests.cs @@ -23,7 +23,7 @@ public async Task ReadMultipleItems() var contentService = new ContentService(mockLogger.Object); //Act - var result = await contentService.GetItemsAsync(); + var result = await contentService.GetAllItemsAsync(); //Assert result.Count().ShouldBe(4); @@ -43,7 +43,7 @@ public async Task InvalidURIThrowsError() var contentService = new ContentService(mockLogger.Object); //Act + Assert - await Should.ThrowAsync(() => contentService.GetItemsAsync()); + await Should.ThrowAsync(() => contentService.GetAllItemsAsync()); } } } diff --git a/lambda/MAS.Tests/appsettings.Development.json b/lambda/MAS.Tests/appsettings.Development.json deleted file mode 100644 index 38fb825a..00000000 --- a/lambda/MAS.Tests/appsettings.Development.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "AWS": { - "Region": "eu-west-1" - } -} \ No newline at end of file diff --git a/lambda/MAS.Tests/appsettings.json b/lambda/MAS.Tests/appsettings.json deleted file mode 100644 index d9f81bc1..00000000 --- a/lambda/MAS.Tests/appsettings.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "Logging": { - "RabbitMQHost": "", - "RabbitMQPort": "", - "IncludeScopes": false, - "LogFilePath": "Serilog-{Date}.json", - "LogLevel": { - "Default": "Debug", - "System": "Information", - "Microsoft": "Information" - }, - "UseRabbit": true, - "UseFile": true - }, - "AppSettings": { - "Environment": { - "Name": "local" - } - }, - "AWS": { - "AccessKey": "don't add this or anything else that needs securing. see here: https://docs.microsoft.com/en-us/aspnet/core/security/app-secrets?tabs=visual-studio", - "SecretKey": "", - "ServiceURL": "", - "BucketName": "" - }, - "CMS": { - "URI": "don't add this or anything else that needs securing. see here: https://docs.microsoft.com/en-us/aspnet/core/security/app-secrets?tabs=visual-studio" - }, - "MailChimp": { - "ApiKey": "don't add this or anything else that needs securing. see here: https://docs.microsoft.com/en-us/aspnet/core/security/app-secrets?tabs=visual-studio", - "ListId": "", - "DailyTemplateId": "", - "CampaignFolderId": "" - } -} \ No newline at end of file diff --git a/lambda/MAS/Configuration/CMSConfig.cs b/lambda/MAS/Configuration/CMSConfig.cs index a7679e92..d007ae1e 100644 --- a/lambda/MAS/Configuration/CMSConfig.cs +++ b/lambda/MAS/Configuration/CMSConfig.cs @@ -2,6 +2,8 @@ { public class CMSConfig { - public string URI { get; set; } + public string BaseUrl { get; set; } + public string AllItemsPath { get; set; } + public string DailyItemsPath { get; set; } } } diff --git a/lambda/MAS/Services/ContentService.cs b/lambda/MAS/Services/ContentService.cs index 2de37dbe..809343de 100644 --- a/lambda/MAS/Services/ContentService.cs +++ b/lambda/MAS/Services/ContentService.cs @@ -11,7 +11,7 @@ namespace MAS.Services { public interface IContentService { - Task> GetItemsAsync(); + Task> GetAllItemsAsync(); Task> GetDailyItemsAsync(DateTime? date = null); } @@ -24,13 +24,13 @@ public ContentService(ILogger logger) _logger = logger; } - public async Task> GetItemsAsync() + public async Task> GetAllItemsAsync() { using (WebClient client = new WebClient()) { try { - var jsonStr = await client.DownloadStringTaskAsync(new Uri(AppSettings.CMSConfig.URI)); + var jsonStr = await client.DownloadStringTaskAsync(new Uri(AppSettings.CMSConfig.BaseUrl + AppSettings.CMSConfig.AllItemsPath)); var item = JsonConvert.DeserializeObject(jsonStr); return item; } @@ -48,9 +48,10 @@ public async Task> GetDailyItemsAsync(DateTime? date = null) using (WebClient client = new WebClient()) { + var path = string.Format(AppSettings.CMSConfig.DailyItemsPath, date.Value.ToString("yyyy-MM-dd")); try { - var jsonStr = await client.DownloadStringTaskAsync(new Uri(AppSettings.CMSConfig.URI)); //TODO Use date + var jsonStr = await client.DownloadStringTaskAsync(new Uri(AppSettings.CMSConfig.BaseUrl + path)); var item = JsonConvert.DeserializeObject(jsonStr); return item; } diff --git a/lambda/MAS/appsettings.json b/lambda/MAS/appsettings.json index 09fba1e0..9f5cbf9f 100644 --- a/lambda/MAS/appsettings.json +++ b/lambda/MAS/appsettings.json @@ -26,12 +26,14 @@ "StaticURL": "" }, "CMS": { - "URI": "don't add this or anything else that needs securing. see here: https://docs.microsoft.com/en-us/aspnet/core/security/app-secrets?tabs=visual-studio" + "BaseUrl": "don't add this or anything else that needs securing. see here: https://docs.microsoft.com/en-us/aspnet/core/security/app-secrets?tabs=visual-studio", + "AllItemsPath": "/items", + "DailyItemsPath": "/items/daily/{0}" }, "MailChimp": { "ApiKey": "don't add this or anything else that needs securing. see here: https://docs.microsoft.com/en-us/aspnet/core/security/app-secrets?tabs=visual-studio", "ListId": "", - "DailyTemplateId": "", + "DailyTemplateId": 0, "CampaignFolderId": "" } } \ No newline at end of file From 7804e5c79152bb61889c1fa8d1f8c7b4c7bdf9cb Mon Sep 17 00:00:00 2001 From: Ian Routledge Date: Thu, 16 Jan 2020 11:10:21 +0000 Subject: [PATCH 08/18] MAS-129 Reafctor tests --- .../{multiple-items.json => all-items.json} | 0 .../MAS.Tests/Feeds/daily-items-single.json | 42 +++++ lambda/MAS.Tests/Feeds/daily-items.json | 162 ++++++++++++++++ .../MASWebApplicationFactory.cs | 46 +++++ .../Infrastructure/TestAppSettings.cs | 36 ++-- lambda/MAS.Tests/Infrastructure/TestBase.cs | 54 ++---- ....EmailBodyHtmlMatchesApproved.approved.txt | 53 ++++++ ...lMatchesApprovedForSingleItem.approved.txt | 20 ++ .../IntegrationTests/DailyEmailTests.cs | 93 ++++++++++ .../IntegrationTests/MailControllerTests.cs | 44 +++++ ...ts.PutCMSItemSavesItemIntoS3.approved.txt} | 4 +- .../IntegrationTests/StaticContentTests.cs | 68 +++++++ .../ContentControllerTests.cs | 72 -------- .../IntergrationTests/DailyEmailTests.cs | 173 ------------------ .../IntergrationTests/MailControllerTests.cs | 45 ----- lambda/MAS.Tests/MAS.Tests.csproj | 24 ++- .../Controllers/MailControllerTests.cs | 2 +- .../UnitTests/Services/ContentServiceTests.cs | 6 +- .../UnitTests/Services/MailServiceTests.cs | 4 +- lambda/MAS.Tests/appsettings.test.json | 15 ++ lambda/MAS/Controllers/MailController.cs | 2 +- lambda/MAS/Views/DailyEmail.cshtml | 2 +- 22 files changed, 618 insertions(+), 349 deletions(-) rename lambda/MAS.Tests/Feeds/{multiple-items.json => all-items.json} (100%) create mode 100644 lambda/MAS.Tests/Feeds/daily-items-single.json create mode 100644 lambda/MAS.Tests/Feeds/daily-items.json create mode 100644 lambda/MAS.Tests/Infrastructure/MASWebApplicationFactory.cs create mode 100644 lambda/MAS.Tests/IntegrationTests/DailyEmailTests.EmailBodyHtmlMatchesApproved.approved.txt create mode 100644 lambda/MAS.Tests/IntegrationTests/DailyEmailTests.EmailBodyHtmlMatchesApprovedForSingleItem.approved.txt create mode 100644 lambda/MAS.Tests/IntegrationTests/DailyEmailTests.cs create mode 100644 lambda/MAS.Tests/IntegrationTests/MailControllerTests.cs rename lambda/MAS.Tests/{IntergrationTests/ContentControllerTests.PutCMSItemSavesItemIntoS3.approved.txt => IntegrationTests/StaticContentTests.PutCMSItemSavesItemIntoS3.approved.txt} (96%) create mode 100644 lambda/MAS.Tests/IntegrationTests/StaticContentTests.cs delete mode 100644 lambda/MAS.Tests/IntergrationTests/ContentControllerTests.cs delete mode 100644 lambda/MAS.Tests/IntergrationTests/DailyEmailTests.cs delete mode 100644 lambda/MAS.Tests/IntergrationTests/MailControllerTests.cs create mode 100644 lambda/MAS.Tests/appsettings.test.json diff --git a/lambda/MAS.Tests/Feeds/multiple-items.json b/lambda/MAS.Tests/Feeds/all-items.json similarity index 100% rename from lambda/MAS.Tests/Feeds/multiple-items.json rename to lambda/MAS.Tests/Feeds/all-items.json diff --git a/lambda/MAS.Tests/Feeds/daily-items-single.json b/lambda/MAS.Tests/Feeds/daily-items-single.json new file mode 100644 index 00000000..e1537f99 --- /dev/null +++ b/lambda/MAS.Tests/Feeds/daily-items-single.json @@ -0,0 +1,42 @@ +[ + { + "_id": "5daf1aa18a34d4bb8405b5e0", + "slug": "testing-789", + "source": { + "_id": "5de526fc43e373581b7810c8", + "oldEPiServerId": 2, + "title": "ACP Journal Club", + "__v": 0 + }, + "shortSummary": "testing", + "resourceLinks": "http://localhost:3010/keystone/items", + "url": "https://www.nhs.uk/", + "title": "Wonder Drug", + "__v": 0, + "comment": "

A

", + "publicationDate": "2019-11-25T13:48:36.000Z", + "createdDate": "2019-10-22T15:05:05.927Z", + "specialities": [ + { + "_id": "5e0e4331200a585f718e1ee5", + "key": "be1c2e2f-745e-4a82-b5aa-d4cef4d31a1b", + "title": "Anaesthesia and pain", + "__v": 0 + }, + { + "_id": "5e0e4331200a58dead8e1ee8", + "key": "53fc67a4-46d8-4171-9447-7fcf216c8749", + "title": "Complementary and alternative therapies", + "__v": 0 + } + ], + "evidenceType": { + "_id": "5df7abf383138898ee1f67ef", + "title": "Safety alerts", + "key": "mas_evidence_types:Safety%20alerts", + "oldEPiServerId": 778708, + "__v": 0 + }, + "isInitial": false + } +] \ No newline at end of file diff --git a/lambda/MAS.Tests/Feeds/daily-items.json b/lambda/MAS.Tests/Feeds/daily-items.json new file mode 100644 index 00000000..ef0788d5 --- /dev/null +++ b/lambda/MAS.Tests/Feeds/daily-items.json @@ -0,0 +1,162 @@ +[ + { + "_id": "5daf1aa18a34d4bb8405b5e0", + "slug": "testing-789", + "source": { + "_id": "5de526fc43e373581b7810c8", + "oldEPiServerId": 2, + "title": "ACP Journal Club", + "__v": 0 + }, + "shortSummary": "testing", + "resourceLinks": "http://localhost:3010/keystone/items", + "url": "https://www.nhs.uk/", + "title": "Wonder Drug", + "__v": 0, + "comment": "

A

", + "publicationDate": "2019-11-25T13:48:36.000Z", + "createdDate": "2019-10-22T15:05:05.927Z", + "specialities": [ + { + "_id": "5e0e4331200a585f718e1ee5", + "key": "be1c2e2f-745e-4a82-b5aa-d4cef4d31a1b", + "title": "Anaesthesia and pain", + "__v": 0 + }, + { + "_id": "5e0e4331200a58dead8e1ee8", + "key": "53fc67a4-46d8-4171-9447-7fcf216c8749", + "title": "Complementary and alternative therapies", + "__v": 0 + } + ], + "evidenceType": { + "_id": "5df7abf383138898ee1f67ef", + "title": "Safety alerts", + "key": "mas_evidence_types:Safety%20alerts", + "oldEPiServerId": 778708, + "__v": 0 + }, + "isInitial": false + }, + { + "_id": "5db02d6d8a34d43bde05b5e1", + "slug": "my-medicine", + "source": { + "_id": "5de5261b8ce66f142e44cd08", + "oldEPiServerId": 1394, + "title": "ADHD Awareness Month", + "__v": 0 + }, + "shortSummary": "this is my medicine", + "resourceLinks": "http://localhost:3010/keystone/items", + "url": "https://www.nice.org.uk/", + "title": "MY MEDICINE", + "__v": 0, + "comment": "

this is my comment

", + "publicationDate": "2019-11-23T13:48:36.000Z", + "createdDate": "2019-10-23T10:38:43.000Z", + "specialities": [ + { + "_id": "5e0e4331200a585f718e1ee5", + "key": "be1c2e2f-745e-4a82-b5aa-d4cef4d31a1b", + "title": "Anaesthesia and pain", + "__v": 0 + }, + { + "_id": "5e0e4331200a58dead8e1ee8", + "key": "53fc67a4-46d8-4171-9447-7fcf216c8749", + "title": "Complementary and alternative therapies", + "__v": 0 + } + ], + "evidenceType": { + "_id": "5df7abf383138898ee1f67ef", + "title": "Safety alerts", + "key": "mas_evidence_types:Safety%20alerts", + "oldEPiServerId": 778708, + "__v": 0 + }, + "isInitial": false + }, + { + "_id": "5db031d78a34d4a69905b5e3", + "slug": "hello-world", + "source": { + "_id": "5de52a02c676eee677e97f41", + "oldEPiServerId": 2, + "title": "Substance Misuse Management in General Practice", + "__v": 0 + }, + "shortSummary": "test", + "resourceLinks": "http://localhost:3010/keystone/items", + "url": "https://www.bbc.co.uk/", + "title": "Hello world", + "__v": 0, + "comment": "

comment

", + "publicationDate": "2019-10-23T00:00:00.000Z", + "createdDate": "2019-10-23T10:56:40.000Z", + "specialities": [ + { + "_id": "5e0e4331200a585f718e1ee5", + "key": "be1c2e2f-745e-4a82-b5aa-d4cef4d31a1b", + "title": "Anaesthesia and pain", + "__v": 0 + }, + { + "_id": "5e0e4331200a58dead8e1ee8", + "key": "53fc67a4-46d8-4171-9447-7fcf216c8749", + "title": "Complementary and alternative therapies", + "__v": 0 + } + ], + "evidenceType": { + "_id": "5df7abf383138898ee1f67ef", + "title": "Safety alerts", + "key": "mas_evidence_types:Safety%20alerts", + "oldEPiServerId": 778708, + "__v": 0 + }, + "isInitial": false + }, + { + "_id": "5db6cbcc8a34d4ca5905b5e4", + "slug": "hello-there", + "source": { + "_id": "5de52a01c676ee589ae97d2a", + "oldEPiServerId": 2, + "title": "Journal of Surgical Oncology", + "__v": 0 + }, + "shortSummary": "hello", + "resourceLinks": "http://localhost:3010/keystone/items", + "url": "https://www.google.com/", + "title": "A Placebo", + "__v": 0, + "comment": "

test

", + "publicationDate": "2019-10-28T00:00:00.000Z", + "createdDate": "2019-10-28T11:07:11.000Z", + "specialities": [ + { + "_id": "5e0e4331200a585f718e1ee5", + "key": "be1c2e2f-745e-4a82-b5aa-d4cef4d31a1b", + "title": "Anaesthesia and pain", + "__v": 0 + }, + { + "_id": "5e0e4331200a58dead8e1ee8", + "key": "53fc67a4-46d8-4171-9447-7fcf216c8749", + "title": "Complementary and alternative therapies", + "__v": 0 + } + ], + "evidenceType": { + "_id": "5df7abf383138898ee1f67ef", + "title": "Safety alerts", + "key": "mas_evidence_types:Safety%20alerts", + "oldEPiServerId": 778708, + "__v": 0 + }, + "isInitial": false + } +] \ No newline at end of file diff --git a/lambda/MAS.Tests/Infrastructure/MASWebApplicationFactory.cs b/lambda/MAS.Tests/Infrastructure/MASWebApplicationFactory.cs new file mode 100644 index 00000000..38b75bca --- /dev/null +++ b/lambda/MAS.Tests/Infrastructure/MASWebApplicationFactory.cs @@ -0,0 +1,46 @@ +using MAS.Configuration; +using Microsoft.AspNetCore; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc.Testing; +using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net.Http; +using System.Threading.Tasks; + +namespace MAS.Tests.Infrastructure +{ + /// + /// See https://github.com/aspnet/AspNetCore.Docs/issues/7063#issuecomment-414661566 + /// + public class MASWebApplicationFactory : WebApplicationFactory + { + protected override IWebHostBuilder CreateWebHostBuilder() + { + return WebHost.CreateDefaultBuilder() + .UseStartup() + .UseLambdaServer(); + } + + protected override void ConfigureWebHost(IWebHostBuilder builder) + { + var projectDir = Directory.GetCurrentDirectory(); + + builder.ConfigureAppConfiguration((context, conf) => + { + conf.AddJsonFile(Path.Combine(projectDir, "appsettings.test.json")); + }); + + builder.ConfigureTestServices(services => { + AppSettings.CMSConfig.BaseUrl = new Uri("file://" + projectDir + "/Feeds").ToString(); + }); + + base.ConfigureWebHost(builder); + } + } +} diff --git a/lambda/MAS.Tests/Infrastructure/TestAppSettings.cs b/lambda/MAS.Tests/Infrastructure/TestAppSettings.cs index ef2d8fae..1fb859af 100644 --- a/lambda/MAS.Tests/Infrastructure/TestAppSettings.cs +++ b/lambda/MAS.Tests/Infrastructure/TestAppSettings.cs @@ -4,22 +4,36 @@ namespace MAS.Tests.Infrastructure { - public class TestAppSettings + /// + /// Use this in integration tests to override CMS config per test e.g. AppSettings.CMSConfig = TestAppSettings.CMS.InvalidURI; + /// + public static class TestAppSettings { - public static CMSConfig GetInvalidURI() + public static class CMS { - return new CMSConfig() + public static CMSConfig Default { - BaseUrl = new Uri("file://" + Directory.GetCurrentDirectory() + "/Feeds/nonexistanturl").ToString() - }; - } + get + { + return new CMSConfig() + { + BaseUrl = new Uri("file://" + Directory.GetCurrentDirectory() + "/Feeds").ToString(), + AllItemsPath = "/all-items.json", + DailyItemsPath = "/daily-items.json", + }; + } + } - public static CMSConfig GetMultipleItemsFeed() - { - return new CMSConfig() + public static CMSConfig InvalidURI { - BaseUrl = new Uri("file://" + Directory.GetCurrentDirectory() + "/Feeds/multiple-items.json").ToString() - }; + get + { + return new CMSConfig() + { + BaseUrl = new Uri("file://" + Directory.GetCurrentDirectory() + "/Feeds/nonexistanturl").ToString() + }; + } + } } } } diff --git a/lambda/MAS.Tests/Infrastructure/TestBase.cs b/lambda/MAS.Tests/Infrastructure/TestBase.cs index b952e0d4..bec7c97b 100644 --- a/lambda/MAS.Tests/Infrastructure/TestBase.cs +++ b/lambda/MAS.Tests/Infrastructure/TestBase.cs @@ -6,55 +6,41 @@ using System; using System.Net.Http; using Microsoft.Extensions.DependencyInjection; +using Xunit; +using Microsoft.AspNetCore.Mvc.Testing; +using Microsoft.Extensions.DependencyInjection.Extensions; namespace MAS.Tests.Infrastructure { - public class TestBase : IDisposable + public abstract class TestBase : IDisposable { - protected readonly IWebHostBuilder _builder; - protected readonly TestServer _server; - protected readonly HttpClient _client; - //protected readonly IConfigurationRoot _config; + protected readonly MASWebApplicationFactory _factory; public TestBase() { - //_config = new ConfigurationBuilder() - // .AddJsonFile("appsettings.json") - // //.AddUserSecrets("adafe3d8-65fb-49fd-885e-03341a36dc88") - // .Build(); - - //var builder = new WebHostBuilder() - _builder = WebHost.CreateDefaultBuilder(new string[0]) - .UseContentRoot("../../../../MAS") - .ConfigureServices(services => - { - //AppSettings.Configure(services, _config); - }) - .UseEnvironment("Production") - .UseStartup(typeof(Startup)); - - _server = new TestServer(_builder); - _client = _server.CreateClient(); - //_client.BaseAddress = new Uri("http://localhost:5000"); + _factory = new MASWebApplicationFactory(); } - protected void WithImplementation() - where TService : class where TImplementation : class, TService + protected WebApplicationFactory WithImplementation(TService implementation) { - _builder.ConfigureServices(serviceCollection => serviceCollection.AddTransient()); - } + return _factory.WithWebHostBuilder(builder => + { + builder.ConfigureTestServices(services => + { + var serviceProvider = services.BuildServiceProvider(); - protected void WithImplementation(TService implementation) - where TService : class - { - _builder.ConfigureServices(serviceCollection => serviceCollection.AddTransient(serviceProvier => implementation)); + var descriptor = + new ServiceDescriptor( + typeof(TService), implementation); + + services.Replace(descriptor); + }); + }); } public virtual void Dispose() { - // Do "global" teardown here; Called after every test method. - _client.Dispose(); - _server.Dispose(); + _factory.Dispose(); } } } diff --git a/lambda/MAS.Tests/IntegrationTests/DailyEmailTests.EmailBodyHtmlMatchesApproved.approved.txt b/lambda/MAS.Tests/IntegrationTests/DailyEmailTests.EmailBodyHtmlMatchesApproved.approved.txt new file mode 100644 index 00000000..aed03a43 --- /dev/null +++ b/lambda/MAS.Tests/IntegrationTests/DailyEmailTests.EmailBodyHtmlMatchesApproved.approved.txt @@ -0,0 +1,53 @@ + +

DailyEmail

+ + +
+ Safety alerts + +
+ Wonder Drug +
+ ACP Journal Club +
+ Anaesthesia and pain | Complementary and alternative therapies +
+ testing +
+ SPS Comment +
+
+ MY MEDICINE +
+ ADHD Awareness Month +
+ Anaesthesia and pain | Complementary and alternative therapies +
+ this is my medicine +
+ SPS Comment +
+
+ Hello world +
+ Substance Misuse Management in General Practice +
+ Anaesthesia and pain | Complementary and alternative therapies +
+ test +
+ SPS Comment +
+
+ A Placebo +
+ Journal of Surgical Oncology +
+ Anaesthesia and pain | Complementary and alternative therapies +
+ hello +
+ SPS Comment +
+
+ diff --git a/lambda/MAS.Tests/IntegrationTests/DailyEmailTests.EmailBodyHtmlMatchesApprovedForSingleItem.approved.txt b/lambda/MAS.Tests/IntegrationTests/DailyEmailTests.EmailBodyHtmlMatchesApprovedForSingleItem.approved.txt new file mode 100644 index 00000000..634797a8 --- /dev/null +++ b/lambda/MAS.Tests/IntegrationTests/DailyEmailTests.EmailBodyHtmlMatchesApprovedForSingleItem.approved.txt @@ -0,0 +1,20 @@ + +

DailyEmail

+ + +
+ Safety alerts + +
+ Wonder Drug +
+ ACP Journal Club +
+ Anaesthesia and pain | Complementary and alternative therapies +
+ testing +
+ SPS Comment +
+
+ diff --git a/lambda/MAS.Tests/IntegrationTests/DailyEmailTests.cs b/lambda/MAS.Tests/IntegrationTests/DailyEmailTests.cs new file mode 100644 index 00000000..53c57ce1 --- /dev/null +++ b/lambda/MAS.Tests/IntegrationTests/DailyEmailTests.cs @@ -0,0 +1,93 @@ +using MailChimp.Net.Core; +using MailChimp.Net.Interfaces; +using MailChimp.Net.Models; +using MAS.Configuration; +using MAS.Models; +using MAS.Services; +using MAS.Tests.Infrastructure; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Controllers; +using Microsoft.AspNetCore.Mvc.Testing; +using Microsoft.AspNetCore.Mvc.ViewFeatures; +using Microsoft.AspNetCore.Routing; +using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Extensions.Logging; +using Moq; +using Shouldly; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using System.Web.Mvc; +using Xunit; +using Source = MAS.Models.Source; + +namespace MAS.Tests.IntegrationTests +{ + public class TestMailService : IMailService + { + public Task CreateAndSendDailyAsync(string subject, string previewText, string body) + { + throw new NotImplementedException(); + } + } + + public class DailyEmailTests : TestBase + { + [Fact] + public async void EmailBodyHtmlMatchesApproved() + { + // Arrange + var fakeMailService = new Mock(); + + string bodyHtml = string.Empty; + fakeMailService.Setup(s => s.CreateAndSendDailyAsync(It.IsAny(), It.IsAny(), It.IsAny())) + .Callback((subject, previewText, body) => bodyHtml = body) + .ReturnsAsync("1234"); + + var client = WithImplementation(fakeMailService.Object).CreateClient(); + + // Act + var response = await client.PutAsync("/api/mail/daily", null); + + // Assert + response.StatusCode.ShouldBe(System.Net.HttpStatusCode.OK); + + var responseText = await response.Content.ReadAsStringAsync(); + responseText.ShouldBe("1234"); + + bodyHtml.ShouldMatchApproved(); + } + + [Fact] + public async void EmailBodyHtmlMatchesApprovedForSingleItem() + { + // Arrange + var fakeMailService = new Mock(); + + string bodyHtml = string.Empty; + fakeMailService.Setup(s => s.CreateAndSendDailyAsync(It.IsAny(), It.IsAny(), It.IsAny())) + .Callback((subject, previewText, body) => bodyHtml = body) + .ReturnsAsync("1234"); + + var client = WithImplementation(fakeMailService.Object).CreateClient(); + + AppSettings.CMSConfig.DailyItemsPath = "/daily-items-single.json"; + + // Act + var response = await client.PutAsync("/api/mail/daily", null); + + // Assert + response.StatusCode.ShouldBe(System.Net.HttpStatusCode.OK); + + var responseText = await response.Content.ReadAsStringAsync(); + responseText.ShouldBe("1234"); + + bodyHtml.ShouldMatchApproved(); + } + } +} diff --git a/lambda/MAS.Tests/IntegrationTests/MailControllerTests.cs b/lambda/MAS.Tests/IntegrationTests/MailControllerTests.cs new file mode 100644 index 00000000..9616a263 --- /dev/null +++ b/lambda/MAS.Tests/IntegrationTests/MailControllerTests.cs @@ -0,0 +1,44 @@ +using System.Threading.Tasks; +using Xunit; +using Shouldly; +using MAS.Tests.Infrastructure; +using System.Text; +using System; +using MAS.Configuration; +using System.Net.Http.Headers; +using System.Net.Http; +using MailChimp.Net.Models; +using Newtonsoft.Json; + +namespace MAS.Tests.IntegrationTests.Mail +{ + //public class MailControllerTests : TestBase + //{ + // [Fact] + // public async Task PutRequestCreatesAndSendsCampaign() + // { + // //Arrange + // const string mailChimpCampaignsURI = "https://us5.api.mailchimp.com/3.0/campaigns/"; + + // var authValue = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.UTF8.GetBytes($"anystring:{AppSettings.MailConfig.ApiKey}"))); + // var client = new HttpClient() + // { + // DefaultRequestHeaders = { Authorization = authValue } + // }; + + // //Act + // var response = await _client.PutAsync("/api/mail/daily", null); + + // //Get campaign to check if it saved + // var campaignId = await response.Content.ReadAsStringAsync(); + // var campaign = await client.GetAsync(mailChimpCampaignsURI + campaignId); + // var campaignJson = await campaign.Content.ReadAsStringAsync(); + // var campaignResult = JsonConvert.DeserializeObject(campaignJson); + + // // Assert + // response.StatusCode.ShouldBe(System.Net.HttpStatusCode.OK); + // campaignResult.Status.ShouldNotBeNull(); + // campaignResult.Status.ShouldNotBe("Draft"); + // } + //} +} diff --git a/lambda/MAS.Tests/IntergrationTests/ContentControllerTests.PutCMSItemSavesItemIntoS3.approved.txt b/lambda/MAS.Tests/IntegrationTests/StaticContentTests.PutCMSItemSavesItemIntoS3.approved.txt similarity index 96% rename from lambda/MAS.Tests/IntergrationTests/ContentControllerTests.PutCMSItemSavesItemIntoS3.approved.txt rename to lambda/MAS.Tests/IntegrationTests/StaticContentTests.PutCMSItemSavesItemIntoS3.approved.txt index 4f56ef60..d1a2aef8 100644 --- a/lambda/MAS.Tests/IntergrationTests/ContentControllerTests.PutCMSItemSavesItemIntoS3.approved.txt +++ b/lambda/MAS.Tests/IntegrationTests/StaticContentTests.PutCMSItemSavesItemIntoS3.approved.txt @@ -18,10 +18,10 @@ - + - + diff --git a/lambda/MAS.Tests/IntegrationTests/StaticContentTests.cs b/lambda/MAS.Tests/IntegrationTests/StaticContentTests.cs new file mode 100644 index 00000000..39b8957a --- /dev/null +++ b/lambda/MAS.Tests/IntegrationTests/StaticContentTests.cs @@ -0,0 +1,68 @@ +using System.Threading.Tasks; +using Xunit; +using MAS.Tests.Infrastructure; +using Shouldly; +using Amazon.S3; +using Amazon; +using MAS.Configuration; +using System.IO; +using MAS.Models; +using Amazon.S3.Model; +using Newtonsoft.Json; +using System.Text; +using System.Net.Http; +using Microsoft.AspNetCore.Mvc.Testing; +using Moq; +using System.Threading; + +namespace MAS.Tests.IntegrationTests +{ + public class StaticContentTests : TestBase + { + private Item item = new Item() + { + Id = "1234", + Slug = "effect-of-vit-d", + URL = "www.website.com", + Title = "Effect of Vitamin D and Omega-3 Fatty Acid Supplementation on Kidney Function in Patients With Type 2 Diabetes: A Randomized Clinical Trial", + ShortSummary = "RCT (n=1,312) found that among adults with type 2 diabetes, supplementation with vitamin D3 or omega-3 fatty acids, compared with placebo, resulted in no significant difference in change in eGFR at 5 years.", + Source = new Source() + { + Id = "789", + Title = "Journal of the American Medical Association" + }, + EvidenceType = new EvidenceType + { + Key = "mas_evidence_types:Safety%20alerts", + Title = "Safety alerts" + }, + Comment = "A related editorial discusses this research and details previous epidemiological studies that suggest improved outcomes with vitamin D supplementation in various clinical scenarios. It states that contrasting the results of this study and its predecessor vitamin D trials with the impressive body of epidemiological research that implicated vitamin D deficiency in various adverse health outcomes offers a stark lesson on the chasm between association and causation. Editorial authors highlight that it now seems safe to conclude that many prior epidemiological associations between vitamin D deficiency and adverse health outcomes were driven by unmeasured residual confounding or reverse causality.", + ResourceLinks = "

Vitamin D and Health Outcomes - Then Came the Randomized Clinical Trials

\r\n

Link 2

" + + }; + + [Fact] + public async Task PutCMSItemSavesItemIntoS3() + { + // Arrange + var fakeS3Service = new Mock(); + + PutObjectRequest putObjectRequest = null; + fakeS3Service.Setup(s => s.PutObjectAsync(It.IsAny(), default(CancellationToken))) + .Callback((pOR, cT) => putObjectRequest = pOR) + .ReturnsAsync(new PutObjectResponse { HttpStatusCode = System.Net.HttpStatusCode.OK }); + + var client = WithImplementation(fakeS3Service.Object).CreateClient(); + var content = new StringContent(JsonConvert.SerializeObject(item), Encoding.UTF8, "application/json"); + + // Act + var response = await client.PutAsync("/api/content/", content); + + // Assert + response.StatusCode.ShouldBe(System.Net.HttpStatusCode.OK); + putObjectRequest.Key.ShouldBe("effect-of-vit-d.html"); + putObjectRequest.ContentBody.ShouldMatchApproved(); + } + + } +} diff --git a/lambda/MAS.Tests/IntergrationTests/ContentControllerTests.cs b/lambda/MAS.Tests/IntergrationTests/ContentControllerTests.cs deleted file mode 100644 index 2dc7100b..00000000 --- a/lambda/MAS.Tests/IntergrationTests/ContentControllerTests.cs +++ /dev/null @@ -1,72 +0,0 @@ -using System.Threading.Tasks; -using Xunit; -using MAS.Tests.Infrastructure; -using Shouldly; -using Amazon.S3; -using Amazon; -using MAS.Configuration; -using System.IO; -using MAS.Models; -using Amazon.S3.Model; -using Newtonsoft.Json; -using System.Text; -using System.Net.Http; - -namespace MAS.Tests.IntergrationTests.Content -{ - public class ContentControllerTests : TestBase - { - [Fact] - public async Task PutCMSItemSavesItemIntoS3() - { - //Arrange - AmazonS3Config config = new AmazonS3Config() - { - RegionEndpoint = RegionEndpoint.EUWest1, - ServiceURL = AppSettings.AWSConfig.ServiceURL, - ForcePathStyle = true - }; - AmazonS3Client s3Client = new AmazonS3Client(AppSettings.AWSConfig.AccessKey, AppSettings.AWSConfig.SecretKey, config); - - Item item = new Item() - { - Id = "1234", - Slug = "Effect-of-vit-d", - URL = "www.website.com", - Title = "Effect of Vitamin D and Omega-3 Fatty Acid Supplementation on Kidney Function in Patients With Type 2 Diabetes: A Randomized Clinical Trial", - ShortSummary = "RCT (n=1,312) found that among adults with type 2 diabetes, supplementation with vitamin D3 or omega-3 fatty acids, compared with placebo, resulted in no significant difference in change in eGFR at 5 years.", - Source = new Source() - { - Id = "789", - Title = "Journal of the American Medical Association" - }, - EvidenceType = new EvidenceType - { - Key = "mas_evidence_types:Safety%20alerts", - Title = "Safety alerts" - }, - Comment = "A related editorial discusses this research and details previous epidemiological studies that suggest improved outcomes with vitamin D supplementation in various clinical scenarios. It states that contrasting the results of this study and its predecessor vitamin D trials with the impressive body of epidemiological research that implicated vitamin D deficiency in various adverse health outcomes offers a stark lesson on the chasm between association and causation. Editorial authors highlight that it now seems safe to conclude that many prior epidemiological associations between vitamin D deficiency and adverse health outcomes were driven by unmeasured residual confounding or reverse causality.", - ResourceLinks = "

Vitamin D and Health Outcomes - Then Came the Randomized Clinical Trials

\r\n

Link 2

" - - }; - - var content = new StringContent(JsonConvert.SerializeObject(item), Encoding.UTF8, "application/json"); - - //Act - var response = await _client.PutAsync("/api/content/", content); - - // Assert - response.StatusCode.ShouldBe(System.Net.HttpStatusCode.OK); - - using (var bucketItem = await s3Client.GetObjectAsync(AppSettings.AWSConfig.BucketName, "Effect-of-vit-d.html")) - { - using (StreamReader reader = new StreamReader(bucketItem.ResponseStream)) - { - string contents = reader.ReadToEnd(); - contents.ShouldMatchApproved(); - } - } - } - - } -} diff --git a/lambda/MAS.Tests/IntergrationTests/DailyEmailTests.cs b/lambda/MAS.Tests/IntergrationTests/DailyEmailTests.cs deleted file mode 100644 index 271d0f9a..00000000 --- a/lambda/MAS.Tests/IntergrationTests/DailyEmailTests.cs +++ /dev/null @@ -1,173 +0,0 @@ -using MailChimp.Net.Core; -using MailChimp.Net.Interfaces; -using MailChimp.Net.Models; -using MAS.Models; -using MAS.Services; -using MAS.Tests.Infrastructure; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.Controllers; -using Microsoft.AspNetCore.Mvc.ViewFeatures; -using Microsoft.AspNetCore.Routing; -using Microsoft.Extensions.Logging; -using Moq; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using System.Web.Mvc; -using Xunit; -using Source = MAS.Models.Source; - -namespace MAS.Tests.IntergrationTests -{ - //public class FakeMailChimpManager : IMailChimpManager - //{ - - //} - - public class DailyEmailTests : TestBase - { - - - public DailyEmailTests() - { - } - - Item exampleItem = new Item() - { - Id = "123", - Title = "Some Title", - Slug = "abc", - ShortSummary = "Some short summary", - ResourceLinks = "", - Comment = "", - Specialities = new List - { - new Speciality() - { - Key = "1a", - Title = "Some speciality", - - } - }, - EvidenceType = new EvidenceType() - { - Key = "1b", - Title = "Some evidence type" - }, - Source = new Source() - { - Id = "1c", - Title = "Some source" - } - }; - Item exampleItem2 = new Item() - { - Id = "123", - Title = "Some Title", - Slug = "abc", - ShortSummary = "Some short summary", - ResourceLinks = "", - Comment = "", - Specialities = new List - { - new Speciality() - { - Key = "1a", - Title = "Some speciality 2", - - } - }, - EvidenceType = new EvidenceType() - { - Key = "1c", - Title = "Some evidence type 2" - }, - Source = new Source() - { - Id = "1c", - Title = "Some source" - } - }; - - [Fact] - public async void SomeTest() - { - var fakeMailService = new Mock(); - - string bodyHtml = string.Empty; - fakeMailService.Setup(s => s.CreateAndSendDailyAsync(It.IsAny(), It.IsAny(), It.IsAny())) - .Callback((subject, previewText, body) => bodyHtml = body) - .ReturnsAsync("1234"); - - WithImplementation(fakeMailService); - - await _client.PutAsync("/api/mail/daily", null); - - bodyHtml.ShouldMatchApproved(); - } - - - - - - - - - - - //[Fact] - //public void CanCreateSingleItemEmail() - //{ - - // var items = new List { exampleItem }; - // var actualHtml = this.MailService.CreateDailyEmailBody(items, mockController.Object); - - // actualHtml.ShouldMatchApproved(); - - //} - - //[Fact] - //public void CanCreateEmailWithTwoItemsSharingEvidenceType() - //{ - // var items = new List { exampleItem, exampleItem }; - // var actualHtml = this.MailService.CreateDailyEmailBody(items, new FakeController()); - - // actualHtml.ShouldMatchApproved(); - - //} - - //[Fact] - //public void CanCreateEmailWithTwoItemsDifferentEvidenceType() - //{ - // var items = new List { exampleItem, exampleItem2 }; - // var actualHtml = ""; - // try - // { - // actualHtml = this.MailService.CreateDailyEmailBody(items, new FakeController()); - // } - // catch(Exception e) - // { - // var p = e.Message; - // } - - - // actualHtml.ShouldMatchApproved(); - - //} - - //[Fact] - //public void ItemsWithManySpecialitiesRenderCorrectly() - //{ - // exampleItem.Specialities.Add(new Speciality() { Key = "abcd", Title = "Another speciality" }); - // var items = new List { exampleItem }; - // var actualHtml = this.MailService.CreateDailyEmailBody(items, new FakeController()); - - // actualHtml.ShouldMatchApproved(); - - //} - - } -} diff --git a/lambda/MAS.Tests/IntergrationTests/MailControllerTests.cs b/lambda/MAS.Tests/IntergrationTests/MailControllerTests.cs deleted file mode 100644 index 096ce967..00000000 --- a/lambda/MAS.Tests/IntergrationTests/MailControllerTests.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System.Threading.Tasks; -using Xunit; -using Shouldly; -using MAS.Tests.Infrastructure; -using System.Text; -using System; -using MAS.Configuration; -using System.Net.Http.Headers; -using System.Net.Http; -using MailChimp.Net.Models; -using Newtonsoft.Json; - -namespace MAS.Tests.IntergrationTests.Mail -{ - public class MailControllerTests : TestBase - { - [Fact] - public async Task PutRequestCreatesAndSendsCampaign() - { - //Arrange - const string mailChimpCampaignsURI = "https://us5.api.mailchimp.com/3.0/campaigns/"; - AppSettings.CMSConfig = TestAppSettings.GetMultipleItemsFeed(); - - var authValue = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.UTF8.GetBytes($"anystring:{AppSettings.MailConfig.ApiKey}"))); - var client = new HttpClient() - { - DefaultRequestHeaders = { Authorization = authValue } - }; - - //Act - var response = await _client.PutAsync("/api/mail/daily", null); - - //Get campaign to check if it saved - var campaignId = await response.Content.ReadAsStringAsync(); - var campaign = await client.GetAsync(mailChimpCampaignsURI + campaignId); - var campaignJson = await campaign.Content.ReadAsStringAsync(); - var campaignResult = JsonConvert.DeserializeObject(campaignJson); - - // Assert - response.StatusCode.ShouldBe(System.Net.HttpStatusCode.OK); - campaignResult.Status.ShouldNotBeNull(); - campaignResult.Status.ShouldNotBe("Draft"); - } - } -} diff --git a/lambda/MAS.Tests/MAS.Tests.csproj b/lambda/MAS.Tests/MAS.Tests.csproj index 2a80da57..19dee507 100644 --- a/lambda/MAS.Tests/MAS.Tests.csproj +++ b/lambda/MAS.Tests/MAS.Tests.csproj @@ -10,14 +10,27 @@
- + + + + + - - Always - + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + @@ -25,7 +38,8 @@ - + + diff --git a/lambda/MAS.Tests/UnitTests/Controllers/MailControllerTests.cs b/lambda/MAS.Tests/UnitTests/Controllers/MailControllerTests.cs index d7b4f480..e339839d 100644 --- a/lambda/MAS.Tests/UnitTests/Controllers/MailControllerTests.cs +++ b/lambda/MAS.Tests/UnitTests/Controllers/MailControllerTests.cs @@ -57,7 +57,7 @@ public async void RendersDailyViewWithContentItems() await mailController.PutMailAsync(); //Assert - mockViewRenderer.Verify(mock => mock.RenderViewAsync(mailController, "~/Views/MAD.cshtml", items, false), Times.Once()); + mockViewRenderer.Verify(mock => mock.RenderViewAsync(mailController, "~/Views/DailyEmail.cshtml", items, false), Times.Once()); } } } diff --git a/lambda/MAS.Tests/UnitTests/Services/ContentServiceTests.cs b/lambda/MAS.Tests/UnitTests/Services/ContentServiceTests.cs index d63be9e9..c60ae0aa 100644 --- a/lambda/MAS.Tests/UnitTests/Services/ContentServiceTests.cs +++ b/lambda/MAS.Tests/UnitTests/Services/ContentServiceTests.cs @@ -11,7 +11,7 @@ namespace MAS.Tests.UnitTests { - public class ContentServiceTests : TestBase + public class ContentServiceTests { [Fact] public async Task ReadMultipleItems() @@ -19,7 +19,7 @@ public async Task ReadMultipleItems() //Arrange var mockLogger = new Mock>(); - AppSettings.CMSConfig = TestAppSettings.GetMultipleItemsFeed(); + AppSettings.CMSConfig = TestAppSettings.CMS.Default; var contentService = new ContentService(mockLogger.Object); //Act @@ -39,7 +39,7 @@ public async Task InvalidURIThrowsError() //Arrange var mockLogger = new Mock>(); - AppSettings.CMSConfig = TestAppSettings.GetInvalidURI(); + AppSettings.CMSConfig = TestAppSettings.CMS.InvalidURI; var contentService = new ContentService(mockLogger.Object); //Act + Assert diff --git a/lambda/MAS.Tests/UnitTests/Services/MailServiceTests.cs b/lambda/MAS.Tests/UnitTests/Services/MailServiceTests.cs index 0403f3be..98c6bde0 100644 --- a/lambda/MAS.Tests/UnitTests/Services/MailServiceTests.cs +++ b/lambda/MAS.Tests/UnitTests/Services/MailServiceTests.cs @@ -13,7 +13,7 @@ namespace MAS.Tests.UnitTests { - public class MailServiceTests : TestBase + public class MailServiceTests { [Fact] public void CreateCampaignAndSendToMailChimp() @@ -28,6 +28,8 @@ public void CreateCampaignAndSendToMailChimp() var mailService = new MailService(mockMailChimpManager.Object, mockLogger.Object); + AppSettings.MailConfig = Mock.Of(); + //Act var response = mailService.CreateAndSendDailyAsync("Test Subject", "Preview Text", "Body Text"); diff --git a/lambda/MAS.Tests/appsettings.test.json b/lambda/MAS.Tests/appsettings.test.json new file mode 100644 index 00000000..8f0b8bf5 --- /dev/null +++ b/lambda/MAS.Tests/appsettings.test.json @@ -0,0 +1,15 @@ +{ + "AppSettings": { + "Environment": { + "Name": "tests" + } + }, + "AWS": { + "StaticUrl": "http://mas-integration-tests/" + }, + "CMS": { + "BaseUrl": "We override this in test setup to point to the Feeds folder on the filesystem", + "AllItemsPath": "/all-items.json", + "DailyItemsPath": "/daily-items.json" + } +} diff --git a/lambda/MAS/Controllers/MailController.cs b/lambda/MAS/Controllers/MailController.cs index 1f0172a3..5d0752b8 100644 --- a/lambda/MAS/Controllers/MailController.cs +++ b/lambda/MAS/Controllers/MailController.cs @@ -29,7 +29,7 @@ public async Task PutMailAsync(DateTime? date = null) { var items = await _contentService.GetDailyItemsAsync(date); - var body = await _viewRenderer.RenderViewAsync(this, "~/Views/MAD.cshtml", items.ToList()); + var body = await _viewRenderer.RenderViewAsync(this, "~/Views/DailyEmail.cshtml", items.ToList()); var subject = "MAS Email"; var previewText = "This MAS email was created " + DateTime.Now.ToShortDateString(); diff --git a/lambda/MAS/Views/DailyEmail.cshtml b/lambda/MAS/Views/DailyEmail.cshtml index 58765bd9..197d0e4b 100644 --- a/lambda/MAS/Views/DailyEmail.cshtml +++ b/lambda/MAS/Views/DailyEmail.cshtml @@ -2,7 +2,7 @@ @model List @{ ViewData["Title"] = "DailyEmail"; - Layout = "~/Views/Shared/_EmailLayout.cshtml"; + //Layout = "~/Views/Shared/_EmailLayout.cshtml"; var groupedItems = Model.GroupBy(x => x.EvidenceType.Title).ToList(); } From c94036c2ed1adc9a9d42fe11943a1813cf7f8c15 Mon Sep 17 00:00:00 2001 From: dylan Date: Thu, 16 Jan 2020 11:23:46 +0000 Subject: [PATCH 09/18] MASD-129 Add mailchimp template --- mailchimp-templates/daily.html | 2539 ++++++++++++++++++++++++++++++++ 1 file changed, 2539 insertions(+) create mode 100644 mailchimp-templates/daily.html diff --git a/mailchimp-templates/daily.html b/mailchimp-templates/daily.html new file mode 100644 index 00000000..811128e8 --- /dev/null +++ b/mailchimp-templates/daily.html @@ -0,0 +1,2539 @@ + + + + + + Medicines awareness service - daily edition + + + + + + + + + +
+
+ + + + + + +
 
+ + + + + + +
+ + + + + + + + + +
+ + + + +
+
+
+
+
+ + + + +
+
+
+
+
+ + + + + + + +
+ + + + + +
+

*|DATE:jS F|*

+

Medicines awareness service - daily

+

The very latest current awareness and evidence-based medicines information

+

Content produced by the Specialist Pharmacy Service - Medicines Information

+
+
+ + +
+ + + + + + + + +
+ + + + + +
+

Specialist Pharmacy Service and NICE bring you the latest current awareness and evidence-based medicines information, direct to your inbox.
Terms and conditions | Contact us +

+
+
+ + + + + + + +
+
+
+ + \ No newline at end of file From ef4e6b2833f028fdfd098db569cd78c7cc72280d Mon Sep 17 00:00:00 2001 From: Ian Routledge Date: Thu, 16 Jan 2020 12:04:58 +0000 Subject: [PATCH 10/18] MAS-129 Fix JS linting errors --- cms/models/EvidenceType.js | 19 ++++++++++++------- cms/models/Item.js | 6 +++--- cms/routes/api/items.js | 11 ++++++----- 3 files changed, 21 insertions(+), 15 deletions(-) diff --git a/cms/models/EvidenceType.js b/cms/models/EvidenceType.js index 80db1abf..db05f9d4 100644 --- a/cms/models/EvidenceType.js +++ b/cms/models/EvidenceType.js @@ -1,6 +1,9 @@ const keystone = require("keystone"), fs = require("fs"), - path = require("path"); + path = require("path"), + log4js = require("log4js"); + +const logger = log4js.getLogger("Item.js"); // Load the JSONLD for evidence types on application load // so we don't have to hit the file system again and again @@ -10,7 +13,7 @@ fs.readFile( "UTF-8", function(err, contents) { if (err) { - loggger.error(err); + logger.error(err); throw err; } @@ -30,7 +33,11 @@ const EvidenceType = new keystone.List("EvidenceType", { EvidenceType.add({ title: { type: String, required: true }, key: { type: String, required: true }, - oldEPiServerId: { label: "Old EPiServer ID", type: Types.Number, required: false }, + oldEPiServerId: { + label: "Old EPiServer ID", + type: Types.Number, + required: false + } }); EvidenceType.relationship({ @@ -47,10 +54,8 @@ EvidenceType.schema.virtual("broaderTitle").get(function() { // Assume only 2 levels const broaderConcept = evidenceTypes.find(e => e["@id"] === concept.broader); - if(broaderConcept.broader) - return broaderConcept.prefLabel["@value"]; - else - return concept.prefLabel["@value"]; + if (broaderConcept.broader) return broaderConcept.prefLabel["@value"]; + else return concept.prefLabel["@value"]; }); EvidenceType.defaultColumns = "title, oldEPiServerId, key"; diff --git a/cms/models/Item.js b/cms/models/Item.js index 7476907d..6b36024c 100644 --- a/cms/models/Item.js +++ b/cms/models/Item.js @@ -95,7 +95,7 @@ Item.add({ staticPath: { type: Types.Text, watch: "title", - value: function () { + value: function() { return ( process.env.STATIC_SITE_PATH + (this.slug || utils.slug(this.title)) + @@ -107,7 +107,7 @@ Item.add({ } }); -Item.schema.pre("validate", function (next) { +Item.schema.pre("validate", function(next) { if (this.isInitial) { this.isInitial = false; next(); @@ -152,7 +152,7 @@ const createWeeklyIfNeeded = async () => { }; // Post save hook to trigger a lambda with the document details -Item.schema.post("save", async function (doc, next) { +Item.schema.post("save", async function(doc, next) { await createWeeklyIfNeeded(); logger.info("Post save, sending request...", doc); diff --git a/cms/routes/api/items.js b/cms/routes/api/items.js index 62f9543e..0b911403 100644 --- a/cms/routes/api/items.js +++ b/cms/routes/api/items.js @@ -5,13 +5,13 @@ const keystone = require("keystone"), const log4js = require("log4js"), logger = log4js.getLogger(); -exports.single = function (req, res, next) { +exports.single = function(req, res, next) { Items.model .findById(req.params.itemId) .populate("source") .populate("evidenceType") .populate("specialities") - .exec(function (err, item) { + .exec(function(err, item) { if (err) { logger.error(`Error getting item with id ${req.params.itemId}`, err); return res.error(err, true); @@ -40,7 +40,8 @@ exports.single = function (req, res, next) { "evidenceType._id", "evidenceType.title", "evidenceType.key", - "evidenceType.broaderTitle"]); + "evidenceType.broaderTitle" + ]); return res.json(obj); }); @@ -49,14 +50,14 @@ exports.single = function (req, res, next) { /** * List Items */ -exports.list = function (req, res) { +exports.list = function(req, res) { // TODO: Pagination Items.model .find() .populate("source") .populate("evidenceType") .populate("specialities") - .exec(function (err, items) { + .exec(function(err, items) { if (err) { logger.error(`Failed to get list of items`, err); return res.error(err, true); From 78f6c65528b09a4daf1e86c96cbbf838e58c4554 Mon Sep 17 00:00:00 2001 From: dylan Date: Thu, 16 Jan 2020 12:25:46 +0000 Subject: [PATCH 11/18] MAS-129 Test getting items for specific date --- .../Feeds/daily-items-2020-01-01.json | 42 +++++++++++++++++++ ...ForSingleItemWithSpecificDate.approved.txt | 20 +++++++++ .../IntegrationTests/DailyEmailTests.cs | 28 +++++++++++++ lambda/MAS.Tests/MAS.Tests.csproj | 5 +++ lambda/MAS/Controllers/MailController.cs | 2 +- 5 files changed, 96 insertions(+), 1 deletion(-) create mode 100644 lambda/MAS.Tests/Feeds/daily-items-2020-01-01.json create mode 100644 lambda/MAS.Tests/IntegrationTests/DailyEmailTests.EmailBodyHtmlMatchesApprovedForSingleItemWithSpecificDate.approved.txt diff --git a/lambda/MAS.Tests/Feeds/daily-items-2020-01-01.json b/lambda/MAS.Tests/Feeds/daily-items-2020-01-01.json new file mode 100644 index 00000000..e1537f99 --- /dev/null +++ b/lambda/MAS.Tests/Feeds/daily-items-2020-01-01.json @@ -0,0 +1,42 @@ +[ + { + "_id": "5daf1aa18a34d4bb8405b5e0", + "slug": "testing-789", + "source": { + "_id": "5de526fc43e373581b7810c8", + "oldEPiServerId": 2, + "title": "ACP Journal Club", + "__v": 0 + }, + "shortSummary": "testing", + "resourceLinks": "http://localhost:3010/keystone/items", + "url": "https://www.nhs.uk/", + "title": "Wonder Drug", + "__v": 0, + "comment": "

A

", + "publicationDate": "2019-11-25T13:48:36.000Z", + "createdDate": "2019-10-22T15:05:05.927Z", + "specialities": [ + { + "_id": "5e0e4331200a585f718e1ee5", + "key": "be1c2e2f-745e-4a82-b5aa-d4cef4d31a1b", + "title": "Anaesthesia and pain", + "__v": 0 + }, + { + "_id": "5e0e4331200a58dead8e1ee8", + "key": "53fc67a4-46d8-4171-9447-7fcf216c8749", + "title": "Complementary and alternative therapies", + "__v": 0 + } + ], + "evidenceType": { + "_id": "5df7abf383138898ee1f67ef", + "title": "Safety alerts", + "key": "mas_evidence_types:Safety%20alerts", + "oldEPiServerId": 778708, + "__v": 0 + }, + "isInitial": false + } +] \ No newline at end of file diff --git a/lambda/MAS.Tests/IntegrationTests/DailyEmailTests.EmailBodyHtmlMatchesApprovedForSingleItemWithSpecificDate.approved.txt b/lambda/MAS.Tests/IntegrationTests/DailyEmailTests.EmailBodyHtmlMatchesApprovedForSingleItemWithSpecificDate.approved.txt new file mode 100644 index 00000000..634797a8 --- /dev/null +++ b/lambda/MAS.Tests/IntegrationTests/DailyEmailTests.EmailBodyHtmlMatchesApprovedForSingleItemWithSpecificDate.approved.txt @@ -0,0 +1,20 @@ + +

DailyEmail

+ + +
+ Safety alerts + +
+ Wonder Drug +
+ ACP Journal Club +
+ Anaesthesia and pain | Complementary and alternative therapies +
+ testing +
+ SPS Comment +
+
+ diff --git a/lambda/MAS.Tests/IntegrationTests/DailyEmailTests.cs b/lambda/MAS.Tests/IntegrationTests/DailyEmailTests.cs index 53c57ce1..363780a9 100644 --- a/lambda/MAS.Tests/IntegrationTests/DailyEmailTests.cs +++ b/lambda/MAS.Tests/IntegrationTests/DailyEmailTests.cs @@ -89,5 +89,33 @@ public async void EmailBodyHtmlMatchesApprovedForSingleItem() bodyHtml.ShouldMatchApproved(); } + + [Fact] + public async void EmailBodyHtmlMatchesApprovedForSingleItemWithSpecificDate() + { + // Arrange + var fakeMailService = new Mock(); + + string bodyHtml = string.Empty; + fakeMailService.Setup(s => s.CreateAndSendDailyAsync(It.IsAny(), It.IsAny(), It.IsAny())) + .Callback((subject, previewText, body) => bodyHtml = body) + .ReturnsAsync("1234"); + + var client = WithImplementation(fakeMailService.Object).CreateClient(); + + AppSettings.CMSConfig.DailyItemsPath = "/daily-items-{0}.json"; + + // Act + var response = await client.PutAsync("/api/mail/daily?date=01-01-2020", null); + + // Assert + response.StatusCode.ShouldBe(System.Net.HttpStatusCode.OK); + + var responseText = await response.Content.ReadAsStringAsync(); + responseText.ShouldBe("1234"); + + bodyHtml.ShouldMatchApproved(); + } + } } diff --git a/lambda/MAS.Tests/MAS.Tests.csproj b/lambda/MAS.Tests/MAS.Tests.csproj index 19dee507..d4bafc9b 100644 --- a/lambda/MAS.Tests/MAS.Tests.csproj +++ b/lambda/MAS.Tests/MAS.Tests.csproj @@ -51,4 +51,9 @@ + + + PreserveNewest + + \ No newline at end of file diff --git a/lambda/MAS/Controllers/MailController.cs b/lambda/MAS/Controllers/MailController.cs index 5d0752b8..e81a8635 100644 --- a/lambda/MAS/Controllers/MailController.cs +++ b/lambda/MAS/Controllers/MailController.cs @@ -23,7 +23,7 @@ public MailController(IMailService mailService, IContentService contentService, _logger = logger; } - //PUT api/mail/daily + //PUT api/mail/daily?date=01-01-2020 [HttpPut("daily")] public async Task PutMailAsync(DateTime? date = null) { From 130f2b77366058ee07a10e03d2ccd0d64b0664ac Mon Sep 17 00:00:00 2001 From: dylan Date: Thu, 16 Jan 2020 15:21:31 +0000 Subject: [PATCH 12/18] MAS-129 Add styling and branding to daily email --- lambda/MAS/Views/DailyEmail.cshtml | 118 +++++++++++++++++++++++------ 1 file changed, 96 insertions(+), 22 deletions(-) diff --git a/lambda/MAS/Views/DailyEmail.cshtml b/lambda/MAS/Views/DailyEmail.cshtml index 197d0e4b..94260d91 100644 --- a/lambda/MAS/Views/DailyEmail.cshtml +++ b/lambda/MAS/Views/DailyEmail.cshtml @@ -5,32 +5,106 @@ //Layout = "~/Views/Shared/_EmailLayout.cshtml"; var groupedItems = Model.GroupBy(x => x.EvidenceType.Title).ToList(); + var specialitiesList = Model.SelectMany(x => x.Specialities).Select(x => x.Title).ToList(); + var specialitiesAsString = String.Join(',', specs); } -

DailyEmail

+ + + + + + +
+ + + + + + + +
+

+ *|INTERESTED:Daily specialities of interest:@specialitiesAsString|* + Articles that match your chosen specialities today + *|ELSE|* + No articles match your chosen specialities today + *|END:INTERESTED|* + - Edit your subscription +

+
+
- @foreach (var group in groupedItems) + +@foreach (var group in groupedItems) +{ + var evidenceType = group.Key; + + @:*| INTERESTED:Daily specialities of interest:specialitiesAsString | * + + + + + + +
+

evidenceType

+
+ + @foreach (var item in group) { - var evidenceType = group.Key; - -
- @evidenceType - - @foreach (var item in group) - { -
- @item.Title -
- @item.Source.Title -
- @String.Join(" | ", item.Specialities.Select(x => x.Title)) -
- @item.ShortSummary -
- SPS Comment -
- } -
+ @:*| INTERESTED:Daily specialities of interest:specialitiesAsString | * + + + + + + +
+ + + + + + + +
+

@item.Title

+

@item.ShortSummary

+

Read SPS commentary

+ +
+
+ @:*| END:INTERESTED | * } +@:*| END:INTERESTED | * +} + + + + + + + +
+ + + + + + + +
+

+ All articles from Medicines awareness services: Daily edition +

+
+
From 3261e8868f3965296b00d792b8bcb983e261765a Mon Sep 17 00:00:00 2001 From: dylan Date: Thu, 16 Jan 2020 15:35:00 +0000 Subject: [PATCH 13/18] MAS-129 Add 'send me everything' option to daily email --- lambda/MAS/Views/DailyEmail.cshtml | 96 ++++++++++++++++++++++-------- 1 file changed, 72 insertions(+), 24 deletions(-) diff --git a/lambda/MAS/Views/DailyEmail.cshtml b/lambda/MAS/Views/DailyEmail.cshtml index 94260d91..2fcbae18 100644 --- a/lambda/MAS/Views/DailyEmail.cshtml +++ b/lambda/MAS/Views/DailyEmail.cshtml @@ -41,7 +41,7 @@ { var evidenceType = group.Key; - @:*| INTERESTED:Daily specialities of interest:specialitiesAsString | * + @:*|INTERESTED:Daily specialities of interest:specialitiesAsString|* @@ -54,7 +54,7 @@ @foreach (var item in group) { - @:*| INTERESTED:Daily specialities of interest:specialitiesAsString | * + @:*|INTERESTED:Daily specialities of interest:specialitiesAsString|*
@@ -82,29 +82,77 @@
- @:*| END:INTERESTED | * + @:*|END:INTERESTED|* } -@:*| END:INTERESTED | * + @:*|END:INTERESTED|* } - - - - - - -
- - - - - - - -
-

- All articles from Medicines awareness services: Daily edition -

-
-
+*|INTERESTED:Send me everything from the Medicines Awareness Daily service:Send me everything|* + + + + + + +
+ + + + + + + +
+

+ All articles from Medicines awareness services: Daily edition +

+
+
+ + @foreach (var group in groupedItems) + { + var evidenceType = group.Key; + + + + + + + +
+

evidenceType

+
+ + @foreach (var item in group) + { + + + + + + +
+ + + + + + + +
+

@item.Title

+

@item.ShortSummary

+

Read SPS commentary

+ +
+
+ } + } +*|END:INTERESTED|* From a8b66830918e7f2b35d95d968b555db8dac5177c Mon Sep 17 00:00:00 2001 From: dylan Date: Thu, 16 Jan 2020 15:45:07 +0000 Subject: [PATCH 14/18] MAS-129 Layout template fixes Remove Sharelinks from daily layout Add link styling fix in daily email via daily layout --- mailchimp-templates/daily.html | 31 ++++--------------------------- 1 file changed, 4 insertions(+), 27 deletions(-) diff --git a/mailchimp-templates/daily.html b/mailchimp-templates/daily.html index 811128e8..a0d0f89a 100644 --- a/mailchimp-templates/daily.html +++ b/mailchimp-templates/daily.html @@ -2389,6 +2389,9 @@ .footer-link{ color:#393939; } + .standard-link{ + color:#0000CD; + } @@ -2491,33 +2494,7 @@

The very latest current awareness and evidence-based - -
- - - - - -
- From a36736693a1ca9c2661ce23e4b75b4af87a71ee7 Mon Sep 17 00:00:00 2001 From: Ian Routledge Date: Thu, 16 Jan 2020 15:46:26 +0000 Subject: [PATCH 15/18] MAS-129 Add daily items endpoint in CMS --- cms/__tests__/routes/api/items.test.js | 93 ++++++++++++++++++++++++++ cms/models/Item.js | 30 ++++++++- cms/routes/api/items.js | 77 ++++++++++++++------- cms/routes/index.js | 1 + cms/routes/middleware.js | 8 +++ lambda/MAS/appsettings.json | 4 +- 6 files changed, 183 insertions(+), 30 deletions(-) create mode 100644 cms/__tests__/routes/api/items.test.js diff --git a/cms/__tests__/routes/api/items.test.js b/cms/__tests__/routes/api/items.test.js new file mode 100644 index 00000000..cd9d2293 --- /dev/null +++ b/cms/__tests__/routes/api/items.test.js @@ -0,0 +1,93 @@ +jest.mock("keystone", () => { + const item = { + model: { + find: jest.fn() + } + }; + + return { + list: () => item + }; +}); + +describe("items", () => { + let keystone, daily, Item, request, response, json; + + beforeEach(() => { + jest.resetModules(); + + keystone = require("keystone"); + Item = keystone.list("Item"); + daily = require("../../../routes/api/items").daily; + request = { params: {} }; + json = jest.fn(); + response = { + json, + badRequest: jest.fn(), + error: jest.fn() + }; + }); + + describe("daily", () => {}); + + it("should return a 400 bad request when the date format is invalid", async () => { + const date = "not a date"; + await daily({ ...request, ...{ params: { date } } }, response); + + expect(response.badRequest).toHaveBeenCalledWith( + "Couldn't get daily items", + "Date 'not a date' is not in the format YYYY-M-D", + true + ); + }); + + it("should search for daily items between start and end of the given date", async () => { + const date = "2020-01-09"; + await daily({ ...request, ...{ params: { date } } }, response); + + expect(Item.model.find).toHaveBeenCalledWith({ + createdAt: { + $gte: new Date(Date.parse("2020-01-09")), + $lt: new Date(Date.parse("2020-01-09 23:59:59.999Z")) + } + }); + }); + + it("should return a 500 JSON error response when there's an error getting the daily items", async () => { + const error = new Error("An error getting daily items"); + + Item.model.find.mockImplementation(() => { + throw error; + }); + + await daily( + { ...request, ...{ params: { date: "2020-01-09" } } }, + response + ); + + expect(response.error).toHaveBeenCalledWith(error, true); + }); + + it("should return the found item as json with whitelist of fields", async () => { + const items = [{ title: "test", excludedField: "not used" }]; + + Item.model.find.mockImplementation(() => { + return { + populate: () => ({ + populate: () => ({ + populate: () => ({ + exec: () => items + }) + }) + }) + }; + }); + + await daily( + { ...request, ...{ params: { date: "2020-01-09" } } }, + response + ); + + expect(json).toHaveBeenCalledWith([{ title: "test" }]); + }); +}); diff --git a/cms/models/Item.js b/cms/models/Item.js index 6b36024c..356fe167 100644 --- a/cms/models/Item.js +++ b/cms/models/Item.js @@ -3,7 +3,8 @@ const keystone = require("keystone"), https = require("https"), http = require("http"), log4js = require("log4js"), - utils = require("keystone-utils"); + utils = require("keystone-utils"), + _ = require("lodash"); const Types = keystone.Field.Types; @@ -107,6 +108,27 @@ Item.add({ } }); +Item.fullResponseFields = [ + "_id", + "title", + "slug", + "url", + "shortSummary", + "comment", + "resourceLinks", + "staticPath", + "source._id", + "source.title", + "specialities", + "evidenceType._id", + "evidenceType.title", + "evidenceType.key", + "evidenceType.broaderTitle", + "publicationDate", + "updatedAt", + "createdAt" +]; + Item.schema.pre("validate", function(next) { if (this.isInitial) { this.isInitial = false; @@ -155,7 +177,7 @@ const createWeeklyIfNeeded = async () => { Item.schema.post("save", async function(doc, next) { await createWeeklyIfNeeded(); - logger.info("Post save, sending request...", doc); + logger.info("Post save, sending request..."); let item; @@ -166,6 +188,7 @@ Item.schema.post("save", async function(doc, next) { .populate("source") .populate("evidenceType") .populate("specialities") + .select(Item.fullResponseFields.join(" ")) .exec(); } catch (err) { logger.error("An error occurred finding item: ", err.message); @@ -176,7 +199,8 @@ Item.schema.post("save", async function(doc, next) { const hostname = process.env.HOST_NAME; const hostport = process.env.HOST_PORT; - const data = JSON.stringify(item); + const data = JSON.stringify(_.pick(item, Item.fullResponseFields)); + logger.debug("Sending: ", data); var options = { hostname: hostname, diff --git a/cms/routes/api/items.js b/cms/routes/api/items.js index 0b911403..a1f7a5a0 100644 --- a/cms/routes/api/items.js +++ b/cms/routes/api/items.js @@ -1,16 +1,22 @@ const keystone = require("keystone"), _ = require("lodash"), + moment = require("moment"), Items = keystone.list("Item"); const log4js = require("log4js"), logger = log4js.getLogger(); -exports.single = function(req, res, next) { +/** + * Single item, given an id + * /api/items/5e205dadc12944a35f24572b + */ +exports.single = function(req, res) { Items.model .findById(req.params.itemId) .populate("source") .populate("evidenceType") .populate("specialities") + .select(Items.fullResponseFields.join(" ")) .exec(function(err, item) { if (err) { logger.error(`Error getting item with id ${req.params.itemId}`, err); @@ -23,40 +29,20 @@ exports.single = function(req, res, next) { return res.notfound("Item not found", notFoundMsg, true); } - const obj = _.pick(item, [ - "_id", - "updatedAt", - "createdAt", - "slug", - "shortSummary", - "source._id", - "source.title", - "url", - "title", - "comment", - "publicationDate", - "resourceLinks", - "speciality", - "evidenceType._id", - "evidenceType.title", - "evidenceType.key", - "evidenceType.broaderTitle" - ]); + const obj = _.pick(item, Items.fullResponseFields); return res.json(obj); }); }; /** - * List Items + * List of all items + * /api/items */ exports.list = function(req, res) { - // TODO: Pagination Items.model .find() - .populate("source") - .populate("evidenceType") - .populate("specialities") + .select("title slug") .exec(function(err, items) { if (err) { logger.error(`Failed to get list of items`, err); @@ -66,3 +52,44 @@ exports.list = function(req, res) { res.json(items); }); }; + +/** + * Daily items for a given date + * /api/items/daily/2020-01-16 + */ +exports.daily = async function(req, res) { + const dateStr = req.params.date, + date = moment(dateStr, "YYYY-M-D", true); + + if (!date.isValid()) { + const errorMessage = `Date '${dateStr}' is not in the format YYYY-M-D`; + logger.error(errorMessage); + return res.badRequest("Couldn't get daily items", errorMessage, true); + } + + const startOfDay = date.clone().startOf("day"), + endOfDay = date.clone().endOf("day"); + + let items; + try { + items = await Items.model + .find({ + createdAt: { $gte: startOfDay.toDate(), $lt: endOfDay.toDate() } + }) + .populate("source") + .populate("evidenceType") + .populate("specialities") + .select(Items.fullResponseFields.join(" ")) + .exec(); + } catch (err) { + logger.error(`Error getting daily items for date ${dateStr}`, err); + return res.error(err, true); + } + + if (items.length === 0) + logger.warn(`Zero daily items found for date ${dateStr}`); + + const obj = _.map(items, _.partialRight(_.pick, Items.fullResponseFields)); + + res.json(obj); +}; diff --git a/cms/routes/index.js b/cms/routes/index.js index 3b4488c5..4559b728 100644 --- a/cms/routes/index.js +++ b/cms/routes/index.js @@ -44,6 +44,7 @@ exports = module.exports = function(app) { // Views app.get("/", routes.views.index); app.get("/api/items/:itemId", routes.api.items.single); + app.get("/api/items/daily/:date", routes.api.items.daily); app.get("/api/items", routes.api.items.list); app.get("/api/specialities/:specialityId", routes.api.specialities.single); app.get("/api/specialities", routes.api.specialities.list); diff --git a/cms/routes/middleware.js b/cms/routes/middleware.js index d94e6d6a..54d68ad1 100644 --- a/cms/routes/middleware.js +++ b/cms/routes/middleware.js @@ -108,5 +108,13 @@ exports.initErrorHandlers = function(req, res, next) { return res.status(404).send(keystone.wrapHTMLError(title, message)); }; + res.badRequest = function(title, message, useJson) { + if (useJson || req.is("json")) { + return res.status(400).json({ title, message }); + } + + return res.status(400).send(keystone.wrapHTMLError(title, message)); + }; + next(); }; diff --git a/lambda/MAS/appsettings.json b/lambda/MAS/appsettings.json index 9f5cbf9f..10618c25 100644 --- a/lambda/MAS/appsettings.json +++ b/lambda/MAS/appsettings.json @@ -27,8 +27,8 @@ }, "CMS": { "BaseUrl": "don't add this or anything else that needs securing. see here: https://docs.microsoft.com/en-us/aspnet/core/security/app-secrets?tabs=visual-studio", - "AllItemsPath": "/items", - "DailyItemsPath": "/items/daily/{0}" + "AllItemsPath": "items", + "DailyItemsPath": "items/daily/{0}" }, "MailChimp": { "ApiKey": "don't add this or anything else that needs securing. see here: https://docs.microsoft.com/en-us/aspnet/core/security/app-secrets?tabs=visual-studio", From 8137313a7bee8e0ebcafc61b3c55be38e3f9574a Mon Sep 17 00:00:00 2001 From: dylan Date: Thu, 16 Jan 2020 16:12:46 +0000 Subject: [PATCH 16/18] MAS-129 Update mailchimp template daily --- mailchimp-templates/daily.html | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/mailchimp-templates/daily.html b/mailchimp-templates/daily.html index a0d0f89a..6c5c3216 100644 --- a/mailchimp-templates/daily.html +++ b/mailchimp-templates/daily.html @@ -2328,10 +2328,16 @@ .article-title{ font-size:16px; font-weight:800; + color:#000000; + padding-top:24px; + line-height:24px; + } + .article-link{ color:#0000CD; + font-size:16px; + font-weight:800; padding-top:24px; line-height:24px; - text-decoration:underline; } .article-description{ font-weight:600; @@ -2458,10 +2464,9 @@

The very latest current awareness and evidence-based - -
- +
+
@@ -2479,7 +2484,7 @@

The very latest current awareness and evidence-based

-
+ @@ -2493,7 +2498,7 @@

The very latest current awareness and evidence-based

- + From 242f5e2014caccff1f3d14d07023a7e3ce4d0ca2 Mon Sep 17 00:00:00 2001 From: dylan Date: Thu, 16 Jan 2020 16:17:21 +0000 Subject: [PATCH 17/18] MAS-129 Update DailyEmail.cshtml --- lambda/MAS/Views/DailyEmail.cshtml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lambda/MAS/Views/DailyEmail.cshtml b/lambda/MAS/Views/DailyEmail.cshtml index 2fcbae18..d4224a57 100644 --- a/lambda/MAS/Views/DailyEmail.cshtml +++ b/lambda/MAS/Views/DailyEmail.cshtml @@ -1,12 +1,10 @@ @using MAS.Models; @model List @{ - ViewData["Title"] = "DailyEmail"; - //Layout = "~/Views/Shared/_EmailLayout.cshtml"; + ViewData["Title"] = "MAS Daily"; var groupedItems = Model.GroupBy(x => x.EvidenceType.Title).ToList(); - var specialitiesList = Model.SelectMany(x => x.Specialities).Select(x => x.Title).ToList(); - var specialitiesAsString = String.Join(',', specs); + var specialitiesAsString = String.Join(',', Model.SelectMany(x => x.Specialities).Select(x => x.Title).ToList()); } From e2700db9639835b04bf6c6116161e0b068268421 Mon Sep 17 00:00:00 2001 From: dylan Date: Thu, 16 Jan 2020 19:40:37 +0000 Subject: [PATCH 18/18] MAS-129 DailyEmail generation --- .../Feeds/daily-items-2020-01-01.json | 113 ++++++++++++----- lambda/MAS/Views/DailyEmail.cshtml | 120 +++++++++--------- mailchimp-templates/daily.html | 7 + 3 files changed, 150 insertions(+), 90 deletions(-) diff --git a/lambda/MAS.Tests/Feeds/daily-items-2020-01-01.json b/lambda/MAS.Tests/Feeds/daily-items-2020-01-01.json index e1537f99..f6e325a6 100644 --- a/lambda/MAS.Tests/Feeds/daily-items-2020-01-01.json +++ b/lambda/MAS.Tests/Feeds/daily-items-2020-01-01.json @@ -1,42 +1,95 @@ [ + { + "_id": "5e208e42306e5d4f5ff39659", + "title": "Item 1", + "slug": "item-1", + "url": "https://www.abc.com", + "shortSummary": "This is some short summary", + "comment": "

This is some comment

", + "resourceLinks": "

https://www.abc.com

", + "staticPath": "item-1.html", + "source": { + "_id": "5e1ddf2a306e5d5250f3904a", + "title": "2020health" + }, + "specialities": [ + { + "_id": "5e1ddf2c306e5d0fa9f39611", + "key": "8f13726e-5635-471f-ad3c-fc910a6ac2b1", + "title": "Cancers", + "__v": 0 + } + ], + "evidenceType": { + "_id": "5e1ddf2c306e5d6eabf39641", + "title": "Evidence summary - Evidence summaries", + "key": "mas_evidence_types:Evidence%20summaries", + "broaderTitle": "Evidence summary" + }, + "publicationDate": "2020-01-16T00:00:00.000Z", + "updatedAt": "2020-01-16T16:25:37.188Z", + "createdAt": "2020-01-16T16:24:34.985Z" + }, { - "_id": "5daf1aa18a34d4bb8405b5e0", - "slug": "testing-789", + "_id": "5e208f7f0348d6060f8d448c", + "title": "Item 2", + "slug": "item-2", + "url": "https://www.google.com", + "shortSummary": "Some short summary", + "comment": "

This is some comment

", + "resourceLinks": "

https://www.google.com/

", + "staticPath": "item-2.html", "source": { - "_id": "5de526fc43e373581b7810c8", - "oldEPiServerId": 2, - "title": "ACP Journal Club", - "__v": 0 + "_id": "5e1ddf2a306e5da2b0f3904d", + "title": "ABL Health Ltd" }, - "shortSummary": "testing", - "resourceLinks": "http://localhost:3010/keystone/items", - "url": "https://www.nhs.uk/", - "title": "Wonder Drug", - "__v": 0, - "comment": "

A

", - "publicationDate": "2019-11-25T13:48:36.000Z", - "createdDate": "2019-10-22T15:05:05.927Z", "specialities": [ { - "_id": "5e0e4331200a585f718e1ee5", - "key": "be1c2e2f-745e-4a82-b5aa-d4cef4d31a1b", - "title": "Anaesthesia and pain", - "__v": 0 - }, - { - "_id": "5e0e4331200a58dead8e1ee8", - "key": "53fc67a4-46d8-4171-9447-7fcf216c8749", - "title": "Complementary and alternative therapies", + "_id": "5e1ddf2c306e5d4e6cf39631", + "key": "d0ea592c-932a-462f-b206-cf454e40fa20", + "title": "Stroke", "__v": 0 } ], "evidenceType": { - "_id": "5df7abf383138898ee1f67ef", - "title": "Safety alerts", - "key": "mas_evidence_types:Safety%20alerts", - "oldEPiServerId": 778708, - "__v": 0 + "_id": "5e1ddf2c306e5d6eabf39641", + "title": "Evidence summary - Evidence summaries", + "key": "mas_evidence_types:Evidence%20summaries", + "broaderTitle": "Evidence summary" }, - "isInitial": false - } + "publicationDate": "2020-01-16T00:00:00.000Z", + "updatedAt": "2020-01-16T16:31:11.333Z", + "createdAt": "2020-01-16T16:29:51.539Z" + }, + { + "_id": "5e208fe00348d67ca08d448d", + "title": "Item 3", + "slug": "item-3", + "url": "https://www.google.com/", + "shortSummary": "Some short summary", + "comment": "

Some comment

", + "resourceLinks": "

https://www.google.com/

\r\n

https://www.google.com/

\r\n

https://www.google.com/

", + "staticPath": "item-3.html", + "source": { + "_id": "5e1ddf2a306e5da2b0f3904d", + "title": "ABL Health Ltd" + }, + "specialities": [ + { + "_id": "5e1ddf2c306e5d7e78f3961f", + "key": "cb0faa85-1781-484e-a265-2715621c1be9", + "title": "Later life", + "__v": 0 + } + ], + "evidenceType": { + "_id": "5e1ddf2c306e5dcf61f39644", + "title": "Evidence summary - Eyes on evidence commentaries", + "key": "mas_evidence_types:Eyes%20on%20evidence%20commentaries", + "broaderTitle": "Evidence summary" + }, + "publicationDate": "2020-01-16T00:00:00.000Z", + "updatedAt": "2020-01-16T16:32:02.535Z", + "createdAt": "2020-01-16T16:31:28.754Z" + } ] \ No newline at end of file diff --git a/lambda/MAS/Views/DailyEmail.cshtml b/lambda/MAS/Views/DailyEmail.cshtml index d4224a57..f9e2fbde 100644 --- a/lambda/MAS/Views/DailyEmail.cshtml +++ b/lambda/MAS/Views/DailyEmail.cshtml @@ -8,7 +8,7 @@ } - +
@@ -39,12 +39,12 @@ { var evidenceType = group.Key; - @:*|INTERESTED:Daily specialities of interest:specialitiesAsString|* + @:*|INTERESTED:Daily specialities of interest:@specialitiesAsString|*
@@ -19,10 +19,10 @@

*|INTERESTED:Daily specialities of interest:@specialitiesAsString|* Articles that match your chosen specialities today - *|ELSE|* + *|ELSE:|* No articles match your chosen specialities today *|END:INTERESTED|* - - Edit your subscription + - Edit your subscription

@@ -52,7 +52,7 @@ @foreach (var item in group) { - @:*|INTERESTED:Daily specialities of interest:specialitiesAsString|* + @:*|INTERESTED:Daily specialities of interest:@specialitiesAsString|*
-

evidenceType

+

@evidenceType

@@ -85,72 +85,72 @@ @:*|END:INTERESTED|* } -*|INTERESTED:Send me everything from the Medicines Awareness Daily service:Send me everything|* -
+*|INTERESTED:Send me everything from Medicines Awareness Daily:Send me everything|* +
+ + + + + +
+ + + + + + + +
+

+ All articles from Medicines awareness services: Daily edition +

+
+
+ +@foreach (var group in groupedItems) +{ + var evidenceType = group.Key; + + - +
- - - - - - - -
-

- All articles from Medicines awareness services: Daily edition -

-
-
+

@evidenceType

+
- @foreach (var group in groupedItems) + @foreach (var item in group) { - var evidenceType = group.Key; - - +
- +
-

evidenceType

-
+ + + + + + + +
+

@item.Title

+

@item.ShortSummary

+

Read SPS commentary

+ +
+
- - @foreach (var item in group) - { - - - - - - -
- - - - - - - -
-

@item.Title

-

@item.ShortSummary

-

Read SPS commentary

- -
-
- } } +} *|END:INTERESTED|* diff --git a/mailchimp-templates/daily.html b/mailchimp-templates/daily.html index 6c5c3216..38e603e4 100644 --- a/mailchimp-templates/daily.html +++ b/mailchimp-templates/daily.html @@ -2398,6 +2398,13 @@ .standard-link{ color:#0000CD; } + .subtle-link + color: inherit; + } + .article-section-header{ + border-top: 1px solid black; + border-bottom: 1px solid black; + }