ruby-jss 0.7.0 → 0.8.1

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 (109) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES.md +29 -22
  3. data/README.md +66 -86
  4. data/bin/jamfHelperBackgrounder +148 -0
  5. data/bin/netseg-update +0 -1
  6. data/lib/jss.rb +20 -9
  7. data/lib/jss/api_connection.rb +369 -295
  8. data/lib/jss/api_object.rb +651 -418
  9. data/lib/jss/api_object/account.rb +69 -77
  10. data/lib/jss/api_object/advanced_search.rb +201 -236
  11. data/lib/jss/api_object/advanced_search/advanced_computer_search.rb +42 -42
  12. data/lib/jss/api_object/advanced_search/advanced_mobile_device_search.rb +33 -43
  13. data/lib/jss/api_object/advanced_search/advanced_user_search.rb +33 -43
  14. data/lib/jss/api_object/building.rb +39 -52
  15. data/lib/jss/api_object/categorizable.rb +221 -0
  16. data/lib/jss/api_object/category.rb +81 -89
  17. data/lib/jss/api_object/computer.rb +486 -525
  18. data/lib/jss/api_object/computer_invitation.rb +73 -86
  19. data/lib/jss/api_object/criteriable.rb +6 -7
  20. data/lib/jss/api_object/ebook.rb +21 -0
  21. data/lib/jss/api_object/extendable.rb +6 -8
  22. data/lib/jss/api_object/group.rb +0 -3
  23. data/lib/jss/api_object/locatable.rb +19 -20
  24. data/lib/jss/api_object/mac_application.rb +21 -0
  25. data/lib/jss/api_object/mobile_device.rb +30 -21
  26. data/lib/jss/api_object/mobile_device_application.rb +447 -0
  27. data/lib/jss/api_object/mobile_device_configuration_profile.rb +21 -0
  28. data/lib/jss/api_object/osx_configuration_profile.rb +0 -3
  29. data/lib/jss/api_object/package.rb +21 -34
  30. data/lib/jss/api_object/peripheral.rb +16 -18
  31. data/lib/jss/api_object/policy.rb +5 -83
  32. data/lib/jss/api_object/purchasable.rb +11 -13
  33. data/lib/jss/api_object/scopable.rb +11 -12
  34. data/lib/jss/api_object/script.rb +3 -17
  35. data/lib/jss/api_object/self_servable.rb +419 -205
  36. data/lib/jss/api_object/self_servable/icon.rb +179 -0
  37. data/lib/jss/api_object/updatable.rb +35 -34
  38. data/lib/jss/api_object/uploadable.rb +72 -70
  39. data/lib/jss/api_object/user.rb +6 -7
  40. data/lib/jss/api_object/vppable.rb +117 -0
  41. data/lib/jss/client.rb +264 -225
  42. data/lib/jss/db_connection.rb +7 -5
  43. data/lib/jss/exceptions.rb +50 -42
  44. data/lib/jss/ruby_extensions.rb +8 -7
  45. data/lib/jss/ruby_extensions/object.rb +19 -0
  46. data/lib/jss/utility.rb +82 -40
  47. data/lib/jss/version.rb +1 -1
  48. metadata +37 -68
  49. data/bin/jss-webhook-server +0 -3
  50. data/lib/jss/webhooks.rb +0 -53
  51. data/lib/jss/webhooks/README.md +0 -269
  52. data/lib/jss/webhooks/configuration.rb +0 -213
  53. data/lib/jss/webhooks/data/sample_handlers/RestAPIOperation-executable +0 -91
  54. data/lib/jss/webhooks/data/sample_handlers/RestAPIOperation.rb +0 -45
  55. data/lib/jss/webhooks/data/sample_jsons/ComputerAdded.json +0 -27
  56. data/lib/jss/webhooks/data/sample_jsons/ComputerCheckIn.json +0 -27
  57. data/lib/jss/webhooks/data/sample_jsons/ComputerInventoryCompleted.json +0 -27
  58. data/lib/jss/webhooks/data/sample_jsons/ComputerPolicyFinished.json +0 -27
  59. data/lib/jss/webhooks/data/sample_jsons/ComputerPushCapabilityChanged.json +0 -27
  60. data/lib/jss/webhooks/data/sample_jsons/JSSShutdown.json +0 -14
  61. data/lib/jss/webhooks/data/sample_jsons/JSSStartup.json +0 -14
  62. data/lib/jss/webhooks/data/sample_jsons/MobileDeviceCheckIn.json +0 -26
  63. data/lib/jss/webhooks/data/sample_jsons/MobileDeviceCommandCompleted.json +0 -26
  64. data/lib/jss/webhooks/data/sample_jsons/MobileDeviceEnrolled.json +0 -26
  65. data/lib/jss/webhooks/data/sample_jsons/MobileDevicePushSent.json +0 -26
  66. data/lib/jss/webhooks/data/sample_jsons/MobileDeviceUnEnrolled.json +0 -26
  67. data/lib/jss/webhooks/data/sample_jsons/PatchSoftwareTitleUpdated.json +0 -14
  68. data/lib/jss/webhooks/data/sample_jsons/PushSent.json +0 -11
  69. data/lib/jss/webhooks/data/sample_jsons/RestAPIOperation.json +0 -15
  70. data/lib/jss/webhooks/data/sample_jsons/SCEPChallenge.json +0 -10
  71. data/lib/jss/webhooks/data/sample_jsons/SmartGroupComputerMembershipChange.json +0 -13
  72. data/lib/jss/webhooks/data/sample_jsons/SmartGroupMobileDeviceMembershipChange.json +0 -13
  73. data/lib/jss/webhooks/event.rb +0 -139
  74. data/lib/jss/webhooks/event/computer_added.rb +0 -38
  75. data/lib/jss/webhooks/event/computer_check_in.rb +0 -38
  76. data/lib/jss/webhooks/event/computer_inventory_completed.rb +0 -38
  77. data/lib/jss/webhooks/event/computer_policy_finished.rb +0 -38
  78. data/lib/jss/webhooks/event/computer_push_capability_changed.rb +0 -38
  79. data/lib/jss/webhooks/event/handlers.rb +0 -192
  80. data/lib/jss/webhooks/event/jss_shutdown.rb +0 -38
  81. data/lib/jss/webhooks/event/jss_startup.rb +0 -38
  82. data/lib/jss/webhooks/event/mobile_device_check_in.rb +0 -38
  83. data/lib/jss/webhooks/event/mobile_device_command_completed.rb +0 -38
  84. data/lib/jss/webhooks/event/mobile_device_enrolled.rb +0 -38
  85. data/lib/jss/webhooks/event/mobile_device_push_sent.rb +0 -38
  86. data/lib/jss/webhooks/event/mobile_device_unenrolled.rb +0 -38
  87. data/lib/jss/webhooks/event/patch_software_title_updated.rb +0 -38
  88. data/lib/jss/webhooks/event/push_sent.rb +0 -38
  89. data/lib/jss/webhooks/event/rest_api_operation.rb +0 -38
  90. data/lib/jss/webhooks/event/scep_challenge.rb +0 -38
  91. data/lib/jss/webhooks/event/smart_group_computer_membership_change.rb +0 -38
  92. data/lib/jss/webhooks/event/smart_group_mobile_device_membership_change.rb +0 -38
  93. data/lib/jss/webhooks/event/webhook.rb +0 -40
  94. data/lib/jss/webhooks/event_objects.rb +0 -112
  95. data/lib/jss/webhooks/event_objects/computer.rb +0 -49
  96. data/lib/jss/webhooks/event_objects/jss.rb +0 -36
  97. data/lib/jss/webhooks/event_objects/mobile_device.rb +0 -48
  98. data/lib/jss/webhooks/event_objects/patch_software_title_update.rb +0 -38
  99. data/lib/jss/webhooks/event_objects/push.rb +0 -33
  100. data/lib/jss/webhooks/event_objects/rest_api_operation.rb +0 -37
  101. data/lib/jss/webhooks/event_objects/scep_challenge.rb +0 -32
  102. data/lib/jss/webhooks/event_objects/smart_group.rb +0 -35
  103. data/lib/jss/webhooks/server_app.rb +0 -37
  104. data/lib/jss/webhooks/server_app/routes.rb +0 -27
  105. data/lib/jss/webhooks/server_app/routes/handle_webhook_event.rb +0 -39
  106. data/lib/jss/webhooks/server_app/routes/home.rb +0 -37
  107. data/lib/jss/webhooks/server_app/self_signed_cert.rb +0 -65
  108. data/lib/jss/webhooks/server_app/server.rb +0 -60
  109. data/lib/jss/webhooks/version.rb +0 -32
@@ -26,139 +26,139 @@
26
26
  ###
27
27
  module JSS
28
28
 
29
- ### Module Variables
29
+ # Module Variables
30
30
  #####################################
31
31
 
32
- ### Module Methods
32
+ # Module Methods
33
33
  #####################################
34
34
 
35
- ### Classes
35
+ # Classes
36
36
  #####################################
37
37
 
38
- ### This class is the parent to all JSS API objects. It provides standard methods and structures
39
- ### that apply to all API resouces.
40
- ###
41
- ### See the README.md file for general info about using subclasses of JSS::APIObject
42
- ###
43
- ### == Subclassing
44
- ###
45
- ### === Constructor
46
- ###
47
- ### In general, subclasses should do any class-specific argument checking before
48
- ### calling super, and then afterwards, use the contents of @init_data to populate
49
- ### any class-specific attributes. @id, @name, @rest_rsrc, and @in_jss are handled here.
50
- ###
51
- ### If a subclass can be looked up by some key other than :name or :id, the subclass must
52
- ### pass the keys as an Array in the second argument when calling super from #initialize.
53
- ### See {JSS::Computer#initialize} for an example of how to implement this feature.
54
- ###
55
- ### === Object Creation
56
- ###
57
- ### If a subclass should be able to be created in the JSS be sure to include {JSS::Creatable}
58
- ###
59
- ### The constructor should verify any extra required data (aside from :name) in the args before or after
60
- ### calling super.
61
- ###
62
- ### See {JSS::Creatable} for more details.
63
- ###
64
- ### === Object Modification
65
- ###
66
- ### If a subclass should be modifiable in the JSS, include {JSS::Updatable}, q.v. for details.
67
- ###
68
- ### === Object Deletion
69
- ###
70
- ### All subclasses can be deleted in the JSS.
71
- ###
72
- ### === Required Constants
73
- ###
74
- ### Subclasses *must* provide certain Constants in order to correctly interpret API data and
75
- ### communicate with the API.
76
- ###
77
- ### ==== RSRC_BASE = [String], The base for REST resources of this class
78
- ###
79
- ### e.g. 'computergroups' in "https://casper.mycompany.com:8443/JSSResource/computergroups/id/12"
80
- ###
81
- ### ==== RSRC_LIST_KEY = [Symbol] The Hash key for the JSON list output of all objects of this class in the JSS.
82
- ###
83
- ### e.g. the JSON output of resource "JSSResource/computergroups" is a hash
84
- ### with one item (an Array of computergroups). That item's key is the Symbol :computer_groups
85
- ###
86
- ### ==== RSRC_OBJECT_KEY = [Symbol] The Hash key used for individual JSON object output.
87
- ### It's also used in various error messages
88
- ###
89
- ### e.g. the JSON output of the resource "JSSResource/computergroups/id/436" is
90
- ### a hash with one item (another hash with details of one computergroup).
91
- ### That item's key is the Symbol :computer_group
92
- ###
93
- ### ==== VALID_DATA_KEYS = [Array<Symbol>] The Hash keys used to verify validity of :data
94
- ### When instantiating a subclass using :data => somehash, some minimal checks are performed
95
- ### to ensure the data is valid for the subclass
96
- ###
97
- ### The Symbols in this Array are compared to the keys of the hash provided.
98
- ### If any of these don't exist in the hash's keys, then the :data is
99
- ### not valid and an exception is raised.
100
- ###
101
- ### The keys :id and :name must always exist in the hash.
102
- ### If only :id and :name are valid, VALID_DATA_KEYS should be an empty array.
103
- ###
104
- ### e.g. for a department, only :id and :name are valid, so VALID_DATA_KEYS is an empty Array ([])
105
- ### but for a computer group, the keys :computers and :is_smart must be present as well.
106
- ### so VALID_DATA_KEYS will be [:computers, :is_smart]
107
- ###
108
- ### *NOTE* Some API objects have data broken into subsections, in which case the
109
- ### VALID_DATA_KEYS are expected in the section :general.
110
- ###
38
+ # This class is the parent to all JSS API objects. It provides standard methods and structures
39
+ # that apply to all API resouces.
40
+ #
41
+ # See the README.md file for general info about using subclasses of JSS::APIObject
42
+ #
43
+ # == Subclassing
44
+ #
45
+ # === Constructor
46
+ #
47
+ # In general, subclasses should do any class-specific argument checking before
48
+ # calling super, and then afterwards, use the contents of @init_data to populate
49
+ # any class-specific attributes. @id, @name, @rest_rsrc, and @in_jss are handled here.
50
+ #
51
+ # If a subclass can be looked up by some key other than :name or :id, the subclass must
52
+ # pass the keys as an Array in the second argument when calling super from #initialize.
53
+ # See {JSS::Computer#initialize} for an example of how to implement this feature.
54
+ #
55
+ # === Object Creation
56
+ #
57
+ # If a subclass should be able to be created in the JSS be sure to include {JSS::Creatable}
58
+ #
59
+ # The constructor should verify any extra required data (aside from :name) in the args before or after
60
+ # calling super.
61
+ #
62
+ # See {JSS::Creatable} for more details.
63
+ #
64
+ # === Object Modification
65
+ #
66
+ # If a subclass should be modifiable in the JSS, include {JSS::Updatable}, q.v. for details.
67
+ #
68
+ # === Object Deletion
69
+ #
70
+ # All subclasses can be deleted in the JSS.
71
+ #
72
+ # === Required Constants
73
+ #
74
+ # Subclasses *must* provide certain Constants in order to correctly interpret API data and
75
+ # communicate with the API.
76
+ #
77
+ # ==== RSRC_BASE = [String], The base for REST resources of this class
78
+ #
79
+ # e.g. 'computergroups' in "https://casper.mycompany.com:8443/JSSResource/computergroups/id/12"
80
+ #
81
+ # ==== RSRC_LIST_KEY = [Symbol] The Hash key for the JSON list output of all objects of this class in the JSS.
82
+ #
83
+ # e.g. the JSON output of resource "JSSResource/computergroups" is a hash
84
+ # with one item (an Array of computergroups). That item's key is the Symbol :computer_groups
85
+ #
86
+ # ==== RSRC_OBJECT_KEY = [Symbol] The Hash key used for individual JSON object output.
87
+ # It's also used in various error messages
88
+ #
89
+ # e.g. the JSON output of the resource "JSSResource/computergroups/id/436" is
90
+ # a hash with one item (another hash with details of one computergroup).
91
+ # That item's key is the Symbol :computer_group
92
+ #
93
+ # ==== VALID_DATA_KEYS = [Array<Symbol>] The Hash keys used to verify validity of :data
94
+ # When instantiating a subclass using :data => somehash, some minimal checks are performed
95
+ # to ensure the data is valid for the subclass
96
+ #
97
+ # The Symbols in this Array are compared to the keys of the hash provided.
98
+ # If any of these don't exist in the hash's keys, then the :data is
99
+ # not valid and an exception is raised.
100
+ #
101
+ # The keys :id and :name must always exist in the hash.
102
+ # If only :id and :name are valid, VALID_DATA_KEYS should be an empty array.
103
+ #
104
+ # e.g. for a department, only :id and :name are valid, so VALID_DATA_KEYS is an empty Array ([])
105
+ # but for a computer group, the keys :computers and :is_smart must be present as well.
106
+ # so VALID_DATA_KEYS will be [:computers, :is_smart]
107
+ #
108
+ # *NOTE* Some API objects have data broken into subsections, in which case the
109
+ # VALID_DATA_KEYS are expected in the section :general.
110
+ #
111
111
  class APIObject
112
112
 
113
- ### Mix-Ins
113
+ # Mix-Ins
114
114
  #####################################
115
115
 
116
- ### Class Variables
116
+ # Class Variables
117
117
  #####################################
118
118
 
119
- ### This Hash holds the most recent API query for a list of all items in any subclass,
120
- ### keyed by the subclass's RSRC_LIST_KEY. See the self.all class method.
121
- ###
122
- ### When the .all method is called without an argument, and this hash has
123
- ### a matching value, the value is returned, rather than requerying the
124
- ### API. The first time a class calls .all, or whnever refresh is
125
- ### not false, the API is queried and the value in this hash is updated.
126
- ###
119
+ # This Hash holds the most recent API query for a list of all items in any subclass,
120
+ # keyed by the subclass's RSRC_LIST_KEY. See the self.all class method.
121
+ #
122
+ # When the .all method is called without an argument, and this hash has
123
+ # a matching value, the value is returned, rather than requerying the
124
+ # API. The first time a class calls .all, or whnever refresh is
125
+ # not false, the API is queried and the value in this hash is updated.
126
+ #
127
127
  @@all_items = {}
128
128
 
129
- ### Class Methods
129
+ # Class Methods
130
130
  #####################################
131
131
 
132
- ### Return an Array of Hashes for all objects of this subclass in the JSS.
133
- ###
134
- ### This method is only valid in subclasses of JSS::APIObject, and is
135
- ### the parsed JSON output of an API query for the resource defined in the subclass's RSRC_BASE,
136
- ### e.g. for JSS::Computer, with the RSRC_BASE of :computers,
137
- ### This method retuens the output of the 'JSSResource/computers' resource,
138
- ### which is a list of all computers in the JSS.
139
- ###
140
- ### Each item in the Array is a Hash with at least two keys, :id and :name.
141
- ### The class methods .all_ids and .all_names provide easier access to those data
142
- ### as mapped Arrays.
143
- ###
144
- ### Some API classes provide other data in each Hash, e.g. :udid (for computers
145
- ### and mobile devices) or :is_smart (for groups).
146
- ###
147
- ### Subclasses implementing those API classes should provide .all_xxx
148
- ### class methods for accessing those other values as mapped Arrays,
149
- ### e.g. JSS::Computer.all_udids
150
- ###
151
- ### The results of the first query for each subclass is stored in @@all_items
152
- ### and returned at every future call, so as to not requery the server every time.
153
- ###
154
- ### To force requerying to get updated data, provided a non-false argument.
155
- ### I usually use :refresh, so that it's obvious what I'm doing, but true, 1,
156
- ### or anything besides false or nil will work.
157
- ###
158
- ### @param refresh[Boolean] should the data be re-queried from the API?
159
- ###
160
- ### @return [Array<Hash{:name=>String, :id=> Integer}>]
161
- ###
132
+ # Return an Array of Hashes for all objects of this subclass in the JSS.
133
+ #
134
+ # This method is only valid in subclasses of JSS::APIObject, and is
135
+ # the parsed JSON output of an API query for the resource defined in the subclass's RSRC_BASE,
136
+ # e.g. for JSS::Computer, with the RSRC_BASE of :computers,
137
+ # This method retuens the output of the 'JSSResource/computers' resource,
138
+ # which is a list of all computers in the JSS.
139
+ #
140
+ # Each item in the Array is a Hash with at least two keys, :id and :name.
141
+ # The class methods .all_ids and .all_names provide easier access to those data
142
+ # as mapped Arrays.
143
+ #
144
+ # Some API classes provide other data in each Hash, e.g. :udid (for computers
145
+ # and mobile devices) or :is_smart (for groups).
146
+ #
147
+ # Subclasses implementing those API classes should provide .all_xxx
148
+ # class methods for accessing those other values as mapped Arrays,
149
+ # e.g. JSS::Computer.all_udids
150
+ #
151
+ # The results of the first query for each subclass is stored in @@all_items
152
+ # and returned at every future call, so as to not requery the server every time.
153
+ #
154
+ # To force requerying to get updated data, provided a non-false argument.
155
+ # I usually use :refresh, so that it's obvious what I'm doing, but true, 1,
156
+ # or anything besides false or nil will work.
157
+ #
158
+ # @param refresh[Boolean] should the data be re-queried from the API?
159
+ #
160
+ # @return [Array<Hash{:name=>String, :id=> Integer}>]
161
+ #
162
162
  def self.all(refresh = false)
163
163
  raise JSS::UnsupportedError, '.all can only be called on subclasses of JSS::APIObject' if self == JSS::APIObject
164
164
  @@all_items[self::RSRC_LIST_KEY] = nil if refresh
@@ -166,92 +166,92 @@ module JSS
166
166
  @@all_items[self::RSRC_LIST_KEY] = JSS::API.get_rsrc(self::RSRC_BASE)[self::RSRC_LIST_KEY]
167
167
  end
168
168
 
169
- ### Returns an Array of the JSS id numbers of all the members
170
- ### of the subclass.
171
- ###
172
- ### e.g. When called from subclass JSS::Computer,
173
- ### returns the id's of all computers in the JSS
174
- ###
175
- ### @param refresh[Boolean] should the data be re-queried from the API?
176
- ###
177
- ### @return [Array<Integer>] the ids of all items of this subclass in the JSS
178
- ###
169
+ # Returns an Array of the JSS id numbers of all the members
170
+ # of the subclass.
171
+ #
172
+ # e.g. When called from subclass JSS::Computer,
173
+ # returns the id's of all computers in the JSS
174
+ #
175
+ # @param refresh[Boolean] should the data be re-queried from the API?
176
+ #
177
+ # @return [Array<Integer>] the ids of all items of this subclass in the JSS
178
+ #
179
179
  def self.all_ids(refresh = false)
180
180
  all(refresh).map { |i| i[:id] }
181
181
  end
182
182
 
183
- ### Returns an Array of the JSS names of all the members
184
- ### of the subclass.
185
- ###
186
- ### e.g. When called from subclass JSS::Computer,
187
- ### returns the names of all computers in the JSS
188
- ###
189
- ### @param refresh[Boolean] should the data be re-queried from the API?
190
- ###
191
- ### @return [Array<String>] the names of all item of this subclass in the JSS
192
- ###
183
+ # Returns an Array of the JSS names of all the members
184
+ # of the subclass.
185
+ #
186
+ # e.g. When called from subclass JSS::Computer,
187
+ # returns the names of all computers in the JSS
188
+ #
189
+ # @param refresh[Boolean] should the data be re-queried from the API?
190
+ #
191
+ # @return [Array<String>] the names of all item of this subclass in the JSS
192
+ #
193
193
  def self.all_names(refresh = false)
194
194
  all(refresh).map { |i| i[:name] }
