Refactored actions out into separate files.

This commit is contained in:
2012-04-27 21:32:16 -04:00
parent 3873f88235
commit 715c528a12
7 changed files with 429 additions and 419 deletions

View File

@@ -6,7 +6,7 @@
<name>Zappy</name>
<artifactId>zappy</artifactId>
<groupId>dstu.zappy</groupId>
<version>0.1.0-SNAPSHOT</version>
<version>0.3.0-SNAPSHOT</version>
<inceptionYear>2012</inceptionYear>
<properties>

View File

@@ -1,418 +0,0 @@
package dstu.zappy
import com.lambdaworks.jacks.JacksMapper
import com.sun.jersey.api.client.Client
import com.sun.jersey.core.util.MultivaluedMapImpl
import javax.ws.rs.core.{MediaType, MultivaluedMap}
/**
* Singleton container for API resources.
*/
object Zappos {
val BASE_URL = "http://api.zappos.com/"
lazy val client = Client.create
lazy val resource = client.resource(BASE_URL)
}
/**
* Parent class of all API actions.
*
* @constructor builds a new action
* @param path the additional path component for the specific action (like "Search" or "Product"). This corresponds to an action in the main Zappos API.
* @param key the API key to send with the action
*/
abstract class Base[T](path: String, key: String) {
type Response = T
/**
* Provides type conversion from the Scala Map trait to the Jersey MultivaluedMap interface.
*/
private implicit def mapAsMultivaluedMap(m: Map[String, String]): MultivaluedMap[String, String] =
m.foldLeft(new MultivaluedMapImpl)((result, entry) => {
result.add(entry._1, entry._2)
result
})
/**
* The parameter map for the action. This specifies the
parameters of the GET request that the action will
send. Subclasses should override this method and fill in
their own fields.
*
* @return the parameters for the action's GET request
*/
def parameters: Map[String, String]
/**
* Hydrates a Response object from raw JSON. Subclasses may
override this method and fill in their own implementation.
* @param json the raw JSON
* @return a newly hydrated Response object
*/
def decodeResponse(s: String)(implicit m: Manifest[Response]): Response = JacksMapper.readValue[Response](s)
/**
* Makes the call to the Zappos API.
* @return the raw JSON of the API response
*/
def getJson(): String =
Zappos.resource.path(path).queryParams(parameters + Pair("key", key)).accept(MediaType.APPLICATION_JSON_TYPE).get(classOf[java.lang.String])
/**
* Makes the call to the Zappos API and decodes it.
* @return the response, decoded
*/
def get()(implicit m: Manifest[Response]): Response = decodeResponse(getJson)
}
case class SearchResponse(statusCode: Int,
currentResultCount: Option[Int],
totalResultCount: Option[Int],
limit: Option[Int],
currentPage: Option[Int],
pageCount: Option[Int],
term: String,
originalTerm: String,
results: List[SearchResult])
case class SearchResult(styleId: Int,
productId: Int,
colorId: String,
brandName: String,
productUrl: String,
thumbnailImageUrl: String,
price: String,
productName: String,
originalPrice: String,
percentOff: String,
description: String,
videoUrl: String,
videoFileName: String,
videoUploadedDate: String,
productRating: String,
brandId: String,
categoryFacet: String,
heelHeight: String,
subCategoryFacet: String,
gender: String)
/**
* A search action with the Zappos API. An API key and search
* term must be specified. Other parameters may be specified by
* setter methods.
*
* Instances of this class are immutable; setter methods create
* new instances.
*/
case class Search(key: String,
term: String,
includes: Set[String] = Set.empty,
excludes: Set[String] = Set.empty,
facets: Set[String] = Set.empty,
facetFilters: Map[String, Set[String]] = Map.empty,
limit: Option[Int] = None,
page: Option[Int] = None) extends Base[SearchResponse]("Search", key) {
private def include(item: String) = copy(includes = includes + item, excludes = excludes - item)
private def exclude(item: String) = copy(includes = includes - item, excludes = excludes + item)
def withSearchTerm(s: String) = copy(term = s)
/**
* Sets the maximum number of items to return.
* @param i the desired maximum number of items
* @return a new Search object with the item limit set to the desired value
*/
def withLimit(i: Int) = copy(limit = Some(i))
/**
* Unsets the maximum number of items to return. (The default behavior of the Zappos API will be used instead.)
* @return a new Search object with the item limit unset
*/
def withoutLimit = copy(limit = None)
/**
* Sets the page of results to return.
* @param i the desired page
* @return a new Search object with the page set to the desired value
*/
def withPage(i: Int) = copy(page = Some(i))
/**
* Unsets the page to return. (The default behavior of the Zappos API will be used instead.)
* @return a new Search object with the page unset
*/
def withoutPage = copy(page = None)
/**
* Adds a facet filter, filtering for items that have one of
the given values in the given facet. This replaces any
existing filter on the given facet. Multiple filters may be
specified; they are logically AND'ed by the Zappos API.
* @param facet the facet to filter on
* @param values the values to filter for
* @return a new Search object with the new facet filter
*/
def withFacetFilter(facet: String, values: Set[String]) = copy(facetFilters = facetFilters + Pair(facet, values))
/**
* Removes all filters on the given facet.
* @param facet the facet for which to remove filters
* @return a new Search object without the given facet filter
*/
def withoutFacetFilter(facet: String) = copy(facetFilters = facetFilters - facet)
/**
* Removes all facet filters.
* @return a new Search object without any facet filters
*/
def withoutFacetFilters = copy(facetFilters = Map.empty)
def withStyleId = include("styleId")
def withoutStyleId = exclude("styleId")
def withProductId = include("productId")
def withoutProductId = exclude("productId")
def withColorId = include("colorId")
def withoutColorId = exclude("colorId")
def withBrandName = include("withBrandName")
def withoutBrandName = exclude("withBrandName")
def withProductName = include("productName")
def withoutProductName = exclude("productName")
def withProductUrl = include("productUrl")
def withoutProductUrl = exclude("productUrl")
def withThumbnailImageUrl = include("thumbnailImageUrl")
def withoutThumbnailImageUrl = exclude("thumbnailImageUrl")
def withPrice = include("price")
def withoutPrice = exclude("price")
def withOriginalPrice = include("originalPrice")
def withoutOriginalPrice = exclude("originalPrice")
def withPercentOff = include("percentOff")
def withoutPercentOff = exclude("percentOff")
def withDescription = include("description")
def withoutDescription = exclude("description")
def withVideoUrl = include("videoUrl")
def withoutVideoUrl = exclude("videoUrl")
def withVideoFileName = include("videoFileName")
def withoutVideoFileName = exclude("videoFileName")
def withVideoUploadedDate = include("videoUploadedDate")
def withoutVideoUploadedDate = exclude("videoUploadedDate")
def withProductRating = include("productRating")
def withoutProductRating = exclude("productRating")
def withBrandId = include("brandId")
def withoutBrandId = exclude("brandId")
def withCategoryFacet = include("categoryFacet")
def withoutCategoryFacet = exclude("categoryFacet")
def withHeelHeight = include("heelHeight")
def withoutHeelHeight = exclude("heelHeight")
def withSubCategoryFacet = include("subCategoryFacet")
def withoutSubCategoryFacet = exclude("subCategoryFacet")
def withGender = include("txAttrFacet_Gender")
def withoutGender = exclude("txAttrFacet_Gender")
def withTotalResultCount = include("totalResultCount")
def withoutTotalResultCount = exclude("totalResultCount")
def withFacets = include("facets")
def withoutFacets = exclude("facets")
override def parameters =
Map("term" -> term) ++
includes.map(i => Pair("includes", JacksMapper.writeValueAsString(i))).toMap ++
excludes.map(e => Pair("excludes", JacksMapper.writeValueAsString(e))).toMap ++
facets.map(f => Pair("facets", JacksMapper.writeValueAsString(f))).toMap ++
(if (facetFilters.isEmpty) Map.empty
else Map("filters" -> JacksMapper.writeValueAsString(facetFilters))) ++
limit.map(i => Pair("limit", i.toString)).toMap ++
page.map(i => Pair("page", i.toString)).toMap
}
case class ImageResponse(statusCode: Int,
productId: String,
images: Map[String, List[ImageResult]])
case class ImageResult(styleId: String,
productId: String,
`type`: String,
recipeName: String,
format: String,
filename: String,
colorId: String,
width: Option[Int],
height: Option[Int],
uploadDate: String,
isHighResolution: Boolean,
tiles: String)
/**
* An image query. An API key and product ID must be
specified. Other parameters may be specified by setter
methods.
*
* Instances of this class are immutable; setter methods create
* new instances.
*/
case class Image(key: String, productId: String, includes: Set[String] = Set.empty, excludes: Set[String] = Set.empty) extends Base[ImageResponse]("Image", key) {
def withStyleId = copy(includes = includes + "styleId", excludes = excludes - "styleId")
def withoutStyleId = copy(includes = includes - "styleId", excludes = excludes + "styleId")
def withProductId = copy(includes = includes + "productId", excludes = excludes - "productId")
def withoutProductId = copy(includes = includes - "productId", excludes = excludes + "productId")
def withType = copy(includes = includes + "type", excludes = excludes - "type")
def withoutType = copy(includes = includes - "type", excludes = excludes + "type")
def withRecipeName = copy(includes = includes + "recipeName", excludes = excludes - "recipeName")
def withoutRecipeName = copy(includes = includes - "recipeName", excludes = excludes + "recipeName")
def withFormat = copy(includes = includes + "format", excludes = excludes - "format")
def withoutFormat = copy(includes = includes - "format", excludes = excludes + "format")
def withFilename = copy(includes = includes + "filename", excludes = excludes - "filename")
def withoutFilename = copy(includes = includes - "filename", excludes = excludes + "filename")
def withColorId = copy(includes = includes + "colorId", excludes = excludes - "colorId")
def withoutColorId = copy(includes = includes - "colorId", excludes = excludes + "colorId")
def withWidth = copy(includes = includes + "width", excludes = excludes - "width")
def withoutWidth = copy(includes = includes - "width", excludes = excludes + "width")
def withHeight = copy(includes = includes + "height", excludes = excludes - "height")
def withoutHeight = copy(includes = includes - "height", excludes = excludes + "height")
def withUploadDate = copy(includes = includes + "uploadDate", excludes = excludes - "uploadDate")
def withoutUploadDate = copy(includes = includes - "uploadDate", excludes = excludes + "uploadDate")
def withHighResolution = copy(includes = includes + "isHighResolution", excludes = excludes - "isHighResolution")
def withoutHighResolution = copy(includes = includes - "isHighResolution", excludes = excludes + "isHighResolution")
def withTiles = copy(includes = includes + "tiles", excludes = excludes - "tiles")
def withoutTiles = copy(includes = includes - "tiles", excludes = excludes + "tiles")
override def parameters =
Map("productId" -> productId) ++
includes.map(i => Pair("includes", JacksMapper.writeValueAsString(i))).toMap ++
excludes.map(e => Pair("excludes", JacksMapper.writeValueAsString(e))).toMap
}
case class ProductResponse(statusCode: Int,
product: List[ProductResult])
case class ProductResult(productId: String,
brandName: String,
productName: String,
defaultProductUrl: String,
defaultPrettyProductUrl: String,
defaultImageUrl: String,
description: String,
gender: String,
weight: String,
videos: List[VideoInfo],
sizeFit: String,
widthFit: String,
archFit: String,
productRating: Double,
overallRating: Double,
comfortRating: Double,
lookRating: Double,
styles: List[StyleInfo],
defaultProductType: String,
defaultCategory: String,
defaultSubCategory: String,
attributeFacetFields: Map[String, String])
case class VideoInfo(id: String, filename: String, productId: String, uploadedDate: String, videoEncodingId: String, videoEncodingExtension: String)
case class StyleInfo(styleId: String, color: String, originalPrice: String, price: String, productUrl: String, imageUrl: String, stocks: List[Map[String, String]])
/**
* A product query action with the Zappos API. An API key and
* at least one product ID must be specified. Other parameters may be
* specified by setter methods.
*
* Instances of this class are immutable; setter methods create
* new instances.
*/
case class Product(key: String,
productIds: Set[String] = Set.empty,
stockIds: Set[String] = Set.empty,
upcs: Set[String] = Set.empty,
includes: Set[String] = Set.empty,
excludes: Set[String] = Set.empty) extends Base[ProductResponse]("Product", key) {
private def include(item: String) = copy(includes = includes + item, excludes = excludes - item)
private def exclude(item: String) = copy(includes = includes - item, excludes = excludes + item)
def withProductIdss(s: Seq[String]) = copy(productIds = s.toSet)
def withStockIds(s: Seq[String]) = copy(stockIds = s.toSet)
def withUpcs(s: Seq[String]) = copy(upcs = s.toSet)
def withProductId = copy(includes = includes + "productId", excludes = excludes - "productId")
def withoutProductId = copy(includes = includes - "productId", excludes = excludes + "productId")
def withBrandName = copy(includes = includes + "brandName", excludes = excludes - "brandName")
def withoutBrandName = copy(includes = includes - "brandName", excludes = excludes + "brandName")
def withProductName = copy(includes = includes + "productName", excludes = excludes - "productName")
def withoutProductName = copy(includes = includes - "productName", excludes = excludes + "productName")
def withDefaultProductUrl = copy(includes = includes + "defaultProductUrl", excludes = excludes - "defaultProductUrl")
def withoutDefaultProductUrl = copy(includes = includes - "defaultProductUrl", excludes = excludes + "defaultProductUrl")
def withDefaultPrettyProductUrl = copy(includes = includes + "defaultPrettyProductUrl", excludes = excludes - "defaultPrettyProductUrl")
def withoutDefaultPrettyProductUrl = copy(includes = includes - "defaultPrettyProductUrl", excludes = excludes + "defaultPrettyProductUrl")
def withDefaultImageUrl = copy(includes = includes + "defaultImageUrl", excludes = excludes - "defaultImageUrl")
def withoutDefaultImageUrl = copy(includes = includes - "defaultImageUrl", excludes = excludes + "defaultImageUrl")
def withDescription = copy(includes = includes + "description", excludes = excludes - "description")
def withoutDescription = copy(includes = includes - "description", excludes = excludes + "description")
def withGender = copy(includes = includes + "gender", excludes = excludes - "gender")
def withoutGender = copy(includes = includes - "gender", excludes = excludes + "gender")
def withWeight = copy(includes = includes + "weight", excludes = excludes - "weight")
def withoutWeight = copy(includes = includes - "weight", excludes = excludes + "weight")
def withVideos = copy(includes = includes + "videos", excludes = excludes - "videos")
def withoutVideos = copy(includes = includes - "videos", excludes = excludes + "videos")
def withVideoFileName = copy(includes = includes + "videoFileName", excludes = excludes - "videoFileName")
def withoutVideoFileName = copy(includes = includes - "videoFileName", excludes = excludes + "videoFileName")
def withVideoUrl = copy(includes = includes + "videoUrl", excludes = excludes - "videoUrl")
def withoutVideoUrl = copy(includes = includes - "videoUrl", excludes = excludes + "videoUrl")
def withVideoUploadedDate = copy(includes = includes + "videoUploadedDate", excludes = excludes - "videoUploadedDate")
def withoutVideoUploadedDate = copy(includes = includes - "videoUploadedDate", excludes = excludes + "videoUploadedDate")
def withSizeFit = copy(includes = includes + "sizeFit", excludes = excludes - "sizeFit")
def withoutSizeFit = copy(includes = includes - "sizeFit", excludes = excludes + "sizeFit")
def withWidthFit = copy(includes = includes + "widthFit", excludes = excludes - "widthFit")
def withoutWidthFit = copy(includes = includes - "widthFit", excludes = excludes + "widthFit")
def withArchFit = copy(includes = includes + "archFit", excludes = excludes - "archFit")
def withoutArchFit = copy(includes = includes - "archFit", excludes = excludes + "archFit")
def withProductRating = copy(includes = includes + "productRating", excludes = excludes - "productRating")
def withoutProductRating = copy(includes = includes - "productRating", excludes = excludes + "productRating")
def withOverallRating = copy(includes = includes + "overallRating", excludes = excludes - "overallRating")
def withoutOverallRating = copy(includes = includes - "overallRating", excludes = excludes + "overallRating")
def withComfortRating = copy(includes = includes + "comfortRating", excludes = excludes - "comfortRating")
def withoutComfortRating = copy(includes = includes - "comfortRating", excludes = excludes + "comfortRating")
def withLookRating = copy(includes = includes + "lookRating", excludes = excludes - "lookRating")
def withoutLookRating = copy(includes = includes - "lookRating", excludes = excludes + "lookRating")
def withStyles = copy(includes = includes + "styles", excludes = excludes - "styles")
def withoutStyles = copy(includes = includes - "styles", excludes = excludes + "styles")
def withDefaultCategory = copy(includes = includes + "defaultCategory", excludes = excludes - "defaultCategory")
def withoutDefaultCategory = copy(includes = includes - "defaultCategory", excludes = excludes + "defaultCategory")
def withDefaultSubCategory = copy(includes = includes + "defaultSubCategory", excludes = excludes - "defaultSubCategory")
def withoutDefaultSubCategory = copy(includes = includes - "defaultSubCategory", excludes = excludes + "defaultSubCategory")
def withAttributeFacetFields = copy(includes = includes + "attributeFacetFields", excludes = excludes - "attributeFacetFields")
def withoutAttributeFacetFields = copy(includes = includes - "attributeFacetFields", excludes = excludes + "attributeFacetFields")
override def parameters = {
val nonEmpty = List(Pair("id", productIds), Pair("stockId", stockIds), Pair("upc", upcs)).filter(!_._2.isEmpty)
assert(nonEmpty.map(_._2.size).sum <= 10, "Must specify at most 10 products")
// assert(nonEmpty.size == 1, "Must specify only one of product ID, stock ID, or UPC")
// val searchField = nonEmpty.head._1
// val searchValues = nonEmpty.head._2
nonEmpty.map(p => Pair(p._1, JacksMapper.writeValueAsString(p._2))).toMap ++
includes.map(i => Pair("includes", JacksMapper.writeValueAsString(i))).toMap ++
excludes.map(e => Pair("excludes", JacksMapper.writeValueAsString(e))).toMap
}
}
case class ReviewResponse(statusCode: Int,
page: Option[Int],
offset: Option[Int],
reviews: List[ReviewResult])
case class ReviewResult(id: String,
date: String,
name: String,
location: String,
otherShoes: String,
summary: String,
shoeSize: String,
shoeWidth: String,
shoeArch: String,
overallRating: Option[Int],
comfortRating: Option[Int],
lookRating: Option[Int])
case class Review(key: String,
productId: String,
page: Option[Int] = None,
startId: Option[Int] = None) extends Base[ReviewResponse]("Review", key) {
override def parameters =
Map("productId" -> productId) ++
page.map(i => Pair("page", i.toString)).toMap ++
startId.map(i => Pair("startId", i.toString)).toMap
}

View File

@@ -0,0 +1,66 @@
package dstu.zappy
import com.lambdaworks.jacks.JacksMapper
import com.sun.jersey.api.client.Client
import com.sun.jersey.core.util.MultivaluedMapImpl
import javax.ws.rs.core.{MediaType, MultivaluedMap}
/**
* Singleton container for API resources.
*/
object Zappos {
val BASE_URL = "http://api.zappos.com/"
lazy val client = Client.create
lazy val resource = client.resource(BASE_URL)
}
/**
* Parent class of all API actions.
*
* @constructor builds a new action
* @param path the additional path component for the specific action (like "Search" or "Product"). This corresponds to an action in the main Zappos API.
* @param key the API key to send with the action
*/
abstract class BaseAction[T](path: String, key: String) {
type Response = T
/**
* Provides type conversion from the Scala Map trait to the Jersey MultivaluedMap interface.
*/
private implicit def mapAsMultivaluedMap(m: Map[String, String]): MultivaluedMap[String, String] =
m.foldLeft(new MultivaluedMapImpl)((result, entry) => {
result.add(entry._1, entry._2)
result
})
/**
* The parameter map for the action. This specifies the
parameters of the GET request that the action will
send. Subclasses should override this method and fill in
their own fields.
*
* @return the parameters for the action's GET request
*/
def parameters: Map[String, String]
/**
* Hydrates a Response object from raw JSON. Subclasses may
override this method and fill in their own implementation.
* @param json the raw JSON
* @return a newly hydrated Response object
*/
def decodeResponse(s: String)(implicit m: Manifest[Response]): Response = JacksMapper.readValue[Response](s)
/**
* Makes the call to the Zappos API.
* @return the raw JSON of the API response
*/
def getJson(): String =
Zappos.resource.path(path).queryParams(parameters + Pair("key", key)).accept(MediaType.APPLICATION_JSON_TYPE).get(classOf[java.lang.String])
/**
* Makes the call to the Zappos API and decodes it.
* @return the response, decoded
*/
def get()(implicit m: Manifest[Response]): Response = decodeResponse(getJson)
}

View File

@@ -0,0 +1,60 @@
package dstu.zappy
import com.lambdaworks.jacks.JacksMapper
case class ImageResponse(statusCode: Int,
productId: String,
images: Map[String, List[ImageResult]])
case class ImageResult(styleId: String,
productId: String,
`type`: String,
recipeName: String,
format: String,
filename: String,
colorId: String,
width: Option[Int],
height: Option[Int],
uploadDate: String,
isHighResolution: Option[Boolean],
tiles: String)
/**
* An image query. An API key and product ID must be
specified. Other parameters may be specified by setter
methods.
*
* Instances of this class are immutable; setter methods create
* new instances.
*/
case class Image(key: String, productId: String, includes: Set[String] = Set.empty, excludes: Set[String] = Set.empty) extends BaseAction[ImageResponse]("Image", key) {
def withStyleId = copy(includes = includes + "styleId", excludes = excludes - "styleId")
def withoutStyleId = copy(includes = includes - "styleId", excludes = excludes + "styleId")
def withProductId = copy(includes = includes + "productId", excludes = excludes - "productId")
def withoutProductId = copy(includes = includes - "productId", excludes = excludes + "productId")
def withType = copy(includes = includes + "type", excludes = excludes - "type")
def withoutType = copy(includes = includes - "type", excludes = excludes + "type")
def withRecipeName = copy(includes = includes + "recipeName", excludes = excludes - "recipeName")
def withoutRecipeName = copy(includes = includes - "recipeName", excludes = excludes + "recipeName")
def withFormat = copy(includes = includes + "format", excludes = excludes - "format")
def withoutFormat = copy(includes = includes - "format", excludes = excludes + "format")
def withFilename = copy(includes = includes + "filename", excludes = excludes - "filename")
def withoutFilename = copy(includes = includes - "filename", excludes = excludes + "filename")
def withColorId = copy(includes = includes + "colorId", excludes = excludes - "colorId")
def withoutColorId = copy(includes = includes - "colorId", excludes = excludes + "colorId")
def withWidth = copy(includes = includes + "width", excludes = excludes - "width")
def withoutWidth = copy(includes = includes - "width", excludes = excludes + "width")
def withHeight = copy(includes = includes + "height", excludes = excludes - "height")
def withoutHeight = copy(includes = includes - "height", excludes = excludes + "height")
def withUploadDate = copy(includes = includes + "uploadDate", excludes = excludes - "uploadDate")
def withoutUploadDate = copy(includes = includes - "uploadDate", excludes = excludes + "uploadDate")
def withHighResolution = copy(includes = includes + "isHighResolution", excludes = excludes - "isHighResolution")
def withoutHighResolution = copy(includes = includes - "isHighResolution", excludes = excludes + "isHighResolution")
def withTiles = copy(includes = includes + "tiles", excludes = excludes - "tiles")
def withoutTiles = copy(includes = includes - "tiles", excludes = excludes + "tiles")
override def parameters =
Map("productId" -> productId) ++
includes.map(i => Pair("includes", JacksMapper.writeValueAsString(i))).toMap ++
excludes.map(e => Pair("excludes", JacksMapper.writeValueAsString(e))).toMap
}

View File

@@ -0,0 +1,115 @@
package dstu.zappy
import com.lambdaworks.jacks.JacksMapper
case class ProductResponse(statusCode: Int,
product: List[ProductResult])
case class ProductResult(productId: String,
brandName: String,
productName: String,
defaultProductUrl: String,
defaultPrettyProductUrl: String,
defaultImageUrl: String,
description: String,
gender: String,
weight: String,
videos: List[VideoInfo],
sizeFit: String,
widthFit: String,
archFit: String,
productRating: Double,
overallRating: Double,
comfortRating: Double,
lookRating: Double,
styles: List[StyleInfo],
defaultProductType: String,
defaultCategory: String,
defaultSubCategory: String,
attributeFacetFields: Map[String, String])
case class VideoInfo(id: String, filename: String, productId: String, uploadedDate: String, videoEncodingId: String, videoEncodingExtension: String)
case class StyleInfo(styleId: String, color: String, originalPrice: String, price: String, productUrl: String, imageUrl: String, stocks: List[Map[String, String]])
/**
* A product query action with the Zappos API. An API key and
* at least one product ID must be specified. Other parameters may be
* specified by setter methods.
*
* Instances of this class are immutable; setter methods create
* new instances.
*/
case class Product(key: String,
productIds: Set[String] = Set.empty,
stockIds: Set[String] = Set.empty,
upcs: Set[String] = Set.empty,
includes: Set[String] = Set.empty,
excludes: Set[String] = Set.empty) extends BaseAction[ProductResponse]("Product", key) {
private def include(item: String) = copy(includes = includes + item, excludes = excludes - item)
private def exclude(item: String) = copy(includes = includes - item, excludes = excludes + item)
def withProductIdss(s: Seq[String]) = copy(productIds = s.toSet)
def withStockIds(s: Seq[String]) = copy(stockIds = s.toSet)
def withUpcs(s: Seq[String]) = copy(upcs = s.toSet)
def withProductId = copy(includes = includes + "productId", excludes = excludes - "productId")
def withoutProductId = copy(includes = includes - "productId", excludes = excludes + "productId")
def withBrandName = copy(includes = includes + "brandName", excludes = excludes - "brandName")
def withoutBrandName = copy(includes = includes - "brandName", excludes = excludes + "brandName")
def withProductName = copy(includes = includes + "productName", excludes = excludes - "productName")
def withoutProductName = copy(includes = includes - "productName", excludes = excludes + "productName")
def withDefaultProductUrl = copy(includes = includes + "defaultProductUrl", excludes = excludes - "defaultProductUrl")
def withoutDefaultProductUrl = copy(includes = includes - "defaultProductUrl", excludes = excludes + "defaultProductUrl")
def withDefaultPrettyProductUrl = copy(includes = includes + "defaultPrettyProductUrl", excludes = excludes - "defaultPrettyProductUrl")
def withoutDefaultPrettyProductUrl = copy(includes = includes - "defaultPrettyProductUrl", excludes = excludes + "defaultPrettyProductUrl")
def withDefaultImageUrl = copy(includes = includes + "defaultImageUrl", excludes = excludes - "defaultImageUrl")
def withoutDefaultImageUrl = copy(includes = includes - "defaultImageUrl", excludes = excludes + "defaultImageUrl")
def withDescription = copy(includes = includes + "description", excludes = excludes - "description")
def withoutDescription = copy(includes = includes - "description", excludes = excludes + "description")
def withGender = copy(includes = includes + "gender", excludes = excludes - "gender")
def withoutGender = copy(includes = includes - "gender", excludes = excludes + "gender")
def withWeight = copy(includes = includes + "weight", excludes = excludes - "weight")
def withoutWeight = copy(includes = includes - "weight", excludes = excludes + "weight")
def withVideos = copy(includes = includes + "videos", excludes = excludes - "videos")
def withoutVideos = copy(includes = includes - "videos", excludes = excludes + "videos")
def withVideoFileName = copy(includes = includes + "videoFileName", excludes = excludes - "videoFileName")
def withoutVideoFileName = copy(includes = includes - "videoFileName", excludes = excludes + "videoFileName")
def withVideoUrl = copy(includes = includes + "videoUrl", excludes = excludes - "videoUrl")
def withoutVideoUrl = copy(includes = includes - "videoUrl", excludes = excludes + "videoUrl")
def withVideoUploadedDate = copy(includes = includes + "videoUploadedDate", excludes = excludes - "videoUploadedDate")
def withoutVideoUploadedDate = copy(includes = includes - "videoUploadedDate", excludes = excludes + "videoUploadedDate")
def withSizeFit = copy(includes = includes + "sizeFit", excludes = excludes - "sizeFit")
def withoutSizeFit = copy(includes = includes - "sizeFit", excludes = excludes + "sizeFit")
def withWidthFit = copy(includes = includes + "widthFit", excludes = excludes - "widthFit")
def withoutWidthFit = copy(includes = includes - "widthFit", excludes = excludes + "widthFit")
def withArchFit = copy(includes = includes + "archFit", excludes = excludes - "archFit")
def withoutArchFit = copy(includes = includes - "archFit", excludes = excludes + "archFit")
def withProductRating = copy(includes = includes + "productRating", excludes = excludes - "productRating")
def withoutProductRating = copy(includes = includes - "productRating", excludes = excludes + "productRating")
def withOverallRating = copy(includes = includes + "overallRating", excludes = excludes - "overallRating")
def withoutOverallRating = copy(includes = includes - "overallRating", excludes = excludes + "overallRating")
def withComfortRating = copy(includes = includes + "comfortRating", excludes = excludes - "comfortRating")
def withoutComfortRating = copy(includes = includes - "comfortRating", excludes = excludes + "comfortRating")
def withLookRating = copy(includes = includes + "lookRating", excludes = excludes - "lookRating")
def withoutLookRating = copy(includes = includes - "lookRating", excludes = excludes + "lookRating")
def withStyles = copy(includes = includes + "styles", excludes = excludes - "styles")
def withoutStyles = copy(includes = includes - "styles", excludes = excludes + "styles")
def withDefaultCategory = copy(includes = includes + "defaultCategory", excludes = excludes - "defaultCategory")
def withoutDefaultCategory = copy(includes = includes - "defaultCategory", excludes = excludes + "defaultCategory")
def withDefaultSubCategory = copy(includes = includes + "defaultSubCategory", excludes = excludes - "defaultSubCategory")
def withoutDefaultSubCategory = copy(includes = includes - "defaultSubCategory", excludes = excludes + "defaultSubCategory")
def withAttributeFacetFields = copy(includes = includes + "attributeFacetFields", excludes = excludes - "attributeFacetFields")
def withoutAttributeFacetFields = copy(includes = includes - "attributeFacetFields", excludes = excludes + "attributeFacetFields")
override def parameters = {
val nonEmpty = List(Pair("id", productIds), Pair("stockId", stockIds), Pair("upc", upcs)).filter(!_._2.isEmpty)
assert(nonEmpty.map(_._2.size).sum <= 10, "Must specify at most 10 products")
// assert(nonEmpty.size == 1, "Must specify only one of product ID, stock ID, or UPC")
// val searchField = nonEmpty.head._1
// val searchValues = nonEmpty.head._2
nonEmpty.map(p => Pair(p._1, JacksMapper.writeValueAsString(p._2))).toMap ++
includes.map(i => Pair("includes", JacksMapper.writeValueAsString(i))).toMap ++
excludes.map(e => Pair("excludes", JacksMapper.writeValueAsString(e))).toMap
}
}

