From 9dffdfabb2f8902263fded3b521981c59a086c3d Mon Sep 17 00:00:00 2001 From: Brian Del Vecchio Date: Wed, 14 Dec 2016 15:57:16 -0500 Subject: [PATCH 01/30] configuration for tests --- Gruntfile.js | 8 ++++---- secrets_example.json | 5 +++-- tasks/grunt-postmark.js | 14 +++++++------- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 89c8ebb..454f711 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -15,14 +15,14 @@ module.exports = function(grunt) { serverToken: '<%= secrets.serverToken %>' }, email: { - from: 'you@youremail.com', - to: 'you@youremail.com', + from: '<%= secrets.testEmailSender %>', + to: '<%= secrets.testEmailSender %>', subject: 'Yo', src: ['test/email.html'] }, bulk: { - from: 'you@youremail.com', - to: 'you@youremail.com', + from: '<%= secrets.testEmailSender %>', + to: '<%= secrets.testEmailSender %>', subject: 'Hey', src: ['test/*.html'] } diff --git a/secrets_example.json b/secrets_example.json index a108905..bc2a8e2 100644 --- a/secrets_example.json +++ b/secrets_example.json @@ -1,3 +1,4 @@ { - "server_token": "POSTMARK_API_TEST" -} \ No newline at end of file + "serverToken": "POSTMARK_API_TEST", + "testEmailSender": "you@youremail.com", +} diff --git a/tasks/grunt-postmark.js b/tasks/grunt-postmark.js index ccf7f8b..e29e81a 100644 --- a/tasks/grunt-postmark.js +++ b/tasks/grunt-postmark.js @@ -3,10 +3,10 @@ * https://github.com/wildbit/grunt-postmark.git */ -'use strict'; - module.exports = function(grunt) { + 'use strict'; + grunt.registerMultiTask('postmark', 'Send emails through Postmark', function() { var done = this.async(); @@ -14,7 +14,7 @@ module.exports = function(grunt) { var _data = this.data; // Check for server token - if (!options.serverToken && !_data.serverToken) { + if (!options.serverToken && !_data.serverToken) { grunt.fail.warn('Missing Postmark server token \n'); } @@ -25,8 +25,8 @@ module.exports = function(grunt) { if (this.files.length > 0) { var message = { - 'From': _data.from || options.from, - 'To': _data.to || options.to, + 'From': _data.from || options.from, + 'To': _data.to || options.to, 'Subject': _data.subject || options.subject }; @@ -56,7 +56,7 @@ module.exports = function(grunt) { handleResponse(err, response, done); }); } - + } else { // Warn about no files being passed to task grunt.log.warn('No src file found \n'); @@ -66,7 +66,7 @@ module.exports = function(grunt) { function handleResponse(err, response, done) { - err ? errorMessage(err) : successMessage(response); + var _ = err ? errorMessage(err) : successMessage(response); done(); } From abb4d27909f4ae85582353de85c732bec566ab28 Mon Sep 17 00:00:00 2001 From: Brian Del Vecchio Date: Wed, 14 Dec 2016 16:48:54 -0500 Subject: [PATCH 02/30] more test config cleanup --- Gruntfile.js | 8 ++++---- secrets_example.json | 3 ++- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 454f711..0544cb5 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -15,14 +15,14 @@ module.exports = function(grunt) { serverToken: '<%= secrets.serverToken %>' }, email: { - from: '<%= secrets.testEmailSender %>', - to: '<%= secrets.testEmailSender %>', + from: '<%= secrets.testSender %>', + to: '<%= secrets.testRecipient %>', subject: 'Yo', src: ['test/email.html'] }, bulk: { - from: '<%= secrets.testEmailSender %>', - to: '<%= secrets.testEmailSender %>', + from: '<%= secrets.testSender %>', + to: '<%= secrets.testRecipient %>', subject: 'Hey', src: ['test/*.html'] } diff --git a/secrets_example.json b/secrets_example.json index bc2a8e2..1cc375a 100644 --- a/secrets_example.json +++ b/secrets_example.json @@ -1,4 +1,5 @@ { "serverToken": "POSTMARK_API_TEST", - "testEmailSender": "you@youremail.com", + "testSender": "you@youremail.com", + "testRecipient": "you@youremail.com" } From 86c4d292b8b0957bf43808a2a0d512afc9f27f85 Mon Sep 17 00:00:00 2001 From: Brian Del Vecchio Date: Wed, 14 Dec 2016 17:11:46 -0500 Subject: [PATCH 03/30] basic grunt task implemented --- Gruntfile.js | 14 +++++++- tasks/grunt-postmark-templates.js | 55 +++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 tasks/grunt-postmark-templates.js diff --git a/Gruntfile.js b/Gruntfile.js index 0544cb5..c8a797a 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -26,12 +26,24 @@ module.exports = function(grunt) { subject: 'Hey', src: ['test/*.html'] } + }, + + "postmark-templates": { + options: { + serverToken: '<%= secrets.serverToken %>', + }, + build: { + name: "testing-template-node-js" + Date(), + subject: "{{subject}}", + textBody: "text body for template {{id}}!", + htmlBody: "{{content}}", + } } }); grunt.loadTasks('tasks'); - grunt.registerTask('default', ['postmark']); + grunt.registerTask('default', ['postmark', 'postmark-templates']); }; diff --git a/tasks/grunt-postmark-templates.js b/tasks/grunt-postmark-templates.js new file mode 100644 index 0000000..3589def --- /dev/null +++ b/tasks/grunt-postmark-templates.js @@ -0,0 +1,55 @@ +/* + * grunt-postmark + * https://github.com/wildbit/grunt-postmark.git + */ + +module.exports = function(grunt) { + + 'use strict'; + + grunt.registerMultiTask('postmark-templates', 'Push templates', function() { + + var done = this.async(); + var options = this.options(); + var _data = this.data; + + // Check for server token + if (!options.serverToken && !_data.serverToken) { + grunt.fail.warn('Missing Postmark server token \n'); + } + + // Postmark lib + var postmark = require('postmark'); + var client = new postmark.Client(options.serverToken || _data.serverToken); + + client.createTemplate({ + name: _data.name || options.name, + textBody: _data.textBody || options.textBody, + htmlBody: _data.htmlBody || options.htmlBody, + subject: _data.subject || options.subject, + }, function(err, response) { + handleResponse(err, response, done); + }); + + }); + + function handleResponse(err, response, done) { + if (err){ + errorMessage(err); + done(); + } else { + var templateId = response.TemplateId; + successMessage(templateId); + done(templateId); + } + } + + function errorMessage(response) { + grunt.log.warn('Error response: ' + JSON.stringify(response)); + } + + function successMessage(templateId) { + grunt.log.writeln('Template pushed: ' + JSON.stringify(templateId)); + } + +}; From e8ae0241ee24522dd10c086441672fe541e5b4e9 Mon Sep 17 00:00:00 2001 From: Brian Del Vecchio Date: Thu, 15 Dec 2016 15:41:14 -0500 Subject: [PATCH 04/30] read template files from filesystem --- Gruntfile.js | 9 +++++---- tasks/grunt-postmark-templates.js | 29 +++++++++++++++++++++++++---- test/email.txt | 1 + 3 files changed, 31 insertions(+), 8 deletions(-) create mode 100644 test/email.txt diff --git a/Gruntfile.js b/Gruntfile.js index c8a797a..d55371a 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -33,10 +33,11 @@ module.exports = function(grunt) { serverToken: '<%= secrets.serverToken %>', }, build: { - name: "testing-template-node-js" + Date(), - subject: "{{subject}}", - textBody: "text body for template {{id}}!", - htmlBody: "{{content}}", + name: "testing-template-node-js-" + new Date().valueOf(), + subject: "Testing grunt-postmark-templates", + // NOTE these are read from filesystem. globbing not supported + htmlFile: 'test/email.html', + textFile: 'test/email.txt', } } diff --git a/tasks/grunt-postmark-templates.js b/tasks/grunt-postmark-templates.js index 3589def..12c3b26 100644 --- a/tasks/grunt-postmark-templates.js +++ b/tasks/grunt-postmark-templates.js @@ -1,5 +1,5 @@ /* - * grunt-postmark + * grunt-postmark-templates * https://github.com/wildbit/grunt-postmark.git */ @@ -15,17 +15,32 @@ module.exports = function(grunt) { // Check for server token if (!options.serverToken && !_data.serverToken) { - grunt.fail.warn('Missing Postmark server token \n'); + grunt.fail.warn('Missing required option "serverToken" \n'); } + // Check for required attributes + ['name', 'subject'].forEach(function(name){ + requiredProperty(name, options, _data); + }); + + grunt.log.writeln('Template data: ' + JSON.stringify(_data)); + // Postmark lib var postmark = require('postmark'); var client = new postmark.Client(options.serverToken || _data.serverToken); + var htmlBody = _data.htmlBody || options.htmlBody; + var textBody = _data.textBody || options.textBody; + if (_data.htmlFile) { + htmlBody = grunt.file.read(_data.htmlFile); + } + if (_data.textFile) { + textBody = grunt.file.read(_data.textFile); + } client.createTemplate({ name: _data.name || options.name, - textBody: _data.textBody || options.textBody, - htmlBody: _data.htmlBody || options.htmlBody, + textBody: textBody, + htmlBody: htmlBody, subject: _data.subject || options.subject, }, function(err, response) { handleResponse(err, response, done); @@ -33,6 +48,12 @@ module.exports = function(grunt) { }); + function requiredProperty(name, options, data) { + if (!data[name] && !options[name]) { + grunt.fail.warn('Missing required property "' + name + '" \n'); + } + } + function handleResponse(err, response, done) { if (err){ errorMessage(err); diff --git a/test/email.txt b/test/email.txt new file mode 100644 index 0000000..e5494a8 --- /dev/null +++ b/test/email.txt @@ -0,0 +1 @@ +Hello from grunt-postmark-templates From 4057b64cf777147058dfcdd9271bdef85a66e38c Mon Sep 17 00:00:00 2001 From: Brian Del Vecchio Date: Thu, 15 Dec 2016 15:42:52 -0500 Subject: [PATCH 05/30] remove debug --- tasks/grunt-postmark-templates.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/tasks/grunt-postmark-templates.js b/tasks/grunt-postmark-templates.js index 12c3b26..6223d04 100644 --- a/tasks/grunt-postmark-templates.js +++ b/tasks/grunt-postmark-templates.js @@ -23,8 +23,6 @@ module.exports = function(grunt) { requiredProperty(name, options, _data); }); - grunt.log.writeln('Template data: ' + JSON.stringify(_data)); - // Postmark lib var postmark = require('postmark'); var client = new postmark.Client(options.serverToken || _data.serverToken); From d988011c35b9a1d7d1d687020d7b3cb6b36dc73e Mon Sep 17 00:00:00 2001 From: Brian Del Vecchio Date: Thu, 15 Dec 2016 15:44:32 -0500 Subject: [PATCH 06/30] TODO for validating htmlBody/htmlFile etc. --- tasks/grunt-postmark-templates.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tasks/grunt-postmark-templates.js b/tasks/grunt-postmark-templates.js index 6223d04..a35b8cc 100644 --- a/tasks/grunt-postmark-templates.js +++ b/tasks/grunt-postmark-templates.js @@ -19,8 +19,9 @@ module.exports = function(grunt) { } // Check for required attributes + // TODO: require one of fooBody or fooFile ['name', 'subject'].forEach(function(name){ - requiredProperty(name, options, _data); + requireProperty(name, options, _data); }); // Postmark lib @@ -46,7 +47,7 @@ module.exports = function(grunt) { }); - function requiredProperty(name, options, data) { + function requireProperty(name, options, data) { if (!data[name] && !options[name]) { grunt.fail.warn('Missing required property "' + name + '" \n'); } From aac24c87d920e3ae427544df057bc067e7f95518 Mon Sep 17 00:00:00 2001 From: Brian Del Vecchio Date: Thu, 15 Dec 2016 15:53:03 -0500 Subject: [PATCH 07/30] pretty good support for creating a server --- Gruntfile.js | 17 +++++++-- tasks/grunt-postmark-servers.js | 63 +++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 2 deletions(-) create mode 100644 tasks/grunt-postmark-servers.js diff --git a/Gruntfile.js b/Gruntfile.js index d55371a..3a91364 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -39,12 +39,25 @@ module.exports = function(grunt) { htmlFile: 'test/email.html', textFile: 'test/email.txt', } - } + }, + + "postmark-servers": { + options: { + accountToken: '<%= secrets.accountToken %>', + }, + build: { + name: "testing-server-" + new Date().valueOf(), + smtpApiActivated: true, + + // NOTE complete list of server attributes: + // http://developer.postmarkapp.com/developer-api-servers.html#create-server + } + }, }); grunt.loadTasks('tasks'); - grunt.registerTask('default', ['postmark', 'postmark-templates']); + grunt.registerTask('default', ['postmark', 'postmark-templates', 'postmark-servers']); }; diff --git a/tasks/grunt-postmark-servers.js b/tasks/grunt-postmark-servers.js new file mode 100644 index 0000000..c493945 --- /dev/null +++ b/tasks/grunt-postmark-servers.js @@ -0,0 +1,63 @@ +/* + * grunt-postmark-servers + * https://github.com/wildbit/grunt-postmark.git + */ + +module.exports = function(grunt) { + + 'use strict'; + + grunt.registerMultiTask('postmark-servers', 'Create server', function() { + + var done = this.async(); + var options = this.options(); + var _data = this.data; + + // Check for account token + if (!options.accountToken && !_data.accountToken) { + grunt.fail.warn('Missing option "accountToken" \n'); + } + + // Postmark lib + var postmark = require('postmark'); + var client = new postmark.AdminClient(options.accountToken || _data.accountToken); + + // Check for server name + if (!_data.name && !options.name) { + grunt.fail.warn('Missing required server property "name" \n'); + } + + client.createServer({ + name: _data.name || options.name, + color: _data.color || options.color || "Turquoise", + smtpApiActivated: _data.smtpApiActivated || options.smtpApiActivated || true, + trackOpens: _data.trackOpens || options.trackOpens || false, + trackLinks: _data.trackLinks || options.trackLinks || "none", + + // TODO: handle other attributes + }, function(err, response) { + handleResponse(err, response, done); + }); + + }); + + function handleResponse(err, response, done) { + if (err){ + errorMessage(err); + done(); + } else { + var serverId = response.ID; + successMessage(response); + done(response); + } + } + + function errorMessage(err) { + grunt.log.warn('Error creating server: ' + JSON.stringify(err)); + } + + function successMessage(response) { + grunt.log.writeln('Server created: ' + JSON.stringify(response)); + } + +}; From 0873473bf10a8548f00f9815487380a48a8b1d14 Mon Sep 17 00:00:00 2001 From: Brian Del Vecchio Date: Fri, 16 Dec 2016 11:10:24 -0500 Subject: [PATCH 08/30] make create server task idempotent if server with the specified name exists, its config is returned in exactly the same form as a create response. --- tasks/grunt-postmark-servers.js | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/tasks/grunt-postmark-servers.js b/tasks/grunt-postmark-servers.js index c493945..8a718d3 100644 --- a/tasks/grunt-postmark-servers.js +++ b/tasks/grunt-postmark-servers.js @@ -27,8 +27,10 @@ module.exports = function(grunt) { grunt.fail.warn('Missing required server property "name" \n'); } + var name = _data.name || options.name; + client.createServer({ - name: _data.name || options.name, + name: name, color: _data.color || options.color || "Turquoise", smtpApiActivated: _data.smtpApiActivated || options.smtpApiActivated || true, trackOpens: _data.trackOpens || options.trackOpens || false, @@ -36,11 +38,32 @@ module.exports = function(grunt) { // TODO: handle other attributes }, function(err, response) { - handleResponse(err, response, done); + // NOTE if a server with the specified name already exists, we get this response: + // {"status":422,"message":"This server name already exists.","code":603} + if (err && err.status == 422){ + existingServer(client, name, done); + } else { + handleResponse(err, response, done); + } }); }); + // find the server with matching name and return its configuration + function existingServer(client, name, done) { + // listServers implements find-by-property + client.listServers({name: name}, function(err, servers){ + if (err){ + grunt.log.warn('Error retrieving existing server: ' + JSON.stringify(err)); + done(); + } else { + var server = servers.Servers[0]; + grunt.log.writeln('Server found: ' + JSON.stringify(server)); + done(server); + } + }); + } + function handleResponse(err, response, done) { if (err){ errorMessage(err); @@ -52,6 +75,7 @@ module.exports = function(grunt) { } } + function errorMessage(err) { grunt.log.warn('Error creating server: ' + JSON.stringify(err)); } From bcef3169d554c1bacacf0680bfaf37611f6e6708 Mon Sep 17 00:00:00 2001 From: Brian Del Vecchio Date: Fri, 16 Dec 2016 12:58:17 -0500 Subject: [PATCH 09/30] refactor postmark-servers task using more idiomatic grunt usage --- Gruntfile.js | 20 +++++++-------- tasks/grunt-postmark-servers.js | 45 +++++++++++++++++---------------- 2 files changed, 33 insertions(+), 32 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 3a91364..ecb968a 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -28,36 +28,36 @@ module.exports = function(grunt) { } }, - "postmark-templates": { + 'postmark-templates': { options: { serverToken: '<%= secrets.serverToken %>', }, build: { - name: "testing-template-node-js-" + new Date().valueOf(), - subject: "Testing grunt-postmark-templates", + name: 'testing-template-node-js-' + new Date().valueOf(), + subject: 'Testing grunt-postmark-templates', // NOTE these are read from filesystem. globbing not supported htmlFile: 'test/email.html', textFile: 'test/email.txt', } }, - "postmark-servers": { + 'postmark-servers': { options: { - accountToken: '<%= secrets.accountToken %>', - }, - build: { - name: "testing-server-" + new Date().valueOf(), + name: 'testing-server-' + new Date().valueOf(), smtpApiActivated: true, // NOTE complete list of server attributes: // http://developer.postmarkapp.com/developer-api-servers.html#create-server - } + }, }, }); grunt.loadTasks('tasks'); - grunt.registerTask('default', ['postmark', 'postmark-templates', 'postmark-servers']); + // test create of an existing server (by name), + grunt.registerTask('duplicate-server', ['postmark-servers', 'postmark-servers']); + + grunt.registerTask('default', ['postmark', 'postmark-templates', 'postmark-servers', 'duplicate-server']); }; diff --git a/tasks/grunt-postmark-servers.js b/tasks/grunt-postmark-servers.js index 8a718d3..fe8aa17 100644 --- a/tasks/grunt-postmark-servers.js +++ b/tasks/grunt-postmark-servers.js @@ -7,41 +7,42 @@ module.exports = function(grunt) { 'use strict'; - grunt.registerMultiTask('postmark-servers', 'Create server', function() { + grunt.registerTask('postmark-servers', 'Create server', function() { var done = this.async(); - var options = this.options(); - var _data = this.data; + // default options (attempting to disable all hooks) + var options = this.options({ + color: "turquoise", + smtpApiActivated: true, + trackOpens: false, + trackLinks: 'none', + DeliveryHookUrl: '', + InboundHookUrl: '', + BounceHookUrl: '', + OpenHookUrl: '', + }); + + var accountToken = grunt.config('secrets.accountToken') || options.accountToken; // Check for account token - if (!options.accountToken && !_data.accountToken) { - grunt.fail.warn('Missing option "accountToken" \n'); + if (!accountToken) { + grunt.fail.warn('Missing required config property "accountToken" \n'); } - // Postmark lib - var postmark = require('postmark'); - var client = new postmark.AdminClient(options.accountToken || _data.accountToken); - - // Check for server name - if (!_data.name && !options.name) { + if (!options.name) { grunt.fail.warn('Missing required server property "name" \n'); } - var name = _data.name || options.name; - - client.createServer({ - name: name, - color: _data.color || options.color || "Turquoise", - smtpApiActivated: _data.smtpApiActivated || options.smtpApiActivated || true, - trackOpens: _data.trackOpens || options.trackOpens || false, - trackLinks: _data.trackLinks || options.trackLinks || "none", + // Postmark lib + var postmark = require('postmark'); + var client = new postmark.AdminClient(accountToken); - // TODO: handle other attributes - }, function(err, response) { + // TODO use merge for default options + client.createServer(options, function(err, response) { // NOTE if a server with the specified name already exists, we get this response: // {"status":422,"message":"This server name already exists.","code":603} if (err && err.status == 422){ - existingServer(client, name, done); + existingServer(client, options.name, done); } else { handleResponse(err, response, done); } From 71ccf9116fea49afcdcedc6ad59a107d39d9c2dc Mon Sep 17 00:00:00 2001 From: Brian Del Vecchio Date: Fri, 16 Dec 2016 12:59:59 -0500 Subject: [PATCH 10/30] duplicate test requires only one additional iteration, not two --- Gruntfile.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gruntfile.js b/Gruntfile.js index ecb968a..db942c7 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -56,7 +56,7 @@ module.exports = function(grunt) { grunt.loadTasks('tasks'); // test create of an existing server (by name), - grunt.registerTask('duplicate-server', ['postmark-servers', 'postmark-servers']); + grunt.registerTask('duplicate-server', ['postmark-servers']); grunt.registerTask('default', ['postmark', 'postmark-templates', 'postmark-servers', 'duplicate-server']); From d3331ca1952338160fd2527f549e5688c84f0750 Mon Sep 17 00:00:00 2001 From: Brian Del Vecchio Date: Fri, 23 Dec 2016 11:37:46 -0500 Subject: [PATCH 11/30] collect serverToken from secrets.json --- Gruntfile.js | 2 +- tasks/grunt-postmark-templates.js | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index db942c7..0a91249 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -6,7 +6,7 @@ module.exports = function(grunt) { grunt.initConfig({ - secrets: grunt.file.readJSON('secrets.json'), + secrets: grunt.file.exists('secrets.json') ? grunt.file.readJSON('secrets.json') : {}, /* Postmark ------------------------------------------------- */ diff --git a/tasks/grunt-postmark-templates.js b/tasks/grunt-postmark-templates.js index a35b8cc..7ab3d49 100644 --- a/tasks/grunt-postmark-templates.js +++ b/tasks/grunt-postmark-templates.js @@ -12,9 +12,10 @@ module.exports = function(grunt) { var done = this.async(); var options = this.options(); var _data = this.data; + var serverToken = options.serverToken || _data.serverToken || grunt.config('secrets.serverToken') || grunt.config('secret.postmark.server_token'); // Check for server token - if (!options.serverToken && !_data.serverToken) { + if (!serverToken) { grunt.fail.warn('Missing required option "serverToken" \n'); } @@ -26,7 +27,7 @@ module.exports = function(grunt) { // Postmark lib var postmark = require('postmark'); - var client = new postmark.Client(options.serverToken || _data.serverToken); + var client = new postmark.Client(serverToken); var htmlBody = _data.htmlBody || options.htmlBody; var textBody = _data.textBody || options.textBody; From a48658c1d82bee8b4481ec0e58bf1fc702083b42 Mon Sep 17 00:00:00 2001 From: Brian Del Vecchio Date: Fri, 23 Dec 2016 11:38:37 -0500 Subject: [PATCH 12/30] read template config from templates.json --- Gruntfile.js | 40 +++++++++++++++++++++---------- tasks/grunt-postmark-templates.js | 36 ++++++++++++++-------------- 2 files changed, 45 insertions(+), 31 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 0a91249..cadae43 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -7,6 +7,7 @@ module.exports = function(grunt) { grunt.initConfig({ secrets: grunt.file.exists('secrets.json') ? grunt.file.readJSON('secrets.json') : {}, + templates: grunt.file.exists('templates.json') ? grunt.file.readJSON('templates.json') : null, /* Postmark ------------------------------------------------- */ @@ -28,19 +29,6 @@ module.exports = function(grunt) { } }, - 'postmark-templates': { - options: { - serverToken: '<%= secrets.serverToken %>', - }, - build: { - name: 'testing-template-node-js-' + new Date().valueOf(), - subject: 'Testing grunt-postmark-templates', - // NOTE these are read from filesystem. globbing not supported - htmlFile: 'test/email.html', - textFile: 'test/email.txt', - } - }, - 'postmark-servers': { options: { name: 'testing-server-' + new Date().valueOf(), @@ -51,6 +39,27 @@ module.exports = function(grunt) { }, }, + 'postmark-templates': { + build: { + name: 'testing-template-node-js-' + new Date().valueOf(), + subject: 'Testing grunt-postmark-templates', + // NOTE these are read from filesystem. globbing not supported + htmlFile: 'test/confirm_email.html', + textFile: 'test/confirm_email.txt', + } + }, + + 'update-templates': { + 'templates': { + // key is used as template name + test_email: { + subject: 'Testing grunt postmark-update-templates task', + htmlFile: 'test/confirm_email.html', + textFile: 'test/confirm_email.txt', + } + } + } + }); grunt.loadTasks('tasks'); @@ -58,6 +67,11 @@ module.exports = function(grunt) { // test create of an existing server (by name), grunt.registerTask('duplicate-server', ['postmark-servers']); + grunt.registerTask('update-templates', 'create or update a set of templates', function() { + grunt.config.set('postmark-templates', grunt.config('templates') || grunt.config('update-templates.templates')); + grunt.task.run('postmark-templates'); + }); + grunt.registerTask('default', ['postmark', 'postmark-templates', 'postmark-servers', 'duplicate-server']); }; diff --git a/tasks/grunt-postmark-templates.js b/tasks/grunt-postmark-templates.js index 7ab3d49..81ff321 100644 --- a/tasks/grunt-postmark-templates.js +++ b/tasks/grunt-postmark-templates.js @@ -7,11 +7,13 @@ module.exports = function(grunt) { 'use strict'; - grunt.registerMultiTask('postmark-templates', 'Push templates', function() { + grunt.registerMultiTask('postmark-templates', 'Create or update PostMark template', function() { var done = this.async(); var options = this.options(); var _data = this.data; + var tmplName = _data.name || this.target; + var serverToken = options.serverToken || _data.serverToken || grunt.config('secrets.serverToken') || grunt.config('secret.postmark.server_token'); // Check for server token @@ -19,11 +21,13 @@ module.exports = function(grunt) { grunt.fail.warn('Missing required option "serverToken" \n'); } - // Check for required attributes - // TODO: require one of fooBody or fooFile - ['name', 'subject'].forEach(function(name){ - requireProperty(name, options, _data); - }); + if (!tmplName) { + grunt.fail.warn('Missing required property "name" \n'); + } + + if (!_data.subject) { + grunt.fail.warn('Missing required property "subject" \n'); + } // Postmark lib var postmark = require('postmark'); @@ -38,30 +42,25 @@ module.exports = function(grunt) { textBody = grunt.file.read(_data.textFile); } client.createTemplate({ - name: _data.name || options.name, + name: tmplName, textBody: textBody, htmlBody: htmlBody, - subject: _data.subject || options.subject, + subject: _data.subject, }, function(err, response) { handleResponse(err, response, done); }); }); - function requireProperty(name, options, data) { - if (!data[name] && !options[name]) { - grunt.fail.warn('Missing required property "' + name + '" \n'); - } - } - function handleResponse(err, response, done) { if (err){ errorMessage(err); done(); } else { var templateId = response.TemplateId; - successMessage(templateId); - done(templateId); + var name = response.Name; + successMessage(name, templateId); + done({name: name, templateId: templateId}); } } @@ -69,8 +68,9 @@ module.exports = function(grunt) { grunt.log.warn('Error response: ' + JSON.stringify(response)); } - function successMessage(templateId) { - grunt.log.writeln('Template pushed: ' + JSON.stringify(templateId)); + function successMessage(name, templateId) { + grunt.log.writeln('Template ' + name + ' pushed: ' + JSON.stringify(templateId)); + } }; From a650a90cf0fe4c1d68d51c326d50c62222f9e802 Mon Sep 17 00:00:00 2001 From: Brian Del Vecchio Date: Fri, 23 Dec 2016 11:44:37 -0500 Subject: [PATCH 13/30] clean up example template files --- Gruntfile.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index cadae43..990653f 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -44,18 +44,19 @@ module.exports = function(grunt) { name: 'testing-template-node-js-' + new Date().valueOf(), subject: 'Testing grunt-postmark-templates', // NOTE these are read from filesystem. globbing not supported - htmlFile: 'test/confirm_email.html', - textFile: 'test/confirm_email.txt', + htmlFile: 'test/email.html', + textFile: 'test/email.txt', } }, + // you can either specify the template configuration here, or in templates.json 'update-templates': { 'templates': { // key is used as template name test_email: { - subject: 'Testing grunt postmark-update-templates task', - htmlFile: 'test/confirm_email.html', - textFile: 'test/confirm_email.txt', + subject: 'Testing grunt postmark-update-templates task', + htmlFile: 'test/email.html', + textFile: 'test/email.txt', } } } From d87365d8b7104d65c360dc31776f7987576bc43d Mon Sep 17 00:00:00 2001 From: Brian Del Vecchio Date: Fri, 23 Dec 2016 14:29:54 -0500 Subject: [PATCH 14/30] simplify configuration --- Gruntfile.js | 22 +++------------------- tasks/grunt-postmark-templates.js | 11 +++++++++-- 2 files changed, 12 insertions(+), 21 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 990653f..d2c18db 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -39,26 +39,15 @@ module.exports = function(grunt) { }, }, + // you can either specify the template configuration here, or in templates.json 'postmark-templates': { - build: { - name: 'testing-template-node-js-' + new Date().valueOf(), + test_email_file: { + name: 'testing-postmark-templates-js-' + new Date().valueOf(), subject: 'Testing grunt-postmark-templates', // NOTE these are read from filesystem. globbing not supported htmlFile: 'test/email.html', textFile: 'test/email.txt', } - }, - - // you can either specify the template configuration here, or in templates.json - 'update-templates': { - 'templates': { - // key is used as template name - test_email: { - subject: 'Testing grunt postmark-update-templates task', - htmlFile: 'test/email.html', - textFile: 'test/email.txt', - } - } } }); @@ -68,11 +57,6 @@ module.exports = function(grunt) { // test create of an existing server (by name), grunt.registerTask('duplicate-server', ['postmark-servers']); - grunt.registerTask('update-templates', 'create or update a set of templates', function() { - grunt.config.set('postmark-templates', grunt.config('templates') || grunt.config('update-templates.templates')); - grunt.task.run('postmark-templates'); - }); - grunt.registerTask('default', ['postmark', 'postmark-templates', 'postmark-servers', 'duplicate-server']); }; diff --git a/tasks/grunt-postmark-templates.js b/tasks/grunt-postmark-templates.js index 81ff321..1d4865e 100644 --- a/tasks/grunt-postmark-templates.js +++ b/tasks/grunt-postmark-templates.js @@ -7,7 +7,12 @@ module.exports = function(grunt) { 'use strict'; - grunt.registerMultiTask('postmark-templates', 'Create or update PostMark template', function() { + grunt.registerTask('postmark-templates', 'create or update a set of templates', function() { + grunt.config.set('_postmark-template', grunt.config('templates') || grunt.config('postmark-templates')); + grunt.task.run('_postmark-template'); + }); + + grunt.registerMultiTask('_postmark-template', 'Create or update PostMark template', function() { var done = this.async(); var options = this.options(); @@ -59,8 +64,10 @@ module.exports = function(grunt) { } else { var templateId = response.TemplateId; var name = response.Name; + var result = {name: name, templateId: templateId}; + grunt.config.merge('updatedTemplates', result); successMessage(name, templateId); - done({name: name, templateId: templateId}); + done(result); } } From bb51efc9831364269e0e08ec0252098675ba415b Mon Sep 17 00:00:00 2001 From: Brian Del Vecchio Date: Fri, 23 Dec 2016 16:01:39 -0500 Subject: [PATCH 15/30] use more canonical htmlBody and textBody --- Gruntfile.js | 4 ++-- tasks/grunt-postmark-templates.js | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index d2c18db..bf9236a 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -45,8 +45,8 @@ module.exports = function(grunt) { name: 'testing-postmark-templates-js-' + new Date().valueOf(), subject: 'Testing grunt-postmark-templates', // NOTE these are read from filesystem. globbing not supported - htmlFile: 'test/email.html', - textFile: 'test/email.txt', + htmlBody: 'test/email.html', + textBody: 'test/email.txt', } } diff --git a/tasks/grunt-postmark-templates.js b/tasks/grunt-postmark-templates.js index 1d4865e..665c713 100644 --- a/tasks/grunt-postmark-templates.js +++ b/tasks/grunt-postmark-templates.js @@ -40,11 +40,11 @@ module.exports = function(grunt) { var htmlBody = _data.htmlBody || options.htmlBody; var textBody = _data.textBody || options.textBody; - if (_data.htmlFile) { - htmlBody = grunt.file.read(_data.htmlFile); + if (_data.htmlBody) { + htmlBody = grunt.file.read(htmlBody); } - if (_data.textFile) { - textBody = grunt.file.read(_data.textFile); + if (_data.textBody) { + textBody = grunt.file.read(textBody); } client.createTemplate({ name: tmplName, From 3967d29d4002b42b6a6b9caf9a59f7067c1dc049 Mon Sep 17 00:00:00 2001 From: Brian Del Vecchio Date: Fri, 23 Dec 2016 16:21:38 -0500 Subject: [PATCH 16/30] read templates.json in the postmark-templates task --- tasks/grunt-postmark-templates.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tasks/grunt-postmark-templates.js b/tasks/grunt-postmark-templates.js index 665c713..c0965e3 100644 --- a/tasks/grunt-postmark-templates.js +++ b/tasks/grunt-postmark-templates.js @@ -8,7 +8,10 @@ module.exports = function(grunt) { 'use strict'; grunt.registerTask('postmark-templates', 'create or update a set of templates', function() { - grunt.config.set('_postmark-template', grunt.config('templates') || grunt.config('postmark-templates')); + var templates = grunt.config('templates') || grunt.config('postmark-templates'); + templates = templates || grunt.file.exists('templates.json') ? grunt.file.readJSON('templates.json') : null; + + grunt.config.set('_postmark-template', templates); grunt.task.run('_postmark-template'); }); From 82fb13c92abcd2a6b5a6a962514a1988933b33bb Mon Sep 17 00:00:00 2001 From: Brian Del Vecchio Date: Tue, 3 Jan 2017 17:32:56 -0500 Subject: [PATCH 17/30] checkpoint: making progress toward saving templateID output --- tasks/grunt-postmark-templates.js | 69 +++++++++++++++++++------------ 1 file changed, 43 insertions(+), 26 deletions(-) diff --git a/tasks/grunt-postmark-templates.js b/tasks/grunt-postmark-templates.js index c0965e3..afa2399 100644 --- a/tasks/grunt-postmark-templates.js +++ b/tasks/grunt-postmark-templates.js @@ -9,66 +9,69 @@ module.exports = function(grunt) { grunt.registerTask('postmark-templates', 'create or update a set of templates', function() { var templates = grunt.config('templates') || grunt.config('postmark-templates'); - templates = templates || grunt.file.exists('templates.json') ? grunt.file.readJSON('templates.json') : null; + templates = templates || (grunt.file.exists('templates.json') ? grunt.file.readJSON('templates.json') : null); grunt.config.set('_postmark-template', templates); + grunt.config.set('updatedTemplates', []); grunt.task.run('_postmark-template'); + grunt.task.run('_postmark-output'); }); grunt.registerMultiTask('_postmark-template', 'Create or update PostMark template', function() { var done = this.async(); var options = this.options(); - var _data = this.data; - var tmplName = _data.name || this.target; + var template = this.data; - var serverToken = options.serverToken || _data.serverToken || grunt.config('secrets.serverToken') || grunt.config('secret.postmark.server_token'); + var serverToken = options.serverToken || grunt.config('secrets.serverToken') || grunt.config('secret.postmark.server_token'); // Check for server token if (!serverToken) { grunt.fail.warn('Missing required option "serverToken" \n'); } - if (!tmplName) { - grunt.fail.warn('Missing required property "name" \n'); + template.name = template.name || this.target; + if (!template.name) { + grunt.fail.warn('Missing required template property "name" \n'); } - if (!_data.subject) { - grunt.fail.warn('Missing required property "subject" \n'); + if (!template.subject) { + grunt.fail.warn('Missing required template property "subject" \n'); } + grunt.log.writeln('tmpl options: ' + JSON.stringify(options)); + grunt.log.writeln('template: ' + JSON.stringify(template)); + // Postmark lib var postmark = require('postmark'); var client = new postmark.Client(serverToken); - var htmlBody = _data.htmlBody || options.htmlBody; - var textBody = _data.textBody || options.textBody; - if (_data.htmlBody) { - htmlBody = grunt.file.read(htmlBody); + var expanded = Object.assign({}, template); + + if (expanded.htmlBody) { + expanded.htmlBody = grunt.file.read(expanded.htmlBody); } - if (_data.textBody) { - textBody = grunt.file.read(textBody); + if (expanded.textBody) { + expanded.textBody = grunt.file.read(expanded.textBody); } - client.createTemplate({ - name: tmplName, - textBody: textBody, - htmlBody: htmlBody, - subject: _data.subject, - }, function(err, response) { - handleResponse(err, response, done); + + client.createTemplate(expanded, function(err, response) { + handleResponse(template, err, response, done); }); }); - function handleResponse(err, response, done) { + function handleResponse(template, err, response, done) { if (err){ errorMessage(err); done(); } else { - var templateId = response.TemplateId; - var name = response.Name; - var result = {name: name, templateId: templateId}; - grunt.config.merge('updatedTemplates', result); + template.templateId = response.TemplateId; + // append this record to the result array + var upd = grunt.config.get('updatedTemplates'); + upd.unshift(template); + grunt.config.set('updatedTemplates', upd); + successMessage(name, templateId); done(result); } @@ -83,4 +86,18 @@ module.exports = function(grunt) { } + grunt.registerTask('_postmark-output', 'writes out the resulting template IDs', function() { + + var options = this.options({ + filename: "templates-output.json" + }); + + var results = grunt.config('updatedTemplates'); + + grunt.file.write(options.filename, JSON.stringify(results, null, 1)); + + grunt.log.writeln("Updated template information written to " + options.filename); + + }); + }; From a0a6f6dd3b43ae2be4c0e4f0d0600759947269cb Mon Sep 17 00:00:00 2001 From: Brian Del Vecchio Date: Tue, 3 Jan 2017 21:04:29 -0500 Subject: [PATCH 18/30] output template info (including IDs) in templates.json format --- tasks/grunt-postmark-templates.js | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/tasks/grunt-postmark-templates.js b/tasks/grunt-postmark-templates.js index afa2399..c747a8e 100644 --- a/tasks/grunt-postmark-templates.js +++ b/tasks/grunt-postmark-templates.js @@ -12,7 +12,7 @@ module.exports = function(grunt) { templates = templates || (grunt.file.exists('templates.json') ? grunt.file.readJSON('templates.json') : null); grunt.config.set('_postmark-template', templates); - grunt.config.set('updatedTemplates', []); + grunt.config.set('updatedTemplates', {}); grunt.task.run('_postmark-template'); grunt.task.run('_postmark-output'); }); @@ -31,6 +31,7 @@ module.exports = function(grunt) { } template.name = template.name || this.target; + if (!template.name) { grunt.fail.warn('Missing required template property "name" \n'); } @@ -39,13 +40,10 @@ module.exports = function(grunt) { grunt.fail.warn('Missing required template property "subject" \n'); } - grunt.log.writeln('tmpl options: ' + JSON.stringify(options)); - grunt.log.writeln('template: ' + JSON.stringify(template)); - - // Postmark lib var postmark = require('postmark'); var client = new postmark.Client(serverToken); + // read the referenced files, but hold on to the original filenames var expanded = Object.assign({}, template); if (expanded.htmlBody) { @@ -69,21 +67,26 @@ module.exports = function(grunt) { template.templateId = response.TemplateId; // append this record to the result array var upd = grunt.config.get('updatedTemplates'); - upd.unshift(template); + var tname = template.name; + delete template.name; + upd[tname] = template; grunt.config.set('updatedTemplates', upd); - successMessage(name, templateId); - done(result); + successMessage(tname, template.templateId); + done(template); } } - function errorMessage(response) { - grunt.log.warn('Error response: ' + JSON.stringify(response)); + function errorMessage(err) { + if (err.message) { + grunt.log.warn('Error: ' + err.message); + } else { + grunt.log.warn('Error: ' + JSON.stringify(err)); + } } function successMessage(name, templateId) { grunt.log.writeln('Template ' + name + ' pushed: ' + JSON.stringify(templateId)); - } grunt.registerTask('_postmark-output', 'writes out the resulting template IDs', function() { @@ -94,7 +97,7 @@ module.exports = function(grunt) { var results = grunt.config('updatedTemplates'); - grunt.file.write(options.filename, JSON.stringify(results, null, 1)); + grunt.file.write(options.filename, JSON.stringify(results, null, 2)); grunt.log.writeln("Updated template information written to " + options.filename); From 8fb62e577a8703f1c0349007882483ee79687308 Mon Sep 17 00:00:00 2001 From: Brian Del Vecchio Date: Tue, 3 Jan 2017 21:05:00 -0500 Subject: [PATCH 19/30] update existing templates if templateID is present --- tasks/grunt-postmark-templates.js | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/tasks/grunt-postmark-templates.js b/tasks/grunt-postmark-templates.js index c747a8e..c6f3306 100644 --- a/tasks/grunt-postmark-templates.js +++ b/tasks/grunt-postmark-templates.js @@ -52,10 +52,16 @@ module.exports = function(grunt) { if (expanded.textBody) { expanded.textBody = grunt.file.read(expanded.textBody); } - - client.createTemplate(expanded, function(err, response) { - handleResponse(template, err, response, done); - }); + + if (template.templateId) { + client.editTemplate(template.templateId, expanded, function(err, response) { + handleResponse(template, err, response, done); + }); + } else { + client.createTemplate(expanded, function(err, response) { + handleResponse(template, err, response, done); + }); + } }); From a518367f6ef43e5f1fc567d2662f4a58b1853fb0 Mon Sep 17 00:00:00 2001 From: Brian Del Vecchio Date: Tue, 3 Jan 2017 21:29:37 -0500 Subject: [PATCH 20/30] if update fails with bad Id, then revert to create --- .gitignore | 3 ++- tasks/grunt-postmark-templates.js | 36 +++++++++++++++++++++++++++++-- 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index add4ce7..7e27fa6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ node_modules .idea -secrets.json \ No newline at end of file +secrets.json +templates-output.json diff --git a/tasks/grunt-postmark-templates.js b/tasks/grunt-postmark-templates.js index c6f3306..fe9db11 100644 --- a/tasks/grunt-postmark-templates.js +++ b/tasks/grunt-postmark-templates.js @@ -7,6 +7,30 @@ module.exports = function(grunt) { 'use strict'; + // There are a number of different ways to populate this configuration object, + // including gruntfile configuration for "postmark-templates", + // or a templates.json file in your project. + // + // The object should have the following format, using the template name as the key. + // Here is an example templates.json file from a Mailmason project: + // + // { + // "confirm_email": { + // "subject": "Please confirm your email", + // "htmlBody": "dist/confirm_email.html", + // "textBody": "dist/confirm_email.txt", + // "templateId": 1201660 + // } + // } + // + // templateId is optional; if present, the template with the given ID will be updated. This will prevent + // proliferation of outdated versions of templates with the same name, and depending on how you're using + // them, reduce the configuration overhead (for example publishing these templateIds to your backend). + // + // The postmark-templates task will write an output file in this format including the current templateId. + // The default output file is named templates-output.json. You may ignore this file, or manually copy it + // to templates.json after a successful run. + grunt.registerTask('postmark-templates', 'create or update a set of templates', function() { var templates = grunt.config('templates') || grunt.config('postmark-templates'); templates = templates || (grunt.file.exists('templates.json') ? grunt.file.readJSON('templates.json') : null); @@ -25,7 +49,6 @@ module.exports = function(grunt) { var serverToken = options.serverToken || grunt.config('secrets.serverToken') || grunt.config('secret.postmark.server_token'); - // Check for server token if (!serverToken) { grunt.fail.warn('Missing required option "serverToken" \n'); } @@ -55,7 +78,16 @@ module.exports = function(grunt) { if (template.templateId) { client.editTemplate(template.templateId, expanded, function(err, response) { - handleResponse(template, err, response, done); + if (err && err.code === 1101) { + grunt.log.warn('Template ' + template.templateId + ' not found, so attempting create'); + delete template.templateId; + delete expanded.templateId; + client.createTemplate(expanded, function(err, response) { + handleResponse(template, err, response, done); + }); + } else { + handleResponse(template, err, response, done); + } }); } else { client.createTemplate(expanded, function(err, response) { From 61311967f5ab73a73a59537b0ce84e08c8acebe1 Mon Sep 17 00:00:00 2001 From: Brian Del Vecchio Date: Tue, 3 Jan 2017 21:31:48 -0500 Subject: [PATCH 21/30] move postmark-servers task to separate PR --- Gruntfile.js | 10 ---- tasks/grunt-postmark-servers.js | 88 --------------------------------- 2 files changed, 98 deletions(-) delete mode 100644 tasks/grunt-postmark-servers.js diff --git a/Gruntfile.js b/Gruntfile.js index bf9236a..786a2e8 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -29,16 +29,6 @@ module.exports = function(grunt) { } }, - 'postmark-servers': { - options: { - name: 'testing-server-' + new Date().valueOf(), - smtpApiActivated: true, - - // NOTE complete list of server attributes: - // http://developer.postmarkapp.com/developer-api-servers.html#create-server - }, - }, - // you can either specify the template configuration here, or in templates.json 'postmark-templates': { test_email_file: { diff --git a/tasks/grunt-postmark-servers.js b/tasks/grunt-postmark-servers.js deleted file mode 100644 index fe8aa17..0000000 --- a/tasks/grunt-postmark-servers.js +++ /dev/null @@ -1,88 +0,0 @@ -/* - * grunt-postmark-servers - * https://github.com/wildbit/grunt-postmark.git - */ - -module.exports = function(grunt) { - - 'use strict'; - - grunt.registerTask('postmark-servers', 'Create server', function() { - - var done = this.async(); - - // default options (attempting to disable all hooks) - var options = this.options({ - color: "turquoise", - smtpApiActivated: true, - trackOpens: false, - trackLinks: 'none', - DeliveryHookUrl: '', - InboundHookUrl: '', - BounceHookUrl: '', - OpenHookUrl: '', - }); - - var accountToken = grunt.config('secrets.accountToken') || options.accountToken; - // Check for account token - if (!accountToken) { - grunt.fail.warn('Missing required config property "accountToken" \n'); - } - - if (!options.name) { - grunt.fail.warn('Missing required server property "name" \n'); - } - - // Postmark lib - var postmark = require('postmark'); - var client = new postmark.AdminClient(accountToken); - - // TODO use merge for default options - client.createServer(options, function(err, response) { - // NOTE if a server with the specified name already exists, we get this response: - // {"status":422,"message":"This server name already exists.","code":603} - if (err && err.status == 422){ - existingServer(client, options.name, done); - } else { - handleResponse(err, response, done); - } - }); - - }); - - // find the server with matching name and return its configuration - function existingServer(client, name, done) { - // listServers implements find-by-property - client.listServers({name: name}, function(err, servers){ - if (err){ - grunt.log.warn('Error retrieving existing server: ' + JSON.stringify(err)); - done(); - } else { - var server = servers.Servers[0]; - grunt.log.writeln('Server found: ' + JSON.stringify(server)); - done(server); - } - }); - } - - function handleResponse(err, response, done) { - if (err){ - errorMessage(err); - done(); - } else { - var serverId = response.ID; - successMessage(response); - done(response); - } - } - - - function errorMessage(err) { - grunt.log.warn('Error creating server: ' + JSON.stringify(err)); - } - - function successMessage(response) { - grunt.log.writeln('Server created: ' + JSON.stringify(response)); - } - -}; From ac17e378e3ce800a6d0474c89f416c0079061536 Mon Sep 17 00:00:00 2001 From: Brian Del Vecchio Date: Tue, 3 Jan 2017 21:35:07 -0500 Subject: [PATCH 22/30] remove references to postmark-servers (for another PR) --- Gruntfile.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 786a2e8..b4c307f 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -44,9 +44,6 @@ module.exports = function(grunt) { grunt.loadTasks('tasks'); - // test create of an existing server (by name), - grunt.registerTask('duplicate-server', ['postmark-servers']); - - grunt.registerTask('default', ['postmark', 'postmark-templates', 'postmark-servers', 'duplicate-server']); + grunt.registerTask('default', ['postmark', 'postmark-templates']); }; From f3a7f7e2e57055be35f24a1d83899d07645fe22c Mon Sep 17 00:00:00 2001 From: Brian Del Vecchio Date: Tue, 3 Jan 2017 21:37:54 -0500 Subject: [PATCH 23/30] one last comment --- tasks/grunt-postmark-templates.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tasks/grunt-postmark-templates.js b/tasks/grunt-postmark-templates.js index fe9db11..5f5123c 100644 --- a/tasks/grunt-postmark-templates.js +++ b/tasks/grunt-postmark-templates.js @@ -1,5 +1,7 @@ /* * grunt-postmark-templates + * push templates to a Postmark server for use with SendTemplatedEmail + * * https://github.com/wildbit/grunt-postmark.git */ From 4a0b60cda639899ae5eed9b36cddd48e73bc8720 Mon Sep 17 00:00:00 2001 From: Brian Del Vecchio Date: Fri, 13 Jan 2017 15:04:25 -0500 Subject: [PATCH 24/30] node.js style callback --- tasks/grunt-postmark-templates.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tasks/grunt-postmark-templates.js b/tasks/grunt-postmark-templates.js index 5f5123c..2cfdf88 100644 --- a/tasks/grunt-postmark-templates.js +++ b/tasks/grunt-postmark-templates.js @@ -85,21 +85,21 @@ module.exports = function(grunt) { delete template.templateId; delete expanded.templateId; client.createTemplate(expanded, function(err, response) { - handleResponse(template, err, response, done); + handleResponse(err, response, done, template); }); } else { - handleResponse(template, err, response, done); + handleResponse(err, response, done, template); } }); } else { client.createTemplate(expanded, function(err, response) { - handleResponse(template, err, response, done); + handleResponse(err, response, done, template); }); } }); - function handleResponse(template, err, response, done) { + function handleResponse(err, response, done, template) { if (err){ errorMessage(err); done(); From aa3194b574784e9f1ea04e90210ed74df2731e63 Mon Sep 17 00:00:00 2001 From: Brian Del Vecchio Date: Fri, 13 Jan 2017 15:38:42 -0500 Subject: [PATCH 25/30] simplify configuration following suggestions from @derekrushforth --- Gruntfile.js | 15 ++++++++--- tasks/grunt-postmark-templates.js | 43 +++++-------------------------- 2 files changed, 18 insertions(+), 40 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index b4c307f..746d869 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -7,7 +7,6 @@ module.exports = function(grunt) { grunt.initConfig({ secrets: grunt.file.exists('secrets.json') ? grunt.file.readJSON('secrets.json') : {}, - templates: grunt.file.exists('templates.json') ? grunt.file.readJSON('templates.json') : null, /* Postmark ------------------------------------------------- */ @@ -31,8 +30,15 @@ module.exports = function(grunt) { // you can either specify the template configuration here, or in templates.json 'postmark-templates': { - test_email_file: { - name: 'testing-postmark-templates-js-' + new Date().valueOf(), + test_email: { + name: 'testing-postmark-templates-js1-' + new Date().valueOf(), + subject: 'Testing grunt-postmark-templates', + // NOTE these are read from filesystem. globbing not supported + htmlBody: 'test/email.html', + textBody: 'test/email.txt', + }, + test_email_again: { + name: 'testing-postmark-templates-js2-' + new Date().valueOf(), subject: 'Testing grunt-postmark-templates', // NOTE these are read from filesystem. globbing not supported htmlBody: 'test/email.html', @@ -44,6 +50,9 @@ module.exports = function(grunt) { grunt.loadTasks('tasks'); + // you can also get a JSON report of uploaded templates (default filename: templates-output.json) + grunt.registerTask('all-templates', ['postmark-templates', 'postmark-templates-output']); + grunt.registerTask('default', ['postmark', 'postmark-templates']); }; diff --git a/tasks/grunt-postmark-templates.js b/tasks/grunt-postmark-templates.js index 2cfdf88..c575b72 100644 --- a/tasks/grunt-postmark-templates.js +++ b/tasks/grunt-postmark-templates.js @@ -9,41 +9,7 @@ module.exports = function(grunt) { 'use strict'; - // There are a number of different ways to populate this configuration object, - // including gruntfile configuration for "postmark-templates", - // or a templates.json file in your project. - // - // The object should have the following format, using the template name as the key. - // Here is an example templates.json file from a Mailmason project: - // - // { - // "confirm_email": { - // "subject": "Please confirm your email", - // "htmlBody": "dist/confirm_email.html", - // "textBody": "dist/confirm_email.txt", - // "templateId": 1201660 - // } - // } - // - // templateId is optional; if present, the template with the given ID will be updated. This will prevent - // proliferation of outdated versions of templates with the same name, and depending on how you're using - // them, reduce the configuration overhead (for example publishing these templateIds to your backend). - // - // The postmark-templates task will write an output file in this format including the current templateId. - // The default output file is named templates-output.json. You may ignore this file, or manually copy it - // to templates.json after a successful run. - - grunt.registerTask('postmark-templates', 'create or update a set of templates', function() { - var templates = grunt.config('templates') || grunt.config('postmark-templates'); - templates = templates || (grunt.file.exists('templates.json') ? grunt.file.readJSON('templates.json') : null); - - grunt.config.set('_postmark-template', templates); - grunt.config.set('updatedTemplates', {}); - grunt.task.run('_postmark-template'); - grunt.task.run('_postmark-output'); - }); - - grunt.registerMultiTask('_postmark-template', 'Create or update PostMark template', function() { + grunt.registerMultiTask('postmark-templates', 'Create or update Postmark templates', function() { var done = this.async(); var options = this.options(); @@ -106,7 +72,7 @@ module.exports = function(grunt) { } else { template.templateId = response.TemplateId; // append this record to the result array - var upd = grunt.config.get('updatedTemplates'); + var upd = grunt.config.get('updatedTemplates') || {}; var tname = template.name; delete template.name; upd[tname] = template; @@ -129,7 +95,10 @@ module.exports = function(grunt) { grunt.log.writeln('Template ' + name + ' pushed: ' + JSON.stringify(templateId)); } - grunt.registerTask('_postmark-output', 'writes out the resulting template IDs', function() { + // invoke this task after postmark-templates to get an output file containing the resulting template IDs + // this is in the same format as the postmark-templates config. + + grunt.registerTask('postmark-templates-output', 'writes out the resulting template IDs', function() { var options = this.options({ filename: "templates-output.json" From 90ec8054e476815956996fbd42ed31849d50c6b4 Mon Sep 17 00:00:00 2001 From: Brian Del Vecchio Date: Fri, 13 Jan 2017 15:51:13 -0500 Subject: [PATCH 26/30] log "created" vs "updated" --- tasks/grunt-postmark-templates.js | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/tasks/grunt-postmark-templates.js b/tasks/grunt-postmark-templates.js index c575b72..1778e7e 100644 --- a/tasks/grunt-postmark-templates.js +++ b/tasks/grunt-postmark-templates.js @@ -52,13 +52,16 @@ module.exports = function(grunt) { delete expanded.templateId; client.createTemplate(expanded, function(err, response) { handleResponse(err, response, done, template); + grunt.log.writeln('Template ' + template.name + ' created: ' + JSON.stringify(response.TemplateId)); }); } else { + grunt.log.writeln('Template ' + template.name + ' updated: ' + JSON.stringify(response.TemplateId)); handleResponse(err, response, done, template); } }); } else { client.createTemplate(expanded, function(err, response) { + grunt.log.writeln('Template ' + template.name + ' created: ' + JSON.stringify(response.TemplateId)); handleResponse(err, response, done, template); }); } @@ -71,14 +74,13 @@ module.exports = function(grunt) { done(); } else { template.templateId = response.TemplateId; - // append this record to the result array + // append this record to the result array, used by postmark-templates-output task var upd = grunt.config.get('updatedTemplates') || {}; var tname = template.name; delete template.name; upd[tname] = template; grunt.config.set('updatedTemplates', upd); - successMessage(tname, template.templateId); done(template); } } @@ -91,10 +93,6 @@ module.exports = function(grunt) { } } - function successMessage(name, templateId) { - grunt.log.writeln('Template ' + name + ' pushed: ' + JSON.stringify(templateId)); - } - // invoke this task after postmark-templates to get an output file containing the resulting template IDs // this is in the same format as the postmark-templates config. From 315fd1930b4fcf934fffc4851b1627db09d01c7c Mon Sep 17 00:00:00 2001 From: Brian Del Vecchio Date: Fri, 13 Jan 2017 15:51:46 -0500 Subject: [PATCH 27/30] sample Gruntfile reads templates.json or defaults to inline config --- Gruntfile.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 746d869..45a90d0 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -29,18 +29,16 @@ module.exports = function(grunt) { }, // you can either specify the template configuration here, or in templates.json - 'postmark-templates': { + 'postmark-templates': grunt.file.exists('templates.json') ? grunt.file.readJSON('templates.json') : { test_email: { name: 'testing-postmark-templates-js1-' + new Date().valueOf(), subject: 'Testing grunt-postmark-templates', - // NOTE these are read from filesystem. globbing not supported htmlBody: 'test/email.html', textBody: 'test/email.txt', }, test_email_again: { name: 'testing-postmark-templates-js2-' + new Date().valueOf(), subject: 'Testing grunt-postmark-templates', - // NOTE these are read from filesystem. globbing not supported htmlBody: 'test/email.html', textBody: 'test/email.txt', } @@ -51,7 +49,7 @@ module.exports = function(grunt) { grunt.loadTasks('tasks'); // you can also get a JSON report of uploaded templates (default filename: templates-output.json) - grunt.registerTask('all-templates', ['postmark-templates', 'postmark-templates-output']); + grunt.registerTask('templates-logged', ['postmark-templates', 'postmark-templates-output']); grunt.registerTask('default', ['postmark', 'postmark-templates']); From 314332ba93fd772f98102935408e3e09f1d1fee8 Mon Sep 17 00:00:00 2001 From: Brian Del Vecchio Date: Sun, 18 Nov 2018 08:12:07 -0500 Subject: [PATCH 28/30] older WIP --- tasks/grunt-postmark-templates.js | 33 ++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/tasks/grunt-postmark-templates.js b/tasks/grunt-postmark-templates.js index 1778e7e..bcd147e 100644 --- a/tasks/grunt-postmark-templates.js +++ b/tasks/grunt-postmark-templates.js @@ -31,28 +31,39 @@ module.exports = function(grunt) { grunt.fail.warn('Missing required template property "subject" \n'); } + if (!template.htmlBody && !template.htmlSrc) { + grunt.log.error('Missing template property "htmlBody" or "htmlSrc"\n'); + } + + if (!template.textBody && !template.textSrc) { + grunt.log.error('Missing template property "textBody" or "textSrc"\n'); + } + var postmark = require('postmark'); var client = new postmark.Client(serverToken); // read the referenced files, but hold on to the original filenames - var expanded = Object.assign({}, template); - - if (expanded.htmlBody) { - expanded.htmlBody = grunt.file.read(expanded.htmlBody); - } - if (expanded.textBody) { - expanded.textBody = grunt.file.read(expanded.textBody); - } + var expanded = { + Name: template.name, + Subject: template.subject, + HtmlBody: template.htmlBody || grunt.file.read(template.htmlSrc), + TextBody: template.textBody || grunt.file.read(template.textSrc), + TemplateId: template.templateId + }; if (template.templateId) { client.editTemplate(template.templateId, expanded, function(err, response) { if (err && err.code === 1101) { grunt.log.warn('Template ' + template.templateId + ' not found, so attempting create'); delete template.templateId; - delete expanded.templateId; + delete expanded.TemplateId; client.createTemplate(expanded, function(err, response) { + if (err == null) { + grunt.log.writeln('Template ' + JSON.stringify(template.name) + ' created: ' + JSON.stringify(response.TemplateId)); + } else { + grunt.log.writeln('Error creating template ' + template.name + ': ' + JSON.stringify(err)); + } handleResponse(err, response, done, template); - grunt.log.writeln('Template ' + template.name + ' created: ' + JSON.stringify(response.TemplateId)); }); } else { grunt.log.writeln('Template ' + template.name + ' updated: ' + JSON.stringify(response.TemplateId)); @@ -61,7 +72,7 @@ module.exports = function(grunt) { }); } else { client.createTemplate(expanded, function(err, response) { - grunt.log.writeln('Template ' + template.name + ' created: ' + JSON.stringify(response.TemplateId)); + grunt.log.writeln('Template ' + expanded.Name + ' created: ' + JSON.stringify(response.TemplateId)); handleResponse(err, response, done, template); }); } From 53e7131ce39e5b337c4486120170c6a838ff1769 Mon Sep 17 00:00:00 2001 From: Brian Del Vecchio Date: Wed, 2 Jan 2019 11:31:45 -0500 Subject: [PATCH 29/30] improve error handling on createTemplate --- tasks/grunt-postmark-templates.js | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/tasks/grunt-postmark-templates.js b/tasks/grunt-postmark-templates.js index bcd147e..cd27164 100644 --- a/tasks/grunt-postmark-templates.js +++ b/tasks/grunt-postmark-templates.js @@ -15,6 +15,8 @@ module.exports = function(grunt) { var options = this.options(); var template = this.data; + var defaultLocale = 'en'; + var serverToken = options.serverToken || grunt.config('secrets.serverToken') || grunt.config('secret.postmark.server_token'); if (!serverToken) { @@ -42,6 +44,8 @@ module.exports = function(grunt) { var postmark = require('postmark'); var client = new postmark.Client(serverToken); + var locale = template.locale || defaultLocale; + // read the referenced files, but hold on to the original filenames var expanded = { Name: template.name, @@ -72,8 +76,14 @@ module.exports = function(grunt) { }); } else { client.createTemplate(expanded, function(err, response) { - grunt.log.writeln('Template ' + expanded.Name + ' created: ' + JSON.stringify(response.TemplateId)); + if (!err && response.TemplateId) { + grunt.log.writeln('Template ' + expanded.Name + ' created: ' + JSON.stringify(response.TemplateId)); + } else { + grunt.log.writeln('Error on createTemplate(' + expanded.name + '): ' + JSON.stringify(err) + JSON.stringify(response)); + + } handleResponse(err, response, done, template); + }); } @@ -84,6 +94,7 @@ module.exports = function(grunt) { errorMessage(err); done(); } else { + template.templateId = response.TemplateId; // append this record to the result array, used by postmark-templates-output task var upd = grunt.config.get('updatedTemplates') || {}; From 394f7bc6fa35111ee24bbb4cc5e1b170c001c781 Mon Sep 17 00:00:00 2001 From: Brian Del Vecchio Date: Thu, 3 Jan 2019 06:57:28 -0500 Subject: [PATCH 30/30] improve error handling --- tasks/grunt-postmark-templates.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tasks/grunt-postmark-templates.js b/tasks/grunt-postmark-templates.js index cd27164..471e98f 100644 --- a/tasks/grunt-postmark-templates.js +++ b/tasks/grunt-postmark-templates.js @@ -67,12 +67,15 @@ module.exports = function(grunt) { } else { grunt.log.writeln('Error creating template ' + template.name + ': ' + JSON.stringify(err)); } - handleResponse(err, response, done, template); }); - } else { + } else if (!err && response.TemplateId) { grunt.log.writeln('Template ' + template.name + ' updated: ' + JSON.stringify(response.TemplateId)); - handleResponse(err, response, done, template); + } else { + grunt.log.writeln('Error on createTemplate(' + template.name + '): ' + JSON.stringify(err) + JSON.stringify(response)); } + + handleResponse(err, response, done, template); + }); } else { client.createTemplate(expanded, function(err, response) {