123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449 |
- {-# LANGUAGE OverloadedStrings #-}
- {-|
- Module : Codec.ActivityStream.Representation
- Description : A (more dynamic) interface to Activity Streams
- Copyright : (c) Getty Ritter, 2014
- Maintainer : gdritter@galois.com
- This is an interface to ActivityStreams that simply wraps an underlying
- @aeson@ Object, and exposes a set of (convenient) lenses to access the
- values inside. If an @aeson@ object is wrapped in the respective wrapper,
- it will contain the obligatory values for that type (e.g. an 'Activity'
- is guaranteed to have a @published@ date.)
- Most of the inline documentation is drawn directly from the
- <http://activitystrea.ms/specs/json/1.0/ JSON Activity Streams 1.0>
- specification, with minor modifications
- to refer to the corresponding data types in this module and to clarify
- certain aspects.
- -}
- module Codec.ActivityStream.Representation
- ( Lens'
- -- * Object
- , Object
- , emptyObject
- -- ** Object Lenses
- , oAttachments
- , oAuthor
- , oContent
- , oDisplayName
- , oDownstreamDuplicates
- , oId
- , oImage
- , oObjectType
- , oPublished
- , oSummary
- , oUpdated
- , oUpstreamDuplicates
- , oURL
- , oRest
- -- * Activity
- , Activity
- , makeActivity
- , asObject
- -- ** Activity Lenses
- , acActor
- , acContent
- , acGenerator
- , acIcon
- , acId
- , acObject
- , acPublished
- , acProvider
- , acTarget
- , acTitle
- , acUpdated
- , acURL
- , acVerb
- , acRest
- -- * MediaLink
- , MediaLink
- , makeMediaLink
- -- ** MediaLink Lenses
- , mlDuration
- , mlHeight
- , mlWidth
- , mlURL
- , mlRest
- -- * Collection
- , Collection
- , makeCollection
- -- ** Collection Lenses
- , cTotalItems
- , cItems
- , cURL
- , cRest
- ) where
- import Data.Aeson ( FromJSON(..)
- , ToJSON(..)
- , Result(..)
- , fromJSON
- )
- import qualified Data.Aeson as A
- import Data.Time.Clock (UTCTime)
- import qualified Data.HashMap.Strict as HM
- import Data.Text (Text)
- import Codec.ActivityStream.Internal (ensure)
- import Codec.ActivityStream.LensInternal
- -- | Some types of objects may have an alternative visual representation in
- -- the form of an image, video or embedded HTML fragments. A 'MediaLink'
- -- represents a hyperlink to such resources.
- newtype MediaLink = MediaLink { fromMediaLink :: A.Object } deriving (Eq, Show)
- instance FromJSON MediaLink where
- parseJSON (A.Object o) = do
- ensure "MediaLink" o ["url"]
- return (MediaLink o)
- parseJSON _ = fail "MediaLink not an object"
- instance ToJSON MediaLink where
- toJSON (MediaLink o) = A.Object o
- -- | Access the underlying JSON object that represents a Media Link
- mlRest :: Lens' MediaLink A.Object
- mlRest = makeLens fromMediaLink (\ o' m -> m { fromMediaLink = o' })
- -- | A hint to the consumer about the length, in seconds, of the media
- -- resource identified by the url property. A media link MAY contain
- -- a "duration" property when the target resource is a time-based
- -- media item such as an audio or video.
- mlDuration :: Lens' MediaLink (Maybe Int)
- mlDuration = makeAesonLensMb "duration" mlRest
- -- | A hint to the consumer about the height, in pixels, of the media
- -- resource identified by the url property. A media link MAY contain
- -- a @height@ property when the target resource is a visual media item
- -- such as an image, video or embeddable HTML page.
- mlHeight :: Lens' MediaLink (Maybe Int)
- mlHeight = makeAesonLensMb "height" mlRest
- -- | A hint to the consumer about the width, in pixels, of the media
- -- resource identified by the url property. A media link MAY contain
- -- a @width@ property when the target resource is a visual media item
- -- such as an image, video or embeddable HTML page.
- mlWidth :: Lens' MediaLink (Maybe Int)
- mlWidth = makeAesonLensMb "width" mlRest
- -- | The IRI of the media resource being linked. A media link MUST have a
- -- @url@ property.
- mlURL :: Lens' MediaLink Text
- mlURL = makeAesonLens "url" mlRest
- -- | Create a @MediaLink@ with just a @url@ property, and all other
- -- properties undefined.
- makeMediaLink :: Text -> MediaLink
- makeMediaLink url = MediaLink (HM.insert "url" (toJSON url) HM.empty)
- -- | Within the specification, an 'Object' is a thing, real or
- -- imaginary, which participates in an activity. It may be the
- -- entity performing the activity, or the entity on which the
- -- activity was performed. An object consists of properties
- -- defined below. Certain object types may
- -- further refine the meaning of these properties, or they may
- -- define additional properties.
- --
- -- To maintain this flexibility in the Haskell environment, an
- -- 'Object' is an opaque wrapper over an underlying JSON value,
- -- and the 'oRest' accessor can be used to access that underlying
- -- value.
- newtype Object = Object { fromObject :: A.Object } deriving (Eq, Show)
- instance FromJSON Object where
- parseJSON (A.Object o) = return (Object o)
- parseJSON _ = fail "Object not an object"
- instance ToJSON Object where
- toJSON (Object o) = A.Object o
- -- | Access the underlying JSON object that represents an 'Object'
- oRest :: Lens' Object A.Object
- oRest = makeLens fromObject (\ o' m -> m { fromObject = o' })
- -- | A collection of one or more additional, associated objects, similar
- -- to the concept of attached files in an email message. An object MAY
- -- have an attachments property whose value is a JSON Array of 'Object's.
- oAttachments :: Lens' Object (Maybe [Object])
- oAttachments = makeAesonLensMb "attachments" oRest
- -- | Describes the entity that created or authored the object. An object
- -- MAY contain a single author property whose value is an 'Object' of any
- -- type. Note that the author field identifies the entity that created
- -- the object and does not necessarily identify the entity that
- -- published the object. For instance, it may be the case that an
- -- object created by one person is posted and published to a system by
- -- an entirely different entity.
- oAuthor :: Lens' Object (Maybe Object)
- oAuthor = makeAesonLensMb "author" oRest
- -- | Natural-language description of the object encoded as a single JSON
- -- String containing HTML markup. Visual elements such as thumbnail
- -- images MAY be included. An object MAY contain a @content@ property.
- oContent :: Lens' Object (Maybe Text)
- oContent = makeAesonLensMb "content" oRest
- -- | A natural-language, human-readable and plain-text name for the
- -- object. HTML markup MUST NOT be included. An object MAY contain
- -- a @displayName@ property. If the object does not specify an @objectType@
- -- property, the object SHOULD specify a @displayName@.
- oDisplayName :: Lens' Object (Maybe Text)
- oDisplayName = makeAesonLensMb "displayName" oRest
- -- | A JSON Array of one or more absolute IRI's
- -- <http://www.ietf.org/rfc/rfc3987.txt [RFC3987]> identifying
- -- objects that duplicate this object's content. An object SHOULD
- -- contain a @downstreamDuplicates@ property when there are known objects,
- -- possibly in a different system, that duplicate the content in this
- -- object. This MAY be used as a hint for consumers to use when
- -- resolving duplicates between objects received from different sources.
- oDownstreamDuplicates :: Lens' Object (Maybe [Text])
- oDownstreamDuplicates = makeAesonLensMb "downstreamDuplicates" oRest
- -- | Provides a permanent, universally unique identifier for the object in
- -- the form of an absolute IRI
- -- <http://www.ietf.org/rfc/rfc3987.txt [RFC3987]>. An
- -- object SHOULD contain a single @id@ property. If an object does not
- -- contain an @id@ property, consumers MAY use the value of the @url@
- -- property as a less-reliable, non-unique identifier.
- oId :: Lens' Object (Maybe Text)
- oId = makeAesonLensMb "id" oRest
- -- | Description of a resource providing a visual representation of the
- -- object, intended for human consumption. An object MAY contain an
- -- @image@ property whose value is a 'MediaLink'.
- oImage :: Lens' Object (Maybe MediaLink)
- oImage = makeAesonLensMb "image" oRest
- -- | Identifies the type of object. An object MAY contain an @objectType@
- -- property whose value is a JSON String that is non-empty and matches
- -- either the "isegment-nz-nc" or the \"IRI\" production in
- -- <http://www.ietf.org/rfc/rfc3987.txt [RFC3987]>. Note
- -- that the use of a relative reference other than a simple name is
- -- not allowed. If no @objectType@ property is contained, the object has
- -- no specific type.
- oObjectType :: (FromJSON o, ToJSON o) => Lens' Object (Maybe o)
- oObjectType = makeAesonLensMb "objectType" oRest
- -- | The date and time at which the object was published. An object MAY
- -- contain a @published@ property.
- oPublished :: Lens' Object (Maybe UTCTime)
- oPublished = makeAesonLensMb "published" oRest
- -- | Natural-language summarization of the object encoded as a single
- -- JSON String containing HTML markup. Visual elements such as thumbnail
- -- images MAY be included. An activity MAY contain a @summary@ property.
- oSummary :: Lens' Object (Maybe Text)
- oSummary = makeAesonLensMb "summary" oRest
- -- | The date and time at which a previously published object has been
- -- modified. An Object MAY contain an @updated@ property.
- oUpdated :: Lens' Object (Maybe UTCTime)
- oUpdated = makeAesonLensMb "updated" oRest
- -- | A JSON Array of one or more absolute IRI's
- -- <http://www.ietf.org/rfc/rfc3987.txt [RFC3987]> identifying
- -- objects that duplicate this object's content. An object SHOULD contain
- -- an @upstreamDuplicates@ property when a publisher is knowingly
- -- duplicating with a new ID the content from another object. This MAY be
- -- used as a hint for consumers to use when resolving duplicates between
- -- objects received from different sources.
- oUpstreamDuplicates :: Lens' Object (Maybe [Text])
- oUpstreamDuplicates = makeAesonLensMb "upstreamDuplicates" oRest
- -- | An IRI <http://www.ietf.org/rfc/rfc3987.txt [RFC3987]>
- -- identifying a resource providing an HTML representation of the
- -- object. An object MAY contain a url property
- oURL :: Lens' Object (Maybe Text)
- oURL = makeAesonLensMb "url" oRest
- -- | Create an @Object@ with no fields.
- emptyObject :: Object
- emptyObject = Object HM.empty
- -- | In its simplest form, an 'Activity' consists of an @actor@, a @verb@, an
- -- @object@, and a @target@. It tells the story of a person performing an
- -- action on or with an object -- "Geraldine posted a photo to her
- -- album" or "John shared a video". In most cases these components
- -- will be explicit, but they may also be implied.
- newtype Activity = Activity { fromActivity :: A.Object } deriving (Eq, Show)
- instance FromJSON Activity where
- parseJSON (A.Object o) = do
- ensure "Activity" o ["published", "provider"]
- return (Activity o)
- parseJSON _ = fail "\"Activity\" not an object"
- instance ToJSON Activity where
- toJSON (Activity o) = A.Object o
- -- | Access the underlying JSON object that represents an 'Activity'
- acRest :: Lens' Activity A.Object
- acRest = makeLens fromActivity (\ o' m -> m { fromActivity = o' })
- -- | Describes the entity that performed the activity. An activity MUST
- -- contain one @actor@ property whose value is a single 'Object'.
- acActor :: Lens' Activity Object
- acActor = makeAesonLens "actor" acRest
- -- | Natural-language description of the activity encoded as a single
- -- JSON String containing HTML markup. Visual elements such as
- -- thumbnail images MAY be included. An activity MAY contain a
- -- @content@ property.
- acContent :: Lens' Activity (Maybe Text)
- acContent = makeAesonLensMb "content" acRest
- -- | Describes the application that generated the activity. An activity
- -- MAY contain a @generator@ property whose value is a single 'Object'.
- acGenerator :: Lens' Activity (Maybe Object)
- acGenerator = makeAesonLens "generator" acRest
- -- | Description of a resource providing a visual representation of the
- -- object, intended for human consumption. The image SHOULD have an
- -- aspect ratio of one (horizontal) to one (vertical) and SHOULD be
- -- suitable for presentation at a small size. An activity MAY have
- -- an @icon@ property.
- acIcon :: Lens' Activity (Maybe MediaLink)
- acIcon = makeAesonLensMb "icon" acRest
- -- | Provides a permanent, universally unique identifier for the activity
- -- in the form of an absolute IRI
- -- <http://www.ietf.org/rfc/rfc3987.txt [RFC3987]>. An
- -- activity SHOULD contain a single @id@ property. If an activity does
- -- not contain an @id@ property, consumers MAY use the value of the
- -- @url@ property as a less-reliable, non-unique identifier.
- acId :: Lens' Activity (Maybe Text)
- acId = makeAesonLensMb "id" acRest
- -- | Describes the primary object of the activity. For instance, in the
- -- activity, "John saved a movie to his wishlist", the object of the
- -- activity is "movie". An activity SHOULD contain an @object@ property
- -- whose value is a single 'Object'. If the @object@ property is not
- -- contained, the primary object of the activity MAY be implied by
- -- context.
- acObject :: Lens' Activity (Maybe Object)
- acObject = makeAesonLensMb "object" acRest
- -- | The date and time at which the activity was published. An activity
- -- MUST contain a @published@ property.
- acPublished :: Lens' Activity UTCTime
- acPublished = makeAesonLens "published" acRest
- -- | Describes the application that published the activity. Note that this
- -- is not necessarily the same entity that generated the activity. An
- -- activity MAY contain a @provider@ property whose value is a
- -- single 'Object'.
- acProvider :: Lens' Activity (Maybe Object)
- acProvider = makeAesonLensMb "provider" acRest
- -- | Describes the target of the activity. The precise meaning of the
- -- activity's target is dependent on the activities verb, but will
- -- often be the object the English preposition "to". For instance, in
- -- the activity, "John saved a movie to his wishlist", the target of
- -- the activity is "wishlist". The activity target MUST NOT be used
- -- to identity an indirect object that is not a target of the
- -- activity. An activity MAY contain a @target@ property whose value
- -- is a single 'Object'.
- acTarget :: Lens' Activity (Maybe Object)
- acTarget = makeAesonLensMb "target" acRest
- -- | Natural-language title or headline for the activity encoded as a
- -- single JSON String containing HTML markup. An activity MAY contain
- -- a @title@ property.
- acTitle :: Lens' Activity (Maybe Text)
- acTitle = makeAesonLensMb "title" acRest
- -- | The date and time at which a previously published activity has
- -- been modified. An Activity MAY contain an @updated@ property.
- acUpdated :: Lens' Activity (Maybe UTCTime)
- acUpdated = makeAesonLensMb "updated" acRest
- -- | An IRI <http://www.ietf.org/rfc/rfc3987.txt RFC3987>
- -- identifying a resource providing an HTML representation of the
- -- activity. An activity MAY contain a @url@ property.
- acURL :: Lens' Activity (Maybe Text)
- acURL = makeAesonLensMb "url" acRest
- -- | Identifies the action that the activity describes. An activity SHOULD
- -- contain a verb property whose value is a JSON String that is
- -- non-empty and matches either the \"isegment-nz-nc\" or the
- -- \"IRI\" production in <http://www.ietf.org/rfc/rfc3987.txt [RFC3987]>.
- -- Note that the use of a relative
- -- reference other than a simple name is not allowed. If the @verb@ is
- -- not specified, or if the value is null, the @verb@ is
- -- assumed to be \"post\".
- acVerb :: (FromJSON v, ToJSON v) => Lens' Activity (Maybe v)
- acVerb = makeAesonLensMb "verb" acRest
- -- | Create an @Activity@ with an @actor@, @published@, and
- -- @provider@ property.
- makeActivity :: Object -> UTCTime -> Activity
- makeActivity actor published = Activity
- $ HM.insert "actor" (toJSON actor)
- $ HM.insert "published" (toJSON published)
- $ HM.empty
- -- | JSON Activity Streams 1.0 specificies that an @Activity@ may be used as an
- -- @Object@. In such a case, the object may have fields permitted on either an
- -- @Activity@ or an @Object@
- asObject :: Activity -> Object
- asObject act = Object (fromActivity act)
- -- | A "collection" is a generic list of 'Object's of any object type.
- -- The @objectType@ of each item in the collection MAY be omitted if
- -- the type of object can be established through context. The collection
- -- is used primarily as the root of an Activity Streams document as described
- -- in Section 4,
- -- but can be used as the value of extension properties in a variety of
- -- situations.
- newtype Collection = Collection { fromCollection :: A.Object } deriving (Eq, Show)
- instance FromJSON Collection where
- parseJSON (A.Object o) = return (Collection o)
- parseJSON _ = fail "\"Collection\" not an object"
- instance ToJSON Collection where
- toJSON (Collection o) = A.Object o
- -- | Access the underlying JSON object that represents a 'Collection'
- cRest :: Lens' Collection A.Object
- cRest = makeLens fromCollection (\ o' m -> m { fromCollection = o' })
- -- | Non-negative integer specifying the total number of activities
- -- within the stream. The Stream serialization MAY contain a
- -- @totalItems@ property. (NOTE: there is a typo in the original
- -- specification, in which it inconsistently refers to this as
- -- either @totalItems@ or @count@.)
- cTotalItems :: Lens' Collection (Maybe Int)
- cTotalItems = makeAesonLensMb "totalItems" cRest
- -- | An array containing a listing of 'Object's of any object type.
- -- If used in combination with the @url@ property, the @items@ array
- -- can be used to provide a subset of the objects that may be
- -- found in the resource identified by the @url@.
- cItems :: Lens' Collection (Maybe [Object])
- cItems = makeAesonLensMb "items" cRest
- -- | An IRI <http://activitystrea.ms/specs/json/1.0/#RFC3987 [RFC3987]>
- -- referencing a JSON document containing the full
- -- listing of objects in the collection.
- cURL :: Lens' Collection (Maybe Text)
- cURL = makeAesonLensMb "url" cRest
- -- | Create a @Collection@ with an @items@ and a @url@ property
- -- and fill in the corresponding @totalItems@ field with the
- -- length of the @items@ array.
- makeCollection :: [Object] -> Text -> Collection
- makeCollection objs url = Collection
- $ HM.insert "totalItems" (toJSON (length objs))
- $ HM.insert "items" (toJSON objs)
- $ HM.insert "url" (toJSON url)
- $ HM.empty
|