Skip to content

Commit

Permalink
fix(delegate): base fields not properly wrapped inside relation layer…
Browse files Browse the repository at this point in the history
… when injected from policies (#1736)
  • Loading branch information
ymc9 authored Sep 23, 2024
1 parent 9fb8d5b commit bfe6983
Show file tree
Hide file tree
Showing 17 changed files with 179 additions and 39 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": "2.6.0",
"version": "2.6.1",
"description": "",
"scripts": {
"build": "pnpm -r build",
Expand Down
45 changes: 34 additions & 11 deletions packages/ide/jetbrains/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,38 +1,61 @@
# Changelog

## [Unreleased]

### Fixed

- ZModel validation issues when accessing fields defined in a base model from `future().` or `this.`.

## 2.5.0

### Added
- A new `path` parameter to the `@@validate` attribute for providing an optional path to the field that caused the error.

- A new `path` parameter to the `@@validate` attribute for providing an optional path to the field that caused the error.

## 2.4.0

### Added
- The `uuid()` function is updated to support the new UUID version feature from Prisma.

## 2.3.0
- The `uuid()` function is updated to support the new UUID version feature from Prisma.

## 2.3.0

### Added
- New `check()` policy rule function.

- New `check()` policy rule function.

### Fixed
- Fixed the issue with formatting schemas containing `Unsupported` type.

- Fixed the issue with formatting schemas containing `Unsupported` type.

## 2.2.0

### Added
- Support comparing fields from different models in mutation policy rules ("create", "update", and "delete).

- Support comparing fields from different models in mutation policy rules ("create", "update", and "delete).

## 2.1.0

### Added
- Support using ZModel type names (e.g., `DateTime`) as model field names.
- `auth()` is resolved from all reachable schema files.

- Support using ZModel type names (e.g., `DateTime`) as model field names.
- `auth()` is resolved from all reachable schema files.

## 2.0.0

### Added
- ZenStack V2 release!

- ZenStack V2 release!

## 1.11.0

### Added
- Added support to complex usage of `@@index` attribute like `@@index([content(ops: raw("gin_trgm_ops"))], type: Gin)`.

- Added support to complex usage of `@@index` attribute like `@@index([content(ops: raw("gin_trgm_ops"))], type: Gin)`.

### Fixed
- Fixed several ZModel validation issues related to model inheritance.

- Fixed several ZModel validation issues related to model inheritance.

## 1.7.0

Expand Down
2 changes: 1 addition & 1 deletion packages/ide/jetbrains/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ plugins {
}

group = "dev.zenstack"
version = "2.6.0"
version = "2.6.1"

repositories {
mavenCentral()
Expand Down
2 changes: 1 addition & 1 deletion packages/ide/jetbrains/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "jetbrains",
"version": "2.6.0",
"version": "2.6.1",
"displayName": "ZenStack JetBrains IDE Plugin",
"description": "ZenStack JetBrains IDE plugin",
"homepage": "https://zenstack.dev",
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": "2.6.0",
"version": "2.6.1",
"displayName": "ZenStack modeling language compiler",
"description": "ZenStack modeling language compiler",
"homepage": "https://zenstack.dev",
Expand Down
2 changes: 1 addition & 1 deletion packages/misc/redwood/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@zenstackhq/redwood",
"displayName": "ZenStack RedwoodJS Integration",
"version": "2.6.0",
"version": "2.6.1",
"description": "CLI and runtime for integrating ZenStack with RedwoodJS projects.",
"repository": {
"type": "git",
Expand Down
2 changes: 1 addition & 1 deletion packages/plugins/openapi/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@zenstackhq/openapi",
"displayName": "ZenStack Plugin and Runtime for OpenAPI",
"version": "2.6.0",
"version": "2.6.1",
"description": "ZenStack plugin and runtime supporting OpenAPI",
"main": "index.js",
"repository": {
Expand Down
2 changes: 1 addition & 1 deletion packages/plugins/swr/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@zenstackhq/swr",
"displayName": "ZenStack plugin for generating SWR hooks",
"version": "2.6.0",
"version": "2.6.1",
"description": "ZenStack plugin for generating SWR hooks",
"main": "index.js",
"repository": {
Expand Down
2 changes: 1 addition & 1 deletion packages/plugins/tanstack-query/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@zenstackhq/tanstack-query",
"displayName": "ZenStack plugin for generating tanstack-query hooks",
"version": "2.6.0",
"version": "2.6.1",
"description": "ZenStack plugin for generating tanstack-query hooks",
"main": "index.js",
"exports": {
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": "2.6.0",
"version": "2.6.1",
"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": "2.6.0",
"version": "2.6.1",
"description": "Runtime of ZenStack for both client-side and server-side environments.",
"repository": {
"type": "git",
Expand Down
38 changes: 24 additions & 14 deletions packages/runtime/src/enhancements/node/delegate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ export class DelegateProxyHandler extends DefaultPrismaProxyHandler {
}
}

private async buildSelectIncludeHierarchy(model: string, args: any) {
private async buildSelectIncludeHierarchy(model: string, args: any, includeConcreteFields = true) {
args = clone(args);
const selectInclude: any = this.extractSelectInclude(args) || {};

Expand All @@ -257,7 +257,10 @@ export class DelegateProxyHandler extends DefaultPrismaProxyHandler {

if (!selectInclude.select) {
this.injectBaseIncludeRecursively(model, selectInclude);
await this.injectConcreteIncludeRecursively(model, selectInclude);

if (includeConcreteFields) {
await this.injectConcreteIncludeRecursively(model, selectInclude);
}
}
return selectInclude;
}
Expand Down Expand Up @@ -342,19 +345,9 @@ export class DelegateProxyHandler extends DefaultPrismaProxyHandler {
for (const subModel of subModels) {
// include sub model relation field
const subRelationName = this.makeAuxRelationName(subModel);
const includePayload: any = {};

if (this.options.processIncludeRelationPayload) {
// use the callback in options to process the include payload, so enhancements
// like 'policy' can do extra work (e.g., inject policy rules)
await this.options.processIncludeRelationPayload(
this.prisma,
subModel.name,
includePayload,
this.options,
this.context
);
}
// create a payload to include the sub model relation
const includePayload = await this.createConcreteRelationIncludePayload(subModel.name);

if (selectInclude.select) {
selectInclude.include = { [subRelationName]: includePayload, ...selectInclude.select };
Expand All @@ -366,6 +359,23 @@ export class DelegateProxyHandler extends DefaultPrismaProxyHandler {
}
}

private async createConcreteRelationIncludePayload(model: string) {
let result: any = {};

if (this.options.processIncludeRelationPayload) {
// use the callback in options to process the include payload, so enhancements
// like 'policy' can do extra work (e.g., inject policy rules)
await this.options.processIncludeRelationPayload(this.prisma, model, result, this.options, this.context);

// the callback may directly reference fields from polymorphic bases, we need to fix it
// into a proper hierarchy by moving base field references to the base layer relations
const properHierarchy = await this.buildSelectIncludeHierarchy(model, result, false);
result = { ...result, ...properHierarchy };
}

return result;
}

// #endregion

// #region create
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": "FullStack enhancement for Prisma ORM: seamless integration from database to UI",
"version": "2.6.0",
"version": "2.6.1",
"author": {
"name": "ZenStack Team"
},
Expand Down
2 changes: 1 addition & 1 deletion packages/sdk/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@zenstackhq/sdk",
"version": "2.6.0",
"version": "2.6.1",
"description": "ZenStack plugin development SDK",
"main": "index.js",
"scripts": {
Expand Down
2 changes: 1 addition & 1 deletion packages/server/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@zenstackhq/server",
"version": "2.6.0",
"version": "2.6.1",
"displayName": "ZenStack Server-side Adapters",
"description": "ZenStack server-side adapters",
"homepage": "https://zenstack.dev",
Expand Down
2 changes: 1 addition & 1 deletion packages/testtools/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@zenstackhq/testtools",
"version": "2.6.0",
"version": "2.6.1",
"description": "ZenStack Test Tools",
"main": "index.js",
"private": true,
Expand Down
107 changes: 107 additions & 0 deletions tests/regression/tests/issue-1734.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import { loadSchema } from '@zenstackhq/testtools';
describe('issue 1734', () => {
it('regression', async () => {
const { enhance, enhanceRaw, prisma } = await loadSchema(
`
abstract model Base {
id String @id @default(cuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model Profile extends Base {
displayName String
type String
@@allow('read', true)
@@delegate(type)
}
model User extends Profile {
username String @unique
access Access[]
organization Organization[]
}
model Access extends Base {
user User @relation(fields: [userId], references: [id])
userId String
organization Organization @relation(fields: [organizationId], references: [id])
organizationId String
manage Boolean @default(false)
superadmin Boolean @default(false)
@@unique([userId,organizationId])
}
model Organization extends Profile {
owner User @relation(fields: [ownerId], references: [id])
ownerId String @default(auth().id)
published Boolean @default(false) @allow('read', access?[user == auth()])
access Access[]
}
`
);
const db = enhance();
const rootDb = enhanceRaw(prisma, undefined, {
kinds: ['delegate'],
});

const user = await rootDb.user.create({
data: {
username: 'test',
displayName: 'test',
},
});

const organization = await rootDb.organization.create({
data: {
displayName: 'test',
owner: {
connect: {
id: user.id,
},
},
access: {
create: {
user: {
connect: {
id: user.id,
},
},
manage: true,
superadmin: true,
},
},
},
});

const foundUser = await db.profile.findFirst({
where: {
id: user.id,
},
});
expect(foundUser).toMatchObject(user);

const foundOrg = await db.profile.findFirst({
where: {
id: organization.id,
},
});
// published field not readable
expect(foundOrg).toMatchObject({ id: organization.id, displayName: 'test', type: 'Organization' });
expect(foundOrg.published).toBeUndefined();

const foundOrg1 = await enhance({ id: user.id }).profile.findFirst({
where: {
id: organization.id,
},
});
// published field readable
expect(foundOrg1.published).not.toBeUndefined();
});
});

0 comments on commit bfe6983

Please sign in to comment.