Skip to content

Commit

Permalink
wip: share
Browse files Browse the repository at this point in the history
  • Loading branch information
nichenqin committed Jun 24, 2024
1 parent eeb0fab commit 792dcc4
Show file tree
Hide file tree
Showing 16 changed files with 292 additions and 5 deletions.
6 changes: 6 additions & 0 deletions apps/backend/src/registry/db.registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import {
RecordOutboxService,
RecordQueryRepository,
RecordRepository,
ShareQueryRepository,
ShareRepository,
TableOutboxService,
TableQueryRepository,
TableRepository,
Expand All @@ -27,6 +29,7 @@ import {
WorkspaceMemberQueryRepository,
WorkspaceMemberRepository,
} from "@undb/persistence"
import { SHARE_QUERY_REPOSITORY, SHARE_REPOSITORY, SHARE_SERVICE, ShareService } from "@undb/share"
import {
RECORD_OUTBOX_SERVICE,
RECORD_QUERY_REPOSITORY,
Expand Down Expand Up @@ -57,4 +60,7 @@ export const registerDb = () => {
container.register(BASE_REPOSITORY, BaseRepository)
container.register(BASE_QUERY_REPOSITORY, BaseQueryRepository)
container.register(BASE_OUTBOX_SERVICE, BaseOutboxService)
container.register(SHARE_SERVICE, ShareService)
container.register(SHARE_REPOSITORY, ShareRepository)
container.register(SHARE_QUERY_REPOSITORY, ShareQueryRepository)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { EnableShareCommand } from "@undb/commands"
import { commandHandler } from "@undb/cqrs"
import { singleton } from "@undb/di"
import type { ICommandHandler } from "@undb/domain"
import { createLogger } from "@undb/logger"
import { injectShareService, type IShareService } from "@undb/share"

@commandHandler(EnableShareCommand)
@singleton()
export class EnableShareCommandHandler implements ICommandHandler<EnableShareCommand, any> {
private readonly logger = createLogger(EnableShareCommandHandler.name)

constructor(
@injectShareService()
private readonly service: IShareService,
) {}

async execute(command: EnableShareCommand): Promise<any> {
this.logger.debug(command)

await this.service.enableShare(command)
}
}
18 changes: 18 additions & 0 deletions packages/commands/src/enable-share.command.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Command, type CommandProps } from "@undb/domain"
import { enableShareDTO, type IShareTarget } from "@undb/share"
import { z } from "@undb/zod"

export const enableShareCommand = enableShareDTO

export type IEnableShareCommand = z.infer<typeof enableShareCommand>

export class EnableShareCommand extends Command implements IEnableShareCommand {
public readonly target: IShareTarget
public readonly enabled: boolean

constructor(props: CommandProps<IEnableShareCommand>) {
super(props)
this.enabled = props.enabled
this.target = props.target
}
}
1 change: 1 addition & 0 deletions packages/graphql/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"@undb/di": "workspace:*",
"@undb/context": "workspace:*",
"@undb/cqrs": "workspace:*",
"@undb/share": "workspace:*",
"@undb/user": "workspace:*",
"@undb/queries": "workspace:*",
"@elysiajs/graphql-yoga": "^1.0.3",
Expand Down
15 changes: 15 additions & 0 deletions packages/graphql/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
GetTablesByBaseIdQuery,
GetTablesQuery,
} from "@undb/queries"
import { injectShareService, type IShareService } from "@undb/share"
import { TableIdVo, injectRecordQueryRepository, type IRecordQueryRepository } from "@undb/table"
import { injectUserQueryRepository, type IUserQueryRepository } from "@undb/user"

Expand All @@ -24,6 +25,8 @@ export class Graphql {
public readonly repo: IRecordQueryRepository,
@injectUserQueryRepository()
public readonly userRepo: IUserQueryRepository,
@injectShareService()
public readonly shareService: IShareService,
) {}

public route() {
Expand Down Expand Up @@ -235,6 +238,18 @@ export class Graphql {
return (await this.userRepo.findOneById(member.userId)).unwrap()
},
},
View: {
// @ts-ignore
share: async (view) => {
return this.shareService.getShareByTarget({ type: "view", id: view.id })
},
},
Form: {
// @ts-ignore
share: async (form) => {
return this.shareService.getShareByTarget({ type: "form", id: form.id })
},
},
},
batching: true,
})
Expand Down
1 change: 1 addition & 0 deletions packages/persistence/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export type { Database } from "./db"
export { injectDb } from "./db.provider"
export * from "./member"
export * from "./record"
export * from "./share"
export * from "./table"
export * from "./tables"
export * from "./user"
Expand Down
2 changes: 2 additions & 0 deletions packages/persistence/src/share/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from "./share.query-repository"
export * from "./share.repository"
19 changes: 19 additions & 0 deletions packages/persistence/src/share/share.filter-visitor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import type { IShareSpecVisitor, Share, WithShareEnabled, WithShareForm, WithShareId, WithShareView } from "@undb/share"
import { and, eq } from "drizzle-orm"
import { AbstractDBFilterVisitor } from "../abstract-db.visitor"
import { shareTable } from "../tables"

export class ShareFilterVisitor extends AbstractDBFilterVisitor<Share> implements IShareSpecVisitor {
idEqual(s: WithShareId): void {
throw new Error("Method not implemented.")
}
targetView(s: WithShareView): void {
this.addCond(and(eq(shareTable.targetType, "view"), eq(shareTable.targetId, s.viewId)))
}
targetForm(s: WithShareForm): void {
this.addCond(and(eq(shareTable.targetType, "form"), eq(shareTable.targetId, s.formId)))
}
enabled(s: WithShareEnabled): void {
throw new Error("Method not implemented.")
}
}
36 changes: 36 additions & 0 deletions packages/persistence/src/share/share.mapper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { singleton } from "@undb/di"
import type { Mapper } from "@undb/domain"
import { ShareFactory, type IShareDTO, type IShareTarget, type Share as ShareDo } from "@undb/share"
import type { Share } from "../tables"

@singleton()
export class ShareMapper implements Mapper<ShareDo, Share, IShareDTO> {
toDo(entity: Share): ShareDo {
return ShareFactory.fromJSON({
id: entity.id,
target: {
type: entity.targetType,
id: entity.targetId,
} as IShareTarget,
enabled: entity.enabled,
})
}
toEntity(domain: ShareDo): Share {
return {
id: domain.id.value,
targetId: domain.target.id,
targetType: domain.target.type,
enabled: domain.enabled,
}
}
toDTO(entity: Share): IShareDTO {
return {
id: entity.id,
target: {
id: entity.targetId,
type: entity.targetType,
} as IShareTarget,
enabled: entity.enabled,
}
}
}
42 changes: 42 additions & 0 deletions packages/persistence/src/share/share.query-repository.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { inject, singleton } from "@undb/di"
import { None, Some, type Option } from "@undb/domain"
import type { IShareDTO, IShareQueryRepository, ShareSpecification } from "@undb/share"
import type { Database } from "../db"
import { injectDb } from "../db.provider"
import { shareTable } from "../tables"
import { ShareFilterVisitor } from "./share.filter-visitor"
import { ShareMapper } from "./share.mapper"

@singleton()
export class ShareQueryRepository implements IShareQueryRepository {
constructor(
@injectDb()
private readonly db: Database,
@inject(ShareMapper)
private readonly mapper: ShareMapper,
) {}
async findOneById(id: string): Promise<Option<IShareDTO>> {
throw new Error("Method not implemented.")
}

async findOne(spec: ShareSpecification): Promise<Option<IShareDTO>> {
const qb = this.db.select().from(shareTable).$dynamic()
const visitor = new ShareFilterVisitor()
spec.accept(visitor)

const results = await qb.where(visitor.cond).limit(1)
if (results.length === 0) {
return None
}

const [share] = results
if (!share) {
return None
}

return Some(this.mapper.toDTO(share))
}
async find(): Promise<IShareDTO[]> {
throw new Error("Method not implemented.")
}
}
39 changes: 39 additions & 0 deletions packages/persistence/src/share/share.repository.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { inject, singleton } from "@undb/di"
import type { Option } from "@undb/domain"
import type { IShareRepository, Share, ShareSpecification } from "@undb/share"
import type { Database } from "../db"
import { injectDb } from "../db.provider"
import { shareTable } from "../tables"
import { ShareMapper } from "./share.mapper"

@singleton()
export class ShareRepository implements IShareRepository {
constructor(
@injectDb()
private readonly db: Database,
@inject(ShareMapper)
private readonly mapper: ShareMapper,
) {}
insert(share: Share): Promise<void> {
throw new Error("Method not implemented.")
}
async updateOneById(share: Share, spec: ShareSpecification): Promise<void> {
const entity = this.mapper.toEntity(share)
await this.db
.insert(shareTable)
.values(entity)
.onConflictDoUpdate({ target: [shareTable.targetId, shareTable.targetType], set: { enabled: share.enabled } })
}
findOneById(id: string): Promise<Option<Share>> {
throw new Error("Method not implemented.")
}
findOne(spec: ShareSpecification): Promise<Option<Share>> {
throw new Error("Method not implemented.")
}
find(spec: ShareSpecification): Promise<Share[]> {
throw new Error("Method not implemented.")
}
deleteOneById(id: string): Promise<void> {
throw new Error("Method not implemented.")
}
}
20 changes: 19 additions & 1 deletion packages/persistence/src/tables.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import type { IFormsDTO, IRLSGroupDTO, ISchemaDTO, IViewsDTO, RECORD_EVENTS } fr
import type { IWebhookHeaders, IWebhookMethod } from "@undb/webhook"
import type { IRootWebhookCondition } from "@undb/webhook/src/webhook.condition"
import { sql } from "drizzle-orm"
import { index, integer, sqliteTableCreator, text } from "drizzle-orm/sqlite-core"
import { index, integer, sqliteTableCreator, text, unique } from "drizzle-orm/sqlite-core"

const sqliteTable = sqliteTableCreator((name) => `undb_${name}`)

Expand Down Expand Up @@ -160,3 +160,21 @@ export const baseTable = sqliteTable("base", {

export type Base = typeof baseTable.$inferSelect
export type NewBase = typeof baseTable.$inferInsert

export const shareTable = sqliteTable(
"share",
{
id: text("id").notNull().primaryKey(),
targetType: text("target").notNull(),
targetId: text("target_id").notNull(),
enabled: integer("enabled", { mode: "boolean" }).notNull(),
},
(table) => {
return {
uniqueIdx: unique("share_unique_idx").on(table.targetType, table.targetId),
}
},
)

export type Share = typeof shareTable.$inferSelect
export type NewShare = typeof shareTable.$inferInsert
51 changes: 51 additions & 0 deletions packages/share/src/services/share.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { inject, singleton } from "@undb/di"
import { Option } from "@undb/domain"
import type { IEnableShareDTO, IShareDTO } from "../dto"
import type { IShareTarget } from "../share-target.vo"
import { ShareFactory } from "../share.factory"
import {
injectShareQueryRepository,
injectShareRepository,
type IShareQueryRepository,
type IShareRepository,
} from "../share.repository"
import { WithShareId, withShare } from "../specifications"

export interface IShareService {
enableShare(dto: IEnableShareDTO): Promise<void>
getShareByTarget(target: IShareTarget): Promise<Option<IShareDTO>>
}

export const SHARE_SERVICE = Symbol.for("SHARE_SERVICE")
export const injectShareService = () => inject(SHARE_SERVICE)

@singleton()
export class ShareService implements IShareService {
constructor(
@injectShareRepository()
private readonly repo: IShareRepository,
@injectShareQueryRepository()
private readonly queryRepo: IShareQueryRepository,
) {}

async enableShare(dto: IEnableShareDTO): Promise<void> {
const spec = withShare(dto.target.type, dto.target.id)

let share = (await this.repo.findOne(spec)).into(undefined)
if (!share) {
share = ShareFactory.create(WithShareId.create(), spec)
}

const s = share.$enable(dto)

if (s.isSome()) {
await this.repo.updateOneById(share, s.unwrap())
}
}

async getShareByTarget(target: IShareTarget): Promise<Option<IShareDTO>> {
const spec = withShare(target.type, target.id)

return this.queryRepo.findOne(spec)
}
}
7 changes: 7 additions & 0 deletions packages/share/src/share-target.vo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,11 @@ export class ShareTarget extends ValueObject<IShareTarget> {
public get type() {
return this.unpack()?.type
}

public toJSON() {
return {
id: this.id,
type: this.type,
}
}
}
8 changes: 4 additions & 4 deletions packages/share/src/share.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import type { ShareSpecification } from "./specifications/interface.js"

export interface IShareRepository {
insert(share: Share): Promise<void>
updateOneById(id: string, spec: ShareSpecification): Promise<void>
updateOneById(share: Share, spec: ShareSpecification): Promise<void>
findOneById(id: string): Promise<Option<Share>>
findOne(spec: ShareSpecification): Promise<Option<Share>>
find(spec: ShareSpecification): Promise<Share[]>
Expand All @@ -17,9 +17,9 @@ export const SHARE_REPOSITORY = Symbol("SHARE_REPOSITORY")
export const injectShareRepository = () => inject(SHARE_REPOSITORY)

export interface IShareQueryRepository {
findOneById: (id: string) => Promise<Option<IShareDTO>>
findOne: (spec: ShareSpecification) => Promise<Option<IShareDTO>>
find: (spec: ShareSpecification | null) => Promise<IShareDTO[]>
findOneById(id: string): Promise<Option<IShareDTO>>
findOne(spec: ShareSpecification): Promise<Option<IShareDTO>>
find(spec: ShareSpecification | null): Promise<IShareDTO[]>
}

export const SHARE_QUERY_REPOSITORY = Symbol("SHARE_QUERY_REPOSITORY")
Expand Down
Loading

0 comments on commit 792dcc4

Please sign in to comment.