Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@
"dotenv": "16.4.5",
"embla-carousel-react": "8.1.8",
"fast-deep-equal": "3.1.3",
"fast-json-stable-stringify": "2.1.0",
"fastify": "5.8.3",
"fastify-favicon": "5.0.0",
"fastify-plugin": "5.0.1",
Expand Down
1 change: 0 additions & 1 deletion packages/engine/src/lib/variables/props-resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -245,10 +245,9 @@
scriptContext: contextAsScope,
});
return result ?? '';
} catch (exception) {
console.warn('[evalInScope] Error evaluating variable', exception);
return '';
}

Check warning on line 250 in packages/engine/src/lib/variables/props-resolver.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Handle this exception or don't catch it at all.

See more on https://sonarcloud.io/project/issues?id=openops-cloud_openops&issues=AZ15DPW6TMiJv82Rs5eB&open=AZ15DPW6TMiJv82Rs5eB&pullRequest=2217
}

async function replaceAsync(
Expand Down
7 changes: 7 additions & 0 deletions packages/server/shared/src/lib/hash.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as crypto from 'crypto';
import stringify from 'fast-json-stable-stringify';

function hashObject(
object: object,
Expand All @@ -8,6 +9,12 @@ function hashObject(
return crypto.createHash('sha256').update(jsonString).digest('hex');
}

function hashDeterministicObject(object: object): string {
const jsonString = stringify(object);
return crypto.createHash('sha256').update(jsonString).digest('hex');
}

export const hashUtils = {
hashObject,
hashDeterministicObject,
};
73 changes: 72 additions & 1 deletion packages/server/shared/test/hash.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ describe('Hash Object', () => {

it('should allow custom replacers to modify the hash', () => {
const object = { key: 'value', anotherKey: 42 };
const replacer = (key: string, value: unknown) =>
const replacer = (key: string, value: unknown): unknown =>
key === 'key' ? 'modifiedValue' : value;

const hashWithReplacer = hashUtils.hashObject(object, replacer);
Expand Down Expand Up @@ -71,3 +71,74 @@ describe('Hash Object', () => {
expect(() => hashUtils.hashObject(object)).toThrow();
});
});

describe('Hash Deterministic Object', () => {
it('should return a consistent hash for the same object', () => {
const object = { key: 'value', anotherKey: 42 };
const hash1 = hashUtils.hashDeterministicObject(object);
const hash2 = hashUtils.hashDeterministicObject(object);

expect(hash1).toEqual(hash2);
expect(hash1).toEqual(
'bcfbe7147cc6988bf4987c322ca615a900ed0bce28c358e8404c1b5c14ac389f',
);
});

it('should return the same hash for objects with keys in different orders', () => {
const object1 = { key: 'value', anotherKey: 42 };
const object2 = { anotherKey: 42, key: 'value' };

const hash1 = hashUtils.hashDeterministicObject(object1);
const hash2 = hashUtils.hashDeterministicObject(object2);

expect(hash1).toEqual(hash2);
expect(hash1).toEqual(
'bcfbe7147cc6988bf4987c322ca615a900ed0bce28c358e8404c1b5c14ac389f',
);
});

it('should return different hashes for different objects', () => {
const object1 = { key: 'value1' };
const object2 = { key: 'value2' };

const hash1 = hashUtils.hashDeterministicObject(object1);
const hash2 = hashUtils.hashDeterministicObject(object2);

expect(hash1).not.toEqual(hash2);
expect(hash1).toEqual(
'dfada72ccc2244e8c7aef8f0dbe7c026a6553bc5bda3f7654f3d0b94dd51a23b',
);
expect(hash2).toEqual(
'711db6965d4867a7c0f6f20864ae49896b97ba3616a9aa53b536a773468f662e',
);
});

it('should handle nested objects correctly and deterministically', () => {
const nestedObject1 = { key: { b: 2, a: 1 } };
const nestedObject2 = { key: { a: 1, b: 2 } };

const hash1 = hashUtils.hashDeterministicObject(nestedObject1);
const hash2 = hashUtils.hashDeterministicObject(nestedObject2);

expect(hash1).toEqual(hash2);
expect(hash1).toEqual(
'44a71c92a8d07443b2539b0d82a137c3a620aa81be56de2f29a9e2fe45fefbc4',
);
});

it('should handle empty objects', () => {
const object = {};

const hash = hashUtils.hashDeterministicObject(object);

expect(hash).toEqual(
'44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a',
);
});

it('should handle edge case with undefined object', () => {
const object = undefined as unknown as object;

expect(() => hashUtils.hashDeterministicObject(object)).toThrow();
});
});
Loading