@gnar-engine/cli 1.0.8 → 1.0.10

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.
@@ -7,8 +7,9 @@ export const config = {
7
7
  serviceName: 'controlService',
8
8
 
9
9
  // environment
10
- environment: process.env.CONTROL_NODE_ENV || 'dev',
10
+ environment: process.env.CONTROL_NODE_ENV || 'development',
11
11
  runTests: process.env.CONTROL_RUN_TESTS || false,
12
+ resetDatabase: process.env.CONTROL_RESET_DATABASE || false,
12
13
 
13
14
  // microservice | modular-monolith
14
15
  architecture: process.env.GLOBAL_ARCHITECTURE || 'microservice',
@@ -5,6 +5,11 @@ export const config = {
5
5
  // service name
6
6
  serviceName: 'pageService',
7
7
 
8
+ // environment
9
+ environment: process.env.PAGE_NODE_ENV || 'developnent',
10
+ runTests: process.env.PAGE_RUN_TESTS || false,
11
+ resetDatabase: process.env.PAGE_RESET_DATABASE || false,
12
+
8
13
  // microservice | modular-monolith
9
14
  architecture: process.env.GLOBAL_ARCHITECTURE || 'microservice',
10
15
 
@@ -9,6 +9,7 @@ export const config = {
9
9
  // environment
10
10
  environment: process.env.USER_NODE_ENV || 'dev',
11
11
  runTests: process.env.USER_RUN_TESTS || false,
12
+ resetDatabase: process.env.USER_RESET_DATABASE || false,
12
13
 
13
14
  // microservice | modular-monolith
14
15
  architecture: process.env.GLOBAL_ARCHITECTURE || 'microservice',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gnar-engine/cli",
3
- "version": "1.0.8",
3
+ "version": "1.0.10",
4
4
  "description": "Gnar Engine Development Framework CLI: Project bootstrap, scaffolder & control plane.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -15,7 +15,7 @@ export function registerDevCommands(program) {
15
15
  .option('-a, --attach-all', 'Attach all services including database and message queues for debugging')
16
16
  .option('-t --test', 'Run all tests with ephemeral databases *NOT IMPLEMENTED')
17
17
  .option('--test-service <service>', 'Run the tests for the specified service with ephemeral databases (e.g. --test-service user)')
18
- .option('-reset-databases, --reset-databases', 'Drop all service databases, re-running all migrations and seeders *NOT IMPLEMENTED')
18
+ .option('--reset-databases, --reset-databases', 'Drop all service databases, re-running all migrations and seeders *NOT IMPLEMENTED')
19
19
  .option('--reset-database <service>', 'Drop the specified service database, re-running all migrations and seeders (e.g. --reset-database user)')
20
20
  .addOption(new Option('--core-dev').hideHelp())
21
21
  .addOption(new Option('--bootstrap-dev').hideHelp())
@@ -430,6 +430,13 @@ async function buildAndUpContainers({
430
430
  serviceVolumes.push(`${gnarEngineCliConfig.coreDevPath}:${gnarEngineCliConfig.corePath}`);
431
431
  }
432
432
 
433
+ if (svc.extra_binds) {
434
+ svc.extra_binds.forEach((bind, index) => {
435
+ bind = `${gnarHiddenDir}/data/${svc.name}-data/bind-${index}/:${bind}`;
436
+ serviceVolumes.push(bind);
437
+ })
438
+ }
439
+
433
440
  // split from "port:port" to { port: port }
434
441
  const ports = {};
435
442
  for (const portMapping of svc.ports || []) {
@@ -1,23 +1,93 @@
1
- import { db } from '@gnar-engine/core';
1
+ import { db, utils } from '@gnar-engine/core';
2
2
 
3
3
  export const {{entityName}} = {
4
4
  async getById({ id }) {
5
5
  const [result] = await db.query('SELECT * FROM {{lowerCasePlural entityName}} WHERE id = ?', [id]);
6
- return result || null;
6
+
7
+ if (!result) {
8
+ return null;
9
+ }
10
+
11
+ return db.sql.helpers.objectToCamelCase(result);
7
12
  },
8
13
 
9
- async getAll() {
10
- return await db.query('SELECT * FROM {{lowerCasePlural entityName}}');
14
+ async getAll({ pageSize = 100, pageNum = 1 }) {
15
+ pageSize = Number(pageSize);
16
+ pageNum = Number(pageNum);
17
+ const offset = (pageNum - 1) * pageSize;
18
+
19
+ const [rows] = await db.query(
20
+ 'SELECT * FROM {{lowerCasePlural entityName}} LIMIT ? OFFSET ?',
21
+ [pageSize, offset]
22
+ );
23
+
24
+ const [[{ total }]] = await db.query(
25
+ 'SELECT COUNT(*) AS total FROM {{lowerCasePlural entityName}}'
26
+ );
27
+
28
+ return {
29
+ data: rows.map(row => db.sql.helpers.objectToCamelCase(row)),
30
+ pagination: {
31
+ pageSize,
32
+ pageNum,
33
+ total
34
+ }
35
+ }
11
36
  },
12
37
 
13
38
  async create(data) {
14
- const { insertId } = await db.query('INSERT INTO {{lowerCasePlural entityName}} (created_at, updated_at) VALUES (CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)');
15
- return await this.getById({ id: insertId });
39
+ const id = utils.uuid();
40
+
41
+ // map columns and insert
42
+ const columns = ['id', ...Object.keys(data).map(db.sql.helpers.toSnake)];
43
+ const placeholders = columns.map(() => '?');
44
+ const values = [id, ...Object.values(data)];
45
+
46
+ const sql = `
47
+ INSERT INTO {{lowerCasePlural entityName}} (
48
+ ${columns.join(', ')},
49
+ created_at,
50
+ updated_at
51
+ )
52
+ VALUES (
53
+ ${placeholders.join(', ')},
54
+ CURRENT_TIMESTAMP,
55
+ CURRENT_TIMESTAMP
56
+ )
57
+ `;
58
+ await db.query(sql, [...values, id]);
59
+
60
+ // return result
61
+ const newItem = await this.getById({ id });
62
+ return db.sql.helpers.objectToCamelCase(newItem);
16
63
  },
17
64
 
18
- async update({ id, ...data }) {
19
- await db.query('UPDATE {{lowerCasePlural entityName}} SET updated_at = CURRENT_TIMESTAMP WHERE id = ?', [id]);
20
- return await this.getById({ id });
65
+ async update({ id, updatedData }) {
66
+ if (!Object.keys(updatedData).length) {
67
+ return this.getById({ id });
68
+ }
69
+
70
+ // Handle many to many relations
71
+ const newOwnerIds = updatedData.ownerIds ? updatedData.ownerIds : null;
72
+ delete updatedData.ownerIds;
73
+
74
+ // map columns and update
75
+ const columns = Object.keys(updatedData).map((key) => db.sql.helpers.toSnake(key));
76
+ const assignments = columns.map(col => `${col} = ?`);
77
+ const values = Object.values(updatedData);
78
+
79
+ const sql = `
80
+ UPDATE {{lowerCasePlural entityName}}
81
+ SET
82
+ ${assignments.join(', ')},
83
+ updated_at = CURRENT_TIMESTAMP
84
+ WHERE id = ?
85
+ `;
86
+ await db.query(sql, [...values, id]);
87
+
88
+ // return result
89
+ const newItem = await this.getById({ id });
90
+ return db.sql.helpers.objectToCamelCase(newItem);
21
91
  },
22
92
 
23
93
  async delete({ id }) {
@@ -8,13 +8,9 @@ import { httpController as {{lowerCase serviceName}}PlatformHttpController } fro
8
8
  */
9
9
  export const initService = async () => {
10
10
 
11
- // Run migrations
12
- if (config.db.type == 'mysql') {
13
- db.migrations.runMigrations({config});
14
- }
15
-
16
- // Run seeders
17
- db.seeders.runSeeders({config});
11
+ // Run migrations & seeders
12
+ await db.migrations.runMigrations({config});
13
+ await db.seeders.runSeeders({config});
18
14
 
19
15
  // Import command handlers after the command bus is initialised
20
16
  await import('./commands/{{lowerCase serviceName}}.handler.js');
@@ -5,6 +5,11 @@ export const config = {
5
5
  // service name
6
6
  serviceName: '{{serviceName}}Service',
7
7
 
8
+ // environment
9
+ environment: process.env.{{upperCase serviceName}}_NODE_ENV || 'development',
10
+ runTests: process.env.{{upperCase serviceName}}_RUN_TESTS || false,
11
+ resetDatabase: process.env.{{upperCase serviceName}}_RESET_DATABASE || false,
12
+
8
13
  // microservice | modular-monolith
9
14
  architecture: process.env.GLOBAL_ARCHITECTURE || 'microservice',
10
15
 
@@ -6,8 +6,9 @@ export const config = {
6
6
  serviceName: '{{serviceName}}Service',
7
7
 
8
8
  // environment
9
- environment: process.env.{{upperCase serviceName}}_NODE_ENV || 'dev',
9
+ environment: process.env.{{upperCase serviceName}}_NODE_ENV || 'development',
10
10
  runTests: process.env.{{upperCase serviceName}}_RUN_TESTS || false,
11
+ resetDatabase: process.env.{{upperCase serviceName}}_RESET_DATABASE || false,
11
12
 
12
13
  // microservice | modular-monolith
13
14
  architecture: process.env.GLOBAL_ARCHITECTURE || 'microservice',
@@ -1,28 +1,93 @@
1
- import { db } from '@gnar-engine/core';
1
+ import { db, utils } from '@gnar-engine/core';
2
2
 
3
3
  export const {{serviceName}} = {
4
4
  async getById({ id }) {
5
5
  const [result] = await db.query('SELECT * FROM {{lowerCasePlural serviceName}} WHERE id = ?', [id]);
6
- return result || null;
7
- },
8
6
 
9
- async getByEmail({ email }) {
10
- // Placeholder: implement if your service uses email
11
- return null;
7
+ if (!result) {
8
+ return null;
9
+ }
10
+
11
+ return db.sql.helpers.objectToCamelCase(result);
12
12
  },
13
13
 
14
- async getAll() {
15
- return await db.query('SELECT * FROM {{lowerCasePlural serviceName}}');
14
+ async getAll({ pageSize = 100, pageNum = 1 }) {
15
+ pageSize = Number(pageSize);
16
+ pageNum = Number(pageNum);
17
+ const offset = (pageNum - 1) * pageSize;
18
+
19
+ const [rows] = await db.query(
20
+ 'SELECT * FROM {{lowerCasePlural serviceName}} LIMIT ? OFFSET ?',
21
+ [pageSize, offset]
22
+ );
23
+
24
+ const [[{ total }]] = await db.query(
25
+ 'SELECT COUNT(*) AS total FROM {{lowerCasePlural serviceName}}'
26
+ );
27
+
28
+ return {
29
+ data: rows.map(row => db.sql.helpers.objectToCamelCase(row)),
30
+ pagination: {
31
+ pageSize,
32
+ pageNum,
33
+ total
34
+ }
35
+ }
16
36
  },
17
37
 
18
38
  async create(data) {
19
- const { insertId } = await db.query('INSERT INTO {{lowerCasePlural serviceName}} (created_at, updated_at) VALUES (CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)');
20
- return await this.getById({ id: insertId });
39
+ const id = utils.uuid();
40
+
41
+ // map columns and insert
42
+ const columns = ['id', ...Object.keys(data).map(db.sql.helpers.toSnake)];
43
+ const placeholders = columns.map(() => '?');
44
+ const values = [id, ...Object.values(data)];
45
+
46
+ const sql = `
47
+ INSERT INTO {{lowerCasePlural serviceName}} (
48
+ ${columns.join(', ')},
49
+ created_at,
50
+ updated_at
51
+ )
52
+ VALUES (
53
+ ${placeholders.join(', ')},
54
+ CURRENT_TIMESTAMP,
55
+ CURRENT_TIMESTAMP
56
+ )
57
+ `;
58
+ await db.query(sql, [...values, id]);
59
+
60
+ // return result
61
+ const newItem = await this.getById({ id });
62
+ return db.sql.helpers.objectToCamelCase(newItem);
21
63
  },
22
64
 
23
- async update({ id, ...data }) {
24
- await db.query('UPDATE {{lowerCasePlural serviceName}} SET updated_at = CURRENT_TIMESTAMP WHERE id = ?', [id]);
25
- return await this.getById({ id });
65
+ async update({ id, updatedData }) {
66
+ if (!Object.keys(updatedData).length) {
67
+ return this.getById({ id });
68
+ }
69
+
70
+ // Handle many to many relations
71
+ const newOwnerIds = updatedData.ownerIds ? updatedData.ownerIds : null;
72
+ delete updatedData.ownerIds;
73
+
74
+ // map columns and update
75
+ const columns = Object.keys(updatedData).map((key) => db.sql.helpers.toSnake(key));
76
+ const assignments = columns.map(col => `${col} = ?`);
77
+ const values = Object.values(updatedData);
78
+
79
+ const sql = `
80
+ UPDATE {{lowerCasePlural serviceName}}
81
+ SET
82
+ ${assignments.join(', ')},
83
+ updated_at = CURRENT_TIMESTAMP
84
+ WHERE id = ?
85
+ `;
86
+ await db.query(sql, [...values, id]);
87
+
88
+ // return result
89
+ const newItem = await this.getById({ id });
90
+ return db.sql.helpers.objectToCamelCase(newItem);
26
91
  },
27
92
 
28
93
  async delete({ id }) {