forest_liana 1.2.5 → 1.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/app/controllers/forest_liana/apimaps_controller.rb +2 -11
- data/app/controllers/forest_liana/sessions_controller.rb +38 -24
- data/app/deserializers/forest_liana/resource_deserializer.rb +17 -0
- data/app/serializers/forest_liana/serializer_factory.rb +1 -9
- data/app/services/forest_liana/allowed_users_getter.rb +36 -0
- data/app/services/forest_liana/collection.rb +3 -33
- data/app/services/forest_liana/has_many_getter.rb +4 -1
- data/app/services/forest_liana/resources_getter.rb +1 -1
- data/app/services/forest_liana/schema_adapter.rb +8 -0
- data/lib/forest_liana/base64_string_io.rb +30 -0
- data/lib/forest_liana/bootstraper.rb +6 -13
- data/lib/forest_liana/version.rb +1 -1
- data/test/services/forest_liana/resources_getter_test.rb +0 -30
- metadata +4 -2
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA1:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 0e5aaaf20833287b7bd967b29fc446819c729c6b
         | 
| 4 | 
            +
              data.tar.gz: 42dcb1bda73a88f0c989f6abbf3432c7ea687478
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: a62ec769824286587ad781b0116c59d95e56b972af8c7bd71572e87b43f5fb108ca78ca7d9078b70a6f68464fa67f58cd85a990dfcaa45960c795be99c97a8ab
         | 
| 7 | 
            +
              data.tar.gz: 55013515cf0b5f3e12f25949cbcd382c99e2cbc92b51f39fa86e99eefdb9950a7768e9b4a0c4109d758587a17329102e7716afce1c4d42996f4baadb0be6858b
         | 
| @@ -1,16 +1,7 @@ | |
| 1 | 
            -
            require 'jsonapi-serializers'
         | 
| 2 | 
            -
             | 
| 3 1 | 
             
            module ForestLiana
         | 
| 4 | 
            -
              class ApimapsController <  | 
| 2 | 
            +
              class ApimapsController < ActionController::Base
         | 
| 5 3 | 
             
                def index
         | 
| 6 | 
            -
                   | 
| 7 | 
            -
             | 
| 8 | 
            -
                  SchemaUtils.tables_names.map do |table_name|
         | 
| 9 | 
            -
                    model = SchemaUtils.find_model_from_table_name(table_name)
         | 
| 10 | 
            -
                    result << SchemaAdapter.new(model).perform if model.try(:table_exists?)
         | 
| 11 | 
            -
                  end
         | 
| 12 | 
            -
             | 
| 13 | 
            -
                  render json: serialize_models(result)
         | 
| 4 | 
            +
                  render nothing: true, status: 204
         | 
| 14 5 | 
             
                end
         | 
| 15 6 | 
             
              end
         | 
| 16 7 | 
             
            end
         | 
| @@ -2,38 +2,52 @@ module ForestLiana | |
| 2 2 | 
             
              class SessionsController < ActionController::Base
         | 
| 3 3 |  | 
| 4 4 | 
             
                def create
         | 
| 5 | 
            -
                   | 
| 5 | 
            +
                  fetch_allowed_users
         | 
| 6 | 
            +
                  user = check_user
         | 
| 7 | 
            +
                  token = encode_token(user) if user
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                  if token
         | 
| 10 | 
            +
                    render json: { token: token }
         | 
| 11 | 
            +
                  else
         | 
| 12 | 
            +
                    render nothing: true, status: 401
         | 
| 13 | 
            +
                  end
         | 
| 14 | 
            +
                end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                private
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                def fetch_allowed_users
         | 
| 19 | 
            +
                  AllowedUsersGetter.new.perform
         | 
| 20 | 
            +
                end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                def check_user
         | 
| 23 | 
            +
                  ForestLiana.allowed_users.find do |allowed_user|
         | 
| 6 24 | 
             
                    allowed_user['email'] == params['email'] &&
         | 
| 7 25 | 
             
                      allowed_user['outlines'].include?(params['outlineId']) &&
         | 
| 8 26 | 
             
                      BCrypt::Password.new(allowed_user['password']) == params['password']
         | 
| 9 27 | 
             
                  end
         | 
| 28 | 
            +
                end
         | 
| 10 29 |  | 
| 11 | 
            -
             | 
| 12 | 
            -
             | 
| 13 | 
            -
             | 
| 30 | 
            +
                def encode_token(user)
         | 
| 31 | 
            +
                  JWT.encode({
         | 
| 32 | 
            +
                    exp: Time.now.to_i + 2.weeks.to_i,
         | 
| 33 | 
            +
                    data: {
         | 
| 34 | 
            +
                      id: user['id'],
         | 
| 35 | 
            +
                      type: 'users',
         | 
| 14 36 | 
             
                      data: {
         | 
| 15 | 
            -
                         | 
| 16 | 
            -
                         | 
| 17 | 
            -
                         | 
| 18 | 
            -
             | 
| 19 | 
            -
             | 
| 20 | 
            -
             | 
| 21 | 
            -
             | 
| 22 | 
            -
             | 
| 23 | 
            -
             | 
| 24 | 
            -
             | 
| 25 | 
            -
                              type: 'outlines',
         | 
| 26 | 
            -
                              id: params['outlineId']
         | 
| 27 | 
            -
                            }]
         | 
| 28 | 
            -
                          }
         | 
| 37 | 
            +
                        email: user['email'],
         | 
| 38 | 
            +
                        first_name: user['first_name'],
         | 
| 39 | 
            +
                        last_name: user['last_name']
         | 
| 40 | 
            +
                      },
         | 
| 41 | 
            +
                      relationships: {
         | 
| 42 | 
            +
                        outlines: {
         | 
| 43 | 
            +
                          data: [{
         | 
| 44 | 
            +
                            type: 'outlines',
         | 
| 45 | 
            +
                            id: params['outlineId']
         | 
| 46 | 
            +
                          }]
         | 
| 29 47 | 
             
                        }
         | 
| 30 48 | 
             
                      }
         | 
| 31 | 
            -
                    } | 
| 32 | 
            -
             | 
| 33 | 
            -
                    render json: { token: token }
         | 
| 34 | 
            -
                  else
         | 
| 35 | 
            -
                    render nothing: true, status: 401
         | 
| 36 | 
            -
                  end
         | 
| 49 | 
            +
                    }
         | 
| 50 | 
            +
                  } , ForestLiana.auth_key, 'HS256')
         | 
| 37 51 | 
             
                end
         | 
| 38 52 | 
             
              end
         | 
| 39 53 | 
             
            end
         | 
| @@ -1,3 +1,5 @@ | |
| 1 | 
            +
            require_relative '../../../lib/forest_liana/base64_string_io'
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            module ForestLiana
         | 
| 2 4 | 
             
              class ResourceDeserializer
         | 
| 3 5 |  | 
| @@ -10,6 +12,7 @@ module ForestLiana | |
| 10 12 | 
             
                  @attributes = extract_attributes
         | 
| 11 13 | 
             
                  extract_relationships
         | 
| 12 14 | 
             
                  extract_paperclip
         | 
| 15 | 
            +
                  extract_carrierwave
         | 
| 13 16 |  | 
| 14 17 | 
             
                  @attributes
         | 
| 15 18 | 
             
                end
         | 
| @@ -53,6 +56,16 @@ module ForestLiana | |
| 53 56 | 
             
                  @attributes.merge!(paperclip_attr) if paperclip_attr
         | 
| 54 57 | 
             
                end
         | 
| 55 58 |  | 
| 59 | 
            +
                def extract_carrierwave
         | 
| 60 | 
            +
                  return unless @resource.respond_to?(:uploaders)
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                  @params['data']['attributes'].each do |key, value|
         | 
| 63 | 
            +
                    if carrierwave_attribute?(key)
         | 
| 64 | 
            +
                      @attributes[key] = ForestLiana::Base64StringIO.new(value)
         | 
