Skip to content

Commit 285a66f

Browse files
fix: type dedup and substantial filters (#994)
<!-- Pull requests are squashed and merged using: - their title as the commit message - their description as the commit body Having a good title and description is important for the users to get readable changelog. --> <!-- 1. Explain WHAT the change is about --> - Fixes type dedup when using list and optional - Add `run_id` filter for substantial ([MET-870](https://linear.app/metatypedev/issue/MET-870/subs-filter-by-run-id)) <!-- 3. Explain HOW users should update their code --> #### Migration notes --- - [x] The change comes with new or modified tests - [ ] Hard-to-understand functions have explanatory comments - [ ] End-user documentation is updated to reflect the change <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Introduced two public endpoints for retrieving structured data as lists and optional objects. - Expanded filtering capabilities to support an additional identifier. - **Improvements** - Enhanced asynchronous processing to ensure operations complete reliably. - Streamlined messaging and error feedback for greater clarity. - Optimized naming consistency across system components. - Enhanced functionality of the `SpecialTerms` type for better expression evaluation. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
1 parent feb7436 commit 285a66f

File tree

6 files changed

+125
-109
lines changed

6 files changed

+125
-109
lines changed

src/typegate/src/runtimes/substantial/agent.ts

Lines changed: 17 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -100,11 +100,9 @@ export class Agent {
100100
this.workflows = workflows;
101101

102102
this.logger.warn(
103-
`Initializing agent to handle ${
104-
workflows
105-
.map(({ name }) => name)
106-
.join(", ")
107-
}`,
103+
`Initializing agent to handle ${workflows
104+
.map(({ name }) => name)
105+
.join(", ")}`,
108106
);
109107

110108
this.pollIntervalHandle = setInterval(async () => {
@@ -267,11 +265,9 @@ export class Agent {
267265
// A consequence of the above, a workflow is always triggered by gql { start(..) }
268266
// This can also occur if an event is sent from gql under a runId that is not valid (e.g. due to typo)
269267
this.logger.warn(
270-
`First item in the operation list is not a Start, got "${
271-
JSON.stringify(
272-
first,
273-
)
274-
}" instead. Closing the underlying schedule.`,
268+
`First item in the operation list is not a Start, got "${JSON.stringify(
269+
first,
270+
)}" instead. Closing the underlying schedule.`,
275271
);
276272

277273
await Meta.substantial.storeCloseSchedule(schedDef);
@@ -280,7 +276,7 @@ export class Agent {
280276

281277
const { taskContext } = first.event.kwargs as unknown as StdKwargs;
282278
try {
283-
this.workerManager
279+
await this.workerManager
284280
.triggerStart(
285281
workflow.name,
286282
next.run_id,
@@ -322,11 +318,7 @@ export class Agent {
322318
let result;
323319
let error;
324320
try {
325-
result = await hostcall(
326-
this.hostcallCtx,
327-
event.opName,
328-
event.json,
329-
);
321+
result = await hostcall(this.hostcallCtx, event.opName, event.json);
330322
} catch (err) {
331323
error = err;
332324
}
@@ -423,11 +415,9 @@ export class Agent {
423415
const result = event.type == "SUCCESS" ? event.result : event.error;
424416

425417
this.logger.info(
426-
`gracefull completion of "${runId}" (${event.type}): ${
427-
JSON.stringify(
428-
result,
429-
)
430-
} started at "${startedAt}"`,
418+
`gracefull completion of "${runId}" (${event.type}): ${JSON.stringify(
419+
result,
420+
)} started at "${startedAt}"`,
431421
);
432422

433423
this.logger.info(`Append Stop ${runId}`);
@@ -479,11 +469,9 @@ function checkIfRunHasStopped(run: Run) {
479469
if (op.event.type == "Start") {
480470
if (life >= 1) {
481471
logger.error(
482-
`bad logs: ${
483-
JSON.stringify(
484-
run.operations.map(({ event }) => event.type),
485-
)
486-
}`,
472+
`bad logs: ${JSON.stringify(
473+
run.operations.map(({ event }) => event.type),
474+
)}`,
487475
);
488476

489477
throw new Error(
@@ -496,11 +484,9 @@ function checkIfRunHasStopped(run: Run) {
496484
} else if (op.event.type == "Stop") {
497485
if (life <= 0) {
498486
logger.error(
499-
`bad logs: ${
500-
JSON.stringify(
501-
run.operations.map(({ event }) => event.type),
502-
)
503-
}`,
487+
`bad logs: ${JSON.stringify(
488+
run.operations.map(({ event }) => event.type),
489+
)}`,
504490
);
505491

506492
throw new Error(

src/typegate/src/runtimes/substantial/filter_utils.ts

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,10 @@ type ORD = KVHelper<["eq", "lt", "lte", "gt", "gte"], string>;
2222
type INCL = KVHelper<["in", "contains"], string>;
2323
type Terms = ORD & INCL;
2424

25-
type SpecialTerms = KVHelper<["started_at", "ended_at", "status"], Terms>;
25+
type SpecialTerms = KVHelper<
26+
["run_id", "started_at", "ended_at", "status"],
27+
Terms
28+
>;
2629

2730
export type Expr = Terms & SpecialTerms & AND & OR & NOT;
2831

@@ -101,7 +104,6 @@ export async function applyFilter(
101104
return searchResults;
102105
}
103106

104-
105107
export function evalExpr(
106108
sResult: SearchItem,
107109
filter: Expr,
@@ -121,14 +123,12 @@ export function evalExpr(
121123
const exprList = filter[op];
122124
if (!Array.isArray(exprList)) {
123125
// should be unreachable since filter is validated at push
124-
throw new Error(
125-
`Fatal: array expected at ${path.join(".")}`,
126-
);
126+
throw new Error(`Fatal: array expected at ${path.join(".")}`);
127127
}
128128
const fn = op == "or" ? "some" : "every";
129129
if (
130130
!exprList[fn]((subFilter, index) =>
131-
evalExpr(sResult, subFilter, [...newPath, `#${index}`])
131+
evalExpr(sResult, subFilter, [...newPath, `#${index}`]),
132132
)
133133
) {
134134
return false;
@@ -142,6 +142,7 @@ export function evalExpr(
142142
break;
143143
}
144144
// Special
145+
case "run_id":
145146
case "status":
146147
case "started_at":
147148
case "ended_at": {
@@ -182,7 +183,7 @@ function evalTerm(sResult: SearchItem, terms: Terms, path: Array<string>) {
182183
return false;
183184
}
184185

185-
if (!testCompare(value, toJS(terms[op]))) {
186+
if (!testCompare(value, toJS(terms[op]))) {
186187
return false;
187188
}
188189

@@ -199,17 +200,13 @@ function evalTerm(sResult: SearchItem, terms: Terms, path: Array<string>) {
199200
}
200201
case "contains":
201202
case "in": {
202-
if (
203-
!inclusion(value, toJS(terms[op]), op, newPath)
204-
) {
203+
if (!inclusion(value, toJS(terms[op]), op, newPath)) {
205204
return false;
206205
}
207206
break;
208207
}
209208
default: {
210-
throw new Error(
211-
`Unknown operator "${op}" at ${path.join(".")}`,
212-
);
209+
throw new Error(`Unknown operator "${op}" at ${path.join(".")}`);
213210
}
214211
}
215212

@@ -241,8 +238,8 @@ function ord(l: unknown, r: unknown, cp: keyof ORD, path: Array<string>) {
241238
}
242239

243240
if (
244-
typeof l == "string" && typeof r == "string" ||
245-
typeof l == "number" && typeof r == "number"
241+
(typeof l == "string" && typeof r == "string") ||
242+
(typeof l == "number" && typeof r == "number")
246243
) {
247244
switch (cp) {
248245
case "lt":
@@ -278,7 +275,9 @@ function inclusion(
278275
} else if (typeof left == "string" && typeof right == "string") {
279276
return right.includes(left);
280277
} else if (
281-
typeof left == typeof right && typeof left == "object" && left != null
278+
typeof left == typeof right &&
279+
typeof left == "object" &&
280+
left != null
282281
) {
283282
// Example: { a: { b: 1 } } in { a: { b: 1 }, c: 2 } => true
284283
const rightV = (right ?? {}) as Record<string, unknown>;

src/typegraph/core/src/utils/postprocess/naming.rs

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -130,9 +130,8 @@ fn visit_type(cx: &VisitContext, acc: &mut VisitCollector, id: u32) -> anyhow::R
130130
));
131131
let inner_name = visit_type(cx, acc, data.item)?;
132132
acc.path.pop();
133-
// gen_name(cx, acc, id, "optional")
133+
134134
gen_name(cx, acc, id, &format!("{inner_name}_optional"))
135-
// format!("{inner_name}_optional").into()
136135
}
137136
TypeNode::List { data, .. } => {
138137
acc.path.push((
@@ -144,13 +143,7 @@ fn visit_type(cx: &VisitContext, acc: &mut VisitCollector, id: u32) -> anyhow::R
144143
));
145144
let inner_name = visit_type(cx, acc, data.items)?;
146145
acc.path.pop();
147-
// gen_name(cx, acc, id, "list")
148-
/* if cx.user_named.contains(&data.items) {
149-
gen_name(cx, acc, id, &format!("{_inner_name}_list"))
150-
} else {
151-
format!("{_inner_name}_list").into()
152-
} */
153-
// format!("{inner_name}_list").into()
146+
154147
gen_name(cx, acc, id, &format!("{inner_name}_list"))
155148
}
156149
TypeNode::Object { data, .. } => {
@@ -344,15 +337,31 @@ fn gen_name(cx: &VisitContext, acc: &mut VisitCollector, id: u32, ty_name: &str)
344337

345338
let title;
346339
let mut last = acc.path.len();
340+
let mut wrapped = false;
347341
loop {
348342
last -= 1;
349343
let (last_segment, last_name) = &acc.path[last];
350344
title = match &last_segment.edge {
351345
// we don't include optional and list nodes in
352346
// generated names (useless but also, they might be placeholders)
353-
Edge::OptionalItem | Edge::ArrayItem => continue,
354-
Edge::FunctionInput => format!("{}_input", last_name),
355-
Edge::FunctionOutput => format!("{}_output", last_name),
347+
Edge::OptionalItem | Edge::ArrayItem => {
348+
wrapped = true;
349+
continue;
350+
}
351+
Edge::FunctionInput => {
352+
format!(
353+
"{}_input{}",
354+
last_name,
355+
wrapped.then_some(format!("_{ty_name}")).unwrap_or_default()
356+
)
357+
}
358+
Edge::FunctionOutput => {
359+
format!(
360+
"{}_output{}",
361+
last_name,
362+
wrapped.then_some(format!("_{ty_name}")).unwrap_or_default()
363+
)
364+
}
356365
Edge::ObjectProp(key) => {
357366
join_if_ok!(format!("{last_name}_{key}"), ty_name)
358367
}

tests/dedup/tg.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,4 +51,18 @@ await typegraph("materializer-dedup", (g: any) => {
5151
f1: f.withPolicy(Policy.public()),
5252
f2: f.withPolicy(Policy.internal()),
5353
});
54+
55+
g.expose(
56+
{
57+
f3: deno.func(t.struct({}), t.list(t.struct({ key: t.string() })), {
58+
code: () => [{ key: "value" }],
59+
}),
60+
},
61+
{
62+
f4: deno.func(t.struct({}), t.optional(t.struct({ key: t.string() })), {
63+
code: () => ({ key: "value" }),
64+
}),
65+
},
66+
Policy.public(),
67+
);
5468
});

0 commit comments

Comments
 (0)