ruby-jss 1.2.9 → 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 +196 -1
- 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 +107 -83
- data/lib/jamf/api/abstract_classes/prestage.rb +55 -30
- 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 +20 -12
- data/lib/jamf/api/connection/api_error.rb +8 -8
- data/lib/jamf/api/connection/token.rb +36 -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/department.rb +1 -1
- data/lib/jamf/api/resources/collection_resources/device_enrollment.rb +13 -13
- 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/configuration.rb +7 -9
- data/lib/jamf/ruby_extensions.rb +1 -0
- data/lib/jamf/ruby_extensions/array.rb +1 -1
- data/lib/jamf/ruby_extensions/array/utils.rb +3 -3
- data/lib/jamf/ruby_extensions/dig.rb +52 -0
- data/lib/jamf/validate.rb +63 -24
- data/lib/jamf/version.rb +1 -1
- data/lib/jss.rb +4 -1
- data/lib/jss/api_connection.rb +110 -397
- data/lib/jss/api_object.rb +16 -13
- data/lib/jss/api_object/advanced_search.rb +27 -26
- data/lib/jss/api_object/app_store_country_codes.rb +298 -0
- data/lib/jss/api_object/categorizable.rb +1 -1
- data/lib/jss/api_object/computer.rb +5 -1
- data/lib/jss/api_object/configuration_profile.rb +34 -3
- data/lib/jss/api_object/directory_binding.rb +273 -0
- data/lib/jss/api_object/directory_binding_type.rb +96 -0
- data/lib/jss/api_object/directory_binding_type/active_directory.rb +539 -0
- data/lib/jss/api_object/directory_binding_type/admitmac.rb +594 -0
- data/lib/jss/api_object/directory_binding_type/centrify.rb +226 -0
- data/lib/jss/api_object/directory_binding_type/open_directory.rb +178 -0
- data/lib/jss/api_object/directory_binding_type/powerbroker_identity_services.rb +73 -0
- data/lib/jss/api_object/disk_encryption_configurations.rb +114 -0
- data/lib/jss/api_object/distribution_point.rb +97 -37
- data/lib/jss/api_object/dock_item.rb +143 -0
- data/lib/jss/api_object/ebook.rb +1 -2
- data/lib/jss/api_object/extendable.rb +68 -32
- data/lib/jss/api_object/extension_attribute.rb +4 -3
- data/lib/jss/api_object/group.rb +33 -2
- data/lib/jss/api_object/mac_application.rb +107 -8
- data/lib/jss/api_object/mobile_device.rb +3 -0
- data/lib/jss/api_object/mobile_device_application.rb +12 -0
- data/lib/jss/api_object/network_segment.rb +195 -70
- data/lib/jss/api_object/package.rb +105 -40
- data/lib/jss/api_object/patch_source.rb +10 -9
- data/lib/jss/api_object/policy.rb +491 -7
- data/lib/jss/api_object/printer.rb +446 -0
- data/lib/jss/api_object/scopable.rb +10 -15
- data/lib/jss/api_object/scopable/scope.rb +386 -71
- data/lib/jss/api_object/self_servable.rb +17 -9
- data/lib/jss/api_object/uploadable.rb +1 -1
- data/lib/jss/api_object/user.rb +42 -1
- data/lib/jss/api_object/vpp_account.rb +209 -0
- data/lib/jss/api_object/vppable.rb +169 -13
- data/lib/jss/composer.rb +1 -1
- data/lib/jss/exceptions.rb +3 -0
- data/lib/jss/server.rb +15 -0
- data/lib/jss/utility.rb +8 -22
- data/lib/jss/validate.rb +53 -10
- data/lib/jss/version.rb +1 -1
- metadata +50 -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,198 @@ 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
|
+
|
45
|
+
## \[1.4.1] - 2020-10-01
|
46
|
+
|
47
|
+
### Added
|
48
|
+
|
49
|
+
- Support for JP API connections to https://tryitout.jamfcloud.com/, the open API test server provided by Jamf. It uses internal tokens, so the /auth endpoint is disabled. Now as with the Classic API, `Jamf::Connection.connect` will accpt any name & password when connecting to that host.
|
50
|
+
|
51
|
+
## \[1.4.0] - 2020-09-14
|
52
|
+
|
53
|
+
### Added
|
54
|
+
|
55
|
+
- Class JSS::VPPAccount, implementing the 'vppacconts' endpoint.
|
56
|
+
|
57
|
+
- Constant JSS::APP_STORE_COUNTRY_CODES, a Hash with keys being the official country names used by the App Store, and values being the two-letter codes for those names. This static Hash is derived from a Jamf Pro API end point, and will be updated as needed. These codes are used by JSS::VPPAccount
|
58
|
+
|
59
|
+
- Module Method JSS.country_code_match(str) whic allows you to filter the JSS::APP_STORE_COUNTRY_CODES Hash to only those key-value pairs that include the given string.
|
60
|
+
|
61
|
+
- Mixin Class Method VPPable.all_vpp_device_assignable, returns a Hash of Hashes showing the total, used, and remaining licenses for all members of the target class that are VPP-assignable by device.
|
62
|
+
|
63
|
+
- Scopable::Scope#in_scope?(machine) Given a JSS::Computer or MobileDevice, or an identifier for one, it is in the scope? WARNING: For scopes that include Jamf Users or User Groups as targets or exclusions, this method may return an incorrect value. See the discussion in the comments/documentation for the Scopable::Scope class under `IMPORTANT - Users & User Groups in Targets and Exclusions`
|
64
|
+
|
65
|
+
- Scopable::Scope#scoped_machines returns a Hash of ids=>names for all machines in this scope. WARNING: This must instantiate all machines in the target class. It will still be slow, at least the first time for each target class. On the upside, the instantiated machines will be cached, so generating this list for other scopes with the same target class will be much much faster. In tests, with 1600 Computers in the JSS, it took about 7 minutes the first time, but less than 1 second after caching.
|
66
|
+
See also the warning for #in_scope? above, which applies here as well.
|
67
|
+
|
68
|
+
- JSS::Policy objects support 'Policy Retry' via the getter/setter methods #retry_event, #retry_attempts, and #notify_failed_retries. You can only set these values if the #frequency is :once_per_computer. To turn off policy-retry, either set the retry_event to :none, or set the retry_attempts to 0
|
69
|
+
|
70
|
+
### Changed
|
71
|
+
|
72
|
+
- Prettier XML for JSS::APIObject#ppx
|
73
|
+
|
74
|
+
- Improved JSS::Validate.boolean. Accepts: true, false, 'true', 'false', 'yes', 'no', 't','f', 'y', or 'n' as Strings or Symbols, case insensitive
|
75
|
+
|
76
|
+
- The JSS::MacApplication class is more fully implemented
|
77
|
+
|
78
|
+
- JSS::Scopable::Scope now uses the word 'targets' consistently to match the UI's 'Targets' tab. The previous word 'inclusions' still works as before.
|
79
|
+
|
80
|
+
- When using the Jamf module to access the Jamf Pro API, the minumum JamfPro version is now 10.23.0. WARNING: Like the Jamf Pro API itself, the Jamf module that accesses it is in beta and may have breaking changes at any time.
|
81
|
+
|
82
|
+
### Fixed
|
83
|
+
|
84
|
+
- JSS::ExtensionAttribute: when used as a display field in an AdvancedSearch, the name of the EA in the search result Hash comes from the API as a String (turned into a Symbol) that is the EA name with colons removed and spaces & dashes turned to underscores. Previously ruby-jss didn't remove the colons
|
85
|
+
|
86
|
+
- Used an XML workaround for the common classic API bug where an XML array comes as a single-item JSON hash. This time in the JSS::User class's user_groups method.
|
87
|
+
|
88
|
+
- The Jamf Pro API endpoints for /v1/device-enrollment changed to /v1/device-enrollments and /v1/device-enrollment/sync/<id> changed to /v1/device-enrollments/<id>/syncs. /v1/devive-enrollments/syncs.
|
89
|
+
|
90
|
+
- The Jamf Pro API endpoint for bulk-deleting Departments changed from 'delete-departments' to 'delete-multiple'
|
91
|
+
|
92
|
+
## \[1.3.3] - 2020-08-07
|
93
|
+
|
94
|
+
### Fixed
|
95
|
+
- Regression where JSS::Package#required_processor= wouldn't take 'x86'
|
96
|
+
|
97
|
+
## \[1.3.2] - 2020-07-31
|
98
|
+
Many thanks to @cybertunnel for adding a huge amount of code to get JSS::Policy fully implimented, as well as other fixes and updates!
|
99
|
+
|
100
|
+
### Added
|
101
|
+
- new class JSS::DockItem
|
102
|
+
- new classes JSS::DirectoryBinding and JSS::DirectoryBindingType
|
103
|
+
- new class JSS::Printer
|
104
|
+
- new class JSS::DiskEncryptionConfiguration
|
105
|
+
- JSS::Policy:
|
106
|
+
- getters and setters for `#user_message_start` and `#user_message_end`
|
107
|
+
- `#set_management_account` and `#verify_management_password`
|
108
|
+
- `#add_dock_item` and `#remove_dock_item`
|
109
|
+
- `#directory_bindings`, `#add_directory_binding` and `#remove_directory_binding`
|
110
|
+
- `#add_printer` and `#remove_printer`
|
111
|
+
- `#reissue_key`, `#apply_encryption_configuration`, and `#remove_encryption_configuration`
|
112
|
+
|
113
|
+
|
114
|
+
### Changed
|
115
|
+
- JSS::Package:
|
116
|
+
- no longer issues a warning when changing the file_name of a package
|
117
|
+
- Updated the CPU type string from 'x86' to 'Intel/x86'
|
118
|
+
- Methods which used to always use the master distribution point now accept a parameter `dist_point: dp` where dp is the name or id of a fileshare distribution point. If not specified, it still defaults to the Master Distribution Point. This is needed because if the Cloud Distribution Point is the master, there is no access to it via the Classic API, and any use of DistributionPoint.master_distribution_point will raise an error.
|
119
|
+
|
120
|
+
## \[1.3.1] - 2020-06-21
|
121
|
+
|
122
|
+
### Changed
|
123
|
+
|
124
|
+
- JSS::MobileDeviceApplication when using PrettyPrint (pp) in irb, no longer shows the base64 data for the ipa file.
|
125
|
+
|
126
|
+
- JSS::DistributionPoint.my_distribution_point and .master_distribution_point now have options for dealing with the Cloud Distribution Point (which is not available in the classic API) being the master.
|
127
|
+
|
128
|
+
### Fixed
|
129
|
+
|
130
|
+
- JSS::NetworkSegment.distribution_point= now takes nil or an empty string to unset the dist point.
|
131
|
+
|
132
|
+
## \[1.3.0] - 2020-06-05
|
133
|
+
|
134
|
+
### Added
|
135
|
+
|
136
|
+
- JSS::NetworkSegment.network_ranges_as_integers method, Similar to NetworkSegment.network_ranges, but the ranges are of Integers, not IPAddr instances. This makes for *MUCH* faster range calculations, needed to implement improvements to NetworkSegment.network_segment_for_ip
|
137
|
+
|
138
|
+
- JSS::Package.all_filenames_by, returns a Hash of all distribution point filenames for all packages, keyed by either the package id, or the package name. NOTE: as with JSS::Package.all_filenames, this method must instantiate all JSS::Package objects, so it will be slow.
|
139
|
+
|
140
|
+
### Changed
|
141
|
+
|
142
|
+
- JSS.expand_min_os now expands to macOS 10.30.x, which should hold us for a while
|
143
|
+
|
144
|
+
- JSS::NetworkSegment.network_segment_for_ip and .my_network_segment are no longer deprecated, but now return an integer NetSeg id (or nil). The plural forms of those methods still return an Array of ids for all the matching network segments.
|
145
|
+
|
146
|
+
- The logic for JSS::NetworkSegment.network_segment_for_ip (and .my_network_segment) now matches how the Jamf server does it: when you IP address is in more than one Network Segment, Jamf uses the smallest/narrowest one (the one containing fewest IP addresses). If more than one of your Network Segments are that same width, the one with the lowest starting IP address is used.
|
147
|
+
|
148
|
+
- In some networking situations (e.g. Split-tunnel VPN with multiple active network ports) the JSS::APIObject.delete method will raise a 404 NotFound error, seemingly because the object was already deleted but a second http DELETE is sent (I think). We now just rescue and ignore that error, since the fact that it's not found means it was indeed deleted.
|
149
|
+
|
150
|
+
### Fixed
|
151
|
+
|
152
|
+
- A copy/paste bug in Jamf::Prestage.serials_for_prestage
|
153
|
+
|
154
|
+
## \[1.2.15] - 2020-04-30
|
155
|
+
|
156
|
+
### Fixed
|
157
|
+
|
158
|
+
- USER_CONF_FILE is always a pathname, never nil
|
159
|
+
|
160
|
+
- issues with Array#j_ci_* methods related to removing safe navigation
|
161
|
+
|
162
|
+
## \[1.2.13] - 2020-04-29
|
163
|
+
|
164
|
+
### Fixed
|
165
|
+
|
166
|
+
- Ruby 2.6 needs parens in more places than 2.3, apparently
|
167
|
+
|
168
|
+
## \[1.2.12] - 2020-04-29
|
169
|
+
|
170
|
+
### Added
|
171
|
+
|
172
|
+
- Backport of `#dig` for Arrays, Hashes and OpenStructs, for compatibiliy with older rubiesd (for a while longer anyway). Gratefully borrowed from https://github.com/Invoca/ruby_dig
|
173
|
+
|
174
|
+
### Changed
|
175
|
+
|
176
|
+
- Removed all safe navigation operators (`&.`) for compatibility with older rubies (for a while longer anyway)
|
177
|
+
|
178
|
+
|
179
|
+
## \[1.2.11] - 2020-04-26
|
180
|
+
|
181
|
+
### Fixed
|
182
|
+
|
183
|
+
- Bug in Package#install that prevented installs from 'alt_download_url'.
|
184
|
+
|
185
|
+
## \[1.2.10] - 2020-04-25
|
186
|
+
|
187
|
+
### Added
|
188
|
+
|
189
|
+
- Computer#reported_ip_address. This value is collected in newer versions of Jamf Pro. While the #ip_address is the client's IP address from the Jamf Server's perspective, the #reported_ip_address is the IP from the client's perspective, which may be different on a NATted network like a home network.
|
190
|
+
|
191
|
+
### Fixed
|
192
|
+
|
193
|
+
- MobileDevice#upload now works like Computer#upload
|
194
|
+
|
195
|
+
### Changed
|
196
|
+
|
197
|
+
- Validation of Ext. Attribute values is improved, namely for EAs with integer values, integer-strings like "12" are accepted and converted to real integers as needed.
|
198
|
+
|
7
199
|
## \[1.2.9] - 2020-04-13
|
8
200
|
|
9
201
|
### Fixed
|
@@ -41,7 +233,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
41
233
|
|
42
234
|
- Classic API (JSS module)
|
43
235
|
- Sitable objects now recognize the string "None" as meaning no site is assigned. Thanks @cybertunnel for this fix!
|
236
|
+
|
44
237
|
- Scopable::Scope now deals with some bugs in the API regarding Jamf & LDAP users & user groups in targets, limitations, & exclusions. Please see the documentation/comments for the class in the file or the online documentation. Thanks @cybertunnel again!
|
238
|
+
|
45
239
|
- Criteriable::Criteria can now be empty - containing no criterion objects. When criteriable objects are created (such as Advanced Searches) the default JSS::Criteriable::Criteria object has no criteria. To remove all criteria, use `criteria.clear`, `criteria = nil`, or `criteria = JSS::Criteriable::Criteria.new` and then save. Once again, thanks to @cybertunnel for finding this.
|
46
240
|
|
47
241
|
- Jamf Pro API (Jamf module)
|
@@ -85,7 +279,8 @@ Note that the `last_inventory_update` value does NOT indicate such communication
|
|
85
279
|
|
86
280
|
- All APIObject Subclasses (Policy, Computer, MobileDevice, ComputerGroup, etc..) now have `get_raw`, `post_raw` & `put_raw` class methods, which are simpler wrappers for APIConnection#get_rsrc, #post_rsrc, and #put_rsrc.
|
87
281
|
- `get_raw` takes an object's id, and returns the 'raw' JSON (parsed into a ruby Hash with symbolized keys) or a REXML::Document (from which you'll probably want to use the `root` element). If you pass `as_string: true` you'll get the un-parsed JSON or XML string directly from the API
|
88
|
-
This can be useful when you need to retrieve the full object, to get some data not available in the summary-list, but instantiating the full ruby class is too slow
|
282
|
+
This can be useful when you need to retrieve the full object, to get some data not available in the summary-list, but instantiating the full ruby class is too slow
|
283
|
+
|
89
284
|
- `post_raw` & `put_raw` can send raw XML to the API without instantiating objects. In some cases, where you're making simple changes to simple XML, this can be faster than fetching a full instance and the re-saving it.
|
90
285
|
WARNING You must create or acquire the XML to be sent, and no validation will be performed on it. It must be a String of XML, or something that returns such a string with #to_s, such as a REXML::Document, or a REXML::Element.
|
91
286
|
|
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
|
|