@gokiteam/goki-dev 0.2.0

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 (205) hide show
  1. package/README.md +478 -0
  2. package/bin/goki-dev.js +452 -0
  3. package/bin/mcp-server.js +16 -0
  4. package/bin/secrets-cli.js +302 -0
  5. package/cli/ComposeOverrideGenerator.js +226 -0
  6. package/cli/ComposeParser.js +73 -0
  7. package/cli/ConfigGenerator.js +304 -0
  8. package/cli/ConfigManager.js +46 -0
  9. package/cli/DatabaseManager.js +94 -0
  10. package/cli/DevToolsChecker.js +21 -0
  11. package/cli/DevToolsDir.js +66 -0
  12. package/cli/DevToolsManager.js +451 -0
  13. package/cli/DockerManager.js +138 -0
  14. package/cli/FunctionManager.js +95 -0
  15. package/cli/HttpProxyRewriter.js +91 -0
  16. package/cli/Logger.js +10 -0
  17. package/cli/McpConfigManager.js +123 -0
  18. package/cli/NgrokManager.js +431 -0
  19. package/cli/ProjectCLI.js +2322 -0
  20. package/cli/PubSubManager.js +129 -0
  21. package/cli/SnapshotManager.js +88 -0
  22. package/cli/UiFormatter.js +292 -0
  23. package/cli/WebhookUrlRewriter.js +32 -0
  24. package/cli/secrets/BiometricAuth.js +125 -0
  25. package/cli/secrets/SecretInjector.js +47 -0
  26. package/cli/secrets/SecretsConfig.js +141 -0
  27. package/cli/secrets/SecretsDoctor.js +384 -0
  28. package/cli/secrets/SecretsManager.js +255 -0
  29. package/client/dist/client.d.ts +332 -0
  30. package/client/dist/client.js +507 -0
  31. package/client/dist/helpers.d.ts +62 -0
  32. package/client/dist/helpers.js +122 -0
  33. package/client/dist/index.d.ts +59 -0
  34. package/client/dist/index.js +78 -0
  35. package/client/dist/package.json +1 -0
  36. package/client/dist/types.d.ts +280 -0
  37. package/client/dist/types.js +7 -0
  38. package/config.development +46 -0
  39. package/config.test +18 -0
  40. package/guidelines/CodingStyleGuideline.md +148 -0
  41. package/guidelines/CommentingGuideline.md +10 -0
  42. package/guidelines/HttpApiImplementationGuideline.md +137 -0
  43. package/guidelines/NamingGuideline.md +182 -0
  44. package/package.json +138 -0
  45. package/patterns/api/[collectionName]/Controllers.md +62 -0
  46. package/patterns/api/[collectionName]/Logic.md +154 -0
  47. package/patterns/api/[collectionName]/Permissions.md +81 -0
  48. package/patterns/api/[collectionName]/Router.md +83 -0
  49. package/patterns/api/[collectionName]/Schemas.md +197 -0
  50. package/patterns/configs/Patterns.md +7 -0
  51. package/patterns/enums/Patterns.md +24 -0
  52. package/patterns/errorHandling/Patterns.md +185 -0
  53. package/patterns/testing/Patterns.md +232 -0
  54. package/src/Server.js +238 -0
  55. package/src/api/dashboard/Controllers.js +9 -0
  56. package/src/api/dashboard/Logic.js +76 -0
  57. package/src/api/dashboard/Router.js +11 -0
  58. package/src/api/dashboard/Schemas.js +47 -0
  59. package/src/api/data/Controllers.js +26 -0
  60. package/src/api/data/Logic.js +188 -0
  61. package/src/api/data/Router.js +16 -0
  62. package/src/api/docker/Controllers.js +33 -0
  63. package/src/api/docker/Logic.js +268 -0
  64. package/src/api/docker/Router.js +15 -0
  65. package/src/api/docker/Schemas.js +80 -0
  66. package/src/api/docs/Controllers.js +15 -0
  67. package/src/api/docs/Logic.js +85 -0
  68. package/src/api/docs/Router.js +12 -0
  69. package/src/api/export/Controllers.js +30 -0
  70. package/src/api/export/Logic.js +143 -0
  71. package/src/api/export/Router.js +18 -0
  72. package/src/api/export/Schemas.js +104 -0
  73. package/src/api/firestore/Controllers.js +152 -0
  74. package/src/api/firestore/Logic.js +474 -0
  75. package/src/api/firestore/Router.js +23 -0
  76. package/src/api/functions/Controllers.js +261 -0
  77. package/src/api/functions/Logic.js +710 -0
  78. package/src/api/functions/Router.js +50 -0
  79. package/src/api/functions/Schemas.js +193 -0
  80. package/src/api/gateway/Controllers.js +72 -0
  81. package/src/api/gateway/Logic.js +74 -0
  82. package/src/api/gateway/Router.js +10 -0
  83. package/src/api/gateway/Schemas.js +19 -0
  84. package/src/api/health/Controllers.js +14 -0
  85. package/src/api/health/Logic.js +24 -0
  86. package/src/api/health/Router.js +12 -0
  87. package/src/api/httpTraffic/Controllers.js +29 -0
  88. package/src/api/httpTraffic/Logic.js +33 -0
  89. package/src/api/httpTraffic/Router.js +9 -0
  90. package/src/api/httpTraffic/Schemas.js +23 -0
  91. package/src/api/logging/Controllers.js +80 -0
  92. package/src/api/logging/Logic.js +461 -0
  93. package/src/api/logging/Router.js +24 -0
  94. package/src/api/logging/Schemas.js +43 -0
  95. package/src/api/mqtt/Controllers.js +17 -0
  96. package/src/api/mqtt/Logic.js +66 -0
  97. package/src/api/mqtt/Router.js +12 -0
  98. package/src/api/postgres/Controllers.js +97 -0
  99. package/src/api/postgres/Logic.js +221 -0
  100. package/src/api/postgres/Router.js +21 -0
  101. package/src/api/pubsub/Controllers.js +236 -0
  102. package/src/api/pubsub/Logic.js +732 -0
  103. package/src/api/pubsub/Router.js +41 -0
  104. package/src/api/pubsub/Schemas.js +355 -0
  105. package/src/api/redis/Controllers.js +63 -0
  106. package/src/api/redis/Logic.js +239 -0
  107. package/src/api/redis/Router.js +21 -0
  108. package/src/api/scheduler/Controllers.js +27 -0
  109. package/src/api/scheduler/Logic.js +49 -0
  110. package/src/api/scheduler/Router.js +16 -0
  111. package/src/api/services/Controllers.js +26 -0
  112. package/src/api/services/Logic.js +205 -0
  113. package/src/api/services/Router.js +14 -0
  114. package/src/api/services/Schemas.js +66 -0
  115. package/src/api/snapshots/Controllers.js +37 -0
  116. package/src/api/snapshots/Logic.js +797 -0
  117. package/src/api/snapshots/Router.js +15 -0
  118. package/src/api/snapshots/Schemas.js +23 -0
  119. package/src/api/webhooks/Controllers.js +49 -0
  120. package/src/api/webhooks/Logic.js +137 -0
  121. package/src/api/webhooks/Router.js +12 -0
  122. package/src/api/webhooks/Schemas.js +31 -0
  123. package/src/configs/Application.js +147 -0
  124. package/src/configs/Default.js +13 -0
  125. package/src/consumers/BlackboxLogsConsumer.js +235 -0
  126. package/src/consumers/DockerLogsConsumer.js +687 -0
  127. package/src/db/Tables.js +66 -0
  128. package/src/db/schemas/firestore.js +18 -0
  129. package/src/db/schemas/functions.js +65 -0
  130. package/src/db/schemas/httpTraffic.js +43 -0
  131. package/src/db/schemas/logging.js +74 -0
  132. package/src/db/schemas/migrations.js +64 -0
  133. package/src/db/schemas/mqtt.js +56 -0
  134. package/src/db/schemas/pubsub.js +90 -0
  135. package/src/db/schemas/pubsubRegistry.js +22 -0
  136. package/src/db/schemas/webhooks.js +28 -0
  137. package/src/emulation/awsiot/Controllers.js +91 -0
  138. package/src/emulation/awsiot/Logic.js +70 -0
  139. package/src/emulation/awsiot/Router.js +19 -0
  140. package/src/emulation/awsiot/Server.js +100 -0
  141. package/src/emulation/firestore/Server.js +136 -0
  142. package/src/emulation/logging/Controllers.js +212 -0
  143. package/src/emulation/logging/Logic.js +416 -0
  144. package/src/emulation/logging/Router.js +36 -0
  145. package/src/emulation/logging/Schemas.js +82 -0
  146. package/src/emulation/logging/Server.js +108 -0
  147. package/src/emulation/pubsub/Controllers.js +279 -0
  148. package/src/emulation/pubsub/DefaultTopics.js +162 -0
  149. package/src/emulation/pubsub/Logic.js +427 -0
  150. package/src/emulation/pubsub/README.md +309 -0
  151. package/src/emulation/pubsub/Router.js +33 -0
  152. package/src/emulation/pubsub/Server.js +104 -0
  153. package/src/emulation/pubsub/ShadowPoller.js +276 -0
  154. package/src/emulation/pubsub/ShadowSubscriptionManager.js +199 -0
  155. package/src/enums/ContainerNames.js +106 -0
  156. package/src/enums/ErrorReason.js +28 -0
  157. package/src/enums/FunctionStatuses.js +15 -0
  158. package/src/enums/FunctionTriggerTypes.js +15 -0
  159. package/src/enums/GatewayState.js +7 -0
  160. package/src/enums/ServiceNames.js +68 -0
  161. package/src/jobs/DatabaseMaintenance.js +184 -0
  162. package/src/jobs/MessageHistoryCleanup.js +152 -0
  163. package/src/mcp/ApiClient.js +25 -0
  164. package/src/mcp/Server.js +52 -0
  165. package/src/mcp/prompts/debugging.js +104 -0
  166. package/src/mcp/resources/platform.js +118 -0
  167. package/src/mcp/tools/data.js +84 -0
  168. package/src/mcp/tools/docker.js +166 -0
  169. package/src/mcp/tools/firestore.js +162 -0
  170. package/src/mcp/tools/functions.js +380 -0
  171. package/src/mcp/tools/httpTraffic.js +69 -0
  172. package/src/mcp/tools/logging.js +174 -0
  173. package/src/mcp/tools/mqtt.js +37 -0
  174. package/src/mcp/tools/postgres.js +130 -0
  175. package/src/mcp/tools/pubsub.js +316 -0
  176. package/src/mcp/tools/redis.js +146 -0
  177. package/src/mcp/tools/services.js +169 -0
  178. package/src/mcp/tools/snapshots.js +88 -0
  179. package/src/mcp/tools/webhooks.js +115 -0
  180. package/src/middleware/DevProxy.js +67 -0
  181. package/src/middleware/ErrorCatcher.js +35 -0
  182. package/src/middleware/HttpProxy.js +215 -0
  183. package/src/middleware/Reply.js +24 -0
  184. package/src/middleware/TraceId.js +9 -0
  185. package/src/middleware/WebhookProxy.js +234 -0
  186. package/src/protocols/mqtt/Broker.js +92 -0
  187. package/src/protocols/mqtt/Handlers.js +175 -0
  188. package/src/protocols/mqtt/PubSubBridge.js +162 -0
  189. package/src/protocols/mqtt/Server.js +116 -0
  190. package/src/runtime/FunctionRunner.js +179 -0
  191. package/src/services/AppGatewayService.js +582 -0
  192. package/src/singletons/FirestoreBroadcaster.js +367 -0
  193. package/src/singletons/FunctionTriggerDispatcher.js +456 -0
  194. package/src/singletons/FunctionsService.js +418 -0
  195. package/src/singletons/HttpProxy.js +224 -0
  196. package/src/singletons/LogBroadcaster.js +159 -0
  197. package/src/singletons/Logger.js +49 -0
  198. package/src/singletons/MemoryJsonStore.js +175 -0
  199. package/src/singletons/MessageBroadcaster.js +190 -0
  200. package/src/singletons/PostgresBroadcaster.js +367 -0
  201. package/src/singletons/PostgresClient.js +180 -0
  202. package/src/singletons/RedisClient.js +184 -0
  203. package/src/singletons/SqliteStore.js +480 -0
  204. package/src/singletons/TickService.js +151 -0
  205. package/src/singletons/WebhookProxy.js +223 -0
@@ -0,0 +1,81 @@
1
+ # API Permissions Pattern
2
+
3
+ For API gateway microservices, define permission combinations for each endpoint.
4
+
5
+ ## Permission combination structure
6
+
7
+ Coding pattern:
8
+ ```javascript
9
+ export const Permissions = {
10
+ create: [
11
+ ['property:write', 'device:write']
12
+ ],
13
+ update: [
14
+ ['property:write', 'device:write']
15
+ ],
16
+ details: [
17
+ ['property:read', 'device:read']
18
+ ],
19
+ list: [
20
+ ['property:read', 'device:read']
21
+ ],
22
+ delete: [
23
+ ['property:write', 'device:write', 'device:delete']
24
+ ]
25
+ }
26
+ ```
27
+
28
+ ## Router integration
29
+
30
+ Permissions are used in the middleware stack:
31
+
32
+ ```javascript
33
+ import KoaRouter from 'koa-router'
34
+ import { PermissionsEvaluator, JoiValidator } from '@gokiteam/koa'
35
+
36
+ import { Schemas } from './Schemas.js'
37
+ import { Controllers } from './Controllers.js'
38
+ import { Permissions } from './Permissions.js'
39
+
40
+ const Router = new KoaRouter()
41
+ const v1 = new KoaRouter({ prefix: '/v1/devices' })
42
+
43
+ v1.post(
44
+ '/create',
45
+ PermissionsEvaluator({ combinations: Permissions.create }),
46
+ JoiValidator({ schema: Schemas.create }),
47
+ Controllers.create
48
+ )
49
+
50
+ Router.use(v1.routes())
51
+ export { Router }
52
+ ```
53
+
54
+ ## Permission combinations
55
+
56
+ Each endpoint can have multiple permission combinations (OR logic):
57
+
58
+ ```javascript
59
+ export const Permissions = {
60
+ create: [
61
+ ['admin'], // Admin can create
62
+ ['property:write', 'device:write'] // Or users with both permissions
63
+ ],
64
+ delete: [
65
+ ['admin'], // Only admin can delete
66
+ ['property:owner', 'device:delete'] // Or property owners with delete permission
67
+ ]
68
+ }
69
+ ```
70
+
71
+ ## Non-gateway microservices
72
+
73
+ For non-gateway microservices (internal services), no Permissions.js file is needed. The Router middleware stack is:
74
+
75
+ ```javascript
76
+ v1.post(
77
+ '/create',
78
+ JoiValidator({ schema: Schemas.create }),
79
+ Controllers.create
80
+ )
81
+ ```
@@ -0,0 +1,83 @@
1
+ # API Route definition
2
+ Each api endpoint:
3
+ - The routing rules should be defined in "src/api/[collection]/Router.js" file.
4
+ - The router is a koa router with a stack of middleware registered on each endpoint route.
5
+ - The router should be added to "src/HttpServer.js" router list.
6
+
7
+ The common scenario is to have a v1 router like:
8
+ ```javascript
9
+ import KoaRouter from 'koa-router'
10
+
11
+ const Router = new KoaRouter()
12
+ const v1 = new KoaRouter({ prefix: '/v1/[collection]' })
13
+
14
+ // route defintions...
15
+
16
+ Router.use(v1.routes())
17
+
18
+ export { Router }
19
+ ```
20
+
21
+ In some cases we might have other versions like:
22
+ ```javascript
23
+ import KoaRouter from 'koa-router'
24
+
25
+ const Router = new KoaRouter()
26
+ const v1 = new KoaRouter({ prefix: '/v1/[collection]' })
27
+ const v2 = new KoaRouter({ prefix: '/v2/[collection]' })
28
+
29
+ // v1 route defintions...
30
+
31
+ // v2 route defintions
32
+
33
+ Router.use(v1.routes())
34
+ Router.use(v2.routes())
35
+
36
+ export { Router }
37
+ ```
38
+
39
+ For each endpoint the common middleware stack is "path", "validation", "controller" like:
40
+ ```javascript
41
+ import KoaRouter from 'koa-router'
42
+
43
+ import { Schemas } from './Schemas.js'
44
+ import { Controllers } from './Controllers.js'
45
+
46
+ const Router = new KoaRouter()
47
+ const v1 = new KoaRouter({ prefix: '/v1/[collection]' })
48
+
49
+ v1
50
+ .post(
51
+ '/create',
52
+ JoiValidator({ schema: Schemas.create }),
53
+ Controllers.create
54
+ )
55
+
56
+ Router.use(v1.routes())
57
+
58
+ export { Router }
59
+ ```
60
+
61
+ For API gateway microservices, the common middleware stack is "path", "permission checks", "validation", "controller" like:
62
+ ```javascript
63
+ import KoaRouter from 'koa-router'
64
+
65
+ import { Schemas } from './Schemas.js'
66
+ import { Controllers } from './Controllers.js'
67
+ import { Permissions } from './Permissions.js'
68
+
69
+ const Router = new KoaRouter()
70
+ const v1 = new KoaRouter({ prefix: '/v1/[collection]' })
71
+
72
+ v1
73
+ .post(
74
+ '/create',
75
+ PermissionsEvaluator({ combinations: Permissions.create }),
76
+ JoiValidator({ schema: Schemas.create }),
77
+ Controllers.create
78
+ )
79
+
80
+ Router.use(v1.routes())
81
+
82
+ export { Router }
83
+ ```
@@ -0,0 +1,197 @@
1
+ # API Schema definition and rules
2
+ Each API endpoint should have schemas for:
3
+ - success response
4
+ - error responses
5
+ - For each error reason being thrown from the related logic, specify Oops error meta object
6
+
7
+ For entities and shared objects, we define schemas inside "src/schemas" directory in the root of the project then use
8
+ them inside "src/api/[collection]/Schemas.js"
9
+
10
+ Coding pattern:
11
+
12
+ Consider the endpoint is `/v1/apps/activateConnection`.
13
+
14
+ The content of "src/schemas/App.js"
15
+ ```javascript
16
+ import { Joi } from '@gokiteam/koa'
17
+
18
+ import { CategoryValues } from '../enums/Category.js'
19
+ import { PermissionValues } from '../enums/Permission.js'
20
+ import { AppStatusValues } from '../enums/AppStatus.js'
21
+
22
+ export const App = Joi.object().keys({
23
+ id: Joi.string().uuid(),
24
+ name: Joi.string(),
25
+ category: Joi.string().valid(...CategoryValues),
26
+ logo: Joi.object().keys({
27
+ url: Joi.string().uri(),
28
+ publicId: Joi.string()
29
+ }).allow(null),
30
+ summary: Joi.string(),
31
+ description: Joi.string(),
32
+ supportEmail: Joi.string().email(),
33
+ isPrivate: Joi.boolean(),
34
+ isCustom: Joi.boolean(),
35
+ status: Joi.string().valid(...AppStatusValues),
36
+ partnerId: Joi.string().uuid().allow(null),
37
+ externalLinks: Joi.array().items(Joi.object().keys({
38
+ title: Joi.string(),
39
+ url: Joi.string().uri()
40
+ })),
41
+ permissions: Joi.array().items(Joi.string().valid(...PermissionValues)),
42
+ createdAt: Joi.string(),
43
+ updatedAt: Joi.string()
44
+ })
45
+ ```
46
+
47
+ The content of "src/api/apps/Schemas.js"
48
+ ```javascript
49
+ import { Joi } from '@gokiteam/koa'
50
+
51
+ import { ErrorReason } from '../../enums/ErrorReason.js'
52
+ import { App } from '../../schemas/App.js'
53
+
54
+ export const Schemas = {
55
+ // other endpoints schema definitions ...
56
+ activateConnection: {
57
+ request: {
58
+ body: {
59
+ id: Joi.string().uuid().required(),
60
+ partnerId: Joi.string().uuid().required(),
61
+ propertyId: Joi.string().uuid().required()
62
+ }
63
+ },
64
+ responses: {
65
+ success: { app: App },
66
+ [ErrorReason.invalidAppId]: {
67
+ traceId: Joi.string().required(),
68
+ id: Joi.string().uuid().required()
69
+ },
70
+ [ErrorReason.connectionDoesNotExist]: {
71
+ traceId: Joi.string().required(),
72
+ id: Joi.string().uuid().required(),
73
+ propertyId: Joi.string().uuid().required()
74
+ },
75
+ [ErrorReason.connectionIsAlreadyActive]: {
76
+ traceId: Joi.string().required(),
77
+ id: Joi.string().uuid().required(),
78
+ propertyId: Joi.string().uuid().required()
79
+ },
80
+ [ErrorReason.insufficientPermission]: {
81
+ traceId: Joi.string().required(),
82
+ id: Joi.string().uuid().required(),
83
+ propertyId: Joi.string().uuid().required()
84
+ }
85
+ }
86
+ }
87
+ }
88
+ ```
89
+
90
+ The summarized content of "src/api/apps/Logic.js"
91
+ ```javascript
92
+ export const Logic = {
93
+ // other endpoint logic implementation ...
94
+ async activateConnection (params) {
95
+ // some code
96
+ if (!hasAccess) {
97
+ throw Oops.permissionDenied('You are not allowed to activate the connection')
98
+ .reason(ErrorReason.insufficientPermission).resource(logicName).meta({ traceId, partnerId, id })
99
+ }
100
+ // some code
101
+ if (!appExists || app.doc.partnerId !== partnerId) {
102
+ throw Oops.notFound('The given app ID does not exist!')
103
+ .reason(ErrorReason.invalidAppId).resource(logicName).meta({ traceId, id })
104
+ }
105
+ // some code
106
+ if (!exists) {
107
+ throw Oops.notAcceptable('There is no connection to activate!')
108
+ .reason(ErrorReason.connectionDoesNotExist).resource(logicName).meta({ traceId, id, propertyId })
109
+ }
110
+ if (connection.doc.status === ConnectionStatus.active) {
111
+ throw Oops.notAcceptable('The connection is already active!')
112
+ .reason(ErrorReason.connectionIsAlreadyActive).resource(logicName).meta({ traceId, id, propertyId })
113
+ }
114
+ // some code
115
+ return { app: app.mask() }
116
+ }
117
+ }
118
+ ```
119
+
120
+ If an API endpoint returns 204 status, the schema should be like:
121
+ ```javascript
122
+ export const Schemas = {
123
+ // other endpoints schemas ...
124
+ emptyResponseEndpoint: {
125
+ request: { /* defintion */ },
126
+ responses: {
127
+ success: {},
128
+ // error reasons ...
129
+ }
130
+ }
131
+ }
132
+ ```
133
+
134
+ If an API endpoint input is a file, the schema should be like:
135
+ ```javascript
136
+ import { Default } from '../../configs/Default.js'
137
+
138
+ export const Schemas = {
139
+ // other endpoints schemas ...
140
+ createUsersWithCsv: {
141
+ request: {
142
+ csvFile: Joi.object().unknown().keys({
143
+ koaAddedFields: {
144
+ contentType: Joi.string().valid(Default.csvFile.correctMimType),
145
+ size: Joi.number()
146
+ .max(Default.csvFile.maxFileSizeMb)
147
+ .message(`The maximum supported file size is ${Default.csvFile.maxFileSizeMb}Mb`),
148
+ filename: Joi.string()
149
+ }
150
+ }).required()
151
+ },
152
+ responses: { /* definition */ }
153
+ }
154
+ }
155
+ ```
156
+
157
+ If an API endpoint is a list endpoint with pagination, filtering, and sorting:
158
+ ```javascript
159
+ import { Joi } from '@gokiteam/koa'
160
+
161
+ import { Device } from '../../schemas/Device.js'
162
+ import { ErrorReason } from '../../enums/ErrorReason.js'
163
+
164
+ export const Schemas = {
165
+ // other endpoints schema definitions ...
166
+ list: {
167
+ request: {
168
+ body: {
169
+ propertyId: Joi.string().uuid().required(),
170
+ query: Joi.string().optional(),
171
+ filter: Joi.object({
172
+ status: Joi.string().optional(),
173
+ model: Joi.number().optional()
174
+ }).optional(),
175
+ page: Joi.object({
176
+ limit: Joi.number().integer().min(1).max(100).default(20),
177
+ offset: Joi.number().integer().min(0).default(0)
178
+ }).optional(),
179
+ sort: Joi.array().items(Joi.object({
180
+ field: Joi.string().required(),
181
+ order: Joi.string().valid('asc', 'desc').required()
182
+ })).optional()
183
+ }
184
+ },
185
+ responses: {
186
+ success: {
187
+ devices: Joi.array().items(Device),
188
+ total: Joi.number().integer()
189
+ },
190
+ [ErrorReason.invalidPropertyId]: {
191
+ traceId: Joi.string().required(),
192
+ propertyId: Joi.string().uuid().required()
193
+ }
194
+ }
195
+ }
196
+ }
197
+ ```
@@ -0,0 +1,7 @@
1
+ # Config definition rules
2
+ - All config should be added to the "src/configs" directory.
3
+ - All values should be defined as environment variables.
4
+ - The environment variables should be added to "config.development" and "config.test" files in the root of the project.
5
+ - The default values or constants should be added to "Default.js"
6
+ - All configs of application's dependent services and tools like databases, cache, search engine, cloud services, etc. should be added to "Application.js"
7
+ - All other microservice API base URLs should be added to "BaseUrls.js"
@@ -0,0 +1,24 @@
1
+ ## Enums definition patterns
2
+
3
+ We define enums when we see:
4
+ - Finite, Known Sets of Values
5
+ - Statuses or States
6
+ - Categories or Types
7
+ - Options
8
+
9
+ All enums should be defined as objects inside "src/enums" directory in the root of the project.
10
+ The file name should be same as the enum name.
11
+
12
+ Coding pattern:
13
+
14
+ The content of "src/enums/Gender.js":
15
+ ```javascript
16
+ export const Gender = Object.freeze({
17
+ male: 'male',
18
+ female: 'female',
19
+ other: 'other'
20
+ })
21
+
22
+ export const GenderValues = Object.values(Gender)
23
+
24
+ ```
@@ -0,0 +1,185 @@
1
+ # Error Handling Patterns
2
+
3
+ ## When to use Oops vs throw
4
+
5
+ ### Use Oops for known business logic errors
6
+ - Validation failures
7
+ - Resource not found
8
+ - Permission denied
9
+ - Logical constraints violated
10
+
11
+ ### Use throw for unexpected errors
12
+ - Unknown errors that should trigger retry
13
+ - System failures
14
+ - External service failures
15
+
16
+ ### Use ProtocolError for device protocol errors
17
+ - Invalid protocol packets
18
+ - Encryption failures
19
+ - Device-specific errors
20
+
21
+ ## Error structure pattern
22
+
23
+ All Oops errors should include:
24
+ - **reason**: from ErrorReason enum
25
+ - **resource**: function name (logicName)
26
+ - **meta**: traceId + relevant context
27
+
28
+ Coding pattern:
29
+ ```javascript
30
+ import { Oops } from '@gokiteam/oops'
31
+ import { ErrorReason } from '../enums/ErrorReason.js'
32
+
33
+ export const Logic = {
34
+ async create (params) {
35
+ const { data, traceId } = params
36
+ const { email } = data
37
+ const logicName = Logic.create.name
38
+ const exists = await User.doesExistCompositeId({ email })
39
+ if (exists) {
40
+ throw Oops.notAcceptable('The email already exists')
41
+ .reason(ErrorReason.userAlreadyExists)
42
+ .resource(logicName)
43
+ .meta({ traceId, email })
44
+ }
45
+ // ... logic
46
+ }
47
+ }
48
+ ```
49
+
50
+ ## Common Oops error types
51
+
52
+ ```javascript
53
+ // Not found (404)
54
+ throw Oops.notFound('Device not found')
55
+ .reason(ErrorReason.deviceNotFound)
56
+ .resource(logicName)
57
+ .meta({ traceId, macAddress })
58
+
59
+ // Invalid argument (400)
60
+ throw Oops.invalidArgument('Invalid MAC address format')
61
+ .reason(ErrorReason.invalidMacAddress)
62
+ .resource(logicName)
63
+ .meta({ traceId, macAddress })
64
+
65
+ // Not acceptable (406)
66
+ throw Oops.notAcceptable('Device already exists')
67
+ .reason(ErrorReason.deviceAlreadyExists)
68
+ .resource(logicName)
69
+ .meta({ traceId, macAddress })
70
+
71
+ // Permission denied (403)
72
+ throw Oops.permissionDenied('Insufficient permissions')
73
+ .reason(ErrorReason.insufficientPermission)
74
+ .resource(logicName)
75
+ .meta({ traceId, userId })
76
+ ```
77
+
78
+ ## Error chaining with .previous()
79
+
80
+ When catching and re-throwing errors, use `.previous()` to maintain error chain:
81
+
82
+ ```javascript
83
+ export const Logic = {
84
+ async create (params) {
85
+ const { data, traceId } = params
86
+ const { email } = data
87
+ const logicName = Logic.create.name
88
+ try {
89
+ const result = await externalService.createUser(email)
90
+ return { user: result }
91
+ } catch (error) {
92
+ throw Oops.internal('Failed to create user in external service')
93
+ .reason(ErrorReason.externalServiceFailed)
94
+ .resource(logicName)
95
+ .previous(error) // Chain the original error
96
+ .meta({ traceId, email })
97
+ }
98
+ }
99
+ }
100
+ ```
101
+
102
+ ## ErrorReason enum usage
103
+
104
+ All error reasons should be defined in `src/enums/ErrorReason.js`:
105
+
106
+ ```javascript
107
+ export const ErrorReason = Object.freeze({
108
+ deviceNotFound: 'deviceNotFound',
109
+ deviceAlreadyExists: 'deviceAlreadyExists',
110
+ invalidMacAddress: 'invalidMacAddress',
111
+ userAlreadyExists: 'userAlreadyExists',
112
+ insufficientPermission: 'insufficientPermission'
113
+ // ... more reasons
114
+ })
115
+ ```
116
+
117
+ ## Logging patterns
118
+
119
+ ### Info logging
120
+ ```javascript
121
+ import { Logger } from '../singletons/Logger.js'
122
+ import { LogEvent } from '../enums/LogEvent.js'
123
+
124
+ Logger.log({
125
+ level: 'info',
126
+ message: 'Processing event',
127
+ data: { event: LogEvent.processingEvent, deviceId, traceId }
128
+ })
129
+ ```
130
+
131
+ ### Warning logging
132
+ ```javascript
133
+ Logger.log({
134
+ level: 'warn',
135
+ message: 'Device not found',
136
+ data: { event: LogEvent.deviceNotFound, deviceId, traceId }
137
+ })
138
+ ```
139
+
140
+ ### Error logging
141
+ ```javascript
142
+ import { ConvertErrorToLog } from '@gokiteam/utils'
143
+
144
+ Logger.log(ConvertErrorToLog({
145
+ error,
146
+ message: 'Failed to process event',
147
+ data: { event: LogEvent.eventProcessingFailed, traceId }
148
+ }))
149
+ ```
150
+
151
+ ## Subscriber error handling
152
+
153
+ ### Known errors - don't retry
154
+ ```javascript
155
+ async process ({ data, attributes }) {
156
+ const traceId = this.getTraceId({ attributes })
157
+ try {
158
+ // ... processing logic
159
+ } catch (error) {
160
+ if (error.isOops) {
161
+ const knownErrorReasons = [
162
+ ErrorReason.deviceNotFound,
163
+ ErrorReason.invalidMqttClient
164
+ ]
165
+ if (knownErrorReasons.includes(error.data.reason)) {
166
+ // Don't retry - mark as failed
167
+ return { state: SubscriberResultState.processFailed, error: error.data }
168
+ }
169
+ }
170
+ // Unknown error - let it throw to trigger retry
171
+ throw error
172
+ }
173
+ }
174
+ ```
175
+
176
+ ### Unknown errors - trigger retry
177
+ ```javascript
178
+ async process ({ data, attributes }) {
179
+ const traceId = this.getTraceId({ attributes })
180
+ // Don't catch unknown errors - let them throw
181
+ // Pub/Sub will retry automatically
182
+ const result = await someOperation({ traceId })
183
+ return { state: SubscriberResultState.processedSuccessfully }
184
+ }
185
+ ```