I am trying to migrate a script from the Profiles API to the Directory API. Its purpose is to manage the visibility of certain Google Apps Domain users' contact information using the includeInGlobalAddressList attribute.
I can fetch the user objects, and am able to submit a patch request, but the change does not get made. The object returned from the patch method has the original value for includeInGlobalAddressList. There are some vague references to etags in the api documentation, but it's not clear to me if the etag is supposed to be included in the patch body or as an If-Match header. (if the answer to that is an If-Match header, then how am I supposed to pass that in using the python library?)
http = httplib2.Http()
http = credentials.authorize(http)
service = build("admin", "directory_v1", http=http)
usersvc=service.users()
d=usersvc.get(userKey=userkey, projection="basic",fields="etag,id,includeInGlobalAddressList").execute()
visible = d['includeInGlobalAddressList']
# logic to decide what change elided. this is "transition from visible to invisible contact"
pch=json.dumps({'includeInGlobalAddressList': False, 'etag': d['etag']})
print pch
res=usersvc.patch(userKey=userkey, body=pch, fields="etag,id,includeInGlobalAddressList").execute()
print json.dumps(res, indent=4)
The result I get is:
{"includeInGlobalAddressList": false, "etag": "\"WIg4sZOp0a-9Z5MJXVMQx1SQW5A/avXX6NaPX78Y6qFG7S4TqKFMIEU\""}
{
"includeInGlobalAddressList": true,
"etag": "\"WIg4sZOp0a-9Z5MJXVMQx1SQW5A/J0MuBtIvJoTcifknCMScMlyCQnc\"",
"id": "109793672165131484748"
}
This happens even if I do not use fields or pass the etag.
There's no need to do a GET before the PATCH request. The idea of PATCH is that only changed data is sent across the wire. Try:
service = build("admin", "directory_v1", http=http)
usersvc = service.users()
res = usersvc.patch(userKey=userkey, body={"includeInGlobalAddressList": False}, fields="id,includeInGlobalAddressList").execute()
print res
Related
I am currently creating a basic app with React-Native (frontend) and Flask/MongoDB (backend). I am planning on using AWS S3 as cheap cloud storage for all the images and videos that are going to be uploaded and viewed. My current idea (and this could be totally off), is when a user uploads content, it will go through my Flask API and then to the S3 storage. When a user wants to view content, I am not sure what the plan of attack is here. Should I use my Flask API as a proxy, or is there a way to simply send a link to the content directly on S3 (which would avoid the extra traffic through my API)?
I am quite new to using AWS and if there is already a post discussing this topic, please let me know, and I'd be more than happy to take down this duplicate. I just can't seem to find anything.
Should I use my Flask API as a proxy, or is there a way to simply send a link to the content directly on S3 (which would avoid the extra traffic through my API)?
If the content is public, you just provide an URL which points directly to the file on the S3 bucket.
If the content is private, you generate presigned url on your backend for the file for which you want to give access. This URL should be valid for a short amount of time (for example: 15/30 minutes). You can regenerate it, if it becomes unavailable.
Moreover, you can generate a presigned URL which can be used for uploads directly from the front-end to the S3 bucket. This might be an option if you don't want the upload traffic to go through the backend or you want faster uploads.
There is an API boto3, try to use it.
It is not so difficult, I have done something similar, will post code here.
I have done like #Ervin said.
frontend asks backend to generate credentials
backend sends to frontend the credentials
Frontend upload file to S3
Frontend warns backend it has done.
Backend validate if everything is ok.
Backend will create a link to download, you have a lot of security options.
example of item 6) To generate a presigned url to download content.
bucket = app.config.get('BOTO3_BUCKET', None)
client = boto_flask.clients.get('s3')
params = {}
params['Bucket'] = bucket
params['Key'] = attachment_model.s3_filename
params['ResponseContentDisposition'] = 'attachment; filename={0}'.format(attachment_model.filename)
if attachment_model.mimetype is not None:
params['ResponseContentType'] = attachment_model.mimetype
url = client.generate_presigned_url('get_object', ExpiresIn=3600, Params=params)
example of item 2) Backend will create presigned credentials to post your file on S3, send s3_credentials to frontend
acl_permission = 'private' if private_attachment else 'public-read'
condition = [{'acl': acl_permission},
["starts-with", "$key", '{0}/'.format(folder_name)],
{'Content-Type': mimetype }]
bucket = app.config.get('BOTO3_BUCKET', None)
fields = {"acl": acl_permission, 'Bucket': bucket, 'Content-Type': mimetype}
client = boto_flask.clients.get('s3')
s3_credentials = client.generate_presigned_post(bucket, s3_filename, Fields=fields, Conditions=condition, ExpiresIn=3600)
example of item 5) Here are an example how backend can check if file on S3 are ok.
bucket = app.config.get('BOTO3_BUCKET', None)
client = boto_flask.clients.get('s3')
response = client.head_object(Bucket=bucket, Key=s3_filename)
if response is None:
return None, None
md5 = response.get('ETag').replace('"', '')
size = response.get('ContentLength')
Here are an example how frontend will ask for credentials, upload file to S3 and inform backend it is done.
I tried to remove a lot of particular code.
//frontend asking backend to create credentials, frontend will send some file metadata
AttachmentService.createPostUrl(payload).then((responseCredentials) => {
let form = new FormData();
Object.keys(responseCredentials.s3.fields).forEach(key => {
form.append(key, responseCredentials.s3.fields[key]);
});
form.append("file", file);
let payload = {
data: form,
url: responseCredentials.s3.url
}
//Frontend will send file to S3
axios.post(payload.url, payload.data).then((res) => {
return Promise.resolve(true);
}).then((result) => {
//when it is done, frontend informs backend
AttachmentService.uploadSuccess(...).then((refreshCase) => {
//Success
});
});
});
I am a learner in postman and do not have much experience in programming/scripting.
Here the issue.
Used POST api request - For getting the access token;
Used POST api request - To create an account;
Used POST api request - To cancel an account with CancellationReason
Need to crosscheck the cancellation details (some fields like cancellationReason) in web application.
In order to avoid manually check, i have used GET request api like below
by passing all the mapped fields (as per web application) in the GET request end url
(i.e. by sending the details in fetch_xml query parameter in the end url) in order to get those required fields returned.
Now i got a successful response with status code.
After that i want to compare the fetched values (from the response body) ... VS.... to the data i passed while cancelling the account (i.e. in POST api request - To cancel the account) and make sure both are same.
After that under Test tab - I have updated query like below, however it throwing an Unexpected '#' error (as the below query contains '#' in middle of the field name)
tests["Verify the CancellationReason matches"] = pm.expect(data._usr_cancellationReason_value#OData.Community.Display.V1.FormattedValue).to.eql("CancellationReason");
Can someone please help me to understand whether i should remove this #symbol or should replace with something else ?
Here is the response body:
{
"#odata.context": "https://hfrdcompanies.integrationdev01.crm3.cs.com/api/data/v9.1/$metadata#hfrd_workorders(_usr_cancellationchannel_value,usr_CancellationChannel,_usr_workorderreason_value,usr_WorkOrderReason,hfrd_workorderid,usr_cancellationuser,_usr_cancellationsource_value,usr_CancellationSource,hfrd_name,usr_CancellationChannel(),usr_AccountReason(),usr_CancellationSource())",
"value": [
{
"#odata.etag": "W/\"2345234523\"",
"_usr_cancellationchannel_value#OData.Community.Display.V1.FormattedValue": "Mobile App",
"_usr_cancellationchannel_value": "acefsdflin89-f9jf07-e969f1-a245nk11-00jnfnafn9799fc2a",
"_usr_Accountreason_value#OData.Community.Display.V1.FormattedValue": "Customer Inactive",
"_usr_Accountreason_value": "bde1234522-d45662-e2711-a84561-0007354a2d5c2a",
"hfrd_Accountid": "89025sf3-c668f-e7811-a4331-00asdhh3ab9bd1c",
"usr_cancellationuser": "Testuser08 ABC",
"_usr_cancellationsource_value#OData.Community.Display.V1.FormattedValue": "MOBILE",
"_usr_cancellationsource_value": "6c23asdf-c562-e411-a841-00asdfa",
"hfrd_name": "FP-WK-1000000642"
}
]
}
I want to validate the bold row
I have been successful in creating a new Account User from following this tutorial: https://forge.autodesk.com/en/docs/bim360/v1/reference/http/users-POST/#example, and have used the PATCH method to set their status to active on Postman.
I would like to set their role and access_level but I am having trouble doing so. I have followed the link below to try and perform this function, but it requires the user to already be a BIM 360 Project Admin for it to work.
https://forge.autodesk.com/en/docs/bim360/v1/reference/http/projects-project_id-users-user_id-PATCH/
I also tried following the next link below to add a User to a project, but I am getting errors that I am unsure how to fix.
https://forge.autodesk.com/en/docs/bim360/v1/reference/http/projects-project_id-users-import-POST/
URI: https://developer.api.autodesk.com/hq/v2/accounts/:account_id/projects/:project_id/users/import
Method: PATCH
Authorization: *******************************************
Content-Type: application/json
x-user-id: {{user_id}}
Body:
{
"email": "john.smith#mail.com",
"services": {
"document_management": {
"access_level": "account_admin"
}
},
"company_id": ************************************,
"industry_roles": [
************************************
]
}
(The id for industry_role is IT).
Error:
{
"code": 1004,
"message": "this user doesn't exist."
}
I am unsure how I am getting this error since the User Id used for x-user-id is the same user_id associated with the email given in the request body. Is there a way to fix this or another method I can use?
The x-user-id header is not for specifying the user to import but rather:
x-user-id
string
In a two-legged authentication context, the app has access to all users specified by the administrator in the SaaS integrations UI. By providing this header, the API call will be limited to act on behalf of only the user specified.
Remove this field if that's not what you intended.
Verify the user id and email match each other via /GET users and /GET users:userid.
And be sure to provide either the user's email or the user ID and don't provide them both:
Note that you need to specify either an email, or a user_id. However, you cannot specify both.
See doc here
I have a Lambda Function that it is accessible by an API Gateway. I can handle all POST and GET submitted requests to API endpoint (https://XXXXXXX.execute-api.us-east-1.amazonaws.com/default/myapi) inside my Lambda, but I need to use some segments at end of my URL when I am using PUT requests.
My Python code to call the API is here and it is working correctly:
import requests
import json
url = 'https://XXXXXXX.execute-api.us-east-1.amazonaws.com/default/myapi'
token = "my token"
data = {
"first_name": "Reza",
"birthday": "1986-09-12"
}
headers = {"Content-Type" : "application/json", "x-api-key":"MY_API_KEY"}
response = requests.put(url, data=json.dumps(data), headers=headers)
print(response.text)
But if I add users segment to end of the URL like this:
url = 'https://XXXXXXX.execute-api.us-east-1.amazonaws.com/default/myapi/users'
it will show this error:
{"message":"Missing Authentication Token"}
I need to add some static segments like users to return the list of all users and some dynamic segments like users/USER_ID (when USER_ID is a dynamic number) to return the information for a special user.
can you please guide me how I can use segmented URL in my AWS API Gateway?
The term you are using segmented URL might have caused your confusion. It is called path parameters with AWS. There is more than one way to do it. ANY+ integration is the easiest to handle.
Integrate with ANY+ integration to your lambda and you are good to go. All the path parameters will be delivered to your lambda.
http://www.1strategy.com/blog/2017/06/06/how-to-use-amazon-api-gateway-proxy/
Additional path parameter documentation,
https://docs.aws.amazon.com/apigateway/latest/developerguide/integrating-api-with-aws-services-lambda.html#api-as-lambda-proxy-expose-get-method-with-path-parameters-to-call-lambda-function
Good luck.
Background
I have a service A accessible with HTTP requests. And I have other services that want to invoke these APIs.
Problem
When I test service A's APIs with POSTMAN, every request works fine. But when I user python's requests library to make these request, there is one PUT method that just won't work. For some reason, the PUT method being called cannot receive the data (HTTP body) at all, though it can receive headers. On the other side, the POST method called in the same manner receives the data perfectly.
I managed to achieve my goal simply by using httplib library instead, but I am still quite baffled by what exactly happened here.
The Crime Scene
Route 1:
#app.route("/private/serviceA", methods = ['POST'])
#app.route("/private/serviceA/", methods = ['POST'])
def A_create():
# request.data contains correct data that can be read with request.get_json()
Route 2:
#app.route("/private/serviceA/<id>", methods = ['PUT'])
#app.route("/private/serviceA/<id>/", methods = ['PUT'])
def A_update(id):
# request.data is empty, though request.headers contains headers I passed in
# This happens when sending the request with Python requests library, but not when sending with httplib library or with POSTMAN
# Also, data comes in fine when all other routes are commented out
# Unless all other routes are commented out, this happens even when the function body has only one line printing request.data
Route 3:
#app.route("/private/serviceA/schema", methods = ['PUT'])
def schema_update_column():
# This one again works perfectly fine
Using POSTMAN:
Using requests library from another service:
#app.route("/public/serviceA/<id>", methods = ['PUT'])
def A_update(id):
content = request.get_json()
headers = {'content-type': 'application/json'}
response = requests.put('%s:%s' % (router_config.HOST, serviceA_instance_id) + '/private/serviceA/' + str(id), data=json.dumps(content), headers = headers)
return Response(response.content, mimetype='application/json', status=response.status_code)
Using httplib library from another service:
#app.route('/public/serviceA/<id>', methods=['PUT'])
def update_course(id):
content= request.get_json()
headers = {'content-type': 'application/json'}
conn = httplib.HTTPConnection('%s:%s' % (router_config.HOST, serviceA_instance_id))
conn.request("PUT", "/private/serviceA/%s/" % id, json.dumps(content), headers)
return str(conn.getresponse().read())
Questions
1. What am I doing wrong for the route 2?
2. For route 2, the handler doesn't seem to be executed when either handler is commented out, which also confuses me. Is there something important about Flask that I'm not aware of?
Code Repo
Just in case some nice ppl are interested enough to look at the messy undocumented code...
https://github.com/fantastic4ever/project1
The serviceA corresponds to course service (course_flask.py), and the service calling it corresponds to router service (router.py).
The version that was still using requests library is 747e69a11ed746c9e8400a8c1e86048322f4ec39.
In your use of the requests library, you are using requests.post, which is sending a POST request. If you use requests.put then you would send a PUT request. That could be the issue.
Request documentation