Class: Jamf::CollectionResource Abstract
- Inherits:
-
Resource
- Object
- JSONObject
- Resource
- Jamf::CollectionResource
- Extended by:
- BaseClass, Filterable, Pageable, Sortable
- Includes:
- Comparable
- Defined in:
- lib/jamf/api/base_classes/collection_resource.rb
Overview
A Collection Resource in Jamf Pro
See Resource for general info about API resources.
Collection resources have more than one resource within them, and those can (usually) be created and deleted as well as fetched and updated. The entire collection (or a part of it) can also be retrieved as an Array. When the whole collection is retrieved, the result may be cached for future use.
# Subclassing
## Creatability, & Deletability
Sometimes the API doesn't support creation of new members of the collection. If that's the case, just extend the subclass with Jamf::UnCreatable and the '.create' class method will raise an error.
Similarly for deletion of members: if the API doesn't have a way to delete them, extend the subclass with Jamf::UnDeletable
See also Jamf::JSONObject, which talks about extending subclasses with Jamf::Immutable
## Bulk Deletion
Some collection resources have a resource for bulk deletion, passing in a JSON array of ids to delete.
If so, just define a BULK_DELETE_RSRC, and the .delete class method will use it, rather than making multiple calls to delete individual items. See Jamf::Category::BULK_DELETE_RSRC for an example
Direct Known Subclasses
Building, Category, Department, DeviceEnrollment, InventoryPreloadRecord, Prestage, Script
Class Method Summary collapse
-
.all(sort: nil, filter: nil, paged: nil, page_size: nil, refresh: false, instantiate: false, cnx: Jamf.cnx) ⇒ Array<Hash, Jamf::CollectionResource>
Get all instances of a CollectionResource, possibly limited by a filter.
-
.all_ids(refresh = false, cnx: Jamf.cnx) ⇒ Array<Integer>
An array of the ids for all collection members.
-
.allocate(*args, &block) ⇒ Object
extended
from BaseClass
Can't allocate if base class.
- .base_class? ⇒ Boolean extended from BaseClass
-
.collection_count(rsrc, cnx: Jamf.cnx) ⇒ Integer
extended
from Pageable
How many items exist in this collection?.
- .count(cnx: Jamf.cnx) ⇒ Object
-
.creatable? ⇒ Boolean
Bu default, subclasses are creatable, i.e.
-
.create(**params) ⇒ Object
Make a new thing to be added to the API.
-
.deletable? ⇒ Boolean
By default, CollectionResource subclass 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.
-
.fetch(random = nil, cnx: Jamf.cnx, **ident_and_val) ⇒ CollectionResource
Retrieve a member of a CollectionResource from the API.
-
.fetch_collection_page(rsrc, page, page_size, sort, filter, cnx: Jamf.cnx) ⇒ Array<Object>
extended
from Pageable
Get a specific page of a paged collection request, possibly sorted & filtered.
-
.identifiers ⇒ Array<Symbol>
The attribute names that are marked as identifiers.
-
.map_all(ident, to:, cnx: Jamf.cnx, refresh: false) ⇒ Hash {Symbol: Object}
A Hash of all members of this collection where the keys are some identifier and values are any other attribute.
-
.new(*args, &block) ⇒ Object
extended
from BaseClass
Can't instantiate if base_class.
-
.next_page_of_all ⇒ Object
Fetch the next page of a paged .all request.
-
.raw_data(value = nil, cnx: Jamf.cnx, **ident_and_val) ⇒ 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.
-
.stop_if_base_class(action = DEFAULT_ACTION) ⇒ Object
extended
from BaseClass
raise an exception if this class is a base class.
-
.valid_id(value = nil, cnx: Jamf.cnx, **ident_and_val) ⇒ String?
Look up the valid ID for any arbitrary identifier.
Instance Method Summary collapse
-
#<=>(other) ⇒ Object
Two collection resource objects are the same if their id's are the same.
- #delete ⇒ Object
-
#exist? ⇒ Boolean
Instance Methods.
- #rsrc_path ⇒ Object
Constructor Details
This class inherits a constructor from Jamf::JSONObject
Class Method Details
.all(sort: nil, filter: nil, paged: nil, page_size: nil, refresh: false, instantiate: false, cnx: Jamf.cnx) ⇒ Array<Hash, Jamf::CollectionResource>
Get all instances of a CollectionResource, possibly limited by a filter.
When called without specifying paged:, sort:, or filter: (see below) this method will return a single Array of all items of its CollectionResouce subclass, in the server's default sort order. This full list is cached for future use (see Caching, below)
However, the Array can be sorted by the server, filtered to contain only matching objects, or 'paged', i.e. retrieved in successive Arrays of a certain size.
Sorting, filtering, and paging can all be used at the same time.
#### Server-side Sorting
Sorting criteria can be provided in the String 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
#### Filtering
Some CollectionResouces support RSQL filters to limit which objects are returned. These filters can be applied using the filter: parameter, in which case this `all` method will return `all that match the filter`.
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.
#### Paging
To reduce server load and local memory usage, you can request the results in 'pages', i.e. successivly retrieved Arrays, using the paged: and page_size: parameters.
When paged: is truthy, the call to `all` returns the first group of objects containing however many are specified by page_size: The default page size is 100, the minimum is 1, and the maximum is 2000.
Once you have made a paged call to `all`, you must use the `next_page_of_all` method to get the next Array of objects. That method merely repeats the last request made by `all` after incrementing the page number by 1. When `next_page_of_all` returns an empty array, you have retrieved all availalble objects.
`next_page_of_all` always reflects the last paged call to `all`. Any subsequent paged call to `all` will reset the paging process for that collection class, and any unfinished previous paged calls to `all` will be forgotten
#### 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 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's far more efficient to do so.
However sometimes you really need the fully instantiated ruby objects for all of them - 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
When called without specifying paged:, sort:, or filter: this method will return a single Array of all items of its CollectionResouce subclass, in the server's default sort order.
This Array is cached in ruby-jss, and future calls to this method without those parameters will return the cached Array. Use `refresh: true` to re-request that Array from the server. Note that the cache is of the raw JSON Hash data. Using 'instantiate:' will still be slower as each item in the cache is instantiated. See 'Instantiation' above.
Some other class methods, e.g. .all_names, will generate or use this cached Array to derive their values.
If any of the parameters paged:, sort:, or filter: are used, an API request is made every time, and no caches are used or stored.
216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 |
# File 'lib/jamf/api/base_classes/collection_resource.rb', line 216 def self.all(sort: nil, filter: nil, paged: nil, page_size: nil, refresh: false, instantiate: false, cnx: Jamf.cnx) stop_if_base_class # use the cache if not paging, filtering or sorting return cached_all(refresh, instantiate, cnx) if !paged && !sort && !filter # we are sorting, filtering or paging sort = parse_collection_sort(sort) filter = parse_collection_filter(filter) result = if paged first_collection_page(rsrc_path, page_size, sort, filter, cnx) else fetch_all_collection_pages(rsrc_path, sort, filter, cnx) end instantiate ? result.map { |m| new m } : result end |
.all_ids(refresh = false, cnx: Jamf.cnx) ⇒ Array<Integer>
An array of the ids for all collection members. According to the specs ALL collection resources must have an ID, which is used in the resource path.
NOTE: This method uses the cached version of .all
277 278 279 |
# File 'lib/jamf/api/base_classes/collection_resource.rb', line 277 def self.all_ids(refresh = false, cnx: Jamf.cnx) all(refresh: refresh, cnx: cnx).map { |m| m[:id] } end |
.allocate(*args, &block) ⇒ Object Originally defined in module BaseClass
Can't allocate if base class
.base_class? ⇒ Boolean Originally defined in module BaseClass
.collection_count(rsrc, cnx: Jamf.cnx) ⇒ Integer Originally defined in module Pageable
Returns How many items exist in this collection?.
.count(cnx: Jamf.cnx) ⇒ Object
82 83 84 |
# File 'lib/jamf/api/base_classes/collection_resource.rb', line 82 def self.count(cnx: Jamf.cnx) collection_count(rsrc_path, cnx: Jamf.cnx) end |
.creatable? ⇒ Boolean
Bu default, subclasses 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
456 457 458 |
# File 'lib/jamf/api/base_classes/collection_resource.rb', line 456 def self.creatable? true end |
.create(**params) ⇒ Object
Make a new thing to be added to the API
461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 |
# File 'lib/jamf/api/base_classes/collection_resource.rb', line 461 def self.create(**params) stop_if_base_class raise Jamf::UnsupportedError, "#{self}'s are not currently creatable via the API" unless creatable? # Which connection to use cnx = params.delete :cnx cnx ||= Jamf.cnx params.delete :id # no such animal when .creating params.keys.each do |param| raise ArgumentError, "Unknown parameter: #{param}" unless self::OBJECT_MODEL.key? param if params[param].is_a? Array params[param].map! { |val| validate_attr param, val, cnx: cnx } else params[param] = validate_attr param, params[param], cnx: cnx end end params[:creating_from_create] = true new params, cnx: cnx end |
.deletable? ⇒ Boolean
By default, CollectionResource subclass instances are deletable. If not, just extend the subclass with Jamf::UnDeletable, and this will return false, and .delete & #delete will raise errors
517 518 519 |
# File 'lib/jamf/api/base_classes/collection_resource.rb', line 517 def self.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
530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 |
# File 'lib/jamf/api/base_classes/collection_resource.rb', line 530 def self.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 ancestors.include? Jamf::BulkDeletable errs = [] ids.each do |id_to_delete| begin cnx.delete "#{rsrc_path}/#{id_to_delete}" rescue Jamf::Connection::APIError => e raise e unless e.httpStatus == 404 errs += e.errors end # begin end # ids.each errs end |
.fetch(random = nil, 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 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'`
500 501 502 503 504 505 506 507 508 509 510 511 512 |
# File 'lib/jamf/api/base_classes/collection_resource.rb', line 500 def self.fetch(random = nil, cnx: Jamf.cnx, **ident_and_val) stop_if_base_class ident, value = ident_and_val.first data = if random all.sample elsif ident && value raw_data(cnx: cnx, **ident_and_val) end raise Jamf::NoSuchItemError, "No matching #{self}" unless data new data, cnx: cnx end |
.fetch_collection_page(rsrc, page, page_size, sort, filter, cnx: Jamf.cnx) ⇒ Array<Object> Originally defined in module Pageable
Get a specific page of a paged collection request, possibly sorted & filtered.
.identifiers ⇒ Array<Symbol>
Returns the attribute names that are marked as identifiers.
78 79 80 |
# File 'lib/jamf/api/base_classes/collection_resource.rb', line 78 def self.identifiers self::OBJECT_MODEL.select { |_attr, deets| deets[:identifier] }.keys end |
.map_all(ident, to:, cnx: Jamf.cnx, refresh: false) ⇒ Hash {Symbol: Object}
A Hash of all members of this collection where the keys are some identifier and values are any other attribute.
NOTE: This method uses the cached version of .all
298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 |
# File 'lib/jamf/api/base_classes/collection_resource.rb', line 298 def self.map_all(ident, to:, cnx: Jamf.cnx, refresh: false) real_ident = attr_key_for_alias ident raise Jamf::InvalidDataError, "No identifier #{ident} for class #{self}" unless identifiers.include? real_ident real_to = attr_key_for_alias to raise Jamf::NoSuchItemError, "No attribute #{to} for class #{self}" unless self::OBJECT_MODEL.key? real_to list = all refresh: refresh, cnx: cnx to_class = self::OBJECT_MODEL[real_to][:class] mapped = list.map do |i| [ i[real_ident], to_class.is_a?(Symbol) ? i[real_to] : to_class.new(i[real_to]) ] end # do i mapped.to_h end |
.next_page_of_all ⇒ Object
Fetch the next page of a paged .all request. See Pagable.next_collection_page
261 262 263 |
# File 'lib/jamf/api/base_classes/collection_resource.rb', line 261 def self.next_page_of_all next_collection_page end |
.raw_data(value = nil, cnx: Jamf.cnx, **ident_and_val) ⇒ 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
353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 |
# File 'lib/jamf/api/base_classes/collection_resource.rb', line 353 def self.raw_data(value = nil, cnx: Jamf.cnx, **ident_and_val) stop_if_base_class # given a value with no ident key return raw_data_by_value_only(value, cnx: Jamf.cnx) if value # if we're here, we should know our ident key and value ident, value = ident_and_val.first 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 |
.stop_if_base_class(action = DEFAULT_ACTION) ⇒ Object Originally defined in module BaseClass
raise an exception if this class is a base class
.valid_id(value = 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)
445 446 447 |
# File 'lib/jamf/api/base_classes/collection_resource.rb', line 445 def self.valid_id(value = nil, cnx: Jamf.cnx, **ident_and_val) raw_data(value, cnx: cnx, **ident_and_val)&.dig(:id) end |
Instance Method Details
#<=>(other) ⇒ Object
Two collection resource objects are the same if their id's are the same
597 598 599 |
# File 'lib/jamf/api/base_classes/collection_resource.rb', line 597 def <=>(other) id <=> other.id end |
#delete ⇒ Object
590 591 592 593 594 |
# File 'lib/jamf/api/base_classes/collection_resource.rb', line 590 def delete raise Jamf::UnsupportedError, "Deleting #{self} objects is not currently supported" unless self.class.deletable? @cnx.delete rsrc_path end |
#exist? ⇒ Boolean
Instance Methods
580 581 582 |
# File 'lib/jamf/api/base_classes/collection_resource.rb', line 580 def exist? !@id.nil? end |
#rsrc_path ⇒ Object
584 585 586 587 588 |
# File 'lib/jamf/api/base_classes/collection_resource.rb', line 584 def rsrc_path return unless exist? "#{self.class.rsrc_path}/#{@id}" end |