@microsoft/power-apps-cli 0.6.1 → 0.6.7

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 (313) hide show
  1. package/lib/ArgumentProvider.d.ts +9 -4
  2. package/lib/ArgumentProvider.d.ts.map +1 -1
  3. package/lib/ArgumentProvider.js +36 -15
  4. package/lib/ArgumentProvider.js.map +1 -1
  5. package/lib/Authentication/NodeMsalAuthenticationProvider.d.ts +2 -2
  6. package/lib/Authentication/NodeMsalAuthenticationProvider.d.ts.map +1 -1
  7. package/lib/Authentication/NodeMsalAuthenticationProvider.js +2 -3
  8. package/lib/Authentication/NodeMsalAuthenticationProvider.js.map +1 -1
  9. package/lib/Authentication/PacCliAuthenticationProvider.d.ts +2 -1
  10. package/lib/Authentication/PacCliAuthenticationProvider.d.ts.map +1 -1
  11. package/lib/Authentication/PacCliAuthenticationProvider.js +1 -1
  12. package/lib/Authentication/PacCliAuthenticationProvider.js.map +1 -1
  13. package/lib/Authentication/ServicePrincipalAuthenticationProvider.d.ts +2 -2
  14. package/lib/Authentication/ServicePrincipalAuthenticationProvider.d.ts.map +1 -1
  15. package/lib/Authentication/ServicePrincipalAuthenticationProvider.js +3 -3
  16. package/lib/Authentication/ServicePrincipalAuthenticationProvider.js.map +1 -1
  17. package/lib/Cli.d.ts +0 -3
  18. package/lib/Cli.d.ts.map +1 -1
  19. package/lib/Cli.js +16 -30
  20. package/lib/Cli.js.map +1 -1
  21. package/lib/CliUtils.js +3 -3
  22. package/lib/CliUtils.js.map +1 -1
  23. package/lib/HttpClient/CliHttpClient.d.ts.map +1 -1
  24. package/lib/HttpClient/CliHttpClient.js +2 -1
  25. package/lib/HttpClient/CliHttpClient.js.map +1 -1
  26. package/lib/Logger/CliLogger.d.ts +7 -1
  27. package/lib/Logger/CliLogger.d.ts.map +1 -1
  28. package/lib/Logger/CliLogger.js +45 -24
  29. package/lib/Logger/CliLogger.js.map +1 -1
  30. package/lib/Types/Cli.types.d.ts +2 -2
  31. package/lib/Types/Cli.types.d.ts.map +1 -1
  32. package/lib/Utils/EnhanceNetworkError.d.ts +14 -0
  33. package/lib/Utils/EnhanceNetworkError.d.ts.map +1 -0
  34. package/lib/Utils/EnhanceNetworkError.js +59 -0
  35. package/lib/Utils/EnhanceNetworkError.js.map +1 -0
  36. package/lib/Verbs/AddDataSource.d.ts +1 -2
  37. package/lib/Verbs/AddDataSource.d.ts.map +1 -1
  38. package/lib/Verbs/AddDataSource.js +3 -1
  39. package/lib/Verbs/AddDataSource.js.map +1 -1
  40. package/lib/Verbs/DeleteDataSource.d.ts +1 -2
  41. package/lib/Verbs/DeleteDataSource.d.ts.map +1 -1
  42. package/lib/Verbs/DeleteDataSource.js +3 -1
  43. package/lib/Verbs/DeleteDataSource.js.map +1 -1
  44. package/lib/Verbs/Init.d.ts.map +1 -1
  45. package/lib/Verbs/Init.js +4 -1
  46. package/lib/Verbs/Init.js.map +1 -1
  47. package/lib/Verbs/ListConnectionReferences.d.ts +1 -2
  48. package/lib/Verbs/ListConnectionReferences.d.ts.map +1 -1
  49. package/lib/Verbs/ListConnectionReferences.js +3 -1
  50. package/lib/Verbs/ListConnectionReferences.js.map +1 -1
  51. package/lib/Verbs/ListDatasets.d.ts +1 -2
  52. package/lib/Verbs/ListDatasets.d.ts.map +1 -1
  53. package/lib/Verbs/ListDatasets.js +3 -1
  54. package/lib/Verbs/ListDatasets.js.map +1 -1
  55. package/lib/Verbs/ListEnvironmentVariables.d.ts +1 -2
  56. package/lib/Verbs/ListEnvironmentVariables.d.ts.map +1 -1
  57. package/lib/Verbs/ListEnvironmentVariables.js +3 -1
  58. package/lib/Verbs/ListEnvironmentVariables.js.map +1 -1
  59. package/lib/Verbs/ListSqlStoredProcedures.d.ts +1 -2
  60. package/lib/Verbs/ListSqlStoredProcedures.d.ts.map +1 -1
  61. package/lib/Verbs/ListSqlStoredProcedures.js +3 -1
  62. package/lib/Verbs/ListSqlStoredProcedures.js.map +1 -1
  63. package/lib/Verbs/ListTables.d.ts +1 -2
  64. package/lib/Verbs/ListTables.d.ts.map +1 -1
  65. package/lib/Verbs/ListTables.js +3 -1
  66. package/lib/Verbs/ListTables.js.map +1 -1
  67. package/lib/Verbs/Push.d.ts +1 -2
  68. package/lib/Verbs/Push.d.ts.map +1 -1
  69. package/lib/Verbs/Push.js +6 -4
  70. package/lib/Verbs/Push.js.map +1 -1
  71. package/lib/Verbs/Run.d.ts.map +1 -1
  72. package/lib/Verbs/Run.js +9 -8
  73. package/lib/Verbs/Run.js.map +1 -1
  74. package/lib/Verbs/VerbConstants.d.ts +19 -1
  75. package/lib/Verbs/VerbConstants.d.ts.map +1 -1
  76. package/lib/Verbs/VerbConstants.js +23 -5
  77. package/lib/Verbs/VerbConstants.js.map +1 -1
  78. package/lib/__tests__/E2eTests/basicSetup/loggingConfiguration.test.d.ts +5 -0
  79. package/lib/__tests__/E2eTests/basicSetup/loggingConfiguration.test.d.ts.map +1 -0
  80. package/lib/__tests__/E2eTests/basicSetup/loggingConfiguration.test.js +246 -0
  81. package/lib/__tests__/E2eTests/basicSetup/loggingConfiguration.test.js.map +1 -0
  82. package/lib/__tests__/E2eTests/cliUsability/cliHelp.test.d.ts +5 -0
  83. package/lib/__tests__/E2eTests/cliUsability/cliHelp.test.d.ts.map +1 -0
  84. package/lib/__tests__/E2eTests/cliUsability/cliHelp.test.js +162 -0
  85. package/lib/__tests__/E2eTests/cliUsability/cliHelp.test.js.map +1 -0
  86. package/lib/__tests__/E2eTests/cliUsability/logoutReauth.test.d.ts +5 -0
  87. package/lib/__tests__/E2eTests/cliUsability/logoutReauth.test.d.ts.map +1 -0
  88. package/lib/__tests__/E2eTests/cliUsability/logoutReauth.test.js +115 -0
  89. package/lib/__tests__/E2eTests/cliUsability/logoutReauth.test.js.map +1 -0
  90. package/lib/__tests__/E2eTests/cliUsability/missingParameters.test.d.ts +5 -0
  91. package/lib/__tests__/E2eTests/cliUsability/missingParameters.test.d.ts.map +1 -0
  92. package/lib/__tests__/E2eTests/cliUsability/missingParameters.test.js +189 -0
  93. package/lib/__tests__/E2eTests/cliUsability/missingParameters.test.js.map +1 -0
  94. package/lib/__tests__/E2eTests/commonUserWorkflowSmokeTest.test.js +8 -6
  95. package/lib/__tests__/E2eTests/commonUserWorkflowSmokeTest.test.js.map +1 -1
  96. package/lib/__tests__/E2eTests/e2eConfig.d.ts +95 -0
  97. package/lib/__tests__/E2eTests/e2eConfig.d.ts.map +1 -0
  98. package/lib/__tests__/E2eTests/e2eConfig.js +122 -0
  99. package/lib/__tests__/E2eTests/e2eConfig.js.map +1 -0
  100. package/lib/__tests__/UnitTests/CliLogger.test.js +34 -24
  101. package/lib/__tests__/UnitTests/CliLogger.test.js.map +1 -1
  102. package/lib/__tests__/UnitTests/CliUtils.spec.js +2 -2
  103. package/lib/__tests__/UnitTests/CliUtils.spec.js.map +1 -1
  104. package/lib/__tests__/UnitTests/ListEnvironmentVariables.spec.js +5 -5
  105. package/lib/__tests__/UnitTests/ListEnvironmentVariables.spec.js.map +1 -1
  106. package/lib/__tests__/UnitTests/ServicePrincipalAuthenticationProvider.spec.js +3 -3
  107. package/lib/__tests__/UnitTests/ServicePrincipalAuthenticationProvider.spec.js.map +1 -1
  108. package/lib/__tests__/UnitTests/enhanceNetworkError.test.d.ts +5 -0
  109. package/lib/__tests__/UnitTests/enhanceNetworkError.test.d.ts.map +1 -0
  110. package/lib/__tests__/UnitTests/enhanceNetworkError.test.js +177 -0
  111. package/lib/__tests__/UnitTests/enhanceNetworkError.test.js.map +1 -0
  112. package/lib/__tests__/helpers/e2eTestHelpers.d.ts.map +1 -1
  113. package/lib/__tests__/helpers/e2eTestHelpers.js +16 -0
  114. package/lib/__tests__/helpers/e2eTestHelpers.js.map +1 -1
  115. package/lib/__tests__/helpers/testHelpers.d.ts +146 -30
  116. package/lib/__tests__/helpers/testHelpers.d.ts.map +1 -1
  117. package/lib/__tests__/helpers/testHelpers.js +245 -74
  118. package/lib/__tests__/helpers/testHelpers.js.map +1 -1
  119. package/lib/__tests__/mocks/CliLogger.mocks.d.ts +7 -0
  120. package/lib/__tests__/mocks/CliLogger.mocks.d.ts.map +1 -0
  121. package/lib/__tests__/mocks/CliLogger.mocks.js +22 -0
  122. package/lib/__tests__/mocks/CliLogger.mocks.js.map +1 -0
  123. package/lib-cjs/ArgumentProvider.d.ts +9 -4
  124. package/lib-cjs/ArgumentProvider.d.ts.map +1 -1
  125. package/lib-cjs/ArgumentProvider.js +44 -16
  126. package/lib-cjs/ArgumentProvider.js.map +1 -1
  127. package/lib-cjs/Authentication/NodeMsalAuthenticationProvider.d.ts +2 -2
  128. package/lib-cjs/Authentication/NodeMsalAuthenticationProvider.d.ts.map +1 -1
  129. package/lib-cjs/Authentication/NodeMsalAuthenticationProvider.js +3 -4
  130. package/lib-cjs/Authentication/NodeMsalAuthenticationProvider.js.map +1 -1
  131. package/lib-cjs/Authentication/PacCliAuthenticationProvider.d.ts +2 -1
  132. package/lib-cjs/Authentication/PacCliAuthenticationProvider.d.ts.map +1 -1
  133. package/lib-cjs/Authentication/PacCliAuthenticationProvider.js +1 -1
  134. package/lib-cjs/Authentication/PacCliAuthenticationProvider.js.map +1 -1
  135. package/lib-cjs/Authentication/ServicePrincipalAuthenticationProvider.d.ts +2 -2
  136. package/lib-cjs/Authentication/ServicePrincipalAuthenticationProvider.d.ts.map +1 -1
  137. package/lib-cjs/Authentication/ServicePrincipalAuthenticationProvider.js +3 -4
  138. package/lib-cjs/Authentication/ServicePrincipalAuthenticationProvider.js.map +1 -1
  139. package/lib-cjs/Cli.d.ts +0 -3
  140. package/lib-cjs/Cli.d.ts.map +1 -1
  141. package/lib-cjs/Cli.js +43 -63
  142. package/lib-cjs/Cli.js.map +1 -1
  143. package/lib-cjs/CliUtils.js +3 -3
  144. package/lib-cjs/CliUtils.js.map +1 -1
  145. package/lib-cjs/HttpClient/CliHttpClient.d.ts.map +1 -1
  146. package/lib-cjs/HttpClient/CliHttpClient.js +2 -1
  147. package/lib-cjs/HttpClient/CliHttpClient.js.map +1 -1
  148. package/lib-cjs/Logger/CliLogger.d.ts +7 -1
  149. package/lib-cjs/Logger/CliLogger.d.ts.map +1 -1
  150. package/lib-cjs/Logger/CliLogger.js +58 -30
  151. package/lib-cjs/Logger/CliLogger.js.map +1 -1
  152. package/lib-cjs/Types/Cli.types.d.ts +2 -2
  153. package/lib-cjs/Types/Cli.types.d.ts.map +1 -1
  154. package/lib-cjs/Utils/EnhanceNetworkError.d.ts +13 -0
  155. package/lib-cjs/Utils/EnhanceNetworkError.d.ts.map +1 -0
  156. package/lib-cjs/Utils/EnhanceNetworkError.js +63 -0
  157. package/lib-cjs/Utils/EnhanceNetworkError.js.map +1 -0
  158. package/lib-cjs/Verbs/AddDataSource.d.ts +1 -2
  159. package/lib-cjs/Verbs/AddDataSource.d.ts.map +1 -1
  160. package/lib-cjs/Verbs/AddDataSource.js +3 -2
  161. package/lib-cjs/Verbs/AddDataSource.js.map +1 -1
  162. package/lib-cjs/Verbs/DeleteDataSource.d.ts +1 -2
  163. package/lib-cjs/Verbs/DeleteDataSource.d.ts.map +1 -1
  164. package/lib-cjs/Verbs/DeleteDataSource.js +3 -2
  165. package/lib-cjs/Verbs/DeleteDataSource.js.map +1 -1
  166. package/lib-cjs/Verbs/Init.d.ts.map +1 -1
  167. package/lib-cjs/Verbs/Init.js +15 -9
  168. package/lib-cjs/Verbs/Init.js.map +1 -1
  169. package/lib-cjs/Verbs/ListConnectionReferences.d.ts +1 -2
  170. package/lib-cjs/Verbs/ListConnectionReferences.d.ts.map +1 -1
  171. package/lib-cjs/Verbs/ListConnectionReferences.js +3 -2
  172. package/lib-cjs/Verbs/ListConnectionReferences.js.map +1 -1
  173. package/lib-cjs/Verbs/ListDatasets.d.ts +1 -2
  174. package/lib-cjs/Verbs/ListDatasets.d.ts.map +1 -1
  175. package/lib-cjs/Verbs/ListDatasets.js +3 -2
  176. package/lib-cjs/Verbs/ListDatasets.js.map +1 -1
  177. package/lib-cjs/Verbs/ListEnvironmentVariables.d.ts +1 -2
  178. package/lib-cjs/Verbs/ListEnvironmentVariables.d.ts.map +1 -1
  179. package/lib-cjs/Verbs/ListEnvironmentVariables.js +3 -2
  180. package/lib-cjs/Verbs/ListEnvironmentVariables.js.map +1 -1
  181. package/lib-cjs/Verbs/ListSqlStoredProcedures.d.ts +1 -2
  182. package/lib-cjs/Verbs/ListSqlStoredProcedures.d.ts.map +1 -1
  183. package/lib-cjs/Verbs/ListSqlStoredProcedures.js +3 -2
  184. package/lib-cjs/Verbs/ListSqlStoredProcedures.js.map +1 -1
  185. package/lib-cjs/Verbs/ListTables.d.ts +1 -2
  186. package/lib-cjs/Verbs/ListTables.d.ts.map +1 -1
  187. package/lib-cjs/Verbs/ListTables.js +3 -2
  188. package/lib-cjs/Verbs/ListTables.js.map +1 -1
  189. package/lib-cjs/Verbs/Push.d.ts +1 -2
  190. package/lib-cjs/Verbs/Push.d.ts.map +1 -1
  191. package/lib-cjs/Verbs/Push.js +7 -5
  192. package/lib-cjs/Verbs/Push.js.map +1 -1
  193. package/lib-cjs/Verbs/Run.d.ts.map +1 -1
  194. package/lib-cjs/Verbs/Run.js +9 -8
  195. package/lib-cjs/Verbs/Run.js.map +1 -1
  196. package/lib-cjs/Verbs/VerbConstants.d.ts +19 -1
  197. package/lib-cjs/Verbs/VerbConstants.d.ts.map +1 -1
  198. package/lib-cjs/Verbs/VerbConstants.js +24 -6
  199. package/lib-cjs/Verbs/VerbConstants.js.map +1 -1
  200. package/lib-cjs/__tests__/E2eTests/basicSetup/loggingConfiguration.test.d.ts +4 -0
  201. package/lib-cjs/__tests__/E2eTests/basicSetup/loggingConfiguration.test.d.ts.map +1 -0
  202. package/lib-cjs/__tests__/E2eTests/basicSetup/loggingConfiguration.test.js +271 -0
  203. package/lib-cjs/__tests__/E2eTests/basicSetup/loggingConfiguration.test.js.map +1 -0
  204. package/lib-cjs/__tests__/E2eTests/cliUsability/cliHelp.test.d.ts +4 -0
  205. package/lib-cjs/__tests__/E2eTests/cliUsability/cliHelp.test.d.ts.map +1 -0
  206. package/lib-cjs/__tests__/E2eTests/cliUsability/cliHelp.test.js +187 -0
  207. package/lib-cjs/__tests__/E2eTests/cliUsability/cliHelp.test.js.map +1 -0
  208. package/lib-cjs/__tests__/E2eTests/cliUsability/logoutReauth.test.d.ts +4 -0
  209. package/lib-cjs/__tests__/E2eTests/cliUsability/logoutReauth.test.d.ts.map +1 -0
  210. package/lib-cjs/__tests__/E2eTests/cliUsability/logoutReauth.test.js +140 -0
  211. package/lib-cjs/__tests__/E2eTests/cliUsability/logoutReauth.test.js.map +1 -0
  212. package/lib-cjs/__tests__/E2eTests/cliUsability/missingParameters.test.d.ts +4 -0
  213. package/lib-cjs/__tests__/E2eTests/cliUsability/missingParameters.test.d.ts.map +1 -0
  214. package/lib-cjs/__tests__/E2eTests/cliUsability/missingParameters.test.js +191 -0
  215. package/lib-cjs/__tests__/E2eTests/cliUsability/missingParameters.test.js.map +1 -0
  216. package/lib-cjs/__tests__/E2eTests/commonUserWorkflowSmokeTest.test.js +5 -5
  217. package/lib-cjs/__tests__/E2eTests/commonUserWorkflowSmokeTest.test.js.map +1 -1
  218. package/lib-cjs/__tests__/E2eTests/e2eConfig.d.ts +94 -0
  219. package/lib-cjs/__tests__/E2eTests/e2eConfig.d.ts.map +1 -0
  220. package/lib-cjs/__tests__/E2eTests/e2eConfig.js +149 -0
  221. package/lib-cjs/__tests__/E2eTests/e2eConfig.js.map +1 -0
  222. package/lib-cjs/__tests__/UnitTests/CliLogger.test.js +54 -24
  223. package/lib-cjs/__tests__/UnitTests/CliLogger.test.js.map +1 -1
  224. package/lib-cjs/__tests__/UnitTests/CliUtils.spec.js +2 -2
  225. package/lib-cjs/__tests__/UnitTests/CliUtils.spec.js.map +1 -1
  226. package/lib-cjs/__tests__/UnitTests/ListEnvironmentVariables.spec.js +5 -5
  227. package/lib-cjs/__tests__/UnitTests/ListEnvironmentVariables.spec.js.map +1 -1
  228. package/lib-cjs/__tests__/UnitTests/ServicePrincipalAuthenticationProvider.spec.js +3 -3
  229. package/lib-cjs/__tests__/UnitTests/ServicePrincipalAuthenticationProvider.spec.js.map +1 -1
  230. package/lib-cjs/__tests__/UnitTests/enhanceNetworkError.test.d.ts +4 -0
  231. package/lib-cjs/__tests__/UnitTests/enhanceNetworkError.test.d.ts.map +1 -0
  232. package/lib-cjs/__tests__/UnitTests/enhanceNetworkError.test.js +179 -0
  233. package/lib-cjs/__tests__/UnitTests/enhanceNetworkError.test.js.map +1 -0
  234. package/lib-cjs/__tests__/helpers/e2eTestHelpers.d.ts.map +1 -1
  235. package/lib-cjs/__tests__/helpers/e2eTestHelpers.js +16 -0
  236. package/lib-cjs/__tests__/helpers/e2eTestHelpers.js.map +1 -1
  237. package/lib-cjs/__tests__/helpers/testHelpers.d.ts +146 -30
  238. package/lib-cjs/__tests__/helpers/testHelpers.d.ts.map +1 -1
  239. package/lib-cjs/__tests__/helpers/testHelpers.js +242 -98
  240. package/lib-cjs/__tests__/helpers/testHelpers.js.map +1 -1
  241. package/lib-cjs/__tests__/mocks/CliLogger.mocks.d.ts +6 -0
  242. package/lib-cjs/__tests__/mocks/CliLogger.mocks.d.ts.map +1 -0
  243. package/lib-cjs/__tests__/mocks/CliLogger.mocks.js +26 -0
  244. package/lib-cjs/__tests__/mocks/CliLogger.mocks.js.map +1 -0
  245. package/node_modules/@microsoft/powerapps-data/package.json +3 -3
  246. package/node_modules/@microsoft/powerapps-player-actions/lib/Actions/AddDataSource.js +8 -3
  247. package/node_modules/@microsoft/powerapps-player-actions/lib/Actions/AddDataSource.js.map +1 -1
  248. package/node_modules/@microsoft/powerapps-player-actions/lib/Actions/DeleteDataSource.d.ts.map +1 -1
  249. package/node_modules/@microsoft/powerapps-player-actions/lib/Actions/DeleteDataSource.js +7 -2
  250. package/node_modules/@microsoft/powerapps-player-actions/lib/Actions/DeleteDataSource.js.map +1 -1
  251. package/node_modules/@microsoft/powerapps-player-actions/lib/Actions/ListTables.d.ts +1 -1
  252. package/node_modules/@microsoft/powerapps-player-actions/lib/Actions/ListTables.d.ts.map +1 -1
  253. package/node_modules/@microsoft/powerapps-player-actions/lib/Actions/ListTables.js +11 -2
  254. package/node_modules/@microsoft/powerapps-player-actions/lib/Actions/ListTables.js.map +1 -1
  255. package/node_modules/@microsoft/powerapps-player-actions/lib/Actions/PushApp.d.ts.map +1 -1
  256. package/node_modules/@microsoft/powerapps-player-actions/lib/Actions/PushApp.js +7 -3
  257. package/node_modules/@microsoft/powerapps-player-actions/lib/Actions/PushApp.js.map +1 -1
  258. package/node_modules/@microsoft/powerapps-player-actions/lib/CodeGen/modelServiceGenerator.d.ts +16 -290
  259. package/node_modules/@microsoft/powerapps-player-actions/lib/CodeGen/modelServiceGenerator.d.ts.map +1 -1
  260. package/node_modules/@microsoft/powerapps-player-actions/lib/CodeGen/modelServiceGenerator.js +2266 -2244
  261. package/node_modules/@microsoft/powerapps-player-actions/lib/CodeGen/modelServiceGenerator.js.map +1 -1
  262. package/node_modules/@microsoft/powerapps-player-actions/lib/__tests__/AzureDevOpsModelServiceGenerator.spec.js +11 -3
  263. package/node_modules/@microsoft/powerapps-player-actions/lib/__tests__/AzureDevOpsModelServiceGenerator.spec.js.map +1 -1
  264. package/node_modules/@microsoft/powerapps-player-actions/lib/__tests__/DeleteDataSource.spec.js +3 -5
  265. package/node_modules/@microsoft/powerapps-player-actions/lib/__tests__/DeleteDataSource.spec.js.map +1 -1
  266. package/node_modules/@microsoft/powerapps-player-actions/lib/__tests__/ModelServiceGenerator.spec.js +110 -21
  267. package/node_modules/@microsoft/powerapps-player-actions/lib/__tests__/ModelServiceGenerator.spec.js.map +1 -1
  268. package/node_modules/@microsoft/powerapps-player-actions/lib/__tests__/arrayTypeGeneration.spec.js +6 -2
  269. package/node_modules/@microsoft/powerapps-player-actions/lib/__tests__/arrayTypeGeneration.spec.js.map +1 -1
  270. package/node_modules/@microsoft/powerapps-player-actions/lib-cjs/Actions/AddDataSource.js +7 -2
  271. package/node_modules/@microsoft/powerapps-player-actions/lib-cjs/Actions/AddDataSource.js.map +1 -1
  272. package/node_modules/@microsoft/powerapps-player-actions/lib-cjs/Actions/DeleteDataSource.d.ts.map +1 -1
  273. package/node_modules/@microsoft/powerapps-player-actions/lib-cjs/Actions/DeleteDataSource.js +6 -1
  274. package/node_modules/@microsoft/powerapps-player-actions/lib-cjs/Actions/DeleteDataSource.js.map +1 -1
  275. package/node_modules/@microsoft/powerapps-player-actions/lib-cjs/Actions/ListTables.d.ts +1 -1
  276. package/node_modules/@microsoft/powerapps-player-actions/lib-cjs/Actions/ListTables.d.ts.map +1 -1
  277. package/node_modules/@microsoft/powerapps-player-actions/lib-cjs/Actions/ListTables.js +14 -5
  278. package/node_modules/@microsoft/powerapps-player-actions/lib-cjs/Actions/ListTables.js.map +1 -1
  279. package/node_modules/@microsoft/powerapps-player-actions/lib-cjs/Actions/PushApp.d.ts.map +1 -1
  280. package/node_modules/@microsoft/powerapps-player-actions/lib-cjs/Actions/PushApp.js +14 -10
  281. package/node_modules/@microsoft/powerapps-player-actions/lib-cjs/Actions/PushApp.js.map +1 -1
  282. package/node_modules/@microsoft/powerapps-player-actions/lib-cjs/CodeGen/modelServiceGenerator.d.ts +16 -290
  283. package/node_modules/@microsoft/powerapps-player-actions/lib-cjs/CodeGen/modelServiceGenerator.d.ts.map +1 -1
  284. package/node_modules/@microsoft/powerapps-player-actions/lib-cjs/CodeGen/modelServiceGenerator.js +2787 -2765
  285. package/node_modules/@microsoft/powerapps-player-actions/lib-cjs/CodeGen/modelServiceGenerator.js.map +1 -1
  286. package/node_modules/@microsoft/powerapps-player-actions/lib-cjs/__tests__/AzureDevOpsModelServiceGenerator.spec.js +10 -2
  287. package/node_modules/@microsoft/powerapps-player-actions/lib-cjs/__tests__/AzureDevOpsModelServiceGenerator.spec.js.map +1 -1
  288. package/node_modules/@microsoft/powerapps-player-actions/lib-cjs/__tests__/DeleteDataSource.spec.js +2 -4
  289. package/node_modules/@microsoft/powerapps-player-actions/lib-cjs/__tests__/DeleteDataSource.spec.js.map +1 -1
  290. package/node_modules/@microsoft/powerapps-player-actions/lib-cjs/__tests__/ModelServiceGenerator.spec.js +124 -19
  291. package/node_modules/@microsoft/powerapps-player-actions/lib-cjs/__tests__/ModelServiceGenerator.spec.js.map +1 -1
  292. package/node_modules/@microsoft/powerapps-player-actions/lib-cjs/__tests__/arrayTypeGeneration.spec.js +5 -1
  293. package/node_modules/@microsoft/powerapps-player-actions/lib-cjs/__tests__/arrayTypeGeneration.spec.js.map +1 -1
  294. package/node_modules/@microsoft/powerapps-player-actions/package.json +3 -3
  295. package/node_modules/@pa-client/powerapps-player-services/lib/Services/Connectivity/__tests__/ApimService.spec.js +3 -0
  296. package/node_modules/@pa-client/powerapps-player-services/lib/Services/Connectivity/__tests__/ApimService.spec.js.map +1 -1
  297. package/node_modules/@pa-client/powerapps-player-services/lib/Services/PlayerLaunchService/PlayerLaunchService.types.d.ts +13 -0
  298. package/node_modules/@pa-client/powerapps-player-services/lib/Services/PlayerLaunchService/PlayerLaunchService.types.d.ts.map +1 -1
  299. package/node_modules/@pa-client/powerapps-player-services/lib/index.d.ts +2 -2
  300. package/node_modules/@pa-client/powerapps-player-services/lib/index.d.ts.map +1 -1
  301. package/node_modules/@pa-client/powerapps-player-services/lib/index.js +1 -1
  302. package/node_modules/@pa-client/powerapps-player-services/lib/index.js.map +1 -1
  303. package/node_modules/@pa-client/powerapps-player-services/lib-cjs/Services/Connectivity/__tests__/ApimService.spec.js +3 -0
  304. package/node_modules/@pa-client/powerapps-player-services/lib-cjs/Services/Connectivity/__tests__/ApimService.spec.js.map +1 -1
  305. package/node_modules/@pa-client/powerapps-player-services/lib-cjs/Services/PlayerLaunchService/PlayerLaunchService.types.d.ts +13 -0
  306. package/node_modules/@pa-client/powerapps-player-services/lib-cjs/Services/PlayerLaunchService/PlayerLaunchService.types.d.ts.map +1 -1
  307. package/node_modules/@pa-client/powerapps-player-services/lib-cjs/index.d.ts +2 -2
  308. package/node_modules/@pa-client/powerapps-player-services/lib-cjs/index.d.ts.map +1 -1
  309. package/node_modules/@pa-client/powerapps-player-services/lib-cjs/index.js +8 -1
  310. package/node_modules/@pa-client/powerapps-player-services/lib-cjs/index.js.map +1 -1
  311. package/node_modules/@pa-client/powerapps-player-services/package.json +69 -69
  312. package/package.json +4 -4
  313. package/readme.md +286 -1
@@ -1,7 +1,6 @@
1
1
  /*!
2
2
  * Copyright (C) Microsoft Corporation. All rights reserved.
3
3
  */
4
- var _a;
5
4
  import * as path from 'path';
6
5
  import { ParameterPropertyInfo } from '../Common/parameterPropertyInfo';
7
6
  import { getVfs } from '../VfsManager';
@@ -10,7 +9,7 @@ import { extractModelName, formatPropertyName, getServiceName, isValidPropertyNa
10
9
  /**
11
10
  * TypeScript type constants
12
11
  */
13
- export const TypeScriptTypes = {
12
+ const TypeScriptTypes = {
14
13
  STRING: 'string',
15
14
  NUMBER: 'number',
16
15
  BOOLEAN: 'boolean',
@@ -18,598 +17,656 @@ export const TypeScriptTypes = {
18
17
  UNKNOWN: 'unknown',
19
18
  OBJECT: 'Record<string, unknown>',
20
19
  };
20
+ // Configuration constants for output folders
21
+ const GENERATED_FOLDER = 'generated';
22
+ const MODELS_FOLDER = `${GENERATED_FOLDER}/models`;
23
+ const SERVICES_FOLDER = `${GENERATED_FOLDER}/services`;
24
+ const RELATIVE_MODEL_PATH = '../models';
25
+ let _schemaFolderPath;
26
+ let _outputDir;
21
27
  /*
22
- * Main generator utility
23
- * This class provides methods to generate TypeScript models and services from JSON schema files.
24
- *
28
+ * The main entry point for the model service generator.
29
+ * It processes the schema files in the specified folder and generates TypeScript models and services.
25
30
  */
26
- // eslint-disable-next-line @typescript-eslint/no-extraneous-class
27
- export class ModelServiceGenerator {
28
- /*
29
- * The main entry point for the ModelServiceGenerator class.
30
- * It processes the schema files in the specified folder and generates TypeScript models and services.
31
- */
32
- static async run(args, logger) {
33
- const scenario = logger.trackScenario('ModelServiceGenerator.Run', { args });
34
- try {
35
- const vfs = getVfs();
36
- const { isValid, schemaFolderPath, outputDir, schemaFilePath } = this.validateArguments(args);
37
- if (!isValid || !schemaFolderPath || !outputDir) {
38
- return;
39
- }
40
- _a._schemaFolderPath = schemaFolderPath;
41
- _a._outputDir = outputDir;
42
- await this.ensureOutputDirectoryExists(outputDir, vfs);
43
- let schemaFiles = [];
44
- if (schemaFilePath) {
45
- schemaFiles = [schemaFilePath];
46
- }
47
- else {
48
- schemaFiles = await getAllJsonFiles(schemaFolderPath);
49
- }
50
- for (const schemaPath of schemaFiles) {
51
- try {
52
- await this.processSchema(schemaPath, outputDir, vfs);
53
- }
54
- catch (ex) {
55
- const err = ex;
56
- throw new Error(`Error processing schema at path '${schemaPath}': ${err.message}`);
57
- }
58
- }
59
- // Generate index.ts file
60
- await this.generateIndexFile(outputDir, vfs);
61
- scenario.complete();
62
- }
63
- catch (error) {
64
- scenario.failure({ error });
65
- }
66
- finally {
67
- _a._schemaFolderPath = undefined;
68
- _a._outputDir = undefined;
69
- }
70
- }
71
- /**
72
- * The validateArguments method validates the command line arguments.
73
- * It checks if the correct number of arguments is provided and returns an object containing their values.
74
- * It returns {isValid, schemaFolderPath, outputDir}.
75
- */
76
- static validateArguments(args) {
77
- if (args.length < 2) {
78
- return { isValid: false };
79
- }
80
- return {
81
- isValid: true,
82
- schemaFolderPath: args[0],
83
- outputDir: args[1],
84
- schemaFilePath: args[2], // Optional third argument for single schema file
85
- };
86
- }
87
- /*
88
- * The EnsureOutputDirectoryExists method ensures that the output directory exists.
89
- * It creates the directory if it does not exist.
90
- */
91
- static async ensureOutputDirectoryExists(outputDir, vfs) {
92
- if (!(await vfs.exists(outputDir))) {
93
- await vfs.mkdir(outputDir, { recursive: true });
94
- }
95
- }
96
- /**
97
- * The processSchema method processes a single schema file.
98
- * It generates TypeScript models and services based on the schema.
99
- */
100
- static async processSchema(schemaPath, outputDir, vfs) {
101
- // Destructure result from initializeSchemaProcessing (returns: schema, schemaJson, outputDir, modelName, dataSourceName)
102
- const { schema, schemaJson, outputDir: processedOutputDir, modelName, dataSourceName, } = await this.initializeSchemaProcessing(schemaPath, outputDir, vfs);
103
- if (this.isSwaggerSchema(schema)) {
104
- await this.handleSwaggerSchema(modelName, dataSourceName, schema, processedOutputDir, vfs);
105
- }
106
- else if (isSqlStoredProcedure(schema)) {
107
- await this.handleSqlStoredProcedure(modelName, dataSourceName, schema, schemaJson, processedOutputDir, vfs);
31
+ export async function generateModelService(options) {
32
+ const { logger, schemaFolderPath, codeGenPath, schemaFilePath } = options;
33
+ const scenario = logger.trackScenario('ModelServiceGenerator.GenerateModelService', options);
34
+ try {
35
+ const vfs = getVfs();
36
+ if (!schemaFolderPath || !codeGenPath) {
37
+ return;
108
38
  }
109
- else if (isSharepointSchema(schema)) {
110
- await this.handleSharepointSchema(modelName, dataSourceName, schema, schemaJson, processedOutputDir, vfs);
39
+ _schemaFolderPath = schemaFolderPath;
40
+ _outputDir = codeGenPath;
41
+ await ensureOutputDirectoryExists(codeGenPath, vfs);
42
+ let schemaFiles = [];
43
+ if (schemaFilePath) {
44
+ schemaFiles = [schemaFilePath];
111
45
  }
112
46
  else {
113
- await this.handleStandardSchema(modelName, dataSourceName, schema, schemaJson, processedOutputDir, vfs);
47
+ schemaFiles = await getAllJsonFiles(schemaFolderPath);
48
+ }
49
+ for (const schemaPath of schemaFiles) {
50
+ try {
51
+ await processSchema(schemaPath, codeGenPath, vfs);
52
+ }
53
+ catch (ex) {
54
+ const err = ex;
55
+ throw new Error(`Error processing schema at path '${schemaPath}': ${err.message}`);
56
+ }
114
57
  }
58
+ // Generate index.ts file
59
+ await generateIndexFile(codeGenPath, vfs);
60
+ scenario.complete();
115
61
  }
116
- /**
117
- * The getModelNameFromSchema method retrieves the model name from a schema file.
118
- */
119
- static async getModelNameFromSchema(schemaPath, vfs) {
120
- const schema = await this.loadSchemaFromFile(schemaPath, vfs, false);
121
- const { modelName } = extractModelName(schema, schemaPath, vfs);
122
- return modelName;
62
+ catch (error) {
63
+ scenario.failure({ error });
123
64
  }
124
- /**
125
- * The initializeSchemaProcessing method initializes the schema processing.
126
- * Returns an object containing the parsed schema, the schema JSON string, the output directory, model name, and data source name.
127
- */
128
- static async initializeSchemaProcessing(schemaPath, outputDir, vfs) {
129
- const { schema, schemaJson } = await this.loadSchemaFromFile(schemaPath, vfs, true);
130
- await this.ensureOutputDirectoriesExist(outputDir, vfs);
131
- // Ensure schema is a JsonObject for processing
132
- if (typeof schema !== 'object' || schema === null || Array.isArray(schema)) {
133
- throw new Error('Schema must be a valid JSON object');
134
- }
135
- // model name is used for the generated TypeScript interface and file names
136
- // dataSourceName is the logical name of the datasource ex:entity set name for Dataverse
137
- const { modelName, dataSourceName } = extractModelName(schema, schemaPath, vfs);
138
- return {
139
- schema: schema,
140
- schemaJson,
141
- outputDir,
142
- modelName,
143
- dataSourceName,
144
- };
145
- }
146
- static async loadSchemaFromFile(schemaPath, vfs, asObjectWithRaw = false) {
147
- if (!(await vfs.exists(schemaPath))) {
148
- throw new Error(`Schema file not found at path: ${schemaPath}`);
149
- }
150
- const schemaJson = await vfs.readFile(schemaPath, 'utf-8');
151
- const schema = JSON.parse(schemaJson);
152
- if (asObjectWithRaw) {
153
- return { schema, schemaJson };
154
- }
155
- else {
156
- return schema;
157
- }
65
+ finally {
66
+ _schemaFolderPath = undefined;
67
+ _outputDir = undefined;
158
68
  }
159
- /**
160
- * The ensureOutputDirectoriesExist method ensures that the required output directories exist.
161
- * It creates the main output directory, plus 'generated/models' and 'generated/services' subfolders if needed.
162
- */
163
- static async ensureOutputDirectoriesExist(outputDir, vfs) {
69
+ }
70
+ /**
71
+ * Clears and regenerates the model and service files.
72
+ */
73
+ export async function clearAndRegenerateModelService(options) {
74
+ const { schemaFolderPath, codeGenPath, vfs, logger } = options;
75
+ const modelsDir = vfs.join(codeGenPath, MODELS_FOLDER);
76
+ const servicesDir = vfs.join(codeGenPath, SERVICES_FOLDER);
77
+ // Delete Models directory if it exists
78
+ if (await vfs.exists(modelsDir)) {
79
+ await vfs.rmdir(modelsDir);
80
+ }
81
+ // Delete Services directory if it exists
82
+ if (await vfs.exists(servicesDir)) {
83
+ await vfs.rmdir(servicesDir);
84
+ }
85
+ await generateModelService({ schemaFolderPath, codeGenPath, logger });
86
+ }
87
+ /*
88
+ * The EnsureOutputDirectoryExists method ensures that the output directory exists.
89
+ * It creates the directory if it does not exist.
90
+ */
91
+ async function ensureOutputDirectoryExists(outputDir, vfs) {
92
+ if (!(await vfs.exists(outputDir))) {
164
93
  await vfs.mkdir(outputDir, { recursive: true });
165
- await vfs.mkdir(vfs.join(outputDir, this.MODELS_FOLDER), { recursive: true });
166
- await vfs.mkdir(vfs.join(outputDir, this.SERVICES_FOLDER), {
167
- recursive: true,
168
- });
169
94
  }
170
- /**
171
- * The isSwaggerSchema method checks if the schema is a Swagger schema.
172
- * Returns true if schema.properties.swagger exists.
173
- */
174
- static isSwaggerSchema(schema) {
175
- return (typeof schema === 'object' &&
176
- schema !== null &&
177
- !Array.isArray(schema) &&
178
- 'properties' in schema &&
179
- schema.properties !== null &&
180
- typeof schema.properties === 'object' &&
181
- !Array.isArray(schema.properties) &&
182
- Object.prototype.hasOwnProperty.call(schema.properties, 'swagger'));
95
+ }
96
+ /**
97
+ * The processSchema method processes a single schema file.
98
+ * It generates TypeScript models and services based on the schema.
99
+ */
100
+ async function processSchema(schemaPath, outputDir, vfs) {
101
+ // Destructure result from initializeSchemaProcessing (returns: schema, schemaJson, outputDir, modelName, dataSourceName)
102
+ const { schema, schemaJson, outputDir: processedOutputDir, modelName, dataSourceName, } = await initializeSchemaProcessing(schemaPath, outputDir, vfs);
103
+ if (isSwaggerSchema(schema)) {
104
+ await handleSwaggerSchema(modelName, dataSourceName, schema, processedOutputDir, vfs);
183
105
  }
184
- /**
185
- * Checks if a property is a Dataverse lookup column.
186
- */
187
- static isDataverseLookupProperty(propValue) {
188
- return propValue['x-ms-dataverse-type'] === 'LookupType';
106
+ else if (isSqlStoredProcedure(schema)) {
107
+ await handleSqlStoredProcedure(modelName, dataSourceName, schema, schemaJson, processedOutputDir, vfs);
189
108
  }
190
- /**
191
- * Gets all Dataverse lookup columns from the properties object.
192
- */
193
- static getDataverseLookupColumns(propertiesNode) {
194
- const lookupColumns = new Map();
195
- for (const [propName, propValue] of Object.entries(propertiesNode)) {
196
- const propValueAsObject = typeof propValue === 'object' && propValue !== null && !Array.isArray(propValue)
197
- ? propValue
198
- : undefined;
199
- if (propValueAsObject && this.isDataverseLookupProperty(propValueAsObject)) {
200
- lookupColumns.set(propName, propValueAsObject);
201
- }
202
- }
203
- return lookupColumns;
109
+ else if (isSharepointSchema(schema)) {
110
+ await handleSharepointSchema(modelName, dataSourceName, schema, schemaJson, processedOutputDir, vfs);
204
111
  }
205
- /**
206
- * Generates the OData bind property name for a lookup column.
207
- * Example: "cr160_lookupcol" -> "cr160_lookupcol@odata.bind"
208
- */
209
- static generateODataBindPropertyName(schemaName) {
210
- return `${schemaName}@odata.bind`;
112
+ else {
113
+ await handleStandardSchema(modelName, dataSourceName, schema, schemaJson, processedOutputDir, vfs);
211
114
  }
212
- /**
213
- * Generates the value property name for a lookup column.
214
- * Example: "cr160_lookupcol" -> "_cr160_lookupcol_value"
215
- */
216
- static generateLookupValuePropertyName(lookupPropertyName) {
217
- return `_${lookupPropertyName}_value`;
115
+ }
116
+ /**
117
+ * The initializeSchemaProcessing method initializes the schema processing.
118
+ * Returns an object containing the parsed schema, the schema JSON string, the output directory, model name, and data source name.
119
+ */
120
+ async function initializeSchemaProcessing(schemaPath, outputDir, vfs) {
121
+ const { schema, schemaJson } = await loadSchemaFromFile(schemaPath, vfs, true);
122
+ await ensureOutputDirectoriesExist(outputDir, vfs);
123
+ // Ensure schema is a JsonObject for processing
124
+ if (typeof schema !== 'object' || schema === null || Array.isArray(schema)) {
125
+ throw new Error('Schema must be a valid JSON object');
126
+ }
127
+ // model name is used for the generated TypeScript interface and file names
128
+ // dataSourceName is the logical name of the datasource ex:entity set name for Dataverse
129
+ const { modelName, dataSourceName } = extractModelName(schema, schemaPath, vfs);
130
+ return {
131
+ schema: schema,
132
+ schemaJson,
133
+ outputDir,
134
+ modelName,
135
+ dataSourceName,
136
+ };
137
+ }
138
+ async function loadSchemaFromFile(schemaPath, vfs, asObjectWithRaw = false) {
139
+ if (!(await vfs.exists(schemaPath))) {
140
+ throw new Error(`Schema file not found at path: ${schemaPath}`);
218
141
  }
219
- /**
220
- * The addCopyrightNotice method adds a copyright notice to the generated files.
221
- */
222
- static addCopyrightNotice(lines) {
223
- lines.push('/*!');
224
- lines.push(' * Copyright (C) Microsoft Corporation. All rights reserved.');
225
- lines.push(' * This file is autogenerated. Do not edit this file directly.');
226
- lines.push(' */');
227
- lines.push('');
142
+ const schemaJson = await vfs.readFile(schemaPath, 'utf-8');
143
+ const schema = JSON.parse(schemaJson);
144
+ if (asObjectWithRaw) {
145
+ return { schema, schemaJson };
228
146
  }
229
- /**
230
- * Generates TypeScript interfaces for Dataverse entities with lookup columns.
231
- * Creates both a base interface (for PATCH/POST) and an extended interface (for GET).
232
- */
233
- static generateDataverseInterfaces(modelName, definition) {
234
- const lines = [];
235
- this.addCopyrightNotice(lines);
236
- const propertiesNode = this.getPropertiesNode(definition);
237
- const lookupColumns = this.getDataverseLookupColumns(propertiesNode);
238
- if (lookupColumns.size === 0) {
239
- // No lookup columns, generate single interface
240
- const singleInterface = this.generatePropertyLines(modelName, definition);
241
- return { baseInterface: lines.join('\n') + singleInterface, extendedInterface: '' };
242
- }
243
- // Generate base interface for PATCH operations
244
- const baseInterfaceName = `${modelName}Base`;
245
- const baseInterfaceCode = this.generateDataverseBaseInterface(baseInterfaceName, modelName, definition, lookupColumns);
246
- // Generate extended interface for GET operations
247
- const extendedInterfaceName = modelName;
248
- const extendedInterfaceCode = this.generateDataverseExtendedInterface(extendedInterfaceName, baseInterfaceName, definition, lookupColumns);
249
- const baseInterface = lines.join('\n') + baseInterfaceCode;
250
- const extendedInterface = extendedInterfaceCode;
251
- return { baseInterface, extendedInterface };
147
+ else {
148
+ return schema;
252
149
  }
253
- /**
254
- * Generates the base interface for Dataverse PATCH/POST operations.
255
- * Replaces lookup columns with @odata.bind properties.
256
- */
257
- static generateDataverseBaseInterface(interfaceName, originalModelName, definition, lookupColumns) {
258
- const lines = [];
259
- const requiredFields = this.getRequiredFields(definition);
260
- const propertiesNode = this.getPropertiesNode(definition);
261
- // Generate optionset enums first - use original model name for enum generation
262
- const enumLines = this.generateOptionsetEnums(originalModelName, propertiesNode, '');
263
- if (enumLines.length > 0) {
264
- lines.push(...enumLines);
265
- lines.push('');
266
- }
267
- lines.push(`export interface ${convertToValidIdentifier(interfaceName)} {`);
268
- for (const [propName, propValue] of Object.entries(propertiesNode)) {
269
- const propValueAsObject = typeof propValue === 'object' && propValue !== null && !Array.isArray(propValue)
270
- ? propValue
271
- : undefined;
272
- // Skip read-only properties. They should not be included in PATCH/POST interfaces.
273
- if (propValueAsObject === null || propValueAsObject === void 0 ? void 0 : propValueAsObject['x-ms-read-only']) {
274
- continue;
275
- }
276
- // Handle lookup columns and their related properties
277
- if (lookupColumns.has(propName) || this.isLookupRelatedProperty(propName, lookupColumns)) {
278
- if (lookupColumns.has(propName)) {
279
- // Add @odata.bind property instead
280
- const schemaName = propValueAsObject === null || propValueAsObject === void 0 ? void 0 : propValueAsObject['x-ms-schema-name'];
281
- // Ensure schemaName is a string before passing to generateODataBindPropertyName
282
- const schemaNameStr = typeof schemaName === 'string' ? schemaName : propName;
283
- const odataBindProp = this.generateODataBindPropertyName(schemaNameStr);
284
- const formattedPropName = formatPropertyName(odataBindProp);
285
- const optionalMark = requiredFields.has(propName) ? '' : '?';
286
- lines.push(` ${formattedPropName}${optionalMark}: string;`);
287
- }
288
- continue;
289
- }
290
- let formattedPropName = formatPropertyName(convertToValidIdentifier(propName));
291
- formattedPropName = normalizePropertyName(formattedPropName);
292
- const tsType = this.determineTypeScriptType(originalModelName, propValueAsObject, '', propName);
293
- const optionalMark = requiredFields.has(propName) ? '' : '?';
294
- this.addPropertyComment(lines, propValueAsObject, '');
295
- lines.push(` ${formattedPropName}${optionalMark}: ${tsType};`);
150
+ }
151
+ /**
152
+ * The ensureOutputDirectoriesExist method ensures that the required output directories exist.
153
+ * It creates the main output directory, plus 'generated/models' and 'generated/services' subfolders if needed.
154
+ */
155
+ async function ensureOutputDirectoriesExist(outputDir, vfs) {
156
+ await vfs.mkdir(outputDir, { recursive: true });
157
+ await vfs.mkdir(vfs.join(outputDir, MODELS_FOLDER), { recursive: true });
158
+ await vfs.mkdir(vfs.join(outputDir, SERVICES_FOLDER), {
159
+ recursive: true,
160
+ });
161
+ }
162
+ /**
163
+ * The isSwaggerSchema method checks if the schema is a Swagger schema.
164
+ * Returns true if schema.properties.swagger exists.
165
+ */
166
+ function isSwaggerSchema(schema) {
167
+ return (typeof schema === 'object' &&
168
+ schema !== null &&
169
+ !Array.isArray(schema) &&
170
+ 'properties' in schema &&
171
+ schema.properties !== null &&
172
+ typeof schema.properties === 'object' &&
173
+ !Array.isArray(schema.properties) &&
174
+ Object.prototype.hasOwnProperty.call(schema.properties, 'swagger'));
175
+ }
176
+ /**
177
+ * Checks if a property is a Dataverse lookup column.
178
+ */
179
+ function isDataverseLookupProperty(propValue) {
180
+ return propValue['x-ms-dataverse-type'] === 'LookupType';
181
+ }
182
+ /**
183
+ * Gets all Dataverse lookup columns from the properties object.
184
+ */
185
+ function getDataverseLookupColumns(propertiesNode) {
186
+ const lookupColumns = new Map();
187
+ for (const [propName, propValue] of Object.entries(propertiesNode)) {
188
+ const propValueAsObject = typeof propValue === 'object' && propValue !== null && !Array.isArray(propValue)
189
+ ? propValue
190
+ : undefined;
191
+ if (propValueAsObject && isDataverseLookupProperty(propValueAsObject)) {
192
+ lookupColumns.set(propName, propValueAsObject);
296
193
  }
297
- lines.push('}');
298
- return lines.join('\n');
299
194
  }
300
- /**
301
- * Generates the extended interface for Dataverse GET operations.
302
- * Extends the base interface and adds lookup object and value properties.
303
- */
304
- static generateDataverseExtendedInterface(modelName, baseInterfaceName, definition, lookupColumns) {
305
- const lines = [];
306
- lines.push(`export interface ${convertToValidIdentifier(modelName)} extends ${convertToValidIdentifier(baseInterfaceName)} {`);
307
- const requiredFields = this.getRequiredFields(definition);
308
- const propertiesNode = this.getPropertiesNode(definition);
309
- // Add read-only properties (excluding lookup columns which are handled separately)
310
- for (const [propName, propValue] of Object.entries(propertiesNode)) {
311
- const propValueAsObject = typeof propValue === 'object' && propValue !== null && !Array.isArray(propValue)
312
- ? propValue
313
- : undefined;
314
- // Only include read-only properties in the extended interface
315
- if (!(propValueAsObject === null || propValueAsObject === void 0 ? void 0 : propValueAsObject['x-ms-read-only'])) {
316
- continue;
317
- }
318
- // Skip lookup columns as they will be handled in the lookup-specific section
195
+ return lookupColumns;
196
+ }
197
+ /**
198
+ * Generates the OData bind property name for a lookup column.
199
+ * Example: "cr160_lookupcol" -> "cr160_lookupcol@odata.bind"
200
+ */
201
+ function generateODataBindPropertyName(schemaName) {
202
+ return `${schemaName}@odata.bind`;
203
+ }
204
+ /**
205
+ * Generates the value property name for a lookup column.
206
+ * Example: "cr160_lookupcol" -> "_cr160_lookupcol_value"
207
+ */
208
+ function generateLookupValuePropertyName(lookupPropertyName) {
209
+ return `_${lookupPropertyName}_value`;
210
+ }
211
+ /**
212
+ * The addCopyrightNotice method adds a copyright notice to the generated files.
213
+ */
214
+ function addCopyrightNotice(lines) {
215
+ lines.push('/*!');
216
+ lines.push(' * Copyright (C) Microsoft Corporation. All rights reserved.');
217
+ lines.push(' * This file is autogenerated. Do not edit this file directly.');
218
+ lines.push(' */');
219
+ lines.push('');
220
+ }
221
+ /**
222
+ * Generates TypeScript interfaces for Dataverse entities with lookup columns.
223
+ * Creates both a base interface (for PATCH/POST) and an extended interface (for GET).
224
+ */
225
+ function generateDataverseInterfaces(modelName, definition) {
226
+ const lines = [];
227
+ addCopyrightNotice(lines);
228
+ const propertiesNode = getPropertiesNode(definition);
229
+ const lookupColumns = getDataverseLookupColumns(propertiesNode);
230
+ if (lookupColumns.size === 0) {
231
+ // No lookup columns, generate single interface
232
+ const singleInterface = generatePropertyLines(modelName, definition);
233
+ return { baseInterface: lines.join('\n') + singleInterface, extendedInterface: '' };
234
+ }
235
+ // Generate base interface for PATCH operations
236
+ const baseInterfaceName = `${modelName}Base`;
237
+ const baseInterfaceCode = generateDataverseBaseInterface(baseInterfaceName, modelName, definition, lookupColumns);
238
+ // Generate extended interface for GET operations
239
+ const extendedInterfaceName = modelName;
240
+ const extendedInterfaceCode = generateDataverseExtendedInterface(extendedInterfaceName, baseInterfaceName, definition, lookupColumns);
241
+ const baseInterface = lines.join('\n') + baseInterfaceCode;
242
+ const extendedInterface = extendedInterfaceCode;
243
+ return { baseInterface, extendedInterface };
244
+ }
245
+ /**
246
+ * Generates the base interface for Dataverse PATCH/POST operations.
247
+ * Replaces lookup columns with @odata.bind properties.
248
+ */
249
+ function generateDataverseBaseInterface(interfaceName, originalModelName, definition, lookupColumns) {
250
+ const lines = [];
251
+ const requiredFields = getRequiredFields(definition);
252
+ const propertiesNode = getPropertiesNode(definition);
253
+ // Generate optionset enums first - use original model name for enum generation
254
+ const enumLines = generateOptionsetEnums(originalModelName, propertiesNode, '');
255
+ if (enumLines.length > 0) {
256
+ lines.push(...enumLines);
257
+ lines.push('');
258
+ }
259
+ lines.push(`export interface ${convertToValidIdentifier(interfaceName)} {`);
260
+ for (const [propName, propValue] of Object.entries(propertiesNode)) {
261
+ const propValueAsObject = typeof propValue === 'object' && propValue !== null && !Array.isArray(propValue)
262
+ ? propValue
263
+ : undefined;
264
+ // Skip read-only properties. They should not be included in PATCH/POST interfaces.
265
+ if (propValueAsObject === null || propValueAsObject === void 0 ? void 0 : propValueAsObject['x-ms-read-only']) {
266
+ continue;
267
+ }
268
+ // Handle lookup columns and their related properties
269
+ if (lookupColumns.has(propName) || isLookupRelatedProperty(propName, lookupColumns)) {
319
270
  if (lookupColumns.has(propName)) {
320
- continue;
271
+ // Add @odata.bind property instead
272
+ const schemaName = propValueAsObject === null || propValueAsObject === void 0 ? void 0 : propValueAsObject['x-ms-schema-name'];
273
+ // Ensure schemaName is a string before passing to generateODataBindPropertyName
274
+ const schemaNameStr = typeof schemaName === 'string' ? schemaName : propName;
275
+ const odataBindProp = generateODataBindPropertyName(schemaNameStr);
276
+ const formattedPropName = formatPropertyName(odataBindProp);
277
+ const optionalMark = requiredFields.has(propName) ? '' : '?';
278
+ lines.push(` ${formattedPropName}${optionalMark}: string;`);
321
279
  }
322
- let formattedPropName = formatPropertyName(convertToValidIdentifier(propName));
323
- formattedPropName = normalizePropertyName(formattedPropName);
324
- const tsType = this.determineTypeScriptType(modelName, propValueAsObject, '', propName);
325
- const optionalMark = requiredFields.has(propName) ? '' : '?';
326
- this.addPropertyComment(lines, propValueAsObject, '');
327
- lines.push(` ${formattedPropName}${optionalMark}: ${tsType};`);
280
+ continue;
328
281
  }
329
- // Add lookup-specific properties
330
- const lookupPropNames = Array.from(lookupColumns.keys());
331
- for (const lookupPropName of lookupPropNames) {
332
- // Add the lookup object property
333
- lines.push(` ${formatPropertyName(convertToValidIdentifier(lookupPropName))}?: object;`);
334
- // Add the lookup value property (_propname_value)
335
- const valuePropName = this.generateLookupValuePropertyName(lookupPropName);
336
- const formattedValuePropName = formatPropertyName(valuePropName);
337
- lines.push(` ${formattedValuePropName}?: string;`);
338
- }
339
- lines.push('}');
340
- return lines.join('\n');
282
+ let formattedPropName = formatPropertyName(convertToValidIdentifier(propName));
283
+ formattedPropName = normalizePropertyName(formattedPropName);
284
+ const tsType = determineTypeScriptType(originalModelName, propValueAsObject, '', propName);
285
+ const optionalMark = requiredFields.has(propName) ? '' : '?';
286
+ addPropertyComment(lines, propValueAsObject, '');
287
+ lines.push(` ${formattedPropName}${optionalMark}: ${tsType};`);
341
288
  }
342
- /**
343
- * Checks if a property is related to a lookup column (like name properties).
344
- */
345
- static isLookupRelatedProperty(propName, lookupColumns) {
346
- // Check if this property is a name property for any lookup column
347
- const lookupPropNames = Array.from(lookupColumns.keys());
348
- for (const lookupPropName of lookupPropNames) {
349
- if (propName === `${lookupPropName}name`) {
350
- return true;
351
- }
289
+ lines.push('}');
290
+ return lines.join('\n');
291
+ }
292
+ /**
293
+ * Generates the extended interface for Dataverse GET operations.
294
+ * Extends the base interface and adds lookup object and value properties.
295
+ */
296
+ function generateDataverseExtendedInterface(modelName, baseInterfaceName, definition, lookupColumns) {
297
+ const lines = [];
298
+ lines.push(`export interface ${convertToValidIdentifier(modelName)} extends ${convertToValidIdentifier(baseInterfaceName)} {`);
299
+ const requiredFields = getRequiredFields(definition);
300
+ const propertiesNode = getPropertiesNode(definition);
301
+ // Add read-only properties (excluding lookup columns which are handled separately)
302
+ for (const [propName, propValue] of Object.entries(propertiesNode)) {
303
+ const propValueAsObject = typeof propValue === 'object' && propValue !== null && !Array.isArray(propValue)
304
+ ? propValue
305
+ : undefined;
306
+ // Only include read-only properties in the extended interface
307
+ if (!(propValueAsObject === null || propValueAsObject === void 0 ? void 0 : propValueAsObject['x-ms-read-only'])) {
308
+ continue;
309
+ }
310
+ // Skip lookup columns as they will be handled in the lookup-specific section
311
+ if (lookupColumns.has(propName)) {
312
+ continue;
313
+ }
314
+ let formattedPropName = formatPropertyName(convertToValidIdentifier(propName));
315
+ formattedPropName = normalizePropertyName(formattedPropName);
316
+ const tsType = determineTypeScriptType(modelName, propValueAsObject, '', propName);
317
+ const optionalMark = requiredFields.has(propName) ? '' : '?';
318
+ addPropertyComment(lines, propValueAsObject, '');
319
+ lines.push(` ${formattedPropName}${optionalMark}: ${tsType};`);
320
+ }
321
+ // Add lookup-specific properties
322
+ const lookupPropNames = Array.from(lookupColumns.keys());
323
+ for (const lookupPropName of lookupPropNames) {
324
+ // Add the lookup object property
325
+ lines.push(` ${formatPropertyName(convertToValidIdentifier(lookupPropName))}?: object;`);
326
+ // Add the lookup value property (_propname_value)
327
+ const valuePropName = generateLookupValuePropertyName(lookupPropName);
328
+ const formattedValuePropName = formatPropertyName(valuePropName);
329
+ lines.push(` ${formattedValuePropName}?: string;`);
330
+ }
331
+ lines.push('}');
332
+ return lines.join('\n');
333
+ }
334
+ /**
335
+ * Checks if a property is related to a lookup column (like name properties).
336
+ */
337
+ function isLookupRelatedProperty(propName, lookupColumns) {
338
+ // Check if this property is a name property for any lookup column
339
+ const lookupPropNames = Array.from(lookupColumns.keys());
340
+ for (const lookupPropName of lookupPropNames) {
341
+ if (propName === `${lookupPropName}name`) {
342
+ return true;
352
343
  }
353
- return false;
354
344
  }
355
- /**
356
- * The handleSwaggerSchema method handles the Swagger schema.
357
- * It generates TypeScript models and services from the Swagger schema.
358
- */
359
- static async handleSwaggerSchema(modelName, dataSourceName, schema, outputDir, vfs) {
360
- const swaggerObject = this.getSwaggerObject(schema);
361
- const modelFilePath = vfs.join(outputDir, this.MODELS_FOLDER, `${modelName}Model.ts`);
362
- const serviceFilePath = vfs.join(outputDir, this.SERVICES_FOLDER, `${modelName}Service.ts`);
363
- const modelLines = [];
364
- this.addCopyrightNotice(modelLines);
365
- this.processDefinitions(swaggerObject, modelLines);
366
- const parameterTypes = this.processParameters(swaggerObject, modelLines);
367
- await this.writeToFile(modelFilePath, modelLines, vfs);
368
- if ('paths' in swaggerObject &&
369
- typeof swaggerObject.paths === 'object' &&
370
- swaggerObject.paths !== null &&
371
- !Array.isArray(swaggerObject.paths)) {
372
- const serviceCode = this.generateServiceFromPaths(modelName, dataSourceName, swaggerObject.paths, parameterTypes);
373
- await this.writeToFileWithContent(serviceFilePath, serviceCode, vfs);
374
- }
345
+ return false;
346
+ }
347
+ /**
348
+ * The handleSwaggerSchema method handles the Swagger schema.
349
+ * It generates TypeScript models and services from the Swagger schema.
350
+ */
351
+ async function handleSwaggerSchema(modelName, dataSourceName, schema, outputDir, vfs) {
352
+ const swaggerObject = getSwaggerObject(schema);
353
+ const modelFilePath = vfs.join(outputDir, MODELS_FOLDER, `${modelName}Model.ts`);
354
+ const serviceFilePath = vfs.join(outputDir, SERVICES_FOLDER, `${modelName}Service.ts`);
355
+ const modelLines = [];
356
+ addCopyrightNotice(modelLines);
357
+ processDefinitions(swaggerObject, modelLines);
358
+ const parameterTypes = processParameters(swaggerObject, modelLines);
359
+ await writeToFile(modelFilePath, modelLines, vfs);
360
+ if ('paths' in swaggerObject &&
361
+ typeof swaggerObject.paths === 'object' &&
362
+ swaggerObject.paths !== null &&
363
+ !Array.isArray(swaggerObject.paths)) {
364
+ const serviceCode = generateServiceFromPaths(modelName, dataSourceName, swaggerObject.paths, parameterTypes);
365
+ await writeToFileWithContent(serviceFilePath, serviceCode, vfs);
375
366
  }
376
- /**
377
- * The getSwaggerObject method retrieves the Swagger object from the schema.
378
- * Throws an error if the schema is not valid Swagger format.
379
- */
380
- static getSwaggerObject(schema) {
381
- if (schema !== null && typeof schema === 'object' && !Array.isArray(schema)) {
382
- const properties = schema.properties;
383
- if (typeof properties === 'object' && properties !== null && !Array.isArray(properties)) {
384
- const swaggerNode = properties.swagger;
385
- if (!swaggerNode || typeof swaggerNode !== 'object' || Array.isArray(swaggerNode)) {
386
- throw new Error("Invalid schema format: 'swagger' node is missing or malformed.");
387
- }
388
- return swaggerNode;
367
+ }
368
+ /**
369
+ * The getSwaggerObject method retrieves the Swagger object from the schema.
370
+ * Throws an error if the schema is not valid Swagger format.
371
+ */
372
+ function getSwaggerObject(schema) {
373
+ if (schema !== null && typeof schema === 'object' && !Array.isArray(schema)) {
374
+ const properties = schema.properties;
375
+ if (typeof properties === 'object' && properties !== null && !Array.isArray(properties)) {
376
+ const swaggerNode = properties.swagger;
377
+ if (!swaggerNode || typeof swaggerNode !== 'object' || Array.isArray(swaggerNode)) {
378
+ throw new Error("Invalid schema format: 'swagger' node is missing or malformed.");
389
379
  }
380
+ return swaggerNode;
390
381
  }
391
- throw new Error('Schema must be a JSON object with properties to extract swagger object');
392
382
  }
393
- /*
394
- * The processDefinitions function processes the definitions node of the Swagger schema.
395
- * It collects model interface names and pushes generated code lines to modelLines.
396
- */
397
- static processDefinitions(swaggerObject, modelLines) {
398
- const definitionTypes = [];
399
- if ('definitions' in swaggerObject &&
400
- typeof swaggerObject.definitions === 'object' &&
401
- swaggerObject.definitions !== null &&
402
- !Array.isArray(swaggerObject.definitions)) {
403
- const definitionsObject = swaggerObject.definitions;
404
- for (const [interfaceName, definitionValue] of Object.entries(definitionsObject)) {
405
- if (definitionValue && typeof definitionValue === 'object' && !Array.isArray(definitionValue)) {
406
- const modelCode = this.generateModelFromSchema(interfaceName, definitionValue);
407
- modelLines.push(modelCode);
408
- modelLines.push(''); // Add a blank line between models
409
- definitionTypes.push(interfaceName);
410
- }
383
+ throw new Error('Schema must be a JSON object with properties to extract swagger object');
384
+ }
385
+ /*
386
+ * The processDefinitions function processes the definitions node of the Swagger schema.
387
+ * It collects model interface names and pushes generated code lines to modelLines.
388
+ */
389
+ function processDefinitions(swaggerObject, modelLines) {
390
+ const definitionTypes = [];
391
+ if ('definitions' in swaggerObject &&
392
+ typeof swaggerObject.definitions === 'object' &&
393
+ swaggerObject.definitions !== null &&
394
+ !Array.isArray(swaggerObject.definitions)) {
395
+ const definitionsObject = swaggerObject.definitions;
396
+ for (const [interfaceName, definitionValue] of Object.entries(definitionsObject)) {
397
+ if (definitionValue && typeof definitionValue === 'object' && !Array.isArray(definitionValue)) {
398
+ const modelCode = generateModelFromSchema(interfaceName, definitionValue);
399
+ modelLines.push(modelCode);
400
+ modelLines.push(''); // Add a blank line between models
401
+ definitionTypes.push(interfaceName);
411
402
  }
412
403
  }
413
- return definitionTypes;
414
404
  }
415
- /*
416
- * The processParameters function processes the parameters node of the Swagger schema.
417
- */
418
- static processParameters(swaggerObject, modelLines) {
419
- const parameterTypes = {};
420
- if ('parameters' in swaggerObject &&
421
- typeof swaggerObject.parameters === 'object' &&
422
- swaggerObject.parameters !== null &&
423
- !Array.isArray(swaggerObject.parameters)) {
424
- const parametersObject = swaggerObject.parameters;
425
- for (const [interfaceName, parameterValue] of Object.entries(parametersObject)) {
426
- if (parameterValue && typeof parameterValue === 'object' && !Array.isArray(parameterValue)) {
427
- const { propertyInfo } = this.generateModelFromParameters(interfaceName, parameterValue);
428
- // Optionally: modelLines.push(modelCode); // If you want to collect interface code
429
- parameterTypes[interfaceName] = propertyInfo;
430
- }
405
+ return definitionTypes;
406
+ }
407
+ /*
408
+ * The processParameters function processes the parameters node of the Swagger schema.
409
+ */
410
+ function processParameters(swaggerObject, modelLines) {
411
+ const parameterTypes = {};
412
+ if ('parameters' in swaggerObject &&
413
+ typeof swaggerObject.parameters === 'object' &&
414
+ swaggerObject.parameters !== null &&
415
+ !Array.isArray(swaggerObject.parameters)) {
416
+ const parametersObject = swaggerObject.parameters;
417
+ for (const [interfaceName, parameterValue] of Object.entries(parametersObject)) {
418
+ if (parameterValue && typeof parameterValue === 'object' && !Array.isArray(parameterValue)) {
419
+ const { propertyInfo } = generateModelFromParameters(interfaceName, parameterValue);
420
+ // Optionally: modelLines.push(modelCode); // If you want to collect interface code
421
+ parameterTypes[interfaceName] = propertyInfo;
431
422
  }
432
423
  }
433
- return parameterTypes;
434
- }
435
- /**
436
- * Writes the lines to a file at the specified path.
437
- *
438
- * @param {string} filePath The path to the file to write
439
- * @param {string[]} lines The lines to write to the file
440
- * @param {boolean} [createDirPath=true] Create the directory path if it does not exist
441
- */
442
- static async writeToFile(filePath, lines, vfs, createDirPath = true) {
443
- const dir = vfs.dirname(filePath);
444
- if (createDirPath && !(await vfs.exists(dir))) {
445
- await vfs.mkdir(dir, { recursive: true });
446
- }
447
- await vfs.writeFile(filePath, lines.join('\n'));
448
424
  }
449
- /**
450
- * The writeToFile method writes the generated code to a file from a string.
451
- */
452
- static async writeToFileWithContent(filePath, content, vfs) {
453
- await vfs.writeFile(filePath, content);
425
+ return parameterTypes;
426
+ }
427
+ /**
428
+ * Writes the lines to a file at the specified path.
429
+ *
430
+ * @param {string} filePath The path to the file to write
431
+ * @param {string[]} lines The lines to write to the file
432
+ * @param {boolean} [createDirPath=true] Create the directory path if it does not exist
433
+ */
434
+ async function writeToFile(filePath, lines, vfs, createDirPath = true) {
435
+ const dir = vfs.dirname(filePath);
436
+ if (createDirPath && !(await vfs.exists(dir))) {
437
+ await vfs.mkdir(dir, { recursive: true });
454
438
  }
455
- /**
456
- * The generateModelFromSchema method generates TypeScript models from the schema.
457
- * Returns a type alias or interface definition string.
458
- */
459
- static generateModelFromSchema(modelName, definition) {
460
- modelName = convertToValidIdentifier(this.simplifyGenericInterfaceName(modelName));
461
- const lines = [];
462
- // If it's a simple type (e.g., string, number, boolean) and has no "properties" or "items", generate a type alias
463
- // Note: We exclude definitions with "items" because those are array types that need special handling
464
- if ('type' in definition &&
465
- definition.type &&
466
- !('properties' in definition) &&
467
- !('items' in definition)) {
468
- const tsType = this.mapJsonTypeToTypeScript(definition.type);
469
- lines.push(`export type ${modelName} = ${tsType};`);
470
- return lines.join('\n');
471
- }
472
- // If it's an empty object, generate a type alias for object
473
- const { isEmpty, itemsNode } = this.isEmptyObject(definition);
474
- if (isEmpty) {
475
- lines.push(this.generateTypeAliasForEmptyObject(modelName));
476
- return lines.join('\n');
477
- }
478
- // Check if this is an array type definition
479
- const isArrayType = definition.type === 'array' && itemsNode;
480
- if (isArrayType) {
481
- // For array types, generate an interface for the item type and a type alias for the array
482
- const itemInterfaceName = `${modelName}Item`;
483
- const itemModelCode = this.generatePropertyLines(itemInterfaceName, itemsNode);
484
- lines.push(itemModelCode);
485
- lines.push('');
486
- lines.push(`export type ${modelName} = ${itemInterfaceName}[];`);
487
- return lines.join('\n');
488
- }
489
- // If itemsNode exists but not an array type, pass that; otherwise, pass the full definition
490
- const modelCode = itemsNode
491
- ? this.generatePropertyLines(modelName, itemsNode)
492
- : this.generatePropertyLines(modelName, definition);
493
- lines.push(modelCode);
439
+ await vfs.writeFile(filePath, lines.join('\n'));
440
+ }
441
+ /**
442
+ * The writeToFile method writes the generated code to a file from a string.
443
+ */
444
+ async function writeToFileWithContent(filePath, content, vfs) {
445
+ await vfs.writeFile(filePath, content);
446
+ }
447
+ /**
448
+ * The generateModelFromSchema method generates TypeScript models from the schema.
449
+ * Returns a type alias or interface definition string.
450
+ */
451
+ function generateModelFromSchema(modelName, definition) {
452
+ modelName = convertToValidIdentifier(simplifyGenericInterfaceName(modelName));
453
+ const lines = [];
454
+ // If it's a simple type (e.g., string, number, boolean) and has no "properties" or "items", generate a type alias
455
+ // Note: We exclude definitions with "items" because those are array types that need special handling
456
+ if ('type' in definition && definition.type && !('properties' in definition) && !('items' in definition)) {
457
+ const tsType = mapJsonTypeToTypeScript(definition.type);
458
+ lines.push(`export type ${modelName} = ${tsType};`);
494
459
  return lines.join('\n');
495
460
  }
496
- /**
497
- * The simplifyGenericInterfaceName method simplifies the generic interface name.
498
- * It removes the generic parameters from the name.
499
- */
500
- static simplifyGenericInterfaceName(modelName) {
501
- return this.normalizeGenericTypeName(modelName);
502
- }
503
- /**
504
- * The isEmptyObject method checks if the definition is an empty object.
505
- * Returns an object with isEmpty (boolean) and itemsNode.
506
- */
507
- static isEmptyObject(definition) {
508
- let itemsNode;
509
- const hasNoProperties = !('properties' in definition) ||
510
- typeof definition.properties !== 'object' ||
511
- definition.properties === null ||
512
- Array.isArray(definition.properties) ||
513
- Object.keys(definition.properties).length === 0;
514
- const hasNoItems = !('items' in definition) ||
515
- typeof definition.items !== 'object' ||
516
- definition.items === null ||
517
- Array.isArray(definition.items) ||
518
- Object.keys(definition.items).length === 0;
519
- if ('items' in definition &&
520
- typeof definition.items === 'object' &&
521
- definition.items !== null &&
522
- !Array.isArray(definition.items)) {
523
- itemsNode = definition.items;
524
- }
525
- return { isEmpty: hasNoProperties && hasNoItems, itemsNode };
461
+ // If it's an empty object, generate a type alias for object
462
+ const { isEmpty, itemsNode } = isEmptyObject(definition);
463
+ if (isEmpty) {
464
+ lines.push(generateTypeAliasForEmptyObject(modelName));
465
+ return lines.join('\n');
526
466
  }
527
- /**
528
- * The generateTypeAliasForEmptyObject method generates a TypeScript type alias for an empty object.
529
- */
530
- static generateTypeAliasForEmptyObject(modelName) {
531
- return `export type ${convertToValidIdentifier(modelName)} = object;`;
467
+ // Check if this is an array type definition
468
+ const isArrayType = definition.type === 'array' && itemsNode;
469
+ if (isArrayType) {
470
+ // For array types, generate an interface for the item type and a type alias for the array
471
+ const itemInterfaceName = `${modelName}Item`;
472
+ const itemModelCode = generatePropertyLines(itemInterfaceName, itemsNode);
473
+ lines.push(itemModelCode);
474
+ lines.push('');
475
+ lines.push(`export type ${modelName} = ${itemInterfaceName}[];`);
476
+ return lines.join('\n');
532
477
  }
533
- /*
534
- * The GeneratePropertyLines method generates TypeScript property lines from the schema.
535
- */
536
- static generatePropertyLines(modelName, definition, addInterfaceName = true, padding = '') {
537
- if (!definition || typeof definition !== 'object' || Array.isArray(definition)) {
538
- throw new Error('The definition must be a valid JSON object.');
539
- }
540
- const lines = [];
541
- const requiredFields = this.getRequiredFields(definition);
542
- const propertiesNode = this.getPropertiesNode(definition);
543
- // Generate optionset enums first (before the interface)
544
- const enumLines = this.generateOptionsetEnums(modelName, propertiesNode, padding);
545
- if (enumLines.length > 0) {
546
- lines.push(...enumLines);
478
+ // If itemsNode exists but not an array type, pass that; otherwise, pass the full definition
479
+ const modelCode = itemsNode
480
+ ? generatePropertyLines(modelName, itemsNode)
481
+ : generatePropertyLines(modelName, definition);
482
+ lines.push(modelCode);
483
+ return lines.join('\n');
484
+ }
485
+ /**
486
+ * The simplifyGenericInterfaceName method simplifies the generic interface name.
487
+ * It removes the generic parameters from the name.
488
+ */
489
+ function simplifyGenericInterfaceName(modelName) {
490
+ return normalizeGenericTypeName(modelName);
491
+ }
492
+ /**
493
+ * The isEmptyObject method checks if the definition is an empty object.
494
+ * Returns an object with isEmpty (boolean) and itemsNode.
495
+ */
496
+ function isEmptyObject(definition) {
497
+ let itemsNode;
498
+ const hasNoProperties = !('properties' in definition) ||
499
+ typeof definition.properties !== 'object' ||
500
+ definition.properties === null ||
501
+ Array.isArray(definition.properties) ||
502
+ Object.keys(definition.properties).length === 0;
503
+ const hasNoItems = !('items' in definition) ||
504
+ typeof definition.items !== 'object' ||
505
+ definition.items === null ||
506
+ Array.isArray(definition.items) ||
507
+ Object.keys(definition.items).length === 0;
508
+ if ('items' in definition &&
509
+ typeof definition.items === 'object' &&
510
+ definition.items !== null &&
511
+ !Array.isArray(definition.items)) {
512
+ itemsNode = definition.items;
513
+ }
514
+ return { isEmpty: hasNoProperties && hasNoItems, itemsNode };
515
+ }
516
+ /**
517
+ * The generateTypeAliasForEmptyObject method generates a TypeScript type alias for an empty object.
518
+ */
519
+ function generateTypeAliasForEmptyObject(modelName) {
520
+ return `export type ${convertToValidIdentifier(modelName)} = object;`;
521
+ }
522
+ /*
523
+ * The GeneratePropertyLines method generates TypeScript property lines from the schema.
524
+ */
525
+ function generatePropertyLines(modelName, definition, addInterfaceName = true, padding = '') {
526
+ if (!definition || typeof definition !== 'object' || Array.isArray(definition)) {
527
+ throw new Error('The definition must be a valid JSON object.');
528
+ }
529
+ const lines = [];
530
+ const requiredFields = getRequiredFields(definition);
531
+ const propertiesNode = getPropertiesNode(definition);
532
+ // Generate optionset enums first (before the interface)
533
+ // BUT only for top-level interfaces (addInterfaceName === true)
534
+ // Nested inline objects should not generate enums to avoid exporting them mid-interface
535
+ if (addInterfaceName) {
536
+ // Collect all enums including from nested objects
537
+ const allEnumLines = collectAllEnums(modelName, propertiesNode, padding);
538
+ if (allEnumLines.length > 0) {
539
+ lines.push(...allEnumLines);
547
540
  lines.push(''); // Add blank line between enums and interface
548
541
  }
549
- this.addInterfaceDeclarationWithPadding(lines, modelName, addInterfaceName, padding);
550
- for (const [propName, propValue] of Object.entries(propertiesNode)) {
551
- let formattedPropName = formatPropertyName(convertToValidIdentifier(propName));
552
- // Normalize specific property names (e.g., "resultsets" => "ResultSets")
553
- formattedPropName = normalizePropertyName(formattedPropName);
554
- const propValueAsObject = typeof propValue === 'object' && propValue !== null && !Array.isArray(propValue)
555
- ? propValue
556
- : undefined;
557
- const tsType = this.determineTypeScriptType(modelName, propValueAsObject, padding, propName);
558
- const optionalMark = requiredFields.has(propName) ? '' : '?';
559
- this.addPropertyComment(lines, propValueAsObject, padding);
560
- lines.push(`${padding} ${formattedPropName}${optionalMark}: ${tsType};`);
542
+ }
543
+ addInterfaceDeclarationWithPadding(lines, modelName, addInterfaceName, padding);
544
+ for (const [propName, propValue] of Object.entries(propertiesNode)) {
545
+ let formattedPropName = formatPropertyName(convertToValidIdentifier(propName));
546
+ // Normalize specific property names (e.g., "resultsets" => "ResultSets")
547
+ formattedPropName = normalizePropertyName(formattedPropName);
548
+ const propValueAsObject = typeof propValue === 'object' && propValue !== null && !Array.isArray(propValue)
549
+ ? propValue
550
+ : undefined;
551
+ const tsType = determineTypeScriptType(modelName, propValueAsObject, padding, propName);
552
+ const optionalMark = requiredFields.has(propName) ? '' : '?';
553
+ addPropertyComment(lines, propValueAsObject, padding);
554
+ lines.push(`${padding} ${formattedPropName}${optionalMark}: ${tsType};`);
555
+ }
556
+ closeInterfaceDeclarationWithPadding(lines, padding);
557
+ return lines.join('\n');
558
+ }
559
+ /**
560
+ * Recursively collects all enum declarations from properties, including nested objects.
561
+ * This ensures enums are hoisted to the top level and not exported mid-interface.
562
+ */
563
+ function collectAllEnums(modelName, propertiesNode, padding) {
564
+ const enumLines = [];
565
+ for (const [propName, propValue] of Object.entries(propertiesNode)) {
566
+ const propValueAsObject = typeof propValue === 'object' && propValue !== null && !Array.isArray(propValue)
567
+ ? propValue
568
+ : undefined;
569
+ if (!propValueAsObject) {
570
+ continue;
571
+ }
572
+ // Check if this property has optionset data
573
+ if (hasOptionsetData(propValueAsObject)) {
574
+ const enumName = generateOptionsetEnumName(modelName, propName);
575
+ const enumDeclaration = generateOptionsetConstDeclaration(enumName, propValueAsObject, padding);
576
+ enumLines.push(enumDeclaration);
577
+ }
578
+ // Recursively collect enums from nested objects
579
+ if (propValueAsObject.properties && typeof propValueAsObject.properties === 'object') {
580
+ const nestedEnums = collectAllEnums(modelName, propValueAsObject.properties, padding);
581
+ enumLines.push(...nestedEnums);
582
+ }
583
+ // Recursively collect enums from array items
584
+ if (propValueAsObject.type === 'array' &&
585
+ propValueAsObject.items &&
586
+ typeof propValueAsObject.items === 'object' &&
587
+ !Array.isArray(propValueAsObject.items)) {
588
+ const itemsObj = propValueAsObject.items;
589
+ if (itemsObj.properties && typeof itemsObj.properties === 'object') {
590
+ const itemEnums = collectAllEnums(modelName, itemsObj.properties, padding);
591
+ enumLines.push(...itemEnums);
592
+ }
561
593
  }
562
- this.closeInterfaceDeclarationWithPadding(lines, padding);
563
- return lines.join('\n');
564
594
  }
565
- /**
566
- * Checks if a property has optionset data (enum and x-ms-enum-values)
567
- */
568
- static hasOptionsetData(propValue) {
569
- const hasEnum = Array.isArray(propValue.enum) && propValue.enum.length > 0;
570
- return hasEnum;
595
+ return enumLines;
596
+ }
597
+ /**
598
+ * Checks if a property has optionset data (enum and x-ms-enum-values)
599
+ */
600
+ function hasOptionsetData(propValue) {
601
+ const hasEnum = Array.isArray(propValue.enum) && propValue.enum.length > 0;
602
+ return hasEnum;
603
+ }
604
+ /**
605
+ * Generates the enum name for an optionset property
606
+ */
607
+ function generateOptionsetEnumName(modelName, propName) {
608
+ const formattedPropName = formatPropertyName(propName);
609
+ return `${modelName}${formattedPropName}`;
610
+ }
611
+ /**
612
+ * The generateOptionsetEnums method generates TypeScript const objects for optionset properties.
613
+ * These const objects are compatible with TypeScript's erasableSyntaxOnly flag and provide
614
+ * type safety while preserving the original numeric values from the schema.
615
+ * @returns An array of const object declaration strings.
616
+ */
617
+ function generateOptionsetEnums(modelName, propertiesNode, padding) {
618
+ const enumLines = [];
619
+ for (const [propName, propValue] of Object.entries(propertiesNode)) {
620
+ const propValueAsObject = typeof propValue === 'object' && propValue !== null && !Array.isArray(propValue)
621
+ ? propValue
622
+ : undefined;
623
+ // Check if this property has optionset data
624
+ if (propValueAsObject && hasOptionsetData(propValueAsObject)) {
625
+ const enumName = generateOptionsetEnumName(modelName, propName);
626
+ const enumDeclaration = generateOptionsetConstDeclaration(enumName, propValueAsObject, padding);
627
+ enumLines.push(enumDeclaration);
628
+ }
629
+ }
630
+ return enumLines;
631
+ }
632
+ /**
633
+ * Formats an enum label as a quoted string literal, escaping single quotes.
634
+ * Returns an empty string literal for empty labels.
635
+ */
636
+ function formatEnumLabel(label) {
637
+ if (!label || (typeof label === 'string' && label.trim() === '')) {
638
+ return `''`; // Placeholder for empty labels
571
639
  }
572
- /**
573
- * Generates the enum name for an optionset property
574
- */
575
- static generateOptionsetEnumName(modelName, propName) {
576
- const formattedPropName = formatPropertyName(propName);
577
- return `${modelName}${formattedPropName}`;
640
+ else {
641
+ // Escape single quotes in the label to prevent breaking the string literal
642
+ const escapedLabel = label.replace(/'/g, "\\'");
643
+ return `'${escapedLabel}'`;
578
644
  }
579
- /**
580
- * The generateOptionsetEnums method generates TypeScript const objects for optionset properties.
581
- * These const objects are compatible with TypeScript's erasableSyntaxOnly flag and provide
582
- * type safety while preserving the original numeric values from the schema.
583
- * @returns An array of const object declaration strings.
584
- */
585
- static generateOptionsetEnums(modelName, propertiesNode, padding) {
586
- const enumLines = [];
587
- for (const [propName, propValue] of Object.entries(propertiesNode)) {
588
- const propValueAsObject = typeof propValue === 'object' && propValue !== null && !Array.isArray(propValue)
589
- ? propValue
590
- : undefined;
591
- // Check if this property has optionset data
592
- if (propValueAsObject && this.hasOptionsetData(propValueAsObject)) {
593
- const enumName = this.generateOptionsetEnumName(modelName, propName);
594
- const enumDeclaration = this.generateOptionsetConstDeclaration(enumName, propValueAsObject, padding);
595
- enumLines.push(enumDeclaration);
596
- }
645
+ }
646
+ /**
647
+ * Generates const object declaration for an optionset property.
648
+ * Creates a const object with typed properties that is compatible with erasableSyntaxOnly.
649
+ * Also generates a corresponding type alias for type safety.
650
+ */
651
+ function generateOptionsetConstDeclaration(enumName, propValue, padding) {
652
+ // Check for x-ms-enum-values format (array of objects or numbers)
653
+ const enumValues = propValue['x-ms-enum-values'];
654
+ const enumLabels = propValue.enum;
655
+ const lines = [];
656
+ enumName = convertToValidIdentifier(enumName);
657
+ // Check if x-ms-enum-values exists and is an array
658
+ if (enumValues && Array.isArray(enumValues) && enumLabels && Array.isArray(enumLabels)) {
659
+ // Check if x-ms-enum-values contains objects with numeric values (Dataverse format)
660
+ const hasObjectFormat = enumValues.length > 0 && typeof enumValues[0] === 'object' && enumValues[0] !== null;
661
+ if (hasObjectFormat) {
662
+ // This is the Swagger format where x-ms-enum-values contains objects like {displayName, value}
663
+ // Just use the enum array for string union type
664
+ const labelStrings = enumLabels.map((label) => formatEnumLabel(label));
665
+ lines.push(`${padding}export type ${enumName} = ${labelStrings.join('|')};`);
597
666
  }
598
- return enumLines;
599
- }
600
- /**
601
- * Generates const object declaration for an optionset property.
602
- * Creates a const object with typed properties that is compatible with erasableSyntaxOnly.
603
- * Also generates a corresponding type alias for type safety.
604
- */
605
- static generateOptionsetConstDeclaration(enumName, propValue, padding) {
606
- // Check for x-ms-enum-values format
607
- const enumNumericValues = propValue['x-ms-enum-values'];
608
- const enumLabels = propValue.enum;
609
- const lines = [];
610
- enumName = convertToValidIdentifier(enumName);
611
- if (enumNumericValues && Array.isArray(enumNumericValues) && enumLabels && Array.isArray(enumLabels)) {
612
- // This is the path for dataverse where option sets have numeric values and display labels
667
+ else {
668
+ // This is the Dataverse format where x-ms-enum-values contains numeric values
669
+ const enumNumericValues = enumValues;
613
670
  // Generate const object with numeric keys
614
671
  lines.push(`${padding}export const ${enumName} = {`);
615
672
  // Handle the format where enum contains display names and x-ms-enum-values contains numeric values
@@ -632,1809 +689,1774 @@ export class ModelServiceGenerator {
632
689
  // Generate type alias for the numeric values
633
690
  lines.push(`${padding}export type ${enumName} = keyof typeof ${enumName};`);
634
691
  }
635
- else {
636
- // This is the path for swaggers where option sets have display labels only
637
- const labelStrings = enumLabels.map((label) => {
638
- if (!label || (typeof label === 'string' && label.trim() === '')) {
639
- return `''`; // Placeholder for empty labels
640
- }
641
- else {
642
- return `'${label}'`;
643
- }
644
- });
645
- lines.push(`${padding}export type ${enumName} = ${labelStrings.join('|')};`);
646
- }
647
- return lines.join('\n');
648
692
  }
649
- /*
650
- * The addInterfaceDeclarationWithPadding function adds the interface declaration to the lines.
651
- * It takes the lines to write to, model name, and padding as parameters.
652
- */
653
- static addInterfaceDeclarationWithPadding(lines, modelName, addInterfaceName, padding) {
654
- if (addInterfaceName) {
655
- lines.push(`${padding}export interface ${convertToValidIdentifier(modelName)} {`);
656
- }
657
- else {
658
- lines.push(`{`);
659
- }
693
+ else {
694
+ // This is the path for swaggers where only enum array exists (no x-ms-enum-values)
695
+ const labelStrings = enumLabels.map((label) => formatEnumLabel(label));
696
+ lines.push(`${padding}export type ${enumName} = ${labelStrings.join('|')};`);
660
697
  }
661
- /*
662
- * The closeInterfaceDeclarationWithPadding function closes the interface declaration with padding.
663
- * It takes the lines to write to and padding as parameters.
664
- */
665
- static closeInterfaceDeclarationWithPadding(lines, padding) {
666
- lines.push(`${padding}}`);
698
+ return lines.join('\n');
699
+ }
700
+ /*
701
+ * The addInterfaceDeclarationWithPadding function adds the interface declaration to the lines.
702
+ * It takes the lines to write to, model name, and padding as parameters.
703
+ */
704
+ function addInterfaceDeclarationWithPadding(lines, modelName, addInterfaceName, padding) {
705
+ if (addInterfaceName) {
706
+ lines.push(`${padding}export interface ${convertToValidIdentifier(modelName)} {`);
667
707
  }
668
- /*
669
- * The getRequiredFields method retrieves the required fields from the definition.
670
- * It returns a Set of required field names.
671
- */
672
- static getRequiredFields(definition) {
673
- if ('required' in definition && Array.isArray(definition.required)) {
674
- // Convert all entries to string and add to Set
675
- return new Set(definition.required.map((r) => String(r)));
676
- }
677
- return new Set();
708
+ else {
709
+ lines.push(`{`);
678
710
  }
679
- /*
680
- * The getPropertiesNode method retrieves the properties node from the definition.
681
- * It throws an error if the properties node is not found or is not a valid object.
682
- */
683
- static getPropertiesNode(definition) {
684
- if (!('properties' in definition) ||
685
- typeof definition.properties !== 'object' ||
686
- definition.properties === null ||
687
- Array.isArray(definition.properties)) {
688
- throw new Error("The definition does not contain a valid 'properties' node.");
689
- }
690
- return definition.properties;
711
+ }
712
+ /*
713
+ * The closeInterfaceDeclarationWithPadding function closes the interface declaration with padding.
714
+ * It takes the lines to write to and padding as parameters.
715
+ */
716
+ function closeInterfaceDeclarationWithPadding(lines, padding) {
717
+ lines.push(`${padding}}`);
718
+ }
719
+ /*
720
+ * The getRequiredFields method retrieves the required fields from the definition.
721
+ * It returns a Set of required field names.
722
+ */
723
+ function getRequiredFields(definition) {
724
+ if ('required' in definition && Array.isArray(definition.required)) {
725
+ // Convert all entries to string and add to Set
726
+ return new Set(definition.required.map((r) => String(r)));
691
727
  }
692
- /*
693
- * The determineTypeScriptType method determines the TypeScript type for a property.
694
- * It handles different cases such as arrays, nested properties, references, and optionsets.
695
- */
696
- static determineTypeScriptType(modelName, propValue, padding, propName) {
697
- // Check if this property has optionset data first
698
- if (propValue && propName && this.hasOptionsetData(propValue)) {
699
- const enumName = this.generateOptionsetEnumName(modelName, propName);
700
- return enumName;
701
- }
702
- if ((propValue === null || propValue === void 0 ? void 0 : propValue.type) === 'array' && typeof propValue.items === 'object' && propValue.items !== null) {
703
- return this.determineArrayType(modelName, propValue.items, padding);
704
- }
705
- else if ((propValue === null || propValue === void 0 ? void 0 : propValue.properties) && typeof propValue.properties === 'object') {
706
- return this.generatePropertyLines(modelName, propValue, false, padding + ' ');
707
- }
708
- else if (typeof (propValue === null || propValue === void 0 ? void 0 : propValue.$ref) === 'string') {
709
- return this.extractReferencedType(propValue.$ref);
710
- }
711
- else {
712
- return this.mapJsonTypeToTypeScript(propValue === null || propValue === void 0 ? void 0 : propValue.type);
713
- }
728
+ return new Set();
729
+ }
730
+ /*
731
+ * The getPropertiesNode method retrieves the properties node from the definition.
732
+ * It throws an error if the properties node is not found or is not a valid object.
733
+ */
734
+ function getPropertiesNode(definition) {
735
+ if (!('properties' in definition) ||
736
+ typeof definition.properties !== 'object' ||
737
+ definition.properties === null ||
738
+ Array.isArray(definition.properties)) {
739
+ throw new Error("The definition does not contain a valid 'properties' node.");
740
+ }
741
+ return definition.properties;
742
+ }
743
+ /*
744
+ * The determineTypeScriptType method determines the TypeScript type for a property.
745
+ * It handles different cases such as arrays, nested properties, references, and optionsets.
746
+ */
747
+ function determineTypeScriptType(modelName, propValue, padding, propName) {
748
+ // Check if this property has optionset data first
749
+ if (propValue && propName && hasOptionsetData(propValue)) {
750
+ const enumName = generateOptionsetEnumName(modelName, propName);
751
+ return enumName;
714
752
  }
715
- /*
716
- * The determineArrayType method determines the TypeScript type for an array property.
717
- * It handles different cases such as nested properties and references.
718
- */
719
- static determineArrayType(modelName, itemsNode, padding) {
720
- if ('properties' in itemsNode && typeof itemsNode.properties === 'object') {
721
- const inline = this.generatePropertyLines(modelName, itemsNode, false, padding + ' ');
722
- return `${inline}[]`;
723
- }
724
- else if ('$ref' in itemsNode && typeof itemsNode.$ref === 'string') {
725
- return `${this.extractReferencedType(itemsNode.$ref)}[]`;
726
- }
727
- else {
728
- const itemType = ('type' in itemsNode ? itemsNode.type : 'unknown');
729
- return this.mapJsonTypeToTypeScript(itemType) + '[]';
730
- }
753
+ if ((propValue === null || propValue === void 0 ? void 0 : propValue.type) === 'array' && typeof propValue.items === 'object' && propValue.items !== null) {
754
+ return determineArrayType(modelName, propValue.items, padding);
731
755
  }
732
- /*
733
- * The extractReferencedType method extracts the referenced type from the ref value.
734
- * It splits the ref value by '/' and returns the last part as the type name.
735
- * The result is converted to a valid TypeScript identifier (e.g., spaces become underscores).
736
- */
737
- static extractReferencedType(refValue) {
738
- const parts = refValue.split('/');
739
- const typeName = parts[parts.length - 1];
740
- return convertToValidIdentifier(this.normalizeGenericTypeName(typeName));
741
- }
742
- /*
743
- * The mapDataverseTypeToTypeScript method maps Dataverse types to TypeScript types.
744
- * Based on official Microsoft documentation: https://learn.microsoft.com/en-us/power-apps/maker/data-platform/types-of-fields
745
- */
746
- static mapDataverseTypeToTypeScript(dataverseType) {
747
- const lowerType = dataverseType.toLowerCase();
748
- switch (lowerType) {
749
- // Text-based types - all map to string
750
- case 'string':
751
- case 'stringtype':
752
- case 'text':
753
- case 'textarea':
754
- case 'multilinetext':
755
- case 'memotype':
756
- case 'memo':
757
- case 'email':
758
- case 'phone':
759
- case 'url':
760
- case 'tickersymbol':
761
- case 'entityname':
762
- case 'uniqueidentifier':
763
- case 'uniqueidentifiertype':
764
- return TypeScriptTypes.STRING;
765
- // Choice/Picklist types - return string (option labels)
766
- case 'picklist':
767
- case 'picklisttype':
768
- case 'choice':
769
- case 'choicetype':
770
- case 'choices':
771
- case 'choicestype':
772
- case 'multiselectpicklisttype':
773
- case 'state':
774
- case 'statetype':
775
- case 'status':
776
- case 'statustype':
777
- return TypeScriptTypes.STRING;
778
- // Number types
779
- case 'integer':
780
- case 'integertype':
781
- case 'wholenumber':
782
- case 'duration':
783
- case 'language':
784
- case 'timezone':
785
- return TypeScriptTypes.NUMBER;
786
- case 'bigint':
787
- case 'biginttype':
788
- case 'big':
789
- case 'timestamp':
790
- return TypeScriptTypes.NUMBER;
791
- case 'decimal':
792
- case 'decimaltype':
793
- case 'decimalnumber':
794
- return TypeScriptTypes.NUMBER;
795
- case 'double':
796
- case 'doubletype':
797
- case 'floatingpointnumber':
798
- return TypeScriptTypes.NUMBER;
799
- case 'money':
800
- case 'moneytype':
801
- case 'currency':
802
- return TypeScriptTypes.NUMBER;
803
- // Boolean type
804
- case 'boolean':
805
- case 'booleantype':
806
- case 'yesno':
807
- case 'twooptions':
808
- return TypeScriptTypes.BOOLEAN;
809
- // Date/Time types
810
- case 'datetime':
811
- case 'datetimetype':
812
- case 'dateonly':
813
- return TypeScriptTypes.DATE;
814
- // Lookup types - return string (typically GUID or display name)
815
- case 'lookup':
816
- case 'lookuptype':
817
- case 'customer':
818
- case 'customertype':
819
- case 'owner':
820
- case 'ownertype':
821
- return TypeScriptTypes.STRING;
822
- // Binary/File types - return string (base64 or URL)
823
- case 'file':
824
- case 'filetype':
825
- case 'image':
826
- case 'imagetype':
827
- return TypeScriptTypes.STRING;
828
- // Special types
829
- case 'virtual':
830
- case 'virtualtype':
831
- case 'formula':
832
- case 'formulatype':
833
- return TypeScriptTypes.STRING;
834
- default:
835
- return TypeScriptTypes.STRING; // Default Dataverse fields to string
836
- }
756
+ else if ((propValue === null || propValue === void 0 ? void 0 : propValue.properties) && typeof propValue.properties === 'object') {
757
+ return generatePropertyLines(modelName, propValue, false, padding + ' ');
837
758
  }
838
- /*
839
- * The mapJsonTypeToTypeScript method maps JSON types to TypeScript types.
840
- */
841
- static mapJsonTypeToTypeScript(jsonType) {
842
- switch (jsonType) {
843
- case 'string':
844
- return TypeScriptTypes.STRING;
845
- case 'number':
846
- case 'integer':
847
- return TypeScriptTypes.NUMBER;
848
- case 'boolean':
849
- return TypeScriptTypes.BOOLEAN;
850
- case 'object':
851
- return TypeScriptTypes.OBJECT;
852
- default:
853
- return TypeScriptTypes.UNKNOWN;
854
- }
759
+ else if (typeof (propValue === null || propValue === void 0 ? void 0 : propValue.$ref) === 'string') {
760
+ return extractReferencedType(propValue.$ref);
855
761
  }
856
- /*
857
- * The addPropertyComment method adds a comment for the property.
858
- */
859
- static addPropertyComment(lines, propValue, padding) {
860
- if ((propValue === null || propValue === void 0 ? void 0 : propValue.description) && typeof propValue.description === 'string') {
861
- lines.push(`${padding} // ${propValue.description}`);
862
- }
762
+ else {
763
+ return mapJsonTypeToTypeScript(propValue === null || propValue === void 0 ? void 0 : propValue.type);
863
764
  }
864
- /*
865
- * The generateModelFromParameters method generates TypeScript models from the parameters.
866
- */
867
- static generateModelFromParameters(modelName, parameterObject) {
868
- const lines = [];
869
- this.addInterfaceDeclaration(lines, modelName);
870
- const propName = this.getParameterName(parameterObject, 'name');
871
- const tsType = this.determineParameterType(parameterObject);
872
- const optionalMark = this.isParameterRequired(parameterObject) ? '' : '?';
873
- const propertyDefinition = `${propName}${optionalMark}: ${tsType}`;
874
- lines.push(` ${propertyDefinition};`);
875
- this.closeInterfaceDeclaration(lines);
876
- const propertyInfo = new ParameterPropertyInfo(propName, propertyDefinition);
877
- return { modelCode: lines.join('\n'), propertyInfo };
878
- }
879
- /*
880
- * The addInterfaceDeclaration method adds the interface declaration to the lines.
881
- * It takes the lines to write to and model name as parameters.
882
- */
883
- static addInterfaceDeclaration(lines, modelName) {
884
- lines.push(`export interface ${convertToValidIdentifier(modelName)} {`);
765
+ }
766
+ /*
767
+ * The determineArrayType method determines the TypeScript type for an array property.
768
+ * It handles different cases such as nested properties and references.
769
+ */
770
+ function determineArrayType(modelName, itemsNode, padding) {
771
+ if ('properties' in itemsNode && typeof itemsNode.properties === 'object') {
772
+ const inline = generatePropertyLines(modelName, itemsNode, false, padding + ' ');
773
+ return `${inline}[]`;
885
774
  }
886
- /*
887
- * The closeInterfaceDeclaration method closes the interface declaration.
888
- */
889
- static closeInterfaceDeclaration(lines) {
890
- lines.push('}');
775
+ else if ('$ref' in itemsNode && typeof itemsNode.$ref === 'string') {
776
+ return `${extractReferencedType(itemsNode.$ref)}[]`;
891
777
  }
892
- /*
893
- * The determineParameterType method determines the type of the parameter.
894
- * It takes the parameter object as input and returns the type as a string.
895
- */
896
- static determineParameterType(parameterObject) {
897
- if ('type' in parameterObject && typeof parameterObject.type === 'string') {
898
- return this.mapJsonTypeToTypeScript(parameterObject.type);
899
- }
900
- return 'unknown';
778
+ else {
779
+ const itemType = ('type' in itemsNode ? itemsNode.type : 'unknown');
780
+ return mapJsonTypeToTypeScript(itemType) + '[]';
901
781
  }
902
- /*
903
- * The isParameterRequired method checks if the parameter is required.
904
- * It takes the parameter object as input and returns true if required, otherwise false.
905
- */
906
- static isParameterRequired(parameterObject) {
907
- return ('required' in parameterObject &&
908
- (parameterObject.required === true || parameterObject.required === 'true'));
782
+ }
783
+ /*
784
+ * The extractReferencedType method extracts the referenced type from the ref value.
785
+ * It splits the ref value by '/' and returns the last part as the type name.
786
+ * The result is converted to a valid TypeScript identifier (e.g., spaces become underscores).
787
+ */
788
+ function extractReferencedType(refValue) {
789
+ const parts = refValue.split('/');
790
+ const typeName = parts[parts.length - 1];
791
+ return convertToValidIdentifier(normalizeGenericTypeName(typeName));
792
+ }
793
+ /*
794
+ * The mapDataverseTypeToTypeScript method maps Dataverse types to TypeScript types.
795
+ * Based on official Microsoft documentation: https://learn.microsoft.com/en-us/power-apps/maker/data-platform/types-of-fields
796
+ */
797
+ function mapDataverseTypeToTypeScript(dataverseType) {
798
+ const lowerType = dataverseType.toLowerCase();
799
+ switch (lowerType) {
800
+ // Text-based types - all map to string
801
+ case 'string':
802
+ case 'stringtype':
803
+ case 'text':
804
+ case 'textarea':
805
+ case 'multilinetext':
806
+ case 'memotype':
807
+ case 'memo':
808
+ case 'email':
809
+ case 'phone':
810
+ case 'url':
811
+ case 'tickersymbol':
812
+ case 'entityname':
813
+ case 'uniqueidentifier':
814
+ case 'uniqueidentifiertype':
815
+ return TypeScriptTypes.STRING;
816
+ // Choice/Picklist types - return string (option labels)
817
+ case 'picklist':
818
+ case 'picklisttype':
819
+ case 'choice':
820
+ case 'choicetype':
821
+ case 'choices':
822
+ case 'choicestype':
823
+ case 'multiselectpicklisttype':
824
+ case 'state':
825
+ case 'statetype':
826
+ case 'status':
827
+ case 'statustype':
828
+ return TypeScriptTypes.STRING;
829
+ // Number types
830
+ case 'integer':
831
+ case 'integertype':
832
+ case 'wholenumber':
833
+ case 'duration':
834
+ case 'language':
835
+ case 'timezone':
836
+ return TypeScriptTypes.NUMBER;
837
+ case 'bigint':
838
+ case 'biginttype':
839
+ case 'big':
840
+ case 'timestamp':
841
+ return TypeScriptTypes.NUMBER;
842
+ case 'decimal':
843
+ case 'decimaltype':
844
+ case 'decimalnumber':
845
+ return TypeScriptTypes.NUMBER;
846
+ case 'double':
847
+ case 'doubletype':
848
+ case 'floatingpointnumber':
849
+ return TypeScriptTypes.NUMBER;
850
+ case 'money':
851
+ case 'moneytype':
852
+ case 'currency':
853
+ return TypeScriptTypes.NUMBER;
854
+ // Boolean type
855
+ case 'boolean':
856
+ case 'booleantype':
857
+ case 'yesno':
858
+ case 'twooptions':
859
+ return TypeScriptTypes.BOOLEAN;
860
+ // Date/Time types
861
+ case 'datetime':
862
+ case 'datetimetype':
863
+ case 'dateonly':
864
+ return TypeScriptTypes.DATE;
865
+ // Lookup types - return string (typically GUID or display name)
866
+ case 'lookup':
867
+ case 'lookuptype':
868
+ case 'customer':
869
+ case 'customertype':
870
+ case 'owner':
871
+ case 'ownertype':
872
+ return TypeScriptTypes.STRING;
873
+ // Binary/File types - return string (base64 or URL)
874
+ case 'file':
875
+ case 'filetype':
876
+ case 'image':
877
+ case 'imagetype':
878
+ return TypeScriptTypes.STRING;
879
+ // Special types
880
+ case 'virtual':
881
+ case 'virtualtype':
882
+ case 'formula':
883
+ case 'formulatype':
884
+ return TypeScriptTypes.STRING;
885
+ default:
886
+ return TypeScriptTypes.STRING; // Default Dataverse fields to string
909
887
  }
910
- /*
911
- * The generateServiceFromPaths method generates the service class from the paths node.
912
- */
913
- static generateServiceFromPaths(modelName, dataSourceName, pathsNode, parameterTypes) {
914
- const lines = [];
915
- this.addCopyrightNotice(lines);
916
- const imports = this.collectStandardImports();
917
- const operations = this.processPaths(pathsNode, modelName, dataSourceName, imports, parameterTypes);
918
- this.addImports(lines, imports);
919
- this.addClassDefinition(lines, modelName, dataSourceName, operations);
920
- return lines.join('\n');
888
+ }
889
+ /*
890
+ * The mapJsonTypeToTypeScript method maps JSON types to TypeScript types.
891
+ */
892
+ function mapJsonTypeToTypeScript(jsonType) {
893
+ switch (jsonType) {
894
+ case 'string':
895
+ return TypeScriptTypes.STRING;
896
+ case 'number':
897
+ case 'integer':
898
+ return TypeScriptTypes.NUMBER;
899
+ case 'boolean':
900
+ return TypeScriptTypes.BOOLEAN;
901
+ case 'object':
902
+ return TypeScriptTypes.OBJECT;
903
+ default:
904
+ return TypeScriptTypes.UNKNOWN;
921
905
  }
922
- /*
923
- * The collectStandardImports method collects the standard imports for the service class.
924
- * It returns a Set of import statements.
925
- */
926
- static collectStandardImports(importType = 'execute') {
927
- if (!_a._schemaFolderPath || !_a._outputDir) {
928
- throw new Error('collectStandardImports called before generator context was initialized. Ensure run() was invoked.');
929
- }
930
- const vfs = getVfs();
931
- // Services are always emitted under <outputDir>/generated/services
932
- const servicesDir = vfs.join(_a._outputDir, this.SERVICES_FOLDER);
933
- // path to dataSourcesInfo.ts
934
- const dataSourceInfoPath = vfs.join(_a._schemaFolderPath, 'appschemas', 'dataSourcesInfo');
935
- let relativePath = path.relative(servicesDir, dataSourceInfoPath).replace(/\\/g, '/');
936
- if (!relativePath.startsWith('.') && !relativePath.startsWith('/')) {
937
- relativePath = `./${relativePath}`;
938
- }
939
- // Base imports common to both CRUD & execute contexts
940
- const imports = new Set([
941
- `import { dataSourcesInfo } from '${relativePath}';`,
942
- "import type { IOperationResult } from '@microsoft/power-apps/data';",
943
- "import { getClient } from '@microsoft/power-apps/data';",
944
- ]);
945
- // Reserved for future differentiation if importType impacts imports
946
- // Currently identical for 'crud' | 'execute' | 'both'.
947
- return imports;
948
- }
949
- /*
950
- * The processPaths method processes the paths node of the Swagger schema.
951
- * It extracts the operations and generates the methods for the service class.
952
- * It takes the paths node, model name, data source name, and imports set as parameters.
953
- * It returns a list of operations as strings.
954
- */
955
- static processPaths(pathsNode, modelName, dataSourceName, imports, parameterTypes) {
956
- const operations = [];
957
- for (const pathKey in pathsNode) {
958
- if (!Object.prototype.hasOwnProperty.call(pathsNode, pathKey)) {
959
- continue;
960
- }
961
- const pathObject = pathsNode[pathKey];
962
- if (typeof pathObject === 'object' && pathObject !== null && !Array.isArray(pathObject)) {
963
- const pathObjectTyped = pathObject;
964
- for (const operationKey in pathObjectTyped) {
965
- if (!Object.prototype.hasOwnProperty.call(pathObjectTyped, operationKey)) {
966
- continue;
967
- }
968
- const operationObject = pathObjectTyped[operationKey];
969
- if (typeof operationObject === 'object' &&
970
- operationObject !== null &&
971
- !Array.isArray(operationObject)) {
972
- this.processOperation(operationObject, modelName, dataSourceName, imports, operations, parameterTypes);
973
- }
974
- }
975
- }
976
- }
977
- return operations;
906
+ }
907
+ /*
908
+ * The addPropertyComment method adds a comment for the property.
909
+ */
910
+ function addPropertyComment(lines, propValue, padding) {
911
+ if ((propValue === null || propValue === void 0 ? void 0 : propValue.description) && typeof propValue.description === 'string') {
912
+ lines.push(`${padding} // ${propValue.description}`);
978
913
  }
979
- /*
980
- * The processOperation method processes each operation in the path object.
981
- * It extracts the operation ID, summary, description, and parameters.
982
- * It generates the method for the service class.
983
- */
984
- static processOperation(operationObject, modelName, dataSourceName, imports, operations, parameterTypes) {
985
- if (!('operationId' in operationObject)) {
986
- return;
987
- }
988
- const operationId = String(operationObject.operationId);
989
- const summary = 'summary' in operationObject ? String(operationObject.summary) : '';
990
- const description = 'description' in operationObject ? String(operationObject.description) : '';
991
- const outputModel = this.determineOutputModel(operationObject, modelName, imports);
992
- const { parametersList, parametersProps } = this.collectParameters(operationObject, modelName, imports, parameterTypes);
993
- this.generateOperationMethod(operations, operationId, summary, description, parametersList, parametersProps, outputModel, modelName);
994
- }
995
- /*
996
- * The determineOutputModel method determines the output model for the operation.
997
- * It takes the operation object, model name, and imports set as parameters.
998
- * It returns the output model as a string.
999
- */
1000
- static determineOutputModel(operationObject, modelName, imports) {
1001
- if ('responses' in operationObject &&
1002
- typeof operationObject.responses === 'object' &&
1003
- operationObject.responses !== null) {
1004
- const responsesObj = operationObject.responses;
1005
- if (typeof responsesObj === 'object' &&
1006
- responsesObj !== null &&
1007
- !Array.isArray(responsesObj) &&
1008
- '200' in responsesObj) {
1009
- const responsesObject = responsesObj;
1010
- const response200Value = responsesObject['200'];
1011
- if (typeof response200Value === 'object' &&
1012
- response200Value !== null &&
1013
- !Array.isArray(response200Value)) {
1014
- const response200Obj = response200Value;
1015
- if ('schema' in response200Obj &&
1016
- typeof response200Obj.schema === 'object' &&
1017
- response200Obj.schema !== null &&
1018
- !Array.isArray(response200Obj.schema)) {
1019
- const schemaObj = response200Obj.schema;
1020
- return this.extractOutputModelFromSchema(schemaObj, modelName, imports);
1021
- }
914
+ }
915
+ /*
916
+ * The generateModelFromParameters method generates TypeScript models from the parameters.
917
+ */
918
+ function generateModelFromParameters(modelName, parameterObject) {
919
+ const lines = [];
920
+ addInterfaceDeclaration(lines, modelName);
921
+ const propName = getParameterName(parameterObject, 'name');
922
+ const tsType = determineParameterType(parameterObject);
923
+ const optionalMark = isParameterRequired(parameterObject) ? '' : '?';
924
+ const propertyDefinition = `${propName}${optionalMark}: ${tsType}`;
925
+ lines.push(` ${propertyDefinition};`);
926
+ closeInterfaceDeclaration(lines);
927
+ const propertyInfo = new ParameterPropertyInfo(propName, propertyDefinition);
928
+ return { modelCode: lines.join('\n'), propertyInfo };
929
+ }
930
+ /*
931
+ * The addInterfaceDeclaration method adds the interface declaration to the lines.
932
+ * It takes the lines to write to and model name as parameters.
933
+ */
934
+ function addInterfaceDeclaration(lines, modelName) {
935
+ lines.push(`export interface ${convertToValidIdentifier(modelName)} {`);
936
+ }
937
+ /*
938
+ * The closeInterfaceDeclaration method closes the interface declaration.
939
+ */
940
+ function closeInterfaceDeclaration(lines) {
941
+ lines.push('}');
942
+ }
943
+ /*
944
+ * The determineParameterType method determines the type of the parameter.
945
+ * It takes the parameter object as input and returns the type as a string.
946
+ */
947
+ function determineParameterType(parameterObject) {
948
+ if ('type' in parameterObject && typeof parameterObject.type === 'string') {
949
+ return mapJsonTypeToTypeScript(parameterObject.type);
950
+ }
951
+ return 'unknown';
952
+ }
953
+ /*
954
+ * The isParameterRequired method checks if the parameter is required.
955
+ * It takes the parameter object as input and returns true if required, otherwise false.
956
+ */
957
+ function isParameterRequired(parameterObject) {
958
+ return ('required' in parameterObject &&
959
+ (parameterObject.required === true || parameterObject.required === 'true'));
960
+ }
961
+ /*
962
+ * The generateServiceFromPaths method generates the service class from the paths node.
963
+ */
964
+ function generateServiceFromPaths(modelName, dataSourceName, pathsNode, parameterTypes) {
965
+ const lines = [];
966
+ addCopyrightNotice(lines);
967
+ const imports = collectStandardImports();
968
+ const operations = processPaths(pathsNode, modelName, dataSourceName, imports, parameterTypes);
969
+ addImports(lines, imports);
970
+ addClassDefinition(lines, modelName, dataSourceName, operations);
971
+ return lines.join('\n');
972
+ }
973
+ /*
974
+ * The collectStandardImports method collects the standard imports for the service class.
975
+ * It returns a Set of import statements.
976
+ */
977
+ function collectStandardImports(importType = 'execute') {
978
+ if (!_schemaFolderPath || !_outputDir) {
979
+ throw new Error('collectStandardImports called before generator context was initialized. Ensure generateModelService() was invoked.');
980
+ }
981
+ const vfs = getVfs();
982
+ // Services are always emitted under <outputDir>/generated/services
983
+ const servicesDir = vfs.join(_outputDir, SERVICES_FOLDER);
984
+ // path to dataSourcesInfo.ts
985
+ const dataSourceInfoPath = vfs.join(_schemaFolderPath, 'appschemas', 'dataSourcesInfo');
986
+ let relativePath = path.relative(servicesDir, dataSourceInfoPath).replace(/\\/g, '/');
987
+ if (!relativePath.startsWith('.') && !relativePath.startsWith('/')) {
988
+ relativePath = `./${relativePath}`;
989
+ }
990
+ // Base imports common to both CRUD & execute contexts
991
+ const imports = new Set([
992
+ `import { dataSourcesInfo } from '${relativePath}';`,
993
+ "import type { IOperationResult } from '@microsoft/power-apps/data';",
994
+ "import { getClient } from '@microsoft/power-apps/data';",
995
+ ]);
996
+ // Reserved for future differentiation if importType impacts imports
997
+ // Currently identical for 'crud' | 'execute' | 'both'.
998
+ return imports;
999
+ }
1000
+ /*
1001
+ * The processPaths method processes the paths node of the Swagger schema.
1002
+ * It extracts the operations and generates the methods for the service class.
1003
+ * It takes the paths node, model name, data source name, and imports set as parameters.
1004
+ * It returns a list of operations as strings.
1005
+ */
1006
+ function processPaths(pathsNode, modelName, dataSourceName, imports, parameterTypes) {
1007
+ const operations = [];
1008
+ for (const pathKey in pathsNode) {
1009
+ if (!Object.prototype.hasOwnProperty.call(pathsNode, pathKey)) {
1010
+ continue;
1011
+ }
1012
+ const pathObject = pathsNode[pathKey];
1013
+ if (typeof pathObject === 'object' && pathObject !== null && !Array.isArray(pathObject)) {
1014
+ const pathObjectTyped = pathObject;
1015
+ for (const operationKey in pathObjectTyped) {
1016
+ if (!Object.prototype.hasOwnProperty.call(pathObjectTyped, operationKey)) {
1017
+ continue;
1018
+ }
1019
+ const operationObject = pathObjectTyped[operationKey];
1020
+ if (typeof operationObject === 'object' &&
1021
+ operationObject !== null &&
1022
+ !Array.isArray(operationObject)) {
1023
+ processOperation(operationObject, modelName, dataSourceName, imports, operations, parameterTypes);
1022
1024
  }
1023
1025
  }
1024
1026
  }
1025
- return 'void';
1026
1027
  }
1027
- /*
1028
- * The collectParameters method collects the parameters from the operation object.
1029
- * It takes the operation object, model name, and imports set as parameters.
1030
- * It returns an object containing the parameters list and parameters properties.
1031
- */
1032
- static collectParameters(operationObject, modelName, imports, parameterTypes) {
1033
- let parametersList = [];
1034
- let parametersProps = [];
1035
- if ('parameters' in operationObject && Array.isArray(operationObject.parameters)) {
1036
- const paramsArray = operationObject.parameters;
1037
- const orderedParams = this.getOrderedPropertiesByRequired(paramsArray);
1038
- for (const property of orderedParams) {
1039
- // property.Value in C# is property.value in TS - use property.value for the parameter object
1040
- const result = this.processRequestParameters(property.value, modelName, imports, parametersList, parametersProps, 'name', parameterTypes);
1041
- parametersList = result[0];
1042
- parametersProps = result[1];
1028
+ return operations;
1029
+ }
1030
+ /*
1031
+ * The processOperation method processes each operation in the path object.
1032
+ * It extracts the operation ID, summary, description, and parameters.
1033
+ * It generates the method for the service class.
1034
+ */
1035
+ function processOperation(operationObject, modelName, dataSourceName, imports, operations, parameterTypes) {
1036
+ if (!('operationId' in operationObject)) {
1037
+ return;
1038
+ }
1039
+ const operationId = String(operationObject.operationId);
1040
+ const summary = 'summary' in operationObject ? String(operationObject.summary) : '';
1041
+ const description = 'description' in operationObject ? String(operationObject.description) : '';
1042
+ const outputModel = determineOutputModel(operationObject, modelName, imports);
1043
+ const { parametersList, parametersProps } = collectParameters(operationObject, modelName, imports, parameterTypes);
1044
+ generateOperationMethod(operations, operationId, summary, description, parametersList, parametersProps, outputModel, modelName);
1045
+ }
1046
+ /*
1047
+ * The determineOutputModel method determines the output model for the operation.
1048
+ * It takes the operation object, model name, and imports set as parameters.
1049
+ * It returns the output model as a string.
1050
+ */
1051
+ function determineOutputModel(operationObject, modelName, imports) {
1052
+ if ('responses' in operationObject &&
1053
+ typeof operationObject.responses === 'object' &&
1054
+ operationObject.responses !== null) {
1055
+ const responsesObj = operationObject.responses;
1056
+ if (typeof responsesObj === 'object' &&
1057
+ responsesObj !== null &&
1058
+ !Array.isArray(responsesObj) &&
1059
+ '200' in responsesObj) {
1060
+ const responsesObject = responsesObj;
1061
+ const response200Value = responsesObject['200'];
1062
+ if (typeof response200Value === 'object' &&
1063
+ response200Value !== null &&
1064
+ !Array.isArray(response200Value)) {
1065
+ const response200Obj = response200Value;
1066
+ if ('schema' in response200Obj &&
1067
+ typeof response200Obj.schema === 'object' &&
1068
+ response200Obj.schema !== null &&
1069
+ !Array.isArray(response200Obj.schema)) {
1070
+ const schemaObj = response200Obj.schema;
1071
+ return extractOutputModelFromSchema(schemaObj, modelName, imports);
1072
+ }
1043
1073
  }
1044
1074
  }
1045
- return { parametersList, parametersProps };
1046
1075
  }
1047
- /*
1048
- * The addImports method adds the import statements to the lines.
1049
- * It takes the lines to write to and the imports set as parameters.
1050
- */
1051
- static addImports(lines, imports) {
1052
- const sortedImports = Array.from(imports);
1053
- sortedImports.sort();
1054
- lines.push(...sortedImports);
1055
- }
1056
- /*
1057
- * The addClassDefinition method adds the class definition to the lines.
1058
- * It takes the lines to write to, model name, data source name, and operations as parameters.
1059
- */
1060
- static addClassDefinition(lines, modelName, dataSourceName, operations) {
1061
- this.addServiceClassHeader(lines, modelName, dataSourceName);
1062
- lines.push(...operations);
1063
- lines.push('}');
1076
+ return 'void';
1077
+ }
1078
+ /*
1079
+ * The collectParameters method collects the parameters from the operation object.
1080
+ * It takes the operation object, model name, and imports set as parameters.
1081
+ * It returns an object containing the parameters list and parameters properties.
1082
+ */
1083
+ function collectParameters(operationObject, modelName, imports, parameterTypes) {
1084
+ let parametersList = [];
1085
+ let parametersProps = [];
1086
+ if ('parameters' in operationObject && Array.isArray(operationObject.parameters)) {
1087
+ const paramsArray = operationObject.parameters;
1088
+ const orderedParams = getOrderedPropertiesByRequired(paramsArray);
1089
+ for (const property of orderedParams) {
1090
+ // property.Value in C# is property.value in TS - use property.value for the parameter object
1091
+ const result = processRequestParameters(property.value, modelName, imports, parametersList, parametersProps, 'name', parameterTypes);
1092
+ parametersList = result[0];
1093
+ parametersProps = result[1];
1094
+ }
1095
+ }
1096
+ return { parametersList, parametersProps };
1097
+ }
1098
+ /*
1099
+ * The addImports method adds the import statements to the lines.
1100
+ * It takes the lines to write to and the imports set as parameters.
1101
+ */
1102
+ function addImports(lines, imports) {
1103
+ const sortedImports = Array.from(imports);
1104
+ sortedImports.sort();
1105
+ lines.push(...sortedImports);
1106
+ }
1107
+ /*
1108
+ * The addClassDefinition method adds the class definition to the lines.
1109
+ * It takes the lines to write to, model name, data source name, and operations as parameters.
1110
+ */
1111
+ function addClassDefinition(lines, modelName, dataSourceName, operations) {
1112
+ addServiceClassHeader(lines, modelName, dataSourceName);
1113
+ lines.push(...operations);
1114
+ lines.push('}');
1115
+ lines.push('');
1116
+ }
1117
+ /*
1118
+ * The addServiceClassHeader method adds the service class header to the lines.
1119
+ * It takes the lines to write to, model name, and data source name as parameters.
1120
+ */
1121
+ function addServiceClassHeader(lines, modelName, dataSourceName, additionalLine = true) {
1122
+ if (additionalLine) {
1064
1123
  lines.push('');
1065
1124
  }
1066
- /*
1067
- * The addServiceClassHeader method adds the service class header to the lines.
1068
- * It takes the lines to write to, model name, and data source name as parameters.
1069
- */
1070
- static addServiceClassHeader(lines, modelName, dataSourceName, additionalLine = true) {
1071
- if (additionalLine) {
1072
- lines.push('');
1073
- }
1074
- lines.push(`export class ${modelName}Service {`);
1075
- this.addServiceFields(lines, dataSourceName);
1076
- }
1077
- /*
1078
- * The extractOutputModelFromSchema method extracts the output model from the schema object.
1079
- */
1080
- static extractOutputModelFromSchema(schemaObj, modelName, imports) {
1081
- let outputModel = '';
1082
- if (this.tryExtractArrayOutputModel(schemaObj, modelName, imports, (result) => {
1083
- outputModel = result;
1084
- })) {
1085
- return outputModel;
1086
- }
1087
- if (this.tryExtractDirectRefOutputModel(schemaObj, modelName, imports, (result) => {
1088
- outputModel = result;
1089
- })) {
1090
- return outputModel;
1091
- }
1092
- const [found, typeOutputModel] = this.tryExtractTypeOutputModel(schemaObj);
1093
- if (found) {
1094
- outputModel = typeOutputModel;
1095
- return outputModel;
1096
- }
1097
- return 'void'; // Default to void if no specific type is defined
1098
- }
1099
- /*
1100
- * The tryExtractArrayOutputModel method extracts the array output model from the schema object.
1101
- * Returns true if type is successfully extracted, otherwise false.
1102
- */
1103
- static tryExtractArrayOutputModel(schemaObj, modelName, imports, setOutputModel) {
1104
- // First case: properties.value.type === 'array' && properties.value.items.$ref
1105
- if ('properties' in schemaObj &&
1106
- typeof schemaObj.properties === 'object' &&
1107
- schemaObj.properties !== null &&
1108
- !Array.isArray(schemaObj.properties)) {
1109
- const propertiesObj = schemaObj.properties;
1110
- if ('value' in propertiesObj &&
1111
- typeof propertiesObj.value === 'object' &&
1112
- propertiesObj.value !== null &&
1113
- !Array.isArray(propertiesObj.value)) {
1114
- const valueObj = propertiesObj.value;
1115
- if ('type' in valueObj &&
1116
- valueObj.type === 'array' &&
1117
- 'items' in valueObj &&
1118
- typeof valueObj.items === 'object' &&
1119
- valueObj.items !== null &&
1120
- !Array.isArray(valueObj.items)) {
1121
- const itemsObj = valueObj.items;
1122
- if ('$ref' in itemsObj && typeof itemsObj.$ref === 'string') {
1123
- const refType = this.extractReferencedType(itemsObj.$ref);
1124
- setOutputModel(`{ value: ${refType}[] }`);
1125
- this.addImport(refType, modelName, imports);
1126
- return true;
1127
- }
1125
+ lines.push(`export class ${modelName}Service {`);
1126
+ addServiceFields(lines, dataSourceName);
1127
+ }
1128
+ /*
1129
+ * The extractOutputModelFromSchema method extracts the output model from the schema object.
1130
+ */
1131
+ function extractOutputModelFromSchema(schemaObj, modelName, imports) {
1132
+ let outputModel = '';
1133
+ if (tryExtractArrayOutputModel(schemaObj, modelName, imports, (result) => {
1134
+ outputModel = result;
1135
+ })) {
1136
+ return outputModel;
1137
+ }
1138
+ if (tryExtractDirectRefOutputModel(schemaObj, modelName, imports, (result) => {
1139
+ outputModel = result;
1140
+ })) {
1141
+ return outputModel;
1142
+ }
1143
+ const [found, typeOutputModel] = tryExtractTypeOutputModel(schemaObj);
1144
+ if (found) {
1145
+ outputModel = typeOutputModel;
1146
+ return outputModel;
1147
+ }
1148
+ return 'void'; // Default to void if no specific type is defined
1149
+ }
1150
+ /*
1151
+ * The tryExtractArrayOutputModel method extracts the array output model from the schema object.
1152
+ * Returns true if type is successfully extracted, otherwise false.
1153
+ */
1154
+ function tryExtractArrayOutputModel(schemaObj, modelName, imports, setOutputModel) {
1155
+ // First case: properties.value.type === 'array' && properties.value.items.$ref
1156
+ if ('properties' in schemaObj &&
1157
+ typeof schemaObj.properties === 'object' &&
1158
+ schemaObj.properties !== null &&
1159
+ !Array.isArray(schemaObj.properties)) {
1160
+ const propertiesObj = schemaObj.properties;
1161
+ if ('value' in propertiesObj &&
1162
+ typeof propertiesObj.value === 'object' &&
1163
+ propertiesObj.value !== null &&
1164
+ !Array.isArray(propertiesObj.value)) {
1165
+ const valueObj = propertiesObj.value;
1166
+ if ('type' in valueObj &&
1167
+ valueObj.type === 'array' &&
1168
+ 'items' in valueObj &&
1169
+ typeof valueObj.items === 'object' &&
1170
+ valueObj.items !== null &&
1171
+ !Array.isArray(valueObj.items)) {
1172
+ const itemsObj = valueObj.items;
1173
+ if ('$ref' in itemsObj && typeof itemsObj.$ref === 'string') {
1174
+ const refType = extractReferencedType(itemsObj.$ref);
1175
+ setOutputModel(`{ value: ${refType}[] }`);
1176
+ addImport(refType, modelName, imports);
1177
+ return true;
1128
1178
  }
1129
1179
  }
1130
1180
  }
1131
- // Second case: type === 'array' && items.$ref
1132
- if ('type' in schemaObj &&
1133
- schemaObj.type === 'array' &&
1134
- 'items' in schemaObj &&
1135
- typeof schemaObj.items === 'object' &&
1136
- schemaObj.items !== null &&
1137
- !Array.isArray(schemaObj.items)) {
1138
- const itemsObj = schemaObj.items;
1139
- if ('$ref' in itemsObj && typeof itemsObj.$ref === 'string') {
1140
- const refType = this.extractReferencedType(itemsObj.$ref);
1141
- setOutputModel(`${refType}[]`);
1142
- this.addImport(refType, modelName, imports);
1143
- return true;
1144
- }
1145
- }
1146
- return false;
1147
1181
  }
1148
- /*
1149
- * The tryExtractDirectRefOutputModel method extracts the direct reference output model from the schema object.
1150
- * Returns true if type is successfully extracted, otherwise false.
1151
- */
1152
- static tryExtractDirectRefOutputModel(schemaObj, modelName, imports, setOutputModel) {
1153
- // Case: items.$ref
1154
- if ('items' in schemaObj &&
1155
- typeof schemaObj.items === 'object' &&
1156
- schemaObj.items !== null &&
1157
- !Array.isArray(schemaObj.items)) {
1158
- const itemsObj = schemaObj.items;
1159
- if ('$ref' in itemsObj && typeof itemsObj.$ref === 'string') {
1160
- const outputModel = this.simplifyGenericTypeName(this.extractReferencedType(itemsObj.$ref));
1161
- setOutputModel(outputModel);
1162
- this.addImport(outputModel, modelName, imports);
1163
- return true;
1164
- }
1165
- }
1166
- // Case: $ref
1167
- if ('$ref' in schemaObj && typeof schemaObj.$ref === 'string') {
1168
- const outputModel = this.simplifyGenericTypeName(this.extractReferencedType(schemaObj.$ref));
1169
- setOutputModel(outputModel);
1170
- this.addImport(outputModel, modelName, imports);
1182
+ // Second case: type === 'array' && items.$ref
1183
+ if ('type' in schemaObj &&
1184
+ schemaObj.type === 'array' &&
1185
+ 'items' in schemaObj &&
1186
+ typeof schemaObj.items === 'object' &&
1187
+ schemaObj.items !== null &&
1188
+ !Array.isArray(schemaObj.items)) {
1189
+ const itemsObj = schemaObj.items;
1190
+ if ('$ref' in itemsObj && typeof itemsObj.$ref === 'string') {
1191
+ const refType = extractReferencedType(itemsObj.$ref);
1192
+ setOutputModel(`${refType}[]`);
1193
+ addImport(refType, modelName, imports);
1171
1194
  return true;
1172
1195
  }
1173
- return false;
1174
1196
  }
1175
- /*
1176
- * The tryExtractTypeOutputModel method extracts the type output model from the schema object.
1177
- * Returns a tuple: [found: boolean, outputModel: string].
1178
- */
1179
- static tryExtractTypeOutputModel(schemaObj) {
1180
- if ('type' in schemaObj && typeof schemaObj.type === 'string') {
1181
- return [true, this.mapJsonTypeToTypeScript(schemaObj.type)];
1197
+ return false;
1198
+ }
1199
+ /*
1200
+ * The tryExtractDirectRefOutputModel method extracts the direct reference output model from the schema object.
1201
+ * Returns true if type is successfully extracted, otherwise false.
1202
+ */
1203
+ function tryExtractDirectRefOutputModel(schemaObj, modelName, imports, setOutputModel) {
1204
+ // Case: items.$ref
1205
+ if ('items' in schemaObj &&
1206
+ typeof schemaObj.items === 'object' &&
1207
+ schemaObj.items !== null &&
1208
+ !Array.isArray(schemaObj.items)) {
1209
+ const itemsObj = schemaObj.items;
1210
+ if ('$ref' in itemsObj && typeof itemsObj.$ref === 'string') {
1211
+ const outputModel = simplifyGenericTypeName(extractReferencedType(itemsObj.$ref));
1212
+ setOutputModel(outputModel);
1213
+ addImport(outputModel, modelName, imports);
1214
+ return true;
1182
1215
  }
1183
- return [false, ''];
1184
- }
1185
- /*
1186
- * The addImport method adds an import statement to the imports set.
1187
- * Takes the type name, model name, and imports set as parameters.
1188
- */
1189
- static addImport(typeName, modelName, imports) {
1190
- imports.add(`import type { ${typeName} } from '${this.RELATIVE_MODEL_PATH}/${modelName}Model';`);
1191
1216
  }
1192
- /*
1193
- * The simplifyGenericTypeName method simplifies the generic type name by removing the generic parameters.
1194
- */
1195
- static simplifyGenericTypeName(typeName) {
1196
- return this.normalizeGenericTypeName(typeName);
1217
+ // Case: $ref
1218
+ if ('$ref' in schemaObj && typeof schemaObj.$ref === 'string') {
1219
+ const outputModel = simplifyGenericTypeName(extractReferencedType(schemaObj.$ref));
1220
+ setOutputModel(outputModel);
1221
+ addImport(outputModel, modelName, imports);
1222
+ return true;
1197
1223
  }
1198
- /*
1199
- * The normalizeGenericTypeName method normalizes the generic type name.
1200
- * It replaces the brackets with underscores and returns the normalized name.
1201
- */
1202
- static normalizeGenericTypeName(typeName) {
1203
- return typeName.replace(/\[/g, '_').replace(/\]/g, '');
1224
+ return false;
1225
+ }
1226
+ /*
1227
+ * The tryExtractTypeOutputModel method extracts the type output model from the schema object.
1228
+ * Returns a tuple: [found: boolean, outputModel: string].
1229
+ */
1230
+ function tryExtractTypeOutputModel(schemaObj) {
1231
+ if ('type' in schemaObj && typeof schemaObj.type === 'string') {
1232
+ return [true, mapJsonTypeToTypeScript(schemaObj.type)];
1204
1233
  }
1205
- /*
1206
- * The processRequestParameters method processes the parameters of the operation.
1207
- * It extracts the parameter name and type from the schema and adds them to the lists.
1208
- * It takes the parameter object, model name, imports set, parameters list, and parameters properties as parameters.
1209
- */
1210
- static processRequestParameters(param, modelName, imports, parametersList, parametersProps, paramNodeName, parameterTypes) {
1211
- if (typeof param !== 'object' || param === null || Array.isArray(param)) {
1212
- return [parametersList, parametersProps];
1213
- }
1214
- const paramObj = param;
1215
- const paramName = this.getParameterName(paramObj, paramNodeName);
1216
- // Skip 'connectionId' parameter
1217
- if (paramName.toLowerCase() === 'connectionid') {
1218
- return [parametersList, parametersProps];
1219
- }
1220
- if (parameterTypes && parameterTypes[paramName]) {
1221
- parametersList.push(`${parameterTypes[paramName].propertyDefinition}`);
1222
- parametersProps.push(`${parameterTypes[paramName].propertyName}`);
1223
- }
1224
- else {
1225
- // Only add to imports if not an override
1226
- const paramType = this.getParameterType(paramObj, modelName, imports);
1227
- const optionalMark = this.isParameterRequired(paramObj) ? '' : '?';
1228
- // Normalize parameter name
1229
- const normalizedParamName = paramName.replace(/-/g, '_');
1230
- parametersList.push(`${normalizedParamName}${optionalMark}: ${paramType}`);
1231
- parametersProps.push(normalizedParamName);
1232
- }
1234
+ return [false, ''];
1235
+ }
1236
+ /*
1237
+ * The addImport method adds an import statement to the imports set.
1238
+ * Takes the type name, model name, and imports set as parameters.
1239
+ */
1240
+ function addImport(typeName, modelName, imports) {
1241
+ imports.add(`import type { ${typeName} } from '${RELATIVE_MODEL_PATH}/${modelName}Model';`);
1242
+ }
1243
+ /*
1244
+ * The simplifyGenericTypeName method simplifies the generic type name by removing the generic parameters.
1245
+ */
1246
+ function simplifyGenericTypeName(typeName) {
1247
+ return normalizeGenericTypeName(typeName);
1248
+ }
1249
+ /*
1250
+ * The normalizeGenericTypeName method normalizes the generic type name.
1251
+ * It replaces the brackets with underscores and returns the normalized name.
1252
+ */
1253
+ function normalizeGenericTypeName(typeName) {
1254
+ return typeName.replace(/\[/g, '_').replace(/\]/g, '');
1255
+ }
1256
+ /*
1257
+ * The processRequestParameters method processes the parameters of the operation.
1258
+ * It extracts the parameter name and type from the schema and adds them to the lists.
1259
+ * It takes the parameter object, model name, imports set, parameters list, and parameters properties as parameters.
1260
+ */
1261
+ function processRequestParameters(param, modelName, imports, parametersList, parametersProps, paramNodeName, parameterTypes) {
1262
+ if (typeof param !== 'object' || param === null || Array.isArray(param)) {
1233
1263
  return [parametersList, parametersProps];
1234
1264
  }
1235
- /*
1236
- * The getParameterName method extracts the name of the parameter from the schema.
1237
- * It handles the case where the schema is an array or a reference.
1238
- * It takes the parameter object and node name as parameters.
1239
- */
1240
- static getParameterName(paramObj, paramNodeName) {
1241
- const refNode = paramObj.$ref;
1242
- if (refNode && typeof refNode === 'string') {
1243
- const refValue = refNode;
1244
- if (refValue.toLowerCase().startsWith('#/parameters/')) {
1245
- const parameterName = refValue.substring('#/parameters/'.length);
1246
- return parameterName;
1247
- }
1248
- }
1249
- const nameNode = paramObj[paramNodeName];
1250
- return nameNode ? String(nameNode) : 'param';
1251
- }
1252
- /*
1253
- * The getParameterType method extracts the type of the parameter from the schema.
1254
- * It handles the case where the schema is an array or a reference.
1255
- * It takes the parameter object, model name, and imports set as parameters.
1256
- */
1257
- static getParameterType(paramObj, modelName, imports) {
1258
- if ('schema' in paramObj &&
1259
- typeof paramObj.schema === 'object' &&
1260
- paramObj.schema !== null &&
1261
- !Array.isArray(paramObj.schema)) {
1262
- return this.getSchemaBasedType(paramObj.schema, modelName, imports);
1263
- }
1264
- if ('$ref' in paramObj && typeof paramObj.$ref === 'string') {
1265
- return this.getRefBasedType(paramObj.$ref, modelName, imports);
1266
- }
1267
- if ('type' in paramObj && typeof paramObj.type === 'string') {
1268
- return this.mapJsonTypeToTypeScript(paramObj.type);
1269
- }
1270
- return 'unknown';
1265
+ const paramObj = param;
1266
+ const paramName = getParameterName(paramObj, paramNodeName);
1267
+ // Skip 'connectionId' parameter
1268
+ if (paramName.toLowerCase() === 'connectionid') {
1269
+ return [parametersList, parametersProps];
1271
1270
  }
1272
- /*
1273
- * The getSchemaBasedType method extracts the type from the schema object.
1274
- * It handles the case where the schema is an array or a reference.
1275
- * It takes the schema object, model name, and imports set as parameters.
1276
- */
1277
- static getSchemaBasedType(schemaObj, modelName, imports) {
1278
- if ('$ref' in schemaObj && typeof schemaObj.$ref === 'string') {
1279
- return this.getRefBasedType(schemaObj.$ref, modelName, imports);
1280
- }
1281
- if ('type' in schemaObj && typeof schemaObj.type === 'string') {
1282
- const typeValue = schemaObj.type;
1283
- // Handle binary format as string
1284
- if ('format' in schemaObj && schemaObj.format === 'binary' && typeValue === 'string') {
1285
- return 'string';
1286
- }
1287
- return this.mapJsonTypeToTypeScript(typeValue);
1288
- }
1289
- return 'unknown';
1271
+ if (parameterTypes && parameterTypes[paramName]) {
1272
+ parametersList.push(`${parameterTypes[paramName].propertyDefinition}`);
1273
+ parametersProps.push(`${parameterTypes[paramName].propertyName}`);
1290
1274
  }
1291
- /*
1292
- * The getRefBasedType method extracts the referenced type from the ref value.
1293
- * It adds the import statement for the referenced type to the imports set.
1294
- * It takes the ref value, model name, and imports set as parameters.
1295
- */
1296
- static getRefBasedType(refValue, modelName, imports) {
1297
- const refType = this.extractReferencedType(refValue);
1298
- this.addImport(refType, modelName, imports);
1299
- return refType;
1300
- }
1301
- /*
1302
- * The generateOperationMethod method generates a method for the service class.
1303
- * It takes the operations list, operation ID, summary, description,
1304
- * parameters list, parameters properties, output model, and data source name as parameters.
1305
- */
1306
- static generateOperationMethod(operations, operationId, summary, description, parametersList, parametersProps, outputModel, modelName) {
1307
- this.addMethodSummary(operations, summary, description);
1308
- const operationFuncName = this.formatOperationName(operationId);
1309
- const parameterSignature = this.getParameterSignature(parametersList);
1310
- const parameterObject = this.getParameterObject(parametersProps);
1311
- const parametersType = this.getParametersType(parametersList);
1312
- if (parametersList.length === 0) {
1313
- this.addMethodWithoutParameters(operations, operationFuncName, outputModel, operationId, modelName);
1314
- }
1315
- else {
1316
- this.addMethodWithParameters(operations, operationFuncName, parameterSignature, parametersType, parameterObject, outputModel, operationId, modelName);
1317
- }
1275
+ else {
1276
+ // Only add to imports if not an override
1277
+ const paramType = getParameterType(paramObj, modelName, imports);
1278
+ const optionalMark = isParameterRequired(paramObj) ? '' : '?';
1279
+ // Normalize parameter name
1280
+ const normalizedParamName = paramName.replace(/-/g, '_');
1281
+ parametersList.push(`${normalizedParamName}${optionalMark}: ${paramType}`);
1282
+ parametersProps.push(normalizedParamName);
1318
1283
  }
1319
- /*
1320
- * The addMethodSummary method adds a summary and description to the method.
1321
- * It takes the operations list, summary, and description as parameters.
1322
- */
1323
- static addMethodSummary(operations, summary, description) {
1324
- if (summary && summary.trim().length > 0) {
1325
- operations.push('');
1326
- operations.push(' /**');
1327
- operations.push(` * ${summary}`);
1328
- if (description && description.trim().length > 0) {
1329
- operations.push(` * ${description}`);
1330
- }
1331
- operations.push(' */');
1332
- }
1333
- else {
1334
- operations.push('');
1284
+ return [parametersList, parametersProps];
1285
+ }
1286
+ /*
1287
+ * The getParameterName method extracts the name of the parameter from the schema.
1288
+ * It handles the case where the schema is an array or a reference.
1289
+ * It takes the parameter object and node name as parameters.
1290
+ */
1291
+ function getParameterName(paramObj, paramNodeName) {
1292
+ const refNode = paramObj.$ref;
1293
+ if (refNode && typeof refNode === 'string') {
1294
+ const refValue = refNode;
1295
+ if (refValue.toLowerCase().startsWith('#/parameters/')) {
1296
+ const parameterName = refValue.substring('#/parameters/'.length);
1297
+ return parameterName;
1335
1298
  }
1336
1299
  }
1337
- /*
1338
- * The formatOperationName method formats the operation name by replacing invalid characters with underscores.
1339
- * It takes the operation ID as input and returns the formatted operation name.
1340
- */
1341
- static formatOperationName(operationId) {
1342
- return operationId.replace(/[^a-zA-Z0-9_]/g, '_');
1343
- }
1344
- /*
1345
- * The getParameterSignature method generates the parameter signature for the service class.
1346
- * It takes the parameters list as input and returns the parameter signature as a string.
1347
- */
1348
- static getParameterSignature(parametersList) {
1349
- return parametersList.length > 0 ? parametersList.join(', ') : '';
1350
- }
1351
- /*
1352
- * The getParameterObject method generates the parameter object for the service class.
1353
- * It takes the parameters properties list as input and returns the parameter object as a string.
1354
- */
1355
- static getParameterObject(parametersProps) {
1356
- return parametersProps.length > 0 ? parametersProps.join(', ') : '';
1300
+ const nameNode = paramObj[paramNodeName];
1301
+ return nameNode ? String(nameNode) : 'param';
1302
+ }
1303
+ /*
1304
+ * The getParameterType method extracts the type of the parameter from the schema.
1305
+ * It handles the case where the schema is an array or a reference.
1306
+ * It takes the parameter object, model name, and imports set as parameters.
1307
+ */
1308
+ function getParameterType(paramObj, modelName, imports) {
1309
+ if ('schema' in paramObj &&
1310
+ typeof paramObj.schema === 'object' &&
1311
+ paramObj.schema !== null &&
1312
+ !Array.isArray(paramObj.schema)) {
1313
+ return getSchemaBasedType(paramObj.schema, modelName, imports);
1357
1314
  }
1358
- /*
1359
- * The getParametersType method generates the parameters type for the service class.
1360
- * It takes the parameters list as input and returns the parameters type as a string.
1361
- */
1362
- static getParametersType(parametersList) {
1363
- return parametersList.length > 0 ? `{ ${parametersList.join(', ')} }` : 'object';
1315
+ if ('$ref' in paramObj && typeof paramObj.$ref === 'string') {
1316
+ return getRefBasedType(paramObj.$ref, modelName, imports);
1364
1317
  }
1365
- /*
1366
- * The addMethodWithoutParameters method generates a method without parameters for the service class.
1367
- * It takes the operations list, operation function name, output model, data source name,
1368
- * and operation ID as parameters.
1369
- */
1370
- static addMethodWithoutParameters(operations, operationFuncName, outputModel, operationId, modelName) {
1371
- operations.push(` public static async ${operationFuncName}(): Promise<IOperationResult<${outputModel}>> {`, ` const result = await ${modelName}Service.client.executeAsync<void, ${outputModel}>(`, ` {`, ` connectorOperation: {`, ` tableName: ${getServiceName(modelName)},`, ` operationName: '${operationId}',`, ` },`, ` });`, ` return result;`, ` }`);
1318
+ if ('type' in paramObj && typeof paramObj.type === 'string') {
1319
+ return mapJsonTypeToTypeScript(paramObj.type);
1372
1320
  }
1373
- /*
1374
- * The addMethodWithParameters method generates a method with parameters for the service class.
1375
- * It takes the operations list, operation function name, parameter signature, parameters type,
1376
- * parameter object, output model, data source name, and operation ID as parameters.
1377
- */
1378
- static addMethodWithParameters(operations, operationFuncName, parameterSignature, parametersType, parameterObject, outputModel, operationId, modelName) {
1379
- operations.push(` public static async ${operationFuncName}(${parameterSignature}): Promise<IOperationResult<${outputModel}>> {`, ` const params: ${parametersType} = { ${parameterObject} };`, ` const result = await ${modelName}Service.client.executeAsync<${parametersType}, ${outputModel}>(`, ` {`, ` connectorOperation: {`, ` tableName: ${getServiceName(modelName)},`, ` operationName: '${operationId}',`, ` parameters: params`, ` },`, ` });`, ` return result;`, ` }`);
1321
+ return 'unknown';
1322
+ }
1323
+ /*
1324
+ * The getSchemaBasedType method extracts the type from the schema object.
1325
+ * It handles the case where the schema is an array or a reference.
1326
+ * It takes the schema object, model name, and imports set as parameters.
1327
+ */
1328
+ function getSchemaBasedType(schemaObj, modelName, imports) {
1329
+ if ('$ref' in schemaObj && typeof schemaObj.$ref === 'string') {
1330
+ return getRefBasedType(schemaObj.$ref, modelName, imports);
1380
1331
  }
1381
- /*
1382
- * The handleSqlStoredProcedure method handles the SQL stored procedure generation.
1383
- */
1384
- static async handleSqlStoredProcedure(modelName, dataSourceName, schema, schemaJson, outputDir, vfs) {
1385
- const { modelCode, responseModelName } = this.generateStoredProcedureModel(modelName, schema);
1386
- const serviceCode = this.generateStoredProcedureService(modelName, dataSourceName, schema, responseModelName);
1387
- await this.writeModelFile(outputDir, modelName, modelCode, vfs);
1388
- await this.writeServiceFile(outputDir, modelName, serviceCode, vfs);
1389
- }
1390
- /*
1391
- * The generateStoredProcedureModel method generates the model code for the stored procedure.
1392
- * It takes the model name and schema as parameters and returns the generated model code.
1393
- */
1394
- static generateStoredProcedureModel(modelName, schema) {
1395
- const lines = [];
1396
- this.addCopyrightNotice(lines);
1397
- // Validate schema
1398
- if (typeof schema !== 'object' || schema === null || !('schema' in schema)) {
1399
- throw new Error('Invalid schema format for stored procedure.');
1400
- }
1401
- const schemaNode = schema.schema;
1402
- // Ensure schemaNode is a JsonObject
1403
- if (typeof schemaNode !== 'object' || schemaNode === null || Array.isArray(schemaNode)) {
1404
- throw new Error('Invalid schema node format for stored procedure.');
1332
+ if ('type' in schemaObj && typeof schemaObj.type === 'string') {
1333
+ const typeValue = schemaObj.type;
1334
+ // Handle binary format as string
1335
+ if ('format' in schemaObj && schemaObj.format === 'binary' && typeValue === 'string') {
1336
+ return 'string';
1405
1337
  }
1406
- this.addInputParametersModel(lines, modelName, schemaNode);
1407
- const responseModelName = this.addOutputParametersModel(lines, modelName, schemaNode);
1408
- return { modelCode: lines.join('\n'), responseModelName };
1338
+ return mapJsonTypeToTypeScript(typeValue);
1409
1339
  }
1410
- /*
1411
- * The addInputParametersModel method generates the input parameters model for the stored procedure.
1412
- * It takes the lines to write to, model name, and schema node as parameters.
1413
- */
1414
- static addInputParametersModel(lines, modelName, schemaNode) {
1415
- if ('inputparameters' in schemaNode &&
1416
- typeof schemaNode.inputparameters === 'object' &&
1417
- schemaNode.inputparameters !== null &&
1418
- !Array.isArray(schemaNode.inputparameters)) {
1419
- const requestModelName = `${modelName}Request`;
1420
- const modelCode = this.generateModelFromSchema(requestModelName, schemaNode.inputparameters);
1421
- lines.push(modelCode);
1422
- lines.push(''); // Add a blank line between models
1423
- }
1340
+ return 'unknown';
1341
+ }
1342
+ /*
1343
+ * The getRefBasedType method extracts the referenced type from the ref value.
1344
+ * It adds the import statement for the referenced type to the imports set.
1345
+ * It takes the ref value, model name, and imports set as parameters.
1346
+ */
1347
+ function getRefBasedType(refValue, modelName, imports) {
1348
+ const refType = extractReferencedType(refValue);
1349
+ addImport(refType, modelName, imports);
1350
+ return refType;
1351
+ }
1352
+ /*
1353
+ * The generateOperationMethod method generates a method for the service class.
1354
+ * It takes the operations list, operation ID, summary, description,
1355
+ * parameters list, parameters properties, output model, and data source name as parameters.
1356
+ */
1357
+ function generateOperationMethod(operations, operationId, summary, description, parametersList, parametersProps, outputModel, modelName) {
1358
+ addMethodSummary(operations, summary, description);
1359
+ const operationFuncName = formatOperationName(operationId);
1360
+ const parameterSignature = getParameterSignature(parametersList);
1361
+ const parameterObject = getParameterObject(parametersProps);
1362
+ const parametersType = getParametersType(parametersList);
1363
+ if (parametersList.length === 0) {
1364
+ addMethodWithoutParameters(operations, operationFuncName, outputModel, operationId, modelName);
1365
+ }
1366
+ else {
1367
+ addMethodWithParameters(operations, operationFuncName, parameterSignature, parametersType, parameterObject, outputModel, operationId, modelName);
1424
1368
  }
1425
- /*
1426
- * The addOutputParametersModel method generates the output parameters model for the stored procedure.
1427
- * It takes the lines to write to, model name, and schema node as parameters.
1428
- */
1429
- static addOutputParametersModel(lines, modelName, schemaNode) {
1430
- if ('procedureresultschema' in schemaNode &&
1431
- typeof schemaNode.procedureresultschema === 'object' &&
1432
- schemaNode.procedureresultschema !== null &&
1433
- !Array.isArray(schemaNode.procedureresultschema)) {
1434
- const responseModelName = `${modelName}Response`;
1435
- const modelCode = this.generateModelFromSchema(responseModelName, schemaNode.procedureresultschema);
1436
- lines.push(modelCode);
1437
- lines.push(''); // Add a blank line between models
1438
- return responseModelName;
1369
+ }
1370
+ /*
1371
+ * The addMethodSummary method adds a summary and description to the method.
1372
+ * It takes the operations list, summary, and description as parameters.
1373
+ */
1374
+ function addMethodSummary(operations, summary, description) {
1375
+ if (summary && summary.trim().length > 0) {
1376
+ operations.push('');
1377
+ operations.push(' /**');
1378
+ operations.push(` * ${summary}`);
1379
+ if (description && description.trim().length > 0) {
1380
+ operations.push(` * ${description}`);
1439
1381
  }
1440
- return '';
1441
- }
1442
- /*
1443
- * The generateStoredProcedureService method generates the service code for the stored procedure.
1444
- * It takes the model name, data source name, schema, and response model name as parameters.
1445
- */
1446
- static generateStoredProcedureService(modelName, dataSourceName, schema, responseModelName) {
1447
- const lines = [];
1448
- this.addCopyrightNotice(lines);
1449
- this.addServiceImports(lines, modelName, responseModelName);
1450
- this.addStoredProcedureServiceClassDefinition(lines, modelName, dataSourceName, schema, responseModelName);
1451
- return lines.join('\n');
1452
- }
1453
- /*
1454
- * The addServiceImports method adds necessary imports to the service file.
1455
- * It takes the lines to write to, model name, and response model name as parameters.
1456
- */
1457
- static addServiceImports(lines, modelName, responseModelName) {
1458
- const standardImports = this.collectStandardImports();
1459
- lines.push(...standardImports); // Add standard imports
1460
- lines.push(`import type { ${responseModelName} } from '${this.RELATIVE_MODEL_PATH}/${modelName}Model';`);
1461
- lines.push('');
1462
- }
1463
- /*
1464
- * The addServiceClassDefinition method generates the service class definition.
1465
- * It takes the lines to write to, model name, data source name, schema, and response model name as parameters.
1466
- */
1467
- static addStoredProcedureServiceClassDefinition(lines, modelName, dataSourceName, schema, responseModelName) {
1468
- this.addServiceClassHeader(lines, modelName, dataSourceName, false);
1469
- this.addStoredProcedureServiceMethods(lines, modelName, schema, responseModelName);
1470
- lines.push('}');
1471
- lines.push('');
1382
+ operations.push(' */');
1472
1383
  }
1473
- /*
1474
- * The addStoredProcedureServiceMethods method generates service methods for the stored procedure.
1475
- * It takes the lines to write to, model name, schema, and response model name as parameters.
1476
- */
1477
- static addStoredProcedureServiceMethods(lines, modelName, schema, responseModelName) {
1478
- if (schema !== null && typeof schema === 'object' && !Array.isArray(schema)) {
1479
- const schemaObj = schema;
1480
- const schemaNode = schemaObj.schema;
1481
- if (typeof schemaNode === 'object' && schemaNode !== null && !Array.isArray(schemaNode)) {
1482
- const schemaNodeObj = schemaNode;
1483
- const inputparameters = schemaNodeObj.inputparameters;
1484
- if (typeof inputparameters === 'object' &&
1485
- inputparameters !== null &&
1486
- !Array.isArray(inputparameters)) {
1487
- let parametersList = [];
1488
- let parametersProps = [];
1489
- const imports = new Set();
1490
- const inputparametersObj = inputparameters;
1491
- const inputProperties = inputparametersObj.properties;
1492
- if (typeof inputProperties === 'object' &&
1493
- inputProperties !== null &&
1494
- !Array.isArray(inputProperties)) {
1495
- // Collect properties with required status and original index
1496
- const orderedProperties = this.getOrderedPropertiesByRequired(inputProperties);
1497
- for (let i = 0; i < orderedProperties.length; i++) {
1498
- const property = orderedProperties[i];
1499
- [parametersList, parametersProps] = this.processRequestParameters(property.value, modelName, imports, parametersList, parametersProps, 'title');
1500
- }
1501
- }
1502
- this.generateOperationMethod(lines, modelName, '', '', parametersList, parametersProps, responseModelName, modelName);
1503
- }
1504
- }
1505
- }
1384
+ else {
1385
+ operations.push('');
1506
1386
  }
1507
- /*
1508
- * The getOrderedPropertiesByRequired method sorts the properties by required property and original index.
1509
- */
1510
- static getOrderedPropertiesByRequired(propertiesOrArray) {
1511
- let propertiesObj;
1512
- if (Array.isArray(propertiesOrArray)) {
1513
- propertiesObj = {};
1514
- for (let idx = 0; idx < propertiesOrArray.length; idx++) {
1515
- propertiesObj[`param_${idx}`] = propertiesOrArray[idx];
1516
- }
1517
- }
1518
- else if (typeof propertiesOrArray === 'object' && propertiesOrArray !== null) {
1519
- propertiesObj = propertiesOrArray;
1520
- }
1521
- else {
1522
- throw new Error('Input must be an object or array');
1523
- }
1524
- const propertyList = [];
1525
- let index = 0;
1526
- for (const key in propertiesObj) {
1527
- if (!Object.prototype.hasOwnProperty.call(propertiesObj, key)) {
1528
- continue;
1529
- }
1530
- const value = propertiesObj[key];
1531
- let required = false;
1532
- if (value && typeof value === 'object' && 'required' in value) {
1533
- required = String(value.required) === 'true';
1387
+ }
1388
+ /*
1389
+ * The formatOperationName method formats the operation name by replacing invalid characters with underscores.
1390
+ * It takes the operation ID as input and returns the formatted operation name.
1391
+ */
1392
+ function formatOperationName(operationId) {
1393
+ return operationId.replace(/[^a-zA-Z0-9_]/g, '_');
1394
+ }
1395
+ /*
1396
+ * The getParameterSignature method generates the parameter signature for the service class.
1397
+ * It takes the parameters list as input and returns the parameter signature as a string.
1398
+ */
1399
+ function getParameterSignature(parametersList) {
1400
+ return parametersList.length > 0 ? parametersList.join(', ') : '';
1401
+ }
1402
+ /*
1403
+ * The getParameterObject method generates the parameter object for the service class.
1404
+ * It takes the parameters properties list as input and returns the parameter object as a string.
1405
+ */
1406
+ function getParameterObject(parametersProps) {
1407
+ return parametersProps.length > 0 ? parametersProps.join(', ') : '';
1408
+ }
1409
+ /*
1410
+ * The getParametersType method generates the parameters type for the service class.
1411
+ * It takes the parameters list as input and returns the parameters type as a string.
1412
+ */
1413
+ function getParametersType(parametersList) {
1414
+ return parametersList.length > 0 ? `{ ${parametersList.join(', ')} }` : 'object';
1415
+ }
1416
+ /*
1417
+ * The addMethodWithoutParameters method generates a method without parameters for the service class.
1418
+ * It takes the operations list, operation function name, output model, data source name,
1419
+ * and operation ID as parameters.
1420
+ */
1421
+ function addMethodWithoutParameters(operations, operationFuncName, outputModel, operationId, modelName) {
1422
+ operations.push(` public static async ${operationFuncName}(): Promise<IOperationResult<${outputModel}>> {`, ` const result = await ${modelName}Service.client.executeAsync<void, ${outputModel}>(`, ` {`, ` connectorOperation: {`, ` tableName: ${getServiceName(modelName)},`, ` operationName: '${operationId}',`, ` },`, ` });`, ` return result;`, ` }`);
1423
+ }
1424
+ /*
1425
+ * The addMethodWithParameters method generates a method with parameters for the service class.
1426
+ * It takes the operations list, operation function name, parameter signature, parameters type,
1427
+ * parameter object, output model, data source name, and operation ID as parameters.
1428
+ */
1429
+ function addMethodWithParameters(operations, operationFuncName, parameterSignature, parametersType, parameterObject, outputModel, operationId, modelName) {
1430
+ operations.push(` public static async ${operationFuncName}(${parameterSignature}): Promise<IOperationResult<${outputModel}>> {`, ` const params: ${parametersType} = { ${parameterObject} };`, ` const result = await ${modelName}Service.client.executeAsync<${parametersType}, ${outputModel}>(`, ` {`, ` connectorOperation: {`, ` tableName: ${getServiceName(modelName)},`, ` operationName: '${operationId}',`, ` parameters: params`, ` },`, ` });`, ` return result;`, ` }`);
1431
+ }
1432
+ /*
1433
+ * The handleSqlStoredProcedure method handles the SQL stored procedure generation.
1434
+ */
1435
+ async function handleSqlStoredProcedure(modelName, dataSourceName, schema, schemaJson, outputDir, vfs) {
1436
+ const { modelCode, responseModelName } = generateStoredProcedureModel(modelName, schema);
1437
+ const serviceCode = generateStoredProcedureService(modelName, dataSourceName, schema, responseModelName);
1438
+ await writeModelFile(outputDir, modelName, modelCode, vfs);
1439
+ await writeServiceFile(outputDir, modelName, serviceCode, vfs);
1440
+ }
1441
+ /*
1442
+ * The generateStoredProcedureModel method generates the model code for the stored procedure.
1443
+ * It takes the model name and schema as parameters and returns the generated model code.
1444
+ */
1445
+ function generateStoredProcedureModel(modelName, schema) {
1446
+ const lines = [];
1447
+ addCopyrightNotice(lines);
1448
+ // Validate schema
1449
+ if (typeof schema !== 'object' || schema === null || !('schema' in schema)) {
1450
+ throw new Error('Invalid schema format for stored procedure.');
1451
+ }
1452
+ const schemaNode = schema.schema;
1453
+ // Ensure schemaNode is a JsonObject
1454
+ if (typeof schemaNode !== 'object' || schemaNode === null || Array.isArray(schemaNode)) {
1455
+ throw new Error('Invalid schema node format for stored procedure.');
1456
+ }
1457
+ addInputParametersModel(lines, modelName, schemaNode);
1458
+ const responseModelName = addOutputParametersModel(lines, modelName, schemaNode);
1459
+ return { modelCode: lines.join('\n'), responseModelName };
1460
+ }
1461
+ /*
1462
+ * The addInputParametersModel method generates the input parameters model for the stored procedure.
1463
+ * It takes the lines to write to, model name, and schema node as parameters.
1464
+ */
1465
+ function addInputParametersModel(lines, modelName, schemaNode) {
1466
+ if ('inputparameters' in schemaNode &&
1467
+ typeof schemaNode.inputparameters === 'object' &&
1468
+ schemaNode.inputparameters !== null &&
1469
+ !Array.isArray(schemaNode.inputparameters)) {
1470
+ const requestModelName = `${modelName}Request`;
1471
+ const modelCode = generateModelFromSchema(requestModelName, schemaNode.inputparameters);
1472
+ lines.push(modelCode);
1473
+ lines.push(''); // Add a blank line between models
1474
+ }
1475
+ }
1476
+ /*
1477
+ * The addOutputParametersModel method generates the output parameters model for the stored procedure.
1478
+ * It takes the lines to write to, model name, and schema node as parameters.
1479
+ */
1480
+ function addOutputParametersModel(lines, modelName, schemaNode) {
1481
+ if ('procedureresultschema' in schemaNode &&
1482
+ typeof schemaNode.procedureresultschema === 'object' &&
1483
+ schemaNode.procedureresultschema !== null &&
1484
+ !Array.isArray(schemaNode.procedureresultschema)) {
1485
+ const responseModelName = `${modelName}Response`;
1486
+ const modelCode = generateModelFromSchema(responseModelName, schemaNode.procedureresultschema);
1487
+ lines.push(modelCode);
1488
+ lines.push(''); // Add a blank line between models
1489
+ return responseModelName;
1490
+ }
1491
+ return '';
1492
+ }
1493
+ /*
1494
+ * The generateStoredProcedureService method generates the service code for the stored procedure.
1495
+ * It takes the model name, data source name, schema, and response model name as parameters.
1496
+ */
1497
+ function generateStoredProcedureService(modelName, dataSourceName, schema, responseModelName) {
1498
+ const lines = [];
1499
+ addCopyrightNotice(lines);
1500
+ addServiceImports(lines, modelName, responseModelName);
1501
+ addStoredProcedureServiceClassDefinition(lines, modelName, dataSourceName, schema, responseModelName);
1502
+ return lines.join('\n');
1503
+ }
1504
+ /*
1505
+ * The addServiceImports method adds necessary imports to the service file.
1506
+ * It takes the lines to write to, model name, and response model name as parameters.
1507
+ */
1508
+ function addServiceImports(lines, modelName, responseModelName) {
1509
+ const standardImports = collectStandardImports();
1510
+ lines.push(...standardImports); // Add standard imports
1511
+ lines.push(`import type { ${responseModelName} } from '${RELATIVE_MODEL_PATH}/${modelName}Model';`);
1512
+ lines.push('');
1513
+ }
1514
+ /*
1515
+ * The addServiceClassDefinition method generates the service class definition.
1516
+ * It takes the lines to write to, model name, data source name, schema, and response model name as parameters.
1517
+ */
1518
+ function addStoredProcedureServiceClassDefinition(lines, modelName, dataSourceName, schema, responseModelName) {
1519
+ addServiceClassHeader(lines, modelName, dataSourceName, false);
1520
+ addStoredProcedureServiceMethods(lines, modelName, schema, responseModelName);
1521
+ lines.push('}');
1522
+ lines.push('');
1523
+ }
1524
+ /*
1525
+ * The addStoredProcedureServiceMethods method generates service methods for the stored procedure.
1526
+ * It takes the lines to write to, model name, schema, and response model name as parameters.
1527
+ */
1528
+ function addStoredProcedureServiceMethods(lines, modelName, schema, responseModelName) {
1529
+ if (schema !== null && typeof schema === 'object' && !Array.isArray(schema)) {
1530
+ const schemaObj = schema;
1531
+ const schemaNode = schemaObj.schema;
1532
+ if (typeof schemaNode === 'object' && schemaNode !== null && !Array.isArray(schemaNode)) {
1533
+ const schemaNodeObj = schemaNode;
1534
+ const inputparameters = schemaNodeObj.inputparameters;
1535
+ if (typeof inputparameters === 'object' &&
1536
+ inputparameters !== null &&
1537
+ !Array.isArray(inputparameters)) {
1538
+ let parametersList = [];
1539
+ let parametersProps = [];
1540
+ const imports = new Set();
1541
+ const inputparametersObj = inputparameters;
1542
+ const inputProperties = inputparametersObj.properties;
1543
+ if (typeof inputProperties === 'object' &&
1544
+ inputProperties !== null &&
1545
+ !Array.isArray(inputProperties)) {
1546
+ // Collect properties with required status and original index
1547
+ const orderedProperties = getOrderedPropertiesByRequired(inputProperties);
1548
+ for (let i = 0; i < orderedProperties.length; i++) {
1549
+ const property = orderedProperties[i];
1550
+ [parametersList, parametersProps] = processRequestParameters(property.value, modelName, imports, parametersList, parametersProps, 'title');
1551
+ }
1552
+ }
1553
+ generateOperationMethod(lines, modelName, '', '', parametersList, parametersProps, responseModelName, modelName);
1534
1554
  }
1535
- propertyList.push({ key, value, required, index });
1536
- index++;
1537
1555
  }
1538
- // Required first, then original order
1539
- propertyList.sort((a, b) => {
1540
- const reqCompare = Number(b.required) - Number(a.required);
1541
- if (reqCompare !== 0) {
1542
- return reqCompare;
1543
- }
1544
- return a.index - b.index;
1545
- });
1546
- return propertyList.map((p) => ({ key: p.key, value: p.value }));
1547
1556
  }
1548
- /*
1549
- * The WriteModelFile method writes the generated model code to a file.
1550
- */
1551
- static async writeModelFile(outputDir, modelName, modelCode, vfs) {
1552
- const modelFilePath = vfs.join(outputDir, this.MODELS_FOLDER, `${modelName}Model.ts`);
1553
- await this.writeToFile(modelFilePath, [modelCode], vfs);
1554
- }
1555
- /*
1556
- * The WriteServiceFile method writes the generated service code to a file.
1557
- * It takes the output directory, model name, and service code as parameters.
1558
- */
1559
- static async writeServiceFile(outputDir, modelName, serviceCode, vfs) {
1560
- const serviceFilePath = vfs.join(outputDir, this.SERVICES_FOLDER, `${modelName}Service.ts`);
1561
- await this.writeToFile(serviceFilePath, [serviceCode], vfs);
1557
+ }
1558
+ /*
1559
+ * The getOrderedPropertiesByRequired method sorts the properties by required property and original index.
1560
+ */
1561
+ function getOrderedPropertiesByRequired(propertiesOrArray) {
1562
+ let propertiesObj;
1563
+ if (Array.isArray(propertiesOrArray)) {
1564
+ propertiesObj = {};
1565
+ for (let idx = 0; idx < propertiesOrArray.length; idx++) {
1566
+ propertiesObj[`param_${idx}`] = propertiesOrArray[idx];
1567
+ }
1568
+ }
1569
+ else if (typeof propertiesOrArray === 'object' && propertiesOrArray !== null) {
1570
+ propertiesObj = propertiesOrArray;
1571
+ }
1572
+ else {
1573
+ throw new Error('Input must be an object or array');
1574
+ }
1575
+ const propertyList = [];
1576
+ let index = 0;
1577
+ for (const key in propertiesObj) {
1578
+ if (!Object.prototype.hasOwnProperty.call(propertiesObj, key)) {
1579
+ continue;
1580
+ }
1581
+ const value = propertiesObj[key];
1582
+ let required = false;
1583
+ if (value && typeof value === 'object' && 'required' in value) {
1584
+ required = String(value.required) === 'true';
1585
+ }
1586
+ propertyList.push({ key, value, required, index });
1587
+ index++;
1588
+ }
1589
+ // Required first, then original order
1590
+ propertyList.sort((a, b) => {
1591
+ const reqCompare = Number(b.required) - Number(a.required);
1592
+ if (reqCompare !== 0) {
1593
+ return reqCompare;
1594
+ }
1595
+ return a.index - b.index;
1596
+ });
1597
+ return propertyList.map((p) => ({ key: p.key, value: p.value }));
1598
+ }
1599
+ /*
1600
+ * The WriteModelFile method writes the generated model code to a file.
1601
+ */
1602
+ async function writeModelFile(outputDir, modelName, modelCode, vfs) {
1603
+ const modelFilePath = vfs.join(outputDir, MODELS_FOLDER, `${modelName}Model.ts`);
1604
+ await writeToFile(modelFilePath, [modelCode], vfs);
1605
+ }
1606
+ /*
1607
+ * The WriteServiceFile method writes the generated service code to a file.
1608
+ * It takes the output directory, model name, and service code as parameters.
1609
+ */
1610
+ async function writeServiceFile(outputDir, modelName, serviceCode, vfs) {
1611
+ const serviceFilePath = vfs.join(outputDir, SERVICES_FOLDER, `${modelName}Service.ts`);
1612
+ await writeToFile(serviceFilePath, [serviceCode], vfs);
1613
+ }
1614
+ /*
1615
+ * The handleStandardSchema method generates TypeScript models and services from any schema type.
1616
+ * It routes to the appropriate handler based on schema type.
1617
+ */
1618
+ async function handleStandardSchema(modelName, dataSourceName, schema, schemaJson, outputDir, vfs) {
1619
+ // Check if this is a Dataverse entity schema (has nested schema.items structure)
1620
+ if (isDataverseEntity(schema)) {
1621
+ await handleDataverseSchema(modelName, dataSourceName, schema, outputDir, vfs);
1562
1622
  }
1563
- /*
1564
- * The handleStandardSchema method generates TypeScript models and services from any schema type.
1565
- * It routes to the appropriate handler based on schema type.
1566
- */
1567
- static async handleStandardSchema(modelName, dataSourceName, schema, schemaJson, outputDir, vfs) {
1568
- // Check if this is a Dataverse entity schema (has nested schema.items structure)
1569
- if (isDataverseEntity(schema)) {
1570
- await this.handleDataverseSchema(modelName, dataSourceName, schema, outputDir, vfs);
1571
- }
1572
- else {
1573
- await this.handleStandardSchemaOnly(modelName, dataSourceName, schemaJson, outputDir, vfs);
1574
- }
1575
- await this.ensureCommonModelFileExists(outputDir, vfs);
1623
+ else {
1624
+ await handleStandardSchemaOnly(modelName, dataSourceName, schemaJson, outputDir, vfs);
1576
1625
  }
1577
- /*
1578
- * The handleDataverseSchema method generates TypeScript models and services specifically for Dataverse entities.
1579
- */
1580
- static async handleDataverseSchema(modelName, dataSourceName, schema, outputDir, vfs) {
1581
- const schemaObject = schema;
1582
- if (typeof schemaObject.schema !== 'object' || schemaObject.schema === null) {
1583
- throw new Error(`Invalid Dataverse entity schema structure: missing 'schema' property for ${modelName}`);
1584
- }
1585
- const schemaProperty = schemaObject.schema;
1586
- if (typeof schemaProperty.items !== 'object' || schemaProperty.items === null) {
1587
- throw new Error(`Invalid Dataverse entity schema structure: missing 'schema.items' property for ${modelName}`);
1588
- }
1589
- const itemsNode = schemaProperty.items;
1590
- let primaryKey = 'id'; // default
1591
- // Extract primary key from Dataverse entity schema inline
1592
- const properties = itemsNode.properties;
1593
- if (properties) {
1594
- for (const [propName, propValue] of Object.entries(properties)) {
1595
- if (typeof propValue === 'object' && propValue !== null) {
1596
- const prop = propValue;
1597
- if (prop['x-ms-dataverse-primary-id'] === true || prop['x-ms-keyType'] === 'primary') {
1598
- primaryKey = propName;
1599
- break;
1600
- }
1626
+ await ensureCommonModelFileExists(outputDir, vfs);
1627
+ }
1628
+ /*
1629
+ * The handleDataverseSchema method generates TypeScript models and services specifically for Dataverse entities.
1630
+ */
1631
+ async function handleDataverseSchema(modelName, dataSourceName, schema, outputDir, vfs) {
1632
+ const schemaObject = schema;
1633
+ if (typeof schemaObject.schema !== 'object' || schemaObject.schema === null) {
1634
+ throw new Error(`Invalid Dataverse entity schema structure: missing 'schema' property for ${modelName}`);
1635
+ }
1636
+ const schemaProperty = schemaObject.schema;
1637
+ if (typeof schemaProperty.items !== 'object' || schemaProperty.items === null) {
1638
+ throw new Error(`Invalid Dataverse entity schema structure: missing 'schema.items' property for ${modelName}`);
1639
+ }
1640
+ const itemsNode = schemaProperty.items;
1641
+ let primaryKey = 'id'; // default
1642
+ // Extract primary key from Dataverse entity schema inline
1643
+ const properties = itemsNode.properties;
1644
+ if (properties) {
1645
+ for (const [propName, propValue] of Object.entries(properties)) {
1646
+ if (typeof propValue === 'object' && propValue !== null) {
1647
+ const prop = propValue;
1648
+ if (prop['x-ms-dataverse-primary-id'] === true || prop['x-ms-keyType'] === 'primary') {
1649
+ primaryKey = propName;
1650
+ break;
1601
1651
  }
1602
1652
  }
1603
1653
  }
1604
- // For Dataverse entities, generate both base and extended interfaces for lookup columns
1605
- const { baseInterface, extendedInterface } = this.generateDataverseInterfaces(modelName, itemsNode);
1606
- const hasLookupColumns = !!extendedInterface;
1607
- if (hasLookupColumns) {
1608
- // Generate both interfaces when there are lookup columns
1609
- const combinedModelCode = baseInterface + '\n\n' + extendedInterface;
1610
- await this.writeModelFile(outputDir, modelName, combinedModelCode, vfs);
1611
- }
1612
- else {
1613
- // Generate single interface when no lookup columns
1614
- await this.writeModelFile(outputDir, modelName, baseInterface, vfs);
1615
- }
1616
- const serviceCode = this.generateDataverseTypeScriptService(modelName, dataSourceName, primaryKey, hasLookupColumns);
1617
- await this.writeServiceFile(outputDir, modelName, serviceCode, vfs);
1618
1654
  }
1619
- /*
1620
- * The handleStandardSchemaOnly method generates TypeScript models and services for standard (non-Dataverse) schemas.
1621
- */
1622
- static async handleStandardSchemaOnly(modelName, dataSourceName, schemaJson, outputDir, vfs) {
1623
- const { modelCode, primaryKey } = this.generateTypeScriptModel(schemaJson, modelName);
1624
- const serviceCode = this.generateTypeScriptService(modelName, dataSourceName, primaryKey, false);
1625
- await this.writeModelFile(outputDir, modelName, modelCode, vfs);
1626
- await this.writeServiceFile(outputDir, modelName, serviceCode, vfs);
1655
+ // For Dataverse entities, generate both base and extended interfaces for lookup columns
1656
+ const { baseInterface, extendedInterface } = generateDataverseInterfaces(modelName, itemsNode);
1657
+ const hasLookupColumns = !!extendedInterface;
1658
+ if (hasLookupColumns) {
1659
+ // Generate both interfaces when there are lookup columns
1660
+ const combinedModelCode = baseInterface + '\n\n' + extendedInterface;
1661
+ await writeModelFile(outputDir, modelName, combinedModelCode, vfs);
1627
1662
  }
1628
- /**
1629
- * The handleSharepointSchema method handles SharePoint schema generation.
1630
- * It generates TypeScript models and services specifically for SharePoint data sources.
1631
- */
1632
- static async handleSharepointSchema(modelName, dataSourceName, schema, schemaJson, outputDir, vfs) {
1633
- // SharePoint-specific processing logic
1634
- const { modelCode, primaryKey, hasReferenceEntities } = this.generateSharepointTypeScriptModel(schemaJson, modelName);
1635
- // Generate SharePoint-specific service code
1636
- const serviceCode = this.generateSharepointService(modelName, dataSourceName, primaryKey, schemaJson, hasReferenceEntities);
1637
- await this.writeModelFile(outputDir, modelName, modelCode, vfs);
1638
- await this.writeServiceFile(outputDir, modelName, serviceCode, vfs);
1639
- await this.ensureCommonModelFileExists(outputDir, vfs);
1640
- }
1641
- /**
1642
- * Generates SharePoint-specific service code.
1643
- */
1644
- static generateSharepointService(modelName, dataSourceName, primaryKey, schemaJson, hasReferenceEntities) {
1645
- const lines = [];
1646
- this.addCopyrightNotice(lines);
1647
- this.addSharepointServiceImports(lines, modelName, hasReferenceEntities);
1648
- this.addSharepointServiceClassDefinition(lines, modelName, dataSourceName, schemaJson, primaryKey, hasReferenceEntities);
1649
- return lines.join('\n');
1663
+ else {
1664
+ // Generate single interface when no lookup columns
1665
+ await writeModelFile(outputDir, modelName, baseInterface, vfs);
1650
1666
  }
1651
- /**
1652
- * Adds the SharePoint service class definition to the generated code.
1653
- */
1654
- static addSharepointServiceClassDefinition(lines, modelName, dataSourceName, schemaJson, primaryKey, hasReferenceEntities) {
1655
- this.addServiceClassHeader(lines, modelName, dataSourceName, true);
1656
- // Add CRUD methods
1657
- const methods = [];
1658
- methods.push(this.generateSharepointCreateMethod(modelName, primaryKey, hasReferenceEntities));
1659
- methods.push(this.generateSharepointUpdateMethod(modelName, primaryKey, hasReferenceEntities));
1660
- methods.push(this.generateSharepointDeleteMethod(modelName));
1661
- methods.push(this.generateSharepointGetMethod(modelName, hasReferenceEntities));
1662
- methods.push(this.generateSharepointGetAllMethod(modelName, hasReferenceEntities));
1663
- for (const method of methods) {
1664
- lines.push('');
1665
- lines.push(...method);
1666
- }
1667
- // Add SharePoint-specific methods
1668
- this.addGetMethodForReferenceEntities(lines, modelName, dataSourceName, schemaJson);
1669
- lines.push('}');
1667
+ const serviceCode = generateDataverseTypeScriptService(modelName, dataSourceName, primaryKey, hasLookupColumns);
1668
+ await writeServiceFile(outputDir, modelName, serviceCode, vfs);
1669
+ }
1670
+ /*
1671
+ * The handleStandardSchemaOnly method generates TypeScript models and services for standard (non-Dataverse) schemas.
1672
+ */
1673
+ async function handleStandardSchemaOnly(modelName, dataSourceName, schemaJson, outputDir, vfs) {
1674
+ const { modelCode, primaryKey } = generateTypeScriptModel(schemaJson, modelName);
1675
+ const serviceCode = generateTypeScriptService(modelName, dataSourceName, primaryKey, false);
1676
+ await writeModelFile(outputDir, modelName, modelCode, vfs);
1677
+ await writeServiceFile(outputDir, modelName, serviceCode, vfs);
1678
+ }
1679
+ /**
1680
+ * The handleSharepointSchema method handles SharePoint schema generation.
1681
+ * It generates TypeScript models and services specifically for SharePoint data sources.
1682
+ */
1683
+ async function handleSharepointSchema(modelName, dataSourceName, schema, schemaJson, outputDir, vfs) {
1684
+ // SharePoint-specific processing logic
1685
+ const { modelCode, primaryKey, hasReferenceEntities } = generateSharepointTypeScriptModel(schemaJson, modelName);
1686
+ // Generate SharePoint-specific service code
1687
+ const serviceCode = generateSharepointService(modelName, dataSourceName, primaryKey, schemaJson, hasReferenceEntities);
1688
+ await writeModelFile(outputDir, modelName, modelCode, vfs);
1689
+ await writeServiceFile(outputDir, modelName, serviceCode, vfs);
1690
+ await ensureCommonModelFileExists(outputDir, vfs);
1691
+ }
1692
+ /**
1693
+ * Generates SharePoint-specific service code.
1694
+ */
1695
+ function generateSharepointService(modelName, dataSourceName, primaryKey, schemaJson, hasReferenceEntities) {
1696
+ const lines = [];
1697
+ addCopyrightNotice(lines);
1698
+ addSharepointServiceImports(lines, modelName, hasReferenceEntities);
1699
+ addSharepointServiceClassDefinition(lines, modelName, dataSourceName, schemaJson, primaryKey, hasReferenceEntities);
1700
+ return lines.join('\n');
1701
+ }
1702
+ /**
1703
+ * Adds the SharePoint service class definition to the generated code.
1704
+ */
1705
+ function addSharepointServiceClassDefinition(lines, modelName, dataSourceName, schemaJson, primaryKey, hasReferenceEntities) {
1706
+ addServiceClassHeader(lines, modelName, dataSourceName, true);
1707
+ // Add CRUD methods
1708
+ const methods = [];
1709
+ methods.push(generateSharepointCreateMethod(modelName, primaryKey, hasReferenceEntities));
1710
+ methods.push(generateSharepointUpdateMethod(modelName, primaryKey, hasReferenceEntities));
1711
+ methods.push(generateSharepointDeleteMethod(modelName));
1712
+ methods.push(generateSharepointGetMethod(modelName, hasReferenceEntities));
1713
+ methods.push(generateSharepointGetAllMethod(modelName, hasReferenceEntities));
1714
+ for (const method of methods) {
1670
1715
  lines.push('');
1716
+ lines.push(...method);
1671
1717
  }
1672
- /**
1673
- * Adds the SharePoint-specific get method for reference entities.
1674
- */
1675
- static addGetMethodForReferenceEntities(lines, modelName, dataSourceName, schemaJson) {
1676
- if (!schemaJson) {
1677
- return;
1678
- }
1679
- const schema = JSON.parse(schemaJson);
1680
- const referencedEntities = typeof schema.referencedEntities === 'object' && schema.referencedEntities !== null
1681
- ? schema.referencedEntities
1682
- : null;
1683
- if (referencedEntities) {
1684
- this.addGetMethodForReferenceEntitiesCore(lines, modelName);
1685
- }
1718
+ // Add SharePoint-specific methods
1719
+ addGetMethodForReferenceEntities(lines, modelName, dataSourceName, schemaJson);
1720
+ lines.push('}');
1721
+ lines.push('');
1722
+ }
1723
+ /**
1724
+ * Adds the SharePoint-specific get method for reference entities.
1725
+ */
1726
+ function addGetMethodForReferenceEntities(lines, modelName, dataSourceName, schemaJson) {
1727
+ if (!schemaJson) {
1728
+ return;
1686
1729
  }
1687
- /*
1688
- * The get method adds get function for sharepoint referenceEntities
1689
- */
1690
- static addGetMethodForReferenceEntitiesCore(lines, modelName) {
1691
- // Add method summary
1692
- lines.push(`/**`, `* Utility method to get possible values of a referenced entity by its name.`, `*/`, ` public static async getReferencedEntity(search: string, referencedEntityKeyNormalized: string): Promise<IOperationResult<unknown>> {`, ` const params: {search: string} = { search };`, ` const result = await ${modelName}Service.client.executeAsync<{search: string}, unknown>(`, ` {`, ` connectorOperation: {`, ` tableName: ${getServiceName(modelName)},`, ` operationName: 'Get' + referencedEntityKeyNormalized,`, ` parameters: params`, ` }`, ` });`, ` return result;`, ` }`, ``);
1730
+ const schema = JSON.parse(schemaJson);
1731
+ const referencedEntities = typeof schema.referencedEntities === 'object' && schema.referencedEntities !== null
1732
+ ? schema.referencedEntities
1733
+ : null;
1734
+ if (referencedEntities) {
1735
+ addGetMethodForReferenceEntitiesCore(lines, modelName);
1693
1736
  }
1694
- /*
1695
- * The ensureCommonModelFileExists method checks if the CommonModels.ts file exists in the specified output directory.
1696
- * If it does not exist, it creates the file with a copyright notice and common model code.
1697
- */
1698
- static async ensureCommonModelFileExists(outputDir, vfs) {
1699
- // Node.js built-in modules are statically imported at the top of the file
1700
- const commonModelPath = vfs.join(outputDir, this.MODELS_FOLDER, 'CommonModels.ts');
1701
- if (!(await vfs.exists(commonModelPath))) {
1702
- const lines = [];
1703
- this.addCopyrightNotice(lines);
1704
- this.addCommonModelCode(lines);
1705
- await this.writeToFile(commonModelPath, lines, vfs);
1706
- }
1737
+ }
1738
+ /*
1739
+ * The get method adds get function for sharepoint referenceEntities
1740
+ */
1741
+ function addGetMethodForReferenceEntitiesCore(lines, modelName) {
1742
+ // Add method summary
1743
+ lines.push(`/**`, `* Utility method to get possible values of a referenced entity by its name.`, `*/`, ` public static async getReferencedEntity(search: string, referencedEntityKeyNormalized: string): Promise<IOperationResult<unknown>> {`, ` const params: {search: string} = { search };`, ` const result = await ${modelName}Service.client.executeAsync<{search: string}, unknown>(`, ` {`, ` connectorOperation: {`, ` tableName: ${getServiceName(modelName)},`, ` operationName: 'Get' + referencedEntityKeyNormalized,`, ` parameters: params`, ` }`, ` });`, ` return result;`, ` }`, ``);
1744
+ }
1745
+ /*
1746
+ * The ensureCommonModelFileExists method checks if the CommonModels.ts file exists in the specified output directory.
1747
+ * If it does not exist, it creates the file with a copyright notice and common model code.
1748
+ */
1749
+ async function ensureCommonModelFileExists(outputDir, vfs) {
1750
+ // Node.js built-in modules are statically imported at the top of the file
1751
+ const commonModelPath = vfs.join(outputDir, MODELS_FOLDER, 'CommonModels.ts');
1752
+ if (!(await vfs.exists(commonModelPath))) {
1753
+ const lines = [];
1754
+ addCopyrightNotice(lines);
1755
+ addCommonModelCode(lines);
1756
+ await writeToFile(commonModelPath, lines, vfs);
1707
1757
  }
1708
- /*
1709
- * The addCommonModelCode method generates common TypeScript model code for the CommonModels.ts file.
1710
- * It includes interfaces for IGetOptions and IGetAllOptions.
1711
- */
1712
- static addCommonModelCode(lines) {
1713
- lines.push('export interface IGetOptions {');
1714
- lines.push(' select?: string[];');
1715
- lines.push('};');
1716
- lines.push('');
1717
- lines.push('export interface IGetAllOptions {');
1718
- lines.push(' maxPageSize?: number;');
1719
- lines.push(' select?: string[];');
1720
- lines.push(' filter?: string;');
1721
- lines.push(' orderBy?: string[];');
1722
- lines.push(' top?: number;');
1723
- lines.push(' skip?: number;');
1724
- lines.push(' skipToken?: string;');
1725
- lines.push('}');
1726
- lines.push('');
1758
+ }
1759
+ /*
1760
+ * The addCommonModelCode method generates common TypeScript model code for the CommonModels.ts file.
1761
+ * It includes interfaces for IGetOptions and IGetAllOptions.
1762
+ */
1763
+ function addCommonModelCode(lines) {
1764
+ lines.push('export interface IGetOptions {');
1765
+ lines.push(' select?: string[];');
1766
+ lines.push('};');
1767
+ lines.push('');
1768
+ lines.push('export interface IGetAllOptions {');
1769
+ lines.push(' maxPageSize?: number;');
1770
+ lines.push(' select?: string[];');
1771
+ lines.push(' filter?: string;');
1772
+ lines.push(' orderBy?: string[];');
1773
+ lines.push(' top?: number;');
1774
+ lines.push(' skip?: number;');
1775
+ lines.push(' skipToken?: string;');
1776
+ lines.push('}');
1777
+ lines.push('');
1778
+ }
1779
+ /*
1780
+ * The generateTypeScriptModel method generates a TypeScript model from the JSON schema.
1781
+ * It returns the generated model code and the primary key of the model.
1782
+ */
1783
+ function generateTypeScriptModel(schemaJson, modelName) {
1784
+ const root = parseSchemaJson(schemaJson);
1785
+ const propertiesNode = getPropertiesNode(root);
1786
+ const requiredFields = getRequiredFieldsFromStandardSchema(root);
1787
+ const lines = [];
1788
+ addCopyrightNotice(lines);
1789
+ const primaryKeyObj = { value: '' };
1790
+ addModelInterfaceDeclaration(lines, modelName, propertiesNode, requiredFields, primaryKeyObj);
1791
+ return { modelCode: lines.join('\n'), primaryKey: primaryKeyObj.value };
1792
+ }
1793
+ function generateSharepointTypeScriptModel(schemaJson, modelName) {
1794
+ const root = parseSchemaJson(schemaJson);
1795
+ const propertiesNode = getPropertiesNode(root);
1796
+ const requiredFields = getRequiredFieldsFromStandardSchema(root);
1797
+ const lines = [];
1798
+ addCopyrightNotice(lines);
1799
+ const primaryKeyObj = { value: '' };
1800
+ const { hasReferenceEntities } = addSPModelInterfaceDeclaration(lines, modelName, propertiesNode, requiredFields, primaryKeyObj, schemaJson);
1801
+ return { modelCode: lines.join('\n'), primaryKey: primaryKeyObj.value, hasReferenceEntities };
1802
+ }
1803
+ /*
1804
+ * The parseSchemaJson method parses the JSON schema and retrieves the items node.
1805
+ * It throws an exception if the schema format is invalid.
1806
+ */
1807
+ function parseSchemaJson(schemaJson) {
1808
+ const root = JSON.parse(schemaJson);
1809
+ if (!root.schema || !root.schema.items) {
1810
+ throw new Error('Invalid schema format');
1727
1811
  }
1728
- /*
1729
- * The generateTypeScriptModel method generates a TypeScript model from the JSON schema.
1730
- * It returns the generated model code and the primary key of the model.
1731
- */
1732
- static generateTypeScriptModel(schemaJson, modelName) {
1733
- const root = this.parseSchemaJson(schemaJson);
1734
- const propertiesNode = this.getPropertiesNode(root);
1735
- const requiredFields = this.getRequiredFieldsFromStandardSchema(root);
1736
- const lines = [];
1737
- this.addCopyrightNotice(lines);
1738
- const primaryKeyObj = { value: '' };
1739
- this.addModelInterfaceDeclaration(lines, modelName, propertiesNode, requiredFields, primaryKeyObj);
1740
- return { modelCode: lines.join('\n'), primaryKey: primaryKeyObj.value };
1741
- }
1742
- static generateSharepointTypeScriptModel(schemaJson, modelName) {
1743
- const root = this.parseSchemaJson(schemaJson);
1744
- const propertiesNode = this.getPropertiesNode(root);
1745
- const requiredFields = this.getRequiredFieldsFromStandardSchema(root);
1746
- const lines = [];
1747
- this.addCopyrightNotice(lines);
1748
- const primaryKeyObj = { value: '' };
1749
- const { hasReferenceEntities } = this.addSPModelInterfaceDeclaration(lines, modelName, propertiesNode, requiredFields, primaryKeyObj, schemaJson);
1750
- return { modelCode: lines.join('\n'), primaryKey: primaryKeyObj.value, hasReferenceEntities };
1751
- }
1752
- /*
1753
- * The parseSchemaJson method parses the JSON schema and retrieves the items node.
1754
- * It throws an exception if the schema format is invalid.
1755
- */
1756
- static parseSchemaJson(schemaJson) {
1757
- const root = JSON.parse(schemaJson);
1758
- if (!root.schema || !root.schema.items) {
1759
- throw new Error('Invalid schema format');
1760
- }
1761
- const items = root.schema.items;
1762
- if (typeof items !== 'object' || items === null || Array.isArray(items)) {
1763
- throw new Error('Schema items must be a valid object');
1764
- }
1765
- return items;
1812
+ const items = root.schema.items;
1813
+ if (typeof items !== 'object' || items === null || Array.isArray(items)) {
1814
+ throw new Error('Schema items must be a valid object');
1766
1815
  }
1767
- /*
1768
- * The getRequiredFieldsFromStandardSchema method retrieves the required fields from the JSON schema.
1769
- * It returns a Set of required field names.
1770
- */
1771
- static getRequiredFieldsFromStandardSchema(itemsNode) {
1772
- if (typeof itemsNode !== 'object' || itemsNode === null || Array.isArray(itemsNode)) {
1773
- return new Set();
1774
- }
1775
- const itemsObj = itemsNode;
1776
- if (!itemsObj.required || !Array.isArray(itemsObj.required)) {
1777
- return new Set();
1778
- }
1779
- return new Set(itemsObj.required.map((r) => String(r)));
1816
+ return items;
1817
+ }
1818
+ /*
1819
+ * The getRequiredFieldsFromStandardSchema method retrieves the required fields from the JSON schema.
1820
+ * It returns a Set of required field names.
1821
+ */
1822
+ function getRequiredFieldsFromStandardSchema(itemsNode) {
1823
+ if (typeof itemsNode !== 'object' || itemsNode === null || Array.isArray(itemsNode)) {
1824
+ return new Set();
1780
1825
  }
1781
- /*
1782
- * The addSPModelInterfaceDeclaration method generates the TypeScript interface declaration for the model particular to Sharepoint.
1783
- */
1784
- static addSPModelInterfaceDeclaration(lines, modelName, propertiesNode, requiredFields, primaryKeyObj, schemaJson) {
1785
- const result = this.addSPReferencedEntityModelInterfaceDeclaration(schemaJson, lines);
1786
- const referenceEntitiesObjectMapping = result.mapping;
1787
- // lines are already updated by the method above
1788
- // We will generate 3 interfaces:
1789
- // A base one with properties that are not reference entities
1790
- // A "read" model that extends the base one and adds reference entity properties with an object type (inferred from the referencedEntities section)
1791
- // A "write" model that extends the base one and adds reference entities with the string/string[] type
1792
- // All 3 interfaces will be exported
1793
- const baseInterfaceLines = [];
1794
- const readInterfaceLines = [];
1795
- const writeInterfaceLines = [];
1796
- // This will be true if there is at least one referenced entity
1797
- // This flag is later used to determine if we need to use multiple interfaces in the service file
1798
- let hasReferenceEntities = false;
1799
- baseInterfaceLines.push(`export interface ${modelName}Base {`);
1800
- readInterfaceLines.push(`export interface ${modelName}Read extends ${modelName}Base {`);
1801
- writeInterfaceLines.push(`export interface ${modelName}Write extends ${modelName}Base {`);
1802
- // lines.push(`export interface ${modelName} {`);
1803
- for (const propName of Object.keys(propertiesNode)) {
1804
- let propNameNormalized = propName;
1805
- if (!isValidPropertyName(propName)) {
1806
- propNameNormalized = `"${propName}"`; // Enclose invalid property names in double quotes
1807
- }
1808
- const isRequired = requiredFields.has(propName);
1809
- const optionalMark = isRequired ? '' : '?';
1810
- if (propName in referenceEntitiesObjectMapping) {
1811
- if (isSharepointChoiceColumn(propertiesNode[propName])) {
1812
- // Note: SharePoint choice/multi-choice columns are intentionally mapped to string/string[]
1813
- // instead of the schema's { Value: string } object. This simplifies the SDK's public
1814
- // surface and prevents recurring errors in AI agents (especially App Builder) that
1815
- // frequently mishandled the complex object wrapper.
1816
- if (propertiesNode[propName].type === 'array') {
1817
- writeInterfaceLines.push(` ${propNameNormalized}${optionalMark}: string[];`);
1818
- }
1819
- else {
1820
- writeInterfaceLines.push(` ${propNameNormalized}${optionalMark}: string;`);
1821
- }
1826
+ const itemsObj = itemsNode;
1827
+ if (!itemsObj.required || !Array.isArray(itemsObj.required)) {
1828
+ return new Set();
1829
+ }
1830
+ return new Set(itemsObj.required.map((r) => String(r)));
1831
+ }
1832
+ /*
1833
+ * The addSPModelInterfaceDeclaration method generates the TypeScript interface declaration for the model particular to Sharepoint.
1834
+ */
1835
+ function addSPModelInterfaceDeclaration(lines, modelName, propertiesNode, requiredFields, primaryKeyObj, schemaJson) {
1836
+ const result = addSPReferencedEntityModelInterfaceDeclaration(schemaJson, lines);
1837
+ const referenceEntitiesObjectMapping = result.mapping;
1838
+ // lines are already updated by the method above
1839
+ // We will generate 3 interfaces:
1840
+ // A base one with properties that are not reference entities
1841
+ // A "read" model that extends the base one and adds reference entity properties with an object type (inferred from the referencedEntities section)
1842
+ // A "write" model that extends the base one and adds reference entities with the string/string[] type
1843
+ // All 3 interfaces will be exported
1844
+ const baseInterfaceLines = [];
1845
+ const readInterfaceLines = [];
1846
+ const writeInterfaceLines = [];
1847
+ // This will be true if there is at least one referenced entity
1848
+ // This flag is later used to determine if we need to use multiple interfaces in the service file
1849
+ let hasReferenceEntities = false;
1850
+ baseInterfaceLines.push(`export interface ${modelName}Base {`);
1851
+ readInterfaceLines.push(`export interface ${modelName}Read extends ${modelName}Base {`);
1852
+ writeInterfaceLines.push(`export interface ${modelName}Write extends ${modelName}Base {`);
1853
+ // lines.push(`export interface ${modelName} {`);
1854
+ for (const propName of Object.keys(propertiesNode)) {
1855
+ let propNameNormalized = propName;
1856
+ if (!isValidPropertyName(propName)) {
1857
+ propNameNormalized = `"${propName}"`; // Enclose invalid property names in double quotes
1858
+ }
1859
+ const isRequired = requiredFields.has(propName);
1860
+ const optionalMark = isRequired ? '' : '?';
1861
+ if (propName in referenceEntitiesObjectMapping) {
1862
+ if (isSharepointChoiceColumn(propertiesNode[propName])) {
1863
+ // Note: SharePoint choice/multi-choice columns are intentionally mapped to string/string[]
1864
+ // instead of the schema's { Value: string } object. This simplifies the SDK's public
1865
+ // surface and prevents recurring errors in AI agents (especially App Builder) that
1866
+ // frequently mishandled the complex object wrapper.
1867
+ if (propertiesNode[propName].type === 'array') {
1868
+ writeInterfaceLines.push(` ${propNameNormalized}${optionalMark}: string[];`);
1822
1869
  }
1823
1870
  else {
1824
- writeInterfaceLines.push(` ${propNameNormalized}${optionalMark}: ${referenceEntitiesObjectMapping[propName]};`);
1871
+ writeInterfaceLines.push(` ${propNameNormalized}${optionalMark}: string;`);
1825
1872
  }
1826
- readInterfaceLines.push(` ${propNameNormalized}${optionalMark}: ${referenceEntitiesObjectMapping[propName]};`);
1827
- hasReferenceEntities = true;
1873
+ }
1874
+ else {
1875
+ writeInterfaceLines.push(` ${propNameNormalized}${optionalMark}: ${referenceEntitiesObjectMapping[propName]};`);
1876
+ }
1877
+ readInterfaceLines.push(` ${propNameNormalized}${optionalMark}: ${referenceEntitiesObjectMapping[propName]};`);
1878
+ hasReferenceEntities = true;
1879
+ continue;
1880
+ }
1881
+ addPropertyDeclaration(baseInterfaceLines, propName, propertiesNode[propName], requiredFields, primaryKeyObj);
1882
+ }
1883
+ baseInterfaceLines.push('}');
1884
+ baseInterfaceLines.push('');
1885
+ lines.push(...baseInterfaceLines);
1886
+ if (hasReferenceEntities) {
1887
+ readInterfaceLines.push('}');
1888
+ readInterfaceLines.push('');
1889
+ lines.push(...readInterfaceLines);
1890
+ }
1891
+ if (hasReferenceEntities) {
1892
+ writeInterfaceLines.push('}');
1893
+ writeInterfaceLines.push('');
1894
+ lines.push(...writeInterfaceLines);
1895
+ }
1896
+ return { referenceEntitiesObjectMapping, lines, hasReferenceEntities };
1897
+ }
1898
+ function addSPReferencedEntityModelInterfaceDeclaration(schemaJson, lines) {
1899
+ const referenceEntitiesObjectMapping = {};
1900
+ const schema = JSON.parse(schemaJson);
1901
+ const root = parseSchemaJson(schemaJson);
1902
+ const propertiesNode = getPropertiesNode(root);
1903
+ const referencedEntities = typeof schema.referencedEntities === 'object' && schema.referencedEntities !== null
1904
+ ? schema.referencedEntities
1905
+ : null;
1906
+ if (referencedEntities) {
1907
+ for (const referencedEntityKey in referencedEntities) {
1908
+ if (!Object.prototype.hasOwnProperty.call(referencedEntities, referencedEntityKey)) {
1828
1909
  continue;
1829
1910
  }
1830
- this.addPropertyDeclaration(baseInterfaceLines, propName, propertiesNode[propName], requiredFields, primaryKeyObj);
1831
- }
1832
- baseInterfaceLines.push('}');
1833
- baseInterfaceLines.push('');
1834
- lines.push(...baseInterfaceLines);
1835
- if (hasReferenceEntities) {
1836
- readInterfaceLines.push('}');
1837
- readInterfaceLines.push('');
1838
- lines.push(...readInterfaceLines);
1839
- }
1840
- if (hasReferenceEntities) {
1841
- writeInterfaceLines.push('}');
1842
- writeInterfaceLines.push('');
1843
- lines.push(...writeInterfaceLines);
1911
+ referenceEntitiesObjectMapping[referencedEntityKey] = referencedEntityKey + 'Value';
1912
+ if (!(referencedEntityKey in propertiesNode)) {
1913
+ continue;
1914
+ }
1915
+ lines.push(`export interface ${referenceEntitiesObjectMapping[referencedEntityKey]} {`);
1916
+ // Add actual declaration of the referenced entity
1917
+ addInnerPropertyDeclaration(lines, propertiesNode, referencedEntityKey);
1918
+ lines.push('}');
1919
+ lines.push('');
1844
1920
  }
1845
- return { referenceEntitiesObjectMapping, lines, hasReferenceEntities };
1846
- }
1847
- static addSPReferencedEntityModelInterfaceDeclaration(schemaJson, lines) {
1848
- const referenceEntitiesObjectMapping = {};
1849
- const schema = JSON.parse(schemaJson);
1850
- const root = this.parseSchemaJson(schemaJson);
1851
- const propertiesNode = this.getPropertiesNode(root);
1852
- const referencedEntities = typeof schema.referencedEntities === 'object' && schema.referencedEntities !== null
1853
- ? schema.referencedEntities
1854
- : null;
1855
- if (referencedEntities) {
1856
- for (const referencedEntityKey in referencedEntities) {
1857
- if (!Object.prototype.hasOwnProperty.call(referencedEntities, referencedEntityKey)) {
1921
+ }
1922
+ return {
1923
+ mapping: referenceEntitiesObjectMapping,
1924
+ lines,
1925
+ };
1926
+ }
1927
+ function addInnerPropertyDeclaration(lines, propertiesNode, referencedEntityKey) {
1928
+ const propertyValue = propertiesNode[referencedEntityKey];
1929
+ if (propertyValue !== null && typeof propertyValue === 'object' && !Array.isArray(propertyValue)) {
1930
+ const propObj = propertyValue;
1931
+ if (propObj.properties && typeof propObj.properties === 'object' && !Array.isArray(propObj.properties)) {
1932
+ const properties = propObj.properties;
1933
+ for (const prop in properties) {
1934
+ if (!Object.prototype.hasOwnProperty.call(properties, prop)) {
1858
1935
  continue;
1859
1936
  }
1860
- referenceEntitiesObjectMapping[referencedEntityKey] = referencedEntityKey + 'Value';
1861
- if (!(referencedEntityKey in propertiesNode)) {
1862
- continue;
1937
+ const propValue = properties[prop];
1938
+ if (propValue !== null && typeof propValue === 'object' && !Array.isArray(propValue)) {
1939
+ const propValueObj = propValue;
1940
+ const normalizedProp = convertToValidIdentifier(prop);
1941
+ lines.push(` ${normalizedProp}: ${mapJsonTypeToTypeScript(propValueObj.type)};`);
1863
1942
  }
1864
- lines.push(`export interface ${referenceEntitiesObjectMapping[referencedEntityKey]} {`);
1865
- // Add actual declaration of the referenced entity
1866
- this.addInnerPropertyDeclaration(lines, propertiesNode, referencedEntityKey);
1867
- lines.push('}');
1868
- lines.push('');
1869
1943
  }
1870
1944
  }
1871
- return {
1872
- mapping: referenceEntitiesObjectMapping,
1873
- lines,
1874
- };
1875
- }
1876
- static addInnerPropertyDeclaration(lines, propertiesNode, referencedEntityKey) {
1877
- const propertyValue = propertiesNode[referencedEntityKey];
1878
- if (propertyValue !== null && typeof propertyValue === 'object' && !Array.isArray(propertyValue)) {
1879
- const propObj = propertyValue;
1880
- if (propObj.properties &&
1881
- typeof propObj.properties === 'object' &&
1882
- !Array.isArray(propObj.properties)) {
1883
- const properties = propObj.properties;
1884
- for (const prop in properties) {
1885
- if (!Object.prototype.hasOwnProperty.call(properties, prop)) {
1945
+ else if ('items' in propObj && typeof propObj.items === 'object' && propObj.items !== null) {
1946
+ const items = propObj.items;
1947
+ if ('properties' in items && typeof items.properties === 'object' && items.properties !== null) {
1948
+ const itemProperties = items.properties;
1949
+ for (const itemProp in itemProperties) {
1950
+ if (!Object.prototype.hasOwnProperty.call(itemProperties, itemProp)) {
1886
1951
  continue;
1887
1952
  }
1888
- const propValue = properties[prop];
1889
- if (propValue !== null && typeof propValue === 'object' && !Array.isArray(propValue)) {
1890
- const propValueObj = propValue;
1891
- const normalizedProp = convertToValidIdentifier(prop);
1892
- lines.push(` ${normalizedProp}: ${this.mapJsonTypeToTypeScript(propValueObj.type)};`);
1953
+ const itemPropValue = itemProperties[itemProp];
1954
+ if (itemPropValue !== null && typeof itemPropValue === 'object' && !Array.isArray(itemPropValue)) {
1955
+ const itemPropValueObj = itemPropValue;
1956
+ const normalizedItemProp = convertToValidIdentifier(itemProp);
1957
+ lines.push(` ${normalizedItemProp}: ${mapJsonTypeToTypeScript(itemPropValueObj.type)};`);
1893
1958
  }
1894
1959
  }
1895
1960
  }
1896
- else if ('items' in propObj && typeof propObj.items === 'object' && propObj.items !== null) {
1897
- const items = propObj.items;
1898
- if ('properties' in items && typeof items.properties === 'object' && items.properties !== null) {
1899
- const itemProperties = items.properties;
1900
- for (const itemProp in itemProperties) {
1901
- if (!Object.prototype.hasOwnProperty.call(itemProperties, itemProp)) {
1902
- continue;
1903
- }
1904
- const itemPropValue = itemProperties[itemProp];
1905
- if (itemPropValue !== null &&
1906
- typeof itemPropValue === 'object' &&
1907
- !Array.isArray(itemPropValue)) {
1908
- const itemPropValueObj = itemPropValue;
1909
- const normalizedItemProp = convertToValidIdentifier(itemProp);
1910
- lines.push(` ${normalizedItemProp}: ${this.mapJsonTypeToTypeScript(itemPropValueObj.type)};`);
1911
- }
1912
- }
1913
- }
1914
- }
1915
- }
1916
- }
1917
- /*
1918
- * The addModelInterfaceDeclaration method generates the TypeScript interface declaration for the model.
1919
- */
1920
- static addModelInterfaceDeclaration(lines, modelName, propertiesNode, requiredFields, primaryKey) {
1921
- lines.push(`export interface ${convertToValidIdentifier(modelName)} {`);
1922
- primaryKey.value = '';
1923
- // propertiesNode is an object with keys as property names.
1924
- for (const propName of Object.keys(propertiesNode)) {
1925
- this.addPropertyDeclaration(lines, propName, propertiesNode[propName], requiredFields, primaryKey);
1926
- }
1927
- lines.push('}');
1928
- lines.push('');
1929
- }
1930
- /*
1931
- * The addPropertyDeclaration method generates the TypeScript property declaration for a given JSON property.
1932
- * It checks if the property name is valid, maps the JSON type to TypeScript type,
1933
- * and adds the property to the lines list.
1934
- */
1935
- static addPropertyDeclaration(lines, propertyName, propertyValue, requiredFields, primaryKey) {
1936
- let propName = propertyName;
1937
- // Extract type information
1938
- let propType = 'unknown';
1939
- let dataverseType;
1940
- if (propertyValue !== null && typeof propertyValue === 'object' && !Array.isArray(propertyValue)) {
1941
- const propObj = propertyValue;
1942
- // Check for Dataverse-specific type first
1943
- if ('x-ms-dataverse-type' in propObj && typeof propObj['x-ms-dataverse-type'] === 'string') {
1944
- dataverseType = propObj['x-ms-dataverse-type'];
1945
- // Map Dataverse type to TypeScript type
1946
- propType = this.mapDataverseTypeToTypeScript(dataverseType);
1947
- }
1948
- else if ('type' in propObj && typeof propObj.type === 'string') {
1949
- // Map standard JSON type to TypeScript type
1950
- propType = this.determineTypeScriptType(propertyName, propObj, '', propName);
1951
- }
1952
- }
1953
- const keyType = typeof propertyValue === 'object' &&
1954
- propertyValue !== null &&
1955
- !Array.isArray(propertyValue) &&
1956
- 'x-ms-keyType' in propertyValue
1957
- ? propertyValue['x-ms-keyType']
1958
- : '';
1959
- if (keyType === 'primary') {
1960
- primaryKey.value = propName;
1961
- }
1962
- // Check if the property name is valid
1963
- if (!isValidPropertyName(propName)) {
1964
- propName = `"${propName}"`; // Enclose invalid property names in double quotes
1965
1961
  }
1966
- const isRequired = requiredFields.has(propertyName);
1967
- const optionalMark = isRequired ? '' : '?';
1968
- lines.push(` ${propName}${optionalMark}: ${propType};`);
1969
- }
1970
- /*
1971
- * The generateTypeScriptService method generates a TypeScript service class for the model.
1972
- * It includes methods for CRUD operations and uses the Power SDK for data access.
1973
- */
1974
- static generateTypeScriptService(modelName, dataSourceName, primaryKey, isADataverseEntity) {
1975
- const lines = [];
1976
- this.addCopyrightNotice(lines);
1977
- this.addStandardServiceImports(lines, modelName, isADataverseEntity, false);
1978
- this.addServiceClassDefinition(lines, modelName, dataSourceName, primaryKey, isADataverseEntity);
1979
- return lines.join('\n');
1980
1962
  }
1981
- /*
1982
- * The generateDataverseTypeScriptService method generates a TypeScript service class specifically for Dataverse entities.
1983
- * It handles lookup columns by using base interface for Create/Update and extended interface for Get/GetAll operations.
1984
- */
1985
- static generateDataverseTypeScriptService(modelName, dataSourceName, primaryKey, hasLookupColumns) {
1986
- const lines = [];
1987
- this.addCopyrightNotice(lines);
1988
- this.addDataverseServiceImports(lines, modelName, hasLookupColumns);
1989
- this.addDataverseServiceClassDefinition(lines, modelName, dataSourceName, primaryKey, hasLookupColumns);
1990
- return lines.join('\n');
1991
- }
1992
- /*
1993
- * The addStandardServiceImports method adds the necessary imports for the service class.
1994
- */
1995
- static addStandardServiceImports(lines, modelName, isADataverseEntity, additionalLine = true) {
1996
- const imports = [...this.collectStandardImports('crud')];
1997
- imports.push(`import type { IGetOptions, IGetAllOptions } from '${this.RELATIVE_MODEL_PATH}/CommonModels';`, `import type { ${modelName} } from '${this.RELATIVE_MODEL_PATH}/${modelName}Model';`);
1998
- if (isADataverseEntity) {
1999
- imports.push("import type { GetEntityMetadataOptions, EntityMetadata } from '@microsoft/power-apps/data/metadata/dataverse';");
2000
- }
2001
- lines.push(...imports.sort());
2002
- if (additionalLine) {
2003
- lines.push('');
2004
- }
2005
- }
2006
- /*
2007
- * The addDataverseServiceImports method adds the necessary imports for Dataverse service classes with lookup columns.
2008
- */
2009
- static addDataverseServiceImports(lines, modelName, hasLookupColumns) {
2010
- const imports = [...this.collectStandardImports('crud')];
2011
- imports.push(`import type { IGetOptions, IGetAllOptions } from '${this.RELATIVE_MODEL_PATH}/CommonModels';`);
2012
- if (hasLookupColumns) {
2013
- // Import both base and extended interfaces
2014
- imports.push(`import type { ${modelName}Base, ${modelName} } from '${this.RELATIVE_MODEL_PATH}/${modelName}Model';`);
2015
- }
2016
- else {
2017
- // Import single interface
2018
- imports.push(`import type { ${modelName}Base } from '${this.RELATIVE_MODEL_PATH}/${modelName}Model';`);
2019
- }
1963
+ }
1964
+ /*
1965
+ * The addModelInterfaceDeclaration method generates the TypeScript interface declaration for the model.
1966
+ */
1967
+ function addModelInterfaceDeclaration(lines, modelName, propertiesNode, requiredFields, primaryKey) {
1968
+ lines.push(`export interface ${convertToValidIdentifier(modelName)} {`);
1969
+ primaryKey.value = '';
1970
+ // propertiesNode is an object with keys as property names.
1971
+ for (const propName of Object.keys(propertiesNode)) {
1972
+ addPropertyDeclaration(lines, propName, propertiesNode[propName], requiredFields, primaryKey);
1973
+ }
1974
+ lines.push('}');
1975
+ lines.push('');
1976
+ }
1977
+ /*
1978
+ * The addPropertyDeclaration method generates the TypeScript property declaration for a given JSON property.
1979
+ * It checks if the property name is valid, maps the JSON type to TypeScript type,
1980
+ * and adds the property to the lines list.
1981
+ */
1982
+ function addPropertyDeclaration(lines, propertyName, propertyValue, requiredFields, primaryKey) {
1983
+ let propName = propertyName;
1984
+ // Extract type information
1985
+ let propType = 'unknown';
1986
+ let dataverseType;
1987
+ if (propertyValue !== null && typeof propertyValue === 'object' && !Array.isArray(propertyValue)) {
1988
+ const propObj = propertyValue;
1989
+ // Check for Dataverse-specific type first
1990
+ if ('x-ms-dataverse-type' in propObj && typeof propObj['x-ms-dataverse-type'] === 'string') {
1991
+ dataverseType = propObj['x-ms-dataverse-type'];
1992
+ // Map Dataverse type to TypeScript type
1993
+ propType = mapDataverseTypeToTypeScript(dataverseType);
1994
+ }
1995
+ else if ('type' in propObj && typeof propObj.type === 'string') {
1996
+ // Map standard JSON type to TypeScript type
1997
+ propType = determineTypeScriptType(propertyName, propObj, '', propName);
1998
+ }
1999
+ }
2000
+ const keyType = typeof propertyValue === 'object' &&
2001
+ propertyValue !== null &&
2002
+ !Array.isArray(propertyValue) &&
2003
+ 'x-ms-keyType' in propertyValue
2004
+ ? propertyValue['x-ms-keyType']
2005
+ : '';
2006
+ if (keyType === 'primary') {
2007
+ primaryKey.value = propName;
2008
+ }
2009
+ // Check if the property name is valid
2010
+ if (!isValidPropertyName(propName)) {
2011
+ propName = `"${propName}"`; // Enclose invalid property names in double quotes
2012
+ }
2013
+ const isRequired = requiredFields.has(propertyName);
2014
+ const optionalMark = isRequired ? '' : '?';
2015
+ lines.push(` ${propName}${optionalMark}: ${propType};`);
2016
+ }
2017
+ /*
2018
+ * The generateTypeScriptService method generates a TypeScript service class for the model.
2019
+ * It includes methods for CRUD operations and uses the Power SDK for data access.
2020
+ */
2021
+ function generateTypeScriptService(modelName, dataSourceName, primaryKey, isADataverseEntity) {
2022
+ const lines = [];
2023
+ addCopyrightNotice(lines);
2024
+ addStandardServiceImports(lines, modelName, isADataverseEntity, false);
2025
+ addServiceClassDefinition(lines, modelName, dataSourceName, primaryKey, isADataverseEntity);
2026
+ return lines.join('\n');
2027
+ }
2028
+ /*
2029
+ * The generateDataverseTypeScriptService method generates a TypeScript service class specifically for Dataverse entities.
2030
+ * It handles lookup columns by using base interface for Create/Update and extended interface for Get/GetAll operations.
2031
+ */
2032
+ function generateDataverseTypeScriptService(modelName, dataSourceName, primaryKey, hasLookupColumns) {
2033
+ const lines = [];
2034
+ addCopyrightNotice(lines);
2035
+ addDataverseServiceImports(lines, modelName, hasLookupColumns);
2036
+ addDataverseServiceClassDefinition(lines, modelName, dataSourceName, primaryKey, hasLookupColumns);
2037
+ return lines.join('\n');
2038
+ }
2039
+ /*
2040
+ * The addStandardServiceImports method adds the necessary imports for the service class.
2041
+ */
2042
+ function addStandardServiceImports(lines, modelName, isADataverseEntity, additionalLine = true) {
2043
+ const imports = [...collectStandardImports('crud')];
2044
+ imports.push(`import type { IGetOptions, IGetAllOptions } from '${RELATIVE_MODEL_PATH}/CommonModels';`, `import type { ${modelName} } from '${RELATIVE_MODEL_PATH}/${modelName}Model';`);
2045
+ if (isADataverseEntity) {
2020
2046
  imports.push("import type { GetEntityMetadataOptions, EntityMetadata } from '@microsoft/power-apps/data/metadata/dataverse';");
2021
- lines.push(...imports.sort());
2022
- lines.push('');
2023
2047
  }
2024
- static addSharepointServiceImports(lines, modelName, hasReferenceEntities) {
2025
- const imports = [...this.collectStandardImports('crud')];
2026
- imports.push(`import type { IGetOptions, IGetAllOptions } from '${this.RELATIVE_MODEL_PATH}/CommonModels';`);
2027
- if (hasReferenceEntities) {
2028
- // Import both base and read/write interfaces
2029
- imports.push(`import type { ${modelName}Read, ${modelName}Write } from '${this.RELATIVE_MODEL_PATH}/${modelName}Model';`);
2030
- }
2031
- else {
2032
- // Import single interface
2033
- imports.push(`import type { ${modelName}Base } from '${this.RELATIVE_MODEL_PATH}/${modelName}Model';`);
2034
- }
2035
- lines.push(...imports.sort());
2048
+ lines.push(...imports.sort());
2049
+ if (additionalLine) {
2036
2050
  lines.push('');
2037
2051
  }
2038
- /*
2039
- * The addServiceFields method adds fields to the service class.
2040
- * It includes the Power SDK instance and a static instance of the service.
2041
- */
2042
- static addServiceFields(lines, dataSourceName) {
2043
- lines.push(` private static readonly dataSourceName = '${dataSourceName.toLowerCase()}';`);
2044
- lines.push(``);
2045
- lines.push(` private static readonly client = getClient(dataSourcesInfo);`);
2046
- }
2047
- /*
2048
- * The AddServiceClassDefinition method generates the service class definition.
2049
- * It takes the lines to write to, model name, data source name, schema, and response model name as parameters.
2050
- */
2051
- static addServiceClassDefinition(lines, modelName, dataSourceName, primaryKey, isADataverseEntity) {
2052
- this.addServiceClassHeader(lines, modelName, dataSourceName, true);
2053
- this.addServiceMethods(lines, modelName, dataSourceName, primaryKey, isADataverseEntity);
2054
- lines.push('}');
2052
+ }
2053
+ /*
2054
+ * The addDataverseServiceImports method adds the necessary imports for Dataverse service classes with lookup columns.
2055
+ */
2056
+ function addDataverseServiceImports(lines, modelName, hasLookupColumns) {
2057
+ const imports = [...collectStandardImports('crud')];
2058
+ imports.push(`import type { IGetOptions, IGetAllOptions } from '${RELATIVE_MODEL_PATH}/CommonModels';`);
2059
+ if (hasLookupColumns) {
2060
+ // Import both base and extended interfaces
2061
+ imports.push(`import type { ${modelName}Base, ${modelName} } from '${RELATIVE_MODEL_PATH}/${modelName}Model';`);
2062
+ }
2063
+ else {
2064
+ // Import single interface
2065
+ imports.push(`import type { ${modelName}Base } from '${RELATIVE_MODEL_PATH}/${modelName}Model';`);
2066
+ }
2067
+ imports.push("import type { GetEntityMetadataOptions, EntityMetadata } from '@microsoft/power-apps/data/metadata/dataverse';");
2068
+ lines.push(...imports.sort());
2069
+ lines.push('');
2070
+ }
2071
+ function addSharepointServiceImports(lines, modelName, hasReferenceEntities) {
2072
+ const imports = [...collectStandardImports('crud')];
2073
+ imports.push(`import type { IGetOptions, IGetAllOptions } from '${RELATIVE_MODEL_PATH}/CommonModels';`);
2074
+ if (hasReferenceEntities) {
2075
+ // Import both base and read/write interfaces
2076
+ imports.push(`import type { ${modelName}Read, ${modelName}Write } from '${RELATIVE_MODEL_PATH}/${modelName}Model';`);
2077
+ }
2078
+ else {
2079
+ // Import single interface
2080
+ imports.push(`import type { ${modelName}Base } from '${RELATIVE_MODEL_PATH}/${modelName}Model';`);
2081
+ }
2082
+ lines.push(...imports.sort());
2083
+ lines.push('');
2084
+ }
2085
+ /*
2086
+ * The addServiceFields method adds fields to the service class.
2087
+ * It includes the Power SDK instance and a static instance of the service.
2088
+ */
2089
+ function addServiceFields(lines, dataSourceName) {
2090
+ lines.push(` private static readonly dataSourceName = '${dataSourceName.toLowerCase()}';`);
2091
+ lines.push(``);
2092
+ lines.push(` private static readonly client = getClient(dataSourcesInfo);`);
2093
+ }
2094
+ /*
2095
+ * The AddServiceClassDefinition method generates the service class definition.
2096
+ * It takes the lines to write to, model name, data source name, schema, and response model name as parameters.
2097
+ */
2098
+ function addServiceClassDefinition(lines, modelName, dataSourceName, primaryKey, isADataverseEntity) {
2099
+ addServiceClassHeader(lines, modelName, dataSourceName, true);
2100
+ addServiceMethods(lines, modelName, dataSourceName, primaryKey, isADataverseEntity);
2101
+ lines.push('}');
2102
+ lines.push('');
2103
+ }
2104
+ /*
2105
+ * The addDataverseServiceClassDefinition method generates the service class definition for Dataverse entities with lookup columns.
2106
+ */
2107
+ function addDataverseServiceClassDefinition(lines, modelName, dataSourceName, primaryKey, hasLookupColumns) {
2108
+ addServiceClassHeader(lines, modelName, dataSourceName, true);
2109
+ addDataverseServiceMethods(lines, modelName, dataSourceName, primaryKey, hasLookupColumns);
2110
+ lines.push('}');
2111
+ lines.push('');
2112
+ }
2113
+ /*
2114
+ * The addServiceMethods method generates the CRUD methods for the service class.
2115
+ * It includes create, update, delete, get, and getAll methods.
2116
+ */
2117
+ function addServiceMethods(lines, modelName, dataSourceName, primaryKey, isADataverseEntity) {
2118
+ const methods = [];
2119
+ methods.push(generateCreateMethod(modelName, primaryKey));
2120
+ methods.push(generateUpdateMethod(modelName, primaryKey));
2121
+ methods.push(generateDeleteMethod(modelName));
2122
+ methods.push(generateGetMethod(modelName));
2123
+ methods.push(generateGetAllMethod(modelName));
2124
+ if (isADataverseEntity) {
2125
+ methods.push(generateGetMetadataMethod(modelName));
2126
+ }
2127
+ for (const method of methods) {
2055
2128
  lines.push('');
2129
+ lines.push(...method);
2056
2130
  }
2057
- /*
2058
- * The addDataverseServiceClassDefinition method generates the service class definition for Dataverse entities with lookup columns.
2059
- */
2060
- static addDataverseServiceClassDefinition(lines, modelName, dataSourceName, primaryKey, hasLookupColumns) {
2061
- this.addServiceClassHeader(lines, modelName, dataSourceName, true);
2062
- this.addDataverseServiceMethods(lines, modelName, dataSourceName, primaryKey, hasLookupColumns);
2063
- lines.push('}');
2131
+ }
2132
+ /*
2133
+ * The addDataverseServiceMethods method generates the CRUD methods for Dataverse service classes with lookup columns.
2134
+ * Uses base interface for Create/Update and extended interface for Get/GetAll operations.
2135
+ */
2136
+ function addDataverseServiceMethods(lines, modelName, dataSourceName, primaryKey, hasLookupColumns) {
2137
+ const methods = [];
2138
+ methods.push(generateDataverseCreateMethod(modelName, primaryKey, hasLookupColumns));
2139
+ methods.push(generateDataverseUpdateMethod(modelName, primaryKey, hasLookupColumns));
2140
+ methods.push(generateDeleteMethod(modelName));
2141
+ methods.push(generateDataverseGetMethod(modelName, hasLookupColumns));
2142
+ methods.push(generateDataverseGetAllMethod(modelName, hasLookupColumns));
2143
+ methods.push(generateDataverseGetMetadataMethod(modelName, hasLookupColumns));
2144
+ for (const method of methods) {
2064
2145
  lines.push('');
2146
+ lines.push(...method);
2065
2147
  }
2066
- /*
2067
- * The addServiceMethods method generates the CRUD methods for the service class.
2068
- * It includes create, update, delete, get, and getAll methods.
2069
- */
2070
- static addServiceMethods(lines, modelName, dataSourceName, primaryKey, isADataverseEntity) {
2071
- const methods = [];
2072
- methods.push(this.generateCreateMethod(modelName, primaryKey));
2073
- methods.push(this.generateUpdateMethod(modelName, primaryKey));
2074
- methods.push(this.generateDeleteMethod(modelName));
2075
- methods.push(this.generateGetMethod(modelName));
2076
- methods.push(this.generateGetAllMethod(modelName));
2077
- if (isADataverseEntity) {
2078
- methods.push(this.generateGetMetadataMethod(modelName));
2079
- }
2080
- for (const method of methods) {
2081
- lines.push('');
2082
- lines.push(...method);
2083
- }
2084
- }
2085
- /*
2086
- * The addDataverseServiceMethods method generates the CRUD methods for Dataverse service classes with lookup columns.
2087
- * Uses base interface for Create/Update and extended interface for Get/GetAll operations.
2088
- */
2089
- static addDataverseServiceMethods(lines, modelName, dataSourceName, primaryKey, hasLookupColumns) {
2090
- const methods = [];
2091
- methods.push(this.generateDataverseCreateMethod(modelName, primaryKey, hasLookupColumns));
2092
- methods.push(this.generateDataverseUpdateMethod(modelName, primaryKey, hasLookupColumns));
2093
- methods.push(this.generateDeleteMethod(modelName));
2094
- methods.push(this.generateDataverseGetMethod(modelName, hasLookupColumns));
2095
- methods.push(this.generateDataverseGetAllMethod(modelName, hasLookupColumns));
2096
- methods.push(this.generateDataverseGetMetadataMethod(modelName, hasLookupColumns));
2097
- for (const method of methods) {
2098
- lines.push('');
2099
- lines.push(...method);
2100
- }
2101
- }
2102
- /*
2103
- * The create method adds a new record to the data source.
2104
- */
2105
- static generateCreateMethod(modelName, primaryKey) {
2106
- const datatypeForCreate = primaryKey !== '' ? `Omit<${modelName}, '${primaryKey}'>` : modelName;
2107
- return [
2108
- ` public static async create(record: ${datatypeForCreate}): Promise<IOperationResult<${modelName}>> {`,
2109
- ` const result = await ${modelName}Service.client.createRecordAsync<${datatypeForCreate}, ${modelName}>(`,
2110
- ` ${getServiceName(modelName)},`,
2111
- ` record`,
2112
- ` );`,
2113
- ` return result;`,
2114
- ` }`,
2115
- ];
2116
- }
2117
- /*
2118
- * The update method modifies an existing record.
2119
- */
2120
- static generateUpdateMethod(modelName, primaryKey) {
2121
- const datatypeForUpdate = primaryKey !== '' ? `Partial<Omit<${modelName}, '${primaryKey}'>>` : modelName;
2122
- return [
2123
- ` public static async update(id: string, changedFields: ${datatypeForUpdate}): Promise<IOperationResult<${modelName}>> {`,
2124
- ` const result = await ${modelName}Service.client.updateRecordAsync<${datatypeForUpdate}, ${modelName}>(`,
2125
- ` ${getServiceName(modelName)},`,
2126
- ` id.toString(),`,
2127
- ` changedFields`,
2128
- ` );`,
2129
- ` return result;`,
2130
- ` }`,
2131
- ];
2132
- }
2133
- /*
2134
- * The delete method removes a record by its ID.
2135
- */
2136
- static generateDeleteMethod(modelName) {
2137
- return [
2138
- ` public static async delete(id: string): Promise<void> {`,
2139
- ` await ${modelName}Service.client.deleteRecordAsync(`,
2140
- ` ${getServiceName(modelName)},`,
2141
- ` id.toString());`,
2142
- ` }`,
2143
- ];
2144
- }
2145
- /*
2146
- * The get method retrieves a record by its ID.
2147
- */
2148
- static generateGetMethod(modelName) {
2149
- return [
2150
- ` public static async get(id: string, options?: IGetOptions): Promise<IOperationResult<${modelName}>> {`,
2151
- ` const result = await ${modelName}Service.client.retrieveRecordAsync<${modelName}>(`,
2152
- ` ${getServiceName(modelName)},`,
2153
- ` id.toString(),`,
2154
- ` options`,
2155
- ` );`,
2156
- ` return result;`,
2157
- ` }`,
2158
- ];
2159
- }
2160
- /*
2161
- * The getAll method retrieves all records of the specified model.
2162
- */
2163
- static generateGetAllMethod(modelName) {
2164
- return [
2165
- ` public static async getAll(options?: IGetAllOptions): Promise<IOperationResult<${modelName}[]>> {`,
2166
- ` const result = await ${modelName}Service.client.retrieveMultipleRecordsAsync<${modelName}>(`,
2167
- ` ${getServiceName(modelName)},`,
2168
- ` options`,
2169
- ` );`,
2170
- ` return result;`,
2171
- ` }`,
2172
- ];
2173
- }
2174
- static generateGetMetadataMethod(modelName) {
2175
- return [
2176
- ` public static getMetadata(`,
2177
- ` options: GetEntityMetadataOptions<${modelName}> = {}`,
2178
- ` ): Promise<IOperationResult<Partial<EntityMetadata>>> {`,
2179
- ` return ${modelName}Service.client.executeAsync({`,
2180
- ` dataverseRequest: {`,
2181
- ` action: "getEntityMetadata",`,
2182
- ` parameters: {`,
2183
- ` tableName: ${getServiceName(modelName)},`,
2184
- ` options: options as GetEntityMetadataOptions,`,
2185
- ` },`,
2186
- ` },`,
2187
- ` });`,
2188
- ` }`,
2189
- ];
2190
- }
2191
- /*
2192
- * The create method adds a new record to the data source.
2193
- */
2194
- static generateSharepointCreateMethod(modelName, primaryKey, hasReferenceEntities) {
2195
- /**
2196
- * if hasReferenceEntities is true take in modelNameWrite-primaryKey as input and return modelNameRead
2197
- * else take in modelNameBase-primaryKey as input and return modelNameBase
2198
- */
2199
- const datatypeForCreate = primaryKey !== ''
2200
- ? `Omit<${hasReferenceEntities ? `${modelName}Write` : `${modelName}Base`}, '${primaryKey}'>`
2201
- : hasReferenceEntities
2202
- ? `${modelName}Write`
2203
- : `${modelName}Base`;
2204
- const returnType = hasReferenceEntities ? `${modelName}Read` : `${modelName}Base`;
2205
- return [
2206
- ` public static async create(record: ${datatypeForCreate}): Promise<IOperationResult<${returnType}>> {`,
2207
- ` const result = await ${modelName}Service.client.createRecordAsync<${datatypeForCreate}, ${returnType}>(`,
2208
- ` ${getServiceName(modelName)},`,
2209
- ` record`,
2210
- ` );`,
2211
- ` return result;`,
2212
- ` }`,
2213
- ];
2214
- }
2215
- /*
2216
- * The update method modifies an existing record.
2217
- */
2218
- static generateSharepointUpdateMethod(modelName, primaryKey, hasReferenceEntities) {
2219
- /**
2220
- * if hasReferenceEntities is true take in modelNameWrite-primaryKey as input and return modelNameRead
2221
- * else take in modelNameBase-primaryKey as input and return modelNameBase
2222
- */
2223
- const datatypeForUpdate = primaryKey !== ''
2224
- ? `Partial<Omit<${hasReferenceEntities ? `${modelName}Write` : `${modelName}Base`}, '${primaryKey}'>>`
2225
- : hasReferenceEntities
2226
- ? `${modelName}Write`
2227
- : `${modelName}Base`;
2228
- const returnType = hasReferenceEntities ? `${modelName}Read` : `${modelName}Base`;
2229
- return [
2230
- ` public static async update(id: string, changedFields: ${datatypeForUpdate}): Promise<IOperationResult<${returnType}>> {`,
2231
- ` const result = await ${modelName}Service.client.updateRecordAsync<${datatypeForUpdate}, ${returnType}>(`,
2232
- ` ${getServiceName(modelName)},`,
2233
- ` id.toString(),`,
2234
- ` changedFields`,
2235
- ` );`,
2236
- ` return result;`,
2237
- ` }`,
2238
- ];
2239
- }
2240
- /*
2241
- * The delete method removes a record by its ID.
2242
- */
2243
- static generateSharepointDeleteMethod(modelName) {
2244
- return [
2245
- ` public static async delete(id: string): Promise<void> {`,
2246
- ` await ${modelName}Service.client.deleteRecordAsync(`,
2247
- ` ${getServiceName(modelName)},`,
2248
- ` id.toString());`,
2249
- ` }`,
2250
- ];
2251
- }
2252
- /*
2253
- * The get method retrieves a record by its ID.
2254
- */
2255
- static generateSharepointGetMethod(modelName, hasReferenceEntities) {
2256
- /**
2257
- * if referenceEntities is true return modelNameRead
2258
- * else return modelNameBase
2259
- */
2260
- const returnType = hasReferenceEntities ? `${modelName}Read` : `${modelName}Base`;
2261
- /**
2262
- */
2263
- return [
2264
- ` public static async get(id: string, options?: IGetOptions): Promise<IOperationResult<${returnType}>> {`,
2265
- ` const result = await ${modelName}Service.client.retrieveRecordAsync<${returnType}>(`,
2266
- ` ${getServiceName(modelName)},`,
2267
- ` id.toString(),`,
2268
- ` options`,
2269
- ` );`,
2270
- ` return result;`,
2271
- ` }`,
2272
- ];
2273
- }
2274
- /*
2275
- * The getAll method retrieves all records of the specified model.
2276
- */
2277
- static generateSharepointGetAllMethod(modelName, hasReferenceEntities) {
2278
- const returnType = hasReferenceEntities ? `${modelName}Read` : `${modelName}Base`;
2279
- return [
2280
- ` public static async getAll(options?: IGetAllOptions): Promise<IOperationResult<${returnType}[]>> {`,
2281
- ` const result = await ${modelName}Service.client.retrieveMultipleRecordsAsync<${returnType}>(`,
2282
- ` ${getServiceName(modelName)},`,
2283
- ` options`,
2284
- ` );`,
2285
- ` return result;`,
2286
- ` }`,
2287
- ];
2288
- }
2289
- /*
2290
- * The generateDataverseCreateMethod method adds a new record using the base interface for Dataverse entities.
2291
- */
2292
- static generateDataverseCreateMethod(modelName, primaryKey, hasLookupColumns) {
2293
- const baseInterfaceName = hasLookupColumns ? `${modelName}Base` : modelName;
2294
- const extendedInterfaceName = modelName;
2295
- const datatypeForCreate = primaryKey !== '' ? `Omit<${baseInterfaceName}, '${primaryKey}'>` : baseInterfaceName;
2296
- return [
2297
- ` public static async create(record: ${datatypeForCreate}): Promise<IOperationResult<${extendedInterfaceName}>> {`,
2298
- ` const result = await ${modelName}Service.client.createRecordAsync<${datatypeForCreate}, ${extendedInterfaceName}>(`,
2299
- ` ${getServiceName(modelName)},`,
2300
- ` record`,
2301
- ` );`,
2302
- ` return result;`,
2303
- ` }`,
2304
- ];
2305
- }
2306
- /*
2307
- * The generateDataverseUpdateMethod method modifies an existing record using the base interface for Dataverse entities.
2308
- */
2309
- static generateDataverseUpdateMethod(modelName, primaryKey, hasLookupColumns) {
2310
- const baseInterfaceName = hasLookupColumns ? `${modelName}Base` : modelName;
2311
- const extendedInterfaceName = modelName;
2312
- const datatypeForUpdate = primaryKey !== '' ? `Partial<Omit<${baseInterfaceName}, '${primaryKey}'>>` : baseInterfaceName;
2313
- return [
2314
- ` public static async update(id: string, changedFields: ${datatypeForUpdate}): Promise<IOperationResult<${extendedInterfaceName}>> {`,
2315
- ` const result = await ${modelName}Service.client.updateRecordAsync<${datatypeForUpdate}, ${extendedInterfaceName}>(`,
2316
- ` ${getServiceName(modelName)},`,
2317
- ` id.toString(),`,
2318
- ` changedFields`,
2319
- ` );`,
2320
- ` return result;`,
2321
- ` }`,
2322
- ];
2323
- }
2324
- /*
2325
- * The generateDataverseGetMethod method retrieves a record by its ID using the extended interface for Dataverse entities.
2326
- */
2327
- static generateDataverseGetMethod(modelName, hasLookupColumns) {
2328
- const extendedInterfaceName = modelName;
2329
- return [
2330
- ` public static async get(id: string, options?: IGetOptions): Promise<IOperationResult<${extendedInterfaceName}>> {`,
2331
- ` const result = await ${modelName}Service.client.retrieveRecordAsync<${extendedInterfaceName}>(`,
2332
- ` ${getServiceName(modelName)},`,
2333
- ` id.toString(),`,
2334
- ` options`,
2335
- ` );`,
2336
- ` return result;`,
2337
- ` }`,
2338
- ];
2339
- }
2340
- /*
2341
- * The generateDataverseGetAllMethod method retrieves all records using the extended interface for Dataverse entities.
2342
- */
2343
- static generateDataverseGetAllMethod(modelName, hasLookupColumns) {
2344
- const extendedInterfaceName = modelName;
2345
- return [
2346
- ` public static async getAll(options?: IGetAllOptions): Promise<IOperationResult<${extendedInterfaceName}[]>> {`,
2347
- ` const result = await ${modelName}Service.client.retrieveMultipleRecordsAsync<${extendedInterfaceName}>(`,
2348
- ` ${getServiceName(modelName)},`,
2349
- ` options`,
2350
- ` );`,
2351
- ` return result;`,
2352
- ` }`,
2353
- ];
2354
- }
2355
- /*
2356
- * The generateDataverseGetMetadataMethod method gets metadata using the extended interface for Dataverse entities.
2148
+ }
2149
+ /*
2150
+ * The create method adds a new record to the data source.
2151
+ */
2152
+ function generateCreateMethod(modelName, primaryKey) {
2153
+ const datatypeForCreate = primaryKey !== '' ? `Omit<${modelName}, '${primaryKey}'>` : modelName;
2154
+ return [
2155
+ ` public static async create(record: ${datatypeForCreate}): Promise<IOperationResult<${modelName}>> {`,
2156
+ ` const result = await ${modelName}Service.client.createRecordAsync<${datatypeForCreate}, ${modelName}>(`,
2157
+ ` ${getServiceName(modelName)},`,
2158
+ ` record`,
2159
+ ` );`,
2160
+ ` return result;`,
2161
+ ` }`,
2162
+ ];
2163
+ }
2164
+ /*
2165
+ * The update method modifies an existing record.
2166
+ */
2167
+ function generateUpdateMethod(modelName, primaryKey) {
2168
+ const datatypeForUpdate = primaryKey !== '' ? `Partial<Omit<${modelName}, '${primaryKey}'>>` : modelName;
2169
+ return [
2170
+ ` public static async update(id: string, changedFields: ${datatypeForUpdate}): Promise<IOperationResult<${modelName}>> {`,
2171
+ ` const result = await ${modelName}Service.client.updateRecordAsync<${datatypeForUpdate}, ${modelName}>(`,
2172
+ ` ${getServiceName(modelName)},`,
2173
+ ` id.toString(),`,
2174
+ ` changedFields`,
2175
+ ` );`,
2176
+ ` return result;`,
2177
+ ` }`,
2178
+ ];
2179
+ }
2180
+ /*
2181
+ * The delete method removes a record by its ID.
2182
+ */
2183
+ function generateDeleteMethod(modelName) {
2184
+ return [
2185
+ ` public static async delete(id: string): Promise<void> {`,
2186
+ ` await ${modelName}Service.client.deleteRecordAsync(`,
2187
+ ` ${getServiceName(modelName)},`,
2188
+ ` id.toString());`,
2189
+ ` }`,
2190
+ ];
2191
+ }
2192
+ /*
2193
+ * The get method retrieves a record by its ID.
2194
+ */
2195
+ function generateGetMethod(modelName) {
2196
+ return [
2197
+ ` public static async get(id: string, options?: IGetOptions): Promise<IOperationResult<${modelName}>> {`,
2198
+ ` const result = await ${modelName}Service.client.retrieveRecordAsync<${modelName}>(`,
2199
+ ` ${getServiceName(modelName)},`,
2200
+ ` id.toString(),`,
2201
+ ` options`,
2202
+ ` );`,
2203
+ ` return result;`,
2204
+ ` }`,
2205
+ ];
2206
+ }
2207
+ /*
2208
+ * The getAll method retrieves all records of the specified model.
2209
+ */
2210
+ function generateGetAllMethod(modelName) {
2211
+ return [
2212
+ ` public static async getAll(options?: IGetAllOptions): Promise<IOperationResult<${modelName}[]>> {`,
2213
+ ` const result = await ${modelName}Service.client.retrieveMultipleRecordsAsync<${modelName}>(`,
2214
+ ` ${getServiceName(modelName)},`,
2215
+ ` options`,
2216
+ ` );`,
2217
+ ` return result;`,
2218
+ ` }`,
2219
+ ];
2220
+ }
2221
+ function generateGetMetadataMethod(modelName) {
2222
+ return [
2223
+ ` public static getMetadata(`,
2224
+ ` options: GetEntityMetadataOptions<${modelName}> = {}`,
2225
+ ` ): Promise<IOperationResult<Partial<EntityMetadata>>> {`,
2226
+ ` return ${modelName}Service.client.executeAsync({`,
2227
+ ` dataverseRequest: {`,
2228
+ ` action: "getEntityMetadata",`,
2229
+ ` parameters: {`,
2230
+ ` tableName: ${getServiceName(modelName)},`,
2231
+ ` options: options as GetEntityMetadataOptions,`,
2232
+ ` },`,
2233
+ ` },`,
2234
+ ` });`,
2235
+ ` }`,
2236
+ ];
2237
+ }
2238
+ /*
2239
+ * The create method adds a new record to the data source.
2240
+ */
2241
+ function generateSharepointCreateMethod(modelName, primaryKey, hasReferenceEntities) {
2242
+ /**
2243
+ * if hasReferenceEntities is true take in modelNameWrite-primaryKey as input and return modelNameRead
2244
+ * else take in modelNameBase-primaryKey as input and return modelNameBase
2245
+ */
2246
+ const datatypeForCreate = primaryKey !== ''
2247
+ ? `Omit<${hasReferenceEntities ? `${modelName}Write` : `${modelName}Base`}, '${primaryKey}'>`
2248
+ : hasReferenceEntities
2249
+ ? `${modelName}Write`
2250
+ : `${modelName}Base`;
2251
+ const returnType = hasReferenceEntities ? `${modelName}Read` : `${modelName}Base`;
2252
+ return [
2253
+ ` public static async create(record: ${datatypeForCreate}): Promise<IOperationResult<${returnType}>> {`,
2254
+ ` const result = await ${modelName}Service.client.createRecordAsync<${datatypeForCreate}, ${returnType}>(`,
2255
+ ` ${getServiceName(modelName)},`,
2256
+ ` record`,
2257
+ ` );`,
2258
+ ` return result;`,
2259
+ ` }`,
2260
+ ];
2261
+ }
2262
+ /*
2263
+ * The update method modifies an existing record.
2264
+ */
2265
+ function generateSharepointUpdateMethod(modelName, primaryKey, hasReferenceEntities) {
2266
+ /**
2267
+ * if hasReferenceEntities is true take in modelNameWrite-primaryKey as input and return modelNameRead
2268
+ * else take in modelNameBase-primaryKey as input and return modelNameBase
2269
+ */
2270
+ const datatypeForUpdate = primaryKey !== ''
2271
+ ? `Partial<Omit<${hasReferenceEntities ? `${modelName}Write` : `${modelName}Base`}, '${primaryKey}'>>`
2272
+ : hasReferenceEntities
2273
+ ? `${modelName}Write`
2274
+ : `${modelName}Base`;
2275
+ const returnType = hasReferenceEntities ? `${modelName}Read` : `${modelName}Base`;
2276
+ return [
2277
+ ` public static async update(id: string, changedFields: ${datatypeForUpdate}): Promise<IOperationResult<${returnType}>> {`,
2278
+ ` const result = await ${modelName}Service.client.updateRecordAsync<${datatypeForUpdate}, ${returnType}>(`,
2279
+ ` ${getServiceName(modelName)},`,
2280
+ ` id.toString(),`,
2281
+ ` changedFields`,
2282
+ ` );`,
2283
+ ` return result;`,
2284
+ ` }`,
2285
+ ];
2286
+ }
2287
+ /*
2288
+ * The delete method removes a record by its ID.
2289
+ */
2290
+ function generateSharepointDeleteMethod(modelName) {
2291
+ return [
2292
+ ` public static async delete(id: string): Promise<void> {`,
2293
+ ` await ${modelName}Service.client.deleteRecordAsync(`,
2294
+ ` ${getServiceName(modelName)},`,
2295
+ ` id.toString());`,
2296
+ ` }`,
2297
+ ];
2298
+ }
2299
+ /*
2300
+ * The get method retrieves a record by its ID.
2301
+ */
2302
+ function generateSharepointGetMethod(modelName, hasReferenceEntities) {
2303
+ /**
2304
+ * if referenceEntities is true return modelNameRead
2305
+ * else return modelNameBase
2357
2306
  */
2358
- static generateDataverseGetMetadataMethod(modelName, hasLookupColumns) {
2359
- const extendedInterfaceName = modelName;
2360
- return [
2361
- ` public static getMetadata(`,
2362
- ` options: GetEntityMetadataOptions<${extendedInterfaceName}> = {}`,
2363
- ` ): Promise<IOperationResult<Partial<EntityMetadata>>> {`,
2364
- ` return ${modelName}Service.client.executeAsync({`,
2365
- ` dataverseRequest: {`,
2366
- ` action: "getEntityMetadata",`,
2367
- ` parameters: {`,
2368
- ` tableName: ${getServiceName(modelName)},`,
2369
- ` options: options as GetEntityMetadataOptions,`,
2370
- ` },`,
2371
- ` },`,
2372
- ` });`,
2373
- ` }`,
2374
- ];
2375
- }
2307
+ const returnType = hasReferenceEntities ? `${modelName}Read` : `${modelName}Base`;
2376
2308
  /**
2377
- * The generateIndexFile method creates an index.ts file that exports all models and services.
2378
2309
  */
2379
- static async generateIndexFile(outputDir, vfs) {
2380
- const indexPath = vfs.join(outputDir, this.GENERATED_FOLDER, 'index.ts');
2381
- const lines = [];
2382
- // Add copyright notice
2383
- this.addCopyrightNotice(lines);
2384
- // Export all models from generated/models
2385
- const modelsDir = vfs.join(outputDir, this.MODELS_FOLDER);
2386
- if (await vfs.exists(modelsDir)) {
2387
- const modelFiles = (await vfs.readdir(modelsDir)).filter((file) => file.endsWith('.ts'));
2388
- if (modelFiles.length > 0) {
2389
- lines.push('// Models');
2390
- for (const file of modelFiles) {
2391
- const fileName = file.replace('.ts', '');
2392
- lines.push(`export * as ${fileName} from './models/${fileName}';`);
2393
- }
2394
- lines.push('');
2395
- }
2396
- }
2397
- // Export all services from generated/services
2398
- const servicesDir = vfs.join(outputDir, this.SERVICES_FOLDER);
2399
- if (await vfs.exists(servicesDir)) {
2400
- const serviceFiles = (await vfs.readdir(servicesDir)).filter((file) => file.endsWith('.ts'));
2401
- if (serviceFiles.length > 0) {
2402
- lines.push('// Services');
2403
- for (const file of serviceFiles) {
2404
- const fileName = file.replace('.ts', '');
2405
- lines.push(`export * from './services/${fileName}';`);
2406
- }
2407
- lines.push('');
2310
+ return [
2311
+ ` public static async get(id: string, options?: IGetOptions): Promise<IOperationResult<${returnType}>> {`,
2312
+ ` const result = await ${modelName}Service.client.retrieveRecordAsync<${returnType}>(`,
2313
+ ` ${getServiceName(modelName)},`,
2314
+ ` id.toString(),`,
2315
+ ` options`,
2316
+ ` );`,
2317
+ ` return result;`,
2318
+ ` }`,
2319
+ ];
2320
+ }
2321
+ /*
2322
+ * The getAll method retrieves all records of the specified model.
2323
+ */
2324
+ function generateSharepointGetAllMethod(modelName, hasReferenceEntities) {
2325
+ const returnType = hasReferenceEntities ? `${modelName}Read` : `${modelName}Base`;
2326
+ return [
2327
+ ` public static async getAll(options?: IGetAllOptions): Promise<IOperationResult<${returnType}[]>> {`,
2328
+ ` const result = await ${modelName}Service.client.retrieveMultipleRecordsAsync<${returnType}>(`,
2329
+ ` ${getServiceName(modelName)},`,
2330
+ ` options`,
2331
+ ` );`,
2332
+ ` return result;`,
2333
+ ` }`,
2334
+ ];
2335
+ }
2336
+ /*
2337
+ * The generateDataverseCreateMethod method adds a new record using the base interface for Dataverse entities.
2338
+ */
2339
+ function generateDataverseCreateMethod(modelName, primaryKey, hasLookupColumns) {
2340
+ const baseInterfaceName = hasLookupColumns ? `${modelName}Base` : modelName;
2341
+ const extendedInterfaceName = modelName;
2342
+ const datatypeForCreate = primaryKey !== '' ? `Omit<${baseInterfaceName}, '${primaryKey}'>` : baseInterfaceName;
2343
+ return [
2344
+ ` public static async create(record: ${datatypeForCreate}): Promise<IOperationResult<${extendedInterfaceName}>> {`,
2345
+ ` const result = await ${modelName}Service.client.createRecordAsync<${datatypeForCreate}, ${extendedInterfaceName}>(`,
2346
+ ` ${getServiceName(modelName)},`,
2347
+ ` record`,
2348
+ ` );`,
2349
+ ` return result;`,
2350
+ ` }`,
2351
+ ];
2352
+ }
2353
+ /*
2354
+ * The generateDataverseUpdateMethod method modifies an existing record using the base interface for Dataverse entities.
2355
+ */
2356
+ function generateDataverseUpdateMethod(modelName, primaryKey, hasLookupColumns) {
2357
+ const baseInterfaceName = hasLookupColumns ? `${modelName}Base` : modelName;
2358
+ const extendedInterfaceName = modelName;
2359
+ const datatypeForUpdate = primaryKey !== '' ? `Partial<Omit<${baseInterfaceName}, '${primaryKey}'>>` : baseInterfaceName;
2360
+ return [
2361
+ ` public static async update(id: string, changedFields: ${datatypeForUpdate}): Promise<IOperationResult<${extendedInterfaceName}>> {`,
2362
+ ` const result = await ${modelName}Service.client.updateRecordAsync<${datatypeForUpdate}, ${extendedInterfaceName}>(`,
2363
+ ` ${getServiceName(modelName)},`,
2364
+ ` id.toString(),`,
2365
+ ` changedFields`,
2366
+ ` );`,
2367
+ ` return result;`,
2368
+ ` }`,
2369
+ ];
2370
+ }
2371
+ /*
2372
+ * The generateDataverseGetMethod method retrieves a record by its ID using the extended interface for Dataverse entities.
2373
+ */
2374
+ function generateDataverseGetMethod(modelName, hasLookupColumns) {
2375
+ const extendedInterfaceName = modelName;
2376
+ return [
2377
+ ` public static async get(id: string, options?: IGetOptions): Promise<IOperationResult<${extendedInterfaceName}>> {`,
2378
+ ` const result = await ${modelName}Service.client.retrieveRecordAsync<${extendedInterfaceName}>(`,
2379
+ ` ${getServiceName(modelName)},`,
2380
+ ` id.toString(),`,
2381
+ ` options`,
2382
+ ` );`,
2383
+ ` return result;`,
2384
+ ` }`,
2385
+ ];
2386
+ }
2387
+ /*
2388
+ * The generateDataverseGetAllMethod method retrieves all records using the extended interface for Dataverse entities.
2389
+ */
2390
+ function generateDataverseGetAllMethod(modelName, hasLookupColumns) {
2391
+ const extendedInterfaceName = modelName;
2392
+ return [
2393
+ ` public static async getAll(options?: IGetAllOptions): Promise<IOperationResult<${extendedInterfaceName}[]>> {`,
2394
+ ` const result = await ${modelName}Service.client.retrieveMultipleRecordsAsync<${extendedInterfaceName}>(`,
2395
+ ` ${getServiceName(modelName)},`,
2396
+ ` options`,
2397
+ ` );`,
2398
+ ` return result;`,
2399
+ ` }`,
2400
+ ];
2401
+ }
2402
+ /*
2403
+ * The generateDataverseGetMetadataMethod method gets metadata using the extended interface for Dataverse entities.
2404
+ */
2405
+ function generateDataverseGetMetadataMethod(modelName, hasLookupColumns) {
2406
+ const extendedInterfaceName = modelName;
2407
+ return [
2408
+ ` public static getMetadata(`,
2409
+ ` options: GetEntityMetadataOptions<${extendedInterfaceName}> = {}`,
2410
+ ` ): Promise<IOperationResult<Partial<EntityMetadata>>> {`,
2411
+ ` return ${modelName}Service.client.executeAsync({`,
2412
+ ` dataverseRequest: {`,
2413
+ ` action: "getEntityMetadata",`,
2414
+ ` parameters: {`,
2415
+ ` tableName: ${getServiceName(modelName)},`,
2416
+ ` options: options as GetEntityMetadataOptions,`,
2417
+ ` },`,
2418
+ ` },`,
2419
+ ` });`,
2420
+ ` }`,
2421
+ ];
2422
+ }
2423
+ /**
2424
+ * The generateIndexFile method creates an index.ts file that exports all models and services.
2425
+ */
2426
+ async function generateIndexFile(outputDir, vfs) {
2427
+ const indexPath = vfs.join(outputDir, GENERATED_FOLDER, 'index.ts');
2428
+ const lines = [];
2429
+ // Add copyright notice
2430
+ addCopyrightNotice(lines);
2431
+ // Export all models from generated/models
2432
+ const modelsDir = vfs.join(outputDir, MODELS_FOLDER);
2433
+ if (await vfs.exists(modelsDir)) {
2434
+ const modelFiles = (await vfs.readdir(modelsDir)).filter((file) => file.endsWith('.ts'));
2435
+ if (modelFiles.length > 0) {
2436
+ lines.push('// Models');
2437
+ for (const file of modelFiles) {
2438
+ const fileName = file.replace('.ts', '');
2439
+ lines.push(`export * as ${fileName} from './models/${fileName}';`);
2408
2440
  }
2441
+ lines.push('');
2409
2442
  }
2410
- // Write the index file
2411
- await this.writeToFile(indexPath, lines, vfs);
2412
2443
  }
2413
- /**
2414
- * Clears and regenerates the model and service files.
2415
- * @param {string} schemaFolderPath
2416
- * @param {string} outputDir
2417
- */
2418
- static async clearAndRegenerate(schemaFolderPath, outputDir, vfs, logger) {
2419
- const modelsDir = vfs.join(outputDir, _a.MODELS_FOLDER);
2420
- const servicesDir = vfs.join(outputDir, _a.SERVICES_FOLDER);
2421
- // Delete Models directory if it exists
2422
- if (await vfs.exists(modelsDir)) {
2423
- await vfs.rmdir(modelsDir);
2424
- }
2425
- // Delete Services directory if it exists
2426
- if (await vfs.exists(servicesDir)) {
2427
- await vfs.rmdir(servicesDir);
2444
+ // Export all services from generated/services
2445
+ const servicesDir = vfs.join(outputDir, SERVICES_FOLDER);
2446
+ if (await vfs.exists(servicesDir)) {
2447
+ const serviceFiles = (await vfs.readdir(servicesDir)).filter((file) => file.endsWith('.ts'));
2448
+ if (serviceFiles.length > 0) {
2449
+ lines.push('// Services');
2450
+ for (const file of serviceFiles) {
2451
+ const fileName = file.replace('.ts', '');
2452
+ lines.push(`export * from './services/${fileName}';`);
2453
+ }
2454
+ lines.push('');
2428
2455
  }
2429
- await _a.run([schemaFolderPath, outputDir], logger);
2430
2456
  }
2457
+ // Write the index file
2458
+ await writeToFile(indexPath, lines, vfs);
2431
2459
  }
2432
- _a = ModelServiceGenerator;
2433
- // Configuration constants for output folders
2434
- ModelServiceGenerator.GENERATED_FOLDER = 'generated';
2435
- ModelServiceGenerator.MODELS_FOLDER = `${_a.GENERATED_FOLDER}/models`;
2436
- ModelServiceGenerator.SERVICES_FOLDER = `${_a.GENERATED_FOLDER}/services`;
2437
- ModelServiceGenerator.RELATIVE_MODEL_PATH = '../models';
2438
2460
  /**
2439
2461
  * Check if the propertyNode is a sharepoint choice or multi select choice
2440
2462
  *
@@ -2442,14 +2464,14 @@ ModelServiceGenerator.RELATIVE_MODEL_PATH = '../models';
2442
2464
  * @returns {boolean}
2443
2465
  */
2444
2466
  function isSharepointChoiceColumn(propNode) {
2445
- var _b;
2467
+ var _a;
2446
2468
  try {
2447
2469
  if (!propNode || typeof propNode !== 'object' || Array.isArray(propNode)) {
2448
2470
  return false;
2449
2471
  }
2450
2472
  else if ((propNode === null || propNode === void 0 ? void 0 : propNode['x-ms-capabilities']) !== undefined) {
2451
2473
  const capabilities = propNode['x-ms-capabilities'];
2452
- if (((_b = capabilities === null || capabilities === void 0 ? void 0 : capabilities['x-ms-sp']) === null || _b === void 0 ? void 0 : _b.IsChoice) === true) {
2474
+ if (((_a = capabilities === null || capabilities === void 0 ? void 0 : capabilities['x-ms-sp']) === null || _a === void 0 ? void 0 : _a.IsChoice) === true) {
2453
2475
  return true;
2454
2476
  }
2455
2477
  }