@memberjunction/server 2.111.1 → 2.112.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 (250) hide show
  1. package/dist/agents/skip-agent.d.ts +4 -4
  2. package/dist/agents/skip-agent.d.ts.map +1 -1
  3. package/dist/agents/skip-agent.js +808 -951
  4. package/dist/agents/skip-agent.js.map +1 -1
  5. package/dist/agents/skip-sdk.d.ts +1 -1
  6. package/dist/agents/skip-sdk.d.ts.map +1 -1
  7. package/dist/agents/skip-sdk.js +53 -43
  8. package/dist/agents/skip-sdk.js.map +1 -1
  9. package/dist/apolloServer/index.js +1 -1
  10. package/dist/auth/AuthProviderFactory.d.ts +1 -1
  11. package/dist/auth/AuthProviderFactory.d.ts.map +1 -1
  12. package/dist/auth/AuthProviderFactory.js +1 -3
  13. package/dist/auth/AuthProviderFactory.js.map +1 -1
  14. package/dist/auth/BaseAuthProvider.d.ts +1 -1
  15. package/dist/auth/BaseAuthProvider.d.ts.map +1 -1
  16. package/dist/auth/BaseAuthProvider.js +3 -2
  17. package/dist/auth/BaseAuthProvider.js.map +1 -1
  18. package/dist/auth/IAuthProvider.d.ts +1 -1
  19. package/dist/auth/IAuthProvider.d.ts.map +1 -1
  20. package/dist/auth/exampleNewUserSubClass.d.ts.map +1 -1
  21. package/dist/auth/exampleNewUserSubClass.js +1 -1
  22. package/dist/auth/exampleNewUserSubClass.js.map +1 -1
  23. package/dist/auth/index.d.ts +1 -1
  24. package/dist/auth/index.d.ts.map +1 -1
  25. package/dist/auth/index.js +6 -6
  26. package/dist/auth/index.js.map +1 -1
  27. package/dist/auth/initializeProviders.js +1 -1
  28. package/dist/auth/initializeProviders.js.map +1 -1
  29. package/dist/auth/newUsers.d.ts +1 -1
  30. package/dist/auth/newUsers.d.ts.map +1 -1
  31. package/dist/auth/newUsers.js +7 -7
  32. package/dist/auth/newUsers.js.map +1 -1
  33. package/dist/auth/providers/Auth0Provider.d.ts +1 -1
  34. package/dist/auth/providers/Auth0Provider.d.ts.map +1 -1
  35. package/dist/auth/providers/Auth0Provider.js +1 -1
  36. package/dist/auth/providers/Auth0Provider.js.map +1 -1
  37. package/dist/auth/providers/CognitoProvider.d.ts +1 -1
  38. package/dist/auth/providers/CognitoProvider.d.ts.map +1 -1
  39. package/dist/auth/providers/CognitoProvider.js +3 -6
  40. package/dist/auth/providers/CognitoProvider.js.map +1 -1
  41. package/dist/auth/providers/GoogleProvider.d.ts +1 -1
  42. package/dist/auth/providers/GoogleProvider.d.ts.map +1 -1
  43. package/dist/auth/providers/GoogleProvider.js +1 -1
  44. package/dist/auth/providers/GoogleProvider.js.map +1 -1
  45. package/dist/auth/providers/MSALProvider.d.ts +1 -1
  46. package/dist/auth/providers/MSALProvider.d.ts.map +1 -1
  47. package/dist/auth/providers/MSALProvider.js +1 -1
  48. package/dist/auth/providers/MSALProvider.js.map +1 -1
  49. package/dist/auth/providers/OktaProvider.d.ts +1 -1
  50. package/dist/auth/providers/OktaProvider.d.ts.map +1 -1
  51. package/dist/auth/providers/OktaProvider.js +1 -1
  52. package/dist/auth/providers/OktaProvider.js.map +1 -1
  53. package/dist/config.d.ts.map +1 -1
  54. package/dist/config.js +22 -10
  55. package/dist/config.js.map +1 -1
  56. package/dist/context.d.ts +1 -1
  57. package/dist/context.d.ts.map +1 -1
  58. package/dist/context.js +9 -7
  59. package/dist/context.js.map +1 -1
  60. package/dist/entitySubclasses/entityPermissions.server.d.ts +1 -1
  61. package/dist/entitySubclasses/entityPermissions.server.d.ts.map +1 -1
  62. package/dist/entitySubclasses/entityPermissions.server.js +1 -1
  63. package/dist/entitySubclasses/entityPermissions.server.js.map +1 -1
  64. package/dist/generated/generated.d.ts +648 -648
  65. package/dist/generated/generated.d.ts.map +1 -1
  66. package/dist/generated/generated.js +2986 -1133
  67. package/dist/generated/generated.js.map +1 -1
  68. package/dist/generic/KeyInputOutputTypes.d.ts +1 -1
  69. package/dist/generic/KeyInputOutputTypes.d.ts.map +1 -1
  70. package/dist/generic/KeyInputOutputTypes.js +1 -1
  71. package/dist/generic/KeyInputOutputTypes.js.map +1 -1
  72. package/dist/generic/ResolverBase.d.ts +1 -1
  73. package/dist/generic/ResolverBase.d.ts.map +1 -1
  74. package/dist/generic/ResolverBase.js +15 -10
  75. package/dist/generic/ResolverBase.js.map +1 -1
  76. package/dist/generic/RunViewResolver.d.ts +1 -1
  77. package/dist/generic/RunViewResolver.d.ts.map +1 -1
  78. package/dist/generic/RunViewResolver.js +15 -15
  79. package/dist/generic/RunViewResolver.js.map +1 -1
  80. package/dist/index.d.ts.map +1 -1
  81. package/dist/index.js +18 -9
  82. package/dist/index.js.map +1 -1
  83. package/dist/resolvers/ActionResolver.d.ts +2 -2
  84. package/dist/resolvers/ActionResolver.d.ts.map +1 -1
  85. package/dist/resolvers/ActionResolver.js +28 -30
  86. package/dist/resolvers/ActionResolver.js.map +1 -1
  87. package/dist/resolvers/AskSkipResolver.d.ts +2 -2
  88. package/dist/resolvers/AskSkipResolver.d.ts.map +1 -1
  89. package/dist/resolvers/AskSkipResolver.js +60 -50
  90. package/dist/resolvers/AskSkipResolver.js.map +1 -1
  91. package/dist/resolvers/ComponentRegistryResolver.d.ts.map +1 -1
  92. package/dist/resolvers/ComponentRegistryResolver.js +36 -38
  93. package/dist/resolvers/ComponentRegistryResolver.js.map +1 -1
  94. package/dist/resolvers/CreateQueryResolver.d.ts +1 -1
  95. package/dist/resolvers/CreateQueryResolver.d.ts.map +1 -1
  96. package/dist/resolvers/CreateQueryResolver.js +43 -40
  97. package/dist/resolvers/CreateQueryResolver.js.map +1 -1
  98. package/dist/resolvers/DatasetResolver.d.ts.map +1 -1
  99. package/dist/resolvers/DatasetResolver.js +1 -1
  100. package/dist/resolvers/DatasetResolver.js.map +1 -1
  101. package/dist/resolvers/EntityRecordNameResolver.d.ts +1 -1
  102. package/dist/resolvers/EntityRecordNameResolver.d.ts.map +1 -1
  103. package/dist/resolvers/EntityRecordNameResolver.js +1 -1
  104. package/dist/resolvers/EntityRecordNameResolver.js.map +1 -1
  105. package/dist/resolvers/EntityResolver.d.ts.map +1 -1
  106. package/dist/resolvers/EntityResolver.js +1 -1
  107. package/dist/resolvers/EntityResolver.js.map +1 -1
  108. package/dist/resolvers/FileCategoryResolver.js +1 -1
  109. package/dist/resolvers/FileCategoryResolver.js.map +1 -1
  110. package/dist/resolvers/FileResolver.js +1 -1
  111. package/dist/resolvers/FileResolver.js.map +1 -1
  112. package/dist/resolvers/GetDataContextDataResolver.d.ts +1 -1
  113. package/dist/resolvers/GetDataContextDataResolver.d.ts.map +1 -1
  114. package/dist/resolvers/GetDataContextDataResolver.js +5 -5
  115. package/dist/resolvers/GetDataContextDataResolver.js.map +1 -1
  116. package/dist/resolvers/GetDataResolver.d.ts.map +1 -1
  117. package/dist/resolvers/GetDataResolver.js +8 -6
  118. package/dist/resolvers/GetDataResolver.js.map +1 -1
  119. package/dist/resolvers/MergeRecordsResolver.d.ts +3 -3
  120. package/dist/resolvers/MergeRecordsResolver.d.ts.map +1 -1
  121. package/dist/resolvers/MergeRecordsResolver.js +3 -3
  122. package/dist/resolvers/MergeRecordsResolver.js.map +1 -1
  123. package/dist/resolvers/PotentialDuplicateRecordResolver.d.ts +1 -1
  124. package/dist/resolvers/PotentialDuplicateRecordResolver.d.ts.map +1 -1
  125. package/dist/resolvers/PotentialDuplicateRecordResolver.js +1 -1
  126. package/dist/resolvers/PotentialDuplicateRecordResolver.js.map +1 -1
  127. package/dist/resolvers/QueryResolver.d.ts.map +1 -1
  128. package/dist/resolvers/QueryResolver.js +11 -11
  129. package/dist/resolvers/QueryResolver.js.map +1 -1
  130. package/dist/resolvers/ReportResolver.js +1 -1
  131. package/dist/resolvers/ReportResolver.js.map +1 -1
  132. package/dist/resolvers/RunAIAgentResolver.d.ts.map +1 -1
  133. package/dist/resolvers/RunAIAgentResolver.js +27 -28
  134. package/dist/resolvers/RunAIAgentResolver.js.map +1 -1
  135. package/dist/resolvers/RunAIPromptResolver.d.ts.map +1 -1
  136. package/dist/resolvers/RunAIPromptResolver.js +31 -31
  137. package/dist/resolvers/RunAIPromptResolver.js.map +1 -1
  138. package/dist/resolvers/RunTemplateResolver.d.ts.map +1 -1
  139. package/dist/resolvers/RunTemplateResolver.js +9 -9
  140. package/dist/resolvers/RunTemplateResolver.js.map +1 -1
  141. package/dist/resolvers/SqlLoggingConfigResolver.d.ts.map +1 -1
  142. package/dist/resolvers/SqlLoggingConfigResolver.js +10 -10
  143. package/dist/resolvers/SqlLoggingConfigResolver.js.map +1 -1
  144. package/dist/resolvers/SyncDataResolver.d.ts +1 -1
  145. package/dist/resolvers/SyncDataResolver.d.ts.map +1 -1
  146. package/dist/resolvers/SyncDataResolver.js +15 -14
  147. package/dist/resolvers/SyncDataResolver.js.map +1 -1
  148. package/dist/resolvers/SyncRolesUsersResolver.d.ts +1 -1
  149. package/dist/resolvers/SyncRolesUsersResolver.d.ts.map +1 -1
  150. package/dist/resolvers/SyncRolesUsersResolver.js +48 -44
  151. package/dist/resolvers/SyncRolesUsersResolver.js.map +1 -1
  152. package/dist/resolvers/TaskResolver.d.ts.map +1 -1
  153. package/dist/resolvers/TaskResolver.js +7 -7
  154. package/dist/resolvers/TaskResolver.js.map +1 -1
  155. package/dist/resolvers/TransactionGroupResolver.d.ts +1 -1
  156. package/dist/resolvers/TransactionGroupResolver.d.ts.map +1 -1
  157. package/dist/resolvers/TransactionGroupResolver.js +12 -12
  158. package/dist/resolvers/TransactionGroupResolver.js.map +1 -1
  159. package/dist/resolvers/UserFavoriteResolver.d.ts +1 -1
  160. package/dist/resolvers/UserFavoriteResolver.d.ts.map +1 -1
  161. package/dist/resolvers/UserFavoriteResolver.js +1 -1
  162. package/dist/resolvers/UserFavoriteResolver.js.map +1 -1
  163. package/dist/resolvers/UserViewResolver.d.ts.map +1 -1
  164. package/dist/resolvers/UserViewResolver.js.map +1 -1
  165. package/dist/rest/EntityCRUDHandler.d.ts +1 -1
  166. package/dist/rest/EntityCRUDHandler.d.ts.map +1 -1
  167. package/dist/rest/EntityCRUDHandler.js +14 -16
  168. package/dist/rest/EntityCRUDHandler.js.map +1 -1
  169. package/dist/rest/RESTEndpointHandler.d.ts.map +1 -1
  170. package/dist/rest/RESTEndpointHandler.js +23 -25
  171. package/dist/rest/RESTEndpointHandler.js.map +1 -1
  172. package/dist/rest/ViewOperationsHandler.d.ts +1 -1
  173. package/dist/rest/ViewOperationsHandler.d.ts.map +1 -1
  174. package/dist/rest/ViewOperationsHandler.js +17 -21
  175. package/dist/rest/ViewOperationsHandler.js.map +1 -1
  176. package/dist/scheduler/LearningCycleScheduler.d.ts.map +1 -1
  177. package/dist/scheduler/LearningCycleScheduler.js.map +1 -1
  178. package/dist/services/ScheduledJobsService.d.ts.map +1 -1
  179. package/dist/services/ScheduledJobsService.js +4 -6
  180. package/dist/services/ScheduledJobsService.js.map +1 -1
  181. package/dist/services/TaskOrchestrator.d.ts +1 -1
  182. package/dist/services/TaskOrchestrator.d.ts.map +1 -1
  183. package/dist/services/TaskOrchestrator.js +30 -30
  184. package/dist/services/TaskOrchestrator.js.map +1 -1
  185. package/dist/types.d.ts +3 -3
  186. package/dist/types.d.ts.map +1 -1
  187. package/dist/types.js +0 -1
  188. package/dist/types.js.map +1 -1
  189. package/dist/util.d.ts +1 -1
  190. package/dist/util.d.ts.map +1 -1
  191. package/dist/util.js +2 -2
  192. package/dist/util.js.map +1 -1
  193. package/package.json +36 -37
  194. package/src/agents/skip-agent.ts +1067 -1200
  195. package/src/agents/skip-sdk.ts +877 -851
  196. package/src/apolloServer/index.ts +2 -2
  197. package/src/auth/AuthProviderFactory.ts +8 -14
  198. package/src/auth/BaseAuthProvider.ts +5 -4
  199. package/src/auth/IAuthProvider.ts +2 -2
  200. package/src/auth/exampleNewUserSubClass.ts +9 -2
  201. package/src/auth/index.ts +31 -26
  202. package/src/auth/initializeProviders.ts +3 -3
  203. package/src/auth/newUsers.ts +166 -134
  204. package/src/auth/providers/Auth0Provider.ts +5 -5
  205. package/src/auth/providers/CognitoProvider.ts +7 -10
  206. package/src/auth/providers/GoogleProvider.ts +4 -5
  207. package/src/auth/providers/MSALProvider.ts +5 -5
  208. package/src/auth/providers/OktaProvider.ts +6 -7
  209. package/src/config.ts +63 -54
  210. package/src/context.ts +42 -30
  211. package/src/entitySubclasses/entityPermissions.server.ts +3 -3
  212. package/src/generated/generated.ts +48130 -39930
  213. package/src/generic/KeyInputOutputTypes.ts +3 -6
  214. package/src/generic/ResolverBase.ts +119 -78
  215. package/src/generic/RunViewResolver.ts +27 -23
  216. package/src/index.ts +66 -42
  217. package/src/resolvers/ActionResolver.ts +46 -57
  218. package/src/resolvers/AskSkipResolver.ts +607 -533
  219. package/src/resolvers/ComponentRegistryResolver.ts +547 -562
  220. package/src/resolvers/CreateQueryResolver.ts +683 -655
  221. package/src/resolvers/DatasetResolver.ts +5 -6
  222. package/src/resolvers/EntityCommunicationsResolver.ts +1 -1
  223. package/src/resolvers/EntityRecordNameResolver.ts +9 -5
  224. package/src/resolvers/EntityResolver.ts +9 -7
  225. package/src/resolvers/FileCategoryResolver.ts +2 -2
  226. package/src/resolvers/FileResolver.ts +4 -4
  227. package/src/resolvers/GetDataContextDataResolver.ts +106 -118
  228. package/src/resolvers/GetDataResolver.ts +194 -205
  229. package/src/resolvers/MergeRecordsResolver.ts +5 -5
  230. package/src/resolvers/PotentialDuplicateRecordResolver.ts +1 -1
  231. package/src/resolvers/QueryResolver.ts +95 -78
  232. package/src/resolvers/ReportResolver.ts +2 -2
  233. package/src/resolvers/RunAIAgentResolver.ts +818 -828
  234. package/src/resolvers/RunAIPromptResolver.ts +693 -709
  235. package/src/resolvers/RunTemplateResolver.ts +105 -103
  236. package/src/resolvers/SqlLoggingConfigResolver.ts +69 -72
  237. package/src/resolvers/SyncDataResolver.ts +386 -352
  238. package/src/resolvers/SyncRolesUsersResolver.ts +387 -350
  239. package/src/resolvers/TaskResolver.ts +110 -115
  240. package/src/resolvers/TransactionGroupResolver.ts +143 -138
  241. package/src/resolvers/UserFavoriteResolver.ts +17 -8
  242. package/src/resolvers/UserViewResolver.ts +17 -12
  243. package/src/rest/EntityCRUDHandler.ts +291 -268
  244. package/src/rest/RESTEndpointHandler.ts +782 -776
  245. package/src/rest/ViewOperationsHandler.ts +191 -195
  246. package/src/scheduler/LearningCycleScheduler.ts +8 -52
  247. package/src/services/ScheduledJobsService.ts +129 -132
  248. package/src/services/TaskOrchestrator.ts +792 -776
  249. package/src/types.ts +15 -9
  250. package/src/util.ts +112 -109
@@ -1,17 +1,17 @@
1
1
  import { Arg, Ctx, Field, InputType, ObjectType, Query, Mutation, Resolver } from 'type-graphql';
2
- import { UserInfo, Metadata, LogError, LogStatus } from '@memberjunction/core';
2
+ import { UserInfo, Metadata, LogError, LogStatus } from '@memberjunction/global';
3
3
  import { UserCache } from '@memberjunction/sqlserver-dataprovider';
4
4
  import { ComponentEntity, ComponentRegistryEntity, ComponentMetadataEngine } from '@memberjunction/core-entities';
5
5
  import { ComponentSpec } from '@memberjunction/interactive-component-types';
6
6
  import {
7
- ComponentRegistryClient,
8
- ComponentResponse,
9
- ComponentSearchResult,
10
- DependencyTree,
11
- RegistryError,
12
- RegistryErrorCode,
13
- ComponentFeedbackParams as SDKComponentFeedbackParams,
14
- ComponentFeedbackResponse as SDKComponentFeedbackResponse
7
+ ComponentRegistryClient,
8
+ ComponentResponse,
9
+ ComponentSearchResult,
10
+ DependencyTree,
11
+ RegistryError,
12
+ RegistryErrorCode,
13
+ ComponentFeedbackParams as SDKComponentFeedbackParams,
14
+ ComponentFeedbackResponse as SDKComponentFeedbackResponse,
15
15
  } from '@memberjunction/component-registry-client-sdk';
