contensis-cli 1.0.0-beta.5 → 1.0.0-beta.51

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 (71) hide show
  1. package/README.md +669 -74
  2. package/dist/commands/connect.js +3 -3
  3. package/dist/commands/connect.js.map +2 -2
  4. package/dist/commands/create.js +30 -10
  5. package/dist/commands/create.js.map +2 -2
  6. package/dist/commands/diff.js +57 -0
  7. package/dist/commands/diff.js.map +7 -0
  8. package/dist/commands/get.js +61 -12
  9. package/dist/commands/get.js.map +2 -2
  10. package/dist/commands/globalOptions.js +22 -17
  11. package/dist/commands/globalOptions.js.map +2 -2
  12. package/dist/commands/import.js +36 -10
  13. package/dist/commands/import.js.map +2 -2
  14. package/dist/commands/index.js +9 -1
  15. package/dist/commands/index.js.map +2 -2
  16. package/dist/commands/list.js +19 -8
  17. package/dist/commands/list.js.map +2 -2
  18. package/dist/commands/login.js +3 -3
  19. package/dist/commands/login.js.map +2 -2
  20. package/dist/commands/push.js +8 -4
  21. package/dist/commands/push.js.map +2 -2
  22. package/dist/commands/release.js +47 -0
  23. package/dist/commands/release.js.map +7 -0
  24. package/dist/commands/remove.js +10 -8
  25. package/dist/commands/remove.js.map +2 -2
  26. package/dist/commands/set.js +53 -12
  27. package/dist/commands/set.js.map +2 -2
  28. package/dist/localisation/en-GB.js +97 -47
  29. package/dist/localisation/en-GB.js.map +2 -2
  30. package/dist/providers/CredentialProvider.js +36 -7
  31. package/dist/providers/CredentialProvider.js.map +3 -3
  32. package/dist/providers/SessionCacheProvider.js +21 -1
  33. package/dist/providers/SessionCacheProvider.js.map +2 -2
  34. package/dist/providers/file-provider.js +8 -4
  35. package/dist/providers/file-provider.js.map +3 -3
  36. package/dist/services/ContensisCliService.js +600 -336
  37. package/dist/services/ContensisCliService.js.map +3 -3
  38. package/dist/shell.js +27 -10
  39. package/dist/shell.js.map +3 -3
  40. package/dist/util/console.printer.js +170 -47
  41. package/dist/util/console.printer.js.map +2 -2
  42. package/dist/util/index.js +5 -2
  43. package/dist/util/index.js.map +3 -3
  44. package/dist/util/logger.js +45 -13
  45. package/dist/util/logger.js.map +2 -2
  46. package/dist/version.js +1 -1
  47. package/dist/version.js.map +1 -1
  48. package/package.json +2 -2
  49. package/src/commands/connect.ts +3 -2
  50. package/src/commands/create.ts +37 -8
  51. package/src/commands/diff.ts +41 -0
  52. package/src/commands/get.ts +80 -5
  53. package/src/commands/globalOptions.ts +18 -17
  54. package/src/commands/import.ts +43 -4
  55. package/src/commands/index.ts +9 -1
  56. package/src/commands/list.ts +35 -9
  57. package/src/commands/login.ts +3 -2
  58. package/src/commands/push.ts +9 -2
  59. package/src/commands/release.ts +32 -0
  60. package/src/commands/remove.ts +12 -4
  61. package/src/commands/set.ts +65 -9
  62. package/src/localisation/en-GB.ts +142 -64
  63. package/src/providers/CredentialProvider.ts +39 -6
  64. package/src/providers/SessionCacheProvider.ts +29 -2
  65. package/src/providers/file-provider.ts +8 -4
  66. package/src/services/ContensisCliService.ts +742 -387
  67. package/src/shell.ts +31 -11
  68. package/src/util/console.printer.ts +234 -66
  69. package/src/util/index.ts +12 -6
  70. package/src/util/logger.ts +84 -15
  71. package/src/version.ts +1 -1
@@ -3,7 +3,8 @@ import path from 'path';
3
3
  import fetch from 'node-fetch';
4
4
  import inquirer from 'inquirer';
5
5
  import to from 'await-to-js';
6
- import { Component, ContentType } from 'contensis-core-api';
6
+ import chalk from 'chalk';
7
+ import { Component, ContentType, Project } from 'contensis-core-api';
7
8
  import { isPassword, isSharedSecret, isUuid, url } from '~/util';
8
9
  import SessionCacheProvider from '../providers/SessionCacheProvider';
9
10
  import ContensisAuthService from './ContensisAuthService';
@@ -16,13 +17,21 @@ import {
16
17
  PushBlockParams,
17
18
  SourceCms,
18
19
  logEntriesTable,
20
+ ContentTypesResult,
21
+ Model,
22
+ MigrateModelsResult,
19
23
  } from 'migratortron';
20
24
  import { Entry } from 'contensis-management-api/lib/models';
21
25
 
22
26
  import { csvFormatter } from '~/util/csv.formatter';
23
27
  import { xmlFormatter } from '~/util/xml.formatter';
24
28
  import { jsonFormatter } from '~/util/json.formatter';
25
- import { printBlockVersion, printMigrateResult } from '~/util/console.printer';
29
+ import {
30
+ printBlockVersion,
31
+ printMigrateResult,
32
+ printModelMigrationAnalysis,
33
+ printModelMigrationResult,
34
+ } from '~/util/console.printer';
26
35
  import { readJsonFile } from '~/providers/file-provider';
27
36
 
28
37
  type OutputFormat = 'json' | 'csv' | 'xml';
@@ -45,28 +54,31 @@ interface IAuthOptions {
45
54
  }
46
55
 
47
56
  interface IImportOptions {
48
- sourceEnv?: string;
57
+ sourceAlias?: string;
49
58
  sourceProjectId?: string;
50
59
  }
51
60
 
61
+ let insecurePasswordWarningShown = false;
62
+
52
63
  class ContensisCli {
53
64
  static quit = (error?: Error) => {
54
65
  process.removeAllListeners('exit');
55
66
  const exitCode = error ? 1 : 0;
56
67
 
57
- console.info(`\nExiting contensis-cli with exit code: ${exitCode}\n`);
68
+ // console.info(`\nExiting contensis-cli with exit code: ${exitCode}\n`);
58
69
  process.exit(exitCode);
59
70
  };
60
71
 
61
- cache: SessionCache;
72
+ private command: CliCommand;
73
+ private format?: OutputFormat;
74
+ private output?: string;
75
+ private session: SessionCacheProvider;
76
+
62
77
  contensis?: ContensisMigrationService;
63
78
  contensisOpts: Partial<MigrateRequest>;
64
- contentTypes?: ContentType[];
65
- components?: Component[];
66
- currentEnv: string;
67
79
  currentProject: string;
68
- env: EnvironmentCache;
69
- sourceEnv?: string;
80
+
81
+ sourceAlias?: string;
70
82
  targetEnv?: string;
71
83
  urls:
72
84
  | {
@@ -78,17 +90,53 @@ class ContensisCli {
78
90
  iisPreviewWeb: string;
79
91
  }
80
92
  | undefined;
81
- private command: CliCommand;
82
- private format?: OutputFormat;
83
- private output?: string;
84
93
  log = Logger;
85
94
  messages = LogMessages;
86
- private session: SessionCacheProvider;
87
95
 
88
96
  verb: string;
89
97
  noun: string;
90
98
  thirdArg: string;
91
99
 
100
+ get cache() {
101
+ return this.session.Get();
102
+ }
103
+
104
+ get currentEnv() {
105
+ return this.cache.currentEnvironment || '';
106
+ }
107
+
108
+ set currentEnv(currentEnvironment: string) {
109
+ this.session.Update({ currentEnvironment });
110
+ }
111
+
112
+ get env() {
113
+ const currentEnvironment = this.currentEnv;
114
+ const environments = this.cache.environments || {};
115
+
116
+ if (!currentEnvironment) return {} as EnvironmentCache;
117
+ else if (!!environments[currentEnvironment])
118
+ return environments[currentEnvironment];
119
+ else {
120
+ return {
121
+ history: [],
122
+ lastUserId: '',
123
+ projects: [],
124
+ versionStatus: 'latest',
125
+ } as EnvironmentCache;
126
+ }
127
+ }
128
+
129
+ get contentTypes() {
130
+ return this.contensis?.models.contentTypes();
131
+ }
132
+
133
+ get components() {
134
+ return this.contensis?.models.components();
135
+ }
136
+ get models(): Model[] | undefined {
137
+ return this.contensis?.models.contentModels();
138
+ }
139
+
92
140
  constructor(
93
141
  args: string[],
94
142
  outputOpts?: OutputOptions & IConnectOptions & IImportOptions,
@@ -106,40 +154,28 @@ class ContensisCli {
106
154
  }`.trim();
107
155
 
108
156
  this.session = new SessionCacheProvider();
109
- this.cache = this.session.Get();
157
+
110
158
  this.contensisOpts = contensisOpts;
111
159
  this.format = outputOpts?.format;
112
160
  this.output =
113
161
  outputOpts?.output && path.join(process.cwd(), outputOpts.output);
114
162
 
115
- const currentEnvironment =
116
- outputOpts?.alias || this.cache.currentEnvironment || '';
163
+ const currentEnvironment = outputOpts?.alias || this.currentEnv;
117
164
  const environments = this.cache.environments || {};
118
-
119
- if (!currentEnvironment) this.env = {} as EnvironmentCache;
120
- else if (!!environments[currentEnvironment])
121
- this.env = environments[currentEnvironment];
122
- else {
123
- this.env = {
124
- history: [],
125
- lastUserId: '',
126
- projects: [],
127
- versionStatus: 'latest',
128
- };
129
- }
165
+ this.currentEnv = currentEnvironment;
130
166
 
131
167
  const env = this.env;
132
168
 
133
169
  if (outputOpts?.projectId) env.currentProject = outputOpts.projectId;
134
170
  if (outputOpts?.user) env.lastUserId = outputOpts.user;
171
+ // setting this in env means passwordFallback is written to environments.json
135
172
  if (outputOpts?.password) env.passwordFallback = outputOpts.password;
136
173
  if (outputOpts?.clientId) env.lastUserId = outputOpts.clientId;
137
174
  if (outputOpts?.sharedSecret)
138
175
  env.passwordFallback = outputOpts.sharedSecret;
139
176
 
140
- this.currentEnv = currentEnvironment;
141
177
  this.currentProject = env?.currentProject || 'null';
142
- this.sourceEnv = outputOpts?.sourceEnv || currentEnvironment;
178
+ this.sourceAlias = outputOpts?.sourceAlias || currentEnvironment;
143
179
 
144
180
  if (currentEnvironment) {
145
181
  this.urls = url(currentEnvironment, env?.currentProject || 'website');
@@ -154,7 +190,7 @@ class ContensisCli {
154
190
  if (currentEnvironment) {
155
191
  env.history = [this.command];
156
192
  if (commandText) {
157
- environments[currentEnvironment] = this.env;
193
+ environments[currentEnvironment] = env;
158
194
  this.session.Update({
159
195
  currentEnvironment,
160
196
  environments,
@@ -181,45 +217,24 @@ class ContensisCli {
181
217
  };
182
218
 
183
219
  Connect = async (environment: string) => {
184
- const { cache, log, messages, session } = this;
220
+ const { log, messages, session } = this;
185
221
 
186
222
  if (environment) {
187
- const envCache = cache.environments[environment];
188
- if (!envCache)
189
- cache.environments[environment] = {
190
- versionStatus: 'published',
191
- history: [],
192
- lastUserId: '',
193
- projects: [],
194
- ...(!this.currentEnv ? this.env : {}),
195
- };
196
-
197
- this.env = cache.environments[environment];
198
223
  this.currentEnv = environment;
199
224
  this.urls = url(environment, 'website');
200
225
 
201
226
  const [fetchErr, response] = await to(fetch(this.urls.cms));
202
227
  if (response && response?.status < 400) {
203
228
  log.success(messages.connect.connected(environment));
229
+ session.UpdateEnv(this.env, environment);
204
230
 
205
231
  if (this.env?.lastUserId) {
206
- await this.ConnectContensis();
232
+ // await this.ConnectContensis();
207
233
  await this.PrintProjects();
208
234
  } else {
209
235
  log.warning(messages.projects.noList());
210
236
  log.help(messages.connect.tip());
211
- // cache.environments[environment] = {
212
- // versionStatus: 'published',
213
- // history: [],
214
- // lastUserId: '',
215
- // projects: [],
216
- // };
217
237
  }
218
-
219
- session.Update({
220
- currentEnvironment: environment,
221
- environments: cache.environments,
222
- });
223
238
  } else {
224
239
  // Cannot reach environment - status X
225
240
  log.error(
@@ -233,68 +248,75 @@ class ContensisCli {
233
248
  };
234
249
 
235
250
  ConnectContensis = async ({ commit = false } = {}) => {
236
- const { contensisOpts, currentEnv, env, log, messages } = this;
237
- const userId = env?.lastUserId;
238
- const isGuidId = userId && isUuid(userId);
251
+ if (!this.contensis) {
252
+ const { contensisOpts, currentEnv, env, log, messages } = this;
253
+ const userId = env?.lastUserId;
254
+ const isGuidId = userId && isUuid(userId);
239
255
 
240
- if (currentEnv && userId) {
241
- const [credentialError, credentials] = await new CredentialProvider(
242
- {
256
+ if (currentEnv && userId) {
257
+ const credentials = await this.GetCredentials(
243
258
  userId,
244
- alias: currentEnv,
245
- },
246
- env.passwordFallback
247
- ).Init();
259
+ env.passwordFallback
260
+ );
248
261
 
249
- if (credentialError && !credentials.current) {
250
- // Log problem with Credential Provider
251
- log.error(credentialError as any);
252
- return;
253
- }
254
- const cachedPassword = credentials?.current?.password;
255
-
256
- if (cachedPassword) {
257
- this.contensis = new ContensisMigrationService(
258
- {
259
- ...contensisOpts,
260
- source: {
261
- url: this.urls?.cms || '',
262
- username: !isGuidId ? userId : undefined,
263
- password: !isGuidId ? cachedPassword : undefined,
264
- clientId: isGuidId ? userId : undefined,
265
- sharedSecret: isGuidId ? cachedPassword : undefined,
266
- project: env?.currentProject || '',
267
- assetHostname: this.urls?.previewWeb,
262
+ const cachedPassword = credentials?.current?.password;
263
+
264
+ if (cachedPassword) {
265
+ this.contensis = new ContensisMigrationService(
266
+ {
267
+ ...contensisOpts,
268
+ source: {
269
+ url: this.urls?.cms || '',
270
+ username: !isGuidId ? userId : undefined,
271
+ password: !isGuidId ? cachedPassword : undefined,
272
+ clientId: isGuidId ? userId : undefined,
273
+ sharedSecret: isGuidId ? cachedPassword : undefined,
274
+ project: env?.currentProject || '',
275
+ assetHostname: this.urls?.previewWeb,
276
+ },
277
+ concurrency:
278
+ typeof contensisOpts.concurrency !== 'undefined'
279
+ ? contensisOpts.concurrency
280
+ : 3,
281
+ outputProgress: true,
268
282
  },
269
- concurrency:
270
- typeof contensisOpts.concurrency !== 'undefined'
271
- ? contensisOpts.concurrency
272
- : 3,
273
- outputProgress: true,
274
- },
275
- !commit
276
- );
283
+ !commit
284
+ );
285
+ }
286
+ } else {
287
+ if (!currentEnv) log.help(messages.connect.help());
288
+ if (!userId) log.help(messages.connect.tip());
277
289
  }
278
- } else {
279
- if (!currentEnv) log.help(messages.connect.help());
280
- if (!userId) log.help(messages.connect.tip());
281
290
  }
291
+ return this.contensis;
282
292
  };
283
293
 
284
294
  ConnectContensisImport = async ({
285
- commit,
286
- source,
287
- fileData,
288
- fileDataType,
295
+ commit = false,
296
+ fromFile,
297
+ importDataType,
289
298
  }: {
290
- commit: boolean;
291
- source: 'contensis' | 'file' | 'input';
292
- fileData?: any[] | string;
293
- fileDataType?: 'entries' | 'contentTypes' | 'components';
299
+ commit?: boolean;
300
+ fromFile?: string;
301
+ importDataType?:
302
+ | 'entries'
303
+ | 'contentTypes'
304
+ | 'components'
305
+ | 'models'
306
+ | 'user-input';
294
307
  }) => {
295
- const { contensisOpts, currentEnv, env, log, messages, sourceEnv } = this;
308
+ const source: 'contensis' | 'file' = fromFile ? 'file' : 'contensis';
309
+
310
+ const fileData = fromFile
311
+ ? readJsonFile<(Entry | ContentType | Component)[]>(fromFile) || []
312
+ : [];
313
+
314
+ if (typeof fileData === 'string')
315
+ throw new Error(`Import file format must be of type JSON`);
316
+
317
+ const { contensisOpts, currentEnv, env, log, messages, sourceAlias } = this;
296
318
  const environments = this.cache.environments || {};
297
- const sourceEnvironment = environments[sourceEnv || ''] || {};
319
+ const sourceEnvironment = environments[sourceAlias || ''] || {};
298
320
  const sourceCms =
299
321
  ('source' in contensisOpts && contensisOpts.source) ||
300
322
  ({} as Partial<SourceCms>);
@@ -303,7 +325,7 @@ class ContensisCli {
303
325
  const sourceProjectId =
304
326
  sourceCms.project || sourceEnvironment.currentProject || 'website';
305
327
  const isSourceGuidId = sourceUserId && isUuid(sourceUserId);
306
- const sourceUrls = url(sourceEnv || '', sourceProjectId);
328
+ const sourceUrls = url(sourceAlias || '', sourceProjectId);
307
329
 
308
330
  const sourcePassword =
309
331
  sourceCms.sharedSecret ||
@@ -314,40 +336,24 @@ class ContensisCli {
314
336
  const isTargetGuidId = targetUserId && isUuid(targetUserId);
315
337
 
316
338
  if (sourceUserId && currentEnv && targetUserId) {
317
- const [sourceCredentialError, sourceCredentials] =
318
- await new CredentialProvider(
319
- {
320
- userId: sourceUserId,
321
- alias: currentEnv,
322
- },
323
- sourcePassword
324
- ).Init();
325
-
326
- if (sourceCredentialError && !sourceCredentials.current) {
327
- // Log problem with Credential Provider
328
- logError(sourceCredentialError);
329
- return;
330
- }
339
+ const sourceCredentials = await this.GetCredentials(
340
+ sourceUserId,
341
+ sourcePassword,
342
+ sourceAlias,
343
+ false
344
+ );
345
+
331
346
  const cachedSourcePassword = sourceCredentials?.current?.password;
332
347
 
333
- const [targetCredentialError, targetCredentials] =
334
- await new CredentialProvider(
335
- {
336
- userId: targetUserId,
337
- alias: currentEnv,
338
- },
339
- env.passwordFallback
340
- ).Init();
348
+ const targetCredentials = await this.GetCredentials(
349
+ targetUserId,
350
+ env.passwordFallback
351
+ );
341
352
 
342
- if (targetCredentialError && !targetCredentials.current) {
343
- // Log problem with Credential Provider
344
- log.error(targetCredentialError as any);
345
- return;
346
- }
347
353
  const cachedTargetPassword = targetCredentials?.current?.password;
348
354
 
349
355
  if (cachedSourcePassword && cachedTargetPassword) {
350
- if (source === 'file' || source === 'input') {
356
+ if (source === 'file' || importDataType === 'user-input') {
351
357
  this.contensis = new ContensisMigrationService(
352
358
  {
353
359
  concurrency: 3,
@@ -362,12 +368,11 @@ class ContensisCli {
362
368
  targetProjects: [env.currentProject || ''],
363
369
  assetHostname: this.urls?.previewWeb,
364
370
  },
365
- ...(fileDataType ? { [fileDataType]: fileData } : {}),
371
+ ...(importDataType ? { [importDataType]: fileData } : {}),
366
372
  },
367
373
  !commit
368
374
  );
369
- }
370
- if (source === 'contensis') {
375
+ } else if (source === 'contensis') {
371
376
  this.contensis = new ContensisMigrationService(
372
377
  {
373
378
  concurrency: 3,
@@ -400,6 +405,40 @@ class ContensisCli {
400
405
  if (!currentEnv) log.help(messages.connect.help());
401
406
  if (!targetUserId) log.help(messages.connect.tip());
402
407
  }
408
+ return this.contensis;
409
+ };
410
+
411
+ GetCredentials = async (
412
+ userId: string,
413
+ password?: string,
414
+ currentEnv = this.currentEnv,
415
+ saveCurrentEnv = true
416
+ ): Promise<CredentialProvider | undefined> => {
417
+ const { log, messages } = this;
418
+ if (userId) {
419
+ const [credentialError, credentials] = await new CredentialProvider(
420
+ { userId, alias: currentEnv },
421
+ password
422
+ ).Init();
423
+
424
+ if (credentialError && !credentials.current) {
425
+ // Log problem with Credential Provider
426
+ log.error(credentialError as any);
427
+ return;
428
+ }
429
+
430
+ if (credentials.remarks.secure !== true) {
431
+ if (!insecurePasswordWarningShown) {
432
+ log.warning(messages.login.insecurePassword());
433
+ insecurePasswordWarningShown = true;
434
+ }
435
+ } else {
436
+ const env = this.cache.environments[currentEnv];
437
+ env.passwordFallback = undefined;
438
+ this.session.UpdateEnv(env, currentEnv, saveCurrentEnv);
439
+ }
440
+ return credentials;
441
+ }
403
442
  };
404
443
 
405
444
  Login = async (
@@ -409,111 +448,102 @@ class ContensisCli {
409
448
  promptPassword = true,
410
449
  sharedSecret = isSharedSecret(this.env.passwordFallback),
411
450
  silent = false,
451
+ attempt = 1,
412
452
  }: {
413
453
  password?: string;
414
454
  promptPassword?: boolean;
415
455
  sharedSecret?: string;
416
456
  silent?: boolean;
417
- }
457
+ attempt?: number;
458
+ } = {}
418
459
  ): Promise<string | undefined> => {
419
- let inputPassword = password;
460
+ let inputPassword = password || sharedSecret;
420
461
  const { log, messages } = this;
421
462
 
422
463
  if (userId) {
423
- const { cache, currentEnv, env } = this;
464
+ const { currentEnv, env } = this;
424
465
 
425
466
  if (currentEnv) {
426
- const [credentialError, credentials] = await new CredentialProvider(
427
- { userId, alias: currentEnv },
428
- inputPassword || sharedSecret
429
- ).Init();
430
-
431
- if (credentialError && !credentials.current) {
432
- // Log problem with Credential Provider
433
- log.error(credentialError as any);
434
- return;
435
- }
436
-
437
- if (credentials.remarks.secure !== true)
438
- log.warning(messages.login.insecurePassword());
439
-
440
- const cachedPassword = isPassword(credentials?.current?.password);
441
- const cachedSecret = isSharedSecret(credentials?.current?.password);
442
-
443
- if (
444
- !sharedSecret &&
445
- !inputPassword &&
446
- !cachedPassword &&
447
- !cachedSecret &&
448
- promptPassword
449
- ) {
450
- // Password prompt
451
- ({ inputPassword } = await inquirer.prompt([
452
- {
453
- type: 'password',
454
- message: messages.login.passwordPrompt(currentEnv, userId),
455
- name: 'inputPassword',
456
- mask: '*',
457
- prefix: undefined,
458
- },
459
- ]));
460
- }
467
+ const credentials = await this.GetCredentials(userId, inputPassword);
468
+
469
+ if (credentials) {
470
+ const cachedPassword = isPassword(credentials.current?.password);
471
+ const cachedSecret = isSharedSecret(credentials.current?.password);
472
+
473
+ if (!cachedPassword && !cachedSecret && promptPassword) {
474
+ // Password prompt
475
+ ({ inputPassword } = await inquirer.prompt([
476
+ {
477
+ type: 'password',
478
+ message: messages.login.passwordPrompt(currentEnv, userId),
479
+ name: 'inputPassword',
480
+ mask: '*',
481
+ prefix: undefined,
482
+ },
483
+ ]));
484
+ }
461
485
 
462
- if (sharedSecret || cachedSecret || inputPassword || cachedPassword) {
463
- const authService = new ContensisAuthService({
464
- username: userId,
465
- password: inputPassword || cachedPassword,
466
- projectId: env?.currentProject || 'website',
467
- rootUrl: this.urls?.cms || '',
468
- clientId: userId,
469
- clientSecret: sharedSecret || cachedSecret,
470
- });
486
+ if (inputPassword || cachedPassword || cachedSecret) {
487
+ const authService = new ContensisAuthService({
488
+ username: userId,
489
+ password: inputPassword || cachedPassword,
490
+ projectId: env?.currentProject || 'website',
491
+ rootUrl: this.urls?.cms || '',
492
+ clientId: userId,
493
+ clientSecret: sharedSecret || cachedSecret,
494
+ });
495
+
496
+ const [authError, bearerToken] = await to(
497
+ authService.BearerToken()
498
+ );
471
499
 
472
- const [authError, bearerToken] = await to(authService.BearerToken());
473
-
474
- // Login successful
475
- if (bearerToken) {
476
- // Set env vars
477
- env.authToken = bearerToken;
478
- env.lastUserId = userId;
479
- env.passwordFallback =
480
- credentials.remarks.secure !== true
481
- ? credentials.current?.password
482
- : undefined;
483
-
484
- if (!silent) {
485
- Logger.success(messages.login.success(currentEnv, userId));
486
- await this.PrintProjects();
500
+ // Login successful
501
+ if (bearerToken) {
502
+ // Set env vars
503
+ env.authToken = bearerToken;
504
+ env.lastUserId = userId;
505
+ env.passwordFallback =
506
+ credentials.remarks.secure !== true
507
+ ? credentials.current?.password
508
+ : undefined;
509
+
510
+ // Persist env before finding projects or doing anything else
511
+ this.session.UpdateEnv(env);
512
+ if (inputPassword) await credentials.Save(inputPassword);
513
+ if (sharedSecret) await credentials.Save(sharedSecret);
514
+
515
+ if (!silent) {
516
+ Logger.success(messages.login.success(currentEnv, userId));
517
+ await this.PrintProjects();
518
+ }
519
+ } else if (authError) {
520
+ Logger.error(authError.toString());
521
+ // Clear env vars
522
+ env.authToken = '';
523
+ env.lastUserId = '';
524
+ env.passwordFallback = undefined;
525
+ // Persist env to remove cleared values
526
+ this.session.UpdateEnv(env);
527
+
528
+ // If the auth error was raised using a cached password
529
+ if (
530
+ (cachedPassword || cachedSecret) &&
531
+ credentials.remarks.secure
532
+ ) {
533
+ // Remove any bad stored credential and trigger login prompt again
534
+ await credentials.Delete();
535
+ return await this.Login(userId, { password, sharedSecret });
536
+ } else {
537
+ throw new Error(messages.login.failed(currentEnv, userId));
538
+ }
487
539
  }
488
- if (inputPassword) await credentials.Save(inputPassword);
489
- if (sharedSecret) await credentials.Save(sharedSecret);
490
- } else if (authError) {
491
- Logger.error(authError.toString());
492
- // Clear env vars
493
- env.authToken = '';
494
- env.lastUserId = '';
495
- env.passwordFallback = undefined;
496
-
497
- // If the auth error was raised using a cached password
498
- if (
499
- (cachedPassword || cachedSecret) &&
500
- credentials.remarks.secure
501
- ) {
502
- // Remove any bad stored credential and trigger login prompt again
503
- await credentials.Delete();
504
- return await this.Login(userId, { password, sharedSecret });
505
- } else {
506
- throw new Error(messages.login.failed(currentEnv, userId));
507
- }
508
- }
509
540
 
510
- // Persist env
511
- this.session.Update({
512
- environments: cache.environments,
513
- });
514
- return env.authToken;
515
- } else {
516
- Logger.error(messages.login.passwordPrompt(currentEnv, userId));
541
+ return env.authToken;
542
+ } else {
543
+ Logger.error(messages.login.passwordPrompt());
544
+ if (attempt < 2)
545
+ return await this.Login(userId, { attempt: attempt + 1 });
546
+ }
517
547
  }
518
548
  } else {
519
549
  // No environment set, use `contensis connect {alias}` first
@@ -525,19 +555,42 @@ class ContensisCli {
525
555
  }
526
556
  };
527
557
 
558
+ PrintContensisVersion = async () => {
559
+ const { log, messages } = this;
560
+ const contensis = await this.ConnectContensis();
561
+
562
+ if (contensis) {
563
+ // Retrieve projects list for env
564
+ const [projectsErr, projects] = await to(
565
+ contensis.projects.GetSourceProjects()
566
+ );
567
+
568
+ if (Array.isArray(projects)) {
569
+ // Print contensis version to console
570
+ this.HandleFormattingAndOutput(contensis.contensisVersion, () =>
571
+ log.raw(log.highlightText(contensis.contensisVersion))
572
+ );
573
+ }
574
+
575
+ if (projectsErr) {
576
+ log.error(messages.projects.noList());
577
+ log.error(projectsErr.message);
578
+ }
579
+ }
580
+ };
581
+
528
582
  PrintProjects = async () => {
529
- const { cache, currentEnv, currentProject, log, messages, session } = this;
530
- if (!this.contensis) await this.ConnectContensis();
583
+ const { currentProject, log, messages, session } = this;
584
+ const contensis = await this.ConnectContensis();
531
585
 
532
- if (this.contensis) {
586
+ if (contensis) {
533
587
  // Retrieve projects list for env
534
588
  const [projectsErr, projects] = await to(
535
- this.contensis.projects.GetSourceProjects()
589
+ contensis.projects.GetSourceProjects()
536
590
  );
537
591
 
538
592
  if (Array.isArray(projects)) {
539
593
  // save these projects in cache
540
- const currentVals = cache.environments[currentEnv] || {};
541
594
  const nextCurrentProject =
542
595
  currentProject && currentProject !== 'null'
543
596
  ? currentProject
@@ -545,46 +598,80 @@ class ContensisCli {
545
598
  ? 'website'
546
599
  : undefined;
547
600
 
548
- cache.environments[currentEnv] = {
549
- ...currentVals,
601
+ session.UpdateEnv({
550
602
  projects: projects.map(p => p.id),
551
603
  currentProject: nextCurrentProject,
552
- };
604
+ });
553
605
 
554
606
  log.success(messages.projects.list());
607
+ log.raw('');
608
+
555
609
  this.HandleFormattingAndOutput(projects, () => {
556
610
  // print the projects to console
557
- for (const project of projects) {
611
+ for (const project of projects.sort((a, b) =>
612
+ a.id.localeCompare(b.id)
613
+ )) {
614
+ let color;
615
+ try {
616
+ color = chalk.keyword((project as any).color);
617
+ } catch (ex) {
618
+ color = chalk.white;
619
+ }
558
620
  console.log(
559
- ` - ${nextCurrentProject === project.id ? '* ' : ''}[${
560
- project.primaryLanguage
561
- }] ${project.id}`
621
+ `${
622
+ nextCurrentProject === project.id
623
+ ? `>> ${log.boldText(color(project.id))}`
624
+ : ` ${color(project.id)}`
625
+ } ${log.infoText(
626
+ `[${project.supportedLanguages
627
+ .map(l =>
628
+ l === project.primaryLanguage ? `*${log.boldText(l)}` : l
629
+ )
630
+ .join(' ')}]`
631
+ )}`
562
632
  );
563
633
  }
564
634
  });
565
635
 
566
- session.Update({
567
- environments: cache.environments,
568
- });
636
+ if (!this.SetProject(nextCurrentProject))
637
+ log.warning(messages.projects.tip());
638
+ }
569
639
 
570
- if (nextCurrentProject) {
571
- this.env = cache.environments[currentEnv];
572
- this.SetProject(nextCurrentProject);
573
- }
640
+ if (projectsErr) {
641
+ log.error(messages.projects.noList());
642
+ log.error(projectsErr.message);
643
+ }
644
+ }
645
+ };
646
+
647
+ PrintProject = async (projectId = this.currentProject) => {
648
+ const { log, messages, session } = this;
649
+ const contensis = await this.ConnectContensis();
650
+
651
+ if (contensis) {
652
+ // Retrieve projects list for env
653
+ const [projectsErr, projects] = await to(
654
+ contensis.projects.GetSourceProjects()
655
+ );
656
+
657
+ const foundProject = projects?.find(
658
+ p => p.id.toLowerCase() === projectId.toLowerCase()
659
+ );
660
+
661
+ if (foundProject) {
662
+ log.raw('');
663
+ this.HandleFormattingAndOutput(foundProject, log.object);
574
664
  }
575
665
 
576
666
  if (projectsErr) {
577
667
  log.error(messages.projects.noList());
578
668
  log.error(projectsErr.message);
579
669
  }
580
- // } else {
581
- // log.warning(messages.projects.noList());
582
- // log.help(messages.connect.tip());
583
670
  }
584
671
  };
585
672
 
586
- SetProject = async (projectId = '') => {
587
- const { cache, env, log, messages, session } = this;
673
+ SetProject = (projectId = 'website') => {
674
+ const { env, log, messages, session } = this;
588
675
  let nextProjectId: string | undefined;
589
676
  if (env?.projects.length > 0 && env?.lastUserId) {
590
677
  nextProjectId = env.projects.find(
@@ -592,10 +679,9 @@ class ContensisCli {
592
679
  );
593
680
  if (nextProjectId) {
594
681
  env.currentProject = nextProjectId;
595
- session.Update({
596
- environments: cache.environments,
597
- });
682
+ session.UpdateEnv(env);
598
683
  log.success(messages.projects.set(projectId));
684
+ log.raw('');
599
685
  } else {
600
686
  log.error(messages.projects.failedSet(projectId));
601
687
  }
@@ -607,8 +693,8 @@ class ContensisCli {
607
693
  return nextProjectId;
608
694
  };
609
695
 
610
- SetVersion = async (versionStatus: 'latest' | 'published') => {
611
- const { cache, env, log, messages, session } = this;
696
+ SetVersion = (versionStatus: 'latest' | 'published') => {
697
+ const { env, log, messages, session } = this;
612
698
  if (!['latest', 'published'].includes(versionStatus)) {
613
699
  log.error(messages.version.invalid(versionStatus));
614
700
  return false;
@@ -618,10 +704,7 @@ class ContensisCli {
618
704
  return false;
619
705
  }
620
706
  if (env?.projects.length > 0 && env?.lastUserId) {
621
- env.versionStatus = versionStatus;
622
- session.Update({
623
- environments: cache.environments,
624
- });
707
+ session.UpdateEnv({ versionStatus });
625
708
  log.success(messages.version.set(this.currentEnv, versionStatus));
626
709
  return true;
627
710
  } else {
@@ -634,12 +717,12 @@ class ContensisCli {
634
717
 
635
718
  HydrateContensis = async () => {
636
719
  const { log } = this;
637
- if (!this.contensis) await this.ConnectContensis();
720
+ const contensis = await this.ConnectContensis();
638
721
 
639
- if (this.contensis) {
722
+ if (contensis) {
640
723
  // Retrieve content types list for env
641
724
  const [contensisErr, models] = await to(
642
- this.contensis.models.HydrateContensisRepositories()
725
+ contensis.models.HydrateContensisRepositories()
643
726
  );
644
727
 
645
728
  if (contensisErr) {
@@ -651,11 +734,11 @@ class ContensisCli {
651
734
 
652
735
  PrintApiKeys = async () => {
653
736
  const { currentEnv, log, messages } = this;
654
- if (!this.contensis) await this.ConnectContensis();
737
+ const contensis = await this.ConnectContensis();
655
738
 
656
- if (this.contensis) {
739
+ if (contensis) {
657
740
  // Retrieve keys list for env
658
- const [keysErr, apiKeys] = await this.contensis.apiKeys.GetKeys();
741
+ const [keysErr, apiKeys] = await contensis.apiKeys.GetKeys();
659
742
 
660
743
  if (Array.isArray(apiKeys)) {
661
744
  log.success(messages.keys.list(currentEnv));
@@ -689,13 +772,10 @@ class ContensisCli {
689
772
 
690
773
  CreateApiKey = async (name: string, description?: string) => {
691
774
  const { currentEnv, log, messages } = this;
692
- if (!this.contensis) await this.ConnectContensis();
775
+ const contensis = await this.ConnectContensis();
693
776
 
694
- if (this.contensis) {
695
- const [err, key] = await this.contensis.apiKeys.CreateKey(
696
- name,
697
- description
698
- );
777
+ if (contensis) {
778
+ const [err, key] = await contensis.apiKeys.CreateKey(name, description);
699
779
 
700
780
  if (key) {
701
781
  log.success(messages.keys.created(currentEnv, name));
@@ -719,10 +799,10 @@ class ContensisCli {
719
799
 
720
800
  RemoveApiKey = async (id: string) => {
721
801
  const { currentEnv, log, messages } = this;
722
- if (!this.contensis) await this.ConnectContensis({ commit: true });
802
+ const contensis = await this.ConnectContensis({ commit: true });
723
803
 
724
- if (this.contensis) {
725
- const [err, key] = await this.contensis.apiKeys.RemoveKey(id);
804
+ if (contensis) {
805
+ const [err, key] = await contensis.apiKeys.RemoveKey(id);
726
806
 
727
807
  if (!err) {
728
808
  log.success(messages.keys.removed(currentEnv, id));
@@ -733,17 +813,221 @@ class ContensisCli {
733
813
  }
734
814
  };
735
815
 
816
+ CreateProject = async (project: Project) => {
817
+ const { currentEnv, log, messages } = this;
818
+ const contensis = await this.ConnectContensis();
819
+
820
+ if (contensis) {
821
+ const [err, created] = await contensis.projects.CreateProject(project);
822
+
823
+ if (created) {
824
+ log.success(messages.projects.created(currentEnv, project.id));
825
+
826
+ this.HandleFormattingAndOutput(created, () => {
827
+ // set the CLI project to the newly created project
828
+ this.SetProject(project.id);
829
+ // print all the projects to console
830
+ this.PrintProjects();
831
+ });
832
+ return project.id;
833
+ }
834
+
835
+ if (err) {
836
+ log.error(messages.projects.failedCreate(currentEnv, project.id), err);
837
+ }
838
+ }
839
+ };
840
+
841
+ UpdateProject = async (project: Partial<Project>) => {
842
+ const { currentEnv, currentProject, log, messages } = this;
843
+ const contensis = await this.ConnectContensis();
844
+
845
+ if (contensis) {
846
+ const [err, updated] = await contensis.projects.UpdateProject({
847
+ id: currentProject,
848
+ ...project,
849
+ });
850
+
851
+ if (updated) {
852
+ log.success(messages.projects.updated(currentEnv, currentProject));
853
+
854
+ this.HandleFormattingAndOutput(updated, log.object);
855
+ return updated.id;
856
+ }
857
+
858
+ if (err) {
859
+ log.error(
860
+ messages.projects.failedUpdate(currentEnv, currentProject),
861
+ err
862
+ );
863
+ }
864
+ }
865
+ };
866
+
736
867
  GetContentTypes = async () => {
737
868
  const { currentProject, log, messages } = this;
738
869
  let err;
739
870
  if (!this.contensis) err = await this.HydrateContensis();
740
871
 
741
- if (err) log.error(messages.contenttypes.noList(currentProject));
742
- if (this.contensis) {
743
- this.contentTypes = this.contensis.models.contentTypes();
744
- this.components = this.contensis.models.components();
872
+ if (err) log.error(messages.models.noList(currentProject));
873
+ if (!this.contensis) log.warning(messages.models.noList(currentProject));
874
+
875
+ return this.contensis;
876
+ };
877
+
878
+ PrintContentModels = async (modelIds: string[] = []) => {
879
+ const { currentProject, log, messages } = this;
880
+ const contensis = await this.GetContentTypes();
881
+ if (contensis) {
882
+ // Retrieve models list for env
883
+ const { models, contentTypes = [], components = [] } = this;
884
+
885
+ // Models to output to console
886
+ const returnModels = modelIds?.length
887
+ ? models?.filter((m: Model) =>
888
+ modelIds.some(id => id.toLowerCase() === m.id.toLowerCase())
889
+ )
890
+ : undefined;
891
+
892
+ // Generate a list of contentTypeIds and componentIds from all models
893
+ // and dependencies
894
+ const contentTypeIds = Array.from(
895
+ new Set([
896
+ ...(returnModels || models || []).map(m => m.id),
897
+ ...(returnModels || models || [])
898
+ .map(m => m.dependencies?.contentTypes?.map(c => c[0]) || [])
899
+ .flat(),
900
+ ])
901
+ );
902
+ const componentIds = Array.from(
903
+ new Set(
904
+ (returnModels || models || [])
905
+ .map(m => m.dependencies?.components?.map(c => c[0]) || [])
906
+ .flat()
907
+ )
908
+ );
909
+
910
+ // Create an array of all the content types and component definitions
911
+ // we will use this when outputting to a file
912
+ const contentModelBackup = [
913
+ ...contentTypes.filter(c => contentTypeIds.includes(c.id)),
914
+ ...components.filter(c => componentIds.includes(c.id)),
915
+ ];
916
+
917
+ if (Array.isArray(returnModels)) {
918
+ log.success(messages.models.list(currentProject));
919
+ this.HandleFormattingAndOutput(contentModelBackup, () => {
920
+ // print the content models to console
921
+ for (const model of returnModels) {
922
+ log.raw('');
923
+ log.object(model);
924
+ }
925
+ log.raw('');
926
+ });
927
+ } else {
928
+ log.success(
929
+ messages.models.get(currentProject, models?.length.toString() || '0')
930
+ );
931
+ log.raw('');
932
+ if (models?.length) {
933
+ this.HandleFormattingAndOutput(contentModelBackup, () => {
934
+ // print the content models s#qto console
935
+ for (const model of models) {
936
+ const components = model.components?.length || 0;
937
+ const contentTypes = model.contentTypes?.length || 0;
938
+ const dependencies =
939
+ (model.dependencies?.components?.length || 0) +
940
+ (model.dependencies?.contentTypes?.length || 0);
941
+ const dependencyOf =
942
+ (model.dependencyOf?.components?.length || 0) +
943
+ (model.dependencyOf?.contentTypes?.length || 0);
944
+
945
+ const hasAny =
946
+ components + contentTypes + dependencies + dependencyOf;
947
+ log.raw(
948
+ ` - ${log.highlightText(log.boldText(model.id))} ${
949
+ hasAny
950
+ ? log.infoText(
951
+ `{ ${components ? `components: ${components}, ` : ''}${
952
+ contentTypes ? `contentTypes: ${contentTypes}, ` : ''
953
+ }${
954
+ dependencies ? `references: ${dependencies}, ` : ''
955
+ }${
956
+ dependencyOf ? `required by: ${dependencyOf}` : ''
957
+ } }`
958
+ )
959
+ : ''
960
+ }`
961
+ );
962
+ }
963
+ log.raw('');
964
+ });
965
+ }
966
+ }
967
+ }
968
+ };
969
+
970
+ ImportContentModels = async ({
971
+ commit,
972
+ fromFile,
973
+ }: {
974
+ commit: boolean;
975
+ fromFile: string;
976
+ }) => {
977
+ const { currentProject, log, messages } = this;
978
+
979
+ const fileData = fromFile
980
+ ? readJsonFile<(ContentType | Component)[]>(fromFile) || []
981
+ : [];
982
+ if (typeof fileData === 'string')
983
+ throw new Error(`Import file format must be of type JSON`);
984
+
985
+ const contensis = await this.ConnectContensisImport({
986
+ commit,
987
+ fromFile,
988
+ importDataType: 'models',
989
+ });
990
+
991
+ if (contensis) {
992
+ log.line();
993
+ if (contensis.isPreview) {
994
+ console.log(log.successText(` -- IMPORT PREVIEW -- `));
995
+ } else {
996
+ console.log(log.warningText(` *** COMMITTING IMPORT *** `));
997
+ }
998
+
999
+ const [migrateErr, result] = await contensis.MigrateContentModels();
1000
+
1001
+ if (migrateErr) logError(migrateErr);
1002
+ else
1003
+ this.HandleFormattingAndOutput(result, () => {
1004
+ // print the results to console
1005
+ if (!commit) {
1006
+ log.raw(log.boldText(`\nContent types:`));
1007
+ if (!result.contentTypes) log.info(`- None returned\n`);
1008
+ else printModelMigrationAnalysis(this, result.contentTypes);
1009
+
1010
+ log.raw(log.boldText(`\nComponents:`));
1011
+ if (!result.components) log.info(`- None returned\n`);
1012
+ else printModelMigrationAnalysis(this, result.components);
1013
+ } else {
1014
+ const migrateResult = result as MigrateModelsResult;
1015
+ log.raw(log.boldText(`\nContent types:`));
1016
+ printModelMigrationResult(
1017
+ this,
1018
+ migrateResult[currentProject].contentTypes
1019
+ );
1020
+
1021
+ log.raw(log.boldText(`\nComponents:`));
1022
+ printModelMigrationResult(
1023
+ this,
1024
+ migrateResult[currentProject].components
1025
+ );
1026
+ }
1027
+ });
745
1028
  } else {
746
- log.warning(messages.contenttypes.noList(currentProject));
1029
+ log.warning(messages.models.noList(currentProject));
1030
+ log.help(messages.connect.tip());
747
1031
  }
748
1032
  };
749
1033
 
@@ -799,15 +1083,12 @@ class ContensisCli {
799
1083
 
800
1084
  RemoveContentTypes = async (contentTypeIds: string[], commit = false) => {
801
1085
  const { currentProject, log, messages } = this;
802
- if (!this.contensis)
803
- await this.ConnectContensisImport({
804
- source: 'input',
805
- commit,
806
- });
807
- if (this.contensis) {
808
- const [err, result] = await this.contensis.DeleteContentTypes(
809
- contentTypeIds
810
- );
1086
+ const contensis = await this.ConnectContensisImport({
1087
+ commit,
1088
+ importDataType: 'user-input',
1089
+ });
1090
+ if (contensis) {
1091
+ const [err, result] = await contensis.DeleteContentTypes(contentTypeIds);
811
1092
 
812
1093
  if (err) {
813
1094
  log.error(
@@ -822,7 +1103,7 @@ class ContensisCli {
822
1103
  messages.contenttypes.removed(
823
1104
  currentProject,
824
1105
  contentTypeIds.join('", "'),
825
- !this.contensis.isPreview
1106
+ !contensis.isPreview
826
1107
  )
827
1108
  );
828
1109
  // print the results to console
@@ -851,22 +1132,21 @@ class ContensisCli {
851
1132
 
852
1133
  if (!Array.isArray(fileData)) fileData = [fileData];
853
1134
 
854
- await this.ConnectContensisImport({
1135
+ const contensis = await this.ConnectContensisImport({
855
1136
  commit,
856
- source: fromFile ? 'file' : 'contensis',
1137
+ importDataType: fromFile ? 'user-input' : undefined,
857
1138
  });
858
1139
 
859
- if (this.contensis) {
1140
+ if (contensis) {
860
1141
  // Pass each content type to the target repo
861
1142
  for (const contentType of fileData) {
862
1143
  // Fix invalid data
863
1144
  contentType.projectId = currentProject;
864
1145
  delete contentType.uuid;
865
1146
 
866
- const [err, created, createStatus] =
867
- await this.contensis.models.targetRepos[
868
- currentProject
869
- ].repo.UpsertContentType(false, contentType);
1147
+ const [err, created, createStatus] = await contensis.models.targetRepos[
1148
+ currentProject
1149
+ ].repo.UpsertContentType(false, contentType);
870
1150
 
871
1151
  if (err) log.error(err.message, err);
872
1152
  if (createStatus) {
@@ -884,6 +1164,53 @@ class ContensisCli {
884
1164
  }
885
1165
  };
886
1166
 
1167
+ DiffModels = async (
1168
+ {
1169
+ fromFile,
1170
+ }: {
1171
+ fromFile: string;
1172
+ },
1173
+ modelIds: string[] = []
1174
+ ) => {
1175
+ const { log } = this;
1176
+
1177
+ let fileData = fromFile ? readJsonFile<ContentType[]>(fromFile) || [] : [];
1178
+ if (typeof fileData === 'string')
1179
+ throw new Error(`Import file format must be of type JSON`);
1180
+
1181
+ if (!Array.isArray(fileData)) fileData = [fileData];
1182
+
1183
+ const contensis = await this.ConnectContensisImport({
1184
+ fromFile,
1185
+ importDataType: 'models',
1186
+ });
1187
+
1188
+ if (contensis) {
1189
+ const [err, result] = (await to(
1190
+ contensis.models.Diff(fileData.length ? fileData : modelIds)
1191
+ )) as [Error | null, ContentTypesResult | undefined];
1192
+
1193
+ if (err) log.error(err.message, err);
1194
+ if (result)
1195
+ // print the content type to console
1196
+ this.HandleFormattingAndOutput(result, () => {
1197
+ log.success(
1198
+ `Queried models ${log.infoText(
1199
+ `"${result.query.modelIds?.join(', ')}"`
1200
+ )}\n`
1201
+ );
1202
+
1203
+ log.raw(log.boldText(`Content types:`));
1204
+ if (!result.contentTypes) log.info(`- None returned\n`);
1205
+ else printModelMigrationAnalysis(this, result.contentTypes);
1206
+
1207
+ log.raw(log.boldText(`Components:`));
1208
+ if (!result.components) log.info(`- None returned\n`);
1209
+ else printModelMigrationAnalysis(this, result.components);
1210
+ });
1211
+ }
1212
+ };
1213
+
887
1214
  PrintComponents = async () => {
888
1215
  const { currentProject, log, messages } = this;
889
1216
  await this.GetContentTypes();
@@ -933,13 +1260,12 @@ class ContensisCli {
933
1260
 
934
1261
  RemoveComponents = async (componentIds: string[], commit = false) => {
935
1262
  const { currentProject, log, messages } = this;
936
- if (!this.contensis)
937
- await this.ConnectContensisImport({
938
- source: 'input',
939
- commit,
940
- });
941
- if (this.contensis) {
942
- const [err, result] = await this.contensis.DeleteContentTypes(
1263
+ const contensis = await this.ConnectContensisImport({
1264
+ commit,
1265
+ importDataType: 'user-input',
1266
+ });
1267
+ if (contensis) {
1268
+ const [err, result] = await contensis.DeleteContentTypes(
943
1269
  undefined,
944
1270
  componentIds
945
1271
  );
@@ -957,7 +1283,7 @@ class ContensisCli {
957
1283
  messages.components.removed(
958
1284
  currentProject,
959
1285
  componentIds.join('", "'),
960
- !this.contensis.isPreview
1286
+ !contensis.isPreview
961
1287
  )
962
1288
  );
963
1289
  // print the results to console
@@ -986,22 +1312,21 @@ class ContensisCli {
986
1312
 
987
1313
  if (!Array.isArray(fileData)) fileData = [fileData];
988
1314
 
989
- await this.ConnectContensisImport({
1315
+ const contensis = await this.ConnectContensisImport({
990
1316
  commit,
991
- source: fromFile ? 'file' : 'contensis',
1317
+ importDataType: fromFile ? 'user-input' : undefined,
992
1318
  });
993
1319
 
994
- if (this.contensis) {
1320
+ if (contensis) {
995
1321
  // Pass each component to the target repo
996
1322
  for (const component of fileData) {
997
1323
  // Fix invalid data
998
1324
  component.projectId = currentProject;
999
1325
  delete component.uuid;
1000
1326
 
1001
- const [err, created, createStatus] =
1002
- await this.contensis.models.targetRepos[
1003
- currentProject
1004
- ].repo.UpsertComponent(false, component);
1327
+ const [err, created, createStatus] = await contensis.models.targetRepos[
1328
+ currentProject
1329
+ ].repo.UpsertComponent(false, component);
1005
1330
 
1006
1331
  if (err) log.error(err.message, err);
1007
1332
  if (createStatus) {
@@ -1019,21 +1344,20 @@ class ContensisCli {
1019
1344
  }
1020
1345
  };
1021
1346
 
1022
- RemoveEntry = async (id: string, commit = false) => {
1347
+ RemoveEntries = async (commit = false) => {
1023
1348
  const { currentEnv, log, messages } = this;
1024
- if (!this.contensis)
1025
- await this.ConnectContensisImport({
1026
- source: 'input',
1027
- commit,
1028
- });
1349
+ const contensis = await this.ConnectContensisImport({
1350
+ commit,
1351
+ importDataType: 'user-input',
1352
+ });
1029
1353
 
1030
- if (this.contensis) {
1031
- if (this.contensis.isPreview) {
1354
+ if (contensis) {
1355
+ if (contensis.isPreview) {
1032
1356
  console.log(log.successText(` -- PREVIEW -- `));
1033
1357
  } else {
1034
1358
  console.log(log.warningText(` *** COMMITTING DELETE *** `));
1035
1359
  }
1036
- const [err, result] = await this.contensis.DeleteEntries();
1360
+ const [err, result] = await contensis.DeleteEntries();
1037
1361
  if (result)
1038
1362
  this.HandleFormattingAndOutput(result, () => {
1039
1363
  // print the migrateResult to console
@@ -1045,12 +1369,12 @@ class ContensisCli {
1045
1369
  Object.values(result.entriesToMigrate)?.[0].totalCount > 0) ||
1046
1370
  (commit && result.migrateResult?.deleted))
1047
1371
  ) {
1048
- log.success(messages.entries.removed(currentEnv, id, commit));
1372
+ log.success(messages.entries.removed(currentEnv, commit));
1049
1373
  if (!commit) log.help(messages.entries.commitTip());
1050
1374
  } else {
1051
- log.error(messages.entries.failedRemove(currentEnv, id), err);
1375
+ log.error(messages.entries.failedRemove(currentEnv), err);
1052
1376
  if (!Object.values(result.entriesToMigrate)?.[0].totalCount)
1053
- log.help(messages.entries.notFound(id));
1377
+ log.help(messages.entries.notFound(currentEnv));
1054
1378
  }
1055
1379
  }
1056
1380
  };
@@ -1061,21 +1385,21 @@ class ContensisCli {
1061
1385
  withDependents?: boolean;
1062
1386
  }) => {
1063
1387
  const { currentProject, log, messages } = this;
1064
- await this.ConnectContensis();
1388
+ const contensis = await this.ConnectContensis();
1065
1389
 
1066
- if (this.contensis) {
1390
+ if (contensis) {
1067
1391
  log.line();
1068
- const entries = await this.contensis.GetEntries({ withDependents });
1392
+ const entries = await contensis.GetEntries({ withDependents });
1069
1393
  this.HandleFormattingAndOutput(entries, () =>
1070
1394
  // print the entries to console
1071
1395
  logEntriesTable(
1072
1396
  entries,
1073
1397
  currentProject,
1074
- this.contensis?.payload.query?.fields
1398
+ contensis.payload.query?.fields
1075
1399
  )
1076
1400
  );
1077
1401
  } else {
1078
- log.warning(messages.contenttypes.noList(currentProject));
1402
+ log.warning(messages.models.noList(currentProject));
1079
1403
  log.help(messages.connect.tip());
1080
1404
  }
1081
1405
  };
@@ -1089,26 +1413,21 @@ class ContensisCli {
1089
1413
  }) => {
1090
1414
  const { currentProject, log, messages } = this;
1091
1415
 
1092
- const fileData = fromFile ? readJsonFile<Entry[]>(fromFile) || [] : [];
1093
- if (typeof fileData === 'string')
1094
- throw new Error(`Import file format must be of type JSON`);
1095
-
1096
- await this.ConnectContensisImport({
1416
+ const contensis = await this.ConnectContensisImport({
1097
1417
  commit,
1098
- source: fromFile ? 'file' : 'contensis',
1099
- fileData,
1100
- fileDataType: 'entries',
1418
+ fromFile,
1419
+ importDataType: 'entries',
1101
1420
  });
1102
1421
 
1103
- if (this.contensis) {
1422
+ if (contensis) {
1104
1423
  log.line();
1105
- if (this.contensis.isPreview) {
1424
+ if (contensis.isPreview) {
1106
1425
  console.log(log.successText(` -- IMPORT PREVIEW -- `));
1107
1426
  } else {
1108
1427
  console.log(log.warningText(` *** COMMITTING IMPORT *** `));
1109
1428
  }
1110
1429
 
1111
- const [migrateErr, migrateResult] = await this.contensis.MigrateEntries();
1430
+ const [migrateErr, migrateResult] = await contensis.MigrateEntries();
1112
1431
 
1113
1432
  if (migrateErr) logError(migrateErr);
1114
1433
  else
@@ -1117,7 +1436,7 @@ class ContensisCli {
1117
1436
  printMigrateResult(this, migrateResult);
1118
1437
  });
1119
1438
  } else {
1120
- log.warning(messages.contenttypes.noList(currentProject));
1439
+ log.warning(messages.models.noList(currentProject));
1121
1440
  log.help(messages.connect.tip());
1122
1441
  }
1123
1442
  };
@@ -1127,19 +1446,19 @@ class ContensisCli {
1127
1446
  name?: string
1128
1447
  ) => {
1129
1448
  const { currentEnv, log, messages } = this;
1130
- if (!this.contensis) await this.ConnectContensis();
1131
- if (this.contensis) {
1449
+ const contensis = await this.ConnectContensis();
1450
+ if (contensis) {
1132
1451
  // Retrieve webhooks list for env
1133
1452
  const [webhooksErr, webhooks] =
1134
- await this.contensis.subscriptions.webhooks.GetSubscriptions();
1453
+ await contensis.subscriptions.webhooks.GetSubscriptions();
1135
1454
 
1136
1455
  const filteredResults =
1137
1456
  typeof name === 'string'
1138
- ? webhooks.filter(w =>
1457
+ ? webhooks?.filter(w =>
1139
1458
  w.name?.toLowerCase().includes(name.toLowerCase())
1140
1459
  )
1141
1460
  : Array.isArray(subscriptionIds)
1142
- ? webhooks.filter(w => subscriptionIds?.some(id => id === w.id))
1461
+ ? webhooks?.filter(w => subscriptionIds?.some(id => id === w.id))
1143
1462
  : webhooks;
1144
1463
 
1145
1464
  if (Array.isArray(filteredResults)) {
@@ -1176,16 +1495,16 @@ class ContensisCli {
1176
1495
  };
1177
1496
 
1178
1497
  PrintBlocks = async () => {
1179
- const { currentEnv, log, messages } = this;
1180
- if (!this.contensis) await this.ConnectContensis();
1181
- if (this.contensis) {
1498
+ const { currentEnv, env, log, messages } = this;
1499
+ const contensis = await this.ConnectContensis();
1500
+ if (contensis) {
1182
1501
  // Retrieve blocks list for env
1183
- const [err, blocks] = await this.contensis.blocks.GetBlocks();
1502
+ const [err, blocks] = await contensis.blocks.GetBlocks();
1184
1503
 
1185
1504
  if (Array.isArray(blocks)) {
1186
1505
  this.HandleFormattingAndOutput(blocks, () => {
1187
1506
  // print the blocks to console
1188
- log.success(messages.blocks.list(currentEnv));
1507
+ log.success(messages.blocks.list(currentEnv, env.currentProject));
1189
1508
  for (const {
1190
1509
  id,
1191
1510
  description,
@@ -1226,10 +1545,10 @@ class ContensisCli {
1226
1545
  version: string
1227
1546
  ) => {
1228
1547
  const { currentEnv, env, log, messages } = this;
1229
- if (!this.contensis) await this.ConnectContensis();
1230
- if (this.contensis) {
1548
+ const contensis = await this.ConnectContensis();
1549
+ if (contensis) {
1231
1550
  // Retrieve block version
1232
- const [err, blocks] = await this.contensis.blocks.GetBlockVersions(
1551
+ const [err, blocks] = await contensis.blocks.GetBlockVersions(
1233
1552
  blockId,
1234
1553
  branch,
1235
1554
  version
@@ -1239,7 +1558,7 @@ class ContensisCli {
1239
1558
  this.HandleFormattingAndOutput(blocks, () => {
1240
1559
  // print the version detail to console
1241
1560
  log.success(
1242
- messages.blocks.get(`${currentEnv}:${env.currentProject}`)
1561
+ messages.blocks.get(blockId, currentEnv, env.currentProject)
1243
1562
  );
1244
1563
  for (const block of blocks)
1245
1564
  printBlockVersion(
@@ -1268,18 +1587,20 @@ class ContensisCli {
1268
1587
  const { currentEnv, env, log, messages } = this;
1269
1588
 
1270
1589
  // Output request to console
1271
- messages.blocks.tryPush(
1272
- block.id,
1273
- block.source.branch,
1274
- currentEnv,
1275
- env.currentProject
1590
+ log.info(
1591
+ messages.blocks.tryPush(
1592
+ block.id,
1593
+ block.source.branch,
1594
+ currentEnv,
1595
+ env.currentProject
1596
+ )
1276
1597
  );
1277
1598
  console.log(jsonFormatter(block));
1278
1599
 
1279
- if (!this.contensis) await this.ConnectContensis();
1280
- if (this.contensis) {
1600
+ const contensis = await this.ConnectContensis();
1601
+ if (contensis) {
1281
1602
  // Push new block version
1282
- const [err, blockVersion] = await this.contensis.blocks.PushBlockVersion(
1603
+ const [err, blockVersion] = await contensis.blocks.PushBlockVersion(
1283
1604
  block
1284
1605
  );
1285
1606
  if (!err) {
@@ -1291,7 +1612,6 @@ class ContensisCli {
1291
1612
  env.currentProject
1292
1613
  )
1293
1614
  );
1294
- console.log(jsonFormatter(blockVersion));
1295
1615
  }
1296
1616
  if (blockVersion) {
1297
1617
  this.HandleFormattingAndOutput(blockVersion, () => {
@@ -1306,17 +1626,51 @@ class ContensisCli {
1306
1626
  }
1307
1627
  };
1308
1628
 
1629
+ ReleaseBlock = async (blockId: string, version: string) => {
1630
+ const { currentEnv, env, log, messages } = this;
1631
+ const contensis = await this.ConnectContensis();
1632
+ if (contensis) {
1633
+ // Retrieve block version
1634
+ const [err, blockVersion] = await contensis.blocks.BlockAction(
1635
+ blockId,
1636
+ 'release',
1637
+ version
1638
+ );
1639
+
1640
+ if (blockVersion) {
1641
+ this.HandleFormattingAndOutput(blockVersion, () => {
1642
+ // print the version detail to console
1643
+ log.success(
1644
+ messages.blocks.released(blockId, currentEnv, env.currentProject)
1645
+ );
1646
+ printBlockVersion(this, blockVersion);
1647
+ });
1648
+ }
1649
+
1650
+ if (err) {
1651
+ log.error(
1652
+ messages.blocks.failedRelease(blockId, currentEnv, env.currentProject)
1653
+ );
1654
+ log.error(jsonFormatter(err));
1655
+ }
1656
+ }
1657
+ };
1658
+
1309
1659
  PrintBlockLogs = async (
1310
1660
  blockId: string,
1311
1661
  branch: string,
1312
1662
  version: string,
1313
1663
  dataCenter: 'hq' | 'manchester' | 'london'
1314
1664
  ) => {
1315
- const { currentEnv, log, messages } = this;
1316
- if (!this.contensis) await this.ConnectContensis();
1317
- if (this.contensis) {
1665
+ const { currentEnv, env, log, messages } = this;
1666
+ const contensis = await this.ConnectContensis();
1667
+ if (contensis) {
1318
1668
  // Retrieve block logs
1319
- const [err, blockLogs] = await this.contensis.blocks.GetBlockLogs({
1669
+ log.success(
1670
+ messages.blocks.getLogs(blockId, branch, currentEnv, env.currentProject)
1671
+ );
1672
+
1673
+ const [err, blockLogs] = await contensis.blocks.GetBlockLogs({
1320
1674
  blockId,
1321
1675
  branchId: branch,
1322
1676
  version,
@@ -1325,8 +1679,7 @@ class ContensisCli {
1325
1679
 
1326
1680
  if (blockLogs) {
1327
1681
  this.HandleFormattingAndOutput(blockLogs, () => {
1328
- // print the logs to console
1329
- log.success(messages.blocks.list(currentEnv));
1682
+ // print the logs to console
1330
1683
  console.log(
1331
1684
  ` - ${blockId} ${branch} ${
1332
1685
  Number(version) ? `v${version}` : version
@@ -1339,7 +1692,9 @@ class ContensisCli {
1339
1692
  }
1340
1693
 
1341
1694
  if (err) {
1342
- log.error(messages.blocks.noList(currentEnv));
1695
+ log.error(
1696
+ messages.blocks.failedGetLogs(blockId, currentEnv, env.currentProject)
1697
+ );
1343
1698
  log.error(jsonFormatter(err));
1344
1699
  }
1345
1700
  }
@@ -1347,6 +1702,21 @@ class ContensisCli {
1347
1702
 
1348
1703
  HandleFormattingAndOutput = <T>(obj: T, logFn: (obj: T) => void) => {
1349
1704
  const { format, log, messages, output } = this;
1705
+ if (!format) {
1706
+ // print the object to console
1707
+ logFn(obj);
1708
+ } else if (format === 'csv') {
1709
+ log.raw('');
1710
+ log.raw(log.infoText(csvFormatter(obj)));
1711
+ } else if (format === 'xml') {
1712
+ log.raw('');
1713
+ log.raw(log.infoText(xmlFormatter(obj)));
1714
+ } else if (format === 'json') {
1715
+ log.raw('');
1716
+ log.raw(log.infoText(jsonFormatter(obj)));
1717
+ }
1718
+ log.raw('');
1719
+
1350
1720
  if (output) {
1351
1721
  let writeString = '';
1352
1722
  if (format === 'csv') {
@@ -1361,21 +1731,6 @@ class ContensisCli {
1361
1731
  } else {
1362
1732
  log.info(messages.app.noFileOutput());
1363
1733
  }
1364
- } else {
1365
- if (!format) {
1366
- // print the object to console
1367
- logFn(obj);
1368
- } else if (format === 'csv') {
1369
- log.raw('');
1370
- log.raw(log.infoText(csvFormatter(obj)));
1371
- } else if (format === 'xml') {
1372
- log.raw('');
1373
- log.raw(log.infoText(xmlFormatter(obj)));
1374
- } else if (format === 'json') {
1375
- log.raw('');
1376
- log.raw(log.infoText(jsonFormatter(obj)));
1377
- }
1378
- log.raw('');
1379
1734
  }
1380
1735
  };
1381
1736
  }