| 65 | 
            +
                    end
         | 
| 66 | 
            +
                  end
         | 
| 67 | 
            +
                end
         | 
| 68 | 
            +
             | 
| 56 69 | 
             
                def paperclip_handler?(attr)
         | 
| 57 70 | 
             
                  begin
         | 
| 58 71 | 
             
                    Paperclip.io_adapters.handler_for(attr)
         | 
| @@ -65,5 +78,9 @@ module ForestLiana | |
| 65 78 | 
             
                def column?(attribute)
         | 
| 66 79 | 
             
                  @resource.columns.find {|x| x.name == attribute}.present?
         | 
| 67 80 | 
             
                end
         | 
| 81 | 
            +
             | 
| 82 | 
            +
                def carrierwave_attribute?(attr)
         | 
| 83 | 
            +
                  @resource.uploaders.include?(attr.try(:to_sym))
         | 
| 84 | 
            +
                end
         | 
| 68 85 | 
             
              end
         | 
| 69 86 | 
             
            end
         | 
| @@ -98,15 +98,7 @@ module ForestLiana | |
| 98 98 | 
             
                          relationship_records = object.send(attribute_name)
         | 
| 99 99 |  | 
| 100 100 | 
             
                          if relationship_records.respond_to?(:each)
         | 
| 101 | 
            -
                             | 
| 102 | 
            -
                              ret[:href] = "/forest/#{object.class.table_name}/#{object.id}/#{attribute_name}"
         | 
| 103 | 
            -
                              ret[:meta] = { count: relationship_records.distinct.count }
         | 
| 104 | 
            -
                            else
         | 
| 105 | 
            -
                              ret[:href] = "/forest/#{object.class.table_name}/#{object.id}/#{attribute_name}"
         | 
| 106 | 
            -
                              ret[:meta] = {
         | 
| 107 | 
            -
                                count: relationship_records.count(:id, distinct: true)
         | 
| 108 | 
            -
                              }
         | 
| 109 | 
            -
                            end
         | 
| 101 | 
            +
                            ret[:href] = "/forest/#{object.class.table_name}/#{object.id}/#{attribute_name}"
         | 
| 110 102 | 
             
                          end
         | 
| 111 103 | 
             
                        rescue TypeError, ActiveRecord::StatementInvalid
         | 
| 112 104 | 
             
                          puts "Cannot load the association #{attribute_name} on #{object.class.name} #{object.id}."
         | 
| @@ -0,0 +1,36 @@ | |
| 1 | 
            +
            module ForestLiana
         | 
| 2 | 
            +
              class AllowedUsersGetter
         | 
| 3 | 
            +
                def perform
         | 
| 4 | 
            +
                  uri = URI.parse("#{forest_url}/forest/allowed-users")
         | 
| 5 | 
            +
                  http = Net::HTTP.new(uri.host, uri.port)
         | 
| 6 | 
            +
                  http.use_ssl = true if forest_url.start_with?('https')
         | 
| 7 | 
            +
                  http.start do |client|
         | 
| 8 | 
            +
                    request = Net::HTTP::Get.new(uri.path)
         | 
| 9 | 
            +
                    request['Content-Type'] = 'application/json'
         | 
| 10 | 
            +
                    request['forest-secret-key'] = ForestLiana.secret_key
         | 
| 11 | 
            +
                    response = client.request(request)
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                    if response.is_a?(Net::HTTPOK)
         | 
| 14 | 
            +
                      body = JSON.parse(response.body)['data']
         | 
| 15 | 
            +
                      ForestLiana.allowed_users = body.map do |d|
         | 
| 16 | 
            +
                        user = d['attributes']
         | 
| 17 | 
            +
                        user['id'] = d['id']
         | 
| 18 | 
            +
                        user['outlines'] = d['relationships']['outlines']['data'].map {
         | 
| 19 | 
            +
                          |x| x['id']
         | 
| 20 | 
            +
                        }
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                        user
         | 
| 23 | 
            +
                      end
         | 
| 24 | 
            +
                    else
         | 
| 25 | 
            +
                      []
         | 
| 26 | 
            +
                    end
         | 
| 27 | 
            +
                  end
         | 
| 28 | 
            +
                end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                private
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                def forest_url
         | 
| 33 | 
            +
                  ENV['FOREST_URL'] || 'https://forestadmin-server.herokuapp.com';
         | 
| 34 | 
            +
                end
         | 
| 35 | 
            +
              end
         | 
| 36 | 
            +
            end
         | 
| @@ -1,37 +1,7 @@ | |
| 1 1 | 
             
            class ForestLiana::Collection
         | 
| 2 | 
            -
              mattr_accessor :collection_name
         | 
| 3 | 
            -
              mattr_accessor :is_read_only
         | 
| 4 2 |  | 
| 5 | 
            -
              def self. | 
| 6 | 
            -
                collection = ForestLiana.apimap.find  | 
| 7 | 
            -
             | 
| 8 | 
            -
                end
         | 
| 9 | 
            -
             | 
| 10 | 
            -
                collection.fields << opts.merge({ field: field }) if collection
         | 
| 11 | 
            -
              end
         | 
| 12 | 
            -
             | 
| 13 | 
            -
              def self.fields(fields)
         | 
| 14 | 
            -
                collection = ForestLiana::Model::Collection.new({
         | 
| 15 | 
            -
                  name: self.collection_name,
         | 
| 16 | 
            -
                  is_read_only: self.is_read_only,
         | 
| 17 | 
            -
                  fields: fields
         | 
| 18 | 
            -
                })
         | 
| 19 | 
            -
             | 
| 20 | 
            -
                ForestLiana.apimap << collection
         | 
| 21 | 
            -
              end
         | 
| 22 | 
            -
             | 
| 23 | 
            -
              def self.action(name, opts = {})
         | 
| 24 | 
            -
                collection = ForestLiana.apimap.find do |x|
         | 
| 25 | 
            -
                  x.name == self.collection_name.try(:to_s)
         | 
| 26 | 
            -
                end
         | 
| 27 | 
            -
             | 
| 28 | 
            -
                return if collection.blank?
         | 
| 29 | 
            -
             | 
| 30 | 
            -
                collection.actions << ForestLiana::Model::Action.new({
         | 
| 31 | 
            -
                  name: name,
         | 
| 32 | 
            -
                  http_method: opts[:http_method],
         | 
| 33 | 
            -
                  endpoint: opts[:endpoint],
         | 
| 34 | 
            -
                  fields: opts[:fields]
         | 
| 35 | 
            -
                })
         | 
| 3 | 
            +
              def self.add_actions(collection_name, actions)
         | 
| 4 | 
            +
                collection = ForestLiana.apimap.find {|x| x.name == collection_name}
         | 
| 5 | 
            +
                collection.actions += actions if collection
         | 
| 36 6 | 
             
              end
         | 
| 37 7 | 
             
            end
         | 
| @@ -82,6 +82,14 @@ module ForestLiana | |
| 82 82 | 
             
                      @collection.fields << { field: key, type: 'File' }
         | 
| 83 83 | 
             
                    end
         | 
| 84 84 | 
             
                  end
         | 
| 85 | 
            +
             | 
| 86 | 
            +
                  # CarrierWave attribute
         | 
| 87 | 
            +
                  if @model.respond_to?(:uploaders)
         | 
| 88 | 
            +
                    @model.uploaders.each do |key, value|
         | 
| 89 | 
            +
                      field = @collection.fields.find {|x| x[:field] == key.to_s}
         | 
| 90 | 
            +
                      field[:type] = 'File' if field
         | 
| 91 | 
            +
                    end
         | 
| 92 | 
            +
                  end
         | 
| 85 93 | 
             
                end
         | 
| 86 94 |  | 
| 87 95 | 
             
                def add_associations
         | 
| @@ -0,0 +1,30 @@ | |
| 1 | 
            +
            module ForestLiana
         | 
| 2 | 
            +
              class Base64StringIO < StringIO
         | 
