billstclair/elm-dynamodb/1.0.0

NOTE: this package is not compatible with Elm 0.19.1

DynamoBackend

This module provides an Elm backend to Amazon's DynamoDB.

By itself, in pure Elm, you can only access a simulation of the Dynamo database, with key/value pairs that persist only for the current session. The README for the GitHub archive tells how to hook up the JavaScript via ports to your application, and how to configure DynamoDB via Amazon's Web Services console for use with DynamoBackend.

There is a simple example that clearly illustrates the difference between the pure-Elm simulator and the real Amazon backend.

DynamoBackend targets a single DynamoDB table with three attributes. It enables use of that table by multiple Amazon accounts, with each account's data insulated from the others. It also allows multiple different applications to store their data in that one backend table, without interference.

The data store is a simple key/value store, mapping a string key to string data. I expect that one common use will be to JSON encode state, and store it by key. I built it to do that for my application.

The one drawback of Amazon's authentication mechanism that I was not able to work around is that a login session lasts only one hour. Each hour, your users will have to click on the "OK" button in the login dialog, to renew the session. I consider this to be a bug on Amazon's part.

Classes

type alias Profile =
{ email : String
, name : String
, userId : String
}

The application-level result of a successful login

type alias Properties =
List ( String, String )

The communication through the ports to the backend JavaScript happens with Properties lists, lists of string pairs.

type alias StringDict =
Dict String String

An Elm Dict mapping String keys to String values.

You need to provide one of these in your Model for the simulated backend.

type alias DynamoServerInfo =
{ clientId : String
, tableName : String
, appName : String
, roleArn : String
, providerId : String
, awsRegion : String
}

This record is sent to Elm as the "flags" argument from the startup code. It is stored internally by the JavaScript backend code, and isn't used by any of the Elm code, except that you store it in your Dynamo database. Can be useful for debugging (though I'm tempted to leave it solely in the JavaScript code).

The properties are setup in Amazon's AWS Console for DynamoDB, and are stored in a JavaScript file that you create.

This is not secret information. It simply identifies your application and the table you use to store your key/value pairs.

type alias ResultDispatcher model msg =
{ login : Profile -> Database model msg -> model -> ( model, Cmd msg )
, get :
String
-> Maybe String
-> Database model msg
-> model
-> ( model, Cmd msg )
, put :
String
-> Maybe String
-> Database model msg
-> model
-> ( model, Cmd msg )
, scan :
List String
-> List String
-> Database model msg
-> model
-> ( model, Cmd msg )
, logout : Database model msg -> model -> ( model, Cmd msg )
}

When results return from the backend, they are passed to one of these functions that you provide.

DynamoBackend.login gives results to the ResultDispatcher.login function.

DynamoBackend.get gives results to the ResultDispatcher.get function.

DynamoBackend.put and DynamoBackend.remove give results to the ResultDispatcher.put function.

DynamoBackend.scan gives results to the ResultDispatcher.scan function.

DynamoBackend.logout results to the ResultDispatcher.logout function.

type Database model msg

The generic type for a Simulated or Dynamo database

type alias SimDb model msg =
{ profile : Profile
, getDict : model -> StringDict
, setDict : StringDict -> model -> model
, simulatedPort : Properties -> Cmd msg
, dispatcher : ResultDispatcher model msg
}

Properties for a simulated backend Database.

profile - A fake login Profile.

getDict - Return from your Model an Elm Dict in which the simulator can store its key/value pairs.

setDict - Set the dictionary in your Model.

simulatedPort - This simulates the return port from the real backend. DynamoBackend.makeMsgCmd is often useful for turning one of your messages into a Cmd.

dispatcher - The ResultDispatcher that will handle the values returned through the simulatedPort.

type alias DynamoDb model msg =
{ serverInfo : DynamoServerInfo
, getProperties : model -> Properties
, setProperties : Properties -> model -> model
, backendPort : Properties -> Cmd msg
, backendMsg : Properties -> msg
, dispatcher : ResultDispatcher model msg
}

Properties for a real Dynamo backend Database.

serverInfo - The ServerInfo record sent in as the startup "flags" from the JavaScript.

getProperties - Your Model must contain a Properties list that the DynamoBackend code can use to store state. This function extracts that list from your Model.

setProperties - Set the Properties list in your Model.

backendPort - your outgoing backend port to the JavaScript code.

backendMsg - Create a message as if it came from the incoming backend port to the JavaScript code.

dispatcher - The record of functions to call for return data from the backend JavaScript.

= FetchProfileError
| AccessTokenError
| InternalError
| ReturnedProfileError
| AccessExpired Properties
| AwsError String String Bool
| Other

Errors that can be returned in the errorType property of an Error record.

AccessExpired - Happens when your Amazon login session expires. Your application needs to call DynamoBackend.login again to establish a new session.

FetchProfileError - denotes a problem in turning an access token into a profile.

AccessTokenError - Either Amazon didn't return the state arg when logging in, or a cross-site forgery made the state sent not match the state received.

InternalError - denotes a bug in the DynamoBackend code. Shouldn't happen.

ReturnedProfileError - Means that Amazon's return for profile lookup was missing the email, name, or userId.

AwsError - An error was returned by the Amazon AWS JavaScript library. Usually denotes a network problem.

Other - Shouldn't happen. Means that that the backend code neglected to tag an error with an "errorType".

type alias Error =
{ errorType : ErrorType
, message : String
}

DynamoBackend.update returns errors in an Error record.

Functions

formatError : Error -> String

Format an Error record as a string

getProp : String -> Properties -> Maybe String

Lookup a key in a Properties list. Return Nothing if its not there, or Just value if it is.

setProp : String -> String -> Properties -> Properties

Set the value for a key to a value in a Properties list.

setProp key value properties

removeProp : String -> Properties -> Properties

Remove the property for a key from a Properties list.

removeProps : List String -> Properties -> Properties

Remove a list of keys from a Properties list.

mergeProps : Properties -> Properties -> Properties

Merge two Properties lists.

If both contain a value for the same key, use the value from the first list (from).

mergeProps from to

DynamoServerInfo
-> (model -> Properties)
-> (Properties -> model -> model)
-> (Properties -> Cmd msg)
-> (Properties -> msg)
-> ResultDispatcher model msg
-> Database model msg

Create a real Dynamo backend Database.

The arguments become the properties of the returned (wrapped) DynamoDb record.

Profile
-> (model -> StringDict)
-> (StringDict -> model -> model)
-> (Properties -> Cmd msg)
-> ResultDispatcher model msg
-> Database model msg

Create a simulated backend Database.

The arguments become the properties of the returned (wrapped) SimDb record.

isRealDatabase : Database model msg -> Bool

Return True if the argument is a real datbase (the result of calling makeDynamoDb) or False if it is simulated (from makeSimulatedDb).

makeMsgCmd : msg -> Cmd msg

Wrap a message as a Cmd.

installLoginScript : Database model msg -> model -> Cmd msg

The Amazon login code attaches a script to the <div> with an id of "amazon-root". Your view code needs to create that <div>. Call this when your application starts to attach the login script. It will auto-login if a recent session in the same browser has not yet expired.

login : Database model msg -> model -> Cmd msg

Call this when the user clicks on your "login" button, or when you get an AccessExpired error.

retry : Database model msg -> Properties -> Cmd msg

Call this to retry an operation that got an AccessExpired error

put :
String
-> String
-> String
-> Database model msg
-> model
-> ( model, Cmd msg )

Call this to store a key/value pair in the database. The userId comes from the Profile record.

put userId key value database

remove : String -> String -> Database model msg -> model -> ( model, Cmd msg )

Call this to remove a key/value pair from the database. The userId comes from the Profile record.

remove userId key database

get : String -> String -> Database model msg -> model -> Cmd msg

Call this to get the value for a key from the database. The userId comes from the Profile record.

get userId key database

scan : Bool -> String -> Database model msg -> model -> Cmd msg

Call this to scan the database for all keys. If fetchValues is True, will also return values.

The userId comes from the Profile record.

scan fetchValues userId database

logout : Database model msg -> model -> Cmd msg

Call this to logout from Amazon. Clears all state the could be used to create a session.

partialLogout : Database model msg -> model -> Cmd msg

For testing. Invalidates the saved access tokens

Properties
-> Database model msg
-> model
-> Result Error ( model, Cmd msg )

This handles the Properties that are sent back from the backend JavaScript (real or simulated). Your application needs to map that command to a message, and handle that message by calling DynamoBackend.update, and then process the resulting Err or Ok value. Before returning, it will usually call one of the functions in the database's ResultSetDispatcher.