dynalite
Version: 
An implementation of Amazon's DynamoDB built on LevelDB
111 lines (92 loc) • 3.96 kB
JavaScript
var crypto = require('crypto')
module.exports = function createTable (store, data, cb) {
  var key = data.TableName, tableDb = store.tableDb
  tableDb.lock(key, function (release) {
    cb = release(cb)
    tableDb.get(key, function (err, existingTable) {
      if (err && err.name != 'NotFoundError') return cb(err)
      // Check if table exists and is valid
      if (!err && existingTable && typeof existingTable === 'object' && existingTable.TableStatus) {
        err = new Error
        err.statusCode = 400
        err.body = {
          __type: 'com.amazonaws.dynamodb.v20120810#ResourceInUseException',
          message: '',
        }
        return cb(err)
      }
      // If table exists but is corrupted, delete it first
      if (!err && existingTable && (!existingTable.TableStatus || typeof existingTable !== 'object')) {
        tableDb.del(key, function () {
          // Ignore deletion errors and proceed with creation
          createNewTable()
        })
        return
      }
      // Table doesn't exist, create it
      createNewTable()
      function createNewTable () {
        data.TableArn = 'arn:aws:dynamodb:' + tableDb.awsRegion + ':' + tableDb.awsAccountId + ':table/' + data.TableName
        data.TableId = uuidV4()
        data.CreationDateTime = Date.now() / 1000
        data.ItemCount = 0
        if (!data.ProvisionedThroughput) {
          data.ProvisionedThroughput = { ReadCapacityUnits: 0, WriteCapacityUnits: 0 }
        }
        data.ProvisionedThroughput.NumberOfDecreasesToday = 0
        data.TableSizeBytes = 0
        data.TableStatus = 'CREATING'
        if (data.BillingMode == 'PAY_PER_REQUEST') {
          data.BillingModeSummary = { BillingMode: 'PAY_PER_REQUEST' }
          data.TableThroughputModeSummary = { TableThroughputMode: 'PAY_PER_REQUEST' }
          delete data.BillingMode
        }
        if (data.LocalSecondaryIndexes) {
          data.LocalSecondaryIndexes.forEach(function (index) {
            index.IndexArn = 'arn:aws:dynamodb:' + tableDb.awsRegion + ':' + tableDb.awsAccountId + ':table/' +
            data.TableName + '/index/' + index.IndexName
            index.IndexSizeBytes = 0
            index.ItemCount = 0
          })
        }
        if (data.GlobalSecondaryIndexes) {
          data.GlobalSecondaryIndexes.forEach(function (index) {
            index.IndexArn = 'arn:aws:dynamodb:' + tableDb.awsRegion + ':' + tableDb.awsAccountId + ':table/' +
            data.TableName + '/index/' + index.IndexName
            index.IndexSizeBytes = 0
            index.ItemCount = 0
            index.IndexStatus = 'CREATING'
            if (!index.ProvisionedThroughput) {
              index.ProvisionedThroughput = { ReadCapacityUnits: 0, WriteCapacityUnits: 0 }
            }
            index.ProvisionedThroughput.NumberOfDecreasesToday = 0
          })
        }
        tableDb.put(key, data, function (err) {
          if (err) return cb(err)
          setTimeout(function () {
            // Shouldn't need to lock/fetch as nothing should have changed
            data.TableStatus = 'ACTIVE'
            if (data.GlobalSecondaryIndexes) {
              data.GlobalSecondaryIndexes.forEach(function (index) {
                index.IndexStatus = 'ACTIVE'
              })
            }
            if (data.BillingModeSummary) {
              data.BillingModeSummary.LastUpdateToPayPerRequestDateTime = data.CreationDateTime
            }
            tableDb.put(key, data, function (err) {
              if (err && !/Database is (not open|closed)/.test(err)) console.error(err.stack || err)
            })
          }, store.options.createTableMs)
          cb(null, { TableDescription: data })
        })
      }
    })
  })
}
function uuidV4 () {
  var bytes = crypto.randomBytes(14).toString('hex')
  return bytes.slice(0, 8) + '-' + bytes.slice(8, 12) + '-4' + bytes.slice(13, 16) + '-' +
    bytes.slice(16, 20) + '-' + bytes.slice(20, 28)
}