Class: Shamu::Entities::EntityLookupService

Inherits:
Services::Service show all
Defined in:
lib/shamu/entities/entity_lookup_service.rb

Overview

Looks up entities from compiled EntityPath strings allowing references to be stored as opaque values in an external service and later looked up without knowing which services are required to look up the entities.

Useful for implementing a node field in a Relay GraphQL endpoint or resolving entities in an audit log.

#lookup maps entity types to the service class used to look up entities of that type. The services must implement the well known #lookup method that takes an array of ids as an argument.

If there is no entry mapping in the entity_map provided to the constructor then EntityLookupService will attempt to locate a service named Shamu::EntitiesService in the same namespace as the entity type.

The EntityLookupService should be configured as a per-request singleton in Scorpion with all custom entity mappings set.

Scorpion.prepare do
  capture Shamu::Entities::EntityLookupService do |scorpion|
    scorpion.new( Shamu::Entities::EntityLookupService, { "User" => Users::ExternalUsersService }, {} )
  end
end

Direct Known Subclasses

OpaqueEntityLookupService

Instance Method Summary collapse

Methods inherited from Services::Service

#cache_for, #cached_lookup, #entity_list, #entity_lookup_list, #find_by_lookup, #lazy_association, #lookup_association

Constructor Details

#initialize(entity_map = nil) ⇒ EntityLookupService

Returns a new instance of EntityLookupService.



33
34
35
36
37
38
39
40
41
# File 'lib/shamu/entities/entity_lookup_service.rb', line 33

def initialize( entity_map = nil )
  entity_map ||= {}
  @entity_map_cache = Hash.new do |hash, entity_type|
    hash[ entity_type ] = entity_map[ entity_type ] \
                          || entity_map[ entity_type.to_s ] \
                          || find_implicit_service_class( entity_type.to_s )
  end
  super()
end

Instance Method Details

#ids(entities) ⇒ Object

Map the given entities to their Shamu::Entities::EntityPath that can later be used to #lookup the given entity.



53
54
55
56
57
# File 'lib/shamu/entities/entity_lookup_service.rb', line 53

def ids( entities )
  Array.wrap( entities ).map do |entity|
    EntityPath.compose_entity_path( [ entity ] )
  end
end

#lookup(*ids) ⇒ EntityList<Entity>

Look up all the entities from their composed Shamu::Entities::EntityPath.

given ids.

Parameters:

Returns:

  • (EntityList<Entity>)

    the entities in the same order as the



74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/shamu/entities/entity_lookup_service.rb', line 74

def lookup( *ids ) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
  types = {}

  # Decompose entity paths and group by entity type
  ids.each do |composed|
    path = EntityPath.decompose_entity_path( composed )
    fail "Only root entities can be restored" unless path.size == 1
    type, id = path.first

    types[ type ] ||= { paths: [], ids: [] }

    types[ type ][ :paths ] << composed
    types[ type ][ :ids ]   << id
  end

  # Short-circuit if we only have one entity type
  if types.size == 1
    service_class = service_class_for( types.first.first )
    service = scorpion.fetch service_class

    return service.lookup( *types.first.last[ :ids ] )
  end

  # Lookup all entities in batches
  hydrated = types.map do |type, map|
    service_class = service_class_for( type )
    service = scorpion.fetch service_class

    entities = service.lookup( *map[ :ids ] )

    Hash[ map[ :paths ].zip( entities ) ]
  end

  # Map found entities back to their original input order
  mapped = ids.map do |id|
    found = nil
    hydrated.each do |map|
      break if found = map[ id ]
    end

    found
  end

  Entities::List.new mapped
end

#record_ids(ids) ⇒ Object

Map the encoded ids back to their raw record IDs discarding any type information.

Parameters:

  • ids (Array<String>)

    an array of ids encoded with #ids.



63
64
65
66
67
# File 'lib/shamu/entities/entity_lookup_service.rb', line 63

def record_ids( ids )
  Array.wrap( ids ).map do |id|
    EntityPath.decompose_entity_path( id ).first.last.to_model_id
  end
end

#service_class_for(entity_type) ⇒ Shamu::Services::Service

Gets the class of the service used to look up entities of the given type. Use a scorpion to get an instance of the service class.

Returns:



47
48
49
# File 'lib/shamu/entities/entity_lookup_service.rb', line 47

def service_class_for( entity_type )
  entity_map_cache[ entity_type.to_sym ]
end