Class: Jamf::ExtensionAttribute

Inherits:
APIObject show all
Includes:
Creatable, Updatable
Defined in:
lib/jamf/api/classic/base_classes/extension_attribute.rb

Overview

The parent class of ExtensionAttribute objects in the JSS.

The API extension attribute objects work with the definitions of extension attributes, not the resulting values stored in the JSS with the inventory reports.

This superclass, however, uses the AdvancedSearch subclasses to provide access to the reported values in two ways:

  • A list of target objects with a certain value for the ExtensionAttribute instance. See the #all_with_result method for details

  • A list of the most recent value for this ExtensionAttribute in all targets in the JSS

The ComputerExtensionAttribute subclass offers a ComputerExtensionAttribute#history method providing the history of values for the EA for one computer. This requires MySQL access to the JSS database since that history isn’t available via the API.

Subclasses of ExtensionAttribute must define these constants:

See Also:

Constant Summary collapse

DATA_TYPE_STRING =

What kinds of data can be created by EAs? Note, Dates must be in the format “YYYY-MM-DD hh:mm:ss”

'String'.freeze
DATA_TYPE_NUMBER =
'Number'.freeze
DATA_TYPE_INTEGER =
'Integer'.freeze
DATA_TYPE_DATE =
'Date'.freeze
DATA_TYPES =
[DATA_TYPE_STRING, DATA_TYPE_DATE, DATA_TYPE_INTEGER].freeze
DEFAULT_DATA_TYPE =
DATA_TYPE_STRING
NUMERIC_TYPES =

ExtensionAttributes refer to the numeric data type as “Integer” but the ext. attr values that come with extendable objects refer to that data type as “Number”. Here’s an array with both, so we can work with ether more easily.

[DATA_TYPE_NUMBER, DATA_TYPE_INTEGER].freeze
INPUT_TYPE_FIELD =

Where does the data come from?

'Text Field'.freeze
INPUT_TYPE_POPUP =
'Pop-up Menu'.freeze
INPUT_TYPE_SCRIPT =
'script'.freeze
INPUT_TYPE_LDAP =
'Directory Service Attribute Mapping'.freeze
INPUT_TYPES =
[
  INPUT_TYPE_FIELD,
  INPUT_TYPE_POPUP,
  INPUT_TYPE_SCRIPT,
  INPUT_TYPE_LDAP
].freeze
DEFAULT_INPUT_TYPE =
INPUT_TYPE_FIELD
WEB_DISPLAY_CHOICE_GENERAL =

Where can it be displayed in the WebApp?

'General'.freeze
WEB_DISPLAY_CHOICE_OS =
'Operating System'.freeze
WEB_DISPLAY_CHOICE_HW =
'Hardware'.freeze
WEB_DISPLAY_CHOICE_USER_LOC =
'User and Location'.freeze
WEB_DISPLAY_CHOICE_PURCHASING =
'Purchasing'.freeze
WEB_DISPLAY_CHOICE_EAS =
'Extension Attributes'.freeze
WEB_DISPLAY_CHOICES =
[
  WEB_DISPLAY_CHOICE_GENERAL,
  WEB_DISPLAY_CHOICE_OS,
  WEB_DISPLAY_CHOICE_HW,
  WEB_DISPLAY_CHOICE_USER_LOC,
  WEB_DISPLAY_CHOICE_PURCHASING,
  WEB_DISPLAY_CHOICE_EAS
].freeze
DEFAULT_WEB_DISPLAY_CHOICE =
WEB_DISPLAY_CHOICE_EAS
LAST_RECON_FIELD =
'Last Inventory Update'.freeze
LAST_RECON_FIELD_SYM =
LAST_RECON_FIELD.tr(' ', '_').to_sym
USERNAME_FIELD =
'Username'.freeze
USERNAME_FIELD_SYM =
USERNAME_FIELD.to_sym

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(**args) ⇒ ExtensionAttribute

Returns a new instance of ExtensionAttribute.



134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/jamf/api/classic/base_classes/extension_attribute.rb', line 134

def initialize(**args)
  super

  # @init_data now has the raw data
  # so fill in our attributes or set defaults
  @description = @init_data[:description]
  @data_type = @init_data[:data_type] || DEFAULT_DATA_TYPE

  if @init_data[:input_type]
    @input_type = @init_data[:input_type][:type]

    @script = @init_data[:input_type][:script]

    @attribute_mapping = @init_data[:input_type][:attribute_mapping]
    @popup_choices = @init_data[:input_type][:popup_choices]
    # popups can always contain blank
    @popup_choices << Jamf::BLANK if @popup_choices

    # These two are deprecated - windows won't be supported for long
    @platform = @init_data[:input_type][:platform]
    @scripting_language = @init_data[:input_type][:scripting_language]
  end
  @input_type ||= DEFAULT_INPUT_TYPE

  @enabled = @init_data[:enabled]

  @web_display = @init_data[:inventory_display] || DEFAULT_WEB_DISPLAY_CHOICE
  # deprecated - no longer in the UI
  @recon_display = @init_data[:recon_display] || @web_display

  # When used in Advanced Search results, the EA name
  # has colons removed, spaces & dashes turned to underscores.
  # and then ruby-jss symbolizes the name.
  @symbolized_name = @name.gsub(':', '').gsub(/-| /, '_').to_sym
end

Instance Attribute Details

#attribute_mappingString



124
125
126
# File 'lib/jamf/api/classic/base_classes/extension_attribute.rb', line 124

def attribute_mapping
  @attribute_mapping
end

#data_typeString



115
116
117
# File 'lib/jamf/api/classic/base_classes/extension_attribute.rb', line 115

def data_type
  @data_type
end

#descriptionString Also known as: desc



111
112
113
# File 'lib/jamf/api/classic/base_classes/extension_attribute.rb', line 111

def description
  @description
end

#input_typeString



118
119
120
# File 'lib/jamf/api/classic/base_classes/extension_attribute.rb', line 118

def input_type
  @input_type
end

#need_to_updateBoolean (readonly) Originally defined in module Updatable



121
122
123
# File 'lib/jamf/api/classic/base_classes/extension_attribute.rb', line 121

def popup_choices
  @popup_choices
end

#web_displayString



127
128
129
# File 'lib/jamf/api/classic/base_classes/extension_attribute.rb', line 127

def web_display
  @web_display
end

Instance Method Details

#all_with_result(search_type, desired_value) ⇒ Array<Hash{:id=>Integer,:name=>String,:value=>String,Integer,Time}>

Get an Array of Hashes for all inventory objects with a desired result in their latest report for this EA.

Each Hash is one inventory object (computer, mobile device, user), with these keys:

:id - the computer id
:name - the computer name
:value - the matching ext attr value for the objects latest report.

This is done by creating a temprary AdvancedSearch for objects with matching values in the EA field, then getting the #search_results hash from it.

The AdvancedSearch is then deleted.

must be a member of Jamf::Criterion::SEARCH_TYPES



371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
# File 'lib/jamf/api/classic/base_classes/extension_attribute.rb', line 371

def all_with_result(search_type, desired_value)
  raise Jamf::NoSuchItemError, "EA Not In JSS! Use #create to create this #{self.class::RSRC_OBJECT_KEY}." unless @in_jss
  unless Jamf::Criteriable::Criterion::SEARCH_TYPES.include? search_type.to_s
    raise Jamf::InvalidDataError, 'Invalid search_type, see Jamf::Criteriable::Criterion::SEARCH_TYPES'
  end

  begin
    search_class = self.class::TARGET_CLASS::SEARCH_CLASS
    acs = search_class.make cnx: @cnx, name: "ruby-jss-EA-result-search-#{Time.now.to_jss_epoch}"
    acs.display_fields = [@name]
    crit_list = [Jamf::Criteriable::Criterion.new(and_or: 'and', name: @name, search_type: search_type.to_s, value: desired_value)]
    acs.criteria = Jamf::Criteriable::Criteria.new crit_list

    acs.create :get_results

    results = []

    acs.search_results.each do |i|
      value =
        case @data_type
        when 'Date' then Jamf.parse_time i[@symbolized_name]
        when 'Integer' then i[@symbolized_name].to_i
        else i[@symbolized_name]
        end # case
      results << { id: i[:id], name: i[:name], value: value }
    end # each
  ensure
    acs.delete if acs.is_a? self.class::TARGET_CLASS::SEARCH_CLASS
  end
  results
end

#clone(new_name, api: nil, cnx: nil) ⇒ APIObject Originally defined in module Creatable

make a clone of this API object, with a new name. The class must be creatable

#createObject

See Also:

  • Creatable#create


175
176
177
178
# File 'lib/jamf/api/classic/base_classes/extension_attribute.rb', line 175

def create
  validate_for_save
  super
end

#deleteObject

See Also:



191
192
193
194
195
196
197
198
199
200
201
202
203
# File 'lib/jamf/api/classic/base_classes/extension_attribute.rb', line 191

def delete
  orig_open_timeout = @cnx.open_timeout
  orig_timeout = @cnx.timeout
  @cnx.timeout = orig_timeout + 1800
  @cnx.open_timeout = orig_open_timeout + 1800
  begin
    super
    @cnx.flushcache self.class
  ensure
    @cnx.timeout = orig_timeout
    @cnx.open_timeout = orig_open_timeout
  end
end

#from_ldap?Boolean



213
214
215
# File 'lib/jamf/api/classic/base_classes/extension_attribute.rb', line 213

def from_ldap?
  @input_type == INPUT_TYPE_LDAP
end

#from_popup_menu?Boolean



209
210
211
# File 'lib/jamf/api/classic/base_classes/extension_attribute.rb', line 209

def from_popup_menu?
  @input_type == INPUT_TYPE_POPUP
end

#from_script?Boolean



217
218
219
# File 'lib/jamf/api/classic/base_classes/extension_attribute.rb', line 217

def from_script?
  @input_type == INPUT_TYPE_SCRIPT
end

#from_text_field?Boolean



205
206
207
# File 'lib/jamf/api/classic/base_classes/extension_attribute.rb', line 205

def from_text_field?
  @input_type == INPUT_TYPE_FIELD
end

#latest_valuesArray<Hash{:id=>Integer,:name=>String,:value=>String,Integer,Time,:as_of=>Time}>

for this EA on all inventory objects in the JSS.

Each Hash is one inventory object (computer, mobile device, user), with these keys:

:id - the jss id
:name - the object (computer, user, mobiledevice) name
:value - the most recent ext attr value for the object.
:as_of - the timestamp of when the value was collected (nil for User EAs, or for devices that have never collected inventory)
:username - the username associated with the object

This is done by creating a temporary AdvancedSearch for all objects, with the EA as a display field. The #search_result then contains the desired data.

The AdvancedSearch is then deleted.



428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
# File 'lib/jamf/api/classic/base_classes/extension_attribute.rb', line 428

def latest_values
  raise Jamf::NoSuchItemError, "EA Not In JSS! Use #create to create this #{self.class::RSRC_OBJECT_KEY}." unless @in_jss

  tmp_advsrch = "ruby-jss-EA-latest-search-#{Time.now.to_jss_epoch}"

  begin
    search_class = self.class::TARGET_CLASS::SEARCH_CLASS
    acs = search_class.make name: tmp_advsrch, cnx: @cnx
    acs.display_fields = self.class::TARGET_CLASS == Jamf::User ? [@name, USERNAME_FIELD] : [@name, USERNAME_FIELD, LAST_RECON_FIELD]

    # search for 'Username like "" ' because all searchable object classes have a "Username" value
    crit = Jamf::Criteriable::Criterion.new(and_or: 'and', name: 'Username', search_type: 'like', value: '')
    # crit = self.class::ALL_TARGETS_CRITERION
    acs.criteria = Jamf::Criteriable::Criteria.new [crit]
    acs.create :get_results

    results = []

    acs.search_results.each do |i|
      value =
        case @data_type
        when 'Date' then Jamf.parse_time i[@symbolized_name]
        when 'Integer' then i[@symbolized_name].to_i
        else i[@symbolized_name]
        end # case

      as_of = Jamf.parse_time(i[LAST_RECON_FIELD_SYM])

      results << { id: i[:id], name: i[:name], username: i[USERNAME_FIELD_SYM], value: value, as_of: as_of }
    end # acs.search_results.each
  ensure
    if defined? acs
      acs.delete if acs
    elsif search_class.all_names(:refresh, cnx: @cnx).include? tmp_advsrch
      search_class.fetch(name: tmp_advsrch, cnx: @cnx).delete
    end
  end

  results
end

#name=(newname) ⇒ void Originally defined in module Updatable

This method returns an undefined value.

Change the name of this item Remember to #update to push changes to the server.

#updateObject

See Also:

  • Updatable#update


182
183
184
185
186
187
# File 'lib/jamf/api/classic/base_classes/extension_attribute.rb', line 182

def update
  validate_for_save
  super
  # this flushes the cached EA itself, used for validating ea values.
  @cnx.flushcache self.class
end