| 3 | 
            +
                class ArgumentError < StandardError; end
         | 
| 4 | 
            +
             | 
| 5 | 
            +
                attr_accessor :file_format
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                def initialize(encoded_file)
         | 
| 8 | 
            +
                  description, encoded_bytes = encoded_file.split(",")
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                  raise ArgumentError unless encoded_bytes
         | 
| 11 | 
            +
                  raise ArgumentError if encoded_bytes.eql?("(null)")
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                  @file_format = get_file_format description
         | 
| 14 | 
            +
                  bytes = ::Base64.decode64 encoded_bytes
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                  super bytes
         | 
| 17 | 
            +
                end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                def original_filename
         | 
| 20 | 
            +
                  File.basename("file.#{@file_format}")
         | 
| 21 | 
            +
                end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                private
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                def get_file_format(description)
         | 
| 26 | 
            +
                  regex = /([a-z0-9]+);base64\z/
         | 
| 27 | 
            +
                  regex.match(description).try(:[], 1)
         | 
| 28 | 
            +
                end
         | 
| 29 | 
            +
              end
         | 
| 30 | 
            +
            end
         | 
| @@ -37,7 +37,11 @@ More info at: https://github.com/ForestAdmin/forest-rails/releases/tag/1.2.0" | |
| 37 37 | 
             
                  # good serializer to use.
         | 
| 38 38 | 
             
                  ::JSONAPI::Serializer.class_eval do
         | 
| 39 39 | 
             
                    def self.find_serializer_class_name(obj)
         | 
| 40 | 
            -
                       | 
| 40 | 
            +
                      if obj.respond_to?(:jsonapi_serializer_class_name)
         | 
| 41 | 
            +
                        obj.jsonapi_serializer_class_name.to_s
         | 
| 42 | 
            +
                      else
         | 
| 43 | 
            +
                        ForestLiana::SerializerFactory.get_serializer_name(obj.class)
         | 
| 44 | 
            +
                      end
         | 
| 41 45 | 
             
                    end
         | 
| 42 46 | 
             
                  end
         | 
| 43 47 | 
             
                end
         | 
| @@ -50,7 +54,7 @@ More info at: https://github.com/ForestAdmin/forest-rails/releases/tag/1.2.0" | |
| 50 54 | 
             
                    end
         | 
| 51 55 | 
             
                  end
         | 
