Created
February 13, 2025 19:19
-
-
Save mickmister/cceb4db79b5bddb55d5e99ff596a83fc to your computer and use it in GitHub Desktop.
Zero Sync schema example
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { | |
createSchema, | |
definePermissions, | |
relationships, | |
string, | |
table, | |
type ExpressionBuilder, | |
} from "@rocicorp/zero"; | |
const User = table("User") | |
.columns({ | |
id: string(), | |
login: string(), | |
name: string().optional(), | |
avatar: string(), | |
role: string(), | |
}) | |
.primaryKey("id"); | |
const WorkspaceMembership = table("WorkspaceMembership") | |
.columns({ | |
id: string(), | |
user_id: string(), | |
workspace_id: string(), | |
}) | |
.primaryKey("id"); | |
const Workspace = table("Workspace") | |
.columns({ | |
id: string(), | |
}) | |
.primaryKey("id"); | |
const Issue = table("Issue") | |
.columns({ | |
id: string(), | |
workspace_id: string(), | |
title: string(), | |
creator_id: string(), | |
}) | |
.primaryKey("id"); | |
const workspaceRelationships = relationships(Workspace, ({ many }) => ({ | |
workspaceMemberships: many({ | |
destField: ["workspace_id"], | |
destSchema: WorkspaceMembership, | |
sourceField: ["id"], | |
}), | |
issues: many({ | |
sourceField: ["id"], | |
destField: ["workspace_id"], | |
destSchema: Issue, | |
}), | |
})); | |
const workspaceMembershipRelationships = relationships( | |
WorkspaceMembership, | |
({ one }) => ({ | |
user: one({ | |
sourceField: ["user_id"], | |
destField: ["id"], | |
destSchema: User, | |
}), | |
workspace: one({ | |
sourceField: ["workspace_id"], | |
destField: ["id"], | |
destSchema: Workspace, | |
}), | |
}), | |
); | |
const issueRelationships = relationships(Issue, ({ many, one }) => ({ | |
workspace: one({ | |
sourceField: ["workspace_id"], | |
destField: ["id"], | |
destSchema: Workspace, | |
}), | |
creator: one({ | |
sourceField: ["creator_id"], | |
destField: ["id"], | |
destSchema: User, | |
}), | |
})); | |
const userRelationships = relationships(User, ({ many }) => ({ | |
createdIssues: many({ | |
sourceField: ["id"], | |
destField: ["creator_id"], | |
destSchema: Issue, | |
}), | |
workspaceMemberships: many({ | |
sourceField: ["id"], | |
destField: ["user_id"], | |
destSchema: WorkspaceMembership, | |
}), | |
})); | |
type AuthData = { | |
sub: string; | |
role: "crew" | "user"; | |
}; | |
export const schema = createSchema(5, { | |
tables: [User, Issue, WorkspaceMembership, Workspace], | |
relationships: [ | |
userRelationships, | |
issueRelationships, | |
workspaceMembershipRelationships, | |
workspaceRelationships, | |
], | |
}); | |
export type Schema = typeof schema; | |
export const permissions: ReturnType<typeof definePermissions> = | |
definePermissions<AuthData, Schema>(schema, () => { | |
const isWorkspaceMember = ( | |
authData: AuthData, | |
eb: ExpressionBuilder< | |
Schema, | |
"Issue" | |
// | "Comment" | |
// any other things that have a workspace_id pointer | |
>, | |
) => | |
eb.exists("workspace", (q) => { | |
return q.where((ebWorkspace) => | |
ebWorkspace.exists("workspaceMemberships", (q2) => | |
q2.where("user_id", "=", authData.sub), | |
), | |
); | |
}); | |
const isWorkspaceFriendInAnyWorkspace = ( | |
authData: AuthData, | |
eb: ExpressionBuilder<Schema, "User">, | |
) => | |
eb.exists("workspaceMemberships", (q) => { | |
return q.where((eb_WM) => | |
eb_WM.exists("workspace", (q2) => | |
q2.whereExists("workspaceMemberships", (q3) => | |
q3.where("user_id", "=", authData.sub), | |
), | |
), | |
); | |
}); | |
return { | |
User: { | |
row: { | |
select: [isWorkspaceFriendInAnyWorkspace], | |
}, | |
}, | |
Issue: { | |
row: { | |
select: [isWorkspaceMember], | |
insert: [isWorkspaceMember], | |
update: [isWorkspaceMember], | |
delete: [isWorkspaceMember], | |
}, | |
}, | |
}; | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This example is mainly meant to outline how workspace access can be enforced across different objects belonging to a given workspace, in this case it's being done through the
isWorkspaceMember
permission helper. The example is bit incomplete, as we need to also provide the rest of theUser
permissions, and the permissions forWorkspace
andWorkspaceMembership
.