Python - HttpError when using google drive API - python-3.x

I am using python 3.4 to leverage the Google API to access and read files from a users google drive. If a user has already used the app before they should have a credentials file so I was hoping to be able to test if the credentials are still valid by attempting to list the files on the users drive. The idea being if this errors then the app knows it needs to ask for access again.
After a lot of searching I've tried to piece together code from the following Examples:
Google API commands
Google Example
I currently have the following pieces of code:
import httplib2
from apiclient.discovery import build
from oauth2client.file import Storage
from oauth2client.client import AccessTokenRefreshError
from oauth2client.client import OAuth2WebServerFlow
def getAccess():
flow = OAuth2WebServerFlow(client_id, client_secret, scope, redirect_uri="urn:ietf:wg:oauth:2.0:oob")
auth_uri = flow.step1_get_authorize_url()
print("Please go to the following webpage and copy and paste the access key onto the command line:\n" + auth_uri + "\n")
code = input("Please enter your access code:\n")
credentials = flow.step2_exchange(code)
storage.put(credentials)
client_id = MYCLIENT_ID
client_secret = MYCLIENT_SECRET
scope = "https://www.googleapis.com/auth/drive"
storage = Storage('~/credentials.dat')
credentials = storage.get()
if credentials is None or credentials.invalid:
getAccess()
else:
try:
http = httplib2.Http()
http = credentials.authorize(http)
service = build('drive', 'v2', http=http)
param = {}
service.files().list(**param).execute()
except:
getAccess()
However the service.files().list(**param).execute() line produces the following error message:
Traceback (most recent call last):
File "GoogleAuth.py", line 64, in <module>
service.files().list(**param).execute()
File "C:\Anaconda3\lib\site-packages\oauth2client\util.py", line 137, in positional_wrapper
return wrapped(*args, **kwargs)
File "C:\Anaconda3\lib\site-packages\googleapiclient\http.py", line 729, in execute
raise HttpError(resp, content, uri=self.uri)
googleapiclient.errors.HttpError
I've tried playing around with a few different combinations such as:
service.files().list().execute()
service.apps().list().execute()
However I still get the same error message. Any idea what's going on ?

Issue was that
service = build('drive', 'v2')
Should have been
service = build('drive', 'v2', http=http)

Related

Youtube data api error when changing video title

why did i get this error on the script given below:
Traceback (most recent call last):
File "C:\Path\YoutubeApi\main.py", line 54, in <module>
main()
File "C:\Path\YoutubeApi\main.py", line 49, in main
response = request.execute()
File "C:\Users\$user\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.9_qbz5n2kfra8p0\LocalCache\local-packages\Python39\site-packages\googleapiclient\_helpers.py", line 131, in positional_wrapper
return wrapped(*args, **kwargs)
File "C:\Users\$user\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.9_qbz5n2kfra8p0\LocalCache\local-packages\Python39\site-packages\googleapiclient\http.py", line 937, in execute
raise HttpError(resp, content, uri=self.uri)
googleapiclient.errors.HttpError: <HttpError 400 when requesting https://youtube.googleapis.com/youtube/v3/videos?part=snippet&alt=json returned "The <code>snippet.categoryId</code> property specifies an invalid category ID. Use the <code>videoCategories.list</code> method to retrieve supported categories.". Details: "[{'message': 'The <code>snippet.categoryId</code> property specifies an invalid category ID. Use the <code>videoCategories.list</code> method to retrieve supported categories.', 'domain': 'youtube.video', 'reason': 'invalidCategoryId', 'location': 'body.snippet.categoryId', 'locationType': 'other'}]">
Main.py
import os
import pickle
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
from googleapiclient.discovery import build
def main():
credentials = None
# token.pickle stores the user's credentials from previously successful logins
if os.path.exists('token.pickle'):
print('Loading Credentials From File...')
with open('token.pickle', 'rb') as token:
credentials = pickle.load(token)
if not credentials or not credentials.valid:
if credentials and credentials.expired and credentials.refresh_token:
print('Refreshing Access Token...')
credentials.refresh(Request())
else:
print('Fetching New Tokens...')
flow = InstalledAppFlow.from_client_secrets_file(
'client_secrets.json',
scopes=[
'https://www.googleapis.com/auth/youtube.force-ssl'
]
)
flow.run_local_server(port=8080, prompt='consent',
authorization_prompt_message='')
credentials = flow.credentials
# Save the credentials for the next run
with open('token.pickle', 'wb') as f:
print('Saving Credentials for Future Use...')
pickle.dump(credentials, f)
youtube = build('youtube','v3',credentials=credentials)
request = youtube.videos().update(
part = 'snippet',
body={
"id": "OSxK-tscmVA",
"snippet":{
"title":"It's changed",
}
}
)
response = request.execute()
print(response)
if __name__ == '__main__':
main()
Script Explanation:
What the script does is that if there's no file named token.pickle it will ask the user to authorize the application and the script will store the user credentials in token.pickle file so that the user doesn't have to authorize the application on every single run and the part 2 of the script changes my YouTube video's title.
Unfortunately, you seem not inclined to make use of the official specification of the Videos.update API endpoint:
The request body of your call to the endpoint needs to specify the video category ID too for to be accepted by the API back-end.
Note that the update_video.py sample code from Google -- that I indicated you at the beginning of your series of posts as valuable source of information -- has all these required things in it.
Again, please acknowledge that you have to absorb (i.e. understand) that code. There's no other way than going through reading carefully the official documentation.

