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'