通过 Apex 的 Salesforce 报告和仪表板 API

通过 Apex 的 Salesforce 报告和仪表板 API,您可以以编程方式访问您的 报表生成器中定义的报表数据。

该 API 使您能够将报表数据集成到任何 Web 或移动应用程序中,内部或 在 Salesforce 平台之外。例如,您可以使用 API 触发带有 每个季度表现最好的销售代表的快照。

通过 Apex 的 Salesforce 报告和仪表板 API 彻底改变了您访问和 可视化您的数据。您可以:

  • 将报表数据集成到自定义对象中。
  • 将报表数据集成到丰富的可视化效果中,以动画化 数据。
  • 构建自定义仪表板。
  • 自动执行报告任务。

概括地说,API 资源使您能够查询和筛选 报告数据。您可以:

  • 同步或异步运行表格、摘要或矩阵报表。
  • 动态筛选特定数据。
  • 查询报表数据和元数据。
  • 要求和限制
    通过 Apex 的 Salesforce 报告和仪表板 API 适用于启用了 API 的组织。
  • 运行报告
    您可以通过 Apex 通过 Salesforce 报告和仪表板 API 同步或异步运行报告。
  • 列出报表
    的异步运行 您最多可以检索异步运行的报表的 2,000 个实例。
  • 获取报表元数据
    可以检索报表元数据以获取有关报表及其报表类型的信息。
  • 获取报表数据 可以使用该类获取事实映射,其中包含与报表关联的数据
    ReportResults
  • 筛选报告
    若要动态获取特定结果,可以通过 API 筛选报告。
  • 解码事实数据图
    事实数据地图包含报表的摘要和记录级数据值。
  • 测试报告
    与所有 Apex 代码一样,通过 Apex 代码的 Salesforce 报告和仪表板 API 需要测试覆盖率。

要求和限制

通过 Apex 的 Salesforce 报告和仪表板 API 适用于以下组织 已启用 API。

除了以下限制外,还适用于通过 Apex 的报表和仪表板 API 常规 API 限制。

  • 在以下情况下,交叉筛选器、标准报表筛选器和按行限制筛选不可用 筛选数据。
  • 只有矩阵报告才支持历史跟踪报告。
  • 历史跟踪报告不支持订阅。
  • API 只能处理包含最多 100 个选为列的字段的报表。
  • 最多可以返回 200 个最近查看的报告的列表。
  • 您的组织每小时最多可以请求 500 次同步报表运行。
  • API 一次最多支持 20 个同步报表运行请求。
  • 异步运行的报表的最多 2,000 个实例的列表可以是 返回。
  • 该 API 一次最多支持 200 个请求来获取异步报告的结果 运行。
  • 您的组织每小时最多可以请求 1,200 个异步请求。
  • 异步报告运行结果在 24 小时滚动周期内可用。
  • API 最多返回前 2,000 个报表行。您可以使用以下方法缩小结果范围 过滤 器。
  • 运行报表时,您最多可以添加 20 个自定义字段筛选器。
  • 如果报表以 Apex 中的自动化流程用户身份在标准或自定义对象上运行 test 类,则仅返回必填的自定义字段。非必填自定义字段不是 结果中显示。
    • 您的组织每小时最多可以请求 200 次仪表板刷新。
    • 您的组织每小时最多可以请求 5,000 个仪表板的结果。

此外,以下限制适用于通过 Apex 的 Reports and Dashboards API。

  • 批处理 Apex 中不允许异步报告调用。
  • Apex 触发器中不允许报告调用。
  • 没有 Apex 方法可以列出最近运行的报告。
  • 同步报表运行期间处理的报表行数计入 将 SOQL 查询检索的总行数限制为 50,000 行的调控器限制 每笔交易。异步运行报表时,不会施加此限制。
  • 在 Apex 测试中,报告运行始终忽略注释,无论注释是否设置为 或 。这 表示报告结果将包含测试未创建的预先存在的数据。 无法禁用注释 用于执行报表。若要限制结果,请对报表使用筛选器。SeeAllDatatruefalseSeeAllData
  • 在 Apex 测试中,异步报告运行仅在测试停止后执行 方法。Test.stopTest

注意

适用于在报表生成器中创建的报表的所有限制也适用于 API。为 有关详细信息,请参阅 Salesforce 联机帮助中的“分析限制”。

运行报表

您可以通过 Salesforce 报表和仪表板同步或异步运行报表 API 通过 Apex。

报表可以在有或没有详细信息的情况下运行,并且可以通过设置报表元数据进行筛选。什么时候 运行报告时,API 将返回相同数量的记录的数据,这些记录在 报告在 Salesforce 用户界面中运行。

如果希望报表快速完成运行,请同步运行报表。否则,我们建议 出于以下原因,您通过 Salesforce API 异步运行报告:

  • 长时间运行的报表在达到超时限制时的风险较低 异步运行。
  • 通过 Apex 的 Salesforce 报告和仪表板 API 可以处理更多数量的 一次异步运行请求。
  • 因为异步运行报表的结果将存储 24 小时 滚动期,它们可用于定期访问。

同步运行报表

运行报表 同步地,使用其中一种方法。例如:ReportManager.runReport()

// Get the report ID
List <Report> reportList = [SELECT Id,DeveloperName FROM Report where 
    DeveloperName = 'Closed_Sales_This_Quarter'];
String reportId = (String)reportList.get(0).get('Id');

// Run the report
Reports.ReportResults results = Reports.ReportManager.runReport(reportId, true);
System.debug('Synchronous results: ' + results);

异步运行报表

运行报表 异步使用其中一种方法。例如:ReportManager.runAsyncReport()

// Get the report ID
List <Report> reportList = [SELECT Id,DeveloperName FROM Report where 
    DeveloperName = 'Closed_Sales_This_Quarter'];
String reportId = (String)reportList.get(0).get('Id');

// Run the report
Reports.ReportInstance instance = Reports.ReportManager.runAsyncReport(reportId, true);
System.debug('Asynchronous instance: ' + instance);

列出报表的异步运行

您最多可以检索 2,000 个报表实例,这些实例 异步运行。

实例列表按日期和时间排序 报告已运行。报告结果滚动存储 24 小时 时期。在此期间,根据您的用户访问级别,您可以 访问已运行的报表的每个实例的结果。

您可以通过调用该方法获取实例列表。 例如:ReportManager.getReportInstances

// Get the report ID
List <Report> reportList = [SELECT Id,DeveloperName FROM Report where
    DeveloperName = 'Closed_Sales_This_Quarter'];
String reportId = (String)reportList.get(0).get('Id');

// Run a report asynchronously
Reports.ReportInstance instance = Reports.ReportManager.runAsyncReport(reportId, true);
System.debug('List of asynchronous runs: ' + 
    Reports.ReportManager.getReportInstances(reportId));

获取报表元数据

您可以检索报表元数据以获取有关以下内容的信息 报表及其报表类型。元数据包括有关 筛选器、分组、详细数据和摘要的报告。您可以 使用元数据执行以下几项操作:

  • 了解您可以在报告中筛选哪些字段和值 类型。
  • 使用元数据信息构建自定义图表可视化效果 关于字段、分组、详细数据和摘要。
  • 运行报表时更改报表元数据中的筛选器。

使用该方法检索报表元数据。然后,您可以使用“get” 类上的方法来访问元数据值。ReportResults.getReportMetadataReportMetadata

下面的示例检索报表的元数据。

// Get the report ID
List <Report> reportList = [SELECT Id,DeveloperName FROM Report where 
    DeveloperName = 'Closed_Sales_This_Quarter'];
String reportId = (String)reportList.get(0).get('Id');

// Run a report
Reports.ReportResults results = Reports.ReportManager.runReport(reportId);

// Get the report metadata
Reports.ReportMetadata rm = results.getReportMetadata();
System.debug('Name: ' + rm.getName());
System.debug('ID: ' + rm.getId());
System.debug('Currency code: ' + rm.getCurrencyCode());
System.debug('Developer name: ' + rm.getDeveloperName());

// Get grouping info for first grouping
Reports.GroupingInfo gInfo = rm.getGroupingsDown()[0];
System.debug('Grouping name: ' + gInfo.getName());
System.debug('Grouping sort order: ' + gInfo.getSortOrder());
System.debug('Grouping date granularity: ' + gInfo.getDateGranularity());

// Get aggregates
System.debug('First aggregate: ' + rm.getAggregates()[0]);
System.debug('Second aggregate: ' + rm.getAggregates()[1]);

// Get detail columns
System.debug('Detail columns: ' + rm.getDetailColumns());

// Get report format
System.debug('Report format: ' + rm.getReportFormat());

获取报表数据

可以使用该类获取事实映射,其中包含关联的数据 有一份报告。

ReportResults

要访问事实映射的数据值,您可以映射分组 value 键设置为相应的事实映射键。在以下示例中, 想象一下,您有一个已分组的商机报表 按收月,您已经汇总了金额字段。自 获取 报告:

  1. 使用以下方法获取报表中的第一个缩减分组 并访问第一个对象。ReportResults.getGroupingsDownGroupingValue
  2. 使用该方法从对象中获取分组键值。GroupingValuegetKey
  3. 通过追加到此键值来构造事实映射键。生成的事实图键表示 第一个缩减分组的汇总值。‘!T’
  4. 使用事实地图从报告结果中获取事实地图 钥匙。
  5. 使用方法获取第一个汇总金额值,并 访问第一个对象。ReportFact.getAggregatesSummaryValue
  6. 从第一行的第一个数据单元格中获取字段值 的报表。ReportFactWithDetails.getRows
// Get the report ID
List <Report> reportList = [SELECT Id,DeveloperName FROM Report where 
    DeveloperName = 'Closed_Sales_This_Quarter'];
String reportId = (String)reportList.get(0).get('Id');

// Run a report synchronously
Reports.reportResults results = Reports.ReportManager.runReport(reportId, true);

// Get the first down-grouping in the report
Reports.Dimension dim = results.getGroupingsDown();
Reports.GroupingValue groupingVal = dim.getGroupings()[0];
System.debug('Key: ' + groupingVal.getKey());
System.debug('Label: ' + groupingVal.getLabel());
System.debug('Value: ' + groupingVal.getValue());

// Construct a fact map key, using the grouping key value
String factMapKey = groupingVal.getKey() + '!T';

// Get the fact map from the report results
Reports.ReportFactWithDetails factDetails =
    (Reports.ReportFactWithDetails)results.getFactMap().get(factMapKey);

// Get the first summary amount from the fact map
Reports.SummaryValue sumVal = factDetails.getAggregates()[0];
System.debug('Summary Value: ' + sumVal.getLabel());

// Get the field value from the first data cell of the first row of the report
Reports.ReportDetailRow detailRow = factDetails.getRows()[0];
System.debug(detailRow.getDataCells()[0].getLabel());

筛选报表

要即时获得特定结果,您可以通过 应用程序接口。

通过 API 对筛选器所做的更改不会影响源 报告定义。使用 API,您可以使用最多 20 个自定义字段筛选器进行筛选 并添加筛选器逻辑(例如 AND 和 OR)。但是标准过滤器(如范围), 按行限制进行筛选,并且交叉筛选器不可用。

在筛选报表之前,检查以下筛选器值会很有帮助 在元数据中。

  • 该方法告诉您是否可以筛选字段。ReportTypeColumn.getFilterable
  • 方法 返回字段的所有筛选器值。ReportTypeColumn.filterValues
  • 该方法列出字段 可用于筛选报表的数据类型。ReportManager.dataTypeFilterOperatorMap
  • 该方法列出报表中存在的所有筛选器。ReportMetadata.getReportFilters

