archsight 0.3.3 → 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.
Files changed (173) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +7 -1
  3. data/docs/archimate.md +21 -1
  4. data/docs/configuration.md +1 -0
  5. data/docs/diagram.md +15 -3
  6. data/docs/docker.md +4 -1
  7. data/docs/icons.md +8 -2
  8. data/docs/import.md +178 -55
  9. data/docs/index.md.erb +24 -18
  10. data/docs/modeling.md +637 -34
  11. data/docs/pages.md +44 -13
  12. data/docs/search.md +21 -1
  13. data/docs/togaf.md +2 -1
  14. data/lib/archsight/analysis/result.rb +8 -2
  15. data/lib/archsight/analysis/sandbox.rb +8 -0
  16. data/lib/archsight/analysis/typst_renderer.rb +246 -0
  17. data/lib/archsight/analysis.rb +1 -0
  18. data/lib/archsight/annotations/annotation.rb +30 -3
  19. data/lib/archsight/annotations/architecture_annotations.rb +47 -0
  20. data/lib/archsight/annotations/link_annotations.rb +15 -0
  21. data/lib/archsight/annotations/resource_reference.rb +12 -0
  22. data/lib/archsight/cli.rb +124 -23
  23. data/lib/archsight/database.rb +40 -0
  24. data/lib/archsight/diagram/cli.rb +50 -2
  25. data/lib/archsight/diagram/formatter.rb +112 -0
  26. data/lib/archsight/diagram/routing/edge_router/orthogonal_path.rb +38 -10
  27. data/lib/archsight/diagram/routing/edge_router.rb +29 -4
  28. data/lib/archsight/diagram/routing/edge_routing.rb +61 -5
  29. data/lib/archsight/diagram/style/theme.rb +7 -2
  30. data/lib/archsight/documentation.rb +4 -4
  31. data/lib/archsight/editor/page_source.rb +13 -1
  32. data/lib/archsight/export/confluence/analysis_output.rb +101 -0
  33. data/lib/archsight/export/confluence/client.rb +13 -0
  34. data/lib/archsight/export/confluence/diagram_links.rb +1 -1
  35. data/lib/archsight/export/confluence/exporter.rb +40 -13
  36. data/lib/archsight/export/confluence/labels.rb +25 -0
  37. data/lib/archsight/export/confluence/page_properties.rb +4 -6
  38. data/lib/archsight/export/confluence/storage.rb +38 -2
  39. data/lib/archsight/helpers/embeds.rb +18 -7
  40. data/lib/archsight/helpers/resource_resolver.rb +25 -0
  41. data/lib/archsight/import/executor.rb +139 -16
  42. data/lib/archsight/import/handler.rb +55 -6
  43. data/lib/archsight/import/handler_loader.rb +64 -0
  44. data/lib/archsight/import/handlers/cache_pruner.rb +30 -7
  45. data/lib/archsight/import/handlers/command.rb +120 -0
  46. data/lib/archsight/import/handlers/github.rb +4 -1
  47. data/lib/archsight/import/handlers/gitlab.rb +4 -1
  48. data/lib/archsight/import/handlers/go_dep_resolver.rb +10 -2
  49. data/lib/archsight/import/handlers/go_grapher.rb +102 -5
  50. data/lib/archsight/import/handlers/go_module_parser.rb +88 -0
  51. data/lib/archsight/import/handlers/grapher.rb +40 -1
  52. data/lib/archsight/import/handlers/repository.rb +3 -0
  53. data/lib/archsight/import/handlers/rest_api_index.rb +3 -0
  54. data/lib/archsight/import/registry.rb +22 -1
  55. data/lib/archsight/import.rb +64 -1
  56. data/lib/archsight/linter/structure.rb +239 -0
  57. data/lib/archsight/linter.rb +121 -14
  58. data/lib/archsight/page_loader.rb +12 -1
  59. data/lib/archsight/query/evaluator.rb +13 -6
  60. data/lib/archsight/query/parser.rb +73 -6
  61. data/lib/archsight/query.rb +1 -1
  62. data/lib/archsight/references.rb +3 -3
  63. data/lib/archsight/renderer.rb +80 -23
  64. data/lib/archsight/resources/analysis.rb +3 -2
  65. data/lib/archsight/resources/application_component.rb +75 -7
  66. data/lib/archsight/resources/application_service.rb +12 -1
  67. data/lib/archsight/resources/base.rb +58 -5
  68. data/lib/archsight/resources/business_actor.rb +5 -0
  69. data/lib/archsight/resources/business_contract.rb +72 -0
  70. data/lib/archsight/resources/business_product.rb +12 -0
  71. data/lib/archsight/resources/business_role.rb +1 -0
  72. data/lib/archsight/resources/implementation_deliverable.rb +1 -0
  73. data/lib/archsight/resources/implementation_plateau.rb +8 -2
  74. data/lib/archsight/resources/implementation_work_package.rb +2 -1
  75. data/lib/archsight/resources/import.rb +37 -11
  76. data/lib/archsight/resources/location.rb +56 -0
  77. data/lib/archsight/resources/motivation_requirement.rb +6 -0
  78. data/lib/archsight/resources/motivation_stakeholder.rb +14 -0
  79. data/lib/archsight/resources/page.rb +4 -7
  80. data/lib/archsight/resources/strategy_capability.rb +9 -0
  81. data/lib/archsight/resources/technology_artifact.rb +11 -4
  82. data/lib/archsight/resources/technology_communication_network.rb +89 -0
  83. data/lib/archsight/resources/technology_device.rb +88 -0
  84. data/lib/archsight/resources/technology_equipment.rb +75 -0
  85. data/lib/archsight/resources/technology_facility.rb +70 -0
  86. data/lib/archsight/resources/technology_node.rb +50 -0
  87. data/lib/archsight/resources/technology_service.rb +33 -0
  88. data/lib/archsight/resources/technology_system_software.rb +28 -0
  89. data/lib/archsight/resources.rb +12 -0
  90. data/lib/archsight/version.rb +1 -1
  91. data/lib/archsight/web/api/analysis_helpers.rb +55 -0
  92. data/lib/archsight/web/api/json_helpers.rb +3 -37
  93. data/lib/archsight/web/api/openapi/spec.yaml +10 -8
  94. data/lib/archsight/web/api/page_helpers.rb +1 -8
  95. data/lib/archsight/web/api/routes.rb +7 -2
  96. data/lib/archsight/web/application.rb +38 -2
  97. data/lib/archsight/web/editor/routes.rb +2 -1
  98. data/lib/archsight/web/public/vue/ApiDocsPage-COp4onwn.js +1 -0
  99. data/lib/archsight/web/public/vue/DocPage-D52OwaKc.js +1 -0
  100. data/lib/archsight/web/public/vue/EditorPage-3aqL7yVf.js +35 -0
  101. data/lib/archsight/web/public/vue/ErrorPage--b9XeSYa.js +2 -0
  102. data/lib/archsight/web/public/vue/GraphView-qh_EtRrQ.js +1 -0
  103. data/lib/archsight/web/public/vue/HomePage-C2TNh2U-.js +2 -0
  104. data/lib/archsight/web/public/vue/InstanceRouter-ZaCcjlIX.css +1 -0
  105. data/lib/archsight/web/public/vue/InstanceRouter-uzb_OMG0.js +1 -0
  106. data/lib/archsight/web/public/vue/KindList-CyJuP_uq.js +1 -0
  107. data/lib/archsight/web/public/vue/KindList-yYcRT2BV.css +1 -0
  108. data/lib/archsight/web/public/vue/PageView-B93woeN3.css +1 -0
  109. data/lib/archsight/web/public/vue/PageView-FyygrS-b.js +1 -0
  110. data/lib/archsight/web/public/vue/QueryError-B4Ex_csZ.js +1 -0
  111. data/lib/archsight/web/public/vue/ResourceList-BY6Z1Kcb.js +2 -0
  112. data/lib/archsight/web/public/vue/ResourceList-D75O87zG.css +1 -0
  113. data/lib/archsight/web/public/vue/{SearchResults-BewvsfOc.css → SearchResults-BV6UHriJ.css} +1 -1
  114. data/lib/archsight/web/public/vue/SearchResults-CkFpHF8m.js +1 -0
  115. data/lib/archsight/web/public/vue/WikiPage-BoHNewCX.js +3 -0
  116. data/lib/archsight/web/public/vue/WikiPage-TpmM1VAF.css +1 -0
  117. data/lib/archsight/web/public/vue/architecture-7GRP2DOG-BvzKZE4e.js +1 -0
  118. data/lib/archsight/web/public/vue/cynefin-OW5HDTMX-D-xs_Hxh.js +1 -0
  119. data/lib/archsight/web/public/vue/eventmodeling-NTZA5JFV-nbjxG01S.js +1 -0
  120. data/lib/archsight/web/public/vue/gitGraph-4MIJSDKK-CW6mw2ab.js +1 -0
  121. data/lib/archsight/web/public/vue/index-BGlYCbi7.js +3 -0
  122. data/lib/archsight/web/public/vue/index-eUHmJ7K2.css +1 -0
  123. data/lib/archsight/web/public/vue/info-A6RAGUB7-C12sjlHe.js +1 -0
  124. data/lib/archsight/web/public/vue/{mermaid-BjEi5URd.js → mermaid-DjsXQ_XN.js} +35 -35
  125. data/lib/archsight/web/public/vue/packet-AYTQ26CC-wMwKwHmA.js +1 -0
  126. data/lib/archsight/web/public/vue/pie-WAS4IAKB-w1-sAU_K.js +1 -0
  127. data/lib/archsight/web/public/vue/radar-RG4KPBEZ-DpwqIFoy.js +1 -0
  128. data/lib/archsight/web/public/vue/railroad-74A4TZTK-Ce5XC24d.js +1 -0
  129. data/lib/archsight/web/public/vue/railroad-abnf-HS5TGJTU-BSQO2pds.js +1 -0
  130. data/lib/archsight/web/public/vue/railroad-ebnf-LZEXJU2U-Dq1c2YtD.js +1 -0
  131. data/lib/archsight/web/public/vue/railroad-peg-WCYAUIDC-DKj6dli0.js +1 -0
  132. data/lib/archsight/web/public/vue/rolldown-runtime-hePW80VL.js +1 -0
  133. data/lib/archsight/web/public/vue/treeView-Q6P3EWNA-BmHNTWxU.js +1 -0
  134. data/lib/archsight/web/public/vue/treemap-WGGIJYW6-DsEI7sSD.js +1 -0
  135. data/lib/archsight/web/public/vue/{useGraphviz-DlSnFeCL.js → useGraphviz-4evusK9i.js} +1 -1
  136. data/lib/archsight/web/public/vue/{useHighlight-D5Gf8DLl.js → useHighlight-DPguaxfm.js} +8 -8
  137. data/lib/archsight/web/public/vue/usePanZoom-seQE1TPk.js +12 -0
  138. data/lib/archsight/web/public/vue/wardley-WFR3VGLG-CSCW83-M.js +1 -0
  139. data/lib/archsight/web/public/vue.html +5 -5
  140. metadata +58 -38
  141. data/lib/archsight/web/public/vue/ApiDocsPage-BgqnQgwa.js +0 -1
  142. data/lib/archsight/web/public/vue/DocPage-CNO71nBH.js +0 -1
  143. data/lib/archsight/web/public/vue/EditorPage-DR0FCNTz.js +0 -35
  144. data/lib/archsight/web/public/vue/ErrorPage-BfYArv6s.js +0 -2
  145. data/lib/archsight/web/public/vue/GraphView-BVTW30Ak.js +0 -1
  146. data/lib/archsight/web/public/vue/HomePage-Wk9P4Pma.js +0 -2
  147. data/lib/archsight/web/public/vue/InstanceRouter-BV8ycydz.js +0 -1
  148. data/lib/archsight/web/public/vue/InstanceRouter-jeavM6PW.css +0 -1
  149. data/lib/archsight/web/public/vue/KindList-C8yMsN5J.js +0 -1
  150. data/lib/archsight/web/public/vue/PageView-BaN6TyJB.js +0 -1
  151. data/lib/archsight/web/public/vue/QueryError-D790wH-a.js +0 -1
  152. data/lib/archsight/web/public/vue/ResourceList-B0FLaFR3.css +0 -1
  153. data/lib/archsight/web/public/vue/ResourceList-D-66nas2.js +0 -2
  154. data/lib/archsight/web/public/vue/SearchResults-CHyqIerQ.js +0 -1
  155. data/lib/archsight/web/public/vue/WikiPage-D2svpk6B.js +0 -13
  156. data/lib/archsight/web/public/vue/WikiPage-DoFJ0dbW.css +0 -1
  157. data/lib/archsight/web/public/vue/architecture-7GRP2DOG-BfA1TPxQ.js +0 -1
  158. data/lib/archsight/web/public/vue/cynefin-OW5HDTMX-BmgdUKVD.js +0 -1
  159. data/lib/archsight/web/public/vue/eventmodeling-NTZA5JFV-DbXDvAWF.js +0 -1
  160. data/lib/archsight/web/public/vue/gitGraph-4MIJSDKK-BkVhrKS1.js +0 -1
  161. data/lib/archsight/web/public/vue/index-BXXkUK1V.js +0 -3
  162. data/lib/archsight/web/public/vue/index-Cov1SnzY.css +0 -1
  163. data/lib/archsight/web/public/vue/info-A6RAGUB7-BIlCVuUY.js +0 -1
  164. data/lib/archsight/web/public/vue/packet-AYTQ26CC-eoQTjY3j.js +0 -1
  165. data/lib/archsight/web/public/vue/pie-WAS4IAKB-DEOotGhh.js +0 -1
  166. data/lib/archsight/web/public/vue/radar-RG4KPBEZ-C2yucuUN.js +0 -1
  167. data/lib/archsight/web/public/vue/railroad-74A4TZTK-CzONK5VY.js +0 -1
  168. data/lib/archsight/web/public/vue/railroad-abnf-HS5TGJTU-9zRPsDsx.js +0 -1
  169. data/lib/archsight/web/public/vue/railroad-ebnf-LZEXJU2U-BJ4dkz9l.js +0 -1
  170. data/lib/archsight/web/public/vue/railroad-peg-WCYAUIDC-DFy_rqAj.js +0 -1
  171. data/lib/archsight/web/public/vue/treeView-Q6P3EWNA-2cq5CyD2.js +0 -1
  172. data/lib/archsight/web/public/vue/treemap-WGGIJYW6-DK-e9Ez4.js +0 -1
  173. data/lib/archsight/web/public/vue/wardley-WFR3VGLG-BQwqUqfB.js +0 -1
