class Jamf::AdvancedSearch
A Parent class for Advanced Computer
, MobileDevice
, and User
searchs
Subclasses must define:
-
the constant RESULT_CLASS which is the
JSS
Module class of the item returned by the search, e.g.Jamf::Computer
-
the constant RESULT_ID_FIELDS, which is an
Array
of Symbols that come from the API in thesearch_results
along with the symbolized display fields. E.g. for AdvancedComputerSearches, :id, :name, and :udid are present along with whatever display fields have been defined.
@see Jamf::AdvancedComputerSearch
@see Jamf::AdvancedMobileDeviceSearch
@see Jamf::AdvancedUserSearch
@see Jamf::APIObject
Constants
- EXPORT_FORMATS
Class
Constants
- SITE_SUBSET
Where is site data located in the API JSON?
Attributes
@return [Array<String>] the fields to be returned with the search results
The API delivers these as an array of Hashes, where each hash has only one key, :name => the name of the fields/ExtAttrib to display. It should probably not have the underlying Hashes, and just be an array of names. This class converts it to just an Array
of field names (Strings) for internal use.
These fields are returned in the @search_results data along with :id, :name, and other unique identifiers for each found item. In that data, their names have colons removed, abd spaces and dashes converted to underscores, and they are symbolized. See attribute result_display_keys
@return [Array<Symbol>]
The search result Hash
keys for the {#display_fields} of the search
The field names in {#display_fields} are strings matching how the field is labeled in the web UI (including the names of Extension Attributes). They have to be that way when submitting them to the API, and thats mostly what {#display_fields} and related methods are for.
However, when those names come back as the Hash
Keys of the {#search_results} they (inconsistently) have spaces and/or dashes converted to underscores, and colons are removed. The JSON module then converts the keys to Symbols, so they don’t match the {#display_fields}.
For example, the display field “Last Check-in” might come back as any of these Symbols:
-
:“Last Check-in”
-
:Last_Check_in
-
:“Last_Check-in”
Also, the data returned in the {#search_results} contains more keys than just the {#display_fields} - namely it comes with some standard identifiers for each found item. such as JSS
id number and name.
{#result_display_keys} will hold just the Hash
keys corresponding to the {#display_fields} by taking the keys from the first result Hash
, and removing the identifier keys as listed in each subclass’s RESULT_ID_FIELDS constant.
@return [Array<Hash>] the results of the search
Each Hash
is one object that matches the criteria. Within each hash there are variable keys, but always at least the keys defined in each subclasses RESULT_ID_FIELDS
The other keys correspond to the {AdvancedSearch#display_fields} defined for this Advanced Search.
@return [String] the name of the site for this search
@return [String] the SQL query generated by the JSS
based on the critera
Public Class Methods
@see APIObject#initialize
Jamf::APIObject::new
# File lib/jamf/api/classic/base_classes/advanced_search.rb 146 def initialize(**args) 147 super **args 148 149 # @init_data now has the raw data 150 # so fill in our attributes or set defaults 151 152 @sql_text = @init_data[:sql_text] 153 @site = Jamf::APIObject.get_name(@init_data[:site]) 154 155 @display_fields = @init_data[:display_fields] ? @init_data[:display_fields].map { |f| f[:name] } : [] 156 157 @search_results = @init_data[self.class::RESULT_CLASS::RSRC_LIST_KEY] 158 @search_results ||= [] 159 @result_display_keys = if @search_results.empty? 160 [] 161 else 162 @search_results[0].keys - self.class::RESULT_ID_FIELDS 163 end 164 165 # make sure each hash of the search results 166 # has a key matching a standard key. 167 # 168 # @search_results.each do |hash| 169 # hash.keys.each do |key| 170 # std_key = key.to_s.gsub(':', '').gsub(/ |-/, '_').to_sym 171 # next if hash[std_key] 172 # hash[std_key] = hash[key] 173 # end 174 # end 175 end
Public Instance Methods
@return [Integer] the number of items found by the search
# File lib/jamf/api/classic/base_classes/advanced_search.rb 266 def count 267 @search_results.count 268 end
Create in the JSS
If get_results is true, they’ll be available in {#search_results}. This might be slow.
@param get_results should the results of the search be queried immediately?
@return [Integer] the id of the newly created search
Jamf::APIObject::create
# File lib/jamf/api/classic/base_classes/advanced_search.rb 188 def create(get_results = false) 189 raise Jamf::InvalidDataError, 'Jamf::Criteriable::Criteria instance required' unless @criteria.is_a? Jamf::Criteriable::Criteria 190 raise Jamf::InvalidDataError, 'display_fields must be an Array.' unless @display_fields.is_a? Array 191 192 orig_timeout = @cnx.timeout 193 @cnx.timeout = 1800 194 super() 195 requery_search_results if get_results 196 @cnx.timeout = orig_timeout 197 198 @id # remember to return the id 199 end
Set the list of fields to be retrieved with the search results.
@param new_val the new field names
# File lib/jamf/api/classic/base_classes/advanced_search.rb 257 def display_fields=(new_val) 258 raise Jamf::InvalidDataError, 'display_fields must be an Array.' unless new_val.is_a? Array 259 return if new_val.sort == @display_fields.sort 260 @display_fields = new_val 261 @need_to_update = true 262 end
Export the display fields of the search results to a file.
@param output_file The file in which to store the exported results
@param format one of :csv, :tab, or :xml, defaults to :csv
@param overwrite should the output_file be overwrite if it exists? Defaults to false
@return [Pathname] the path to the output file
@note This method only exports the display fields defined in this advanced search for the search_result members (computers, mobile_devices, or users) It doesn’t currently provide the ability to export subsets of info about those objects, as the Web UI does (e.g. group memberships, applications, receipts, etc)
# File lib/jamf/api/classic/base_classes/advanced_search.rb 285 def export(output_file, format = :csv, overwrite = false) 286 raise Jamf::InvalidDataError, "Export format must be one of: :#{EXPORT_FORMATS.join ', :'}" unless EXPORT_FORMATS.include? format 287 288 out = Pathname.new output_file 289 290 unless overwrite 291 raise Jamf::AlreadyExistsError, "The output file already exists: #{out}" if out.exist? 292 end 293 294 case format 295 when :csv 296 require 'csv' 297 CSV.open(out.to_s, 'wb') do |csv| 298 csv << @result_display_keys 299 @search_results.each do |row| 300 csv << @result_display_keys.map { |key| row[key] } 301 end # each do row 302 end # CSV.open 303 304 when :tab 305 tabbed = @result_display_keys.join("\t") + "\n" 306 @search_results.each do |row| 307 tabbed << @result_display_keys.map { |key| row[key] }.join("\t") + "\n" 308 end # each do row 309 out.jss_save tabbed.chomp 310 311 else # :xml 312 doc = REXML::Document.new '<?xml version="1.0" encoding="ISO-8859-1"?>' 313 members = doc.add_element self.class::RESULT_CLASS::RSRC_LIST_KEY.to_s 314 @search_results.each do |row| 315 member = members.add_element self.class::RESULT_CLASS::RSRC_OBJECT_KEY.to_s 316 @result_display_keys.each do |field| 317 member.add_element(field.to_s.tr(' ', '_')).text = row[field].empty? ? nil : row[field] 318 end # ech do field 319 end # each do row 320 out.jss_save doc.to_s.gsub('><', ">\n<") 321 end # case 322 323 out 324 end
Requery the API for the search results.
This can be very slow, so temporarily reset the API timeout to 30 minutes
@return [Array<Hash>] the new search results
# File lib/jamf/api/classic/base_classes/advanced_search.rb 237 def requery_search_results 238 orig_open_timeout = @cnx.open_timeout 239 orig_timeout = @cnx.timeout 240 @cnx.timeout = 1800 241 @cnx.open_timeout = 1800 242 begin 243 requery = self.class.fetch(id: @id) 244 @search_results = requery.search_results 245 @result_display_keys = requery.result_display_keys 246 ensure 247 @cnx.timeout = orig_timeout 248 @cnx.open_timeout = orig_open_timeout 249 end 250 end
Wrapper/alias for both create and update
# File lib/jamf/api/classic/base_classes/advanced_search.rb 220 def save(get_results = false) 221 if @in_jss 222 raise Jamf::UnsupportedError, 'Updating this object in the JSS is currently not supported by ruby-jss' unless updatable? 223 update get_results 224 else 225 raise Jamf::UnsupportedError, 'Creating this object in the JSS is currently not supported by ruby-jss' unless creatable? 226 create get_results 227 end 228 end
Save any changes
If get_results is true, they’ll be available in {#search_results}. This might be slow.
@param get_results should the results of the search be queried immediately?
@return [Integer] the id of the updated search
Jamf::APIObject#update
# File lib/jamf/api/classic/base_classes/advanced_search.rb 209 def update(get_results = false) 210 orig_timeout = @cnx.timeout 211 @cnx.timeout = 1800 212 super() 213 requery_search_results if get_results 214 @cnx.timeout = orig_timeout 215 216 @id # remember to return the id 217 end
Private Instance Methods
Clean up the inconsistent “Display Field” keys in the search results.
Sometimes spaces have been converted to underscores, sometimes not, sometimes both. Same for dashes. E.g :“Last Check-in”/:Last_Check_in/:“Last_Check-in”, :Computer_Name, and :“Display Name”/:Display_Name
This ensures there’s always the fully underscored version.
Update an internally used array of the display field names, symbolized, with spaces and dashes converted to underscores. We use these to overcome inconsistencies in how the names come from the API
@return [void]
def standardize_display_field_keys
spdash = us = @display_field_std_keys = @display_fields.map { |f| f.gsub(/ |-/, '_').to_sym }
end
# File lib/jamf/api/classic/base_classes/advanced_search.rb 350 def rest_xml 351 doc = REXML::Document.new Jamf::Connection::XML_HEADER 352 acs = doc.add_element self.class::RSRC_OBJECT_KEY.to_s 353 acs.add_element('name').text = @name 354 acs.add_element('sort_1').text = @sort_1 if @sort_1 355 acs.add_element('sort_2').text = @sort_2 if @sort_2 356 acs.add_element('sort_3').text = @sort_3 if @sort_3 357 358 acs << @criteria.rest_xml 359 360 df = acs.add_element('display_fields') 361 @display_fields.each { |f| df.add_element('display_field').add_element('name').text = f } 362 add_site_to_xml(doc) 363 doc.to_s 364 end