Class: DecisionAgent::Web::DmnEditor

Inherits:
Object
  • Object
show all
Includes:
Serialization, XmlBuilder
Defined in:
lib/decision_agent/web/dmn_editor.rb,
lib/decision_agent/web/dmn_editor/xml_builder.rb,
lib/decision_agent/web/dmn_editor/serialization.rb

Overview

DMN Editor Backend Provides API endpoints for visual DMN modeling

Defined Under Namespace

Modules: Serialization, XmlBuilder

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from XmlBuilder

#build_decision_table_xml, #generate_dmn_xml

Methods included from Serialization

#serialize_decision, #serialize_decision_table, #serialize_input, #serialize_model, #serialize_output, #serialize_rule

Constructor Details

#initialize(storage: nil) ⇒ DmnEditor

Returns a new instance of DmnEditor.



25
26
27
28
# File 'lib/decision_agent/web/dmn_editor.rb', line 25

def initialize(storage: nil)
  @storage = storage || {}
  @storage_mutex = Mutex.new
end

Instance Attribute Details

#storageObject (readonly)

Returns the value of attribute storage.



23
24
25
# File 'lib/decision_agent/web/dmn_editor.rb', line 23

def storage
  @storage
end

Instance Method Details

#add_decision(model_id:, decision_id:, name:, type: "decision_table") ⇒ Object

Add a decision to a model



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
# File 'lib/decision_agent/web/dmn_editor.rb', line 81

def add_decision(model_id:, decision_id:, name:, type: "decision_table")
  model = retrieve_model(model_id)
  return nil unless model

  decision = Dmn::Decision.new(
    id: decision_id,
    name: name
  )

  # Initialize decision logic based on type
  case type
  when "decision_table"
    decision.instance_variable_set(:@decision_table, Dmn::DecisionTable.new(
                                                       id: "#{decision_id}_table",
                                                       hit_policy: "FIRST"
                                                     ))
  when "decision_tree"
    decision.instance_variable_set(:@decision_tree, Dmn::DecisionTree.new(
                                                      id: "#{decision_id}_tree",
                                                      name: name
                                                    ))
  when "literal"
    decision.instance_variable_set(:@literal_expression, "")
  end

  model.add_decision(decision)
  store_model(model_id, model)

  serialize_decision(decision)
end

#add_input(model_id:, decision_id:, input_id:, label:, type_ref: nil, expression: nil) ⇒ Object

Add input to decision table



139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
# File 'lib/decision_agent/web/dmn_editor.rb', line 139

def add_input(model_id:, decision_id:, input_id:, label:, type_ref: nil, expression: nil)
  model = retrieve_model(model_id)
  return nil unless model

  decision = model.find_decision(decision_id)
  return nil unless decision&.decision_table

  input = Dmn::Input.new(
    id: input_id,
    label: label,
    type_ref: type_ref,
    expression: expression
  )

  decision.decision_table.inputs << input
  store_model(model_id, model)

  serialize_input(input)
end

#add_output(model_id:, decision_id:, output_id:, label:, type_ref: nil, name: nil) ⇒ Object

Add output to decision table



160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
# File 'lib/decision_agent/web/dmn_editor.rb', line 160

def add_output(model_id:, decision_id:, output_id:, label:, type_ref: nil, name: nil)
  model = retrieve_model(model_id)
  return nil unless model

  decision = model.find_decision(decision_id)
  return nil unless decision&.decision_table

  output = Dmn::Output.new(
    id: output_id,
    label: label,
    type_ref: type_ref,
    name: name
  )

  decision.decision_table.outputs << output
  store_model(model_id, model)

  serialize_output(output)
end

#add_rule(model_id:, decision_id:, rule_id:, input_entries:, output_entries:, description: nil) ⇒ Object

Add rule to decision table



181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
# File 'lib/decision_agent/web/dmn_editor.rb', line 181