16
16
  import { AppContext } from '../types.js';
17
17
  import { configInfo } from '../config.js';
@@ -22,80 +22,80 @@ import { configInfo } from '../config.js';
22
22
 
23
23
  @ObjectType()
24
24
  class ComponentSpecWithHashType {
25
- @Field(() => String, { nullable: true })
26
- specification?: string; // JSON string of ComponentSpec
27
-
28
- @Field(() => String)
29
- hash: string;
30
-
31
- @Field(() => Boolean)
32
- notModified: boolean;
33
-
34
- @Field(() => String, { nullable: true })
35
- message?: string;
25
+ @Field(() => String, { nullable: true })
26
+ specification?: string; // JSON string of ComponentSpec
27
+
28
+ @Field(() => String)
29
+ hash: string;
30
+
31
+ @Field(() => Boolean)
32
+ notModified: boolean;
33
+
34
+ @Field(() => String, { nullable: true })
35
+ message?: string;
36
36
  }
37
37
 
38
38
  @InputType()
39
39
  class SearchRegistryComponentsInput {
40
- @Field({ nullable: true })
41
- registryId?: string;
40
+ @Field({ nullable: true })
41
+ registryId?: string;
42
42
 
43
- @Field({ nullable: true })
44
- namespace?: string;
43
+ @Field({ nullable: true })
44
+ namespace?: string;
45
45
 
46
- @Field({ nullable: true })
47
- query?: string;
46
+ @Field({ nullable: true })
47
+ query?: string;
48
48
 
49
- @Field({ nullable: true })
50
- type?: string;
49
+ @Field({ nullable: true })
50
+ type?: string;
51
51
 
52
- @Field(() => [String], { nullable: true })
53
- tags?: string[];
52
+ @Field(() => [String], { nullable: true })
53
+ tags?: string[];
54
54
 
55
- @Field({ nullable: true })
56
- limit?: number;
55
+ @Field({ nullable: true })
56
+ limit?: number;
57
57
 
58
- @Field({ nullable: true })
59
- offset?: number;
58
+ @Field({ nullable: true })
59
+ offset?: number;
60
60
  }
61
61
 
62
62
  @ObjectType()
63
63
  class RegistryComponentSearchResultType {
64
- @Field(() => [String])
65
- components: string[]; // Array of JSON strings of ComponentSpec
64
+ @Field(() => [String])
65
+ components: string[]; // Array of JSON strings of ComponentSpec
66
66
 
67
- @Field()
68
- total: number;
67
+ @Field()
68
+ total: number;
69
69
 
70
- @Field()
71
- offset: number;
70
+ @Field()
71
+ offset: number;
72
72
 
73
- @Field()
74
- limit: number;
73
+ @Field()
74
+ limit: number;
75
75
  }
76
76
 
77
77
  @ObjectType()
78
78
  class ComponentDependencyTreeType {
79
- @Field()
80
- componentId: string;
79
+ @Field()
80
+ componentId: string;
81
81
 
82
- @Field({ nullable: true })
83
- name?: string;
82
+ @Field({ nullable: true })
83
+ name?: string;
84
84
 
85
- @Field({ nullable: true })
86
- namespace?: string;
85
+ @Field({ nullable: true })
86
+ namespace?: string;
87
87
 
88
- @Field({ nullable: true })
89
- version?: string;
88
+ @Field({ nullable: true })
89
+ version?: string;
90
90
 
91
- @Field({ nullable: true })
92
- circular?: boolean;
91
+ @Field({ nullable: true })
92
+ circular?: boolean;
93
93
 
94
- @Field({ nullable: true })
95
- totalCount?: number;
94
+ @Field({ nullable: true })
95
+ totalCount?: number;
96
96
 
97
- @Field(() => [ComponentDependencyTreeType], { nullable: true })
98
- dependencies?: ComponentDependencyTreeType[];
97
+ @Field(() => [ComponentDependencyTreeType], { nullable: true })
98
+ dependencies?: ComponentDependencyTreeType[];
99
99
  }
