Possible understandings

Use Cases

Game Scoring

Use key like Pig-Game-JoeSmith Resident or Pig-Game-JoeUUID, store a JSON scorecard of your choosing.

Visitor Log

Suppose we wanted to keep a visitor log by visitor name, storing time and such. We’d naively want to have a key of name,time or maybe just time. We could use a full standard time-stamp lke 2018-08-03T142136Z, with JSON or text info about that arrival, but how would we ever find anything?

There is an operation Query, and another,, Scan that can return a batch of records.

aws dynamodb scan \
    --table-name Movies \
    --projection-expression "title" \
    --filter-expression 'contains(info.genres,:gen)' \
    --expression-attribute-values '{":gen":{"S":"Sci-Fi"}}' \
    --page-size 100  \
    --debug

From https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Scan.html#Scan.Pagination

We could produce a general scan/continue scan operation somehow? You get back LastEvaluatedKey and can provide ExclusiveStartKey. There may be a PageSize option to limit the number of records returned. There is also Limit to limit the number processed (whether or not returned). Could be expensive.

Query is used when you have a primary key with a subordinate sort key. That might make more sense for a visitor record with PK the avatar UUID and sort key the arrival timestamp. Then you could get all records for a given AV.

Possibly we should have our database include a sort key which would generally be blank but possibly used in some applications. For GetItem you provide both PK and SK. Note also that PK (and SK?) can be composite items, apparently.

For our purposes perhaps we want a string PK and SK. PK and SK can each be only one attribute but can be of any type? Note below that both are of type S, and we’d surely use that. We’d recommend that most people use a fixed SK, probably empty string.

GetItem requires PK and SK and returns an item. Query, I think, can take PK and return all the SKs.

{
    TableName : "KeyValue",
    KeySchema: [       
        { 
            AttributeName: "Artist", 
            KeyType: "HASH", //Partition key
        },
        { 
            AttributeName: "SongTitle", 
            KeyType: "RANGE" //Sort key
        }
    ],
    AttributeDefinitions: [
        { 
            AttributeName: "Artist", 
            AttributeType: "S" 
        },
        { 
            AttributeName: "SongTitle", 
            AttributeType: "S" 
        }
    ],
    ProvisionedThroughput: {       
        ReadCapacityUnits: 1, 
        WriteCapacityUnits: 1
    }
}

Ban Log

Probably store PK = PerpID, SK = timestamp. Need query to access?

Notes en route

New api movies 6ri69o9kw5 resources/ qznvakztog

Adding ron again:

User name,Password,Access key ID,Secret access key,Console login link
ronjeffries@acm.org,,AKIAJRRAFBZ3YUWCTKBA,gmgDsPaqdg4Xajy50y5mx+Y51zBex/4Pz6MDOpxB,https://454572464863.signin.aws.amazon.com/console

Role arn:aws:iam::454572464863:role/aws_api_policy_vtrans I think this is right and it seems to go thru. Role trusted entities include apigateway.

Everything (I think) is in us-east-1 (VA)

Still getting resource not found. Bug was bad name favorite-movies not movies:

{
  "TableName": "movies",
  "Key": {
    "name": {
      "S": "$input.params('name')"
    }
  }
}

Works now. Wonder how I can dump the whole thing.

Record everything tomorrow.

Apparently we must next deploy the API to a stage … whatever those things mean …

I’m following this: https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-create-api-from-example-console.html

Deployed current API Gateway

Invoke URL: https://53jm8kpora.execute-api.us-east-1.amazonaws.com/vt-001 Following that gives: {"message":"Missing Authentication Token"} (makes sense)

Deployment stage is vt-001, various descriptions.

Doc says to expand the stage (triangle) and select the GET, get new invoke URL: https://53jm8kpora.execute-api.us-east-1.amazonaws.com/vt-001/{name}

Pasting https://53jm8kpora.execute-api.us-east-1.amazonaws.com/vt-001/{ron} returns {}

Wow, pasting https://53jm8kpora.execute-api.us-east-1.amazonaws.com/vt-001/ron returns {"Item":{"name":{"S":"ron"},"title":{"S":"shrek"}}}

https://53jm8kpora.execute-api.us-east-1.amazonaws.com/vt-001/jan returns {"Item":{"name":{"S":"jan"},"title":{"S":"star wars"}}}

This should work in SL just fine. Let’s try it.

// http access to AWS API Gateway
// JR 2018-08-01

// https://53jm8kpora.execute-api.us-east-1.amazonaws.com/vt-001/{name}

string API = "https://53jm8kpora.execute-api.us-east-1.amazonaws.com/vt-001/jan";
key Query;

default {
  touch_start(integer num_detected) {
    if ( llDetectedKey(0) != llGetOwner() ) return;
    Query = llHTTPRequest(API, [], "");
  }

  http_response(key request_id, integer status, list metadata, string body) {
    if ( request_id == Query ) {
      string r = llDumpList2String([status, body], "\n");
      llOwnerSay(r);
    }
  }
}

And it works!!

Followed Knape to put in a PUT.

{ 
    "TableName": "movies",
    "Item": {
      "name": {
            "S": "$input.params('name')"
        },
        "title": {
            "S": "$input.path('$.title')"
        }
    }
}

Works in test! Now to deploy. To vt-001 again:

Get: https://53jm8kpora.execute-api.us-east-1.amazonaws.com/vt-001/{name} (same)

Put: https://53jm8kpora.execute-api.us-east-1.amazonaws.com/vt-001/{name} (same)

Apparently, and sensibly, the addresses do not change on deployment. (Otherwise, all one’s apps would break on a new version.)

Difference will be GET/PUT in the call (and stuff in the body).

Remaining questions:

  • How to do JSON?
  • Can we create new tables and new fields this way?
  • How do we find out the magical JSON meanings?
// http access to AWS API Gateway
// JR 2018-08-01

// https://53jm8kpora.execute-api.us-east-1.amazonaws.com/vt-001/{name}

string Short_API = "https://53jm8kpora.execute-api.us-east-1.amazonaws.com/vt-001/";
key Query;

default {
  state_entry() {
    llListen(33, "", llGetOwner(), "");
  }

  listen(integer channel, string name, key id, string msg) {
    string api = Short_API + msg;
    Query = llHTTPRequest(api, [], "");
  }

  touch_start(integer num_detected) {
    if ( llDetectedKey(0) != llGetOwner() ) return;
    Query = llHTTPRequest(Short_API + "fred", [], "");
  }

  http_response(key request_id, integer status, list metadata, string body) {
    if ( request_id == Query ) {
      string r = llDumpList2String([status, body], "\n");
      llOwnerSay(r);
      string v = llJsonGetValue(body, ["Item", "title", "S"]);
      llOwnerSay("title is " + v);
    }
  }
}
// http write to AWS API Gateway
// JR 2018-08-01

// https://53jm8kpora.execute-api.us-east-1.amazonaws.com/vt-001/{name}

string Short_API = "https://53jm8kpora.execute-api.us-east-1.amazonaws.com/vt-001/";
key Query;

default {
  state_entry() {
    llListen(34, "", llGetOwner(), "");
  }

  listen(integer channel, string name, key id, string msg) {
    list name_value = llCSV2List(msg);
    string name = llStringTrim(llList2String(name_value, 0),STRING_TRIM);
    string value = llStringTrim(llList2String(name_value, 1),STRING_TRIM);
    string body = llList2Json(JSON_OBJECT, ["title", value]);
    string api = Short_API + name;
    llOwnerSay(llDumpList2String([api, body], "\n"));
    Query = llHTTPRequest(api, [HTTP_METHOD, "PUT", HTTP_MIMETYPE, "application/json"], body);
  }

  http_response(key request_id, integer status, list metadata, string body) {
    if ( request_id == Query ) {
      string r = llDumpList2String([status, body], "\n");
      llOwnerSay(r);
    }
  }
}

Deploying key-aux-value: https://tz70gb4ih6.execute-api.us-east-1.amazonaws.com/vt-kv-1

https://tz70gb4ih6.execute-api.us-east-1.amazonaws.com/vt-kv-1/query?key=bill

{"Count":3,"Items":[{"value":{"S":"this is bill's first value"},"vt_aux":{"S":"001"},"vt_key":{"S":"bill"}},{"value":{"S":"here is bill's #2 value"},"vt_aux":{"S":"002"},"vt_key":{"S":"bill"}},{"value":{"S":"here is bill number four"},"vt_aux":{"S":"004"},"vt_key":{"S":"bill"}}],"ScannedCount":3}