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::JamfProAPIError::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.
584 585 586 587 588 589 590 591 592 593 594 595 596 597 |
# File 'lib/jamf/api/jamf_pro/mixins/collection_resource.rb', line 584 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 elsif method.to_s == 'all_names' && defined?(self::OBJECT_NAME_ATTR) define_singleton_method(:all_names) do |_refresh = nil, cnx: Jamf.cnx, cached_list: nil| send "all_#{self::OBJECT_NAME_ATTR}s", *args end 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.
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
#### 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.
234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 |
# File 'lib/jamf/api/jamf_pro/mixins/collection_resource.rb', line 234 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.
608 609 610 611 612 613 614 615 616 617 |
# File 'lib/jamf/api/jamf_pro/mixins/collection_resource.rb', line 608 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
549 550 551 |
# File 'lib/jamf/api/jamf_pro/mixins/collection_resource.rb', line 549 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
479 480 481 |
# File 'lib/jamf/api/jamf_pro/mixins/collection_resource.rb', line 479 def creatable? true end |
#create(**params) ⇒ Object
Make a new thing to be added to the API
485 486 487 488 489 490 491 492 493 494 495 496 497 |
# File 'lib/jamf/api/jamf_pro/mixins/collection_resource.rb', line 485 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
541 542 543 |
# File 'lib/jamf/api/jamf_pro/mixins/collection_resource.rb', line 541 def deletable? true end |
#delete(*ids, cnx: Jamf.cnx) ⇒ Array<Jamf::Connection::JamfProAPIError::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
565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 |
# File 'lib/jamf/api/jamf_pro/mixins/collection_resource.rb', line 565 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 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.
128 129 130 |
# File 'lib/jamf/api/jamf_pro/mixins/collection_resource.rb', line 128 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’‘
515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 |
# File 'lib/jamf/api/jamf_pro/mixins/collection_resource.rb', line 515 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
545 546 547 |
# File 'lib/jamf/api/jamf_pro/mixins/collection_resource.rb', line 545 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.
87 88 89 |
# File 'lib/jamf/api/jamf_pro/mixins/collection_resource.rb', line 87 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.
135 136 137 138 139 140 141 |
# File 'lib/jamf/api/jamf_pro/mixins/collection_resource.rb', line 135 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.
297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 |
# File 'lib/jamf/api/jamf_pro/mixins/collection_resource.rb', line 297 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 a method in NEW_CALLERS, or the data must include :instantiate_me
WARNING: do not abuse :instantiate_me, it exists so we don’t constantly have to update NEW_CALLERS
#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
264 265 266 267 268 269 270 271 272 273 274 275 276 277 |
# File 'lib/jamf/api/jamf_pro/mixins/collection_resource.rb', line 264 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 instantiate &&= self 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.
110 111 112 |
# File 'lib/jamf/api/jamf_pro/mixins/collection_resource.rb', line 110 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.
119 120 121 |
# File 'lib/jamf/api/jamf_pro/mixins/collection_resource.rb', line 119 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.
98 99 100 |
# File 'lib/jamf/api/jamf_pro/mixins/collection_resource.rb', line 98 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
358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 |
# File 'lib/jamf/api/jamf_pro/mixins/collection_resource.rb', line 358 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 # if the ident is :name, and there's a constant for the name attr, use that ident = self::OBJECT_NAME_ATTR if defined?(self::OBJECT_NAME_ATTR) && ident == :name 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
392 393 394 395 396 397 398 |
# File 'lib/jamf/api/jamf_pro/mixins/collection_resource.rb', line 392 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
404 405 406 407 408 409 410 411 412 413 414 415 416 |
# File 'lib/jamf/api/jamf_pro/mixins/collection_resource.rb', line 404 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 # which can be slow if there are lots of objects. 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
375 376 377 378 379 380 381 382 383 384 385 386 387 |
# File 'lib/jamf/api/jamf_pro/mixins/collection_resource.rb', line 375 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!
601 602 603 |
# File 'lib/jamf/api/jamf_pro/mixins/collection_resource.rb', line 601 def respond_to_missing?(method, *) available_list_methods.key?(method.to_s) || method.to_s == 'all_names' || 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)
457 458 459 460 461 462 463 464 465 466 467 468 |
# File 'lib/jamf/api/jamf_pro/mixins/collection_resource.rb', line 457 def valid_id(searchterm = nil, cnx: Jamf.cnx, **ident_and_val) data = if ident_and_val.empty? raw_data(searchterm, cnx: cnx) else ident = ident_and_val.keys.first value = ident_and_val.values.first raw_data(cnx: cnx, ident: ident, value: value) end data&.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