Skip to content
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

Add source location mapping #168

Merged

Conversation

paulpdaniels
Copy link
Collaborator

This PR adds source location mapping to fields and fragments. It is primarily used for outputting when errors occur.

Copy link
Owner

@ghostdogpr ghostdogpr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks awesome! Just a couple questions.

@@ -29,6 +31,7 @@ object CalibanError {
case class ExecutionError(
msg: String,
path: List[Either[String, Int]] = Nil,
locationInfo: Option[LocationInfo] = None,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about adding it to ParsingError and ValidationError?

@@ -418,9 +420,13 @@ trait DerivationSchema[R] {

object GenericSchema {

def effectfulExecutionError(path: List[Either[String, Int]], e: Throwable): ExecutionError =
def effectfulExecutionError(
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not related to your code, but this one seems only used in Executor, how about moving it there?

case ExecutionError(_, path, location, _) if path.nonEmpty =>
val locationJson =
location.fold(Json.obj())(
l => Json.obj("location" -> Json.obj("column" -> l.column.asJson, "line" -> l.line.asJson))
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, one more thing. As mentioned in #156, the spec suggests a format which is slightly different from what you did: "locations": [ { "line": 6, "column": 7 } ].

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note that location is evaluated only when path is set. This may not be what you want.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah you’re right, though ive never actually seen multiple locations in the wild. I guess I should change it to be a List instead of an Option too

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean path and location are presumably independent of each other therefore the guard if path.nonEmpty => looks to be dangerous as it impacts evaluation of location. One could consider location being non-empty and path being empty even though it's more of an intellectual exercise than a "reality".

Comment on lines 60 to 62
case EnumValue(v) if mergeFields(currentField, v).collectFirst {
case Field("__typename", _, _, _, _, _, _) => true
case Field("__typename", _, _, _, _, _, _, _) => true
}.nonEmpty =>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I personally don't like many underscores, though I understand that was the case prior to this PR...

How about this notation?
case f: Field if f.name == "__typename" => true

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree it gets verbose. One concern I have about this change is that it removes exhaustive checks on the case match. I think it’s less of a concern where there are if constraints because I think that already disables the check as well.

Comment on lines 17 to 22
/**
* Implementation taken from https://github.com/lihaoyi/fastparse/blob/master/fastparse/src/fastparse/ParserInput.scala
*/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you may need to reproduce his MIT license somewhere if you have not taken care of that.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. Is that usually at the file level or do we just need to have it once at the top level?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know of firms who do it at product level. You'd have to ask ZIO principals or Pierre I guess.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@@ -132,7 +133,7 @@ object Validator {
private def collectSelectionSets(selectionSet: List[Selection]): List[Selection] =
selectionSet ++ selectionSet.flatMap {
case _: FragmentSpread => Nil
case Field(_, _, _, _, selectionSet) => collectSelectionSets(selectionSet)
case Field(_, _, _, _, selectionSet, _) => collectSelectionSets(selectionSet)
Copy link
Contributor

@phderome phderome Jan 18, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd be inclined to use a more concise notation that does not use underscores much, once again here. Again, my comment relates to pre-PR code, but since we modify this line, we may as well consider it.
case f: Field => collectSelectionSets(f.selectionSet)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here should be fine since exhaustive checking would already be disabled from the line above

@@ -161,7 +162,7 @@ object Validator {
IO.foreach(selectionSet) {
case FragmentSpread(_, directives) =>
checkDirectivesUniqueness(directives).as(directives.map((_, __DirectiveLocation.FRAGMENT_SPREAD)))
case Field(_, _, _, directives, selectionSet) =>
case Field(_, _, _, directives, selectionSet, _) =>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since we don't match on special values, again just case f: Field => ... access f members where needed

Comment on lines 60 to 54
case EnumValue(v) if mergeFields(currentField, v).collectFirst {
case Field("__typename", _, _, _, _, _, _) => true
case Field("__typename", _, _, _, _, _, _, _) => true
}.nonEmpty =>
// special case of an hybrid union containing case objects, those should return an object instead of a string
val obj = mergeFields(currentField, v).collectFirst {
case Field(name @ "__typename", _, _, alias, _, _, _) =>
case Field(name @ "__typename", _, _, alias, _, _, _, _) =>
ObjectValue(List(alias.getOrElse(name) -> StringValue(v)))
}
obj.fold(s)(PureStep(_))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In fact I am proposing this simplification for current lines 60-68, which pass the tests.

        case s @ PureStep(value) =>
          value match {
            case EnumValue(v) =>
              // special case of an hybrid union containing case objects, those should return an object instead of a string
              val obj = mergeFields(currentField, v).collectFirst {
                case f: Field if f.name == "__typename" =>
                  ObjectValue(List(f.alias.getOrElse(f.name) -> StringValue(v)))
              }
              obj.fold(s)(PureStep(_))
            case _ => s
          }

I don't see any need to do mergeFields...collectFirst twice here, once in match guard and once in evaluation optional obj.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that I am considering s to be the default value for value match in the complex case and the second match all case just like existing code.

@paulpdaniels paulpdaniels force-pushed the feature/source-location-mapping branch from 81adfd8 to 88b4cba Compare January 28, 2020 10:08
@ghostdogpr
Copy link
Owner

Looks good to me! It needs a rebase to solve the merge conflict with master and I think we’re good.

@paulpdaniels paulpdaniels force-pushed the feature/source-location-mapping branch from 88b4cba to 88ffb52 Compare January 28, 2020 11:46
Copy link
Owner

@ghostdogpr ghostdogpr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, it's awesome!

@ghostdogpr ghostdogpr merged commit 9cd1643 into ghostdogpr:master Jan 28, 2020
@ghostdogpr ghostdogpr mentioned this pull request Jan 28, 2020
@paulpdaniels paulpdaniels deleted the feature/source-location-mapping branch January 28, 2020 13:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants