Skip to content

Commit

Permalink
added gen-graphql-schema command
Browse files Browse the repository at this point in the history
  • Loading branch information
yaswanth-godspeed committed Dec 6, 2023
1 parent bd5f8e8 commit 34af2f7
Show file tree
Hide file tree
Showing 2 changed files with 162 additions and 9 deletions.
13 changes: 13 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,12 @@ import devOpsPluginCommands from "./commands/devops-plugin";
import pluginCommands from "./commands/plugin";
import prismaCommands from "./commands/prisma";
import otelCommands from "./commands/otel";
import {genGraphqlSchema} from "./utils/index";
const fsExtras = require("fs-extra");
import { cwd } from "process";
import { readFileSync } from "fs";
import { globSync } from "glob";
import inquirer from "inquirer";

// load .env
dotenv.config({ path: path.resolve(__dirname, "../.env") });
Expand Down Expand Up @@ -193,6 +196,16 @@ export const isAGodspeedProject = () => {
spawnSync("npm", ["run", "gen-crud-api"], { stdio: "inherit" });
}
});
program
.command("gen-graphql-schema")
.description(
"scans your graphql events and generate graphql schema"
)
.action(async () => {
if (isAGodspeedProject()) {
await genGraphqlSchema()
}
});
program
.command("build")
.description("build the godspeed project. create a production build.")
Expand Down
158 changes: 149 additions & 9 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import { spawnSync } from "child_process";
import crossSpawn from "cross-spawn";
import spawnCommand from "cross-spawn";
import ora from "ora";
import loadYaml from '@godspeedsystems/core/dist/core/yamlLoader';
const { exec } = require('child_process');

const userID = (): string => {
if (process.platform == "linux") {
Expand Down Expand Up @@ -160,7 +162,7 @@ export const compileAndCopyOrJustCopy = async (
isUpdateCall = fsExtras
.lstatSync(path.resolve(process.cwd(), ".godspeed"))
.isFile();
} catch (error) {}
} catch (error) { }

fileList.map(async (sourceFilePath: string) => {
if (fsExtras.lstatSync(sourceFilePath).isFile()) {
Expand All @@ -170,17 +172,17 @@ export const compileAndCopyOrJustCopy = async (

relativeDestinationPath = !isUpdateCall
? path.relative(
path.resolve(projectDirPath, sourceFolder),
sourceFilePath
)
: path.resolve(
projectDirPath,
destinationFolder,
path.relative(
path.resolve(projectDirPath, sourceFolder),
sourceFilePath
)
: path.resolve(
projectDirPath,
destinationFolder,
path.relative(
path.resolve(projectDirPath, sourceFolder),
sourceFilePath
)
);
);

let finalDestinationWithFileName = path.resolve(
projectDirPath,
Expand Down Expand Up @@ -440,4 +442,142 @@ export const generateProjectFromDotGodspeed = async (
} catch (error) {
log.fatal("Error while generating files.", error);
}
}

export const genGraphqlSchema = async () => {
try {

const availableApoloeventsources = globSync(
path.join(process.cwd(), 'src/eventsources/*.yaml').replace(/\\/g, '/')
);
// Filter files that contain 'Apollo' in their name
const apolloEventsources = availableApoloeventsources
.map((file) => path.parse(file).name);

const questions = [
{
type: 'checkbox',
name: 'selectedOptions',
message: 'Please select the Graphql Event Sources for which you wish to generate the Graphql schema from Godspeed event defs:',
choices: apolloEventsources,
},
];

async function runPrompt() {
try {
const answers = await inquirer.prompt(questions);
if (answers.selectedOptions.length == 0) {
console.log(chalk.red("Please select atleast one GraphQL eventsource"))
} else {
await createSwaggerFile(answers.selectedOptions)
}
} catch (error) {
console.error(error);
}
}
runPrompt();
} catch (error) {
console.log(error)
};

const createSwaggerFile = async (apolloEventsources: string[]) => {
const eventPath = path.join(process.cwd(), "/src/events");
const eventsSchema: PlainObject = await loadYaml(eventPath, true);
apolloEventsources.map(async (each: string) => {
const apolloEndpoints = Object.fromEntries(
Object.entries(eventsSchema).filter(([key]) => key.split(".")[0] == each)
);
if (Object.keys(apolloEndpoints).length === 0) {
console.log(chalk.red(`there is no events of ${each} eventsource`))
process.exit(1);
}
let swaggerSchema = await generateSwaggerui(apolloEndpoints);
const cwd = process.cwd();
const tempFolderPath = path.join(cwd, '.temp');
// Check if the .temp folder exists, and create it if not
if (!fsExtras.existsSync(tempFolderPath)) {
fsExtras.mkdirSync(tempFolderPath);
}
const swaggerFilePath = path.join(tempFolderPath, `${each}swagger.json`);
// Write the swagger.json file in the .temp folder
await fsExtras.writeFileSync(swaggerFilePath, JSON.stringify(swaggerSchema, null, 2));

const command = `npx swagger-to-graphql --swagger-schema=.temp/${each}swagger.json > ./src/eventsources/${each}.graphql`;
exec(command, (error: any, stdout: any, stderr: any) => {
if (error) {
console.log(
chalk.red.bold(`Failed to generate Graphql schema for eventsource ${each}`)
);
console.log(
chalk.red(error.message)
);
return;
}
if (stderr) {
console.error(`stderr: ${stderr}`);
return;
}
console.log(
chalk.green(`Graphql schema generated successfuly for eventsource ${each} at ./src/eventsources/${each}.graphql`)

);
});
})


}
}

const generateSwaggerui = (eventsSchema: any) => {
let finalSpec: PlainObject = {};
const swaggerCommonPart = {
"openapi": "3.0.0",
"info": {
"version": "0.0.1",
"title": "Godspeed: Sample Microservice",
"description": "Sample API calls demonstrating the functionality of Godspeed framework",
"termsOfService": "http://swagger.io/terms/",
"contact": {
"name": "Mindgrep Technologies Pvt Ltd",
"email": "talktous@mindgrep.com",
"url": "https://docs.mindgrep.com/docs/microservices/intro"
},
"license": {
"name": "Apache 2.0",
"url": "https://www.apache.org/licenses/LICENSE-2.0.html"
}
},
"paths": {}
};
let swaggerSpecBase = JSON.parse(JSON.stringify(swaggerCommonPart));

finalSpec = swaggerSpecBase;

Object.keys(eventsSchema).forEach((event: any) => {
let apiEndPoint = event.split('.')[2];
apiEndPoint = apiEndPoint.replace(/:([^\/]+)/g, '{$1}'); //We take :path_param. OAS3 takes {path_param}
const method = event.split('.')[1];
const eventSchema = eventsSchema[event];

//Initialize the schema for this method, for given event
let methodSpec: PlainObject = {
summary: eventSchema.summary,
description: eventSchema.description,
requestBody: eventSchema.body || eventSchema.data?.schema?.body,
parameters:
eventSchema.parameters ||
eventSchema.params ||
eventSchema.data?.schema?.params,
responses: eventSchema.responses,
};

// Set it in the overall schema
finalSpec.paths[apiEndPoint] = {
...finalSpec.paths[apiEndPoint],
[method]: methodSpec,
};
});

return finalSpec;
};

0 comments on commit 34af2f7

Please sign in to comment.