Last active
October 23, 2024 22:46
-
-
Save elijahr/bab853c48077bdfb4c9da9e181b49684 to your computer and use it in GitHub Desktop.
Parse the Expandable fields for all Stripe API resources.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function upperCamelToTitlePhrase(upperCamel) { | |
console.debug(`Converting upper camel case to phrase: ${upperCamel}`); | |
return upperCamel.replace(/([a-z])([A-Z])/g, '$1 $2').replace(/^[a-z]/, firstChar => firstChar.toUpperCase()); | |
} | |
function pluralize(str) { | |
console.debug(`Pluralizing string: ${str}`); | |
return str.endsWith('s') ? str : str + 's'; | |
} | |
function jsonWithTrailingCommas(obj) { | |
console.debug('Converting object to JSON with trailing commas:', obj); | |
const jsonString = JSON.stringify(obj, null, 4); | |
const lines = jsonString.split('\n'); | |
for (let i = 0; i < lines.length - 1; i++) { | |
if (lines[i].trim().endsWith('}') || lines[i].trim().endsWith('"') || lines[i].trim().endsWith(']')) { | |
lines[i] += ','; | |
} | |
} | |
return lines.join('\n'); | |
} | |
function useProgressBar({totalCount}) { | |
console.debug('Creating or replacing progress bar'); | |
const existingProgressBarContainer = document.getElementById('progress-bar-container'); | |
if (existingProgressBarContainer) { | |
existingProgressBarContainer.remove(); | |
} | |
// Create and style the progress bar | |
const progressBarContainer = document.createElement('div'); | |
progressBarContainer.id = 'progress-bar-container'; | |
progressBarContainer.style.position = 'fixed'; | |
progressBarContainer.style.bottom = '36px'; | |
progressBarContainer.style.right = '100px'; | |
progressBarContainer.style.width = '300px'; | |
progressBarContainer.style.height = '36px'; | |
progressBarContainer.style.backgroundColor = '#f3f3f3'; | |
progressBarContainer.style.border = '1px solid #ccc'; | |
progressBarContainer.style.borderRadius = '5px'; | |
progressBarContainer.style.zIndex = '1000'; | |
// Create and style the label for the progress bar | |
const progressBarLabel = document.createElement('div'); | |
progressBarLabel.textContent = 'Loading Documentation... 0%'; | |
progressBarLabel.style.position = 'absolute'; | |
progressBarLabel.style.top = '-25px'; | |
progressBarLabel.style.left = '0'; | |
progressBarLabel.style.width = '100%'; | |
progressBarLabel.style.textAlign = 'center'; | |
progressBarLabel.style.fontSize = '14px'; | |
progressBarLabel.style.color = '#fff'; | |
progressBarLabel.style.fontWeight = 'bold'; | |
progressBarContainer.appendChild(progressBarLabel); | |
const progressBar = document.createElement('div'); | |
progressBar.style.height = '100%'; | |
progressBar.style.width = '0%'; | |
progressBar.style.backgroundColor = '#4caf50'; | |
progressBar.style.transition = 'width 0.2s'; | |
progressBarContainer.appendChild(progressBar); | |
document.body.appendChild(progressBarContainer); | |
let count = 0; | |
let percentage = 0; | |
return () => { | |
count++; | |
percentage = 100 * (count / totalCount); | |
progressBar.style.width = `${percentage}%`; | |
progressBarLabel.textContent = `Loading Documentation... ${Math.round(percentage)}%`; | |
}; | |
} | |
function findSectionForResourceName(resourceName) { | |
const headerContent = upperCamelToTitlePhrase(resourceName); | |
const mainLink = Array.from(document.querySelectorAll('main [data-testid^="api-section-group"] a[data-testid^="api-section-"]')).filter(a => a.innerText === headerContent || a.innerText === pluralize(headerContent))[0]; | |
if (resourceName === 'Subscription') debugger | |
if (!mainLink) { | |
console.error(`Could not find link for ${headerContent}`); | |
throw new Error(`Could not find link for ${headerContent}`); | |
} | |
const section = mainLink.closest('[data-testid^="api-section-group"]'); | |
if (!section) { | |
console.error(`Could not find section for ${headerContent}`); | |
throw new Error(`Could not find section for ${headerContent}`); | |
} | |
return section; | |
} | |
async function loadSection(section) { | |
while (section.querySelector('ul.method-list') === null) { | |
console.log(`Loading section ${section.id}`); | |
const loadButton = section.querySelector('[data-testid="api-section-group-load-button"]'); | |
if (!loadButton) { | |
console.error(`Could not find load button for ${section.id}`); | |
throw new Error(`Could not find load button for ${section.id}`); | |
} | |
loadButton.click(); | |
await new Promise(resolve => setTimeout(resolve, 1000)); // Adjust delay as needed | |
} | |
} | |
async function getExpandableFields({tickProgress, resourceNames}) { | |
const result = {}; | |
for (const resourceName of resourceNames) { | |
result[resourceName] = []; | |
const section = findSectionForResourceName(resourceName); | |
await loadSection(section); | |
const methodList = section.querySelector('ul.method-list'); | |
methodList.querySelectorAll('li.ApiReference-Element').forEach(li => { | |
li.querySelectorAll('span').forEach(span => { | |
if (span.textContent === 'Expandable' && span.children.length === 0) { | |
const labelElement = span.parentElement.querySelector('span:first-child'); | |
if (labelElement) { | |
if (labelElement.textContent === 'line_items') { | |
debugger | |
} | |
result[resourceName].push(labelElement.textContent); | |
} | |
} | |
}); | |
}); | |
tickProgress(); | |
} | |
return result; | |
} | |
function findDefaultExpandedPaths(obj, currentPath = '') { | |
const paths = []; | |
for (const key in obj) { | |
if (obj.hasOwnProperty(key) && typeof obj[key] === 'object' && obj[key] !== null) { | |
const newPath = currentPath ? `${currentPath}.${key}` : key; | |
if (obj[key].hasOwnProperty('object')) { | |
if (obj[key].object === 'list') { | |
paths.push(...findDefaultExpandedPaths(obj[key].data, newPath + ".data")); | |
} else { | |
paths.push(newPath); | |
} | |
} | |
paths.push(...findDefaultExpandedPaths(obj[key], newPath)); | |
} | |
} | |
return paths; | |
} | |
async function getDefaultExpandedFields({tickProgress, resourceNames}) { | |
const result = {}; | |
for (const resourceName of resourceNames) { | |
result[resourceName] = []; | |
const section = findSectionForResourceName(resourceName); | |
await loadSection(section) | |
const code = section.querySelector('pre code'); | |
const exampleObject = JSON.parse(code.textContent); | |
result[resourceName] = findDefaultExpandedPaths(exampleObject); | |
tickProgress(); | |
} | |
return result; | |
} | |
async function setup({totalCount}) { | |
const tickProgress = useProgressBar({totalCount}); | |
// Scroll up and down to ensure all header contents are loaded | |
window.scrollTo(0, document.body.scrollHeight); | |
await new Promise(resolve => setTimeout(resolve, 2000)); | |
window.scrollTo(0, 0); | |
await new Promise(resolve => setTimeout(resolve, 2000)); | |
return tickProgress; | |
} | |
async function main() { | |
const resourceNames = [ | |
"Account", | |
"AccountLink", | |
"ApplicationFee", | |
"Balance", | |
"Charge", | |
"Coupon", | |
"Customer", | |
"Dispute", | |
"Invoice", | |
"PaymentIntent", | |
"Payout", | |
"PromotionCode", | |
"Refund", | |
"Subscription", | |
"Token", | |
"Transfer", | |
]; | |
const tickProgress = await setup({totalCount: resourceNames.length * 2}); | |
console.log("default_expanded_fields_by_stripe_resource = " + jsonWithTrailingCommas(await getDefaultExpandedFields({tickProgress, resourceNames}))); | |
console.log("expandable_fields_by_stripe_resource = " + jsonWithTrailingCommas(await getExpandableFields({tickProgress, resourceNames}))); | |
} | |
main(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment