Google Analytics Data API (GA4) How to use runRealtimeReport function? - javascript

I do a call to
await analyticsDataClient.runRealtimeReport({
entity: {
propertyId: propertyId,
},
dateRanges: [
{
startDate: '2020-03-31',
endDate: 'today',
},
],
dimensions: [
{
name: 'city',
},
],
metrics: [
{
name: 'activeUsers',
},
],
});
but this return the following error:
A property in the form 'properties/1234' where '1234' is a GA4 property Id is required
I think it is because my object inside my runRealtimeReport function is wrong but I don't know how to put in.

To create a realtime report, you need to update the request to be similar to
const propertyId = 'YOUR-GA4-PROPERTY-ID';
const [response] = await client.runRealtimeReport({
property: 'properties/' + propertyId,
dimensions: [{ name: 'city', },],
metrics: [{ name: 'activeUsers', },],
});
Please replace the 'YOUR-GA4-PROPERTY-ID' with your numeric property ID. This page describes where to find your GA4 property ID.
Realtime reports do not need dateRanges. Realtime reports are always for the last 30 minutes for your App or Website. There is more information about creating realtime reports on this page.

Related

Extract data from Shopify into a Google Sheet using Google Apps Script

I am trying to get specific product fields of a JSON object to send to a google sheet. The log tells me that 'console.log(productTitle)' is undefined and that 'Error: TypeError: products.forEach' is not a function. The structure of the Shopify object is below.
function getProducts() {
//set up url
const url = 'https://*****.myshopify.com/admin/'
const endpoint = 'products.json'
//set up parameters
const params = {
headers: {
'Content-Type': 'application/json',
'X-Shopify-Access-Token': '*****'
},
muteHttpExceptions: true
}
try {
//call the url to fetch access token
const response = UrlFetchApp.fetch(url + endpoint, params)
//parse the response and get the access token
const products = JSON.parse(response.getContentText())
console.log(response)
console.log(products)
products.forEach(product => {
const productTitle = product.products_title
console.log(productTitle)
const productId = product.products_id
const productStatus = product.products_status
})
return result
}
catch (e) {
console.log('Error: ' + e)
}
}
/* { products:
[ { id: 121345678910,
title: 'Title',
body_html: 'Body Text',
vendor: 'Vendor Name',
product_type: 'candyrack_generated',
created_at: '2021-07-18T11:04:34-05:00',
handle: 'extended-warranty-1',
updated_at: '2022-10-11T09:15:18-05:00',
published_at: '2021-07-18T11:04:34-05:00',
template_suffix: 'water-pump',
status: 'active',
published_scope: 'web',
tags: '',
admin_graphql_api_id: 'gid://shopify/Product/121345678910',
variants:
[ { product_id: 121345678910,
id: 9876543210,
title: 'Default Title',
price: '9.95',
sku: '',
position: 1,
inventory_policy: 'continue',
compare_at_price: null,
fulfillment_service: 'manual',
inventory_management: null,
option1: 'Default Title',
option2: null,
option3: null,
created_at: '2021-07-20T08:43:11-05:00',
updated_at: '2022-10-11T09:14:17-05:00',
taxable: true,
barcode: '',
grams: 0,
image_id: null,
weight: 0,
weight_unit: 'kg',
inventory_item_id: 24681012,
inventory_quantity: -708,
old_inventory_quantity: -708,
requires_shipping: false,
admin_graphql_api_id: 'gid://shopify/ProductVariant/987654' } ],
options:
[ { product_id: 121345678910,
id: 909000,
name: 'Title',
position: 1,
values: [Object] } ],
images:
[ { product_id: 121345678910,
id: 3693336,
position: 1,
created_at: '2022-04-03T08:43:29-05:00',
updated_at: '2022-04-03T08:43:32-05:00',
alt: null,
width: 1080,
height: 1080,
src: 'http://cdn.shopify.com/s/files/1/0541/4132/13/products/freereplacements.png?v=164899',
variant_ids: [],
admin_graphql_api_id: 'gid://shopify/ProductImage/369333' } ],
image:
{ product_id: 121345678910,
id: 3693336,
position: 1,
created_at: '2022-04-03T08:43:29-05:00',
updated_at: '2022-04-03T08:43:32-05:00',
alt: null,
width: 1080,
height: 1080,
src: 'http://cdn.shopify.com/s/files/1/0541/4132/13/products/freereplacements.png?v=1648993',
variant_ids: [],
admin_graphql_api_id: 'gid://shopify/ProductImage/3693336' } }
*/
I would like to pull different keys into different columns to populate rows for all products. I would also like to know how to access the Finance Reports to pull into Sheets as well. I do get a successful return of all product data 'const products = JSON.parse(response.getContentText())' , can't separate the data. Thank you.
The log tells me that 'console.log(productTitle)' is undefined
Google Apps Script code does not run in the browser. It runs in a sandboxed environment on Google's servers, so logging works a little differently than what you might be used to in the browser. See the docs on Logging for Apps Script.
'Error: TypeError: products.forEach' is not a function
Take a second look at the Shopify response. It's structured as:
{
products: [...]
}
So your code products = JSON.parse(...) is actually referencing the response object, not the products array inside it. Try something like this:
const responseObj = JSON.parse(response.getContentText())
const products = responseObj.products
I would like to pull different keys into different columns to populate rows for all products. I would also like to know how to access the Finance Reports to pull into Sheets as well. I do get a successful return of all product data 'const products = JSON.parse(response.getContentText())' , can't separate the data.
You are on the right track with using forEach to loop through the products in the products array (once you fix the above issue) to get individual properties of each object.
To write the data out to a Google Sheet, you'll need to explore the setValues() API.
In short, you will need to structure the data from the Shopify response into a two-dimensional array to write the data to a sheet. Search Google for two-dimensional array javascript if you are not familiar with this concept; two-dimensional arrays are just a way of structuring tabular data, like that in a spreadsheet. Once you have your two-dimensional array, you will write it back to the Google Sheet with setValues().

