// Code generated by smithy-go-codegen DO NOT EDIT.

package dynamodb

import (
	"context"
	"fmt"
	awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware"
	"github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
	internalEndpointDiscovery "github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery"
	"github.com/aws/smithy-go/middleware"
	smithyhttp "github.com/aws/smithy-go/transport/http"
)

// Edits an existing item's attributes, or adds a new item to the table if it does
// not already exist. You can put, delete, or add attribute values. You can also
// perform a conditional update on an existing item (insert a new attribute
// name-value pair if it doesn't exist, or replace an existing name-value pair if
// it has certain expected attribute values).
//
// You can also return the item's attribute values in the same UpdateItem
// operation using the ReturnValues parameter.
func (c *Client) UpdateItem(ctx context.Context, params *UpdateItemInput, optFns ...func(*Options)) (*UpdateItemOutput, error) {
	if params == nil {
		params = &UpdateItemInput{}
	}

	result, metadata, err := c.invokeOperation(ctx, "UpdateItem", params, optFns, c.addOperationUpdateItemMiddlewares)
	if err != nil {
		return nil, err
	}

	out := result.(*UpdateItemOutput)
	out.ResultMetadata = metadata
	return out, nil
}

// Represents the input of an UpdateItem operation.
type UpdateItemInput struct {

	// The primary key of the item to be updated. Each element consists of an
	// attribute name and a value for that attribute.
	//
	// For the primary key, you must provide all of the attributes. For example, with
	// a simple primary key, you only need to provide a value for the partition key.
	// For a composite primary key, you must provide values for both the partition key
	// and the sort key.
	//
	// This member is required.
	Key map[string]types.AttributeValue

	// The name of the table containing the item to update. You can also provide the
	// Amazon Resource Name (ARN) of the table in this parameter.
	//
	// This member is required.
	TableName *string

	// This is a legacy parameter. Use UpdateExpression instead. For more information,
	// see [AttributeUpdates]in the Amazon DynamoDB Developer Guide.
	//
	// [AttributeUpdates]: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/LegacyConditionalParameters.AttributeUpdates.html
	AttributeUpdates map[string]types.AttributeValueUpdate

	// A condition that must be satisfied in order for a conditional update to succeed.
	//
	// An expression can contain any of the following:
	//
	//   - Functions: attribute_exists | attribute_not_exists | attribute_type |
	//   contains | begins_with | size
	//
	// These function names are case-sensitive.
	//
	//   - Comparison operators: = | <> | < | > | <= | >= | BETWEEN | IN
	//
	//   - Logical operators: AND | OR | NOT
	//
	// For more information about condition expressions, see [Specifying Conditions] in the Amazon DynamoDB
	// Developer Guide.
	//
	// [Specifying Conditions]: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.SpecifyingConditions.html
	ConditionExpression *string

	// This is a legacy parameter. Use ConditionExpression instead. For more
	// information, see [ConditionalOperator]in the Amazon DynamoDB Developer Guide.
	//
	// [ConditionalOperator]: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/LegacyConditionalParameters.ConditionalOperator.html
	ConditionalOperator types.ConditionalOperator

	// This is a legacy parameter. Use ConditionExpression instead. For more
	// information, see [Expected]in the Amazon DynamoDB Developer Guide.
	//
	// [Expected]: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/LegacyConditionalParameters.Expected.html
	Expected map[string]types.ExpectedAttributeValue

	// One or more substitution tokens for attribute names in an expression. The
	// following are some use cases for using ExpressionAttributeNames :
	//
	//   - To access an attribute whose name conflicts with a DynamoDB reserved word.
	//
	//   - To create a placeholder for repeating occurrences of an attribute name in
	//   an expression.
	//
	//   - To prevent special characters in an attribute name from being
	//   misinterpreted in an expression.
	//
	// Use the # character in an expression to dereference an attribute name. For
	// example, consider the following attribute name:
	//
	//   - Percentile
	//
	// The name of this attribute conflicts with a reserved word, so it cannot be used
	// directly in an expression. (For the complete list of reserved words, see [Reserved Words]in the
	// Amazon DynamoDB Developer Guide.) To work around this, you could specify the
	// following for ExpressionAttributeNames :
	//
	//   - {"#P":"Percentile"}
	//
	// You could then use this substitution in an expression, as in this example:
	//
	//   - #P = :val
	//
	// Tokens that begin with the : character are expression attribute values, which
	// are placeholders for the actual value at runtime.
	//
	// For more information about expression attribute names, see [Specifying Item Attributes] in the Amazon
	// DynamoDB Developer Guide.
	//
	// [Reserved Words]: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/ReservedWords.html
	// [Specifying Item Attributes]: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.AccessingItemAttributes.html
	ExpressionAttributeNames map[string]string

	// One or more values that can be substituted in an expression.
	//
	// Use the : (colon) character in an expression to dereference an attribute value.
	// For example, suppose that you wanted to check whether the value of the
	// ProductStatus attribute was one of the following:
	//
	//     Available | Backordered | Discontinued
	//
	// You would first need to specify ExpressionAttributeValues as follows:
	//
	//     { ":avail":{"S":"Available"}, ":back":{"S":"Backordered"},
	//     ":disc":{"S":"Discontinued"} }
	//
	// You could then use these values in an expression, such as this:
	//
	//     ProductStatus IN (:avail, :back, :disc)
	//
	// For more information on expression attribute values, see [Condition Expressions] in the Amazon
	// DynamoDB Developer Guide.
	//
	// [Condition Expressions]: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.SpecifyingConditions.html
	ExpressionAttributeValues map[string]types.AttributeValue

	// Determines the level of detail about either provisioned or on-demand throughput
	// consumption that is returned in the response:
	//
	//   - INDEXES - The response includes the aggregate ConsumedCapacity for the
	//   operation, together with ConsumedCapacity for each table and secondary index
	//   that was accessed.
	//
	// Note that some operations, such as GetItem and BatchGetItem , do not access any
	//   indexes at all. In these cases, specifying INDEXES will only return
	//   ConsumedCapacity information for table(s).
	//
	//   - TOTAL - The response includes only the aggregate ConsumedCapacity for the
	//   operation.
	//
	//   - NONE - No ConsumedCapacity details are included in the response.
	ReturnConsumedCapacity types.ReturnConsumedCapacity

	// Determines whether item collection metrics are returned. If set to SIZE , the
	// response includes statistics about item collections, if any, that were modified
	// during the operation are returned in the response. If set to NONE (the
	// default), no statistics are returned.
	ReturnItemCollectionMetrics types.ReturnItemCollectionMetrics

	// Use ReturnValues if you want to get the item attributes as they appear before
	// or after they are successfully updated. For UpdateItem , the valid values are:
	//
	//   - NONE - If ReturnValues is not specified, or if its value is NONE , then
	//   nothing is returned. (This setting is the default for ReturnValues .)
	//
	//   - ALL_OLD - Returns all of the attributes of the item, as they appeared before
	//   the UpdateItem operation.
	//
	//   - UPDATED_OLD - Returns only the updated attributes, as they appeared before
	//   the UpdateItem operation.
	//
	//   - ALL_NEW - Returns all of the attributes of the item, as they appear after
	//   the UpdateItem operation.
	//
	//   - UPDATED_NEW - Returns only the updated attributes, as they appear after the
	//   UpdateItem operation.
	//
	// There is no additional cost associated with requesting a return value aside
	// from the small network and processing overhead of receiving a larger response.
	// No read capacity units are consumed.
	//
	// The values returned are strongly consistent.
	ReturnValues types.ReturnValue

	// An optional parameter that returns the item attributes for an UpdateItem
	// operation that failed a condition check.
	//
	// There is no additional cost associated with requesting a return value aside
	// from the small network and processing overhead of receiving a larger response.
	// No read capacity units are consumed.
	ReturnValuesOnConditionCheckFailure types.ReturnValuesOnConditionCheckFailure

	// An expression that defines one or more attributes to be updated, the action to
	// be performed on them, and new values for them.
	//
	// The following action values are available for UpdateExpression .
	//
	//   - SET - Adds one or more attributes and values to an item. If any of these
	//   attributes already exist, they are replaced by the new values. You can also use
	//   SET to add or subtract from an attribute that is of type Number. For example:
	//   SET myNum = myNum + :val
	//
	// SET supports the following functions:
	//
	//   - if_not_exists (path, operand) - if the item does not contain an attribute at
	//   the specified path, then if_not_exists evaluates to operand; otherwise, it
	//   evaluates to path. You can use this function to avoid overwriting an attribute
	//   that may already be present in the item.
	//
	//   - list_append (operand, operand) - evaluates to a list with a new element
	//   added to it. You can append the new element to the start or the end of the list
	//   by reversing the order of the operands.
	//
	// These function names are case-sensitive.
	//
	//   - REMOVE - Removes one or more attributes from an item.
	//
	//   - ADD - Adds the specified value to the item, if the attribute does not
	//   already exist. If the attribute does exist, then the behavior of ADD depends
	//   on the data type of the attribute:
	//
	//   - If the existing attribute is a number, and if Value is also a number, then
	//   Value is mathematically added to the existing attribute. If Value is a
	//   negative number, then it is subtracted from the existing attribute.
	//
	// If you use ADD to increment or decrement a number value for an item that doesn't
	//   exist before the update, DynamoDB uses 0 as the initial value.
	//
	// Similarly, if you use ADD for an existing item to increment or decrement an
	//   attribute value that doesn't exist before the update, DynamoDB uses 0 as the
	//   initial value. For example, suppose that the item you want to update doesn't
	//   have an attribute named itemcount , but you decide to ADD the number 3 to this
	//   attribute anyway. DynamoDB will create the itemcount attribute, set its
	//   initial value to 0 , and finally add 3 to it. The result will be a new
	//   itemcount attribute in the item, with a value of 3 .
	//
	//   - If the existing data type is a set and if Value is also a set, then Value is
	//   added to the existing set. For example, if the attribute value is the set
	//   [1,2] , and the ADD action specified [3] , then the final attribute value is
	//   [1,2,3] . An error occurs if an ADD action is specified for a set attribute
	//   and the attribute type specified does not match the existing set type.
	//
	// Both sets must have the same primitive data type. For example, if the existing
	//   data type is a set of strings, the Value must also be a set of strings.
	//
	// The ADD action only supports Number and set data types.
	//
	//   - DELETE - Deletes an element from a set.
	//
	// If a set of values is specified, then those values are subtracted from the old
	//   set. For example, if the attribute value was the set [a,b,c] and the DELETE
	//   action specifies [a,c] , then the final attribute value is [b] . Specifying an
	//   empty set is an error.
	//
	// The DELETE action only supports set data types.
	//
	// You can have many actions in a single expression, such as the following: SET
	// a=:value1, b=:value2 DELETE :value3, :value4, :value5
	//
	// For more information on update expressions, see [Modifying Items and Attributes] in the Amazon DynamoDB
	// Developer Guide.
	//
	// [Modifying Items and Attributes]: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.Modifying.html
	UpdateExpression *string

	noSmithyDocumentSerde
}

