From aca51b1a0192a97b6aa25f7788f473d75b405652 Mon Sep 17 00:00:00 2001 From: Bo Zhang Date: Sun, 20 Feb 2022 14:45:38 +0800 Subject: [PATCH] Add raw mode Previous there is no support for complex dynamodb type (e.g. string set) because JSON array is always interpreted as dynamodb list. This commit adds a "raw" mode with which people can work with dynamodb type. --- lib/backend.js | 130 +++++++++++++++++++++++++++++++++---------------- lib/util.js | 42 ++++++++++++---- views/item.ejs | 21 ++++++++ views/scan.ejs | 3 +- 4 files changed, 144 insertions(+), 52 deletions(-) diff --git a/lib/backend.js b/lib/backend.js index bbc85d3..c52a98f 100644 --- a/lib/backend.js +++ b/lib/backend.js @@ -3,7 +3,7 @@ const path = require('path') const fs = require('fs') const os = require('os') const errorhandler = require('errorhandler') -const { extractKey, extractKeysForItems, parseKey, doSearch } = require('./util') +const { extractKey, extractKeysForItems, parseKey, doSearch, parseRawKey} = require('./util') const { purgeTable } = require('./actions/purgeTable') const asyncMiddleware = require('./utils/asyncMiddleware') const bodyParser = require('body-parser') @@ -129,6 +129,8 @@ exports.createServer = (dynamodb, docClient) => { const listTables = (...args) => dynamodb.listTables(...args).promise() const describeTable = (...args) => dynamodb.describeTable(...args).promise() + const getRawItem = (...args) => dynamodb.getItem(...args).promise() + const putRawItem = (...args) => dynamodb.putItem(...args).promise() const getItem = (...args) => docClient.get(...args).promise() const putItem = (...args) => docClient.put(...args).promise() const deleteItem = (...args) => docClient.delete(...args).promise() @@ -553,7 +555,7 @@ exports.createServer = (dynamodb, docClient) => { }) })) - app.delete('/tables/:TableName/items/:key', asyncMiddleware((req, res) => { + app.delete('/tables/:TableName/*items/:key', asyncMiddleware((req, res) => { const TableName = req.params.TableName return describeTable({ TableName }) .then(result => { @@ -568,7 +570,7 @@ exports.createServer = (dynamodb, docClient) => { }) })) - app.get('/tables/:TableName/add-item', asyncMiddleware((req, res) => { + function addItemPage(req, res, isRaw) { const TableName = req.params.TableName return describeTable({ TableName }) .then(result => { @@ -578,90 +580,136 @@ exports.createServer = (dynamodb, docClient) => { const definition = table.AttributeDefinitions.find(attribute => { return attribute.AttributeName === key.AttributeName }) - Item[key.AttributeName] = definition.AttributeType === 'S' ? '' : 0 + + if (isRaw) { + Item[key.AttributeName] = definition.AttributeType === 'S' ? {S: ''} : {'N': 0} + } else { + Item[key.AttributeName] = definition.AttributeType === 'S' ? '' : 0 + } }) + res.render('item', { Table: table, + isRaw: isRaw, TableName: req.params.TableName, Item: Item, isNew: true }) }) + } + + app.get('/tables/:TableName/add-raw-item', asyncMiddleware((req, res) => { + addItemPage(req, res, true) })) - app.get('/tables/:TableName/items/:key', asyncMiddleware((req, res) => { + app.get('/tables/:TableName/add-item', asyncMiddleware((req, res) => { + addItemPage(req, res, false) + })) + + function getItemPage(req, res, isRaw) { const TableName = req.params.TableName return describeTable({ TableName }) .then(result => { const params = { TableName, - Key: parseKey(req.params.key, result.Table) + Key: (isRaw ? parseRawKey : parseKey)(req.params.key, result.Table) } - return getItem(params).then(response => { + return (isRaw ? getRawItem : getItem)(params).then(response => { if (!response.Item) { return res.status(404).send('Not found') } res.render('item', { Table: result.Table, + isRaw: isRaw, TableName: req.params.TableName, Item: response.Item, isNew: false }) }) }) + } + + app.get('/tables/:TableName/items/:key', asyncMiddleware((req, res) => { + return getItemPage(req, res, false) })) - app.put( - '/tables/:TableName/add-item', - bodyParser.json({ limit: '500kb' }), - asyncMiddleware((req, res) => { - const TableName = req.params.TableName - return describeTable({ TableName }) - .then(description => { + app.get('/tables/:TableName/raw-items/:key', asyncMiddleware((req, res) => { + return getItemPage(req, res, true) + })) + + function createNewItem(req, res, isRaw) { + const TableName = req.params.TableName + return describeTable({ TableName }) + .then(description => { + const params = { + TableName, + Item: req.body + } + + return (isRaw ? putRawItem : putRawItem)(params).then(() => { + const Key = extractKey(req.body, description.Table.KeySchema) const params = { TableName, - Item: req.body + Key } - - return putItem(params).then(() => { - const Key = extractKey(req.body, description.Table.KeySchema) - const params = { - TableName, - Key + return (isRaw ? getRawItem : getItem)(params).then(response => { + if (!response.Item) { + return res.status(404).send('Not found') } - return getItem(params).then(response => { - if (!response.Item) { - return res.status(404).send('Not found') - } - return res.json(Key) - }) + return res.json(Key) }) }) + }) + } + + app.put( + '/tables/:TableName/add-item', + bodyParser.json({ limit: '500kb' }), + asyncMiddleware((req, res) => { + createNewItem(req, res, false) })) app.put( - '/tables/:TableName/items/:key', + '/tables/:TableName/add-raw-item', bodyParser.json({ limit: '500kb' }), asyncMiddleware((req, res) => { - const TableName = req.params.TableName - return describeTable({ TableName }) - .then(result => { + createNewItem(req, res, true) + })) + + function updateItem(req, res, isRaw) { + const TableName = req.params.TableName + return describeTable({ TableName }) + .then(result => { + const params = { + TableName, + Item: req.body + } + + return (isRaw ? putRawItem : putItem)(params).then(() => { const params = { TableName, - Item: req.body + Key: parseRawKey(req.params.key, result.Table) } - - return putItem(params).then(() => { - const params = { - TableName, - Key: parseKey(req.params.key, result.Table) - } - return getItem(params).then(response => { - return res.json(response.Item) - }) + return (isRaw ? getRawItem : getItem)(params).then(response => { + return res.json(response.Item) }) }) + }) + } + + app.put( + '/tables/:TableName/raw-items/:key', + bodyParser.json({ limit: '500kb' }), + asyncMiddleware((req, res) => { + updateItem(req, res, true) + })) + + app.put( + '/tables/:TableName/items/:key', + bodyParser.json({ limit: '500kb' }), + asyncMiddleware((req, res) => { + updateItem(req, res, false) })) app.use((err, req, res, next) => { diff --git a/lib/util.js b/lib/util.js index 4b5ec99..2593f87 100644 --- a/lib/util.js +++ b/lib/util.js @@ -7,17 +7,11 @@ exports.extractKey = function(item, KeySchema) { } exports.parseKey = function(keys, table) { - const splitKeys = keys.split(',') + return doParseKey(keys, table, typecastKey) +} - return table.KeySchema.reduce((prev, current, index) => { - return Object.assign({}, prev, { - [current.AttributeName]: typecastKey( - current.AttributeName, - splitKeys[index], - table - ) - }) - }, {}) +exports.parseRawKey = function(keys, table) { + return doParseKey(keys, table, typecastRawKey) } exports.extractKeysForItems = function(Items) { @@ -110,6 +104,34 @@ function doSearch(docClient, tableName, scanParams, limit, startKey, progress, return getNextBite(params) } +function doParseKey(keys, table, typecastFunction) { + const splitKeys = keys.split(',') + + return table.KeySchema.reduce((prev, current, index) => { + return Object.assign({}, prev, { + [current.AttributeName]: typecastFunction( + current.AttributeName, + splitKeys[index], + table + ) + }) + }, {}) +} + +function typecastRawKey(keyName, keyValue, table) { + const definition = table.AttributeDefinitions.find(attribute => { + return attribute.AttributeName === keyName + }) + if (definition) { + switch (definition.AttributeType) { + case 'N': + return {'N': Number(keyValue) } + case 'S': + return { 'S': String(keyValue) } + } + } + return { 'S': String(keyValue) } +} function typecastKey(keyName, keyValue, table) { const definition = table.AttributeDefinitions.find(attribute => { diff --git a/views/item.ejs b/views/item.ejs index c395bf2..d4e67b6 100644 --- a/views/item.ejs +++ b/views/item.ejs @@ -69,6 +69,15 @@ }) } +
+ + checked + <% } %> + > Raw +