def add_rule(model_id:, decision_id:, rule_id:, input_entries:, output_entries:, description: nil)
  model = retrieve_model(model_id)
  return nil unless model

  decision = model.find_decision(decision_id)
  return nil unless decision&.decision_table

  rule = Dmn::Rule.new(id: rule_id)
  rule.instance_variable_set(:@input_entries, input_entries)
  rule.instance_variable_set(:@output_entries, output_entries)
  rule.instance_variable_set(:@description, description) if description

  decision.decision_table.rules << rule
  store_model(model_id, model)

  serialize_rule(rule)
end

#create_model(name:, namespace: nil) ⇒ Object

Create a new DMN model



31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/decision_agent/web/dmn_editor.rb', line 31

def create_model(name:, namespace: nil)
  model_id = generate_id
  namespace ||= "http://decisonagent.com/dmn/#{model_id}"

  model = Dmn::Model.new(
    id: model_id,
    name: name,
    namespace: namespace
  )

  store_model(model_id, model)

  {
    id: model_id,
    name: name,
    namespace: namespace,
    decisions: [],
    created_at: Time.now.utc.iso8601
  }
end

#delete_decision(model_id:, decision_id:) ⇒ Object

Delete a decision



129
130
131
132
133
134
135
136
# File 'lib/decision_agent/web/dmn_editor.rb', line 129

def delete_decision(model_id:, decision_id:)
  model = retrieve_model(model_id)
  return false unless model

  model.decisions.reject! { |d| d.id == decision_id }
  store_model(model_id, model)
  true
end

#delete_model(model_id) ⇒ Object

Delete a DMN model



73
74
75
76
77
78
# File 'lib/decision_agent/web/dmn_editor.rb', line 73

def delete_model(model_id)
  @storage_mutex.synchronize do
    @storage.delete(model_id)
  end
  true
end

#delete_rule(model_id:, decision_id:, rule_id:) ⇒ Object

Delete rule



219
220
221
222
223
224
225
226
227
228
229
# File 'lib/decision_agent/web/dmn_editor.rb', line 219

def delete_rule(model_id:, decision_id:, rule_id:)
  model = retrieve_model(model_id)
  return false unless model

  decision = model.find_decision(decision_id)
  return false unless decision&.decision_table

  decision.decision_table.rules.reject! { |r| r.id == rule_id }
  store_model(model_id, model)
  true
end

#export_to_xml(model_id) ⇒ Object

Export DMN model to XML



247
248
249
250
251
252
253
254
# File 'lib/decision_agent/web/dmn_editor.rb', line 247

def export_to_xml(model_id)
  model = retrieve_model(model_id)
  return nil unless model

  # Convert model to DMN XML directly using the model's to_xml method
  # or generate XML from the model's structure
  generate_dmn_xml(model)
end

#get_model(model_id) ⇒ Object

Get a DMN model



53
54
55
56
57
58
# File 'lib/decision_agent/web/dmn_editor.rb', line 53

def get_model(model_id)
  model = retrieve_model(model_id)
  return nil unless model

  serialize_model(model)
end

#import_from_xml(xml_content, name: nil) ⇒ Object

Import DMN model from XML



257
258
259
260
261
262
263
264
265
266
267
268
269
# File 'lib/decision_agent/web/dmn_editor.rb', line 257

def import_from_xml(xml_content, name: nil)
  parser = Dmn::Parser.new
  model = parser.parse(xml_content)

  # Generate new ID for imported model
  model_id = generate_id
  model.instance_variable_set(:@id, model_id)
  model.instance_variable_set(:@name, name) if name

  store_model(model_id, model)

  serialize_model(model)
end

#list_modelsObject

List all models



322
323
324
325
326
327
328
329
330
331
332
333
# File 'lib/decision_agent/web/dmn_editor.rb', line 322

def list_models
  @storage_mutex.synchronize do
    @storage.map do |id, model|
      {
        id: id,
        name: model.name,
        namespace: model.namespace,
        decision_count: model.decisions.size
      }
    end
  end
end

#update_decision(model_id:, decision_id:, name: nil, logic: nil) ⇒ Object

Update a decision



113
114
115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/decision_agent/web/dmn_editor.rb', line 113

