genezio
Version:
Command line utility to interact with Genezio infrastructure.
213 lines (212 loc) • 8.9 kB
JavaScript
import { log } from "../utils/logging.js";
import { exit } from "process";
import { languages } from "../utils/languages.js";
import { Language, TriggerType } from "../projectConfiguration/yaml/models.js";
import { getProjectEnvFromProject } from "../requests/getProjectInfo.js";
import listProjects from "../requests/listProjects.js";
import { scanClassesForDecorators } from "../utils/configuration.js";
import { GenezioTelemetry, TelemetryEventTypes } from "../telemetry/telemetry.js";
import path from "path";
import { mapDbAstToSdkGeneratorAst } from "../generateSdk/utils/mapDbAstToFullAst.js";
import { generateSdk } from "../generateSdk/sdkGeneratorHandler.js";
import inquirer from "inquirer";
import { SourceType } from "../models/commandOptions.js";
import { mapYamlClassToSdkClassConfiguration, sdkGeneratorApiHandler, } from "../generateSdk/generateSdkApi.js";
import { YamlConfigurationIOController } from "../projectConfiguration/yaml/v2.js";
import { writeSdk } from "../generateSdk/sdkWriter/sdkWriter.js";
import { reportSuccessForSdk } from "../generateSdk/sdkSuccessReport.js";
import { GenezioCommand } from "../utils/reporter.js";
import { UserError } from "../errors.js";
import fs from "fs";
export async function generateSdkCommand(projectName, options) {
switch (options.source) {
case SourceType.LOCAL:
await generateLocalSdkCommand(options);
break;
case SourceType.REMOTE:
await generateRemoteSdkCommand(projectName, options);
break;
}
}
function inferProjectLanguage() {
if (fs.existsSync("package.json")) {
return Language.ts;
}
else if (fs.existsSync("go.mod")) {
return Language.go;
}
return Language.ts;
}
export async function generateLocalSdkCommand(options) {
const url = options.url;
if (!url) {
throw new UserError("You must provide a url when generating a local SDK.");
}
const sdk = await sdkGeneratorApiHandler([options.language], mapYamlClassToSdkClassConfiguration(await scanClassesForDecorators({
path: process.cwd(),
classes: [],
language: {
name: inferProjectLanguage(),
},
}), options.language, process.cwd()), options.output, options.packageName).catch((error) => {
// TODO: this is not very generic error handling. The SDK should throw Genezio errors, not babel.
if (error.code === "BABEL_PARSER_SYNTAX_ERROR") {
log.error("Syntax error:");
log.error(`Reason Code: ${error.reasonCode}`);
log.error(`File: ${error.path}:${error.loc.line}:${error.loc.column}`);
throw error;
}
throw error;
});
const sdkResponse = sdk.generatorResponses[0];
const classUrls = sdkResponse.files.map((c) => ({
name: c.className,
cloudUrl: url,
}));
await writeSdk({
language: options.language,
packageName: options.packageName,
packageVersion: options.packageVersion,
sdkResponse,
classUrls,
publish: false,
installPackage: false,
outputPath: options.output,
exportAsTarball: options.tarball,
});
reportSuccessForSdk(options.language, sdkResponse, GenezioCommand.sdk);
}
export async function generateRemoteSdkCommand(projectName, options) {
await GenezioTelemetry.sendEvent({
eventType: TelemetryEventTypes.GENEZIO_GENERATE_SDK,
commandOptions: JSON.stringify(options),
});
const language = options.language;
const sdkPath = options.output;
const stage = options.stage;
const region = options.region;
// check if language is supported using languages array
if (!languages.includes(language)) {
throw new UserError(`The language you specified is not supported. Please use one of the following: ${languages}.`);
}
if (projectName) {
await generateRemoteSdkHandler(language, sdkPath, projectName, stage, region, options.packageName, options.packageVersion);
}
else {
let config = options.config;
// check if path ends in .genezio.yaml or else append it
if (!config.endsWith("genezio.yaml")) {
config = path.join(config, "genezio.yaml");
}
let configuration;
const yamlIOController = new YamlConfigurationIOController(config, {
stage: options.stage,
});
try {
configuration = await yamlIOController.read();
}
catch (error) {
if (error instanceof Error &&
error.message === "The configuration file does not exist.") {
const answers = await inquirer.prompt([
{
type: "confirm",
name: "createConfig",
message: "Oops! The configuration file does not exist at the specified path. Do you want us to create one for you?",
},
]);
if (answers["createConfig"]) {
const projects = await listProjects();
const options = [
...new Set(projects.map((p) => ({
name: p.name,
region: p.region,
}))),
].map((p) => ({
name: `${p.name} (${p.region})`,
value: p,
}));
const answers = await inquirer.prompt([
{
type: "list",
name: "project",
message: "Select the project you want to generate the SDK for:",
choices: options,
},
]);
const project = answers["project"];
configuration = {
name: project.name,
region: project.region,
yamlVersion: 2,
};
yamlIOController.write(configuration);
}
else {
exit(1);
}
}
else {
throw error;
}
}
const name = configuration.name;
const configurationRegion = configuration.region;
await generateRemoteSdkHandler(language, sdkPath, name, stage, configurationRegion, options.packageName, options.packageVersion);
}
}
async function generateRemoteSdkHandler(language, sdkPath, projectName, stage, region, packageName, packageVersion) {
// get all project classes
const projects = await listProjects();
// check if the project exists with the configuration project name, region
const project = projects.find((project) => project.name === projectName && project.region === region);
if (!project) {
throw new UserError(`The project ${projectName} on region ${region} doesn't exist. You must deploy it first with 'genezio deploy'.`);
}
// get project info
const projectEnv = await getProjectEnvFromProject(project.id, stage);
// if the project doesn't exist, throw an error
if (!projectEnv) {
throw new UserError(`The project ${projectName} on stage ${stage} doesn't exist in the region ${region}. You must deploy it first with 'genezio deploy'.`);
}
const sdkGeneratorInput = {
classesInfo: projectEnv.classes.map((c) => ({
program: mapDbAstToSdkGeneratorAst(c.ast),
classConfiguration: {
path: c.ast.path,
type: TriggerType.jsonrpc,
methods: [],
language: path.extname(c.ast.path),
name: c.name,
},
fileName: path.basename(c.ast.path),
})),
language: language,
packageName: `@genezio-sdk/${projectName}`,
};
const sdkGeneratorOutput = await generateSdk(sdkGeneratorInput, undefined);
const sdkGeneratorResponse = {
files: sdkGeneratorOutput.files,
sdkGeneratorInput: sdkGeneratorInput,
};
// replace the placeholder urls in the sdk with the actual cloud urls
const classUrls = [];
// populate a map of class name and cloud url
projectEnv.classes.forEach((classInfo) => {
classUrls.push({
name: classInfo.name,
cloudUrl: classInfo.cloudUrl,
});
});
await writeSdk({
language,
packageName: packageName || `@genezio-sdk/${projectName}`,
packageVersion: packageVersion || `1.0.0-${stage}`,
sdkResponse: sdkGeneratorResponse,
classUrls,
publish: false,
installPackage: false,
outputPath: sdkPath,
});
reportSuccessForSdk(language, sdkGeneratorResponse, GenezioCommand.sdk);
}