Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.evomaster.client.java.controller.api.dto.database.execution;

import java.util.ArrayList;
import java.util.List;

/**
* DynamoDB reads that can be satisfied by generated initialization data.
*/
public class DynamoDbExecutionsDto {

public List<DynamoDbFailedQuery> failedQueries = new ArrayList<>();

public DynamoDbExecutionsDto() {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package org.evomaster.client.java.controller.api.dto.database.execution;

import org.evomaster.client.java.controller.api.dto.database.operations.DynamoDbAttributeValueDto;

import java.util.ArrayList;
import java.util.List;

/**
* Equality constraints from a successful DynamoDB read that returned no matching item.
*/
public class DynamoDbFailedQuery {

public String tableName;
public List<DynamoDbAttributeValueDto> attributes = new ArrayList<>();

public DynamoDbFailedQuery() {
}

/**
* Creates a failed-query description.
*
* @param tableName target table
* @param attributes equality-constrained attributes
*/
public DynamoDbFailedQuery(String tableName, List<DynamoDbAttributeValueDto> attributes) {
this.tableName = tableName;
this.attributes = new ArrayList<>(attributes);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package org.evomaster.client.java.controller.api.dto.database.operations;

/**
* A named scalar DynamoDB attribute value.
*/
public class DynamoDbAttributeValueDto {

public String attributeName;
public DynamoDbScalarTypeDto type;
public String value;

public DynamoDbAttributeValueDto() {
}

/**
* Creates an attribute value.
*
* @param attributeName attribute name
* @param type DynamoDB scalar type
* @param value string-preserved value
*/
public DynamoDbAttributeValueDto(String attributeName, DynamoDbScalarTypeDto type, String value) {
this.attributeName = attributeName;
this.type = type;
this.value = value;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package org.evomaster.client.java.controller.api.dto.database.operations;

import java.util.ArrayList;
import java.util.List;

/**
* DynamoDB insertion commands sent to the controller.
*/
public class DynamoDbDatabaseCommandsDto {

public List<DynamoDbInsertionDto> insertions = new ArrayList<>();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package org.evomaster.client.java.controller.api.dto.database.operations;

import java.util.ArrayList;
import java.util.List;

/**
* An item to insert into a DynamoDB table.
*/
public class DynamoDbInsertionDto {

public String tableName;
public List<DynamoDbAttributeValueDto> attributes = new ArrayList<>();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package org.evomaster.client.java.controller.api.dto.database.operations;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
* Results of a sequence of DynamoDB insertions.
*/
public class DynamoDbInsertionResultsDto {

public List<Boolean> executionResults = new ArrayList<>();
public Integer failedInsertionIndex;

/**
* Records the insertion that failed while preserving earlier successes.
*
* @param insertions attempted insertions
* @param failedIndex zero-based index of the failed insertion
*/
public void handleFailedInsertion(List<DynamoDbInsertionDto> insertions, int failedIndex) {
executionResults = new ArrayList<>(Collections.nCopies(insertions.size(), false));
for (int i = 0; i < failedIndex; i++) {
executionResults.set(i, true);
}
failedInsertionIndex = failedIndex;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package org.evomaster.client.java.controller.api.dto.database.operations;

/**
* Scalar DynamoDB attribute types supported by generated insertions.
*/
public enum DynamoDbScalarTypeDto {
S,
N,
BOOL
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
package org.evomaster.client.java.controller.dynamodb;

import org.evomaster.client.java.controller.api.dto.database.operations.DynamoDbAttributeValueDto;
import org.evomaster.client.java.controller.api.dto.database.operations.DynamoDbInsertionDto;
import org.evomaster.client.java.controller.api.dto.database.operations.DynamoDbInsertionResultsDto;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletionStage;

/**
* Executes DynamoDB insertions without binding the controller API to an AWS SDK version.
*/
public final class DynamoDbCommandExecutor {

private DynamoDbCommandExecutor() {
}

/**
* Executes insertions using a synchronous or asynchronous AWS SDK v2 client.
*
* @param client DynamoDB client
* @param insertions items to insert
* @return per-insertion results
*/
public static DynamoDbInsertionResultsDto executeInsert(Object client, List<DynamoDbInsertionDto> insertions) {
if (client == null) {
throw new IllegalArgumentException("No DynamoDB client");
}
if (insertions == null || insertions.isEmpty()) {
throw new IllegalArgumentException("No data to insert");
}

DynamoDbInsertionResultsDto results = new DynamoDbInsertionResultsDto();
results.executionResults = new ArrayList<>(Collections.nCopies(insertions.size(), false));
for (int i = 0; i < insertions.size(); i++) {
try {
executeOne(client, insertions.get(i));
results.executionResults.set(i, true);
} catch (RuntimeException e) {
results.failedInsertionIndex = i;
throw new DynamoDbInsertionException(i, results, e);
}
}
return results;
}

private static void executeOne(Object client, DynamoDbInsertionDto insertion) {
try {
ClassLoader loader = client.getClass().getClassLoader();
Class<?> attributeValueClass = Class.forName(
"software.amazon.awssdk.services.dynamodb.model.AttributeValue", true, loader);
Class<?> attributeValueBuilderClass = Class.forName(
"software.amazon.awssdk.services.dynamodb.model.AttributeValue$Builder", true, loader);
Class<?> putItemRequestClass = Class.forName(
"software.amazon.awssdk.services.dynamodb.model.PutItemRequest", true, loader);
Class<?> putItemRequestBuilderClass = Class.forName(
"software.amazon.awssdk.services.dynamodb.model.PutItemRequest$Builder", true, loader);

Map<String, Object> item = new LinkedHashMap<>();
for (DynamoDbAttributeValueDto attribute : insertion.attributes) {
Object builder = attributeValueClass.getMethod("builder").invoke(null);
String setter;
Object value;
switch (attribute.type) {
case S:
setter = "s";
value = attribute.value;
break;
case N:
setter = "n";
value = attribute.value;
break;
case BOOL:
setter = "bool";
value = Boolean.valueOf(attribute.value);
break;
default:
throw new IllegalArgumentException("Unsupported DynamoDB attribute type: " + attribute.type);
}
attributeValueBuilderClass.getMethod(setter, value.getClass()).invoke(builder, value);
item.put(attribute.attributeName, attributeValueBuilderClass.getMethod("build").invoke(builder));
}

Object requestBuilder = putItemRequestClass.getMethod("builder").invoke(null);
putItemRequestBuilderClass.getMethod("tableName", String.class)
.invoke(requestBuilder, insertion.tableName);
putItemRequestBuilderClass.getMethod("item", Map.class).invoke(requestBuilder, item);
Object request = putItemRequestBuilderClass.getMethod("build").invoke(requestBuilder);
Method putItem = findPutItemMethod(client, loader, putItemRequestClass);
Object response = putItem.invoke(client, request);
if (response instanceof CompletionStage) {
((CompletionStage<?>) response).toCompletableFuture().join();
}
} catch (InvocationTargetException e) {
Throwable cause = e.getCause() == null ? e : e.getCause();
throw new RuntimeException("Failed DynamoDB insertion into table '" + insertion.tableName + "'", cause);
} catch (ReflectiveOperationException e) {
throw new RuntimeException("Failed DynamoDB insertion into table '" + insertion.tableName + "'", e);
}
}

private static Method findPutItemMethod(Object client, ClassLoader loader, Class<?> putItemRequestClass)
throws ClassNotFoundException, NoSuchMethodException {
Class<?> syncClientClass = Class.forName("software.amazon.awssdk.services.dynamodb.DynamoDbClient", true, loader);
if (syncClientClass.isInstance(client)) {
return syncClientClass.getMethod("putItem", putItemRequestClass);
}

Class<?> asyncClientClass = Class.forName("software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClient", true, loader);
if (asyncClientClass.isInstance(client)) {
return asyncClientClass.getMethod("putItem", putItemRequestClass);
}

throw new IllegalArgumentException("Unsupported DynamoDB client: " + client.getClass().getName());
}

/**
* Exception carrying partial insertion results.
*/
public static class DynamoDbInsertionException extends RuntimeException {

private final int failedIndex;
private final DynamoDbInsertionResultsDto results;

private DynamoDbInsertionException(int failedIndex, DynamoDbInsertionResultsDto results, Throwable cause) {
super("Failed DynamoDB insertion at index " + failedIndex, cause);
this.failedIndex = failedIndex;
this.results = results;
}

/**
* @return failed insertion index
*/
public int getFailedIndex() {
return failedIndex;
}

/**
* @return partial results
*/
public DynamoDbInsertionResultsDto getResults() {
return results;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package org.evomaster.client.java.controller.dynamodb.dsl;

import org.evomaster.client.java.controller.api.dto.database.operations.DynamoDbAttributeValueDto;
import org.evomaster.client.java.controller.api.dto.database.operations.DynamoDbInsertionDto;
import org.evomaster.client.java.controller.api.dto.database.operations.DynamoDbScalarTypeDto;

import java.util.ArrayList;
import java.util.List;

/**
* DSL for DynamoDB insertions in generated tests.
*/
public final class DynamoDbDsl implements DynamoDbSequenceDsl, DynamoDbStatementDsl {

private List<DynamoDbInsertionDto> insertions = new ArrayList<>();
private DynamoDbInsertionDto current;

private DynamoDbDsl() {
}

/**
* @return a new DynamoDB insertion sequence
*/
public static DynamoDbSequenceDsl dynamoDb() {
return new DynamoDbDsl();
}

@Override
public DynamoDbStatementDsl insertInto(String tableName) {
checkOpen();
if (tableName == null || tableName.isEmpty()) {
throw new IllegalArgumentException("Unspecified table");
}
current = new DynamoDbInsertionDto();
current.tableName = tableName;
insertions.add(current);
return this;
}

@Override
public DynamoDbStatementDsl s(String name, String value) {
return attribute(name, DynamoDbScalarTypeDto.S, value);
}

@Override
public DynamoDbStatementDsl n(String name, String value) {
return attribute(name, DynamoDbScalarTypeDto.N, value);
}

@Override
public DynamoDbStatementDsl bool(String name, boolean value) {
return attribute(name, DynamoDbScalarTypeDto.BOOL, Boolean.toString(value));
}

@Override
public List<DynamoDbInsertionDto> dtos() {
checkOpen();
List<DynamoDbInsertionDto> result = insertions;
insertions = null;
current = null;
return result;
}

private DynamoDbStatementDsl attribute(String name, DynamoDbScalarTypeDto type, String value) {
checkOpen();
if (current == null) {
throw new IllegalStateException("Call insertInto before adding attributes");
}
if (name == null || name.isEmpty()) {
throw new IllegalArgumentException("Unspecified attribute name");
}
current.attributes.add(new DynamoDbAttributeValueDto(name, type, value));
return this;
}

private void checkOpen() {
if (insertions == null) {
throw new IllegalStateException("DTO was already built for this object");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.evomaster.client.java.controller.dynamodb.dsl;

/**
* Entry point for a DynamoDB insertion sequence.
*/
public interface DynamoDbSequenceDsl {

/**
* Starts an item insertion.
*
* @param tableName target table
* @return item statement
*/
DynamoDbStatementDsl insertInto(String tableName);
}
Loading
Loading