curl --location '<https://api.notion.com/v1/blocks/2bc14096c91f80a0a1aed5800eee69ce/children>' \\
--header 'Authorization: Bearer <NOTION_TOKEN>' \\
--header 'Notion-Version: 2022-06-28'
Use this curl to fetch notion page content Note: Few blocks content is not visible for that you need to hit another curl with the id you get in above call.
curl --location --request GET \\
"<https://graph.microsoft.com/beta/sites/${SITE_ID}/pages/${PAGE_ID}/microsoft.graph.sitePage/webparts>" \\
--header "Authorization: Bearer ${ACCESS_TOKEN}" \\
--header "Content-Type: application/json"
This is the api to get sharepoint page content
Pseudocode For mapping
function notionBlocksToSharePointWebParts(notionBlocks) {
const webParts = [];
let htmlBuffer = "";
const flushTextWebPart = () => {
if (!htmlBuffer.trim()) return;
webParts.push({
"@odata.type": "#microsoft.graph.textWebPart",
"innerHtml": htmlBuffer
});
htmlBuffer = "";
};
for (const block of notionBlocks) {
switch (block.type) {
// TEXT GROUP – stay in htmlBuffer
case "paragraph":
case "heading_1":
case "heading_2":
case "heading_3":
case "bulleted_list_item":
case "numbered_list_item":
case "quote":
case "to_do":
case "toggle":
case "callout":
case "divider": // if using <hr> approach
case "table":
htmlBuffer += renderBlockToHtml(block); // your function to turn Notion block -> HTML
break;
// SPECIAL BLOCKS – flush text, then emit dedicated webpart
case "image":
flushTextWebPart();
webParts.push(buildImageWebPart(block)); // upload + build JSON like your example
break;
case "video":
flushTextWebPart();
webParts.push(buildVideoWebPart(block)); // File and Media or YouTube depending on type
break;
case "audio":
flushTextWebPart();
webParts.push(buildAudioWebPart(block)); // File and Media or Embed
break;
case "file":
flushTextWebPart();
webParts.push(buildFileWebPart(block));
break;
case "code":
flushTextWebPart();
webParts.push(buildCodeSnippetWebPart(block));
break;
case "bookmark":
flushTextWebPart();
webParts.push(addBookmarkToQuickLinks(block)); // or Button
break;
case "child_page":
flushTextWebPart();
webParts.push(addChildPageQuickLink(block)); // assumes you've created the child page already
break;
case "child_database":
flushTextWebPart();
webParts.push(addDatabaseListWebPart(block)); // assumes you've created the list
break;
default:
// unsupported: log and maybe add a comment in HTML
htmlBuffer += `<!-- unsupported block: ${block.type} -->`;
break;
}
}
flushTextWebPart();
return webParts;
}
Helper Function
/**
* Turn a single Notion block into HTML suitable for a SharePoint text web part.
* Assumes block structure from Notion API v2022-06-28.
*/
function renderBlockToHtml(block) {
if (!block || !block.type) return "";
switch (block.type) {
case "paragraph":
return renderParagraph(block.paragraph);
case "heading_1":
return renderHeading(block.heading_1, 1);
case "heading_2":
return renderHeading(block.heading_2, 2);
case "heading_3":
return renderHeading(block.heading_3, 3);
case "bulleted_list_item":
return renderBulletedListItem(block.bulleted_list_item);
case "numbered_list_item":
return renderNumberedListItem(block.numbered_list_item);
case "quote":
return renderQuote(block.quote);
case "to_do":
return renderTodo(block.to_do);
case "toggle":
return renderToggle(block.toggle);
case "callout":
return renderCallout(block.callout);
case "divider":
return `<hr class="notion-divider" />\\n`;
case "table":
// Assumes you've already loaded table_row children into block.children
return renderTable(block);
default:
// For any text-ish unknown types, just dump a comment
return `<!-- unsupported block type in renderBlockToHtml: ${escapeHtml(
block.type
)} -->\\n`;
}
}
/* ---------- TEXT HELPERS ---------- */
function renderParagraph(paragraph) {
const text = renderRichText(paragraph?.rich_text || []);
// preserve “blank lines” for spacing
const inner = text.trim() === "" ? " " : text;
return `<p class="noSpacingAbove spacingBelow" data-text-type="withSpacing">${inner}</p>\\n`;
}
function renderHeading(heading, level) {
const text = renderRichText(heading?.rich_text || []);
const tag = `h${Math.min(Math.max(level, 1), 6)}`;
return `<${tag} class="headingSpacingAbove headingSpacingBelow lineHeight1_4">${text}</${tag}>\\n`;
}
function renderBulletedListItem(item) {
const text = renderRichText(item?.rich_text || []);
// NOTE: This wraps each item in its own <ul>. If you want grouped lists,
// you’d handle grouping in notionBlocksToSharePointWebParts instead.
return `<ul class="customListStyle" style="list-style-type:disc;"><li>${text}</li></ul>\\n`;
}
function renderNumberedListItem(item) {
const text = renderRichText(item?.rich_text || []);
return `<ol class="customListStyle"><li>${text}</li></ol>\\n`;
}
function renderQuote(quote) {
const text = renderRichText(quote?.rich_text || []);
return `<blockquote class="notion-quote">${text}</blockquote>\\n`;
}
function renderTodo(todo) {
const text = renderRichText(todo?.rich_text || []);
const checked = todo?.checked;
const checkbox = checked ? "☑" : "☐";
return `<p class="notion-todo noSpacingAbove spacingBelow">${checkbox} ${text}</p>\\n`;
}
function renderToggle(toggle) {
const text = renderRichText(toggle?.rich_text || []);
// Flattened – if you want to render children, do it in your recursive walker
return `<p class="notion-toggle noSpacingAbove spacingBelow"><strong>${text}</strong></p>\\n`;
}
function renderCallout(callout) {
const text = renderRichText(callout?.rich_text || []);
const emoji =
callout?.icon?.type === "emoji" ? escapeHtml(callout.icon.emoji) : "💡";
const colorClass =
callout?.color && callout.color !== "default"
? ` notion-color-${callout.color}`
: "";
return `<div class="notion-callout${colorClass}"><span class="notion-callout-icon">${emoji}</span><span class="notion-callout-text">${text}</span></div>\\n`;
}
/* ---------- TABLE ---------- */
function renderTable(tableBlock) {
const hasHeader = !!tableBlock.table?.has_column_header;
const rows = Array.isArray(tableBlock.children)
? tableBlock.children
: []; // assumes you attach table_row children beforehand
let thead = "";
let tbody = "";
rows.forEach((row, index) => {
if (row.type !== "table_row") return;
const cells = row.table_row?.cells || [];
const cellTag = hasHeader && index === 0 ? "th" : "td";
const rowHtml =
"<tr>" +
cells
.map((cellRichTextArray) => {
const cellText = renderRichText(cellRichTextArray || []);
return `<${cellTag}>${cellText || " "}</${cellTag}>`;
})
.join("") +
"</tr>";
if (hasHeader && index === 0) {
thead += rowHtml;
} else {
tbody += rowHtml;
}
});
if (!thead && !tbody) {
return "<!-- empty table -->\\n";
}
return `
<figure class="table canvasRteResponsiveTable tableLeftAlign" title="Table">
<table class="customCells rteTableBackgroundTransparent rteTableUnboldTableHeaderCell">
${thead ? `<thead>${thead}</thead>` : ""}
<tbody>${tbody}</tbody>
</table>
</figure>\\n`;
}
/* ---------- RICH TEXT ---------- */
function renderRichText(richTextArray) {
if (!Array.isArray(richTextArray)) return "";
return richTextArray
.map((rt) => {
const plain =
(rt.plain_text ||
(rt.text && rt.text.content) ||
"").replace(/\\n/g, "<br />");
let html = escapeHtml(plain);
const ann = rt.annotations || {};
if (ann.code) html = `<code>${html}</code>`;
if (ann.bold) html = `<strong>${html}</strong>`;
if (ann.italic) html = `<em>${html}</em>`;
if (ann.underline) html = `<u>${html}</u>`;
if (ann.strikethrough) html = `<s>${html}</s>`;
const href =
rt.href || (rt.text && rt.text.link && rt.text.link.url) || null;
if (href) {
html = `<a href="${escapeHtml(href)}">${html}</a>`;
}
const color = ann.color;
if (color && color !== "default") {
// Map Notion colors to CSS classes, you can style them in your CSS:
// .notion-color-red { color: #e03e3e; } etc.
html = `<span class="notion-color-${color}">${html}</span>`;
}
return html;
})
.join("");
}
/* ---------- UTILS ---------- */
function escapeHtml(str) {
return String(str)
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
}
Other Helper function
/* =============================================================================
BUILDER FUNCTIONS FOR SHAREPOINT WEBPART JSON
============================================================================= */
function buildImageWebPart(block, options = {}) {
const img = block.image;
const url =
img?.file?.url ||
img?.external?.url ||
"";
const fileName = url.split("/").pop().split("?")[0];
return {
"@odata.type": "#microsoft.graph.standardWebPart",
"webPartType": "d1d91016-032f-456d-98a4-721247c305e8",
"data": {
"audiences": [],
"dataVersion": "1.2",
"title": "Image",
"properties": {
"imageSourceType": 2,
"captionText": "",
"altText": "",
"overlayText": "",
"fileName": fileName
},
"serverProcessedContent": {
"imageSources": [
{
"key": "imageSource",
"value": url
}
],
"links": [],
"htmlStrings": [],
"searchablePlainTexts": []
}
}
};
}
function buildVideoWebPart(block, options = {}) {
const video = block.video;
// Case 1: YouTube or external URL = YouTube embed webpart
if (video?.external?.url?.includes("youtube.com") || video?.external?.url?.includes("youtu.be")) {
return {
"@odata.type": "#microsoft.graph.standardWebPart",
"webPartType": "544dd15b-cf3c-441b-96da-004d5a8cea1d",
"data": {
"dataVersion": "1.2",
"title": "YouTube",
"properties": {
"embedCode": video.external.url,
"thumbnailUrl": "",
"shouldScaleWidth": true
},
"serverProcessedContent": {
"htmlStrings": [],
"imageSources": [],
"links": []
}
}
};
}
// Case 2: file video → File and Media
const url = video?.file?.url || "";
const fileName = url.split("/").pop().split("?")[0];
return {
"@odata.type": "#microsoft.graph.standardWebPart",
"webPartType": "b7dd04e1-19ce-4b24-9132-b60a1c2b910d",
"data": {
"dataVersion": "1.4",
"title": "Video",
"properties": {
"file": url,
"fileName": fileName
},
"serverProcessedContent": {
"links": [
{ key: "serverRelativeUrl", value: url }
],
"searchablePlainTexts": [],
"imageSources": []
}
}
};
}
function buildAudioWebPart(block, options = {}) {
const audio = block.audio;
const url = audio?.external?.url || "";
return {
"@odata.type": "#microsoft.graph.standardWebPart",
"webPartType": "490d7c76-1824-45b2-9de3-676421c997fa",
"data": {
"dataVersion": "1.2",
"title": "Audio",
"properties": {
"embedCode": url,
"shouldScaleWidth": true
},
"serverProcessedContent": {
"links": [],
"imageSources": [],
"htmlStrings": []
}
}
};
}
function buildFileWebPart(block, options = {}) {
const file = block.file;
const url =
file?.external?.url ||
file?.file?.url ||
"";
const fileName = url.split("/").pop().split("?")[0];
return {
"@odata.type": "#microsoft.graph.standardWebPart",
"webPartType": "b7dd04e1-19ce-4b24-9132-b60a1c2b910d",
"data": {
"title": "File",
"properties": { "file": url, "fileName": fileName },
"serverProcessedContent": {
"links": [{ key: "serverRelativeUrl", value: url }],
"htmlStrings": [],
"imageSources": [],
"searchablePlainTexts": []
}
}
};
}
function buildCodeSnippetWebPart(block) {
const codeText = (block.code?.rich_text || [])
.map((rt) => rt.plain_text)
.join("");
return {
"@odata.type": "#microsoft.graph.standardWebPart",
"webPartType": "7b317bca-c919-4982-af2f-8399173e5a1e",
"data": {
"title": "Code Snippet",
"properties": {
"language": block.code?.language || "javascript",
"lineNumbers": true,
"lineWrapping": true,
"theme": "Monokai"
},
"serverProcessedContent": {
"htmlStrings": [],
"searchablePlainTexts": [
{ key: "code", value: codeText }
],
"links": [],
"imageSources": []
}
}
};
}
function addBookmarkToQuickLinks(block) {
const url = block.bookmark?.url || "";
return {
"@odata.type": "#microsoft.graph.standardWebPart",
"webPartType": "c70391ea-0b10-4ee9-b2b4-006d3fcad0cd",
"data": {
"title": "Quick Link",
"properties": {
"layoutId": "CompactCard",
"items": [
{
id: 1,
name: "Bookmark",
thumbnailType: 3,
sourceItem: {
url
}
}
]
},
"serverProcessedContent": {
"links": [
{ key: "items[0].sourceItem.url", value: url }
],
"htmlStrings": [],
"imageSources": []
}
}
};
}
function addChildPageQuickLink(block, options = {}) {
const title = block.child_page?.title || "Child Page";
const url = options.childPageResolver
? options.childPageResolver(block.id)
: "#";
return {
"@odata.type": "#microsoft.graph.standardWebPart",
"webPartType": "c70391ea-0b10-4ee9-b2b4-006d3fcad0cd",
"data": {
"title": "Subpage Link",
"properties": {
"layoutId": "CompactCard",
"items": [
{
id: 1,
name: title,
thumbnailType: 3,
sourceItem: {
url
}
}
]
},
"serverProcessedContent": {
"links": [
{ key: "items[0].sourceItem.url", value: url }
]
}
}
};
}
function addDatabaseListWebPart(block, options = {}) {
const listId =
options.databaseListResolver?.(block.id) ||
"00000000-0000-0000-0000-000000000000";
return {
"@odata.type": "#microsoft.graph.standardWebPart",
"webPartType": "f92bf067-bc19-489e-a556-7fe95f508720",
"data": {
"title": block.child_database?.title || "Database",
"properties": {
"selectedListId": listId,
"isDocumentLibrary": false
},
"serverProcessedContent": {
"htmlStrings": [],
"links": [],
"imageSources": []
}
}
};
}
Complete mapping
/****************************************************************************************
* COMPLETE NOTION → SHAREPOINT TEXT + WEBPART MAPPER
* -------------------------------------------------
* This file contains:
* - renderBlockToHtml + helpers
* - notionBlocksToSharePointWebParts
* - All builder functions (image, video, file, audio, code, bookmark, database...)
*
* All functions are self-contained and runnable.
****************************************************************************************/
/* =============================================================================
RENDER NOTION BLOCK → HTML (used inside SharePoint text web parts)
============================================================================= */
function renderBlockToHtml(block) {
if (!block || !block.type) return "";
switch (block.type) {
case "paragraph":
return renderParagraph(block.paragraph);
case "heading_1":
return renderHeading(block.heading_1, 1);
case "heading_2":
return renderHeading(block.heading_2, 2);
case "heading_3":
return renderHeading(block.heading_3, 3);
case "bulleted_list_item":
return renderBulletedListItem(block.bulleted_list_item);
case "numbered_list_item":
return renderNumberedListItem(block.numbered_list_item);
case "quote":
return renderQuote(block.quote);
case "to_do":
return renderTodo(block.to_do);
case "toggle":
return renderToggle(block.toggle);
case "callout":
return renderCallout(block.callout);
case "divider":
return `<hr class="notion-divider" />\\n`;
case "table":
return renderTable(block);
default:
return `<!-- unsupported block type in renderBlockToHtml: ${escapeHtml(
block.type
)} -->\\n`;
}
}
/* ======== TEXT HELPERS ========= */
function renderParagraph(paragraph) {
const text = renderRichText(paragraph?.rich_text || []);
const inner = text.trim() === "" ? " " : text;
return `<p class="noSpacingAbove spacingBelow" data-text-type="withSpacing">${inner}</p>\\n`;
}
function renderHeading(heading, level) {
const text = renderRichText(heading?.rich_text || []);
const tag = `h${Math.min(Math.max(level, 1), 6)}`;
return `<${tag} class="headingSpacingAbove headingSpacingBelow lineHeight1_4">${text}</${tag}>\\n`;
}
function renderBulletedListItem(item) {
const text = renderRichText(item?.rich_text || []);
return `<ul class="customListStyle" style="list-style-type:disc;"><li>${text}</li></ul>\\n`;
}
function renderNumberedListItem(item) {
const text = renderRichText(item?.rich_text || []);
return `<ol class="customListStyle"><li>${text}</li></ol>\\n`;
}
function renderQuote(quote) {
const text = renderRichText(quote?.rich_text || []);
return `<blockquote class="notion-quote">${text}</blockquote>\\n`;
}
function renderTodo(todo) {
const text = renderRichText(todo?.rich_text || []);
const checked = todo?.checked;
const checkbox = checked ? "☑" : "☐";
return `<p class="notion-todo noSpacingAbove spacingBelow">${checkbox} ${text}</p>\\n`;
}
function renderToggle(toggle) {
const text = renderRichText(toggle?.rich_text || []);
return `<p class="notion-toggle noSpacingAbove spacingBelow"><strong>${text}</strong></p>\\n`;
}
function renderCallout(callout) {
const text = renderRichText(callout?.rich_text || []);
const emoji =
callout?.icon?.type === "emoji" ? escapeHtml(callout.icon.emoji) : "💡";
const colorClass =
callout?.color && callout.color !== "default"
? ` notion-color-${callout.color}`
: "";
return `<div class="notion-callout${colorClass}"><span class="notion-callout-icon">${emoji}</span><span class="notion-callout-text">${text}</span></div>\\n`;
}
/* ======== TABLE ======== */
function renderTable(tableBlock) {
const hasHeader = !!tableBlock.table?.has_column_header;
const rows = Array.isArray(tableBlock.children)
? tableBlock.children
: [];
let thead = "";
let tbody = "";
rows.forEach((row, index) => {
if (row.type !== "table_row") return;
const cells = row.table_row?.cells || [];
const cellTag = hasHeader && index === 0 ? "th" : "td";
const rowHtml =
"<tr>" +
cells
.map((cellRichTextArray) => {
const cellText = renderRichText(cellRichTextArray || []);
return `<${cellTag}>${cellText || " "}</${cellTag}>`;
})
.join("") +
"</tr>";
if (hasHeader && index === 0) {
thead += rowHtml;
} else {
tbody += rowHtml;
}
});
return `
<figure class="table canvasRteResponsiveTable tableLeftAlign" title="Table">
<table class="customCells rteTableBackgroundTransparent rteTableUnboldTableHeaderCell">
${thead ? `<thead>${thead}</thead>` : ""}
<tbody>${tbody}</tbody>
</table>
</figure>\\n`;
}
/* ======== RICH TEXT ======== */
function renderRichText(richTextArray) {
if (!Array.isArray(richTextArray)) return "";
return richTextArray
.map((rt) => {
const plain =
(rt.plain_text ||
(rt.text && rt.text.content) ||
"").replace(/\\n/g, "<br />");
let html = escapeHtml(plain);
const ann = rt.annotations || {};
if (ann.code) html = `<code>${html}</code>`;
if (ann.bold) html = `<strong>${html}</strong>`;
if (ann.italic) html = `<em>${html}</em>`;
if (ann.underline) html = `<u>${html}</u>`;
if (ann.strikethrough) html = `<s>${html}</s>`;
const href =
rt.href || (rt.text && rt.text.link && rt.text.link.url) || null;
if (href) {
html = `<a href="${escapeHtml(href)}">${html}</a>`;
}
const color = ann.color;
if (color && color !== "default") {
html = `<span class="notion-color-${color}">${html}</span>`;
}
return html;
})
.join("");
}
/* ======== UTILS ======== */
function escapeHtml(str) {
return String(str)
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
}
/* =============================================================================
NOTION → SHAREPOINT WEBPART BUILDER
============================================================================= */
function notionBlocksToSharePointWebParts(notionBlocks, options = {}) {
const webParts = [];
let htmlBuffer = "";
const flushTextWebPart = () => {
if (!htmlBuffer.trim()) return;
webParts.push({
"@odata.type": "#microsoft.graph.textWebPart",
innerHtml: htmlBuffer
});
htmlBuffer = "";
};
for (const block of notionBlocks) {
switch (block.type) {
// TEXT = merge into HTML buffer
case "paragraph":
case "heading_1":
case "heading_2":
case "heading_3":
case "bulleted_list_item":
case "numbered_list_item":
case "quote":
case "to_do":
case "toggle":
case "callout":
case "divider":
case "table":
htmlBuffer += renderBlockToHtml(block);
break;
// SPECIAL BLOCKS = separate webparts
case "image":
flushTextWebPart();
webParts.push(buildImageWebPart(block, options));
break;
case "video":
flushTextWebPart();
webParts.push(buildVideoWebPart(block, options));
break;
case "audio":
flushTextWebPart();
webParts.push(buildAudioWebPart(block, options));
break;
case "file":
flushTextWebPart();
webParts.push(buildFileWebPart(block, options));
break;
case "code":
flushTextWebPart();
webParts.push(buildCodeSnippetWebPart(block));
break;
case "bookmark":
flushTextWebPart();
webParts.push(addBookmarkToQuickLinks(block));
break;
case "child_page":
flushTextWebPart();
webParts.push(addChildPageQuickLink(block, options));
break;
case "child_database":
flushTextWebPart();
webParts.push(addDatabaseListWebPart(block, options));
break;
default:
htmlBuffer += `<!-- unsupported block: ${block.type} -->\\n`;
break;
}
}
flushTextWebPart();
return webParts;
}
/* =============================================================================
BUILDER FUNCTIONS FOR SHAREPOINT WEBPART JSON
============================================================================= */
function buildImageWebPart(block, options = {}) {
const img = block.image;
const url =
img?.file?.url ||
img?.external?.url ||
"";
const fileName = url.split("/").pop().split("?")[0];
return {
"@odata.type": "#microsoft.graph.standardWebPart",
"webPartType": "d1d91016-032f-456d-98a4-721247c305e8",
"data": {
"audiences": [],
"dataVersion": "1.2",
"title": "Image",
"properties": {
"imageSourceType": 2,
"captionText": "",
"altText": "",
"overlayText": "",
"fileName": fileName
},
"serverProcessedContent": {
"imageSources": [
{
"key": "imageSource",
"value": url
}
],
"links": [],
"htmlStrings": [],
"searchablePlainTexts": []
}
}
};
}
function buildVideoWebPart(block, options = {}) {
const video = block.video;
// Case 1: YouTube or external URL = YouTube embed webpart
if (video?.external?.url?.includes("youtube.com") || video?.external?.url?.includes("youtu.be")) {
return {
"@odata.type": "#microsoft.graph.standardWebPart",
"webPartType": "544dd15b-cf3c-441b-96da-004d5a8cea1d",
"data": {
"dataVersion": "1.2",
"title": "YouTube",
"properties": {
"embedCode": video.external.url,
"thumbnailUrl": "",
"shouldScaleWidth": true
},
"serverProcessedContent": {
"htmlStrings": [],
"imageSources": [],
"links": []
}
}
};
}
// Case 2: file video → File and Media
const url = video?.file?.url || "";
const fileName = url.split("/").pop().split("?")[0];
return {
"@odata.type": "#microsoft.graph.standardWebPart",
"webPartType": "b7dd04e1-19ce-4b24-9132-b60a1c2b910d",
"data": {
"dataVersion": "1.4",
"title": "Video",
"properties": {
"file": url,
"fileName": fileName
},
"serverProcessedContent": {
"links": [
{ key: "serverRelativeUrl", value: url }
],
"searchablePlainTexts": [],
"imageSources": []
}
}
};
}
function buildAudioWebPart(block, options = {}) {
const audio = block.audio;
const url = audio?.external?.url || "";
return {
"@odata.type": "#microsoft.graph.standardWebPart",
"webPartType": "490d7c76-1824-45b2-9de3-676421c997fa",
"data": {
"dataVersion": "1.2",
"title": "Audio",
"properties": {
"embedCode": url,
"shouldScaleWidth": true
},
"serverProcessedContent": {
"links": [],
"imageSources": [],
"htmlStrings": []
}
}
};
}
function buildFileWebPart(block, options = {}) {
const file = block.file;
const url =
file?.external?.url ||
file?.file?.url ||
"";
const fileName = url.split("/").pop().split("?")[0];
return {
"@odata.type": "#microsoft.graph.standardWebPart",
"webPartType": "b7dd04e1-19ce-4b24-9132-b60a1c2b910d",
"data": {
"title": "File",
"properties": { "file": url, "fileName": fileName },
"serverProcessedContent": {
"links": [{ key: "serverRelativeUrl", value: url }],
"htmlStrings": [],
"imageSources": [],
"searchablePlainTexts": []
}
}
};
}
function buildCodeSnippetWebPart(block) {
const codeText = (block.code?.rich_text || [])
.map((rt) => rt.plain_text)
.join("");
return {
"@odata.type": "#microsoft.graph.standardWebPart",
"webPartType": "7b317bca-c919-4982-af2f-8399173e5a1e",
"data": {
"title": "Code Snippet",
"properties": {
"language": block.code?.language || "javascript",
"lineNumbers": true,
"lineWrapping": true,
"theme": "Monokai"
},
"serverProcessedContent": {
"htmlStrings": [],
"searchablePlainTexts": [
{ key: "code", value: codeText }
],
"links": [],
"imageSources": []
}
}
};
}
function addBookmarkToQuickLinks(block) {
const url = block.bookmark?.url || "";
return {
"@odata.type": "#microsoft.graph.standardWebPart",
"webPartType": "c70391ea-0b10-4ee9-b2b4-006d3fcad0cd",
"data": {
"title": "Quick Link",
"properties": {
"layoutId": "CompactCard",
"items": [
{
id: 1,
name: "Bookmark",
thumbnailType: 3,
sourceItem: {
url
}
}
]
},
"serverProcessedContent": {
"links": [
{ key: "items[0].sourceItem.url", value: url }
],
"htmlStrings": [],
"imageSources": []
}
}
};
}
function addChildPageQuickLink(block, options = {}) {
const title = block.child_page?.title || "Child Page";
const url = options.childPageResolver
? options.childPageResolver(block.id)
: "#";
return {
"@odata.type": "#microsoft.graph.standardWebPart",
"webPartType": "c70391ea-0b10-4ee9-b2b4-006d3fcad0cd",
"data": {
"title": "Subpage Link",
"properties": {
"layoutId": "CompactCard",
"items": [
{
id: 1,
name: title,
thumbnailType: 3,
sourceItem: {
url
}
}
]
},
"serverProcessedContent": {
"links": [
{ key: "items[0].sourceItem.url", value: url }
]
}
}
};
}
function addDatabaseListWebPart(block, options = {}) {
const listId =
options.databaseListResolver?.(block.id) ||
"00000000-0000-0000-0000-000000000000";
return {
"@odata.type": "#microsoft.graph.standardWebPart",
"webPartType": "f92bf067-bc19-489e-a556-7fe95f508720",
"data": {
"title": block.child_database?.title || "Database",
"properties": {
"selectedListId": listId,
"isDocumentLibrary": false
},
"serverProcessedContent": {
"htmlStrings": [],
"links": [],
"imageSources": []
}
}
};
}