archsight 0.3.2 → 0.4.0
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/README.md +7 -1
- data/docs/archimate.md +21 -1
- data/docs/configuration.md +1 -0
- data/docs/diagram.md +15 -3
- data/docs/docker.md +4 -1
- data/docs/icons.md +32 -0
- data/docs/import.md +178 -55
- data/docs/index.md.erb +31 -8
- data/docs/modeling.md +772 -24
- data/docs/pages.md +44 -13
- data/docs/search.md +21 -1
- data/docs/togaf.md +5 -1
- data/lib/archsight/analysis/result.rb +8 -2
- data/lib/archsight/analysis/sandbox.rb +8 -0
- data/lib/archsight/analysis/typst_renderer.rb +246 -0
- data/lib/archsight/analysis.rb +1 -0
- data/lib/archsight/annotations/annotation.rb +30 -3
- data/lib/archsight/annotations/architecture_annotations.rb +47 -0
- data/lib/archsight/annotations/asset_annotations.rb +34 -0
- data/lib/archsight/annotations/link_annotations.rb +15 -0
- data/lib/archsight/annotations/resource_reference.rb +12 -0
- data/lib/archsight/annotations/risk_annotations.rb +21 -0
- data/lib/archsight/cli.rb +124 -23
- data/lib/archsight/database.rb +40 -0
- data/lib/archsight/diagram/cli.rb +50 -2
- data/lib/archsight/diagram/formatter.rb +112 -0
- data/lib/archsight/diagram/routing/edge_router/orthogonal_path.rb +38 -10
- data/lib/archsight/diagram/routing/edge_router.rb +29 -4
- data/lib/archsight/diagram/routing/edge_routing.rb +61 -5
- data/lib/archsight/diagram/style/theme.rb +7 -2
- data/lib/archsight/documentation.rb +3 -2
- data/lib/archsight/editor/page_source.rb +13 -1
- data/lib/archsight/export/confluence/analysis_output.rb +101 -0
- data/lib/archsight/export/confluence/client.rb +13 -0
- data/lib/archsight/export/confluence/diagram_links.rb +1 -1
- data/lib/archsight/export/confluence/exporter.rb +40 -13
- data/lib/archsight/export/confluence/labels.rb +25 -0
- data/lib/archsight/export/confluence/page_properties.rb +4 -6
- data/lib/archsight/export/confluence/storage.rb +38 -2
- data/lib/archsight/helpers/embeds.rb +18 -7
- data/lib/archsight/helpers/resource_resolver.rb +25 -0
- data/lib/archsight/import/executor.rb +139 -16
- data/lib/archsight/import/handler.rb +55 -6
- data/lib/archsight/import/handler_loader.rb +64 -0
- data/lib/archsight/import/handlers/cache_pruner.rb +30 -7
- data/lib/archsight/import/handlers/command.rb +120 -0
- data/lib/archsight/import/handlers/github.rb +4 -1
- data/lib/archsight/import/handlers/gitlab.rb +4 -1
- data/lib/archsight/import/handlers/go_dep_resolver.rb +10 -2
- data/lib/archsight/import/handlers/go_grapher.rb +102 -5
- data/lib/archsight/import/handlers/go_module_parser.rb +88 -0
- data/lib/archsight/import/handlers/grapher.rb +40 -1
- data/lib/archsight/import/handlers/repository.rb +3 -0
- data/lib/archsight/import/handlers/rest_api_index.rb +3 -0
- data/lib/archsight/import/registry.rb +22 -1
- data/lib/archsight/import.rb +64 -1
- data/lib/archsight/linter/structure.rb +239 -0
- data/lib/archsight/linter.rb +217 -6
- data/lib/archsight/page_loader.rb +12 -1
- data/lib/archsight/query/evaluator.rb +13 -6
- data/lib/archsight/query/parser.rb +73 -6
- data/lib/archsight/query.rb +1 -1
- data/lib/archsight/references.rb +3 -3
- data/lib/archsight/renderer.rb +80 -23
- data/lib/archsight/resources/analysis.rb +3 -2
- data/lib/archsight/resources/application_component.rb +83 -8
- data/lib/archsight/resources/application_event.rb +79 -0
- data/lib/archsight/resources/application_service.rb +18 -2
- data/lib/archsight/resources/base.rb +58 -5
- data/lib/archsight/resources/business_actor.rb +14 -1
- data/lib/archsight/resources/business_contract.rb +72 -0
- data/lib/archsight/resources/business_control.rb +10 -2
- data/lib/archsight/resources/business_event.rb +79 -0
- data/lib/archsight/resources/business_process.rb +8 -1
- data/lib/archsight/resources/business_product.rb +12 -0
- data/lib/archsight/resources/business_role.rb +70 -0
- data/lib/archsight/resources/compliance_evidence.rb +8 -2
- data/lib/archsight/resources/data_object.rb +6 -1
- data/lib/archsight/resources/implementation_deliverable.rb +63 -0
- data/lib/archsight/resources/implementation_event.rb +52 -0
- data/lib/archsight/resources/implementation_gap.rb +49 -0
- data/lib/archsight/resources/implementation_plateau.rb +67 -0
- data/lib/archsight/resources/implementation_work_package.rb +84 -0
- data/lib/archsight/resources/import.rb +37 -11
- data/lib/archsight/resources/location.rb +56 -0
- data/lib/archsight/resources/motivation_assessment.rb +121 -0
- data/lib/archsight/resources/motivation_constraint.rb +7 -1
- data/lib/archsight/resources/motivation_driver.rb +50 -0
- data/lib/archsight/resources/motivation_goal.rb +13 -1
- data/lib/archsight/resources/motivation_principle.rb +75 -0
- data/lib/archsight/resources/motivation_requirement.rb +18 -2
- data/lib/archsight/resources/motivation_stakeholder.rb +21 -0
- data/lib/archsight/resources/page.rb +9 -7
- data/lib/archsight/resources/strategy_capability.rb +9 -0
- data/lib/archsight/resources/technology_artifact.rb +11 -4
- data/lib/archsight/resources/technology_communication_network.rb +89 -0
- data/lib/archsight/resources/technology_device.rb +88 -0
- data/lib/archsight/resources/technology_equipment.rb +75 -0
- data/lib/archsight/resources/technology_event.rb +80 -0
- data/lib/archsight/resources/technology_facility.rb +70 -0
- data/lib/archsight/resources/technology_node.rb +57 -1
- data/lib/archsight/resources/technology_service.rb +33 -0
- data/lib/archsight/resources/technology_system_software.rb +28 -0
- data/lib/archsight/resources.rb +12 -0
- data/lib/archsight/version.rb +1 -1
- data/lib/archsight/web/api/analysis_helpers.rb +55 -0
- data/lib/archsight/web/api/json_helpers.rb +3 -37
- data/lib/archsight/web/api/openapi/spec.yaml +10 -8
- data/lib/archsight/web/api/page_helpers.rb +1 -8
- data/lib/archsight/web/api/routes.rb +7 -2
- data/lib/archsight/web/application.rb +38 -2
- data/lib/archsight/web/editor/routes.rb +2 -1
- data/lib/archsight/web/public/vue/ApiDocsPage-COp4onwn.js +1 -0
- data/lib/archsight/web/public/vue/DocPage-D52OwaKc.js +1 -0
- data/lib/archsight/web/public/vue/EditorPage-3aqL7yVf.js +35 -0
- data/lib/archsight/web/public/vue/ErrorPage--b9XeSYa.js +2 -0
- data/lib/archsight/web/public/vue/GraphView-qh_EtRrQ.js +1 -0
- data/lib/archsight/web/public/vue/HomePage-C2TNh2U-.js +2 -0
- data/lib/archsight/web/public/vue/InstanceRouter-ZaCcjlIX.css +1 -0
- data/lib/archsight/web/public/vue/InstanceRouter-uzb_OMG0.js +1 -0
- data/lib/archsight/web/public/vue/KindList-CyJuP_uq.js +1 -0
- data/lib/archsight/web/public/vue/KindList-yYcRT2BV.css +1 -0
- data/lib/archsight/web/public/vue/PageView-B93woeN3.css +1 -0
- data/lib/archsight/web/public/vue/PageView-FyygrS-b.js +1 -0
- data/lib/archsight/web/public/vue/QueryError-B4Ex_csZ.js +1 -0
- data/lib/archsight/web/public/vue/ResourceList-BY6Z1Kcb.js +2 -0
- data/lib/archsight/web/public/vue/ResourceList-D75O87zG.css +1 -0
- data/lib/archsight/web/public/vue/{SearchResults-CyPUZZSC.css → SearchResults-BV6UHriJ.css} +1 -1
- data/lib/archsight/web/public/vue/SearchResults-CkFpHF8m.js +1 -0
- data/lib/archsight/web/public/vue/WikiPage-BoHNewCX.js +3 -0
- data/lib/archsight/web/public/vue/WikiPage-TpmM1VAF.css +1 -0
- data/lib/archsight/web/public/vue/architecture-7GRP2DOG-BvzKZE4e.js +1 -0
- data/lib/archsight/web/public/vue/cynefin-OW5HDTMX-D-xs_Hxh.js +1 -0
- data/lib/archsight/web/public/vue/eventmodeling-NTZA5JFV-nbjxG01S.js +1 -0
- data/lib/archsight/web/public/vue/gitGraph-4MIJSDKK-CW6mw2ab.js +1 -0
- data/lib/archsight/web/public/vue/index-BGlYCbi7.js +3 -0
- data/lib/archsight/web/public/vue/index-eUHmJ7K2.css +1 -0
- data/lib/archsight/web/public/vue/info-A6RAGUB7-C12sjlHe.js +1 -0
- data/lib/archsight/web/public/vue/{mermaid-BjEi5URd.js → mermaid-DjsXQ_XN.js} +35 -35
- data/lib/archsight/web/public/vue/packet-AYTQ26CC-wMwKwHmA.js +1 -0
- data/lib/archsight/web/public/vue/pie-WAS4IAKB-w1-sAU_K.js +1 -0
- data/lib/archsight/web/public/vue/radar-RG4KPBEZ-DpwqIFoy.js +1 -0
- data/lib/archsight/web/public/vue/railroad-74A4TZTK-Ce5XC24d.js +1 -0
- data/lib/archsight/web/public/vue/railroad-abnf-HS5TGJTU-BSQO2pds.js +1 -0
- data/lib/archsight/web/public/vue/railroad-ebnf-LZEXJU2U-Dq1c2YtD.js +1 -0
- data/lib/archsight/web/public/vue/railroad-peg-WCYAUIDC-DKj6dli0.js +1 -0
- data/lib/archsight/web/public/vue/rolldown-runtime-hePW80VL.js +1 -0
- data/lib/archsight/web/public/vue/treeView-Q6P3EWNA-BmHNTWxU.js +1 -0
- data/lib/archsight/web/public/vue/treemap-WGGIJYW6-DsEI7sSD.js +1 -0
- data/lib/archsight/web/public/vue/{useGraphviz-DweKV7Kg.js → useGraphviz-4evusK9i.js} +13 -1
- data/lib/archsight/web/public/vue/{useHighlight-D5Gf8DLl.js → useHighlight-DPguaxfm.js} +8 -8
- data/lib/archsight/web/public/vue/usePanZoom-seQE1TPk.js +12 -0
- data/lib/archsight/web/public/vue/wardley-WFR3VGLG-CSCW83-M.js +1 -0
- data/lib/archsight/web/public/vue.html +5 -5
- metadata +72 -38
- data/lib/archsight/web/public/vue/ApiDocsPage-DoOxKjG0.js +0 -1
- data/lib/archsight/web/public/vue/DocPage-CfsC3CeQ.js +0 -1
- data/lib/archsight/web/public/vue/EditorPage-CsJA0q8n.js +0 -35
- data/lib/archsight/web/public/vue/ErrorPage-OJpJz9df.js +0 -2
- data/lib/archsight/web/public/vue/GraphView-CBq6oFRV.js +0 -1
- data/lib/archsight/web/public/vue/HomePage-BwgMLgVC.js +0 -2
- data/lib/archsight/web/public/vue/InstanceRouter-DQz-SsQq.js +0 -1
- data/lib/archsight/web/public/vue/InstanceRouter-jeavM6PW.css +0 -1
- data/lib/archsight/web/public/vue/KindList-4q0K9Cll.js +0 -1
- data/lib/archsight/web/public/vue/PageView-DzeGnvoS.js +0 -1
- data/lib/archsight/web/public/vue/QueryError-kg6Yf-pW.js +0 -1
- data/lib/archsight/web/public/vue/ResourceList-B0FLaFR3.css +0 -1
- data/lib/archsight/web/public/vue/ResourceList-Df_0e0qt.js +0 -2
- data/lib/archsight/web/public/vue/SearchResults-CKgY6_wH.js +0 -1
- data/lib/archsight/web/public/vue/WikiPage-DoFJ0dbW.css +0 -1
- data/lib/archsight/web/public/vue/WikiPage-DrvCHwJ7.js +0 -13
- data/lib/archsight/web/public/vue/architecture-7GRP2DOG-BfA1TPxQ.js +0 -1
- data/lib/archsight/web/public/vue/cynefin-OW5HDTMX-BmgdUKVD.js +0 -1
- data/lib/archsight/web/public/vue/eventmodeling-NTZA5JFV-DbXDvAWF.js +0 -1
- data/lib/archsight/web/public/vue/gitGraph-4MIJSDKK-BkVhrKS1.js +0 -1
- data/lib/archsight/web/public/vue/index-D0Q5GZRs.js +0 -3
- data/lib/archsight/web/public/vue/index-oF-iyZvk.css +0 -1
- data/lib/archsight/web/public/vue/info-A6RAGUB7-BIlCVuUY.js +0 -1
- data/lib/archsight/web/public/vue/packet-AYTQ26CC-eoQTjY3j.js +0 -1
- data/lib/archsight/web/public/vue/pie-WAS4IAKB-DEOotGhh.js +0 -1
- data/lib/archsight/web/public/vue/radar-RG4KPBEZ-C2yucuUN.js +0 -1
- data/lib/archsight/web/public/vue/railroad-74A4TZTK-CzONK5VY.js +0 -1
- data/lib/archsight/web/public/vue/railroad-abnf-HS5TGJTU-9zRPsDsx.js +0 -1
- data/lib/archsight/web/public/vue/railroad-ebnf-LZEXJU2U-BJ4dkz9l.js +0 -1
- data/lib/archsight/web/public/vue/railroad-peg-WCYAUIDC-DFy_rqAj.js +0 -1
- data/lib/archsight/web/public/vue/treeView-Q6P3EWNA-2cq5CyD2.js +0 -1
- data/lib/archsight/web/public/vue/treemap-WGGIJYW6-DK-e9Ez4.js +0 -1
- data/lib/archsight/web/public/vue/wardley-WFR3VGLG-BQwqUqfB.js +0 -1
data/lib/archsight/query.rb
CHANGED
|
@@ -52,7 +52,7 @@ module Archsight
|
|
|
52
52
|
def parse(source)
|
|
53
53
|
lexer = Lexer.new(source)
|
|
54
54
|
tokens = lexer.tokenize
|
|
55
|
-
parser = Parser.new(tokens)
|
|
55
|
+
parser = Parser.new(tokens, source: source)
|
|
56
56
|
parser.parse
|
|
57
57
|
rescue LexerError, ParseError, InvalidRegexError => e
|
|
58
58
|
# Re-raise with source context
|
data/lib/archsight/references.rb
CHANGED
|
@@ -32,7 +32,7 @@ module Archsight
|
|
|
32
32
|
end
|
|
33
33
|
end
|
|
34
34
|
|
|
35
|
-
# The resources a markdown text mentions with `[[...]]` links and `![[
|
|
35
|
+
# The resources a markdown text mentions with `[[...]]` links and `![[Kind/...]]` embeds, outside code.
|
|
36
36
|
# @return [Array<Base>]
|
|
37
37
|
def mentions(markdown, finder)
|
|
38
38
|
visible = markdown.to_s.gsub(FENCE, "").gsub(INLINE_CODE, "")
|
|
@@ -111,10 +111,10 @@ module Archsight
|
|
|
111
111
|
@links.exact_target_for(target)
|
|
112
112
|
end
|
|
113
113
|
|
|
114
|
-
# An `![[Kind/Name]]` embed:
|
|
114
|
+
# An `![[Kind/Name]]` embed: any resource but a page
|
|
115
115
|
def embed(reference)
|
|
116
116
|
kind = reference.split("/", 2).first
|
|
117
|
-
return unless Helpers::Embeds
|
|
117
|
+
return unless Helpers::Embeds.embeddable_kind?(kind)
|
|
118
118
|
|
|
119
119
|
reference(reference)
|
|
120
120
|
end
|
data/lib/archsight/renderer.rb
CHANGED
|
@@ -40,9 +40,11 @@ module Archsight
|
|
|
40
40
|
def gnode(graph, inst, opts = {})
|
|
41
41
|
label = "<TABLE><TR><TD><B>#{inst.name}</B></TD></TR><TR><TD>#{inst.klass}</TD></TR></TABLE>"
|
|
42
42
|
label = inst.name if opts[:simple_label]
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
43
|
+
attrs = { class: css_class(inst), shape: :box, style: "rounded,filled", fontname: FONT, label: label,
|
|
44
|
+
href: "/kinds/#{inst.klass}/instances/#{inst.name}" }
|
|
45
|
+
# A resource at the depth limit with more dependencies: dashed, its own page shows them
|
|
46
|
+
attrs.merge!(style: "rounded,filled,dashed", tooltip: "More dependencies: open it to explore them") if opts[:truncated]
|
|
47
|
+
graph.node gname(inst), attrs
|
|
46
48
|
end
|
|
47
49
|
|
|
48
50
|
def gedge(graph, a_inst, b_inst, label)
|
|
@@ -104,29 +106,84 @@ module Archsight
|
|
|
104
106
|
end
|
|
105
107
|
end
|
|
106
108
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
109
|
+
# The layer an instance graph also follows by default, below the instance's own layer (technology is the bottom
|
|
110
|
+
# of the stack; implementation points at what it changes)
|
|
111
|
+
NEXT_LAYER = { "motivation" => "strategy", "strategy" => "business", "business" => "application",
|
|
112
|
+
"application" => "technology", "implementation" => "application" }.freeze
|
|
113
|
+
|
|
114
|
+
# The layers the graph of a resource of this layer follows when its page opens: its own and the next one below,
|
|
115
|
+
# every layer for `other` (pages, views, locations, ... refer to anything)
|
|
116
|
+
def self.default_graph_layers(layer)
|
|
117
|
+
return Archsight::Resources::LAYERS.dup unless Archsight::Resources::LAYERS.include?(layer) && layer != "other"
|
|
118
|
+
|
|
119
|
+
[layer, NEXT_LAYER[layer]].compact
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# The dependency graph of one instance: the resources its relations lead to, breadth first, so a resource gets
|
|
123
|
+
# the depth of its shortest path.
|
|
124
|
+
# @param max_depth [Integer, nil] follow relations this many steps from the instance (nil: all the way). A
|
|
125
|
+
# resource at the limit that has more relations is drawn dashed.
|
|
126
|
+
# @param layers [Array<String>, nil] follow only relations to resources of these layers (nil: every layer); the
|
|
127
|
+
# instance itself is always shown
|
|
128
|
+
def create_graph_one(db, klass_pat, name_pat, max_depth: nil, layers: nil)
|
|
129
|
+
klass = Archsight::Resources[klass_pat] || raise("kind #{klass_pat} unknown")
|
|
130
|
+
root = db.instances[klass]&.[](name_pat) || raise("name #{name_pat} for kind #{klass_pat} not found")
|
|
131
|
+
|
|
132
|
+
Archsight::Graphvis.new("#{klass_pat}:#{name_pat}").draw_dot do |g|
|
|
133
|
+
nodes = {}
|
|
134
|
+
edges = {}
|
|
135
|
+
queue = [[root, 0]]
|
|
136
|
+
while queue.any?
|
|
137
|
+
inst, depth = queue.shift
|
|
138
|
+
next if nodes[inst]
|
|
139
|
+
|
|
140
|
+
nodes[inst] = true
|
|
141
|
+
targets = graph_targets(inst, layers)
|
|
142
|
+
if max_depth && depth >= max_depth
|
|
143
|
+
gnode(g, inst, truncated: targets.any?)
|
|
144
|
+
next
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
gnode(g, inst)
|
|
148
|
+
targets.each do |verb, rel|
|
|
149
|
+
edge_name = "#{gname(inst)}|#{verb}|#{gname(rel)}"
|
|
150
|
+
gedge(g, inst, rel, verb) unless edges[edge_name]
|
|
151
|
+
edges[edge_name] = true
|
|
152
|
+
queue << [rel, depth + 1] unless nodes[rel]
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
# The relations of a kind to its own kind (a team's sub-teams, a service's dependencies on other services) as
|
|
159
|
+
# [verb, key] pairs
|
|
160
|
+
def self.self_relations(klass)
|
|
161
|
+
kind = klass.name.to_s.split("::").last
|
|
162
|
+
klass.declared_relations.filter_map { |verb, key, target| [verb, key] if target.to_s == kind }
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# The graph of a kind: its resources that are related to each other by its relations to its own kind (an org
|
|
166
|
+
# chart for BusinessActor). Resources without such a relation are left out, the kind's list shows them.
|
|
167
|
+
def create_graph_kind(db, klass_pat)
|
|
168
|
+
klass = Archsight::Resources[klass_pat] || raise("kind #{klass_pat} unknown")
|
|
169
|
+
relations = Archsight::GraphvisRenderer.self_relations(klass)
|
|
170
|
+
sources = (db.instances[klass] || {}).values.reject(&:abandoned?)
|
|
171
|
+
edges = sources.flat_map do |inst|
|
|
172
|
+
relations.flat_map { |verb, key| inst.relations(verb, key).reject(&:abandoned?).map { |rel| [inst, verb, rel] } }
|
|
173
|
+
end
|
|
174
|
+
edges.uniq! { |inst, verb, rel| "#{gname(inst)}|#{verb}|#{gname(rel)}" }
|
|
175
|
+
|
|
176
|
+
Archsight::Graphvis.new(klass_pat).draw_dot do |g|
|
|
177
|
+
edges.flat_map { |inst, _, rel| [inst, rel] }.uniq.each { |inst| gnode(g, inst, simple_label: true) }
|
|
178
|
+
edges.each { |inst, verb, rel| gedge(g, inst, rel, verb) }
|
|
116
179
|
end
|
|
117
180
|
end
|
|
118
181
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
inst.class.relations.each do |verb, kind|
|
|
125
|
-
inst.relations(verb, kind).each do |rel|
|
|
126
|
-
edge_name = "#{inst}|#{verb}|#{rel}"
|
|
127
|
-
gedge(graph, inst, rel, verb) unless edges[edge_name]
|
|
128
|
-
edges[edge_name] = true
|
|
129
|
-
create_graph_one_inst(db, graph, rel, nodes, edges)
|
|
182
|
+
# The [verb, target] pairs of an instance's relations the graph follows: those to resources of the layers
|
|
183
|
+
def graph_targets(inst, layers)
|
|
184
|
+
inst.class.relations.flat_map do |verb, kind|
|
|
185
|
+
inst.relations(verb, kind).filter_map do |rel|
|
|
186
|
+
[verb, rel] if layers.nil? || layers.include?(rel.class.layer)
|
|
130
187
|
end
|
|
131
188
|
end
|
|
132
189
|
end
|
|
@@ -57,8 +57,9 @@ class Archsight::Resources::Analysis < Archsight::Resources::Base
|
|
|
57
57
|
|
|
58
58
|
# Description
|
|
59
59
|
annotation "analysis/description",
|
|
60
|
-
description: "Human-readable description of what this analysis validates",
|
|
61
|
-
title: "Description"
|
|
60
|
+
description: "Human-readable description of what this analysis validates (markdown)",
|
|
61
|
+
title: "Description",
|
|
62
|
+
format: :markdown
|
|
62
63
|
|
|
63
64
|
# Execution configuration
|
|
64
65
|
annotation "analysis/timeout",
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
# ApplicationComponent a part of the ApplicationService
|
|
4
4
|
class Archsight::Resources::ApplicationComponent < Archsight::Resources::Base
|
|
5
|
-
include_annotations :git, :architecture, :generated, :backup
|
|
5
|
+
include_annotations :git, :architecture, :generated, :backup, :asset, :risk
|
|
6
6
|
|
|
7
7
|
description <<~MD
|
|
8
8
|
Represents a logical part of an application service that can be deployed independently.
|
|
@@ -24,6 +24,53 @@ class Archsight::Resources::ApplicationComponent < Archsight::Resources::Base
|
|
|
24
24
|
- Backend components
|
|
25
25
|
- Frontend applications
|
|
26
26
|
- Background workers
|
|
27
|
+
|
|
28
|
+
## Choosing this kind
|
|
29
|
+
|
|
30
|
+
**Question:** What is built, deployed and versioned?
|
|
31
|
+
**Test:** It has artifacts and a `component/type`, and can be deployed or linked.
|
|
32
|
+
**Name:** The part, by runtime role (API, Operator, Worker) as in "Payments:API", not by repository category
|
|
33
|
+
("Testing", "Tools", "Infrastructure"). Test, tooling and infrastructure-as-code repositories belong to the
|
|
34
|
+
component they build, test or deploy.
|
|
35
|
+
|
|
36
|
+
A component belongs to exactly one service. See the modeling guide, "Choosing the Kind".
|
|
37
|
+
|
|
38
|
+
## Component types
|
|
39
|
+
|
|
40
|
+
Set `component/type` to say what the component is built as; importers set it from the manifests:
|
|
41
|
+
|
|
42
|
+
- `executable`: has an entry point and is run or deployed (services, workers, operators, command-line tools)
|
|
43
|
+
- `library`: imported by other components and not run on its own (shared libraries, SDKs)
|
|
44
|
+
- `module`: a unit of versioning or dependency that bundles executables and importable libraries (a Go module
|
|
45
|
+
with `cmd/` and shared packages, a Maven parent, an npm workspace root)
|
|
46
|
+
- `plugin`: loaded by a host program (kubectl, Terraform, Grafana)
|
|
47
|
+
- `frontend`: runs in the browser
|
|
48
|
+
- `other`
|
|
49
|
+
|
|
50
|
+
`component/role` refines an executable or plugin (`service`, `cli`, `job`, `operator`, `agent`); importers cannot
|
|
51
|
+
tell these apart, so it is set by hand and never overwritten. Importers also add machine tags
|
|
52
|
+
(`namespace:value`) to the regular `architecture/tags`: `ecosystem:go`, `packaging:go-module`,
|
|
53
|
+
`linkage:shared`; the tags a person set stay, and a re-import only replaces the machine tags of these
|
|
54
|
+
namespaces. The entry points (main packages such as `cmd/api`) go to `component/entrypoints`, a list that
|
|
55
|
+
works in queries like a tag but is not a sidebar filter, as there are too many distinct values. Deployable forms (container image, chart, package) are `TechnologyArtifact`s, not
|
|
56
|
+
component types.
|
|
57
|
+
|
|
58
|
+
## Where it runs
|
|
59
|
+
|
|
60
|
+
A component is `servedBy` the `TechnologyService` it is deployed on (a product's cluster, a managed platform) and
|
|
61
|
+
`servedBy` the `TechnologySystemSoftware` it runs on (the runtime, such as a Kubernetes distribution). Deployment
|
|
62
|
+
importers write the service per component, so a sidecar or agent deployed into many clusters shows where it runs
|
|
63
|
+
without making its own service look like it runs in all of them.
|
|
64
|
+
|
|
65
|
+
The computed `component/dependents` counts the components that depend on this one, which tells shared libraries
|
|
66
|
+
and modules from components nobody consumes.
|
|
67
|
+
|
|
68
|
+
## Security and risk modelling
|
|
69
|
+
|
|
70
|
+
- **Asset at risk:** set `asset/value` and the protection needs; `assesses` from a vulnerability or risk.
|
|
71
|
+
- **Control implementation:** a component that implements a control measure `realizes` the requirement and is
|
|
72
|
+
`evidencedBy` evidence.
|
|
73
|
+
- **Threat agent:** a component can be the `causedBy` of an event (a compromised service).
|
|
27
74
|
MD
|
|
28
75
|
|
|
29
76
|
icon "component"
|
|
@@ -39,6 +86,18 @@ class Archsight::Resources::ApplicationComponent < Archsight::Resources::Base
|
|
|
39
86
|
title: "Architecture Size",
|
|
40
87
|
enum: %w[microservice monolith]
|
|
41
88
|
|
|
89
|
+
# Classification
|
|
90
|
+
annotation "component/type",
|
|
91
|
+
description: "What the component is built as (executable, library, module, ...)",
|
|
92
|
+
title: "Component Type",
|
|
93
|
+
enum: %w[executable library module plugin frontend other],
|
|
94
|
+
filter: :word
|
|
95
|
+
annotation "component/role",
|
|
96
|
+
description: "What an executable or plugin does; set by hand",
|
|
97
|
+
title: "Component Role",
|
|
98
|
+
enum: %w[service cli job operator agent],
|
|
99
|
+
filter: :word
|
|
100
|
+
|
|
42
101
|
# Availability
|
|
43
102
|
annotation "availability/quality",
|
|
44
103
|
description: "Service availability quality target per year",
|
|
@@ -122,6 +181,12 @@ class Archsight::Resources::ApplicationComponent < Archsight::Resources::Base
|
|
|
122
181
|
enum: ["no support", "support by dev", "support by ops", "support by api",
|
|
123
182
|
"self service support via a frontend"]
|
|
124
183
|
|
|
184
|
+
annotation "component/entrypoints",
|
|
185
|
+
description: "Comma-separated entry points (main packages or commands such as cmd/api), set by importers",
|
|
186
|
+
title: "Entry points",
|
|
187
|
+
sidebar: false,
|
|
188
|
+
filter: :list
|
|
189
|
+
|
|
125
190
|
# Deployment (from ArgoCD/cluster)
|
|
126
191
|
annotation "deployment/images",
|
|
127
192
|
description: "Deployed container images",
|
|
@@ -129,20 +194,21 @@ class Archsight::Resources::ApplicationComponent < Archsight::Resources::Base
|
|
|
129
194
|
sidebar: false,
|
|
130
195
|
filter: :list
|
|
131
196
|
annotation "deployment/chart",
|
|
132
|
-
description: "Deployed Helm
|
|
197
|
+
description: "Deployed Helm charts, comma separated",
|
|
133
198
|
title: "Helm Chart",
|
|
134
199
|
sidebar: false,
|
|
135
|
-
filter: :
|
|
200
|
+
filter: :list
|
|
136
201
|
annotation "deployment/namespace",
|
|
137
|
-
description: "Kubernetes
|
|
202
|
+
description: "Kubernetes namespaces, comma separated",
|
|
138
203
|
title: "Namespace",
|
|
139
204
|
sidebar: false,
|
|
140
|
-
filter: :
|
|
205
|
+
filter: :list
|
|
141
206
|
annotation "deployment/cluster",
|
|
142
|
-
description: "Target
|
|
207
|
+
description: "Target clusters, comma separated",
|
|
143
208
|
title: "Cluster",
|
|
144
|
-
filter: :
|
|
145
|
-
summary: true
|
|
209
|
+
filter: :list,
|
|
210
|
+
summary: true,
|
|
211
|
+
deprecated: "list the clusters as spec.servedBy.technologyServices"
|
|
146
212
|
|
|
147
213
|
# Computed Annotations
|
|
148
214
|
computed_annotation "repository/artifacts/total",
|
|
@@ -190,6 +256,14 @@ class Archsight::Resources::ApplicationComponent < Archsight::Resources::Base
|
|
|
190
256
|
end
|
|
191
257
|
end
|
|
192
258
|
|
|
259
|
+
computed_annotation "component/dependents",
|
|
260
|
+
title: "Dependents",
|
|
261
|
+
description: "Number of components that depend directly on this one",
|
|
262
|
+
type: Integer,
|
|
263
|
+
sidebar: true do
|
|
264
|
+
count(incoming(:ApplicationComponent))
|
|
265
|
+
end
|
|
266
|
+
|
|
193
267
|
computed_annotation "scc/language",
|
|
194
268
|
title: "Primary Language",
|
|
195
269
|
summary: true,
|
|
@@ -343,6 +417,7 @@ class Archsight::Resources::ApplicationComponent < Archsight::Resources::Base
|
|
|
343
417
|
relation :realizedThrough, :technologyArtifacts, :TechnologyArtifact
|
|
344
418
|
relation :realizedBy, :technologyComponents, :TechnologyNode
|
|
345
419
|
relation :servedBy, :technologyComponents, :TechnologySystemSoftware
|
|
420
|
+
relation :servedBy, :technologyServices, :TechnologyService
|
|
346
421
|
relation :exposes, :applicationInterfaces, :ApplicationInterface
|
|
347
422
|
relation :dependsOn, :applicationInterfaces, :ApplicationInterface
|
|
348
423
|
relation :dependsOn, :applicationComponents, :ApplicationComponent
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# ApplicationEvent represents an application event: something that happens in an application and triggers or interrupts application behavior
|
|
4
|
+
class Archsight::Resources::ApplicationEvent < Archsight::Resources::Base
|
|
5
|
+
include_annotations :git, :architecture, :risk
|
|
6
|
+
|
|
7
|
+
description <<~MD
|
|
8
|
+
Represents an application event: something that happens in an application and triggers or interrupts application behavior.
|
|
9
|
+
|
|
10
|
+
## ArchiMate Definition
|
|
11
|
+
|
|
12
|
+
**Layer:** Application
|
|
13
|
+
**Aspect:** Behavior
|
|
14
|
+
|
|
15
|
+
An event is something that happens and influences behavior. It does not last: it triggers or interrupts
|
|
16
|
+
processes and services. In the risk and security overlay of the Open Group paper (*Modeling Enterprise Risk
|
|
17
|
+
Management and Security with the ArchiMate Language*) a threat event and a loss event are events that carry a
|
|
18
|
+
type; set `event/type` to say which.
|
|
19
|
+
|
|
20
|
+
## Usage
|
|
21
|
+
|
|
22
|
+
Use ApplicationEvent for events on the application level:
|
|
23
|
+
|
|
24
|
+
- Threat events and loss events of applications (an exploit of a service, a burst of failed logins, a data leak)
|
|
25
|
+
- Security alerts raised by an application
|
|
26
|
+
- Triggers of application services (a message arrives, a job is scheduled)
|
|
27
|
+
|
|
28
|
+
## Event types
|
|
29
|
+
|
|
30
|
+
- `threat-event`: an event with the potential to harm an asset; it can trigger a loss event
|
|
31
|
+
- `attack`: a threat event caused by intentional malicious activity
|
|
32
|
+
- `loss-event`: an event that harms an asset (a hazard materialises, a vulnerability is exploited)
|
|
33
|
+
- `incident`: a loss event that has happened
|
|
34
|
+
- `opportunity-event`: an event that can add value
|
|
35
|
+
- `audit`, `scan`, `change`: events that produce assessments or alter the architecture
|
|
36
|
+
- `other`
|
|
37
|
+
|
|
38
|
+
Filter, group and query by `event/type`, `risk/domain` and `risk/category` to see, for example, every loss
|
|
39
|
+
event of a risk domain.
|
|
40
|
+
|
|
41
|
+
## How it connects
|
|
42
|
+
|
|
43
|
+
- A threat event `triggers` a loss event or a process or service; threat and loss events may sit on different
|
|
44
|
+
layers (a technology event triggers an application or business event)
|
|
45
|
+
- `causedBy` the threat agent (an actor, component or node)
|
|
46
|
+
- `affects` the assets it harms
|
|
47
|
+
- A `MotivationDriver` (the threat) `triggers` it; a vulnerability `MotivationAssessment` `influences` it
|
|
48
|
+
MD
|
|
49
|
+
|
|
50
|
+
icon "bell-notification"
|
|
51
|
+
layer "application"
|
|
52
|
+
|
|
53
|
+
annotation "event/type",
|
|
54
|
+
description: "What kind of event this is (specialization of the event, see the risk and security overlay)",
|
|
55
|
+
enum: %w[threat-event attack loss-event incident opportunity-event audit scan change other],
|
|
56
|
+
filter: :word,
|
|
57
|
+
summary: true
|
|
58
|
+
|
|
59
|
+
annotation "event/severity",
|
|
60
|
+
description: "Severity of the event",
|
|
61
|
+
enum: %w[info low medium high critical],
|
|
62
|
+
filter: :word,
|
|
63
|
+
summary: true
|
|
64
|
+
|
|
65
|
+
annotation "event/occurred",
|
|
66
|
+
description: "When the event happened or is expected (ISO 8601 date or time)",
|
|
67
|
+
title: "Occurred",
|
|
68
|
+
validator: ->(value) { Archsight::Resources::Page.timestamp_error(value) }
|
|
69
|
+
|
|
70
|
+
relation :triggers, :applicationServices, :ApplicationService
|
|
71
|
+
relation :triggers, :applicationEvents, :ApplicationEvent
|
|
72
|
+
relation :triggers, :businessEvents, :BusinessEvent
|
|
73
|
+
relation :causedBy, :businessActors, :BusinessActor
|
|
74
|
+
relation :causedBy, :applicationComponents, :ApplicationComponent
|
|
75
|
+
relation :causedBy, :technologyNodes, :TechnologyNode
|
|
76
|
+
relation :affects, :applicationServices, :ApplicationService
|
|
77
|
+
relation :affects, :applicationComponents, :ApplicationComponent
|
|
78
|
+
relation :affects, :dataObjects, :DataObject
|
|
79
|
+
end
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
# ApplicationService represents the high level application service that implements capabilities
|
|
4
4
|
class Archsight::Resources::ApplicationService < Archsight::Resources::Base
|
|
5
|
-
include_annotations :git, :architecture, :generated, :backup
|
|
5
|
+
include_annotations :git, :architecture, :generated, :backup, :asset, :risk
|
|
6
6
|
|
|
7
7
|
description <<~MD
|
|
8
8
|
Represents a high-level application service that implements business capabilities.
|
|
@@ -20,10 +20,25 @@ class Archsight::Resources::ApplicationService < Archsight::Resources::Base
|
|
|
20
20
|
|
|
21
21
|
Use ApplicationService to represent:
|
|
22
22
|
|
|
23
|
-
-
|
|
23
|
+
- Behaviour software offers to a user or another system ("Payment Authorization", "Document Search")
|
|
24
24
|
- Logical groupings of application components
|
|
25
25
|
- Services exposed to business processes
|
|
26
26
|
- APIs and their implementations as a cohesive unit
|
|
27
|
+
|
|
28
|
+
## Choosing this kind
|
|
29
|
+
|
|
30
|
+
**Question:** What behaviour does software offer a consumer?
|
|
31
|
+
**Test:** It can be described without naming repositories, deployments or runtime objects, and has one owning actor.
|
|
32
|
+
**Name:** The function, not the technology or a deployment object ("Payment Authorization").
|
|
33
|
+
|
|
34
|
+
A component belongs to exactly one service. One service using another is `dependsOn` between the two services, not
|
|
35
|
+
the other service's components listed again; a service that many others use is modeled once. A service whose only component has the same name is a smell. See
|
|
36
|
+
the modeling guide, "Choosing the Kind".
|
|
37
|
+
|
|
38
|
+
## Security and risk modelling
|
|
39
|
+
|
|
40
|
+
A service is an asset at risk (`asset/*` profile, protection needs) that loss events `affects`, a trigger target
|
|
41
|
+
for `ApplicationEvent`s, and the place where requirements are realized and evidenced.
|
|
27
42
|
MD
|
|
28
43
|
|
|
29
44
|
icon "cube"
|
|
@@ -212,6 +227,7 @@ class Archsight::Resources::ApplicationService < Archsight::Resources::Base
|
|
|
212
227
|
end
|
|
213
228
|
|
|
214
229
|
relation :realizedThrough, :applicationComponents, :ApplicationComponent
|
|
230
|
+
relation :dependsOn, :applicationServices, :ApplicationService
|
|
215
231
|
relation :servedBy, :businessActors, :BusinessActor
|
|
216
232
|
relation :servedBy, :technologyServices, :TechnologyService
|
|
217
233
|
relation :realizes, :motivationConstraints, :MotivationConstraint
|
|
@@ -35,12 +35,20 @@ module Archsight
|
|
|
35
35
|
# A kind may mark at most this many annotations as summary (see Base.annotation)
|
|
36
36
|
MAX_SUMMARY_ANNOTATIONS = 3
|
|
37
37
|
|
|
38
|
+
# The options `annotation` accepts (see Archsight::Annotations::Annotation)
|
|
39
|
+
ANNOTATION_OPTIONS = %i[description filter title format enum sidebar type summary editor validator unique
|
|
40
|
+
deprecated default].freeze
|
|
41
|
+
|
|
38
42
|
# Define an annotation using the Annotation class
|
|
39
43
|
# `summary: true` marks it as a summary attribute: it is returned with every search hit of this kind.
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
+
# `unique: true` makes it an identifier: its values (each element of a list) are unique among the resources
|
|
45
|
+
# of the kind, which `archsight lint` checks, and its values are shown as chips without links (not tags).
|
|
46
|
+
# `deprecated: "what to use instead"` keeps it working while `archsight lint` lists every use.
|
|
47
|
+
# `default: "value"` is the value of a resource that does not set it (see #apply_annotation_defaults!).
|
|
48
|
+
def self.annotation(key, **options)
|
|
49
|
+
unknown = options.keys - ANNOTATION_OPTIONS
|
|
50
|
+
raise ArgumentError, "unknown annotation option(s) #{unknown.join(", ")} for #{key}" if unknown.any?
|
|
51
|
+
|
|
44
52
|
register_annotation(Archsight::Annotations::Annotation.new(key, options))
|
|
45
53
|
end
|
|
46
54
|
|
|
@@ -143,8 +151,11 @@ module Archsight
|
|
|
143
151
|
end
|
|
144
152
|
end
|
|
145
153
|
|
|
154
|
+
# The layer of the kind, one of Archsight::Resources::LAYERS (default `other`)
|
|
146
155
|
def self.layer(layer_name = nil)
|
|
147
156
|
if layer_name
|
|
157
|
+
raise ArgumentError, "unknown layer #{layer_name.inspect} for #{name}" unless Archsight::Resources::LAYERS.include?(layer_name)
|
|
158
|
+
|
|
148
159
|
@layer = layer_name
|
|
149
160
|
else
|
|
150
161
|
@layer || "other" # default layer
|
|
@@ -223,6 +234,26 @@ module Archsight
|
|
|
223
234
|
@raw["metadata"]["annotations"][key] = value
|
|
224
235
|
end
|
|
225
236
|
|
|
237
|
+
# Gives every annotation with a `default:` that the file does not set its default, in the annotations hash like
|
|
238
|
+
# a written value, so queries, filters and the page see it. The editor leaves these out (#defaulted?), so a
|
|
239
|
+
# save does not write them into the file.
|
|
240
|
+
def apply_annotation_defaults!
|
|
241
|
+
self.class.annotations.each do |annotation|
|
|
242
|
+
next if annotation.default.nil? || annotation.pattern? || annotations.key?(annotation.key)
|
|
243
|
+
|
|
244
|
+
defaulted = (@defaulted ||= []) #: Array[String]
|
|
245
|
+
defaulted << annotation.key
|
|
246
|
+
@raw["metadata"] ||= {}
|
|
247
|
+
@raw["metadata"]["annotations"] = @raw["metadata"]["annotations"] || {} #: Hash[String, untyped]
|
|
248
|
+
@raw["metadata"]["annotations"][annotation.key] = annotation.default
|
|
249
|
+
end
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
# Whether the value of the annotation is its default, not written in the file
|
|
253
|
+
def defaulted?(key)
|
|
254
|
+
@defaulted&.include?(key) || false
|
|
255
|
+
end
|
|
256
|
+
|
|
226
257
|
# Get a computed annotation value from the cache
|
|
227
258
|
# @param key [String] The annotation key
|
|
228
259
|
# @return [Object, nil] The computed value or nil
|
|
@@ -338,7 +369,29 @@ module Archsight
|
|
|
338
369
|
|
|
339
370
|
def merge!(inst)
|
|
340
371
|
# NOTE: path reference is preserved from the original instance
|
|
341
|
-
|
|
372
|
+
merged = Archsight::Helpers.deep_merge(@raw, inst.raw)
|
|
373
|
+
union_list_annotations!(merged, inst.raw)
|
|
374
|
+
@raw = merged
|
|
375
|
+
end
|
|
376
|
+
|
|
377
|
+
# Annotations hold strings, which replace each other on merge. The values of a list annotation (tags, aliases)
|
|
378
|
+
# are joined instead, so two documents of one resource can each add to it.
|
|
379
|
+
def union_list_annotations!(merged, other_raw)
|
|
380
|
+
mine = @raw.dig("metadata", "annotations")
|
|
381
|
+
theirs = other_raw.dig("metadata", "annotations")
|
|
382
|
+
return unless mine.is_a?(Hash) && theirs.is_a?(Hash)
|
|
383
|
+
|
|
384
|
+
theirs.each do |key, value|
|
|
385
|
+
next unless mine[key].is_a?(String) && value.is_a?(String)
|
|
386
|
+
next unless self.class.annotation_matching(key)&.list?
|
|
387
|
+
|
|
388
|
+
values = (split_list(mine[key]) + split_list(value)).uniq
|
|
389
|
+
merged["metadata"]["annotations"][key] = values.join(",")
|
|
390
|
+
end
|
|
391
|
+
end
|
|
392
|
+
|
|
393
|
+
def split_list(text)
|
|
394
|
+
text.split(/,|\n/).map(&:strip).reject(&:empty?)
|
|
342
395
|
end
|
|
343
396
|
|
|
344
397
|
# raise provides a helper for better error messages including current path and line no
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
# BusinessActor represents teams or organizational units
|
|
4
4
|
class Archsight::Resources::BusinessActor < Archsight::Resources::Base
|
|
5
|
-
include_annotations :git, :architecture, :generated
|
|
5
|
+
include_annotations :git, :architecture, :generated, :asset, :risk
|
|
6
6
|
|
|
7
7
|
description <<~MD
|
|
8
8
|
Represents a team, organizational unit, or external entity that performs business behavior.
|
|
@@ -25,6 +25,19 @@ class Archsight::Resources::BusinessActor < Archsight::Resources::Base
|
|
|
25
25
|
- External vendors or partners
|
|
26
26
|
- Support organizations
|
|
27
27
|
- Cross-functional groups
|
|
28
|
+
- Customers and providers: the parties a `BusinessContract` is `agreedBy`
|
|
29
|
+
- The party behind a `MotivationStakeholder`: the stakeholder is `representedBy` it
|
|
30
|
+
- The team behind infrastructure: a `TechnologyDevice`, `TechnologyEquipment`, `TechnologyNode` or
|
|
31
|
+
`TechnologyCommunicationNetwork` is `ownedBy` the team that operates it, and a device, equipment or node is
|
|
32
|
+
`servedBy` the team it is dedicated to (a test server, a cluster partition)
|
|
33
|
+
|
|
34
|
+
## Security and risk modelling
|
|
35
|
+
|
|
36
|
+
- **Threat agent:** an actor (also an external one, such as an attacker group or a supplier) can be the
|
|
37
|
+
`causedBy` of a threat or loss event.
|
|
38
|
+
- **Owner and executor:** actors own risks (`MotivationAssessment` `ownedBy`), policies and controls, directly or through a `BusinessRole` they `performedBy`.
|
|
39
|
+
- **Asset:** a supplier or a team can be an asset at risk; set the `asset/*` profile and `assesses` it from a
|
|
40
|
+
supplier assessment.
|
|
28
41
|
MD
|
|
29
42
|
|
|
30
43
|
icon "community"
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# BusinessContract represents an agreement between a provider and a consumer
|
|
4
|
+
class Archsight::Resources::BusinessContract < Archsight::Resources::Base
|
|
5
|
+
include_annotations :git, :architecture, :risk
|
|
6
|
+
|
|
7
|
+
description <<~MD
|
|
8
|
+
Represents an agreement between a provider and a consumer: the rights and obligations both agreed to.
|
|
9
|
+
|
|
10
|
+
## ArchiMate Definition
|
|
11
|
+
|
|
12
|
+
**Layer:** Business
|
|
13
|
+
**Aspect:** Passive Structure
|
|
14
|
+
|
|
15
|
+
A contract is a formal or informal specification of an agreement between a provider and a consumer that
|
|
16
|
+
specifies rights and obligations. It is a specialization of a business object. A product is accompanied by a
|
|
17
|
+
contract, and a contract can consist of parts.
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
Use BusinessContract to represent:
|
|
22
|
+
|
|
23
|
+
- Framework contracts with a customer
|
|
24
|
+
- Call-offs under a framework contract (an order for one more block of capacity)
|
|
25
|
+
- Service level agreements
|
|
26
|
+
- Amendments of a contract
|
|
27
|
+
|
|
28
|
+
A contract is a `BusinessContract`, not a `DataObject` and not text in a description: only then do the graph,
|
|
29
|
+
views and analyses see what it agrees, who agreed it and what delivered it.
|
|
30
|
+
|
|
31
|
+
## How it connects
|
|
32
|
+
|
|
33
|
+
- A `BusinessProduct` `aggregates` the contract that comes with it
|
|
34
|
+
- It is `agreedBy` its parties: the actual organisations (the customer, the provider), not a generic role
|
|
35
|
+
- It is `composedOf` its parts: call-offs, SLAs and amendments, which are contracts themselves (no cycles)
|
|
36
|
+
- It `covers` what it obliges the provider to deliver: equipment, nodes, technology and application services
|
|
37
|
+
- It is `realizedThrough` the `ImplementationDeliverable` that fulfils it; the `ImplementationWorkPackage` that
|
|
38
|
+
`realizes` the deliverable `affects` the equipment it installs
|
|
39
|
+
|
|
40
|
+
The other side shows these as incoming relations: the contracts an actor agreed, what covers a piece of
|
|
41
|
+
equipment or a service, the contract a deliverable fulfils.
|
|
42
|
+
MD
|
|
43
|
+
|
|
44
|
+
icon "journal-page"
|
|
45
|
+
layer "business"
|
|
46
|
+
|
|
47
|
+
annotation "contract/type",
|
|
48
|
+
description: "What kind of contract this is",
|
|
49
|
+
enum: %w[framework call-off sla amendment other],
|
|
50
|
+
filter: :word,
|
|
51
|
+
summary: true
|
|
52
|
+
annotation "contract/reference",
|
|
53
|
+
description: "The contract number",
|
|
54
|
+
title: "Reference",
|
|
55
|
+
summary: true
|
|
56
|
+
annotation "contract/from",
|
|
57
|
+
description: "When the contract starts (ISO 8601 date or time)",
|
|
58
|
+
title: "From",
|
|
59
|
+
validator: ->(value) { Archsight::Resources::Page.timestamp_error(value) }
|
|
60
|
+
annotation "contract/until",
|
|
61
|
+
description: "When the contract ends (ISO 8601 date or time)",
|
|
62
|
+
title: "Until",
|
|
63
|
+
validator: ->(value) { Archsight::Resources::Page.timestamp_error(value) }
|
|
64
|
+
|
|
65
|
+
relation :agreedBy, :businessActors, :BusinessActor
|
|
66
|
+
relation :composedOf, :businessContracts, :BusinessContract
|
|
67
|
+
relation :covers, :technologyEquipment, :TechnologyEquipment
|
|
68
|
+
relation :covers, :technologyNodes, :TechnologyNode
|
|
69
|
+
relation :covers, :technologyServices, :TechnologyService
|
|
70
|
+
relation :covers, :applicationServices, :ApplicationService
|
|
71
|
+
relation :realizedThrough, :implementationDeliverables, :ImplementationDeliverable
|
|
72
|
+
end
|