From 7797107cb939e3daf1a32b75ad9fc7d8f81e09b2 Mon Sep 17 00:00:00 2001 From: ey-mailosaur Date: Mon, 30 Sep 2024 11:49:43 +0100 Subject: [PATCH] Introduced deliverability reporting request/models --- lib/models/blockListResult.js | 9 ++ lib/models/content.js | 15 +++ lib/models/deliverabilityReport.js | 19 +++ lib/models/dnsRecords.js | 9 ++ lib/models/emailAuthenticationResult.js | 10 ++ lib/models/index.d.ts | 153 ++++++++++++++++++++++++ lib/models/index.js | 6 + lib/models/spamAssassinResult.js | 11 ++ lib/operations/analysis.js | 15 +++ lib/operations/index.d.ts | 10 ++ test/emails.js | 37 ++++++ 11 files changed, 294 insertions(+) create mode 100644 lib/models/blockListResult.js create mode 100644 lib/models/content.js create mode 100644 lib/models/deliverabilityReport.js create mode 100644 lib/models/dnsRecords.js create mode 100644 lib/models/emailAuthenticationResult.js create mode 100644 lib/models/spamAssassinResult.js diff --git a/lib/models/blockListResult.js b/lib/models/blockListResult.js new file mode 100644 index 0000000..251a9b8 --- /dev/null +++ b/lib/models/blockListResult.js @@ -0,0 +1,9 @@ +class BlockListResult { + constructor(data = {}) { + this.id = data.id; + this.name = data.name; + this.result = data.result; + } +} + +module.exports = BlockListResult; diff --git a/lib/models/content.js b/lib/models/content.js new file mode 100644 index 0000000..f2efbf1 --- /dev/null +++ b/lib/models/content.js @@ -0,0 +1,15 @@ +class Content { + constructor(data = {}) { + this.embed = data.embed; + this.iframe = data.iframe; + this.object = data.object; + this.script = data.script; + this.shortUrls = data.shortUrls; + this.textSize = data.textSize; + this.totalSize = data.totalSize; + this.missingAlt = data.missingAlt; + this.missingListUnsubscribe = data.missingListUnsubscribe; + } +} + +module.exports = Content; diff --git a/lib/models/deliverabilityReport.js b/lib/models/deliverabilityReport.js new file mode 100644 index 0000000..938772d --- /dev/null +++ b/lib/models/deliverabilityReport.js @@ -0,0 +1,19 @@ +const EmailAuthenticationResult = require('./emailAuthenticationResult'); +const BlockListResult = require('./blockListResult'); +const Content = require('./content'); +const DnsRecords = require('./dnsRecords'); +const SpamAssassinResult = require('./spamAssassinResult'); + +class DeliverabilityReport { + constructor(data = {}) { + this.spf = new EmailAuthenticationResult(data.spf); + this.dkim = (data.dkim || []).map((i) => (new EmailAuthenticationResult(i))); + this.dmarc = new EmailAuthenticationResult(data.dmarc); + this.blockLists = (data.blockLists || []).map((i) => (new BlockListResult(i))); + this.content = new Content(data.content); + this.dnsRecords = new DnsRecords(data.dnsRecords); + this.spamAssassin = new SpamAssassinResult(data.spamAssassin); + } +} + +module.exports = DeliverabilityReport; diff --git a/lib/models/dnsRecords.js b/lib/models/dnsRecords.js new file mode 100644 index 0000000..18c7286 --- /dev/null +++ b/lib/models/dnsRecords.js @@ -0,0 +1,9 @@ +class DnsRecords { + constructor(data = {}) { + this.a = data.a; + this.mx = data.mx; + this.ptr = data.ptr; + } +} + +module.exports = DnsRecords; diff --git a/lib/models/emailAuthenticationResult.js b/lib/models/emailAuthenticationResult.js new file mode 100644 index 0000000..0186da6 --- /dev/null +++ b/lib/models/emailAuthenticationResult.js @@ -0,0 +1,10 @@ +class EmailAuthenticationResult { + constructor(data = {}) { + this.result = data.result; + this.description = data.description; + this.rawValue = data.rawValue; + this.tags = data.tags; + } +} + +module.exports = EmailAuthenticationResult; diff --git a/lib/models/index.d.ts b/lib/models/index.d.ts index a2ffe6e..4287ad5 100644 --- a/lib/models/index.d.ts +++ b/lib/models/index.d.ts @@ -542,6 +542,159 @@ export interface SpamAnalysisResult { score?: number; } + +/** + * The results of deliverability report performed by Mailosaur. + */ +export interface DeliverabilityReport { + /** + * The result of checking for SPF issues + */ + spf?: EmailAuthenticationResult; + /** + * The result of checking for DKIM issues + */ + dkim?: EmailAuthenticationResult[]; + /** + * The result of checking for DMARC issues + */ + dmarc?: EmailAuthenticationResult; + /** + * The result of each blocklist that was checked + */ + blockLists?: BlockListResult[]; + /** + * The result of content checks made on the email + */ + content?: Content; + /** + * The DNS records checks made against the sender's domain + */ + dnsRecords?: DnsRecords; + /** + * The result of spam analysis performed by Mailosaur + */ + spamAssassin?: SpamAssassinResult; +} + +/** +* The result of an email domain check +*/ +export interface EmailAuthenticationResult { + /** + * The result of the check + */ + result: string; + /** + * A description of any issue/notes found + */ + description: string; + /** + * The raw values returned from the check + */ + rawValue: string; + /** + * The seperated tags returned from the check + */ + tags: { [key: string]: string}; +} + +/** +* The result of an domain check against a blocklist checker +*/ +export interface BlockListResult{ + /** + * The identifier of the blocklist + */ + id: string; + /** + * The name of the blocklist + */ + name: string; + /** + * The result of the blocklist check + */ + result: string; +} + +/** +* The results of email content analysis +*/ +export interface Content{ + /** + * The content contained embed tags + */ + embed: boolean; + /** + * The content contained Iframe tags + */ + iframe: boolean; + /** + * The content contained object tags + */ + object: boolean; + /** + * The content contained script tags + */ + script: boolean; + /** + * The content contained URL's that have been shortened + */ + shortUrls: boolean; + /** + * The length of all text that the content contained + */ + textSize: number; + /** + * The length of all HTML that the content contained + */ + totalSize: number; + /** + * Image(s) were missing "alt" properties + */ + missingAlt: boolean; + /** + * The message is missing a "List-Unsubscribe" header + */ + missingListUnsubscribe: boolean; +} + +/** +* The records found when checking DNS records of an email sender's domain +*/ +export interface DnsRecords{ + /** + * The A Records of the sender's domain + */ + a: string[]; + /** + * The MX Records of the sender's domain + */ + mx: string[]; + /** + * The PTR Record of the sender's domain + */ + ptr: string[]; +} + +/** +* The results of spam assassin check performed by Mailosaur. +*/ +export interface SpamAssassinResult{ + /** + * Overall Mailosaur spam score. + */ + score: number; + /** + * The result of the spam check + */ + result: string; + /** + * Spam Assassin filter rules. + */ + rules?: SpamAssassinRule[]; +} + /** * The detail of an individual account limit. */ diff --git a/lib/models/index.js b/lib/models/index.js index 0d7fd61..797a923 100644 --- a/lib/models/index.js +++ b/lib/models/index.js @@ -1,6 +1,12 @@ exports.SpamAssassinRule = require('./spamAssassinRule'); exports.SpamFilterResults = require('./spamFilterResults'); exports.SpamAnalysisResult = require('./spamAnalysisResult'); +exports.DeliverabilityReport = require('./deliverabilityReport'); +exports.EmailAuthenticationResult = require('./emailAuthenticationResult'); +exports.BlockListResult = require('./blockListResult'); +exports.Content = require('./content'); +exports.DnsRecords = require('./dnsRecords'); +exports.SpamAssassinResult = require('./spamAssassinResult'); exports.MailosaurError = require('./mailosaurError'); exports.MessageAddress = require('./messageAddress'); exports.Link = require('./link'); diff --git a/lib/models/spamAssassinResult.js b/lib/models/spamAssassinResult.js new file mode 100644 index 0000000..c83c5c3 --- /dev/null +++ b/lib/models/spamAssassinResult.js @@ -0,0 +1,11 @@ +const SpamAssassinRule = require('./spamAssassinRule'); + +class SpamAssassinResult { + constructor(data = {}) { + this.score = data.score; + this.result = data.result; + this.rules = (data.rules || []).map((i) => (new SpamAssassinRule(i))); + } +} + +module.exports = SpamAssassinResult; diff --git a/lib/operations/analysis.js b/lib/operations/analysis.js index 4b6f166..f06d234 100644 --- a/lib/operations/analysis.js +++ b/lib/operations/analysis.js @@ -1,4 +1,5 @@ const SpamAnalysisResult = require('../models/spamAnalysisResult'); +const DeliverabilityReport = require('../models/deliverabilityReport'); class Analysis { constructor(client) { @@ -18,6 +19,20 @@ class Analysis { }); }); } + + deliverability(messageId) { + const self = this; + const url = `api/analysis/deliverability/${messageId}`; + + return new Promise((resolve, reject) => { + self.client.request.get(url, (err, response, body) => { + if (err || response.statusCode !== 200) { + return reject(err || self.client.httpError(response)); + } + resolve(new DeliverabilityReport(body)); + }); + }); + } } module.exports = Analysis; diff --git a/lib/operations/index.d.ts b/lib/operations/index.d.ts index 327bd23..9c7f866 100644 --- a/lib/operations/index.d.ts +++ b/lib/operations/index.d.ts @@ -14,6 +14,16 @@ export interface Analysis { */ messageId: string ): Promise; + + /** + * Perform a deliverability report of an email. + */ + deliverability( + /** + * The identifier of the message to be analyzed. + */ + messageId: string + ): Promise; } /** diff --git a/test/emails.js b/test/emails.js index 0ab0d22..0537b17 100644 --- a/test/emails.js +++ b/test/emails.js @@ -304,6 +304,43 @@ describe('emails', () => { }); }); + describe('deliverabilityReport', () => { + it('should perform a deliverability report on an email', async () => { + const targetId = emails[0].id; + const result = await client.analysis.deliverability(targetId); + + assert.isOk(result.spf); + + assert.isOk(result.dkim); + result.dkim.forEach((dkim) => { + assert.isOk(dkim); + }); + + assert.isOk(result.dmarc); + + assert.isOk(result.blockLists); + result.blockLists.forEach((blockList) => { + assert.isOk(blockList); + assert.isOk(blockList.id); + assert.isOk(blockList.name); + }); + + assert.isOk(result.content); + + assert.isOk(result.dnsRecords); + assert.isOk(result.dnsRecords.a); + assert.isOk(result.dnsRecords.mx); + assert.isOk(result.dnsRecords.ptr); + + assert.isOk(result.spamAssassin); + result.spamAssassin.rules.forEach((rule) => { + assert.isNumber(rule.score); + assert.isOk(rule.rule); + assert.isOk(rule.description); + }); + }); + }); + describe('del', () => { it('should delete an email', async () => { const targetEmailId = emails[4].id;