How to get GraphQL document validation error before launching a server? - validation

I have some schema with simple misuse cases like on the picture bellow.
I was able to get them only via this stupid idea running apollo-server, as this was the first time when I saw them, so it would perform validation on create.
const { ApolloServer } = require("apollo-server-express");
const { readFileSync } = require("fs");
const typeDefs = readFileSync(process.argv[2]).toString("utf-8");
try {
new ApolloServer({
typeDefs: typeDefs,
debug: true,
mockEntireSchema: true,
});
} catch (error) {
console.error(error.message);
process.exit(-1);
}
I wasn't really able to get any validation cli tool which can accept only SDL definition and do this simple syntax checks. Are there any? I'm just trying to build build pipeline and injecting as many as I can into compile time, rather than runtime. I mean, hack is working, but this is so weird that there is no ready solutions for that.
I also using graphql-codegen for typescript, but it just doesn't care and no errors are thrown. The tool is okay with interfaces being used in unions.
graphql-cli with it's validate requires document and schema, but it's doing a bit different kind of validation.

Related

Load/stress test in a SPA with Hasura Cloud Graphql as a backend and subscriptions

I'm trying to do a performance test on a
SPA with a Frontend in React, deployed with Netlify
As a backend we're using Hasura Cloud Graphql (std version) https://hasura.io/, where everything from the client goes directly through Hasura to the DB.
DB is in Postgress housed in Heroku (Std 0 tier).
We're hoping to be able to have around 800 users simultaneous.
The problem is that i'm loss about how to do it or if i'm doing it correctly, seeing how most of our stuff are "subscriptions/mutations" that I had to transform into queries. I tried doing those test with k6 and Jmeter but i'm not sure if i'm doing them properly.
k6 test
At first, i did a quick search and collected around 10 subscriptions that are commonly used. Then i tried to create a performance test with k6 https://k6.io/docs/using-k6/http-requests/ but i wasn't able to create a working subscription test so i just transform each subscription into a query and perform a http.post with this setup:
export const options = {
stages: [
{ duration: '30s', target: 75 },
{ duration: '120s', target: 75 },
{ duration: '60s', target: 50 },
{ duration: '30s', target: 30 },
{ duration: '10s', target: 0 }
]
};
export default function () {
var res = http.post(prod,
JSON.stringify({
query: listaQueries.GetDesafiosCursosByKey(
keys.desafioCursoKey
)}), params);
sleep(1)
}
I did this for every query and ran each test individually. Unfortunately, the numbers i got were bad, and somehow our test environment was getting better times than production. (The only difference afaik is that we're using Hasura Cloud for production).
I tried to implement websocket, but i couldn't getthem work and configure them to do a stress/load test.
K6 result
Jmeter test
After that, i tried something similar with Jmeter, but again i couldn't figure how to set up a subscription test (after i while, i read in a blog that jmeter doesn't support it
https://qainsights.com/deep-dive-into-graphql-in-jmeter/ ) so i simply transformed all subscriptions into a query and tried to do the same, but the numbers I was getting were different and much higher than k6.
Jmeter query Config 1
Jmeter query config 2
Jmeter thread config
Questions
I'm not sure if i'm doing it correctly, if transforming every subscription into a query and perform a http request is a correct approach for it. (At least I know that those queries return the data correctly).
Should i just increase the number of VUS/threads until i get a constant timeout to simulate a stress test? There were some test that are causing a graphql error on the website Graphql error, and others were having a
""WARN[0059] Request Failed error="Post \"https://xxxxxxx-xxxxx.herokuapp.com/v1/graphql\": EOF""
in the k6 console.
Or should i just give up with k6/jmeter and try to search for another tool to perfom those test?
Thanks you in advance, and sorry for my English and explanation, but i'm a complete newbie at this.
I'm not sure if i'm doing it correctly, if transforming every
subscription into a query and perform a http request is a correct
approach for it. (At least I know that those queries return the data
correctly).
Ideally you would be using WebSocket as that is what actual clients will most likely be using.
For code samples, check out the answer here.
Here's a more complete example utilizing a main.js entry script with modularized Subscription code in subscriptions\bikes.brands.js. It also uses the Httpx library to set a global request header:
// main.js
import { Httpx } from 'https://jslib.k6.io/httpx/0.0.5/index.js';
import { getBikeBrandsByIdSub } from './subscriptions/bikes-brands.js';
const session = new Httpx({
baseURL: `http://54.227.75.222:8080`
});
const wsUri = 'wss://54.227.75.222:8080/v1/graphql';
const pauseMin = 2;
const pauseMax = 6;
export const options = {};
export default function () {
session.addHeader('Content-Type', 'application/json');
getBikeBrandsByIdSub(1);
}
// subscriptions/bikes-brands.js
import ws from 'k6/ws';
/* using string concatenation */
export function getBikeBrandsByIdSub(id) {
const query = `
subscription getBikeBrandsByIdSub {
bikes_brands(where: {id: {_eq: ${id}}}) {
id
brand
notes
updated_at
created_at
}
}
`;
const subscribePayload = {
id: "1",
payload: {
extensions: {},
operationName: "query",
query: query,
variables: {},
},
type: "start",
}
const initPayload = {
payload: {
headers: {
"content-type": "application/json",
},
lazy: true,
},
type: "connection_init",
};
console.debug(JSON.stringify(subscribePayload));
// start a WS connection
const res = ws.connect(wsUri, initPayload, function(socket) {
socket.on('open', function() {
console.debug('WS connection established!');
// send the connection_init:
socket.send(JSON.stringify(initPayload));
// send the chat subscription:
socket.send(JSON.stringify(subscribePayload));
});
socket.on('message', function(message) {
let messageObj;
try {
messageObj = JSON.parse(message);
}
catch (err) {
console.warn('Unable to parse WS message as JSON: ' + message);
}
if (messageObj.type === 'data') {
console.log(`${messageObj.type} message received by VU ${__VU}: ${Object.keys(messageObj.payload.data)[0]}`);
}
console.log(`WS message received by VU ${__VU}:\n` + message);
});
});
}
Should i just increase the number of VUS/threads until i get a
constant timeout to simulate a stress test?
Timeouts and errors that only happen under load are signals that you may be hitting a bottleneck somewhere. Do you only see the EOFs under load? These are basically the server sending back incomplete responses/closing connections early which shouldn't happen under normal circumstances.
My expectation is that your test should be replicating the real user activity as close as possible. I doubt that real users will be sending requests to GraphQL directly and well-behaved load test must replicate the real life application usage as close as possible.
So I believe you should move to HTTP protocol level and mimic the network footprint of the real browser instead of trying to come up with individual GraphQL queries.
With regards to JMeter and k6 differences it might be the case that k6 produces higher throughput given the same hardware and running requests at maximum speed as it evidenced by kind of benchmark in the Open Source Load Testing Tools 2021 article, however given you're trying to simulate real users using real browsers accessing your applications and the real users don't hammer the application non-stop, they need some time to "think" between operations you should be getting the same number of requests for both load testing tools, if JMeter doesn't give you the load you want to conduct make sure to follow JMeter Best Practices and/or consider running it in distributed mode .

How to use custom directives graphql-modules with apollo

I need help with custom directives when using graphql-modules library. Have no idea where to place my custom directives so it is combined with overall schema
I would like to post answer from Discord community, from user Maapteh.
here it the quote from him
in our app with old version we had everything in common module. We
kept that approach partly when using the latest version of modules.
See some parts of our codebase:
import { ApolloServer, SchemaDirectiveVisitor } from
'apollo-server-express';
const schema = AppModule.createSchemaForApollo();
SchemaDirectiveVisitor.visitSchemaDirectives(schema, {
isMember: IsMemberDirective,
deprecated: isDeprecated,
...SNIP... });
as you can see we create the schema we pass eventually to the apollo
server (example using apollo). We add our generic directives like so.
The schema for them is in our common module. Read further...
For common scalars we added a common module. With their schema (so in
schema directory we also have the directives schemas) and their
resolver.
const typeDefsArray = loadFilesSync(${__dirname}/schema/*.graphql, {
useRequire: true, }); const typeDefs = mergeTypeDefs(typeDefsArray, { useSchemaDefinition: false });
const resolverFunctions = {
ImageUrl: ImageUrlType,
PageSize: PageSizeType,
Date: DateResolver,
...SNIP... };
export const CommonModule = createModule({
id: 'common',
typeDefs: typeDefs,
resolvers: resolverFunctions, });
hope it helps you
https://discord.com/channels/625400653321076807/631489837416841253/832595211166548049

GraphQL Nexus Schema (nexusjs) doesn't compile with scalar types

I am trying to follow the documentation on the Nexus-Schema (nexusjs) website for adding scalar types to my GraphQL application.
I have tried adding many of the different implementations to my src/types/Types.ts file using the samples provided in the documentation and the interactive examples. My attempts include:
Without a 3rd party libraries:
const DateScalar = scalarType({
name: 'Date',
asNexusMethod: 'date',
description: 'Date custom scalar type',
parseValue(value) {
return new Date(value)
},
serialize(value) {
return value.getTime()
},
parseLiteral(ast) {
if (ast.kind === Kind.INT) {
return new Date(ast.value)
}
return null
},
})
With graphql-iso-date 3rd party library:
import { GraphQLDate } from 'graphql-iso-date'
export const DateTime = GraphQLDate
With graphql-scalars 3rd party library (as shown in the ghost example):
export const GQLDate = decorateType(GraphQLDate, {
rootTyping: 'Date',
asNexusMethod: 'date',
})
I am using this new scalar type in an object definition like the following:
const SomeObject = objectType({
name: 'SomeObject',
definition(t) {
t.date('createdAt') // t.date() is supposed to be available because of `asNexusMethod`
},
})
In all cases, these types are exported from the types file and imported into the makeSchema's types property.
import * as types from './types/Types'
console.log("Found types", types)
export const apollo = new ApolloServer({
schema: makeSchema({
types,
...
context:()=>(
...
})
})
The console.log statement above does show that consts declared in the types file are in scope:
Found types {
GQLDate: Date,
...
}
If I run the app in development mode, everything boots up and runs fine.
ts-node-dev --transpile-only ./src/app.ts
However, I encounter errors whenever I try to compile the app to deploy to a server
ts-node ./src/app.ts && tsc
Note: This error occurs occurs running just ts-node ./src/app.ts before it gets to tsc
The errors that shown during the build process are the following:
/Users/user/checkouts/project/node_modules/ts-node/src/index.ts:500
return new TSError(diagnosticText, diagnosticCodes)
^
TSError: тип Unable to compile TypeScript:
src/types/SomeObject.ts:11:7 - error TS2339: Property 'date' does not exist on type 'ObjectDefinitionBlock<"SomeObject">'.
11 t.date('createdAt')
Does anyone have any ideas on either:
a) How can I work around this error? While long-term solutions are ideal, temporary solutions would also be appreciated.
b) Any steps I could follow to debug this error? Or ideas on how get additional information to assist with debugging?
Any assistance would be very much welcomed. Thanks!
The issue seems to be resolved when --transpile-only flag is added to the nexus:reflect command.
This means the reflection command gets updated to:
ts-node --transpile-only ./src/app.ts
and the build comand gets updated to:
env-cmd -f ./config/.env ts-node --transpile-only ./src/app.ts --nexusTypegen && tsc
A github issue has also been created which can be reviewed here: https://github.com/graphql-nexus/schema/issues/690

Apollo-server-express introspection disabled but still possible over websocket connections

We use apollo-server-express to expose a graphql server.
For this server, we have set the introspection variable to false to hide our schema from the outer world which works fine for Graphql calls that go over rest calls.
However, when we set up a websocket connection with this same server, we manage to execute introspection queries, even though that during the instantiation of the apollo server, the introspection is explicitly set to false
the config for booting the Apollo-server looks something like this:
{
schema: <schema>,
context: <context_function>,
formatError: <format_error_function>,
debug: false,
tracing: false,
subscriptions: {
path: <graphQl_path>,
keepAlive: <keep_alive_param>,
onConnect: <connect_function>,
onDisconnect: <disconnect_function>
},
introspection: false,
playground: false
};
Did someone had a similar issue? And if yes, were you able to solve it and how?
apollo-server-express version = 2.1.0
npm version = 6.4.1
node version = 10.13.0
What ApolloServer does internally is prevent you from using the __schema and __type resolvers. I assume you could do the same thing:
export const resolvers = {
Query: {
__type() {
throw new Error('You cannot make introspection');
},
__schema() {
throw new Error('You cannot make introspection');
}
}
}

How to capture the transactions while doing testing using Mocha

I am in the process of writing unit/behavioural tests using Mocha for a particular blockchain network use-case. Based on what I can see, these tests are not hitting the actual fabric, in other words, they seem to be running in some kind of a simulated environment. I don't get to see any of the transactions that took place as a part of the test. Can someone please tell me if it is somehow possible to capture the transactions that take place as part of the Mocha tests?
Initial portion of my code below:
describe('A Network', () => {
// In-memory card store for testing so cards are not persisted to the file system
const cardStore = require('composer-common').NetworkCardStoreManager.getCardStore( { type: 'composer-wallet-inmemory' } );
let adminConnection;
let businessNetworkConnection;
let businessNetworkDefinition;
let businessNetworkName;
let factory;
//let clock;
// Embedded connection used for local testing
const connectionProfile = {
name: 'hlfv1',
'x-type': 'hlfv1',
'version': '1.0.0'
};
before(async () => {
// Generate certificates for use with the embedded connection
const credentials = CertificateUtil.generate({ commonName: 'admin' });
// PeerAdmin identity used with the admin connection to deploy business networks
const deployerMetadata = {
version: 1,
userName: 'PeerAdmin',
roles: [ 'PeerAdmin', 'ChannelAdmin' ]
};
const deployerCard = new IdCard(deployerMetadata, connectionProfile);
console.log("line 63")
const deployerCardName = 'PeerAdmin';
deployerCard.setCredentials(credentials);
console.log("line 65")
// setup admin connection
adminConnection = new AdminConnection({ cardStore: cardStore });
console.log("line 69")
await adminConnection.importCard(deployerCardName, deployerCard);
console.log("line 70")
await adminConnection.connect(deployerCardName);
console.log("line 71")
});
Earlier, my connection profile was using the embedded mode, which I changed to hlfv1 after looking at the answer below. Now, I am getting the error: Error: the string "Failed to import identity. Error: Client.createUser parameter 'opts mspid' is required." was thrown, throw an Error :). This is coming from
await adminConnection.importCard(deployerCardName, deployerCard);. Can someone please tell me what needs to be changed. Any documentation/resource will be helpful.
Yes you can use a real Fabric. Which means you could interact with the created transactions using your test framework or indeed other means such as REST or Playground etc.
In Composer's own test setup, the option for testing against an hlfv1 Fabric environment is used in its setup (ie whether you want to use embedded, web or real Fabric) -> see https://github.com/hyperledger/composer/blob/master/packages/composer-tests-functional/systest/historian.js#L120
Setup is captured here
https://github.com/hyperledger/composer/blob/master/packages/composer-tests-functional/systest/testutil.js#L192
Example of setting up artifacts that you would need to setup to use a real Fabric here
https://github.com/hyperledger/composer/blob/master/packages/composer-tests-functional/systest/testutil.js#L247
Also see this blog for more guidelines -> https://medium.com/#mrsimonstone/debug-your-blockchain-business-network-using-hyperledger-composer-9bea20b49a74

Resources