@friggframework/core 2.0.0-next.0 → 2.0.0-next.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.
package/database/index.js CHANGED
@@ -1,14 +1,15 @@
1
- const { mongoose} = require('./mongoose');
1
+ const { mongoose } = require('./mongoose');
2
2
  const {
3
3
  connectToDatabase,
4
4
  disconnectFromDatabase,
5
5
  createObjectId,
6
6
  } = require('./mongo');
7
- const {IndividualUser} = require('./models/IndividualUser');
8
- const {OrganizationUser} = require('./models/OrganizationUser');
9
- const {State} = require('./models/State');
10
- const {Token} = require('./models/Token');
11
- const {UserModel} = require('./models/UserModel');
7
+ const { IndividualUser } = require('./models/IndividualUser');
8
+ const { OrganizationUser } = require('./models/OrganizationUser');
9
+ const { State } = require('./models/State');
10
+ const { Token } = require('./models/Token');
11
+ const { UserModel } = require('./models/UserModel');
12
+ const { WebsocketConnection } = require('./models/WebsocketConnection');
12
13
 
13
14
  module.exports = {
14
15
  mongoose,
@@ -19,5 +20,6 @@ module.exports = {
19
20
  OrganizationUser,
20
21
  State,
21
22
  Token,
22
- UserModel
23
- }
23
+ UserModel,
24
+ WebsocketConnection,
25
+ };
@@ -0,0 +1,49 @@
1
+ const { mongoose } = require('../mongoose');
2
+ const AWS = require('aws-sdk');
3
+
4
+ const schema = new mongoose.Schema({
5
+ connectionId: { type: mongoose.Schema.Types.String },
6
+ });
7
+
8
+ // Add a static method to get active connections
9
+ schema.statics.getActiveConnections = async function () {
10
+ try {
11
+ const connections = await this.find({}, 'connectionId');
12
+ return connections.map((conn) => ({
13
+ connectionId: conn.connectionId,
14
+ send: async (data) => {
15
+ const apigwManagementApi = new AWS.ApiGatewayManagementApi({
16
+ apiVersion: '2018-11-29',
17
+ endpoint: process.env.WEBSOCKET_API_ENDPOINT,
18
+ });
19
+
20
+ try {
21
+ await apigwManagementApi
22
+ .postToConnection({
23
+ ConnectionId: conn.connectionId,
24
+ Data: JSON.stringify(data),
25
+ })
26
+ .promise();
27
+ } catch (error) {
28
+ if (error.statusCode === 410) {
29
+ console.log(`Stale connection ${conn.connectionId}`);
30
+ await this.deleteOne({
31
+ connectionId: conn.connectionId,
32
+ });
33
+ } else {
34
+ throw error;
35
+ }
36
+ }
37
+ },
38
+ }));
39
+ } catch (error) {
40
+ console.error('Error getting active connections:', error);
41
+ throw error;
42
+ }
43
+ };
44
+
45
+ const WebsocketConnection =
46
+ mongoose.models.WebsocketConnection ||
47
+ mongoose.model('WebsocketConnection', schema);
48
+
49
+ module.exports = { WebsocketConnection };
@@ -0,0 +1,57 @@
1
+ const { createHandler, flushDebugLog } = require('@friggframework/core');
2
+ const express = require('express');
3
+ const bodyParser = require('body-parser');
4
+ const cors = require('cors');
5
+ const Boom = require('@hapi/boom');
6
+ const loadUserManager = require('./routers/middleware/loadUser');
7
+ const serverlessHttp = require('serverless-http');
8
+
9
+ const createApp = (applyMiddleware) => {
10
+ const app = express();
11
+
12
+ app.use(bodyParser.json({ limit: '10mb' }));
13
+ app.use(bodyParser.urlencoded({ extended: true }));
14
+ app.use(
15
+ cors({
16
+ origin: '*',
17
+ credentials: true,
18
+ })
19
+ );
20
+
21
+ app.use(loadUserManager);
22
+
23
+ if (applyMiddleware) applyMiddleware(app);
24
+
25
+ // Handle sending error response and logging server errors to console
26
+ app.use((err, req, res, next) => {
27
+ const boomError = err.isBoom ? err : Boom.boomify(err);
28
+ const {
29
+ output: { statusCode = 500 },
30
+ } = boomError;
31
+
32
+ if (statusCode >= 500) {
33
+ flushDebugLog(boomError);
34
+ res.status(statusCode).json({ error: 'Internal Server Error' });
35
+ } else {
36
+ res.status(statusCode).json({ error: err.message });
37
+ }
38
+ });
39
+
40
+ return app;
41
+ };
42
+
43
+ function createAppHandler(eventName, router, shouldUseDatabase = true) {
44
+ const app = createApp((app) => {
45
+ app.use(router);
46
+ });
47
+ return createHandler({
48
+ eventName,
49
+ method: serverlessHttp(app),
50
+ shouldUseDatabase,
51
+ });
52
+ }
53
+
54
+ module.exports = {
55
+ createApp,
56
+ createAppHandler,
57
+ };
@@ -0,0 +1,87 @@
1
+ const { createFriggBackend, Worker } = require('@friggframework/core');
2
+ const {
3
+ findNearestBackendPackageJson,
4
+ } = require('../../devtools/frigg-cli/utils/backend-path');
5
+ const path = require('node:path');
6
+ const fs = require('fs-extra');
7
+
8
+ const backendPath = findNearestBackendPackageJson();
9
+ if (!backendPath) {
10
+ throw new Error('Could not find backend package.json');
11
+ }
12
+
13
+ const backendDir = path.dirname(backendPath);
14
+ const backendFilePath = path.join(backendDir, 'index.js');
15
+ if (!fs.existsSync(backendFilePath)) {
16
+ throw new Error('Could not find index.js');
17
+ }
18
+
19
+ const backendJsFile = require(backendFilePath);
20
+ const { Router } = require('express');
21
+ const appDefinition = backendJsFile.Definition;
22
+
23
+ const backend = createFriggBackend(appDefinition);
24
+ const loadRouterFromObject = (IntegrationClass, routerObject) => {
25
+ const router = Router();
26
+ const { path, method, event } = routerObject;
27
+ console.log(
28
+ `Registering ${method} ${path} for ${IntegrationClass.Definition.name}`
29
+ );
30
+ router[method.toLowerCase()](path, async (req, res, next) => {
31
+ try {
32
+ const integration = new IntegrationClass({});
33
+ await integration.loadModules();
34
+ await integration.registerEventHandlers();
35
+ const result = await integration.send(event, {req, res, next});
36
+ res.json(result);
37
+ } catch (error) {
38
+ next(error);
39
+ }
40
+ });
41
+
42
+ return router;
43
+ };
44
+ const createQueueWorker = (integrationClass) => {
45
+ class QueueWorker extends Worker {
46
+ async _run(params, context) {
47
+ try {
48
+ let instance;
49
+ if (!params.integrationId) {
50
+ instance = new integrationClass({});
51
+ await instance.loadModules();
52
+ // await instance.loadUserActions();
53
+ await instance.registerEventHandlers();
54
+ console.log(
55
+ `${params.event} for ${integrationClass.Definition.name} integration with no integrationId`
56
+ );
57
+ } else {
58
+ instance =
59
+ await integrationClass.getInstanceFromIntegrationId({
60
+ integrationId: params.integrationId,
61
+ });
62
+ console.log(
63
+ `${params.event} for ${instance.integration.config.type} of integrationId: ${params.integrationId}`
64
+ );
65
+ }
66
+ const res = await instance.send(params.event, {
67
+ data: params.data,
68
+ context,
69
+ });
70
+ return res;
71
+ } catch (error) {
72
+ console.error(
73
+ `Error in ${params.event} for ${integrationClass.Definition.name}:`,
74
+ error
75
+ );
76
+ throw error;
77
+ }
78
+ }
79
+ }
80
+ return QueueWorker;
81
+ };
82
+
83
+ module.exports = {
84
+ ...backend,
85
+ loadRouterFromObject,
86
+ createQueueWorker,
87
+ };
@@ -0,0 +1,26 @@
1
+ const { createIntegrationRouter } = require('@friggframework/core');
2
+ const { createAppHandler } = require('./../app-handler-helpers');
3
+ const { requireLoggedInUser } = require('./middleware/requireLoggedInUser');
4
+ const {
5
+ moduleFactory,
6
+ integrationFactory,
7
+ IntegrationHelper,
8
+ } = require('./../backend-utils');
9
+
10
+ const router = createIntegrationRouter({
11
+ factory: { moduleFactory, integrationFactory, IntegrationHelper },
12
+ requireLoggedInUser,
13
+ getUserId: (req) => req.user.getUserId(),
14
+ });
15
+
16
+ router.route('/redirect/:appId').get((req, res) => {
17
+ res.redirect(
18
+ `${process.env.FRONTEND_URI}/redirect/${
19
+ req.params.appId
20
+ }?${new URLSearchParams(req.query)}`
21
+ );
22
+ });
23
+
24
+ const handler = createAppHandler('HTTP Event: Auth', router);
25
+
26
+ module.exports = { handler, router };
@@ -0,0 +1,42 @@
1
+ const { createAppHandler } = require('./../app-handler-helpers');
2
+ const {
3
+ integrationFactory,
4
+ loadRouterFromObject,
5
+ } = require('./../backend-utils');
6
+ const { Router } = require('express');
7
+
8
+ const handlers = {};
9
+ for (const IntegrationClass of integrationFactory.integrationClasses) {
10
+ const router = Router();
11
+ const basePath = `/api/${IntegrationClass.Definition.name}-integration`;
12
+
13
+ console.log(`\n│ Configuring routes for ${IntegrationClass.Definition.name} Integration:`);
14
+
15
+ for (const routeDef of IntegrationClass.Definition.routes) {
16
+ if (typeof routeDef === 'function') {
17
+ router.use(basePath, routeDef(IntegrationClass));
18
+ console.log(`│ ANY ${basePath}/* (function handler)`);
19
+ } else if (typeof routeDef === 'object') {
20
+ router.use(
21
+ basePath,
22
+ loadRouterFromObject(IntegrationClass, routeDef)
23
+ );
24
+ const method = (routeDef.method || 'ANY').toUpperCase();
25
+ const fullPath = `${basePath}${routeDef.path}`;
26
+ console.log(`│ ${method} ${fullPath}`);
27
+ } else if (routeDef instanceof express.Router) {
28
+ router.use(basePath, routeDef);
29
+ console.log(`│ ANY ${basePath}/* (express router)`);
30
+ }
31
+ }
32
+ console.log('│');
33
+
34
+ handlers[`${IntegrationClass.Definition.name}`] = {
35
+ handler: createAppHandler(
36
+ `HTTP Event: ${IntegrationClass.Definition.name}`,
37
+ router
38
+ ),
39
+ };
40
+ }
41
+
42
+ module.exports = { handlers };
@@ -0,0 +1,15 @@
1
+ const catchAsyncError = require('express-async-handler');
2
+ const { User } = require('../../backend-utils');
3
+
4
+ module.exports = catchAsyncError(async (req, res, next) => {
5
+ const authorizationHeader = req.headers.authorization;
6
+
7
+ if (authorizationHeader) {
8
+ // Removes "Bearer " and trims
9
+ const token = authorizationHeader.split(' ')[1].trim();
10
+ // Load user for later middleware/routes to use
11
+ req.user = await User.newUser({ token });
12
+ }
13
+
14
+ return next();
15
+ });
@@ -0,0 +1,12 @@
1
+ const Boom = require('@hapi/boom');
2
+
3
+ // CheckLoggedIn Middleware
4
+ const requireLoggedInUser = (req, res, next) => {
5
+ if (!req.user || !req.user.isLoggedIn()) {
6
+ throw Boom.unauthorized('Invalid Token');
7
+ }
8
+
9
+ next();
10
+ };
11
+
12
+ module.exports = { requireLoggedInUser };
@@ -0,0 +1,41 @@
1
+ const express = require('express');
2
+ const { createAppHandler } = require('../app-handler-helpers');
3
+ const { checkRequiredParams } = require('@friggframework/core');
4
+ const { User } = require('../backend-utils');
5
+ const catchAsyncError = require('express-async-handler');
6
+
7
+ const router = express();
8
+
9
+ // define the login endpoint
10
+ router.route('/user/login').post(
11
+ catchAsyncError(async (req, res) => {
12
+ const { username, password } = checkRequiredParams(req.body, [
13
+ 'username',
14
+ 'password',
15
+ ]);
16
+ const user = await User.loginUser({ username, password });
17
+ const token = await user.createUserToken(120);
18
+ res.status(201);
19
+ res.json({ token });
20
+ })
21
+ );
22
+
23
+ router.route('/user/create').post(
24
+ catchAsyncError(async (req, res) => {
25
+ const { username, password } = checkRequiredParams(req.body, [
26
+ 'username',
27
+ 'password',
28
+ ]);
29
+ const user = await User.createIndividualUser({
30
+ username,
31
+ password,
32
+ });
33
+ const token = await user.createUserToken(120);
34
+ res.status(201);
35
+ res.json({ token });
36
+ })
37
+ );
38
+
39
+ const handler = createAppHandler('HTTP Event: User', router);
40
+
41
+ module.exports = { handler, router };
@@ -0,0 +1,55 @@
1
+ const { createHandler } = require('@friggframework/core');
2
+ const { WebsocketConnection } = require('@friggframework/core');
3
+
4
+ const handleWebSocketConnection = async (event, context) => {
5
+ // Handle different WebSocket events
6
+ switch (event.requestContext.eventType) {
7
+ case 'CONNECT':
8
+ // Handle new connection
9
+ try {
10
+ const connectionId = event.requestContext.connectionId;
11
+ await WebsocketConnection.create({ connectionId });
12
+ console.log(`Stored new connection: ${connectionId}`);
13
+ return { statusCode: 200, body: 'Connected.' };
14
+ } catch (error) {
15
+ console.error('Error storing connection:', error);
16
+ return { statusCode: 500, body: 'Error connecting.' };
17
+ }
18
+
19
+ case 'DISCONNECT':
20
+ // Handle disconnection
21
+ try {
22
+ const connectionId = event.requestContext.connectionId;
23
+ await WebsocketConnection.deleteOne({ connectionId });
24
+ console.log(`Removed connection: ${connectionId}`);
25
+ return { statusCode: 200, body: 'Disconnected.' };
26
+ } catch (error) {
27
+ console.error('Error removing connection:', error);
28
+ return { statusCode: 500, body: 'Error disconnecting.' };
29
+ }
30
+
31
+ case 'MESSAGE':
32
+ // Handle incoming message
33
+ const message = JSON.parse(event.body);
34
+ console.log('Received message:', message);
35
+
36
+ // Process the message and send a response
37
+ const responseMessage = { message: 'Message received' };
38
+ return {
39
+ statusCode: 200,
40
+ body: JSON.stringify(responseMessage),
41
+ };
42
+
43
+ default:
44
+ return { statusCode: 400, body: 'Unhandled event type.' };
45
+ }
46
+ };
47
+
48
+ const handler = createHandler({
49
+ eventName: 'WebSocket Event',
50
+ method: handleWebSocketConnection,
51
+ shouldUseDatabase: true, // Set to true as we're using the database
52
+ isUserFacingResponse: true, // This is a server-to-server response
53
+ });
54
+
55
+ module.exports = { handler };
@@ -0,0 +1,24 @@
1
+ const { createHandler } = require('@friggframework/core');
2
+ const { integrationFactory, createQueueWorker } = require('../backend-utils');
3
+
4
+ const handlers = {};
5
+ integrationFactory.integrationClasses.forEach((IntegrationClass) => {
6
+ const defaultQueueWorker = createQueueWorker(IntegrationClass);
7
+
8
+ handlers[`${IntegrationClass.Definition.name}`] = {
9
+ queueWorker: createHandler({
10
+ eventName: `Queue Worker for ${IntegrationClass.Definition.name}`,
11
+ isUserFacingResponse: false,
12
+ method: async (event, context) => {
13
+ const worker = new defaultQueueWorker();
14
+ await worker.run(event, context);
15
+ return {
16
+ message: 'Successfully processed the Generic Queue Worker',
17
+ input: event,
18
+ };
19
+ },
20
+ }),
21
+ };
22
+ });
23
+
24
+ module.exports = { handlers };
package/index.js CHANGED
@@ -7,7 +7,12 @@ const {
7
7
  getArrayParamAndVerifyParamType,
8
8
  getAndVerifyType,
9
9
  } = require('./assertions/index');
