/**
   * Wait for the table to reach the desired status and returns the table description
   *
   * @param dynamo Dynamo client to use
   * @param tableName Table name to poll status of
   * @param desiredStatus Desired {@link TableStatus} to wait for. If null this method simply waits
   *     until DescribeTable returns something non-null (i.e. any status)
   * @param timeout Timeout in milliseconds to continue to poll for desired status
   * @param interval Time to wait in milliseconds between poll attempts
   * @return Null if DescribeTables never returns a result, otherwise the result of the last poll
   *     attempt (which may or may not have the desired state)
   * @throws InterruptedException
   * @throws {@link IllegalArgumentException} If timeout or interval is invalid
   */
  private static TableDescription waitForTableDescription(
      final AmazonDynamoDB dynamo,
      final String tableName,
      TableStatus desiredStatus,
      final int timeout,
      final int interval)
      throws InterruptedException, IllegalArgumentException {
    if (timeout < 0) {
      throw new IllegalArgumentException("Timeout must be >= 0");
    }
    if (interval <= 0 || interval >= timeout) {
      throw new IllegalArgumentException("Interval must be > 0 and < timeout");
    }
    long startTime = System.currentTimeMillis();
    long endTime = startTime + timeout;

    TableDescription table = null;
    while (System.currentTimeMillis() < endTime) {
      try {
        table = dynamo.describeTable(new DescribeTableRequest(tableName)).getTable();
        if (desiredStatus == null || table.getTableStatus().equals(desiredStatus.toString())) {
          return table;
        }
      } catch (ResourceNotFoundException rnfe) {
        // ResourceNotFound means the table doesn't exist yet,
        // so ignore this error and just keep polling.
      }

      Thread.sleep(interval);
    }
    return table;
  }
  /**
   * Waits up to a specified amount of time for a specified DynamoDB table to move into the <code>
   * ACTIVE</code> state. If the table does not exist or does not transition to the <code>ACTIVE
   * </code> state after this time, then an AmazonClientException is thrown.
   *
   * @param dynamo The DynamoDB client to use to make requests.
   * @param tableName The name of the table whose status is being checked.
   * @param timeout The maximum number of milliseconds to wait.
   * @param interval The poll interval in milliseconds.
   * @throws TableNeverTransitionedToStateException If the specified table does not exist or does
   *     not transition into the <code>ACTIVE</code> state before this method times out and stops
   *     polling.
   * @throws InterruptedException If the thread is interrupted while waiting for the table to
   *     transition into the <code>ACTIVE</code> state.
   */
  public static void waitUntilActive(
      final AmazonDynamoDB dynamo, final String tableName, final int timeout, final int interval)
      throws InterruptedException, TableNeverTransitionedToStateException {
    TableDescription table =
        waitForTableDescription(dynamo, tableName, TableStatus.ACTIVE, timeout, interval);

    if (table == null || !table.getTableStatus().equals(TableStatus.ACTIVE.toString())) {
      throw new TableNeverTransitionedToStateException(tableName, TableStatus.ACTIVE);
    }
  }
  @Test
  public void createsGlobalIndices() {
    List<TableDescription> tables = DynamoTestUtils.MAPPER.getTables(TestTask.class);
    TableDescription table = DynamoTestUtils.getTableByName(tables, "TestTask");
    assertEquals(3, table.getGlobalSecondaryIndexes().size());

    GlobalSecondaryIndexDescription index =
        findIndex(table.getGlobalSecondaryIndexes(), "guid-index");
    assertEquals("INCLUDE", index.getProjection().getProjectionType());
    assertEquals("guid", index.getKeySchema().get(0).getAttributeName());
    assertEquals(Long.valueOf(18), index.getProvisionedThroughput().getWriteCapacityUnits());
    assertEquals(Long.valueOf(20), index.getProvisionedThroughput().getReadCapacityUnits());

    index = findIndex(table.getGlobalSecondaryIndexes(), "healthCode-scheduledOn-index");
    assertEquals("ALL", index.getProjection().getProjectionType());
    assertEquals("healthCode", index.getKeySchema().get(0).getAttributeName());
    assertEquals("scheduledOn", index.getKeySchema().get(1).getAttributeName());
    assertEquals(Long.valueOf(18), index.getProvisionedThroughput().getWriteCapacityUnits());
    assertEquals(Long.valueOf(20), index.getProvisionedThroughput().getReadCapacityUnits());

    index = findIndex(table.getGlobalSecondaryIndexes(), "healthCode-expiresOn-index");
    assertEquals("expiresOn", index.getKeySchema().get(0).getAttributeName());
  }
 @Test
 public void testGetAnnotatedTables() {
   List<TableDescription> tables = DynamoTestUtils.MAPPER.getTables(DynamoTestUtils.PACKAGE);
   assertNotNull(tables);
   assertEquals(2, tables.size());
   Map<String, TableDescription> tableMap = new HashMap<String, TableDescription>();
   for (TableDescription table : tables) {
     tableMap.put(table.getTableName(), table);
   }
   String tableName =
       Environment.LOCAL.name().toLowerCase()
           + "-"
           + DynamoTestUtils.class.getSimpleName()
           + "-TestHealthDataRecord";
   TableDescription table = tableMap.get(tableName);
   assertNotNull(table);
   assertEquals(2, table.getKeySchema().size());
   assertEquals(5, table.getAttributeDefinitions().size());
   assertEquals(2, table.getLocalSecondaryIndexes().size());
   assertEquals(1, table.getGlobalSecondaryIndexes().size());
   assertEquals(30L, table.getProvisionedThroughput().getReadCapacityUnits().longValue());
   assertEquals(50L, table.getProvisionedThroughput().getWriteCapacityUnits().longValue());
 }