@gnar-engine/cli 1.0.5 → 1.0.7

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.
Files changed (48) hide show
  1. package/bootstrap/deploy.localdev.yml +14 -0
  2. package/bootstrap/secrets.localdev.yml +7 -3
  3. package/bootstrap/services/notification/Dockerfile +2 -2
  4. package/bootstrap/services/notification/package.json +14 -32
  5. package/bootstrap/services/notification/src/app.js +50 -48
  6. package/bootstrap/services/notification/src/commands/notification.handler.js +96 -0
  7. package/bootstrap/services/notification/src/config.js +55 -12
  8. package/bootstrap/services/notification/src/controllers/http.controller.js +87 -0
  9. package/bootstrap/services/notification/src/controllers/message.controller.js +39 -70
  10. package/bootstrap/services/notification/src/db/migrations/01-init.js +50 -0
  11. package/bootstrap/services/notification/src/db/migrations/02-notification-service-init.js +23 -0
  12. package/bootstrap/services/notification/src/policies/notification.policy.js +49 -0
  13. package/bootstrap/services/notification/src/schema/notification.schema.js +17 -0
  14. package/bootstrap/services/notification/src/services/notification.service.js +32 -0
  15. package/bootstrap/services/portal/src/services/client.js +3 -0
  16. package/bootstrap/services/user/src/commands/user.handler.js +35 -18
  17. package/bootstrap/services/user/src/tests/commands/user.test.js +15 -6
  18. package/install-from-clone.sh +1 -1
  19. package/package.json +1 -1
  20. package/src/cli.js +0 -6
  21. package/src/config.js +13 -1
  22. package/src/dev/commands.js +7 -3
  23. package/src/dev/dev.service.js +192 -128
  24. package/src/helpers/helpers.js +24 -0
  25. package/src/profiles/command.js +41 -0
  26. package/src/profiles/profiles.client.js +23 -0
  27. package/src/scaffolder/commands.js +57 -1
  28. package/src/scaffolder/scaffolder.handler.js +127 -60
  29. package/src/services/docker.js +173 -0
  30. package/templates/entity/src/commands/{{entityName}}.handler.js.hbs +94 -0
  31. package/templates/entity/src/controllers/{{entityName}}.http.controller.js.hbs +87 -0
  32. package/templates/entity/src/mysql.db/migrations/03-{{entityName}}-entity-init.js.hbs +23 -0
  33. package/templates/entity/src/policies/{{entityName}}.policy.js.hbs +49 -0
  34. package/templates/entity/src/schema/{{entityName}}.schema.js.hbs +17 -0
  35. package/templates/entity/src/services/mongodb.{{entityName}}.service.js.hbs +70 -0
  36. package/templates/entity/src/services/mysql.{{entityName}}.service.js.hbs +27 -0
  37. package/bootstrap/services/notification/Dockerfile.prod +0 -37
  38. package/bootstrap/services/notification/README.md +0 -3
  39. package/bootstrap/services/notification/src/commands/command-bus.js +0 -20
  40. package/bootstrap/services/notification/src/commands/handlers/control.handler.js +0 -18
  41. package/bootstrap/services/notification/src/commands/handlers/notification.handler.js +0 -157
  42. package/bootstrap/services/notification/src/services/logger.service.js +0 -16
  43. package/bootstrap/services/notification/src/services/ses.service.js +0 -23
  44. package/bootstrap/services/notification/src/templates/admin-order-recieved.hbs +0 -136
  45. package/bootstrap/services/notification/src/templates/admin-subscription-failed.hbs +0 -87
  46. package/bootstrap/services/notification/src/templates/customer-order-recieved.hbs +0 -132
  47. package/bootstrap/services/notification/src/templates/customer-subscription-failed.hbs +0 -77
  48. package/bootstrap/services/notification/src/tests/notification.test.js +0 -0
