-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix(control): reliably sort schema control declaration by dependency …
…order
- Loading branch information
1 parent
10e5d69
commit 286e62d
Showing
10 changed files
with
513 additions
and
129 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
33 changes: 33 additions & 0 deletions
33
...ic/__test_assets__/exampleProject/src/domain/objects/AsyncTaskPredictStationCongestion.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import { DomainEntity } from 'domain-objects'; | ||
import { AsyncTask, AsyncTaskStatus } from 'simple-async-tasks'; | ||
|
||
/** | ||
* an async task which predicts station congestion by comparing expected arrival time with estimated arrival time | ||
*/ | ||
export interface AsyncTaskPredictStationCongestion extends AsyncTask { | ||
id?: number; | ||
uuid?: string; | ||
createdAt?: Date; | ||
updatedAt?: Date; | ||
status: AsyncTaskStatus; | ||
|
||
/** | ||
* the station to run this prediction for | ||
*/ | ||
stationUuid: string; | ||
|
||
/** | ||
* the event to run the prediction on | ||
* | ||
* note | ||
* - this has a nested dependency on a train, which we use to test sql-schema-control order | ||
*/ | ||
trainLocatedEventUuid: string; | ||
} | ||
export class AsyncTaskPredictStationCongestion | ||
extends DomainEntity<AsyncTaskPredictStationCongestion> | ||
implements AsyncTaskPredictStationCongestion | ||
{ | ||
public static unique = ['stationUuid', 'trainLocatedEventUuid']; | ||
public static updatable = ['status']; | ||
} |
1 change: 1 addition & 0 deletions
1
src/logic/__test_assets__/exampleProject/src/domain/objects/index.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
55 changes: 55 additions & 0 deletions
55
...lSchemaControl/__snapshots__/defineDependentReferenceAvailableProvisionOrder.test.ts.snap
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
// Jest Snapshot v1, https://goo.gl/fbAQLP | ||
|
||
exports[`defineDependentReferenceAvailableProvisionOrder should work on the example project 1`] = ` | ||
{ | ||
"depth": 3, | ||
"order": [ | ||
"Carriage", | ||
"Certificate", | ||
"Geocode", | ||
"Locomotive", | ||
"Price", | ||
"TrainEngineer", | ||
"TrainStation", | ||
"InvoiceLineItem", | ||
"Train", | ||
"TrainLocatedEvent", | ||
"AsyncTaskPredictStationCongestion", | ||
"Invoice", | ||
], | ||
"reason": { | ||
"AsyncTaskPredictStationCongestion": [ | ||
"TrainStation", | ||
"TrainLocatedEvent", | ||
], | ||
"Carriage": [], | ||
"Certificate": [], | ||
"Geocode": [], | ||
"Invoice": [ | ||
"InvoiceLineItem", | ||
"Price", | ||
], | ||
"InvoiceLineItem": [ | ||
"Price", | ||
], | ||
"Locomotive": [], | ||
"Price": [], | ||
"Train": [ | ||
"Geocode", | ||
"Locomotive", | ||
"Carriage", | ||
"TrainEngineer", | ||
], | ||
"TrainEngineer": [ | ||
"Certificate", | ||
], | ||
"TrainLocatedEvent": [ | ||
"Train", | ||
"Geocode", | ||
], | ||
"TrainStation": [ | ||
"Geocode", | ||
], | ||
}, | ||
} | ||
`; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
24 changes: 24 additions & 0 deletions
24
src/logic/define/sqlSchemaControl/defineDependentReferenceAvailableProvisionOrder.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import { introspect } from 'domain-objects-metadata'; | ||
|
||
import { defineSqlSchemaRelationshipsForDomainObjects } from '../sqlSchemaRelationship/defineSqlSchemaRelationshipsForDomainObjects'; | ||
import { defineDependentReferenceAvailableProvisionOrder } from './defineDependentReferenceAvailableProvisionOrder'; | ||
import { defineSqlSchemaControlCodeFilesForDomainObjects } from './defineSqlSchemaControlCodeFilesForDomainObjects'; | ||
|
||
describe('defineDependentReferenceAvailableProvisionOrder', () => { | ||
it('should work on the example project', () => { | ||
const domainObjects = introspect( | ||
`${__dirname}/../../__test_assets__/exampleProject/src/domain/objects/index.ts`, | ||
); | ||
const sqlSchemaRelationships = defineSqlSchemaRelationshipsForDomainObjects( | ||
{ domainObjects }, | ||
); | ||
|
||
const { order, reason, depth } = | ||
defineDependentReferenceAvailableProvisionOrder({ | ||
sqlSchemaRelationships, | ||
}); | ||
console.log({ order, reason, depth }); | ||
|
||
expect({ order, reason, depth }).toMatchSnapshot(); | ||
}); | ||
}); |
75 changes: 75 additions & 0 deletions
75
src/logic/define/sqlSchemaControl/defineDependentReferenceAvailableProvisionOrder.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
import { UnexpectedCodePathError } from '@ehmpathy/error-fns'; | ||
import { isPresent } from 'type-fns'; | ||
|
||
import { SqlSchemaToDomainObjectRelationship } from '../../../domain'; | ||
|
||
/** | ||
* .what = defines the order in which schemas must be provisioned to ensure their dependent references are available | ||
* .why = | ||
* - guarantees that if this order is followed, there will be no schema declaration issues | ||
*/ | ||
export const defineDependentReferenceAvailableProvisionOrder = ({ | ||
sqlSchemaRelationships, | ||
}: { | ||
sqlSchemaRelationships: SqlSchemaToDomainObjectRelationship[]; | ||
}): { order: string[]; reason: Record<string, string[]>; depth: number } => { | ||
// create a map of DobjName -> ReferencedDobjName[] | ||
const dependencyMap = sqlSchemaRelationships.reduce( | ||
(summary, thisRelationship) => { | ||
const key = thisRelationship.name.domainObject; | ||
const value = [ | ||
...new Set( | ||
thisRelationship.properties | ||
.map((property) => property.sqlSchema.reference?.of.name) | ||
.filter(isPresent), | ||
), | ||
]; | ||
return { | ||
...summary, | ||
[key]: value, | ||
}; | ||
}, | ||
{} as Record<string, string[]>, | ||
); | ||
|
||
// loop through each until its dependencies are in the sorted array | ||
const dobjNamesToOrder = Object.keys(dependencyMap).sort(); | ||
const order: string[] = []; | ||
let iterations = 0; | ||
while (order.length < dobjNamesToOrder.length) { | ||
// increment the iterations; fail fast if we've iterated more than 21 times. there should not be a reason to have a reference depth of 21+ times, meaning there's probably a cyclical reference | ||
iterations++; | ||
if (iterations > 21) | ||
throw new UnexpectedCodePathError( | ||
'attempted to resolve reference order for more than 21 iterations. is there a cyclical import?', | ||
{ dependencyMap: dependencyMap }, | ||
); | ||
|
||
// loop through each dobj | ||
for (const dobjName of dobjNamesToOrder) { | ||
// if already ordered, no need to try again | ||
if (order.includes(dobjName)) continue; | ||
|
||
// if a dependency is not found yet, continue until it is | ||
const dependencies = dependencyMap[dobjName]; | ||
if (!dependencies) | ||
throw new UnexpectedCodePathError( | ||
'could not find dependencies for dobj. how is that possible?', | ||
{ dobjName, dependencies }, | ||
); | ||
const hasSomeDependencyMissed = dependencies.some( | ||
(dependency) => !order.includes(dependency), | ||
); | ||
if (hasSomeDependencyMissed) continue; | ||
|
||
// otherwise, add this dobj to the order | ||
order.push(dobjName); | ||
} | ||
} | ||
|
||
return { | ||
order: order, | ||
reason: dependencyMap, | ||
depth: iterations, | ||
}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.