Skip to content

Instantly share code, notes, and snippets.

@nathanforce
Created September 30, 2021 14:55
Show Gist options
  • Save nathanforce/686d64b2dc4fb2afdb2e00fa2a77151d to your computer and use it in GitHub Desktop.
Save nathanforce/686d64b2dc4fb2afdb2e00fa2a77151d to your computer and use it in GitHub Desktop.
Sendgrid Github Action
import * as core from '@actions/core';
import * as github from '@actions/github';
import client from '@sendgrid/client';
import path from 'path';
import fs from 'fs';
import colors from 'ansi-styles';
// @ts-ignore
import HumanHash from 'humanhash';
async function upload(files: string[]) {
// Setup
const SG_API_KEY = core.getInput('SENDGRID_API_KEY', { required: true });
client.setApiKey(SG_API_KEY);
try {
const existingTemplatesByName = await getTemplates();
const versionName = humanize();
let promises: Promise<any>[] = [];
for (const template of files) {
const name = path.basename(template, '.html');
const relativePath = path.relative(process.cwd(), template);
const existing = existingTemplatesByName.get(name);
const html = await fs.promises.readFile(template, 'utf-8');
let promise;
if (existing && existing.activeVersion.html_content === html) {
promise = Promise.resolve().then(() => {
core.info(`${colors.gray.open}[SKIPPED] ${relativePath}`);
});
} else if (existing) {
promise = createTemplateVersion(existing.id, versionName, html).then(
() => {
core.info(
`${colors.green.open}[UPDATED] ${relativePath} => ${name}:${versionName}`
);
}
);
} else {
promise = createTemplate(name).then(template => {
return createTemplateVersion(template.id, versionName, html).then(
() => {
core.info(
`${colors.green.open}[CREATED] ${relativePath} => ${name}:${versionName}`
);
}
);
});
}
promises.push(promise);
}
await Promise.all(promises);
} catch (e) {
throw new Error(`Unable to fetch from Sendgrid: ${e.message}`);
}
}
async function activate(files: string[]) {
const SG_API_KEY = core.getInput('SENDGRID_API_KEY', { required: true });
client.setApiKey(SG_API_KEY);
const existingTemplatesByName = await getTemplates();
try {
const versionName = humanize();
let promises: Promise<any>[] = [];
for (const file of files) {
const templateName = path.basename(file, '.html');
const template = existingTemplatesByName.get(templateName);
const version = template.versions.find(v => {
return v.name === versionName;
});
promises.push(
activateTemplateVersion(template.id, version.id).then(() => {
core.info(
`${colors.green.open}[ACTIVATED] ${versionName} is now the active template for template ${templateName}`
);
})
);
}
await Promise.all(promises);
} catch (e) {
throw new Error(
`Error when trying to activate templates with Sendgrid API: ${e.message}`
);
}
}
function humanize() {
const { sha: VERSION } = github.context;
const humanizer = new HumanHash();
return humanizer.humanize(VERSION);
}
async function createTemplate(name: string) {
const [response] = await client.request({
method: 'POST',
url: '/v3/templates',
body: {
generation: 'dynamic',
name,
},
});
return response.body;
}
async function createTemplateVersion(
template: string,
name: string,
html: string
) {
const [response] = await client.request({
method: 'POST',
url: `/v3/templates/${template}/versions`,
body: {
active: 0, // do not activate yet. This happens in a separate step (deployment)
html_content: html,
name: name,
subject: '{{subject}}',
generate_plain_content: false,
editor: 'code',
},
});
return response.body;
}
async function activateTemplateVersion(template: string, version: string) {
const [response] = await client.request({
method: 'POST',
url: `/v3/templates/${template}/versions/${version}/activate`,
});
return response.body;
}
async function getTemplates() {
const [response] = await client.request({
method: 'GET',
url: '/v3/templates?generations=dynamic',
});
const { templates } = response.body as { templates: any[] };
let promises: Promise<any>[] = [];
for (const template of templates) {
const activeVersion = template.versions.find(v => {
return v.active === 1;
});
const promise = client
.request({
method: 'GET',
url: `/v3/templates/${template.id}/versions/${activeVersion.id}`,
})
.then(([response]) => {
template.activeVersion = response.body;
});
promises.push(promise);
}
await Promise.all(promises);
const existingTemplatesByName = templates.reduce((all, template) => {
all.set(template.name, template);
return all;
}, new Map<string, any>());
return existingTemplatesByName;
}
(async () => {
try {
const action = core.getInput('action', { required: true });
const input = core.getInput('templates');
const templates = JSON.parse(input) || [];
if (templates.length < 1) {
core.info(
`${colors.gray.open}[SKIPPED] No templates provided for action ${action}`
);
return;
}
if (action === 'UPLOAD') {
await upload(templates);
} else if (action === 'ACTIVATE') {
await activate(templates);
}
} catch (error) {
core.setFailed(error);
}
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment