Getting service account credentials Apps Script - TypeError: Cannot read property from null
Getting service account credentials Apps Script - TypeError: Cannot read property from null
我正在使用 Data Studio Advanced Services 开发一个直接链接到 BigQuery 的 Data Studio 社区连接器。但是,我无法检索服务帐户凭据。
我已经将服务帐户密钥的整个 json 文件作为字符串复制并粘贴到 SERVICE_ACCOUNT_CREDS
var 中,以及 'private_key' 中的整个字符串json 在 SERVICE_ACCOUNT_KEY
var 中,以及服务帐户电子邮件和计费项目 ID 作为字符串,分别在 vars SERVICE_ACCOUNT_EMAIL
和 BILLING_PROJECT_ID
中。当我 运行 getData()
函数时尝试进行身份验证时失败。
代码(是世界银行高级服务示例,但有我的凭据):
var cc = DataStudioApp.createCommunityConnector();
var scriptProperties = PropertiesService.getScriptProperties();
function isAdminUser() {
return false;
}
function getAuthType() {
return cc
.newAuthTypeResponse()
.setAuthType(cc.AuthType.NONE)
.build();
}
function getConfig(request) {
var config = cc.getConfig();
config
.newInfo()
.setId('info')
.setText(
'No configuration is required for this connector. Click connect to create a new data source.'
);
return config.build();
}
function getFields() {
var fields = cc.getFields();
var types = cc.FieldType;
var aggregations = cc.AggregationType;
fields
.newDimension()
.setId('country_name')
.setName('Country')
.setType(types.TEXT);
fields
.newDimension()
.setId('country_code')
.setName('Country Code')
.setType(types.TEXT);
fields
.newDimension()
.setId('indicator_name')
.setName('Indicator')
.setType(types.TEXT);
fields
.newDimension()
.setId('year')
.setName('Year')
.setType(types.YEAR);
fields
.newMetric()
.setId('value')
.setName('Value')
.setType(types.NUMBER)
.setIsReaggregatable(true)
.setAggregation(aggregations.SUM);
return fields;
}
function getSchema(request) {
return {
schema: getFields().build()
};
}
var SERVICE_ACCOUNT_CREDS = '{'+
'"type": "service_account",'+
'"project_id": "string for the project id",'+
'"private_key_id": "string for the private key id",'+
'"private_key": "-----BEGIN PRIVATE KEY-----the private key-----END PRIVATE KEY-----\n",'+
'"client_email": "serviceaccount@projectid.iam.gserviceaccount.com",'+
'"client_id": "the client id",'+
'"auth_uri": "https://accounts.google.com/o/oauth2/auth",'+
'"token_uri": "https://oauth2.googleapis.com/token",'+
'"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",'+
'"client_x509_cert_url": "url"'+
'}';
var SERVICE_ACCOUNT_KEY = 'private key string';
var SERVICE_ACCOUNT_EMAIL = 'serviceaccount@projectid.iam.gserviceaccount.com';
var BILLING_PROJECT_ID = 'projectid';
/**
* Copy the entire credentials JSON file from creating a service account in GCP.
*/
function getServiceAccountCreds() {
return JSON.parse(scriptProperties.getProperty(SERVICE_ACCOUNT_CREDS));
}
function getOauthService() {
var serviceAccountCreds = getServiceAccountCreds();
var serviceAccountKey = serviceAccountCreds[SERVICE_ACCOUNT_KEY];
var serviceAccountEmail = serviceAccountCreds[SERVICE_ACCOUNT_EMAIL];
return OAuth2.createService('WorldBankHealthPopulation')
.setAuthorizationBaseUrl('https://accounts.google.com/o/oauth2/auth')
.setTokenUrl('https://accounts.google.com/o/oauth2/token')
.setPrivateKey(serviceAccountKey)
.setIssuer(serviceAccountEmail)
.setPropertyStore(scriptProperties)
.setCache(CacheService.getScriptCache())
.setScope(['https://www.googleapis.com/auth/bigquery.readonly']);
}
var FIELDS_WHITELIST = [
'country_name',
'country_code',
'indicator_name',
'year',
'value'
];
var BASE_SQL =
'SELECT {{FIELDS}} FROM `bigquery-public-data.world_bank_health_population.health_nutrition_population`';
function makeSQL(request) {
// Create an object of {[fieldName]: boolean} to use as a constant time lookup.
var simpleSet = FIELDS_WHITELIST.reduce(function(obj, field) {
obj[field] = true;
return obj;
}, {});
var requestFieldNames = request.fields.map(function(field) {
return field.name;
});
var fieldNames = FIELDS_WHITELIST.filter(function(fieldName) {
return simpleSet[fieldName];
});
var fieldsSQL = fieldNames.join(', ');
return BASE_SQL.replace('{{FIELDS}}', fieldsSQL);
}
function getData(request) {
var accessToken = getOauthService().getAccessToken();
var serviceAccountCreds = getServiceAccountCreds();
var billingProjectId = serviceAccountCreds[BILLING_PROJECT_ID];
var sql = makeSQL(request);
return cc
.newBigQueryConfig()
.setAccessToken(accessToken)
.setBillingProjectId(billingProjectId)
.setUseStandardSql(true)
.setQuery(sql)
.build();
}
每当我 运行 getOauthService()
或 getData()
我得到“无法从 null
读取 属性 'private key string'
非常感谢任何适合 5 岁儿童的帮助或教程。
回答
在您的 getServiceAccountCreds
函数中,您通过使用对象 scriptProperties.getProperty(SERVICE_ACCOUNT_CREDS)
检索字段,该对象 SERVICE_ACCOUNT_CREDS。根据 Google Apps Script: Properties 示例,预期的唯一参数类型是 String。因此,您应该避免解析 JSON 并使用在代码开头创建的 Properties 对象。
我推荐你使用下面的代码。
代码
替换您的代码
var SERVICE_ACCOUNT_CREDS = '{'+
'"type": "service_account",'+
'"project_id": "string for the project id",'+
'"private_key_id": "string for the private key id",'+
'"private_key": "-----BEGIN PRIVATE KEY-----the private key-----END PRIVATE KEY-----\n",'+
'"client_email": "serviceaccount@projectid.iam.gserviceaccount.com",'+
'"client_id": "the client id",'+
'"auth_uri": "https://accounts.google.com/o/oauth2/auth",'+
'"token_uri": "https://oauth2.googleapis.com/token",'+
'"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",'+
'"client_x509_cert_url": "url"'+
'}';
var SERVICE_ACCOUNT_KEY = 'private key string';
var SERVICE_ACCOUNT_EMAIL = 'serviceaccount@projectid.iam.gserviceaccount.com';
var BILLING_PROJECT_ID = 'projectid';
/**
* Copy the entire credentials JSON file from creating a service account in GCP.
*/
function getServiceAccountCreds() {
return JSON.parse(scriptProperties.getProperty(SERVICE_ACCOUNT_CREDS));
}
对于此代码
var SERVICE_ACCOUNT_CREDS_PROPS = {
type: "service_account",
project_id: "string for the project id",
private_key_id: "string for the private key id",
private_key: "-----BEGIN PRIVATE KEY-----the private key-----END PRIVATE KEY-----\n",
client_email: "serviceaccount@projectid.iam.gserviceaccount.com",
client_id: "the client id",
auth_uri: "https://accounts.google.com/o/oauth2/auth",
token_uri: "https://oauth2.googleapis.com/token",
auth_provider_x509_cert_url: "https://www.googleapis.com/oauth2/v1/certs",
client_x509_cert_url: "url"
};
scriptProperties.setProperties(SERVICE_ACCOUNT_CREDS_PROPS);
var SERVICE_ACCOUNT_KEY = 'private_key';
var SERVICE_ACCOUNT_EMAIL = 'client_email';
var BILLING_PROJECT_ID = 'project_id';
/**
* Copy the entire credentials JSON file from creating a service account in GCP.
*/
function getServiceAccountCreds() {
return scriptProperties.getProperties();
}
参考
实际上,尽管在@Jose Vasquez 建议后应用程序脚本中的连接器代码 运行,但在 Google Data Studio 上使用它会返回错误 Insufficient permissions to the underlying data set.
。在进一步评估教程后,我错过了一个非常重要的点。 credentials对应的json不要粘贴到代码本身,而是在项目属性中引用:
然后在“脚本属性”选项卡上,作为一对 key-value,键为 SERVICE_ACCOUNT_CREDS
,值为 json 服务帐户键的内容:
那么,代码替换就变成了:
var SERVICE_ACCOUNT_CREDS = '{'+
'"type": "service_account",'+
'"project_id": "string for the project id",'+
'"private_key_id": "string for the private key id",'+
'"private_key": "-----BEGIN PRIVATE KEY-----the private key-----END PRIVATE KEY-----\n",'+
'"client_email": "serviceaccount@projectid.iam.gserviceaccount.com",'+
'"client_id": "the client id",'+
'"auth_uri": "https://accounts.google.com/o/oauth2/auth",'+
'"token_uri": "https://oauth2.googleapis.com/token",'+
'"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",'+
'"client_x509_cert_url": "url"'+
'}';
var SERVICE_ACCOUNT_KEY = 'private key string';
var SERVICE_ACCOUNT_EMAIL = 'serviceaccount@projectid.iam.gserviceaccount.com';
var BILLING_PROJECT_ID = 'string for the project id';
/**
* Copy the entire credentials JSON file from creating a service account in GCP.
*/
function getServiceAccountCreds() {
return JSON.parse(scriptProperties.getProperty(SERVICE_ACCOUNT_CREDS));
}
简单地说:
var SERVICE_ACCOUNT_CREDS = 'SERVICE_ACCOUNT_CREDS';
var SERVICE_ACCOUNT_KEY = 'private_key';
var SERVICE_ACCOUNT_EMAIL = 'client_email';
var BILLING_PROJECT_ID = 'project_id';
/**
* Copy the entire credentials JSON file from creating a service account in GCP.
*/
function getServiceAccountCreds() {
return JSON.parse(scriptProperties.getProperty(SERVICE_ACCOUNT_CREDS));
}
由于服务帐户凭据已在脚本属性中设置
我正在使用 Data Studio Advanced Services 开发一个直接链接到 BigQuery 的 Data Studio 社区连接器。但是,我无法检索服务帐户凭据。
我已经将服务帐户密钥的整个 json 文件作为字符串复制并粘贴到 SERVICE_ACCOUNT_CREDS
var 中,以及 'private_key' 中的整个字符串json 在 SERVICE_ACCOUNT_KEY
var 中,以及服务帐户电子邮件和计费项目 ID 作为字符串,分别在 vars SERVICE_ACCOUNT_EMAIL
和 BILLING_PROJECT_ID
中。当我 运行 getData()
函数时尝试进行身份验证时失败。
代码(是世界银行高级服务示例,但有我的凭据):
var cc = DataStudioApp.createCommunityConnector();
var scriptProperties = PropertiesService.getScriptProperties();
function isAdminUser() {
return false;
}
function getAuthType() {
return cc
.newAuthTypeResponse()
.setAuthType(cc.AuthType.NONE)
.build();
}
function getConfig(request) {
var config = cc.getConfig();
config
.newInfo()
.setId('info')
.setText(
'No configuration is required for this connector. Click connect to create a new data source.'
);
return config.build();
}
function getFields() {
var fields = cc.getFields();
var types = cc.FieldType;
var aggregations = cc.AggregationType;
fields
.newDimension()
.setId('country_name')
.setName('Country')
.setType(types.TEXT);
fields
.newDimension()
.setId('country_code')
.setName('Country Code')
.setType(types.TEXT);
fields
.newDimension()
.setId('indicator_name')
.setName('Indicator')
.setType(types.TEXT);
fields
.newDimension()
.setId('year')
.setName('Year')
.setType(types.YEAR);
fields
.newMetric()
.setId('value')
.setName('Value')
.setType(types.NUMBER)
.setIsReaggregatable(true)
.setAggregation(aggregations.SUM);
return fields;
}
function getSchema(request) {
return {
schema: getFields().build()
};
}
var SERVICE_ACCOUNT_CREDS = '{'+
'"type": "service_account",'+
'"project_id": "string for the project id",'+
'"private_key_id": "string for the private key id",'+
'"private_key": "-----BEGIN PRIVATE KEY-----the private key-----END PRIVATE KEY-----\n",'+
'"client_email": "serviceaccount@projectid.iam.gserviceaccount.com",'+
'"client_id": "the client id",'+
'"auth_uri": "https://accounts.google.com/o/oauth2/auth",'+
'"token_uri": "https://oauth2.googleapis.com/token",'+
'"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",'+
'"client_x509_cert_url": "url"'+
'}';
var SERVICE_ACCOUNT_KEY = 'private key string';
var SERVICE_ACCOUNT_EMAIL = 'serviceaccount@projectid.iam.gserviceaccount.com';
var BILLING_PROJECT_ID = 'projectid';
/**
* Copy the entire credentials JSON file from creating a service account in GCP.
*/
function getServiceAccountCreds() {
return JSON.parse(scriptProperties.getProperty(SERVICE_ACCOUNT_CREDS));
}
function getOauthService() {
var serviceAccountCreds = getServiceAccountCreds();
var serviceAccountKey = serviceAccountCreds[SERVICE_ACCOUNT_KEY];
var serviceAccountEmail = serviceAccountCreds[SERVICE_ACCOUNT_EMAIL];
return OAuth2.createService('WorldBankHealthPopulation')
.setAuthorizationBaseUrl('https://accounts.google.com/o/oauth2/auth')
.setTokenUrl('https://accounts.google.com/o/oauth2/token')
.setPrivateKey(serviceAccountKey)
.setIssuer(serviceAccountEmail)
.setPropertyStore(scriptProperties)
.setCache(CacheService.getScriptCache())
.setScope(['https://www.googleapis.com/auth/bigquery.readonly']);
}
var FIELDS_WHITELIST = [
'country_name',
'country_code',
'indicator_name',
'year',
'value'
];
var BASE_SQL =
'SELECT {{FIELDS}} FROM `bigquery-public-data.world_bank_health_population.health_nutrition_population`';
function makeSQL(request) {
// Create an object of {[fieldName]: boolean} to use as a constant time lookup.
var simpleSet = FIELDS_WHITELIST.reduce(function(obj, field) {
obj[field] = true;
return obj;
}, {});
var requestFieldNames = request.fields.map(function(field) {
return field.name;
});
var fieldNames = FIELDS_WHITELIST.filter(function(fieldName) {
return simpleSet[fieldName];
});
var fieldsSQL = fieldNames.join(', ');
return BASE_SQL.replace('{{FIELDS}}', fieldsSQL);
}
function getData(request) {
var accessToken = getOauthService().getAccessToken();
var serviceAccountCreds = getServiceAccountCreds();
var billingProjectId = serviceAccountCreds[BILLING_PROJECT_ID];
var sql = makeSQL(request);
return cc
.newBigQueryConfig()
.setAccessToken(accessToken)
.setBillingProjectId(billingProjectId)
.setUseStandardSql(true)
.setQuery(sql)
.build();
}
每当我 运行 getOauthService()
或 getData()
我得到“无法从 null
非常感谢任何适合 5 岁儿童的帮助或教程。
回答
在您的 getServiceAccountCreds
函数中,您通过使用对象 scriptProperties.getProperty(SERVICE_ACCOUNT_CREDS)
检索字段,该对象 SERVICE_ACCOUNT_CREDS。根据 Google Apps Script: Properties 示例,预期的唯一参数类型是 String。因此,您应该避免解析 JSON 并使用在代码开头创建的 Properties 对象。
我推荐你使用下面的代码。
代码
替换您的代码
var SERVICE_ACCOUNT_CREDS = '{'+
'"type": "service_account",'+
'"project_id": "string for the project id",'+
'"private_key_id": "string for the private key id",'+
'"private_key": "-----BEGIN PRIVATE KEY-----the private key-----END PRIVATE KEY-----\n",'+
'"client_email": "serviceaccount@projectid.iam.gserviceaccount.com",'+
'"client_id": "the client id",'+
'"auth_uri": "https://accounts.google.com/o/oauth2/auth",'+
'"token_uri": "https://oauth2.googleapis.com/token",'+
'"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",'+
'"client_x509_cert_url": "url"'+
'}';
var SERVICE_ACCOUNT_KEY = 'private key string';
var SERVICE_ACCOUNT_EMAIL = 'serviceaccount@projectid.iam.gserviceaccount.com';
var BILLING_PROJECT_ID = 'projectid';
/**
* Copy the entire credentials JSON file from creating a service account in GCP.
*/
function getServiceAccountCreds() {
return JSON.parse(scriptProperties.getProperty(SERVICE_ACCOUNT_CREDS));
}
对于此代码
var SERVICE_ACCOUNT_CREDS_PROPS = {
type: "service_account",
project_id: "string for the project id",
private_key_id: "string for the private key id",
private_key: "-----BEGIN PRIVATE KEY-----the private key-----END PRIVATE KEY-----\n",
client_email: "serviceaccount@projectid.iam.gserviceaccount.com",
client_id: "the client id",
auth_uri: "https://accounts.google.com/o/oauth2/auth",
token_uri: "https://oauth2.googleapis.com/token",
auth_provider_x509_cert_url: "https://www.googleapis.com/oauth2/v1/certs",
client_x509_cert_url: "url"
};
scriptProperties.setProperties(SERVICE_ACCOUNT_CREDS_PROPS);
var SERVICE_ACCOUNT_KEY = 'private_key';
var SERVICE_ACCOUNT_EMAIL = 'client_email';
var BILLING_PROJECT_ID = 'project_id';
/**
* Copy the entire credentials JSON file from creating a service account in GCP.
*/
function getServiceAccountCreds() {
return scriptProperties.getProperties();
}
参考
实际上,尽管在@Jose Vasquez 建议后应用程序脚本中的连接器代码 运行,但在 Google Data Studio 上使用它会返回错误 Insufficient permissions to the underlying data set.
。在进一步评估教程后,我错过了一个非常重要的点。 credentials对应的json不要粘贴到代码本身,而是在项目属性中引用:
然后在“脚本属性”选项卡上,作为一对 key-value,键为 SERVICE_ACCOUNT_CREDS
,值为 json 服务帐户键的内容:
那么,代码替换就变成了:
var SERVICE_ACCOUNT_CREDS = '{'+
'"type": "service_account",'+
'"project_id": "string for the project id",'+
'"private_key_id": "string for the private key id",'+
'"private_key": "-----BEGIN PRIVATE KEY-----the private key-----END PRIVATE KEY-----\n",'+
'"client_email": "serviceaccount@projectid.iam.gserviceaccount.com",'+
'"client_id": "the client id",'+
'"auth_uri": "https://accounts.google.com/o/oauth2/auth",'+
'"token_uri": "https://oauth2.googleapis.com/token",'+
'"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",'+
'"client_x509_cert_url": "url"'+
'}';
var SERVICE_ACCOUNT_KEY = 'private key string';
var SERVICE_ACCOUNT_EMAIL = 'serviceaccount@projectid.iam.gserviceaccount.com';
var BILLING_PROJECT_ID = 'string for the project id';
/**
* Copy the entire credentials JSON file from creating a service account in GCP.
*/
function getServiceAccountCreds() {
return JSON.parse(scriptProperties.getProperty(SERVICE_ACCOUNT_CREDS));
}
简单地说:
var SERVICE_ACCOUNT_CREDS = 'SERVICE_ACCOUNT_CREDS';
var SERVICE_ACCOUNT_KEY = 'private_key';
var SERVICE_ACCOUNT_EMAIL = 'client_email';
var BILLING_PROJECT_ID = 'project_id';
/**
* Copy the entire credentials JSON file from creating a service account in GCP.
*/
function getServiceAccountCreds() {
return JSON.parse(scriptProperties.getProperty(SERVICE_ACCOUNT_CREDS));
}
由于服务帐户凭据已在脚本属性中设置