windoo 1.0.1
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.
- checksums.yaml +7 -0
- data/CHANGES.md +9 -0
- data/LICENSE.txt +177 -0
- data/README.md +222 -0
- data/lib/windoo/base_classes/array_manager.rb +335 -0
- data/lib/windoo/base_classes/criteria_manager.rb +327 -0
- data/lib/windoo/base_classes/criterion.rb +226 -0
- data/lib/windoo/base_classes/json_object.rb +472 -0
- data/lib/windoo/configuration.rb +221 -0
- data/lib/windoo/connection/actions.rb +152 -0
- data/lib/windoo/connection/attributes.rb +156 -0
- data/lib/windoo/connection/connect.rb +402 -0
- data/lib/windoo/connection/constants.rb +55 -0
- data/lib/windoo/connection/token.rb +489 -0
- data/lib/windoo/connection.rb +92 -0
- data/lib/windoo/converters.rb +31 -0
- data/lib/windoo/exceptions.rb +34 -0
- data/lib/windoo/mixins/api_collection.rb +408 -0
- data/lib/windoo/mixins/constants.rb +43 -0
- data/lib/windoo/mixins/default_connection.rb +75 -0
- data/lib/windoo/mixins/immutable.rb +34 -0
- data/lib/windoo/mixins/loading.rb +38 -0
- data/lib/windoo/mixins/patch/component.rb +102 -0
- data/lib/windoo/mixins/software_title/extension_attribute.rb +106 -0
- data/lib/windoo/mixins/utility.rb +23 -0
- data/lib/windoo/objects/capability.rb +82 -0
- data/lib/windoo/objects/capability_manager.rb +52 -0
- data/lib/windoo/objects/component.rb +99 -0
- data/lib/windoo/objects/component_criteria_manager.rb +26 -0
- data/lib/windoo/objects/component_criterion.rb +66 -0
- data/lib/windoo/objects/extension_attribute.rb +149 -0
- data/lib/windoo/objects/kill_app.rb +92 -0
- data/lib/windoo/objects/kill_app_manager.rb +89 -0
- data/lib/windoo/objects/patch.rb +235 -0
- data/lib/windoo/objects/patch_manager.rb +240 -0
- data/lib/windoo/objects/requirement.rb +85 -0
- data/lib/windoo/objects/requirement_manager.rb +52 -0
- data/lib/windoo/objects/software_title.rb +407 -0
- data/lib/windoo/validate.rb +548 -0
- data/lib/windoo/version.rb +15 -0
- data/lib/windoo/zeitwerk_config.rb +158 -0
- data/lib/windoo.rb +56 -0
- metadata +141 -0
@@ -0,0 +1,407 @@
|
|
1
|
+
# Copyright 2025 Pixar
|
2
|
+
#
|
3
|
+
# Licensed under the terms set forth in the LICENSE.txt file available at
|
4
|
+
# at the root of this project.
|
5
|
+
#
|
6
|
+
|
7
|
+
# frozen_string_literal: true
|
8
|
+
|
9
|
+
module Windoo
|
10
|
+
|
11
|
+
# A Software Title in the Title Editor
|
12
|
+
#
|
13
|
+
# NOTE: SoftwareTitles cannot be enabled when created.
|
14
|
+
# You must call 'enable' on them after creating any
|
15
|
+
# necessary sub-objects.
|
16
|
+
#
|
17
|
+
class SoftwareTitle < Windoo::BaseClasses::JSONObject
|
18
|
+
|
19
|
+
# Mixins
|
20
|
+
######################
|
21
|
+
|
22
|
+
include Windoo::Mixins::APICollection
|
23
|
+
include Windoo::Mixins::SoftwareTitle::ExtensionAttribute
|
24
|
+
|
25
|
+
# Constants
|
26
|
+
######################
|
27
|
+
|
28
|
+
LOCAL_TITLE_EDITOR_SOURCE_NAME = 'Local'
|
29
|
+
LOCAL_TITLE_EDITOR_SOURCE_ID = 0
|
30
|
+
|
31
|
+
RSRC_PATH = 'softwaretitles'
|
32
|
+
|
33
|
+
CONTAINER_CLASS = nil
|
34
|
+
|
35
|
+
# Public Class Methods
|
36
|
+
######################
|
37
|
+
|
38
|
+
# Software Titles are the only collection resource that
|
39
|
+
# has an endpoint that returns summary list.
|
40
|
+
#
|
41
|
+
# All others, patches, components, ext attrs, etc...
|
42
|
+
# can only be individually accessed using their
|
43
|
+
# primary identifier, so the .all and .valid_id methods
|
44
|
+
# are not applicable to them.
|
45
|
+
#
|
46
|
+
# .all returns summmary Hashes for all Software Titles
|
47
|
+
# in the Title Editor
|
48
|
+
# the Hash keys are:
|
49
|
+
#
|
50
|
+
# :softwareTitleId=>1,
|
51
|
+
# :enabled=>false,
|
52
|
+
# :name=>"Test",
|
53
|
+
# :publisher=>"Some Company",
|
54
|
+
# :appName=>nil,
|
55
|
+
# :bundleId=>nil,
|
56
|
+
# :lastModified=>"2022-09-10T22:06:39Z",
|
57
|
+
# :currentVersion=>"5.0.1",
|
58
|
+
# :requirements=>3,
|
59
|
+
# :patches=>0, # Number of enabled patches, not existing patches?
|
60
|
+
# :id=>"com.somecompany.test",
|
61
|
+
# :sourceId=>0,
|
62
|
+
# :source=>"Local"
|
63
|
+
#
|
64
|
+
# @return [Array<Hash>]
|
65
|
+
####
|
66
|
+
def self.all(cnx: Windoo.cnx)
|
67
|
+
cnx.get(self::RSRC_PATH)
|
68
|
+
end
|
69
|
+
|
70
|
+
# @return [Array<String>] The ids (unique names) of all titles
|
71
|
+
#####
|
72
|
+
def self.all_ids(cnx: Windoo.cnx)
|
73
|
+
all(cnx: cnx).map { |t| t[:id] }
|
74
|
+
end
|
75
|
+
|
76
|
+
# @return [Array<Integer>] The all_softwareTitleIds (numeric id numbers) of all titles
|
77
|
+
#####
|
78
|
+
def self.all_softwareTitleIds(cnx: Windoo.cnx)
|
79
|
+
all(cnx: cnx).map { |t| t[:softwareTitleId] }
|
80
|
+
end
|
81
|
+
|
82
|
+
# Override the method from APICollection, because
|
83
|
+
# SoftwareTitles can be looked up by both the primary_ident
|
84
|
+
# (softwareTitleId) and secondary (id)
|
85
|
+
#
|
86
|
+
# @return [Windoo::SoftwareTitle]
|
87
|
+
####
|
88
|
+
def self.fetch(ident = nil, cnx: Windoo.cnx, **key_and_ident)
|
89
|
+
unless ident || !key_and_ident.empty?
|
90
|
+
raise ArgumentError,
|
91
|
+
"ident, or 'key: ident' is required to fetch #{self.class}"
|
92
|
+
end
|
93
|
+
|
94
|
+
id =
|
95
|
+
if ident
|
96
|
+
valid_id ident, raise_if_not_found: true, cnx: cnx
|
97
|
+
else
|
98
|
+
key, ident = key_and_ident.first
|
99
|
+
|
100
|
+
# Dont call valid_id if we are fetching based on the primary_id_key
|
101
|
+
# just used the value provided. The API will complain if it
|
102
|
+
# doesn't exist
|
103
|
+
key == primary_id_key ? ident : valid_id(ident, key: key, raise_if_not_found: true, cnx: cnx)
|
104
|
+
end
|
105
|
+
|
106
|
+
init_data = cnx.get("#{self::RSRC_PATH}/#{id}")
|
107
|
+
init_data[:cnx] = cnx
|
108
|
+
init_data[:fetching] = true
|
109
|
+
|
110
|
+
new(**init_data)
|
111
|
+
end
|
112
|
+
|
113
|
+
# @param ident [Integer, String] the identifier value to search for
|
114
|
+
#
|
115
|
+
# @param key [Symbol] if given, Only look for the value in this key.
|
116
|
+
#
|
117
|
+
# @return [Integer, nil] given any identifier, return the matching primary id
|
118
|
+
# or nil if no match
|
119
|
+
####
|
120
|
+
def self.valid_id(ident, key: nil, raise_if_not_found: false, cnx: Windoo.cnx)
|
121
|
+
matched_summary =
|
122
|
+
if key
|
123
|
+
all(cnx: cnx).select { |summary| summary[key] == ident }.first
|
124
|
+
else
|
125
|
+
find_summary_for_ident(ident, cnx: cnx)
|
126
|
+
end
|
127
|
+
|
128
|
+
value = matched_summary ? matched_summary[primary_id_key] : nil
|
129
|
+
|
130
|
+
raise Windoo::NoSuchItemError, "No #{self} found for identifier '#{ident}'" if raise_if_not_found && value.nil?
|
131
|
+
|
132
|
+
value
|
133
|
+
end
|
134
|
+
|
135
|
+
####
|
136
|
+
def self.find_summary_for_ident(ident, cnx: Windoo.cnx)
|
137
|
+
all(cnx: cnx).each do |summary|
|
138
|
+
ident_keys.each do |key|
|
139
|
+
return summary if summary[key] == ident
|
140
|
+
end
|
141
|
+
end
|
142
|
+
nil
|
143
|
+
end
|
144
|
+
private_class_method :find_summary_for_ident
|
145
|
+
|
146
|
+
# Get the 'autofill patches' for a given software title
|
147
|
+
#
|
148
|
+
# @param ident [String, Integer] An identifier for a software title
|
149
|
+
#
|
150
|
+
# @return [Array<Hash>] the autofill patch data
|
151
|
+
####
|
152
|
+
def self.autofill_patches(ident, cnx: Windoo.cnx)
|
153
|
+
id = valid_id ident, raise_if_not_found: true, cnx: cnx
|
154
|
+
|
155
|
+
cnx.get("#{self::RSRC_PATH}/#{id}/patches/autofill")
|
156
|
+
end
|
157
|
+
|
158
|
+
# Get the 'autofill requirements' for a given software title
|
159
|
+
#
|
160
|
+
# @param ident [String, Integer] An identifier for a software title
|
161
|
+
#
|
162
|
+
# @return [Array<Hash>] the autofill requirement data
|
163
|
+
####
|
164
|
+
def self.autofill_requirements(ident, cnx: Windoo.cnx)
|
165
|
+
id = valid_id ident, raise_if_not_found: true, cnx: cnx
|
166
|
+
|
167
|
+
cnx.get("#{self::RSRC_PATH}/#{id}/requirements/autofill")
|
168
|
+
end
|
169
|
+
|
170
|
+
# Attributes
|
171
|
+
######################
|
172
|
+
|
173
|
+
# Attributes not defined in the superclasses
|
174
|
+
|
175
|
+
JSON_ATTRIBUTES = {
|
176
|
+
|
177
|
+
# @!attribute softwareTitleId
|
178
|
+
# @return [Integer] The id of this title in the Title Editor
|
179
|
+
softwareTitleId: {
|
180
|
+
class: :Integer,
|
181
|
+
# primary means this is the one used to fetch via API calls
|
182
|
+
identifier: :primary,
|
183
|
+
readonly: true,
|
184
|
+
do_not_send: true
|
185
|
+
},
|
186
|
+
|
187
|
+
# @!attribute id
|
188
|
+
# @return [String] A string, unique any patch source (in this case
|
189
|
+
# the TitleEditor), that identifies this Software Title.
|
190
|
+
# Can be thought of as the unique name on the Title Editor.
|
191
|
+
# Not to be confused with the 'name' attribute, which is more
|
192
|
+
# of a Display Name, and is not unique
|
193
|
+
id: {
|
194
|
+
class: :String,
|
195
|
+
# true means this is a unique value in and can be used to find a valid
|
196
|
+
# primary identifier.
|
197
|
+
identifier: true,
|
198
|
+
# required means this value is required to create or update this
|
199
|
+
# object on the server(s)
|
200
|
+
required: true
|
201
|
+
},
|
202
|
+
|
203
|
+
# @!attribute enabled
|
204
|
+
# @return [Boolean] Is this title enabled, and available to be subscribed to?
|
205
|
+
enabled: {
|
206
|
+
class: :Boolean
|
207
|
+
},
|
208
|
+
|
209
|
+
# @!attribute name
|
210
|
+
# @return [String] The name of this title in the Title Editor. NOT UNIQUE,
|
211
|
+
# and not an identfier. See 'id'.
|
212
|
+
name: {
|
213
|
+
class: :String,
|
214
|
+
required: true
|
215
|
+
},
|
216
|
+
|
217
|
+
# @!attribute publisher
|
218
|
+
# @return [String] The publisher of this software
|
219
|
+
publisher: {
|
220
|
+
class: :String,
|
221
|
+
required: true
|
222
|
+
},
|
223
|
+
|
224
|
+
# @attribute appName
|
225
|
+
# @return [String] Currently not used by the Title Editor
|
226
|
+
# or Jamf Pro Patch Titles.
|
227
|
+
# There is no matching data in the Web UI.
|
228
|
+
# These data exist in the killApps associated with Patches
|
229
|
+
appName: {
|
230
|
+
class: :String
|
231
|
+
},
|
232
|
+
|
233
|
+
# @attribute bundleId
|
234
|
+
# @return [String] Currently not used by the Title Editor
|
235
|
+
# or Jamf Pro Patch Titles.
|
236
|
+
# There is no matching data in the Web UI.
|
237
|
+
# These data exist in the killApps associated with Patches
|
238
|
+
bundleId: {
|
239
|
+
class: :String
|
240
|
+
},
|
241
|
+
|
242
|
+
# @!attribute lastModified
|
243
|
+
# @return [Time] When was the title last modified, in UTC?
|
244
|
+
# @note This timestamp is only valid as of the last time
|
245
|
+
# you fetched this SoftwareTitle or updated one of its
|
246
|
+
# immediate attributes (i.e. not arrays of other API
|
247
|
+
# objects like requirements or patches.)
|
248
|
+
# To be sure of the most recent time, accounting for
|
249
|
+
# potential updates from other places (like the Web UI)
|
250
|
+
# you should re-fetch the Title
|
251
|
+
lastModified: {
|
252
|
+
class: :Time,
|
253
|
+
|
254
|
+
# for classes (like Time) that are not Symbols (like :String)
|
255
|
+
# This is the Class method to call on them to convert the
|
256
|
+
# raw API data into the ruby value we want. The API data
|
257
|
+
# will be passed as the sole param to this method.
|
258
|
+
# For most, it will be :new, but for, e.g., Time, it is
|
259
|
+
# :parse
|
260
|
+
to_ruby: :to_time,
|
261
|
+
|
262
|
+
# The method to call on the value when converting to
|
263
|
+
# data to be sent to the API.
|
264
|
+
# e.g. on Time values, convert to iso8601
|
265
|
+
# to_api: :iso8601
|
266
|
+
|
267
|
+
# attributes with this set to true are never
|
268
|
+
# sent to the server when creating or updating
|
269
|
+
do_not_send: true,
|
270
|
+
readonly: true
|
271
|
+
},
|
272
|
+
|
273
|
+
# @!attribute currentVersion
|
274
|
+
# @return [String] the version number of the most recent patch
|
275
|
+
currentVersion: {
|
276
|
+
class: :String,
|
277
|
+
required: true
|
278
|
+
},
|
279
|
+
|
280
|
+
# This value only appears in the .all summary hash, not in the
|
281
|
+
# full instance init_data.
|
282
|
+
#
|
283
|
+
# _!attribute source
|
284
|
+
# _return [String] The name of the Patch Source that ultimately
|
285
|
+
# hosts this title definition. If hosted by our TitleEditor
|
286
|
+
# directly, this is LOCAL_TITLE_EDITOR_SOURCE_NAME
|
287
|
+
#
|
288
|
+
# @todo implement external patches.
|
289
|
+
# source: {
|
290
|
+
# class: :String
|
291
|
+
# },
|
292
|
+
|
293
|
+
# @!attribute sourceId
|
294
|
+
# @return [Integer] The id of the Patch Source that ultimately
|
295
|
+
# hosts this title definition. If hosted by our TitleEditor
|
296
|
+
# directly, this is LOCAL_TITLE_EDITOR_SOURCE_ID
|
297
|
+
#
|
298
|
+
# @todo implement external patches.
|
299
|
+
# sourceId: {
|
300
|
+
# class: :Integer,
|
301
|
+
# do_not_send: true
|
302
|
+
# },
|
303
|
+
|
304
|
+
# @!attribute requirements
|
305
|
+
# @return [Array<Windoo::Requirement>] The requirements - criteria that
|
306
|
+
# define which computers have the software installed.
|
307
|
+
requirements: {
|
308
|
+
class: Windoo::RequirementManager,
|
309
|
+
do_not_send: true,
|
310
|
+
readonly: true
|
311
|
+
},
|
312
|
+
|
313
|
+
# @!attribute patches
|
314
|
+
# @return [Array<Windoo::Patch>] The patches available for this title
|
315
|
+
patches: {
|
316
|
+
class: Windoo::PatchManager,
|
317
|
+
do_not_send: true,
|
318
|
+
readonly: true
|
319
|
+
},
|
320
|
+
|
321
|
+
# @!attribute extensionAttributes
|
322
|
+
# @return [Windoo::ExtensionAttribute] The Extension Attribute used by this title.
|
323
|
+
# NOTE: See the module Windoo::Mixins::SoftwareTitle::ExtentionAttribute
|
324
|
+
extensionAttribute: {
|
325
|
+
class: Windoo::ExtensionAttribute,
|
326
|
+
do_not_send: true,
|
327
|
+
readonly: true
|
328
|
+
}
|
329
|
+
}.freeze
|
330
|
+
|
331
|
+
# Construcor
|
332
|
+
######################
|
333
|
+
def initialize(**init_data)
|
334
|
+
super
|
335
|
+
|
336
|
+
@requirements = Windoo::RequirementManager.new @requirements, container: self
|
337
|
+
@patches = Windoo::PatchManager.new @patches, container: self
|
338
|
+
end
|
339
|
+
|
340
|
+
# Public Instance Methods
|
341
|
+
###################################
|
342
|
+
|
343
|
+
# Get the 'autofill patches' for this software title
|
344
|
+
# @return [Array<Hash>] the autofill patch data
|
345
|
+
def autofill_patches
|
346
|
+
id = send self.class.primary_id_key
|
347
|
+
self.class.autofill_patches id
|
348
|
+
end
|
349
|
+
|
350
|
+
# Get the 'autofill requirements' for this software title
|
351
|
+
# @return [Array<Hash>] the autofill requirement data
|
352
|
+
def autofill_requirements
|
353
|
+
id = send self.class.primary_id_key
|
354
|
+
self.class.autofill_requirements id
|
355
|
+
end
|
356
|
+
|
357
|
+
# Enable this SoftwareTitle
|
358
|
+
def enable
|
359
|
+
return if enabled?
|
360
|
+
|
361
|
+
if requirements.empty? || patches.all_enabled.empty?
|
362
|
+
raise Windoo::MissingDataError,
|
363
|
+
'SoftwareTitles must have at least one requirement and one enabled patch before they can be enabled'
|
364
|
+
end
|
365
|
+
|
366
|
+
self.enabled = true
|
367
|
+
:enabled
|
368
|
+
end
|
369
|
+
|
370
|
+
# Disable this SoftwareTitle
|
371
|
+
def disable
|
372
|
+
return unless enabled?
|
373
|
+
|
374
|
+
self.enabled = false
|
375
|
+
:disabled
|
376
|
+
end
|
377
|
+
|
378
|
+
# Update our modification timestamp from other objects
|
379
|
+
# to the current time when they are changed on the server.
|
380
|
+
def update_modification_time
|
381
|
+
@lastModified = Time.now.utc
|
382
|
+
end
|
383
|
+
|
384
|
+
# Private Instance Methods
|
385
|
+
##########################################
|
386
|
+
private
|
387
|
+
|
388
|
+
# See the section 'REQUIRED ITEMS WHEN MIXING IN'
|
389
|
+
# in the APICollection mixin.
|
390
|
+
def handle_create_response(post_response, container_id: nil)
|
391
|
+
@softwareTitleId = post_response[:softwareTitleId]
|
392
|
+
|
393
|
+
@sourceId = post_response[:sourceId]
|
394
|
+
@enabled = post_response[:enabled]
|
395
|
+
|
396
|
+
@softwareTitleId
|
397
|
+
end
|
398
|
+
|
399
|
+
# See the section 'REQUIRED ITEMS WHEN MIXING IN'
|
400
|
+
# in the APICollection mixin.
|
401
|
+
def handle_update_response(_put_response)
|
402
|
+
@softwareTitleId
|
403
|
+
end
|
404
|
+
|
405
|
+
end # class SoftwareTitle
|
406
|
+
|
407
|
+
end # Module Windoo
|