class Jamf::APIObject
This class is the parent to all JSS
API objects. It provides standard methods and constants that apply to all API resouces.
See the README.md file for general info about using subclasses of Jamf::APIObject
Subclassing¶ ↑
Initilize / Constructor¶ ↑
All subclasses must call ‘super` in their initialize method, which will call the method defined here in APIObject
. Not only does this retrieve the data from the API, it parses the raw JSON data into a Hash
, & stores it in @init_data.
In general, subclasses should do any class-specific argument checking before calling super, and then afterwards use the contents of @init_data to populate any class-specific attributes. Populating @id, @name, @rest_rsrc, and @in_jss are handled here.
This class also handles parsing @init_data for any mixed-in modules, e.g. Scopable
, Categorizable
or Extendable
. See those modules for any requirements they have when including them.
Object
Creation¶ ↑
If a subclass should be able to be created in the JSS
be sure to include {Jamf::Creatable}
The constructor should verify any extra required data in the args
See {Jamf::Creatable} for more details.
Object
Modification¶ ↑
If a subclass should be modifiable in the JSS
, include {Jamf::Updatable}, q.v. for details.
Object
Deletion¶ ↑
All subclasses can be deleted in the JSS
.
Required Constants
¶ ↑
Subclasses must provide certain constants in order to correctly interpret API data and communicate with the API:
RSRC_BASE [String]¶ ↑
The base for REST resources of this class
e.g. ‘computergroups’ in
https://casper.mycompany.com:8443/JSSResource/computergroups/id/12
RSRC_LIST_KEY [Symbol]¶ ↑
When GETting the RSRC_BASE for a subclass, an Array
of Hashes is returned with one Hash
of basic info for each object of that type in the JSS
. All objects have their JSS
id and name in that Hash
, some have other data as well. This Array
is used for a variety of purposes when using ruby-jss, since it gives you basic info about all objects, without having to fetch each one individually.
Here’s the top of the output from the ‘computergroups’ RSRC_BASE:
{:computer_groups=> [{:id=>1020, :name=>"config-no-turnstile", :is_smart=>true}, {:id=>1357, :name=>"10.8 Laptops", :is_smart=>true}, {:id=>1094, :name=>"wifi_cert-expired", :is_smart=>true}, {:id=>1144, :name=>"mytestgroup", :is_smart=>false}, ...
Notice that the Array
we want is embedded in a one-item Hash
, and the key in that Hash
for the desired Array
is the Symbol :computer_groups.
That symbol is the value needed in the RSRC_LIST_KEY constant.
The ‘.all_ids’, ‘.all_names’ and other ‘.all_*’ class methods use the list-resource Array
to extract other Arrays of the desired values - which can be used to check for existance without retrieving an entire object, among other uses.
RSRC_OBJECT_KEY [Symbol]¶ ↑
The one-item Hash
key used for individual JSON object output. It’s also used in various error messages
As with the list-resource output mentioned above, when GETting a specific object resource, there’s an extra layer of encapsulation in a one-item Hash
. Here’s the top of the JSON for a single computer group fetched from ‘…computergroups/id/1043’
{:computer_group=> {:id=>1043, :name=>"tmp-no-d3", :is_smart=>false, :site=>{:id=>-1, :name=>"None"}, :criteria=>[], :computers=>[ ...
The data for the group itself is the inner Hash
.
The RSRC_OBJECT_KEY in this case is set to :computer_group - the key in the top-level, one-item Hash
that we need to get the real Hash
about the object.
Optional Constants
¶ ↑
OTHER_LOOKUP_KEYS¶ ↑
Fetching individual objects from the API is usuallly done via the object’s unique JSS
id, via a resrouce URL like so:
...JSSResource/<RSRC_BASE>/id/<idnumber>
Most objects can also be looked-up by name, because the API also has and endpoint ..JSSResource/<RSRC_BASE>/name/<name> (See {NON_UNIQUE_NAMES} below)
Some objects, like Computers and MobileDevices, have other values that serve as unique identifiers and can also be used as ‘lookup keys’ for fetching individual objects. When this is the case, those values always appear in the objects list-resource data (See {RSRC_LIST_KEY} above).
For example, here’s a summary-hash for a single MobileDevice
from the list-resource ‘…JSSResource/mobiledevices’, which you might get in the Array
returned by Jamf::MobileDevice.all
:
{ :id=>3964, :name=>"Bear", :device_name=>"Bear", :udid=>"XXX", :serial_number=>"YYY2244MM60", :phone_number=>"510-555-1212", :wifi_mac_address=>"00:00:00:00:00:00", :managed=>true, :supervised=>false, :model=>"iPad Pro (9.7-inch Cellular)", :model_identifier=>"iPad6,4", :modelDisplay=>"iPad Pro (9.7-inch Cellular)", :model_display=>"iPad Pro (9.7-inch Cellular)", :username=>"fred" }
For MobileDevices, serial_number, udid, and wifi_mac_address are also all unique identifiers for an individual device, and can be used to fetch them.
To specify other identifiers for an APIObject
subclass, create the constant OTHER_LOOKUP_KEYS containing a Hash
of Hashes, like so:
OTHER_LOOKUP_KEYS = { serial_number: { aliases: [:serialnumber, :sn], fetch_rsrc_key: :serialnumber }, udid: { fetch_rsrc_key: :udid }, wifi_mac_address: { aliases: [:macaddress, :macaddr], fetch_rsrc_key: :macaddress } }.freeze
The keys in OTHER_LOOKUP_KEYS are the keys in a summary-hash data from .all that hold a unique identifier. Each value is a Hash
with one or two keys:
- aliases: [Array<Symbol>] Aliases for that identifier, i.e. abbreviations or spelling variants. These aliases can be used in fetching, and they also have matching `.all_<aliase>s` methods. If no aliases are needed, don't specify anything, as with the udid: in the example above - fetch_rsrc_key: [Symbol] Often a unique identifier can be used to build a URL for fetching (or updating or deleteing) an object with that value, rather than with id. For example, while the MobileDevice in the example data above would normally be fetched at the resource 'JSSResource/mobiledevices/id/3964' it can also be fetched at 'JSSResource/mobiledevices/serialnumber/YYY2244MM60'. Since the URL is built using 'serialnumber', the symbol :serialnumber is used as the fetch_rsrc_key. Setting a fetch_rsrc_key: for one of the OTHER_LOOKUP_KEYS tells ruby-jss that such a URL is available, and fetching by that lookup key will be faster when using that URL. If a fetch_rsrc_key is not set, fetching will be slower, since the fetch method must first refresh the list of all available objects to find the id to use for building the resource URL. This is also true when fetching without specifying which lookup key to use, e.g. `.fetch 'foo'` vs. `.fetch sn: 'foo'`
The OTHER_LOOKUP_KEYS, if defined, are merged with the DEFAULT_LOOKUP_KEYS
defined below via the {APIObject.lookup_keys} class method, They are used for:
-
creating list-methods: For each lookup key, a class method ‘.all_<key>s` is created automatically, e.g. `.all_serial_numbers`. The aliases are used to make alises of those methods, e.g. `.all_sns`
-
finding valid ids: The {APIObject.valid_id} class method looks at the known lookup keys to find an object’s id.
-
fetching: When an indentifier is given to ‘.fetch`, the
fetch_rsrc_key
is used to build the resource URL for fetching the object. If there is nofetch_rsrc_key
, thelookup_keys
and aliases are used to find the matching id, which is used to build the URL.When no identifier is specified, .fetch uses .valid_id, described above.
NON_UNIQUE_NAMES¶ ↑
Some JSS
objects, like Computers and MobileDevices, do not treat names as unique in the JSS
, but they can still be used for fetching objects. The API itself will return data for a non-unique name lookup, but there’s no way to guarantee which object you get back.
In those subclasses, set NON_UNIQUE_NAMES to any value, and a Jamf::AmbiguousError
exception will be raised when trying to fetch by name and the name isn’t unique.
Because of the extra processing, the check for this state will only happen when NON_UNIQUE_NAMES is set. If not set at all, the check doesn’t happen and if multiple objects have the same name, which one is returned is undefined.
When that’s the case, fetching explicitly by name, or when fetching with a plain search term that matches a non-unique name, will raise a Jamf::AmbiguousError
exception,when the name isn’t unique. If that happens, you’ll have to use some other identifier to fetch the desired object.
Note: Fetching, finding valid id, and name collisions are case-insensitive.
Constants
- API_SOURCE
which API do APIObjects come from? The JPAPI equivalent is in
Jamf::JPAPIResource
- DEFAULT_LOOKUP_KEYS
See the discussion of ‘Lookup Keys’ in the comments/docs for {Jamf::APIObject}
- OBJECT_HISTORY_TABLE
This table holds the object history for
JSS
objects.Object
history is not available via the API, only MySQL.- OK_INSTANTIATORS
‘.new’ can only be called from these methods:
Attributes
@return [Jamf::Connection] the API connection thru which we deal with
this object.
@return [Jamf::Connection] the API connection thru which we deal with
this object.
@return [Integer] the JSS
id number
@return [Boolean] is it in the JSS
?
@return [Boolean] is it in the JSS
?
@return the parsed JSON data retrieved from the API when this object was
fetched
@return [String] the name
@return [String] the Rest resource for API access (the part after “JSSResource/” )
Public Class Methods
Return an Array
of Hashes for all objects of this subclass in the JSS
.
This method is only valid in subclasses of Jamf::APIObject
, and is the parsed JSON output of an API query for the resource defined in the subclass’s RSRC_BASE
e.g. for Jamf::Computer
, with the RSRC_BASE of :computers, This method retuens the output of the ‘JSSResource/computers’ resource, which is a list of all computers in the JSS
.
Each item in the Array
is a Hash
with at least two keys, :id and :name. The class methods .all_ids and .all_names provide easier access to those dataas mapped Arrays.
Some API classes provide other keys in each Hash
, e.g. :udid (for computers and mobile devices) or :is_smart (for groups).
For those keys that are listed in a subclass’s lookup_keys
method, there are matching methods ‘.all_(key)s` which return an array just of those values, from the values of this hash. For example, `.all_udids` will use the .all array to return an array of just udids, if the subclass defines :udid in its OTHER_LOOKUP_KEYS (See ’Lookup Keys’ in the class comments/docs above)
Subclasses should provide appropriate .all_xxx class methods for accessing any other other values as Arrays, e.g. Jamf::Computer.all_managed
– Caching
The results of the first call to .all for each subclass is cached in the .c_object_list_cache of the given {Jamf::Connection} and that cache is used for all future calls, so as to not requery the server every time.
To force requerying to get updated data, provided a truthy argument. I usually use :refresh, so that it’s obvious what I’m doing, but true, 1, or anything besides false or nil will work.
The various methods that use the output of this method also take the refresh parameter which will be passed here as needed.
– Alternate API connections
To query an APIConnection
other than the currently active one, provide one via the cnx: named parameter.
@param refresh should the data be re-queried from the API?
@param cnx [Jamf::Connection] an API connection to use for the query.
Defaults to the corrently active API. See {Jamf::Connection}
@return [Array<Hash{:name=>String, :id=> Integer}>]
# File lib/jamf/api/classic/base_classes/api_object.rb 510 def self.all(refresh = false, api: nil, cnx: Jamf.cnx) 511 cnx = api if api 512 513 validate_not_metaclass(self) 514 515 cache = cnx.c_object_list_cache 516 cache_key = self::RSRC_LIST_KEY 517 cnx.flushcache(cache_key) if refresh 518 return cache[cache_key] if cache[cache_key] 519 520 cache[cache_key] = cnx.c_get(self::RSRC_BASE)[cache_key] 521 end
Return an Array
of Jamf::APIObject
subclass instances e.g when called on Jamf::Package
, return a hash of Jamf::Package
instancesa for every package in the JSS
.
WARNING: This may be slow as it has to look up each object individually! use it wisely.
@param refresh should the data re-queried from the API?
@param cnx [Jamf::Connection] an API connection to use for the query.
Defaults to the corrently active API. See {Jamf::Connection}
@return [Array<APIObject>] the objects requested
# File lib/jamf/api/classic/base_classes/api_object.rb 650 def self.all_objects(refresh = false, api: nil, cnx: Jamf.cnx) 651 cnx = api if api 652 653 objects_cache_key ||= "#{self::RSRC_LIST_KEY}_objects".to_sym 654 api_cache = cnx.c_object_list_cache 655 api_cache[objects_cache_key] = nil if refresh 656 657 return api_cache[objects_cache_key] if api_cache[objects_cache_key] 658 659 all_result = all(refresh, cnx: cnx) 660 api_cache[objects_cache_key] = all_result.map do |o| 661 fetch id: o[:id], cnx: cnx, refresh: false 662 end 663 end
Make a ruby instance of a not-yet-existing APIObject
.
This is how to create new objects in the JSS
. A name: must be provided, and different subclasses can take other named parameters.
For retrieving existing objects in the JSS
, use {APIObject.fetch}
After calling this you’ll have a local instance, which will be created in the JSS
when you call create
on it. see {APIObject#create}
@param name The name of this object, generally must be uniqie
@param cnx [Jamf::Connection] the connection thru which to make this
object. Defaults to the deault API connection in Jamf.cnx
@param args [Hash] The data for creating an object, such as name:
See {APIObject#initialize}
@return [APIObject] The un-created ruby-instance of a JSS
object
# File lib/jamf/api/classic/base_classes/api_object.rb 1139 def self.create(**args) 1140 validate_not_metaclass(self) 1141 unless constants.include?(:CREATABLE) 1142 raise Jamf::UnsupportedError, "Creating #{self::RSRC_LIST_KEY} isn't yet supported. Please use other Casper workflows." 1143 end 1144 raise ArgumentError, "Use '#{self.class}.fetch id: xx' to retrieve existing JSS objects" if args[:id] 1145 1146 args[:cnx] ||= args[:api] # deprecated 1147 args[:cnx] ||= Jamf.cnx 1148 args[:id] = :new 1149 new(**args) 1150 end
Loop through the defined lookup keys and make .all_<key>s methods for each one, with alises as needed.
This is called automatically when subclasses are loaded by zeitwerk
# File lib/jamf/api/classic/base_classes/api_object.rb 297 def self.define_identifier_list_methods 298 Jamf.load_msg "Defining list-methods for APIObject subclass #{self}" 299 300 lookup_keys.each do |als, key| 301 meth_name = key.to_s.end_with?('s') ? "all_#{key}es" : "all_#{key}s" 302 if als == key 303 # the all_ method - skip if defined in the class 304 next if singleton_methods.include? meth_name.to_sym 305 306 define_singleton_method meth_name do |refresh = false, cached_list: nil, api: nil, cnx: Jamf.cnx| 307 cnx = api if api 308 list = cached_list || all(refresh, cnx: cnx) 309 list.map { |i| i[key] } 310 end 311 Jamf.load_msg "Defined method #{self}##{meth_name}" 312 313 else 314 # an alias - skip if defined in the class 315 als_name = als.to_s.end_with?('s') ? "all_#{als}es" : "all_#{als}s" 316 317 next if singleton_methods.include? als_name.to_sym 318 319 define_singleton_method als_name do |refresh = false, api: nil, cnx: Jamf.cnx| 320 cnx = api if api 321 send meth_name, refresh, cnx: cnx 322 end 323 Jamf.load_msg "Defined alias '#{als_name}' of #{self}##{meth_name}" 324 325 end # if 326 end # lookup_keys.each 327 328 true 329 end
Delete one or more API objects by jss_id without instantiating them. Non-existent id’s are skipped and an array of skipped ids is returned.
If an Array
is provided, it is passed through uniq! before being processed.
@param victims An object id or an array of them
to be deleted
@param cnx [Jamf::Connection] the API connection to use.
Defaults to the corrently active API. See {Jamf::Connection}
@return [Array<Integer>] The id’s that didn’t exist when we tried to
delete them.
# File lib/jamf/api/classic/base_classes/api_object.rb 1183 def self.delete(victims, refresh = true, api: nil, cnx: Jamf.cnx) 1184 cnx = api if api 1185 1186 validate_not_metaclass(self) 1187 1188 raise Jamf::InvalidDataError, 'Parameter must be an Integer ID or an Array of them' unless victims.is_a?(Integer) || victims.is_a?(Array) 1189 1190 case victims 1191 when Integer 1192 victims = [victims] 1193 when Array 1194 victims.uniq! 1195 end 1196 1197 skipped = [] 1198 current_ids = all_ids refresh, cnx: cnx 1199 victims.each do |vid| 1200 if current_ids.include? vid 1201 cnx.c_delete "#{self::RSRC_BASE}/id/#{vid}" 1202 else 1203 skipped << vid 1204 end # if current_ids include vid 1205 end # each victim 1206 1207 # clear any cached all-lists or id-maps for this class 1208 # so they'll re-cache as needed 1209 cnx.flushcache self::RSRC_LIST_KEY 1210 # all :refresh, cnx: cnx 1211 1212 skipped 1213 end
@return [Hash {String => Integer}] name => number of occurances
# File lib/jamf/api/classic/base_classes/api_object.rb 525 def self.duplicate_names(refresh = false, api: nil, cnx: Jamf.cnx) 526 cnx = api if api 527 528 return {} unless defined? self::NON_UNIQUE_NAMES 529 530 dups = {} 531 all(refresh, cnx: cnx).each do |obj| 532 if dups[obj[:name]] 533 dups[obj[:name]] += 1 534 else 535 dups[obj[:name]] = 1 536 end # if 537 end # all(refresh, cnx: cnx).each 538 dups.delete_if { |_k, v| v == 1 } 539 dups 540 end
Return true or false if an object of this subclass with the given Identifier exists on the server
@param identfier [String,Integer] An identifier for an object, a value for one of the available lookup_keys
@param refresh [Boolean] Should the data be re-read from the server
@param cnx [Jamf::Connection] an API connection to use for the query.
Defaults to the corrently active API. See {Jamf::Connection}
@return [Boolean] does an object with the given identifier exist?
# File lib/jamf/api/classic/base_classes/api_object.rb 831 def self.exist?(identifier, refresh = false, api: nil, cnx: Jamf.cnx) 832 cnx = api if api 833 834 !valid_id(identifier, refresh, cnx: cnx).nil? 835 end
Retrieve an object from the API and return an instance of this APIObject
subclass.
@example
# computer where 'xyxyxyxy' is in any of the lookup key fields Jamf::Computer.fetch 'xyxyxyxy' # computer where 'xyxyxyxy' is the serial number Jamf::Computer.fetch serial_number: 'xyxyxyxy'
Fetching is faster when specifying a lookup key, and that key has a fetch_rsrc_key
defined in its OTHER_LOOKUP_KEYS constant, as in the second example above.
When no lookup key is given, as in the first example above, or when that key doesn’t have a defined fetch_rsrc_key
, ruby-jss uses the currently cached list resource data to find the id matching the value given, and that id is used to fetch the object. (see ‘List Resources and Lookup Keys’ in the APIObject
comments/docs above)
Since that cached list data may be out of date, you can provide the param ‘refrsh: true`, to reload the list from the server. This will cause the fetch to be slower still, so use with caution.
For creating new objects in the JSS
, use {APIObject.make}
@param searchterm[String, Integer] An single value to
search for in all the lookup keys for this clsss. This is slower than specifying a lookup key
@param args the remaining options for fetching an object.
If no searchterm is provided, one of the args must be a valid lookup key and value to find in that key, e.g. `serial_number: '1234567'`
@option args cnx an API connection to use for the query.
Defaults to the corrently active API. See {Jamf::Connection}
@option args refresh should the summary list of all objects be
reloaded from the API before being used to look for this object.
@return [APIObject] The ruby-instance of a JSS
object
# File lib/jamf/api/classic/base_classes/api_object.rb 961 def self.fetch(searchterm = nil, **args) 962 validate_not_metaclass(self) 963 964 # which connection? 965 cnx = args.delete :cnx 966 cnx ||= args.delete :api # backward compatibility, deprecated 967 cnx ||= Jamf.cnx 968 969 # refresh the .all list if needed 970 if args.delete(:refresh) || searchterm == :random 971 all(:refresh, cnx: cnx) 972 just_refreshed = true 973 else 974 just_refreshed = false 975 end 976 977 # a random object? 978 if searchterm == :random || args[:random] 979 rnd_thing = all(cnx: cnx).sample 980 raise Jamf::NoSuchItemError, "No #{self::RSRC_LIST_KEY} found" unless rnd_thing 981 982 return new id: rnd_thing[:id], cnx: cnx 983 end 984 985 # get the lookup key and value, if given 986 fetch_key, fetch_val = args.to_a.first 987 fetch_rsrc_key = fetch_rsrc_key(fetch_key) 988 989 # names should raise an error if more than one exists, 990 # so we always have to do id_for_identifier, which will do so. 991 if fetch_rsrc_key == :name 992 id = id_for_identifier fetch_key, fetch_val, !just_refreshed, cnx: cnx 993 fetch_rsrc = id ? "#{self::RSRC_BASE}/name/#{CGI.escape fetch_val.to_s}" : nil 994 995 # if the fetch rsrc key exists, it can be used directly in an endpoint path 996 # so, use it directly, rather than looking up the id first. 997 elsif fetch_rsrc_key 998 fetch_rsrc = "#{self::RSRC_BASE}/#{fetch_rsrc_key}/#{CGI.escape fetch_val.to_s}" 999 1000 # it has an OTHER_LOOKUP_KEY but that key doesn't have a fetch_rsrc 1001 # so we look in the .map_all_ids_to_* hash for it. 1002 elsif fetch_key 1003 id = id_for_identifier fetch_key, fetch_val, !just_refreshed, cnx: cnx 1004 fetch_rsrc = id ? "#{self::RSRC_BASE}/id/#{id}" : nil 1005 1006 # no fetch key was given in the args, so try a search term 1007 elsif searchterm 1008 id = valid_id searchterm, cnx: cnx 1009 fetch_rsrc = id ? "#{self::RSRC_BASE}/id/#{id}" : nil 1010 1011 else 1012 raise ArgumentError, 'Missing searchterm or fetch key' 1013 end 1014 1015 new fetch_rsrc: fetch_rsrc, cnx: cnx 1016 end
Given a lookup key, or an alias of one, return the matching fetch_rsrc_key
for building a fetch/GET resource URL, or nil if no fetch_rsrc_key
is defined.
See {OTHER_LOOKUP_KEYS} in the APIObject
class comments/docs above for details.
@param lookup_key [Symbol] A lookup key, or an aliases of one, for this
subclass.
@return [Symbol, nil] the fetch_rsrc_key
for that lookup key.
# File lib/jamf/api/classic/base_classes/api_object.rb 418 def self.fetch_rsrc_key(lookup_key) 419 parse_lookup_keys unless @fetch_rsrc_keys 420 @fetch_rsrc_keys[lookup_key] 421 end
Some API objects contain references to other API objects. Usually those references are a Hash
containing the :id and :name of the target. Sometimes, however the reference is just the name of the target.
A Script
has a property :category, which comes from the API as a String
, the name of the category for that script. e.g. “GoodStuff”
A Policy
also has a property :category, but it comes from the API as a Hash
with both the name and id, e.g. !{:id => 8, :name => “GoodStuff”}
When that reference is to a single thing (like the category to which something belongs) APIObject
subclasses usually store only the name, and use the name when returning data to the API.
When an object references a list of related objects (like the computers assigned to a user) that list will be and Array
of Hashes as above, with both the :id and :name
This method is just a handy way to extract the name regardless of how it comes from the API. Most APIObject
subclasses use it in their initialize method
@param a_thing the api data from which we’re extracting the name
@return [String] the name extracted from a_thing
# File lib/jamf/api/classic/base_classes/api_object.rb 908 def self.get_name(a_thing) 909 case a_thing 910 when String 911 a_thing 912 when Hash 913 a_thing[:name] 914 when nil 915 nil 916 end 917 end
Fetch the mostly- or fully-raw JSON or XML data for an object of this subclass.
By default, returns the JSON data parsed into a Hash
.
When format: is anything but :json, returns the XML data parsed into a REXML::Document
When as_string: is truthy, returns an unparsed JSON String
(or XML String
if format: is not :json) as it comes directly from the API.
When fetching raw JSON, the returned Hash
will have its keys symbolized.
This can be substantialy faster than instantiating, especially when you don’t need all the ruby goodness of a full instance, but just want a few values for an object that aren’t available in the ‘all` data
This is really just a wrapper around {APIConnection.c_get} that automatically fills in the RSRC::BASE value for you.
@param id [Integer] the id of the object to fetch
@param format :json or :xml, defaults to :json
@param as_string return the raw JSON or XML string as it comes
from the API, do not parse into a Hash or REXML::Document
@param cnx [Jamf::Connection] the connection thru which to fetch this
object. Defaults to the deault API connection in Jamf.cnx
@return [Hash, REXML::Document, String] the raw data for the object
# File lib/jamf/api/classic/base_classes/api_object.rb 1050 def self.get_raw(id, format: :json, as_string: false, api: nil, cnx: Jamf.cnx) 1051 cnx = api if api 1052 1053 validate_not_metaclass(self) 1054 rsrc = "#{self::RSRC_BASE}/id/#{id}" 1055 data = cnx.c_get rsrc, format, raw_json: as_string 1056 return data if format == :json || as_string 1057 1058 REXML::Document.new(**data) 1059 end
Return the id of the object of this subclass with the given lookup key == a given identifier.
Return nil if no object has that value in that key
@example
# get the id for the computer with serialnum 'xyxyxyxy' Jamf::Computer.id_for_identifier :serial_number, 'xyxyxyxy' # => the Integer id, or nil if no such serial number
Raises a Jamf::Ambiguous error if there’s more than one matching value for any key, which might be true of names for Computers and Devices
This is similar to .valid_id, except only one key is searched
@param key [Symbol] they key in which to look for the identifier. Must be
a valid lookup key for this subclass.
@param identfier [String,Integer] An identifier for an object, a value for
one of the available lookup_keys
@param refresh [Boolean] Should the cached summary data be re-read from
the server first?
@param cnx [Jamf::Connection] an API connection to use for the query.
Defaults to the corrently active API. See {Jamf::Connection}
@return [Integer, nil] the id of the matching object, or nil if it
doesn't exist
# File lib/jamf/api/classic/base_classes/api_object.rb 797 def self.id_for_identifier(key, val, refresh = false, api: nil, cnx: Jamf.cnx) 798 cnx = api if api 799 800 # refresh if needed 801 all(refresh, cnx: cnx) if refresh 802 803 # get the real key if an alias was used 804 key = real_lookup_key key 805 806 # do id's expicitly, they are integers 807 return all_ids(cnx: cnx).include?(val) ? val : nil if key == :id 808 809 mapped_ids = map_all_ids_to key, cnx: cnx 810 matches = mapped_ids.select { |_id, map_val| val.casecmp? map_val } 811 raise Jamf::AmbiguousError, "Key #{key}: value '#{val}' is not unique for #{self}" if matches.size > 1 812 813 return nil if matches.size.zero? 814 815 matches.keys.first 816 end
What are all the lookup keys available for this class, with all their aliases (or optionally not) or with their fetch_rsrc_keys
This method combines the DEFAULT_LOOOKUP_KEYS defined above, with the optional OTHER_LOOKUP_KEYS from a subclass (See ‘Lookup Keys’ in the class comments/docs above)
The hash returned flattens and inverts the two source hashes, so that all possible lookup keys (the keys and their aliases) are hash keys and the non-aliased lookup key is the value.
For example, when
OTHER_LOOKUP_KEYS = { serial_number: { aliases: [:serialnumber, :sn], fetch_rsrc_key: :serialnumber }, udid: { fetch_rsrc_key: :udid }, wifi_mac_address: { aliases: [:macaddress, :macaddr], fetch_rsrc_key: :macaddress } }
It is combined with DEFAULT_LOOKUP_KEYS
to produce:
{ id: :id, name: :name, serial_number: :serial_number, serialnumber: :serial_number, sn: :serial_number, udid: :udid, wifi_mac_address: :wifi_mac_address, macaddress: :wifi_mac_address, macaddr: :wifi_mac_address }
If the optional parameter no_aliases: is truthy, only the real keynames are returned in an array, so the above would become
[:id, :name, :serial_number, :udid, :wifi_mac_address]
@param no_aliases [Boolean] Only return the real keys, no aliases.
@return [Hash {Symbol: Symbol}] when no_aliases is falsey, the lookup keys
and aliases for this subclass.
@return [Array<Symbol>] when no_aliases is truthy, the lookup keys for this
subclass
# File lib/jamf/api/classic/base_classes/api_object.rb 403 def self.lookup_keys(no_aliases: false, fetch_rsrc_keys: false) 404 parse_lookup_keys unless @lookup_keys 405 no_aliases ? @lookup_keys.values.uniq : @lookup_keys 406 end
backward compatability @deprecated use .create instead
# File lib/jamf/api/classic/base_classes/api_object.rb 1154 def self.make(**args) 1155 create(**args) 1156 end
A Hash
of all members of this collection where the keys are some identifier and values are any other attribute.
@param ident [Symbol] An identifier of this Class, used as the key
for the mapping Hash. Aliases are acceptable, e.g. :sn for :serialNumber
@param to [Symbol] The attribute to which the ident will be mapped.
Aliases are acceptable, e.g. :name for :displayName
@param refresh [Boolean] Re-read the ‘all’ data from the API? otherwise
use the cached data if available.
@param cnx (see .all)
@return [Hash {Symbol: Object}] A Hash
of identifier mapped to attribute
# File lib/jamf/api/classic/base_classes/api_object.rb 559 def self.map_all(ident, to:, cnx: Jamf.cnx, refresh: false, cached_list: nil) 560 orig_ident = ident 561 ident = lookup_keys[ident] 562 raise Jamf::InvalidDataError, "No identifier :#{orig_ident} for class #{self}" unless ident 563 564 list = cached_list || all(refresh, cnx: cnx) 565 mapped = list.map do |i| 566 [ 567 i[ident], 568 i[to] 569 ] 570 end # do i 571 572 mapped.to_h 573 end
Return a hash of all objects of this subclass in the JSS
where the key is the id, and the value is some other key in the data items returned by the Jamf::APIObject.all
.
If the other key doesn’t exist in the API summary data from .all (eg :udid for Jamf::Department
) the values will be nil.
Use this method to map ID numbers to other identifiers returned by the API list resources. Invert its result to map the other identfier to ids.
@example
Jamf::Computer.map_all_ids_to(:serial_number) # Returns, eg {2 => "C02YD3U8JHD3", 5 => "VMMz7xgg8lYZ"} Jamf::Computer.map_all_ids_to(:serial_number).invert # Returns, eg {"C02YD3U8JHD3" => 2, "VMMz7xgg8lYZ" => 5}
These hashes are cached separately from the .all data, and when the refresh parameter is truthy, both will be refreshed.
WARNING: Some values in the output of .all are not guaranteed to be unique in Jamf
Pro. This is fine in the direct output of this method, each id will be the key for some value and many ids might have the same value. However if you invert that hash, the values become keys, and the ids become the values, and there can be only one id per each new key. Which id becomes associated with a value is undefined, and data about the others is lost. This is especially important if you ‘.map_all_ids_to :name`, since, for some objects, names are not unique.
@param other_key the other data key with which to associate each id
@param refresh should the data re-queried from the API?
@param cnx [Jamf::Connection] an API connection to use for the query.
Defaults to the corrently active API. See {Jamf::Connection}
@return [Hash{Integer => Oject}] the associated ids and data
# File lib/jamf/api/classic/base_classes/api_object.rb 616 def self.map_all_ids_to(other_key, refresh = false, cached_list: nil, api: nil, cnx: Jamf.cnx) 617 cnx = api if api 618 619 map_all :id, to: other_key, refresh: refresh, cnx: cnx, cached_list: cached_list 620 621 # # we will accept any key, it'll just return nil if not in the 622 # # .all hashes. However if we're given an alias of a lookup key 623 # # we need to convert it to its real name. 624 # other_key = lookup_keys[other_key] if lookup_keys[other_key] 625 # 626 # cache_key = "#{self::RSRC_LIST_KEY}_map_#{other_key}".to_sym 627 # cache = cnx.c_object_list_cache 628 # cache[cache_key] = nil if refresh 629 # return cache[cache_key] if cache[cache_key] 630 # 631 # map = {} 632 # all(refresh, cnx: cnx).each { |i| map[i[:id]] = i[other_key] } 633 # cache[cache_key] = map 634 end
Disallow direct use of ruby’s .new class method for creating instances. Require use of .fetch or .make
# File lib/jamf/api/classic/base_classes/api_object.rb 1160 def self.new(**args) 1161 validate_not_metaclass(self) 1162 1163 calling_method = caller_locations(1..1).first.label 1164 raise Jamf::UnsupportedError, 'Use .fetch or .create to instantiate APIObject classes' unless OK_INSTANTIATORS.include? calling_method 1165 1166 super 1167 end
The args hash must include :id, :name, or :data.
-
:id or :name will be looked up via the API
-
if the subclass includes
Jamf::Creatable
, :id can be :new, to create a new object in theJSS
. and :name is required
-
-
:data must be the JSON output of a separate {Jamf::Connection} query (a
Hash
of valid object data)
Some subclasses can accept other options, by pasing their keys in a final Array
@param args the data for looking up, or constructing, a new object.
@option args :id the jss id to look up
@option args :name the name to look up
@option args :fetch_rsrc a non-standard resource for fetching
API data e.g. to limit the data returned
# File lib/jamf/api/classic/base_classes/api_object.rb 1271 def initialize(**args) 1272 @cnx = args[:cnx] 1273 @cnx ||= args[:api] 1274 @cnx ||= Jamf.cnx 1275 1276 # we're making a new one in the JSS 1277 if args[:id] == :new 1278 validate_init_for_creation(args) 1279 setup_object_for_creation(args) 1280 @need_to_update = true 1281 1282 # we're instantiating an existing one in the jss 1283 else 1284 @init_data = look_up_object_data(args) 1285 @need_to_update = false 1286 end ## end arg parsing 1287 1288 parse_init_data 1289 end
POST some raw XML to the API for a given id in this subclass.
WARNING: You must create or acquire the XML to be sent, and no validation will be performed on it. It must be a String
, or something that returns an XML string with to_s
, such as a REXML::Document, or a REXML::Element.
This probably isn’t as much of a speed gain as get_raw
or put_raw
, as opposed to instantiating a ruby object, but might still be useful.
This is really just a wrapper around {APIConnection.c_post} that automatically fills in the RSRC::BASE value for you.
@param xml [String, to_s
] The XML to send
@param cnx [Jamf::Connection] the connection thru which to fetch this
object. Defaults to the deault API connection in Jamf.cnx
@return [REXML::Document] the XML response from the API
# File lib/jamf/api/classic/base_classes/api_object.rb 1111 def self.post_raw(xml, api: nil, cnx: Jamf.cnx) 1112 cnx = api if api 1113 1114 validate_not_metaclass(self) 1115 rsrc = "#{self::RSRC_BASE}/id/-1" 1116 REXML::Document.new(cnx.c_post(rsrc, xml.to_s)) 1117 end
PUT some raw XML to the API for a given id in this subclass.
WARNING: You must create or acquire the XML to be sent, and no validation will be performed on it. It must be a String
, or something that returns an XML string with to_s
, such as a REXML::Document, or a REXML::Element.
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.
This is really just a wrapper around {APIConnection.c_put} that automatically fills in the RSRC::BASE value for you.
@param id [Integer] the id of the object to PUT
@param xml [String, to_s
] The XML to send
@param cnx [Jamf::Connection] the connection thru which to fetch this
object. Defaults to the deault API connection in Jamf.cnx
@return [REXML::Document] the XML response from the API
# File lib/jamf/api/classic/base_classes/api_object.rb 1083 def self.put_raw(id, xml, api: nil, cnx: Jamf.cnx) 1084 cnx = api if api 1085 1086 validate_not_metaclass(self) 1087 rsrc = "#{self::RSRC_BASE}/id/#{id}" 1088 REXML::Document.new(cnx.c_put(rsrc, xml.to_s)) 1089 end
get the real lookup key frm a given alias
@param key the key or an aliase of the key
@return [Symbol] the real key for the given key
# File lib/jamf/api/classic/base_classes/api_object.rb 451 def self.real_lookup_key(key) 452 real_key = lookup_keys[key] 453 raise ArgumentError, "Unknown lookup key '#{key}' for #{self}" unless real_key 454 455 real_key 456 end
Return the id of the object of this subclass with the given identifier.
Return nil if no object has an identifier that matches.
For all objects the ‘name’ is an identifier. Some objects have more, e.g. udid, mac_address & serial_number. Matches are case-insensitive.
NOTE: while name is an identifier, for Computers and MobileDevices, it need not be unique in Jamf
. If name is matched, which one gets returned is undefined. In short - dont’ use names here unless you know they are unique.
NOTE: Integers passed in as strings, e.g. ‘12345’ will be converted to integers and return the matching integer id if it exists.
This means that if you have names that might match ‘12345’ and you use
valid_id '12345'
you will get back the id 12345, if such an id exists, even if it is not the object with the name ‘12345’
To explicitly look for ‘12345’ as a name, use:
valid_id name: '12345' See the ident_and_val param below.
@param identfier [String,Integer] An identifier for an object, a value for
one of the available lookup_keys. Omit this and use 'identifier: value' if you want to limit the search to a specific indentifier key, e.g. name: 'somename' or id: 76538
@param refresh [Boolean] Should the data be re-read from the server
@param ident_and_val [Hash] Do not pass in Hash
.
This Hash internally holds the arbitrary identifier key and desired value when you call ".valid_id ident: 'value'", e.g. ".valid_id name: 'somename'" or ".valid_id udid: some_udid" Using explicit identifier keys like this will speed things up, since the method doesn't have to search through all available identifiers for the desired value.
@param cnx [Jamf::Connection] an API connection to use for the query.
Defaults to the corrently active API. See {Jamf::Connection}
@return [Integer, nil] the id of the matching object, or nil if it doesn’t exist
# File lib/jamf/api/classic/base_classes/api_object.rb 711 def self.valid_id(identifier = nil, refresh = false, api: nil, cnx: Jamf.cnx, **ident_and_val) 712 cnx = api if api 713 714 # refresh the cache if needed 715 all(refresh, cnx: cnx) if refresh 716 717 # Were we given an explict identifier key, like name: or id:? 718 # If so, just look for that. 719 unless ident_and_val.empty? 720 # only the first k/v pair of the ident_and_val hash is used 721 key = ident_and_val.keys.first 722 val = ident_and_val[key] 723 724 # if we are explicitly looking for an id, ensure we use an integer 725 # even if we were given an integer in a string. 726 if key == :id 727 val = val.to_i if val.is_a?(String) && val.j_integer? 728 return all_ids(cnx: cnx).include?(val) ? val : nil 729 end 730 731 # map the identifiers to ids, and return the id if there's 732 # a case-insensitive matching identifire 733 map_all(key, to: :id).each do |ident_val, id| 734 return id if ident_val.to_s.casecmp? val.to_s 735 end 736 nil 737 end 738 739 # If we are here, we need to seach all available identifier keys 740 # Start by looking for it as an id. 741 742 # it its a valid integer id, return it 743 return identifier if all_ids(cnx: cnx).include? identifier 744 745 # if its a valid integer-in-a-string id, return it 746 if identifier.is_a?(String) && identifier.j_integer? 747 int_id = identifier.to_i 748 return int_id if all_ids(cnx: cnx).include? int_id 749 end 750 751 # Now go through all the other identifier keys 752 keys_to_check = lookup_keys(no_aliases: true) 753 keys_to_check.delete :id # we've already checked :id 754 755 # loop thru looking for a match 756 keys_to_check.each do |key| 757 mapped_ids = map_all_ids_to key, cnx: cnx 758 matches = mapped_ids.select { |_id, ident| ident.casecmp? identifier } 759 # If exactly one match, return the id 760 return matches.keys.first if matches.size == 1 761 end 762 763 nil 764 end
Can’t use APIObject
directly.
# File lib/jamf/api/classic/base_classes/api_object.rb 1216 def self.validate_not_metaclass(klass) 1217 raise Jamf::UnsupportedError, 'Jamf::APIObject is a metaclass. Do not use it directly' if klass == Jamf::APIObject 1218 end
Convert an Array
of Hashes of API object data to a REXML element.
Given an Array
of Hashes of items in the subclass where each Hash
has at least an :id or a :name key, (as what comes from the .all class method) return a REXML <classes> element with one <class> element per Hash
member.
@example
# for class Jamf::Computer some_comps = [{:id=>2, :name=>"kimchi"},{:id=>5, :name=>"mantis"}] xml_names = Jamf::Computer.xml_list some_comps puts xml_names # output manually formatted for clarity, xml.to_s has no newlines between elements <computers> <computer> <name>kimchi</name> </computer> <computer> <name>mantis</name> </computer> </computers> xml_ids = Jamf::Computer.xml_list some_comps, :id puts xml_names # output manually formatted for clarity, xml.to_s has no newlines between elements <computers> <computer> <id>2</id> </computer> <computer> <id>5</id> </computer> </computers>
@param array[Array<Hash{:name=>String, :id =>Integer, Symbol=>#to_s}>] the Array
of subclass data to convert
@param content the Hash
key to use as the inner element for each member of the Array
@return [REXML::Element] the XML element representing the data
# File lib/jamf/api/classic/base_classes/api_object.rb 879 def self.xml_list(array, content = :name) 880 JSS.item_list_to_rexml_list self::RSRC_LIST_KEY, self::RSRC_OBJECT_KEY, array, content 881 end
Private Class Methods
Used by .lookup_keys
# File lib/jamf/api/classic/base_classes/api_object.rb 425 def self.parse_lookup_keys 426 @lookup_keys = {} 427 @fetch_rsrc_keys = {} 428 429 hsh = DEFAULT_LOOKUP_KEYS.dup 430 hsh.merge!(self::OTHER_LOOKUP_KEYS) if defined? self::OTHER_LOOKUP_KEYS 431 432 hsh.each do |key, info| 433 @lookup_keys[key] = key 434 @fetch_rsrc_keys[key] = info[:fetch_rsrc_key] 435 next unless info[:aliases] 436 437 info[:aliases].each do |a| 438 @lookup_keys[a] = key 439 @fetch_rsrc_keys[a] = info[:fetch_rsrc_key] 440 end 441 end # self::OTHER_LOOKUP_KEYS.each 442 end
Public Instance Methods
COMPARABLE APIobjects are == if all of their lookup key values are the same
# File lib/jamf/api/classic/base_classes/api_object.rb 280 def <=>(other) 281 idents_combined <=> other.idents_combined 282 end
Make an entry in this object’s Object
History. For this to work, the APIObject
subclass must define OBJECT_HISTORY_OBJECT_TYPE, an integer indicating the object type in the OBJECT_HISTORY_TABLE
in the database (e.g. for computers, the object type is 1)
NOTE: Object
history is not available via the Classic API,
so access is only available through direct MySQL connections
Also: the ‘details’ column in the table shows up in the
'notes' column of the Web UI. and the 'object_description' column of the table shows up in the 'details' column of the UI, under the 'details' button. The params below reflect the UI, not the table.
@param user the username creating the entry.
@param notes A string that appears as a ‘note’ in the history
@param details A string that appears as the ‘details’ in the history
@return [void]
# File lib/jamf/api/classic/base_classes/api_object.rb 1482 def add_object_history_entry(user: nil, notes: nil, details: nil) 1483 validate_object_history_available 1484 1485 raise Jamf::MissingDataError, 'A user: must be provided to make the entry' unless user 1486 1487 raise Jamf::MissingDataError, 'notes: must be provided to make the entry' unless notes 1488 1489 user = "'#{Mysql.quote user.to_s}'" 1490 notes = "'#{Mysql.quote notes.to_s}'" 1491 obj_type = self.class::OBJECT_HISTORY_OBJECT_TYPE 1492 1493 field_list = 'object_type, object_id, username, details, timestamp_epoch' 1494 value_list = "#{obj_type}, #{@id}, #{user}, #{notes}, #{Time.now.to_jss_epoch}" 1495 1496 if details 1497 field_list << ', object_description' 1498 value_list << ", '#{Mysql.quote details.to_s}'" 1499 end # if details 1500 1501 q = "INSERT INTO #{OBJECT_HISTORY_TABLE} 1502 (#{field_list}) 1503 VALUES 1504 (#{value_list})" 1505 1506 Jamf::DB_CNX.db.query q 1507 end
@return [Boolean] See {Jamf::Categorizable}
# File lib/jamf/api/classic/base_classes/api_object.rb 1352 def categorizable? 1353 defined? self.class::CATEGORIZABLE 1354 end
@return [Boolean] See {Jamf::Creatable}
# File lib/jamf/api/classic/base_classes/api_object.rb 1342 def creatable? 1343 defined? self.class::CREATABLE 1344 end
@deprecated, use save
# File lib/jamf/api/classic/base_classes/api_object.rb 1322 def create 1323 raise Jamf::UnsupportedError, 'Creating this object in the JSS is currently not supported by ruby-jss' unless creatable? 1324 1325 create_in_jamf 1326 end
@return [Boolean] See {Jamf::Criteriable}
# File lib/jamf/api/classic/base_classes/api_object.rb 1367 def criterable? 1368 defined? self.class::CRITERIABLE 1369 end
Delete this item from the JSS
.
@seealso {APIObject.delete} for deleting one or more objects by id without needing to instantiate
Subclasses may want to redefine this method, first calling super, then setting other attributes to nil, false, empty, etc..
@return [void]
# File lib/jamf/api/classic/base_classes/api_object.rb 1417 def delete 1418 return unless @in_jss 1419 1420 @cnx.c_delete @rest_rsrc 1421 1422 @rest_rsrc = "#{self.class::RSRC_BASE}/name/#{CGI.escape @name.to_s}" 1423 @id = nil 1424 @in_jss = false 1425 @need_to_update = false 1426 1427 # clear any cached all-lists or id-maps for this class 1428 # so they'll re-cache as needed 1429 @cnx.flushcache self.class::RSRC_LIST_KEY 1430 # self.class.all :refresh, cnx: @cnx 1431 1432 :deleted 1433 end
@return [Boolean] See {Jamf::extendable}
# File lib/jamf/api/classic/base_classes/api_object.rb 1377 def extendable? 1378 defined? self.class::EXTENDABLE 1379 end
# File lib/jamf/api/classic/base_classes/api_object.rb 284 def idents_combined 285 my_keys = self.class.lookup_keys.values.uniq 286 my_keys.map { |k| send(k).to_s }.sort.join 287 end
@return [Boolean] See {Jamf::Locatable}
# File lib/jamf/api/classic/base_classes/api_object.rb 1387 def locatable? 1388 defined? self.class::LOCATABLE 1389 end
@return [Boolean] See {Jamf::Matchable}
# File lib/jamf/api/classic/base_classes/api_object.rb 1382 def matchable? 1383 defined? self.class::MATCHABLE 1384 end
the object history for this object, an array of hashes one per history entry, in order of creation. Each hash contains:
user: String, the username that created the entry notes: String, the notes for the entry date: Time, the timestamp for the entry details: String or nil, any details provided for the entry
@return [Array<Hash>] the object history
# File lib/jamf/api/classic/base_classes/api_object.rb 1519 def object_history 1520 validate_object_history_available 1521 1522 q = "SELECT username, details, timestamp_epoch, object_description 1523 FROM #{OBJECT_HISTORY_TABLE} 1524 WHERE object_type = #{self.class::OBJECT_HISTORY_OBJECT_TYPE} 1525 AND object_id = #{@id} 1526 ORDER BY object_history_id ASC" 1527 1528 result = Jamf::DB_CNX.db.query q 1529 history = [] 1530 result.each do |entry| 1531 history << { 1532 user: entry[0], 1533 notes: entry[1], 1534 date: JSS.epoch_to_time(entry[2]), 1535 details: entry[3] 1536 } 1537 end # each do entry 1538 history 1539 end
Print the rest_xml
value of the object to stdout, with indentation. Useful for debugging.
@return [void]
# File lib/jamf/api/classic/base_classes/api_object.rb 1546 def ppx 1547 return nil unless creatable? || updatable? 1548 1549 formatter = REXML::Formatters::Pretty.new(2) 1550 formatter.compact = true 1551 formatter.write(REXML::Document.new(rest_xml), $stdout) 1552 puts 1553 end
Remove the init_data
and api object from the instance_variables used to create pretty-print (pp) output.
@return [Array] the desired instance_variables
# File lib/jamf/api/classic/base_classes/api_object.rb 1449 def pretty_print_instance_variables 1450 vars = instance_variables.sort 1451 vars.delete :@cnx 1452 vars.delete :@init_data 1453 vars.delete :@main_subset 1454 vars 1455 end
@return [Boolean] See {Jamf::Purchasable}
# File lib/jamf/api/classic/base_classes/api_object.rb 1392 def purchasable? 1393 defined? self.class::PURCHASABLE 1394 end
Either Create or Update this object in the JSS
If this item is creatable or updatable, then create it if needed, or update it if it already exists.
@return [Integer] the id of the item created or updated
# File lib/jamf/api/classic/base_classes/api_object.rb 1313 def save 1314 if @in_jss 1315 update 1316 else 1317 create 1318 end 1319 end
@return [Boolean] See {Jamf::Scopable}
# File lib/jamf/api/classic/base_classes/api_object.rb 1397 def scopable? 1398 defined? self.class::SCOPABLE 1399 end
@return [Boolean] See {Jamf::SelfServable}
# File lib/jamf/api/classic/base_classes/api_object.rb 1362 def self_servable? 1363 defined? self.class::SELF_SERVABLE 1364 end
@return [Boolean] See {Jamf::Sitable}
# File lib/jamf/api/classic/base_classes/api_object.rb 1372 def sitable? 1373 defined? self.class::SITABLE 1374 end
A meaningful string representation of this object
@return [String]
# File lib/jamf/api/classic/base_classes/api_object.rb 1439 def to_s 1440 "#{self.class}@#{cnx.host}, id: #{@id}" 1441 end
@return [Boolean] See {Jamf::Updatable}
# File lib/jamf/api/classic/base_classes/api_object.rb 1347 def updatable? 1348 defined? self.class::UPDATABLE 1349 end
@deprecated, use save
# File lib/jamf/api/classic/base_classes/api_object.rb 1329 def update 1330 raise Jamf::UnsupportedError, 'Updating this object in the JSS is currently not supported by ruby-jss' unless updatable? 1331 1332 update_in_jamf 1333 end
@return [Boolean] See {Jamf::Uploadable}
# File lib/jamf/api/classic/base_classes/api_object.rb 1402 def uploadable? 1403 defined? self.class::UPLOADABLE 1404 end
@return [Boolean] See {Jamf::VPPable}
# File lib/jamf/api/classic/base_classes/api_object.rb 1357 def vppable? 1358 defined? self.class::VPPABLE 1359 end
Private Instance Methods
Find which part of the @init_data contains the :id and :name
If they aren’t at the top-level of the init hash they are in a subset hash, usually :general, but sometimes someething else, like ldap servers, which have them in :connection Whereever both :id and :name are, that’s the main subset
@return [Hash] The part of the @init_data containg the :id and :name
# File lib/jamf/api/classic/base_classes/api_object.rb 1670 def find_main_subset 1671 return @init_data if @init_data[:id] && @init_data[:name] 1672 return @init_data[:general] if @init_data[:general] && @init_data[:general][:id] && @init_data[:general][:name] 1673 1674 @init_data.each do |_key, value| 1675 next unless value.is_a? Hash 1676 return value if value.keys.include?(:id) && value.keys.include?(:name) 1677 end 1678 end
parse category data during initialization
@return [void]
# File lib/jamf/api/classic/base_classes/api_object.rb 1684 def initialize_category 1685 parse_category if categorizable? && @in_jss 1686 end
parse criteria data during initialization
@return [void]
# File lib/jamf/api/classic/base_classes/api_object.rb 1724 def initialize_criteria 1725 parse_criteria if criterable? 1726 end
parse ext_attrs data during initialization
@return [void]
# File lib/jamf/api/classic/base_classes/api_object.rb 1732 def initialize_ext_attrs 1733 parse_ext_attrs if extendable? 1734 end
parse location data during initialization
@return [void]
# File lib/jamf/api/classic/base_classes/api_object.rb 1700 def initialize_location 1701 parse_location if locatable? 1702 end
parse purchasing data during initialization
@return [void]
# File lib/jamf/api/classic/base_classes/api_object.rb 1708 def initialize_purchasing 1709 parse_purchasing if purchasable? 1710 end
parse scope data during initialization
@return [void]
# File lib/jamf/api/classic/base_classes/api_object.rb 1716 def initialize_scope 1717 parse_scope if scopable? 1718 end
parse self_service data during initialization
@return [void]
# File lib/jamf/api/classic/base_classes/api_object.rb 1748 def initialize_self_service 1749 parse_self_service if self_servable? 1750 end
parse site data during initialization
@return [void]
# File lib/jamf/api/classic/base_classes/api_object.rb 1692 def initialize_site 1693 parse_site if sitable? 1694 end
parse vpp data during initialization
@return [void]
# File lib/jamf/api/classic/base_classes/api_object.rb 1740 def initialize_vpp 1741 parse_vpp if vppable? 1742 end
Given initialization args, perform an API lookup for an object.
@param args The args passed to initialize, which must have either
key :id or key :fetch_rsrc
@return [Hash] The parsed JSON data for the object from the API
# File lib/jamf/api/classic/base_classes/api_object.rb 1606 def look_up_object_data(args) 1607 rsrc = args[:fetch_rsrc] 1608 rsrc ||= "#{self.class::RSRC_BASE}/id/#{args[:id]}" 1609 1610 # if needed, a non-standard object key can be passed by a subclass. 1611 # e.g. User when loookup is by email. 1612 args[:rsrc_object_key] ||= self.class::RSRC_OBJECT_KEY 1613 1614 raw_json = 1615 if defined? self.class::USE_XML_WORKAROUND 1616 # if we're here, the API JSON is borked, so use the XML 1617 Jamf::XMLWorkaround.data_via_xml rsrc, self.class::USE_XML_WORKAROUND, @cnx 1618 else 1619 # otherwise 1620 @cnx.c_get(rsrc) 1621 end 1622 1623 raw_json[args[:rsrc_object_key]] 1624 end
Start examining the @init_data recieved from the API
@return [void]
# File lib/jamf/api/classic/base_classes/api_object.rb 1630 def parse_init_data 1631 @init_data ||= {} 1632 # set empty strings to nil 1633 @init_data.jss_nillify! '', :recurse 1634 1635 # Find the "main" subset which contains :id and :name 1636 @main_subset = find_main_subset 1637 @name = @main_subset[:name] 1638 1639 if @main_subset[:id] == :new 1640 @id = 0 1641 @in_jss = false 1642 else 1643 @id = @main_subset[:id].to_i 1644 @in_jss = true 1645 end 1646 1647 @rest_rsrc = "#{self.class::RSRC_BASE}/id/#{@id}" 1648 1649 ##### Handle Mix-ins 1650 initialize_category 1651 initialize_site 1652 initialize_location 1653 initialize_purchasing 1654 initialize_scope 1655 initialize_criteria 1656 initialize_ext_attrs 1657 initialize_vpp 1658 initialize_self_service 1659 end
Return a String
with the XML Resource for submitting creation or changes to the JSS
via the API via the Creatable
or Updatable
modules
Most classes will redefine this method.
# File lib/jamf/api/classic/base_classes/api_object.rb 1774 def rest_xml 1775 doc = REXML::Document.new Jamf::Connection::XML_HEADER 1776 tmpl = doc.add_element self.class::RSRC_OBJECT_KEY.to_s 1777 tmpl.add_element('name').text = @name 1778 doc.to_s 1779 end
Set the basics for creating a new object in the JSS
@param args describe_args
@return [Type] description_of_returned_object
# File lib/jamf/api/classic/base_classes/api_object.rb 1758 def setup_object_for_creation(args) 1759 # NOTE: subclasses may want to pre-populate more keys in @init_data when :id == :new 1760 # then parse them into attributes later. 1761 @init_data = args 1762 @name = args[:name] 1763 @in_jss = false 1764 @rest_rsrc = "#{self.class::RSRC_BASE}/name/#{CGI.escape @name.to_s}" 1765 @need_to_update = true 1766 end
If we’re making a new object in the JSS
, make sure we were given valid data to do so, raise exceptions otherwise.
NOTE: some subclasses may do further validation.
TODO: support for objects that can have duplicate names.
@param args The args passed to initialize
@return [void]
# File lib/jamf/api/classic/base_classes/api_object.rb 1587 def validate_init_for_creation(args) 1588 raise Jamf::UnsupportedError, "Creating #{self.class::RSRC_LIST_KEY} isn't yet supported. Please use other Casper workflows." unless creatable? 1589 1590 raise Jamf::MissingDataError, "You must provide a :name to create a #{self.class::RSRC_OBJECT_KEY}." unless args[:name] 1591 1592 return if defined? self.class::NON_UNIQUE_NAMES 1593 1594 matches = self.class.all_names(:refresh, cnx: @cnx).select { |n| n.casecmp? args[:name] } 1595 1596 raise Jamf::AlreadyExistsError, "A #{self.class::RSRC_OBJECT_KEY} already exists with the name '#{args[:name]}'" unless matches.empty? 1597 end
Raise an exception if object history is not available for this object
@return [void]
# File lib/jamf/api/classic/base_classes/api_object.rb 1565 def validate_object_history_available 1566 raise Jamf::NoSuchItemError, 'Object not yet created' unless @id && @in_jss 1567 1568 raise Jamf::InvalidConnectionError, 'Not connected to MySQL' unless Jamf::DB_CNX.connected? 1569 1570 return if defined? self.class::OBJECT_HISTORY_OBJECT_TYPE 1571 1572 raise Jamf::UnsupportedError, 1573 "Object History access is not supported for #{self.class} objects at this time" 1574 end