ruby-jss 1.2.10 → 1.3.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 (33) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES.md +92 -1
  3. data/lib/jamf/api/abstract_classes/json_object.rb +1 -1
  4. data/lib/jamf/api/abstract_classes/prestage.rb +1 -1
  5. data/lib/jamf/api/connection.rb +7 -3
  6. data/lib/jamf/configuration.rb +7 -9
  7. data/lib/jamf/ruby_extensions.rb +1 -0
  8. data/lib/jamf/ruby_extensions/array.rb +1 -1
  9. data/lib/jamf/ruby_extensions/array/utils.rb +3 -3
  10. data/lib/jamf/ruby_extensions/dig.rb +52 -0
  11. data/lib/jss.rb +2 -0
  12. data/lib/jss/api_connection.rb +2 -29
  13. data/lib/jss/api_object.rb +15 -2
  14. data/lib/jss/api_object/directory_binding.rb +273 -0
  15. data/lib/jss/api_object/directory_binding_type.rb +90 -0
  16. data/lib/jss/api_object/directory_binding_type/active_directory.rb +502 -0
  17. data/lib/jss/api_object/directory_binding_type/admitmac.rb +525 -0
  18. data/lib/jss/api_object/directory_binding_type/centrify.rb +212 -0
  19. data/lib/jss/api_object/directory_binding_type/open_directory.rb +178 -0
  20. data/lib/jss/api_object/directory_binding_type/powerbroker_identity_services.rb +73 -0
  21. data/lib/jss/api_object/disk_encryption_configurations.rb +114 -0
  22. data/lib/jss/api_object/distribution_point.rb +95 -35
  23. data/lib/jss/api_object/dock_item.rb +137 -0
  24. data/lib/jss/api_object/mobile_device_application.rb +12 -0
  25. data/lib/jss/api_object/network_segment.rb +152 -58
  26. data/lib/jss/api_object/package.rb +106 -41
  27. data/lib/jss/api_object/policy.rb +379 -4
  28. data/lib/jss/api_object/printer.rb +440 -0
  29. data/lib/jss/api_object/scopable/scope.rb +24 -24
  30. data/lib/jss/composer.rb +1 -1
  31. data/lib/jss/utility.rb +8 -22
  32. data/lib/jss/version.rb +1 -1
  33. metadata +13 -2
@@ -0,0 +1,440 @@
1
+ module JSS
2
+ ### Module Constants
3
+ #####################################
4
+
5
+ ### Module Variables
6
+ #####################################
7
+
8
+ ### Module Methods
9
+ #####################################
10
+
11
+ ### Classes
12
+ #####################################
13
+
14
+ ### Printer object inside JSS
15
+ ###
16
+ ### @see JSS::APIObject
17
+ class Printer < JSS::APIObject
18
+ ## Mix-Ins
19
+ #####################################
20
+ include JSS::Creatable
21
+ include JSS::Updatable
22
+ include JSS::Categorizable
23
+
24
+ ## Class Constants
25
+ #####################################
26
+
27
+ ### The base for REST resources of this class
28
+ RSRC_BASE = 'printers'.freeze
29
+
30
+ ### the hash key used for the JSON list output of all objects in the JSS
31
+ RSRC_LIST_KEY = :printers
32
+
33
+ ### The hash key used for the JSON object output
34
+ ### It's also used in various error messages
35
+ RSRC_OBJECT_KEY = :printer
36
+
37
+ # Where is the Category in the API JSON?
38
+ CATEGORY_SUBSET = :top
39
+
40
+ # How is the category stored in the API data?
41
+ CATEGORY_DATA_TYPE = String
42
+
43
+ ## Attributes
44
+ #####################################
45
+
46
+ ### @return [String] The URI path for the specific printer.
47
+ attr_reader :uri
48
+
49
+ ### @return [String] The CUPs name to be used
50
+ attr_reader :CUPS_name
51
+
52
+ ### @return [String] The physical location of the printer.
53
+ attr_reader :location
54
+
55
+ ### @return [String] The specific model of printer.
56
+ attr_reader :model
57
+
58
+ ### @return [Boolean] Is this printer to be shared?
59
+ attr_reader :shared
60
+
61
+ ### @return [String] Information for this specific printer.
62
+ attr_reader :info
63
+
64
+ ### @return [String] Notes for this specific printer.
65
+ attr_reader :notes
66
+
67
+ ### @return [Boolean] Make this printer as the default printer upon installation.
68
+ attr_reader :make_default
69
+
70
+ ### @return [Boolean] Use a generic PPD.
71
+ attr_reader :use_generic
72
+
73
+ ### @return [String] The PPD file name.
74
+ attr_reader :ppd
75
+
76
+ ### @return [String] The contents of the PPD file.
77
+ attr_reader :ppd_contents
78
+
79
+ ### @return [String] The path the PPD file will be installed.
80
+ attr_reader :ppd_path
81
+
82
+ ### @return [String] The OS version requirements seperated by commas
83
+ attr_reader :os_requirements
84
+
85
+ ## Constructor
86
+ #####################################
87
+
88
+ ###
89
+ def initialize(args = {})
90
+ super args
91
+
92
+ if self.in_jss?
93
+
94
+ @uri = @init_data[:uri]
95
+ @CUPS_name = @init_data[:CUPS_name]
96
+ @location = @init_data[:location]
97
+ @model = @init_data[:model]
98
+ @shared = @init_data[:shared]
99
+ @info = @init_data[:info]
100
+ @notes = @init_data[:notes]
101
+ @make_default = @init_data[:make_default]
102
+ @use_generic = @init_data[:use_generic]
103
+ @ppd = @init_data[:ppd]
104
+ @ppd_contents = @init_data[:ppd_contents]
105
+ @ppd_path = @init_data[:ppd_path]
106
+ @os_requirements = @init_data[:os_requirements]
107
+ else
108
+
109
+ raise JSS::MissingDataError, "CUPS_name must be provided." unless !@init_data[:CUPS_name].nil?
110
+ raise JSS::MissingDataError, "uri must be provided." unless !@init_data[:uri].nil?
111
+
112
+ raise JSS::InvalidDataError, "uri must be a String." unless @init_data[:uri].is_a?(String) || @init_data[:uri].nil?
113
+ raise JSS::InvalidDataError, "CUPS_name must be a String." unless @init_data[:CUPS_name].is_a?(String)
114
+ raise JSS::InvalidDataError, "location must be a String." unless @init_data[:location].is_a?(String) || @init_data[:location].nil?
115
+ raise JSS::InvalidDataError, "model must be a String." unless @init_data[:model].is_a?(String) || @init_data[:model].nil?
116
+ raise JSS::InvalidDataError, "info must be a String." unless @init_data[:info].is_a?(String) || @init_data[:info].nil?
117
+ raise JSS::InvalidDataError, "notes must be a String." unless @init_data[:notes].is_a?(String) || @init_data[:notes].nil?
118
+ raise JSS::InvalidDataError, "ppd must be a String." unless @init_data[:ppd].is_a?(String) || @init_data[:ppd].nil?
119
+ raise JSS::InvalidDataError, "ppd_contents must be a String." unless @init_data[:ppd_contents].is_a?(String) || @init_data[:ppd_contents].nil?
120
+ raise JSS::InvalidDataError, "ppd_path must be a String." unless @init_data[:ppd_path].is_a?(String) || @init_data[:ppd_path].nil?
121
+ raise JSS::InvalidDataError, "os_requirements must be a String." unless @init_data[:os_requirements].is_a?(String) || @init_data[:os_requirements].nil?
122
+ raise JSS::InvalidDataError, "shared must be a String." unless (@init_data[:shared].is_a?(TrueClass) || @init_data[:shared].is_a?(FalseClass)) || @init_data[:shared].nil?
123
+ raise JSS::InvalidDataError, "make_default must be a String." unless (@init_data[:make_default].is_a?(TrueClass) || @init_data[:make_default].is_a?(FalseClass)) || @init_data[:make_default].nil?
124
+ raise JSS::InvalidDataError, "use_generic must be a String." unless (@init_data[:use_generic].is_a?(TrueClass) || @init_data[:use_generic].is_a?(FalseClass)) || @init_data[:use_generic].nil?
125
+
126
+ @uri = @init_data[:uri]
127
+ @CUPS_name = @init_data[:CUPS_name]
128
+ @location = @init_data[:location]
129
+ @model = @init_data[:model]
130
+ @shared = @init_data[:shared]
131
+ @info = @init_data[:info]
132
+ @notes = @init_data[:notes]
133
+ @make_default = @init_data[:make_default]
134
+ @use_generic = @init_data[:use_generic]
135
+ @ppd = @init_data[:ppd]
136
+ @ppd_contents = @init_data[:ppd_contents]
137
+ @ppd_path = @init_data[:ppd_path]
138
+ @os_requirements = @init_data[:os_requirements]
139
+ end
140
+ end
141
+
142
+
143
+ ## Class Methods
144
+ #####################################
145
+
146
+
147
+ # The URI path for the specific printer.
148
+ #
149
+ # @author Tyler Morgan
150
+ #
151
+ # @param newvalue [String]
152
+ #
153
+ # @raise [JSS::InvalidDataError] If newvalue is not a String
154
+ #
155
+ # @return [void]
156
+ def uri=(newvalue)
157
+
158
+ raise JSS::InvalidDataError, "URI must be a string." unless newvalue.is_a? String
159
+
160
+ @uri = newvalue
161
+
162
+ @need_to_update = true
163
+ end
164
+
165
+
166
+ # The CUPs name to be used
167
+ #
168
+ # @author Tyler Morgan
169
+ #
170
+ # @param newvalue [String]
171
+ #
172
+ # @raise [JSS::InvalidDataError] If newvalue is not a String
173
+ #
174
+ # @return [void]
175
+ def CUPS_name=(newvalue)
176
+
177
+ raise JSS::InvalidDataError, "CUPS_name must be a string." unless newvalue.is_a? String
178
+
179
+ @CUPS_name = newvalue
180
+
181
+ @need_to_update = true
182
+ end
183
+
184
+
185
+ # The physical location of the printer.
186
+ #
187
+ # @author Tyler Morgan
188
+ #
189
+ # @param newvalue [String]
190
+ #
191
+ # @raise [JSS::InvalidDataError] If newvalue is not a String
192
+ #
193
+ # @return [void]
194
+ def location=(newvalue)
195
+
196
+ raise JSS::InvalidDataError, "location must be a string." unless newvalue.is_a? String
197
+
198
+ @location = newvalue
199
+
200
+ @need_to_update = true
201
+ end
202
+
203
+
204
+ # The specific model of printer.
205
+ #
206
+ # @author Tyler Morgan
207
+ #
208
+ # @param newvalue [String]
209
+ #
210
+ # @raise [JSS::InvalidDataError] If newvalue is not a String
211
+ #
212
+ # @return [void]
213
+ def model=(newvalue)
214
+
215
+ raise JSS::InvalidDataError, "model must be a string." unless newvalue.is_a? String
216
+
217
+ @model = newvalue
218
+
219
+ @need_to_update = true
220
+ end
221
+
222
+
223
+ # Is this printer to be shared?
224
+ #
225
+ # @author Tyler Morgan
226
+ #
227
+ # @param newvalue [Boolean]
228
+ #
229
+ # @raise [JSS::InvalidDataError] If newvalue is not a Boolean
230
+ #
231
+ # @return [void]
232
+ def shared=(newvalue)
233
+
234
+ raise JSS::InvalidDataError, "shared must be a string." unless newvalue.is_a?(TrueClass) || newvalue.is_a?(FalseClass)
235
+
236
+ @shared = newvalue
237
+
238
+ @need_to_update = true
239
+ end
240
+
241
+
242
+ # Information for this specific printer.
243
+ #
244
+ # @author Tyler Morgan
245
+ #
246
+ # @param newvalue [String]
247
+ #
248
+ # @raise [JSS::InvalidDataError] If newvalue is not a String
249
+ #
250
+ # @return [void]
251
+ def info=(newvalue)
252
+
253
+ raise JSS::InvalidDataError, "info must be a string." unless newvalue.is_a? String
254
+
255
+ @info = newvalue
256
+
257
+ @need_to_update = true
258
+ end
259
+
260
+
261
+ # Notes for this specific printer.
262
+ #
263
+ # @author Tyler Morgan
264
+ #
265
+ # @param newvalue [String]
266
+ #
267
+ # @raise [JSS::InvalidDataError] If newvalue is not a String
268
+ #
269
+ # @return [void]
270
+ def notes=(newvalue)
271
+
272
+ raise JSS::InvalidDataError, "notes must be a string." unless newvalue.is_a? String
273
+
274
+ @notes = newvalue
275
+
276
+ @need_to_update = true
277
+ end
278
+
279
+
280
+ # Make this printer as the default printer upon installation.
281
+ #
282
+ # @author Tyler Morgan
283
+ #
284
+ # @param newvalue [Boolean]
285
+ #
286
+ # @raise [JSS::InvalidDataError] If newvalue is not a Boolean
287
+ #
288
+ # @return [void]
289
+ def make_default=(newvalue)
290
+
291
+ raise JSS::InvalidDataError, "make_default must be a string." unless newvalue.is_a?(TrueClass) || newvalue.is_a?(FalseClass)
292
+
293
+ @make_default = newvalue
294
+
295
+ @need_to_update = true
296
+ end
297
+
298
+
299
+ # Use a generic PPD.
300
+ #
301
+ # @author Tyler Morgan
302
+ #
303
+ # @param newvalue [Boolean]
304
+ #
305
+ # @raise [JSS::InvalidDataError] If newvalue is not a Boolean
306
+ #
307
+ # @return [void]
308
+ def use_generic=(newvalue)
309
+
310
+ raise JSS::InvalidDataError, "use_generic must be a string." unless newvalue.is_a?(TrueClass) || newvalue.is_a?(FalseClass)
311
+
312
+ @use_generic = newvalue
313
+
314
+ @need_to_update = true
315
+ end
316
+
317
+
318
+ # The PPD file name.
319
+ #
320
+ # @author Tyler Morgan
321
+ #
322
+ # @param newvalue [String]
323
+ #
324
+ # @raise [JSS::InvalidDataError] If newvalue is not a String
325
+ #
326
+ # @return [void]
327
+ def ppd=(newvalue)
328
+
329
+ raise JSS::InvalidDataError, "ppd must be a string." unless newvalue.is_a? String
330
+
331
+ @ppd = newvalue
332
+
333
+ @need_to_update = true
334
+ end
335
+
336
+
337
+ # The contents of the PPD file.
338
+ #
339
+ # @author Tyler Morgan
340
+ #
341
+ # @param newvalue [String]
342
+ #
343
+ # @raise [JSS::InvalidDataError] If newvalue is not a String
344
+ #
345
+ # @return [void]
346
+ def ppd_contents=(newvalue)
347
+
348
+ raise JSS::InvalidDataError, "ppd_contents must be a string." unless newvalue.is_a? String
349
+
350
+ @ppd_contents = newvalue
351
+
352
+ @need_to_update = true
353
+ end
354
+
355
+
356
+ # The path the PPD file will be installed.
357
+ #
358
+ # @author Tyler Morgan
359
+ #
360
+ # @param newvalue [String]
361
+ #
362
+ # @raise [JSS::InvalidDataError] If newvalue is not a String
363
+ #
364
+ # @return [void]
365
+ def ppd_path=(newvalue)
366
+
367
+ raise JSS::InvalidDataError, "ppd_path must be a string." unless newvalue.is_a? String
368
+
369
+ @ppd_path = newvalue
370
+
371
+ @need_to_update = true
372
+ end
373
+
374
+
375
+ # The OS version requirements seperated by commas
376
+ #
377
+ # @author Tyler Morgan
378
+ #
379
+ # @param newvalue [String]
380
+ #
381
+ # @raise [JSS::InvalidDataError] If newvalue is not a String
382
+ #
383
+ # @example Limit Printer object to only High Sierra devices and Mojave 10.14.5 OS versions
384
+ # printer.os_requirements = "10.13.x, 10.14.5"
385
+ #
386
+ # @return [void]
387
+ def os_requirements=(newvalue)
388
+
389
+ raise JSS::InvalidDataError, "os_requirements must be a string." unless newvalue.is_a? String
390
+
391
+ @os_requirements = newvalue
392
+
393
+ @need_to_update = true
394
+ end
395
+
396
+ # Remove the various large data
397
+ # from the instance_variables used to create
398
+ # pretty-print (pp) output.
399
+ #
400
+ # @return [Array] the desired instance_variables
401
+ #
402
+ def pretty_print_instance_variables
403
+ vars = super
404
+ vars.delete :@ppd_contents
405
+ vars
406
+ end
407
+
408
+
409
+ ## Private Instance Methods
410
+ #####################################
411
+ private
412
+
413
+ ### Return the xml for creating or updating this script in the JSS
414
+ ###
415
+ def rest_xml
416
+ doc = REXML::Document.new
417
+ printer = doc.add_element 'printer'
418
+
419
+ printer.add_element('id').text = @id
420
+ printer.add_element('name').text = @name
421
+ printer.add_element('uri').text = @uri
422
+ printer.add_element('CUPS_name').text = @CUPS_name
423
+ printer.add_element('location').text = @location
424
+ printer.add_element('model').text = @model
425
+ printer.add_element('shared').text = @shared
426
+ printer.add_element('info').text = @info
427
+ printer.add_element('notes').text = @notes
428
+ printer.add_element('make_default').text = @make_default
429
+ printer.add_element('use_generic').text = @use_generic
430
+ printer.add_element('ppd').text = @ppd
431
+ printer.add_element('ppd_contents').text = @ppd_contents
432
+ printer.add_element('ppd_path').text = @ppd_path
433
+ printer.add_element('os_requirements').text = @os_requirements
434
+ add_category_to_xml(doc)
435
+
436
+ doc.to_s
437
+
438
+ end
439
+ end
440
+ end
@@ -392,7 +392,7 @@ module JSS
392
392
  @exclusions = {}
393
393
  @exclusion_keys.each { |k| @exclusions[k] = [] }
394
394
  end
395
- @container&.should_update
395
+ @container.should_update if @container
396
396
  end
397
397
 
398
398
  # Replace a list of item names for as targets in this scope.
@@ -420,7 +420,7 @@ module JSS
420
420
  list.map! do |ident|
421
421
  item_id = validate_item(:target, key, ident)
422
422
 
423
- if @exclusions[key]&.include?(item_id)
423
+ if @exclusions[key] && @exclusions[key].include?(item_id)
424
424
  raise JSS::AlreadyExistsError, \
425
425
  "Can't set #{key} target to '#{ident}' because it's already an explicit exclusion."
426
426
  end
@@ -432,7 +432,7 @@ module JSS
432
432
 
433
433
  @inclusions[key] = list
434
434
  @all_targets = false
435
- @container&.should_update
435
+ @container.should_update if @container
436
436
  end # sinclude_in_scope
437
437
  alias set_target set_targets
438
438
  alias set_inclusion set_targets
@@ -458,13 +458,13 @@ module JSS
458
458
  def add_target(key, item)
459
459
  key = pluralize_key(key)
460
460
  item_id = validate_item(:target, key, item)
461
- return if @inclusions[key]&.include?(item_id)
461
+ return if @inclusions[key] && @exclusions[key].include?(item_id)
462
462
 
463
- raise JSS::AlreadyExistsError, "Can't set #{key} target to '#{item}' because it's already an explicit exclusion." if @exclusions[key]&.include?(item_id)
463
+ raise JSS::AlreadyExistsError, "Can't set #{key} target to '#{item}' because it's already an explicit exclusion." if @exclusions[key] && @exclusions[key].include?(item_id)
464
464
 
465
465
  @inclusions[key] << item_id
466
466
  @all_targets = false
467
- @container&.should_update
467
+ @container.should_update if @container
468
468
  end
469
469
  alias add_inclusion add_target
470
470
 
@@ -483,10 +483,10 @@ module JSS
483
483
  key = pluralize_key(key)
484
484
  item_id = validate_item :target, key, item, error_if_not_found: false
485
485
  return unless item_id
486
- return unless @inclusions[key]&.include?(item_id)
486
+ return unless @inclusions[key] && @exclusions[key].include?(item_id)
487
487
 
488
488
  @inclusions[key].delete item_id
489
- @container&.should_update
489
+ @container.should_update if @container
490
490
  end
491
491
  alias remove_inclusion remove_target
492
492
 
@@ -513,7 +513,7 @@ module JSS
513
513
  # check the idents
514
514
  list.map! do |ident|
515
515
  item_id = validate_item(:limitation, key, ident)
516
- if @exclusions[key]&.include?(item_id)
516
+ if @exclusions[key] && @exclusions[key].include?(item_id)
517
517
  raise JSS::AlreadyExistsError, "Can't set #{key} limitation for '#{name}' because it's already an explicit exclusion."
518
518
  end
519
519
 
@@ -523,7 +523,7 @@ module JSS
523
523
  return nil if list.sort == @limitations[key].sort
524
524
 
525
525
  @limitations[key] = list
526
- @container&.should_update
526
+ @container.should_update if @container
527
527
  end # set_limitation
528
528
  alias set_limitations set_limitation
529
529
 
@@ -545,14 +545,14 @@ module JSS
545
545
  def add_limitation(key, item)
546
546
  key = pluralize_key(key)
547
547
  item_id = validate_item(:limitation, key, item)
548
- return nil if @limitations[key]&.include?(item_id)
548
+ return nil if @limitations[key] && @exclusions[key].include?(item_id)
549
549
 
550
- if @exclusions[key]&.include?(item_id)
550
+ if @exclusions[key] && @exclusions[key].include?(item_id)
551
551
  raise JSS::AlreadyExistsError, "Can't set #{key} limitation for '#{name}' because it's already an explicit exclusion."
552
552
  end
553
553
 
554
554
  @limitations[key] << item_id
555
- @container&.should_update
555
+ @container.should_update if @container
556
556
  end
557
557
 
558
558
  # Remove a single item for limiting this scope.
@@ -572,10 +572,10 @@ module JSS
572
572
  key = pluralize_key(key)
573
573
  item_id = validate_item :limitation, key, item, error_if_not_found: false
574
574
  return unless item_id
575
- return unless @limitations[key]&.include?(item_id)
575
+ return unless @limitations[key] && @exclusions[key].include?(item_id)
576
576
 
577
577
  @limitations[key].delete item_id
578
- @container&.should_update
578
+ @container.should_update if @container
579
579
  end ###
580
580
 
581
581
  # Replace an exclusion list for this scope
@@ -601,9 +601,9 @@ module JSS
601
601
  item_id = validate_item(:exclusion, key, ident)
602
602
  case key
603
603
  when *@inclusion_keys
604
- raise JSS::AlreadyExistsError, "Can't exclude #{key} '#{ident}' because it's already explicitly included." if @inclusions[key]&.include?(item_id)
604
+ raise JSS::AlreadyExistsError, "Can't exclude #{key} '#{ident}' because it's already explicitly included." if @inclusions[key] && @exclusions[key].include?(item_id)
605
605
  when *LIMITATIONS
606
- if @limitations[key]&.include?(item_id)
606
+ if @limitations[key] && @exclusions[key].include?(item_id)
607
607
  raise JSS::AlreadyExistsError, "Can't exclude #{key} '#{ident}' because it's already an explicit limitation."
608
608
  end
609
609
  end
@@ -613,7 +613,7 @@ module JSS
613
613
  return nil if list.sort == @exclusions[key].sort
614
614
 
615
615
  @exclusions[key] = list
616
- @container&.should_update
616
+ @container.should_update if @container
617
617
  end # limit scope
618
618
 
619
619
  # Add a single item for exclusions of this scope.
@@ -632,14 +632,14 @@ module JSS
632
632
  def add_exclusion(key, item)
633
633
  key = pluralize_key(key)
634
634
  item_id = validate_item(:exclusion, key, item)
635
- return if @exclusions[key]&.include?(item_id)
635
+ return if @exclusions[key] && @exclusions[key].include?(item_id)
636
636
 
637
- raise JSS::AlreadyExistsError, "Can't exclude #{key} scope to '#{item}' because it's already explicitly included." if @inclusions[key]&.include?(item)
637
+ raise JSS::AlreadyExistsError, "Can't exclude #{key} scope to '#{item}' because it's already explicitly included." if @inclusions[key] && @inclusions[key].include?(item)
638
638
 
639
- raise JSS::AlreadyExistsError, "Can't exclude #{key} '#{item}' because it's already an explicit limitation." if @limitations[key]&.include?(item)
639
+ raise JSS::AlreadyExistsError, "Can't exclude #{key} '#{item}' because it's already an explicit limitation." if @limitations[key] && @limitations[key].include?(item)
640
640
 
641
641
  @exclusions[key] << item_id
642
- @container&.should_update
642
+ @container.should_update if @container
643
643
  end
644
644
 
645
645
  # Remove a single item for exclusions of this scope
@@ -656,10 +656,10 @@ module JSS
656
656
  def remove_exclusion(key, item)
657
657
  key = pluralize_key(key)
658
658
  item_id = validate_item :exclusion, key, item, error_if_not_found: false
659
- return unless @exclusions[key]&.include?(item_id)
659
+ return unless @exclusions[key] && @exclusions[key].include?(item_id)
660
660
 
661
661
  @exclusions[key].delete item_id
662
- @container&.should_update
662
+ @container.should_update if @container
663
663
  end
664
664
 
665
665
  # @api private