So basically, my custom integration adds multiple users to a specific BIM project. The problem I am facing is that it will only add account admins and not anyone else. Every time I try to add a non account admin, the code spits out an error that says “user must be account admin and project member”. This makes no sense to me as isn’t every user in BIM a project member. The documentation on forge for posting users does say that the users only need to be project admins and project users and that’s exactly what they are. I attached a link to the documentation below.
Documentation: https://aps.autodesk.com/en/docs/bim360/v1/reference/http/projects-project_id-users-import-POST/
TLDR – My code works for adding all account admins to any single specific project. It just won’t add non account admins.
My code:
// Author: Saheer Multani
const express = require('express');
const axios = require('axios');
const ForgeSDK = require('forge-apis');
const fs = require('fs').promises;
const app = express();
const port = 3000;
const CLIENT_ID = '';
const CLIENT_SECRET = '';
const SCOPES = ['data:read', 'data:write', 'account:read', 'account:write'];
const ACCOUNT_ID = '';
const PROJECT_ID = '';
let accessToken = '';
const oAuth2TwoLegged = new ForgeSDK.AuthClientTwoLegged(CLIENT_ID, CLIENT_SECRET, SCOPES, true);
const getAccessToken = async () => {
try {
const credentials = await oAuth2TwoLegged.authenticate();
accessToken = credentials.access_token;
console.log('Access token:', accessToken);
} catch (error) {
console.error('Error getting access token:', error);
}
};
const addUserToProject = async (userId, email, companyId, roleId, isAdmin) => {
try {
const url = `https://developer.api.autodesk.com/hq/v2/accounts/${ACCOUNT_ID}/projects/${PROJECT_ID}/users/import`;
const headers = {
Authorization: `Bearer ${accessToken}`,
'Content-Type': 'application/json',
'x-user-id': userId,
Region: 'US' // Adjust the region based on your deployment
};
const userData = {
email,
company_id: companyId,
industry_roles: [roleId],
services: {
document_management: {
access_level: isAdmin ? 'admin' : 'user'
}
}
};
if (isAdmin) {
userData.services.project_administration = {
access_level: 'admin'
};
}
const data = [userData];
const response = await axios.post(url, data, { headers });
if (response.data.failure > 0) {
console.error('Failed to add user:', response.data.failure_items);
response.data.failure_items.forEach(item => {
console.error(`Failed to add user with email: ${item.email}`);
item.errors.forEach(error => {
console.error(`Error code: ${error.code}, message: ${error.message}`);
});
});
} else {
console.log('User added successfully:', response.data);
}
} catch (error) {
if (error.response) {
console.error('Error adding user:', error.response.data);
} else {
console.error('Error adding user:', error.message);
}
}
};
const parseUserList = async (filePath) => {
try {
const data = await fs.readFile(filePath, 'utf8');
const lines = data.split('n').filter(line => line.trim());
const users = [];
for (let i = 0; i < lines.length; i += 4) {
const userId = lines[i].split(':')[1].trim().replace(/'/g, '');
const email = lines[i + 1].split(':')[1].trim().replace(/'/g, '');
const companyId = lines[i + 2].split(':')[1].trim().replace(/'/g, '');
const roleId = lines[i + 3].split(':')[1].trim().replace(/'/g, '');
users.push({ userId, email, companyId, roleId });
}
return users;
} catch (error) {
console.error('Error reading user list file:', error);
return [];
}
};
const main = async () => {
await getAccessToken();
const adminUsers = await parseUserList('admin_user_list.txt');
const nonAdminUsers = await parseUserList('non_admin_user_list.txt');
for (const user of adminUsers) {
await addUserToProject(user.userId, user.email, user.companyId, user.roleId, true);
}
for (const user of nonAdminUsers) {
await addUserToProject(user.userId, user.email, user.companyId, user.roleId, false);
}
};
main();
app.listen(port, () => {
console.log(`Server is running at http://localhost:${port}`);
});
Saheer Multani is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.