Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci-cd.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ jobs:
cache: "npm"
- run: npm ci
- run: npm run lint
- run: npm run test:unit
- run: npm run test

release:
needs: build
Expand Down
64 changes: 40 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,15 @@ The context stored by this module consists of the following data elements:

* **installedAppId**: the UUID of the installed app instance. This is the primary key of the table.
* **locationId**: the UUID of the location in which the app is installed
* **locale**: the locale client used to install the app
* **authToken**: the access token used in calling the API
* **refreshToken**: the refresh token used in generating a new access token when one expires
* **config**: the current installed app instance configuration, i.e. selected devices, options, etc.
* **state**: name-value storage for the installed app instance

_Note: Version 2.X.X is a breaking change to version 1.X.X as far as configuring the context store is
concerned, but either one can be used with any version of the SmartThings SDK._
**_Note: Version 3.X.X is a breaking change to version 2.X.X as far as configuring the context store is
concerned, but either one can be used with any version of the SmartThings SDK. The new state storage
functions are only available with version 5.X.X or later of the SDK._**

## Installation

Expand All @@ -34,14 +37,17 @@ The more extensive set of options are shown in this example:
smartapp.contextStore(new DynamoDBContextStore(
{
table: {
name: 'custom-table', // defaults to 'smartapp'
hashKey: 'key1', // defaults to 'id'
prefix: 'context', // defaults to 'ctx'
readCapacityUnits: 10, // defaults to 5, applies to automatic creation only
writeCapacityUnits: 10 // defaults to 5, applies to automatic creation only
name: 'custom-table', // defaults to 'smartapp'
hashKey: 'key1', // defaults to 'id'
prefix: 'context', // defaults to 'ctx'
billingMode: 'PROVISIONED', // defaults to 'PAY_PER_REQUEST'
readCapacityUnits: 10, // defaults to 1, applies to automatic creation only
writeCapacityUnits: 10 // defaults to 1, applies to automatic creation only
},
AWSRegion: 'us-east-2', // defaults to 'us-east-1'
autoCreate: true // defaults to true
aws: {
region: 'us-east-2', // defaults to 'us-east-1'
},
autoCreate: true // defaults to true
}
))
```
Expand All @@ -51,6 +57,7 @@ The **table** configuration options are:
* **name** -- The name of the DynamoDB table storing the context
* **hashKey** -- The name of the partition key of the table
* **prefix** -- A string pre-pended to the installed app ID and used as the partition key for the entry
* **billingMode** -- The billing mode of the table. Either `PAY_PER_REQUEST` or `PROVISIONED`
* **readCapacityUnits** -- Number of consistent reads per second. Used only when table is created
* **writeCapacityUnits** -- Number of writes per second. Used only when table is created
* **sortKey** -- Optional sort key definition (see below for more details)
Expand All @@ -66,25 +73,18 @@ Note that only one of the AWS options should be specified or behavior will be in

### AWS Configuration Options

By default, the AWS credentials are picked up from the environment. If you prefer you can read the credentials
from a file with this configuration:
By default, the AWS credentials are picked up from the environment. If you prefer you can
explicitly set the credentials in this way:

```javascript
smartapp.contextStore(new DynamoDBContextStore(
{
AWSConfigPath: './path/to/file.json'
}
))
```

You can also explicitly set the credentials in this way:

```javascript
smartapp.contextStore(new DynamoDBContextStore(
{
AWSConfigJSON: {
accessKeyId: '<YOUR_ACCESS_KEY_ID>',
secretAccessKey: '<YOUR_SECRET_ACCESS_KEY>',
aws: {
endpoint: 'http://localhost:8000',
credentials: {
accessKeyId: '<YOUR_ACCESS_KEY_ID>',
secretAccessKey: '<YOUR_SECRET_ACCESS_KEY>',
},
region: 'us-east-2'
}
}
Expand Down Expand Up @@ -127,3 +127,19 @@ smartapp.contextStore(new DynamoDBContextStore(
}
))
```

### Partition Key Prefix

The default behavior is to construction the partition key of the context records from the installedAppId with
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor grammar error here. construction -> construct.

the prefix `pre:`. If you want to override this prefix you can do so by specifying the `prefix` option:

```javascript
smartapp.contextStore(new DynamoDBContextStore(
{
table: {
name: 'my-application',
prefix: 'context$'
}
}
))
```
1 change: 1 addition & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './lib/dynamodb-context-store'
17 changes: 17 additions & 0 deletions jest-dynamodb-config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
module.exports = {
tables: [
{
TableName: 'context-store-test-1',
KeySchema: [{AttributeName: 'id', KeyType: 'HASH'}],
AttributeDefinitions: [{AttributeName: 'id', AttributeType: 'S'}],
ProvisionedThroughput: {ReadCapacityUnits: 1, WriteCapacityUnits: 1},
},
{
TableName: 'context-store-test-2',
KeySchema: [{AttributeName: 'id', KeyType: 'HASH'}, {AttributeName: 'sk', KeyType: 'RANGE'}],
AttributeDefinitions: [{AttributeName: 'id', AttributeType: 'S'}, {AttributeName: 'sk', AttributeType: 'S'}],
ProvisionedThroughput: {ReadCapacityUnits: 1, WriteCapacityUnits: 1},
},
],
port: 8000
}
5 changes: 3 additions & 2 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ module.exports = {
collectCoverageFrom: ['lib/**/*.js'],
coverageReporters: ['json', 'text'],
testEnvironment: 'node',
testPathIgnorePatterns: ['test/data', 'test/utilities'],
testPathIgnorePatterns: ['test/utilities'],
testMatch: ['**/test/**/*.[jt]s?(x)'],
setupFiles: ['<rootDir>/config/jest.setup.js']
setupFiles: ['<rootDir>/config/jest.setup.js'],
preset: '@shelf/jest-dynamodb',
}
79 changes: 79 additions & 0 deletions lib/dynamodb-context-store.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { DynamoDBClient, DynamoDBClientConfig, KeySchemaElement } from '@aws-sdk/client-dynamodb'

export interface ContextObject {
installedAppId: string
locationId: string
locale: string
authToken: string
refreshToken: string
config: any
state: any
}

export interface DynamoDBContextStore {
get(installedAppId: string): Promise<ContextObject>
put(installedAppId: string, context: ContextObject): Promise<void>
update(installedAppId: string, context: Partial<ContextObject>): Promise<void>
delete(installedAppId: string): Promise<void>
}

export interface ExtendedKeySchemaElement extends KeySchemaElement {
/**
* The type of the attribute. This value is used only for creating the table when autoCreate is true.
*/
AttributeType?: string

/**
* The value used for the sort key when inserting records into the table. All context store values have the same
* sort key value, since there is only one record per partition key.
*/
AttributeValue: any
}

export interface TableOptions {
/**
* The name of the table to create or use.
*/
name: string

/**
* The attribute name of the partition key of the table. Defaults to 'id' if not specified.
*/
hashKey?: string

/**
* The sort key of the table. This can be a string or an extended KeySchemaElement object. If it is a string it is assumed
* to be a RANGE key with a string attribute type. If it is an object and autoCreate is true, then the AttributeType
* property must be supplied. If autoCreate is false, then it can be omitted.
*/
sortKey?: string | ExtendedKeySchemaElement

/**
* The prefix prepended to the installedAppId when constructing the partition key. Defaults to 'ctx:' if not specified.
*/
prefix?: string

/**
* The billing mode of the table. Defaults to PAY_PER_REQUEST if not specified.
*/
billingMode?: 'PAY_PER_REQUEST' | 'PROVISIONED'

/**
* The number of write capability units used when creating the table. Only used if autoCreate is true.
* Defaults to 1 if not specified.
*/
writeCapacityUnits?: number

/**
* The number of read capability units used when creating the table. Only used if autoCreate is true.
* Defaults to 1 if not specified.
*/
readCapacityUnits?: number
}

export interface DynamoDBContextStoreOptions {
table: TableOptions
config: DynamoDBClientConfig
autoCreate?: boolean
client?: DynamoDBClient
}
Loading