Skip to content

Commit

Permalink
Change representation of ont as code interface link types from both s…
Browse files Browse the repository at this point in the history
…ides to just one (#442)

* Change representation of link types from both sides to just one

* remove pnpm stuff

* remove changeset

* fix lint

* fix lint

* changeset

* fix lint

* fix lint

* fix lint

* Fix tests

* Fix tests

---------

Co-authored-by: Alex Parson <[email protected]>
  • Loading branch information
aep000 and Alex Parson authored Jul 9, 2024
1 parent 57b68db commit dc93aa6
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 205 deletions.
5 changes: 5 additions & 0 deletions .changeset/purple-pots-fold.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@osdk/maker": minor
---

Update ontology as code interface link types to reflect internal expectations of ontology integration
2 changes: 1 addition & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"editor.defaultFormatter": "dprint.dprint"
},
"[typescript]": {
"editor.defaultFormatter": "dprint.dprint"
"editor.defaultFormatter": "vscode.typescript-language-features"
},
"[json]": {
"editor.defaultFormatter": "dprint.dprint"
Expand Down
70 changes: 27 additions & 43 deletions packages/maker/src/api/defineInterfaceLinkConstraint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,68 +18,52 @@ import invariant from "tiny-invariant";
import type { InterfaceType } from "./types.js";

type Meta = { apiName: string; displayName?: string; description?: string };
type ApiNameOrMeta = string | Meta;
type ApiNameOrInterfaceType = string | InterfaceType;

type Many = { type: InterfaceType; many: ApiNameOrMeta; one?: never };
type One = { type: InterfaceType; one: ApiNameOrMeta; many?: never };
type Many = {
apiName: string;
from: InterfaceType;
toMany: ApiNameOrInterfaceType;
toOne?: never;
displayName?: string;
description?: string;
};
type One = {
apiName: string;
from: InterfaceType;
toOne: ApiNameOrInterfaceType;
toMany?: never;
displayName?: string;
description?: string;
};

export function defineInterfaceLinkConstraint(
{ from, to }: {
from: Many;
to: One;
} | {
from: One;
to: One | Many;
},
linkDef: One | Many,
) {
invariant(
(from.one == null && from.many) || (from.one && from.many == null),
"from should have either one or many, not both",
);

invariant(
(to.one == null && to.many) || (to.one && to.many == null),
"to should have either one or many, not both",
);

invariant(!(from.many && to.many), "many to many is not supported");

const fromLinkMeta = getLinkMeta(from.one ?? from.many);
const toLinkMeta = getLinkMeta(to.one ?? to.many);
const fromLinkMeta = getLinkMeta(linkDef);

invariant(
from.type.links.find(a => a.metadata.apiName === fromLinkMeta.apiName)
linkDef.from.links.find(a => a.metadata.apiName === fromLinkMeta.apiName)
== null,
`Link with apiName ${fromLinkMeta.apiName} already exists on ${from.type.apiName}`,
);
invariant(
to.type.links.find(a => a.metadata.apiName === toLinkMeta.apiName) == null,
`Link with apiName ${toLinkMeta.apiName} already exists on ${to.type.apiName}`,
`Link with apiName ${fromLinkMeta.apiName} already exists on ${linkDef.apiName}`,
);

from.type.links.push({
cardinality: from.many ? "MANY" : "SINGLE",
linkedEntityTypeId: getLinkedType(to.type),
linkDef.from.links.push({
cardinality: linkDef.toMany ? "MANY" : "SINGLE",
linkedEntityTypeId: getLinkedType(linkDef.toMany ?? linkDef.toOne),
metadata: fromLinkMeta,
required: true, // TODO: expose this?
});

to.type.links.push({
cardinality: to.one ? "SINGLE" : "MANY",
linkedEntityTypeId: getLinkedType(from.type),
metadata: toLinkMeta,
required: true, // TODO: expose this?
});
}

function getLinkedType(t: InterfaceType) {
function getLinkedType(t: string | InterfaceType) {
return {
type: "interfaceType" as const,
interfaceType: t.apiName,
interfaceType: typeof t === "string" ? t : t.apiName,
};
}

function getLinkMeta(meta: ApiNameOrMeta) {
function getLinkMeta(meta: One | Many) {
return typeof meta === "string"
? withDefaults({ apiName: meta })
: withDefaults(meta);
Expand Down
174 changes: 13 additions & 161 deletions packages/maker/src/api/overall.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -217,45 +217,12 @@ describe("Ontology Defining", () => {
b = defineInterface({ apiName: "B" });
});

it("many to many fails", () => {
expect(() => {
// @ts-expect-error
defineInterfaceLinkConstraint({
from: { type: a, many: "manyLinks" }, // need the any to be sure it fails
to: { type: b, many: "manyLinks" },
});
}).toThrowErrorMatchingInlineSnapshot(
`[Error: Invariant failed: many to many is not supported]`,
);
});

it("does not allow passing both one and many", () => {
expect(() => {
defineInterfaceLinkConstraint({
// @ts-expect-error
from: { type: a, one: "singleLink", many: "manyLinks" }, // need the any to be sure it fails
to: { type: b, one: "singleLink" },
});
}).toThrowErrorMatchingInlineSnapshot(
`[Error: Invariant failed: from should have either one or many, not both]`,
);

expect(() => {
defineInterfaceLinkConstraint({
from: { type: a, one: "singleLink" }, // need the any to be sure it fails
// @ts-expect-error
to: { type: b, one: "singleLink", many: "manyLinks" },
});
}).toThrowErrorMatchingInlineSnapshot(
`[Error: Invariant failed: to should have either one or many, not both]`,
);
});

it("single to single works", () => {
it("single link works", () => {
expect(a).not.toBeUndefined();
defineInterfaceLinkConstraint({
from: { type: a, one: "singleLink" },
to: { type: b, one: "singleLink" },
from: a,
toOne: b,
apiName: "singleLink",
});

expect(dumpOntologyFullMetadata()).toMatchInlineSnapshot(`
Expand Down Expand Up @@ -312,109 +279,7 @@ describe("Ontology Defining", () => {
"icon": undefined,
},
"extendsInterfaces": [],
"links": [
{
"cardinality": "SINGLE",
"linkedEntityTypeId": {
"interfaceType": "A",
"type": "interfaceType",
},
"metadata": {
"apiName": "singleLink",
"description": "singleLink",
"displayName": "singleLink",
},
"required": true,
},
],
"properties": [],
"status": {
"active": {},
"type": "active",
},
},
},
},
"sharedPropertyTypes": {},
}
`);
});

it("many to single works", () => {
defineInterfaceLinkConstraint({
from: { type: a, many: "manyLinks" },
to: { type: b, one: "singleLink" },
});

expect(dumpOntologyFullMetadata()).toMatchInlineSnapshot(`
{
"blockPermissionInformation": {
"actionTypes": {},
"linkTypes": {},
"objectTypes": {},
},
"interfaceTypes": {
"A": {
"interfaceType": {
"allExtendsInterfaces": [],
"allLinks": [],
"allProperties": [],
"apiName": "A",
"displayMetadata": {
"description": "A",
"displayName": "A",
"icon": undefined,
},
"extendsInterfaces": [],
"links": [
{
"cardinality": "MANY",
"linkedEntityTypeId": {
"interfaceType": "B",
"type": "interfaceType",
},
"metadata": {
"apiName": "manyLinks",
"description": "manyLinks",
"displayName": "manyLinks",
},
"required": true,
},
],
"properties": [],
"status": {
"active": {},
"type": "active",
},
},
},
"B": {
"interfaceType": {
"allExtendsInterfaces": [],
"allLinks": [],
"allProperties": [],
"apiName": "B",
"displayMetadata": {
"description": "B",
"displayName": "B",
"icon": undefined,
},
"extendsInterfaces": [],
"links": [
{
"cardinality": "SINGLE",
"linkedEntityTypeId": {
"interfaceType": "A",
"type": "interfaceType",
},
"metadata": {
"apiName": "singleLink",
"description": "singleLink",
"displayName": "singleLink",
},
"required": true,
},
],
"links": [],
"properties": [],
"status": {
"active": {},
Expand All @@ -428,10 +293,11 @@ describe("Ontology Defining", () => {
`);
});

it("single to many works", () => {
it("many link works", () => {
defineInterfaceLinkConstraint({
from: { type: a, one: "singleLink" },
to: { type: b, many: "manyLinks" },
from: a,
toMany: b,
apiName: "manyLink",
});

expect(dumpOntologyFullMetadata()).toMatchInlineSnapshot(`
Expand Down Expand Up @@ -462,9 +328,9 @@ describe("Ontology Defining", () => {
"type": "interfaceType",
},
"metadata": {
"apiName": "singleLink",
"description": "singleLink",
"displayName": "singleLink",
"apiName": "manyLink",
"description": "manyLink",
"displayName": "manyLink",
},
"required": true,
},
Expand All @@ -488,21 +354,7 @@ describe("Ontology Defining", () => {
"icon": undefined,
},
"extendsInterfaces": [],
"links": [
{
"cardinality": "MANY",
"linkedEntityTypeId": {
"interfaceType": "A",
"type": "interfaceType",
},
"metadata": {
"apiName": "manyLinks",
"description": "manyLinks",
"displayName": "manyLinks",
},
"required": true,
},
],
"links": [],
"properties": [],
"status": {
"active": {},
Expand Down

0 comments on commit dc93aa6

Please sign in to comment.