class Stax::Cmd::DynamoDB

Constants

COLORS

Public Instance Methods

backup(id = nil) click to toggle source
# File lib/stax/mixin/dynamodb/backup.rb, line 24
def backup(id = nil)
  name = my.resource(id)
  if options[:create]
    create_backup(name, options[:create])
  else
    list_backups(name)
  end
end
client(port) click to toggle source

client for dynamodb-local endpoint on given port

# File lib/stax/mixin/dynamodb/local.rb, line 21
def client(port)
  @_client ||= ::Aws::DynamoDB::Client.new(endpoint: "http://localhost:#{port}")
end
count(id) click to toggle source
# File lib/stax/mixin/dynamodb.rb, line 81
def count(id)
  puts Aws::DynamoDB.count(table_name: my.resource(id))
end
create_backup(table_name, backup_name) click to toggle source
# File lib/stax/mixin/dynamodb/backup.rb, line 13
def create_backup(table_name, backup_name)
  backup_name = Time.now.utc.strftime("#{table_name}-%Y%m%d%H%M%S") if backup_name == 'create' # thor option empty
  debug("Creating backup #{backup_name} from #{table_name}")
  Aws::DynamoDB.create_backup(table_name, backup_name).tap do |b|
    puts YAML.dump(stringify_keys(b.to_hash))
  end
end
dynamo_local_create(payload, port) click to toggle source

create table

# File lib/stax/mixin/dynamodb/local.rb, line 51
def dynamo_local_create(payload, port)
  client(port).create_table(dynamo_ruby_payload(payload))
rescue ::Aws::DynamoDB::Errors::ResourceInUseException => e
  warn(e.message)       # table exists
rescue Seahorse::Client::NetworkingError => e
  warn(e.message)       # dynamodb-local probably not running
end
dynamo_local_tables() click to toggle source

get CFN template and return hash of table configs

# File lib/stax/mixin/dynamodb/local.rb, line 26
def dynamo_local_tables
  JSON.parse(my.cfn_template).fetch('Resources', {}).select do |_, v|
    v['Type'] == 'AWS::DynamoDB::Table'
  end
end
dynamo_payload_from_template(id, template) click to toggle source

convert some CFN properties to their SDK equivalents

# File lib/stax/mixin/dynamodb/local.rb, line 33
def dynamo_payload_from_template(id, template)
  template['Properties'].tap do |p|
    p['TableName'] ||= id # use logical id if no name in template
    p['StreamSpecification']&.merge!( 'StreamEnabled' => true )
    p['SSESpecification'] &&= { 'Enabled' => p.dig('SSESpecification', 'SSEEnabled') }
    p.delete('TimeToLiveSpecification')
    p.delete('Tags')
  end
end
dynamo_ruby_payload(payload) click to toggle source

convert property names to ruby SDK form

# File lib/stax/mixin/dynamodb/local.rb, line 44
def dynamo_ruby_payload(payload)
  payload&.deep_transform_keys do |key|
    key.to_s.underscore.to_sym
  end
end
gsi(id) click to toggle source
# File lib/stax/mixin/dynamodb.rb, line 53
def gsi(id)
  print_table Aws::DynamoDB.gsi(my.resource(id)).map { |i|
    hash  = i.key_schema.find{ |k| k.key_type == 'HASH' }&.attribute_name
    range = i.key_schema.find{ |k| k.key_type == 'RANGE' }&.attribute_name
    [i.index_name, hash, range, i.projection.projection_type, i.index_size_bytes, i.item_count]
  }.sort
end
keys(id) click to toggle source
# File lib/stax/mixin/dynamodb.rb, line 71
def keys(id)
  print_table Aws::DynamoDB.keys(my.resource(id))
end
list_backups(name) click to toggle source
# File lib/stax/mixin/dynamodb/backup.rb, line 6
def list_backups(name)
  debug("Backups for #{name}")
  print_table Aws::DynamoDB.list_backups(table_name: name).map { |b|
    [b.backup_name, color(b.backup_status, COLORS), b.table_name, b.backup_creation_date_time, human_bytes(b.backup_size_bytes)]
  }
end
local_create() click to toggle source
# File lib/stax/mixin/dynamodb/local.rb, line 64
def local_create
  tables = dynamo_local_tables
  tables.slice!(*options[:tables]) if options[:tables]

  tables.each do |id, value|
    payload = dynamo_payload_from_template(id, value)
    payload = my.dynamo_local_payload_hacks(id, payload) # apply user-supplied hacks
    if options[:payload]
      puts JSON.pretty_generate(payload)
    else
      puts "create table #{id}"
      dynamo_local_create(payload, options[:port])
    end
  end
end
local_delete() click to toggle source
# File lib/stax/mixin/dynamodb/local.rb, line 83
def local_delete
  tables = dynamo_local_tables
  tables.slice!(*options[:tables]) if options[:tables]

  tables.each do |id,_value|
    puts "deleting table #{id}"
    client(options[:port]).delete_table(table_name: id)
  end
end
lsi(id) click to toggle source
# File lib/stax/mixin/dynamodb.rb, line 62
def lsi(id)
  print_table Aws::DynamoDB.lsi(my.resource(id)).map { |i|
    hash  = i.key_schema.find{ |k| k.key_type == 'HASH' }&.attribute_name
    range = i.key_schema.find{ |k| k.key_type == 'RANGE' }&.attribute_name
    [i.index_name, hash, range, i.projection.projection_type, i.index_size_bytes, i.item_count]
  }.sort
end
print_throughput(ids) click to toggle source
put(id) click to toggle source
# File lib/stax/mixin/dynamodb.rb, line 104
def put(id)
  table = my.resource(id)
  count = 0
  $stdin.each do |line|
    Aws::DynamoDB.put(table_name: table, item: JSON.parse(line))
    print '.' if options[:verbose]
    count += 1
  end
  print "\n" if options[:verbose]
  puts "put #{count} items to #{table}"
end
query(id, hash_value, range_value = nil) click to toggle source
# File lib/stax/mixin/dynamodb.rb, line 86
def query(id, hash_value, range_value = nil)
  name = my.resource(id)
  k = Aws::DynamoDB.keys(name)
  Aws::DynamoDB.query(
    table_name: name,
    expression_attribute_values: {
      ':h' => hash_value,
      ':r' => range_value,
    }.compact,
    key_condition_expression: [
      "#{k[:hash]} = :h",
      range_value ? "#{k[:range]} = :r" : nil,
    ].compact.join(' and '),
  )
end
restore(arn, table) click to toggle source
# File lib/stax/mixin/dynamodb/backup.rb, line 34
def restore(arn, table)
  debug("Creating table #{table} from backup #{arn}")
  Aws::DynamoDB.restore_backup(table, arn)
end
scan(id) click to toggle source
# File lib/stax/mixin/dynamodb.rb, line 76
def scan(id)
  Aws::DynamoDB.scan(table_name: my.resource(id))
end
stack_table_names(logical_ids) click to toggle source

get table names from logical IDs, return all tables if nil

# File lib/stax/mixin/dynamodb.rb, line 34
def stack_table_names(logical_ids)
  stack_tables.tap do |tables|
    tables.select! { |t| logical_ids.include?(t.logical_resource_id) } if logical_ids
  end.map(&:physical_resource_id)
end
stack_tables() click to toggle source
# File lib/stax/mixin/dynamodb.rb, line 29
def stack_tables
  Aws::Cfn.resources_by_type(my.stack_name, 'AWS::DynamoDB::Table')
end
tables() click to toggle source
# File lib/stax/mixin/dynamodb.rb, line 42
def tables
  debug("Dynamo tables for stack #{my.stack_name}")
  print_table stack_tables.map { |r|
    t = Aws::DynamoDB.table(r.physical_resource_id)
    g = Aws::DynamoDB.global_table(r.physical_resource_id)
    regions = g.nil? ? '-' : g.replication_group.map(&:region_name).sort.join(',')
    [ t.table_name, color(t.table_status, COLORS), t.item_count, t.table_size_bytes, t.creation_date_time, regions ]
  }
end
throughput() click to toggle source
# File lib/stax/mixin/dynamodb/throughput.rb, line 57
def throughput
  if options[:write] || options[:read]
    update_throughput(options[:tables], options[:read], options[:write])
  else
    print_throughput(options[:tables])
  end
end
update_throughput(ids, read, write) click to toggle source
# File lib/stax/mixin/dynamodb/throughput.rb, line 20
def update_throughput(ids, read, write)
  debug("Updating throughput on #{ids ? ids.count : 'all'} tables")
  stack_table_names(ids).each do |name|
    puts name
    table = Aws::DynamoDB.table(name)
    begin
      Aws::DynamoDB.client.update_table(
        table_name: name,
        provisioned_throughput: {
          read_capacity_units:  read  || table.provisioned_throughput.read_capacity_units,
          write_capacity_units: write || table.provisioned_throughput.write_capacity_units,
        },
        global_secondary_index_updates: table.global_secondary_indexes&.map do |gsi|
          {
            update: {
              index_name: gsi.index_name,
              provisioned_throughput: {
                read_capacity_units:  read  || gsi.provisioned_throughput.read_capacity_units,
                write_capacity_units: write || gsi.provisioned_throughput.write_capacity_units,
              }
            }
          }
        end
      )
    rescue ::Aws::DynamoDB::Errors::ValidationException
      puts 'no change'
    rescue ::Aws::DynamoDB::Errors::ResourceInUseException => e
      warn(e.message)
    end
  end
end