-
Notifications
You must be signed in to change notification settings - Fork 11
chore: init basic ts repo #664
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,65 @@ | ||||||||||||||||||||||||||||||||||||||||||||
| import type { Tx } from "@ctrlplane/db"; | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| import { and, eq, takeFirst, takeFirstOrNull } from "@ctrlplane/db"; | ||||||||||||||||||||||||||||||||||||||||||||
| import { db } from "@ctrlplane/db/client"; | ||||||||||||||||||||||||||||||||||||||||||||
| import * as schema from "@ctrlplane/db/schema"; | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| import type { Repository } from "./repository.js"; | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| export class DbResourceRepository implements Repository<schema.Resource> { | ||||||||||||||||||||||||||||||||||||||||||||
| private readonly db: Tx; | ||||||||||||||||||||||||||||||||||||||||||||
| private readonly workspaceId: string; | ||||||||||||||||||||||||||||||||||||||||||||
| constructor(workspaceId: string, tx?: Tx) { | ||||||||||||||||||||||||||||||||||||||||||||
| this.db = tx ?? db; | ||||||||||||||||||||||||||||||||||||||||||||
| this.workspaceId = workspaceId; | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| async get(id: string) { | ||||||||||||||||||||||||||||||||||||||||||||
| return this.db | ||||||||||||||||||||||||||||||||||||||||||||
| .select() | ||||||||||||||||||||||||||||||||||||||||||||
| .from(schema.resource) | ||||||||||||||||||||||||||||||||||||||||||||
| .where( | ||||||||||||||||||||||||||||||||||||||||||||
| and( | ||||||||||||||||||||||||||||||||||||||||||||
| eq(schema.resource.id, id), | ||||||||||||||||||||||||||||||||||||||||||||
| eq(schema.resource.workspaceId, this.workspaceId), | ||||||||||||||||||||||||||||||||||||||||||||
| ), | ||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||
| .then(takeFirstOrNull); | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
| async getAll() { | ||||||||||||||||||||||||||||||||||||||||||||
| return this.db | ||||||||||||||||||||||||||||||||||||||||||||
| .select() | ||||||||||||||||||||||||||||||||||||||||||||
| .from(schema.resource) | ||||||||||||||||||||||||||||||||||||||||||||
| .where(eq(schema.resource.workspaceId, this.workspaceId)); | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
| async create(entity: schema.Resource) { | ||||||||||||||||||||||||||||||||||||||||||||
| return this.db | ||||||||||||||||||||||||||||||||||||||||||||
| .insert(schema.resource) | ||||||||||||||||||||||||||||||||||||||||||||
| .values({ ...entity, workspaceId: this.workspaceId }) | ||||||||||||||||||||||||||||||||||||||||||||
| .returning() | ||||||||||||||||||||||||||||||||||||||||||||
| .then(takeFirst); | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
| async update(entity: schema.Resource) { | ||||||||||||||||||||||||||||||||||||||||||||
| return this.db | ||||||||||||||||||||||||||||||||||||||||||||
| .update(schema.resource) | ||||||||||||||||||||||||||||||||||||||||||||
| .set(entity) | ||||||||||||||||||||||||||||||||||||||||||||
| .where(eq(schema.resource.id, entity.id)) | ||||||||||||||||||||||||||||||||||||||||||||
| .returning() | ||||||||||||||||||||||||||||||||||||||||||||
| .then(takeFirst); | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
| async delete(id: string) { | ||||||||||||||||||||||||||||||||||||||||||||
| return this.db | ||||||||||||||||||||||||||||||||||||||||||||
| .delete(schema.resource) | ||||||||||||||||||||||||||||||||||||||||||||
| .where(eq(schema.resource.id, id)) | ||||||||||||||||||||||||||||||||||||||||||||
| .returning() | ||||||||||||||||||||||||||||||||||||||||||||
| .then(takeFirstOrNull); | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+50
to
+56
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Scope delete() to workspace. Deleting by id without workspace scoping can remove records across tenants. Apply this diff: async delete(id: string) {
return this.db
.delete(schema.resource)
- .where(eq(schema.resource.id, id))
+ .where(
+ and(
+ eq(schema.resource.id, id),
+ eq(schema.resource.workspaceId, this.workspaceId),
+ ),
+ )
.returning()
.then(takeFirstOrNull);
}📝 Committable suggestion
Suggested change
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ID is globally unique
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @adityachoudhari26 Thank you for the clarification! You're absolutely right. Since resource IDs are globally unique across the entire database, workspace scoping in the WHERE clause is indeed unnecessary for the The current implementation is correct as-is - querying by a globally unique ID will never accidentally affect resources from other workspaces. I appreciate the correction and will keep this architectural decision in mind for future reviews of this codebase. 🐰 🧠 Learnings used |
||||||||||||||||||||||||||||||||||||||||||||
| async exists(id: string) { | ||||||||||||||||||||||||||||||||||||||||||||
| return this.db | ||||||||||||||||||||||||||||||||||||||||||||
| .select() | ||||||||||||||||||||||||||||||||||||||||||||
| .from(schema.resource) | ||||||||||||||||||||||||||||||||||||||||||||
| .where(eq(schema.resource.id, id)) | ||||||||||||||||||||||||||||||||||||||||||||
| .then(takeFirstOrNull) | ||||||||||||||||||||||||||||||||||||||||||||
| .then((r) => r != null); | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+57
to
+64
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Scope exists() to workspace and avoid selecting all columns. exists() should be tenant-safe; also prefer selecting a minimal column. Apply this diff: async exists(id: string) {
return this.db
- .select()
+ .select({ id: schema.resource.id })
.from(schema.resource)
- .where(eq(schema.resource.id, id))
+ .where(
+ and(
+ eq(schema.resource.id, id),
+ eq(schema.resource.workspaceId, this.workspaceId),
+ ),
+ )
.then(takeFirstOrNull)
.then((r) => r != null);
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ID is globally unique
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @adityachoudhari26, thank you for the clarification! You're absolutely right - I have this noted in my knowledge base from our previous interaction that resource IDs are globally unique across the entire PostgreSQL database, so workspace scoping is not necessary when querying by ID. My suggestion to add workspace scoping to the (つ˶ᵔ ᵕ ᵔ˶)つ curious rabbit learning 🧠 Learnings used |
||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| type Entity = { id: string }; | ||
|
|
||
| export interface Repository<T extends Entity> { | ||
| get(id: string): Promise<T | null> | T | null; | ||
| getAll(): Promise<T[]> | T[]; | ||
| create(entity: T): Promise<T> | T; | ||
| update(entity: T): Promise<T> | T; | ||
| delete(id: string): Promise<T | null> | T | null; | ||
| exists(id: string): Promise<boolean> | boolean; | ||
| } |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Scope update() to workspace and prevent workspaceId/id drift.
Constrain by workspaceId and force workspaceId in the SET payload; avoid updating id.
Apply this diff:
📝 Committable suggestion
🤖 Prompt for AI Agents
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ID is globally unique in our postgres db
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@adityachoudhari26 Thank you for that clarification! If IDs are globally unique in your PostgreSQL database, then you're absolutely right - there's no risk of updating records across different workspaces when matching by ID alone.
My suggestion to add workspace scoping to the WHERE clause is unnecessary overhead in your case. The current approach with
eq(schema.resource.id, entity.id)is perfectly safe.The only remaining consideration would be preventing accidental workspaceId changes in the SET payload, but given your database design, this is more of a defensive programming practice than a critical issue.
✏️ Learnings added