chook 1.0.0.b1

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.
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