Skip to content

Commit 5413259

Browse files
committedMay 10, 2024
feat: get records
1 parent 13ce35c commit 5413259

36 files changed

+279
-73
lines changed
 

‎.dockerignore

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,4 @@ helm-charts
1313
.editorconfig
1414
.idea
1515
coverage*
16-
undb
16+
.undb

‎Dockerfile

+10-2
Original file line numberDiff line numberDiff line change
@@ -13,22 +13,30 @@ RUN cd /temp/dev && bun install --frozen-lockfile
1313
FROM base AS prerelease
1414
COPY --from=install /temp/dev/node_modules node_modules
1515
COPY . .
16+
RUN mkdir .undb
1617

1718
ENV NODE_ENV=production
1819
ENV PORT=3000
1920
RUN bun run build
2021

21-
FROM oven/bun:1.1-alpine AS release
22+
# Add Tini init-system
23+
ENV TINI_VERSION v0.19.0
24+
ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini-static /tini
25+
RUN chmod +x /tini
26+
27+
FROM gcr.io/distroless/base:nonroot AS release
2228

2329
ENV NODE_ENV=production
2430
ENV PORT=3000
2531

2632
WORKDIR /usr/src/app
2733
COPY --from=prerelease /usr/src/app/apps/backend/undb .
28-
RUN mkdir .undb
34+
COPY --from=prerelease /usr/src/app/.undb ./.undb
2935
COPY --from=prerelease /usr/src/app/apps/backend/drizzle ./drizzle
3036
COPY --from=prerelease /usr/src/app/apps/frontend/dist ./dist
37+
COPY --from=prerelease /tini /tini
3138

3239
# run the app
3340
EXPOSE 3000/tcp
41+
ENTRYPOINT ["/tini", "--"]
3442
CMD [ "./undb" ]
+3-3
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { container } from '@undb/di'
2-
import { RecordRepository, TableQueryRepository, TableRepository } from '@undb/persistence'
3-
import { RECORD_REPOSITORY, TABLE_QUERY_REPOSITORY, TABLE_REPOSITORY } from '@undb/table'
2+
import { RecordQueryRepository, TableQueryRepository, TableRepository } from '@undb/persistence'
3+
import { RECORD_QUERY_REPOSITORY, TABLE_QUERY_REPOSITORY, TABLE_REPOSITORY } from '@undb/table'
44

55
export const registerDb = () => {
66
container.register(TABLE_REPOSITORY, TableRepository)
77
container.register(TABLE_QUERY_REPOSITORY, TableQueryRepository)
8-
container.register(RECORD_REPOSITORY, RecordRepository)
8+
container.register(RECORD_QUERY_REPOSITORY, RecordQueryRepository)
99
}

‎apps/frontend/src/lib/components/blocks/grid-view/grid-view.svelte

+12
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,21 @@
1818
import { cn } from '$lib/utils.js';
1919
import { Input } from '$lib/components/ui/input/index.js';
2020
import type { ITableDTO } from '@undb/table';
21+
import { createQuery } from '@tanstack/svelte-query';
22+
import { trpc } from '$lib/trpc/client';
2123
2224
export let t: ITableDTO;
2325
26+
$: tableId = t.id;
27+
28+
$: getRecords = createQuery({
29+
queryKey: ['records', tableId],
30+
queryHash: tableId,
31+
queryFn: () => trpc.record.list.query({ tableId })
32+
});
33+
34+
$: console.log($getRecords.data);
35+
2436
type Payment = {
2537
id: string;
2638
amount: number;

‎apps/frontend/src/routes/(authed)/t/[tableId]/+layout.ts

+2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { trpc } from '$lib/trpc/client';
22
import type { LayoutLoad } from './$types';
33

4+
export const prerender = 'auto';
5+
46
export const load: LayoutLoad = async (event) => {
57
const { tableId } = event.params;
68

‎bun.lockb

34.9 KB
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import type { ISpecVisitor, ISpecification } from '@undb/domain'
2+
import type { ExpressionBuilder, ExpressionWrapper } from 'kysely'
3+
4+
export interface IAbastractQBVisitor {
5+
get cond(): ExpressionWrapper<any, any, any>
6+
}
7+
8+
export abstract class AbstractQBVisitor<T> implements IAbastractQBVisitor, ISpecVisitor {
9+
constructor(protected readonly eb: ExpressionBuilder<any, any>) {}
10+
11+
#isNot = false
12+
13+
#conds: ExpressionWrapper<any, any, any>[] = []
14+
protected addCond(cond: ExpressionWrapper<any, any, any>): void {
15+
this.#conds.push(cond)
16+
}
17+
get cond(): ExpressionWrapper<any, any, any> {
18+
if (this.#isNot) {
19+
return this.eb.not(this.eb.and(this.#conds))
20+
}
21+
22+
return this.eb.and(this.#conds)
23+
}
24+
or(left: ISpecification<T, ISpecVisitor>, right: ISpecification<T, ISpecVisitor>): this {
25+
const Visitor = Object.getPrototypeOf(this).constructor
26+
27+
const lv = new Visitor(this.eb)
28+
left.accept(lv)
29+
30+
const rv = new Visitor(this.eb)
31+
right.accept(rv)
32+
33+
const cond = this.eb.or([lv.cond, rv.cond])
34+
this.addCond(cond)
35+
36+
return this
37+
}
38+
39+
not(): this {
40+
this.#isNot = true
41+
return this
42+
}
43+
}
+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
export * from './record.repository'
1+
export * from './record.query-repository'
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,9 @@
1-
import type { ISpecification, ISpecVisitor } from '@undb/domain'
2-
import type { IRecordVisitor } from '@undb/table'
3-
import type { StringEqual } from '@undb/table/src/modules/schema/fields/variants/string-field/string-field-value.specification'
4-
import type { ExpressionBuilder, ExpressionWrapper } from 'kysely'
1+
import type { IRecordVisitor, RecordDO, StringEqual } from '@undb/table'
2+
import { AbstractQBVisitor } from '../abstract-qb.visitor'
53

6-
export class RecordFilterVisitor implements IRecordVisitor {
7-
constructor(private readonly eb: ExpressionBuilder<any, any>) {}
8-
9-
#conds: ExpressionWrapper<any, any, any>[] = []
10-
protected addCond(cond: ExpressionWrapper<any, any, any>): void {
11-
this.#conds.push(cond)
12-
}
13-
get cond(): ExpressionWrapper<any, any, any> {
14-
return this.eb.and(this.#conds)
15-
}
4+
export class RecordFilterVisitor extends AbstractQBVisitor<RecordDO> implements IRecordVisitor {
165
stringEqual(spec: StringEqual): void {
17-
this.addCond(this.eb.eb(spec.fieldId.value, '=', spec.values.value))
18-
}
19-
or(left: ISpecification<any, ISpecVisitor>, right: ISpecification<any, ISpecVisitor>): this {
20-
throw new Error('Method not implemented.')
21-
}
22-
not(): this {
23-
throw new Error('Method not implemented.')
6+
const cond = this.eb.eb(spec.fieldId.value, '=', spec.values.value)
7+
this.addCond(cond)
248
}
259
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { singleton } from '@undb/di'
2+
import type { Mapper } from '@undb/domain'
3+
import type { IRecordDTO, RecordDO } from '@undb/table'
4+
5+
@singleton()
6+
export class RecordMapper implements Mapper<RecordDO, any, IRecordDTO> {
7+
toDo(entity: any): RecordDO {
8+
throw new Error('Method not implemented.')
9+
}
10+
toEntity(domain: RecordDO) {
11+
throw new Error('Method not implemented.')
12+
}
13+
toDTO(entity: any): IRecordDTO {
14+
const { id, ...rest } = entity
15+
return {
16+
id: entity.id,
17+
values: rest,
18+
}
19+
}
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { inject, singleton } from '@undb/di'
2+
import type { Option } from '@undb/domain'
3+
import type { IRecordQueryRepository, IRecordsDTO, Query, TableDo } from '@undb/table'
4+
import type { IQueryBuilder } from '../qb'
5+
import { injectQueryBuilder } from '../qb.provider'
6+
import { UnderlyingTable } from '../underlying/underlying-table'
7+
import { RecordFilterVisitor } from './record.filter-visitor'
8+
import { RecordMapper } from './record.mapper'
9+
10+
@singleton()
11+
export class RecordQueryRepository implements IRecordQueryRepository {
12+
constructor(
13+
@injectQueryBuilder()
14+
private readonly qb: IQueryBuilder,
15+
@inject(RecordMapper)
16+
private readonly mapper: RecordMapper
17+
) {}
18+
19+
async find(table: TableDo, query: Option<Query>): Promise<IRecordsDTO> {
20+
const t = new UnderlyingTable(table)
21+
const result = await this.qb
22+
.selectFrom(t.name)
23+
// TODO: select spec
24+
.selectAll()
25+
.where((eb) => {
26+
const spec = query.into(null)?.filter
27+
const visitor = new RecordFilterVisitor(eb)
28+
if (spec?.isSome()) {
29+
spec.unwrap().accept(visitor)
30+
}
31+
return visitor.cond
32+
})
33+
.execute()
34+
35+
return result.map((r) => this.mapper.toDTO(r))
36+
}
37+
}

‎packages/persistence/src/record/record.repository.ts

-26
This file was deleted.
+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { Query, type QueryProps } from '@undb/domain'
2+
import { getRecordsDTO } from '@undb/table'
3+
import { z } from 'zod'
4+
5+
export const getRecordsQuery = getRecordsDTO
6+
7+
export type IGetRecordsQuery = z.infer<typeof getRecordsQuery>
8+
9+
export class GetRecordsQuery extends Query implements IGetRecordsQuery {
10+
public readonly tableId: string
11+
12+
constructor(props: QueryProps<IGetRecordsQuery>) {
13+
super()
14+
this.tableId = props.tableId
15+
}
16+
}

‎packages/queries/src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
1+
export * from './get-records.query'
12
export * from './get-table.query'
23
export * from './get-tables.query'
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { queryHandler } from '@undb/cqrs'
2+
import { singleton } from '@undb/di'
3+
import type { IQueryHandler } from '@undb/domain'
4+
import { createLogger } from '@undb/logger'
5+
import { GetRecordsQuery, type IGetRecordsQuery } from '@undb/queries'
6+
import { injectRecordsQueryService, type IRecordsDTO, type IRecordsQueryService } from '@undb/table'
7+
8+
@queryHandler(GetRecordsQuery)
9+
@singleton()
10+
export class GetRecordsQueryHandler implements IQueryHandler<IGetRecordsQuery, any> {
11+
private readonly logger = createLogger(GetRecordsQueryHandler.name)
12+
13+
constructor(
14+
@injectRecordsQueryService()
15+
private readonly svc: IRecordsQueryService
16+
) {}
17+
18+
async execute(query: IGetRecordsQuery): Promise<IRecordsDTO> {
19+
this.logger.debug(query, 'get records query executed')
20+
21+
return this.svc.getRecords(query)
22+
}
23+
}
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1+
import { GetRecordsQueryHandler } from './get-records.query-handler'
12
import { GetTableQueryHandler } from './get-table.query-handler'
23
import { GetTablesQueryHandler } from './get-tables.query-handler'
34

4-
export const queryHandlers = [GetTablesQueryHandler, GetTableQueryHandler]
5+
export const queryHandlers = [GetTablesQueryHandler, GetTableQueryHandler, GetRecordsQueryHandler]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { z } from 'zod'
2+
import { tableId } from '../../../table-id.vo'
3+
4+
export const getRecordsDTO = z.object({
5+
tableId: tableId,
6+
})
7+
8+
export type IGetRecordsDTO = z.infer<typeof getRecordsDTO>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from './get-records.dto'
2+
export * from './records.dto'
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import type { z } from 'zod'
2+
import { recordDTO } from '../record/dto'
3+
4+
export const recordsDTO = recordDTO.array()
5+
6+
export type IRecordsDTO = z.infer<typeof recordsDTO>
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
1+
export * from './dto'
12
export * from './record'
23
export * from './records.vo'
4+
export * from './services'
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './record.dto'
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { z } from 'zod'
2+
import { recordId } from '../record-id.vo'
3+
import { recordValues } from '../record-values.vo'
4+
5+
export const recordDTO = z.object({
6+
id: recordId,
7+
values: recordValues,
8+
})
9+
10+
export type IRecordDTO = z.infer<typeof recordDTO>

‎packages/table/src/modules/records/record/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
export * from './dto'
12
export * from './record-id.vo'
23
export * from './record-values.vo'
34
export * from './record-visitor.interface'

‎packages/table/src/modules/records/record/record-values.vo.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import { Option, ValueObject } from '@undb/domain'
2+
import { z } from 'zod'
23
import type { FieldValue } from '../../schema'
3-
import type { FieldId, IFieldId } from '../../schema/fields/field-id.vo'
4+
import { fieldId, type FieldId, type IFieldId } from '../../schema/fields/field-id.vo'
5+
6+
export const recordValues = z.record(fieldId, z.any())
47

58
export class RecordValuesVO extends ValueObject {
69
#map: Map<IFieldId, FieldValue>
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
import { inject } from '@undb/di'
22

3-
export const RECORD_REPOSITORY = Symbol('RECORD_REPOSITORY')
4-
export const injectRecordRepository = () => inject(RECORD_REPOSITORY)
3+
export const RECORD_QUERY_REPOSITORY = Symbol('RECORD_QUERY_REPOSITORY')
4+
export const injectRecordQueryRepository = () => inject(RECORD_QUERY_REPOSITORY)
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
import type { Option } from '@undb/domain'
2+
import type { TableDo } from '../../../table.do'
3+
import type { IRecordsDTO } from '../dto/records.dto'
24
import type { RecordComositeSpecification } from './record.composite-specification'
35

4-
export interface IRecordRepository {
5-
find(spec: Option<RecordComositeSpecification>): void
6+
export interface Query {
7+
select: Option<RecordComositeSpecification>
8+
filter: Option<RecordComositeSpecification>
9+
}
10+
11+
export interface IRecordQueryRepository {
12+
find(table: TableDo, query: Option<Query>): Promise<IRecordsDTO>
613
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from './records.query-service'
2+
export * from './records.query-service.provider'
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { None } from '@undb/domain'
2+
import { TableIdVo } from '../../../../table-id.vo'
3+
import type { IGetRecordsDTO, IRecordsDTO } from '../../dto'
4+
import type { RecordsQueryService } from '../records.query-service'
5+
6+
export async function getRecords(this: RecordsQueryService, dto: IGetRecordsDTO): Promise<IRecordsDTO> {
7+
const tableId = new TableIdVo(dto.tableId)
8+
const table = (await this.tableRepository.findOneById(tableId)).expect('Table not found')
9+
10+
return this.repo.find(table, None)
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { container, inject } from '@undb/di'
2+
import { RecordsQueryService } from './records.query-service'
3+
4+
export const RECORDS_QUERY_SERVICE = Symbol.for('RECORDS_QUERY_SERVICE')
5+
export const injectRecordsQueryService = () => inject(RECORDS_QUERY_SERVICE)
6+
container.register(RECORDS_QUERY_SERVICE, { useClass: RecordsQueryService })

0 commit comments

Comments
 (0)