Skip to content

Commit

Permalink
fix(sdk): reduce union/either variant if required in apply syntax (#463)
Browse files Browse the repository at this point in the history
Signed-off-by: Michaël <[email protected]>
  • Loading branch information
michael-0acf4 authored Oct 25, 2023
1 parent 482f74f commit 4923e53
Show file tree
Hide file tree
Showing 21 changed files with 177 additions and 27 deletions.
14 changes: 7 additions & 7 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion dev/lock.yml
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ dev:
WASMEDGE_VERSION: 0.12.1
TYPEGRAPH_VERSION: 0.0.3
PRISMA_VERSION: 5.4.2
METATYPE_VERSION: 0.2.4-0+dev
METATYPE_VERSION: 0.2.5
WASM_OPT_VERSION: 0.116.0
TAGLINE: >-
Declarative API development platform. Build serverless backends with
Expand Down
7 changes: 6 additions & 1 deletion dev/tree-view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,12 @@ export function treeView(tg: TypeGraphDS, rootIdx = 0, depth = 4) {
const indent = " ".repeat(path.edges.length);
const edge = cyan(`${path.edges[path.edges.length - 1] ?? "[root]"}`);
const idxStr = green(`${idx}`);
console.log(`${indent}${edge}${idxStr} ${type.type}:${type.title}`);
const injection = type.injection
? ` (injection ${type.injection.source})`
: "";
console.log(
`${indent}${edge}${idxStr} ${type.type}:${type.title}${injection}`,
);
return path.edges.length < depth;
}, { allowCircular: true });
}
Expand Down
2 changes: 1 addition & 1 deletion examples/templates/python/compose.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
services:
typegate:
image: ghcr.io/metatypedev/typegate:v0.2.4-0+dev
image: ghcr.io/metatypedev/typegate:v0.2.5
restart: always
ports:
- "7890:7890"
Expand Down
4 changes: 2 additions & 2 deletions examples/templates/python/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
[tool.poetry]
name = "example"
version = "0.2.4-0+dev"
version = "0.2.5"
description = ""
authors = []
readme = "README.md"

[tool.poetry.dependencies]
python = ">=3.8,<4.0"
typegraph = "0.2.4-0+dev"
typegraph = "0.2.5"

[build-system]
requires = ["poetry-core"]
Expand Down
2 changes: 1 addition & 1 deletion libs/common/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "common"
version = "0.2.4-0+dev"
version = "0.2.5"
edition = "2021"

[dependencies]
Expand Down
2 changes: 1 addition & 1 deletion libs/macros/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "macros"
version = "0.2.4-0+dev"
version = "0.2.5"
edition = "2021"

[lib]
Expand Down
2 changes: 1 addition & 1 deletion libs/typescript/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "typescript"
version = "0.2.4-0+dev"
version = "0.2.5"
edition = "2021"

[dependencies]
Expand Down
2 changes: 1 addition & 1 deletion libs/xtask/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "xtask"
version = "0.2.4-0+dev"
version = "0.2.5"
edition = "2021"

[dependencies]
Expand Down
2 changes: 1 addition & 1 deletion meta-cli/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "meta-cli"
version = "0.2.4-0+dev"
version = "0.2.5"
edition = "2021"

description = "Declarative API development platform. Build serverless backends with zero-trust and less code, no matter where and how your (legacy) systems are."
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "metatype"
version = "0.2.4-0+dev"
version = "0.2.5"
description = ""
authors = []

Expand Down
2 changes: 1 addition & 1 deletion typegate/native/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "native"
version = "0.2.4-0+dev"
version = "0.2.5"
edition = "2021"

[lib]
Expand Down
2 changes: 1 addition & 1 deletion typegate/tests/runtimes/wasmedge/rust/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "rust"
version = "0.2.4-0+dev"
version = "0.2.5"
edition = "2021"

[lib]
Expand Down
32 changes: 30 additions & 2 deletions typegate/tests/typecheck/apply.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,40 @@

simple_tpe = t.struct(
{
"one": t.string(),
"one": t.union([t.string(), t.integer()]).optional(),
"two": t.struct(
{
"apply": t.integer(),
"user": t.integer(),
"set": t.integer().optional(),
"context": t.string().optional(),
}
),
).optional(),
"branching": t.union(
[
# *.a.b
t.struct({"a": t.struct({"b": t.string()})}, name="V1"),
# *.a.b.c
# .d
t.struct(
{
"a": t.struct(
{
"b": t.union(
[
t.struct({"c": t.string()}, name="A"),
t.struct(
{"c": t.string(), "d": t.string()}, name="B"
),
]
)
}
)
},
name="V2",
),
]
).optional(),
}
)

Expand Down Expand Up @@ -61,6 +86,9 @@ def test_apply_python(g: Graph):
}
)
.with_policy(public),
testBranching=identity_simple.apply(
{"branching": {"a": {"b": {"c": "nested"}}}}
).with_policy(public),
selfReferingType=identity_self_ref.apply(
{
"a": g.inherit(), # A1
Expand Down
35 changes: 35 additions & 0 deletions typegate/tests/typecheck/apply_syntax_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,41 @@ Meta.test("python(sdk): apply", async (t) => {
},
);

// FIXME: "branching" is still mandatory?
// await t.should(
// "work with nested union/either",
// async () => {
// await gql`
// query {
// testBranching {
// branching {
// ... on V1 { a { b } }
// ... on V2 { a {
// b {
// ... on A { c }
// ... on B { c }
// }
// }
// }
// }
// }
// }
// `
// .expectData({
// simpleInjection: {
// branching: {
// a: {
// b: {
// c: "nested",
// },
// },
// },
// },
// })
// .on(e);
// },
// );

await t.should(
"work with self-refering type",
async () => {
Expand Down
2 changes: 1 addition & 1 deletion typegraph/core/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "typegraph_core"
version = "0.2.4-0+dev"
version = "0.2.5"
edition = "2021"

[lib]
Expand Down
81 changes: 81 additions & 0 deletions typegraph/core/src/global_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,83 @@ impl Store {
})
}

pub fn pick_branch_by_path(supertype_id: TypeId, path: &[String]) -> Result<(Type, TypeId)> {
let supertype = supertype_id.as_type()?;
let filter_and_reduce = |variants: Vec<u32>| match path.len() {
0 => Ok((supertype.clone(), supertype_id)), // terminal node
_ => {
let mut compatible = vec![];
let mut failures = vec![];
let chunk = path.first().unwrap();
for (i, variant) in variants.iter().enumerate() {
let variant: TypeId = variant.into();
let unwrapped_variant = variant.resolve_wrapper()?.as_type()?;
match unwrapped_variant {
Type::Struct(t) => {
for (prop_name, prop_id) in t.iter_props() {
if prop_name.eq(chunk) {
// variant is compatible with the path
// try expanding it, if it fails, just skip
match Store::get_type_by_path(prop_id, &path[1..]) {
Ok((_, solution)) => compatible.push(solution),
Err(e) => failures.push(format!(
"[v{i} → {prop_name}]: {}",
e.stack.first().unwrap().clone()
)),
}
}
}
}
Type::Either(..) | Type::Union(..) => {
// get_type_by_path => pick_branch_by_path
match Store::get_type_by_path(variant, &path[1..]) {
Ok((_, solution)) => compatible.push(solution),
Err(e) => failures
.push(format!("[v{i}]: {}", e.stack.first().unwrap().clone())),
}
}
_ => {} // skip
}
}

if compatible.is_empty() {
return Err(format!(
"unable to expand variant with **.{}\nDetails:\n{}",
path.join("."),
failures.join("\n")
)
.into());
}

let first = compatible.first().unwrap().to_owned();
let ret_id = match &supertype {
Type::Union(..) => first,
Type::Either(..) => {
if compatible.len() > 1 {
return Err(format!(
"either node with more than one compatible variant encoutered at path **.{}",
path.join("."),
).into(),
);
}
first
}
_ => {
return Err("invalid state: either or union expected as supertype".into());
}
};

Ok((ret_id.as_type()?, ret_id))
}
};

match &supertype {
Type::Either(t) => filter_and_reduce(t.data.variants.clone()),
Type::Union(t) => filter_and_reduce(t.data.variants.clone()),
_ => Store::get_type_by_path(supertype_id, path), // no branching, trivial case
}
}

pub fn get_type_by_path(struct_id: TypeId, path: &[String]) -> Result<(Type, TypeId)> {
let mut ret = (struct_id.as_type()?, struct_id);

Expand All @@ -190,6 +267,10 @@ impl Store {
}
};
}
Type::Union(..) | Type::Either(..) => {
ret = Store::pick_branch_by_path(unwrapped_id, &path[pos..])?;
break;
}
_ => return Err(errors::expect_object_at_path(&curr_path)),
}
}
Expand Down
4 changes: 2 additions & 2 deletions typegraph/core/src/utils/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use crate::wit::runtimes::MaterializerDenoFunc;
use crate::wit::utils::Auth as WitAuth;
use crate::{t, Lib};

mod apply;
pub mod apply;

fn find_missing_props(
supertype_id: TypeId,
Expand Down Expand Up @@ -109,7 +109,7 @@ impl crate::wit::utils::Guest for crate::Lib {

if item.node.is_leaf() {
let path_infos = item.node.path_infos.clone();
let apply_value = path_infos.value;
let apply_value = path_infos.value.clone();
let id = Store::get_type_by_path(supertype_id.into(), &path_infos.path)?.1;

if apply_value.inherit && apply_value.payload.is_none() {
Expand Down
1 change: 1 addition & 0 deletions typegraph/core/wit/typegraph.wit
Original file line number Diff line number Diff line change
Expand Up @@ -468,6 +468,7 @@ interface utils {
// ]
record apply-value {
inherit: bool,
// json string
payload: option<string>
}

Expand Down
Loading

0 comments on commit 4923e53

Please sign in to comment.