// 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" );
}
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 numPocketPv = configs.getObject( "SelectConfC1" ); // NotRequired
const numPocketSs = configs.getObject( "SelectConfC2" ); // NotRequired
const numPocketEs = configs.getObject( "SelectConfC3" ); // NotRequired
const numPocketAu = configs.getObject( "SelectConfC4" ); // 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 = "hostName";
request1Obj.dimensions[1] = {};
request1Obj.dimensions[1].name = "pagePath";
request1Obj.dimensions[2] = {};
request1Obj.dimensions[2].name = "pageTitle";
request1Obj.metrics = [];
request1Obj.metrics[0] = {};
request1Obj.metrics[0].name = "screenPageViews";
request1Obj.metrics[1] = {};
request1Obj.metrics[1].name = "sessions";
request1Obj.metrics[2] = {};
request1Obj.metrics[2].name = "engagedSessions";
request1Obj.metrics[3] = {};
request1Obj.metrics[3].name = "activeUsers";
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 = "screenPageViews";
request1Obj.orderBys[0].desc = true;
request1Obj.orderBys[1] = {};
request1Obj.orderBys[1].metric = {};
request1Obj.orderBys[1].metric.metricName = "sessions";
request1Obj.orderBys[1].desc = true;
request1Obj.orderBys[2] = {};
request1Obj.orderBys[2].metric = {};
request1Obj.orderBys[2].metric.metricName = "engagedSessions";
request1Obj.orderBys[2].desc = true;
request1Obj.orderBys[3] = {};
request1Obj.orderBys[3].metric = {};
request1Obj.orderBys[3].metric.metricName = "activeUsers";
request1Obj.orderBys[3].desc = true;
request1Obj.metricAggregations = [];
request1Obj.metricAggregations[0] = "TOTAL";
/*
engine.log( request1Obj ); // debug
{
"dimensions": [
{
"name": "hostName"
},
{
"name": "pagePath"
},
{
"name": "pageTitle"
}
],
"metrics": [
{
"name": "screenPageViews"
},
{
"name": "sessions"
},
{
"name": "engagedSessions"
},
{
"name": "activeUsers"
}
],
"dateRanges": [
{
"startDate": "2022-04-01",
"endDate": "today"
}
],
"limit": 20,
"dimensionFilter": {
"filter": {
"fieldName": "country",
"inListFilter": {
"caseSensitive": true,
"values": [
"Japan",
"United States"
]
}
}
},
"orderBys": [
{
"metric": {
"metricName": "screenPageViews"
},
"desc": true
},
{
"metric": {
"metricName": "sessions"
},
"desc": true
},
{
"metric": {
"metricName": "engagedSessions"
},
"desc": true
},
{
"metric": {
"metricName": "activeUsers"
},
"desc": true
}
],
"metricAggregations": [
"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": "hostName"
},
{
"name": "pagePath"
},
{
"name": "pageTitle"
}
],
"metricHeaders": [
{
"name": "screenPageViews",
"type": "TYPE_INTEGER"
},
{
"name": "sessions",
"type": "TYPE_INTEGER"
},
{
"name": "engagedSessions",
"type": "TYPE_INTEGER"
},
{
"name": "activeUsers",
"type": "TYPE_INTEGER"
}
],
"rows": [
{
"dimensionValues": [
{
"value": "example.com"
},
{
"value": "/index.html"
},
{
"value": "Welcome"
}
],
"metricValues": [
{
"value": "2201"
},
{
"value": "375"
},
{
"value": "94"
},
{
"value": "25"
}
]
},
...
{
"dimensionValues": [
...
],
"metricValues": [
...
]
}
],
"totals": [
{
"dimensionValues": [
{
"value": "RESERVED_TOTAL"
},
{
"value": "RESERVED_TOTAL"
},
{
"value": "RESERVED_TOTAL"
}
],
"metricValues": [
{
"value": "53788"
},
{
"value": "26212"
},
{
"value": "3556"
},
{
"value": "2856"
}
]
}
],
"rowCount": 3927,
"metadata": {
"currencyCode": "JPY",
"timeZone": "Asia/Tokyo"
},
"kind": "analyticsData#runReport"
}
*/
let strRankTsv = "";
let numPv = 0;
let numSs = 0;
let numEs = 0;
let numAu = 0;
if( response1Obj.hasOwnProperty("rows") ){
/// Create TSV by rows
strRankTsv += "PV" + "\t" + "SS" + "\t" + "ES" + "\t" + "AU" + "\t" + "URI" + "\t" + "TITLE" + "\n";
for( let i = 0; i < response1Obj.rows.length; i++ ){
strRankTsv += response1Obj.rows[i].metricValues[0].value + "\t";
strRankTsv += response1Obj.rows[i].metricValues[1].value + "\t";
strRankTsv += response1Obj.rows[i].metricValues[2].value + "\t";
strRankTsv += response1Obj.rows[i].metricValues[3].value + "\t";
strRankTsv += response1Obj.rows[i].dimensionValues[0].value;
strRankTsv += response1Obj.rows[i].dimensionValues[1].value + "\t";
strRankTsv += response1Obj.rows[i].dimensionValues[2].value;
if( i !== response1Obj.rows.length - 1 ){
strRankTsv += "\n";
}
}
/// Get TOTAL
numPv = response1Obj.totals[0].metricValues[0].value;
numSs = response1Obj.totals[0].metricValues[1].value;
numEs = response1Obj.totals[0].metricValues[2].value;
numAu = response1Obj.totals[0].metricValues[3].value;
}
////// == Data Updating / ワークフローデータへの代入 ==
if( numPocketPv !== null ){
engine.setData( numPocketPv, new java.math.BigDecimal( numPv ) );
}
if( numPocketSs !== null ){
engine.setData( numPocketSs, new java.math.BigDecimal( numSs ) );
}
if( numPocketEs !== null ){
engine.setData( numPocketEs, new java.math.BigDecimal( numEs ) );
}
if( numPocketAu !== null ){
engine.setData( numPocketAu, new java.math.BigDecimal( numAu ) );
}
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:
- You can automate the extraction work of Google Analytics Data. AnalyticsReports
- When the matter reaches this Automated Step, date will be automatically extracted via API.
- Extracted via "Google Analytics Data API (GA4)" for GA4 properties.
- Data API v1 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 property ID to be extracted is `G-XXXXXXXXXX` (GA4 property).
- GA4 Properties is the latest version of Google Analytics released in October 2020.
- https://support.google.com/analytics/answer/10089681
- If you want to automate the data extraction from the universal analytics (UA) property,
- https://support.questetra.com/ja/addons/google-analytics-reports-pageviews-by-hostname-pagepath-2021/
- The property ID of the UA property is `UA-XXXXXXXX-X`.
- Note that the concept of "session" has changed significantly in the UA and GA4 properties.
- In GA4, "revisit within 30 minutes" is counted as one session.
- The number of sessions in the GA4 property tends to be less than the number of sessions in the UA property.
- https://support.google.com/analytics/answer/9191807?hl=ja
- The measured value for each page is automatically acquired. There are four indicators (metrics) as follows.
- "PageViews" metrics=`screenPageViews`
- "Number of sessions" metrics=`sessions`
- "Number of engaged sessions" metrics=`engagedSessions`
- (Number of sessions that lasted longer than 10 seconds, or had 2 or more screen views )
- https://support.google.com/analytics/answer/11109416
- "Number of active users" metrics=`activeUsers`
- You can also get the total of each. (Not very significant except for "PageViews")
- If any of the following 3 dimensions are different, they will be aggregated as separate pages.
- "Hostname" dimensions=`hostName`
- "Page path" dimensions=`pagePath`
- "Page name" dimensions=`pageTitle`
- Reference: Dimensions and metrics
- "Dimension" is the analysis axis. It is used in expressions such as "aggregate by XX".
- "Metrics" are measured values. (Numerical valuesyou want to count, time you want to total, etc.)
- This Automated Step automatically acquires the "number of pageviews" etc. by each page.
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
- 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`
- 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/auth?access_type=offline&approval_prompt=force`
- Token Endpoint URL:
- `https://accounts.google.com/o/oauth2/token`
- Scope:
- `https://www.googleapis.com/auth/analytics.readonly`
- Client ID / Consumer Secret:
- (see above "OAuth 2.0 client ID")
- 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`
- The dimensions and metrics available in your properties can be listed in "GA4 Dimensions & Metrics Explorer".
- https://ga-dev-tools.web.app/ga4/dimensions-metrics-explorer/
- DOCUMENT https://support.google.com/analytics/answer/9143382
- 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アナリティクスDataの抽出業務を自動化できます。 AnalyticsReports
- 案件が自動工程に到達した際に、API経由で自動的に抽出されます。
- GA4プロパティ用の "Google Analytics Data API (GA4)" を経由して抽出されます。
- Data API v1は、GA4プロパティへのアクセスにのみ使用でき、UniversalAnalyticsとは互換性がありません。
- また、202204時点において、「重大な変更は予想されていない」ものの、"Beta version" です。
- https://developers.google.com/analytics/devguides/reporting/data/v1
- 抽出対象のプロパティIDが `G-XXXXXXXXXX`(GA4プロパティ)となっていることを確認してください。
- GA4プロパティとは、2020年10月にリリースされた最新版のGoogle アナリティクスです。
- https://support.google.com/analytics/answer/10089681?hl=ja
- なお、ユニバーサルアナリティクス(UA)プロパティからのData抽出を自動化したい場合は、コチラ↓
- https://support.questetra.com/ja/addons/google-analytics-reports-pageviews-by-hostname-pagepath-2021/
- UAプロパティのプロパティIDは `UA-XXXXXXXX-X` となっています。
- UAプロパティとGA4プロパティでは "セッション" の概念が大きく変更されている点に注意が必要です。
- GA4では「30分以内の再訪」は、1つのセッションとしてカウントされます。
- GA4プロパティのセッション数は、UAプロパティのセッション数より少なくなる傾向にあります。
- https://support.google.com/analytics/answer/9191807?hl=ja
- ページごとの計測値を自動取得します。指標(メトリックス)は以下の4つです。
- "表示回数"(旧 "ページビュー数") metrics=`screenPageViews`
- "セッション数" metrics=`sessions`
- "エンゲージドセッション数" metrics=`engagedSessions`
- (「10秒以上の閲覧」や「2ページ以上の閲覧」などがあったセッションの数)
- https://support.google.com/analytics/answer/11109416?hl=ja
- "アクティブユーザー数" metrics=`activeUsers`
- それぞれの延べ合計も取得可能です。("表示回数" 以外はあまり有意ではありません)
- もし、以下の3ディメンションのいずれかが異なる場合、別のページとして集計されます。
- "ホスト名" dimensions=`hostName`
- "ページパス" dimensions=`pagePath`
- "ページ名" dimensions=`pageTitle`
- 参考:ディメンションとメトリックス
- 『ディメンション』とは "分析軸" のことです。「○○ごとに集計」といった表現で利用されます。
- 『メトリックス』(指標)とは "計測値" のことです。(数えたい数値や集計したい時間など)
- この自動工程は "ページごと" の "表示回数" 等を自動取得します。
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
- DOCUMENT 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`
- DOCUMENT https://cloud.google.com/endpoints/docs/frameworks/java/javascript-client
- Questetra 側で『"HTTP認証"(OAuth2)』を設定する
- 名前:
- `Questetra-GA4`
- 認可エンドポイントURL:
- `https://accounts.google.com/o/oauth2/auth?access_type=offline&approval_prompt=force`
- トークンエンドポイントURL:
- `https://accounts.google.com/o/oauth2/token`
- スコープ:
- `https://www.googleapis.com/auth/analytics.readonly`
- クライアントID / クライアントシークレット:
- ( see above "OAuth 2.0 client ID" )
- この[アドオン自動工程]では、Googleアナリティクスからのデータ取得に "Data API (GA4)" が利用されています。
- "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
- REFERENCE: 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`
- ご自身のプロパティで利用可能なディメンションやメトリックスは "GA4 Dimensions & Metrics Explorer" にて一覧可能です。
- https://ga-dev-tools.web.app/ga4/dimensions-metrics-explorer/
- DOCUMENT https://support.google.com/analytics/answer/9143382?hl=ja
- 日付範囲の指定には、日付型データや日時型データなどが利用できます。
- `YYYY-MM-DD`, `YYYY/MM/DD`, `YYYY-MM-DD hh:mm` etc
- 区切り文字のある文字列で Year Month Date の順で表記されている必要があります。
*/