From 5f5693099ab57b51d96c5640fcc6ce018b6bab3c Mon Sep 17 00:00:00 2001 From: djohalo2 Date: Fri, 21 Aug 2026 09:31:46 +0200 Subject: [PATCH 1/2] fix(hubspot): blog author name --- plugins/hubspot/src/api.ts | 9 +++++++++ plugins/hubspot/src/blog.ts | 31 +++++++++++++++++++++++-------- 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/plugins/hubspot/src/api.ts b/plugins/hubspot/src/api.ts index 0d49feda9..c8654fc72 100644 --- a/plugins/hubspot/src/api.ts +++ b/plugins/hubspot/src/api.ts @@ -1,3 +1,4 @@ +import type { BlogAuthor } from "@hubspot/api-client/lib/codegen/cms/blogs/authors/models/BlogAuthor" import { BlogPost } from "@hubspot/api-client/lib/codegen/cms/blogs/blog_posts/models/BlogPost" import { HubDbTableRowV3 } from "@hubspot/api-client/lib/codegen/cms/hubdb/models/HubDbTableRowV3" import { HubDbTableV3 } from "@hubspot/api-client/lib/codegen/cms/hubdb/models/HubDbTableV3" @@ -100,6 +101,7 @@ const API_URL = "https://api.hubapi.com" const queryKeys = { blogPosts: (limit: number, properties: string[]) => ["blog-posts", limit, properties] as const, + blogAuthor: (id: string) => ["blog-author", id] as const, publishedTables: (limit: number) => ["hubdb-tables", limit] as const, publishedTable: (tableId: string) => ["hubdb-table", tableId] as const, tableRows: (tableId: string, properties: string[], limit: number) => @@ -177,6 +179,13 @@ export const fetchAllBlogPosts = (limit: number, properties: string[]): Promise< ) } +export const fetchBlogAuthor = (authorId: string): Promise => { + return cachedFetch( + queryKeys.blogAuthor(authorId), + () => request({ path: `/cms/v3/blogs/authors/${authorId}` }) as Promise + ) +} + export const fetchPublishedTables = (limit: number): Promise> => { return cachedFetch( queryKeys.publishedTables(limit), diff --git a/plugins/hubspot/src/blog.ts b/plugins/hubspot/src/blog.ts index 4cc7fec46..5aaf1ee1b 100644 --- a/plugins/hubspot/src/blog.ts +++ b/plugins/hubspot/src/blog.ts @@ -7,7 +7,7 @@ import { type ManagedCollection, type ManagedCollectionFieldInput, } from "framer-plugin" -import { fetchAllBlogPosts } from "./api" +import { fetchAllBlogPosts, fetchBlogAuthor } from "./api" import { computeFieldSets, createFieldSetHash, @@ -22,6 +22,10 @@ import { assert, isDefined } from "./utils" export const PLUGIN_INCLUDED_FIELDS_HASH = "hubspotPluginBlogIncludedFieldsHash" +// Keep this ID stable for existing managed collections and their Framer bindings. +const AUTHOR_NAME_FIELD_ID = "authorName" +const BLOG_AUTHOR_ID_FIELD_ID = "blogAuthorId" + export interface SyncBlogMutation { fields: ManagedCollectionFieldInput[] includedFieldIds: string[] @@ -30,6 +34,7 @@ export interface SyncBlogMutation { export interface ProcessBlogParams { fields: ManagedCollectionFieldInput[] fieldsById: FieldsById + authorNamesById: Map unsyncedItemIds: Set } @@ -166,7 +171,7 @@ function getFieldDataEntryInput( } } -function processPost({ post, fieldsById, unsyncedItemIds, status }: ProcessPostParams) { +function processPost({ post, fieldsById, authorNamesById, unsyncedItemIds, status }: ProcessPostParams) { let slugValue: string | null = null const fieldData: FieldDataInput = {} @@ -184,7 +189,12 @@ function processPost({ post, fieldsById, unsyncedItemIds, status }: ProcessPostP // Not included in field mapping, skip if (!field) continue - const fieldDataEntryInput = getFieldDataEntryInput(field, propertyValue, post) + // HubSpot uses authorName for the user who updated the post, not its blog author. + // Retain that value when the associated Blog Author cannot be resolved. + const resolvedAuthorName = + propertyName === AUTHOR_NAME_FIELD_ID ? authorNamesById.get(post.blogAuthorId) : undefined + const value = resolvedAuthorName ?? propertyValue + const fieldDataEntryInput = getFieldDataEntryInput(field, value, post) if (fieldDataEntryInput) { fieldData[propertyName] = fieldDataEntryInput } else { @@ -232,15 +242,20 @@ export async function syncBlogs({ fields, includedFieldIds }: SyncBlogMutation) const fieldsById = new Map(fields.map(field => [field.id, field])) const unsyncedItemIds = new Set(await collection.getItemIds()) + const requestedProperties = new Set([...includedFieldIds, "id", "slug"]) + + if (fieldsById.has(AUTHOR_NAME_FIELD_ID)) requestedProperties.add(BLOG_AUTHOR_ID_FIELD_ID) + + const { results: posts } = await fetchAllBlogPosts(MAX_CMS_ITEMS, Array.from(requestedProperties)) + + const authorIds = Array.from(new Set(posts.map(post => post.blogAuthorId))) + const authors = authorIds.length ? await Promise.all(authorIds.map(fetchBlogAuthor)) : [] + const authorNamesById = new Map(authors.map(author => [author.id, author.displayName])) - // Always include the blog Id and slug - const { results: posts } = await fetchAllBlogPosts( - MAX_CMS_ITEMS, - Array.from(new Set([...includedFieldIds, "id", "slug"])) - ) const { collectionItems, status } = processBlog(posts, { fields, fieldsById, + authorNamesById, unsyncedItemIds, }) From e31b8e20fba93bc460c19ea948c2e535445be4d5 Mon Sep 17 00:00:00 2001 From: djohalo2 Date: Fri, 21 Aug 2026 12:19:58 +0200 Subject: [PATCH 2/2] chore: compute author names by id only if field exists --- plugins/hubspot/src/blog.ts | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/plugins/hubspot/src/blog.ts b/plugins/hubspot/src/blog.ts index 5aaf1ee1b..8fc2871fb 100644 --- a/plugins/hubspot/src/blog.ts +++ b/plugins/hubspot/src/blog.ts @@ -242,15 +242,22 @@ export async function syncBlogs({ fields, includedFieldIds }: SyncBlogMutation) const fieldsById = new Map(fields.map(field => [field.id, field])) const unsyncedItemIds = new Set(await collection.getItemIds()) + const hasAuthorNameField = fieldsById.has(AUTHOR_NAME_FIELD_ID) const requestedProperties = new Set([...includedFieldIds, "id", "slug"]) - if (fieldsById.has(AUTHOR_NAME_FIELD_ID)) requestedProperties.add(BLOG_AUTHOR_ID_FIELD_ID) + if (hasAuthorNameField) requestedProperties.add(BLOG_AUTHOR_ID_FIELD_ID) const { results: posts } = await fetchAllBlogPosts(MAX_CMS_ITEMS, Array.from(requestedProperties)) - const authorIds = Array.from(new Set(posts.map(post => post.blogAuthorId))) - const authors = authorIds.length ? await Promise.all(authorIds.map(fetchBlogAuthor)) : [] - const authorNamesById = new Map(authors.map(author => [author.id, author.displayName])) + const authorNamesById = new Map() + if (hasAuthorNameField) { + const authorIds = Array.from(new Set(posts.map(post => post.blogAuthorId))) + const authors = authorIds.length ? await Promise.all(authorIds.map(fetchBlogAuthor)) : [] + + for (const author of authors) { + authorNamesById.set(author.id, author.displayName) + } + } const { collectionItems, status } = processBlog(posts, { fields,