Representation.hs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449
  1. {-# LANGUAGE OverloadedStrings #-}
  2. {-|
  3. Module : Codec.ActivityStream.Representation
  4. Description : A (more dynamic) interface to Activity Streams
  5. Copyright : (c) Getty Ritter, 2014
  6. Maintainer : gdritter@galois.com
  7. This is an interface to ActivityStreams that simply wraps an underlying
  8. @aeson@ Object, and exposes a set of (convenient) lenses to access the
  9. values inside. If an @aeson@ object is wrapped in the respective wrapper,
  10. it will contain the obligatory values for that type (e.g. an 'Activity'
  11. is guaranteed to have a @published@ date.)
  12. Most of the inline documentation is drawn directly from the
  13. <http://activitystrea.ms/specs/json/1.0/ JSON Activity Streams 1.0>
  14. specification, with minor modifications
  15. to refer to the corresponding data types in this module and to clarify
  16. certain aspects.
  17. -}
  18. module Codec.ActivityStream.Representation
  19. ( Lens'
  20. -- * Object
  21. , Object
  22. , emptyObject
  23. -- ** Object Lenses
  24. , oAttachments
  25. , oAuthor
  26. , oContent
  27. , oDisplayName
  28. , oDownstreamDuplicates
  29. , oId
  30. , oImage
  31. , oObjectType
  32. , oPublished
  33. , oSummary
  34. , oUpdated
  35. , oUpstreamDuplicates
  36. , oURL
  37. , oRest
  38. -- * Activity
  39. , Activity
  40. , makeActivity
  41. , asObject
  42. -- ** Activity Lenses
  43. , acActor
  44. , acContent
  45. , acGenerator
  46. , acIcon
  47. , acId
  48. , acObject
  49. , acPublished
  50. , acProvider
  51. , acTarget
  52. , acTitle
  53. , acUpdated
  54. , acURL
  55. , acVerb
  56. , acRest
  57. -- * MediaLink
  58. , MediaLink
  59. , makeMediaLink
  60. -- ** MediaLink Lenses
  61. , mlDuration
  62. , mlHeight
  63. , mlWidth
  64. , mlURL
  65. , mlRest
  66. -- * Collection
  67. , Collection
  68. , makeCollection
  69. -- ** Collection Lenses
  70. , cTotalItems
  71. , cItems
  72. , cURL
  73. , cRest
  74. ) where
  75. import Data.Aeson ( FromJSON(..)
  76. , ToJSON(..)
  77. , Result(..)
  78. , fromJSON
  79. )
  80. import qualified Data.Aeson as A
  81. import Data.Time.Clock (UTCTime)
  82. import qualified Data.HashMap.Strict as HM
  83. import Data.Text (Text)
  84. import Codec.ActivityStream.Internal (ensure)
  85. import Codec.ActivityStream.LensInternal
  86. -- | Some types of objects may have an alternative visual representation in
  87. -- the form of an image, video or embedded HTML fragments. A 'MediaLink'
  88. -- represents a hyperlink to such resources.
  89. newtype MediaLink = MediaLink { fromMediaLink :: A.Object } deriving (Eq, Show)
  90. instance FromJSON MediaLink where
  91. parseJSON (A.Object o) = do
  92. ensure "MediaLink" o ["url"]
  93. return (MediaLink o)
  94. parseJSON _ = fail "MediaLink not an object"
  95. instance ToJSON MediaLink where
  96. toJSON (MediaLink o) = A.Object o
  97. -- | Access the underlying JSON object that represents a Media Link
  98. mlRest :: Lens' MediaLink A.Object
  99. mlRest = makeLens fromMediaLink (\ o' m -> m { fromMediaLink = o' })
  100. -- | A hint to the consumer about the length, in seconds, of the media
  101. -- resource identified by the url property. A media link MAY contain
  102. -- a "duration" property when the target resource is a time-based
  103. -- media item such as an audio or video.
  104. mlDuration :: Lens' MediaLink (Maybe Int)
  105. mlDuration = makeAesonLensMb "duration" mlRest
  106. -- | A hint to the consumer about the height, in pixels, of the media
  107. -- resource identified by the url property. A media link MAY contain
  108. -- a @height@ property when the target resource is a visual media item
  109. -- such as an image, video or embeddable HTML page.
  110. mlHeight :: Lens' MediaLink (Maybe Int)
  111. mlHeight = makeAesonLensMb "height" mlRest
  112. -- | A hint to the consumer about the width, in pixels, of the media
  113. -- resource identified by the url property. A media link MAY contain
  114. -- a @width@ property when the target resource is a visual media item
  115. -- such as an image, video or embeddable HTML page.
  116. mlWidth :: Lens' MediaLink (Maybe Int)
  117. mlWidth = makeAesonLensMb "width" mlRest
  118. -- | The IRI of the media resource being linked. A media link MUST have a
  119. -- @url@ property.
  120. mlURL :: Lens' MediaLink Text
  121. mlURL = makeAesonLens "url" mlRest
  122. -- | Create a @MediaLink@ with just a @url@ property, and all other
  123. -- properties undefined.
  124. makeMediaLink :: Text -> MediaLink
  125. makeMediaLink url = MediaLink (HM.insert "url" (toJSON url) HM.empty)
  126. -- | Within the specification, an 'Object' is a thing, real or
  127. -- imaginary, which participates in an activity. It may be the
  128. -- entity performing the activity, or the entity on which the
  129. -- activity was performed. An object consists of properties
  130. -- defined below. Certain object types may
  131. -- further refine the meaning of these properties, or they may
  132. -- define additional properties.
  133. --
  134. -- To maintain this flexibility in the Haskell environment, an
  135. -- 'Object' is an opaque wrapper over an underlying JSON value,
  136. -- and the 'oRest' accessor can be used to access that underlying
  137. -- value.
  138. newtype Object = Object { fromObject :: A.Object } deriving (Eq, Show)
  139. instance FromJSON Object where
  140. parseJSON (A.Object o) = return (Object o)
  141. parseJSON _ = fail "Object not an object"
  142. instance ToJSON Object where
  143. toJSON (Object o) = A.Object o
  144. -- | Access the underlying JSON object that represents an 'Object'
  145. oRest :: Lens' Object A.Object
  146. oRest = makeLens fromObject (\ o' m -> m { fromObject = o' })
  147. -- | A collection of one or more additional, associated objects, similar
  148. -- to the concept of attached files in an email message. An object MAY
  149. -- have an attachments property whose value is a JSON Array of 'Object's.
  150. oAttachments :: Lens' Object (Maybe [Object])
  151. oAttachments = makeAesonLensMb "attachments" oRest
  152. -- | Describes the entity that created or authored the object. An object
  153. -- MAY contain a single author property whose value is an 'Object' of any
  154. -- type. Note that the author field identifies the entity that created
  155. -- the object and does not necessarily identify the entity that
  156. -- published the object. For instance, it may be the case that an
  157. -- object created by one person is posted and published to a system by
  158. -- an entirely different entity.
  159. oAuthor :: Lens' Object (Maybe Object)
  160. oAuthor = makeAesonLensMb "author" oRest
  161. -- | Natural-language description of the object encoded as a single JSON
  162. -- String containing HTML markup. Visual elements such as thumbnail
  163. -- images MAY be included. An object MAY contain a @content@ property.
  164. oContent :: Lens' Object (Maybe Text)
  165. oContent = makeAesonLensMb "content" oRest
  166. -- | A natural-language, human-readable and plain-text name for the
  167. -- object. HTML markup MUST NOT be included. An object MAY contain
  168. -- a @displayName@ property. If the object does not specify an @objectType@
  169. -- property, the object SHOULD specify a @displayName@.
  170. oDisplayName :: Lens' Object (Maybe Text)
  171. oDisplayName = makeAesonLensMb "displayName" oRest
  172. -- | A JSON Array of one or more absolute IRI's
  173. -- <http://www.ietf.org/rfc/rfc3987.txt [RFC3987]> identifying
  174. -- objects that duplicate this object's content. An object SHOULD
  175. -- contain a @downstreamDuplicates@ property when there are known objects,
  176. -- possibly in a different system, that duplicate the content in this
  177. -- object. This MAY be used as a hint for consumers to use when
  178. -- resolving duplicates between objects received from different sources.
  179. oDownstreamDuplicates :: Lens' Object (Maybe [Text])
  180. oDownstreamDuplicates = makeAesonLensMb "downstreamDuplicates" oRest
  181. -- | Provides a permanent, universally unique identifier for the object in
  182. -- the form of an absolute IRI
  183. -- <http://www.ietf.org/rfc/rfc3987.txt [RFC3987]>. An
  184. -- object SHOULD contain a single @id@ property. If an object does not
  185. -- contain an @id@ property, consumers MAY use the value of the @url@
  186. -- property as a less-reliable, non-unique identifier.
  187. oId :: Lens' Object (Maybe Text)
  188. oId = makeAesonLensMb "id" oRest
  189. -- | Description of a resource providing a visual representation of the
  190. -- object, intended for human consumption. An object MAY contain an
  191. -- @image@ property whose value is a 'MediaLink'.
  192. oImage :: Lens' Object (Maybe MediaLink)
  193. oImage = makeAesonLensMb "image" oRest
  194. -- | Identifies the type of object. An object MAY contain an @objectType@
  195. -- property whose value is a JSON String that is non-empty and matches
  196. -- either the "isegment-nz-nc" or the \"IRI\" production in
  197. -- <http://www.ietf.org/rfc/rfc3987.txt [RFC3987]>. Note
  198. -- that the use of a relative reference other than a simple name is
  199. -- not allowed. If no @objectType@ property is contained, the object has
  200. -- no specific type.
  201. oObjectType :: (FromJSON o, ToJSON o) => Lens' Object (Maybe o)
  202. oObjectType = makeAesonLensMb "objectType" oRest
  203. -- | The date and time at which the object was published. An object MAY
  204. -- contain a @published@ property.
  205. oPublished :: Lens' Object (Maybe UTCTime)
  206. oPublished = makeAesonLensMb "published" oRest
  207. -- | Natural-language summarization of the object encoded as a single
  208. -- JSON String containing HTML markup. Visual elements such as thumbnail
  209. -- images MAY be included. An activity MAY contain a @summary@ property.
  210. oSummary :: Lens' Object (Maybe Text)
  211. oSummary = makeAesonLensMb "summary" oRest
  212. -- | The date and time at which a previously published object has been
  213. -- modified. An Object MAY contain an @updated@ property.
  214. oUpdated :: Lens' Object (Maybe UTCTime)
  215. oUpdated = makeAesonLensMb "updated" oRest
  216. -- | A JSON Array of one or more absolute IRI's
  217. -- <http://www.ietf.org/rfc/rfc3987.txt [RFC3987]> identifying
  218. -- objects that duplicate this object's content. An object SHOULD contain
  219. -- an @upstreamDuplicates@ property when a publisher is knowingly
  220. -- duplicating with a new ID the content from another object. This MAY be
  221. -- used as a hint for consumers to use when resolving duplicates between
  222. -- objects received from different sources.
  223. oUpstreamDuplicates :: Lens' Object (Maybe [Text])
  224. oUpstreamDuplicates = makeAesonLensMb "upstreamDuplicates" oRest
  225. -- | An IRI <http://www.ietf.org/rfc/rfc3987.txt [RFC3987]>
  226. -- identifying a resource providing an HTML representation of the
  227. -- object. An object MAY contain a url property
  228. oURL :: Lens' Object (Maybe Text)
  229. oURL = makeAesonLensMb "url" oRest
  230. -- | Create an @Object@ with no fields.
  231. emptyObject :: Object
  232. emptyObject = Object HM.empty
  233. -- | In its simplest form, an 'Activity' consists of an @actor@, a @verb@, an
  234. -- @object@, and a @target@. It tells the story of a person performing an
  235. -- action on or with an object -- "Geraldine posted a photo to her
  236. -- album" or "John shared a video". In most cases these components
  237. -- will be explicit, but they may also be implied.
  238. newtype Activity = Activity { fromActivity :: A.Object } deriving (Eq, Show)
  239. instance FromJSON Activity where
  240. parseJSON (A.Object o) = do
  241. ensure "Activity" o ["published", "provider"]
  242. return (Activity o)
  243. parseJSON _ = fail "\"Activity\" not an object"
  244. instance ToJSON Activity where
  245. toJSON (Activity o) = A.Object o
  246. -- | Access the underlying JSON object that represents an 'Activity'
  247. acRest :: Lens' Activity A.Object
  248. acRest = makeLens fromActivity (\ o' m -> m { fromActivity = o' })
  249. -- | Describes the entity that performed the activity. An activity MUST
  250. -- contain one @actor@ property whose value is a single 'Object'.
  251. acActor :: Lens' Activity Object
  252. acActor = makeAesonLens "actor" acRest
  253. -- | Natural-language description of the activity encoded as a single
  254. -- JSON String containing HTML markup. Visual elements such as
  255. -- thumbnail images MAY be included. An activity MAY contain a
  256. -- @content@ property.
  257. acContent :: Lens' Activity (Maybe Text)
  258. acContent = makeAesonLensMb "content" acRest
  259. -- | Describes the application that generated the activity. An activity
  260. -- MAY contain a @generator@ property whose value is a single 'Object'.
  261. acGenerator :: Lens' Activity (Maybe Object)
  262. acGenerator = makeAesonLens "generator" acRest
  263. -- | Description of a resource providing a visual representation of the
  264. -- object, intended for human consumption. The image SHOULD have an
  265. -- aspect ratio of one (horizontal) to one (vertical) and SHOULD be
  266. -- suitable for presentation at a small size. An activity MAY have
  267. -- an @icon@ property.
  268. acIcon :: Lens' Activity (Maybe MediaLink)
  269. acIcon = makeAesonLensMb "icon" acRest
  270. -- | Provides a permanent, universally unique identifier for the activity
  271. -- in the form of an absolute IRI
  272. -- <http://www.ietf.org/rfc/rfc3987.txt [RFC3987]>. An
  273. -- activity SHOULD contain a single @id@ property. If an activity does
  274. -- not contain an @id@ property, consumers MAY use the value of the
  275. -- @url@ property as a less-reliable, non-unique identifier.
  276. acId :: Lens' Activity (Maybe Text)
  277. acId = makeAesonLensMb "id" acRest
  278. -- | Describes the primary object of the activity. For instance, in the
  279. -- activity, "John saved a movie to his wishlist", the object of the
  280. -- activity is "movie". An activity SHOULD contain an @object@ property
  281. -- whose value is a single 'Object'. If the @object@ property is not
  282. -- contained, the primary object of the activity MAY be implied by
  283. -- context.
  284. acObject :: Lens' Activity (Maybe Object)
  285. acObject = makeAesonLensMb "object" acRest
  286. -- | The date and time at which the activity was published. An activity
  287. -- MUST contain a @published@ property.
  288. acPublished :: Lens' Activity UTCTime
  289. acPublished = makeAesonLens "published" acRest
  290. -- | Describes the application that published the activity. Note that this
  291. -- is not necessarily the same entity that generated the activity. An
  292. -- activity MAY contain a @provider@ property whose value is a
  293. -- single 'Object'.
  294. acProvider :: Lens' Activity (Maybe Object)
  295. acProvider = makeAesonLensMb "provider" acRest
  296. -- | Describes the target of the activity. The precise meaning of the
  297. -- activity's target is dependent on the activities verb, but will
  298. -- often be the object the English preposition "to". For instance, in
  299. -- the activity, "John saved a movie to his wishlist", the target of
  300. -- the activity is "wishlist". The activity target MUST NOT be used
  301. -- to identity an indirect object that is not a target of the
  302. -- activity. An activity MAY contain a @target@ property whose value
  303. -- is a single 'Object'.
  304. acTarget :: Lens' Activity (Maybe Object)
  305. acTarget = makeAesonLensMb "target" acRest
  306. -- | Natural-language title or headline for the activity encoded as a
  307. -- single JSON String containing HTML markup. An activity MAY contain
  308. -- a @title@ property.
  309. acTitle :: Lens' Activity (Maybe Text)
  310. acTitle = makeAesonLensMb "title" acRest
  311. -- | The date and time at which a previously published activity has
  312. -- been modified. An Activity MAY contain an @updated@ property.
  313. acUpdated :: Lens' Activity (Maybe UTCTime)
  314. acUpdated = makeAesonLensMb "updated" acRest
  315. -- | An IRI <http://www.ietf.org/rfc/rfc3987.txt RFC3987>
  316. -- identifying a resource providing an HTML representation of the
  317. -- activity. An activity MAY contain a @url@ property.
  318. acURL :: Lens' Activity (Maybe Text)
  319. acURL = makeAesonLensMb "url" acRest
  320. -- | Identifies the action that the activity describes. An activity SHOULD
  321. -- contain a verb property whose value is a JSON String that is
  322. -- non-empty and matches either the \"isegment-nz-nc\" or the
  323. -- \"IRI\" production in <http://www.ietf.org/rfc/rfc3987.txt [RFC3987]>.
  324. -- Note that the use of a relative
  325. -- reference other than a simple name is not allowed. If the @verb@ is
  326. -- not specified, or if the value is null, the @verb@ is
  327. -- assumed to be \"post\".
  328. acVerb :: (FromJSON v, ToJSON v) => Lens' Activity (Maybe v)
  329. acVerb = makeAesonLensMb "verb" acRest
  330. -- | Create an @Activity@ with an @actor@, @published@, and
  331. -- @provider@ property.
  332. makeActivity :: Object -> UTCTime -> Activity
  333. makeActivity actor published = Activity
  334. $ HM.insert "actor" (toJSON actor)
  335. $ HM.insert "published" (toJSON published)
  336. $ HM.empty
  337. -- | JSON Activity Streams 1.0 specificies that an @Activity@ may be used as an
  338. -- @Object@. In such a case, the object may have fields permitted on either an
  339. -- @Activity@ or an @Object@
  340. asObject :: Activity -> Object
  341. asObject act = Object (fromActivity act)
  342. -- | A "collection" is a generic list of 'Object's of any object type.
  343. -- The @objectType@ of each item in the collection MAY be omitted if
  344. -- the type of object can be established through context. The collection
  345. -- is used primarily as the root of an Activity Streams document as described
  346. -- in Section 4,
  347. -- but can be used as the value of extension properties in a variety of
  348. -- situations.
  349. newtype Collection = Collection { fromCollection :: A.Object } deriving (Eq, Show)
  350. instance FromJSON Collection where
  351. parseJSON (A.Object o) = return (Collection o)
  352. parseJSON _ = fail "\"Collection\" not an object"
  353. instance ToJSON Collection where
  354. toJSON (Collection o) = A.Object o
  355. -- | Access the underlying JSON object that represents a 'Collection'
  356. cRest :: Lens' Collection A.Object
  357. cRest = makeLens fromCollection (\ o' m -> m { fromCollection = o' })
  358. -- | Non-negative integer specifying the total number of activities
  359. -- within the stream. The Stream serialization MAY contain a
  360. -- @totalItems@ property. (NOTE: there is a typo in the original
  361. -- specification, in which it inconsistently refers to this as
  362. -- either @totalItems@ or @count@.)
  363. cTotalItems :: Lens' Collection (Maybe Int)
  364. cTotalItems = makeAesonLensMb "totalItems" cRest
  365. -- | An array containing a listing of 'Object's of any object type.
  366. -- If used in combination with the @url@ property, the @items@ array
  367. -- can be used to provide a subset of the objects that may be
  368. -- found in the resource identified by the @url@.
  369. cItems :: Lens' Collection (Maybe [Object])
  370. cItems = makeAesonLensMb "items" cRest
  371. -- | An IRI <http://activitystrea.ms/specs/json/1.0/#RFC3987 [RFC3987]>
  372. -- referencing a JSON document containing the full
  373. -- listing of objects in the collection.
  374. cURL :: Lens' Collection (Maybe Text)
  375. cURL = makeAesonLensMb "url" cRest
  376. -- | Create a @Collection@ with an @items@ and a @url@ property
  377. -- and fill in the corresponding @totalItems@ field with the
  378. -- length of the @items@ array.
  379. makeCollection :: [Object] -> Text -> Collection
  380. makeCollection objs url = Collection
  381. $ HM.insert "totalItems" (toJSON (length objs))
  382. $ HM.insert "items" (toJSON objs)
  383. $ HM.insert "url" (toJSON url)
  384. $ HM.empty