Skip to content

Commit fa709b8

Browse files
committed
source-hubspot-native: Expand HubSpot Engagement associations to support multiple object types
Update the Engagement model and schema to include associations for contacts, companies, tickets, content, quotes, orders, emails, meetings, notes, tasks, carts, partner_clients, and marketing_event.
1 parent 231abfd commit fa709b8

File tree

4 files changed

+250
-26
lines changed

4 files changed

+250
-26
lines changed

source-hubspot-native/acmeCo/engagements.schema.yaml

+78
Original file line numberDiff line numberDiff line change
@@ -100,12 +100,90 @@ properties:
100100
default: {}
101101
title: Associations
102102
type: object
103+
contacts:
104+
default: []
105+
items:
106+
type: integer
107+
title: Contacts
108+
type: array
109+
companies:
110+
default: []
111+
items:
112+
type: integer
113+
title: Companies
114+
type: array
103115
deals:
104116
default: []
105117
items:
106118
type: integer
107119
title: Deals
108120
type: array
121+
tickets:
122+
default: []
123+
items:
124+
type: integer
125+
title: Tickets
126+
type: array
127+
content:
128+
default: []
129+
items:
130+
type: integer
131+
title: Content
132+
type: array
133+
quotes:
134+
default: []
135+
items:
136+
type: integer
137+
title: Quotes
138+
type: array
139+
orders:
140+
default: []
141+
items:
142+
type: integer
143+
title: Orders
144+
type: array
145+
emails:
146+
default: []
147+
items:
148+
type: integer
149+
title: Emails
150+
type: array
151+
meetings:
152+
default: []
153+
items:
154+
type: integer
155+
title: Meetings
156+
type: array
157+
notes:
158+
default: []
159+
items:
160+
type: integer
161+
title: Notes
162+
type: array
163+
tasks:
164+
default: []
165+
items:
166+
type: integer
167+
title: Tasks
168+
type: array
169+
carts:
170+
default: []
171+
items:
172+
type: integer
173+
title: Carts
174+
type: array
175+
partner_clients:
176+
default: []
177+
items:
178+
type: integer
179+
title: Partner Clients
180+
type: array
181+
marketing_event:
182+
default: []
183+
items:
184+
type: integer
185+
title: Marketing Event
186+
type: array
109187
required:
110188
- id
111189
- createdAt

source-hubspot-native/source_hubspot_native/models.py

+65-26
Original file line numberDiff line numberDiff line change
@@ -110,12 +110,20 @@ class Names(StrEnum):
110110
quotes = auto()
111111
subscriptions = auto()
112112
tickets = auto()
113+
emails = auto()
114+
meetings = auto()
115+
notes = auto()
116+
tasks = auto()
117+
content = auto()
118+
orders = auto()
119+
carts = auto()
120+
partner_clients = auto()
121+
marketing_event = auto()
113122

114123

115124
# A Property is a HubSpot or HubSpot-user defined attribute that's
116125
# attached to a HubSpot CRM object.
117126
class Property(BaseDocument, extra="allow"):
118-
119127
name: str = ""
120128
calculated: bool = False
121129
hubspotObject: str = "unknown" # Added by us.
@@ -126,7 +134,6 @@ class Properties(BaseDocument, extra="forbid"):
126134

127135

128136
class DealPipeline(BaseDocument, extra="allow"):
129-
130137
createdAt: AwareDatetime | None
131138
updatedAt: AwareDatetime | None
132139

@@ -136,7 +143,6 @@ class DealPipelines(BaseDocument, extra="forbid"):
136143

137144

138145
class Owner(BaseDocument, extra="allow"):
139-
140146
createdAt: AwareDatetime | None
141147
updatedAt: AwareDatetime | None
142148

@@ -185,7 +191,6 @@ def _post_init(self) -> Self:
185191
k: v for k, v in self.propertiesWithHistory.items() if len(v)
186192
}
187193

188-
189194
# If the model has attached inline associations,
190195
# hoist them to corresponding arrays. Then clear associations.
191196
for ae in self.ASSOCIATED_ENTITIES:
@@ -222,9 +227,40 @@ class Deal(BaseCRMObject):
222227

223228

224229
class Engagement(BaseCRMObject):
225-
ASSOCIATED_ENTITIES = [Names.deals]
230+
ASSOCIATED_ENTITIES = [
231+
Names.contacts,
232+
Names.companies,
233+
Names.deals,
234+
Names.tickets,
235+
Names.quotes,
236+
Names.orders,
237+
Names.emails,
238+
Names.meetings,
239+
Names.notes,
240+
Names.tasks,
241+
Names.content,
242+
Names.orders,
243+
Names.carts,
244+
Names.partner_clients,
245+
Names.marketing_event,
246+
]
226247

248+
contacts: list[int] = []
249+
companies: list[int] = []
227250
deals: list[int] = []
251+
tickets: list[int] = []
252+
content: list[int] = []
253+
quotes: list[int] = []
254+
orders: list[int] = []
255+
emails: list[int] = []
256+
meetings: list[int] = []
257+
notes: list[int] = []
258+
tasks: list[int] = []
259+
content: list[int] = []
260+
orders: list[int] = []
261+
carts: list[int] = []
262+
partner_clients: list[int] = []
263+
marketing_event: list[int] = []
228264

229265

230266
class Ticket(BaseCRMObject):
@@ -241,7 +277,14 @@ class Product(BaseCRMObject):
241277

242278

243279
class LineItem(BaseCRMObject):
244-
ASSOCIATED_ENTITIES = [Names.commerce_payments, Names.products, Names.deals, Names.invoices, Names.quotes, Names.subscriptions]
280+
ASSOCIATED_ENTITIES = [
281+
Names.commerce_payments,
282+
Names.products,
283+
Names.deals,
284+
Names.invoices,
285+
Names.quotes,
286+
Names.subscriptions,
287+
]
245288

246289
commerce_payments: list[int] = []
247290
products: list[int] = []
@@ -253,7 +296,6 @@ class LineItem(BaseCRMObject):
253296

254297
# An Association, as returned by the v4 associations API.
255298
class Association(BaseModel, extra="forbid"):
256-
257299
class Type(BaseModel, extra="forbid"):
258300
category: Literal["HUBSPOT_DEFINED", "USER_DEFINED"]
259301
# Type IDs are defined here: https://developers.hubspot.com/docs/api/crm/associations
@@ -296,7 +338,6 @@ class Paging(BaseModel, extra="forbid"):
296338
# Common shape of a v3 API paged listing for a GET request to the objects endpoint for a particular
297339
# object.
298340
class PageResult(BaseModel, Generic[Item], extra="forbid"):
299-
300341
class Cursor(BaseModel, extra="forbid"):
301342
after: str
302343
link: str
@@ -311,7 +352,6 @@ class Paging(BaseModel, extra="forbid"):
311352
# Common shape of a v3 search API listing, which is the same as PageResult but includes a field for
312353
# the total number of records returned, and doesn't have a "link" in the paging.next object.
313354
class SearchPageResult(BaseModel, Generic[Item], extra="forbid"):
314-
315355
class Cursor(BaseModel, extra="forbid"):
316356
after: str
317357

@@ -325,7 +365,6 @@ class Paging(BaseModel, extra="forbid"):
325365

326366
# Common shape of a v3 API batch read.
327367
class BatchResult(BaseModel, Generic[Item], extra="forbid"):
328-
329368
class Error(BaseModel, extra="forbid"):
330369
status: Literal["error"]
331370
category: Literal["OBJECT_NOT_FOUND"]
@@ -349,7 +388,6 @@ class Error(BaseModel, extra="forbid"):
349388

350389

351390
class OldRecentCompanies(BaseModel):
352-
353391
class Item(BaseModel):
354392
class Properties(BaseModel):
355393
class Timestamp(BaseModel):
@@ -367,7 +405,6 @@ class Timestamp(BaseModel):
367405

368406

369407
class OldRecentContacts(BaseModel):
370-
371408
class Item(BaseModel):
372409
class Properties(BaseModel):
373410
class Timestamp(BaseModel):
@@ -379,13 +416,12 @@ class Timestamp(BaseModel):
379416
properties: Properties
380417

381418
contacts: list[Item]
382-
has_more: bool = Field(alias="has-more") # type: ignore
383-
time_offset: int = Field(alias="time-offset") # type: ignore
384-
vid_offset: int = Field(alias="vid-offset") # type: ignore
419+
has_more: bool = Field(alias="has-more") # type: ignore
420+
time_offset: int = Field(alias="time-offset") # type: ignore
421+
vid_offset: int = Field(alias="vid-offset") # type: ignore
385422

386423

387424
class OldRecentDeals(BaseModel):
388-
389425
class Item(BaseModel):
390426
class Properties(BaseModel):
391427
class Timestamp(BaseModel):
@@ -403,7 +439,6 @@ class Timestamp(BaseModel):
403439

404440

405441
class OldRecentEngagements(BaseModel):
406-
407442
class Item(BaseModel):
408443
class Engagement(BaseModel):
409444
id: int
@@ -425,6 +460,7 @@ class OldRecentTicket(BaseModel):
425460
# EmailEvent and EmailEventsResponse represent an email event and the shape of the email events API
426461
# response, respectively.
427462

463+
428464
class EmailEvent(BaseDocument, extra="allow"):
429465
id: str
430466
created: AwareDatetime
@@ -442,12 +478,12 @@ class EmailEvent(BaseDocument, extra="allow"):
442478
"STATUSCHANGE",
443479
"SPAMREPORT",
444480
"SUPPRESSED",
445-
"SUPPRESSION", # "SUPPRESSION" is documented in HubSpot's docs, but "SUPPRESSED" isn't. We've seen "SUPPRESSED" events, so "SUPPRESSION" events might not actually occur.
446-
"UNBOUNCE", # This is not actually a type reported by HubSpot, but the absence of the "type" field means its an UNBOUNCE type.
481+
"SUPPRESSION", # "SUPPRESSION" is documented in HubSpot's docs, but "SUPPRESSED" isn't. We've seen "SUPPRESSED" events, so "SUPPRESSION" events might not actually occur.
482+
"UNBOUNCE", # This is not actually a type reported by HubSpot, but the absence of the "type" field means its an UNBOUNCE type.
447483
] = Field(
448484
default="UNBOUNCE",
449-
# Don't schematize the default value.
450-
json_schema_extra=lambda x: x.pop('default'), # type: ignore
485+
# Don't schematize the default value.
486+
json_schema_extra=lambda x: x.pop("default"), # type: ignore
451487
)
452488

453489

@@ -478,7 +514,6 @@ class CustomObjectSchema(BaseDocument, extra="allow"):
478514
# This is the shape of a response from the V3 search API for custom objects. As above, we are
479515
# modeling only the minimum needed to get the IDs and modification time.
480516
class CustomObjectSearchResult(BaseModel):
481-
482517
class Properties(BaseModel):
483518
hs_lastmodifieddate: AwareDatetime
484519

@@ -488,10 +523,14 @@ def set_lastmodifieddate(cls, values):
488523

489524
# The "Contacts" object uses `lastmodifieddate`, while everything
490525
# else uses `hs_lastmodifieddate`.
491-
if 'lastmodifieddate' in values:
492-
values['hs_lastmodifieddate'] = datetime.fromisoformat(values.pop('lastmodifieddate'))
493-
elif 'hs_lastmodifieddate' in values:
494-
values['hs_lastmodifieddate'] = datetime.fromisoformat(values.pop('hs_lastmodifieddate'))
526+
if "lastmodifieddate" in values:
527+
values["hs_lastmodifieddate"] = datetime.fromisoformat(
528+
values.pop("lastmodifieddate")
529+
)
530+
elif "hs_lastmodifieddate" in values:
531+
values["hs_lastmodifieddate"] = datetime.fromisoformat(
532+
values.pop("hs_lastmodifieddate")
533+
)
495534
return values
496535

497536
id: int

source-hubspot-native/tests/snapshots/snapshots__capture__stdout.json

+3
Original file line numberDiff line numberDiff line change
@@ -4993,6 +4993,9 @@
49934993
"uuid": "DocUUIDPlaceholder-329Bb50aa48EAa9ef"
49944994
},
49954995
"archived": false,
4996+
"companies": [
4997+
19015593502
4998+
],
49964999
"createdAt": "2024-02-15T17:25:55.752000Z",
49975000
"id": 47494434080,
49985001
"properties": {

0 commit comments

Comments
 (0)