Boto method for Amazon MWS GetLowestPricedOffersForSKU - python

Boto provides access to most Amazon MWS APIs, but not for GetLowestPricedOffersForSKU. I tried to hack one, but it generates an Invalid MarketplaceId error.
Boto has code for a very similarly structured API -- GetLowestOfferListingsForSKU:
#requires(['MarketplaceId', 'SellerSKUList'])
#structured_lists('SellerSKUList.SellerSKU')
#api_action('Products', 20, 5, 'GetLowestOfferListingsForSKU')
def get_lowest_offer_listings_for_sku(self, request, response, **kw):
"""Returns the lowest price offer listings for a specific
product by item condition and SellerSKUs.
"""
return self._post_request(request, kw, response)
So I modified the #api_action to change the MWS Operation to GetLowestPricedOffersForSKU:
### MINE ###
#requires(['MarketplaceId', 'SellerSKUList'])
#structured_lists('SellerSKUList.SellerSKU')
#api_action('Products', 20, 5, 'GetLowestPricedOffersForSKU')
def get_lowest_priced_offers_for_sku(self, request, response, **kw):
return self._post_request(request, kw, response)
I call this method as follows:
conn = connection.MWSConnection(
aws_access_key_id=ACCESS_KEY,
aws_secret_access_key=SECRET_KEY,
Merchant=ACCOUNT_ID
)
response = conn.get_lowest_priced_offers_for_sku(
MarketplaceId=marketplace_id, SellerSKUList=sku_list, ItemCondition=condition
)
When I call get_lowest_priced_offers_for_sku, I get an Invalid MarketplaceId error. If the only change I make is to call get_lowest_offer_listings_for_sku -- leaving every variable identical -- the code works find and return a valid response object. This works just fine:
response = conn.get_lowest_offer_listings_for_sku(
MarketplaceId=marketplace_id, SellerSKUList=sku_list, ItemCondition=condition
)
What do I need to do to access the Amazon MWS GetLowestPricedOffersForSKU via boto?

Not sure and neither I am python programmer, but In PHP AmazonMWS API I use code below where I use setMarketplaceId()
$request = new MarketplaceWebServiceProducts_Model_GetLowestPricedOffersForSKURequest();
$request->setSellerId($this->seller_id);
$request->setMarketplaceId($this->marketplace_id);
$request->setItemCondition("New");

Boto2 doesnt support GetLowestPricedOffersForSKU as you can see from the documentation http://docs.pythonboto.org/en/latest/ref/mws.html

Related

boto3 lambda payload always returns NULL [python, sync invoked]

I am using Lambda to trigger a step function. It works fine in terms of triggering the step function, but I also need the lambda to return the state machine execution arn (NOT state machine arn). I need the execution arn because I am implementing the whole process as a github action workflow, so I need it to check the status (running/success/failed/aborted) of the state machine.
My code of getting the lambda return for github action, wrapped as docker-compose service:
client = boto3.client("lambda", region_name="us-west-1")
lambda_response = client.invoke(
FunctionName="my-lambda",
InvocationType="RequestResponse",
Payload=json.dumps({"detail-type": "gh-action"}),
)
payload = json.loads(lambda_response["Payload"].read()) # tried .decode() too
print("payload:", payload) # payload prints None as a whole, not {"sfn_exe_arn": None}
The relevant part of my lambda function:
try:
client = boto3.client("stepfunctions")
response = client.start_execution(
stateMachineArn=STATE_MACHINE_ARN,
name=run_name,
input=json.dumps(
{"runName": run_name, "model_name": MODEL_NAME, "queries": QUERIES}
),
)
sfn_exe_arn = response["executionArn"]
except Exception as e:
raise e
return {"sfn_exe_arn": sfn_exe_arn}
# this `sfn_exe_arn` can print out with expected value in console
# but it does not return when called in github action
When I invoke this lambda from the console, most of the time it returns as expected, which is {"sfn_exe_arn": sfn_exe_arn}, but sometime it also returns null.
When I invoke this lambda as part of github action workflow, the return is always null (the lambda_response is returned, just the payload part is always null)
Can anyone help me understand why there is this gap? apparently my lambda got the executionArn, but it just doesn't return to the client.invoke()
The entire lambda_response (it is named response in the screenshot):
You have to decode the bytestream that you get from StreamingBody.read()
add .decode() to the bytes object you get from reading the reponse payload.
payload = json.loads(lambda_response["Payload"].read().decode())
My bad, I didn't try thoroughly other posts' answers. Thanks #jarmod pointed out the solution in the comments: you need to assign StreamingBody to a variable before read it. Link: Lambda Return Payload botocore.response.StreamingBody object prints but then empty in variable

How to send application/x-www-form-urlencoded to slack via python slackclient?

I am trying to use the api call users.profile.get to find a users profile picture. The problem is that the request requires not JSON, but URL encoded queries (I think?). I have the user id already, but I need to know how to send it to slack correctly, preferably with the api_call method.How would I go along doing this?
Here is the documention: https://api.slack.com/methods/users.profile.get
for users in collection.find():
start_date, end_date = users['start_date'], users['end_date']
user_data = client.api_call('users.profile.get',
"""What would I do here?""")
user_images[users['user_id']] = user_data['image_72']
block.section(
text=
f'from *{date_to_words(start_date[0], start_date[1], start_date[2])}* to *{date_to_words(end_date[0], end_date[1], end_date[2])}*'
)
block.context(data=((
'img', user_images[users['user_id']],
'_error displaying image_'
), ('text',
f'<!{users["user_id"]}>. _Contact them if you have any concerns_'
)))
You can pass the parameters of the API as names arguments in your function call.
For users.get.profile you want to provide the user ID, e.g. "U1245678".
Then your call would look like this (with slackclient v1):
response = sc.api_call(
"users.profile.get",
user="U12345678"
)
assert response["ok"]
user_data = response["profile"]
Or like this with slackclient v2:
response = sc.users_profile_get(user="U12345678")
assert response["ok"]
user_data = response["profile"]
To answer your question: You do not have to worry about how the API is called, since that is handled by library. But technically most of Slack's API endpoints accepts parameters both as URL endocded form and JSON.

All v4 Requests are Giving Error Code 403 (Forbidden)

I'm working with the Python Bit.ly API and trying to convert it to work with v4. Has anyone had any success with this? Specifically, I'm trying to pull the data for a user (specifically groups) which only v4 supports, so I cannot use the v3 API. This is the function (located in the bitly_api.py file):
def user_info(self, **kwargs):
"""return or update info about a user"""
#data = self._call_oauth2("v3/user/info", kwargs)
data = self._call_oauth2("v4/groups", kwargs)
return data
The #data = self.... line works correctly, but returns the wrong information. The next line gives me the forbidden error. Does anyone have any idea of what else might need to be changed?
Python API link: https://github.com/bitly/bitly-api-python

Duo Security API with python client

I'm trying to work with Duo's python client (https://github.com/duosecurity/duo_client_python) and I believe I'm simply missing something with my novice python eyes. I somehow need to authenticate my requests to the API using an HMAC signature -- which I've not worked with before but seems trivial to generate (they even provide a python function). Doc here https://duo.com/docs/adminapi#authentication
I'm at a loss as to HOW to craft this signature for authentication, prior to passing my request to the API. From https://duo.com/docs/adminapi#authentication:
The API uses HTTP Basic Authentication to authenticate requests. Use your >Duo application’s integration key as the HTTP Username.
Generate the HTTP Password as an HMAC signature of the request. This will >be different for each request and must be re-generated each time.
It then goes into which parameters are added and necessary for the HMAC signature to be generated properly. I understand this part. My issue is how and when to pass the HMAC signature that I generate. As the Duo doc doesn't specifically go into that, I'm thinking that this is something I should already know [were I not such a python novice].
For example, simple auth calls work fine (as they need no authentication):
root#box:~# python -m duo_client.client --ikey XXXXXXXX --skey XXXXXXXX --host XXXXXXXX.duosecurity.com --method GET --path /auth/v2/check 200 OK
{
"response":
{ "time": 1496437236 }
,
"stat": "OK"
}
However, calls using admin require authentication, and thus fail with:
root#box:~# python -m duo_client.client --ikey XXXXXXXX --skey XXXXXXXX --host XXXXXXXX.duosecurity.com --method GET --path /admin/v1/users signature=XXXXXXXX
401 Unauthorized
{
"code": 40103,
"message": "Invalid signature in request credentials",
"stat": "FAIL"
}
Thanks in advance for any insight!
==EDIT==
So I thought I'd post the function that Duo provide for creating the signature, which appears similar to what is happening in the StackOverflow question I found that I thought might help (Python, HTTPS GET with basic authentication). From https://duo.com/docs/adminapi#authentication:
def sign(method, host, path, params, skey, ikey):
"""
Return HTTP Basic Authentication ("Authorization" and "Date") headers.
method, host, path: strings from request
params: dict of request parameters
skey: secret key
ikey: integration key
"""
# create canonical string
now = email.Utils.formatdate()
canon = [now, method.upper(), host.lower(), path]
args = []
for key in sorted(params.keys()):
val = params[key]
if isinstance(val, unicode):
val = val.encode("utf-8")
args.append(
'%s=%s' % (urllib.quote(key, '~'), urllib.quote(val, '~')))
canon.append('&'.join(args))
canon = '\n'.join(canon)
# sign canonical string
sig = hmac.new(skey, canon, hashlib.sha1)
auth = '%s:%s' % (ikey, sig.hexdigest())
# return headers
return {'Date': now, 'Authorization': 'Basic %s' % base64.b64encode(auth)}
I've used the above function in a simple script to print out (just so I can visualize) what should be getting passed within the script I'll create to server our needs -- I added the function to a script and used print by adding the following:
# Printing Signature Headers ### TESTING ###
print sign('GET', 'XXXhostXXX', '/admin/v1/users', 'XXXparamXXX', 'XXXskeyXXX', 'XXXXikeyXXXX')
However, I'm getting this error:
root#box:~# ./duo.py
Traceback (most recent call last):
File "./duo.py", line 39, in
print sign('GET', 'XXXhostXXX', '/admin/v1/users', 'XXXparamXXX', 'XXXskeyXXX', 'XXXikeyXXX')
File "./duo.py", line 18, in sign
for key in sorted(params.keys()):
AttributeError: 'str' object has no attribute 'keys'
Am I just missing something here? I've been looking for what could cause that error but I thought I'd ask here as well.
This was an "issue" on Duo's side...wrong skey was being used and my user did not have the proper access.

How to process JSON in Flask?

I have asked a few questions about this before, but still haven't solved my problem.
I am trying to allow Salesforce to remotely send commands to a Raspberry Pi via JSON (REST API). The Raspberry Pi controls the power of some RF Plugs via an RF Transmitter called a TellStick. This is all setup, and I can use Python to send these commands. All I need to do now is make the Pi accept JSON, then work out how to send the commands from Salesforce.
Someone kindly forked my repo on GitHub, and provided me with some code which should make it work. But unfortunately it still isn't working.
Here is the previous question: How to accept a JSON POST?
And here is the forked repo: https://github.com/bfagundez/RemotePiControl/blob/master/power.py
What do I need to do? I have sent test JSON messages n the Postman extension and in cURL but keep getting errors.
I just want to be able to send various variables, and let the script work the rest out.
I can currently post to a .py script I have with some URL variables, so /python.py?power=on&device=1&time=10&pass=whatever and it figures it out. Surely there's a simple way to send this in JSON?
Here is the power.py code:
# add flask here
from flask import Flask
app = Flask(__name__)
app.debug = True
# keep your code
import time
import cgi
from tellcore.telldus import TelldusCore
core = TelldusCore()
devices = core.devices()
# define a "power ON api endpoint"
#app.route("/API/v1.0/power-on/<deviceId>",methods=['POST'])
def powerOnDevice(deviceId):
payload = {}
#get the device by id somehow
device = devices[deviceId]
# get some extra parameters
# let's say how long to stay on
params = request.get_json()
try:
device.turn_on()
payload['success'] = True
return payload
except:
payload['success'] = False
# add an exception description here
return payload
# define a "power OFF api endpoint"
#app.route("/API/v1.0/power-off/<deviceId>",methods=['POST'])
def powerOffDevice(deviceId):
payload = {}
#get the device by id somehow
device = devices[deviceId]
try:
device.turn_off()
payload['success'] = True
return payload
except:
payload['success'] = False
# add an exception description here
return payload
app.run()
Your deviceID variable is a string, not an integer; it contains a '1' digit, but that's not yet an integer.
You can either convert it explicitly:
device = devices[int(deviceId)]
or tell Flask you wanted an integer parameter in the route:
#app.route("/API/v1.0/power-on/<int:deviceId>", methods=['POST'])
def powerOnDevice(deviceId):
where the int: part is a URL route converter.
Your views should return a response object, a string or a tuple instead of a dictionary (as you do now), see About Responses. If you wanted to return JSON, use the flask.json.jsonify() function:
# define a "power ON api endpoint"
#app.route("/API/v1.0/power-on/<int:deviceId>", methods=['POST'])
def powerOnDevice(deviceId):
device = devices[deviceId]
# get some extra parameters
# let's say how long to stay on
params = request.get_json()
try:
device.turn_on()
return jsonify(success=True)
except SomeSpecificException as exc:
return jsonify(success=False, exception=str(exc))
where I also altered the exception handler to handle a specific exception only; try to avoid Pokemon exception handling; do not try to catch them all!
To retrieve the Json Post values you must use request.json
if request.json and 'email' in request.json:
request.json['email']

Categories