@finos/legend-application 4.0.3 → 5.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.
Files changed (145) hide show
  1. package/lib/application/LegendApplication.d.ts +9 -1
  2. package/lib/application/LegendApplication.d.ts.map +1 -1
  3. package/lib/application/LegendApplication.js +54 -1
  4. package/lib/application/LegendApplication.js.map +1 -1
  5. package/lib/components/VirtualAssistant.d.ts.map +1 -1
  6. package/lib/components/VirtualAssistant.js +91 -11
  7. package/lib/components/VirtualAssistant.js.map +1 -1
  8. package/lib/components/{BasicValueSpecificationEditor.d.ts → shared/BasicValueSpecificationEditor.d.ts} +2 -2
  9. package/lib/components/shared/BasicValueSpecificationEditor.d.ts.map +1 -0
  10. package/lib/components/{BasicValueSpecificationEditor.js → shared/BasicValueSpecificationEditor.js} +29 -20
  11. package/lib/components/shared/BasicValueSpecificationEditor.js.map +1 -0
  12. package/lib/components/{CustomDatePicker.d.ts → shared/CustomDatePicker.d.ts} +1 -1
  13. package/lib/components/shared/CustomDatePicker.d.ts.map +1 -0
  14. package/lib/components/{CustomDatePicker.js → shared/CustomDatePicker.js} +20 -20
  15. package/lib/components/shared/CustomDatePicker.js.map +1 -0
  16. package/lib/components/{DocumentationLink.d.ts → shared/DocumentationLink.d.ts} +0 -0
  17. package/lib/components/shared/DocumentationLink.d.ts.map +1 -0
  18. package/lib/components/{DocumentationLink.js → shared/DocumentationLink.js} +1 -1
  19. package/lib/components/shared/DocumentationLink.js.map +1 -0
  20. package/lib/components/{LambdaEditor.d.ts → shared/LambdaEditor.d.ts} +1 -1
  21. package/lib/components/shared/LambdaEditor.d.ts.map +1 -0
  22. package/lib/components/{LambdaEditor.js → shared/LambdaEditor.js} +3 -3
  23. package/lib/components/shared/LambdaEditor.js.map +1 -0
  24. package/lib/components/{LambdaParameterValuesEditor.d.ts → shared/LambdaParameterValuesEditor.d.ts} +1 -1
  25. package/lib/components/shared/LambdaParameterValuesEditor.d.ts.map +1 -0
  26. package/lib/components/{LambdaParameterValuesEditor.js → shared/LambdaParameterValuesEditor.js} +2 -2
  27. package/lib/components/shared/LambdaParameterValuesEditor.js.map +1 -0
  28. package/lib/components/shared/PackageableElementOptionRenderer.d.ts +21 -0
  29. package/lib/components/shared/PackageableElementOptionRenderer.d.ts.map +1 -0
  30. package/lib/components/shared/PackageableElementOptionRenderer.js +8 -0
  31. package/lib/components/shared/PackageableElementOptionRenderer.js.map +1 -0
  32. package/lib/components/{TextInputEditor.d.ts → shared/TextInputEditor.d.ts} +1 -1
  33. package/lib/components/shared/TextInputEditor.d.ts.map +1 -0
  34. package/lib/components/{TextInputEditor.js → shared/TextInputEditor.js} +2 -2
  35. package/lib/components/shared/TextInputEditor.js.map +1 -0
  36. package/lib/components/shared/execution-plan-viewer/ExecutionPlanViewer.d.ts +28 -0
  37. package/lib/components/shared/execution-plan-viewer/ExecutionPlanViewer.d.ts.map +1 -0
  38. package/lib/components/shared/execution-plan-viewer/ExecutionPlanViewer.js +182 -0
  39. package/lib/components/shared/execution-plan-viewer/ExecutionPlanViewer.js.map +1 -0
  40. package/lib/components/shared/execution-plan-viewer/SQLExecutionNodeViewer.d.ts +31 -0
  41. package/lib/components/shared/execution-plan-viewer/SQLExecutionNodeViewer.d.ts.map +1 -0
  42. package/lib/components/shared/execution-plan-viewer/SQLExecutionNodeViewer.js +32 -0
  43. package/lib/components/shared/execution-plan-viewer/SQLExecutionNodeViewer.js.map +1 -0
  44. package/lib/const.d.ts +0 -3
  45. package/lib/const.d.ts.map +1 -1
  46. package/lib/const.js +0 -3
  47. package/lib/const.js.map +1 -1
  48. package/lib/index.css +2 -2
  49. package/lib/index.css.map +1 -1
  50. package/lib/index.d.ts +12 -9
  51. package/lib/index.d.ts.map +1 -1
  52. package/lib/index.js +13 -9
  53. package/lib/index.js.map +1 -1
  54. package/lib/stores/ApplicationEvent.d.ts +4 -2
  55. package/lib/stores/ApplicationEvent.d.ts.map +1 -1
  56. package/lib/stores/ApplicationEvent.js +4 -2
  57. package/lib/stores/ApplicationEvent.js.map +1 -1
  58. package/lib/stores/ApplicationStore.d.ts.map +1 -1
  59. package/lib/stores/ApplicationStore.js +2 -1
  60. package/lib/stores/ApplicationStore.js.map +1 -1
  61. package/lib/stores/LegendApplicationAssistantService.d.ts +7 -2
  62. package/lib/stores/LegendApplicationAssistantService.d.ts.map +1 -1
  63. package/lib/stores/LegendApplicationAssistantService.js +22 -10
  64. package/lib/stores/LegendApplicationAssistantService.js.map +1 -1
  65. package/lib/stores/LegendApplicationConfig.d.ts +8 -5
  66. package/lib/stores/LegendApplicationConfig.d.ts.map +1 -1
  67. package/lib/stores/LegendApplicationConfig.js +15 -13
  68. package/lib/stores/LegendApplicationConfig.js.map +1 -1
  69. package/lib/stores/LegendApplicationDocumentationService.d.ts +34 -24
  70. package/lib/stores/LegendApplicationDocumentationService.d.ts.map +1 -1
  71. package/lib/stores/LegendApplicationDocumentationService.js +64 -60
  72. package/lib/stores/LegendApplicationDocumentationService.js.map +1 -1
  73. package/lib/stores/LegendApplicationPlugin.d.ts +14 -3
  74. package/lib/stores/LegendApplicationPlugin.d.ts.map +1 -1
  75. package/lib/stores/LegendApplicationPlugin.js.map +1 -1
  76. package/lib/stores/PureLanguageSupport.d.ts.map +1 -1
  77. package/lib/stores/PureLanguageSupport.js +8 -3
  78. package/lib/stores/PureLanguageSupport.js.map +1 -1
  79. package/lib/stores/shared/ExecutionPlanState.d.ts +62 -0
  80. package/lib/stores/shared/ExecutionPlanState.d.ts.map +1 -0
  81. package/lib/stores/shared/ExecutionPlanState.js +118 -0
  82. package/lib/stores/shared/ExecutionPlanState.js.map +1 -0
  83. package/lib/stores/{LambdaEditorState.d.ts → shared/LambdaEditorState.d.ts} +0 -0
  84. package/lib/stores/shared/LambdaEditorState.d.ts.map +1 -0
  85. package/lib/stores/{LambdaEditorState.js → shared/LambdaEditorState.js} +0 -0
  86. package/lib/stores/shared/LambdaEditorState.js.map +1 -0
  87. package/lib/stores/{LambdaParameterState.d.ts → shared/LambdaParameterState.d.ts} +1 -3
  88. package/lib/stores/shared/LambdaParameterState.d.ts.map +1 -0
  89. package/lib/stores/{LambdaParameterState.js → shared/LambdaParameterState.js} +3 -52
  90. package/lib/stores/shared/LambdaParameterState.js.map +1 -0
  91. package/lib/stores/{PackageableElementOption.d.ts → shared/PackageableElementOption.d.ts} +0 -0
  92. package/lib/stores/shared/PackageableElementOption.d.ts.map +1 -0
  93. package/lib/stores/{PackageableElementOption.js → shared/PackageableElementOption.js} +0 -0
  94. package/lib/stores/shared/PackageableElementOption.js.map +1 -0
  95. package/lib/stores/{ValueSpecificationModifierHelper.d.ts → shared/ValueSpecificationModifierHelper.d.ts} +0 -0
  96. package/lib/stores/shared/ValueSpecificationModifierHelper.d.ts.map +1 -0
  97. package/lib/stores/{ValueSpecificationModifierHelper.js → shared/ValueSpecificationModifierHelper.js} +0 -0
  98. package/lib/stores/shared/ValueSpecificationModifierHelper.js.map +1 -0
  99. package/package.json +13 -12
  100. package/src/application/LegendApplication.tsx +100 -4
  101. package/src/components/VirtualAssistant.tsx +106 -16
  102. package/src/components/{BasicValueSpecificationEditor.tsx → shared/BasicValueSpecificationEditor.tsx} +50 -19
  103. package/src/components/{CustomDatePicker.tsx → shared/CustomDatePicker.tsx} +44 -27
  104. package/src/components/{DocumentationLink.tsx → shared/DocumentationLink.tsx} +1 -1
  105. package/src/components/{LambdaEditor.tsx → shared/LambdaEditor.tsx} +4 -4
  106. package/src/components/{LambdaParameterValuesEditor.tsx → shared/LambdaParameterValuesEditor.tsx} +5 -3
  107. package/src/components/shared/PackageableElementOptionRenderer.tsx +40 -0
  108. package/src/components/{TextInputEditor.tsx → shared/TextInputEditor.tsx} +2 -2
  109. package/src/components/shared/execution-plan-viewer/ExecutionPlanViewer.tsx +550 -0
  110. package/src/components/shared/execution-plan-viewer/SQLExecutionNodeViewer.tsx +46 -0
  111. package/src/const.ts +0 -5
  112. package/src/index.ts +16 -13
  113. package/src/stores/ApplicationEvent.ts +4 -2
  114. package/src/stores/ApplicationStore.ts +3 -1
  115. package/src/stores/LegendApplicationAssistantService.ts +30 -16
  116. package/src/stores/LegendApplicationConfig.ts +28 -27
  117. package/src/stores/LegendApplicationDocumentationService.ts +128 -124
  118. package/src/stores/LegendApplicationPlugin.ts +17 -3
  119. package/src/stores/PureLanguageSupport.ts +8 -3
  120. package/src/stores/shared/ExecutionPlanState.ts +154 -0
  121. package/src/stores/{LambdaEditorState.ts → shared/LambdaEditorState.ts} +0 -0
  122. package/src/stores/{LambdaParameterState.ts → shared/LambdaParameterState.ts} +2 -100
  123. package/src/stores/{PackageableElementOption.ts → shared/PackageableElementOption.ts} +0 -0
  124. package/src/stores/{ValueSpecificationModifierHelper.ts → shared/ValueSpecificationModifierHelper.ts} +0 -0
  125. package/tsconfig.json +14 -10
  126. package/lib/components/BasicValueSpecificationEditor.d.ts.map +0 -1
  127. package/lib/components/BasicValueSpecificationEditor.js.map +0 -1
  128. package/lib/components/CustomDatePicker.d.ts.map +0 -1
  129. package/lib/components/CustomDatePicker.js.map +0 -1
  130. package/lib/components/DocumentationLink.d.ts.map +0 -1
  131. package/lib/components/DocumentationLink.js.map +0 -1
  132. package/lib/components/LambdaEditor.d.ts.map +0 -1
  133. package/lib/components/LambdaEditor.js.map +0 -1
  134. package/lib/components/LambdaParameterValuesEditor.d.ts.map +0 -1
  135. package/lib/components/LambdaParameterValuesEditor.js.map +0 -1
  136. package/lib/components/TextInputEditor.d.ts.map +0 -1
  137. package/lib/components/TextInputEditor.js.map +0 -1
  138. package/lib/stores/LambdaEditorState.d.ts.map +0 -1
  139. package/lib/stores/LambdaEditorState.js.map +0 -1
  140. package/lib/stores/LambdaParameterState.d.ts.map +0 -1
  141. package/lib/stores/LambdaParameterState.js.map +0 -1
  142. package/lib/stores/PackageableElementOption.d.ts.map +0 -1
  143. package/lib/stores/PackageableElementOption.js.map +0 -1
  144. package/lib/stores/ValueSpecificationModifierHelper.d.ts.map +0 -1
  145. package/lib/stores/ValueSpecificationModifierHelper.js.map +0 -1
