ruby-jss 1.4.1 → 1.5.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of ruby-jss might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGES.md +38 -0
- data/lib/jamf.rb +10 -3
- data/lib/jamf/api/abstract_classes/collection_resource.rb +329 -150
- data/lib/jamf/api/abstract_classes/generic_reference.rb +9 -1
- data/lib/jamf/api/abstract_classes/json_object.rb +106 -82
- data/lib/jamf/api/abstract_classes/prestage.rb +54 -29
- data/lib/jamf/api/abstract_classes/prestage_skip_setup_items.rb +21 -0
- data/lib/jamf/api/abstract_classes/resource.rb +4 -4
- data/lib/jamf/api/abstract_classes/singleton_resource.rb +1 -1
- data/lib/jamf/api/connection.rb +13 -9
- data/lib/jamf/api/connection/api_error.rb +8 -8
- data/lib/jamf/api/connection/token.rb +16 -15
- data/lib/jamf/api/json_objects/computer_prestage_skip_setup_items.rb +14 -1
- data/lib/jamf/api/json_objects/device_enrollment_device.rb +14 -7
- data/lib/jamf/api/json_objects/device_enrollment_device_sync_state.rb +81 -0
- data/lib/jamf/api/json_objects/locale.rb +59 -0
- data/lib/jamf/api/json_objects/md_prestage_skip_setup_items.rb +50 -1
- data/lib/jamf/api/json_objects/prestage_location.rb +3 -3
- data/lib/jamf/api/json_objects/prestage_purchasing_data.rb +7 -7
- data/lib/jamf/api/json_objects/prestage_scope.rb +1 -1
- data/lib/jamf/api/{resources/collection_resources → json_objects}/time_zone.rb +9 -23
- data/lib/jamf/api/mixins/bulk_deletable.rb +27 -6
- data/lib/jamf/api/mixins/change_log.rb +201 -51
- data/lib/jamf/api/mixins/filterable.rb +51 -0
- data/lib/jamf/api/mixins/pageable.rb +208 -0
- data/lib/jamf/api/mixins/sortable.rb +59 -0
- data/lib/jamf/api/resources/collection_resources/building.rb +19 -8
- data/lib/jamf/api/resources/collection_resources/category.rb +5 -3
- data/lib/jamf/api/resources/collection_resources/computer_prestage.rb +11 -4
- data/lib/jamf/api/resources/collection_resources/device_enrollment.rb +10 -10
- data/lib/jamf/api/resources/collection_resources/inventory_preload_record.rb +11 -3
- data/lib/jamf/api/resources/collection_resources/mobile_device_prestage.rb +24 -22
- data/lib/jamf/api/resources/collection_resources/script.rb +61 -25
- data/lib/jamf/api/resources/singleton_resources/app_store_country_codes.rb +15 -5
- data/lib/jamf/api/resources/singleton_resources/client_checkin_settings.rb +14 -14
- data/lib/jamf/api/resources/singleton_resources/locales.rb +155 -0
- data/lib/jamf/api/resources/singleton_resources/time_zones.rb +213 -0
- data/lib/jamf/validate.rb +63 -24
- data/lib/jamf/version.rb +1 -1
- data/lib/jss.rb +2 -1
- data/lib/jss/api_connection.rb +110 -370
- data/lib/jss/api_object.rb +3 -19
- data/lib/jss/api_object/categorizable.rb +1 -1
- data/lib/jss/api_object/configuration_profile.rb +34 -3
- data/lib/jss/api_object/directory_binding_type.rb +66 -60
- data/lib/jss/api_object/directory_binding_type/active_directory.rb +71 -34
- data/lib/jss/api_object/directory_binding_type/admitmac.rb +536 -467
- data/lib/jss/api_object/directory_binding_type/centrify.rb +21 -7
- data/lib/jss/api_object/directory_binding_type/open_directory.rb +4 -4
- data/lib/jss/api_object/distribution_point.rb +2 -2
- data/lib/jss/api_object/dock_item.rb +102 -96
- data/lib/jss/api_object/extendable.rb +1 -1
- data/lib/jss/api_object/group.rb +33 -2
- data/lib/jss/api_object/network_segment.rb +43 -12
- data/lib/jss/api_object/patch_source.rb +10 -9
- data/lib/jss/api_object/printer.rb +10 -4
- data/lib/jss/api_object/scopable.rb +10 -15
- data/lib/jss/exceptions.rb +3 -0
- data/lib/jss/server.rb +15 -0
- data/lib/jss/version.rb +1 -1
- metadata +37 -22
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7c229fe64d5af063db9f075fd9345d34447160817a125fb147f72e4603d56d1c
|
4
|
+
data.tar.gz: a4642b384ae4bd853563928d6c399597404935c5cdce329a3f485e5ad2d92095
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dbabf94f300745834e65c574cc1b9918b03540d81f9c54a004803913452147f63e7eb0df20694ea092c05b31c0700144a55df99bc57d4f9fbbaf3c67dab1d034
|
7
|
+
data.tar.gz: 6aa566e804422b49bdcd258ae08386a080a567e14b1cd47e936c8098a307ca6529ec2da4963f6f801d2586a04cd9edb570e6f101e865d19861213b93ea062128
|
data/CHANGES.md
CHANGED
@@ -4,6 +4,44 @@ All notable changes to this project will be documented in this file.
|
|
4
4
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
5
5
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
6
6
|
|
7
|
+
## \[1.5.1] - 2020-11-16
|
8
|
+
|
9
|
+
IMPORTANT: New minimum require ruby version is 2.3.0
|
10
|
+
|
11
|
+
Big thanks to @cybertunnel for many enhancements and fixes.
|
12
|
+
|
13
|
+
### Added
|
14
|
+
|
15
|
+
- The .all method for subclasses of Jamf::CollectionResource now fully supports server-side paging, sorting and filtering (for endpoints that support RSQL filters). See the docs/comments for Jamf::CollectionResource.all for details
|
16
|
+
|
17
|
+
- JSS::ConfigurationProfile subclasses now have a #payload_content=(new_content) method, which takes an Array of Hashes to replace the PayloadContent of the Payload of the profile. All converstion to an XML plist (which is then embedded into the API XML) is handled automatically. WARNING: This is experimental and can easily break your profile if you aren't careful.
|
18
|
+
|
19
|
+
- JSS::Server#update_activation_code method was added
|
20
|
+
|
21
|
+
- Group#set_static and #set_smart can convert smart groups to static and static to smart
|
22
|
+
|
23
|
+
### Changed
|
24
|
+
|
25
|
+
- Minimum required ruby version is 2.3.0
|
26
|
+
|
27
|
+
- The JSS Module now uses the faraday gem, rather than rest-client, as the underlying REST/HTTP engine for communicating with the Classic API. This brings it in line with the Jamf module which has always used faraday for connecting to the Jamf Pro API. Faraday has fewer dependencies, none of which need to be compiled. This means that installing ruby-jss on a Mac no longer requires the XCode command-line tools.
|
28
|
+
|
29
|
+
- The Jamf module, for accessing the Jamf Pro API, now requires Jamf Pro 10.25 or higher. While still in 'beta', the Jamf Pro API is becoming more stable and in compliance with standards. The Jamf module continues to be updated to work with the modernized endpoints of the JP API. Some related changes:
|
30
|
+
- The ids of JP API collection objects are Strings containing Integers.
|
31
|
+
- Boolean property names no longer start with 'is', tho aliases ending with '?' are still automatically created.
|
32
|
+
|
33
|
+
- Removed dependency on net-ldap, which hasn't been used in a while
|
34
|
+
|
35
|
+
- Removed the redundant JSS::APIConnection instance methods that were just wrappers for various APIObject subclass Class methods, e.g. `JSS.api.valid_id :computers, 'compName'`. Please use the class method directly, e.g. `JSS::Computer.valid_id 'compName'`
|
36
|
+
|
37
|
+
### Fixed
|
38
|
+
|
39
|
+
- PatchSource.fetch was totally broken, now fixed
|
40
|
+
|
41
|
+
- Category object's parse_category not properly referencing API object during execution
|
42
|
+
|
43
|
+
- Many small bugs and typos.
|
44
|
+
|
7
45
|
## \[1.4.1] - 2020-10-01
|
8
46
|
|
9
47
|
### Added
|
data/lib/jamf.rb
CHANGED
@@ -41,9 +41,7 @@ require 'shellwords'
|
|
41
41
|
require 'digest'
|
42
42
|
require 'open3'
|
43
43
|
|
44
|
-
|
45
44
|
### Gems
|
46
|
-
require 'rest-client'
|
47
45
|
require 'plist'
|
48
46
|
|
49
47
|
# Used, among other places, in the Connection::APIError class
|
@@ -113,6 +111,10 @@ module Jamf
|
|
113
111
|
autoload :Immutable, 'jamf/api/mixins/immutable'
|
114
112
|
autoload :UnDeletable, 'jamf/api/mixins/undeletable'
|
115
113
|
autoload :Abstract, 'jamf/api/mixins/abstract'
|
114
|
+
autoload :Pageable, 'jamf/api/mixins/pageable'
|
115
|
+
autoload :Filterable, 'jamf/api/mixins/filterable'
|
116
|
+
autoload :Sortable, 'jamf/api/mixins/sortable'
|
117
|
+
autoload :BulkDeletable, 'jamf/api/mixins/bulk_deletable'
|
116
118
|
|
117
119
|
# Utility modules
|
118
120
|
autoload :Validate, 'jamf/validate'
|
@@ -126,6 +128,7 @@ module Jamf
|
|
126
128
|
autoload :Country, 'jamf/api/json_objects/country'
|
127
129
|
autoload :Criterion, 'jamf/api/json_objects/criterion'
|
128
130
|
autoload :DeviceEnrollmentDevice, 'jamf/api/json_objects/device_enrollment_device'
|
131
|
+
autoload :DeviceEnrollmentDeviceSyncState, 'jamf/api/json_objects/device_enrollment_device_sync_state'
|
129
132
|
autoload :DeviceEnrollmentSyncStatus, 'jamf/api/json_objects/device_enrollment_sync_status'
|
130
133
|
autoload :ExtensionAttributeValue, 'jamf/api/json_objects/extension_attribute_value'
|
131
134
|
autoload :InstalledApplication, 'jamf/api/json_objects/installed_application'
|
@@ -135,6 +138,7 @@ module Jamf
|
|
135
138
|
autoload :InstalledProvisioningProfile, 'jamf/api/json_objects/installed_provisioning_profile'
|
136
139
|
autoload :InventoryPreloadExtensionAttribute, 'jamf/api/json_objects/inventory_preload_extension_attribute'
|
137
140
|
autoload :IosDetails, 'jamf/api/json_objects/ios_details'
|
141
|
+
autoload :Locale, 'jamf/api/json_objects/locale'
|
138
142
|
autoload :Location, 'jamf/api/json_objects/location'
|
139
143
|
autoload :PrestageLocation, 'jamf/api/json_objects/prestage_location'
|
140
144
|
autoload :PrestageSyncStatus, 'jamf/api/json_objects/prestage_sync_status'
|
@@ -147,16 +151,20 @@ module Jamf
|
|
147
151
|
autoload :PrestagePurchasingData, 'jamf/api/json_objects/prestage_purchasing_data'
|
148
152
|
autoload :PrestageScope, 'jamf/api/json_objects/prestage_scope'
|
149
153
|
autoload :PrestageAssignment, 'jamf/api/json_objects/prestage_assignment'
|
154
|
+
autoload :TimeZone, 'jamf/api/json_objects/time_zone'
|
150
155
|
|
151
156
|
# Subclasses of SingletonResource
|
152
157
|
autoload :ClientCheckInSettings, 'jamf/api/resources/singleton_resources/client_checkin_settings'
|
153
158
|
autoload :ReEnrollmentSettings, 'jamf/api/resources/singleton_resources/reenrollment_settings'
|
154
159
|
autoload :AppStoreCountryCodes, 'jamf/api/resources/singleton_resources/app_store_country_codes'
|
160
|
+
autoload :TimeZones, 'jamf/api/resources/singleton_resources/time_zones'
|
161
|
+
autoload :Locales, 'jamf/api/resources/singleton_resources/locales'
|
155
162
|
|
156
163
|
# Subclasses of CollectionResource
|
157
164
|
autoload :AdvancedMobileDeviceSearch, 'jamf/api/resources/collection_resources/advanced_mobile_device_search'
|
158
165
|
autoload :AdvancedUserSearch, 'jamf/api/resources/collection_resources/advanced_user_search'
|
159
166
|
autoload :Attachment, 'jamf/api/resources/collection_resources/attachment'
|
167
|
+
autoload :Category, 'jamf/api/resources/collection_resources/category'
|
160
168
|
autoload :Building, 'jamf/api/resources/collection_resources/building'
|
161
169
|
autoload :Computer, 'jamf/api/resources/collection_resources/computer'
|
162
170
|
autoload :ComputerPrestage, 'jamf/api/resources/collection_resources/computer_prestage'
|
@@ -168,7 +176,6 @@ module Jamf
|
|
168
176
|
autoload :MobileDevicePrestage, 'jamf/api/resources/collection_resources/mobile_device_prestage'
|
169
177
|
autoload :Site, 'jamf/api/resources/collection_resources/site'
|
170
178
|
autoload :Script, 'jamf/api/resources/collection_resources/script'
|
171
|
-
autoload :TimeZone, 'jamf/api/resources/collection_resources/time_zone'
|
172
179
|
|
173
180
|
# other classes used as attributes inside the resource classes
|
174
181
|
autoload :IPAddress, 'jamf/api/attribute_classes/ip_address'
|
@@ -32,8 +32,9 @@ module Jamf
|
|
32
32
|
#
|
33
33
|
# Collection resources have more than one resource within them, and those
|
34
34
|
# can (usually) be created and deleted as well as fetched and updated.
|
35
|
-
# The entire collection (or a part of it) can also be
|
36
|
-
# When the whole collection is
|
35
|
+
# The entire collection (or a part of it) can also be retrieved as an Array.
|
36
|
+
# When the whole collection is retrieved, the result may be cached for future
|
37
|
+
# use.
|
37
38
|
#
|
38
39
|
# # Subclassing
|
39
40
|
#
|
@@ -63,6 +64,10 @@ module Jamf
|
|
63
64
|
class CollectionResource < Jamf::Resource
|
64
65
|
|
65
66
|
extend Jamf::Abstract
|
67
|
+
extend Jamf::Pageable
|
68
|
+
extend Jamf::Sortable
|
69
|
+
extend Jamf::Filterable
|
70
|
+
|
66
71
|
include Comparable
|
67
72
|
|
68
73
|
# Public Class Methods
|
@@ -74,63 +79,195 @@ module Jamf
|
|
74
79
|
self::OBJECT_MODEL.select { |_attr, deets| deets[:identifier] }.keys
|
75
80
|
end
|
76
81
|
|
77
|
-
|
78
|
-
|
79
|
-
|
82
|
+
def self.count(cnx: Jamf.cnx)
|
83
|
+
collection_count(rsrc_path, cnx: Jamf.cnx)
|
84
|
+
end
|
85
|
+
|
86
|
+
|
87
|
+
# Get all instances of a CollectionResource, possibly limited by a filter.
|
88
|
+
#
|
89
|
+
# When called without specifying paged:, sort:, or filter: (see below)
|
90
|
+
# this method will return a single Array of all items of its
|
91
|
+
# CollectionResouce subclass, in the server's default sort order. This
|
92
|
+
# full list is cached for future use (see Caching, below)
|
93
|
+
#
|
94
|
+
# However, the Array can be sorted by the server, filtered to contain only
|
95
|
+
# matching objects, or 'paged', i.e. retrieved in successive Arrays of a
|
96
|
+
# certain size.
|
97
|
+
#
|
98
|
+
# Sorting, filtering, and paging can all be used at the same time.
|
99
|
+
#
|
100
|
+
# #### Server-side Sorting
|
101
|
+
#
|
102
|
+
# Sorting criteria can be provided in the String format 'property:direction',
|
103
|
+
# where direction is 'asc' or 'desc' E.g.
|
104
|
+
# "username:asc"
|
105
|
+
#
|
106
|
+
# Multiple properties are supported, either as separate strings in an Array,
|
107
|
+
# or a single string, comma separated. E.g.
|
108
|
+
#
|
109
|
+
# "username:asc,timestamp:desc"
|
110
|
+
# is the same as
|
111
|
+
# ["username:asc", "timestamp:desc"]
|
112
|
+
#
|
113
|
+
# which will sort by username alphabetically, and within each username,
|
114
|
+
# sort by timestamp newest first.
|
115
|
+
#
|
116
|
+
# Please see the JamfPro API documentation for the resource for details
|
117
|
+
# about available sorting properties and default sorting criteria
|
118
|
+
#
|
119
|
+
# #### Filtering
|
120
|
+
#
|
121
|
+
# Some CollectionResouces support RSQL filters to limit which objects
|
122
|
+
# are returned. These filters can be applied using the filter: parameter,
|
123
|
+
# in which case this `all` method will return `all that match the filter`.
|
124
|
+
#
|
125
|
+
# If the resource doesn't support filters, the filter parameter is ignored.
|
126
|
+
#
|
127
|
+
# Please see the JamfPro API documentation for the resource to see if
|
128
|
+
# filters are supported, and a list of available fields.
|
129
|
+
#
|
130
|
+
# #### Paging
|
131
|
+
#
|
132
|
+
# To reduce server load and local memory usage, you can request the results
|
133
|
+
# in 'pages', i.e. successivly retrieved Arrays, using the paged: and page_size:
|
134
|
+
# parameters.
|
135
|
+
#
|
136
|
+
# When paged: is truthy, the call to `all` returns the first group of objects
|
137
|
+
# containing however many are specified by page_size: The default page size
|
138
|
+
# is 100, the minimum is 1, and the maximum is 2000.
|
139
|
+
#
|
140
|
+
# Once you have made a paged call to `all`, you must use the `next_page_of_all`
|
141
|
+
# method to get the next Array of objects. That method merely repeats the last
|
142
|
+
# request made by `all` after incrementing the page number by 1.
|
143
|
+
# When `next_page_of_all` returns an empty array, you have retrieved all
|
144
|
+
# availalble objects.
|
145
|
+
#
|
146
|
+
# `next_page_of_all` always reflects the last _paged_ call to `all`. Any
|
147
|
+
# subsequent paged call to `all` will reset the paging process for that
|
148
|
+
# collection class, and any unfinished previous paged calls to `all` will
|
149
|
+
# be forgotten
|
150
|
+
#
|
151
|
+
# #### Instantiation
|
152
|
+
#
|
153
|
+
# All data from the API comes from the server in JSON format, mostly as
|
154
|
+
# JSON 'objects', which are the equivalent of ruby Hashes.
|
155
|
+
# When fetching an individual instance of an object from the API, ruby-jss
|
156
|
+
# uses the JSON Hash to create the ruby object, i.e. to 'instantiate' it as
|
157
|
+
# an instance of its class. Doing this for many objects can slow things down.
|
158
|
+
#
|
159
|
+
# Because of this, the 'all' method defaults to returning an Array of the
|
160
|
+
# minimally-processed JSON Hashes it gets from the API. If you can get your
|
161
|
+
# desired data from these Hashes, it's far more efficient to do so.
|
162
|
+
#
|
163
|
+
# However sometimes you really need the fully instantiated ruby objects for
|
164
|
+
# all of them - especially if you're using filters and not actually processing
|
165
|
+
# all items of the class. In such cases you can pass a truthy value to the
|
166
|
+
# instantiate: parameter, and the Array will contain fully instantiated
|
167
|
+
# ruby objects, not Hashes of API data.
|
80
168
|
#
|
81
|
-
#
|
82
|
-
# returned by GETing the resource .../settings/obj/building
|
169
|
+
# #### Caching
|
83
170
|
#
|
84
|
-
#
|
85
|
-
#
|
86
|
-
#
|
171
|
+
# When called without specifying paged:, sort:, or filter:
|
172
|
+
# this method will return a single Array of all items of its
|
173
|
+
# CollectionResouce subclass, in the server's default sort order.
|
87
174
|
#
|
88
|
-
#
|
175
|
+
# This Array is cached in ruby-jss, and future calls to this method without
|
176
|
+
# those parameters will return the cached Array. Use `refresh: true` to
|
177
|
+
# re-request that Array from the server. Note that the cache is of the raw
|
178
|
+
# JSON Hash data. Using 'instantiate:' will still be slower as each item in
|
179
|
+
# the cache is instantiated. See 'Instantiation' below.
|
89
180
|
#
|
90
|
-
#
|
181
|
+
# Some other methods, e.g. .all_names, will generate or use this cached Array
|
182
|
+
# to derive their values.
|
91
183
|
#
|
92
|
-
#
|
93
|
-
#
|
184
|
+
# If any of the parameters paged:, sort:, or filter: are used, an API
|
185
|
+
# request is made every time, and no caches are used or stored.
|
94
186
|
#
|
95
|
-
|
96
|
-
# rather than the JSON Hashes from the API.
|
187
|
+
#######
|
97
188
|
#
|
98
|
-
# @
|
189
|
+
# @param sort [String, Array<String>] Server-side sorting criteria in the format:
|
190
|
+
# property:direction, where direction is 'asc' or 'desc'. Multiple
|
191
|
+
# properties are supported, either as separate strings in an Array, or
|
192
|
+
# a single string, comma separated.
|
99
193
|
#
|
100
|
-
|
194
|
+
# @param filter [String] An RSQL filter string. Not all collection resources
|
195
|
+
# currently support filters, and if they don't, this will be ignored.
|
196
|
+
#
|
197
|
+
# @param paged [Boolean] Defaults to false. Returns only the first page of
|
198
|
+
# `page_size` objects. Use {.next_page_of_all} to retrieve each successive
|
199
|
+
# page.
|
200
|
+
#
|
201
|
+
# @param page_size [Integer] How many items are returned per page? Minimum
|
202
|
+
# is 1, maximum is 2000, default is 100. Ignored unless paged: is truthy.
|
203
|
+
# Note: the final page may contain fewer items than the page_size
|
204
|
+
#
|
205
|
+
# @param refresh [Boolean] re-fetch and re-cache the full list of all instances.
|
206
|
+
# Ignored if paged:, page_size:, sort:, filter: or instantiate: are used.
|
207
|
+
#
|
208
|
+
# @param instantiate [Boolean] Defaults to false. Should the items in the
|
209
|
+
# returned Array(s) be ruby instances of the CollectionObject subclass, or
|
210
|
+
# plain Hashes of data as returned by the API?
|
211
|
+
#
|
212
|
+
# @param cnx [Jamf::Connection] The API connection to use, default: Jamf.cnx
|
213
|
+
#
|
214
|
+
# @return [Array<Hash, Jamf::CollectionResource>] The objects in the collection
|
215
|
+
#
|
216
|
+
def self.all(sort: nil, filter: nil, paged: nil, page_size: nil, refresh: false, instantiate: false, cnx: Jamf.cnx)
|
101
217
|
validate_not_abstract
|
102
|
-
cnx.collection_cache[self] = nil if refresh
|
103
|
-
if cnx.collection_cache[self]
|
104
|
-
return cnx.collection_cache[self] unless instantiate
|
105
218
|
|
106
|
-
|
107
|
-
|
219
|
+
# use the cache if not paging, filtering or sorting
|
220
|
+
return cached_all(refresh, instantiate, cnx) if !paged && !sort && !filter
|
108
221
|
|
109
|
-
#
|
110
|
-
|
111
|
-
|
112
|
-
page = 0
|
113
|
-
raw = cnx.get "#{rsrc_path}?page=#{page}&size=1000000&sort=id%3Aasc"
|
114
|
-
results = raw[:results]
|
115
|
-
|
116
|
-
until results.size >= raw[:totalCount]
|
117
|
-
page += 1
|
118
|
-
raw = cnx.get "#{rsrc_path}?page=#{page}&size=1000000&sort=id%3Aasc"
|
119
|
-
results += raw[:results]
|
120
|
-
end
|
222
|
+
# we are sorting, filtering or paging
|
223
|
+
sort = parse_collection_sort(sort)
|
224
|
+
filter = parse_collection_filter(filter)
|
121
225
|
|
226
|
+
result =
|
227
|
+
if paged
|
228
|
+
first_collection_page(rsrc_path, page_size, sort, filter, cnx)
|
229
|
+
else
|
230
|
+
fetch_all_collection_pages(rsrc_path, sort, filter, cnx)
|
231
|
+
end
|
232
|
+
instantiate ? result.map { |m| new m } : result
|
233
|
+
end
|
122
234
|
|
123
|
-
|
235
|
+
# PRIVATE
|
236
|
+
# return the cached/cachable version of .all, possibly instantiated
|
237
|
+
#
|
238
|
+
# @param refresh [Boolean] refetch the cache from the server?
|
239
|
+
#
|
240
|
+
# @param instantiate [Boolean] Return an array of instantiated objects, vs
|
241
|
+
# JSON hashes?
|
242
|
+
#
|
243
|
+
# @param cnx [Jamf::Connection] The Connection to use
|
244
|
+
#
|
245
|
+
# @return [Array<Hash,Object>] All the objects in the collection
|
246
|
+
#
|
247
|
+
def self.cached_all(refresh, instantiate, cnx)
|
248
|
+
cnx.collection_cache[self] = nil if refresh
|
249
|
+
unless cnx.collection_cache[self]
|
250
|
+
sort = nil
|
251
|
+
filter = nil
|
252
|
+
cnx.collection_cache[self] = fetch_all_collection_pages(rsrc_path, sort, filter, cnx)
|
253
|
+
end
|
254
|
+
instantiate ? cnx.collection_cache[self].map { |m| new m } : cnx.collection_cache[self]
|
255
|
+
end
|
256
|
+
private_class_method :cached_all
|
124
257
|
|
125
|
-
return cnx.collection_cache[self] unless instantiate
|
126
258
|
|
127
|
-
|
259
|
+
# Fetch the next page of a paged .all request. See
|
260
|
+
# {Jamf::Pagable.next_collection_page}
|
261
|
+
def self.next_page_of_all
|
262
|
+
next_collection_page
|
128
263
|
end
|
129
264
|
|
130
265
|
# An array of the ids for all collection members. According to the
|
131
266
|
# specs ALL collection resources must have an ID, which is used in the
|
132
267
|
# resource path.
|
133
268
|
#
|
269
|
+
# NOTE: This method uses the cached version of .all
|
270
|
+
#
|
134
271
|
# @param refresh (see .all)
|
135
272
|
#
|
136
273
|
# @param cnx (see .all)
|
@@ -138,14 +275,14 @@ module Jamf
|
|
138
275
|
# @return [Array<Integer>]
|
139
276
|
#
|
140
277
|
def self.all_ids(refresh = false, cnx: Jamf.cnx)
|
141
|
-
all(refresh, cnx: cnx).map { |m|
|
278
|
+
all(refresh: refresh, cnx: cnx).map { |m| m[:id] }
|
142
279
|
end
|
143
280
|
|
144
|
-
# rubocop:disable Naming/UncommunicativeMethodParamName
|
145
|
-
|
146
281
|
# A Hash of all members of this collection where the keys are some
|
147
282
|
# identifier and values are any other attribute.
|
148
283
|
#
|
284
|
+
# NOTE: This method uses the cached version of .all
|
285
|
+
#
|
149
286
|
# @param ident [Symbol] An identifier of this Class, used as the key
|
150
287
|
# for the mapping Hash. Aliases are acceptable, e.g. :sn for :serialNumber
|
151
288
|
#
|
@@ -166,76 +303,149 @@ module Jamf
|
|
166
303
|
real_to = attr_key_for_alias to
|
167
304
|
raise Jamf::NoSuchItemError, "No attribute #{to} for class #{self}" unless self::OBJECT_MODEL.key? real_to
|
168
305
|
|
169
|
-
|
170
|
-
|
171
|
-
list = all refresh, cnx: cnx
|
172
|
-
to_class = self::OBJECT_MODEL[to][:class]
|
306
|
+
list = all refresh: refresh, cnx: cnx
|
307
|
+
to_class = self::OBJECT_MODEL[real_to][:class]
|
173
308
|
mapped = list.map do |i|
|
174
309
|
[
|
175
|
-
i[
|
176
|
-
to_class.is_a?(Symbol) ? i[
|
310
|
+
i[real_ident],
|
311
|
+
to_class.is_a?(Symbol) ? i[real_to] : to_class.new(i[real_to])
|
177
312
|
]
|
178
313
|
end # do i
|
179
314
|
mapped.to_h
|
180
315
|
end
|
181
|
-
# rubocop:enable Naming/UncommunicativeMethodParamName
|
182
316
|
|
183
|
-
# Given
|
184
|
-
# object
|
317
|
+
# Given a key (identifier) and value for this collection, return the raw data
|
318
|
+
# Hash (the JSON object) for the matching API object or nil if there's no
|
319
|
+
# match for the given value.
|
185
320
|
#
|
186
|
-
#
|
321
|
+
# In general you should use this if the form:
|
187
322
|
#
|
188
|
-
#
|
189
|
-
# then you can specify the identifier for a faster search:
|
323
|
+
# raw_data identifier: value
|
190
324
|
#
|
191
|
-
#
|
325
|
+
# where identifier is one of the available identifiers for this class
|
326
|
+
# like id:, name:, serialNumber: etc.
|
192
327
|
#
|
193
|
-
#
|
194
|
-
#
|
328
|
+
# In the unlikely event that you dont know which identifier a value is for
|
329
|
+
# or want to be able to take any of them without specifying, then
|
330
|
+
# you can use
|
195
331
|
#
|
196
|
-
#
|
197
|
-
# valid_id 'SomeComputerName' # => Int or nil
|
332
|
+
# raw_data some_value
|
198
333
|
#
|
199
|
-
#
|
334
|
+
# If some_value is an integer or a string containing an integer, it
|
335
|
+
# is assumed to be an :id otherwise all the available identifers
|
336
|
+
# are searched, in the order you see them when you call <class>.identifiers
|
200
337
|
#
|
201
|
-
#
|
202
|
-
# mixin, which implements enpoints like 'POST /v1/search-mobile-devices'
|
203
|
-
# then use that before using the 'all' list.
|
338
|
+
# If no matching object is found, nil is returned.
|
204
339
|
#
|
205
|
-
#
|
340
|
+
# Everything except :id is treated as a case-insensitive String
|
206
341
|
#
|
207
|
-
# @param
|
342
|
+
# @param value [String, Integer] The identifier value to search fors
|
208
343
|
#
|
209
|
-
# @param
|
210
|
-
# E.g. if :serialNumber, then the value must be
|
211
|
-
#
|
344
|
+
# @param key: [Symbol] The identifier being used for the search.
|
345
|
+
# E.g. if :serialNumber, then the value must be a known serial number, it
|
346
|
+
# is not checked against other identifiers. Defaults to :id
|
212
347
|
#
|
213
348
|
# @param cnx: (see .all)
|
214
349
|
#
|
215
|
-
# @return [
|
350
|
+
# @return [Hash, nil] the basic dataset of the matching object,
|
216
351
|
# or nil if it doesn't exist
|
217
352
|
#
|
218
|
-
def self.
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
353
|
+
def self.raw_data(value = nil, cnx: Jamf.cnx, **ident_and_val)
|
354
|
+
validate_not_abstract
|
355
|
+
|
356
|
+
# given a value with no ident key
|
357
|
+
return raw_data_by_value_only(value, cnx: Jamf.cnx) if value
|
358
|
+
|
359
|
+
# if we're here, we should know our ident key and value
|
360
|
+
ident, value = ident_and_val.first
|
361
|
+
raise ArgumentError, 'Required parameter "identifier: value", where identifier is id:, name: etc.' unless ident && value
|
362
|
+
|
363
|
+
return raw_data_by_id(value, cnx: cnx) if ident == :id
|
364
|
+
return unless identifiers.include? ident
|
365
|
+
|
366
|
+
raw_data_by_other_identifier(ident, value, cnx: cnx)
|
367
|
+
end
|
368
|
+
|
369
|
+
# Match the given value in all possibly identifiers
|
370
|
+
def self.raw_data_by_value_only(value, cnx: Jamf.cnx)
|
371
|
+
return raw_data_by_id(value, cnx: cnx) if value.to_s.j_integer?
|
223
372
|
|
224
|
-
|
225
|
-
|
373
|
+
identifiers.each do |ident|
|
374
|
+
next if ident == :id
|
226
375
|
|
227
|
-
|
228
|
-
|
376
|
+
id = raw_data_by_other_identifier(ident, value, cnx: cnx)
|
377
|
+
return id if id
|
378
|
+
end # identifiers.each
|
379
|
+
return
|
380
|
+
end
|
381
|
+
private_class_method :raw_data_by_value_only
|
382
|
+
|
383
|
+
# get the basic dataset by id, with optional
|
384
|
+
# request params to get more than basic data
|
385
|
+
def self.raw_data_by_id(id, request_params: nil, cnx: Jamf.cnx)
|
386
|
+
cnx.get "#{rsrc_path}/#{id}#{request_params}"
|
387
|
+
rescue => e
|
388
|
+
return if e.httpStatus == 404
|
389
|
+
|
390
|
+
raise e
|
391
|
+
end
|
392
|
+
private_class_method :raw_data_by_id
|
393
|
+
|
394
|
+
# Given an indentier attr. key, and a value,
|
395
|
+
# return the id where that ident has that value, or nil
|
396
|
+
#
|
397
|
+
def self.raw_data_by_other_identifier(identifier, value, refresh: false, cnx: Jamf.cnx)
|
398
|
+
# if the API supports filtering by this identifier, just use that
|
399
|
+
return all(filter: "#{identifier}=='#{value}'", paged: true, page_size: 1, cnx: cnx).first if self::OBJECT_MODEL[identifier][:filter_key]
|
229
400
|
|
230
|
-
|
231
|
-
|
232
|
-
val_is_str ? m[ident].to_s.casecmp?(value) : m[ident] == value
|
233
|
-
end.first
|
234
|
-
return match[:id] if match
|
235
|
-
end # identifiers.each do |ident|
|
401
|
+
# otherwise we have to loop thru all the objects looking for the value
|
402
|
+
all(refresh: refresh, cnx: cnx).each { |data| return data if data[identifier].to_s.casecmp? value.to_s }
|
236
403
|
|
237
404
|
nil
|
238
405
|
end
|
406
|
+
private_class_method :raw_data_by_other_identifier
|
407
|
+
|
408
|
+
# Look up the valid ID for any arbitrary identifier.
|
409
|
+
# In general you should use this if the form:
|
410
|
+
#
|
411
|
+
# valid_id identifier: value
|
412
|
+
#
|
413
|
+
# where identifier is one of the available identifiers for this class
|
414
|
+
# like id:, name:, serialNumber: etc.
|
415
|
+
#
|
416
|
+
# In the unlikely event that you dont know which identifier a value is for
|
417
|
+
# or want to be able to take any of them without specifying, then
|
418
|
+
# you can use
|
419
|
+
#
|
420
|
+
# valid_id some_value
|
421
|
+
#
|
422
|
+
# If some_value is an integer or a string containing an integer, it
|
423
|
+
# is assumed to be an id: otherwise all the available identifers
|
424
|
+
# are searched, in the order you see them when you call <class>.identifiers
|
425
|
+
#
|
426
|
+
# If no matching object is found, nil is returned.
|
427
|
+
#
|
428
|
+
# WARNING: Do not use this to look up ids for getting the
|
429
|
+
# raw API data for an object. Since this calls .raw_data
|
430
|
+
# itself, it is redundant to use .valid_id to get an id
|
431
|
+
# to then pass on to .raw_data
|
432
|
+
# Use raw_data directly like this:
|
433
|
+
# data = raw_data(ident: val)
|
434
|
+
#
|
435
|
+
#
|
436
|
+
# @param value [String,Integer] A value for an arbitrary identifier
|
437
|
+
#
|
438
|
+
# @param cnx [Jamf::Connection] The connection to use. default: Jamf.cnx
|
439
|
+
#
|
440
|
+
# @param ident_and_val [Hash{Symbol: String}] The identifier key and the value
|
441
|
+
# to look for in that key, e.g. name: 'foo' or serialNumber: 'ASDFGH'
|
442
|
+
#
|
443
|
+
# @return [String, nil] The id (integer-in-string) of the object, or nil
|
444
|
+
# if no match found
|
445
|
+
#
|
446
|
+
def self.valid_id(value = nil, cnx: Jamf.cnx, **ident_and_val)
|
447
|
+
raw_data(value, cnx: cnx, **ident_and_val)&.dig(:id)
|
448
|
+
end
|
239
449
|
|
240
450
|
# Bu default, subclasses are creatable, i.e. new instances can be created
|
241
451
|
# with .create, and added to the JSS with .save
|
@@ -253,15 +463,19 @@ module Jamf
|
|
253
463
|
validate_not_abstract
|
254
464
|
raise Jamf::UnsupportedError, "#{self}'s are not currently creatable via the API" unless creatable?
|
255
465
|
|
466
|
+
# Which connection to use
|
256
467
|
cnx = params.delete :cnx
|
257
468
|
cnx ||= Jamf.cnx
|
258
469
|
|
259
470
|
params.delete :id # no such animal when .creating
|
260
|
-
|
261
471
|
params.keys.each do |param|
|
262
472
|
raise ArgumentError, "Unknown parameter: #{param}" unless self::OBJECT_MODEL.key? param
|
263
473
|
|
264
|
-
|
474
|
+
if params[param].is_a? Array
|
475
|
+
params[param].map! { |val| validate_attr param, val, cnx: cnx }
|
476
|
+
else
|
477
|
+
params[param] = validate_attr param, params[param], cnx: cnx
|
478
|
+
end
|
265
479
|
end
|
266
480
|
|
267
481
|
params[:creating_from_create] = true
|
@@ -273,41 +487,27 @@ module Jamf
|
|
273
487
|
# To create new members to be added to the JSS, use
|
274
488
|
# {Jamf::CollectionResource.create}
|
275
489
|
#
|
276
|
-
#
|
490
|
+
# You must know the specific identifier attribute you're looking up, e.g.
|
277
491
|
# :id or :name or :udid, (or an aliase thereof) then you can specify it like
|
278
492
|
# `.fetch name: 'somename'`, or `.fetch udid: 'someudid'`
|
279
493
|
#
|
280
|
-
# If you don't know if (or don't want to type it) you can just use
|
281
|
-
# `.fetch 'somename'`, or `.fetch 'someudid'` and all identifiers will be
|
282
|
-
# searched for a match.
|
283
|
-
#
|
284
|
-
# @param ident_value[Object] A value for any identifier for this subclass.
|
285
|
-
# All identifier attributes will be searched for a match.
|
286
|
-
#
|
287
494
|
# @param cnx[Jamf::Connection] the connection to use to fetch the object
|
288
495
|
#
|
289
|
-
# @param
|
496
|
+
# @param ident_and_val[Hash] an identifier attribute key and a search value
|
290
497
|
#
|
291
498
|
# @return [CollectionResource] The ruby-instance of a Jamf object
|
292
499
|
#
|
293
|
-
def self.fetch(
|
500
|
+
def self.fetch(random = nil, cnx: Jamf.cnx, **ident_and_val)
|
294
501
|
validate_not_abstract
|
295
|
-
|
296
|
-
|
297
|
-
if
|
298
|
-
|
299
|
-
elsif
|
300
|
-
|
301
|
-
elsif ident_hash.empty?
|
302
|
-
nil
|
303
|
-
else
|
304
|
-
ident, lookup_value = ident_hash.first
|
305
|
-
valid_id ident => lookup_value, cnx: cnx
|
502
|
+
ident, value = ident_and_val.first
|
503
|
+
data =
|
504
|
+
if random
|
505
|
+
all.sample
|
506
|
+
elsif ident && value
|
507
|
+
raw_data(cnx: cnx, **ident_and_val)
|
306
508
|
end
|
509
|
+
raise Jamf::NoSuchItemError, "No matching #{self}" unless data
|
307
510
|
|
308
|
-
raise Jamf::NoSuchItemError, "No matching #{self}" unless id
|
309
|
-
|
310
|
-
data = cnx.get "#{rsrc_path}/#{id}"
|
311
511
|
new data, cnx: cnx
|
312
512
|
end # fetch
|
313
513
|
|
@@ -318,36 +518,31 @@ module Jamf
|
|
318
518
|
true
|
319
519
|
end
|
320
520
|
|
321
|
-
# Delete one or more objects by
|
322
|
-
# Any valid identifier for the class can be used (id, name, udid, etc)
|
323
|
-
# Identifiers can be provided as an array or as separate parameters
|
324
|
-
#
|
325
|
-
# e.g. .delete [1,3, 34, 4]
|
326
|
-
# or .delete 'myComputer', 'that-computer', 'OtherComputer'
|
521
|
+
# Delete one or more objects by id
|
327
522
|
#
|
328
|
-
# @param
|
523
|
+
# @param ids [Array<String,Integer>] The ids to delete
|
329
524
|
#
|
330
|
-
# @param cnx[Jamf::Connection]
|
525
|
+
# @param cnx [Jamf::Connection] The connection to use, default: Jamf.cnx
|
331
526
|
#
|
332
|
-
# @return [Array]
|
527
|
+
# @return [Array<Jamf::Connection::APIError::ErrorInfo] Info about any ids
|
528
|
+
# that failed to be deleted.
|
333
529
|
#
|
334
|
-
def self.delete(*
|
530
|
+
def self.delete(*ids, cnx: Jamf.cnx)
|
335
531
|
raise Jamf::UnsupportedError, "Deleting #{self} objects is not currently supported" unless deletable?
|
336
532
|
|
337
|
-
|
338
|
-
no_valid_ids = []
|
533
|
+
return bulk_delete(ids, cnx: Jamf.cnx) if ancestors.include? Jamf::BulkDeletable
|
339
534
|
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
# TODO: some rsrcs have a 'bulk delete' version...
|
348
|
-
idents.each { |id| cnx.delete "#{rsrc_path}/#{id}" }
|
535
|
+
errs = []
|
536
|
+
ids.each do |id_to_delete|
|
537
|
+
begin
|
538
|
+
cnx.delete "#{rsrc_path}/#{id_to_delete}"
|
539
|
+
rescue Jamf::Connection::APIError => e
|
540
|
+
raise e unless e.httpStatus == 404
|
349
541
|
|
350
|
-
|
542
|
+
errs += e.errors
|
543
|
+
end # begin
|
544
|
+
end # ids.each
|
545
|
+
errs
|
351
546
|
end
|
352
547
|
|
353
548
|
# Private Class Methods
|
@@ -359,7 +554,7 @@ module Jamf
|
|
359
554
|
list_method_name = "all_#{attr_name}s"
|
360
555
|
|
361
556
|
define_singleton_method(list_method_name) do |refresh = false, cnx: Jamf.cnx|
|
362
|
-
all_list = all(refresh, cnx: cnx)
|
557
|
+
all_list = all(refresh: refresh, cnx: cnx)
|
363
558
|
if attr_def[:class].is_a? Symbol
|
364
559
|
all_list.map { |i| i[attr_name] }.uniq
|
365
560
|
else
|
@@ -378,24 +573,6 @@ module Jamf
|
|
378
573
|
end # create_list_methods
|
379
574
|
private_class_method :create_list_methods
|
380
575
|
|
381
|
-
# Given an indentier attr. key, and a value,
|
382
|
-
# return the id where that ident has that value, or nil
|
383
|
-
#
|
384
|
-
def self.id_from_other_ident(ident, value, refresh = true, cnx: Jamf.cnx)
|
385
|
-
raise ArgumentError, "Unknown identifier '#{ident}' for #{self}" unless identifiers.include? ident
|
386
|
-
|
387
|
-
# check the id itself first
|
388
|
-
return value if ident == :id && all_ids(refresh, cnx: cnx).include?(value)
|
389
|
-
|
390
|
-
# all ident values => ids
|
391
|
-
ident_map = map_all(ident, to: :id, cnx: cnx, refresh: refresh)
|
392
|
-
|
393
|
-
# case-insensitivity for string values
|
394
|
-
value = ident_map.keys.j_ci_fetch(value) if value.is_a? String
|
395
|
-
|
396
|
-
ident_map[value]
|
397
|
-
end
|
398
|
-
private_class_method :id_from_other_ident
|
399
576
|
|
400
577
|
# Instance Methods
|
401
578
|
#####################################
|
@@ -406,11 +583,13 @@ module Jamf
|
|
406
583
|
|
407
584
|
def rsrc_path
|
408
585
|
return unless exist?
|
586
|
+
|
409
587
|
"#{self.class.rsrc_path}/#{@id}"
|
410
588
|
end
|
411
589
|
|
412
590
|
def delete
|
413
591
|
raise Jamf::UnsupportedError, "Deleting #{self} objects is not currently supported" unless self.class.deletable?
|
592
|
+
|
414
593
|
@cnx.delete rsrc_path
|
415
594
|
end
|
416
595
|
|