View File

@@ -0,0 +1,29 @@
package dstu.zappy
case class ReviewResponse(statusCode: Int,
page: Option[Int],
offset: Option[Int],
reviews: List[ReviewResult])
case class ReviewResult(id: String,
date: String,
name: String,
location: String,
otherShoes: String,
summary: String,
shoeSize: String,
shoeWidth: String,
shoeArch: String,
overallRating: Option[Int],
comfortRating: Option[Int],
lookRating: Option[Int])
case class Review(key: String,
productId: String,
page: Option[Int] = None,
startId: Option[Int] = None) extends BaseAction[ReviewResponse]("Review", key) {
override def parameters =
Map("productId" -> productId) ++
page.map(i => Pair("page", i.toString)).toMap ++
startId.map(i => Pair("startId", i.toString)).toMap
}

View File

@@ -0,0 +1,158 @@
package dstu.zappy
import com.lambdaworks.jacks.JacksMapper
case class SearchResponse(statusCode: Int,
currentResultCount: Option[Int],
totalResultCount: Option[Int],
limit: Option[Int],
currentPage: Option[Int],
pageCount: Option[Int],
term: String,
originalTerm: String,
results: List[SearchResult])
case class SearchResult(styleId: Int,
productId: Int,
colorId: String,
brandName: String,
productUrl: String,
thumbnailImageUrl: String,
price: String,
productName: String,
originalPrice: String,
percentOff: String,
description: String,
videoUrl: String,
videoFileName: String,
videoUploadedDate: String,
productRating: String,
brandId: String,
categoryFacet: String,
heelHeight: String,
subCategoryFacet: String,
gender: String)
/**
* A search action with the Zappos API. An API key and search
* term must be specified. Other parameters may be specified by
* setter methods.
*
* Instances of this class are immutable; setter methods create
* new instances.
*/
case class Search(key: String,
term: String,
includes: Set[String] = Set.empty,
excludes: Set[String] = Set.empty,
facets: Set[String] = Set.empty,
facetFilters: Map[String, Set[String]] = Map.empty,
limit: Option[Int] = None,
page: Option[Int] = None) extends BaseAction[SearchResponse]("Search", key) {
private def include(item: String) = copy(includes = includes + item, excludes = excludes - item)
private def exclude(item: String) = copy(includes = includes - item, excludes = excludes + item)
def withSearchTerm(s: String) = copy(term = s)
/**
* Sets the maximum number of items to return.
* @param i the desired maximum number of items
* @return a new Search object with the item limit set to the desired value
*/
def withLimit(i: Int) = copy(limit = Some(i))
/**
* Unsets the maximum number of items to return. (The default behavior of the Zappos API will be used instead.)
* @return a new Search object with the item limit unset
*/
def withoutLimit = copy(limit = None)
/**
* Sets the page of results to return.
* @param i the desired page
* @return a new Search object with the page set to the desired value
*/
def withPage(i: Int) = copy(page = Some(i))
/**
* Unsets the page to return. (The default behavior of the Zappos API will be used instead.)
* @return a new Search object with the page unset
*/
def withoutPage = copy(page = None)
/**
* Adds a facet filter, filtering for items that have one of
the given values in the given facet. This replaces any
existing filter on the given facet. Multiple filters may be
specified; they are logically AND'ed by the Zappos API.
* @param facet the facet to filter on
* @param values the values to filter for
* @return a new Search object with the new facet filter
*/
def withFacetFilter(facet: String, values: Set[String]) = copy(facetFilters = facetFilters + Pair(facet, values))
/**
* Removes all filters on the given facet.
* @param facet the facet for which to remove filters
* @return a new Search object without the given facet filter
*/
def withoutFacetFilter(facet: String) = copy(facetFilters = facetFilters - facet)
/**
* Removes all facet filters.
* @return a new Search object without any facet filters
*/
def withoutFacetFilters = copy(facetFilters = Map.empty)
def withStyleId = include("styleId")
def withoutStyleId = exclude("styleId")
def withProductId = include("productId")
def withoutProductId = exclude("productId")
def withColorId = include("colorId")
def withoutColorId = exclude("colorId")
def withBrandName = include("withBrandName")
def withoutBrandName = exclude("withBrandName")
def withProductName = include("productName")
def withoutProductName = exclude("productName")
def withProductUrl = include("productUrl")
def withoutProductUrl = exclude("productUrl")
def withThumbnailImageUrl = include("thumbnailImageUrl")
def withoutThumbnailImageUrl = exclude("thumbnailImageUrl")
def withPrice = include("price")
def withoutPrice = exclude("price")
def withOriginalPrice = include("originalPrice")
def withoutOriginalPrice = exclude("originalPrice")
def withPercentOff = include("percentOff")
def withoutPercentOff = exclude("percentOff")
def withDescription = include("description")
def withoutDescription = exclude("description")
def withVideoUrl = include("videoUrl")
def withoutVideoUrl = exclude("videoUrl")
def withVideoFileName = include("videoFileName")
def withoutVideoFileName = exclude("videoFileName")
def withVideoUploadedDate = include("videoUploadedDate")
def withoutVideoUploadedDate = exclude("videoUploadedDate")
def withProductRating = include("productRating")
def withoutProductRating = exclude("productRating")
def withBrandId = include("brandId")
def withoutBrandId = exclude("brandId")
def withCategoryFacet = include("categoryFacet")
def withoutCategoryFacet = exclude("categoryFacet")
def withHeelHeight = include("heelHeight")
def withoutHeelHeight = exclude("heelHeight")
def withSubCategoryFacet = include("subCategoryFacet")
def withoutSubCategoryFacet = exclude("subCategoryFacet")
def withGender = include("txAttrFacet_Gender")
def withoutGender = exclude("txAttrFacet_Gender")
def withTotalResultCount = include("totalResultCount")
def withoutTotalResultCount = exclude("totalResultCount")
def withFacets = include("facets")
def withoutFacets = exclude("facets")
override def parameters =
Map("term" -> term) ++
includes.map(i => Pair("includes", JacksMapper.writeValueAsString(i))).toMap ++
excludes.map(e => Pair("excludes", JacksMapper.writeValueAsString(e))).toMap ++
facets.map(f => Pair("facets", JacksMapper.writeValueAsString(f))).toMap ++
(if (facetFilters.isEmpty) Map.empty
else Map("filters" -> JacksMapper.writeValueAsString(facetFilters))) ++
limit.map(i => Pair("limit", i.toString)).toMap ++
page.map(i => Pair("page", i.toString)).toMap
}