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

com.couchbase.client.scala.manager.analytics.AnalyticsLinks.scala Maven / Gradle / Ivy

/*
 * Copyright (c) 2021 Couchbase, Inc.
 *
 * 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 com.couchbase.client.scala.manager.analytics
import com.couchbase.client.scala.util.CouchbasePickler

import scala.util.{Failure, Success, Try}

/** An abstraction over the various forms of analytics links. */
sealed trait AnalyticsLink {
  private[scala] def toMap: Try[Map[String, String]]

  private[scala] def linkType: AnalyticsLinkType

  /** The name of this link. */
  def name: String

  /** The name of the dataverse this link is on. */
  def dataverseName: String
}

object AnalyticsLink {
  implicit val rw: CouchbasePickler.ReadWriter[AnalyticsLink] = CouchbasePickler
    .readwriter[ujson.Obj]
    .bimap[AnalyticsLink](
      (x: AnalyticsLink) => ???, // Unused
      (json) => {
        json("type").str match {
          case "couchbase" => CouchbasePickler.read[CouchbaseRemoteAnalyticsLink](json)
          case "s3"        => CouchbasePickler.read[S3ExternalAnalyticsLink](json)
          case x           => throw new IllegalStateException(s"Cannot decode analytics link type $x")
        }
      }
    )

  /** An analytics link to a remote Couchbase cluster. */
  case class CouchbaseRemoteAnalyticsLink(
      dataverse: String,
      name: String,
      hostname: String,
      encryption: AnalyticsEncryptionLevel
  ) extends AnalyticsLink {
    private[scala] def linkType: AnalyticsLinkType = AnalyticsLinkType.CouchbaseRemote

    override private[scala] def toMap: Try[Map[String, String]] = {
      encryption.toMap
        .map(
          v =>
            v ++
              Map(
                "type"      -> "couchbase",
                "dataverse" -> dataverse,
                "name"      -> name,
                "hostname"  -> hostname,
                "type"      -> "couchbase"
              )
        )
    }

    override def dataverseName: String = dataverse
  }

  private[scala] object CouchbaseRemoteAnalyticsLink {
    implicit val rw: CouchbasePickler.ReadWriter[CouchbaseRemoteAnalyticsLink] = CouchbasePickler
      .readwriter[ujson.Obj]
      .bimap[CouchbaseRemoteAnalyticsLink](
        (x: CouchbaseRemoteAnalyticsLink) => ???, // Unused
        (json) => {
          val dv = if (json.obj.contains("dataverse")) json("dataverse").str else json("scope").str
          CouchbaseRemoteAnalyticsLink(
            dv,
            json("name").str,
            json("activeHostname").str,
            CouchbasePickler.read[AnalyticsEncryptionLevel](json)
          )
        }
      )
  }

  /** An analytics link to S3. */
  case class S3ExternalAnalyticsLink(
      dataverse: String,
      name: String,
      accessKeyID: String,
      secretAccessKey: String,
      sessionToken: String,
      region: String,
      serviceEndpoint: String
  ) extends AnalyticsLink {
    private[scala] def linkType: AnalyticsLinkType = AnalyticsLinkType.S3External

    override private[scala] def toMap: Try[Map[String, String]] = {

      Success(
        Map(
          "type"            -> "s3",
          "dataverse"       -> dataverse,
          "name"            -> name,
          "accessKeyId"     -> accessKeyID,
          "secretAccessKey" -> secretAccessKey,
          "sessionToken"    -> sessionToken,
          "region"          -> region,
          "serviceEndpoint" -> serviceEndpoint,
          "type"            -> "s3"
        )
      )
    }

    override def dataverseName: String = dataverse
  }

  private[scala] object S3ExternalAnalyticsLink {
    implicit val rw: CouchbasePickler.ReadWriter[S3ExternalAnalyticsLink] = CouchbasePickler
      .readwriter[ujson.Obj]
      .bimap[S3ExternalAnalyticsLink](
        (x: S3ExternalAnalyticsLink) => ???, // Unused
        (json) => {
          val dv = if (json.obj.contains("dataverse")) json("dataverse").str else json("scope").str
          // As per RFC, some fields are blanked out
          S3ExternalAnalyticsLink(
            dv,
            json("name").str,
            json("accessKeyId").str,
            "",
            "",
            json("region").str,
            json("serviceEndpoint").str
          )
        }
      )
  }

}

/** Identifies the type of the analytics link.
  */
sealed trait AnalyticsLinkType {
  private[scala] def encode: String
}

object AnalyticsLinkType {
  case object S3External extends AnalyticsLinkType {
    override private[scala] def encode = "s3"
  }

  case object CouchbaseRemote extends AnalyticsLinkType {
    override private[scala] def encode = "couchbase"
  }
}

/** Abstracts over the various encryption levels for connecting to a remote cluster with an analytics link. */
sealed trait AnalyticsEncryptionLevel {
  private[scala] def toMap: Try[Map[String, String]]
}

case class UsernameAndPassword(username: String, password: String) {
  private[scala] def toMap: Try[Map[String, String]] = {
    Success(Map("username" -> username, "password" -> password))
  }
}

object UsernameAndPassword {
  implicit val rw: CouchbasePickler.ReadWriter[UsernameAndPassword] = CouchbasePickler.macroRW
}

case class AnalyticsClientCertificate(clientCertificate: String, clientKey: String) {
  private[scala] def toMap: Try[Map[String, String]] = {
    Success(Map("clientCertificate" -> clientCertificate, "clientKey" -> clientKey))
  }
}

object AnalyticsClientCertificate {
  implicit val rw: CouchbasePickler.ReadWriter[AnalyticsClientCertificate] =
    CouchbasePickler.macroRW
}

object AnalyticsEncryptionLevel {
  implicit val rw: CouchbasePickler.ReadWriter[AnalyticsEncryptionLevel] = CouchbasePickler
    .readwriter[ujson.Obj]
    .bimap[AnalyticsEncryptionLevel](
      (x: AnalyticsEncryptionLevel) => ???, // Unused
      (json) => {
        // As per RFC, some properties are blanked out on reading
        val auth = UsernameAndPassword(json("username").str, "")
        json("encryption").str match {
          case "none" => None(auth)
          case "half" => Half(auth)
          case "full" =>
            None(UsernameAndPassword(json("username").str, json("password").str))
            val clientCertificate = json("clientCertificate").str
            val clientKey         = json("clientKey").str
            if (clientCertificate != null && clientKey != null) {
              Full(
                json("certificate").str,
                Right(AnalyticsClientCertificate(clientCertificate, ""))
              )
            } else {
              Full(json("certificate").str, Left(auth))
            }
          case x => throw new IllegalStateException(s"Cannot decode analytics encryption $x")
        }
      }
    )

  case object Unavailable extends AnalyticsEncryptionLevel {
    private[scala] def toMap: Try[Map[String, String]] = {
      Failure(
        new IllegalStateException("Cannot use the result of getAllLinks directly with replaceLinks")
      )
    }
  }

  case class None(auth: UsernameAndPassword) extends AnalyticsEncryptionLevel {
    private[scala] def toMap: Try[Map[String, String]] = {
      auth.toMap.map(v => v ++ Map("encryption" -> "none"))
    }
  }

  case class Half(auth: UsernameAndPassword) extends AnalyticsEncryptionLevel {
    private[scala] def toMap: Try[Map[String, String]] = {
      auth.toMap.map(v => v ++ Map("encryption" -> "half"))
    }
  }

  case class Full(
      certificate: String,
      auth: Either[UsernameAndPassword, AnalyticsClientCertificate]
  ) extends AnalyticsEncryptionLevel {
    private[scala] def toMap: Try[Map[String, String]] = {
      (auth match {
        case Left(v)  => v.toMap
        case Right(v) => v.toMap
      }).map(v => v ++ Map("encryption" -> "full", "certificate" -> certificate))
    }
  }
}

sealed trait AnalyticsDataType

object AnalyticsDataType {
  case object AnalyticsString extends AnalyticsDataType

  case object AnalyticsInt64 extends AnalyticsDataType

  case object AnalyticsDouble extends AnalyticsDataType
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy