All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.stargate.graphql.schema.cqlfirst.dml.fetchers.DmlFetcher Maven / Gradle / Ivy

There is a newer version: 2.0.31
Show newest version
package io.stargate.graphql.schema.cqlfirst.dml.fetchers;

import static io.stargate.graphql.schema.SchemaConstants.ASYNC_DIRECTIVE;

import com.datastax.oss.driver.shaded.guava.common.collect.ImmutableList;
import com.datastax.oss.driver.shaded.guava.common.collect.ImmutableMap;
import graphql.language.OperationDefinition;
import graphql.schema.DataFetchingEnvironment;
import io.stargate.db.ImmutableParameters;
import io.stargate.db.Parameters;
import io.stargate.db.datastore.ResultSet;
import io.stargate.db.datastore.Row;
import io.stargate.db.query.BoundQuery;
import io.stargate.db.query.Predicate;
import io.stargate.db.query.builder.BuiltCondition;
import io.stargate.db.schema.Column;
import io.stargate.db.schema.Column.ColumnType;
import io.stargate.db.schema.Table;
import io.stargate.graphql.schema.CassandraFetcher;
import io.stargate.graphql.schema.cqlfirst.dml.NameMapping;
import io.stargate.graphql.web.StargateGraphqlContext;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.function.UnaryOperator;
import java.util.stream.Collectors;
import org.apache.cassandra.stargate.db.ConsistencyLevel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public abstract class DmlFetcher extends CassandraFetcher {
  private static final Logger log = LoggerFactory.getLogger(DmlFetcher.class);
  protected final Table table;
  protected final NameMapping nameMapping;
  protected final DbColumnGetter dbColumnGetter;

  protected DmlFetcher(Table table, NameMapping nameMapping) {
    this.table = table;
    this.nameMapping = nameMapping;
    this.dbColumnGetter = new DbColumnGetter(nameMapping);
  }

  protected Parameters buildParameters(DataFetchingEnvironment environment) {
    Map options = environment.getArgument("options");
    if (options == null) {
      return DEFAULT_PARAMETERS;
    }

    ImmutableParameters.Builder builder = DEFAULT_PARAMETERS.toBuilder();

    Object consistency = options.get("consistency");
    if (consistency != null) {
      builder.consistencyLevel(ConsistencyLevel.valueOf((String) consistency));
    }

    Object serialConsistency = options.get("serialConsistency");
    if (serialConsistency != null) {
      builder.serialConsistencyLevel(ConsistencyLevel.valueOf((String) serialConsistency));
    }

    Object pageSize = options.get("pageSize");
    if (pageSize != null) {
      builder.pageSize((Integer) pageSize);
    }

    Object pageState = options.get("pageState");
    if (pageState != null) {
      builder.pagingState(ByteBuffer.wrap(Base64.getDecoder().decode((String) pageState)));
    }

    return builder.build();
  }

  protected List buildConditions(
      Table table, Map> columnList) {
    if (columnList == null) {
      return ImmutableList.of();
    }
    List where = new ArrayList<>();
    for (Map.Entry> clauseEntry : columnList.entrySet()) {
      Column column = dbColumnGetter.getColumn(table, clauseEntry.getKey());
      for (Map.Entry condition : clauseEntry.getValue().entrySet()) {
        FilterOperator operator = FilterOperator.fromFieldName(condition.getKey());
        where.add(operator.buildCondition(column, condition.getValue(), nameMapping));
      }
    }
    return where;
  }

  protected List buildClause(Table table, DataFetchingEnvironment environment) {
    if (environment.containsArgument("filter")) {
      Map> columnList = environment.getArgument("filter");
      return buildConditions(table, columnList);
    } else {
      Map value = environment.getArgument("value");
      List relations = new ArrayList<>();
      if (value == null) return ImmutableList.of();

      for (Map.Entry entry : value.entrySet()) {
        Column column = dbColumnGetter.getColumn(table, entry.getKey());
        Object whereValue = toDBValue(column.type(), entry.getValue());
        relations.add(BuiltCondition.of(column.name(), Predicate.EQ, whereValue));
      }
      return relations;
    }
  }

  protected List> toBatchResults(
      List rows, List> originalValues) {

    boolean applied = isAppliedBatch(rows);

    List> results = new ArrayList<>();
    for (Map originalValue : originalValues) {
      results.add(
          applied
              ? toAppliedMutationResultWithOriginalValue(originalValue)
              : toUnappliedBatchResult(rows, originalValue));
    }
    return results;
  }

  protected Map toBatchResult(List rows, Map originalValue) {
    return isAppliedBatch(rows)
        ? toAppliedMutationResultWithOriginalValue(originalValue)
        : toUnappliedBatchResult(rows, originalValue);
  }

  private Map toUnappliedBatchResult(
      List rows, final Map originalValue) {
    Map primaryKey =
        table.primaryKeyColumns().stream()
            .collect(
                Collectors.toMap(
                    Column::name, column -> toDBValue(column, originalValue.get(column.name()))));
    return rows.stream()
        .filter(row -> matches(row, primaryKey))
        .findFirst()
        .map(row -> toMutationResultSingleRow(originalValue, row))
        .orElse(ImmutableMap.of("applied", false));
  }

  private boolean matches(Row row, Map primaryKey) {
    for (Map.Entry entry : primaryKey.entrySet()) {
      String name = entry.getKey();
      if (row.columns().stream().noneMatch(c -> name.equals(c.name()))
          || !entry.getValue().equals(row.getObject(name))) {
        return false;
      }
    }
    return true;
  }

  protected CompletableFuture>> toListOfMutationResultsAccepted(
      List> originalValues) {
    List> results = new ArrayList<>();
    for (Map originalValue : originalValues) {
      results.add(toAcceptedMutationResultWithOriginalValue(originalValue));
    }
    return CompletableFuture.completedFuture(results);
  }

  protected Map toMutationResult(ResultSet resultSet, Object originalValue) {

    List rows = resultSet.currentPageRows();

    if (rows.isEmpty()) {
      // if we have no rows means that we got no information back from query execution, return
      // original value to the user and applied true to denote that query was accepted
      // not matter if the underlying data is not changed
      return toAppliedMutationResultWithOriginalValue(originalValue);
    } else {
      // otherwise check what can we get from the results
      // mutation target only one row
      Row row = rows.iterator().next();
      return toMutationResultSingleRow(originalValue, row);
    }
  }

  private ImmutableMap toAppliedMutationResultWithOriginalValue(
      Object originalValue) {
    return ImmutableMap.of("value", originalValue, "applied", true);
  }

  protected ImmutableMap toAcceptedMutationResultWithOriginalValue(
      Object originalValue) {
    return ImmutableMap.of("value", originalValue, "accepted", true);
  }

  private ImmutableMap toMutationResultSingleRow(Object originalValue, Row row) {
    boolean applied = row.getBoolean("[applied]");

    // if applied we can return the original value, otherwise use database state
    Object finalValue =
        applied ? originalValue : DataTypeMapping.toGraphQLValue(nameMapping, table, row);
    return ImmutableMap.of("value", finalValue, "applied", applied);
  }

  protected boolean containsDirective(OperationDefinition operation, String directive) {
    return operation.getDirectives().stream().anyMatch(d -> d.getName().equals(directive));
  }

  /**
   * Executes the query in an async way in a fire and forget fashion. Completes immediately with
   * accepted=true without waiting for the result.
   */
  protected CompletableFuture> executeAsyncAccepted(
      BoundQuery query,
      Object originalValue,
      UnaryOperator parameters,
      StargateGraphqlContext context) {
    context
        .getDataStore()
        .execute(query, parameters)
        .whenComplete(
            (r, throwable) -> {
              if (throwable != null) {
                log.warn(
                    String.format(
                        "The query %s executed within the %s directive failed.",
                        query, ASYNC_DIRECTIVE),
                    throwable);
              }
            });
    // complete immediately with accepted=true without waiting for the result
    return CompletableFuture.completedFuture(
        toAcceptedMutationResultWithOriginalValue(originalValue));
  }

  protected String getDBColumnName(Table table, String fieldName) {
    Column column = getColumn(table, fieldName);
    if (column == null) {
      return null;
    }
    return column.name();
  }

  protected Column getColumn(Table table, String fieldName) {
    String columnName = nameMapping.getCqlName(table, fieldName);
    return table.column(columnName);
  }

  protected Object toDBValue(Column column, Object value) {
    return toDBValue(column.type(), value);
  }

  private Object toDBValue(ColumnType type, Object value) {
    return DataTypeMapping.toDBValue(type, value, nameMapping);
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy