@friggframework/devtools 2.0.0--canary.517.300ded3.0 → 2.0.0--canary.522.cbd3d5a.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 (247) hide show
  1. package/frigg-cli/README.md +1 -1
  2. package/frigg-cli/__tests__/application/use-cases/AddApiModuleToIntegrationUseCase.test.js +326 -0
  3. package/frigg-cli/__tests__/application/use-cases/CreateApiModuleUseCase.test.js +337 -0
  4. package/frigg-cli/__tests__/domain/entities/ApiModule.test.js +373 -0
  5. package/frigg-cli/__tests__/domain/entities/AppDefinition.test.js +313 -0
  6. package/frigg-cli/__tests__/domain/services/IntegrationValidator.test.js +269 -0
  7. package/frigg-cli/__tests__/domain/value-objects/IntegrationName.test.js +82 -0
  8. package/frigg-cli/__tests__/infrastructure/adapters/IntegrationJsUpdater.test.js +408 -0
  9. package/frigg-cli/__tests__/infrastructure/repositories/FileSystemApiModuleRepository.test.js +583 -0
  10. package/frigg-cli/__tests__/infrastructure/repositories/FileSystemAppDefinitionRepository.test.js +314 -0
  11. package/frigg-cli/__tests__/infrastructure/repositories/FileSystemIntegrationRepository.test.js +430 -0
  12. package/frigg-cli/__tests__/unit/commands/doctor.test.js +0 -2
  13. package/frigg-cli/__tests__/unit/commands/init.test.js +406 -0
  14. package/frigg-cli/__tests__/unit/commands/install.test.js +21 -17
  15. package/frigg-cli/__tests__/unit/commands/repair.test.js +275 -0
  16. package/frigg-cli/__tests__/unit/start-command/application/RunPreflightChecksUseCase.test.js +411 -0
  17. package/frigg-cli/__tests__/unit/start-command/infrastructure/DatabaseAdapter.test.js +405 -0
  18. package/frigg-cli/__tests__/unit/start-command/infrastructure/DockerAdapter.test.js +496 -0
  19. package/frigg-cli/__tests__/unit/start-command/presentation/InteractivePromptAdapter.test.js +474 -0
  20. package/frigg-cli/__tests__/unit/utils/output.test.js +196 -0
  21. package/frigg-cli/application/use-cases/AddApiModuleToIntegrationUseCase.js +93 -0
  22. package/frigg-cli/application/use-cases/CreateApiModuleUseCase.js +93 -0
  23. package/frigg-cli/application/use-cases/CreateIntegrationUseCase.js +103 -0
  24. package/frigg-cli/container.js +172 -0
  25. package/frigg-cli/docs/OUTPUT_MIGRATION_GUIDE.md +286 -0
  26. package/frigg-cli/doctor-command/index.js +17 -16
  27. package/frigg-cli/domain/entities/ApiModule.js +272 -0
  28. package/frigg-cli/domain/entities/AppDefinition.js +227 -0
  29. package/frigg-cli/domain/entities/Integration.js +198 -0
  30. package/frigg-cli/domain/exceptions/DomainException.js +24 -0
  31. package/frigg-cli/domain/ports/IApiModuleRepository.js +53 -0
  32. package/frigg-cli/domain/ports/IAppDefinitionRepository.js +43 -0
  33. package/frigg-cli/domain/ports/IIntegrationRepository.js +61 -0
  34. package/frigg-cli/domain/services/IntegrationValidator.js +185 -0
  35. package/frigg-cli/domain/value-objects/IntegrationId.js +42 -0
  36. package/frigg-cli/domain/value-objects/IntegrationName.js +60 -0
  37. package/frigg-cli/domain/value-objects/SemanticVersion.js +70 -0
  38. package/frigg-cli/index.js +21 -6
  39. package/frigg-cli/index.test.js +7 -1
  40. package/frigg-cli/infrastructure/UnitOfWork.js +46 -0
  41. package/frigg-cli/infrastructure/adapters/BackendJsUpdater.js +197 -0
  42. package/frigg-cli/infrastructure/adapters/FileSystemAdapter.js +224 -0
  43. package/frigg-cli/infrastructure/adapters/IntegrationJsUpdater.js +249 -0
  44. package/frigg-cli/infrastructure/adapters/SchemaValidator.js +92 -0
  45. package/frigg-cli/infrastructure/repositories/FileSystemApiModuleRepository.js +373 -0
  46. package/frigg-cli/infrastructure/repositories/FileSystemAppDefinitionRepository.js +116 -0
  47. package/frigg-cli/infrastructure/repositories/FileSystemIntegrationRepository.js +277 -0
  48. package/frigg-cli/init-command/backend-first-handler.js +124 -42
  49. package/frigg-cli/init-command/index.js +2 -1
  50. package/frigg-cli/init-command/template-handler.js +13 -3
  51. package/frigg-cli/install-command/backend-js.js +3 -3
  52. package/frigg-cli/install-command/environment-variables.js +16 -19
  53. package/frigg-cli/install-command/environment-variables.test.js +12 -13
  54. package/frigg-cli/install-command/index.js +14 -9
  55. package/frigg-cli/install-command/integration-file.js +3 -3
  56. package/frigg-cli/install-command/validate-package.js +5 -9
  57. package/frigg-cli/jest.config.js +4 -1
  58. package/frigg-cli/package-lock.json +16226 -0
  59. package/frigg-cli/repair-command/index.js +101 -128
  60. package/frigg-cli/start-command/application/RunPreflightChecksUseCase.js +376 -0
  61. package/frigg-cli/start-command/index.js +246 -2
  62. package/frigg-cli/start-command/infrastructure/DatabaseAdapter.js +591 -0
  63. package/frigg-cli/start-command/infrastructure/DockerAdapter.js +306 -0
  64. package/frigg-cli/start-command/presentation/InteractivePromptAdapter.js +329 -0
  65. package/frigg-cli/templates/backend/.env.example +62 -0
  66. package/frigg-cli/templates/backend/.eslintrc.json +12 -0
  67. package/frigg-cli/templates/backend/.prettierrc +6 -0
  68. package/frigg-cli/templates/backend/docker-compose.yml +22 -0
  69. package/frigg-cli/templates/backend/index.js +96 -0
  70. package/frigg-cli/templates/backend/infrastructure.js +12 -0
  71. package/frigg-cli/templates/backend/jest.config.js +17 -0
  72. package/frigg-cli/templates/backend/package.json +50 -0
  73. package/frigg-cli/templates/backend/src/api-modules/.gitkeep +10 -0
  74. package/frigg-cli/templates/backend/src/base/.gitkeep +7 -0
  75. package/frigg-cli/templates/backend/src/integrations/.gitkeep +10 -0
  76. package/frigg-cli/templates/backend/src/integrations/ExampleIntegration.js +65 -0
  77. package/frigg-cli/templates/backend/src/utils/.gitkeep +7 -0
  78. package/frigg-cli/templates/backend/test/setup.js +30 -0
  79. package/frigg-cli/templates/backend/ui-extensions/.gitkeep +0 -0
  80. package/frigg-cli/templates/backend/ui-extensions/README.md +77 -0
  81. package/frigg-cli/ui-command/index.js +58 -36
  82. package/frigg-cli/utils/__tests__/repo-detection.test.js +436 -0
  83. package/frigg-cli/utils/output.js +382 -0
  84. package/frigg-cli/utils/repo-detection.js +85 -37
  85. package/frigg-cli/validate-command/__tests__/adapters/validate-command.test.js +205 -0
  86. package/frigg-cli/validate-command/__tests__/application/validate-app-use-case.test.js +104 -0
  87. package/frigg-cli/validate-command/__tests__/domain/fix-suggestion.test.js +153 -0
  88. package/frigg-cli/validate-command/__tests__/domain/validation-error.test.js +162 -0
  89. package/frigg-cli/validate-command/__tests__/domain/validation-result.test.js +152 -0
  90. package/frigg-cli/validate-command/__tests__/infrastructure/api-module-validator.test.js +332 -0
  91. package/frigg-cli/validate-command/__tests__/infrastructure/app-definition-validator.test.js +191 -0
  92. package/frigg-cli/validate-command/__tests__/infrastructure/integration-class-validator.test.js +146 -0
  93. package/frigg-cli/validate-command/__tests__/infrastructure/template-validation.test.js +155 -0
  94. package/frigg-cli/validate-command/adapters/cli/validate-command.js +199 -0
  95. package/frigg-cli/validate-command/application/use-cases/validate-app-use-case.js +35 -0
  96. package/frigg-cli/validate-command/domain/entities/validation-result.js +74 -0
  97. package/frigg-cli/validate-command/domain/value-objects/fix-suggestion.js +74 -0
  98. package/frigg-cli/validate-command/domain/value-objects/validation-error.js +68 -0
  99. package/frigg-cli/validate-command/infrastructure/validators/api-module-validator.js +181 -0
  100. package/frigg-cli/validate-command/infrastructure/validators/app-definition-validator.js +128 -0
  101. package/frigg-cli/validate-command/infrastructure/validators/integration-class-validator.js +113 -0
  102. package/infrastructure/docs/iam-policy-templates.md +1 -1
  103. package/infrastructure/domains/networking/vpc-builder.test.js +2 -4
  104. package/infrastructure/domains/networking/vpc-resolver.test.js +1 -1
  105. package/infrastructure/domains/shared/cloudformation-discovery.test.js +4 -7
  106. package/infrastructure/domains/shared/resource-discovery.js +5 -5
  107. package/infrastructure/domains/shared/types/discovery-result.test.js +1 -1
  108. package/infrastructure/domains/shared/utilities/base-definition-factory.js +25 -2
  109. package/infrastructure/domains/shared/utilities/base-definition-factory.test.js +2 -2
  110. package/infrastructure/infrastructure-composer.test.js +2 -2
  111. package/management-ui/README.md +245 -109
  112. package/package.json +17 -7
  113. package/.eslintrc.json +0 -3
  114. package/CHANGELOG.md +0 -132
  115. package/frigg-cli/install-command/logger.js +0 -12
  116. package/layers/prisma/.build-complete +0 -3
  117. package/layers/prisma/nodejs/package.json +0 -8
  118. package/management-ui/.eslintrc.js +0 -22
  119. package/management-ui/components.json +0 -21
  120. package/management-ui/docs/phase2-integration-guide.md +0 -320
  121. package/management-ui/index.html +0 -13
  122. package/management-ui/package.json +0 -76
  123. package/management-ui/packages/devtools/frigg-cli/ui-command/index.js +0 -302
  124. package/management-ui/postcss.config.js +0 -6
  125. package/management-ui/server/api/backend.js +0 -256
  126. package/management-ui/server/api/cli.js +0 -315
  127. package/management-ui/server/api/codegen.js +0 -663
  128. package/management-ui/server/api/connections.js +0 -857
  129. package/management-ui/server/api/discovery.js +0 -185
  130. package/management-ui/server/api/environment/index.js +0 -1
  131. package/management-ui/server/api/environment/router.js +0 -378
  132. package/management-ui/server/api/environment.js +0 -328
  133. package/management-ui/server/api/integrations.js +0 -876
  134. package/management-ui/server/api/logs.js +0 -248
  135. package/management-ui/server/api/monitoring.js +0 -282
  136. package/management-ui/server/api/open-ide.js +0 -31
  137. package/management-ui/server/api/project.js +0 -1029
  138. package/management-ui/server/api/users/sessions.js +0 -371
  139. package/management-ui/server/api/users/simulation.js +0 -254
  140. package/management-ui/server/api/users.js +0 -362
  141. package/management-ui/server/api-contract.md +0 -275
  142. package/management-ui/server/index.js +0 -873
  143. package/management-ui/server/middleware/errorHandler.js +0 -93
  144. package/management-ui/server/middleware/security.js +0 -32
  145. package/management-ui/server/processManager.js +0 -296
  146. package/management-ui/server/server.js +0 -346
  147. package/management-ui/server/services/aws-monitor.js +0 -413
  148. package/management-ui/server/services/npm-registry.js +0 -347
  149. package/management-ui/server/services/template-engine.js +0 -538
  150. package/management-ui/server/utils/cliIntegration.js +0 -220
  151. package/management-ui/server/utils/environment/auditLogger.js +0 -471
  152. package/management-ui/server/utils/environment/awsParameterStore.js +0 -275
  153. package/management-ui/server/utils/environment/encryption.js +0 -278
  154. package/management-ui/server/utils/environment/envFileManager.js +0 -286
  155. package/management-ui/server/utils/import-commonjs.js +0 -28
  156. package/management-ui/server/utils/response.js +0 -83
  157. package/management-ui/server/websocket/handler.js +0 -325
  158. package/management-ui/src/App.jsx +0 -25
  159. package/management-ui/src/assets/FriggLogo.svg +0 -1
  160. package/management-ui/src/components/AppRouter.jsx +0 -65
  161. package/management-ui/src/components/Button.jsx +0 -70
  162. package/management-ui/src/components/Card.jsx +0 -97
  163. package/management-ui/src/components/EnvironmentCompare.jsx +0 -400
  164. package/management-ui/src/components/EnvironmentEditor.jsx +0 -372
  165. package/management-ui/src/components/EnvironmentImportExport.jsx +0 -469
  166. package/management-ui/src/components/EnvironmentSchema.jsx +0 -491
  167. package/management-ui/src/components/EnvironmentSecurity.jsx +0 -463
  168. package/management-ui/src/components/ErrorBoundary.jsx +0 -73
  169. package/management-ui/src/components/IntegrationCard.jsx +0 -481
  170. package/management-ui/src/components/IntegrationCardEnhanced.jsx +0 -770
  171. package/management-ui/src/components/IntegrationExplorer.jsx +0 -379
  172. package/management-ui/src/components/IntegrationStatus.jsx +0 -336
  173. package/management-ui/src/components/Layout.jsx +0 -716
  174. package/management-ui/src/components/LoadingSpinner.jsx +0 -113
  175. package/management-ui/src/components/RepositoryPicker.jsx +0 -248
  176. package/management-ui/src/components/SessionMonitor.jsx +0 -350
  177. package/management-ui/src/components/StatusBadge.jsx +0 -208
  178. package/management-ui/src/components/UserContextSwitcher.jsx +0 -212
  179. package/management-ui/src/components/UserSimulation.jsx +0 -327
  180. package/management-ui/src/components/Welcome.jsx +0 -434
  181. package/management-ui/src/components/codegen/APIEndpointGenerator.jsx +0 -637
  182. package/management-ui/src/components/codegen/APIModuleSelector.jsx +0 -227
  183. package/management-ui/src/components/codegen/CodeGenerationWizard.jsx +0 -247
  184. package/management-ui/src/components/codegen/CodePreviewEditor.jsx +0 -316
  185. package/management-ui/src/components/codegen/DynamicModuleForm.jsx +0 -271
  186. package/management-ui/src/components/codegen/FormBuilder.jsx +0 -737
  187. package/management-ui/src/components/codegen/IntegrationGenerator.jsx +0 -855
  188. package/management-ui/src/components/codegen/ProjectScaffoldWizard.jsx +0 -797
  189. package/management-ui/src/components/codegen/SchemaBuilder.jsx +0 -303
  190. package/management-ui/src/components/codegen/TemplateSelector.jsx +0 -586
  191. package/management-ui/src/components/codegen/index.js +0 -10
  192. package/management-ui/src/components/connections/ConnectionConfigForm.jsx +0 -362
  193. package/management-ui/src/components/connections/ConnectionHealthMonitor.jsx +0 -182
  194. package/management-ui/src/components/connections/ConnectionTester.jsx +0 -200
  195. package/management-ui/src/components/connections/EntityRelationshipMapper.jsx +0 -292
  196. package/management-ui/src/components/connections/OAuthFlow.jsx +0 -204
  197. package/management-ui/src/components/connections/index.js +0 -5
  198. package/management-ui/src/components/index.js +0 -21
  199. package/management-ui/src/components/monitoring/APIGatewayMetrics.jsx +0 -222
  200. package/management-ui/src/components/monitoring/LambdaMetrics.jsx +0 -169
  201. package/management-ui/src/components/monitoring/MetricsChart.jsx +0 -197
  202. package/management-ui/src/components/monitoring/MonitoringDashboard.jsx +0 -393
  203. package/management-ui/src/components/monitoring/SQSMetrics.jsx +0 -246
  204. package/management-ui/src/components/monitoring/index.js +0 -6
  205. package/management-ui/src/components/monitoring/monitoring.css +0 -218
  206. package/management-ui/src/components/theme-provider.jsx +0 -52
  207. package/management-ui/src/components/theme-toggle.jsx +0 -39
  208. package/management-ui/src/components/ui/badge.tsx +0 -36
  209. package/management-ui/src/components/ui/button.test.jsx +0 -56
  210. package/management-ui/src/components/ui/button.tsx +0 -57
  211. package/management-ui/src/components/ui/card.tsx +0 -76
  212. package/management-ui/src/components/ui/dropdown-menu.tsx +0 -199
  213. package/management-ui/src/components/ui/select.tsx +0 -157
  214. package/management-ui/src/components/ui/skeleton.jsx +0 -15
  215. package/management-ui/src/hooks/useFrigg.jsx +0 -387
  216. package/management-ui/src/hooks/useSocket.jsx +0 -58
  217. package/management-ui/src/index.css +0 -193
  218. package/management-ui/src/lib/utils.ts +0 -6
  219. package/management-ui/src/main.jsx +0 -10
  220. package/management-ui/src/pages/CodeGeneration.jsx +0 -14
  221. package/management-ui/src/pages/Connections.jsx +0 -252
  222. package/management-ui/src/pages/ConnectionsEnhanced.jsx +0 -633
  223. package/management-ui/src/pages/Dashboard.jsx +0 -311
  224. package/management-ui/src/pages/Environment.jsx +0 -314
  225. package/management-ui/src/pages/IntegrationConfigure.jsx +0 -669
  226. package/management-ui/src/pages/IntegrationDiscovery.jsx +0 -567
  227. package/management-ui/src/pages/IntegrationTest.jsx +0 -742
  228. package/management-ui/src/pages/Integrations.jsx +0 -253
  229. package/management-ui/src/pages/Monitoring.jsx +0 -17
  230. package/management-ui/src/pages/Simulation.jsx +0 -155
  231. package/management-ui/src/pages/Users.jsx +0 -492
  232. package/management-ui/src/services/api.js +0 -41
  233. package/management-ui/src/services/apiModuleService.js +0 -193
  234. package/management-ui/src/services/websocket-handlers.js +0 -120
  235. package/management-ui/src/test/api/project.test.js +0 -273
  236. package/management-ui/src/test/components/Welcome.test.jsx +0 -378
  237. package/management-ui/src/test/mocks/server.js +0 -178
  238. package/management-ui/src/test/setup.js +0 -61
  239. package/management-ui/src/test/utils/test-utils.jsx +0 -134
  240. package/management-ui/src/utils/repository.js +0 -98
  241. package/management-ui/src/utils/repository.test.js +0 -118
  242. package/management-ui/src/workflows/phase2-integration-workflows.js +0 -884
  243. package/management-ui/tailwind.config.js +0 -63
  244. package/management-ui/tsconfig.json +0 -37
  245. package/management-ui/tsconfig.node.json +0 -10
  246. package/management-ui/vite.config.js +0 -26
  247. package/management-ui/vitest.config.js +0 -38
