Downloading and uploading files from OneDrive using MS Drive API - python

I've created an Azure app to update OneDrive files automatically via Python. I want it to enter someone else's drive and download files from or upload files there. However, I'm struggling forming the correct request link.
I'm currently doing something like this:
r = requests.post(f"https://graph.microsoft.com/v1.0/me/drive", headers = {"Authorization": "Bearer "+at, "content-type":"Application/Json"}) #at = authorization token
and get the error
'{\r\n "error": {\r\n "code": "BadRequest",\r\n "message": "Empty Payload. JSON content expected.",\r\n "innerError": {\r\n "date": "2021-02-01T07:52:02",\r\n "request-id": "6d159ace-252a-41a3-8805-dad6cd633348",\r\n "client-request-id": "6d159ace-252a-41a3-8805-dad6cd633348"\r\n }\r\n }\r\n}'
I'd like to enter one drive with link like this: https://xxx-my.sharepoint.com/personal/someemail_xxx_ru/_layouts/15/onedrive.aspx
How should I form the request?

You just sent a post request to the endpoint, but in this request, you did not specify other required information, such as directory, source file object, etc.These informations should be included in the json carried in the request.By the way, your endpoint also seems to be missing your onddrive path
Here's exampel document
you can using python like this:
requests.post(
'<endpoint>',
headers={'Authorization': 'Bearer ' + <token>, 'Content-type': 'application/json'},
data=json.dumps(<json_information>).json())
Fill in the <> with your information

Related

Get Request to Azure DevOps with Personal Access Token (PAT) using Python

I'm trying to make a get request to Azure DevOps.
I have the URL and the Personal_Access_Token. The URL was created following these intructions https://learn.microsoft.com/en-us/rest/api/azure/devops/git/items/get?view=azure-devops-rest-6.1&tabs=HTTP#definitions , and it is working fine in the browser. It is possible to see the information of the file that I'm targeting.
However, when I execute the request in python:
import requests
headers = {
'Authorization': 'Bearer myPAT',
}
response = requests.get('exampleurl.com/content', headers=headers)
I'm getting the 203 response...
I have also try other options following this link Python requests library how to pass Authorization header with single token without success. Including these headers:
personal_access_token_encoded = base64.b64encode(personal_access_token.encode('utf-8')).decode('utf-8')
headers={'Authorization': 'Basic '+personal_access_token_encoded}
headers={'Authorization': 'Basic '+personal_access_token}
But in both cases still having the same response.
For sure I'm not considering something. What could be missing?
For Azure DevOps API, you need to use Basic Auth instead of Baerear, providing only the PAT token encoded in base64.
Hi error feedback 203 is about your invalid token.
So what is the authorization type of your request call?
For pat headers = {'Authorization': 'Basic pat'}
For bearer token headers = {'Authorization': 'Bearer MYREALLYLONGTOKENIGOT'}
You could put your rest api in postman and click the code button at the right-side bar to overview the rest api into different script.

Upload a file using python requests

I have a problem trying to upload a file into an API. In the swagger UI I have not problem uploading an excel file manually. When I try tu upload using request, I recive a 415 error (Invalid format of my file). Here is a simple code of that post request:
headers = {
'Authorization':"bearer "+ CLIENT.token,
'Content-Type': 'form-data'
}
files = [('file', open(path_to_file, 'rb'))]
response = requests.post(api_url,
files=files,
headers=headers)
My response has status code 415, I dont Know what is happening. When I used the swagger, I inspected the newtwork browser, and I saw this header in the request
Content-Type: multipart/form-data; boundary=----WebKitFormBoundarywkrdGd3ROh0GkfsW
But, I don't know what is the term "boundary", and if I pass this header manually into the requests, the API throws a 500.
The server is saying that your Content-Type is wrong. If you're uploading a .xls file use:
'Content-Type': 'application/vnd.ms-excel'
If you're uploading a .xlsx file use:
'Content-Type': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'

Generating a downloadable link for a private file uploaded in the Google drive

Is it possible to generate the downloadable link for a private file uploaded in the google drive?
I have tried with the files API and generated the 'webContent Link', but it is accessible only to the owner and the shared users.
(Expecting a public link that can be shared with anyone)
def file_info_drive(access_token, file_id):
headers = {'Authorization': 'Bearer ' + access_token, "content-type": "application/json"}
response = requests.get('https://www.googleapis.com/drive/v2/files/{file_id}', headers=headers)
response = response.json()
link = response['webContentLink']
return link
You want to make anyone download the file using webContentLink.
You want to use Drive API v2.
You want to achieve this using 'request' module with Python.
You have already been able to upload and download the file using Drive API.
If my understanding is correct, how about this modification?
Modification points:
In order to make anyone download the file using webContentLink, it is required to share publicly the file.
In this modified script, the file is publicly shared with the condition of {'role': 'reader', 'type': 'anyone', 'withLink': True}. In this case, the persons who know the URL can download the file.
Modified script:
When your script is modified, it becomes as follows.
def file_info_drive(access_token, file_id):
headers = {'Authorization': 'Bearer ' + access_token, "content-type": "application/json"}
# Using the following script, the file is shared publicly. By this, anyone can download the file.
payload = {'role': 'reader', 'type': 'anyone', 'withLink': True}
requests.post('https://www.googleapis.com/drive/v2/files/{file_id}/permissions', json=payload, headers=headers)
response = requests.get('https://www.googleapis.com/drive/v2/files/{file_id}', headers=headers)
response = response.json()
link = response['webContentLink']
return link
Note:
In this case, the POST method is used. So if an error for the scopes occurs, please add https://www.googleapis.com/auth/drive to the scope.
Reference:
Permissions: insert
If I misunderstood your question and this was not the direction you want, I apologize.

How to fix 'Unsupported media type" error on python POST request to Twilio

I have been following Twilio's Quickstart page for their Functions API.
I am stuck at the part where I am supposed to manually upload the Function JS file.
Their POST examples use cURL and node.js but I am using Python 3.6:
# Manually upload the subscription function file
upload_url = f'https://serverless-upload.twilio.com/v1/Services/{sub_service_sid}/Functions/{sub_function_sid}/Versions'
function_request = requests.post(
upload_url,
files = {'subscription_function_file': open('subscriptionFunction.js', 'rb')},
auth = (account_sid, auth_token),
headers = {
'content-type': 'application/javascript',
'path': '/subscription-function',
'visibility': 'public'
}
)
In both examples, they declare the content type as application/javascript. However, I get this error when I do the same:
{"status":415,"message":"Unsupported media type","detail":"The server does not support the media type transmitted in the request.","code":20415,"moreInfo":"https://www/twilio.com/docs/errors/20415"}
That URL throws a 404 so I went digging in Twilio's Error Dictionary but that code is not listed. Furthermore, application/javascript is absent from their supported media types page.
Am I uploading the file incorrectly? Or is their tutorial wrong?
Twilio developer evangelist here.
I think you may have translated some of the curl request to the wrong parts of a request made with requests and I think this is causing the issue. You don't want to set the request type to be application/javascript that wants to be the type of the file you are uploading. You can set this as part of the files tuple.
You don't want to send the other bits of data, Path and Visibility as headers either, they should be part of the data so they become part of the request body.
Try something like this instead:
upload_url = f'https://serverless-upload.twilio.com/v1/Services/{sub_service_sid}/Functions/{sub_function_sid}/Versions'
files = { 'Content': ('subscriptionFunction.js', open('subscriptionFunction.js', 'rb'), 'application/javascript') }
function_request = requests.post(
upload_url,
files = files,
auth = (account_sid, auth_token),
data = {
'Path': '/subscription-function',
'Visibility': 'public'
}
)
Let me know if that helps.

Azure AD: "scope" Property Missing from Access Token Response Body

I am writing a Python script to edit an Excel spreadsheet that lives in SharePoint. We have Office 365 for Business. I am using Microsoft Graph API.
I registered the Python app with Microsoft Azure Active Directory (AD) and added the following 2 (app-only) permissions for Graph API: (1) Read and write files in all site collections (preview) and (2) Read directory data. (I had our company administrator register the app for me.)
My Python script uses the requests library to send REST requests:
import json
import requests
MSGRAPH_URI = 'https://graph.microsoft.com'
VERSION = 'v1.0'
def requestAccessToken(tenant_id, app_id, app_key):
MSLOGIN_URI = 'https://login.microsoftonline.com'
ACCESS_TOKEN_PATH = 'oauth2/token'
GRANT_TYPE = 'client_credentials'
endpoint = '{0}/{1}/{2}'.format(MSLOGIN_URI, tenant_id, ACCESS_TOKEN_PATH)
headers = {'Content-Type': 'Application/Json'}
data = {
'grant_type': GRANT_TYPE,
'client_id': app_id,
'client_secret': app_key,
'resource': MSGRAPH_URI
}
access_token = response.json()['access_token']
return access_token
def getWorkbookID(access_token, fileName):
endpoint = '{0}/{1}/me/drive/root/children'.format(MSGRAPH_URI, VERSION)
headers = {
'Content-Type': 'Application/Json',
'Authorization': 'Bearer {}'.format(access_token)
}
response = requests.get(endpoint, headers=headers)
print response.text
assert response.status_code == 200
items = response.json()['value']
workbook_id = None
for item in items:
if item['name'] == fileName:
workbook_id = item['id']
return workbook_id
access_token = requestAccessToken(TENANT_ID, APP_ID, APP_KEY)
workbook_id = getWorkbookID(access_token, WORKBOOK_FILENAME)
The Python app successfully requests and receives an access_token from the Microsoft server. The access token starts like this
eyJ0eXAiOiJKV1QiLCJub25jZSI6...
Then it requests a list of my files in getWorkbookID():
GET https://graph.microsoft.com/v1.0/me/drive/root/children
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJub25jZSI6...
This is the response to that REST request:
{
"error": {
"code": "InternalServerError",
"message": "Object reference not set to an instance of an object.",
"innerError": {
"request-id": "aa97a822-7ac5-4986-8ac0-9852146e149a",
"date": "2016-12-26T22:13:54"
}
}
}
Note that I successfully get a list of my files when I request it via Graph Explorer (https://graph.microsoft.io/en-us/graph-explorer).
EDIT:
Changed the title from "Microsoft Graph API Returns Object reference not set to an instance of an object" to "Azure AD "scope" Missing from Access Token Response".
Changed the "me" in the uri of the GET request to "myOrganization", after reading this: graph.microsft.io/en-us/docs/overview/call_api
That is,
GET https://graph.microsoft.com/v1.0/myOrganization/drive/root/children
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJub25jZSI6...
Now get the following response.
{
"error": {
"code": "AccessDenied",
"message": "Either scp or roles claim need to be present in the token.",
"innerError": {
"request-id": "bddb8c51-535f-456b-b43e-5cfdf32bd8a5",
"date": "2016-12-28T22:39:25"
}
}
}
Looking at an example in graph.microsoft.io/en-us/docs/authorization/app_authorization, I see that the access token response body contains a "scope" property that lists the permissions granted for the app during the app's registration. However, the access token response I receive from the server does not have the "scope" property. Here is what my access token response looks like.
{
"token_type":"Bearer",
"expires_in":"3599",
"ext_expires_in":"0",
"expires_on":"1482968367",
"not_before":"1482964467",
"resource":"https://graph.microsoft.com",
"access_token":"eyJ0eXAiOiJKV..."
}
Questions:
I had the administrator register the app in Azure AD and check the boxes for the Microsoft Graph API application permissions needed. Apparently that is not enough. What else is needed? Why are the permissions not in the access token response body?
What is the correct URI for the GET request? Is "MyOrganization" the correct value in the URI?
Thanks all for your responses. After more research, I found the answer to my question.
The original problem: "Microsoft Graph API Returns Object reference not set to an instance of an object"
The request
GET https://graph.microsoft.com/v1.0/me/drive/root/children
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJub25jZSI6...
gets the response
{
"error": {
"code": "InternalServerError",
"message": "Object reference not set to an instance of an object.",
"innerError": {
"request-id": "aa97a822-7ac5-4986-8ac0-9852146e149a",
"date": "2016-12-26T22:13:54"
}
}
}
As #SriramDhanasekaran-MSFT noted, /me refers to the currently signed-in user. In our case, we do not have a signed-in user, so we cannot use /me. Instead, we can either use /myOrganization or nothing, it is optional.
The updated problem: "Azure AD "scope" Property Missing from Access Token Response"
The updated (replaces /me with /myOrganization) request
GET https://graph.microsoft.com/v1.0/myOrganization/drive/root/children
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJub25jZSI6...
gets the response
{
"error": {
"code": "AccessDenied",
"message": "Either scp or roles claim need to be present in the token.",
"innerError": {
"request-id": "bddb8c51-535f-456b-b43e-5cfdf32bd8a5",
"date": "2016-12-28T22:39:25"
}
}
}
As #SriramDhanasekaran-MSFT and #DanKershaw-MSFT mentioned, the reason why the access_token response was missing the scope property is that the permissions had not been "granted" in Azure AD by the administrator.
However, the solution that #SriramDhanasekaran-MSFT provided:
https://login.microsoftonline.com/common/oauth2/authorize?client_id=&response_type=code&redirect_uri=http://localhost&resource=https://graph.microsoft.com&prompt=consent
doesn't help me because my app doesn't have a redirect uri. The solution to granting the permissions is simpler than that: simply have the administrator login in to Azure AD and click the "Grant Permissions" link to grant the permissions.
Additionally, /myOrganization/driver/root/children lists the contents of the administrator's drive. As #PeterPan-MSFT noted, to list a different user's drive, replace /myOrganization with /users/<user-id>.
Success:
My application can now edit my Excel spreadsheets online, without the intervention of a human user. Contrary to what #PeterPan-MSFT stated, this is possible with Graph API and there is no need to download the Excel spreadsheet and edit offline.
Summary:
There were two problems: (1) using /me and (2) the application permissions had not been granted in Azure AD by the administrator.
Since client_credential token flow is being used (i.e., there is no authenticated user context), any request with /me is not valid as /me refers to current signed-in user. Please try with delegated token if you to access files in user's drive.
To access root drive in Sharepoint, request url is /drive/root/children (myorganization is optional).
With regards to missing claims, admin has to consent the app. You can force consent by asking the admin to access the below url (replace with that of your app's)
https://login.microsoftonline.com/common/oauth2/authorize?client_id=&response_type=code&redirect_uri=http://localhost&resource=https://graph.microsoft.com&prompt=consent
As #juunas said, the first error information below should be the NULL exception in .NET which be caused at the server end, but also not an API bug or a permission issue.
error": {
"code": "InternalServerError",
"message": "Object reference not set to an instance of an object.",
You can refer to the SO thread to know this case which seems to update for backend, please try again later.
To explain for the second error when you changed the option me to myOrganization in the Graph API url, as #SriramDhanasekaran-MSFT, it's accessing files for the current user or a specified user with <user-id> instead of me.
However, based on my understanding, you want to edit an Excel spreadsheet lived in SharePoint for your orgnization, it seems to be not suitable via using Graph APIs. Per my experience, it should be done via using the APIs of OneDrive for Business to copy the Excel file to local to edit & upload to overwrite it, please refer to the dev documents for OneDrive and try to use the API for drive resource.
Hope it helps.

Categories

Resources