您可以在同步或异步报表运行期间筛选报表。

若要筛选报表,请在报表元数据中设置筛选器值,然后运行报表。 以下示例检索报表元数据,重写筛选器值,然后 运行报表。示例:

  1. 使用该方法从元数据中检索报表筛选器对象。ReportMetadata.getReportFilters
  2. 使用方法将筛选器中的值设置为特定日期,然后运行 报告。ReportFilter.setValue
  3. 将筛选器值覆盖到其他日期并运行报表 再。

该示例的输出显示了不同的总值,基于 已应用的日期筛选器。

// Get the report ID
List <Report> reportList = [SELECT Id,DeveloperName FROM Report where 
    DeveloperName = 'Closed_Sales_This_Quarter'];
String reportId = (String)reportList.get(0).get('Id');

// Get the report metadata
Reports.ReportDescribeResult describe = Reports.ReportManager.describeReport(reportId);
Reports.ReportMetadata reportMd = describe.getReportMetadata();

// Override filter and run report
Reports.ReportFilter filter = reportMd.getReportFilters()[0];
filter.setValue('2013-11-01');
Reports.ReportResults results = Reports.ReportManager.runReport(reportId, reportMd);
Reports.ReportFactWithSummaries factSum = 
    (Reports.ReportFactWithSummaries)results.getFactMap().get('T!T');
System.debug('Value for November: ' + factSum.getAggregates()[0].getLabel());

// Override filter and run report
filter = reportMd.getReportFilters()[0];
filter.setValue('2013-10-01');
results = Reports.ReportManager.runReport(reportId, reportMd);
factSum = (Reports.ReportFactWithSummaries)results.getFactMap().get('T!T');
System.debug('Value for October: ' + factSum.getAggregates()[0].getLabel());

解码事实地图

事实地图包含摘要和记录级别的数据 报表的值。

根据报表的运行方式,报表结果中的事实映射可以包含以下值 仅摘要或摘要和详细数据。事实映射值表示为键,其中 可以通过编程方式用于可视化报表数据。事实图键提供 事实地图的每个部分,您可以从中访问摘要和详细数据。

事实映射键的模式因报表格式而异,如下表所示。

报告格式事实地图键模式
表格式的T!T:报表的总计。两者都记录数据 值和总计由此键表示。
总结<First level row grouping_second level row grouping_third level row grouping>!T:T表示行大 总。
矩阵<First level row grouping_second level row grouping>!<First level column grouping_second level column grouping>.

行或列分组中的每个项目都以 开头编号。以下是事实地图键的一些示例:0

事实地图键描述
0!T第一级分组中的第一项。
1!T第一级分组中的第二项。
0_0!T第一级分组中的第一项和第二级分组中的第一项 分组。
0_1!T第一级分组中的第一项和第二级分组中的第二项 分组。

让我们看一下事实映射键如何表示数据的示例,因为它出现在 Salesforce 表格、摘要或矩阵报告。

表格报表事实地图

下面是表格格式的商机报告示例。由于表格报告 没有分组,所有记录级别的数据和摘要都由键表示,键指的是总计。T!T

表格报表事实数据映射键

摘要报告事实地图

此示例显示摘要报告中的值在事实映射中的表示方式。

摘要报告事实图键
事实地图键描述
0!T“勘探”阶段机会价值的摘要。
1_0!T需求分析中制造机会的概率摘要 阶段。

矩阵报告事实地图

下面是矩阵商机报告中数据的一些事实映射键的示例,其中 几个行和列分组。

矩阵事实图键
事实地图键描述
0!02010 年第 4 季度处于探矿阶段的总机会量。
0_0!0_0制造业勘探阶段的总机会量 2010 年 10 月。
2_1!1_1技术领域价值主张阶段的机会总价值 在2011年2月。
T!T报告的总摘要。

测试报告

与所有 Apex 代码一样,通过 Apex 代码的 Salesforce 报告和仪表板 API 需要测试 覆盖。

