Skip to main content

Project Issues

Now, we will implement our server to consume ACC Issues APIs to fetch project issues and create/modify issues.

Work with Issues

We shall be utilizing ACC Issue SDK in both NodeJs and .NET samples

Add the @aps_sdk/construction-issues library, and also create an instance of SDK client of IssuesClient at the beginning of services/aps.js file:

services/aps.js
const { SdkManagerBuilder } = require('@aps_sdk/autodesk-sdkmanager');
const { AuthenticationClient, Scopes, ResponseType } = require('@aps_sdk/authentication');
const { DataManagementClient } = require('@aps_sdk/data-management');
const { IssuesClient } = require('@aps_sdk/construction-issues');
const { APS_CLIENT_ID, APS_CLIENT_SECRET, APS_CALLBACK_URL, INTERNAL_TOKEN_SCOPES, PUBLIC_TOKEN_SCOPES } = require('../config.js');

const service = module.exports = {};

const sdk = SdkManagerBuilder.create().build();
const authenticationClient = new AuthenticationClient(sdk);
const dataManagementClient = new DataManagementClient(sdk);
const issuesClient = new IssuesClient(sdk);

Extract Issues

Next, we will add a couple of helper functions to extract issues. Append the following code to the end of the services/aps.js file. We will extract 100 issues in one call and move to the next 100 issues until all issues are fetched.

services/aps.js
// ACC Assue APIs

//Extract Issue Data
service.getIssues = async (projectId, token) => {
let allIssues = [];
let offset = 0;
let totalResults = 0;
do{
const resp = await issuesClient.getIssues(projectId, {accessToken:token,offset:offset});
allIssues = allIssues.concat(resp.results);
offset += resp.pagination.limit;
totalResults = resp.pagination.totalResults;
}while (offset < totalResults)
return allIssues;
};
tip

The 'Issues' collection is managed in the APS cloud database. To ensure optimal performance, the API follows the web standard of pagination meaning each HTTP request returns only a portion of the records in the collection. With current design, ACC Issue API returns 1-100 issues in one call by default. We also have chance to specify the limit parameter which indicates how many records in one page. The other parameter offset specifies from which index of issue to extract the records. Check API reference of GET:Issues for more information.

Import Issues

Moving forward, we will add helper functions to create and modify issues. POST Issues for creating a new issue, PATCH Issues/:IssueId for modifying an existing issue. When the server receives records imported from the client (via CSV), it checks whether a record contains an ID value. If not, it creates a new issue. If an ID exists, it updates the corresponding issue with the new data.

To track status, a JSON array is logged on console with the successfully created or modified issues, along with any failed API calls and their corresponding CSV row numbers.

Append the following code to the end of the services/aps.js file.

services/aps.js
//import issues (create new issue or modify existing issue)
service.createOrModifyIssues = async (projectId,token,data) => {

let results = {
created:[],
modified:[],
failed:[]
}

await Promise.all(
data.map(async (oneIssueData)=>{
try{
//remove unsupported fields and build the payload
const {id, csvRowNum, ...payload } = oneIssueData;
if(id == '' || id==undefined || id==null){
//create new issue
const resp = await issuesClient.createIssue(projectId,payload,{accessToken:token});
results.created.push({id:resp.id,csvRowNum:oneIssueData.csvRowNum});
}else{
//modify an issue
const resp = await issuesClient.patchIssueDetails(projectId,id,payload,{accessToken:token});
results.modified.push({id:resp.id,csvRowNum:oneIssueData.csvRowNum});
}
}catch(e){
results.failed.push({csvRowNum:oneIssueData.csvRowNum,reason:e.toString()});
}
}));

return results;
};

info

The issue creation and modification APIs only accept a limited set of fields in the request payload. This sample demonstrates how to just use some required fields from the CSV data.

  • title
  • description
  • issueSubtypeId
  • status
  • dueDate
  • assignedTo
  • assignedToType
  • rootCauseId
  • published

Server endpoints

Next, let's expose the routings to extract issues and import issue to the client-side code through another set of endpoints.

Next, let's expose the routings to extract and import issues to the client-side code through set of endpoints. Create issues.js file under the routes subfolder with the following content:

routes/issues.js
const express = require('express');
var bodyParser = require('body-parser');

const { authRefreshMiddleware,
getIssues,
createOrModifyIssues
} = require('../services/aps.js');

let router = express.Router();

router.use(authRefreshMiddleware);

//get issues
router.get('/api/issues/issues', async function(req, res, next){
try {
const issues = await getIssues(req.query.projectId,req.internalOAuthToken.access_token);
res.json(issues);
} catch (err) {
next(err);
}
});

//create new issue or modify issue
router.post('/api/issues/issues', bodyParser.json(), async function (req, res, next) {
const projectId = req.body.projectId;
const issues = req.body.data;

try {
const importResults = await createOrModifyIssues(projectId,req.internalOAuthToken.access_token,issues);
res.json(importResults);

} catch (err) {
next(err);
}
});

module.exports = router;

And mount the router to our server application by modifying server.js:

server.js
const express = require('express');
const session = require('cookie-session');
const { PORT, SERVER_SESSION_SECRET } = require('./config.js');

let app = express();
app.use(express.static('wwwroot'));
app.use(session({ secret: SERVER_SESSION_SECRET, maxAge: 24 * 60 * 60 * 1000 }));
app.use(require('./routes/auth.js'));
app.use(require('./routes/hubs.js'));
app.use(require('./routes/issues.js'));
app.listen(PORT, () => console.log(`Server listening on port ${PORT}...`));

Try it out

And that's it for the server side. Time to try it out!

Issues Response

info
  • It would take a bit long time if the volume of the issue records is large. This sample extracts raw data of issue.
  • Some fields are json object or array object. Their value will be dumped as string in this sample.
  • To test creating or modifying issues, use HTTP test tools or commandline scripts to perform the test with access token and request payload.Recommended HTTP test tools: Postman, Insomnia