Google Reports API - reports.customerUsageReports.get issue - python

I am having some issues with the Google Reports API. I have no issues running the sample code provided in the documentation to get the reports.activities.list data, but when I change to program to to try and pull full domain data (reports.customerUsageReports.get) I get an error stating that "'Resource' object has no attribute 'usage'".
I have no issue with auth and have changed the api scopes in the program to https://www.googleapis.com/auth/admin.reports.usage.readonly as required.
I am running the following snippet to try and access the data
credentials = get_credentials()
http = credentials.authorize(httplib2.Http())
service = discovery.build('admin', 'reports_v1', http=http)
print('Getting data')
results = service.usage().list(date='2016-01-01').execute()
usage = results.get('items', [])
if not usage:
print('No data found.')
else:
print('Data:')
for use in usageReports:
print(use)

Make sure you follow these steps:
According to the Python quickstart, if you modify the scopes, you need to delete the previously saved credentials at ~/.credentials/admin-reports_v1-python-quickstart.json
For the Scopes, in requesting access using OAuth 2.0, your application needs the scope information, as well as information that Google supplies when you register your application (such as the client ID and the client secret).
For more information check this link and the documentation about CustomerUsageReports: get to know how to use these parameter correctly.

The method you want to call is:
service.customerUsageReports()
You are calling:
service.usage()
This does not exist.

Related

Call Microsoft Graph API without refreshing authorization to API every time

I am want to use the Microsoft Graph API to get data from my outlook mailbox.
Using https://github.com/O365/python-o365#authentication and help from SO I have managed to connect to the Microsoft Graph API using the On behalf of a user (public) auth method.
This method returns a URL into the console which then needs to be copied into a browser to Authorize.
Surely I don't have to do this every time I call the Graph API??, and if im reading the above DOC correctly, I don't.
I believe I need to store the AUTH token given back from the URL and then use that when calling the Graph API? But I am having trouble implementing this into my code. Possibly using connection.request_token to store the token and then account.is_authenticated?
This is my first go at working with an API with this type of Auth method
Here is what I have so far.. Hoping someone can point me in the right direction
from O365 import Account, FileSystemTokenBackend
credentials = ('my_client_ID',)
token_backend = FileSystemTokenBackend(token_path=(r'C:\Users\Desktop', token_filename = 'my_token.txt')
account = Account(credentials, auth_flow_type = 'public', token_backend = token_backend)
account.authenticate(scopes = ['Mail.Read', 'offline_access'])

How to get Google's Oauth screen to load in a window on a Flask app on Heroku

I'm building a Python Flask app that gathers some information about the user's Google Tag Manager (GTM) account. To do this, I need them to authorize read only access to their GTM account, and then I go and retrieve the data I need.
The ideal flow here is:
User clicks "log in with Google"
A new tab opens up asking them to select their Google account
The next page tells them what permissions I'm looking for, and to allow access
They accept, the auth flow is complete and the tab closes.
I've managed to get the app running locally with the desired flow, no problem.
However, once I deployed it to Heroku, I hit a roadblock, in that the auth flow simply doesn't work. Heroku's logs read:
If your browser is on a different machine then exit and re-run this
application with the command-line parameter
--noauth_local_webserver
I dug a bit, added args.noauth_local_webserver = True as a parameter to the Oauth Flow object.
However, that creates a new problem, where on page load, this shows up in Heroku's logs:
Go to the following link in your browser:
https://accounts.google.com/o/oauth2/auth?client_id=629932623162-nguh8ssud6her0e6bbc5vloe1k3aq95q.apps.googleusercontent.com&redirect_uri=urn%3Aietf%3Awg%3Aoauth%3A2.0%3Aoob&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Ftagmanager.readonly&access_type=offline&response_type=code&prompt=select_account
Enter verification code:
This works in my local environment as I can visit that page and paste the verification code into my console, but isn't really useful on Heroku. I'm not sure where I'd paste the verification code, and this certainly doesn't make sense if someone other than me is going to try and log on.
Does anyone know how I can get the user's web browser to open the auth flow automatically, rather than having it show up in the console?
If it helps, here's the GetService() function I'm using (pulled from Google's docs):
def GetService():
# Parse command-line arguments.
parser = argparse.ArgumentParser(
formatter_class=argparse.RawDescriptionHelpFormatter,
parents=[tools.argparser])
flags = parser.parse_args([])
flags.noauth_local_webserver = True
# Set up a Flow object to be used if we need to authenticate.
flow = client.flow_from_clientsecrets(
client_secrets_path, scope=scope,
prompt = 'consent',
message=tools.message_if_missing(client_secrets_path))
storage = file.Storage(api_name + '.dat')
credentials = storage.get()
if credentials is None or credentials.invalid:
credentials = tools.run_flow(flow, storage, flags)
http = credentials.authorize(http=httplib2.Http())
# Build the service object.
service = build(api_name, api_version, http=http)
return service
I then retrieve data from the service object using calls like this:
def retrieveAcctList():
accountObj = GetService().accounts().list(
access_token = token).execute()
which triggers the flow on my local machine.
Any help is appreciated!

Unable to perform Cloud Function trigger a HTTP triggered Cloud Function that doesn't allow unauthenticated invocations?

I have a situation where I am trying to create two Cloud Functions namely CF1 & CF2 and I have one Cloud Scheduler. Both cloud functions are having authenticated invocation enabled. My flow is Cloud Scheduler will trigger CF1. On completion of CF1, the CF1 will trigger CF2 as a http call. I have referred Cannot invoke Google Cloud Function from GCP Scheduler to access authenticated CF1 from Cloud Scheduler and able to access CF1. But I am getting problem when accessing CF2 from CF1. The CF1 does not trigger CF2 and also not giving any error message. Do we need to follow any other technique when accessing authenticated Cloud Function from another authenticated Cloud Function.
CF1 code:
import json
import logging
from requests_futures.sessions import FuturesSession
def main(request):
# To read parameter values from request (url arguments or Json body).
raw_request_data = request.data
string_request_data = raw_request_data.decode("utf-8")
request_json: dict = json.loads(string_request_data)
request_args = request.args
if request_json and 'cf2_endpoint' in request_json:
cf2_endpoint = request_json['cf2_endpoint']
elif request_args and 'cf2_endpoint' in request_args:
cf2_endpoint = request_args['cf2_endpoint']
else:
cf2_endpoint = 'Invalid endpoint for CF2'
logger = logging.getLogger('test')
try:
session = FuturesSession()
session.get("{}".format(cf2_endpoint))
logger.info("First cloud function executed successfully.")
except RuntimeError:
logger.error("Exception occurred {}".format(RuntimeError))
CF2 code:
import logging
def main(request):
logger = logging.getLogger('test')
logger.info("second cloud function executed successfully.")
Current output logs:
First cloud function executed successfully.
Expected output logs:
First cloud function executed successfully.
second cloud function executed successfully.
Note: Same flow is working if I use unauthenticated access to the both cloud functions.
Two things are happening here:
You're not using request-futures entirely correctly. Since the request is made asynchronously, you need to block on the result before the function implicitly returns, otherwise it might return before your HTTP request completes (although it probably is in this example):
session = FuturesSession()
future = session.get("{}".format(cf2_endpoint))
resp = future.result() # Block on the request completing
The request you're making to the second function is not actually an authenticated request. Outbound requests from a Cloud Function are not authenticated by default. If you looked at what the actual response is above, you would see:
>>> resp.status_code
403
>>> resp.content
b'\n<html><head>\n<meta http-equiv="content-type" content="text/html;charset=utf-8">\n<title>403 Forbidden</title>\n</head>\n<body text=#000000 bgcolor=#ffffff>\n<h1>Error: Forbidden</h1>\n<h2>Your client does not have permission to get URL <code>/function_two</code> from this server.</h2>\n<h2></h2>\n</body></html>\n'
You could jump through a lot of hoops to properly authenticate this request, as detailed in the docs: https://cloud.google.com/functions/docs/securing/authenticating#function-to-function
However, a better alternative would be to make your second function a "background" function and invoke it via a PubSub message published from the first function instead:
from google.cloud import pubsub
publisher = pubsub.PublisherClient()
topic_name = 'projects/{project_id}/topics/{topic}'.format(
project_id=<your project id>,
topic='MY_TOPIC_NAME', # Set this to something appropriate.
)
def function_one(request):
message = b'My first message!'
publisher.publish(topic_name, message)
def function_two(event, context):
message = event['data'].decode('utf-8')
print(message)
As long as your functions have the permissions to publish PubSub messages, this avoids the need to add authorization to the HTTP requests, and also ensures at-least-once delivery.
Google Cloud Function provide REST API interface what include call method that can be used in another Cloud Function HTTP invokation.
Although the documentation mention using Google-provided client libraries there is still non one for Cloud Function on Python.
And instead you need to use general Google API Client Libraries. [This is the python one].3
Probably, the main difficulties while using this approach is an understanding of authentification process.
Generally you need provide two things to build a client service:
credentials ans scopes.
The simpliest way to get credentials is relay on Application Default Credentials (ADC) library. The rigth documentation about that are:
https://cloud.google.com/docs/authentication/production
https://github.com/googleapis/google-api-python-client/blob/master/docs/auth.md
The place where to get scopes is the each REST API function documentation page.
Like, OAuth scope: https://www.googleapis.com/auth/cloud-platform
The complete code example of calling 'hello-world' clound fucntion is below.
Before run:
Create default Cloud Function on GCP in your project.
Keep and notice the default service account to use
Keep the default body.
Notice the project_id, function name, location where you deploy function.
If you will call function outside Cloud Function environment (locally for instance) setup the environment variable GOOGLE_APPLICATION_CREDENTIALS according the doc mentioned above
If you will call actualy from another Cloud Function you don't need to configure credentials at all.
from googleapiclient.discovery import build
from googleapiclient.discovery_cache.base import Cache
import google.auth
import pprint as pp
def get_cloud_function_api_service():
class MemoryCache(Cache):
_CACHE = {}
def get(self, url):
return MemoryCache._CACHE.get(url)
def set(self, url, content):
MemoryCache._CACHE[url] = content
scopes = ['https://www.googleapis.com/auth/cloud-platform']
# If the environment variable GOOGLE_APPLICATION_CREDENTIALS is set,
# ADC uses the service account file that the variable points to.
#
# If the environment variable GOOGLE_APPLICATION_CREDENTIALS isn't set,
# ADC uses the default service account that Compute Engine, Google Kubernetes Engine, App Engine, Cloud Run,
# and Cloud Functions provide
#
# see more on https://cloud.google.com/docs/authentication/production
credentials, project_id = google.auth.default(scopes)
service = build('cloudfunctions', 'v1', credentials=credentials, cache=MemoryCache())
return service
google_api_service = get_cloud_function_api_service()
name = 'projects/{project_id}/locations/us-central1/functions/function-1'
body = {
'data': '{ "message": "It is awesome, you are develop on Stack Overflow language!"}' # json passed as a string
}
result_call = google_api_service.projects().locations().functions().call(name=name, body=body).execute()
pp.pprint(result_call)
# expected out out is:
# {'executionId': '3h4c8cb1kwe2', 'result': 'It is awesome, you are develop on Stack Overflow language!'}

Python Google API get() vs get_media()

I am attempting to use Python to access some files stored on a Google Team Drive. I have figured out the functionality to download files, but am running into a mysterious issue when attempting to get metadata
If I execute the following:
myfileid = 'thegooglefileid'
self.service = build('drive', 'v3', http=creds.authorize(Http()))
data = self.service.files().get_media(fileId=myfileid).execute()
meta = self.service.files().get(fileId=myfileid,fields="*").execute()
"data" returns as expected allowing me the info to download my files as I expect. "meta" returns with a HttpError 404 indicating it can not find the file (which it in fact found in the line above).
I know this issue can occur with incorrectly set authorization, but my authorization is set such that I expect this to work
SCOPES = ['https://www.googleapis.com/auth/drive.metadata.readonly',
'https://www.googleapis.com/auth/drive',
'https://www.googleapis.com/auth/drive.file']
Any ideas why the file is visible to one part of the API but not the other ?
In this case, an important point is this is accessing TeamDrive. In the specific case of the get call in which "meta" is retrieved, the API needs to be informed that it is using a TeamDrive
The following code works once I figured this out
myfileid = 'thegooglefileid'
self.service = build('drive', 'v3', http=creds.authorize(Http()))
data = self.service.files().get_media(fileId=myfileid).execute()
meta = self.service.files().get(fileId=myfileid,fields="*",supportsTeamDrives=True).execute()
Interestingly, get requires this parameter while get_media functions fine without it

Python Mendeley SDK API returns error in authentication

I have a mendeley account which I am using from their online version. I created a userid and client secret, saved it in config.yml file from and using it to authenticate. I am using the below code available on their website
import yaml
from mendeley import Mendeley
with open('config.yml') as f:
config = yaml.load(f)
REDIRECT_URI = 'http://localhost:5000/oauth'
mendeley = Mendeley(config['clientId'], config['clientSecret'], REDIRECT_URI)
auth = mendeley.start_client_credentials_flow()
session = auth.authenticate()
This code works fine and I got not errors. But when I am trying to access data using the commands in the example it throws error. For example
>> print (session.profiles.me.display_name)
mendeley.exception.MendeleyApiException: The Mendeley API returned an error (status: 400, message: No userid found in auth token)
>> for document in session.documents.iter():
print document.title
mendeley.exception.MendeleyApiException: The Mendeley API returned an error (status: 403, message: Access to this document is not allowed)
I am stuck here and do not know how to access the data or articles I have on mendeley fom its API. Any help will be highly appreciated.
You are using the Client Credentials flow (a term from OAuth). Quoting from the docs:
This flow does not require the user to log in. However, it only
provides access to a limited set of resources (the read-only Mendeley
Catalog of crowd sourced documents).
The two calls from your questions are private (user) information, and will therefor fail.
On the other hand, the following should work:
print session.catalog.by_identifier(doi='10.1371/journal.pmed.0020124', view='stats').reader_count
If you want to access private information, you will need users to log in. This question should help.

Categories

Resources