archsight 0.1.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 +7 -0
- data/CHANGELOG.md +24 -0
- data/CODE_OF_CONDUCT.md +10 -0
- data/CONTRIBUTING.md +186 -0
- data/Dockerfile +39 -0
- data/LICENSE.txt +201 -0
- data/README.md +170 -0
- data/SECURITY.md +27 -0
- data/exe/archsight +9 -0
- data/lib/archsight/annotations/aggregators.rb +109 -0
- data/lib/archsight/annotations/annotation.rb +168 -0
- data/lib/archsight/annotations/architecture_annotations.rb +59 -0
- data/lib/archsight/annotations/backup_annotations.rb +21 -0
- data/lib/archsight/annotations/computed.rb +264 -0
- data/lib/archsight/annotations/email_recipient.rb +35 -0
- data/lib/archsight/annotations/generated_annotations.rb +17 -0
- data/lib/archsight/annotations/git_annotations.rb +21 -0
- data/lib/archsight/annotations/relation_resolver.rb +160 -0
- data/lib/archsight/cli.rb +120 -0
- data/lib/archsight/configuration.rb +36 -0
- data/lib/archsight/database.rb +183 -0
- data/lib/archsight/documentation.rb +171 -0
- data/lib/archsight/graph.rb +113 -0
- data/lib/archsight/helpers.rb +210 -0
- data/lib/archsight/linter.rb +77 -0
- data/lib/archsight/mcp/analyze_resource_tool.rb +222 -0
- data/lib/archsight/mcp/base.rb +48 -0
- data/lib/archsight/mcp/query_tool.rb +113 -0
- data/lib/archsight/mcp/resource_doc_tool.rb +87 -0
- data/lib/archsight/mcp.rb +6 -0
- data/lib/archsight/query/ast.rb +279 -0
- data/lib/archsight/query/errors.rb +39 -0
- data/lib/archsight/query/evaluator.rb +707 -0
- data/lib/archsight/query/lexer.rb +289 -0
- data/lib/archsight/query/parser.rb +506 -0
- data/lib/archsight/query.rb +68 -0
- data/lib/archsight/renderer.rb +134 -0
- data/lib/archsight/resources/application_component.rb +346 -0
- data/lib/archsight/resources/application_interface.rb +54 -0
- data/lib/archsight/resources/application_service.rb +222 -0
- data/lib/archsight/resources/base.rb +300 -0
- data/lib/archsight/resources/business_actor.rb +195 -0
- data/lib/archsight/resources/business_constraint.rb +32 -0
- data/lib/archsight/resources/business_process.rb +37 -0
- data/lib/archsight/resources/business_product.rb +206 -0
- data/lib/archsight/resources/business_requirement.rb +56 -0
- data/lib/archsight/resources/compliance_evidence.rb +42 -0
- data/lib/archsight/resources/data_object.rb +49 -0
- data/lib/archsight/resources/motivation_goal.rb +37 -0
- data/lib/archsight/resources/motivation_outcome.rb +33 -0
- data/lib/archsight/resources/motivation_stakeholder.rb +38 -0
- data/lib/archsight/resources/strategy_capability.rb +38 -0
- data/lib/archsight/resources/technology_artifact.rb +154 -0
- data/lib/archsight/resources/technology_interface.rb +34 -0
- data/lib/archsight/resources/technology_node.rb +42 -0
- data/lib/archsight/resources/technology_service.rb +35 -0
- data/lib/archsight/resources/technology_system_software.rb +37 -0
- data/lib/archsight/resources/view.rb +51 -0
- data/lib/archsight/resources.rb +49 -0
- data/lib/archsight/template.rb +49 -0
- data/lib/archsight/version.rb +5 -0
- data/lib/archsight/web/application.rb +290 -0
- data/lib/archsight/web/doc/archimate.md +215 -0
- data/lib/archsight/web/doc/computed_annotations.md +316 -0
- data/lib/archsight/web/doc/icons.md +303 -0
- data/lib/archsight/web/doc/index.md.erb +74 -0
- data/lib/archsight/web/doc/modeling.md +200 -0
- data/lib/archsight/web/doc/search.md +227 -0
- data/lib/archsight/web/doc/togaf.md +255 -0
- data/lib/archsight/web/doc/tool.md +90 -0
- data/lib/archsight/web/public/css/artifact.css +985 -0
- data/lib/archsight/web/public/css/base.css +201 -0
- data/lib/archsight/web/public/css/graph.css +106 -0
- data/lib/archsight/web/public/css/highlight.min.css +10 -0
- data/lib/archsight/web/public/css/iconoir.css +22 -0
- data/lib/archsight/web/public/css/instance.css +329 -0
- data/lib/archsight/web/public/css/layout.css +421 -0
- data/lib/archsight/web/public/css/mermaid-layers.css +188 -0
- data/lib/archsight/web/public/css/pico.min.css +4 -0
- data/lib/archsight/web/public/favicon.ico +0 -0
- data/lib/archsight/web/public/img/archimate.png +0 -0
- data/lib/archsight/web/public/img/togaf-high-level.png +0 -0
- data/lib/archsight/web/public/js/graph-zoom.js +18 -0
- data/lib/archsight/web/public/js/highlight.min.js +3899 -0
- data/lib/archsight/web/public/js/htmx.min.js +1 -0
- data/lib/archsight/web/public/js/mermaid-init.js +88 -0
- data/lib/archsight/web/public/js/mermaid.min.js +2811 -0
- data/lib/archsight/web/public/js/sparkline.js +42 -0
- data/lib/archsight/web/public/js/svg-pan-zoom.min.js +3 -0
- data/lib/archsight/web/public/js/svg-zoom-controls.js +93 -0
- data/lib/archsight/web/views/index.haml +12 -0
- data/lib/archsight/web/views/partials/artifact/_activity.haml +55 -0
- data/lib/archsight/web/views/partials/artifact/_agentic.haml +25 -0
- data/lib/archsight/web/views/partials/artifact/_deployment.haml +29 -0
- data/lib/archsight/web/views/partials/artifact/_git_info.haml +16 -0
- data/lib/archsight/web/views/partials/artifact/_language_stats.haml +53 -0
- data/lib/archsight/web/views/partials/artifact/_links.haml +24 -0
- data/lib/archsight/web/views/partials/artifact/_project_estimate.haml +26 -0
- data/lib/archsight/web/views/partials/artifact/_repositories.haml +55 -0
- data/lib/archsight/web/views/partials/artifact/_team.haml +83 -0
- data/lib/archsight/web/views/partials/artifact/_workflow.haml +69 -0
- data/lib/archsight/web/views/partials/components/_activity.haml +37 -0
- data/lib/archsight/web/views/partials/components/_git.haml +17 -0
- data/lib/archsight/web/views/partials/components/_jira.haml +18 -0
- data/lib/archsight/web/views/partials/components/_languages.haml +29 -0
- data/lib/archsight/web/views/partials/components/_owner.haml +15 -0
- data/lib/archsight/web/views/partials/components/_repositories.haml +37 -0
- data/lib/archsight/web/views/partials/components/_status.haml +23 -0
- data/lib/archsight/web/views/partials/instance/_detail.haml +99 -0
- data/lib/archsight/web/views/partials/instance/_graph.haml +6 -0
- data/lib/archsight/web/views/partials/instance/_list.haml +84 -0
- data/lib/archsight/web/views/partials/instance/_relations.haml +43 -0
- data/lib/archsight/web/views/partials/instance/_requirements.haml +41 -0
- data/lib/archsight/web/views/partials/instance/_view_detail.haml +57 -0
- data/lib/archsight/web/views/partials/layout/_content.haml +40 -0
- data/lib/archsight/web/views/partials/layout/_error.haml +22 -0
- data/lib/archsight/web/views/partials/layout/_head.haml +24 -0
- data/lib/archsight/web/views/partials/layout/_navigation.haml +20 -0
- data/lib/archsight/web/views/partials/layout/_sidebar.haml +27 -0
- data/lib/archsight/web/views/search.haml +53 -0
- data/lib/archsight.rb +17 -0
- metadata +311 -0
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# ApplicationComponent a part of the ApplicationService
|
|
4
|
+
class Archsight::Resources::ApplicationComponent < Archsight::Resources::Base
|
|
5
|
+
include_annotations :git, :architecture, :generated, :backup
|
|
6
|
+
|
|
7
|
+
description <<~MD
|
|
8
|
+
Represents a logical part of an application service that can be deployed independently.
|
|
9
|
+
|
|
10
|
+
## ArchiMate Definition
|
|
11
|
+
|
|
12
|
+
**Layer:** Application
|
|
13
|
+
**Aspect:** Active Structure
|
|
14
|
+
|
|
15
|
+
An application component represents an encapsulation of application functionality aligned
|
|
16
|
+
with implementation structure. Components represent the deployable units that together
|
|
17
|
+
form a service.
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
Use ApplicationComponent to represent:
|
|
22
|
+
|
|
23
|
+
- Microservices (api-server, worker, scheduler)
|
|
24
|
+
- Backend components
|
|
25
|
+
- Frontend applications
|
|
26
|
+
- Background workers
|
|
27
|
+
MD
|
|
28
|
+
|
|
29
|
+
icon "component"
|
|
30
|
+
layer "application"
|
|
31
|
+
|
|
32
|
+
# Architecture
|
|
33
|
+
annotation "architecture/kind",
|
|
34
|
+
description: "Architecture pattern or style",
|
|
35
|
+
title: "Architecture Kind",
|
|
36
|
+
enum: %w[3-tier hexagonal kubernetes-operator]
|
|
37
|
+
annotation "architecture/size",
|
|
38
|
+
description: "Architecture size classification",
|
|
39
|
+
title: "Architecture Size",
|
|
40
|
+
enum: %w[microservice monolith]
|
|
41
|
+
|
|
42
|
+
# Availability
|
|
43
|
+
annotation "availability/quality",
|
|
44
|
+
description: "Service availability quality target per year",
|
|
45
|
+
title: "Availability",
|
|
46
|
+
enum: ["no goal", "99.5", "99.95", "99.995", "99.9995"]
|
|
47
|
+
|
|
48
|
+
# Frontend
|
|
49
|
+
annotation "frontend/responsiveness",
|
|
50
|
+
description: "Frontend 99th percentile responsiveness target",
|
|
51
|
+
title: "Frontend Responsiveness (99p)",
|
|
52
|
+
enum: %w[10ms 100ms 1000ms 2s 5s 10s unresponsive]
|
|
53
|
+
|
|
54
|
+
# Persistence
|
|
55
|
+
annotation "persistence/provider",
|
|
56
|
+
description: "Persistence provider type",
|
|
57
|
+
title: "Persistence Provider",
|
|
58
|
+
enum: %w[self-hosted managed-service]
|
|
59
|
+
annotation "persistence/confidential",
|
|
60
|
+
description: "Persistence data confidentiality level",
|
|
61
|
+
title: "Persistence Confidentiality",
|
|
62
|
+
enum: %w[none simple-full-disk per-customer-key rotated-per-customer-key customer-provided-key]
|
|
63
|
+
annotation "persistence/failover",
|
|
64
|
+
description: "Persistence failover configuration",
|
|
65
|
+
title: "Persistence Failover",
|
|
66
|
+
enum: %w[none cold-standby hot-standby]
|
|
67
|
+
|
|
68
|
+
# Audit
|
|
69
|
+
annotation "audit/logging",
|
|
70
|
+
description: "Audit logging destination",
|
|
71
|
+
title: "Audit Logging",
|
|
72
|
+
filter: :word
|
|
73
|
+
annotation "audit/activity",
|
|
74
|
+
description: "Audit activity reliability",
|
|
75
|
+
title: "Audit Activity",
|
|
76
|
+
enum: %w[reliable unreliable]
|
|
77
|
+
|
|
78
|
+
# Security
|
|
79
|
+
annotation "security/handling",
|
|
80
|
+
description: "Security credential handling approach",
|
|
81
|
+
title: "Security Handling",
|
|
82
|
+
enum: ["none", "static-tokens", "revokable", "dynamic-tokens", "work/identity separation"]
|
|
83
|
+
annotation "security/kill-switch",
|
|
84
|
+
description: "Security kill-switch capability",
|
|
85
|
+
title: "Security Kill Switch",
|
|
86
|
+
filter: :word
|
|
87
|
+
|
|
88
|
+
# Abuse
|
|
89
|
+
annotation "abuse/query",
|
|
90
|
+
description: "Abuse query mechanism",
|
|
91
|
+
title: "Abuse Query",
|
|
92
|
+
enum: %w[none manual api]
|
|
93
|
+
annotation "abuse/response",
|
|
94
|
+
description: "Abuse response mechanism",
|
|
95
|
+
title: "Abuse Response",
|
|
96
|
+
enum: %w[none manual api]
|
|
97
|
+
|
|
98
|
+
# Lawful Interception
|
|
99
|
+
annotation "lawful/interception",
|
|
100
|
+
description: "Lawful interception handling capability",
|
|
101
|
+
title: "Lawful Interception",
|
|
102
|
+
enum: %w[none manual api]
|
|
103
|
+
|
|
104
|
+
# Configuration
|
|
105
|
+
annotation "config/handling",
|
|
106
|
+
description: "Configuration handling approach",
|
|
107
|
+
title: "Config Handling",
|
|
108
|
+
enum: ["no configuration", "configuration via file unencrypted secrets",
|
|
109
|
+
"config via ENV encrypted secrets"]
|
|
110
|
+
|
|
111
|
+
# Fault Tolerance
|
|
112
|
+
annotation "faultTolerance/quality",
|
|
113
|
+
description: "Fault tolerance quality level",
|
|
114
|
+
title: "Fault Tolerance",
|
|
115
|
+
enum: ["no fault tolerance", "survive server failure", "survive rack failure", "survive DC failure",
|
|
116
|
+
"regional architecture"]
|
|
117
|
+
|
|
118
|
+
# Supportability
|
|
119
|
+
annotation "supportability/quality",
|
|
120
|
+
description: "Supportability quality level",
|
|
121
|
+
title: "Supportability",
|
|
122
|
+
enum: ["no support", "support by dev", "support by ops", "support by api",
|
|
123
|
+
"self service support via a frontend"]
|
|
124
|
+
|
|
125
|
+
# Deployment (from ArgoCD/cluster)
|
|
126
|
+
annotation "deployment/images",
|
|
127
|
+
description: "Deployed container images",
|
|
128
|
+
title: "Container Images",
|
|
129
|
+
sidebar: false,
|
|
130
|
+
filter: :list
|
|
131
|
+
annotation "deployment/chart",
|
|
132
|
+
description: "Deployed Helm chart name",
|
|
133
|
+
title: "Helm Chart",
|
|
134
|
+
sidebar: false,
|
|
135
|
+
filter: :word
|
|
136
|
+
annotation "deployment/namespace",
|
|
137
|
+
description: "Kubernetes namespace",
|
|
138
|
+
title: "Namespace",
|
|
139
|
+
sidebar: false,
|
|
140
|
+
filter: :word
|
|
141
|
+
annotation "deployment/cluster",
|
|
142
|
+
description: "Target cluster name",
|
|
143
|
+
title: "Cluster",
|
|
144
|
+
filter: :word
|
|
145
|
+
|
|
146
|
+
# Computed Annotations
|
|
147
|
+
computed_annotation "repository/artifacts/total",
|
|
148
|
+
title: "Total Git Repositories",
|
|
149
|
+
description: "Number of related git repositories",
|
|
150
|
+
type: Integer do
|
|
151
|
+
count(outgoing_transitive('TechnologyArtifact: artifact/type == "repo"'))
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
computed_annotation "repository/artifacts/active",
|
|
155
|
+
title: "Active Git Repositories",
|
|
156
|
+
description: "Number of related git repositories",
|
|
157
|
+
type: Integer do
|
|
158
|
+
count(outgoing_transitive('TechnologyArtifact: artifact/type == "repo" & activity/status == "active"'))
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
computed_annotation "repository/artifacts/abandoned",
|
|
162
|
+
title: "Abandoned Git Repositories",
|
|
163
|
+
description: "Number of related git repositories",
|
|
164
|
+
type: Integer do
|
|
165
|
+
count(outgoing_transitive('TechnologyArtifact: artifact/type == "repo" & activity/status == "abandoned"'))
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
computed_annotation "repository/artifacts/highBusFactor",
|
|
169
|
+
title: "High Bus Factor Repositories",
|
|
170
|
+
description: "Number of active git repositories with high bus factor",
|
|
171
|
+
type: Integer do
|
|
172
|
+
count(outgoing_transitive('TechnologyArtifact: artifact/type == "repo" & activity/status == "active" & activity/busFactor == "high"'))
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
computed_annotation "repository/artifacts/archived",
|
|
176
|
+
title: "Archived Repositories",
|
|
177
|
+
description: "Number of archived git repositories",
|
|
178
|
+
type: Integer do
|
|
179
|
+
count(outgoing_transitive('TechnologyArtifact: artifact/type == "repo" & activity/status == "archived"'))
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
%w[scc/estimatedCost scc/estimatedScheduleMonths scc/estimatedPeople].each do |anno_key|
|
|
183
|
+
computed_annotation anno_key,
|
|
184
|
+
title: "Total #{anno_key.split("/").last.split(/(?=[A-Z])/).map(&:capitalize).join(" ")}",
|
|
185
|
+
description: "Total estimated #{anno_key.split("/").last} from related artifacts",
|
|
186
|
+
type: Integer do
|
|
187
|
+
sum(outgoing_transitive(:TechnologyArtifact), anno_key)
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
computed_annotation "scc/language",
|
|
192
|
+
title: "Primary Language",
|
|
193
|
+
list: true,
|
|
194
|
+
description: "Programming language with most lines of code across related artifacts",
|
|
195
|
+
filter: :word,
|
|
196
|
+
sidebar: true do
|
|
197
|
+
# Aggregate LOC per language across all related artifacts
|
|
198
|
+
artifacts = outgoing_transitive(:TechnologyArtifact)
|
|
199
|
+
loc_by_language = Hash.new(0)
|
|
200
|
+
|
|
201
|
+
artifacts.each do |artifact|
|
|
202
|
+
artifact.annotations.each do |key, value|
|
|
203
|
+
# Match annotations like scc/language/Java/loc
|
|
204
|
+
if key =~ %r{^scc/language/(.+)/loc$}
|
|
205
|
+
language = ::Regexp.last_match(1)
|
|
206
|
+
loc_by_language[language] += value.to_i
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
# Return the language with the most LOC
|
|
212
|
+
loc_by_language.max_by { |_, loc| loc }&.first
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
computed_annotation "activity/commits",
|
|
216
|
+
title: "Monthly Commits",
|
|
217
|
+
description: "Accumulated monthly commit counts across all related repositories (excluding community repos)",
|
|
218
|
+
sidebar: false do
|
|
219
|
+
artifacts = outgoing_transitive('TechnologyArtifact: artifact/type == "repo"')
|
|
220
|
+
next nil if artifacts.empty?
|
|
221
|
+
|
|
222
|
+
# Collect all commit arrays (they all end at the current month)
|
|
223
|
+
# Skip community repos - their commit data reflects upstream project activity, not IONOS work
|
|
224
|
+
commit_arrays = artifacts.map do |artifact|
|
|
225
|
+
next nil if artifact.annotations["repository/visibility"] == "open-source"
|
|
226
|
+
|
|
227
|
+
commits_str = artifact.annotations["activity/commits"]
|
|
228
|
+
next nil if commits_str.nil? || commits_str.empty?
|
|
229
|
+
|
|
230
|
+
commits_str.split(",").map(&:to_i)
|
|
231
|
+
end.compact
|
|
232
|
+
|
|
233
|
+
next nil if commit_arrays.empty?
|
|
234
|
+
|
|
235
|
+
# Find the maximum length (oldest repo determines the timeline)
|
|
236
|
+
max_length = commit_arrays.map(&:length).max
|
|
237
|
+
|
|
238
|
+
# Pad shorter arrays at the beginning with zeros and sum
|
|
239
|
+
result = Array.new(max_length, 0)
|
|
240
|
+
commit_arrays.each do |arr|
|
|
241
|
+
offset = max_length - arr.length
|
|
242
|
+
arr.each_with_index do |count, idx|
|
|
243
|
+
result[offset + idx] += count
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
result.join(",")
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
computed_annotation "activity/createdAt",
|
|
251
|
+
title: "Created",
|
|
252
|
+
description: "Earliest repository creation date across all related repositories (excluding community repos)",
|
|
253
|
+
type: Time do
|
|
254
|
+
artifacts = outgoing_transitive('TechnologyArtifact: artifact/type == "repo"')
|
|
255
|
+
next nil if artifacts.empty?
|
|
256
|
+
|
|
257
|
+
# Skip community repos - their creation dates reflect upstream project history, not IONOS work
|
|
258
|
+
dates = artifacts.map do |artifact|
|
|
259
|
+
next nil if artifact.annotations["repository/visibility"] == "open-source"
|
|
260
|
+
|
|
261
|
+
date_str = artifact.annotations["activity/createdAt"]
|
|
262
|
+
next nil if date_str.nil? || date_str.empty?
|
|
263
|
+
|
|
264
|
+
begin
|
|
265
|
+
Time.parse(date_str)
|
|
266
|
+
rescue StandardError
|
|
267
|
+
nil
|
|
268
|
+
end
|
|
269
|
+
end.compact
|
|
270
|
+
|
|
271
|
+
dates.min
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
computed_annotation "activity/contributors",
|
|
275
|
+
title: "Monthly Contributors",
|
|
276
|
+
description: "Accumulated monthly unique contributor counts across all related repositories (excluding community repos)",
|
|
277
|
+
sidebar: false do
|
|
278
|
+
artifacts = outgoing_transitive('TechnologyArtifact: artifact/type == "repo"')
|
|
279
|
+
next nil if artifacts.empty?
|
|
280
|
+
|
|
281
|
+
# Collect all contributor arrays (they all end at the current month)
|
|
282
|
+
# Skip community repos - their contributor data reflects upstream project, not IONOS team size
|
|
283
|
+
contrib_arrays = artifacts.map do |artifact|
|
|
284
|
+
next nil if artifact.annotations["repository/visibility"] == "open-source"
|
|
285
|
+
|
|
286
|
+
contrib_str = artifact.annotations["activity/contributors"]
|
|
287
|
+
next nil if contrib_str.nil? || contrib_str.empty?
|
|
288
|
+
|
|
289
|
+
contrib_str.split(",").map(&:to_i)
|
|
290
|
+
end.compact
|
|
291
|
+
|
|
292
|
+
next nil if contrib_arrays.empty?
|
|
293
|
+
|
|
294
|
+
# Find the maximum length (oldest repo determines the timeline)
|
|
295
|
+
max_length = contrib_arrays.map(&:length).max
|
|
296
|
+
|
|
297
|
+
# Pad shorter arrays at the beginning with zeros and sum
|
|
298
|
+
result = Array.new(max_length, 0)
|
|
299
|
+
contrib_arrays.each do |arr|
|
|
300
|
+
offset = max_length - arr.length
|
|
301
|
+
arr.each_with_index do |count, idx|
|
|
302
|
+
result[offset + idx] += count
|
|
303
|
+
end
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
result.join(",")
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
computed_annotation "activity/contributors/6m",
|
|
310
|
+
title: "Contributors (6 months)",
|
|
311
|
+
description: "Sum of unique contributors in the last 6 months across related repositories",
|
|
312
|
+
type: Integer do
|
|
313
|
+
artifacts = outgoing_transitive('TechnologyArtifact: artifact/type == "repo"')
|
|
314
|
+
next nil if artifacts.empty?
|
|
315
|
+
|
|
316
|
+
# Sum unique contributor counts from related artifacts (excluding open-source repos)
|
|
317
|
+
total = artifacts.sum do |artifact|
|
|
318
|
+
next 0 if artifact.annotations["repository/visibility"] == "open-source"
|
|
319
|
+
|
|
320
|
+
artifact.annotations["activity/contributors/6m"].to_i
|
|
321
|
+
end
|
|
322
|
+
total.positive? ? total : nil
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
computed_annotation "activity/contributors/total",
|
|
326
|
+
title: "Contributors (total)",
|
|
327
|
+
description: "Sum of unique contributors across related repositories",
|
|
328
|
+
type: Integer do
|
|
329
|
+
artifacts = outgoing_transitive('TechnologyArtifact: artifact/type == "repo"')
|
|
330
|
+
next nil if artifacts.empty?
|
|
331
|
+
|
|
332
|
+
# Sum unique contributor counts from related artifacts (excluding open-source repos)
|
|
333
|
+
total = artifacts.sum do |artifact|
|
|
334
|
+
next 0 if artifact.annotations["repository/visibility"] == "open-source"
|
|
335
|
+
|
|
336
|
+
artifact.annotations["activity/contributors/total"].to_i
|
|
337
|
+
end
|
|
338
|
+
total.positive? ? total : nil
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
relation :realizedThrough, :technologyArtifacts, :TechnologyArtifact
|
|
342
|
+
relation :realizedBy, :technologyComponents, :TechnologyNode
|
|
343
|
+
relation :servedBy, :technologyComponents, :TechnologySystemSoftware
|
|
344
|
+
relation :exposes, :applicationInterfaces, :ApplicationInterface
|
|
345
|
+
relation :dependsOn, :applicationInterfaces, :ApplicationInterface
|
|
346
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# ApplicationInterface between ApplicationComponent
|
|
4
|
+
class Archsight::Resources::ApplicationInterface < Archsight::Resources::Base
|
|
5
|
+
include_annotations :git, :architecture, :generated
|
|
6
|
+
|
|
7
|
+
description <<~MD
|
|
8
|
+
Represents a point of access where application services are made available.
|
|
9
|
+
|
|
10
|
+
## ArchiMate Definition
|
|
11
|
+
|
|
12
|
+
**Layer:** Application
|
|
13
|
+
**Aspect:** Active Structure (external)
|
|
14
|
+
|
|
15
|
+
An application interface represents a point of access where application services
|
|
16
|
+
are made available to a user, another application component, or a node. It exposes
|
|
17
|
+
application behavior to the environment.
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
Use ApplicationInterface to represent:
|
|
22
|
+
|
|
23
|
+
- REST APIs
|
|
24
|
+
- GraphQL endpoints
|
|
25
|
+
- gRPC services
|
|
26
|
+
- Message queues (producer/consumer interfaces)
|
|
27
|
+
- WebSocket endpoints
|
|
28
|
+
MD
|
|
29
|
+
|
|
30
|
+
icon "usb"
|
|
31
|
+
layer "application"
|
|
32
|
+
|
|
33
|
+
# API
|
|
34
|
+
annotation "api/responsiveness",
|
|
35
|
+
description: "API 99th percentile responsiveness target",
|
|
36
|
+
title: "API Responsiveness (99p)",
|
|
37
|
+
enum: %w[10ms 100ms 1000ms 2s 5s 10s unresponsive]
|
|
38
|
+
annotation "api/authenticationMethod",
|
|
39
|
+
description: "API authentication method",
|
|
40
|
+
title: "API Authentication Method",
|
|
41
|
+
enum: ["none", "hard coded", "token", "oidc"]
|
|
42
|
+
annotation "api/authenticationProvider",
|
|
43
|
+
description: "API authentication provider",
|
|
44
|
+
title: "API Authentication Provider",
|
|
45
|
+
enum: %w[eiam cloud custom]
|
|
46
|
+
annotation "api/authorization",
|
|
47
|
+
description: "API authorization mechanism",
|
|
48
|
+
title: "API Authorization",
|
|
49
|
+
enum: ["none", "hard coded", "pbac", "abac", "rbac"]
|
|
50
|
+
|
|
51
|
+
relation :servedBy, :technologyComponents, :TechnologyInterface
|
|
52
|
+
relation :realizes, :businessConstraints, :BusinessConstraint
|
|
53
|
+
relation :serves, :dataObjects, :DataObject
|
|
54
|
+
end
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# ApplicationService represents the high level application service that implements capabilities
|
|
4
|
+
class Archsight::Resources::ApplicationService < Archsight::Resources::Base
|
|
5
|
+
include_annotations :git, :architecture, :generated, :backup
|
|
6
|
+
|
|
7
|
+
description <<~MD
|
|
8
|
+
Represents a high-level application service that implements business capabilities.
|
|
9
|
+
|
|
10
|
+
## ArchiMate Definition
|
|
11
|
+
|
|
12
|
+
**Layer:** Application
|
|
13
|
+
**Aspect:** Behavior
|
|
14
|
+
|
|
15
|
+
An application service represents an explicitly defined exposed application behavior.
|
|
16
|
+
It is the externally visible functionality provided by application components, grouping
|
|
17
|
+
multiple components that work together to deliver business value.
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
Use ApplicationService to represent:
|
|
22
|
+
|
|
23
|
+
- Complete product offerings (ManagedKubernetes, DBaaS PostgreSQL)
|
|
24
|
+
- Logical groupings of application components
|
|
25
|
+
- Services exposed to business processes
|
|
26
|
+
- APIs and their implementations as a cohesive unit
|
|
27
|
+
MD
|
|
28
|
+
|
|
29
|
+
icon "cube"
|
|
30
|
+
layer "application"
|
|
31
|
+
|
|
32
|
+
# Service plane classification
|
|
33
|
+
annotation "architecture/plane",
|
|
34
|
+
description: "Service plane classification (control manages resources, data handles traffic)",
|
|
35
|
+
title: "Service Plane",
|
|
36
|
+
enum: %w[control data],
|
|
37
|
+
list: true
|
|
38
|
+
|
|
39
|
+
# Computed Annotations
|
|
40
|
+
computed_annotation "repository/artifacts/total",
|
|
41
|
+
title: "Total Git Repositories",
|
|
42
|
+
description: "Number of related git repositories across all related application components",
|
|
43
|
+
type: Integer do
|
|
44
|
+
sum(outgoing_transitive(:ApplicationComponent), "repository/artifacts/total")
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
computed_annotation "repository/artifacts/active",
|
|
48
|
+
title: "Active Git Repositories",
|
|
49
|
+
description: "Number of related git repositories across all related application components",
|
|
50
|
+
type: Integer do
|
|
51
|
+
sum(outgoing_transitive(:ApplicationComponent), "repository/artifacts/active")
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
computed_annotation "repository/artifacts/abandoned",
|
|
55
|
+
title: "Abandoned Git Repositories",
|
|
56
|
+
description: "Number of related git repositories across all related application components",
|
|
57
|
+
type: Integer do
|
|
58
|
+
sum(outgoing_transitive(:ApplicationComponent), "repository/artifacts/abandoned")
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
computed_annotation "repository/artifacts/highBusFactor",
|
|
62
|
+
title: "High Bus Factor Repositories",
|
|
63
|
+
description: "Number of active git repositories with high bus factor across all related application components",
|
|
64
|
+
type: Integer do
|
|
65
|
+
sum(outgoing_transitive(:ApplicationComponent), "repository/artifacts/highBusFactor")
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
computed_annotation "repository/artifacts/archived",
|
|
69
|
+
title: "Archived Repositories",
|
|
70
|
+
description: "Number of archived git repositories across all related application components",
|
|
71
|
+
type: Integer do
|
|
72
|
+
sum(outgoing_transitive(:ApplicationComponent), "repository/artifacts/archived")
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
%w[scc/estimatedCost scc/estimatedScheduleMonths scc/estimatedPeople].each do |anno_key|
|
|
76
|
+
computed_annotation anno_key,
|
|
77
|
+
title: "Total #{anno_key.split("/").last.split(/(?=[A-Z])/).map(&:capitalize).join(" ")}",
|
|
78
|
+
description: "Total estimated #{anno_key.split("/").last} from related artifacts across all related application components",
|
|
79
|
+
type: Integer do
|
|
80
|
+
sum(outgoing_transitive(:ApplicationComponent), anno_key)
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
computed_annotation "scc/languages",
|
|
85
|
+
title: "Primary Languages",
|
|
86
|
+
list: true,
|
|
87
|
+
description: "Top 4 programming languages by lines of code across related artifacts",
|
|
88
|
+
filter: :list,
|
|
89
|
+
sidebar: true do
|
|
90
|
+
collect(outgoing_transitive(:ApplicationComponent), "scc/language")
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
computed_annotation "activity/commits",
|
|
94
|
+
title: "Monthly Commits",
|
|
95
|
+
description: "Accumulated monthly commit counts across all related application components",
|
|
96
|
+
sidebar: false do
|
|
97
|
+
components = outgoing_transitive(:ApplicationComponent)
|
|
98
|
+
next nil if components.empty?
|
|
99
|
+
|
|
100
|
+
# Collect all commit arrays from components (they all end at the current month)
|
|
101
|
+
# Use get() to trigger computation of computed annotations on ApplicationComponents
|
|
102
|
+
commit_arrays = components.map do |component|
|
|
103
|
+
commits_str = get(component, "activity/commits")
|
|
104
|
+
next nil if commits_str.nil? || commits_str.empty?
|
|
105
|
+
|
|
106
|
+
commits_str.split(",").map(&:to_i)
|
|
107
|
+
end.compact
|
|
108
|
+
|
|
109
|
+
next nil if commit_arrays.empty?
|
|
110
|
+
|
|
111
|
+
# Find the maximum length (oldest component determines the timeline)
|
|
112
|
+
max_length = commit_arrays.map(&:length).max
|
|
113
|
+
|
|
114
|
+
# Pad shorter arrays at the beginning with zeros and sum
|
|
115
|
+
result = Array.new(max_length, 0)
|
|
116
|
+
commit_arrays.each do |arr|
|
|
117
|
+
offset = max_length - arr.length
|
|
118
|
+
arr.each_with_index do |count, idx|
|
|
119
|
+
result[offset + idx] += count
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
result.join(",")
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
computed_annotation "activity/createdAt",
|
|
127
|
+
title: "Created",
|
|
128
|
+
description: "Earliest repository creation date across all related application components",
|
|
129
|
+
type: Time do
|
|
130
|
+
components = outgoing_transitive(:ApplicationComponent)
|
|
131
|
+
next nil if components.empty?
|
|
132
|
+
|
|
133
|
+
# Use get() to trigger computation of computed annotations on ApplicationComponents
|
|
134
|
+
dates = components.map do |component|
|
|
135
|
+
date_val = get(component, "activity/createdAt")
|
|
136
|
+
next nil if date_val.nil?
|
|
137
|
+
|
|
138
|
+
if date_val.is_a?(Time)
|
|
139
|
+
date_val
|
|
140
|
+
else
|
|
141
|
+
begin
|
|
142
|
+
Time.parse(date_val.to_s)
|
|
143
|
+
rescue StandardError
|
|
144
|
+
nil
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
end.compact
|
|
148
|
+
|
|
149
|
+
dates.min
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
computed_annotation "activity/contributors",
|
|
153
|
+
title: "Monthly Contributors",
|
|
154
|
+
description: "Accumulated monthly unique contributor counts across all related application components",
|
|
155
|
+
sidebar: false do
|
|
156
|
+
components = outgoing_transitive(:ApplicationComponent)
|
|
157
|
+
next nil if components.empty?
|
|
158
|
+
|
|
159
|
+
# Collect all contributor arrays from components (they all end at the current month)
|
|
160
|
+
# Use get() to trigger computation of computed annotations on ApplicationComponents
|
|
161
|
+
contrib_arrays = components.map do |component|
|
|
162
|
+
contrib_str = get(component, "activity/contributors")
|
|
163
|
+
next nil if contrib_str.nil? || contrib_str.empty?
|
|
164
|
+
|
|
165
|
+
contrib_str.split(",").map(&:to_i)
|
|
166
|
+
end.compact
|
|
167
|
+
|
|
168
|
+
next nil if contrib_arrays.empty?
|
|
169
|
+
|
|
170
|
+
# Find the maximum length (oldest component determines the timeline)
|
|
171
|
+
max_length = contrib_arrays.map(&:length).max
|
|
172
|
+
|
|
173
|
+
# Pad shorter arrays at the beginning with zeros and sum
|
|
174
|
+
result = Array.new(max_length, 0)
|
|
175
|
+
contrib_arrays.each do |arr|
|
|
176
|
+
offset = max_length - arr.length
|
|
177
|
+
arr.each_with_index do |count, idx|
|
|
178
|
+
result[offset + idx] += count
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
result.join(",")
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
computed_annotation "activity/contributors/6m",
|
|
186
|
+
title: "Contributors (6 months)",
|
|
187
|
+
description: "Sum of unique contributors in the last 6 months across related components",
|
|
188
|
+
type: Integer do
|
|
189
|
+
components = outgoing_transitive(:ApplicationComponent)
|
|
190
|
+
next nil if components.empty?
|
|
191
|
+
|
|
192
|
+
# Sum unique contributor counts from related components
|
|
193
|
+
total = components.sum do |component|
|
|
194
|
+
get(component, "activity/contributors/6m").to_i
|
|
195
|
+
end
|
|
196
|
+
total.positive? ? total : nil
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
computed_annotation "activity/contributors/total",
|
|
200
|
+
title: "Contributors (total)",
|
|
201
|
+
description: "Sum of unique contributors across related components",
|
|
202
|
+
type: Integer do
|
|
203
|
+
components = outgoing_transitive(:ApplicationComponent)
|
|
204
|
+
next nil if components.empty?
|
|
205
|
+
|
|
206
|
+
# Sum unique contributor counts from related components
|
|
207
|
+
total = components.sum do |component|
|
|
208
|
+
get(component, "activity/contributors/total").to_i
|
|
209
|
+
end
|
|
210
|
+
total.positive? ? total : nil
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
relation :realizedThrough, :applicationComponents, :ApplicationComponent
|
|
214
|
+
relation :servedBy, :businessActors, :BusinessActor
|
|
215
|
+
relation :servedBy, :technologyServices, :TechnologyService
|
|
216
|
+
relation :realizes, :businessConstraints, :BusinessConstraint
|
|
217
|
+
relation :realizes, :businessRequirements, :BusinessRequirement
|
|
218
|
+
relation :partiallyRealizes, :businessRequirements, :BusinessRequirement
|
|
219
|
+
relation :plans, :businessRequirements, :BusinessRequirement
|
|
220
|
+
relation :realizes, :dataObjects, :DataObject
|
|
221
|
+
relation :evidencedBy, :complianceEvidences, :ComplianceEvidence
|
|
222
|
+
end
|