Module: Jamf::CollectionResource::ClassMethods
- Includes:
- JPAPIResource::ClassMethods
- Defined in:
- lib/jamf/api/jamf_pro/mixins/collection_resource.rb
Overview
Class Methods
Class Method Summary collapse
-
.extended(extender) ⇒ Object
when this module is included, also extend our 'parent' class methods.
Instance Method Summary collapse
-
#all(sort: nil, filter: nil, instantiate: false, cnx: Jamf.cnx, refresh: nil) ⇒ Array<Hash, Jamf::CollectionResource>
Get all instances of a CollectionResource, possibly sorted or limited by a filter.
-
#available_list_methods ⇒ Hash{String: Symbol}
Method name to matching attribute name for all identifiers.
- #bulk_deletable? ⇒ Boolean
-
#creatable? ⇒ Boolean
By default, Collection Resources are creatable, i.e.
-
#create(**params) ⇒ Object
Make a new thing to be added to the API.
-
#deletable? ⇒ Boolean
By default, CollectionResource instances are deletable.
-
#delete(*ids, cnx: Jamf.cnx) ⇒ Array<Jamf::Connection::APIError::ErrorInfo] Info about any ids that failed to be deleted.
Delete one or more objects by id TODO: fix this return value, no more ErrorInfo.
-
#delete_path ⇒ Object
The path for DELETEing a single object from the collection.
-
#fetch(searchterm = nil, random: false, cnx: Jamf.cnx, **ident_and_val) ⇒ CollectionResource
Retrieve a member of a CollectionResource from the API.
- #filterable? ⇒ Boolean
-
#get_path ⇒ Object
The path for GETting a single object.
-
#identifiers ⇒ Array<Symbol>
The attribute names that are marked as identifiers.
-
#map_all(ident, to:, cnx: Jamf.cnx, cached_list: nil, refresh: nil) ⇒ Hash {Symbol: Object}
A Hash of all members of this collection where the keys are some identifier and values are any other attribute.
-
#method_missing(method, *args, &block) ⇒ Object
Dynamically create_identifier_list_methods when one is called.
-
#new(**data) ⇒ Object
included
from JPAPIResource::ClassMethods
Disallow direct use of ruby's .new class method for creating instances.
-
#pager(page_size: Jamf::Pager::DEFAULT_PAGE_SIZE, sort: nil, filter: nil, instantiate: false, cnx: Jamf.cnx) ⇒ Jamf::Pager
Return a Jamf::Pager object for retrieving all collection items in smaller groups.
-
#patch_path ⇒ Object
The path for PATCHing (updating in-place) a single object.
-
#post_path ⇒ Object
The path for POSTing to create a single object in the collection.
-
#put_path ⇒ Object
The path for PUTting (replacing) a single object.
-
#raw_data(searchterm = nil, ident: nil, value: nil, cnx: Jamf.cnx) ⇒ Hash?
Given a key (identifier) and value for this collection, return the raw data Hash (the JSON object) for the matching API object or nil if there's no match for the given value.
-
#raw_data_by_id(id, cnx: Jamf.cnx) ⇒ Object
get the basic dataset by id, with optional request params to get more than basic data.
-
#raw_data_by_other_identifier(identifier, value, cnx: Jamf.cnx) ⇒ Object
Given an indentier attr.
-
#raw_data_by_searchterm_only(searchterm, cnx: Jamf.cnx) ⇒ Object
Match the given value in all possibly identifiers.
-
#respond_to_missing?(method) ⇒ Boolean
this is needed to prevent problems with method_missing!.
-
#valid_id(searchterm = nil, cnx: Jamf.cnx, **ident_and_val) ⇒ String?
Look up the valid ID for any arbitrary identifier.
-
#which_api ⇒ Object
included
from JPAPIResource::ClassMethods
Indicate that this class comes from the Jamf Pro API.
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(method, *args, &block) ⇒ Object
Dynamically create_identifier_list_methods when one is called.
600 601 602 603 604 605 606 607 608 |
# File 'lib/jamf/api/jamf_pro/mixins/collection_resource.rb', line 600 def method_missing(method, *args, &block) if available_list_methods.key? method.to_s attr_name = available_list_methods[method.to_s] create_identifier_list_method attr_name.to_sym, method send method, *args else super end end |
Class Method Details
Instance Method Details
#all(sort: nil, filter: nil, instantiate: false, cnx: Jamf.cnx, refresh: nil) ⇒ Array<Hash, Jamf::CollectionResource>
Get all instances of a CollectionResource, possibly sorted or limited by a filter.
By default, this method will return a single Array data about all items in the CollectionResouce, in the server's default sort order, or a sort order you specify. As long as you don't request a filtered result, this full list is cached for future use (see Caching, below)
If you specify a filter, the Array returned by the server will contain only matching objects, and it will not be cached.
#### Server-side Sorting
Sorting criteria can be provided using the 'sort:' parameter, which is a String of the format 'property:direction', where direction is 'asc' or 'desc' E.g.
"username:asc"
Multiple properties are supported, either as separate strings in an Array, or a single string, comma separated. E.g.
"username:asc,timestamp:desc"
is the same as
["username:asc", "timestamp:desc"]
which will sort by username alphabetically, and within each username, sort by timestamp newest first.
Please see the JamfPro API documentation for the resource for details about available sorting properties and default sorting criteria
When the sort: param is provided, the server is always queried, and the results will be cached, as long as the results were not filtered. See Caching, below.
#### Filtering
Some CollectionResouces support RSQL filters to limit which objects are returned by the server. These filters can be applied using the filter: parameter, in which case this `all` method will return “all that match the filter”.
The filter parameter is a string, as it would be provided in the API URL manually, e.g. 'categoryName==“Category”' (be careful about inner quoting)
If the resource doesn't support filters, the filter parameter is ignored.
Please see the JamfPro API documentation for the resource to see if filters are supported, and a list of available fields. See also developer.jamf.com/jamf-pro/docs/filtering-with-rsql
#### Instantiation
All data from the API comes from the server in JSON format, mostly as JSON 'objects', which are the equivalent of ruby Hashes. When fetching an individual instance of an object from the API, ruby-jss uses the JSON Hash to create the ruby object, i.e. to 'instantiate' it as an instance of its ruby class. Doing this for many objects can slow things down.
Because of this, the 'all' method defaults to returning an Array of the minimally-processed JSON Hashes it gets from the API. If you can get your desired data from these Hashes, it may be more efficient to do so.
However sometimes you really need the fully instantiated ruby objects for all items returned - especially if you're using filters and not actually
processing all items of the class. In such cases you can pass a truthy
value to the instantiate: parameter, and the Array will contain fully instantiated ruby objects, not Hashes of API data.
#### Caching - none for Jamf Pro API objects.
Unlike the Classic APIObjects, Objects from the Jamf Pro API are not cached and any call to 'all' or methods that use it, will always query the API. If you need to use the resulting array for multiple tasks, save it into a variable and use that.
258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 |
# File 'lib/jamf/api/jamf_pro/mixins/collection_resource.rb', line 258 def all(sort: nil, filter: nil, instantiate: false, cnx: Jamf.cnx, refresh: nil) # if we are here, we need to query for all items, possibly filtered and # sorted sort = Jamf::Sortable.parse_url_sort_param(sort) filter = filterable? ? Jamf::Filterable.parse_url_filter_param(filter) : nil instantiate &&= self # always use a pager to get all pages, because even if you don't ask for # paged data, it comes in pages or 2000 Jamf::Pager.all_pages( list_path: self::LIST_PATH, sort: sort, filter: filter, instantiate: instantiate, cnx: cnx ) end |
#available_list_methods ⇒ Hash{String: Symbol}
Returns Method name to matching attribute name for all identifiers.
617 618 619 620 621 622 623 624 625 626 627 |
# File 'lib/jamf/api/jamf_pro/mixins/collection_resource.rb', line 617 def available_list_methods return @available_list_methods if @available_list_methods @available_list_methods = {} identifiers.each do |i| meth_name = i.to_s.end_with?('s') ? "all_#{i}es" : "all_#{i}s" @available_list_methods[meth_name] = i end @available_list_methods end |
#bulk_deletable? ⇒ Boolean
566 567 568 |
# File 'lib/jamf/api/jamf_pro/mixins/collection_resource.rb', line 566 def bulk_deletable? singleton_class.ancestors.include? Jamf::BulkDeletable end |
#creatable? ⇒ Boolean
By default, Collection Resources are creatable, i.e. new instances can be created with .create, and added to the JSS with .save If a subclass is NOT creatble for any reason, just add
extend Jamf::Uncreatable
and this method will return false
494 495 496 |
# File 'lib/jamf/api/jamf_pro/mixins/collection_resource.rb', line 494 def creatable? true end |
#create(**params) ⇒ Object
Make a new thing to be added to the API
500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 |
# File 'lib/jamf/api/jamf_pro/mixins/collection_resource.rb', line 500 def create(**params) # no such animal when .creating params.delete :id # Which connection to use params[:cnx] ||= Jamf.cnx # So the super constructor knows we are instantiating an object that # isn't from the API, and will do validation on all params. params[:creating_from_create] = true new(**params) end |
#deletable? ⇒ Boolean
By default, CollectionResource instances are deletable. If not, just extend the subclass with Jamf::Undeletable, and this will return false, and .delete & #delete will raise errors
558 559 560 |
# File 'lib/jamf/api/jamf_pro/mixins/collection_resource.rb', line 558 def deletable? true end |
#delete(*ids, cnx: Jamf.cnx) ⇒ Array<Jamf::Connection::APIError::ErrorInfo] Info about any ids that failed to be deleted.
Delete one or more objects by id TODO: fix this return value, no more ErrorInfo
582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 |
# File 'lib/jamf/api/jamf_pro/mixins/collection_resource.rb', line 582 def delete(*ids, cnx: Jamf.cnx) raise Jamf::UnsupportedError, "Deleting #{self} objects is not currently supported" unless deletable? return bulk_delete(ids, cnx: Jamf.cnx) if self.bulk_deletable? errs = [] ids.each do |id_to_delete| cnx.jp_delete "#{delete_path}/#{id_to_delete}" rescue Jamf::Connection::JamfProAPIError => e raise e unless e.http_response.status == 404 errs += e.errors end # ids.each errs end |
#delete_path ⇒ Object
The path for DELETEing a single object from the collection.
Classes including CollectionResource really need to define DELETE_PATH if it is not the same as the LIST_PATH.
146 147 148 |
# File 'lib/jamf/api/jamf_pro/mixins/collection_resource.rb', line 146 def delete_path @delete_path ||= defined?(self::DELETE_PATH) ? self::DELETE_PATH : self::LIST_PATH end |
#fetch(searchterm = nil, random: false, cnx: Jamf.cnx, **ident_and_val) ⇒ CollectionResource
Retrieve a member of a CollectionResource from the API
To create new members to be added to the JSS, use Jamf::CollectionResource.create
You must know the specific identifier attribute you're looking up, e.g. :id or :name or :udid, (or an aliase thereof) then you can specify it like `.fetch name: 'somename'`, or `.fetch udid: 'someudid'`
532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 |
# File 'lib/jamf/api/jamf_pro/mixins/collection_resource.rb', line 532 def fetch(searchterm = nil, random: false, cnx: Jamf.cnx, **ident_and_val) if searchterm == :random random = true searchterm = nil end data = if searchterm raw_data searchterm, cnx: cnx elsif random all.sample else ident, value = ident_and_val.first ident && value ? raw_data(ident: ident, value: value, cnx: cnx) : nil end raise Jamf::NoSuchItemError, "No matching #{self}" unless data data[:cnx] = cnx new(**data) end |
#filterable? ⇒ Boolean
562 563 564 |
# File 'lib/jamf/api/jamf_pro/mixins/collection_resource.rb', line 562 def filterable? singleton_class.ancestors.include? Jamf::Filterable end |
#get_path ⇒ Object
The path for GETting a single object. The desired object id will be appended to the end, e.g. if this value is 'v1/buildings' and you want to GET the record for building id 23, then we will GET from 'v1/buildings/23'
Classes including CollectionResource really need to define GET_PATH if it is not the same as the LIST_PATH. rubocop:disable Naming/AccessorMethodName
103 104 105 |
# File 'lib/jamf/api/jamf_pro/mixins/collection_resource.rb', line 103 def get_path @get_path ||= defined?(self::GET_PATH) ? self::GET_PATH : self::LIST_PATH end |
#identifiers ⇒ Array<Symbol>
Returns the attribute names that are marked as identifiers.
153 154 155 156 157 158 159 |
# File 'lib/jamf/api/jamf_pro/mixins/collection_resource.rb', line 153 def identifiers idents = self::OAPI_PROPERTIES.select { |_attr, deets| deets[:identifier] }.keys idents += self::ALT_IDENTIFIERS if defined? self::ALT_IDENTIFIERS idents += self::NON_UNIQUE_IDENTIFIERS if defined? self::NON_UNIQUE_IDENTIFIERS idents.delete_if { |i| !self::OAPI_PROPERTIES.key?(i) } idents end |
#map_all(ident, to:, cnx: Jamf.cnx, cached_list: nil, refresh: nil) ⇒ Hash {Symbol: Object}
A Hash of all members of this collection where the keys are some identifier and values are any other attribute.
322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 |
# File 'lib/jamf/api/jamf_pro/mixins/collection_resource.rb', line 322 def map_all(ident, to:, cnx: Jamf.cnx, cached_list: nil, refresh: nil) raise Jamf::InvalidDataError, "No identifier :#{ident} for class #{self}" unless identifiers.include? ident raise Jamf::NoSuchItemError, "No attribute :#{to} for class #{self}" unless self::OAPI_PROPERTIES.key? to list = cached_list || all(cnx: cnx) to_class = self::OAPI_PROPERTIES[to][:class] to_multi = self::OAPI_PROPERTIES[to][:multi] mapped = list.map do |i| mapped_val = if to_class.is_a?(Symbol) i[to] elsif to_multi i[to].map { |sub_i| to_class.new(sub_i) } else to_class.new(i[to]) end [i[ident], mapped_val] end # do i mapped.to_h end |
#new(**data) ⇒ Object Originally defined in module JPAPIResource::ClassMethods
Disallow direct use of ruby's .new class method for creating instances. Require use of .fetch or .create, or 'all'
#pager(page_size: Jamf::Pager::DEFAULT_PAGE_SIZE, sort: nil, filter: nil, instantiate: false, cnx: Jamf.cnx) ⇒ Jamf::Pager
Return a Jamf::Pager object for retrieving all collection items in smaller groups.
For other parameters, see CollectionResource.all
289 290 291 292 293 294 295 296 297 298 299 300 301 302 |
# File 'lib/jamf/api/jamf_pro/mixins/collection_resource.rb', line 289 def pager(page_size: Jamf::Pager::DEFAULT_PAGE_SIZE, sort: nil, filter: nil, instantiate: false, cnx: Jamf.cnx) sort = Jamf::Sortable.parse_url_sort_param(sort) filter = filterable? ? Jamf::Filterable.parse_url_filter_param(filter) : nil Jamf::Pager.new( page_size: page_size, list_path: self::LIST_PATH, sort: sort, filter: filter, instantiate: instantiate, cnx: cnx ) end |
#patch_path ⇒ Object
The path for PATCHing (updating in-place) a single object. The desired object id will be appended to the end, e.g. if this value is 'v1/buildings' and you want to PATCH the record for building id 23, then we will PATCH to 'v1/buildings/23'
Classes including CollectionResource really need to define PATCH_PATH if it is not the same as the LIST_PATH.
127 128 129 |
# File 'lib/jamf/api/jamf_pro/mixins/collection_resource.rb', line 127 def patch_path @patch_path ||= defined?(self::PATCH_PATH) ? self::PATCH_PATH : self::LIST_PATH end |
#post_path ⇒ Object
The path for POSTing to create a single object in the collection.
Classes including CollectionResource really need to define POST_PATH if it is not the same as the LIST_PATH.
137 138 139 |
# File 'lib/jamf/api/jamf_pro/mixins/collection_resource.rb', line 137 def post_path @post_path ||= defined?(self::POST_PATH) ? self::POST_PATH : self::LIST_PATH end |
#put_path ⇒ Object
The path for PUTting (replacing) a single object. The desired object id will be appended to the end, e.g. if this value is 'v1/buildings' and you want to PUT the record for building id 23, then we will PUT to 'v1/buildings/23'
Classes including CollectionResource really need to define PUT_PATH if it is not the same as the LIST_PATH.
115 116 117 |
# File 'lib/jamf/api/jamf_pro/mixins/collection_resource.rb', line 115 def put_path @put_path ||= defined?(self::PUT_PATH) ? self::PUT_PATH : self::LIST_PATH end |
#raw_data(searchterm = nil, ident: nil, value: nil, cnx: Jamf.cnx) ⇒ Hash?
Given a key (identifier) and value for this collection, return the raw data Hash (the JSON object) for the matching API object or nil if there's no match for the given value.
In general you should use this if the form:
raw_data identifier: value
where identifier is one of the available identifiers for this class like id:, name:, serialNumber: etc.
In the unlikely event that you dont know which identifier a value is for or want to be able to take any of them without specifying, then you can use
raw_data some_value
If some_value is an integer or a string containing an integer, it is assumed to be an :id otherwise all the available identifers are searched, in the order you see them when you call <class>.identifiers
If no matching object is found, nil is returned.
Everything except :id is treated as a case-insensitive String
384 385 386 387 388 389 390 391 392 393 394 395 396 397 |
# File 'lib/jamf/api/jamf_pro/mixins/collection_resource.rb', line 384 def raw_data(searchterm = nil, ident: nil, value: nil, cnx: Jamf.cnx) # given a value with no ident key return raw_data_by_searchterm_only(searchterm, cnx: cnx) if searchterm # if we're here, we should know our ident key and value raise ArgumentError, 'Required parameter "identifier: value", where identifier is id:, name: etc.' unless ident && value return raw_data_by_id(value, cnx: cnx) if ident == :id return unless identifiers.include? ident raw_data_by_other_identifier(ident, value, cnx: cnx) end |
#raw_data_by_id(id, cnx: Jamf.cnx) ⇒ Object
get the basic dataset by id, with optional request params to get more than basic data
417 418 419 420 421 422 423 |
# File 'lib/jamf/api/jamf_pro/mixins/collection_resource.rb', line 417 def raw_data_by_id(id, cnx: Jamf.cnx) cnx.jp_get "#{get_path}/#{id}" rescue Jamf::Connection::JamfProAPIError => e return nil if e.errors.any? { |err| err.code == 'INVALID_ID' } raise e end |
#raw_data_by_other_identifier(identifier, value, cnx: Jamf.cnx) ⇒ Object
Given an indentier attr. key, and a value, return the raw data where that ident has that value, or nil
429 430 431 432 433 434 435 436 437 438 439 440 |
# File 'lib/jamf/api/jamf_pro/mixins/collection_resource.rb', line 429 def raw_data_by_other_identifier(identifier, value, cnx: Jamf.cnx) # if the API supports filtering by this identifier, just use that return pager(filter: "#{identifier}==\"#{value}\"", page_size: 1, cnx: cnx).page(:first).first if filterable? && filter_keys.include?(identifier) # otherwise we have to loop thru all the objects looking for the value cmp_val = value.to_s all(cnx: cnx).each do |data| return data if data[identifier].to_s.casecmp? cmp_val end nil end |
#raw_data_by_searchterm_only(searchterm, cnx: Jamf.cnx) ⇒ Object
Match the given value in all possibly identifiers
400 401 402 403 404 405 406 407 408 409 410 411 412 |
# File 'lib/jamf/api/jamf_pro/mixins/collection_resource.rb', line 400 def raw_data_by_searchterm_only(searchterm, cnx: Jamf.cnx) # if this is an integer or j_integer, assume its an ID return raw_data_by_id(searchterm, cnx: cnx) if searchterm.to_s.j_integer? identifiers.each do |ident| next if ident == :id data = raw_data_by_other_identifier(ident, searchterm, cnx: cnx) return data if data end # identifiers.each nil end |
#respond_to_missing?(method) ⇒ Boolean
this is needed to prevent problems with method_missing!
611 612 613 |
# File 'lib/jamf/api/jamf_pro/mixins/collection_resource.rb', line 611 def respond_to_missing?(method, *) available_list_methods.key?(method.to_s) || super end |
#valid_id(searchterm = nil, cnx: Jamf.cnx, **ident_and_val) ⇒ String?
Look up the valid ID for any arbitrary identifier. In general you should use this if the form:
valid_id identifier: value
where identifier is one of the available identifiers for this class like id:, name:, serialNumber: etc.
In the unlikely event that you dont know which identifier a value is for or want to be able to take any of them without specifying, then you can use
valid_id some_value
If some_value is an integer or a string containing an integer, it is assumed to be an id: otherwise all the available identifers are searched, in the order you see them when you call <class>.identifiers
If no matching object is found, nil is returned.
WARNING: Do not use this to look up ids for getting the raw API data for an object. Since this calls .raw_data itself, it is redundant to use .valid_id to get an id to then pass on to .raw_data Use raw_data directly like this:
data = raw_data(ident: val)
481 482 483 |
# File 'lib/jamf/api/jamf_pro/mixins/collection_resource.rb', line 481 def valid_id(searchterm = nil, cnx: Jamf.cnx, **ident_and_val) raw_data(searchterm, cnx: cnx, **ident_and_val)&.dig(:id) end |
#which_api ⇒ Object Originally defined in module JPAPIResource::ClassMethods
Indicate that this class comes from the Jamf Pro API. The same method exists in APIObject to indicate coming from Classic