Spring ServerSentEvent with multi-line data - spring

I want to consume ServerSentEvent objects in Spring and I observed that it works as expected only if the data spreads on a single line.
public void subscribe(String url, Consumer<String> function) {
final Flux<ServerSentEvent<String>> eventStream = client.get()
.uri(url)
.retrieve()
.bodyToFlux(type);
eventStream
.doOnNext(input -> System.out.println("[" + input + "]"))
.filter(content -> content.data() != null)
.subscribe(
content -> function.accept(content.data()),
error -> log.error("Error receiving SSE: {}", error.getMessage(), error),
() -> log.info("Event stream completed")
);
}
If I send an event like: { "id": "1" } I will see in the console: [ServerSentEvent [id = 'event.test', event='event', retry=null, comment='null', data={ "id": "1" }]]
But if my event looks like this (it spreads on multiple lines):
{
"id": 1
}
I receive: [ServerSentEvent [id = 'event.test', event='event', retry=null, comment='null', data={ ]]
If I listen to the same events from the console using CURL (curl -XGET http://localhost:8111/subscribe/event.test) I recieve the whole message:
id:event.test
event:event
data:{
"id": "1"
}
Am I missing something?

Related

Is there any cost advantage of Parse.Object.saveAll vs. saving individually?

The Parse JS SDK provides a Parse.Object.saveAll() method to save many objects with one command.
From looking at ParseServerRESTController.js it seems that each object is saved individually:
if (path === '/batch') {
let initialPromise = Promise.resolve();
if (data.transaction === true) {
initialPromise = config.database.createTransactionalSession();
}
return initialPromise.then(() => {
const promises = data.requests.map(request => {
return handleRequest(
request.method,
request.path,
request.body,
options,
config
).then(
response => {
return {
success: response
};
},
error => {
return {
error: {
code: error.code,
error: error.message
},
};
}
);
});
return Promise.all(promises).then(result => {
if (data.transaction === true) {
if (
result.find(resultItem => typeof resultItem.error === 'object')
) {
return config.database.abortTransactionalSession().then(() => {
return Promise.reject(result);
});
} else {
return config.database.commitTransactionalSession().then(() => {
return result;
});
}
} else {
return result;
}
});
});
}
It seems that saveAll is merely a convenience wrapper around saving each object individually, so it still does seem to make n database requests for n objects.
It it correct that saveAll has no cost advantage (performance, network traffic, etc) vs. saving each object individually in Cloud Code?
I can tell you that the answer is that Parse.Object.saveAll and Parse.Object.destroyAll batch requests by default in batches of 20 objects. But why take my word for it? Let's test it out!
Turn verbose logging on and then run the following:
const run = async function run() {
const objects = [...Array(10).keys()].map(i => new Parse.Object('Test').set({i}));
await Parse.Object.saveAll(objects);
const promises = objects.map(o => o.increment('i').save());
return Promise.all(promises);
};
run()
.then(console.log)
.catch(console.error);
And here's the output from the parse-server logs (I've truncated it, but it should be enough to be apparent what is going on):
verbose: REQUEST for [POST] /parse/batch: { // <--- note the path
"requests": [ // <--- an array of requests!!!
{
"method": "POST",
"body": {
"i": 0
},
"path": "/parse/classes/Test"
},
... skip the next 7, you get the idea
{
"method": "POST",
"body": {
"i": 9
},
"path": "/parse/classes/Test"
}
]
}
.... // <-- remove some irrelevent output for brevity.
verbose: RESPONSE from [POST] /parse/batch: {
"response": [
{
"success": {
"objectId": "szVkuqURVq",
"createdAt": "2020-03-05T21:25:44.487Z"
}
},
...
{
"success": {
"objectId": "D18WB4Nsra",
"createdAt": "2020-03-05T21:25:44.491Z"
}
}
]
}
...
// now we iterate through and there's a request per object.
verbose: REQUEST for [PUT] /parse/classes/Test/szVkuqURVq: {
"i": {
"__op": "Increment",
"amount": 1
}
}
...
verbose: REQUEST for [PUT] /parse/classes/Test/HtIqDIsrX3: {
"i": {
"__op": "Increment",
"amount": 1
}
}
// and the responses...
verbose: RESPONSE from [PUT] /parse/classes/Test/szVkuqURVq: {
"response": {
"i": 1,
"updatedAt": "2020-03-05T21:25:44.714Z"
}
}
...
In the core manager code, you do correctly identify that we are making a request for each object to the data store (i.e. MongoDB), This is necessary because an object may have relations or pointers that have to be handled and that may require additional calls to the data store.
BUT! calls between the parse server and the data store are usually over very fast networks using a binary format, whereas calls between the client and the parse server are JSON and go over longer distances with ordinarily much slower connections.
There is one other potential advantage that you can see in the core manager code which is that the batch is done in a transaction.

Graphql returning Cannot return null for non-nullable field Query.getDate. As I am new to graphql I want to know is my approach is wrong or my code?

I have created resolver, schema and handler which will fetch some record from dynamoDB. Now when I perform query then I am getting "Cannot return null for non-nullable field Query.getDate" error. I would like to know whether my approach is wrong or there is any change required in code.
My code : https://gist.github.com/vivek-chavan/95e7450ff73c8382a48fb5e6a5b96025
Input to lambda :
{
"query": "query getDate {\r\n getDate(id: \"0f92fa40-8036-11e8-b106-952d7c9eb822#eu-west-1:ba1c96e7-92ff-4d63-879a-93d5e397b18a\") {\r\n id\r\n transaction_date\r\n }\r\n }"
}
Response :
{
"errors": [
{
"message": "Cannot return null for non-nullable field Query.getDate.",
"locations": [
{
"line": 2,
"column": 7
}
],
"path": [
"getDate"
]
}
],
"data": null
}
Logs of lambda function :
[ { Error: Cannot return null for non-nullable field Query.getDate.
at completeValue (/var/task/node_modules/graphql/execution/execute.js:568:13)
at completeValueCatchingError (/var/task/node_modules/graphql/execution/execute.js:503:19)
at resolveField (/var/task/node_modules/graphql/execution/execute.js:447:10)
at executeFields (/var/task/node_modules/graphql/execution/execute.js:293:18)
at executeOperation (/var/task/node_modules/graphql/execution/execute.js:237:122)
at executeImpl (/var/task/node_modules/graphql/execution/execute.js:85:14)
at execute (/var/task/node_modules/graphql/execution/execute.js:62:229)
at graphqlImpl (/var/task/node_modules/graphql/graphql.js:86:31)
at /var/task/node_modules/graphql/graphql.js:32:223
at graphql (/var/task/node_modules/graphql/graphql.js:30:10)
message: 'Cannot return null for non-nullable field Query.getDate.',
locations: [Object],
path: [Object] } ],
data: null }
2019-02-25T10:07:16.340Z 9f75d1ea-2659-490b-ba59-5289a5d18d73 { Item:
{ model: 'g5',
transaction_date: '2018-07-05T09:30:31.391Z',
id: '0f92fa40-8036-11e8-b106-952d7c9eb822#eu-west-1:ba1c96e7-92ff-4d63-879a-93d5e397b18a',
make: 'moto' } }
Thanks in advance!
This is your code:
const data = {
getDate(args) {
var params = {
TableName: 'delete_this',
Key: {
"id": args.id
}
};
client.get(params, function(err,data){
if(err){
console.log('error occured '+err)
}else{
console.log(data)
}
});
},
};
const resolvers = {
Query: {
getDate: (root, args) => data.getDate(args),
},
};
You're seeing that error because getDate is a a Non-Null field in your schema, but it is resolving to null. Your resolver needs to return either a value of the appropriate type, or a Promise that will resolve to that value. If you change data like this
const data = {
getDate(args) {
return {
id: 'someString',
transaction_date: 'someString',
}
}
}
you'll see the error go away. Of course, your goal is to return data from your database, so we need to add that code back in. However, your existing code utilizes a callback. Anything you do inside the callback is irrelevant because it's ran after your resolver function returns. So we need to use a Promise instead.
While you can wrap a callback with Promise, that shouldn't be necessary with aws-sdk since newer versions support Promises. Something like this should be sufficient:
const data = {
getDate(args) {
const params = //...
// must return the resulting Promise here
return client.get(params).promise().then(result => {
return {
// id and transaction_date based on result
}
})
}
}
Or using async/await syntax:
const data = {
async getDate(args) {
const params = //...
const result = await client.get(params).promise()
return {
// id and transaction_date based on result
}
}
}

Send partial list with errors in GraphQL

With a query that returns a list:
{
users(first: 10) {
messages(first: 10) {
foo
}
}
}
and a resolver for messages -
const errors = [];
async function messagesResolver(user, {first}, ctx, info) {
const messages = await Promise.all(
user.messages.map(
messageId => fetch(messageId).catch(e => {
// Collect error
errors.push(err);
// Ignore this failed message id
return null;
})
)
);
// TODO:
// How do I add errors to the list of errors
// sent to the client
return messages.filter(m => m != null);
}
Is it possible to return only a Partial List of messages whose fetch succeeded? Is it possible send the errors along and let the client decide how to handle the errors?
Replacing message with an Error does this to an extent, but with null values in the resolved list -
messages = [ { 1 }, { 2 }, new Error(3), { 4 }, new Error(5) ]
resolves to
data = [ { 1 }, { 2 }, null, { 4 }, null ]
errors = [ Error(3), Error(5) ]
But, is it possible to send the errors via another API (say, info.addError(new Error(3)) to achieve the following result?
data = [ { 1 }, { 2 }, { 4 } ]
errors = [ Error(3), Error(5) ]

I need Apollo GraphQL to send back http responses

I'd like for the ability to have my express graphQL server to send back the HTTP status code along with the response to the client. At the moment, all I see is data, however, additional functionality based on the status code would be helpful.
I don't see anything here, unless I'm looking in the wrong place:
http://dev.apollodata.com/tools/graphql-server/setup.html
For Apollo Server 2.2.x and later, we can use plugins to custom the HTTP status code. Custom the HTTP status code in willsendresponse event handler. You can check the errors array and set the corresponding HTTP status code for different type of error.
E.g.
server.ts:
import { ApolloServer, gql } from 'apollo-server';
import { ApolloServerPlugin } from 'apollo-server-plugin-base';
function customHTTPStatusPlugin(): ApolloServerPlugin {
return {
requestDidStart(requestContext) {
return {
willSendResponse({ errors, response }) {
if (response && response.http) {
if (errors) {
response.data = undefined;
response.http.status = 500;
}
}
},
};
},
};
}
const typeDefs = gql`
type Query {
hello: String
}
`;
const resolvers = {
Query: {
hello() {
throw new Error('something happened');
},
},
};
const server = new ApolloServer({ typeDefs, resolvers, plugins: [customHTTPStatusPlugin()] });
const port = 3000;
server.listen(port).then(({ url }) => console.log(`Server is ready at ${url}`));
The response:
{
"error": {
"errors": [
{
"message": "something happened",
"locations": [
{
"line": 2,
"column": 3
}
],
"path": [
"hello"
],
"extensions": {
"code": "INTERNAL_SERVER_ERROR",
"exception": {
"stacktrace": [
"Error: something happened",
" at hello (/Users/ldu020/workspace/github.com/mrdulin/apollo-graphql-tutorial/src/stackoverflow/40387508/server.ts:30:13)",
" at field.resolve (/Users/ldu020/workspace/github.com/mrdulin/apollo-graphql-tutorial/node_modules/apollo-server-express/node_modules/graphql-extensions/src/index.ts:274:18)",
" at field.resolve (/Users/ldu020/workspace/github.com/mrdulin/apollo-graphql-tutorial/node_modules/apollo-server-express/node_modules/apollo-server-core/src/utils/schemaInstrumentation.ts:103:18)",
" at resolveFieldValueOrError (/Users/ldu020/workspace/github.com/mrdulin/apollo-graphql-tutorial/node_modules/graphql/execution/execute.js:467:18)",
" at resolveField (/Users/ldu020/workspace/github.com/mrdulin/apollo-graphql-tutorial/node_modules/graphql/execution/execute.js:434:16)",
" at executeFields (/Users/ldu020/workspace/github.com/mrdulin/apollo-graphql-tutorial/node_modules/graphql/execution/execute.js:275:18)",
" at executeOperation (/Users/ldu020/workspace/github.com/mrdulin/apollo-graphql-tutorial/node_modules/graphql/execution/execute.js:219:122)",
" at executeImpl (/Users/ldu020/workspace/github.com/mrdulin/apollo-graphql-tutorial/node_modules/graphql/execution/execute.js:104:14)",
" at Object.execute (/Users/ldu020/workspace/github.com/mrdulin/apollo-graphql-tutorial/node_modules/graphql/execution/execute.js:64:35)",
" at /Users/ldu020/workspace/github.com/mrdulin/apollo-graphql-tutorial/node_modules/apollo-server-express/node_modules/apollo-server-core/src/requestPipeline.ts:548:22"
]
}
}
}
]
}
}
You will also get the HTTP status code - 500 Internal Server Error at the client-side.

How to display mongoose error to backbone views

So, I have a backbone view where I am trying to save a user
this.model.save(user_details, { // this is backbone model
error: function (model, errors) {
},
success: function (model, response) {
}
});
Backbone Model urlRoot points to a backend function where
// here user is a Mongoose schema
user.save(function (err) {
if (err) {
res.send(err.errors);
}
});
I am running some validation in Mongoose schema.
If the validation fails how can I display these "err.errors" on my backbone view.
I can see at terminal if i console log the errors but not being able to send them back to the views.
Found out the solution after looking through the "errors" object
All errors are returned in "errors.resposeText" which has format like
{
"key name": {
"message": "",
"name": "",
"path": "",
"type": "",
"value": ""
}
}
this.model.save(user_details, { // this is backbone model
error: function (model, errors) {
var err = JSON.parse(errors.responseText);
$.each(errors, function (name, err) {
// do something with error
console.log(name + err.message);
}
},
success: function (model, response) {
}
});
NOTE: Errors from mongodb like unique, dup keys are not appended in this format. So its upto us to change them to json and wrap it up in res.errors.
In case of error in unique keys user.save(function (err) {
if (err) {
if(err.code!='undefined' && err.code=='11000')
err.errors = {'email':{'message':'This unique value is already in db'}};
res.send(500, err.errors);
}

Resources