lib/jss/api_object.rb in ruby-jss-0.14.0 vs lib/jss/api_object.rb in ruby-jss-1.0.0b2
- old
+ new
@@ -118,10 +118,15 @@
# For more details about this hash, see {APIObject::DEFAULT_LOOKUP_KEYS},
# {APIObject.fetch}, and {APIObject#lookup_object_data}
#
class APIObject
+ # Constants
+ ####################################
+
+ OK_INSTANTIATORS = ['make', 'fetch', 'block in fetch'].freeze
+
# Class Methods
#####################################
# Return an Array of Hashes for all objects of this subclass in the JSS.
#
@@ -286,17 +291,16 @@
#
# @return [Integer, nil] the id of the matching object, or nil if it doesn't exist
#
def self.valid_id(identifier, refresh = false, api: JSS.api)
return identifier if all_ids(refresh, api: api).include? identifier
- id = nil
all_lookup_keys.keys.each do |key|
next if key == :id
id = map_all_ids_to(key).invert[identifier]
return id if id
end # do key
- id
+ nil
end
# Convert an Array of Hashes of API object data to a
# REXML element.
#
@@ -424,18 +428,17 @@
end
# Retrieve an object from the API.
#
# This is the preferred way to retrieve existing objects from the JSS.
- # It's a wrapper for using APIObject.new
- # and avoids the confusion of using ruby's .new class method when you're not
- # creating a new object.
+ # It's a wrapper for using APIObject.new and avoids the confusion of using
+ # ruby's .new class method when you're not creating a new object in the JSS
#
# For creating new objects in the JSS, use {APIObject.make}
#
# @param args[Hash] The data for fetching an object, such as id: or name:
- # See {APIObject#initialize}
+ # Each APIObject subclass can define additional lookup keys for fetching.
#
# @return [APIObject] The ruby-instance of a JSS object
#
def self.fetch(arg, api: JSS.api)
raise JSS::UnsupportedError, 'JSS::APIObject cannot be instantiated' if self.class == JSS::APIObject
@@ -454,11 +457,11 @@
lookup_key_list_methods.each do |key, method_name|
return new(key => arg, :api => api) if method_name && send(method_name).include?(arg)
end # each key
# if we're here, we couldn't find a matching object
- raise NoSuchItemError, "No #{self::RSRC_OBJECT_KEY} found matching '#{arg}'"
+ raise NoSuchItemError, "No matching #{self::RSRC_OBJECT_KEY} found"
end # fetch
# Make a ruby instance of a not-yet-existing APIObject.
#
# This is the preferred way to create new objects in the JSS.
@@ -481,10 +484,19 @@
raise ArgumentError, "Use '#{self.class}.fetch id: xx' to retrieve existing JSS objects" if args[:id]
args[:id] = :new
new args
end
+ # Disallow direct use of ruby's .new class method for creating instances.
+ # Require use of .fetch or .make
+ def self.new(**args)
+ calling_method = caller_locations(1..1).first.label
+ # puts "Called By: #{calling_method}"
+ raise JSS::UnsupportedError, 'Use .fetch or .make to instantiate APIObject classes' unless OK_INSTANTIATORS.include? calling_method
+ super
+ end
+
# Delete one or more API objects by jss_id without instantiating them.
# Non-existent id's are skipped and an array of skipped ids is returned.
#
# If an Array is provided, it is passed through #uniq! before being processed.
#
@@ -607,34 +619,26 @@
#
# @option args :id[Integer] the jss id to look up
#
# @option args :name[String] the name to look up
#
- # @option args :data[Hash] the JSON output of a separate {JSS::APIConnection} query
- # NOTE: This arg is deprecated and will be removed in a future release.
+ # @option args :fetch_rsrc[String] a non-standard resource for fetching
+ # API data e.g. to limit the data returned
#
#
def initialize(args = {})
args[:api] ||= JSS.api
@api = args[:api]
- raise JSS::UnsupportedError, 'JSS::APIObject cannot be instantiated' if self.class == JSS::APIObject
+ raise JSS::UnsupportedError, 'JSS::APIObject is a metaclass and cannot be instantiated' if self.class == JSS::APIObject
- ####### Previously looked-up JSON data
- # DEPRECATED: pre-lookedup data is never used
- # and support for it will be going away.
- if args[:data]
-
- @init_data = args[:data]
-
- validate_external_init_data
-
- ###### Make a new one in the JSS, but only if we've included the Creatable module
- elsif args[:id] == :new
+ # we're making a new one in the JSS
+ if args[:id] == :new
validate_init_for_creation(args)
setup_object_for_creation(args)
@need_to_update = true
- ###### Look up the data via the API
+
+ # we're instantiating an existing one in the jss
else
@init_data = look_up_object_data(args)
@need_to_update = false
end ## end arg parsing
@@ -769,10 +773,11 @@
#
def pretty_print_instance_variables
vars = instance_variables.sort
vars.delete :@api
vars.delete :@init_data
+ vars.delete :@main_subset
vars
end
# Make an entry in this object's Object History.
# For this to work, the APIObject subclass must define
@@ -869,10 +874,11 @@
puts
end
# Private Instance Methods
#####################################
+
private
# Raise an exception if object history is not
# available for this object
#
@@ -890,10 +896,12 @@
# raising exceptions if not valid.
#
# DEPRECATED: pre-lookedup data is never used
# and support for it will be going away.
#
+ # TODO: delete this and all defined VALID_DATA_KEYS
+ #
# @return [void]
#
def validate_external_init_data
# data must include all they keys in REQUIRED_DATA_KEYS + VALID_DATA_KEYS
# in either the main hash keys or the :general sub-hash, if it exists
@@ -935,25 +943,68 @@
# @param args[Hash] The args passed to #initialize
#
# @return [Hash] The parsed JSON data for the object from the API
#
def look_up_object_data(args)
- # what lookup key are we using?
+ rsrc =
+ if args[:fetch_rsrc]
+ args[:fetch_rsrc]
+ else
+ # what lookup key are we using?
+ # TODO: simplify this, see the notes at #find_rsrc_keys
+ rsrc_key, lookup_value = find_rsrc_keys(args)
+ "#{self.class::RSRC_BASE}/#{rsrc_key}/#{lookup_value}"
+ end
+
+ # if needed, a non-standard object key can be passed by a subclass.
+ # e.g. User when loookup is by email.
+ args[:rsrc_object_key] ||= self.class::RSRC_OBJECT_KEY
+
+ raw_json =
+ if defined? self.class::USE_XML_WORKAROUND
+ # if we're here, the API JSON is borked, so use the XML
+ JSS::XMLWorkaround.data_via_xml rsrc, self.class::USE_XML_WORKAROUND, @api
+ else
+ # otherwise
+ @api.get_rsrc(rsrc)
+ end
+ raw_json[args[:rsrc_object_key]]
+ rescue RestClient::ResourceNotFound
+ raise NoSuchItemError, "No #{self.class::RSRC_OBJECT_KEY} found matching resource #{rsrc}"
+ end
+
+ # Given initialization args, determine the rsrc key and
+ # lookup value to be used in building the GET resource.
+ # E.g. for looking up something with id 345,
+ # return the rsrc_key :id, and the value 345, which
+ # can be used to create the resrouce
+ # '/things/id/345'
+ #
+ # CHANGE: some the new patch-related objects don't have
+ # GET resources by name, only id. So this method now always
+ # returns the id-based resource.
+ #
+ # TODO: clean up this and the above methods, since the
+ # id-only get rsrcs actually should simplify the code.
+ #
+ # @param args[Hash] The args passed to #initialize
+ #
+ # @return [Array] Two item array: [ rsrc_key, lookup_value]
+ #
+ def find_rsrc_keys(args)
lookup_keys = self.class.lookup_keys
lookup_key = (self.class.lookup_keys & args.keys)[0]
+
raise JSS::MissingDataError, "Args must include a lookup key, one of: :#{lookup_keys.join(', :')}" unless lookup_key
- rsrc_key = self.class.rsrc_keys[lookup_key]
- rsrc = "#{self.class::RSRC_BASE}/#{rsrc_key}/#{args[lookup_key]}"
+ vid = self.class.valid_id args[lookup_key], :refresh
- # if needed, a non-standard object key can be passed by a subclass.
- # e.g. User when loookup is by email.
- rsrc_object_key = args[:rsrc_object_key] ? args[:rsrc_object_key] : self.class::RSRC_OBJECT_KEY
+ raise NoSuchItemError, "No #{self.class::RSRC_OBJECT_KEY} found with #{lookup_key} '#{args[lookup_key]}'" unless vid
- return @api.get_rsrc(rsrc)[rsrc_object_key]
- rescue RestClient::ResourceNotFound
- raise NoSuchItemError, "No #{self.class::RSRC_OBJECT_KEY} found matching: #{rsrc_key}/#{args[lookup_key]}"
+ [:id, vid]
+ # rsrc_key = self.class.rsrc_keys[lookup_key]
+ # [rsrc_key, args[lookup_key]]
end
# Start examining the @init_data recieved from the API
#
# @return [void]
@@ -969,11 +1020,11 @@
if @main_subset[:id] == :new
@id = 0
@in_jss = false
else
- @id = @main_subset[:id]
+ @id = @main_subset[:id].to_i
@in_jss = true
end
@rest_rsrc = "#{self.class::RSRC_BASE}/id/#{@id}"
@@ -1015,11 +1066,11 @@
# parse category data during initialization
#
# @return [void]
#
def initialize_category
- parse_category if categorizable?
+ parse_category if categorizable? && @in_jss
end
# parse site data during initialization
#
# @return [void]
@@ -1143,10 +1194,11 @@
### APIObject SubClasses with SubClasses
require 'jss/api_object/advanced_search'
require 'jss/api_object/configuration_profile'
require 'jss/api_object/extension_attribute'
require 'jss/api_object/group'
+require 'jss/api_object/patch_source'
### APIObject SubClasses without SubClasses
require 'jss/api_object/account'
require 'jss/api_object/building'
require 'jss/api_object/category'
@@ -1160,10 +1212,10 @@
require 'jss/api_object/mobile_device'
require 'jss/api_object/mobile_device_application'
require 'jss/api_object/netboot_server'
require 'jss/api_object/network_segment'
require 'jss/api_object/package'
-require 'jss/api_object/patch'
+require 'jss/api_object/patch_title'
require 'jss/api_object/patch_policy'
require 'jss/api_object/peripheral_type'
require 'jss/api_object/peripheral'
require 'jss/api_object/policy'
require 'jss/api_object/removable_macaddr'