Skip to content

Commit

Permalink
feat(sdk): implement injection (#403)
Browse files Browse the repository at this point in the history
Signed-off-by: Michaël <[email protected]>
  • Loading branch information
michael-0acf4 authored Sep 6, 2023
1 parent 88f5ecd commit 30fc24a
Show file tree
Hide file tree
Showing 18 changed files with 548 additions and 21 deletions.
2 changes: 1 addition & 1 deletion libs/common/src/typegraph/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use super::{EffectType, PolicyIndices};
#[cfg_attr(feature = "codegen", derive(JsonSchema))]
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct SingleValue<T> {
value: T,
pub value: T,
}

#[cfg_attr(feature = "codegen", derive(JsonSchema))]
Expand Down
55 changes: 55 additions & 0 deletions typegate/tests/injection/injection.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Copyright Metatype OÜ, licensed under the Elastic License 2.0.
// SPDX-License-Identifier: Elastic-2.0

import { Policy, t, typegraph } from "@typegraph/deno/src/mod.ts";
import { CREATE, DELETE, NONE, UPDATE } from "@typegraph/deno/src/effects.ts";
import { DenoRuntime } from "@typegraph/deno/src/runtimes/deno.ts";

const tpe = t.struct({
"a": t.integer({}, { name: "A" }),
"raw_int": t.integer().set({
[CREATE]: 1,
[UPDATE]: 2,
[DELETE]: 3,
[NONE]: 4,
}),
"raw_str": t.string().set("2"),
"secret": t.integer().fromSecret("TEST_VAR"),
"context": t.string().fromContext("userId"),
"optional_context": t.string().optional().fromContext("inexistent"),
"raw_obj": t.struct({ "in": t.integer() }).set({ "in": -1 }),
"alt_raw": t.string().set("2"),
"alt_secret": t.string().fromSecret("TEST_VAR"),
"alt_context": t.string().fromContext("userId"),
"alt_context_opt": t.string().optional().fromContext("userId"),
"alt_context_opt_missing": t.string().optional().fromContext("userId"),
"date": t.datetime().inject("now"),
});

typegraph("injection", (g) => {
const deno = new DenoRuntime();
const pub = Policy.public();

g.expose({
test: deno.func(
t.struct({ input: tpe }),
t.struct({
fromInput: tpe,
parent: t.integer({}, { name: "someName" }),
fromParent: deno.func(
t.struct({
value: t.integer().fromParent({
[NONE]: "someName",
}),
}),
t.struct({ value: t.integer() }),
{ code: "(value) => value" },
),
}),
{
code:
"({ input }) => { return { fromInput: input, parent: 1234567 }; }",
},
).withPolicy(pub),
});
});
51 changes: 51 additions & 0 deletions typegate/tests/injection/injection_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -252,3 +252,54 @@ Meta.test("dynamic value injection", async (t) => {
});
unfreezeDate();
});

Meta.test("Deno: value injection", async (t) => {
const e = await t.engine("injection/injection.ts", {
secrets: { TG_INJECTION_TEST_VAR: "3" },
});

freezeDate();
await t.should("work", async () => {
await gql`
query {
test(input: {a: 12}) {
fromInput {
a
context
optional_context
raw_int
raw_obj { in }
alt_raw
alt_secret
alt_context_opt
alt_context_opt_missing
date
}
parent
fromParent { value }
}
}`
.withContext({
userId: "123",
})
.expectData({
test: {
fromInput: {
a: 12,
context: "123",
raw_int: 4,
raw_obj: { in: -1 },
alt_raw: "2",
alt_secret: "3",
alt_context_opt: "123",
alt_context_opt_missing: "123",
date: new Date().toISOString(),
},
parent: 1234567,
fromParent: { value: 1234567 },
},
})
.on(e);
});
unfreezeDate();
});
6 changes: 6 additions & 0 deletions typegraph/core/src/global_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,12 @@ impl Store {
.ok_or_else(|| errors::object_not_found("type", type_id))
}

pub fn get_type_mut(&mut self, type_id: TypeId) -> Result<&mut Type, TgError> {
self.types
.get_mut(type_id as usize)
.ok_or_else(|| errors::object_not_found("type", type_id))
}

pub fn get_type_by_name(&self, name: &str) -> Option<TypeId> {
self.type_by_names.get(name).copied()
}
Expand Down
8 changes: 6 additions & 2 deletions typegraph/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@ use indoc::formatdoc;
use regex::Regex;
use types::{
Array, Boolean, Either, Float, Func, Integer, Optional, Proxy, StringT, Struct, Type,
TypeBoolean, Union, WithPolicy,
TypeBoolean, Union, WithInjection, WithPolicy,
};
use validation::validate_name;
use wit::core::{
ContextCheck, Policy, PolicyId, TypeArray, TypeBase, TypeEither, TypeFloat, TypeFunc, TypeId,
TypeInteger, TypeOptional, TypePolicy, TypeProxy, TypeString, TypeStruct, TypeUnion,
TypegraphInitParams,
TypeWithInjection, TypegraphInitParams,
};
use wit::runtimes::{MaterializerDenoFunc, Runtimes};

Expand Down Expand Up @@ -198,6 +198,10 @@ impl wit::core::Core for Lib {
})
}

fn with_injection(data: TypeWithInjection) -> Result<TypeId> {
with_store_mut(|s| Ok(s.add_type(|id| Type::WithInjection(WithInjection { id, data }))))
}

fn with_policy(data: TypePolicy) -> Result<TypeId> {
with_store_mut(|s| Ok(s.add_type(|id| Type::WithPolicy(WithPolicy { id, data }))))
}
Expand Down
3 changes: 2 additions & 1 deletion typegraph/core/src/typedef/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ pub mod float;
pub mod func;
pub mod integer;
pub mod optional;
pub mod policy;
pub mod proxy;
pub mod string;
pub mod struct_;
pub mod union;
pub mod with_injection;
pub mod with_policy;
101 changes: 101 additions & 0 deletions typegraph/core/src/typedef/with_injection.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
// Copyright Metatype OÜ, licensed under the Mozilla Public License Version 2.0.
// SPDX-License-Identifier: MPL-2.0

use crate::{
conversion::types::TypeConversion,
errors::Result,
global_store::{with_store, Store},
typegraph::TypegraphContext,
types::{Type, TypeData, WithInjection, WrapperTypeData},
wit::core::TypeWithInjection,
};
use common::typegraph::{EffectType, Injection, InjectionData, SingleValue, TypeNode};

use std::collections::HashMap;

impl TypeConversion for WithInjection {
fn convert(&self, ctx: &mut TypegraphContext, runtime_id: Option<u32>) -> Result<TypeNode> {
with_store(|s| -> Result<_> {
let tpe = s.get_type(self.data.tpe)?;
let mut type_node = tpe.convert(ctx, runtime_id)?;
let base = type_node.base_mut();
let value: Injection =
serde_json::from_str(&self.data.injection).map_err(|e| e.to_string())?;
if let Injection::Parent(data) = value {
let get_correct_id = |v: u32| -> Result<u32> {
let id = s.resolve_proxy(v)?;
if let Some(index) = ctx.find_type_index_by_store_id(&id) {
return Ok(index);
}
Err(format!("unable to find type for store id {}", id))
};
let new_data = match data {
InjectionData::SingleValue(SingleValue { value }) => {
InjectionData::SingleValue(SingleValue {
value: get_correct_id(value)?,
})
}
InjectionData::ValueByEffect(per_effect) => {
let mut new_per_effect: HashMap<EffectType, u32> = HashMap::new();
for (k, v) in per_effect.iter() {
new_per_effect.insert(*k, get_correct_id(*v)?);
}
InjectionData::ValueByEffect(new_per_effect)
}
};
base.injection = Some(Injection::Parent(new_data));
} else {
base.injection = Some(value);
}
Ok(type_node)
})
}
}

impl TypeData for TypeWithInjection {
fn get_display_params_into(&self, params: &mut Vec<String>) {
let value: Injection = serde_json::from_str(&self.injection).unwrap();
let gen_display = |data: InjectionData<String>| match data {
InjectionData::SingleValue(t) => t.value,
InjectionData::ValueByEffect(t) => {
let mut res: Vec<String> = vec![];
for (effect, value) in t.iter() {
res.push(format!("{:?}:{}", effect, value));
}
res.join(", ")
}
};

let gen_display_u32 = |data: InjectionData<u32>| match data {
InjectionData::SingleValue(t) => t.value.to_string(),
InjectionData::ValueByEffect(t) => {
let mut res: Vec<String> = vec![];
for (effect, value) in t.iter() {
res.push(format!("{:?}:{}", effect, value));
}
res.join(", ")
}
};

params.push(format!(
"injection='[{}]'",
match value {
Injection::Static(data) => format!("static={}", gen_display(data)),
Injection::Context(data) => format!("context={}", gen_display(data)),
Injection::Secret(data) => format!("secret={}", gen_display(data)),
Injection::Dynamic(data) => format!("dynamic={}", gen_display(data)),
Injection::Parent(data) => format!("parent={}", gen_display_u32(data)),
},
));
}

fn variant_name(&self) -> String {
"injection".to_string()
}
}

impl WrapperTypeData for TypeWithInjection {
fn get_wrapped_type<'a>(&self, store: &'a Store) -> Option<&'a Type> {
store.get_type(self.tpe).ok()
}
}
File renamed without changes.
6 changes: 6 additions & 0 deletions typegraph/core/src/typegraph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ impl TypegraphContext {
let tpe = s.get_type(type_id)?;
let tpe = match tpe {
Type::WithPolicy(t) => t.data.get_wrapped_type(s).unwrap(),
Type::WithInjection(t) => t.data.get_wrapped_type(s).unwrap(),
_ => tpe,
};
if !matches!(tpe, Type::Func(_)) {
Expand Down Expand Up @@ -238,6 +239,7 @@ impl TypegraphContext {
self.types.push(None);

let tpe = store.get_type(id)?;

let type_node = tpe.convert(self, runtime_id)?;

self.types[idx] = Some(type_node);
Expand Down Expand Up @@ -328,4 +330,8 @@ impl TypegraphContext {
Ok(idx as RuntimeId)
}
}

pub fn find_type_index_by_store_id(&self, id: &u32) -> Option<u32> {
self.mapping.types.get(id).copied()
}
}
6 changes: 5 additions & 1 deletion typegraph/core/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use crate::global_store::{with_store, Store};
use crate::typegraph::TypegraphContext;
use crate::wit::core::{
TypeArray, TypeBase, TypeEither, TypeFloat, TypeFunc, TypeId, TypeInteger, TypeOptional,
TypePolicy, TypeProxy, TypeString, TypeStruct, TypeUnion,
TypePolicy, TypeProxy, TypeString, TypeStruct, TypeUnion, TypeWithInjection,
};

pub trait TypeData {
Expand Down Expand Up @@ -59,7 +59,10 @@ pub type Array = ConcreteType<TypeArray>;
pub type Optional = ConcreteType<TypeOptional>;
pub type Union = ConcreteType<TypeUnion>;
pub type Either = ConcreteType<TypeEither>;

// Note: TypePolicy|TypeWithInjection|Proxy => Struct | Integer | ...
pub type WithPolicy = WrapperType<TypePolicy>;
pub type WithInjection = WrapperType<TypeWithInjection>;

#[derive(Debug)]
#[enum_dispatch(TypeFun, TypeConversion)]
Expand All @@ -76,6 +79,7 @@ pub enum Type {
Union(Union),
Either(Either),
WithPolicy(WithPolicy),
WithInjection(WithInjection),
}

#[enum_dispatch]
Expand Down
8 changes: 7 additions & 1 deletion typegraph/core/wit/typegraph.wit
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,19 @@ interface core {
finalize-typegraph: func() -> result<string, error>

type type-id = u32

record type-base {
name: option<string>,
// string => json string
runtime-config: option<list<tuple<string, string>>>
}

record type-with-injection {
tpe: type-id,
injection: string
}

with-injection: func(data: type-with-injection) -> result<type-id, error>

record type-proxy {
name: string,
}
Expand Down
11 changes: 11 additions & 0 deletions typegraph/deno/src/effects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,14 @@ export function delete_(idempotent = true): EffectDelete {
export function update(idempotent = true): EffectUpdate {
return { tag: "update", val: idempotent };
}

export const UPDATE = Symbol("update");
export const DELETE = Symbol("delete");
export const CREATE = Symbol("create");
export const NONE = Symbol("none");
export type PerEffect = {
[CREATE]?: string;
[UPDATE]?: string;
[DELETE]?: string;
[NONE]?: string;
};
Loading

0 comments on commit 30fc24a

Please sign in to comment.