@@ -0,0 +1,49 @@
1
+ import { config } from '../config.js';
2
+
3
+ export const authorise = {
4
+
5
+ /**
6
+ * Authorise get single notification
7
+ */
8
+ getSingle: async (request, reply) => {
9
+ if (!request.user || request.user.role !== 'service_admin') {
10
+ reply.code(403).send({error: 'not authorised'});
11
+ }
12
+ },
13
+
14
+ /**
15
+ * Authorise get many notifications
16
+ */
17
+ getMany: async (request, reply) => {
18
+ if (!request.user || request.user.role !== 'service_admin') {
19
+ reply.code(403).send({error: 'not authorised'});
20
+ }
21
+ },
22
+
23
+ /**
24
+ * Authorise create notifications
25
+ */
26
+ create: async (request, reply) => {
27
+ if (!request.user || request.user.role !== 'service_admin') {
28
+ reply.code(403).send({error: 'not authorised'});
29
+ }
30
+ },
31
+
32
+ /**
33
+ * Authorise update notification
34
+ */
35
+ update: async (request, reply) => {
36
+ if (!request.user || request.user.role !== 'service_admin') {
37
+ reply.code(403).send({error: 'not authorised'});
38
+ }
39
+ },
40
+
41
+ /**
42
+ * Authorise delete notification
43
+ */
44
+ delete: async (request, reply) => {
45
+ if (!request.user || request.user.role !== 'service_admin') {
46
+ reply.code(403).send({error: 'not authorised'});
47
+ }
48
+ }
49
+ }
@@ -0,0 +1,17 @@
1
+ import { schema } from '@gnar-engine/core';
2
+ import { config } from '../config.js';
3
+
4
+ export const notificationSchema = {
5
+ schemaName: 'notificationService.notificationSchema',
6
+ schema: {
7
+ type: 'object',
8
+ properties: {
9
+ // Add your properties here
10
+
11
+ },
12
+ required: [],
13
+ additionalProperties: false
14
+ }
15
+ };
16
+
17
+ export const validateNotification = schema.compile(notificationSchema);
@@ -0,0 +1,32 @@
1
+ import { db } from '@gnar-engine/core';
2
+
3
+ export const notification = {
4
+ async getById({ id }) {
5
+ const [result] = await db.query('SELECT * FROM notifications WHERE id = ?', [id]);
6
+ return result || null;
7
+ },
8
+
9
+ async getByEmail({ email }) {
10
+ // Placeholder: implement if your service uses email
11
+ return null;
12
+ },
13
+
14
+ async getAll() {
15
+ return await db.query('SELECT * FROM notifications');
16
+ },
17
+
18
+ async create(data) {
19
+ const { insertId } = await db.query('INSERT INTO notifications (created_at, updated_at) VALUES (CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)');
20
+ return await this.getById({ id: insertId });
21
+ },
22
+
23
+ async update({ id, ...data }) {
24
+ await db.query('UPDATE notifications SET updated_at = CURRENT_TIMESTAMP WHERE id = ?', [id]);
25
+ return await this.getById({ id });
26
+ },
27
+
28
+ async delete({ id }) {
29
+ await db.query('DELETE FROM notifications WHERE id = ?', [id]);
30
+ return true;
31
+ },
32
+ };
@@ -20,6 +20,9 @@ client.interceptors.request.use(
20
20
  config.headers['Authorization'] = `Bearer ${authToken}`;
21
21
  }
22
22
 
23
+ // add content-type application/json to config.headers
24
+ config.headers['Content-Type'] = 'application/json';
25
+
23
26
  return config;
24
27
  },
