Expose GraphqlExceptions using spring boot starter - spring

When using the Spring Boot starter for graphql, all of the exceptions thrown while data fetching appear in the output console as "Internal Server Error(s) while executing query" I would like the just the e.message() string of the GraphQLException I throw to come out of the "message" part of the error field in Graphql so that the front end of this API can see what went wrong.
I've googled this to no avail. Most people expose the errors by editing the Servlet, but since I'm using the Spring Boot starter I cannot do this easily. I know that the graphql-servlet used by starter has a class called DefaultGraphQLErrorHandler found here but I do not know how to override or change it or just somehow get those errors to display.
I tried overriding the SimpleDataFetcherExceptionHandler in graphql, creating a CustomException that overrides GraphQlException but neither worked, the SimpleDataFetcherExceptionHandler implementation was never called during a debug.
I only ever see this:
Thanks

So I figured this out. I had to provide a custom implementation of a GraphQLErrorHandler and add it to the list of beans by marking as a #Component. I then overrided the processErrors() function in the error handler. When marked as a component, this bean was autowired into the graphql serverlet configurator in the graphql spring boot starter package, replacing the default one.
the CustomGraphQlErrorHandler I added is seen below:
import graphql.GraphQLError
import graphql.servlet.GenericGraphQLError
import graphql.servlet.GraphQLErrorHandler
import org.springframework.stereotype.Component
#Component
class CustomGraphQlErrorHandler: GraphQLErrorHandler {
override fun processErrors(errors: MutableList<GraphQLError>?): MutableList<GraphQLError> {
val errorList = mutableListOf<GraphQLError>()
for(error in errors!!){
errorList.add(GenericGraphQLError(error.message))
}
return errorList
}
}
the output looks like this now:

SimpleDataFetcherExceptionHandler implementation will be called when you have graphql.servlet.exception-handlers-enabled=true in application.properties

Great solution works like a charm.
A more compact way of writing it in kotlin would look like this:
#Component
class CustomGraphQlErrorHandler : GraphQLErrorHandler {
override fun processErrors(errors: MutableList<GraphQLError>?) =
errors?.map { GenericGraphQLError(it.message) }?.toMutableList() ?: mutableListOf()
}
I really like the functional style of this

Related

How to disable management context access log in Spring Boot using undertow

I'm using Spring Boot 2.2.4 with embedded Undertow.
I've enabled the access log using server.underdow.accesslog.enabled=true and everything works as expected.
I'm utilizing the actuator endpoints on a different port which sets up a child context. I do not want requests to the actuator to be logged. Currently they automatically go to management_access.log where access. is the prefix of my main access log.
Any ideas on how to disable that access log? I know Spring is creating a separate WebServer via Factory for the actuator context, but I haven't found a way to customize the factory.
I found my own answer (spent way too much time doing it).
It's a little bit of a hack, but it works:
New configuration class: foo.ManagementConfig
package foo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration;
import org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.context.annotation.Bean;
#ManagementContextConfiguration
public class ManagementConfig {
#Bean
WebServerFactoryCustomizer<UndertowServletWebServerFactory> actuatorCustomizer(#Value("${management.server.port}") int managementPort) {
return factory -> {
if (managementPort == factory.getPort()) {
factory.setAccessLogEnabled(false);
}
};
}
}
Created resources/META-INF/spring.factories so that it gets picked up by the ManagementContext:
org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration=foo.ManagementConfig
The part that's a bit of a hack is the if statement. It would have been great if it applied only to the management context, but for some reason it's trying to apply to both. With the if statement, it just doesn't do anything for the primary context.
This would have unintended consequences if management.server.port was undefined or if it was the same as the primary context.

Micronaut declarative REST client throws an error - #Introduction method interceptor missing

When I autowire the client interface for my Micronaut declarative client, I get this error:
Caused by: java.lang.IllegalStateException: At least one #Introduction method interceptor required, but missing. Check if your #Introduction stereotype annotation is marked with #Retention(RUNTIME) and #Type(..) with the interceptor type. Otherwise do not load #Introduction beans if their interceptor definitions are missing!
at io.micronaut.aop.chain.InterceptorChain.resolveIntroductionInterceptors(InterceptorChain.java:194)
at io.micronaut.context.DefaultBeanContext.doCreateBean(DefaultBeanContext.java:1494)
What's the proper way to fix it?
Details
I have an established Grails application that I recently upgraded from 3.x to 4.0.1.
This app has a service which does several REST calls in parallel, and I am trying to add a new REST call that uses the new Micronaut HTTP declarative client.
I added the client library to dependencies in build.gradle:
compile "io.micronaut:micronaut-http-client"
My client interface looks like this (in src/main/groovy):
package com.mycompany.xyz.rest
import com.mycompany.xyz.rest.myendpoint.Results
import io.micronaut.http.annotation.Get
import io.micronaut.http.annotation.Header
import io.micronaut.http.client.annotation.Client
#Client('xyzRest')
#Header(name = 'myauthkey', value = '${myAuthKey}')
interface XyzRestClient {
#Get('/myendpoint')
Results myendpoint(String param1, String param2)
}
package com.mycompany.xyz.rest.myendpoint
import com.mycompany.xyz.rest.myendpoint.DataItem
import groovy.transform.CompileStatic
#CompileStatic
interface Results extends List<DataItem> {
}
I configured the URL in application.yml:
environments:
development:
micronaut:
http:
services:
xyzRest:
urls:
- http://xyz.mycompany.com/rest/v1
The message about #Introduction makes me think that Micronaut is not doing the process of compiling the declarative client. Is there some
What else am I missing?
Update:
I tried changing the build.gradle dependency to implementation as shown in the Micronaut docs, insteadl of compile, as shown in the Grails docs. No dice.
Update 2:
I found that the constructor for HttpClientIntroductionAdvice is never invoked during startup. I don't know why it's not being included in my project. IntelliJ shows micronaut-http-client:1.1.4 in external libraries, and it's set to compile scope.
A gradlew clean seems to have fixed the issue.
I tried to work backwards and duplicate the problem for posterity's sake, but so far I have not been able to.

Override a Service in Grails using Spring Bean declaration

I am creating a new plugin containing CustomService which is intended to replace an existing service from an existing plugin. Following the pattern found in custom security implementations and shown here, I've added the configuration to the resources.groovy, oldService(path.to.new.CustomService). I've also tried adding all injected classes into the closure for this service.
(Actual service names are RegistrationPersonRegistrationCompositeService and NewRegistrationPersonRegistrationCompositeService in code block)
I dont want the original application code to have any reference to the new plugin. However, BuildConfig at the application level will require plugin.location entry. My resource.groovy mods are in the new plugin. I have not had success in this endeavor. Am I modifying the wrong resources.groovy? If this change is required in the original application code, I've lost the ability to leave the original code unaltered. I'm not extending the original Service nor using override annotation. My intent is to replace the service (Spring bean) on start-up. The new plugin has a dependency on the old plugin in an attempt to manage order of operations in loading these classes.
Does it matter that the old service is previously injected in a controller? this would require me to override the controller in the new plugin in the same fashion and inject the correct service for desired behavior?
I've found documentation showing that within a plugin, the resources.groovy will be ignored. Also, building the resources.groovy into a war is problematic. I have not found a solution. I'm getting no error that I can share, just that the desired behavior is missing; the original service is handling the requests.
//was resource.groovy - now renamed to serviceOverRide.groovy - still located in \grails-app\conf\spring of plugin
//tried this with and without the BeanBuilder. Theory: I'm missing the autowire somehow
import org.springframework.context.ApplicationContext
import grails.spring.BeanBuilder
def bb = new BeanBuilder()
bb.beans {
registrationPersonRegistrationCompositeService(path.to.services.registration.NewRegistrationPersonRegistrationCompositeService) { bean ->
bean.autowire = true
registrationRestrictionCompositeService = ref("registrationRestrictionCompositeService")
registrationPersonTermVerificationService = ref("registrationPersonTermVerificationService")
}
classRegistrationController(path.to.services.registration.ClassRegistrationController) { bean ->
bean.autowire = true
selfServiceLookupService = ref("selfServiceLookupService")
registrationPersonRegistrationCompositeService = ref("registrationPersonRegistrationCompositeService")
}
}
ApplicationContext appContext = bb.createApplicationContext()
Additional information: Added the following lines to the PluginGrailsPlugin.groovy. The original service is still handling these requests
def dependsOn = ['appPersonRegistration': '1.0.20 > *']
List loadAfter = ['appPersonRegistration']
def doWithSpring = {
registrationPersonCourseRegistrationCompositeService(path.to.new.registration.TccRegistrationPersonCourseRegistrationCompositeService)
}
def doWithApplicationContext = { applicationContext ->
SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL)
DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) applicationContext.getBeanFactory()
beanFactory.registerBeanDefinition("registrationPersonCourseRegistrationCompositeService", BeanDefinitionBuilder.rootBeanDefinition(TccRegistrationPersonCourseRegistrationCompositeService.class.getName()).getBeanDefinition())
}
I highly recommend you read the section of the documentation on Plugins. The reason why I recommend this is because plugins:
Do not include, or make use of resources.groovy
Provide a means through doWithSpring to effect the spring application
Following the information in the documentation you should have no issue overriding the service in the application context.
You must implement your changes to the application context using doWithSpring this is the key to solving your issues.
In this implementation, I had a utility method in a service for which I was attempting to provide an override. Problem is, the Aspect works as a proxy and must override a method that is called directly from another class. In my classRegistrationController, I was calling service processRegistration() which in turn called applyRules(). Example-only method names used. Since the service was calling its own utility, there was no opportunity for the proxy/wrapper to circumvent the call to applyRules(). Once this was discovered, I refactored the code in this fashion: Controller calls processRegistration as it always had. After returning, another call is made to the service, processLocalRules(). The new method is an empty placeholder intended to be overridden by the client's custom logic. The plugin with Aspect works now using resources.groovy. I prefer the doWithSpring as Joshua explained for this reason: my intent to get the plugin to work without modification to the original app-config; otherwise resource.groovy is a valid approach. Upvoting Joshua's answer as it does satisfy the requirement and is cleaner. Thanks!

Add property to entity in spring data REST

I am trying out the ResourceProcessor interface in Spring Data REST. I don't think my Processor ever gets called. No error message either.
Here is my processor (in Groovy):
#Autowired
PersonService personService
#Override
public Resource<Person> process(Resource<Person> resource) {
resource.content.isAdult = personService.isAdult(resource.content)
// sanity check: does this link get added?? (NOPE!!)
resource.add(new Link("http://localhost:8080/people", "added-link"))
log.info "## resource.content.isAdult ==> ${resource.content}"
return resource
}
Any help would be most appreciated!! You can see the entire project here in GitHub: https://github.com/txiasummer/spring-data-rest-examples
Finally got it to work! It turns out to be something completely trivial and I can't believe I missed it. I have a PersonProcessor classes which implements Spring's native ResourceProcessor interface. But PersonProcessor is still just a basic class that must be injected by Spring!! I think I was getting it confused with #Projection, which will be recognized automatically and does not need to be injected explicitly.
I addd #ComponentScan to my Application.groovy and now everything is working swimmingly. Alternatively, you an also manually define the PersonProcessor class and its service PersonService class as #Bean in Application.groovy. Again, you can see the whole project here: https://github.com/txiasummer/spring-data-rest-examples

Inject constructor argument Spring Resource file with Grails/Groovy

From our Grails/Groovy app we need to use a service from a legacy Java service class, the constructor of which has a parameter of type org.springframework.core.io.Resource, e.g.
public ServiceClass(Resource someResource)
We need to inject an instance of the service class into a Groovy class of our app using Spring DSL, with the Resource referring to an XML file within our /src/main/resources. I tried to create the Spring config for this purpose, but so far I couldn't find a working solution. The relevant part of the config file looks like this
beans = {
xmlns aop:"http://www.springframework.org/schema/aop",
sec:"http://www.springframework.org/schema/security",
context:"http://www.springframework.org/schema/context"
serviceClass(com.somepackage.ServiceClass) {
//here we need to refer to the constructor arg XML file some way
}
}
I have tried multiple syntaxes found in various tutorials, e.g. closure for beanDefinition.constructorArgs, but unfortunately without success so far. Although neither the app compilation (grails:war) nor the startup (grails:run-app) indicates any problems with the bean wiring, when the app is actually loaded into the browser, we receive a NPE stating that our Groovy class into which the service class is injected, is a null object. So it seems that the bean wiring was not successful after all. Any help is appreciated
After fixing various issues with the project setup itself and multiple cleanups/recompiles, it seems that the following two approaches are both OK
serviceClass(com.somepackage.ServiceClass, '/WEB-INF/constructor-arg-xml-file.xml') {}
and
serviceClass(com.somepackage.ServiceClass) { bean ->
bean.constructorArgs = [
'/WEB-INF/constructor-arg-xml-file.xml'
]
}

Resources