I'm using nock to fake out the response from a GraphQL server, however it seems that my nock code isn't being respected, and I can't quite seem to understand why.
What follows is a large chunk of code, but it seems that when I output the result of my function call, the data isn't what I expect.
In the test for: retrieves each release id individually it calls nock twice, but where i'm expecting output like:
[ { id: 123456,
asset_controller: { id: 54321 },
display_author: 'Jason',
author: [ [Object] ] },
{ id: 78902,
asset_controller: { id: 54321 },
display_author: 'King',
author: [ [Object] ] } ]
I'm instead getting 2 of the exact same Jason objects back.
In the test for: returns an expected result containing errors when the release is not available, I should be getting an array with an error in, but i am getting a single Jason object back.
I'm wondering if my beforeEach, where i stub a nock response, is overriding the nock responses in each of my tests? Removing the nock response in the beforeEach results in errors.
const Releases = require('../../../src/services/Releases');
const authorisation = require('../../../src/services/Authorisation');
const ServiceDiscovery = require('service-discovery');
const Lock = require('../../../src/helpers/Lock');
const http = require('http');
describe('Releases', () => {
let serviceDiscovery;
const address = '192.0.0.1';
const port = 4002;
const url = 'http://' + address + ':' + port;
const token = 'abcd';
beforeEach(() => {
sinon.stub(authorisation, 'authorise').resolves(
{
token: token,
expiresAt: 12344657547
}
);
authorisation.token = token;
serviceDiscovery = sinon.createStubInstance(ServiceDiscovery);
serviceDiscovery.discoverServiceDetails.resolves(
[
{
Address: address,
ServicePort: port,
},
]
);
nock(url, {
reqheaders: {
'authorization': 'Bearer ' + token,
'content-type': 'application/json',
},
})
.post('/graphql')
.reply(200, {
data: {
ReleaseFormat: [
{
id: 123456,
asset_controller: {
id: 54321,
},
display_author: 'Jason',
author: [
{
id: 123456,
name: 'jason',
},
],
}
]
}
});
});
afterEach(() => {
authorisation.authorise.restore();
});
describe('getReleases', () => {
it('retrieves each release id individually', async () => {
const releaseIDs = [123456, 78902];
const releases = new Releases(authorisation, http, serviceDiscovery, Lock);
serviceDiscovery.discoverServiceDetails.resolves(
[
{
Address: address,
ServicePort: port,
alive: true,
},
]
);
nock(url, {
reqheaders: {
'Authorization': 'Bearer ' + token,
'content-type': 'application/json',
},
})
.post('/graphql', JSON.stringify({
'query': `{
ReleaseFormat(qbe: {
id: 123456
asset_controller: {
id: 57753805
}
})
{
id,
asset_controller {
id,
name
},
display_author,
author {
id,
name,
ISNI
},
}
}`
}))
.reply(200, {
data: {
ReleaseFormat: [
{
id: 123456,
asset_controller: {
id: 54321,
},
display_author: 'Jason',
author: [
{
id: 123456,
name: 'jason',
},
],
}
]
}
})
.post('/graphql', JSON.stringify({
'query': `{
ReleaseFormat(qbe: {
id: 78902
asset_controller: {
id: 57753805
}
})
{
id,
asset_controller {
id,
name
},
display_author,
author {
id,
name,
},
}
}`
}))
.reply(200, {
data: {
ReleaseFormat: [
{
id: 78902,
asset_controller: {
id: 54321,
},
display_author: 'King',
author: [
{
id: 8764567,
name: 'king',
},
],
}
]
}
});
const spy = sinon.spy(releases, '_getReleaseData');
const actual = await releases.getReleases(releaseIDs);
console.log(actual);
expect(spy.called).to.eql(true);
expect(spy.callCount).to.eql(releaseIDs.length);
spy.restore();
});
it('returns an expected result containing errors when the release is not available', async () => {
const releaseIDs = [123456];
const releases = new Releases(authorisation, http, serviceDiscovery, Lock);
serviceDiscovery.discoverServiceDetails.resolves(
[
{
Address: address,
ServicePort: port,
alive: true,
},
]
);
nock(url, {
reqheaders: {
'authorization': 'Bearer ' + token,
'content-type': 'application/json',
},
})
.post('/graphql', JSON.stringify({
'query': `{
ReleaseFormat(qbe: {
id: 123456
asset_controller: {
id: 57753805
}
})
{
id,
asset_controller {
id,
name
},
display_author,
author {
id,
name,
},
}
}`
}))
.reply(200, {
data: {
ReleaseFormat: []
}
});
const expected = [
new Error(`Not a valid release for ID: ${releaseIDs[0]}`)
];
const actual = await releases.getReleases(releaseIDs);
console.log(actual);
expect(actual).to.be.an('array');
expect(actual.length).to.be.eql(expected.length);
expect(actual.filter(e => e instanceof Error).length).to.be.eql(1);
});
});
});
Related
{
id: 23323091,
userId: 1743514,
teamId: 1693131,
name: 'test1'
},
{
id: 2332950,
userId: 1743514,
teamId: 1693131,
name: 'test2'
},
{
id: 23323850,
userId: 1743514,
teamId: 1693131,
name: 'test3'
}
Im trying find solution how to parse json result returned from GET request in node.js.
So I want find in json array with ex. name: 'test3' return id:
Here is full code:
const axios = require('axios');
let token = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6MTc0MzUxNCwidXNlcm5hbWUiOiJzbmFwa2l0cmljaEBnbWFpbC5jb20iLCJyb2xlIjoiYWRtaW4iLCJ0ZWFtSWQiOjE2OTMxMzEsInRv';
axios.get('https://dolphin-anty-api.com/browser_profiles', {
headers: {
Authorization: `Bearer ${token}`
}
})
.then(function (response) {
const data = response.data;
console.log(data);
})
.catch(function (error) {
console.log(error);
});
and here is full data response from console log https://pastebin.com/wY0RVsUv
You can use the Array#find method
const elements = [
{
id: 23323091,
userId: 1743514,
teamId: 1693131,
name: 'test1'
},
{
id: 2332950,
userId: 1743514,
teamId: 1693131,
name: 'test2'
},
{
id: 23323850,
userId: 1743514,
teamId: 1693131,
name: 'test3'
}
]
function search(name) {
return elements.find(elem => elem.name == name)?.id
}
console.log(search('test3'))
console.log(search('test4'))
If you are inside a GET request, to return a Json just do response.json()
Store your JSON inside a const for example
const data = myParsedJsonObject
You can find whatever data you looking for using the ES6 find() method.
const names = data.find(el => el.name === "test3")
I've been looking all over for an answer to this and I've been banging my head on the wall. I wrote a cursor based pagination example that works well with graphql and the thing is I thought I would do the same thing with authors, that I did with books and the only way I can figure out how to do this is to completely duplicate everything. On the root query there is quite a long chunk of code handling the pagination and I would hate to do that all over for the authors endpoint but I can't seem to find a way to do this while reusing the code
Here is the code
const express = require('express')
const { graphqlHTTP } = require('express-graphql')
const {
GraphQLSchema,
GraphQLObjectType,
GraphQLString,
GraphQLList,
GraphQLInt,
GraphQLNonNull
} = require('graphql')
const {
PageType,
convertNodeToCursor,
convertCursorToNodeId
} = require('./pagination')
const app = express()
const authors = [
{ id: 1, name: "Author 1"},
{ id: 2, name: "Author 2"},
{ id: 3, name: "Author 3"}
]
const books = [
{ id: 1, title: "Book 1", authorId: 1 },
{ id: 2, title: "Book 2", authorId: 1 },
{ id: 3, title: "Book 3", authorId: 1 },
{ id: 4, title: "Book 4", authorId: 2 },
{ id: 5, title: "Book 5", authorId: 2 },
{ id: 6, title: "Book 6", authorId: 2 },
{ id: 7, title: "Book 7", authorId: 3 },
{ id: 8, title: "Book 8", authorId: 3 },
{ id: 9, title: "Book 9", authorId: 3 }
]
const Book = new GraphQLObjectType({
name: 'Book',
description: 'this is a book',
fields: () => ({
id: { type: GraphQLNonNull(GraphQLInt) },
title: { type: GraphQLNonNull(GraphQLString) },
authorId: { type: GraphQLNonNull(GraphQLInt) },
author: {
type: Author,
resolve: ({authorId}) => {
return authors.find(author => author.id === authorId)
}
}
})
})
const Author = new GraphQLObjectType({
name: 'Author',
description: 'this represents the author of a book',
fields: () => ({
id: { type: GraphQLNonNull(GraphQLInt) },
name: { type: GraphQLNonNull(GraphQLString) },
books: {
type: GraphQLList(Book),
resolve: ({id}) => {
return books.filter(book => book.authorId === id)
}
}
})
})
const RootQuery = new GraphQLObjectType({
name: 'RootQueryType',
description: 'this is the root query',
fields: () => ({
book: {
type: Book,
description: 'a single book',
args: {
id: { type: GraphQLInt }
},
resolve: (_, { id }) => {
return books.find(book => book.id === id)
}
},
author: {
type: Author,
description: 'a single author',
args: {
id: { type: GraphQLInt },
},
resolve: (_, { id }) => {
return authors.find(author => author.id === id)
}
},
books: {
type: PageType(Book),
description: 'a list of books',
args: {
first: { type: GraphQLInt },
afterCursor: { type: GraphQLString }
},
resolve: (_, { first, afterCursor }) => {
let afterIndex = 0
if (typeof afterCursor === 'string') {
let nodeId = convertCursorToNodeId(afterCursor)
let nodeIndex = books.findIndex(book => book.id === nodeId)
if (nodeIndex >= 0) {
afterIndex = nodeIndex + 1
}
}
const slicedData = books.slice(afterIndex, afterIndex + first)
console.log('sliced data: ', slicedData)
const edges = slicedData.map(node => ({
node,
cursor: convertNodeToCursor(node)
}))
let startCursor = null
let endCursor = null
if (edges.length > 0) {
startCursor = convertNodeToCursor(edges[0].node)
endCursor = convertNodeToCursor(edges[edges.length - 1].node)
}
let hasNextPage = books.length > afterIndex + first
return {
totalCount: books.length,
edges,
pageInfo: {
startCursor,
endCursor,
hasNextPage
}
}
}
}
})
})
const schema = new GraphQLSchema({
query: RootQuery
})
app.use('/graphql', graphqlHTTP({
schema,
graphiql: true
}))
app.listen(3000, () => console.log('app running at http://localhost:3000/graphql'))
and I handle the pagination in another file here:
const {
GraphQLString,
GraphQLInt,
GraphQLBoolean,
GraphQLObjectType,
GraphQLList,
} = require('graphql')
const Edge = (itemType) => {
return new GraphQLObjectType({
name: 'EdgeType',
fields: () => ({
node: { type: itemType },
cursor: { type: GraphQLString }
})
})
}
const PageInfo = new GraphQLObjectType({
name: 'PageInfoType',
fields: () => ({
startCursor: { type: GraphQLString },
endCursor: { type: GraphQLString },
hasNextPage: { type: GraphQLBoolean }
})
})
const PageType = (itemType) => {
return new GraphQLObjectType({
name: 'PageType',
fields: () => ({
totalCount: { type: GraphQLInt },
edges: { type: new GraphQLList(Edge(itemType)) },
pageInfo: { type: PageInfo }
})
})
}
const convertNodeToCursor = (node) => {
// Encoding the cursor value to Base 64 as suggested in GraphQL documentation
return Buffer.from((node.id).toString()).toString('base64')
}
const convertCursorToNodeId = (cursor) => {
// Decoding the cursor value from Base 64 to integer
return parseInt(Buffer.from(cursor, 'base64').toString('ascii'))
}
module.exports = {
PageType,
convertNodeToCursor,
convertCursorToNodeId
}
Now if I copy and paste the books endpoint and change it to authors, and change the type to PageType(Author) then I get another error:
Schema must contain uniquely named types but contains multiple types named "PageType".
So this clearly isn't a solution either
You cannot have one EdgeType that contains Authors and another EdgeType that contains Books. Instead, you will need one AuthorEdge and one BookEdge type.
The same holds for the PageType - there can't be two different types with different fields but the same name.
The solution is relatively simple though - if you dynamically generated these types in a function, also name them dynamically:
const Edge = (itemType) => {
return new GraphQLObjectType({
name: itemType.name + 'Edge',
// ^^^^^^^^^^^^^^^^^^^^^^
fields: () => ({
node: { type: itemType },
cursor: { type: GraphQLString }
})
})
}
const PageInfo = new GraphQLObjectType({
name: 'PageInfo',
fields: () => ({
startCursor: { type: GraphQLString },
endCursor: { type: GraphQLString },
hasNextPage: { type: GraphQLBoolean }
})
})
const PageType = (itemType) => {
return new GraphQLObjectType({
name: itemType.name + 'sPage',
// ^^^^^^^^^^^^^^^^^^^^^^^
fields: () => ({
totalCount: { type: GraphQLInt },
edges: { type: new GraphQLList(Edge(itemType)) },
pageInfo: { type: PageInfo }
})
})
}
I want to derive a pattern from an array. The Array could be n number of elements
This is the Array pattern I receive from DB, (Note here elements could be n numbers)
[
{ id: '2', name: 'ONe' },
{ id: '3', name: 'Twop' },
{ id: '1', name: 'ThreeC' }
]
And I want to for a patter like AccountId=2&AccountId=3&AccountId=1 formed from the array and id in it
And I want to pass that formed data into the below URL as a query parameter to make an API call.
const config = {
method: 'get',
url: `${URL}api/cost?AccountId=1&AccountId=2&AccountId=3`,
headers: {
'Cookie': 'ARRAffinity=6f6eb54d3b6d7ed13173b9203b0bd6571b611d626818fba77a815805a7c90146'
},
data: data
};
const dataOutput = await axios(config )
.then(function (response) {
console.log(JSON.stringify(response.data));
return response.data;
})
.catch(function (error) {
console.log(error);
});
You can do it like this:
stackOverflow = () => {
let requestPartStr = '';
const data = [
{ id: '2', name: 'ONe' },
{ id: '3', name: 'Twop' },
{ id: '1', name: 'ThreeC' }
];
data.forEach((val, index) => {
requestPartStr += index !== data.length - 1
? `AccountId=${val.id}&`
: `AccountId=${val.id}`;
})
return requestPartStr;
};
Based on given code this can be a full code example:
const data = [
{ id: '2', name: 'ONe' },
{ id: '3', name: 'Twop' },
{ id: '1', name: 'ThreeC' }
];
getRequestParameters = (data) => {
let requestPartStr = '';
data.forEach((val, index) => {
requestPartStr += index !== data.length - 1
? `AccountId=${val.id}&`
: `AccountId=${val.id}`;
})
return requestPartStr;
};
const requestParameters = getRequestParameters(data);
const config = {
method: 'get',
url: `${URL}api/cost?${requestParameters}`,
headers: {
'Cookie': 'ARRAffinity=6f6eb54d3b6d7ed13173b9203b0bd6571b611d626818fba77a815805a7c90146'
},
data: data
};
const dataOutput = await axios(config)
.then(function (response) {
console.log(JSON.stringify(response.data));
return response.data;
})
.catch(function (error) {
console.log(error);
});
Use map and join to build a params string
const data = [
{ id: "2", name: "ONe" },
{ id: "3", name: "Twop" },
{ id: "1", name: "ThreeC" },
];
const params = data.map(({ id }) => `AccountId=${id}`).join("&");
const url = `foo.com/api/cost?${params}`;
console.log(url);
I have a set of data to validate:
{
"createUser": {
"isCustomer": true or false,
"data": {
"product": [ // sometimes array of object with id : 3
{
"id":46,
"sumInsured":"4562000",
},
{
"id":45,
"sumInsured":"8532000",
},
]
}
}
These are the following scenarios we need to validate:
1) validate array of objects
2) isCustomer is mandatory when id is 45
3) isCustomer not allowed when id is 3
First is done:
Joi.object({
data: Joi.array()
.items({
id: Joi.number().valid(3,45,46)
.required(),
sumInsured: Joi.string()
.required()
}),
})
I searched a lot regarding the same, but still not able to find a solution for the same.
Any help would be really appreciated.
Thanks in advance.
This a combined Schema for both points 2 & 3.
You will need to use the method .when - which will define your first
if condition. From there, you will have to include another .when to add your second if condition
Such that
.when("data", {
is: <first if condition>,
then: <first if condition do something>
otherwise: .when("data",{
is: <else if>
then: <else if do something>
})
})
To understand the above logically,
it would result in the following
Joi.any().when("data", {
is: <is id 45?>,
then: <is required>
otherwise: Joi.any().when("data",{
is: <is id 3?>
then: <is forbidden>
})
})
Test cases
const test_id_3_ok = {
createUser: {
data: {
product: [
{
id: 3,
},
],
},
},
};
const test_id_46_ok = {
createUser: {
data: {
product: [
{
id: 46,
},
],
},
},
};
const test_id_46_has_customer_ok = {
createUser: {
isCustomer: true,
data: {
product: [
{
id: 46,
},
],
},
},
};
const test_id_46_no_customer_ok = {
createUser: {
data: {
product: [
{
id: 46,
},
],
},
},
};
const test_id_3_has_customer_should_error = {
isCustomer: true,
createUser: {
data: {
product: [
{
id: 3,
},
],
},
},
};
const test_id_45_ok = {
createUser: {
isCustomer: true,
data: {
product: [
{
id: 45,
},
],
},
},
};
const test_id_45_no_customer_should_error = {
createUser: {
data: {
product: [
{
id: 45,
},
],
},
},
};
Schema
const dataSchema = Joi.object({
product: Joi.array().items(
Joi.object({
id: Joi.number().valid(3, 45, 46),
})
),
});
const mandatory = (value) =>
Joi.object({
product: Joi.array().items(
Joi.object({
id: Joi.number().valid(value),
})
),
});
const schema = Joi.object({
createUser: Joi.object({
isCustomer: Joi.any().when("data", {
is: mandatory(3),
then: Joi.forbidden(),
otherwise: Joi.any().when("data", {
is: mandatory(45),
then: Joi.bool().required(),
}),
}),
data: dataSchema,
}),
});
schema.validate(test_id_3_ok) //?
schema.validate(test_id_3_has_customer_should_error); //?
schema.validate(test_id_45_ok); //?
schema.validate(test_id_45_no_customer_should_error); //?
schema.validate(test_id_46_ok); //?
schema.validate(test_id_46_has_customer_ok); //?
schema.validate(test_id_46_no_customer_ok); //?
I always got "grant_type is required error invalid_request" with the following JS script and I have not idea why:
let tokenUrl = 'myTokenURL';
let clientId = 'myClientID';
let clientSecret = 'myClientSecret';
let scope = 'myScope';
let getTokenRequest = {
method: 'POST',
url: tokenUrl,
auth: {
type: "basic",
basic: [
{ key: "username", value: clientId },
{ key: "password", value: clientSecret }
]
},
header: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: {
mode: 'formdata',
formdata: [
{ key: "grant_type", value: "authorization_code" },
{ key: 'scope', value: scope },
{ key: 'redirect_uri', value: 'MyRedirectURI' },
{ key: 'client_id', value: clientId},
{ key: 'clientSecret', value: clientSecret},
]
}
};
pm.sendRequest(getTokenRequest, (err, response) => {
let jsonResponse = jsonResponse = response.json();;
let newAccessToken = jsonResponse.access_token;
console.log({ err, jsonResponse, newAccessToken })
});
this is using Postman sendRequest function
For those all the people interested in the solution for this, please see the following code which practically it is the Danny Daiton's answer in the above comments
let tokenUrl = 'myTokenURL';
let clientId = 'myClientID';
let clientSecret = 'myClientSecret';
let scope = 'myScope';
let getTokenRequest = {
method: 'POST',
url: tokenUrl,
auth: {
type: "basic",
basic: [
{ key: "username", value: clientId },
{ key: "password", value: clientSecret }
]
},
header: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: {
mode: 'urlencoded',
urlencoded: [
{ key: "grant_type", value: "authorization_code" },
{ key: 'scope', value: scope },
{ key: 'redirect_uri', value: 'MyRedirectURI' },
{ key: 'client_id', value: clientId},
{ key: 'clientSecret', value: clientSecret},
]
}
};
pm.sendRequest(getTokenRequest, (err, response) => {
let jsonResponse = jsonResponse = response.json();;
let newAccessToken = jsonResponse.access_token;
console.log({ err, jsonResponse, newAccessToken })
});