| 52 56 |  | 
| 53 | 
            -
                  Dir["#{@app.root}/app/ | 
| 57 | 
            +
                  Dir["#{@app.root}/app/forest/**/*.rb"].each {|file| require file }
         | 
| 54 58 |  | 
| 55 59 | 
             
                  setup_stripe_integration if stripe_integration?
         | 
| 56 60 | 
             
                  setup_intercom_integration if intercom_integration?
         | 
| @@ -76,17 +80,6 @@ More info at: https://github.com/ForestAdmin/forest-rails/releases/tag/1.2.0" | |
| 76 80 | 
             
                    if response.is_a?(Net::HTTPNotFound)
         | 
| 77 81 | 
             
                      @logger.warn "Forest cannot find your project secret key. " \
         | 
| 78 82 | 
             
                        "Please, run `rails g forest_liana:install`."
         | 
| 79 | 
            -
                    else
         | 
| 80 | 
            -
                      body = JSON.parse(response.body)['data']
         | 
| 81 | 
            -
                      ForestLiana.allowed_users = body.map do |d|
         | 
| 82 | 
            -
                        user = d['attributes']
         | 
| 83 | 
            -
                        user['id'] = d['id']
         | 
| 84 | 
            -
                        user['outlines'] = d['relationships']['outlines']['data'].map {
         | 
| 85 | 
            -
                          |x| x['id']
         | 
| 86 | 
            -
                        }
         | 
| 87 | 
            -
             | 
| 88 | 
            -
                        user
         | 
| 89 | 
            -
                      end
         | 
| 90 83 | 
             
                    end
         | 
| 91 84 | 
             
                  end
         | 
| 92 85 | 
             
                end
         | 
    
        data/lib/forest_liana/version.rb
    CHANGED
    
    
| @@ -74,35 +74,5 @@ module ForestLiana | |
| 74 74 | 
             
                  assert records.first.belongs_to_field.id == 30
         | 
| 75 75 | 
             
                  assert records.last.belongs_to_field.id == 21
         | 
| 76 76 | 
             
                end
         | 
| 77 | 
            -
             | 
| 78 | 
            -
                #test 'Sort by a has_many association' do
         | 
| 79 | 
            -
                  #getter = ResourcesGetter.new(HasManyField, {
         | 
| 80 | 
            -
                    #page: { size: 10, number: 1 },
         | 
| 81 | 
            -
                    #sort: '-belongs_to_fields'
         | 
| 82 | 
            -
                  #})
         | 
| 83 | 
            -
                  #getter.perform
         | 
| 84 | 
            -
                  #records = getter.records
         | 
| 85 | 
            -
                  #count = getter.count
         | 
| 86 | 
            -
             | 
| 87 | 
            -
                  #assert records.count == 10
         | 
| 88 | 
            -
                  #assert count = 30
         | 
| 89 | 
            -
                  #assert records.first.id = 7
         | 
| 90 | 
            -
                #end
         | 
| 91 | 
            -
             | 
| 92 | 
            -
                #test 'Sort by a has_many through association' do
         | 
| 93 | 
            -
                  #getter = ResourcesGetter.new(HasManyThroughField, {
         | 
| 94 | 
            -
                    #page: { size: 10, number: 1 },
         | 
| 95 | 
            -
                    #sort: '-belongs_to_fields'
         | 
| 96 | 
            -
                  #})
         | 
| 97 | 
            -
                  #getter.perform
         | 
| 98 | 
            -
                  #records = getter.records
         | 
| 99 | 
            -
                  #count = getter.count
         | 
| 100 | 
            -
             | 
| 101 | 
            -
                  #assert records.count == 10
         | 
| 102 | 
            -
                  #assert count = 30
         | 
| 103 | 
            -
                  #assert records.first.id = 2
         | 
| 104 | 
            -
                  #assert records.second.id = 3
         | 
| 105 | 
            -
                #end
         | 
| 106 | 
            -
             | 
| 107 77 | 
             
              end
         | 
| 108 78 | 
             
            end
         | 
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: forest_liana
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 1.2. | 
| 4 | 
            +
              version: 1.2.6
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Sandro Munda
         | 
| 8 8 | 
             
            autorequire: 
         | 
| 9 9 | 
             
            bindir: bin
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date: 2016- | 
| 11 | 
            +
            date: 2016-04-04 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: rails
         | 
| @@ -159,6 +159,7 @@ files: | |
| 159 159 | 
             
            - app/serializers/forest_liana/stripe_invoice_serializer.rb
         | 
| 160 160 | 
             
            - app/serializers/forest_liana/stripe_payment_serializer.rb
         | 
| 161 161 | 
             
            - app/services/forest_liana/activity_logger.rb
         | 
| 162 | 
            +
            - app/services/forest_liana/allowed_users_getter.rb
         | 
| 162 163 | 
             
            - app/services/forest_liana/collection.rb
         | 
| 163 164 | 
             
            - app/services/forest_liana/has_many_getter.rb
         | 
| 164 165 | 
             
            - app/services/forest_liana/intercom_attributes_getter.rb
         | 
| @@ -183,6 +184,7 @@ files: | |
| 183 184 | 
             
            - config/initializers/time_formats.rb
         | 
| 184 185 | 
             
            - config/routes.rb
         | 
| 185 186 | 
             
            - lib/forest_liana.rb
         | 
| 187 | 
            +
            - lib/forest_liana/base64_string_io.rb
         | 
| 186 188 | 
             
            - lib/forest_liana/bootstraper.rb
         | 
| 187 189 | 
             
            - lib/forest_liana/engine.rb
         | 
| 188 190 | 
             
            - lib/forest_liana/version.rb
         |