@@ -15,10 +15,7 @@
15
15
  */
16
16
 
17
17
  import { action, makeObservable, observable, computed } from 'mobx';
18
- import type {
19
- LegendApplicationContextualDocumentationEntry,
20
- LegendApplicationDocumentationEntry,
21
- } from './LegendApplicationDocumentationService.js';
18
+ import type { LegendApplicationDocumentationEntry } from './LegendApplicationDocumentationService.js';
22
19
  import type { LegendApplicationConfig } from './LegendApplicationConfig.js';
23
20
  import type { ApplicationStore } from './ApplicationStore.js';
24
21
  import { Fuse } from './CJS__Fuse.cjs';
@@ -69,13 +66,14 @@ export class VirtualAssistantContextualDocumentationEntry {
69
66
  related: VirtualAssistantDocumentationEntry[];
70
67
 
71
68
  constructor(
72
- contextualDocEntry: LegendApplicationContextualDocumentationEntry,
69
+ context: string,
70
+ docEntry: LegendApplicationDocumentationEntry,
73
71
  related: VirtualAssistantDocumentationEntry[],
74
72
  ) {
75
- this.context = contextualDocEntry._context;
76
- this.title = contextualDocEntry.title;
77
- this.content = contextualDocEntry.markdownText ?? contextualDocEntry.text;
78
- this.url = contextualDocEntry.url;
73
+ this.context = context;
74
+ this.title = docEntry.title;
75
+ this.content = docEntry.markdownText ?? docEntry.text;
76
+ this.url = docEntry.url;
79
77
  this.related = related;
80
78
  }
81
79
  }
