Read Model Hierarchy of a Design
This code sample demonstrates the use of the Manufacturing Data Model API to read the complete hierarchy of the structure of a design. It takes in the name of the hub, the name of the project, and the name of the design (component) as inputs. Thereafter it extracts the hierarchical structure of how the different components occur in the design.
See this code sample on the Manufacturing Data Model Samples Git Repository.
Note: This code sample requires Node.js version 16.13.2 or later.
Before you begin
- If you do not have an app registered for the Manufacturing Data Model API, follow the procedure outlined in Create an App to sign up for an APS account (if necessary) and obtain a client ID for your app.
- Make sure that you add the following as the Callback URL:
http://localhost:3000/callback/oauth
- In the APIs section, verify that the Manufacturing Data Model API is selected.
- Make sure that you add the following as the Callback URL:
- Clone or fork the Git Repository.
- In the folder 1.Read the Complete Model Hierarchy of a Design, install all dependencies by running:
npm i
Setting up the application
index.js:
// Replace the string literal values with your own client id, client secret,
// hub name, project name and component name.
const clientId = '<YOUR_CLIENT_ID>';
const clientSecret = '<YOUR_CLIENT_SECRET>';
const hubName = '<YOUR_HUB_NAME>';
const projectName = '<YOUR_PROJECT_NAME>';
const componentName = '<YOUR_COMPONENT_NAME>';
In index.js shown earlier, specify values for the following variables:
clientId
- Your Client IDclientSecret
- Your Client SecrethubName
- The name of the hub that contains the design you want to read. (See the following image)projectName
- The name of the project that contains the design you want to read.componentName
- The name of thedesign (component) you want to read.

