@friggframework/core 2.0.0-next.1 → 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/handlers/app-handler-helpers.js +57 -0
- package/handlers/backend-utils.js +87 -0
- package/handlers/routers/auth.js +26 -0
- package/handlers/routers/integration-defined-routers.js +42 -0
- package/handlers/routers/middleware/loadUser.js +15 -0
- package/handlers/routers/middleware/requireLoggedInUser.js +12 -0
- package/handlers/routers/user.js +41 -0
- package/handlers/routers/websocket.js +55 -0
- package/handlers/workers/integration-defined-workers.js +24 -0
- package/index.js +28 -12
- package/integrations/integration-router.js +120 -96
- package/integrations/options.js +2 -3
- package/module-plugin/auther.js +97 -54
- package/package.json +5 -5
|
@@ -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
|
|
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
|
-
} =
|
|
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
|
+
};
|
|
@@ -7,7 +7,11 @@ function createIntegrationRouter(params) {
|
|
|
7
7
|
const router = get(params, 'router', express());
|
|
8
8
|
const factory = get(params, 'factory');
|
|
9
9
|
const getUserId = get(params, 'getUserId', (req) => null);
|
|
10
|
-
const requireLoggedInUser = get(
|
|
10
|
+
const requireLoggedInUser = get(
|
|
11
|
+
params,
|
|
12
|
+
'requireLoggedInUser',
|
|
13
|
+
(req, res, next) => next()
|
|
14
|
+
);
|
|
11
15
|
|
|
12
16
|
router.all('/api/entities*', requireLoggedInUser);
|
|
13
17
|
router.all('/api/authorize', requireLoggedInUser);
|
|
@@ -43,22 +47,23 @@ function checkRequiredParams(params, requiredKeys) {
|
|
|
43
47
|
}
|
|
44
48
|
|
|
45
49
|
function setIntegrationRoutes(router, factory, getUserId) {
|
|
46
|
-
const {moduleFactory, integrationFactory, IntegrationHelper} = factory;
|
|
50
|
+
const { moduleFactory, integrationFactory, IntegrationHelper } = factory;
|
|
47
51
|
router.route('/api/integrations').get(
|
|
48
52
|
catchAsyncError(async (req, res) => {
|
|
49
53
|
const results = await integrationFactory.getIntegrationOptions();
|
|
50
|
-
results.entities.authorized =
|
|
51
|
-
getUserId(req)
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
54
|
+
results.entities.authorized =
|
|
55
|
+
await moduleFactory.getEntitiesForUser(getUserId(req));
|
|
56
|
+
results.integrations =
|
|
57
|
+
await IntegrationHelper.getIntegrationsForUserId(
|
|
58
|
+
getUserId(req)
|
|
59
|
+
);
|
|
56
60
|
|
|
57
61
|
for (const integrationRecord of results.integrations) {
|
|
58
|
-
const integration =
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
+
const integration =
|
|
63
|
+
await integrationFactory.getInstanceFromIntegrationId({
|
|
64
|
+
integrationId: integrationRecord.id,
|
|
65
|
+
userId: getUserId(req),
|
|
66
|
+
});
|
|
62
67
|
integrationRecord.userActions = integration.userActions;
|
|
63
68
|
}
|
|
64
69
|
res.json(results);
|
|
@@ -75,21 +80,22 @@ function setIntegrationRoutes(router, factory, getUserId) {
|
|
|
75
80
|
get(params.config, 'type');
|
|
76
81
|
|
|
77
82
|
// create integration
|
|
78
|
-
const integration =
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
);
|
|
83
|
+
const integration = await integrationFactory.createIntegration(
|
|
84
|
+
params.entities,
|
|
85
|
+
getUserId(req),
|
|
86
|
+
params.config
|
|
87
|
+
);
|
|
84
88
|
|
|
85
89
|
// post integration initialization
|
|
86
90
|
debug(
|
|
87
91
|
`Calling onCreate on the ${integration?.constructor?.Config?.name} Integration with no arguments`
|
|
88
92
|
);
|
|
89
|
-
await integration.
|
|
93
|
+
await integration.send('ON_CREATE', {});
|
|
90
94
|
|
|
91
95
|
res.status(201).json(
|
|
92
|
-
await IntegrationHelper.getFormattedIntegration(
|
|
96
|
+
await IntegrationHelper.getFormattedIntegration(
|
|
97
|
+
integration.record
|
|
98
|
+
)
|
|
93
99
|
);
|
|
94
100
|
})
|
|
95
101
|
);
|
|
@@ -108,19 +114,19 @@ function setIntegrationRoutes(router, factory, getUserId) {
|
|
|
108
114
|
`Calling onUpdate on the ${integration?.constructor?.Config?.name} Integration arguments: `,
|
|
109
115
|
params
|
|
110
116
|
);
|
|
111
|
-
await integration.
|
|
117
|
+
await integration.send('ON_UPDATE', params);
|
|
112
118
|
|
|
113
119
|
res.json(
|
|
114
|
-
await IntegrationHelper.getFormattedIntegration(
|
|
120
|
+
await IntegrationHelper.getFormattedIntegration(
|
|
121
|
+
integration.record
|
|
122
|
+
)
|
|
115
123
|
);
|
|
116
124
|
})
|
|
117
125
|
);
|
|
118
126
|
|
|
119
127
|
router.route('/api/integrations/:integrationId').delete(
|
|
120
128
|
catchAsyncError(async (req, res) => {
|
|
121
|
-
const params = checkRequiredParams(req.params, [
|
|
122
|
-
'integrationId',
|
|
123
|
-
]);
|
|
129
|
+
const params = checkRequiredParams(req.params, ['integrationId']);
|
|
124
130
|
const integration =
|
|
125
131
|
await integrationFactory.getInstanceFromIntegrationId({
|
|
126
132
|
userId: getUserId(req),
|
|
@@ -128,9 +134,9 @@ function setIntegrationRoutes(router, factory, getUserId) {
|
|
|
128
134
|
});
|
|
129
135
|
|
|
130
136
|
debug(
|
|
131
|
-
`Calling onUpdate on the ${integration?.constructor?.
|
|
137
|
+
`Calling onUpdate on the ${integration?.constructor?.Definition?.name} Integration with no arguments`
|
|
132
138
|
);
|
|
133
|
-
await integration.
|
|
139
|
+
await integration.send('ON_DELETE');
|
|
134
140
|
await IntegrationHelper.deleteIntegrationForUserById(
|
|
135
141
|
getUserId(req),
|
|
136
142
|
params.integrationId
|
|
@@ -142,79 +148,101 @@ function setIntegrationRoutes(router, factory, getUserId) {
|
|
|
142
148
|
|
|
143
149
|
router.route('/api/integrations/:integrationId/config/options').get(
|
|
144
150
|
catchAsyncError(async (req, res) => {
|
|
145
|
-
const params = checkRequiredParams(req.params, [
|
|
146
|
-
'integrationId',
|
|
147
|
-
]);
|
|
151
|
+
const params = checkRequiredParams(req.params, ['integrationId']);
|
|
148
152
|
const integration =
|
|
149
153
|
await integrationFactory.getInstanceFromIntegrationId(params);
|
|
150
|
-
res.json(await integration.
|
|
154
|
+
res.json(await integration.send('GET_CONFIG_OPTIONS'));
|
|
151
155
|
})
|
|
152
156
|
);
|
|
153
157
|
|
|
154
|
-
router
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
158
|
+
router
|
|
159
|
+
.route('/api/integrations/:integrationId/config/options/refresh')
|
|
160
|
+
.post(
|
|
161
|
+
catchAsyncError(async (req, res) => {
|
|
162
|
+
const params = checkRequiredParams(req.params, [
|
|
163
|
+
'integrationId',
|
|
164
|
+
]);
|
|
165
|
+
const integration =
|
|
166
|
+
await integrationFactory.getInstanceFromIntegrationId(
|
|
167
|
+
params
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
res.json(
|
|
171
|
+
await integration.send('REFRESH_CONFIG_OPTIONS', req.body)
|
|
172
|
+
);
|
|
173
|
+
})
|
|
174
|
+
);
|
|
175
|
+
router.route('/api/integrations/:integrationId/actions').all(
|
|
169
176
|
catchAsyncError(async (req, res) => {
|
|
170
|
-
const params = checkRequiredParams(req.params, [
|
|
171
|
-
'integrationId',
|
|
172
|
-
'actionId'
|
|
173
|
-
]);
|
|
177
|
+
const params = checkRequiredParams(req.params, ['integrationId']);
|
|
174
178
|
const integration =
|
|
175
179
|
await integrationFactory.getInstanceFromIntegrationId(params);
|
|
176
|
-
|
|
177
|
-
res.json(
|
|
178
|
-
await integration.getActionOptions(params.actionId)
|
|
179
|
-
);
|
|
180
|
+
res.json(await integration.send('GET_USER_ACTIONS', req.body));
|
|
180
181
|
})
|
|
181
182
|
);
|
|
182
183
|
|
|
183
|
-
router
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
184
|
+
router
|
|
185
|
+
.route('/api/integrations/:integrationId/actions/:actionId/options')
|
|
186
|
+
.all(
|
|
187
|
+
catchAsyncError(async (req, res) => {
|
|
188
|
+
const params = checkRequiredParams(req.params, [
|
|
189
|
+
'integrationId',
|
|
190
|
+
'actionId',
|
|
191
|
+
]);
|
|
192
|
+
const integration =
|
|
193
|
+
await integrationFactory.getInstanceFromIntegrationId(
|
|
194
|
+
params
|
|
195
|
+
);
|
|
196
|
+
|
|
197
|
+
res.json(
|
|
198
|
+
await integration.send('GET_USER_ACTION_OPTIONS', {
|
|
199
|
+
actionId: params.actionId,
|
|
200
|
+
data: req.body,
|
|
201
|
+
})
|
|
202
|
+
);
|
|
203
|
+
})
|
|
204
|
+
);
|
|
191
205
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
206
|
+
router
|
|
207
|
+
.route(
|
|
208
|
+
'/api/integrations/:integrationId/actions/:actionId/options/refresh'
|
|
209
|
+
)
|
|
210
|
+
.post(
|
|
211
|
+
catchAsyncError(async (req, res) => {
|
|
212
|
+
const params = checkRequiredParams(req.params, [
|
|
213
|
+
'integrationId',
|
|
214
|
+
'actionId',
|
|
215
|
+
]);
|
|
216
|
+
const integration =
|
|
217
|
+
await integrationFactory.getInstanceFromIntegrationId(
|
|
218
|
+
params
|
|
219
|
+
);
|
|
220
|
+
|
|
221
|
+
res.json(
|
|
222
|
+
await integration.send('REFRESH_USER_ACTION_OPTIONS', {
|
|
223
|
+
actionId: params.actionId,
|
|
224
|
+
data: req.body,
|
|
225
|
+
})
|
|
226
|
+
);
|
|
227
|
+
})
|
|
228
|
+
);
|
|
197
229
|
|
|
198
230
|
router.route('/api/integrations/:integrationId/actions/:actionId').post(
|
|
199
231
|
catchAsyncError(async (req, res) => {
|
|
200
232
|
const params = checkRequiredParams(req.params, [
|
|
201
233
|
'integrationId',
|
|
202
|
-
'actionId'
|
|
234
|
+
'actionId',
|
|
203
235
|
]);
|
|
204
236
|
const integration =
|
|
205
237
|
await integrationFactory.getInstanceFromIntegrationId(params);
|
|
206
238
|
|
|
207
|
-
res.json(
|
|
208
|
-
await integration.notify(params.actionId, req.body)
|
|
209
|
-
);
|
|
239
|
+
res.json(await integration.send(params.actionId, req.body));
|
|
210
240
|
})
|
|
211
241
|
);
|
|
212
242
|
|
|
213
243
|
router.route('/api/integrations/:integrationId').get(
|
|
214
244
|
catchAsyncError(async (req, res) => {
|
|
215
|
-
const params = checkRequiredParams(req.params, [
|
|
216
|
-
'integrationId',
|
|
217
|
-
]);
|
|
245
|
+
const params = checkRequiredParams(req.params, ['integrationId']);
|
|
218
246
|
const integration = await IntegrationHelper.getIntegrationById(
|
|
219
247
|
params.integrationId
|
|
220
248
|
);
|
|
@@ -231,13 +259,12 @@ function setIntegrationRoutes(router, factory, getUserId) {
|
|
|
231
259
|
|
|
232
260
|
router.route('/api/integrations/:integrationId/test-auth').get(
|
|
233
261
|
catchAsyncError(async (req, res) => {
|
|
234
|
-
const params = checkRequiredParams(req.params, [
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
});
|
|
262
|
+
const params = checkRequiredParams(req.params, ['integrationId']);
|
|
263
|
+
const instance =
|
|
264
|
+
await integrationFactory.getInstanceFromIntegrationId({
|
|
265
|
+
userId: getUserId(req),
|
|
266
|
+
integrationId: params.integrationId,
|
|
267
|
+
});
|
|
241
268
|
|
|
242
269
|
if (!instance) {
|
|
243
270
|
throw Boom.notFound();
|
|
@@ -260,7 +287,7 @@ function setIntegrationRoutes(router, factory, getUserId) {
|
|
|
260
287
|
}
|
|
261
288
|
|
|
262
289
|
function setEntityRoutes(router, factory, getUserId) {
|
|
263
|
-
const {moduleFactory, IntegrationHelper} = factory;
|
|
290
|
+
const { moduleFactory, IntegrationHelper } = factory;
|
|
264
291
|
const getModuleInstance = async (req, entityType) => {
|
|
265
292
|
if (!moduleFactory.checkIsValidType(entityType)) {
|
|
266
293
|
throw Boom.badRequest(
|
|
@@ -269,14 +296,15 @@ function setEntityRoutes(router, factory, getUserId) {
|
|
|
269
296
|
)}`
|
|
270
297
|
);
|
|
271
298
|
}
|
|
272
|
-
return await moduleFactory.getInstanceFromTypeName(
|
|
299
|
+
return await moduleFactory.getInstanceFromTypeName(
|
|
300
|
+
entityType,
|
|
301
|
+
getUserId(req)
|
|
302
|
+
);
|
|
273
303
|
};
|
|
274
304
|
|
|
275
305
|
router.route('/api/authorize').get(
|
|
276
306
|
catchAsyncError(async (req, res) => {
|
|
277
|
-
const params = checkRequiredParams(req.query, [
|
|
278
|
-
'entityType',
|
|
279
|
-
]);
|
|
307
|
+
const params = checkRequiredParams(req.query, ['entityType']);
|
|
280
308
|
const module = await getModuleInstance(req, params.entityType);
|
|
281
309
|
const areRequirementsValid =
|
|
282
310
|
module.validateAuthorizationRequirements();
|
|
@@ -330,11 +358,9 @@ function setEntityRoutes(router, factory, getUserId) {
|
|
|
330
358
|
null,
|
|
331
359
|
null,
|
|
332
360
|
getUserId(req)
|
|
333
|
-
)
|
|
334
|
-
|
|
335
|
-
res.json(
|
|
336
|
-
await module.findOrCreateEntity(entityDetails)
|
|
337
361
|
);
|
|
362
|
+
|
|
363
|
+
res.json(await module.findOrCreateEntity(entityDetails));
|
|
338
364
|
})
|
|
339
365
|
);
|
|
340
366
|
|
|
@@ -349,9 +375,7 @@ function setEntityRoutes(router, factory, getUserId) {
|
|
|
349
375
|
throw Boom.forbidden('Credential does not belong to user');
|
|
350
376
|
}
|
|
351
377
|
|
|
352
|
-
const params = checkRequiredParams(req.query, [
|
|
353
|
-
'entityType',
|
|
354
|
-
]);
|
|
378
|
+
const params = checkRequiredParams(req.query, ['entityType']);
|
|
355
379
|
const module = await getModuleInstance(req, params.entityType);
|
|
356
380
|
|
|
357
381
|
res.json(await module.getEntityOptions());
|
|
@@ -384,7 +408,7 @@ function setEntityRoutes(router, factory, getUserId) {
|
|
|
384
408
|
],
|
|
385
409
|
});
|
|
386
410
|
} else {
|
|
387
|
-
res.json({status: 'ok'});
|
|
411
|
+
res.json({ status: 'ok' });
|
|
388
412
|
}
|
|
389
413
|
})
|
|
390
414
|
);
|
|
@@ -409,7 +433,7 @@ function setEntityRoutes(router, factory, getUserId) {
|
|
|
409
433
|
catchAsyncError(async (req, res) => {
|
|
410
434
|
const params = checkRequiredParams(req.params, [
|
|
411
435
|
'entityId',
|
|
412
|
-
getUserId(req)
|
|
436
|
+
getUserId(req),
|
|
413
437
|
]);
|
|
414
438
|
const module = await moduleFactory.getModuleInstanceFromEntityId(
|
|
415
439
|
params.entityId,
|
|
@@ -428,7 +452,7 @@ function setEntityRoutes(router, factory, getUserId) {
|
|
|
428
452
|
catchAsyncError(async (req, res) => {
|
|
429
453
|
const params = checkRequiredParams(req.params, [
|
|
430
454
|
'entityId',
|
|
431
|
-
getUserId(req)
|
|
455
|
+
getUserId(req),
|
|
432
456
|
]);
|
|
433
457
|
const module = await moduleFactory.getModuleInstanceFromEntityId(
|
|
434
458
|
params.entityId,
|
package/integrations/options.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
const { RequiredPropertyError } = require('../errors');
|
|
2
2
|
const { get, getAndVerifyType } = require('../assertions');
|
|
3
|
-
const { ModuleManager } = require('../module-plugin');
|
|
4
3
|
|
|
5
4
|
class Options {
|
|
6
5
|
constructor(params) {
|
|
@@ -18,7 +17,7 @@ class Options {
|
|
|
18
17
|
}
|
|
19
18
|
|
|
20
19
|
this.display = {};
|
|
21
|
-
this.display.name = get(params.display, '
|
|
20
|
+
this.display.name = get(params.display, 'label');
|
|
22
21
|
this.display.description = get(params.display, 'description');
|
|
23
22
|
this.display.detailsUrl = get(params.display, 'detailsUrl');
|
|
24
23
|
this.display.icon = get(params.display, 'icon');
|
|
@@ -26,7 +25,7 @@ class Options {
|
|
|
26
25
|
|
|
27
26
|
get() {
|
|
28
27
|
return {
|
|
29
|
-
type: this.module.getName(),
|
|
28
|
+
type: this.module.definition.getName(),
|
|
30
29
|
|
|
31
30
|
// Flag for if the User can configure any settings
|
|
32
31
|
hasUserConfig: this.hasUserConfig,
|
package/module-plugin/auther.js
CHANGED
|
@@ -25,15 +25,14 @@
|
|
|
25
25
|
// 1. Add definition of expected params to API Class (or could just be credential?)
|
|
26
26
|
// 2.
|
|
27
27
|
|
|
28
|
-
|
|
29
28
|
const { Delegate } = require('../core');
|
|
30
29
|
const { get } = require('../assertions');
|
|
31
30
|
const _ = require('lodash');
|
|
32
|
-
const {flushDebugLog} = require('../logs');
|
|
31
|
+
const { flushDebugLog } = require('../logs');
|
|
33
32
|
const { Credential } = require('./credential');
|
|
34
33
|
const { Entity } = require('./entity');
|
|
35
34
|
const { mongoose } = require('../database/mongoose');
|
|
36
|
-
const {ModuleConstants} = require(
|
|
35
|
+
const { ModuleConstants } = require('./ModuleConstants');
|
|
37
36
|
|
|
38
37
|
class Auther extends Delegate {
|
|
39
38
|
static validateDefinition(definition) {
|
|
@@ -55,21 +54,35 @@ class Auther extends Delegate {
|
|
|
55
54
|
if (!definition.requiredAuthMethods) {
|
|
56
55
|
throw new Error('Auther definition requires requiredAuthMethods');
|
|
57
56
|
} else {
|
|
58
|
-
if (
|
|
59
|
-
|
|
60
|
-
|
|
57
|
+
if (
|
|
58
|
+
definition.API.requesterType ===
|
|
59
|
+
ModuleConstants.authType.oauth2 &&
|
|
60
|
+
!definition.requiredAuthMethods.getToken
|
|
61
|
+
) {
|
|
62
|
+
throw new Error(
|
|
63
|
+
'Auther definition requires requiredAuthMethods.getToken'
|
|
64
|
+
);
|
|
61
65
|
}
|
|
62
66
|
if (!definition.requiredAuthMethods.getEntityDetails) {
|
|
63
|
-
throw new Error(
|
|
67
|
+
throw new Error(
|
|
68
|
+
'Auther definition requires requiredAuthMethods.getEntityDetails'
|
|
69
|
+
);
|
|
64
70
|
}
|
|
65
71
|
if (!definition.requiredAuthMethods.getCredentialDetails) {
|
|
66
|
-
throw new Error(
|
|
72
|
+
throw new Error(
|
|
73
|
+
'Auther definition requires requiredAuthMethods.getCredentialDetails'
|
|
74
|
+
);
|
|
67
75
|
}
|
|
68
76
|
if (!definition.requiredAuthMethods.apiPropertiesToPersist) {
|
|
69
|
-
throw new Error(
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
77
|
+
throw new Error(
|
|
78
|
+
'Auther definition requires requiredAuthMethods.apiPropertiesToPersist'
|
|
79
|
+
);
|
|
80
|
+
} else if (definition.Credential) {
|
|
81
|
+
for (const prop of definition.requiredAuthMethods
|
|
82
|
+
.apiPropertiesToPersist?.credential) {
|
|
83
|
+
if (
|
|
84
|
+
!definition.Credential.schema.paths.hasOwnProperty(prop)
|
|
85
|
+
) {
|
|
73
86
|
throw new Error(
|
|
74
87
|
`Auther definition requires Credential schema to have property ${prop}`
|
|
75
88
|
);
|
|
@@ -77,7 +90,9 @@ class Auther extends Delegate {
|
|
|
77
90
|
}
|
|
78
91
|
}
|
|
79
92
|
if (!definition.requiredAuthMethods.testAuthRequest) {
|
|
80
|
-
throw new Error(
|
|
93
|
+
throw new Error(
|
|
94
|
+
'Auther definition requires requiredAuthMethods.testAuth'
|
|
95
|
+
);
|
|
81
96
|
}
|
|
82
97
|
}
|
|
83
98
|
}
|
|
@@ -97,14 +112,17 @@ class Auther extends Delegate {
|
|
|
97
112
|
this.name = definition.moduleName;
|
|
98
113
|
this.modelName = definition.modelName;
|
|
99
114
|
this.apiClass = definition.API;
|
|
100
|
-
this.CredentialModel =
|
|
115
|
+
this.CredentialModel =
|
|
116
|
+
definition.Credential || this.getCredentialModel();
|
|
101
117
|
this.EntityModel = definition.Entity || this.getEntityModel();
|
|
102
118
|
}
|
|
103
119
|
|
|
104
120
|
static async getInstance(params) {
|
|
105
121
|
const instance = new this(params);
|
|
106
122
|
if (params.entityId) {
|
|
107
|
-
instance.entity = await instance.EntityModel.findById(
|
|
123
|
+
instance.entity = await instance.EntityModel.findById(
|
|
124
|
+
params.entityId
|
|
125
|
+
);
|
|
108
126
|
instance.credential = await instance.CredentialModel.findById(
|
|
109
127
|
instance.entity.credential
|
|
110
128
|
);
|
|
@@ -132,7 +150,7 @@ class Auther extends Delegate {
|
|
|
132
150
|
}
|
|
133
151
|
|
|
134
152
|
static getEntityModelFromDefinition(definition) {
|
|
135
|
-
const partialModule = new this({definition});
|
|
153
|
+
const partialModule = new this({ definition });
|
|
136
154
|
return partialModule.getEntityModel();
|
|
137
155
|
}
|
|
138
156
|
|
|
@@ -151,30 +169,38 @@ class Auther extends Delegate {
|
|
|
151
169
|
getEntityModel() {
|
|
152
170
|
if (!this.EntityModel) {
|
|
153
171
|
const prefix = this.modelName ?? _.upperFirst(this.getName());
|
|
154
|
-
const arrayToDefaultObject = (array, defaultValue) =>
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
172
|
+
const arrayToDefaultObject = (array, defaultValue) =>
|
|
173
|
+
_.mapValues(_.keyBy(array), () => defaultValue);
|
|
174
|
+
const schema = new mongoose.Schema(
|
|
175
|
+
arrayToDefaultObject(this.apiPropertiesToPersist.entity, {
|
|
176
|
+
type: mongoose.Schema.Types.Mixed,
|
|
177
|
+
trim: true,
|
|
178
|
+
})
|
|
179
|
+
);
|
|
159
180
|
const name = `${prefix}Entity`;
|
|
160
181
|
this.EntityModel =
|
|
161
|
-
Entity.discriminators?.[name] ||
|
|
182
|
+
Entity.discriminators?.[name] ||
|
|
183
|
+
Entity.discriminator(name, schema);
|
|
162
184
|
}
|
|
163
185
|
return this.EntityModel;
|
|
164
186
|
}
|
|
165
187
|
|
|
166
188
|
getCredentialModel() {
|
|
167
189
|
if (!this.CredentialModel) {
|
|
168
|
-
const arrayToDefaultObject = (array, defaultValue) =>
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
190
|
+
const arrayToDefaultObject = (array, defaultValue) =>
|
|
191
|
+
_.mapValues(_.keyBy(array), () => defaultValue);
|
|
192
|
+
const schema = new mongoose.Schema(
|
|
193
|
+
arrayToDefaultObject(this.apiPropertiesToPersist.credential, {
|
|
194
|
+
type: mongoose.Schema.Types.Mixed,
|
|
195
|
+
trim: true,
|
|
196
|
+
lhEncrypt: true,
|
|
197
|
+
})
|
|
198
|
+
);
|
|
174
199
|
const prefix = this.modelName ?? _.upperFirst(this.getName());
|
|
175
200
|
const name = `${prefix}Credential`;
|
|
176
201
|
this.CredentialModel =
|
|
177
|
-
Credential.discriminators?.[name] ||
|
|
202
|
+
Credential.discriminators?.[name] ||
|
|
203
|
+
Credential.discriminator(name, schema);
|
|
178
204
|
}
|
|
179
205
|
return this.CredentialModel;
|
|
180
206
|
}
|
|
@@ -197,7 +223,10 @@ class Auther extends Delegate {
|
|
|
197
223
|
async validateAuthorizationRequirements() {
|
|
198
224
|
const requirements = await this.getAuthorizationRequirements();
|
|
199
225
|
let valid = true;
|
|
200
|
-
if (
|
|
226
|
+
if (
|
|
227
|
+
['oauth1', 'oauth2'].includes(requirements.type) &&
|
|
228
|
+
!requirements.url
|
|
229
|
+
) {
|
|
201
230
|
valid = false;
|
|
202
231
|
}
|
|
203
232
|
return valid;
|
|
@@ -238,20 +267,32 @@ class Auther extends Delegate {
|
|
|
238
267
|
throw new Error('Authorization failed');
|
|
239
268
|
}
|
|
240
269
|
const entityDetails = await this.getEntityDetails(
|
|
241
|
-
this.api,
|
|
270
|
+
this.api,
|
|
271
|
+
params,
|
|
272
|
+
tokenResponse,
|
|
273
|
+
this.userId
|
|
274
|
+
);
|
|
275
|
+
Object.assign(
|
|
276
|
+
entityDetails.details,
|
|
277
|
+
this.apiParamsFromEntity(this.api)
|
|
242
278
|
);
|
|
243
|
-
Object.assign(entityDetails.details, this.apiParamsFromEntity(this.api));
|
|
244
279
|
await this.findOrCreateEntity(entityDetails);
|
|
245
280
|
return {
|
|
246
281
|
credential_id: this.credential.id,
|
|
247
282
|
entity_id: this.entity.id,
|
|
248
283
|
type: this.getName(),
|
|
249
|
-
}
|
|
284
|
+
};
|
|
250
285
|
}
|
|
251
286
|
|
|
252
287
|
async onTokenUpdate() {
|
|
253
|
-
const credentialDetails = await this.getCredentialDetails(
|
|
254
|
-
|
|
288
|
+
const credentialDetails = await this.getCredentialDetails(
|
|
289
|
+
this.api,
|
|
290
|
+
this.userId
|
|
291
|
+
);
|
|
292
|
+
Object.assign(
|
|
293
|
+
credentialDetails.details,
|
|
294
|
+
this.apiParamsFromCredential(this.api)
|
|
295
|
+
);
|
|
255
296
|
credentialDetails.details.auth_is_valid = true;
|
|
256
297
|
await this.updateOrCreateCredential(credentialDetails);
|
|
257
298
|
}
|
|
@@ -259,11 +300,9 @@ class Auther extends Delegate {
|
|
|
259
300
|
async receiveNotification(notifier, delegateString, object = null) {
|
|
260
301
|
if (delegateString === this.api.DLGT_TOKEN_UPDATE) {
|
|
261
302
|
await this.onTokenUpdate();
|
|
262
|
-
}
|
|
263
|
-
else if (delegateString === this.api.DLGT_TOKEN_DEAUTHORIZED) {
|
|
303
|
+
} else if (delegateString === this.api.DLGT_TOKEN_DEAUTHORIZED) {
|
|
264
304
|
await this.deauthorize();
|
|
265
|
-
}
|
|
266
|
-
else if (delegateString === this.api.DLGT_INVALID_AUTH) {
|
|
305
|
+
} else if (delegateString === this.api.DLGT_INVALID_AUTH) {
|
|
267
306
|
await this.markCredentialsInvalid();
|
|
268
307
|
}
|
|
269
308
|
}
|
|
@@ -286,10 +325,10 @@ class Auther extends Delegate {
|
|
|
286
325
|
const search = await this.EntityModel.find(identifiers);
|
|
287
326
|
if (search.length > 1) {
|
|
288
327
|
throw new Error(
|
|
289
|
-
'Multiple entities found with the same identifiers: ' +
|
|
328
|
+
'Multiple entities found with the same identifiers: ' +
|
|
329
|
+
JSON.stringify(identifiers)
|
|
290
330
|
);
|
|
291
|
-
}
|
|
292
|
-
else if (search.length === 0) {
|
|
331
|
+
} else if (search.length === 0) {
|
|
293
332
|
this.entity = await this.EntityModel.create({
|
|
294
333
|
credential: this.credential.id,
|
|
295
334
|
...details,
|
|
@@ -308,25 +347,27 @@ class Auther extends Delegate {
|
|
|
308
347
|
const identifiers = get(credentialDetails, 'identifiers');
|
|
309
348
|
const details = get(credentialDetails, 'details');
|
|
310
349
|
|
|
311
|
-
if (!this.credential){
|
|
312
|
-
const credentialSearch = await this.CredentialModel.find(
|
|
350
|
+
if (!this.credential) {
|
|
351
|
+
const credentialSearch = await this.CredentialModel.find(
|
|
352
|
+
identifiers
|
|
353
|
+
);
|
|
313
354
|
if (credentialSearch.length > 1) {
|
|
314
|
-
throw new Error(
|
|
315
|
-
|
|
316
|
-
|
|
355
|
+
throw new Error(
|
|
356
|
+
`Multiple credentials found with same identifiers: ${identifiers}`
|
|
357
|
+
);
|
|
358
|
+
} else if (credentialSearch.length === 1) {
|
|
317
359
|
// found exactly one credential with these identifiers
|
|
318
360
|
this.credential = credentialSearch[0];
|
|
319
|
-
}
|
|
320
|
-
else {
|
|
361
|
+
} else {
|
|
321
362
|
// found no credential with these identifiers (match none for insert)
|
|
322
|
-
this.credential = {$exists: false};
|
|
363
|
+
this.credential = { $exists: false };
|
|
323
364
|
}
|
|
324
365
|
}
|
|
325
366
|
// update credential or create if none was found
|
|
326
367
|
this.credential = await this.CredentialModel.findOneAndUpdate(
|
|
327
|
-
{_id: this.credential},
|
|
328
|
-
{$set: {...identifiers, ...details}},
|
|
329
|
-
{useFindAndModify: true, new: true, upsert: true}
|
|
368
|
+
{ _id: this.credential },
|
|
369
|
+
{ $set: { ...identifiers, ...details } },
|
|
370
|
+
{ useFindAndModify: true, new: true, upsert: true }
|
|
330
371
|
);
|
|
331
372
|
}
|
|
332
373
|
|
|
@@ -340,7 +381,9 @@ class Auther extends Delegate {
|
|
|
340
381
|
async deauthorize() {
|
|
341
382
|
this.api = new this.apiClass();
|
|
342
383
|
if (this.entity?.credential) {
|
|
343
|
-
await this.CredentialModel.deleteOne({
|
|
384
|
+
await this.CredentialModel.deleteOne({
|
|
385
|
+
_id: this.entity.credential,
|
|
386
|
+
});
|
|
344
387
|
this.entity.credential = undefined;
|
|
345
388
|
await this.entity.save();
|
|
346
389
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@friggframework/core",
|
|
3
3
|
"prettier": "@friggframework/prettier-config",
|
|
4
|
-
"version": "2.0.0-next.
|
|
4
|
+
"version": "2.0.0-next.10",
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"@hapi/boom": "^10.0.1",
|
|
7
7
|
"aws-sdk": "^2.1200.0",
|
|
@@ -15,9 +15,9 @@
|
|
|
15
15
|
"node-fetch": "^2.6.7"
|
|
16
16
|
},
|
|
17
17
|
"devDependencies": {
|
|
18
|
-
"@friggframework/eslint-config": "2.0.0-next.
|
|
19
|
-
"@friggframework/prettier-config": "2.0.0-next.
|
|
20
|
-
"@friggframework/test": "2.0.0-next.
|
|
18
|
+
"@friggframework/eslint-config": "2.0.0-next.10",
|
|
19
|
+
"@friggframework/prettier-config": "2.0.0-next.10",
|
|
20
|
+
"@friggframework/test": "2.0.0-next.10",
|
|
21
21
|
"@types/lodash": "^4.14.191",
|
|
22
22
|
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
|
23
23
|
"chai": "^4.3.6",
|
|
@@ -48,5 +48,5 @@
|
|
|
48
48
|
},
|
|
49
49
|
"homepage": "https://github.com/friggframework/frigg#readme",
|
|
50
50
|
"description": "",
|
|
51
|
-
"gitHead": "
|
|
51
|
+
"gitHead": "67dc76e94c02c0074f7ff2d7292c4d2203bf3949"
|
|
52
52
|
}
|