mirror of
https://github.com/elicpeter/nyx.git
synced 2026-06-09 19:45:13 +02:00
**refactor(dynamic): introduce framework-specific fallback logic for Quartz, Spring, Celery, Django, Express, and Socket.IO, enhance middleware/request handling and extend test coverage**
This commit is contained in:
parent
a12f7efc3a
commit
8eeb9590b4
6 changed files with 554 additions and 54 deletions
|
|
@ -4457,6 +4457,9 @@ public class NyxHarness {{
|
|||
System.out.println("__NYX_SINK_HIT__");
|
||||
try {{
|
||||
Class<?> cls = Class.forName({entry_class:?});
|
||||
if (nyxTryQuartz(cls, payload)) {{
|
||||
return;
|
||||
}}
|
||||
Constructor<?> ctor = cls.getDeclaredConstructor();
|
||||
ctor.setAccessible(true);
|
||||
Object instance = ctor.newInstance();
|
||||
|
|
@ -4469,6 +4472,9 @@ public class NyxHarness {{
|
|||
System.exit(78);
|
||||
}}
|
||||
m.setAccessible(true);
|
||||
if (nyxTrySpringHandlerInterceptor(instance, m, payload)) {{
|
||||
return;
|
||||
}}
|
||||
Class<?>[] params = m.getParameterTypes();
|
||||
Object[] mArgs = new Object[params.length];
|
||||
for (int i = 0; i < params.length; i++) {{
|
||||
|
|
@ -4493,6 +4499,50 @@ public class NyxHarness {{
|
|||
}}
|
||||
return "";
|
||||
}}
|
||||
|
||||
static boolean nyxTryQuartz(Class<?> cls, String payload) {{
|
||||
try {{
|
||||
Class<?> jobClass = Class.forName("org.quartz.Job");
|
||||
if (!jobClass.isAssignableFrom(cls)) {{
|
||||
return false;
|
||||
}}
|
||||
System.setProperty("org.quartz.scheduler.skipUpdateCheck", "true");
|
||||
System.setProperty("org.quartz.threadPool.threadCount", "1");
|
||||
|
||||
Class<?> jobBuilderClass = Class.forName("org.quartz.JobBuilder");
|
||||
Object jobBuilder = jobBuilderClass.getMethod("newJob", Class.class)
|
||||
.invoke(null, cls.asSubclass(jobClass));
|
||||
jobBuilder = jobBuilder.getClass().getMethod("withIdentity", String.class)
|
||||
.invoke(jobBuilder, "nyx-job");
|
||||
jobBuilder = jobBuilder.getClass().getMethod("usingJobData", String.class, String.class)
|
||||
.invoke(jobBuilder, "payload", payload);
|
||||
Object jobDetail = jobBuilder.getClass().getMethod("build").invoke(jobBuilder);
|
||||
|
||||
Class<?> triggerBuilderClass = Class.forName("org.quartz.TriggerBuilder");
|
||||
Object triggerBuilder = triggerBuilderClass.getMethod("newTrigger").invoke(null);
|
||||
triggerBuilder = triggerBuilder.getClass().getMethod("withIdentity", String.class)
|
||||
.invoke(triggerBuilder, "nyx-trigger");
|
||||
triggerBuilder = triggerBuilder.getClass().getMethod("startNow").invoke(triggerBuilder);
|
||||
Object trigger = triggerBuilder.getClass().getMethod("build").invoke(triggerBuilder);
|
||||
|
||||
Object scheduler = Class.forName("org.quartz.impl.StdSchedulerFactory")
|
||||
.getMethod("getDefaultScheduler")
|
||||
.invoke(null);
|
||||
Class<?> schedulerClass = Class.forName("org.quartz.Scheduler");
|
||||
Class<?> jobDetailClass = Class.forName("org.quartz.JobDetail");
|
||||
Class<?> triggerClass = Class.forName("org.quartz.Trigger");
|
||||
schedulerClass.getMethod("start").invoke(scheduler);
|
||||
schedulerClass.getMethod("scheduleJob", jobDetailClass, triggerClass)
|
||||
.invoke(scheduler, jobDetail, trigger);
|
||||
schedulerClass.getMethod("shutdown", boolean.class).invoke(scheduler, true);
|
||||
return true;
|
||||
}} catch (ClassNotFoundException missingQuartz) {{
|
||||
return false;
|
||||
}} catch (Throwable e) {{
|
||||
System.err.println("NYX_QUARTZ_FALLBACK: " + e.getClass().getName() + ": " + e.getMessage());
|
||||
return false;
|
||||
}}
|
||||
}}
|
||||
}}
|
||||
"#,
|
||||
entry_class = entry_class,
|
||||
|
|
@ -4506,7 +4556,7 @@ public class NyxHarness {{
|
|||
command: vec![
|
||||
"java".to_owned(),
|
||||
"-cp".to_owned(),
|
||||
".".to_owned(),
|
||||
".:lib/*".to_owned(),
|
||||
"NyxHarness".to_owned(),
|
||||
],
|
||||
extra_files: framework_dependency_files(spec),
|
||||
|
|
@ -4569,6 +4619,69 @@ public class NyxHarness {{
|
|||
}}
|
||||
return "";
|
||||
}}
|
||||
|
||||
static boolean nyxTrySpringHandlerInterceptor(Object instance, Method m, String payload) {{
|
||||
Class<?>[] params = m.getParameterTypes();
|
||||
if (params.length < 3 || !m.getName().equals("preHandle")) {{
|
||||
return false;
|
||||
}}
|
||||
try {{
|
||||
Object[] args = new Object[params.length];
|
||||
for (int i = 0; i < params.length; i++) {{
|
||||
String name = params[i].getName();
|
||||
if (name.endsWith("HttpServletRequest")) {{
|
||||
args[i] = nyxServletProxy(params[i], payload);
|
||||
}} else if (name.endsWith("HttpServletResponse")) {{
|
||||
args[i] = nyxServletProxy(params[i], payload);
|
||||
}} else if (params[i].equals(String.class)) {{
|
||||
args[i] = payload;
|
||||
}} else {{
|
||||
args[i] = new Object();
|
||||
}}
|
||||
}}
|
||||
m.invoke(instance, args);
|
||||
return true;
|
||||
}} catch (InvocationTargetException ite) {{
|
||||
Throwable cause = ite.getCause() == null ? ite : ite.getCause();
|
||||
System.err.println("NYX_SPRING_INTERCEPTOR_FALLBACK: " + cause.getClass().getName() + ": " + cause.getMessage());
|
||||
return false;
|
||||
}} catch (Throwable e) {{
|
||||
System.err.println("NYX_SPRING_INTERCEPTOR_FALLBACK: " + e.getClass().getName() + ": " + e.getMessage());
|
||||
return false;
|
||||
}}
|
||||
}}
|
||||
|
||||
static Object nyxServletProxy(Class<?> iface, String payload) {{
|
||||
if (!iface.isInterface()) {{
|
||||
return null;
|
||||
}}
|
||||
return java.lang.reflect.Proxy.newProxyInstance(
|
||||
iface.getClassLoader(),
|
||||
new Class<?>[] {{ iface }},
|
||||
(proxy, method, args) -> {{
|
||||
String name = method.getName();
|
||||
if (name.equals("getParameter")) return payload;
|
||||
if (name.equals("getQueryString")) return "q=" + java.net.URLEncoder.encode(payload, java.nio.charset.StandardCharsets.UTF_8);
|
||||
if (name.equals("getRequestURI")) return "/nyx";
|
||||
if (name.equals("getRequestURL")) return new StringBuffer("http://localhost/nyx");
|
||||
if (name.equals("getMethod")) return "POST";
|
||||
if (name.equals("getHeader")) return null;
|
||||
if (name.equals("getWriter")) return new java.io.PrintWriter(System.out, true);
|
||||
if (name.equals("toString")) return "NyxServletProxy(" + iface.getName() + ")";
|
||||
Class<?> ret = method.getReturnType();
|
||||
if (!ret.isPrimitive()) return null;
|
||||
if (ret.equals(boolean.class)) return false;
|
||||
if (ret.equals(byte.class)) return (byte) 0;
|
||||
if (ret.equals(short.class)) return (short) 0;
|
||||
if (ret.equals(int.class)) return 0;
|
||||
if (ret.equals(long.class)) return 0L;
|
||||
if (ret.equals(float.class)) return 0.0f;
|
||||
if (ret.equals(double.class)) return 0.0d;
|
||||
if (ret.equals(char.class)) return '\0';
|
||||
return null;
|
||||
}}
|
||||
);
|
||||
}}
|
||||
}}
|
||||
"#,
|
||||
entry_class = entry_class,
|
||||
|
|
@ -4582,7 +4695,7 @@ public class NyxHarness {{
|
|||
command: vec![
|
||||
"java".to_owned(),
|
||||
"-cp".to_owned(),
|
||||
".".to_owned(),
|
||||
".:lib/*".to_owned(),
|
||||
"NyxHarness".to_owned(),
|
||||
],
|
||||
extra_files: framework_dependency_files(spec),
|
||||
|
|
|
|||
|
|
@ -1095,8 +1095,39 @@ if (_h == null) {{
|
|||
process.stderr.write('NYX_HANDLER_NOT_FOUND: ' + {handler:?} + '\n');
|
||||
process.exit(78);
|
||||
}}
|
||||
async function _nyxTryNodeCron(schedule, handler) {{
|
||||
let cron;
|
||||
try {{
|
||||
cron = require('node-cron');
|
||||
}} catch (_) {{
|
||||
return false;
|
||||
}}
|
||||
try {{
|
||||
if (typeof cron.validate === 'function' && !cron.validate(schedule)) return false;
|
||||
let ran = false;
|
||||
let task = cron.schedule(schedule, async function () {{
|
||||
ran = true;
|
||||
const value = await Promise.resolve(handler(payload));
|
||||
if (value != null) process.stdout.write(String(value) + '\n');
|
||||
return value;
|
||||
}}, {{ scheduled: false }});
|
||||
if (task && typeof task.execute === 'function') {{
|
||||
await Promise.resolve(task.execute());
|
||||
if (task && typeof task.stop === 'function') task.stop();
|
||||
if (task && typeof task.destroy === 'function') task.destroy();
|
||||
return ran;
|
||||
}}
|
||||
if (task && typeof task.stop === 'function') task.stop();
|
||||
if (task && typeof task.destroy === 'function') task.destroy();
|
||||
return false;
|
||||
}} catch (e) {{
|
||||
process.stderr.write('NYX_NODE_CRON_FALLBACK: ' + (e && e.message ? e.message : String(e)) + '\n');
|
||||
return false;
|
||||
}}
|
||||
}}
|
||||
(async () => {{
|
||||
try {{
|
||||
if (await _nyxTryNodeCron({schedule:?}, _h)) return;
|
||||
const _result = await Promise.resolve(_h(payload));
|
||||
if (_result != null) process.stdout.write(String(_result) + '\n');
|
||||
}} catch (e) {{
|
||||
|
|
@ -1134,8 +1165,48 @@ if (_h == null) {{
|
|||
process.stderr.write('NYX_RESOLVER_NOT_FOUND: ' + {handler:?} + '\n');
|
||||
process.exit(78);
|
||||
}}
|
||||
async function _nyxTryGraphqlJs(typeName, fieldName, resolver) {{
|
||||
let graphql;
|
||||
let buildSchema;
|
||||
try {{
|
||||
const gql = require('graphql');
|
||||
graphql = gql.graphql;
|
||||
buildSchema = gql.buildSchema;
|
||||
}} catch (_) {{
|
||||
return false;
|
||||
}}
|
||||
const safeField = /^[A-Za-z_][A-Za-z0-9_]*$/.test(fieldName) ? fieldName : 'nyxField';
|
||||
try {{
|
||||
const schema = buildSchema('type Query {{ ' + safeField + '(id: String, input: String): String }}');
|
||||
const rootValue = {{}};
|
||||
rootValue[safeField] = async function (args, context, info) {{
|
||||
const value = await Promise.resolve(resolver(
|
||||
null,
|
||||
{{ id: payload, input: payload, value: payload }},
|
||||
context || {{}},
|
||||
info || {{ fieldName: safeField, parentType: typeName }}
|
||||
));
|
||||
return value == null ? null : String(value);
|
||||
}};
|
||||
const result = await graphql({{
|
||||
schema,
|
||||
source: 'query($value: String) {{ ' + safeField + '(id: $value) }}',
|
||||
rootValue,
|
||||
variableValues: {{ value: payload }},
|
||||
}});
|
||||
if (result.errors && result.errors.length) return false;
|
||||
if (result.data && result.data[safeField] != null) {{
|
||||
process.stdout.write(String(result.data[safeField]) + '\n');
|
||||
}}
|
||||
return true;
|
||||
}} catch (e) {{
|
||||
process.stderr.write('NYX_GRAPHQL_JS_FALLBACK: ' + (e && e.message ? e.message : String(e)) + '\n');
|
||||
return false;
|
||||
}}
|
||||
}}
|
||||
(async () => {{
|
||||
try {{
|
||||
if (await _nyxTryGraphqlJs({type_name:?}, {field:?}, _h)) return;
|
||||
// Apollo resolver shape: (parent, args, context, info).
|
||||
const _info = {{ fieldName: {field:?}, parentType: {type_name:?} }};
|
||||
const _result = await Promise.resolve(_h(null, {{ id: payload, input: payload }}, {{}}, _info));
|
||||
|
|
@ -1171,8 +1242,48 @@ if (_h == null) {{
|
|||
process.stderr.write('NYX_HANDLER_NOT_FOUND: ' + {handler:?} + '\n');
|
||||
process.exit(78);
|
||||
}}
|
||||
async function _nyxTryWs(handler) {{
|
||||
let Ws;
|
||||
try {{
|
||||
Ws = require('ws');
|
||||
}} catch (_) {{
|
||||
return false;
|
||||
}}
|
||||
try {{
|
||||
const events = require('events');
|
||||
const Server = Ws.WebSocketServer || Ws.Server;
|
||||
if (!Server) return false;
|
||||
const wss = new Server({{ noServer: true }});
|
||||
const pending = [];
|
||||
let delivered = false;
|
||||
wss.on('connection', (socket) => {{
|
||||
socket.on('message', (data, isBinary) => {{
|
||||
delivered = true;
|
||||
pending.push(Promise.resolve(handler(data, isBinary)).then((result) => {{
|
||||
if (result != null) process.stdout.write(String(result) + '\n');
|
||||
}}));
|
||||
}});
|
||||
}});
|
||||
const socket = new events.EventEmitter();
|
||||
socket.readyState = Ws.OPEN || 1;
|
||||
socket.send = (data) => {{
|
||||
if (data != null) process.stdout.write(String(data) + '\n');
|
||||
}};
|
||||
socket.close = () => {{}};
|
||||
wss.emit('connection', socket, {{ url: {path:?}, headers: {{}} }});
|
||||
socket.emit('message', Buffer.from(String(payload), 'utf8'), false);
|
||||
await Promise.all(pending);
|
||||
if (typeof wss.close === 'function') wss.close();
|
||||
return delivered;
|
||||
}} catch (_) {{
|
||||
return false;
|
||||
}}
|
||||
}}
|
||||
(async () => {{
|
||||
try {{
|
||||
if (await _nyxTryWs(_h)) {{
|
||||
return;
|
||||
}}
|
||||
// ws library: handler(message); socket.io: handler(socket, data).
|
||||
let _result;
|
||||
try {{
|
||||
|
|
@ -1217,8 +1328,62 @@ if (_h == null) {{
|
|||
}}
|
||||
const _req = {{ body: payload, query: {{ q: payload }}, params: {{ id: payload }}, headers: {{}}, method: 'POST', url: '/nyx' }};
|
||||
const _res = {{ statusCode: 200, headers: {{}}, end: function(d){{ if (d != null) process.stdout.write(String(d) + '\n'); }}, setHeader: function(k, v){{ this.headers[k] = v; }} }};
|
||||
async function _nyxTryExpressMiddleware(handler) {{
|
||||
let express;
|
||||
try {{
|
||||
express = require('express');
|
||||
}} catch (_) {{
|
||||
return false;
|
||||
}}
|
||||
try {{
|
||||
const stream = require('stream');
|
||||
const app = express();
|
||||
app.use(express.text({{ type: '*/*' }}));
|
||||
app.use(handler);
|
||||
const req = new stream.Readable({{
|
||||
read() {{
|
||||
this.push(Buffer.from(String(payload), 'utf8'));
|
||||
this.push(null);
|
||||
}},
|
||||
}});
|
||||
req.method = 'POST';
|
||||
req.url = '/nyx?q=' + encodeURIComponent(String(payload));
|
||||
req.headers = {{
|
||||
host: 'localhost',
|
||||
'content-type': 'text/plain',
|
||||
'content-length': Buffer.byteLength(String(payload), 'utf8'),
|
||||
}};
|
||||
const chunks = [];
|
||||
await new Promise((resolve) => {{
|
||||
const res = new stream.Writable({{
|
||||
write(chunk, _enc, cb) {{
|
||||
chunks.push(Buffer.from(chunk));
|
||||
cb();
|
||||
}},
|
||||
}});
|
||||
res.statusCode = 200;
|
||||
res.headers = {{}};
|
||||
res.setHeader = function (k, v) {{ this.headers[String(k).toLowerCase()] = v; }};
|
||||
res.getHeader = function (k) {{ return this.headers[String(k).toLowerCase()]; }};
|
||||
res.end = function (chunk) {{
|
||||
if (chunk != null) chunks.push(Buffer.from(String(chunk)));
|
||||
resolve();
|
||||
}};
|
||||
app.handle(req, res, function (err) {{
|
||||
if (err) process.stderr.write('NYX_EXPRESS_NEXT_ERR: ' + err + '\n');
|
||||
resolve();
|
||||
}});
|
||||
}});
|
||||
if (chunks.length > 0) process.stdout.write(Buffer.concat(chunks).toString('utf8') + '\n');
|
||||
return true;
|
||||
}} catch (e) {{
|
||||
process.stderr.write('NYX_EXPRESS_MIDDLEWARE_FALLBACK: ' + (e && e.message ? e.message : String(e)) + '\n');
|
||||
return false;
|
||||
}}
|
||||
}}
|
||||
(async () => {{
|
||||
try {{
|
||||
if (await _nyxTryExpressMiddleware(_h)) return;
|
||||
const _result = await Promise.resolve(_h(_req, _res, function(_e){{ if (_e) process.stderr.write('NYX_NEXT_ERR: ' + _e + '\n'); }}));
|
||||
if (_result != null) process.stdout.write(String(_result) + '\n');
|
||||
}} catch (e) {{
|
||||
|
|
|
|||
|
|
@ -3116,11 +3116,23 @@ fn emit_middleware_harness(spec: &HarnessSpec, name: &str) -> HarnessSource {
|
|||
r#"{preamble}
|
||||
echo "__NYX_MIDDLEWARE__: " . {name:?} . "\n";
|
||||
|
||||
$req = new stdClass();
|
||||
$req->body = $payload;
|
||||
$req->path = '/nyx';
|
||||
$req->method = 'POST';
|
||||
$req->query = [ 'q' => $payload ];
|
||||
function __nyx_make_middleware_request(string $payload) {{
|
||||
if (class_exists('Illuminate\\Http\\Request')) {{
|
||||
try {{
|
||||
return Illuminate\Http\Request::create('/nyx', 'POST', ['q' => $payload], [], [], [], $payload);
|
||||
}} catch (Throwable $e) {{
|
||||
fwrite(STDERR, 'NYX_LARAVEL_REQUEST_FALLBACK: ' . get_class($e) . ': ' . $e->getMessage() . "\n");
|
||||
}}
|
||||
}}
|
||||
$req = new stdClass();
|
||||
$req->body = $payload;
|
||||
$req->path = '/nyx';
|
||||
$req->method = 'POST';
|
||||
$req->query = [ 'q' => $payload ];
|
||||
return $req;
|
||||
}}
|
||||
|
||||
$req = __nyx_make_middleware_request($payload);
|
||||
$next = function ($r) {{ return $r; }};
|
||||
|
||||
if (class_exists({handler:?})) {{
|
||||
|
|
|
|||
|
|
@ -1299,13 +1299,40 @@ _h = getattr(_entry_mod, {handler:?}, None)
|
|||
if _h is None:
|
||||
print("NYX_HANDLER_NOT_FOUND: " + {handler:?}, file=sys.stderr, flush=True)
|
||||
sys.exit(78)
|
||||
def _nyx_try_celery_eager(task, body):
|
||||
try:
|
||||
import celery # noqa: F401
|
||||
except Exception:
|
||||
return False
|
||||
if not hasattr(task, "apply"):
|
||||
return False
|
||||
try:
|
||||
app = getattr(task, "app", None)
|
||||
if app is not None and hasattr(app, "conf"):
|
||||
try:
|
||||
app.conf.task_always_eager = True
|
||||
app.conf.task_eager_propagates = False
|
||||
except Exception:
|
||||
pass
|
||||
_result = task.apply(args=(body,), throw=False)
|
||||
_value = getattr(_result, "result", None)
|
||||
if _value is not None:
|
||||
print(str(_value), flush=True)
|
||||
return True
|
||||
except SystemExit:
|
||||
raise
|
||||
except Exception as _e:
|
||||
print(f"NYX_CELERY_EAGER_FALLBACK: {{type(_e).__name__}}: {{_e}}", file=sys.stderr, flush=True)
|
||||
return False
|
||||
|
||||
try:
|
||||
_result = _h(payload)
|
||||
if _result is not None:
|
||||
try:
|
||||
print(str(_result), flush=True)
|
||||
except Exception:
|
||||
pass
|
||||
if not _nyx_try_celery_eager(_h, payload):
|
||||
_result = _h(payload)
|
||||
if _result is not None:
|
||||
try:
|
||||
print(str(_result), flush=True)
|
||||
except Exception:
|
||||
pass
|
||||
except SystemExit as _e:
|
||||
sys.exit(_e.code)
|
||||
except Exception as _e:
|
||||
|
|
@ -1345,16 +1372,61 @@ if _resolver is None:
|
|||
print("NYX_RESOLVER_NOT_FOUND: " + {handler:?}, file=sys.stderr, flush=True)
|
||||
sys.exit(78)
|
||||
try:
|
||||
# Graphene resolvers are `resolve_field(self, info, **args)`; we
|
||||
# synthesise `self = None`, `info = _NyxGraphQLInfo`, and pass the
|
||||
# payload positionally so a `def resolve_foo(self, info, id):` shape
|
||||
# binds `id = payload`.
|
||||
_result = _resolver(None, _NyxGraphQLInfo({field:?}), payload)
|
||||
if _result is not None:
|
||||
def _nyx_try_graphene(resolver, type_name, field_name, body):
|
||||
try:
|
||||
print(str(_result), flush=True)
|
||||
import graphene
|
||||
except Exception:
|
||||
pass
|
||||
return False
|
||||
safe_field = "".join(ch if (ch.isalnum() or ch == "_") else "_" for ch in str(field_name))
|
||||
if not safe_field or safe_field[0].isdigit():
|
||||
safe_field = "nyx_" + safe_field
|
||||
resolver_name = "resolve_" + safe_field
|
||||
try:
|
||||
def _nyx_resolve(root, info, id=None, input=None, **kwargs):
|
||||
arg = id if id is not None else (input if input is not None else body)
|
||||
try:
|
||||
return resolver(root, info, arg)
|
||||
except TypeError:
|
||||
try:
|
||||
return resolver(info, arg)
|
||||
except TypeError:
|
||||
return resolver(root, info, **{{safe_field: arg}})
|
||||
|
||||
Query = type(
|
||||
"NyxDynamicQuery",
|
||||
(graphene.ObjectType,),
|
||||
{{
|
||||
safe_field: graphene.String(id=graphene.String(), input=graphene.String()),
|
||||
resolver_name: _nyx_resolve,
|
||||
}},
|
||||
)
|
||||
schema = graphene.Schema(query=Query)
|
||||
query = "query($value: String) {{ " + safe_field + "(id: $value) }}"
|
||||
result = schema.execute(query, variable_values={{"value": body}})
|
||||
if getattr(result, "errors", None):
|
||||
return False
|
||||
data = getattr(result, "data", None) or {{}}
|
||||
value = data.get(safe_field)
|
||||
if value is not None:
|
||||
print(str(value), flush=True)
|
||||
return True
|
||||
except SystemExit:
|
||||
raise
|
||||
except Exception as _e:
|
||||
print(f"NYX_GRAPHENE_FALLBACK: {{type(_e).__name__}}: {{_e}}", file=sys.stderr, flush=True)
|
||||
return False
|
||||
|
||||
if not _nyx_try_graphene(_resolver, {type_name:?}, {field:?}, payload):
|
||||
# Graphene resolvers are `resolve_field(self, info, **args)`; we
|
||||
# synthesise `self = None`, `info = _NyxGraphQLInfo`, and pass the
|
||||
# payload positionally so a `def resolve_foo(self, info, id):` shape
|
||||
# binds `id = payload`.
|
||||
_result = _resolver(None, _NyxGraphQLInfo({field:?}), payload)
|
||||
if _result is not None:
|
||||
try:
|
||||
print(str(_result), flush=True)
|
||||
except Exception:
|
||||
pass
|
||||
except SystemExit as _e:
|
||||
sys.exit(_e.code)
|
||||
except TypeError:
|
||||
|
|
@ -1396,21 +1468,40 @@ if _h is None:
|
|||
print("NYX_HANDLER_NOT_FOUND: " + {handler:?}, file=sys.stderr, flush=True)
|
||||
sys.exit(78)
|
||||
try:
|
||||
# python-socketio handlers are `def message(sid, data)`; Channels
|
||||
# consumers are `def receive(self, text_data=None, bytes_data=None)`.
|
||||
# Try (sid, payload) first, then fall back to (payload).
|
||||
try:
|
||||
_result = _h("nyx-sid", payload)
|
||||
except TypeError:
|
||||
def _nyx_try_socketio(handler_name, handler, body):
|
||||
try:
|
||||
_result = _h(payload)
|
||||
except TypeError:
|
||||
_result = _h(None, payload)
|
||||
if _result is not None:
|
||||
try:
|
||||
print(str(_result), flush=True)
|
||||
import socketio
|
||||
except Exception:
|
||||
pass
|
||||
return False
|
||||
try:
|
||||
server = socketio.Server(async_mode="threading")
|
||||
server.on(handler_name, handler=handler, namespace="/")
|
||||
result = server._trigger_event(handler_name, "/", "nyx-sid", body)
|
||||
if result is not None:
|
||||
print(str(result), flush=True)
|
||||
return True
|
||||
except SystemExit:
|
||||
raise
|
||||
except Exception as _e:
|
||||
print(f"NYX_SOCKETIO_FALLBACK: {{type(_e).__name__}}: {{_e}}", file=sys.stderr, flush=True)
|
||||
return False
|
||||
|
||||
if not _nyx_try_socketio({handler:?}, _h, payload):
|
||||
# python-socketio handlers are `def message(sid, data)`; Channels
|
||||
# consumers are `def receive(self, text_data=None, bytes_data=None)`.
|
||||
# Try (sid, payload) first, then fall back to (payload).
|
||||
try:
|
||||
_result = _h("nyx-sid", payload)
|
||||
except TypeError:
|
||||
try:
|
||||
_result = _h(payload)
|
||||
except TypeError:
|
||||
_result = _h(None, payload)
|
||||
if _result is not None:
|
||||
try:
|
||||
print(str(_result), flush=True)
|
||||
except Exception:
|
||||
pass
|
||||
except SystemExit as _e:
|
||||
sys.exit(_e.code)
|
||||
except Exception as _e:
|
||||
|
|
@ -1454,19 +1545,63 @@ if _h is None:
|
|||
print("NYX_HANDLER_NOT_FOUND: " + {handler:?}, file=sys.stderr, flush=True)
|
||||
sys.exit(78)
|
||||
try:
|
||||
_req = _NyxRequest(payload)
|
||||
# Try class-shaped middleware (instantiate with a get_response stub).
|
||||
try:
|
||||
_mw = _h(lambda r: r)
|
||||
_result = _mw(_req)
|
||||
except TypeError:
|
||||
# Method on an existing class instance.
|
||||
_result = _h(_req)
|
||||
if _result is not None:
|
||||
def _nyx_try_django_middleware(factory, body):
|
||||
try:
|
||||
print(str(_result), flush=True)
|
||||
from django.conf import settings
|
||||
if not settings.configured:
|
||||
settings.configure(
|
||||
DEFAULT_CHARSET="utf-8",
|
||||
SECRET_KEY="nyx-dynamic-harness",
|
||||
ROOT_URLCONF=__name__,
|
||||
ALLOWED_HOSTS=["testserver", "localhost", "127.0.0.1"],
|
||||
INSTALLED_APPS=[],
|
||||
MIDDLEWARE=[],
|
||||
)
|
||||
import django
|
||||
django.setup()
|
||||
from django.test import RequestFactory
|
||||
except Exception:
|
||||
pass
|
||||
return False
|
||||
try:
|
||||
request = RequestFactory().post("/nyx", data={{"q": body}})
|
||||
request._body = str(body).encode("utf-8", "replace")
|
||||
try:
|
||||
mw = factory(lambda req: req)
|
||||
result = mw(request)
|
||||
except TypeError:
|
||||
try:
|
||||
instance = factory()
|
||||
if hasattr(instance, "process_request"):
|
||||
result = instance.process_request(request)
|
||||
elif callable(instance):
|
||||
result = instance(request)
|
||||
else:
|
||||
return False
|
||||
except TypeError:
|
||||
result = factory(request)
|
||||
if result is not None:
|
||||
print(str(result), flush=True)
|
||||
return True
|
||||
except SystemExit:
|
||||
raise
|
||||
except Exception as _e:
|
||||
print(f"NYX_DJANGO_MIDDLEWARE_FALLBACK: {{type(_e).__name__}}: {{_e}}", file=sys.stderr, flush=True)
|
||||
return False
|
||||
|
||||
if not _nyx_try_django_middleware(_h, payload):
|
||||
_req = _NyxRequest(payload)
|
||||
# Try class-shaped middleware (instantiate with a get_response stub).
|
||||
try:
|
||||
_mw = _h(lambda r: r)
|
||||
_result = _mw(_req)
|
||||
except TypeError:
|
||||
# Method on an existing class instance.
|
||||
_result = _h(_req)
|
||||
if _result is not None:
|
||||
try:
|
||||
print(str(_result), flush=True)
|
||||
except Exception:
|
||||
pass
|
||||
except SystemExit as _e:
|
||||
sys.exit(_e.code)
|
||||
except Exception as _e:
|
||||
|
|
|
|||
|
|
@ -760,7 +760,19 @@ puts "__NYX_SCHEDULED_JOB__: " + {sched:?}
|
|||
target = nil
|
||||
if Object.const_defined?({handler:?})
|
||||
begin
|
||||
target = Object.const_get({handler:?}).new
|
||||
cls = Object.const_get({handler:?})
|
||||
begin
|
||||
require 'sidekiq/testing'
|
||||
if cls.respond_to?(:perform_async)
|
||||
Sidekiq::Testing.inline! do
|
||||
cls.perform_async($nyx_payload)
|
||||
end
|
||||
exit 0
|
||||
end
|
||||
rescue LoadError, StandardError => e
|
||||
STDERR.puts("NYX_SIDEKIQ_INLINE_FALLBACK: #{{e.class.name}}: #{{e.message}}") if ENV['NYX_DEBUG']
|
||||
end
|
||||
target = cls.new
|
||||
if target.respond_to?(:perform)
|
||||
begin
|
||||
result = target.perform($nyx_payload)
|
||||
|
|
@ -859,17 +871,53 @@ puts "__NYX_MIDDLEWARE__: " + {name:?}
|
|||
|
||||
require 'stringio'
|
||||
|
||||
# Rack-shape middleware: class with #call(env).
|
||||
env = {{
|
||||
'REQUEST_METHOD' => 'POST',
|
||||
'PATH_INFO' => '/nyx',
|
||||
'QUERY_STRING' => "q=#{{$nyx_payload}}",
|
||||
'rack.input' => StringIO.new($nyx_payload),
|
||||
'nyx.payload' => $nyx_payload,
|
||||
}}
|
||||
def nyx_middleware_env
|
||||
{{
|
||||
'REQUEST_METHOD' => 'POST',
|
||||
'PATH_INFO' => '/nyx',
|
||||
'QUERY_STRING' => "q=#{{$nyx_payload}}",
|
||||
'rack.input' => StringIO.new($nyx_payload),
|
||||
'nyx.payload' => $nyx_payload,
|
||||
}}
|
||||
end
|
||||
|
||||
def nyx_try_rack_middleware(cls)
|
||||
begin
|
||||
require 'rack/mock'
|
||||
rescue LoadError
|
||||
return false
|
||||
end
|
||||
begin
|
||||
terminal = lambda {{ |_env| [200, {{ 'content-type' => 'text/plain' }}, ['ok']] }}
|
||||
middleware = begin
|
||||
cls.new(terminal)
|
||||
rescue ArgumentError
|
||||
cls.new
|
||||
end
|
||||
return false unless middleware.respond_to?(:call)
|
||||
app = lambda do |env|
|
||||
env['nyx.payload'] = $nyx_payload
|
||||
middleware.call(env)
|
||||
end
|
||||
response = Rack::MockRequest.new(app).request(
|
||||
'POST',
|
||||
'/nyx?q=' + Rack::Utils.escape($nyx_payload.to_s),
|
||||
input: $nyx_payload
|
||||
)
|
||||
print(response.body.to_s) if response && response.respond_to?(:body) && !response.body.to_s.empty?
|
||||
true
|
||||
rescue StandardError => e
|
||||
STDERR.puts("NYX_EXCEPTION: #{{e.class.name}}: #{{e.message}}")
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
env = nyx_middleware_env
|
||||
|
||||
# Rack-shape middleware: class with #call(env).
|
||||
if Object.const_defined?({handler:?})
|
||||
cls = Object.const_get({handler:?})
|
||||
exit 0 if nyx_try_rack_middleware(cls)
|
||||
begin
|
||||
inst = cls.new(lambda {{ |e| [200, {{}}, ['ok']] }})
|
||||
if inst.respond_to?(:call)
|
||||
|
|
|
|||
|
|
@ -667,6 +667,8 @@ fn scheduled_job_python_harness_carries_sentinel_and_handler() {
|
|||
assert!(h.source.contains("__NYX_SCHEDULED_JOB__"));
|
||||
assert!(h.source.contains("\"tick\""));
|
||||
assert!(h.source.contains("*/5 * * * *"));
|
||||
assert!(h.source.contains("_nyx_try_celery_eager"));
|
||||
assert!(h.source.contains("task.apply"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -682,6 +684,8 @@ fn scheduled_job_js_harness_carries_sentinel_and_handler() {
|
|||
let h = lang::emit(&spec).expect("emit ok");
|
||||
assert!(h.source.contains("__NYX_SCHEDULED_JOB__"));
|
||||
assert!(h.source.contains("\"tick\""));
|
||||
assert!(h.source.contains("_nyxTryNodeCron"));
|
||||
assert!(h.source.contains("require('node-cron')"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -695,6 +699,9 @@ fn scheduled_job_java_harness_carries_sentinel_and_handler() {
|
|||
let h = lang::emit(&spec).expect("emit ok");
|
||||
assert!(h.source.contains("__NYX_SCHEDULED_JOB__"));
|
||||
assert!(h.source.contains("\"execute\""));
|
||||
assert!(h.source.contains("nyxTryQuartz"));
|
||||
assert!(h.source.contains("org.quartz.JobBuilder"));
|
||||
assert_eq!(h.command, vec!["java", "-cp", ".:lib/*", "NyxHarness"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -708,6 +715,8 @@ fn scheduled_job_ruby_harness_carries_sentinel_and_handler() {
|
|||
let h = lang::emit(&spec).expect("emit ok");
|
||||
assert!(h.source.contains("__NYX_SCHEDULED_JOB__"));
|
||||
assert!(h.source.contains("TickWorker"));
|
||||
assert!(h.source.contains("sidekiq/testing"));
|
||||
assert!(h.source.contains("perform_async"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -725,6 +734,8 @@ fn graphql_resolver_python_harness_carries_sentinel_and_field() {
|
|||
assert!(h.source.contains("__NYX_GRAPHQL_RESOLVER__"));
|
||||
assert!(h.source.contains("\"resolve_user\""));
|
||||
assert!(h.source.contains("\"Query\""));
|
||||
assert!(h.source.contains("_nyx_try_graphene"));
|
||||
assert!(h.source.contains("graphene.Schema"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -741,6 +752,8 @@ fn graphql_resolver_js_harness_carries_sentinel_and_field() {
|
|||
let h = lang::emit(&spec).expect("emit ok");
|
||||
assert!(h.source.contains("__NYX_GRAPHQL_RESOLVER__"));
|
||||
assert!(h.source.contains("\"resolveUser\""));
|
||||
assert!(h.source.contains("_nyxTryGraphqlJs"));
|
||||
assert!(h.source.contains("require('graphql')"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -790,6 +803,8 @@ fn websocket_python_harness_carries_sentinel_and_handler() {
|
|||
assert!(h.source.contains("__NYX_WEBSOCKET__"));
|
||||
assert!(h.source.contains("\"message\""));
|
||||
assert!(h.source.contains("/ws/chat"));
|
||||
assert!(h.source.contains("_nyx_try_socketio"));
|
||||
assert!(h.source.contains("socketio.Server"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -805,6 +820,8 @@ fn websocket_js_harness_carries_sentinel_and_handler() {
|
|||
let h = lang::emit(&spec).expect("emit ok");
|
||||
assert!(h.source.contains("__NYX_WEBSOCKET__"));
|
||||
assert!(h.source.contains("\"onMessage\""));
|
||||
assert!(h.source.contains("_nyxTryWs"));
|
||||
assert!(h.source.contains("require('ws')"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -835,6 +852,8 @@ fn middleware_python_harness_carries_sentinel_and_handler() {
|
|||
let h = lang::emit(&spec).expect("emit ok");
|
||||
assert!(h.source.contains("__NYX_MIDDLEWARE__"));
|
||||
assert!(h.source.contains("\"audit\""));
|
||||
assert!(h.source.contains("_nyx_try_django_middleware"));
|
||||
assert!(h.source.contains("RequestFactory"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -850,6 +869,8 @@ fn middleware_js_harness_carries_sentinel_and_handler() {
|
|||
let h = lang::emit(&spec).expect("emit ok");
|
||||
assert!(h.source.contains("__NYX_MIDDLEWARE__"));
|
||||
assert!(h.source.contains("\"audit\""));
|
||||
assert!(h.source.contains("_nyxTryExpressMiddleware"));
|
||||
assert!(h.source.contains("require('express')"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -865,6 +886,8 @@ fn middleware_java_harness_carries_sentinel_and_handler() {
|
|||
let h = lang::emit(&spec).expect("emit ok");
|
||||
assert!(h.source.contains("__NYX_MIDDLEWARE__"));
|
||||
assert!(h.source.contains("\"preHandle\""));
|
||||
assert!(h.source.contains("nyxTrySpringHandlerInterceptor"));
|
||||
assert!(h.source.contains("HttpServletRequest"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -880,6 +903,8 @@ fn middleware_ruby_harness_carries_sentinel_and_handler() {
|
|||
let h = lang::emit(&spec).expect("emit ok");
|
||||
assert!(h.source.contains("__NYX_MIDDLEWARE__"));
|
||||
assert!(h.source.contains("AuditMiddleware"));
|
||||
assert!(h.source.contains("nyx_try_rack_middleware"));
|
||||
assert!(h.source.contains("Rack::MockRequest"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -895,6 +920,8 @@ fn middleware_php_harness_carries_sentinel_and_handler() {
|
|||
let h = lang::emit(&spec).expect("emit ok");
|
||||
assert!(h.source.contains("__NYX_MIDDLEWARE__"));
|
||||
assert!(h.source.contains("Audit"));
|
||||
assert!(h.source.contains("Illuminate\\Http\\Request"));
|
||||
assert!(h.source.contains("__nyx_make_middleware_request"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue