Google Analytics Multiple Date Ranges - NodeJS - google-analytics-api

How can I get a report using multiple date ranges, like the example listed below, using nodeJS client library?
It was taken from https://developers.google.com/analytics/devguides/reporting/core/v4/basics#multiple_date_ranges
POST https://analyticsreporting.googleapis.com/v4/reports:batchGet
{
"reportRequests":
[
{
"viewId": "XXXX",
"dateRanges": [
{"startDate": "2014-11-01", "endDate": "2014-11-30"},
{"startDate": "2014-10-01", "endDate": "2014-10-30"}
],
"metrics": [
{"expression": "ga:pageviews"},
{"expression": "ga:sessions"}
],
"dimensions": [{"name": "ga:pageTitle"}]
}
]
}
I tried this:
"dateRanges": [
{ "startDate": "2018-03-17", "endDate": "2018-03-24" },
{ "startDate": "14daysAgo", "endDate": "7daysAgo" }
]
And got the following error:
Missing required parameters: start-date, end-date
Thanks very much for the help!
UPDATE
I figured I was using the wrong function analytics.data.ga.get instead of analyticsreporting.reports.batchGet
But when I try this:
analyticsreporting.reports.batchGet({
"reportRequests": [
{
"viewId": req.params.profileId,
"dateRanges": [
{
"startDate": "2018-03-17",
"endDate": "2018-03-24"
},
{
"startDate": "14daysAgo",
"endDate": "7daysAgo"
}
],
"metrics": [
{
"expression": "ga:users"
}
]
}
]
}, function (err, results) {
if (err){
console.log('ERROR: ');
console.log(err.errors);
res.status(500).send(err.errors);
}
console.log(JSON.stringify(results));
res.send({results: results});
});
I get
message: 'Invalid JSON payload received. Unknown name "reportRequests[dateRanges][endDate]": Cannot bind query parameter. Field \'reportRequests[dateRanges][endDate]\' could not be found in request message.\nInvalid JSON payload received. Unknown name "reportRequests[dateRanges][startDate]": Cannot bind query parameter. Field \'reportRequests[dateRanges][startDate]\' could not be found in request message.\nInvalid JSON payload received. Unknown name "reportRequests[viewId]": Cannot bind query parameter. Field \'reportRequests[viewId]\' could not be found in request message.\nInvalid JSON payload received. Unknown name "reportRequests[metrics][expression]": Cannot bind query parameter. Field \'reportRequests[metrics][expression]\' could not be found in request message.',
What am I missing here?
Thanks!

For future reference
The reportsRequest object here needs to be inside of a resource object, as it's part of the post body as stated by JustinBeckwith at https://github.com/google/google-api-nodejs-client/issues/1085

Related

How to properly pass the session_id to GA4 via Measurement protocol

GA4 purchase events are sent from client server via measurement protocol. But there is no session_id parameter in the queries, because of that source and medium is lost. We tried to pass the session_id parameter in MP request, but no data were received.
Example of submitted request:
{
"timestamp_micros": "1664522406546590",
"non_personalized_ads": false,
"events": [
{
"name": "purchase_balance_top_up",
"params": {
"user_id": "11111111",
"crm_id": "11111111",
"balance": 990,
"payment_method": "paymore"
}
}
],
"client_id": "1119492379.1652295143",
"session_id": "1664522264",
"user_id": "11111111"
}
Attaching a screenshot of the raw data from BigQuery on events sent by MP.
Screenshot of the raw data from BigQuery
Help, how to properly pass the session_id? Or how to make sure that events don't lose source param?
We found a solution to the problem. It's simple. The parameter "session_id" must be passed inside the array "params" of the event.
Here is an example of the correct event data array to be sent via measurement protocol:
{
"timestamp_micros": "1664522406546590",
"non_personalized_ads": false,
"events": [
{
"name": "purchase_balance_top_up",
"params": {
"user_id": "11111111",
"crm_id": "11111111",
"balance": 990,
"payment_method": "paymore",
"session_id": "1664522264"
}
}
],
"client_id": "1119492379.1652295143",
"user_id": "11111111"
}
Actually we are sending purchase events similarly with such request:
{
"client_id": "xxx.xxx",
"user_id" : "xxxx",
"non_personalized_ads": false,
"user_properties": {
"user_id_dimension": {
"value": "xxxx"
}
},
"events": [{
"name": "purchase",
"params": {
"currency": "USD",
"transaction_id": "T_12345",
"value": 12.21,
"engagement_time_msec": 10,
"session_id": "XXXXXXXXXX",
"items": [
{
"item_name": "Top-up"
}
]
}
}]
}
but we are not sending timestamp_micros. And we send 'user_id_dimension' as user property with the same value as 'user_id' parameter to observe user id further in Exploration reports. We've created user-scoped custom dimension in GA4 interface with dimension name User ID and this user property 'user_id_dimension'. Everything works

