class LicenseAcceptance::Acceptor

def self.check_and_persist(product_id, version, opts = {})

def self.check_and_persist(product_id, version, opts = {})
  new(opts).check_and_persist(product_id, version)
end

def self.check_and_persist!(product_id, version, opts = {})

def self.check_and_persist!(product_id, version, opts = {})
  new(opts).check_and_persist!(product_id, version)
end

def acceptance_value

that was used. Value is only guaranteed to be set after calling check_and_persist.
have to know the precedence order between provided/environment/argument. Can just get back the value
Return the value that was matched ("accept", "accept-no-persist", etc.). Used by callers so they do not
def acceptance_value
  @acceptance_value
end

def acceptance_value_provided?

def acceptance_value_provided?
  provided_strategy.value? || env_strategy.value? || arg_strategy.value?
end

def accepted?

def accepted?
  provided_strategy.accepted? || env_strategy.accepted? || arg_strategy.accepted?
end

def accepted_license_prompt?(product_relationship, missing_licenses)

def accepted_license_prompt?(product_relationship, missing_licenses)
  prompt_strategy.request(missing_licenses) do
    # We have to infer the acceptance value if they use the prompt to accept
    if config.persist
      @acceptance_value = ACCEPT
      file_strategy.persist(product_relationship, missing_licenses)
    else
      @acceptance_value = ACCEPT_NO_PERSIST
      []
    end
  end
end

def accepted_no_persist?

no-persist is silent too
def accepted_no_persist?
  provided_strategy.no_persist? || env_strategy.no_persist? || arg_strategy.no_persist?
end

def accepted_silent?

persist but be silent like no-persist
def accepted_silent?
  provided_strategy.silent? || env_strategy.silent? || arg_strategy.silent?
end

def check_and_persist(product_id, version)

def check_and_persist(product_id, version)
  if accepted_no_persist?
    logger.debug("Chef License accepted with no persistence")
    @acceptance_value = ACCEPT_NO_PERSIST
    return true
  end
  product_relationship = product_reader.lookup(product_id, version)
  missing_licenses = file_strategy.accepted?(product_relationship)
  # They have already accepted all licenses and stored their acceptance in the persistent files
  if missing_licenses.empty?
    logger.debug("All licenses present")
    @acceptance_value = ACCEPT
    return true
  end
  if accepted? || accepted_silent?
    if config.persist
      errs = file_strategy.persist(product_relationship, missing_licenses)
      if errs.empty?
        output_num_persisted(missing_licenses.size) unless accepted_silent?
      else
        output_persist_failed(errs)
      end
    end
    @acceptance_value = accepted_silent? ? ACCEPT_SILENT : ACCEPT
    return true
  end
  if acceptance_value_provided?
    value = provided_strategy.value || env_strategy.value || arg_strategy.value
    output.puts("Unrecognized license acceptance value '#{value}', expected one of: '#{ACCEPT}', '#{ACCEPT_SILENT}', '#{ACCEPT_NO_PERSIST}'")
    raise LicenseNotAcceptedError.new(product_relationship.parent, missing_licenses)
  end
  return true if output.isatty && accepted_license_prompt?(product_relationship, missing_licenses)
  raise LicenseNotAcceptedError.new(product_relationship.parent, missing_licenses)
end

def check_and_persist!(product_id, version)

handle the exception.
wrapper. Apps with more complex logic (like logging to a logging engine) should call the non-bang version and
For applications that just need simple logic to handle a failed license acceptance flow we include this small
def check_and_persist!(product_id, version)
  check_and_persist(product_id, version)
rescue LicenseNotAcceptedError => e
  output.puts "#{e.product.pretty_name} cannot execute without accepting the license"
  exit 172
end

def id_from_mixlib(mixlib_name)

id as this library knows it.
Some callers only know about mixlib names so we need a way for them to get the product
def id_from_mixlib(mixlib_name)
  product = product_reader.lookup_by_mixlib(mixlib_name)
  return nil if product.nil?
  product.id
end

def initialize(opts = {})

def initialize(opts = {})
  @config = Config.new(opts)
  Logger.initialize(config.logger)
  @product_reader = ProductReader.new
  product_reader.read
  @env_strategy = Strategy::Environment.new(ENV)
  @file_strategy = Strategy::File.new(config)
  @arg_strategy = Strategy::Argument.new(ARGV)
  @prompt_strategy = Strategy::Prompt.new(config)
  @provided_strategy = Strategy::ProvidedValue.new(opts.fetch(:provided, nil))
  @acceptance_value = nil
end

def license_required?(mixlib_name, version)

Check whether the specified product requires license acceptance for the given version.
def license_required?(mixlib_name, version)
  product = product_reader.lookup_by_mixlib(mixlib_name)
  return false if product.nil?
  # If they don't pass a version we assume they want latest
  # All versions in all channels require license acceptance
  return true if %w{latest unstable current stable}.include?(version.to_s) || version.nil?
  Gem::Version.new(version) >= Gem::Version.new(product.license_required_version)
end

def output_num_persisted(count)

we still want to output the fact that the filesystem was changed.
In the case where users accept with a command line argument or environment variable
def output_num_persisted(count)
  s = count > 1 ? "s" : ""
  output.puts <<~EOM
    #{Strategy::Prompt::BORDER}
    #{Strategy::Prompt::CHECK} #{count} product license#{s} accepted.
    #{Strategy::Prompt::BORDER}
  EOM
end

def output_persist_failed(errs)

def output_persist_failed(errs)
  output.puts <<~EOM
    #{Strategy::Prompt::BORDER}
    #{Strategy::Prompt::CHECK} Product license accepted.
    Could not persist acceptance:\n\t* #{errs.map(&:message).join("\n\t* ")}
    #{Strategy::Prompt::BORDER}
  EOM
end