All Downloads are FREE. Search and download functionalities are using the official Maven repository.

farm.nurture.laminar.generator.ProtoGenerator Maven / Gradle / Ivy

The newest version!
/*
 *  Copyright 2022 Nurture.Farm
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package farm.nurture.laminar.generator;

import static farm.nurture.laminar.generator.Constants.ENUM;
import static farm.nurture.laminar.generator.Constants.JAVA_TYPE_INTEGER;
import static farm.nurture.laminar.generator.Constants.JAVA_TYPE_LONG;
import static farm.nurture.laminar.generator.Constants.JAVA_TYPE_STRING;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import farm.nurture.infra.util.StringUtils;
import farm.nurture.laminar.core.util.JsonUtil;
import farm.nurture.laminar.generator.ast.AstBase;
import farm.nurture.laminar.generator.ast.AstBase.Flags;
import farm.nurture.laminar.generator.ast.AstTree;
import farm.nurture.laminar.generator.config.input.CodeGeneratorInputFactory;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class ProtoGenerator {

    private static final Logger logger = LoggerFactory.getLogger(ProtoGenerator.class);
    private String localBasePath;

    public void main(String[] args) throws Exception {
        generateCode(args);
    }

    public String generateCode(String[] args) throws IOException, SQLException {
        String configFilePath = args[1];
        String sqlDumpFile = args[2];
        String lamFilePath = args[3];
        String dbName = args[4];
        String dbUserName = args[5];
        String dbPwd = args[6];
        String dbUrl = args[7];
        this.localBasePath = args[8];
        startUpComments();

        if (StringUtils.isEmpty(configFilePath)) {
            throw new FileNotFoundException("config file is not given");
        }

        Configuration configuration = getConfiguration(configFilePath);
        String language;

        language = args[0];
        createDummyDb(sqlDumpFile, dbName, dbUserName, dbPwd, dbUrl);

        AstTree astTree = new AstTree();
        astTree.setConfigFilePath(configFilePath);
        astTree.setDumpFilePath(sqlDumpFile);
        astTree.setLamFilePath(lamFilePath);
        AstBase.Flags flags = astTree.setLang(language);
        astTree.onStart(configuration, flags);

        Map schemaColDataTypes;

        try{
            schemaColDataTypes =
                new ProtoGeneratorShowtables().showTables(dbName, dbUserName, dbPwd, dbUrl, flags);
            }
        catch (SQLException e){
            throw new SQLException("Problem in connection with the database.",e);
        }
        catch (IOException e){
            throw new IOException("SQL file is not correct.",e);
        }

        //        if(flags.contracts) {
        //            loadContractEnums(astTree.contractPath, schemaColDataTypes);
        //        }

        List entries;

        try{
            entries = new CodeGeneratorInputFactory()
                    .getCodeGeneratorInputService(
                        configuration.getProto().getCodeGenerationInputConfigSource())
                    .getApplicationConfiguration(lamFilePath);
        }
        catch (Exception e){
            throw new IOException("LAM file is incorrect.",e);
        }

        if (entries == null) {throw new NullPointerException("LAM file is incorrect.");}

        for (AppConfigVO entry : entries) {
            processEntries(dbName, dbUserName, dbPwd, dbUrl, configuration, schemaColDataTypes, astTree,
                flags, entry);
        }
        try{
            astTree.onEnd();
        }
        catch (NullPointerException e){
            throw new NullPointerException("config file is incorrect.");
        }

        try{
            dropDummyDb(dbName, dbUserName, dbPwd, dbUrl);
            }
        catch (SQLException e){
            throw new SQLException("Problem in connection with database.");
        }
        return configuration.getProto().getServiceName();
    }

    private Configuration getConfiguration(String configFilePath) throws IOException {
        Configuration configuration =
            JsonUtil.deserialize(Files.readAllBytes(Paths.get(configFilePath)), Configuration.class);

        configuration.validateConfig();

        String language = "java,proto,graphql";

        if (!configuration.getProto().getCodeGenerationInputConfigSource().equals("LAM")) {
            throw new IllegalArgumentException("code_generation_input_config_source is incorrect in config file.");
        }
        return configuration;
    }

    private void processEntries(String dbName, String dbUserName, String dbPwd, String dbUrl,
        Configuration configuration, Map schemaColDataTypes, AstTree astTree,
        Flags flags, AppConfigVO entry) {
        String title = entry.getTitle();
        String body = entry.getSqlStmt();
        String parameterNameTypeStr = (null == entry.getSqlParams()) ? "" : entry.getSqlParams();

        debugAppConfig(title, body);
        List requestFields = analyzeParameters(title, parameterNameTypeStr, schemaColDataTypes, entry, flags);
        List responseFields = null;

        if (!entry.isMutation()) { // Run the sql to check the response
            try{
                responseFields = ifNotIsMutation(dbName, dbUserName, dbPwd, dbUrl, configuration, entry, requestFields);
                }
            catch (Exception e){
                throw new RuntimeException("LAM file is incorrect.");
            }
        }

        try{
            astTree.onEntry(entry, requestFields, responseFields);
        } catch (Exception e){
            throw new RuntimeException("LAM file or SQL file is incorrect.");
        }

    }

    private List ifNotIsMutation(
        String dbName,
        String dbUserName,
        String dbPwd,
        String dbUrl,
        Configuration configuration,
        AppConfigVO entry,
        List requestFields) {
        List responseFields;
        Set sqlReplaceNames = new HashSet<>();
        if (!entry.isNullSqlReplace()) {
            List replaceTokens = StringUtils.fastSplit(entry.getSqlReplace(), ',');
            for (String rToken : replaceTokens) {
                String StrTrimmed = rToken.trim();
                int cutPos = StrTrimmed.indexOf(':');
                String Name = StrTrimmed.substring(0, cutPos);
                sqlReplaceNames.add(Name);
            }
        }
        Object[] parameters = null;
        if (!requestFields.isEmpty()) {
            parameters = processRequestFields(requestFields, sqlReplaceNames);
        }

        String sqlStmt = entry.getSqlStmt();
        if (!entry.isNullSqlReplace()) {
            List replaceTokens = StringUtils.fastSplit(entry.getSqlReplace(), ',');
            for (String rToken : replaceTokens) {
                String rTokenName = rToken.substring(0, rToken.indexOf(':'));
                sqlStmt = sqlStmt.replace("@" + rTokenName + "@", "''");
                logger.info("Replaced sql stmt = {}",sqlStmt);
            }
        }

        if (StringUtils.isEmpty(sqlStmt)) responseFields = new ArrayList<>();
        else
            responseFields =
                new CodeGeneratorInputFactory()
                    .getCodeGeneratorInputService(
                        configuration.getProto().getCodeGenerationInputConfigSource())
                    .getFieldDetailsForReqRes(sqlStmt, parameters, dbName, dbUrl, dbUserName, dbPwd);
        return responseFields;
    }

    private Object[] processRequestFields(
        List requestFields, Set sqlReplaceNames) {
        Object[] parameters;
        int requestFieldSize = 0;
        for (Iterator iter = requestFields.listIterator(); iter.hasNext(); ) {
            FieldDetail fieldDetail = iter.next();
            if (!sqlReplaceNames.contains(fieldDetail.getFieldName())) {
                requestFieldSize++;
            }
        }
        parameters = new Object[requestFieldSize];
        int index = 0;
        for (FieldDetail fld : requestFields) {
            if (sqlReplaceNames.contains(fld.getFieldName())) {
                continue;
            }
            if (fld.getJavaType().equals(JAVA_TYPE_INTEGER) || fld.getJavaType().equals(JAVA_TYPE_LONG)) {
                parameters[index] = 0;
            }
            index++;
        }
        return parameters;
    }

    private void startUpComments() {
        logger.info("********************************************");
        logger.info("********************************************");
        logger.info("********************************************");
        logger.info("********************************************");
        logger.info(
            "Please don't add secrets to your code - api key, passwords or any other thing that can leak.");
        logger.info("Instead use env variables.");
        logger.info("********************************************");
        logger.info("********************************************");
        logger.info("********************************************");
        logger.info("********************************************");
    }

    private void createDummyDb(
        String sqlDumpFile, String dbName, String dbUserName, String dbPwd, String dbUrl)
        throws IOException {
        logger.info("################## Database Interaction ############");

        String mysqlCreateDatabaseCmd =
            "create database dbName; GRANT ALL PRIVILEGES ON dbName.* TO userName IDENTIFIED BY 'pwd'";
        mysqlCreateDatabaseCmd = mysqlCreateDatabaseCmd.replaceAll("dbName", dbName);
        mysqlCreateDatabaseCmd = mysqlCreateDatabaseCmd.replace("userName", dbUserName);
        mysqlCreateDatabaseCmd = mysqlCreateDatabaseCmd.replace("pwd", dbPwd);

        String mysqlImportCommand = "mysql -h " + dbUrl + " -u userName -ppwd dbName < sqlDumpFile;";
        mysqlImportCommand = mysqlImportCommand.replaceAll("dbName", dbName);
        mysqlImportCommand = mysqlImportCommand.replace("sqlDumpFile", sqlDumpFile);
        mysqlImportCommand = mysqlImportCommand.replace("userName", dbUserName);
        mysqlImportCommand = mysqlImportCommand.replace("pwd", dbPwd);

        String fileName = this.localBasePath + dbName + ".sh";

        FileWriter fileWriter = new FileWriter(fileName);
        fileWriter.write("mysql -h " + dbUrl + " -u " + dbUserName + " -p" + dbPwd + " -e \"");
        fileWriter.write(mysqlCreateDatabaseCmd);
        fileWriter.write("\"");
        fileWriter.write("\n");
        fileWriter.write(mysqlImportCommand);
        fileWriter.close();
        Runtime.getRuntime().exec(new String[] {"sh", fileName});
        sleep(5000);

        File file = new File(fileName);
        file.delete();
    }

    private void dropDummyDb(String dbName, String dbUserName, String dbPwd, String dbUrl)
        throws SQLException {
        // Deleting the temp database

        try {

            String mysqlDropDatabaseCmd = "drop database " + dbName + ";";

            logger.info("mysqlDropDatabaseCmd is {}",mysqlDropDatabaseCmd);

            String fileName = localBasePath + dbName + ".sh";

            FileWriter fileWriter = new FileWriter(fileName);
            fileWriter.write("mysql " + "-h " + dbUrl + " -u " + dbUserName + " -p" + dbPwd + " -e \"");
            fileWriter.write(mysqlDropDatabaseCmd);
            fileWriter.write("\"");
            fileWriter.close();
            Runtime.getRuntime().exec(new String[] {"sh", fileName});
            sleep(5000);

            File file = new File(fileName);
            file.delete();
        }
        catch (Exception e) {
            throw new SQLException("Problem in dropping the database, check connection!");
        }
    }

    private void sleep(int ms) {
        try {
            Thread.sleep(ms);
        } catch ( InterruptedException e) {
            logger.error("exception inside sleep method",e);
        }
    }

    private List analyzeParameters(
        String title,
        String parameterNameTypeStr,
        Map schemaColDataTypes,
        AppConfigVO entry,
        AstBase.Flags flags) {

        List paramDetails = new ArrayList<>();
        if (parameterNameTypeStr.trim().length() > 0) {
            List parameterNameTypeList = StringUtils.fastSplit(parameterNameTypeStr, ',');

            for (String aNameTypeStr : parameterNameTypeList) {
                processANameTypeStr(title, schemaColDataTypes, entry, flags, paramDetails, aNameTypeStr);
            }
        }
        return paramDetails;
    }

    private void processANameTypeStr(String title, Map schemaColDataTypes,
        AppConfigVO entry, Flags flags, List paramDetails, String aNameTypeStr) {
        String aNameTypeStrTrimmed = aNameTypeStr.trim();
        int cutPos = aNameTypeStrTrimmed.indexOf(':');
        if (cutPos <= 0) {
            logger.error(
                "Service :{} >> Missing {:} separator in [{}] , "
                    + "expecting something like {}:table.field or {}:string"
                ,title,aNameTypeStrTrimmed , aNameTypeStrTrimmed,aNameTypeStrTrimmed);
            System.exit(1);
        }
        String aName = aNameTypeStrTrimmed.substring(0, cutPos);
        String aType = aNameTypeStrTrimmed.substring(cutPos + 1);
        if (aType.startsWith("farm.nurture.core.contracts")) {
            AppConfigVO.setIsContractUsed(true);
            paramDetails.add(new FieldDetail(aName, aType, JAVA_TYPE_STRING, ENUM));
            flags.setContracts(true);
        } else if (schemaColDataTypes.containsKey(aType)) {
            FieldDetail fldDetail = schemaColDataTypes.get(aType);
            //
            // if(fldDetail.protoType.toLowerCase().startsWith("farm.nurture.core.contracts") &&
            // !AppConfigVO.isContractUsed) AppConfigVO.isContractUsed = true;
            //                    if(aType.toLowerCase().startsWith("farm.nurture.core.contracts") &&
            // !AppConfigVO.isContractUsed)
            paramDetails.add(fldDetail);
        } else {
            //                    System.err.println("Service :" + title + " >> Improper datatype in
            // [" + aNameTypeStrTrimmed + "] aName = " + aName + " , DataType=[" + aType + "]");
            //                    System.out.println("Missing in list : " +
            // schemaColDataTypes.toString());
            if (entry.isImplDao())
                paramDetails.add(new FieldDetail(aType, aType, JAVA_TYPE_STRING, ENUM));
        }
    }

    private void debugAppConfig(String title, String body) {
        logger.info(
            "\n\n******************************************************************************************************  ");
        logger.info("\t\t\t\t{}\t\t\t\t",title);
        logger.info("body = {}",body);
        logger.info(
            "***************************************************************************************************** */");
    }

    private void loadContractEnums(String contractPath, Map schemaColDataTypes)
        throws IOException {

        try {
            String contractCommonPath = contractPath + "/Common";
            File dir = new File(contractCommonPath);
            File[] files = dir.listFiles((dir1, name) -> name.endsWith(".proto"));

            for (File protofile : files) {
                List lines;
                lines = Files.readAllLines(protofile.toPath());
                if (lines != null) {
                    for (String line : lines) {
                        if (line.trim().startsWith(ENUM)) {
                            String enumType = line.replace(ENUM, "").replace("{", "").trim();
                            FieldDetail gType =
                                new DatabaseReader().deriveProtoDataType(enumType, "enum()", "ContractEnum");
                            schemaColDataTypes.put(
                                "ContractEnum.farm.nurture.core.contracts.common." + enumType, gType);
                        }
                    }
                }
            }
        } catch (IOException e) {
            throw new IOException("Error in loading Contract Enums");
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy