question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Make generated classes extend common super-type

See original GitHub issue

Is your feature request related to a problem? Please describe. I want to create a function where some common transforms/error-handling/logging can live. As of now each class exists independently in the class hierarchy, and thus it is non-trivial to create abstractions with them.

Given the example classes here;

class FetchName(private val graphQLClient: GraphQLClient<*>) { ... } // Generated for fetchName.graphql
class FetchNameBulk(private val graphQLClient: GraphQLClient<*>) { ... } // Generated for fetchNameBulk.graphql

In order to pack some custom-logic in here (mainly custom error-handling and logging) I want to do something like this;

fun exec(query: FetchName, variables: FetchName.Variables): GraphlQLResponse<FetchName.Result>? {
  val uuid = UUID.randomUUID();
  return try {
    val transformedVariables = transform(variables)
    servicecallLog.info(uuid, "FetchNameRequest", transformedVariables)
    val response = query.execute(transformedVariables)
    if (response.errors.isNullOrEmpty()) {
      servicecallLog.info(uuid, "FetchNameResponse", response)
      response
    } else {
      servicecallLog.error(uuid, "FetchNameError", response.errors)
      null
    }
  } catch (exception: Exception) {
    servicecallLog.error(uuid, "FetchNameError", e)
    null
  }
}

Now the code can be duplicated and changed to fit for FetchNameBulk as well, but I would prefer a more generic approach.

Describe the solution you’d like Experimented a bit with the code-generation, and how it could be structured. That way I found that introducing a common super-interface could solve my issues pretty neatly without (as far as I could tell) introducing any adverse effects.

What I propose is something along the lines of;

class FetchName(private val graphQLClient: GraphQLClient<*>) : GraphQLRequestDescription<FetchName.Variables, FetchName.Result> { ... }

That way the exec function above could be generalize

fun <VARS, RESULT> exec(query: GraphQLRequestDescription<VARS, RESULT>, variables: VARS): GraphQLResponse<RESULT>

Personally I would prefer GraphQLRequest instead of GraphQLRequestDescription, but since it’s already used in the types-package I used GraphQLRequestDescription when testing.

Describe alternatives you’ve considered The “problem” can be though of as three separate issues; variable-transforms, logging and custom-errorhandling.

Errorhandling is trivial to solve as the return-type already has a common return type GraphQLResponse<T>.

For logging I looked into using ktor-client-logging, but from what I could understand you are pretty limited in what you can do with it. Another possibility is to create a custom HttpClientFeature, but again it makes it hard to log just the graphql-variables for instance.

Custom variable-transforms could be hoisted to the callsite of the exec function. But I would then have to rely on every developer to call the transform-function before passing the variables.

Addition context I’ve played around with some code locally, and believe I may have working proof-of-concept (absent the tests). But wanted to create a issue to discuss the feature before diving even deeper into the issue or create a PR. The changes can be seen here; https://github.com/nutgaard/graphql-kotlin/commit/4d8887878f372d521877801cc09549b952c125de

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Comments:8 (1 by maintainers)

github_iconTop GitHub Comments

1reaction
dariuszkuccommented, Jul 25, 2020

I looked into updating GraphQLClient into a generic interface and while its possible to do it we would loose some of the existing Ktor customizability, i.e. current execute method signature accepts HttpRequestBuilder.() lambda which allows us to apply per request customization. If we move to generic interface then we would need to provide some generic mechanism to duplicate the same (or part of) functionality (e.g… maybe explicitly accept list of headers) that could then be applied in the specific implementations. I’m going to icebox generic client interface until we figure out a nice way to generically apply those customizations.

In the meantime I just opened up the client (#811) so you could still extend it and create your custom client.

0reactions
dariuszkuccommented, Jul 31, 2020

Change was released in 3.5.0

Read more comments on GitHub >

github_iconTop Results From Across the Web

Use generic to store common supertype in Java - Stack Overflow
I could move the arguments to the class itself, like List2<R, T extends R, S extends R> , but the type information really...
Read more >
Subclassing and Inheritance - Learning Java, 4th Edition [Book]
A class in Java can be declared as a subclass of another class using the extends keyword. A subclass inherits variables and methods...
Read more >
Reading 14: Inheritance & Composition - MIT
Instead of extending an existing class, give your new class a private field that references an instance of the existing class. This design...
Read more >
Generics, Inheritance, and Subtypes (The Java™ Tutorials ...
You can subtype a generic class or interface by extending or implementing it. The relationship between the type parameters of one class or...
Read more >
super - JavaScript - MDN Web Docs - Mozilla
The super keyword is used to access properties on an object literal or class's [[Prototype]], or invoke a superclass's constructor.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found