Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow assigning subtypes in queries #606

Draft
wants to merge 21 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions packages/generate/dbschema/default.esdl
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,13 @@ module default {
scalar type global_seq extending sequence;
global seq_global -> global_seq;

abstract type Power {
property name -> str;
}

type GoodPower extending Power {}

type EvilPower extending Power {}

abstract link movie_character {
property character_name -> str;
Expand All @@ -31,16 +37,21 @@ module default {
constraint exclusive;
};
property height -> decimal;
multi link powers -> Power;
}

type Villain extending Person {
abstract type MainCharacter extending Person {}

type Villain extending MainCharacter {
link nemesis -> Hero;
overloaded multi link powers -> EvilPower;
}

type Hero extending Person {
type Hero extending MainCharacter {
property secret_identity -> str;
property number_of_movies -> int64;
multi link villains := .<nemesis[IS Villain];
overloaded multi link powers -> GoodPower;
}

scalar type year extending int16 {
Expand Down
35 changes: 35 additions & 0 deletions packages/generate/dbschema/migrations/00021.edgeql
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
CREATE MIGRATION m1u4jn5jcn65vrccu2a2ynlio44tm67exuplgdmmu4m2q2ej5u2fxa
ONTO m1sxhoqfjqn7vtpmatzanmwydtxndf3jlf33npkblmya42fx3bcdoa
{
CREATE ABSTRACT TYPE default::Power {
CREATE PROPERTY name -> std::str;
};
CREATE TYPE default::EvilPower EXTENDING default::Power;
ALTER TYPE default::Person {
CREATE MULTI LINK powers -> default::Power;
};
CREATE ABSTRACT TYPE default::MainCharacter EXTENDING default::Person;
ALTER TYPE default::Villain {
DROP EXTENDING default::Person;
EXTENDING default::MainCharacter LAST;
};
ALTER TYPE default::Villain {
ALTER LINK powers {
SET MULTI;
SET OWNED;
SET TYPE default::EvilPower USING (<default::EvilPower>{});
};
};
CREATE TYPE default::GoodPower EXTENDING default::Power;
ALTER TYPE default::Hero {
DROP EXTENDING default::Person;
EXTENDING default::MainCharacter LAST;
};
ALTER TYPE default::Hero {
ALTER LINK powers {
SET MULTI;
SET OWNED;
SET TYPE default::GoodPower USING (<default::GoodPower>{});
};
};
};
10 changes: 4 additions & 6 deletions packages/generate/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,8 @@ module.exports = {
testEnvironment: "node",
testPathIgnorePatterns: ["./dist", "./esm", "./mts", "./cjs", "./deno"],
globalSetup: "./test/globalSetup.ts",
transform: {},
globals: {
"ts-jest": {
tsconfig: "tsconfig.json"
}
}
transform: {
"^.+\\.tsx?$": "ts-jest",
},
globals: {},
};
123 changes: 94 additions & 29 deletions packages/generate/src/edgeql-js/generateObjectTypes.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { CodeFragment, dts, r, t, ts } from "../builders";
import { CodeFragment, IdentRef, dts, r, t, ts } from "../builders";
import type { GeneratorParams } from "../genutil";
import type { $ } from "../genutil";
import {
Expand Down Expand Up @@ -381,12 +381,21 @@ export const generateObjectTypes = (params: GeneratorParams) => {
const baseTypesUnion = type.bases.length
? frag`${joinFrags(
type.bases.map((base) => {
function getRecursiveLinks(b: {
id: string;
}): ($.introspect.Pointer | $.introspect.Backlink)[] {
const { pointers, backlinks, backlink_stubs, bases } = types.get(
b.id
) as $.introspect.ObjectType;
return [
...pointers,
...backlinks,
...backlink_stubs,
...(bases.length ? bases.flatMap(getRecursiveLinks) : []),
];
}
const baseType = types.get(base.id) as $.introspect.ObjectType;
const overloadedFields = [
...baseType.pointers,
...baseType.backlinks,
...baseType.backlink_stubs,
]
const overloadedFields = getRecursiveLinks(base)
.filter((field) => fieldNames.has(field.name))
.map((field) => quote(field.name));
const baseRef = getRef(baseType.name);
Expand Down Expand Up @@ -444,34 +453,90 @@ export const generateObjectTypes = (params: GeneratorParams) => {
// instantiate ObjectType subtype from shape
body.writeln([
dts`declare `,
t`type ${ref} = $.ObjectType<${quote(type.name)}, ${ref}λShape, null, [`,
// type Foo = $.ObjectType<
t`type ${ref} = $.ObjectType<`,
]);

const bases = type.bases
.map((b) => types.get(b.id))
.map((b) => getRef(b.name));
body.indented(() => {
for (const b of bases) {
body.writeln([t`...${b}['__exclusives__'],`]);
// Name
body.writeln([t`${quote(type.name)},`]);
// Pointers
body.writeln([t`${ref}λShape, `]);
// Shape
body.writeln([t`null, `]);
// Exclusives
body.writeln([t`[`]);

// Base types exclusives
const bases = (function findRecursiveBases(
bs: readonly { id: string }[]
): IdentRef[] {
return [
...bs
.map((b) => types.get(b.id) as $.introspect.ObjectType)
.flatMap((b) => {
if (b.bases.length) {
return [...findRecursiveBases(b.bases), b];
}
return [b];
})
.map((b) => getRef(b.name)),
];
})(type.bases);

body.indented(() => {
for (const b of bases) {
body.writeln([t`...${b}['__exclusives__'],`]);
}
});

// This type exclusives
body.indented(() => {
for (const ex of type.exclusives) {
body.writeln([
t`{`,
...Object.keys(ex).map((key) => {
const target = types.get(ex[key].target_id);
const { staticType } = getStringRepresentation(target, { types });
const card = `$.Cardinality.One | $.Cardinality.AtMostOne `;
return t`${key}: {__element__: ${staticType}, __cardinality__: ${card}},`;
}),
t`},`,
]);
}
});

// Exclusives, End
body.writeln([t`],`]);

// Subtypes
body.writeln([t`| ${quote(ref.name)}`]);
if (
type.is_abstract &&
!["std", "sys", "schema"].includes(splitName(type.name).mod)
) {
const subtypes = (function findRecursiveSubtypes(sub: {
id: string;
}): $.introspect.ObjectType[] {
const subs: $.introspect.ObjectType[] = [];
for (const candidate of types.values()) {
if (
candidate.kind === "object" &&
candidate.bases.find((b) => b.id === sub.id)
) {
subs.push(candidate);
subs.push(...findRecursiveSubtypes(candidate));
}
}
return subs;
})(type);

for (const sub of subtypes) {
body.writeln([t`| ${quote(getRef(sub.name).name)}`]);
}
}
});

// const ref = getRef(type.name);
for (const ex of type.exclusives) {
body.writeln([
t` {`,
...Object.keys(ex).map((key) => {
const target = types.get(ex[key].target_id);
const { staticType } = getStringRepresentation(target, { types });
const card = `$.Cardinality.One | $.Cardinality.AtMostOne `;
return t`${key}: {__element__: ${staticType}, __cardinality__: ${card}},`;
}),
t`},`,
]);
// body.writeln([t`\n {${lines.join(", ")}}`]);
}

body.writeln([t`]>;`]);
body.writeln([t`>;`]);

if (type.name === "std::Object") {
body.writeln([t`export `, dts`declare `, t`type $Object = ${ref}`]);
Expand Down
13 changes: 9 additions & 4 deletions packages/generate/src/syntax/casting.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import type {
TupleType,
TypeSet,
RangeType,
ExclusiveTuple,
ObjectTypePointers,
} from "./typesystem";
import type { cardutil } from "./cardinality";

Expand Down Expand Up @@ -73,22 +75,25 @@ export type pointerToAssignmentExpression<
export type setToAssignmentExpression<
Set extends TypeSet,
IsSetModifier extends boolean
> = [Set] extends [PrimitiveTypeSet]
> = Set extends PrimitiveTypeSet
?
| TypeSet<
assignableBy<Set["__element__"]>,
cardutil.assignable<
// Set["__cardinality__"]
cardutil.overrideLowerBound<Set["__cardinality__"], "Zero">
>
>
| getAssignmentLiteral<Set, IsSetModifier>
: [Set] extends [ObjectTypeSet]
: Set extends ObjectTypeSet
? TypeSet<
ObjectType<
// anonymize the object type
string,
Set["__element__"]["__pointers__"]
ObjectTypePointers,
any,
ExclusiveTuple,
// Allow expressions that are assignable to a supertype
Set["__element__"]["__subNames__"]
>,
cardutil.assignable<
// Allow expressions with AtMostOne or Many cardinality in
Expand Down
57 changes: 7 additions & 50 deletions packages/generate/src/syntax/select.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,57 +111,12 @@ export type exclusivesToFilterSingle<E extends ExclusiveTuple> =
: orLiteralValue<E[j][k]>;
};
}[number];

export type SelectModifiers<T extends ObjectType = ObjectType> = {
// export type SelectModifiers = {
filter?: SelectFilterExpression;
filter_single?: // | Partial<
// typeutil.stripNever<{
// [k in keyof T["__pointers__"]]: T["__pointers__"][k]
// extends PropertyDesc
// ? orScalarLiteral<{
// __element__: T["__pointers__"][k]["target"];
// __cardinality__: T["__pointers__"][k]["cardinality"];
// }>
// : never;
// }>
// >

// | (ObjectType extends T
// ? unknown
// : typeutil.stripNever<{
// [k in keyof T["__pointers__"]]: T["__pointers__"][k]
// extends PropertyDesc<
// infer T,
// infer C,
// infer E
// >
// ? E extends true
// ? orScalarLiteral<{
// __element__: T;
// __cardinality__: C;
// }>
// : never
// : never;
// }>)
exclusivesToFilterSingle<T["__exclusives__"]> | SelectFilterExpression;

// | (ObjectType extends T
// ? unknown
// : typeutil.stripNever<{
// [k in keyof T["__pointers__"]]: T["__pointers__"][k]
// extends PropertyDesc<
// infer T,
// infer C,
// infer E
// >
// ? E extends true
// ? orScalarLiteral<{
// __element__: T;
// __cardinality__: C;
// }>
// : never
// : never;
// }>);
filter_single?:
| exclusivesToFilterSingle<T["__exclusives__"]>
| SelectFilterExpression;
order_by?: OrderByExpression;
offset?: OffsetExpression | number;
limit?: LimitExpression | number;
Expand Down Expand Up @@ -872,7 +827,9 @@ export function select<Expr extends ObjectTypeExpression>(
__element__: ObjectType<
`${Expr["__element__"]["__name__"]}`, // _shape
Expr["__element__"]["__pointers__"],
Expr["__element__"]["__shape__"] // {id: true}
Expr["__element__"]["__shape__"], // {id: true}
Expr["__element__"]["__exclusives__"],
Expr["__element__"]["__subNames__"]
>;
__cardinality__: Expr["__cardinality__"];
}>;
Expand Down
5 changes: 3 additions & 2 deletions packages/generate/src/syntax/typesystem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -217,14 +217,15 @@ export interface ObjectType<
Name extends string = string,
Pointers extends ObjectTypePointers = ObjectTypePointers,
Shape extends object | null = any,
Exclusives extends ExclusiveTuple = ExclusiveTuple
// Polys extends Poly[] = any[]
Exclusives extends ExclusiveTuple = ExclusiveTuple,
SubtypeNames extends string = Name
> extends BaseType {
__kind__: TypeKind.object;
__name__: Name;
__pointers__: Pointers;
__shape__: Shape;
__exclusives__: Exclusives;
__subNames__: SubtypeNames;
}

export type PropertyTypes =
Expand Down
Loading