Apollo Server Health Check custom response - graphql

I am currently looking to provide more information to the health check other than status: pass. Is this possible? I tried sending test strings unfortunately, I am still seeing the same response json. Thank you in advance!
Code:
onHealthCheck: (req) => {
return new Promise((resolve, reject) => {
if(true) {
console.log(req);
resolve(req);
//resolve("test")
} else {
reject();
}
})
}

onHealthCheck implementation looks like this (express is used):
if (onHealthCheck) {
onHealthCheck(req)
.then(() => {
res.json({ status: 'pass' });
})
.catch(() => {
res.status(503).json({ status: 'fail' });
});
}
so as you can see, it returns hardcoded value.
You can always implement your custom health check also using express.

Related

Cypress cy.request .then chaining returning undefined

I'm upgrading Cypress from 10.2.0 to 10.11.0 and I'm encountering some behaviour I'm trying to understand.
In the second .then, the response is undefined. This had previously worked on 10.2.0.
public makeRequest(params) {
return cy.request({
...params
})
.then((response) => {
// do something with response
});
}
this.makeRequest(params)
.then((response) => {
// response is undefined
});
Can anyone point me in the right direction, I have checked the changelogs for every version since 10.3.0 and cannot find anything to explain this behaviour.
Thanks!
Cypress (version 10.11.0 and previous versions) returns the last command result taken within the cy.request().then() chain, when there is no explicit return value given.
For example, if // do some async tasks is a non-Cypress asynchronous query, the response is returned:
cy.visit('http://example.com');
function makeRequest(params) {
return cy.request(params)
.then((response) => {
// do some async tasks
setTimeout(() => {
console.log(response.title)
expect(response.body.title).to.eq('delectus aut autem') // passes
}, 1000)
})
}
makeRequest({url: 'https://jsonplaceholder.typicode.com/todos/1'})
.then(response => {
expect(response.body.title).to.eq('delectus aut autem') // passes
})
If however you issue more Cypress commands inside the // do some async tasks block, the last chained "subject" changes, and you get a different return value
cy.visit('http://example.com');
function makeRequest(params) {
return cy.request(params)
.then((response) => {
// do some async tasks
cy.get('h1') // changes "subject" from response to <h1> element
})
}
makeRequest({url: 'https://jsonplaceholder.typicode.com/todos/1'})
.then(response => {
console.log(response) // not your response, but the last "subject" found above
expect(response.text()).to.eq('Example Domain') // passes
})
Adding a return returns the response
public makeRequest(params) {
return cy.request({
...params
})
.then((response) => {
// do something with response
return response;
});
}
If you are doing some async tasks in makeRequest.then(), do your async tasks and return response in another .then. E.g:
public makeRequest(params) {
return cy.request({
...params
})
.then((response) => {
// do some async tasks
})
.then((response) => {
return response;
});
}

error Policy in Apollo Client React does'nt work

I have aproblem when test Apollo.When I try query with apollo and graphql, i want response return error and partical data, so I set property errorPolicy:'all'. But its not work. I don't no why? Help please!
Here my code:
query { animal {
name
age }, school {
name
numberfd } } `
const { loading,data,error} = useQuery(GET_DASHBOARD_DATA, {
errorPolicy:'all',
onCompleted: (res) => {console.log("complete",res)},
onError : (res,data) => {console.log("ERRRR",res,data)},
})
and i want to receive:
{
error:[...], data:[animal:[...]] }
but its only response error.Here is Apollo's doc: https://www.apollographql.com/docs/react/data/error-handling/
onError type is onError?: (error: ApolloError) => void;. You don't have data inside onError callback.
After useQuery you can add:
console.log('data', data)
console.log('error', error)
I faced the same issue with errorPolicy: 'all', I only received the partial result inside onCompleted callback of useQuery, but no errors.
I created an ErrorLink like this:
private createErrorLink = () => {
return new ApolloLink((operation, forward) => {
return forward(operation).map((response) => {
// filter out errors you don't want to display
const errors = filterSomeErrors(response.errors);
if (errors && response?.data) {
response.data.errors = errors;
}
return response;
});
});
};
Now inside my onCompleted callback I get my data as well as errors. You will have to tweak your types a bit, because seems there is no errors field on response.data by default.
Mind that if you use onError from Apollo and return something from the link, it will retry your request containing errors!

Mock specific graphql request in cypress when running e2e tests

When running e2e tests with Cypress, my goal is to mock a specific graphql query.
Currently, I can mock all requests like this:
cy.server();
cy.route('POST', '/graphql', {
data: {
foo: 'bar'
},
});
The problem is that this mocks all /graphql queries. It would be awesome if I somehow could say:
cy.route('POST', '/graphql', 'fooQuery', {
data: {
foo: 'bar'
},
});
In our application, we are using Apollo Graphql - and thus all queries are named.
With cypress 6.0 route and route2 are deprecated, suggesting the use of intercept. As written in the docs (https://docs.cypress.io/api/commands/intercept.html#Aliasing-individual-GraphQL-requests) you can mock the GraphQL requests in this way:
cy.intercept('POST', '/api', (req) => {
if (req.body.operationName === 'operationName') {
req.reply({ fixture: 'mockData.json'});
}
}
One way to go about it is to provide the mocked data for the graphql operations in question inside one fixture file
cypress/support/commands.js
Cypress.Commands.add('stubGraphQL', (graphQlFixture) => {
cy.fixture(graphQlFixture).then((mockedData) => {
cy.on('window:before:load', (win) => {
function fetch(path, { body }) {
const { operationName } = JSON.parse(body)
return responseStub(mockedData[operationName])
}
cy.stub(win, 'fetch', fetch).withArgs("/graphql").as('graphql');
});
})
})
const responseStub = result => Promise.resolve({
json: () => Promise.resolve(result),
text: () => Promise.resolve(JSON.stringify(result)),
ok: true,
})
//TODO how to get it to stop listening and trying to stub once the list of operations provided in fixture have been stubbed?
example fixture file cypress/fixtures/signInOperation.json (note that there are 2 operations in there and that's how you can specify which response to mock)
{
"SIGNIN_MUTATION": {
"data":{"signin":{"id":"ck896k87jac8w09343gs9bl5h","email":"sams#automation.com","name":"Sam","__typename":"User"}}
},
"CURRENT_USER_QUERY" : {
"data":{"me":{"id":"ck896k87jac8w09343gs9bl5h","email":"sams#automation.com","name":"!!Sam's Mock","permissions":["USER"],"cart":[{"id":"ck89gebgvse9w0981bhh4a147","quantity":5,"item":{"id":"ck896py6sacox0934lqc8c4bx","price":62022,"image":"https://res.cloudinary.com/deadrobot/image/upload/v1585253000/sickfitz/ecgqu4i1wgcj41pdlbty.jpg","title":"MensShoes","description":"Men's Shoes","__typename":"Item"},"__typename":"CartItem"},{"id":"ck89gec6mb3ei0934lmyxne52","quantity":5,"item":{"id":"ck896os7oacl90934xczopgfa","price":70052,"image":"https://res.cloudinary.com/deadrobot/image/upload/v1585252932/sickfitz/i7ac6fqhsebxpmnyd2ui.jpg","title":"WomensShoes2","description":"Women's Shoes","__typename":"Item"},"__typename":"CartItem"},{"id":"ck89gl45psely0981b2bvk6q5","quantity":7,"item":{"id":"ck89ghqkpb3ng0934l67rzjxk","price":100000,"image":"https://res.cloudinary.com/deadrobot/image/upload/v1585269417/sickfitz/eecjz883y7ucshlwvsbw.jpg","title":"watch","description":"Fancy Watch","__typename":"Item"},"__typename":"CartItem"}],"__typename":"User"}}
}
}
in your spec file
cy.stubGraphQL('signInOperation.json')
cy.visit(yourURL)
cy.get(loginButton).click()
With cypress 5.1, using the new route2 command it is very simple to mock GraphQL requests, for example:
cy.route2('/graphql', (req) => {
if(req.body.includes('operationName')){
req.reply({ fixture: 'mockData.json'});
}
});
I just added an if condition to evaluate if the body of the GraphQL request contains certain string as part of the query.
If that is true, then I reply back with a custom body loaded from a fixture.
Documentation for cy.route2():
https://docs.cypress.io/api/commands/route2.html
You can try this if want to use fixture for graphql response:
cy.intercept('POST', '/test_api/graphql', (req) => {
req.continue((res) => {
if (req.body.operationName === 'op_name') {
res.send({ fixture: 'MyFixture/xyz.json' }),
req.alias = 'graphql'
}
})
})

Back-end tests with elasticsearch fails without setTimeout

I am writing tests for back-end which uses MongoDB and Elasticsearch. The problem is that without wrapping test with setTimeout test fails, and it looks like elasticsearch can't index mock data into db before test. Here is the code:
let elasticSearch = require('elasticsearch');
let elasticClient = new elasticSearch.Client({
host: 'localhost:9200'
});
let app = require('./dist/app'); //path to my application
let supertest = require('supertest');
before((done) => {
elasticClient.index(elasticMockData, function() {
done();
});
});
beforeEach(() => {
request = supertest(app);
});
it('should get data from elastic', () => {
setTimeout(() => { // if I comment this timeout, test will fail
request.get('/api/elastic')
.end((err, res) => {
expect(res.body.hits.hits.length).not.to.equal(0);
})
}, 1000); // if I comment this timeout, test will fail
});
I think you will agree that timeout is not an elegant and nice solution, which slows every test to 1 second or more. Maybe, am I missing something?
Found a solution, maybe it will be useful for someone.
According to Elasticsearch docs:
By default, the document will be available for get() actions immediately, but will only be available for searching after an index refresh (which can happen automatically or manually).
So, in this case, done() should be called within another callback function:
before((done) => {
elasticClient.index(elasticMockData, function() {
elasticClient.indices.refresh(function (err: any, res: any) {
if (err) {
return;
}
done();
});
});
});

Apollo client mutation error handling

I'm using GraphQL and mongoose on the server.
When a validation error occurs the GraphQL mutation sends a response with status code 200. On the client side the response looks like this:
{
"data": null,
"errors": [{
"message": "error for id...",
"path": "_id"
}]
}
I would like to get access to the validation error using the catch functionality of the apollo-client mutation promise. Something like:
this.props.deleteProduct(this.state.selectedProductId).then(response => {
// handle successful mutation
}).catch(response => {
const errors = response.errors; // does not work
this.setState({ errorMessages: errors.map(error => error.message) });
});
How can this be done?
The previous answer from #stubailo does not seem to cover all use cases. If I throw an error on my server side code the response code will be different than 200 and the error will be handled using .catch() and not using .then().
Link to the issue on GitHub.
The best is probably to handle the error on both .then() and .catch().
const { deleteProduct } = this.props;
const { selectedProductId } = this.state;
deleteProduct(selectedProductId)
.then(res => {
if (!res.errors) {
// handle success
} else {
// handle errors with status code 200
}
})
.catch(e => {
// GraphQL errors can be extracted here
if (e.graphQLErrors) {
// reduce to get message
_.reduce(
e.graphQLErrors,
(res, err) => [...res, error.message],
[]
);
}
})
Note: This answer (and arguably the whole question) is now outdated, since mutation errors show up in catch in more recent versions of Apollo Client.
GraphQL errors from the mutation currently show up in the errors field on the response inside then. I think there's definitely a claim to be made that they should show up in the catch instead, but here's a snippet of a mutation from GitHunt:
// The container
const withData = graphql(SUBMIT_REPOSITORY_MUTATION, {
props: ({ mutate }) => ({
submit: repoFullName => mutate({
variables: { repoFullName },
}),
}),
});
// Where it's called
return submit(repoFullName).then((res) => {
if (!res.errors) {
browserHistory.push('/feed/new');
} else {
this.setState({ errors: res.errors });
}
});
Using graphql tag notation, yo have access to errors:
<Mutation mutation={UPDATE_TODO} key={id}>
{(updateTodo, { loading, error }) => (
<div>
<p>{type}</p>
<form
onSubmit={e => {
e.preventDefault();
updateTodo({ variables: { id, type: input.value } });
input.value = "";
}}
>
<input
ref={node => {
input = node;
}}
/>
<button type="submit">Update Todo</button>
</form>
{loading && <p>Loading...</p>}
{error && <p>Error :( Please try again</p>}
</div>
)}
</Mutation>
https://www.apollographql.com/docs/react/essentials/mutations.html

Resources