@ruiapp/rapid-core 0.5.0 → 0.5.1

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.
package/dist/index.js CHANGED
@@ -202,8 +202,8 @@ const pgPropertyTypeColumnMap = {
202
202
  "image[]": "jsonb",
203
203
  };
204
204
 
205
- const objLeftQuoteChar = "\"";
206
- const objRightQuoteChar = "\"";
205
+ const objLeftQuoteChar = '"';
206
+ const objRightQuoteChar = '"';
207
207
  const relationalOperatorsMap = new Map([
208
208
  ["eq", "="],
209
209
  ["ne", "<>"],
@@ -250,6 +250,9 @@ class QueryBuilder {
250
250
  }
251
251
  }
252
252
  }
253
+ formatValueToSqlLiteral(value) {
254
+ return formatValueToSqlLiteral(value);
255
+ }
253
256
  select(model, options) {
254
257
  const ctx = {
255
258
  model,
@@ -369,7 +372,7 @@ class QueryBuilder {
369
372
  paramToLiteral: false,
370
373
  };
371
374
  let { filters } = options;
372
- let command = "SELECT COUNT(*)::int as \"count\" FROM ";
375
+ let command = 'SELECT COUNT(*)::int as "count" FROM ';
373
376
  command += this.quoteTable(model);
374
377
  if (filters && filters.length) {
375
378
  command += " WHERE ";
@@ -389,7 +392,7 @@ class QueryBuilder {
389
392
  paramToLiteral: false,
390
393
  };
391
394
  let { filters } = options;
392
- let command = "SELECT COUNT(*)::int as \"count\" FROM ";
395
+ let command = 'SELECT COUNT(*)::int as "count" FROM ';
393
396
  command += `${this.quoteTable(derivedModel)} LEFT JOIN ${this.quoteTable(baseModel)} ON ${this.quoteObject(derivedModel.tableName)}.id = ${this.quoteObject(baseModel.tableName)}.id`;
394
397
  if (filters && filters.length) {
395
398
  command += " WHERE ";
@@ -4638,7 +4641,7 @@ function listDataDictionaries(server) {
4638
4641
  async function syncDatabaseSchema(server, applicationConfig) {
4639
4642
  const logger = server.getLogger();
4640
4643
  logger.info("Synchronizing database schema...");
4641
- const sqlQueryTableInformations = `SELECT table_schema, table_name FROM information_schema.tables`;
4644
+ const sqlQueryTableInformations = `SELECT table_schema, table_name, obj_description((table_schema||'.'||quote_ident(table_name))::regclass) as table_description FROM information_schema.tables`;
4642
4645
  const tablesInDb = await server.queryDatabaseObject(sqlQueryTableInformations);
4643
4646
  const { queryBuilder } = server;
4644
4647
  for (const model of applicationConfig.models) {
@@ -4649,9 +4652,14 @@ async function syncDatabaseSchema(server, applicationConfig) {
4649
4652
  if (!tableInDb) {
4650
4653
  await server.queryDatabaseObject(`CREATE TABLE IF NOT EXISTS ${queryBuilder.quoteTable(model)} ()`, []);
4651
4654
  }
4655
+ if (!tableInDb || tableInDb.table_description != model.name) {
4656
+ await server.queryDatabaseObject(`COMMENT ON TABLE ${queryBuilder.quoteTable(model)} IS ${queryBuilder.formatValueToSqlLiteral(model.name)};`, []);
4657
+ }
4652
4658
  }
4653
- const sqlQueryColumnInformations = `SELECT table_schema, table_name, column_name, data_type, udt_name, is_nullable, column_default, character_maximum_length, numeric_precision, numeric_scale
4654
- FROM information_schema.columns;`;
4659
+ const sqlQueryColumnInformations = `SELECT c.table_schema, c.table_name, c.column_name, c.ordinal_position, d.description, c.data_type, c.udt_name, c.is_nullable, c.column_default, c.character_maximum_length, c.numeric_precision, c.numeric_scale
4660
+ FROM information_schema.columns c
4661
+ INNER JOIN pg_catalog.pg_statio_all_tables st ON (st.schemaname = c.table_schema and st.relname = c.table_name)
4662
+ LEFT JOIN pg_catalog.pg_description d ON (d.objoid = st.relid and d.objsubid = c.ordinal_position);`;
4655
4663
  const columnsInDb = await server.queryDatabaseObject(sqlQueryColumnInformations, []);
4656
4664
  for (const model of applicationConfig.models) {
4657
4665
  logger.debug(`Checking data columns for '${model.namespace}.${model.singularCode}'...`);
@@ -4678,6 +4686,9 @@ async function syncDatabaseSchema(server, applicationConfig) {
4678
4686
  notNull: property.required,
4679
4687
  });
4680
4688
  }
4689
+ if (!columnInDb || columnInDb.description != property.name) {
4690
+ await server.queryDatabaseObject(`COMMENT ON COLUMN ${queryBuilder.quoteTable(model)}.${queryBuilder.quoteObject(property.targetIdColumnName)} IS ${queryBuilder.formatValueToSqlLiteral(property.name)};`, []);
4691
+ }
4681
4692
  }
4682
4693
  else if (property.relation === "many") {
4683
4694
  if (property.linkTableName) {
@@ -4780,6 +4791,9 @@ async function syncDatabaseSchema(server, applicationConfig) {
4780
4791
  }
4781
4792
  }
4782
4793
  }
4794
+ if (!columnInDb || columnInDb.description != property.name) {
4795
+ await server.queryDatabaseObject(`COMMENT ON COLUMN ${queryBuilder.quoteTable(model)}.${queryBuilder.quoteObject(property.columnName || property.code)} IS ${queryBuilder.formatValueToSqlLiteral(property.name)};`, []);
4796
+ }
4783
4797
  }
4784
4798
  }
4785
4799
  }
@@ -1,4 +1,4 @@
1
- import { RpdDataModel, CreateEntityOptions, QuoteTableOptions, DatabaseQuery } from "../types";
1
+ import { RpdDataModel, CreateEntityOptions, QuoteTableOptions, DatabaseQuery, IQueryBuilder } from "../types";
2
2
  import { CountRowOptions, DeleteRowOptions, FindRowOptions, RowFilterOptions, UpdateRowOptions, ColumnSelectOptions } from "../dataAccess/dataAccessTypes";
3
3
  export interface BuildQueryContext {
4
4
  model: RpdDataModel;
@@ -13,12 +13,13 @@ export interface BuildQueryContext {
13
13
  export interface InitQueryBuilderOptions {
14
14
  dbDefaultSchema: string;
15
15
  }
16
- export default class QueryBuilder {
16
+ export default class QueryBuilder implements IQueryBuilder {
17
17
  #private;
18
18
  constructor(options: InitQueryBuilderOptions);
19
19
  quoteTable(options: QuoteTableOptions): string;
20
20
  quoteObject(name: string): string;
21
21
  quoteColumn(model: RpdDataModel, column: ColumnSelectOptions, emitTableAlias: boolean): string;
22
+ formatValueToSqlLiteral(value: any): string;
22
23
  select(model: RpdDataModel, options: FindRowOptions): DatabaseQuery;
23
24
  selectDerived(derivedModel: RpdDataModel, baseModel: RpdDataModel, options: FindRowOptions): DatabaseQuery;
24
25
  count(model: RpdDataModel, options: CountRowOptions): DatabaseQuery;
package/dist/types.d.ts CHANGED
@@ -154,6 +154,7 @@ export interface IQueryBuilder {
154
154
  quoteTable: (options: QuoteTableOptions) => string;
155
155
  quoteObject: (name: string) => string;
156
156
  buildFiltersExpression(model: RpdDataModel, filters: RowFilterOptions[]): any;
157
+ formatValueToSqlLiteral: (value: any) => string;
157
158
  }
158
159
  export interface RpdApplicationConfig {
159
160
  code?: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ruiapp/rapid-core",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -181,12 +181,15 @@ function listDataDictionaries(server: IRpdServer) {
181
181
  type TableInformation = {
182
182
  table_schema: string;
183
183
  table_name: string;
184
+ table_description: string;
184
185
  };
185
186
 
186
187
  type ColumnInformation = {
187
188
  table_schema: string;
188
189
  table_name: string;
189
190
  column_name: string;
191
+ ordinal_position: number;
192
+ description?: string;
190
193
  data_type: string;
191
194
  udt_name: string;
192
195
  is_nullable: "YES" | "NO";
@@ -206,7 +209,7 @@ type ConstraintInformation = {
206
209
  async function syncDatabaseSchema(server: IRpdServer, applicationConfig: RpdApplicationConfig) {
207
210
  const logger = server.getLogger();
208
211
  logger.info("Synchronizing database schema...");
209
- const sqlQueryTableInformations = `SELECT table_schema, table_name FROM information_schema.tables`;
212
+ const sqlQueryTableInformations = `SELECT table_schema, table_name, obj_description((table_schema||'.'||quote_ident(table_name))::regclass) as table_description FROM information_schema.tables`;
210
213
  const tablesInDb: TableInformation[] = await server.queryDatabaseObject(sqlQueryTableInformations);
211
214
  const { queryBuilder } = server;
212
215
 
@@ -219,10 +222,15 @@ async function syncDatabaseSchema(server: IRpdServer, applicationConfig: RpdAppl
219
222
  if (!tableInDb) {
220
223
  await server.queryDatabaseObject(`CREATE TABLE IF NOT EXISTS ${queryBuilder.quoteTable(model)} ()`, []);
221
224
  }
225
+ if (!tableInDb || tableInDb.table_description != model.name) {
226
+ await server.queryDatabaseObject(`COMMENT ON TABLE ${queryBuilder.quoteTable(model)} IS ${queryBuilder.formatValueToSqlLiteral(model.name)};`, []);
227
+ }
222
228
  }
223
229
 
224
- const sqlQueryColumnInformations = `SELECT table_schema, table_name, column_name, data_type, udt_name, is_nullable, column_default, character_maximum_length, numeric_precision, numeric_scale
225
- FROM information_schema.columns;`;
230
+ const sqlQueryColumnInformations = `SELECT c.table_schema, c.table_name, c.column_name, c.ordinal_position, d.description, c.data_type, c.udt_name, c.is_nullable, c.column_default, c.character_maximum_length, c.numeric_precision, c.numeric_scale
231
+ FROM information_schema.columns c
232
+ INNER JOIN pg_catalog.pg_statio_all_tables st ON (st.schemaname = c.table_schema and st.relname = c.table_name)
233
+ LEFT JOIN pg_catalog.pg_description d ON (d.objoid = st.relid and d.objsubid = c.ordinal_position);`;
226
234
  const columnsInDb: ColumnInformation[] = await server.queryDatabaseObject(sqlQueryColumnInformations, []);
227
235
 
228
236
  for (const model of applicationConfig.models) {
@@ -253,6 +261,15 @@ async function syncDatabaseSchema(server: IRpdServer, applicationConfig: RpdAppl
253
261
  notNull: property.required,
254
262
  });
255
263
  }
264
+
265
+ if (!columnInDb || columnInDb.description != property.name) {
266
+ await server.queryDatabaseObject(
267
+ `COMMENT ON COLUMN ${queryBuilder.quoteTable(model)}.${queryBuilder.quoteObject(
268
+ property.targetIdColumnName,
269
+ )} IS ${queryBuilder.formatValueToSqlLiteral(property.name)};`,
270
+ [],
271
+ );
272
+ }
256
273
  } else if (property.relation === "many") {
257
274
  if (property.linkTableName) {
258
275
  const tableInDb = find(tablesInDb, {
@@ -359,6 +376,15 @@ async function syncDatabaseSchema(server: IRpdServer, applicationConfig: RpdAppl
359
376
  }
360
377
  }
361
378
  }
379
+
380
+ if (!columnInDb || columnInDb.description != property.name) {
381
+ await server.queryDatabaseObject(
382
+ `COMMENT ON COLUMN ${queryBuilder.quoteTable(model)}.${queryBuilder.quoteObject(
383
+ property.columnName || property.code,
384
+ )} IS ${queryBuilder.formatValueToSqlLiteral(property.name)};`,
385
+ [],
386
+ );
387
+ }
362
388
  }
363
389
  }
364
390
  }
@@ -1,5 +1,5 @@
1
1
  import { find, isBoolean, isNull, isNumber, isString, isUndefined } from "lodash";
2
- import { RpdDataModel, RpdDataModelProperty, CreateEntityOptions, QuoteTableOptions, DatabaseQuery } from "../types";
2
+ import { RpdDataModel, RpdDataModelProperty, CreateEntityOptions, QuoteTableOptions, DatabaseQuery, IQueryBuilder } from "../types";
3
3
  import {
4
4
  CountRowOptions,
5
5
  DeleteRowOptions,
@@ -19,8 +19,8 @@ import {
19
19
  } from "~/dataAccess/dataAccessTypes";
20
20
  import { pgPropertyTypeColumnMap } from "~/dataAccess/columnTypeMapper";
21
21
 
22
- const objLeftQuoteChar = "\"";
23
- const objRightQuoteChar = "\"";
22
+ const objLeftQuoteChar = '"';
23
+ const objRightQuoteChar = '"';
24
24
 
25
25
  const relationalOperatorsMap = new Map<RowFilterRelationalOperators, string>([
26
26
  ["eq", "="],
@@ -46,7 +46,7 @@ export interface InitQueryBuilderOptions {
46
46
  dbDefaultSchema: string;
47
47
  }
48
48
 
49
- export default class QueryBuilder {
49
+ export default class QueryBuilder implements IQueryBuilder {
50
50
  #dbDefaultSchema: string;
51
51
 
52
52
  constructor(options: InitQueryBuilderOptions) {
@@ -84,6 +84,10 @@ export default class QueryBuilder {
84
84
  }
85
85
  }
86
86
 
87
+ formatValueToSqlLiteral(value: any) {
88
+ return formatValueToSqlLiteral(value);
89
+ }
90
+
87
91
  select(model: RpdDataModel, options: FindRowOptions): DatabaseQuery {
88
92
  const ctx: BuildQueryContext = {
89
93
  model,
@@ -223,7 +227,7 @@ export default class QueryBuilder {
223
227
  paramToLiteral: false,
224
228
  };
225
229
  let { filters } = options;
226
- let command = "SELECT COUNT(*)::int as \"count\" FROM ";
230
+ let command = 'SELECT COUNT(*)::int as "count" FROM ';
227
231
 
228
232
  command += this.quoteTable(model);
229
233
 
@@ -247,7 +251,7 @@ export default class QueryBuilder {
247
251
  paramToLiteral: false,
248
252
  };
249
253
  let { filters } = options;
250
- let command = "SELECT COUNT(*)::int as \"count\" FROM ";
254
+ let command = 'SELECT COUNT(*)::int as "count" FROM ';
251
255
 
252
256
  command += `${this.quoteTable(derivedModel)} LEFT JOIN ${this.quoteTable(baseModel)} ON ${this.quoteObject(derivedModel.tableName)}.id = ${this.quoteObject(
253
257
  baseModel.tableName,
@@ -498,11 +502,10 @@ function buildRangeFilterQuery(ctx: BuildQueryContext, filter: FindRowRangeFilte
498
502
  ctx.params.push(filter.value[0]);
499
503
  command += `$${ctx.params.length}`;
500
504
 
501
- command += " AND "
505
+ command += " AND ";
502
506
 
503
507
  ctx.params.push(filter.value[1]);
504
508
  command += `$${ctx.params.length}`;
505
-
506
509
  } else {
507
510
  throw new Error(`Filter operator '${filter.operator}' is not supported.`);
508
511
  }
package/src/types.ts CHANGED
@@ -179,6 +179,7 @@ export interface IQueryBuilder {
179
179
  quoteTable: (options: QuoteTableOptions) => string;
180
180
  quoteObject: (name: string) => string;
181
181
  buildFiltersExpression(model: RpdDataModel, filters: RowFilterOptions[]);
182
+ formatValueToSqlLiteral: (value: any) => string;
182
183
  }
183
184
 
184
185
  export interface RpdApplicationConfig {