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

org.scanamo.ops.package.scala Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2019 Scanamo
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License 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.
 */

package org.scanamo

import cats.data.NonEmptyList
import cats.free.{ Free, FreeT }
import cats.syntax.apply.*
import org.scanamo.request.*
import software.amazon.awssdk.services.dynamodb.model.*

package object ops {
  type ScanamoOps[A] = Free[ScanamoOpsA, A]
  type ScanamoOpsT[M[_], A] = FreeT[ScanamoOpsA, M, A]

  private[ops] object JavaRequests {
    import collection.JavaConverters.*

    def scan(req: ScanamoScanRequest): ScanRequest = {
      def queryRefinement[T](
        o: ScanamoScanRequest => Option[T]
      )(rt: (ScanRequest.Builder, T) => ScanRequest.Builder): ScanRequest.Builder => ScanRequest.Builder = { qr =>
        o(req).foldLeft(qr)(rt)
      }

      NonEmptyList
        .of(
          queryRefinement(_.index)(_.indexName(_)),
          queryRefinement(_.options.limit)(_.limit(_)),
          queryRefinement(_.options.exclusiveStartKey)((r, k) => r.exclusiveStartKey(k.toJavaMap)),
          queryRefinement(_.options.filter) { (r, f) =>
            val requestCondition = f.apply.runEmptyA.value
            requestCondition.dynamoValues
              .filter(_.nonEmpty)
              .flatMap(_.toExpressionAttributeValues)
              .foldLeft(
                r.filterExpression(requestCondition.expression)
                  .expressionAttributeNames(requestCondition.attributeNames.asJava)
              )(_ expressionAttributeValues _)
          }
        )
        .reduceLeft(_.compose(_))(
          ScanRequest.builder.tableName(req.tableName).consistentRead(req.options.consistent)
        )
        .build
    }

    def query(req: ScanamoQueryRequest): QueryRequest = {
      def queryRefinement[T](
        f: ScanamoQueryRequest => Option[T]
      )(g: (QueryRequest.Builder, T) => QueryRequest.Builder): QueryRequest.Builder => QueryRequest.Builder = { qr =>
        f(req).foldLeft(qr)(g)
      }

      val queryCondition: RequestCondition = req.query.apply
      val requestCondition: Option[RequestCondition] = req.options.filter.map(_.apply.runEmptyA.value)

      val requestBuilder = NonEmptyList
        .of(
          queryRefinement(_.index)(_ indexName _),
          queryRefinement(_.options.limit)(_ limit _),
          queryRefinement(_.options.exclusiveStartKey.map(_.toJavaMap))(_ exclusiveStartKey _)
        )
        .reduceLeft(_ compose _)(
          QueryRequest.builder
            .tableName(req.tableName)
            .consistentRead(req.options.consistent)
            .scanIndexForward(req.options.ascending)
            .keyConditionExpression(queryCondition.expression)
        )

      requestCondition.fold {
        val requestWithCondition = requestBuilder.expressionAttributeNames(queryCondition.attributeNames.asJava)
        queryCondition.dynamoValues
          .filter(_.nonEmpty)
          .flatMap(_.toExpressionAttributeValues)
          .foldLeft(requestWithCondition)(_ expressionAttributeValues _)
      } { condition =>
        val requestWithCondition = requestBuilder
          .filterExpression(condition.expression)
          .expressionAttributeNames((queryCondition.attributeNames ++ condition.attributeNames).asJava)
        val attributeValues =
          (
            queryCondition.dynamoValues orElse Some(DynamoObject.empty),
            condition.dynamoValues orElse Some(DynamoObject.empty)
          ).mapN(_ <> _)

        attributeValues
          .flatMap(_.toExpressionAttributeValues)
          .foldLeft(requestWithCondition)(_ expressionAttributeValues _)
      }.build
    }

    def put(req: ScanamoPutRequest): PutItemRequest = {
      val request = PutItemRequest.builder
        .tableName(req.tableName)
        .item(req.item.asObject.getOrElse(DynamoObject.empty).toJavaMap)
        .returnValues(req.ret.asDynamoValue)

      req.condition
        .fold(request) { condition =>
          val requestWithCondition = request
            .conditionExpression(condition.expression)
            .expressionAttributeNames(condition.attributeNames.asJava)

          condition.dynamoValues
            .flatMap(_.toExpressionAttributeValues)
            .foldLeft(requestWithCondition)(_ expressionAttributeValues _)
        }
        .build
    }

    def delete(req: ScanamoDeleteRequest): DeleteItemRequest = {
      val request = DeleteItemRequest.builder
        .tableName(req.tableName)
        .key(req.key.toJavaMap)
        .returnValues(req.ret.asDynamoValue)

      req.condition
        .fold(request) { condition =>
          val requestWithCondition = request
            .conditionExpression(condition.expression)
            .expressionAttributeNames(condition.attributeNames.asJava)

          condition.dynamoValues
            .flatMap(_.toExpressionAttributeValues)
            .foldLeft(requestWithCondition)(_ expressionAttributeValues _)
        }
        .build
    }

    def update(req: ScanamoUpdateRequest): UpdateItemRequest = {
      val attributeNames: Map[String, String] = req.condition.map(_.attributeNames).foldLeft(req.attributeNames)(_ ++ _)
      val attributeValues: DynamoObject = req.condition.flatMap(_.dynamoValues).foldLeft(req.dynamoValues)(_ <> _)
      val request = UpdateItemRequest.builder
        .tableName(req.tableName)
        .key(req.key.toJavaMap)
        .updateExpression(req.updateExpression)
        .returnValues(ReturnValue.ALL_NEW)
        .expressionAttributeNames(attributeNames.asJava)

      val requestWithCondition =
        req.condition.fold(request)(condition => request.conditionExpression(condition.expression))

      attributeValues.toExpressionAttributeValues
        .fold(requestWithCondition) { avs =>
          if (req.addEmptyList)
            avs.put(":emptyList", DynamoValue.EmptyList)
          requestWithCondition expressionAttributeValues avs
        }
        .build
    }

    def transactItems(req: ScanamoTransactWriteRequest): TransactWriteItemsRequest = {
      val putItems = req.putItems.map { item =>
        TransactWriteItem.builder
          .put(
            software.amazon.awssdk.services.dynamodb.model.Put.builder
              .item(item.item.asObject.getOrElse(DynamoObject.empty).toJavaMap)
              .tableName(item.tableName)
              .build
          )
          .build
      }

      val updateItems = req.updateItems.map { item =>
        val update = software.amazon.awssdk.services.dynamodb.model.Update.builder
          .tableName(item.tableName)
          .updateExpression(item.updateExpression.expression)
          .expressionAttributeNames(item.updateExpression.attributeNames.asJava)
          .key(item.key.toJavaMap)

        val updateWithAvs = DynamoObject(item.updateExpression.dynamoValues).toExpressionAttributeValues
          .fold(update)(avs => update.expressionAttributeValues(avs))
          .build

        TransactWriteItem.builder.update(updateWithAvs).build
      }
      val deleteItems = req.deleteItems.map { item =>
        TransactWriteItem.builder
          .delete(
            software.amazon.awssdk.services.dynamodb.model.Delete.builder
              .key(item.key.toJavaMap)
              .tableName(item.tableName)
              .build
          )
          .build
      }

      val conditionChecks = req.conditionCheck.map { item =>
        val check = software.amazon.awssdk.services.dynamodb.model.ConditionCheck.builder
          .key(item.key.toJavaMap)
          .tableName(item.tableName)
          .conditionExpression(item.condition.expression)
          .expressionAttributeNames(item.condition.attributeNames.asJava)

        val checkWithAvs = item.condition.dynamoValues
          .flatMap(_.toExpressionAttributeValues)
          .foldLeft(check)(_ expressionAttributeValues _)
          .build

        TransactWriteItem.builder.conditionCheck(checkWithAvs).build
      }

      TransactWriteItemsRequest.builder
        .transactItems((putItems ++ updateItems ++ deleteItems ++ conditionChecks): _*)
        .build
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy