class Jamf::OAPIObject

The parent class for all objects auto-generated in the Jamf::OAPISchemas module more docs to come

Attributes

init_data[R]

the raw hash from which this object was constructed @return [Hash]

Public Class Methods

mutable?() click to toggle source

By default,OAPIObjects (as a whole) are mutable, although some attributes may not be (see OAPI_PROPERTIES in the JSONObject docs)

When an entire sublcass of OAPIObject is read-only/immutable, ‘extend Jamf::Immutable`, which will override this to return false. Doing so will prevent any setters from being created for the subclass and will cause Jamf::Resource.save to raise an error

   # File lib/jamf/api/jamf_pro/base_classes/oapi_object.rb
50 def self.mutable?
51   !singleton_class.ancestors.include? Jamf::Immutable
52 end
new(data) click to toggle source

Make an instance. Data comes from the API

@param data the data for constructing a new object.

    # File lib/jamf/api/jamf_pro/base_classes/oapi_object.rb
350 def initialize(data)
351   @init_data = data
352 
353   # creating a new one via ruby-jss, not fetching one from the API
354   creating = data.delete :creating_from_create if data.is_a?(Hash)
355 
356   if creating
357     self.class::OAPI_PROPERTIES.each_key do |attr_name|
358       # we'll enforce required values when we save
359       next unless data.key? attr_name
360 
361       # use our setters for each value so that they are validated, and
362       # in the unsaved changes list
363       send "#{attr_name}=", data[attr_name]
364     end
365     return
366   end
367 
368   parse_init_data data
369 end
oapi_properties_parsed?() click to toggle source

have we already parsed our OAPI_PROPERTIES? If so, we shoudn’t do it again, an this can be used to check

   # File lib/jamf/api/jamf_pro/base_classes/oapi_object.rb
64 def self.oapi_properties_parsed?
65   @oapi_properties_parsed
66 end
parse_oapi_properties() click to toggle source

create getters and setters for subclasses of APIObject based on their OAPI_PROPERTIES Hash.

This method can’t be private, cuz we want to call it from a Zeitwerk callback when subclasses are loaded.

    # File lib/jamf/api/jamf_pro/base_classes/oapi_object.rb
 74 def self.parse_oapi_properties
 75   # only do this once
 76   return if @oapi_properties_parsed
 77 
 78   # only if this constant is defined
 79   return unless defined? self::OAPI_PROPERTIES
 80 
 81   # TODO: is the concept of 'primary' needed anymore?
 82   got_primary = false
 83 
 84   self::OAPI_PROPERTIES.each do |attr_name, attr_def|
 85     Jamf.load_msg "Creating getters and setters for attribute '#{attr_name}' of #{self}"
 86 
 87     # see above comment
 88     # don't make one for :id, that one's hard-coded into CollectionResource
 89     # create_list_methods(attr_name, attr_def) if need_list_methods && attr_def[:identifier] && attr_name != :id
 90 
 91     # there can be only one (primary ident)
 92     if attr_def[:identifier] == :primary
 93       raise Jamf::UnsupportedError, 'Two identifiers marked as :primary' if got_primary
 94 
 95       got_primary = true
 96     end
 97 
 98     # create getter unless the attr is write only
 99     create_getters attr_name, attr_def unless attr_def[:writeonly]
100 
101     # Don't crete setters for readonly attrs, or immutable objects
102     next if attr_def[:readonly] || !mutable?
103 
104     create_setters attr_name, attr_def
105   end #  do |attr_name, attr_def|
106 
107   @oapi_properties_parsed = true
108 end
required_attributes() click to toggle source

An array of attribute names that are required when making new instances - they cannot be nil, but may be empty.

See the OAPI_PROPERTIES documentation in {Jamf::OAPIObject}

   # File lib/jamf/api/jamf_pro/base_classes/oapi_object.rb
58 def self.required_attributes
59   self::OAPI_PROPERTIES.select { |_attr, deets| deets[:required] }.keys
60 end
validate_attr(attr_name, value) click to toggle source

Used by auto-generated setters and .create to validate new values.

returns a valid value or raises an exception

This method only validates single values. When called from multi-value setters, it is used for each value individually.

@param attr_name, a top-level key from OAPI_PROPERTIES for this class

@param value [Object] the value to validate for that attribute.

@return [Object] The validated, possibly converted, value.

    # File lib/jamf/api/jamf_pro/base_classes/oapi_object.rb
324 def self.validate_attr(attr_name, value)
325   attr_def = self::OAPI_PROPERTIES[attr_name]
326   raise ArgumentError, "Unknown attribute: #{attr_name} for #{self} objects" unless attr_def
327 
328   # validate the value based on the OAPI definition.
329   Jamf::Validate.oapi_attr value, attr_def: attr_def, attr_name: attr_name
330 
331   # if this is an identifier, it must be unique
332   # TODO: move this to colloection resouce code
333   # Jamf::Validate.doesnt_exist(value, self, attr_name, cnx: cnx) if attr_def[:identifier] && superclass == Jamf::CollectionResource
334 end

Private Class Methods

create_append_setters(attr_name, attr_def) click to toggle source

The attr_append(newval) setter method for array values

    # File lib/jamf/api/jamf_pro/base_classes/oapi_object.rb
194 def self.create_append_setters(attr_name, attr_def)
195   define_method("#{attr_name}_append") do |new_value|
196     initialize_multi_value_attr_array attr_name
197 
198     new_value = validate_attr attr_name, new_value
199 
200     new_array = instance_variable_get("@#{attr_name}")
201     old_array = new_array.dup
202     new_array << new_value
203 
204     # now validate the array as a whole for oapi constraints
205     Jamf::Validate.validate_array_constraints(new_array, attr_def: attr_def, attr_name: attr_name)
206 
207     note_unsaved_change attr_name, old_array
208   end # define method
209 
210   # always have a << alias
211   alias_method "#{attr_name}<<", "#{attr_name}_append"
212 end
create_array_setters(attr_name, attr_def) click to toggle source
    # File lib/jamf/api/jamf_pro/base_classes/oapi_object.rb
156 def self.create_array_setters(attr_name, attr_def)
157   create_full_array_setters(attr_name, attr_def)
158   create_append_setters(attr_name, attr_def)
159   create_prepend_setters(attr_name, attr_def)
160   create_insert_setters(attr_name, attr_def)
161   create_delete_setters(attr_name, attr_def)
162   create_delete_at_setters(attr_name, attr_def)
163   create_delete_if_setters(attr_name, attr_def)
164 end
create_delete_at_setters(attr_name, attr_def) click to toggle source

The attr_delete_at(index) setter method for array values

    # File lib/jamf/api/jamf_pro/base_classes/oapi_object.rb
275 def self.create_delete_at_setters(attr_name, attr_def)
276   define_method("#{attr_name}_delete_at") do |index|
277     initialize_multi_value_attr_array attr_name
278 
279     new_array = instance_variable_get("@#{attr_name}")
280     old_array = new_array.dup
281     deleted = new_array.delete_at index
282     return unless deleted
283 
284     # now validate the array as a whole for oapi constraints
285     Jamf::Validate.validate_array_constraints(new_array, attr_def: attr_def, attr_name: attr_name)
286 
287     note_unsaved_change attr_name, old_array
288   end # define method
289 end
create_delete_if_setters(attr_name, attr_def) click to toggle source

The attr_delete_if(block) setter method for array values

    # File lib/jamf/api/jamf_pro/base_classes/oapi_object.rb
294 def self.create_delete_if_setters(attr_name, attr_def)
295   define_method("#{attr_name}_delete_if") do |&block|
296     initialize_multi_value_attr_array attr_name
297 
298     new_array = instance_variable_get("@#{attr_name}")
299     old_array = new_array.dup
300     new_array.delete_if(&block)
301     return if old_array == new_array
302 
303     # now validate the array as a whole for oapi constraints
304     Jamf::Validate.validate_array_constraints(new_array, attr_def: attr_def, attr_name: attr_name)
305 
306     note_unsaved_change attr_name, old_array
307   end # define method
308 end
create_delete_setters(attr_name, attr_def) click to toggle source

The attr_delete(val) setter method for array values

    # File lib/jamf/api/jamf_pro/base_classes/oapi_object.rb
256 def self.create_delete_setters(attr_name, attr_def)
257   define_method("#{attr_name}_delete") do |val|
258     initialize_multi_value_attr_array attr_name
259 
260     new_array = instance_variable_get("@#{attr_name}")
261     old_array = new_array.dup
262     new_array.delete val
263     return if old_array == new_array
264 
265     # now validate the array as a whole for oapi constraints
266     Jamf::Validate.validate_array_constraints(new_array, attr_def: attr_def, attr_name: attr_name)
267 
268     note_unsaved_change attr_name, old_array
269   end # define method
270 end
create_full_array_setters(attr_name, attr_def) click to toggle source

The attr=(newval) setter method for array values

    # File lib/jamf/api/jamf_pro/base_classes/oapi_object.rb
169 def self.create_full_array_setters(attr_name, attr_def)
170   define_method("#{attr_name}=") do |new_value|
171     initialize_multi_value_attr_array attr_name
172 
173     raise Jamf::InvalidDataError, "Value for '#{attr_name}=' must be an Array" unless new_value.is_a? Array
174 
175     # validate each item of the new array
176     new_value.map! { |item| validate_attr attr_name, item }
177 
178     # now validate the array as a whole for oapi constraints
179     Jamf::Validate.validate_array_constraints(new_value, attr_def: attr_def, attr_name: attr_name)
180 
181     old_value = instance_variable_get("@#{attr_name}")
182     return if new_value == old_value
183 
184     instance_variable_set("@#{attr_name}", new_value)
185     note_unsaved_change attr_name, old_value
186   end # define method
187 
188   return unless attr_def[:aliases]
189 end
create_getters(attr_name, attr_def) click to toggle source

create a getter for an attribute, and any aliases needed

    # File lib/jamf/api/jamf_pro/base_classes/oapi_object.rb
115 def self.create_getters(attr_name, attr_def)
116   # multi_value - only return a frozen dup, no direct editing of the Array
117   if attr_def[:multi]
118     define_method(attr_name) do
119       initialize_multi_value_attr_array attr_name
120 
121       instance_variable_get("@#{attr_name}").dup.freeze
122     end
123 
124   # single value
125   else
126     define_method(attr_name) { instance_variable_get("@#{attr_name}") }
127   end
128 
129   # all booleans get predicate ? aliases
130   alias_method("#{attr_name}?", attr_name) if attr_def[:class] == :boolean
131 end
create_insert_setters(attr_name, attr_def) click to toggle source

The attr_insert(index, newval) setter method for array values

    # File lib/jamf/api/jamf_pro/base_classes/oapi_object.rb
236 def self.create_insert_setters(attr_name, attr_def)
237   define_method("#{attr_name}_insert") do |index, new_value|
238     initialize_multi_value_attr_array attr_name
239 
240     new_value = validate_attr attr_name, new_value
241 
242     new_array = instance_variable_get("@#{attr_name}")
243     old_array = new_array.dup
244     new_array.insert index, new_value
245 
246     # now validate the array as a whole for oapi constraints
247     Jamf::Validate.validate_array_constraints(new_array, attr_def: attr_def, attr_name: attr_name)
248 
249     note_unsaved_change attr_name, old_array
250   end # define method
251 end
create_prepend_setters(attr_name, attr_def) click to toggle source

The attr_prepend(newval) setter method for array values

    # File lib/jamf/api/jamf_pro/base_classes/oapi_object.rb
217 def self.create_prepend_setters(attr_name, attr_def)
218   define_method("#{attr_name}_prepend") do |new_value|
219     initialize_multi_value_attr_array attr_name
220 
221     new_value = validate_attr attr_name, new_value
222 
223     new_array = instance_variable_get("@#{attr_name}")
224     old_array = new_array.dup
225     new_array.unshift new_value
226 
227     # now validate the array as a whole for oapi constraints
228     Jamf::Validate.validate_array_constraints(new_array, attr_def: attr_def, attr_name: attr_name)
229 
230     note_unsaved_change attr_name, old_array
231   end # define method
232 end
create_setters(attr_name, attr_def) click to toggle source

create setter(s) for an attribute, and any aliases needed

    # File lib/jamf/api/jamf_pro/base_classes/oapi_object.rb
136 def self.create_setters(attr_name, attr_def)
137   # multi_value
138   if attr_def[:multi]
139     create_array_setters(attr_name, attr_def)
140     return
141   end
142 
143   # single value
144   define_method("#{attr_name}=") do |new_value|
145     new_value = validate_attr attr_name, new_value
146     old_value = instance_variable_get("@#{attr_name}")
147     return if new_value == old_value
148 
149     instance_variable_set("@#{attr_name}", new_value)
150     note_unsaved_change attr_name, old_value
151   end # define method
152 end

Public Instance Methods

<=>(other) click to toggle source

Comparable by the sha1 hash of our properties. Subclasses or mixins may override this in ways that make sense for them

    # File lib/jamf/api/jamf_pro/base_classes/oapi_object.rb
473 def <=>(other)
474   sha1_hash <=> other.sha1_hash
475 end
clear_unsaved_changes() click to toggle source
    # File lib/jamf/api/jamf_pro/base_classes/oapi_object.rb
418 def clear_unsaved_changes
419   return unless self.class.mutable?
420 
421   unsaved_changes.keys.each do |attr_name|
422     attrib_val = instance_variable_get "@#{attr_name}"
423     if self.class::OAPI_PROPERTIES[attr_name][:multi]
424       attrib_val.each { |item| item.send :clear_unsaved_changes if item.respond_to? :clear_unsaved_changes }
425     elsif attrib_val.respond_to? :clear_unsaved_changes
426       attrib_val.send :clear_unsaved_changes
427     end
428   end
429   ext_attrs_clear_unsaved_changes if self.class.include? Jamf::Extendable
430   @unsaved_changes = {}
431 end
mutable?() click to toggle source

Are objects of this class mutable? @see the class method in OAPIObject

    # File lib/jamf/api/jamf_pro/base_classes/oapi_object.rb
376 def mutable?
377   self.class.mutable?
378 end
pretty_jamf_json() click to toggle source

Print the JSON version of the to_jamf outout mostly for debugging/troubleshooting

    # File lib/jamf/api/jamf_pro/base_classes/oapi_object.rb
454 def pretty_jamf_json
455   puts JSON.pretty_generate(to_jamf)
456 end
pretty_print_instance_variables() click to toggle source

Remove large cached items from the instance_variables used to create pretty-print (pp) output.

@return [Array] the desired instance_variables

Calls superclass method
    # File lib/jamf/api/jamf_pro/base_classes/oapi_object.rb
464 def pretty_print_instance_variables
465   vars = super.sort
466   vars.delete :@init_data
467   vars
468 end
sha1_hash() click to toggle source

The SHA1 hash of all the values of our properties as defined in the OAPI schema

    # File lib/jamf/api/jamf_pro/base_classes/oapi_object.rb
479 def sha1_hash
480   Digest::SHA1.hexdigest(to_jamf.to_s)
481 end
to_jamf() click to toggle source

@return [Hash] The data to be sent to the API, as a Hash

to be converted to JSON before sending to the JPAPI
    # File lib/jamf/api/jamf_pro/base_classes/oapi_object.rb
436 def to_jamf
437   jamf_data = {}
438   self.class::OAPI_PROPERTIES.each do |attr_name, attr_def|
439     raw_value = instance_variable_get "@#{attr_name}"
440     jamf_data[attr_name] = attr_def[:multi] ? multi_to_jamf(raw_value, attr_def) : single_to_jamf(raw_value, attr_def)
441   end
442   jamf_data
443 end
to_json(*_args) click to toggle source

@return [String] the JSON to be sent to the API for this

object
    # File lib/jamf/api/jamf_pro/base_classes/oapi_object.rb
448 def to_json(*_args)
449   to_jamf.to_json
450 end
unsaved_changes() click to toggle source

a hash of all unsaved changes

    # File lib/jamf/api/jamf_pro/base_classes/oapi_object.rb
382 def unsaved_changes
383   return {} unless self.class.mutable?
384 
385   @unsaved_changes ||= {}
386 
387   changes = @unsaved_changes.dup
388 
389   self.class::OAPI_PROPERTIES.each do |attr_name, attr_def|
390     # skip non-Class attrs
391     next unless attr_def[:class].is_a? Class
392 
393     # the current value of the thing, e.g. a Location
394     # which may have unsaved changes
395     value = instance_variable_get "@#{attr_name}"
396 
397     # skip those that don't have any changes
398     next unless value.respond_to? :unsaved_changes?
399 
400     attr_changes = value.unsaved_changes
401     next if attr_changes.empty?
402 
403     # add the sub-changes to ours
404     changes[attr_name] = attr_changes
405   end
406   changes[:ext_attrs] = ext_attrs_unsaved_changes if self.class.include? Jamf::Extendable
407   changes
408 end
unsaved_changes?() click to toggle source

return true if we or any of our attributes have unsaved changes

    # File lib/jamf/api/jamf_pro/base_classes/oapi_object.rb
412 def unsaved_changes?
413   return false unless self.class.mutable?
414 
415   !unsaved_changes.empty?
416 end

Private Instance Methods

initialize_multi_value_attr_array(attr_name) click to toggle source

Initialize a multi-values attribute as an empty array if it hasn’t been created yet

    # File lib/jamf/api/jamf_pro/base_classes/oapi_object.rb
489 def initialize_multi_value_attr_array(attr_name)
490   return if instance_variable_get("@#{attr_name}").is_a? Array
491 
492   instance_variable_set("@#{attr_name}", [])
493 end
multi_to_jamf(raw_array, attr_def) click to toggle source

Call to_jamf on an array value

    # File lib/jamf/api/jamf_pro/base_classes/oapi_object.rb
587 def multi_to_jamf(raw_array, attr_def)
588   raw_array ||= []
589   raw_array.map { |raw_value| single_to_jamf(raw_value, attr_def) }.compact
590 end
note_unsaved_change(attr_name, old_value) click to toggle source
    # File lib/jamf/api/jamf_pro/base_classes/oapi_object.rb
495 def note_unsaved_change(attr_name, old_value)
496   return unless self.class.mutable?
497 
498   @unsaved_changes ||= {}
499   new_val = instance_variable_get "@#{attr_name}"
500   if @unsaved_changes[attr_name]
501     @unsaved_changes[attr_name][:new] = new_val
502   else
503     @unsaved_changes[attr_name] = { old: old_value, new: new_val }
504   end
505 end
parse_enum_value(api_value, attr_name, attr_def) click to toggle source

Parse an api value into an attribute with an enum

@param (see parse_single_init_value) @return (see parse_single_init_value)

    # File lib/jamf/api/jamf_pro/base_classes/oapi_object.rb
574 def parse_enum_value(api_value, attr_name, attr_def)
575   Jamf::Validate.in_enum  api_value, enum: attr_def[:enum],
576                                      msg: "#{api_value} is not in the allowed values for attribute #{attr_name}. Must be one of: #{attr_def[:enum].join ', '}"
577 end
parse_init_data(data) click to toggle source

take data from the API and populate an our instance attributes

@param data The parsed API JSON data for this instance

@return [void]

    # File lib/jamf/api/jamf_pro/base_classes/oapi_object.rb
513 def parse_init_data(data)
514   self.class::OAPI_PROPERTIES.each do |attr_name, attr_def|
515     if data.is_a? Hash
516       unless data.key? attr_name
517         raise Jamf::InvalidDataError, "Initialization must include the key '#{attr_name}:'" if attr_def[:required]
518 
519         next
520       end
521 
522       value =
523         if attr_def[:multi]
524           raw_array = data[attr_name] || []
525           raw_array.map { |v| parse_single_init_value v, attr_name, attr_def }
526         else
527           parse_single_init_value data[attr_name], attr_name, attr_def
528         end
529     else # not a Hash, this is a single-value object, so the data is the value
530       value = data
531     end
532 
533     instance_variable_set "@#{attr_name}", value
534   end # OAPI_PROPERTIES.each
535 end
parse_single_init_value(api_value, attr_name, attr_def) click to toggle source

Parse an individual value from the API into an attribute or a member of a multi attribute Description of parse_single_init_value

@param api_value [Object] The parsed JSON value from the API @param attr_name [Symbol] The attribute we’re processing @param attr_def [Hash] The attribute definition

@return [Object] The storable value.

    # File lib/jamf/api/jamf_pro/base_classes/oapi_object.rb
547 def parse_single_init_value(api_value, attr_name, attr_def)
548   # we do get nils from the API, and they should stay nil
549   return nil if api_value.nil?
550 
551   # an enum value
552   if attr_def[:enum]
553     parse_enum_value(api_value, attr_name, attr_def)
554 
555   # a Class value
556   elsif attr_def[:class].instance_of? Class
557     attr_def[:class].new api_value
558 
559   # a :j_id value. See the docs for OAPI_PROPERTIES in Jamf::OAPIObject
560   elsif attr_def[:class] == :j_id
561     api_value.to_s
562 
563   # a JSON value
564   else
565     api_value
566   end # if attr_def[:class].class
567 end
single_to_jamf(raw_value, _attr_def) click to toggle source

call to_jamf on a single value if it knows that method

    # File lib/jamf/api/jamf_pro/base_classes/oapi_object.rb
581 def single_to_jamf(raw_value, _attr_def)
582   raw_value.respond_to?(:to_jamf) ? raw_value.to_jamf : raw_value
583 end
to_s() click to toggle source

A meaningful string representation of this object

@return [String]

    # File lib/jamf/api/jamf_pro/base_classes/oapi_object.rb
613 def to_s
614   inspect
615 end
validate_attr(attr_name, value) click to toggle source

wrapper for class method

    # File lib/jamf/api/jamf_pro/base_classes/oapi_object.rb
593 def validate_attr(attr_name, value)
594   self.class.validate_attr attr_name, value
595 end