GA4 (Google Analytics) sessions based on UTM params

I am trying to fetch sessions from GA4 which are relevant to specific UTM params.
In GA3 we were able to use segments (sessions::condition::ga:source==X;ga:medium==Y) but I can not find a way to do this on GA4.
POST https://analyticsdata.googleapis.com/v1beta/#{property}:runReport`
Payload like this:
body = {
"metrics": [
{
"name": "sessions::condition::ga:source==X;ga:medium==Y"
}
],
"dimensions": [
{
"name": "date"
}
],
"dateRanges": [
{
"startDate": '2022-01-01',
"endDate": '2022-01-30',
"name": "current_year"
}
]
}
Returns: Field sessions::condition::ga:source==X;ga:medium==Y is not a valid metric.. Is there a way to do this via new API?
Should I use dimension filter to achieve that? I need to query on both source&medium but it is not clear how do I do this?
"dimensionFilter": {
"filter": {
"fieldName": "firstUserMedium",
"stringFilter": {
"value": "Y"
}
}
}
A dimension filter on sessionSource & sessionMedium returns sessions that have those specific utm_source & utm_medium values. See the dimensions & metrics page for a description of these and other dimensions & metrics.
The needed dimension filter is similar to the following. See Dimension Filters in Creating a Report for more info.
"dimensionFilter": {
"andGroup": {
"expressions": [
{
"filter": {
"fieldName": "sessionSource",
"stringFilter": {
"value": "X"
}
}
},
{
"filter": {
"fieldName": "sessionMedium",
"stringFilter": {
"value": "Y"
}
}
}
]
}
},
Segments are not yet available today in the GA4 Data API.
I think you should check the dimensions and metrcis list for GA4 they dont start with ga
POST https://analyticsdata.googleapis.com/v1beta/properties/GA4_PROPERTY_ID:runReport
{
"dateRanges": [{ "startDate": "2020-09-01", "endDate": "2020-09-15" }],
"dimensions": [{ "name": "country" }],
"metrics": [{ "name": "activeUsers" }]
}
Also at this time i don't think it supports segments.

Google Action / Dialogflow : how to ask for geolocation

I'm trying to implement a simple app for Google Assistant.
All works fine, but now I have a problem with the "permission" helper :
https://developers.google.com/actions/assistant/helpers#helper_intents
I have an intent connected with webhook to my java application. When an user types a sentence similar to "near to me", I want to ask to him his location and then use lat/lon to perform a search.
es: Brazilian restaurant near to me
my intent "searchRestaurant" is fired
I receive the webhook request and I parse it
if I find a parameter that is connected to a sentence like "near to me", so instead to response with a "Card" or a "List" I return a JSON that represent the helper request :
{
"conversationToken": "[]",
"expectUserResponse": true,
"expectedInputs": [
{
"inputPrompt": {
"initialPrompts": [
{
"textToSpeech": "PLACEHOLDER_FOR_PERMISSION"
}
],
"noInputPrompts": []
},
"possibileIntents": [
{
"intent": "actions.intent.PERMISSION",
"inputValueData": {
"#type": "type.googleapis.com/google.actions.v2.PermissionValueSpec",
"optContext": "Posso accedere alla tua posizione?",
"permission": [
"NAME",
"DEVICE_PRECISE_LOCATION"
]
}
}
]
}
]
}
but something seems to be wrong, and I receive an error:
"{\n \"responseMetadata\": {\n \"status\": {\n \"code\": 10,\n \"message\": \"Failed to parse Dialogflow response into AppResponse because of empty speech response\",\n \"details\": [{\n \"#type\": \"type.googleapis.com/google.protobuf.Value\",\n \"value\": \"{\\"id\\":\\"1cc45c5e-c398-4ea7-98a5-408f31ce142d\\",\\"timestamp\\":\\"2018-08-02T14:45:05.752Z\\",\\"lang\\":\\"it\\",\\"result\\":{},\\"alternateResult\\":{},\\"status\\":{\\"code\\":206,\\"errorType\\":\\"partial_content\\",\\"errorDetails\\":\\"Webhook call failed. Error: Failed to parse webhook JSON response: Cannot find field: conversationToken in message google.cloud.dialogflow.v2.WebhookResponse.\\"},\\"sessionId\\":\\"1533221100163\\"}\"\n }]\n }\n }\n}"
The "conversationToken" is filled, so I don't understand the error message.
Maybe I'm trying to perform the operation in a wrong way.
So, which is the correct way to call this helper?
--> I've created a second intent "askGeolocation" that have "actions_intent_PERMISSION" as "Event", and ... if I understand correctly the documentation, should be trigger if the request for helper is correct.
How can I get this working?
UPDATE :
I find some example of the json response for ask permission and seems that it should be different from the one above that i'm using :
https://github.com/dialogflow/fulfillment-webhook-json/blob/master/responses/v2/ActionsOnGoogle/AskForPermission.json
{
"payload": {
"google": {
"expectUserResponse": true,
"systemIntent": {
"intent": "actions.intent.PERMISSION",
"data": {
"#type": "type.googleapis.com/google.actions.v2.PermissionValueSpec",
"optContext": "To deliver your order",
"permissions": [
"NAME",
"DEVICE_PRECISE_LOCATION"
]
}
}
}
}
}
so, i've implemented it and now the response seems to be good (no more error on parsing it), but i still receive an error on it validation :
UnparseableJsonResponse
API Version 2: Failed to parse JSON response string with 'INVALID_ARGUMENT' error: "permission: Cannot find field."
so, a problem still persist.
Anyone know the cause?
Thanks
After some tests i found the problem.
I was returning a wrong json repsonse with "permission" instead of "permissions":
"permission**s**": [
"NAME",
"DEVICE_PRECISE_LOCATION"
]
So the steps to ask for location are correct. I report them here as a little tutorial in order to help who is facing on it for the first time:
1) In DialogFlow, add some logic to your Intent, in order to understand when is ok to ask to user his location. In my case, i've added a "parameter" that identify sentences like "nearby" and so on.
2) When my Intent is fired i receive to my java application a request like this :
...
"queryResult": {
"queryText": "ristorante argentino qui vicino",
"action": "bycategory",
"parameters": {
"askgeolocation": "qui vicino",
"TipoRistorante": ["ristorante", "argentino"]
},
...
3) If "askgeolocation" parameter is filled, instead to return a "simple message" o other type of message, i return a json for ask the permission to geolocation :
{
"payload": {
"google": {
"expectUserResponse": true,
"systemIntent": {
"intent": "actions.intent.PERMISSION",
"data": {
"#type": "type.googleapis.com/google.actions.v2.PermissionValueSpec",
"optContext": "To deliver your order",
"permissions": [
"NAME",
"DEVICE_PRECISE_LOCATION"
]
}
}
}
}
}
4) You MUST have a second Intent that is configured with "actions_intent_PERMISSION " event :
No training phrases
No Action and params
No Responses
But with Fulfillment active :
5) Once your response arrive to Google Assistant this is the message that appear :
6) Now, if user answer "ok" you receive this json on your webhook :
{
"responseId": "32cf46cf-80d8-xxxxxxxxxxxxxxxxxxxxx",
"queryResult": {
"queryText": "actions_intent_PERMISSION",
"action": "geoposition",
"parameters": {
},
"allRequiredParamsPresent": true,
"fulfillmentMessages": [{
"text": {
"text": [""]
}
}],
"outputContexts": [{
"name": "projects/xxxxxxxxxxxxxxxxxxxxx"
}, {
"name": "projects/xxxxxxxxxxxxxxxxxxxxx",
"parameters": {
"PERMISSION": true
}
}, {
"name": "projects/xxxxxxxxxxxxxxxxxxxxx"
}, {
"name": "projects/xxxxxxxxxxxxxxxxxxxxx"
}, {
"name": "projects/xxxxxxxxxxxxxxxxxxxxx"
}, {
"name": "projects/xxxxxxxxxxxxxxxxxxxxx"
}],
"intent": {
"name": "projects/xxxxxxxxxxxxxxxxxxxxx",
"displayName": "geoposition"
},
"intentDetectionConfidence": 1.0,
"languageCode": "it"
},
"originalDetectIntentRequest": {
"source": "google",
"version": "2",
"payload": {
"isInSandbox": true,
"surface": {
"capabilities": [{
"name": "actions.capability.MEDIA_RESPONSE_AUDIO"
}, {
"name": "actions.capability.SCREEN_OUTPUT"
}, {
"name": "actions.capability.AUDIO_OUTPUT"
}, {
"name": "actions.capability.WEB_BROWSER"
}]
},
"requestType": "SIMULATOR",
"inputs": [{
"rawInputs": [{
"inputType": "KEYBOARD"
}],
"arguments": [{
"textValue": "true",
"name": "PERMISSION",
"boolValue": true
}],
"intent": "actions.intent.PERMISSION"
}],
"user": {
"lastSeen": "2018-08-03T08:55:20Z",
"permissions": ["NAME", "DEVICE_PRECISE_LOCATION"],
"profile": {
"displayName": ".... full name of the user ...",
"givenName": "... name ...",
"familyName": "... surname ..."
},
"locale": "it-IT",
"userId": "xxxxxxxxxxxxxxxxxxxxx"
},
"device": {
"location": {
"coordinates": {
"latitude": 45.xxxxxx,
"longitude": 9.xxxxxx
}
}
},
"conversation": {
"conversationId": "xxxxxxxxxxxxxxxxxxxxx",
"type": "ACTIVE",
"conversationToken": "[]"
},
"availableSurfaces": [{
"capabilities": [{
"name": "actions.capability.SCREEN_OUTPUT"
}, {
"name": "actions.capability.AUDIO_OUTPUT"
}, {
"name": "actions.capability.WEB_BROWSER"
}]
}]
}
},
"session": "projects/xxxxxxxxxxxxxxxxxxxxx"
}
that contains, name/surname and latitude/longitude. This information can be saved in your application, in order to not perform again this steps.
I hope this helps.
Davide
In your intent, you can ask for a parameter with a custom Entity. This you can do like this:
entity you can define as "near"
put all the synonyms for near for which you want to trigger location permission in this entity
do not mark this parameter as "required"
do not put any prompt
in the training phrases, add some phrases with this parameter
in your webhook, keep a check on the parameter, if present ask for permission if not continue.
add a permission event to another intent
do your post permission processing in that intent
Entity
Intent
I hope you get it.
There are samples on this topic specifically that guide you through exactly what's needed for requesting permissions in Node and Java.
Note: There are helper intents samples available in Node and Java as well.

Google Fit REST API - dataStreamId with whitespace results in error

I want to use Google's REST API to get the Fitness data of my account. To do so i issue 2 subsequent calls.
GET https://www.googleapis.com/fitness/v1/users/me/dataSources. This returns a list of all available dataSources as in [1].
POST https://www.googleapis.com/fitness/v1/users/me/dataset:aggregate.
I use the dataType name and dataStreamId in the request body from [1] to build the request body [2].
The problem: The second call returns an error [3] for all dataSourceIds that contain whitespace although they were returned exactly that way in the first request. In the code sample there is whitespace because the dataSourceId contains the phone model "Nexus 5". If there is no whitespace, the request succeeds without errors.
I already tried replacing the space by something else ("%20" or "_" or "+") but nothing helped. Is this a bug in the API or am i doing something fundamentally wrong?
Thanks in advance!
Edit 1:
btw i am using Google's oauth-playground with all the fitness scopes selected.
https://developers.google.com/oauthplayground/
Edit 2:
In code sample [2] i used the wrong dataTypeName. Was "activity_confidence" but should be "com.google.activity.samples".
[1] GET response
{
"dataSource": [
{
"application": {
"packageName": "com.google.android.gms"
},
"dataQualityStandard": [
],
"dataStreamId": "derived:com.google.activity.samples:com.google.android.gms:LGE:Nexus 5:c80045fc:detailed",
"dataStreamName": "detailed",
"dataType": {
"field": [
{
"format": "map",
"name": "activity_confidence"
}
],
"name": "com.google.activity.samples"
},
"device": {...},
"type": "derived"
},
...
]
}
[2] POST body
{
"aggregateBy": [
{
"dataSourceId": "derived:com.google.activity.samples:com.google.android.gms:LGE:Nexus 5:c80045fc:detailed",
"dataTypeName": "com.google.activity.samples"
}
],
"endTimeMillis": 1511132400000,
"startTimeMillis": 1510268400000
}
[3] POST Error message
{
"error": {
"code": 400,
"errors": [
{
"domain": "global",
"message": "datasource not found: derived:com.google.activity.samples:com.google.android.gms:LGE:Nexus 5:c80045fc:detailed",
"reason": "invalidArgument"
}
],
"message": "datasource not found: derived:com.google.activity.samples:com.google.android.gms:LGE:Nexus 5:c80045fc:detailed"
}
}
Did you try using a escape character like '\'?
Your data stream ID would look like
derived:com.google.activity.samples:com.google.android.gms:LGE:Nexus\ 5:c80045fc:detailed

GoodData "Create Report Definition" API Call giving 500 Internal Server Error

I'm trying to create a report definition using the GoodData REST API. I use the following endpoint to invoke the rest call.
"/gdc/md/{project-id}/obj"
When i try to invoke the API call with the following dataset in which the projectId and the userId are valid, it gives me the error with the response code 500.
{
"reportDefinition": {
"content": {
"filters": [],
"format": "grid",
"grid": {
"rows": [],
"columns": [
"metricGroup"
],
"sort": {
"columns": [],
"rows": []
},
"columnWidths": [],
"metrics": [
{
"uri": "/gdc/md/qy48iv4flikdlcwpwioizuip74wt8nb5/obj/63f3cecd2a8d3ce2ec9378381c8f39e3",
"alias": ""
}
]
}
},
"meta": {
"title": "Sample report definition",
"summary": "This is a sample report",
"tags": "",
"deprecated": 0,
"category": "samplecategory"
}
}
}
{
"error": {
"message": "Internal server error. Please fill in bug report with request_id='lp78FL5S1IPMqB2n'"
}
}
I'm certain that the user project_id and the user_id are valid. Is this an error in the API?
Thank you in advance.
Apart from the metrics URI that looks weird (hash instead of numeric ID), I was able to dig in our logs an error that says: "Category is not equal to tag structure".
In your example you have its value set to "samplecategory". "category" property defines what type of object are you creating. If you are creating a report definition it should have value of "reportDefinition".
Last time I worked with GoodData API, metrics had numeric IDs. That seems most likely to be the culprit. Where did you get "/gdc/md/qy48iv4flikdlcwpwioizuip74wt8nb5/obj/63f3cecd2a8d3ce2ec9378381c8f39e3" from, especially the "63f3cecd2a8d3ce2ec9378381c8f39e3" part?