From 6256ec69cbe7ac296d9a2cac837deb9d7a29941e Mon Sep 17 00:00:00 2001 From: Rhianna Souder Date: Fri, 20 Jun 2025 12:18:20 -0400 Subject: [PATCH 1/5] First commit to my Integrating With HubSpot I: Foundations practicum repository. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 76d6d581..e9b7a12c 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ 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-na2.hubspot.com/contacts/243065647/objects/2-169705155/views/all/list ___ ## Tips: From ff422d5bf65696382700eae6e699f8159f290446 Mon Sep 17 00:00:00 2001 From: Rhianna Souder Date: Fri, 20 Jun 2025 12:26:53 -0400 Subject: [PATCH 2/5] updates.pug file --- views/updates.pug | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 views/updates.pug diff --git a/views/updates.pug b/views/updates.pug new file mode 100644 index 00000000..ca59ed53 --- /dev/null +++ b/views/updates.pug @@ -0,0 +1,27 @@ +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") + body + h1= title + form(method="POST", action="/update-cobj") + label(for="pet_name") Pet Name: + input(type="text", name="pet_name", required) + br + + label(for="pet_breed") Pet Breed: + input(type="text". name="pet_breed") + br + + label(for="pet_favorite_toy") Pet Favorite Toy: + input(type="text", name="pet_favorite_toy") + br + + button(type="submit") Submit + + br + a(href="/") Return to the homepage + \ No newline at end of file From 0341038307c0854ba9628fbcca633114298cdc71 Mon Sep 17 00:00:00 2001 From: Rhianna Souder Date: Fri, 20 Jun 2025 12:33:42 -0400 Subject: [PATCH 3/5] app.post call --- index.js | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index f337a32d..5c117e30 100644 --- a/index.js +++ b/index.js @@ -20,7 +20,29 @@ const PRIVATE_APP_ACCESS = ''; // 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 +// POST form submission +app.post('/update-cobj', async (req, res) => { + const { pet_name, pet_breed, pet_favorite_toy } = req.body + + try { + const response = await axios.post(`${HUBSPOT_API_URL}/crm/v3/objects/${OBJECT_TYPE_ID}`, { + properties: { + pet_name, + pet_breed, + pet_favorite_toy, + }, + }, { + headers: { + Authorization: `Bearer ${PRIVATE_APP_ACCESS}`, + 'Content-Type': 'application/json', + }, + }); + res.redirect('/'); + } catch(err) { + console.error('Error creating object:', err.response?.data || err.message); + res.status(500).send('Failed to create record'); + } +}); /** * * This is sample code to give you a reference for how you should structure your calls. From 68ae5df0129e0a0087e37e2a569d691619dd6357 Mon Sep 17 00:00:00 2001 From: Rhianna Souder Date: Fri, 20 Jun 2025 12:49:49 -0400 Subject: [PATCH 4/5] app.get skipped this step --- index.js | 10 ++++++++-- package.json | 6 +++--- public/css/style.css | 18 ++++++++++++++++++ 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/index.js b/index.js index 5c117e30..4552b674 100644 --- a/index.js +++ b/index.js @@ -16,13 +16,18 @@ const PRIVATE_APP_ACCESS = ''; // 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 +app.get('/update-cobj', (req, res) => { + res.render('updates', { + title: 'Update Custom Object Form | Integrating With HubSpot I Practicum', + }); +}); // 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. // POST form submission app.post('/update-cobj', async (req, res) => { - const { pet_name, pet_breed, pet_favorite_toy } = req.body + const { pet_name, pet_breed, pet_favorite_toy } = req.body; + console.log('Form submitted: ', req.body); try { const response = await axios.post(`${HUBSPOT_API_URL}/crm/v3/objects/${OBJECT_TYPE_ID}`, { @@ -37,6 +42,7 @@ app.post('/update-cobj', async (req, res) => { 'Content-Type': 'application/json', }, }); + console.log("Record created:", response.data); res.redirect('/'); } catch(err) { console.error('Error creating object:', err.response?.data || err.message); diff --git a/package.json b/package.json index 62db37aa..bc6c3989 100644 --- a/package.json +++ b/package.json @@ -9,8 +9,8 @@ "author": "HubSpot Academy learner", "license": "ISC", "dependencies": { - "axios": "^1.3.5", - "express": "^4.18.2", - "pug": "^3.0.2" + "axios": "^1.10.0", + "express": "^16.5.0", + "pug": "^3.0.3" } } diff --git a/public/css/style.css b/public/css/style.css index 85587bb4..a5624872 100644 --- a/public/css/style.css +++ b/public/css/style.css @@ -55,4 +55,22 @@ input[type="submit"] { border: none; padding: .375rem 1rem; margin-top: 10px; +} + +body { + font-family: Arial, sans-serif; + padding: 20px; +} +table { + border-collapse: collapse; + width: 100%; +} +th, td { + border: 1px solid #CCC; + padding: 8px; + text-align: left; +} +a { + display: inline-block; + margin-bottom: 10px; } \ No newline at end of file From ffae96fb82c7371cdcea6fc756642ed338d7ee17 Mon Sep 17 00:00:00 2001 From: Rhianna Souder Date: Fri, 20 Jun 2025 13:20:21 -0400 Subject: [PATCH 5/5] create, read custom object practicum --- index.js | 34 ++++++++++++++++++++++++++++++++-- package.json | 3 ++- views/homepage.pug | 26 ++++++++++++++++++++++++++ views/updates.pug | 2 +- 4 files changed, 61 insertions(+), 4 deletions(-) create mode 100644 views/homepage.pug diff --git a/index.js b/index.js index 4552b674..23ba2eb9 100644 --- a/index.js +++ b/index.js @@ -1,18 +1,48 @@ const express = require('express'); const axios = require('axios'); +const path = require('path'); +require('dotenv').config(); + const app = express(); +const PORT = process.env.PORT || 3000; app.set('view engine', 'pug'); +app.set('views', path.join(__dirname, 'views')); app.use(express.static(__dirname + '/public')); app.use(express.urlencoded({ extended: true })); + app.use(express.json()); +const HUBSPOT_API_URL = process.env.HUBSPOT_API_URL; +const OBJECT_TYPE_ID = process.env.OBJECT_TYPE_ID; + // * 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 = ''; +const PRIVATE_APP_ACCESS = process.env.HUBSPOT_ACCESS_TOKEN; // 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. -// * Code for Route 1 goes here +app.get('/', async (req, res) => { + try { + const response = await axios.get(`${HUBSPOT_API_URL}/crm/v3/objects/${OBJECT_TYPE_ID}`, { + headers: { + Authorization: `Bearer ${PRIVATE_APP_ACCESS}`, + 'Content-Type' : 'application/json' + }, + params: { + properties: 'pet_name, pet_breed, pet_favorite_toy', + limit: 100, + }, + }); + const records = response.data.results; + res.render('homepage', { + title: 'Custom Object Records | Practicum', + records, + }); + } catch(error) { + console.error('Error fetching custom object: ', error.response?.data || error.message); + res.status(500).send('Error fetching custom object'); + } +}); // 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. diff --git a/package.json b/package.json index bc6c3989..1da5e679 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,8 @@ "license": "ISC", "dependencies": { "axios": "^1.10.0", - "express": "^16.5.0", + "dotenv": "^16.5.0", + "express": "4.21.2", "pug": "^3.0.3" } } diff --git a/views/homepage.pug b/views/homepage.pug new file mode 100644 index 00000000..5700a1de --- /dev/null +++ b/views/homepage.pug @@ -0,0 +1,26 @@ +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") + body + h1= title + a(href="/update-cobj") Add to this table + table + thead + tr + th Pet Name + th Pet Breed + th Favorite Toy + tbody + if records.length + each record in records + tr + td= record.properties.pet_name + td= record.properties.pet_breed + td= record.properties.pet_favorite_toy + else + tr + td(colspan="3") No record found \ No newline at end of file diff --git a/views/updates.pug b/views/updates.pug index ca59ed53..e41e0ce2 100644 --- a/views/updates.pug +++ b/views/updates.pug @@ -13,7 +13,7 @@ html(lang="en") br label(for="pet_breed") Pet Breed: - input(type="text". name="pet_breed") + input(type="text", name="pet_breed") br label(for="pet_favorite_toy") Pet Favorite Toy: