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

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 +741 -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,101 @@ 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
+ // Persist env before finding projects or doing anything else
510
+ this.session.UpdateEnv(env);
511
+
512
+ if (!silent) {
513
+ Logger.success(messages.login.success(currentEnv, userId));
514
+ await this.PrintProjects();
515
+ }
516
+ if (inputPassword) await credentials.Save(inputPassword);
517
+ if (sharedSecret) await credentials.Save(sharedSecret);
518
+ } else if (authError) {
519
+ Logger.error(authError.toString());
520
+ // Clear env vars
521
+ env.authToken = '';
522
+ env.lastUserId = '';
523
+ env.passwordFallback = undefined;
524
+ // Persist env to remove cleared values
525
+ this.session.UpdateEnv(env);
526
+
527
+ // If the auth error was raised using a cached password
528
+ if (
529
+ (cachedPassword || cachedSecret) &&
530
+ credentials.remarks.secure
531
+ ) {
532
+ // Remove any bad stored credential and trigger login prompt again
533
+ await credentials.Delete();
534
+ return await this.Login(userId, { password, sharedSecret });
535
+ } else {
536
+ throw new Error(messages.login.failed(currentEnv, userId));
537
+ }
487
538
  }
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
539
 
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));
540
+ return env.authToken;
541
+ } else {
542
+ Logger.error(messages.login.passwordPrompt());
543
+ if (attempt < 2)
544
+ return await this.Login(userId, { attempt: attempt + 1 });
545
+ }
517
546
  }
518
547
  } else {
519
548
  // No environment set, use `contensis connect {alias}` first
@@ -525,19 +554,42 @@ class ContensisCli {
525
554
  }
526
555
  };
527
556
 