@@ -14,6 +14,12 @@ module Archsight
14
14
  "BusinessConstraint" => "MotivationConstraint"
15
15
  }.freeze
16
16
 
17
+ # Annotations that were renamed: the old key is rewritten to the new one when a document is loaded (see
18
+ # Database#accept_old_names) and reported as deprecated by `archsight lint`.
19
+ ANNOTATION_ALIASES = {
20
+ "page/confluence" => "link/confluence"
21
+ }.freeze
22
+
17
23
  # Relation keys (the plural kind names under `spec.<verb>`) that were renamed together with their kind
18
24
  RELATION_KEY_ALIASES = {
19
25
  "businessRequirements" => "motivationRequirements",
@@ -28,6 +34,12 @@ module Archsight
28
34
  DERIVED_VERBS = %i[mentions depicts].freeze
29
35
  DERIVED_KEY = :resources
30
36
 
37
+ # The layers a kind belongs to (Base.layer), top to bottom: why (motivation), what we want to become (strategy),
38
+ # how the change happens (implementation and migration), then the architecture it changes (business, application,
39
+ # technology). `other` holds the tool's own kinds (pages, views, imports, ...) and stays last. Everything that
40
+ # lists or orders layers uses this; the frontend repeats it in frontend/src/composables/useLayers.js.
41
+ LAYERS = %w[motivation strategy implementation business application technology other].freeze
42
+
31
43
  # The derived relations of every class, next to the ones it declares: [verb, key, kind]
32
44
  DERIVED_RELATIONS = DERIVED_VERBS.map { |verb| [verb, DERIVED_KEY, "Resource"] }.freeze
33
45
 
@@ -4,5 +4,5 @@
4
4
  # Do not edit manually.
5
5
 
6
6
  module Archsight
7
- VERSION = "0.3.3"
7
+ VERSION = "0.4.0"
8
8
  end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Archsight; end
4
+ module Archsight::Web; end
5
+ module Archsight::Web::API; end
6
+
7
+ # JSON of `POST /api/v1/kinds/Analysis/instances/:name/execute`: the result and its sections
8
+ module Archsight::Web::API::AnalysisHelpers
9
+ def build_analysis_result(result)
10
+ data = {
11
+ name: result.name,
12
+ success: result.success?,
13
+ has_findings: result.has_findings?,
14
+ duration: result.duration,
15
+ sections: result.sections.map { |s| serialize_analysis_section(s) }
16
+ }
17
+
18
+ if result.failed?
19
+ data[:error] = result.error
20
+ data[:error_backtrace] = result.error_backtrace if result.error_backtrace&.any?
21
+ end
22
+
23
+ data
24
+ end
25
+
26
+ def serialize_analysis_section(section)
27
+ case section[:type]
28
+ when :heading
29
+ { type: "heading", level: section[:level], text: section[:text] }
30
+ when :text
31
+ { type: "text", content: markdown(section[:content]) }
32
+ # the *_html fields: the values as inline markdown with [[Kind/Name]] links, next to the raw values
33
+ when :message
34
+ { type: "message", level: section[:level].to_s, message: section[:message],
35
+ message_html: analysis_html(section[:message]) }
36
+ when :table
37
+ { type: "table", headers: section[:headers], rows: section[:rows],
38
+ rows_html: section[:rows].map { |row| row.map { |cell| analysis_html(cell) } } }
39
+ when :list
40
+ { type: "list", items: section[:items], items_html: section[:items].map { |item| analysis_html(item) } }
41
+ when :code
42
+ { type: "code", lang: section[:lang], content: section[:content] }
43
+ else
44
+ { type: section[:type].to_s }
45
+ end
46
+ end
47
+
48
+ # A value of a table cell, list item or message as html: HTML in it stays text (escaped first) except a line
49
+ # break (`<br>` or a newline, the usual way to break a line in a table cell), markdown and [[Kind/Name]] links are
50
+ # rendered; a value without markup is only escaped, so large tables stay cheap
51
+ def analysis_html(value)
52
+ escaped = Rack::Utils.escape_html(value.to_s).gsub(%r{&lt;br\s*/?&gt;}i, "<br>").gsub(/\r?\n/, "<br>")
53
+ escaped.match?(/[*_`\[{]|https?:/) ? inline_markdown(escaped) : escaped
54
+ end
55
+ end
@@ -66,7 +66,8 @@ module Archsight::Web::API::JsonHelpers
66
66
  description: klass.description,
67
67
  layer: klass.layer,
68
68
  icon: klass.icon,
69
- instance_count: count
69
+ instance_count: count,
70
+ self_relations: Archsight::GraphvisRenderer.self_relations(klass).any?
70
71
  }
71
72
  end
72
73
  kinds.sort_by { |k| k[:kind] }
@@ -92,6 +93,7 @@ module Archsight::Web::API::JsonHelpers
92
93
  name: instance.name,
93
94
  metadata: { annotations: render_annotations(instance.annotations, base: Archsight::Assets.base_dir_for(instance, resources_dir: Archsight.resources_dir), kind: kind) },
94
95
  annotation_formats: Archsight::Resources[kind]&.annotation_formats(instance.annotations.keys) || {},
96
+ annotation_links: Archsight::Helpers::ResourceResolver.new(db).annotation_links(kind, instance.annotations),
95
97
  spec: serialize_spec(instance.spec),
96
98
  relations: extract_relations(instance),
97
99
  references: extract_references(instance),
@@ -193,42 +195,6 @@ module Archsight::Web::API::JsonHelpers
193
195
  end
194
196
  end
195
197
 
196
- def build_analysis_result(result)
197
- data = {
198
- name: result.name,
199
- success: result.success?,
200
- has_findings: result.has_findings?,
201
- duration: result.duration,
202
- sections: result.sections.map { |s| serialize_analysis_section(s) }
203
- }
204
-
205
- if result.failed?
206
- data[:error] = result.error
207
- data[:error_backtrace] = result.error_backtrace if result.error_backtrace&.any?
208
- end
209
-
210
- data
211
- end
212
-
213
- def serialize_analysis_section(section)
214
- case section[:type]
215
- when :heading
216
- { type: "heading", level: section[:level], text: section[:text] }
217
- when :text
218
- { type: "text", content: markdown(section[:content]) }
219
- when :message
220
- { type: "message", level: section[:level].to_s, message: section[:message] }
221
- when :table
222
- { type: "table", headers: section[:headers], rows: section[:rows] }
223
- when :list
224
- { type: "list", items: section[:items] }
225
- when :code
226
- { type: "code", lang: section[:lang], content: section[:content] }
227
- else
228
- { type: section[:type].to_s }
229
- end
230
- end
231
-
232
198
  def extract_references(instance)
233
199
  references = {}
234
200
 
@@ -254,7 +254,7 @@ paths:
254
254
  Returns one page with its metadata and its body rendered to HTML.
255
255
 
256
256
  **Metadata** comes from the frontmatter: `title` (falls back to the name), `status`, `author` and
257
- `owner` (`Name <email>`, split into `name` and `email`), `tags` and the `confluence` URL.
257
+ `owner` (`Name <email>`, split into `name` and `email`), `tags` and the `links` (name and URL, e.g. `confluence`).
258
258
 
259
259
  **`html`** is the markdown body rendered once per request:
260
260
  - GitHub-flavoured markdown (tables, fenced code, task lists).
@@ -309,7 +309,7 @@ paths:
309
309
  owner:
310
310
  name: Jane Doe
311
311
  email: jane.doe@example.com
312
- confluence: null
312
+ links: {}
313
313
  tags:
314
314
  - howto
315
315
  - wiki
@@ -1489,7 +1489,7 @@ components:
1489
1489
  description: Whether the analysis executed successfully
1490
1490
  has_findings:
1491
1491
  type: boolean
1492
- description: Whether the analysis produced findings
1492
+ description: Whether the script produced any output (tables, lists, text, ...). Output is not a problem; errors and warnings the script reports are message sections
1493
1493
  duration:
1494
1494
  type: number
1495
1495
  format: float
@@ -1869,7 +1869,7 @@ components:
1869
1869
 
1870
1870
  PageResponse:
1871
1871
  type: object
1872
- required: [name, title, status, author, owner, created, updated, properties, confluence, tags, toc, breadcrumb, html, backlinks]
1872
+ required: [name, title, status, author, owner, created, updated, properties, links, tags, toc, breadcrumb, html, backlinks]
1873
1873
  properties:
1874
1874
  name:
1875
1875
  type: string
@@ -1909,10 +1909,12 @@ components:
1909
1909
  html:
1910
1910
  type: string
1911
1911
  description: The value rendered as inline markdown (links, emphasis, `{macros}`, `[[wiki links]]`), without a paragraph around it
1912
- confluence:
1913
- type: [string, 'null']
1914
- format: uri
1915
- description: URL of the corresponding Confluence page
1912
+ links:
1913
+ type: object
1914
+ description: Links of the page to other systems, by name (`confluence`, `jira`, ...)
1915
+ additionalProperties:
1916
+ type: string
1917
+ format: uri
1916
1918
  tags:
1917
1919
  type: array
1918
1920
  items:
@@ -41,7 +41,7 @@ module Archsight::Web::API::PageHelpers
41
41
  created: annotations["page/created"],
42
42
  updated: annotations["page/updated"],
43
43
  properties: page.properties.map { |key, value| { key: key, value: value, html: inline_markdown(value, page) } },
44
- confluence: annotations["page/confluence"],
44
+ links: annotations.select { |key, _| key.start_with?("link/") }.transform_keys { |key| key.delete_prefix("link/") },
45
45
  tags: Archsight::Resources::Page.annotation_matching("page/tags")&.value_for(page) || [],
46
46
  toc: annotations["page/toc"] == "yes" ? Archsight::PageTree.toc(body) : [],
47
47
  breadcrumb: tree.breadcrumb(page),
@@ -50,13 +50,6 @@ module Archsight::Web::API::PageHelpers
50
50
  }
51
51
  end
52
52
 
53
- # A short text (a page property) rendered as inline markdown: links, emphasis, `{macros}`, [[wiki links]], without the
54
- # paragraph around it
55
- def inline_markdown(text, page)
56
- html = markdown(text.to_s, base: Archsight::Assets.base_dir_for(page, resources_dir: Archsight.resources_dir), page: page)
57
- html.strip.sub(%r{\A<p>(.*)</p>\z}m, '\1')
58
- end
59
-
60
53
  # The header already shows the title, so a leading "# Title" that repeats it is dropped
61
54
  # (Confluence exports and hand-written pages usually start with one).
62
55
  def strip_duplicate_title(body, page)
@@ -5,6 +5,7 @@ require "sinatra/extension"
5
5
  require_relative "json_helpers"
6
6
  require_relative "page_helpers"
7
7
  require_relative "requirements_helpers"
8
+ require_relative "analysis_helpers"
8
9
  require_relative "../../assets"
9
10
 
10
11
  module Archsight; end
@@ -18,6 +19,7 @@ module Archsight::Web::API::Routes
18
19
  helpers Archsight::Web::API::JsonHelpers
19
20
  helpers Archsight::Web::API::PageHelpers
20
21
  helpers Archsight::Web::API::RequirementsHelpers
22
+ helpers Archsight::Web::API::AnalysisHelpers
21
23
 
22
24
  # Rendering is CPU work, previews are typed, not pasted
23
25
  MAX_DIAGRAM_SOURCE = 100_000
@@ -67,10 +69,13 @@ module Archsight::Web::API::Routes
67
69
  klass = Archsight::Resources[kind]
68
70
  json_error("Kind '#{kind}' not found", status: 404, error_type: "NotFound") unless klass
69
71
 
70
- instance = db.instance_by_kind(kind, name)
72
+ # an alias finds the one resource that has it; the response carries the real name
73
+ instance = db.instance_by_kind(kind, name) || db.instance_by_alias(kind, name)
71
74
  json_error("Instance '#{name}' not found", status: 404, error_type: "NotFound") unless instance
72
75
 
73
- json_response(build_instance_response(kind, instance))
76
+ # graph_layers: the layers its dependency graph follows when the page opens
77
+ json_response(build_instance_response(kind, instance)
78
+ .merge(graph_layers: Archsight::GraphvisRenderer.default_graph_layers(instance.class.layer)))
74
79
  end
75
80
 
76
81
  # GET /api/v1/pages - Page tree built from PageMenu resources, page tags and filters and the home page
@@ -25,17 +25,25 @@ class Archsight::Web::Application < Sinatra::Base
25
25
  attr_accessor :db
26
26
 
27
27
  def database
28
- @database ||= Archsight::Database.new(Archsight.resources_dir).tap(&:reload!)
28
+ @database ||= Archsight::Database.new(Archsight.resources_dir).tap(&:reload!).tap { |db| load_import_handlers(db) }
29
29
  end
30
30
 
31
31
  def reload!
32
32
  start = Time.new
33
33
  puts "== Reloading ..." if database.verbose
34
34
  database.reload!
35
+ load_import_handlers(database)
35
36
  dur = (Time.new - start) * 1000
36
37
  puts format("== done %0.2f ms", dur) if database.verbose
37
38
  end
38
39
 
40
+ # The import handlers, built-in and of the resources repository (import/handlerPath), so the editor knows the
41
+ # same handler names as `archsight lint`. A file that does not load is reported, the server keeps running.
42
+ def load_import_handlers(db)
43
+ require "archsight/import"
44
+ Archsight::Import::HandlerLoader.load(db).each { |problem| warn "Import handler: #{problem}" }
45
+ end
46
+
39
47
  # Configure application for the given environment
40
48
  # @param env [Symbol] :development or :production
41
49
  # @param logging [Boolean, nil] Override default logging setting
@@ -135,6 +143,15 @@ class Archsight::Web::Application < Sinatra::Base
135
143
  settings.environment == :development
136
144
  end
137
145
 
146
+ # A short text (a page property, an analysis table cell) rendered as inline markdown: links, emphasis,
147
+ # `{macros}`, [[wiki links]], without the paragraph around it
148
+ # @param page [Archsight::Resources::Page, nil] the page the text belongs to, for its relative images and macros
149
+ def inline_markdown(text, page = nil)
150
+ base = page && Archsight::Assets.base_dir_for(page, resources_dir: Archsight.resources_dir)
151
+ html = markdown(text.to_s, base: base, page: page)
152
+ html.strip.sub(%r{\A<p>(.*)</p>\z}m, '\1')
153
+ end
154
+
138
155
  # Render markdown to HTML with optional URL resolution for repository content
139
156
  # @param data [String] Markdown content
140
157
  # @param git_url [String, nil] Git URL for resolving relative paths (e.g., for README images)
@@ -282,14 +299,33 @@ class Archsight::Web::Application < Sinatra::Base
282
299
  serve_vue
283
300
  end
284
301
 
302
+ # The resources of a kind and the relations between them (see GraphvisRenderer#create_graph_kind)
303
+ get "/kinds/:kind/dot" do
304
+ halt 404, "kind #{params["kind"]} unknown" unless Archsight::Resources[params["kind"]]
305
+
306
+ content_type "text/plain"
307
+ create_graph_kind(db, params["kind"])
308
+ end
309
+
310
+ # An alias in the URL redirects to the resource's real name (`:` stays readable, the rest is escaped)
285
311
  get "/kinds/:kind/instances/:instance" do
312
+ kind = params["kind"]
313
+ name = params["instance"]
314
+ if Archsight::Resources[kind] && !db.instance_by_kind(kind, name) && (instance = db.instance_by_alias(kind, name))
315
+ redirect "/kinds/#{kind}/instances/#{ERB::Util.url_encode(instance.name).gsub("%3A", ":")}"
316
+ end
317
+
286
318
  serve_vue
287
319
  end
288
320
 
321
+ # `depth`: steps from the instance (a positive number; missing or `all`: every step), `layers`: the layers it
322
+ # follows, comma separated (missing or none known: every layer)
289
323
  get "/kinds/:kind/instances/:instance/dot" do
290
324
  @kind = params["kind"]
291
325
  @instance = params["instance"]
326
+ depth = params["depth"].to_s.match?(/\A[1-9]\d*\z/) ? params["depth"].to_i : nil
327
+ layers = params["layers"].to_s.split(",").map(&:strip) & Archsight::Resources::LAYERS
292
328
  content_type "text/plain"
293
- create_graph_one(db, @kind, @instance)
329
+ create_graph_one(db, @kind, @instance, max_depth: depth, layers: layers.empty? ? nil : layers)
294
330
  end
295
331
  end
@@ -57,7 +57,8 @@ module Archsight
57
57
  data = build_form_metadata(kind, klass)
58
58
  data[:mode] = "edit"
59
59
  data[:name] = instance.name
60
- data[:annotations] = instance.annotations.dup
60
+ # defaults are not in the file, a save leaves them out
61
+ data[:annotations] = instance.annotations.reject { |key, _| instance.defaulted?(key) }
61
62
  data[:relations] = extract_instance_relations(instance)
62
63
  data[:path_ref] = source_ref(instance)
63
64
  data[:content_hash] = source_content_hash(instance)
@@ -0,0 +1 @@
1
+ import{F as e,I as t,K as n,N as r,T as i,W as a,et as o,i as s,it as c,k as l,tt as u}from"./index-BGlYCbi7.js";var d={class:`api-docs-page`},f={key:0,class:`pico-color-red-500`},p=s({__name:`ApiDocsPage`,setup(s){let p=i(),m=o(null),h=o(null);function g(e){return new Promise((t,n)=>{if(window.Redoc)return t();let r=document.createElement(`script`);r.src=e,r.onload=t,r.onerror=()=>n(Error(`Failed to load ReDoc`)),document.head.appendChild(r)})}return a(async()=>{try{await g(`https://cdn.redoc.ly/redoc/latest/bundles/redoc.standalone.js`),window.Redoc.init(`/api/v1/openapi.yaml`,{},m.value)}catch(e){h.value=e.message}}),(i,a)=>(n(),t(`div`,d,[r(`a`,{href:`#`,class:`back-link`,onClick:a[0]||=l(e=>u(p).back(),[`prevent`])},`← Back`),h.value?(n(),t(`div`,f,c(h.value),1)):e(``,!0),r(`div`,{ref_key:`container`,ref:m},null,512)]))}},[[`__scopeId`,`data-v-4823b89b`]]);export{p as default};
@@ -0,0 +1 @@
1
+ import{I as e,K as t,N as n,P as r,Z as i,et as a,t as o,u as s}from"./index-BGlYCbi7.js";import{t as c}from"./WikiPage-BoHNewCX.js";var l={key:1},u={__name:`DocPage`,props:{filename:String},setup(u){let d=u,f=a(null);function p(e,t){let n=new DOMParser().parseFromString(e,`text/html`).body,r=n.querySelector(`:scope > article`)||n,i=r.querySelector(`h1`),a=i?.textContent.trim()||t.split(`/`).pop().replace(/[-_]/g,` `);i?.remove();let o=[...r.querySelectorAll(`h2[id], h3[id], h4[id]`)].map(e=>({level:Number(e.tagName[1]),id:e.id,text:e.textContent.trim()})),s=[{title:`Documentation`}];return t.startsWith(`resources/`)&&s.push({title:`Resource types`}),{title:a,html:r.innerHTML,toc:o.length>=2?o:[],breadcrumb:s}}async function m(){let e=await s(d.filename);f.value=e?p(e,d.filename):{title:`Not found`,html:`<p>Documentation not found.</p>`,toc:[],breadcrumb:[{title:`Documentation`}]},o(f.value.title)}return i(()=>d.filename,m,{immediate:!0}),(i,a)=>f.value?(t(),r(c,{key:0,title:f.value.title,html:f.value.html,breadcrumb:f.value.breadcrumb,toc:f.value.toc},null,8,[`title`,`html`,`breadcrumb`,`toc`])):(t(),e(`article`,l,[...a[0]||=[n(`p`,null,`Loading...`,-1)]]))}};export{u as default};