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,112 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'json'
|
|
4
|
+
require_relative 'factory'
|
|
5
|
+
|
|
6
|
+
module Coarnotify
|
|
7
|
+
# Supporting classes for COAR Notify server implementations
|
|
8
|
+
module Server
|
|
9
|
+
# An object representing the response from a COAR Notify server.
|
|
10
|
+
#
|
|
11
|
+
# Server implementations should construct and return this object with the appropriate properties
|
|
12
|
+
# when implementing the COARNotifyServiceBinding#notification_received binding
|
|
13
|
+
class COARNotifyReceipt
|
|
14
|
+
# The status code for a created resource
|
|
15
|
+
CREATED = 201
|
|
16
|
+
|
|
17
|
+
# The status code for an accepted request
|
|
18
|
+
ACCEPTED = 202
|
|
19
|
+
|
|
20
|
+
attr_reader :status, :location
|
|
21
|
+
|
|
22
|
+
# Construct a new COARNotifyReceipt object with the status code and location URL (optional)
|
|
23
|
+
#
|
|
24
|
+
# @param status [Integer] the HTTP status code, should be one of the constants CREATED (201) or ACCEPTED (202)
|
|
25
|
+
# @param location [String, nil] the HTTP URI for the resource that was created (if present)
|
|
26
|
+
def initialize(status, location = nil)
|
|
27
|
+
@status = status
|
|
28
|
+
@location = location
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Interface for implementing a COAR Notify server binding.
|
|
33
|
+
#
|
|
34
|
+
# Server implementation should extend this class and implement the notification_received method
|
|
35
|
+
#
|
|
36
|
+
# That method will receive a NotifyPattern object, which will be one of the known types
|
|
37
|
+
# and should return a COARNotifyReceipt object with the appropriate status code and location URL
|
|
38
|
+
class COARNotifyServiceBinding
|
|
39
|
+
# Process the receipt of the given notification, and respond with an appropriate receipt object
|
|
40
|
+
#
|
|
41
|
+
# @param notification [Core::Notify::NotifyPattern] the notification object received
|
|
42
|
+
# @return [COARNotifyReceipt] the receipt object to send back to the client
|
|
43
|
+
def notification_received(notification)
|
|
44
|
+
raise NotImplementedError
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# An exception class for server errors in the COAR Notify server implementation.
|
|
49
|
+
#
|
|
50
|
+
# The web layer of your server implementation should be able to intercept this from the
|
|
51
|
+
# COARNotifyServer#receive method and return the appropriate HTTP status code and message to the
|
|
52
|
+
# user in its standard way.
|
|
53
|
+
class COARNotifyServerError < StandardError
|
|
54
|
+
attr_reader :status, :message
|
|
55
|
+
|
|
56
|
+
# Construct a new COARNotifyServerError with the given status code and message
|
|
57
|
+
#
|
|
58
|
+
# @param status [Integer] HTTP Status code to respond to the client with
|
|
59
|
+
# @param msg [String] Message to send back to the client
|
|
60
|
+
def initialize(status, msg)
|
|
61
|
+
@status = status
|
|
62
|
+
@message = msg
|
|
63
|
+
super(msg)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# The main entrypoint to the COAR Notify server implementation.
|
|
68
|
+
#
|
|
69
|
+
# The web layer of your application should pass the json/raw payload of any incoming notification to the
|
|
70
|
+
# receive method, which will parse the payload and pass it to the COARNotifyServiceBinding#notification_received
|
|
71
|
+
# method of your service implementation
|
|
72
|
+
#
|
|
73
|
+
# This object should be constructed with your service implementation passed to it, for example:
|
|
74
|
+
#
|
|
75
|
+
# server = COARNotifyServer.new(MyServiceBinding.new)
|
|
76
|
+
# begin
|
|
77
|
+
# response = server.receive(request.body)
|
|
78
|
+
# # return response as JSON
|
|
79
|
+
# rescue COARNotifyServerError => e
|
|
80
|
+
# # return error with status e.status and message e.message
|
|
81
|
+
# end
|
|
82
|
+
class COARNotifyServer
|
|
83
|
+
# Construct a new COARNotifyServer with the given service implementation
|
|
84
|
+
#
|
|
85
|
+
# @param service_impl [COARNotifyServiceBinding] Your service implementation
|
|
86
|
+
def initialize(service_impl)
|
|
87
|
+
@service_impl = service_impl
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Receive an incoming notification as JSON, parse and validate (optional) and then pass to the
|
|
91
|
+
# service implementation
|
|
92
|
+
#
|
|
93
|
+
# @param raw [Hash, String] The JSON representation of the data, either as a string or a hash
|
|
94
|
+
# @param validate [Boolean] Whether to validate the notification before passing to the service implementation
|
|
95
|
+
# @return [COARNotifyReceipt] The COARNotifyReceipt response from the service implementation
|
|
96
|
+
def receive(raw, validate: true)
|
|
97
|
+
raw = JSON.parse(raw) if raw.is_a?(String)
|
|
98
|
+
|
|
99
|
+
obj = Factory::COARNotifyFactory.get_by_object(raw, validate_stream_on_construct: false)
|
|
100
|
+
if validate
|
|
101
|
+
begin
|
|
102
|
+
obj.validate
|
|
103
|
+
rescue ValidationError => e
|
|
104
|
+
raise COARNotifyServerError.new(400, "Invalid notification")
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
@service_impl.notification_received(obj)
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'uri'
|
|
4
|
+
require 'set'
|
|
5
|
+
|
|
6
|
+
module Coarnotify
|
|
7
|
+
# This module provides a set of validation functions that can be used to validate properties on objects.
|
|
8
|
+
# It also contains a Validator class which is used to wrap the protocol-wide validation rules which
|
|
9
|
+
# are shared across all objects.
|
|
10
|
+
module Validate
|
|
11
|
+
REQUIRED_MESSAGE = "`%s` is a required field"
|
|
12
|
+
|
|
13
|
+
# A wrapper around a set of validation rules which can be used to select the appropriate validator
|
|
14
|
+
# in a given context.
|
|
15
|
+
#
|
|
16
|
+
# The validation rules are structured as follows:
|
|
17
|
+
#
|
|
18
|
+
# {
|
|
19
|
+
# "<property>" => {
|
|
20
|
+
# "default" => default_validator_function,
|
|
21
|
+
# "context" => {
|
|
22
|
+
# "<context>" => {
|
|
23
|
+
# "default" => default_validator_function
|
|
24
|
+
# }
|
|
25
|
+
# }
|
|
26
|
+
# }
|
|
27
|
+
# }
|
|
28
|
+
#
|
|
29
|
+
# Here the <property> key is the name of the property being validated, which may be a string (the property name)
|
|
30
|
+
# or an array of strings (the property name and the namespace for the property name).
|
|
31
|
+
#
|
|
32
|
+
# If a context is provided, then if the top level property is being validated, and it appears inside a field
|
|
33
|
+
# present in the context then the default validator at the top level is overridden by the default validator
|
|
34
|
+
# in the context.
|
|
35
|
+
class Validator
|
|
36
|
+
attr_reader :rules
|
|
37
|
+
|
|
38
|
+
# Create a new validator with the given rules
|
|
39
|
+
#
|
|
40
|
+
# @param rules [Hash] The rules to use for validation
|
|
41
|
+
def initialize(rules)
|
|
42
|
+
@rules = rules
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Get the validation function for the given property in the given context
|
|
46
|
+
#
|
|
47
|
+
# @param property [String, Array] the property to get the validation function for
|
|
48
|
+
# @param context [String, Array] the context in which the property is being validated
|
|
49
|
+
# @return [Proc] a function which can be used to validate the property
|
|
50
|
+
def get(property, context = nil)
|
|
51
|
+
default = @rules.dig(property, "default")
|
|
52
|
+
if context
|
|
53
|
+
# FIXME: down the line this might need to become recursive
|
|
54
|
+
specific = @rules.dig(property, "context", context, "default")
|
|
55
|
+
return specific if specific
|
|
56
|
+
end
|
|
57
|
+
default
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Add additional rules to this validator
|
|
61
|
+
#
|
|
62
|
+
# @param rules [Hash] additional rules to merge
|
|
63
|
+
def add_rules(rules)
|
|
64
|
+
@rules = merge_dicts_recursive(@rules, rules)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
private
|
|
68
|
+
|
|
69
|
+
def merge_dicts_recursive(dict1, dict2)
|
|
70
|
+
merged = dict1.dup
|
|
71
|
+
dict2.each do |key, value|
|
|
72
|
+
if merged.key?(key) && merged[key].is_a?(Hash) && value.is_a?(Hash)
|
|
73
|
+
merged[key] = merge_dicts_recursive(merged[key], value)
|
|
74
|
+
else
|
|
75
|
+
merged[key] = value
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
merged
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# URI validation regular expressions
|
|
83
|
+
URI_RE = /^(([^:\/?#]+):)?(\/\/([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/
|
|
84
|
+
SCHEME_RE = /^[a-zA-Z][a-zA-Z0-9+\-.]*$/
|
|
85
|
+
IPV6_RE = /(?:^|(?<=\s))\[{0,1}(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))\]{0,1}(?=\s|$)/
|
|
86
|
+
|
|
87
|
+
HOSTPORT_RE = /^(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|localhost|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|(?:^|(?<=\s))(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))(?=\s|$))(?::\d+)?$/i
|
|
88
|
+
|
|
89
|
+
MARK = "\\-_.!~*'()"
|
|
90
|
+
UNRESERVED = "a-zA-Z0-9#{MARK}"
|
|
91
|
+
PCHARS = "#{UNRESERVED}:@&=+$,%/;"
|
|
92
|
+
PATH_RE = /^#{Regexp.escape('/')}?[#{PCHARS}]*$/
|
|
93
|
+
|
|
94
|
+
RESERVED = ";/?:@&=+$,"
|
|
95
|
+
URIC = "#{RESERVED}#{UNRESERVED}%"
|
|
96
|
+
FREE_RE = /^[#{URIC}]+$/
|
|
97
|
+
|
|
98
|
+
USERINFO_RE = /^[#{UNRESERVED}%;:&=+$,]*$/
|
|
99
|
+
|
|
100
|
+
# Validate that the given string is an absolute URI
|
|
101
|
+
#
|
|
102
|
+
# @param obj [Object] The Notify object to which the property being validated belongs
|
|
103
|
+
# @param uri [String] The string that claims to be an absolute URI
|
|
104
|
+
# @return [Boolean] true if the URI is valid, otherwise ArgumentError is raised
|
|
105
|
+
def self.absolute_uri(obj, uri)
|
|
106
|
+
m = URI_RE.match(uri)
|
|
107
|
+
raise ArgumentError, "Invalid URI" unless m
|
|
108
|
+
|
|
109
|
+
# URI must be absolute, so requires a scheme
|
|
110
|
+
raise ArgumentError, "URI requires a scheme (this may be a relative rather than absolute URI)" unless m[2]
|
|
111
|
+
|
|
112
|
+
scheme = m[2]
|
|
113
|
+
authority = m[4]
|
|
114
|
+
path = m[5]
|
|
115
|
+
query = m[7]
|
|
116
|
+
fragment = m[9]
|
|
117
|
+
|
|
118
|
+
# scheme must be alpha followed by alphanum or +, -, or .
|
|
119
|
+
if scheme
|
|
120
|
+
raise ArgumentError, "Invalid URI scheme `#{scheme}`" unless SCHEME_RE.match?(scheme)
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
if authority
|
|
124
|
+
userinfo = nil
|
|
125
|
+
hostport = authority
|
|
126
|
+
if authority.include?("@")
|
|
127
|
+
userinfo, hostport = authority.split("@", 2)
|
|
128
|
+
end
|
|
129
|
+
if userinfo
|
|
130
|
+
raise ArgumentError, "Invalid URI authority `#{authority}`" unless USERINFO_RE.match?(userinfo)
|
|
131
|
+
end
|
|
132
|
+
# determine if the domain is ipv6
|
|
133
|
+
if hostport.start_with?("[") # ipv6 with an optional port
|
|
134
|
+
port_separator = hostport.rindex("]:")
|
|
135
|
+
port = nil
|
|
136
|
+
if port_separator
|
|
137
|
+
port = hostport[port_separator+2..-1]
|
|
138
|
+
host = hostport[1...port_separator]
|
|
139
|
+
else
|
|
140
|
+
host = hostport[1..-2]
|
|
141
|
+
end
|
|
142
|
+
raise ArgumentError, "Invalid URI authority `#{authority}`" unless IPV6_RE.match?(host)
|
|
143
|
+
if port
|
|
144
|
+
begin
|
|
145
|
+
Integer(port)
|
|
146
|
+
rescue ArgumentError
|
|
147
|
+
raise ArgumentError, "Invalid URI port `#{port}`"
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
else
|
|
151
|
+
raise ArgumentError, "Invalid URI authority `#{authority}`" unless HOSTPORT_RE.match?(hostport)
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
if path
|
|
156
|
+
raise ArgumentError, "Invalid URI path `#{path}`" unless PATH_RE.match?(path)
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
if query
|
|
160
|
+
raise ArgumentError, "Invalid URI query `#{query}`" unless FREE_RE.match?(query)
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
if fragment
|
|
164
|
+
raise ArgumentError, "Invalid URI fragment `#{fragment}`" unless FREE_RE.match?(fragment)
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
true
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
# Validate that the given string is an absolute HTTP URI (i.e. a URL)
|
|
171
|
+
#
|
|
172
|
+
# @param obj [Object] The Notify object to which the property being validated belongs
|
|
173
|
+
# @param url [String] The string that claims to be an HTTP URI
|
|
174
|
+
# @return [Boolean] true if the URI is valid, otherwise ArgumentError is raised
|
|
175
|
+
def self.url(obj, url)
|
|
176
|
+
absolute_uri(obj, url)
|
|
177
|
+
o = URI.parse(url)
|
|
178
|
+
raise ArgumentError, "URL scheme must be http or https" unless %w[http https].include?(o.scheme)
|
|
179
|
+
raise ArgumentError, "Does not appear to be a valid URL" if o.host.nil? || o.host.empty?
|
|
180
|
+
true
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
# Closure that returns a validation function that checks that the value is one of the given values
|
|
184
|
+
#
|
|
185
|
+
# @param values [Array<String>] The list of values to choose from
|
|
186
|
+
# @return [Proc] a validation function
|
|
187
|
+
def self.one_of(values)
|
|
188
|
+
proc do |obj, x|
|
|
189
|
+
unless values.include?(x)
|
|
190
|
+
raise ArgumentError, "`#{x}` is not one of the valid values: #{values}"
|
|
191
|
+
end
|
|
192
|
+
true
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
# Closure that returns a validation function that checks that a list of values contains at least one
|
|
197
|
+
# of the given values
|
|
198
|
+
#
|
|
199
|
+
# @param values [Array<String>] The list of values to choose from
|
|
200
|
+
# @return [Proc] a validation function
|
|
201
|
+
def self.at_least_one_of(values)
|
|
202
|
+
proc do |obj, x|
|
|
203
|
+
x = [x] unless x.is_a?(Array)
|
|
204
|
+
|
|
205
|
+
found = x.any? { |entry| values.include?(entry) }
|
|
206
|
+
|
|
207
|
+
unless found
|
|
208
|
+
# if we don't find one of the document values in the list of "at least one of" values,
|
|
209
|
+
# raise an exception
|
|
210
|
+
raise ArgumentError, "`#{x}` is not one of the valid values: #{values}"
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
true
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
# Closure that returns a validation function that checks the provided values contain the required value
|
|
218
|
+
#
|
|
219
|
+
# @param value [String, Array<String>] The value(s) that must be present
|
|
220
|
+
# @return [Proc] a validation function
|
|
221
|
+
def self.contains(value)
|
|
222
|
+
values = value.is_a?(Array) ? value : [value]
|
|
223
|
+
values_set = values.to_set
|
|
224
|
+
|
|
225
|
+
proc do |obj, x|
|
|
226
|
+
x = [x] unless x.is_a?(Array)
|
|
227
|
+
x_set = x.to_set
|
|
228
|
+
|
|
229
|
+
intersection = x_set & values_set
|
|
230
|
+
unless intersection == values_set
|
|
231
|
+
raise ArgumentError, "`#{x}` does not contain the required value(s): #{values}"
|
|
232
|
+
end
|
|
233
|
+
true
|
|
234
|
+
end
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
# Validate that the given value is of the correct type for the object
|
|
238
|
+
#
|
|
239
|
+
# @param obj [Object] the notify object being validated
|
|
240
|
+
# @param value [String, Array<String>] the type being validated
|
|
241
|
+
# @return [Boolean] true if the type is valid, otherwise ArgumentError is raised
|
|
242
|
+
def self.type_checker(obj, value)
|
|
243
|
+
if obj.respond_to?(:allowed_types)
|
|
244
|
+
allowed = obj.allowed_types
|
|
245
|
+
return true if allowed.empty?
|
|
246
|
+
validator = one_of(allowed)
|
|
247
|
+
validator.call(obj, value)
|
|
248
|
+
elsif obj.respond_to?(:type_constant)
|
|
249
|
+
ty = obj.type_constant
|
|
250
|
+
validator = contains(ty)
|
|
251
|
+
validator.call(obj, value)
|
|
252
|
+
end
|
|
253
|
+
true
|
|
254
|
+
end
|
|
255
|
+
end
|
|
256
|
+
end
|
data/lib/coarnotify.rb
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'json'
|
|
4
|
+
|
|
5
|
+
# This is the base of the coarnotifyrb module.
|
|
6
|
+
#
|
|
7
|
+
# In here you will find
|
|
8
|
+
# a full set of model objects for all the Notify Patterns documented in
|
|
9
|
+
# https://coar-notify.net/specification/1.0.1/
|
|
10
|
+
#
|
|
11
|
+
# You will also find a client library that will allow you to send notifications
|
|
12
|
+
# to an inbox, and a server library that will allow you to write a service
|
|
13
|
+
# binding to your own systems to receive notifications via an inbox.
|
|
14
|
+
#
|
|
15
|
+
# There are also unit tests demonstrating the various features of the system,
|
|
16
|
+
# integration tests which can be run against a remote inbox, and a
|
|
17
|
+
# stand-alone inbox you can use for local testing.
|
|
18
|
+
|
|
19
|
+
require_relative 'coarnotify/version'
|
|
20
|
+
require_relative 'coarnotify/exceptions'
|
|
21
|
+
require_relative 'coarnotify/validate'
|
|
22
|
+
require_relative 'coarnotify/core/activity_streams2'
|
|
23
|
+
require_relative 'coarnotify/core/notify'
|
|
24
|
+
require_relative 'coarnotify/http'
|
|
25
|
+
require_relative 'coarnotify/patterns/accept'
|
|
26
|
+
require_relative 'coarnotify/patterns/announce_endorsement'
|
|
27
|
+
require_relative 'coarnotify/patterns/announce_relationship'
|
|
28
|
+
require_relative 'coarnotify/patterns/announce_review'
|
|
29
|
+
require_relative 'coarnotify/patterns/announce_service_result'
|
|
30
|
+
require_relative 'coarnotify/patterns/reject'
|
|
31
|
+
require_relative 'coarnotify/patterns/request_endorsement'
|
|
32
|
+
require_relative 'coarnotify/patterns/request_review'
|
|
33
|
+
require_relative 'coarnotify/patterns/tentatively_accept'
|
|
34
|
+
require_relative 'coarnotify/patterns/tentatively_reject'
|
|
35
|
+
require_relative 'coarnotify/patterns/undo_offer'
|
|
36
|
+
require_relative 'coarnotify/patterns/unprocessable_notification'
|
|
37
|
+
require_relative 'coarnotify/factory'
|
|
38
|
+
require_relative 'coarnotify/client'
|
|
39
|
+
require_relative 'coarnotify/server'
|
|
40
|
+
|
|
41
|
+
# Main module for the COAR Notify Ruby implementation
|
|
42
|
+
module Coarnotify
|
|
43
|
+
# Convenience method to create a new COAR Notify client
|
|
44
|
+
#
|
|
45
|
+
# @param inbox_url [String, nil] HTTP URI of the inbox to communicate with by default
|
|
46
|
+
# @param http_layer [Http::HttpLayer, nil] An implementation of the HttpLayer interface
|
|
47
|
+
# @return [Client::COARNotifyClient] a new client instance
|
|
48
|
+
def self.client(inbox_url: nil, http_layer: nil)
|
|
49
|
+
Client::COARNotifyClient.new(inbox_url: inbox_url, http_layer: http_layer)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Convenience method to create a new COAR Notify server
|
|
53
|
+
#
|
|
54
|
+
# @param service_impl [Server::COARNotifyServiceBinding] Your service implementation
|
|
55
|
+
# @return [Server::COARNotifyServer] a new server instance
|
|
56
|
+
def self.server(service_impl)
|
|
57
|
+
Server::COARNotifyServer.new(service_impl)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Convenience method to create a pattern from a hash
|
|
61
|
+
#
|
|
62
|
+
# @param data [Hash] The raw stream data to parse and instantiate around
|
|
63
|
+
# @param options [Hash] any options to pass to the object constructor
|
|
64
|
+
# @return [Core::Notify::NotifyPattern] A NotifyPattern of the correct type
|
|
65
|
+
def self.from_hash(data, **options)
|
|
66
|
+
Factory::COARNotifyFactory.get_by_object(data, **options)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Convenience method to create a pattern from JSON
|
|
70
|
+
#
|
|
71
|
+
# @param json [String] The JSON string to parse and instantiate around
|
|
72
|
+
# @param options [Hash] any options to pass to the object constructor
|
|
73
|
+
# @return [Core::Notify::NotifyPattern] A NotifyPattern of the correct type
|
|
74
|
+
def self.from_json(json, **options)
|
|
75
|
+
data = JSON.parse(json)
|
|
76
|
+
from_hash(data, **options)
|
|
77
|
+
end
|
|
78
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: coarnotify
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Cottage Labs
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2025-12-16 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: json
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '2.0'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '2.0'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: rspec
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - "~>"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '3.0'
|
|
34
|
+
type: :development
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - "~>"
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '3.0'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: rake
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - "~>"
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '13.0'
|
|
48
|
+
type: :development
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - "~>"
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '13.0'
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: rubocop
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - "~>"
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '1.0'
|
|
62
|
+
type: :development
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - "~>"
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '1.0'
|
|
69
|
+
description: COAR Notify Common Library
|
|
70
|
+
email:
|
|
71
|
+
- us@cottagelabs.com
|
|
72
|
+
executables: []
|
|
73
|
+
extensions: []
|
|
74
|
+
extra_rdoc_files: []
|
|
75
|
+
files:
|
|
76
|
+
- lib/coarnotify.rb
|
|
77
|
+
- lib/coarnotify/client.rb
|
|
78
|
+
- lib/coarnotify/core/activity_streams2.rb
|
|
79
|
+
- lib/coarnotify/core/notify.rb
|
|
80
|
+
- lib/coarnotify/exceptions.rb
|
|
81
|
+
- lib/coarnotify/factory.rb
|
|
82
|
+
- lib/coarnotify/http.rb
|
|
83
|
+
- lib/coarnotify/patterns/accept.rb
|
|
84
|
+
- lib/coarnotify/patterns/announce_endorsement.rb
|
|
85
|
+
- lib/coarnotify/patterns/announce_relationship.rb
|
|
86
|
+
- lib/coarnotify/patterns/announce_review.rb
|
|
87
|
+
- lib/coarnotify/patterns/announce_service_result.rb
|
|
88
|
+
- lib/coarnotify/patterns/reject.rb
|
|
89
|
+
- lib/coarnotify/patterns/request_endorsement.rb
|
|
90
|
+
- lib/coarnotify/patterns/request_review.rb
|
|
91
|
+
- lib/coarnotify/patterns/tentatively_accept.rb
|
|
92
|
+
- lib/coarnotify/patterns/tentatively_reject.rb
|
|
93
|
+
- lib/coarnotify/patterns/undo_offer.rb
|
|
94
|
+
- lib/coarnotify/patterns/unprocessable_notification.rb
|
|
95
|
+
- lib/coarnotify/server.rb
|
|
96
|
+
- lib/coarnotify/validate.rb
|
|
97
|
+
- lib/coarnotify/version.rb
|
|
98
|
+
homepage:
|
|
99
|
+
licenses:
|
|
100
|
+
- MIT
|
|
101
|
+
metadata: {}
|
|
102
|
+
post_install_message:
|
|
103
|
+
rdoc_options: []
|
|
104
|
+
require_paths:
|
|
105
|
+
- lib
|
|
106
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
107
|
+
requirements:
|
|
108
|
+
- - ">="
|
|
109
|
+
- !ruby/object:Gem::Version
|
|
110
|
+
version: '0'
|
|
111
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
112
|
+
requirements:
|
|
113
|
+
- - ">="
|
|
114
|
+
- !ruby/object:Gem::Version
|
|
115
|
+
version: '0'
|
|
116
|
+
requirements: []
|
|
117
|
+
rubygems_version: 3.4.20
|
|
118
|
+
signing_key:
|
|
119
|
+
specification_version: 4
|
|
120
|
+
summary: COAR Notify Common Library
|
|
121
|
+
test_files: []
|