Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Deleted old Xml parser, lift-json now used for all parsing

  • Loading branch information...
commit 03ec742b142445c9ac64c776f2bd82591e80d256 1 parent adb733b
@tackley tackley authored
Showing with 212 additions and 1,129 deletions.
  1. +38 −38 src/main/scala/com/gu/openplatform/contentapi/{connection → }/Api.scala
  2. +17 −11 src/main/scala/com/gu/openplatform/contentapi/connection/Http.scala
  3. +59 −114 src/main/scala/com/gu/openplatform/contentapi/model/Model.scala
  4. +0 −147 src/main/scala/com/gu/openplatform/contentapi/model/NewModel.scala
  5. +69 −0 src/main/scala/com/gu/openplatform/contentapi/model/Responses.scala
  6. +1 −0  src/main/scala/com/gu/openplatform/contentapi/parser/JodaJsonSerializer.scala
  7. +5 −5 src/main/scala/com/gu/openplatform/contentapi/parser/JsonParser.scala
  8. +0 −220 src/main/scala/com/gu/openplatform/contentapi/parser/XmlParser.scala
  9. +16 −89 src/test/scala/{com/gu/openplatform/contentapi → }/ExampleUsageTest.scala
  10. +0 −1  src/test/scala/com/gu/openplatform/contentapi/ApiTest.scala
  11. +0 −32 src/test/scala/com/gu/openplatform/contentapi/NewTest.scala
  12. +2 −48 src/test/scala/com/gu/openplatform/contentapi/parser/SearchJsonParserTest.scala
  13. +1 −1  src/test/scala/com/gu/openplatform/contentapi/parser/SectionsJsonParserTest.scala
  14. +3 −3 src/test/scala/com/gu/openplatform/contentapi/parser/TagItemJsonParserTest.scala
  15. +1 −1  src/test/scala/com/gu/openplatform/contentapi/parser/TagsJsonParserTest.scala
  16. +0 −419 src/test/scala/com/gu/openplatform/contentapi/parser/XMLParserTest.scala
View
76 ...m/gu/openplatform/contentapi/connection/Api.scala → ...in/scala/com/gu/openplatform/contentapi/Api.scala
@@ -1,15 +1,16 @@
-package com.gu.openplatform.contentapi.connection
+package com.gu.openplatform.contentapi
-import com.gu.openplatform.contentapi.model._
-import com.gu.openplatform.contentapi.parser.XmlParser
-import java.net.{URLEncoder, URL}
+import connection.{ApacheHttpClient, Http}
+import java.net.URLEncoder
+import com.gu.openplatform.contentapi.parser.JsonParser
+import scala.collection
// thrown when an "expected" error is thrown by the api
case class ApiError(val httpStatus: Int, val httpMessage: String)
extends Exception(httpMessage)
-object Api {
+abstract class Api extends Http {
val targetUrl = "http://content.guardianapis.com"
var apiKey: Option[String] = None
@@ -20,13 +21,11 @@ object Api {
def itemQuery = new ItemQuery
- trait ApiQuery[T]{
- def getResponse(endpoint: String, responseString: String) =
- XmlParser.parseEndpoint(endpoint, responseString)
-
- def mandatoryParameters = "?format=xml"
+ trait ApiQuery[T] extends JsonParser {
+ def mandatoryParameters = "?"
def optionalParameters = apiKey.map("&api-key=" + _).getOrElse("")
+
}
trait PaginatedQuery[T] {
@@ -180,14 +179,11 @@ object Api {
}
}
- class SectionsQuery extends ApiQuery[SectionsQuery] with SearchTermQuery[SectionsQuery] {
+ class SectionsQuery
+ extends ApiQuery[SectionsQuery]
+ with SearchTermQuery[SectionsQuery] {
- def sections = parseSectionsResponse(Http GET buildUrl)
-
- private def parseSectionsResponse(httpResponse: HttpResponse) = {
- val response = getResponse("sections", httpResponse.body)
- response.asInstanceOf[SectionsResponse]
- }
+ def sections = parseSections(fetch(buildUrl))
def buildUrl = new StringBuilder()
.append(targetUrl)
@@ -199,7 +195,9 @@ object Api {
}
class TagsQuery extends ApiQuery[TagsQuery]
- with PaginatedQuery[TagsQuery] with ConfigurableItemDisplay[TagsQuery] with SearchTermQuery[TagsQuery] {
+ with PaginatedQuery[TagsQuery]
+ with ConfigurableItemDisplay[TagsQuery]
+ with SearchTermQuery[TagsQuery] {
var sectionTerm: Option[String] = None
var typeTerm: Option[String] = None
@@ -214,12 +212,7 @@ object Api {
this
}
- def tags: TagsResponse = parseTagsResponse(Http GET buildUrl)
-
- private def parseTagsResponse(httpResponse: HttpResponse): TagsResponse = {
- val response = getResponse("tags", httpResponse.body)
- response.asInstanceOf[TagsResponse]
- }
+ def tags = parseTags(fetch(buildUrl))
def buildUrl = {
var urlBuilder = new StringBuilder
@@ -245,12 +238,7 @@ object Api {
with RefineableQuery[SearchQuery] with SearchTermQuery[SearchQuery]
with FilterableResultsQuery[SearchQuery] {
- def search: SearchResponse = parseSearchResponse(Http GET buildUrl)
-
- private def parseSearchResponse(httpResponse: HttpResponse): SearchResponse = {
- val response = getResponse("search", httpResponse.body)
- response.asInstanceOf[SearchResponse]
- }
+ def search = parseSearch(fetch(buildUrl))
def buildUrl = {
var urlBuilder = new StringBuilder
@@ -274,19 +262,14 @@ object Api {
with FilterableResultsQuery[ItemQuery] with PaginatedQuery[ItemQuery]
with SearchTermQuery[ItemQuery]{
- var apiUrl: Option[URL] = None
+ var apiUrl: Option[String] = None
- def withApiUrl(newContentPath: URL) = {
+ def withApiUrl(newContentPath: String) = {
apiUrl = Some(newContentPath)
this
}
- def query: ItemResponse = parseItemResponse(Http GET buildUrl)
-
- private def parseItemResponse(httpResponse: HttpResponse): ItemResponse = {
- val response = getResponse("id", httpResponse.body)
- response.asInstanceOf[ItemResponse]
- }
+ def query = parseItem(fetch(buildUrl))
def buildUrl = {
var urlBuilder = new StringBuilder
@@ -303,4 +286,21 @@ object Api {
urlBuilder.toString
}
}
+
+
+ def fetch(url: String, parameters: Map[String, String] = Map.empty): String = {
+ require(parameters.isEmpty || !url.contains('?'), "must either specifiy paramters or append to query string, not both")
+
+
+ val response = GET(url, List("User-Agent" -> "scala-api-client", "Accept" -> "application/json"))
+
+ if (List(200, 302) contains response.statusCode) {
+ response.body
+ } else {
+ throw new ApiError(response.statusCode, response.statusMessage)
+ }
+
+ }
}
+
+object Api extends Api with ApacheHttpClient
View
28 src/main/scala/com/gu/openplatform/contentapi/connection/Http.scala
@@ -5,29 +5,35 @@ import java.lang.String
import org.apache.commons.httpclient.methods.GetMethod
import org.apache.commons.httpclient.HttpClient
+import com.gu.openplatform.contentapi.ApiError
-object Http {
+case class HttpResponse(body: String, statusCode: Int, statusMessage: String)
+
+trait Http {
+ // this is what the Api client requires of an http connection
+ def GET(url: String, headers: Iterable[ (String, String) ] = Nil): HttpResponse
+}
+
+
+trait ApacheHttpClient extends Http {
var httpClient = new HttpClient
- def GET(url: String): HttpResponse = {
+ def GET(url: String, headers: Iterable[ (String, String) ] = Nil): HttpResponse = {
val method = new GetMethod(url)
- method.setFollowRedirects(false)
+
+ headers.foreach { case (k, v) => method.addRequestHeader(k, v) }
httpClient.executeMethod(method)
val statusLine = method getStatusLine
- val responseBody = Source.fromInputStream(method.getResponseBodyAsStream).mkString
+ val responseBody = Option(method.getResponseBodyAsStream)
+ .map(Source.fromInputStream(_).mkString)
+ .getOrElse("")
method.releaseConnection
- if (List(200, 302) contains statusLine.getStatusCode) {
- new HttpResponse(responseBody, statusLine.getStatusCode)
- } else {
- Console.err.println(" => response '%s'" format statusLine)
- throw new ApiError(statusLine.getStatusCode, statusLine.getReasonPhrase)
- }
+ new HttpResponse(responseBody, statusLine.getStatusCode, statusLine.getReasonPhrase)
}
}
-class HttpResponse(val body: String, val status: Int)
View
173 src/main/scala/com/gu/openplatform/contentapi/model/Model.scala
@@ -1,134 +1,79 @@
package com.gu.openplatform.contentapi.model
import org.joda.time.DateTime
-import java.net.URL
-class Response(
- val format: String,
- val status: String,
- val userTier: String
- )
-
-class PagedResponse(
- format: String,
- status: String,
- userTier: String,
- val startIndex: Int,
- val currentPage: Int,
- val pages: Int,
- val pageSize: Int,
- val total: Int
- ) extends Response (format, status, userTier)
-
-// "/search" endpoint
-case class SearchResponse(
- override val format: String,
- override val status: String,
- override val userTier: String,
- override val startIndex: Int,
- override val currentPage: Int,
- override val pages: Int,
- override val pageSize: Int,
- override val total: Int,
- val orderBy: String,
- val results: List[Content],
- val refinementGroups: List[RefinementGroup]
- ) extends PagedResponse(format, status, userTier, startIndex, currentPage, pages, pageSize, total)
-
-// "/tags"
-case class TagsResponse(
- override val format: String,
- override val status: String,
- override val userTier: String,
- override val startIndex: Int,
- override val currentPage: Int,
- override val pages: Int,
- override val pageSize: Int,
- override val total: Int,
- val results: List[Tag]
- ) extends PagedResponse(format, status, userTier, startIndex, currentPage, pages, pageSize, total)
-
-// "/sections"
-case class SectionsResponse(
- override val format: String,
- override val status: String,
- override val userTier: String,
- val total: Int,
- val results: List[Section]
- ) extends Response(format, status, userTier)
-
-// "/<id>" look up a single item by id endpoint
-case class ItemResponse(
- override val format: String,
- override val status: String,
- override val userTier: String,
- val startIndex: Option[Int],
- val currentPage: Option[Int],
- val pages: Option[Int],
- val pageSize: Option[Int],
- val total: Option[Int],
- val content: Option[Content],
- val tag: Option[Tag],
- val section: Option[Section],
- val results: List[Content]
- ) extends Response (format, status, userTier)
-
case class Content(
- val id: String,
- val sectionId: Option[String],
- val sectionName: Option[String],
- val webPublicationDate: DateTime,
- val webTitle: String,
- val webUrl: URL,
- val apiUrl: URL,
- val fields: Map[String, String],
- val tags : List[Tag],
- val factboxes : List[Factbox],
- val mediaAssets : List[MediaAsset]
- )
+ // the id of this item of content: this should always be the path
+ // to the item on www.guardian.co.uk
+ id: String,
+ // section is usually provided: some content (such as user help information)
+ // does not belong to any section so this will be None
+ sectionId: Option[String],
+ sectionName: Option[String],
+ webPublicationDate: DateTime,
+ webTitle: String,
+ webUrl: String,
+ apiUrl: String,
+ fields: Option[Map[String, String]],
+ tags: List[Tag],
+ factboxes: List[Factbox],
+ mediaAssets: List[MediaAsset]
+)
+
case class Tag(
- val id: String,
- val tagType: String, // "type" in xml and json
- val sectionId: Option[String],
- val sectionName: Option[String],
- val webTitle: String, // external name
- val webUrl: URL,
- val apiUrl: URL
- )
+ id: String,
+ `type` : String,
+ sectionId: Option[String],
+ sectionName: Option[String],
+ webTitle: String,
+ webUrl: String,
+ apiUrl: String
+ ) {
+ // for those that don't like backticks
+ def tagType = `type`
+}
+
case class Section(
- val id: String,
- val webTitle: String, // external name
- val webUrl: URL,
- val apiUrl: URL
+ id: String,
+ webTitle: String,
+ webUrl: String,
+ apiUrl: String
)
+
case class Factbox(
- val heading : Option[String],
- val factboxType : String,
- val picture : Option[String],
- val fields: Map[String, String]
- )
+ `type`: String,
+ heading: Option[String],
+ picture: Option[String],
+ fields: Option[Map[String, String]]
+ ) {
+ def factboxType = `type`
+}
case class MediaAsset(
- val mediaType : String,
- val relationship : String,
- val index : Int,
- val file : String,
- val fields: Map[String, String]
- )
+ `type`: String,
+ rel: String,
+ index: Int,
+ file: String,
+ fields: Option[Map[String, String]]
+ ) {
+ def mediaAssetType = `type`
+}
case class RefinementGroup(
- val refinementType: String, // "type" in xml and json
- val refinements: List[Refinement]
- )
+ `type`: String,
+ refinements: List[Refinement]
+ ) {
+ def refinementType = `type`
+}
case class Refinement(
- val count: Int,
- val refinedUrl: URL,
- val displayName: String,
- val id: String,
- val apiUrl: URL
+ count: Int,
+ refinedUrl: String,
+ displayName: String,
+ id: String,
+ apiUrl: String
)
View
147 src/main/scala/com/gu/openplatform/contentapi/model/NewModel.scala
@@ -1,147 +0,0 @@
-package com.gu.openplatform.contentapi.model.json
-
-import org.joda.time.DateTime
-
-// NB:
-// Due to the way lift-json parses lists and collections
-// we have to be inconsitant with optional lists.
-// Everywhere else where things are optional, we make then Options.
-// But for lists, this doesn't work (you get back Some(Nil) even if
-// the element is not present.
-// So Lists are never Option[List], you will just get back Nil if the
-// the element was not present in the response
-
-
-// Responses
-
-// /search
-case class SearchResponse(
- status: String,
- userTier: String,
- total: Long,
- startIndex: Long,
- pageSize: Long,
- currentPage: Long,
- pages: Long,
- orderBy: String,
- results: List[Content],
- refinementGroups: List[RefinementGroup]
-)
-
-// /tags
-case class TagsResponse(
- status: String,
- userTier: String,
- total: Long,
- startIndex: Long,
- pageSize: Long,
- currentPage: Long,
- pages: Long,
- results: List[Tag]
-)
-
-
-// /sections
-case class SectionsResponse(
- status: String,
- userTier: String,
- total: Long,
- results: List[Section]
-)
-
-
-// /anythingelse
-case class ItemResponse(
- status: String,
- userTier: String,
- total: Option[Long],
- startIndex: Option[Long],
- pageSize: Option[Long],
- currentPage: Option[Long],
- pages: Option[Long],
- orderBy: Option[String],
- tag: Option[Tag],
- section: Option[Section],
- content: Option[Content],
- results: List[Content],
- relatedContent: List[Content]
- )
-
-
-
-
-// Model classes
-case class Content(
- // the id of this item of content: this should always be the path
- // to the item on www.guardian.co.uk
- id: String,
- // section is usually provided: some content (such as user help information)
- // does not belong to any section
- sectionId: Option[String],
- sectionName: Option[String],
- webPublicationDate: DateTime,
- webTitle: String,
- webUrl: String,
- apiUrl: String,
- fields: Option[Map[String, String]],
- tags: List[Tag],
- factboxes: List[Factbox],
- mediaAssets: List[MediaAsset]
-)
-
-
-case class Tag(
- id: String,
- `type` : String,
- sectionId: Option[String],
- sectionName: Option[String],
- webTitle: String,
- webUrl: String,
- apiUrl: String
- ) {
- // for those that don't like backticks
- def tagType = `type`
-}
-
-
-case class Section(
- id: String,
- webTitle: String,
- webUrl: String,
- apiUrl: String
- )
-
-
-case class Factbox(
- `type`: String,
- heading: Option[String],
- picture: Option[String],
- fields: Option[Map[String, String]]
- ) {
- def factboxType = `type`
-}
-
-case class MediaAsset(
- `type`: String,
- rel: String,
- index: Int,
- file: String,
- fields: Option[Map[String, String]]
- ) {
- def mediaAssetType = `type`
-}
-
-case class RefinementGroup(
- `type`: String,
- refinements: List[Refinement]
- ) {
- def refinementType = `type`
-}
-
-case class Refinement(
- count: Long,
- refinedUrl: String,
- displayName: String,
- id: String,
- apiUrl: String
- )
View
69 src/main/scala/com/gu/openplatform/contentapi/model/Responses.scala
@@ -0,0 +1,69 @@
+package com.gu.openplatform.contentapi.model
+
+// NB:
+// Due to the way lift-json parses lists and collections
+// we have to be inconsitant with optional lists.
+// Everywhere else where things are optional, we make then Options.
+// But for lists, this doesn't work (you get back Some(Nil) even if
+// the element is not present.
+// So Lists are never Option[List], you will just get back Nil if the
+// the element was not present in the response
+
+
+// Responses
+
+// /search
+case class SearchResponse(
+ status: String,
+ userTier: String,
+ total: Int,
+ startIndex: Int,
+ pageSize: Int,
+ currentPage: Int,
+ pages: Int,
+ orderBy: String,
+ results: List[Content],
+ refinementGroups: List[RefinementGroup]
+)
+
+// /tags
+case class TagsResponse(
+ status: String,
+ userTier: String,
+ total: Int,
+ startIndex: Int,
+ pageSize: Int,
+ currentPage: Int,
+ pages: Int,
+ results: List[Tag]
+)
+
+
+// /sections
+case class SectionsResponse(
+ status: String,
+ userTier: String,
+ total: Int,
+ results: List[Section]
+)
+
+
+// /anythingelse
+case class ItemResponse(
+ status: String,
+ userTier: String,
+ total: Option[Int],
+ startIndex: Option[Int],
+ pageSize: Option[Int],
+ currentPage: Option[Int],
+ pages: Option[Int],
+ orderBy: Option[String],
+ tag: Option[Tag],
+ section: Option[Section],
+ content: Option[Content],
+ results: List[Content],
+ relatedContent: List[Content]
+ )
+
+
+
View
1  src/main/scala/com/gu/openplatform/contentapi/parser/JodaJsonSerializer.scala
@@ -6,6 +6,7 @@ import net.liftweb.json.JsonAST._
import org.joda.time.format.ISODateTimeFormat
import net.liftweb.json.{MappingException, TypeInfo, Formats, Serializer}
+// Adds Joda DateTime support to the lift-json serializer
class JodaJsonSerializer extends Serializer[DateTime] {
private val DateTimeClass = classOf[DateTime]
val formatter = ISODateTimeFormat.dateTimeNoMillis
View
10 src/main/scala/com/gu/openplatform/contentapi/parser/JsonParser.scala
@@ -1,17 +1,17 @@
package com.gu.openplatform.contentapi.parser
-import com.gu.openplatform.contentapi.model.json._
+import com.gu.openplatform.contentapi.model._
import net.liftweb.json.JsonParser._
+import net.liftweb.json.DefaultFormats
-class LiftJsonParser {
- implicit val formats = net.liftweb.json.DefaultFormats + new JodaJsonSerializer
+class JsonParser {
+ implicit val formats = DefaultFormats + new JodaJsonSerializer
def parseSearch(json: String) = (parse(json) \ "response").extract[SearchResponse]
def parseTags(json: String) = (parse(json) \ "response").extract[TagsResponse]
def parseSections(json: String) = (parse(json) \ "response").extract[SectionsResponse]
def parseItem(json: String) = (parse(json) \ "response").extract[ItemResponse]
-
}
-object LiftJsonParser extends LiftJsonParser
+object JsonParser extends JsonParser
View
220 src/main/scala/com/gu/openplatform/contentapi/parser/XmlParser.scala
@@ -1,220 +0,0 @@
-package com.gu.openplatform.contentapi.parser
-
-import scala.xml._
-import org.joda.time.format.ISODateTimeFormat
-import com.gu.openplatform.contentapi.model._
-import java.net.URL
-
-object XmlParser {
-
- def parseEndpoint(endpointName: String, xml: String) : Response = {
- parseEndpoint(endpointName, XML.loadString(xml))
- }
-
- def parseEndpoint(endpointName: String, xml: Elem) : Response = {
- endpointName match {
- case "search" => parseSearchEndpoint(xml)
- case "tags" => parseTagsEndpoint(xml)
- case "sections" => parseSectionsEndpoint(xml)
- case "id" => parseIdEndpoint(xml)
- case _ => throw new UnsupportedOperationException("unsupported endpoint " + endpointName)
- }
- }
-
- def parseSearchEndpoint(xml: String) : SearchResponse = {
- parseSearchEndpoint(XML.loadString(xml))
- }
-
- def parseSearchEndpoint(xml: Elem) : SearchResponse = {
- SearchResponse(
- "xml",
- (xml \ "@status" text),
- (xml \ "@user-tier" text),
- (xml \ "@start-index" text).toInt,
- (xml \ "@current-page" text).toInt,
- (xml \ "@pages" text).toInt,
- (xml \ "@page-size" text).toInt,
- (xml \ "@total" text).toInt,
- (xml \ "@order-by" text),
- (xml \ "results" \ "content" map { contentNode => parseContentNode(contentNode)}).toList,
- (xml \ "refinement-groups" \ "refinement-group" map {refinementGroupNode => parseRefinementGroupNode(refinementGroupNode)}).toList
- )
- }
-
- def parseTagsEndpoint(xml: String) : TagsResponse = {
- parseTagsEndpoint(XML.loadString(xml))
- }
-
- def parseTagsEndpoint(xml: Elem) : TagsResponse = {
- TagsResponse(
- "xml",
- (xml \ "@status" text),
- (xml \ "@user-tier" text),
- (xml \ "@start-index" text).toInt,
- (xml \ "@current-page" text).toInt,
- (xml \ "@pages" text).toInt,
- (xml \ "@page-size" text).toInt,
- (xml \ "@total" text).toInt,
- (xml \ "results" \ "tag" map { tagNode => parseTagNode(tagNode)}).toList
- )
- }
-
- def parseSectionsEndpoint(xml: String) : SectionsResponse = {
- parseSectionsEndpoint(XML.loadString(xml))
- }
-
- def parseSectionsEndpoint(xml: Elem) : SectionsResponse = {
- SectionsResponse(
- "xml",
- (xml \ "@status" text),
- (xml \ "@user-tier" text),
- (xml \ "@total" text).toInt,
- (xml \ "results" \ "section" map { sectionNode => parseSectionNode(sectionNode)}).toList
- )
- }
-
- def parseIdEndpoint(xml: String) : ItemResponse = {
- parseIdEndpoint(XML.loadString(xml))
- }
-
- def parseIdEndpoint(xml: Elem) : ItemResponse = {
- ItemResponse(
- "xml",
- (xml \ "@status" text),
- (xml \ "@user-tier" text),
- (xml \ "@start-index" size match {
- case 0 => None
- case 1 => Some((xml \ "@start-index" text)toInt)
- }),
- (xml \ "@current-page" size match {
- case 0 => None
- case 1 => Some((xml \ "@current-page" text)toInt)
- }),
- (xml \ "@pages" size match {
- case 0 => None
- case 1 => Some((xml \ "@pages" text)toInt)
- }),
- (xml \ "@page-size" size match {
- case 0 => None
- case 1 => Some((xml \ "@page-size" text)toInt)
- }),
- (xml \ "@total" size match {
- case 0 => None
- case 1 => Some((xml \ "@total" text)toInt)
- }),
- (xml \ "content" size match {
- case 0 => None
- case 1 => Some(parseContentNode(xml \ "content" head))
- case _ => throw new RuntimeException("more than 1 content item returned from id endpoint")
- }),
- (xml \ "tag" size match {
- case 0 => None
- case 1 => Some(parseTagNode(xml \ "tag" head))
- case _ => throw new RuntimeException("more than 1 tag returned from id endpoint")
- }),
- (xml \ "section" size match {
- case 0 => None
- case 1 => Some(parseSectionNode(xml \ "section" head))
- case _ => throw new RuntimeException("more than 1 section returned from id endpoint")
- }),
- (xml \ "results" \ "content" map { contentNode => parseContentNode(contentNode)}).toList
- )
- }
-
- def parseContentNode(contentNode: Node) : Content = {
- Content(
- contentNode \ "@id" text,
- contentNode \ "@section-id" size match {
- case 0 => None
- case 1 => Some(contentNode \ "@section-id" text)
- },
- contentNode \ "@section-name" size match {
- case 0 => None
- case 1 => Some(contentNode \ "@section-name" text)
- },
- ISODateTimeFormat.dateTimeNoMillis.parseDateTime(contentNode \ "@web-publication-date" text),
- contentNode \ "@web-title" text,
- new URL(contentNode \ "@web-url" text),
- new URL(contentNode \ "@api-url" text),
- parseFields(contentNode),
- (contentNode \ "tags" \ "tag" map {tagNode => parseTagNode(tagNode)}).toList,
- (contentNode \ "factboxes" \ "factbox" map {factboxNode => parseFactboxNode(factboxNode)}).toList,
- (contentNode \ "media-assets" \ "asset" map {mediaAssetNode => parseMediaAssetNode(mediaAssetNode)}).toList
- )
- }
-
- def parseFields(xmlItem: Node) : Map[String,String] =
- Map.empty[String, String] ++
- ( xmlItem \ "fields" \ "field" map { xmlField => (camelCase(xmlField \ "@name" text) , xmlField text) } )
-
- def camelCase(text: String) = text split("-") reduceLeft(_ + _.capitalize)
-
- def parseTagNode(tagNode: Node) : Tag = {
- Tag(
- tagNode \ "@id" text,
- tagNode \ "@type" text,
- tagNode \ "@section-id" size match {
- case 0 => None
- case 1 => Some(tagNode \ "@section-id" text)
- },
- tagNode \ "@section-name" size match {
- case 0 => None
- case 1 => Some(tagNode \ "@section-name" text)
- },
- tagNode \ "@web-title" text,
- new URL(tagNode \ "@web-url" text),
- new URL(tagNode \ "@api-url" text)
- )
- }
-
- def parseSectionNode(sectionNode: Node) : Section = {
- Section(
- sectionNode \ "@id" text,
- sectionNode \ "@web-title" text,
- new URL(sectionNode \ "@web-url" text),
- new URL(sectionNode \ "@api-url" text)
- )
- }
-
- def parseFactboxNode(factboxNode: Node) : Factbox = {
- Factbox(
- factboxNode \ "@heading" size match {
- case 0 => None
- case 1 => Some(factboxNode \ "@heading" text)
- },
- factboxNode \ "@type" text,
- factboxNode \ "@picture" size match {
- case 0 => None
- case 1 => Some(factboxNode \ "@picture" text)
- },
- parseFields(factboxNode)
- )
- }
-
- def parseMediaAssetNode(mediaAssetNode: Node) : MediaAsset = {
- MediaAsset(
- mediaAssetNode \ "@type" text,
- mediaAssetNode \ "@rel" text,
- (mediaAssetNode \ "@index" text).toInt,
- mediaAssetNode \ "@file" text,
- parseFields(mediaAssetNode)
- )
- }
-
- def parseRefinementGroupNode(refinementGroupNode: Node) : RefinementGroup = {
- RefinementGroup(
- refinementGroupNode \ "@type" text,
- (refinementGroupNode \ "refinements" \ "refinement" map { refinementNode => parseRefinementNode(refinementNode)}).toList
- )
- }
-
- def parseRefinementNode(refinementNode: Node) : Refinement = {
- Refinement(
- (refinementNode \ "@count" text).toInt,
- new URL(refinementNode \ "@refined-url" text),
- refinementNode \ "@display-name" text,
- refinementNode \ "@id" text,
- new URL(refinementNode \ "@api-url" text)
- )
- }
-}
View
105 ...gu/openplatform/contentapi/ExampleUsageTest.scala → src/test/scala/ExampleUsageTest.scala
@@ -1,12 +1,15 @@
-package com.gu.openplatform.contentapi
-
-import connection.Api
-import model.{Refinement, RefinementGroup, ItemResponse}
+import com.gu.openplatform.contentapi.Api
import org.scalatest.matchers.ShouldMatchers
-import org.scalatest.FeatureSpec
+import org.scalatest.{BeforeAndAfterEach, FeatureSpec}
+
+class ExampleUsageTest extends FeatureSpec with ShouldMatchers with BeforeAndAfterEach {
+ override protected def beforeEach() {
+ // if you run all these tests they will exceed the rate limit in the free tier,
+ // so putting in a cheeky sleep
+ Thread.sleep(500)
+ }
-class ExampleUsageTest extends FeatureSpec with ShouldMatchers {
feature("Pagination:") {
@@ -16,10 +19,6 @@ class ExampleUsageTest extends FeatureSpec with ShouldMatchers {
latest10Items.pageSize should be (10)
latest10Items.results.foreach ( item => println(item.webTitle))
-
- // if you run all these tests they will exceed the rate limit in the basic tier,
- // so putting in a cheeky sleep
- Thread.sleep(500)
}
scenario("get the second page of 10 recent items") {
@@ -29,10 +28,6 @@ class ExampleUsageTest extends FeatureSpec with ShouldMatchers {
items11to20.pageSize should be (10)
items11to20.currentPage should be (2)
items11to20.results.foreach ( item => println(item.webTitle))
-
- // if you run all these tests they will exceed the rate limit in the basic tier,
- // so putting in a cheeky sleep
- Thread.sleep(500)
}
scenario("get the most recent 25 items") {
@@ -41,10 +36,6 @@ class ExampleUsageTest extends FeatureSpec with ShouldMatchers {
items11to20.pageSize should be (25)
items11to20.results.foreach ( item => println(item.webTitle))
-
- // if you run all these tests they will exceed the rate limit in the basic tier,
- // so putting in a cheeky sleep
- Thread.sleep(500)
}
}
@@ -56,10 +47,6 @@ class ExampleUsageTest extends FeatureSpec with ShouldMatchers {
search.total should be > (0)
search.results.foreach (item => println(item.webTitle))
-
- // if you run all these tests they will exceed the rate limit in the basic tier,
- // so putting in a cheeky sleep
- Thread.sleep(500)
}
scenario("ordering free text query results by relevance") {
@@ -70,10 +57,6 @@ class ExampleUsageTest extends FeatureSpec with ShouldMatchers {
search.total should be > (0)
search.results.foreach (item => println(item.webTitle))
-
- // if you run all these tests they will exceed the rate limit in the basic tier,
- // so putting in a cheeky sleep
- Thread.sleep(500)
}
scenario("find content by tag") {
@@ -81,10 +64,6 @@ class ExampleUsageTest extends FeatureSpec with ShouldMatchers {
search.total should be > (0)
search.results.foreach (item => println(item.webTitle))
-
- // if you run all these tests they will exceed the rate limit in the basic tier,
- // so putting in a cheeky sleep
- Thread.sleep(500)
}
scenario("find content by multiple tags") {
@@ -92,10 +71,6 @@ class ExampleUsageTest extends FeatureSpec with ShouldMatchers {
search.total should be > (0)
search.results.foreach (item => println(item.webTitle))
-
- // if you run all these tests they will exceed the rate limit in the basic tier,
- // so putting in a cheeky sleep
- Thread.sleep(500)
}
scenario("find content in a section") {
@@ -103,10 +78,6 @@ class ExampleUsageTest extends FeatureSpec with ShouldMatchers {
search.total should be > (0)
search.results.foreach (item => println(item.webTitle))
-
- // if you run all these tests they will exceed the rate limit in the basic tier,
- // so putting in a cheeky sleep
- Thread.sleep(500)
}
scenario("find content between 2 dates") {
@@ -116,10 +87,6 @@ class ExampleUsageTest extends FeatureSpec with ShouldMatchers {
search.total should be > (0)
search.results.foreach (item => println(item.webTitle))
-
- // if you run all these tests they will exceed the rate limit in the basic tier,
- // so putting in a cheeky sleep
- Thread.sleep(500)
}
}
@@ -131,10 +98,6 @@ class ExampleUsageTest extends FeatureSpec with ShouldMatchers {
val tags = search.results.head.tags
tags.length should be > (0)
tags.foreach (tag => println(tag.tagType + ":" +tag.webTitle))
-
- // if you run all these tests they will exceed the rate limit in the basic tier,
- // so putting in a cheeky sleep
- Thread.sleep(500)
}
scenario("retrieving just the content's keywords") {
@@ -142,34 +105,22 @@ class ExampleUsageTest extends FeatureSpec with ShouldMatchers {
val tags = search.results.head.tags
tags.foreach (tag => println(tag.tagType + ":" +tag.webTitle))
-
- // if you run all these tests they will exceed the rate limit in the basic tier,
- // so putting in a cheeky sleep
- Thread.sleep(500)
}
scenario("retrieving an article's headline and trail") {
val search = Api.searchQuery.withPageSize(1).withTagTerm("type/article")
.withFields("headline,trail-text").search
- val fields: Map[String, String] = search.results.head.fields
+ val fields = search.results.head.fields.getOrElse(Map())
fields.keys.foreach (fieldKey => println(fieldKey + "->" +fields(fieldKey)))
-
- // if you run all these tests they will exceed the rate limit in the basic tier,
- // so putting in a cheeky sleep
- Thread.sleep(500)
}
scenario("retrieving all article's fields") {
val search = Api.searchQuery.withPageSize(1).withTagTerm("type/article")
.withFields("all").search
- val fields: Map[String, String] = search.results.head.fields
+ val fields = search.results.head.fields.getOrElse(Map())
fields.keys.foreach (fieldKey => println(fieldKey + "->" +fields(fieldKey)))
-
- // if you run all these tests they will exceed the rate limit in the basic tier,
- // so putting in a cheeky sleep
- Thread.sleep(500)
}
}
@@ -180,28 +131,16 @@ class ExampleUsageTest extends FeatureSpec with ShouldMatchers {
scenario("find some tags") {
val search = Api.tagsQuery.tags
search.results.foreach(tag => println(tag.webTitle))
-
- // if you run all these tests they will exceed the rate limit in the basic tier,
- // so putting in a cheeky sleep
- Thread.sleep(500)
}
scenario("find tags representing series") {
val search = Api.tagsQuery.withTypeTerm("series").tags
search.results.foreach(tag => println(tag.tagType + ":" + tag.webTitle))
-
- // if you run all these tests they will exceed the rate limit in the basic tier,
- // so putting in a cheeky sleep
- Thread.sleep(500)
}
scenario("find tags in the technology section") {
val search = Api.tagsQuery.withSectionTerm("technology").tags
search.results.foreach(tag => println(tag.webTitle + " (" + tag.sectionName.get + ")"))
-
- // if you run all these tests they will exceed the rate limit in the basic tier,
- // so putting in a cheeky sleep
- Thread.sleep(500)
}
}
@@ -214,10 +153,6 @@ class ExampleUsageTest extends FeatureSpec with ShouldMatchers {
scenario("listing the sections") {
val search = Api.sectionsQuery.sections
search.results.foreach(section => println(section.id))
-
- // if you run all these tests they will exceed the rate limit in the basic tier,
- // so putting in a cheeky sleep
- Thread.sleep(500)
}
}
@@ -235,31 +170,23 @@ class ExampleUsageTest extends FeatureSpec with ShouldMatchers {
val contentApiUrl = search.results.head.apiUrl
println("following api url: " + contentApiUrl)
- val item: ItemResponse = Api.itemQuery.withApiUrl(contentApiUrl).query
+ val item = Api.itemQuery.withApiUrl(contentApiUrl).query
println("loaded " + item.content.get.webTitle)
-
- // if you run all these tests they will exceed the rate limit in the basic tier,
- // so putting in a cheeky sleep
- Thread.sleep(500)
}
}
- feature("refineing search results") {
+ feature("refining search results") {
scenario("finding the most popular keywords for a seach") {
val search = Api.searchQuery.withPageSize(1).withSectionTerm("music")
.withShowRefinements("keyword").withRefinementSize(20).search
- search.refinementGroups foreach { group: RefinementGroup =>
+ search.refinementGroups foreach { group =>
println(group.refinementType)
- group.refinements.foreach { refinement: Refinement =>
+ group.refinements.foreach { refinement =>
println("\t" + refinement.displayName + " (" + refinement.count + ")")
}
}
-
- // if you run all these tests they will exceed the rate limit in the basic tier,
- // so putting in a cheeky sleep
- Thread.sleep(500)
}
}
-}
+}
View
1  src/test/scala/com/gu/openplatform/contentapi/ApiTest.scala
@@ -1,6 +1,5 @@
package com.gu.openplatform.contentapi
-import connection.Api
import org.scalatest.matchers.ShouldMatchers
import org.scalatest.FunSuite
View
32 src/test/scala/com/gu/openplatform/contentapi/NewTest.scala
@@ -1,32 +0,0 @@
-
-package com.gu.openplatform.contentapi
-
-import connection.Http
-import model.json.{SearchResponse}
-import org.scalatest.matchers.ShouldMatchers
-import org.scalatest.FeatureSpec
-
-import net.liftweb.json._
-
-class NewTest extends FeatureSpec with ShouldMatchers {
-
- implicit val formats = net.liftweb.json.DefaultFormats
-
- feature("new json stuff") {
-
- scenario("get stuff") {
-
- val result = Http GET "http://content.guardianapis.com/search.json?show-tags=all"
-
- //println(result.body)
-
- val json = JsonParser.parse(result.body)
-
- val parsed = (json \ "response").extract[SearchResponse]
-
- //println(parsed)
-
- }
- }
-}
-
View
50 src/test/scala/com/gu/openplatform/contentapi/parser/SearchJsonParserTest.scala
@@ -11,12 +11,12 @@ class SearchJsonParserTest extends FlatSpec with ShouldMatchers {
// generated by:
// http://content.guardianapis.com/search.json
// ?page-size=2&show-fields=all&show-tags=all&show-refinements=all&refinement-size=2
- lazy val searchResponse = LiftJsonParser.parseSearch(loadFile("search.json"))
+ lazy val searchResponse = JsonParser.parseSearch(loadFile("search.json"))
// generated by:
// http://content.guardianapis.com/search.json
// ?api-key=<PARTNER KEY>&ids=music/picture/2010/aug/25/georgemichael-ukcrime&show-media=all&show-factboxes=all
- lazy val partnerSearchResponse = LiftJsonParser.parseSearch(loadFile("search-partner.json"))
+ lazy val partnerSearchResponse = JsonParser.parseSearch(loadFile("search-partner.json"))
"search endpoint parser" should "parse basic reponse header" in {
searchResponse.status should be ("ok")
@@ -119,50 +119,4 @@ class SearchJsonParserTest extends FlatSpec with ShouldMatchers {
}
-
-
-
- /*
- scenariosFor(
- testBasicResponseHeaderCanBeParsed("tag endpoint", "tags", tagEndpointXml),
- testPagedResponseHeaderCanBeParsed("tag endpoint", "tags", tagEndpointXml),
- testThereAre2ItemsInTheResults("tag endpoint response -> results", "tags", tagEndpointXml, _.asInstanceOf[TagsResponse].results),
- testTheStandardTagIsParsedCorrectly("tag endpoint response -> results -> tag", "tags", tagEndpointXml, _.asInstanceOf[TagsResponse].results.head)
- )
-
- scenariosFor(
- testBasicResponseHeaderCanBeParsed("section endpoint", "sections", sectionEndpointXml),
- testThereAre2ItemsInTheResults("section endpoint response -> results", "sections", sectionEndpointXml, _.asInstanceOf[SectionsResponse].results),
- testTheStandardSectionIsParsedCorrectly("section endpoint response -> results -> section", "sections", sectionEndpointXml, _.asInstanceOf[SectionsResponse].results.head)
- )
-
- scenariosFor(
- testBasicResponseHeaderCanBeParsed("id endpoint returning content", "id", idEndpointContentXml),
- testTheStandardArticleIsParsedCorrectly("id endpoint", "id", idEndpointContentXml, _.asInstanceOf[ItemResponse].content.get),
- testThereAre2ItemsInTheResults("id endpoint response -> content -> tags", "id", idEndpointContentXml, _.asInstanceOf[ItemResponse].content.get.tags),
- testTheStandardTagIsParsedCorrectly("id endpoint response -> content -> tag", "id", idEndpointContentXml, _.asInstanceOf[ItemResponse].content.get.tags.head),
- testTheStandardFieldsAreParsedCorrectly("id endpoint", "id", idEndpointContentXml, _.asInstanceOf[ItemResponse].content.get),
- testTheStandardFactboxIsParsedCorrectly("id endpoint", "id", idEndpointContentXml, _.asInstanceOf[ItemResponse].content.get.factboxes.head),
- testTheStandardMediaAssetIsParsedCorrectly("id endpoint -> content -> media-assets", "id", idEndpointContentXml, _.asInstanceOf[ItemResponse].content.get.mediaAssets.head)
- )
-
- scenariosFor(
- testBasicResponseHeaderCanBeParsed("id endpoint returning tag", "id", idEndpointTagXml),
- testTheStandardTagIsParsedCorrectly("id endpoint response -> tag", "id", idEndpointTagXml, _.asInstanceOf[ItemResponse].tag.get),
- testPagedItemResponseHeaderCanBeParsed("id endpoint returning tag", "id", idEndpointTagXml),
- testThereAre2ItemsInTheResults("id endpoint returning tag -> results ", "id", idEndpointTagXml, _.asInstanceOf[ItemResponse].results),
- testTheStandardArticleIsParsedCorrectly("id endpoint returning tag -> results", "id", idEndpointTagXml, _.asInstanceOf[ItemResponse].results.head),
- testThereAre2ItemsInTheResults("id endpoint returning tag -> results -> tags", "id", idEndpointTagXml, _.asInstanceOf[ItemResponse].results.head.tags),
- testTheStandardTagIsParsedCorrectly("id endpoint returning tag -> results -> content -> tag", "id", idEndpointTagXml, _.asInstanceOf[ItemResponse].results.head.tags.head),
- testTheStandardFieldsAreParsedCorrectly("id endpoint returning tag -> results", "id", idEndpointTagXml, _.asInstanceOf[ItemResponse].results.head)
- )
-
- scenariosFor(
- testBasicResponseHeaderCanBeParsed("id endpoint returning section", "id", idEndpointSectionXml),
- testPagedItemResponseHeaderCanBeParsed("id endpoint returning section", "id", idEndpointSectionXml),
- testTheStandardSectionIsParsedCorrectly("id endpoint response -> section", "id", idEndpointSectionXml, _.asInstanceOf[ItemResponse].section.get)
- )
- */
-
-
}
View
2  src/test/scala/com/gu/openplatform/contentapi/parser/SectionsJsonParserTest.scala
@@ -8,7 +8,7 @@ class SectionsJsonParserTest extends FlatSpec with ShouldMatchers {
// generated by:
// http://content.guardianapis.com/sections.json
- lazy val sectionsResponse = LiftJsonParser.parseSections(loadFile("sections.json"))
+ lazy val sectionsResponse = JsonParser.parseSections(loadFile("sections.json"))
"sections endpoint parser" should "parse basic reponse header" in {
sectionsResponse.status should be ("ok")
View
6 src/test/scala/com/gu/openplatform/contentapi/parser/TagItemJsonParserTest.scala
@@ -8,17 +8,17 @@ class TagItemJsonParserTest extends FlatSpec with ShouldMatchers {
// generated by:
// http://content.guardianapis.com/travel/france.json
- lazy val tagItemResponse = LiftJsonParser.parseItem(loadFile("item-tag.json"))
+ lazy val tagItemResponse = JsonParser.parseItem(loadFile("item-tag.json"))
// generated by:
// http://content.guardianapis.com/travel.json
- lazy val sectionItemResponse = LiftJsonParser.parseItem(loadFile("item-section.json"))
+ lazy val sectionItemResponse = JsonParser.parseItem(loadFile("item-section.json"))
// generated by:
// http://content.guardianapis.com/travel/2010/aug/27/britains-best-budget-eats-oxford.json
// ?show-related=true
- lazy val contentItemResponse = LiftJsonParser.parseItem(loadFile("item-content.json"))
+ lazy val contentItemResponse = JsonParser.parseItem(loadFile("item-content.json"))
"item endpoint parser" should "parse basic reponse header" in {
View
2  src/test/scala/com/gu/openplatform/contentapi/parser/TagsJsonParserTest.scala
@@ -9,7 +9,7 @@ class TagsJsonParserTest extends FlatSpec with ShouldMatchers {
// generated by:
// http://content.guardianapis.com/tags.json
// ?page-size=2&format=json
- lazy val tagsResponse = LiftJsonParser.parseTags(loadFile("tags.json"))
+ lazy val tagsResponse = JsonParser.parseTags(loadFile("tags.json"))
"tags endpoint parser" should "parse basic reponse header" in {
tagsResponse.status should be ("ok")
View
419 src/test/scala/com/gu/openplatform/contentapi/parser/XMLParserTest.scala
@@ -1,419 +0,0 @@
-package com.gu.openplatform.contentapi.parser
-
-import org.scalatest.matchers.ShouldMatchers
-import org.scalatest.FeatureSpec
-import java.net.URL
-import org.joda.time.DateTime
-import xml.Elem
-import com.gu.openplatform.contentapi.model._
-
-class XmlParserTest extends FeatureSpec with ShouldMatchers {
-
- protected def scenariosFor(unit: Unit*) {}
-
- private val searchEndpointXml =
- <response status="ok" total="13976" start-index="0" user-tier="free" page-size="2" current-page="1" pages="6988">
- <results>
- <content id="politics/2008/sep/25/ruthkelly.transport" type="article" section-id="/politics" section-name="Politics" web-publication-date="2008-09-25T00:00:00+01:00" web-title="Accusations fly as Kelly's farewell leaves bad taste" web-url="http://www.guardian.co.uk/politics/2008/sep/25/ruthkelly.transport" api-url="http://api.guardianapis.com/politics/2008/sep/25/ruthkelly.transport">
- <fields>
- <field name="headline">Accusations fly as Kelly's farewell leaves bad taste</field>
- <field name="standfirst">Blairite wing claims dirty tricks by No 10 forced minister to confirm departure</field>
- </fields>
- <tags>
- <tag type="keyword" web-title="Photography" id="artanddesign/photography" api-url="http://api.guardianapis.com/artanddesign/photography" web-url="http://www.guardian.co.uk/artanddesign/photography" section-id="artanddesign" section-name="Art and design"/>
- <tag type="tone" web-title="Reviews" id="tone/reviews" api-url="http://api.guardianapis.com/tone/reviews" web-url="http://www.guardian.co.uk/tone/reviews"/>
- </tags>
- <factboxes>
- <factbox type="book" heading="the bible" picture="http://static.guim.co.uk/thebible.jpg">
- <fields>
- <field name="rating">5 stars</field>
- <field name="author">God</field>
- </fields>
- </factbox>
- </factboxes>
- <media-assets>
- <asset type="picture" rel="body" index="1" file="http://static.guim.co.uk/thebible.jpg">
- <fields>
- <field name="alt-text">alt text</field>
- <field name="caption">caption</field>
- </fields>
- </asset>
- </media-assets>
- </content>
- <content id="politics/2008/sep/04/conservatives33" type="article" section-id="/politics" section-name="Politics" web-publication-date="2008-09-09T00:00:00+01:00" web-title="The new Tories: Simon Jones, Dagenham and Rainham" web-url="http://www.guardian.co.uk/politics/2008/sep/04/conservatives33" api-url="http://api.guardianapis.com/politics/2008/sep/04/conservatives33">
- <fields>
- <field name="headline">Simon Jones, Dagenham and Rainham</field>
- <field name="standfirst">Target seat no 164</field>
- </fields>
- <tags>
- <tag type="contributor" webTitle="Ewen MacAskill" id="profile/ewenmacaskill" api-url="http://api.guardianapis.com/profile/ewenmacaskill" web-url="http://www.guardian.co.uk/profile/ewenmacaskill"/>
- <tag type="keyword" webTitle="US elections 2008" id="world/us-elections-2008" api-url="http://api.guardianapis.com/world/us-elections-2008" web-url="http://www.guardian.co.uk/world/us-elections-2008" section-id="world" section-name="World news"/>
- </tags>
- </content>
- </results>
- <refinement-groups>
- <refinement-group type="keyword">
- <refinements>
- <refinement count="42" refined-url="http://api.guardianapis.com/search?format=xml" display-name="Photography" id="artanddesign/photography" api-url="http://api.guardianapis.com/artanddesign/photography" />
- <refinement count="12" refined-url="http://api.guardianapis.com/search?format=xml" display-name="US elections 2008" id="world/us-elections-2008" api-url="http://api.guardianapis.com/world/us-elections-2008" />
- </refinements>
- </refinement-group>
- <refinement-group type="section">
- <refinements>
- <refinement count="42" refined-url="http://api.guardianapis.com/search?format=xml" display-name="World news" id="world" api-url="http://api.guardianapis.com/world" />
- <refinement count="12" refined-url="http://api.guardianapis.com/search?format=xml" display-name="Art and design" id="artanddesign" api-url="http://api.guardianapis.com/artanddesign" />
- </refinements>
- </refinement-group>
- </refinement-groups>
- </response>
-
- private val tagEndpointXml =
- <response status="ok" total="13976" start-index="0" user-tier="free" page-size="2" current-page="1" pages="6988">
- <results>
- <tag type="keyword" web-title="Photography" id="artanddesign/photography" api-url="http://api.guardianapis.com/artanddesign/photography" web-url="http://www.guardian.co.uk/artanddesign/photography" section-id="artanddesign" section-name="Art and design"/>
- <tag type="tone" web-title="Reviews" id="tone/reviews" api-url="http://api.guardianapis.com/tone/reviews" web-url="http://www.guardian.co.uk/tone/reviews"/>
- </results>
- </response>
-
- private val sectionEndpointXml =
- <response status="ok" total="2" user-tier="free">
- <results>
- <section web-title="World news" id="world" api-url="http://api.guardianapis.com/world" web-url="http://www.guardian.co.uk/world" />
- <section web-title="Art and design" id="artanddesign" api-url="http://api.guardianapis.com/artanddesign" web-url="http://www.guardian.co.uk/artanddesign"/>
- </results>
- </response>
-
- private val idEndpointContentXml =
- <response status="ok" user-tier="free">
- <content id="politics/2008/sep/25/ruthkelly.transport" type="article" section-id="/politics" section-name="Politics" web-publication-date="2008-09-25T00:00:00+01:00" web-title="Accusations fly as Kelly's farewell leaves bad taste" web-url="http://www.guardian.co.uk/politics/2008/sep/25/ruthkelly.transport" api-url="http://api.guardianapis.com/politics/2008/sep/25/ruthkelly.transport">
- <fields>
- <field name="headline">Accusations fly as Kelly's farewell leaves bad taste</field>
- <field name="standfirst">Blairite wing claims dirty tricks by No 10 forced minister to confirm departure</field>
- </fields>
- <tags>
- <tag type="keyword" web-title="Photography" id="artanddesign/photography" api-url="http://api.guardianapis.com/artanddesign/photography" web-url="http://www.guardian.co.uk/artanddesign/photography" section-id="artanddesign" section-name="Art and design"/>
- <tag type="tone" web-title="Reviews" id="tone/reviews" api-url="http://api.guardianapis.com/tone/reviews" web-url="http://www.guardian.co.uk/tone/reviews"/>
- </tags>
- <factboxes>
- <factbox type="book" heading="the bible" picture="http://static.guim.co.uk/thebible.jpg">
- <fields>
- <field name="rating">5 stars</field>
- <field name="author">God</field>
- </fields>
- </factbox>
- </factboxes>
- <media-assets>
- <asset type="picture" rel="body" index="1" file="http://static.guim.co.uk/thebible.jpg">
- <fields>
- <field name="alt-text">alt text</field>
- <field name="caption">caption</field>
- </fields>
- </asset>
- </media-assets>
- </content>
- </response>
-
- private val idEndpointTagXml =
- <response status="ok" total="13976" start-index="0" user-tier="free" page-size="2" current-page="1" pages="6988">
- <tag type="keyword" web-title="Photography" id="artanddesign/photography" api-url="http://api.guardianapis.com/artanddesign/photography" web-url="http://www.guardian.co.uk/artanddesign/photography" section-id="artanddesign" section-name="Art and design"/>
- <results>
- <content id="politics/2008/sep/25/ruthkelly.transport" type="article" section-id="/politics" section-name="Politics" web-publication-date="2008-09-25T00:00:00+01:00" web-title="Accusations fly as Kelly's farewell leaves bad taste" web-url="http://www.guardian.co.uk/politics/2008/sep/25/ruthkelly.transport" api-url="http://api.guardianapis.com/politics/2008/sep/25/ruthkelly.transport">
- <fields>
- <field name="headline">Accusations fly as Kelly's farewell leaves bad taste</field>
- <field name="standfirst">Blairite wing claims dirty tricks by No 10 forced minister to confirm departure</field>
- </fields>
- <tags>
- <tag type="keyword" web-title="Photography" id="artanddesign/photography" api-url="http://api.guardianapis.com/artanddesign/photography" web-url="http://www.guardian.co.uk/artanddesign/photography" section-id="artanddesign" section-name="Art and design"/>
- <tag type="tone" web-title="Reviews" id="tone/reviews" api-url="http://api.guardianapis.com/tone/reviews" web-url="http://www.guardian.co.uk/tone/reviews"/>
- </tags>
- </content>
- <content id="politics/2008/sep/04/conservatives33" type="article" section-id="/politics" section-name="Politics" web-publication-date="2008-09-09T00:00:00+01:00" web-title="The new Tories: Simon Jones, Dagenham and Rainham" web-url="http://www.guardian.co.uk/politics/2008/sep/04/conservatives33" api-url="http://api.guardianapis.com/politics/2008/sep/04/conservatives33">
- <fields>
- <field name="headline">Simon Jones, Dagenham and Rainham</field>
- <field name="standfirst">Target seat no 164</field>
- </fields>
- <tags>
- <tag type="contributor" webTitle="Ewen MacAskill" id="profile/ewenmacaskill" api-url="http://api.guardianapis.com/profile/ewenmacaskill" web-url="http://www.guardian.co.uk/profile/ewenmacaskill"/>
- <tag type="keyword" webTitle="US elections 2008" id="world/us-elections-2008" api-url="http://api.guardianapis.com/world/us-elections-2008" web-url="http://www.guardian.co.uk/world/us-elections-2008" section-id="world" section-name="World news"/>
- </tags>
- </content>
- </results>
- </response>
-
- private val idEndpointSectionXml =
- <response status="ok" total="13976" start-index="0" user-tier="free" page-size="2" current-page="1" pages="6988">
- <section web-title="World news" id="world" api-url="http://api.guardianapis.com/world" web-url="http://www.guardian.co.uk/world" />
- </response>
-
- private val camelCaseTestEndpointXml =
- <response status="ok" total="13976" start-index="0" user-tier="free" page-size="2" current-page="1" pages="6988">
- <results>
- <content id="politics/2008/sep/25/ruthkelly.transport" type="article" section-id="/politics" section-name="Politics" web-publication-date="2008-09-25T00:00:00+01:00" web-title="Accusations fly as Kelly's farewell leaves bad taste" web-url="http://www.guardian.co.uk/politics/2008/sep/25/ruthkelly.transport" api-url="http://api.guardianapis.com/politics/2008/sep/25/ruthkelly.transport">
- <fields>
- <field name="duration-minutes">Parser wants to camel case this field's name</field>
- <field name="duration-seconds">Parser wants to camel case this field's name</field>
- <field name="picture-count">Parser wants to camel case this field's name</field>
- <field name="show-notes">Parser wants to camel case this field's name</field>
- <field name="alt-text">Parser wants to camel case this field's name</field>
- </fields>
- </content>
- </results>
- </response>
-
- def testBasicResponseHeaderCanBeParsed(xmlDescription: String, endpointName: String, endpointXml :Elem) {
- feature("xml parser for " + xmlDescription) {
- scenario("should parse basic response header") {
- val response: Response = XmlParser.parseEndpoint(endpointName, endpointXml)
- response.status should be ("ok")
- response.userTier should be ("free")
- }
- }
- }
-
- def testPagedResponseHeaderCanBeParsed(xmlDescription: String, endpointName: String, endpointXml :Elem) {
- feature("xml parser for " + xmlDescription) {
- scenario("should parse pagination information from response header") {
- val response: PagedResponse = (XmlParser.parseEndpoint(endpointName, endpointXml)).asInstanceOf[PagedResponse]
-
- response.startIndex should be (0)
- response.pageSize should be (2)
- response.currentPage should be (1)
- response.pages should be (6988)
- response.total should be (13976)
- }
- }
- }
-
- def testPagedItemResponseHeaderCanBeParsed(xmlDescription: String, endpointName: String, endpointXml :Elem) {
- feature("xml parser for " + xmlDescription) {
- scenario("should parse pagination information from response header") {
- val response: ItemResponse = (XmlParser.parseEndpoint(endpointName, endpointXml)).asInstanceOf[ItemResponse]
-
- response.startIndex.get should be (0)
- response.pageSize.get should be (2)
- response.currentPage.get should be (1)
- response.pages.get should be (6988)
- response.total.get should be (13976)
- }
- }
- }
-
- def testThereAre2ItemsInTheResults(xmlDescription: String, endpointName: String, endpointXml :Elem, resultsSelector :Response => List[Any]) {
- feature("xml parser for " + xmlDescription) {
- scenario("should find 2 entries") {
- val response: Response = XmlParser.parseEndpoint(endpointName, endpointXml)
-
- resultsSelector(response).length should be (2)
- }
- }
- }
-
- def testTheStandardArticleIsParsedCorrectly(xmlDescription: String, endpointName: String, endpointXml :Elem, contentSelector :Response => Content) {
- feature("xml parser for " + xmlDescription) {
- scenario("should parse the standard article ") {
- val response: Response = XmlParser.parseEndpoint(endpointName, endpointXml)
-
- val content :Content = contentSelector(response)
-
- content.sectionId should be (Some("/politics"))
- content.sectionName should be (Some("Politics"))
- content.webPublicationDate should be (new DateTime(2008, 9, 25, 0, 0, 0, 0))
- content.webTitle should be ("Accusations fly as Kelly's farewell leaves bad taste")
- content.webUrl should be (new URL("http://www.guardian.co.uk/politics/2008/sep/25/ruthkelly.transport"))
- content.apiUrl should be (new URL("http://api.guardianapis.com/politics/2008/sep/25/ruthkelly.transport"))
- }
- }
- }
-
- def testTheStandardTagIsParsedCorrectly(xmlDescription: String, endpointName: String, endpointXml :Elem, tagSelector :Response => Tag) {
- feature("xml parser for " + xmlDescription) {
- scenario("should parse the standard tag ") {
- val response: Response = XmlParser.parseEndpoint(endpointName, endpointXml)
-
- val tag :Tag = tagSelector(response)
-
- tag.id should be ("artanddesign/photography")
- tag.tagType should be ("keyword")
- tag.sectionId.get should be ("artanddesign")
- tag.sectionName.get should be ("Art and design")
- tag.webTitle should be ("Photography")
- tag.webUrl should be (new URL("http://www.guardian.co.uk/artanddesign/photography"))
- tag.apiUrl should be (new URL("http://api.guardianapis.com/artanddesign/photography"))
- }
- }
- }
-
- def testTheStandardSectionIsParsedCorrectly(xmlDescription: String, endpointName: String, endpointXml :Elem, sectionSelector :Response => Section) {
- feature("xml parser for " + xmlDescription) {
- scenario("should parse the standard section ") {
- val response: Response = XmlParser.parseEndpoint(endpointName, endpointXml)
-
- val section :Section = sectionSelector(response)
-
- section.webTitle should be ("World news")
- section.id should be ("world")
- section.webUrl should be (new URL("http://www.guardian.co.uk/world"))
- section.apiUrl should be (new URL("http://api.guardianapis.com/world"))
- }
- }
- }
-
- def testTheStandardFieldsAreParsedCorrectly(xmlDescription: String, endpointName: String, endpointXml :Elem, contentSelector :Response => Content) {
- feature("xml parser for " + xmlDescription) {
- scenario("should parse the standard fields ") {
- val response: Response = XmlParser.parseEndpoint(endpointName, endpointXml)
-
- val content :Content = contentSelector(response)
-
- content.fields should not be (None)
- val fields: Map[String, String] = content.fields
- fields should have size(2)
-
- fields("headline") should be ("Accusations fly as Kelly's farewell leaves bad taste")
- fields("standfirst") should be ("Blairite wing claims dirty tricks by No 10 forced minister to confirm departure")
- }
- }
- }
-
- def testTheStandardRefinementGroupIsParsedCorrectly(xmlDescription: String, endpointName: String, endpointXml :Elem, refinementGroupSelector :Response => RefinementGroup) {
- feature("xml parser for " + xmlDescription) {
- scenario("should parse the standard refinement group ") {
- val response: Response = XmlParser.parseEndpoint(endpointName, endpointXml)
-
- val refinementGroup :RefinementGroup = refinementGroupSelector(response)
-
- refinementGroup.refinementType should be ("keyword")
- }
- }
- }
-
- def testTheStandardRefinementIsParsedCorrectly(xmlDescription: String, endpointName: String, endpointXml :Elem, refinementSelector :Response => Refinement) {
- feature("xml parser for " + xmlDescription) {
- scenario("should parse the standard refinement ") {
- val response: Response = XmlParser.parseEndpoint(endpointName, endpointXml)
-
- val refinement :Refinement = refinementSelector(response)
-
- refinement.count should be (42)
- refinement.refinedUrl should be (new URL("http://api.guardianapis.com/search?format=xml"))
- }
- }
- }
-
- def testTheStandardFactboxIsParsedCorrectly(xmlDescription: String, endpointName: String, endpointXml :Elem, factboxSelector :Response => Factbox) {
-
- feature("xml parser for " + xmlDescription) {
- scenario("should parse the standard factbox ") {
- val response: Response = XmlParser.parseEndpoint(endpointName, endpointXml)
-
- val factbox :Factbox = factboxSelector(response)
-
- factbox.factboxType should be ("book")
- factbox.heading.get should be ("the bible")
- factbox.picture.get should be ("http://static.guim.co.uk/thebible.jpg")
-
- val fields: Map[String, String] = factbox.fields
- fields should have size(2)
- fields("rating") should be ("5 stars")
- fields("author") should be ("God")
- }
- }
- }
-
- def testTheStandardMediaAssetIsParsedCorrectly(xmlDescription: String, endpointName: String, endpointXml :Elem, mediaSelector :Response => MediaAsset) {
-
- feature("xml parser for " + xmlDescription) {
- scenario("should parse the standard media asset ") {
- val response: Response = XmlParser.parseEndpoint(endpointName, endpointXml)
-
- val mediaAsset: MediaAsset = mediaSelector(response)
-
- mediaAsset.mediaType should be ("picture")
- mediaAsset.relationship should be ("body")
- mediaAsset.index should be (1)
- mediaAsset.file should be ("http://static.guim.co.uk/thebible.jpg")
-
- val fields: Map[String, String] = mediaAsset.fields
- fields should have size(2)
- fields("altText") should be ("alt text")
- fields("caption") should be ("caption")
- }
- }
- }
-
- def testCamelCasingForHyphenatedFieldNames(xmlDescription: String, endpointName: String, endpointXml :Elem, contentSelector :Response => Content) {
- feature("xml parser for " + xmlDescription) {
- scenario("should camelcase field names ") {
- val response: Response = XmlParser.parseEndpoint(endpointName, endpointXml)
-
- val content :Content = contentSelector(response)
- val fields: Map[String, String] = contentSelector(response).fields
- fields should have size(5)
-
- fields("durationMinutes") should not be (None)
- fields("durationSeconds") should not be (None)
- fields("pictureCount") should not be (None)
- fields("showNotes") should not be (None)
- fields("altText") should not be (None)
- }
- }
- }
-
- scenariosFor(
- testBasicResponseHeaderCanBeParsed("search endpoint", "search", searchEndpointXml),
- testPagedResponseHeaderCanBeParsed("search endpoint", "search", searchEndpointXml),
- testThereAre2ItemsInTheResults("search endpoint response -> results ", "search", searchEndpointXml, _.asInstanceOf[SearchResponse].results),
- testTheStandardArticleIsParsedCorrectly("search endpoint", "search", searchEndpointXml, _.asInstanceOf[SearchResponse].results.head),
- testThereAre2ItemsInTheResults("search endpoint response -> results -> tags", "search", searchEndpointXml, _.asInstanceOf[SearchResponse].results.head.tags),
- testTheStandardTagIsParsedCorrectly("search endpoint response -> results -> content -> tag", "search", searchEndpointXml, _.asInstanceOf[SearchResponse].results.head.tags.head),
- testTheStandardFieldsAreParsedCorrectly("search endpoint", "search", searchEndpointXml, _.asInstanceOf[SearchResponse].results.head),
- testCamelCasingForHyphenatedFieldNames("search endpoint", "search", camelCaseTestEndpointXml, _.asInstanceOf[SearchResponse].results.head),
- testThereAre2ItemsInTheResults("search endpoint response -> refinementGroups", "search", searchEndpointXml, _.asInstanceOf[SearchResponse].refinementGroups),
- testTheStandardRefinementGroupIsParsedCorrectly("search endpoint response -> refinementGroups -> refinementGroup", "search", searchEndpointXml, _.asInstanceOf[SearchResponse].refinementGroups.head),
- testThereAre2ItemsInTheResults("search endpoint response -> refinementGroups -> refinementGroup -> refinements", "search", searchEndpointXml, _.asInstanceOf[SearchResponse].refinementGroups.head.refinements),
- testTheStandardRefinementIsParsedCorrectly("search endpoint response -> refinementGroups -> refinementGroup -> refinements -> refinement", "search", searchEndpointXml, _.asInstanceOf[SearchResponse].refinementGroups.head.refinements.head),
- testTheStandardFactboxIsParsedCorrectly("search endpoint response -> results -> content -> factboxes", "search", searchEndpointXml, _.asInstanceOf[SearchResponse].results.head.factboxes.head),
- testTheStandardMediaAssetIsParsedCorrectly("search endpoint response -> results -> content -> media-assets", "search", searchEndpointXml, _.asInstanceOf[SearchResponse].results.head.mediaAssets.head)
- )
-
- scenariosFor(
- testBasicResponseHeaderCanBeParsed("tag endpoint", "tags", tagEndpointXml),
- testPagedResponseHeaderCanBeParsed("tag endpoint", "tags", tagEndpointXml),
- testThereAre2ItemsInTheResults("tag endpoint response -> results", "tags", tagEndpointXml, _.asInstanceOf[TagsResponse].results),
- testTheStandardTagIsParsedCorrectly("tag endpoint response -> results -> tag", "tags", tagEndpointXml, _.asInstanceOf[TagsResponse].results.head)
- )
-
- scenariosFor(
- testBasicResponseHeaderCanBeParsed("section endpoint", "sections", sectionEndpointXml),
- testThereAre2ItemsInTheResults("section endpoint response -> results", "sections", sectionEndpointXml, _.asInstanceOf[SectionsResponse].results),
- testTheStandardSectionIsParsedCorrectly("section endpoint response -> results -> section", "sections", sectionEndpointXml, _.asInstanceOf[SectionsResponse].results.head)
- )
-
- scenariosFor(
- testBasicResponseHeaderCanBeParsed("id endpoint returning content", "id", idEndpointContentXml),
- testTheStandardArticleIsParsedCorrectly("id endpoint", "id", idEndpointContentXml, _.asInstanceOf[ItemResponse].content.get),
- testThereAre2ItemsInTheResults("id endpoint response -> content -> tags", "id", idEndpointContentXml, _.asInstanceOf[ItemResponse].content.get.tags),
- testTheStandardTagIsParsedCorrectly("id endpoint response -> content -> tag", "id", idEndpointContentXml, _.asInstanceOf[ItemResponse].content.get.tags.head),
- testTheStandardFieldsAreParsedCorrectly("id endpoint", "id", idEndpointContentXml, _.asInstanceOf[ItemResponse].content.get),
- testTheStandardFactboxIsParsedCorrectly("id endpoint", "id", idEndpointContentXml, _.asInstanceOf[ItemResponse].content.get.factboxes.head),
- testTheStandardMediaAssetIsParsedCorrectly("id endpoint -> content -> media-assets", "id", idEndpointContentXml, _.asInstanceOf[ItemResponse].content.get.mediaAssets.head)
- )
-
- scenariosFor(
- testBasicResponseHeaderCanBeParsed("id endpoint returning tag", "id", idEndpointTagXml),
- testTheStandardTagIsParsedCorrectly("id endpoint response -> tag", "id", idEndpointTagXml, _.asInstanceOf[ItemResponse].tag.get),
- testPagedItemResponseHeaderCanBeParsed("id endpoint returning tag", "id", idEndpointTagXml),
- testThereAre2ItemsInTheResults("id endpoint returning tag -> results ", "id", idEndpointTagXml, _.asInstanceOf[ItemResponse].results),
- testTheStandardArticleIsParsedCorrectly("id endpoint returning tag -> results", "id", idEndpointTagXml, _.asInstanceOf[ItemResponse].results.head),
- testThereAre2ItemsInTheResults("id endpoint returning tag -> results -> tags", "id", idEndpointTagXml, _.asInstanceOf[ItemResponse].results.head.tags),
- testTheStandardTagIsParsedCorrectly("id endpoint returning tag -> results -> content -> tag", "id", idEndpointTagXml, _.asInstanceOf[ItemResponse].results.head.tags.head),
- testTheStandardFieldsAreParsedCorrectly("id endpoint returning tag -> results", "id", idEndpointTagXml, _.asInstanceOf[ItemResponse].results.head)
- )
-
- scenariosFor(
- testBasicResponseHeaderCanBeParsed("id endpoint returning section", "id", idEndpointSectionXml),
- testPagedItemResponseHeaderCanBeParsed("id endpoint returning section", "id", idEndpointSectionXml),
- testTheStandardSectionIsParsedCorrectly("id endpoint response -> section", "id", idEndpointSectionXml, _.asInstanceOf[ItemResponse].section.get)
- )
-
-}
Please sign in to comment.
Something went wrong with that request. Please try again.