Skip to content

Instantly share code, notes, and snippets.

@ryanrousseau
Last active February 15, 2020 18:31
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ryanrousseau/2f9b42f4c20681f73001c42205d6c5c3 to your computer and use it in GitHub Desktop.
Save ryanrousseau/2f9b42f4c20681f73001c42205d6c5c3 to your computer and use it in GitHub Desktop.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const rp = require('request-promise');
admin.initializeApp(functions.config().firebase);
let categoryToEmojiMapping = null;
let projectToChannelMapping = null;
let db = admin.firestore();
db.settings({ timestampsInSnapshots: true });
function authorizeRequest(req, res) {
const providedToken = req.get('octolog-token');
const token = functions.config().octolog.authtoken;
if (!providedToken || providedToken !== token) {
return Promise.reject({
code: 401,
message: 'Missing or invalid token'
});
}
return Promise.resolve([req, res]);
}
function getPayload([req, res]) {
const payload = req.body.Payload;
if (payload) {
return Promise.resolve(payload);
}
return Promise.reject({
code: 400,
message: 'No payload provided'
});
}
function loadMappings(payload) {
if (categoryToEmojiMapping && projectToChannelMapping) {
return Promise.resolve([payload, categoryToEmojiMapping, projectToChannelMapping]);
}
const collection = db.collection("mappings");
const categoryToEmojiPromise = collection.doc('categoryToEmoji').get();
const projectToChannelPromise = collection.doc('projectToChannel').get();
return Promise.all([categoryToEmojiPromise, projectToChannelPromise])
.then(([categoryToEmojiDoc, projectToChannelDoc]) => {
categoryToEmojiMapping = categoryToEmojiDoc.data();
projectToChannelMapping = projectToChannelDoc.data();
return [payload, categoryToEmojiMapping, projectToChannelMapping];
});
}
function createSlackOptions([payload, categoryToEmoji, projectToChannel]) {
const projectId = payload.Event.RelatedDocumentIds.find(id => id.startsWith('Projects-'));
const channel = projectToChannel[projectId];
const emoji = categoryToEmoji[payload.Event.Category];
return {
"channel": channel,
"text": `${emoji} ${payload.Event.Message} ${emoji}`,
"username": `Octopus Subscription: ${payload.Subscription.Name}`
};
}
function sendSlackMessage(options) {
const slackUri = functions.config().slack.uri;
const requestOptions = {
method: 'POST',
uri: slackUri,
body: {
"channel": options.channel,
"username": options.username,
"icon_emoji": ":octopusdeploy:",
"text": options.text
},
json: true
}
return rp(requestOptions);
}
function checkForDuplicate(payload) {
return db.runTransaction((transaction) => {
const eventReference = db.collection("deployments").doc(payload.Event.Id);
return transaction.get(eventReference).then((eventDoc) => {
if (eventDoc.exists) {
return Promise.reject({
code: 200,
message: `Event ${payload.Event.Id} has already been processed.`
});
}
transaction.set(eventReference, payload);
console.log("Document written with ID: ", payload.Event.Id);
return payload;
});
});
}
function handleRejection(res, reason) {
if (reason.message) {
console.warn(reason.message);
return res.status(reason.code).send(reason.message);
}
console.warn(reason);
return res.status(400).send();
}
exports.logOctopusEvent = functions.https.onRequest((req, res) => {
const sendOkResponse = () => { return res.status(200).send(); };
const callHandleRejection = (reason) => {
return handleRejection(res, reason);
}
return authorizeRequest(req, res)
.then(getPayload)
.then(checkForDuplicate)
.then(loadMappings)
.then(createSlackOptions)
.then(sendSlackMessage)
.then(sendOkResponse)
.catch(callHandleRejection);
});
{
"Timestamp": "2019-04-26T18:37:44.1581725+00:00",
"EventType": "SubscriptionPayload",
"Payload": {
"ServerUri": "https://myoctopusurl",
"ServerAuditUri": "https://myoctopusurl/#/configuration/audit?environments=Environments-246&eventCategories=DeploymentFailed&eventCategories=DeploymentStarted&eventCategories=DeploymentSucceeded&from=2019-04-26T18%3a37%3a12.%2b00%3a00&to=2019-04-26T18%3a37%3a42.%2b00%3a00",
"BatchProcessingDate": "2019-04-26T18:37:42.7832114+00:00",
"Subscription": {
"Id": "Subscriptions-161",
"Name": "Production Deployments",
"Type": 0,
"IsDisabled": false,
"EventNotificationSubscription": {
"Filter": {
"Users": [],
"Projects": [],
"Environments": [
"Environments-246"
],
"EventGroups": [],
"EventCategories": [
"DeploymentFailed",
"DeploymentStarted",
"DeploymentSucceeded"
],
"EventAgents": [],
"Tenants": [],
"Tags": [],
"DocumentTypes": []
},
"EmailTeams": [],
"EmailFrequencyPeriod": "01:00:00",
"EmailPriority": 0,
"EmailDigestLastProcessed": null,
"EmailDigestLastProcessedEventAutoId": null,
"EmailShowDatesInTimeZoneId": "UTC",
"WebhookURI": "https://mywebhookurl/logOctopusEvent",
"WebhookTeams": [],
"WebhookTimeout": "00:00:10",
"WebhookHeaderKey": null,
"WebhookHeaderValue": null,
"WebhookLastProcessed": "2019-04-26T18:37:12.4560433+00:00",
"WebhookLastProcessedEventAutoId": 187275
},
"SpaceId": "Spaces-83",
"Links": {
"Self": {}
}
},
"Event": {
"Id": "Events-189579",
"RelatedDocumentIds": [
"Deployments-15970",
"Projects-670",
"Releases-6856",
"Environments-246",
"ServerTasks-318123",
"Channels-690",
"ProjectGroups-302"
],
"Category": "DeploymentStarted",
"UserId": "users-system",
"Username": "system",
"IsService": false,
"IdentityEstablishedWith": "",
"UserAgent": "Server",
"Occurred": "2019-04-26T18:37:34.3616214+00:00",
"Message": "Deploy to Prod (#3) started for Accounting Database release 10.33.210 to Prod",
"MessageHtml": "<a href='#/deployments/Deployments-15970'>Deploy to Prod (#3)</a> started for <a href='#/projects/Projects-670'>Accounting Database</a> release <a href='#/releases/Releases-6856'>10.33.210</a> to <a href='#/environments/Environments-246'>Prod</a>",
"MessageReferences": [
{
"ReferencedDocumentId": "Deployments-15970",
"StartIndex": 0,
"Length": 19
},
{
"ReferencedDocumentId": "Projects-670",
"StartIndex": 33,
"Length": 19
},
{
"ReferencedDocumentId": "Releases-6856",
"StartIndex": 61,
"Length": 9
},
{
"ReferencedDocumentId": "Environments-246",
"StartIndex": 74,
"Length": 4
}
],
"Comments": null,
"Details": null,
"SpaceId": "Spaces-83",
"Links": {
"Self": {}
}
},
"BatchId": "e6df5aae-a42a-4bd8-8b0d-43065f82d5f0",
"TotalEventsInBatch": 1,
"EventNumberInBatch": 1
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment