bradb 3.1.1 → 3.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,3 +1,4 @@
1
+ import { PgTable } from 'drizzle-orm/pg-core';
1
2
  import { BradConfig } from './config';
2
3
  export declare const nodeTypes: readonly ["validator", "controller", "service", "schema", "router", "filter", "db"];
3
4
  export type NodeType = typeof nodeTypes[number];
@@ -8,6 +9,8 @@ type Node = {
8
9
  adj: Node[];
9
10
  exports: string[];
10
11
  };
12
+ type GeneratorFunc = (name: string, table: PgTable, imports: string[]) => string;
13
+ export declare const generators: Record<NodeType, GeneratorFunc | null>;
11
14
  type Export = Record<string, any>;
12
15
  export declare const valid: Record<string, boolean>;
13
16
  export declare let mods: Export;
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.mods = exports.valid = exports.nodeTypes = void 0;
6
+ exports.mods = exports.valid = exports.generators = exports.nodeTypes = void 0;
7
7
  exports.buildGraph = buildGraph;
8
8
  exports.addNodeTo = addNodeTo;
9
9
  exports.build = build;
@@ -24,6 +24,12 @@ const dependOn = {
24
24
  schema: [],
25
25
  db: []
26
26
  };
27
+ /* CRUD Operations
28
+ * # READ
29
+ * router.getAll -> controller.getAll -> service.getAll -> schema
30
+ * controller.count -> service.count -> schema
31
+ * controller
32
+ */
27
33
  // Default and base exports for each node type
28
34
  const requiredExports = {
29
35
  validator: (name) => [`${name}Validator`],
@@ -34,7 +40,7 @@ const requiredExports = {
34
40
  filter: (name) => [`${name}FilterMap`],
35
41
  db: (name) => ["db"]
36
42
  };
37
- const generators = {
43
+ exports.generators = {
38
44
  router: generators_1.generateRouter,
39
45
  controller: generators_1.generateController,
40
46
  validator: generators_1.generateValidator,
@@ -63,7 +69,7 @@ function buildGraph(cfg, name, type) {
63
69
  checked[id(n)] = true;
64
70
  try {
65
71
  // If config is set to override = true we not check the modules and re-write anywise
66
- if (cfg.override) {
72
+ if (cfg.override && dependOn[type].length > 0) {
67
73
  exports.valid[id(n)] = false;
68
74
  }
69
75
  else {
@@ -73,8 +79,6 @@ function buildGraph(cfg, name, type) {
73
79
  }
74
80
  }
75
81
  catch (err) {
76
- console.error(err.message);
77
- console.log(cfg.override);
78
82
  // it's not necessary to set it to false
79
83
  exports.valid[id(n)] = false;
80
84
  }
@@ -109,7 +113,7 @@ function build(root) {
109
113
  build(dep);
110
114
  }
111
115
  if (!exports.valid[id(root)]) {
112
- const gen = generators[root.type];
116
+ const gen = exports.generators[root.type];
113
117
  if (!gen) {
114
118
  throw Error(`Missing generator for ${root.type}`);
115
119
  }
@@ -144,6 +148,10 @@ function checkNode(root) {
144
148
  console.log(`Checking ${root.name}.${root.type}...`);
145
149
  // throw the require error
146
150
  const module = require(root.path);
151
+ // const code = fs.readFileSync(root.path).toString();
152
+ //
153
+ // const a = addCrudOp(`${root.name}${requiredExports[root.type]}`, code, "getAll");
154
+ // console.log(a);
147
155
  for (const exp of root.exports) {
148
156
  if (!module[exp]) {
149
157
  throw new Error(`[Missing export]: Cannot import ${exp} from ${root.path}`);
@@ -151,6 +159,14 @@ function checkNode(root) {
151
159
  }
152
160
  return module;
153
161
  }
162
+ function addCrudOp(name, code, op) {
163
+ const regex = new RegExp(`(export\s+const\s+${name}\s*=\s*\{)([\s\S]*?)\};`);
164
+ const match = code.match(regex);
165
+ if (!match) {
166
+ return code;
167
+ }
168
+ return match.toString();
169
+ }
154
170
  function buildImportLine(from, to, maxColumns = 80) {
155
171
  const relativePath = buildRelativeImport(from.path, to.path);
156
172
  let sep = ' ';
@@ -4,6 +4,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
4
4
  const commander_1 = require("commander");
5
5
  const builder_1 = require("./builder");
6
6
  const config_1 = require("./config");
7
+ const generators_1 = require("./generators");
7
8
  const program = new commander_1.Command();
8
9
  program.name("brad").description("BRAD - Generator");
9
10
  program
@@ -13,16 +14,19 @@ program
13
14
  .choices(builder_1.nodeTypes)
14
15
  .default("router"))
15
16
  .option('-o, --override', 'Generator will override the current code', false)
17
+ .option('--cb, --controller-with-builder', 'Generate controller using builder', false)
16
18
  .description("generate entity recursively")
17
19
  .action(async (name, type, options) => {
18
20
  const cfg = (0, config_1.loadConfig)();
19
21
  cfg.override = options.override || false;
22
+ if (options.controllerWithBuilder) {
23
+ builder_1.generators["controller"] = generators_1.generateControllerBuilder;
24
+ }
20
25
  require('ts-node/register');
21
26
  const root = (0, builder_1.buildGraph)(cfg, name, type);
22
27
  // Aca tendríamos que ir desde (name, type) hasta la raiz (name, "router")
23
28
  // y desde ahi, ver si tenemos dependencias de otra entidad
24
29
  console.log(JSON.stringify(root, null, 4));
25
- console.log(builder_1.valid);
26
30
  (0, builder_1.build)(root);
27
31
  });
28
32
  program
@@ -1,43 +1,20 @@
1
- import { PgTable } from "drizzle-orm/pg-core";
2
1
  import { Request, Response } from "express";
3
2
  import { z, ZodObject } from "zod";
4
3
  import { Filter } from "./types";
5
- import { InferInsertModel } from "drizzle-orm";
6
- export declare function findOneBuilder<TReturn>(service: {
7
- findOne: (id: any) => Promise<TReturn>;
8
- }, hook?: (item: TReturn) => Promise<object>): (req: Request, res: Response) => Promise<Response<any, Record<string, any>> | undefined>;
9
- export declare function findAllBuilder<FSchema extends ZodObject, TReturn>(service: {
10
- findAll: (filters?: Filter<FSchema>, page?: number, pageSize?: number) => Promise<TReturn[]>;
11
- count: (filters?: Filter<FSchema>) => Promise<number>;
12
- }, filter: FSchema, hook?: (items: TReturn[], total: number) => Promise<object[]>, hasPagination?: boolean): (req: Request, res: Response) => Promise<Response<any, Record<string, any>>>;
13
- export declare function createBuilder<CSchema extends ZodObject, TReturn>(service: {
14
- create: (data: z.core.output<CSchema>) => Promise<TReturn>;
15
- }, schema: CSchema, hook?: (req: Request, res: Response, data: z.core.output<CSchema>, item: TReturn) => Promise<object>): (req: Request, res: Response) => Promise<Response<any, Record<string, any>>>;
16
- export declare function updateBuilder<USchema extends ZodObject, TReturn>(service: {
17
- update: (id: any, data: z.core.output<USchema>) => Promise<TReturn>;
18
- }, schema: USchema): (req: Request, res: Response) => Promise<Response<any, Record<string, any>>>;
19
- export declare function deleteBuilder(service: {
20
- delete: (id: any) => Promise<void>;
21
- }): (req: Request, res: Response) => Promise<Response<any, Record<string, any>>>;
4
+ export declare const paginationSchema: z.ZodObject<{
5
+ page: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
6
+ pageSize: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
7
+ }, z.core.$strip>;
8
+ export type Pagination = {
9
+ page: number;
10
+ pageSize: number;
11
+ total?: number;
12
+ count?: number;
13
+ };
14
+ export declare function newPagination(data: unknown): Pagination;
15
+ export declare function findOneBuilder<PK extends ZodObject, Item>(findOne: (pk: z.output<PK>) => Promise<Item>, pkSchema: PK, hook?: (item: Item) => Promise<object>): (req: Request, res: Response) => Promise<Response<any, Record<string, any>> | undefined>;
16
+ export declare function findAllBuilder<FSchema extends ZodObject, Item>(findAll: (filters?: Filter<FSchema>, p?: Pagination) => Promise<Item[]>, filter: FSchema, hook?: (items: Item[], total: number) => Promise<object[]>): (req: Request, res: Response) => Promise<Response<any, Record<string, any>>>;
17
+ export declare function createBuilder<CSchema extends ZodObject, Item>(create: (data: z.core.output<CSchema>) => Promise<Item>, schema: CSchema, hook?: (req: Request, res: Response, data: z.core.output<CSchema>, item: Item) => Promise<object>): (req: Request, res: Response) => Promise<Response<any, Record<string, any>>>;
18
+ export declare function updateBuilder<PKSchema extends ZodObject, USchema extends ZodObject, Item>(update: (pks: z.core.output<PKSchema>, data: z.core.output<USchema>) => Promise<Item>, pkSchema: PKSchema, schema: USchema): (req: Request, res: Response) => Promise<Response<any, Record<string, any>>>;
19
+ export declare function deleteBuilder<PKSchema extends ZodObject>(remove: (id: z.core.output<PKSchema>) => Promise<void>, pkSchema: PKSchema): (req: Request, res: Response) => Promise<Response<any, Record<string, any>>>;
22
20
  export declare function validate<T extends ZodObject>(schema: T, fn: (req: Request, res: Response, data: z.output<T>) => Promise<Response>): (req: Request, res: Response) => Promise<Response<any, Record<string, any>>>;
23
- interface CRUDService<FSchema extends ZodObject> {
24
- findOne: (id: any) => Promise<any>;
25
- findAll: (filters?: Filter<FSchema>, page?: number, pageSize?: number) => Promise<any[]>;
26
- count: (filters: Filter<FSchema>) => Promise<number>;
27
- create: (data: any) => Promise<any>;
28
- update: (id: any, data: Partial<any>) => Promise<any>;
29
- delete: (id: any) => Promise<any>;
30
- }
31
- export declare class BaseController<T extends PgTable, FSchema extends z.ZodObject> {
32
- protected service: CRUDService<FSchema>;
33
- private filterSchema;
34
- private createSchema;
35
- private updateSchema;
36
- constructor(service: CRUDService<FSchema>, base: z.ZodObject, filter: FSchema, createSchema?: z.ZodSchema<InferInsertModel<T>>, updateSchema?: Partial<InferInsertModel<T>>);
37
- getAll: (req: Request, res: Response) => Promise<void>;
38
- getById: (req: Request, res: Response) => Promise<void>;
39
- create: (req: Request, res: Response) => Promise<void>;
40
- update: (req: Request, res: Response) => Promise<void>;
41
- delete: (req: Request, res: Response) => Promise<void>;
42
- }
43
- export {};
@@ -1,15 +1,26 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.BaseController = void 0;
3
+ exports.paginationSchema = void 0;
4
+ exports.newPagination = newPagination;
4
5
  exports.findOneBuilder = findOneBuilder;
5
6
  exports.findAllBuilder = findAllBuilder;
6
7
  exports.createBuilder = createBuilder;
7
8
  exports.updateBuilder = updateBuilder;
8
9
  exports.deleteBuilder = deleteBuilder;
9
10
  exports.validate = validate;
10
- function findOneBuilder(service, hook) {
11
+ const zod_1 = require("zod");
12
+ exports.paginationSchema = zod_1.z.object({
13
+ page: zod_1.z.coerce.number().default(1),
14
+ pageSize: zod_1.z.coerce.number().default(10)
15
+ });
16
+ function newPagination(data) {
17
+ const p = exports.paginationSchema.parse(data);
18
+ return p;
19
+ }
20
+ function findOneBuilder(findOne, pkSchema, hook) {
11
21
  return async (req, res) => {
12
- const item = await service.findOne(req.params.id);
22
+ const pk = pkSchema.parse(req.params);
23
+ const item = await findOne(pk);
13
24
  if (hook !== undefined) {
14
25
  const i = await hook(item);
15
26
  return res.status(200).json(i);
@@ -17,44 +28,33 @@ function findOneBuilder(service, hook) {
17
28
  res.status(200).json(item);
18
29
  };
19
30
  }
20
- function findAllBuilder(service, filter, hook, hasPagination = true) {
31
+ function findAllBuilder(findAll, filter, hook) {
21
32
  return async (req, res) => {
22
33
  const filters = filter.parse({
23
34
  ...req.params,
24
35
  ...req.query
25
36
  });
26
- let pagination, itemsProm;
27
- if (hasPagination) {
28
- pagination = getPagination(req);
29
- itemsProm = service.findAll(filters, pagination.page, pagination.pageSize);
30
- }
31
- else {
32
- itemsProm = service.findAll(filters);
33
- }
34
- const totalProm = service.count(filters);
35
- const [items, total] = await Promise.all([itemsProm, totalProm]);
37
+ const pagination = newPagination(req.query);
38
+ const items = await findAll(filters, pagination);
36
39
  let resItems = items;
37
40
  if (hook != undefined) {
38
- resItems = await hook(items, total);
39
- }
40
- if (hasPagination) {
41
- return res.status(200).json({
42
- pagination,
43
- items: resItems,
44
- total
45
- });
46
- }
47
- else {
48
- return res.status(200).json({
49
- items: resItems,
50
- total
51
- });
41
+ if (!pagination.total) {
42
+ resItems = await hook(items, items.length);
43
+ }
44
+ else {
45
+ resItems = await hook(items, pagination.total);
46
+ }
52
47
  }
48
+ return res.status(200).json({
49
+ pagination,
50
+ items: resItems,
51
+ total: pagination.total
52
+ });
53
53
  };
54
54
  }
55
- function createBuilder(service, schema, hook) {
55
+ function createBuilder(create, schema, hook) {
56
56
  return validate(schema, async (req, res, data) => {
57
- const item = await service.create(data);
57
+ const item = await create(data);
58
58
  if (hook) {
59
59
  const i = await hook(req, res, data, item);
60
60
  return res.status(201).json(i);
@@ -62,15 +62,17 @@ function createBuilder(service, schema, hook) {
62
62
  return res.status(201).json(item);
63
63
  });
64
64
  }
65
- function updateBuilder(service, schema) {
65
+ function updateBuilder(update, pkSchema, schema) {
66
66
  return validate(schema, async (req, res, data) => {
67
- const item = await service.update(req.params.id, data);
67
+ const pk = pkSchema.parse(req.params);
68
+ const item = await update(pk, data);
68
69
  return res.status(200).json(item);
69
70
  });
70
71
  }
71
- function deleteBuilder(service) {
72
+ function deleteBuilder(remove, pkSchema) {
72
73
  return async (req, res) => {
73
- await service.delete(req.params.id);
74
+ const pk = pkSchema.parse(req.params);
75
+ await remove(pk);
74
76
  return res.status(204).send();
75
77
  };
76
78
  }
@@ -80,65 +82,3 @@ function validate(schema, fn) {
80
82
  return fn(req, res, validated);
81
83
  };
82
84
  }
83
- function getPagination(req) {
84
- return {
85
- page: req.query.page ? parseInt(req.query.page) : 1,
86
- pageSize: req.query.pageSize
87
- ? parseInt(req.query.pageSize)
88
- : 10
89
- };
90
- }
91
- class BaseController {
92
- constructor(service, base, filter, createSchema, updateSchema) {
93
- this.getAll = async (req, res) => {
94
- const filters = getFilters(req, this.filterSchema);
95
- const pagination = getPagination(req);
96
- const [items, total] = await Promise.all([
97
- this.service.findAll(filters, pagination.page, pagination.pageSize),
98
- this.service.count(filters)
99
- ]);
100
- res.status(200).json({
101
- pagination,
102
- items,
103
- total
104
- });
105
- };
106
- this.getById = async (req, res) => {
107
- const item = await this.service.findOne(req.params.id);
108
- res.status(200).json(item);
109
- };
110
- this.create = async (req, res) => {
111
- const data = this.createSchema.parse(req.body);
112
- const item = await this.service.create(data);
113
- res.status(201).json(item);
114
- };
115
- this.update = async (req, res) => {
116
- const data = this.updateSchema.parse(req.body);
117
- const item = await this.service.update(req.params.id, data);
118
- res.status(200).json(item);
119
- };
120
- this.delete = async (req, res) => {
121
- await this.service.delete(req.params.id);
122
- res.status(204).send();
123
- };
124
- this.service = service;
125
- // @ts-expect-error infer schema
126
- this.createSchema = createSchema || base.omit({
127
- id: true,
128
- deletedAt: true
129
- });
130
- // @ts-expect-error infer schema
131
- this.updateSchema = updateSchema || base.partial();
132
- this.filterSchema = filter;
133
- }
134
- }
135
- exports.BaseController = BaseController;
136
- /*
137
- * Extract the filters from the Request
138
- */
139
- function getFilters(req, filter) {
140
- return filter.parse({
141
- ...req.params,
142
- ...req.query
143
- });
144
- }
@@ -2,7 +2,6 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.errorHandler = exports.Unauthorized = exports.Forbidden = exports.BadRequest = exports.Duplicated = exports.NotFound = exports.ServiceError = void 0;
4
4
  exports.handleSqlError = handleSqlError;
5
- const drizzle_orm_1 = require("drizzle-orm");
6
5
  const zod_1 = require("zod");
7
6
  class ServiceError extends Error {
8
7
  constructor(statusCode, message) {
@@ -45,10 +44,16 @@ class Unauthorized extends ServiceError {
45
44
  exports.Unauthorized = Unauthorized;
46
45
  const errorHandler = (err, req, res, next) => {
47
46
  if (err instanceof ServiceError) {
48
- return res.status(err.statusCode).json({ message: err.message });
47
+ return res.status(err.statusCode).json({
48
+ success: false,
49
+ code: err.statusCode,
50
+ message: err.message
51
+ });
49
52
  }
50
53
  if (err instanceof zod_1.ZodError) {
51
54
  return res.status(400).json({
55
+ success: false,
56
+ code: 400,
52
57
  message: "Validation failed",
53
58
  errors: err.issues
54
59
  });
@@ -61,7 +66,7 @@ exports.errorHandler = errorHandler;
61
66
  // infiera bien los tipos
62
67
  function handleSqlError(e) {
63
68
  let err = e;
64
- if (e instanceof drizzle_orm_1.DrizzleQueryError && e.cause) {
69
+ if (instanceOf(e) && e.cause) {
65
70
  const cause = e.cause;
66
71
  if (/duplicate key/i.test(cause.message)) {
67
72
  if (isPgErrorWithDetail(cause)) {
@@ -82,6 +87,14 @@ function handleSqlError(e) {
82
87
  }
83
88
  throw err;
84
89
  }
90
+ // Safer version of instanceof that works on vitest too
91
+ function instanceOf(err) {
92
+ return typeof err === 'object' &&
93
+ err !== null &&
94
+ 'constructor' in err &&
95
+ 'name' in err.constructor &&
96
+ err.constructor.name === "DrizzleQueryError";
97
+ }
85
98
  function isPgErrorWithDetail(err) {
86
99
  return (typeof err === "object" &&
87
100
  err !== null &&
@@ -5,5 +5,8 @@ import { AnyPgTable, PgColumn } from "drizzle-orm/pg-core";
5
5
  export declare const createFilterSchema: import("drizzle-zod").CreateInsertSchema<{
6
6
  number: true;
7
7
  }>;
8
+ export declare const createPkSchema: import("drizzle-zod").CreateSelectSchema<{
9
+ number: true;
10
+ }>;
8
11
  export declare function buildFilters<FSchema extends ZodObject>(map: FilterMap<FSchema>, filters?: Filter<FSchema>): SQL<unknown> | undefined;
9
12
  export declare function buildPKFilters<T extends AnyPgTable>(pks: PgColumn[], values: PrimaryKeyData<T>): SQL<unknown> | undefined;
@@ -1,16 +1,17 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.createFilterSchema = void 0;
3
+ exports.createPkSchema = exports.createFilterSchema = void 0;
4
4
  exports.buildFilters = buildFilters;
5
5
  exports.buildPKFilters = buildPKFilters;
6
6
  const drizzle_orm_1 = require("drizzle-orm");
7
7
  const drizzle_zod_1 = require("drizzle-zod");
8
- const { createInsertSchema } = (0, drizzle_zod_1.createSchemaFactory)({
8
+ const { createInsertSchema, createSelectSchema } = (0, drizzle_zod_1.createSchemaFactory)({
9
9
  coerce: {
10
10
  number: true,
11
11
  },
12
12
  });
13
13
  exports.createFilterSchema = createInsertSchema;
14
+ exports.createPkSchema = createSelectSchema;
14
15
  function buildFilters(map, filters) {
15
16
  const conditions = [];
16
17
  if (!filters)
@@ -2,6 +2,7 @@ import { PgTable } from "drizzle-orm/pg-core";
2
2
  export declare function generateRouter(name: string, table: PgTable, imports: string[]): string;
3
3
  export declare function generateValidator(name: string, table: PgTable, imports: string[]): string;
4
4
  export declare function generateController(name: string, table: PgTable, imports: string[]): string;
5
+ export declare function generateControllerBuilder(name: string, table: PgTable, imports: string[]): string;
5
6
  export declare function generateService(name: string, table: PgTable, imports: string[]): string;
6
7
  export declare function generateFilter(name: string, table: PgTable, imports: string[]): string;
7
8
  export declare function getRelationName(fkName: string): string;
@@ -3,14 +3,21 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.generateRouter = generateRouter;
4
4
  exports.generateValidator = generateValidator;
5
5
  exports.generateController = generateController;
6
+ exports.generateControllerBuilder = generateControllerBuilder;
6
7
  exports.generateService = generateService;
7
8
  exports.generateFilter = generateFilter;
8
9
  exports.getRelationName = getRelationName;
9
- const pg_core_1 = require("drizzle-orm/pg-core");
10
10
  const pg_1 = require("./pg");
11
+ const drizzle_orm_1 = require("drizzle-orm");
11
12
  function generateRouter(name, table, imports) {
12
13
  const pkColumns = (0, pg_1.getPKs)(table);
13
- const route = pkColumns.map(pk => `${getRelationName(pk.name)}/:${pk.name}`).join("/");
14
+ let route = '';
15
+ if (pkColumns.length == 1) {
16
+ route = `:${pkColumns[0].name}`;
17
+ }
18
+ else {
19
+ route = pkColumns.map(pk => `${getRelationName(pk.name)}/:${pk.name}`).join("/");
20
+ }
14
21
  return `import { Router } from "express";
15
22
  ${imports.join('\n')};
16
23
  export const ${name}Router = Router();
@@ -22,19 +29,19 @@ ${name}Router.get("/", ${name}Controller.getAll);
22
29
  ${name}Router.post("/", ${name}Controller.create);
23
30
 
24
31
  // Get one
25
- ${name}Router.get("${route}", ${name}Controller.getOne);
32
+ ${name}Router.get("/${route}", ${name}Controller.getOne);
26
33
 
27
34
  // Update
28
- ${name}Router.put("${route}", ${name}Controller.update);
35
+ ${name}Router.put("/${route}", ${name}Controller.update);
29
36
 
30
37
  // Delete
31
- ${name}Router.delete("${route}", ${name}Controller.remove);`;
38
+ ${name}Router.delete("/${route}", ${name}Controller.remove);`;
32
39
  }
33
40
  function generateValidator(name, table, imports) {
34
41
  const pkColumns = (0, pg_1.getPKs)(table);
35
42
  const zodPkPicker = buildZodPkPicker(pkColumns);
36
43
  return `import { createInsertSchema, createSelectSchema } from "drizzle-zod";
37
- import { createFilterSchema } from "bradb";
44
+ import { createFilterSchema, createPkSchema } from "bradb";
38
45
  import { z } from "zod";
39
46
  ${imports.join('\n')};
40
47
 
@@ -42,7 +49,7 @@ const select = createSelectSchema(${name}Table);
42
49
  const insert = createInsertSchema(${name}Table);
43
50
  const update = insert.partial();
44
51
  const filter = createFilterSchema(${name}Table).partial();
45
- const pk = select.pick({
52
+ const pk = createPkSchema(${name}Table).pick({
46
53
  ${zodPkPicker}
47
54
  });
48
55
 
@@ -62,16 +69,18 @@ export const ${name}Validator = {
62
69
  }
63
70
  function generateController(name, table, imports) {
64
71
  return `import { Request, Response } from "express";
72
+ import { newPagination } from "bradb";
65
73
  ${imports.join('\n')};
66
74
 
67
75
  async function getAll(req: Request, res: Response) {
76
+ const pagination = newPagination(req.query);
68
77
  const filters = ${name}Validator.filter.parse(req.query);
69
- const items = await ${name}Service.findAll(filters);
70
- const total = await ${name}Service.count(filters);
78
+ const items = await ${name}Service.findAll(filters, pagination);
71
79
 
72
80
  res.json({
81
+ pagination,
73
82
  items,
74
- total
83
+ total: pagination.total // this is going to be removed
75
84
  });
76
85
  }
77
86
 
@@ -108,29 +117,44 @@ export const ${name}Controller = {
108
117
  remove
109
118
  };`;
110
119
  }
120
+ function generateControllerBuilder(name, table, imports) {
121
+ return `import {
122
+ findAllBuilder,
123
+ findOneBuilder,
124
+ updateBuilder,
125
+ deleteBuilder,
126
+ createBuilder
127
+ } from "bradb";
128
+ ${imports.join('\n')};
129
+
130
+ export const ${name}Controller = {
131
+ getAll: findAllBuilder(${name}Service.findAll, ${name}Validator.filter),
132
+ getOne: findOneBuilder(${name}Service.findOne, ${name}Validator.pk),
133
+ create: createBuilder(${name}Service.create, ${name}Validator.insert),
134
+ update: updateBuilder(${name}Service.update, ${name}Validator.pk, ${name}Validator.update),
135
+ remove: deleteBuilder(${name}Service.delete, ${name}Validator.pk)
136
+ };`;
137
+ }
111
138
  function generateService(name, table, imports) {
112
139
  return `import { ServiceBuilder } from "bradb";
113
140
  ${imports.join('\n')};
114
141
 
115
142
  const builder = new ServiceBuilder(db, ${name}Table, ${name}FilterMap);
116
143
 
117
- const selection = db
118
- .select()
119
- .from(${name}Table)
120
- .$dynamic();
121
-
122
144
  export const ${name}Service = {
123
145
  create: builder.create(),
124
146
  update: builder.update(),
125
- count: builder.count(),
126
147
  delete: builder.delete(),
127
- findAll: builder.findAll(selection),
128
- findOne: builder.findOne(selection)
148
+ findAll: builder.findAll(),
149
+ findOne: builder.findOne()
129
150
  };`;
130
151
  }
131
152
  function generateFilter(name, table, imports) {
132
- const { columns } = (0, pg_core_1.getTableConfig)(table);
133
- const filters = columns.map(c => `${c.name}: (val) => eq(${name}Table.${c.name}, val)`).join(',\n\t');
153
+ // const { columns } = getTableConfig(table);
154
+ const columns = (0, drizzle_orm_1.getTableColumns)(table);
155
+ const filters = Object.entries(columns).map(([colName, _]) => {
156
+ return `${colName}: (val) => eq(${name}Table.${colName}, val)`;
157
+ }).join(',\n\t');
134
158
  return `import { FilterMap } from "bradb";
135
159
  import { eq } from "drizzle-orm";
136
160
  ${imports.join('\n')};
@@ -3,6 +3,7 @@ import { AnyPgTable, PgColumn, PgSelect, PgTransaction } from "drizzle-orm/pg-co
3
3
  import { NodePgDatabase } from "drizzle-orm/node-postgres";
4
4
  import { InferInsertModel, SQL } from "drizzle-orm";
5
5
  import { ZodObject } from "zod";
6
+ import { Pagination } from "./controller";
6
7
  export declare class ServiceBuilder<T extends AnyPgTable, TSchema extends Record<string, unknown>, FSchema extends ZodObject, PKType extends object = PrimaryKeyData<T>> {
7
8
  private readonly db;
8
9
  private readonly table;
@@ -13,9 +14,9 @@ export declare class ServiceBuilder<T extends AnyPgTable, TSchema extends Record
13
14
  readonly findOneConditions: (pkFilter: PKType) => SQL | undefined;
14
15
  readonly tableName: string;
15
16
  constructor(db: NodePgDatabase<TSchema>, table: T, map: FilterMap<FSchema>);
16
- findOne<S extends PgSelect>(select: S): (pkValues: PKType) => Promise<Awaited<ReturnType<S["execute"]>>[number]>;
17
- findAll<S extends PgSelect>(select: S, paginated?: boolean): (filters?: Filter<FSchema>, page?: number, pageSize?: number) => Promise<Awaited<ReturnType<S["execute"]>>[number][]>;
18
- count(): (filters?: Filter<FSchema>) => Promise<number>;
17
+ findOne<S extends PgSelect>(select?: () => PgSelect): (pkValues: PKType) => Promise<S["_"]["result"][0]>;
18
+ findAll<S extends PgSelect>(select?: () => PgSelect, paginated?: boolean): (filters?: Filter<FSchema>, p?: Pagination) => Promise<S["_"]["result"][0][]>;
19
+ count<S extends PgSelect>(select?: S): (filters?: Filter<FSchema>) => Promise<number>;
19
20
  create<S extends any>(hook?: (data: S extends Object ? S : InferInsertModel<T>, tx?: PgTransaction<any, TSchema, any>) => Promise<InferInsertModel<T>>): (data: S extends Object ? S : InferInsertModel<T>, tx?: PgTransaction<any, TSchema, any>) => Promise<{ [Key in keyof T["_"]["columns"] & string as Key]: T["_"]["columns"][Key]["_"]["notNull"] extends true ? T["_"]["columns"][Key]["_"]["data"] : T["_"]["columns"][Key]["_"]["data"] | null; } extends infer T_1 ? { [K in keyof T_1]: T_1[K]; } : never>;
20
21
  update<S extends any>(pre?: (data: S extends Object ? S : Partial<InferInsertModel<T>>, tx?: PgTransaction<any, TSchema, any>) => Promise<InferInsertModel<T>>): (pks: PKType, data: S extends Object ? S : Partial<InferInsertModel<T>>, tx?: PgTransaction<any, TSchema, any>) => Promise<{ [Key in keyof T["_"]["columns"] & string as Key]: T["_"]["columns"][Key]["_"]["notNull"] extends true ? T["_"]["columns"][Key]["_"]["data"] : T["_"]["columns"][Key]["_"]["data"] | null; } extends infer T_1 ? { [K in keyof T_1]: T_1[K]; } : never>;
21
22
  delete(): (pks: PKType, tx?: PgTransaction<any, TSchema, any>) => Promise<void>;
@@ -24,30 +24,51 @@ class ServiceBuilder {
24
24
  }
25
25
  }
26
26
  findOne(select) {
27
+ if (!select)
28
+ select = () => this.db.select().from(this.table).$dynamic();
27
29
  return async (pkValues) => {
28
- const result = await select.where(this.findOneConditions(pkValues));
30
+ const result = await select().where(this.findOneConditions(pkValues));
29
31
  if (result.length == 0)
30
32
  throw notFoundWithId(this.tableName, pkValues);
31
33
  return result[0];
32
34
  };
33
35
  }
34
36
  findAll(select, paginated = true) {
35
- const base = (f) => select.where(this.findAllConditions(f));
37
+ if (!select)
38
+ select = () => this.db.select().from(this.table).$dynamic();
39
+ const base = (f) => select().where(this.findAllConditions(f));
36
40
  if (paginated) {
37
- return async (filters, page = 1, pageSize = 10) => {
38
- const offset = (page - 1) * pageSize;
39
- return await base(filters)
40
- .limit(pageSize)
41
- .offset(offset);
41
+ return async (filters, p) => {
42
+ if (!p)
43
+ p = { page: 1, pageSize: 10 };
44
+ const sub = select().where(this.findAllConditions(filters)).as('sub'); // TODO: not any
45
+ const countQuery = this.db
46
+ .select({ count: (0, drizzle_orm_1.count)() })
47
+ .from(sub);
48
+ const offset = (p.page - 1) * p.pageSize;
49
+ const items = await base(filters).limit(p.pageSize).offset(offset);
50
+ const [res] = await countQuery;
51
+ p.total = res.count;
52
+ p.count = items.length;
53
+ return items;
42
54
  };
43
55
  }
44
56
  else {
45
57
  return async (filters) => {
46
- return await base(filters);
58
+ const items = await base(filters);
59
+ return items;
47
60
  };
48
61
  }
49
62
  }
50
- count() {
63
+ count(select) {
64
+ if (select) {
65
+ return async (filters) => {
66
+ const base = select.where(this.findAllConditions(filters));
67
+ const subquery = base.as('countsubquery');
68
+ const [result] = await this.db.select({ count: (0, drizzle_orm_1.count)() }).from(subquery);
69
+ return result.count;
70
+ };
71
+ }
51
72
  return async (filters) => {
52
73
  const [result] = await this.db
53
74
  .select({ count: (0, drizzle_orm_1.count)() })
@@ -1,22 +1,12 @@
1
1
  import { SQL } from "drizzle-orm";
2
- import { AnyPgTable, PgColumn, PgTable, PgTransaction } from "drizzle-orm/pg-core";
2
+ import { AnyPgTable, PgTable, PgTransaction } from "drizzle-orm/pg-core";
3
3
  import z, { ZodObject } from "zod";
4
- export interface RetrieverService<FSchema extends ZodObject, TTable extends PgTable> {
5
- findAll: (filters?: Filter<FSchema>, page?: number, pageSize?: number) => Promise<TTable["$inferSelect"][]>;
6
- count: (filters: Filter<FSchema>) => Promise<number>;
7
- }
8
- export interface FindOneService<TTable extends PgTable> {
9
- findOne: (id: PrimaryKeyType<TTable>) => Promise<Partial<TTable>>;
10
- }
11
- export interface CreateService<TTable extends PgTable, TSchema extends Record<string, unknown>> {
12
- create: (data: TTable["$inferInsert"], tx?: PgTransaction<any, TSchema, any>) => Promise<TTable["$inferSelect"]>;
13
- }
14
- export interface UpdateService<TTable extends PgTable, TSchema extends Record<string, unknown>> {
15
- update: (id: PrimaryKeyType<TTable>, data: Partial<TTable["$inferInsert"]>, tx?: PgTransaction<any, TSchema, any>) => Promise<TTable["$inferSelect"]>;
16
- }
17
- export interface DeleteService<TTable extends PgTable, TSchema extends Record<string, unknown>> {
18
- delete: (id: PrimaryKeyType<TTable>, tx?: PgTransaction<any, TSchema, any>) => Promise<void>;
19
- }
4
+ export type FindOneFunc<T extends PgTable> = (pks: PrimaryKeyData<T>) => Promise<object>;
5
+ export type FindAllFunc<F extends ZodObject> = (filters?: F, page?: number, pageSize?: number) => Promise<object>;
6
+ export type CountFunc<F extends ZodObject> = (filters?: F) => Promise<object>;
7
+ export type CreateFunc<T extends PgTable> = (data: T["$inferInsert"], tx?: PgTransaction<any, any, any>) => Promise<T["$inferSelect"]>;
8
+ export type UpdateFunc<T extends PgTable> = (pks: PrimaryKeyData<T>, data: Partial<T["$inferInsert"]>, tx?: PgTransaction<any, any, any>) => Promise<T["$inferSelect"]>;
9
+ export type DeleteFunc<T extends PgTable> = (pks: PrimaryKeyData<T>) => Promise<void>;
20
10
  export type PrimaryKeyData<TTable extends AnyPgTable> = {
21
11
  [K in keyof TTable["_"]["columns"] as TTable["_"]["columns"][K] extends {
22
12
  _: {
@@ -24,15 +14,6 @@ export type PrimaryKeyData<TTable extends AnyPgTable> = {
24
14
  };
25
15
  } ? K : never]: TTable["_"]["columns"][K]["_"]["data"];
26
16
  };
27
- export interface PaginationParams {
28
- page: number;
29
- pageSize: number;
30
- }
31
- export type Table = AnyPgTable & {
32
- deletedAt: PgColumn;
33
- id: any;
34
- };
35
- export type PrimaryKeyType<T extends AnyPgTable> = T["_"]["columns"]["id"]["_"]["data"];
36
17
  export type FilterMap<Schema extends z.ZodObject, Out = z.infer<Schema>> = {
37
18
  [K in keyof Out]: (value: NonNullable<Out[K]>) => SQL;
38
19
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bradb",
3
- "version": "3.1.1",
3
+ "version": "3.1.3",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",