ruby-jss 1.2.10 → 1.5.2

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.

Files changed (119) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES.md +208 -1
  3. data/lib/jamf.rb +18 -16
  4. data/lib/jamf/api/base_classes/collection_resource.rb +613 -0
  5. data/lib/jamf/api/{abstract_classes → base_classes}/json_object.rb +110 -102
  6. data/lib/jamf/api/{abstract_classes → base_classes}/prestage.rb +56 -31
  7. data/lib/jamf/api/{abstract_classes → base_classes}/resource.rb +10 -6
  8. data/lib/jamf/api/{abstract_classes → base_classes}/singleton_resource.rb +4 -3
  9. data/lib/jamf/api/connection.rb +20 -12
  10. data/lib/jamf/api/connection/api_error.rb +8 -8
  11. data/lib/jamf/api/connection/token.rb +36 -15
  12. data/lib/jamf/api/json_objects/device_enrollment_device.rb +14 -7
  13. data/lib/jamf/api/json_objects/{location.rb → device_enrollment_device_sync_state.rb} +27 -41
  14. data/lib/jamf/api/json_objects/device_enrollment_sync_status.rb +1 -1
  15. data/lib/jamf/api/json_objects/{attachment.rb → locale.rb} +14 -23
  16. data/lib/jamf/api/json_objects/md_prestage_name.rb +1 -1
  17. data/lib/jamf/api/json_objects/md_prestage_names.rb +2 -2
  18. data/lib/jamf/api/json_objects/md_prestage_skip_setup_items.rb +50 -1
  19. data/lib/jamf/api/json_objects/prestage_assignment.rb +2 -2
  20. data/lib/jamf/api/json_objects/prestage_location.rb +3 -3
  21. data/lib/jamf/api/json_objects/prestage_purchasing_data.rb +7 -7
  22. data/lib/jamf/api/json_objects/prestage_scope.rb +1 -1
  23. data/lib/jamf/api/{resources/collection_resources → json_objects}/time_zone.rb +9 -23
  24. data/lib/jamf/api/mixins/{abstract.rb → base_class.rb} +34 -16
  25. data/lib/jamf/api/mixins/bulk_deletable.rb +27 -6
  26. data/lib/jamf/api/mixins/change_log.rb +201 -51
  27. data/lib/jamf/api/{resources/collection_resources/extension_attribute.rb → mixins/filterable.rb} +20 -14
  28. data/lib/jamf/api/mixins/pageable.rb +208 -0
  29. data/lib/jamf/api/{json_objects/installed_application.rb → mixins/sortable.rb} +33 -33
  30. data/lib/jamf/api/resources/collection_resources/building.rb +16 -9
  31. data/lib/jamf/api/resources/collection_resources/category.rb +5 -4
  32. data/lib/jamf/api/resources/collection_resources/computer_prestage.rb +12 -5
  33. data/lib/jamf/api/resources/collection_resources/department.rb +1 -3
  34. data/lib/jamf/api/resources/collection_resources/device_enrollment.rb +13 -13
  35. data/lib/jamf/api/resources/collection_resources/inventory_preload_record.rb +11 -3
  36. data/lib/jamf/api/resources/collection_resources/mobile_device_prestage.rb +25 -23
  37. data/lib/jamf/api/resources/collection_resources/script.rb +61 -25
  38. data/lib/jamf/api/resources/singleton_resources/app_store_country_codes.rb +15 -5
  39. data/lib/jamf/api/resources/singleton_resources/locales.rb +155 -0
  40. data/lib/jamf/api/resources/singleton_resources/time_zones.rb +213 -0
  41. data/lib/jamf/configuration.rb +7 -9
  42. data/lib/jamf/ruby_extensions.rb +1 -0
  43. data/lib/jamf/ruby_extensions/array.rb +1 -1
  44. data/lib/jamf/ruby_extensions/array/utils.rb +3 -3
  45. data/lib/jamf/{api/resources/collection_resources/computer.rb → ruby_extensions/dig.rb} +22 -19
  46. data/lib/jamf/validate.rb +63 -24
  47. data/lib/jamf/version.rb +1 -1
  48. data/lib/jss.rb +4 -1
  49. data/lib/jss/api_connection.rb +111 -433
  50. data/lib/jss/api_object.rb +16 -13
  51. data/lib/jss/api_object/advanced_search.rb +27 -26
  52. data/lib/jss/api_object/app_store_country_codes.rb +298 -0
  53. data/lib/jss/api_object/categorizable.rb +1 -1
  54. data/lib/jss/api_object/computer.rb +13 -0
  55. data/lib/jss/api_object/configuration_profile.rb +60 -4
  56. data/lib/jss/api_object/directory_binding.rb +273 -0
  57. data/lib/jss/api_object/directory_binding_type.rb +96 -0
  58. data/lib/jss/api_object/directory_binding_type/active_directory.rb +539 -0
  59. data/lib/jss/api_object/directory_binding_type/admitmac.rb +594 -0
  60. data/lib/jss/api_object/directory_binding_type/centrify.rb +226 -0
  61. data/lib/jss/api_object/directory_binding_type/open_directory.rb +178 -0
  62. data/lib/jss/api_object/directory_binding_type/powerbroker_identity_services.rb +73 -0
  63. data/lib/jss/api_object/disk_encryption_configurations.rb +114 -0
  64. data/lib/jss/api_object/distribution_point.rb +97 -37
  65. data/lib/jss/api_object/dock_item.rb +143 -0
  66. data/lib/jss/api_object/ebook.rb +1 -2
  67. data/lib/jss/api_object/extendable.rb +1 -1
  68. data/lib/jss/api_object/extension_attribute.rb +4 -3
  69. data/lib/jss/api_object/group.rb +33 -2
  70. data/lib/jss/api_object/mac_application.rb +107 -8
  71. data/lib/jss/api_object/mobile_device_application.rb +12 -0
  72. data/lib/jss/api_object/network_segment.rb +195 -70
  73. data/lib/jss/api_object/package.rb +105 -40
  74. data/lib/jss/api_object/patch_source.rb +10 -9
  75. data/lib/jss/api_object/policy.rb +596 -32
  76. data/lib/jss/api_object/printer.rb +446 -0
  77. data/lib/jss/api_object/scopable.rb +10 -15
  78. data/lib/jss/api_object/scopable/scope.rb +371 -55
  79. data/lib/jss/api_object/self_servable.rb +17 -9
  80. data/lib/jss/api_object/uploadable.rb +1 -1
  81. data/lib/jss/api_object/user.rb +42 -1
  82. data/lib/jss/api_object/vpp_account.rb +209 -0
  83. data/lib/jss/api_object/vppable.rb +169 -13
  84. data/lib/jss/composer.rb +1 -1
  85. data/lib/jss/exceptions.rb +3 -0
  86. data/lib/jss/server.rb +15 -0
  87. data/lib/jss/utility.rb +143 -52
  88. data/lib/jss/validate.rb +53 -10
  89. data/lib/jss/version.rb +1 -1
  90. metadata +56 -61
  91. data/lib/jamf/api/abstract_classes/advanced_search.rb +0 -86
  92. data/lib/jamf/api/abstract_classes/collection_resource.rb +0 -433
  93. data/lib/jamf/api/abstract_classes/generic_reference.rb +0 -145
  94. data/lib/jamf/api/abstract_classes/prestage_skip_setup_items.rb +0 -126
  95. data/lib/jamf/api/json_objects/account_prefs.rb +0 -79
  96. data/lib/jamf/api/json_objects/android_details.rb +0 -139
  97. data/lib/jamf/api/json_objects/appletv_details.rb +0 -110
  98. data/lib/jamf/api/json_objects/cellular_network.rb +0 -151
  99. data/lib/jamf/api/json_objects/computer_prestage_skip_setup_items.rb +0 -67
  100. data/lib/jamf/api/json_objects/criterion.rb +0 -152
  101. data/lib/jamf/api/json_objects/extension_attribute_value.rb +0 -128
  102. data/lib/jamf/api/json_objects/installed_certificate.rb +0 -53
  103. data/lib/jamf/api/json_objects/installed_configuration_profile.rb +0 -67
  104. data/lib/jamf/api/json_objects/installed_ebook.rb +0 -58
  105. data/lib/jamf/api/json_objects/installed_provisioning_profile.rb +0 -59
  106. data/lib/jamf/api/json_objects/ios_details.rb +0 -244
  107. data/lib/jamf/api/json_objects/mobile_device_details.rb +0 -219
  108. data/lib/jamf/api/json_objects/mobile_device_security.rb +0 -101
  109. data/lib/jamf/api/json_objects/purchasing_data.rb +0 -125
  110. data/lib/jamf/api/mixins/locatable.rb +0 -124
  111. data/lib/jamf/api/mixins/referable.rb +0 -92
  112. data/lib/jamf/api/resources/collection_resources/account.rb +0 -163
  113. data/lib/jamf/api/resources/collection_resources/advanced_mobile_device_search.rb +0 -52
  114. data/lib/jamf/api/resources/collection_resources/advanced_user_search.rb +0 -52
  115. data/lib/jamf/api/resources/collection_resources/mobile_device.rb +0 -315
  116. data/lib/jamf/api/resources/collection_resources/site.rb +0 -77
  117. data/lib/jamf/api/resources/singleton_resources/authorization.rb +0 -88
  118. data/lib/jamf/api/resources/singleton_resources/client_checkin_settings.rb +0 -139
  119. data/lib/jamf/api/resources/singleton_resources/reenrollment_settings.rb +0 -95
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0734256ff4d9e553418ff9e0189e85cbb455751e682af0278dfff3f85f9a6070
4
- data.tar.gz: 40fde852a9d6f4cc22a804b0240038bdb17d319e1d51edaa8c974ad50d05861d
3
+ metadata.gz: 609ac9ae5ba3ddf1c4c8392acda8d1f1f0bba99e119da2781bd1e8d841a294f5
4
+ data.tar.gz: 28be9609494e02d11e863e5a64d80c82d866650efd599967400e434d157633ba
5
5
  SHA512:
6
- metadata.gz: 721bc4ef63c7c1114a66bb772a609c4589879977e65922eb008dacf478ea17cc09a78e367e4a8297b46139b9d08f5d16fa6ffa7e0516af6c7ccc571ab37e39da
7
- data.tar.gz: 0d8788ff29178d488fc11da8fa4c77c3ca5b40c8a770979cc7bf11bb8ab727f43315e2632ed74c8ae9685d79142e2536e1239624c236ba218fc2de5c70adddd4
6
+ metadata.gz: f57b6c5c4b127688a6b9fd190a7927e1d1c6332061d1126b23d03b2d39566898fd5a0876ac73e801f6f9824cb45e149d13d6a4e97154b5c08f199ac593e74cdc
7
+ data.tar.gz: 1086564724dd9398f1bf540df6a092c90045d9226a78ff1eeade1fc6af4442b566fca2fea896f3e0a25aa62a60d568cc43331c5adb89c5929d53a3bcdf063419
data/CHANGES.md CHANGED
@@ -4,6 +4,210 @@ 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.2] - 2020-12-21
8
+
9
+ ### Added
10
+
11
+ - JSS::Policy#flush_logs can now be called as a class method JSS::Policy.flush_logs, passing in the policy names or ids, without instantiating the policy
12
+
13
+ - Both the class and instance 'flush_logs' methods for JSS::Policy take a named parameter 'computers:' which is an array of the computer identifiers for which the policy should be flushed.
14
+
15
+ - JSS::Computer instances now have a 'flush_policy_logs' method which is a wrapper for calling JSS::Policy.flush_logs for just that computer
16
+
17
+ - JSS::ConfigurationProfile: #update/#save now takes boolean param redeploy_to_all: which defaults to false. The default means redeploy only to newly assigned machines in scope. Setting this to true will push the profile out to all machines in scope, even if they already have the profile.
18
+
19
+ ### Changed
20
+
21
+ - JSS.expand_min_os, used to expand strings like '>=10.14.5' into comma-separated versions to be used in Package and Script os_limitations, has been updated to handle Big Sur being both 10.16 and 11.0, and for future OSes to be 12.x, 13.x etc.
22
+ NOTE: If you've used this feature in the past, you might want to look at your package and script seetings and update them, since they will refer to OSes 10.17 and higher.
23
+
24
+ - JSS::APIConnection: initialize @object_list_cache as an empty hash. This provides more useful error messages when forgetting to pass non-default connection objects, and the default one is unused.
25
+
26
+ ### Fixed
27
+
28
+ - JSS::Scopable::Scope#remove_target and #remove_limitation didn't always remove the item.
29
+
30
+ - JSS::Scopable::Scope: when calling the API for any reason, we now pass in the .api connection of the container. Not doing so when using a non-default connection object would cause problems.
31
+
32
+
33
+ ## \[1.5.1] - 2020-11-16
34
+
35
+ IMPORTANT: New minimum require ruby version is 2.3.0
36
+
37
+ Big thanks to @cybertunnel for many enhancements and fixes.
38
+
39
+ ### Added
40
+
41
+ - 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
42
+
43
+ - 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.
44
+
45
+ - JSS::Server#update_activation_code method was added
46
+
47
+ - Group#set_static and #set_smart can convert smart groups to static and static to smart
48
+
49
+ ### Changed
50
+
51
+ - Minimum required ruby version is 2.3.0
52
+
53
+ - 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.
54
+
55
+ - 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:
56
+ - The ids of JP API collection objects are Strings containing Integers.
57
+ - Boolean property names no longer start with 'is', tho aliases ending with '?' are still automatically created.
58
+
59
+ - Removed dependency on net-ldap, which hasn't been used in a while
60
+
61
+ - 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'`
62
+
63
+ ### Fixed
64
+
65
+ - PatchSource.fetch was totally broken, now fixed
66
+
67
+ - Category object's parse_category not properly referencing API object during execution
68
+
69
+ - Many small bugs and typos.
70
+
71
+ ## \[1.4.1] - 2020-10-01
72
+
73
+ ### Added
74
+
75
+ - 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.
76
+
77
+ ## \[1.4.0] - 2020-09-14
78
+
79
+ ### Added
80
+
81
+ - Class JSS::VPPAccount, implementing the 'vppacconts' endpoint.
82
+
83
+ - 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
84
+
85
+ - 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.
86
+
87
+ - 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.
88
+
89
+ - 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`
90
+
91
+ - 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.
92
+ See also the warning for #in_scope? above, which applies here as well.
93
+
94
+ - 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
95
+
96
+ ### Changed
97
+
98
+ - Prettier XML for JSS::APIObject#ppx
99
+
100
+ - Improved JSS::Validate.boolean. Accepts: true, false, 'true', 'false', 'yes', 'no', 't','f', 'y', or 'n' as Strings or Symbols, case insensitive
101
+
102
+ - The JSS::MacApplication class is more fully implemented
103
+
104
+ - JSS::Scopable::Scope now uses the word 'targets' consistently to match the UI's 'Targets' tab. The previous word 'inclusions' still works as before.
105
+
106
+ - 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.
107
+
108
+ ### Fixed
109
+
110
+ - 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
111
+
112
+ - 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.
113
+
114
+ - 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.
115
+
116
+ - The Jamf Pro API endpoint for bulk-deleting Departments changed from 'delete-departments' to 'delete-multiple'
117
+
118
+ ## \[1.3.3] - 2020-08-07
119
+
120
+ ### Fixed
121
+ - Regression where JSS::Package#required_processor= wouldn't take 'x86'
122
+
123
+ ## \[1.3.2] - 2020-07-31
124
+ Many thanks to @cybertunnel for adding a huge amount of code to get JSS::Policy fully implimented, as well as other fixes and updates!
125
+
126
+ ### Added
127
+ - new class JSS::DockItem
128
+ - new classes JSS::DirectoryBinding and JSS::DirectoryBindingType
129
+ - new class JSS::Printer
130
+ - new class JSS::DiskEncryptionConfiguration
131
+ - JSS::Policy:
132
+ - getters and setters for `#user_message_start` and `#user_message_end`
133
+ - `#set_management_account` and `#verify_management_password`
134
+ - `#add_dock_item` and `#remove_dock_item`
135
+ - `#directory_bindings`, `#add_directory_binding` and `#remove_directory_binding`
136
+ - `#add_printer` and `#remove_printer`
137
+ - `#reissue_key`, `#apply_encryption_configuration`, and `#remove_encryption_configuration`
138
+
139
+
140
+ ### Changed
141
+ - JSS::Package:
142
+ - no longer issues a warning when changing the file_name of a package
143
+ - Updated the CPU type string from 'x86' to 'Intel/x86'
144
+ - 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.
145
+
146
+ ## \[1.3.1] - 2020-06-21
147
+
148
+ ### Changed
149
+
150
+ - JSS::MobileDeviceApplication when using PrettyPrint (pp) in irb, no longer shows the base64 data for the ipa file.
151
+
152
+ - 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.
153
+
154
+ ### Fixed
155
+
156
+ - JSS::NetworkSegment.distribution_point= now takes nil or an empty string to unset the dist point.
157
+
158
+ ## \[1.3.0] - 2020-06-05
159
+
160
+ ### Added
161
+
162
+ - 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
163
+
164
+ - 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.
165
+
166
+ ### Changed
167
+
168
+ - JSS.expand_min_os now expands to macOS 10.30.x, which should hold us for a while
169
+
170
+ - 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.
171
+
172
+ - 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.
173
+
174
+ - 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.
175
+
176
+ ### Fixed
177
+
178
+ - A copy/paste bug in Jamf::Prestage.serials_for_prestage
179
+
180
+ ## \[1.2.15] - 2020-04-30
181
+
182
+ ### Fixed
183
+
184
+ - USER_CONF_FILE is always a pathname, never nil
185
+
186
+ - issues with Array#j_ci_* methods related to removing safe navigation
187
+
188
+ ## \[1.2.13] - 2020-04-29
189
+
190
+ ### Fixed
191
+
192
+ - Ruby 2.6 needs parens in more places than 2.3, apparently
193
+
194
+ ## \[1.2.12] - 2020-04-29
195
+
196
+ ### Added
197
+
198
+ - 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
199
+
200
+ ### Changed
201
+
202
+ - Removed all safe navigation operators (`&.`) for compatibility with older rubies (for a while longer anyway)
203
+
204
+
205
+ ## \[1.2.11] - 2020-04-26
206
+
207
+ ### Fixed
208
+
209
+ - Bug in Package#install that prevented installs from 'alt_download_url'.
210
+
7
211
  ## \[1.2.10] - 2020-04-25
8
212
 
9
213
  ### Added
@@ -55,7 +259,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
55
259
 
56
260
  - Classic API (JSS module)
57
261
  - Sitable objects now recognize the string "None" as meaning no site is assigned. Thanks @cybertunnel for this fix!
262
+
58
263
  - 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!
264
+
59
265
  - 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.
60
266
 
61
267
  - Jamf Pro API (Jamf module)
@@ -99,7 +305,8 @@ Note that the `last_inventory_update` value does NOT indicate such communication
99
305
 
100
306
  - 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.
101
307
  - `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
102
- 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.
308
+ 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
309
+
103
310
  - `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.
104
311
  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.
105
312
 
@@ -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
@@ -88,19 +86,14 @@ module Jamf
88
86
  # AUTOLOADING
89
87
  ##################################
90
88
 
91
- # Top-level API Abstract Classes
92
- autoload :JSONObject, 'jamf/api/abstract_classes/json_object'
93
- autoload :Resource, 'jamf/api/abstract_classes/resource'
94
- autoload :SingletonResource, 'jamf/api/abstract_classes/singleton_resource'
95
- autoload :CollectionResource, 'jamf/api/abstract_classes/collection_resource'
96
-
97
- # Abstract Classes used for JSONObject subclasses
98
- autoload :AdvancedSearch, 'jamf/api/abstract_classes/advanced_search'
99
- autoload :Prestage, 'jamf/api/abstract_classes/prestage'
100
- autoload :PrestageSkipSetupItems, 'jamf/api/abstract_classes/prestage_skip_setup_items'
89
+ # Top-level API Base Classes
90
+ autoload :JSONObject, 'jamf/api/base_classes/json_object'
91
+ autoload :Resource, 'jamf/api/base_classes/resource'
92
+ autoload :SingletonResource, 'jamf/api/base_classes/singleton_resource'
93
+ autoload :CollectionResource, 'jamf/api/base_classes/collection_resource'
101
94
 
102
- # Abstract Classes not used for JSONObject subclasses
103
- autoload :GenericReference, 'jamf/api/abstract_classes/generic_reference'
95
+ # Base Classes used for JSONObject subclasses
96
+ autoload :Prestage, 'jamf/api/base_classes/prestage'
104
97
 
105
98
  # MixIn Modules
106
99
  autoload :ChangeLog, 'jamf/api/mixins/change_log'
@@ -112,7 +105,11 @@ module Jamf
112
105
  autoload :UnCreatable, 'jamf/api/mixins/uncreatable'
113
106
  autoload :Immutable, 'jamf/api/mixins/immutable'
114
107
  autoload :UnDeletable, 'jamf/api/mixins/undeletable'
115
- autoload :Abstract, 'jamf/api/mixins/abstract'
108
+ autoload :BaseClass, 'jamf/api/mixins/base_class'
109
+ autoload :Pageable, 'jamf/api/mixins/pageable'
110
+ autoload :Filterable, 'jamf/api/mixins/filterable'
111
+ autoload :Sortable, 'jamf/api/mixins/sortable'
112
+ autoload :BulkDeletable, 'jamf/api/mixins/bulk_deletable'
116
113
 
117
114
  # Utility modules
118
115
  autoload :Validate, 'jamf/validate'
@@ -126,6 +123,7 @@ module Jamf
126
123
  autoload :Country, 'jamf/api/json_objects/country'
127
124
  autoload :Criterion, 'jamf/api/json_objects/criterion'
128
125
  autoload :DeviceEnrollmentDevice, 'jamf/api/json_objects/device_enrollment_device'
126
+ autoload :DeviceEnrollmentDeviceSyncState, 'jamf/api/json_objects/device_enrollment_device_sync_state'
129
127
  autoload :DeviceEnrollmentSyncStatus, 'jamf/api/json_objects/device_enrollment_sync_status'
130
128
  autoload :ExtensionAttributeValue, 'jamf/api/json_objects/extension_attribute_value'
131
129
  autoload :InstalledApplication, 'jamf/api/json_objects/installed_application'
@@ -135,6 +133,7 @@ module Jamf
135
133
  autoload :InstalledProvisioningProfile, 'jamf/api/json_objects/installed_provisioning_profile'
136
134
  autoload :InventoryPreloadExtensionAttribute, 'jamf/api/json_objects/inventory_preload_extension_attribute'
137
135
  autoload :IosDetails, 'jamf/api/json_objects/ios_details'
136
+ autoload :Locale, 'jamf/api/json_objects/locale'
138
137
  autoload :Location, 'jamf/api/json_objects/location'
139
138
  autoload :PrestageLocation, 'jamf/api/json_objects/prestage_location'
140
139
  autoload :PrestageSyncStatus, 'jamf/api/json_objects/prestage_sync_status'
@@ -147,16 +146,20 @@ module Jamf
147
146
  autoload :PrestagePurchasingData, 'jamf/api/json_objects/prestage_purchasing_data'
148
147
  autoload :PrestageScope, 'jamf/api/json_objects/prestage_scope'
149
148
  autoload :PrestageAssignment, 'jamf/api/json_objects/prestage_assignment'
149
+ autoload :TimeZone, 'jamf/api/json_objects/time_zone'
150
150
 
151
151
  # Subclasses of SingletonResource
152
152
  autoload :ClientCheckInSettings, 'jamf/api/resources/singleton_resources/client_checkin_settings'
153
153
  autoload :ReEnrollmentSettings, 'jamf/api/resources/singleton_resources/reenrollment_settings'
154
154
  autoload :AppStoreCountryCodes, 'jamf/api/resources/singleton_resources/app_store_country_codes'
155
+ autoload :TimeZones, 'jamf/api/resources/singleton_resources/time_zones'
156
+ autoload :Locales, 'jamf/api/resources/singleton_resources/locales'
155
157
 
156
158
  # Subclasses of CollectionResource
157
159
  autoload :AdvancedMobileDeviceSearch, 'jamf/api/resources/collection_resources/advanced_mobile_device_search'
158
160
  autoload :AdvancedUserSearch, 'jamf/api/resources/collection_resources/advanced_user_search'
159
161
  autoload :Attachment, 'jamf/api/resources/collection_resources/attachment'
162
+ autoload :Category, 'jamf/api/resources/collection_resources/category'
160
163
  autoload :Building, 'jamf/api/resources/collection_resources/building'
161
164
  autoload :Computer, 'jamf/api/resources/collection_resources/computer'
162
165
  autoload :ComputerPrestage, 'jamf/api/resources/collection_resources/computer_prestage'
@@ -168,7 +171,6 @@ module Jamf
168
171
  autoload :MobileDevicePrestage, 'jamf/api/resources/collection_resources/mobile_device_prestage'
169
172
  autoload :Site, 'jamf/api/resources/collection_resources/site'
170
173
  autoload :Script, 'jamf/api/resources/collection_resources/script'
171
- autoload :TimeZone, 'jamf/api/resources/collection_resources/time_zone'
172
174
 
173
175
  # other classes used as attributes inside the resource classes
174
176
  autoload :IPAddress, 'jamf/api/attribute_classes/ip_address'
@@ -0,0 +1,613 @@
1
+ # Copyright 2020 Pixar
2
+
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "Apache License")
5
+ # with the following modification; you may not use this file except in
6
+ # compliance with the Apache License and the following modification to it:
7
+ # Section 6. Trademarks. is deleted and replaced with:
8
+ #
9
+ # 6. Trademarks. This License does not grant permission to use the trade
10
+ # names, trademarks, service marks, or product names of the Licensor
11
+ # and its affiliates, except as required to comply with Section 4(c) of
12
+ # the License and to reproduce the content of the NOTICE file.
13
+ #
14
+ # You may obtain a copy of the Apache License at
15
+ #
16
+ # http://www.apache.org/licenses/LICENSE-2.0
17
+ #
18
+ # Unless required by applicable law or agreed to in writing, software
19
+ # distributed under the Apache License with the above modification is
20
+ # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
21
+ # KIND, either express or implied. See the Apache License for the specific
22
+ # language governing permissions and limitations under the Apache License.
23
+ #
24
+ #
25
+
26
+ # The module
27
+ module Jamf
28
+
29
+ # A Collection Resource in Jamf Pro
30
+ #
31
+ # See {Jamf::Resource} for general info about API resources.
32
+ #
33
+ # Collection resources have more than one resource within them, and those
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 retrieved as an Array.
36
+ # When the whole collection is retrieved, the result may be cached for future
37
+ # use.
38
+ #
39
+ # # Subclassing
40
+ #
41
+ # ## Creatability, & Deletability
42
+ #
43
+ # Sometimes the API doesn't support creation of new members of the collection.
44
+ # If that's the case, just extend the subclass with Jamf::UnCreatable
45
+ # and the '.create' class method will raise an error.
46
+ #
47
+ # Similarly for deletion of members: if the API doesn't have a way to delete
48
+ # them, extend the subclass with Jamf::UnDeletable
49
+ #
50
+ # See also Jamf::JSONObject, which talks about extending subclasses
51
+ # with Jamf::Immutable
52
+ #
53
+ # ## Bulk Deletion
54
+ #
55
+ # Some collection resources have a resource for bulk deletion, passing in
56
+ # a JSON array of ids to delete.
57
+ #
58
+ # If so, just define a BULK_DELETE_RSRC, and the .delete class method
59
+ # will use it, rather than making multiple calls to delete individual
60
+ # items. See Jamf::Category::BULK_DELETE_RSRC for an example
61
+ #
62
+ # @abstract
63
+ #
64
+ class CollectionResource < Jamf::Resource
65
+
66
+ extend Jamf::BaseClass
67
+ extend Jamf::Pageable
68
+ extend Jamf::Sortable
69
+ extend Jamf::Filterable
70
+
71
+ include Comparable
72
+
73
+ # Public Class Methods
74
+ #####################################
75
+
76
+ # @return [Array<Symbol>] the attribute names that are marked as identifiers
77
+ #
78
+ def self.identifiers
79
+ self::OBJECT_MODEL.select { |_attr, deets| deets[:identifier] }.keys
80
+ end
81
+
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.
168
+ #
169
+ # #### Caching
170
+ #
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.
174
+ #
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.
180
+ #
181
+ # Some other methods, e.g. .all_names, will generate or use this cached Array
182
+ # to derive their values.
183
+ #
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.
186
+ #
187
+ #######
188
+ #
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.
193
+ #
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)
217
+ stop_if_base_class
218
+
219
+ # use the cache if not paging, filtering or sorting
220
+ return cached_all(refresh, instantiate, cnx) if !paged && !sort && !filter
221
+
222
+ # we are sorting, filtering or paging
223
+ sort = parse_collection_sort(sort)
224
+ filter = parse_collection_filter(filter)
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
234
+
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
257
+
258
+
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
263
+ end
264
+
265
+ # An array of the ids for all collection members. According to the
266
+ # specs ALL collection resources must have an ID, which is used in the
267
+ # resource path.
268
+ #
269
+ # NOTE: This method uses the cached version of .all
270
+ #
271
+ # @param refresh (see .all)
272
+ #
273
+ # @param cnx (see .all)
274
+ #
275
+ # @return [Array<Integer>]
276
+ #
277
+ def self.all_ids(refresh = false, cnx: Jamf.cnx)
278
+ all(refresh: refresh, cnx: cnx).map { |m| m[:id] }
279
+ end
280
+
281
+ # A Hash of all members of this collection where the keys are some
282
+ # identifier and values are any other attribute.
283
+ #
284
+ # NOTE: This method uses the cached version of .all
285
+ #
286
+ # @param ident [Symbol] An identifier of this Class, used as the key
287
+ # for the mapping Hash. Aliases are acceptable, e.g. :sn for :serialNumber
288
+ #
289
+ # @param to [Symbol] The attribute to which the ident will be mapped.
290
+ # Aliases are acceptable, e.g. :name for :displayName
291
+ #
292
+ # @param refresh (see .all)
293
+ #
294
+ # @param cnx (see .all)
295
+ #
296
+ # @return [Hash {Symbol: Object}] A Hash of identifier mapped to attribute
297
+ #
298
+ def self.map_all(ident, to:, cnx: Jamf.cnx, refresh: false)
299
+ real_ident = attr_key_for_alias ident
300
+ raise Jamf::InvalidDataError, "No identifier #{ident} for class #{self}" unless
301
+ identifiers.include? real_ident
302
+
303
+ real_to = attr_key_for_alias to
304
+ raise Jamf::NoSuchItemError, "No attribute #{to} for class #{self}" unless self::OBJECT_MODEL.key? real_to
305
+
306
+ list = all refresh: refresh, cnx: cnx
307
+ to_class = self::OBJECT_MODEL[real_to][:class]
308
+ mapped = list.map do |i|
309
+ [
310
+ i[real_ident],
311
+ to_class.is_a?(Symbol) ? i[real_to] : to_class.new(i[real_to])
312
+ ]
313
+ end # do i
314
+ mapped.to_h
315
+ end
316
+
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.
320
+ #
321
+ # In general you should use this if the form:
322
+ #
323
+ # raw_data identifier: value
324
+ #
325
+ # where identifier is one of the available identifiers for this class
326
+ # like id:, name:, serialNumber: etc.
327
+ #
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
331
+ #
332
+ # raw_data some_value
333
+ #
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
337
+ #
338
+ # If no matching object is found, nil is returned.
339
+ #
340
+ # Everything except :id is treated as a case-insensitive String
341
+ #
342
+ # @param value [String, Integer] The identifier value to search fors
343
+ #
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
347
+ #
348
+ # @param cnx: (see .all)
349
+ #
350
+ # @return [Hash, nil] the basic dataset of the matching object,
351
+ # or nil if it doesn't exist
352
+ #
353
+ def self.raw_data(value = nil, cnx: Jamf.cnx, **ident_and_val)
354
+ stop_if_base_class
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?
372
+
373
+ identifiers.each do |ident|
374
+ next if ident == :id
375
+
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]
400
+
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 }
403
+
404
+ nil
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
449
+
450
+ # Bu default, subclasses are creatable, i.e. new instances can be created
451
+ # with .create, and added to the JSS with .save
452
+ # If a subclass is NOT creatble for any reason, just add
453
+ # extend Jamf::UnCreatable
454
+ # and this method will return false
455
+ #
456
+ # @return [Boolean]
457
+ def self.creatable?
458
+ true
459
+ end
460
+
461
+ # Make a new thing to be added to the API
462
+ def self.create(**params)
463
+ stop_if_base_class
464
+
465
+ raise Jamf::UnsupportedError, "#{self}'s are not currently creatable via the API" unless creatable?
466
+
467
+ # Which connection to use
468
+ cnx = params.delete :cnx
469
+ cnx ||= Jamf.cnx
470
+
471
+ params.delete :id # no such animal when .creating
472
+ params.keys.each do |param|
473
+ raise ArgumentError, "Unknown parameter: #{param}" unless self::OBJECT_MODEL.key? param
474
+
475
+ if params[param].is_a? Array
476
+ params[param].map! { |val| validate_attr param, val, cnx: cnx }
477
+ else
478
+ params[param] = validate_attr param, params[param], cnx: cnx
479
+ end
480
+ end
481
+
482
+ params[:creating_from_create] = true
483
+ new params, cnx: cnx
484
+ end
485
+
486
+ # Retrieve a member of a CollectionResource from the API
487
+ #
488
+ # To create new members to be added to the JSS, use
489
+ # {Jamf::CollectionResource.create}
490
+ #
491
+ # You must know the specific identifier attribute you're looking up, e.g.
492
+ # :id or :name or :udid, (or an aliase thereof) then you can specify it like
493
+ # `.fetch name: 'somename'`, or `.fetch udid: 'someudid'`
494
+ #
495
+ # @param cnx[Jamf::Connection] the connection to use to fetch the object
496
+ #
497
+ # @param ident_and_val[Hash] an identifier attribute key and a search value
498
+ #
499
+ # @return [CollectionResource] The ruby-instance of a Jamf object
500
+ #
501
+ def self.fetch(random = nil, cnx: Jamf.cnx, **ident_and_val)
502
+ stop_if_base_class
503
+ ident, value = ident_and_val.first
504
+ data =
505
+ if random
506
+ all.sample
507
+ elsif ident && value
508
+ raw_data(cnx: cnx, **ident_and_val)
509
+ end
510
+ raise Jamf::NoSuchItemError, "No matching #{self}" unless data
511
+
512
+ new data, cnx: cnx
513
+ end # fetch
514
+
515
+ # By default, CollectionResource subclass instances are deletable.
516
+ # If not, just extend the subclass with Jamf::UnDeletable, and this
517
+ # will return false, and .delete & #delete will raise errors
518
+ def self.deletable?
519
+ true
520
+ end
521
+
522
+ # Delete one or more objects by id
523
+ #
524
+ # @param ids [Array<String,Integer>] The ids to delete
525
+ #
526
+ # @param cnx [Jamf::Connection] The connection to use, default: Jamf.cnx
527
+ #
528
+ # @return [Array<Jamf::Connection::APIError::ErrorInfo] Info about any ids
529
+ # that failed to be deleted.
530
+ #
531
+ def self.delete(*ids, cnx: Jamf.cnx)
532
+ raise Jamf::UnsupportedError, "Deleting #{self} objects is not currently supported" unless deletable?
533
+
534
+ return bulk_delete(ids, cnx: Jamf.cnx) if ancestors.include? Jamf::BulkDeletable
535
+
536
+ errs = []
537
+ ids.each do |id_to_delete|
538
+ begin
539
+ cnx.delete "#{rsrc_path}/#{id_to_delete}"
540
+ rescue Jamf::Connection::APIError => e
541
+ raise e unless e.httpStatus == 404
542
+
543
+ errs += e.errors
544
+ end # begin
545
+ end # ids.each
546
+ errs
547
+ end
548
+
549
+ # Private Class Methods
550
+ #####################################
551
+
552
+ # TODO: better pluralizing?
553
+ #
554
+ def self.create_list_methods(attr_name, attr_def)
555
+ list_method_name = "all_#{attr_name}s"
556
+
557
+ define_singleton_method(list_method_name) do |refresh = false, cnx: Jamf.cnx|
558
+ all_list = all(refresh: refresh, cnx: cnx)
559
+ if attr_def[:class].is_a? Symbol
560
+ all_list.map { |i| i[attr_name] }.uniq
561
+ else
562
+ all_list.map { |i| attr_def[:class].new i[attr_name] }
563
+ end
564
+ end # define_singleton_method
565
+
566
+ return unless attr_def[:aliases]
567
+
568
+ # aliases - TODO: is there a more elegant way?
569
+ attr_def[:aliases].each do |a|
570
+ define_singleton_method("all_#{a}s") do |refresh = false, cnx: Jamf.cnx|
571
+ send list_method_name, refresh, cnx: cnx
572
+ end # define_singleton_method
573
+ end # each alias
574
+ end # create_list_methods
575
+ private_class_method :create_list_methods
576
+
577
+
578
+ # Instance Methods
579
+ #####################################
580
+
581
+ def exist?
582
+ !@id.nil?
583
+ end
584
+
585
+ def rsrc_path
586
+ return unless exist?
587
+
588
+ "#{self.class.rsrc_path}/#{@id}"
589
+ end
590
+
591
+ def delete
592
+ raise Jamf::UnsupportedError, "Deleting #{self} objects is not currently supported" unless self.class.deletable?
593
+
594
+ @cnx.delete rsrc_path
595
+ end
596
+
597
+ # Two collection resource objects are the same if their id's are the same
598
+ def <=>(other)
599
+ id <=> other.id
600
+ end
601
+
602
+ # Private Instance Methods
603
+ ############################################
604
+ private
605
+
606
+ def create_in_jamf
607
+ result = @cnx.post self.class.rsrc_path, to_jamf
608
+ @id = result[:id]
609
+ end
610
+
611
+ end # class CollectionResource
612
+
613
+ end # module JAMF