chook 1.0.0.b1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +174 -0
  3. data/README.md +259 -0
  4. data/bin/chook-server +28 -0
  5. data/data/sample_handlers/RestAPIOperation-executable +91 -0
  6. data/data/sample_handlers/RestAPIOperation.rb +45 -0
  7. data/data/sample_handlers/SmartGroupComputerMembershipChange-executable +47 -0
  8. data/data/sample_handlers/SmartGroupComputerMembershipChange.rb +33 -0
  9. data/data/sample_jsons/ComputerAdded.json +27 -0
  10. data/data/sample_jsons/ComputerCheckIn.json +27 -0
  11. data/data/sample_jsons/ComputerInventoryCompleted.json +27 -0
  12. data/data/sample_jsons/ComputerPolicyFinished.json +27 -0
  13. data/data/sample_jsons/ComputerPushCapabilityChanged.json +27 -0
  14. data/data/sample_jsons/JSSShutdown.json +14 -0
  15. data/data/sample_jsons/JSSStartup.json +14 -0
  16. data/data/sample_jsons/MobileDeviceCheckIn.json +26 -0
  17. data/data/sample_jsons/MobileDeviceCommandCompleted.json +26 -0
  18. data/data/sample_jsons/MobileDeviceEnrolled.json +26 -0
  19. data/data/sample_jsons/MobileDevicePushSent.json +26 -0
  20. data/data/sample_jsons/MobileDeviceUnEnrolled.json +26 -0
  21. data/data/sample_jsons/PatchSoftwareTitleUpdated.json +14 -0
  22. data/data/sample_jsons/PushSent.json +11 -0
  23. data/data/sample_jsons/README +4 -0
  24. data/data/sample_jsons/RestAPIOperation.json +15 -0
  25. data/data/sample_jsons/SCEPChallenge.json +10 -0
  26. data/data/sample_jsons/SmartGroupComputerMembershipChange.json +13 -0
  27. data/data/sample_jsons/SmartGroupMobileDeviceMembershipChange.json +13 -0
  28. data/lib/chook.rb +38 -0
  29. data/lib/chook/configuration.rb +198 -0
  30. data/lib/chook/event.rb +153 -0
  31. data/lib/chook/event/handled_event.rb +154 -0
  32. data/lib/chook/event/handled_event/handlers.rb +206 -0
  33. data/lib/chook/event/test_event.rb +140 -0
  34. data/lib/chook/event_handling.rb +40 -0
  35. data/lib/chook/event_testing.rb +43 -0
  36. data/lib/chook/foundation.rb +33 -0
  37. data/lib/chook/handled_events.rb +33 -0
  38. data/lib/chook/handled_subjects.rb +33 -0
  39. data/lib/chook/procs.rb +46 -0
  40. data/lib/chook/server.rb +121 -0
  41. data/lib/chook/server/routes.rb +27 -0
  42. data/lib/chook/server/routes/handle_webhook_event.rb +39 -0
  43. data/lib/chook/server/routes/home.rb +37 -0
  44. data/lib/chook/subject.rb +143 -0
  45. data/lib/chook/subject/computer.rb +121 -0
  46. data/lib/chook/subject/handled_subject.rb +84 -0
  47. data/lib/chook/subject/jss.rb +56 -0
  48. data/lib/chook/subject/mobile_device.rb +115 -0
  49. data/lib/chook/subject/patch_software_title_update.rb +55 -0
  50. data/lib/chook/subject/push.rb +38 -0
  51. data/lib/chook/subject/randomizers.rb +506 -0
  52. data/lib/chook/subject/rest_api_operation.rb +62 -0
  53. data/lib/chook/subject/samplers.rb +360 -0
  54. data/lib/chook/subject/scep_challenge.rb +32 -0
  55. data/lib/chook/subject/smart_group.rb +50 -0
  56. data/lib/chook/subject/test_subject.rb +195 -0
  57. data/lib/chook/subject/validators.rb +117 -0
  58. data/lib/chook/test_events.rb +33 -0
  59. data/lib/chook/test_subjects.rb +33 -0
  60. data/lib/chook/version.rb +32 -0
  61. metadata +129 -0
@@ -0,0 +1,26 @@
1
+ {
2
+ "webhook": {
3
+ "id": 11,
4
+ "name": "MobileEnrolled Webhook",
5
+ "webhookEvent": "MobileDeviceEnrolled"
6
+ },
7
+ "event": {
8
+ "udid": "0d12c74062dxyxyxb4b234f4ac1yxyxyx1efccd0",
9
+ "deviceName": "tipple",
10
+ "version": "4.71.00",
11
+ "model": "iPhone7,2",
12
+ "bluetoothMacAddress": "xx:xx:37:yy:78:xx",
13
+ "wifiMacAddress": "yy:yy:37:xx:78:yy",
14
+ "imei": "xx yyyyyy zzzzzz 0",
15
+ "icciID": "aaaa bbbb cccc dddd eeee",
16
+ "product": null,
17
+ "serialNumber": "C8EM25KSG5PT",
18
+ "userDirectoryID": "-1",
19
+ "room": "s167",
20
+ "osVersion": "9.3.4",
21
+ "osBuild": "13G35",
22
+ "modelDisplay": "iPhone 6",
23
+ "username": "username",
24
+ "jssID": 158
25
+ }
26
+ }
@@ -0,0 +1,26 @@
1
+ {
2
+ "webhook": {
3
+ "id": 12,
4
+ "name": "MobilePush Webhook",
5
+ "webhookEvent": "MobileDevicePushSent"
6
+ },
7
+ "event": {
8
+ "udid": "0d12c74062dxyxyxb4b234f4ac1yxyxyx1efccd0",
9
+ "deviceName": "tipple",
10
+ "version": "4.71.00",
11
+ "model": "iPhone7,2",
12
+ "bluetoothMacAddress": "xx:xx:37:yy:78:xx",
13
+ "wifiMacAddress": "yy:yy:37:xx:78:yy",
14
+ "imei": "xx yyyyyy zzzzzz 0",
15
+ "icciID": "aaaa bbbb cccc dddd eeee",
16
+ "product": null,
17
+ "serialNumber": "C8EM25KSG5PT",
18
+ "userDirectoryID": "-1",
19
+ "room": "s167",
20
+ "osVersion": "9.3.4",
21
+ "osBuild": "13G35",
22
+ "modelDisplay": "iPhone 6",
23
+ "username": "username",
24
+ "jssID": 158
25
+ }
26
+ }
@@ -0,0 +1,26 @@
1
+ {
2
+ "webhook": {
3
+ "id": 13,
4
+ "name": "MobileUnEnrolled Webhook",
5
+ "webhookEvent": "MobileDeviceUnEnrolled"
6
+ },
7
+ "event": {
8
+ "udid": "0d12c74062dxyxyxb4b234f4ac1yxyxyx1efccd0",
9
+ "deviceName": "tipple",
10
+ "version": "4.71.00",
11
+ "model": "iPhone7,2",
12
+ "bluetoothMacAddress": "xx:xx:37:yy:78:xx",
13
+ "wifiMacAddress": "yy:yy:37:xx:78:yy",
14
+ "imei": "xx yyyyyy zzzzzz 0",
15
+ "icciID": "aaaa bbbb cccc dddd eeee",
16
+ "product": null,
17
+ "serialNumber": "C8EM25KSG5PT",
18
+ "userDirectoryID": "-1",
19
+ "room": "s167",
20
+ "osVersion": "9.3.4",
21
+ "osBuild": "13G35",
22
+ "modelDisplay": "iPhone 6",
23
+ "username": "username",
24
+ "jssID": 158
25
+ }
26
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "webhook": {
3
+ "id": 14,
4
+ "name": "PatchSoftwareTitleUpdated Webhook",
5
+ "webhookEvent": "PatchSoftwareTitleUpdated"
6
+ },
7
+ "event": {
8
+ "name": "CoolApp",
9
+ "latestVersion": "12.4.3",
10
+ "lastUpdate": 1463054400000,
11
+ "reportUrl": "http://CoolApp.com/downloads/latest",
12
+ "jssID": 1
13
+ }
14
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ "webhook": {
3
+ "id": 15,
4
+ "name": "PushSent Webhook",
5
+ "webhookEvent": "PushSent"
6
+ },
7
+ "event": {
8
+ "type": "blank",
9
+ "jssID": 1322
10
+ }
11
+ }
@@ -0,0 +1,4 @@
1
+
2
+ # a curl command to test the server with a smaple json file
3
+
4
+ curl -i -H "Accept: application/json" -H "Content-Type: application/json" -X POST --data "$( < /path/to/json/file )" http://localhost:8000/handle_webhook_event
@@ -0,0 +1,15 @@
1
+ {
2
+ "webhook": {
3
+ "id": 16,
4
+ "name": "RestAPIOperation Webhook",
5
+ "webhookEvent": "RestAPIOperation"
6
+ },
7
+ "event": {
8
+ "operationSuccessful": true,
9
+ "objectID": 1233,
10
+ "objectName": "wanderer",
11
+ "objectTypeName": "computer",
12
+ "authorizedUsername": "username",
13
+ "restAPIOperationType": "POST"
14
+ }
15
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "webhook": {
3
+ "id": 17,
4
+ "name": "SCEPChallenge Webhook",
5
+ "webhookEvent": "SCEPChallenge"
6
+ },
7
+ "event": {
8
+ "targetDevice": "computer"
9
+ }
10
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "webhook": {
3
+ "id": 18,
4
+ "name": "SmartGroupComputerMembershipChange Webhook",
5
+ "webhookEvent": "SmartGroupComputerMembershipChange"
6
+ },
7
+ "event": {
8
+ "name": "OddComputers",
9
+ "smartGroup": true,
10
+ "jssid": 95,
11
+ "computer": true
12
+ }
13
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "webhook": {
3
+ "id": 19,
4
+ "name": "SmartGroupMobileDeviceMembershipChange Webhook",
5
+ "webhookEvent": "SmartGroupMobileDeviceMembershipChange"
6
+ },
7
+ "event": {
8
+ "name": "OddMobileDevs",
9
+ "smartGroup": true,
10
+ "jssid": 99,
11
+ "computer": false
12
+ }
13
+ }
@@ -0,0 +1,38 @@
1
+ ### Copyright 2017 Pixar
2
+
3
+ ###
4
+ ### Licensed under the Apache License, Version 2.0 (the "Apache License")
5
+ ### with the following modification; you may not use this file except in
6
+ ### compliance with the Apache License and the following modification to it:
7
+ ### Section 6. Trademarks. is deleted and replaced with:
8
+ ###
9
+ ### 6. Trademarks. This License does not grant permission to use the trade
10
+ ### names, trademarks, service marks, or product names of the Licensor
11
+ ### and its affiliates, except as required to comply with Section 4(c) of
12
+ ### the License and to reproduce the content of the NOTICE file.
13
+ ###
14
+ ### You may obtain a copy of the Apache License at
15
+ ###
16
+ ### http://www.apache.org/licenses/LICENSE-2.0
17
+ ###
18
+ ### Unless required by applicable law or agreed to in writing, software
19
+ ### distributed under the Apache License with the above modification is
20
+ ### distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
21
+ ### KIND, either express or implied. See the Apache License for the specific
22
+ ### language governing permissions and limitations under the Apache License.
23
+ ###
24
+ ###
25
+
26
+ # Requiring this file (require 'chook') will load the entire library
27
+ # including event testing, event handling, and the server.
28
+ #
29
+ # To load a subset, use one of:
30
+ # require 'chook/event_testing'
31
+ # require 'chook/event_handing'
32
+ # require 'chook/server'
33
+
34
+ require 'chook/foundation'
35
+ require 'chook/event_testing'
36
+
37
+ # the server - it loads the event handling files
38
+ require 'chook/server'
@@ -0,0 +1,198 @@
1
+ ### Copyright 2017 Pixar
2
+
3
+ ###
4
+ ### Licensed under the Apache License, Version 2.0 (the "Apache License")
5
+ ### with the following modification; you may not use this file except in
6
+ ### compliance with the Apache License and the following modification to it:
7
+ ### Section 6. Trademarks. is deleted and replaced with:
8
+ ###
9
+ ### 6. Trademarks. This License does not grant permission to use the trade
10
+ ### names, trademarks, service marks, or product names of the Licensor
11
+ ### and its affiliates, except as required to comply with Section 4(c) of
12
+ ### the License and to reproduce the content of the NOTICE file.
13
+ ###
14
+ ### You may obtain a copy of the Apache License at
15
+ ###
16
+ ### http://www.apache.org/licenses/LICENSE-2.0
17
+ ###
18
+ ### Unless required by applicable law or agreed to in writing, software
19
+ ### distributed under the Apache License with the above modification is
20
+ ### distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
21
+ ### KIND, either express or implied. See the Apache License for the specific
22
+ ### language governing permissions and limitations under the Apache License.
23
+ ###
24
+ ###
25
+
26
+ require 'singleton'
27
+
28
+ module Chook
29
+
30
+ # The configuration object
31
+ class Configuration
32
+
33
+ include ::Singleton
34
+
35
+ # The location of the default config file
36
+ DEFAULT_CONF_FILE = Pathname.new '/etc/chook.conf'
37
+
38
+ # The attribute keys we maintain, and the type they should be stored as
39
+ CONF_KEYS = {
40
+ server_port: :to_i,
41
+ server_engine: :to_sym,
42
+ handler_dir: nil,
43
+ use_ssl: Chook::Procs::STRING_TO_BOOLEAN,
44
+ ssl_private_key_path: Chook::Procs::STRING_TO_PATHNAME,
45
+ ssl_private_key_pw_path: nil,
46
+ ssl_cert_path: Chook::Procs::STRING_TO_PATHNAME,
47
+ ssl_cert_name: nil
48
+ }.freeze
49
+
50
+ # Class Variables
51
+ #####################################
52
+
53
+ # Class Methods
54
+ #####################################
55
+
56
+ # Attributes
57
+ #####################################
58
+
59
+ # automatically create accessors for all the CONF_KEYS
60
+ CONF_KEYS.keys.each { |k| attr_accessor k }
61
+
62
+ # Constructor
63
+ #####################################
64
+
65
+ # Initialize!
66
+ #
67
+ def initialize
68
+ read_global
69
+ end
70
+
71
+ # Public Instance Methods
72
+ #####################################
73
+
74
+ # Clear all values
75
+ #
76
+ # @return [void]
77
+ #
78
+ def clear_all
79
+ CONF_KEYS.keys.each { |k| send "#{k}=".to_sym, nil }
80
+ end
81
+
82
+ # (Re)read the global prefs, if it exists.
83
+ #
84
+ # @return [Boolean] was the file loaded?
85
+ #
86
+ def read_global
87
+ return false unless DEFAULT_CONF_FILE.file? && DEFAULT_CONF_FILE.readable?
88
+ read DEFAULT_CONF_FILE
89
+ end
90
+
91
+ # Clear the settings and reload the prefs file, or another file if provided
92
+ #
93
+ # @param file[String,Pathname] a non-standard prefs file to load
94
+ #
95
+ # @return [Boolean] was the file reloaded?
96
+ #
97
+ def reload(file = DEFAULT_CONF_FILE)
98
+ file = Pathname.new file
99
+ return false unless file.file? && file.readable?
100
+ clear_all
101
+ read file
102
+ end
103
+
104
+ # Save the prefs into a file
105
+ #
106
+ # @param file[Symbol,String,Pathname] either :user, :global, or an arbitrary file to save.
107
+ #
108
+ # @return [void]
109
+ #
110
+ def save(file)
111
+ path = Pathname.new(file)
112
+
113
+ # file already exists? read it in and update the values.
114
+ # Don't overwrite it, since the user might have comments
115
+ # in there.
116
+ if path.readable?
117
+ data = path.read
118
+
119
+ # go thru the known attributes/keys
120
+ CONF_KEYS.keys.sort.each do |k|
121
+ # if the key exists, update it.
122
+ if data =~ /^#{k}:/
123
+ data.sub!(/^#{k}:.*$/, "#{k}: #{send k}")
124
+
125
+ # if not, add it to the end unless it's nil
126
+ else
127
+ data += "\n#{k}: #{send k}" unless send(k).nil?
128
+ end # if data =~ /^#{k}:/
129
+ end # each do |k|
130
+
131
+ else # not readable, make a new file
132
+ data = ''
133
+ CONF_KEYS.keys.sort.each do |k|
134
+ data << "#{k}: #{send k}\n" unless send(k).nil?
135
+ end
136
+ end # if path readable
137
+
138
+ # make sure we end with a newline, the save it.
139
+ data << "\n" unless data.end_with?("\n")
140
+ path.open('w') { |f| f.write data }
141
+ end # save file
142
+
143
+ # Print out the current settings to stdout
144
+ #
145
+ # @return [void]
146
+ #
147
+ def print
148
+ CONF_KEYS.keys.sort.each { |k| puts "#{k}: #{send k}" }
149
+ end
150
+
151
+ # Private Instance Methods
152
+ #####################################
153
+ private
154
+
155
+ # Read in a prefs file
156
+ #
157
+ # @param file[String,Pathname] the file to read
158
+ #
159
+ # @return [Boolean] was the file read?
160
+ #
161
+ def read(file)
162
+ available_conf_keys = CONF_KEYS.keys
163
+ Pathname.new(file).read.each_line do |line|
164
+ # skip blank lines and those starting with #
165
+ next if line =~ /^\s*(#|$)/
166
+
167
+ line.strip =~ /^(\w+?):\s*(\S.*)$/
168
+ key = Regexp.last_match(1)
169
+ next unless key
170
+ attr = key.to_sym
171
+ next unless available_conf_keys.include? attr
172
+ setter = "#{key}=".to_sym
173
+ value = Regexp.last_match(2).strip
174
+
175
+ # convert the string value read from the file
176
+ # to the correct class
177
+ value &&= case CONF_KEYS[attr]
178
+ when Proc
179
+ # If its a proc, pass it to the proc
180
+ CONF_KEYS[attr].call value
181
+ when Symbol
182
+ # otherwise its a symbol method name to call on the string
183
+ value.send(CONF_KEYS[attr])
184
+ else
185
+ value
186
+ end
187
+
188
+ send(setter, value)
189
+ end # do line
190
+ true
191
+ end # read file
192
+
193
+ end # class Configuration
194
+
195
+ # The single instance of Configuration
196
+ CONFIG = Chook::Configuration.instance
197
+
198
+ end # module
@@ -0,0 +1,153 @@
1
+ ### Copyright 2017 Pixar
2
+
3
+ ###
4
+ ### Licensed under the Apache License, Version 2.0 (the "Apache License")
5
+ ### with the following modification; you may not use this file except in
6
+ ### compliance with the Apache License and the following modification to it:
7
+ ### Section 6. Trademarks. is deleted and replaced with:
8
+ ###
9
+ ### 6. Trademarks. This License does not grant permission to use the trade
10
+ ### names, trademarks, service marks, or product names of the Licensor
11
+ ### and its affiliates, except as required to comply with Section 4(c) of
12
+ ### the License and to reproduce the content of the NOTICE file.
13
+ ###
14
+ ### You may obtain a copy of the Apache License at
15
+ ###
16
+ ### http://www.apache.org/licenses/LICENSE-2.0
17
+ ###
18
+ ### Unless required by applicable law or agreed to in writing, software
19
+ ### distributed under the Apache License with the above modification is
20
+ ### distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
21
+ ### KIND, either express or implied. See the Apache License for the specific
22
+ ### language governing permissions and limitations under the Apache License.
23
+ ###
24
+ ###
25
+
26
+ #
27
+ module Chook
28
+
29
+ # This is the MetaClass for all Event objects, both handled and test.
30
+ # It holds constants, methods, and attributes that are common to all
31
+ # events in Chook.
32
+ #
33
+ # An 'event' within Chook is the ruby-abstraction of a JSON payload
34
+ # as sent by a JSS Webhook representing an event that takes place in the JSS.
35
+ #
36
+ # All events contain a #subject attribute, which represent the thing
37
+ # upon which the event acted, e.g. a computer, mobile divice, or the
38
+ # JSS itself.
39
+ # The #subject attribute contains an object which is a subclass
40
+ # of Chook::Subject, q.v.
41
+ #
42
+ class Event
43
+
44
+ # A mapping of the Event Names (as they are known to the JSS)
45
+ # to the the matching Subject class names
46
+ #
47
+ # The event names from the JSS are in the JSON hash as the value of
48
+ # JSON_hash[:webhook][:webhookEvent] and they are also the names
49
+ # of the matching event classes within the HandledEvent module and the
50
+ # TestEvent module.
51
+ #
52
+ # E.g.
53
+ # the HandledEvents class that handles the 'ComputerPolicyFinished'
54
+ # event is Chook::HandledEvent::ComputerPolicyFinished
55
+ #
56
+ # and
57
+ #
58
+ # the TestEvents that simulates 'ComputerPolicyFinished' is
59
+ # Chook::TestEvents::ComputerPolicyFinished
60
+ #
61
+ # And, those classes take the matching 'Computer' subject, either
62
+ # Chook::HandledSubjects::Computer or Chook::TestSubjects::Computer
63
+ #
64
+ EVENTS = {
65
+ 'ComputerAdded' => Chook::Subject::COMPUTER,
66
+ 'ComputerCheckIn' => Chook::Subject::COMPUTER,
67
+ 'ComputerInventoryCompleted' => Chook::Subject::COMPUTER,
68
+ 'ComputerPolicyFinished' => Chook::Subject::COMPUTER,
69
+ 'ComputerPushCapabilityChanged' => Chook::Subject::COMPUTER,
70
+ 'JSSShutdown' => Chook::Subject::JAMF_SOFTWARE_SERVER,
71
+ 'JSSStartup' => Chook::Subject::JAMF_SOFTWARE_SERVER,
72
+ 'MobileDeviceCheckIn' => Chook::Subject::MOBILE_DEVICE,
73
+ 'MobileDeviceCommandCompleted' => Chook::Subject::MOBILE_DEVICE,
74
+ 'MobileDeviceEnrolled' => Chook::Subject::MOBILE_DEVICE,
75
+ 'MobileDevicePushSent' => Chook::Subject::MOBILE_DEVICE,
76
+ 'MobileDeviceUnEnrolled' => Chook::Subject::MOBILE_DEVICE,
77
+ 'PatchSoftwareTitleUpdated' => Chook::Subject::PATCH_SW_UPDATE,
78
+ 'PushSent' => Chook::Subject::PUSH,
79
+ 'RestAPIOperation' => Chook::Subject::REST_API_OPERATION,
80
+ 'SCEPChallenge' => Chook::Subject::SCEP_CHALLENGE,
81
+ 'SmartGroupComputerMembershipChange' => Chook::Subject::SMART_GROUP,
82
+ 'SmartGroupMobileDeviceMembershipChange' => Chook::Subject::SMART_GROUP
83
+ }.freeze
84
+
85
+ # Event subclasses will have an EVENT_NAME constant,
86
+ # which contains the name of the event, one of the keys
87
+ # from the Event::EVENTS Hash.
88
+ EVENT_NAME_CONST = 'EVENT_NAME'.freeze
89
+
90
+ # Event subclasses will have a SUBJECT_CLASS constant,
91
+ # which contains the class of the subject of the event, based on one of the
92
+ # values from the Event::EVENTS Hash.
93
+ SUBJECT_CLASS_CONST = 'SUBJECT_CLASS'.freeze
94
+
95
+ #### Attrbutes common to all events
96
+
97
+ # @return [Integer] The webhook id in the JSS that caused this event
98
+ attr_reader :webhook_id
99
+
100
+ # @return [String] The webhook name in the JSS that caused this event
101
+ attr_reader :webhook_name
102
+
103
+ # @return [Object] The subject of this event - i.e. the thing it acted upon.
104
+ # An instance of a class from either the Chook::HandledSubjects module
105
+ # or the Chook::TestSubjects module
106
+ attr_reader :subject
107
+
108
+ # @return [String, nil] If this event object was initialized with a JSON
109
+ # blob as from the JSS, it will be stored here.
110
+ attr_reader :raw_json
111
+
112
+ # @return [Hash, nil] If this event object was initialized with a JSON
113
+ # blob as from the JSS, the Hash parsed from it will be stored here.
114
+ attr_reader :parsed_json
115
+
116
+ # Args are a hash (or group of named params)
117
+ # with these possible keys:
118
+ # raw_json: [String] A raw JSON blob for a full event as sent by the JSS
119
+ # If this is present, all other keys are ignored and the instance is
120
+ # built with this data.
121
+ # parsed_json: [Hash] A pre-parsed JSON blob for a full event.
122
+ # If this is present, all other keys are ignored and the instance is
123
+ # built with this data (however raw_json wins if both are provided)
124
+ # webhook_event: [String] The name of the event, one of the keys of EVENTS
125
+ # webhook_id: [Integer] The id of the webhook defined in the JSS
126
+ # webhook_name: [String] The name of the webhook defined in the JSS
127
+ # The remaning keys are the attributes of the Subject subclass for this
128
+ # event. Any not provided will be nil.
129
+ #
130
+ def initialize(**args)
131
+ if args[:raw_json]
132
+ @raw_json = args[:raw_json]
133
+ @parsed_json = JSON.parse @raw_json, symbolize_names: true
134
+ elsif args[:parsed_json]
135
+ @parsed_json = args[:parsed_json]
136
+ end
137
+
138
+ if @parsed_json
139
+ @webhook_name = @parsed_json[:webhook][:name]
140
+ @webhook_id = @parsed_json[:webhook][:id]
141
+ subject_data = @parsed_json[:event]
142
+ else
143
+ @webhook_name = args.delete :webhook_name
144
+ @webhook_id = args.delete :webhook_id
145
+ subject_data = args
146
+ end
147
+
148
+ @subject = self.class::SUBJECT_CLASS.new subject_data
149
+ end
150
+
151
+ end # class Event
152
+
153
+ end # module