Skip to content

Commit ed629d8

Browse files
committed
chore(prisma-parser): create utility to lookup for relation
1 parent 480bc56 commit ed629d8

File tree

2 files changed

+204
-0
lines changed

2 files changed

+204
-0
lines changed
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import { type Field } from "@mrleebo/prisma-ast";
2+
3+
import { getFieldTypeName } from "../getFieldTypeName";
4+
5+
import { isKeyValue, isRelationArray, isRelationNode } from "@/utils/isTypeOf";
6+
import {
7+
type RelationType,
8+
type RawRelationInfo,
9+
} from "@/types/intermediateFormattedNode";
10+
import { scalarFieldType } from "@/constants/scalarFieldType";
11+
12+
export const lookForRelation = (
13+
field: Field,
14+
tableName: string,
15+
registerRelation: (config: RawRelationInfo) => void,
16+
registerInverseRelation: (name: string, info: RelationType) => void,
17+
): void => {
18+
const fieldType = getFieldTypeName(field.fieldType);
19+
20+
let relationFields: string[] = [];
21+
let relationReferences: string[] = [];
22+
let relationName: string | undefined;
23+
24+
if (field.attributes !== undefined) {
25+
for (const attr of field.attributes) {
26+
if (attr.args === undefined || !isRelationNode(attr)) continue;
27+
28+
for (const argument of attr.args) {
29+
if (typeof argument.value === "string") {
30+
relationName = argument.value;
31+
continue;
32+
}
33+
34+
if (!isKeyValue(argument.value)) {
35+
continue;
36+
}
37+
38+
if (
39+
argument.value.key === "name" &&
40+
typeof argument.value.value === "string"
41+
) {
42+
relationName = argument.value.value;
43+
}
44+
45+
if (!isRelationArray(argument.value.value)) continue;
46+
47+
if (argument.value.key === "fields") {
48+
relationFields = argument.value.value.args;
49+
}
50+
51+
if (argument.value.key === "references") {
52+
relationReferences = argument.value.value.args;
53+
}
54+
}
55+
56+
if (
57+
relationFields.length > 0 &&
58+
relationFields.length === relationReferences.length
59+
) {
60+
relationReferences.forEach((referenceField) => {
61+
registerRelation({
62+
referenceField,
63+
referenceTable: fieldType,
64+
table: tableName,
65+
field: relationFields[0],
66+
name: relationName,
67+
});
68+
});
69+
70+
return;
71+
}
72+
}
73+
}
74+
75+
// check if the field is maybe a inverse relation
76+
// that is the if is not a scalar type and not a composite type
77+
if (scalarFieldType.has(fieldType)) {
78+
return;
79+
}
80+
81+
const inverseKeyPrefix = `${tableName}.${fieldType}`;
82+
registerInverseRelation(
83+
relationName !== undefined
84+
? `${inverseKeyPrefix}.${relationName}`
85+
: inverseKeyPrefix,
86+
field.array === true ? "many" : "one",
87+
);
88+
};
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
import { type Field } from "@mrleebo/prisma-ast";
2+
3+
import { lookForRelation } from "../lookForRelation";
4+
5+
import { type RawRelationInfo } from "@/types/intermediateFormattedNode";
6+
import {
7+
namedRelationTable1,
8+
namedRelationTable2,
9+
productRelation,
10+
productTable,
11+
} from "@/tests/data";
12+
13+
describe("look for relations", () => {
14+
test("identifier relations", () => {
15+
const registerRelation = jest.fn();
16+
const registerInverseRelation = jest.fn();
17+
const testTableName = "TestTable";
18+
19+
lookForRelation(
20+
productRelation,
21+
testTableName,
22+
registerRelation,
23+
registerInverseRelation,
24+
);
25+
26+
expect(registerRelation).toHaveBeenCalledWith({
27+
table: testTableName,
28+
field: "productId",
29+
referenceTable: "Product",
30+
referenceField: "id",
31+
} satisfies RawRelationInfo);
32+
expect(registerInverseRelation).not.toHaveBeenCalled();
33+
});
34+
35+
test("will register named relation defined with keyvalue: @relation(name: 'name-here' )", () => {
36+
const registerRelation = jest.fn();
37+
const registerInverseRelation = jest.fn();
38+
39+
const testTableName = "TestTable";
40+
41+
lookForRelation(
42+
namedRelationTable1.properties[3] as Field,
43+
testTableName,
44+
registerRelation,
45+
registerInverseRelation,
46+
);
47+
48+
expect(registerRelation).toHaveBeenCalledWith({
49+
table: testTableName,
50+
field: "player1Id",
51+
name: '"player1"',
52+
referenceField: "id",
53+
referenceTable: "User",
54+
} satisfies RawRelationInfo);
55+
});
56+
57+
test("will register named relation defined without keyvalue @relation('name-here')", () => {
58+
const registerRelation = jest.fn();
59+
const registerInverseRelation = jest.fn();
60+
61+
const testTableName = "TestTable";
62+
63+
lookForRelation(
64+
namedRelationTable1.properties[5] as Field,
65+
testTableName,
66+
registerRelation,
67+
registerInverseRelation,
68+
);
69+
70+
expect(registerRelation).toHaveBeenCalledWith({
71+
table: testTableName,
72+
field: "player2Id",
73+
name: '"player2"',
74+
referenceField: "id",
75+
referenceTable: "User",
76+
} satisfies RawRelationInfo);
77+
});
78+
79+
test("will register inverse relation", () => {
80+
const registerRelation = jest.fn();
81+
const registerInverseRelation = jest.fn();
82+
83+
const testTableName = "TestTable";
84+
85+
lookForRelation(
86+
productTable.properties[6] as Field,
87+
testTableName,
88+
registerRelation,
89+
registerInverseRelation,
90+
);
91+
expect(registerInverseRelation).toHaveBeenCalledWith(
92+
"TestTable.Order",
93+
"many",
94+
);
95+
expect(registerRelation).not.toHaveBeenCalled();
96+
});
97+
98+
test("will register inverse relation with name", () => {
99+
const registerRelation = jest.fn();
100+
const registerInverseRelation = jest.fn();
101+
102+
const testTableName = "TestTable";
103+
104+
lookForRelation(
105+
namedRelationTable2.properties[5] as Field,
106+
testTableName,
107+
registerRelation,
108+
registerInverseRelation,
109+
);
110+
expect(registerInverseRelation).toHaveBeenCalledWith(
111+
'TestTable.Match."player1"',
112+
"many",
113+
);
114+
expect(registerRelation).not.toHaveBeenCalled();
115+
});
116+
});

0 commit comments

Comments
 (0)