10
- const { Delegate, Worker, loadInstalledModules, createHandler } = require('./core/index');
10
+ const {
11
+ Delegate,
12
+ Worker,
13
+ loadInstalledModules,
14
+ createHandler,
15
+ } = require('./core/index');
11
16
  const {
12
17
  mongoose,
13
18
  connectToDatabase,
@@ -17,7 +22,8 @@ const {
17
22
  OrganizationUser,
18
23
  State,
19
24
  Token,
20
- UserModel
25
+ UserModel,
26
+ WebsocketConnection,
21
27
  } = require('./database/index');
22
28
  const { Encrypt, Cryptor } = require('./encrypt/encrypt');
23
29
  const {
@@ -26,7 +32,7 @@ const {
26
32
  HaltError,
27
33
  RequiredPropertyError,
28
34
  ParameterTypeError,
29
- } = require('./errors/index');
35
+ } = require('./errors/index');
30
36
  const {
31
37
  IntegrationBase,
32
38
  IntegrationModel,
@@ -36,14 +42,10 @@ const {
36
42
  IntegrationHelper,
37
43
  createIntegrationRouter,
38
44
  checkRequiredParams,
39
- createFriggBackend
45
+ createFriggBackend,
40
46
  } = require('./integrations/index');
41
47
  const { TimeoutCatcher } = require('./lambda/index');
42
- const {
43
- debug,
44
- initDebugLog,
45
- flushDebugLog
46
- } = require('./logs/index');
48
+ const { debug, initDebugLog, flushDebugLog } = require('./logs/index');
47
49
  const {
48
50
  Credential,
49
51
  EntityManager,
@@ -55,11 +57,13 @@ const {
55
57
  Requester,
56
58
  ModuleConstants,
57
59
  ModuleFactory,
58
- Auther
60
+ Auther,
59
61
  } = require('./module-plugin/index');
60
62
 
61
63
  // const {Sync } = require('./syncs/model');
62
64
 
65
+ const { QueuerUtil } = require('./queues');
66
+
63
67
  module.exports = {
64
68
  // assertions
65
69
  expectShallowEqualDbObject,
@@ -69,11 +73,13 @@ module.exports = {
69
73
  getParamAndVerifyParamType,
70
74
  getArrayParamAndVerifyParamType,
71
75
  getAndVerifyType,
76
+
72
77
  // core
73
78
  Delegate,
74
79
  Worker,
75
80
  loadInstalledModules,
76
81
  createHandler,
82
+
77
83
  // database
78
84
  mongoose,
79
85
  connectToDatabase,
@@ -84,15 +90,19 @@ module.exports = {
84
90
  State,
85
91
  Token,
86
92
  UserModel,
93
+ WebsocketConnection,
94
+
87
95
  // encrypt
88
96
  Encrypt,
89
97
  Cryptor,
98
+
90
99
  // errors
91
100
  BaseError,
92
101
  FetchError,
93
102
  HaltError,
94
103
  RequiredPropertyError,
95
104
  ParameterTypeError,
105
+
96
106
  // integrations
97
107
  IntegrationBase,
98
108
  IntegrationModel,
@@ -103,12 +113,15 @@ module.exports = {
103
113
  checkRequiredParams,
104
114
  createIntegrationRouter,
105
115
  createFriggBackend,
116
+
106
117
  // lambda
107
118
  TimeoutCatcher,
119
+
108
120
  // logs
109
121
  debug,
110
122
  initDebugLog,
111
123
  flushDebugLog,
124
+
112
125
  // module plugin
113
126
  Credential,
114
127
  EntityManager,
@@ -120,5 +133,8 @@ module.exports = {
120
133
  Requester,
121
134
  ModuleConstants,
122
135
  ModuleFactory,
123
- Auther
124
- }
136
+ Auther,
137
+
138
+ // queues
139
+ QueuerUtil,
140
+ };