<commit_before>require_relative 'account'
require_relative 'transaction'
require_relative 'info'
require 'json'

module Plaid
  class User
    attr_accessor :accounts, :transactions, :access_token, :type, :permissions, :api_res, :pending_mfa_questions, :info, :information

    def initialize
      self.accounts = []
      self.transactions = []
      self.permissions = []
      self.access_token = ''
      self.api_res = ''
      self.info = {}
      self.information = Information.new
    end

    # API: semi-private
    # This class method instantiates a new Plaid::Account object and updates it with the results
    # from the API
    def self.build(res, api_level = nil)
      self.new.update(res, api_level)
    end

    # API: semi-private
    # This method updates Plaid::Account with the results returned from the API
    def update(res, api_level = nil)
      self.permit! api_level

      if res[:msg].nil?
        populate_user!(res)
        clean_up_user!
      else
        set_mfa_request!(res)
      end

      return self
    end

    def mfa_authentication(auth, type = nil)
      type = self.type if type.nil?
      auth_path = self.permissions.last + '/step'
      res = Plaid::Connection.post(auth_path, { mfa: auth, access_token: self.access_token, type: type })
      self.accounts = []
      self.transactions = []
      update(res)
    end

    def select_mfa_method(selection, type=nil)
      type = self.type if type.nil?
      auth_path = self.permissions.last + '/step'
      res = Plaid::Connection.post(auth_path, { options: { send_method: selection }.to_json, access_token: self.access_token, type: type })
      update(res, self.permissions.last)
    end

    def permit?(auth_level)
      self.permissions.include? auth_level
    end

    def permit!(api_level)
      return if api_level.nil? || self.permit?(api_level)
      self.permissions << api_level
    end

    # TODO: (2.0) auth_level should be symbols instead of string
    def get(auth_level, options = {})
      return false unless self.permit? auth_level
      case auth_level
      when 'auth'
        update(Plaid::Connection.post('auth/get', access_token: self.access_token))
      when 'connect'
        payload = { access_token: self.access_token }.merge(options)
        update(Plaid::Connection.post('connect/get', payload))
      when 'info'
        update(Plaid::Connection.secure_get('info', self.access_token))
      else
        raise "Invalid auth level: #{auth_level}"
      end
    end

    def get_auth
      get('auth')
    end

    def get_connect(options={})
      get('connect', options)
    end

    def get_info
      get('info')
    end

    def update_info(username,pass,pin=nil)
      return false unless self.permit? 'info'

      payload = { username: username, password: <PASSWORD>, access_token: self.access_token }
      payload.merge!(pin: pin) if pin
      update(Plaid.patch('info', payload))
    end

    def update_balance
      update(Plaid::Connection.post('balance', { access_token: self.access_token }))
    end

    def upgrade(api_level=nil)
      if api_level.nil?
        api_level = 'auth' unless self.permit? 'auth'
        api_level = 'connect' unless self.permit? 'connect'
      end
      res = Plaid::Connection.post('upgrade', { access_token: self.access_token, upgrade_to: api_level })

      # Reset accounts and transaction
      self.accounts = []
      self.transactions = []
      update(res)
    end

    def delete_user
      Plaid::Connection.delete('info', { access_token: self.access_token })
    end

    private

    def clean_up_user!
      self.accounts.select! { |c| c.instance_of? Plaid::Account }
    end

    def set_mfa_request!(res)
      self.access_token = res[:body]['access_token']
      self.pending_mfa_questions = res[:body]
      self.api_res = res[:msg]
    end

    def populate_user!(res)
      res['accounts'].each do |account|
        if self.accounts.any? { |h| h == account['_id'] }
          owned_account = self.accounts.find { |h| h == account['_id'] }
          owned_account.new(account)
        else
          self.accounts << Plaid::Account.build(account)
        end<commit_msg>Annotated Plaid::User with public or semi-private methods<commit_after>require_relative 'account'
require_relative 'transaction'
require_relative 'info'
require 'json'

module Plaid
  class User
    attr_accessor :accounts, :transactions, :access_token, :type, :permissions, :api_res, :pending_mfa_questions, :info, :information

    def initialize
      self.accounts = []
      self.transactions = []
      self.permissions = []
      self.access_token = ''
      self.api_res = ''
      self.info = {}
      self.information = Information.new
    end

    # API: semi-private
    # This class method instantiates a new Plaid::Account object and updates it with the results
    # from the API
    def self.build(res, api_level = nil)
      self.new.update(res, api_level)
    end

    # API: semi-private
    # This method updates Plaid::Account with the results returned from the API
    def update(res, api_level = nil)
      self.permit! api_level

      if res[:msg].nil?
        populate_user!(res)
        clean_up_user!
      else
        set_mfa_request!(res)
      end

      return self
    end

    # API: public
    # Use this method to send back the MFA code or answer
    def mfa_authentication(auth, type = nil)
      type = self.type if type.nil?
      auth_path = self.permissions.last + '/step'
      res = Plaid::Connection.post(auth_path, { mfa: auth, access_token: self.access_token, type: type })
      self.accounts = []
      self.transactions = []
      update(res)
    end

    # API: public
    # Use this method to select the MFA method
    def select_mfa_method(selection, type=nil)
      type = self.type if type.nil?
      auth_path = self.permissions.last + '/step'
      res = Plaid::Connection.post(auth_path, { options: { send_method: selection }.to_json, access_token: self.access_token, type: type })
      update(res, self.permissions.last)
    end

    # API: public
    # Use this method to find out API levels available for this user
    def permit?(auth_level)
      self.permissions.include? auth_level
    end

    # API: semi-private
    # Internal helper method to set the available API levels
    def permit!(api_level)
      return if api_level.nil? || self.permit?(api_level)
      self.permissions << api_level
    end

    # API: semi-private
    # Gets auth, connect, or info of the user
    # TODO: (2.0) auth_level should be symbols instead of string
    def get(auth_level, options = {})
      return false unless self.permit? auth_level
      case auth_level
      when 'auth'
        update(Plaid::Connection.post('auth/get', access_token: self.access_token))
      when 'connect'
        payload = { access_token: self.access_token }.merge(options)
        update(Plaid::Connection.post('connect/get', payload))
      when 'info'
        update(Plaid::Connection.secure_get('info', self.access_token))
      else
        raise "Invalid auth level: #{auth_level}"
      end
    end

    # API: semi-private
    def get_auth
      get('auth')
    end

    # API: semi-private
    def get_connect(options={})
      get('connect', options)
    end

    # API: semi-private
    def get_info
      get('info')
    end

    # API: semi-private
    # Helper method to update user information
    # Requires 'info' api level
    def update_info(username,pass,pin=nil)
      return false unless self.permit? 'info'

      payload = { username: username, password: <PASSWORD>, access_token: self.access_token }
      payload.merge!(pin: pin) if pin
      update(Plaid.patch('info', payload))
    end

    # API: semi-private
    # Helper method to update user balance
    def update_balance
      update(Plaid::Connection.post('balance', { access_token: self.access_token }))
    end

    # API: public
    # Use this method to upgrade a user to another api level
    def upgrade(api_level=nil)
      if api_level.nil?
        api_level = 'auth' unless self.permit? 'auth'
        api_level = 'connect' unless self.permit? 'connect'
      end
      res = Plaid::Connection.post('upgrade', { access_token: self.access_token, upgrade_to: api_level })

      # Reset accounts and transaction
      self.accounts = []
      self.transactions = []
      update(res)
    end

    # API: public
    # Use this method to delete a user from the Plaid API
    def delete_user
      Plaid::Connection.delete('info', { access_token: self.access_token })
    end

    private

    def clean_up_user!
      self.accounts.select! { |c| c.instance_of? Plaid::Account }
    end

    def set_mfa_request!(res)
      self.access_token = res[:body]['access_token']
      self.pending_mfa_questions = res[:body]
      self.api_res = res[:msg]
    end

    def populate_user!(res)
      res['accounts'].each do |account|
        if self.accounts.any? { |h| h == account['_id'] }
          owned_account = self.accounts.find { |h| h == account['_id'] }
          owned_account.new(account)
        else
          self.accounts << Plaid::Account.build(account)
        end
{'fraction_non_alphanumeric': 0.10687022900763359, 'fraction_numerical': 0.0004362050163576881, 'mean_word_length': 2.726533929297034, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2816483', 'n_tokens_mistral': 2848, 'n_tokens_neox': 2787, 'n_words': 723}