Sending my first message on the slack API

I registered my first app, and it looks like this:
All of the fields are populated below the screen shot.
Now, I have some basic python code using the examples found on their repo.
I create the following test script:
import traceback
app_id = 'FAKE.VALUE'
client_id = 'FAKE.VALUE'
client_secret = 'FAKE.VALUE'
signin_secret = 'FAKE.VALUE'
verification_token = 'FAKE.VALUE'
items = locals()
import os
import slack
items = locals().copy()
for k in items:
if '__' not in k:
val = items[k]
try:
client = slack.WebClient(token=val)
response = client.chat_postMessage(
channel='CE476K9HT',
text='Hello-----' + str(val))
print(response)
except:
print(k)
traceback.print_exc()
print('-'*50)
But all of the responses I get say:
The server responses with: {'ok':False,'error':'invalid_auth'}
For some reason, is it necessary to use path variables?
It is unclear to me what type of auth is required here.
After doing what Erik suggested,
I have a xoxp code and registered the redirect url to http://localhost.
and added the following scopes:
and updated my code to look like:
oauth_token ='xoxp-*****************'
import os
import slack
items = locals().copy()
client = slack.WebClient(token=oauth_token)
response = client.chat_postMessage(
channel='my_channel_id',
text='Hello-----')
I got my channel ID from the url:
https://app.slack.com/client/FOO/my_channel_id
When I run my code, I get the following back:
Traceback (most recent call last):
File "/home/usr/git/slack_messaging/slack_messaging.py", line 20, in <module>
text='Hello-----')
File "/home/usr/git/python-slackclient/slack/web/client.py", line 382, in chat_postMessage
return self.api_call("chat.postMessage", json=kwargs)
File "/home/usr/git/python-slackclient/slack/web/base_client.py", line 172, in api_call
return self._event_loop.run_until_complete(future)
File "/home/usr/anaconda2/envs/beer/lib/python3.7/asyncio/base_events.py", line 573, in run_until_complete
return future.result()
File "/home/usr/git/python-slackclient/slack/web/base_client.py", line 241, in _send
return SlackResponse(**{**data, **res}).validate()
File "/home/usr/git/python-slackclient/slack/web/slack_response.py", line 176, in validate
raise e.SlackApiError(message=msg, response=self)
slack.errors.SlackApiError: The request to the Slack API failed.
The server responded with: {'ok': False, 'error': 'missing_scope', 'needed': 'chat:write:user', 'provided': 'admin,identify'}
Process finished with exit code 1
You need two things to make your script work.
1. OAuth token
You need a valid OAuth token and provide it when initializing the Slack Client:
client = slack.WebClient(token="xoxb-xxx")
To get a token you need to install your Slack app to a workspace. You can do that on the app management page under "Install App". Your OAuth token will also be displayed on that page once you installed it.
2. Permissions
Your Oauth Token / Slack app needs to have the permission to post messages. On the app management pages go to "OAuth & permission" and add the needed permission to your app:. e.g. chat:write:user for user tokens.
Note that you need to reinstall your app every time to add a permission.

