coarnotify 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/lib/coarnotify/client.rb +88 -0
- data/lib/coarnotify/core/activity_streams2.rb +234 -0
- data/lib/coarnotify/core/notify.rb +833 -0
- data/lib/coarnotify/exceptions.rb +106 -0
- data/lib/coarnotify/factory.rb +114 -0
- data/lib/coarnotify/http.rb +148 -0
- data/lib/coarnotify/patterns/accept.rb +50 -0
- data/lib/coarnotify/patterns/announce_endorsement.rb +103 -0
- data/lib/coarnotify/patterns/announce_relationship.rb +82 -0
- data/lib/coarnotify/patterns/announce_review.rb +145 -0
- data/lib/coarnotify/patterns/announce_service_result.rb +145 -0
- data/lib/coarnotify/patterns/reject.rb +51 -0
- data/lib/coarnotify/patterns/request_endorsement.rb +83 -0
- data/lib/coarnotify/patterns/request_review.rb +71 -0
- data/lib/coarnotify/patterns/tentatively_accept.rb +51 -0
- data/lib/coarnotify/patterns/tentatively_reject.rb +40 -0
- data/lib/coarnotify/patterns/undo_offer.rb +48 -0
- data/lib/coarnotify/patterns/unprocessable_notification.rb +42 -0
- data/lib/coarnotify/server.rb +112 -0
- data/lib/coarnotify/validate.rb +256 -0
- data/lib/coarnotify/version.rb +8 -0
- data/lib/coarnotify.rb +78 -0
- metadata +121 -0
|
@@ -0,0 +1,833 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'securerandom'
|
|
4
|
+
require 'set'
|
|
5
|
+
require_relative 'activity_streams2'
|
|
6
|
+
require_relative '../validate'
|
|
7
|
+
require_relative '../exceptions'
|
|
8
|
+
|
|
9
|
+
module Coarnotify
|
|
10
|
+
module Core
|
|
11
|
+
# This module is home to all the core model objects from which the notify patterns extend
|
|
12
|
+
module Notify
|
|
13
|
+
# Namespace for COAR Notify, to be used to construct namespaced properties used in COAR Notify Patterns
|
|
14
|
+
NOTIFY_NAMESPACE = "https://coar-notify.net"
|
|
15
|
+
|
|
16
|
+
# COAR Notify properties used in COAR Notify Patterns
|
|
17
|
+
#
|
|
18
|
+
# Most of these are provided as arrays, where the first element is the property name, and the second element is the namespace.
|
|
19
|
+
# Some are provided as plain strings without namespaces
|
|
20
|
+
#
|
|
21
|
+
# These are suitable to be used as property names in all the property getters/setters in the notify pattern objects
|
|
22
|
+
# and in the validation configuration.
|
|
23
|
+
module NotifyProperties
|
|
24
|
+
# inbox property
|
|
25
|
+
INBOX = ["inbox", NOTIFY_NAMESPACE].freeze
|
|
26
|
+
|
|
27
|
+
# ietf:cite-as property
|
|
28
|
+
CITE_AS = ["ietf:cite-as", NOTIFY_NAMESPACE].freeze
|
|
29
|
+
|
|
30
|
+
# ietf:item property
|
|
31
|
+
ITEM = ["ietf:item", NOTIFY_NAMESPACE].freeze
|
|
32
|
+
|
|
33
|
+
# name property
|
|
34
|
+
NAME = "name"
|
|
35
|
+
|
|
36
|
+
# mediaType property
|
|
37
|
+
MEDIA_TYPE = "mediaType"
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# List of all the COAR Notify types patterns may use.
|
|
41
|
+
#
|
|
42
|
+
# These are in addition to the base Activity Streams types, which are in ActivityStreams2::ActivityStreamsTypes
|
|
43
|
+
module NotifyTypes
|
|
44
|
+
ENDORSEMENT_ACTION = "coar-notify:EndorsementAction"
|
|
45
|
+
INGEST_ACTION = "coar-notify:IngestAction"
|
|
46
|
+
RELATIONSHIP_ACTION = "coar-notify:RelationshipAction"
|
|
47
|
+
REVIEW_ACTION = "coar-notify:ReviewAction"
|
|
48
|
+
UNPROCESSABLE_NOTIFICATION = "coar-notify:UnprocessableNotification"
|
|
49
|
+
|
|
50
|
+
ABOUT_PAGE = "sorg:AboutPage"
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Validation rules for notify patterns
|
|
54
|
+
VALIDATION_RULES = {
|
|
55
|
+
ActivityStreams2::Properties::ID => {
|
|
56
|
+
"default" => proc { |obj, uri| Validate.absolute_uri(obj, uri) },
|
|
57
|
+
"context" => {
|
|
58
|
+
ActivityStreams2::Properties::CONTEXT => {
|
|
59
|
+
"default" => proc { |obj, url| Validate.url(obj, url) }
|
|
60
|
+
},
|
|
61
|
+
ActivityStreams2::Properties::ORIGIN => {
|
|
62
|
+
"default" => proc { |obj, url| Validate.url(obj, url) }
|
|
63
|
+
},
|
|
64
|
+
ActivityStreams2::Properties::TARGET => {
|
|
65
|
+
"default" => proc { |obj, url| Validate.url(obj, url) }
|
|
66
|
+
},
|
|
67
|
+
NotifyProperties::ITEM => {
|
|
68
|
+
"default" => proc { |obj, url| Validate.url(obj, url) }
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
ActivityStreams2::Properties::TYPE => {
|
|
73
|
+
"default" => proc { |obj, value| Validate.type_checker(obj, value) },
|
|
74
|
+
"context" => {
|
|
75
|
+
ActivityStreams2::Properties::ACTOR => {
|
|
76
|
+
"default" => Validate.one_of([
|
|
77
|
+
ActivityStreams2::ActivityStreamsTypes::SERVICE,
|
|
78
|
+
ActivityStreams2::ActivityStreamsTypes::APPLICATION,
|
|
79
|
+
ActivityStreams2::ActivityStreamsTypes::GROUP,
|
|
80
|
+
ActivityStreams2::ActivityStreamsTypes::ORGANIZATION,
|
|
81
|
+
ActivityStreams2::ActivityStreamsTypes::PERSON
|
|
82
|
+
])
|
|
83
|
+
},
|
|
84
|
+
ActivityStreams2::Properties::OBJECT => {
|
|
85
|
+
"default" => Validate.at_least_one_of(ActivityStreams2::ACTIVITY_STREAMS_OBJECTS)
|
|
86
|
+
},
|
|
87
|
+
ActivityStreams2::Properties::CONTEXT => {
|
|
88
|
+
"default" => Validate.at_least_one_of(ActivityStreams2::ACTIVITY_STREAMS_OBJECTS)
|
|
89
|
+
},
|
|
90
|
+
NotifyProperties::ITEM => {
|
|
91
|
+
"default" => Validate.at_least_one_of(ActivityStreams2::ACTIVITY_STREAMS_OBJECTS + [NotifyTypes::ABOUT_PAGE])
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
NotifyProperties::CITE_AS => {
|
|
96
|
+
"default" => proc { |obj, url| Validate.url(obj, url) }
|
|
97
|
+
},
|
|
98
|
+
NotifyProperties::INBOX => {
|
|
99
|
+
"default" => proc { |obj, url| Validate.url(obj, url) }
|
|
100
|
+
},
|
|
101
|
+
ActivityStreams2::Properties::IN_REPLY_TO => {
|
|
102
|
+
"default" => proc { |obj, uri| Validate.absolute_uri(obj, uri) }
|
|
103
|
+
},
|
|
104
|
+
ActivityStreams2::Properties::SUBJECT_TRIPLE => {
|
|
105
|
+
"default" => proc { |obj, uri| Validate.absolute_uri(obj, uri) }
|
|
106
|
+
},
|
|
107
|
+
ActivityStreams2::Properties::OBJECT_TRIPLE => {
|
|
108
|
+
"default" => proc { |obj, uri| Validate.absolute_uri(obj, uri) }
|
|
109
|
+
},
|
|
110
|
+
ActivityStreams2::Properties::RELATIONSHIP_TRIPLE => {
|
|
111
|
+
"default" => proc { |obj, uri| Validate.absolute_uri(obj, uri) }
|
|
112
|
+
}
|
|
113
|
+
}.freeze
|
|
114
|
+
|
|
115
|
+
# Default Validator object for all pattern types
|
|
116
|
+
VALIDATORS = Validate::Validator.new(VALIDATION_RULES)
|
|
117
|
+
|
|
118
|
+
# Base class from which all Notify objects extend.
|
|
119
|
+
#
|
|
120
|
+
# There are two kinds of Notify objects:
|
|
121
|
+
#
|
|
122
|
+
# 1. Patterns, which are the notifications themselves
|
|
123
|
+
# 2. Pattern Parts, which are nested elements in the Patterns, such as objects, contexts, actors, etc
|
|
124
|
+
#
|
|
125
|
+
# This class forms the basis for both of those types, and provides essential services,
|
|
126
|
+
# such as construction, accessors and validation, as well as supporting the essential
|
|
127
|
+
# properties "id" and "type"
|
|
128
|
+
class NotifyBase
|
|
129
|
+
attr_reader :validate_stream_on_construct, :validate_properties, :validators, :properties_by_reference
|
|
130
|
+
|
|
131
|
+
# Base constructor that all subclasses should call
|
|
132
|
+
#
|
|
133
|
+
# @param stream [ActivityStreams2::ActivityStream, Hash] The activity stream object, or a hash from which one can be created
|
|
134
|
+
# @param validate_stream_on_construct [Boolean] should the incoming stream be validated at construction-time
|
|
135
|
+
# @param validate_properties [Boolean] should individual properties be validated as they are set
|
|
136
|
+
# @param validators [Validate::Validator] the validator object for this class and all nested elements
|
|
137
|
+
# @param validation_context [String, Array] the context in which this object is being validated
|
|
138
|
+
# @param properties_by_reference [Boolean] should properties be get and set by reference (the default) or by value
|
|
139
|
+
def initialize(stream: nil, validate_stream_on_construct: true, validate_properties: true,
|
|
140
|
+
validators: nil, validation_context: nil, properties_by_reference: true)
|
|
141
|
+
@validate_stream_on_construct = validate_stream_on_construct
|
|
142
|
+
@validate_properties = validate_properties
|
|
143
|
+
@validators = validators || VALIDATORS
|
|
144
|
+
@validation_context = validation_context
|
|
145
|
+
@properties_by_reference = properties_by_reference
|
|
146
|
+
validate_now = false
|
|
147
|
+
|
|
148
|
+
if stream.nil?
|
|
149
|
+
@stream = ActivityStreams2::ActivityStream.new
|
|
150
|
+
elsif stream.is_a?(Hash)
|
|
151
|
+
validate_now = validate_stream_on_construct
|
|
152
|
+
@stream = ActivityStreams2::ActivityStream.new(stream)
|
|
153
|
+
else
|
|
154
|
+
validate_now = validate_stream_on_construct
|
|
155
|
+
@stream = stream
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
if @stream.get_property(ActivityStreams2::Properties::ID).nil?
|
|
159
|
+
@stream.set_property(ActivityStreams2::Properties::ID, "urn:uuid:#{SecureRandom.hex}")
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
validate if validate_now
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# The underlying ActivityStream object, excluding the JSON-LD @context
|
|
166
|
+
#
|
|
167
|
+
# @return [Hash] the document hash
|
|
168
|
+
def doc
|
|
169
|
+
@stream.doc
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
# The id of the object
|
|
173
|
+
#
|
|
174
|
+
# @return [String] the id
|
|
175
|
+
def id
|
|
176
|
+
get_property(ActivityStreams2::Properties::ID)
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
# Set the id of the object
|
|
180
|
+
#
|
|
181
|
+
# @param value [String] the id to set
|
|
182
|
+
def id=(value)
|
|
183
|
+
set_property(ActivityStreams2::Properties::ID, value)
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
# The type of the object
|
|
187
|
+
#
|
|
188
|
+
# @return [String, Array<String>] the type
|
|
189
|
+
def type
|
|
190
|
+
get_property(ActivityStreams2::Properties::TYPE)
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
# Set the type of the object
|
|
194
|
+
#
|
|
195
|
+
# @param types [String, Array<String>] the type(s) to set
|
|
196
|
+
def type=(types)
|
|
197
|
+
set_property(ActivityStreams2::Properties::TYPE, types)
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
# Generic property getter. It is strongly recommended that all accessors proxy for this method
|
|
201
|
+
# as this enforces by-reference/by-value accessing, and mediates directly with the underlying
|
|
202
|
+
# activity stream object.
|
|
203
|
+
#
|
|
204
|
+
# @param prop_name [String, Array] The property to retrieve
|
|
205
|
+
# @param by_reference [Boolean] Whether to retrieve by_reference or by_value
|
|
206
|
+
# @return [Object] the property value
|
|
207
|
+
def get_property(prop_name, by_reference: nil)
|
|
208
|
+
by_reference = @properties_by_reference if by_reference.nil?
|
|
209
|
+
val = @stream.get_property(prop_name)
|
|
210
|
+
if by_reference
|
|
211
|
+
val
|
|
212
|
+
else
|
|
213
|
+
val.dup rescue val # Deep copy where possible
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
# Generic property setter. It is strongly recommended that all accessors proxy for this method
|
|
218
|
+
# as this enforces by-reference/by-value accessing, and mediates directly with the underlying
|
|
219
|
+
# activity stream object.
|
|
220
|
+
#
|
|
221
|
+
# @param prop_name [String, Array] The property to set
|
|
222
|
+
# @param value [Object] The value to set
|
|
223
|
+
# @param by_reference [Boolean] Whether to set by_reference or by_value
|
|
224
|
+
def set_property(prop_name, value, by_reference: nil)
|
|
225
|
+
by_reference = @properties_by_reference if by_reference.nil?
|
|
226
|
+
validate_property(prop_name, value)
|
|
227
|
+
value = value.dup rescue value unless by_reference # Deep copy where possible
|
|
228
|
+
@stream.set_property(prop_name, value)
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
# Validate the object. This provides the basic validation on id and type.
|
|
232
|
+
# Subclasses should override this method with their own validation, and call this method via super first to ensure
|
|
233
|
+
# the basic properties are validated.
|
|
234
|
+
#
|
|
235
|
+
# @return [Boolean] true or raise a ValidationError if there are errors
|
|
236
|
+
def validate
|
|
237
|
+
ve = ValidationError.new
|
|
238
|
+
|
|
239
|
+
required_and_validate(ve, ActivityStreams2::Properties::ID, id)
|
|
240
|
+
required_and_validate(ve, ActivityStreams2::Properties::TYPE, type)
|
|
241
|
+
|
|
242
|
+
raise ve if ve.has_errors?
|
|
243
|
+
true
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
# Validate a single property. This is used internally by set_property.
|
|
247
|
+
#
|
|
248
|
+
# If the object has validate_properties set to false then that behaviour may be overridden by setting force_validate to true
|
|
249
|
+
#
|
|
250
|
+
# The validator applied to the property will be determined according to the validators property of the object
|
|
251
|
+
# and the validation_context of the object.
|
|
252
|
+
#
|
|
253
|
+
# @param prop_name [String, Array] The property to validate
|
|
254
|
+
# @param value [Object] the value to validate
|
|
255
|
+
# @param force_validate [Boolean] whether to validate anyway, even if property validation is turned off at the object level
|
|
256
|
+
# @param raise_error [Boolean] raise an exception on validation failure, or return a tuple with the result
|
|
257
|
+
# @return [Array] A tuple of whether validation was successful, and the error message if it was not
|
|
258
|
+
def validate_property(prop_name, value, force_validate: false, raise_error: true)
|
|
259
|
+
return [true, ""] if value.nil?
|
|
260
|
+
if @validate_properties || force_validate
|
|
261
|
+
validator = @validators.get(prop_name, @validation_context)
|
|
262
|
+
if validator
|
|
263
|
+
begin
|
|
264
|
+
validator.call(self, value)
|
|
265
|
+
rescue ArgumentError => ve
|
|
266
|
+
if raise_error
|
|
267
|
+
raise ve
|
|
268
|
+
else
|
|
269
|
+
return [false, ve.message]
|
|
270
|
+
end
|
|
271
|
+
end
|
|
272
|
+
end
|
|
273
|
+
end
|
|
274
|
+
[true, ""]
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
# Force validate the property and if an error is found, add it to the validation error
|
|
278
|
+
#
|
|
279
|
+
# @param ve [ValidationError] the validation error to add to
|
|
280
|
+
# @param prop_name [String, Array] the property name
|
|
281
|
+
# @param value [Object] the value to validate
|
|
282
|
+
def register_property_validation_error(ve, prop_name, value)
|
|
283
|
+
success, msg = validate_property(prop_name, value, force_validate: true, raise_error: false)
|
|
284
|
+
ve.add_error(prop_name, msg) unless success
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
# Add a required error to the validation error if the value is nil
|
|
288
|
+
#
|
|
289
|
+
# @param ve [ValidationError] The validation error to which to add the message
|
|
290
|
+
# @param prop_name [String, Array] The property to check
|
|
291
|
+
# @param value [Object] The value
|
|
292
|
+
def required(ve, prop_name, value)
|
|
293
|
+
if value.nil?
|
|
294
|
+
pn = prop_name.is_a?(Array) ? prop_name[0] : prop_name
|
|
295
|
+
ve.add_error(prop_name, Validate::REQUIRED_MESSAGE % pn)
|
|
296
|
+
end
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
# Add a required error to the validation error if the value is nil, and then validate the value if not.
|
|
300
|
+
#
|
|
301
|
+
# Any error messages are added to the ValidationError object
|
|
302
|
+
#
|
|
303
|
+
# @param ve [ValidationError] the validation error to which to add the message
|
|
304
|
+
# @param prop_name [String, Array] The property to check
|
|
305
|
+
# @param value [Object] the value to check
|
|
306
|
+
def required_and_validate(ve, prop_name, value)
|
|
307
|
+
if value.nil?
|
|
308
|
+
pn = prop_name.is_a?(Array) ? prop_name[0] : prop_name
|
|
309
|
+
ve.add_error(prop_name, Validate::REQUIRED_MESSAGE % pn)
|
|
310
|
+
else
|
|
311
|
+
if value.is_a?(NotifyBase)
|
|
312
|
+
begin
|
|
313
|
+
value.validate
|
|
314
|
+
rescue ValidationError => subve
|
|
315
|
+
ve.add_nested_errors(prop_name, subve)
|
|
316
|
+
end
|
|
317
|
+
else
|
|
318
|
+
register_property_validation_error(ve, prop_name, value)
|
|
319
|
+
end
|
|
320
|
+
end
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
# Validate the value if it is not nil, but do not raise a validation error if it is nil
|
|
324
|
+
#
|
|
325
|
+
# @param ve [ValidationError] the validation error to add to
|
|
326
|
+
# @param prop_name [String, Array] the property name
|
|
327
|
+
# @param value [Object] the value to validate
|
|
328
|
+
def optional_and_validate(ve, prop_name, value)
|
|
329
|
+
if value
|
|
330
|
+
if value.is_a?(NotifyBase)
|
|
331
|
+
begin
|
|
332
|
+
value.validate
|
|
333
|
+
rescue ValidationError => subve
|
|
334
|
+
ve.add_nested_errors(prop_name, subve)
|
|
335
|
+
end
|
|
336
|
+
else
|
|
337
|
+
register_property_validation_error(ve, prop_name, value)
|
|
338
|
+
end
|
|
339
|
+
end
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
# Get the notification pattern as JSON-LD
|
|
343
|
+
#
|
|
344
|
+
# @return [Hash] JSON-LD representation of the pattern
|
|
345
|
+
def to_jsonld
|
|
346
|
+
@stream.to_jsonld
|
|
347
|
+
end
|
|
348
|
+
end
|
|
349
|
+
|
|
350
|
+
# Base class for all notification patterns
|
|
351
|
+
class NotifyPattern < NotifyBase
|
|
352
|
+
# The type of the pattern. This should be overridden by subclasses, otherwise defaults to Object
|
|
353
|
+
def self.type_constant
|
|
354
|
+
ActivityStreams2::ActivityStreamsTypes::OBJECT
|
|
355
|
+
end
|
|
356
|
+
|
|
357
|
+
# Constructor for the NotifyPattern
|
|
358
|
+
#
|
|
359
|
+
# This constructor will ensure that the pattern has its mandated type in the type property
|
|
360
|
+
#
|
|
361
|
+
# @param stream [ActivityStreams2::ActivityStream, Hash] The activity stream object, or a hash from which one can be created
|
|
362
|
+
# @param validate_stream_on_construct [Boolean] should the incoming stream be validated at construction-time
|
|
363
|
+
# @param validate_properties [Boolean] should individual properties be validated as they are set
|
|
364
|
+
# @param validators [Validate::Validator] the validator object for this class and all nested elements
|
|
365
|
+
# @param validation_context [String, Array] the context in which this object is being validated
|
|
366
|
+
# @param properties_by_reference [Boolean] should properties be get and set by reference (the default) or by value
|
|
367
|
+
def initialize(stream: nil, validate_stream_on_construct: true, validate_properties: true,
|
|
368
|
+
validators: nil, validation_context: nil, properties_by_reference: true)
|
|
369
|
+
super(stream: stream, validate_stream_on_construct: validate_stream_on_construct,
|
|
370
|
+
validate_properties: validate_properties, validators: validators,
|
|
371
|
+
validation_context: validation_context, properties_by_reference: properties_by_reference)
|
|
372
|
+
ensure_type_contains(self.class.type_constant)
|
|
373
|
+
end
|
|
374
|
+
|
|
375
|
+
# Ensure that the type field contains the given types
|
|
376
|
+
#
|
|
377
|
+
# @param types [String, Array<String>] the types to ensure are present
|
|
378
|
+
def ensure_type_contains(types)
|
|
379
|
+
existing = @stream.get_property(ActivityStreams2::Properties::TYPE)
|
|
380
|
+
if existing.nil?
|
|
381
|
+
set_property(ActivityStreams2::Properties::TYPE, types)
|
|
382
|
+
else
|
|
383
|
+
existing = [existing] unless existing.is_a?(Array)
|
|
384
|
+
types = [types] unless types.is_a?(Array)
|
|
385
|
+
types.each do |t|
|
|
386
|
+
existing << t unless existing.include?(t)
|
|
387
|
+
end
|
|
388
|
+
existing = existing.length == 1 ? existing[0] : existing
|
|
389
|
+
set_property(ActivityStreams2::Properties::TYPE, existing)
|
|
390
|
+
end
|
|
391
|
+
end
|
|
392
|
+
|
|
393
|
+
# Get the origin property of the notification
|
|
394
|
+
#
|
|
395
|
+
# @return [NotifyService, nil] the origin service
|
|
396
|
+
def origin
|
|
397
|
+
o = get_property(ActivityStreams2::Properties::ORIGIN)
|
|
398
|
+
if o
|
|
399
|
+
NotifyService.new(stream: o, validate_stream_on_construct: false,
|
|
400
|
+
validate_properties: @validate_properties, validators: @validators,
|
|
401
|
+
validation_context: ActivityStreams2::Properties::ORIGIN,
|
|
402
|
+
properties_by_reference: @properties_by_reference)
|
|
403
|
+
end
|
|
404
|
+
end
|
|
405
|
+
|
|
406
|
+
# Set the origin property of the notification
|
|
407
|
+
#
|
|
408
|
+
# @param value [NotifyService] the origin service to set
|
|
409
|
+
def origin=(value)
|
|
410
|
+
set_property(ActivityStreams2::Properties::ORIGIN, value.doc)
|
|
411
|
+
end
|
|
412
|
+
|
|
413
|
+
# Get the target property of the notification
|
|
414
|
+
#
|
|
415
|
+
# @return [NotifyService, nil] the target service
|
|
416
|
+
def target
|
|
417
|
+
t = get_property(ActivityStreams2::Properties::TARGET)
|
|
418
|
+
if t
|
|
419
|
+
NotifyService.new(stream: t, validate_stream_on_construct: false,
|
|
420
|
+
validate_properties: @validate_properties, validators: @validators,
|
|
421
|
+
validation_context: ActivityStreams2::Properties::TARGET,
|
|
422
|
+
properties_by_reference: @properties_by_reference)
|
|
423
|
+
end
|
|
424
|
+
end
|
|
425
|
+
|
|
426
|
+
# Set the target property of the notification
|
|
427
|
+
#
|
|
428
|
+
# @param value [NotifyService] the target service to set
|
|
429
|
+
def target=(value)
|
|
430
|
+
set_property(ActivityStreams2::Properties::TARGET, value.doc)
|
|
431
|
+
end
|
|
432
|
+
|
|
433
|
+
# Get the object property of the notification
|
|
434
|
+
#
|
|
435
|
+
# @return [NotifyObject, nil] the object
|
|
436
|
+
def object
|
|
437
|
+
o = get_property(ActivityStreams2::Properties::OBJECT)
|
|
438
|
+
if o
|
|
439
|
+
NotifyObject.new(stream: o, validate_stream_on_construct: false,
|
|
440
|
+
validate_properties: @validate_properties, validators: @validators,
|
|
441
|
+
validation_context: ActivityStreams2::Properties::OBJECT,
|
|
442
|
+
properties_by_reference: @properties_by_reference)
|
|
443
|
+
end
|
|
444
|
+
end
|
|
445
|
+
|
|
446
|
+
# Set the object property of the notification
|
|
447
|
+
#
|
|
448
|
+
# @param value [NotifyObject] the object to set
|
|
449
|
+
def object=(value)
|
|
450
|
+
set_property(ActivityStreams2::Properties::OBJECT, value.doc)
|
|
451
|
+
end
|
|
452
|
+
|
|
453
|
+
# Get the inReplyTo property of the notification
|
|
454
|
+
#
|
|
455
|
+
# @return [String] the inReplyTo value
|
|
456
|
+
def in_reply_to
|
|
457
|
+
get_property(ActivityStreams2::Properties::IN_REPLY_TO)
|
|
458
|
+
end
|
|
459
|
+
|
|
460
|
+
# Set the inReplyTo property of the notification
|
|
461
|
+
#
|
|
462
|
+
# @param value [String] the inReplyTo value to set
|
|
463
|
+
def in_reply_to=(value)
|
|
464
|
+
set_property(ActivityStreams2::Properties::IN_REPLY_TO, value)
|
|
465
|
+
end
|
|
466
|
+
|
|
467
|
+
# Get the actor property of the notification
|
|
468
|
+
#
|
|
469
|
+
# @return [NotifyActor, nil] the actor
|
|
470
|
+
def actor
|
|
471
|
+
a = get_property(ActivityStreams2::Properties::ACTOR)
|
|
472
|
+
if a
|
|
473
|
+
NotifyActor.new(stream: a, validate_stream_on_construct: false,
|
|
474
|
+
validate_properties: @validate_properties, validators: @validators,
|
|
475
|
+
validation_context: ActivityStreams2::Properties::ACTOR,
|
|
476
|
+
properties_by_reference: @properties_by_reference)
|
|
477
|
+
end
|
|
478
|
+
end
|
|
479
|
+
|
|
480
|
+
# Set the actor property of the notification
|
|
481
|
+
#
|
|
482
|
+
# @param value [NotifyActor] the actor to set
|
|
483
|
+
def actor=(value)
|
|
484
|
+
set_property(ActivityStreams2::Properties::ACTOR, value.doc)
|
|
485
|
+
end
|
|
486
|
+
|
|
487
|
+
# Get the context property of the notification
|
|
488
|
+
#
|
|
489
|
+
# @return [NotifyObject, nil] the context
|
|
490
|
+
def context
|
|
491
|
+
c = get_property(ActivityStreams2::Properties::CONTEXT)
|
|
492
|
+
if c
|
|
493
|
+
NotifyObject.new(stream: c, validate_stream_on_construct: false,
|
|
494
|
+
validate_properties: @validate_properties, validators: @validators,
|
|
495
|
+
validation_context: ActivityStreams2::Properties::CONTEXT,
|
|
496
|
+
properties_by_reference: @properties_by_reference)
|
|
497
|
+
end
|
|
498
|
+
end
|
|
499
|
+
|
|
500
|
+
# Set the context property of the notification
|
|
501
|
+
#
|
|
502
|
+
# @param value [NotifyObject] the context to set
|
|
503
|
+
def context=(value)
|
|
504
|
+
set_property(ActivityStreams2::Properties::CONTEXT, value.doc)
|
|
505
|
+
end
|
|
506
|
+
|
|
507
|
+
# Base validator for all notification patterns. This extends the validate function on the superclass.
|
|
508
|
+
#
|
|
509
|
+
# In addition to the base class's constraints, this applies the following validation:
|
|
510
|
+
#
|
|
511
|
+
# * The origin, target and object properties are required and must be valid
|
|
512
|
+
# * The actor inReplyTo and context properties are optional, but if present must be valid
|
|
513
|
+
#
|
|
514
|
+
# @return [Boolean] true if valid, otherwise raises ValidationError
|
|
515
|
+
def validate
|
|
516
|
+
ve = ValidationError.new
|
|
517
|
+
begin
|
|
518
|
+
super
|
|
519
|
+
rescue ValidationError => superve
|
|
520
|
+
ve = superve
|
|
521
|
+
end
|
|
522
|
+
|
|
523
|
+
required_and_validate(ve, ActivityStreams2::Properties::ORIGIN, origin)
|
|
524
|
+
required_and_validate(ve, ActivityStreams2::Properties::TARGET, target)
|
|
525
|
+
required_and_validate(ve, ActivityStreams2::Properties::OBJECT, object)
|
|
526
|
+
optional_and_validate(ve, ActivityStreams2::Properties::ACTOR, actor)
|
|
527
|
+
optional_and_validate(ve, ActivityStreams2::Properties::IN_REPLY_TO, in_reply_to)
|
|
528
|
+
optional_and_validate(ve, ActivityStreams2::Properties::CONTEXT, context)
|
|
529
|
+
|
|
530
|
+
raise ve if ve.has_errors?
|
|
531
|
+
true
|
|
532
|
+
end
|
|
533
|
+
end
|
|
534
|
+
|
|
535
|
+
# Base class for all pattern parts, such as objects, contexts, actors, etc
|
|
536
|
+
#
|
|
537
|
+
# If there is a default type specified, and a type is not given at construction, then
|
|
538
|
+
# the default type will be added
|
|
539
|
+
class NotifyPatternPart < NotifyBase
|
|
540
|
+
# The default type for this object, if none is provided on construction
|
|
541
|
+
def self.default_type
|
|
542
|
+
nil
|
|
543
|
+
end
|
|
544
|
+
|
|
545
|
+
# The list of types that are permissible for this object. If the list is empty, then any type is allowed
|
|
546
|
+
def self.allowed_types
|
|
547
|
+
[]
|
|
548
|
+
end
|
|
549
|
+
|
|
550
|
+
# Constructor for the NotifyPatternPart
|
|
551
|
+
#
|
|
552
|
+
# If there is a default type specified, and a type is not given at construction, then
|
|
553
|
+
# the default type will be added
|
|
554
|
+
#
|
|
555
|
+
# @param stream [ActivityStreams2::ActivityStream, Hash] The activity stream object, or a hash from which one can be created
|
|
556
|
+
# @param validate_stream_on_construct [Boolean] should the incoming stream be validated at construction-time
|
|
557
|
+
# @param validate_properties [Boolean] should individual properties be validated as they are set
|
|
558
|
+
# @param validators [Validate::Validator] the validator object for this class and all nested elements
|
|
559
|
+
# @param validation_context [String, Array] the context in which this object is being validated
|
|
560
|
+
# @param properties_by_reference [Boolean] should properties be get and set by reference (the default) or by value
|
|
561
|
+
def initialize(stream: nil, validate_stream_on_construct: true, validate_properties: true,
|
|
562
|
+
validators: nil, validation_context: nil, properties_by_reference: true)
|
|
563
|
+
super(stream: stream, validate_stream_on_construct: validate_stream_on_construct,
|
|
564
|
+
validate_properties: validate_properties, validators: validators,
|
|
565
|
+
validation_context: validation_context, properties_by_reference: properties_by_reference)
|
|
566
|
+
self.type = self.class.default_type if self.class.default_type && type.nil?
|
|
567
|
+
end
|
|
568
|
+
|
|
569
|
+
# Get the allowed types for this object
|
|
570
|
+
#
|
|
571
|
+
# @return [Array<String>] the allowed types
|
|
572
|
+
def allowed_types
|
|
573
|
+
self.class.allowed_types
|
|
574
|
+
end
|
|
575
|
+
|
|
576
|
+
# Set the type of the object, and validate that it is one of the allowed types if present
|
|
577
|
+
#
|
|
578
|
+
# @param types [String, Array<String>] the type(s) to set
|
|
579
|
+
def type=(types)
|
|
580
|
+
types = [types] unless types.is_a?(Array)
|
|
581
|
+
|
|
582
|
+
if !allowed_types.empty?
|
|
583
|
+
types.each do |t|
|
|
584
|
+
unless allowed_types.include?(t)
|
|
585
|
+
raise ArgumentError, "Type value #{t} is not one of the permitted values"
|
|
586
|
+
end
|
|
587
|
+
end
|
|
588
|
+
end
|
|
589
|
+
|
|
590
|
+
# keep single values as single values, not arrays
|
|
591
|
+
types = types.length == 1 ? types[0] : types
|
|
592
|
+
|
|
593
|
+
set_property(ActivityStreams2::Properties::TYPE, types)
|
|
594
|
+
end
|
|
595
|
+
end
|
|
596
|
+
|
|
597
|
+
# Default class to represent a service in the COAR Notify pattern.
|
|
598
|
+
#
|
|
599
|
+
# Services are used to represent origin and target properties in the notification patterns
|
|
600
|
+
#
|
|
601
|
+
# Specific patterns may need to extend this class to provide their specific behaviours and validation
|
|
602
|
+
class NotifyService < NotifyPatternPart
|
|
603
|
+
# The default type for a service is Service, but the type can be set to any value
|
|
604
|
+
def self.default_type
|
|
605
|
+
ActivityStreams2::ActivityStreamsTypes::SERVICE
|
|
606
|
+
end
|
|
607
|
+
|
|
608
|
+
# Get the inbox property of the service
|
|
609
|
+
#
|
|
610
|
+
# @return [String] the inbox URL
|
|
611
|
+
def inbox
|
|
612
|
+
get_property(NotifyProperties::INBOX)
|
|
613
|
+
end
|
|
614
|
+
|
|
615
|
+
# Set the inbox property of the service
|
|
616
|
+
#
|
|
617
|
+
# @param value [String] the inbox URL to set
|
|
618
|
+
def inbox=(value)
|
|
619
|
+
set_property(NotifyProperties::INBOX, value)
|
|
620
|
+
end
|
|
621
|
+
end
|
|
622
|
+
|
|
623
|
+
# Default class to represent an object in the COAR Notify pattern. Objects can be used for object or context properties
|
|
624
|
+
# in notify patterns
|
|
625
|
+
#
|
|
626
|
+
# Specific patterns may need to extend this class to provide their specific behaviours and validation
|
|
627
|
+
class NotifyObject < NotifyPatternPart
|
|
628
|
+
# Get the ietf:cite-as property of the object
|
|
629
|
+
#
|
|
630
|
+
# @return [String] the cite-as value
|
|
631
|
+
def cite_as
|
|
632
|
+
get_property(NotifyProperties::CITE_AS)
|
|
633
|
+
end
|
|
634
|
+
|
|
635
|
+
# Set the ietf:cite-as property of the object
|
|
636
|
+
#
|
|
637
|
+
# @param value [String] the cite-as value to set
|
|
638
|
+
def cite_as=(value)
|
|
639
|
+
set_property(NotifyProperties::CITE_AS, value)
|
|
640
|
+
end
|
|
641
|
+
|
|
642
|
+
# Get the ietf:item property of the object
|
|
643
|
+
#
|
|
644
|
+
# @return [NotifyItem, nil] the item
|
|
645
|
+
def item
|
|
646
|
+
i = get_property(NotifyProperties::ITEM)
|
|
647
|
+
if i
|
|
648
|
+
NotifyItem.new(stream: i, validate_stream_on_construct: false,
|
|
649
|
+
validate_properties: @validate_properties, validators: @validators,
|
|
650
|
+
validation_context: NotifyProperties::ITEM,
|
|
651
|
+
properties_by_reference: @properties_by_reference)
|
|
652
|
+
end
|
|
653
|
+
end
|
|
654
|
+
|
|
655
|
+
# Set the ietf:item property of the object
|
|
656
|
+
#
|
|
657
|
+
# @param value [NotifyItem] the item to set
|
|
658
|
+
def item=(value)
|
|
659
|
+
set_property(NotifyProperties::ITEM, value)
|
|
660
|
+
end
|
|
661
|
+
|
|
662
|
+
# Get object, relationship and subject properties as a relationship triple
|
|
663
|
+
#
|
|
664
|
+
# @return [Array<String>] array of [object, relationship, subject]
|
|
665
|
+
def triple
|
|
666
|
+
obj = get_property(ActivityStreams2::Properties::OBJECT_TRIPLE)
|
|
667
|
+
rel = get_property(ActivityStreams2::Properties::RELATIONSHIP_TRIPLE)
|
|
668
|
+
subj = get_property(ActivityStreams2::Properties::SUBJECT_TRIPLE)
|
|
669
|
+
[obj, rel, subj]
|
|
670
|
+
end
|
|
671
|
+
|
|
672
|
+
# Set object, relationship and subject properties as a relationship triple
|
|
673
|
+
#
|
|
674
|
+
# @param value [Array<String>] array of [object, relationship, subject]
|
|
675
|
+
def triple=(value)
|
|
676
|
+
obj, rel, subj = value
|
|
677
|
+
set_property(ActivityStreams2::Properties::OBJECT_TRIPLE, obj)
|
|
678
|
+
set_property(ActivityStreams2::Properties::RELATIONSHIP_TRIPLE, rel)
|
|
679
|
+
set_property(ActivityStreams2::Properties::SUBJECT_TRIPLE, subj)
|
|
680
|
+
end
|
|
681
|
+
|
|
682
|
+
# Validate the object. This overrides the base validation, as objects only absolutely require an id property,
|
|
683
|
+
# so the base requirement for a type is relaxed.
|
|
684
|
+
#
|
|
685
|
+
# @return [Boolean] true if valid, otherwise raises ValidationError
|
|
686
|
+
def validate
|
|
687
|
+
ve = ValidationError.new
|
|
688
|
+
|
|
689
|
+
required_and_validate(ve, ActivityStreams2::Properties::ID, id)
|
|
690
|
+
|
|
691
|
+
raise ve if ve.has_errors?
|
|
692
|
+
true
|
|
693
|
+
end
|
|
694
|
+
end
|
|
695
|
+
|
|
696
|
+
# Default class to represents an actor in the COAR Notify pattern.
|
|
697
|
+
# Actors are used to represent the actor property in the notification patterns
|
|
698
|
+
#
|
|
699
|
+
# Specific patterns may need to extend this class to provide their specific behaviours and validation
|
|
700
|
+
class NotifyActor < NotifyPatternPart
|
|
701
|
+
# Default type is Service, but can also be set as any one of the other allowed types
|
|
702
|
+
def self.default_type
|
|
703
|
+
ActivityStreams2::ActivityStreamsTypes::SERVICE
|
|
704
|
+
end
|
|
705
|
+
|
|
706
|
+
# The allowed types for an actor: Service, Application, Group, Organisation, Person
|
|
707
|
+
def self.allowed_types
|
|
708
|
+
[
|
|
709
|
+
ActivityStreams2::ActivityStreamsTypes::SERVICE,
|
|
710
|
+
ActivityStreams2::ActivityStreamsTypes::APPLICATION,
|
|
711
|
+
ActivityStreams2::ActivityStreamsTypes::GROUP,
|
|
712
|
+
ActivityStreams2::ActivityStreamsTypes::ORGANIZATION,
|
|
713
|
+
ActivityStreams2::ActivityStreamsTypes::PERSON
|
|
714
|
+
]
|
|
715
|
+
end
|
|
716
|
+
|
|
717
|
+
# Get the name property of the actor
|
|
718
|
+
#
|
|
719
|
+
# @return [String] the name
|
|
720
|
+
def name
|
|
721
|
+
get_property(NotifyProperties::NAME)
|
|
722
|
+
end
|
|
723
|
+
|
|
724
|
+
# Set the name property of the actor
|
|
725
|
+
#
|
|
726
|
+
# @param value [String] the name to set
|
|
727
|
+
def name=(value)
|
|
728
|
+
set_property(NotifyProperties::NAME, value)
|
|
729
|
+
end
|
|
730
|
+
end
|
|
731
|
+
|
|
732
|
+
# Default class to represent an item in the COAR Notify pattern.
|
|
733
|
+
# Items are used to represent the ietf:item property in the notification patterns
|
|
734
|
+
#
|
|
735
|
+
# Specific patterns may need to extend this class to provide their specific behaviours and validation
|
|
736
|
+
class NotifyItem < NotifyPatternPart
|
|
737
|
+
# Get the mediaType property of the item
|
|
738
|
+
#
|
|
739
|
+
# @return [String] the media type
|
|
740
|
+
def media_type
|
|
741
|
+
get_property(NotifyProperties::MEDIA_TYPE)
|
|
742
|
+
end
|
|
743
|
+
|
|
744
|
+
# Set the mediaType property of the item
|
|
745
|
+
#
|
|
746
|
+
# @param value [String] the media type to set
|
|
747
|
+
def media_type=(value)
|
|
748
|
+
set_property(NotifyProperties::MEDIA_TYPE, value)
|
|
749
|
+
end
|
|
750
|
+
|
|
751
|
+
# Validate the item. This overrides the base validation, as objects only absolutely require an id property,
|
|
752
|
+
# so the base requirement for a type is relaxed.
|
|
753
|
+
#
|
|
754
|
+
# @return [Boolean] true if valid, otherwise raises ValidationError
|
|
755
|
+
def validate
|
|
756
|
+
ve = ValidationError.new
|
|
757
|
+
|
|
758
|
+
required_and_validate(ve, ActivityStreams2::Properties::ID, id)
|
|
759
|
+
|
|
760
|
+
raise ve if ve.has_errors?
|
|
761
|
+
true
|
|
762
|
+
end
|
|
763
|
+
end
|
|
764
|
+
|
|
765
|
+
# Mixins
|
|
766
|
+
##########################################################
|
|
767
|
+
|
|
768
|
+
# A mixin to add to a pattern which can override the default object property to return a full
|
|
769
|
+
# nested pattern from the object property, rather than the default NotifyObject
|
|
770
|
+
#
|
|
771
|
+
# This mixin needs to be included first, as it overrides the object property
|
|
772
|
+
# of the NotifyPattern class.
|
|
773
|
+
#
|
|
774
|
+
# For example:
|
|
775
|
+
#
|
|
776
|
+
# class MySpecialPattern < NotifyPattern
|
|
777
|
+
# include NestedPatternObjectMixin
|
|
778
|
+
# end
|
|
779
|
+
module NestedPatternObjectMixin
|
|
780
|
+
# Retrieve an object as it's correctly typed pattern, falling back to a default NotifyObject if no pattern matches
|
|
781
|
+
#
|
|
782
|
+
# @return [NotifyPattern, NotifyObject, nil] the object
|
|
783
|
+
def object
|
|
784
|
+
o = get_property(ActivityStreams2::Properties::OBJECT)
|
|
785
|
+
if o
|
|
786
|
+
# Try to get the factory class if it's available
|
|
787
|
+
if defined?(::Coarnotify::Factory::COARNotifyFactory)
|
|
788
|
+
begin
|
|
789
|
+
nested = ::Coarnotify::Factory::COARNotifyFactory.get_by_object(o.dup,
|
|
790
|
+
validate_stream_on_construct: false,
|
|
791
|
+
validate_properties: @validate_properties,
|
|
792
|
+
validators: @validators,
|
|
793
|
+
validation_context: nil) # don't supply a validation context, as these objects are not typical nested objects
|
|
794
|
+
return nested if nested
|
|
795
|
+
rescue => e
|
|
796
|
+
# Fall back to generic object if factory fails
|
|
797
|
+
end
|
|
798
|
+
end
|
|
799
|
+
|
|
800
|
+
# if we are unable to construct the typed nested object, just return a generic object
|
|
801
|
+
NotifyObject.new(stream: o.dup, validate_stream_on_construct: false,
|
|
802
|
+
validate_properties: @validate_properties, validators: @validators,
|
|
803
|
+
validation_context: ActivityStreams2::Properties::OBJECT)
|
|
804
|
+
end
|
|
805
|
+
end
|
|
806
|
+
|
|
807
|
+
# Set the object property
|
|
808
|
+
#
|
|
809
|
+
# @param value [NotifyObject, NotifyPattern] the object to set
|
|
810
|
+
def object=(value)
|
|
811
|
+
set_property(ActivityStreams2::Properties::OBJECT, value.doc)
|
|
812
|
+
end
|
|
813
|
+
end
|
|
814
|
+
|
|
815
|
+
# Mixin to provide an API for setting and getting the summary property of a pattern
|
|
816
|
+
module SummaryMixin
|
|
817
|
+
# The summary property of the pattern
|
|
818
|
+
#
|
|
819
|
+
# @return [String] the summary
|
|
820
|
+
def summary
|
|
821
|
+
get_property(ActivityStreams2::Properties::SUMMARY)
|
|
822
|
+
end
|
|
823
|
+
|
|
824
|
+
# Set the summary property of the pattern
|
|
825
|
+
#
|
|
826
|
+
# @param summary [String] the summary to set
|
|
827
|
+
def summary=(summary)
|
|
828
|
+
set_property(ActivityStreams2::Properties::SUMMARY, summary)
|
|
829
|
+
end
|
|
830
|
+
end
|
|
831
|
+
end
|
|
832
|
+
end
|
|
833
|
+
end
|