@contentstack/cli-audit 1.3.5 → 1.4.1
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.
- package/README.md +17 -13
- package/lib/audit-base-command.js +19 -2
- package/lib/config/index.d.ts +3 -0
- package/lib/config/index.js +20 -2
- package/lib/messages/index.js +12 -7
- package/lib/modules/content-types.d.ts +27 -1
- package/lib/modules/content-types.js +106 -11
- package/lib/modules/entries.d.ts +39 -1
- package/lib/modules/entries.js +125 -7
- package/lib/types/common.d.ts +4 -1
- package/lib/types/content-types.d.ts +11 -9
- package/lib/types/entries.d.ts +10 -4
- package/lib/types/extension.d.ts +38 -0
- package/lib/types/extension.js +2 -0
- package/lib/types/utils.d.ts +2 -2
- package/lib/util/log.d.ts +2 -1
- package/lib/util/log.js +15 -6
- package/oclif.manifest.json +2 -2
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -19,7 +19,7 @@ $ npm install -g @contentstack/cli-audit
|
|
|
19
19
|
$ csdx COMMAND
|
|
20
20
|
running command...
|
|
21
21
|
$ csdx (--version|-v)
|
|
22
|
-
@contentstack/cli-audit/1.
|
|
22
|
+
@contentstack/cli-audit/1.4.1 linux-x64 node-v18.19.1
|
|
23
23
|
$ csdx --help [COMMAND]
|
|
24
24
|
USAGE
|
|
25
25
|
$ csdx COMMAND
|
|
@@ -99,14 +99,14 @@ Perform audits and fix possible errors in the exported Contentstack data.
|
|
|
99
99
|
USAGE
|
|
100
100
|
$ csdx audit:fix [-c <value>] [-d <value>] [--report-path <value>] [--modules
|
|
101
101
|
content-types|global-fields|entries] [--copy-path <value> --copy-dir] [--fix-only
|
|
102
|
-
reference|global_field|json:rte|json:
|
|
102
|
+
reference|global_field|json:rte|json:extension|blocks|group] [--columns <value> | ] [--sort <value>] [--filter
|
|
103
103
|
<value>] [--csv | --no-truncate]
|
|
104
104
|
|
|
105
105
|
FLAGS
|
|
106
106
|
--copy-dir Create backup from the original data.
|
|
107
107
|
--copy-path=<value> Provide the path to backup the copied data
|
|
108
108
|
--fix-only=<option>... Provide the list of fix options
|
|
109
|
-
<options: reference|global_field|json:rte|json:
|
|
109
|
+
<options: reference|global_field|json:rte|json:extension|blocks|group>
|
|
110
110
|
--modules=<option>... Provide the list of modules to be audited
|
|
111
111
|
<options: content-types|global-fields|entries>
|
|
112
112
|
--report-path=<value> Path to store the audit reports
|
|
@@ -198,14 +198,14 @@ Perform audits and fix possible errors in the exported Contentstack data.
|
|
|
198
198
|
USAGE
|
|
199
199
|
$ csdx cm:stacks:audit:fix [-c <value>] [-d <value>] [--report-path <value>] [--modules
|
|
200
200
|
content-types|global-fields|entries] [--copy-path <value> --copy-dir] [--fix-only
|
|
201
|
-
reference|global_field|json:rte|json:
|
|
201
|
+
reference|global_field|json:rte|json:extension|blocks|group] [--columns <value> | ] [--sort <value>] [--filter
|
|
202
202
|
<value>] [--csv | --no-truncate]
|
|
203
203
|
|
|
204
204
|
FLAGS
|
|
205
205
|
--copy-dir Create backup from the original data.
|
|
206
206
|
--copy-path=<value> Provide the path to backup the copied data
|
|
207
207
|
--fix-only=<option>... Provide the list of fix options
|
|
208
|
-
<options: reference|global_field|json:rte|json:
|
|
208
|
+
<options: reference|global_field|json:rte|json:extension|blocks|group>
|
|
209
209
|
--modules=<option>... Provide the list of modules to be audited
|
|
210
210
|
<options: content-types|global-fields|entries>
|
|
211
211
|
--report-path=<value> Path to store the audit reports
|
|
@@ -285,7 +285,7 @@ EXAMPLES
|
|
|
285
285
|
$ csdx plugins
|
|
286
286
|
```
|
|
287
287
|
|
|
288
|
-
_See code: [@oclif/plugin-plugins](https://github.com/oclif/plugin-plugins/blob/v4.
|
|
288
|
+
_See code: [@oclif/plugin-plugins](https://github.com/oclif/plugin-plugins/blob/v4.2.6/src/commands/plugins/index.ts)_
|
|
289
289
|
|
|
290
290
|
## `csdx plugins:install PLUGIN...`
|
|
291
291
|
|
|
@@ -354,7 +354,7 @@ EXAMPLES
|
|
|
354
354
|
$ csdx plugins:inspect myplugin
|
|
355
355
|
```
|
|
356
356
|
|
|
357
|
-
_See code: [@oclif/plugin-plugins](https://github.com/oclif/plugin-plugins/blob/v4.
|
|
357
|
+
_See code: [@oclif/plugin-plugins](https://github.com/oclif/plugin-plugins/blob/v4.2.6/src/commands/plugins/inspect.ts)_
|
|
358
358
|
|
|
359
359
|
## `csdx plugins:install PLUGIN...`
|
|
360
360
|
|
|
@@ -398,7 +398,7 @@ EXAMPLES
|
|
|
398
398
|
$ csdx plugins:install someuser/someplugin
|
|
399
399
|
```
|
|
400
400
|
|
|
401
|
-
_See code: [@oclif/plugin-plugins](https://github.com/oclif/plugin-plugins/blob/v4.
|
|
401
|
+
_See code: [@oclif/plugin-plugins](https://github.com/oclif/plugin-plugins/blob/v4.2.6/src/commands/plugins/install.ts)_
|
|
402
402
|
|
|
403
403
|
## `csdx plugins:link PLUGIN`
|
|
404
404
|
|
|
@@ -428,7 +428,7 @@ EXAMPLES
|
|
|
428
428
|
$ csdx plugins:link myplugin
|
|
429
429
|
```
|
|
430
430
|
|
|
431
|
-
_See code: [@oclif/plugin-plugins](https://github.com/oclif/plugin-plugins/blob/v4.
|
|
431
|
+
_See code: [@oclif/plugin-plugins](https://github.com/oclif/plugin-plugins/blob/v4.2.6/src/commands/plugins/link.ts)_
|
|
432
432
|
|
|
433
433
|
## `csdx plugins:uninstall PLUGIN...`
|
|
434
434
|
|
|
@@ -462,10 +462,14 @@ Remove all user-installed and linked plugins.
|
|
|
462
462
|
|
|
463
463
|
```
|
|
464
464
|
USAGE
|
|
465
|
-
$ csdx plugins:reset
|
|
465
|
+
$ csdx plugins:reset [--hard] [--reinstall]
|
|
466
|
+
|
|
467
|
+
FLAGS
|
|
468
|
+
--hard Delete node_modules and package manager related files in addition to uninstalling plugins.
|
|
469
|
+
--reinstall Reinstall all plugins after uninstalling.
|
|
466
470
|
```
|
|
467
471
|
|
|
468
|
-
_See code: [@oclif/plugin-plugins](https://github.com/oclif/plugin-plugins/blob/v4.
|
|
472
|
+
_See code: [@oclif/plugin-plugins](https://github.com/oclif/plugin-plugins/blob/v4.2.6/src/commands/plugins/reset.ts)_
|
|
469
473
|
|
|
470
474
|
## `csdx plugins:uninstall PLUGIN...`
|
|
471
475
|
|
|
@@ -493,7 +497,7 @@ EXAMPLES
|
|
|
493
497
|
$ csdx plugins:uninstall myplugin
|
|
494
498
|
```
|
|
495
499
|
|
|
496
|
-
_See code: [@oclif/plugin-plugins](https://github.com/oclif/plugin-plugins/blob/v4.
|
|
500
|
+
_See code: [@oclif/plugin-plugins](https://github.com/oclif/plugin-plugins/blob/v4.2.6/src/commands/plugins/uninstall.ts)_
|
|
497
501
|
|
|
498
502
|
## `csdx plugins:uninstall PLUGIN...`
|
|
499
503
|
|
|
@@ -537,5 +541,5 @@ DESCRIPTION
|
|
|
537
541
|
Update installed plugins.
|
|
538
542
|
```
|
|
539
543
|
|
|
540
|
-
_See code: [@oclif/plugin-plugins](https://github.com/oclif/plugin-plugins/blob/v4.
|
|
544
|
+
_See code: [@oclif/plugin-plugins](https://github.com/oclif/plugin-plugins/blob/v4.2.6/src/commands/plugins/update.ts)_
|
|
541
545
|
<!-- commandsstop -->
|
|
@@ -75,7 +75,13 @@ class AuditBaseCommand extends base_command_1.BaseCommand {
|
|
|
75
75
|
let { ctSchema, gfSchema } = this.getCtAndGfSchema();
|
|
76
76
|
let missingCtRefs, missingGfRefs, missingEntryRefs;
|
|
77
77
|
for (const module of this.sharedConfig.flags.modules || this.sharedConfig.modules) {
|
|
78
|
-
|
|
78
|
+
(0, log_1.print)([
|
|
79
|
+
{
|
|
80
|
+
bold: true,
|
|
81
|
+
color: 'whiteBright',
|
|
82
|
+
message: this.$t(this.messages.AUDIT_START_SPINNER, { module }),
|
|
83
|
+
},
|
|
84
|
+
]);
|
|
79
85
|
const constructorParam = {
|
|
80
86
|
ctSchema,
|
|
81
87
|
gfSchema,
|
|
@@ -98,7 +104,18 @@ class AuditBaseCommand extends base_command_1.BaseCommand {
|
|
|
98
104
|
await this.prepareReport(module, missingEntryRefs);
|
|
99
105
|
break;
|
|
100
106
|
}
|
|
101
|
-
|
|
107
|
+
(0, log_1.print)([
|
|
108
|
+
{
|
|
109
|
+
bold: true,
|
|
110
|
+
color: 'whiteBright',
|
|
111
|
+
message: this.$t(this.messages.AUDIT_START_SPINNER, { module }),
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
bold: true,
|
|
115
|
+
message: ' done',
|
|
116
|
+
color: 'whiteBright',
|
|
117
|
+
},
|
|
118
|
+
]);
|
|
102
119
|
}
|
|
103
120
|
return { missingCtRefs, missingGfRefs, missingEntryRefs };
|
|
104
121
|
}
|
package/lib/config/index.d.ts
CHANGED
package/lib/config/index.js
CHANGED
|
@@ -3,9 +3,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
const config = {
|
|
4
4
|
showTerminalOutput: true,
|
|
5
5
|
skipRefs: ['sys_assets'],
|
|
6
|
-
skipFieldTypes: ['taxonomy'],
|
|
6
|
+
skipFieldTypes: ['taxonomy', 'group'],
|
|
7
7
|
modules: ['content-types', 'global-fields', 'entries'],
|
|
8
|
-
'fix-fields': ['reference', 'global_field', 'json:rte', 'json:
|
|
8
|
+
'fix-fields': ['reference', 'global_field', 'json:rte', 'json:extension', 'blocks', 'group'],
|
|
9
9
|
moduleConfig: {
|
|
10
10
|
'content-types': {
|
|
11
11
|
name: 'content type',
|
|
@@ -28,5 +28,23 @@ const config = {
|
|
|
28
28
|
fileName: 'locales.json',
|
|
29
29
|
},
|
|
30
30
|
},
|
|
31
|
+
entries: {
|
|
32
|
+
systemKeys: [
|
|
33
|
+
'uid',
|
|
34
|
+
'ACL',
|
|
35
|
+
'tags',
|
|
36
|
+
'locale',
|
|
37
|
+
'_version',
|
|
38
|
+
'_metadata',
|
|
39
|
+
'published',
|
|
40
|
+
'created_at',
|
|
41
|
+
'updated_at',
|
|
42
|
+
'created_by',
|
|
43
|
+
'updated_by',
|
|
44
|
+
'_in_progress',
|
|
45
|
+
'_restore_status',
|
|
46
|
+
'publish_details',
|
|
47
|
+
],
|
|
48
|
+
},
|
|
31
49
|
};
|
|
32
50
|
exports.default = config;
|
package/lib/messages/index.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.tableColumnDescriptions = exports.auditFixMsg = exports.auditMsg = exports.commonMsg = exports.errors = exports.$t = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const memoize_1 = tslib_1.__importDefault(require("lodash/memoize"));
|
|
4
6
|
const errors = {};
|
|
5
7
|
exports.errors = errors;
|
|
6
8
|
const tableColumnDescriptions = {
|
|
@@ -51,13 +53,16 @@ const messages = Object.assign(Object.assign(Object.assign(Object.assign(Object.
|
|
|
51
53
|
* @returns a string.
|
|
52
54
|
*/
|
|
53
55
|
function $t(msg, args) {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
56
|
+
const transfer = (0, memoize_1.default)(function (msg, args) {
|
|
57
|
+
if (!msg)
|
|
58
|
+
return '';
|
|
59
|
+
for (const key of Object.keys(args)) {
|
|
60
|
+
const escapedKey = key.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
61
|
+
msg = msg.replace(new RegExp(`{${escapedKey}}`, 'g'), args[key] || escapedKey);
|
|
62
|
+
}
|
|
63
|
+
return msg;
|
|
64
|
+
});
|
|
65
|
+
return transfer(msg, args);
|
|
61
66
|
}
|
|
62
67
|
exports.$t = $t;
|
|
63
68
|
exports.default = messages;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { LogFn, ConfigType, ModularBlockType, ContentTypeStruct, GroupFieldDataType, RefErrorReturnType, CtConstructorParam, GlobalFieldDataType, JsonRTEFieldDataType, ModularBlocksDataType, ModuleConstructorParam, ReferenceFieldDataType, ContentTypeSchemaType } from '../types';
|
|
1
|
+
import { LogFn, ConfigType, ModularBlockType, ContentTypeStruct, GroupFieldDataType, RefErrorReturnType, CtConstructorParam, GlobalFieldDataType, JsonRTEFieldDataType, ModularBlocksDataType, ModuleConstructorParam, ReferenceFieldDataType, ContentTypeSchemaType, ExtensionOrAppFieldDataType } from '../types';
|
|
2
2
|
import auditConfig from '../config';
|
|
3
3
|
export default class ContentType {
|
|
4
4
|
log: LogFn;
|
|
@@ -8,6 +8,7 @@ export default class ContentType {
|
|
|
8
8
|
folderPath: string;
|
|
9
9
|
currentUid: string;
|
|
10
10
|
currentTitle: string;
|
|
11
|
+
extensions: string[];
|
|
11
12
|
inMemoryFix: boolean;
|
|
12
13
|
gfSchema: ContentTypeStruct[];
|
|
13
14
|
ctSchema: ContentTypeStruct[];
|
|
@@ -21,6 +22,12 @@ export default class ContentType {
|
|
|
21
22
|
* @returns the `missingRefs` object.
|
|
22
23
|
*/
|
|
23
24
|
run(returnFixSchema?: boolean): Promise<Record<string, any>>;
|
|
25
|
+
/**
|
|
26
|
+
* @method prerequisiteData
|
|
27
|
+
* The `prerequisiteData` function reads and parses JSON files to retrieve extension and marketplace
|
|
28
|
+
* app data, and stores them in the `extensions` array.
|
|
29
|
+
*/
|
|
30
|
+
prerequisiteData(): Promise<void>;
|
|
24
31
|
/**
|
|
25
32
|
* The function checks if it can write the fix content to a file and if so, it writes the content as
|
|
26
33
|
* JSON to the specified file path.
|
|
@@ -47,6 +54,15 @@ export default class ContentType {
|
|
|
47
54
|
* @returns an array of RefErrorReturnType.
|
|
48
55
|
*/
|
|
49
56
|
validateReferenceField(tree: Record<string, unknown>[], field: ReferenceFieldDataType): RefErrorReturnType[];
|
|
57
|
+
/**
|
|
58
|
+
* The function `validateExtensionAndAppsField` checks if a given field has a valid extension or app
|
|
59
|
+
* reference and returns any missing references.
|
|
60
|
+
* @param {Record<string, unknown>[]} tree - An array of objects representing a tree structure.
|
|
61
|
+
* @param {ExtensionOrAppFieldDataType} field - The `field` parameter is of type `ExtensionOrAppFieldDataType`.
|
|
62
|
+
* @returns The function `validateExtensionAndAppsField` returns an array of `RefErrorReturnType`
|
|
63
|
+
* objects.
|
|
64
|
+
*/
|
|
65
|
+
validateExtensionAndAppField(tree: Record<string, unknown>[], field: ExtensionOrAppFieldDataType): RefErrorReturnType[];
|
|
50
66
|
/**
|
|
51
67
|
* The function "validateGlobalField" asynchronously validates a global field by looking for a
|
|
52
68
|
* reference in a tree data structure.
|
|
@@ -130,6 +146,16 @@ export default class ContentType {
|
|
|
130
146
|
* @returns an array of `ModularBlockType` objects.
|
|
131
147
|
*/
|
|
132
148
|
fixModularBlocksReferences(tree: Record<string, unknown>[], blocks: ModularBlockType[]): ModularBlockType[];
|
|
149
|
+
/**
|
|
150
|
+
* The function checks for missing extension or app references in a given tree and fixes them if the
|
|
151
|
+
* fix flag is enabled.
|
|
152
|
+
* @param {Record<string, unknown>[]} tree - An array of objects representing a tree structure.
|
|
153
|
+
* @param {ExtensionOrAppFieldDataType} field - The `field` parameter is of type
|
|
154
|
+
* `ExtensionOrAppFieldDataType`.
|
|
155
|
+
* @returns If the `fix` flag is true and there are missing references (`missingRefs` is not empty),
|
|
156
|
+
* then `null` is returned. Otherwise, the `field` parameter is returned.
|
|
157
|
+
*/
|
|
158
|
+
fixMissingExtensionOrApp(tree: Record<string, unknown>[], field: ExtensionOrAppFieldDataType): ExtensionOrAppFieldDataType | null;
|
|
133
159
|
/**
|
|
134
160
|
* The function `fixMissingReferences` checks for missing references in a given tree and field, and
|
|
135
161
|
* attempts to fix them by removing the missing references from the field's `reference_to` array.
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const tslib_1 = require("tslib");
|
|
4
|
+
const map_1 = tslib_1.__importDefault(require("lodash/map"));
|
|
4
5
|
const find_1 = tslib_1.__importDefault(require("lodash/find"));
|
|
5
6
|
const isEmpty_1 = tslib_1.__importDefault(require("lodash/isEmpty"));
|
|
6
7
|
const path_1 = require("path");
|
|
@@ -11,6 +12,7 @@ const messages_1 = require("../messages");
|
|
|
11
12
|
generating a report in JSON and CSV formats. */
|
|
12
13
|
class ContentType {
|
|
13
14
|
constructor({ log, fix, config, moduleName, ctSchema, gfSchema }) {
|
|
15
|
+
this.extensions = [];
|
|
14
16
|
this.inMemoryFix = false;
|
|
15
17
|
this.schema = [];
|
|
16
18
|
this.missingRefs = {};
|
|
@@ -37,6 +39,7 @@ class ContentType {
|
|
|
37
39
|
return returnFixSchema ? [] : {};
|
|
38
40
|
}
|
|
39
41
|
this.schema = this.moduleName === 'content-types' ? this.ctSchema : this.gfSchema;
|
|
42
|
+
await this.prerequisiteData();
|
|
40
43
|
for (const schema of (_a = this.schema) !== null && _a !== void 0 ? _a : []) {
|
|
41
44
|
this.currentUid = schema.uid;
|
|
42
45
|
this.currentTitle = schema.title;
|
|
@@ -58,6 +61,32 @@ class ContentType {
|
|
|
58
61
|
}
|
|
59
62
|
return this.missingRefs;
|
|
60
63
|
}
|
|
64
|
+
/**
|
|
65
|
+
* @method prerequisiteData
|
|
66
|
+
* The `prerequisiteData` function reads and parses JSON files to retrieve extension and marketplace
|
|
67
|
+
* app data, and stores them in the `extensions` array.
|
|
68
|
+
*/
|
|
69
|
+
async prerequisiteData() {
|
|
70
|
+
var _a;
|
|
71
|
+
const extensionPath = (0, path_1.resolve)(this.config.basePath, 'extensions', 'extensions.json');
|
|
72
|
+
const marketplacePath = (0, path_1.resolve)(this.config.basePath, 'marketplace_apps', 'marketplace_apps.json');
|
|
73
|
+
if ((0, fs_1.existsSync)(extensionPath)) {
|
|
74
|
+
try {
|
|
75
|
+
this.extensions = Object.keys(JSON.parse((0, fs_1.readFileSync)(extensionPath, 'utf8')));
|
|
76
|
+
}
|
|
77
|
+
catch (error) { }
|
|
78
|
+
}
|
|
79
|
+
if ((0, fs_1.existsSync)(marketplacePath)) {
|
|
80
|
+
try {
|
|
81
|
+
const marketplaceApps = JSON.parse((0, fs_1.readFileSync)(marketplacePath, 'utf8'));
|
|
82
|
+
for (const app of marketplaceApps) {
|
|
83
|
+
const metaData = (0, map_1.default)((0, map_1.default)((_a = app === null || app === void 0 ? void 0 : app.ui_location) === null || _a === void 0 ? void 0 : _a.locations, 'meta').flat(), 'extension_uid').filter((val) => val);
|
|
84
|
+
this.extensions.push(...metaData);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
catch (error) { }
|
|
88
|
+
}
|
|
89
|
+
}
|
|
61
90
|
/**
|
|
62
91
|
* The function checks if it can write the fix content to a file and if so, it writes the content as
|
|
63
92
|
* JSON to the specified file path.
|
|
@@ -102,12 +131,13 @@ class ContentType {
|
|
|
102
131
|
await this.validateGlobalField([...tree, { uid: child.uid, name: child.display_name }], child);
|
|
103
132
|
break;
|
|
104
133
|
case 'json':
|
|
105
|
-
if (child.field_metadata.extension) {
|
|
106
|
-
if (!fixTypes.includes('json:
|
|
134
|
+
if ('extension' in child.field_metadata && child.field_metadata.extension) {
|
|
135
|
+
if (!fixTypes.includes('json:extension'))
|
|
107
136
|
continue;
|
|
108
137
|
// NOTE Custom field type
|
|
138
|
+
this.missingRefs[this.currentUid].push(...this.validateExtensionAndAppField([...tree, { uid: child.uid, name: child.display_name }], child));
|
|
109
139
|
}
|
|
110
|
-
else if (child.field_metadata.allow_json_rte) {
|
|
140
|
+
else if ('allow_json_rte' in child.field_metadata && child.field_metadata.allow_json_rte) {
|
|
111
141
|
if (!fixTypes.includes('json:rte'))
|
|
112
142
|
continue;
|
|
113
143
|
// NOTE JSON RTE field type
|
|
@@ -134,6 +164,39 @@ class ContentType {
|
|
|
134
164
|
validateReferenceField(tree, field) {
|
|
135
165
|
return this.validateReferenceToValues(tree, field);
|
|
136
166
|
}
|
|
167
|
+
/**
|
|
168
|
+
* The function `validateExtensionAndAppsField` checks if a given field has a valid extension or app
|
|
169
|
+
* reference and returns any missing references.
|
|
170
|
+
* @param {Record<string, unknown>[]} tree - An array of objects representing a tree structure.
|
|
171
|
+
* @param {ExtensionOrAppFieldDataType} field - The `field` parameter is of type `ExtensionOrAppFieldDataType`.
|
|
172
|
+
* @returns The function `validateExtensionAndAppsField` returns an array of `RefErrorReturnType`
|
|
173
|
+
* objects.
|
|
174
|
+
*/
|
|
175
|
+
validateExtensionAndAppField(tree, field) {
|
|
176
|
+
if (this.fix)
|
|
177
|
+
return [];
|
|
178
|
+
const missingRefs = [];
|
|
179
|
+
let { uid, extension_uid, display_name, data_type } = field;
|
|
180
|
+
if (!this.extensions.includes(extension_uid)) {
|
|
181
|
+
missingRefs.push({ uid, extension_uid, type: 'Extension or Apps' });
|
|
182
|
+
}
|
|
183
|
+
return missingRefs.length
|
|
184
|
+
? [
|
|
185
|
+
{
|
|
186
|
+
tree,
|
|
187
|
+
data_type,
|
|
188
|
+
missingRefs,
|
|
189
|
+
display_name,
|
|
190
|
+
ct_uid: this.currentUid,
|
|
191
|
+
name: this.currentTitle,
|
|
192
|
+
treeStr: tree
|
|
193
|
+
.map(({ name }) => name)
|
|
194
|
+
.filter((val) => val)
|
|
195
|
+
.join(' ➜ '),
|
|
196
|
+
},
|
|
197
|
+
]
|
|
198
|
+
: [];
|
|
199
|
+
}
|
|
137
200
|
/**
|
|
138
201
|
* The function "validateGlobalField" asynchronously validates a global field by looking for a
|
|
139
202
|
* reference in a tree data structure.
|
|
@@ -221,8 +284,9 @@ class ContentType {
|
|
|
221
284
|
let { reference_to, display_name, data_type } = field;
|
|
222
285
|
for (const reference of reference_to !== null && reference_to !== void 0 ? reference_to : []) {
|
|
223
286
|
// NOTE Can skip specific references keys (Ex, system defined keys can be skipped)
|
|
224
|
-
if (this.config.skipRefs.includes(reference))
|
|
287
|
+
if (this.config.skipRefs.includes(reference)) {
|
|
225
288
|
continue;
|
|
289
|
+
}
|
|
226
290
|
const refExist = (0, find_1.default)(this.ctSchema, { uid: reference });
|
|
227
291
|
if (!refExist) {
|
|
228
292
|
missingRefs.push(reference);
|
|
@@ -270,14 +334,14 @@ class ContentType {
|
|
|
270
334
|
case 'json':
|
|
271
335
|
case 'reference':
|
|
272
336
|
if (data_type === 'json') {
|
|
273
|
-
if (field.field_metadata.extension) {
|
|
337
|
+
if ('extension' in field.field_metadata && field.field_metadata.extension) {
|
|
274
338
|
// NOTE Custom field type
|
|
275
|
-
if (!fixTypes.includes('json:
|
|
339
|
+
if (!fixTypes.includes('json:extension'))
|
|
276
340
|
return field;
|
|
277
341
|
// NOTE Fix logic
|
|
278
|
-
return field;
|
|
342
|
+
return this.fixMissingExtensionOrApp(tree, field);
|
|
279
343
|
}
|
|
280
|
-
else if (field.field_metadata.allow_json_rte) {
|
|
344
|
+
else if ('allow_json_rte' in field.field_metadata && field.field_metadata.allow_json_rte) {
|
|
281
345
|
if (!fixTypes.includes('json:rte'))
|
|
282
346
|
return field;
|
|
283
347
|
return this.fixMissingReferences(tree, field);
|
|
@@ -369,10 +433,10 @@ class ContentType {
|
|
|
369
433
|
const refErrorObj = {
|
|
370
434
|
tree,
|
|
371
435
|
display_name,
|
|
372
|
-
fixStatus: 'Fixed',
|
|
373
|
-
missingRefs: [reference_to],
|
|
374
436
|
ct_uid: this.currentUid,
|
|
375
437
|
name: this.currentTitle,
|
|
438
|
+
missingRefs: [reference_to],
|
|
439
|
+
fixStatus: this.fix ? 'Fixed' : undefined,
|
|
376
440
|
treeStr: tree.map(({ name }) => name).join(' ➜ '),
|
|
377
441
|
};
|
|
378
442
|
if (!schema) {
|
|
@@ -397,6 +461,36 @@ class ContentType {
|
|
|
397
461
|
})
|
|
398
462
|
.filter((val) => val);
|
|
399
463
|
}
|
|
464
|
+
/**
|
|
465
|
+
* The function checks for missing extension or app references in a given tree and fixes them if the
|
|
466
|
+
* fix flag is enabled.
|
|
467
|
+
* @param {Record<string, unknown>[]} tree - An array of objects representing a tree structure.
|
|
468
|
+
* @param {ExtensionOrAppFieldDataType} field - The `field` parameter is of type
|
|
469
|
+
* `ExtensionOrAppFieldDataType`.
|
|
470
|
+
* @returns If the `fix` flag is true and there are missing references (`missingRefs` is not empty),
|
|
471
|
+
* then `null` is returned. Otherwise, the `field` parameter is returned.
|
|
472
|
+
*/
|
|
473
|
+
fixMissingExtensionOrApp(tree, field) {
|
|
474
|
+
const missingRefs = [];
|
|
475
|
+
const { uid, extension_uid, data_type, display_name } = field;
|
|
476
|
+
if (!this.extensions.includes(extension_uid)) {
|
|
477
|
+
missingRefs.push({ uid, extension_uid, type: 'Extension or Apps' });
|
|
478
|
+
}
|
|
479
|
+
if (this.fix && !(0, isEmpty_1.default)(missingRefs)) {
|
|
480
|
+
this.missingRefs[this.currentUid].push({
|
|
481
|
+
tree,
|
|
482
|
+
data_type,
|
|
483
|
+
missingRefs,
|
|
484
|
+
display_name,
|
|
485
|
+
fixStatus: 'Fixed',
|
|
486
|
+
ct_uid: this.currentUid,
|
|
487
|
+
name: this.currentTitle,
|
|
488
|
+
treeStr: tree.map(({ name }) => name).join(' ➜ '),
|
|
489
|
+
});
|
|
490
|
+
return null;
|
|
491
|
+
}
|
|
492
|
+
return field;
|
|
493
|
+
}
|
|
400
494
|
/**
|
|
401
495
|
* The function `fixMissingReferences` checks for missing references in a given tree and field, and
|
|
402
496
|
* attempts to fix them by removing the missing references from the field's `reference_to` array.
|
|
@@ -412,8 +506,9 @@ class ContentType {
|
|
|
412
506
|
const { reference_to, data_type, display_name } = field;
|
|
413
507
|
for (const reference of reference_to !== null && reference_to !== void 0 ? reference_to : []) {
|
|
414
508
|
// NOTE Can skip specific references keys (Ex, system defined keys can be skipped)
|
|
415
|
-
if (this.config.skipRefs.includes(reference))
|
|
509
|
+
if (this.config.skipRefs.includes(reference)) {
|
|
416
510
|
continue;
|
|
511
|
+
}
|
|
417
512
|
const refExist = (0, find_1.default)(this.ctSchema, { uid: reference });
|
|
418
513
|
if (!refExist) {
|
|
419
514
|
missingRefs.push(reference);
|
package/lib/modules/entries.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import auditConfig from '../config';
|
|
2
|
-
import { LogFn, Locale, ConfigType, EntryStruct, EntryFieldType, ModularBlockType, ContentTypeStruct, CtConstructorParam, GroupFieldDataType, GlobalFieldDataType, JsonRTEFieldDataType, ContentTypeSchemaType, ModularBlocksDataType, ModuleConstructorParam, ReferenceFieldDataType, EntryRefErrorReturnType, EntryGroupFieldDataType, EntryGlobalFieldDataType, EntryJsonRTEFieldDataType, EntryModularBlocksDataType, EntryReferenceFieldDataType } from '../types';
|
|
2
|
+
import { LogFn, Locale, ConfigType, EntryStruct, EntryFieldType, ModularBlockType, ContentTypeStruct, CtConstructorParam, GroupFieldDataType, GlobalFieldDataType, JsonRTEFieldDataType, ContentTypeSchemaType, ModularBlocksDataType, ModuleConstructorParam, ReferenceFieldDataType, EntryRefErrorReturnType, EntryGroupFieldDataType, EntryGlobalFieldDataType, EntryJsonRTEFieldDataType, EntryModularBlocksDataType, EntryReferenceFieldDataType, ExtensionOrAppFieldDataType, EntryExtensionOrAppFieldDataType } from '../types';
|
|
3
3
|
export default class Entries {
|
|
4
4
|
log: LogFn;
|
|
5
5
|
protected fix: boolean;
|
|
@@ -9,6 +9,7 @@ export default class Entries {
|
|
|
9
9
|
folderPath: string;
|
|
10
10
|
currentUid: string;
|
|
11
11
|
currentTitle: string;
|
|
12
|
+
extensions: string[];
|
|
12
13
|
gfSchema: ContentTypeStruct[];
|
|
13
14
|
ctSchema: ContentTypeStruct[];
|
|
14
15
|
protected entries: Record<string, EntryStruct>;
|
|
@@ -63,6 +64,28 @@ export default class Entries {
|
|
|
63
64
|
* `tree`, `fieldStructure`, and `field`.
|
|
64
65
|
*/
|
|
65
66
|
validateReferenceField(tree: Record<string, unknown>[], fieldStructure: ReferenceFieldDataType, field: EntryReferenceFieldDataType[]): EntryRefErrorReturnType[];
|
|
67
|
+
/**
|
|
68
|
+
* The function `validateExtensionAndAppField` checks if a given field has a valid extension
|
|
69
|
+
* reference and returns any missing references.
|
|
70
|
+
* @param {Record<string, unknown>[]} tree - An array of objects representing a tree structure.
|
|
71
|
+
* @param {ExtensionOrAppFieldDataType} fieldStructure - The `fieldStructure` parameter is of type
|
|
72
|
+
* `ExtensionOrAppFieldDataType` and represents the structure of a field in an extension or app. It
|
|
73
|
+
* contains properties such as `uid`, `display_name`, and `data_type`.
|
|
74
|
+
* @param {EntryExtensionOrAppFieldDataType} field - The `field` parameter is of type
|
|
75
|
+
* `EntryExtensionOrAppFieldDataType`, which is an object containing information about a specific
|
|
76
|
+
* field in an entry. It has the following properties:
|
|
77
|
+
* @returns an array containing an object if there are missing references. If there are no missing
|
|
78
|
+
* references, an empty array is returned.
|
|
79
|
+
*/
|
|
80
|
+
validateExtensionAndAppField(tree: Record<string, unknown>[], fieldStructure: ExtensionOrAppFieldDataType, field: EntryExtensionOrAppFieldDataType): {
|
|
81
|
+
tree: Record<string, unknown>[];
|
|
82
|
+
data_type: string;
|
|
83
|
+
missingRefs: any[];
|
|
84
|
+
display_name: string;
|
|
85
|
+
ct_uid: string;
|
|
86
|
+
name: string;
|
|
87
|
+
treeStr: string;
|
|
88
|
+
}[];
|
|
66
89
|
/**
|
|
67
90
|
* The function "validateGlobalField" is an asynchronous function that takes in a tree,
|
|
68
91
|
* fieldStructure, and field as parameters and looks for references in the tree.
|
|
@@ -129,6 +152,7 @@ export default class Entries {
|
|
|
129
152
|
* objects.
|
|
130
153
|
*/
|
|
131
154
|
validateReferenceValues(tree: Record<string, unknown>[], fieldStructure: ReferenceFieldDataType, field: EntryReferenceFieldDataType[]): EntryRefErrorReturnType[];
|
|
155
|
+
removeMissingKeysOnEntry(schema: ContentTypeSchemaType[], entry: EntryFieldType): void;
|
|
132
156
|
/**
|
|
133
157
|
* The function `runFixOnSchema` takes in a tree, schema, and entry, and applies fixes to the entry
|
|
134
158
|
* based on the schema.
|
|
@@ -169,6 +193,20 @@ export default class Entries {
|
|
|
169
193
|
* @returns the updated `entry` array after performing some modifications.
|
|
170
194
|
*/
|
|
171
195
|
fixModularBlocksReferences(tree: Record<string, unknown>[], blocks: ModularBlockType[], entry: EntryModularBlocksDataType[]): EntryModularBlocksDataType[];
|
|
196
|
+
/**
|
|
197
|
+
* The function `fixMissingExtensionOrApp` checks if a field in an entry has a valid extension or app
|
|
198
|
+
* reference, and fixes it if necessary.
|
|
199
|
+
* @param {Record<string, unknown>[]} tree - An array of objects representing a tree structure.
|
|
200
|
+
* @param {ExtensionOrAppFieldDataType} field - The `field` parameter is of type
|
|
201
|
+
* `ExtensionOrAppFieldDataType`, which is an object with properties `uid`, `display_name`, and
|
|
202
|
+
* `data_type`.
|
|
203
|
+
* @param {EntryExtensionOrAppFieldDataType} entry - The `entry` parameter is of type
|
|
204
|
+
* `EntryExtensionOrAppFieldDataType`, which is an object containing the data for a specific entry.
|
|
205
|
+
* It may have a property with the key specified by the `uid` variable, which represents the field
|
|
206
|
+
* that contains the extension or app data.
|
|
207
|
+
* @returns the `field` parameter.
|
|
208
|
+
*/
|
|
209
|
+
fixMissingExtensionOrApp(tree: Record<string, unknown>[], field: ExtensionOrAppFieldDataType, entry: EntryExtensionOrAppFieldDataType): ExtensionOrAppFieldDataType;
|
|
172
210
|
/**
|
|
173
211
|
* The function `fixGroupField` takes in a tree, a field, and an entry, and if the field has a
|
|
174
212
|
* schema, it runs a fix on the schema and returns the updated entry, otherwise it returns the
|
package/lib/modules/entries.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const tslib_1 = require("tslib");
|
|
4
|
+
const map_1 = tslib_1.__importDefault(require("lodash/map"));
|
|
4
5
|
const find_1 = tslib_1.__importDefault(require("lodash/find"));
|
|
5
6
|
const values_1 = tslib_1.__importDefault(require("lodash/values"));
|
|
6
7
|
const isEmpty_1 = tslib_1.__importDefault(require("lodash/isEmpty"));
|
|
@@ -9,9 +10,11 @@ const cli_utilities_1 = require("@contentstack/cli-utilities");
|
|
|
9
10
|
const fs_1 = require("fs");
|
|
10
11
|
const content_types_1 = tslib_1.__importDefault(require("./content-types"));
|
|
11
12
|
const messages_1 = require("../messages");
|
|
13
|
+
const util_1 = require("../util");
|
|
12
14
|
const global_fields_1 = tslib_1.__importDefault(require("./global-fields"));
|
|
13
15
|
class Entries {
|
|
14
16
|
constructor({ log, fix, config, moduleName, ctSchema, gfSchema }) {
|
|
17
|
+
this.extensions = [];
|
|
15
18
|
this.missingRefs = {};
|
|
16
19
|
this.entryMetaData = [];
|
|
17
20
|
this.moduleName = 'entries';
|
|
@@ -53,12 +56,17 @@ class Entries {
|
|
|
53
56
|
if (!this.missingRefs[this.currentUid]) {
|
|
54
57
|
this.missingRefs[this.currentUid] = [];
|
|
55
58
|
}
|
|
59
|
+
if (this.fix) {
|
|
60
|
+
this.removeMissingKeysOnEntry(ctSchema.schema, this.entries[entryUid]);
|
|
61
|
+
}
|
|
56
62
|
this.lookForReference([{ locale: code, uid, name: title }], ctSchema, this.entries[entryUid]);
|
|
57
|
-
|
|
63
|
+
const message = (0, messages_1.$t)(messages_1.auditMsg.SCAN_ENTRY_SUCCESS_MSG, {
|
|
58
64
|
title,
|
|
59
65
|
local: code,
|
|
60
66
|
module: this.config.moduleConfig.entries.name,
|
|
61
|
-
})
|
|
67
|
+
});
|
|
68
|
+
this.log(message, 'hidden');
|
|
69
|
+
(0, util_1.print)([{ message: `info: ${message}`, color: 'green' }]);
|
|
62
70
|
}
|
|
63
71
|
if (this.fix) {
|
|
64
72
|
await this.writeFixContent(`${basePath}/${indexer[fileIndex]}`, this.entries);
|
|
@@ -66,7 +74,7 @@ class Entries {
|
|
|
66
74
|
}
|
|
67
75
|
}
|
|
68
76
|
}
|
|
69
|
-
this.log('', 'info'); // Adding empty line
|
|
77
|
+
// this.log('', 'info'); // Adding empty line
|
|
70
78
|
this.removeEmptyVal();
|
|
71
79
|
return this.missingRefs;
|
|
72
80
|
}
|
|
@@ -85,6 +93,7 @@ class Entries {
|
|
|
85
93
|
* `gfSchema` properties using the `ContentType` class.
|
|
86
94
|
*/
|
|
87
95
|
async fixPrerequisiteData() {
|
|
96
|
+
var _a;
|
|
88
97
|
this.ctSchema = (await new content_types_1.default({
|
|
89
98
|
fix: true,
|
|
90
99
|
log: () => { },
|
|
@@ -101,6 +110,24 @@ class Entries {
|
|
|
101
110
|
ctSchema: this.ctSchema,
|
|
102
111
|
gfSchema: this.gfSchema,
|
|
103
112
|
}).run(true));
|
|
113
|
+
const extensionPath = (0, path_1.resolve)(this.config.basePath, 'extensions', 'extensions.json');
|
|
114
|
+
const marketplacePath = (0, path_1.resolve)(this.config.basePath, 'marketplace_apps', 'marketplace_apps.json');
|
|
115
|
+
if ((0, fs_1.existsSync)(extensionPath)) {
|
|
116
|
+
try {
|
|
117
|
+
this.extensions = Object.keys(JSON.parse((0, fs_1.readFileSync)(extensionPath, 'utf8')));
|
|
118
|
+
}
|
|
119
|
+
catch (error) { }
|
|
120
|
+
}
|
|
121
|
+
if ((0, fs_1.existsSync)(marketplacePath)) {
|
|
122
|
+
try {
|
|
123
|
+
const marketplaceApps = JSON.parse((0, fs_1.readFileSync)(marketplacePath, 'utf8'));
|
|
124
|
+
for (const app of marketplaceApps) {
|
|
125
|
+
const metaData = (0, map_1.default)((0, map_1.default)((_a = app === null || app === void 0 ? void 0 : app.ui_location) === null || _a === void 0 ? void 0 : _a.locations, 'meta').flat(), 'extension_uid').filter((val) => val);
|
|
126
|
+
this.extensions.push(...metaData);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
catch (error) { }
|
|
130
|
+
}
|
|
104
131
|
}
|
|
105
132
|
/**
|
|
106
133
|
* The function checks if it can write the fix content to a file and if so, it writes the content as
|
|
@@ -147,10 +174,11 @@ class Entries {
|
|
|
147
174
|
this.validateGlobalField([...tree, { uid: child.uid, name: child.display_name, field: uid }], child, entry[uid]);
|
|
148
175
|
break;
|
|
149
176
|
case 'json':
|
|
150
|
-
if (child.field_metadata.extension) {
|
|
177
|
+
if ('extension' in child.field_metadata && child.field_metadata.extension) {
|
|
178
|
+
this.missingRefs[this.currentUid].push(...this.validateExtensionAndAppField([...tree, { uid: child.uid, name: child.display_name, field: uid }], child, entry));
|
|
151
179
|
// NOTE Custom field type
|
|
152
180
|
}
|
|
153
|
-
else if (child.field_metadata.allow_json_rte) {
|
|
181
|
+
else if ('allow_json_rte' in child.field_metadata && child.field_metadata.allow_json_rte) {
|
|
154
182
|
// NOTE JSON RTE field type
|
|
155
183
|
this.validateJsonRTEFields([...tree, { uid: child.uid, name: child.display_name, field: uid }], child, entry[uid]);
|
|
156
184
|
}
|
|
@@ -180,6 +208,47 @@ class Entries {
|
|
|
180
208
|
validateReferenceField(tree, fieldStructure, field) {
|
|
181
209
|
return this.validateReferenceValues(tree, fieldStructure, field);
|
|
182
210
|
}
|
|
211
|
+
/**
|
|
212
|
+
* The function `validateExtensionAndAppField` checks if a given field has a valid extension
|
|
213
|
+
* reference and returns any missing references.
|
|
214
|
+
* @param {Record<string, unknown>[]} tree - An array of objects representing a tree structure.
|
|
215
|
+
* @param {ExtensionOrAppFieldDataType} fieldStructure - The `fieldStructure` parameter is of type
|
|
216
|
+
* `ExtensionOrAppFieldDataType` and represents the structure of a field in an extension or app. It
|
|
217
|
+
* contains properties such as `uid`, `display_name`, and `data_type`.
|
|
218
|
+
* @param {EntryExtensionOrAppFieldDataType} field - The `field` parameter is of type
|
|
219
|
+
* `EntryExtensionOrAppFieldDataType`, which is an object containing information about a specific
|
|
220
|
+
* field in an entry. It has the following properties:
|
|
221
|
+
* @returns an array containing an object if there are missing references. If there are no missing
|
|
222
|
+
* references, an empty array is returned.
|
|
223
|
+
*/
|
|
224
|
+
validateExtensionAndAppField(tree, fieldStructure, field) {
|
|
225
|
+
if (this.fix)
|
|
226
|
+
return [];
|
|
227
|
+
const missingRefs = [];
|
|
228
|
+
let { uid, display_name, data_type } = fieldStructure || {};
|
|
229
|
+
if (field[uid]) {
|
|
230
|
+
let { metadata: { extension_uid } = { extension_uid: '' } } = field[uid] || {};
|
|
231
|
+
if (extension_uid && !this.extensions.includes(extension_uid)) {
|
|
232
|
+
missingRefs.push({ uid, extension_uid, type: 'Extension or Apps' });
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
return missingRefs.length
|
|
236
|
+
? [
|
|
237
|
+
{
|
|
238
|
+
tree,
|
|
239
|
+
data_type,
|
|
240
|
+
missingRefs,
|
|
241
|
+
display_name,
|
|
242
|
+
ct_uid: this.currentUid,
|
|
243
|
+
name: this.currentTitle,
|
|
244
|
+
treeStr: tree
|
|
245
|
+
.map(({ name }) => name)
|
|
246
|
+
.filter((val) => val)
|
|
247
|
+
.join(' ➜ '),
|
|
248
|
+
},
|
|
249
|
+
]
|
|
250
|
+
: [];
|
|
251
|
+
}
|
|
183
252
|
/**
|
|
184
253
|
* The function "validateGlobalField" is an asynchronous function that takes in a tree,
|
|
185
254
|
* fieldStructure, and field as parameters and looks for references in the tree.
|
|
@@ -317,6 +386,17 @@ class Entries {
|
|
|
317
386
|
]
|
|
318
387
|
: [];
|
|
319
388
|
}
|
|
389
|
+
removeMissingKeysOnEntry(schema, entry) {
|
|
390
|
+
// NOTE remove invalid entry keys
|
|
391
|
+
const ctFields = (0, map_1.default)(schema, 'uid');
|
|
392
|
+
const entryFields = Object.keys(entry !== null && entry !== void 0 ? entry : {});
|
|
393
|
+
entryFields.forEach((eKey) => {
|
|
394
|
+
// NOTE Key should not be system key and not exist in schema means it's invalid entry key
|
|
395
|
+
if (!this.config.entries.systemKeys.includes(eKey) && !ctFields.includes(eKey)) {
|
|
396
|
+
delete entry[eKey];
|
|
397
|
+
}
|
|
398
|
+
});
|
|
399
|
+
}
|
|
320
400
|
/**
|
|
321
401
|
* The function `runFixOnSchema` takes in a tree, schema, and entry, and applies fixes to the entry
|
|
322
402
|
* based on the schema.
|
|
@@ -345,11 +425,12 @@ class Entries {
|
|
|
345
425
|
case 'json':
|
|
346
426
|
case 'reference':
|
|
347
427
|
if (data_type === 'json') {
|
|
348
|
-
if (field.field_metadata.extension) {
|
|
428
|
+
if ('extension' in field.field_metadata && field.field_metadata.extension) {
|
|
349
429
|
// NOTE Custom field type
|
|
430
|
+
this.fixMissingExtensionOrApp([...tree, { uid: field.uid, name: field.display_name, data_type: field.data_type }], field, entry);
|
|
350
431
|
break;
|
|
351
432
|
}
|
|
352
|
-
else if (field.field_metadata.allow_json_rte) {
|
|
433
|
+
else if ('allow_json_rte' in field.field_metadata && field.field_metadata.allow_json_rte) {
|
|
353
434
|
this.fixJsonRteMissingReferences([...tree, { uid: field.uid, name: field.display_name, data_type: field.data_type }], field, entry[uid]);
|
|
354
435
|
break;
|
|
355
436
|
}
|
|
@@ -410,6 +491,43 @@ class Entries {
|
|
|
410
491
|
});
|
|
411
492
|
return entry;
|
|
412
493
|
}
|
|
494
|
+
/**
|
|
495
|
+
* The function `fixMissingExtensionOrApp` checks if a field in an entry has a valid extension or app
|
|
496
|
+
* reference, and fixes it if necessary.
|
|
497
|
+
* @param {Record<string, unknown>[]} tree - An array of objects representing a tree structure.
|
|
498
|
+
* @param {ExtensionOrAppFieldDataType} field - The `field` parameter is of type
|
|
499
|
+
* `ExtensionOrAppFieldDataType`, which is an object with properties `uid`, `display_name`, and
|
|
500
|
+
* `data_type`.
|
|
501
|
+
* @param {EntryExtensionOrAppFieldDataType} entry - The `entry` parameter is of type
|
|
502
|
+
* `EntryExtensionOrAppFieldDataType`, which is an object containing the data for a specific entry.
|
|
503
|
+
* It may have a property with the key specified by the `uid` variable, which represents the field
|
|
504
|
+
* that contains the extension or app data.
|
|
505
|
+
* @returns the `field` parameter.
|
|
506
|
+
*/
|
|
507
|
+
fixMissingExtensionOrApp(tree, field, entry) {
|
|
508
|
+
const missingRefs = [];
|
|
509
|
+
let { uid, display_name, data_type } = field || {};
|
|
510
|
+
if (entry[uid]) {
|
|
511
|
+
let { metadata: { extension_uid } = { extension_uid: '' } } = entry[uid] || {};
|
|
512
|
+
if (extension_uid && !this.extensions.includes(extension_uid)) {
|
|
513
|
+
missingRefs.push({ uid, extension_uid, type: 'Extension or Apps' });
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
if (this.fix && !(0, isEmpty_1.default)(missingRefs)) {
|
|
517
|
+
this.missingRefs[this.currentUid].push({
|
|
518
|
+
tree,
|
|
519
|
+
data_type,
|
|
520
|
+
missingRefs,
|
|
521
|
+
display_name,
|
|
522
|
+
fixStatus: 'Fixed',
|
|
523
|
+
ct_uid: this.currentUid,
|
|
524
|
+
name: this.currentTitle,
|
|
525
|
+
treeStr: tree.map(({ name }) => name).join(' ➜ '),
|
|
526
|
+
});
|
|
527
|
+
delete entry[uid];
|
|
528
|
+
}
|
|
529
|
+
return field;
|
|
530
|
+
}
|
|
413
531
|
/**
|
|
414
532
|
* The function `fixGroupField` takes in a tree, a field, and an entry, and if the field has a
|
|
415
533
|
* schema, it runs a fix on the schema and returns the updated entry, otherwise it returns the
|
package/lib/types/common.d.ts
CHANGED
|
@@ -2,4 +2,7 @@ import { ux } from "@contentstack/cli-utilities";
|
|
|
2
2
|
type IFlags = typeof ux.table.Flags;
|
|
3
3
|
type IncludeFlags<T, K extends keyof T> = Pick<T, K>;
|
|
4
4
|
type CommandNames = 'cm:stacks:audit' | 'cm:stacks:audit:fix';
|
|
5
|
-
|
|
5
|
+
interface AnyProperty {
|
|
6
|
+
[propName: string]: any;
|
|
7
|
+
}
|
|
8
|
+
export { IFlags, IncludeFlags, CommandNames, AnyProperty };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import config from '../config';
|
|
2
|
+
import { AnyProperty } from './common';
|
|
2
3
|
import { ConfigType, LogFn } from './utils';
|
|
3
|
-
type ContentTypeSchemaType = ReferenceFieldDataType | GlobalFieldDataType |
|
|
4
|
+
type ContentTypeSchemaType = ReferenceFieldDataType | GlobalFieldDataType | ExtensionOrAppFieldDataType | JsonRTEFieldDataType | GroupFieldDataType | ModularBlocksDataType;
|
|
4
5
|
type ContentTypeStruct = {
|
|
5
6
|
uid: string;
|
|
6
7
|
title: string;
|
|
@@ -22,10 +23,9 @@ type CommonDataTypeStruct = {
|
|
|
22
23
|
data_type: string;
|
|
23
24
|
display_name: string;
|
|
24
25
|
field_metadata: {
|
|
25
|
-
extension: boolean;
|
|
26
26
|
ref_multiple: boolean;
|
|
27
27
|
allow_json_rte: boolean;
|
|
28
|
-
} &
|
|
28
|
+
} & AnyProperty;
|
|
29
29
|
};
|
|
30
30
|
type RefErrorReturnType = {
|
|
31
31
|
name: string;
|
|
@@ -44,9 +44,11 @@ type GlobalFieldDataType = CommonDataTypeStruct & {
|
|
|
44
44
|
reference_to?: string;
|
|
45
45
|
schema: GlobalFieldSchemaTypes[];
|
|
46
46
|
};
|
|
47
|
-
type
|
|
48
|
-
reference_to: string[];
|
|
47
|
+
type ExtensionOrAppFieldDataType = Omit<CommonDataTypeStruct, 'field_metadata'> & {
|
|
49
48
|
extension_uid: string;
|
|
49
|
+
field_metadata: {
|
|
50
|
+
extension: boolean;
|
|
51
|
+
};
|
|
50
52
|
};
|
|
51
53
|
type JsonRTEFieldDataType = CommonDataTypeStruct & {
|
|
52
54
|
reference_to: string[];
|
|
@@ -58,13 +60,13 @@ type ModularBlockType = {
|
|
|
58
60
|
uid: string;
|
|
59
61
|
title: string;
|
|
60
62
|
reference_to?: string;
|
|
61
|
-
schema: (JsonRTEFieldDataType | ModularBlocksDataType | ReferenceFieldDataType |
|
|
63
|
+
schema: (JsonRTEFieldDataType | ModularBlocksDataType | ReferenceFieldDataType | ExtensionOrAppFieldDataType | GroupFieldDataType)[];
|
|
62
64
|
};
|
|
63
65
|
type ModularBlocksDataType = CommonDataTypeStruct & {
|
|
64
66
|
blocks: ModularBlockType[];
|
|
65
67
|
};
|
|
66
|
-
type GroupFieldSchemaTypes = GroupFieldDataType | CommonDataTypeStruct | GlobalFieldDataType | ReferenceFieldDataType;
|
|
67
|
-
type GlobalFieldSchemaTypes = ReferenceFieldDataType | GroupFieldDataType |
|
|
68
|
+
type GroupFieldSchemaTypes = GroupFieldDataType | CommonDataTypeStruct | GlobalFieldDataType | ReferenceFieldDataType | ExtensionOrAppFieldDataType;
|
|
69
|
+
type GlobalFieldSchemaTypes = ReferenceFieldDataType | GroupFieldDataType | ExtensionOrAppFieldDataType;
|
|
68
70
|
type ModularBlocksSchemaTypes = ReferenceFieldDataType | JsonRTEFieldDataType;
|
|
69
71
|
declare enum OutputColumn {
|
|
70
72
|
Title = "name",
|
|
@@ -73,4 +75,4 @@ declare enum OutputColumn {
|
|
|
73
75
|
'Missing references' = "missingRefs",
|
|
74
76
|
Path = "treeStr"
|
|
75
77
|
}
|
|
76
|
-
export { CtConstructorParam, ContentTypeStruct, ModuleConstructorParam, ReferenceFieldDataType, GlobalFieldDataType,
|
|
78
|
+
export { CtConstructorParam, ContentTypeStruct, ModuleConstructorParam, ReferenceFieldDataType, GlobalFieldDataType, ExtensionOrAppFieldDataType, JsonRTEFieldDataType, GroupFieldDataType, ModularBlocksDataType, RefErrorReturnType, ModularBlocksSchemaTypes, ModularBlockType, OutputColumn, ContentTypeSchemaType, GlobalFieldSchemaTypes, };
|
package/lib/types/entries.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { AnyProperty } from './common';
|
|
1
2
|
type Locale = {
|
|
2
3
|
uid: string;
|
|
3
4
|
code: string;
|
|
@@ -17,8 +18,13 @@ type EntryReferenceFieldDataType = {
|
|
|
17
18
|
type EntryGlobalFieldDataType = {
|
|
18
19
|
[key: string]: EntryStruct;
|
|
19
20
|
};
|
|
20
|
-
type
|
|
21
|
-
[key: string]:
|
|
21
|
+
type EntryExtensionOrAppFieldDataType = {
|
|
22
|
+
[key: string]: {
|
|
23
|
+
uid: string;
|
|
24
|
+
metadata: {
|
|
25
|
+
extension_uid: string;
|
|
26
|
+
} & AnyProperty;
|
|
27
|
+
};
|
|
22
28
|
};
|
|
23
29
|
type EntryJsonRTEFieldDataType = {
|
|
24
30
|
uid: string;
|
|
@@ -45,5 +51,5 @@ type EntryRefErrorReturnType = {
|
|
|
45
51
|
tree: Record<string, unknown>[];
|
|
46
52
|
missingRefs: string[] | Record<string, unknown>[];
|
|
47
53
|
};
|
|
48
|
-
type EntryFieldType = EntryStruct | EntryGlobalFieldDataType | EntryModularBlocksDataType | EntryGroupFieldDataType;
|
|
49
|
-
export { Locale, EntryStruct, EntryFieldType, EntryGlobalFieldDataType,
|
|
54
|
+
type EntryFieldType = EntryStruct | EntryGlobalFieldDataType | EntryModularBlocksDataType | EntryGroupFieldDataType | EntryExtensionOrAppFieldDataType;
|
|
55
|
+
export { Locale, EntryStruct, EntryFieldType, EntryGlobalFieldDataType, EntryExtensionOrAppFieldDataType, EntryJsonRTEFieldDataType, EntryGroupFieldDataType, EntryModularBlocksDataType, EntryReferenceFieldDataType, EntryRefErrorReturnType, GroupFieldType, EntryModularBlockType, };
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
type AppLocation = 'cs.cm.stack.config' | 'cs.cm.stack.dashboard' | 'cs.cm.stack.sidebar' | 'cs.cm.stack.custom_field' | 'cs.cm.stack.rte' | 'cs.cm.stack.asset_sidebar' | 'cs.org.config';
|
|
2
|
+
interface ExtensionMeta {
|
|
3
|
+
uid?: string;
|
|
4
|
+
name?: string;
|
|
5
|
+
description?: string;
|
|
6
|
+
path?: string;
|
|
7
|
+
signed: boolean;
|
|
8
|
+
extension_uid?: string;
|
|
9
|
+
data_type?: string;
|
|
10
|
+
enabled?: boolean;
|
|
11
|
+
width?: number;
|
|
12
|
+
blur?: boolean;
|
|
13
|
+
default_width?: 'full' | 'half';
|
|
14
|
+
}
|
|
15
|
+
interface Extension {
|
|
16
|
+
type: AppLocation;
|
|
17
|
+
meta: ExtensionMeta[];
|
|
18
|
+
}
|
|
19
|
+
interface LocationConfiguration {
|
|
20
|
+
signed: boolean;
|
|
21
|
+
base_url: string;
|
|
22
|
+
locations: Extension[];
|
|
23
|
+
}
|
|
24
|
+
type MarketplaceAppsInstallationData = {
|
|
25
|
+
uid: string;
|
|
26
|
+
status: string;
|
|
27
|
+
configuration: any;
|
|
28
|
+
server_configuration: any;
|
|
29
|
+
target: {
|
|
30
|
+
type: string;
|
|
31
|
+
uid: string;
|
|
32
|
+
};
|
|
33
|
+
ui_location: LocationConfiguration;
|
|
34
|
+
} & AnyProperty;
|
|
35
|
+
interface AnyProperty {
|
|
36
|
+
[propName: string]: any;
|
|
37
|
+
}
|
|
38
|
+
export { MarketplaceAppsInstallationData };
|
package/lib/types/utils.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Color } from "chalk";
|
|
2
2
|
import { PrintOptions } from "@contentstack/cli-utilities";
|
|
3
3
|
import config from "../config";
|
|
4
|
-
type LogFn = (message: string | any, logType?: LoggerType | PrintOptions | undefined) => void;
|
|
4
|
+
type LogFn = (message: string | any, logType?: LoggerType | PrintOptions | undefined, skipCredentialCheck?: boolean) => void;
|
|
5
5
|
type ExitFn = (code?: number | undefined) => void;
|
|
6
6
|
type Partial<T> = {
|
|
7
7
|
[P in keyof T]?: T[P];
|
|
@@ -10,7 +10,7 @@ type ConfigType = {
|
|
|
10
10
|
config?: string;
|
|
11
11
|
} & typeof config & Record<string, any>;
|
|
12
12
|
export { LogFn, ExitFn, Partial, ConfigType };
|
|
13
|
-
export type LoggerType = "info" | "warn" | "error" | "debug";
|
|
13
|
+
export type LoggerType = "info" | "warn" | "error" | "debug" | 'hidden';
|
|
14
14
|
export type PrintType = {
|
|
15
15
|
message: string;
|
|
16
16
|
bold?: boolean;
|
package/lib/util/log.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ export default class Logger {
|
|
|
5
5
|
private infoLogger;
|
|
6
6
|
private errorLogger;
|
|
7
7
|
private config;
|
|
8
|
+
private hiddenInfoLogger;
|
|
8
9
|
get loggerOptions(): winston.transports.FileTransportOptions;
|
|
9
10
|
constructor(config: Record<string, any>);
|
|
10
11
|
/**
|
|
@@ -24,7 +25,7 @@ export default class Logger {
|
|
|
24
25
|
* @param {LoggerType | PrintOptions | undefined} [logType] - The `logType` parameter is an optional
|
|
25
26
|
* parameter that specifies the type of log. It can be one of the following values:
|
|
26
27
|
*/
|
|
27
|
-
log(message: string | any, logType?: LoggerType | PrintOptions | undefined): void;
|
|
28
|
+
log(message: string | any, logType?: LoggerType | PrintOptions | undefined, skipCredentialCheck?: boolean): void;
|
|
28
29
|
/**
|
|
29
30
|
* The function `returnString` takes a message as input and returns a modified version of the message
|
|
30
31
|
* with sensitive credentials replaced and any ANSI escape codes removed.
|
package/lib/util/log.js
CHANGED
|
@@ -35,6 +35,7 @@ class Logger {
|
|
|
35
35
|
this.config = config;
|
|
36
36
|
this.infoLogger = this.getLoggerInstance('info');
|
|
37
37
|
this.errorLogger = this.getLoggerInstance('error');
|
|
38
|
+
this.hiddenInfoLogger = this.getLoggerInstance('hidden');
|
|
38
39
|
}
|
|
39
40
|
/**
|
|
40
41
|
* The function getLoggerInstance creates and returns a winston logger instance based on the provided
|
|
@@ -48,15 +49,20 @@ class Logger {
|
|
|
48
49
|
const consoleOptions = {
|
|
49
50
|
format: winston_1.default.format.combine(winston_1.default.format.simple(), winston_1.default.format.colorize({ all: true })),
|
|
50
51
|
};
|
|
52
|
+
const isHidden = logType === 'hidden';
|
|
53
|
+
logType = logType === 'hidden' ? 'info' : logType;
|
|
51
54
|
if (logType === 'error') {
|
|
52
55
|
consoleOptions.level = logType;
|
|
53
56
|
}
|
|
54
57
|
const filename = (0, path_1.normalize)((0, path_1.resolve)(this.config.basePath, 'logs', `${logType}.log`)).replace(/^(\.\.(\/|\\|$))+/, '');
|
|
58
|
+
const transports = [
|
|
59
|
+
new winston_1.default.transports.File(Object.assign(Object.assign({}, this.loggerOptions), { level: logType, filename })),
|
|
60
|
+
];
|
|
61
|
+
if (!isHidden) {
|
|
62
|
+
transports.push(new winston_1.default.transports.Console(consoleOptions));
|
|
63
|
+
}
|
|
55
64
|
const loggerOptions = {
|
|
56
|
-
transports
|
|
57
|
-
new winston_1.default.transports.File(Object.assign(Object.assign({}, this.loggerOptions), { level: logType, filename })),
|
|
58
|
-
new winston_1.default.transports.Console(consoleOptions),
|
|
59
|
-
],
|
|
65
|
+
transports,
|
|
60
66
|
levels: customLevels.levels,
|
|
61
67
|
};
|
|
62
68
|
if (logType === 'error') {
|
|
@@ -72,8 +78,8 @@ class Logger {
|
|
|
72
78
|
* @param {LoggerType | PrintOptions | undefined} [logType] - The `logType` parameter is an optional
|
|
73
79
|
* parameter that specifies the type of log. It can be one of the following values:
|
|
74
80
|
*/
|
|
75
|
-
log(message, logType) {
|
|
76
|
-
const logString = this.returnString(message);
|
|
81
|
+
log(message, logType, skipCredentialCheck = false) {
|
|
82
|
+
const logString = skipCredentialCheck ? message : this.returnString(message);
|
|
77
83
|
switch (logType) {
|
|
78
84
|
case 'info':
|
|
79
85
|
case 'debug':
|
|
@@ -83,6 +89,9 @@ class Logger {
|
|
|
83
89
|
case 'error':
|
|
84
90
|
this.errorLogger.error(logString);
|
|
85
91
|
break;
|
|
92
|
+
case 'hidden':
|
|
93
|
+
this.hiddenInfoLogger.log('info', logString);
|
|
94
|
+
break;
|
|
86
95
|
default:
|
|
87
96
|
cli_utilities_1.cliux.print(logString, logType || {});
|
|
88
97
|
break;
|
package/oclif.manifest.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "1.
|
|
2
|
+
"version": "1.4.1",
|
|
3
3
|
"commands": {
|
|
4
4
|
"cm:stacks:audit:fix": {
|
|
5
5
|
"id": "cm:stacks:audit:fix",
|
|
@@ -85,7 +85,7 @@
|
|
|
85
85
|
"reference",
|
|
86
86
|
"global_field",
|
|
87
87
|
"json:rte",
|
|
88
|
-
"json:
|
|
88
|
+
"json:extension",
|
|
89
89
|
"blocks",
|
|
90
90
|
"group"
|
|
91
91
|
]
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contentstack/cli-audit",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.1",
|
|
4
4
|
"description": "Contentstack audit plugin",
|
|
5
5
|
"author": "Contentstack CLI",
|
|
6
6
|
"homepage": "https://github.com/contentstack/cli",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
],
|
|
20
20
|
"dependencies": {
|
|
21
21
|
"@contentstack/cli-command": "~1.2.16",
|
|
22
|
-
"@contentstack/cli-utilities": "~1.5.
|
|
22
|
+
"@contentstack/cli-utilities": "~1.5.12",
|
|
23
23
|
"@oclif/plugin-help": "^5",
|
|
24
24
|
"@oclif/plugin-plugins": "^4.1.9",
|
|
25
25
|
"chalk": "^4.1.2",
|