Skip to content

Commit

Permalink
Merge pull request #1341 from contentstack/feat/CS-44350
Browse files Browse the repository at this point in the history
CS-44350 - Added support for removing the branches that are not being exported in the workflows
  • Loading branch information
cs-raj authored Mar 16, 2024
2 parents 7c850b6 + 71f3aad commit 56365da
Show file tree
Hide file tree
Showing 14 changed files with 263 additions and 151 deletions.
226 changes: 138 additions & 88 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions packages/contentstack-audit/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@contentstack/cli-audit",
"version": "1.5.0",
"version": "1.5.1",
"description": "Contentstack audit plugin",
"author": "Contentstack CLI",
"homepage": "https://github.com/contentstack/cli",
Expand Down Expand Up @@ -86,4 +86,4 @@
"keywords": [
"oclif"
]
}
}
28 changes: 21 additions & 7 deletions packages/contentstack-audit/src/audit-base-command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,13 @@ import { print } from './util/log';
import { auditMsg } from './messages';
import { BaseCommand } from './base-command';
import { Entries, GlobalField, ContentType, Extensions, Workflows } from './modules';
import { CommandNames, ContentTypeStruct, OutputColumn, RefErrorReturnType } from './types';
import {
CommandNames,
ContentTypeStruct,
OutputColumn,
RefErrorReturnType,
WorkflowExtensionsRefErrorReturnType,
} from './types';

export abstract class AuditBaseCommand extends BaseCommand<typeof AuditBaseCommand> {
private currentCommand!: CommandNames;
Expand Down Expand Up @@ -113,7 +119,6 @@ export abstract class AuditBaseCommand extends BaseCommand<typeof AuditBaseComma
config: this.sharedConfig,
fix: this.currentCommand === 'cm:stacks:audit:fix',
};

switch (module) {
case 'content-types':
missingCtRefs = await new ContentType(cloneDeep(constructorParam)).run();
Expand Down Expand Up @@ -303,15 +308,15 @@ export abstract class AuditBaseCommand extends BaseCommand<typeof AuditBaseComma

const tableKeys = Object.keys(missingRefs[0]);
const arrayOfObjects = tableKeys.map((key) => {
if (['title', 'name', 'uid', 'content_types', 'fixStatus'].includes(key)) {
if (['title', 'name', 'uid', 'content_types', 'branches', 'fixStatus'].includes(key)) {
return {
[key]: {
minWidth: 7,
header: key,
get: (row: Record<string, unknown>) => {
if (key === 'fixStatus') {
return chalk.green(typeof row[key] === 'object' ? JSON.stringify(row[key]) : row[key]);
} else if (key === 'content_types') {
} else if (key === 'content_types' || key === 'branches') {
return chalk.red(typeof row[key] === 'object' ? JSON.stringify(row[key]) : row[key]);
} else {
return chalk.white(typeof row[key] === 'object' ? JSON.stringify(row[key]) : row[key]);
Expand Down Expand Up @@ -371,14 +376,22 @@ export abstract class AuditBaseCommand extends BaseCommand<typeof AuditBaseComma
const ws = createWriteStream(csvPath).on('error', reject);
const defaultColumns = Object.keys(OutputColumn);
const userDefinedColumns = this.sharedConfig.flags.columns ? this.sharedConfig.flags.columns.split(',') : null;
let missingRefs: RefErrorReturnType[] = Object.values(listOfMissingRefs).flat();
let missingRefs: RefErrorReturnType[] | WorkflowExtensionsRefErrorReturnType[] =
Object.values(listOfMissingRefs).flat();
const columns: (keyof typeof OutputColumn)[] = userDefinedColumns
? [...userDefinedColumns, ...defaultColumns.filter((val: string) => !userDefinedColumns.includes(val))]
: defaultColumns;

if (this.sharedConfig.flags.filter) {
const [column, value]: [keyof typeof OutputColumn, string] = this.sharedConfig.flags.filter.split('=');
missingRefs = missingRefs.filter((row: RefErrorReturnType) => row[OutputColumn[column]] === value);
// Filter the missingRefs array
missingRefs = missingRefs.filter((row) => {
if (OutputColumn[column] in row) {
const rowKey = OutputColumn[column] as keyof (RefErrorReturnType | WorkflowExtensionsRefErrorReturnType);
return row[rowKey] === value;
}
return false;
});
}

const rowData: Record<string, string | string[]>[] = [];
Expand All @@ -387,7 +400,8 @@ export abstract class AuditBaseCommand extends BaseCommand<typeof AuditBaseComma

for (const column of columns) {
if (Object.keys(issue).includes(OutputColumn[column])) {
row[column] = issue[OutputColumn[column]] as string;
const issueKey = OutputColumn[column] as keyof typeof issue;
row[column] = issue[issueKey] as string;
row[column] = typeof row[column] === 'object' ? JSON.stringify(row[column]) : row[column];
}
}
Expand Down
1 change: 1 addition & 0 deletions packages/contentstack-audit/src/messages/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const commonMsg = {
WORKFLOW_FIX_CONFIRMATION: 'Would you like to overwrite existing file?',
EXTENSION_FIX_WARN: `The extension associated with UID {uid} and title '{title}' will be removed.`,
EXTENSION_FIX_CONFIRMATION: `Would you like to overwrite existing file?`,
WF_BRANCH_REMOVAL: `Removing the branch '{branch} from workflow with UID {uid} and name {name} will be removed.'`,
};

const auditMsg = {
Expand Down
68 changes: 49 additions & 19 deletions packages/contentstack-audit/src/modules/workflows.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export default class Workflows {
public missingCtInWorkflows: Workflow[];
public missingCts: Set<string>;
public workflowPath: string;
public isBranchFixDone: boolean;

constructor({
log,
Expand All @@ -41,6 +42,7 @@ export default class Workflows {
this.missingCtInWorkflows = [];
this.missingCts = new Set();
this.workflowPath = '';
this.isBranchFixDone = false;
}
/**
* Check whether the given path for the workflow exists or not
Expand All @@ -54,69 +56,97 @@ export default class Workflows {
this.log($t(auditMsg.NOT_VALID_PATH, { path: this.folderPath }), { color: 'yellow' });
return {};
}

this.workflowPath = join(this.folderPath, this.fileName);
this.workflowSchema = existsSync(this.workflowPath) ? values(JSON.parse(readFileSync(this.workflowPath, 'utf8')) as Workflow[]) : [];

this.workflowSchema = existsSync(this.workflowPath)
? values(JSON.parse(readFileSync(this.workflowPath, 'utf8')) as Workflow[])
: [];

this.ctSchema.forEach((ct) => this.ctUidSet.add(ct.uid));

for (const workflow of this.workflowSchema) {
const ctNotPresent = workflow.content_types.filter((ct) => !this.ctUidSet.has(ct));
if (ctNotPresent.length) {
const branch = workflow?.branches?.filter((branch) => branch !== this.config?.branch);

if (ctNotPresent.length || branch?.length) {
const tempwf = cloneDeep(workflow);
tempwf.content_types = ctNotPresent;
tempwf.content_types = ctNotPresent || [];

if (workflow?.branches) {
tempwf.branches = branch;
}

if (branch?.length) {
this.isBranchFixDone = true;
}

ctNotPresent.forEach((ct) => this.missingCts.add(ct));
this.missingCtInWorkflows.push(tempwf);
}

this.log(
$t(auditMsg.SCAN_WF_SUCCESS_MSG, {
name: workflow.name,
module: this.config.moduleConfig[this.moduleName].name,
}),
'info'
'info',
);
}
if (this.fix && this.missingCtInWorkflows.length) {

if (this.fix && (this.missingCtInWorkflows.length || this.isBranchFixDone)) {
await this.fixWorkflowSchema();
this.missingCtInWorkflows.forEach((wf) => (wf.fixStatus = 'Fixed'));
}

return this.missingCtInWorkflows;
}

async fixWorkflowSchema() {
async fixWorkflowSchema() {
const newWorkflowSchema: Record<string, Workflow> = existsSync(this.workflowPath)
? JSON.parse(readFileSync(this.workflowPath, 'utf8'))
: {};

if (Object.keys(newWorkflowSchema).length !== 0) {
for (const workflow of this.workflowSchema) {
for (const workflow of this.workflowSchema) {
const fixedCts = workflow.content_types.filter((ct) => !this.missingCts.has(ct));
const fixedBranches: string[] = [];

workflow?.branches?.forEach((branch) => {
if (branch !== this.config?.branch) {
const { uid, name } = workflow;
this.log($t(commonMsg.WF_BRANCH_REMOVAL, { uid, name, branch }), { color: 'yellow' });
} else {
fixedBranches.push(branch);
}
});

if (fixedBranches.length > 0) {
newWorkflowSchema[workflow.uid].branches = fixedBranches;
}

if (fixedCts.length) {
newWorkflowSchema[workflow.uid].content_types = fixedCts;
} else {
const { name, uid } = workflow;
const warningMessage = $t(commonMsg.WORKFLOW_FIX_WARN, { name, uid });

this.log(warningMessage, { color: 'yellow' });

if (this.config.flags.yes || (await ux.confirm(commonMsg.WORKFLOW_FIX_CONFIRMATION))) {
delete newWorkflowSchema[workflow.uid];
}
}
}
}

await this.writeFixContent(newWorkflowSchema);
}

async writeFixContent(newWorkflowSchema: Record<string, Workflow>) {
if (
this.fix ||
!(this.config.flags['copy-dir'] || this.config.flags['external-config']?.skipConfirm) &&
(this.config.flags.yes || (await ux.confirm(commonMsg.FIX_CONFIRMATION)))
(!(this.config.flags['copy-dir'] || this.config.flags['external-config']?.skipConfirm) &&
(this.config.flags.yes || (await ux.confirm(commonMsg.FIX_CONFIRMATION))))
) {
writeFileSync(
join(this.folderPath, this.config.moduleConfig[this.moduleName].fileName),
Expand Down
4 changes: 4 additions & 0 deletions packages/contentstack-audit/src/types/content-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ type RefErrorReturnType = {
title?: string;
};

type WorkflowExtensionsRefErrorReturnType = RefErrorReturnType & { branches?: string[] };

// NOTE Type 1
type ReferenceFieldDataType = CommonDataTypeStruct & {
reference_to: string[];
Expand Down Expand Up @@ -119,6 +121,7 @@ enum OutputColumn {
title = 'title',
'uid' = 'uid',
'missingCts' = 'content_types',
'Missing Branches' = 'branches',
}

export {
Expand All @@ -137,4 +140,5 @@ export {
OutputColumn,
ContentTypeSchemaType,
GlobalFieldSchemaTypes,
WorkflowExtensionsRefErrorReturnType,
};
3 changes: 2 additions & 1 deletion packages/contentstack-audit/src/types/workflow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ export interface Workflow {
enabled?: boolean;
deleted_at?: any;
missingRefs?: any;
fixStatus?:string
fixStatus?: string;
branches?: string[];
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
"org_uid": "org1",
"api_key": "apiKey",
"content_types": [
"ct1"
"ct1",
"ct45",
"ct14"
],
"enabled": false,
"deleted_at": false
Expand All @@ -16,7 +18,8 @@
"org_uid": "org1",
"api_key": "apiKey",
"content_types": [
"ct2"
"ct2",
"ct6"
],
"enabled": false,
"deleted_at": false
Expand All @@ -43,5 +46,21 @@
],
"enabled": false,
"deleted_at": false
},
"wf5": {
"name": "wf5",
"uid": "wf5",
"org_uid": "org1",
"api_key": "apiKey",
"content_types": [
"ct4"
],
"branches": [
"main",
"development",
"stage"
],
"enabled": false,
"deleted_at": false
}
}
40 changes: 16 additions & 24 deletions packages/contentstack-audit/test/unit/modules/workflow.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ describe('Workflows', () => {
.it(
'should expect missingRefs equal to workflow which has missing refs, missingCts equal to missing Cts',
async () => {
wf.config.branch = 'development';
const missingRefs = await wf.run();
expect(wf.workflowSchema).eql(values(JSON.parse(fs.readFileSync(wf.workflowPath, 'utf8'))));
expect(missingRefs).eql([
Expand All @@ -67,33 +68,22 @@ describe('Workflows', () => {
enabled: false,
deleted_at: false,
},
{
api_key: 'apiKey',
branches: ['main', 'stage'],
content_types: [],
deleted_at: false,
enabled: false,
name: 'wf5',
org_uid: 'org1',
uid: 'wf5',
},
]);
expect(wf.missingCts).eql(new Set(['ct45', 'ct14', 'ct6']));
},
);
});

describe('run method with valid path and empty ctSchema to check the missing references', () => {
const wf = new Workflows({
log: () => {},
moduleName: 'workflows',
ctSchema: [],
config: Object.assign(config, {
basePath: resolve(`./test/unit/mock/contents/`),
flags: {},
}),
});

fancy
.stdout({ print: process.env.PRINT === 'true' || true })
.stub(ux, 'confirm', async () => true)
.it('should expect missingRefs equal to all workflows', async () => {
const missingRefs = await wf.run();
wf.workflowSchema.pop();
expect(missingRefs).eql(wf.workflowSchema);
});
});

describe('run method with audit fix for workflows with valid path and empty ctSchema', () => {
const wf = new Workflows({
log: () => {},
Expand All @@ -111,6 +101,7 @@ describe('Workflows', () => {
.stub(wf, 'log', async () => {})
.stub(ux, 'confirm', async () => true)
.stub(wf, 'WriteFileSync', () => {})
.stub(wf, 'writeFixContent', () => {})
.it('the run function should run and flow should go till fixWorkflowSchema', async () => {
const fixedReference = await wf.run();
expect(fixedReference).eql([
Expand All @@ -136,13 +127,14 @@ describe('Workflows', () => {
},
{
api_key: 'apiKey',
content_types: ['ct88'],
branches: ['main', 'stage'],
content_types: [],
deleted_at: false,
enabled: false,
fixStatus: 'Fixed',
name: 'wf6',
name: 'wf5',
org_uid: 'org1',
uid: 'wf6',
uid: 'wf5',
},
]);
});
Expand Down
Loading

0 comments on commit 56365da

Please sign in to comment.