Access spreadsheets inside shared folder

Overview
I am creating a folder and spreadsheets using API I can access all files inside that folder using google spreadsheet API
(problem) But then I upload the file from the front, Now this file can not be accessed via spreadsheet API, I have given full permission(owner) to the user.
So basically the problem is that I want to let other user drop spreadsheets in my shared drive and want to access these files via python API
error message "This operation is not supported for this document"
JSON RESPONSE
{
"error": {
"code": 403,
"message": "The request is missing a valid API key.",
"status": "PERMISSION_DENIED"
}
}
Background:
I am using python to create folders and sheets, and I wanted to share the folder for other users to update the spreadsheet files so I gave permissions to other users, but when they dropped the files in the drive I can access the file metadata but not the contents of the spreadsheets
Relevant Code:
`SCOPES =
['https://www.googleapis.com/auth/drive.appdata','https://www.googleapis.com/auth/spreadsheets', 'https://www.googleapis.com/auth/drive']
sheet_service = build('sheets', 'v4', credentials=get_Service())
drive_service = build('drive', 'v3',credentials=get_Service())
def get_values(self,SAMPLE_SPREADSHEET_ID, SAMPLE_RANGE_NAME):
sheet = sheet_service.spreadsheets()
result = sheet.values().get(spreadsheetId=SAMPLE_SPREADSHEET_ID,
range=SAMPLE_RANGE_NAME).execute()`
Expected help > bypass these permission restrictions
I have noticed something when I drop the file the sheet id is different from when I create it with API or from google drive
{ "error": { "code": 403, "message": "The request is missing a valid API key.", "status": "PERMISSION_DENIED" } }
Means that you are not sending any authorization with your request. Your request needs to be authorized before you can do anything. Notice how the credentials have been added to the service variable in the code below.
from __future__ import print_function
import pickle
import os.path
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
# If modifying these scopes, delete the file token.pickle.
SCOPES = ['https://www.googleapis.com/auth/spreadsheets.readonly']
# The ID and range of a sample spreadsheet.
SAMPLE_SPREADSHEET_ID = '1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms'
SAMPLE_RANGE_NAME = 'Class Data!A2:E'
def main():
"""Shows basic usage of the Sheets API.
Prints values from a sample spreadsheet.
"""
creds = None
# The file token.pickle stores the user's access and refresh tokens, and is
# created automatically when the authorization flow completes for the first
# time.
if os.path.exists('token.pickle'):
with open('token.pickle', 'rb') as token:
creds = pickle.load(token)
# If there are no (valid) credentials available, let the user log in.
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file(
'credentials.json', SCOPES)
creds = flow.run_local_server(port=0)
# Save the credentials for the next run
with open('token.pickle', 'wb') as token:
pickle.dump(creds, token)
service = build('sheets', 'v4', credentials=creds)
# Call the Sheets API
sheet = service.spreadsheets()
result = sheet.values().get(spreadsheetId=SAMPLE_SPREADSHEET_ID,
range=SAMPLE_RANGE_NAME).execute()
values = result.get('values', [])
if not values:
print('No data found.')
else:
print('Name, Major:')
for row in values:
# Print columns A and E, which correspond to indices 0 and 4.
print('%s, %s' % (row[0], row[4]))
if __name__ == '__main__':
main()
Code ripped from Python quickstart
Also note that you cant create a folder in Google drive with the Sheets api you will need to use the Google drive api to create files. Sheets just lets you edit them.
converting files
When you upload the files to Google drive you need to convert them to sheet at that time Import doc types
file_metadata = {
'name': 'My Report',
'mimeType': 'application/vnd.google-apps.spreadsheet'
}
media = MediaFileUpload('files/report.csv',
mimetype='text/csv',
resumable=True)
file = drive_service.files().create(body=file_metadata,
media_body=media,
fields='id').execute()
print 'File ID: %s' % file.get('id')

File "quickstart.py", line 9, in <module>

I am new in Python and trying use Google Drive Apis, but facing this issue. Error I am getting after running python quickstart.py
Traceback (most recent call last):
File "quickstart.py", line 9, in <module>
creds = store.get()
File "/usr/local/lib/python3.6/site-packages/oauth2client/client.py", line 407, in get
return self.locked_get()
File "/usr/local/lib/python3.6/site-packages/oauth2client/file.py", line 54, in locked_get
credentials = client.Credentials.new_from_json(content)
File "/usr/local/lib/python3.6/site-packages/oauth2client/client.py", line 302, in new_from_json
module_name = data['_module']
KeyError: '_module'
I have generated client_secret.json file as per the Python Quickstart tutorial.
All the file are in the same directory as that of quickstart.py.
Here is how my quickstart.py file looks.
from __future__ import print_function
from apiclient.discovery import build
from httplib2 import Http
from oauth2client import file, client, tools
# Setup the Drive v3 API
SCOPES = 'https://www.googleapis.com/auth/drive.metadata.readonly'
store = file.Storage('credentials.json')
creds = store.get()
if not creds or creds.invalid:
flow = client.flow_from_clientsecrets('client_secret.json', SCOPES)
creds = tools.run_flow(flow, store)
service = build('drive', 'v3', http=creds.authorize(Http()))
# Call the Drive v3 API
results = service.files().list(
pageSize=10, fields="nextPageToken, files(id, name)").execute()
items = results.get('files', [])
if not items:
print('No files found.')
else:
print('Files:')
for item in items:
print('{0} ({1})'.format(item['name'], item['id']))
UPDATE:
I checked and it turns out that credentials.json file is auto-generated on the first run and for some reason, this is not happening.
KeyError: '_module'
This key _module is suppose to be present in credentials.json file and that is why this error is thrown. Not sure what is missing. Can someone please tell me how to resolve this issue.
Similar issue here Try to remove both files from your directory - "credentials.json" and "client_secret.json". Then re-generate your credentials and re-create "client_secret.json", this worked for me.

How do you programmatically get a list of Google Forms (with ids) using Google Forms API?

I would like to know if it is possible to get the list of a Google Drive user's Forms (not the Docs, Spreadsheets, ...) using the Forms Service or the Google Drive API.
The reason why I am not sure is because there is no indication on how to do this on the Forms Service page, and on the Drive API's page the indications are very general. They don't take into account the fact that I will be using a OAuth2 token with a 'forms' scope and not a 'drive' scope.
/* List all Google Forms in your Google Drive */
function getGoogleFormsList() {
var files = DriveApp.getFilesByType(MimeType.GOOGLE_FORMS);
while (files.hasNext()) {
var file = files.next();
Logger.log("%s %s %s", file.getName(), file.getId(), file.getUrl());
}
}
Python solution:
from __future__ import print_function
import os.path
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError
# If modifying these scopes, delete the file token.json.
SCOPES = ['https://www.googleapis.com/auth/drive.metadata.readonly']
def main():
"""Shows basic usage of the Drive v3 API.
Prints the names and ids of the first 10 files the user has access to.
"""
creds = None
# The file token.json stores the user's access and refresh tokens, and is
# created automatically when the authorization flow completes for the first
# time.
if os.path.exists('token.json'):
creds = Credentials.from_authorized_user_file('token.json', SCOPES)
# If there are no (valid) credentials available, let the user log in.
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file(
'client_secrets.json', SCOPES)
creds = flow.run_local_server(port=0)
# Save the credentials for the next run
with open('token.json', 'w') as token:
token.write(creds.to_json())
try:
page_token = None
drive_service = build('drive', 'v3', credentials=creds)
while True:
response = drive_service.files().list(
# You can use MIME types to filter query results
q="mimeType='application/vnd.google-apps.form'",
spaces='drive',
fields='nextPageToken, files(id, name)',
pageToken=page_token
).execute()
for file in response.get('files', []):
# Process change
print ('Found file: %s (%s)' % (file.get('name'), file.get('id')))
page_token = response.get('nextPageToken', None)
if page_token is None:
break
except HttpError as error:
# TODO(developer) - Handle errors from drive API.
print(f'An error occurred: {error}')
if __name__ == '__main__':
main()

Resources