Dynamic.hs 18 KB

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