ruby-jss 1.2.3 → 1.2.4a1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. checksums.yaml +4 -4
  2. data/lib/jamf.rb +169 -0
  3. data/lib/jamf/api/abstract_classes/collection_resource.rb +422 -0
  4. data/lib/jamf/api/abstract_classes/generic_reference.rb +145 -0
  5. data/lib/jamf/api/abstract_classes/json_object.rb +1074 -0
  6. data/lib/jamf/api/abstract_classes/prestage.rb +219 -0
  7. data/lib/jamf/api/abstract_classes/prestage_skip_setup_items.rb +126 -0
  8. data/lib/jamf/api/abstract_classes/resource.rb +250 -0
  9. data/lib/jamf/api/abstract_classes/singleton_resource.rb +87 -0
  10. data/lib/jamf/api/attribute_classes/ip_address.rb +66 -0
  11. data/lib/jamf/api/attribute_classes/timestamp.rb +144 -0
  12. data/lib/jamf/api/connection.rb +734 -0
  13. data/lib/jamf/api/connection/api_error.rb +111 -0
  14. data/lib/jamf/api/connection/api_error_styleguide.rb +96 -0
  15. data/lib/jamf/api/connection/token.rb +220 -0
  16. data/lib/jamf/api/json_objects/account_prefs.rb +79 -0
  17. data/lib/jamf/api/json_objects/android_details.rb +139 -0
  18. data/lib/jamf/api/json_objects/appletv_details.rb +110 -0
  19. data/lib/jamf/api/json_objects/attachment.rb +68 -0
  20. data/lib/jamf/api/json_objects/cellular_network.rb +151 -0
  21. data/lib/jamf/api/json_objects/change_log_entry.rb +77 -0
  22. data/lib/jamf/api/json_objects/computer_prestage_skip_setup_items.rb +67 -0
  23. data/lib/jamf/api/json_objects/country.rb +51 -0
  24. data/lib/jamf/api/json_objects/extension_attribute_value.rb +128 -0
  25. data/lib/jamf/api/json_objects/installed_application.rb +59 -0
  26. data/lib/jamf/api/json_objects/installed_certificate.rb +53 -0
  27. data/lib/jamf/api/json_objects/installed_configuration_profile.rb +67 -0
  28. data/lib/jamf/api/json_objects/installed_ebook.rb +58 -0
  29. data/lib/jamf/api/json_objects/installed_provisioning_profile.rb +59 -0
  30. data/lib/jamf/api/json_objects/inventory_preload_extension_attribute.rb +52 -0
  31. data/lib/jamf/api/json_objects/ios_details.rb +244 -0
  32. data/lib/jamf/api/json_objects/location.rb +95 -0
  33. data/lib/jamf/api/json_objects/md_prestage_name.rb +57 -0
  34. data/lib/jamf/api/json_objects/md_prestage_names.rb +82 -0
  35. data/lib/jamf/api/json_objects/md_prestage_skip_setup_items.rb +165 -0
  36. data/lib/jamf/api/json_objects/mobile_device_details.rb +219 -0
  37. data/lib/jamf/api/json_objects/mobile_device_security.rb +101 -0
  38. data/lib/jamf/api/json_objects/prestage_assignment.rb +61 -0
  39. data/lib/jamf/api/json_objects/prestage_location.rb +104 -0
  40. data/lib/jamf/api/json_objects/prestage_purchasing_data.rb +132 -0
  41. data/lib/jamf/api/json_objects/prestage_scope.rb +54 -0
  42. data/lib/jamf/api/json_objects/prestage_sync_status.rb +63 -0
  43. data/lib/jamf/api/json_objects/purchasing_data.rb +125 -0
  44. data/lib/jamf/api/mixins/abstract.rb +58 -0
  45. data/lib/jamf/api/mixins/bulk_deletable.rb +39 -0
  46. data/lib/jamf/api/mixins/change_log.rb +136 -0
  47. data/lib/jamf/api/mixins/extendable.rb +75 -0
  48. data/lib/jamf/api/mixins/immutable.rb +39 -0
  49. data/lib/jamf/api/mixins/locatable.rb +124 -0
  50. data/lib/jamf/api/mixins/lockable.rb +48 -0
  51. data/lib/jamf/api/mixins/referable.rb +92 -0
  52. data/lib/jamf/api/mixins/searchable.rb +202 -0
  53. data/lib/jamf/api/mixins/uncreatable.rb +40 -0
  54. data/lib/jamf/api/mixins/undeletable.rb +40 -0
  55. data/lib/jamf/api/resources/collection_resources/account.rb +163 -0
  56. data/lib/jamf/api/resources/collection_resources/building.rb +114 -0
  57. data/lib/jamf/api/resources/collection_resources/category.rb +82 -0
  58. data/lib/jamf/api/resources/collection_resources/computer.rb +49 -0
  59. data/lib/jamf/api/resources/collection_resources/computer_prestage.rb +80 -0
  60. data/lib/jamf/api/resources/collection_resources/department.rb +79 -0
  61. data/lib/jamf/api/resources/collection_resources/extension_attribute.rb +45 -0
  62. data/lib/jamf/api/resources/collection_resources/inventory_preload_record.rb +274 -0
  63. data/lib/jamf/api/resources/collection_resources/md_prestage.rb +139 -0
  64. data/lib/jamf/api/resources/collection_resources/mobile_device.rb +315 -0
  65. data/lib/jamf/api/resources/collection_resources/script.rb +190 -0
  66. data/lib/jamf/api/resources/collection_resources/site.rb +77 -0
  67. data/lib/jamf/api/resources/singleton_resources/app_store_country_codes.rb +131 -0
  68. data/lib/jamf/api/resources/singleton_resources/authorization.rb +88 -0
  69. data/lib/jamf/api/resources/singleton_resources/client_checkin_settings.rb +139 -0
  70. data/lib/jamf/api/resources/singleton_resources/reenrollment_settings.rb +95 -0
  71. data/lib/jamf/client.rb +301 -0
  72. data/lib/jamf/client/jamf_binary.rb +132 -0
  73. data/lib/jamf/client/jamf_helper.rb +298 -0
  74. data/lib/jamf/client/management_action.rb +114 -0
  75. data/lib/jamf/compatibility.rb +88 -0
  76. data/lib/jamf/composer.rb +190 -0
  77. data/lib/jamf/configuration.rb +281 -0
  78. data/lib/jamf/exceptions.rb +107 -0
  79. data/lib/jamf/ruby_extensions.rb +36 -0
  80. data/lib/jamf/ruby_extensions/array.rb +35 -0
  81. data/lib/jamf/ruby_extensions/array/predicates.rb +46 -0
  82. data/lib/jamf/ruby_extensions/array/utils.rb +47 -0
  83. data/lib/jamf/ruby_extensions/filetest.rb +32 -0
  84. data/lib/jamf/ruby_extensions/filetest/predicates.rb +46 -0
  85. data/lib/jamf/ruby_extensions/hash.rb +33 -0
  86. data/lib/jamf/ruby_extensions/hash/backports.rb +92 -0
  87. data/lib/jamf/ruby_extensions/ipaddr.rb +37 -0
  88. data/lib/jamf/ruby_extensions/ipaddr/utils.rb +95 -0
  89. data/lib/jamf/ruby_extensions/object.rb +30 -0
  90. data/lib/jamf/ruby_extensions/object/predicates.rb +51 -0
  91. data/lib/jamf/ruby_extensions/pathname.rb +39 -0
  92. data/lib/jamf/ruby_extensions/pathname/predicates.rb +50 -0
  93. data/lib/jamf/ruby_extensions/pathname/utils.rb +75 -0
  94. data/lib/jamf/ruby_extensions/string.rb +35 -0
  95. data/lib/jamf/ruby_extensions/string/backports.rb +66 -0
  96. data/lib/jamf/ruby_extensions/string/conversions.rb +65 -0
  97. data/lib/jamf/ruby_extensions/string/predicates.rb +47 -0
  98. data/lib/jamf/utility.rb +423 -0
  99. data/lib/jamf/validate.rb +224 -0
  100. data/lib/jamf/version.rb +32 -0
  101. data/lib/jpapi.rb +26 -0
  102. data/lib/jss/version.rb +1 -1
  103. metadata +104 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: db7db3a85a2ce09946ec84f4b254f948f566daa8f5e0e9b8f939c9efb8eefef1
4
- data.tar.gz: 1edb35f947212312c165f2ff0fa1ed3c4a059fb58f0abb5de9bea8877bfab3a0
3
+ metadata.gz: 25d16b4c34d2790bdf6175543f81a2125e80ddf0c2c49cbb76d340b76dd562df
4
+ data.tar.gz: a09cfceaaecd6c3109cafbd0d7912a1c66326c6c5ddaae479a4260816c50ede9
5
5
  SHA512:
6
- metadata.gz: d8d61d9f03d99a289f0b5c4dd0fefe246f824fac21003e905bbc5dd25c6e2ff84bffbc6eb3244086db8af143ca39722e738711c82cf5f8b9174c6183d8c40d03
7
- data.tar.gz: db954707cef1f340613ecb27657d38673e6117ac4b9db05ba3025bab6ba4446e220808346856e4c50003eca58fc84dfb8c6e640d2a41e3a32987e9dc5766a99a
6
+ metadata.gz: fb2e1e6fb575db16ade4b819bbea3cfb73ce6956c1012569f2be10d757ed05f31c4671605065a6c619667afa3a2540119af0afee5cf506d1ae8455d4aa42d4c0
7
+ data.tar.gz: 4af7406b1457c0c70d4873578920938b942ed7b6ea15702898f74450453720f30b24803addf8762426f617b7e223ef34ae2d51453df5cf283da35c32d27a8854
@@ -0,0 +1,169 @@
1
+ # Copyright 2019 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
+ #
27
+ # JAMF, A Ruby module for interacting with a JAMF Pro Server via the JAMF API
28
+ #
29
+
30
+ ### Standard Libraries
31
+ require 'English'
32
+ require 'json'
33
+ require 'yaml'
34
+ require 'pathname'
35
+ require 'time'
36
+ require 'singleton'
37
+ require 'open-uri'
38
+ require 'ipaddr'
39
+ require 'base64'
40
+ require 'shellwords'
41
+ require 'digest'
42
+ require 'open3'
43
+
44
+
45
+ ### Gems
46
+ require 'rest-client'
47
+ require 'plist'
48
+
49
+ # Used, among other places, in the Connection::APIError class
50
+ require 'immutable-struct'
51
+
52
+ # TODO: needed?
53
+ # require 'recursive-open-struct'
54
+
55
+ # non-api parts of Jamf module
56
+ require 'jamf/configuration'
57
+ require 'jamf/exceptions'
58
+ require 'jamf/utility'
59
+ require 'jamf/validate'
60
+ require 'jamf/version'
61
+
62
+ # backports and extensions to existing Ruby classes
63
+ require 'jamf/compatibility'
64
+ require 'jamf/ruby_extensions'
65
+
66
+ # API connection
67
+ require 'jamf/api/connection'
68
+
69
+
70
+ # The main module.
71
+ # See README.md
72
+ #
73
+ module Jamf
74
+
75
+ # The minimum Ruby version that works with this gem
76
+ # 2.3 allows us to start using some nice features like the safe-navigation
77
+ # operator and Array#dig & Hash#dig, and such.
78
+ #
79
+ # For a list of features, see https://github.com/ruby/ruby/blob/v2_3_0/NEWS
80
+ # and http://nithinbekal.com/posts/ruby-2-3-features/
81
+ #
82
+ MINIMUM_RUBY_VERSION = '2.3'.freeze
83
+
84
+ if Gem::Version.new(RUBY_VERSION) < Gem::Version.new(MINIMUM_RUBY_VERSION)
85
+ raise "Can't use the JAMF module, ruby itself must be version #{MINIMUM_RUBY_VERSION} or greater."
86
+ end
87
+
88
+ # AUTOLOADING
89
+ ##################################
90
+
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 :Prestage, 'jamf/api/abstract_classes/prestage'
99
+ autoload :PrestageSkipSetupItems, 'jamf/api/abstract_classes/prestage_skip_setup_items'
100
+
101
+ # Abstract Classes not used for JSONObject subclasses
102
+ autoload :GenericReference, 'jamf/api/abstract_classes/generic_reference'
103
+
104
+ # MixIn Modules
105
+ autoload :ChangeLog, 'jamf/api/mixins/change_log'
106
+ autoload :Extendable, 'jamf/api/mixins/extendable'
107
+ autoload :Locatable, 'jamf/api/mixins/locatable'
108
+ autoload :Referable, 'jamf/api/mixins/referable'
109
+ autoload :Searchable, 'jamf/api/mixins/searchable'
110
+ autoload :Lockable, 'jamf/api/mixins/lockable'
111
+ autoload :UnCreatable, 'jamf/api/mixins/uncreatable'
112
+ autoload :Immutable, 'jamf/api/mixins/immutable'
113
+ autoload :UnDeletable, 'jamf/api/mixins/undeletable'
114
+ autoload :Abstract, 'jamf/api/mixins/abstract'
115
+
116
+ # Utility modules
117
+ autoload :Validate, 'jamf/validate'
118
+
119
+ # Subclasses of JSONObject, but not Resource
120
+ autoload :AndroidDetails, 'jamf/api/json_objects/android_details'
121
+ autoload :AppleTVDetails, 'jamf/api/json_objects/appletv_details'
122
+ autoload :CellularNetwork, 'jamf/api/json_objects/cellular_network'
123
+ autoload :ChangeLogEntry, 'jamf/api/json_objects/change_log_entry'
124
+ autoload :ComputerPrestageSkipSetupItems, 'jamf/api/json_objects/computer_prestage_skip_setup_items'
125
+ autoload :Country, 'jamf/api/json_objects/country'
126
+ autoload :ExtensionAttributeValue, 'jamf/api/json_objects/extension_attribute_value'
127
+ autoload :InstalledApplication, 'jamf/api/json_objects/installed_application'
128
+ autoload :InstalledCertificate, 'jamf/api/json_objects/installed_certificate'
129
+ autoload :InstalledConfigurationProfile, 'jamf/api/json_objects/installed_configuration_profile'
130
+ autoload :InstalledEBook, 'jamf/api/json_objects/installed_ebook'
131
+ autoload :InstalledProvisioningProfile, 'jamf/api/json_objects/installed_provisioning_profile'
132
+ autoload :InventoryPreloadExtensionAttribute, 'jamf/api/json_objects/inventory_preload_extension_attribute'
133
+ autoload :IosDetails, 'jamf/api/json_objects/ios_details'
134
+ autoload :Location, 'jamf/api/json_objects/location'
135
+ autoload :PrestageLocation, 'jamf/api/json_objects/prestage_location'
136
+ autoload :PrestageSyncStatus, 'jamf/api/json_objects/prestage_sync_status'
137
+ autoload :MobileDeviceDetails, 'jamf/api/json_objects/mobile_device_details'
138
+ autoload :MobileDeviceSecurity, 'jamf/api/json_objects/mobile_device_security'
139
+ autoload :MobileDevicePrestageName, 'jamf/api/json_objects/md_prestage_name'
140
+ autoload :MobileDevicePrestageNames, 'jamf/api/json_objects/md_prestage_names'
141
+ autoload :MobileDevicePrestageSkipSetupItems, 'jamf/api/json_objects/md_prestage_skip_setup_items'
142
+ autoload :PurchasingData, 'jamf/api/json_objects/purchasing_data'
143
+ autoload :PrestagePurchasingData, 'jamf/api/json_objects/prestage_purchasing_data'
144
+ autoload :PrestageScope, 'jamf/api/json_objects/prestage_scope'
145
+ autoload :PrestageAssignment, 'jamf/api/json_objects/prestage_assignment'
146
+
147
+ # Subclasses of SingletonResource
148
+ autoload :ClientCheckInSettings, 'jamf/api/resources/singleton_resources/client_checkin_settings'
149
+ autoload :ReEnrollmentSettings, 'jamf/api/resources/singleton_resources/reenrollment_settings'
150
+ autoload :AppStoreCountryCodes, 'jamf/api/resources/singleton_resources/app_store_country_codes'
151
+
152
+ # Subclasses of CollectionResource
153
+ autoload :Attachment, 'jamf/api/resources/collection_resources/attachment'
154
+ autoload :Building, 'jamf/api/resources/collection_resources/building'
155
+ autoload :Computer, 'jamf/api/resources/collection_resources/computer'
156
+ autoload :ComputerPrestage, 'jamf/api/resources/collection_resources/computer_prestage'
157
+ autoload :Department, 'jamf/api/resources/collection_resources/department'
158
+ autoload :ExtensionAttribute, 'jamf/api/resources/collection_resources/extension_attribute'
159
+ autoload :InventoryPreloadRecord, 'jamf/api/resources/collection_resources/inventory_preload_record'
160
+ autoload :MobileDevice, 'jamf/api/resources/collection_resources/mobile_device'
161
+ autoload :MobileDevicePrestage, 'jamf/api/resources/collection_resources/md_prestage'
162
+ autoload :Site, 'jamf/api/resources/collection_resources/site'
163
+ autoload :Script, 'jamf/api/resources/collection_resources/script'
164
+
165
+ # other classes used as attributes inside the resource classes
166
+ autoload :IPAddress, 'jamf/api/attribute_classes/ip_address'
167
+ autoload :Timestamp, 'jamf/api/attribute_classes/timestamp'
168
+
169
+ end # module
@@ -0,0 +1,422 @@
1
+ # Copyright 2019 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 fetched as an Array.
36
+ # When the whole collection is fetched, the result is cached for future use.
37
+ #
38
+ # # Subclassing
39
+ #
40
+ # ## Creatability, & Deletability
41
+ #
42
+ # Sometimes the API doesn't support creation of new members of the collection.
43
+ # If that's the case, just extend the subclass with Jamf::UnCreatable
44
+ # and the '.create' class method will raise an error.
45
+ #
46
+ # Similarly for deletion of members: if the API doesn't have a way to delete
47
+ # them, extend the subclass with Jamf::UnDeletable
48
+ #
49
+ # See also Jamf::JSONObject, which talks about extending subclasses
50
+ # with Jamf::Immutable
51
+ #
52
+ # ## Bulk Deletion
53
+ #
54
+ # Some collection resources have a resource for bulk deletion, passing in
55
+ # a JSON array of ids to delete.
56
+ #
57
+ # If so, just define a BULK_DELETE_RSRC, and the .delete class method
58
+ # will use it, rather than making multiple calls to delete individual
59
+ # items. See Jamf::Category::BULK_DELETE_RSRC for an example
60
+ #
61
+ # @abstract
62
+ #
63
+ class CollectionResource < Jamf::Resource
64
+
65
+ extend Jamf::Abstract
66
+ include Comparable
67
+
68
+ # Public Class Methods
69
+ #####################################
70
+
71
+ # @return [Array<Symbol>] the attribute names that are marked as identifiers
72
+ #
73
+ def self.identifiers
74
+ self::OBJECT_MODEL.select { |_attr, deets| deets[:identifier] }.keys
75
+ end
76
+
77
+ # An array of attribute names that are required when
78
+ # making new CollectionResources
79
+ # See the OBJECT_MODEL documentation in {Jamf::JSONObject}
80
+ def self.required_attributes
81
+ self::OBJECT_MODEL.select { |_attr, deets| deets[:required] }.keys
82
+ end
83
+
84
+ # The Collection members Array for this class, retrieved from
85
+ # the RSRC_PATH as Parsed JSON, but not instantiated into instances
86
+ # unless instantiate: is truthy.
87
+ #
88
+ # E.g. for {Jamf::Settings::Building}, this would be the Array of Hashes
89
+ # returned by GETing the resource .../settings/obj/building
90
+ #
91
+ # This Array is cached in the {Jamf::Connection} instance used to
92
+ # retrieve it, and future calls to .all will return the cached Array
93
+ # unless refresh is truthy.
94
+ #
95
+ # TODO: Investigate https://www.rubydoc.info/github/mloughran/api_cache
96
+ #
97
+ # @param refresh[Boolean] re-read the data from the API?
98
+ #
99
+ # @param cnx[Jamf::Connection] an API connection to use for the query.
100
+ # Defaults to the corrently active connection. See {Jamf::Connection}
101
+ #
102
+ # @param instantiate[Boolean] The Array contains instances of this class
103
+ # rather than the JSON Hashes from the API.
104
+ #
105
+ # @return [Array<Object>] An Array of all objects of this class in the JSS.
106
+ #
107
+ def self.all(refresh = false, cnx: Jamf.cnx, instantiate: false)
108
+ validate_not_abstract
109
+ cnx.collection_cache[self] = nil if refresh
110
+ return cnx.collection_cache[self] if cnx.collection_cache[self]
111
+
112
+ raw = cnx.get rsrc_path
113
+ cnx.collection_cache[self] =
114
+ if raw.is_a?(Hash) && raw[:results]
115
+ raw[:results]
116
+ else
117
+ raw
118
+ end
119
+
120
+ return cnx.collection_cache[self] unless instantiate
121
+
122
+ cnx.collection_cache[self].map { |m| new m }
123
+ end
124
+
125
+ # An array of the ids for all collection members
126
+ #
127
+ # @param refresh (see .all)
128
+ #
129
+ # @param cnx (see .all)
130
+ #
131
+ # @return [Array<Integer>]
132
+ #
133
+ def self.all_ids(refresh = false, cnx: Jamf.cnx)
134
+ all(refresh, cnx: cnx).map { |m| m[:id] }
135
+ end
136
+
137
+ # rubocop:disable Naming/UncommunicativeMethodParamName
138
+
139
+ # A Hash of all members of this collection where the keys are some
140
+ # identifier and values are any other attribute.
141
+ #
142
+ # @param ident [Symbol] An identifier of this Class, used as the key
143
+ # for the mapping Hash.
144
+ #
145
+ # @param to [Symbol] The attribute to which the ident will be mapped
146
+ #
147
+ # @param refresh (see .all)
148
+ #
149
+ # @param cnx (see .all)
150
+ #
151
+ # @return [Hash {Symbol: Object}] A Hash of identifier mapped to attribute
152
+ #
153
+ def self.map_all(ident, to:, cnx: Jamf.cnx, refresh: false)
154
+ raise Jamf::InvalidDataError, "No identifier #{ident} for class #{self}" unless
155
+ identifiers.include? ident
156
+
157
+ raise Jamf::NoSuchItemError, "No attribute #{to} for class #{self}" unless self::OBJECT_MODEL.key? to
158
+
159
+ list = all refresh, cnx: cnx
160
+ to_class = self::OBJECT_MODEL[to][:class]
161
+ mapped = list.map do |i|
162
+ [
163
+ i[ident],
164
+ to_class.is_a?(Symbol) ? i[to] : to_class.new(i[to])
165
+ ]
166
+ end # do i
167
+ mapped.to_h
168
+ end
169
+ # rubocop:enable Naming/UncommunicativeMethodParamName
170
+
171
+ # Given any identfier value for this collection, return the valid
172
+ # id, or nil if there's no match for the given value.
173
+ #
174
+ # If you know the value is a certain identifier, e.g. a serial_number
175
+ # then you can specify the identifier, for a faster search:
176
+ #
177
+ # valid_id serial_number: 'AB12DE34' # => Int or nil
178
+ #
179
+ # If you don't know wich identifier you have, just pass the value and
180
+ # all identifiers are searched
181
+ #
182
+ # valid_id 'AB12DE34' # => Int or nil
183
+ # valid_id 'SomeComputerName' # => Int or nil
184
+ #
185
+ # When the value is a string, the seach is case-insensitive
186
+ #
187
+ # TODO: When 'Searchability' is more dialed in via the searchable
188
+ # mixin, which implements enpoints like 'POST /v1/search-mobile-devices'
189
+ # then use that before using the 'all' list.
190
+ #
191
+ # @param value [Object] A value to search for as an identifier.
192
+ #
193
+ # @param refresh[Boolean] Reload the list data from the API
194
+ #
195
+ # @param ident: [Symbol] Restrict the search to this identifier.
196
+ # E.g. if :serial_number, then the value must be
197
+ # a known serial number, it is not checked against other identifiers
198
+ #
199
+ # @param cnx: (see .all)
200
+ #
201
+ # @return [Object, nil] the primary identifier of the matching object,
202
+ # or nil if it doesn't exist
203
+ #
204
+ def self.valid_id(value = nil, refresh: true, cnx: Jamf.cnx, **ident_hash)
205
+ unless ident_hash.empty?
206
+ ident, value = ident_hash.first
207
+ return id_from_other_ident ident, value, refresh, cnx: cnx
208
+ end
209
+
210
+ # check the id itself first
211
+ return value if all_ids(refresh, cnx: cnx).include? value
212
+
213
+ idents = identifiers - [:id]
214
+ val_is_str = value.is_a? String
215
+
216
+ idents.each do |ident|
217
+ match = all(cnx: cnx).select do |m|
218
+ val_is_str ? m[ident].to_s.casecmp?(value) : m[ident] == value
219
+ end.first
220
+ return match[:id] if match
221
+ end # identifiers.each do |ident|
222
+
223
+ nil
224
+ end
225
+
226
+ # Bu default, subclasses are creatable, i.e. new instances can be created
227
+ # with .create, and added to the JSS with .save
228
+ # If a subclass is NOT creatble for any reason, just add
229
+ # extend Jamf::UnCreatable
230
+ # and this method will return false
231
+ #
232
+ # @return [Boolean]
233
+ def self.creatable?
234
+ true
235
+ end
236
+
237
+ # Make a new thing to be added to the API
238
+ def self.create(params, cnx: Jamf.cnx)
239
+ raise Jamf::UnsupportedError, "#{self}'s are not currently creatable via the API" unless self.creatable?
240
+
241
+ validate_not_abstract
242
+
243
+ validate_required_attributes params
244
+
245
+ params.delete :id # no such animal when .creating
246
+ params[:creating_from_create] = true
247
+ new params, cnx: cnx
248
+ end
249
+
250
+ # Retrieve a member of a CollectionResource from the API
251
+ #
252
+ # To create new members to be added to the JSS, use
253
+ # {Jamf::CollectionResource.create}
254
+ #
255
+ # If you know the specific identifier attribute you're looking up, e.g.
256
+ # :id or :name or :udid, (or an aliase thereof) then you can specify it like
257
+ # `.fetch name: 'somename'`, or `.fetch udid: 'someudid'`
258
+ #
259
+ # If you don't know if (or don't want to type it) you can just use
260
+ # `.fetch 'somename'`, or `.fetch 'someudid'` and all identifiers will be
261
+ # searched for a match.
262
+ #
263
+ # @param ident_value[Object] A value for any identifier for this subclass.
264
+ # All identifier attributes will be searched for a match.
265
+ #
266
+ # @param cnx[Jamf::Connection] the connection to use to fetch the object
267
+ #
268
+ # @param ident_hash[Hash] an identifier attribute key and a search value
269
+ #
270
+ # @return [CollectionResource] The ruby-instance of a Jamf object
271
+ #
272
+ def self.fetch(ident_value = nil, cnx: Jamf.cnx, **ident_hash)
273
+ validate_not_abstract
274
+ id =
275
+ if ident_value == :random
276
+ all_ids.sample
277
+ elsif ident_value
278
+ valid_id ident_value
279
+ elsif ident_hash.empty?
280
+ nil
281
+ else
282
+ ident, lookup_value = ident_hash.first
283
+ valid_id ident => lookup_value
284
+ end
285
+
286
+ raise Jamf::NoSuchItemError, "No matching #{self}" unless id
287
+
288
+ data = cnx.get "#{rsrc_path}/#{id}"
289
+ new data, cnx: cnx
290
+ end # fetch
291
+
292
+ # By default, CollectionResource subclass instances are deletable.
293
+ # If not, just extend the subclass with Jamf::UnDeletable, and this
294
+ # will return false, and .delete & #delete will raise errors
295
+ def self.deletable?
296
+ true
297
+ end
298
+
299
+ # Delete one or more objects by identifier
300
+ # Any valid identifier for the class can be used (id, name, udid, etc)
301
+ # Identifiers can be provided as an array or as separate parameters
302
+ #
303
+ # e.g. .delete [1,3, 34, 4]
304
+ # or .delete 'myComputer', 'that-computer', 'OtherComputer'
305
+ #
306
+ # @param idents[Array<integer>, Integer]
307
+ #
308
+ # @param cnx[Jamf::Connection]
309
+ #
310
+ # @return [Array] the identifiers that were not found, so couldn't be deleted
311
+ #
312
+ def self.delete(*idents, cnx: Jamf.cnx)
313
+ raise Jamf::UnsupportedError, "Deleting #{self} objects is not currently supported" unless deletable?
314
+
315
+ idents.flatten!
316
+ no_valid_ids = []
317
+
318
+ idents.map do |ident|
319
+ id = valid_id ident
320
+ no_valid_ids << ident unless id
321
+ id
322
+ end
323
+ idents.compact!
324
+
325
+ # TODO: some rsrcs have a 'bulk delete' version...
326
+ idents.each { |id| cnx.delete "#{rsrc_path}/#{id}" }
327
+
328
+ no_valid_ids
329
+ end
330
+
331
+ # Private Class Methods
332
+ #####################################
333
+
334
+ # TODO: better pluralizing?
335
+ #
336
+ def self.create_list_methods(attr_name, attr_def)
337
+ list_method_name = "all_#{attr_name}s"
338
+
339
+ define_singleton_method(list_method_name) do |refresh = false, cnx: Jamf.cnx|
340
+ all_list = all(refresh, cnx: cnx)
341
+ if attr_def[:class].is_a? Symbol
342
+ all_list.map { |i| i[attr_name] }.uniq
343
+ else
344
+ all_list.map { |i| attr_def[:class].new i[attr_name] }
345
+ end
346
+ end # define_singleton_method
347
+
348
+ return unless attr_def[:aliases]
349
+
350
+ # aliases - TODO: is there a more elegant way?
351
+ attr_def[:aliases].each do |a|
352
+ define_singleton_method("all_#{a}s") do |refresh = false, cnx: Jamf.cnx|
353
+ send list_method_name, refresh, cnx: cnx
354
+ end # define_singleton_method
355
+ end # each alias
356
+ end # create_list_methods
357
+ private_class_method :create_list_methods
358
+
359
+ # validate that our .create data has the required attribute values.
360
+ # They can't be nil or empty.
361
+ #
362
+ def self.validate_required_attributes(data)
363
+ required_attributes.each do |atr|
364
+ raise Jamf::MissingDataError, "Required attribute '#{atr}:' may not be nil or empty" if data[atr].to_s.empty?
365
+ end
366
+ end
367
+ private_class_method :validate_required_attributes
368
+
369
+ # Given an indentier attr. key, and a value,
370
+ # return the id where that ident has that value, or nil
371
+ #
372
+ def self.id_from_other_ident(ident, value, refresh = true, cnx: Jamf.cnx)
373
+ raise ArgumentError, "Unknown identifier '#{ident}' for #{self}" unless identifiers.include? ident
374
+
375
+ # check the id itself first
376
+ return value if ident == :id && all_ids(refresh, cnx: cnx).include?(value)
377
+
378
+ # all ident values => ids
379
+ ident_map = map_all(ident, to: :id, cnx: cnx, refresh: refresh)
380
+
381
+ # case-insensitivity for string values
382
+ value = ident_map.keys.j_ci_fetch_string(value) if value.is_a? String
383
+
384
+ ident_map[value]
385
+ end
386
+ private_class_method :id_from_other_ident
387
+
388
+
389
+ # Instance Methods
390
+ #####################################
391
+
392
+ def exist?
393
+ !@id.nil?
394
+ end
395
+
396
+ def rsrc_path
397
+ return unless exist?
398
+ "#{self.class.rsrc_path}/#{@id}"
399
+ end
400
+
401
+ def delete
402
+ raise Jamf::UnsupportedError, "Deleting #{self} objects is not currently supported" unless self.class.deletable?
403
+ @cnx.delete rsrc_path
404
+ end
405
+
406
+ # Two collection resource objects are the same if their id's are the same
407
+ def <=>(other)
408
+ id <=> other.id
409
+ end
410
+
411
+ # Private Instance Methods
412
+ ############################################
413
+ private
414
+
415
+ def create_in_jamf
416
+ result = @cnx.post self.class.rsrc_path, to_jamf
417
+ @id = result[:id]
418
+ end
419
+
420
+ end # class CollectionResource
421
+
422
+ end # module JAMF