**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:
elipeter 2026-05-27 11:47:10 -05:00
parent a55849f1ca
commit a12f7efc3a
7 changed files with 527 additions and 28 deletions

View file

@ -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,

View file

@ -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)