feat: introduce gql schema sdl generation to the backend (#35)
* feat: introduce gql schema sdl generation to the backend * chore: update gql-codegen consumers to get schema from generated sdl * chore: hoppscotch-backend generates gql sdl on postinstall * fix: add back missed part of generate-gql-sdl script * chore: updated generate sdl script to hardcode whitelisted domains * chore: add prisma generate on postinstall script --------- Co-authored-by: ankitsridhar16 <ankit.sridhar16@gmail.com>
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -171,3 +171,6 @@ tests/*/videos
|
|||||||
|
|
||||||
# PNPM
|
# PNPM
|
||||||
.pnpm-store
|
.pnpm-store
|
||||||
|
|
||||||
|
# GQL SDL generated for the frontends
|
||||||
|
gql-gen/
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
"preinstall": "npx only-allow pnpm",
|
"preinstall": "npx only-allow pnpm",
|
||||||
"prepare": "husky install",
|
"prepare": "husky install",
|
||||||
"dev": "pnpm -r do-dev",
|
"dev": "pnpm -r do-dev",
|
||||||
|
"gen-gql": "cross-env GQL_SCHEMA_EMIT_LOCATION='../../../gql-gen/backend-schema.gql' pnpm -r generate-gql-sdl",
|
||||||
"generate": "pnpm -r do-build-prod",
|
"generate": "pnpm -r do-build-prod",
|
||||||
"start": "http-server packages/hoppscotch-web/dist -p 3000",
|
"start": "http-server packages/hoppscotch-web/dist -p 3000",
|
||||||
"lint": "pnpm -r do-lint",
|
"lint": "pnpm -r do-lint",
|
||||||
@@ -29,6 +30,7 @@
|
|||||||
"@commitlint/cli": "^16.2.3",
|
"@commitlint/cli": "^16.2.3",
|
||||||
"@commitlint/config-conventional": "^16.2.1",
|
"@commitlint/config-conventional": "^16.2.1",
|
||||||
"@types/node": "^17.0.24",
|
"@types/node": "^17.0.24",
|
||||||
|
"cross-env": "^7.0.3",
|
||||||
"http-server": "^14.1.1"
|
"http-server": "^14.1.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
3
packages/hoppscotch-backend/.gitignore
vendored
3
packages/hoppscotch-backend/.gitignore
vendored
@@ -38,3 +38,6 @@ lerna-debug.log*
|
|||||||
!.vscode/tasks.json
|
!.vscode/tasks.json
|
||||||
!.vscode/launch.json
|
!.vscode/launch.json
|
||||||
!.vscode/extensions.json
|
!.vscode/extensions.json
|
||||||
|
|
||||||
|
# Generated artifacts (GQL Schema SDL generation etc.)
|
||||||
|
gen/
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"prebuild": "rimraf dist",
|
"prebuild": "rimraf dist",
|
||||||
"build": "nest build",
|
"build": "nest build",
|
||||||
|
"generate-gql-sdl": "cross-env GQL_SCHEMA_EMIT_LOCATION='../../../gql-gen/backend-schema.gql' GENERATE_GQL_SCHEMA=true WHITELISTED_ORIGINS='' nest start",
|
||||||
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
|
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
|
||||||
"start": "nest start",
|
"start": "nest start",
|
||||||
"start:dev": "nest start --watch",
|
"start:dev": "nest start --watch",
|
||||||
@@ -15,6 +16,7 @@
|
|||||||
"start:prod": "node dist/main",
|
"start:prod": "node dist/main",
|
||||||
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
|
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
|
||||||
"test": "jest",
|
"test": "jest",
|
||||||
|
"postinstall": "prisma generate && pnpm run generate-gql-sdl",
|
||||||
"test:watch": "jest --watch",
|
"test:watch": "jest --watch",
|
||||||
"test:cov": "jest --coverage",
|
"test:cov": "jest --coverage",
|
||||||
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
|
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
|
||||||
@@ -76,6 +78,7 @@
|
|||||||
"@types/supertest": "^2.0.12",
|
"@types/supertest": "^2.0.12",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.45.0",
|
"@typescript-eslint/eslint-plugin": "^5.45.0",
|
||||||
"@typescript-eslint/parser": "^5.45.0",
|
"@typescript-eslint/parser": "^5.45.0",
|
||||||
|
"cross-env": "^7.0.3",
|
||||||
"eslint": "^8.29.0",
|
"eslint": "^8.29.0",
|
||||||
"eslint-config-prettier": "^8.5.0",
|
"eslint-config-prettier": "^8.5.0",
|
||||||
"eslint-plugin-prettier": "^4.2.1",
|
"eslint-plugin-prettier": "^4.2.1",
|
||||||
|
|||||||
95
packages/hoppscotch-backend/src/gql-schema.ts
Normal file
95
packages/hoppscotch-backend/src/gql-schema.ts
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
import { NestFactory } from '@nestjs/core';
|
||||||
|
import {
|
||||||
|
GraphQLSchemaBuilderModule,
|
||||||
|
GraphQLSchemaFactory,
|
||||||
|
} from '@nestjs/graphql';
|
||||||
|
import { printSchema } from 'graphql/utilities';
|
||||||
|
import * as path from 'path';
|
||||||
|
import * as fs from 'fs';
|
||||||
|
import { ShortcodeResolver } from './shortcode/shortcode.resolver';
|
||||||
|
import { TeamCollectionResolver } from './team-collection/team-collection.resolver';
|
||||||
|
import { TeamEnvironmentsResolver } from './team-environments/team-environments.resolver';
|
||||||
|
import { TeamInvitationResolver } from './team-invitation/team-invitation.resolver';
|
||||||
|
import { TeamRequestResolver } from './team-request/team-request.resolver';
|
||||||
|
import { TeamMemberResolver } from './team/team-member.resolver';
|
||||||
|
import { TeamResolver } from './team/team.resolver';
|
||||||
|
import { UserCollectionResolver } from './user-collection/user-collection.resolver';
|
||||||
|
import { UserEnvironmentsResolver } from './user-environment/user-environments.resolver';
|
||||||
|
import { UserHistoryResolver } from './user-history/user-history.resolver';
|
||||||
|
import { UserRequestResolver } from './user-request/resolvers/user-request.resolver';
|
||||||
|
import { UserSettingsResolver } from './user-settings/user-settings.resolver';
|
||||||
|
import { UserResolver } from './user/user.resolver';
|
||||||
|
import { Logger } from '@nestjs/common';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All the resolvers present in the application.
|
||||||
|
*
|
||||||
|
* NOTE: This needs to be KEPT UP-TO-DATE to keep the schema accurate
|
||||||
|
*/
|
||||||
|
const RESOLVERS = [
|
||||||
|
ShortcodeResolver,
|
||||||
|
TeamResolver,
|
||||||
|
TeamMemberResolver,
|
||||||
|
TeamCollectionResolver,
|
||||||
|
TeamEnvironmentsResolver,
|
||||||
|
TeamInvitationResolver,
|
||||||
|
TeamRequestResolver,
|
||||||
|
UserResolver,
|
||||||
|
UserCollectionResolver,
|
||||||
|
UserEnvironmentsResolver,
|
||||||
|
UserHistoryResolver,
|
||||||
|
UserCollectionResolver,
|
||||||
|
UserRequestResolver,
|
||||||
|
UserSettingsResolver,
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All the custom scalars present in the application.
|
||||||
|
*
|
||||||
|
* NOTE: This needs to be KEPT UP-TO-DATE to keep the schema accurate
|
||||||
|
*/
|
||||||
|
const SCALARS = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the GraphQL Schema SDL definition and writes it into the location
|
||||||
|
* specified by the `GQL_SCHEMA_EMIT_LOCATION` environment variable.
|
||||||
|
*/
|
||||||
|
export async function emitGQLSchemaFile() {
|
||||||
|
const logger = new Logger('emitGQLSchemaFile');
|
||||||
|
|
||||||
|
try {
|
||||||
|
const destination = path.resolve(
|
||||||
|
__dirname,
|
||||||
|
process.env.GQL_SCHEMA_EMIT_LOCATION ?? '../gen/schema.gql',
|
||||||
|
);
|
||||||
|
|
||||||
|
logger.log(`GQL_SCHEMA_EMIT_LOCATION: ${destination}`);
|
||||||
|
|
||||||
|
const app = await NestFactory.create(GraphQLSchemaBuilderModule);
|
||||||
|
await app.init();
|
||||||
|
|
||||||
|
const gqlSchemaFactory = app.get(GraphQLSchemaFactory);
|
||||||
|
|
||||||
|
logger.log(
|
||||||
|
`Generating Schema against ${RESOLVERS.length} resolvers and ${SCALARS.length} custom scalars`,
|
||||||
|
);
|
||||||
|
|
||||||
|
const schema = await gqlSchemaFactory.create(RESOLVERS, SCALARS);
|
||||||
|
|
||||||
|
const schemaString = printSchema(schema, {
|
||||||
|
commentDescriptions: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
logger.log(`Writing schema to GQL_SCHEMA_EMIT_LOCATION (${destination})`);
|
||||||
|
|
||||||
|
// Generating folders if required to emit to the given output
|
||||||
|
fs.mkdirSync(path.dirname(destination), { recursive: true });
|
||||||
|
fs.writeFileSync(destination, schemaString);
|
||||||
|
|
||||||
|
logger.log(`Wrote schema to GQL_SCHEMA_EMIT_LOCATION (${destination})`);
|
||||||
|
} catch (e) {
|
||||||
|
logger.error(
|
||||||
|
`Failed writing schema to GQL_SCHEMA_EMIT_LOCATION. Reason: ${e}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ import { json } from 'express';
|
|||||||
import { AppModule } from './app.module';
|
import { AppModule } from './app.module';
|
||||||
import * as cookieParser from 'cookie-parser';
|
import * as cookieParser from 'cookie-parser';
|
||||||
import { VersioningType } from '@nestjs/common';
|
import { VersioningType } from '@nestjs/common';
|
||||||
|
import { emitGQLSchemaFile } from './gql-schema';
|
||||||
|
|
||||||
async function bootstrap() {
|
async function bootstrap() {
|
||||||
console.log(`Running in production: ${process.env.PRODUCTION}`);
|
console.log(`Running in production: ${process.env.PRODUCTION}`);
|
||||||
@@ -38,4 +39,9 @@ async function bootstrap() {
|
|||||||
app.use(cookieParser());
|
app.use(cookieParser());
|
||||||
await app.listen(process.env.PORT || 3170);
|
await app.listen(process.env.PORT || 3170);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!process.env.GENERATE_GQL_SCHEMA) {
|
||||||
bootstrap();
|
bootstrap();
|
||||||
|
} else {
|
||||||
|
emitGQLSchemaFile();
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
overwrite: true
|
overwrite: true
|
||||||
schema:
|
schema: "../../gql-gen/*.gql"
|
||||||
- ${VITE_BACKEND_GQL_URL}
|
|
||||||
generates:
|
generates:
|
||||||
src/helpers/backend/graphql.ts:
|
src/helpers/backend/graphql.ts:
|
||||||
documents: "src/**/*.graphql"
|
documents: "src/**/*.graphql"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
overwrite: true
|
overwrite: true
|
||||||
schema: ${VITE_BACKEND_GQL_URL}
|
schema: "../../gql-gen/*.gql"
|
||||||
generates:
|
generates:
|
||||||
src/helpers/backend/graphql.ts:
|
src/helpers/backend/graphql.ts:
|
||||||
documents: 'src/**/*.graphql'
|
documents: 'src/**/*.graphql'
|
||||||
|
|||||||
6
pnpm-lock.yaml
generated
6
pnpm-lock.yaml
generated
@@ -7,6 +7,7 @@ importers:
|
|||||||
'@commitlint/cli': ^16.2.3
|
'@commitlint/cli': ^16.2.3
|
||||||
'@commitlint/config-conventional': ^16.2.1
|
'@commitlint/config-conventional': ^16.2.1
|
||||||
'@types/node': ^17.0.24
|
'@types/node': ^17.0.24
|
||||||
|
cross-env: ^7.0.3
|
||||||
http-server: ^14.1.1
|
http-server: ^14.1.1
|
||||||
husky: ^7.0.4
|
husky: ^7.0.4
|
||||||
lint-staged: ^12.3.8
|
lint-staged: ^12.3.8
|
||||||
@@ -17,6 +18,7 @@ importers:
|
|||||||
'@commitlint/cli': 16.3.0
|
'@commitlint/cli': 16.3.0
|
||||||
'@commitlint/config-conventional': 16.2.4
|
'@commitlint/config-conventional': 16.2.4
|
||||||
'@types/node': 17.0.45
|
'@types/node': 17.0.45
|
||||||
|
cross-env: 7.0.3
|
||||||
http-server: 14.1.1
|
http-server: 14.1.1
|
||||||
|
|
||||||
packages/codemirror-lang-graphql:
|
packages/codemirror-lang-graphql:
|
||||||
@@ -77,6 +79,7 @@ importers:
|
|||||||
bcrypt: ^5.1.0
|
bcrypt: ^5.1.0
|
||||||
cookie: ^0.5.0
|
cookie: ^0.5.0
|
||||||
cookie-parser: ^1.4.6
|
cookie-parser: ^1.4.6
|
||||||
|
cross-env: ^7.0.3
|
||||||
eslint: ^8.29.0
|
eslint: ^8.29.0
|
||||||
eslint-config-prettier: ^8.5.0
|
eslint-config-prettier: ^8.5.0
|
||||||
eslint-plugin-prettier: ^4.2.1
|
eslint-plugin-prettier: ^4.2.1
|
||||||
@@ -166,6 +169,7 @@ importers:
|
|||||||
'@types/supertest': 2.0.12
|
'@types/supertest': 2.0.12
|
||||||
'@typescript-eslint/eslint-plugin': 5.45.0_yjegg5cyoezm3fzsmuszzhetym
|
'@typescript-eslint/eslint-plugin': 5.45.0_yjegg5cyoezm3fzsmuszzhetym
|
||||||
'@typescript-eslint/parser': 5.45.0_s5ps7njkmjlaqajutnox5ntcla
|
'@typescript-eslint/parser': 5.45.0_s5ps7njkmjlaqajutnox5ntcla
|
||||||
|
cross-env: 7.0.3
|
||||||
eslint: 8.29.0
|
eslint: 8.29.0
|
||||||
eslint-config-prettier: 8.5.0_eslint@8.29.0
|
eslint-config-prettier: 8.5.0_eslint@8.29.0
|
||||||
eslint-plugin-prettier: 4.2.1_eabs6augosioka4cd2cz5tdama
|
eslint-plugin-prettier: 4.2.1_eabs6augosioka4cd2cz5tdama
|
||||||
@@ -9957,7 +9961,7 @@ packages:
|
|||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/concat-map/0.0.1:
|
/concat-map/0.0.1:
|
||||||
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
|
resolution: {integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=}
|
||||||
|
|
||||||
/concat-stream/1.6.2:
|
/concat-stream/1.6.2:
|
||||||
resolution: {integrity: sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==}
|
resolution: {integrity: sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==}
|
||||||
|
|||||||
Reference in New Issue
Block a user