def update_decision(model_id:, decision_id:, name: nil, logic: nil)
  model = retrieve_model(model_id)
  return nil unless model

  decision = model.find_decision(decision_id)
  return nil unless decision

  decision.instance_variable_set(:@name, name) if name

  update_decision_table(decision.decision_table, logic) if logic && decision.decision_table

  store_model(model_id, model)
  serialize_decision(decision)
end

#update_model(model_id, name: nil, namespace: nil) ⇒ Object

Update DMN model metadata



61
62
63
64
65
66
67
68
69
70
# File 'lib/decision_agent/web/dmn_editor.rb', line 61

def update_model(model_id, name: nil, namespace: nil)
  model = retrieve_model(model_id)
  return nil unless model

  model.instance_variable_set(:@name, name) if name
  model.instance_variable_set(:@namespace, namespace) if namespace

  store_model(model_id, model)
  serialize_model(model)
end

#update_rule(model_id:, decision_id:, rule_id:, input_entries: nil, output_entries: nil, description: nil) ⇒ Object

Update rule



200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
# File 'lib/decision_agent/web/dmn_editor.rb', line 200

def update_rule(model_id:, decision_id:, rule_id:, input_entries: nil, output_entries: nil, description: nil)
  model = retrieve_model(model_id)
  return nil unless model

  decision = model.find_decision(decision_id)
  return nil unless decision&.decision_table

  rule = decision.decision_table.rules.find { |r| r.id == rule_id }
  return nil unless rule

  rule.instance_variable_set(:@input_entries, input_entries) if input_entries
  rule.instance_variable_set(:@output_entries, output_entries) if output_entries
  rule.instance_variable_set(:@description, description) if description

  store_model(model_id, model)
  serialize_rule(rule)
end

#validate_model(model_id) ⇒ Object

Validate a DMN model



232
233
234
235
236
237
238
239
240
241
242
243
244
# File 'lib/decision_agent/web/dmn_editor.rb', line 232

def validate_model(model_id)
  model = retrieve_model(model_id)
  return { valid: false, errors: ["Model not found"] } unless model

  validator = Dmn::Validator.new
  validator.validate(model)

  {
    valid: validator.valid?,
    errors: validator.errors,
    warnings: validator.warnings
  }
end

#visualize_graph(model_id:, format: "svg") ⇒ Object

Generate visualization for decision graph



290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
# File 'lib/decision_agent/web/dmn_editor.rb', line 290

def visualize_graph(model_id:, format: "svg")
  model = retrieve_model(model_id)
  return nil unless model

  # Convert model to decision graph
  graph = Dmn::DecisionGraph.new(id: model.id, name: model.name)
  model.decisions.each do |decision|
    node = Dmn::DecisionNode.new(
      id: decision.id,
      name: decision.name,
      decision_logic: decision.decision_table || decision.decision_tree
    )

    # Add dependencies from information requirements
    decision.information_requirements.each do |req|
      node.add_dependency(req[:decision_id], req[:variable_name])
    end

    graph.add_decision(node)
  end

  case format.to_s.downcase
  when "svg"
    Dmn::Visualizer.graph_to_svg(graph)
  when "dot"
    Dmn::Visualizer.graph_to_dot(graph)
  when "mermaid"
    Dmn::Visualizer.graph_to_mermaid(graph)
  end
end

#visualize_tree(model_id:, decision_id:, format: "svg") ⇒ Object

Generate visualization for decision tree



272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
# File 'lib/decision_agent/web/dmn_editor.rb', line 272

def visualize_tree(model_id:, decision_id:, format: "svg")
  model = retrieve_model(model_id)
  return nil unless model

  decision = model.find_decision(decision_id)
  return nil unless decision || !decision.decision_tree

  case format.to_s.downcase
  when "svg"
    Dmn::Visualizer.tree_to_svg(decision.decision_tree)
  when "dot"
    Dmn::Visualizer.tree_to_dot(decision.decision_tree)
  when "mermaid"
    Dmn::Visualizer.tree_to_mermaid(decision.decision_tree)
  end
end