@@ -83,6 +81,10 @@ export class VirtualAssistantContextualDocumentationEntry {
83
81
  export class LegendApplicationAssistantService {
84
82
  readonly applicationStore: ApplicationStore<LegendApplicationConfig>;
85
83
  private readonly searchEngine: Fuse<LegendApplicationDocumentationEntry>;
84
+ /**
85
+ * This key is used to allow programmatic re-rendering of the assistant panel
86
+ */
87
+ panelRenderingKey = uuid();
86
88
  isHidden = false;
87
89
  isOpen = false;
88
90
  selectedTab = VIRTUAL_ASSISTANT_TAB.SEARCH;
@@ -95,6 +97,7 @@ export class LegendApplicationAssistantService {
95
97
  makeObservable(this, {
96
98
  isHidden: observable,
97
99
  isOpen: observable,
100
+ panelRenderingKey: observable,
98
101
  selectedTab: observable,
99
102
  searchText: observable,
100
103
  searchResults: observable,
@@ -105,6 +108,7 @@ export class LegendApplicationAssistantService {
105
108
  setSearchText: action,
106
109
  resetSearch: action,
107
110
  search: action,
111
+ refreshPanelRendering: action,
108
112
  });
109
113
 
110
114
  this.applicationStore = applicationStore;
@@ -147,16 +151,20 @@ export class LegendApplicationAssistantService {
147
151
  get currentContextualDocumentationEntry():
148
152
  | VirtualAssistantContextualDocumentationEntry
149
153
  | undefined {
150
- const currentContextualDocumentationEntry = this.applicationStore
151
- .navigationContextService.currentContext
152
- ? this.applicationStore.documentationService.getContextualDocEntry(
153
- this.applicationStore.navigationContextService.currentContext.value,
154
- )
155
- : undefined;
154
+ if (!this.applicationStore.navigationContextService.currentContext) {
155
+ return undefined;
156
+ }
157
+ const currentContext =
158
+ this.applicationStore.navigationContextService.currentContext.value;
159
+ const currentContextualDocumentationEntry =
160
+ this.applicationStore.documentationService.getContextualDocEntry(
161
+ currentContext,
162
+ );
156
163
  return currentContextualDocumentationEntry
157
164
  ? new VirtualAssistantContextualDocumentationEntry(
165
+ currentContext,
158
166
  currentContextualDocumentationEntry,
159
- currentContextualDocumentationEntry.related
167
+ (currentContextualDocumentationEntry.related ?? [])
160
168
  .map((entry) =>
161
169
  this.applicationStore.documentationService.getDocEntry(entry),
162
170
  )
@@ -165,6 +173,8 @@ export class LegendApplicationAssistantService {
165
173
  (entry) =>
166
174
  // NOTE: since we're searching for user-friendly docs, we will discard anything that
167
175
  // doesn't come with a title, or does not have any content/url
176
+ //
177
+ // We could also consider having a flag in each documentation entry to be hidden from users
168
178
  entry.title && (entry.url ?? entry.text ?? entry.markdownText),
169
179
  )
170
180
  .map((entry) => new VirtualAssistantDocumentationEntry(entry)),
@@ -198,6 +208,10 @@ export class LegendApplicationAssistantService {
198
208
  this.selectedTab = val;
199
209
  }
200
210
 
211
+ refreshPanelRendering(): void {
212
+ this.panelRenderingKey = uuid();
213
+ }
214
+
201
215
  setSearchText(val: string): void {
202
216
  this.searchText = val;
203
217
  }
@@ -18,13 +18,15 @@ import {
18
18
  guaranteeNonEmptyString,
19
19
  guaranteeNonNullable,
20
20
  } from '@finos/legend-shared';
21
+ import type { LegendApplicationConfigurationInput } from '../index.js';
21
22
  import {
22
- type LegendApplicationKeyedDocumentationEntry,
23
- type LegendApplicationDocumentationEntryConfig,
24
- type LegendApplicationKeyedContextualDocumentationEntry,
25
- type LegendApplicationContextualDocumentationEntryConfig,
26
23
  collectKeyedDocumnetationEntriesFromConfig,
27
- collectKeyedContextualDocumentationEntriesFromConfig,
24
+ collectContextualDocumnetationEntry,
25
+ type LegendApplicationKeyedDocumentationEntry,
26
+ type LegendApplicationDocumentationConfigEntry,
27
+ type LegendApplicationContextualDocumentationMapConfig,
28
+ type LegendApplicationContextualDocumentationEntry,
29
+ type LegendApplicationDocumentationRegistryEntry,
28
30
  } from './LegendApplicationDocumentationService.js';
29
31
 
30
32
  export interface LegendApplicationVersionData {
@@ -38,13 +40,11 @@ export interface LegendApplicationConfigurationData {
38
40
  env: string;
39
41
  documentation?: {
40
42
  url: string;
41
- entries?: Record<string, LegendApplicationDocumentationEntryConfig>;
42
- contextualEntries?: Record<
43
- string,
44
- LegendApplicationContextualDocumentationEntryConfig
45
- >;
43
+ registry?: LegendApplicationDocumentationRegistryEntry[];
44
+ entries?: Record<string, LegendApplicationDocumentationConfigEntry>;
45
+ contextualDocMap?: LegendApplicationContextualDocumentationMapConfig;
46
46
  };
47
- // TODO: when we support vault-like settings
47
+ // TODO: when we support vault-like settings, we could support `settingOverrides`
48
48
  // See https://github.com/finos/legend-studio/issues/407
49
49
  // settingOverrides
50
50
  extensions?: Record<PropertyKey, unknown>;
@@ -57,9 +57,11 @@ export abstract class LegendApplicationConfig {
57
57
 
58
58
  // documentation
59
59
  readonly documentationUrl: string | undefined;
60
+ readonly documentationRegistryEntries: LegendApplicationDocumentationRegistryEntry[] =
61
+ [];
60
62
  readonly keyedDocumentationEntries: LegendApplicationKeyedDocumentationEntry[] =
61
63
  [];
62
- readonly keyedContextualDocumentationEntries: LegendApplicationKeyedContextualDocumentationEntry[] =
64
+ readonly contextualDocEntries: LegendApplicationContextualDocumentationEntry[] =
63
65
  [];
64
66
 
65
67
  // version
@@ -68,41 +70,40 @@ export abstract class LegendApplicationConfig {
68
70
  readonly appVersionCommitId: string;
69
71
 
70
72
  constructor(
71
- configData: LegendApplicationConfigurationData,
72
- versionData: LegendApplicationVersionData,
73
- baseUrl: string,
73
+ input: LegendApplicationConfigurationInput<LegendApplicationConfigurationData>,
74
74
  ) {
75
- this.baseUrl = baseUrl;
75
+ this.baseUrl = input.baseUrl;
76
76
  this.appName = guaranteeNonEmptyString(
77
- configData.appName,
77
+ input.configData.appName,
78
78
  `Can't configure application: 'appName' field is missing or empty`,
79
79
  );
80
80
  this.env = guaranteeNonEmptyString(
81
- configData.env,
81
+ input.configData.env,
82
82
  `Can't configure application: 'env' field is missing or empty`,
83
83
  );
84
84
 
85
85
  // Documentation
86
- this.documentationUrl = configData.documentation?.url;
86
+ this.documentationUrl = input.configData.documentation?.url;
87
+ this.documentationRegistryEntries =
88
+ input.configData.documentation?.registry ?? [];
87
89
  this.keyedDocumentationEntries = collectKeyedDocumnetationEntriesFromConfig(
88
- configData.documentation?.entries ?? {},
90
+ input.configData.documentation?.entries ?? {},
91
+ );
92
+ this.contextualDocEntries = collectContextualDocumnetationEntry(
93
+ input.configData.documentation?.contextualDocMap ?? {},
89
94
  );
90
- this.keyedContextualDocumentationEntries =
91
- collectKeyedContextualDocumentationEntriesFromConfig(
92
- configData.documentation?.contextualEntries ?? {},
93
- );
94
95
 
95
96
  // Version
96
97
  this.appVersion = guaranteeNonNullable(
97
- versionData.version,
98
+ input.versionData.version,
98
99
  `Can't collect application version: 'version' field is missing`,
99
100
  );
100
101
  this.appVersionBuildTime = guaranteeNonNullable(
101
- versionData.buildTime,
102
+ input.versionData.buildTime,
102
103
  `Can't collect application version: 'buildTime' field is missing`,
103
104
  );
104
105
  this.appVersionCommitId = guaranteeNonNullable(
105
- versionData.commitSHA,
106
+ input.versionData.commitSHA,
106
107
  `Can't collect application version: 'commitSHA' field is missing`,
107
108
  );
108
109
  }
@@ -21,7 +21,6 @@ import {
21
21
  SerializationFactory,
22
22
  LogEvent,
23
23
  uniq,
24
- guaranteeNonNullable,
25
24
  } from '@finos/legend-shared';
26
25
  import {
27
26
  createModelSchema,
@@ -34,11 +33,34 @@ import { APPLICATION_EVENT } from './ApplicationEvent.js';
34
33
  import type { ApplicationStore } from './ApplicationStore.js';
35
34
  import type { LegendApplicationConfig } from './LegendApplicationConfig.js';
36
35
 
37
- export type LegendApplicationDocumentationEntryConfig = {
36
+ export type LegendApplicationDocumentationRegistryEntry = {
37
+ url: string;
38
+ /**
39
+ * Sometimes, we don't need to expose an endpoint to get the documentation data
40
+ * we support the `simple` mode where the endpoint is really just a JSON
41
+ *
42
+ * The caveat about this mode is that the endpoint must have CORS enabled
43
+ * ideally, the CORS-enabled endpoint should be setup with "Access-Control-Allow-Origin", "*"
44
+ * (e.g. Github Pages - See https://stackoverflow.com/questions/26416727/cross-origin-resource-sharing-on-github-pages)
45
+ * With that, the network client used to fetch this request must also be simplified to not include credential
46
+ * See https://stackoverflow.com/questions/19743396/cors-cannot-use-wildcard-in-access-control-allow-origin-when-credentials-flag-i
47
+ *
48
+ * During development, an option is to use our mock-server or some sort of CORS proxies, for example `cors-anywhere`
49
+ * See https://cors-anywhere.herokuapp.com/
50
+ */
51
+ simple?: boolean | undefined;
52
+ };
53
+
54
+ export type LegendApplicationDocumentationRegistryData = {
55
+ entries: Record<string, LegendApplicationDocumentationConfigEntry>;
56
+ };
57
+
58
+ export type LegendApplicationDocumentationConfigEntry = {
38
59
  markdownText?: MarkdownText | undefined;
39
60
  title?: string | undefined;
40
61
  text?: string | undefined;
41
62
  url?: string | undefined;
63
+ related?: string[] | undefined;
42
64
  };
43
65
 
44
66
  export class LegendApplicationDocumentationEntry {
@@ -48,6 +70,7 @@ export class LegendApplicationDocumentationEntry {
48
70
  title?: string | undefined;
49
71
  text?: string | undefined;
50
72
  url?: string | undefined;
73
+ related?: string[] | undefined;
51
74
 
52
75
  static readonly serialization = new SerializationFactory(
53
76
  createModelSchema(LegendApplicationDocumentationEntry, {
@@ -55,6 +78,7 @@ export class LegendApplicationDocumentationEntry {
55
78
  (val) => val,
56
79
  (val) => (val.value ? val : undefined),
57
80
  ),
81
+ related: optional(list(primitive())),
58
82
  title: optional(primitive()),
59
83
  text: optional(primitive()),
60
84
  url: optional(primitive()),
@@ -73,100 +97,70 @@ export class LegendApplicationDocumentationEntry {
73
97
  }
74
98
  }
75
99
 
76
- export type LegendApplicationContextualDocumentationEntryConfig =
77
- LegendApplicationDocumentationEntryConfig & {
78
- related?: string[];
79
- };
80
-
81
- export class LegendApplicationContextualDocumentationEntry {
82
- readonly _context!: string;
83
-
84
- markdownText?: MarkdownText | undefined;
85
- title?: string | undefined;
86
- text?: string | undefined;
87
- url?: string | undefined;
88
- related: string[] = [];
89
-
90
- static readonly serialization = new SerializationFactory(
91
- createModelSchema(LegendApplicationContextualDocumentationEntry, {
92
- markdownText: custom(
93
- (val) => val,
94
- (val) => (val.value ? val : undefined),
95
- ),
96
- title: optional(primitive()),
97
- text: optional(primitive()),
98
- url: optional(primitive()),
99
- related: list(primitive()),
100
- }),
101
- );
102
-
103
- static create(
104
- json: PlainObject<LegendApplicationContextualDocumentationEntry>,
105
- context: string,
106
- ): LegendApplicationContextualDocumentationEntry {
107
- const entry =
108
- LegendApplicationContextualDocumentationEntry.serialization.fromJson(
109
- json,
110
- );
111
- (
112
- entry as Writable<LegendApplicationContextualDocumentationEntry>
113
- )._context = context;
114
- return entry;
115
- }
116
- }
117
-
118
- export interface LegendApplicationKeyedContextualDocumentationEntry {
119
- key: string;
120
- content: LegendApplicationContextualDocumentationEntry;
121
- }
122
-
123
- export const collectKeyedContextualDocumentationEntriesFromConfig = (
124
- rawEntries: Record<
125
- string,
126
- LegendApplicationContextualDocumentationEntryConfig
127
- >,
128
- ): LegendApplicationKeyedContextualDocumentationEntry[] =>
129
- Object.entries(rawEntries).map((entry) => ({
130
- key: entry[0],
131
- content: LegendApplicationContextualDocumentationEntry.create(
132
- entry[1],
133
- entry[0],
134
- ),
135
- }));
136
-
137
100
  export interface LegendApplicationKeyedDocumentationEntry {
138
101
  key: string;
139
102
  content: LegendApplicationDocumentationEntry;
140
103
  }
141
104
 
142
105
  export const collectKeyedDocumnetationEntriesFromConfig = (
143
- rawEntries: Record<string, LegendApplicationDocumentationEntryConfig>,
106
+ rawEntries: Record<string, LegendApplicationDocumentationConfigEntry>,
144
107
  ): LegendApplicationKeyedDocumentationEntry[] =>
145
108
  Object.entries(rawEntries).map((entry) => ({
146
109
  key: entry[0],
147
110
  content: LegendApplicationDocumentationEntry.create(entry[1], entry[0]),
148
111
  }));
149
112
 
113
+ export type LegendApplicationContextualDocumentationMapConfig = Record<
114
+ string,
115
+ string
116
+ >;
117
+ export type LegendApplicationContextualDocumentationEntry = {
118
+ context: string;
119
+ documentationKey: string;
120
+ };
121
+ export const collectContextualDocumnetationEntry = (
122
+ contextualDocMap: LegendApplicationContextualDocumentationMapConfig,
123
+ ): LegendApplicationContextualDocumentationEntry[] =>
124
+ Object.entries(contextualDocMap).map((entry) => ({
125
+ context: entry[0],
126
+ documentationKey: entry[1],
127
+ }));
128
+
150
129
  export class LegendApplicationDocumentationService {
151
130
  url?: string | undefined;
152
131
 
153
132
  private docRegistry = new Map<string, LegendApplicationDocumentationEntry>();
154
- private contextualDocRegistry = new Map<
133
+ private contextualDocMap = new Map<
155
134
  string,
156
- LegendApplicationContextualDocumentationEntry
135
+ LegendApplicationDocumentationEntry
157
136
  >();
158
137
 
159
138
  constructor(applicationStore: ApplicationStore<LegendApplicationConfig>) {
139
+ // set the main documenation site url
140
+ this.url = applicationStore.config.documentationUrl;
141
+
142
+ /**
143
+ * NOTE: the order of documentation entry overidding is (the later override the former):
144
+ * 1. Natively specified: specified in the codebase (no overriding allowed within this group of documentation entries):
145
+ * since we have extension mechanism, the order of plugins matter,
146
+ * we do not allow overriding, i.e. so the first specification for a documentation key wins
147
+ * 2. Fetched from documentation registries (no overriding allowed within this group of documentation entries):
148
+ * since we have extension mechanism and allow specifying multiple registry URLS,
149
+ * we do not allow overriding, i.e. so the first specification for a documentation key wins
150
+ * 3. Configured in application config (overiding allowed within this group)
151
+ */
152
+
153
+ // build doc registry
160
154
  applicationStore.pluginManager
161
155
  .getApplicationPlugins()
162
156
  .flatMap((plugin) => plugin.getExtraKeyedDocumentationEntries?.() ?? [])
163
157
  .forEach((entry) => {
164
- // Entries specified natively will not override each other. This is to prevent entries from extensions
165
- // overriding entries from core.
158
+ // NOTE: Entries specified natively will not override each other. This is to prevent entries from extensions
159
+ // accidentally overide entries from core.
166
160
  if (this.hasDocEntry(entry.key)) {
167
161
  applicationStore.log.warn(
168
162
  LogEvent.create(
169
- APPLICATION_EVENT.APPLICATION_DOCUMTENTION_LOAD_SKIPPED,
163
+ APPLICATION_EVENT.APPLICATION_DOCUMENTATION_LOAD_SKIPPED,
170
164
  ),
171
165
  entry.key,
172
166
  );
@@ -174,55 +168,71 @@ export class LegendApplicationDocumentationService {
174
168
  this.docRegistry.set(entry.key, entry.content);
175
169
  }
176
170
  });
171
+
177
172
  // entries from config will override entries specified natively
178
173
  applicationStore.config.keyedDocumentationEntries.forEach((entry) =>
179
174
  this.docRegistry.set(entry.key, entry.content),
180
175
  );
181
176
 
182
- // Contextual Documentation
183
- applicationStore.pluginManager
177
+ const contextualDocEntries = applicationStore.pluginManager
184
178
  .getApplicationPlugins()
185
179
  .flatMap(
186
- (plugin) =>
187
- plugin.getExtraKeyedContextualDocumentationEntries?.() ?? [],
188
- )
189
- .forEach((entry) => {
190
- // Entries specified natively will not override each other. This is to prevent entries from extensions
191
- // overriding entries from core. However, we will merge the list of related doc entries. This allows
192
- // extensions to broaden related doc entries for certain contexts
193
- if (this.hasContextualDocEntry(entry.key)) {
194
- applicationStore.log.warn(
195
- LogEvent.create(
196
- APPLICATION_EVENT.APPLICATION_CONTEXTUAL_DOCUMTENTION_LOAD_SKIPPED,
197
- ),
198
- entry.key,
199
- );
200
- const existingEntry = guaranteeNonNullable(
201
- this.getContextualDocEntry(entry.key),
202
- );
203
- existingEntry.related = uniq([
204
- ...existingEntry.related,
205
- ...entry.content.related,
206
- ]);
207
- } else {
208
- this.contextualDocRegistry.set(entry.key, entry.content);
180
+ (plugin) => plugin.getExtraContextualDocumentationEntries?.() ?? [],
181
+ );
182
+
183
+ // verify that required documentations are available
184
+ const missingDocumentationEntries: string[] = [];
185
+ uniq(
186
+ applicationStore.pluginManager
187
+ .getApplicationPlugins()
188
+ .flatMap((plugin) => plugin.getExtraRequiredDocumentationKeys?.() ?? [])
189
+ .concat(contextualDocEntries.map((entry) => entry.documentationKey)),
190
+ ).forEach((key) => {
191
+ if (!this.docRegistry.has(key)) {
192
+ missingDocumentationEntries.push(key);
193
+ }
194
+ });
195
+ if (missingDocumentationEntries.length) {
196
+ applicationStore.log.warn(
197
+ LogEvent.create(
198
+ APPLICATION_EVENT.APPLICATION_DOCUMENTATION_REQUIREMENT_CHECK_FAILURE,
199
+ ),
200
+ `Can't find corresponding documentation entry for keys:\n${missingDocumentationEntries
201
+ .map((key) => `- ${key}`)
202
+ .join('\n')}`,
203
+ );
204
+ }
205
+
206
+ // Contextual Documentation
207
+ contextualDocEntries.forEach((entry) => {
208
+ // NOTE: Entries specified natively will not override each other. This is to prevent entries from extensions
209
+ // overriding entries from core.
210
+ //
211
+ // However, it might be useful to allow extending the list of related doc entries.
212
+ // This allows extensions to broaden related doc entries for contextual docs
213
+ // If we need to support this behavior, we could create a dedicated extension method
214
+ if (this.hasContextualDocEntry(entry.context)) {
215
+ applicationStore.log.warn(
216
+ LogEvent.create(
217
+ APPLICATION_EVENT.APPLICATION_CONTEXTUAL_DOCUMENTATION_LOAD_SKIPPED,
218
+ ),
219
+ entry.context,
220
+ );
221
+ } else {
222
+ const existingDocEntry = this.getDocEntry(entry.documentationKey);
223
+ if (existingDocEntry) {
224
+ this.contextualDocMap.set(entry.context, existingDocEntry);
209
225
  }
210
- });
226
+ }
227
+ });
228
+
211
229
  // entries from config will override entries specified natively
212
- // however, we will keep merging related doc entries list
213
- applicationStore.config.keyedContextualDocumentationEntries.forEach(
214
- (entry) => {
215
- const existingEntry = this.getContextualDocEntry(entry.key);
216
- if (existingEntry) {
217
- entry.content.related = uniq([
218
- ...existingEntry.related,
219
- ...entry.content.related,
220
- ]);
221
- }
222
- this.contextualDocRegistry.set(entry.key, entry.content);
223
- },
224
- );
225
- this.url = applicationStore.config.documentationUrl;
230
+ applicationStore.config.contextualDocEntries.forEach((entry) => {
231
+ const existingDocEntry = this.getDocEntry(entry.documentationKey);
232
+ if (existingDocEntry) {
233
+ this.contextualDocMap.set(entry.context, existingDocEntry);
234
+ }
235
+ });
226
236
  }
227
237
 
228
238
  getDocEntry(key: string): LegendApplicationDocumentationEntry | undefined {
@@ -235,12 +245,12 @@ export class LegendApplicationDocumentationService {
235
245
 
236
246
  getContextualDocEntry(
237
247
  key: string,
238
- ): LegendApplicationContextualDocumentationEntry | undefined {
239
- return this.contextualDocRegistry.get(key);
248
+ ): LegendApplicationDocumentationEntry | undefined {
249
+ return this.contextualDocMap.get(key);
240
250
  }
241
251
 
242
252
  hasContextualDocEntry(key: string): boolean {
243
- return this.contextualDocRegistry.has(key);
253
+ return this.contextualDocMap.has(key);
244
254
  }
245
255
 
246
256
  getAllDocEntries(): LegendApplicationDocumentationEntry[] {
@@ -249,9 +259,9 @@ export class LegendApplicationDocumentationService {
249
259
 
250
260
  publishDocRegistry(): Record<
251
261
  string,
252
- LegendApplicationDocumentationEntryConfig
262
+ LegendApplicationDocumentationConfigEntry
253
263
  > {
254
- const result: Record<string, LegendApplicationDocumentationEntryConfig> =
264
+ const result: Record<string, LegendApplicationDocumentationConfigEntry> =
255
265
  {};
256
266
  this.docRegistry.forEach((value, key) => {
257
267
  result[key] =
@@ -260,16 +270,10 @@ export class LegendApplicationDocumentationService {
260
270
  return result;
261
271
  }
262
272
 
263
- publishContextualDocRegistry(): object {
264
- const result: Record<
265
- string,
266
- LegendApplicationContextualDocumentationEntryConfig
267
- > = {};
268
- this.contextualDocRegistry.forEach((value, key) => {
269
- result[key] =
270
- LegendApplicationContextualDocumentationEntry.serialization.toJson(
271
- value,
272
- );
273
+ publishContextualDocMap(): LegendApplicationContextualDocumentationMapConfig {
274
+ const result: LegendApplicationContextualDocumentationMapConfig = {};
275
+ this.contextualDocMap.forEach((value, key) => {
276
+ result[key] = value._documentationKey;
273
277
  });
274
278
  return result;
275
279
  }
@@ -16,18 +16,32 @@
16
16
 
17
17
  import { AbstractPlugin } from '@finos/legend-shared';
18
18
  import type {
19
- LegendApplicationKeyedContextualDocumentationEntry,
19
+ LegendApplicationContextualDocumentationEntry,
20
+ LegendApplicationDocumentationRegistryEntry,
20
21
  LegendApplicationKeyedDocumentationEntry,
21
22
  } from './LegendApplicationDocumentationService.js';
22
23
 
23
24
  export abstract class LegendApplicationPlugin extends AbstractPlugin {
25
+ /**
26
+ * Get the list of documentation registry entries from which the application can fetch
27
+ * documentation config data and load the documentation registry
28
+ */
29
+ getExtraDocumentationRegistryEntries?(): LegendApplicationDocumentationRegistryEntry[];
30
+
24
31
  /**
25
32
  * Get the list of keyed documentation entries to be registered with documentation service.
26
33
  */
27
34
  getExtraKeyedDocumentationEntries?(): LegendApplicationKeyedDocumentationEntry[];
28
35
 
29
36
  /**
30
- * Get the list of keyed contextual documentation entries to be registered with documentation service.
37
+ * Get the list of documentation keys whose corresponding documentation entry is required
38
+ * in the application. The documentation registry will be scanned for the presence of these,
39
+ * if they are not available, warnings will be issued.
40
+ */
41
+ getExtraRequiredDocumentationKeys?(): string[];
42
+
43
+ /**
44
+ * Get the list of contextual documentation entries to be registered with documentation service.
31
45
  */
32
- getExtraKeyedContextualDocumentationEntries?(): LegendApplicationKeyedContextualDocumentationEntry[];
46
+ getExtraContextualDocumentationEntries?(): LegendApplicationContextualDocumentationEntry[];
33
47
  }
@@ -137,15 +137,20 @@ const generateLanguageMonarch = (
137
137
  PURE_CONNECTION_NAME.JSON_MODEL_CONNECTION,
138
138
  PURE_CONNECTION_NAME.MODEL_CHAIN_CONNECTION,
139
139
  PURE_CONNECTION_NAME.XML_MODEL_CONNECTION,
140
- PURE_CONNECTION_NAME.FLAT_DATA_CONNECTION,
141
- PURE_CONNECTION_NAME.RELATIONAL_DATABASE_CONNECTION,
142
140
  // mapping
141
+ 'include',
143
142
  'EnumerationMapping',
144
143
  'Pure',
145
- 'Relational', // to be modularized
146
144
  'AssociationMapping',
147
145
  'XStore',
148
146
  'AggregationAware',
147
+ /**
148
+ * @modularize
149
+ * See https://github.com/finos/legend-studio/issues/65
150
+ */
151
+ PURE_CONNECTION_NAME.FLAT_DATA_CONNECTION,
152
+ PURE_CONNECTION_NAME.RELATIONAL_DATABASE_CONNECTION,
153
+ 'Relational',
149
154
  ],
150
155
 
151
156
  operators: [