Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement If-diff script #711

Merged
merged 23 commits into from
May 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
ebb348c
feat(util): add if-diff, readline helper
narekhovhannisyan May 15, 2024
73cf421
feat(util): add if-diff strategy to args
narekhovhannisyan May 15, 2024
4b986e4
feat(types): init util args
narekhovhannisyan May 15, 2024
ecd3019
feat(types): init compare lib types
narekhovhannisyan May 15, 2024
1b591a4
feat(types): introduce IFDiffArgs to process args
narekhovhannisyan May 15, 2024
7ec7552
fix(lib): typo in parameterize
narekhovhannisyan May 15, 2024
c4b066e
fix(lib): implement if diff files loader
narekhovhannisyan May 15, 2024
8c2546b
feat(lib): implement compare
narekhovhannisyan May 15, 2024
270af5c
feat(config): implement if-diff args
narekhovhannisyan May 15, 2024
dd0b003
feat(builtins): tune export log
narekhovhannisyan May 15, 2024
0d3c0ea
test(util): change parseArgs to parseIEProcessArgs
narekhovhannisyan May 15, 2024
06d17f0
test(builtins): fix export log test
narekhovhannisyan May 15, 2024
6c4c25e
fix(src): rename parse args
narekhovhannisyan May 15, 2024
84057c6
feat(src): implement diff functionality
narekhovhannisyan May 15, 2024
0b454df
feat(package): init if-diff script
narekhovhannisyan May 15, 2024
12999d5
chore(src): fetch changes from `main`
narekhovhannisyan May 17, 2024
85e3090
fix(lib): fixed array members validation issue
MariamKhalatova May 22, 2024
d326f29
chore(src): fetch changes from remote
narekhovhannisyan May 23, 2024
8a855db
fix(util): add objects to convert to xorable
narekhovhannisyan May 24, 2024
27f3fd0
fix(lib): remove array length check in compare
narekhovhannisyan May 24, 2024
ce1a430
feat(util): add equality checker to helpers
narekhovhannisyan May 24, 2024
b91819d
feat(lib): introduce equality checker to compare
narekhovhannisyan May 24, 2024
6e96ca7
fix(src): use logger for errors
narekhovhannisyan May 24, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
"email": "[email protected]"
},
"bin": {
"ie": "./build/index.js"
"ie": "./build/index.js",
"if-diff": "./build/diff.js"
},
"bugs": {
"url": "https://github.com/Green-Software-Foundation/if/issues/new?assignees=&labels=feedback&projects=&template=feedback.md&title=Feedback+-+"
Expand Down Expand Up @@ -67,6 +68,7 @@
"fix": "gts fix",
"fix:package": "fixpack",
"ie": "npx ts-node src/index.ts",
"if-diff": "npx ts-node src/diff.ts",
"lint": "gts lint",
"prepare": "husky install",
"prepublish": "npm run build",
Expand Down
16 changes: 10 additions & 6 deletions src/__tests__/unit/builtins/export-log.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@ describe('builtins/export-log:', () => {
const mockConsoleLog = jest.spyOn(console, 'log').mockImplementation();
await ExportLog().execute(tree, context);

const expectedMessage = `# start
${YAML.dump({...context, tree}, {noRefs: true})}
# end`;

expect(mockConsoleLog).toHaveBeenCalled();
expect(mockConsoleLog).toHaveBeenCalledWith(
YAML.dump({...context, tree}, {noRefs: true})
);
expect(mockConsoleLog).toHaveBeenCalledWith(expectedMessage);

mockConsoleLog.mockRestore();
});
Expand All @@ -21,10 +23,12 @@ describe('builtins/export-log:', () => {
const mockConsoleLog = jest.spyOn(console, 'log').mockImplementation();
await ExportLog().execute({}, context);

const expectedMessage = `# start
${YAML.dump({...context, tree: {}}, {noRefs: true})}
# end`;

expect(mockConsoleLog).toHaveBeenCalled();
expect(mockConsoleLog).toHaveBeenCalledWith(
YAML.dump({...context, tree: {}}, {noRefs: true})
);
expect(mockConsoleLog).toHaveBeenCalledWith(expectedMessage);

mockConsoleLog.mockRestore();
});
Expand Down
20 changes: 10 additions & 10 deletions src/__tests__/unit/util/args.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ jest.mock('ts-command-line-args', () => ({

import path = require('path');

import {parseArgs} from '../../../util/args';
import {parseIEProcessArgs} from '../../../util/args';
import {ERRORS} from '../../../util/errors';

import {STRINGS} from '../../../config';
Expand All @@ -60,22 +60,22 @@ const {MANIFEST_IS_MISSING, FILE_IS_NOT_YAML} = STRINGS;
describe('util/args: ', () => {
const originalEnv = process.env;

describe('parseArgs(): ', () => {
describe('parseIEProcessArgs(): ', () => {
it('throws error if there is no argument passed.', () => {
expect.assertions(5);

process.env.result = 'error'; // used for mocking

try {
parseArgs();
parseIEProcessArgs();
} catch (error) {
expect(error).toEqual(MANIFEST_IS_MISSING);
}

process.env.result = 'throw-error-object';

try {
parseArgs();
parseIEProcessArgs();
} catch (error) {
expect(error).toBeInstanceOf(CliInputError);
expect(error).toEqual(new CliInputError(MANIFEST_IS_MISSING));
Expand All @@ -84,7 +84,7 @@ describe('util/args: ', () => {
process.env.result = 'manifest-is-missing';

try {
parseArgs();
parseIEProcessArgs();
} catch (error) {
expect(error).toBeInstanceOf(CliInputError);
expect(error).toEqual(new CliInputError(MANIFEST_IS_MISSING));
Expand All @@ -96,7 +96,7 @@ describe('util/args: ', () => {

process.env.result = 'manifest';

const result = parseArgs();
const result = parseIEProcessArgs();
const manifestPath = 'manifest-mock.yml';
const expectedResult = {
inputPath: path.normalize(`${processRunningPath}/${manifestPath}`),
Expand All @@ -113,7 +113,7 @@ describe('util/args: ', () => {

process.env.result = 'absolute-path';

const result = parseArgs();
const result = parseIEProcessArgs();
const manifestPath = 'manifest-mock.yml';
const expectedResult = {
inputPath: path.normalize(`${processRunningPath}/${manifestPath}`),
Expand All @@ -128,7 +128,7 @@ describe('util/args: ', () => {

process.env.result = 'override-params';

const result = parseArgs();
const result = parseIEProcessArgs();
const manifestPath = 'manifest-mock.yml';
const expectedResult = {
inputPath: path.normalize(`${processRunningPath}/${manifestPath}`),
Expand All @@ -144,7 +144,7 @@ describe('util/args: ', () => {

process.env.result = 'manifest-output';

const result = parseArgs();
const result = parseIEProcessArgs();
const manifestPath = 'manifest-mock.yml';
const outputPath = 'output-mock.yml';
const expectedResult = {
Expand All @@ -164,7 +164,7 @@ describe('util/args: ', () => {
process.env.result = 'not-yaml';

try {
parseArgs();
parseIEProcessArgs();
} catch (error) {
expect(error).toBeInstanceOf(CliInputError);
expect(error).toEqual(new CliInputError(FILE_IS_NOT_YAML));
Expand Down
5 changes: 4 additions & 1 deletion src/builtins/export-log.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ export const ExportLog = () => {
...context,
tree,
};
console.log(YAML.dump(outputFile, {noRefs: true}));

console.log(`# start
${YAML.dump(outputFile, {noRefs: true})}
# end`);
};

return {execute};
Expand Down
31 changes: 29 additions & 2 deletions src/config/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {ArgumentConfig, ParseOptions} from 'ts-command-line-args';

import {STRINGS} from './strings';

import {ManifestProcessArgs} from '../types/process-args';
import {IFDiffArgs, IEArgs} from '../types/process-args';

const {DISCLAIMER_MESSAGE} = STRINGS;

Expand Down Expand Up @@ -39,7 +39,7 @@ export const CONFIG = {
alias: 'h',
description: '[prints out the above help instruction]',
},
} as ArgumentConfig<ManifestProcessArgs>,
} as ArgumentConfig<IEArgs>,
HELP: {
helpArg: 'help',
headerContentSections: [
Expand All @@ -50,6 +50,33 @@ export const CONFIG = {
],
} as ParseOptions<any>,
},
IF_DIFF: {
ARGS: {
source: {
type: String,
optional: true,
alias: 's',
description: '[path to the source file]',
},
target: {
type: String,
optional: false,
alias: 't',
description: '[path to the target file',
},
} as ArgumentConfig<IFDiffArgs>,
HELP: {
helpArg: 'help',
headerContentSections: [
{header: 'Impact Framework', content: 'IF-Diff Helpful keywords:'},
],
footerContentSections: [
{header: 'Green Software Foundation', content: DISCLAIMER_MESSAGE},
],
} as ParseOptions<any>,
SUCCESS_MESSAGE: 'Files match!',
FAILURE_MESSAGE: 'Files do not match!',
},
GITHUB_PATH: 'https://github.com',
NATIVE_PLUGIN: 'if-plugins',
AGGREGATION_ADDITIONAL_PARAMS: ['timestamp', 'duration'],
Expand Down
47 changes: 47 additions & 0 deletions src/diff.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#!/usr/bin/env node
/* eslint-disable no-process-exit */
import {loadIfDiffFiles} from './lib/load';
import {compare} from './lib/compare';

import {parseIfDiffArgs} from './util/args';
import {formatNotMatchingLog} from './util/helpers';
import {validateManifest} from './util/validations';

import {CONFIG} from './config';
import {logger} from './util/logger';

const {IF_DIFF} = CONFIG;
const {SUCCESS_MESSAGE, FAILURE_MESSAGE} = IF_DIFF;

const IfDiff = async () => {
const {sourcePath, targetPath} = parseIfDiffArgs();

const {rawSourceManifest, rawTargetManifest} = await loadIfDiffFiles({
targetPath,
sourcePath,
});
const [sourceManifest, targetManifest] = [
rawSourceManifest,
rawTargetManifest,
].map(validateManifest);
const result = compare(sourceManifest, targetManifest);

if (Object.keys(result).length) {
formatNotMatchingLog({
message: FAILURE_MESSAGE,
...result,
});

process.exit(1);
}

console.log(SUCCESS_MESSAGE);
process.exit(0);
};

IfDiff().catch(error => {
if (error instanceof Error) {
logger.error(error);
process.exit(2);
}
});
4 changes: 2 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {initialize} from './lib/initialize';
import {load} from './lib/load';
import {parameterize} from './lib/parameterize';

import {parseArgs} from './util/args';
import {parseIEProcessArgs} from './util/args';
import {andHandle} from './util/helpers';
import {logger} from './util/logger';
import {validateManifest} from './util/validations';
Expand All @@ -17,7 +17,7 @@ import {STRINGS} from './config';
const {DISCLAIMER_MESSAGE} = STRINGS;

const impactEngine = async () => {
const options = parseArgs();
const options = parseIEProcessArgs();

logger.info(DISCLAIMER_MESSAGE);
const {inputPath, paramPath, outputOptions} = options;
Expand Down
48 changes: 48 additions & 0 deletions src/lib/compare.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import {checkIfEqual, oneIsPrimitive} from '../util/helpers';

import {Difference} from '../types/lib/compare';

/**
* 1. If objects are not of the same type or are primitive types, compares directly.
* 2. Gets the keys from both objects.
* 3. Checks for keys present in both objects.
* 4. If both are arrays, checks their elements.
* 5. Checks for differences in values for common keys.
* 6. If all common keys are checked and no differences are found, return empty object.
*/
export const compare = (source: any, target: any, path = ''): Difference => {
if (oneIsPrimitive(source, target)) {
return checkIfEqual(source, target)
? {}
: {
path,
source: source,
target: target,
};
}

const keys1 = Object.keys(source);
const keys2 = Object.keys(target);

const allKeys = new Set([...keys1, ...keys2]);

if (Array.isArray(source) && Array.isArray(target)) {
for (let i = 0; i < source.length; i++) {
compare(source[i], target[i], path ? `${path}[${i}]` : `${i}`);
}
}

for (const key of allKeys) {
const result = compare(
source[key],
target[key],
path ? `${path}.${key}` : key
);

if (Object.keys(result).length) {
return result;
}
}

return {};
};
31 changes: 31 additions & 0 deletions src/lib/load.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
import * as YAML from 'js-yaml';

import {openYamlFileAsObject} from '../util/yaml';
import {readAndParseJson} from '../util/json';
import {parseManifestFromStdin} from '../util/helpers';

import {PARAMETERS} from '../config';

import {Parameters} from '../types/parameters';
import {LoadDiffParams} from '../types/util/args';
import {Manifest} from '../types/manifest';

/**
* Parses manifest file as an object. Checks if parameter file is passed via CLI, then loads it too.
Expand All @@ -22,3 +28,28 @@ export const load = async (inputPath: string, paramPath?: string) => {
parameters,
};
};

/**
* Loads files to compare. As a source file checks if data is piped and then decides which one to take.
*/
export const loadIfDiffFiles = async (params: LoadDiffParams) => {
const {sourcePath, targetPath} = params;
const pipedSourceManifest = await parseManifestFromStdin();

if (!sourcePath && !pipedSourceManifest) {
throw new Error('Source is invalid.');
}

const loadFromSource =
sourcePath && (await openYamlFileAsObject<Manifest>(sourcePath!));
const loadFromSTDIN =
pipedSourceManifest && (await YAML.load(pipedSourceManifest!));

const rawSourceManifest = loadFromSource || loadFromSTDIN;
const rawTargetManifest = await openYamlFileAsObject<Manifest>(targetPath);

return {
rawSourceManifest,
rawTargetManifest,
};
};
4 changes: 2 additions & 2 deletions src/lib/parameterize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const {REJECTING_OVERRIDE, UNKNOWN_PARAM} = STRINGS;
/**
* Parameters manager. Provides get aggregation method and combine functionality.
*/
const Parametrize = () => {
const Parameterize = () => {
let parametersStorage = PARAMETERS;

/**
Expand Down Expand Up @@ -63,4 +63,4 @@ const Parametrize = () => {
};
};

export const parameterize = Parametrize();
export const parameterize = Parameterize();
6 changes: 6 additions & 0 deletions src/types/lib/compare.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export type Difference = {
path?: string;
source?: any;
target?: any;
[key: string]: any;
};
Loading