ruby-jss 1.2.0 → 1.2.2
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 +23 -0
- data/lib/jss/api_connection.rb +20 -13
- data/lib/jss/api_object.rb +105 -1
- data/lib/jss/api_object/extension_attribute.rb +165 -68
- data/lib/jss/api_object/extension_attribute/computer_extension_attribute.rb +176 -282
- data/lib/jss/api_object/extension_attribute/mobile_device_extension_attribute.rb +66 -171
- data/lib/jss/api_object/extension_attribute/user_extension_attribute.rb +51 -92
- data/lib/jss/api_object/management_history.rb +29 -0
- data/lib/jss/db_connection.rb +1 -0
- data/lib/jss/version.rb +1 -1
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 83836b3e036658c6254365b47fe0d2b00b03dd3e75e42cd80f27ba29988d6b3b
|
4
|
+
data.tar.gz: 2e5b73fd7fd6f76f474170cc40165a156b79e37e96807fcfad0ee987e5df8018
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fbf7ba2137f3de2a8aaff77c8c53f84086913f6ee154345d3419e94e874422116405d04ab1448c254a09e1375a734738050f07738d860245dc629f8a157c96d8
|
7
|
+
data.tar.gz: 4480768a24e0cd1372f35bafcce1d2effe646e09da4ee44bf195deff2451f46d946c94c8c34aa0ecb74dd5177ad274f4c7f9c7e0c60abf1fe0da55b1271d0b00
|
data/CHANGES.md
CHANGED
@@ -4,6 +4,29 @@ All notable changes to this project will be documented in this file.
|
|
4
4
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
5
5
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
6
6
|
|
7
|
+
## \[1.2.2] - 2019-10-31
|
8
|
+
### Added
|
9
|
+
- the ManagementHistory mixin module used by the Computer and MobileDevice classes, now has a `last_mdm_contact` class and instance method, which returns a Time object for the timestamp of the most recent completed MDM command. This is useful for MobileDevices, which don't have anything like the `last_checkin` value for comptuers, indicating real communication between the device and Jamf Pro.
|
10
|
+
Note that the `last_inventory_update` value does NOT indicate such communication, since that timestamp is updated when values are changed via the API
|
11
|
+
|
12
|
+
- All APIObject Subclasses (Policy, Computer, MobileDevice, ComputerGroup, etc..) now have `get_raw`, `post_raw` & `put_raw` class methods, which are simpler wrappers for APIConnection#get_rsrc, #post_rsrc, and #put_rsrc.
|
13
|
+
- `get_raw` takes an object's id, and returns the 'raw' JSON (parsed into a ruby Hash with symbolized keys) or a REXML::Document (from which you'll probably want to use the `root` element). If you pass `as_string: true` you'll get the un-parsed JSON or XML string directly from the API
|
14
|
+
This can be useful when you need to retrieve the full object, to get some data not available in the summary-list, but instantiating the full ruby class is too slow.
|
15
|
+
- `post_raw` & `put_raw` can send raw XML to the API without instantiating objects. In some cases, where you're making simple changes to simple XML, this can be faster than fetching a full instance and the re-saving it.
|
16
|
+
WARNING You must create or acquire the XML to be sent, and no validation will be performed on it. It must be a String of XML, or something that returns such a string with #to_s, such as a REXML::Document, or a REXML::Element.
|
17
|
+
|
18
|
+
- APIConnection#get_rsrc now takes the boolean raw_json: parameter (defaults to false). If true, the raw JSON string is
|
19
|
+
returned, not parsed into a Hash. When requesting XML, it already comes as a string.
|
20
|
+
|
21
|
+
- ExtensionAttribute#attribute_mapping getter & setter for EAs that have the 'LDAP Attribute Mapping' input type.
|
22
|
+
|
23
|
+
### Fixed
|
24
|
+
- DB_CNX.valid_server? now specifies utf8 charset, needed for newer versions of mysql.
|
25
|
+
|
26
|
+
### Changed
|
27
|
+
- Cleaned up and modernized ExtensionAttribute and its subclasses. Marked a few things as deprecated: recon_display, scripting_language, and platform, all in ComputerExtensionAttribute.
|
28
|
+
- Better error message when using `APIObject.fetch :random` on a subclass with no objects
|
29
|
+
|
7
30
|
## \[1.2.0] - 2019-10-17
|
8
31
|
### Added
|
9
32
|
- APIConnection#flushcache can be used to flush all cached data, or just for specific APIObject lists or ExtensionAttribute definitions. This is now used more often throughout ruby-jss.
|
data/lib/jss/api_connection.rb
CHANGED
@@ -327,6 +327,9 @@ module JSS
|
|
327
327
|
# These classes are extendable, and may need cache flushing for EA definitions
|
328
328
|
EXTENDABLE_CLASSES = [JSS::Computer, JSS::MobileDevice, JSS::User].freeze
|
329
329
|
|
330
|
+
# values for the format param of get_rsrc
|
331
|
+
GET_FORMATS = %i[json xml].freeze
|
332
|
+
|
330
333
|
# Attributes
|
331
334
|
#####################################
|
332
335
|
|
@@ -546,36 +549,40 @@ module JSS
|
|
546
549
|
# after the 'JSSResource/' ) The resource must be properly URL escaped
|
547
550
|
# beforehand. Note: URL.encode is deprecated, use CGI.escape
|
548
551
|
#
|
549
|
-
# By default we get the data in JSON, and parse it
|
550
|
-
# into a ruby data structure (arrays, hashes, strings, etc)
|
552
|
+
# By default we get the data in JSON, and parse it into a ruby Hash
|
551
553
|
# with symbolized Hash keys.
|
552
554
|
#
|
555
|
+
# If the second parameter is :xml then the XML version is retrieved and
|
556
|
+
# returned as a String.
|
557
|
+
#
|
558
|
+
# To get the raw JSON string as it comes from the API, pass raw_json: true
|
559
|
+
#
|
553
560
|
# @param rsrc[String] the resource to get
|
554
561
|
# (the part of the API url after the 'JSSResource/' )
|
555
562
|
#
|
556
563
|
# @param format[Symbol] either ;json or :xml
|
557
|
-
#
|
564
|
+
# If the second argument is :xml, the XML data is returned as a String.
|
565
|
+
#
|
566
|
+
# @param raw_json[Boolean] When GETting JSON, return the raw unparsed string
|
567
|
+
# (the XML is always returned as a raw string)
|
558
568
|
#
|
559
569
|
# @return [Hash,String] the result of the get
|
560
570
|
#
|
561
|
-
def get_rsrc(rsrc, format = :json)
|
562
|
-
# puts object_id
|
571
|
+
def get_rsrc(rsrc, format = :json, raw_json: false)
|
563
572
|
validate_connected
|
564
|
-
|
565
|
-
raise JSS::InvalidDataError, 'format must be :json or :xml' unless %i[json xml].include? format
|
573
|
+
raise JSS::InvalidDataError, 'format must be :json or :xml' unless GET_FORMATS.include? format
|
566
574
|
|
567
575
|
begin
|
568
576
|
@last_http_response = @cnx[rsrc].get(accept: format)
|
577
|
+
return JSON.parse(@last_http_response.body, symbolize_names: true) if format == :json && !raw_json
|
578
|
+
|
569
579
|
@last_http_response.body
|
570
580
|
rescue RestClient::ExceptionWithResponse => e
|
571
581
|
handle_http_error e
|
572
582
|
end
|
573
|
-
# TODO: make sure we're returning the String version of the
|
574
|
-
# response (i.e. its body) here and in POST, PUT, DELETE.
|
575
|
-
format == :json ? JSON.parse(@last_http_response, symbolize_names: true) : @last_http_response
|
576
583
|
end
|
577
584
|
|
578
|
-
#
|
585
|
+
# Update an existing JSS resource
|
579
586
|
#
|
580
587
|
# @param rsrc[String] the API resource being changed, the URL part after 'JSSResource/'
|
581
588
|
#
|
@@ -1219,7 +1226,7 @@ module JSS
|
|
1219
1226
|
raise exception
|
1220
1227
|
when RestClient::Conflict
|
1221
1228
|
err = JSS::ConflictError
|
1222
|
-
msg_matcher = /<p>Error:(
|
1229
|
+
msg_matcher = /<p>Error:(.*?)(<|$)/m
|
1223
1230
|
when RestClient::BadRequest
|
1224
1231
|
err = JSS::BadRequestError
|
1225
1232
|
msg_matcher = %r{>Bad Request</p>\n<p>(.*?)</p>\n<p>You can get technical detail}m
|
@@ -1227,7 +1234,7 @@ module JSS
|
|
1227
1234
|
raise
|
1228
1235
|
else
|
1229
1236
|
err = JSS::APIRequestError
|
1230
|
-
msg_matcher = %r{<body.*?>(
|
1237
|
+
msg_matcher = %r{<body.*?>(.*?)</body>}m
|
1231
1238
|
end
|
1232
1239
|
exception.http_body =~ msg_matcher
|
1233
1240
|
msg = Regexp.last_match(1)
|
data/lib/jss/api_object.rb
CHANGED
@@ -809,7 +809,12 @@ module JSS
|
|
809
809
|
end
|
810
810
|
|
811
811
|
# a random object?
|
812
|
-
|
812
|
+
if searchterm == :random
|
813
|
+
rnd_thing = all.sample
|
814
|
+
raise JSS::NoSuchItemError, "No #{self::RSRC_LIST_KEY} found" unless rnd_thing
|
815
|
+
|
816
|
+
return new id: rnd_thing[:id], api: api
|
817
|
+
end
|
813
818
|
|
814
819
|
# get the lookup key and value, if given
|
815
820
|
fetch_key, fetch_val = args.to_a.first
|
@@ -851,6 +856,105 @@ module JSS
|
|
851
856
|
end
|
852
857
|
end # fetch
|
853
858
|
|
859
|
+
# Fetch the mostly- or fully-raw JSON or XML data for an object of this
|
860
|
+
# subclass.
|
861
|
+
#
|
862
|
+
# By default, returns the JSON data parsed into a Hash.
|
863
|
+
#
|
864
|
+
# When format: is anything but :json, returns the XML data parsed into
|
865
|
+
# a REXML::Document
|
866
|
+
#
|
867
|
+
# When as_string: is truthy, returns an unparsed JSON String (or XML String
|
868
|
+
# if format: is not :json) as it comes directly from the API.
|
869
|
+
#
|
870
|
+
# When fetching raw JSON, the returned Hash will have its keys symbolized.
|
871
|
+
#
|
872
|
+
# This can be substantialy faster than instantiating, especially when you don't need
|
873
|
+
# all the ruby goodness of a full instance, but just want a few values for
|
874
|
+
# an object that aren't available in the `all` data
|
875
|
+
#
|
876
|
+
# This is really just a wrapper around {APIConnection.get_rsrc} that
|
877
|
+
# automatically fills in the RSRC::BASE value for you.
|
878
|
+
#
|
879
|
+
# @param id [Integer] the id of the object to fetch
|
880
|
+
#
|
881
|
+
# @param format[Symbol] :json or :xml, defaults to :json
|
882
|
+
#
|
883
|
+
# @param as_string[Boolean] return the raw JSON or XML string as it comes
|
884
|
+
# from the API, do not parse into a Hash or REXML::Document
|
885
|
+
#
|
886
|
+
# @param api[JSS::APIConnection] the connection thru which to fetch this
|
887
|
+
# object. Defaults to the deault API connection in JSS.api
|
888
|
+
#
|
889
|
+
# @return [Hash, REXML::Document, String] the raw data for the object
|
890
|
+
#
|
891
|
+
def self.get_raw(id, format: :json, as_string: false, api: JSS.api)
|
892
|
+
validate_not_metaclass(self)
|
893
|
+
rsrc = "#{self::RSRC_BASE}/id/#{id}"
|
894
|
+
data = api.get_rsrc rsrc, format, raw_json: as_string
|
895
|
+
return data if format == :json || as_string
|
896
|
+
|
897
|
+
REXML::Document.new(data)
|
898
|
+
rescue RestClient::NotFound
|
899
|
+
raise JSS::NoSuchItemError, "No #{self} with id #{id}"
|
900
|
+
end
|
901
|
+
|
902
|
+
# PUT some raw XML to the API for a given id in this subclass.
|
903
|
+
#
|
904
|
+
# WARNING: You must create or acquire the XML to be sent, and no validation
|
905
|
+
# will be performed on it. It must be a String, or something that returns
|
906
|
+
# an XML string with #to_s, such as a REXML::Document, or
|
907
|
+
# a REXML::Element.
|
908
|
+
#
|
909
|
+
# In some cases, where you're making simple changes to simple XML,
|
910
|
+
# this can be faster than fetching a full instance and the re-saving it.
|
911
|
+
#
|
912
|
+
# This is really just a wrapper around {APIConnection.put_rsrc} that
|
913
|
+
# automatically fills in the RSRC::BASE value for you.
|
914
|
+
#
|
915
|
+
# @param id [Integer] the id of the object to PUT
|
916
|
+
#
|
917
|
+
# @param xml [String, #to_s] The XML to send
|
918
|
+
#
|
919
|
+
# @param api[JSS::APIConnection] the connection thru which to fetch this
|
920
|
+
# object. Defaults to the deault API connection in JSS.api
|
921
|
+
#
|
922
|
+
# @return [REXML::Document] the XML response from the API
|
923
|
+
#
|
924
|
+
def self.put_raw(id, xml, api: JSS.api)
|
925
|
+
validate_not_metaclass(self)
|
926
|
+
rsrc = "#{self::RSRC_BASE}/id/#{id}"
|
927
|
+
REXML::Document.new(api.put_rsrc rsrc, xml.to_s)
|
928
|
+
rescue RestClient::NotFound
|
929
|
+
raise JSS::NoSuchItemError, "No #{self} with id #{id}"
|
930
|
+
end
|
931
|
+
|
932
|
+
# POST some raw XML to the API for a given id in this subclass.
|
933
|
+
#
|
934
|
+
# WARNING: You must create or acquire the XML to be sent, and no validation
|
935
|
+
# will be performed on it. It must be a String, or something that returns
|
936
|
+
# an XML string with #to_s, such as a REXML::Document, or
|
937
|
+
# a REXML::Element.
|
938
|
+
#
|
939
|
+
# This probably isn't as much of a speed gain as get_raw or put_raw, as
|
940
|
+
# opposed to instantiating a ruby object, but might still be useful.
|
941
|
+
#
|
942
|
+
# This is really just a wrapper around {APIConnection.post_rsrc} that
|
943
|
+
# automatically fills in the RSRC::BASE value for you.
|
944
|
+
#
|
945
|
+
# @param xml [String, #to_s] The XML to send
|
946
|
+
#
|
947
|
+
# @param api[JSS::APIConnection] the connection thru which to fetch this
|
948
|
+
# object. Defaults to the deault API connection in JSS.api
|
949
|
+
#
|
950
|
+
# @return [REXML::Document] the XML response from the API
|
951
|
+
#
|
952
|
+
def self.post_raw( xml, api: JSS.api)
|
953
|
+
validate_not_metaclass(self)
|
954
|
+
rsrc = "#{self::RSRC_BASE}/id/-1"
|
955
|
+
REXML::Document.new(api.post_rsrc rsrc, xml.to_s)
|
956
|
+
end
|
957
|
+
|
854
958
|
# Make a ruby instance of a not-yet-existing APIObject.
|
855
959
|
#
|
856
960
|
# This is how to create new objects in the JSS. A name: must be provided,
|
@@ -20,10 +20,7 @@
|
|
20
20
|
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
21
21
|
# KIND, either express or implied. See the Apache License for the specific
|
22
22
|
# language governing permissions and limitations under the Apache License.
|
23
|
-
#
|
24
|
-
#
|
25
23
|
|
26
|
-
#
|
27
24
|
module JSS
|
28
25
|
|
29
26
|
# Classes
|
@@ -89,20 +86,33 @@ module JSS
|
|
89
86
|
INPUT_TYPE_SCRIPT = 'script'.freeze
|
90
87
|
INPUT_TYPE_LDAP = 'LDAP Attribute Mapping'.freeze
|
91
88
|
|
92
|
-
INPUT_TYPES = [
|
89
|
+
INPUT_TYPES = [
|
90
|
+
INPUT_TYPE_FIELD,
|
91
|
+
INPUT_TYPE_POPUP,
|
92
|
+
INPUT_TYPE_SCRIPT,
|
93
|
+
INPUT_TYPE_LDAP
|
94
|
+
].freeze
|
95
|
+
|
93
96
|
DEFAULT_INPUT_TYPE = INPUT_TYPE_FIELD
|
94
97
|
|
95
98
|
# Where can it be displayed in the WebApp?
|
96
|
-
|
99
|
+
WEB_DISPLAY_CHOICE_GENERAL = 'General'.freeze
|
100
|
+
WEB_DISPLAY_CHOICE_OS = 'Operating System'.freeze
|
101
|
+
WEB_DISPLAY_CHOICE_HW = 'Hardware'.freeze
|
102
|
+
WEB_DISPLAY_CHOICE_USER_LOC = 'User and Location'.freeze
|
103
|
+
WEB_DISPLAY_CHOICE_PURCHASING = 'Purchasing'.freeze
|
104
|
+
WEB_DISPLAY_CHOICE_EAS = 'Extension Attributes'.freeze
|
105
|
+
|
97
106
|
WEB_DISPLAY_CHOICES = [
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
107
|
+
WEB_DISPLAY_CHOICE_GENERAL,
|
108
|
+
WEB_DISPLAY_CHOICE_OS,
|
109
|
+
WEB_DISPLAY_CHOICE_HW,
|
110
|
+
WEB_DISPLAY_CHOICE_USER_LOC,
|
111
|
+
WEB_DISPLAY_CHOICE_PURCHASING,
|
112
|
+
WEB_DISPLAY_CHOICE_EAS
|
104
113
|
].freeze
|
105
|
-
|
114
|
+
|
115
|
+
DEFAULT_WEB_DISPLAY_CHOICE = WEB_DISPLAY_CHOICE_EAS
|
106
116
|
|
107
117
|
LAST_RECON_FIELD = 'Last Inventory Update'.freeze
|
108
118
|
LAST_RECON_FIELD_SYM = LAST_RECON_FIELD.tr(' ', '_').to_sym
|
@@ -117,6 +127,7 @@ module JSS
|
|
117
127
|
|
118
128
|
# @return [String] description of the ext attrib
|
119
129
|
attr_reader :description
|
130
|
+
alias desc description
|
120
131
|
|
121
132
|
# @return [String] the type of data created by the EA. Must be one of DATA_TYPES
|
122
133
|
attr_reader :data_type
|
@@ -127,14 +138,15 @@ module JSS
|
|
127
138
|
# @return [Array<String>] the choices available in the UI when the @input_type is "Pop-up Menu"
|
128
139
|
attr_reader :popup_choices
|
129
140
|
|
141
|
+
# @return [String] the LDAP attribute for the User's ldap entry that maps to this EA, when input type is INPUT_TYPE_LDAP
|
142
|
+
attr_reader :attribute_mapping
|
143
|
+
|
130
144
|
# @return [String] In which part of the web UI does the data appear?
|
131
145
|
attr_reader :web_display
|
132
146
|
|
133
|
-
|
134
147
|
# Constructor
|
135
148
|
###################################
|
136
149
|
|
137
|
-
|
138
150
|
# @see JSS::APIObject#initialize
|
139
151
|
#
|
140
152
|
def initialize(args = {})
|
@@ -142,19 +154,30 @@ module JSS
|
|
142
154
|
|
143
155
|
# @init_data now has the raw data
|
144
156
|
# so fill in our attributes or set defaults
|
145
|
-
|
146
157
|
@description = @init_data[:description]
|
147
158
|
@data_type = @init_data[:data_type] || DEFAULT_DATA_TYPE
|
148
|
-
@web_display = @init_data[:inventory_display] || DEFAULT_WEB_DISPLAY_CHOICE
|
149
159
|
|
150
160
|
if @init_data[:input_type]
|
151
|
-
@input_type = @init_data[:input_type][:type]
|
161
|
+
@input_type = @init_data[:input_type][:type]
|
162
|
+
|
163
|
+
@script = @init_data[:input_type][:script]
|
164
|
+
|
165
|
+
@attribute_mapping = @init_data[:input_type][:attribute_mapping]
|
152
166
|
@popup_choices = @init_data[:input_type][:popup_choices]
|
153
167
|
# popups can always contain blank
|
154
168
|
@popup_choices << JSS::BLANK if @popup_choices
|
155
|
-
|
156
|
-
|
169
|
+
|
170
|
+
# These two are deprecated - windows won't be supported for long
|
171
|
+
@platform = @init_data[:input_type][:platform]
|
172
|
+
@scripting_language = @init_data[:input_type][:scripting_language]
|
157
173
|
end
|
174
|
+
@input_type ||= DEFAULT_INPUT_TYPE
|
175
|
+
|
176
|
+
@enabled = @init_data[:enabled]
|
177
|
+
|
178
|
+
@web_display = @init_data[:inventory_display] || DEFAULT_WEB_DISPLAY_CHOICE
|
179
|
+
# deprecated - no longer in the UI
|
180
|
+
@recon_display = @init_data[:recon_display] || @web_display
|
158
181
|
|
159
182
|
# the name of the EA might have spaces and caps, which the will come to us as symbols with the spaces
|
160
183
|
# as underscores, like this.
|
@@ -167,19 +190,16 @@ module JSS
|
|
167
190
|
# @see JSS::Creatable#create
|
168
191
|
#
|
169
192
|
def create
|
170
|
-
|
171
|
-
raise MissingDataError, 'No popup_choices set for Pop-up Menu input_type.' unless @popup_choices.is_a?(Array) && !@popup_choices.empty?
|
172
|
-
end
|
193
|
+
validate_for_save
|
173
194
|
super
|
174
195
|
end
|
175
196
|
|
176
197
|
# @see JSS::Updatable#update
|
177
198
|
#
|
178
199
|
def update
|
179
|
-
|
180
|
-
raise MissingDataError, 'No popup_choices set for Pop-up Menu input_type.' unless @popup_choices.is_a?(Array) && !@popup_choices.empty?
|
181
|
-
end
|
200
|
+
validate_for_save
|
182
201
|
super
|
202
|
+
# this flushes the cached EA itself, used for validating ea values.
|
183
203
|
@api.flushcache self.class
|
184
204
|
end
|
185
205
|
|
@@ -222,10 +242,12 @@ module JSS
|
|
222
242
|
# @return [void]
|
223
243
|
#
|
224
244
|
def description=(new_val)
|
225
|
-
|
245
|
+
new_val = new_val.to_s
|
246
|
+
return if @description == new_val
|
247
|
+
|
226
248
|
@description = new_val
|
227
249
|
@need_to_update = true
|
228
|
-
end #
|
250
|
+
end # name=(newname)
|
229
251
|
|
230
252
|
# Change the data type of this EA
|
231
253
|
#
|
@@ -234,24 +256,28 @@ module JSS
|
|
234
256
|
# @return [void]
|
235
257
|
#
|
236
258
|
def data_type=(new_val)
|
237
|
-
return
|
259
|
+
return if @data_type == new_val
|
238
260
|
raise JSS::InvalidDataError, "data_type must be a string, one of: '#{DATA_TYPES.join("', '")}'" unless DATA_TYPES.include? new_val
|
261
|
+
|
239
262
|
@data_type = new_val
|
240
263
|
@need_to_update = true
|
241
|
-
end
|
264
|
+
end
|
242
265
|
|
243
266
|
# Change the inventory_display of this EA
|
244
267
|
#
|
245
|
-
# @param new_val[String] the new value, which must be a member of
|
268
|
+
# @param new_val[String] the new value, which must be a member of WEB_DISPLAY_CHOICES
|
246
269
|
#
|
247
270
|
# @return [void]
|
248
271
|
#
|
249
272
|
def web_display=(new_val)
|
250
|
-
return
|
251
|
-
|
273
|
+
return if @web_display == new_val
|
274
|
+
unless WEB_DISPLAY_CHOICES.include? new_val
|
275
|
+
raise JSS::InvalidDataError, "inventory_display must be a string, one of: #{WEB_DISPLAY_CHOICES.join(', ')}"
|
276
|
+
end
|
277
|
+
|
252
278
|
@web_display = new_val
|
253
279
|
@need_to_update = true
|
254
|
-
end
|
280
|
+
end
|
255
281
|
|
256
282
|
# Change the input type of this EA
|
257
283
|
#
|
@@ -260,17 +286,39 @@ module JSS
|
|
260
286
|
# @return [void]
|
261
287
|
#
|
262
288
|
def input_type=(new_val)
|
263
|
-
return
|
289
|
+
return if @input_type == new_val
|
264
290
|
raise JSS::InvalidDataError, "input_type must be a string, one of: #{INPUT_TYPES.join(', ')}" unless INPUT_TYPES.include? new_val
|
291
|
+
|
265
292
|
@input_type = new_val
|
266
|
-
|
293
|
+
case @input_type
|
294
|
+
when INPUT_TYPE_FIELD
|
295
|
+
@script = nil
|
296
|
+
@scripting_language = nil
|
297
|
+
@platform = nil
|
298
|
+
@popup_choices = nil
|
299
|
+
@attribute_mapping = nil
|
300
|
+
when INPUT_TYPE_POPUP
|
301
|
+
@script = nil
|
302
|
+
@scripting_language = nil
|
303
|
+
@platform = nil
|
304
|
+
@attribute_mapping = nil
|
305
|
+
when INPUT_TYPE_SCRIPT
|
306
|
+
@popup_choices = nil
|
307
|
+
@attribute_mapping = nil
|
308
|
+
when INPUT_TYPE_LDAP
|
309
|
+
@script = nil
|
310
|
+
@scripting_language = nil
|
311
|
+
@platform = nil
|
312
|
+
@popup_choices = nil
|
313
|
+
end # case
|
314
|
+
|
267
315
|
@need_to_update = true
|
268
|
-
end
|
316
|
+
end
|
269
317
|
|
270
318
|
# Change the Popup Choices of this EA
|
271
319
|
# New value must be an Array, the items will be converted to Strings.
|
272
320
|
#
|
273
|
-
# This automatically sets input_type to
|
321
|
+
# This automatically sets input_type to INPUT_TYPE_POPUP
|
274
322
|
#
|
275
323
|
# Values are checked to ensure they match the @data_type
|
276
324
|
# Note, Dates must be in the format "YYYY-MM-DD hh:mm:ss"
|
@@ -280,7 +328,7 @@ module JSS
|
|
280
328
|
# @return [void]
|
281
329
|
#
|
282
330
|
def popup_choices=(new_val)
|
283
|
-
return
|
331
|
+
return if @popup_choices == new_val
|
284
332
|
raise JSS::InvalidDataError, 'popup_choices must be an Array' unless new_val.is_a?(Array)
|
285
333
|
|
286
334
|
# convert each one to a String,
|
@@ -295,10 +343,29 @@ module JSS
|
|
295
343
|
end
|
296
344
|
v
|
297
345
|
end
|
346
|
+
|
298
347
|
self.input_type = INPUT_TYPE_POPUP
|
299
348
|
@popup_choices = new_val
|
300
349
|
@need_to_update = true
|
301
|
-
end
|
350
|
+
end
|
351
|
+
|
352
|
+
# Change the LDAP Attribute Mapping of this EA
|
353
|
+
# New value must be a String, or respond correctly to #to_s
|
354
|
+
#
|
355
|
+
# This automatically sets input_type to INPUT_TYPE_LDAP
|
356
|
+
#
|
357
|
+
# @param new_val[String, #to_s] the new value
|
358
|
+
#
|
359
|
+
# @return [void]
|
360
|
+
#
|
361
|
+
def attribute_mapping=(new_mapping)
|
362
|
+
new_mapping = JSS::Validate.non_empty_string new_mapping.to_s
|
363
|
+
return if new_mapping == @attribute_mapping
|
364
|
+
|
365
|
+
self.input_type = INPUT_TYPE_LDAP
|
366
|
+
@attribute_mapping = new_mapping
|
367
|
+
@need_to_update = true
|
368
|
+
end
|
302
369
|
|
303
370
|
# Get an Array of Hashes for all inventory objects
|
304
371
|
# with a desired result in their latest report for this EA.
|
@@ -322,7 +389,10 @@ module JSS
|
|
322
389
|
#
|
323
390
|
def all_with_result(search_type, desired_value)
|
324
391
|
raise JSS::NoSuchItemError, "EA Not In JSS! Use #create to create this #{self.class::RSRC_OBJECT_KEY}." unless @in_jss
|
325
|
-
|
392
|
+
unless JSS::Criteriable::Criterion::SEARCH_TYPES.include? search_type.to_s
|
393
|
+
raise JSS::InvalidDataError, 'Invalid search_type, see JSS::Criteriable::Criterion::SEARCH_TYPES'
|
394
|
+
end
|
395
|
+
|
326
396
|
begin
|
327
397
|
search_class = self.class::TARGET_CLASS::SEARCH_CLASS
|
328
398
|
acs = search_class.make api: @api, name: "ruby-jss-EA-result-search-#{Time.now.to_jss_epoch}"
|
@@ -335,13 +405,14 @@ module JSS
|
|
335
405
|
results = []
|
336
406
|
|
337
407
|
acs.search_results.each do |i|
|
338
|
-
value =
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
408
|
+
value =
|
409
|
+
case @data_type
|
410
|
+
when 'Date' then JSS.parse_datetime i[@symbolized_name]
|
411
|
+
when 'Integer' then i[@symbolized_name].to_i
|
412
|
+
else i[@symbolized_name]
|
413
|
+
end # case
|
343
414
|
results << { id: i[:id], name: i[:name], value: value }
|
344
|
-
end
|
415
|
+
end # each
|
345
416
|
ensure
|
346
417
|
acs.delete if acs.is_a? self.class::TARGET_CLASS::SEARCH_CLASS
|
347
418
|
end
|
@@ -375,6 +446,7 @@ module JSS
|
|
375
446
|
#
|
376
447
|
def latest_values
|
377
448
|
raise JSS::NoSuchItemError, "EA Not In JSS! Use #create to create this #{self.class::RSRC_OBJECT_KEY}." unless @in_jss
|
449
|
+
|
378
450
|
tmp_advsrch = "ruby-jss-EA-latest-search-#{Time.now.to_jss_epoch}"
|
379
451
|
|
380
452
|
begin
|
@@ -391,11 +463,12 @@ module JSS
|
|
391
463
|
results = []
|
392
464
|
|
393
465
|
acs.search_results.each do |i|
|
394
|
-
value =
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
466
|
+
value =
|
467
|
+
case @data_type
|
468
|
+
when 'Date' then JSS.parse_datetime i[@symbolized_name]
|
469
|
+
when 'Integer' then i[@symbolized_name].to_i
|
470
|
+
else i[@symbolized_name]
|
471
|
+
end # case
|
399
472
|
|
400
473
|
as_of = Time.parse(i[LAST_RECON_FIELD_SYM]) if i[LAST_RECON_FIELD_SYM] != ''
|
401
474
|
|
@@ -404,42 +477,66 @@ module JSS
|
|
404
477
|
ensure
|
405
478
|
if defined? acs
|
406
479
|
acs.delete if acs
|
407
|
-
|
408
|
-
search_class.fetch(name: tmp_advsrch, api: @api).delete
|
480
|
+
elsif search_class.all_names(:refresh, api: @api).include? tmp_advsrch
|
481
|
+
search_class.fetch(name: tmp_advsrch, api: @api).delete
|
409
482
|
end
|
410
483
|
end
|
411
484
|
|
412
485
|
results
|
413
486
|
end
|
414
487
|
|
415
|
-
# aliases
|
416
|
-
|
417
|
-
alias desc description
|
418
|
-
|
419
488
|
# Private Instance Methods
|
420
489
|
###################
|
421
490
|
|
422
491
|
private
|
423
492
|
|
424
|
-
#
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
493
|
+
# make sure things are OK for saving to Jamf
|
494
|
+
def validate_for_save
|
495
|
+
case @input_type
|
496
|
+
when INPUT_TYPE_POPUP
|
497
|
+
raise MissingDataError, "No popup_choices set for input type: #{INPUT_TYPE_POPUP}" unless @popup_choices.is_a?(Array) && !@popup_choices.empty?
|
498
|
+
when INPUT_TYPE_LDAP
|
499
|
+
raise MissingDataError, "No attribute_mapping set for input type: #{INPUT_TYPE_LDAP}" if @attribute_mapping.to_s.empty?
|
500
|
+
when INPUT_TYPE_SCRIPT
|
501
|
+
raise MissingDataError, "No script set for input_type: #{INPUT_TYPE_SCRIPT}" unless @script
|
502
|
+
|
503
|
+
# Next two lines DEPRECATED
|
504
|
+
@platform ||= 'Mac'
|
505
|
+
raise MissingDataError, "No scripting_language set for Windows '#{INPUT_TYPE_SCRIPT}' input_type." if @platform == 'Windows' && !@scripting_language
|
506
|
+
end
|
507
|
+
end
|
508
|
+
|
509
|
+
# Return a REXML doc string for this ext attr, with the current values.
|
510
|
+
def rest_xml
|
430
511
|
ea = REXML::Element.new self.class::RSRC_OBJECT_KEY.to_s
|
431
512
|
ea.add_element('name').text = @name
|
432
|
-
ea.add_element('description').text = @description
|
513
|
+
ea.add_element('description').text = @description if @description
|
433
514
|
ea.add_element('data_type').text = @data_type
|
434
|
-
|
515
|
+
|
516
|
+
unless self.class == JSS::UserExtensionAttribute
|
517
|
+
ea.add_element('inventory_display').text = @web_display if @web_display
|
518
|
+
ea.add_element('recon_display').text = @recon_display if @recon_display
|
519
|
+
end
|
435
520
|
|
436
521
|
it = ea.add_element('input_type')
|
437
522
|
it.add_element('type').text = @input_type
|
438
|
-
|
523
|
+
|
524
|
+
case @input_type
|
525
|
+
when INPUT_TYPE_POPUP
|
439
526
|
pcs = it.add_element('popup_choices')
|
440
|
-
@popup_choices.each { |pc| pcs.add_element('choice').text = pc }
|
527
|
+
@popup_choices.each { |pc| pcs.add_element('choice').text = pc } if @popup_choices.is_a? Array
|
528
|
+
when INPUT_TYPE_LDAP
|
529
|
+
it.add_element('attribute_mapping').text = @attribute_mapping
|
530
|
+
when INPUT_TYPE_SCRIPT
|
531
|
+
ea.add_element('enabled').text = @enabled ? 'true' : 'false'
|
532
|
+
it.add_element('script').text = @script
|
533
|
+
it.add_element('platform').text = @platform || 'Mac'
|
534
|
+
it.add_element('scripting_language').text = @scripting_language if @scripting_language
|
441
535
|
end
|
442
|
-
|
536
|
+
|
537
|
+
doc = REXML::Document.new APIConnection::XML_HEADER
|
538
|
+
doc << ea
|
539
|
+
doc.to_s
|
443
540
|
end # rest xml
|
444
541
|
|
445
542
|
end # class ExtAttrib
|