ruby-jss 1.0.4 → 1.1.0b1

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.

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