Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ZModel language #7065

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -1425,6 +1425,9 @@
[submodule "vendor/grammars/zeek-sublime"]
path = vendor/grammars/zeek-sublime
url = https://github.com/zeek/zeek-sublime
[submodule "vendor/grammars/zenstack"]
path = vendor/grammars/zenstack
url = https://github.com/zenstackhq/zenstack.git
[submodule "vendor/grammars/zephir-sublime"]
path = vendor/grammars/zephir-sublime
url = https://github.com/phalcon/zephir-sublime
2 changes: 2 additions & 0 deletions grammars.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1292,5 +1292,7 @@ vendor/grammars/xml.tmbundle:
- text.xml.xsl
vendor/grammars/zeek-sublime:
- source.zeek
vendor/grammars/zenstack:
- source.zmodel
vendor/grammars/zephir-sublime:
- source.php.zephir
8 changes: 8 additions & 0 deletions lib/linguist/languages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8632,3 +8632,11 @@ xBase:
tm_scope: source.harbour
ace_mode: text
language_id: 421
ZenStack:
jiashengguo marked this conversation as resolved.
Show resolved Hide resolved
type: data
color: "#ff7100"
extensions:
- ".zmodel"
tm_scope: source.zmodel
ace_mode: text
language_id: 1012829740
jiashengguo marked this conversation as resolved.
Show resolved Hide resolved
88 changes: 88 additions & 0 deletions samples/ZenStack/blog.zmodel
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema

generator client {
provider = "prisma-client-js"
}

datasource db {
provider = "sqlite"
// NOTE: When using mysql or sqlserver, uncomment the @db.Text annotations in model Account below
// Further reading:
// https://next-auth.js.org/adapters/prisma#create-the-prisma-schema
// https://www.prisma.io/docs/reference/api-reference/prisma-schema-reference#string
url = env("DATABASE_URL")
}

plugin hooks {
provider = '@zenstackhq/tanstack-query'
target = 'react'
version = 'v5'
output = "./src/lib/hooks"
}

model Post {
id Int @id @default(autoincrement())
name String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt

createdBy User @relation(fields: [createdById], references: [id])
createdById String

@@index([name])
}

// Necessary for Next auth
model Account {
id String @id @default(cuid())
userId String
type String
provider String
providerAccountId String
refresh_token String? // @db.Text
access_token String? // @db.Text
expires_at Int?
token_type String?
scope String?
id_token String? // @db.Text
session_state String?
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
refresh_token_expires_in Int?

@@unique([provider, providerAccountId])
}

model Session {
id String @id @default(cuid())
sessionToken String @unique
userId String
expires DateTime
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
}

model User {
id String @id @default(cuid())
name String?
email String? @unique
emailVerified DateTime?
password String @password @omit
image String?
accounts Account[]
sessions Session[]
posts Post[]

// everyone can signup, and user profile is also publicly readable
@@allow('create,read', true)

// only the user can update or delete their own profile
@@allow('update,delete', auth() == this)
}

model VerificationToken {
identifier String
token String @unique
expires DateTime

@@unique([identifier, token])
}
96 changes: 96 additions & 0 deletions samples/ZenStack/saas.zmodel
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
datasource db {
provider="sqlite"
url="file:./dev.db"
}

generator client {
provider = "prisma-client-js"
}

/**
* Model for a user
*/
model User {
id String @id @default(uuid())
email String @unique
password String? @password @omit
name String?
orgs Organization[]
posts Post[]
groups Group[]

// can be created by anyone, even not logged in
@@allow('create', true)
// can be read by users in the same organization
@@allow('read', orgs?[members?[auth() == this]])
// full access by oneself
@@allow('all', auth() == this)
}

/**
* Model for a organization
*/
model Organization {
id String @id @default(uuid())
name String
members User[]
post Post[]
groups Group[]

// everyone can create a organization
@@allow('create', true)
// any user in the organization can read the organization
@@allow('read', members?[auth().id == id])
}

/**
* Base model for all entites in a organization
*/
abstract model organizationBaseEntity {
id String @id @default(uuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
isDeleted Boolean @default(false) @omit
isPublic Boolean @default(false)
owner User @relation(fields: [ownerId], references: [id], onDelete: Cascade)
ownerId String
org Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)
orgId String
groups Group[]

// when create, owner must be set to current user, and user must be in the organization
@@allow('create', owner == auth() && org.members?[id == auth().id])
// only the owner can update it and is not allowed to change the owner
@@allow('update', owner == auth() && org.members?[id == auth().id] && future().owner == owner)
// allow owner to read
@@allow('read', owner == auth())
// allow shared group members to read it
@@allow('read', groups?[users?[id == auth().id]])
// allow organization to access if public
@@allow('read', isPublic && org.members?[id == auth().id])
// can not be read if deleted
@@deny('all', isDeleted == true)
}

/**
* Model for a post
*/
model Post extends organizationBaseEntity {
title String
content String
}

/**
* Model for a group
*/
model Group {
id String @id @default(uuid())
name String
users User[]
posts Post[]
org Organization @relation(fields: [orgId], references: [id])
orgId String

// group is shared by organization
@@allow('all', org.members?[auth().id == id])
}
178 changes: 178 additions & 0 deletions samples/ZenStack/todo.zmodel
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
/*
* Sample model for a collaborative Todo app
*/

datasource db {
provider = 'sqlite'
url = 'file:./dev.db'
}

generator js {
provider = 'prisma-client-js'
}

plugin enhancer {
provider = '@core/enhancer'
generatePermissionChecker = true
}

plugin hooks {
provider = '@zenstackhq/swr'
output = 'lib/hooks'
}


/**
* Model for a space in which users can collaborate on Lists and Todos
*/
model Space {
id String @id @default(uuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
name String @length(4, 50)
slug String @unique @regex('^[0-9a-zA-Z]{4,16}$')
members SpaceUser[]
lists List[]

// require login
@@deny('all', auth() == null)

// everyone can create a space
@@allow('create', true)

// any user in the space can read the space
@@allow('read', members?[user == auth()])

// space admin can update and delete
@@allow('update,delete', members?[user == auth() && role == 'ADMIN'])

@@deny('update', future().members?[user == auth()])
}

/**
* Model representing membership of a user in a space
*/
model SpaceUser {
id String @id @default(uuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
space Space @relation(fields: [spaceId], references: [id], onDelete: Cascade)
spaceId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
userId String
role String
@@unique([userId, spaceId])

// require login
@@deny('all', auth() == null)

// space admin can create/update/delete
@@allow('create,update,delete', space.members?[user == auth() && role == 'ADMIN'])

// user can read entries for spaces which he's a member of
@@allow('read', space.members?[user == auth()])
}

/**
* Model for a user
*/
model User {
id String @id @default(uuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
email String @unique @email
emailVerified DateTime?
password String? @password @omit
name String?
spaces SpaceUser[]
image String? @url
lists List[]
todos Todo[]

// next-auth
accounts Account[]

// can be created by anyone, even not logged in
@@allow('create', true)

// can be read by users sharing any space
@@allow('read', spaces?[space.members?[user == auth()]])

// full access by oneself
@@allow('all', auth() == this)
}

abstract model BaseEntity {
id String @id @default(uuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt

space Space @relation(fields: [spaceId], references: [id], onDelete: Cascade)
spaceId String
owner User @relation(fields: [ownerId], references: [id], onDelete: Cascade)
ownerId String @default(auth().id)

// can be read by owner or space members
@@allow('read', owner == auth() || (space.members?[user == auth()]))

// when create, owner must be set to current user, and user must be in the space
@@allow('create', owner == auth() && space.members?[user == auth()])

// when create, owner must be set to current user, and user must be in the space
// update is not allowed to change owner
@@allow('update', owner == auth() && space.members?[user == auth()] && future().owner == owner)

// can be deleted by owner
@@allow('delete', owner == auth())
}

/**
* Model for a Todo list
*/
model List extends BaseEntity {
title String @length(1, 100)
private Boolean @default(false)
todos Todo[]

// can't be read by others if it's private
@@deny('read', private == true && owner != auth())
}

/**
* Model for a single Todo
*/
model Todo {
id String @id @default(uuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
owner User @relation(fields: [ownerId], references: [id], onDelete: Cascade)
ownerId String @default(auth().id)
list List @relation(fields: [listId], references: [id], onDelete: Cascade)
listId String
title String @length(1, 100)
completedAt DateTime?

// full access if the parent list is readable
@@allow('all', check(list, 'read'))
}

/**
* Next-auth user account
*/
model Account {
id String @id @default(uuid())
userId String
type String
provider String
providerAccountId String
refresh_token String?
refresh_token_expires_in Int?
access_token String?
expires_at Int?
token_type String?
scope String?
id_token String?
session_state String?
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@unique([provider, providerAccountId])
}
Loading