mirror of
https://github.com/elicpeter/nyx.git
synced 2026-06-18 20:15:14 +02:00
**refactor(dynamic): introduce NATS protocol emulator with publish/deliver support, enhance endpoint handling, and extend SDK compatibility for Go and Python**
This commit is contained in:
parent
a55849f1ca
commit
a12f7efc3a
7 changed files with 527 additions and 28 deletions
|
|
@ -2155,12 +2155,65 @@ fn emit_message_handler_harness(spec: &HarnessSpec, queue: &str) -> HarnessSourc
|
|||
let handler = &spec.entry_name;
|
||||
let broker = go_broker_for_adapter(spec);
|
||||
|
||||
let (broker_src, publish_marker, dispatch) = match broker {
|
||||
let (broker_src, publish_marker, broker_imports, broker_helpers, dispatch) = match broker {
|
||||
GoBroker::Nats => (
|
||||
crate::dynamic::stubs::nats_source(crate::symbol::Lang::Go),
|
||||
crate::dynamic::stubs::NATS_PUBLISH_MARKER,
|
||||
"\tnats \"github.com/nats-io/nats.go\"\n",
|
||||
r##"
|
||||
func nyxTryRealNats(subject string, payload string, dispatcher func(interface{}), marker string) bool {
|
||||
endpoint := os.Getenv("NYX_NATS_ENDPOINT")
|
||||
if !(strings.HasPrefix(endpoint, "nats://") || strings.HasPrefix(endpoint, "tls://")) {
|
||||
return false
|
||||
}
|
||||
nc, err := nats.Connect(endpoint, nats.Name("nyx-harness"), nats.Timeout(2*time.Second))
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "NYX_NATS_CLIENT_FALLBACK: %v\n", err)
|
||||
return false
|
||||
}
|
||||
defer nc.Close()
|
||||
done := make(chan struct{}, 1)
|
||||
sub, err := nc.Subscribe(subject, func(msg *nats.Msg) {
|
||||
natsMsg := &NyxNatsMsg{Subject: msg.Subject, Data: msg.Data, Reply: msg.Reply}
|
||||
nyxRecordBrokerEvent("NYX_NATS_LOG", "deliver", subject, string(msg.Data))
|
||||
dispatcher(natsMsg)
|
||||
nyxRecordBrokerEvent("NYX_NATS_LOG", "ack", subject, msg.Subject)
|
||||
select {
|
||||
case done <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "NYX_NATS_CLIENT_FALLBACK: %v\n", err)
|
||||
return false
|
||||
}
|
||||
defer sub.Unsubscribe()
|
||||
if err := nc.FlushTimeout(2 * time.Second); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "NYX_NATS_CLIENT_FALLBACK: %v\n", err)
|
||||
return false
|
||||
}
|
||||
fmt.Println(marker + " " + subject)
|
||||
if err := nc.Publish(subject, []byte(payload)); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "NYX_NATS_CLIENT_FALLBACK: %v\n", err)
|
||||
return false
|
||||
}
|
||||
if err := nc.FlushTimeout(2 * time.Second); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "NYX_NATS_CLIENT_FALLBACK: %v\n", err)
|
||||
return false
|
||||
}
|
||||
select {
|
||||
case <-done:
|
||||
return true
|
||||
case <-time.After(2 * time.Second):
|
||||
fmt.Fprintln(os.Stderr, "NYX_NATS_CLIENT_FALLBACK: timeout waiting for delivery")
|
||||
return false
|
||||
}
|
||||
}
|
||||
"##,
|
||||
format!(
|
||||
r##" if msg, ok := nyxFetchHttpBroker("NYX_NATS_ENDPOINT", "subjects", "{queue}", payload, "{publish_marker}"); ok {{
|
||||
r##" if nyxTryRealNats("{queue}", payload, nyxDispatch, "{publish_marker}") {{
|
||||
return
|
||||
}} else if msg, ok := nyxFetchHttpBroker("NYX_NATS_ENDPOINT", "subjects", "{queue}", payload, "{publish_marker}"); ok {{
|
||||
data := msg["data"]
|
||||
natsMsg := &NyxNatsMsg{{Subject: msg["subject"], Data: []byte(data), Reply: msg["reply"]}}
|
||||
if natsMsg.Subject == "" {{
|
||||
|
|
@ -2192,6 +2245,8 @@ fn emit_message_handler_harness(spec: &HarnessSpec, queue: &str) -> HarnessSourc
|
|||
GoBroker::Pubsub => (
|
||||
crate::dynamic::stubs::pubsub_source(crate::symbol::Lang::Go),
|
||||
crate::dynamic::stubs::PUBSUB_PUBLISH_MARKER,
|
||||
"",
|
||||
"",
|
||||
format!(
|
||||
r##" if msg, ok := nyxFetchHttpBroker("NYX_PUBSUB_ENDPOINT", "topics", "{queue}", payload, "{publish_marker}"); ok {{
|
||||
data := msg["data"]
|
||||
|
|
@ -2281,7 +2336,7 @@ import (
|
|||
"syscall"
|
||||
"time"
|
||||
|
||||
"nyx-harness/entry"
|
||||
{broker_imports} "nyx-harness/entry"
|
||||
)
|
||||
|
||||
{shim}
|
||||
|
|
@ -2290,6 +2345,8 @@ import (
|
|||
|
||||
{dispatch_inner}
|
||||
|
||||
{broker_helpers}
|
||||
|
||||
func nyxPayload() string {{
|
||||
if v := os.Getenv("NYX_PAYLOAD"); v != "" {{
|
||||
return v
|
||||
|
|
@ -2403,6 +2460,8 @@ func main() {{
|
|||
}}
|
||||
"##,
|
||||
broker_src = broker_src,
|
||||
broker_imports = broker_imports,
|
||||
broker_helpers = broker_helpers,
|
||||
dispatch_inner = dispatch_inner,
|
||||
dispatch = dispatch,
|
||||
handler = handler,
|
||||
|
|
|
|||
|
|
@ -1495,7 +1495,8 @@ fn emit_migration(spec: &HarnessSpec, version: Option<&str>) -> HarnessSource {
|
|||
r#"# Shape: migration — Phase 21 / Track M.3.
|
||||
print("__NYX_MIGRATION__: " + {version:?}, flush=True)
|
||||
_h = getattr(_entry_mod, {handler:?}, None)
|
||||
if _h is None:
|
||||
_migration_cls = getattr(_entry_mod, "Migration", None)
|
||||
if _h is None and _migration_cls is None:
|
||||
print("NYX_HANDLER_NOT_FOUND: " + {handler:?}, file=sys.stderr, flush=True)
|
||||
sys.exit(78)
|
||||
|
||||
|
|
@ -1504,6 +1505,7 @@ def _nyx_migration_sql_record(sql, driver):
|
|||
upper = text.upper()
|
||||
if not any(k in upper for k in ("SELECT", "INSERT", "UPDATE", "DELETE", "CREATE", "ALTER", "DROP")):
|
||||
return
|
||||
print("NYX_MIGRATION_SQL: " + text, flush=True)
|
||||
__nyx_stub_sql_record(text, driver=driver, source="migration")
|
||||
endpoint = os.environ.get("NYX_SQL_ENDPOINT", "")
|
||||
if endpoint:
|
||||
|
|
@ -1521,11 +1523,38 @@ def _nyx_migration_sql_record(sql, driver):
|
|||
class _NyxMigrationOpProxy:
|
||||
def __init__(self, inner=None):
|
||||
self._inner = inner
|
||||
def _call_inner(self, name, *args, **kwargs):
|
||||
if self._inner is not None and self._inner is not self and hasattr(self._inner, name):
|
||||
return getattr(self._inner, name)(*args, **kwargs)
|
||||
return None
|
||||
def execute(self, sql, *args, **kwargs):
|
||||
_nyx_migration_sql_record(sql, "alembic")
|
||||
if self._inner is not None and self._inner is not self and hasattr(self._inner, "execute"):
|
||||
return self._inner.execute(sql, *args, **kwargs)
|
||||
return None
|
||||
return self._call_inner("execute", sql, *args, **kwargs)
|
||||
def create_table(self, name, *args, **kwargs):
|
||||
_nyx_migration_sql_record("CREATE TABLE " + str(name) + " (id INTEGER)", "alembic")
|
||||
return self._call_inner("create_table", name, *args, **kwargs)
|
||||
def drop_table(self, name, *args, **kwargs):
|
||||
_nyx_migration_sql_record("DROP TABLE " + str(name), "alembic")
|
||||
return self._call_inner("drop_table", name, *args, **kwargs)
|
||||
def add_column(self, table_name, column, *args, **kwargs):
|
||||
col_name = getattr(column, "name", column)
|
||||
_nyx_migration_sql_record(
|
||||
"ALTER TABLE " + str(table_name) + " ADD COLUMN " + str(col_name) + " TEXT",
|
||||
"alembic",
|
||||
)
|
||||
return self._call_inner("add_column", table_name, column, *args, **kwargs)
|
||||
def drop_column(self, table_name, column_name, *args, **kwargs):
|
||||
_nyx_migration_sql_record(
|
||||
"ALTER TABLE " + str(table_name) + " DROP COLUMN " + str(column_name),
|
||||
"alembic",
|
||||
)
|
||||
return self._call_inner("drop_column", table_name, column_name, *args, **kwargs)
|
||||
def alter_column(self, table_name, column_name, *args, **kwargs):
|
||||
_nyx_migration_sql_record(
|
||||
"ALTER TABLE " + str(table_name) + " ALTER COLUMN " + str(column_name),
|
||||
"alembic",
|
||||
)
|
||||
return self._call_inner("alter_column", table_name, column_name, *args, **kwargs)
|
||||
def __getattr__(self, name):
|
||||
if self._inner is not None and self._inner is not self:
|
||||
return getattr(self._inner, name)
|
||||
|
|
@ -1572,9 +1601,7 @@ def _nyx_record_migration_result(result):
|
|||
elif isinstance(result, str):
|
||||
_nyx_migration_sql_record(result, "migration")
|
||||
elif hasattr(result, "database_forwards"):
|
||||
sql = getattr(result, "sql", None)
|
||||
if sql is not None:
|
||||
_nyx_migration_sql_record(sql, "django")
|
||||
_nyx_record_django_operation_shape(result)
|
||||
try:
|
||||
from django.conf import settings
|
||||
if not settings.configured:
|
||||
|
|
@ -1592,21 +1619,59 @@ def _nyx_record_migration_result(result):
|
|||
except Exception:
|
||||
pass
|
||||
|
||||
def _nyx_record_django_operation_shape(op):
|
||||
sql = getattr(op, "sql", None)
|
||||
if sql is not None:
|
||||
_nyx_migration_sql_record(sql, "django")
|
||||
return
|
||||
name = op.__class__.__name__
|
||||
if name == "CreateModel":
|
||||
model = getattr(op, "name", "nyx_model")
|
||||
_nyx_migration_sql_record("CREATE TABLE " + str(model) + " (id INTEGER)", "django")
|
||||
elif name == "DeleteModel":
|
||||
model = getattr(op, "name", "nyx_model")
|
||||
_nyx_migration_sql_record("DROP TABLE " + str(model), "django")
|
||||
elif name in ("AddField", "RemoveField", "AlterField"):
|
||||
model = getattr(op, "model_name", "nyx_model")
|
||||
field = getattr(op, "name", "nyx_field")
|
||||
verb = "ADD COLUMN" if name == "AddField" else ("DROP COLUMN" if name == "RemoveField" else "ALTER COLUMN")
|
||||
_nyx_migration_sql_record("ALTER TABLE " + str(model) + " " + verb + " " + str(field), "django")
|
||||
|
||||
def _nyx_run_django_migration_operations(cls):
|
||||
if cls is None:
|
||||
return False
|
||||
operations = getattr(cls, "operations", None)
|
||||
if operations is None:
|
||||
try:
|
||||
operations = cls().operations
|
||||
except Exception:
|
||||
operations = None
|
||||
if not operations:
|
||||
return False
|
||||
for op in list(operations):
|
||||
_nyx_record_migration_result(op)
|
||||
return True
|
||||
|
||||
try:
|
||||
_nyx_install_migration_sql_hooks()
|
||||
# Migrations conventionally take no arguments; pass payload if the
|
||||
# function declares positional params (best-effort introspection).
|
||||
import inspect
|
||||
sig = None
|
||||
try:
|
||||
sig = inspect.signature(_h)
|
||||
except (TypeError, ValueError):
|
||||
sig = None
|
||||
if sig is not None and len(sig.parameters) >= 1:
|
||||
_result = _h(payload)
|
||||
_result = None
|
||||
if _h is _migration_cls or ({handler:?} == "Migration" and _migration_cls is not None):
|
||||
_nyx_run_django_migration_operations(_migration_cls)
|
||||
else:
|
||||
_result = _h()
|
||||
_nyx_record_migration_result(_result)
|
||||
# Migrations conventionally take no arguments; pass payload if the
|
||||
# function declares positional params (best-effort introspection).
|
||||
import inspect
|
||||
sig = None
|
||||
try:
|
||||
sig = inspect.signature(_h)
|
||||
except (TypeError, ValueError):
|
||||
sig = None
|
||||
if sig is not None and len(sig.parameters) >= 1:
|
||||
_result = _h(payload)
|
||||
else:
|
||||
_result = _h()
|
||||
_nyx_record_migration_result(_result)
|
||||
_nyx_run_django_migration_operations(_migration_cls)
|
||||
if _result is not None:
|
||||
try:
|
||||
print(str(_result), flush=True)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue