Google Apps Script

Google Apps Script 是一种 JavaScript 云脚本语言,它提供便捷方式来跨 Google 产品和第三方服务自动化任务,并构建网络应用程序。

通过内置的Targetprocess REST API,您可以将Targetprocess账户中的数据导出至 Google 电子表格文档。 更新操作同样受支持,具体说明如下。

如何构建并运行您的首个集成脚本

打开您的 Google 电子表格。

按下工具脚本编辑器

剧本编辑

重命名新创建的项目。

重命名

将源代码复制到文件的 Code.gs 编辑区域。 使用下文中的Targetprocess示例获取数据

请将主机名和身份验证令牌的值替换为您在Targetprocess中自有客户账户的对应值。

账户名称
警告: 如何获取令牌? 对于 isTokenSetFromUserProfileTab 参数,请使用以下两种选项之一。 若您的令牌是从 Targetprocess 中的 true “用户资料 > 访问令牌 ”选项卡中签发的,请使用此令牌。 当令牌从 /api/v1/authentication REST API 端点签发时 false 使用。

在下拉菜单中 fetchTargetprocessData 选择名为 的函数进行执行。

运行函数

按下运行键。

运行

授予此脚本所需的权限。 此操作只需执行一次。

请求许可 操作

成功! 您的电子表格现已填入Targetprocess实体。

美国列表

从Targetprocess获取数据

此脚本通过基于令牌的身份验证连接到 myaccount.tpondemand.com Targetprocess账户,并查询用户故事列表。 已加载故事的详细信息附加于最初清空的 Google 电子表格之后。


function fetchTargetprocessData() {
  
var hostname = 'https://myaccount.tpondemand.com';
var authenticationToken = 'MTpFQUVXMHdRVGRMN0x1OXJPYWRXZWZaRkc2aDJaSkRyVWdyWm9rK2tFcldBPS==';
var isTokenSetFromUserProfileTab = true;
//use 'true' if token is issued from User Profile > Access Tokens tab
//use 'false' when token is issued from /api/v1/Authentication API endpoint
  
var takeCount = 10;
var entityTypeResourceName = 'userstories';
var filter = '';
var includeFields = '';
//use the example below to specify exact set of fields to include
//var includeFields = '[ID,Name,Project[Name],EntityState[Name],CustomFields]';

var dateFormat = 'yyyy-MM-dd';
//to see times with dates, use 'yyyy-MM-dd HH:mm';

var dataUrl = hostname + '/api/v1/' + entityTypeResourceName + '?format=json' + '&where=' + filter + '&take=' + takeCount + (includeFields.length > 0 ? '&include=' + includeFields : '') + '&' + (isTokenSetFromUserProfileTab ? 'access_token' : 'token') + '=' + authenticationToken;
var options = {
   'method': 'get'
};
var response = UrlFetchApp.fetch(dataUrl, options);

var json = response.getContentText();
var data = JSON.parse(json);
var entities = data.Items;

var sheet = SpreadsheetApp.getActiveSheet();
  
//initial cleanup of the sheet
sheet.clear();

//creates column names row
var entity = entities[0];
var columnNames = Object.keys(entity);

var customFieldsColumnIndex = 0;
for (var i = 0; i < columnNames.length; i++) {
  if (columnNames[i] == 'CustomFields') {
     customFieldsColumnIndex = i;
  }
}  

if (customFieldsColumnIndex > 0) {
  var entityArray = Object.keys(entity).map(function(k) { return entity[k] });
  var customFieldsData = entityArray[customFieldsColumnIndex];
  for (var k = 0; k < customFieldsData.length; k++) {
  var fieldsPairData = customFieldsData[k];
    columnNames.push(fieldsPairData.Name);
  }  
}

sheet.appendRow(columnNames);

//appends data line by line
for (var i = 0; i < entities.length; i++) {
 var entity = entities[i];
 var entityArray = Object.keys(entity).map(function(k) { return entity[k] });
 
 if (customFieldsColumnIndex > 0) {
 //format custom fields
  var customFieldsData = entityArray[customFieldsColumnIndex];
  var fieldValues = '';
  for (var j = 0; j < customFieldsData.length; j++) {
   var fieldsPairData = customFieldsData[j];
   entityArray.push(fieldsPairData.Value);
  }
  entityArray[customFieldsColumnIndex] = customFieldsData.length;
 }
 
 //add data formatting functions there
 for (var j = 0; j < entityArray.length; j++) {
   var cellValue = entityArray[j];
   if (typeof cellValue == 'string') {
     if (cellValue.indexOf("Date") > -1) {
       var milliseconds = cellValue.substring(6, cellValue.length - 7);
       var originTimeZone = cellValue.substring(cellValue.length - 7, cellValue.length - 4);
       var dateObject = new Date(parseInt(milliseconds) + originTimeZone * 1000 * 60 * 60);
       entityArray[j] = Utilities.formatDate(dateObject, originTimeZone, dateFormat);
     }
   }
   if (typeof cellValue == 'object') {
     var keys = [];
     var resourceType = '';
     var name = '';
     var items = null;
     for(var key in cellValue) {
       keys.push(key);
       if (key == "ResourceType") {
          resourceType = cellValue[key];
       }
       if (key == "Name") {
          name = cellValue[key];
       }  
     };
     if (
            resourceType == "Project" 
         || resourceType == "EntityState" 
         || resourceType == "EntityType" 
         || resourceType == "Priority" 
         || resourceType == "Feature" 
         || resourceType == "Epic" 
         || resourceType == "UserStory" 
         || resourceType == "Program" 
         || resourceType == "Release" 
         || resourceType == "Iteration" 
         || resourceType == "TeamIteration"
        ) {
        entityArray[j] = name;
     }
   }
 }
 sheet.appendRow(entityArray);
}
}

在Targetprocess中更新数据

此脚本通过基于令牌的身份验证连接到 myaccount.tpondemand.com Targetprocess账户,并在项目#2中创建新的用户故事实体。 用户故事的数字ID、名称、描述及创建时间戳将附加至初始清空 Google 的电子表格中。
function updateTargetprocessData() {
  var payload = {
    "Project" : {"ID" : 2},
    "Name": "Test Google Script Story Name",
    "Description": "Test Google Script Story Description"
  };
  var hostname = 'https://myaccount.tpondemand.com';
  var authenticationToken = 'MTozemxBMjI4dkNPMFVqbmlWV21WUVlSb0RYTTAzbkhQZ1lvaTJKanMvQWFBPS==';
  var isTokenSetFromUserProfileTab = true;
  //use 'true' if token is issued from User Profile > Access Tokens tab
  //use 'false' when token is issued from /api/v1/Authentication API endpoint

  var entityTypeResourceName = 'userstories';
  var resultIncludeFields = '[Id,Name,Description,CreateDate]';
  var dateFormat = 'yyyy-MM-dd HH:mm'; //to see no times with dates, use 'yyyy-MM-dd';
  
  var dataUrl = hostname + '/api/v1/' + entityTypeResourceName + '/?format=json' 
  + '&resultInclude=' + resultIncludeFields
  + '&' + (isTokenSetFromUserProfileTab ? 'access_token' : 'token') + '=' + authenticationToken;

  var options = {
   'method': 'POST',
   'payload': JSON.stringify(payload),
   'contentType' : 'application/json',
};
  
var response = UrlFetchApp.fetch(dataUrl, options);
  
var sheet = SpreadsheetApp.getActiveSheet();
  
//initial cleanup of the sheet
sheet.clear();
  
var json = response.getContentText();
var data = JSON.parse(json);
var entities = data;//data.Items; for multiple objects
  
var entity = entities;//entities[0]; for multiple objects
var columnNames = Object.keys(entity);

var customFieldsColumnIndex = 0;
for (var i = 0; i < columnNames.length; i++) {
  if (columnNames[i] == 'CustomFields') {
     customFieldsColumnIndex = i;
  }
}  

if (customFieldsColumnIndex > 0) {
  var entityArray = Object.keys(entity).map(function(k) { return entity[k] });
  var customFieldsData = entityArray[customFieldsColumnIndex];
  for (var k = 0; k < customFieldsData.length; k++) {
  var fieldsPairData = customFieldsData[k];
    columnNames.push(fieldsPairData.Name);
  }  
}

sheet.appendRow(columnNames);

 var entity = entities;//entities[i]; for multiple objects
 var entityArray = Object.keys(entity).map(function(k) { return entity[k] });
 
 if (customFieldsColumnIndex > 0) {
 //format custom fields
  var customFieldsData = entityArray[customFieldsColumnIndex];
  var fieldValues = '';
  for (var j = 0; j < customFieldsData.length; j++) {
   var fieldsPairData = customFieldsData[j];
   entityArray.push(fieldsPairData.Value);
  }
  entityArray[customFieldsColumnIndex] = customFieldsData.length;
 }

 //add data formatting functions there
 for (var j = 0; j < entityArray.length; j++) {
   var cellValue = entityArray[j];
   if (typeof cellValue == 'string') {
     if (cellValue.indexOf("Date") > -1) {
       var milliseconds = cellValue.substring(6, cellValue.length - 7);
       var originTimeZone = cellValue.substring(cellValue.length - 7, cellValue.length - 4);
       var dateObject = new Date(parseInt(milliseconds) + originTimeZone * 1000 * 60 * 60);
       entityArray[j] = Utilities.formatDate(dateObject, originTimeZone, dateFormat);
     }
   }
   if (typeof cellValue == 'object') {
     var keys = [];
     var resourceType = '';
     var name = '';
     var items = null;
     for(var key in cellValue) {
       keys.push(key);
       if (key == "ResourceType") {
          resourceType = cellValue[key];
       }
       if (key == "Name") {
          name = cellValue[key];
       }  
     };
     if (
            resourceType == "Project" 
         || resourceType == "EntityState" 
         || resourceType == "EntityType" 
         || resourceType == "Priority" 
         || resourceType == "Feature" 
         || resourceType == "Epic" 
         || resourceType == "UserStory" 
         || resourceType == "Program" 
         || resourceType == "Release" 
         || resourceType == "Iteration" 
         || resourceType == "TeamIteration"
        ) {
        entityArray[j] = name;
     }
   }   
 }

 sheet.appendRow(entityArray);
}

响应中的实体计数

在演示样本中,我们检索前10个匹配的用户故事。 我们在脚本头部编码了查询实体的数量:
var takeCount = 10;
参数值可 takeCount 增大至1000。 如果查询结果超过1000个实体,则需要编写脚本,通过分页参数多次调用API,随后合并获取的数据。

过滤

在与Excel集成时,还可采用更高级的REST API技术(例如过滤、附加字段和集合)。 有关过滤器的更多信息,请参阅Targetprocess REST API过滤器说明。 以下是支持过滤器的源代码修改示例: 在自定义字段中,日期范围为:
var filter = '(\'CustomFields.Next Date\' gte \'2017-01-09\') and (\'CustomFields.Next Date\' lte \'2017-01-16\')';

延伸阅读

请使用 Google Apps Script 指南或联系我们的支持团队以获取进一步帮助。
注: 替代方案:Zapier目标流程实体也可通过 Zapier连接器与 Google 电子表格文档同步。