How to create "products filter" efficiently in Node.js and Angular?

I'm creating an angular application (computer online store) with a node/express backened. I have a products page to display all the products in my DB. A product has this model (typescript):
interface Product {
name: string
properties: {name: string, value: string | number}[]
}
I have a section within the page where you can filter products by properties. for instance a user can filter all the CPUs that have 4 cores or 8 cores. right now this is implemented like this:
In the angular application i query ALL THE PRODUCTS of the requested category,
loop through all of them, collect their properties and all the possible values and filter like this...
const products = [
{
name: 'intel cpu 1',
properties: [
{name: 'cores', value: 8},
{name: 'clock speed', value: 2.6}
]
},
{
name: 'intel cpu 2',
properties: [
{name: 'cores', value: 4},
{name: 'clock speed', value: 1.2}
]
}
]
collectPropertiesFromProducts(products)
// RESULT:
[
{property: 'cores', possibleValues: [4,8]},
{property: 'clock speed', possibleValues: [1.2,2.6]}
]
For now it works great, i can filter products easily by the result and it is all dynamic (i can just add a property to a product and thats it).
The problem is that it scales VERY BADLY, because:
I have to query all of the products to know their properties
The more products/properties = more CPU time = blocks main thread
My question is how can i do better? i have a node server so moving all the logic to there its pretty useless, i could also just move the "property collecting" function to a worker thread but again, ill have to query all the products...
Instead of dealing with this in the client or in the service itself, you can let mongodb do the calculations for you. E.g. you could write the following aggregation:
db.getCollection('products').aggregate([{
$unwind: "$properties"
},
{
$project: {
name: "$properties.name",
total: {
$add: ["$properties.value", ]
}
}
}, {
$group: {
_id: "$name",
possibleValues: {
$addToSet: "$total"
}
}
}
])
You could then expose this query through a custom endpoint (e.g. GET /product-properties) on your node-server and consume the response on the client.
You should consider doing multiple requests to the backend:
First:
getQueryParams, a new endpoint which returns your RESULT
Second:
A none filtered request to receive the initial set of products
Third:
When select a filter (based on first request) you do a new request with the selected filter

Adding custom card information to Payment Request

I want to implement payment request api to my website.
Payment request popup lists users saved cards associated with mobile phone and Google&Apple accounts.
I also want to put custom credit cards into that popup because we have already implemented save credit cards feature and it is been working for a while. I couldn't find any support for it reading documentations. Is it possible?
Expected to have a support like
var supportedInstruments = [{
supportedMethods: 'basic-card',
data: {
supportedNetworks: ['visa', 'mastercard'],
supportedTypes: ['credit', 'debit']
},
customCards: [{ //Custom credit cards
number: '000000000000000',
owner: 'John Doe'
}]
}];
var details = {
total: {label: 'Donation', amount: {currency: 'USD', value: '65.00'}},
displayItems: [
{
label: 'Original donation amount',
amount: {currency: 'USD', value: '65.00'}
}
],
shippingOptions: [
{
id: 'standard',
label: 'Standard shipping',
amount: {currency: 'USD', value: '0.00'},
selected: true
}
]
};
var request = new PaymentRequest(supportedInstruments, details);
There's no such feature nor proposal as far as I know, though I have heard similar requests in the past.
One work around would be to implement a merchant specific payment method using the Payment Handler API. This API allows you to provide your own payment method. You can consume it by yourself and allow third parties to do so.