100
100
 
101
101
  /**
@@ -104,38 +104,38 @@ class ComponentDependencyTreeType {
104
104
  */
105
105
  @InputType()
106
106
  class ComponentFeedbackInput {
107
- @Field()
108
- componentName: string;
107
+ @Field()
108
+ componentName: string;
109
109
 
110
- @Field()
111
- componentNamespace: string;
110
+ @Field()
111
+ componentNamespace: string;
112
112
 
113
- @Field({ nullable: true })
114
- componentVersion?: string;
113
+ @Field({ nullable: true })
114
+ componentVersion?: string;
115
115
 
116
- @Field({ nullable: true })
117
- registryName?: string;
116
+ @Field({ nullable: true })
117
+ registryName?: string;
118
118
 
119
- @Field()
120
- rating: number;
119
+ @Field()
120
+ rating: number;
121
121
 
122
- @Field({ nullable: true })
123
- feedbackType?: string;
122
+ @Field({ nullable: true })
123
+ feedbackType?: string;
124
124
 
125
- @Field({ nullable: true })
126
- comments?: string;
125
+ @Field({ nullable: true })
126
+ comments?: string;
127
127
 
128
- @Field({ nullable: true })
129
- conversationID?: string;
128
+ @Field({ nullable: true })
129
+ conversationID?: string;
130
130
 
131
- @Field({ nullable: true })
132
- conversationDetailID?: string;
131
+ @Field({ nullable: true })
132
+ conversationDetailID?: string;
133
133
 
134
- @Field({ nullable: true })
135
- reportID?: string;
134
+ @Field({ nullable: true })
135
+ reportID?: string;
136
136
 
137
- @Field({ nullable: true })
138
- dashboardID?: string;
137
+ @Field({ nullable: true })
138
+ dashboardID?: string;
139
139
  }
140
140
 
141
141
  /**
@@ -143,522 +143,507 @@ class ComponentFeedbackInput {
143
143
  */
144
144
  @ObjectType()
145
145
  class ComponentFeedbackResponse {
146
- @Field()
147
- success: boolean;
146
+ @Field()
147
+ success: boolean;
148
148
 
149
- @Field({ nullable: true })
150
- feedbackID?: string;
149
+ @Field({ nullable: true })
150
+ feedbackID?: string;
151
151
 
152
- @Field({ nullable: true })
153
- error?: string;
152
+ @Field({ nullable: true })
153
+ error?: string;
154
154
  }
155
155
 
156
156
  /**
157
157
  * Resolver for Component Registry operations
158
- *
158
+ *
159
159
  * Environment Variables for Development:
160
160
  * - REGISTRY_URI_OVERRIDE_<REGISTRY_NAME>: Override the URI for a specific registry
161
161
  * Example: REGISTRY_URI_OVERRIDE_MJ_CENTRAL=http://localhost:8080
162
162
  * Registry names are converted to uppercase with non-alphanumeric chars replaced by underscores
163
- *
163
+ *
164
164
  * - REGISTRY_API_KEY_<REGISTRY_NAME>: API key for authenticating with the registry
165
165
  * Example: REGISTRY_API_KEY_MJ_CENTRAL=your-api-key-here
166
166
  */
167
167
  @Resolver()