@@ -1281,7 +1281,7 @@ npm install @friggframework/frigg-cli@latest
1281
1281
  npm install -g @friggframework/frigg-cli
1282
1282
 
1283
1283
  # Local project dependencies
1284
- npx create-frigg-app my-app
1284
+ frigg init my-app
1285
1285
  # (Will automatically include @friggframework/frigg-cli in package.json)
1286
1286
  ```
1287
1287
 
@@ -0,0 +1,326 @@
1
+ const {AddApiModuleToIntegrationUseCase} = require('../../../application/use-cases/AddApiModuleToIntegrationUseCase');
2
+ const {Integration} = require('../../../domain/entities/Integration');
3
+ const {IntegrationName} = require('../../../domain/value-objects/IntegrationName');
4
+ const {SemanticVersion} = require('../../../domain/value-objects/SemanticVersion');
5
+ const {ValidationException} = require('../../../domain/exceptions/DomainException');
6
+
7
+ // Mock dependencies
8
+ class MockIntegrationRepository {
9
+ constructor() {
10
+ this.integrations = new Map();
11
+ this.saveCalled = false;
12
+ }
13
+
14
+ async save(integration) {
15
+ this.saveCalled = true;
16
+ this.integrations.set(integration.name.value, integration);
17
+ return integration;
18
+ }
19
+
20
+ async findByName(name) {
21
+ return this.integrations.get(name) || null;
22
+ }
23
+
24
+ async exists(name) {
25
+ return this.integrations.has(name);
26
+ }
27
+ }
28
+
29
+ class MockApiModuleRepository {
30
+ constructor() {
31
+ this.modules = new Set(['salesforce', 'stripe', 'hubspot']);
32
+ }
33
+
34
+ async exists(name) {
35
+ return this.modules.has(name);
36
+ }
37
+ }
38
+
39
+ class MockUnitOfWork {
40
+ constructor() {
41
+ this.committed = false;
42
+ this.rolledBack = false;
43
+ }
44
+
45
+ async commit() {
46
+ this.committed = true;
47
+ }
48
+
49
+ async rollback() {
50
+ this.rolledBack = true;
51
+ }
52
+ }
53
+
54
+ class MockIntegrationValidator {
55
+ constructor() {
56
+ this.validateCalled = false;
57
+ this.shouldFail = false;
58
+ this.errors = [];
59
+ }
60
+
61
+ validateApiModuleAddition(integration, moduleName, moduleVersion) {
62
+ this.validateCalled = true;
63
+ if (this.shouldFail) {
64
+ return {
65
+ isValid: false,
66
+ errors: this.errors
67
+ };
68
+ }
69
+ return {
70
+ isValid: true,
71
+ errors: []
72
+ };
73
+ }
74
+ }
75
+
76
+ describe('AddApiModuleToIntegrationUseCase', () => {
77
+ let useCase;
78
+ let mockIntegrationRepository;
79
+ let mockApiModuleRepository;
80
+ let mockUnitOfWork;
81
+ let mockValidator;
82
+
83
+ beforeEach(() => {
84
+ mockIntegrationRepository = new MockIntegrationRepository();
85
+ mockApiModuleRepository = new MockApiModuleRepository();
86
+ mockUnitOfWork = new MockUnitOfWork();
87
+ mockValidator = new MockIntegrationValidator();
88
+
89
+ useCase = new AddApiModuleToIntegrationUseCase(
90
+ mockIntegrationRepository,
91
+ mockApiModuleRepository,
92
+ mockUnitOfWork,
93
+ mockValidator
94
+ );
95
+
96
+ // Add a test integration
97
+ const integration = Integration.create({
98
+ name: 'test-integration',
99
+ type: 'api',
100
+ displayName: 'Test Integration',
101
+ description: 'Test',
102
+ category: 'CRM'
103
+ });
104
+ mockIntegrationRepository.integrations.set('test-integration', integration);
105
+ });
106
+
107
+ describe('execute()', () => {
108
+ test('successfully adds API module to integration', async () => {
109
+ const request = {
110
+ integrationName: 'test-integration',
111
+ moduleName: 'salesforce',
112
+ moduleVersion: '1.0.0',
113
+ source: 'local'
114
+ };
115
+
116
+ const result = await useCase.execute(request);
117
+
118
+ expect(result.success).toBe(true);
119
+ expect(result.message).toContain("API module 'salesforce' added");
120
+ expect(result.integration.apiModules).toHaveLength(1);
121
+ expect(result.integration.apiModules[0].name).toBe('salesforce');
122
+ expect(mockIntegrationRepository.saveCalled).toBe(true);
123
+ expect(mockUnitOfWork.committed).toBe(true);
124
+ });
125
+
126
+ test('adds module with correct version', async () => {
127
+ const request = {
128
+ integrationName: 'test-integration',
129
+ moduleName: 'salesforce',
130
+ moduleVersion: '2.3.0'
131
+ };
132
+
133
+ const result = await useCase.execute(request);
134
+
135
+ expect(result.success).toBe(true);
136
+ expect(result.integration.apiModules[0].version).toBe('2.3.0');
137
+ });
138
+
139
+ test('defaults version to 1.0.0 when not provided', async () => {
140
+ const request = {
141
+ integrationName: 'test-integration',
142
+ moduleName: 'salesforce'
143
+ };
144
+
145
+ const result = await useCase.execute(request);
146
+
147
+ expect(result.success).toBe(true);
148
+ expect(result.integration.apiModules[0].version).toBe('1.0.0');
149
+ });
150
+
151
+ test('defaults source to local when not provided', async () => {
152
+ const request = {
153
+ integrationName: 'test-integration',
154
+ moduleName: 'salesforce'
155
+ };
156
+
157
+ const result = await useCase.execute(request);
158
+
159
+ expect(result.success).toBe(true);
160
+ expect(result.integration.apiModules[0].source).toBe('local');
161
+ });
162
+
163
+ test('allows custom source', async () => {
164
+ const request = {
165
+ integrationName: 'test-integration',
166
+ moduleName: 'salesforce',
167
+ source: 'npm'
168
+ };
169
+
170
+ const result = await useCase.execute(request);
171
+
172
+ expect(result.success).toBe(true);
173
+ expect(result.integration.apiModules[0].source).toBe('npm');
174
+ });
175
+
176
+ test('throws error when integration not found', async () => {
177
+ const request = {
178
+ integrationName: 'non-existent',
179
+ moduleName: 'salesforce'
180
+ };
181
+
182
+ await expect(useCase.execute(request)).rejects.toThrow(ValidationException);
183
+ await expect(useCase.execute(request)).rejects.toThrow("Integration 'non-existent' not found");
184
+ expect(mockUnitOfWork.rolledBack).toBe(true);
185
+ });
186
+
187
+ test('throws error when API module does not exist', async () => {
188
+ const request = {
189
+ integrationName: 'test-integration',
190
+ moduleName: 'non-existent-module'
191
+ };
192
+
193
+ await expect(useCase.execute(request)).rejects.toThrow(ValidationException);
194
+ await expect(useCase.execute(request)).rejects.toThrow("API module 'non-existent-module' not found");
195
+ await expect(useCase.execute(request)).rejects.toThrow('Create it first');
196
+ expect(mockUnitOfWork.rolledBack).toBe(true);
197
+ });
198
+
199
+ test('throws error when API module already added', async () => {
200
+ // First add
201
+ await useCase.execute({
202
+ integrationName: 'test-integration',
203
+ moduleName: 'salesforce'
204
+ });
205
+
206
+ // Try to add again
207
+ const request = {
208
+ integrationName: 'test-integration',
209
+ moduleName: 'salesforce'
210
+ };
211
+
212
+ await expect(useCase.execute(request)).rejects.toThrow();
213
+ await expect(useCase.execute(request)).rejects.toThrow('already added');
214
+ expect(mockUnitOfWork.rolledBack).toBe(true);
215
+ });
216
+
217
+ test('calls validator with correct parameters', async () => {
218
+ const request = {
219
+ integrationName: 'test-integration',
220
+ moduleName: 'salesforce',
221
+ moduleVersion: '1.5.0'
222
+ };
223
+
224
+ await useCase.execute(request);
225
+
226
+ expect(mockValidator.validateCalled).toBe(true);
227
+ });
228
+
229
+ test('throws error when validation fails', async () => {
230
+ mockValidator.shouldFail = true;
231
+ mockValidator.errors = ['Some validation error'];
232
+
233
+ const request = {
234
+ integrationName: 'test-integration',
235
+ moduleName: 'salesforce'
236
+ };
237
+
238
+ await expect(useCase.execute(request)).rejects.toThrow(ValidationException);
239
+ expect(mockUnitOfWork.rolledBack).toBe(true);
240
+ });
241
+
242
+ test('commits transaction only after all operations succeed', async () => {
243
+ const request = {
244
+ integrationName: 'test-integration',
245
+ moduleName: 'salesforce'
246
+ };
247
+
248
+ await useCase.execute(request);
249
+
250
+ expect(mockIntegrationRepository.saveCalled).toBe(true);
251
+ expect(mockUnitOfWork.committed).toBe(true);
252
+ expect(mockUnitOfWork.rolledBack).toBe(false);
253
+ });
254
+
255
+ test('rollsback on repository save error', async () => {
256
+ mockIntegrationRepository.save = async () => {
257
+ throw new Error('Database error');
258
+ };
259
+
260
+ const request = {
261
+ integrationName: 'test-integration',
262
+ moduleName: 'salesforce'
263
+ };
264
+
265
+ await expect(useCase.execute(request)).rejects.toThrow('Database error');
266
+ expect(mockUnitOfWork.rolledBack).toBe(true);
267
+ });
268
+
269
+ test('allows adding multiple different API modules', async () => {
270
+ await useCase.execute({
271
+ integrationName: 'test-integration',
272
+ moduleName: 'salesforce'
273
+ });
274
+
275
+ const result = await useCase.execute({
276
+ integrationName: 'test-integration',
277
+ moduleName: 'stripe'
278
+ });
279
+
280
+ expect(result.success).toBe(true);
281
+ expect(result.integration.apiModules).toHaveLength(2);
282
+ expect(result.integration.apiModules.map(m => m.name)).toContain('salesforce');
283
+ expect(result.integration.apiModules.map(m => m.name)).toContain('stripe');
284
+ });
285
+
286
+ test('returns integration object with updated apiModules', async () => {
287
+ const request = {
288
+ integrationName: 'test-integration',
289
+ moduleName: 'salesforce',
290
+ moduleVersion: '1.0.0',
291
+ source: 'local'
292
+ };
293
+
294
+ const result = await useCase.execute(request);
295
+
296
+ expect(result.integration).toHaveProperty('id');
297
+ expect(result.integration).toHaveProperty('name');
298
+ expect(result.integration).toHaveProperty('version');
299
+ expect(result.integration).toHaveProperty('apiModules');
300
+ expect(result.integration.apiModules).toBeInstanceOf(Array);
301
+ });
302
+
303
+ test('preserves existing integration data', async () => {
304
+ const integration = Integration.create({
305
+ name: 'salesforce-sync',
306
+ type: 'sync',
307
+ displayName: 'Salesforce Sync',
308
+ description: 'Sync data with Salesforce',
309
+ category: 'CRM'
310
+ });
311
+ mockIntegrationRepository.integrations.set('salesforce-sync', integration);
312
+
313
+ const request = {
314
+ integrationName: 'salesforce-sync',
315
+ moduleName: 'salesforce'
316
+ };
317
+
318
+ const result = await useCase.execute(request);
319
+
320
+ expect(result.integration.name).toBe('salesforce-sync');
321
+ expect(result.integration.type).toBe('sync');
322
+ expect(result.integration.displayName).toBe('Salesforce Sync');
323
+ expect(result.integration.description).toBe('Sync data with Salesforce');
324
+ });
325
+ });
326
+ });
@@ -0,0 +1,337 @@
1
+ const {CreateApiModuleUseCase} = require('../../../application/use-cases/CreateApiModuleUseCase');
2
+ const {ApiModule} = require('../../../domain/entities/ApiModule');
3
+ const {DomainException, ValidationException} = require('../../../domain/exceptions/DomainException');
4
+
5
+ // Mock dependencies
6
+ class MockApiModuleRepository {
7
+ constructor() {
8
+ this.modules = new Map();
9
+ this.saveCalled = false;
10
+ }
11
+
12
+ async save(apiModule) {
13
+ this.saveCalled = true;
14
+ this.modules.set(apiModule.name, apiModule);
15
+ return apiModule;
16
+ }
17
+
18
+ async findByName(name) {
19
+ return this.modules.get(name) || null;
20
+ }
21
+
22
+ async exists(name) {
23
+ return this.modules.has(name);
24
+ }
25
+
26
+ async list() {
27
+ return Array.from(this.modules.values());
28
+ }
29
+ }
30
+
31
+ class MockAppDefinitionRepository {
32
+ constructor() {
33
+ this.appDef = null;
34
+ this.loadCalled = false;
35
+ this.saveCalled = false;
36
+ }
37
+
38
+ async load() {
39
+ this.loadCalled = true;
40
+ return this.appDef;
41
+ }
42
+
43
+ async save(appDef) {
44
+ this.saveCalled = true;
45
+ this.appDef = appDef;
46
+ return appDef;
47
+ }
48
+
49
+ async exists() {
50
+ return this.appDef !== null;
51
+ }
52
+ }
53
+
54
+ class MockUnitOfWork {
55
+ constructor() {
56
+ this.committed = false;
57
+ this.rolledBack = false;
58
+ this.operations = [];
59
+ }
60
+
61
+ addOperation(operation) {
62
+ this.operations.push(operation);
63
+ }
64
+
65
+ async commit() {
66
+ this.committed = true;
67
+ }
68
+
69
+ async rollback() {
70
+ this.rolledBack = true;
71
+ }
72
+ }
73
+
74
+ describe('CreateApiModuleUseCase', () => {
75
+ let useCase;
76
+ let mockApiModuleRepository;
77
+ let mockAppDefinitionRepository;
78
+ let mockUnitOfWork;
79
+
80
+ beforeEach(() => {
81
+ mockApiModuleRepository = new MockApiModuleRepository();
82
+ mockAppDefinitionRepository = new MockAppDefinitionRepository();
83
+ mockUnitOfWork = new MockUnitOfWork();
84
+
85
+ useCase = new CreateApiModuleUseCase(
86
+ mockApiModuleRepository,
87
+ mockUnitOfWork,
88
+ mockAppDefinitionRepository
89
+ );
90
+ });
91
+
92
+ describe('execute()', () => {
93
+ test('successfully creates a new API module with required fields', async () => {
94
+ const request = {
95
+ name: 'salesforce',
96
+ displayName: 'Salesforce',
97
+ description: 'Salesforce CRM API',
98
+ baseUrl: 'https://api.salesforce.com',
99
+ authType: 'oauth2',
100
+ apiVersion: 'v1'
101
+ };
102
+
103
+ const result = await useCase.execute(request);
104
+
105
+ expect(result.success).toBe(true);
106
+ expect(result.apiModule.name).toBe('salesforce');
107
+ expect(result.apiModule.displayName).toBe('Salesforce');
108
+ expect(mockApiModuleRepository.saveCalled).toBe(true);
109
+ expect(mockUnitOfWork.committed).toBe(true);
110
+ });
111
+
112
+ test('creates module with minimal required fields', async () => {
113
+ const request = {
114
+ name: 'stripe-api',
115
+ authType: 'api-key'
116
+ };
117
+
118
+ const result = await useCase.execute(request);
119
+
120
+ expect(result.success).toBe(true);
121
+ expect(result.apiModule.name).toBe('stripe-api');
122
+ expect(result.apiModule.displayName).toBe('Stripe Api');
123
+ expect(result.apiModule.apiConfig.authType).toBe('api-key');
124
+ });
125
+
126
+ test('creates module with entities', async () => {
127
+ const request = {
128
+ name: 'salesforce',
129
+ authType: 'oauth2',
130
+ entities: {
131
+ account: {
132
+ label: 'Salesforce Account',
133
+ required: true,
134
+ fields: ['id', 'name']
135
+ }
136
+ }
137
+ };
138
+
139
+ const result = await useCase.execute(request);
140
+
141
+ expect(result.success).toBe(true);
142
+ expect(result.apiModule.entities).toHaveProperty('account');
143
+ expect(result.apiModule.entities.account.label).toBe('Salesforce Account');
144
+ });
145
+
146
+ test('creates module with OAuth scopes', async () => {
147
+ const request = {
148
+ name: 'salesforce',
149
+ authType: 'oauth2',
150
+ scopes: ['read:accounts', 'write:accounts']
151
+ };
152
+
153
+ const result = await useCase.execute(request);
154
+
155
+ expect(result.success).toBe(true);
156
+ expect(result.apiModule.scopes).toEqual(['read:accounts', 'write:accounts']);
157
+ });
158
+
159
+ test('creates module with credentials', async () => {
160
+ const request = {
161
+ name: 'salesforce',
162
+ authType: 'oauth2',
163
+ credentials: [
164
+ {
165
+ name: 'clientId',
166
+ type: 'string',
167
+ required: true,
168
+ description: 'OAuth client ID'
169
+ }
170
+ ]
171
+ };
172
+
173
+ const result = await useCase.execute(request);
174
+
175
+ expect(result.success).toBe(true);
176
+ expect(result.apiModule.credentials).toHaveLength(1);
177
+ expect(result.apiModule.credentials[0].name).toBe('clientId');
178
+ });
179
+
180
+ test('registers API module in app definition if available', async () => {
181
+ // Setup app definition
182
+ const AppDefinition = require('../../../domain/entities/AppDefinition').AppDefinition;
183
+ mockAppDefinitionRepository.appDef = AppDefinition.create({
184
+ name: 'test-app',
185
+ version: '1.0.0'
186
+ });
187
+
188
+ const request = {
189
+ name: 'salesforce',
190
+ authType: 'oauth2'
191
+ };
192
+
193
+ const result = await useCase.execute(request);
194
+
195
+ expect(result.success).toBe(true);
196
+ expect(mockAppDefinitionRepository.loadCalled).toBe(true);
197
+ expect(mockAppDefinitionRepository.saveCalled).toBe(true);
198
+ expect(mockAppDefinitionRepository.appDef.hasApiModule('salesforce')).toBe(true);
199
+ });
200
+
201
+ test('succeeds even if app definition does not exist', async () => {
202
+ mockAppDefinitionRepository.appDef = null;
203
+
204
+ const request = {
205
+ name: 'salesforce',
206
+ authType: 'oauth2'
207
+ };
208
+
209
+ const result = await useCase.execute(request);
210
+
211
+ expect(result.success).toBe(true);
212
+ expect(mockAppDefinitionRepository.loadCalled).toBe(true);
213
+ expect(mockAppDefinitionRepository.saveCalled).toBe(false);
214
+ });
215
+
216
+ test('throws validation error when name is missing', async () => {
217
+ const request = {
218
+ authType: 'oauth2'
219
+ };
220
+
221
+ await expect(useCase.execute(request)).rejects.toThrow(DomainException);
222
+ expect(mockUnitOfWork.rolledBack).toBe(true);
223
+ });
224
+
225
+ test('throws validation error when name is invalid', async () => {
226
+ const request = {
227
+ name: 'Invalid Name!',
228
+ authType: 'oauth2'
229
+ };
230
+
231
+ await expect(useCase.execute(request)).rejects.toThrow();
232
+ expect(mockUnitOfWork.rolledBack).toBe(true);
233
+ });
234
+
235
+ test('defaults authType to oauth2 when missing', async () => {
236
+ const request = {
237
+ name: 'salesforce'
238
+ };
239
+
240
+ const result = await useCase.execute(request);
241
+
242
+ expect(result.success).toBe(true);
243
+ expect(result.apiModule.apiConfig.authType).toBe('oauth2');
244
+ });
245
+
246
+ test('rollsback on repository error', async () => {
247
+ mockApiModuleRepository.save = async () => {
248
+ throw new Error('Database error');
249
+ };
250
+
251
+ const request = {
252
+ name: 'salesforce',
253
+ authType: 'oauth2'
254
+ };
255
+
256
+ await expect(useCase.execute(request)).rejects.toThrow('Database error');
257
+ expect(mockUnitOfWork.rolledBack).toBe(true);
258
+ });
259
+
260
+ test('succeeds even if app definition registration fails', async () => {
261
+ // Setup app definition that will fail
262
+ const AppDefinition = require('../../../domain/entities/AppDefinition').AppDefinition;
263
+ mockAppDefinitionRepository.appDef = AppDefinition.create({
264
+ name: 'test-app',
265
+ version: '1.0.0'
266
+ });
267
+
268
+ // Make save throw error
269
+ const consoleSpy = jest.spyOn(console, 'warn').mockImplementation();
270
+ mockAppDefinitionRepository.save = async () => {
271
+ throw new Error('Failed to update app definition');
272
+ };
273
+
274
+ const request = {
275
+ name: 'salesforce',
276
+ authType: 'oauth2'
277
+ };
278
+
279
+ const result = await useCase.execute(request);
280
+
281
+ expect(result.success).toBe(true);
282
+ expect(consoleSpy).toHaveBeenCalledWith(
283
+ expect.stringContaining('Could not register API module'),
284
+ expect.any(String)
285
+ );
286
+ expect(mockUnitOfWork.committed).toBe(true);
287
+ expect(mockUnitOfWork.rolledBack).toBe(false);
288
+
289
+ consoleSpy.mockRestore();
290
+ });
291
+
292
+ test('commits transaction only after all operations succeed', async () => {
293
+ const AppDefinition = require('../../../domain/entities/AppDefinition').AppDefinition;
294
+ mockAppDefinitionRepository.appDef = AppDefinition.create({
295
+ name: 'test-app',
296
+ version: '1.0.0'
297
+ });
298
+
299
+ const request = {
300
+ name: 'salesforce',
301
+ authType: 'oauth2'
302
+ };
303
+
304
+ const result = await useCase.execute(request);
305
+
306
+ expect(result.success).toBe(true);
307
+ expect(mockApiModuleRepository.saveCalled).toBe(true);
308
+ expect(mockAppDefinitionRepository.saveCalled).toBe(true);
309
+ expect(mockUnitOfWork.committed).toBe(true);
310
+ expect(mockUnitOfWork.rolledBack).toBe(false);
311
+ });
312
+
313
+ test('returns full API module object', async () => {
314
+ const request = {
315
+ name: 'salesforce',
316
+ displayName: 'Salesforce',
317
+ description: 'Salesforce CRM API',
318
+ authType: 'oauth2',
319
+ baseUrl: 'https://api.salesforce.com',
320
+ version: '2.0.0'
321
+ };
322
+
323
+ const result = await useCase.execute(request);
324
+
325
+ expect(result.apiModule).toHaveProperty('name');
326
+ expect(result.apiModule).toHaveProperty('version');
327
+ expect(result.apiModule).toHaveProperty('displayName');
328
+ expect(result.apiModule).toHaveProperty('description');
329
+ expect(result.apiModule).toHaveProperty('apiConfig');
330
+ expect(result.apiModule).toHaveProperty('entities');
331
+ expect(result.apiModule).toHaveProperty('scopes');
332
+ expect(result.apiModule).toHaveProperty('credentials');
333
+ expect(result.apiModule).toHaveProperty('createdAt');
334
+ expect(result.apiModule).toHaveProperty('updatedAt');
335
+ });
336
+ });
337
+ });