ruby-jss 0.7.0 → 0.8.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of ruby-jss might be problematic. Click here for more details.

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'