@starbemtech/star-db-query-builder 1.0.13 → 1.0.15

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,6 +1,6 @@
1
1
  <?xml version="1.0" encoding="UTF-8"?>
2
- <coverage generated="1712321331534" clover="3.2.0">
3
- <project timestamp="1712321331534" name="All files">
2
+ <coverage generated="1713917118420" clover="3.2.0">
3
+ <project timestamp="1713917118420" name="All files">
4
4
  <metrics statements="0" coveredstatements="0" conditionals="0" coveredconditionals="0" methods="0" coveredmethods="0" elements="0" coveredelements="0" complexity="0" loc="0" ncloc="0" packages="0" files="0" classes="0"/>
5
5
  </project>
6
6
  </coverage>
@@ -86,7 +86,7 @@
86
86
  <div class='footer quiet pad2 space-top1 center small'>
87
87
  Code coverage generated by
88
88
  <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
89
- at 2024-04-05T12:48:51.531Z
89
+ at 2024-04-24T00:05:18.412Z
90
90
  </div>
91
91
  <script src="prettify.js"></script>
92
92
  <script>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@starbemtech/star-db-query-builder",
3
- "version": "1.0.13",
3
+ "version": "1.0.15",
4
4
  "description": "A query builder to be used with mysql or postgres",
5
5
  "author": "Julio Sousa <julio.sousa@starbem.app>",
6
6
  "main": "./dist/index.js",
@@ -1,5 +1,5 @@
1
1
  import { v4 as uuid } from 'uuid'
2
- import { QueryParams } from './types'
2
+ import { QueryParams, QueryBuilder } from './types'
3
3
  import {
4
4
  createGroupByClause,
5
5
  createLimitClause,
@@ -145,7 +145,7 @@ export const update = async <P, R>({
145
145
  const values: any[] = Object.values(data)
146
146
 
147
147
  const setClause = generateSetClause(keys, dbClient.clientType)
148
- let query = `UPDATE ${tableName} SET ${setClause} WHERE ${id}`
148
+ let query = `UPDATE ${tableName} SET ${setClause} WHERE id = '${id}'`
149
149
 
150
150
  if (dbClient.clientType === 'pg') {
151
151
  if (returning && returning.length > 0) {
@@ -195,3 +195,47 @@ export const deleteOne = async <T>({
195
195
  [id]
196
196
  )
197
197
  }
198
+
199
+ export const joins = async <T>({
200
+ tableName,
201
+ dbClient,
202
+ select,
203
+ joins,
204
+ where,
205
+ }: QueryParams<T>): Promise<T[]> => {
206
+ if (!tableName) throw new Error('Table name is required')
207
+ if (!dbClient) throw new Error('DB client is required')
208
+
209
+ const fields = Array.isArray(select) ? select : []
210
+ const selectFields = createSelectFields(fields, dbClient.clientType)
211
+ const [whereClause, params] = createWhereClause(where, 1, dbClient.clientType)
212
+
213
+ const queryBuilder: QueryBuilder = {
214
+ select: [selectFields],
215
+ from: tableName,
216
+ joins: joins,
217
+ where: whereClause,
218
+ }
219
+
220
+ const queryString = await buildQuery(queryBuilder)
221
+
222
+ const rows = await dbClient.query<T[]>(queryString, params)
223
+
224
+ return rows
225
+ }
226
+
227
+ async function buildQuery(params: QueryBuilder): Promise<string> {
228
+ let queryString = `SELECT ${params.select.join(', ')} FROM ${params.from}`
229
+
230
+ if (params.joins) {
231
+ for (const join of params.joins) {
232
+ queryString += ` ${join.type} JOIN ${join.table} ON ${join.on}`
233
+ }
234
+ }
235
+
236
+ if (params.where) {
237
+ queryString += `${params.where}`
238
+ }
239
+
240
+ return queryString
241
+ }
@@ -31,6 +31,7 @@ export type Condition<T> = OperatorCondition | LogicalCondition<T>
31
31
  interface LogicalCondition<T> {
32
32
  OR?: Conditions<T>[]
33
33
  AND?: Conditions<T>[]
34
+ JOINS?: Conditions<object>
34
35
  }
35
36
 
36
37
  export type Conditions<T> = {
@@ -41,6 +42,7 @@ export type DBClients = 'pg' | 'mysql'
41
42
 
42
43
  export type OrderBy = { field: string; direction: 'ASC' | 'DESC' }[]
43
44
 
45
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
44
46
  export interface QueryParams<T> {
45
47
  tableName: string
46
48
  dbClient: IDatabaseClient
@@ -50,4 +52,18 @@ export interface QueryParams<T> {
50
52
  orderBy?: OrderBy
51
53
  groupBy?: string[]
52
54
  limit?: number
55
+ joins?: JoinClause[]
56
+ }
57
+
58
+ interface JoinClause {
59
+ type: 'INNER' | 'LEFT' | 'RIGHT' | 'FULL'
60
+ table: string
61
+ on: string
62
+ }
63
+
64
+ export interface QueryBuilder {
65
+ select: string[]
66
+ from?: string
67
+ joins?: JoinClause[]
68
+ where?: string
53
69
  }
@@ -63,12 +63,19 @@ export const createWhereClause = <T>(
63
63
  : mysqlPlaceholderGenerator()
64
64
  )
65
65
  .join(', ')
66
+
66
67
  if (operator === 'BETWEEN') {
67
68
  whereParts.push(
68
69
  clientType === 'pg'
69
70
  ? `"${key}" ${operator} ${placeholders.replace(', ', ' AND ')}`
70
71
  : `${key} ${operator} ${placeholders.replace(', ', ' AND ')}`
71
72
  )
73
+ } else if (operator === 'IN') {
74
+ whereParts.push(
75
+ clientType === 'pg'
76
+ ? `"${key}" ${operator} ${placeholders.replace(', ', ' AND ')}`
77
+ : `${key} ${operator} (${placeholders})`
78
+ )
72
79
  } else {
73
80
  whereParts.push(
74
81
  clientType === 'pg'
@@ -83,14 +90,48 @@ export const createWhereClause = <T>(
83
90
  ? `"${key}" ${operator} ${pgPlaceholderGenerator(index++)}`
84
91
  : `${key} ${operator} ${mysqlPlaceholderGenerator()}`
85
92
  )
93
+
86
94
  values.push(value)
87
95
  }
88
- } else {
89
- console.error('Unsupported condition format: ', condition)
90
96
  }
91
97
  }
92
98
  }
93
99
 
100
+ if ('JOINS' in conditions) {
101
+ const logicalOperator = conditions.JOINS ? 'AND' : 'OR'
102
+ const compositeConditions = conditions.JOINS
103
+
104
+ if (Array.isArray(compositeConditions)) {
105
+ const subWhereParts = compositeConditions
106
+ .map((subCondition: any) => {
107
+ if (
108
+ typeof subCondition === 'object' &&
109
+ !Array.isArray(subCondition) &&
110
+ subCondition !== null
111
+ ) {
112
+ const key = Object.keys(subCondition)[0]
113
+ const condition = subCondition[key]
114
+
115
+ processCondition(key, condition)
116
+ return whereParts.pop()
117
+ }
118
+ return ''
119
+ })
120
+ .filter((part) => part)
121
+ whereParts.push(
122
+ clientType === 'pg'
123
+ ? `(${subWhereParts.join(` ${logicalOperator} `)})`
124
+ : `${subWhereParts.join(` ${logicalOperator} `)}`
125
+ )
126
+ }
127
+ } else {
128
+ Object.entries(conditions).forEach(([key, value]) => {
129
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
130
+ // @ts-expect-error
131
+ processCondition(key, value)
132
+ })
133
+ }
134
+
94
135
  if ('OR' in conditions || 'AND' in conditions) {
95
136
  const logicalOperator = conditions.OR ? 'OR' : 'AND'
96
137
  const compositeConditions = conditions.OR || conditions.AND
@@ -21,7 +21,7 @@ export const getByUserAndSubscription = async <T>({
21
21
  `SELECT
22
22
  ${fields}
23
23
  FROM payments.plan_association_users pa
24
- INNER JOIN payments.plans ON pa.plan_id = plans.id
24
+ LEFT JOIN payments.plans ON pa.plan_id = plans.id
25
25
  ${whereClause}
26
26
  AND pa.status IN ('active', 'cancelation_requested', 'blocked')`,
27
27
  params