Issue Settings
In the previous step, we extracted raw issue data. Some fields contain specific IDs like Issue Sub Type
and Root Cause
,
while Custom Attributes
follow an id-value pair pattern. These are all part of the Issue Settings. To retrieve their full details, we'll need to make additional API calls to the Issues endpoints.
In addition, the APIs permissions follows the user permission setting of the logged user, who authorizes this sample to manipulate the issues. We will also add one more help function to get the user permissions.
- Node.js & VSCode
- .NET & VSCode
- .NET & VS2022
We will append a couple of helper functions below to the end of the services/aps.js
file, for fetching related issue settings.
// Issue Settings
//get issue sub types setting
service.getIssueSubtypes = async (projectId, token) => {
let allSubtypes = [];
let offset = 0;
let totalResults = 0;
do{
const resp = await issuesClient.getIssuesTypes(projectId, {accessToken:token,include:'subtypes',offset:offset});
let eachPage = resp.results.flatMap(item => item.subtypes);
allSubtypes = allSubtypes.concat(eachPage);
offset += resp.pagination.limit;
totalResults = resp.pagination.totalResults;
}while (offset < totalResults)
return allSubtypes;
};
//get issue root causes setting
service.getIssueRootcauses = async (projectId, token) => {
let allRootcauses = [];
let offset = 0;
let totalResults = 0;
do{
const resp = await issuesClient.getRootCauseCategories(projectId, {accessToken:token,include:'rootcauses',offset:offset});
let eachPage = resp.results.flatMap(item => item.rootCauses);
allRootcauses = allRootcauses.concat(eachPage);
offset += resp.pagination.limit;
totalResults = resp.pagination.totalResults;
}while (offset < totalResults)
return allRootcauses;
};
//get custom attributes definitions
service.getIssueCustomAttributesDefs = async (projectId, token) => {
let allCustomAttributesDefs = [];
let offset = 0;
let totalResults = 0;
do{
const resp = await issuesClient.getAttributeDefinitions(projectId, {accessToken:token,offset:offset});
allCustomAttributesDefs = allCustomAttributesDefs.concat( resp.results);
offset += resp.pagination.limit;
totalResults = resp.pagination.totalResults;
}while (offset < totalResults)
return allCustomAttributesDefs;
};
//get issue permissions of the user
service.getIssueUserProfile= async (projectId, token) => {
const resp = await issuesClient.getUserProfile(projectId, {accessToken:token});
return resp
};
Add the following content on APS.Issues.cs
file under Models
folder
// Issue Settings
//get issue sub types setting
public async Task<IEnumerable<dynamic>> GetIssueSubTypes(string projectId, Tokens tokens)
{
IssuesClient issueClient = new IssuesClient(_SDKManager);
var allSubIssueTypes = new List<IssueTypeResultsSubtypes>();
var offset = 0;
var totalResult = 0;
do
{
var issueTypes = await issueClient.GetIssuesTypesAsync(projectId, accessToken: tokens.InternalToken, include: "subtypes", offset: offset);
List<IssueTypeResultsSubtypes> eachPage = issueTypes.Results
.Where(type => type.Subtypes != null && type.Subtypes.Any()) // Skip type with empty subtypes
.SelectMany(type => type.Subtypes) // Flatten the non-empty subtypes lists
.ToList();
allSubIssueTypes.AddRange(eachPage);
offset += (int)issueTypes.Pagination.Limit;
totalResult = (int)issueTypes.Pagination.TotalResults;
} while (offset < totalResult);
return allSubIssueTypes;
}
//get issue root causes setting
public async Task<IEnumerable<dynamic>> GetIssueRootcauses(string projectId, Tokens tokens)
{
IssuesClient issueClient = new IssuesClient(_SDKManager);
var allRootcauses = new List<IssueRootCauseResultsRootCauses>();
var offset = 0;
var totalResult = 0;
do
{
var categories = await issueClient.GetRootCauseCategoriesAsync(projectId, accessToken: tokens.InternalToken, include: "rootcauses", offset: offset);
List<IssueRootCauseResultsRootCauses> eachPage = categories.Results
.Where(type => type.RootCauses != null && type.RootCauses.Any()) // Skip categories with empty rootcasues lists
.SelectMany(type => type.RootCauses) // Flatten the non-empty rootcasues lists
.ToList();
allRootcauses.AddRange(eachPage);
offset += (int)categories.Pagination.Limit;
totalResult = (int)categories.Pagination.TotalResults;
} while (offset < totalResult);
return allRootcauses;
}
//get custom attributes definitions
public async Task<IEnumerable<dynamic>> GetIssueCustomAttDefs(string projectId, Tokens tokens)
{
IssuesClient issueClient = new IssuesClient(_SDKManager);
var allCustomAttDefs = new List<AttrDefinitionResults>();
var offset = 0;
var totalResult = 0;
do
{
var attdefs = await issueClient.GetAttributeDefinitionsAsync(projectId, accessToken: tokens.InternalToken, offset: offset);
allCustomAttDefs.AddRange(attdefs.Results);
offset += (int)attdefs.Pagination.Limit;
totalResult = (int)attdefs.Pagination.TotalResults;
} while (offset < totalResult);
return allCustomAttDefs;
}
//get user permission in Issue
public async Task<Autodesk.Construction.Issues.Model.User> GetIssueUserProfile(string projectId, Tokens tokens)
{
IssuesClient issueClient = new IssuesClient(_SDKManager);
var userInfo = await issueClient.GetUserProfileAsync(projectId, accessToken: tokens.InternalToken);
return userInfo;
}
Add the following content on APS.Issues.cs
file under Models
folder
// Issue Settings
//get issue sub types setting
public async Task<IEnumerable<dynamic>> GetIssueSubTypes(string projectId, Tokens tokens)
{
IssuesClient issueClient = new IssuesClient(_SDKManager);
var allSubIssueTypes = new List<IssueTypeResultsSubtypes>();
var offset = 0;
var totalResult = 0;
do
{
var issueTypes = await issueClient.GetIssuesTypesAsync(projectId, accessToken: tokens.InternalToken, include: "subtypes", offset: offset);
List<IssueTypeResultsSubtypes> eachPage = issueTypes.Results
.Where(type => type.Subtypes != null && type.Subtypes.Any()) // Skip type with empty subtypes
.SelectMany(type => type.Subtypes) // Flatten the non-empty subtypes lists
.ToList();
allSubIssueTypes.AddRange(eachPage);
offset += (int)issueTypes.Pagination.Limit;
totalResult = (int)issueTypes.Pagination.TotalResults;
} while (offset < totalResult);
return allSubIssueTypes;
}
//get issue root causes setting
public async Task<IEnumerable<dynamic>> GetIssueRootcauses(string projectId, Tokens tokens)
{
IssuesClient issueClient = new IssuesClient(_SDKManager);
var allRootcauses = new List<IssueRootCauseResultsRootCauses>();
var offset = 0;
var totalResult = 0;
do
{
var categories = await issueClient.GetRootCauseCategoriesAsync(projectId, accessToken: tokens.InternalToken, include: "rootcauses", offset: offset);
List<IssueRootCauseResultsRootCauses> eachPage = categories.Results
.Where(type => type.RootCauses != null && type.RootCauses.Any()) // Skip categories with empty rootcasues lists
.SelectMany(type => type.RootCauses) // Flatten the non-empty rootcasues lists
.ToList();
allRootcauses.AddRange(eachPage);
offset += (int)categories.Pagination.Limit;
totalResult = (int)categories.Pagination.TotalResults;
} while (offset < totalResult);
return allRootcauses;
}
//get custom attributes definitions
public async Task<IEnumerable<dynamic>> GetIssueCustomAttDefs(string projectId, Tokens tokens)
{
IssuesClient issueClient = new IssuesClient(_SDKManager);
var allCustomAttDefs = new List<AttrDefinitionResults>();
var offset = 0;
var totalResult = 0;
do
{
var attdefs = await issueClient.GetAttributeDefinitionsAsync(projectId, accessToken: tokens.InternalToken, offset: offset);
allCustomAttDefs.AddRange(attdefs.Results);
offset += (int)attdefs.Pagination.Limit;
totalResult = (int)attdefs.Pagination.TotalResults;
} while (offset < totalResult);
return allCustomAttDefs;
}
//get user permission in Issue
public async Task<Autodesk.Construction.Issues.Model.User> GetIssueUserProfile(string projectId, Tokens tokens)
{
IssuesClient issueClient = new IssuesClient(_SDKManager);
var userInfo = await issueClient.GetUserProfileAsync(projectId, accessToken: tokens.InternalToken);
return userInfo;
}
Issue Sub Type is a child of Issue Type . This sample iterates each type, extracts its sub types to build the collection. Similarly, Issue Root Cause is a child of Issue Root Cause Caregory. This sample iterates each category , extracts its root causes to build the collection. The corresponding APIs also return types or categories in pagination.
No API yet to get Issues Permission table like ACC UI does. Current API supports to fetch the logged user permission only.
Server endpoints
Next, let's expose the routes to retrieve issue settings to the client-side code through another set of endpoints.
- Node.js & VSCode
- .NET & VSCode
- .NET & VS2022
We will start by importing the issue settings helper functions defined in services/aps.js
file. Add the following content to routes/issues.js
file.
const { authRefreshMiddleware,
getIssues,
createOrModifyIssues
getIssueSubtypes,
getIssueRootcauses,
getIssueCustomAttributesDefs,
getIssueUserProfile
} = require('../services/aps.js');
router.get('/api/issues/subtypes', async function(req, res, next){
try {
const subTypes = await getIssueSubtypes(req.query.projectId,req.internalOAuthToken.access_token);
res.json(subTypes);
} catch (err) {
next(err);
}
});
router.get('/api/issues/rootcauses', async function(req, res, next){
try {
const rootcauses = await getIssueRootcauses(req.query.projectId,req.internalOAuthToken.access_token);
res.json(rootcauses);
} catch (err) {
next(err);
}
});
router.get('/api/issues/customAttDefs', async function(req, res, next){
try {
const customAttributes = await getIssueCustomAttributesDefs(req.query.projectId,req.internalOAuthToken.access_token);
res.json(customAttributes);
} catch (err) {
next(err);
}
});
router.get('/api/issues/issueUserProfile', async function(req, res, next){
try {
const issueUserProfile = await getIssueUserProfile(req.query.projectId,req.internalOAuthToken.access_token);
res.json([issueUserProfile]);
} catch (err) {
next(err);
}
});
Append the following endpoints on IssuesController.cs
file, under Controllers
folder
[HttpGet("subtypes")]
public async Task<ActionResult<string>> ListIssuesSubTypes( string projectId)
{
var tokens = await AuthController.PrepareTokens(Request, Response, _aps);
if (tokens == null)
{
return Unauthorized();
}
var subtypes = await _aps.GetIssueSubTypes(Request.Query["projectId"], tokens);
return JsonConvert.SerializeObject(subtypes);
}
[HttpGet("rootcauses")]
public async Task<ActionResult<string>> ListRootCauses( string projectId)
{
var tokens = await AuthController.PrepareTokens(Request, Response, _aps);
if (tokens == null)
{
return Unauthorized();
}
var rootcauses = await _aps.GetIssueRootcauses(Request.Query["projectId"], tokens);
return JsonConvert.SerializeObject(rootcauses);
}
[HttpGet("customAttDefs")]
public async Task<ActionResult<string>> ListCustomAttDefs( string projectId)
{
var tokens = await AuthController.PrepareTokens(Request, Response, _aps);
if (tokens == null)
{
return Unauthorized();
}
var attdefs = await _aps.GetIssueCustomAttDefs(Request.Query["projectId"], tokens);
return JsonConvert.SerializeObject(attdefs);
}
[HttpGet("issueUserProfile")]
public async Task<ActionResult<string>> IssueUserProfile(string projectId)
{
var tokens = await AuthController.PrepareTokens(Request, Response, _aps);
if (tokens == null)
{
return Unauthorized();
}
var list = new List<Autodesk.Construction.Issues.Model.User>(); //to feed the table view of client side. build a dummy json array.
var userInfo = await _aps.GetIssueUserProfile(Request.Query["projectId"], tokens);
list.Add(userInfo);
return JsonConvert.SerializeObject(list, settings); ;
}
Append the following endpoints on IssuesController.cs
file, under Controllers
folder
[HttpGet("subtypes")]
public async Task<ActionResult<string>> ListIssuesSubTypes( string projectId)
{
var tokens = await AuthController.PrepareTokens(Request, Response, _aps);
if (tokens == null)
{
return Unauthorized();
}
var subtypes = await _aps.GetIssueSubTypes(Request.Query["projectId"], tokens);
return JsonConvert.SerializeObject(subtypes);
}
[HttpGet("rootcauses")]
public async Task<ActionResult<string>> ListRootCauses( string projectId)
{
var tokens = await AuthController.PrepareTokens(Request, Response, _aps);
if (tokens == null)
{
return Unauthorized();
}
var rootcauses = await _aps.GetIssueRootcauses(Request.Query["projectId"], tokens);
return JsonConvert.SerializeObject(rootcauses);
}
[HttpGet("customAttDefs")]
public async Task<ActionResult<string>> ListCustomAttDefs( string projectId)
{
var tokens = await AuthController.PrepareTokens(Request, Response, _aps);
if (tokens == null)
{
return Unauthorized();
}
var attdefs = await _aps.GetIssueCustomAttDefs(Request.Query["projectId"], tokens);
return JsonConvert.SerializeObject(attdefs);
}
[HttpGet("issueUserProfile")]
public async Task<ActionResult<string>> IssueUserProfile(string projectId)
{
var tokens = await AuthController.PrepareTokens(Request, Response, _aps);
if (tokens == null)
{
return Unauthorized();
}
var list = new List<Autodesk.Construction.Issues.Model.User>(); //to feed the table view of client side. build a dummy json array.
var userInfo = await _aps.GetIssueUserProfile(Request.Query["projectId"], tokens);
list.Add(userInfo);
return JsonConvert.SerializeObject(list, settings); ;
}
Try it out
And that's it for the server side. Time to try it out!
- Use same project ID in previous step (remove b.) and try to call the endpoint: http://localhost:8080/api/issues/subtypes?projectId={your-project-id}, the server application should respond with a JSON list of all the issue sub types from this project.