Running the sample
- In the terminal, navigate to the 1.Read the Complete Model Hierarchy of a Design folder and execute the following command:
npm start
- A URL is provided to be opened in a browser to sign in to your Autodesk Account. Open a browser and go to http://localhost:8080.
- Once the “Got the access token. You can close the browser!” message is displayed on the browser, return to the terminal. Allow about 3 minutes to pass. You will see a screen like the following in the terminal.
Workflow description
The workflow can be summarized in two steps:
- Get the root component and its references based on the hub, project, and component name.
- Keep gathering the references for child components.
The file app.js sends a query to the GraphQL server to traverse the hub, project, and its root folder. It then queries the root component and extracts the assembly hierarchy.
app.js:
async getModelHierarchy(hubName, projectName, componentName) {
try {
let projectId = await this.getProjectId(hubName, projectName);
let componentVersionId = await this.getComponentVersionId(projectId, componentName);
// Get first batch of occurrences
let response = await this.sendQuery(
`query GetModelHierarchy($componentVersionId: ID!) {
componentVersion(componentVersionId: $componentVersionId) {
id
name
allOccurrences {
results {
parentComponentVersion {
id
}
componentVersion {
id
name
}
}
pagination {
cursor
}
}
}
}`,
{
componentVersionId
}
);
let rootComponentVersion =
response.data.data.componentVersion;
let cursor = rootComponentVersion.allOccurrences.pagination.cursor;
// Keep getting the rest of the occurrences if needed
while (cursor) {
response = await this.sendQuery(
`query GetModelHierarchy($componentVersionId: ID!, $cursor: String) {
componentVersion(componentVersionId: $componentVersionId) {
allOccurrences (pagination: {cursor: $cursor}) {
results {
parentComponentVersion {
id
}
componentVersion {
id
name
}
}
pagination {
cursor
}
}
}
}`,
{
componentVersionId: rootComponentVersion.id,
cursor
}
);
rootComponentVersion.allOccurrences.results =
rootComponentVersion.allOccurrences.results.concat(
response.data.data.componentVersion.allOccurrences.results
);
cursor =
response.data.data.componentVersion.allOccurrences.pagination.cursor;
}
return rootComponentVersion;
} catch (err) {
console.log("There was an issue: " + err.message);
}
}
JavaScript source code walk-through
app.js
This is the main operational code of the application. It sends a GraphQL query to obtain the root component and iterates through child components to obtain the full hierarchy of the design.
// Axios is a promise-based HTTP client for the browser and node.js.
import axios from "axios";
// Application constructor
export default class App {
constructor(accessToken) {
this.graphAPI = "https://developer.api.autodesk.com/mfg/graphql";
this.accessToken = accessToken;
}
getRequestHeaders() {
return {
"Content-type": "application/json; charset=utf-8",
Authorization: "Bearer " + this.accessToken,
};
}
async sendQuery(query, variables) {
try {
let response = await axios({
method: "POST",
url: `${this.graphAPI}`,
headers: this.getRequestHeaders(),
data: {
query,
variables,
},
});
return response;
} catch (err) {
if (err.response.data.errors) {
let formatted = JSON.stringify(err.response.data.errors, null, 2);
console.log(`API error:\n${formatted}`);
}
throw err;
}
}
async getProjectId(hubName, projectName) {
try {
// Get first batch of occurrences
let response = await this.sendQuery(
`query GetProjectId($hubName: String!, $projectName: String!) {
hubs(filter:{name:$hubName}) {
results {
name
projects(filter:{name:$projectName}) {
results {
name
id
}
}
}
}
}`,
{
hubName,
projectName
}
);
let projectId = response.data.data.hubs.results[0].projects.results[0].id;
return projectId;
} catch (err) {
console.log("There was an issue: " + err.message);
}
}
async getComponentVersionId(projectId, componentName) {
try {
// Get first batch of occurrences
let response = await this.sendQuery(
`query GetComponentVersionId($projectId: ID!, $componentName: String!) {
project(projectId: $projectId) {
name
items(filter:{name:$componentName}) {
results {
... on DesignItem {
name
tipRootComponentVersion {
id
}
}
}
}
}
}`,
{
projectId,
componentName
}
);
let componentVersionId = response.data.data.project.items.results[0].tipRootComponentVersion.id;
return componentVersionId;
} catch (err) {
console.log("There was an issue: " + err.message);
}
}
// <getModelHierarchy>
async getModelHierarchy(hubName, projectName, componentName) {
try {
let projectId = await this.getProjectId(hubName, projectName);
let componentVersionId = await this.getComponentVersionId(projectId, componentName);
// Get first batch of occurrences
let response = await this.sendQuery(
`query GetModelHierarchy($componentVersionId: ID!) {
componentVersion(componentVersionId: $componentVersionId) {
id
name
allOccurrences {
results {
parentComponentVersion {
id
}
componentVersion {
id
name
}
}
pagination {
cursor
}
}
}
}`,
{
componentVersionId
}
);
let rootComponentVersion =
response.data.data.componentVersion;
let cursor = rootComponentVersion.allOccurrences.pagination.cursor;
// Keep getting the rest of the occurrences if needed
while (cursor) {
response = await this.sendQuery(
`query GetModelHierarchy($componentVersionId: ID!, $cursor: String) {
componentVersion(componentVersionId: $componentVersionId) {
allOccurrences (pagination: {cursor: $cursor}) {
results {
parentComponentVersion {
id
}
componentVersion {
id
name
}
}
pagination {
cursor
}
}
}
}`,
{
componentVersionId: rootComponentVersion.id,
cursor
}
);
rootComponentVersion.allOccurrences.results =
rootComponentVersion.allOccurrences.results.concat(
response.data.data.componentVersion.allOccurrences.results
);
cursor =
response.data.data.componentVersion.allOccurrences.pagination.cursor;
}
return rootComponentVersion;
} catch (err) {
console.log("There was an issue: " + err.message);
}
}
// </getModelHierarchy>
}
index.js
This is the module that you use to run the application and set the required constant variables.
import MyApp from './app.js';
import MyAuth from './auth.js';
// Replace the string literal values with your own client id, client secret,
// hub name, project name and component name.
const clientId = '<YOUR_CLIENT_ID>';
const clientSecret = '<YOUR_CLIENT_SECRET>';
const hubName = '<YOUR_HUB_NAME>';
const projectName = '<YOUR_PROJECT_NAME>';
const componentName = '<YOUR_COMPONENT_NAME>';
// Create an instance of auth.js.
let myApsAuth = new MyAuth(clientId, clientSecret);
// Get an access token from your auth.js instance.
let accessToken = await myApsAuth.getAccessToken();
// Create an instance of app.js using the variable set above.
let myApsApp = new MyApp(
accessToken
);
let info = await myApsApp.getModelHierarchy(
hubName,
projectName,
componentName
);
if (info) {
console.log("Model hierarchy:");
printInfo(info.allModelOccurrences, info, "");
}
function printInfo (componentVersions, componentVersion, indent) {
console.log(indent + componentVersion.name);
let subOccurrences = componentVersions.results.filter(
item => item.parentComponentVersion.id === componentVersion.id);
for (let occurrence of subOccurrences) {
printInfo(componentVersions, occurrence.componentVersion, indent + " ");
}
}
auth.js
This is the module that you use to obtain a three-legged access token to be used with the POST request you send to the Manufacturing Data Model API.
It uses the POST /v1/gettoken endpoint.
// Axios is a promise-based HTTP client for the browser and node.js.
// See npmjs.com/package/axios
import axios from 'axios';
// Express is a JavaSscript web application framework. See expressjs.com.
import express from 'express';
// Instantiate an express application.
let app = express();
// Export the Auth class for use by other app modules.
export default class Auth {
// Construct the class instance and set global variables, based on the client ID and secret.
constructor(clientId, clientSecret) {
this.host = 'https://developer.api.autodesk.com/';
this.authAPI = `${this.host}authentication/v2/`;
this.port = 8080;
this.redirectUri = `http://localhost:${this.port}/callback/oauth`;
this.accessTokenPromise = new Promise((resolve, reject) => {
this.resolve = resolve;
this.reject = reject;
});
// Handle the callback/redirection by the Autodesk server once the user approves our app’s access to their data.
app.get('/callback/oauth', async (req, res) => {
const { code } = req.query;
// When you are redirected to the callback URL, the URL also contains a ‘code’ parameter with a value that you can exchange for an actual access token.
try {
const response = await axios({
method: 'POST',
url: `${this.authAPI}token`,
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
data: `client_id=${clientId}&client_secret=${clientSecret}&grant_type=authorization_code&code=${code}&redirect_uri=${this.redirectUri}`
})
// Set the accessToken variable to the value in the response.
this.accessToken = response.data.access_token
// Resolve the Promise passed by the getAccessToken() function below with the access token.
// Let the rest of the application continue.
this.resolve(this.accessToken);
// No need to listen for incoming calls anymore.
this.server.close();
res.redirect('/');
} catch (error) {
console.log(error);
this.reject(error);
}
});
app.get('/', async (req, res) => {
// Once you have the access token, then there is nothing more to do.
if (this.accessToken) {
res.send('Got the access token. You can close the browser!').end();
return;
}
// Otherwise, redirect the user to the Autodesk log-in site where they can log in with their credentials
// and approve our app’s access to their data.
// Once that happens, the Autodesk server redirects the user to the callback URL provided.
// That callback is handled above in the app.get('/callback/oauth' …) function.
const url =
`${this.authAPI}authorize?response_type=code` +
`&client_id=${clientId}` +
`&redirect_uri=${this.redirectUri}` +
'&scope=data:read data:write data:create';
res.redirect(url);
})
this.server = app.listen(this.port);
console.log(
`Open http://localhost:${this.port} in a web browser in order to log in with your Autodesk account!`
);
}
// Pass back a Promise that only resolves and lets the rest of the application continue
// once you have an access token.
getAccessToken = async () => {
return this.accessTokenPromise;
}
}