25
28
  (error) => {
@@ -121,31 +121,48 @@ commands.register('userService.getManyUsers', async ({}) => {
121
121
  * Creat users with random password
122
122
  *
123
123
  * @param {Object} params
124
- * @param {Object} params.user - New user data
124
+ * @param {Array} params.users - New user data
125
125
  */
126
- commands.register('userService.createUserWithRandomPassword', async ({user}) => {
126
+ commands.register('userService.createUserWithRandomPassword', async ({users}) => {
127
127
 
128
- // create random password
129
- const password = Math.random().toString(36);
130
- const userData = {
131
- ...user,
132
- password: password
133
- };
128
+ const validationErrors = [];
129
+ let createdNewUsers = [];
134
130
 
135
- logger.info('creating new user : ' + JSON.stringify(userData));
131
+ // validate user data
132
+ for (const newUserData of users) {
136
133
 
137
- // create user
138
- try {
139
- const newUsers = await createUsers({users: [userData]})
134
+ // create random password
135
+ const password = Math.random().toString(36);
136
+ newUserData.password = password;
140
137
 
141
- if (!newUsers || newUsers.length === 0) {
142
- throw new error.badRequest('User creation failed');
138
+ const { errors } = validateUser(newUserData);
139
+
140
+ if (errors?.length) {
141
+ validationErrors.push(errors);
142
+ continue;
143
+ }
144
+
145
+ if (!newUserData.role || newUserData.role !== 'service_admin') {
146
+ // ensure emails are unique
147
+ const existingUser = await user.getByEmail({email: newUserData.email});
148
+
149
+ if (existingUser) {
150
+ validationErrors.push(`User with email ${newUserData.email} already exists`);
151
+ }
143
152
  }
144
-
145
- return newUsers[0];
146
- } catch (error) {
147
- throw new error.badRequest('User creation failed: ' + error);
148
153
  }
154
+
155
+ if (validationErrors.length) {
156
+ throw new error.badRequest(`Invalid user data: ${validationErrors}`);
157
+ }
158
+
159
+ // add users
160
+ for (const newUserData of users) {
161
+ const newUser = await user.create(newUserData);
162
+ createdNewUsers.push(newUser);
163
+ }
164
+
165
+ return createdNewUsers;
149
166
  });
150
167
 
151
168
  /**
@@ -8,15 +8,24 @@ test.prep(async () => {
8
8
  })
9
9
 
10
10
  // Test create user command
11
- test.run('Create User Command', async () => {
12
- const users = await commands.execute('createUsers', [
11
+ test.run('Create user command', async () => {
12
+ const users = await commands.execute('createUsers', { users: [
13
13
  {
14
- email: 'test@gnar.co.uk'
14
+ email: 'test@gnar.co.uk',
15
+ password: 'p4ssw0rd987'
15
16
  }
16
- ]);
17
-
17
+ ]});
18
18
  test.assert(users.length === 1, 'User was not created successfully');
19
19
  test.assert(users[0].email === 'test@gnar.co.uk', 'User email does not match');
20
20
  });
21
21
 
22
-
22
+ // Test create user with random password command
23
+ test.run('Create user with random password command', async () => {
24
+ const users = await commands.execute('createUserWithRandomPassword', { users: [
25
+ {
26
+ email: 'test2@gnar.co.uk'
27
+ }
28
+ ]});
29
+ test.assert(users.length === 1, 'User was not created successfully');
30
+ test.assert(users[0].email === 'test3@gnar.co.uk', 'User email does not match');
31
+ });
@@ -13,7 +13,7 @@ cd "$CLI_DIR"
13
13
  npm install
14
14
 
15
15
  # Link CLI to custom global folder
16
- npm link --prefix "$TARGET_DIR"
16
+ npm link
17
17
 
18
18
  # Bin path for npm link
19
19
  BIN_PATH="$TARGET_DIR/bin"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gnar-engine/cli",
3
- "version": "1.0.5",
3
+ "version": "1.0.7",
4
4
  "description": "Gnar Engine Development Framework CLI: Project bootstrap, scaffolder & control plane.",
5
5
  "type": "module",
6
6
  "bin": {
package/src/cli.js CHANGED
@@ -30,9 +30,3 @@ G n a r E n g i n e - A powerful, AI ready microservice framework for modern a
30
30
  // Parse CLI input
31
31
  program.parse(process.argv);
32
32
 
33
- // Consts
34
- export const directories = {
35
- scaffolderTemplates: path.join(import.meta.dirname, '../templates/service'),
36
- bootstrap: path.join(import.meta.dirname, '../bootstrap'),
37
- provisioner: path.join(import.meta.dirname, './provisioner')
38
- }
package/src/config.js CHANGED
@@ -1,9 +1,21 @@
1
+ import path from 'path';
2
+ import { fileURLToPath } from 'url';
1
3
 
4
+ const __filename = fileURLToPath(import.meta.url);
5
+ const __dirname = path.dirname(__filename);
2
6
  export const gnarEngineCliConfig = {
3
7
 
4
8
  /**
5
9
  * The path the Gnar Engine service core should be found in the service containers
6
10
  */
7
- corePath: '/usr/gnar_engine/app/node_modules/@gnar-engine/core'
11
+ corePath: '/usr/gnar_engine/app/node_modules/@gnar-engine/core',
12
+ coreDevPath: path.join(__dirname, '../../core'),
8
13
 
9
14
  }
15
+
16
+ export const directories = {
17
+ scaffolderServiceTemplates: path.join(import.meta.dirname, '../templates/service'),
18
+ scaffolderEntityTemplates: path.join(import.meta.dirname, '../templates/entity'),
19
+ bootstrap: path.join(import.meta.dirname, '../bootstrap'),
20
+ provisioner: path.join(import.meta.dirname, './provisioner')
21
+ };
@@ -10,11 +10,13 @@ export function registerDevCommands(program) {
10
10
  devCmd
11
11
  .command('up')
12
12
  .description('🛠️ Up Development Containers')
13
- .option('-b, --build', 'Ruild without cache')
13
+ .option('-b, --build', 'Build without cache')
14
14
  .option('-d, --detach', 'Run containers in background')
15
+ .option('-a, --attach-all', 'Attach all services including database and message queues for debugging')
15
16
  .option('-t --test', 'Run the tests with ephemeral databases')
16
- .option('--test-service <service>', 'Run the tests for the specified service with ephemeral databases')
17
+ .option('--test-service <service>', 'Run the tests for the specified service with ephemeral databases (e.g. --test-service user)')
17
18
  .addOption(new Option('--core-dev').hideHelp())
19
+ .addOption(new Option('--bootstrap-dev').hideHelp())
18
20
  .action(async (options) => {
19
21
  let response = {};
20
22
 
@@ -39,8 +41,10 @@ export function registerDevCommands(program) {
39
41
  build: options.build || false,
40
42
  detach: options.detach || false,
41
43
  coreDev: options.coreDev || false,
44
+ bootstrapDev: options.bootstrapDev || false,
42
45
  test: options.test || false,
43
- testService: options.testService || ''
46
+ testService: options.testService || '',
47
+ attachAll: options.attachAll || false
44
48
  });
45
49
  } catch (err) {
46
50
  console.error("❌ Error running containers:", err.message);