AWS Dynamodb boto3 batch_write_item isn't working - amazon-web-services

I've been trying to wrap my mind around how batch_write_item works in the boto3 (Python SDK for AWS). My code is as follows:
users = self.scan_table(filterKey="key",filterValue="value",table="users")
deleteUsers = []
# Create lists to delete
for u in users:
deleteUsers.append({"DeleteRequest":{"Key":{"S":u["user_id"]}}})
# Delete items
ret = self.db.batch_write_item(
RequestItems={
"users":delUsers
}
)
The output is as follows (for each item to be deleted):
ClientError: An error occurred (ValidationException) when calling the
BatchWriteItem operation: The provided key element does not match the schema
As far as I can tell, I am following the prescribed instructions in the documentation exactly. What am I missing?
EDIT:
As per jarmod's comment, I felt I needed to specify that I am indeed using the boto3 resource, not the client. There are two separate sets of documentation for the client vs. the resource. Turns out that using the correct documentation for the function you are using is advisable.

Oh, it turns out I wasn't reading the documentation correctly (though it could stand to be a lot clearer in places). I changed the following code:
# Create lists to delete
for u in users:
delUsers.append({"DeleteRequest":{"Key":{"S":u["user_id"]}}})
To:
# Create lists to delete
for u in users:
delUsers.append({"DeleteRequest":{"Key":{"user_id":u["user_id"]}}})
The explanation is that I assumed that "S" was to designate the type of the key, but that isn't what's being asked for. After playing around removing the brackets, and playing around with dummy data, it became clear that I needed to change "S" to the actual name of the primary key of the table I was trying to effect, in this case, "user_id".
It worked just fine after that.

Related

How to get all the versions of a particular FHIR resource in AWS FHIR store?

Is there a way to get the list of all the versions of a specific resource created in FHIR store. I have used the following call,
<FHIR_URL>/<resource-type>/<resource-id>/_history
but its not returning response
If I add version to this url:
<FHIR_URL>/<resource-type>/<resource-id>/_history/<version>
then it only shows that particular version of the resource, but all the versions of a specific resource are required, is there a way to get this?
When in doubt, I always try the reference-implementations.
(all GET requests below)
http://wildfhir4.aegis.net/fhir4-0-1/Patient/example/_history
https://vonk.fire.ly/R4/Patient/pat1/_history
http://hapi.fhir.org/baseR4/Patient/616330/_history?_count=50
I got each systems Patient-Fhir-Logical-Id ("example", "pat1", "616330") by using the search function, and picking a random Patient. The Search function is as simple as /Patient/? and no query string values.
While always subject to change and "re doing the seed data", the aegis example above (today) is returning multiple rows of history for a single patient.
If AWS does not work "mostly the same" as the 3 reference implementations, I would submit a bug report.
But based on your examples, it seems to fall in line with the reference implementation examples above and the HL7 documentation below.
https://build.fhir.org/http.html#history

How to construct signing request for Amazon EC2 API call with filters?

This question is a follow-up to my earlier question on getting a REST API call to EC2 API working.
Having got that working, I wanted to generalize it and tried a couple of things. For example, I tried to filter by region name and make the request
https://ec2.amazonaws.com/?Action=DescribeRegions
&RegionName.1=us-east-1
&RegionName.2=eu-west-1
which they show in the documentation.
Signing that was easy, stick the RegionName(s) into the signing request in their alphabetical position and sign the whole thing just as I would without them. That worked out just fine.
So I progressed to the second example that they provide in the documentation.
https://ec2.amazonaws.com/?Action=DescribeRegions
&Filter.1.Name=endpoint
&Filter.1.Value.1=*ap*
OK, I tried to put the filters into my signing parameters in the alphabetically sorted location as required ...
char * signing_parameters_template =
"AWSAccessKeyId=%s&"
"Action=DescribeRegions&"
"Filter.1.Name=endpoint&"
"Filter.1.Value=*ap*&"
"SignatureMethod=HmacSHA256&"
"SignatureVersion=2&"
"Timestamp=%s&"
"Version=2013-08-15";
and sign that. I get an error that the signature doesn't match. I've tried a bunch of variations of this, no luck.
How does one sign a request that includes filters?
I have to escape the "*" in the filter
"Filter.1.Value=*ap*&"
That's it!

ACCESS_DENIED Status from Places API

I'm developing an Android App that uses the Places API to retrieve information and displays it on a map. The initial request to retrieve to places fails with a ACCESS_DENIED status message from the HTTP request. Below is the code that I used to generate the request:
try {
HttpRequestFactory httpRequestFactory = createRequestFactory(HTTP_TRANSPORT);
HttpRequest request = httpRequestFactory
.buildGetRequest(new GenericUrl(PLACES_SEARCH_URL));
request.getUrl().put("key", API_KEY);
request.getUrl().put("location", _latitude + "," + _longitude);
request.getUrl().put("radius", _radius); // in meters
request.getUrl().put("sensor", "false");
if(types != null)
request.getUrl().put("types", types);
PlacesList list = request.execute().parseAs(PlacesList.class);
// Check log cat for places response status
Log.d("Places Status", "" + list.status);
return list;
In another Stackoverflow posting someone had suggested that the poster try the following to test their key:
Go to the api console here, then to SERVICES. Click Active services
tab and verify 'Places API' is turned ON. Click on the ? "try" link
next to it. It should create a proper URL with your key which should
work. Compare the link that you are trying against this URL for
differences.
I followed these instructions. Based on the fact that I received the following results when I clicked on the ? to "try" the link I suspect something is fundamentally wrong with the API Key independent of the code...otherwise I would think I would get a SUCCESS rather than REQUEST_DENIED:
{
"html_attributions" : [],
"results" : [],
"status" : "REQUEST_DENIED"
}
I obtained my key by entering the SHA1 of my debug certificate (which i obtained using Keytool with all the appropriate parameters...e.g, androiddebugkey....debug.keystore) followed by a ";" and the Package Name of the app.
Not sure what the problem is...I'm sure it's something simple but I'm not seeing it and I'm stuck. Thoughts?
I never received a response to this posting so ultimately I've resolved the problem by creating a brand new key under a new project name and I was at least able to retrieve Places from Google..I'm still having issues with populating maps but that could be a code issue.
I noticed that the key that I was using that gave me the ACCESS DENIED results had a title of: "Key for Android apps (with certificates)" and it had a label "Android apps:" listed just under the actual key. The key value is the SHA1 value ";" followed by the Package Name. Whereas the key I created under a new Project Name (Places API) that ultimately worked had a title of: "Key for browser apps (with referers)" and it had a label of "Referers:" and value of "Any referer allowed".
So there is definitely something different about these two keys. I'm not sure what I did differently when I generated the keys. I'd like to understand what I did to generate these two "different" types of keys so that I and perhaps others won't repeat my "mistake(s)".
There are many references to creating keys in the Google documentation. The fact that there are so many postings regarding problems with the keys tells me that the Google documentation is not very clear otherwise so many issues wouldn't exist on this topic.

Passing negative values as arguments to custom management commands in django

Like many others, I've been learning web development on django through building a test app. I've the basic models set up. I've populated a few of the tables with the absolute minimum data needed for further testing though using fixtures.
Now for a different table, I want to create data tuples through a custom management command which takes the required arguments. If this works as expected, I'll save the created data to the database by adding the --save option.
The syntax of the command is like this
create_raw_data owner_id temperature [--save]
where owner_id is required and temperature (in C) is optional. Within the Handle method, I'm using factory boy to create the raw_data with the given arguments etc.
I did have some issues but searching on SO, google, django docs etc, I've got the command working fine.
EXCEPT when I input a negative temperature...
Then I get the following error
Usage: C:\test\manage.py create_raw_data [options]
Creates a RawData object. Usage: create_raw_data owner_id temperature [--save]
C:\test\manage.py: error: no such option: -5
The code I have for parsing the args is like this
for index, item in enumerate(args):
if index == 0:
owner_id = int(item)
else index == 1:
temp = int(item)
I put a print(args) as the 1st line inside Handle but it seems the control is not even reaching here.
I'm not sure what is wrong... please help...
Thanks a lot
got the issue fixed so providing an answer to others who may come across this.
The issue was with parse_args method of optparse. I've read in a number of places that though optparse is deprecated and instead argparse is recommended, django recommends using optparse since that is what it uses. Long story short, the link at link suggested a few alternatives and using create_raw_data 1 -- -5 works as expected. So I did get a workaround. Thanks.

Does Tastypie have a helper function to generate API keys?

What I'm trying to do is whenever the user requests an API key--regardless of whether the user already generated one or not--the system will generate an entirely new key.
I know that whenever calling ApiKey.objects.create() will generate an API key for the user that doesn't have one generated. However, if a user does have one, then trying to call the .create() method throws an error.
In this case, I figured that it would be best to write my own key generator. However, I am now hoping that maybe someone here might know of a helper function that will allow me to generate a random API key, and then let me save it to the database manually myself.
Would anyone might know of any such a helper function?
Or, you can just use tastypie's built-in command:
python manage.py backfill_api_keys
I figured it out.
First, you make an attempt to get the the user's API key. If it exists, then there will be no error thrown. To regenerate, set the value of the retrieved user's key to None, and then save the key.
If there was an error thrown, then simply create a new key.
try:
api_key = ApiKey.objects.get(user=someuser)
api_key.key = None
api_key.save()
except ApiKey.DoesNotExist:
api_key = ApiKey.objects.create(user=someuser)
Yes, the code for generating the key is defined as an instance method ApiKey.generate_key() which you can use directly.
Here's a simpler version that takes out some of the guesswork of whether the user already exists or not and uses ApiKey.generate_key() directly, rather than implicitly through ApiKey.save(), which I believe makes it a bit more clearer of what's trying to be accomplished:
api_key = ApiKey.objects.get_or_create(user=someuser)
api_key.key = api_key.generate_key()
api_key.save()
UPDATE:
Thus, the shortest version is:
return ApiKey.objects.get_or_create(user=someuser)[0].key
This will generate a new key or return an existing one.
Based on Filip Dupanović's answer the working code for me was something like this:
user = get_user_model().objects.get(email="some#email.com")
api_key = ApiKey.objects.get_or_create(user=user)
api_key[0].key = api_key[0].generate_key()
api_key[0].save()
Its way too easy to use inbuilt functions, always. To generate Api keys in Tastypie use "create_api_key" of TastypiesApikeyAuthentication`.
you have to just import "create_api_key" from tastypie.models
and then call it by django-signal or as per u require.
i.e.
signals.post_save.connect(create_api_key, sender=User)
Explained in detailed and easier at :
http://django-tastypie.readthedocs.org/en/latest/authentication.html#apikeyauthentication