Ticked version; removed non-Dynamic version of library
Getty Ritter
10 years ago
1 | {-# LANGUAGE Rank2Types #-} | |
2 | {-# LANGUAGE OverloadedStrings #-} | |
3 | ||
4 | {-| | |
5 | Module : Codec.ActivityStream.DynamicSchema | |
6 | Description : A (more dynamic) interface to the Activity Streams Base Schema | |
7 | Copyright : (c) Getty Ritter, 2014 | |
8 | Maintainer : gdritter@galois.com | |
9 | ||
10 | This is an interface to the extended ActivityStreams schema which defines | |
11 | an extensive set of @verb@ values, additional @objectType@ values, and a | |
12 | set of extended properties for 'Object's. | |
13 | ||
14 | Most of the inline documentation is drawn directly from the | |
15 | <https://github.com/activitystreams/activity-schema/blob/master/activity-schema.md Activity Base Schema draft> | |
16 | specification, with minor modifications | |
17 | to refer to the corresponding data types in this module and to clarify | |
18 | certain aspects. This is not an approved draft, and as such may be | |
19 | subject to changes which will be reflected in this module. In contrast to | |
20 | "Codec.ActivityStream", the API in this module makes __no guarantees about | |
21 | long-term stability__. | |
22 | -} | |
23 | ||
24 | module Codec.ActivityStream.DynamicSchema | |
25 | ( module Codec.ActivityStream.Dynamic | |
26 | -- * Verbs | |
27 | , SchemaVerb(..) | |
28 | -- * Object Types | |
29 | , SchemaObjectType(..) | |
30 | -- ** Audio/Video | |
31 | , avEmbedCode | |
32 | , avStream | |
33 | -- ** Binary | |
34 | , bnCompression | |
35 | , bnData | |
36 | , bnFileUrl | |
37 | , bnLength | |
38 | , bnMd5 | |
39 | , bnMimeType | |
40 | -- ** Event | |
41 | , evAttendedBy | |
42 | , evAttending | |
43 | , evEndTime | |
44 | , evInvited | |
45 | , evMaybeAttending | |
46 | , evNotAttendedBy | |
47 | , evNotAttending | |
48 | , evStartTime | |
49 | -- ** Issue | |
50 | , isTypes | |
51 | -- ** Permission | |
52 | , pmScope | |
53 | , pmActions | |
54 | -- ** Place | |
55 | , plPosition | |
56 | , plAddress | |
57 | -- *** PlacePosition | |
58 | , PlacePosition | |
59 | -- *** PlaceAddress | |
60 | , PlaceAddress | |
61 | -- ** Role/Group | |
62 | , rlMembers | |
63 | -- ** Task | |
64 | , tsActor | |
65 | , tsBy | |
66 | , tsObject | |
67 | , tsPrerequisites | |
68 | , tsRequired | |
69 | , tsSupersedes | |
70 | , tsVerb | |
71 | -- * Basic Extension Properties | |
72 | , acContext | |
73 | , getLocation | |
74 | , oMood | |
75 | , oRating | |
76 | , acResult | |
77 | , getSource | |
78 | , getStartTime | |
79 | , getEndTime | |
80 | , oTags | |
81 | -- * Mood | |
82 | , Mood | |
83 | , moodRest | |
84 | , moodDisplayName | |
85 | , moodImage | |
86 | ) where | |
87 | ||
88 | import qualified Data.Aeson as Aeson | |
89 | import Data.DateTime (DateTime) | |
90 | import Data.Aeson ( FromJSON(..), ToJSON(..) ) | |
91 | import qualified Data.HashMap.Strict as HM | |
92 | import Data.Text (Text) | |
93 | ||
94 | import Codec.ActivityStream.LensInternal | |
95 | import Codec.ActivityStream.Dynamic | |
96 | import Codec.ActivityStream.Schema (SchemaVerb(..), SchemaObjectType(..)) | |
97 | ||
98 | -- audio/video | |
99 | ||
100 | -- | A fragment of HTML markup that, when embedded within another HTML | |
101 | -- page, provides an interactive user-interface for viewing or listening | |
102 | -- to the video or audio stream. | |
103 | avEmbedCode :: Lens' Object (Maybe Text) | |
104 | avEmbedCode = makeAesonLensMb "embedCode" oRest | |
105 | ||
106 | -- | An Activity Streams Media Link to the video or audio content itself. | |
107 | avStream :: Lens' Object (Maybe MediaLink) | |
108 | avStream = makeAesonLensMb "stream" oRest | |
109 | ||
110 | -- binary | |
111 | ||
112 | -- | An optional token identifying a compression algorithm applied to | |
113 | -- the binary data prior to Base64-encoding. Possible algorithms | |
114 | -- are "deflate" and "gzip", respectively indicating the use of | |
115 | -- the compression mechanisms defined by RFC 1951 and RFC 1952. | |
116 | -- Additional compression algorithms MAY be used but are not defined | |
117 | -- by this specification. Note that previous versions of this | |
118 | -- specification allowed for multiple compression algorithms to be | |
119 | -- applied and listed using a comma-separated format. The use of | |
120 | -- multiple compressions is no longer permitted. | |
121 | bnCompression :: Lens' Object (Maybe Text) | |
122 | bnCompression = makeAesonLensMb "compression" oRest | |
123 | ||
124 | -- | The URL-Safe Base64-encoded representation of the binary data | |
125 | bnData :: Lens' Object (Maybe Text) | |
126 | bnData = makeAesonLensMb "data" oRest | |
127 | -- | An optional IRI for the binary data described by this object. | |
128 | bnFileUrl :: Lens' Object (Maybe Text) | |
129 | bnFileUrl = makeAesonLensMb "fileUrl" oRest | |
130 | ||
131 | -- | The total number of unencoded, uncompressed octets contained | |
132 | -- within the "data" field. | |
133 | bnLength :: Lens' Object (Maybe Text) | |
134 | bnLength = makeAesonLensMb "length" oRest | |
135 | ||
136 | -- | An optional MD5 checksum calculated over the unencoded, | |
137 | -- uncompressed octets contained within the "data" field | |
138 | bnMd5 :: Lens' Object (Maybe Text) | |
139 | bnMd5 = makeAesonLensMb "md5" oRest | |
140 | ||
141 | -- | The MIME Media Type of the binary data contained within the object. | |
142 | bnMimeType :: Lens' Object (Maybe Text) | |
143 | bnMimeType = makeAesonLensMb "mimeType" oRest | |
144 | ||
145 | -- event | |
146 | ||
147 | -- | A collection object as defined in Section 3.5 of the JSON | |
148 | -- Activity Streams specification that provides information about | |
149 | -- entities that attended the event. | |
150 | evAttendedBy :: Lens' Object (Maybe Collection) | |
151 | evAttendedBy = makeAesonLensMb "attendedBy" oRest | |
152 | ||
153 | -- | A collection object as defined in Section 3.5 of the JSON | |
154 | -- Activity Streams specification that provides information about | |
155 | -- entities that intend to attend the event. | |
156 | evAttending :: Lens' Object (Maybe Collection) | |
157 | evAttending = makeAesonLensMb "attending" oRest | |
158 | ||
159 | -- | The date and time that the event ends represented as a String | |
160 | -- conforming to the "date-time" production in [RFC3339]. | |
161 | evEndTime :: Lens' Object (Maybe DateTime) | |
162 | evEndTime = makeAesonLensMb "endTime" oRest | |
163 | ||
164 | -- | A collection object as defined in Section 3.5 of the JSON | |
165 | -- Activity Streams specification that provides information about | |
166 | -- entities that have been invited to the event. | |
167 | evInvited :: Lens' Object (Maybe Collection) | |
168 | evInvited = makeAesonLensMb "invited" oRest | |
169 | ||
170 | -- | A collection object as defined in Section 3.5 of the JSON | |
171 | -- Activity Streams specification that provides information about | |
172 | -- entities that possibly may attend the event. | |
173 | evMaybeAttending :: Lens' Object (Maybe Collection) | |
174 | evMaybeAttending = makeAesonLensMb "maybeAttending" oRest | |
175 | ||
176 | -- | A collection object as defined in Section 3.5 of the JSON | |
177 | -- Activity Streams specification that provides information about | |
178 | -- entities that did not attend the event. | |
179 | evNotAttendedBy :: Lens' Object (Maybe Collection) | |
180 | evNotAttendedBy = makeAesonLensMb "notAttendedBy" oRest | |
181 | ||
182 | -- | A collection object as defined in Section 3.5 of the JSON | |
183 | -- Activity Streams specification that provides information about | |
184 | -- entities that do not intend to attend the event. | |
185 | evNotAttending :: Lens' Object (Maybe Collection) | |
186 | evNotAttending = makeAesonLensMb "notAttending" oRest | |
187 | ||
188 | -- | The date and time that the event begins represented as a String | |
189 | -- confirming to the "date-time" production in RFC 3339. | |
190 | evStartTime :: Lens' Object (Maybe DateTime) | |
191 | evStartTime = makeAesonLensMb "startTime" oRest | |
192 | ||
193 | -- issue | |
194 | ||
195 | -- | An array of one or more absolute IRI's that describe the type of | |
196 | -- issue represented by the object. Note that the IRI's are intended | |
197 | -- for use as identifiers and MAY or MAY NOT be dereferenceable. | |
198 | isTypes :: Lens' Object (Maybe [Text]) | |
199 | isTypes = makeAesonLensMb "types" oRest | |
200 | ||
201 | -- permission | |
202 | ||
203 | -- | A single Activity Streams Object, of any objectType, that | |
204 | -- identifies the scope of the permission. For example, if the | |
205 | -- permission objects describes write permissions for a given file, | |
206 | -- the scope property would be a file object describing that file. | |
207 | pmScope :: Lens' Object (Maybe Object) | |
208 | pmScope = makeAesonLensMb "scope" oRest | |
209 | ||
210 | -- | An array of Strings that identify the specific actions associated | |
211 | -- with the permission. The actions are application and scope | |
212 | -- specific. No common, core set of actions is defined by this | |
213 | -- specification. | |
214 | pmActions :: Lens' Object (Maybe [Text]) | |
215 | pmActions = makeAesonLensMb "actions" oRest | |
216 | ||
217 | -- place | |
218 | ||
219 | -- | The latitude, longitude and altitude of the place as a point on | |
220 | -- Earth. Represented as a JSON Object as described below. | |
221 | plPosition :: Lens' Object (Maybe PlacePosition) | |
222 | plPosition = makeAesonLensMb "position" oRest | |
223 | ||
224 | -- | A physical address represented as a JSON object as described below. | |
225 | plAddress :: Lens' Object (Maybe PlaceAddress) | |
226 | plAddress = makeAesonLensMb "address" oRest | |
227 | ||
228 | data PlacePosition = PPO { fromPPO :: Aeson.Object } deriving (Eq, Show) | |
229 | ||
230 | instance FromJSON PlacePosition where | |
231 | parseJSON (Aeson.Object o) | |
232 | | HM.member "altitude" o | |
233 | && HM.member "latitude" o | |
234 | && HM.member "longitude" o = return (PPO o) | |
235 | | otherwise = fail "..." | |
236 | parseJSON _ = fail "..." | |
237 | ||
238 | instance ToJSON PlacePosition where | |
239 | toJSON = Aeson.Object . fromPPO | |
240 | ||
241 | data PlaceAddress = PAO { fromPAO :: Aeson.Object } deriving (Eq, Show) | |
242 | ||
243 | instance FromJSON PlaceAddress where | |
244 | parseJSON (Aeson.Object o) | |
245 | | HM.member "formatted" o | |
246 | && HM.member "streetAddress" o | |
247 | && HM.member "locality" o | |
248 | && HM.member "region" o | |
249 | && HM.member "postalCode" o | |
250 | && HM.member "country" o = return (PAO o) | |
251 | | otherwise = fail "..." | |
252 | parseJSON _ = fail "..." | |
253 | ||
254 | instance ToJSON PlaceAddress where | |
255 | toJSON = Aeson.Object . fromPAO | |
256 | ||
257 | -- role/group | |
258 | ||
259 | -- | An optional Activity Streams Collection object listing the | |
260 | -- members of a group, or listing the entities assigned to a | |
261 | -- particular role. | |
262 | rlMembers :: Lens' Object (Maybe [Object]) | |
263 | rlMembers = makeAesonLensMb "members" oRest | |
264 | ||
265 | -- Task | |
266 | ||
267 | -- | An Activity Streams Object that provides information about the | |
268 | -- actor that is expected to complete the task. | |
269 | tsActor :: Lens' Object (Maybe Object) | |
270 | tsActor = makeAesonLensMb "actor" oRest | |
271 | ||
272 | -- | A RFC 3339 date-time specifying the date and time by which the | |
273 | -- task is to be completed. | |
274 | tsBy :: Lens' Object (Maybe DateTime) | |
275 | tsBy = makeAesonLensMb "by" oRest | |
276 | ||
277 | -- | An Activity Streams object describing the object of the task. | |
278 | tsObject :: Lens' Object (Maybe Object) | |
279 | tsObject = makeAesonLensMb "object" oRest | |
280 | ||
281 | -- | An Array of other Task objects that are to be completed before | |
282 | -- this task can be completed. | |
283 | tsPrerequisites :: Lens' Object (Maybe [Object]) | |
284 | tsPrerequisites = makeAesonLensMb "prerequisites" oRest | |
285 | ||
286 | -- | A boolean value indicating whether completion of this task is | |
287 | -- considered to be mandatory. | |
288 | tsRequired :: Lens' Object (Maybe Bool) | |
289 | tsRequired = makeAesonLensMb "required" oRest | |
290 | ||
291 | -- | An Array of other Task objects that are superseded by this task object. | |
292 | tsSupersedes :: Lens' Object (Maybe [Object]) | |
293 | tsSupersedes = makeAesonLensMb "supersedes" oRest | |
294 | ||
295 | -- | A string indicating the verb for this task as defined in Section | |
296 | -- 3.2 of [activitystreams]. | |
297 | tsVerb :: Lens' Object (Maybe SchemaVerb) | |
298 | tsVerb = makeAesonLensMb "verb" oRest | |
299 | ||
300 | -- extra properties | |
301 | ||
302 | -- | The additional @context@ property allows an 'Activity' to further | |
303 | -- include information about why a particular action occurred by | |
304 | -- providing details about the context within which a particular | |
305 | -- Activity was performed. The value of the @context@ property is an | |
306 | -- 'Object' of any @objectType@. The meaning of the @context@ property is | |
307 | -- only defined when used within an 'Activity' object. | |
308 | acContext :: Lens' Activity (Maybe Object) | |
309 | acContext = makeAesonLensMb "context" acRest | |
310 | ||
311 | -- | When appearing within an activity, the location data indicates | |
312 | -- the location where the activity occurred. When appearing within an | |
313 | -- object, the location data indicates the location of that object at | |
314 | -- the time the activity occurred. | |
315 | getLocation :: Lens' a Aeson.Object -> Lens' a (Maybe Object) | |
316 | getLocation = makeAesonLensMb "location" | |
317 | ||
318 | -- | Mood describes the mood of the user when the activity was | |
319 | -- performed. This is usually collected via an extra field in the user | |
320 | -- interface used to perform the activity. For the purpose of the | |
321 | -- schema, a mood is a freeform, short mood keyword or phrase along | |
322 | -- with an optional mood icon image. | |
323 | oMood :: Lens' Object (Maybe Mood) | |
324 | oMood = makeAesonLensMb "mood" oRest | |
325 | ||
326 | -- | A rating given as a number between 1.0 and 5.0 inclusive with one | |
327 | -- decimal place of precision. Represented in JSON as a property | |
328 | -- called @rating@ whose value is a JSON number giving the rating. | |
329 | oRating :: Lens' Object (Maybe Double) | |
330 | oRating = makeAesonLensMb "rating" oRest | |
331 | ||
332 | -- | The @result@ provides a description of the result of any particular | |
333 | -- activity. The value of the @result@ property is an Object of any | |
334 | -- objectType. The meaning of the @result@ property is only defined when | |
335 | -- used within an 'Activity' object. | |
336 | acResult :: Lens' Activity (Maybe Object) | |
337 | acResult = makeAesonLensMb "result" acRest | |
338 | ||
339 | -- | The @source@ property provides a reference to the original source of | |
340 | -- an object or activity. The value of the @source@ property is an | |
341 | -- Object of any objectType. | |
342 | -- | |
343 | -- The @source@ property is closely related to | |
344 | -- the @generator@ and @provider@ properties but serves the distinct | |
345 | -- purpose of identifying where the activity or object was originally | |
346 | -- published as opposed to identifying the applications that generated | |
347 | -- or published it. | |
348 | getSource :: Lens' a Aeson.Object -> Lens' a (Maybe Object) | |
349 | getSource = makeAesonLensMb "source" | |
350 | ||
351 | -- | When an long running Activity occurs over a distinct period of | |
352 | -- time, or when an Object represents a long-running process or event, | |
353 | -- the @startTime@ propertiy can be used to specify the | |
354 | -- date and time at which the activity or object begins. | |
355 | -- The values for each are represented as JSON Strings | |
356 | -- conforming to the "date-time" production in RFC3339. | |
357 | getStartTime :: Lens' a Aeson.Object -> Lens' a (Maybe Text) | |
358 | getStartTime = makeAesonLensMb "startTime" | |
359 | ||
360 | -- | When an long running Activity occurs over a distinct period of | |
361 | -- time, or when an Object represents a long-running process or event, | |
362 | -- the @endTime@ propertiy can be used to specify the | |
363 | -- date and time at which the activity or object concludes. | |
364 | -- The values for each are represented as JSON Strings | |
365 | -- conforming to the "date-time" production in RFC3339. | |
366 | getEndTime :: Lens' a Aeson.Object -> Lens' a (Maybe Text) | |
367 | getEndTime = makeAesonLensMb "endTime" | |
368 | ||
369 | -- | A listing of the objects that have been associated with a | |
370 | -- particular object. Represented in JSON using a property named @tags@ | |
371 | -- whose value is an Array of objects. | |
372 | oTags :: Lens' Object (Maybe [Object]) | |
373 | oTags = makeAesonLensMb "tags" oRest | |
374 | ||
375 | -- mood | |
376 | ||
377 | -- | Mood describes the mood of the user when the activity was | |
378 | -- performed. This is usually collected via an extra field in the user | |
379 | -- interface used to perform the activity. For the purpose of this | |
380 | -- schema, a mood is a freeform, short mood keyword or phrase along | |
381 | -- with an optional mood icon image. | |
382 | data Mood = Mood { fromMood :: Aeson.Object } deriving (Eq, Show) | |
383 | ||
384 | instance FromJSON Mood where | |
385 | parseJSON (Aeson.Object o) | |
386 | | HM.member "displayName" o | |
387 | && HM.member "image" o = return (Mood o) | |
388 | | otherwise = fail "..." | |
389 | parseJSON _ = fail "..." | |
390 | ||
391 | instance ToJSON Mood where | |
392 | toJSON = Aeson.Object . fromMood | |
393 | ||
394 | -- | Access to the underlying JSON object of a 'Mood' | |
395 | moodRest :: Lens' Mood Aeson.Object | |
396 | moodRest = makeLens fromMood (\ o' m -> m { fromMood = o' }) | |
397 | ||
398 | -- | The natural-language, human-readable and plain-text keyword or | |
399 | -- phrase describing the mood. HTML markup MUST NOT be included. | |
400 | moodDisplayName :: Lens' Mood Text | |
401 | moodDisplayName = makeAesonLens "displayName" moodRest | |
402 | ||
403 | -- | An optional image that provides a visual representation of the mood. | |
404 | moodImage :: Lens' Mood MediaLink | |
405 | moodImage = makeAesonLens "image" moodRest |
1 | 1 | {-# LANGUAGE ViewPatterns #-} |
2 | 2 | |
3 |
module Codec.ActivityStream.Internal (commonOpts, commonOptsCC |
|
3 | module Codec.ActivityStream.Internal (commonOpts, commonOptsCC, ensure) where | |
4 | 4 | |
5 | 5 | import Control.Monad (mzero) |
6 | 6 | import Data.Aeson |
7 | 7 | import Data.Aeson.TH |
8 | 8 | import Data.Char |
9 | import Data.Text (pack, unpack) | |
10 | import Network.URI (URI, parseURI) | |
9 | import Data.HashMap.Strict (HashMap, member) | |
10 | import Data.Monoid ((<>)) | |
11 | import Data.Text (Text, pack, unpack) | |
11 | 12 | |
12 | instance FromJSON URI where | |
13 | parseJSON (String ((parseURI . unpack) -> Just u)) = return u | |
14 | parseJSON _ = mzero | |
15 | ||
16 | instance ToJSON URI where | |
17 | toJSON = String . pack . show | |
13 | ensure :: Monad m => String -> HashMap Text Value -> [Text] -> m () | |
14 | ensure objName obj keys = mapM_ go keys | |
15 | where go k | |
16 | | member k obj = return () | |
17 | | otherwise = fail ("Object \"" <> objName <> | |
18 | "\" does not contain property \"" <> | |
19 | unpack k <> "\"") | |
18 | 20 | |
19 | 21 | toCamelCaseUpper :: String -> String |
20 | 22 | toCamelCaseUpper = toCamelCase True |
50 | 52 | { fieldLabelModifier = fromCamelCase . drop (length prefix) |
51 | 53 | , constructorTagModifier = fromCamelCase |
52 | 54 | , omitNothingFields = True |
55 | ||
53 | 56 | } |
1 | 1 | {-# LANGUAGE OverloadedStrings #-} |
2 | {-# LANGUAGE TemplateHaskell #-} | |
3 | ||
4 | module Codec.ActivityStream.Representation where | |
5 | ||
6 | import Control.Applicative | |
7 | import Control.Lens hiding ((.=)) | |
2 | ||
3 | ||
4 | {-| | |
5 | Module : Codec.ActivityStream.Representation | |
6 | Description : A (more dynamic) interface to Activity Streams | |
7 | Copyright : (c) Getty Ritter, 2014 | |
8 | Maintainer : gdritter@galois.com | |
9 | ||
10 | This is an interface to ActivityStreams that simply wraps an underlying | |
11 | @aeson@ Object, and exposes a set of (convenient) lenses to access the | |
12 | values inside. If an @aeson@ object is wrapped in the respective wrapper, | |
13 | it will contain the obligatory values for that type (e.g. an 'Activity' | |
14 | is guaranteed to have a @published@ date.) | |
15 | ||
16 | Most of the inline documentation is drawn directly from the | |
17 | <http://activitystrea.ms/specs/json/1.0/ JSON Activity Streams 1.0> | |
18 | specification, with minor modifications | |
19 | to refer to the corresponding data types in this module and to clarify | |
20 | certain aspects. | |
21 | -} | |
22 | ||
23 | module Codec.ActivityStream.Representation | |
24 | ( Lens' | |
25 | -- * Object | |
26 | , Object | |
27 | , emptyObject | |
28 | -- ** Object Lenses | |
29 | , oAttachments | |
30 | , oAuthor | |
31 | , oContent | |
32 | , oDisplayName | |
33 | , oDownstreamDuplicates | |
34 | , oId | |
35 | , oImage | |
36 | , oObjectType | |
37 | , oPublished | |
38 | , oSummary | |
39 | , oUpdated | |
40 | , oUpstreamDuplicates | |
41 | , oURL | |
42 | , oRest | |
43 | -- * Activity | |
44 | , Activity | |
45 | , makeActivity | |
46 | , asObject | |
47 | -- ** Activity Lenses | |
48 | , acActor | |
49 | , acContent | |
50 | , acGenerator | |
51 | , acIcon | |
52 | , acId | |
53 | , acObject | |
54 | , acPublished | |
55 | , acProvider | |
56 | , acTarget | |
57 | , acTitle | |
58 | , acUpdated | |
59 | , acURL | |
60 | , acVerb | |
61 | , acRest | |
62 | -- * MediaLink | |
63 | , MediaLink | |
64 | , makeMediaLink | |
65 | -- ** MediaLink Lenses | |
66 | , mlDuration | |
67 | , mlHeight | |
68 | , mlWidth | |
69 | , mlURL | |
70 | , mlRest | |
71 | -- * Collection | |
72 | , Collection | |
73 | , makeCollection | |
74 | -- ** Collection Lenses | |
75 | , cTotalItems | |
76 | , cItems | |
77 | , cURL | |
78 | , cRest | |
79 | ) where | |
80 | ||
8 | 81 | import Data.Aeson ( FromJSON(..) |
9 | 82 | , ToJSON(..) |
10 |
, |
|
83 | , Result(..) | |
11 | 84 | , fromJSON |
12 | , object | |
13 | , (.=) | |
14 | , (.:) | |
15 | , (.:?) | |
16 | 85 | ) |
17 | import qualified Data.Aeson as Ae | |
18 | import Data.Aeson.TH | |
19 |
import |
|
86 | import qualified Data.Aeson as A | |
87 | import Data.DateTime (DateTime) | |
20 | 88 | import qualified Data.HashMap.Strict as HM |
21 | import Data.Maybe (catMaybes) | |
22 | 89 | import Data.Text (Text) |
23 | import Network.URI | |
24 | ||
25 | import Codec.ActivityStream.Internal | |
26 | ||
27 | data Verb ext | |
28 | = Post | |
29 | | VerbExt ext | |
30 | deriving (Eq, Show) | |
31 | ||
32 | instance FromJSON ext => FromJSON (Verb ext) where | |
33 | parseJSON (Ae.String "post") = return Post | |
34 | parseJSON ext = VerbExt `fmap` parseJSON ext | |
35 | ||
36 | instance ToJSON ext => ToJSON (Verb ext) where | |
37 | toJSON Post = Ae.String "post" | |
38 | toJSON (VerbExt ext) = toJSON ext | |
39 | ||
40 | data MediaLink = MediaLink | |
41 | { _mlDuration :: Maybe Int | |
42 | , _mlHeight :: Maybe Int | |
43 | , _mlURL :: Text | |
44 | , _mlWidth :: Maybe Int | |
45 | } deriving (Eq, Show) | |
46 | ||
47 | makeLenses ''MediaLink | |
48 | deriveJSON (commonOpts "_ml") ''MediaLink | |
49 | ||
50 | data Object objType = Object | |
51 | { _oAttachments :: [Object objType] | |
52 | , _oAuthor :: Maybe (Object objType) | |
53 | , _oContent :: Maybe Text | |
54 | , _oDisplayName :: Maybe Text | |
55 | , _oDownstreamDuplicates :: [URI] | |
56 | , _oId :: Maybe URI | |
57 | , _oImage :: Maybe MediaLink | |
58 | , _oObjectType :: Maybe objType | |
59 | , _oPublished :: Maybe DateTime | |
60 | , _oSummary :: Maybe Text | |
61 | , _oUpdated :: Maybe DateTime | |
62 | , _oUpstreamDuplicates :: [URI] | |
63 | , _oURL :: Maybe URI | |
64 | , _oRest :: [(Text, Value)] | |
65 | } deriving (Eq, Show) | |
66 | ||
67 | makeLenses ''Object | |
68 | ||
69 | objectFields :: [Text] | |
70 | objectFields = | |
71 | [ "attachments" | |
72 | , "author" | |
73 | , "content" | |
74 | , "displayName" | |
75 | , "downstreamDuplicates" | |
76 | , "id" | |
77 | , "image" | |
78 | , "objectType" | |
79 | , "published" | |
80 | , "summary" | |
81 | , "updated" | |
82 | , "upstreamDuplicates" | |
83 | , "url" | |
84 | ] | |
85 | ||
86 | instance FromJSON objType => FromJSON (Object objType) where | |
87 | parseJSON (Ae.Object o) = | |
88 | Object <$> fmap go (o .:? "attachments") | |
89 | <*> o .:? "author" | |
90 | <*> o .:? "content" | |
91 | <*> o .:? "displayName" | |
92 | <*> fmap go (o .:? "downstreamDuplicates") | |
93 | <*> o .:? "id" | |
94 | <*> o .:? "image" | |
95 | <*> o .:? "objectType" | |
96 | <*> o .:? "published" | |
97 | <*> o .:? "summary" | |
98 | <*> o .:? "updated" | |
99 | <*> fmap go (o .:? "upstreamDuplicates") | |
100 | <*> o .:? "url" | |
101 | <*> pure rest | |
102 | where rest = HM.toList (foldr HM.delete o objectFields) | |
103 | go :: Maybe [a] -> [a] | |
104 | go Nothing = [] | |
105 | go (Just xs) = xs | |
106 | ||
107 | instance ToJSON objType => ToJSON (Object objType) where | |
108 | toJSON obj = object (attrs ++ _oRest obj) | |
109 | where attrs = catMaybes | |
110 | [ "attachments" .=! _oAttachments obj | |
111 | , "author" .=? _oAuthor obj | |
112 | , "content" .=? _oContent obj | |
113 | , "displayName" .=? _oDisplayName obj | |
114 | , "downstreamDuplicates" .=! _oDownstreamDuplicates obj | |
115 | , "id" .=? _oId obj | |
116 | , "image" .=? _oImage obj | |
117 | , "objectType" .=? _oObjectType obj | |
118 | , "published" .=? _oPublished obj | |
119 | , "summary" .=? _oSummary obj | |
120 | , "updated" .=? _oUpdated obj | |
121 | , "upstreamDuplicates" .=! _oUpstreamDuplicates obj | |
122 | , "url" .=? _oURL obj | |
123 | ] | |
124 | (.=?) :: ToJSON a => Text -> Maybe a -> Maybe (Text, Value) | |
125 | x .=? Just y = Just (x, toJSON y) | |
126 | _ .=? Nothing = Nothing | |
127 | infix 1 .=? | |
128 | (.=!) :: ToJSON a => Text -> [a] -> Maybe (Text, Value) | |
129 | _ .=! [] = Nothing | |
130 | x .=! ys = Just (x, toJSON ys) | |
131 | infix 1 .=! | |
132 | ||
133 | emptyObject :: Object objType | |
134 | emptyObject = Object | |
135 | { _oAttachments = [] | |
136 | , _oAuthor = Nothing | |
137 | , _oContent = Nothing | |
138 | , _oDisplayName = Nothing | |
139 | , _oDownstreamDuplicates = [] | |
140 | , _oId = Nothing | |
141 | , _oImage = Nothing | |
142 | , _oObjectType = Nothing | |
143 | , _oPublished = Nothing | |
144 | , _oSummary = Nothing | |
145 | , _oUpdated = Nothing | |
146 | , _oUpstreamDuplicates = [] | |
147 | , _oURL = Nothing | |
148 | , _oRest = [] | |
149 | } | |
150 | ||
151 | data Activity verb objType = Activity | |
152 | { _acActor :: Object objType | |
153 | , _acContent :: Maybe Text | |
154 | , _acGenerator :: Maybe (Object objType) | |
155 | , _acIcon :: Maybe MediaLink | |
156 | , _acId :: Maybe URI | |
157 | , _acPublished :: DateTime | |
158 | , _acProvider :: Object objType | |
159 | , _acTarget :: Maybe (Object objType) | |
160 | , _acTitle :: Maybe Text | |
161 | , _acUpdated :: Maybe DateTime | |
162 | , _acURL :: Maybe URI | |
163 | , _acVerb :: Maybe verb | |
164 | } deriving (Eq, Show) | |
165 | ||
166 | makeLenses ''Activity | |
167 | deriveJSON (commonOpts "_ac") ''Activity | |
168 | ||
169 | makeMinimalActivity :: Object objType -> DateTime -> Object objType | |
170 | -> Activity verb objType | |
171 | makeMinimalActivity actor published provider = Activity | |
172 | { _acActor = actor | |
173 | , _acContent = Nothing | |
174 | , _acGenerator = Nothing | |
175 | , _acIcon = Nothing | |
176 | , _acId = Nothing | |
177 | , _acPublished = published | |
178 | , _acProvider = provider | |
179 | , _acTarget = Nothing | |
180 | , _acTitle = Nothing | |
181 | , _acUpdated = Nothing | |
182 | , _acURL = Nothing | |
183 | , _acVerb = Nothing | |
184 | } | |
185 | ||
186 | data Collection objType = Collection | |
187 | { _cTotalItems :: Maybe Int | |
188 | , _cItems :: [Object objType] | |
189 | , _cURL :: Maybe URI | |
190 | } deriving (Eq, Show) | |
191 | ||
192 | makeLenses ''Collection | |
193 | deriveJSON (commonOpts "_c") ''Collection | |
194 | ||
195 | makeCollection :: [Object objType] -> Maybe URI -> Collection objType | |
90 | ||
91 | import Codec.ActivityStream.Internal (ensure) | |
92 | import Codec.ActivityStream.LensInternal | |
93 | ||
94 | -- | Some types of objects may have an alternative visual representation in | |
95 | -- the form of an image, video or embedded HTML fragments. A 'MediaLink' | |
96 | -- represents a hyperlink to such resources. | |
97 | newtype MediaLink = MediaLink { fromMediaLink :: A.Object } deriving (Eq, Show) | |
98 | ||
99 | instance FromJSON MediaLink where | |
100 | parseJSON (A.Object o) = do | |
101 | ensure "MediaLink" o ["url"] | |
102 | return (MediaLink o) | |
103 | parseJSON _ = fail "MediaLink not an object" | |
104 | ||
105 | instance ToJSON MediaLink where | |
106 | toJSON (MediaLink o) = A.Object o | |
107 | ||
108 | -- | Access the underlying JSON object that represents a Media Link | |
109 | mlRest :: Lens' MediaLink A.Object | |
110 | mlRest = makeLens fromMediaLink (\ o' m -> m { fromMediaLink = o' }) | |
111 | ||
112 | -- | A hint to the consumer about the length, in seconds, of the media | |
113 | -- resource identified by the url property. A media link MAY contain | |
114 | -- a "duration" property when the target resource is a time-based | |
115 | -- media item such as an audio or video. | |
116 | mlDuration :: Lens' MediaLink (Maybe Int) | |
117 | mlDuration = makeAesonLensMb "duration" mlRest | |
118 | ||
119 | -- | A hint to the consumer about the height, in pixels, of the media | |
120 | -- resource identified by the url property. A media link MAY contain | |
121 | -- a @height@ property when the target resource is a visual media item | |
122 | -- such as an image, video or embeddable HTML page. | |
123 | mlHeight :: Lens' MediaLink (Maybe Int) | |
124 | mlHeight = makeAesonLensMb "height" mlRest | |
125 | ||
126 | -- | A hint to the consumer about the width, in pixels, of the media | |
127 | -- resource identified by the url property. A media link MAY contain | |
128 | -- a @width@ property when the target resource is a visual media item | |
129 | -- such as an image, video or embeddable HTML page. | |
130 | mlWidth :: Lens' MediaLink (Maybe Int) | |
131 | mlWidth = makeAesonLensMb "width" mlRest | |
132 | ||
133 | -- | The IRI of the media resource being linked. A media link MUST have a | |
134 | -- @url@ property. | |
135 | mlURL :: Lens' MediaLink Text | |
136 | mlURL = makeAesonLens "url" mlRest | |
137 | ||
138 | -- | Create a @MediaLink@ with just a @url@ property, and all other | |
139 | -- properties undefined. | |
140 | makeMediaLink :: Text -> MediaLink | |
141 | makeMediaLink url = MediaLink (HM.insert "url" (toJSON url) HM.empty) | |
142 | ||
143 | -- | Within the specification, an 'Object' is a thing, real or | |
144 | -- imaginary, which participates in an activity. It may be the | |
145 | -- entity performing the activity, or the entity on which the | |
146 | -- activity was performed. An object consists of properties | |
147 | -- defined below. Certain object types may | |
148 | -- further refine the meaning of these properties, or they may | |
149 | -- define additional properties. | |
150 | -- | |
151 | -- To maintain this flexibility in the Haskell environment, an | |
152 | -- 'Object' is an opaque wrapper over an underlying JSON value, | |
153 | -- and the 'oRest' accessor can be used to access that underlying | |
154 | -- value. | |
155 | ||
156 | newtype Object = Object { fromObject :: A.Object } deriving (Eq, Show) | |
157 | ||
158 | instance FromJSON Object where | |
159 | parseJSON (A.Object o) = return (Object o) | |
160 | parseJSON _ = fail "Object not an object" | |
161 | ||
162 | instance ToJSON Object where | |
163 | toJSON (Object o) = A.Object o | |
164 | ||
165 | -- | Access the underlying JSON object that represents an 'Object' | |
166 | oRest :: Lens' Object A.Object | |
167 | oRest = makeLens fromObject (\ o' m -> m { fromObject = o' }) | |
168 | ||
169 | -- | A collection of one or more additional, associated objects, similar | |
170 | -- to the concept of attached files in an email message. An object MAY | |
171 | -- have an attachments property whose value is a JSON Array of 'Object's. | |
172 | oAttachments :: Lens' Object (Maybe [Object]) | |
173 | oAttachments = makeAesonLensMb "attachments" oRest | |
174 | ||
175 | -- | Describes the entity that created or authored the object. An object | |
176 | -- MAY contain a single author property whose value is an 'Object' of any | |
177 | -- type. Note that the author field identifies the entity that created | |
178 | -- the object and does not necessarily identify the entity that | |
179 | -- published the object. For instance, it may be the case that an | |
180 | -- object created by one person is posted and published to a system by | |
181 | -- an entirely different entity. | |
182 | oAuthor :: Lens' Object (Maybe Object) | |
183 | oAuthor = makeAesonLensMb "author" oRest | |
184 | ||
185 | -- | Natural-language description of the object encoded as a single JSON | |
186 | -- String containing HTML markup. Visual elements such as thumbnail | |
187 | -- images MAY be included. An object MAY contain a @content@ property. | |
188 | oContent :: Lens' Object (Maybe Text) | |
189 | oContent = makeAesonLensMb "content" oRest | |
190 | ||
191 | -- | A natural-language, human-readable and plain-text name for the | |
192 | -- object. HTML markup MUST NOT be included. An object MAY contain | |
193 | -- a @displayName@ property. If the object does not specify an @objectType@ | |
194 | -- property, the object SHOULD specify a @displayName@. | |
195 | oDisplayName :: Lens' Object (Maybe Text) | |
196 | oDisplayName = makeAesonLensMb "displayName" oRest | |
197 | ||
198 | -- | A JSON Array of one or more absolute IRI's | |
199 | -- <http://www.ietf.org/rfc/rfc3987.txt [RFC3987]> identifying | |
200 | -- objects that duplicate this object's content. An object SHOULD | |
201 | -- contain a @downstreamDuplicates@ property when there are known objects, | |
202 | -- possibly in a different system, that duplicate the content in this | |
203 | -- object. This MAY be used as a hint for consumers to use when | |
204 | -- resolving duplicates between objects received from different sources. | |
205 | oDownstreamDuplicates :: Lens' Object (Maybe [Text]) | |
206 | oDownstreamDuplicates = makeAesonLensMb "downstreamDuplicates" oRest | |
207 | ||
208 | -- | Provides a permanent, universally unique identifier for the object in | |
209 | -- the form of an absolute IRI | |
210 | -- <http://www.ietf.org/rfc/rfc3987.txt [RFC3987]>. An | |
211 | -- object SHOULD contain a single @id@ property. If an object does not | |
212 | -- contain an @id@ property, consumers MAY use the value of the @url@ | |
213 | -- property as a less-reliable, non-unique identifier. | |
214 | ||
215 | oId :: Lens' Object (Maybe Text) | |
216 | oId = makeAesonLensMb "id" oRest | |
217 | ||
218 | -- | Description of a resource providing a visual representation of the | |
219 | -- object, intended for human consumption. An object MAY contain an | |
220 | -- @image@ property whose value is a 'MediaLink'. | |
221 | oImage :: Lens' Object (Maybe MediaLink) | |
222 | oImage = makeAesonLensMb "image" oRest | |
223 | ||
224 | -- | Identifies the type of object. An object MAY contain an @objectType@ | |
225 | -- property whose value is a JSON String that is non-empty and matches | |
226 | -- either the "isegment-nz-nc" or the \"IRI\" production in | |
227 | -- <http://www.ietf.org/rfc/rfc3987.txt [RFC3987]>. Note | |
228 | -- that the use of a relative reference other than a simple name is | |
229 | -- not allowed. If no @objectType@ property is contained, the object has | |
230 | -- no specific type. | |
231 | oObjectType :: (FromJSON o, ToJSON o) => Lens' Object (Maybe o) | |
232 | oObjectType = makeAesonLensMb "objectType" oRest | |
233 | ||
234 | -- | The date and time at which the object was published. An object MAY | |
235 | -- contain a @published@ property. | |
236 | oPublished :: Lens' Object (Maybe DateTime) | |
237 | oPublished = makeAesonLensMb "published" oRest | |
238 | ||
239 | -- | Natural-language summarization of the object encoded as a single | |
240 | -- JSON String containing HTML markup. Visual elements such as thumbnail | |
241 | -- images MAY be included. An activity MAY contain a @summary@ property. | |
242 | oSummary :: Lens' Object (Maybe Text) | |
243 | oSummary = makeAesonLensMb "summary" oRest | |
244 | ||
245 | -- | The date and time at which a previously published object has been | |
246 | -- modified. An Object MAY contain an @updated@ property. | |
247 | oUpdated :: Lens' Object (Maybe DateTime) | |
248 | oUpdated = makeAesonLensMb "updated" oRest | |
249 | ||
250 | -- | A JSON Array of one or more absolute IRI's | |
251 | -- <http://www.ietf.org/rfc/rfc3987.txt [RFC3987]> identifying | |
252 | -- objects that duplicate this object's content. An object SHOULD contain | |
253 | -- an @upstreamDuplicates@ property when a publisher is knowingly | |
254 | -- duplicating with a new ID the content from another object. This MAY be | |
255 | -- used as a hint for consumers to use when resolving duplicates between | |
256 | -- objects received from different sources. | |
257 | oUpstreamDuplicates :: Lens' Object (Maybe [Text]) | |
258 | oUpstreamDuplicates = makeAesonLensMb "upstreamDuplicates" oRest | |
259 | ||
260 | -- | An IRI <http://www.ietf.org/rfc/rfc3987.txt [RFC3987]> | |
261 | -- identifying a resource providing an HTML representation of the | |
262 | -- object. An object MAY contain a url property | |
263 | oURL :: Lens' Object (Maybe Text) | |
264 | oURL = makeAesonLensMb "url" oRest | |
265 | ||
266 | -- | Create an @Object@ with no fields. | |
267 | emptyObject :: Object | |
268 | emptyObject = Object HM.empty | |
269 | ||
270 | -- | In its simplest form, an 'Activity' consists of an @actor@, a @verb@, an | |
271 | -- @object@, and a @target@. It tells the story of a person performing an | |
272 | -- action on or with an object -- "Geraldine posted a photo to her | |
273 | -- album" or "John shared a video". In most cases these components | |
274 | -- will be explicit, but they may also be implied. | |
275 | ||
276 | newtype Activity = Activity { fromActivity :: A.Object } deriving (Eq, Show) | |
277 | ||
278 | instance FromJSON Activity where | |
279 | parseJSON (A.Object o) = do | |
280 | ensure "Activity" o ["published", "provider"] | |
281 | return (Activity o) | |
282 | parseJSON _ = fail "\"Activity\" not an object" | |
283 | ||
284 | instance ToJSON Activity where | |
285 | toJSON (Activity o) = A.Object o | |
286 | ||
287 | -- | Access the underlying JSON object that represents an 'Activity' | |
288 | acRest :: Lens' Activity A.Object | |
289 | acRest = makeLens fromActivity (\ o' m -> m { fromActivity = o' }) | |
290 | ||
291 | -- | Describes the entity that performed the activity. An activity MUST | |
292 | -- contain one @actor@ property whose value is a single 'Object'. | |
293 | acActor :: Lens' Activity Object | |
294 | acActor = makeAesonLens "actor" acRest | |
295 | ||
296 | -- | Natural-language description of the activity encoded as a single | |
297 | -- JSON String containing HTML markup. Visual elements such as | |
298 | -- thumbnail images MAY be included. An activity MAY contain a | |
299 | -- @content@ property. | |
300 | acContent :: Lens' Activity (Maybe Text) | |
301 | acContent = makeAesonLensMb "content" acRest | |
302 | ||
303 | -- | Describes the application that generated the activity. An activity | |
304 | -- MAY contain a @generator@ property whose value is a single 'Object'. | |
305 | acGenerator :: Lens' Activity (Maybe Object) | |
306 | acGenerator = makeAesonLens "generator" acRest | |
307 | ||
308 | -- | Description of a resource providing a visual representation of the | |
309 | -- object, intended for human consumption. The image SHOULD have an | |
310 | -- aspect ratio of one (horizontal) to one (vertical) and SHOULD be | |
311 | -- suitable for presentation at a small size. An activity MAY have | |
312 | -- an @icon@ property. | |
313 | acIcon :: Lens' Activity (Maybe MediaLink) | |
314 | acIcon = makeAesonLensMb "icon" acRest | |
315 | ||
316 | -- | Provides a permanent, universally unique identifier for the activity | |
317 | -- in the form of an absolute IRI | |
318 | -- <http://www.ietf.org/rfc/rfc3987.txt [RFC3987]>. An | |
319 | -- activity SHOULD contain a single @id@ property. If an activity does | |
320 | -- not contain an @id@ property, consumers MAY use the value of the | |
321 | -- @url@ property as a less-reliable, non-unique identifier. | |
322 | acId :: Lens' Activity (Maybe Text) | |
323 | acId = makeAesonLensMb "id" acRest | |
324 | ||
325 | -- | Describes the primary object of the activity. For instance, in the | |
326 | -- activity, "John saved a movie to his wishlist", the object of the | |
327 | -- activity is "movie". An activity SHOULD contain an @object@ property | |
328 | -- whose value is a single 'Object'. If the @object@ property is not | |
329 | -- contained, the primary object of the activity MAY be implied by | |
330 | -- context. | |
331 | acObject :: Lens' Activity (Maybe Object) | |
332 | acObject = makeAesonLensMb "object" acRest | |
333 | ||
334 | -- | The date and time at which the activity was published. An activity | |
335 | -- MUST contain a @published@ property. | |
336 | acPublished :: Lens' Activity DateTime | |
337 | acPublished = makeAesonLens "published" acRest | |
338 | ||
339 | -- | Describes the application that published the activity. Note that this | |
340 | -- is not necessarily the same entity that generated the activity. An | |
341 | -- activity MAY contain a @provider@ property whose value is a | |
342 | -- single 'Object'. | |
343 | acProvider :: Lens' Activity (Maybe Object) | |
344 | acProvider = makeAesonLensMb "provider" acRest | |
345 | ||
346 | -- | Describes the target of the activity. The precise meaning of the | |
347 | -- activity's target is dependent on the activities verb, but will | |
348 | -- often be the object the English preposition "to". For instance, in | |
349 | -- the activity, "John saved a movie to his wishlist", the target of | |
350 | -- the activity is "wishlist". The activity target MUST NOT be used | |
351 | -- to identity an indirect object that is not a target of the | |
352 | -- activity. An activity MAY contain a @target@ property whose value | |
353 | -- is a single 'Object'. | |
354 | acTarget :: Lens' Activity (Maybe Object) | |
355 | acTarget = makeAesonLensMb "target" acRest | |
356 | ||
357 | -- | Natural-language title or headline for the activity encoded as a | |
358 | -- single JSON String containing HTML markup. An activity MAY contain | |
359 | -- a @title@ property. | |
360 | acTitle :: Lens' Activity (Maybe Text) | |
361 | acTitle = makeAesonLensMb "title" acRest | |
362 | ||
363 | -- | The date and time at which a previously published activity has | |
364 | -- been modified. An Activity MAY contain an @updated@ property. | |
365 | acUpdated :: Lens' Activity (Maybe DateTime) | |
366 | acUpdated = makeAesonLensMb "updated" acRest | |
367 | ||
368 | -- | An IRI <http://www.ietf.org/rfc/rfc3987.txt RFC3987> | |
369 | -- identifying a resource providing an HTML representation of the | |
370 | -- activity. An activity MAY contain a @url@ property. | |
371 | acURL :: Lens' Activity (Maybe Text) | |
372 | acURL = makeAesonLensMb "url" acRest | |
373 | ||
374 | -- | Identifies the action that the activity describes. An activity SHOULD | |
375 | -- contain a verb property whose value is a JSON String that is | |
376 | -- non-empty and matches either the \"isegment-nz-nc\" or the | |
377 | -- \"IRI\" production in <http://www.ietf.org/rfc/rfc3987.txt [RFC3987]>. | |
378 | -- Note that the use of a relative | |
379 | -- reference other than a simple name is not allowed. If the @verb@ is | |
380 | -- not specified, or if the value is null, the @verb@ is | |
381 | -- assumed to be \"post\". | |
382 | acVerb :: (FromJSON v, ToJSON v) => Lens' Activity (Maybe v) | |
383 | acVerb = makeAesonLensMb "verb" acRest | |
384 | ||
385 | -- | Create an @Activity@ with an @actor@, @published@, and | |
386 | -- @provider@ property. | |
387 | makeActivity :: Object -> DateTime -> Activity | |
388 | makeActivity actor published = Activity | |
389 | $ HM.insert "actor" (toJSON actor) | |
390 | $ HM.insert "published" (toJSON published) | |
391 | $ HM.empty | |
392 | ||
393 | -- | JSON Activity Streams 1.0 specificies that an @Activity@ may be used as an | |
394 | -- @Object@. In such a case, the object may have fields permitted on either an | |
395 | -- @Activity@ or an @Object@ | |
396 | asObject :: Activity -> Object | |
397 | asObject act = Object (fromActivity act) | |
398 | ||
399 | -- | A "collection" is a generic list of 'Object's of any object type. | |
400 | -- The @objectType@ of each item in the collection MAY be omitted if | |
401 | -- the type of object can be established through context. The collection | |
402 | -- is used primarily as the root of an Activity Streams document as described | |
403 | -- in Section 4, | |
404 | -- but can be used as the value of extension properties in a variety of | |
405 | -- situations. | |
406 | ||
407 | newtype Collection = Collection { fromCollection :: A.Object } deriving (Eq, Show) | |
408 | ||
409 | instance FromJSON Collection where | |
410 | parseJSON (A.Object o) = return (Collection o) | |
411 | parseJSON _ = fail "\"Collection\" not an object" | |
412 | ||
413 | instance ToJSON Collection where | |
414 | toJSON (Collection o) = A.Object o | |
415 | ||
416 | -- | Access the underlying JSON object that represents a 'Collection' | |
417 | cRest :: Lens' Collection A.Object | |
418 | cRest = makeLens fromCollection (\ o' m -> m { fromCollection = o' }) | |
419 | ||
420 | -- | Non-negative integer specifying the total number of activities | |
421 | -- within the stream. The Stream serialization MAY contain a | |
422 | -- @totalItems@ property. (NOTE: there is a typo in the original | |
423 | -- specification, in which it inconsistently refers to this as | |
424 | -- either @totalItems@ or @count@.) | |
425 | cTotalItems :: Lens' Collection (Maybe Int) | |
426 | cTotalItems = makeAesonLensMb "totalItems" cRest | |
427 | ||
428 | -- | An array containing a listing of 'Object's of any object type. | |
429 | -- If used in combination with the @url@ property, the @items@ array | |
430 | -- can be used to provide a subset of the objects that may be | |
431 | -- found in the resource identified by the @url@. | |
432 | cItems :: Lens' Collection (Maybe [Object]) | |
433 | cItems = makeAesonLensMb "items" cRest | |
434 | ||
435 | -- | An IRI <http://activitystrea.ms/specs/json/1.0/#RFC3987 [RFC3987]> | |
436 | -- referencing a JSON document containing the full | |
437 | -- listing of objects in the collection. | |
438 | cURL :: Lens' Collection (Maybe Text) | |
439 | cURL = makeAesonLensMb "url" cRest | |
440 | ||
441 | -- | Create a @Collection@ with an @items@ and a @url@ property | |
442 | -- and fill in the corresponding @totalItems@ field with the | |
443 | -- length of the @items@ array. | |
444 | makeCollection :: [Object] -> Text -> Collection | |
196 | 445 | makeCollection objs url = Collection |
197 | { _cTotalItems = Just (length objs) | |
198 | , _cItems = objs | |
199 | , _cURL = url | |
200 | } | |
446 | $ HM.insert "totalItems" (toJSON (length objs)) | |
447 | $ HM.insert "items" (toJSON objs) | |
448 | $ HM.insert "url" (toJSON url) | |
449 | $ HM.empty |
1 | {-# LANGUAGE Rank2Types #-} | |
2 | {-# LANGUAGE OverloadedStrings #-} | |
1 | 3 | {-# LANGUAGE TemplateHaskell #-} |
2 | 4 | |
3 | module Codec.ActivityStream.Schema where | |
4 | ||
5 | import Data.Aeson hiding (Object) | |
6 | import Data.Aeson.TH | |
7 | import Data.DateTime | |
8 | import Data.Text (Text) | |
5 | {-| | |
6 | Module : Codec.ActivityStream.Schema | |
7 | Description : An interface to the Activity Streams Base Schema | |
8 | Copyright : (c) Getty Ritter, 2014 | |
9 | Maintainer : gdritter@galois.com | |
10 | ||
11 | This is an interface to the extended ActivityStreams schema which defines | |
12 | an extensive set of @verb@ values, additional @objectType@ values, and a | |
13 | set of extended properties for 'Object's. | |
14 | ||
15 | Most of the inline documentation is drawn directly from the | |
16 | <https://github.com/activitystreams/activity-schema/blob/master/activity-schema.md Activity Base Schema draft> | |
17 | specification, with minor modifications | |
18 | to refer to the corresponding data types in this module and to clarify | |
19 | certain aspects. This is not an approved draft, and as such may be | |
20 | subject to changes which will be reflected in this module. In contrast to | |
21 | "Codec.ActivityStream", the API in this module makes | |
22 | __no guarantees about long-term stability__. | |
23 | -} | |
24 | ||
25 | module Codec.ActivityStream.Schema | |
26 | ( module Codec.ActivityStream | |
27 | -- * Verbs | |
28 | , SchemaVerb(..) | |
29 | -- * Object Types | |
30 | , SchemaObjectType(..) | |
31 | -- ** Audio/Video | |
32 | , avEmbedCode | |
33 | , avStream | |
34 | -- ** Binary | |
35 | , bnCompression | |
36 | , bnData | |
37 | , bnFileUrl | |
38 | , bnLength | |
39 | , bnMd5 | |
40 | , bnMimeType | |
41 | -- ** Event | |
42 | , evAttendedBy | |
43 | , evAttending | |
44 | , evEndTime | |
45 | , evInvited | |
46 | , evMaybeAttending | |
47 | , evNotAttendedBy | |
48 | , evNotAttending | |
49 | , evStartTime | |
50 | -- ** Issue | |
51 | , isTypes | |
52 | -- ** Permission | |
53 | , pmScope | |
54 | , pmActions | |
55 | -- ** Place | |
56 | , plPosition | |
57 | , plAddress | |
58 | -- *** PlacePosition | |
59 | , PlacePosition | |
60 | -- *** PlaceAddress | |
61 | , PlaceAddress | |
62 | -- ** Role/Group | |
63 | , rlMembers | |
64 | -- ** Task | |
65 | , tsActor | |
66 | , tsBy | |
67 | , tsObject | |
68 | , tsPrerequisites | |
69 | , tsRequired | |
70 | , tsSupersedes | |
71 | , tsVerb | |
72 | -- * Basic Extension Properties | |
73 | , acContext | |
74 | , getLocation | |
75 | , oMood | |
76 | , oRating | |
77 | , acResult | |
78 | , getSource | |
79 | , getStartTime | |
80 | , getEndTime | |
81 | , oTags | |
82 | -- * Mood | |
83 | , Mood | |
84 | , moodRest | |
85 | , moodDisplayName | |
86 | , moodImage | |
87 | ) where | |
88 | ||
89 | import qualified Data.Aeson as Aeson | |
90 | import Data.Aeson.TH (deriveJSON) | |
91 | import Data.DateTime (DateTime) | |
92 | import Data.Aeson ( FromJSON(..), ToJSON(..) ) | |
93 | import qualified Data.HashMap.Strict as HM | |
94 | import Data.Text (Text) | |
9 | 95 | |
10 | 96 | import Codec.ActivityStream.Internal |
11 |
import Codec.ActivityStream. |
|
97 | import Codec.ActivityStream.LensInternal | |
98 | import Codec.ActivityStream | |
12 | 99 | |
13 | 100 | -- | The ActivityStreams Base Schema specification defines the |
14 | 101 | -- following core verbs in addition to the default post verb that is |
547 | 634 | |
548 | 635 | deriveJSON (commonOptsCC "") ''SchemaObjectType |
549 | 636 | |
550 | type SchemaObject = Object SchemaObjectType | |
551 | type SchemaCollection = Collection SchemaObjectType | |
552 | ||
553 | data AVObj = AVObj | |
554 | { avEmbedCode :: Maybe Text | |
555 | , avStream :: Maybe MediaLink | |
556 | , avRest :: SchemaObject | |
557 | } deriving (Eq, Show) | |
558 | ||
559 | data BinaryObj = BinaryObj | |
560 | { bnCompression :: Maybe Text | |
561 | , bnData :: Maybe Text | |
562 | , bnFileUrl :: Maybe Text | |
563 | , bnLength :: Maybe Int | |
564 | , bnMd5 :: Maybe Text | |
565 | , bnMimeType :: Maybe Text | |
566 | , bnRest :: SchemaObject | |
567 | } deriving (Eq, Show) | |
568 | ||
569 | data EventObj = EventObj | |
570 | { evAttendedBy :: Maybe SchemaCollection | |
571 | , evAttending :: Maybe SchemaCollection | |
572 | , evEndTime :: Maybe DateTime | |
573 | , evInvited :: Maybe SchemaCollection | |
574 | , evMaybeAttending :: Maybe SchemaCollection | |
575 | , evNotAttendedBy :: Maybe SchemaCollection | |
576 | , evNotAttending :: Maybe SchemaCollection | |
577 | , evStartTime :: Maybe DateTime | |
578 | , evRest :: SchemaObject | |
579 | } deriving (Eq, Show) | |
580 | ||
581 | data IssueObj = IssueObj | |
582 | { isTypes :: Maybe [Text] | |
583 | , isRest :: SchemaObject | |
584 | } deriving (Eq, Show) | |
585 | ||
586 | data PlaceObj = PlaceObj | |
587 | { plPosition :: Maybe PlacePositionObj | |
588 | , plAddress :: Maybe PlaceAddressObj | |
589 | , plRest :: SchemaObject | |
590 | } deriving (Eq, Show) | |
591 | ||
592 | data PlacePositionObj = PlacePositionObj | |
593 | { ppAltitude :: Integer | |
594 | , ppLatitude :: Integer | |
595 | , ppLongitude :: Integer | |
596 | } deriving (Eq, Show) | |
597 | ||
598 | data PlaceAddressObj = PlaceAddressObj | |
599 | { paFormatted :: Text | |
600 | , paStreetAddress :: Text | |
601 | , paLocality :: Text | |
602 | , paRegion :: Text | |
603 | , paPostalCode :: Text | |
604 | , paCountry :: Text | |
605 | } deriving (Eq, Show) | |
606 | ||
607 | data TaskObj = TaskObj | |
608 | { tsActor :: Maybe SchemaObject | |
609 | , tsBy :: Maybe DateTime | |
610 | , tsObject :: Maybe SchemaObject | |
611 | , tsPrerequisites :: Maybe [TaskObj] | |
612 | , tsRequired :: Maybe Bool | |
613 | , tsSupersedes :: Maybe [TaskObj] | |
614 | , tsVerb :: Maybe SchemaVerb | |
615 | , tsRest :: SchemaObject | |
616 | } deriving (Eq, Show) | |
637 | ||
638 | -- audio/video | |
639 | ||
640 | -- | A fragment of HTML markup that, when embedded within another HTML | |
641 | -- page, provides an interactive user-interface for viewing or listening | |
642 | -- to the video or audio stream. | |
643 | avEmbedCode :: Lens' Object (Maybe Text) | |
644 | avEmbedCode = makeAesonLensMb "embedCode" oRest | |
645 | ||
646 | -- | An Activity Streams Media Link to the video or audio content itself. | |
647 | avStream :: Lens' Object (Maybe MediaLink) | |
648 | avStream = makeAesonLensMb "stream" oRest | |
649 | ||
650 | -- binary | |
651 | ||
652 | -- | An optional token identifying a compression algorithm applied to | |
653 | -- the binary data prior to Base64-encoding. Possible algorithms | |
654 | -- are "deflate" and "gzip", respectively indicating the use of | |
655 | -- the compression mechanisms defined by RFC 1951 and RFC 1952. | |
656 | -- Additional compression algorithms MAY be used but are not defined | |
657 | -- by this specification. Note that previous versions of this | |
658 | -- specification allowed for multiple compression algorithms to be | |
659 | -- applied and listed using a comma-separated format. The use of | |
660 | -- multiple compressions is no longer permitted. | |
661 | bnCompression :: Lens' Object (Maybe Text) | |
662 | bnCompression = makeAesonLensMb "compression" oRest | |
663 | ||
664 | -- | The URL-Safe Base64-encoded representation of the binary data | |
665 | bnData :: Lens' Object (Maybe Text) | |
666 | bnData = makeAesonLensMb "data" oRest | |
667 | -- | An optional IRI for the binary data described by this object. | |
668 | bnFileUrl :: Lens' Object (Maybe Text) | |
669 | bnFileUrl = makeAesonLensMb "fileUrl" oRest | |
670 | ||
671 | -- | The total number of unencoded, uncompressed octets contained | |
672 | -- within the "data" field. | |
673 | bnLength :: Lens' Object (Maybe Text) | |
674 | bnLength = makeAesonLensMb "length" oRest | |
675 | ||
676 | -- | An optional MD5 checksum calculated over the unencoded, | |
677 | -- uncompressed octets contained within the "data" field | |
678 | bnMd5 :: Lens' Object (Maybe Text) | |
679 | bnMd5 = makeAesonLensMb "md5" oRest | |
680 | ||
681 | -- | The MIME Media Type of the binary data contained within the object. | |
682 | bnMimeType :: Lens' Object (Maybe Text) | |
683 | bnMimeType = makeAesonLensMb "mimeType" oRest | |
684 | ||
685 | -- event | |
686 | ||
687 | -- | A collection object as defined in Section 3.5 of the JSON | |
688 | -- Activity Streams specification that provides information about | |
689 | -- entities that attended the event. | |
690 | evAttendedBy :: Lens' Object (Maybe Collection) | |
691 | evAttendedBy = makeAesonLensMb "attendedBy" oRest | |
692 | ||
693 | -- | A collection object as defined in Section 3.5 of the JSON | |
694 | -- Activity Streams specification that provides information about | |
695 | -- entities that intend to attend the event. | |
696 | evAttending :: Lens' Object (Maybe Collection) | |
697 | evAttending = makeAesonLensMb "attending" oRest | |
698 | ||
699 | -- | The date and time that the event ends represented as a String | |
700 | -- conforming to the "date-time" production in [RFC3339]. | |
701 | evEndTime :: Lens' Object (Maybe DateTime) | |
702 | evEndTime = makeAesonLensMb "endTime" oRest | |
703 | ||
704 | -- | A collection object as defined in Section 3.5 of the JSON | |
705 | -- Activity Streams specification that provides information about | |
706 | -- entities that have been invited to the event. | |
707 | evInvited :: Lens' Object (Maybe Collection) | |
708 | evInvited = makeAesonLensMb "invited" oRest | |
709 | ||
710 | -- | A collection object as defined in Section 3.5 of the JSON | |
711 | -- Activity Streams specification that provides information about | |
712 | -- entities that possibly may attend the event. | |
713 | evMaybeAttending :: Lens' Object (Maybe Collection) | |
714 | evMaybeAttending = makeAesonLensMb "maybeAttending" oRest | |
715 | ||
716 | -- | A collection object as defined in Section 3.5 of the JSON | |
717 | -- Activity Streams specification that provides information about | |
718 | -- entities that did not attend the event. | |
719 | evNotAttendedBy :: Lens' Object (Maybe Collection) | |
720 | evNotAttendedBy = makeAesonLensMb "notAttendedBy" oRest | |
721 | ||
722 | -- | A collection object as defined in Section 3.5 of the JSON | |
723 | -- Activity Streams specification that provides information about | |
724 | -- entities that do not intend to attend the event. | |
725 | evNotAttending :: Lens' Object (Maybe Collection) | |
726 | evNotAttending = makeAesonLensMb "notAttending" oRest | |
727 | ||
728 | -- | The date and time that the event begins represented as a String | |
729 | -- confirming to the "date-time" production in RFC 3339. | |
730 | evStartTime :: Lens' Object (Maybe DateTime) | |
731 | evStartTime = makeAesonLensMb "startTime" oRest | |
732 | ||
733 | -- issue | |
734 | ||
735 | -- | An array of one or more absolute IRI's that describe the type of | |
736 | -- issue represented by the object. Note that the IRI's are intended | |
737 | -- for use as identifiers and MAY or MAY NOT be dereferenceable. | |
738 | isTypes :: Lens' Object (Maybe [Text]) | |
739 | isTypes = makeAesonLensMb "types" oRest | |
740 | ||
741 | -- permission | |
742 | ||
743 | -- | A single Activity Streams Object, of any objectType, that | |
744 | -- identifies the scope of the permission. For example, if the | |
745 | -- permission objects describes write permissions for a given file, | |
746 | -- the scope property would be a file object describing that file. | |
747 | pmScope :: Lens' Object (Maybe Object) | |
748 | pmScope = makeAesonLensMb "scope" oRest | |
749 | ||
750 | -- | An array of Strings that identify the specific actions associated | |
751 | -- with the permission. The actions are application and scope | |
752 | -- specific. No common, core set of actions is defined by this | |
753 | -- specification. | |
754 | pmActions :: Lens' Object (Maybe [Text]) | |
755 | pmActions = makeAesonLensMb "actions" oRest | |
756 | ||
757 | -- place | |
758 | ||
759 | -- | The latitude, longitude and altitude of the place as a point on | |
760 | -- Earth. Represented as a JSON Object as described below. | |
761 | plPosition :: Lens' Object (Maybe PlacePosition) | |
762 | plPosition = makeAesonLensMb "position" oRest | |
763 | ||
764 | -- | A physical address represented as a JSON object as described below. | |
765 | plAddress :: Lens' Object (Maybe PlaceAddress) | |
766 | plAddress = makeAesonLensMb "address" oRest | |
767 | ||
768 | newtype PlacePosition = PPO { fromPPO :: Aeson.Object } deriving (Eq, Show) | |
769 | ||
770 | instance FromJSON PlacePosition where | |
771 | parseJSON (Aeson.Object o) = do | |
772 | ensure "Position" o | |
773 | ["altitude", "latitude", "longitude"] | |
774 | return (PPO o) | |
775 | parseJSON _ = fail "\"Position\" not an object" | |
776 | ||
777 | instance ToJSON PlacePosition where | |
778 | toJSON = Aeson.Object . fromPPO | |
779 | ||
780 | newtype PlaceAddress = PAO { fromPAO :: Aeson.Object } deriving (Eq, Show) | |
781 | ||
782 | instance FromJSON PlaceAddress where | |
783 | parseJSON (Aeson.Object o) = do | |
784 | ensure "Address" o | |
785 | [ "formatted" | |
786 | , "streetAddress" | |
787 | , "locality" | |
788 | , "postalCode" | |
789 | , "country" | |
790 | ] | |
791 | return (PAO o) | |
792 | parseJSON _ = fail "Address not an object" | |
793 | ||
794 | instance ToJSON PlaceAddress where | |
795 | toJSON = Aeson.Object . fromPAO | |
796 | ||
797 | -- role/group | |
798 | ||
799 | -- | An optional Activity Streams Collection object listing the | |
800 | -- members of a group, or listing the entities assigned to a | |
801 | -- particular role. | |
802 | rlMembers :: Lens' Object (Maybe [Object]) | |
803 | rlMembers = makeAesonLensMb "members" oRest | |
804 | ||
805 | -- Task | |
806 | ||
807 | -- | An Activity Streams Object that provides information about the | |
808 | -- actor that is expected to complete the task. | |
809 | tsActor :: Lens' Object (Maybe Object) | |
810 | tsActor = makeAesonLensMb "actor" oRest | |
811 | ||
812 | -- | A RFC 3339 date-time specifying the date and time by which the | |
813 | -- task is to be completed. | |
814 | tsBy :: Lens' Object (Maybe DateTime) | |
815 | tsBy = makeAesonLensMb "by" oRest | |
816 | ||
817 | -- | An Activity Streams object describing the object of the task. | |
818 | tsObject :: Lens' Object (Maybe Object) | |
819 | tsObject = makeAesonLensMb "object" oRest | |
820 | ||
821 | -- | An Array of other Task objects that are to be completed before | |
822 | -- this task can be completed. | |
823 | tsPrerequisites :: Lens' Object (Maybe [Object]) | |
824 | tsPrerequisites = makeAesonLensMb "prerequisites" oRest | |
825 | ||
826 | -- | A boolean value indicating whether completion of this task is | |
827 | -- considered to be mandatory. | |
828 | tsRequired :: Lens' Object (Maybe Bool) | |
829 | tsRequired = makeAesonLensMb "required" oRest | |
830 | ||
831 | -- | An Array of other Task objects that are superseded by this task object. | |
832 | tsSupersedes :: Lens' Object (Maybe [Object]) | |
833 | tsSupersedes = makeAesonLensMb "supersedes" oRest | |
834 | ||
835 | -- | A string indicating the verb for this task as defined in Section | |
836 | -- 3.2 of [activitystreams]. | |
837 | tsVerb :: Lens' Object (Maybe SchemaVerb) | |
838 | tsVerb = makeAesonLensMb "verb" oRest | |
839 | ||
840 | -- extra properties | |
841 | ||
842 | -- | The additional @context@ property allows an 'Activity' to further | |
843 | -- include information about why a particular action occurred by | |
844 | -- providing details about the context within which a particular | |
845 | -- Activity was performed. The value of the @context@ property is an | |
846 | -- 'Object' of any @objectType@. The meaning of the @context@ property is | |
847 | -- only defined when used within an 'Activity' object. | |
848 | acContext :: Lens' Activity (Maybe Object) | |
849 | acContext = makeAesonLensMb "context" acRest | |
850 | ||
851 | -- | When appearing within an activity, the location data indicates | |
852 | -- the location where the activity occurred. When appearing within an | |
853 | -- object, the location data indicates the location of that object at | |
854 | -- the time the activity occurred. | |
855 | getLocation :: Lens' a Aeson.Object -> Lens' a (Maybe Object) | |
856 | getLocation = makeAesonLensMb "location" | |
857 | ||
858 | -- | Mood describes the mood of the user when the activity was | |
859 | -- performed. This is usually collected via an extra field in the user | |
860 | -- interface used to perform the activity. For the purpose of the | |
861 | -- schema, a mood is a freeform, short mood keyword or phrase along | |
862 | -- with an optional mood icon image. | |
863 | oMood :: Lens' Object (Maybe Mood) | |
864 | oMood = makeAesonLensMb "mood" oRest | |
865 | ||
866 | -- | A rating given as a number between 1.0 and 5.0 inclusive with one | |
867 | -- decimal place of precision. Represented in JSON as a property | |
868 | -- called @rating@ whose value is a JSON number giving the rating. | |
869 | oRating :: Lens' Object (Maybe Double) | |
870 | oRating = makeAesonLensMb "rating" oRest | |
871 | ||
872 | -- | The @result@ provides a description of the result of any particular | |
873 | -- activity. The value of the @result@ property is an Object of any | |
874 | -- objectType. The meaning of the @result@ property is only defined when | |
875 | -- used within an 'Activity' object. | |
876 | acResult :: Lens' Activity (Maybe Object) | |
877 | acResult = makeAesonLensMb "result" acRest | |
878 | ||
879 | -- | The @source@ property provides a reference to the original source of | |
880 | -- an object or activity. The value of the @source@ property is an | |
881 | -- Object of any objectType. | |
882 | -- | |
883 | -- The @source@ property is closely related to | |
884 | -- the @generator@ and @provider@ properties but serves the distinct | |
885 | -- purpose of identifying where the activity or object was originally | |
886 | -- published as opposed to identifying the applications that generated | |
887 | -- or published it. | |
888 | getSource :: Lens' a Aeson.Object -> Lens' a (Maybe Object) | |
889 | getSource = makeAesonLensMb "source" | |
890 | ||
891 | -- | When an long running Activity occurs over a distinct period of | |
892 | -- time, or when an Object represents a long-running process or event, | |
893 | -- the @startTime@ propertiy can be used to specify the | |
894 | -- date and time at which the activity or object begins. | |
895 | -- The values for each are represented as JSON Strings | |
896 | -- conforming to the "date-time" production in RFC3339. | |
897 | getStartTime :: Lens' a Aeson.Object -> Lens' a (Maybe Text) | |
898 | getStartTime = makeAesonLensMb "startTime" | |
899 | ||
900 | -- | When an long running Activity occurs over a distinct period of | |
901 | -- time, or when an Object represents a long-running process or event, | |
902 | -- the @endTime@ propertiy can be used to specify the | |
903 | -- date and time at which the activity or object concludes. | |
904 | -- The values for each are represented as JSON Strings | |
905 | -- conforming to the "date-time" production in RFC3339. | |
906 | getEndTime :: Lens' a Aeson.Object -> Lens' a (Maybe Text) | |
907 | getEndTime = makeAesonLensMb "endTime" | |
908 | ||
909 | -- | A listing of the objects that have been associated with a | |
910 | -- particular object. Represented in JSON using a property named @tags@ | |
911 | -- whose value is an Array of objects. | |
912 | oTags :: Lens' Object (Maybe [Object]) | |
913 | oTags = makeAesonLensMb "tags" oRest | |
914 | ||
915 | -- mood | |
916 | ||
917 | -- | Mood describes the mood of the user when the activity was | |
918 | -- performed. This is usually collected via an extra field in the user | |
919 | -- interface used to perform the activity. For the purpose of this | |
920 | -- schema, a mood is a freeform, short mood keyword or phrase along | |
921 | -- with an optional mood icon image. | |
922 | newtype Mood = Mood { fromMood :: Aeson.Object } deriving (Eq, Show) | |
923 | ||
924 | instance FromJSON Mood where | |
925 | parseJSON (Aeson.Object o) = do | |
926 | ensure "Mood" o ["displayname", "image"] | |
927 | return (Mood o) | |
928 | parseJSON _ = fail "Mood not an object" | |
929 | ||
930 | instance ToJSON Mood where | |
931 | toJSON = Aeson.Object . fromMood | |
932 | ||
933 | -- | Access to the underlying JSON object of a 'Mood' | |
934 | moodRest :: Lens' Mood Aeson.Object | |
935 | moodRest = makeLens fromMood (\ o' m -> m { fromMood = o' }) | |
936 | ||
937 | -- | The natural-language, human-readable and plain-text keyword or | |
938 | -- phrase describing the mood. HTML markup MUST NOT be included. | |
939 | moodDisplayName :: Lens' Mood Text | |
940 | moodDisplayName = makeAesonLens "displayName" moodRest | |
941 | ||
942 | -- | An optional image that provides a visual representation of the mood. | |
943 | moodImage :: Lens' Mood MediaLink | |
944 | moodImage = makeAesonLens "image" moodRest |
1 | 1 | {-# LANGUAGE TemplateHaskell #-} |
2 | ||
3 | {-| | |
4 | Module : Codec.ActivityStream | |
5 | Description : The basic Activity Streams structures | |
6 | Copyright : (c) Getty Ritter, 2014 | |
7 | Maintainer : gdritter@galois.com | |
8 | ||
9 | This is an interface to ActivityStreams that simply wraps an underlying | |
10 | @aeson@ Object, and exposes a set of convenient lenses to access the | |
11 | values inside. If an @aeson@ object appears wrapped in some respective wrapper, | |
12 | it will necessarily contain the obligatory values for that type | |
13 | (e.g. an 'Activity' is guaranteed to have a @published@ date.) | |
14 | ||
15 | Most of the inline documentation is drawn directly from the | |
16 | <http://activitystrea.ms/specs/json/1.0/ JSON Activity Streams 1.0> | |
17 | specification, with minor modifications | |
18 | to refer to the corresponding data types in this module and to clarify | |
19 | certain aspects. | |
20 | -} | |
2 | 21 | |
3 | 22 | module Codec.ActivityStream |
4 | 23 | ( module Codec.ActivityStream.Representation |
1 | -- Initial activitystreams-aeson.cabal generated by cabal init. For | |
2 | -- further documentation, see http://haskell.org/cabal/users-guide/ | |
1 | name: activitystreams-aeson | |
2 | version: 0.2.0.0 | |
3 | synopsis: An interface to the ActivityStreams specification | |
4 | description: An interface to the | |
5 | <http://activitystrea.ms/ Activity Streams> | |
6 | specifications, using an @aeson@-based representation | |
7 | of the underlying ActivityStream structures. | |
3 | 8 | |
4 | name: activitystreams-aeson | |
5 | version: 0.1.0.0 | |
6 | -- synopsis: | |
7 | -- description: | |
9 | An ActivityStream is a representation of social | |
10 | activities in JSON format, using a standard set of | |
11 | structures. The specification is very flexible in | |
12 | allowing most fields to be omitted, while also | |
13 | allowing arbitrary new fields to be created when | |
14 | necessary. This library attempts to maximize | |
15 | type safety while retaining the flexibility present | |
16 | in the specification. | |
8 | 17 | license: BSD3 |
9 | 18 | license-file: LICENSE |
10 | 19 | author: Getty Ritter |
11 | 20 | maintainer: gettylefou@gmail.com |
12 | -- copyright: | |
21 | copyright: (c) 2014 Getty Ritter | |
13 | 22 | category: Codec |
14 | 23 | build-type: Simple |
15 | -- extra-source-files: | |
16 | 24 | cabal-version: >=1.10 |
17 | 25 | |
18 | 26 | library |
19 | exposed-modules: Codec.ActivityStream.Dynamic, | |
20 | Codec.ActivityStream.DynamicSchema, | |
27 | exposed-modules: Codec.ActivityStream | |
28 | Codec.ActivityStream.Schema | |
29 | other-modules: Codec.ActivityStream.Internal, | |
21 | 30 | Codec.ActivityStream.Representation, |
22 | Codec.ActivityStream.Schema, | |
23 | Codec.ActivityStream | |
24 | other-modules: Codec.ActivityStream.Internal, | |
25 | 31 | Codec.ActivityStream.LensInternal |
26 | build-depends: base >=4.7 && <4.8, | |
27 | aeson, | |
28 | text, | |
29 | url, | |
30 | lens, | |
31 | datetime, | |
32 | unordered-containers, | |
33 | network-uri | |
32 | build-depends: base >=4.7 && <4.8, | |
33 | aeson ==0.8.*, | |
34 | text >=1.1, | |
35 | datetime ==0.2.*, | |
36 | unordered-containers >=0.2.5 | |
34 | 37 | default-language: Haskell2010 |