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

Error policies, huge refactors, and a lot of other breaking changes #480

Merged
merged 14 commits into from
Mar 7, 2023

Conversation

rpiaggio
Copy link
Collaborator

@rpiaggio rpiaggio commented Mar 4, 2023

This PR address #475, #476 and #477, and partially #341.


Regarding the handling of the case when both data and errors (#476), the solution could easily be extended to address (optionally) not stopping a subscription on error (#475).

The solution I sought aimed to allow data+errors handling, but allow users to ignore this cases, raising all errors if desired and simplifying the interface when the invoked service is known not to return these cases. I think it's simpler to deal with an F[Data] than with an F[Ior[GraphQLErrors, Data]].

The approach taken was to add a new ErrorPolicy implicit parameter to all queries and subscriptions. This determines how to handle errors and therefore the return types of queries and subscriptions.

Return types are determined via path-dependent types. This results in the caveat that the ErrorPolicy must be statically known at the call sites for the compiler to be able to solve the return type into a useful type.

For example, ErrorPolicy.RaiseAlways determines that a query return type is F[Data]. The compiler won't be able to solve this if the ErrorPolicy was passed as a parameter to the a method from where the query is made. In practice, this means that we can have a global error policy and import it everywhere queries and subscriptions are made (or pass it explicitly).


Regarding extensions (#477): they are now supported in the protocol, paving the way for clients that makes use of it.


Regarding spec compliance (#341): I've reached the conclusion that's it's neither easy nor trivial to pursue modeling the base GraphQL specification. We should still seek to adhere to the HTTP and WS specs. There are number of changes towards that goal, but there's still work to do, especially untangling the WS protocol from the connection and reconnection logic.


A number of other refactors were made to better adjust types.

Changes to documentation were done by "search and replace", the documentation is still heavily outdated.

The Demo was updated and now it runs again (at least as long as we have the old lucuma-odb development server up).

Copy link
Contributor

@toddburnside toddburnside left a comment

Choose a reason for hiding this comment

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

Nice implementation! Thanks for taking care of this.

@@ -3,7 +3,7 @@

package clue.data

import io.circe.Json
// import io.circe.Json
Copy link
Contributor

Choose a reason for hiding this comment

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

Commented import

}

protected def subscribeInternal[D: Decoder](
// def subscribe_(
Copy link
Contributor

Choose a reason for hiding this comment

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

Commented code.

@@ -118,16 +127,16 @@ object Demo extends IOApp.Simple {
withLogger[IO].use { implicit logger =>
withStreamingClient[IO].use { implicit client =>
for {
result <- client.request(Query)
result <- client.request(Query) // (ErrorPolicyInfo.RaiseAlwaysInfo)
Copy link
Contributor

Choose a reason for hiding this comment

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

Commented code

Copy link
Contributor

@armanbilge armanbilge left a comment

Choose a reason for hiding this comment

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

A pass of comments/questions. I'm a bit confused about the Sync upgrade.

}

sealed trait ErrorPolicyProcessor[D, R] {
def process[F[_]: Sync](result: GraphQLCombinedResponse[D]): F[R]
Copy link
Contributor

Choose a reason for hiding this comment

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

Why does this need Sync? Can the delays below just be pure, in which case ApplicativeThrow is all that is needed?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

That sounds right, good catch too.

Comment on lines 14 to 12
class ConnectionException() extends GraphQLException("Could not establish connection")
case object ConnectionException extends GraphQLException("Could not establish connection")

class DisconnectedException() extends GraphQLException("Connection was closed")
case object DisconnectedException extends GraphQLException("Connection was closed")
Copy link
Contributor

Choose a reason for hiding this comment

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

Exceptions that are case objects can extend scala.util.control.NoStackTrace since their stack trace would be confusing since it's not related to the error.

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 good catch, maybe they should't be objects then, stack traces may be useful.

@rpiaggio rpiaggio requested a review from armanbilge March 7, 2023 03:08
@rpiaggio rpiaggio merged commit c0c39b0 into master Mar 7, 2023
@rpiaggio rpiaggio deleted the error-policy branch March 7, 2023 16:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants