Skip to content

Added Raw logic for Get config from PSQL Db #333

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 4 commits into
base: CU-86c0km2vf_Create-Bootstrapper-Logic-for-Configuration-in-Postgres
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ public class BootstrapperConfig {
public final String KAFKA_APPLICATION_ID;
public final String[] DGRAPH_ALPHA_HOSTS;
public final int[] DGRAPH_ALPHA_PORTS;
public final String API_CONFIG_REFERENCE_FILENAME;
public final String API_FIELDS_CONFIG_FILENAME;
public final String SYSTEM_CONFIG_DIR;

public BootstrapperConfig(final Config parsedConfig) {
POSTGRESQL_IP = parsedConfig.getString("POSTGRESQL_IP");
Expand All @@ -40,6 +43,9 @@ public BootstrapperConfig(final Config parsedConfig) {
KAFKA_BOOTSTRAP_SERVERS = parsedConfig.getString("KAFKA_BOOTSTRAP_SERVERS");
KAFKA_APPLICATION_ID = parsedConfig.getString("KAFKA_APPLICATION_ID");
DGRAPH_ALPHA_HOSTS = parsedConfig.getString("DGRAPH_HOSTS").split(",");
API_CONFIG_REFERENCE_FILENAME = parsedConfig.getString("API_CONFIG_REFERENCE_FILENAME");
API_FIELDS_CONFIG_FILENAME = parsedConfig.getString("API_FIELDS_CONFIG_FILENAME");
SYSTEM_CONFIG_DIR = parsedConfig.getString("SYSTEM_CONFIG_DIR");
DGRAPH_ALPHA_PORTS = Arrays.stream(parsedConfig.getString("DGRAPH_PORTS").split(",")).mapToInt(s -> {
try {
return Integer.parseInt(s);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import org.jembi.jempi.bootstrapper.data.DataBootstrapper;
import org.jembi.jempi.bootstrapper.data.utils.DataBootstraperConsts;
import org.jembi.jempi.shared.models.GlobalConstants;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
Expand Down Expand Up @@ -150,7 +151,7 @@ public Boolean insertConfigurationData() throws SQLException, IOException {
}

private String[][] getConfigFiles(final String configurationTable) {
String configDir = Optional.of(Paths.get("/app/conf_system"))
String configDir = Optional.of(Paths.get(this.loadedConfig.SYSTEM_CONFIG_DIR))
.filter(Files::exists)
.map(Path::toString)
.orElseGet(() -> System.getenv("SYSTEM_CSV_DIR"));
Expand All @@ -159,11 +160,11 @@ private String[][] getConfigFiles(final String configurationTable) {
LOGGER.info("configDir " + configDir);
// Define an array of config file paths and their corresponding keys
String[][] configFiles = {
{configDir + "/config.json", "config"},
{configDir + "/config-api.json", "config-api"},
// Add more configuration files as needed
};
return configFiles;
{configDir + "/" + this.loadedConfig.API_CONFIG_REFERENCE_FILENAME, GlobalConstants.CONFIGURATION_CONFIG_KEY},
{configDir + "/" + this.loadedConfig.API_FIELDS_CONFIG_FILENAME, GlobalConstants.CONFIGURATION_CONFIG_API_KEY},
// Add more configuration files as needed
};
return configFiles;
}

private String readJsonFile(final String filePath) throws IOException {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
CREATE TABLE IF NOT EXISTS configuration (
id SERIAL PRIMARY KEY,
key VARCHAR(255) NOT NULL,
key VARCHAR(255) NOT NULL UNIQUE,
json JSON NOT NULL,
dateCreated TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
dateUpdated TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import akka.actor.typed.Behavior;
import akka.actor.typed.javadsl.*;
import akka.http.javadsl.server.directives.FileInfo;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.vavr.control.Either;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
Expand All @@ -24,17 +23,19 @@
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.util.*;


public final class BackEnd extends AbstractBehavior<BackEnd.Event> {

private static final Logger LOGGER = LogManager.getLogger(BackEnd.class);
private final PsqlClient psqlClient;
private final String pgIP;
private final int pgPort;
private final String pgUser;
private final String pgPassword;
private final String pgNotificationsDb;
Expand All @@ -50,6 +51,8 @@ public final class BackEnd extends AbstractBehavior<BackEnd.Event> {
private String[] dgraphHosts = null;
private int[] dgraphPorts = null;

private final PostgresClientDao postgresClientDao;

private BackEnd(
final Level debugLevel,
final ActorContext<Event> context,
Expand All @@ -74,7 +77,7 @@ private BackEnd(
this.dgraphHosts = dgraphHosts;
this.dgraphPorts = dgraphPorts;
this.pgIP = sqlIP;
Integer pgPort = sqlPort;
this.pgPort = sqlPort;
this.pgUser = sqlUser;
this.pgPassword = sqlPassword;
this.pgNotificationsDb = sqlNotificationsDb;
Expand All @@ -87,6 +90,8 @@ private BackEnd(
psqlNotifications = new PsqlNotifications(sqlIP, sqlPort, sqlNotificationsDb, sqlUser, sqlPassword);
psqlAuditTrail = new PsqlAuditTrail(sqlIP, sqlPort, sqlAuditDb, sqlUser, sqlPassword);
openMPI(kafkaBootstrapServers, kafkaClientId, debugLevel);
psqlClient = new PsqlClient(sqlIP, sqlPort, sqlConfigurationDb, sqlUser, sqlPassword);
this.postgresClientDao = PostgresClientDaoImpl.create(sqlIP, sqlPort, sqlConfigurationDb, sqlUser, sqlPassword);
} catch (Exception e) {
LOGGER.error(e.getMessage(), e);
throw e;
Expand Down Expand Up @@ -499,76 +504,32 @@ private Behavior<Event> getSqlDashboardDataHandler(final SQLDashboardDataRequest
}

private Behavior<Event> getConfigurationHandler(final GetConfigurationRequest request) {
Path configMasterJsonFilePath = Paths.get(systemConfigDirectory, configMasterFileName);
Path configReferenceJsonFilePath = Paths.get(systemConfigDirectory, configReferenceFileName);

Path configFilePath = configMasterJsonFilePath;
if (!Files.exists(configFilePath)) {
configFilePath = configReferenceJsonFilePath;
}

try {
String configFileContent = new String(Files.readAllBytes(configFilePath), StandardCharsets.UTF_8);
ObjectMapper mapper = new ObjectMapper();
Configuration configuration = mapper.readValue(configFileContent, Configuration.class);
Configuration configuration = postgresClientDao.getConfiguration(GlobalConstants.CONFIGURATION_CONFIG_KEY);
request.replyTo.tell(new GetConfigurationResponse(configuration));
} catch (Exception exception) {
LOGGER.error("getConfigurationHandler failed with error: {}", exception.getMessage());
} catch (Exception e) {
LOGGER.error("getConfigurationHandler failed with error: {}", e.getMessage());
}

return Behaviors.same();
}

private Behavior<Event> getFieldsConfigurationHandler(final GetFieldsConfigurationRequest request) {
final var separator = FileSystems.getDefault().getSeparator();
final String configDir = System.getenv("SYSTEM_CONFIG_DIRS");
Path filePath = Paths.get(""); // Start with an empty path
// Create ubuntuFilePath
Path ubuntuFilePath = new File(String.format("%sapp%sconf_system%s%s", separator, separator, separator, fieldsConfigurationFileName)).toPath();
// Check if ubuntuFilePath exists
if (Files.exists(ubuntuFilePath)) {
filePath = ubuntuFilePath;
} else {
// If ubuntuFilePath does not exist, assign the alternative path
filePath = Paths.get(configDir, "config-api.json");
}
try {
String configFileContent = new String(Files.readAllBytes(filePath), StandardCharsets.UTF_8);
FieldsConfiguration fieldsConfiguration = AppUtils.OBJECT_MAPPER.readValue(configFileContent, FieldsConfiguration.class);
ArrayList<FieldsConfiguration.Field> fields = new ArrayList<>();
fields.addAll(fieldsConfiguration.systemFields());
fields.addAll(fieldsConfiguration.fields());
List<FieldsConfiguration.Field> fields = postgresClientDao.getFieldsConfiguration(GlobalConstants.CONFIGURATION_CONFIG_API_KEY);
request.replyTo.tell(new GetFieldsConfigurationResponse(fields));
} catch (Exception exception) {
LOGGER.error("getFieldsConfigurationHandler failed with error: {}", exception.getLocalizedMessage());
} catch (Exception e) {
LOGGER.error("getFieldsConfigurationHandler failed with error: {}", e.getMessage());
}

return Behaviors.same();
}

private Behavior<Event> postConfigurationHandler(final PostConfigurationRequest request) {
Path configMasterJsonFilePath = Paths.get(systemConfigDirectory, configMasterFileName);
ObjectMapper objectMapper = new ObjectMapper();
Configuration configJson = request.configuration;

if (configJson == null) {
request.replyTo.tell(new PostConfigurationResponse("error: configuration is missing"));
LOGGER.error("postConfigurationHandler failed: configuration is missing in the request");
return Behaviors.same();
}

try {
String jsonConfig = objectMapper.writeValueAsString(configJson);
Files.write(configMasterJsonFilePath,
jsonConfig.getBytes(StandardCharsets.UTF_8),
StandardOpenOption.CREATE,
StandardOpenOption.TRUNCATE_EXISTING);
postgresClientDao.saveConfiguration(request.configuration, GlobalConstants.CONFIGURATION_CONFIG_KEY);
request.replyTo.tell(new PostConfigurationResponse("ok"));
} catch (Exception exception) {
LOGGER.error("postConfigurationHandler failed with error: {}", exception.getMessage());
request.replyTo.tell(new PostConfigurationResponse("error: " + exception.getMessage()));
} catch (Exception e) {
LOGGER.error("postConfigurationHandler failed with error: {}", e.getMessage());
}

return Behaviors.same();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package org.jembi.jempi.libapi;

import org.jembi.jempi.shared.models.ConfigurationModel.Configuration;
import org.jembi.jempi.shared.models.FieldsConfiguration;
import java.util.List;

public interface PostgresClientDao {
void connect();
void disconnect();
Configuration getConfiguration(String configKey);
List<FieldsConfiguration.Field> getFieldsConfiguration(String configKey);
void saveConfiguration(Configuration configuration, String configKey);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
package org.jembi.jempi.libapi;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jembi.jempi.shared.models.ConfigurationModel.Configuration;
import org.jembi.jempi.shared.models.FieldsConfiguration;
import org.jembi.jempi.shared.utils.AppUtils;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

/**
* Implementation of PostgresClientDao interface for interacting with a PostgreSQL database.
* This class can be extended to provide custom behavior for database operations.
*/
public final class PostgresClientDaoImpl implements PostgresClientDao {
private static final Logger LOGGER = LogManager.getLogger(PostgresClientDaoImpl.class);
private final PsqlClient psqlClient;

/**
* Constructs a new PostgresClientDaoImpl with the given database connection parameters.
*
* @param pgIP The IP address of the PostgreSQL server
* @param pgPort The port number of the PostgreSQL server
* @param pgDatabase The name of the database to connect to
* @param pgUser The username for database authentication
* @param pgPassword The password for database authentication
*/
private PostgresClientDaoImpl(
final String pgIP,
final int pgPort,
final String pgDatabase,
final String pgUser,
final String pgPassword) {
this.psqlClient = new PsqlClient(pgIP, pgPort, pgDatabase, pgUser, pgPassword);
}

/**
* Creates a new instance of PostgresClientDaoImpl with the given database connection parameters.
*
* @param ip The IP address of the PostgreSQL server
* @param port The port number of the PostgreSQL server
* @param db The name of the database to connect to
* @param user The username for database authentication
* @param password The password for database authentication
*/
public static PostgresClientDaoImpl create(
final String ip,
final int port,
final String db,
final String user,
final String password) {
return new PostgresClientDaoImpl(
ip,
port,
db,
user,
password);
}

/**
* Establishes a connection to the PostgreSQL database.
* This method can be overridden to provide custom connection logic.
*
* @throws SQLException if a database access error occurs
*/
@Override
public void connect() {
LOGGER.info("Connecting to PostgreSQL database");
psqlClient.connect();
LOGGER.info("Successfully connected to PostgreSQL database");
}

/**
* Closes the connection to the PostgreSQL database.
* This method can be overridden to provide custom disconnection logic.
*
* @throws SQLException if a database access error occurs
*/
@Override
public void disconnect() {
LOGGER.info("Disconnecting from PostgreSQL database");
psqlClient.disconnect();
LOGGER.info("Successfully disconnected from PostgreSQL database");
}

/**
* Retrieves the current configuration from the database.
* This method can be overridden to provide custom configuration retrieval logic.
*
* @return The Configuration object, or null if no configuration is found
* @throws SQLException if a database access error occurs or the retrieved JSON is invalid
*/
@Override
public Configuration getConfiguration(final String configKey) {
this.connect();
LOGGER.info("Retrieving configuration from database");
String sql = String.format("SELECT json FROM CONFIGURATION WHERE key = '%s' ORDER BY id DESC LIMIT 1", configKey);
try (PreparedStatement preparedStatement = psqlClient.prepareStatement(sql);
ResultSet rs = preparedStatement.executeQuery()) {

if (rs.next()) {
String configFileContent = rs.getString("json");
ObjectMapper mapper = new ObjectMapper();
Configuration config = mapper.readValue(configFileContent, Configuration.class);
LOGGER.info("Successfully retrieved configuration from database");
return config;
} else {
LOGGER.info("No configuration found in the database");
return null;
}
} catch (Exception e) {
LOGGER.error(e);
}
this.disconnect();
return null;
}

/**
* Retrieves the fields configuration from the database.
* This method can be overridden to provide custom fields configuration retrieval logic.
*
* @return A List of FieldsConfiguration.Field objects, or null if no configuration is found
* @throws SQLException if a database access error occurs or the retrieved JSON is invalid
*/
@Override
public List<FieldsConfiguration.Field> getFieldsConfiguration(final String configKey) {
this.connect();
LOGGER.info("Retrieving fields configuration from database");
String sql = String.format("SELECT json FROM CONFIGURATION WHERE key = '%s' ORDER BY id DESC LIMIT 1", configKey);
try (PreparedStatement preparedStatement = psqlClient.prepareStatement(sql);
ResultSet rs = preparedStatement.executeQuery()) {
if (rs.next()) {
String configFileContent = rs.getString("json");
FieldsConfiguration fieldsConfiguration = AppUtils.OBJECT_MAPPER.readValue(configFileContent, FieldsConfiguration.class);
ArrayList<FieldsConfiguration.Field> fields = new ArrayList<>();
if (fieldsConfiguration != null && fieldsConfiguration.systemFields() != null && fieldsConfiguration.fields() != null) {
fields.addAll(fieldsConfiguration.systemFields());
fields.addAll(fieldsConfiguration.fields());
}
LOGGER.info("Successfully retrieved fields configuration from database");
return fields;
} else {
LOGGER.info("No fields configuration found in the database");
return null;
}
} catch (Exception e) {
LOGGER.error(e);
}
this.disconnect();
return null;
}

/**
* Saves the given configuration to the database.
* This method can be overridden to provide custom configuration saving logic.
*
* @param configuration The Configuration object to be saved
* @throws SQLException if a database access error occurs or the configuration cannot be converted to JSON
*/
@Override
public void saveConfiguration(final Configuration configuration, final String configKey) {
LOGGER.info("Saving configuration to database");
this.connect();
String sql = "UPDATE CONFIGURATION SET json = ?::jsonb WHERE key = ?";
try (PreparedStatement preparedStatement = psqlClient.prepareStatement(sql)) {
String jsonConfig = AppUtils.OBJECT_MAPPER.writeValueAsString(configuration);
preparedStatement.setString(1, jsonConfig);
preparedStatement.setString(2, configKey);
int rowsAffected = preparedStatement.executeUpdate();
LOGGER.info("Successfully saved configuration to database. Rows affected: {}", rowsAffected);
} catch (Exception e) {
LOGGER.error(e);
}
this.disconnect();
}
}
Loading