Skip to content

Commit 56cec15

Browse files
committed
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.
1 parent 017d197 commit 56cec15

File tree

4 files changed

+144
-52
lines changed

4 files changed

+144
-52
lines changed

lib/backend.js

+89-41
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ const path = require('path')
33
const fs = require('fs')
44
const os = require('os')
55
const errorhandler = require('errorhandler')
6-
const { extractKey, extractKeysForItems, parseKey, doSearch } = require('./util')
6+
const { extractKey, extractKeysForItems, parseKey, doSearch, parseRawKey} = require('./util')
77
const { purgeTable } = require('./actions/purgeTable')
88
const asyncMiddleware = require('./utils/asyncMiddleware')
99
const bodyParser = require('body-parser')
@@ -129,6 +129,8 @@ exports.createServer = (dynamodb, docClient) => {
129129

130130
const listTables = (...args) => dynamodb.listTables(...args).promise()
131131
const describeTable = (...args) => dynamodb.describeTable(...args).promise()
132+
const getRawItem = (...args) => dynamodb.getItem(...args).promise()
133+
const putRawItem = (...args) => dynamodb.putItem(...args).promise()
132134
const getItem = (...args) => docClient.get(...args).promise()
133135
const putItem = (...args) => docClient.put(...args).promise()
134136
const deleteItem = (...args) => docClient.delete(...args).promise()
@@ -553,7 +555,7 @@ exports.createServer = (dynamodb, docClient) => {
553555
})
554556
}))
555557

556-
app.delete('/tables/:TableName/items/:key', asyncMiddleware((req, res) => {
558+
app.delete('/tables/:TableName/*items/:key', asyncMiddleware((req, res) => {
557559
const TableName = req.params.TableName
558560
return describeTable({ TableName })
559561
.then(result => {
@@ -568,7 +570,7 @@ exports.createServer = (dynamodb, docClient) => {
568570
})
569571
}))
570572

571-
app.get('/tables/:TableName/add-item', asyncMiddleware((req, res) => {
573+
function addItemPage(req, res, isRaw) {
572574
const TableName = req.params.TableName
573575
return describeTable({ TableName })
574576
.then(result => {
@@ -578,90 +580,136 @@ exports.createServer = (dynamodb, docClient) => {
578580
const definition = table.AttributeDefinitions.find(attribute => {
579581
return attribute.AttributeName === key.AttributeName
580582
})
581-
Item[key.AttributeName] = definition.AttributeType === 'S' ? '' : 0
583+
584+
if (isRaw) {
585+
Item[key.AttributeName] = definition.AttributeType === 'S' ? {S: ''} : {'N': 0}
586+
} else {
587+
Item[key.AttributeName] = definition.AttributeType === 'S' ? '' : 0
588+
}
582589
})
590+
583591
res.render('item', {
584592
Table: table,
593+
isRaw: isRaw,
585594
TableName: req.params.TableName,
586595
Item: Item,
587596
isNew: true
588597
})
589598
})
599+
}
600+
601+
app.get('/tables/:TableName/add-raw-item', asyncMiddleware((req, res) => {
602+
addItemPage(req, res, true)
590603
}))
591604

592-
app.get('/tables/:TableName/items/:key', asyncMiddleware((req, res) => {
605+
app.get('/tables/:TableName/add-item', asyncMiddleware((req, res) => {
606+
addItemPage(req, res, false)
607+
}))
608+
609+
function getItemPage(req, res, isRaw) {
593610
const TableName = req.params.TableName
594611
return describeTable({ TableName })
595612
.then(result => {
596613
const params = {
597614
TableName,
598-
Key: parseKey(req.params.key, result.Table)
615+
Key: (isRaw ? parseRawKey : parseKey)(req.params.key, result.Table)
599616
}
600617

601-
return getItem(params).then(response => {
618+
return (isRaw ? getRawItem : getItem)(params).then(response => {
602619
if (!response.Item) {
603620
return res.status(404).send('Not found')
604621
}
605622
res.render('item', {
606623
Table: result.Table,
624+
isRaw: isRaw,
607625
TableName: req.params.TableName,
608626
Item: response.Item,
609627
isNew: false
610628
})
611629
})
612630
})
631+
}
632+
633+
app.get('/tables/:TableName/items/:key', asyncMiddleware((req, res) => {
634+
return getItemPage(req, res, false)
613635
}))
614636

615-
app.put(
616-
'/tables/:TableName/add-item',
617-
bodyParser.json({ limit: '500kb' }),
618-
asyncMiddleware((req, res) => {
619-
const TableName = req.params.TableName
620-
return describeTable({ TableName })
621-
.then(description => {
637+
app.get('/tables/:TableName/raw-items/:key', asyncMiddleware((req, res) => {
638+
return getItemPage(req, res, true)
639+
}))
640+
641+
function createNewItem(req, res, isRaw) {
642+
const TableName = req.params.TableName
643+
return describeTable({ TableName })
644+
.then(description => {
645+
const params = {
646+
TableName,
647+
Item: req.body
648+
}
649+
650+
return (isRaw ? putRawItem : putRawItem)(params).then(() => {
651+
const Key = extractKey(req.body, description.Table.KeySchema)
622652
const params = {
623653
TableName,
624-
Item: req.body
654+
Key
625655
}
626-
627-
return putItem(params).then(() => {
628-
const Key = extractKey(req.body, description.Table.KeySchema)
629-
const params = {
630-
TableName,
631-
Key
656+
return (isRaw ? getRawItem : getItem)(params).then(response => {
657+
if (!response.Item) {
658+
return res.status(404).send('Not found')
632659
}
633-
return getItem(params).then(response => {
634-
if (!response.Item) {
635-
return res.status(404).send('Not found')
636-
}
637-
return res.json(Key)
638-
})
660+
return res.json(Key)
639661
})
640662
})
663+
})
664+
}
665+
666+
app.put(
667+
'/tables/:TableName/add-item',
668+
bodyParser.json({ limit: '500kb' }),
669+
asyncMiddleware((req, res) => {
670+
createNewItem(req, res, false)
641671
}))
642672

643673
app.put(
644-
'/tables/:TableName/items/:key',
674+
'/tables/:TableName/add-raw-item',
645675
bodyParser.json({ limit: '500kb' }),
646676
asyncMiddleware((req, res) => {
647-
const TableName = req.params.TableName
648-
return describeTable({ TableName })
649-
.then(result => {
677+
createNewItem(req, res, true)
678+
}))
679+
680+
function updateItem(req, res, isRaw) {
681+
const TableName = req.params.TableName
682+
return describeTable({ TableName })
683+
.then(result => {
684+
const params = {
685+
TableName,
686+
Item: req.body
687+
}
688+
689+
return (isRaw ? putRawItem : putItem)(params).then(() => {
650690
const params = {
651691
TableName,
652-
Item: req.body
692+
Key: parseRawKey(req.params.key, result.Table)
653693
}
654-
655-
return putItem(params).then(() => {
656-
const params = {
657-
TableName,
658-
Key: parseKey(req.params.key, result.Table)
659-
}
660-
return getItem(params).then(response => {
661-
return res.json(response.Item)
662-
})
694+
return (isRaw ? getRawItem : getItem)(params).then(response => {
695+
return res.json(response.Item)
663696
})
664697
})
698+
})
699+
}
700+
701+
app.put(
702+
'/tables/:TableName/raw-items/:key',
703+
bodyParser.json({ limit: '500kb' }),
704+
asyncMiddleware((req, res) => {
705+
updateItem(req, res, true)
706+
}))
707+
708+
app.put(
709+
'/tables/:TableName/items/:key',
710+
bodyParser.json({ limit: '500kb' }),
711+
asyncMiddleware((req, res) => {
712+
updateItem(req, res, false)
665713
}))
666714

667715
app.use((err, req, res, next) => {

lib/util.js

+32-10
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,11 @@ exports.extractKey = function(item, KeySchema) {
77
}
88

99
exports.parseKey = function(keys, table) {
10-
const splitKeys = keys.split(',')
10+
return doParseKey(keys, table, typecastKey)
11+
}
1112

12-
return table.KeySchema.reduce((prev, current, index) => {
13-
return Object.assign({}, prev, {
14-
[current.AttributeName]: typecastKey(
15-
current.AttributeName,
16-
splitKeys[index],
17-
table
18-
)
19-
})
20-
}, {})
13+
exports.parseRawKey = function(keys, table) {
14+
return doParseKey(keys, table, typecastRawKey)
2115
}
2216

2317
exports.extractKeysForItems = function(Items) {
@@ -110,6 +104,34 @@ function doSearch(docClient, tableName, scanParams, limit, startKey, progress,
110104
return getNextBite(params)
111105
}
112106

107+
function doParseKey(keys, table, typecastFunction) {
108+
const splitKeys = keys.split(',')
109+
110+
return table.KeySchema.reduce((prev, current, index) => {
111+
return Object.assign({}, prev, {
112+
[current.AttributeName]: typecastFunction(
113+
current.AttributeName,
114+
splitKeys[index],
115+
table
116+
)
117+
})
118+
}, {})
119+
}
120+
121+
function typecastRawKey(keyName, keyValue, table) {
122+
const definition = table.AttributeDefinitions.find(attribute => {
123+
return attribute.AttributeName === keyName
124+
})
125+
if (definition) {
126+
switch (definition.AttributeType) {
127+
case 'N':
128+
return {'N': Number(keyValue) }
129+
case 'S':
130+
return { 'S': String(keyValue) }
131+
}
132+
}
133+
return { 'S': String(keyValue) }
134+
}
113135

114136
function typecastKey(keyName, keyValue, table) {
115137
const definition = table.AttributeDefinitions.find(attribute => {

views/item.ejs

+21
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,15 @@
6969
})
7070
}
7171
</script>
72+
<div>
73+
<input type="checkbox"
74+
id='rawCheckbox'
75+
onclick='handleCheckboxClick()'
76+
<% if (isRaw) { %>
77+
checked
78+
<% } %>
79+
> Raw
80+
</div>
7281
<button
7382
class='btn btn-primary'
7483
id='saveButton'
@@ -79,6 +88,18 @@
7988
</button>
8089

8190
<script>
91+
function handleCheckboxClick (event) {
92+
var checked = document.getElementById('rawCheckbox').checked
93+
if (!checked && <%= isRaw %> && <%= !isNew %>) {
94+
window.location.href = window.location.href.replace('/raw-items/', '/items/')
95+
} else if (checked && <%= !isRaw %> && <%= !isNew %>) {
96+
window.location.href = window.location.href.replace('/items/', '/raw-items/')
97+
} else if (!checked && <%= isRaw %> && <%= isNew %>) {
98+
window.location.href = window.location.href.replace('/add-raw-item', '/add-item')
99+
} else if (checked && <%= !isRaw %> && <%= isNew %>) {
100+
window.location.href = window.location.href.replace('/add-item', '/add-raw-item')
101+
}
102+
}
82103
function handleDeleteClick (event) {
83104
event.preventDefault()
84105
fetch(document.location.pathname, {

views/scan.ejs

+2-1
Original file line numberDiff line numberDiff line change
@@ -383,7 +383,8 @@
383383
if (data.Items.length) {
384384
$('#items-container').append(data.Items.map(item => {
385385
const viewUrl = '/tables/<%= Table.TableName %>/items/' + encodeURIComponent(Object.values(item.__key).join(','))
386-
const rowEl = $('<tr><td><a href="' + viewUrl + '">View</a></td></tr>')
386+
const viewRawUrl = '/tables/<%= Table.TableName %>/raw-items/' + encodeURIComponent(Object.values(item.__key).join(','))
387+
const rowEl = $('<tr><td><div class="btn-group"><a class="btn btn-primary" href="' + viewUrl + '">View</a><a class="btn btn-primary" href="' + viewRawUrl + '">View Raw</a></div></td></tr>')
387388
388389
for (const column of data.uniqueKeys) {
389390
const columnEl = $('<td></td>')

0 commit comments

Comments
 (0)