// GraalJS Script (engine type: 2)
//////// START "main()" /////////////////////////////////////////////////////////////////
main();
function main(){
////// == Config Retrieving / 工程コンフィグの参照 ==
const strAuthzSetting = configs.get ( "AuthzConfU" ); /// REQUIRED
engine.log( " AutomatedTask Config: Authz Setting: " + strAuthzSetting );
const strPropertyId = configs.get ( "StrConfA1" ); /// REQUIRED
if( strPropertyId === "" ){
throw new Error( "\n AutomatedTask ConfigError:" +
" Config {A1: PropertyId} must be non-empty \n" );
}
const strMetric = configs.get ( "StrConfA2" ); /// REQUIRED
if( strMetric === "" ){
throw new Error( "\n AutomatedTask ConfigError:" +
" Config {A2: Metric} must be non-empty \n" );
}
const strDimension = configs.get ( "StrConfA3" ); /// REQUIRED
if( strDimension === "" ){
throw new Error( "\n AutomatedTask ConfigError:" +
" Config {A3: Dimension} must be non-empty \n" );
}
let strStartDate = configs.get ( "StrConfB1" ); // NotRequired
if( strStartDate === "" ){
strStartDate = "yesterday";
}else{
strStartDate = formatToBpmsDateStr( strStartDate );
}
let strEndDate = configs.get ( "StrConfB2" ); // NotRequired
if( strEndDate === "" ){
strEndDate = "today";
}else{
strEndDate = formatToBpmsDateStr( strEndDate );
}
let strDimFilter = configs.get ( "StrConfB3" ); // NotRequired
let strDimFilterValues = configs.get ( "StrConfB4" ); // NotRequired
if( strDimFilter === "" && strDimFilterValues === "" ){
engine.log( " AutomatedTask Config:" +
" DimensionFilter not applied" );
}else if( strDimFilter !== "" && strDimFilterValues !== "" ){
engine.log( " AutomatedTask Config:" +
" DimensionFilter: " + strDimFilter + " " + strDimFilterValues );
}else if( strDimFilter === "" ){
engine.log( " AutomatedTask ConfigWarning:" +
" DimensionFilter ignored, Config {B3: DimensionName} required" );
}else if( strDimFilterValues === "" ){
engine.log( " AutomatedTask ConfigWarning:" +
" DimensionFilter ignored, Config {B4: MatchList} required" );
}else{
throw new Error( "\n AutomatedTask Unexpected Error \n" );
}
const numPocketTotal = configs.getObject( "SelectConfC1" ); // NotRequired
let strRankSize = configs.get ( "StrConfD1" ); // NotRequired
if( strRankSize === "" ){
strRankSize = "20";
}
let numRankSize = strRankSize - 0;
const strPocketRankTsv = configs.getObject( "SelectConfD2" ); // NotRequired
////// == Data Retrieving / ワークフローデータの参照 ==
// (Nothing. Retrieved via Expression Language in Config Retrieving)
////// == Calculating / 演算 ==
/// request1, prepare body
// Google Analytics > Reporting > Google Analytics Data API (GA4) > runReport
// https://developers.google.com/analytics/devguides/reporting/data/v1/rest/v1beta/properties/runReport
let request1Obj = {};
request1Obj.dimensions = [];
request1Obj.dimensions[0] = {};
request1Obj.dimensions[0].name = strDimension;
request1Obj.metrics = [];
request1Obj.metrics[0] = {};
request1Obj.metrics[0].name = strMetric;
request1Obj.dateRanges = [];
request1Obj.dateRanges[0] = {};
request1Obj.dateRanges[0].startDate = strStartDate;
request1Obj.dateRanges[0].endDate = strEndDate;
request1Obj.limit = numRankSize;
if( strDimFilter !== "" && strDimFilterValues !== "" ){
request1Obj.dimensionFilter = {};
request1Obj.dimensionFilter.filter = {};
request1Obj.dimensionFilter.filter.fieldName = strDimFilter;
request1Obj.dimensionFilter.filter.inListFilter = {};
request1Obj.dimensionFilter.filter.inListFilter.caseSensitive = true;
request1Obj.dimensionFilter.filter.inListFilter.values =
strDimFilterValues.split(",");
}
request1Obj.orderBys = [];
request1Obj.orderBys[0] = {};
request1Obj.orderBys[0].metric = {};
request1Obj.orderBys[0].metric.metricName = strMetric;
request1Obj.orderBys[0].desc = true;
request1Obj.metricAggregations = [];
request1Obj.metricAggregations[0] = "TOTAL";
/// request1, prepare header
let request1Uri = "https://analyticsdata.googleapis.com/v1beta/properties/" +
strPropertyId + ":runReport";
let request1 = httpClient.begin(); // HttpRequestWrapper
request1 = request1.authSetting( strAuthzSetting ); // with "Authorization: Bearer XX"
// https://questetra.zendesk.com/hc/en-us/articles/360024574471-R2300#HttpRequestWrapper
request1 = request1.body( JSON.stringify( request1Obj ), "application/json" );
/// request1, try
const response1 = request1.post( request1Uri ); // HttpResponseWrapper
engine.log( " AutomatedTask ApiRequest1 Start: " + request1Uri );
const response1Code = response1.getStatusCode() + "";
const response1Body = response1.getResponseAsString() + "";
engine.log( " AutomatedTask ApiResponse Status: " + response1Code );
if( response1Code !== "200"){
throw new Error( "\n AutomatedTask UnexpectedResponseError: " +
response1Code + "\n" + response1Body + "\n" );
}
/// response1, parse
const response1Obj = JSON.parse( response1Body );
/*
engine.log( response1Body ); // debug
{
"dimensionHeaders": [
{
"name": "city"
}
],
"metricHeaders": [
{
"name": "sessions",
"type": "TYPE_INTEGER"
}
],
"totals": [
{}
],
"metadata": {
"currencyCode": "JPY",
"timeZone": "Asia/Tokyo"
},
"kind": "analyticsData#runReport"
}
*/
/*
{
"dimensionHeaders": [
{
"name": "city"
}
],
"metricHeaders": [
{
"name": "sessions",
"type": "TYPE_INTEGER"
}
],
"rows": [
{
"dimensionValues": [
{
"value": "Chiyoda City"
}
],
"metricValues": [
{
"value": "4137"
}
]
},
{ ... }
],
"totals": [
{
"dimensionValues": [
{
"value": "RESERVED_TOTAL"
}
],
"metricValues": [
{
"value": "13899"
}
]
}
],
"rowCount": 248,
"metadata": {
"currencyCode": "JPY",
"timeZone": "Asia/Tokyo"
},
"kind": "analyticsData#runReport"
}
*/
let strRankTsv = "";
let numTotal = 0;
if( response1Obj.hasOwnProperty("rows") ){
/// Create TSV by rows
strRankTsv += strMetric + "\t" + strDimension + "\n";
for( let i = 0; i < response1Obj.rows.length; i++ ){
strRankTsv += response1Obj.rows[i].metricValues[0].value + "\t";
strRankTsv += response1Obj.rows[i].dimensionValues[0].value;
if( i !== response1Obj.rows.length - 1 ){
strRankTsv += "\n";
}
}
/// Get TOTAL
numTotal = response1Obj.totals[0].metricValues[0].value;
}
////// == Data Updating / ワークフローデータへの代入 ==
if( numPocketTotal !== null ){
engine.setData( numPocketTotal, new java.math.BigDecimal( numTotal ) );
}
if( strPocketRankTsv !== null ){
engine.setData( strPocketRankTsv, strRankTsv );
}
} //////// END "main()" /////////////////////////////////////////////////////////////////
function formatToBpmsDateStr( str ) { // Format from DateStr to "DateStr for Bpms"
if( str === "" ){
throw new Error( "\n AutomatedTask ParseDateError:" +
" String is empty \n" );
}
var arrNumParts = str.match( /\d+/g );
if( arrNumParts === null ){
throw new Error( "\n AutomatedTask ParseDateError:" +
" No numeric characters in: " + str + "\n" );
}
if( arrNumParts.length < 3){
throw new Error( "\n AutomatedTask ParseDateError:" +
" 3 Parts of numeric characters are needed in: " + str + "\n" );
}
let strNew = arrNumParts[0] + "-" +
( '0' + arrNumParts[1] ).slice(-2) + "-" +
( '0' + arrNumParts[2] ).slice(-2);
return strNew;
}
/*
Notes:
- Requests to the property (data storage) of Google Analytics when the process reaches the Automated Step.
- You can automate the extraction work of Google Analytics Data. Analytics Reports
- Extracts via "Google Analytics Data API" for GA4 properties.
- Data API can only be used to access GA4 properties and is not compatible with Universal Analytics.
- As of 202204, it is a "Beta version", although "no breaking changes are expected".
- https://developers.google.com/analytics/devguides/reporting/data/v1
- Make sure that the Mesurement ID to be extracted is `G-XXXXXXXXXX` (GA4 property).
- GA4 Properties is the latest version of Google Analytics released in October 2020.
- The Mesurement ID of the UA property is `UA-XXXXXXXX-X`.
- https://support.google.com/analytics/answer/10089681
- For GA4 properties with "Google Signals" enabled
- "Threshold" may be applied in "Report including number of users" etc., and some data may be excluded.
- From the viewpoint of privacy protection, it is applied when the amount of data is small. "(not set)"
- "Do not include user attribute information" and "Extend the specified period" are effective.
- Official Document: GA4 - Data thresholds
- https://support.google.com/analytics/answer/9383630
- Refer to "GA4 Dimensions & Metrics Explorer" for "API Name" of metrics and dimensions.
- https://ga-dev-tools.web.app/ga4/dimensions-metrics-explorer/
- e.g., `sessions` by `city`
- e.g., `screenPageViews` by `browser`
- e.g., `conversions` by `pagePath`
- e.g., `activeUsers` by `hostName`
- e.g., `userEngagementDuration` by `dayOfWeek`
- Official Document: GA4 - Dimensions and metrics
- https://support.google.com/analytics/answer/9143382
APPENDIX
- To place this "Add-on Automated Step" on the design screen of the workflow diagram
- Import Addon-XML (definition file of this automated step) to Workflow App in advance.
- The system admins can also make it available in all Workflow Apps. (App-shared Add-on)
- Manual M415: Adding an Auto-Step to be Used in a Business Process Definition
- https://questetra.zendesk.com/hc/en-us/articles/360002247792-M415
- To activate the Workflow App including this "Add-on automated Step"
- Authorize API communications by an authorized user in advance.
- Enable "Google Analytics Data API" on the Google Cloud Platform
- APIs and Services: `Google Analytics Data API`
- https://console.cloud.google.com/project/_/apis/library
- OFFICIAL DOCUMENT
- https://cloud.google.com/service-usage/docs/enable-disable
- Create an "OAuth2.0 client ID" on the Google Cloud Platform
- Creating credentials:
- `OAuth Client ID`
- Application type:
- `Web application`
- Name:
- `Questetra to GA4`
- Authorized JavaScript origins:
- `https://your-subdomain.questetra.net`
- Authorized redirect URI:
- `https://s.questetra.net/oauth2callback`
- OFFICIAL DOCUMENT
- https://cloud.google.com/endpoints/docs/frameworks/java/javascript-client
- Set "HTTP authentication (OAuth2)" on the Questetra Workflow-App
- Name:
- `Questetra-GA4`
- Authorization Endpoint URL:
- `https://accounts.google.com/o/oauth2/v2/auth?access_type=offline&prompt=consent`
- Token Endpoint URL:
- `https://oauth2.googleapis.com/token`
- Scope:
- `https://www.googleapis.com/auth/analytics.readonly`
- Client ID / Consumer Secret:
- (see above "OAuth 2.0 client ID")
- OFFICIAL DOCUMENT
- https://developers.google.com/identity/protocols/oauth2/web-server#httprest
- In this Add-on Automated Step, "Data API (GA4)" is used to acquire data from Google Analytics.
- Not "Reporting API V4".
- Not "Core Reporting API V3".
- Requests with a response longer than 30 seconds may result in an error.
- Detailed specifications of "Google Analytics Data API (GA4)"
- GUIDE: Creating a Report
- https://developers.google.com/analytics/devguides/reporting/data/v1/basics
- REFERENCE: Method: properties.runReport
- https://developers.google.com/analytics/devguides/reporting/data/v1/rest/v1beta/properties/runReport
- Can also filter the ranking list by Dimension Filter (inListFilter).
- Only data which dimension valuesexactly match any of the values will be extracted. (Case Sensitive)
- `country`: `Canada,Japan`
- `customEvent:locale`: `ja`
- DATE type data or DATETIME type data are also available to specify the date range.
- `YYYY-MM-DD`, `YYYY/MM/DD`, `YYYY-MM-DD hh:mm` etc
- Must be written in the order of Year Month Date with a delimiter.
Notes-ja:
- 案件が自動工程に到達した際、Google アナリティクスのプロパティ(データ格納庫)にリクエストします。
- GoogleアナリティクスDataの抽出業務を自動化できます。 AnalyticsReports
- GA4プロパティ用の "Google Analytics Data API" を経由して抽出します。
- GA4プロパティへのアクセスにのみ使用でき、UniversalAnalyticsとは互換性がありません。
- また、202204時点において、「重大な変更は予想されていない」ものの、"Beta version" です。
- https://developers.google.com/analytics/devguides/reporting/data/v1
- GA4の計測IDは `G-XXXXXXXXXX` の書式になります。
- GA4プロパティは、2020年10月にリリースされた最新版の Google アナリティクスプロパティです。
- UAプロパティの計測IDは `UA-XXXXXXXX-X` となります。
- https://support.google.com/analytics/answer/10089681
- "Googleシグナル" が有効となっているGA4プロパティの場合、
- 「ユーザー数を含むレポート」等で "しきい値" が適用され、一部データが除外される場合があります。
- 主にプライバシー保護の観点から、データ量が少ない場合に適用されます。"(not set)"
- 「ユーザー属性情報を組み込まない」や「指定期間を長くする」といった方法が有効です。
- 公式マニュアル: [GA4]データしきい値
- https://support.google.com/analytics/answer/9383630
- 指標やディメンジョンの "API Name" は「GA4 Dimensions & Metrics Explorer」を参照します。
- https://ga-dev-tools.web.app/ga4/dimensions-metrics-explorer/
- 例) `city` ごとの `sessions` (セッション数)
- 例) `browser` ごとの `screenPageViews` (表示回数)
- 例) `pagePath` ごとの `conversions` (コンバージョン)
- 例) `hostName` ごとの `activeUsers` (アクティブユーザ数)
- 例) `dayOfWeek` (曜日) ごとの `userEngagementDuration` (フォアグラウンド秒数)
- 公式マニュアル: [GA4]ディメンションと指標
- https://support.google.com/analytics/answer/9143382?hl=ja
APPENDIX-ja
- この[アドオン自動工程]を、ワークフロー図の設計画面で配置(利用)できるようにするには
- 予めアドオンXML(この自動工程の定義ファイル)を、[Workflowアプリ]に追加(アドオン)します。
- システム管理者は、Workflow基盤の全アプリで配置できるようにすることも可能です。(アプリ共有アドオン)
- マニュアル M415: 業務プロセス定義で利用可能な自動工程を追加する (Professional edition)
- https://questetra.zendesk.com/hc/ja/articles/360002247792-M415
- この[アドオン自動工程]を含むワークフローアプリを、運用できるようにするには
- あらかじめ、権限ユーザによる認可(API通信の許可)が必要です。
- Google Cloud Platform 側で『Google Analytics Data API』を有効化する
- APIとサービスの有効化: `Google Analytics Data API`
- https://console.cloud.google.com/project/_/apis/library
- 公式マニュアル: サービスの有効化と無効化
- https://cloud.google.com/service-usage/docs/enable-disable
- Google Cloud Platform 側で『OAuth2.0クライアントID』を作成する
- 認証情報の作成:
- `OAuthクライアントID`
- アプリケーションの種類:
- `ウェブ アプリケーション`
- 名前:
- `Questetra to GA4`
- 承認済みのJavaScript生成元:
- `https://your-subdomain.questetra.net`
- 承認済みのリダイレクト URI:
- `https://s.questetra.net/oauth2callback`
- 公式マニュアル: https://cloud.google.com/endpoints/docs/frameworks/java/javascript-client
- Questetra 側で『"HTTP認証"(OAuth2)』を設定する
- 名前:
- `Questetra-GA4`
- 認可エンドポイントURL:
- `https://accounts.google.com/o/oauth2/v2/auth?access_type=offline&prompt=consent`
- トークンエンドポイントURL:
- `https://oauth2.googleapis.com/token`
- スコープ:
- `https://www.googleapis.com/auth/analytics.readonly`
- クライアントID / クライアントシークレット:
- ( see above "OAuth 2.0 client ID" )
- 公式マニュアル: https://developers.google.com/identity/protocols/oauth2/web-server#httprest
- この[アドオン自動工程]では、Googleアナリティクスからのデータ取得に "Data API" が利用されています。
- "Reporting API V4" ではありません。
- "Core Reporting API V3" ではありません。
- レスポンスが30秒を超えるようなリクエストは、エラーになる場合があります。
- "Google Analytics Data API (GA4)" の詳細仕様
- GUIDE: Creating a Report
- https://developers.google.com/analytics/devguides/reporting/data/v1/basics
- 公式マニュアル: Method: properties.runReport
- https://developers.google.com/analytics/devguides/reporting/data/v1/rest/v1beta/properties/runReport
- 完全一致リスト(inListFilter)を設定すれば、ランキングリストの絞り込みが可能です。
- ディメンションの値がいずれかの値と完全一致するデータだけが抽出されます。(Case Sensitive)
- `country`: `Canada,Japan`
- `customEvent:locale`: `ja`
- 日付範囲の指定には、日付型データや日時型データなどが利用できます。
- `YYYY-MM-DD`, `YYYY/MM/DD`, `YYYY-MM-DD hh:mm` etc
- 区切り文字のある文字列で Year Month Date の順で表記されている必要があります。
*/