UNPKG

genezio

Version:

Command line utility to interact with Genezio infrastructure.

304 lines (293 loc) 10.4 kB
import Mustache from "mustache"; import { AstNodeType, } from "../../models/genezioModels.js"; import { TriggerType } from "../../projectConfiguration/yaml/models.js"; import { kotlinSdk } from "../templates/kotlinSdk.js"; import { castArrayRecursivelyInitial, castMapRecursivelyInitial, getParamType, } from "../../utils/kotlinAstCasting.js"; // https://kotlinlang.org/docs/keyword-reference.html const KOTLIN_RESERVED_KEYWORDS = [ "as", "is", "as?", "break", "class", "continue", "do", "else", "false", "for", "fun", "if", "in", "!in", "interface", "is", "!is", "null", "object", "package", "return", "super", "this", "throw", "true", "try", "typealias", "typeof", "val", "var", "when", "while", "by", "catch", "constructor", "delegate", "dynamic", "field", "file", "finally", "get", "import", "init", "param", "property", "receiver", "set", "setparam", "value", "where", "abstract", "actual", "annotation", "companion", "const", "crossinline", "data", "enum", "expect", "external", "final", "infix", "inline", "inner", "internal", "lateinit", "noinline", "open", "operator", "out", "override", "private", "protected", "public", "reified", "sealed", "suspend", "tailrec", "vararg", "field", "it", ]; // TODO: Handle "package" in a more robust way, non-hardcoded const template = `// This is an auto generated code. This code should not be modified since the file can be overwritten // if new genezio commands are executed. package com.genezio.sdk import com.google.gson.Gson import com.google.gson.JsonElement import com.google.gson.reflect.TypeToken // User Classes {{#otherClasses}} data class {{name}} ( {{#fields}} var {{fieldName}} : {{{type}}}, {{/fields}} ) { } {{/otherClasses}} class {{{className}}} { private val remote = Remote("{{{_url}}}/") private val gson = Gson() {{#methods}} suspend fun {{{name}}}({{#parameters}}{{{name}}}{{^last}}, {{/last}}{{/parameters}}): {{{returnType}}} { val params = listOf<JsonElement>( {{#sendParameters}} gson.toJsonTree({{{name}}}){{^last}}, {{/last}} {{/sendParameters}} ) val type = object : TypeToken<{{{returnType}}}>(){}.type {{#returnTypeCast}} var res : {{{returnType}}} res = remote.makeRequest(type, {{{methodCaller}}}, params) return res {{/returnTypeCast}} {{^returnTypeCast}} val res = remote.makeRequest(type, {{{methodCaller}}}, params) {{/returnTypeCast}} } {{/methods}} } `; class SdkGenerator { async generateSdk(sdkGeneratorInput) { const generateSdkOutput = { files: [], }; for (const classInfo of sdkGeneratorInput.classesInfo) { const _url = "%%%link_to_be_replace%%%"; const classConfiguration = classInfo.classConfiguration; let classDefinition = undefined; if (classInfo.program.body === undefined) { continue; } const view = { otherClasses: [], className: undefined, _url: _url, methods: [], }; let exportClassChecker = false; for (const elem of classInfo.program.body) { if (elem.type === AstNodeType.ClassDefinition) { classDefinition = elem; view.className = classDefinition.name; for (const methodDefinition of classDefinition.methods) { const methodConfiguration = classConfiguration.methods.find((e) => e.name === methodDefinition.name); const methodConfigurationType = methodConfiguration?.type || classConfiguration.type; if (methodConfigurationType !== TriggerType.jsonrpc || classConfiguration.type !== TriggerType.jsonrpc) { continue; } exportClassChecker = true; const methodView = { name: methodDefinition.name, parameters: [], methodCaller: methodDefinition.params.length === 0 ? `"${classDefinition.name}.${methodDefinition.name}"` : `"${classDefinition.name}.${methodDefinition.name}"`, }; if (methodDefinition.returnType.type === AstNodeType.VoidLiteral) { methodView.returnType = this.getReturnType(methodDefinition.returnType); methodView.returnTypeCast = undefined; } else { methodView.returnTypeCast = this.castReturnTypeToPropertyType(methodDefinition.returnType, "response"); methodView.returnType = this.getReturnType(methodDefinition.returnType); } methodView.parameters = methodDefinition.params.map((e) => { return { name: (KOTLIN_RESERVED_KEYWORDS.includes(e.name) ? e.name + "_" : e.name) + ": " + getParamType(e.paramType), last: false, }; }); if (methodView.parameters.length > 0) { methodView.parameters[methodView.parameters.length - 1].last = true; } methodView.sendParameters = methodDefinition.params.map((e) => { return { name: KOTLIN_RESERVED_KEYWORDS.includes(e.name) ? e.name + "_" : e.name, last: false, }; }); if (methodView.sendParameters.length > 0) { methodView.sendParameters[methodView.parameters.length - 1].last = true; } view.methods.push(methodView); } } else if (elem.type === AstNodeType.StructLiteral) { const structLiteral = elem; view.otherClasses.push({ name: structLiteral.name, fields: structLiteral.typeLiteral.properties.map((e) => ({ type: getParamType(e.type), fieldName: e.name, })), }); } } if (classDefinition === undefined) { continue; } if (!exportClassChecker) { continue; } const rawSdkClassName = `${classDefinition.name}.kt`; const sdkClassName = rawSdkClassName .replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`) .slice(1); generateSdkOutput.files.push({ path: sdkClassName, data: Mustache.render(template, view), className: classDefinition.name, }); } // generate remote.kt generateSdkOutput.files.push({ className: "Remote", path: "remote.kt", data: kotlinSdk, }); return generateSdkOutput; } castReturnTypeToPropertyType(node, variableName) { let implementation = ""; switch (node.type) { case AstNodeType.StringLiteral: implementation += `${variableName}.toString().substring(1, ${variableName}.toString().length - 1)`; break; case AstNodeType.DoubleLiteral: implementation += `${variableName}.toDouble()`; break; case AstNodeType.BooleanLiteral: implementation += `${variableName}.toString().toBoolean()`; break; case AstNodeType.IntegerLiteral: implementation += `Integer.parseInt(${variableName}.toString())`; break; case AstNodeType.CustomNodeLiteral: implementation += `Json.decodeFromString<${node.rawValue}>(${variableName}.toString())`; break; case AstNodeType.ArrayType: implementation += castArrayRecursivelyInitial(node, variableName); break; case AstNodeType.MapType: implementation += castMapRecursivelyInitial(node, variableName); } return implementation; } getReturnType(elem) { switch (elem.type) { case AstNodeType.StringLiteral: return "String"; case AstNodeType.DoubleLiteral: return "Double"; case AstNodeType.BooleanLiteral: return "Boolean"; case AstNodeType.IntegerLiteral: return "Int"; case AstNodeType.AnyLiteral: return "Abject"; case AstNodeType.VoidLiteral: return "Unit"; case AstNodeType.PromiseType: return `${getParamType(elem.generic)}`; case AstNodeType.ArrayType: return `List<${getParamType(elem.generic)}>`; case AstNodeType.MapType: return `Map<${getParamType(elem.genericKey)}, ${getParamType(elem.genericValue)}>`; case AstNodeType.CustomNodeLiteral: return `${elem.rawValue}`; case AstNodeType.DateType: return "Date"; default: return "Any"; } } } const supportedLanguages = ["kt", "kotlin"]; export default { SdkGenerator, supportedLanguages };