@pax8-cta/core 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (226) hide show
  1. package/LICENSE +198 -0
  2. package/dist/auth/device-code-login.d.ts +40 -0
  3. package/dist/auth/device-code-login.d.ts.map +1 -0
  4. package/dist/auth/device-code-login.js +59 -0
  5. package/dist/auth/device-code-login.js.map +1 -0
  6. package/dist/auth/gdap-client.d.ts +81 -0
  7. package/dist/auth/gdap-client.d.ts.map +1 -0
  8. package/dist/auth/gdap-client.js +128 -0
  9. package/dist/auth/gdap-client.js.map +1 -0
  10. package/dist/auth/index.d.ts +19 -0
  11. package/dist/auth/index.d.ts.map +1 -0
  12. package/dist/auth/index.js +19 -0
  13. package/dist/auth/index.js.map +1 -0
  14. package/dist/auth/token-manager.d.ts +54 -0
  15. package/dist/auth/token-manager.d.ts.map +1 -0
  16. package/dist/auth/token-manager.js +150 -0
  17. package/dist/auth/token-manager.js.map +1 -0
  18. package/dist/client.d.ts +27 -0
  19. package/dist/client.d.ts.map +1 -0
  20. package/dist/client.js +27 -0
  21. package/dist/client.js.map +1 -0
  22. package/dist/config/client.d.ts +24 -0
  23. package/dist/config/client.d.ts.map +1 -0
  24. package/dist/config/client.js +18 -0
  25. package/dist/config/client.js.map +1 -0
  26. package/dist/config/index.d.ts +18 -0
  27. package/dist/config/index.d.ts.map +1 -0
  28. package/dist/config/index.js +18 -0
  29. package/dist/config/index.js.map +1 -0
  30. package/dist/config/loader.d.ts +81 -0
  31. package/dist/config/loader.d.ts.map +1 -0
  32. package/dist/config/loader.js +271 -0
  33. package/dist/config/loader.js.map +1 -0
  34. package/dist/config/schema.d.ts +751 -0
  35. package/dist/config/schema.d.ts.map +1 -0
  36. package/dist/config/schema.js +556 -0
  37. package/dist/config/schema.js.map +1 -0
  38. package/dist/constants.d.ts +116 -0
  39. package/dist/constants.d.ts.map +1 -0
  40. package/dist/constants.js +170 -0
  41. package/dist/constants.js.map +1 -0
  42. package/dist/dataverse/agent-resolver.d.ts +98 -0
  43. package/dist/dataverse/agent-resolver.d.ts.map +1 -0
  44. package/dist/dataverse/agent-resolver.js +185 -0
  45. package/dist/dataverse/agent-resolver.js.map +1 -0
  46. package/dist/dataverse/client.d.ts +104 -0
  47. package/dist/dataverse/client.d.ts.map +1 -0
  48. package/dist/dataverse/client.js +272 -0
  49. package/dist/dataverse/client.js.map +1 -0
  50. package/dist/dataverse/connection-refs.d.ts +115 -0
  51. package/dist/dataverse/connection-refs.d.ts.map +1 -0
  52. package/dist/dataverse/connection-refs.js +203 -0
  53. package/dist/dataverse/connection-refs.js.map +1 -0
  54. package/dist/dataverse/index.d.ts +20 -0
  55. package/dist/dataverse/index.d.ts.map +1 -0
  56. package/dist/dataverse/index.js +20 -0
  57. package/dist/dataverse/index.js.map +1 -0
  58. package/dist/dataverse/solution-ops.d.ts +100 -0
  59. package/dist/dataverse/solution-ops.d.ts.map +1 -0
  60. package/dist/dataverse/solution-ops.js +288 -0
  61. package/dist/dataverse/solution-ops.js.map +1 -0
  62. package/dist/errors.d.ts +171 -0
  63. package/dist/errors.d.ts.map +1 -0
  64. package/dist/errors.js +178 -0
  65. package/dist/errors.js.map +1 -0
  66. package/dist/index.d.ts +27 -0
  67. package/dist/index.d.ts.map +1 -0
  68. package/dist/index.js +40 -0
  69. package/dist/index.js.map +1 -0
  70. package/dist/mock/demo-data.d.ts +213 -0
  71. package/dist/mock/demo-data.d.ts.map +1 -0
  72. package/dist/mock/demo-data.js +1096 -0
  73. package/dist/mock/demo-data.js.map +1 -0
  74. package/dist/mock/demo-deployment-store.d.ts +77 -0
  75. package/dist/mock/demo-deployment-store.d.ts.map +1 -0
  76. package/dist/mock/demo-deployment-store.js +85 -0
  77. package/dist/mock/demo-deployment-store.js.map +1 -0
  78. package/dist/powerplatform/admin-client.d.ts +226 -0
  79. package/dist/powerplatform/admin-client.d.ts.map +1 -0
  80. package/dist/powerplatform/admin-client.js +315 -0
  81. package/dist/powerplatform/admin-client.js.map +1 -0
  82. package/dist/powerplatform/index.d.ts +18 -0
  83. package/dist/powerplatform/index.d.ts.map +1 -0
  84. package/dist/powerplatform/index.js +18 -0
  85. package/dist/powerplatform/index.js.map +1 -0
  86. package/dist/powerplatform/tenant-discovery.d.ts +100 -0
  87. package/dist/powerplatform/tenant-discovery.d.ts.map +1 -0
  88. package/dist/powerplatform/tenant-discovery.js +205 -0
  89. package/dist/powerplatform/tenant-discovery.js.map +1 -0
  90. package/dist/preconditions/check.d.ts +41 -0
  91. package/dist/preconditions/check.d.ts.map +1 -0
  92. package/dist/preconditions/check.js +173 -0
  93. package/dist/preconditions/check.js.map +1 -0
  94. package/dist/preconditions/index.d.ts +20 -0
  95. package/dist/preconditions/index.d.ts.map +1 -0
  96. package/dist/preconditions/index.js +20 -0
  97. package/dist/preconditions/index.js.map +1 -0
  98. package/dist/preconditions/loader.d.ts +33 -0
  99. package/dist/preconditions/loader.d.ts.map +1 -0
  100. package/dist/preconditions/loader.js +65 -0
  101. package/dist/preconditions/loader.js.map +1 -0
  102. package/dist/preconditions/schema.d.ts +103 -0
  103. package/dist/preconditions/schema.d.ts.map +1 -0
  104. package/dist/preconditions/schema.js +93 -0
  105. package/dist/preconditions/schema.js.map +1 -0
  106. package/dist/preconditions/types.d.ts +118 -0
  107. package/dist/preconditions/types.d.ts.map +1 -0
  108. package/dist/preconditions/types.js +17 -0
  109. package/dist/preconditions/types.js.map +1 -0
  110. package/dist/queue/index.d.ts +17 -0
  111. package/dist/queue/index.d.ts.map +1 -0
  112. package/dist/queue/index.js +17 -0
  113. package/dist/queue/index.js.map +1 -0
  114. package/dist/queue/memory-queue.d.ts +86 -0
  115. package/dist/queue/memory-queue.d.ts.map +1 -0
  116. package/dist/queue/memory-queue.js +221 -0
  117. package/dist/queue/memory-queue.js.map +1 -0
  118. package/dist/services/audit-log.d.ts +59 -0
  119. package/dist/services/audit-log.d.ts.map +1 -0
  120. package/dist/services/audit-log.js +193 -0
  121. package/dist/services/audit-log.js.map +1 -0
  122. package/dist/services/auth-error-parser.d.ts +36 -0
  123. package/dist/services/auth-error-parser.d.ts.map +1 -0
  124. package/dist/services/auth-error-parser.js +90 -0
  125. package/dist/services/auth-error-parser.js.map +1 -0
  126. package/dist/services/deployment-doctor.d.ts +109 -0
  127. package/dist/services/deployment-doctor.d.ts.map +1 -0
  128. package/dist/services/deployment-doctor.js +476 -0
  129. package/dist/services/deployment-doctor.js.map +1 -0
  130. package/dist/services/deployment-notifications.d.ts +41 -0
  131. package/dist/services/deployment-notifications.d.ts.map +1 -0
  132. package/dist/services/deployment-notifications.js +161 -0
  133. package/dist/services/deployment-notifications.js.map +1 -0
  134. package/dist/services/deployment-progress.d.ts +89 -0
  135. package/dist/services/deployment-progress.d.ts.map +1 -0
  136. package/dist/services/deployment-progress.js +244 -0
  137. package/dist/services/deployment-progress.js.map +1 -0
  138. package/dist/services/deployment-service.d.ts +97 -0
  139. package/dist/services/deployment-service.d.ts.map +1 -0
  140. package/dist/services/deployment-service.js +375 -0
  141. package/dist/services/deployment-service.js.map +1 -0
  142. package/dist/services/drift-analyzer.d.ts +86 -0
  143. package/dist/services/drift-analyzer.d.ts.map +1 -0
  144. package/dist/services/drift-analyzer.js +273 -0
  145. package/dist/services/drift-analyzer.js.map +1 -0
  146. package/dist/services/environment-setup.d.ts +97 -0
  147. package/dist/services/environment-setup.d.ts.map +1 -0
  148. package/dist/services/environment-setup.js +250 -0
  149. package/dist/services/environment-setup.js.map +1 -0
  150. package/dist/services/health-check.d.ts +168 -0
  151. package/dist/services/health-check.d.ts.map +1 -0
  152. package/dist/services/health-check.js +705 -0
  153. package/dist/services/health-check.js.map +1 -0
  154. package/dist/services/index.d.ts +39 -0
  155. package/dist/services/index.d.ts.map +1 -0
  156. package/dist/services/index.js +39 -0
  157. package/dist/services/index.js.map +1 -0
  158. package/dist/services/logger.d.ts +139 -0
  159. package/dist/services/logger.d.ts.map +1 -0
  160. package/dist/services/logger.js +268 -0
  161. package/dist/services/logger.js.map +1 -0
  162. package/dist/services/notification-service.d.ts +55 -0
  163. package/dist/services/notification-service.d.ts.map +1 -0
  164. package/dist/services/notification-service.js +184 -0
  165. package/dist/services/notification-service.js.map +1 -0
  166. package/dist/services/risk-analyzer.d.ts +252 -0
  167. package/dist/services/risk-analyzer.d.ts.map +1 -0
  168. package/dist/services/risk-analyzer.js +866 -0
  169. package/dist/services/risk-analyzer.js.map +1 -0
  170. package/dist/services/rollback.d.ts +57 -0
  171. package/dist/services/rollback.d.ts.map +1 -0
  172. package/dist/services/rollback.js +270 -0
  173. package/dist/services/rollback.js.map +1 -0
  174. package/dist/services/scheduler.d.ts +80 -0
  175. package/dist/services/scheduler.d.ts.map +1 -0
  176. package/dist/services/scheduler.js +350 -0
  177. package/dist/services/scheduler.js.map +1 -0
  178. package/dist/services/secrets.d.ts +31 -0
  179. package/dist/services/secrets.d.ts.map +1 -0
  180. package/dist/services/secrets.js +206 -0
  181. package/dist/services/secrets.js.map +1 -0
  182. package/dist/services/settings-service.d.ts +132 -0
  183. package/dist/services/settings-service.d.ts.map +1 -0
  184. package/dist/services/settings-service.js +378 -0
  185. package/dist/services/settings-service.js.map +1 -0
  186. package/dist/services/solution-diff.d.ts +127 -0
  187. package/dist/services/solution-diff.d.ts.map +1 -0
  188. package/dist/services/solution-diff.js +260 -0
  189. package/dist/services/solution-diff.js.map +1 -0
  190. package/dist/services/solution-mode-detector.d.ts +35 -0
  191. package/dist/services/solution-mode-detector.d.ts.map +1 -0
  192. package/dist/services/solution-mode-detector.js +84 -0
  193. package/dist/services/solution-mode-detector.js.map +1 -0
  194. package/dist/services/tenant-resolver.d.ts +55 -0
  195. package/dist/services/tenant-resolver.d.ts.map +1 -0
  196. package/dist/services/tenant-resolver.js +126 -0
  197. package/dist/services/tenant-resolver.js.map +1 -0
  198. package/dist/services/unmanaged-customizations.d.ts +104 -0
  199. package/dist/services/unmanaged-customizations.d.ts.map +1 -0
  200. package/dist/services/unmanaged-customizations.js +521 -0
  201. package/dist/services/unmanaged-customizations.js.map +1 -0
  202. package/dist/services/url-templater.d.ts +184 -0
  203. package/dist/services/url-templater.d.ts.map +1 -0
  204. package/dist/services/url-templater.js +327 -0
  205. package/dist/services/url-templater.js.map +1 -0
  206. package/dist/services/version-checker.d.ts +108 -0
  207. package/dist/services/version-checker.d.ts.map +1 -0
  208. package/dist/services/version-checker.js +403 -0
  209. package/dist/services/version-checker.js.map +1 -0
  210. package/dist/services/waves.d.ts +90 -0
  211. package/dist/services/waves.d.ts.map +1 -0
  212. package/dist/services/waves.js +222 -0
  213. package/dist/services/waves.js.map +1 -0
  214. package/dist/services/webhook.d.ts +95 -0
  215. package/dist/services/webhook.d.ts.map +1 -0
  216. package/dist/services/webhook.js +244 -0
  217. package/dist/services/webhook.js.map +1 -0
  218. package/dist/utils/deployment-tools.d.ts +110 -0
  219. package/dist/utils/deployment-tools.d.ts.map +1 -0
  220. package/dist/utils/deployment-tools.js +121 -0
  221. package/dist/utils/deployment-tools.js.map +1 -0
  222. package/dist/utils/index.d.ts +17 -0
  223. package/dist/utils/index.d.ts.map +1 -0
  224. package/dist/utils/index.js +18 -0
  225. package/dist/utils/index.js.map +1 -0
  226. package/package.json +49 -0
