class Jamf::OAPIObject
The parent class for all objects auto-generated in the Jamf::OAPISchemas
module more docs to come
Attributes
the raw hash from which this object was constructed @return [Hash]
Public Class Methods
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
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
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
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
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
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
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
# 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
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
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
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
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 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
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
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 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
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
# 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
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
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
Remove large cached items from the instance_variables used to create pretty-print (pp) output.
@return [Array] the desired instance_variables
# 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
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
@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
@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
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
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 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
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
# 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 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
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 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
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
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
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