Search…
⌃K

Zapier

To add Zapier in their project, users have to go on Zapier website and login or sign-up, find and connect the Teta with any other app they want to automate actions with, and then just activate their new Zap and test if it works as intended.
An example of Zap could be composed of a Trigger that listens to email in Gmail containing the keyword “Appointment” in the header, and an Action that adds all the details of the appointment in a document inside the user project inside Teta.
To manage all of this, there is a custom NodeJS software running on the Teta Servers that listens for user generated zaps, and registers it.

Authentication

This program manages different requests routes, the first step to enable some of them is to check whenever a valid user is sending the request.
To check it there is the useAuth() function:
export function useAuth(req: Request, res: Response, next: NextFunction) {
const apiKey = req.headers['x-zapier-api-key'] as string;
if (!apiKey) return res.status(401).json({ error: 'Authorization needed' });
const result = decode(apiKey);
if (result === false) return res.status(401).json({ error:
'Malformed token' });
res.locals.prj_id = parseInt(result.prj_id);
next();
}
used as middleware, it takes the ‘x-zapier-api-key’ header from the request and, if it exists and is valid, decodes it and extracts the prj_id of the user saving it in the locals object inside res. In this manner, every route has the info available in their res object.

Adding and Removing

To let user add Teta in Zapier, they only need to know their x-zapier-api-key (it’s the same key used to do requests in the TetaCms), right now there is no native option to do it in the Teta Web editor and the only way to do it is to check in the network requests the browser makes with the Teta Cms while on the editor. So an interface displaying this key to the final user it’s necessary.
router.post('/subscribe', useAuth, json(), async (req, res) => {
try {
const { prj_id } = res.locals;
const { collection_name } = req.body;
const subscription = new WebhookModel<IWebhook>({
prj_id,
action: 'insert',
collection_name,
webhook_url: req.body.hookUrl
});
const save = await subscription.save();
return res.status(201).json(save);
} catch (ex) {
console.error(ex);
return res.status(500).json({ error: ex.message });
} });
router.delete('/unsubscribe', useAuth, json(), async (req, res) => {
try {
await WebhookModel.deleteOne({ _id: req.body.id });
return res.status(200);
} catch (ex) {
console.error(ex);
return res.status(500).json({ error: ex.message });
}
});

Route 1: '/subscribe'

This route is used to create a new webhook subscription for a specific collection in the project associated with the authenticated user.
It is protected by the useAuth middleware, meaning that the user must be authenticated to access this route.
It takes in a JSON object in the request body containing the following properties:
  • collection_name: the name of the collection to subscribe to.
  • hookUrl: the URL of the webhook to be notified when an 'insert' action occurs on the specified collection.
It creates a new instance of the WebhookModel class with the project ID from the res.locals object, the specified collection name, and the provided webhook URL.
It then saves this new WebHook to the database and returns the saved instance to the client with a status code of 201.

Route 2: '/unsubscribe'

This route is used to delete an existing webhook subscription.
It is protected by the useAuth middleware, meaning that the user must be authenticated to access this route.
It takes in a JSON object in the request body containing the property 'id': the ID of the webhook subscription to delete.
It uses the deleteOne() method provided by the WebhookModel to remove the specified subscription from the database.
If the unsubscription is successful, it returns a 200 status code.
These two routes work together to allow the app to create and delete webhook subscriptions in relation to Zapier webhooks. The '/subscribe' route allows a user to specify a collection and a webhook URL to be notified when an 'insert' action occurs on that collection, while the '/unsubscribe' route allows a user to delete an existing webhook subscription.

Activation

To manage execute Zapier we use this route handler:
app.get('/trigger/:action/:prj_id/:coll_id/:doc_id', async (req, res) => {
try {
const prj_id = parseInt(req.params.prj_id);
const collection = await getCollection(prj_id, req.params.coll_id);
const targets = await WebhookModel.find({ prj_id, collection_name:
collection.name, action: req.params.action });
const documents = await getDocuments(prj_id, req.params.coll_id);
const document = documents.length > 0 ? documents[documents.length -
1] : {}
for (const target of targets) {
const url = target.webhook_url;
fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(document)
}).then(data => {
console.log(data.status);
return data.json();
}).then(data => {
console.log(data);
}) }
} catch (ex) {
console.error(ex);
return res.status(500).json({ error: ex.message });
} });
When a client makes a request to this endpoint, the server performs the following actions:
  1. 1.
    It parses the project ID from the URL parameters and uses it to retrieve information about the collection that the event pertains to.
  2. 2.
    It queries the WebhookModel to find all targets (i.e., Zapier webhooks) that have been set up for the specified action and collection.
  3. 3.
    It retrieves the latest document from the specified collection using the project ID and collection ID passed in the URL parameters.
  4. 4.
    For each target, it sends an POST request to the specified webhook URL, with the latest document as the payload in JSON format.
The purpose of this code is to allow Teta to communicate with Zapier and trigger actions when certain events occur. This allows for a seamless integration with other services and tools that can be configured on Zapier.