/** * Generated by API Builder - https://www.apibuilder.io * Service version: 0.2.6 * User agent: apibuilder app.apibuilder.io/bombas/storeback-api/0.2.6/http4s_0_15 */ package bombas.storeback.api.v0.models { /** * @param name The name of the breadcrumb section * @param link Pieces of a collections page url to use for link * @param position Order position */ final case class Breadcrumb( name: _root_.scala.Option[String] = None, link: _root_.scala.Option[String] = None, position: _root_.scala.Option[Int] = None ) /** * @param id The slug id of the color * @param position The order in which the color should be displayed * @param name The name of the color * @param fill A 80x80 url of an image of the swatch to use on FE */ final case class Color( id: _root_.scala.Option[String] = None, position: _root_.scala.Option[Int] = None, name: _root_.scala.Option[String] = None, fill: _root_.scala.Option[String] = None ) /** * @param iconHandle the name used to generate the silhouette icon image for a product detail * @param title the title of the details / set of tech features */ final case class Detail( iconHandle: _root_.scala.Option[String] = None, title: _root_.scala.Option[String] = None, tech: _root_.scala.Option[Seq[bombas.storeback.api.v0.models.Tech]] = None ) /** * @param id A unique numeric identifier for the product image. * @param position The order of the product image in the list. The first product image is at * position 1 and is the 'main' image for the product. * @param alt ALT text * @param src Specifies the location of the product image. This parameter supports URL filters * that you can use to retrieve modified copies of the image. For example, add * _small, to the filename to retrieve a scaled copy of the image at 100 x 100 px * (for example, ipod-nano_small.png), or add _2048x2048 to retrieve a copy of the * image constrained at 2048 x 2048 px resolution (for example, * ipod-nano_2048x2048.png). */ final case class Image( id: _root_.scala.Option[Int] = None, position: _root_.scala.Option[Int] = None, alt: _root_.scala.Option[String] = None, src: _root_.scala.Option[String] = None ) /** * @param msrp The original price of the item before an adjustment or a sale. * @param price The price of the product variant. * @param isDiscounted true If the product is discounted * @param percentOff percentOff which is a whole number as a string, based on the percentage price is * off of msrp prices. */ final case class Price( msrp: _root_.scala.Option[String] = None, price: _root_.scala.Option[String] = None, isDiscounted: _root_.scala.Option[Boolean] = None, percentOff: _root_.scala.Option[String] = None ) /** * @param id An unsigned 64-bit integer that's used as a unique identifier for the product. * Each id is unique across the Shopify system. No two products will have the same * id, even if they're from different shops. * @param title The name of the product. * @param handle A unique human-friendly string for the product. Automatically generated from the * product's title. Used by the Liquid templating language to refer to objects. * @param description A description of the product. * @param descriptionHtml A description of the product. Supports HTML formatting. * @param itemCount The number of units included. If it is > 1, then it is a pack. * @param productType The product type * @param gender The marketed gender of the products * @param createdAt The date and time (ISO 8601 format) when the product was created. * @param updatedAt The date and time (ISO 8601 format) when the product was last modified. */ final case class Product( id: _root_.scala.Option[Long] = None, title: _root_.scala.Option[String] = None, handle: _root_.scala.Option[String] = None, description: _root_.scala.Option[String] = None, descriptionHtml: _root_.scala.Option[String] = None, itemCount: _root_.scala.Option[Int] = None, productType: _root_.scala.Option[String] = None, gender: _root_.scala.Option[String] = None, colors: _root_.scala.Option[Seq[bombas.storeback.api.v0.models.Color]] = None, sizes: _root_.scala.Option[Seq[bombas.storeback.api.v0.models.Size]] = None, images: _root_.scala.Option[Seq[Map[String, _root_.io.circe.Json]]] = None, variants: _root_.scala.Option[Seq[bombas.storeback.api.v0.models.Variant]] = None, breadcrumbs: _root_.scala.Option[Seq[bombas.storeback.api.v0.models.Breadcrumb]] = None, details: _root_.scala.Option[Seq[bombas.storeback.api.v0.models.Detail]] = None, createdAt: _root_.scala.Option[String] = None, updatedAt: _root_.scala.Option[String] = None ) /** * @param requiresShipping This property is deprecated. Use the `requires_shipping` property on the * InventoryItem resource instead. * @param grams The weight of the product variant in grams. * @param weight The weight of the product variant in the unit system specified with weight_unit. * @param weightUnit The unit of measurement that applies to the product variant's weight. If you * don't specify a value for weight_unit, then the shop's default unit of * measurement is applied. Valid values: g, kg, oz, and lb. */ final case class Shipping( requiresShipping: _root_.scala.Option[Boolean] = None, grams: _root_.scala.Option[Int] = None, weight: _root_.scala.Option[Int] = None, weightUnit: _root_.scala.Option[String] = None ) /** * A particular size options for a product * * @param name The name of the option which this size was created from */ final case class Size( name: _root_.scala.Option[String] = None, values: _root_.scala.Option[Seq[bombas.storeback.api.v0.models.SizeValues]] = None ) /** * @param id The internal name of the size * @param name The visual name of the size * @param guidance Size related guidance for more specific examples for the user */ final case class SizeValues( id: _root_.scala.Option[String] = None, name: _root_.scala.Option[String] = None, guidance: _root_.scala.Option[String] = None ) /** * @param title name of the tech feature * @param handle a unique internal name used to identify the tech feature * @param content the copy wrote for the tech feature */ final case class Tech( title: _root_.scala.Option[String] = None, handle: _root_.scala.Option[String] = None, content: _root_.scala.Option[String] = None ) /** * @param id The unique numeric identifier for the product variant. * @param productId The unique numeric identifier for the product. * @param sku A unique identifier for the product variant in the shop. Required in order to * connect to a FulfillmentService. * @param title The title of the product variant. * @param isAvailable If the variant is availabor not * @param createdAt The date and time (ISO 8601 format) when the product was created. * @param updatedAt The date and time (ISO 8601 format) when the product was last modified. */ final case class Variant( id: _root_.scala.Option[Long] = None, productId: _root_.scala.Option[Long] = None, sku: _root_.scala.Option[String] = None, title: _root_.scala.Option[String] = None, isAvailable: _root_.scala.Option[Boolean] = None, price: _root_.scala.Option[bombas.storeback.api.v0.models.Price] = None, shipping: _root_.scala.Option[bombas.storeback.api.v0.models.Shipping] = None, createdAt: _root_.scala.Option[String] = None, updatedAt: _root_.scala.Option[String] = None, sizes: _root_.scala.Option[Seq[bombas.storeback.api.v0.models.Size]] = None ) } package bombas.storeback.api.v0.models { package object json { import io.circe.Decoder._ import io.circe.Encoder._ import scala.language.implicitConversions // See below - Make Scala 2.11 Either monadic import scala.util.Try import io.circe.{Json, JsonObject, Encoder, Decoder, DecodingFailure} import io.circe.syntax._ import bombas.storeback.api.v0.models.json._ // Make Scala 2.11 Either monadic private[v0] implicit def eitherOps[A,B](e: Either[A,B]) = cats.implicits.catsSyntaxEither(e) private[v0] implicit val decodeUUID: Decoder[_root_.java.util.UUID] = Decoder.decodeString.emapTry(str => Try(_root_.java.util.UUID.fromString(str))) private[v0] implicit val encodeUUID: Encoder[_root_.java.util.UUID] = Encoder.encodeString.contramap[_root_.java.util.UUID](uuid => uuid.toString) private[v0] implicit val decodeInstant: Decoder[_root_.java.time.Instant] = Decoder.decodeString.emapTry(str => Try(_root_.java.time.OffsetDateTime.parse(str).toInstant)) private[v0] implicit val encodeInstant: Encoder[_root_.java.time.Instant] = Encoder.encodeString.contramap[_root_.java.time.Instant](_.toString) private[v0] implicit val decodeLocalDate: Decoder[_root_.java.time.LocalDate] = Decoder.decodeString.emapTry(str => Try(_root_.java.time.LocalDate.parse(str))) private[v0] implicit val encodeLocalDate: Encoder[_root_.java.time.LocalDate] = Encoder.encodeString.contramap[_root_.java.time.LocalDate](_.toString) implicit def decodeStorebackApiBreadcrumb: Decoder[Breadcrumb] = Decoder.instance { c => for { name <- c.downField("name").as[Option[String]] link <- c.downField("link").as[Option[String]] position <- c.downField("position").as[Option[Int]] } yield { Breadcrumb( name = name, link = link, position = position ) } } implicit def encodeStorebackApiBreadcrumb: Encoder[Breadcrumb] = Encoder.instance { t => Json.fromFields(Seq( t.name.map(t => "name" -> t.asJson), t.link.map(t => "link" -> t.asJson), t.position.map(t => "position" -> t.asJson) ).flatten) } implicit def decodeStorebackApiColor: Decoder[Color] = Decoder.instance { c => for { id <- c.downField("id").as[Option[String]] position <- c.downField("position").as[Option[Int]] name <- c.downField("name").as[Option[String]] fill <- c.downField("fill").as[Option[String]] } yield { Color( id = id, position = position, name = name, fill = fill ) } } implicit def encodeStorebackApiColor: Encoder[Color] = Encoder.instance { t => Json.fromFields(Seq( t.id.map(t => "id" -> t.asJson), t.position.map(t => "position" -> t.asJson), t.name.map(t => "name" -> t.asJson), t.fill.map(t => "fill" -> t.asJson) ).flatten) } implicit def decodeStorebackApiDetail: Decoder[Detail] = Decoder.instance { c => for { iconHandle <- c.downField("icon_handle").as[Option[String]] title <- c.downField("title").as[Option[String]] tech <- c.downField("tech").as[Option[Seq[bombas.storeback.api.v0.models.Tech]]] } yield { Detail( iconHandle = iconHandle, title = title, tech = tech ) } } implicit def encodeStorebackApiDetail: Encoder[Detail] = Encoder.instance { t => Json.fromFields(Seq( t.iconHandle.map(t => "icon_handle" -> t.asJson), t.title.map(t => "title" -> t.asJson), t.tech.map(t => "tech" -> t.asJson) ).flatten) } implicit def decodeStorebackApiImage: Decoder[Image] = Decoder.instance { c => for { id <- c.downField("id").as[Option[Int]] position <- c.downField("position").as[Option[Int]] alt <- c.downField("alt").as[Option[String]] src <- c.downField("src").as[Option[String]] } yield { Image( id = id, position = position, alt = alt, src = src ) } } implicit def encodeStorebackApiImage: Encoder[Image] = Encoder.instance { t => Json.fromFields(Seq( t.id.map(t => "id" -> t.asJson), t.position.map(t => "position" -> t.asJson), t.alt.map(t => "alt" -> t.asJson), t.src.map(t => "src" -> t.asJson) ).flatten) } implicit def decodeStorebackApiPrice: Decoder[Price] = Decoder.instance { c => for { msrp <- c.downField("msrp").as[Option[String]] price <- c.downField("price").as[Option[String]] isDiscounted <- c.downField("isDiscounted").as[Option[Boolean]] percentOff <- c.downField("percentOff").as[Option[String]] } yield { Price( msrp = msrp, price = price, isDiscounted = isDiscounted, percentOff = percentOff ) } } implicit def encodeStorebackApiPrice: Encoder[Price] = Encoder.instance { t => Json.fromFields(Seq( t.msrp.map(t => "msrp" -> t.asJson), t.price.map(t => "price" -> t.asJson), t.isDiscounted.map(t => "isDiscounted" -> t.asJson), t.percentOff.map(t => "percentOff" -> t.asJson) ).flatten) } implicit def decodeStorebackApiProduct: Decoder[Product] = Decoder.instance { c => for { id <- c.downField("id").as[Option[Long]] title <- c.downField("title").as[Option[String]] handle <- c.downField("handle").as[Option[String]] description <- c.downField("description").as[Option[String]] descriptionHtml <- c.downField("description_html").as[Option[String]] itemCount <- c.downField("item_count").as[Option[Int]] productType <- c.downField("product_type").as[Option[String]] gender <- c.downField("gender").as[Option[String]] colors <- c.downField("colors").as[Option[Seq[bombas.storeback.api.v0.models.Color]]] sizes <- c.downField("sizes").as[Option[Seq[bombas.storeback.api.v0.models.Size]]] images <- c.downField("images").as[Option[Seq[Map[String, _root_.io.circe.Json]]]] variants <- c.downField("variants").as[Option[Seq[bombas.storeback.api.v0.models.Variant]]] breadcrumbs <- c.downField("breadcrumbs").as[Option[Seq[bombas.storeback.api.v0.models.Breadcrumb]]] details <- c.downField("details").as[Option[Seq[bombas.storeback.api.v0.models.Detail]]] createdAt <- c.downField("created_at").as[Option[String]] updatedAt <- c.downField("updated_at").as[Option[String]] } yield { Product( id = id, title = title, handle = handle, description = description, descriptionHtml = descriptionHtml, itemCount = itemCount, productType = productType, gender = gender, colors = colors, sizes = sizes, images = images, variants = variants, breadcrumbs = breadcrumbs, details = details, createdAt = createdAt, updatedAt = updatedAt ) } } implicit def encodeStorebackApiProduct: Encoder[Product] = Encoder.instance { t => Json.fromFields(Seq( t.id.map(t => "id" -> t.asJson), t.title.map(t => "title" -> t.asJson), t.handle.map(t => "handle" -> t.asJson), t.description.map(t => "description" -> t.asJson), t.descriptionHtml.map(t => "description_html" -> t.asJson), t.itemCount.map(t => "item_count" -> t.asJson), t.productType.map(t => "product_type" -> t.asJson), t.gender.map(t => "gender" -> t.asJson), t.colors.map(t => "colors" -> t.asJson), t.sizes.map(t => "sizes" -> t.asJson), t.images.map(t => "images" -> t.asJson), t.variants.map(t => "variants" -> t.asJson), t.breadcrumbs.map(t => "breadcrumbs" -> t.asJson), t.details.map(t => "details" -> t.asJson), t.createdAt.map(t => "created_at" -> t.asJson), t.updatedAt.map(t => "updated_at" -> t.asJson) ).flatten) } implicit def decodeStorebackApiShipping: Decoder[Shipping] = Decoder.instance { c => for { requiresShipping <- c.downField("requires_shipping").as[Option[Boolean]] grams <- c.downField("grams").as[Option[Int]] weight <- c.downField("weight").as[Option[Int]] weightUnit <- c.downField("weight_unit").as[Option[String]] } yield { Shipping( requiresShipping = requiresShipping, grams = grams, weight = weight, weightUnit = weightUnit ) } } implicit def encodeStorebackApiShipping: Encoder[Shipping] = Encoder.instance { t => Json.fromFields(Seq( t.requiresShipping.map(t => "requires_shipping" -> t.asJson), t.grams.map(t => "grams" -> t.asJson), t.weight.map(t => "weight" -> t.asJson), t.weightUnit.map(t => "weight_unit" -> t.asJson) ).flatten) } implicit def decodeStorebackApiSize: Decoder[Size] = Decoder.instance { c => for { name <- c.downField("name").as[Option[String]] values <- c.downField("values").as[Option[Seq[bombas.storeback.api.v0.models.SizeValues]]] } yield { Size( name = name, values = values ) } } implicit def encodeStorebackApiSize: Encoder[Size] = Encoder.instance { t => Json.fromFields(Seq( t.name.map(t => "name" -> t.asJson), t.values.map(t => "values" -> t.asJson) ).flatten) } implicit def decodeStorebackApiSizeValues: Decoder[SizeValues] = Decoder.instance { c => for { id <- c.downField("id").as[Option[String]] name <- c.downField("name").as[Option[String]] guidance <- c.downField("guidance").as[Option[String]] } yield { SizeValues( id = id, name = name, guidance = guidance ) } } implicit def encodeStorebackApiSizeValues: Encoder[SizeValues] = Encoder.instance { t => Json.fromFields(Seq( t.id.map(t => "id" -> t.asJson), t.name.map(t => "name" -> t.asJson), t.guidance.map(t => "guidance" -> t.asJson) ).flatten) } implicit def decodeStorebackApiTech: Decoder[Tech] = Decoder.instance { c => for { title <- c.downField("title").as[Option[String]] handle <- c.downField("handle").as[Option[String]] content <- c.downField("content").as[Option[String]] } yield { Tech( title = title, handle = handle, content = content ) } } implicit def encodeStorebackApiTech: Encoder[Tech] = Encoder.instance { t => Json.fromFields(Seq( t.title.map(t => "title" -> t.asJson), t.handle.map(t => "handle" -> t.asJson), t.content.map(t => "content" -> t.asJson) ).flatten) } implicit def decodeStorebackApiVariant: Decoder[Variant] = Decoder.instance { c => for { id <- c.downField("id").as[Option[Long]] productId <- c.downField("product_id").as[Option[Long]] sku <- c.downField("sku").as[Option[String]] title <- c.downField("title").as[Option[String]] isAvailable <- c.downField("is_available").as[Option[Boolean]] price <- c.downField("price").as[Option[bombas.storeback.api.v0.models.Price]] shipping <- c.downField("shipping").as[Option[bombas.storeback.api.v0.models.Shipping]] createdAt <- c.downField("created_at").as[Option[String]] updatedAt <- c.downField("updated_at").as[Option[String]] sizes <- c.downField("sizes").as[Option[Seq[bombas.storeback.api.v0.models.Size]]] } yield { Variant( id = id, productId = productId, sku = sku, title = title, isAvailable = isAvailable, price = price, shipping = shipping, createdAt = createdAt, updatedAt = updatedAt, sizes = sizes ) } } implicit def encodeStorebackApiVariant: Encoder[Variant] = Encoder.instance { t => Json.fromFields(Seq( t.id.map(t => "id" -> t.asJson), t.productId.map(t => "product_id" -> t.asJson), t.sku.map(t => "sku" -> t.asJson), t.title.map(t => "title" -> t.asJson), t.isAvailable.map(t => "is_available" -> t.asJson), t.price.map(t => "price" -> t.asJson), t.shipping.map(t => "shipping" -> t.asJson), t.createdAt.map(t => "created_at" -> t.asJson), t.updatedAt.map(t => "updated_at" -> t.asJson), t.sizes.map(t => "sizes" -> t.asJson) ).flatten) } } } package bombas.storeback.api.v0 { import org.http4s.client.blaze._ import io.circe.syntax._ object Constants { val BaseUrl = "https://bombas-storeback-api.global.ssl.fastly.net/v1" val Namespace = "bombas.storeback.api.v0" val UserAgent = "apibuilder app.apibuilder.io/bombas/storeback-api/0.2.6/http4s_0_15" val Version = "0.2.6" val VersionMajor = 0 } class Client( val baseUrl: org.http4s.Uri = org.http4s.Uri.unsafeFromString("https://bombas-storeback-api.global.ssl.fastly.net/v1"), auth: scala.Option[bombas.storeback.api.v0.Authorization] = None, defaultHeaders: Seq[(String, String)] = Nil, asyncHttpClient: org.http4s.client.Client = Client.defaultAsyncHttpClient ) extends interfaces.Client { import org.http4s.Response import bombas.storeback.api.v0.models.json._ def closeAsyncHttpClient(): Unit = { asyncHttpClient.shutdownNow() } def products: Products = Products object Products extends Products { override def get( productId: String, requestHeaders: Seq[(String, String)] = Nil ): scalaz.concurrent.Task[bombas.storeback.api.v0.models.Product] = { val urlPath = Seq("v1", "products", productId) _executeRequest[Unit, bombas.storeback.api.v0.models.Product]("GET", path = urlPath, requestHeaders = requestHeaders) { case r if r.status.code == 200 => _root_.bombas.storeback.api.v0.Client.parseJson[bombas.storeback.api.v0.models.Product]("bombas.storeback.api.v0.models.Product", r) case r if r.status.code == 400 => scalaz.concurrent.Task.fail(new bombas.storeback.api.v0.errors.UnitResponse(r.status.code)) case r if r.status.code == 404 => scalaz.concurrent.Task.fail(new bombas.storeback.api.v0.errors.UnitResponse(r.status.code)) case r => scalaz.concurrent.Task.fail(new bombas.storeback.api.v0.errors.UnitResponse(r.status.code)) } } } private lazy val defaultApiHeaders = Seq( ("User-Agent", Constants.UserAgent), ("X-Apidoc-Version", Constants.Version), ("X-Apidoc-Version-Major", Constants.VersionMajor.toString) ) def apiHeaders: Seq[(String, String)] = defaultApiHeaders def modifyRequest(request: scalaz.concurrent.Task[org.http4s.Request]): scalaz.concurrent.Task[org.http4s.Request] = request implicit def circeJsonEncoder[A](implicit encoder: io.circe.Encoder[A]) = org.http4s.circe.jsonEncoderOf[A] def _executeRequest[T, U]( method: String, path: Seq[String], queryParameters: Seq[(String, String)] = Nil, requestHeaders: Seq[(String, String)] = Nil, body: Option[T] = None, formBody : Option[org.http4s.UrlForm] = None )(handler: org.http4s.Response => scalaz.concurrent.Task[U] )(implicit encoder: io.circe.Encoder[T]): scalaz.concurrent.Task[U] = { import org.http4s.QueryParamEncoder._ val m = org.http4s.Method.fromString(method) match { case scalaz.\/-(m) => m case scalaz.-\/(e) => sys.error(e.toString) } val headers = org.http4s.Headers(( apiHeaders ++ defaultHeaders ++ requestHeaders ).groupBy(_._1).map { case (k, l) => org.http4s.Header(k, l.last._2) }.toList) val queryMap = queryParameters.groupBy(_._1).map { case (k, v) => k -> v.map(_._2) } val uri = path.foldLeft(baseUrl){ case (uri, segment) => uri / segment }.setQueryParams(queryMap) val request = org.http4s.Request(method = m, uri = uri, headers = headers) val reqAndMaybeAuth = auth.fold(request) { case Authorization.Basic(username, passwordOpt) => { val userpass = s"$username:${passwordOpt.getOrElse("")}" val token = java.util.Base64.getEncoder.encodeToString(userpass.getBytes(java.nio.charset.StandardCharsets.ISO_8859_1)) request.putHeaders(org.http4s.Header("Authorization", s"Basic $token")) } case a => sys.error("Invalid authorization scheme[" + a.getClass + "]") } val reqAndMaybeAuthAndBody = if (formBody.nonEmpty) formBody.fold(scalaz.concurrent.Task.now(reqAndMaybeAuth))(reqAndMaybeAuth.withBody) else body.fold(scalaz.concurrent.Task.now(reqAndMaybeAuth))(reqAndMaybeAuth.withBody) asyncHttpClient.fetch(modifyRequest(reqAndMaybeAuthAndBody))(handler) } } object Client { implicit def circeJsonDecoder[A](implicit decoder: io.circe.Decoder[A]) = org.http4s.circe.jsonOf[A] private lazy val defaultAsyncHttpClient = PooledHttp1Client() def parseJson[T]( className: String, r: org.http4s.Response )(implicit decoder: io.circe.Decoder[T]): scalaz.concurrent.Task[T] = r.attemptAs[T].run.flatMap { case scalaz.\/-(value) => scalaz.concurrent.Task.now(value) case scalaz.-\/(error) => scalaz.concurrent.Task.fail(new bombas.storeback.api.v0.errors.FailedRequest(r.status.code, s"Invalid json for class[" + className + "]", None, error)) } } sealed trait Authorization extends _root_.scala.Product with _root_.scala.Serializable object Authorization { final case class Basic(username: String, password: Option[String] = None) extends Authorization } package interfaces { trait Client { def baseUrl: org.http4s.Uri def products: bombas.storeback.api.v0.Products } } trait Products { /** * Returns a product by ID or by handle. * The following API will return a product object sanitized of private information * from shopify. It should be thought of as a serializer or alturnative interface * to the public shopify information per a product. * * @param productId The ID or handle of the product to return. */ def get( productId: String, requestHeaders: Seq[(String, String)] = Nil ): scalaz.concurrent.Task[bombas.storeback.api.v0.models.Product] } package errors { import bombas.storeback.api.v0.models.json._ final case class UnitResponse(status: Int) extends Exception(s"HTTP $status") final case class FailedRequest(responseCode: Int, message: String, requestUri: Option[_root_.java.net.URI] = None, parent: Exception = null) extends _root_.java.lang.Exception(s"HTTP $responseCode: $message", parent) } }