Initial project import
This commit is contained in:
commit
23f2d29e32
11957 files changed
+1754577
No files matched your search
@@ -0,0 +1,67 @@
|
||||
module main
|
||||
|
||||
import db.sqlite
|
||||
import time
|
||||
import orm
|
||||
|
||||
@[table: 'sys_users']
|
||||
struct User {
|
||||
pub:
|
||||
id string @[immutable; primary; sql: 'id'; sql_type: 'VARCHAR(255)'; unique]
|
||||
name ?string @[immutable; sql: 'nick_name'; sql_type: 'VARCHAR(255)'; unique]
|
||||
created_at ?time.Time @[omitempty; sql_type: 'TIMESTAMP']
|
||||
updated_at time.Time @[default: new; omitempty; sql_type: 'TIMESTAMP']
|
||||
}
|
||||
|
||||
fn main() {
|
||||
mut db := sqlite.connect(':memory:')!
|
||||
defer { db.close() or {} }
|
||||
|
||||
user1 := User{
|
||||
id: '001'
|
||||
name: 'Jengro'
|
||||
created_at: time.now()
|
||||
updated_at: time.now()
|
||||
}
|
||||
|
||||
user2 := User{
|
||||
id: '002'
|
||||
name: 'Dev'
|
||||
created_at: time.now()
|
||||
updated_at: time.now()
|
||||
}
|
||||
|
||||
mut qb := orm.new_query[User](db)
|
||||
|
||||
// create table
|
||||
qb.create()!
|
||||
|
||||
// insert into table
|
||||
qb.insert(user1)!
|
||||
qb.insert(user2)!
|
||||
|
||||
// query all fields
|
||||
all_users := qb.query()!
|
||||
dump(all_users)
|
||||
|
||||
// query all users' nick_name
|
||||
all_user_names := qb.select('nick_name')!.query()!
|
||||
dump(all_user_names)
|
||||
|
||||
// where
|
||||
selected_users := qb.where('id = ?', '001')!.query()!
|
||||
dump(selected_users)
|
||||
|
||||
// update
|
||||
qb.set('nick_name = ?', 'Tom')!.where('id = ?', '001')!.update()!
|
||||
updated_user := qb.query()!
|
||||
dump(updated_user)
|
||||
|
||||
// delete
|
||||
qb.where('id = ?', '001')!.delete()!
|
||||
remain_users := qb.query()!
|
||||
dump(remain_users)
|
||||
|
||||
// drop table
|
||||
qb.drop()!
|
||||
}
|
||||
@@ -0,0 +1,269 @@
|
||||
// orm_scope_middleware.v - ORM middleware pattern demo
|
||||
//
|
||||
// Shows how middleware manages DataScope (multi-tenant) transparently.
|
||||
// Business code only changes acquire() -> pool_acquire_scoped().
|
||||
//
|
||||
// Run: v run orm_scope_middleware.v
|
||||
|
||||
module main
|
||||
|
||||
import db.sqlite
|
||||
import orm
|
||||
|
||||
// =============================================================================
|
||||
// Model
|
||||
// =============================================================================
|
||||
|
||||
@[table: 'sys_users']
|
||||
struct SysUser {
|
||||
id int @[primary; sql: serial]
|
||||
name string
|
||||
tenant_id int
|
||||
org_id int
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Request context
|
||||
// =============================================================================
|
||||
|
||||
struct UserInfo {
|
||||
mut:
|
||||
role string
|
||||
tenant_id int
|
||||
}
|
||||
|
||||
pub struct Context {
|
||||
pub mut:
|
||||
user UserInfo
|
||||
dbpool &DatabasePool
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Connection pool (SQLite version)
|
||||
// In real projects, use adapter.dbpool.DatabasePoolable
|
||||
// =============================================================================
|
||||
|
||||
pub struct DatabasePool {
|
||||
conn &sqlite.DB
|
||||
}
|
||||
|
||||
pub fn new_pool() &DatabasePool {
|
||||
mut db := sqlite.connect(':memory:') or { panic(err) }
|
||||
return &DatabasePool{
|
||||
conn: &db
|
||||
}
|
||||
}
|
||||
|
||||
// acquire gets a connection from pool
|
||||
pub fn (mut p DatabasePool) acquire() !&sqlite.DB {
|
||||
return p.conn
|
||||
}
|
||||
|
||||
// release returns a connection to pool
|
||||
pub fn (mut p DatabasePool) release(conn &sqlite.DB) ! {
|
||||
// no-op for single-connection SQLite
|
||||
// real pool: p.inner.put(conn)!
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Middleware layer -- pool_acquire_scoped
|
||||
//
|
||||
// Responsibilities:
|
||||
// 1. Acquire connection from pool
|
||||
// 2. Create orm.DB with DataScope injected
|
||||
// 3. Skip scope filters based on user role
|
||||
// 4. Return configured orm.DB
|
||||
//
|
||||
// Business code just replaces acquire() -> pool_acquire_scoped()
|
||||
// =============================================================================
|
||||
|
||||
pub fn pool_acquire_scoped(mut ctx Context) !(orm.DB, &sqlite.DB) {
|
||||
// 1. acquire raw connection
|
||||
raw_conn := ctx.dbpool.acquire()!
|
||||
|
||||
// 2. create base orm.DB with tenant scope
|
||||
base_db := orm.new_db(raw_conn, orm.DataScope{
|
||||
filters: [
|
||||
orm.QueryFilter{
|
||||
field: 'tenant_id'
|
||||
value: orm.Primitive(ctx.user.tenant_id)
|
||||
mode: .dynamic
|
||||
},
|
||||
]
|
||||
})
|
||||
|
||||
// 3. skip scope filters by role
|
||||
mut scoped_db := base_db
|
||||
match ctx.user.role {
|
||||
'admin' {
|
||||
scoped_db = scoped_db.unscoped()
|
||||
}
|
||||
'manager' {
|
||||
scoped_db = scoped_db.unscoped('org_id')
|
||||
}
|
||||
'normal' {}
|
||||
else {}
|
||||
}
|
||||
|
||||
// 4. return scoped DB
|
||||
return scoped_db, raw_conn
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Business layer -- Repository
|
||||
//
|
||||
// No awareness of DataScope at all.
|
||||
// Only change: acquire() -> pool_acquire_scoped()
|
||||
// =============================================================================
|
||||
|
||||
fn get_users(mut ctx Context) ![]SysUser {
|
||||
db, conn := pool_acquire_scoped(mut ctx)!
|
||||
defer {
|
||||
ctx.dbpool.release(conn) or {}
|
||||
}
|
||||
|
||||
return sql db {
|
||||
select from SysUser
|
||||
}!
|
||||
}
|
||||
|
||||
fn get_user_by_name(mut ctx Context, name string) ![]SysUser {
|
||||
db, conn := pool_acquire_scoped(mut ctx)!
|
||||
defer {
|
||||
ctx.dbpool.release(conn) or {}
|
||||
}
|
||||
|
||||
return sql db {
|
||||
select from SysUser where name == name
|
||||
}!
|
||||
}
|
||||
|
||||
fn count_users(mut ctx Context) !int {
|
||||
db, conn := pool_acquire_scoped(mut ctx)!
|
||||
defer {
|
||||
ctx.dbpool.release(conn) or {}
|
||||
}
|
||||
|
||||
return sql db {
|
||||
select count from SysUser
|
||||
}!
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Demo
|
||||
// =============================================================================
|
||||
|
||||
fn main() {
|
||||
// setup
|
||||
mut pool := new_pool()
|
||||
db := pool.conn
|
||||
|
||||
sql db {
|
||||
create table SysUser
|
||||
}!
|
||||
|
||||
users := [
|
||||
SysUser{
|
||||
name: 'Alice'
|
||||
tenant_id: 1
|
||||
org_id: 10
|
||||
},
|
||||
SysUser{
|
||||
name: 'Bob'
|
||||
tenant_id: 1
|
||||
org_id: 20
|
||||
},
|
||||
SysUser{
|
||||
name: 'Charlie'
|
||||
tenant_id: 2
|
||||
org_id: 10
|
||||
},
|
||||
SysUser{
|
||||
name: 'Diana'
|
||||
tenant_id: 2
|
||||
org_id: 20
|
||||
},
|
||||
]
|
||||
for u in users {
|
||||
sql db {
|
||||
insert u into SysUser
|
||||
}!
|
||||
}
|
||||
|
||||
println('=== test data ===')
|
||||
println('Alice : tenant=1, org=10')
|
||||
println('Bob : tenant=1, org=20')
|
||||
println('Charlie: tenant=2, org=10')
|
||||
println('Diana : tenant=2, org=20')
|
||||
|
||||
mut ctx := Context{
|
||||
user: UserInfo{
|
||||
role: 'normal'
|
||||
tenant_id: 1
|
||||
}
|
||||
dbpool: pool
|
||||
}
|
||||
|
||||
// scenario 1: normal user, tenant=1
|
||||
println('\n--- normal user (tenant=1) ---')
|
||||
users1 := get_users(mut ctx) or { panic(err) }
|
||||
println('got ${users1.len} rows:')
|
||||
for u in users1 {
|
||||
println(' - ${u.name} (tenant=${u.tenant_id})')
|
||||
}
|
||||
|
||||
// scenario 2: manager, tenant=1 (skip org_id, no effect since only tenant_id scope)
|
||||
println('\n--- manager user (tenant=1) ---')
|
||||
ctx.user.role = 'manager'
|
||||
|
||||
users2 := get_users(mut ctx) or { panic(err) }
|
||||
println('got ${users2.len} rows:')
|
||||
for u in users2 {
|
||||
println(' - ${u.name} (tenant=${u.tenant_id})')
|
||||
}
|
||||
|
||||
// scenario 3: admin (skip all scopes)
|
||||
println('\n--- admin user (skip all scopes) ---')
|
||||
ctx.user.role = 'admin'
|
||||
|
||||
users3 := get_users(mut ctx) or { panic(err) }
|
||||
println('got ${users3.len} rows:')
|
||||
for u in users3 {
|
||||
println(' - ${u.name} (tenant=${u.tenant_id})')
|
||||
}
|
||||
|
||||
// scenario 4: normal user, tenant=2
|
||||
println('\n--- normal user (tenant=2) ---')
|
||||
ctx.user.role = 'normal'
|
||||
ctx.user.tenant_id = 2
|
||||
|
||||
users4 := get_users(mut ctx) or { panic(err) }
|
||||
println('got ${users4.len} rows:')
|
||||
for u in users4 {
|
||||
println(' - ${u.name} (tenant=${u.tenant_id})')
|
||||
}
|
||||
|
||||
// scenario 5: get_user_by_name
|
||||
println('\n--- get_user_by_name (admin, name=Alice) ---')
|
||||
ctx.user.role = 'admin'
|
||||
users_by_name := get_user_by_name(mut ctx, 'Alice') or { panic(err) }
|
||||
println('got ${users_by_name.len} rows:')
|
||||
for u in users_by_name {
|
||||
println(' - ${u.name} (tenant=${u.tenant_id})')
|
||||
}
|
||||
|
||||
// scenario 6: count
|
||||
println('\n--- count (admin) ---')
|
||||
total := count_users(mut ctx) or { panic(err) }
|
||||
println('total: ${total}')
|
||||
|
||||
// assertions
|
||||
println('\n=== assertions ===')
|
||||
assert users1.len == 2 // normal/tenant=1: Alice + Bob
|
||||
assert users2.len == 2 // manager/tenant=1: Alice + Bob
|
||||
assert users3.len == 4 // admin: all 4
|
||||
assert users4.len == 2 // normal/tenant=2: Charlie + Diana
|
||||
assert users_by_name.len == 1 // admin: Alice
|
||||
assert total == 4 // admin: all 4
|
||||
println('all passed ✓')
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
module main
|
||||
|
||||
import db.sqlite
|
||||
import time
|
||||
|
||||
@[table: 'sys_users']
|
||||
struct User {
|
||||
pub:
|
||||
id string @[immutable; primary; sql: 'id'; sql_type: 'VARCHAR(255)'; unique]
|
||||
name ?string @[immutable; sql: 'name'; sql_type: 'VARCHAR(255)'; unique]
|
||||
created_at ?time.Time @[omitempty; sql_type: 'TIMESTAMP']
|
||||
updated_at time.Time @[default: new; omitempty; sql_type: 'TIMESTAMP']
|
||||
}
|
||||
|
||||
fn main() {
|
||||
mut db := sqlite.connect(':memory:')!
|
||||
defer { db.close() or {} }
|
||||
|
||||
user1 := User{
|
||||
id: '001'
|
||||
name: 'Jengro'
|
||||
created_at: time.now()
|
||||
updated_at: time.now()
|
||||
}
|
||||
|
||||
user2 := User{
|
||||
id: '002'
|
||||
name: 'Dev'
|
||||
created_at: time.now()
|
||||
updated_at: time.now()
|
||||
}
|
||||
|
||||
// create table
|
||||
sql db {
|
||||
create table User
|
||||
}!
|
||||
|
||||
// insert into table
|
||||
sql db {
|
||||
insert user1 into User
|
||||
insert user2 into User
|
||||
}!
|
||||
|
||||
// query all fields
|
||||
all_users := sql db {
|
||||
select from User
|
||||
}!
|
||||
dump(all_users)
|
||||
|
||||
// where
|
||||
selected_users := sql db {
|
||||
select from User where id == '001'
|
||||
}!
|
||||
dump(selected_users)
|
||||
|
||||
// update
|
||||
sql db {
|
||||
update User set name = 'Tom' where id == '001'
|
||||
}!
|
||||
updated_user := sql db {
|
||||
select from User
|
||||
}!
|
||||
dump(updated_user)
|
||||
|
||||
// delete
|
||||
sql db {
|
||||
delete from User where id == '001'
|
||||
}!
|
||||
remain_users := sql db {
|
||||
select from User
|
||||
}!
|
||||
dump(remain_users)
|
||||
|
||||
// drop table
|
||||
sql db {
|
||||
drop table User
|
||||
}!
|
||||
}
|
||||
Reference in new issue
Block a user