Controller in DGS Netflix Graphql - java

We are developing a project Using Spring boot with DGS Netflix graphql. We are created all the schemas and datafethers which is working absolutely fine with a default endpoint "/graphql". we would like to expose this app with custom endpoing so we are trying to add a controller with a custom endpoint as below. But When i run the application and send a query, my data fetcher is called twice . first time called from controller and second time i believe from framework it self. Anybody has any thoughts on this why its being called twice and how to avoid it? You help is highly appreciated. Please see the below Datafetcher and Controller.
Controller:
#RestController
#RequestMapping("/sample-information/model")
#Slf4j
public class CustomController {
#Autowired
DgsQueryExecutor dgsQueryExecutor;
#PostMapping(consumes = {MediaType.APPLICATION_JSON_VALUE, "application/graphql"})
public Mono<ResponseEntity<Object>> getDetails(#RequestBody String query,
#RequestHeader HttpHeaders headers
) {
GraphQLQueryInput inputs = null;
try {
inputs = ObjectMapperHelper.objectMapper.readValue(query, GraphQLQueryInput.class);
} catch (Exception e) {
log.error("TraceId: {} - Application Error: Error message: Error converting query to GraphQLQueryInput: {} "+ query);
}
if(inputs.getVariables() == null) {
inputs.setVariables(new HashMap<>());
}
if(inputs.getOperationName() == null) {
inputs.setOperationName("");
}
final String que = inputs.getQuery();
final Map<String, Object> var = inputs.getVariables();
final String opn = inputs.getOperationName();
ExecutionInput.Builder executionInput = ExecutionInput.newExecutionInput()
.query(inputs.getQuery())
.operationName(inputs.getOperationName())
.variables(inputs.getVariables());
return Mono.fromCallable(()-> {
return dgsQueryExecutor.execute(que, var, opn);
}).subscribeOn(Schedulers.elastic()).map(result -> {
return new ResponseEntity<>(result, HttpStatus.OK);
});
}
}
Datafetcher:
#DgsComponent
#Slf4j
public class SampleDataFetcher {
#Autowired
SampleBuilder sampleBuilder;
#DgsData(parentType = DgsConstants.QUERY_TYPE, field = DgsConstants.QUERY.SampleField)
public CompletableFuture<StoreDirectoryByStateResponse> getStoreDirectoryByState(#InputArgument String state, DataFetchingEnvironment dfe) throws BadRequestException {
Mono<StoreDirectoryByStateResponse> storeDirectoryResponse = null;
try {
sampleResponse = sampleBuilder.buildResponse(modelGraphQLContext);
} catch (BaseException e) {
}
return sampleResponse.map(response -> {
return response;
}).toFuture();
}
}

Related

Grails RestBuilder dont findendPoint with Object in signature

I have a code with a RestBuilder that needs to connect to another application, the target endPoint have an object in the signature with the attributes. The problem is the request return 404. How I solve this? I tried use x-www-form-urlencoded (doesn't work)
Request Method:
RestResponse restResponse;
String parameters = '{"qtdThreads":3,"channel":"http://localhost:8081/application2"}'
try {
restResponse = new RestBuilder().post("http://localhost:8080/application/endPoint", {
accept("application/json")
contentType "application/json; utf-8"
body(parameters.getBytes("UTF-8"))
connectTimeout: 1000
})
} catch (Exception e) {
e.printStackTrace();
} finally {
return restResponse;
}
Target endPoint:
Object endPoint(ObjectCommand command) {
render(status: HttpStatus.OK)
}
Object used on signature
import grails.validation.Validateable
#Validateable
class ObjectCommand {
URL channel
Integer qtdThreads
static constraints = {
qtdThreads(validator: { Integer val ->
if (!val || val <= 0) {
return "message1"
}
})
channel(validator: { URL val ->
if (!val) {
return "message2"
}
})
}
}
did you check if the target application is running and exposing that endpoint?

React "fetch" method not talking to java based Controller

I am attempting to get some data from a database to load into a React based component in my front end. In order to do this, I have created a fetch request that talks to a java based controller on the backend that then eventually hits the database and pulls the information from the database. However, when I attempt to maven build the project, it fails repeatedly and provides the error "TypeError: Cannot read property 'then' of undefined" which from searching implies a Promise isn't being returned by the fetch request.
My assumption based on several tests is that my fetch request isn't actually being directed to my controller somehow (and debugging the build fails to hit a breakpoint within the controller as well). Looking for any assistance to see why these two pieces aren't talking to each other.
I should also mention I have an insert method in the same view portion that talks to the same controller and that works successfully.
Controller Portion:
#Component
#RestController
#RequestMapping(value="/api/process/")
public class ProcessController {
private final ProcessService processService;
#Autowired
public ProcessController(ProcessService processService) { this.processService = processService; }
#RequestMapping(value="add", method = RequestMethod.POST)
public ResponseEntity<String> insertProcess(#RequestBody() String processJson) {
Gson gson = new Gson();
Process process = gson.fromJson(processJson, Process.class);
try {
return new ResponseEntity<>(gson.toJson(processService.insertProcess(process)), HttpStatus.OK);
} catch(DataAccessException de) {
return new ResponseEntity<>(de.getCause().getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
}
}
#RequestMapping(value="select", method = RequestMethod.POST)
public ResponseEntity<String> selectProcesses() {
try {
return new ResponseEntity<>(new Gson().toJson(processService.selectProcesses()), HttpStatus.OK);
} catch(DataAccessException de) {
return new ResponseEntity<>(de.getCause().getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
}
}
}
And the fetch calls:
class ProcessApi {
static addProcess(processJson) {
return fetch('/api/process/add', {
method: 'POST',
body: processJson
})
.then(response => {
if(response.status === 200) {
return response.json()
.then(jsonData => {
return jsonData;
});
}
})
.catch((error) => {
console.error(error);
return error.json();
});
};
static getProcesses() {
return fetch('/api/process/select', {
method: 'POST'
})
.then(response => {
if(response.status === 200) {
return response.json()
.then(jsonData => {
return jsonData;
});
}
})
.catch((error) => {
console.error(error);
return error.json();
});
};
}
As mentioned, the "addProcess" portion works just fine. The select just isn't connecting up and I can't seem to find out why. For reference I have the API call happening in the componentDidMount() method in my view portion.

Micronaut get handling object/method in Filter

So i have following controller
Controller("/test")
public class MyController {
#Get("/anno")
#MyAnnotation(value="my annotation value") // <---- i want this value
public Object testAnnotation(HttpRequest<?> request){
return "Hello world";
}
}
I'm trying to implement custom filter on micronauts http server.
#Filter("/**")
public class MyFilter implements HttpServerFilter {
#Override
public Publisher<? extends HttpResponse<?>> doFilter(HttpRequest<?> request, FilterChain chain) {
// HERE
// how to get the MyAnnotation value from the handling method for the request ?
return chain.proceed(request);
}
}
How to get my custom annotation inside the filter ?
Thank you.
You would need AOP, Micronauts supports it. But you get it from a MethodInterceptor, not HttpFilter. Here is the code I modified based on what I wrote for tracing, in Kotlin, it would be very similar in Java:
#Singleton
#InterceptorBean(MyAnnotation::class)
class MyAnnotationInterceptor : MethodInterceptor<Any, Any> {
override fun intercept(context: MethodInvocationContext<Any, Any>): Any? {
val myAnnotation: AnnotationValue<MyAnnotation> = context.getAnnotation(MyAnnotation::class.java)!!
val value = myAnnotation.get("value", String::class.java).orElse(null)
val className = context.declaringType.simpleName
val methodName = context.methodName
val operationName = "$className.$methodName"
val interceptedMethod = InterceptedMethod.of(context)
try {
when (interceptedMethod.resultType()) {
SYNCHRONOUS -> {
try {
return context.proceed()
} catch (e: Exception) {
throw e
} finally {
}
}
COMPLETION_STAGE -> {
try {
var completionStage = interceptedMethod.interceptResultAsCompletionStage()
return interceptedMethod.handleResult(completionStage)
} catch (e: Exception) {
logError(newSpan, e)
throw e
}
}
else -> return interceptedMethod.unsupported()
}
} catch (e: Exception) {
return interceptedMethod.handleException<RuntimeException>(e)
}
}
}
val value = myAnnotation.get("value", String::class.java).orElse(null) is where you get the value.
We use the above code to extract the tracing sampling rate and it works well for us. Note that your annotation will need "around" AOP annotation:
#Retention(RUNTIME)
#Target(CLASS, FILE, FUNCTION, PROPERTY_GETTER, PROPERTY_SETTER)
#Around
annotation class MyAnnotation(
val value: String = "",
)

Graphql return enum collection

I want to return all values of enum using graphql.
I have schema:
schema {
query: Query
}
type Query {
getDataTypes: [DictionaryType]
}
enum DictionaryType{
RISK
SALES_CHANNEL
PERSON_TYPE
}
We have normal java enum:
public enum DictionaryType {
RISK,
SALES_CHANNEL,
PERSON_TYPE
}
and Controller with configuration:
public class DictionaryController {
#Value("classpath:items.graphqls")
private Resource schemaResource;
private GraphQL graphQL;
private final DictionaryService dictionaryService;
#PostConstruct
public void loadSchema() throws IOException {
File schemaFile = schemaResource.getFile();
TypeDefinitionRegistry registry = new SchemaParser().parse(schemaFile);
RuntimeWiring wiring = buildWiring();
GraphQLSchema schema = new SchemaGenerator().makeExecutableSchema(registry, wiring);
graphQL = GraphQL.newGraphQL(schema).build();
}
private RuntimeWiring buildWiring() {
DataFetcher<Set<DictionaryType>> fetcher3 = dataFetchingEnvironment -> {
return dictionaryService.getDictionaryTypes();
};
return RuntimeWiring.newRuntimeWiring().type("Query", typeWriting ->
typeWriting
.dataFetcher("getDataTypes", fetcher3))
.build();
}
#PostMapping("getDataTypes")
public ResponseEntity<Object> getDataTypes(#RequestBody String query) {
ExecutionResult result = graphQL.execute(query);
return new ResponseEntity<Object>(result, HttpStatus.OK);
}
}
When i make POST to http://localhost:50238/getDataTypes
with body:
{
getDataTypes {
}
}
I get "errorType": "InvalidSyntax",
in response.
That's an invalid query as you have braces with no content (i.e. { }). Your schema suggests that the query should be much simpler though:
{ getDataTypes }

Springboot: How to make Rest Service Controller non-blocking

I have a spring boot application which validates a file for a given client id and returns a json response of validation errors and warnings, while performing load test we noticed most the requests being blocked, so am trying to make our application non blocking by leveraging Spring's non blocking api
Below is my spring version
springBootVersion = '1.5.3.RELEASE'
springVersion = '4.3.8.RELEASE'
Below is my springboot ValidationController.groovy which is blocking requests
#Controller
#ResponseBody
class ValidationController {
#RequestMapping(value = "/validate", method = RequestMethod.POST, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
ResponseEntity<ValidationResult> validate(#RequestParam(value = "file", required = true) MultipartFile file,
#RequestParam(value = "client_id", required = true) String clientId)
{
if (clientId.isEmpty()) {
String msg = "client id is required"
if (LOGGER.isErrorEnabled()) {
LOGGER.error(msg)
}
return ResponseEntity.status(HttpStatus.UNPROCESSABLE_ENTITY).body(msg);
}
String contentType = file.contentType.toLowerCase();
if (LOGGER.isDebugEnabled()) LOGGER.debug("content type = $contentType");
Client client = clientRepository.findByExternalId(clientId)
if (client == null) {
String msg = "client id is invalid"
if (LOGGER.isErrorEnabled()) {
LOGGER.error(msg)
}
return ResponseEntity.status(HttpStatus.UNPROCESSABLE_ENTITY).body(msg);
}
if (file.isEmpty()) {
String msg = "file is empty"
if(LOGGER.isErrorEnabled()) {
LOGGER.error(msg)
}
return ResponseEntity.status(HttpStatus.UNPROCESSABLE_ENTITY).body(msg);
}
ValidationResult result = validationService.validate(file, client);
return ResponseEntity.ok(result)
}
}
class ValidationResult {
private List<Warning> warnings
private List<Error> errors
//getters setters for warnings and errors
}
class Warning {
private String message
private String type
//getters setters for message and type
}
class Error {
private String message
private String type
//getters setters for message and type
}
I have modified my ValidationController.groovy as below
#Controller
#ResponseBody
class ValidationController {
#Autowired
#Qualifier("postRequestExecutorService")
private ExecutorService postRequestExecutor;
#RequestMapping(value = "/validate", method = RequestMethod.POST, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public DeferredResult<ResponseEntity<ValidationResult>> validate(#RequestParam(value = "file", required = true) MultipartFile file,
#RequestParam(value = "client_id", required = true) String clientId)
{
DeferredResult<ResponseEntity<ValidationResult>> deferredResult = new DeferredResult<>();
CompletableFuture.supplyAsync(() -> validate(clientId, file), postRequestExecutor)
.whenComplete((result, throwable) ->
{
deferredResult.setResult(result);
} );
}
private ResponseEntity<ValidationResult> validateLedes(String clientId, MultipartFile file) {
ValidationResult result;
try{
if (clientId.isEmpty()) {
String msg = messageSource.getMessage("client.id.required", null, Locale.getDefault())
LOGGER.error(msg)
return ResponseEntity.status(HttpStatus.UNPROCESSABLE_ENTITY).body(msg);
}
String contentType = file.contentType.toLowerCase();
if (LOGGER.isDebugEnabled()) LOGGER.debug("content type = $contentType");
Client client = clientRepository.findByExternalId(clientId)
if (client == null) {
String msg = messageSource.getMessage("client.id.invalid", null, Locale.getDefault())
LOGGER.error(msg)
return ResponseEntity.status(HttpStatus.UNPROCESSABLE_ENTITY).body(msg);
}
if (file.isEmpty()) {
String msg = messageSource.getMessage("ledes.file.empty", null, Locale.getDefault())
LOGGER.error(msg)
return ResponseEntity.status(HttpStatus.UNPROCESSABLE_ENTITY).body(msg);
}
result = validationService.validate(file, Ledesxmlebilling21.class, client);
}
catch (Exception ex){
LOGGER.error("Exception in validateLedes = "+ex.message)
LOGGER.error("StackTrace in validateLedes = "+ex.stackTrace)
}
return ResponseEntity.ok(result)
}
}
And below is my ExecutorServiceConfiguration
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
#Configuration
public class RequestsExecuterServiceConfiguration {
/**
* Dedicated Thread Modeling to handle POST Requests
*/
#Bean
public ExecutorService postRequestExecutorService() {
final ThreadFactory threadFactory = new ThreadFactoryBuilder()
.setNameFormat("postRequestExecutor-%d")
.setDaemon(true)
.build();
ExecutorService es = Executors.newFixedThreadPool(10,threadFactory);
return es;
}
}
Since my controller is a groovy class am seeing some compiler errors for the CompletableFuture lambda expression, can someone please help me make it work for groovy controller?
UPDATE1
As per the Answer I've changed the labda expression to closure as below
#RequestMapping(value = "/validate", method = RequestMethod.POST, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public DeferredResult<ResponseEntity<ValidationResult>> validate(#RequestParam(value = "file", required = true) MultipartFile file,
#RequestParam(value = "client_id", required = true) String clientId)
{
DeferredResult<ResponseEntity<ValidationResult>> deferredResult = new DeferredResult<ResponseEntity<ValidationResult>>();
CompletableFuture.supplyAsync({ -> validateLedes(clientId, file) }, postRequestExecutor)
.whenComplete({ futureResult, throwable -> deferredResult.setResult(futureResult);})
deferredResult
}
With the above controller, am not getting below errors
2018-04-11 15:07:45 - Exception in validateLedes = failed to lazily initialize a collection of role: com.validation.entity.Client.ruleConfigurations, could not initialize proxy - no Session
2018-04-11 15:07:45 - StackTrace in validateLedes = org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:587), org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:204), org.hibernate.collection.internal.AbstractPersistentCollection.readSize(AbstractPersistentCollection.java:148), org.hibernate.collection.internal.PersistentSet.size(PersistentSet.java:143)
Looks like the issue is, Hibernate session is not bound to ExecutorService and the new thread in which validateLedes method is executing it's unable to read from the database, can someone please me to binding Hibernate session to the the ExecutorService's thread pool?
You can't just stick lambdas into Groovy (until Groovy 3)
You'll need to translate them to Closures, so for example:
() -> validate(clientId, file)
becomes:
{ -> validate(clientId, file) }
And
(result, throwable) ->
{
deferredResult.setResult(result);
}
would be:
{ result, throwable -> deferredResult.setResult(result) }

Categories