-
Notifications
You must be signed in to change notification settings - Fork 327
Update usage of shape crate for 0.7.0-preview.n releases
#8616
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
base: dev
Are you sure you want to change the base?
Conversation
|
@benjamn, please consider creating a changeset entry in |
f063150 to
07d35ec
Compare
| { | ||
| input_shape | ||
| .locations | ||
| .extend(method_name.shape_location(context.source_id())); | ||
| input_shape.locations | ||
| let mut locations = input_shape.locations().cloned().collect::<IndexSet<_>>(); | ||
| locations.extend(method_name.shape_location(context.source_id())); | ||
| locations.into_iter() | ||
| }, |
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.
| { | |
| input_shape | |
| .locations | |
| .extend(method_name.shape_location(context.source_id())); | |
| input_shape.locations | |
| let mut locations = input_shape.locations().cloned().collect::<IndexSet<_>>(); | |
| locations.extend(method_name.shape_location(context.source_id())); | |
| locations.into_iter() | |
| }, | |
| input_shape | |
| .locations() | |
| .cloned() | |
| .chain(method_name.shape_location(context.source_id())), |
Saves 1 or 2 allocations. Unless you needed the deduplication from the IndexSet
| @@ -162,7 +162,7 @@ fn filter_shape( | |||
| ); | |||
| } | |||
|
|
|||
| Shape::list(input_shape.any_item([]), input_shape.locations) | |||
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.
Unfortunate that we have to do all this extra cloning now since it's behind a &self method. Why was the change needed?
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 internal representation of metadata (including locations) is completely different now. Directly accessing the vector struct field is not an option any more.
That said, I would very much like to make all Shape::helper methods take a sequence of locations: impl IntoIter<Item = &Location> (and clone them if they need to be stored) rather than Item = Location. I think that would help prevent cloning, and it matches what shape.locations() returns.
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.
From my scan of the changes, most of the places we were using it we were cloning anyway. So saving the couple of places we didn't need to is not worth stressing over
| { | ||
| input_shape | ||
| .locations | ||
| .extend(method_name.shape_location(context.source_id())); | ||
| input_shape.locations | ||
| let mut locations = input_shape.locations().cloned().collect::<IndexSet<_>>(); | ||
| locations.extend(method_name.shape_location(context.source_id())); | ||
| locations.into_iter() | ||
| }, |
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.
| { | |
| input_shape | |
| .locations | |
| .extend(method_name.shape_location(context.source_id())); | |
| input_shape.locations | |
| let mut locations = input_shape.locations().cloned().collect::<IndexSet<_>>(); | |
| locations.extend(method_name.shape_location(context.source_id())); | |
| locations.into_iter() | |
| }, | |
| input_shape | |
| .locations() | |
| .cloned() | |
| .chain(method_name.shape_location(context.source_id())), |
| @@ -6,7 +6,7 @@ input_file: apollo-federation/src/connectors/validation/test_data/env-vars.graph | |||
| [ | |||
| Message { | |||
| code: InvalidHeader, | |||
| message: "In `@source(http.headers:)`: object values aren't valid here", | |||
| message: "In `@source(http.headers:)`: expected `One<Float, Bool, String, null, None>` rejects `Dict<String>`", | |||
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 worry this message is less actionable for the user, since they don't know (though might intuit) what One<> and rejects means.
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.
Maybe, but it's much better information about what went wrong, once you get a little bit familiar with it. Saying nothing about the expected shape (and very little about the received shape) was unhelpful before.
We can workshop how we display these errors later, but I don't want to go back to an opaque error out of some vague fear of confusing beginners (with accurate information they do genuinely need).
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 like that this gives more information so I think that is more helpful... I suspect it's just the wording that could be improved here.
Is there an option to tweak the wording so it's something like this?
Got unexpected 'Dict<String>', expected one of `Float, Bool, String, null, None`
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.
Ok, thanks for the feedback on this!
If you have a look at aeb5a9b, I think I've taken the main line of the error message back to the previous style (adding a short name for the expected shape), with a \n-separated second line that gives the full Details: ... (but without the extra explain_mismatch logic, just the pretty-printed expected/received shapes).
I think this is at least as useful as the previous error message now, and potentially much more useful, especially for LLMs trying to understand the errors.
...on/snapshots/validation_tests@headers__invalid_nested_paths_in_header_variables.graphql.snap
Show resolved
Hide resolved
| @@ -323,12 +323,12 @@ mod tests { | |||
|
|
|||
| assert_eq!( | |||
| selection!("person->as", spec).shape().pretty_print(), | |||
| "Error<\"Method ->as requires one or two arguments (got 0)\", $root.person>", | |||
| "Error<\n \"Method ->as requires one or two arguments (got 0)\",\n $root.person,\n>", | |||
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.
Confused by these changes... are the \n line breaks in the error message?
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.
This is how we pretty-print a ShapeCase::Error (and yes, [email protected] introduced pretty printing with line breaks for overlong lines), but it's not necessarily what we would put into a higher-level error message. In practice, we'd iterate over shape.errors() and use the error.message string from each one (like "Method ->as requires one or two arguments (got 0)"), so the \ns you see here wouldn't matter.
| @@ -1315,7 +1315,7 @@ impl ApplyToInternal for SubSelection { | |||
|
|
|||
| // Build up the merged object shape using Shape::all to merge the | |||
| // individual named_selection object shapes. | |||
| let mut all_shape = Shape::none(); | |||
| let mut all_shape = Shape::unknown([]); | |||
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.
This seems like a pretty major difference to go from none to unknown. 😅
Is the comment above it still accurate?
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.
Mostly just flagging this as a "I don't know enough about what is going on here to say this is fine or not fine... but being cautious" haha
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.
Good question/caution!
To explain this change specifically, all_shape serves as an empty starting shape that gets repeatedly merged with the output of each named selection (typically an object, even for single properties), ultimately generating a single object shape (or union of object shapes) representing all their contributions.
The Shape::unknown([]) shape works well for this, given the way it disappears from intersections (All<Unknown, T> => T, or All<Unknown, T, U> => All<T, U>), which is why this change is safe.
Previously I was using Shape::none() because I mistakenly thought it had the same behavior as Shape::unknown([]). Specifically, I thought None should be automatically dropped from All<None, ...> intersections during simplification. If that was true/appropriate, then Shape::none() would behave basically like Shape::unknown([]) does (for this code's needs). Since then, I've realized None should not disappear from intersections, since it provides important information, often making the intersection unsatisfiable, which is noteworthy.
So, long story short, we're now using Unknown because it always worked, and not using None because it only seemed to work before I fixed a bug.
| @@ -6,16 +6,15 @@ input_file: apollo-federation/src/connectors/validation/test_data/uri_templates/ | |||
| [ | |||
| Message { | |||
| code: InvalidUrl, | |||
| message: "In `GET` in `@connect(http:)` on `Query.argIsArray`: array values aren't valid here", | |||
| message: "In `GET` in `@connect(http:)` on `Query.argIsArray`: expected `One<Float, Bool, String, null, None>` rejects `One<List<One<String, null>>, null>` (because `One<Float, Bool, String, null, None>` rejects `List<One<String, null>>`)", | |||
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'm very confused by this message 😆 This again is probably a wording problem more than anything but this is breaking my brain trying to read it lol
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
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.
Here's the new format for this particular error (after aeb5a9b):
In `GET` in `@connect(http:)` on `Query.argIsArray`: expected union but received incompatible union
Details: `One<Float, Bool, String, null, None>` does not accept `One<List<One<String, null>>, null>`
Note that using (just) the short name union leads to a collision here that makes the error nearly useless without the extra Details: ... line. To anticipate collisions like this, I put in the word "incompatible" so it's clear the two things are probably different/incompatible even though they happen to have the same short name. However, in cases where the short name is different, that bit of information (the first line of the error) is usually more important than the Details. To balance usefulness and verbosity of the Details line, I also removed the trailing (because ...) part.
I'm happy to keep workshopping this error format, but I would submit it's already an extremely helpful error, especially to an LLM.
7d7f6dd to
aeb5a9b
Compare
aeb5a9b to
1deaed9
Compare
✅ Docs preview has no changesThe preview was not built because there were no changes. Build ID: 1e69c6e325f0cd0bfda1986d |
#8616 (comment) The first line is still an improvement over the previous logic because it gives the short names of _both_ `mismatch.expected` and `mismatch.received`, rather than just the received shape name. The `Details: {} did not accept {}` line is important for providing more exact information, including explaining situations where the short names collide (two "object" shapes might be incompatible for any number of reasons, for example). The structure of this shape display syntax should also be helpful for LLMs to make sense of these errors, whereas the previous errors were rather opaque.
1deaed9 to
10ed3b5
Compare
| assert_eq!( | ||
| selection!("person->as", spec).shape().pretty_print(), | ||
| "Error<\"Method ->as requires one or two arguments (got 0)\", $root.person>", | ||
| "Error<\n \"Method ->as requires one or two arguments (got 0)\",\n $root.person,\n>", |
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.
Maybe use r#""# syntax for those blocks with \n:
r#"Error<
"Method ->as requires one or two arguments (got 0)",
$root.person,
>"#and get for free removing \"
#8616 (comment) The first line is still an improvement over the previous logic because it gives the short names of _both_ `mismatch.expected` and `mismatch.received`, rather than just the received shape name. The `Details: {} did not accept {}` line is important for providing more exact information, including explaining situations where the short names collide (two "object" shapes might be incompatible for any number of reasons, for example). The structure of this shape display syntax should also be helpful for LLMs to make sense of these errors, whereas the previous errors were rather opaque.
10ed3b5 to
19253d8
Compare
cc1f10b to
9df22d9
Compare
#8616 (comment) The first line is still an improvement over the previous logic because it gives the short names of _both_ `mismatch.expected` and `mismatch.received`, rather than just the received shape name. The `Details: {} did not accept {}` line is important for providing more exact information, including explaining situations where the short names collide (two "object" shapes might be incompatible for any number of reasons, for example). The structure of this shape display syntax should also be helpful for LLMs to make sense of these errors, whereas the previous errors were rather opaque.
19253d8 to
71872d5
Compare
shape crate for 0.7.0-preview.1+ releasesshape crate for 0.7.0-preview.n releases
#8616 (comment) The first line is still an improvement over the previous logic because it gives the short names of _both_ `mismatch.expected` and `mismatch.received`, rather than just the received shape name. The `Details: {} did not accept {}` line is important for providing more exact information, including explaining situations where the short names collide (two "object" shapes might be incompatible for any number of reasons, for example). The structure of this shape display syntax should also be helpful for LLMs to make sense of these errors, whereas the previous errors were rather opaque.
71872d5 to
4fd9902
Compare
9df22d9 to
717b043
Compare
#8616 (comment) The first line is still an improvement over the previous logic because it gives the short names of _both_ `mismatch.expected` and `mismatch.received`, rather than just the received shape name. The `Details: {} did not accept {}` line is important for providing more exact information, including explaining situations where the short names collide (two "object" shapes might be incompatible for any number of reasons, for example). The structure of this shape display syntax should also be helpful for LLMs to make sense of these errors, whereas the previous errors were rather opaque.
4fd9902 to
9504be3
Compare
717b043 to
c8f8920
Compare
#8616 (comment) The first line is still an improvement over the previous logic because it gives the short names of _both_ `mismatch.expected` and `mismatch.received`, rather than just the received shape name. The `Details: {} did not accept {}` line is important for providing more exact information, including explaining situations where the short names collide (two "object" shapes might be incompatible for any number of reasons, for example). The structure of this shape display syntax should also be helpful for LLMs to make sense of these errors, whereas the previous errors were rather opaque.
9504be3 to
6a6cfea
Compare
This PR proposes a fix for an apparent bug where the connectors expansion code was creating `@join__directive` directives with empty `graphs: []` arguments, accounting for (the only) composition errors in at least 161/4530 supergraphs in our test corpus: - Before (v2.12.1): 4283 successful, 247 failed - After (this fix, based on current dev): 4444 successful, 86 failed In an even more recent snapshot of this corpus, with 5741 supergraphs, this fix reduced the number of failures from 707 to 236 failures, and that only represents graphs where this empty `@join__directive(graphs: [])` bug was the _only_ problem, suggesting this particular problem is increasing in frequency and probably confusing new developers. This code was originally related to license enforcement, but I believe the plan of record is for connectors to be a free feature, so we can probably do without any license enforcement here. I'm certainly open to alternatives, including explanations of existing behavior, but this seems like an accidental regression that is worth fixing before (or as part of) the next connectors-related release.
We are updating the `shape` crate before introducing any abstract types-related changes, because this update benefits all connect spec versions, while still allowing us to preserve backwards/forwards compatibility for older versions (connect/v0.3 and earlier). It would be a shame to have to maintain two slightly different `shape` APIs (hogging the same cargo package name) in order to keep connect/v0.3 and earlier working as before. One of the goals of this commit is to show how limited/reasonable the updates are, because the new functionality coming in [email protected] does not radically alter the behavior of the library (at least not in the ways we were previously using it). Some notable (expected) changes: we've started using a `shape.locations()` method instead of accessing/mutating the `shape.locations` vector directly, the new shape name tracking system allows better error messages, and shape pretty-printing may now include newlines for readability.
#8616 (comment) The first line is still an improvement over the previous logic because it gives the short names of _both_ `mismatch.expected` and `mismatch.received`, rather than just the received shape name. The `Details: {} did not accept {}` line is important for providing more exact information, including explaining situations where the short names collide (two "object" shapes might be incompatible for any number of reasons, for example). The structure of this shape display syntax should also be helpful for LLMs to make sense of these errors, whereas the previous errors were rather opaque.
c8f8920 to
f87bcbd
Compare
6a6cfea to
1ce8d37
Compare
This PR cherry-picks the crucial
[email protected]version update from PR #8143 as a standalone commit (note: previously known as[email protected]), before introducing any abstract types-related changes, which should make theseShape-related updates much easier to review.This update benefits all
ConnectSpecversions, while still allowing us to preserve backwards/forwards compatibility for older versions (connect/v0.3 and earlier). It would be a shame to have to maintain two slightly differentshapeAPIs (colliding on the same cargo package name) in order to keep connect/v0.3 and earlier working as before. Fortunately, we do not need to maintain a fork like that at the level of theshapelibrary.One of the goals of this commit is to show how limited/reasonable the updates are, because the new functionality coming in [email protected] does not radically alter the behavior of the library (at least not in the ways we were previously using it). Notable (expected) changes: we've started using a
shape.locations()method instead of accessing/mutating theshape.locationsvector directly, the new shape name tracking system allows better error messages, and shape pretty-printing may now include newlines/indentation for readability.If this PR looks good and it meets the needs of the Router connectors implementation, I hope it can lend general confidence/support to the
[email protected]minor version update, even though I know most of the team has not had a chance to review the upstream changes in detail.