Redux: local state id's and/or api uuid's

I'm using Redux with a REST api that uses UUID's. The usual pattern for storing state is using id's as a key for objects:
entities: {
articles: {
1: {
id: 1,
title: 'Some Article',
author: 1
},
2: {
id: 2,
title: 'Other Article',
author: 1
}
},
users: {
1: {
id: 1,
name: 'Dan'
}
}
}
How would I use the UUID's from the api in this? I'd like to be able to create a new entity without having to request the UUID from the server first (for offline capabilities).
Should I:
Use local id's, keep the UUID in a _id property of the entity, and only use it when making an API request? This seems the easiest way, although it feels redundant and I will probably have to search through entities for a certain _id in some cases.
entities: {
articles: {
1: {
_id: 'UUID',
title: 'Some Article',
author: 1
},
2: {
id: 'UUID',
title: 'Other Article',
author: 1
}
},
users: {
1: {
_id: 'UUID',
name: 'Dan'
}
}
}
Use only UUID's from the API, and when creating a new item use a sort if temporary id until the API call is resolved? This seems the best way, although I'm not sure how I would go about changing the id's, which also feels wrong (as they're id's).
entities: {
articles: {
'UUID': {
_id: 'UUID',
title: 'Some Article',
author: 'UUID'
},
'UUID': {
_id: 'UUID',
title: 'Other Article',
author: 'creating'
}
},
users: {
'UUID': {
_id: 'UUID',
name: 'Dan'
},
'creating': {
name: 'Stan'
}
}
}
Do it some other way?
I wouldn't add it to the Redux store until the API returns a response.
In Vue, in my data for a given component I usually have two (sometimes more) root keys, one of which points to the part of my Redux store that handles the data, and the other that is usually form or something of that sort, and the Vue data changes due to binding.
After the user initiates the action to add the resource (POST /resources), and the server returns a successful response, I dispatch an action addedResource. And prior to that I'd dispatch something like addingResource, etc.
Is there any reason this wouldn't work? There shouldn't be a difference using an auto incremented, integer id field vs. a UUID. Your data normalization should still work the same way.

Using Facebook graph API to retrieve whole friendList in Node.js

I log my users with Facebook using Passport.js and then I want to get their friends list. I use FbGraph to request Facebook graph API.
So far, so well. Facebook graph API results are paged, but I can't manage to go beyond the first page. Then responses are empty.
Here's the code:
var graph = require('fbgraph');
// accessToken is returned by Express
graph.setAccessToken(accessToken);
var getFriends = function(url) {
graph.get(url, function (err, res) {
console.log(res);
if (res.paging && res.paging.next) {
getFriends(res.paging.next);
}
});
};
getFriends('/me/friends');
And the output:
{ data:
[ { name: 'Gonzague xxx', id: 'xxx' },
{ name: 'Cyprien xxx', id: 'xxx' },
{ name: 'ChloƩ xxx', id: 'xxx' },
{ name: 'Elena xxx', id: 'xxx' },
{ name: 'Sari xxx', id: 'xxx' },
{ name: 'Marie xxx', id: 'xxx' } ],
paging: { next: 'https://graph.facebook.com/v2.0/xxx/friends?access_token=xxx&limit=5000&offset=5000&__after_id=enc_xxx' },
summary: { total_count: 424 } }
404
{ data: [],
paging: { previous: 'https://graph.facebook.com/v2.0/xxx/friends?access_token=xxx' },
summary: { total_count: 424 }
}
Unfortunately, you can't get the whole friends list on Graph API 2.0+, you can only get your friends who are using your app (using means, have granted permission to that app on Facebook). The total_count key means the user has 424 friends, it doesn't mean that you can get all 424 friends from your app.
The list you are seeing is just the people who have authorized your app, and since there are only a few people there, pagination yields an empty result as what you see is all you have.

Categories

Resources