Skip to content

Commit 9ffdb37

Browse files
committed
Feat: Added Field Rules for Audit and Audit fix
1 parent 3615ace commit 9ffdb37

8 files changed

Lines changed: 460 additions & 26 deletions

File tree

packages/contentstack-audit/src/audit-base-command.ts

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,11 @@ import { join, resolve } from 'path';
77
import cloneDeep from 'lodash/cloneDeep';
88
import { cliux, sanitizePath, ux } from '@contentstack/cli-utilities';
99
import { createWriteStream, existsSync, mkdirSync, readFileSync, writeFileSync, rmSync } from 'fs';
10-
1110
import config from './config';
1211
import { print } from './util/log';
1312
import { auditMsg } from './messages';
1413
import { BaseCommand } from './base-command';
15-
import { Entries, GlobalField, ContentType, Extensions, Workflows, Assets } from './modules';
14+
import { Entries, GlobalField, ContentType, Extensions, Workflows, Assets, FieldRule } from './modules';
1615
import {
1716
CommandNames,
1817
ContentTypeStruct,
@@ -60,7 +59,8 @@ export abstract class AuditBaseCommand extends BaseCommand<typeof AuditBaseComma
6059
missingTitleFields,
6160
missingRefInCustomRoles,
6261
missingEnvLocalesInAssets,
63-
missingEnvLocalesInEntries
62+
missingEnvLocalesInEntries,
63+
missingFieldRules
6464
} = await this.scanAndFix();
6565

6666
this.showOutputOnScreen([
@@ -70,6 +70,7 @@ export abstract class AuditBaseCommand extends BaseCommand<typeof AuditBaseComma
7070
]);
7171
this.showOutputOnScreenWorkflowsAndExtension([{ module: 'Extensions', missingRefs: missingCtRefsInExtensions }]);
7272
this.showOutputOnScreenWorkflowsAndExtension([{ module: 'Workflows', missingRefs: missingCtRefsInWorkflow }]);
73+
7374
this.showOutputOnScreenWorkflowsAndExtension([{ module: 'Entries Select Field', missingRefs: missingSelectFeild }]);
7475
this.showOutputOnScreenWorkflowsAndExtension([
7576
{ module: 'Entries Mandatory Field', missingRefs: missingMandatoryFields },
@@ -80,6 +81,8 @@ export abstract class AuditBaseCommand extends BaseCommand<typeof AuditBaseComma
8081
this.showOutputOnScreenWorkflowsAndExtension([{ module: 'Custom Roles', missingRefs: missingRefInCustomRoles }]);
8182
this.showOutputOnScreenWorkflowsAndExtension([{ module: 'Assets', missingRefs: missingEnvLocalesInAssets }]);
8283
this.showOutputOnScreenWorkflowsAndExtension([{ module: 'Entries Missing Locale and Environments', missingRefs: missingEnvLocalesInEntries }])
84+
this.showOutputOnScreenWorkflowsAndExtension([{ module: 'Field Rules', missingRefs: missingFieldRules }])
85+
8386
if (
8487
!isEmpty(missingCtRefs) ||
8588
!isEmpty(missingGfRefs) ||
@@ -90,7 +93,8 @@ export abstract class AuditBaseCommand extends BaseCommand<typeof AuditBaseComma
9093
!isEmpty(missingTitleFields) ||
9194
!isEmpty(missingRefInCustomRoles) ||
9295
!isEmpty(missingEnvLocalesInAssets) ||
93-
!isEmpty(missingEnvLocalesInEntries)
96+
!isEmpty(missingEnvLocalesInEntries) ||
97+
!isEmpty(missingSelectFeild)
9498
) {
9599
if (this.currentCommand === 'cm:stacks:audit') {
96100
this.log(this.$t(auditMsg.FINAL_REPORT_PATH, { path: this.sharedConfig.reportPath }), 'warn');
@@ -120,7 +124,8 @@ export abstract class AuditBaseCommand extends BaseCommand<typeof AuditBaseComma
120124
!isEmpty(missingSelectFeild) ||
121125
!isEmpty(missingRefInCustomRoles) ||
122126
!isEmpty(missingEnvLocalesInAssets) ||
123-
!isEmpty(missingEnvLocalesInEntries)
127+
!isEmpty(missingEnvLocalesInEntries) ||
128+
!isEmpty(missingFieldRules)
124129
);
125130
}
126131

@@ -143,7 +148,8 @@ export abstract class AuditBaseCommand extends BaseCommand<typeof AuditBaseComma
143148
missingTitleFields,
144149
missingRefInCustomRoles,
145150
missingEnvLocalesInAssets,
146-
missingEnvLocalesInEntries;
151+
missingEnvLocalesInEntries,
152+
missingFieldRules;
147153

148154
for (const module of this.sharedConfig.flags.modules || this.sharedConfig.modules) {
149155
print([
@@ -211,6 +217,10 @@ export abstract class AuditBaseCommand extends BaseCommand<typeof AuditBaseComma
211217
missingRefInCustomRoles = await new CustomRoles(cloneDeep(constructorParam)).run();
212218
await this.prepareReport(module, missingRefInCustomRoles);
213219
break;
220+
case 'field-rules':
221+
missingFieldRules = await new FieldRule(cloneDeep(constructorParam)).run();
222+
await this.prepareReport(module, missingFieldRules);
223+
break;
214224
}
215225

216226
print([
@@ -238,7 +248,8 @@ export abstract class AuditBaseCommand extends BaseCommand<typeof AuditBaseComma
238248
missingTitleFields,
239249
missingRefInCustomRoles,
240250
missingEnvLocalesInAssets,
241-
missingEnvLocalesInEntries
251+
missingEnvLocalesInEntries,
252+
missingFieldRules
242253
};
243254
}
244255

@@ -435,7 +446,7 @@ export abstract class AuditBaseCommand extends BaseCommand<typeof AuditBaseComma
435446
* @returns The function `prepareReport` returns a Promise that resolves to `void`.
436447
*/
437448
prepareReport(
438-
moduleName: keyof typeof config.moduleConfig | keyof typeof config.ReportTitleForEntries,
449+
moduleName: keyof typeof config.moduleConfig | keyof typeof config.ReportTitleForEntries | 'field-rules',
439450
listOfMissingRefs: Record<string, any>,
440451
): Promise<void> {
441452
if (isEmpty(listOfMissingRefs)) return Promise.resolve(void 0);
@@ -462,7 +473,7 @@ export abstract class AuditBaseCommand extends BaseCommand<typeof AuditBaseComma
462473
* @returns The function `prepareCSV` returns a Promise that resolves to `void`.
463474
*/
464475
prepareCSV(
465-
moduleName: keyof typeof config.moduleConfig | keyof typeof config.ReportTitleForEntries,
476+
moduleName: keyof typeof config.moduleConfig | keyof typeof config.ReportTitleForEntries | 'field-rules',
466477
listOfMissingRefs: Record<string, any>,
467478
): Promise<void> {
468479
if (Object.keys(config.moduleConfig).includes(moduleName) || config.feild_level_modules.includes(moduleName)) {

packages/contentstack-audit/src/config/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ const config = {
22
showTerminalOutput: true,
33
skipRefs: ['sys_assets'],
44
skipFieldTypes: ['taxonomy', 'group'],
5-
modules: ['content-types', 'global-fields', 'entries', 'extensions', 'workflows', 'custom-roles', 'assets'],
5+
modules: ['content-types', 'global-fields', 'entries', 'extensions', 'workflows', 'custom-roles', 'assets', 'field-rules'],
66
'fix-fields': ['reference', 'global_field', 'json:rte', 'json:extension', 'blocks', 'group', 'content_types'],
77
moduleConfig: {
88
'content-types': {
@@ -103,7 +103,7 @@ const config = {
103103
Entry_Missing_Locale_and_Env: 'Entry_Missing_Locale_and_Env',
104104
Entry_Missing_Locale_and_Env_in_Publish_Details: 'Entry_Missing_Locale_and_Env_in_Publish_Details'
105105
},
106-
feild_level_modules: ['Entries_Title_feild', 'Entries_Mandatory_feild', 'Entries_Select_feild', 'Entry_Missing_Locale_and_Env_in_Publish_Details'],
106+
feild_level_modules: ['Entries_Title_feild', 'Entries_Mandatory_feild', 'Entries_Select_feild', 'Entry_Missing_Locale_and_Env_in_Publish_Details', 'field-rules'],
107107
fixSelectField: false
108108
};
109109

packages/contentstack-audit/src/messages/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ const auditMsg = {
4343
CT_REFERENCE_FIELD: `The mentioned Reference field is not Array field reference is '{reference_to}' having display name '{display_name}''`,
4444
ASSET_NOT_EXIST: `The publish_details either does not exist or is not an array for asset uid '{uid}'`,
4545
ENTRY_PUBLISH_DETAILS_NOT_EXIST: `The publish_details either does not exist or is not an array for entry uid '{uid}'`,
46+
FIELD_RULE_CONDITION_ABSENT: `The operand field '{condition_field}' is not present in the schema of the content-type {ctUid}`,
47+
FIELD_RULE_TARGET_ABSENT: `The target field '{target_field}' is not present in the schema of the content-type {ctUid}`,
48+
FIELD_RULE_CONDITION_SCAN_MESSAGE: `Completed Scanning of Field Rule '{num}' condition of Content-type '{ctUid}'`,
49+
FIELD_RULE_TARGE_SCAN_MESSAGE: `Completed Scanning of Field Rule '{num}' target of Content-type '{ctUid}'`
4650
};
4751

4852
const auditFixMsg = {

packages/contentstack-audit/src/modules/content-types.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ export default class ContentType {
4444
protected schema: ContentTypeStruct[] = [];
4545
protected missingRefs: Record<string, any> = {};
4646
public moduleName: keyof typeof auditConfig.moduleConfig;
47-
47+
public f:any = []
4848
constructor({ log, fix, config, moduleName, ctSchema, gfSchema }: ModuleConstructorParam & CtConstructorParam) {
4949
this.log = log;
5050
this.config = config;
@@ -91,7 +91,7 @@ export default class ContentType {
9191
this.currentTitle = schema.title;
9292
this.missingRefs[this.currentUid] = [];
9393
const { uid, title } = schema;
94-
await this.lookForReference([{ uid, name: title }], schema);
94+
await this.lookForReference([{ uid, name: title }], schema, null);
9595
this.log(
9696
$t(auditMsg.SCAN_CT_SUCCESS_MSG, { title, module: this.config.moduleConfig[this.moduleName].name }),
9797
'info',
@@ -179,14 +179,18 @@ export default class ContentType {
179179
async lookForReference(
180180
tree: Record<string, unknown>[],
181181
field: ContentTypeStruct | GlobalFieldDataType | ModularBlockType | GroupFieldDataType,
182+
parent: any = null
182183
): Promise<void> {
183184
const fixTypes = this.config.flags['fix-only'] ?? this.config['fix-fields'];
184185

185186
if (this.fix) {
186187
field.schema = this.runFixOnSchema(tree, field.schema as ContentTypeSchemaType[]);
187188
}
188-
189189
for (let child of field.schema ?? []) {
190+
if(parent) {
191+
this.f.push(parent+"."+child.uid)
192+
}
193+
190194
if (!fixTypes.includes(child.data_type) && child.data_type !== 'json') continue;
191195

192196
switch (child.data_type) {
@@ -317,7 +321,7 @@ export default class ContentType {
317321
return void 0;
318322
}
319323

320-
await this.lookForReference(tree, field);
324+
await this.lookForReference(tree, field, field.uid);
321325
}
322326

323327
/**
@@ -351,7 +355,7 @@ export default class ContentType {
351355
for (const block of blocks) {
352356
const { uid, title } = block;
353357

354-
await this.lookForReference([...tree, { uid, name: title }], block);
358+
await this.lookForReference([...tree, { uid, name: title }], block, block.uid);
355359
}
356360
}
357361

@@ -367,7 +371,7 @@ export default class ContentType {
367371
*/
368372
async validateGroupField(tree: Record<string, unknown>[], field: GroupFieldDataType): Promise<void> {
369373
// NOTE Any Group Field related logic can be added here (Ex data serialization or picking any metadata for report etc.,)
370-
await this.lookForReference(tree, field);
374+
await this.lookForReference(tree, field, field.uid);
371375
}
372376

373377
/**

packages/contentstack-audit/src/modules/entries.ts

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ export default class Entries {
115115
const { uid, title } = entry;
116116
this.currentUid = uid;
117117
this.currentTitle = title;
118+
this.currentTitle = this.removeEmojiAndImages(this.currentTitle)
118119

119120
if (!this.missingRefs[this.currentUid]) {
120121
this.missingRefs[this.currentUid] = [];
@@ -131,7 +132,7 @@ export default class Entries {
131132
this.removeMissingKeysOnEntry(ctSchema.schema as ContentTypeSchemaType[], this.entries[entryUid]);
132133
}
133134

134-
this.lookForReference([{ locale: code, uid, name: title }], ctSchema, this.entries[entryUid]);
135+
this.lookForReference([{ locale: code, uid, name: this.removeEmojiAndImages(title) }], ctSchema, this.entries[entryUid]);
135136

136137
if (this.missingRefs[this.currentUid]?.length) {
137138
this.missingRefs[this.currentUid].forEach((entry: any) => {
@@ -650,8 +651,8 @@ export default class Entries {
650651
if (!uid && reference.startsWith('blt')) {
651652
const refExist = find(this.entryMetaData, { uid: reference });
652653
if (!refExist) {
653-
if(Array.isArray(reference_to) && reference_to.length===1) {
654-
missingRefs.push({uid:reference, _content_type_uid: reference_to[0]});
654+
if (Array.isArray(reference_to) && reference_to.length === 1) {
655+
missingRefs.push({ uid: reference, _content_type_uid: reference_to[0] });
655656
} else {
656657
missingRefs.push(reference);
657658
}
@@ -799,6 +800,13 @@ export default class Entries {
799800
* @returns if there is missing field returns field and path
800801
* Else empty array
801802
*/
803+
removeEmojiAndImages(str: string) {
804+
return str.replace(
805+
/[\p{Emoji}\p{Emoji_Presentation}\p{Emoji_Modifier}\p{Emoji_Modifier_Base}\p{Emoji_Component}]+/gu,
806+
'',
807+
);
808+
}
809+
802810
validateSelectField(tree: Record<string, unknown>[], fieldStructure: SelectFeildStruct, field: any) {
803811
const { display_name, enum: selectOptions, multiple, min_instance, display_type, data_type } = fieldStructure;
804812
if (
@@ -1225,8 +1233,8 @@ export default class Entries {
12251233
if (!uid && reference.startsWith('blt')) {
12261234
const refExist = find(this.entryMetaData, { uid: reference });
12271235
if (!refExist) {
1228-
if(Array.isArray(reference_to) && reference_to.length===1) {
1229-
missingRefs.push({uid:reference, _content_type_uid: reference_to[0]});
1236+
if (Array.isArray(reference_to) && reference_to.length === 1) {
1237+
missingRefs.push({ uid: reference, _content_type_uid: reference_to[0] });
12301238
} else {
12311239
missingRefs.push(reference);
12321240
}
@@ -1399,7 +1407,7 @@ export default class Entries {
13991407
`error`,
14001408
);
14011409
}
1402-
this.entryMetaData.push({ uid: entryUid, title, ctUid:uid });
1410+
this.entryMetaData.push({ uid: entryUid, title, ctUid: uid });
14031411
}
14041412
}
14051413
}

0 commit comments

Comments
 (0)