This working script automates the creation of hierarchical project maps in Fibery, dynamically organizing project data into a structured document. It generates a visual representation of project hierarchies in the Description field of an entity Page, and an additional automation attaches a PDF version.
Use Cases:
The script shared is more like a general purpose script, that I adapted in several ways in other scripts to accomodate our needs. But I think for everyone still useful for snapshot creation for:
- Regulatory compliance and auditing
- Historical record-keeping
- Decision-making support
- Sharing with various external stakeholders
The video shows 4 buttons, which are 4 slightly different tree displays
Compact, Clean Compact, Spacious, Clean Spacious.
// Configuration Module. Replace the Space/Database names with your ones.
const config = {
entities: {
1: { type: 'YO/Project', childField: 'Goals', indent: '' },
2: { type: 'YO/Goal', childField: 'Stones', indent: ' └─ ' },
3: { type: 'YO/Stone', childField: 'Tasks', indent: ' └─ ' },
4: { type: 'YO/Task', childField: 'Sub Tasks', indent: ' └─ ' },
5: { type: 'YO/Task', childField: 'Sub Tasks', indent: ' └─ ' },
6: { type: 'YO/Task', childField: 'Sub Tasks', indent: ' └─ ' }
},
pageType: 'YO/Page'
};
// Initialization of the Fibery API service
const fibery = context.getService('fibery');
// Function to fetch entity type IDs
async function fetchEntityTypeIDs(fibery, entityNames) {
const schema = await fibery.getSchema();
const ids = {};
entityNames.forEach(entityName => {
const entityType = schema['typeObjects'].find(obj => obj.name === entityName);
if (entityType) {
ids[entityName] = entityType.id;
} else {
console.error(`Entity type '${entityName}' not found in schema.`);
}
});
return ids;
}
// Function to initialize entity configurations with type IDs
async function initializeEntityConfigs(fibery, config) {
const entityNames = Object.values(config.entities).map(entity => entity.type);
const ids = await fetchEntityTypeIDs(fibery, entityNames);
Object.keys(config.entities).forEach(key => {
config.entities[key].typeId = ids[config.entities[key].type];
});
}
// Recursive function to append entity content
async function appendEntityContent(contentArray, entity, level, config) {
const entityConfig = config.entities[level];
if (entityConfig.indent) {
contentArray.push({
type: 'text',
text: entityConfig.indent
});
}
contentArray.push({
type: 'entity',
attrs: {
id: entity.id,
text: entity.Name,
typeId: entityConfig.typeId
}
}, {
type: 'hard_break'
});
console.log(`Level ${level}: Processing ${entity.Name}`);
if (entityConfig.childField) {
console.log(`Fetching children for ${entity.Name} at level ${level}`);
const children = await fibery.getEntityById(entityConfig.type, entity.id, [entityConfig.childField]);
if (children && children[entityConfig.childField] && children[entityConfig.childField].length > 0) {
const childPromises = children[entityConfig.childField].map(child => {
const childContentArray = [];
return appendEntityContent(childContentArray, child, level + 1, config).then(() => childContentArray);
});
const childContents = await Promise.all(childPromises);
childContents.forEach(childContent => {
contentArray.push(...childContent);
});
} else {
console.log(`No children found for ${entity.Name} at level ${level}`);
}
}
}
// Main function to generate the project map
async function generateProjectMap() {
try {
await initializeEntityConfigs(fibery, config);
const currentProjectEntity = args.currentEntities[0];
console.log('Fetching Project Entity...');
let projectEntity = await fibery.getEntityById(config.entities[1].type, currentProjectEntity.id, ['Name', 'Pages', config.entities[1].childField]);
console.log('Creating new Page Entity...');
let pageEntity = await fibery.createEntity(config.pageType, {
'Name': `Project Map for ${projectEntity.Name}`,
'Project': projectEntity.id
});
let projectMapContent = [{ type: 'paragraph', content: [] }];
await appendEntityContent(projectMapContent[0].content, projectEntity, 1, config);
console.log('Setting Document Content...');
if (projectMapContent[0].content.length > 0) {
await fibery.setDocumentContent(pageEntity.Description.Secret, JSON.stringify({ doc: { type: 'doc', content: projectMapContent } }), 'json');
}
console.log('Linking Page to Project...');
await fibery.addCollectionItem(config.entities[1].type, projectEntity.id, 'Pages', pageEntity.id);
} catch (error) {
console.error('Error generating project map:', error);
}
}
// Execute the generation of the project map
await generateProjectMap();
Script Explanation
Configuration Module
config
Object: Contains configuration details for the entity types and their relationship fields. This includes the type of each entity level, the child field to navigate through the hierarchy, and the indentation for display purposes.pageType
: Specifies the type of page that will be created in Fibery for displaying the project map.
Initialization
- Fibery API Service: Initializes a connection to the Fibery API.
Functions
fetchEntityTypeIDs()
: Fetches the IDs for specified entity types from Fibery’s schema. This is critical for ensuring that API calls refer to the correct entity types by their unique identifiers.initializeEntityConfigs()
: Integrates the fetched type IDs into the configuration, preparing the entity settings with actual IDs from the Fibery system to be used in later operations.
Recursive Function for Content Generation
appendEntityContent()
:- Appends content related to an entity in a structured format (indentation, entity details).
- Recursively handles child entities to build a nested structure. This function generates content for each entity, adding necessary formatting and recursively processing any children entities to map the entire hierarchy.
- Manages parallel asynchronous operations to fetch and process child entities efficiently.
Main Execution Function
generateProjectMap()
:- Calls
initializeEntityConfigs()
to set up entity configurations with actual type IDs. - Retrieves the main project entity based on provided arguments (
args.currentEntities[0]
). - Creates a new page in Fibery named appropriately to reflect it is a project map.
- Calls
appendEntityContent()
to populate the project map with detailed information about the project and its hierarchy. - Saves this content to the newly created page and links the page to the project for easy access.
- Handles exceptions and logs errors to help diagnose issues during the map generation process.
- Calls
Execution
- The script concludes with an explicit call to
generateProjectMap()
, triggering the entire process when the script is run.