@estaldo/n8n-nodes-postmarkapp 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/nodes/PostmarkEmail/PostmarkEmail.node.js +83 -0
- package/dist/nodes/PostmarkEmail/descriptions/SendEmailBatch.js +73 -0
- package/dist/nodes/PostmarkEmail/descriptions/SendEmailBatchWithTemplates.js +76 -0
- package/dist/nodes/PostmarkEmail/descriptions/SendEmailSingle.js +76 -0
- package/dist/nodes/PostmarkEmail/descriptions/SendEmailSingleWithTemplate.js +50 -0
- package/dist/nodes/PostmarkEmail/descriptions/additionalFields.js +165 -0
- package/dist/nodes/PostmarkEmail/descriptions/index.js +22 -0
- package/dist/nodes/PostmarkEmail/descriptions/operation.js +37 -0
- package/dist/nodes/PostmarkEmail/descriptions/sharedFields.js +31 -0
- package/dist/nodes/PostmarkEmail/helpers/buildEmailBody.js +133 -0
- package/dist/nodes/PostmarkEmail/helpers/errors.js +15 -0
- package/dist/nodes/PostmarkEmail/helpers/index.js +19 -0
- package/dist/nodes/PostmarkEmail/helpers/mergeAdditionalFields.js +75 -0
- package/dist/nodes/PostmarkEmail/operations.js +21 -0
- package/dist/nodes/PostmarkEmail/postmarkEmail.svg +5 -0
- package/dist/package.json +14 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +24 -0
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.collectSingleMessage = collectSingleMessage;
|
|
4
|
+
exports.collectBatchMessages = collectBatchMessages;
|
|
5
|
+
exports.buildEmailBody = buildEmailBody;
|
|
6
|
+
const n8n_workflow_1 = require("n8n-workflow");
|
|
7
|
+
const mergeAdditionalFields_1 = require("./mergeAdditionalFields");
|
|
8
|
+
// --- Collectors -------------------------------------------------------------
|
|
9
|
+
//
|
|
10
|
+
// Read the raw n8n parameters for one or more messages and shape them into the flat
|
|
11
|
+
// source objects that `buildEmailBody` consumes. The same source shape is used for
|
|
12
|
+
// both single and batch operations, so single send / sendWithTemplate paths reuse
|
|
13
|
+
// the same body builder as each entry in a batch.
|
|
14
|
+
// Reads the top-level single-send / sendWithTemplate parameters into a source object.
|
|
15
|
+
// Optional fields are now flat at the top level (no "Additional Fields" wrapper) — we
|
|
16
|
+
// collect them individually and pack them into `additionalFields` so buildEmailBody's
|
|
17
|
+
// merge logic stays unchanged.
|
|
18
|
+
function collectSingleMessage(ctx, itemIndex, kind) {
|
|
19
|
+
const source = {
|
|
20
|
+
from: ctx.getNodeParameter('from', itemIndex),
|
|
21
|
+
to: ctx.getNodeParameter('to', itemIndex),
|
|
22
|
+
additionalFields: collectFlatOptionalFields(ctx, itemIndex),
|
|
23
|
+
};
|
|
24
|
+
if (kind === 'plain') {
|
|
25
|
+
source.subject = ctx.getNodeParameter('subject', itemIndex);
|
|
26
|
+
source.htmlBody = ctx.getNodeParameter('htmlBody', itemIndex, '');
|
|
27
|
+
source.textBody = ctx.getNodeParameter('textBody', itemIndex, '');
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
source.templateSource = ctx.getNodeParameter('templateSource', itemIndex);
|
|
31
|
+
source.templateId = ctx.getNodeParameter('templateId', itemIndex);
|
|
32
|
+
source.templateModel = ctx.getNodeParameter('templateModel', itemIndex, '{}');
|
|
33
|
+
}
|
|
34
|
+
return source;
|
|
35
|
+
}
|
|
36
|
+
// Pulls the per-message array out of the appropriate batch fixedCollection. Each
|
|
37
|
+
// raw message has flat fields (Bcc, Cc, Headers, etc. all at the message-entry
|
|
38
|
+
// level, no nested Additional Fields). We restructure each message to nest the
|
|
39
|
+
// optional fields under `additionalFields` so buildEmailBody can consume them
|
|
40
|
+
// uniformly with single-send sources.
|
|
41
|
+
function collectBatchMessages(ctx, itemIndex, kind) {
|
|
42
|
+
const paramName = kind === 'templated' ? 'batchMessagesWithTemplates' : 'batchMessages';
|
|
43
|
+
const collection = ctx.getNodeParameter(paramName, itemIndex, {});
|
|
44
|
+
const messages = collection.message;
|
|
45
|
+
if (!Array.isArray(messages)) {
|
|
46
|
+
return [];
|
|
47
|
+
}
|
|
48
|
+
return messages.map(restructureBatchMessage);
|
|
49
|
+
}
|
|
50
|
+
// Reads each top-level optional Postmark field into a flat object. mergeAdditionalFields
|
|
51
|
+
// downstream skips empty strings and empty fixedCollections, so unfilled fields don't
|
|
52
|
+
// leak into the request body.
|
|
53
|
+
function collectFlatOptionalFields(ctx, itemIndex) {
|
|
54
|
+
return {
|
|
55
|
+
Attachments: ctx.getNodeParameter('Attachments', itemIndex, {}),
|
|
56
|
+
Bcc: ctx.getNodeParameter('Bcc', itemIndex, ''),
|
|
57
|
+
Cc: ctx.getNodeParameter('Cc', itemIndex, ''),
|
|
58
|
+
Headers: ctx.getNodeParameter('Headers', itemIndex, {}),
|
|
59
|
+
Metadata: ctx.getNodeParameter('Metadata', itemIndex, {}),
|
|
60
|
+
ReplyTo: ctx.getNodeParameter('ReplyTo', itemIndex, ''),
|
|
61
|
+
Tag: ctx.getNodeParameter('Tag', itemIndex, ''),
|
|
62
|
+
TrackLinks: ctx.getNodeParameter('TrackLinks', itemIndex, 'None'),
|
|
63
|
+
TrackOpens: ctx.getNodeParameter('TrackOpens', itemIndex, false),
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
// Splits a flat batch message entry into primary fields + an `additionalFields` bucket
|
|
67
|
+
// containing everything else, matching the source shape buildEmailBody expects.
|
|
68
|
+
function restructureBatchMessage(msg) {
|
|
69
|
+
const { from, to, subject, htmlBody, textBody, templateSource, templateId, templateModel, ...additionalFields } = msg;
|
|
70
|
+
return {
|
|
71
|
+
from,
|
|
72
|
+
to,
|
|
73
|
+
subject,
|
|
74
|
+
htmlBody,
|
|
75
|
+
textBody,
|
|
76
|
+
templateSource,
|
|
77
|
+
templateId,
|
|
78
|
+
templateModel,
|
|
79
|
+
additionalFields,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
// --- Builder ----------------------------------------------------------------
|
|
83
|
+
//
|
|
84
|
+
// Turn a flat source object (whether from `collectSingleMessage` or one entry of
|
|
85
|
+
// `collectBatchMessages`) into the Postmark request body for that message.
|
|
86
|
+
function buildEmailBody(ctx, itemIndex, source, kind) {
|
|
87
|
+
const body = {
|
|
88
|
+
From: source.from,
|
|
89
|
+
To: source.to,
|
|
90
|
+
};
|
|
91
|
+
if (kind === 'plain') {
|
|
92
|
+
const htmlBody = source.htmlBody ?? '';
|
|
93
|
+
const textBody = source.textBody ?? '';
|
|
94
|
+
if (!htmlBody && !textBody) {
|
|
95
|
+
throw new n8n_workflow_1.NodeOperationError(ctx.getNode(), 'At least one of HTML Body or Text Body must be set', { itemIndex });
|
|
96
|
+
}
|
|
97
|
+
body.Subject = source.subject;
|
|
98
|
+
if (htmlBody) {
|
|
99
|
+
body.HtmlBody = htmlBody;
|
|
100
|
+
}
|
|
101
|
+
if (textBody) {
|
|
102
|
+
body.TextBody = textBody;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
const templateSource = source.templateSource;
|
|
107
|
+
const templateId = source.templateId;
|
|
108
|
+
if (templateSource === 'alias') {
|
|
109
|
+
body.TemplateAlias = templateId;
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
const id = parseInt(templateId, 10);
|
|
113
|
+
if (Number.isNaN(id)) {
|
|
114
|
+
throw new n8n_workflow_1.NodeOperationError(ctx.getNode(), `Template ID must be numeric, got '${templateId}'`, { itemIndex });
|
|
115
|
+
}
|
|
116
|
+
body.TemplateId = id;
|
|
117
|
+
}
|
|
118
|
+
const modelRaw = source.templateModel ?? '{}';
|
|
119
|
+
let model;
|
|
120
|
+
try {
|
|
121
|
+
model = typeof modelRaw === 'string'
|
|
122
|
+
? JSON.parse(modelRaw)
|
|
123
|
+
: modelRaw;
|
|
124
|
+
}
|
|
125
|
+
catch (err) {
|
|
126
|
+
throw new n8n_workflow_1.NodeOperationError(ctx.getNode(), `Invalid Template Model JSON: ${err.message}`, { itemIndex });
|
|
127
|
+
}
|
|
128
|
+
body.TemplateModel = model;
|
|
129
|
+
}
|
|
130
|
+
const additionalFields = source.additionalFields ?? {};
|
|
131
|
+
(0, mergeAdditionalFields_1.mergeAdditionalFields)(body, additionalFields);
|
|
132
|
+
return body;
|
|
133
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.wrapPostmarkError = wrapPostmarkError;
|
|
4
|
+
const n8n_workflow_1 = require("n8n-workflow");
|
|
5
|
+
function wrapPostmarkError(ctx, err, itemIndex) {
|
|
6
|
+
// The HTTP helper surfaces Postmark's { ErrorCode, Message } body on error responses.
|
|
7
|
+
// Pull it through so workflow authors see the API's reason rather than a generic 422.
|
|
8
|
+
const e = err;
|
|
9
|
+
const postmarkMessage = e.response?.body?.Message;
|
|
10
|
+
const postmarkCode = e.response?.body?.ErrorCode;
|
|
11
|
+
const detail = postmarkMessage
|
|
12
|
+
? `Postmark error ${postmarkCode ?? '?'}: ${postmarkMessage}`
|
|
13
|
+
: (e.message ?? 'Unknown error');
|
|
14
|
+
return new n8n_workflow_1.NodeOperationError(ctx.getNode(), detail, { itemIndex });
|
|
15
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./mergeAdditionalFields"), exports);
|
|
18
|
+
__exportStar(require("./buildEmailBody"), exports);
|
|
19
|
+
__exportStar(require("./errors"), exports);
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.mergeAdditionalFields = mergeAdditionalFields;
|
|
4
|
+
exports.applyDefaultMessageStream = applyDefaultMessageStream;
|
|
5
|
+
function mergeAdditionalFields(body, additional) {
|
|
6
|
+
for (const [key, value] of Object.entries(additional)) {
|
|
7
|
+
if (value === undefined || value === null || value === '') {
|
|
8
|
+
continue;
|
|
9
|
+
}
|
|
10
|
+
if (key === 'Metadata') {
|
|
11
|
+
const pairs = unwrapFixedCollection(value, 'pair');
|
|
12
|
+
const metadata = {};
|
|
13
|
+
for (const pair of pairs) {
|
|
14
|
+
const k = pair.key;
|
|
15
|
+
const v = pair.value;
|
|
16
|
+
if (k) {
|
|
17
|
+
metadata[k] = v;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
if (Object.keys(metadata).length > 0) {
|
|
21
|
+
body.Metadata = metadata;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
else if (key === 'Headers') {
|
|
25
|
+
const headers = unwrapFixedCollection(value, 'header')
|
|
26
|
+
.filter((h) => h.Name)
|
|
27
|
+
.map((h) => ({
|
|
28
|
+
Name: h.Name,
|
|
29
|
+
Value: h.Value ?? '',
|
|
30
|
+
}));
|
|
31
|
+
if (headers.length > 0) {
|
|
32
|
+
body.Headers = headers;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
else if (key === 'Attachments') {
|
|
36
|
+
const attachments = unwrapFixedCollection(value, 'attachment')
|
|
37
|
+
.filter((a) => a.Name && a.Content)
|
|
38
|
+
.map((a) => {
|
|
39
|
+
const att = {
|
|
40
|
+
Name: a.Name,
|
|
41
|
+
Content: a.Content,
|
|
42
|
+
ContentType: a.ContentType || 'application/octet-stream',
|
|
43
|
+
};
|
|
44
|
+
if (a.ContentID) {
|
|
45
|
+
att.ContentID = a.ContentID;
|
|
46
|
+
}
|
|
47
|
+
return att;
|
|
48
|
+
});
|
|
49
|
+
if (attachments.length > 0) {
|
|
50
|
+
body.Attachments = attachments;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
body[key] = value;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
// Sets MessageStream on the body if not already present and a default is provided.
|
|
59
|
+
// Per-message MessageStream (set via Additional Fields) wins over the top-level default.
|
|
60
|
+
function applyDefaultMessageStream(body, defaultStream) {
|
|
61
|
+
if (defaultStream && !body.MessageStream) {
|
|
62
|
+
body.MessageStream = defaultStream;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
// fixedCollection with multipleValues=true wraps the items array under a single key
|
|
66
|
+
// (defined as `name` on the option). Pull that array out, defending against missing or
|
|
67
|
+
// malformed shapes that n8n sometimes hands us when a user adds zero items.
|
|
68
|
+
function unwrapFixedCollection(value, innerKey) {
|
|
69
|
+
const obj = value;
|
|
70
|
+
const collection = obj?.[innerKey];
|
|
71
|
+
if (!Array.isArray(collection)) {
|
|
72
|
+
return [];
|
|
73
|
+
}
|
|
74
|
+
return collection;
|
|
75
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Single source of truth for the operation identifiers this node supports. Used both
|
|
3
|
+
// by description files (option values, displayOptions) and by the execute() switch
|
|
4
|
+
// logic, so a typo anywhere becomes a TypeScript error rather than a silent runtime
|
|
5
|
+
// no-op.
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.OPERATION_ENDPOINTS = exports.Operation = void 0;
|
|
8
|
+
exports.Operation = {
|
|
9
|
+
Send: 'send',
|
|
10
|
+
SendWithTemplate: 'sendWithTemplate',
|
|
11
|
+
SendBatch: 'sendBatch',
|
|
12
|
+
SendBatchWithTemplates: 'sendBatchWithTemplates',
|
|
13
|
+
};
|
|
14
|
+
// Every operation must have an endpoint — TypeScript's exhaustiveness check on
|
|
15
|
+
// `Record<Operation, string>` keeps this in lockstep with the const above.
|
|
16
|
+
exports.OPERATION_ENDPOINTS = {
|
|
17
|
+
[exports.Operation.Send]: '/email',
|
|
18
|
+
[exports.Operation.SendWithTemplate]: '/email/withTemplate',
|
|
19
|
+
[exports.Operation.SendBatch]: '/email/batch',
|
|
20
|
+
[exports.Operation.SendBatchWithTemplates]: '/email/batchWithTemplates',
|
|
21
|
+
};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 60" width="60" height="60">
|
|
2
|
+
<rect width="60" height="60" rx="10" fill="#FFDE00"/>
|
|
3
|
+
<path d="M14 20 H46 V44 H14 Z" fill="none" stroke="#1A1A1A" stroke-width="3" stroke-linejoin="round"/>
|
|
4
|
+
<path d="M14 20 L30 34 L46 20" fill="none" stroke="#1A1A1A" stroke-width="3" stroke-linejoin="round"/>
|
|
5
|
+
</svg>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@estaldo/n8n-nodes-postmarkapp",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Postmark integration nodes for n8n",
|
|
5
|
+
"n8n": {
|
|
6
|
+
"n8nNodesApiVersion": 1,
|
|
7
|
+
"nodes": [
|
|
8
|
+
"nodes/PostmarkEmail/PostmarkEmail.node.js"
|
|
9
|
+
]
|
|
10
|
+
},
|
|
11
|
+
"peerDependencies": {
|
|
12
|
+
"n8n-workflow": "*"
|
|
13
|
+
}
|
|
14
|
+
}
|