Class: ForestAdminAgent::Builder::AgentFactory

Inherits:
Object
  • Object
show all
Includes:
Http::Exceptions, Utils::Schema, ForestAdminDatasourceCustomizer::DSL::DatasourceHelpers, ForestAdminDatasourceToolkit::Exceptions, Singleton
Defined in:
lib/forest_admin_agent/builder/agent_factory.rb,
sig/forest_admin_agent/builder/agent_factory.rbs

Constant Summary collapse

ENV_SECRET_FORMAT =
/\A[0-9a-f]{64}\z/
TTL_CONFIG =

Returns:

  • (Integer)
TTL_SCHEMA =

Returns:

  • (Integer)

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeAgentFactory

Returns a new instance of AgentFactory.



18
19
20
21
# File 'lib/forest_admin_agent/builder/agent_factory.rb', line 18

def initialize
  super
  @reloading = false
end

Instance Attribute Details

#containerObject (readonly)

Returns the value of attribute container.

Returns:

  • (Object)


13
14
15
# File 'lib/forest_admin_agent/builder/agent_factory.rb', line 13

def container
  @container
end

#customizerObject (readonly)

Returns the value of attribute customizer.

Returns:

  • (Object)


13
14
15
# File 'lib/forest_admin_agent/builder/agent_factory.rb', line 13

def customizer
  @customizer
end

#has_env_secretObject (readonly)

Returns the value of attribute has_env_secret.

Returns:

  • (Object)


13
14
15
# File 'lib/forest_admin_agent/builder/agent_factory.rb', line 13

def has_env_secret
  @has_env_secret
end

#schema_only_modeObject

Returns the value of attribute schema_only_mode.



14
15
16
# File 'lib/forest_admin_agent/builder/agent_factory.rb', line 14

def schema_only_mode
  @schema_only_mode
end

Instance Method Details

#add_chart(name, &definition) ⇒ Object



43
44
45
46
47
# File 'lib/forest_admin_agent/builder/agent_factory.rb', line 43

def add_chart(name, &definition)
  @customizer.add_chart(name, &definition)

  self
end

#add_datasource(datasource, options = {}) ⇒ Object



33
34
35
36
37
# File 'lib/forest_admin_agent/builder/agent_factory.rb', line 33

def add_datasource(datasource, options = {})
  @customizer.add_datasource(datasource, options)

  self
end

#buildnil

Returns:

  • (nil)


61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/forest_admin_agent/builder/agent_factory.rb', line 61

def build
  install_audit_trail
  @container.register(:datasource, @customizer.datasource(@logger))

  # Reset route cache to ensure routes are computed with all customizations
  ForestAdminAgent::Http::Router.reset_cached_routes!

  if @schema_only_mode
    generate_schema_only
  else
    send_schema
  end
end

#build_cachenil

Returns:

  • (nil)


290
291
292
293
294
295
296
297
298
299
# File 'lib/forest_admin_agent/builder/agent_factory.rb', line 290

def build_cache
  @options[:customize_error_message] =
    clean_option_value(@options[:customize_error_message], 'config.customize_error_message =')
  @options[:logger] = clean_option_value(@options[:logger], 'config.logger =')
  build_audit_trail_store

  @container.register(:config, @options.to_h)

  configure_rpc_polling_pool if @options[:rpc_max_polling_threads]
end

#build_containerObject

Returns:

  • (Object)


286
287
288
# File 'lib/forest_admin_agent/builder/agent_factory.rb', line 286

def build_container
  @container = Dry::Container.new
end

#build_loggerObject

Returns:

  • (Object)


328
329
330
331
# File 'lib/forest_admin_agent/builder/agent_factory.rb', line 328

def build_logger
  @logger = Services::LoggerService.new(@options[:logger_level], @options[:logger])
  @container.register(:logger, @logger)
end

#build_schema(datasource) ⇒ Hash

Builds the schema hash from the datasource

Parameters:

  • datasource (Object)

    The datasource to generate schema from

Returns:

  • (Hash)

    The schema hash with :meta and :collections keys



176
177
178
179
180
181
182
183
184
# File 'lib/forest_admin_agent/builder/agent_factory.rb', line 176

def build_schema(datasource)
  generated = SchemaEmitter.generate(datasource)
  meta = SchemaEmitter.meta

  {
    meta: meta,
    collections: generated
  }
end

#customize_collection(name, &handle) ⇒ Object



49
50
51
52
53
# File 'lib/forest_admin_agent/builder/agent_factory.rb', line 49

def customize_collection(name, &handle)
  @customizer.customize_collection(name, &handle)

  self
end

#generate_schema_fileHash

Generates or loads the schema and writes it to file (in development mode). This method can be overridden by subclasses that need to customize schema handling.

Returns:

  • (Hash)

    The schema hash with :meta and :collections keys



153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
# File 'lib/forest_admin_agent/builder/agent_factory.rb', line 153

def generate_schema_file
  schema_path = Facades::Container.cache(:schema_path)

  if Facades::Container.cache(:is_production)
    unless schema_path && File.exist?(schema_path)
      raise InternalServerError.new(
        'Schema file not found in production',
        details: { schema_path: schema_path }
      )
    end

    JSON.parse(File.read(schema_path), symbolize_names: true)
  else
    datasource = @container.resolve(:datasource)
    schema = build_schema(datasource)
    write_schema_file(schema_path, schema)
    schema
  end
end

#generate_schema_onlyObject



75
76
77
78
79
80
81
82
83
84
# File 'lib/forest_admin_agent/builder/agent_factory.rb', line 75

def generate_schema_only
  datasource = @container.resolve(:datasource)
  schema = build_schema(datasource)
  schema_path = Facades::Container.cache(:schema_path)

  write_schema_file(schema_path, schema)
  @logger.log('Info', "[ForestAdmin] Schema generated successfully at #{schema_path}")

  schema
end

#reload!Object



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
# File 'lib/forest_admin_agent/builder/agent_factory.rb', line 86

def reload!
  if @reloading
    @logger.log('Info', 'Agent is already reloading. Do nothing.')
    return
  end

  @reloading = true
  @logger.log('Info', 'Agent is reloading...')

  begin
    @customizer.reload!(logger: @logger)
  rescue StandardError => e
    @logger.log('Error', "Error reloading agent: #{e.message}")
    return
  end

  container_replace(:datasource, @customizer.datasource(@logger))

  # Reset route cache before sending schema to ensure routes are recomputed with all customizations
  ForestAdminAgent::Http::Router.reset_cached_routes!
  # The datasource was rebuilt, so the serializer's primary-key cache may be stale
  ForestAdminAgent::Serializer::ForestSerializer.reset_cache!
  @logger.log('Info', 'route cache cleared due to agent reload')

  send_schema
ensure
  @reloading = false
end

#remove_collection(names) ⇒ Object



39
40
41
# File 'lib/forest_admin_agent/builder/agent_factory.rb', line 39

def remove_collection(names)
  @customizer.remove_collection(names)
end

#send_schema(force: false) ⇒ nil

Parameters:

  • force: (false) (defaults to: false)

Returns:

  • (nil)


115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
# File 'lib/forest_admin_agent/builder/agent_factory.rb', line 115

def send_schema(force: false)
  if should_skip_schema_update? && !force
    log_schema_skip
    return
  end

  return unless @has_env_secret
  return unless secrets_format_valid?

  schema = generate_schema_file

  if (append_schema_path = Facades::Container.cache(:append_schema_path))
    begin
      append_schema_file = JSON.parse(File.read(append_schema_path), symbolize_names: true)
      schema[:collections] = schema[:collections] + append_schema_file[:collections]
    rescue StandardError => e
      raise "Can't load additional schema #{append_schema_path}: #{e.message}"
    end
  end

  begin
    post_schema(schema, force)
  rescue StandardError => e
    # A well-formed secret can still be rejected by the server (wrong project, revoked
    # secret, network down, ...). Same rule as an invalid format: block boot in
    # production, but never in dev - warn and move on instead. Scoped to this call
    # only, so a local bug (a broken customization, an unwritable schema_path, ...)
    # still surfaces immediately instead of being logged as a generic warning.
    raise e if Facades::Container.cache(:is_production)

    @logger.log('Warn', "[ForestAdmin] #{e.message}")
    @logger.log('Warn', '[ForestAdmin] Schema sync failed, continuing without it.')
  end
end

#setup(options) ⇒ Object

Parameters:

  • options (Array[string])

Returns:

  • (Object)


23
24
25
26
27
28
29
30
31
# File 'lib/forest_admin_agent/builder/agent_factory.rb', line 23

def setup(options)
  @options = options
  @has_env_secret = !options.to_h[:env_secret].nil?
  @customizer = ForestAdminDatasourceCustomizer::DatasourceCustomizer.new
  build_container
  build_cache
  build_logger
  warn_relation_read_permissions_skipped
end

#use(plugin, options = {}) ⇒ Object



55
56
57
58
59
# File 'lib/forest_admin_agent/builder/agent_factory.rb', line 55

def use(plugin, options = {})
  @customizer.use(plugin, options)

  self
end

#write_schema_file(schema_path, schema) ⇒ Object

Writes the schema to a file

Parameters:

  • schema_path (String)

    Path to write the schema file

  • schema (Hash)

    The schema to write



189
190
191
# File 'lib/forest_admin_agent/builder/agent_factory.rb', line 189

def write_schema_file(schema_path, schema)
  File.write(schema_path, format_schema_json(schema))
end