557
+ PrintContensisVersion = async () => {
558
+ const { log, messages } = this;
559
+ const contensis = await this.ConnectContensis();
560
+
561
+ if (contensis) {
562
+ // Retrieve projects list for env
563
+ const [projectsErr, projects] = await to(
564
+ contensis.projects.GetSourceProjects()
565
+ );
566
+
567
+ if (Array.isArray(projects)) {
568
+ // Print contensis version to console
569
+ this.HandleFormattingAndOutput(contensis.contensisVersion, () =>
570
+ log.raw(log.highlightText(contensis.contensisVersion))
571
+ );
572
+ }
573
+
574
+ if (projectsErr) {
575
+ log.error(messages.projects.noList());
576
+ log.error(projectsErr.message);
577
+ }
578
+ }
579
+ };
580
+
528
581
  PrintProjects = async () => {
529
- const { cache, currentEnv, currentProject, log, messages, session } = this;
530
- if (!this.contensis) await this.ConnectContensis();
582
+ const { currentProject, log, messages, session } = this;
583
+ const contensis = await this.ConnectContensis();
531
584
 
532
- if (this.contensis) {
585
+ if (contensis) {
533
586
  // Retrieve projects list for env
534
587
  const [projectsErr, projects] = await to(
535
- this.contensis.projects.GetSourceProjects()
588
+ contensis.projects.GetSourceProjects()
536
589
  );
537
590
 
538
591
  if (Array.isArray(projects)) {
539
592
  // save these projects in cache
540
- const currentVals = cache.environments[currentEnv] || {};
541
593
  const nextCurrentProject =
542
594
  currentProject && currentProject !== 'null'
543
595
  ? currentProject
@@ -545,46 +597,80 @@ class ContensisCli {
545
597
  ? 'website'
546
598
  : undefined;
547
599
 
548
- cache.environments[currentEnv] = {
549
- ...currentVals,
600
+ session.UpdateEnv({
550
601
  projects: projects.map(p => p.id),
551
602
  currentProject: nextCurrentProject,
552
- };
603
+ });
553
604
 
554
605
  log.success(messages.projects.list());
606
+ log.raw('');
607
+
555
608
  this.HandleFormattingAndOutput(projects, () => {
556
609
  // print the projects to console
557
- for (const project of projects) {
610
+ for (const project of projects.sort((a, b) =>
611
+ a.id.localeCompare(b.id)
612
+ )) {
613
+ let color;
614
+ try {
615
+ color = chalk.keyword((project as any).color);
616
+ } catch (ex) {
617
+ color = chalk.white;
618
+ }
558
619
  console.log(
559
- ` - ${nextCurrentProject === project.id ? '* ' : ''}[${
560
- project.primaryLanguage
561
- }] ${project.id}`
620
+ `${
621
+ nextCurrentProject === project.id
622
+ ? `>> ${log.boldText(color(project.id))}`
623
+ : ` ${color(project.id)}`
624
+ } ${log.infoText(
625
+ `[${project.supportedLanguages
626
+ .map(l =>
627
+ l === project.primaryLanguage ? `*${log.boldText(l)}` : l
628
+ )
629
+ .join(' ')}]`
630
+ )}`
562
631
  );
563
632
  }
564
633
  });
565
634
 
566
- session.Update({
567
- environments: cache.environments,
568
- });
635
+ if (!this.SetProject(nextCurrentProject))
636
+ log.warning(messages.projects.tip());
637
+ }
569
638
 
570
- if (nextCurrentProject) {
571
- this.env = cache.environments[currentEnv];
572
- this.SetProject(nextCurrentProject);
573
- }
639
+ if (projectsErr) {
640
+ log.error(messages.projects.noList());
641
+ log.error(projectsErr.message);
642
+ }
643
+ }
644
+ };
645
+
646
+ PrintProject = async (projectId = this.currentProject) => {
647
+ const { log, messages, session } = this;
648
+ const contensis = await this.ConnectContensis();
649
+
650
+ if (contensis) {
651
+ // Retrieve projects list for env
652
+ const [projectsErr, projects] = await to(
653
+ contensis.projects.GetSourceProjects()
654
+ );
655
+
656
+ const foundProject = projects?.find(
657
+ p => p.id.toLowerCase() === projectId.toLowerCase()
658
+ );
659
+
660
+ if (foundProject) {
661
+ log.raw('');
662
+ this.HandleFormattingAndOutput(foundProject, log.object);
574
663
  }
575
664
 
576
665
  if (projectsErr) {
577
666
  log.error(messages.projects.noList());
578
667
  log.error(projectsErr.message);
579
668
  }
580
- // } else {
581
- // log.warning(messages.projects.noList());
582
- // log.help(messages.connect.tip());
583
669
  }
584
670
  };
585
671
 
586
- SetProject = async (projectId = '') => {
587
- const { cache, env, log, messages, session } = this;
672
+ SetProject = (projectId = 'website') => {
673
+ const { env, log, messages, session } = this;
588
674
  let nextProjectId: string | undefined;
589
675
  if (env?.projects.length > 0 && env?.lastUserId) {
590
676
  nextProjectId = env.projects.find(
@@ -592,10 +678,9 @@ class ContensisCli {
592
678
  );
593
679
  if (nextProjectId) {
594
680
  env.currentProject = nextProjectId;
595
- session.Update({
596
- environments: cache.environments,
597
- });
681
+ session.UpdateEnv(env);
598
682
  log.success(messages.projects.set(projectId));
683
+ log.raw('');
599
684
  } else {
600
685
  log.error(messages.projects.failedSet(projectId));
601
686
  }
@@ -607,8 +692,8 @@ class ContensisCli {
607
692
  return nextProjectId;
608
693
  };
609
694
 
610
- SetVersion = async (versionStatus: 'latest' | 'published') => {
611
- const { cache, env, log, messages, session } = this;
695
+ SetVersion = (versionStatus: 'latest' | 'published') => {
696
+ const { env, log, messages, session } = this;
612
697
  if (!['latest', 'published'].includes(versionStatus)) {
613
698
  log.error(messages.version.invalid(versionStatus));
614
699
  return false;
@@ -618,10 +703,7 @@ class ContensisCli {
618
703
  return false;
619
704
  }
620
705
  if (env?.projects.length > 0 && env?.lastUserId) {
621
- env.versionStatus = versionStatus;
622
- session.Update({
623
- environments: cache.environments,
624
- });
706
+ session.UpdateEnv({ versionStatus });
625
707
  log.success(messages.version.set(this.currentEnv, versionStatus));
626
708
  return true;
627
709
  } else {
@@ -634,12 +716,12 @@ class ContensisCli {
634
716
 
635
717
  HydrateContensis = async () => {
636
718
  const { log } = this;
637
- if (!this.contensis) await this.ConnectContensis();
719
+ const contensis = await this.ConnectContensis();
638
720
 
639
- if (this.contensis) {
721
+ if (contensis) {
640
722
  // Retrieve content types list for env
641
723
  const [contensisErr, models] = await to(
642
- this.contensis.models.HydrateContensisRepositories()
724
+ contensis.models.HydrateContensisRepositories()
643
725
  );
644
726
 
645
727
  if (contensisErr) {
@@ -651,11 +733,11 @@ class ContensisCli {
651
733
 
652
734
  PrintApiKeys = async () => {
653
735
  const { currentEnv, log, messages } = this;
654
- if (!this.contensis) await this.ConnectContensis();
736
+ const contensis = await this.ConnectContensis();
655
737
 
656
- if (this.contensis) {
738
+ if (contensis) {
657
739
  // Retrieve keys list for env
658
- const [keysErr, apiKeys] = await this.contensis.apiKeys.GetKeys();
740
+ const [keysErr, apiKeys] = await contensis.apiKeys.GetKeys();
659
741
 
660
742
  if (Array.isArray(apiKeys)) {
661
743
  log.success(messages.keys.list(currentEnv));
@@ -689,13 +771,10 @@ class ContensisCli {
689
771
 
690
772
  CreateApiKey = async (name: string, description?: string) => {
691
773
  const { currentEnv, log, messages } = this;
692
- if (!this.contensis) await this.ConnectContensis();
774
+ const contensis = await this.ConnectContensis();
693
775
 
694
- if (this.contensis) {
695
- const [err, key] = await this.contensis.apiKeys.CreateKey(
696
- name,
697
- description
698
- );
776
+ if (contensis) {
777
+ const [err, key] = await contensis.apiKeys.CreateKey(name, description);
699
778
 
700
779
  if (key) {
701
780
  log.success(messages.keys.created(currentEnv, name));
@@ -719,10 +798,10 @@ class ContensisCli {
719
798
 
720
799
  RemoveApiKey = async (id: string) => {
721
800
  const { currentEnv, log, messages } = this;
722
- if (!this.contensis) await this.ConnectContensis({ commit: true });
801
+ const contensis = await this.ConnectContensis({ commit: true });
723
802
 
724
- if (this.contensis) {
725
- const [err, key] = await this.contensis.apiKeys.RemoveKey(id);
803
+ if (contensis) {
804
+ const [err, key] = await contensis.apiKeys.RemoveKey(id);
726
805
 
727
806
  if (!err) {
728
807
  log.success(messages.keys.removed(currentEnv, id));
@@ -733,17 +812,221 @@ class ContensisCli {
733
812
  }
734
813
  };
735
814
 
815
+ CreateProject = async (project: Project) => {
816
+ const { currentEnv, log, messages } = this;
817
+ const contensis = await this.ConnectContensis();
818
+
819
+ if (contensis) {
820
+ const [err, created] = await contensis.projects.CreateProject(project);
821
+
822
+ if (created) {
823
+ log.success(messages.projects.created(currentEnv, project.id));
824
+
825
+ this.HandleFormattingAndOutput(created, () => {
826
+ // set the CLI project to the newly created project
827
+ this.SetProject(project.id);
828
+ // print all the projects to console
829
+ this.PrintProjects();
830
+ });
831
+ return project.id;
832
+ }
833
+
834
+ if (err) {
835
+ log.error(messages.projects.failedCreate(currentEnv, project.id), err);
836
+ }
837
+ }
838
+ };
839
+
840
+ UpdateProject = async (project: Partial<Project>) => {
841
+ const { currentEnv, currentProject, log, messages } = this;
842
+ const contensis = await this.ConnectContensis();
843
+
844
+ if (contensis) {
845
+ const [err, updated] = await contensis.projects.UpdateProject({
846
+ id: currentProject,
847
+ ...project,
848
+ });
849
+
850
+ if (updated) {
851
+ log.success(messages.projects.updated(currentEnv, currentProject));
852
+
853
+ this.HandleFormattingAndOutput(updated, log.object);
854
+ return updated.id;
855
+ }
856
+
857
+ if (err) {
858
+ log.error(
859
+ messages.projects.failedUpdate(currentEnv, currentProject),
860
+ err
861
+ );
862
+ }
863
+ }
864
+ };
865
+
736
866
  GetContentTypes = async () => {
737
867
  const { currentProject, log, messages } = this;
738
868
  let err;
739
869
  if (!this.contensis) err = await this.HydrateContensis();
740
870
 
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();
871
+ if (err) log.error(messages.models.noList(currentProject));
872
+ if (!this.contensis) log.warning(messages.models.noList(currentProject));
873
+
874
+ return this.contensis;
875
+ };
876
+
877
+ PrintContentModels = async (modelIds: string[] = []) => {
878
+ const { currentProject, log, messages } = this;
879
+ const contensis = await this.GetContentTypes();
880
+ if (contensis) {
881
+ // Retrieve models list for env
882
+ const { models, contentTypes = [], components = [] } = this;
883
+
884
+ // Models to output to console
885
+ const returnModels = modelIds?.length
886
+ ? models?.filter((m: Model) =>
887
+ modelIds.some(id => id.toLowerCase() === m.id.toLowerCase())
888
+ )
889
+ : undefined;
890
+
891
+ // Generate a list of contentTypeIds and componentIds from all models
892
+ // and dependencies
893
+ const contentTypeIds = Array.from(
894
+ new Set([
895
+ ...(returnModels || models || []).map(m => m.id),
896
+ ...(returnModels || models || [])
897
+ .map(m => m.dependencies?.contentTypes?.map(c => c[0]) || [])
898
+ .flat(),
899
+ ])
900
+ );
901
+ const componentIds = Array.from(
902
+ new Set(
903
+ (returnModels || models || [])
904
+ .map(m => m.dependencies?.components?.map(c => c[0]) || [])
905
+ .flat()
906
+ )
907
+ );
908
+
909
+ // Create an array of all the content types and component definitions
910
+ // we will use this when outputting to a file
911
+ const contentModelBackup = [
912
+ ...contentTypes.filter(c => contentTypeIds.includes(c.id)),
913
+ ...components.filter(c => componentIds.includes(c.id)),
914
+ ];
915
+
916
+ if (Array.isArray(returnModels)) {
917
+ log.success(messages.models.list(currentProject));
918
+ this.HandleFormattingAndOutput(contentModelBackup, () => {
919
+ // print the content models to console
920
+ for (const model of returnModels) {
921
+ log.raw('');
922
+ log.object(model);
923
+ }
924
+ log.raw('');
925
+ });
926
+ } else {
927
+ log.success(
928
+ messages.models.get(currentProject, models?.length.toString() || '0')
929
+ );
930
+ log.raw('');
931
+ if (models?.length) {
932
+ this.HandleFormattingAndOutput(contentModelBackup, () => {
933
+ // print the content models s#qto console
934
+ for (const model of models) {
935
+ const components = model.components?.length || 0;
936
+ const contentTypes = model.contentTypes?.length || 0;
937
+ const dependencies =
938
+ (model.dependencies?.components?.length || 0) +
939
+ (model.dependencies?.contentTypes?.length || 0);
940
+ const dependencyOf =
941
+ (model.dependencyOf?.components?.length || 0) +
942
+ (model.dependencyOf?.contentTypes?.length || 0);
943
+
944
+ const hasAny =
945
+ components + contentTypes + dependencies + dependencyOf;
946
+ log.raw(
947
+ ` - ${log.highlightText(log.boldText(model.id))} ${
948
+ hasAny
949
+ ? log.infoText(
950
+ `{ ${components ? `components: ${components}, ` : ''}${
951
+ contentTypes ? `contentTypes: ${contentTypes}, ` : ''
952
+ }${
953
+ dependencies ? `references: ${dependencies}, ` : ''
954
+ }${
955
+ dependencyOf ? `required by: ${dependencyOf}` : ''
956
+ } }`
957
+ )
958
+ : ''
959
+ }`
960
+ );
961
+ }
962
+ log.raw('');
963
+ });
964
+ }
965
+ }
966
+ }
967
+ };
968
+
969
+ ImportContentModels = async ({
970
+ commit,
971
+ fromFile,
972
+ }: {
973
+ commit: boolean;
974
+ fromFile: string;
975
+ }) => {
976
+ const { currentProject, log, messages } = this;
977
+
978
+ const fileData = fromFile
979
+ ? readJsonFile<(ContentType | Component)[]>(fromFile) || []
980
+ : [];
981
+ if (typeof fileData === 'string')
982
+ throw new Error(`Import file format must be of type JSON`);
983
+
984
+ const contensis = await this.ConnectContensisImport({
985
+ commit,
986
+ fromFile,
987
+ importDataType: 'models',
988
+ });
989
+
990
+ if (contensis) {
991
+ log.line();
992
+ if (contensis.isPreview) {
993
+ console.log(log.successText(` -- IMPORT PREVIEW -- `));
994
+ } else {
995
+ console.log(log.warningText(` *** COMMITTING IMPORT *** `));
996
+ }
997
+
998
+ const [migrateErr, result] = await contensis.MigrateContentModels();
999
+
1000
+ if (migrateErr) logError(migrateErr);
1001
+ else
1002
+ this.HandleFormattingAndOutput(result, () => {
1003
+ // print the results to console
1004
+ if (!commit) {
1005
+ log.raw(log.boldText(`\nContent types:`));
1006
+ if (!result.contentTypes) log.info(`- None returned\n`);
1007
+ else printModelMigrationAnalysis(this, result.contentTypes);
1008
+
1009
+ log.raw(log.boldText(`\nComponents:`));
1010
+ if (!result.components) log.info(`- None returned\n`);
1011
+ else printModelMigrationAnalysis(this, result.components);
1012
+ } else {
1013
+ const migrateResult = result as MigrateModelsResult;
1014
+ log.raw(log.boldText(`\nContent types:`));
1015
+ printModelMigrationResult(
1016
+ this,
1017
+ migrateResult[currentProject].contentTypes
1018
+ );
1019
+
1020
+ log.raw(log.boldText(`\nComponents:`));
1021
+ printModelMigrationResult(
1022
+ this,
1023
+ migrateResult[currentProject].components
1024
+ );
1025
+ }
1026
+ });
745
1027
  } else {
746
- log.warning(messages.contenttypes.noList(currentProject));
1028
+ log.warning(messages.models.noList(currentProject));
1029
+ log.help(messages.connect.tip());
747
1030
  }
748
1031
  };
749
1032
 
@@ -799,15 +1082,12 @@ class ContensisCli {
799
1082
 
800
1083
  RemoveContentTypes = async (contentTypeIds: string[], commit = false) => {
801
1084
  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
- );
1085
+ const contensis = await this.ConnectContensisImport({
1086
+ commit,
1087
+ importDataType: 'user-input',
1088
+ });
1089
+ if (contensis) {
1090
+ const [err, result] = await contensis.DeleteContentTypes(contentTypeIds);
811
1091
 
812
1092
  if (err) {
813
1093
  log.error(
@@ -822,7 +1102,7 @@ class ContensisCli {
822
1102
  messages.contenttypes.removed(
823
1103
  currentProject,
824
1104
  contentTypeIds.join('", "'),
825
- !this.contensis.isPreview
1105
+ !contensis.isPreview
826
1106
  )
827
1107
  );
828
1108
  // print the results to console
@@ -851,22 +1131,21 @@ class ContensisCli {
851
1131
 
852
1132
  if (!Array.isArray(fileData)) fileData = [fileData];
853
1133
 
854
- await this.ConnectContensisImport({
1134
+ const contensis = await this.ConnectContensisImport({
855
1135
  commit,
856
- source: fromFile ? 'file' : 'contensis',
1136
+ importDataType: fromFile ? 'user-input' : undefined,
857
1137
  });
858
1138
 
859
- if (this.contensis) {
1139
+ if (contensis) {
860
1140
  // Pass each content type to the target repo
861
1141
  for (const contentType of fileData) {
862
1142
  // Fix invalid data
863
1143
  contentType.projectId = currentProject;
864
1144
  delete contentType.uuid;
865
1145
 
866
- const [err, created, createStatus] =
867
- await this.contensis.models.targetRepos[
868
- currentProject
869
- ].repo.UpsertContentType(false, contentType);
1146
+ const [err, created, createStatus] = await contensis.models.targetRepos[
1147
+ currentProject
1148
+ ].repo.UpsertContentType(false, contentType);
870
1149
 
871
1150
  if (err) log.error(err.message, err);
872
1151
  if (createStatus) {
@@ -884,6 +1163,53 @@ class ContensisCli {
884
1163
  }
885
1164
  };
886
1165
 
1166
+ DiffModels = async (
1167
+ {
1168
+ fromFile,
1169
+ }: {
1170
+ fromFile: string;
1171
+ },
1172
+ modelIds: string[] = []
1173
+ ) => {
1174
+ const { log } = this;
1175
+
1176
+ let fileData = fromFile ? readJsonFile<ContentType[]>(fromFile) || [] : [];
1177
+ if (typeof fileData === 'string')
1178
+ throw new Error(`Import file format must be of type JSON`);
1179
+
1180
+ if (!Array.isArray(fileData)) fileData = [fileData];
1181
+
1182
+ const contensis = await this.ConnectContensisImport({
1183
+ fromFile,
1184
+ importDataType: 'models',
1185
+ });
1186
+
1187
+ if (contensis) {
1188
+ const [err, result] = (await to(
1189
+ contensis.models.Diff(fileData.length ? fileData : modelIds)
1190
+ )) as [Error | null, ContentTypesResult | undefined];
1191
+
1192
+ if (err) log.error(err.message, err);
1193
+ if (result)
1194
+ // print the content type to console
1195
+ this.HandleFormattingAndOutput(result, () => {
1196
+ log.success(
1197
+ `Queried models ${log.infoText(
1198
+ `"${result.query.modelIds?.join(', ')}"`
1199
+ )}\n`
1200
+ );
1201
+
1202
+ log.raw(log.boldText(`Content types:`));
1203
+ if (!result.contentTypes) log.info(`- None returned\n`);
1204
+ else printModelMigrationAnalysis(this, result.contentTypes);
1205
+
1206
+ log.raw(log.boldText(`Components:`));
1207
+ if (!result.components) log.info(`- None returned\n`);
1208
+ else printModelMigrationAnalysis(this, result.components);
1209
+ });
1210
+ }
1211
+ };
1212
+
887
1213
  PrintComponents = async () => {
888
1214
  const { currentProject, log, messages } = this;
889
1215
  await this.GetContentTypes();
@@ -933,13 +1259,12 @@ class ContensisCli {
933
1259
 
934
1260
  RemoveComponents = async (componentIds: string[], commit = false) => {
935
1261
  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(
1262
+ const contensis = await this.ConnectContensisImport({
1263
+ commit,
1264
+ importDataType: 'user-input',
1265
+ });
1266
+ if (contensis) {
1267
+ const [err, result] = await contensis.DeleteContentTypes(
943
1268
  undefined,
944
1269
  componentIds
945
1270
  );
@@ -957,7 +1282,7 @@ class ContensisCli {
957
1282
  messages.components.removed(
958
1283
  currentProject,
959
1284
  componentIds.join('", "'),
960
- !this.contensis.isPreview
1285
+ !contensis.isPreview
961
1286
  )
962
1287
  );
963
1288
  // print the results to console
@@ -986,22 +1311,21 @@ class ContensisCli {
986
1311
 
987
1312
  if (!Array.isArray(fileData)) fileData = [fileData];
988
1313
 
989
- await this.ConnectContensisImport({
1314
+ const contensis = await this.ConnectContensisImport({
990
1315
  commit,
991
- source: fromFile ? 'file' : 'contensis',
1316
+ importDataType: fromFile ? 'user-input' : undefined,
992
1317
  });
993
1318
 
994
- if (this.contensis) {
1319
+ if (contensis) {
995
1320
  // Pass each component to the target repo
996
1321
  for (const component of fileData) {
997
1322
  // Fix invalid data
998
1323
  component.projectId = currentProject;
999
1324
  delete component.uuid;
1000
1325
 
1001
- const [err, created, createStatus] =
1002
- await this.contensis.models.targetRepos[
1003
- currentProject
1004
- ].repo.UpsertComponent(false, component);
1326
+ const [err, created, createStatus] = await contensis.models.targetRepos[
1327
+ currentProject
1328
+ ].repo.UpsertComponent(false, component);
1005
1329
 
1006
1330
  if (err) log.error(err.message, err);
1007
1331
  if (createStatus) {
@@ -1019,21 +1343,20 @@ class ContensisCli {
1019
1343
  }
1020
1344
  };
1021
1345
 
1022
- RemoveEntry = async (id: string, commit = false) => {
1346
+ RemoveEntries = async (commit = false) => {
1023
1347
  const { currentEnv, log, messages } = this;
1024
- if (!this.contensis)
1025
- await this.ConnectContensisImport({
1026
- source: 'input',
1027
- commit,
1028
- });
1348
+ const contensis = await this.ConnectContensisImport({
1349
+ commit,
1350
+ importDataType: 'user-input',
1351
+ });
1029
1352
 
1030
- if (this.contensis) {
1031
- if (this.contensis.isPreview) {
1353
+ if (contensis) {
1354
+ if (contensis.isPreview) {
1032
1355
  console.log(log.successText(` -- PREVIEW -- `));
1033
1356
  } else {
1034
1357
  console.log(log.warningText(` *** COMMITTING DELETE *** `));
1035
1358
  }
1036
- const [err, result] = await this.contensis.DeleteEntries();
1359
+ const [err, result] = await contensis.DeleteEntries();
1037
1360
  if (result)
1038
1361
  this.HandleFormattingAndOutput(result, () => {
1039
1362
  // print the migrateResult to console
@@ -1045,12 +1368,12 @@ class ContensisCli {
1045
1368
  Object.values(result.entriesToMigrate)?.[0].totalCount > 0) ||
1046
1369
  (commit && result.migrateResult?.deleted))
1047
1370
  ) {
1048
- log.success(messages.entries.removed(currentEnv, id, commit));
1371
+ log.success(messages.entries.removed(currentEnv, commit));
1049
1372
  if (!commit) log.help(messages.entries.commitTip());
1050
1373
  } else {
1051
- log.error(messages.entries.failedRemove(currentEnv, id), err);
1374
+ log.error(messages.entries.failedRemove(currentEnv), err);
1052
1375
  if (!Object.values(result.entriesToMigrate)?.[0].totalCount)
1053
- log.help(messages.entries.notFound(id));
1376
+ log.help(messages.entries.notFound(currentEnv));
1054
1377
  }
1055
1378
  }
1056
1379
  };
@@ -1061,21 +1384,21 @@ class ContensisCli {
1061
1384
  withDependents?: boolean;
1062
1385
  }) => {
1063
1386
  const { currentProject, log, messages } = this;
1064
- await this.ConnectContensis();
1387
+ const contensis = await this.ConnectContensis();
1065
1388
 
1066
- if (this.contensis) {
1389
+ if (contensis) {
1067
1390
  log.line();
1068
- const entries = await this.contensis.GetEntries({ withDependents });
1391
+ const entries = await contensis.GetEntries({ withDependents });
1069
1392
  this.HandleFormattingAndOutput(entries, () =>
1070
1393
  // print the entries to console
1071
1394
  logEntriesTable(
1072
1395
  entries,
1073
1396
  currentProject,
1074
- this.contensis?.payload.query?.fields
1397
+ contensis.payload.query?.fields
1075
1398
  )
1076
1399
  );
1077
1400
  } else {
1078
- log.warning(messages.contenttypes.noList(currentProject));
1401
+ log.warning(messages.models.noList(currentProject));
1079
1402
  log.help(messages.connect.tip());
1080
1403
  }
1081
1404
  };
@@ -1089,26 +1412,21 @@ class ContensisCli {
1089
1412
  }) => {
1090
1413
  const { currentProject, log, messages } = this;
1091
1414
 
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({
1415
+ const contensis = await this.ConnectContensisImport({
1097
1416
  commit,
1098
- source: fromFile ? 'file' : 'contensis',
1099
- fileData,
1100
- fileDataType: 'entries',
1417
+ fromFile,
1418
+ importDataType: 'entries',
1101
1419
  });
1102
1420
 
1103
- if (this.contensis) {
1421
+ if (contensis) {
1104
1422
  log.line();
1105
- if (this.contensis.isPreview) {
1423
+ if (contensis.isPreview) {
1106
1424
  console.log(log.successText(` -- IMPORT PREVIEW -- `));
1107
1425
  } else {
1108
1426
  console.log(log.warningText(` *** COMMITTING IMPORT *** `));
1109
1427
  }
1110
1428
 
1111
- const [migrateErr, migrateResult] = await this.contensis.MigrateEntries();
1429
+ const [migrateErr, migrateResult] = await contensis.MigrateEntries();
1112
1430
 
1113
1431
  if (migrateErr) logError(migrateErr);
1114
1432
  else
@@ -1117,7 +1435,7 @@ class ContensisCli {
1117
1435
  printMigrateResult(this, migrateResult);
1118
1436
  });
1119
1437
  } else {
1120
- log.warning(messages.contenttypes.noList(currentProject));
1438
+ log.warning(messages.models.noList(currentProject));
1121
1439
  log.help(messages.connect.tip());
1122
1440
  }
1123
1441
  };
@@ -1127,19 +1445,19 @@ class ContensisCli {
1127
1445
  name?: string
1128
1446
  ) => {
1129
1447
  const { currentEnv, log, messages } = this;
1130
- if (!this.contensis) await this.ConnectContensis();
1131
- if (this.contensis) {
1448
+ const contensis = await this.ConnectContensis();
1449
+ if (contensis) {
1132
1450
  // Retrieve webhooks list for env
1133
1451
  const [webhooksErr, webhooks] =
1134
- await this.contensis.subscriptions.webhooks.GetSubscriptions();
1452
+ await contensis.subscriptions.webhooks.GetSubscriptions();
1135
1453
 
1136
1454
  const filteredResults =
1137
1455
  typeof name === 'string'
1138
- ? webhooks.filter(w =>
1456
+ ? webhooks?.filter(w =>
1139
1457
  w.name?.toLowerCase().includes(name.toLowerCase())
1140
1458
  )
1141
1459
  : Array.isArray(subscriptionIds)
1142
- ? webhooks.filter(w => subscriptionIds?.some(id => id === w.id))
1460
+ ? webhooks?.filter(w => subscriptionIds?.some(id => id === w.id))
1143
1461
  : webhooks;
1144
1462
 
1145
1463
  if (Array.isArray(filteredResults)) {
@@ -1176,16 +1494,16 @@ class ContensisCli {
1176
1494
  };
1177
1495
 
1178
1496
  PrintBlocks = async () => {
1179
- const { currentEnv, log, messages } = this;
1180
- if (!this.contensis) await this.ConnectContensis();
1181
- if (this.contensis) {
1497
+ const { currentEnv, env, log, messages } = this;
1498
+ const contensis = await this.ConnectContensis();
1499
+ if (contensis) {
1182
1500
  // Retrieve blocks list for env
1183
- const [err, blocks] = await this.contensis.blocks.GetBlocks();
1501
+ const [err, blocks] = await contensis.blocks.GetBlocks();
1184
1502
 
1185
1503
  if (Array.isArray(blocks)) {
1186
1504
  this.HandleFormattingAndOutput(blocks, () => {
1187
1505
  // print the blocks to console
1188
- log.success(messages.blocks.list(currentEnv));
1506
+ log.success(messages.blocks.list(currentEnv, env.currentProject));
1189
1507
  for (const {
1190
1508
  id,
1191
1509
  description,
@@ -1226,10 +1544,10 @@ class ContensisCli {
1226
1544
  version: string
1227
1545
  ) => {
1228
1546
  const { currentEnv, env, log, messages } = this;
1229
- if (!this.contensis) await this.ConnectContensis();
1230
- if (this.contensis) {
1547
+ const contensis = await this.ConnectContensis();
1548
+ if (contensis) {
1231
1549
  // Retrieve block version
1232
- const [err, blocks] = await this.contensis.blocks.GetBlockVersions(
1550
+ const [err, blocks] = await contensis.blocks.GetBlockVersions(
1233
1551
  blockId,
1234
1552
  branch,
1235
1553
  version
@@ -1239,7 +1557,7 @@ class ContensisCli {
1239
1557
  this.HandleFormattingAndOutput(blocks, () => {
1240
1558
  // print the version detail to console
1241
1559
  log.success(
1242
- messages.blocks.get(`${currentEnv}:${env.currentProject}`)
1560
+ messages.blocks.get(blockId, currentEnv, env.currentProject)
1243
1561
  );
1244
1562
  for (const block of blocks)
1245
1563
  printBlockVersion(
@@ -1268,18 +1586,20 @@ class ContensisCli {
1268
1586
  const { currentEnv, env, log, messages } = this;
1269
1587
 
1270
1588
  // Output request to console
1271
- messages.blocks.tryPush(
1272
- block.id,
1273
- block.source.branch,
1274
- currentEnv,
1275
- env.currentProject
1589
+ log.info(
1590
+ messages.blocks.tryPush(
1591
+ block.id,
1592
+ block.source.branch,
1593
+ currentEnv,
1594
+ env.currentProject
1595
+ )
1276
1596
  );
1277
1597
  console.log(jsonFormatter(block));
1278
1598
 
1279
- if (!this.contensis) await this.ConnectContensis();
1280
- if (this.contensis) {
1599
+ const contensis = await this.ConnectContensis();
1600
+ if (contensis) {
1281
1601
  // Push new block version
1282
- const [err, blockVersion] = await this.contensis.blocks.PushBlockVersion(
1602
+ const [err, blockVersion] = await contensis.blocks.PushBlockVersion(
1283
1603
  block
1284
1604
  );
1285
1605
  if (!err) {
@@ -1291,7 +1611,6 @@ class ContensisCli {
1291
1611
  env.currentProject
1292
1612
  )
1293
1613
  );
1294
- console.log(jsonFormatter(blockVersion));
1295
1614
  }
1296
1615
  if (blockVersion) {
1297
1616
  this.HandleFormattingAndOutput(blockVersion, () => {
@@ -1306,17 +1625,51 @@ class ContensisCli {
1306
1625
  }
1307
1626
  };
1308
1627
 
1628
+ ReleaseBlock = async (blockId: string, version: string) => {
1629
+ const { currentEnv, env, log, messages } = this;
1630
+ const contensis = await this.ConnectContensis();
1631
+ if (contensis) {
1632
+ // Retrieve block version
1633
+ const [err, blockVersion] = await contensis.blocks.BlockAction(
1634
+ blockId,
1635
+ 'release',
1636
+ version
1637
+ );
1638
+
1639
+ if (blockVersion) {
1640
+ this.HandleFormattingAndOutput(blockVersion, () => {
1641
+ // print the version detail to console
1642
+ log.success(
1643
+ messages.blocks.released(blockId, currentEnv, env.currentProject)
1644
+ );
1645
+ printBlockVersion(this, blockVersion);
1646
+ });
1647
+ }
1648
+
1649
+ if (err) {
1650
+ log.error(
1651
+ messages.blocks.failedRelease(blockId, currentEnv, env.currentProject)
1652
+ );
1653
+ log.error(jsonFormatter(err));
1654
+ }
1655
+ }
1656
+ };
1657
+
1309
1658
  PrintBlockLogs = async (
1310
1659
  blockId: string,
1311
1660
  branch: string,
1312
1661
  version: string,
1313
1662
  dataCenter: 'hq' | 'manchester' | 'london'
1314
1663
  ) => {
1315
- const { currentEnv, log, messages } = this;
1316
- if (!this.contensis) await this.ConnectContensis();
1317
- if (this.contensis) {
1664
+ const { currentEnv, env, log, messages } = this;
1665
+ const contensis = await this.ConnectContensis();
1666
+ if (contensis) {
1318
1667
  // Retrieve block logs
1319
- const [err, blockLogs] = await this.contensis.blocks.GetBlockLogs({
1668
+ log.success(
1669
+ messages.blocks.getLogs(blockId, branch, currentEnv, env.currentProject)
1670
+ );
1671
+
1672
+ const [err, blockLogs] = await contensis.blocks.GetBlockLogs({
1320
1673
  blockId,
1321
1674
  branchId: branch,
1322
1675
  version,
@@ -1325,8 +1678,7 @@ class ContensisCli {
1325
1678
 
1326
1679
  if (blockLogs) {
1327
1680
  this.HandleFormattingAndOutput(blockLogs, () => {
1328
- // print the logs to console
1329
- log.success(messages.blocks.list(currentEnv));
1681
+ // print the logs to console
1330
1682
  console.log(
1331
1683
  ` - ${blockId} ${branch} ${
1332
1684
  Number(version) ? `v${version}` : version
@@ -1339,7 +1691,9 @@ class ContensisCli {
1339
1691
  }
1340
1692
 
1341
1693
  if (err) {
1342
- log.error(messages.blocks.noList(currentEnv));
1694
+ log.error(
1695
+ messages.blocks.failedGetLogs(blockId, currentEnv, env.currentProject)
1696
+ );
1343
1697
  log.error(jsonFormatter(err));
1344
1698
  }
1345
1699
  }
@@ -1347,6 +1701,21 @@ class ContensisCli {
1347
1701
 
1348
1702
  HandleFormattingAndOutput = <T>(obj: T, logFn: (obj: T) => void) => {
1349
1703
  const { format, log, messages, output } = this;
1704
+ if (!format) {
1705
+ // print the object to console
1706
+ logFn(obj);
1707
+ } else if (format === 'csv') {
1708
+ log.raw('');
1709
+ log.raw(log.infoText(csvFormatter(obj)));
1710
+ } else if (format === 'xml') {
1711
+ log.raw('');
1712
+ log.raw(log.infoText(xmlFormatter(obj)));
1713
+ } else if (format === 'json') {
1714
+ log.raw('');
1715
+ log.raw(log.infoText(jsonFormatter(obj)));
1716
+ }
1717
+ log.raw('');
1718
+
1350
1719
  if (output) {
1351
1720
  let writeString = '';
1352
1721
  if (format === 'csv') {
@@ -1361,21 +1730,6 @@ class ContensisCli {
1361
1730
  } else {
1362
1731
  log.info(messages.app.noFileOutput());
1363
1732
  }
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
1733
  }
1380
1734
  };
1381
1735
  }