diff --git a/packages/schema/src/language-server/validator/datamodel-validator.ts b/packages/schema/src/language-server/validator/datamodel-validator.ts index eb6d06400..6b27d2f9a 100644 --- a/packages/schema/src/language-server/validator/datamodel-validator.ts +++ b/packages/schema/src/language-server/validator/datamodel-validator.ts @@ -7,13 +7,7 @@ import { isEnum, isStringLiteral, } from '@zenstackhq/language/ast'; -import { - getLiteral, - getModelFieldsWithBases, - getModelIdFields, - getModelUniqueFields, - isDelegateModel, -} from '@zenstackhq/sdk'; +import { getModelFieldsWithBases, getModelIdFields, getModelUniqueFields, isDelegateModel } from '@zenstackhq/sdk'; import { AstNode, DiagnosticInfo, ValidationAcceptor, getDocument } from 'langium'; import { findUpInheritance } from '../../utils/ast-utils'; import { IssueCodes, SCALAR_TYPES } from '../constants'; @@ -147,15 +141,15 @@ export default class DataModelValidator implements AstValidator { } } + if (!fields && !references) { + return { attr: relAttr, name, fields, references, valid: true }; + } + if (!fields || !references) { - if (this.isSelfRelation(field, name)) { - // self relations are partial - // https://www.prisma.io/docs/concepts/components/prisma-schema/relations/self-relations - } else { - if (accept) { - accept('error', `Both "fields" and "references" must be provided`, { node: relAttr }); - } + if (accept) { + accept('error', `"fields" and "references" must be provided together`, { node: relAttr }); } + // } } else { // validate "fields" and "references" typing consistency if (fields.length !== references.length) { @@ -203,34 +197,8 @@ export default class DataModelValidator implements AstValidator { return { attr: relAttr, name, fields, references, valid }; } - private isSelfRelation(field: DataModelField, relationName?: string) { - if (field.type.reference?.ref === field.$container) { - // field directly references back to its type - return true; - } - - if (relationName) { - // field's relation points to another type, and that type's opposite relation field - // points back - const oppositeModel = field.type.reference?.ref as DataModel; - if (oppositeModel) { - const oppositeModelFields = getModelFieldsWithBases(oppositeModel); - for (const oppositeField of oppositeModelFields) { - // 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(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; - } - } - } - } - } - - return false; + private isSelfRelation(field: DataModelField) { + return field.type.reference?.ref === field.$container; } private validateRelationField(contextModel: DataModel, field: DataModelField, accept: ValidationAcceptor) { @@ -330,10 +298,10 @@ export default class DataModelValidator implements AstValidator { // if both the field is array, then it's an implicit many-to-many relation if (!(field.type.array && oppositeField.type.array)) { [field, oppositeField].forEach((f) => { - if (!this.isSelfRelation(f, thisRelation.name)) { + if (!this.isSelfRelation(f)) { accept( 'error', - 'Field for one side of relation must carry @relation attribute with both "fields" and "references" fields', + 'Field for one side of relation must carry @relation attribute with both "fields" and "references"', { node: f } ); } diff --git a/packages/schema/tests/schema/validation/datamodel-validation.test.ts b/packages/schema/tests/schema/validation/datamodel-validation.test.ts index e7dd6bf84..d89e96a44 100644 --- a/packages/schema/tests/schema/validation/datamodel-validation.test.ts +++ b/packages/schema/tests/schema/validation/datamodel-validation.test.ts @@ -451,7 +451,7 @@ describe('Data Model Validation Tests', () => { aId String } `) - ).toMatchObject(errorLike(`Both "fields" and "references" must be provided`)); + ).toMatchObject(errorLike(`"fields" and "references" must be provided together`)); // one-to-one inconsistent attribute expect( @@ -541,7 +541,7 @@ describe('Data Model Validation Tests', () => { `) ).toMatchObject( errorLike( - `Field for one side of relation must carry @relation attribute with both "fields" and "references" fields` + `Field for one side of relation must carry @relation attribute with both "fields" and "references"` ) );