diff --git a/.github/workflows/zip-and-upload-to-s3-production.yml b/.github/workflows/zip-and-upload-to-s3-production.yml index dbc0d00..e70da42 100644 --- a/.github/workflows/zip-and-upload-to-s3-production.yml +++ b/.github/workflows/zip-and-upload-to-s3-production.yml @@ -1,9 +1,9 @@ -name: Zip and Upload to S3 for Development +name: Zip and Upload to S3 for Production on: push: branches: - - production + - main jobs: deploy-after-testing: @@ -21,4 +21,4 @@ jobs: aws_secret_access_key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws_bucket: ${{ secrets.AWS_BUCKET }} source_dir: ./zip - destination_dir: "v1/production" \ No newline at end of file + destination_dir: "v1/production" diff --git a/manifest.json b/manifest.json index 1e20bb0..c4032f5 100644 --- a/manifest.json +++ b/manifest.json @@ -3,6 +3,9 @@ { "metadata": "/snippets/functions/third-party/manifest.json" }, + { + "metadata": "/snippets/functions/boilerplate/manifest.json" + }, { "metadata": "/snippets/functions/mongodb-crud/manifest.json" }, @@ -19,5 +22,5 @@ "metadata": "/snippets/triggers/project/manifest.json" } ], - "version": "a1c17cc564ffff9fe3413fdba419df03c8e34778" + "version": "7395fc1e8214723dc941ed9e718b4ea36d303e1c" } diff --git a/snippets/functions/boilerplate/databaseTrigger.js b/snippets/functions/boilerplate/databaseTrigger.js new file mode 100644 index 0000000..508921d --- /dev/null +++ b/snippets/functions/boilerplate/databaseTrigger.js @@ -0,0 +1,40 @@ +exports = async function (changeEvent) { + // A Database Trigger will always call a function with a changeEvent. + // Documentation on ChangeEvents: https://www.mongodb.com/docs/manual/reference/change-events + + // This sample function will listen for events and replicate them to a collection in a different Database + + // Access the _id of the changed document: + const docId = changeEvent.documentKey._id; + + // Get the MongoDB service you want to use (see "Linked Data Sources" tab) + const serviceName = "mongodb-atlas"; + const databaseName = "other_db_name"; + const collection = context.services + .get(serviceName) + .db(databaseName) + .collection(changeEvent.ns.coll); + + // Get the "FullDocument" present in the Insert/Replace/Update ChangeEvents + try { + // If this is a "delete" event, delete the document in the other collection + if (changeEvent.operationType === "delete") { + await collection.deleteOne({ _id: docId }); + } + + // If this is an "insert" event, insert the document into the other collection + else if (changeEvent.operationType === "insert") { + await collection.insertOne(changeEvent.fullDocument); + } + + // If this is an "update" or "replace" event, then replace the document in the other collection + else if ( + changeEvent.operationType === "update" || + changeEvent.operationType === "replace" + ) { + await collection.replaceOne({ _id: docId }, changeEvent.fullDocument); + } + } catch (err) { + console.log("error performing mongodb write: ", err.message); + } +}; diff --git a/snippets/functions/boilerplate/eventbridgeErrorFunction.js b/snippets/functions/boilerplate/eventbridgeErrorFunction.js new file mode 100644 index 0000000..fb8c4f5 --- /dev/null +++ b/snippets/functions/boilerplate/eventbridgeErrorFunction.js @@ -0,0 +1,18 @@ +exports = async function (error, changeEvent) { + // This sample function will log additional details if the error is not + // a DOCUMENT_TOO_LARGE error + if (error.code === "DOCUMENT_TOO_LARGE") { + console.log("Document too large error"); + + // Comment out the line below in order to skip this event and not suspend the Trigger + throw new Error(`Encountered error: ${error.code}`); + } + + console.log("Error sending event to EventBridge"); + console.log(`DB: ${changeEvent.ns.db}`); + console.log(`Collection: ${changeEvent.ns.coll}`); + console.log(`Operation type: ${changeEvent.operationType}`); + + // Throw an error in your function to suspend the trigger and stop processing additional events + throw new Error(`Encountered error: ${error.message}`); +}; diff --git a/snippets/functions/boilerplate/generalFunction.js b/snippets/functions/boilerplate/generalFunction.js new file mode 100644 index 0000000..3c6055f --- /dev/null +++ b/snippets/functions/boilerplate/generalFunction.js @@ -0,0 +1,42 @@ +exports = async function (arg) { + // This default function will get a value and find a document in MongoDB + // To see plenty more examples of what you can do with functions see: + // https://www.mongodb.com/docs/atlas/app-services/functions/ + + // Find the name of the MongoDB service you want to use (see "Linked Data Sources" tab) + const serviceName = "mongodb-atlas"; + + // Update these to reflect your db/collection + const dbName = "db_name"; + const collName = "coll_name"; + + // Get a collection from the context + const collection = context.services + .get(serviceName) + .db(dbName) + .collection(collName); + + let findResult; + try { + // Get a value from the context (see "Values" tab) + // Update this to reflect your value's name. + const valueName = "value_name"; + const value = context.values.get(valueName); + + // Execute a FindOne in MongoDB + findResult = await collection.findOne({ + owner_id: context.user.id, + fieldName: value, + argField: arg, + }); + } catch (err) { + console.log("Error occurred while executing findOne:", err.message); + + return { error: err.message }; + } + + // To call other named functions: + // const result = context.functions.execute("function_name", arg1, arg2); + + return { result: findResult }; +}; diff --git a/snippets/functions/boilerplate/manifest.json b/snippets/functions/boilerplate/manifest.json new file mode 100644 index 0000000..63348fa --- /dev/null +++ b/snippets/functions/boilerplate/manifest.json @@ -0,0 +1,31 @@ +{ + "category": "Boilerplate Template by Function Type", + "categoryId": "4756072f-b3c7-4469-b038-cdf6b346789c", + "viewType": "functionSnippet", + "snippets": [ + { + "id": "7c84ed29-b8b2-466c-af3d-c240889d3112", + "title": "Database Trigger", + "snippet": "/databaseTrigger.js", + "description": "Example shows a template for a DB trigger function, which always accepts a changeEvent as an argument." + }, + { + "id": "ba6113d7-5e52-48e4-8ab4-03df30c0d61a", + "title": "Scheduled Trigger", + "snippet": "/scheduledTrigger.js", + "description": "Shows an example of a Scheduled Trigger function, which will always call a function without arguments." + }, + { + "id": "1dca4a58-c150-4a8e-b602-9edf5c338f3d", + "title": "General Function", + "snippet": "/generalFunction.js", + "description": "Example of a default function that will get a value and find a document in MongoDB, the parameters passed in the header differ from other templates." + }, + { + "id": "b43b9abb-dde7-4389-880e-38ef995d501a", + "title": "Eventbridge Custom Error Function", + "snippet": "/eventbridgeErrorFunction.js", + "description": "Example of a custom error function, which takes in the error and changeEvent as arguments." + } + ] +} diff --git a/snippets/functions/boilerplate/scheduledTrigger.js b/snippets/functions/boilerplate/scheduledTrigger.js new file mode 100644 index 0000000..df4d635 --- /dev/null +++ b/snippets/functions/boilerplate/scheduledTrigger.js @@ -0,0 +1,21 @@ +exports = async function () { + // A Scheduled Trigger will always call a function without arguments. + // Documentation on Triggers: https://www.mongodb.com/docs/atlas/app-services/triggers/ + + // Functions run by Triggers are run as System users and have full access to Services, Functions, and MongoDB Data. + + // Get the MongoDB service you want to use (see "Linked Data Sources" tab) + const serviceName = "mongodb-atlas"; + const databaseName = "db_name"; + const collectionName = "coll_name"; + const collection = context.services + .get(serviceName) + .db(databaseName) + .collection(collectionName); + + try { + const doc = await collection.findOne({ name: "mongodb" }); + } catch (err) { + console.log("error performing mongodb findOne: ", err.message); + } +}; diff --git a/snippets/triggers/match/manifest.json b/snippets/triggers/match/manifest.json index ee0a057..e18bcc4 100644 --- a/snippets/triggers/match/manifest.json +++ b/snippets/triggers/match/manifest.json @@ -1,31 +1,31 @@ { - "category": "MatchTriggerExpressions", - "categoryId": "ed4b3598-6800-413f-9158-d9b1b2a2a080", - "viewType": "triggerMatch", - "snippets": [ - { - "id": "ed4b3598-6800-413f-9158-d9b1b2a2a081", - "title": "Match Expression for Updates", - "snippet": "/update.json", - "description": "This trigger will only execute if the `storeLocation` array has changed to \"East Langley\" and the \"storeItems\" field has been removed." - }, - { - "id": "ed4b3598-6800-413f-9158-d9b1b2a2a082", - "title": "Match Expression for Deletes", - "snippet": "/delete.json", - "description": "This trigger will only execute if the document (before deletion) has a userName of \"Alice Smith\"" - }, - { - "id": "ed4b3598-6800-413f-9158-d9b1b2a2a083", - "title": "Match Expression for Inserts", - "snippet": "/insert.json", - "description": "This trigger will only execute if the document has a userName of \"Alice Smith\" and a department of \"engineering\"" - }, - { - "id": "ed4b3598-6800-413f-9158-d9b1b2a2a084", - "title": "Match Expression for Replace", - "snippet": "/replace.json", - "description": "This trigger will only execute if the name field was \"Alice Smith\" before the Replace, and \"Alex Smith\" after the Replace." - } - ] + "category": "MatchTriggerExpressions", + "categoryId": "ed4b3598-6800-413f-9158-d9b1b2a2a080", + "viewType": "triggerMatch", + "snippets": [ + { + "id": "ed4b3598-6800-413f-9158-d9b1b2a2a081", + "title": "Update", + "snippet": "/update.json", + "description": "This trigger will only execute if the `storeLocation` array has changed to \"East Langley\" and the \"storeItems\" field has been removed." + }, + { + "id": "ed4b3598-6800-413f-9158-d9b1b2a2a082", + "title": "Delete", + "snippet": "/delete.json", + "description": "This trigger will only execute if the document (before deletion) has a userName of \"Alice Smith\"" + }, + { + "id": "ed4b3598-6800-413f-9158-d9b1b2a2a083", + "title": "Insert", + "snippet": "/insert.json", + "description": "This trigger will only execute if the document has a userName of \"Alice Smith\" and a department of \"engineering\"" + }, + { + "id": "ed4b3598-6800-413f-9158-d9b1b2a2a084", + "title": "Replace", + "snippet": "/replace.json", + "description": "This trigger will only execute if the name field was \"Alice Smith\" before the Replace, and \"Alex Smith\" after the Replace." + } + ] } diff --git a/snippets/triggers/project/manifest.json b/snippets/triggers/project/manifest.json index 4025c78..905d08c 100644 --- a/snippets/triggers/project/manifest.json +++ b/snippets/triggers/project/manifest.json @@ -1,31 +1,31 @@ { - "category": "ProjectTriggerExpressions", - "categoryId": "ee1e471e-72c7-4dc9-b80a-9f745836a254", - "viewType": "triggerProject", - "snippets": [ - { - "id": "7dd66555-8efb-4a76-8920-64c0874e0214", - "title": "Project Expression for Updates", - "snippet": "/update.json", - "description": "This trigger will return the \"operationType\" and \"updateDescription.updateFields.storeLocation\" field values." - }, - { - "id": "7c9b14f4-30ec-4865-b153-8bd52c6566ad", - "title": "Project Expression for Deletes", - "snippet": "/delete.json", - "description": "This trigger will return the \"documentKey._id\", \"operationType\", and \"wallTime\" field values." - }, - { - "id": "e040947b-08a4-4a8f-9f82-1bccd7826892", - "title": "Project Expression for Inserts", - "snippet": "/insert.json", - "description": "This trigger will return the \"baseSalary\", \"department\", \"operationType\", and \"documentKey.userName\" field values." - }, - { - "id": "90768f43-3d5a-4746-8ccc-f709cda09eae", - "title": "Project Expression for Replace", - "snippet": "/replace.json", - "description": "This trigger will return the \"operationType\", \"fullDocument.name\", and \"fullDocumentBeforeChange.name\" field values." - } - ] + "category": "ProjectTriggerExpressions", + "categoryId": "ee1e471e-72c7-4dc9-b80a-9f745836a254", + "viewType": "triggerProject", + "snippets": [ + { + "id": "7dd66555-8efb-4a76-8920-64c0874e0214", + "title": "Update", + "snippet": "/update.json", + "description": "This trigger will return the \"operationType\" and \"updateDescription.updateFields.storeLocation\" field values." + }, + { + "id": "7c9b14f4-30ec-4865-b153-8bd52c6566ad", + "title": "Delete", + "snippet": "/delete.json", + "description": "This trigger will return the \"documentKey._id\", \"operationType\", and \"wallTime\" field values." + }, + { + "id": "e040947b-08a4-4a8f-9f82-1bccd7826892", + "title": "Insert", + "snippet": "/insert.json", + "description": "This trigger will return the \"baseSalary\", \"department\", \"operationType\", and \"documentKey.userName\" field values." + }, + { + "id": "90768f43-3d5a-4746-8ccc-f709cda09eae", + "title": "Replace", + "snippet": "/replace.json", + "description": "This trigger will return the \"operationType\", \"fullDocument.name\", and \"fullDocumentBeforeChange.name\" field values." + } + ] }