Skip to content

Commit f7c1580

Browse files
committed
feat: fail records with circular dependencies before deployment starts
1 parent 3762d16 commit f7c1580

File tree

2 files changed

+43
-7
lines changed

2 files changed

+43
-7
lines changed

packages/vlocity-deploy/src/__tests__/datapackDeployment.test.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -214,8 +214,8 @@ describe('DatapackDeployment', () => {
214214
});
215215
});
216216

217-
describe('isCircularDependencies', () => {
218-
it('should return true when circular dependencies (A->B->A) exist', () => {
217+
describe('hasCircularDependencies', () => {
218+
it('should return path when circular dependencies (A->B->A) exist', () => {
219219
// Arrange
220220
const deployment = container.create(DatapackDeployment);
221221
const recordA = mockDatapackRecord( { sourceKey: 'A', datapackKey: 'A' });
@@ -227,10 +227,12 @@ describe('DatapackDeployment', () => {
227227
deployment.add(recordA, recordB);
228228

229229
// Act
230-
const result = deployment['isCircularDependencies'](recordA, recordB);
230+
const result1 = deployment['hasCircularDependencies'](recordA);
231+
const result2 = deployment['hasCircularDependencies'](recordB);
231232

232233
// Assert
233-
expect(result).toBe(true);
234+
expect(result1).toStrictEqual([ 'A', 'B', 'A' ]);
235+
expect(result2).toStrictEqual([ 'B', 'A', 'B' ]);
234236
});
235237
it('should return true when a deep circular dependencies (A->B->C->A) exist', () => {
236238
// Arrange
@@ -246,10 +248,10 @@ describe('DatapackDeployment', () => {
246248
deployment.add(recordA, recordB, recordC);
247249

248250
// Act
249-
const result = deployment['isCircularDependencies'](recordA, recordB);
251+
const result = deployment['hasCircularDependencies'](recordA);
250252

251253
// Assert
252-
expect(result).toBe(true);
254+
expect(result).toStrictEqual([ 'A', 'C', 'B', 'A' ]);
253255
});
254256
});
255257
});

packages/vlocity-deploy/src/datapackDeployment.ts

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,10 @@ export class DatapackDeployment extends AsyncEventEmitter<DatapackDeploymentEven
356356
if (this.options.strictOrder && isExternalDependency) {
357357
const dependencyStatus = this.getDatapackStatus(dependentRecord.datapackKey);
358358
if (dependencyStatus !== undefined && dependencyStatus < DeploymentStatus.Deployed) {
359+
if (this.isCircularDatapackDependency(record.datapackKey, dependentRecord.datapackKey)) {
360+
this.reportWarning(record, `Circular datapack dependency: ${record.datapackKey}->${dependentRecord.datapackKey}->${record.datapackKey}`);
361+
continue;
362+
}
359363
return true;
360364
}
361365
}
@@ -372,14 +376,44 @@ export class DatapackDeployment extends AsyncEventEmitter<DatapackDeploymentEven
372376
}
373377
}
374378

379+
/**
380+
* Checks if there is a circular dependency between two datapacks.
381+
* @param datapackKeyA - The key of the first datapack.
382+
* @param datapackKeyB - The key of the second datapack.
383+
* @returns True if there is a circular dependency, false otherwise.
384+
*/
385+
private isCircularDatapackDependency(datapackKeyA: string, datapackKeyB: string, visited = new Set<string>()): boolean {
386+
if (visited.has(datapackKeyB)) {
387+
// Prevent a circular dependency check from going into an infinite loop
388+
return false;
389+
} else {
390+
visited.add(datapackKeyB);
391+
}
392+
393+
for(const record of this.getRecords(datapackKeyB)) {
394+
for (const dependency of record.getDependencies()) {
395+
const dependentRecord = this.records.get(dependency.VlocityMatchingRecordSourceKey ?? dependency.VlocityLookupRecordSourceKey);
396+
if (!dependentRecord || dependentRecord.datapackKey === datapackKeyB) {
397+
continue;
398+
}
399+
if (dependentRecord.datapackKey === datapackKeyA) {
400+
return true;
401+
} else if (this.isCircularDatapackDependency(datapackKeyA, dependentRecord.datapackKey, visited)) {
402+
return true;
403+
}
404+
}
405+
}
406+
return false;
407+
}
408+
375409
private hasCircularDependencies(record: DatapackDeploymentRecord, graph = Array<string>()): Array<string> | false {
376410
if (!graph.length) {
377411
graph.push(record.sourceKey);
378412
}
379413

380414
for(const key of record.getDependencySourceKeys()) {
381415
if (graph.includes(key)) {
382-
return graph;
416+
return [...graph, key];
383417
}
384418

385419
const depedency = this.records.get(key);

0 commit comments

Comments
 (0)