@@ -0,0 +1,273 @@
1
+ /**
2
+ * Copyright 2024 Pax8, Inc.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ // ============================================================================
17
+ // Configuration
18
+ // ============================================================================
19
+ /** Weights for risk factors (higher = more impact on score) */
20
+ const WEIGHTS = {
21
+ versionsBehind: 8,
22
+ lastDeployResult: 7,
23
+ timeSinceLastDeploy: 5,
24
+ deploySuccessRate: 4,
25
+ productionTag: 6,
26
+ };
27
+ const HIGH_RISK_TAGS = ["production", "enterprise", "priority", "finance"];
28
+ const LOW_RISK_TAGS = ["test", "staging", "dev", "sandbox"];
29
+ // ============================================================================
30
+ // DriftAnalyzer
31
+ // ============================================================================
32
+ export class DriftAnalyzer {
33
+ /**
34
+ * Analyze drift risk for a single tenant
35
+ */
36
+ analyzeTenant(tenant, versionStatus, history) {
37
+ const factors = [];
38
+ const outdatedSolutions = versionStatus.solutions.filter((s) => s.status === "outdated");
39
+ // If tenant is current, short-circuit
40
+ if (versionStatus.overallStatus === "current") {
41
+ return {
42
+ tenantId: tenant.tenantId,
43
+ tenantName: tenant.name,
44
+ environmentUrl: tenant.environmentUrl,
45
+ riskLevel: "low",
46
+ riskScore: 0,
47
+ recommendation: "current",
48
+ recommendationReason: "All solutions are at the expected version",
49
+ factors: [],
50
+ versionStatus,
51
+ outdatedSolutions: [],
52
+ };
53
+ }
54
+ // Factor 1: How many versions behind
55
+ const maxDrift = Math.max(...outdatedSolutions.map((s) => Math.abs(s.versionDrift)), 0);
56
+ if (maxDrift >= 3) {
57
+ factors.push({
58
+ name: "versions_behind",
59
+ level: "high",
60
+ weight: WEIGHTS.versionsBehind,
61
+ description: `${maxDrift} version(s) behind — significant gap increases upgrade risk`,
62
+ });
63
+ }
64
+ else if (maxDrift === 2) {
65
+ factors.push({
66
+ name: "versions_behind",
67
+ level: "medium",
68
+ weight: WEIGHTS.versionsBehind,
69
+ description: `${maxDrift} version(s) behind`,
70
+ });
71
+ }
72
+ else if (maxDrift === 1) {
73
+ factors.push({
74
+ name: "versions_behind",
75
+ level: "low",
76
+ weight: WEIGHTS.versionsBehind,
77
+ description: "1 version behind — minor update",
78
+ });
79
+ }
80
+ // Factor 2: Last deployment result
81
+ if (history) {
82
+ if (history.lastDeployResult === "failure") {
83
+ factors.push({
84
+ name: "last_deploy_failed",
85
+ level: "high",
86
+ weight: WEIGHTS.lastDeployResult,
87
+ description: "Last deployment failed — retrying may hit the same issue",
88
+ });
89
+ }
90
+ else if (history.lastDeployResult === "success") {
91
+ factors.push({
92
+ name: "last_deploy_succeeded",
93
+ level: "low",
94
+ weight: WEIGHTS.lastDeployResult,
95
+ description: "Last deployment succeeded",
96
+ });
97
+ }
98
+ // Factor 3: Time since last deploy
99
+ if (history.lastDeployDate) {
100
+ const daysSince = Math.floor((Date.now() - new Date(history.lastDeployDate).getTime()) / (1000 * 60 * 60 * 24));
101
+ if (daysSince > 90) {
102
+ factors.push({
103
+ name: "stale_environment",
104
+ level: "high",
105
+ weight: WEIGHTS.timeSinceLastDeploy,
106
+ description: `${daysSince} days since last deploy — environment may have drifted significantly`,
107
+ });
108
+ }
109
+ else if (daysSince > 30) {
110
+ factors.push({
111
+ name: "aging_environment",
112
+ level: "medium",
113
+ weight: WEIGHTS.timeSinceLastDeploy,
114
+ description: `${daysSince} days since last deploy`,
115
+ });
116
+ }
117
+ else {
118
+ factors.push({
119
+ name: "recent_deploy",
120
+ level: "low",
121
+ weight: WEIGHTS.timeSinceLastDeploy,
122
+ description: `${daysSince} days since last deploy — recently active`,
123
+ });
124
+ }
125
+ }
126
+ // Factor 4: Deploy success rate
127
+ if (history.totalDeploys > 0) {
128
+ const successRate = history.successfulDeploys / history.totalDeploys;
129
+ if (successRate < 0.5) {
130
+ factors.push({
131
+ name: "low_success_rate",
132
+ level: "high",
133
+ weight: WEIGHTS.deploySuccessRate,
134
+ description: `${Math.round(successRate * 100)}% deploy success rate — this tenant is flaky`,
135
+ });
136
+ }
137
+ else if (successRate < 0.8) {
138
+ factors.push({
139
+ name: "moderate_success_rate",
140
+ level: "medium",
141
+ weight: WEIGHTS.deploySuccessRate,
142
+ description: `${Math.round(successRate * 100)}% deploy success rate`,
143
+ });
144
+ }
145
+ }
146
+ }
147
+ // Factor 5: Tenant tags (production = higher risk)
148
+ const tags = tenant.tags || [];
149
+ const hasHighRiskTag = tags.some((t) => HIGH_RISK_TAGS.includes(t.toLowerCase()));
150
+ const hasLowRiskTag = tags.some((t) => LOW_RISK_TAGS.includes(t.toLowerCase()));
151
+ if (hasHighRiskTag) {
152
+ factors.push({
153
+ name: "production_tenant",
154
+ level: "medium",
155
+ weight: WEIGHTS.productionTag,
156
+ description: `Tagged as ${tags.filter((t) => HIGH_RISK_TAGS.includes(t.toLowerCase())).join(", ")} — higher blast radius`,
157
+ });
158
+ }
159
+ else if (hasLowRiskTag) {
160
+ factors.push({
161
+ name: "non_production_tenant",
162
+ level: "low",
163
+ weight: WEIGHTS.productionTag,
164
+ description: `Tagged as ${tags.filter((t) => LOW_RISK_TAGS.includes(t.toLowerCase())).join(", ")} — lower risk`,
165
+ });
166
+ }
167
+ // Calculate composite score
168
+ const riskScore = this.calculateScore(factors);
169
+ const riskLevel = this.scoreToLevel(riskScore);
170
+ const { recommendation, reason } = this.getRecommendation(riskLevel, factors);
171
+ return {
172
+ tenantId: tenant.tenantId,
173
+ tenantName: tenant.name,
174
+ environmentUrl: tenant.environmentUrl,
175
+ riskLevel,
176
+ riskScore,
177
+ recommendation,
178
+ recommendationReason: reason,
179
+ factors,
180
+ versionStatus,
181
+ outdatedSolutions,
182
+ };
183
+ }
184
+ /**
185
+ * Analyze drift risk for a fleet of tenants
186
+ */
187
+ analyzeFleet(tenants, versionStatuses, histories) {
188
+ const analyses = tenants.map((tenant, i) => {
189
+ const versionStatus = versionStatuses[i];
190
+ const history = histories?.get(tenant.tenantId);
191
+ return this.analyzeTenant(tenant, versionStatus, history);
192
+ });
193
+ return {
194
+ tenants: analyses,
195
+ summary: {
196
+ total: analyses.length,
197
+ current: analyses.filter((a) => a.recommendation === "current").length,
198
+ safeToUpdate: analyses.filter((a) => a.recommendation === "safe_to_update").length,
199
+ reviewRecommended: analyses.filter((a) => a.recommendation === "review_recommended").length,
200
+ risky: analyses.filter((a) => a.recommendation === "update_risky").length,
201
+ doNotUpdate: analyses.filter((a) => a.recommendation === "do_not_update").length,
202
+ },
203
+ };
204
+ }
205
+ // ============================================================================
206
+ // Private
207
+ // ============================================================================
208
+ calculateScore(factors) {
209
+ if (factors.length === 0)
210
+ return 0;
211
+ let weightedSum = 0;
212
+ let totalWeight = 0;
213
+ for (const factor of factors) {
214
+ const levelScore = factor.level === "high" ? 100 : factor.level === "medium" ? 50 : 10;
215
+ weightedSum += levelScore * factor.weight;
216
+ totalWeight += factor.weight;
217
+ }
218
+ return totalWeight > 0 ? Math.round(weightedSum / totalWeight) : 0;
219
+ }
220
+ scoreToLevel(score) {
221
+ if (score >= 65)
222
+ return "high";
223
+ if (score >= 35)
224
+ return "medium";
225
+ return "low";
226
+ }
227
+ getRecommendation(level, factors) {
228
+ const hasLastDeployFailed = factors.some((f) => f.name === "last_deploy_failed");
229
+ const hasStaleEnv = factors.some((f) => f.name === "stale_environment");
230
+ const hasLowSuccessRate = factors.some((f) => f.name === "low_success_rate");
231
+ // Hard blockers
232
+ if (hasLastDeployFailed && hasStaleEnv) {
233
+ return {
234
+ recommendation: "do_not_update",
235
+ reason: "Last deploy failed and environment is stale — investigate before retrying",
236
+ };
237
+ }
238
+ if (hasLowSuccessRate && hasLastDeployFailed) {
239
+ return {
240
+ recommendation: "do_not_update",
241
+ reason: "Chronically failing tenant — fix underlying issues first",
242
+ };
243
+ }
244
+ // Score-based
245
+ if (level === "high") {
246
+ return {
247
+ recommendation: "update_risky",
248
+ reason: this.getTopRiskDescription(factors),
249
+ };
250
+ }
251
+ if (level === "medium") {
252
+ return {
253
+ recommendation: "review_recommended",
254
+ reason: this.getTopRiskDescription(factors),
255
+ };
256
+ }
257
+ return {
258
+ recommendation: "safe_to_update",
259
+ reason: "Low risk — safe to proceed",
260
+ };
261
+ }
262
+ getTopRiskDescription(factors) {
263
+ const highFactors = factors.filter((f) => f.level === "high" || f.level === "medium");
264
+ if (highFactors.length === 0)
265
+ return "Minor risk factors present";
266
+ // Sort by weight descending, take top factor
267
+ highFactors.sort((a, b) => b.weight - a.weight);
268
+ return highFactors[0].description;
269
+ }
270
+ }
271
+ // Export singleton
272
+ export const driftAnalyzer = new DriftAnalyzer();
273
+ //# sourceMappingURL=drift-analyzer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"drift-analyzer.js","sourceRoot":"","sources":["../../src/services/drift-analyzer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Copyright 2024 Pax8, Inc.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ /**
17
+ * Environment Setup Service
18
+ *
19
+ * Manages Power Platform application user lifecycle:
20
+ * - Checking if an app user exists in a Dataverse environment
21
+ * - Verifying security role assignments
22
+ * - Creating app users and assigning roles
23
+ * - Validating tenant readiness for deployment
24
+ *
25
+ * Extracted from CLI setup.ts, validate.ts, and deploy.ts (prepareEnvironment)
26
+ * to eliminate duplication and make the logic testable and reusable.
27
+ */
28
+ import { DataverseClient } from "../dataverse/client.js";
29
+ export interface SystemUser {
30
+ systemuserid: string;
31
+ fullname?: string;
32
+ applicationid?: string;
33
+ isdisabled?: boolean;
34
+ }
35
+ export type SetupStatusCode = "ready" | "needs_setup" | "partial" | "error";
36
+ export interface SetupStatus {
37
+ tenantName: string;
38
+ environmentUrl: string;
39
+ appRegistered: boolean;
40
+ roleAssigned: boolean;
41
+ status: SetupStatusCode;
42
+ error?: string;
43
+ userId?: string;
44
+ }
45
+ export interface TenantValidationResult {
46
+ appUserExists: boolean;
47
+ hasSystemAdminRole: boolean;
48
+ userId?: string;
49
+ }
50
+ export interface PrepareEnvironmentResult {
51
+ success: boolean;
52
+ message: string;
53
+ }
54
+ export declare class EnvironmentSetupService {
55
+ /**
56
+ * Check if an app user exists and has the System Administrator role.
57
+ *
58
+ * This is the core validation used by `pax8-cta validate` and `pax8-cta setup --check`.
59
+ */
60
+ validateTenant(client: DataverseClient, appId: string): Promise<TenantValidationResult>;
61
+ /**
62
+ * Check the full setup status for a tenant, including error handling.
63
+ *
64
+ * Returns a structured status object suitable for display in tables or JSON output.
65
+ * Used by `pax8-cta setup --check`.
66
+ */
67
+ checkSetupStatus(client: DataverseClient, appId: string, tenantName: string, environmentUrl: string): Promise<SetupStatus>;
68
+ /**
69
+ * Create an application user in the Dataverse environment.
70
+ *
71
+ * Finds the root business unit and creates the app user bound to it.
72
+ * Returns the new user's system user ID.
73
+ */
74
+ createAppUser(client: DataverseClient, appId: string): Promise<string>;
75
+ /**
76
+ * Assign the System Administrator role to a user.
77
+ */
78
+ assignSystemAdminRole(client: DataverseClient, userId: string, environmentUrl: string): Promise<void>;
79
+ /**
80
+ * Ensure an app user is created and has System Administrator role.
81
+ *
82
+ * This is the "setup" action used by both `pax8-cta setup` and
83
+ * the auto-setup step in `pax8-cta deploy`.
84
+ */
85
+ setupTenant(client: DataverseClient, appId: string, environmentUrl: string, currentStatus: SetupStatus): Promise<void>;
86
+ /**
87
+ * Prepare an environment for deployment by ensuring app user exists with proper permissions.
88
+ *
89
+ * Combines check + setup into a single operation. Returns a result suitable for
90
+ * progress reporting.
91
+ *
92
+ * This was previously `prepareEnvironment()` in deploy.ts.
93
+ */
94
+ prepareEnvironment(client: DataverseClient, appId: string, environmentUrl: string): Promise<PrepareEnvironmentResult>;
95
+ }
96
+ export declare const environmentSetupService: EnvironmentSetupService;
97
+ //# sourceMappingURL=environment-setup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"environment-setup.d.ts","sourceRoot":"","sources":["../../src/services/environment-setup.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAOzD,MAAM,WAAW,UAAU;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,MAAM,eAAe,GAAG,OAAO,GAAG,aAAa,GAAG,SAAS,GAAG,OAAO,CAAC;AAE5E,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,OAAO,CAAC;IACvB,YAAY,EAAE,OAAO,CAAC;IACtB,MAAM,EAAE,eAAe,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,sBAAsB;IACrC,aAAa,EAAE,OAAO,CAAC;IACvB,kBAAkB,EAAE,OAAO,CAAC;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,wBAAwB;IACvC,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB;AAMD,qBAAa,uBAAuB;IAClC;;;;OAIG;IACG,cAAc,CAAC,MAAM,EAAE,eAAe,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,sBAAsB,CAAC;IAiC7F;;;;;OAKG;IACG,gBAAgB,CACpB,MAAM,EAAE,eAAe,EACvB,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,MAAM,EAClB,cAAc,EAAE,MAAM,GACrB,OAAO,CAAC,WAAW,CAAC;IA2DvB;;;;;OAKG;IACG,aAAa,CAAC,MAAM,EAAE,eAAe,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAgC5E;;OAEG;IACG,qBAAqB,CACzB,MAAM,EAAE,eAAe,EACvB,MAAM,EAAE,MAAM,EACd,cAAc,EAAE,MAAM,GACrB,OAAO,CAAC,IAAI,CAAC;IAoBhB;;;;;OAKG;IACG,WAAW,CACf,MAAM,EAAE,eAAe,EACvB,KAAK,EAAE,MAAM,EACb,cAAc,EAAE,MAAM,EACtB,aAAa,EAAE,WAAW,GACzB,OAAO,CAAC,IAAI,CAAC;IAchB;;;;;;;OAOG;IACG,kBAAkB,CACtB,MAAM,EAAE,eAAe,EACvB,KAAK,EAAE,MAAM,EACb,cAAc,EAAE,MAAM,GACrB,OAAO,CAAC,wBAAwB,CAAC;CAmErC;AAGD,eAAO,MAAM,uBAAuB,yBAAgC,CAAC"}
@@ -0,0 +1,250 @@
1
+ /**
2
+ * Copyright 2024 Pax8, Inc.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ // ============================================================================
17
+ // Service
18
+ // ============================================================================
19
+ export class EnvironmentSetupService {
20
+ /**
21
+ * Check if an app user exists and has the System Administrator role.
22
+ *
23
+ * This is the core validation used by `pax8-cta validate` and `pax8-cta setup --check`.
24
+ */
25
+ async validateTenant(client, appId) {
26
+ // Check if app user exists
27
+ const result = await client.get("/systemusers", {
28
+ $filter: `applicationid eq '${appId}'`,
29
+ $select: "systemuserid,fullname,applicationid,isdisabled",
30
+ });
31
+ if (result.value.length === 0) {
32
+ return {
33
+ appUserExists: false,
34
+ hasSystemAdminRole: false,
35
+ };
36
+ }
37
+ const user = result.value[0];
38
+ // Check if System Administrator role is assigned
39
+ const rolesResult = await client.get(`/systemusers(${user.systemuserid})/systemuserroles_association`, {
40
+ $select: "roleid,name",
41
+ });
42
+ const hasAdminRole = rolesResult.value.some((r) => r.name === "System Administrator");
43
+ return {
44
+ appUserExists: true,
45
+ hasSystemAdminRole: hasAdminRole,
46
+ userId: user.systemuserid,
47
+ };
48
+ }
49
+ /**
50
+ * Check the full setup status for a tenant, including error handling.
51
+ *
52
+ * Returns a structured status object suitable for display in tables or JSON output.
53
+ * Used by `pax8-cta setup --check`.
54
+ */
55
+ async checkSetupStatus(client, appId, tenantName, environmentUrl) {
56
+ try {
57
+ const validation = await this.validateTenant(client, appId);
58
+ if (!validation.appUserExists) {
59
+ return {
60
+ tenantName,
61
+ environmentUrl,
62
+ appRegistered: false,
63
+ roleAssigned: false,
64
+ status: "needs_setup",
65
+ };
66
+ }
67
+ if (!validation.hasSystemAdminRole) {
68
+ return {
69
+ tenantName,
70
+ environmentUrl,
71
+ appRegistered: true,
72
+ roleAssigned: false,
73
+ status: "partial",
74
+ userId: validation.userId,
75
+ };
76
+ }
77
+ return {
78
+ tenantName,
79
+ environmentUrl,
80
+ appRegistered: true,
81
+ roleAssigned: true,
82
+ status: "ready",
83
+ userId: validation.userId,
84
+ };
85
+ }
86
+ catch (error) {
87
+ const errorMsg = error instanceof Error ? error.message : String(error);
88
+ // Check if it's an auth error (app not registered at tenant level)
89
+ if (errorMsg.includes("not a member of the organization")) {
90
+ return {
91
+ tenantName,
92
+ environmentUrl,
93
+ appRegistered: false,
94
+ roleAssigned: false,
95
+ status: "needs_setup",
96
+ error: "App not registered in this environment (bootstrap required)",
97
+ };
98
+ }
99
+ return {
100
+ tenantName,
101
+ environmentUrl,
102
+ appRegistered: false,
103
+ roleAssigned: false,
104
+ status: "error",
105
+ error: errorMsg,
106
+ };
107
+ }
108
+ }
109
+ /**
110
+ * Create an application user in the Dataverse environment.
111
+ *
112
+ * Finds the root business unit and creates the app user bound to it.
113
+ * Returns the new user's system user ID.
114
+ */
115
+ async createAppUser(client, appId) {
116
+ // Get root business unit
117
+ const buResult = await client.get("/businessunits", {
118
+ $filter: "parentbusinessunitid eq null",
119
+ $select: "businessunitid,name",
120
+ });
121
+ if (buResult.value.length === 0) {
122
+ throw new Error("Could not find root business unit");
123
+ }
124
+ const buId = buResult.value[0].businessunitid;
125
+ // Create app user
126
+ await client.post("/systemusers", {
127
+ applicationid: appId,
128
+ "businessunitid@odata.bind": `/businessunits(${buId})`,
129
+ });
130
+ // Get the newly created user's ID
131
+ const userResult = await client.get("/systemusers", {
132
+ $filter: `applicationid eq '${appId}'`,
133
+ $select: "systemuserid",
134
+ });
135
+ if (userResult.value.length === 0) {
136
+ throw new Error("Failed to create app user");
137
+ }
138
+ return userResult.value[0].systemuserid;
139
+ }
140
+ /**
141
+ * Assign the System Administrator role to a user.
142
+ */
143
+ async assignSystemAdminRole(client, userId, environmentUrl) {
144
+ // Get System Administrator role
145
+ const roleResult = await client.get("/roles", {
146
+ $filter: "name eq 'System Administrator'",
147
+ $select: "roleid,name",
148
+ });
149
+ if (roleResult.value.length === 0) {
150
+ throw new Error("Could not find System Administrator role");
151
+ }
152
+ const roleId = roleResult.value[0].roleid;
153
+ // Assign role to user
154
+ const apiUrl = environmentUrl.replace(/\/$/, "") + "/api/data/v9.2";
155
+ await client.post(`/systemusers(${userId})/systemuserroles_association/$ref`, {
156
+ "@odata.id": `${apiUrl}/roles(${roleId})`,
157
+ });
158
+ }
159
+ /**
160
+ * Ensure an app user is created and has System Administrator role.
161
+ *
162
+ * This is the "setup" action used by both `pax8-cta setup` and
163
+ * the auto-setup step in `pax8-cta deploy`.
164
+ */
165
+ async setupTenant(client, appId, environmentUrl, currentStatus) {
166
+ let userId = currentStatus.userId;
167
+ // Create app user if needed
168
+ if (!currentStatus.appRegistered) {
169
+ userId = await this.createAppUser(client, appId);
170
+ }
171
+ // Assign System Administrator role if needed
172
+ if (!currentStatus.roleAssigned && userId) {
173
+ await this.assignSystemAdminRole(client, userId, environmentUrl);
174
+ }
175
+ }
176
+ /**
177
+ * Prepare an environment for deployment by ensuring app user exists with proper permissions.
178
+ *
179
+ * Combines check + setup into a single operation. Returns a result suitable for
180
+ * progress reporting.
181
+ *
182
+ * This was previously `prepareEnvironment()` in deploy.ts.
183
+ */
184
+ async prepareEnvironment(client, appId, environmentUrl) {
185
+ try {
186
+ const validation = await this.validateTenant(client, appId);
187
+ if (validation.appUserExists && validation.hasSystemAdminRole) {
188
+ return { success: true, message: "Ready" };
189
+ }
190
+ // Need to create user or assign role
191
+ if (!validation.appUserExists) {
192
+ try {
193
+ const userId = await this.createAppUser(client, appId);
194
+ await this.assignSystemAdminRole(client, userId, environmentUrl);
195
+ return {
196
+ success: true,
197
+ message: "Created app user and assigned System Administrator role",
198
+ };
199
+ }
200
+ catch (error) {
201
+ const errorMsg = error instanceof Error ? error.message : String(error);
202
+ if (errorMsg.includes("not a member of the organization")) {
203
+ return {
204
+ success: false,
205
+ message: "App not registered (requires manual bootstrap in Power Platform admin center)",
206
+ };
207
+ }
208
+ return {
209
+ success: false,
210
+ message: `Failed to create app user: ${errorMsg}`,
211
+ };
212
+ }
213
+ }
214
+ // User exists but needs role
215
+ if (validation.userId) {
216
+ try {
217
+ await this.assignSystemAdminRole(client, validation.userId, environmentUrl);
218
+ return {
219
+ success: true,
220
+ message: "Assigned System Administrator role",
221
+ };
222
+ }
223
+ catch (error) {
224
+ const errorMsg = error instanceof Error ? error.message : String(error);
225
+ return {
226
+ success: false,
227
+ message: `Failed to assign role: ${errorMsg}`,
228
+ };
229
+ }
230
+ }
231
+ return { success: false, message: "Unexpected state" };
232
+ }
233
+ catch (error) {
234
+ const errorMsg = error instanceof Error ? error.message : String(error);
235
+ if (errorMsg.includes("not a member of the organization")) {
236
+ return {
237
+ success: false,
238
+ message: "App not registered (requires manual bootstrap in Power Platform admin center)",
239
+ };
240
+ }
241
+ return {
242
+ success: false,
243
+ message: `Error: ${errorMsg}`,
244
+ };
245
+ }
246
+ }
247
+ }
248
+ // Export singleton for convenience
249
+ export const environmentSetupService = new EnvironmentSetupService();
250
+ //# sourceMappingURL=environment-setup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"environment-setup.js","sourceRoot":"","sources":["../../src/services/environment-setup.ts"],"names":[],"mappings":"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"}