func (in *UpdateItemInput) bindEndpointParams(p *EndpointParameters) {

	p.ResourceArn = in.TableName

}

// Represents the output of an UpdateItem operation.
type UpdateItemOutput struct {

	// A map of attribute values as they appear before or after the UpdateItem
	// operation, as determined by the ReturnValues parameter.
	//
	// The Attributes map is only present if the update was successful and ReturnValues
	// was specified as something other than NONE in the request. Each element
	// represents one attribute.
	Attributes map[string]types.AttributeValue

	// The capacity units consumed by the UpdateItem operation. The data returned
	// includes the total provisioned throughput consumed, along with statistics for
	// the table and any indexes involved in the operation. ConsumedCapacity is only
	// returned if the ReturnConsumedCapacity parameter was specified. For more
	// information, see [Capacity unity consumption for write operations]in the Amazon DynamoDB Developer Guide.
	//
	// [Capacity unity consumption for write operations]: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/read-write-operations.html#write-operation-consumption
	ConsumedCapacity *types.ConsumedCapacity

	// Information about item collections, if any, that were affected by the UpdateItem
	// operation. ItemCollectionMetrics is only returned if the
	// ReturnItemCollectionMetrics parameter was specified. If the table does not have
	// any local secondary indexes, this information is not returned in the response.
	//
	// Each ItemCollectionMetrics element consists of:
	//
	//   - ItemCollectionKey - The partition key value of the item collection. This is
	//   the same as the partition key value of the item itself.
	//
	//   - SizeEstimateRangeGB - An estimate of item collection size, in gigabytes.
	//   This value is a two-element array containing a lower bound and an upper bound
	//   for the estimate. The estimate includes the size of all the items in the table,
	//   plus the size of all attributes projected into all of the local secondary
	//   indexes on that table. Use this estimate to measure whether a local secondary
	//   index is approaching its size limit.
	//
	// The estimate is subject to change over time; therefore, do not rely on the
	//   precision or accuracy of the estimate.
	ItemCollectionMetrics *types.ItemCollectionMetrics

	// Metadata pertaining to the operation's result.
	ResultMetadata middleware.Metadata

	noSmithyDocumentSerde
}

func (c *Client) addOperationUpdateItemMiddlewares(stack *middleware.Stack, options Options) (err error) {
	if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil {
		return err
	}
	err = stack.Serialize.Add(&awsAwsjson10_serializeOpUpdateItem{}, middleware.After)
	if err != nil {
		return err
	}
	err = stack.Deserialize.Add(&awsAwsjson10_deserializeOpUpdateItem{}, middleware.After)
	if err != nil {
		return err
	}
	if err := addProtocolFinalizerMiddlewares(stack, options, "UpdateItem"); err != nil {
		return fmt.Errorf("add protocol finalizers: %v", err)
	}

	if err = addlegacyEndpointContextSetter(stack, options); err != nil {
		return err
	}
	if err = addSetLoggerMiddleware(stack, options); err != nil {
		return err
	}
	if err = addClientRequestID(stack); err != nil {
		return err
	}
	if err = addComputeContentLength(stack); err != nil {
		return err
	}
	if err = addResolveEndpointMiddleware(stack, options); err != nil {
		return err
	}
	if err = addComputePayloadSHA256(stack); err != nil {
		return err
	}
	if err = addRetry(stack, options); err != nil {
		return err
	}
	if err = addRawResponseToMetadata(stack); err != nil {
		return err
	}
	if err = addRecordResponseTiming(stack); err != nil {
		return err
	}
	if err = addSpanRetryLoop(stack, options); err != nil {
		return err
	}
	if err = addClientUserAgent(stack, options); err != nil {
		return err
	}
	if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil {
		return err
	}
	if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil {
		return err
	}
	if err = addOpUpdateItemDiscoverEndpointMiddleware(stack, options, c); err != nil {
		return err
	}
	if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil {
		return err
	}
	if err = addTimeOffsetBuild(stack, c); err != nil {
		return err
	}
	if err = addUserAgentRetryMode(stack, options); err != nil {
		return err
	}
	if err = addUserAgentAccountIDEndpointMode(stack, options); err != nil {
		return err
	}
	if err = addCredentialSource(stack, options); err != nil {
		return err
	}
	if err = addOpUpdateItemValidationMiddleware(stack); err != nil {
		return err
	}
	if err = stack.Initialize.Add(newServiceMetadataMiddleware_opUpdateItem(options.Region), middleware.Before); err != nil {
		return err
	}
	if err = addRecursionDetection(stack); err != nil {
		return err
	}
	if err = addRequestIDRetrieverMiddleware(stack); err != nil {
		return err
	}
	if err = addResponseErrorMiddleware(stack); err != nil {
		return err
	}
	if err = addValidateResponseChecksum(stack, options); err != nil {
		return err
	}
	if err = addAcceptEncodingGzip(stack, options); err != nil {
		return err
	}
	if err = addRequestResponseLogging(stack, options); err != nil {
		return err
	}
	if err = addDisableHTTPSMiddleware(stack, options); err != nil {
		return err
	}
	if err = addInterceptBeforeRetryLoop(stack, options); err != nil {
		return err
	}
	if err = addInterceptAttempt(stack, options); err != nil {
		return err
	}
	if err = addInterceptors(stack, options); err != nil {
		return err
	}
	return nil
}

func addOpUpdateItemDiscoverEndpointMiddleware(stack *middleware.Stack, o Options, c *Client) error {
	return stack.Finalize.Insert(&internalEndpointDiscovery.DiscoverEndpoint{
		Options: []func(*internalEndpointDiscovery.DiscoverEndpointOptions){
			func(opt *internalEndpointDiscovery.DiscoverEndpointOptions) {
				opt.DisableHTTPS = o.EndpointOptions.DisableHTTPS
				opt.Logger = o.Logger
			},
		},
		DiscoverOperation:            c.fetchOpUpdateItemDiscoverEndpoint,
		EndpointDiscoveryEnableState: o.EndpointDiscovery.EnableEndpointDiscovery,
		EndpointDiscoveryRequired:    false,
		Region:                       o.Region,
	}, "ResolveEndpointV2", middleware.After)
}

func (c *Client) fetchOpUpdateItemDiscoverEndpoint(ctx context.Context, region string, optFns ...func(*internalEndpointDiscovery.DiscoverEndpointOptions)) (internalEndpointDiscovery.WeightedAddress, error) {
	input := getOperationInput(ctx)
	in, ok := input.(*UpdateItemInput)
	if !ok {
		return internalEndpointDiscovery.WeightedAddress{}, fmt.Errorf("unknown input type %T", input)
	}
	_ = in

	identifierMap := make(map[string]string, 0)
	identifierMap["sdk#Region"] = region

	key := fmt.Sprintf("DynamoDB.%v", identifierMap)

	if v, ok := c.endpointCache.Get(key); ok {
		return v, nil
	}

	discoveryOperationInput := &DescribeEndpointsInput{}

	opt := internalEndpointDiscovery.DiscoverEndpointOptions{}
	for _, fn := range optFns {
		fn(&opt)
	}

	go c.handleEndpointDiscoveryFromService(ctx, discoveryOperationInput, region, key, opt)
	return internalEndpointDiscovery.WeightedAddress{}, nil
}

func newServiceMetadataMiddleware_opUpdateItem(region string) *awsmiddleware.RegisterServiceMetadata {
	return &awsmiddleware.RegisterServiceMetadata{
		Region:        region,
		ServiceID:     ServiceID,
		OperationName: "UpdateItem",
	}
}
