1 /*
2  * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License").
5  * You may not use this file except in compliance with the License.
6  * A copy of the License is located at
7  *
8  *  http://aws.amazon.com/apache2.0
9  *
10  * or in the "license" file accompanying this file. This file is distributed
11  * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12  * express or implied. See the License for the specific language governing
13  * permissions and limitations under the License.
14  */
15 
16 package software.amazon.awssdk.enhanced.dynamodb.internal.operations;
17 
18 import java.util.concurrent.CompletableFuture;
19 import java.util.function.Function;
20 import software.amazon.awssdk.annotations.SdkInternalApi;
21 import software.amazon.awssdk.enhanced.dynamodb.DynamoDbAsyncIndex;
22 import software.amazon.awssdk.enhanced.dynamodb.DynamoDbAsyncTable;
23 import software.amazon.awssdk.enhanced.dynamodb.DynamoDbEnhancedClientExtension;
24 import software.amazon.awssdk.enhanced.dynamodb.DynamoDbIndex;
25 import software.amazon.awssdk.enhanced.dynamodb.DynamoDbTable;
26 import software.amazon.awssdk.enhanced.dynamodb.OperationContext;
27 import software.amazon.awssdk.enhanced.dynamodb.TableSchema;
28 import software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClient;
29 import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
30 
31 
32 /**
33  * Common interface for a single operation that can be executed in a synchronous or non-blocking asynchronous fashion
34  * against a mapped database table. These operations can be made against either the primary index of a table or a
35  * secondary index, although some implementations of this interface do not support secondary indices and will throw
36  * an exception when executed against one. Conceptually an operation maps 1:1 with an actual DynamoDb call.
37  * <p>
38  * This interface is extended by {@link TableOperation} and {@link IndexOperation} which contain implementations of
39  * the behavior to actually execute the operation in the context of a table or secondary index and are used by
40  * {@link DynamoDbTable} or {@link DynamoDbAsyncTable} and {@link DynamoDbIndex} or {@link DynamoDbAsyncIndex}
41  * respectively. By sharing this common interface operations are able to re-use code regardless of whether they are
42  * executed in the context of a primary or secondary index or whether they are being executed in a synchronous or
43  * non-blocking asynchronous fashion.
44  *
45  * @param <ItemT> The modelled object that this table maps records to.
46  * @param <RequestT>  The type of the request object for the DynamoDb call in the low level {@link DynamoDbClient} or
47  *                  {@link DynamoDbAsyncClient}.
48  * @param <ResponseT> The type of the response object for the DynamoDb call in the low level {@link DynamoDbClient}
49  *                  or {@link DynamoDbAsyncClient}.
50  * @param <ResultT> The type of the mapped result object that will be returned by the execution of this operation.
51  */
52 @SdkInternalApi
53 public interface CommonOperation<ItemT, RequestT, ResponseT, ResultT> {
54     /**
55      * This method generates the request that needs to be sent to a low level {@link DynamoDbClient}.
56      * @param tableSchema A {@link TableSchema} that maps the table to a modelled object.
57      * @param context An object containing the context, or target, of the command execution.
58      * @param extension A {@link DynamoDbEnhancedClientExtension} that may modify the request of this operation. A null
59      *                  value here will result in no modifications.
60      * @return A request that can be used as an argument to a {@link DynamoDbClient} call to perform the operation.
61      */
generateRequest(TableSchema<ItemT> tableSchema, OperationContext context, DynamoDbEnhancedClientExtension extension)62     RequestT generateRequest(TableSchema<ItemT> tableSchema, OperationContext context,
63                              DynamoDbEnhancedClientExtension extension);
64 
65     /**
66      * Provides a function for making the low level synchronous SDK call to DynamoDb.
67      * @param dynamoDbClient A low level {@link DynamoDbClient} to make the call against.
68      * @return A function that calls DynamoDb with a provided request object and returns the response object.
69      */
serviceCall(DynamoDbClient dynamoDbClient)70     Function<RequestT, ResponseT> serviceCall(DynamoDbClient dynamoDbClient);
71 
72     /**
73      * Provides a function for making the low level non-blocking asynchronous SDK call to DynamoDb.
74      * @param dynamoDbAsyncClient A low level {@link DynamoDbAsyncClient} to make the call against.
75      * @return A function that calls DynamoDb with a provided request object and returns the response object.
76      */
asyncServiceCall(DynamoDbAsyncClient dynamoDbAsyncClient)77     Function<RequestT, CompletableFuture<ResponseT>> asyncServiceCall(DynamoDbAsyncClient dynamoDbAsyncClient);
78 
79     /**
80      * Takes the response object returned by the actual DynamoDb call and maps it into a higher level abstracted
81      * result object.
82      * @param response The response object returned by the DynamoDb call for this operation.
83      * @param tableSchema A {@link TableSchema} that maps the table to a modelled object.
84      * @param context An object containing the context, or target, of the command execution.
85      * @param extension A {@link DynamoDbEnhancedClientExtension} that may modify the result of this operation. A null
86      *                  value here will result in no modifications.
87      * @return A high level result object as specified by the implementation of this operation.
88      */
transformResponse(ResponseT response, TableSchema<ItemT> tableSchema, OperationContext context, DynamoDbEnhancedClientExtension extension)89     ResultT transformResponse(ResponseT response,
90                               TableSchema<ItemT> tableSchema,
91                               OperationContext context,
92                               DynamoDbEnhancedClientExtension extension);
93 
94     /**
95      * Default implementation of a complete synchronous execution of this operation against either the primary or a
96      * secondary index.
97      * It performs three steps:
98      * 1) Call generateRequest() to get the request object.
99      * 2) Call getServiceCall() and call it using the request object generated in the previous step.
100      * 3) Call transformResponse() to convert the response object returned in the previous step to a high level result.
101      *
102      * @param tableSchema A {@link TableSchema} that maps the table to a modelled object.
103      * @param context An object containing the context, or target, of the command execution.
104      * @param dynamoDbClient A {@link DynamoDbClient} to make the call against.
105      * @param extension A {@link DynamoDbEnhancedClientExtension} that may modify the request or result of this
106      *                  operation. A null value here will result in no modifications.
107      * @return A high level result object as specified by the implementation of this operation.
108      */
execute(TableSchema<ItemT> tableSchema, OperationContext context, DynamoDbEnhancedClientExtension extension, DynamoDbClient dynamoDbClient)109     default ResultT execute(TableSchema<ItemT> tableSchema,
110                             OperationContext context,
111                             DynamoDbEnhancedClientExtension extension,
112                             DynamoDbClient dynamoDbClient) {
113         RequestT request = generateRequest(tableSchema, context, extension);
114         ResponseT response = serviceCall(dynamoDbClient).apply(request);
115         return transformResponse(response, tableSchema, context, extension);
116     }
117 
118     /**
119      * Default implementation of a complete non-blocking asynchronous execution of this operation against either the
120      * primary or a secondary index.
121      * It performs three steps:
122      * 1) Call generateRequest() to get the request object.
123      * 2) Call getServiceCall() and call it using the request object generated in the previous step.
124      * 3) Wraps the {@link CompletableFuture} returned by the SDK in a new one that calls transformResponse() to
125      * convert the response object returned in the previous step to a high level result.
126      *
127      * @param tableSchema A {@link TableSchema} that maps the table to a modelled object.
128      * @param context An object containing the context, or target, of the command execution.
129      * @param dynamoDbAsyncClient A {@link DynamoDbAsyncClient} to make the call against.
130      * @param extension A {@link DynamoDbEnhancedClientExtension} that may modify the request or result of this
131      *                  operation. A null value here will result in no modifications.
132      * @return A {@link CompletableFuture} of the high level result object as specified by the implementation of this
133      * operation.
134      */
executeAsync(TableSchema<ItemT> tableSchema, OperationContext context, DynamoDbEnhancedClientExtension extension, DynamoDbAsyncClient dynamoDbAsyncClient)135     default CompletableFuture<ResultT> executeAsync(TableSchema<ItemT> tableSchema,
136                                                     OperationContext context,
137                                                     DynamoDbEnhancedClientExtension extension,
138                                                     DynamoDbAsyncClient dynamoDbAsyncClient) {
139         RequestT request = generateRequest(tableSchema, context, extension);
140         CompletableFuture<ResponseT> response = asyncServiceCall(dynamoDbAsyncClient).apply(request);
141         return response.thenApply(r -> transformResponse(r, tableSchema, context, extension));
142     }
143 
144     /**
145      * The type, or name, of the operation.
146      */
operationName()147     OperationName operationName();
148 }
149