195
195
  end
196
196
 
197
- ### Return a hash of all objects of this subclass
198
- ### in the JSS where the key is the id, and the value
199
- ### is some other key in the data items returned by the JSS::APIObject.all.
200
- ###
201
- ### If the other key doesn't exist in the API
202
- ### data, (eg :udid for JSS::Department) the values will be nil.
203
- ###
204
- ### Use this method to map ID numbers to other identifiers returned
205
- ### by the API list resources. Invert its result to map the other
206
- ### identfier to ids.
207
- ###
208
- ### @example
209
- ### JSS::Computer.map_all_ids_to(:name)
210
- ###
211
- ### # Returns, eg {2 => "kimchi", 5 => "mantis"}
212
- ###
213
- ### JSS::Computer.map_all_ids_to(:name).invert
214
- ###
215
- ### # Returns, eg {"kimchi" => 2, "mantis" => 5}
216
- ###
217
- ### @param other_key[Symbol] the other data key with which to associate each id
218
- ###
219
- ### @param refresh[Boolean] should the data re-queried from the API?
220
- ###
221
- ### @return [Hash{Integer => Oject}] the associated ids and data
222
- ###
197
+ # Return a hash of all objects of this subclass
198
+ # in the JSS where the key is the id, and the value
199
+ # is some other key in the data items returned by the JSS::APIObject.all.
200
+ #
201
+ # If the other key doesn't exist in the API
202
+ # data, (eg :udid for JSS::Department) the values will be nil.
203
+ #
204
+ # Use this method to map ID numbers to other identifiers returned
205
+ # by the API list resources. Invert its result to map the other
206
+ # identfier to ids.
207
+ #
208
+ # @example
209
+ # JSS::Computer.map_all_ids_to(:name)
210
+ #
211
+ # # Returns, eg {2 => "kimchi", 5 => "mantis"}
212
+ #
213
+ # JSS::Computer.map_all_ids_to(:name).invert
214
+ #
215
+ # # Returns, eg {"kimchi" => 2, "mantis" => 5}
216
+ #
217
+ # @param other_key[Symbol] the other data key with which to associate each id
218
+ #
219
+ # @param refresh[Boolean] should the data re-queried from the API?
220
+ #
221
+ # @return [Hash{Integer => Oject}] the associated ids and data
222
+ #
223
223
  def self.map_all_ids_to(other_key, refresh = false)
224
224
  h = {}
225
225
  all(refresh).each { |i| h[i[:id]] = i[other_key] }
226
226
  h
227
227
  end
228
228
 
229
- ### Return an Array of JSS::APIObject subclass instances
230
- ### e.g when called on JSS::Package, return all JSS::Package
231
- ### objects in the JSS.
232
- ###
233
- ### NOTE: This may be slow as it has to look up each object individually!
234
- ### use it wisely.
235
- ###
236
- ### @param refresh[Boolean] should the data re-queried from the API?
237
- ###
238
- ### @return [Hash{Integer => Object}] the objects requested
229
+ # Return an Array of JSS::APIObject subclass instances
230
+ # e.g when called on JSS::Package, return all JSS::Package
231
+ # objects in the JSS.
232
+ #
233
+ # NOTE: This may be slow as it has to look up each object individually!
234
+ # use it wisely.
235
+ #
236
+ # @param refresh[Boolean] should the data re-queried from the API?
237
+ #
238
+ # @return [Hash{Integer => Object}] the objects requested
239
239
  def self.all_objects(refresh = false)
240
240
  objects_key = "#{self::RSRC_LIST_KEY}_objects".to_sym
241
241
  @@all_items[objects_key] = nil if refresh
242
242
  return @@all_items[objects_key] if @@all_items[objects_key]
243
- @@all_items[objects_key] = all(refresh = false).map { |o| new id: o[:id] }
244
- end
245
-
246
- ### Return true or false if an object of this subclass
247
- ### with the given name or id exists on the server
248
- ###
249
- ### @param identfier [String,Integer] The name or id of object to check for
250
- ###
251
- ### @param refresh [Boolean] Should the data be re-read from the server
252
- ###
253
- ### @return [Boolean] does an object with the given name or id exist?
254
- ###
243
+ @@all_items[objects_key] = all(refresh).map { |o| new id: o[:id] }
244
+ end
245
+
246
+ # Return true or false if an object of this subclass
247
+ # with the given name or id exists on the server
248
+ #
249
+ # @param identfier [String,Integer] The name or id of object to check for
250
+ #
251
+ # @param refresh [Boolean] Should the data be re-read from the server
252
+ #
253
+ # @return [Boolean] does an object with the given name or id exist?
254
+ #
255
255
  def self.exist?(identfier, refresh = false)
256
256
  case identfier
257
257
  when Integer
@@ -263,18 +263,18 @@ module JSS
263
263
  end
264
264
  end
265
265
 
266
- ### Return an id or nil if an object of this subclass
267
- ### with the given name or id exists on the server
268
- ###
269
- ### Subclasses may want to override this method to support more
270
- ### identifiers than name and id.
271
- ###
272
- ### @param identfier [String,Integer] The name or id of object to check for
273
- ###
274
- ### @param refresh [Boolean] Should the data be re-read from the server
275
- ###
276
- ### @return [Integer, nil] the id of the matching object, or nil if it doesn't exist
277
- ###
266
+ # Return an id or nil if an object of this subclass
267
+ # with the given name or id exists on the server
268
+ #
269
+ # Subclasses may want to override this method to support more
270
+ # identifiers than name and id.
271
+ #
272
+ # @param identfier [String,Integer] The name or id of object to check for
273
+ #
274
+ # @param refresh [Boolean] Should the data be re-read from the server
275
+ #
276
+ # @return [Integer, nil] the id of the matching object, or nil if it doesn't exist
277
+ #
278
278
  def self.valid_id(identfier, refresh = false)
279
279
  case identfier
280
280
  when Integer
@@ -286,78 +286,77 @@ module JSS
286
286
  end
287
287
  end
288
288
 
289
- ### Convert an Array of Hashes of API object data to a
290
- ### REXML element.
291
- ###
292
- ### Given an Array of Hashes of items in the subclass
293
- ### where each Hash has at least an :id or a :name key,
294
- ### (as what comes from the .all class method)
295
- ### return a REXML <classes> element
296
- ### with one <class> element per Hash member.
297
- ###
298
- ### @example
299
- ### # for class JSS::Computer
300
- ### some_comps = [{:id=>2, :name=>"kimchi"},{:id=>5, :name=>"mantis"}]
301
- ### xml_names = JSS::Computer.xml_list some_comps
302
- ### puts xml_names # output manually formatted for clarity, xml.to_s has no newlines between elements
303
- ###
304
- ### <computers>
305
- ### <computer>
306
- ### <name>kimchi</name>
307
- ### </computer>
308
- ### <computer>
309
- ### <name>mantis</name>
310
- ### </computer>
311
- ### </computers>
312
- ###
313
- ### xml_ids = JSS::Computer.xml_list some_comps, :id
314
- ### puts xml_names # output manually formatted for clarity, xml.to_s has no newlines between elements
315
- ###
316
- ### <computers>
317
- ### <computer>
318
- ### <id>2</id>
319
- ### </computer>
320
- ### <computer>
321
- ### <id>5</id>
322
- ### </computer>
323
- ### </computers>
324
- ###
325
- ### @param array[Array<Hash{:name=>String, :id =>Integer, Symbol=>#to_s}>] the Array of subclass data to convert
326
- ###
327
- ### @param content[Symbol] the Hash key to use as the inner element for each member of the Array
328
- ###
329
- ### @return [REXML::Element] the XML element representing the data
330
- ###
289
+ # Convert an Array of Hashes of API object data to a
290
+ # REXML element.
291
+ #
292
+ # Given an Array of Hashes of items in the subclass
293
+ # where each Hash has at least an :id or a :name key,
294
+ # (as what comes from the .all class method)
295
+ # return a REXML <classes> element
296
+ # with one <class> element per Hash member.
297
+ #
298
+ # @example
299
+ # # for class JSS::Computer
300
+ # some_comps = [{:id=>2, :name=>"kimchi"},{:id=>5, :name=>"mantis"}]
301
+ # xml_names = JSS::Computer.xml_list some_comps
302
+ # puts xml_names # output manually formatted for clarity, xml.to_s has no newlines between elements
303
+ #
304
+ # <computers>
305
+ # <computer>
306
+ # <name>kimchi</name>
307
+ # </computer>
308
+ # <computer>
309
+ # <name>mantis</name>
310
+ # </computer>
311
+ # </computers>
312
+ #
313
+ # xml_ids = JSS::Computer.xml_list some_comps, :id
314
+ # puts xml_names # output manually formatted for clarity, xml.to_s has no newlines between elements
315
+ #
316
+ # <computers>
317
+ # <computer>
318
+ # <id>2</id>
319
+ # </computer>
320
+ # <computer>
321
+ # <id>5</id>
322
+ # </computer>
323
+ # </computers>
324
+ #
325
+ # @param array[Array<Hash{:name=>String, :id =>Integer, Symbol=>#to_s}>] the Array of subclass data to convert
326
+ #
327
+ # @param content[Symbol] the Hash key to use as the inner element for each member of the Array
328
+ #
329
+ # @return [REXML::Element] the XML element representing the data
330
+ #
331
331
  def self.xml_list(array, content = :name)
332
332
  JSS.item_list_to_rexml_list self::RSRC_LIST_KEY, self::RSRC_OBJECT_KEY, array, content
333
333
  end
334
334
 
335
- ### Some API objects contain references to other API objects. Usually those
336
- ### references are a Hash containing the :id and :name of the target. Sometimes,
337
- ### however the reference is just the name of the target.
338
- ###
339
- ### A Script has a property :category, which comes from the API as
340
- ### a String, the name of the category for that script. e.g. "GoodStuff"
341
- ###
342
- ### A Policy also has a property :category, but it comes from the API as a Hash
343
- ### with both the name and id, e.g. !{:id => 8, :name => "GoodStuff"}
344
- ###
345
- ### When that reference is to a single thing (like the category to which something belongs)
346
- ### APIObject subclasses usually store only the name, and use the name when
347
- ### returning data to the API.
348
- ###
349
- ### When an object references a list of related objects
350
- ### (like the computers assigned to a user) that list will be and Array of Hashes
351
- ### as above, with both the :id and :name
352
- ###
353
- ###
354
- ### This method is just a handy way to extract the name regardless of how it comes
355
- ### from the API. Most APIObject subclasses use it in their #initialize method
356
- ###
357
- ### @param a_thing[String,Array] the api data from which we're extracting the name
358
- ###
359
- ### @return [String] the name extracted from a_thing
360
- ###
335
+ # Some API objects contain references to other API objects. Usually those
336
+ # references are a Hash containing the :id and :name of the target. Sometimes,
337
+ # however the reference is just the name of the target.
338
+ #
339
+ # A Script has a property :category, which comes from the API as
340
+ # a String, the name of the category for that script. e.g. "GoodStuff"
341
+ #
342
+ # A Policy also has a property :category, but it comes from the API as a Hash
343
+ # with both the name and id, e.g. !{:id => 8, :name => "GoodStuff"}
344
+ #
345
+ # When that reference is to a single thing (like the category to which something belongs)
346
+ # APIObject subclasses usually store only the name, and use the name when
347
+ # returning data to the API.
348
+ #
349
+ # When an object references a list of related objects
350
+ # (like the computers assigned to a user) that list will be and Array of Hashes
351
+ # as above, with both the :id and :name
352
+ #
353
+ # This method is just a handy way to extract the name regardless of how it comes
354
+ # from the API. Most APIObject subclasses use it in their #initialize method
355
+ #
356
+ # @param a_thing[String,Array] the api data from which we're extracting the name
357
+ #
358
+ # @return [String] the name extracted from a_thing
359
+ #
361
360
  def self.get_name(a_thing)
362
361
  case a_thing
363
362
  when String
@@ -369,194 +368,232 @@ module JSS
369
368
  end
370
369
  end
371
370
 
371
+ # Retrieve an object from the API.
372
+ #
373
+ # This is the preferred way to retrieve existing objects from the JSS.
374
+ # It's a wrapper for using APIObject.new
375
+ # and avoids the confusion of using ruby's .new class method when you're not
376
+ # creating a new object.
377
+ #
378
+ # For creating new objects in the JSS, use {APIObject.make}
379
+ #
380
+ # @param args[Hash] The data for fetching an object, such as id: or name:
381
+ # See {APIObject#initialize}
382
+ #
383
+ # @return [APIObject] The ruby-instance of a JSS object
384
+ #
385
+ def self.fetch(**args)
386
+ raise JSS::UnsupportedError, 'JSS::APIObject cannot be instantiated' if self.class == JSS::APIObject
387
+ raise ArgumentError, 'Use .create to create new JSS objects' if args[:id] == :new
388
+ new args
389
+ end
390
+
391
+ # Make a ruby instance of a not-yet-existing APIObject.
392
+ #
393
+ # This is the preferred way to create new objects in the JSS.
394
+ # It's a wrapper for using APIObject.new with the 'id: :new' parameter.
395
+ # and helps avoid the confusion of using ruby's .new class method for making
396
+ # ruby instances.
397
+ #
398
+ # For retrieving existing objects in the JSS, use {APIObject.fetch}
399
+ #
400
+ # For actually creating the object in the JSS, see {APIObject#create}
401
+ #
402
+ # @param args[Hash] The data for creating an object, such as name:
403
+ # See {APIObject#initialize}
404
+ #
405
+ # @return [APIObject] The un-created ruby-instance of a JSS object
406
+ #
407
+ def self.make(**args)
408
+ raise JSS::UnsupportedError, 'JSS::APIObject cannot be instantiated' if self.class == JSS::APIObject
409
+ raise ArgumentError, "Use '#{self.class}.fetch id: xx' to retrieve existing JSS objects" if args[:id]
410
+ args[:id] = :new
411
+ new args
412
+ end
413
+
414
+
372
415
  ### Class Constants
373
416
  #####################################
374
417
 
375
- ### These Symbols are added to VALID_DATA_KEYS for performing the
376
- ### :data validity test described above.
377
- ###
418
+ # These Symbols are added to VALID_DATA_KEYS for performing the
419
+ # :data validity test described above.
420
+ #
378
421
  REQUIRED_DATA_KEYS = [:id, :name].freeze
379
422
 
380
- ### By default, these keys are available for object lookups
381
- ### Others can be added by subclasses using an array of them
382
- ### as the second argument to super(initialize)
383
- ### The keys must be Symbols that match the keyname in the resource url.
384
- ### e.g. :serialnumber for JSSResource/computers/serialnumber/xxxxx
385
- ###
423
+ # By default, these keys are available for object lookups
424
+ # Others can be added by subclasses using an array of them
425
+ # as the second argument to super(initialize)
426
+ # The keys must be Symbols that match the keyname in the resource url.
427
+ # e.g. :serialnumber for JSSResource/computers/serialnumber/xxxxx
428
+ #
386
429
  DEFAULT_LOOKUP_KEYS = [:id, :name].freeze
387
430
 
388
- ### Attributes
431
+ # Attributes
389
432
  #####################################
390
433
 
391
- ### @return [Integer] the JSS id number
434
+ # @return [Integer] the JSS id number
392
435
  attr_reader :id
393
436
 
394
- ### @return [String] the name
437
+ # @return [String] the name
395
438
  attr_reader :name
396
439
 
397
- ### @return [Boolean] is it in the JSS?
440
+ # @return [Boolean] is it in the JSS?
398
441
  attr_reader :in_jss
399
442
 
400
- ### @return [String] the Rest resource for API access (the part after "JSSResource/" )
443
+ # @return [String] the Rest resource for API access (the part after "JSSResource/" )
401
444
  attr_reader :rest_rsrc
402
445
 
403
- ### Constructor
446
+ # Constructor
404
447
  #####################################
405
448
 
406
- ### The args hash must include :id, :name, or :data.
407
- ### * :id or :name will be looked up via the API
408
- ### * * if the subclass includes JSS::Creatable, :id can be :new, to create a new object in the JSS.
409
- ### and :name is required
410
- ### * :data must be the JSON output of a separate {JSS::APIConnection} query (a Hash of valid object data)
411
- ###
412
- ### Some subclasses can accept other options, by pasing their keys in a final Array
413
- ###
414
- ### @param args[Hash] the data for looking up, or constructing, a new object.
415
- ###
416
- ### @option args :id[Integer] the jss id to look up
417
- ###
418
- ### @option args :name[String] the name to look up
419
- ###
420
- ### @option args :data[Hash] the JSON output of a separate {JSS::APIConnection} query
421
- ###
422
- ### @param other_lookup_keys[Array<Symbol>] Hash keys other than :id and :name, by which an API
423
- ### lookup may be performed.
424
- ###
449
+ # The args hash must include :id, :name, or :data.
450
+ # * :id or :name will be looked up via the API
451
+ # * * if the subclass includes JSS::Creatable, :id can be :new, to create a new object in the JSS.
452
+ # and :name is required
453
+ # * :data must be the JSON output of a separate {JSS::APIConnection} query (a Hash of valid object data)
454
+ #
455
+ # Some subclasses can accept other options, by pasing their keys in a final Array
456
+ #
457
+ # @param args[Hash] the data for looking up, or constructing, a new object.
458
+ #
459
+ # @option args :id[Integer] the jss id to look up
460
+ #
461
+ # @option args :name[String] the name to look up
462
+ #
463
+ # @option args :data[Hash] the JSON output of a separate {JSS::APIConnection} query
464
+ #
465
+ # @param other_lookup_keys[Array<Symbol>] Hash keys other than :id and :name, by which an API
466
+ # lookup may be performed.
467
+ #
425
468
  def initialize(args = {}, other_lookup_keys = [])
469
+ args[:other_lookup_keys] ||= other_lookup_keys
470
+
471
+ raise JSS::UnsupportedError, 'JSS::APIObject cannot be instantiated' if self.class == JSS::APIObject
472
+
473
+ ### what lookup key are we using, if any?
474
+ lookup_keys = DEFAULT_LOOKUP_KEYS
475
+ lookup_keys += self.class::OTHER_LOOKUP_KEYS if defined? self.class::OTHER_LOOKUP_KEYS
476
+ lookup_key = (lookup_keys & args.keys)[0]
426
477
 
427
478
  ####### Previously looked-up JSON data
479
+ # DEPRECATED: pre-lookedup data is never used
480
+ # and support for it will be going away.
428
481
  if args[:data]
429
482
 
430
483
  @init_data = args[:data]
431
484
 
432
- ### Does this data come in subsets?
433
- @got_subsets = @init_data[:general].is_a?(Hash)
434
-
435
- ### data must include all they keys in REQUIRED_DATA_KEYS + VALID_DATA_KEYS
436
- ### in either the main hash keys or the :general sub-hash, if it exists
437
- hash_to_check = @got_subsets ? @init_data[:general] : @init_data
438
- combined_valid_keys = self.class::REQUIRED_DATA_KEYS + self.class::VALID_DATA_KEYS
439
- keys_ok = (hash_to_check.keys & combined_valid_keys).count == combined_valid_keys.count
440
- unless keys_ok
441
- raise(
442
- JSS::InvalidDataError,
443
- ":data is not valid JSON for a #{self.class::RSRC_OBJECT_KEY} from the API. It needs at least the keys :#{combined_valid_keys.join ', :'}"
444
- )
445
- end
446
-
447
- ### and the id must be in the jss
448
- raise NoSuchItemError, "No #{self.class::RSRC_OBJECT_KEY} with JSS id: #{@init_data[:id]}" unless \
449
- self.class.all_ids.include? hash_to_check[:id]
485
+ validate_external_init_data
450
486
 
451
487
  ###### Make a new one in the JSS, but only if we've included the Creatable module
452
488
  elsif args[:id] == :new
453
489
 
454
- raise JSS::UnsupportedError, "Creating #{self.class::RSRC_LIST_KEY} isn't yet supported. Please use other Casper workflows." \
455
- unless defined? self.class::CREATABLE
490
+ validate_init_for_creation(args)
491
+ setup_object_for_creation(args)
492
+ return
456
493
 
457
- raise JSS::MissingDataError, "You must provide a :name for a new #{self.class::RSRC_OBJECT_KEY}." \
458
- unless args[:name]
494
+ ###### Look up the data via the API
495
+ else
496
+ @init_data = look_up_object_data(args)
497
+ end ## end arg parsing
459
498
 
460
- raise JSS::AlreadyExistsError, "A #{self.class::RSRC_OBJECT_KEY} already exists with the name '#{args[:name]}'" \
461
- if self.class.all_names.include? args[:name]
499
+ parse_init_data
500
+ @need_to_update = false
501
+ end # init
462
502
 
463
- ### NOTE: subclasses may want to pre-populate more keys in @init_data when :id == :new
464
- ### then parse them into attributes later.
465
- @name = args[:name]
466
- @init_data = { name: args[:name] }
467
- @in_jss = false
468
- @rest_rsrc = "#{self.class::RSRC_BASE}/name/#{CGI.escape @name}"
469
- @need_to_update = true
470
- return
503
+ # Public Instance Methods
504
+ #####################################
471
505
 
472
- ###### Look up the data via the API
506
+ # Either Create or Update this object in the JSS
507
+ #
508
+ # If this item is creatable or updatable, then
509
+ # create it if needed, or update it if it already exists.
510
+ #
511
+ # @return [Integer] the id of the item created or updated
512
+ #
513
+ def save
514
+ if @in_jss
515
+ raise JSS::UnsupportedError, 'Updating this object in the JSS is currently not supported' unless updatable?
516
+ update
473
517
  else
474
- ### what lookup key are we using?
475
- combined_lookup_keys = self.class::DEFAULT_LOOKUP_KEYS + other_lookup_keys
476
- lookup_key = (combined_lookup_keys & args.keys)[0]
518
+ raise JSS::UnsupportedError, 'Creating this object in the JSS is currently not supported' unless creatable?
519
+ create
520
+ end
521
+ end
477
522
 
478
- raise JSS::MissingDataError, "Args must include :#{combined_lookup_keys.join(', :')}, or :data" unless lookup_key
523
+ # Mix-in Modules.
524
+ # Each module made for mixing in to APIObject subclasses
525
+ # sets an appropriate constant to true.
526
+ # These methods provide a simple way to programattically determine
527
+ # if an object has one of the mixed-in modules available.
479
528
 
480
- rsrc = "#{self.class::RSRC_BASE}/#{lookup_key}/#{args[lookup_key]}"
529
+ # @return [Boolean] See {JSS::Creatable}
530
+ def creatable?
531
+ defined? self.class::CREATABLE
532
+ end
481
533
 
482
- begin
483
- @init_data = JSS::API.get_rsrc(rsrc)[self.class::RSRC_OBJECT_KEY]
534
+ # @return [Boolean] See {JSS::Updatable}
535
+ def updatable?
536
+ defined? self.class::UPDATABLE
537
+ end
484
538
 
485
- ### If we're looking up by id or name and we're here,
486
- ### then we have it regardless of which subset it's in
487
- ### otherwise, first assume they are in the init hash
488
- @id = lookup_key == :id ? args[lookup_key] : @init_data[:id]
489
- @name = lookup_key == :name ? args[lookup_key] : @init_data[:name]
539
+ # @return [Boolean] See {JSS::Categorizable}
540
+ def categorizable?
541
+ defined? self.class::CATEGORIZABLE
542
+ end
490
543
 
491
- rescue RestClient::ResourceNotFound
492
- raise NoSuchItemError, "No #{self.class::RSRC_OBJECT_KEY} found matching: #{args[:name] ? args[:name] : args[:id]}"
493
- end
494
- end ## end arg parsing
544
+ # @return [Boolean] See {JSS::VPPable}
545
+ def vppable?
546
+ defined? self.class::VPPABLE
547
+ end
495
548
 
496
- # Find the "main" subset which contains :id and :name
497
- #
498
- # If they aren't at the top-level of the init hash they are in a subset hash,
499
- # usually :general, but sometimes someething else,
500
- # like ldap servers, which have them in :connection
501
- # Whereever both :id and :name are, that's the main subset
502
-
503
- @init_data.keys.each do |subset|
504
- @main_subset = @init_data[subset] if @init_data[subset].is_a?(Hash) && @init_data[subset][:id] && @init_data[subset][:name]
505
- break if @main_subset
506
- end
507
- @main_subset ||= @init_data
549
+ # @return [Boolean] See {JSS::SelfServable}
550
+ def self_servable?
551
+ defined? self.class::SELF_SERVABLE
552
+ end
508
553
 
509
- @id ||= @main_subset[:id]
510
- @name ||= @main_subset[:name]
554
+ # @return [Boolean] See {JSS::criteriable}
555
+ def criterable?
556
+ defined? self.class::CRITERIABLE
557
+ end
511
558
 
512
- # many things have a :site
513
- if @main_subset[:site]
514
- @site = JSS::APIObject.get_name(@main_subset[:site])
515
- end
559
+ # @return [Boolean] See {JSS::extendable}
560
+ def extendable?
561
+ defined? self.class::EXTENDABLE
562
+ end
516
563
 
517
- # many things have a :category
518
- if @main_subset[:category]
519
- @category = JSS::APIObject.get_name(@main_subset[:category])
520
- end
564
+ # @return [Boolean] See {JSS::Matchable}
565
+ def matchable?
566
+ defined? self.class::MATCHABLE
567
+ end
521
568
 
522
- # set empty strings to nil
523
- @init_data.jss_nillify! '', :recurse
569
+ # @return [Boolean] See {JSS::Locatable}
570
+ def locatable?
571
+ defined? self.class::LOCATABLE
572
+ end
524
573
 
525
- @in_jss = true
526
- @rest_rsrc = "#{self.class::RSRC_BASE}/id/#{@id}"
527
- @need_to_update = false
528
- end # init
574
+ # @return [Boolean] See {JSS::Purchasable}
575
+ def purchasable?
576
+ defined? self.class::PURCHASABLE
577
+ end
529
578
 
530
- ### Public Instance Methods
531
- #####################################
579
+ # @return [Boolean] See {JSS::Scopable}
580
+ def scopable?
581
+ defined? self.class::SCOPABLE
582
+ end
532
583
 
533
- ### Either Create or Update this object in the JSS
534
- ###
535
- ### If this item is creatable or updatable, then
536
- ### create it if needed, or update it if it already exists.
537
- ###
538
- ### @return [Integer] the id of the item created or updated
539
- ###
540
- def save
541
- if @in_jss
542
- raise JSS::UnsupportedError, 'Updating this object in the JSS is currently not supported' \
543
- unless defined? self.class::UPDATABLE
544
- update
545
- else
546
- raise JSS::UnsupportedError, 'Creating this object in the JSS is currently not supported' \
547
- unless defined? self.class::CREATABLE
548
- create
549
- end
584
+ # @return [Boolean] See {JSS::Uploadable}
585
+ def uploadable?
586
+ defined? self.class::UPLOADABLE
550
587
  end
551
588
 
552
- ### Delete this item from the JSS.
553
- ###
554
- ### Subclasses may want to redefine this method,
555
- ### first calling super, then setting other attributes to
556
- ### nil, false, empty, etc..
557
- ###
558
- ### @return [void]
559
- ###
589
+ # Delete this item from the JSS.
590
+ #
591
+ # Subclasses may want to redefine this method,
592
+ # first calling super, then setting other attributes to
593
+ # nil, false, empty, etc..
594
+ #
595
+ # @return [void]
596
+ #
560
597
  def delete
561
598
  return nil unless @in_jss
562
599
  JSS::API.delete_rsrc @rest_rsrc
@@ -567,34 +604,224 @@ module JSS
567
604
  :deleted
568
605
  end # delete
569
606
 
570
-
571
- ### A meaningful string representation of this object
572
- ###
573
- ### @return [String]
574
- ###
607
+ # A meaningful string representation of this object
608
+ #
609
+ # @return [String]
610
+ #
575
611
  def to_s
576
612
  "#{self.class}, name: #{@name}, id: #{@id}"
577
613
  end
578
614
 
579
-
580
- ### Private Instance Methods
615
+ # Private Instance Methods
581
616
  #####################################
582
617
  private
583
618
 
584
- ### Return a String with the XML Resource
585
- ### for submitting creation or changes to the JSS via
586
- ### the API via the Creatable or Updatable modules
587
- ###
588
- ### Most classes will redefine this method.
589
- ###
619
+ # If we were passed pre-lookedup API data, validate it,
620
+ # raising exceptions if not valid.
621
+ #
622
+ # DEPRECATED: pre-lookedup data is never used
623
+ # and support for it will be going away.
624
+ #
625
+ # @return [void]
626
+ #
627
+ def validate_external_init_data
628
+ # data must include all they keys in REQUIRED_DATA_KEYS + VALID_DATA_KEYS
629
+ # in either the main hash keys or the :general sub-hash, if it exists
630
+ hash_to_check = @init_data[:general] ? @init_data[:general] : @init_data
631
+ combined_valid_keys = self.class::REQUIRED_DATA_KEYS + self.class::VALID_DATA_KEYS
632
+ keys_ok = (hash_to_check.keys & combined_valid_keys).count == combined_valid_keys.count
633
+ unless keys_ok
634
+ raise(
635
+ JSS::InvalidDataError,
636
+ ":data is not valid JSON for a #{self.class::RSRC_OBJECT_KEY} from the API. It needs at least the keys :#{combined_valid_keys.join ', :'}"
637
+ )
638
+ end
639
+ # and the id must be in the jss
640
+ raise NoSuchItemError, "No #{self.class::RSRC_OBJECT_KEY} with JSS id: #{@init_data[:id]}" unless \
641
+ self.class.all_ids.include? hash_to_check[:id]
642
+ end # validate_init_data
643
+
644
+ # If we're making a new object in the JSS, make sure we were given
645
+ # valid data to do so, raise exceptions otherwise.
646
+ #
647
+ # NOTE: some subclasses may do further validation.
648
+ #
649
+ # TODO: support for objects that can have duplicate names.
650
+ #
651
+ # @param args[Hash] The args passed to #initialize
652
+ #
653
+ # @return [void]
654
+ #
655
+ def validate_init_for_creation(args)
656
+ raise JSS::UnsupportedError, "Creating #{self.class::RSRC_LIST_KEY} isn't yet supported. Please use other Casper workflows." unless creatable?
657
+
658
+ raise JSS::MissingDataError, "You must provide a :name to create a #{self.class::RSRC_OBJECT_KEY}." unless args[:name]
659
+
660
+ raise JSS::AlreadyExistsError, "A #{self.class::RSRC_OBJECT_KEY} already exists with the name '#{args[:name]}'" if self.class.all_names.include? args[:name]
661
+ end
662
+
663
+ # Given initialization args, perform an API lookup for an object.
664
+ #
665
+ # @param args[Hash] The args passed to #initialize
666
+ #
667
+ # @return [Hash] The parsed JSON data for the object from the API
668
+ #
669
+ def look_up_object_data(args)
670
+ # what lookup key are we using?
671
+ combined_lookup_keys = self.class::DEFAULT_LOOKUP_KEYS + args[:other_lookup_keys]
672
+ lookup_key = (combined_lookup_keys & args.keys)[0]
673
+
674
+ raise JSS::MissingDataError, "Args must include a lookup key, one of: :#{combined_lookup_keys.join(', :')}" unless lookup_key
675
+
676
+ rsrc = "#{self.class::RSRC_BASE}/#{lookup_key}/#{args[lookup_key]}"
677
+
678
+ return JSS::API.get_rsrc(rsrc)[self.class::RSRC_OBJECT_KEY]
679
+ rescue RestClient::ResourceNotFound
680
+ raise NoSuchItemError, "No #{self.class::RSRC_OBJECT_KEY} found matching: #{lookup_key}/#{args[lookup_key]}"
681
+ end
682
+
683
+ # Start examining the @init_data recieved from the API
684
+ #
685
+ # @return [void]
686
+ #
687
+ def parse_init_data
688
+ # set empty strings to nil
689
+ @init_data.jss_nillify! '', :recurse
690
+
691
+ # Find the "main" subset which contains :id and :name
692
+ @main_subset = find_main_subset
693
+
694
+ @id = @main_subset[:id]
695
+ @name = @main_subset[:name]
696
+ @in_jss = true
697
+ @rest_rsrc = "#{self.class::RSRC_BASE}/id/#{@id}"
698
+
699
+ # many things have a :site
700
+ # TODO: Implement a Sitable mixin module
701
+ @site = JSS::APIObject.get_name(@main_subset[:site]) if @main_subset[:site]
702
+
703
+ ##### Handle Mix-ins
704
+ initialize_category
705
+ initialize_location
706
+ initialize_purchasing
707
+ initialize_scope
708
+ initialize_criteria
709
+ initialize_ext_attrs
710
+ initialize_vpp
711
+ initialize_self_service
712
+ end
713
+
714
+ # Find which part of the @init_data contains the :id and :name
715
+ #
716
+ # If they aren't at the top-level of the init hash they are in a subset hash,
717
+ # usually :general, but sometimes someething else,
718
+ # like ldap servers, which have them in :connection
719
+ # Whereever both :id and :name are, that's the main subset
720
+ #
721
+ # @return [Hash] The part of the @init_data containg the :id and :name
722
+ #
723
+ def find_main_subset
724
+ @init_data.each do |key, value|
725
+ next unless value.is_a? Hash
726
+ return value if value.keys.include?(:id) && value.keys.include?(:name)
727
+ end
728
+ @init_data
729
+ end
730
+
731
+ # parse category data during initialization
732
+ #
733
+ # @return [void]
734
+ #
735
+ def initialize_category
736
+ parse_category if categorizable?
737
+ end
738
+
739
+ # parse location data during initialization
740
+ #
741
+ # @return [void]
742
+ #
743
+ def initialize_location
744
+ parse_location if locatable?
745
+ end
746
+
747
+ # parse purchasing data during initialization
748
+ #
749
+ # @return [void]
750
+ #
751
+ def initialize_purchasing
752
+ parse_purchasing if purchasable?
753
+ end
754
+
755
+ # parse scope data during initialization
756
+ #
757
+ # @return [void]
758
+ #
759
+ def initialize_scope
760
+ parse_scope if scopable?
761
+ end
762
+
763
+ # parse criteria data during initialization
764
+ #
765
+ # @return [void]
766
+ #
767
+ def initialize_criteria
768
+ parse_criteria if criterable?
769
+ end
770
+
771
+ # parse ext_attrs data during initialization
772
+ #
773
+ # @return [void]
774
+ #
775
+ def initialize_ext_attrs
776
+ parse_ext_attrs if extendable?
777
+ end
778
+
779
+ # parse vpp data during initialization
780
+ #
781
+ # @return [void]
782
+ #
783
+ def initialize_vpp
784
+ parse_vpp if vppable?
785
+ end
786
+
787
+ # parse self_service data during initialization
788
+ #
789
+ # @return [void]
790
+ #
791
+ def initialize_self_service
792
+ parse_self_service if self_servable?
793
+ end
794
+
795
+ # Set the basics for creating a new object in the JSS
796
+ #
797
+ # @param args[Type] describe_args
798
+ #
799
+ # @return [Type] description_of_returned_object
800
+ #
801
+ def setup_object_for_creation(args)
802
+ # NOTE: subclasses may want to pre-populate more keys in @init_data when :id == :new
803
+ # then parse them into attributes later.
804
+ @init_data = { name: args[:name] }
805
+ @name = args[:name]
806
+ @in_jss = false
807
+ @rest_rsrc = "#{self.class::RSRC_BASE}/name/#{CGI.escape @name}"
808
+ @need_to_update = true
809
+ end
810
+
811
+ # Return a String with the XML Resource
812
+ # for submitting creation or changes to the JSS via
813
+ # the API via the Creatable or Updatable modules
814
+ #
815
+ # Most classes will redefine this method.
816
+ #
590
817
  def rest_xml
591
- doc = REXML::Document.new APIConnection::XML_HEADER
818
+ doc = REXML::Document.new JSS::APIConnection::XML_HEADER
592
819
  tmpl = doc.add_element self.class::RSRC_OBJECT_KEY.to_s
593
820
  tmpl.add_element('name').text = @name
594
821
  doc.to_s
595
822
  end
596
823
 
597
- ### Aliases
824
+ # Aliases
598
825
 
599
826
  alias in_jss? in_jss
600
827
 
@@ -611,6 +838,8 @@ require 'jss/api_object/purchasable'
611
838
  require 'jss/api_object/updatable'
612
839
  require 'jss/api_object/extendable'
613
840
  require 'jss/api_object/self_servable'
841
+ require 'jss/api_object/categorizable'
842
+ require 'jss/api_object/vppable'
614
843
 
615
844
  ### Mix-in Sub Modules with Classes
616
845
  require 'jss/api_object/criteriable'
@@ -629,8 +858,12 @@ require 'jss/api_object/computer'
629
858
  require 'jss/api_object/computer_invitation'
630
859
  require 'jss/api_object/department'
631
860
  require 'jss/api_object/distribution_point'
861
+ require 'jss/api_object/ebook'
632
862
  require 'jss/api_object/ldap_server'
863
+ require 'jss/api_object/mac_application'
633
864
  require 'jss/api_object/mobile_device'
865
+ require 'jss/api_object/mobile_device_application'
866
+ require 'jss/api_object/mobile_device_configuration_profile'
634
867
  require 'jss/api_object/netboot_server'
635
868
  require 'jss/api_object/network_segment'
636
869
  require 'jss/api_object/osx_configuration_profile'