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.
- checksums.yaml +4 -4
- data/CHANGES.md +26 -0
- data/lib/jss.rb +47 -24
- data/lib/jss/api_connection.rb +39 -7
- data/lib/jss/api_object.rb +651 -319
- data/lib/jss/api_object/account.rb +19 -5
- data/lib/jss/api_object/advanced_search/advanced_computer_search.rb +0 -3
- data/lib/jss/api_object/advanced_search/advanced_mobile_device_search.rb +0 -3
- data/lib/jss/api_object/advanced_search/advanced_user_search.rb +0 -3
- data/lib/jss/api_object/building.rb +0 -3
- data/lib/jss/api_object/category.rb +0 -3
- data/lib/jss/api_object/computer.rb +83 -28
- data/lib/jss/api_object/computer_invitation.rb +1 -11
- data/lib/jss/api_object/configuration_profile/osx_configuration_profile.rb +0 -3
- data/lib/jss/api_object/department.rb +0 -3
- data/lib/jss/api_object/distribution_point.rb +0 -3
- data/lib/jss/api_object/extendable.rb +113 -57
- data/lib/jss/api_object/extension_attribute.rb +46 -13
- data/lib/jss/api_object/extension_attribute/computer_extension_attribute.rb +0 -3
- data/lib/jss/api_object/extension_attribute/mobile_device_extension_attribute.rb +56 -19
- data/lib/jss/api_object/extension_attribute/user_extension_attribute.rb +0 -3
- data/lib/jss/api_object/group/computer_group.rb +0 -3
- data/lib/jss/api_object/group/mobile_device_group.rb +0 -3
- data/lib/jss/api_object/group/user_group.rb +0 -3
- data/lib/jss/api_object/ldap_server.rb +0 -3
- data/lib/jss/api_object/mobile_device.rb +25 -29
- data/lib/jss/api_object/mobile_device_application.rb +1 -9
- data/lib/jss/api_object/netboot_server.rb +0 -3
- data/lib/jss/api_object/network_segment.rb +0 -3
- data/lib/jss/api_object/package.rb +0 -3
- data/lib/jss/api_object/patch_source/patch_external_source.rb +0 -2
- data/lib/jss/api_object/patch_source/patch_internal_source.rb +0 -3
- data/lib/jss/api_object/patch_title.rb +1 -2
- data/lib/jss/api_object/peripheral.rb +0 -3
- data/lib/jss/api_object/peripheral_type.rb +0 -2
- data/lib/jss/api_object/policy.rb +20 -2
- data/lib/jss/api_object/removable_macaddr.rb +0 -3
- data/lib/jss/api_object/restricted_software.rb +0 -3
- data/lib/jss/api_object/scopable.rb +0 -12
- data/lib/jss/api_object/scopable/scope.rb +1 -1
- data/lib/jss/api_object/script.rb +0 -3
- data/lib/jss/api_object/self_servable.rb +3 -1
- data/lib/jss/api_object/site.rb +0 -3
- data/lib/jss/api_object/software_update_server.rb +0 -3
- data/lib/jss/api_object/user.rb +0 -3
- data/lib/jss/api_object/webhook.rb +0 -3
- data/lib/jss/compatibility.rb +74 -53
- data/lib/jss/exceptions.rb +5 -0
- data/lib/jss/ruby_extensions/string.rb +4 -48
- data/lib/jss/ruby_extensions/string/conversions.rb +69 -0
- data/lib/jss/ruby_extensions/string/predicates.rb +47 -0
- data/lib/jss/version.rb +1 -1
- data/test/README.md +2 -4
- metadata +6 -4
@@ -37,6 +37,9 @@ module JSS
|
|
37
37
|
|
38
38
|
# A User or group in the JSS.
|
39
39
|
#
|
40
|
+
# TODO: Split this into 2 classes, with lots of custom code.
|
41
|
+
# Thanks Jamf!
|
42
|
+
#
|
40
43
|
# @see JSS::APIObject
|
41
44
|
#
|
42
45
|
class Account < JSS::APIObject
|
@@ -60,17 +63,28 @@ module JSS
|
|
60
63
|
# It's also used in various error messages
|
61
64
|
RSRC_OBJECT_KEY = :account
|
62
65
|
|
63
|
-
# these keys, as well as :id and :name, can be used to look up objects of
|
66
|
+
# these keys, as well as :id and :name, can be used to look up objects of
|
67
|
+
# this class in the JSS
|
64
68
|
OTHER_LOOKUP_KEYS = {
|
65
|
-
userid: {
|
66
|
-
username: {
|
67
|
-
groupid: {
|
68
|
-
groupname: {
|
69
|
+
userid: { fetch_rsrc_key: :userid },
|
70
|
+
username: { fetch_rsrc_key: :username },
|
71
|
+
groupid: { fetch_rsrc_key: :groupid },
|
72
|
+
groupname: { fetch_rsrc_key: :groupname }
|
69
73
|
}.freeze
|
70
74
|
|
71
75
|
# Class Methods
|
72
76
|
#####################################
|
73
77
|
|
78
|
+
# override auto-defined method
|
79
|
+
def self.all_ids(_refresh = false, **_bunk)
|
80
|
+
raise '.all_ids is not valid for JSS::Account, use .all_user_ids or .all_group_ids'
|
81
|
+
end
|
82
|
+
|
83
|
+
# override auto-defined method
|
84
|
+
def self.all_names(_refresh = false, **_bunk)
|
85
|
+
raise '.all_names is not valid for JSS::Account, use .all_user_names or .all_group_names'
|
86
|
+
end
|
87
|
+
|
74
88
|
# @return [Array<Hash>] all JSS account users
|
75
89
|
def self.all_users(refresh = false, api: JSS.api)
|
76
90
|
all(refresh, api: api)[:users]
|
@@ -65,9 +65,6 @@ module JSS
|
|
65
65
|
# It's also used in various error messages
|
66
66
|
RSRC_OBJECT_KEY = :advanced_computer_search
|
67
67
|
|
68
|
-
# these keys, as well as :id and :name, are present in valid API JSON data for this class
|
69
|
-
VALID_DATA_KEYS = [:sql_text, :display_fields, :computers].freeze
|
70
|
-
|
71
68
|
# what kind of thing is returned by this search?
|
72
69
|
RESULT_CLASS = JSS::Computer
|
73
70
|
|
@@ -59,9 +59,6 @@ module JSS
|
|
59
59
|
# It's also used in various error messages
|
60
60
|
RSRC_OBJECT_KEY = :advanced_mobile_device_search
|
61
61
|
|
62
|
-
# these keys, as well as :id and :name, are present in valid API JSON data for this class
|
63
|
-
VALID_DATA_KEYS = [:sql_text, :display_fields, :mobile_devices].freeze
|
64
|
-
|
65
62
|
# what kind of thing is returned by this search?
|
66
63
|
RESULT_CLASS = JSS::MobileDevice
|
67
64
|
|
@@ -59,9 +59,6 @@ module JSS
|
|
59
59
|
# It's also used in various error messages
|
60
60
|
RSRC_OBJECT_KEY = :advanced_user_search
|
61
61
|
|
62
|
-
# these keys, as well as :id and :name, are present in valid API JSON data for this class
|
63
|
-
VALID_DATA_KEYS = [:criteria, :display_fields, :users].freeze
|
64
|
-
|
65
62
|
# what kind of thing is returned by this search?
|
66
63
|
RESULT_CLASS = JSS::User
|
67
64
|
|
@@ -63,9 +63,6 @@ module JSS
|
|
63
63
|
# It's also used in various error messages
|
64
64
|
RSRC_OBJECT_KEY = :building
|
65
65
|
|
66
|
-
# these keys, as well as :id and :name, are present in valid API JSON data for this class
|
67
|
-
VALID_DATA_KEYS = [].freeze
|
68
|
-
|
69
66
|
# the object type for this object in
|
70
67
|
# the object history table.
|
71
68
|
# See {APIObject#add_object_history_entry}
|
@@ -69,9 +69,6 @@ module JSS
|
|
69
69
|
# It's also used in various error messages
|
70
70
|
RSRC_OBJECT_KEY = :category
|
71
71
|
|
72
|
-
# these keys, as well as :id and :name, are present in valid API JSON data for this class
|
73
|
-
VALID_DATA_KEYS = [:priority].freeze
|
74
|
-
|
75
72
|
# When no category has been assigned, this is the 'name' and id used
|
76
73
|
NO_CATEGORY_NAME = JSS::Categorizable::NO_CATEGORY_NAME
|
77
74
|
NO_CATEGORY_ID = JSS::Categorizable::NO_CATEGORY_ID
|
@@ -180,19 +180,35 @@ module JSS
|
|
180
180
|
# Where is the Site data in the API JSON?
|
181
181
|
SITE_SUBSET = :general
|
182
182
|
|
183
|
-
# these keys,
|
184
|
-
#
|
185
|
-
|
186
|
-
|
187
|
-
#
|
183
|
+
# these keys, as well as :id and :name, can be used to look up objects
|
184
|
+
# of this class in the JSS
|
185
|
+
#
|
186
|
+
# the wierd alises mac_addresse and macaddresse
|
187
|
+
# are for proper pluralization of 'mac_address' and such
|
188
188
|
OTHER_LOOKUP_KEYS = {
|
189
|
-
udid: {
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
189
|
+
udid: {
|
190
|
+
aliases: [:uuid, :guid],
|
191
|
+
fetch_rsrc_key: :udid
|
192
|
+
},
|
193
|
+
serial_number: {
|
194
|
+
aliases: [:serialnumber, :sn],
|
195
|
+
fetch_rsrc_key: :serialnumber
|
196
|
+
},
|
197
|
+
mac_address: {
|
198
|
+
aliases: [
|
199
|
+
:mac_address,
|
200
|
+
:mac_addresse,
|
201
|
+
:macaddress,
|
202
|
+
:macaddresse,
|
203
|
+
:macaddr
|
204
|
+
],
|
205
|
+
fetch_rsrc_key: :macaddress
|
206
|
+
}
|
207
|
+
|
194
208
|
}.freeze
|
195
209
|
|
210
|
+
NON_UNIQUE_NAMES = true
|
211
|
+
|
196
212
|
# This class lets us seach for computers
|
197
213
|
SEARCH_CLASS = JSS::AdvancedComputerSearch
|
198
214
|
|
@@ -308,25 +324,28 @@ module JSS
|
|
308
324
|
# @return [Array<Hash{:name=>String, :id=> Integer}>]
|
309
325
|
#
|
310
326
|
def self.all(refresh = false, api: JSS.api)
|
311
|
-
api.object_list_cache
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
# @return [Array<String>] all computer serial numbers in the jss
|
317
|
-
def self.all_serial_numbers(refresh = false, api: JSS.api)
|
318
|
-
all(refresh, api: api).map { |i| i[:serial_number] }
|
319
|
-
end
|
327
|
+
cache = api.object_list_cache
|
328
|
+
cache_key = self::RSRC_LIST_KEY
|
329
|
+
cache[cache_key] = nil if refresh
|
330
|
+
return cache[cache_key] if cache[cache_key]
|
320
331
|
|
321
|
-
|
322
|
-
def self.all_mac_addresses(refresh = false, api: JSS.api)
|
323
|
-
all(refresh, api: api).map { |i| i[:mac_address] }
|
332
|
+
cache[cache_key] = api.get_rsrc(self::LIST_RSRC)[cache_key]
|
324
333
|
end
|
325
334
|
|
326
|
-
# @return [Array<String>] all computer
|
327
|
-
def self.
|
328
|
-
|
329
|
-
end
|
335
|
+
# # @return [Array<String>] all computer serial numbers in the jss
|
336
|
+
# def self.all_serial_numbers(refresh = false, api: JSS.api)
|
337
|
+
# all(refresh, api: api).map { |i| i[:serial_number] }
|
338
|
+
# end
|
339
|
+
#
|
340
|
+
# # @return [Array<String>] all computer mac_addresses in the jss
|
341
|
+
# def self.all_mac_addresses(refresh = false, api: JSS.api)
|
342
|
+
# all(refresh, api: api).map { |i| i[:mac_address] }
|
343
|
+
# end
|
344
|
+
#
|
345
|
+
# # @return [Array<String>] all computer udids in the jss
|
346
|
+
# def self.all_udids(refresh = false, api: JSS.api)
|
347
|
+
# all(refresh, api: api).map { |i| i[:udid] }
|
348
|
+
# end
|
330
349
|
|
331
350
|
# @return [Array<Hash>] all managed computers in the jss
|
332
351
|
def self.all_managed(refresh = false, api: JSS.api)
|
@@ -785,7 +804,7 @@ module JSS
|
|
785
804
|
identity: cert[:identity],
|
786
805
|
name: cert[:name]
|
787
806
|
}
|
788
|
-
end
|
807
|
+
end # map do cert
|
789
808
|
|
790
809
|
# Freeze immutable things.
|
791
810
|
# These are updated via recon, and aren't sent
|
@@ -798,6 +817,8 @@ module JSS
|
|
798
817
|
@software.freeze
|
799
818
|
|
800
819
|
@management_password = nil
|
820
|
+
|
821
|
+
# not in jss
|
801
822
|
else
|
802
823
|
@udid = args[:udid]
|
803
824
|
@serial_number = args[:serial_number]
|
@@ -806,9 +827,43 @@ module JSS
|
|
806
827
|
@alt_mac_address = args[:alt_mac_address]
|
807
828
|
@barcode1 = args[:barcode_1]
|
808
829
|
@barcode2 = args[:barcode_2]
|
809
|
-
end
|
830
|
+
end # if in jss
|
810
831
|
end # initialize
|
811
832
|
|
833
|
+
# Make all the keys of the @hardware hash available as top-level methods
|
834
|
+
# on the Computer instance.
|
835
|
+
#
|
836
|
+
# This is done by catching method_missing and seeing if the method exists
|
837
|
+
# as key of @hardware, and if so, retuning that value, if not, passing on
|
838
|
+
# the method_missing call.
|
839
|
+
# So:
|
840
|
+
# comp.processor_type
|
841
|
+
# is now the same as:
|
842
|
+
# comp.hardware[:processor_type]
|
843
|
+
#
|
844
|
+
# The reason for using `method_missing` rather than looping through the
|
845
|
+
# @hardware hash during initialization and doing `define_method` is
|
846
|
+
# speed. When instantiating lots of computers, defining the methods
|
847
|
+
# for each one, when those methods may not be needed, just slows things
|
848
|
+
# down. This way, they're only used when needed.
|
849
|
+
#
|
850
|
+
# This method may be expanded in the future to handle other ad-hoc,
|
851
|
+
# top-level methods.
|
852
|
+
#
|
853
|
+
def method_missing(method, *args, &block)
|
854
|
+
if @hardware.key? method
|
855
|
+
@hardware[method]
|
856
|
+
else
|
857
|
+
super
|
858
|
+
end # if
|
859
|
+
end # def
|
860
|
+
|
861
|
+
# Companion to method_missing, allows for easier debugging in backtraces
|
862
|
+
# that involve missing methods.
|
863
|
+
def respond_to_missing?(method, *)
|
864
|
+
@hardware.key?(method) || super
|
865
|
+
end
|
866
|
+
|
812
867
|
# @return [Array] the JSS groups to which thismachine belongs (smart and static)
|
813
868
|
#
|
814
869
|
def computer_groups
|
@@ -53,13 +53,6 @@ module JSS
|
|
53
53
|
include JSS::Creatable
|
54
54
|
include JSS::Sitable
|
55
55
|
|
56
|
-
# Class Methods
|
57
|
-
#####################################
|
58
|
-
|
59
|
-
def self.all_invitations(refresh = false, api: JSS.api)
|
60
|
-
all(refresh, api: api).map { |ci| ci[:invitation] }
|
61
|
-
end
|
62
|
-
|
63
56
|
# Class Constants
|
64
57
|
#####################################
|
65
58
|
|
@@ -73,12 +66,9 @@ module JSS
|
|
73
66
|
# It's also used in various error messages
|
74
67
|
RSRC_OBJECT_KEY = :computer_invitation
|
75
68
|
|
76
|
-
# these keys, as well as :id and :name, are present in valid API JSON data for this class
|
77
|
-
VALID_DATA_KEYS = [:invitation].freeze
|
78
|
-
|
79
69
|
# See JSS::APIObject
|
80
70
|
OTHER_LOOKUP_KEYS = {
|
81
|
-
invitation: {
|
71
|
+
invitation: { fetch_rsrc_key: :invitation }
|
82
72
|
}.freeze
|
83
73
|
|
84
74
|
# the object type for this object in
|
@@ -51,9 +51,6 @@ module JSS
|
|
51
51
|
# It's also used in various error messages
|
52
52
|
RSRC_OBJECT_KEY = :os_x_configuration_profile
|
53
53
|
|
54
|
-
# these keys, as well as :id and :name, are present in valid API JSON data for this class
|
55
|
-
VALID_DATA_KEYS = %i[distribution_method scope redeploy_on_update].freeze
|
56
|
-
|
57
54
|
# Our scopes deal with computers
|
58
55
|
SCOPE_TARGET_KEY = :computers
|
59
56
|
|
@@ -70,9 +70,6 @@ module JSS
|
|
70
70
|
### It's also used in various error messages
|
71
71
|
RSRC_OBJECT_KEY = :department
|
72
72
|
|
73
|
-
### these keys, as well as :id and :name, are present in valid API JSON data for this class
|
74
|
-
VALID_DATA_KEYS = []
|
75
|
-
|
76
73
|
# the object type for this object in
|
77
74
|
# the object history table.
|
78
75
|
# See {APIObject#add_object_history_entry}
|
@@ -71,9 +71,6 @@ module JSS
|
|
71
71
|
### It's also used in various error messages
|
72
72
|
RSRC_OBJECT_KEY = :distribution_point
|
73
73
|
|
74
|
-
### these keys, as well as :id and :name, are present in valid API JSON data for this class
|
75
|
-
VALID_DATA_KEYS = [:read_only_username, :ssh_username, :is_master ]
|
76
|
-
|
77
74
|
### what are the mount options? these are comma-separated, and are passed with -o
|
78
75
|
MOUNT_OPTIONS = 'nobrowse'
|
79
76
|
|
@@ -65,11 +65,7 @@ module JSS
|
|
65
65
|
|
66
66
|
EXTENDABLE = true
|
67
67
|
|
68
|
-
|
69
|
-
# but the ext. attr values that come with extendable objects refer to
|
70
|
-
# that data type as "Number". Here's an array with both, so we can
|
71
|
-
# work with ether more easily.
|
72
|
-
NUMERIC_TYPES = %w[Number Integer].freeze
|
68
|
+
INVALID_DATE = '-- INVALIDLY FORMATTED DATE --'.freeze
|
73
69
|
|
74
70
|
# Attribtues
|
75
71
|
###################################
|
@@ -77,9 +73,6 @@ module JSS
|
|
77
73
|
# @return [Array<Hash>] The extension attribute values for the object
|
78
74
|
attr_reader :extension_attributes
|
79
75
|
|
80
|
-
# @return [Hash] A mapping of Ext Attrib names to their values
|
81
|
-
attr_reader :ext_attrs
|
82
|
-
|
83
76
|
# Mixed-in Instance Methods
|
84
77
|
###################################
|
85
78
|
|
@@ -93,79 +86,124 @@ module JSS
|
|
93
86
|
def parse_ext_attrs
|
94
87
|
@extension_attributes = @init_data[:extension_attributes]
|
95
88
|
@extension_attributes ||= []
|
96
|
-
@ext_attrs = {}
|
97
89
|
|
90
|
+
# remember changes as they happen so
|
91
|
+
# we only send changes back to the server.
|
92
|
+
@changed_eas = []
|
93
|
+
end
|
94
|
+
|
95
|
+
# @return [Array<String>] the names of all known EAs
|
96
|
+
#
|
97
|
+
def ea_names
|
98
|
+
ea_types.keys
|
99
|
+
end
|
100
|
+
|
101
|
+
# @return [Hash{String => String}] EA names => data type
|
102
|
+
# (one of 'String', 'Number', or 'Date')
|
103
|
+
def ea_types
|
104
|
+
return @ea_types if @ea_types
|
105
|
+
|
106
|
+
@ea_types = {}
|
107
|
+
extension_attributes.each { |ea| @ea_types[ea[:name]] = ea[:type] }
|
108
|
+
@ea_types
|
109
|
+
end
|
110
|
+
|
111
|
+
# An easier-to-use hash of EA name to EA value.
|
112
|
+
# This isn't created until its needed, to speed up instantiation.
|
113
|
+
#
|
114
|
+
def ext_attrs
|
115
|
+
return @ext_attrs if @ext_attrs
|
116
|
+
|
117
|
+
@ext_attrs = {}
|
98
118
|
@extension_attributes.each do |ea|
|
99
|
-
|
119
|
+
@ext_attrs[ea[:name]] =
|
120
|
+
case ea[:type]
|
100
121
|
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
122
|
+
when 'Date'
|
123
|
+
begin # if there's random non-date data, the parse will fail
|
124
|
+
JSS.parse_datetime ea[:value]
|
125
|
+
rescue
|
126
|
+
INVALID_DATE
|
127
|
+
end
|
107
128
|
|
108
|
-
|
109
|
-
|
110
|
-
end # case
|
129
|
+
when *JSS::ExtensionAttribute::NUMERIC_TYPES
|
130
|
+
ea[:value].to_i unless ea[:value].to_s.empty?
|
111
131
|
|
112
|
-
|
132
|
+
else # String
|
133
|
+
ea[:value]
|
134
|
+
end # case
|
113
135
|
end # each do ea
|
114
136
|
|
115
|
-
|
116
|
-
# we only send changes back to the server.
|
117
|
-
@changed_eas = []
|
137
|
+
@ext_attrs
|
118
138
|
end
|
119
139
|
|
120
140
|
# Set the value of an extension attribute
|
121
141
|
#
|
122
|
-
#
|
123
|
-
#
|
142
|
+
# The new value is validated based on the data type of the
|
143
|
+
# Ext. Attrib:
|
144
|
+
#
|
145
|
+
# - If the ext. attrib. is defined with a data type of Integer/Number, the
|
146
|
+
# value must be an Integer.
|
147
|
+
# - If defined with a data type of Date, the value will be parsed as a
|
148
|
+
# timestamp, and parsing may raise an exception. Dates can't be blank.
|
149
|
+
# - If defined wth data type of String, `to_s` will be called on the value.
|
124
150
|
#
|
125
|
-
#
|
151
|
+
# By default, the full EA definition object is fetched to see if the EA's
|
152
|
+
# input type is 'popup menu', and if so, the new value must be one of the
|
153
|
+
# defined popup choices, or blank.
|
126
154
|
#
|
127
|
-
#
|
155
|
+
# The EA definitions used for popup validation are cached, so we don't have
|
156
|
+
# to reach out to the server every time. If you expect the definition to
|
157
|
+
# have changed since it was cached, provide a truthy value to the refresh:
|
158
|
+
# parameter
|
128
159
|
#
|
129
|
-
#
|
130
|
-
#
|
131
|
-
#
|
160
|
+
# To bypass popup validation complepletely, provide a falsey value to the
|
161
|
+
# validate_popup_choice: parameter.
|
162
|
+
# WARNING: beware that your value is the correct type and format, or you might
|
163
|
+
# get errors when saving back to the API.
|
164
|
+
#
|
165
|
+
# Note that while the Jamf Pro Web interface does not allow editing the
|
166
|
+
# values of Extension Attributes populated by Scripts or LDAP, the API does
|
167
|
+
# allow it. Bear in mind however that those values will be reset again at
|
168
|
+
# the next recon.
|
132
169
|
#
|
133
170
|
# @param name[String] the name of the extension attribute to set
|
134
171
|
#
|
135
|
-
# @param value[String,Time,
|
172
|
+
# @param value[String,Time,Integer] the new value for the extension
|
173
|
+
# attribute for this user
|
174
|
+
#
|
175
|
+
# @param validate_popup_choice[Boolean] validate the new value against the E.A. definition.
|
176
|
+
# Defaults to true.
|
177
|
+
#
|
178
|
+
# @param refresh[Boolean] Re-read the ext. attrib definition from the API,
|
179
|
+
# for popup validation.
|
136
180
|
#
|
137
181
|
# @return [void]
|
138
182
|
#
|
139
|
-
def set_ext_attr(name, value)
|
140
|
-
|
141
|
-
ea_def = self.class::EXT_ATTRIB_CLASS.fetch name: name, api: api
|
183
|
+
def set_ext_attr(name, value, validate_popup_choice: true, refresh: false)
|
184
|
+
raise ArgumentError, "Unknown Extension Attribute Name: '#{name}'" unless ea_types.key? name
|
142
185
|
|
143
|
-
|
144
|
-
|
145
|
-
end
|
186
|
+
value ||= JSS::BLANK
|
187
|
+
validate_popup_value(name, value, refresh) if validate_popup_choice
|
146
188
|
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
value = JSS.parse_datetime value
|
189
|
+
case ea_types[name]
|
190
|
+
when JSS::ExtensionAttribute::DATA_TYPE_DATE
|
191
|
+
raise JSS::InvalidDataError, "The value for #{name} must be a date, cannot be blank" if value == JSS::BLANK
|
151
192
|
|
152
|
-
|
153
|
-
raise JSS::InvalidDataError, "The value for #{name} must be an integer" unless value.is_a? Integer
|
193
|
+
value = JSS.parse_datetime value
|
154
194
|
|
155
|
-
|
156
|
-
|
195
|
+
when *JSS::ExtensionAttribute::NUMERIC_TYPES
|
196
|
+
raise JSS::InvalidDataError, "The value for #{name} must be an integer" unless value.is_a? Integer
|
157
197
|
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
@ext_attrs[name] = value
|
198
|
+
else # String
|
199
|
+
value = value.to_s
|
200
|
+
end # case
|
201
|
+
|
202
|
+
# update this ea hash in the @extension_attributes array
|
203
|
+
@extension_attributes.each { |ea| ea[:value] = value if ea[:name] == name }
|
204
|
+
|
205
|
+
# update the shortcut hash too
|
206
|
+
@ext_attrs[name] = value if @ext_attrs
|
169
207
|
@changed_eas << name
|
170
208
|
@need_to_update = true
|
171
209
|
end
|
@@ -210,6 +248,24 @@ module JSS
|
|
210
248
|
eaxml
|
211
249
|
end
|
212
250
|
|
213
|
-
|
251
|
+
# Used by set_ext_attr
|
252
|
+
def validate_popup_value(name, value, refresh)
|
253
|
+
# all popups can take blanks
|
254
|
+
return if value == JSS::BLANK
|
255
|
+
|
256
|
+
# get the ea def. instance from the api cache, or the api
|
257
|
+
api.ext_attr_definition_cache[self.class] ||= {}
|
258
|
+
api.ext_attr_definition_cache[self.class][name] = nil if refresh
|
259
|
+
api.ext_attr_definition_cache[self.class][name] ||= self.class::EXT_ATTRIB_CLASS.fetch name: name, api: api
|
260
|
+
|
261
|
+
ea_def = api.ext_attr_definition_cache[self.class][name]
|
262
|
+
return unless ea_def.from_popup_menu?
|
263
|
+
|
264
|
+
return if ea_def.popup_choices.include? value.to_s
|
265
|
+
|
266
|
+
raise JSS::UnsupportedError, "The value for #{name} must be one of: '#{ea_def.popup_choices.join("' '")}'"
|
267
|
+
end
|
268
|
+
|
269
|
+
end # module extendable
|
214
270
|
|
215
271
|
end # module JSS
|