-
Notifications
You must be signed in to change notification settings - Fork 259
Fixes composition issues with @interfaceObject #2318
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
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -159,6 +159,8 @@ type PathProps<TTrigger, RV extends Vertex = Vertex, TNullEdge extends null | ne | |
/** If the last edge (the one getting to tail) was a DownCast, the runtime types before that edge. */ | ||
readonly runtimeTypesBeforeTailIfLastIsCast?: readonly ObjectType[], | ||
|
||
readonly lastIsInterfaceObjectFakeCastAfterNonCollecting: boolean, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. add a comment for this? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah. Writing such a comment, it occurred to me that there was no need to add a new field, and that I could just reuse the |
||
|
||
readonly deferOnTail?: DeferDirectiveArgs, | ||
} | ||
|
||
|
@@ -206,7 +208,8 @@ export class GraphPath<TTrigger, RV extends Vertex = Vertex, TNullEdge extends n | |
edgeConditions: [], | ||
ownPathIds: [], | ||
overriddingPathIds: [], | ||
runtimeTypesOfTail: runtimeTypes | ||
runtimeTypesOfTail: runtimeTypes, | ||
lastIsInterfaceObjectFakeCastAfterNonCollecting: false, | ||
}); | ||
} | ||
|
||
|
@@ -283,6 +286,10 @@ export class GraphPath<TTrigger, RV extends Vertex = Vertex, TNullEdge extends n | |
return this.props.runtimeTypesOfTail; | ||
} | ||
|
||
lastIsIntefaceObjectFakeDownCastAfterNonCollecting(): boolean { | ||
return this.props.lastIsInterfaceObjectFakeCastAfterNonCollecting; | ||
} | ||
|
||
/** | ||
* Creates the new path corresponding to appending to this path the provided `edge`. | ||
* | ||
|
@@ -341,6 +348,7 @@ export class GraphPath<TTrigger, RV extends Vertex = Vertex, TNullEdge extends n | |
edgeConditions: withReplacedLastElement(this.props.edgeConditions, conditionsResolution.pathTree ?? null), | ||
edgeToTail: updatedEdge, | ||
runtimeTypesOfTail: runtimeTypesWithoutPreviousCast, | ||
lastIsInterfaceObjectFakeCastAfterNonCollecting: false, | ||
// We know the edge is a DownCast, so if there is no new `defer` taking precedence, we just inherit the | ||
// prior version. | ||
deferOnTail: defer ?? this.props.deferOnTail, | ||
|
@@ -375,9 +383,11 @@ export class GraphPath<TTrigger, RV extends Vertex = Vertex, TNullEdge extends n | |
edgeTriggers: withReplacedLastElement(this.props.edgeTriggers, trigger), | ||
edgeIndexes: withReplacedLastElement(this.props.edgeIndexes, edge.index), | ||
edgeConditions: withReplacedLastElement(this.props.edgeConditions, conditionsResolution.pathTree ?? null), | ||
subgraphEnteringEdge, | ||
edgeToTail: edge, | ||
runtimeTypesOfTail: updateRuntimeTypes(this.props.runtimeTypesOfTail, edge), | ||
runtimeTypesBeforeTailIfLastIsCast: undefined, // we know last is not a cast | ||
lastIsInterfaceObjectFakeCastAfterNonCollecting: false, | ||
deferOnTail: defer, | ||
}); | ||
} | ||
|
@@ -394,6 +404,7 @@ export class GraphPath<TTrigger, RV extends Vertex = Vertex, TNullEdge extends n | |
edgeToTail: edge, | ||
runtimeTypesOfTail: updateRuntimeTypes(this.props.runtimeTypesOfTail, edge), | ||
runtimeTypesBeforeTailIfLastIsCast: edge?.transition?.kind === 'DownCast' ? this.props.runtimeTypesOfTail : undefined, | ||
lastIsInterfaceObjectFakeCastAfterNonCollecting: edge?.transition.kind === 'InterfaceObjectFakeDownCast' && !!this.props.edgeToTail?.changesSubgraph(), | ||
// If there is no new `defer` taking precedence, and the edge is downcast, then we inherit the prior version. This | ||
// is because we only try to re-enter subgraphs for @defer on concrete fields, and so as long as we add downcasts, | ||
// we should remember that we still need to try re-entering the subgraph. | ||
|
@@ -436,7 +447,7 @@ export class GraphPath<TTrigger, RV extends Vertex = Vertex, TNullEdge extends n | |
}); | ||
} | ||
|
||
checkDirectPathFomPreviousSubgraphTo( | ||
checkDirectPathFromPreviousSubgraphTo( | ||
typeName: string, | ||
triggerToEdge: (graph: QueryGraph, vertex: Vertex, t: TTrigger) => Edge | null | undefined | ||
): Vertex | undefined { | ||
|
@@ -1116,6 +1127,25 @@ function advancePathWithNonCollectingAndTypePreservingTransitions<TTrigger, V ex | |
convertTransitionWithCondition: (transition: Transition, context: PathContext) => TTrigger, | ||
triggerToEdge: (graph: QueryGraph, vertex: Vertex, t: TTrigger) => Edge | null | undefined | ||
): IndirectPaths<TTrigger, V, TNullEdge, TDeadEnds> { | ||
// If we're asked for indirect paths after an "@interfaceObject fake down cast" but that down cast comes just after a non-collecting edges, then | ||
// we can ignore it (skip indirect paths from there). The reason is that the presence of the non-collecting just before the fake down-cast means | ||
// we add looked at indirect paths just before that down cast, but that fake downcast really does nothing in practice with the subgraph it's on, | ||
// so any indirect path from that fake down cast will have a valid indirect path _before_ it, and so will have been taken into account independently. | ||
if (path.lastIsIntefaceObjectFakeDownCastAfterNonCollecting()) { | ||
// Note: we need to register a dead-end for every subgraphs we "could" be going to, or the code calling this may try to infer a reason on its own | ||
// and we'll run into some assertion. | ||
const reachableSubgraphs = new Set(path.nextEdges().filter((e) => !e.transition.collectOperationElements && e.tail.source !== path.tail.source).map((e) => e.tail.source)); | ||
return { | ||
paths: [], | ||
deadEnds: new Unadvanceables(Array.from(reachableSubgraphs).map((s) => ({ | ||
sourceSubgraph: path.tail.source, | ||
destSubgraph: s, | ||
reason: UnadvanceableReason.IGNORED_INDIRECT_PATH, | ||
details: `ignoring moving from "${path.tail.source}" to "${s}" as a more direct option exists`, | ||
}))) as TDeadEnds, | ||
}; | ||
} | ||
|
||
const isTopLevelPath = path.isOnTopLevelQueryRoot(); | ||
const typeName = isFederatedGraphRootType(path.tail.type) ? undefined : path.tail.type.name; | ||
const originalSource = path.tail.source; | ||
|
@@ -1251,7 +1281,7 @@ function advancePathWithNonCollectingAndTypePreservingTransitions<TTrigger, V ex | |
// loop when calling `hasValidDirectKeyEdge` in that case without additional care and it's not useful because this | ||
// very method already ensure we don't create unnecessary chains of keys for the "current type" | ||
if (subgraphEnteringEdge && edge.transition.kind === 'KeyResolution' && subgraphEnteringEdge.edge.tail.type.name !== typeName) { | ||
const prevSubgraphVertex = toAdvance.checkDirectPathFomPreviousSubgraphTo(edge.tail.type.name, triggerToEdge); | ||
const prevSubgraphVertex = toAdvance.checkDirectPathFromPreviousSubgraphTo(edge.tail.type.name, triggerToEdge); | ||
const backToPreviousSubgraph = subgraphEnteringEdge.edge.head.source === edge.tail.source; | ||
const maxCost = toAdvance.subgraphEnteringEdge.cost + (backToPreviousSubgraph ? 0 : conditionResolution.cost); | ||
if (prevSubgraphVertex | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
kind
was previously just used as a short-circuit to not have to recurse, but this won't change any existing behavior in theory, correct?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I honestly didn't consider when writing this that comparing type of different kind could ever matter, so yes that was mostly a short-circuit to ensure the recursion worked. But yes, I reviewed the call site of this and I'm almost certain this won't change any behavior in an intended way (I mean, if we compare types from the same subgraph, this change makes no difference since a given name uniquely identify a type object in a given schema; so this only matter where comparing types from different schema where we check the kind of types between subgraph independently anyway, so this can't really allow things we don't want).