Skip to content
This repository has been archived by the owner on Jun 27, 2024. It is now read-only.

Commit

Permalink
add seed settings
Browse files Browse the repository at this point in the history
  • Loading branch information
Ankcorn committed Dec 16, 2020
1 parent 5883f8a commit 3cfac2f
Show file tree
Hide file tree
Showing 5 changed files with 301 additions and 858 deletions.
41 changes: 25 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,25 +1,34 @@
# serverless-dynalite

A Serverless plugin to run Dynalite locally to handle DynamoDB development. Can watch for table config changes.
A Serverless plugin to run Dynalite locally to handle DynamoDB development.

Integrates with `serverless-offline`. Also works without `serverless-offline` by running
Integrates with `serverless-offline`.

```
serverless dynalite start
```
## Getting Started

to start dynalite with the tables specified in serverless.yml. Or:
Install the node package with npm or yarn

```bash
npm install @nearst/serverless-dynalite --save-dev
```
serverless dynalite watch
```

to listen for changes to the serverless.yml file and add tables accordingly

```bash
yarn add -D @nearst/serverless-dynalite
```

## Options:

* -p `port` to specify the port (optional, defaults to 4567)
* -d `dir` to create a dynalite db file instead of using the in-memory store (optional)

Something missing? More documentation? All PRs welcome at https://github.com/sdd/serverless-dynalite
Once the package is installed add it to the plugins section of your `serverless.yml`. The serverless offline plugin also needs to be installed

```yaml
plugins:
- '@nearst/serverless-dynalite'
- serverless-offline
custom:
# This is optional
dynalite:
region: localhost
port: 8000
dir: ./
seed:
- table: table-1
source: ./seed/table1.js
```
20 changes: 5 additions & 15 deletions example/serverless.yml
Original file line number Diff line number Diff line change
@@ -1,29 +1,19 @@
service: serverless-dynalite-example

plugins:
- serverless-offline
- serverless-dynalite
- serverless-offline

custom:
dynalite:
start:
port: 4567
region: us-east-1
port: 4567
region: us-east-1

provider:
name: aws
runtime: nodejs6.10
runtime: nodejs12.x
stage: dev

# functions:
# rulesApi:
# handler: sample.handler
# events:
# - http:
# method: GET
# path: /
# cors: true

resources:
Resources:
ResultsTable:
Expand Down Expand Up @@ -53,4 +43,4 @@ resources:
- dynamodb:PutItem
Resource: arn:aws:dynamodb:*:*:table/SampleTable
Roles:
- Ref: IamRoleLambdaExecution
- Ref: IamRoleLambdaExecution
138 changes: 33 additions & 105 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,80 +1,37 @@
'use strict';

require("babel-polyfill");

const _ = require('lodash');
const Dynalite = require('dynalite');
const chokidar = require('graceful-chokidar');
const AWS = require('aws-sdk');

const DEFAULT_PORT = 4567;
const path = require('path')
const DEFAULT_PORT = 8000;
const DEFAULT_REGION = 'localhost';
const DEFAULT_DIR = undefined;

const PORT_OPTIONS = {
shortcut: 'p',
usage: `the port number that dynalite will listen on (default ${ DEFAULT_PORT })`,
required: false
};

const DIR_OPTIONS = {
shortcut: 'd',
usage: `the directory dynalite will store its db file (default In-Memory)`,
required: false
};

class ServerlessDynalite {

constructor(serverless, options) {

this.serverless = serverless;

this.service = serverless.service;

this.log = serverless.cli.log.bind(serverless.cli);
this.config = this.service.custom && this.service.custom.dynalite || {};
this.options = options;

this.commands = {
dynalite: {
commands: {
start: {
usage: 'start a persistent dynalite server',
lifecycleEvents: [ 'startHandler' ],
options: {
port: PORT_OPTIONS,
dir: DIR_OPTIONS
}
},
watch: {
usage: 'start persistent dynalite server and watch for table definition changes',
lifecycleEvents: [ 'watchHandler' ],
options: {
port: PORT_OPTIONS,
dir: DIR_OPTIONS
}
}
}
}
};

this.hooks = {
"dynalite:start:startHandler": this.startHandler.bind(this),
"dynalite:watch:watchHandler": this.watchHandler.bind(this),
"before:offline:start:init": this.watchHandler.bind(this),
"before:offline:start:init": this.startHandler.bind(this),
"before:offline:start:end": this.endHandler.bind(this)
};
}

get port() {
return _.get(this, ['config', 'start', 'port'], DEFAULT_PORT);
return this.config.port || DEFAULT_PORT;
}

get dir() {
return _.get(this, ['config', 'start', 'dir'], DEFAULT_DIR);
return this.config.dir || DEFAULT_DIR
}

get region() {
return _.get(this, ['config', 'start', 'region'], DEFAULT_REGION);
return this.config.region || DEFAULT_REGION
}

get dynamodb() {
Expand All @@ -89,26 +46,14 @@ class ServerlessDynalite {
};

this._dynamodb = {
converter: AWS.DynamoDB.Converter,
raw: new AWS.DynamoDB(dynamoOptions),
doc: new AWS.DynamoDB.DocumentClient(dynamoOptions)
};

return this._dynamodb;
}

async watchHandler() {
await this.startHandler();

this.watcher = chokidar.watch('./serverless.yml', { persistent: true, interval: 1000 })
.on('change', async () => {
this.log('serverless.yml changed, updating...');
await this.reloadService();
this.updateTables();
});

this.log('Listening for table additions / deletions.');
}

async startHandler() {
this.dynalite = Dynalite({ createTableMs: 0, path: this.dir });
await new Promise(
Expand All @@ -120,53 +65,36 @@ class ServerlessDynalite {
}

endHandler() {
if (this.watcher) {
this.watcher.close();
}

if (this.dynalite) {
this.dynalite.close();
}
this.dynalite.close();
}

async reloadService() {
const options = this.serverless.processedInput.options;
await this.service.load(options);
await this.serverless.variables.populateService(options);
await this.service.setFunctionNames(options);
await this.service.mergeResourceArrays();
await this.service.validate();
}

async updateTables() {
const requiredTables = _.map(
_.filter(
_.values(
_.get(this.service, ['resources', 'Resources'], {})
),
{ 'Type': 'AWS::DynamoDB::Table' }
),
'Properties'
);
this.log(`Tables in config: ${ JSON.stringify(_.map(requiredTables, 'TableName')) }`);

const currentTables = await this.dynamodb.raw.listTables({}).promise();
this.log(`Current Tables: ${ JSON.stringify(currentTables.TableNames) }`);

const missingTables = _.reject(requiredTables,
({ TableName }) => _.includes(currentTables.TableNames, TableName)
);
this.log(`Missing Tables: ${ JSON.stringify(_.map(missingTables, 'TableName')) }`);

_.forEach(missingTables, async table => {
this.log(`Creating table ${ table.TableName }...`);
await this.dynamodb.raw.createTable(table).promise();
});
if(this.service.resources && this.service.resources.Resources) {
let resources = Object.values(this.service.resources.Resources);
let tables = resources.filter(el => el.Type === 'AWS::DynamoDB::Table').map(el => ({
TableName: el.Properties.TableName,
AttributeDefinitions: el.Properties.AttributeDefinitions,
KeySchema: el.Properties.KeySchema,
BillingMode: el.Properties.BillingMode,
GlobalSecondaryIndexes: el.Properties.GlobalSecondaryIndexes
}))
for(let table of tables) {
try {
this.log(`Dynalite creating table ${table.TableName}`)
await this.dynamodb.raw.createTable(table).promise()
} catch(e) {
this.log(e.message)
}
}

setTimeout(async () => {
const finalTables = await this.dynamodb.raw.listTables({}).promise();
this.log(`Current Tables: ${ JSON.stringify(finalTables.TableNames) }`);
}, 1000);
if(this.config.seed) {
for(let seed of this.config.seed) {
let items = require(path.join(process.cwd(), seed.source))
await Promise.all(items.map(Item => this.dynamodb.doc.put({ TableName: seed.table, Item}).promise()))
}
}
}
}
}

Expand Down
Loading

0 comments on commit 3cfac2f

Please sign in to comment.