@storecraft/database-sql-base 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.
package/index.js CHANGED
@@ -40,35 +40,35 @@ const assert = (b, msg) => {
40
40
  export class SQL {
41
41
 
42
42
  /** @type {boolean} */
43
- #_is_ready;
43
+ #is_ready;
44
44
 
45
45
  /** @type {App<any, any, any>} */
46
- #_app;
46
+ #app;
47
47
 
48
48
  /** @type {ConfigType} */
49
- #_config;
49
+ #config;
50
50
 
51
51
  /** @type {Kysely<Database>} */
52
- #_client;
52
+ #client;
53
53
 
54
54
  /** @type {db_driver["resources"]} */
55
- #_resources;
55
+ #resources;
56
56
 
57
57
  /**
58
58
  *
59
59
  * @param {ConfigType} [config] config
60
60
  */
61
61
  constructor(config) {
62
- this.#_is_ready = false;
63
- this.#_config = config;
62
+ this.#is_ready = false;
63
+ this.#config = config;
64
64
 
65
65
  assert(
66
- this.#_config.dialect,
66
+ this.#config.dialect,
67
67
  'No Dialect found !'
68
68
  );
69
69
 
70
70
  assert(
71
- this.#_config.dialect_type,
71
+ this.#config.dialect_type,
72
72
  'No Dialect Type specified !'
73
73
  );
74
74
  }
@@ -87,9 +87,9 @@ export class SQL {
87
87
  if(this.isReady)
88
88
  return this;
89
89
 
90
- this.#_app = app;
90
+ this.#app = app;
91
91
 
92
- this.#_resources = {
92
+ this.#resources = {
93
93
  auth_users: auth_users(this),
94
94
  collections: collections(this),
95
95
  customers: customers(this),
@@ -106,7 +106,7 @@ export class SQL {
106
106
  search: search(this),
107
107
  }
108
108
 
109
- this.#_is_ready = true;
109
+ this.#is_ready = true;
110
110
 
111
111
  return this;
112
112
  }
@@ -118,9 +118,10 @@ export class SQL {
118
118
 
119
119
  /**
120
120
  * `database` resources
121
+ * @type {db_driver["resources"]}
121
122
  */
122
123
  get resources () {
123
- return this.#_resources;
124
+ return this.#resources;
124
125
  }
125
126
 
126
127
  get name() {
@@ -128,11 +129,11 @@ export class SQL {
128
129
  }
129
130
 
130
131
  get app() {
131
- return this.#_app;
132
+ return this.#app;
132
133
  }
133
134
 
134
135
  get client() {
135
- this.#_client = this.#_client ?? new Kysely(
136
+ this.#client = this.#client ?? new Kysely(
136
137
  {
137
138
  dialect: this.config.dialect,
138
139
  plugins: [
@@ -142,19 +143,19 @@ export class SQL {
142
143
  }
143
144
  );
144
145
 
145
- return this.#_client;
146
+ return this.#client;
146
147
  }
147
148
 
148
149
  get config() {
149
- return this.#_config;
150
+ return this.#config;
150
151
  }
151
152
 
152
153
  get isReady() {
153
- return this.#_is_ready;
154
+ return this.#is_ready;
154
155
  }
155
156
 
156
157
  get dialectType() {
157
- return this.#_config.dialect_type;
158
+ return this.#config.dialect_type;
158
159
  }
159
160
 
160
161
  get isSqlite() {
package/migrate.js CHANGED
@@ -23,7 +23,7 @@ export async function migrateToLatest(db_driver, destroy_db_upon_completion=true
23
23
  if(!db_driver?.client)
24
24
  throw new Error('No Kysely client found !!!');
25
25
 
26
- console.log('Resolving migrations')
26
+ console.log('Resolving migrations. This may take 2 minutes ...')
27
27
 
28
28
  let db = db_driver.client;
29
29
 
@@ -63,7 +63,7 @@ export async function migrateToLatest(db_driver, destroy_db_upon_completion=true
63
63
 
64
64
  if (error) {
65
65
  console.error('failed to migrate')
66
- console.error(error)
66
+ console.error(JSON.stringify(error, null, 2))
67
67
  process.exit(1)
68
68
  }
69
69
 
@@ -0,0 +1 @@
1
+ export * from '../migrations.shared/00003_alter_auth_users.js'
@@ -0,0 +1 @@
1
+ export * from '../migrations.shared/00003_alter_auth_users.js'
@@ -1,10 +1,10 @@
1
+ /**
2
+ * @import { Database } from '../types.sql.tables.js'
3
+ */
1
4
  import { Kysely } from 'kysely'
2
5
  import { upsert } from '../src/con.templates.js'
3
6
  import { templates } from '@storecraft/core/assets/seed-templates.js';
4
7
 
5
- /**
6
- * @typedef {import('../types.sql.tables.js').Database} Database
7
- */
8
8
 
9
9
  /**
10
10
  *
@@ -0,0 +1,43 @@
1
+ /**
2
+ * @import { Database } from '../types.sql.tables.js'
3
+ */
4
+ import { Kysely } from 'kysely'
5
+
6
+ /**
7
+ *
8
+ * @param {Kysely<Database>} db
9
+ */
10
+ export async function up(db) {
11
+
12
+ try {
13
+ await db.schema
14
+ .alterTable('auth_users')
15
+ .addColumn('firstname', 'text')
16
+ .execute();
17
+
18
+ await db.schema
19
+ .alterTable('auth_users')
20
+ .addColumn('lastname', 'text')
21
+ .execute();
22
+ } catch (e) {
23
+ }
24
+ }
25
+
26
+ /**
27
+ *
28
+ * @param {Kysely<Database>} db
29
+ */
30
+ export async function down(db) {
31
+ try {
32
+ await db.schema
33
+ .alterTable('auth_users')
34
+ .dropColumn('firstname')
35
+ .execute();
36
+
37
+ await db.schema
38
+ .alterTable('auth_users')
39
+ .dropColumn('lastname')
40
+ .execute();
41
+ } catch (e) {
42
+ }
43
+ }
@@ -0,0 +1 @@
1
+ export * from '../migrations.shared/00003_alter_auth_users.js'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@storecraft/database-sql-base",
3
- "version": "1.0.13",
3
+ "version": "1.0.15",
4
4
  "description": "Official SQL Database driver for storecraft",
5
5
  "license": "MIT",
6
6
  "author": "Tomer Shalev (https://github.com/store-craft)",
@@ -8,6 +8,7 @@ import {
8
8
  regular_upsert_me, where_id_or_handle_table, with_media, with_tags
9
9
  } from './con.shared.js'
10
10
  import { query_to_eb, query_to_sort } from './utils.query.js';
11
+ import { remove as remove_customer_and_auth_user } from './con.customers.js';
11
12
 
12
13
 
13
14
  export const table_name = 'auth_users';
@@ -35,9 +36,12 @@ const upsert = (driver) => {
35
36
  created_at: item.created_at,
36
37
  updated_at: item.updated_at,
37
38
  id: item.id,
39
+ attributes: JSON.stringify(item.attributes),
40
+ description: item.description,
41
+ active: item.active ? 1 : 0,
38
42
  roles: JSON.stringify(item.roles),
39
- // firstname: item.firstname,
40
- // lastname: item.lastname
43
+ firstname: item.firstname,
44
+ lastname: item.lastname
41
45
  });
42
46
  }
43
47
  );
@@ -73,7 +77,8 @@ const getByEmail = (driver) => {
73
77
  return async (email) => {
74
78
  return driver.client
75
79
  .selectFrom('auth_users')
76
- .selectAll().where('email', '=', email)
80
+ .selectAll()
81
+ .where('email', '=', email)
77
82
  .executeTakeFirst()
78
83
  .then(sanitize);
79
84
  }
@@ -84,21 +89,18 @@ const getByEmail = (driver) => {
84
89
  * @returns {db_col["remove"]}
85
90
  */
86
91
  const remove = (driver) => {
87
- return async (id_or_email) => {
88
- try {
89
- await driver.client.transaction().execute(
90
- async (trx) => {
91
-
92
- // entities
93
- // delete me
94
- await delete_me(trx, table_name, id_or_email);
95
- }
96
- );
97
- } catch(e) {
98
- console.log(e);
99
- return false;
100
- }
101
- return true;
92
+ return async (handle_or_id) => {
93
+ // delete related auth user
94
+ // customers and auth_users have the same object-id and handle
95
+ let customer_handle_or_id = handle_or_id;
96
+ if(handle_or_id.startsWith('au_')) {
97
+ // found an id
98
+ const object_id = handle_or_id.split('_').at(-1);
99
+ customer_handle_or_id = 'cus_' + object_id;
100
+ }
101
+ return remove_customer_and_auth_user(driver)(
102
+ customer_handle_or_id
103
+ );
102
104
  }
103
105
  }
104
106
  /**
@@ -147,8 +149,8 @@ const list = (driver) => {
147
149
 
148
150
  /**
149
151
  * @param {SQL} driver
150
- * @return {db_col}}
151
- * */
152
+ * @return {db_col}
153
+ */
152
154
  export const impl = (driver) => {
153
155
 
154
156
  return {
@@ -3,18 +3,18 @@
3
3
  */
4
4
  import { SQL } from '../index.js'
5
5
  import { report_document_media } from './con.images.js'
6
- import { delete_entity_values_by_value_or_reporter, delete_me,
6
+ import {
7
+ delete_entity_values_by_value_or_reporter_and_context, delete_me,
7
8
  delete_media_of, delete_search_of, delete_tags_of,
8
9
  insert_media_of, insert_search_of, insert_tags_of,
9
- select_entity_ids_by_value_or_reporter,
10
10
  regular_upsert_me, where_id_or_handle_table,
11
11
  with_media, with_tags,
12
12
  count_regular,
13
- with_search} from './con.shared.js'
13
+ with_search
14
+ } from './con.shared.js'
14
15
  import { sanitize, sanitize_array } from './utils.funcs.js'
15
16
  import { query_to_eb, query_to_sort } from './utils.query.js'
16
17
 
17
-
18
18
  export const table_name = 'collections'
19
19
 
20
20
  /**
@@ -88,16 +88,16 @@ const remove = (driver) => {
88
88
  async (trx) => {
89
89
 
90
90
  // entities
91
- await delete_tags_of(trx, id_or_handle);
92
- await delete_search_of(trx, id_or_handle);
93
- await delete_media_of(trx, id_or_handle);
91
+ await delete_tags_of(trx, id_or_handle, id_or_handle, table_name);
92
+ await delete_search_of(trx, id_or_handle, id_or_handle, table_name);
93
+ await delete_media_of(trx, id_or_handle, id_or_handle, table_name);
94
94
  // PRODUCTS -> COLLECTIONS
95
- await delete_entity_values_by_value_or_reporter('products_to_collections')(
95
+ await delete_entity_values_by_value_or_reporter_and_context('products_to_collections')(
96
96
  trx, id_or_handle, id_or_handle
97
97
  );
98
98
  // STOREFRONT => COLLECTIONS
99
- await delete_entity_values_by_value_or_reporter('storefronts_to_other')(
100
- trx, id_or_handle, id_or_handle
99
+ await delete_entity_values_by_value_or_reporter_and_context('storefronts_to_other')(
100
+ trx, id_or_handle, id_or_handle, table_name
101
101
  );
102
102
 
103
103
  // delete me
@@ -120,7 +120,8 @@ const remove = (driver) => {
120
120
  const list = (driver) => {
121
121
  return async (query) => {
122
122
 
123
- const items = await driver.client.selectFrom(table_name)
123
+ const items = await driver.client
124
+ .selectFrom(table_name)
124
125
  .selectAll()
125
126
  .select(eb => [
126
127
  with_tags(eb, eb.ref('collections.id'), driver.dialectType),
@@ -151,19 +152,27 @@ const list_collection_products = (driver) => {
151
152
 
152
153
  const items = await driver.client
153
154
  .selectFrom('products')
154
- .selectAll()
155
- .select(eb => [
156
- with_media(eb, eb.ref('products.id'), driver.dialectType),
157
- with_tags(eb, eb.ref('products.id'), driver.dialectType),
158
- ])
155
+ .innerJoin(
156
+ 'products_to_collections',
157
+ 'products_to_collections.entity_id',
158
+ 'products.id'
159
+ )
160
+ .selectAll('products')
161
+ .select(
162
+ eb => [
163
+ with_media(eb, eb.ref('products.id'), driver.dialectType),
164
+ with_tags(eb, eb.ref('products.id'), driver.dialectType),
165
+ ]
166
+ )
159
167
  .where(
160
168
  (eb) => eb.and(
161
169
  [
162
170
  query_to_eb(eb, query, 'products'),
163
- eb('products.id', 'in',
164
- eb => select_entity_ids_by_value_or_reporter( // select all the product ids by collection id
165
- eb, 'products_to_collections', handle_or_id
166
- )
171
+ eb.or(
172
+ [
173
+ eb('products_to_collections.reporter', '=', handle_or_id),
174
+ eb('products_to_collections.value', '=', handle_or_id)
175
+ ]
167
176
  )
168
177
  ].filter(Boolean)
169
178
  )
@@ -172,12 +181,104 @@ const list_collection_products = (driver) => {
172
181
  .limit(query.limitToLast ?? query.limit ?? 10)
173
182
  .execute();
174
183
 
184
+ // .compile();
185
+ // console.log(items[0])
186
+
175
187
  if(query.limitToLast) items.reverse();
176
188
 
177
189
  return sanitize_array(items);
178
190
  }
179
191
  }
180
192
 
193
+ /**
194
+ * @param {SQL} driver
195
+ * @returns {db_col["count_collection_products"]}
196
+ */
197
+ const count_collection_products = (driver) => {
198
+ return async (handle_or_id, query={}) => {
199
+
200
+ const result = await driver.client
201
+ .selectFrom('products')
202
+ // .select('products.id')
203
+ .select(
204
+ (eb) => eb.fn.countAll().as('count')
205
+ )
206
+ .innerJoin(
207
+ 'products_to_collections',
208
+ 'products_to_collections.entity_id',
209
+ 'products.id'
210
+ )
211
+ .where(
212
+ (eb) => eb.and(
213
+ [
214
+ query_to_eb(eb, query, 'products'),
215
+ eb.or(
216
+ [
217
+ eb('products_to_collections.reporter', '=', handle_or_id),
218
+ eb('products_to_collections.value', '=', handle_or_id)
219
+ ]
220
+ )
221
+ ].filter(Boolean)
222
+ )
223
+ )
224
+ .executeTakeFirst();
225
+
226
+ // console.log({result})
227
+
228
+ return Number(result.count);
229
+ }
230
+ }
231
+
232
+
233
+ // SELECT
234
+ // entity_to_tags_projections.value as tag
235
+ // FROM products
236
+ // INNER JOIN
237
+ // products_to_collections ON products.id = products_to_collections.entity_id
238
+ // INNER JOIN
239
+ // entity_to_tags_projections ON products.id = entity_to_tags_projections.entity_id
240
+ // WHERE products_to_collections.reporter = 'playstation-4-games'
241
+ // GROUP BY tag
242
+
243
+ /**
244
+ * @param {SQL} driver
245
+ * @returns {db_col["list_used_products_tags"]}
246
+ */
247
+ const list_used_products_tags = (driver) => {
248
+ return async (handle_or_id) => {
249
+
250
+ const items = await driver.client
251
+ .selectFrom('products')
252
+ .innerJoin(
253
+ 'products_to_collections',
254
+ 'products_to_collections.entity_id',
255
+ 'products.id'
256
+ )
257
+ .innerJoin(
258
+ 'entity_to_tags_projections',
259
+ 'entity_to_tags_projections.entity_id',
260
+ 'products.id'
261
+ )
262
+ .select('entity_to_tags_projections.value as tag')
263
+ .where(
264
+ (eb) => eb.or(
265
+ [
266
+ eb('products_to_collections.reporter', '=', handle_or_id),
267
+ eb('products_to_collections.value', '=', handle_or_id)
268
+ ]
269
+ )
270
+ )
271
+ .groupBy('tag')
272
+ .execute();
273
+
274
+ // .compile();
275
+ // console.log(items[0])
276
+
277
+ return items.map(e => e.tag);
278
+ }
279
+ }
280
+
281
+
181
282
  /**
182
283
  * @param {SQL} driver
183
284
  *
@@ -187,13 +288,13 @@ const list_collection_products = (driver) => {
187
288
  export const impl = (driver) => {
188
289
 
189
290
  return {
190
-
191
291
  get: get(driver),
192
292
  upsert: upsert(driver),
193
293
  remove: remove(driver),
194
294
  list: list(driver),
195
295
  list_collection_products: list_collection_products(driver),
296
+ count_collection_products: count_collection_products(driver),
297
+ list_used_products_tags: list_used_products_tags(driver),
196
298
  count: count_regular(driver, table_name),
197
-
198
299
  }
199
300
  }
@@ -87,26 +87,31 @@ const getByEmail = (driver) => {
87
87
  * @param {SQL} driver
88
88
  * @returns {db_col["remove"]}
89
89
  */
90
- const remove = (driver) => {
91
- return async (id) => {
90
+ export const remove = (driver) => {
91
+ return async (handle_or_id) => {
92
92
  try {
93
93
  await driver.client.transaction().execute(
94
94
  async (trx) => {
95
95
 
96
- const valid_auth_id = `au_${id.split('_').at(-1)}`
97
96
  // entities
98
- await delete_search_of(trx, id);
99
- await delete_media_of(trx, id);
100
- await delete_tags_of(trx, id);
101
-
102
- // delete related auth user
103
- await trx
104
- .deleteFrom('auth_users')
105
- .where('auth_users.id', '=', valid_auth_id)
106
- .executeTakeFirst();
97
+ await delete_tags_of(trx, handle_or_id, handle_or_id, table_name);
98
+ await delete_search_of(trx, handle_or_id, handle_or_id, table_name);
99
+ await delete_media_of(trx, handle_or_id, handle_or_id, table_name);
100
+
101
+ { // delete related auth user
102
+ // customers and auth_users have the same object-id and handle
103
+ let auth_user_handle_or_id = handle_or_id;
104
+ if(handle_or_id.startsWith('cus_')) {
105
+ // found an id
106
+ const object_id = handle_or_id.split('_').at(-1);
107
+ auth_user_handle_or_id = 'au_' + object_id;
108
+ }
109
+
110
+ await delete_me(trx, 'auth_users', auth_user_handle_or_id);
111
+ }
107
112
 
108
- // delete me
109
- await delete_me(trx, table_name, id);
113
+ // delete customer
114
+ await delete_me(trx, table_name, handle_or_id);
110
115
  }
111
116
  );
112
117
  } catch(e) {
@@ -150,8 +155,6 @@ const list = (driver) => {
150
155
 
151
156
  /**
152
157
  * @param {SQL} driver
153
- *
154
- *
155
158
  * @returns {db_col["list_customer_orders"]}
156
159
  */
157
160
  const list_customer_orders = (driver) => {
@@ -187,6 +190,37 @@ const list_customer_orders = (driver) => {
187
190
  }
188
191
  }
189
192
 
193
+ /**
194
+ * @param {SQL} driver
195
+ * @returns {db_col["count_customer_orders"]}
196
+ */
197
+ const count_customer_orders = (driver) => {
198
+ return async (id, query) => {
199
+
200
+ const result = await driver.client
201
+ .selectFrom('orders')
202
+ .select(
203
+ (eb) => eb.fn.countAll().as('count')
204
+ )
205
+ .where(
206
+ (eb) => eb.and(
207
+ [
208
+ query_to_eb(eb, query, table_name),
209
+ eb.or(
210
+ [
211
+ eb('_customer_id', '=', id),
212
+ eb('_customer_email', '=', id),
213
+ ]
214
+ )
215
+ ].filter(Boolean)
216
+ )
217
+ )
218
+ .executeTakeFirst();
219
+
220
+ return Number(result.count);
221
+ }
222
+ }
223
+
190
224
  /**
191
225
  * @param {SQL} driver
192
226
  * @return {db_col}}
@@ -200,6 +234,7 @@ export const impl = (driver) => {
200
234
  remove: remove(driver),
201
235
  list: list(driver),
202
236
  list_customer_orders: list_customer_orders(driver),
237
+ count_customer_orders: count_customer_orders(driver),
203
238
  count: count_regular(driver, table_name),
204
239
  }
205
240
  }