Looking for best practice here. I am uploading a file to a folder, and then wanting to check if the json response is good using an if statement.
def upload_report(report,save_folder_id,http):
service = discovery.build('drive', 'v3', http=http)
head, tail = os.path.split(report)
file_metadata = {
'name' : tail,
'mimeType' : 'application/vnd.google-apps.document',
'parents' : [ folder_id ]
}
media = MediaFileUpload(report,mimetype='application/vnd.google-apps.document',resumable=True)
gfile = service.files().create(body=file_metadata,media_body=media,fields='id').execute()
So essentially, if the json response validates successful upload, return true, else, return false. What would be best practice to accomplish this?
Pardon such a rudimentary question, still new to using the api with python.
and this is why I don't like libraries!
The documentation should describe how errors are presented, ideally at https://developers.google.com/resources/api-libraries/documentation/drive/v2/python/latest/drive_v2.files.html#insert
To confirm if the insert succeeded, checking file for a string in the id property would suffice. Unfortunately that doesn't answer your next question which will be "if there is an error, how do I find out what it is, so I can deal with it correctly?".
Hopefully somebody who's used the Python lib can provide more info.
Related
I have a Json service file, and a service account that already accesses translate and sheets, but it will not access user lists. The result is either 400 showing its confused or 401 saying its not authorized. Examples are usually about client involved OAuth processes, where I need server to server. I have enabled that "Enable G Suite domain-wide delegation" feature on the service account too.
I read and tried the JWT method, but I get the same error responses.
https://developers.google.com/identity/protocols/oauth2/service-account#python_2
My goal is to call either one of these end points
https://www.googleapis.com/admin/directory/v1/users
https://www.googleapis.com/admin/directory/v1/users.readonly
Any direction would be greatly appreciated.
UPDATE1:
This is using the Jwt token approach which yields error 401.
with open(CLIENT_SECRET_FILE, "r+") as fh:
config = json.load(fh)
iat = time.time()
exp = iat + 3600
payload = {'iss': config['client_email'],
'sub': config['client_email'],
'aud': 'https://www.googleapis.com/',
'iat': iat,
'exp': exp}
additional_headers = {'kid': config['private_key_id']}
signed_jwt = jwt.encode(payload, config['private_key'], headers=additional_headers,
algorithm='RS256')
url = 'https://www.googleapis.com/admin/directory/v1/users'
headers = {"Authorization": "Bearer " + signed_jwt}
r = requests.get(url, headers=headers)
I have also tried
scopes = ['https://www.googleapis.com/auth/admin.directory.user']
credentials = ServiceAccountCredentials.from_json_keyfile_name(CLIENT_SECRET_FILE, scopes=scopes)
service = build('admin', 'directory_v1', credentials=credentials)
results = service.users().list().execute()
UPDATE2:
This link has great information and simple code to review. As much as I tried to avoid impersonation, the AdminSDK requires it. That makes integrations a bit awkward in my view. In addition, the issue I also faced was the Domain-Wide-Delegation screen in the Google Workspace Admin can get messed up. Deleting the entry and recreating it fixed the forever 403 error I kept getting no matter what I had tried.
https://gist.github.com/lewisrodgers/fa766ebd37c1397d3a014eb73805edd1
You need to incorporate into your code impersonation, so that the service account acts on behalf of the admin
Because only admins have authroization to access Resource: users.
For impersonation in Python you need to implement the additional line
delegated_credentials = credentials.with_subject('admin#example.org')
The link below has great information and simple code to review. As much as I tried to avoid impersonation, the AdminSDK requires it. That makes integrations a bit awkward in my view.
In addition, the issue I also faced was the Domain-Wide-Delegation screen in the Google Workspace Admin that messed up. After much digging over weeks, I found a simple solution of deleting and recreating the client entry in that screen. It fixed the never ending 403 error that hit every test I tried that should have worked and did for many others.
This seems to be the only API set by Google that requires impersonation, and is annoying when attempting to create a SaaS solution.
Really basic, trimmed examples, and decent article references.
https://gist.github.com/lewisrodgers/fa766ebd37c1397d3a014eb73805edd1
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
With file uploaded and file_id known:
media_body = MediaFileUpload(filepath, mimetype=mimetype)
body = {'name': os.path.basename(filepath), 'appProperties':{'my_key': 'my_value'}}
file = drive_service.files().create(body=body, media_body=media_body, fields='id, appProperties').execute()
file_id = file['id']
How to modify the file's appProperties using v3?
There is a Google Drive API v3 Migration post that could be used to get some idea on things not covered in the documentaion. This post's Trash / Update section talks about update functionality in Google Drive API v3.
But it is written in Java and not Python. It suggests of using an empty File object: File newContent = new File();
Another post this time for PHP mentions about update method and this empty File approach too: How to update file in google drive v3 PHP
I would appreciate if someone here would trough a couple of Python snippets to guide me in a right direction.
How about following sample? In order to update appProperties, you can use drive.files.update. The detail information is here.
Sample script :
body = {'appProperties': {'my_key': 'updated_my_value'}}
updated_file = drive_service.files().update(
body=body,
fileId="### file id ###",
fields='id, appProperties'
).execute()
If I misunderstand your question, I'm sorry.
My question is very similar to moment.insert from python code but Fausto has already managed to get further than me so I'm unable to apply that answer at this time.
My code looks like this, and my question is "how do I post to my g+ profile"
user = 'awarner#######.com'
key = open(keyFile, 'r').read()
credentials = SignedJwtAssertionCredentials(
'##########developer.gserviceaccount.com',
key,
scope='https://www.googleapis.com/auth/plus.login',
sub=user)
http = httplib2.Http()
http = credentials.authorize(http)
service = build(serviceName='plus', version='v1', http=http)
# Not really required here but these calls demonstrate that we are authenticated
searchResults = service.people().search(query='AlistairWarner').execute()
listMoments = service.moments().list(userId='me', collection='vault').execute()
moment = { "type" : "http://schemas.google.com/AddActivity",
"target" : {
"id" : "target-id-1",
"type" : "http://schemas.google.com/AddActivity",
"name" : "The Google+ Platform",
"description" : "A page that describes just how awesome Google+ is!",
"image" : "https://developers.google.com/+/plugins/snippet/examples/thing.png"
}
}
# This call fails with a "401 Unauthorized" error
insertResults = service.moments().insert(userId='me', collection='vault', body=moment).execute()
Just for the record I have confirmed that I have correctly delegated domain-wide authority to my service account for the necessary scopes - although I don't believe that this is really an authorization issue per se because the list call is working. I have also confirmed that my "App" appears on my g+ profile under Apps as mentioned by Prisoner in response to Fausto's question (referenced above).
Looking at the API reference page it does warn that you will get the 401 error that I am seeing. It, and other posts here on StackOverflow (I'd give the ref but not allowed to), say that you need to add the data-requestvisibleactions (again, can't give ref) attribute but I am struggling to see the relevance of the Sign-In Button that they describe attaching it to in my code - which has no UI, I just run it from the command line. So, requestvisibleactions does seem to be the best candidate, I just don't know how to translate that into something that I can use here.
Does anyone have a clue how to get this working?
You need to add this as part of your OAuth request credentials. So it might look something like
credentials = SignedJwtAssertionCredentials(
'##########developer.gserviceaccount.com',
key,
scope='https://www.googleapis.com/auth/plus.login',
requestvisibleactions='http://schemas.google.com/AddActivity',
sub=user)
I created a form and a simple server with google appengine with which to upload arbitrary file types to my google drive. The form fails to work for certain file types and just gives this error instead:
HttpError: <HttpError 400 when requesting https://www.googleapis.com/upload/drive/v1/files?alt=json returned "Unsupported content with type: application/pdf">
Aren't pdf files supported?
The appengine code that does the upload goes somewhat like this:
def upload_to_drive(self, filestruct):
resource = {
'title': filestruct.filename,
'mimeType': filestruct.type,
}
resource = self.service.files().insert(
body=resource,
media_body=MediaInMemoryUpload(filestruct.value,
filestruct.type),
).execute()
def post(self):
creds = StorageByKeyName(Credentials, my_user_id, 'credentials').get()
self.service = CreateService('drive', 'v1', creds)
post_dict = self.request.POST
for key in post_dict.keys():
if isinstance(post_dict[key], FieldStorage):#might need to import from cgi
#upload to drive and return link
self.upload_to_drive(post_dict[key]) #TODO: there should be error handling here
I've successfully used it for MS Office documents and images. It doesn't work for textfiles too and gives this error:
HttpError: <HttpError 400 when requesting https://www.googleapis.com/upload/drive/v1/files?alt=json returned "Multipart content has too many non-media parts">
I've tried unsetting the 'mimeType' value in the resource dict to let google drive set it automatically. I also tried unsetting the mime type value in the MediaInMemoryUpload constructor. Sadly, none of both worked.
It seems to me that you are using an old version of the Python client library and referring to Drive API v1, while Drive API v2 has been available since the end of June.
Please try updating your library and check the complete Python sample at https://developers.google.com/drive/examples/python.