@friggframework/devtools 2.0.0--canary.365.52ef5c7.0 → 2.0.0--canary.367.8656c74.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.
- package/index.js +4 -2
- package/infrastructure/app-handler-helpers.js +57 -0
- package/infrastructure/backend-utils.js +90 -0
- package/infrastructure/create-frigg-infrastructure.js +38 -0
- package/infrastructure/index.js +4 -0
- package/infrastructure/routers/auth.js +26 -0
- package/infrastructure/routers/integration-defined-routers.js +37 -0
- package/infrastructure/routers/middleware/loadUser.js +15 -0
- package/infrastructure/routers/middleware/requireLoggedInUser.js +12 -0
- package/infrastructure/routers/user.js +41 -0
- package/infrastructure/routers/websocket.js +55 -0
- package/infrastructure/serverless-template.js +291 -0
- package/infrastructure/workers/integration-defined-workers.js +24 -0
- package/package.json +18 -7
package/index.js
CHANGED
|
@@ -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,90 @@
|
|
|
1
|
+
const { createFriggBackend, Worker } = require('@friggframework/core');
|
|
2
|
+
const {
|
|
3
|
+
findNearestBackendPackageJson,
|
|
4
|
+
} = require('../frigg-cli/utils/backend-path');
|
|
5
|
+
const path = require('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.body);
|
|
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
|
+
constructor(params) {
|
|
47
|
+
super(params);
|
|
48
|
+
}
|
|
49
|
+
async _run(params, context) {
|
|
50
|
+
try {
|
|
51
|
+
let instance;
|
|
52
|
+
if (!params.integrationId) {
|
|
53
|
+
instance = new integrationClass({});
|
|
54
|
+
await instance.loadModules();
|
|
55
|
+
// await instance.loadUserActions();
|
|
56
|
+
await instance.registerEventHandlers();
|
|
57
|
+
console.log(
|
|
58
|
+
`${params.event} for ${integrationClass.Definition.name} integration with no integrationId`
|
|
59
|
+
);
|
|
60
|
+
} else {
|
|
61
|
+
instance =
|
|
62
|
+
await integrationClass.getInstanceFromIntegrationId({
|
|
63
|
+
integrationId: params.integrationId,
|
|
64
|
+
});
|
|
65
|
+
console.log(
|
|
66
|
+
`${params.event} for ${instance.integration.config.type} of integrationId: ${params.integrationId}`
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
const res = await instance.send(params.event, {
|
|
70
|
+
data: params.data,
|
|
71
|
+
context,
|
|
72
|
+
});
|
|
73
|
+
return res;
|
|
74
|
+
} catch (error) {
|
|
75
|
+
console.error(
|
|
76
|
+
`Error in ${params.event} for ${integrationClass.Definition.name}:`,
|
|
77
|
+
error
|
|
78
|
+
);
|
|
79
|
+
throw error;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return QueueWorker;
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
module.exports = {
|
|
87
|
+
...backend,
|
|
88
|
+
loadRouterFromObject,
|
|
89
|
+
createQueueWorker,
|
|
90
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const fs = require('fs-extra');
|
|
3
|
+
const { composeServerlessDefinition } = require('./serverless-template');
|
|
4
|
+
|
|
5
|
+
const {
|
|
6
|
+
findNearestBackendPackageJson,
|
|
7
|
+
} = require('../frigg-cli/utils/backend-path');
|
|
8
|
+
|
|
9
|
+
function createFriggInfrastructure() {
|
|
10
|
+
const backendPath = findNearestBackendPackageJson();
|
|
11
|
+
if (!backendPath) {
|
|
12
|
+
throw new Error('Could not find backend package.json');
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const backendDir = path.dirname(backendPath);
|
|
16
|
+
const backendFilePath = path.join(backendDir, 'index.js');
|
|
17
|
+
if (!fs.existsSync(backendFilePath)) {
|
|
18
|
+
throw new Error('Could not find index.js');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const backend = require(backendFilePath);
|
|
22
|
+
const appDefinition = backend.Definition;
|
|
23
|
+
|
|
24
|
+
// const serverlessTemplate = require(path.resolve(
|
|
25
|
+
// __dirname,
|
|
26
|
+
// './serverless-template.js'
|
|
27
|
+
// ));
|
|
28
|
+
const definition = composeServerlessDefinition(
|
|
29
|
+
appDefinition,
|
|
30
|
+
backend.IntegrationFactory
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
return {
|
|
34
|
+
...definition,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
module.exports = { createFriggInfrastructure };
|
|
@@ -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,37 @@
|
|
|
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
|
+
loadRouterFromObject,
|
|
9
|
+
} = require('./../backend-utils');
|
|
10
|
+
const { Router } = require('express');
|
|
11
|
+
|
|
12
|
+
const handlers = {};
|
|
13
|
+
integrationFactory.integrationClasses.forEach((IntegrationClass) => {
|
|
14
|
+
const router = Router();
|
|
15
|
+
const basePath = `/api/${IntegrationClass.Definition.name}-integration`;
|
|
16
|
+
IntegrationClass.Definition.routes.forEach((routeDef) => {
|
|
17
|
+
if (typeof routeDef === 'function') {
|
|
18
|
+
router.use(basePath, routeDef(IntegrationClass));
|
|
19
|
+
} else if (typeof routeDef === 'object') {
|
|
20
|
+
router.use(
|
|
21
|
+
basePath,
|
|
22
|
+
loadRouterFromObject(IntegrationClass, routeDef)
|
|
23
|
+
);
|
|
24
|
+
} else if (routeDef instanceof express.Router) {
|
|
25
|
+
router.use(basePath, routeDef);
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
handlers[`${IntegrationClass.Definition.name}`] = {
|
|
30
|
+
handler: createAppHandler(
|
|
31
|
+
`HTTP Event: ${IntegrationClass.Definition.name}`,
|
|
32
|
+
router
|
|
33
|
+
),
|
|
34
|
+
};
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
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,291 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
|
|
4
|
+
const composeServerlessDefinition = (AppDefinition, IntegrationFactory) => {
|
|
5
|
+
const definition = {
|
|
6
|
+
frameworkVersion: '>=3.17.0',
|
|
7
|
+
service: AppDefinition.name || 'create-frigg-app',
|
|
8
|
+
package: {
|
|
9
|
+
individually: true,
|
|
10
|
+
},
|
|
11
|
+
useDotenv: true,
|
|
12
|
+
provider: {
|
|
13
|
+
name: AppDefinition.provider || 'aws',
|
|
14
|
+
runtime: 'nodejs20.x',
|
|
15
|
+
timeout: 30,
|
|
16
|
+
region: 'us-east-1',
|
|
17
|
+
stage: '${opt:stage}',
|
|
18
|
+
environment: {
|
|
19
|
+
STAGE: '${opt:stage}',
|
|
20
|
+
AWS_NODEJS_CONNECTION_REUSE_ENABLED: 1,
|
|
21
|
+
},
|
|
22
|
+
iamRoleStatements: [
|
|
23
|
+
{
|
|
24
|
+
Effect: 'Allow',
|
|
25
|
+
Action: ['sns:Publish'],
|
|
26
|
+
Resource: {
|
|
27
|
+
Ref: 'InternalErrorBridgeTopic',
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
],
|
|
31
|
+
},
|
|
32
|
+
plugins: [
|
|
33
|
+
'serverless-dotenv-plugin',
|
|
34
|
+
'serverless-offline-sqs',
|
|
35
|
+
'serverless-offline',
|
|
36
|
+
'@friggframework/serverless-plugin',
|
|
37
|
+
],
|
|
38
|
+
custom: {
|
|
39
|
+
'serverless-offline': {
|
|
40
|
+
httpPort: 3001,
|
|
41
|
+
lambdaPort: 4001,
|
|
42
|
+
websocketPort: 3002,
|
|
43
|
+
},
|
|
44
|
+
'serverless-offline-sqs': {
|
|
45
|
+
autoCreate: false,
|
|
46
|
+
apiVersion: '2012-11-05',
|
|
47
|
+
endpoint: 'http://localhost:4566',
|
|
48
|
+
region: 'us-east-1',
|
|
49
|
+
accessKeyId: 'root',
|
|
50
|
+
secretAccessKey: 'root',
|
|
51
|
+
skipCacheInvalidation: false,
|
|
52
|
+
},
|
|
53
|
+
webpack: {
|
|
54
|
+
webpackConfig: 'webpack.config.js',
|
|
55
|
+
includeModules: {
|
|
56
|
+
forceExclude: ['aws-sdk'],
|
|
57
|
+
},
|
|
58
|
+
packager: 'npm',
|
|
59
|
+
excludeFiles: ['src/**/*.test.js', 'test/'],
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
functions: {
|
|
63
|
+
defaultWebsocket: {
|
|
64
|
+
handler:
|
|
65
|
+
'/../node_modules/@friggframework/devtools/infrastructure/routers/websocket.handler',
|
|
66
|
+
events: [
|
|
67
|
+
{
|
|
68
|
+
websocket: {
|
|
69
|
+
route: '$connect',
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
websocket: {
|
|
74
|
+
route: '$default',
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
websocket: {
|
|
79
|
+
route: '$disconnect',
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
],
|
|
83
|
+
},
|
|
84
|
+
auth: {
|
|
85
|
+
handler:
|
|
86
|
+
'/../node_modules/@friggframework/devtools/infrastructure/routers/auth.handler',
|
|
87
|
+
events: [
|
|
88
|
+
{
|
|
89
|
+
http: {
|
|
90
|
+
path: '/api/integrations',
|
|
91
|
+
method: 'ANY',
|
|
92
|
+
cors: true,
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
http: {
|
|
97
|
+
path: '/api/integrations/{proxy+}',
|
|
98
|
+
method: 'ANY',
|
|
99
|
+
cors: true,
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
http: {
|
|
104
|
+
path: '/api/authorize',
|
|
105
|
+
method: 'ANY',
|
|
106
|
+
cors: true,
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
],
|
|
110
|
+
},
|
|
111
|
+
user: {
|
|
112
|
+
handler:
|
|
113
|
+
'/../node_modules/@friggframework/devtools/infrastructure/routers/user.handler',
|
|
114
|
+
events: [
|
|
115
|
+
{
|
|
116
|
+
http: {
|
|
117
|
+
path: '/user/{proxy+}',
|
|
118
|
+
method: 'ANY',
|
|
119
|
+
cors: true,
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
],
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
resources: {
|
|
126
|
+
Resources: {
|
|
127
|
+
InternalErrorQueue: {
|
|
128
|
+
Type: 'AWS::SQS::Queue',
|
|
129
|
+
Properties: {
|
|
130
|
+
QueueName:
|
|
131
|
+
'internal-error-queue-${self:provider.stage}',
|
|
132
|
+
MessageRetentionPeriod: 300,
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
InternalErrorBridgeTopic: {
|
|
136
|
+
Type: 'AWS::SNS::Topic',
|
|
137
|
+
Properties: {
|
|
138
|
+
Subscription: [
|
|
139
|
+
{
|
|
140
|
+
Protocol: 'sqs',
|
|
141
|
+
Endpoint: {
|
|
142
|
+
'Fn::GetAtt': ['InternalErrorQueue', 'Arn'],
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
],
|
|
146
|
+
},
|
|
147
|
+
},
|
|
148
|
+
InternalErrorBridgePolicy: {
|
|
149
|
+
Type: 'AWS::SQS::QueuePolicy',
|
|
150
|
+
Properties: {
|
|
151
|
+
Queues: [{ Ref: 'InternalErrorQueue' }],
|
|
152
|
+
PolicyDocument: {
|
|
153
|
+
Version: '2012-10-17',
|
|
154
|
+
Statement: [
|
|
155
|
+
{
|
|
156
|
+
Sid: 'Allow Dead Letter SNS to publish to SQS',
|
|
157
|
+
Effect: 'Allow',
|
|
158
|
+
Principal: {
|
|
159
|
+
Service: 'sns.amazonaws.com',
|
|
160
|
+
},
|
|
161
|
+
Resource: {
|
|
162
|
+
'Fn::GetAtt': [
|
|
163
|
+
'InternalErrorQueue',
|
|
164
|
+
'Arn',
|
|
165
|
+
],
|
|
166
|
+
},
|
|
167
|
+
Action: [
|
|
168
|
+
'SQS:SendMessage',
|
|
169
|
+
'SQS:SendMessageBatch',
|
|
170
|
+
],
|
|
171
|
+
Condition: {
|
|
172
|
+
ArnEquals: {
|
|
173
|
+
'aws:SourceArn': {
|
|
174
|
+
Ref: 'InternalErrorBridgeTopic',
|
|
175
|
+
},
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
},
|
|
179
|
+
],
|
|
180
|
+
},
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
ApiGatewayAlarm5xx: {
|
|
184
|
+
Type: 'AWS::CloudWatch::Alarm',
|
|
185
|
+
Properties: {
|
|
186
|
+
AlarmDescription: 'API Gateway 5xx Errors',
|
|
187
|
+
Namespace: 'AWS/ApiGateway',
|
|
188
|
+
MetricName: '5XXError',
|
|
189
|
+
Statistic: 'Sum',
|
|
190
|
+
Threshold: 0,
|
|
191
|
+
ComparisonOperator: 'GreaterThanThreshold',
|
|
192
|
+
EvaluationPeriods: 1,
|
|
193
|
+
Period: 60,
|
|
194
|
+
AlarmActions: [{ Ref: 'InternalErrorBridgeTopic' }],
|
|
195
|
+
Dimensions: [
|
|
196
|
+
{
|
|
197
|
+
Name: 'ApiName',
|
|
198
|
+
Value: {
|
|
199
|
+
'Fn::Join': [
|
|
200
|
+
'-',
|
|
201
|
+
[
|
|
202
|
+
'${self:provider.stage}',
|
|
203
|
+
'${self:service}',
|
|
204
|
+
],
|
|
205
|
+
],
|
|
206
|
+
},
|
|
207
|
+
},
|
|
208
|
+
],
|
|
209
|
+
},
|
|
210
|
+
},
|
|
211
|
+
},
|
|
212
|
+
},
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
// Add integration-specific functions and resources
|
|
216
|
+
AppDefinition.integrations.forEach((integration) => {
|
|
217
|
+
const integrationName = integration.Definition.name;
|
|
218
|
+
|
|
219
|
+
// Add function for the integration
|
|
220
|
+
definition.functions[integrationName] = {
|
|
221
|
+
handler: `/../node_modules/@friggframework/devtools/infrastructure/routers/integration-defined-routers.handlers.${integrationName}.handler`,
|
|
222
|
+
// events: integration.Definition.routes.map((route) => ({
|
|
223
|
+
// http: {
|
|
224
|
+
// path: `/api/${integrationName}-integration${route.path}`,
|
|
225
|
+
// method: route.method || 'ANY',
|
|
226
|
+
// cors: true,
|
|
227
|
+
// },
|
|
228
|
+
// })),
|
|
229
|
+
events: [
|
|
230
|
+
{
|
|
231
|
+
http: {
|
|
232
|
+
path: `/api/${integrationName}-integration/{proxy*}`,
|
|
233
|
+
method: 'ANY',
|
|
234
|
+
cors: true,
|
|
235
|
+
},
|
|
236
|
+
},
|
|
237
|
+
],
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
// Add SQS Queue for the integration
|
|
241
|
+
const queueReference = `${
|
|
242
|
+
integrationName.charAt(0).toUpperCase() + integrationName.slice(1)
|
|
243
|
+
}Queue`;
|
|
244
|
+
const queueName = `\${self:service}--\${self:provider.stage}-${queueReference}`;
|
|
245
|
+
definition.resources.Resources[queueReference] = {
|
|
246
|
+
Type: 'AWS::SQS::Queue',
|
|
247
|
+
Properties: {
|
|
248
|
+
QueueName: `\${self:custom.${queueReference}}`,
|
|
249
|
+
MessageRetentionPeriod: 60,
|
|
250
|
+
RedrivePolicy: {
|
|
251
|
+
maxReceiveCount: 1,
|
|
252
|
+
deadLetterTargetArn: {
|
|
253
|
+
'Fn::GetAtt': ['InternalErrorQueue', 'Arn'],
|
|
254
|
+
},
|
|
255
|
+
},
|
|
256
|
+
},
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
// Add Queue Worker for the integration
|
|
260
|
+
const queueWorkerName = `${integrationName}QueueWorker`;
|
|
261
|
+
definition.functions[queueWorkerName] = {
|
|
262
|
+
handler: `/../node_modules/@friggframework/devtools/infrastructure/workers/integration-defined-workers.handlers.${integrationName}.queueWorker`,
|
|
263
|
+
reservedConcurrency: 5,
|
|
264
|
+
events: [
|
|
265
|
+
{
|
|
266
|
+
sqs: {
|
|
267
|
+
arn: {
|
|
268
|
+
'Fn::GetAtt': [queueReference, 'Arn'],
|
|
269
|
+
},
|
|
270
|
+
batchSize: 1,
|
|
271
|
+
},
|
|
272
|
+
},
|
|
273
|
+
],
|
|
274
|
+
timeout: 600,
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
// Add Queue URL for the integration to the ENVironment variables
|
|
278
|
+
definition.provider.environment = {
|
|
279
|
+
...definition.provider.environment,
|
|
280
|
+
[integrationName.toUpperCase() + '_QUEUE_URL']: {
|
|
281
|
+
Ref: queueReference,
|
|
282
|
+
},
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
definition.custom[queueReference] = queueName;
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
return definition;
|
|
289
|
+
};
|
|
290
|
+
|
|
291
|
+
module.exports = { composeServerlessDefinition };
|
|
@@ -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/package.json
CHANGED
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@friggframework/devtools",
|
|
3
3
|
"prettier": "@friggframework/prettier-config",
|
|
4
|
-
"version": "2.0.0--canary.
|
|
4
|
+
"version": "2.0.0--canary.367.8656c74.0",
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"@babel/eslint-parser": "^7.18.9",
|
|
7
7
|
"@babel/parser": "^7.25.3",
|
|
8
8
|
"@babel/traverse": "^7.25.3",
|
|
9
|
-
"@friggframework/core": "2.0.0--canary.
|
|
10
|
-
"@friggframework/test": "2.0.0--canary.
|
|
9
|
+
"@friggframework/core": "2.0.0--canary.367.8656c74.0",
|
|
10
|
+
"@friggframework/test": "2.0.0--canary.367.8656c74.0",
|
|
11
|
+
"@hapi/boom": "^7.4.11",
|
|
12
|
+
"@inquirer/prompts": "^5.3.8",
|
|
11
13
|
"axios": "^1.7.2",
|
|
14
|
+
"body-parser": "^1.20.2",
|
|
12
15
|
"commander": "^12.1.0",
|
|
16
|
+
"cors": "^2.8.5",
|
|
13
17
|
"dotenv": "^16.4.5",
|
|
14
18
|
"eslint": "^8.22.0",
|
|
15
19
|
"eslint-config-prettier": "^8.5.0",
|
|
@@ -17,12 +21,19 @@
|
|
|
17
21
|
"eslint-plugin-markdown": "^3.0.0",
|
|
18
22
|
"eslint-plugin-no-only-tests": "^3.0.0",
|
|
19
23
|
"eslint-plugin-yaml": "^0.5.0",
|
|
24
|
+
"express": "^4.19.2",
|
|
25
|
+
"express-async-handler": "^1.2.0",
|
|
20
26
|
"fs-extra": "^11.2.0",
|
|
21
|
-
"
|
|
27
|
+
"lodash": "^4.17.21",
|
|
28
|
+
"serverless-http": "^2.7.0"
|
|
22
29
|
},
|
|
23
30
|
"devDependencies": {
|
|
24
|
-
"@friggframework/eslint-config": "2.0.0--canary.
|
|
25
|
-
"@friggframework/prettier-config": "2.0.0--canary.
|
|
31
|
+
"@friggframework/eslint-config": "2.0.0--canary.367.8656c74.0",
|
|
32
|
+
"@friggframework/prettier-config": "2.0.0--canary.367.8656c74.0",
|
|
33
|
+
"serverless-dotenv-plugin": "^6.0.0",
|
|
34
|
+
"serverless-offline": "^13.8.0",
|
|
35
|
+
"serverless-offline-sqs": "^8.0.0",
|
|
36
|
+
"serverless-webpack": "^5.14.1"
|
|
26
37
|
},
|
|
27
38
|
"scripts": {
|
|
28
39
|
"lint:fix": "prettier --write --loglevel error . && eslint . --fix",
|
|
@@ -46,5 +57,5 @@
|
|
|
46
57
|
"publishConfig": {
|
|
47
58
|
"access": "public"
|
|
48
59
|
},
|
|
49
|
-
"gitHead": "
|
|
60
|
+
"gitHead": "8656c74891f954707de1fc9a42013629b039e14d"
|
|
50
61
|
}
|