Skip to content
Merged
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
31 changes: 28 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Dynamic Configuration Service Makefile

.PHONY: help setup infra-up infra-down infra-restart infra-logs infra-ps \
verify cleanup proto distribution-service services sdk test clean install all rebuild \
verify cleanup proto distribution-service validation-service services sdk test clean install all rebuild \
db-shell redis-shell kafka-topics kafka-ui grafana pgadmin wait-for-services dev \
format format-check \
example test-statsd \
Expand Down Expand Up @@ -53,6 +53,7 @@ help:
@echo "$(GREEN)Local Development (Mac/Linux):$(NC)"
@echo " make proto - Generate protobuf and gRPC code"
@echo " make distribution-service - Build distribution service"
@echo " make validation-service - Build validation service"
@echo " make services - Build all C++ services"
@echo " make sdk - Build client SDK"
@echo " make all - Build everything"
Expand Down Expand Up @@ -462,6 +463,12 @@ API_SERVICE_SRCS := $(wildcard $(API_SERVICE_DIR)/*.cpp)
API_SERVICE_OBJS := $(patsubst $(SRC_DIR)/%.cpp,$(BUILD_DIR)/%.o,$(API_SERVICE_SRCS))
API_SERVICE_BIN := $(BIN_DIR)/api-service

# Validation Service
VALIDATION_SERVICE_DIR := $(SRC_DIR)/validation-service
VALIDATION_SERVICE_SRCS := $(wildcard $(VALIDATION_SERVICE_DIR)/*.cpp)
VALIDATION_SERVICE_OBJS := $(patsubst $(SRC_DIR)/%.cpp,$(BUILD_DIR)/%.o,$(VALIDATION_SERVICE_SRCS))
VALIDATION_SERVICE_BIN := $(BIN_DIR)/validation-service

# --- Distribution Service ---

$(DIST_SERVICE_BIN): $(DIST_SERVICE_OBJS) $(PROTO_OBJS) $(STATSD_OBJ) | $(BIN_DIR)
Expand Down Expand Up @@ -493,16 +500,34 @@ $(BUILD_DIR)/api-service/%.o: $(SRC_DIR)/api-service/%.cpp | $(BUILD_DIR)/api-se
$(BUILD_DIR)/api-service:
@mkdir -p $@

# --- Validation Service ---

$(VALIDATION_SERVICE_BIN): $(VALIDATION_SERVICE_OBJS) $(PROTO_OBJS) $(STATSD_OBJ) | $(BIN_DIR)
@echo "$(YELLOW)Linking Validation Service...$(NC)"
@$(CXX) $(LDFLAGS) $^ $(SERVICE_LIBS) -lyaml-cpp -o $@
@echo "$(GREEN)✓ Built $@$(NC)"

$(BUILD_DIR)/validation-service/%.o: $(SRC_DIR)/validation-service/%.cpp | $(BUILD_DIR)/validation-service
@echo "$(YELLOW)Compiling $<...$(NC)"
@$(CXX) $(CXXFLAGS) $(INCLUDES) -c $< -o $@

$(BUILD_DIR)/validation-service:
@mkdir -p $@

validation-service: $(VALIDATION_SERVICE_BIN)
@echo "$(GREEN)✓ Validation service built$(NC)"

# --- All Services ---

services: $(DIST_SERVICE_BIN) $(API_SERVICE_BIN)
services: $(DIST_SERVICE_BIN) $(API_SERVICE_BIN) $(VALIDATION_SERVICE_BIN)
@echo "$(GREEN)✓ All services built$(NC)"

#==============================================================================
# BUILD TARGETS
#==============================================================================

all: proto sdk services
all: proto sdk services cli
@echo "$(GREEN)✓ All components built$(NC)"

proto: $(PROTO_SRCS) $(PROTO_HDRS) $(GRPC_SRCS) $(GRPC_HDRS)
@echo "$(GREEN)✓ Proto files generated$(NC)"
Expand Down
9 changes: 9 additions & 0 deletions config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
service: payment-service
environment: production
settings:
max_connections: 100
timeout_ms: 5000 # ⚠️ Tab character here (intentional error)
features:
fraud_detection: true
database:
host: db.payment.internal
28 changes: 28 additions & 0 deletions config/validation-service.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
server:
port: 8083
max_connections: 500

postgres:
host: postgres
port: 5432
database: configservice
user: configuser
password: configpass
max_connections: 10
connection_timeout: 10

redis:
host: redis
port: 6379
cache_ttl: 600 # 10 minutes

statsd:
host: statsd-exporter
port: 9125
prefix: validation

validation:
max_config_size: 1048576 # 1MB
timeout_seconds: 5
enable_caching: true
strict_mode: false
2 changes: 2 additions & 0 deletions include/api_service/api_service.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "api.grpc.pb.h"
#include "database_manager.h"
#include "statsdclient/statsd_client.h"
#include "validation_client.h"

namespace apiservice {

Expand Down Expand Up @@ -59,6 +60,7 @@ class ApiServiceImpl final : public configservice::ConfigAPIService::Service {
std::unique_ptr<DatabaseManager> db_;
std::unique_ptr<RdKafka::Producer> kafka_producer_;
std::unique_ptr<statsdclient::StatsDClient> statsd_;
std::unique_ptr<ValidationClient> validation_client_;
bool initialized_;

// Helpers
Expand Down
33 changes: 33 additions & 0 deletions include/api_service/validation_client.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#pragma once

#include <grpcpp/grpcpp.h>

#include <memory>
#include <string>

#include "validation.grpc.pb.h"

namespace apiservice {

class ValidationClient {
public:
explicit ValidationClient(const std::string& server_address);
~ValidationClient();

bool Initialize();
void Shutdown();

// Validate config
configservice::ValidateConfigResponse ValidateConfig(const std::string& service_name,
const std::string& content,
const std::string& format,
bool strict = false);

private:
std::string server_address_;
std::shared_ptr<grpc::Channel> channel_;
std::unique_ptr<configservice::ValidationService::Stub> stub_;
bool initialized_;
};

} // namespace apiservice
53 changes: 53 additions & 0 deletions include/validation_service/config.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#pragma once

#include <string>
#include <yaml-cpp/yaml.h>

namespace validationservice {

struct ServerConfig {
int port = 8083;
int max_connections = 500;
};

struct PostgresConfig {
std::string host = "postgres";
int port = 5432;
std::string database = "configservice";
std::string user = "configuser";
std::string password = "configpass";
int max_connections = 10;
int connection_timeout_seconds = 10;
};

struct RedisConfig {
std::string host = "redis";
int port = 6379;
int cache_ttl_seconds = 600;
};

struct StatsDConfig {
std::string host = "statsd-exporter";
int port = 9125;
std::string prefix = "validation";
};

struct ValidationConfig {
size_t max_config_size = 1024 * 1024; // 1MB
int timeout_seconds = 5;
bool enable_caching = true;
bool strict_mode = false;
};

struct ServiceConfig {
ServerConfig server;
PostgresConfig postgres;
RedisConfig redis;
StatsDConfig statsd;
ValidationConfig validation;

static ServiceConfig LoadFromFile(const std::string& path);
static ServiceConfig LoadDefaults();
};

} // namespace validationservice
58 changes: 58 additions & 0 deletions include/validation_service/database_manager.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#pragma once

#include "config.h"

#include <memory>
#include <mutex>
#include <pqxx/pqxx>
#include <string>
#include <vector>

#include "validation.pb.h"

namespace validationservice {

class DatabaseManager {
public:
explicit DatabaseManager(const PostgresConfig& config);
~DatabaseManager();

bool Initialize();
void Shutdown();

// Schema operations
std::pair<bool, std::string> RegisterSchema(const configservice::ValidationSchema& schema);

configservice::ValidationSchema GetSchema(const std::string& schema_id);

std::vector<configservice::ValidationSchema> ListSchemas(const std::string& service_name,
int limit, int offset,
int& total_count);

// Validation history
void RecordValidation(const std::string& service_name, const std::string& content, bool result,
const std::string& errors, const std::string& warnings,
const std::string& validated_by);

// Validation rules
struct ValidationRule {
std::string rule_id;
std::string service_name;
std::string field_path;
std::string rule_type;
std::string rule_config;
std::string error_message;
};

std::vector<ValidationRule> GetRulesForService(const std::string& service_name);

private:
PostgresConfig config_;
std::unique_ptr<pqxx::connection> conn_;
std::mutex mutex_;
bool initialized_;

std::string BuildConnectionString();
};

} // namespace validationservice
36 changes: 36 additions & 0 deletions include/validation_service/json_validator.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#pragma once

#include <string>
#include <vector>

#include "validation.pb.h"

namespace validationservice {

class JsonValidator {
public:
JsonValidator() = default;

// Validate JSON syntax
bool ValidateSyntax(const std::string& content,
std::vector<configservice::ValidationError>& errors);

// Validate against JSON schema
bool ValidateSchema(const std::string& content, const std::string& schema,
std::vector<configservice::ValidationError>& errors);

// Validate value ranges
bool ValidateRanges(const std::string& content, const std::string& service_name,
std::vector<configservice::ValidationError>& errors);

// Check required fields
bool ValidateRequired(const std::string& content,
const std::vector<std::string>& required_fields,
std::vector<configservice::ValidationError>& errors);

private:
void AddError(std::vector<configservice::ValidationError>& errors, const std::string& field,
const std::string& type, const std::string& message);
};

} // namespace validationservice
77 changes: 77 additions & 0 deletions include/validation_service/validation_service.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
#pragma once

#include "config.h"

#include <grpcpp/grpcpp.h>

#include <hiredis/hiredis.h>
#include <memory>
#include <mutex>
#include <string>

#include "database_manager.h"
#include "json_validator.h"
#include "statsdclient/statsd_client.h"
#include "validation.grpc.pb.h"
#include "yaml_validator.h"

namespace validationservice {

class ValidationServiceImpl final : public configservice::ValidationService::Service {
public:
explicit ValidationServiceImpl(const ServiceConfig& config);
~ValidationServiceImpl();

bool Initialize();
void Shutdown();

// ─────────────────────────────────────────────
// gRPC Methods
// ─────────────────────────────────────────────

grpc::Status ValidateConfig(grpc::ServerContext* context,
const configservice::ValidateConfigRequest* request,
configservice::ValidateConfigResponse* response) override;

grpc::Status RegisterSchema(grpc::ServerContext* context,
const configservice::RegisterSchemaRequest* request,
configservice::RegisterSchemaResponse* response) override;

grpc::Status GetSchema(grpc::ServerContext* context,
const configservice::GetSchemaRequest* request,
configservice::GetSchemaResponse* response) override;

grpc::Status ListSchemas(grpc::ServerContext* context,
const configservice::ListSchemasRequest* request,
configservice::ListSchemasResponse* response) override;

private:
ServiceConfig config_;
std::unique_ptr<DatabaseManager> db_;
std::unique_ptr<JsonValidator> json_validator_;
std::unique_ptr<YamlValidator> yaml_validator_;
std::unique_ptr<statsdclient::StatsDClient> statsd_;

// Redis for caching validation results
redisContext* redis_ctx_;
std::mutex redis_mutex_;

bool initialized_;

// Helper methods
bool ValidateSize(const std::string& content,
std::vector<configservice::ValidationError>& errors);

bool ApplyCustomRules(const std::string& service_name, const std::string& content,
std::vector<configservice::ValidationError>& errors);

std::string GetCachedValidationResult(const std::string& cache_key);
void CacheValidationResult(const std::string& cache_key, const std::string& result);

void RecordMetric(const std::string& metric);
void RecordTimer(const std::string& metric, int milliseconds);

std::string ComputeHash(const std::string& content);
};

} // namespace validationservice
Loading
Loading