ruby-jss 1.0.4 → 1.1.0b1

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 (54) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES.md +26 -0
  3. data/lib/jss.rb +47 -24
  4. data/lib/jss/api_connection.rb +39 -7
  5. data/lib/jss/api_object.rb +651 -319
  6. data/lib/jss/api_object/account.rb +19 -5
  7. data/lib/jss/api_object/advanced_search/advanced_computer_search.rb +0 -3
  8. data/lib/jss/api_object/advanced_search/advanced_mobile_device_search.rb +0 -3
  9. data/lib/jss/api_object/advanced_search/advanced_user_search.rb +0 -3
  10. data/lib/jss/api_object/building.rb +0 -3
  11. data/lib/jss/api_object/category.rb +0 -3
  12. data/lib/jss/api_object/computer.rb +83 -28
  13. data/lib/jss/api_object/computer_invitation.rb +1 -11
  14. data/lib/jss/api_object/configuration_profile/osx_configuration_profile.rb +0 -3
  15. data/lib/jss/api_object/department.rb +0 -3
  16. data/lib/jss/api_object/distribution_point.rb +0 -3
  17. data/lib/jss/api_object/extendable.rb +113 -57
  18. data/lib/jss/api_object/extension_attribute.rb +46 -13
  19. data/lib/jss/api_object/extension_attribute/computer_extension_attribute.rb +0 -3
  20. data/lib/jss/api_object/extension_attribute/mobile_device_extension_attribute.rb +56 -19
  21. data/lib/jss/api_object/extension_attribute/user_extension_attribute.rb +0 -3
  22. data/lib/jss/api_object/group/computer_group.rb +0 -3
  23. data/lib/jss/api_object/group/mobile_device_group.rb +0 -3
  24. data/lib/jss/api_object/group/user_group.rb +0 -3
  25. data/lib/jss/api_object/ldap_server.rb +0 -3
  26. data/lib/jss/api_object/mobile_device.rb +25 -29
  27. data/lib/jss/api_object/mobile_device_application.rb +1 -9
  28. data/lib/jss/api_object/netboot_server.rb +0 -3
  29. data/lib/jss/api_object/network_segment.rb +0 -3
  30. data/lib/jss/api_object/package.rb +0 -3
  31. data/lib/jss/api_object/patch_source/patch_external_source.rb +0 -2
  32. data/lib/jss/api_object/patch_source/patch_internal_source.rb +0 -3
  33. data/lib/jss/api_object/patch_title.rb +1 -2
  34. data/lib/jss/api_object/peripheral.rb +0 -3
  35. data/lib/jss/api_object/peripheral_type.rb +0 -2
  36. data/lib/jss/api_object/policy.rb +20 -2
  37. data/lib/jss/api_object/removable_macaddr.rb +0 -3
  38. data/lib/jss/api_object/restricted_software.rb +0 -3
  39. data/lib/jss/api_object/scopable.rb +0 -12
  40. data/lib/jss/api_object/scopable/scope.rb +1 -1
  41. data/lib/jss/api_object/script.rb +0 -3
  42. data/lib/jss/api_object/self_servable.rb +3 -1
  43. data/lib/jss/api_object/site.rb +0 -3
  44. data/lib/jss/api_object/software_update_server.rb +0 -3
  45. data/lib/jss/api_object/user.rb +0 -3
  46. data/lib/jss/api_object/webhook.rb +0 -3
  47. data/lib/jss/compatibility.rb +74 -53
  48. data/lib/jss/exceptions.rb +5 -0
  49. data/lib/jss/ruby_extensions/string.rb +4 -48
  50. data/lib/jss/ruby_extensions/string/conversions.rb +69 -0
  51. data/lib/jss/ruby_extensions/string/predicates.rb +47 -0
  52. data/lib/jss/version.rb +1 -1
  53. data/test/README.md +2 -4
  54. metadata +6 -4
@@ -66,12 +66,31 @@ module JSS
66
66
 
67
67
  # What kinds of data can be created by EAs?
68
68
  # Note, Dates must be in the format "YYYY-MM-DD hh:mm:ss"
69
- DATA_TYPES = %w[String Date Integer].freeze
70
- DEFAULT_DATA_TYPE = 'String'.freeze
69
+
70
+ DATA_TYPE_STRING = 'String'.freeze
71
+ DATA_TYPE_NUMBER = 'Number'.freeze
72
+ DATA_TYPE_INTEGER = 'Integer'.freeze
73
+ DATA_TYPE_DATE = 'Date'.freeze
74
+
75
+ DATA_TYPES = [DATA_TYPE_STRING, DATA_TYPE_DATE, DATA_TYPE_INTEGER].freeze
76
+
77
+ DEFAULT_DATA_TYPE = DATA_TYPE_STRING
78
+
79
+ # ExtensionAttributes refer to the numeric data type as "Integer"
80
+ # but the ext. attr values that come with extendable objects refer to
81
+ # that data type as "Number". Here's an array with both, so we can
82
+ # work with ether more easily.
83
+ NUMERIC_TYPES = [DATA_TYPE_NUMBER, DATA_TYPE_INTEGER].freeze
71
84
 
72
85
  # Where does the data come from?
73
- INPUT_TYPES = ['Text Field', 'Pop-up Menu', 'script', 'LDAP Attribute Mapping'].freeze
74
- DEFAULT_INPUT_TYPE = 'Text Field'.freeze
86
+
87
+ INPUT_TYPE_FIELD = 'Text Field'.freeze
88
+ INPUT_TYPE_POPUP = 'Pop-up Menu'.freeze
89
+ INPUT_TYPE_SCRIPT = 'script'.freeze
90
+ INPUT_TYPE_LDAP = 'LDAP Attribute Mapping'.freeze
91
+
92
+ INPUT_TYPES = [INPUT_TYPE_FIELD, INPUT_TYPE_POPUP, INPUT_TYPE_SCRIPT, INPUT_TYPE_LDAP].freeze
93
+ DEFAULT_INPUT_TYPE = INPUT_TYPE_FIELD
75
94
 
76
95
  # Where can it be displayed in the WebApp?
77
96
  # subclasses can add to this list
@@ -142,15 +161,13 @@ module JSS
142
161
  @symbolized_name = @name.gsub(/-| /, '_').to_sym
143
162
  end # init
144
163
 
145
-
146
164
  # Public Instance Methods
147
165
  ###################################
148
166
 
149
-
150
167
  # @see JSS::Creatable#create
151
168
  #
152
169
  def create
153
- if @input_type == 'Pop-up Menu'
170
+ if @input_type == INPUT_TYPE_POPUP
154
171
  raise MissingDataError, 'No popup_choices set for Pop-up Menu input_type.' unless @popup_choices.is_a?(Array) && !@popup_choices.empty?
155
172
  end
156
173
  super
@@ -159,7 +176,7 @@ module JSS
159
176
  # @see JSS::Updatable#update
160
177
  #
161
178
  def update
162
- if @input_type == 'Pop-up Menu'
179
+ if @input_type == INPUT_TYPE_POPUP
163
180
  raise MissingDataError, 'No popup_choices set for Pop-up Menu input_type.' unless @popup_choices.is_a?(Array) && !@popup_choices.empty?
164
181
  end
165
182
  super
@@ -180,6 +197,22 @@ module JSS
180
197
  end
181
198
  end
182
199
 
200
+ def from_text_field?
201
+ @input_type == INPUT_TYPE_FIELD
202
+ end
203
+
204
+ def from_popup_menu?
205
+ @input_type == INPUT_TYPE_POPUP
206
+ end
207
+
208
+ def from_ldap?
209
+ @input_type == INPUT_TYPE_LDAP
210
+ end
211
+
212
+ def from_script?
213
+ @input_type == INPUT_TYPE_SCRIPT
214
+ end
215
+
183
216
  # Change the description of this EA
184
217
  #
185
218
  # @param new_val[String] the new value
@@ -228,7 +261,7 @@ module JSS
228
261
  return nil if @input_type == new_val
229
262
  raise JSS::InvalidDataError, "input_type must be a string, one of: #{INPUT_TYPES.join(', ')}" unless INPUT_TYPES.include? new_val
230
263
  @input_type = new_val
231
- @popup_choices = nil if @input_type == 'Text Field'
264
+ @popup_choices = nil if @input_type == INPUT_TYPE_FIELD
232
265
  @need_to_update = true
233
266
  end #
234
267
 
@@ -253,14 +286,14 @@ module JSS
253
286
  new_val.map! do |v|
254
287
  v = v.to_s.strip
255
288
  case @data_type
256
- when 'Date'
289
+ when DATA_TYPE_DATE
257
290
  raise JSS::InvalidDataError, "data_type is Date, but '#{v}' is not formatted 'YYYY-MM-DD hh:mm:ss'" unless v =~ /^\d{4}(-\d\d){2} (\d\d:){2}\d\d$/
258
291
  when 'Integer'
259
292
  raise JSS::InvalidDataError, "data_type is Integer, but '#{v}' is not one" unless v =~ /^\d+$/
260
293
  end
261
294
  v
262
295
  end
263
- self.input_type = 'Pop-up Menu'
296
+ self.input_type = INPUT_TYPE_POPUP
264
297
  @popup_choices = new_val
265
298
  @need_to_update = true
266
299
  end #
@@ -319,7 +352,7 @@ module JSS
319
352
  # :id - the jss id
320
353
  # :name - the object (computer, user, mobiledevice) name
321
354
  # :value - the most recent ext attr value for the object.
322
- # :as_of - the timestamp of when the value was collected (nil for User EAs)
355
+ # :as_of - the timestamp of when the value was collected (nil for User EAs, or for devices that have never collected inventory)
323
356
  # :username - the username associated with the object
324
357
  #
325
358
  # This is done by creating a temporary {AdvancedSearch}
@@ -362,7 +395,7 @@ module JSS
362
395
  else i[@symbolized_name]
363
396
  end # case
364
397
 
365
- as_of = Time.parse(i[LAST_RECON_FIELD_SYM]) if i[LAST_RECON_FIELD_SYM]
398
+ as_of = Time.parse(i[LAST_RECON_FIELD_SYM]) if i[LAST_RECON_FIELD_SYM] != ''
366
399
 
367
400
  results << { id: i[:id], name: i[:name], username: i[USERNAME_FIELD_SYM], value: value, as_of: as_of }
368
401
  end # acs.search_results.each
@@ -76,9 +76,6 @@ module JSS
76
76
  ### It's also used in various error messages
77
77
  RSRC_OBJECT_KEY = :computer_extension_attribute
78
78
 
79
- ### these keys, as well as :id and :name, are present in valid API JSON data for this class
80
- VALID_DATA_KEYS = [:description, :inventory_display, :recon_display]
81
-
82
79
  ### these ext attribs are related to these kinds of objects
83
80
  TARGET_CLASS = JSS::Computer
84
81
 
@@ -26,8 +26,6 @@
26
26
  ###
27
27
  module JSS
28
28
 
29
-
30
-
31
29
  #####################################
32
30
  ### Constants
33
31
  #####################################
@@ -44,7 +42,6 @@ module JSS
44
42
  ### Classes
45
43
  #####################################
46
44
 
47
-
48
45
  ###
49
46
  ### An extension attribute as defined in the JSS
50
47
  ###
@@ -67,7 +64,7 @@ module JSS
67
64
  #####################################
68
65
 
69
66
  ### The base for REST resources of this class
70
- RSRC_BASE = "mobiledeviceextensionattributes"
67
+ RSRC_BASE = 'mobiledeviceextensionattributes'.freeze
71
68
 
72
69
  ### the hash key used for the JSON list output of all objects in the JSS
73
70
  RSRC_LIST_KEY = :mobile_device_extension_attributes
@@ -76,14 +73,11 @@ module JSS
76
73
  ### It's also used in various error messages
77
74
  RSRC_OBJECT_KEY = :mobile_device_extension_attribute
78
75
 
79
- ### these keys, as well as :id and :name, are present in valid API JSON data for this class
80
- VALID_DATA_KEYS = [:description, :inventory_display, :recon_display]
81
-
82
76
  ### these ext attribs are related to these kinds of objects
83
77
  TARGET_CLASS = JSS::MobileDevice
84
78
 
85
79
  ### A criterion that will return all members of the TARGET_CLASS
86
- ALL_TARGETS_CRITERION = JSS::Criteriable::Criterion.new(:and_or => "and", :name => "Last Inventory Update", :search_type => "after (yyyy-mm-dd)", :value => "2003-01-01")
80
+ ALL_TARGETS_CRITERION = JSS::Criteriable::Criterion.new(and_or: 'and', name: 'Last Inventory Update', search_type: 'after (yyyy-mm-dd)', value: '2003-01-01')
87
81
 
88
82
  # the object type for this object in
89
83
  # the object history table.
@@ -97,7 +91,6 @@ module JSS
97
91
  ### @return [String] the name of the LDAP attribute to use when the @input Type is "LDAP Attribute Mapping"
98
92
  attr_reader :attribute_mapping
99
93
 
100
-
101
94
  #####################################
102
95
  ### Constructor
103
96
  #####################################
@@ -106,15 +99,10 @@ module JSS
106
99
  ### See JSS::APIObject.initialize
107
100
  ###
108
101
  def initialize(args = {})
109
-
110
102
  super args
111
-
112
- if @init_data[:input_type]
113
- @attribute_mapping = @init_data[:input_type][:attribute_mapping]
114
- end
103
+ @attribute_mapping = @init_data[:input_type][:attribute_mapping] if @init_data[:input_type]
115
104
  end # init
116
105
 
117
-
118
106
  #####################################
119
107
  ### Public Instance Methods
120
108
  #####################################
@@ -129,14 +117,13 @@ module JSS
129
117
  super
130
118
  end
131
119
 
132
-
133
120
  ###
134
121
  ### @see JSS::ExtensionAttribute#web_display=
135
122
  ###
136
123
  def web_display= (new_val)
137
124
  raise JSS::InvalidDataError, "web_display cannot be 'Operating System' for Mobile Device Extension Attributes." if new_val == 'Operating System'
138
125
  super
139
- end #
126
+ end # end web_display
140
127
 
141
128
 
142
129
  ###
@@ -152,7 +139,7 @@ module JSS
152
139
  else
153
140
  @attribute_mapping = nil
154
141
  end
155
- end #
142
+ end # end input_type
156
143
 
157
144
  ###
158
145
  ### Set the ldap attribute to use for input_type 'LDAP Attribute Mapping'
@@ -167,6 +154,56 @@ module JSS
167
154
  @need_to_update = true
168
155
  end
169
156
 
157
+ ### Return an Array of Hashes showing the history of reported values for this EA on one MobileDevice.
158
+ ###
159
+ ### Each hash contains these 2 keys:
160
+ ### * :value - String, Integer, or Time, depending on @data_type
161
+ ### * :timestamp - Time
162
+ ###
163
+ ### This method requires a MySQL database connection established via JSS::DB_CNX.connect
164
+ ###
165
+ ### @see JSS::DBConnection
166
+ ###
167
+ ### @param mobiledevice[Integer,String] the id or name of the MobileDevice.
168
+ ###
169
+ ### @return [Array<Hash{:timestamp=>Time,:value=>String,Integer,Time}>]
170
+ ###
171
+ def history(mobiledevice)
172
+ raise JSS::NoSuchItemError, "EA Not In JSS! Use #create to create this #{RSRC_OBJECT_KEY}." unless @in_jss
173
+ raise JSS::InvalidConnectionError, "Database connection required for 'history' query." unless JSS::DB_CNX.connected?
174
+
175
+ mobile_device_id = case mobiledevice
176
+ when *JSS::MobileDevice.all_ids(api: @api)
177
+ mobiledevice
178
+ when *JSS::MobileDevice.all_names(api: @api)
179
+ JSS::MobileDevice.map_all_ids_to(:name, api: @api).invert[mobiledevice]
180
+ end # case
181
+
182
+ raise JSS::NoSuchItemError, "No MobileDevice found matching '#{mobiledevice}'" unless mobile_device_id
183
+
184
+ the_query = <<-END_Q
185
+ SELECT eav.value_on_client AS value, r.date_entered_epoch AS timestamp_epoch
186
+ FROM mobile_device_extension_attribute_values eav JOIN reports r ON eav.report_id = r.report_id
187
+ WHERE r.mobile_device_id = #{mobile_device_id}
188
+ AND eav.mobile_device_extension_attribute_id = #{@id}
189
+ ORDER BY timestamp_epoch
190
+ END_Q
191
+
192
+ qrez = JSS::DB_CNX.db.query the_query
193
+ history = []
194
+ qrez.each_hash do |entry|
195
+ value = case @data_type
196
+ when 'String' then entry['value']
197
+ when 'Integer' then entry['value'].to_i
198
+ when 'Date' then JSS.parse_datetime(entry['value'])
199
+ end # case
200
+ newhash = { value: value, timestamp: JSS.epoch_to_time(entry['timestamp_epoch']) }
201
+ history << newhash
202
+ end # each hash
203
+
204
+ history
205
+ end # history
206
+
170
207
  ######################
171
208
  ### Private Instance Methods
172
209
  #####################
@@ -187,7 +224,7 @@ module JSS
187
224
  doc = REXML::Document.new APIConnection::XML_HEADER
188
225
  doc << mdea
189
226
 
190
- return doc.to_s
227
+ doc.to_s
191
228
  end # rest xml
192
229
 
193
230
  end # class ExtAttrib
@@ -75,9 +75,6 @@ module JSS
75
75
  ### It's also used in various error messages
76
76
  RSRC_OBJECT_KEY = :user_extension_attribute
77
77
 
78
- ### these keys, as well as :id and :name, are present in valid API JSON data for this class
79
- VALID_DATA_KEYS = [:description, :data_type, :input_type]
80
-
81
78
  ### these ext attribs are related to these kinds of objects
82
79
  TARGET_CLASS = JSS::User
83
80
 
@@ -76,9 +76,6 @@ module JSS
76
76
  ### It's also used in various error messages
77
77
  RSRC_OBJECT_KEY = :computer_group
78
78
 
79
- ### these keys, as well as :id and :name, are present in valid API JSON data for this class
80
- VALID_DATA_KEYS = [:is_smart, :computers ]
81
-
82
79
  ### this allows the parent Group class to do things right
83
80
  MEMBER_CLASS = JSS::Computer
84
81
 
@@ -72,9 +72,6 @@ module JSS
72
72
  ### It's also used in various error messages
73
73
  RSRC_OBJECT_KEY = :mobile_device_group
74
74
 
75
- ### these keys, as well as :id and :name, are present in valid API JSON data for this class
76
- VALID_DATA_KEYS = [:is_smart, :mobile_devices ]
77
-
78
75
  ### this allows the parent Group class to do things right
79
76
  MEMBER_CLASS = JSS::MobileDevice
80
77
 
@@ -72,9 +72,6 @@ module JSS
72
72
  ### It's also used in various error messages
73
73
  RSRC_OBJECT_KEY = :user_group
74
74
 
75
- ### these keys, as well as :id and :name, are present in valid API JSON data for this class
76
- VALID_DATA_KEYS = [:is_smart, :users ]
77
-
78
75
  ### this allows the parent Group class to do things right
79
76
  MEMBER_CLASS = JSS::User
80
77
 
@@ -60,9 +60,6 @@ module JSS
60
60
  # It's also used in various error messages
61
61
  RSRC_OBJECT_KEY = :ldap_server
62
62
 
63
- # these keys, as well as :id and :name, are present in valid API JSON data for this class
64
- VALID_DATA_KEYS = [].freeze
65
-
66
63
  # the default LDAP port
67
64
  DEFAULT_PORT = 389
68
65
 
@@ -100,18 +100,34 @@ module JSS
100
100
  # Where is the Site data in the API JSON?
101
101
  SITE_SUBSET = :general
102
102
 
103
- # these keys, as well as :id and :name, are present in valid API JSON data for this class
104
- VALID_DATA_KEYS = %i[device_name capacity tethered].freeze
105
-
106
- # these keys, as well as :id and :name, can be used to look up objects of this class in the JSS
103
+ # these keys, as well as :id and :name, can be used to look up objects
104
+ # of this class in the JSS
105
+ # the wierd alises wifi_mac_addresse, mac_addresse and macaddresse
106
+ # are for proper pluralization of 'mac_address' and such
107
107
  OTHER_LOOKUP_KEYS = {
108
- udid: { rsrc_key: :udid, list: :all_udids },
109
- serialnumber: { rsrc_key: :serialnumber, list: :all_serial_numbers },
110
- serial_number: { rsrc_key: :serialnumber, list: :all_serial_numbers },
111
- macaddress: { rsrc_key: :macaddress, list: :all_wifi_mac_addresses },
112
- mac_address: { rsrc_key: :macaddress, list: :all_wifi_mac_addresses }
108
+ udid: {
109
+ aliases: [:uuid, :guid],
110
+ fetch_rsrc_key: :udid
111
+ },
112
+ serial_number: {
113
+ aliases: [:serialnumber, :sn],
114
+ fetch_rsrc_key: :serialnumber
115
+ },
116
+ wifi_mac_address: {
117
+ aliases: [
118
+ :wifi_mac_addresse,
119
+ :mac_address,
120
+ :mac_addresse,
121
+ :macaddress,
122
+ :macaddresse,
123
+ :macaddr
124
+ ],
125
+ fetch_rsrc_key: :macaddress
126
+ }
113
127
  }.freeze
114
128
 
129
+ NON_UNIQUE_NAMES = true
130
+
115
131
  # This class lets us seach for computers
116
132
  SEARCH_CLASS = JSS::AdvancedMobileDeviceSearch
117
133
 
@@ -129,31 +145,11 @@ module JSS
129
145
  # Class Methods
130
146
  #####################################
131
147
 
132
- # @return [Array<String>] all mobiledevice serial_numbers
133
- def self.all_serial_numbers(refresh = false, api: JSS.api)
134
- all(refresh, api: api).map { |i| i[:serial_number] }
135
- end
136
-
137
148
  # @return [Array<String>] all mobiledevice phone numbers
138
149
  def self.all_phone_numbers(refresh = false, api: JSS.api)
139
150
  all(refresh, api: api).map { |i| i[:phone_number] }.reject(&:empty?)
140
151
  end
141
152
 
142
- # @return [Array<String>] all mobiledevice wifi mac addrs
143
- def self.all_wifi_mac_addresses(refresh = false, api: JSS.api)
144
- all(refresh, api: api).map { |i| i[:wifi_mac_address] }
145
- end
146
-
147
- # @return [Array<String>] all mobiledevice wifi mac addrs
148
- def self.all_mac_addresses(refresh = false, api: JSS.api)
149
- all_wifi_mac_addresses(refresh, api: api)
150
- end
151
-
152
- # @return [Array<String>] all mobiledevice udids
153
- def self.all_udids(refresh = false, api: JSS.api)
154
- all(refresh, api: api).map { |i| i[:udid] }
155
- end
156
-
157
153
  # @return [Array<Hash>] the list of all managed mobile devices
158
154
  def self.all_managed(refresh = false, api: JSS.api)
159
155
  all(refresh, api: api).select { |d| d[:managed] }
@@ -54,10 +54,6 @@ module JSS
54
54
  # Class Methods
55
55
  #####################################
56
56
 
57
- def self.all_bundle_ids(refresh = false, api: JSS.api)
58
- all(refresh, api: api).map { |mda| mda[:bundle_id] }
59
- end
60
-
61
57
  # Class Constants
62
58
  #####################################
63
59
 
@@ -71,9 +67,6 @@ module JSS
71
67
  # It's also used in various error messages
72
68
  RSRC_OBJECT_KEY = :mobile_device_application
73
69
 
74
- # these keys, as well as :id and :name, are present in valid API JSON data for this class
75
- VALID_DATA_KEYS = [:internal_app].freeze
76
-
77
70
  # See JSS::Scopable
78
71
  SCOPE_TARGET_KEY = :mobile_devices
79
72
 
@@ -86,8 +79,7 @@ module JSS
86
79
 
87
80
  # see JSS::APIObject
88
81
  OTHER_LOOKUP_KEYS = {
89
- bundleid: {rsrc_id: :bundleid, list: :all_bundle_ids},
90
- bundle_id: {rsrc_id: :bundleid, list: :all_bundle_ids}
82
+ bundle_id: { aliases: %i[bundleid], fetch_rsrc_key: :bundleid }
91
83
  }.freeze
92
84
 
93
85
  # the object type for this object in