168
168
  export class ComponentRegistryExtendedResolver {
169
- private componentEngine = ComponentMetadataEngine.Instance;
170
-
171
- constructor() {
172
- // No longer pre-initialize clients - create on demand
173
- }
174
-
175
- /**
176
- * Get a component from a registry with optional hash for caching
177
- */
178
- @Query(() => ComponentSpecWithHashType)
179
- async GetRegistryComponent(
180
- @Arg('registryName') registryName: string,
181
- @Arg('namespace') namespace: string,
182
- @Arg('name') name: string,
183
- @Ctx() { userPayload }: AppContext,
184
- @Arg('version', { nullable: true }) version?: string,
185
- @Arg('hash', { nullable: true }) hash?: string
186
- ): Promise<ComponentSpecWithHashType> {
187
- try {
188
- // Get user from cache
189
- const user = UserCache.Instance.Users.find((u) => u.Email.trim().toLowerCase() === userPayload.email?.trim().toLowerCase());
190
- if (!user) throw new Error(`User ${userPayload.email} not found in UserCache`);
191
-
192
- // Get registry from database by name
193
- const registry = await this.getRegistryByName(registryName, user);
194
- if (!registry) {
195
- throw new Error(`Registry not found: ${registryName}`);
196
- }
197
-
198
- // Check user permissions (use registry ID for permission check)
199
- await this.checkUserAccess(user, registry.ID);
200
-
201
- // Initialize component engine
202
- await this.componentEngine.Config(false, user);
203
-
204
- // Create client on-demand for this registry
205
- const registryClient = this.createClientForRegistry(registry);
206
-
207
- // Fetch component from registry with hash support
208
- const response = await registryClient.getComponentWithHash({
209
- registry: registry.Name,
210
- namespace,
211
- name,
212
- version: version || 'latest',
213
- hash: hash,
214
- userEmail: user.Email
215
- });
216
-
217
- // If not modified (304), return response with notModified flag
218
- if (response.notModified) {
219
- LogStatus(`Component ${namespace}/${name} not modified (hash: ${response.hash})`);
220
- return {
221
- specification: undefined,
222
- hash: response.hash,
223
- notModified: true,
224
- message: response.message || 'Not modified'
225
- };
226
- }
227
-
228
- // Extract the specification from the response
229
- const component = response.specification;
230
- if (!component) {
231
- throw new Error(`Component ${namespace}/${name} returned without specification`);
232
- }
233
-
234
- // Optional: Cache in database if configured
235
- if (this.shouldCache(registry)) {
236
- await this.cacheComponent(component, registry.ID, user);
237
- }
238
-
239
- // Return the ComponentSpec as a JSON string
240
- return {
241
- specification: JSON.stringify(component),
242
- hash: response.hash,
243
- notModified: false,
244
- message: undefined
245
- };
246
- } catch (error) {
247
- if (error instanceof RegistryError) {
248
- // Log specific registry errors
249
- LogError(`Registry error [${error.code}]: ${error.message}`);
250
- if (error.code === RegistryErrorCode.COMPONENT_NOT_FOUND) {
251
- // Return an error response structure
252
- return {
253
- specification: undefined,
254
- hash: '',
255
- notModified: false,
256
- message: 'Component not found'
257
- };
258
- }
259
- }
260
- LogError(error);
261
- throw error;
169
+ private componentEngine = ComponentMetadataEngine.Instance;
170
+
171
+ constructor() {
172
+ // No longer pre-initialize clients - create on demand
173
+ }
174
+
175
+ /**
176
+ * Get a component from a registry with optional hash for caching
177
+ */
178
+ @Query(() => ComponentSpecWithHashType)
179
+ async GetRegistryComponent(
180
+ @Arg('registryName') registryName: string,
181
+ @Arg('namespace') namespace: string,
182
+ @Arg('name') name: string,
183
+ @Ctx() { userPayload }: AppContext,
184
+ @Arg('version', { nullable: true }) version?: string,
185
+ @Arg('hash', { nullable: true }) hash?: string
186
+ ): Promise<ComponentSpecWithHashType> {
187
+ try {
188
+ // Get user from cache
189
+ const user = UserCache.Instance.Users.find((u) => u.Email.trim().toLowerCase() === userPayload.email?.trim().toLowerCase());
190
+ if (!user) throw new Error(`User ${userPayload.email} not found in UserCache`);
191
+
192
+ // Get registry from database by name
193
+ const registry = await this.getRegistryByName(registryName, user);
194
+ if (!registry) {
195
+ throw new Error(`Registry not found: ${registryName}`);
196
+ }
197
+
198
+ // Check user permissions (use registry ID for permission check)
199
+ await this.checkUserAccess(user, registry.ID);
200
+
201
+ // Initialize component engine
202
+ await this.componentEngine.Config(false, user);
203
+
204
+ // Create client on-demand for this registry
205
+ const registryClient = this.createClientForRegistry(registry);
206
+
207
+ // Fetch component from registry with hash support
208
+ const response = await registryClient.getComponentWithHash({
209
+ registry: registry.Name,
210
+ namespace,
211
+ name,
212
+ version: version || 'latest',
213
+ hash: hash,
214
+ userEmail: user.Email,
215
+ });
216
+
217
+ // If not modified (304), return response with notModified flag
218
+ if (response.notModified) {
219
+ LogStatus(`Component ${namespace}/${name} not modified (hash: ${response.hash})`);
220
+ return {
221
+ specification: undefined,
222
+ hash: response.hash,
223
+ notModified: true,
224
+ message: response.message || 'Not modified',
225
+ };
226
+ }
227
+
228
+ // Extract the specification from the response
229
+ const component = response.specification;
230
+ if (!component) {
231
+ throw new Error(`Component ${namespace}/${name} returned without specification`);
232
+ }
233
+
234
+ // Optional: Cache in database if configured
235
+ if (this.shouldCache(registry)) {
236
+ await this.cacheComponent(component, registry.ID, user);
237
+ }
238
+
239
+ // Return the ComponentSpec as a JSON string
240
+ return {
241
+ specification: JSON.stringify(component),
242
+ hash: response.hash,
243
+ notModified: false,
244
+ message: undefined,
245
+ };
246
+ } catch (error) {
247
+ if (error instanceof RegistryError) {
248
+ // Log specific registry errors
249
+ LogError(`Registry error [${error.code}]: ${error.message}`);
250
+ if (error.code === RegistryErrorCode.COMPONENT_NOT_FOUND) {
251
+ // Return an error response structure
252
+ return {
253
+ specification: undefined,
254
+ hash: '',
255
+ notModified: false,
256
+ message: 'Component not found',
257
+ };
262
258
  }
259
+ }
260
+ LogError(error);
261
+ throw error;
263
262
  }
264
-
265
- /**
266
- * Search for components in registries
267
- */
268
- @Query(() => RegistryComponentSearchResultType)
269
- async SearchRegistryComponents(
270
- @Arg('params') params: SearchRegistryComponentsInput,
271
- @Ctx() { userPayload }: AppContext
272
- ): Promise<RegistryComponentSearchResultType> {
273
- try {
274
- // Get user from cache
275
- const user = UserCache.Instance.Users.find((u) => u.Email.trim().toLowerCase() === userPayload.email?.trim().toLowerCase());
276
- if (!user) throw new Error(`User ${userPayload.email} not found in UserCache`);
277
-
278
- // If a specific registry is specified, use only that one
279
- if (params.registryId) {
280
- await this.checkUserAccess(user, params.registryId);
281
-
282
- // Get registry and create client on-demand
283
- const registry = await this.getRegistry(params.registryId, user);
284
- if (!registry) {
285
- throw new Error(`Registry not found: ${params.registryId}`);
286
- }
287
-
288
- const client = this.createClientForRegistry(registry);
289
-
290
- const result = await client.searchComponents({
291
- namespace: params.namespace,
292
- query: params.query,
293
- type: params.type,
294
- tags: params.tags,
295
- limit: params.limit || 10,
296
- offset: params.offset || 0
297
- });
298
-
299
- return this.mapSearchResult(result);
300
- }
301
-
302
- // Otherwise, search across all active registries
303
- const allResults: ComponentSpec[] = [];
304
-
305
- // Get all active registries from database
306
- await this.componentEngine.Config(false, user);
307
- const activeRegistries = this.componentEngine.ComponentRegistries?.filter(
308
- r => r.Status === 'Active'
309
- ) || [];
310
-
311
- for (const registry of activeRegistries) {
312
- try {
313
- await this.checkUserAccess(user, registry.ID);
314
-
315
- const client = this.createClientForRegistry(registry);
316
- const result = await client.searchComponents({
317
- namespace: params.namespace,
318
- query: params.query,
319
- type: params.type,
320
- tags: params.tags,
321
- limit: params.limit || 10,
322
- offset: 0 // Reset offset for each registry
323
- });
324
-
325
- allResults.push(...result.components);
326
- } catch (error) {
327
- // Log but continue with other registries
328
- LogError(`Failed to search registry ${registry.Name}: ${error}`);
329
- }
330
- }
331
-
332
- // Apply pagination to combined results
333
- const offset = params.offset || 0;
334
- const limit = params.limit || 10;
335
- const paginatedResults = allResults.slice(offset, offset + limit);
336
-
337
- return {
338
- components: paginatedResults.map(spec => JSON.stringify(spec)),
339
- total: allResults.length,
340
- offset,
341
- limit
342
- };
343
- } catch (error) {
344
- LogError(error);
345
- throw error;
263
+ }
264
+
265
+ /**
266
+ * Search for components in registries
267
+ */
268
+ @Query(() => RegistryComponentSearchResultType)
269
+ async SearchRegistryComponents(
270
+ @Arg('params') params: SearchRegistryComponentsInput,
271
+ @Ctx() { userPayload }: AppContext
272
+ ): Promise<RegistryComponentSearchResultType> {
273
+ try {
274
+ // Get user from cache
275
+ const user = UserCache.Instance.Users.find((u) => u.Email.trim().toLowerCase() === userPayload.email?.trim().toLowerCase());
276
+ if (!user) throw new Error(`User ${userPayload.email} not found in UserCache`);
277
+
278
+ // If a specific registry is specified, use only that one
279
+ if (params.registryId) {
280
+ await this.checkUserAccess(user, params.registryId);
281
+
282
+ // Get registry and create client on-demand
283
+ const registry = await this.getRegistry(params.registryId, user);
284
+ if (!registry) {
285
+ throw new Error(`Registry not found: ${params.registryId}`);
346
286
  }
347
- }
348
-
349
- /**
350
- * Resolve component dependencies
351
- */
352
- @Query(() => ComponentDependencyTreeType, { nullable: true })
353
- async ResolveComponentDependencies(
354
- @Arg('registryName') registryName: string,
355
- @Arg('componentId') componentId: string,
356
- @Ctx() { userPayload }: AppContext
357
- ): Promise<ComponentDependencyTreeType | null> {
287
+
288
+ const client = this.createClientForRegistry(registry);
289
+
290
+ const result = await client.searchComponents({
291
+ namespace: params.namespace,
292
+ query: params.query,
293
+ type: params.type,
294
+ tags: params.tags,
295
+ limit: params.limit || 10,
296
+ offset: params.offset || 0,
297
+ });
298
+
299
+ return this.mapSearchResult(result);
300
+ }
301
+
302
+ // Otherwise, search across all active registries
303
+ const allResults: ComponentSpec[] = [];
304
+
305
+ // Get all active registries from database
306
+ await this.componentEngine.Config(false, user);
307
+ const activeRegistries = this.componentEngine.ComponentRegistries?.filter((r) => r.Status === 'Active') || [];
308
+
309
+ for (const registry of activeRegistries) {
358
310
  try {
359
- // Get user from cache
360
- const user = UserCache.Instance.Users.find((u) => u.Email.trim().toLowerCase() === userPayload.email?.trim().toLowerCase());
361
- if (!user) throw new Error(`User ${userPayload.email} not found in UserCache`);
362
-
363
- // Get registry to find its ID for permission check
364
- const registry = await this.getRegistryByName(registryName, user);
365
- if (!registry) {
366
- throw new Error(`Registry not found: ${registryName}`);
367
- }
368
-
369
- await this.checkUserAccess(user, registry.ID);
370
-
371
- // Create client on-demand
372
- const client = this.createClientForRegistry(registry);
373
-
374
- const tree = await client.resolveDependencies(componentId);
375
- return tree as ComponentDependencyTreeType;
311
+ await this.checkUserAccess(user, registry.ID);
312
+
313
+ const client = this.createClientForRegistry(registry);
314
+ const result = await client.searchComponents({
315
+ namespace: params.namespace,
316
+ query: params.query,
317
+ type: params.type,
318
+ tags: params.tags,
319
+ limit: params.limit || 10,
320
+ offset: 0, // Reset offset for each registry
321
+ });
322
+
323
+ allResults.push(...result.components);
376
324
  } catch (error) {
377
- LogError(error);
378
- throw error;
325
+ // Log but continue with other registries
326
+ LogError(`Failed to search registry ${registry.Name}: ${error}`);
379
327
  }
328
+ }
329
+
330
+ // Apply pagination to combined results
331
+ const offset = params.offset || 0;
332
+ const limit = params.limit || 10;
333
+ const paginatedResults = allResults.slice(offset, offset + limit);
334
+
335
+ return {
336
+ components: paginatedResults.map((spec) => JSON.stringify(spec)),
337
+ total: allResults.length,
338
+ offset,
339
+ limit,
340
+ };
341
+ } catch (error) {
342
+ LogError(error);
343
+ throw error;
380
344
  }
381
-
382
- /**
383
- * Check if user has access to a registry
384
- */
385
- private async checkUserAccess(userInfo: UserInfo | undefined, registryId: string): Promise<void> {
386
- // TODO: Implement actual permission checking
387
- // For now, just ensure user is authenticated
388
- if (!userInfo) {
389
- throw new Error('User must be authenticated to access component registries');
390
- }
345
+ }
346
+
347
+ /**
348
+ * Resolve component dependencies
349
+ */
350
+ @Query(() => ComponentDependencyTreeType, { nullable: true })
351
+ async ResolveComponentDependencies(
352
+ @Arg('registryName') registryName: string,
353
+ @Arg('componentId') componentId: string,
354
+ @Ctx() { userPayload }: AppContext
355
+ ): Promise<ComponentDependencyTreeType | null> {
356
+ try {
357
+ // Get user from cache
358
+ const user = UserCache.Instance.Users.find((u) => u.Email.trim().toLowerCase() === userPayload.email?.trim().toLowerCase());
359
+ if (!user) throw new Error(`User ${userPayload.email} not found in UserCache`);
360
+
361
+ // Get registry to find its ID for permission check
362
+ const registry = await this.getRegistryByName(registryName, user);
363
+ if (!registry) {
364
+ throw new Error(`Registry not found: ${registryName}`);
365
+ }
366
+
367
+ await this.checkUserAccess(user, registry.ID);
368
+
369
+ // Create client on-demand
370
+ const client = this.createClientForRegistry(registry);
371
+
372
+ const tree = await client.resolveDependencies(componentId);
373
+ return tree as ComponentDependencyTreeType;
374
+ } catch (error) {
375
+ LogError(error);
376
+ throw error;
391
377
  }
392
-
393
- /**
394
- * Get registry entity from database by ID
395
- */
396
- private async getRegistry(registryId: string, userInfo: UserInfo): Promise<ComponentRegistryEntity | null> {
397
- try {
398
- await this.componentEngine.Config(false, userInfo);
399
-
400
- const registry = this.componentEngine.ComponentRegistries?.find(
401
- r => r.ID === registryId
402
- );
403
-
404
- return registry || null;
405
- } catch (error) {
406
- LogError(error);
407
- return null;
408
- }
409
- }
410
-
411
- /**
412
- * Get registry entity from database by Name
413
- */
414
- private async getRegistryByName(registryName: string, userInfo: UserInfo): Promise<ComponentRegistryEntity | null> {
415
- try {
416
- await this.componentEngine.Config(false, userInfo);
417
-
418
- const registry = this.componentEngine.ComponentRegistries?.find(
419
- r => r.Name === registryName && r.Status === 'Active'
420
- );
421
-
422
- return registry || null;
423
- } catch (error) {
424
- LogError(error);
425
- return null;
426
- }
378
+ }
379
+
380
+ /**
381
+ * Check if user has access to a registry
382
+ */
383
+ private async checkUserAccess(userInfo: UserInfo | undefined, registryId: string): Promise<void> {
384
+ // TODO: Implement actual permission checking
385
+ // For now, just ensure user is authenticated
386
+ if (!userInfo) {
387
+ throw new Error('User must be authenticated to access component registries');
427
388
  }
428
-
429
- /**
430
- * Get the registry URI, checking for environment variable override first
431
- * Environment variable format: REGISTRY_URI_OVERRIDE_<REGISTRY_NAME>
432
- * Example: REGISTRY_URI_OVERRIDE_MJ_CENTRAL=http://localhost:8080
433
- */
434
- private getRegistryUri(registry: ComponentRegistryEntity): string {
435
- if (!registry.Name) {
436
- return registry.URI || '';
437
- }
438
-
439
- // Convert registry name to environment variable format
440
- // Replace spaces, hyphens, and other non-alphanumeric chars with underscores
441
- const envVarName = `REGISTRY_URI_OVERRIDE_${registry.Name.replace(/[^A-Za-z0-9]/g, '_').toUpperCase()}`;
442
-
443
- // Check for environment variable override
444
- const override = process.env[envVarName];
445
- if (override) {
446
- LogStatus(`Using URI override for registry ${registry.Name}: ${override}`);
447
- return override;
448
- }
449
-
450
- // Use production URI from database
451
- return registry.URI || '';
389
+ }
390
+
391
+ /**
392
+ * Get registry entity from database by ID
393
+ */
394
+ private async getRegistry(registryId: string, userInfo: UserInfo): Promise<ComponentRegistryEntity | null> {
395
+ try {
396
+ await this.componentEngine.Config(false, userInfo);
397
+
398
+ const registry = this.componentEngine.ComponentRegistries?.find((r) => r.ID === registryId);
399
+
400
+ return registry || null;
401
+ } catch (error) {
402
+ LogError(error);
403
+ return null;
452
404
  }
405
+ }
453
406
 
454
- /**
455
- * Create a client for a registry on-demand
456
- * Checks configuration first, then falls back to default settings
457
- */
458
- private createClientForRegistry(registry: ComponentRegistryEntity): ComponentRegistryClient {
459
- // Check if there's a configuration for this registry
460
- const config = configInfo.componentRegistries?.find(r =>
461
- r.id === registry.ID || r.name === registry.Name
462
- );
463
-
464
- // Get API key from environment or config
465
- const apiKey = process.env[`REGISTRY_API_KEY_${registry.ID.replace(/-/g, '_').toUpperCase()}`] ||
466
- process.env[`REGISTRY_API_KEY_${registry.Name?.replace(/-/g, '_').toUpperCase()}`] ||
467
- config?.apiKey;
468
-
469
- // Get the registry URI (with possible override)
470
- const baseUrl = this.getRegistryUri(registry);
471
-
472
- // Build retry policy with defaults
473
- const retryPolicy = {
474
- maxRetries: config?.retryPolicy?.maxRetries ?? 3,
475
- initialDelay: config?.retryPolicy?.initialDelay ?? 1000,
476
- maxDelay: config?.retryPolicy?.maxDelay ?? 10000,
477
- backoffMultiplier: config?.retryPolicy?.backoffMultiplier ?? 2
478
- };
479
-
480
- // Use config settings if available, otherwise defaults
481
- return new ComponentRegistryClient({
482
- baseUrl: baseUrl,
483
- apiKey: apiKey,
484
- timeout: config?.timeout || 30000,
485
- retryPolicy: retryPolicy,
486
- headers: config?.headers
487
- });
407
+ /**
408
+ * Get registry entity from database by Name
409
+ */
410
+ private async getRegistryByName(registryName: string, userInfo: UserInfo): Promise<ComponentRegistryEntity | null> {
411
+ try {
412
+ await this.componentEngine.Config(false, userInfo);
413
+
414
+ const registry = this.componentEngine.ComponentRegistries?.find((r) => r.Name === registryName && r.Status === 'Active');
415
+
416
+ return registry || null;
417
+ } catch (error) {
418
+ LogError(error);
419
+ return null;
488
420
  }
489
-
490
- /**
491
- * Check if component should be cached
492
- */
493
- private shouldCache(registry: ComponentRegistryEntity): boolean {
494
- // Check config for caching settings
495
- const config = configInfo.componentRegistries?.find(r =>
496
- r.id === registry.ID || r.name === registry.Name
497
- );
498
- return config?.cache !== false; // Cache by default
421
+ }
422
+
423
+ /**
424
+ * Get the registry URI, checking for environment variable override first
425
+ * Environment variable format: REGISTRY_URI_OVERRIDE_<REGISTRY_NAME>
426
+ * Example: REGISTRY_URI_OVERRIDE_MJ_CENTRAL=http://localhost:8080
427
+ */
428
+ private getRegistryUri(registry: ComponentRegistryEntity): string {
429
+ if (!registry.Name) {
430
+ return registry.URI || '';
499
431
  }
500
-
501
- /**
502
- * Cache component in database
503
- */
504
- private async cacheComponent(
505
- component: ComponentSpec,
506
- registryId: string,
507
- userInfo: UserInfo
508
- ): Promise<void> {
509
- try {
510
- // Find or create component entity
511
- const md = new Metadata();
512
- const componentEntity = await md.GetEntityObject<ComponentEntity>('MJ: Components', userInfo);
513
-
514
- // Check if component already exists
515
- const existingComponent = this.componentEngine.Components?.find(
516
- c => c.Name === component.name &&
517
- c.Namespace === component.namespace &&
518
- c.SourceRegistryID === registryId
519
- );
520
-
521
- if (existingComponent) {
522
- // Update existing component
523
- if (!await componentEntity.Load(existingComponent.ID)) {
524
- throw new Error(`Failed to load component: ${existingComponent.ID}`);
525
- }
526
- } else {
527
- // Create new component
528
- componentEntity.NewRecord();
529
- componentEntity.SourceRegistryID = registryId;
530
- }
531
-
532
- // Update component fields
533
- componentEntity.Name = component.name;
534
- componentEntity.Namespace = component.namespace || '';
535
- componentEntity.Version = component.version || '1.0.0';
536
- componentEntity.Title = component.title;
537
- componentEntity.Description = component.description;
538
- componentEntity.Type = this.mapComponentType(component.type);
539
- componentEntity.FunctionalRequirements = component.functionalRequirements;
540
- componentEntity.TechnicalDesign = component.technicalDesign;
541
- componentEntity.Specification = JSON.stringify(component);
542
- componentEntity.LastSyncedAt = new Date();
543
-
544
- if (!existingComponent) {
545
- componentEntity.ReplicatedAt = new Date();
546
- }
547
-
548
- // Save component
549
- const result = await componentEntity.Save();
550
- if (!result) {
551
- throw new Error(`Failed to cache component: ${component.name}`);
552
- }
553
-
554
- // Refresh metadata cache
555
- await this.componentEngine.Config(true, userInfo);
556
- } catch (error) {
557
- // Log but don't throw - caching failure shouldn't break the operation
558
- LogError('Failed to cache component:');
432
+
433
+ // Convert registry name to environment variable format
434
+ // Replace spaces, hyphens, and other non-alphanumeric chars with underscores
435
+ const envVarName = `REGISTRY_URI_OVERRIDE_${registry.Name.replace(/[^A-Za-z0-9]/g, '_').toUpperCase()}`;
436
+
437
+ // Check for environment variable override
438
+ const override = process.env[envVarName];
439
+ if (override) {
440
+ LogStatus(`Using URI override for registry ${registry.Name}: ${override}`);
441
+ return override;
442
+ }
443
+
444
+ // Use production URI from database
445
+ return registry.URI || '';
446
+ }
447
+
448
+ /**
449
+ * Create a client for a registry on-demand
450
+ * Checks configuration first, then falls back to default settings
451
+ */
452
+ private createClientForRegistry(registry: ComponentRegistryEntity): ComponentRegistryClient {
453
+ // Check if there's a configuration for this registry
454
+ const config = configInfo.componentRegistries?.find((r) => r.id === registry.ID || r.name === registry.Name);
455
+
456
+ // Get API key from environment or config
457
+ const apiKey =
458
+ process.env[`REGISTRY_API_KEY_${registry.ID.replace(/-/g, '_').toUpperCase()}`] ||
459
+ process.env[`REGISTRY_API_KEY_${registry.Name?.replace(/-/g, '_').toUpperCase()}`] ||
460
+ config?.apiKey;
461
+
462
+ // Get the registry URI (with possible override)
463
+ const baseUrl = this.getRegistryUri(registry);
464
+
465
+ // Build retry policy with defaults
466
+ const retryPolicy = {
467
+ maxRetries: config?.retryPolicy?.maxRetries ?? 3,
468
+ initialDelay: config?.retryPolicy?.initialDelay ?? 1000,
469
+ maxDelay: config?.retryPolicy?.maxDelay ?? 10000,
470
+ backoffMultiplier: config?.retryPolicy?.backoffMultiplier ?? 2,
471
+ };
472
+
473
+ // Use config settings if available, otherwise defaults
474
+ return new ComponentRegistryClient({
475
+ baseUrl: baseUrl,
476
+ apiKey: apiKey,
477
+ timeout: config?.timeout || 30000,
478
+ retryPolicy: retryPolicy,
479
+ headers: config?.headers,
480
+ });
481
+ }
482
+
483
+ /**
484
+ * Check if component should be cached
485
+ */
486
+ private shouldCache(registry: ComponentRegistryEntity): boolean {
487
+ // Check config for caching settings
488
+ const config = configInfo.componentRegistries?.find((r) => r.id === registry.ID || r.name === registry.Name);
489
+ return config?.cache !== false; // Cache by default
490
+ }
491
+
492
+ /**
493
+ * Cache component in database
494
+ */
495
+ private async cacheComponent(component: ComponentSpec, registryId: string, userInfo: UserInfo): Promise<void> {
496
+ try {
497
+ // Find or create component entity
498
+ const md = new Metadata();
499
+ const componentEntity = await md.GetEntityObject<ComponentEntity>('MJ: Components', userInfo);
500
+
501
+ // Check if component already exists
502
+ const existingComponent = this.componentEngine.Components?.find(
503
+ (c) => c.Name === component.name && c.Namespace === component.namespace && c.SourceRegistryID === registryId
504
+ );
505
+
506
+ if (existingComponent) {
507
+ // Update existing component
508
+ if (!(await componentEntity.Load(existingComponent.ID))) {
509
+ throw new Error(`Failed to load component: ${existingComponent.ID}`);
559
510
  }
511
+ } else {
512
+ // Create new component
513
+ componentEntity.NewRecord();
514
+ componentEntity.SourceRegistryID = registryId;
515
+ }
516
+
517
+ // Update component fields
518
+ componentEntity.Name = component.name;
519
+ componentEntity.Namespace = component.namespace || '';
520
+ componentEntity.Version = component.version || '1.0.0';
521
+ componentEntity.Title = component.title;
522
+ componentEntity.Description = component.description;
523
+ componentEntity.Type = this.mapComponentType(component.type);
524
+ componentEntity.FunctionalRequirements = component.functionalRequirements;
525
+ componentEntity.TechnicalDesign = component.technicalDesign;
526
+ componentEntity.Specification = JSON.stringify(component);
527
+ componentEntity.LastSyncedAt = new Date();
528
+
529
+ if (!existingComponent) {
530
+ componentEntity.ReplicatedAt = new Date();
531
+ }
532
+
533
+ // Save component
534
+ const result = await componentEntity.Save();
535
+ if (!result) {
536
+ throw new Error(`Failed to cache component: ${component.name}`);
537
+ }
538
+
539
+ // Refresh metadata cache
540
+ await this.componentEngine.Config(true, userInfo);
541
+ } catch (error) {
542
+ // Log but don't throw - caching failure shouldn't break the operation
543
+ LogError('Failed to cache component:');
560
544
  }
561
-
562
- /**
563
- * Map component type string to entity enum
564
- */
565
- private mapComponentType(type: string): ComponentEntity['Type'] {
566
- const typeMap: Record<string, ComponentEntity['Type']> = {
567
- 'report': 'Report',
568
- 'dashboard': 'Dashboard',
569
- 'form': 'Form',
570
- 'table': 'Table',
571
- 'chart': 'Chart',
572
- 'navigation': 'Navigation',
573
- 'search': 'Search',
574
- 'widget': 'Widget',
575
- 'utility': 'Utility',
576
- 'other': 'Other'
545
+ }
546
+
547
+ /**
548
+ * Map component type string to entity enum
549
+ */
550
+ private mapComponentType(type: string): ComponentEntity['Type'] {
551
+ const typeMap: Record<string, ComponentEntity['Type']> = {
552
+ report: 'Report',
553
+ dashboard: 'Dashboard',
554
+ form: 'Form',
555
+ table: 'Table',
556
+ chart: 'Chart',
557
+ navigation: 'Navigation',
558
+ search: 'Search',
559
+ widget: 'Widget',
560
+ utility: 'Utility',
561
+ other: 'Other',
562
+ };
563
+
564
+ return typeMap[type.toLowerCase()] || 'Other';
565
+ }
566
+
567
+ /**
568
+ * Map search result to GraphQL type
569
+ */
570
+ private mapSearchResult(result: ComponentSearchResult): RegistryComponentSearchResultType {
571
+ return {
572
+ components: result.components.map((spec) => JSON.stringify(spec)),
573
+ total: result.total,
574
+ offset: result.offset,
575
+ limit: result.limit,
576
+ };
577
+ }
578
+
579
+ /**
580
+ * Send feedback for a component from any registry
581
+ * This is a registry-agnostic mutation that allows feedback collection
582
+ * for components from any source registry (Skip, MJ Central, etc.)
583
+ */
584
+ @Mutation(() => ComponentFeedbackResponse)
585
+ async SendComponentFeedback(
586
+ @Arg('feedback') feedback: ComponentFeedbackInput,
587
+ @Ctx() { userPayload }: AppContext
588
+ ): Promise<ComponentFeedbackResponse> {
589
+ try {
590
+ // Get user from cache
591
+ const user = UserCache.Instance.Users.find((u) => u.Email.trim().toLowerCase() === userPayload.email?.trim().toLowerCase());
592
+ if (!user) {
593
+ return {
594
+ success: false,
595
+ error: `User ${userPayload.email} not found in UserCache`,
577
596
  };
578
-
579
- return typeMap[type.toLowerCase()] || 'Other';
580
- }
581
-
582
- /**
583
- * Map search result to GraphQL type
584
- */
585
- private mapSearchResult(result: ComponentSearchResult): RegistryComponentSearchResultType {
597
+ }
598
+
599
+ // Registry name is required for feedback submission
600
+ if (!feedback.registryName) {
586
601
  return {
587
- components: result.components.map(spec => JSON.stringify(spec)),
588
- total: result.total,
589
- offset: result.offset,
590
- limit: result.limit
602
+ success: false,
603
+ error: 'Registry name is required for feedback submission',
591
604
  };
592
- }
605
+ }
593
606
 
594
- /**
595
- * Send feedback for a component from any registry
596
- * This is a registry-agnostic mutation that allows feedback collection
597
- * for components from any source registry (Skip, MJ Central, etc.)
598
- */
599
- @Mutation(() => ComponentFeedbackResponse)
600
- async SendComponentFeedback(
601
- @Arg('feedback') feedback: ComponentFeedbackInput,
602
- @Ctx() { userPayload }: AppContext
603
- ): Promise<ComponentFeedbackResponse> {
604
- try {
605
- // Get user from cache
606
- const user = UserCache.Instance.Users.find((u) => u.Email.trim().toLowerCase() === userPayload.email?.trim().toLowerCase());
607
- if (!user) {
608
- return {
609
- success: false,
610
- error: `User ${userPayload.email} not found in UserCache`
611
- };
612
- }
613
-
614
- // Registry name is required for feedback submission
615
- if (!feedback.registryName) {
616
- return {
617
- success: false,
618
- error: 'Registry name is required for feedback submission'
619
- };
620
- }
621
-
622
- // Get registry configuration
623
- const registry = await this.getRegistryByName(feedback.registryName, user);
624
- if (!registry) {
625
- return {
626
- success: false,
627
- error: `Registry not found: ${feedback.registryName}`
628
- };
629
- }
630
-
631
- // Check user permissions
632
- await this.checkUserAccess(user, registry.ID);
633
-
634
- // Create client using the same pattern as GetRegistryComponent
635
- // This respects REGISTRY_URI_OVERRIDE_* and REGISTRY_API_KEY_* environment variables
636
- const registryClient = this.createClientForRegistry(registry);
637
-
638
- const sdkParams: SDKComponentFeedbackParams = {
639
- componentName: feedback.componentName,
640
- componentNamespace: feedback.componentNamespace,
641
- componentVersion: feedback.componentVersion,
642
- registryName: feedback.registryName,
643
- rating: feedback.rating,
644
- feedbackType: feedback.feedbackType,
645
- comments: feedback.comments,
646
- conversationID: feedback.conversationID,
647
- conversationDetailID: feedback.conversationDetailID,
648
- reportID: feedback.reportID,
649
- dashboardID: feedback.dashboardID,
650
- userEmail: user.Email // Pass the authenticated user's email to the registry
651
- };
652
-
653
- const result = await registryClient.submitFeedback(sdkParams);
654
-
655
- return result;
656
- } catch (error) {
657
- LogError(error);
658
- return {
659
- success: false,
660
- error: error instanceof Error ? error.message : 'Unknown error'
661
- };
662
- }
607
+ // Get registry configuration
608
+ const registry = await this.getRegistryByName(feedback.registryName, user);
609
+ if (!registry) {
610
+ return {
611
+ success: false,
612
+ error: `Registry not found: ${feedback.registryName}`,
613
+ };
614
+ }
615
+
616
+ // Check user permissions
617
+ await this.checkUserAccess(user, registry.ID);
618
+
619
+ // Create client using the same pattern as GetRegistryComponent
620
+ // This respects REGISTRY_URI_OVERRIDE_* and REGISTRY_API_KEY_* environment variables
621
+ const registryClient = this.createClientForRegistry(registry);
622
+
623
+ const sdkParams: SDKComponentFeedbackParams = {
624
+ componentName: feedback.componentName,
625
+ componentNamespace: feedback.componentNamespace,
626
+ componentVersion: feedback.componentVersion,
627
+ registryName: feedback.registryName,
628
+ rating: feedback.rating,
629
+ feedbackType: feedback.feedbackType,
630
+ comments: feedback.comments,
631
+ conversationID: feedback.conversationID,
632
+ conversationDetailID: feedback.conversationDetailID,
633
+ reportID: feedback.reportID,
634
+ dashboardID: feedback.dashboardID,
635
+ userEmail: user.Email, // Pass the authenticated user's email to the registry
636
+ };
637
+
638
+ const result = await registryClient.submitFeedback(sdkParams);
639
+
640
+ return result;
641
+ } catch (error) {
642
+ LogError(error);
643
+ return {
644
+ success: false,
645
+ error: error instanceof Error ? error.message : 'Unknown error',
646
+ };
663
647
  }
664
- }
648
+ }
649
+ }