@credibledata/cred-cli 0.0.100 → 0.0.103

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 (225) hide show
  1. package/README-DEV.md +2 -2
  2. package/README.md +1 -1
  3. package/coverage/coverage-summary.json +40 -0
  4. package/coverage/tmp/coverage-2152-1789496974457-0.json +1 -0
  5. package/coverage/tmp/coverage-2153-1789496954404-0.json +1 -0
  6. package/dist/client/api.js +6151 -2254
  7. package/dist/client/api.js.map +1 -1
  8. package/dist/client/base.js +1 -1
  9. package/dist/client/common.js +1 -1
  10. package/dist/client/configuration.js +1 -1
  11. package/dist/client/index.js +1 -1
  12. package/dist/clientconfiguration.js +114 -26
  13. package/dist/clientconfiguration.js.map +1 -1
  14. package/dist/clientconfiguration.spec.js +159 -37
  15. package/dist/clientconfiguration.spec.js.map +1 -1
  16. package/dist/commands/add.js +36 -5
  17. package/dist/commands/add.js.map +1 -1
  18. package/dist/commands/connection/createconnections.js +4 -4
  19. package/dist/commands/connection/createconnections.js.map +1 -1
  20. package/dist/commands/connection/createconnections.spec.js +11 -1
  21. package/dist/commands/connection/createconnections.spec.js.map +1 -1
  22. package/dist/commands/connection/deleteconnection.js +4 -4
  23. package/dist/commands/connection/deleteconnection.js.map +1 -1
  24. package/dist/commands/connection/deleteconnection.spec.js +1 -1
  25. package/dist/commands/connection/deleteconnection.spec.js.map +1 -1
  26. package/dist/commands/connection/listconnections.js +4 -4
  27. package/dist/commands/connection/listconnections.js.map +1 -1
  28. package/dist/commands/connection/listconnections.spec.js +1 -1
  29. package/dist/commands/connection/listconnections.spec.js.map +1 -1
  30. package/dist/commands/{project/createproject.js → environment/createenvironment.js} +17 -14
  31. package/dist/commands/environment/createenvironment.js.map +1 -0
  32. package/dist/commands/{project/createproject.spec.js → environment/createenvironment.spec.js} +46 -46
  33. package/dist/commands/environment/createenvironment.spec.js.map +1 -0
  34. package/dist/commands/{project/deleteproject.js → environment/deleteenvironment.js} +9 -9
  35. package/dist/commands/environment/deleteenvironment.js.map +1 -0
  36. package/dist/commands/{project/deleteproject.spec.js → environment/deleteenvironment.spec.js} +29 -29
  37. package/dist/commands/environment/deleteenvironment.spec.js.map +1 -0
  38. package/dist/commands/{project/getproject.js → environment/getenvironment.js} +8 -8
  39. package/dist/commands/environment/getenvironment.js.map +1 -0
  40. package/dist/commands/{project/getproject.spec.js → environment/getenvironment.spec.js} +32 -33
  41. package/dist/commands/environment/getenvironment.spec.js.map +1 -0
  42. package/dist/commands/{project/listprojects.js → environment/listenvironments.js} +10 -10
  43. package/dist/commands/environment/listenvironments.js.map +1 -0
  44. package/dist/commands/{project/listprojects.spec.js → environment/listenvironments.spec.js} +35 -35
  45. package/dist/commands/environment/listenvironments.spec.js.map +1 -0
  46. package/dist/commands/{project/setproject.js → environment/setenvironment.js} +6 -5
  47. package/dist/commands/environment/setenvironment.js.map +1 -0
  48. package/dist/commands/{project/setproject.spec.js → environment/setenvironment.spec.js} +9 -9
  49. package/dist/commands/environment/setenvironment.spec.js.map +1 -0
  50. package/dist/commands/environment/updateenvironment.js +130 -0
  51. package/dist/commands/environment/updateenvironment.js.map +1 -0
  52. package/dist/commands/environment/updateenvironment.spec.js +213 -0
  53. package/dist/commands/environment/updateenvironment.spec.js.map +1 -0
  54. package/dist/commands/get.js +66 -5
  55. package/dist/commands/get.js.map +1 -1
  56. package/dist/commands/legacy.js +9 -0
  57. package/dist/commands/legacy.js.map +1 -0
  58. package/dist/commands/list.js +95 -4
  59. package/dist/commands/list.js.map +1 -1
  60. package/dist/commands/materialization/cancelrun.js +62 -0
  61. package/dist/commands/materialization/cancelrun.js.map +1 -0
  62. package/dist/commands/materialization/getindex.js +52 -0
  63. package/dist/commands/materialization/getindex.js.map +1 -0
  64. package/dist/commands/materialization/getmaterialization.js +54 -0
  65. package/dist/commands/materialization/getmaterialization.js.map +1 -0
  66. package/dist/commands/materialization/getpreaggregate.js +55 -0
  67. package/dist/commands/materialization/getpreaggregate.js.map +1 -0
  68. package/dist/commands/materialization/getrun.js +54 -0
  69. package/dist/commands/materialization/getrun.js.map +1 -0
  70. package/dist/commands/materialization/listindexes.js +58 -0
  71. package/dist/commands/materialization/listindexes.js.map +1 -0
  72. package/dist/commands/materialization/listmaterializations.js +52 -0
  73. package/dist/commands/materialization/listmaterializations.js.map +1 -0
  74. package/dist/commands/materialization/listpreaggregates.js +61 -0
  75. package/dist/commands/materialization/listpreaggregates.js.map +1 -0
  76. package/dist/commands/materialization/listruns.js +60 -0
  77. package/dist/commands/materialization/listruns.js.map +1 -0
  78. package/dist/commands/materialization/rerun.js +111 -0
  79. package/dist/commands/materialization/rerun.js.map +1 -0
  80. package/dist/commands/misc/login.js +16 -2
  81. package/dist/commands/misc/login.js.map +1 -1
  82. package/dist/commands/misc/login.spec.js +133 -5
  83. package/dist/commands/misc/login.spec.js.map +1 -1
  84. package/dist/commands/package/deletepackage.js +4 -4
  85. package/dist/commands/package/deletepackage.js.map +1 -1
  86. package/dist/commands/package/deletepackage.spec.js +1 -1
  87. package/dist/commands/package/deletepackage.spec.js.map +1 -1
  88. package/dist/commands/package/listpackages.js +11 -4
  89. package/dist/commands/package/listpackages.js.map +1 -1
  90. package/dist/commands/package/listpackages.spec.js +13 -1
  91. package/dist/commands/package/listpackages.spec.js.map +1 -1
  92. package/dist/commands/package/publish.js +36 -13
  93. package/dist/commands/package/publish.js.map +1 -1
  94. package/dist/commands/package/publish.spec.js +27 -1
  95. package/dist/commands/package/publish.spec.js.map +1 -1
  96. package/dist/commands/package/updatepackage.js +4 -4
  97. package/dist/commands/package/updatepackage.js.map +1 -1
  98. package/dist/commands/package/updatepackage.spec.js +1 -1
  99. package/dist/commands/package/updatepackage.spec.js.map +1 -1
  100. package/dist/commands/permission/createpermission.js +123 -0
  101. package/dist/commands/permission/createpermission.js.map +1 -0
  102. package/dist/commands/permission/createpermission.spec.js +360 -0
  103. package/dist/commands/permission/createpermission.spec.js.map +1 -0
  104. package/dist/commands/permission/deletepermission.js +86 -0
  105. package/dist/commands/permission/deletepermission.js.map +1 -0
  106. package/dist/commands/permission/getpermission.js +85 -0
  107. package/dist/commands/permission/getpermission.js.map +1 -0
  108. package/dist/commands/permission/listpermissions.js +75 -0
  109. package/dist/commands/permission/listpermissions.js.map +1 -0
  110. package/dist/commands/permission/scope.js +140 -0
  111. package/dist/commands/permission/scope.js.map +1 -0
  112. package/dist/commands/rm.js +26 -4
  113. package/dist/commands/rm.js.map +1 -1
  114. package/dist/commands/set.js +12 -4
  115. package/dist/commands/set.js.map +1 -1
  116. package/dist/commands/update.js +29 -10
  117. package/dist/commands/update.js.map +1 -1
  118. package/dist/commands/version/archive-unarchive-version.js +4 -5
  119. package/dist/commands/version/archive-unarchive-version.js.map +1 -1
  120. package/dist/commands/version/archive-unarchive-version.spec.js +4 -4
  121. package/dist/commands/version/archive-unarchive-version.spec.js.map +1 -1
  122. package/dist/commands/version/listversions.js +4 -4
  123. package/dist/commands/version/listversions.js.map +1 -1
  124. package/dist/commands/version/listversions.spec.js +1 -1
  125. package/dist/commands/version/listversions.spec.js.map +1 -1
  126. package/dist/common/UserGroupId.js +1 -1
  127. package/dist/common/UserGroupId.js.map +1 -1
  128. package/dist/common/UserGroupId.spec.js +2 -4
  129. package/dist/common/UserGroupId.spec.js.map +1 -1
  130. package/dist/constants.js +95 -19
  131. package/dist/constants.js.map +1 -1
  132. package/dist/constants.spec.js +90 -0
  133. package/dist/constants.spec.js.map +1 -0
  134. package/dist/index.js +128 -15
  135. package/dist/index.js.map +1 -1
  136. package/dist/logger.js +5 -1
  137. package/dist/logger.js.map +1 -1
  138. package/openapitools.json +4 -1
  139. package/package.json +10 -9
  140. package/src/client/api.ts +9363 -3093
  141. package/src/client/base.ts +1 -1
  142. package/src/client/common.ts +1 -1
  143. package/src/client/configuration.ts +1 -1
  144. package/src/client/index.ts +1 -1
  145. package/src/clientconfiguration.spec.ts +238 -113
  146. package/src/clientconfiguration.ts +131 -30
  147. package/src/commands/add.ts +78 -12
  148. package/src/commands/connection/createconnections.spec.ts +11 -1
  149. package/src/commands/connection/createconnections.ts +4 -4
  150. package/src/commands/connection/deleteconnection.spec.ts +1 -1
  151. package/src/commands/connection/deleteconnection.ts +4 -4
  152. package/src/commands/connection/listconnections.spec.ts +1 -1
  153. package/src/commands/connection/listconnections.ts +4 -4
  154. package/src/commands/{project/createproject.spec.ts → environment/createenvironment.spec.ts} +56 -54
  155. package/src/commands/{project/createproject.ts → environment/createenvironment.ts} +17 -14
  156. package/src/commands/{project/deleteproject.spec.ts → environment/deleteenvironment.spec.ts} +59 -30
  157. package/src/commands/{project/deleteproject.ts → environment/deleteenvironment.ts} +10 -10
  158. package/src/commands/environment/getenvironment.spec.ts +110 -0
  159. package/src/commands/{project/getproject.ts → environment/getenvironment.ts} +9 -9
  160. package/src/commands/{project/listprojects.spec.ts → environment/listenvironments.spec.ts} +37 -35
  161. package/src/commands/{project/listprojects.ts → environment/listenvironments.ts} +13 -8
  162. package/src/commands/{project/setproject.spec.ts → environment/setenvironment.spec.ts} +8 -8
  163. package/src/commands/{project/setproject.ts → environment/setenvironment.ts} +4 -4
  164. package/src/commands/environment/updateenvironment.spec.ts +386 -0
  165. package/src/commands/environment/updateenvironment.ts +168 -0
  166. package/src/commands/get.ts +131 -9
  167. package/src/commands/legacy.ts +5 -0
  168. package/src/commands/list.ts +204 -9
  169. package/src/commands/materialization/cancelrun.ts +70 -0
  170. package/src/commands/materialization/getindex.ts +55 -0
  171. package/src/commands/materialization/getmaterialization.ts +57 -0
  172. package/src/commands/materialization/getpreaggregate.ts +58 -0
  173. package/src/commands/materialization/getrun.ts +55 -0
  174. package/src/commands/materialization/listindexes.ts +69 -0
  175. package/src/commands/materialization/listmaterializations.ts +53 -0
  176. package/src/commands/materialization/listpreaggregates.ts +74 -0
  177. package/src/commands/materialization/listruns.ts +77 -0
  178. package/src/commands/materialization/rerun.ts +123 -0
  179. package/src/commands/misc/login.spec.ts +165 -7
  180. package/src/commands/misc/login.ts +20 -2
  181. package/src/commands/package/deletepackage.spec.ts +1 -1
  182. package/src/commands/package/deletepackage.ts +4 -4
  183. package/src/commands/package/listpackages.spec.ts +17 -1
  184. package/src/commands/package/listpackages.ts +11 -4
  185. package/src/commands/package/publish.spec.ts +46 -1
  186. package/src/commands/package/publish.ts +38 -13
  187. package/src/commands/package/updatepackage.spec.ts +1 -1
  188. package/src/commands/package/updatepackage.ts +4 -4
  189. package/src/commands/permission/createpermission.spec.ts +507 -0
  190. package/src/commands/permission/createpermission.ts +181 -0
  191. package/src/commands/permission/deletepermission.ts +117 -0
  192. package/src/commands/permission/getpermission.ts +115 -0
  193. package/src/commands/permission/listpermissions.ts +101 -0
  194. package/src/commands/permission/scope.ts +180 -0
  195. package/src/commands/rm.ts +54 -9
  196. package/src/commands/set.ts +13 -4
  197. package/src/commands/update.ts +66 -23
  198. package/src/commands/version/archive-unarchive-version.spec.ts +4 -4
  199. package/src/commands/version/archive-unarchive-version.ts +4 -7
  200. package/src/commands/version/listversions.spec.ts +1 -1
  201. package/src/commands/version/listversions.ts +4 -4
  202. package/src/common/UserGroupId.spec.ts +2 -4
  203. package/src/common/UserGroupId.ts +1 -1
  204. package/src/constants.spec.ts +111 -0
  205. package/src/constants.ts +131 -19
  206. package/src/index.ts +187 -18
  207. package/src/logger.ts +5 -1
  208. package/bun.lock +0 -1158
  209. package/dist/commands/project/createproject.js.map +0 -1
  210. package/dist/commands/project/createproject.spec.js.map +0 -1
  211. package/dist/commands/project/deleteproject.js.map +0 -1
  212. package/dist/commands/project/deleteproject.spec.js.map +0 -1
  213. package/dist/commands/project/getproject.js.map +0 -1
  214. package/dist/commands/project/getproject.spec.js.map +0 -1
  215. package/dist/commands/project/listprojects.js.map +0 -1
  216. package/dist/commands/project/listprojects.spec.js.map +0 -1
  217. package/dist/commands/project/setproject.js.map +0 -1
  218. package/dist/commands/project/setproject.spec.js.map +0 -1
  219. package/dist/commands/project/updateproject.js +0 -68
  220. package/dist/commands/project/updateproject.js.map +0 -1
  221. package/dist/commands/project/updateproject.spec.js +0 -129
  222. package/dist/commands/project/updateproject.spec.js.map +0 -1
  223. package/src/commands/project/getproject.spec.ts +0 -109
  224. package/src/commands/project/updateproject.spec.ts +0 -217
  225. package/src/commands/project/updateproject.ts +0 -82
@@ -0,0 +1,386 @@
1
+ import { expect } from "chai";
2
+ import sinon from "sinon";
3
+ import { EnvironmentsApi } from "../../client/api";
4
+ import { ClientConfiguration } from "../../clientconfiguration";
5
+ import { logger } from "../../logger";
6
+ import { updateEnvironment } from "./updateenvironment";
7
+
8
+ describe("update-environment", () => {
9
+ let sandbox: sinon.SinonSandbox;
10
+ let updateEnvironmentStub: sinon.SinonStub;
11
+ let loggerStub: sinon.SinonStubbedInstance<typeof logger>;
12
+
13
+ beforeEach(() => {
14
+ sandbox = sinon.createSandbox();
15
+ updateEnvironmentStub = sandbox.stub();
16
+ loggerStub = sandbox.stub(logger);
17
+
18
+ // Mock ClientConfiguration constructor
19
+ sandbox
20
+ .stub(ClientConfiguration.prototype, "getOrganization")
21
+ .returns("test-org");
22
+ sandbox
23
+ .stub(ClientConfiguration.prototype, "doesConfigExists")
24
+ .returns(true);
25
+ sandbox
26
+ .stub(ClientConfiguration.prototype, "getHeaders")
27
+ .returns({ Authorization: "Bearer test-token" });
28
+
29
+ // Mock EnvironmentsApi constructor and method
30
+ sandbox
31
+ .stub(EnvironmentsApi.prototype, "updateEnvironment")
32
+ .callsFake(updateEnvironmentStub);
33
+ });
34
+
35
+ afterEach(() => {
36
+ sandbox.restore();
37
+ });
38
+
39
+ describe("updateEnvironment", () => {
40
+ it("should update environment successfully", async () => {
41
+ const mockResponse = { status: 200 };
42
+ updateEnvironmentStub.resolves(mockResponse);
43
+
44
+ await updateEnvironment(
45
+ "http://test-server",
46
+ "test-environment",
47
+ { replicationCount: 3 },
48
+ false,
49
+ true,
50
+ );
51
+
52
+ expect(updateEnvironmentStub.calledOnce).to.be.true;
53
+ expect(updateEnvironmentStub.firstCall.args[0]).to.equal("test-org");
54
+ expect(updateEnvironmentStub.firstCall.args[1]).to.equal(
55
+ "test-environment",
56
+ );
57
+ expect(updateEnvironmentStub.firstCall.args[2]).to.include({
58
+ replicationCount: 3,
59
+ });
60
+ expect(updateEnvironmentStub.firstCall.args[3]).to.deep.include({
61
+ baseURL: "http://test-server",
62
+ headers: { Authorization: "Bearer test-token" },
63
+ });
64
+ expect(
65
+ loggerStub.info.calledWith(
66
+ sinon.match("Successfully updated environment test-environment"),
67
+ ),
68
+ ).to.be.true;
69
+ });
70
+
71
+ it("should handle missing environment name", async () => {
72
+ await updateEnvironment(
73
+ "http://test-server",
74
+ "",
75
+ { replicationCount: 3 },
76
+ false,
77
+ true,
78
+ );
79
+
80
+ expect(updateEnvironmentStub.called).to.be.false;
81
+ expect(
82
+ loggerStub.error.calledWith(
83
+ sinon.match("Please provide an environment name"),
84
+ ),
85
+ ).to.be.true;
86
+ });
87
+
88
+ it("should handle API errors", async () => {
89
+ const mockError = new Error("API Error");
90
+ updateEnvironmentStub.rejects(mockError);
91
+
92
+ await updateEnvironment(
93
+ "http://test-server",
94
+ "test-environment",
95
+ { replicationCount: 3 },
96
+ true,
97
+ true,
98
+ );
99
+
100
+ expect(updateEnvironmentStub.calledOnce).to.be.true;
101
+ expect(
102
+ loggerStub.error.calledWith(
103
+ sinon.match("Failed to update environment test-environment"),
104
+ ),
105
+ ).to.be.true;
106
+ });
107
+
108
+ it("should handle non-200 response status", async () => {
109
+ const mockResponse = { status: 400 };
110
+ updateEnvironmentStub.resolves(mockResponse);
111
+
112
+ await updateEnvironment(
113
+ "http://test-server",
114
+ "test-environment",
115
+ { replicationCount: 3 },
116
+ false,
117
+ true,
118
+ );
119
+
120
+ expect(updateEnvironmentStub.calledOnce).to.be.true;
121
+ expect(
122
+ loggerStub.error.calledWith(
123
+ sinon.match("Failed to update environment. Status: 400"),
124
+ ),
125
+ ).to.be.true;
126
+ });
127
+ });
128
+
129
+ describe("validators", () => {
130
+ describe("authentication validation", () => {
131
+ it("should fail when user is not logged in", async () => {
132
+ sandbox.restore();
133
+ sandbox = sinon.createSandbox();
134
+ updateEnvironmentStub = sandbox.stub();
135
+ loggerStub = sandbox.stub(logger);
136
+
137
+ sandbox
138
+ .stub(ClientConfiguration.prototype, "doesConfigExists")
139
+ .returns(false);
140
+ sandbox
141
+ .stub(EnvironmentsApi.prototype, "updateEnvironment")
142
+ .callsFake(updateEnvironmentStub);
143
+
144
+ await updateEnvironment(
145
+ "http://test-server",
146
+ "test-environment",
147
+ { replicationCount: 3 },
148
+ false,
149
+ true,
150
+ );
151
+
152
+ expect(updateEnvironmentStub.called).to.be.false;
153
+ expect(
154
+ loggerStub.error.calledWith(
155
+ sinon.match("Please, make sure that you are logged in"),
156
+ ),
157
+ ).to.be.true;
158
+ });
159
+ });
160
+
161
+ describe("git-backed modeling", () => {
162
+ it("sends the enrolment flag on its own", async () => {
163
+ updateEnvironmentStub.resolves({ status: 200 });
164
+
165
+ await updateEnvironment(
166
+ "http://test-server",
167
+ "test-environment",
168
+ { gitModeling: "true" },
169
+ false,
170
+ true,
171
+ );
172
+
173
+ expect(updateEnvironmentStub.calledOnce).to.be.true;
174
+ expect(updateEnvironmentStub.firstCall.args[2]).to.deep.equal({
175
+ gitModelingEnabled: true,
176
+ });
177
+ });
178
+
179
+ it("rejects a --git-modeling value that is neither true nor false", async () => {
180
+ await updateEnvironment(
181
+ "http://test-server",
182
+ "test-environment",
183
+ { gitModeling: "yes" },
184
+ false,
185
+ true,
186
+ );
187
+
188
+ expect(updateEnvironmentStub.called).to.be.false;
189
+ expect(
190
+ loggerStub.error.calledWith(
191
+ sinon.match("--git-modeling must be true or false"),
192
+ ),
193
+ ).to.be.true;
194
+ });
195
+
196
+ it("sends all three coordinates together", async () => {
197
+ updateEnvironmentStub.resolves({ status: 200 });
198
+
199
+ await updateEnvironment(
200
+ "http://test-server",
201
+ "test-environment",
202
+ {
203
+ gitRepositoryOwner: "credible-data-repos",
204
+ gitRepositoryName: "models",
205
+ gitBaseBranch: "main",
206
+ },
207
+ false,
208
+ true,
209
+ );
210
+
211
+ expect(updateEnvironmentStub.firstCall.args[2]).to.deep.equal({
212
+ gitRepositoryOwner: "credible-data-repos",
213
+ gitRepositoryName: "models",
214
+ gitBaseBranch: "main",
215
+ });
216
+ });
217
+
218
+ it("refuses a partial coordinate locally rather than sending a 400", async () => {
219
+ // The API rejects this as GIT_REPOSITORY_INCOMPLETE. Refusing here means a
220
+ // script gets the rule from the tool it is driving.
221
+ await updateEnvironment(
222
+ "http://test-server",
223
+ "test-environment",
224
+ { gitRepositoryOwner: "credible-data-repos" },
225
+ false,
226
+ true,
227
+ );
228
+
229
+ expect(updateEnvironmentStub.called).to.be.false;
230
+ expect(loggerStub.error.calledWith(sinon.match("change together")))
231
+ .to.be.true;
232
+ });
233
+
234
+ it("refuses a mix of set and cleared coordinates", async () => {
235
+ await updateEnvironment(
236
+ "http://test-server",
237
+ "test-environment",
238
+ {
239
+ gitRepositoryOwner: "credible-data-repos",
240
+ gitRepositoryName: "",
241
+ gitBaseBranch: "main",
242
+ },
243
+ false,
244
+ true,
245
+ );
246
+
247
+ expect(updateEnvironmentStub.called).to.be.false;
248
+ expect(
249
+ loggerStub.error.calledWith(sinon.match("unset the repository")),
250
+ ).to.be.true;
251
+ });
252
+
253
+ it("sends nulls when all three are emptied, which is how a record is unset", async () => {
254
+ updateEnvironmentStub.resolves({ status: 200 });
255
+
256
+ await updateEnvironment(
257
+ "http://test-server",
258
+ "test-environment",
259
+ {
260
+ gitRepositoryOwner: "",
261
+ gitRepositoryName: "",
262
+ gitBaseBranch: " ",
263
+ },
264
+ false,
265
+ true,
266
+ );
267
+
268
+ expect(updateEnvironmentStub.firstCall.args[2]).to.deep.equal({
269
+ gitRepositoryOwner: null,
270
+ gitRepositoryName: null,
271
+ gitBaseBranch: null,
272
+ });
273
+ });
274
+
275
+ it("carries the enrolment and the coordinates in one request", async () => {
276
+ updateEnvironmentStub.resolves({ status: 200 });
277
+
278
+ await updateEnvironment(
279
+ "http://test-server",
280
+ "test-environment",
281
+ {
282
+ gitModeling: "true",
283
+ gitRepositoryOwner: "credible-data-repos",
284
+ gitRepositoryName: "models",
285
+ gitBaseBranch: "main",
286
+ },
287
+ false,
288
+ true,
289
+ );
290
+
291
+ expect(updateEnvironmentStub.firstCall.args[2]).to.deep.equal({
292
+ gitModelingEnabled: true,
293
+ gitRepositoryOwner: "credible-data-repos",
294
+ gitRepositoryName: "models",
295
+ gitBaseBranch: "main",
296
+ });
297
+ });
298
+
299
+ it("reports a refusal from the server rather than claiming success", async () => {
300
+ updateEnvironmentStub.rejects(
301
+ Object.assign(new Error("Request failed with status code 400"), {
302
+ response: {
303
+ status: 400,
304
+ data: { code: "GIT_REPOSITORY_NOT_REGISTERED" },
305
+ },
306
+ }),
307
+ );
308
+
309
+ await updateEnvironment(
310
+ "http://test-server",
311
+ "test-environment",
312
+ { gitModeling: "true" },
313
+ false,
314
+ true,
315
+ );
316
+
317
+ expect(
318
+ loggerStub.error.calledWith(
319
+ sinon.match("Failed to update environment test-environment"),
320
+ ),
321
+ ).to.be.true;
322
+ expect(
323
+ loggerStub.info.calledWith(sinon.match("Successfully updated")),
324
+ ).to.be.false;
325
+ });
326
+ });
327
+
328
+ describe("replication count validation", () => {
329
+ it("should accept valid replication counts", async () => {
330
+ const mockResponse = { status: 200 };
331
+ updateEnvironmentStub.resolves(mockResponse);
332
+
333
+ const validCounts = [1, 2, 5, 10, 100];
334
+
335
+ for (const count of validCounts) {
336
+ await updateEnvironment(
337
+ "http://test-server",
338
+ "test-environment",
339
+ { replicationCount: count },
340
+ false,
341
+ true,
342
+ );
343
+ expect(updateEnvironmentStub.called).to.be.true;
344
+ expect(updateEnvironmentStub.firstCall.args[2]).to.include({
345
+ replicationCount: count,
346
+ });
347
+ updateEnvironmentStub.reset();
348
+ }
349
+ });
350
+
351
+ it("should reject replication count less than 1", async () => {
352
+ await updateEnvironment(
353
+ "http://test-server",
354
+ "test-environment",
355
+ { replicationCount: 0 },
356
+ false,
357
+ true,
358
+ );
359
+
360
+ expect(updateEnvironmentStub.called).to.be.false;
361
+ expect(
362
+ loggerStub.error.calledWith(
363
+ sinon.match("Replication count must be at least 1"),
364
+ ),
365
+ ).to.be.true;
366
+ });
367
+
368
+ it("should reject negative replication count", async () => {
369
+ await updateEnvironment(
370
+ "http://test-server",
371
+ "test-environment",
372
+ { replicationCount: -1 },
373
+ false,
374
+ true,
375
+ );
376
+
377
+ expect(updateEnvironmentStub.called).to.be.false;
378
+ expect(
379
+ loggerStub.error.calledWith(
380
+ sinon.match("Replication count must be at least 1"),
381
+ ),
382
+ ).to.be.true;
383
+ });
384
+ });
385
+ });
386
+ });
@@ -0,0 +1,168 @@
1
+ import { Environment, EnvironmentsApi } from "../../client/api";
2
+ import { Configuration } from "../../client/configuration";
3
+ import { ClientConfiguration } from "../../clientconfiguration";
4
+ import { logger } from "../../logger";
5
+ import { getConfirmation, printOutError } from "../../utils";
6
+
7
+ const environmentsApi = new EnvironmentsApi(new Configuration());
8
+
9
+ type UpdateEnvironmentProperties = {
10
+ replicationCount?: number;
11
+ /** Raw `--git-modeling` value; parsed here so a typo is refused rather than sent. */
12
+ gitModeling?: string;
13
+ gitRepositoryOwner?: string;
14
+ gitRepositoryName?: string;
15
+ gitBaseBranch?: string;
16
+ };
17
+
18
+ /**
19
+ * The three repository coordinates change as a unit, and the API rejects any other
20
+ * shape (`GIT_REPOSITORY_INCOMPLETE`, `GIT_REPOSITORY_INCOHERENT`). Refused here too, so
21
+ * a scripted caller gets the rule from the tool it is driving rather than from a 400.
22
+ *
23
+ * Returns the body fragment, or a message to print instead of sending anything.
24
+ */
25
+ function gitRepositoryRecord(
26
+ properties: UpdateEnvironmentProperties,
27
+ ): { changes: Partial<Environment> } | { error: string } {
28
+ const mentioned = [
29
+ properties.gitRepositoryOwner,
30
+ properties.gitRepositoryName,
31
+ properties.gitBaseBranch,
32
+ ].filter((value) => value !== undefined) as string[];
33
+
34
+ if (mentioned.length === 0) {
35
+ return { changes: {} };
36
+ }
37
+ if (mentioned.length !== 3) {
38
+ return {
39
+ error:
40
+ "--git-repository-owner, --git-repository-name and --git-base-branch change " +
41
+ "together. Pass all three, or none.",
42
+ };
43
+ }
44
+
45
+ const trimmed = mentioned.map((value) => value.trim());
46
+ const cleared = trimmed.filter((value) => value === "").length;
47
+ if (cleared !== 0 && cleared !== 3) {
48
+ return {
49
+ error:
50
+ "Set all three of --git-repository-owner, --git-repository-name and " +
51
+ '--git-base-branch, or pass "" for all three to unset the repository.',
52
+ };
53
+ }
54
+
55
+ const [owner, name, baseBranch] = trimmed;
56
+ return {
57
+ changes: {
58
+ gitRepositoryOwner: owner || null,
59
+ gitRepositoryName: name || null,
60
+ gitBaseBranch: baseBranch || null,
61
+ },
62
+ };
63
+ }
64
+
65
+ export async function updateEnvironment(
66
+ controlPlane: string,
67
+ environmentName: string,
68
+ properties: UpdateEnvironmentProperties,
69
+ debug?: boolean,
70
+ skipConfirmation?: boolean,
71
+ ) {
72
+ const clientConfig = new ClientConfiguration();
73
+ if (!clientConfig.doesConfigExists()) {
74
+ logger.error("Please, make sure that you are logged in");
75
+ return;
76
+ }
77
+
78
+ if (!environmentName) {
79
+ logger.error("Please provide an environment name");
80
+ return;
81
+ }
82
+
83
+ const organizationName = clientConfig.getOrganization();
84
+
85
+ if (
86
+ properties.replicationCount !== undefined &&
87
+ properties.replicationCount < 1
88
+ ) {
89
+ logger.error("Replication count must be at least 1");
90
+ return;
91
+ }
92
+
93
+ if (
94
+ properties.gitModeling !== undefined &&
95
+ properties.gitModeling !== "true" &&
96
+ properties.gitModeling !== "false"
97
+ ) {
98
+ logger.error("--git-modeling must be true or false");
99
+ return;
100
+ }
101
+
102
+ const repositoryRecord = gitRepositoryRecord(properties);
103
+ if ("error" in repositoryRecord) {
104
+ logger.error(repositoryRecord.error);
105
+ return;
106
+ }
107
+
108
+ try {
109
+ if (!skipConfirmation) {
110
+ const confirmation = await getConfirmation(
111
+ `Are you sure you want to update environment ${environmentName}?`,
112
+ );
113
+ if (!confirmation) {
114
+ logger.info("Operation cancelled.");
115
+ return;
116
+ }
117
+ }
118
+
119
+ const environmentUpdate: Environment = { ...repositoryRecord.changes };
120
+
121
+ if (properties.replicationCount !== undefined) {
122
+ environmentUpdate.replicationCount = properties.replicationCount;
123
+ }
124
+
125
+ if (properties.gitModeling !== undefined) {
126
+ environmentUpdate.gitModelingEnabled =
127
+ properties.gitModeling === "true";
128
+ }
129
+
130
+ const response = await environmentsApi.updateEnvironment(
131
+ organizationName,
132
+ environmentName,
133
+ environmentUpdate,
134
+ {
135
+ baseURL: controlPlane,
136
+ headers: clientConfig.getHeaders(),
137
+ },
138
+ );
139
+
140
+ if (response.status === 200) {
141
+ logger.info(`Successfully updated environment ${environmentName}`);
142
+ if (properties.replicationCount !== undefined) {
143
+ logger.info(
144
+ `Replication count set to ${properties.replicationCount}`,
145
+ );
146
+ }
147
+ if (properties.gitModeling !== undefined) {
148
+ logger.info(
149
+ `Git-backed modeling ${properties.gitModeling === "true" ? "enabled" : "disabled"}`,
150
+ );
151
+ }
152
+ if (environmentUpdate.gitRepositoryOwner !== undefined) {
153
+ logger.info(
154
+ environmentUpdate.gitRepositoryOwner
155
+ ? `Repository set to ${environmentUpdate.gitRepositoryOwner}/${environmentUpdate.gitRepositoryName} (${environmentUpdate.gitBaseBranch})`
156
+ : "Repository unset",
157
+ );
158
+ }
159
+ } else {
160
+ logger.error(
161
+ `Failed to update environment. Status: ${response.status}`,
162
+ );
163
+ }
164
+ } catch (e) {
165
+ logger.error(`Failed to update environment ${environmentName}`);
166
+ printOutError(e, debug);
167
+ }
168
+ }