class Jamf::AdvancedSearch

A Parent class for Advanced Computer, MobileDevice, and User searchs

Subclasses must define:

@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

display_fields[R]

@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

result_display_keys[R]

@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.

search_results[R]

@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.

site[R]

@return [String] the name of the site for this search

sql_text[R]

@return [String] the SQL query generated by the JSS based on the critera

Public Class Methods

new(**args) click to toggle source

@see APIObject#initialize

Calls superclass method 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

count() click to toggle source

@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(get_results = false) click to toggle source

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

Calls superclass method 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
display_fields=(new_val) click to toggle source

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(output_file, format = :csv, overwrite = false) click to toggle source

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_search_results() click to toggle source

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
save(get_results = false) click to toggle source

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
update(get_results = false) click to toggle source

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

Calls superclass method 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

rest_xml() click to toggle source

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