Reporting Apex 方法不在系统模式下运行,而是在 当前用户(也称为上下文用户或登录用户)。这 方法有权访问当前用户有权访问的任何内容。

在 Apex 测试中,无论报告运行如何,报告运行始终忽略注释 批注是否设置为 或 。这 意味着报告结果将包括预先存在的数据,这些数据 测试未创建。无法禁用报表的批注 执行。若要限制结果,请对报表使用筛选器。SeeAllDatatruefalseSeeAllData

创建报表测试类

以下 示例测试异步和同步报表。每种方法:

  • 创建一个新的 Opportunity 对象,并使用它来设置筛选器 报告。
  • 运行报表。
  • 调用断言来验证数据。

注意

在 Apex 测试中,异步报告仅在 使用该方法停止测试。Test.stopTest

@isTest
public class ReportsInApexTest{

    @isTest(SeeAllData='true')
    public static void testAsyncReportWithTestData() {

      List <Report> reportList = [SELECT Id,DeveloperName FROM Report where
          DeveloperName = 'Closed_Sales_This_Quarter'];
      String reportId = (String)reportList.get(0).get('Id');
      
      // Create an Opportunity object.
      Opportunity opp = new Opportunity(Name='ApexTestOpp', StageName='stage',
          Probability = 95, CloseDate=system.today());
      insert opp;
    
      Reports.ReportMetadata reportMetadata =
          Reports.ReportManager.describeReport(reportId).getReportMetadata();
      
      // Add a filter.
      List<Reports.ReportFilter> filters = new List<Reports.ReportFilter>(); 
      Reports.ReportFilter newFilter = new Reports.ReportFilter();
      newFilter.setColumn('OPPORTUNITY_NAME');
      newFilter.setOperator('equals');
      newFilter.setValue('ApexTestOpp');
      filters.add(newFilter);
      reportMetadata.setReportFilters(filters);
      
      Test.startTest();
       
      Reports.ReportInstance instanceObj =
          Reports.ReportManager.runAsyncReport(reportId,reportMetadata,false);
      String instanceId = instanceObj.getId();
      
      // Report instance is not available yet.
      Test.stopTest();
      // After the stopTest method, the report has finished executing
      // and the instance is available.
     
      instanceObj = Reports.ReportManager.getReportInstance(instanceId);
      System.assertEquals(instanceObj.getStatus(),'Success');
      Reports.ReportResults result = instanceObj.getReportResults();
      Reports.ReportFact grandTotal = (Reports.ReportFact)result.getFactMap().get('T!T');
      System.assertEquals(1,(Decimal)grandTotal.getAggregates().get(1).getValue());
    }
  
    @isTest(SeeAllData='true')
    public static void testSyncReportWithTestData() {
    
      // Create an Opportunity Object.
      Opportunity opp = new Opportunity(Name='ApexTestOpp', StageName='stage',
          Probability = 95, CloseDate=system.today());
      insert opp;
      
      List <Report> reportList = [SELECT Id,DeveloperName FROM Report where
          DeveloperName = 'Closed_Sales_This_Quarter'];
      String reportId = (String)reportList.get(0).get('Id');
      
      Reports.ReportMetadata reportMetadata =
          Reports.ReportManager.describeReport(reportId).getReportMetadata();
      
      // Add a filter.
      List<Reports.ReportFilter> filters = new List<Reports.ReportFilter>(); 
      Reports.ReportFilter newFilter = new Reports.ReportFilter();
      newFilter.setColumn('OPPORTUNITY_NAME');
      newFilter.setOperator('equals');
      newFilter.setValue('ApexTestOpp');
      filters.add(newFilter);
      reportMetadata.setReportFilters(filters);
      
      Reports.ReportResults result =
          Reports.ReportManager.runReport(reportId,reportMetadata,false); 
      Reports.ReportFact grandTotal = (Reports.ReportFact)result.getFactMap().get('T!T');
      System.assertEquals(1,(Decimal)grandTotal.getAggregates().get(1).getValue());
    }
}