Skip to content

Commit

Permalink
feat: add prisma passthrough attribute for working around discripanci…
Browse files Browse the repository at this point in the history
…es between zmodel and prisma (#245)
  • Loading branch information
ymc9 authored Mar 7, 2023
1 parent 93cb6bf commit cef96d4
Show file tree
Hide file tree
Showing 26 changed files with 1,618 additions and 90 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "zenstack-monorepo",
"version": "1.0.0-alpha.58",
"version": "1.0.0-alpha.60",
"description": "",
"scripts": {
"build": "pnpm -r build",
Expand Down
2 changes: 1 addition & 1 deletion packages/language/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@zenstackhq/language",
"version": "1.0.0-alpha.58",
"version": "1.0.0-alpha.60",
"displayName": "ZenStack modeling language compiler",
"description": "ZenStack modeling language compiler",
"homepage": "https://zenstack.dev",
Expand Down
2 changes: 1 addition & 1 deletion packages/next/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@zenstackhq/next",
"version": "1.0.0-alpha.58",
"version": "1.0.0-alpha.60",
"displayName": "ZenStack Next.js integration",
"description": "ZenStack Next.js integration",
"homepage": "https://zenstack.dev",
Expand Down
2 changes: 1 addition & 1 deletion packages/plugins/react/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@zenstackhq/react",
"displayName": "ZenStack plugin and runtime for ReactJS",
"version": "1.0.0-alpha.58",
"version": "1.0.0-alpha.60",
"description": "ZenStack plugin and runtime for ReactJS",
"main": "index.js",
"repository": {
Expand Down
2 changes: 1 addition & 1 deletion packages/plugins/trpc/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@zenstackhq/trpc",
"displayName": "ZenStack plugin for tRPC",
"version": "1.0.0-alpha.58",
"version": "1.0.0-alpha.60",
"description": "ZenStack plugin for tRPC",
"main": "index.js",
"repository": {
Expand Down
2 changes: 1 addition & 1 deletion packages/runtime/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@zenstackhq/runtime",
"displayName": "ZenStack Runtime Library",
"version": "1.0.0-alpha.58",
"version": "1.0.0-alpha.60",
"description": "Runtime of ZenStack for both client-side and server-side environments.",
"repository": {
"type": "git",
Expand Down
2 changes: 1 addition & 1 deletion packages/schema/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"publisher": "zenstack",
"displayName": "ZenStack Language Tools",
"description": "A toolkit for building secure CRUD apps with Next.js + Typescript",
"version": "1.0.0-alpha.58",
"version": "1.0.0-alpha.60",
"author": {
"name": "ZenStack Team"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { IssueCodes, SCALAR_TYPES } from '../constants';
import { AstValidator } from '../types';
import { getIdFields, getUniqueFields } from '../utils';
import { validateAttributeApplication, validateDuplicatedDeclarations } from './utils';
import { getLiteral } from '@zenstackhq/sdk';

/**
* Validates data model declarations.
Expand Down Expand Up @@ -174,13 +175,15 @@ export default class DataModelValidator implements AstValidator<DataModel> {
const oppositeModelFields = field.type.reference?.ref?.fields as DataModelField[];
if (oppositeModelFields) {
for (const oppositeField of oppositeModelFields) {
const { name: oppositeRelationName } = this.parseRelation(oppositeField);
if (
oppositeRelationName === relationName &&
oppositeField.type.reference?.ref === field.$container
) {
// found an opposite relation field that points back to this field's type
return true;
// find the opposite relation with the matching name
const relAttr = oppositeField.attributes.find((a) => a.decl.ref?.name === '@relation');
if (relAttr) {
const relNameExpr = relAttr.args.find((a) => !a.name || a.name === 'name');
const relName = getLiteral<string>(relNameExpr?.value);
if (relName === relationName && oppositeField.type.reference?.ref === field.$container) {
// found an opposite relation field that points back to this field's type
return true;
}
}
}
}
Expand Down Expand Up @@ -253,13 +256,15 @@ export default class DataModelValidator implements AstValidator<DataModel> {
relationOwner = field;
}
} else {
[field, oppositeField].forEach((f) =>
accept(
'error',
'Field for one side of relation must carry @relation attribute with both "fields" and "references" fields',
{ node: f }
)
);
[field, oppositeField].forEach((f) => {
if (!this.isSelfRelation(f, thisRelation.name)) {
accept(
'error',
'Field for one side of relation must carry @relation attribute with both "fields" and "references" fields',
{ node: f }
);
}
});
return;
}

Expand Down
70 changes: 50 additions & 20 deletions packages/schema/src/plugins/prisma/prisma-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ export class Generator {
}

export class DeclarationBase {
public documentations: string[] = [];
constructor(public documentations: string[] = []) {}

addComment(name: string): string {
this.documentations.push(name);
Expand All @@ -91,26 +91,38 @@ export class DeclarationBase {
return this.documentations.map((x) => `${x}\n`).join('');
}
}
export class Model extends DeclarationBase {

export class ContainerDeclaration extends DeclarationBase {
constructor(documentations: string[] = [], public attributes: (ContainerAttribute | PassThroughAttribute)[] = []) {
super(documentations);
}
}

export class FieldDeclaration extends DeclarationBase {
constructor(documentations: string[] = [], public attributes: (FieldAttribute | PassThroughAttribute)[] = []) {
super(documentations);
}
}

export class Model extends ContainerDeclaration {
public fields: ModelField[] = [];
public attributes: ModelAttribute[] = [];
constructor(public name: string, public documentations: string[] = []) {
super();
constructor(public name: string, documentations: string[] = []) {
super(documentations);
}

addField(
name: string,
type: ModelFieldType | string,
attributes: FieldAttribute[] = [],
attributes: (FieldAttribute | PassThroughAttribute)[] = [],
documentations: string[] = []
): ModelField {
const field = new ModelField(name, type, attributes, documentations);
this.fields.push(field);
return field;
}

addAttribute(name: string, args: AttributeArg[] = []): ModelAttribute {
const attr = new ModelAttribute(name, args);
addAttribute(name: string, args: AttributeArg[] = []) {
const attr = new ContainerAttribute(name, args);
this.attributes.push(attr);
return attr;
}
Expand Down Expand Up @@ -145,14 +157,14 @@ export class ModelFieldType {
}
}

export class ModelField extends DeclarationBase {
export class ModelField extends FieldDeclaration {
constructor(
public name: string,
public type: ModelFieldType | string,
public attributes: FieldAttribute[] = [],
public documentations: string[] = []
attributes: (FieldAttribute | PassThroughAttribute)[] = [],
documentations: string[] = []
) {
super();
super(documentations, attributes);
}

addAttribute(name: string, args: AttributeArg[] = []): FieldAttribute {
Expand All @@ -178,14 +190,25 @@ export class FieldAttribute {
}
}

export class ModelAttribute {
export class ContainerAttribute {
constructor(public name: string, public args: AttributeArg[] = []) {}

toString(): string {
return `${this.name}(` + this.args.map((a) => a.toString()).join(', ') + `)`;
}
}

/**
* Represents @@prisma.passthrough and @prisma.passthrough
*/
export class PassThroughAttribute {
constructor(public text: string) {}

toString(): string {
return this.text;
}
}

export class AttributeArg {
constructor(public name: string | undefined, public value: AttributeArgValue) {}

Expand Down Expand Up @@ -287,22 +310,25 @@ export class FunctionCallArg {
}
}

export class Enum extends DeclarationBase {
export class Enum extends ContainerDeclaration {
public fields: EnumField[] = [];
public attributes: ModelAttribute[] = [];

constructor(public name: string, public documentations: string[] = []) {
super();
super(documentations);
}

addField(name: string, attributes: FieldAttribute[] = [], documentations: string[] = []): EnumField {
addField(
name: string,
attributes: (FieldAttribute | PassThroughAttribute)[] = [],
documentations: string[] = []
): EnumField {
const field = new EnumField(name, attributes, documentations);
this.fields.push(field);
return field;
}

addAttribute(name: string, args: AttributeArg[] = []): ModelAttribute {
const attr = new ModelAttribute(name, args);
addAttribute(name: string, args: AttributeArg[] = []) {
const attr = new ContainerAttribute(name, args);
this.attributes.push(attr);
return attr;
}
Expand All @@ -323,7 +349,11 @@ export class Enum extends DeclarationBase {
}

export class EnumField extends DeclarationBase {
constructor(public name: string, public attributes: FieldAttribute[] = [], public documentations: string[] = []) {
constructor(
public name: string,
public attributes: (FieldAttribute | PassThroughAttribute)[] = [],
public documentations: string[] = []
) {
super();
}

Expand Down
Loading

0 comments on commit cef96d4

Please sign in to comment.