# frozen_string_literal: true require_relative 'tooling' # Methods for working with instances of global to soql objects, not global overall # Some of these are called internally by methods on an instance of SoqlData, for instance, # case = Case.create # case.update # => This will call Case.update passing it's own id # See https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_sobject_describe.htm module SoqlGlobalObjectData include LeapSalesforce::Tooling # @return [Hash] List of ids to delete when deleting a particular object attr_accessor :ids_to_delete # Override to handle removing dependent records # @param [String] _id Id of record being deleted def remove_dependent_records(_id); end # Find the data for a single SoqlObject using the calling class's table. # Will get the latest created date. # @example Get any contact # Contact.find # @example Get a contact where LastName is 'Bob' using backend name # Contact.find(LastName: 'Bob') # @example Get a contact that includes 'Test' in their first name (using ruby accessor name) # Contact.find(first_name: '~%Test%') # @example Get a contact created 10 days ago # Contact.find CreatedDate: "<#{10.days.ago}" # @param [Hash] lookup Key value pair unique to Salesforce to query for # @option lookup [Boolean] :teardown Whether to remove id after scenario finished # @return [< SoqlData] Instance of itself storing reference to found object def find(lookup = {}) id_lookup = soql.lookup_id lookup lookup.key?(:Id) ? id_lookup : soql.data_from_url(id_lookup['$..url'], lookup) end # @return [LeapSalesforce::Soql] Object to handle Soql interactions def soql @soql ||= LeapSalesforce::Soql.new(self) end # @deprecated # Get details of itself by searching for it's id # Store response within itself # @return [< SoqlData] Exchange with details of data def get(lookup) LeapSalesforce.logger.warn "Method 'get' called when it is deprecated" \ " from #{caller_locations[0]}" find(lookup) end # @return [String] Id that matches filter def id_where(lookup) soql.lookup_id(lookup).id end # @return [Boolean] Whether any result for lookup def any?(lookup = {}) soql.lookup_id(lookup)[:totalSize] != 0 end alias any_where? any? # Perform the code in the block for all the ids matching a query. # If no block, return a list of objects # @param [Hash] lookup Key value pair unique to Salesforce to query for # @yield [id] Perform block for each id returned. The 'id' parameter in a block represents an id matching the query # @return [Array] List of ids matching criteria. Only used if no block given def ids_where(lookup = {}) lookup[:limit] ||= nil # Don't limit results returned SoqlHandler.new("Each Id where #{self}").use results = soql.query soql.soql_id(lookup), wait: false ids = results.ids if block_given? ids.each { |id| yield(id) } else ids end end alias each_id_with ids_where # @param [Hash] lookup Key value pair unique to Salesforce to query for # @return [Array] List of Soql objects matching criteria def where(lookup) ids = each_id_with lookup ids.collect { |id| find(Id: id) } end alias each_with where # Return list of objects (matching criteria if passed) # @param [Hash] lookup Key value pair unique to Salesforce to query for. Leave empty for all # @return [Array] List of all soql objects def list(lookup = {}) where(lookup) end # Remove all ids from table that match lookup criteria # @param [Hash] lookup Key value pair unique to Salesforce to query for def delete_ids_with(lookup) each_id_with(lookup, &method(:delete)) end # Remove object from Salesforce with provided id # @param [String] id Id of element to update # @param [Hash] data Key value pairs with data to update # @return [self] SoqlData object representing result of API update call def update(id, data) enforce_type_for id must_pass = data.delete(:must_pass) data = data.transform_values do |value| value.is_a?(Time) ? value.salesforce_format : value end data.transform_keys! { |key| soql.map_key(key) } # Map keys to valid field names SoqlHandler.new("Update #{id}").use update = new("Update #{self}, #{id} with '#{data}'", method: :patch, suburl: "sobjects/#{soql_object_name}/#{id}", body: data) update.call return update unless must_pass update.successful? update end # Remove object from Salesforce with provided id # @example Delete a contact with a specified id, failing if the delete fails # Contact.delete '0032v00002rgv2pAAA', must_pass: true # # @param [String, Symbol] id Id of element to remove # @param [Boolean] must_pass Whether to raise exception if call is not successful # @return [self] Exchange object making delete call def delete(id, must_pass: false) enforce_type_for id SoqlData.ids_to_delete.reject! { |table, id_to_remove| table == self && id_to_remove == id } # Remove id from list to delete remove_dependent_records(id) SoqlHandler.new("Delete #{id}").use delete = new("SOQL Delete #{id}", method: :delete, suburl: "sobjects/#{soql_object_name}/#{id}") delete.call return delete unless must_pass delete.successful? delete end def soql_element(name, backend_name) # Either set the element (if creating a new record) or update the object # @param [String] new_value Value to update record to define_method("#{name}=") do |new_value| if @response # Performing an update @response = update(backend_name => new_value).response else # Setting value on creating new object self[backend_name] = new_value.class < SoqlData ? new_value.id : new_value end end # @return [String] Value of backend name define_method(name.to_s) { self[backend_name] } # @return [String] Name of backend name for element define_method("#{name}_element") { backend_name } end private # Raise error if incorrect type passed # @param [String, Symbol] id Id to verify def enforce_type_for(id) return if [String, Symbol].include? id.class raise ArgumentError, "Id must be String or Symbol but was #{id.class} to delete" end end