jstor-dynamodb
Version:
jstor dynamodb storage client strategy
134 lines (117 loc) • 5.08 kB
JavaScript
//documentation on the dynamodb client can be found here:
//https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-dynamodb/index.html
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
import {
DynamoDBDocumentClient,
GetCommand,
BatchGetCommand,
PutCommand,
DeleteCommand,
ScanCommand,
} from "@aws-sdk/lib-dynamodb";
import { get } from "lodash-es";
//options can be found here:
//https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-dynamodb/interfaces/dynamodbclientconfig.html
//to login use the credentials attribute
//{credentials: {accessKeyId, secretAccessKey, expiration, sessionToken}}
export default function(options = {}){
//ensure table name!
if(!options.table) throw new Error(`jstor dynamodb: A table name is required to use the dynamodb strategy`);
//add credentials and region if needed
const envs = {
region: process.env.AWS_DEFAULT_REGION || 'eu-west-1',
id: process.env.AWS_ACCESS_KEY_ID,
key: process.env.AWS_SECRET_ACCESS_KEY
};
if(!options.region) options.region = envs.region;
if(!options.keyAttribute) options.keyAttribute = '_id';
options.translatedKeyAttribute = options.keyAttribute.replace(/^_/, '#');
if(!options.credentials?.accessKeyId && envs.id) options.credentials.accessKeyId = envs.id;
if(!options.credentials?.secretAccessKey && envs.key) options.credentials.secretAccessKey = envs.key;
//create dynamodb client
const marshallOptions = {
convertEmptyValues: false, // false, by default.
removeUndefinedValues: false, // false, by default.
convertClassInstanceToMap: false, // false, by default.
};
const unmarshallOptions = {
wrapNumbers: false, // false, by default.
};
const translateConfig = { marshallOptions, unmarshallOptions };
//STORE LOGIC
return function(store){
const defaultStoreOptions = {
cacheOptions: {
keys: {
maxAge: (15 * 60)
},
files: {
maxAge: (1 * 60)
}
}
};
store.client = new DynamoDBClient(options);
store.docClient = new DynamoDBDocumentClient(store.client, translateConfig);
store.setOptions(Object.assign(defaultStoreOptions, options));
function getCmdParams(key, usingExpression){
const params = {
TableName: options.table
};
if(usingExpression && options.translatedKeyAttribute !== options.keyAttribute){
params.ExpressionAttributeNames = {
[options.translatedKeyAttribute]: options.keyAttribute
}
}
if(key) params.Key = {
[options.keyAttribute]: key
};
return params;
}
return {
async get(key){
const getParams = getCmdParams(key);
const cmd = new GetCommand(getParams);
const result = await store.docClient.send(cmd);
if(result.Item) return result.Item;
},
async save(key, document){
const saveParams = getCmdParams();
document[options.keyAttribute] = key;
saveParams.Item = document;
const cmd = new PutCommand(saveParams);
return store.client.send(cmd);
},
async remove(key){
const deleteParams = getCmdParams(key);
const cmd = new DeleteCommand(deleteParams);
return store.client.send(cmd);
},
async keys(){
const queryParams = getCmdParams(null, true);
queryParams.ProjectionExpression = options.translatedKeyAttribute;
let cmd = new ScanCommand(queryParams);
const result = await store.client.send(cmd);
if(result.Items && Array.isArray(result.Items)){
return result.Items.map(item => item[options.keyAttribute])
}else{
throw new Error(`Could not resolve keys`);
}
},
async batch(ids){
const params = {
RequestItems: {
[options.table]: {
Keys: ids.map(id => ({[options.keyAttribute]: id}))
}
}
};
const cmd = new BatchGetCommand(params);
const result = await store.client.send(cmd);
const returnDocs = get(result, `Responses.${options.table}`);
if(Array.isArray(returnDocs)) return returnDocs.map(
document => ({key: document[options.keyAttribute], document:document})
);
}
};
}
}