diff --git a/README.md b/README.md index 76d6d581..d0977861 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,8 @@ This repository is for the Integrating With HubSpot I: Foundations course. This To read the full directions, please go to the [practicum instructions](https://app.hubspot.com/academy/l/tracks/1092124/1093824/5493?language=en). -**Put your HubSpot developer test account custom objects URL link here:** https://app.hubspot.com/contacts/l/objects/${custom-obj-number}/views/all/list +**Put your HubSpot developer test account custom objects URL link here:** (https://app.hubspot.com/contacts/50294925/objects/2-48102010/views/all/list) + ___ ## Tips: diff --git a/index.js b/index.js index f337a32d..506e17a0 100644 --- a/index.js +++ b/index.js @@ -1,71 +1,108 @@ +// Load environment variables from .env file +require('dotenv').config(); + const express = require('express'); const axios = require('axios'); const app = express(); +const PORT = process.env.PORT || 3000; -app.set('view engine', 'pug'); -app.use(express.static(__dirname + '/public')); -app.use(express.urlencoded({ extended: true })); -app.use(express.json()); - -// * Please DO NOT INCLUDE the private app access token in your repo. Don't do this practicum in your normal account. -const PRIVATE_APP_ACCESS = ''; - -// TODO: ROUTE 1 - Create a new app.get route for the homepage to call your custom object data. Pass this data along to the front-end and create a new pug template in the views folder. +// HubSpot Private App Access Token from .env +const HUBSPOT_ACCESS_TOKEN = process.env.HUBSPOT_ACCESS_TOKEN; -// * Code for Route 1 goes here +// This is the fully qualified name for your custom object. +// You MUST replace this placeholder with the exact value from your HubSpot account. +// It should be in the format: 'p' + [Portal ID] + '_' + [Internal name] +const CUSTOM_OBJECT_NAME_FOR_API = 'p50294925_pets'; -// TODO: ROUTE 2 - Create a new app.get route for the form to create or update new custom object data. Send this data along in the next route. - -// * Code for Route 2 goes here - -// TODO: ROUTE 3 - Create a new app.post route for the custom objects form to create or update your custom object data. Once executed, redirect the user to the homepage. - -// * Code for Route 3 goes here +// Middleware to parse URL-encoded bodies (from HTML forms) +app.use(express.urlencoded({ extended: true })); +// Serve static files from the 'public' directory +app.use(express.static('public')); +// Set Pug as the templating engine +app.set('view engine', 'pug'); +app.set('views', './views'); -/** -* * This is sample code to give you a reference for how you should structure your calls. +// =================================== +// === Routes for the Practicum === +// =================================== -* * App.get sample -app.get('/contacts', async (req, res) => { - const contacts = 'https://api.hubspot.com/crm/v3/objects/contacts'; - const headers = { - Authorization: `Bearer ${PRIVATE_APP_ACCESS}`, - 'Content-Type': 'application/json' - } +// App.get for the homepage ("/") +// Retrieves and displays all custom object records in a table. +app.get('/', async (req, res) => { try { - const resp = await axios.get(contacts, { headers }); - const data = resp.data.results; - res.render('contacts', { title: 'Contacts | HubSpot APIs', data }); + // HubSpot API endpoint to retrieve all custom object records + // Make sure to include all your custom property internal names in the 'properties' query parameter. + const getAllUrl = `https://api.hubapi.com/crm/v3/objects/${CUSTOM_OBJECT_NAME_FOR_API}?properties=name,species,bio,dog`; + + const response = await axios.get(getAllUrl, { + headers: { + 'Authorization': `Bearer ${HUBSPOT_ACCESS_TOKEN}`, + 'Content-Type': 'application/json' + } + }); + + // The custom object records are in the 'results' array + const customObjects = response.data.results; + + res.render('homepage', { + title: 'Custom Objects | Integrating With HubSpot I Practicum', + customObjects: customObjects + }); } catch (error) { - console.error(error); + console.error('Error fetching custom object records:', error.response ? error.response.data : error.message); + res.status(500).send('Error fetching custom object records.'); } }); -* * App.post sample -app.post('/update', async (req, res) => { - const update = { - properties: { - "favorite_book": req.body.newVal - } - } +// App.get for rendering the update form ("update-cobj") +app.get('/update-cobj', (req, res) => { + // Renders the 'updates.pug' template, which contains the form. + res.render('updates', { + title: 'Update Custom Object Form | Integrating With HubSpot I Practicum' + }); +}); - const email = req.query.email; - const updateContact = `https://api.hubapi.com/crm/v3/objects/contacts/${email}?idProperty=email`; - const headers = { - Authorization: `Bearer ${PRIVATE_APP_ACCESS}`, - 'Content-Type': 'application/json' +// App.post for creating a new custom object record ("/update-cobj") +app.post('/update-cobj', async (req, res) => { + // Get form data from the request body, including the new 'dog' property + const { name, species, bio, dog } = req.body; + + // Create the properties object to send to HubSpot + // The keys must match your custom property internal names exactly. + const properties = { + name: name, + species: species, + bio: bio, + dog: dog // <-- Added the required 'dog' property }; - try { - await axios.patch(updateContact, update, { headers } ); - res.redirect('back'); - } catch(err) { - console.error(err); - } + const data = { + properties: properties + }; + try { + // HubSpot API endpoint to create a custom object record + const createUrl = `https://api.hubapi.com/crm/v3/objects/${CUSTOM_OBJECT_NAME_FOR_API}`; + + // Make the API call + await axios.post(createUrl, data, { + headers: { + 'Authorization': `Bearer ${HUBSPOT_ACCESS_TOKEN}`, + 'Content-Type': 'application/json' + } + }); + + console.log('Custom object record created successfully!'); + // Redirect to the homepage to see the new record + res.redirect('/'); + } catch (error) { + console.error('Error creating custom object record:', error.response ? error.response.data : error.message); + res.status(500).send('Error creating custom object record.'); + } }); -*/ -// * Localhost -app.listen(3000, () => console.log('Listening on http://localhost:3000')); \ No newline at end of file +// Start the server +app.listen(PORT, () => { + console.log(`Server running on http://localhost:${PORT}`); +}); diff --git a/package.json b/package.json index 62db37aa..3cda101d 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "license": "ISC", "dependencies": { "axios": "^1.3.5", + "dotenv": "^16.4.5", "express": "^4.18.2", "pug": "^3.0.2" } diff --git a/views/homepage.pug b/views/homepage.pug new file mode 100644 index 00000000..f9ffda95 --- /dev/null +++ b/views/homepage.pug @@ -0,0 +1,44 @@ +doctype html +html(lang="en") + head + meta(charset="UTF-8") + meta(name="viewport", content="width=device-width, initial-scale=1.0") + title= title + link(rel="stylesheet", href="/css/style.css") + style. + table { + width: 100%; + border-collapse: collapse; + margin-top: 20px; + } + th, td { + border: 1px solid #ddd; + padding: 8px; + text-align: left; + } + th { + background-color: #f2f2f2; + } + body + .container + h1 Custom Objects + p + a(href="/update-cobj") Add to this table + + if customObjects && customObjects.length > 0 + table + thead + tr + th Name + th Species + th Bio + // Add more