Class: Archsight::Import::Handler

Inherits:
Object
  • Object
show all
Defined in:
lib/archsight/import/handler.rb

Overview

Base class for import handlers

Subclasses must implement the #execute method to perform the actual import. Use the helper methods to read configuration, validate environment, and write output.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(import_resource, database:, resources_dir:, progress: nil, shared_writer: nil) ⇒ Handler

Returns a new instance of Handler.

Parameters:



22
23
24
25
26
27
28
29
# File 'lib/archsight/import/handler.rb', line 22

def initialize(import_resource, database:, resources_dir:, progress: nil, shared_writer: nil)
  @import_resource = import_resource
  @database = database
  @resources_dir = resources_dir
  @progress = progress || Archsight::Import::Progress.new
  @shared_writer = shared_writer
  @tracked_resources = []
end

Instance Attribute Details

#databaseObject (readonly)

Returns the value of attribute database.



15
16
17
# File 'lib/archsight/import/handler.rb', line 15

def database
  @database
end

#import_resourceObject (readonly)

Returns the value of attribute import_resource.



15
16
17
# File 'lib/archsight/import/handler.rb', line 15

def import_resource
  @import_resource
end

#progressObject (readonly)

Returns the value of attribute progress.



15
16
17
# File 'lib/archsight/import/handler.rb', line 15

def progress
  @progress
end

#resources_dirObject (readonly)

Returns the value of attribute resources_dir.



15
16
17
# File 'lib/archsight/import/handler.rb', line 15

def resources_dir
  @resources_dir
end

#shared_writerObject (readonly)

Returns the value of attribute shared_writer.



15
16
17
# File 'lib/archsight/import/handler.rb', line 15

def shared_writer
  @shared_writer
end

Instance Method Details

#compute_config_hashString

Compute a hash of the import’s configuration for cache invalidation

Returns:

  • (String)

    16-character hex hash



58
59
60
61
62
63
64
# File 'lib/archsight/import/handler.rb', line 58

def compute_config_hash
  config_data = {
    handler: import_resource.annotations["import/handler"],
    config: import_resource.annotations.select { |k, _| k.start_with?("import/config/") }.sort.to_h
  }
  Digest::SHA256.hexdigest(config_data.to_json)[0, 16]
end

#config(key, default: nil) ⇒ String?

Get a configuration value from import/config/* annotations

Parameters:

  • key (String)

    Configuration key (without the import/config/ prefix)

  • default (Object, nil) (defaults to: nil)

    Default value if not set

Returns:

  • (String, nil)

    The configuration value



41
42
43
# File 'lib/archsight/import/handler.rb', line 41

def config(key, default: nil)
  import_resource.annotations["import/config/#{key}"] || default
end

#config_allHash

Get all configuration values as a hash

Returns:

  • (Hash)

    Configuration key-value pairs



47
48
49
50
51
52
53
54
# File 'lib/archsight/import/handler.rb', line 47

def config_all
  import_resource.annotations.each_with_object({}) do |(key, value), hash|
    next unless key.start_with?("import/config/")

    config_key = key.sub("import/config/", "")
    hash[config_key] = value
  end
end

#executeObject

Execute the import. Must be implemented by subclasses.

Raises:

  • (NotImplementedError)

    if not overridden



33
34
35
# File 'lib/archsight/import/handler.rb', line 33

def execute
  raise NotImplementedError, "#{self.class}#execute must be implemented"
end

#import_yaml(name:, handler:, config: {}, annotations: {}) ⇒ Hash

Generate an Import resource YAML hash for child imports NOTE: generated/at is NOT set here - it’s only set by the self_marker when the child import actually executes. This ensures caching works correctly regardless of file loading order.

Dependencies are not specified here - they are derived from the ‘generates` relation on the parent import (tracked via write_generates_meta).

Parameters:

  • name (String)

    Import resource name

  • handler (String)

    Handler name

  • config (Hash) (defaults to: {})

    Configuration annotations (added as import/config/*)

  • annotations (Hash) (defaults to: {})

    Additional annotations (added directly)

Returns:

  • (Hash)

    Import resource hash ready for YAML serialization



163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
# File 'lib/archsight/import/handler.rb', line 163

def import_yaml(name:, handler:, config: {}, annotations: {})
  @tracked_resources << { kind: "Import", name: name }
  all_annotations = {
    "import/handler" => handler,
    "generated/script" => import_resource.name
  }

  # Add direct annotations (e.g., import/outputPath)
  all_annotations.merge!(annotations)

  # Add config annotations with prefix
  config.each do |key, value|
    all_annotations["import/config/#{key}"] = value.to_s
  end

  {
    "apiVersion" => "architecture/v1alpha1",
    "kind" => "Import",
    "metadata" => {
      "name" => name,
      "annotations" => all_annotations
    },
    "spec" => {}
  }
end

#resource_yaml(kind:, name:, annotations: {}, spec: {}) ⇒ Hash

Generate a resource YAML hash with standard metadata

Parameters:

  • kind (String)

    Resource kind (e.g., “TechnologyArtifact”)

  • name (String)

    Resource name

  • annotations (Hash) (defaults to: {})

    Resource annotations

  • spec (Hash) (defaults to: {})

    Resource spec (relations)

Returns:

  • (Hash)

    Resource hash ready for YAML serialization



134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
# File 'lib/archsight/import/handler.rb', line 134

def resource_yaml(kind:, name:, annotations: {}, spec: {})
  @tracked_resources << { kind: kind, name: name }
  {
    "apiVersion" => "architecture/v1alpha1",
    "kind" => kind,
    "metadata" => {
      "name" => name,
      "annotations" => annotations.merge(
        "generated/script" => import_resource.name,
        "generated/at" => Time.now.utc.iso8601
      )
    },
    "spec" => spec
  }
end

#resources_to_yaml(resources) ⇒ String

Convert multiple resource hashes to YAML string with document separators

Parameters:

  • resources (Array<Hash>)

    Array of resource hashes

Returns:

  • (String)

    YAML string with — separators



192
193
194
# File 'lib/archsight/import/handler.rb', line 192

def resources_to_yaml(resources)
  resources.map { |r| YAML.dump(r) }.join
end

#self_markerHash

Generate a marker Import for this handler with generated/at timestamp and config hash Used for caching - call at end of execute() to persist the execution timestamp

Returns:

  • (Hash)

    Import resource hash ready for YAML serialization



69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/archsight/import/handler.rb', line 69

def self_marker
  {
    "apiVersion" => "architecture/v1alpha1",
    "kind" => "Import",
    "metadata" => {
      "name" => import_resource.name,
      "annotations" => {
        "generated/at" => Time.now.utc.iso8601,
        "generated/configHash" => compute_config_hash
      }
    },
    "spec" => {}
  }
end

#write_generates_metaObject

Write generates meta record for this Import Call at end of execute() to persist tracking of generated resources Appends to the output file rather than overwriting



199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
# File 'lib/archsight/import/handler.rb', line 199

def write_generates_meta
  return if @tracked_resources.empty?

  meta = generates_meta_record(@tracked_resources)
  output_path = import_resource.annotations["import/outputPath"]

  full_path = if output_path
                File.join(resources_dir, output_path)
              else
                File.join(resources_dir, "generated", "#{safe_filename(import_resource.name)}.yaml")
              end

  if @shared_writer
    @shared_writer.append_yaml(full_path, YAML.dump(meta), sort_key: "#{import_resource.name}:generates")
  else
    # Append to existing file
    File.open(full_path, "a") { |f| f.write(YAML.dump(meta)) }
  end
end

#write_yaml(content, filename: nil, sort_key: nil) ⇒ String

Write YAML content to the output path Output location is determined by import/outputPath annotation:

- Relative to resources_dir (e.g., "generated/repositories.yaml")
- If filename parameter is provided, it replaces the filename from outputPath
- If no outputPath, falls back to resources_dir/generated with import name as filename

When shared_writer is available, uses thread-safe append for concurrent writes.

Parameters:

  • content (String)

    YAML content to write

  • filename (String, nil) (defaults to: nil)

    Output filename (overrides import/outputPath filename)

  • content (String)

    YAML content to write

  • filename (String, nil) (defaults to: nil)

    Output filename (overrides import/outputPath filename)

  • sort_key (String, nil) (defaults to: nil)

    Key for sorting in shared files (default: import name)

Returns:

  • (String)

    Path to the written file



98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/archsight/import/handler.rb', line 98

def write_yaml(content, filename: nil, sort_key: nil)
  output_path = import_resource.annotations["import/outputPath"]

  full_path = if output_path
                base = File.join(resources_dir, output_path)
                if filename
                  # Replace filename portion with provided filename
                  File.join(File.dirname(base), filename)
                else
                  base
                end
              else
                # Fallback to resources_dir/generated
                File.join(resources_dir, "generated", filename || "#{safe_filename(import_resource.name)}.yaml")
              end

  if @shared_writer
    # Use thread-safe shared writer for concurrent execution
    # Default sort key is import name for stable output ordering
    key = sort_key || import_resource.name
    @shared_writer.append_yaml(full_path, content, sort_key: key)
  else
    # Direct write for non-concurrent mode
    FileUtils.mkdir_p(File.dirname(full_path))
    File.write(full_path, content)
  end

  full_path
end