zenstackhq / zenstack

Fullstack TypeScript toolkit that enhances Prisma ORM with flexible Authorization layer for RBAC/ABAC/PBAC/ReBAC, offering auto-generated type-safe APIs and frontend hooks.
https://zenstack.dev
MIT License
2.07k stars 88 forks source link

Model-level validation corrupts field date serialization/validation for RESTful handler #1067

Closed vgarmash closed 7 months ago

vgarmash commented 7 months ago

We use CRUD API generated by the Zenstack with RESTful handler. We have never used the Model-level validation in our schema and decided to enable couple rules in one model. Apparently, there is some problem in Zod configuration or serialization layer in version 1.9.0. Without model-level validation rules we are able to add new entry via REST API but once we add " @@validate" to the model it start throwing us error 403 with Zod validation message for fields with DateTime type.

schema.zmodel:

generator client {
  provider        = "prisma-client-js"
  previewFeatures = ["driverAdapters", "postgresqlExtensions", "views", "tracing"]
}

datasource db {
  provider    = "postgresql"
  url         = env("DATABASE_URL_WITH_SCHEMA")
  extensions  = [uuidOssp(map: "uuid-ossp")]
}

plugin openapi {
  provider = '@zenstackhq/openapi'
  flavor = 'rest'
  output = './dist/crud-api.json'
  title = 'CRUD REST API'
  version = '1.0.0'
  summary = 'CRUD API'
  description = 'This API can be used to make calls to the database using REST. Created with ZenStack.'
  prefix = '/api/crud'
  securitySchemes = {
        bearer: { type: 'http', scheme: 'bearer', bearerFormat: 'JWT' }
    }
}

model participant_interaction {
  participant_interaction_id String @id @default(dbgenerated("uuid_generate_v4()")) @db.Uuid

  summary String?

  interaction_start DateTime? @db.Timestamp(0)
  interaction_duration_minutes Int?

  coach_id String @db.Uuid
  participant_id String @db.Uuid

  created_by String?
  created_timestamp DateTime? @db.Timestamp(6) @default(now())
  last_modified_by String?
  updated_timestamp DateTime? @db.Timestamp(6) @updatedAt()
  mongo_db_id String? @db.VarChar(255) @allow("update", false)

  //Columns for Legacy interaction type
  legacy_interaction_task_type InteractionTaskType?

  //Model-level validations
  @@validate(true, "You should never see this message")

  //Everything allowed (no auth policies yet)
  @@allow('all', true)
}

Request:

POST /api/crud/participant_interaction HTTP/1.1
Host: localhost:3000
Content-Type: application/json
Authorization: Bearer <omitted>

{
  "data": {
    "type": "participant_forms",
    "attributes": {
      "participant_id": "d4a4ed94-b71b-4596-bc30-e13bc6f16ed1",
      "coach_id": "67f9366a-6aa9-4765-838a-07dc1afd0b65",
      "summary": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla non neque mollis, placerat risus sit amet, vehicula justo. Integer in ipsum id tortor porta malesuada. Maecenas ac neque ex. Morbi non est id risus posuere luctus. Vestibulum ac euismod felis. Sed eget imperdiet lorem. Pellentesque id accumsan enim.",
      "interaction_start": "2024-03-02T05:00:00.000Z",
      "interaction_duration_minutes":25,
    }
  }
}

Response:

{
    "errors": [
        {
            "status": 403,
            "code": "forbidden",
            "title": "Operation is forbidden",
            "reason": "DATA_VALIDATION_VIOLATION",
            "zodErrors": {
                "issues": [
                    {
                        "code": "invalid_type",
                        "expected": "date",
                        "received": "string",
                        "path": [
                            "interaction_start"
                        ],
                        "message": "Expected date, received string"
                    }
                ],
                "name": "ZodError"
            }
        }
    ]
}

As soon as I remove "@@validation" from our model, everything works properly.

Environment: package.json tsconfig.json

ymc9 commented 7 months ago

Hi @vgarmash , thanks for reporting this. I'll look into it and update back here soon.

ymc9 commented 7 months ago

Hi @vgarmash , thanks for reporting this. I'll look into it and update back here soon.

When @@validate is used, a zod schema is generated and used to validate the input data. Currently the schema is too strict for DateTime field and requires a Date object. I've made a fix to allow compatible strings (via z.coerce.date). It's included in the latest v1.10.0 release. Please help check if it's working for you. Thanks!