/******************************************************************************* * Copyright 2012-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. * Licensed under the Apache License, Version 2.0 (the "License"). You may not use * this file except in compliance with the License. A copy of the License is located at * * http://aws.amazon.com/apache2.0 * * or in the "license" file accompanying this file. * This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR * CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. * ***************************************************************************** * * AWS Tools for Windows (TM) PowerShell (TM) * */ using System; using System.Collections.Generic; using System.Linq; using System.Management.Automation; using Amazon.PowerShell.Common; using Amazon.Runtime; using Amazon.DynamoDBv2; using Amazon.DynamoDBv2.Model; using Amazon.PowerShell.Cmdlets.DDB.Model; namespace Amazon.PowerShell.Cmdlets.DDB { /// /// The CreateTable operation adds a new table to your account. In an AWS account, table names must be unique within each region. /// That is, you can have two tables with same name if you create the tables in different regions.CreateTable is an /// asynchronous operation. Upon receiving a CreateTable request, Amazon DynamoDB immediately returns a response with a /// TableStatus of CREATING . After the table is created, Amazon DynamoDB sets the TableStatus to ACTIVE . You can /// perform read and write operations only on an ACTIVE table. If you want to create multiple tables with local secondary /// indexes on them, you must create them sequentially. Only one table with local secondary indexes can be in the CREATING state at any /// given time.You can use the DescribeTable API to check the table status. /// [Cmdlet("New", "DDBTable", SupportsShouldProcess = true, ConfirmImpact = ConfirmImpact.Medium)] [OutputType("Amazon.DynamoDBv2.Model.TableDescription")] [AWSCmdlet("Calls the Amazon DynamoDB CreateTable API operation.", Operation = new [] {"CreateTable"}, SelectReturnType = typeof(Amazon.DynamoDBv2.Model.CreateTableResponse))] [AWSCmdletOutput("Amazon.DynamoDBv2.Model.TableDescription or Amazon.DynamoDBv2.Model.CreateTableResponse", "This cmdlet returns aN Amazon.DynamoDBv2.Model.TableDescription object.", "The service call response (type Amazon.DynamoDBv2.Model.CreateTableResponse) can also be referenced from properties attached to the cmdlet entry in the $AWSHistory stack." )] public class NewDDBTableCmdlet : AmazonDynamoDBClientCmdlet, IExecutor { #region Parameter TableName /// /// /// The name of the table to create. /// /// /// Constraints:Length3 - 255Pattern[a-zA-Z0-9_.-]+ /// /// [Parameter(Position = 0, ValueFromPipelineByPropertyName = true, Mandatory = true)] [Amazon.PowerShell.Common.AWSRequiredParameter] public System.String TableName { get; set; } #endregion #region Parameter Schema /// /// TableSchema object containing the attribute and key schema information for the new /// table using the Write-DDBKeySchema and Write-DDBIndexSchema cmdlets. /// [Parameter(ValueFromPipeline = true, ValueFromPipelineByPropertyName = true, Mandatory = true, Position = 1)] [Amazon.PowerShell.Common.AWSRequiredParameter] public Amazon.PowerShell.Cmdlets.DDB.Model.TableSchema Schema { get; set; } #endregion #region Parameter ReadCapacity /// /// /// The maximum number of strongly consistent reads consumed per second before Amazon DynamoDB returns a ThrottlingException. For more /// information, see Specifying Read and /// Write Requirements in the Amazon DynamoDB Developer Guide. /// /// /// Constraints:Range1 - /// /// /// The settings can be modified using the Update-DDBTable cmdlet. For current minimum and maximum provisioned throughput values, see Limits in the Amazon DynamoDB Developer /// Guide. /// /// [Parameter(Position = 2, ValueFromPipelineByPropertyName = true)] public System.Int64? ReadCapacity { get; set; } #endregion #region Parameter WriteCapacity /// /// /// The maximum number of strongly consistent writes consumed per second before Amazon DynamoDB returns a ThrottlingException. For more /// information, see Specifying Read and /// Write Requirements in the Amazon DynamoDB Developer Guide. /// /// /// Constraints:Range1 - /// /// /// The settings can be modified using the Update-DDBTable cmdlet. For current minimum and maximum provisioned throughput values, see Limits in the Amazon DynamoDB Developer /// Guide. /// /// [Parameter(Position = 3, ValueFromPipelineByPropertyName = true)] public System.Int64? WriteCapacity { get; set; } #endregion #region Parameter BillingMode /// /// /// Controls how you are charged for read and write throughput and how you manage capacity. /// This setting can be changed later. /// /// [System.Management.Automation.Parameter(Position = 4, ValueFromPipelineByPropertyName = true)] [AWSConstantClassSource("Amazon.DynamoDBv2.BillingMode")] public Amazon.DynamoDBv2.BillingMode BillingMode { get; set; } #endregion #region Parameter Force /// /// This parameter overrides confirmation prompts to force /// the cmdlet to continue its operation. This parameter should always /// be used with caution. /// [Parameter(ValueFromPipelineByPropertyName = true)] public SwitchParameter Force { get; set; } #endregion #region Parameter Select /// /// Use the -Select parameter to control the cmdlet output. The default value is 'TableDescription'. /// Specifying -Select '*' will result in the cmdlet returning the whole service response (Amazon.DynamoDBv2.Model.CreateTableResponse). /// Specifying the name of a property of type Amazon.DynamoDBv2.Model.CreateTableResponse will result in that property being returned. /// Specifying -Select '^ParameterName' will result in the cmdlet returning the selected cmdlet parameter value. /// [System.Management.Automation.Parameter] public string Select { get; set; } = "TableDescription"; #endregion protected override void ProcessRecord() { base.ProcessRecord(); if (!ConfirmShouldProceed(this.Force.IsPresent, this.TableName, "New-DDBTable (CreateTable)")) return; if (this.BillingMode != null && this.BillingMode == BillingMode.PAY_PER_REQUEST) { if (this.ReadCapacity != null) throw new ArgumentException("ReadCapacity cannot be specified when BillingMode is PAY_PER_REQUEST."); if (this.WriteCapacity != null) throw new ArgumentException("WriteCapacity cannot be specified when BillingMode is PAY_PER_REQUEST."); } // Default BillingMode is PROVISIONED. if (this.BillingMode == null || this.BillingMode == BillingMode.PROVISIONED) { if (this.ReadCapacity == null) throw new ArgumentException("ReadCapacity must be specified when BillingMode is PROVISIONED."); if (this.WriteCapacity == null) throw new ArgumentException("WriteCapacity must be specified when BillingMode is PROVISIONED."); } var context = new CmdletContext { BillingMode = this.BillingMode, TableName = this.TableName, SchemaObject = DDBSchemaCmdletHelper.TableSchemaFromParameter(Schema), ReadCapacityUnits = this.ReadCapacity, WriteCapacityUnits = this.WriteCapacity }; if (ParameterWasBound(nameof(this.Select))) { context.Select = CreateSelectDelegate(Select) ?? throw new System.ArgumentException("Invalid value for -Select parameter.", nameof(this.Select)); } var output = Execute(context) as CmdletOutput; ProcessOutput(output); } #region IExecutor Members public object Execute(ExecutorContext context) { var cmdletContext = context as CmdletContext; // create request var request = new CreateTableRequest { TableName = cmdletContext.TableName }; PopulateRequestFromSchemaObject(cmdletContext.SchemaObject, request); // Both ReadCapacityUnits and WriteCapacityUnits would be specified for BillingMode PROVISIONED. if (cmdletContext.ReadCapacityUnits != null && cmdletContext.WriteCapacityUnits != null) { request.ProvisionedThroughput = new ProvisionedThroughput { ReadCapacityUnits = cmdletContext.ReadCapacityUnits.Value, WriteCapacityUnits = cmdletContext.WriteCapacityUnits.Value }; } if (cmdletContext.BillingMode != null) { request.BillingMode = cmdletContext.BillingMode; } var client = Client ?? CreateClient(_CurrentCredentials, _RegionEndpoint); CmdletOutput output; // issue call try { var response = CallAWSServiceOperation(client, request); output = new CmdletOutput { PipelineOutput = cmdletContext.Select(response, this), ServiceResponse = response }; } catch (Exception e) { output = new CmdletOutput { ErrorResponse = e }; } return output; } private void PopulateRequestFromSchemaObject(TableSchema schemaObject, CreateTableRequest request) { if (!schemaObject.HasDefinedKeys) ThrowArgumentError("Key schema not defined on input object.", Schema); if (!schemaObject.HasDefinedAttributes) ThrowArgumentError("Attribute schema containing keys not defined on input object.", Schema); request.KeySchema = schemaObject.KeySchema; request.AttributeDefinitions = schemaObject.AttributeSchema; // optional if (schemaObject.HasDefinedLocalSecondaryIndices) { // reconstruct each lsi so that the schema builder cmdlets can opt to not // set the primary hash key element, thus enabling re-use on multiple // table creation pipelines var requestIndices = new List(); foreach (var definedLSI in schemaObject.LocalSecondaryIndexSchema) { var lsi = new LocalSecondaryIndex { IndexName = definedLSI.IndexName, KeySchema = new List() }; var hashKey = definedLSI.KeySchema.FirstOrDefault(k => k.KeyType.Equals(KeyType.HASH)); if (hashKey == null) { hashKey = schemaObject.KeySchema.FirstOrDefault(k => k.KeyType.Equals(KeyType.HASH)); if (hashKey == null) throw new ArgumentException(string.Format("Unable to determine primary hash key from supplied schema to use in local secondary index {0}", definedLSI.IndexName)); // DynamoDB requires the hash key be first in the collection lsi.KeySchema.Add(hashKey); lsi.KeySchema.AddRange(definedLSI.KeySchema); } else { // primary hash already set, possibly from earlier setup so just re-use lsi.KeySchema.AddRange(definedLSI.KeySchema); } if (definedLSI.Projection != null) { lsi.Projection = new Projection { ProjectionType = definedLSI.Projection.ProjectionType, NonKeyAttributes = definedLSI.Projection.NonKeyAttributes }; } requestIndices.Add(lsi); } request.LocalSecondaryIndexes = requestIndices; } // optional if (schemaObject.HasDefinedGlobalSecondaryIndices) { // reconstruct each lsi so that the schema builder cmdlets can opt to not // set the primary hash key element, thus enabling re-use on multiple // table creation pipelines var requestIndices = new List(); foreach (var definedGSI in schemaObject.GlobalSecondaryIndexSchema) { var gsi = new GlobalSecondaryIndex { IndexName = definedGSI.IndexName, KeySchema = new List(), ProvisionedThroughput = new ProvisionedThroughput { ReadCapacityUnits = definedGSI.ProvisionedThroughput.ReadCapacityUnits, WriteCapacityUnits = definedGSI.ProvisionedThroughput.WriteCapacityUnits } }; var hashKey = definedGSI.KeySchema.FirstOrDefault(k => k.KeyType.Equals(KeyType.HASH)); if (hashKey == null) { hashKey = schemaObject.KeySchema.FirstOrDefault(k => k.KeyType.Equals(KeyType.HASH)); if (hashKey == null) throw new ArgumentException(string.Format("Unable to determine primary hash key from supplied schema to use in global secondary index {0}", definedGSI.IndexName)); // DynamoDB requires the hash key be first in the collection gsi.KeySchema.Add(hashKey); gsi.KeySchema.AddRange(definedGSI.KeySchema); } else { // primary hash already set, possibly from earlier setup so just re-use gsi.KeySchema.AddRange(definedGSI.KeySchema); } if (definedGSI.Projection != null) { gsi.Projection = new Projection { ProjectionType = definedGSI.Projection.ProjectionType, NonKeyAttributes = definedGSI.Projection.NonKeyAttributes }; } requestIndices.Add(gsi); } request.GlobalSecondaryIndexes = requestIndices; } } public ExecutorContext CreateContext() { return new CmdletContext(); } #endregion #region AWS Service Operation Call private Amazon.DynamoDBv2.Model.CreateTableResponse CallAWSServiceOperation(IAmazonDynamoDB client, Amazon.DynamoDBv2.Model.CreateTableRequest request) { Utils.Common.WriteVerboseEndpointMessage(this, client.Config, "Amazon DynamoDB", "CreateTable"); try { #if DESKTOP return client.CreateTable(request); #elif CORECLR return client.CreateTableAsync(request).GetAwaiter().GetResult(); #else #error "Unknown build edition" #endif } catch (AmazonServiceException exc) { var webException = exc.InnerException as System.Net.WebException; if (webException != null) { throw new Exception(Utils.Common.FormatNameResolutionFailureMessage(client.Config, webException.Message), webException); } throw; } } #endregion internal class CmdletContext : ExecutorContext { public List AttributeDefinitions { get; set; } public Amazon.DynamoDBv2.BillingMode BillingMode { get; set; } public String TableName { get; set; } public List KeySchema { get; set; } public List LocalSecondaryIndexes { get; set; } public List GlobalSecondaryIndexes { get; set; } public Int64? ReadCapacityUnits { get; set; } public Int64? WriteCapacityUnits { get; set; } public TableSchema SchemaObject { get; set; } public System.Func Select { get; set; } = (response, cmdlet) => response.TableDescription; } } }