@friggframework/core 0.2.31-v1-alpha-package-update.0 → 1.0.1-v1-alpha-package-update.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/assertions/CHANGELOG.md +87 -0
- package/assertions/LICENSE.md +9 -0
- package/assertions/README.md +3 -0
- package/assertions/bump.txt +1 -0
- package/assertions/get.js +139 -0
- package/assertions/index.js +19 -0
- package/assertions/is-equal.js +17 -0
- package/associations/LICENSE.md +9 -0
- package/associations/README.md +3 -0
- package/associations/association.js +78 -0
- package/associations/bump3.txt +0 -0
- package/associations/jest.config.js +5 -0
- package/associations/model.js +54 -0
- package/bump.txt +0 -0
- package/core/.eslintrc.json +3 -0
- package/{Delegate.js → core/Delegate.js} +1 -1
- package/core/LICENSE.md +9 -0
- package/{Worker.js → core/Worker.js} +2 -2
- package/core/bump3.txt +0 -0
- package/{create-handler.js → core/create-handler.js} +2 -2
- package/core/index.js +6 -0
- package/core/jest.config.js +5 -0
- package/database/.eslintrc.json +3 -0
- package/database/CHANGELOG.md +97 -0
- package/database/LICENSE.md +9 -0
- package/database/README.md +3 -0
- package/database/bump3.txt +0 -0
- package/database/index.js +17 -0
- package/database/jest.config.js +5 -0
- package/database/models/IndividualUser.js +76 -0
- package/database/models/OrganizationUser.js +29 -0
- package/database/models/State.js +9 -0
- package/database/models/Token.js +70 -0
- package/database/models/UserModel.js +7 -0
- package/database/mongo.js +45 -0
- package/database/mongoose.js +5 -0
- package/encrypt/.eslintrc.json +3 -0
- package/encrypt/CHANGELOG.md +65 -0
- package/encrypt/Cryptor.js +236 -0
- package/encrypt/Cryptor.test.js +32 -0
- package/encrypt/LICENSE.md +9 -0
- package/encrypt/README.md +3 -0
- package/encrypt/aes.js +27 -0
- package/encrypt/bump3.txt +0 -0
- package/encrypt/encrypt.js +124 -0
- package/encrypt/encrypt.test.js +1068 -0
- package/encrypt/index.js +3 -0
- package/encrypt/jest.config.js +5 -0
- package/encrypt/test-encrypt.js +107 -0
- package/errors/.eslintrc.json +3 -0
- package/errors/CHANGELOG.md +44 -0
- package/errors/LICENSE.md +9 -0
- package/errors/README.md +3 -0
- package/errors/base-error.js +23 -0
- package/errors/base-error.test.js +32 -0
- package/errors/bump.txt +1 -0
- package/errors/bump3.txt +0 -0
- package/errors/fetch-error.js +72 -0
- package/errors/fetch-error.test.js +79 -0
- package/errors/halt-error.js +10 -0
- package/errors/halt-error.test.js +11 -0
- package/errors/index.js +15 -0
- package/errors/jest.config.js +5 -0
- package/errors/validation-errors.js +23 -0
- package/errors/validation-errors.test.js +120 -0
- package/eslint-config/.eslintrc.json +3 -0
- package/eslint-config/CHANGELOG.md +17 -0
- package/eslint-config/LICENSE.md +9 -0
- package/eslint-config/README.md +3 -0
- package/eslint-config/bump3.txt +0 -0
- package/eslint-config/index.js +38 -0
- package/index.js +29 -5
- package/integrations/.eslintrc.json +3 -0
- package/integrations/CHANGELOG.md +244 -0
- package/integrations/LICENSE.md +9 -0
- package/integrations/README.md +3 -0
- package/integrations/bump3.txt +0 -0
- package/integrations/create-frigg-backend.js +31 -0
- package/integrations/index.js +19 -0
- package/integrations/integration-base.js +162 -0
- package/integrations/integration-factory.js +166 -0
- package/integrations/integration-mapping.js +43 -0
- package/integrations/integration-model.js +42 -0
- package/integrations/integration-router.js +367 -0
- package/integrations/integration-user.js +144 -0
- package/integrations/jest-setup.js +2 -0
- package/integrations/jest-teardown.js +2 -0
- package/integrations/jest.config.js +12 -0
- package/integrations/options.js +54 -0
- package/integrations/test/integration-base.test.js +143 -0
- package/lambda/README.md +3 -0
- package/lambda/TimeoutCatcher.js +43 -0
- package/lambda/TimeoutCatcher.test.js +68 -0
- package/lambda/bump3.txt +0 -0
- package/lambda/index.js +3 -0
- package/lambda/jest.config.js +3 -0
- package/logs/.eslintrc.json +3 -0
- package/logs/CHANGELOG.md +57 -0
- package/logs/LICENSE.md +9 -0
- package/logs/README.md +3 -0
- package/logs/bump3.txt +0 -0
- package/logs/index.js +7 -0
- package/logs/jest.config.js +5 -0
- package/logs/logger.js +69 -0
- package/logs/logger.test.js +76 -0
- package/migrations/README.md +3 -0
- package/migrations/bump3.txt +0 -0
- package/migrations/index.js +9 -0
- package/migrations/jest.config.js +3 -0
- package/migrations/manager.js +33 -0
- package/migrations/migrator.js +170 -0
- package/migrations/options.js +28 -0
- package/module-plugin/.eslintrc.json +3 -0
- package/module-plugin/CHANGELOG.md +224 -0
- package/module-plugin/LICENSE.md +9 -0
- package/module-plugin/ModuleConstants.js +10 -0
- package/module-plugin/README.md +3 -0
- package/module-plugin/auther.js +342 -0
- package/module-plugin/bump3.txt +0 -0
- package/module-plugin/credential.js +22 -0
- package/module-plugin/entity-manager.js +70 -0
- package/module-plugin/entity.js +46 -0
- package/module-plugin/index.js +25 -0
- package/module-plugin/jest-setup.js +3 -0
- package/module-plugin/jest-teardown.js +2 -0
- package/module-plugin/jest.config.js +20 -0
- package/module-plugin/manager.js +169 -0
- package/module-plugin/module-factory.js +60 -0
- package/module-plugin/requester/api-key.js +36 -0
- package/module-plugin/requester/basic.js +43 -0
- package/module-plugin/requester/oauth-2.js +219 -0
- package/module-plugin/requester/requester.js +150 -0
- package/module-plugin/requester/requester.test.js +28 -0
- package/module-plugin/test/auther.test.js +97 -0
- package/module-plugin/test/mock-api/api.js +29 -0
- package/module-plugin/test/mock-api/definition.js +48 -0
- package/module-plugin/test/mock-api/mocks/hubspot.js +43 -0
- package/package.json +37 -12
- package/prettier-config/.eslintrc.json +3 -0
- package/prettier-config/CHANGELOG.md +17 -0
- package/prettier-config/LICENSE.md +9 -0
- package/prettier-config/README.md +3 -0
- package/prettier-config/bump3.txt +0 -0
- package/prettier-config/index.js +6 -0
- package/syncs/README.md +3 -0
- package/syncs/bump3.txt +0 -0
- package/syncs/jest.config.js +5 -0
- package/syncs/manager.js +466 -0
- package/syncs/model.js +62 -0
- package/syncs/sync.js +114 -0
- package/test-environment/.eslintrc.json +3 -0
- package/test-environment/Authenticator.js +73 -0
- package/test-environment/CHANGELOG.md +46 -0
- package/test-environment/LICENSE.md +9 -0
- package/test-environment/README.md +3 -0
- package/test-environment/auther-definition-method-tester.js +45 -0
- package/test-environment/auther-definition-tester.js +104 -0
- package/test-environment/bump3.txt +0 -0
- package/test-environment/index.js +24 -0
- package/test-environment/integration-validator.js +2 -0
- package/test-environment/jest-global-setup.js +6 -0
- package/test-environment/jest-global-teardown.js +3 -0
- package/test-environment/jest-preset.js +14 -0
- package/test-environment/mock-api-readme.md +102 -0
- package/test-environment/mock-api.js +284 -0
- package/test-environment/mock-integration.js +82 -0
- package/test-environment/mongodb.js +22 -0
- package/test-environment/override-environment.js +11 -0
- package/types/CHANGELOG.md +49 -0
- package/types/README.md +24 -0
- package/types/assertions/index.d.ts +83 -0
- package/types/associations/index.d.ts +74 -0
- package/types/bump3.txt +0 -0
- package/types/core/index.d.ts +54 -0
- package/types/database/index.d.ts +3 -0
- package/types/encrypt/index.d.ts +5 -0
- package/types/errors/index.d.ts +66 -0
- package/types/eslint-config/index.d.ts +41 -0
- package/types/index.d.ts +14 -0
- package/types/integrations/index.d.ts +191 -0
- package/types/lambda/index.d.ts +31 -0
- package/types/logs/index.d.ts +5 -0
- package/types/module-plugin/index.d.ts +293 -0
- package/types/prettier-config/index.d.ts +6 -0
- package/types/syncs/index.d.ts +128 -0
- package/types/test-environment/index.d.ts +17 -0
- package/types/tsconfig.json +103 -0
- /package/{.eslintrc.json → assertions/.eslintrc.json} +0 -0
- /package/{bump3.txt → assertions/bump3.txt} +0 -0
- /package/{jest.config.js → assertions/jest.config.js} +0 -0
- /package/{CHANGELOG.md → core/CHANGELOG.md} +0 -0
- /package/{README.md → core/README.md} +0 -0
- /package/{load-installed-modules.js → core/load-installed-modules.js} +0 -0
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
const bcrypt = require('bcryptjs');
|
|
2
|
+
const crypto = require('crypto');
|
|
3
|
+
const { get } = require('../assertions');
|
|
4
|
+
const { Token } = require('../database/models/Token');
|
|
5
|
+
const { IndividualUser } = require('../database/models/IndividualUser');
|
|
6
|
+
const { OrganizationUser } = require('../database/models/OrganizationUser');
|
|
7
|
+
const Boom = require('@hapi/boom');
|
|
8
|
+
|
|
9
|
+
class User {
|
|
10
|
+
static IndividualUser = IndividualUser;
|
|
11
|
+
static OrganizationUser = OrganizationUser;
|
|
12
|
+
static Token = Token;
|
|
13
|
+
static usePassword = false
|
|
14
|
+
static primary = User.IndividualUser;
|
|
15
|
+
static individualUserRequired = true;
|
|
16
|
+
static organizationUserRequired = false;
|
|
17
|
+
|
|
18
|
+
constructor() {
|
|
19
|
+
this.user = null;
|
|
20
|
+
this.individualUser = null;
|
|
21
|
+
this.organizationUser = null;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
getPrimaryUser() {
|
|
25
|
+
if (User.primary === User.OrganizationUser) {
|
|
26
|
+
return this.organizationUser;
|
|
27
|
+
}
|
|
28
|
+
return this.individualUser;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
getUserId() {
|
|
32
|
+
return this.getPrimaryUser()?.id;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
isLoggedIn() {
|
|
36
|
+
return Boolean(this.getUserId());
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async createUserToken(minutes) {
|
|
40
|
+
const rawToken = crypto.randomBytes(20).toString('hex');
|
|
41
|
+
const createdToken = await User.Token.createTokenWithExpire(this.getUserId(), rawToken, 120);
|
|
42
|
+
const tokenBuf = User.Token.createBase64BufferToken(createdToken, rawToken);
|
|
43
|
+
return tokenBuf;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
static async newUser(params={}) {
|
|
47
|
+
const user = new User();
|
|
48
|
+
const token = get(params, 'token', null);
|
|
49
|
+
if (token) {
|
|
50
|
+
const jsonToken = this.Token.getJSONTokenFromBase64BufferToken(token);
|
|
51
|
+
const sessionToken = await this.Token.validateAndGetTokenFromJSONToken(jsonToken);
|
|
52
|
+
if (this.primary === User.OrganizationUser) {
|
|
53
|
+
user.organizationUser = await this.OrganizationUser.findById(sessionToken.user);
|
|
54
|
+
} else {
|
|
55
|
+
user.individualUser = await this.IndividualUser.findById(sessionToken.user);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return user;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
static async createIndividualUser(params) {
|
|
62
|
+
const user = await this.newUser(params);
|
|
63
|
+
let hashword;
|
|
64
|
+
if (this.usePassword) {
|
|
65
|
+
hashword = get(params, 'password');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const email = get(params, 'email', null);
|
|
69
|
+
const username = get(params, 'username', null);
|
|
70
|
+
if (!email && !username) {
|
|
71
|
+
throw Boom.badRequest('email or username is required');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const appUserId = get(params, 'appUserId', null);
|
|
75
|
+
const organizationUserId = get(params, 'organizationUserId', null);
|
|
76
|
+
|
|
77
|
+
user.individualUser = await this.IndividualUser.create({
|
|
78
|
+
email,
|
|
79
|
+
username,
|
|
80
|
+
hashword,
|
|
81
|
+
appUserId,
|
|
82
|
+
organizationUser: organizationUserId,
|
|
83
|
+
});
|
|
84
|
+
return user;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
static async createOrganizationUser(params) {
|
|
88
|
+
const user = await this.newUser(params);
|
|
89
|
+
const name = get(params, 'name');
|
|
90
|
+
const appOrgId = get(params, 'appOrgId');
|
|
91
|
+
user.organizationUser = await this.OrganizationUser.create({
|
|
92
|
+
name,
|
|
93
|
+
appOrgId,
|
|
94
|
+
});
|
|
95
|
+
return user;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
static async loginUser(params) {
|
|
99
|
+
const user = await this.newUser(params);
|
|
100
|
+
|
|
101
|
+
if (this.usePassword){
|
|
102
|
+
const username = get(params, 'username');
|
|
103
|
+
const password = get(params, 'password');
|
|
104
|
+
|
|
105
|
+
const individualUser = await this.IndividualUser.findOne({username});
|
|
106
|
+
|
|
107
|
+
if (!individualUser) {
|
|
108
|
+
throw Boom.unauthorized('incorrect username or password');
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const isValid = await bcrypt.compareSync(password, individualUser.hashword);
|
|
112
|
+
if (!isValid) {
|
|
113
|
+
throw Boom.unauthorized('incorrect username or password');
|
|
114
|
+
}
|
|
115
|
+
user.individualUser = individualUser;
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
const appUserId = get(params, 'appUserId', null);
|
|
119
|
+
user.individualUser = await this.IndividualUser.getUserByAppUserId(
|
|
120
|
+
appUserId
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const appOrgId = get(params, 'appOrgId', null);
|
|
125
|
+
user.organizationUser = await this.OrganizationUser.getUserByAppOrgId(
|
|
126
|
+
appOrgId
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
if (this.individualUserRequired) {
|
|
130
|
+
if (!user.individualUser) {
|
|
131
|
+
throw Boom.unauthorized('user not found');
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (this.organizationUserRequired) {
|
|
136
|
+
if (!user.organizationUser) {
|
|
137
|
+
throw Boom.unauthorized(`org user ${appOrgId} not found`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return user;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
module.exports = User;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* For a detailed explanation regarding each configuration property, visit:
|
|
3
|
+
* https://jestjs.io/docs/configuration
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
module.exports = {
|
|
7
|
+
// A path to a module which exports an async function that is triggered once before all test suites
|
|
8
|
+
globalSetup: './jest-setup.js',
|
|
9
|
+
|
|
10
|
+
// A path to a module which exports an async function that is triggered once after all test suites
|
|
11
|
+
globalTeardown: './jest-teardown.js',
|
|
12
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
const { RequiredPropertyError } = require('../errors');
|
|
2
|
+
const { get, getAndVerifyType } = require('../assertions');
|
|
3
|
+
const { ModuleManager } = require('../module-plugin');
|
|
4
|
+
|
|
5
|
+
class Options {
|
|
6
|
+
constructor(params) {
|
|
7
|
+
this.module = get(params, 'module');
|
|
8
|
+
this.isMany = Boolean(get(params, 'isMany', false));
|
|
9
|
+
this.hasUserConfig = Boolean(get(params, 'hasUserConfig', false));
|
|
10
|
+
this.requiresNewEntity = Boolean(
|
|
11
|
+
get(params, 'requiresNewEntity', false)
|
|
12
|
+
);
|
|
13
|
+
if (!params.display) {
|
|
14
|
+
throw new RequiredPropertyError({
|
|
15
|
+
parent: this,
|
|
16
|
+
key: 'display',
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
this.display = {};
|
|
21
|
+
this.display.name = get(params.display, 'name');
|
|
22
|
+
this.display.description = get(params.display, 'description');
|
|
23
|
+
this.display.detailsUrl = get(params.display, 'detailsUrl');
|
|
24
|
+
this.display.icon = get(params.display, 'icon');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
get() {
|
|
28
|
+
return {
|
|
29
|
+
type: this.module.getName(),
|
|
30
|
+
|
|
31
|
+
// Flag for if the User can configure any settings
|
|
32
|
+
hasUserConfig: this.hasUserConfig,
|
|
33
|
+
|
|
34
|
+
// if this integration can be used multiple times with the same integration pair. For example I want to
|
|
35
|
+
// connect two different Etsy shops to the same Freshbooks account.
|
|
36
|
+
isMany: this.isMany,
|
|
37
|
+
|
|
38
|
+
// if this is true it means we need to create a new entity for every integration pair and not use an
|
|
39
|
+
// existing one. This would be true for scenarios where the client wishes to have individual control over
|
|
40
|
+
// the integerations it has connected to its app. They would want this to let their users only delete
|
|
41
|
+
// single integrations without notifying our server.
|
|
42
|
+
requiresNewEntity: this.requiresNewEntity,
|
|
43
|
+
|
|
44
|
+
// this is information required for the display side of things on the front end
|
|
45
|
+
display: this.display,
|
|
46
|
+
|
|
47
|
+
// this is information for post-authentication config, using jsonSchema and uiSchema for display on the frontend
|
|
48
|
+
// Maybe include but probably not, I like making someone make a follow-on request
|
|
49
|
+
// configOptions: this.configOptions,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
module.exports = { Options };
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
const _ = require('lodash');
|
|
2
|
+
const { mongoose } = require('../../database/mongoose');
|
|
3
|
+
const { expect } = require('chai');
|
|
4
|
+
const { IntegrationBase } = require("../integration-base");
|
|
5
|
+
const {Credential, Entity} = require('../../module-plugin');
|
|
6
|
+
const { IntegrationMapping } = require('../integration-mapping')
|
|
7
|
+
const {IntegrationModel} = require("../integration-model");
|
|
8
|
+
|
|
9
|
+
describe(`Should fully test the IntegrationBase Class`, () => {
|
|
10
|
+
let integrationRecord;
|
|
11
|
+
let userId;
|
|
12
|
+
const integration = new IntegrationBase;
|
|
13
|
+
|
|
14
|
+
beforeAll(async () => {
|
|
15
|
+
await mongoose.connect(process.env.MONGO_URI);
|
|
16
|
+
userId = new mongoose.Types.ObjectId();
|
|
17
|
+
const credential = await Credential.findOneAndUpdate(
|
|
18
|
+
{
|
|
19
|
+
user: this.userId,
|
|
20
|
+
},
|
|
21
|
+
{ $set: { user: this.userId } },
|
|
22
|
+
{
|
|
23
|
+
new: true,
|
|
24
|
+
upsert: true,
|
|
25
|
+
setDefaultsOnInsert: true,
|
|
26
|
+
}
|
|
27
|
+
);
|
|
28
|
+
const entity1 = await Entity.findOneAndUpdate(
|
|
29
|
+
{
|
|
30
|
+
user: this.userId,
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
$set: {
|
|
34
|
+
credential: credential.id,
|
|
35
|
+
user: userId,
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
new: true,
|
|
40
|
+
upsert: true,
|
|
41
|
+
setDefaultsOnInsert: true,
|
|
42
|
+
}
|
|
43
|
+
);
|
|
44
|
+
const entity2 = await Entity.findOneAndUpdate(
|
|
45
|
+
{
|
|
46
|
+
user: userId,
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
$set: {
|
|
50
|
+
credential: credential.id,
|
|
51
|
+
user: userId,
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
new: true,
|
|
56
|
+
upsert: true,
|
|
57
|
+
setDefaultsOnInsert: true,
|
|
58
|
+
}
|
|
59
|
+
);
|
|
60
|
+
integrationRecord = await IntegrationModel.create({
|
|
61
|
+
entities: [entity1, entity2],
|
|
62
|
+
user: userId
|
|
63
|
+
});
|
|
64
|
+
integration.record = integrationRecord;
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
afterAll(async () => {
|
|
68
|
+
await Entity.deleteMany();
|
|
69
|
+
await Credential.deleteMany();
|
|
70
|
+
await IntegrationMapping.deleteMany();
|
|
71
|
+
await IntegrationModel.deleteMany();
|
|
72
|
+
await mongoose.disconnect();
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
beforeEach(() => {
|
|
76
|
+
integration.record = integrationRecord;
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
describe('getIntegrationMapping()', () => {
|
|
80
|
+
it('should return null if not found', async () => {
|
|
81
|
+
const mappings = await integration.getMapping('badId');
|
|
82
|
+
expect(mappings).to.be.null;
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('should return if valid ids', async () => {
|
|
86
|
+
await integration.upsertMapping('validId', {});
|
|
87
|
+
const mapping = await integration.getMapping('validId');
|
|
88
|
+
expect(mapping).to.eql({})
|
|
89
|
+
});
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
describe('upsertIntegrationMapping()', () => {
|
|
93
|
+
it('should throw error if sourceId is null', async () => {
|
|
94
|
+
try {
|
|
95
|
+
await integration.upsertMapping( null, {});
|
|
96
|
+
fail('should have thrown error')
|
|
97
|
+
} catch(err) {
|
|
98
|
+
expect(err.message).to.contain('sourceId must be set');
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('should return for empty mapping', async () => {
|
|
103
|
+
const mapping = await integration.upsertMapping( 'validId2', {});
|
|
104
|
+
expect(_.pick(mapping, ['integration', 'sourceId', 'mapping'])).to.eql({
|
|
105
|
+
integration: integrationRecord._id,
|
|
106
|
+
sourceId: 'validId2',
|
|
107
|
+
mapping: {}
|
|
108
|
+
})
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it('should return for filled mapping', async () => {
|
|
112
|
+
const mapping = await integration.upsertMapping('validId3', {
|
|
113
|
+
name: 'someName',
|
|
114
|
+
value: 5
|
|
115
|
+
});
|
|
116
|
+
expect(_.pick(mapping, ['integration', 'sourceId', 'mapping'])).to.eql({
|
|
117
|
+
integration: integrationRecord._id,
|
|
118
|
+
sourceId: 'validId3',
|
|
119
|
+
mapping: {
|
|
120
|
+
name: 'someName',
|
|
121
|
+
value: 5
|
|
122
|
+
}
|
|
123
|
+
})
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it('should allow upserting to same id', async () => {
|
|
127
|
+
await integration.upsertMapping('validId4', {});
|
|
128
|
+
const mapping = await integration.upsertMapping('validId4', {
|
|
129
|
+
name: 'trustMe',
|
|
130
|
+
thisWorks: true,
|
|
131
|
+
});
|
|
132
|
+
expect(_.pick(mapping, ['integration', 'sourceId', 'mapping'])).to.eql({
|
|
133
|
+
integration: integrationRecord._id,
|
|
134
|
+
sourceId: 'validId4',
|
|
135
|
+
mapping: {
|
|
136
|
+
name: 'trustMe',
|
|
137
|
+
thisWorks: true,
|
|
138
|
+
}
|
|
139
|
+
})
|
|
140
|
+
});
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
});
|
package/lambda/README.md
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
const isPositive = (n) => Number.isFinite(n) && n > 0;
|
|
2
|
+
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
3
|
+
|
|
4
|
+
class TimeoutCatcher {
|
|
5
|
+
constructor({ work, timeout, cleanUp = () => {}, cleanUpTime = 2_000 }) {
|
|
6
|
+
this.isFinished = false;
|
|
7
|
+
this.work = work;
|
|
8
|
+
this.cleanUp = cleanUp;
|
|
9
|
+
this.waitTime = timeout - cleanUpTime;
|
|
10
|
+
|
|
11
|
+
if (!isPositive(this.waitTime))
|
|
12
|
+
throw new Error("Wait time was not a positive number of milliseconds");
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async watch() {
|
|
16
|
+
try {
|
|
17
|
+
await Promise.race([this.doWork(), this.exitBeforeTimeout()]);
|
|
18
|
+
return true;
|
|
19
|
+
} catch (error) {
|
|
20
|
+
if (error.isSentinelTimeout) return false;
|
|
21
|
+
throw error;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async doWork() {
|
|
26
|
+
await this.work();
|
|
27
|
+
this.isFinished = true;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async exitBeforeTimeout() {
|
|
31
|
+
await sleep(this.waitTime);
|
|
32
|
+
|
|
33
|
+
if (!this.isFinished) {
|
|
34
|
+
await this.cleanUp();
|
|
35
|
+
|
|
36
|
+
const error = new Error("Sentinel Timed Out");
|
|
37
|
+
error.isSentinelTimeout = true;
|
|
38
|
+
throw error;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
module.exports = { TimeoutCatcher };
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
const { TimeoutCatcher } = require("./TimeoutCatcher");
|
|
2
|
+
|
|
3
|
+
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
4
|
+
|
|
5
|
+
describe("Time Sentinel", () => {
|
|
6
|
+
it("requires a positive wait time", () => {
|
|
7
|
+
try {
|
|
8
|
+
new TimeoutCatcher({
|
|
9
|
+
timeout: 1_000,
|
|
10
|
+
});
|
|
11
|
+
throw new Error("Expected error was not thrown");
|
|
12
|
+
} catch (error) {
|
|
13
|
+
expect(error).toHaveProperty(
|
|
14
|
+
"message",
|
|
15
|
+
"Wait time was not a positive number of milliseconds"
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("exits normally if the work is completed", async () => {
|
|
21
|
+
let cleanUpCalled = false;
|
|
22
|
+
const sentinel = new TimeoutCatcher({
|
|
23
|
+
cleanUp: () => (cleanUpCalled = true),
|
|
24
|
+
timeout: 3_000,
|
|
25
|
+
work: async () => {
|
|
26
|
+
await sleep(500);
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
const didFinish = await sentinel.watch();
|
|
31
|
+
expect(didFinish).toEqual(true);
|
|
32
|
+
expect(cleanUpCalled).toEqual(false);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it("terminates before time out if work runs long", async () => {
|
|
36
|
+
let cleanUpCalled = false;
|
|
37
|
+
const sentinel = new TimeoutCatcher({
|
|
38
|
+
cleanUp: () => (cleanUpCalled = true),
|
|
39
|
+
timeout: 3_000,
|
|
40
|
+
work: async () => {
|
|
41
|
+
await sleep(1_500);
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
const didFinish = await sentinel.watch();
|
|
46
|
+
expect(didFinish).toEqual(false);
|
|
47
|
+
expect(cleanUpCalled).toEqual(true);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it("rethrows unexpected errors", async () => {
|
|
51
|
+
let cleanUpCalled = false;
|
|
52
|
+
const sentinel = new TimeoutCatcher({
|
|
53
|
+
cleanUp: () => (cleanUpCalled = true),
|
|
54
|
+
timeout: 3_000,
|
|
55
|
+
work: async () => {
|
|
56
|
+
throw new Error("Spam spam spam spam");
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
try {
|
|
61
|
+
await sentinel.watch();
|
|
62
|
+
throw new Error("Expected error did not occur");
|
|
63
|
+
} catch (error) {
|
|
64
|
+
expect(error).toHaveProperty("message", "Spam spam spam spam");
|
|
65
|
+
expect(cleanUpCalled).toEqual(false);
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
});
|
package/lambda/bump3.txt
ADDED
|
File without changes
|
package/lambda/index.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# v1.0.9 (Tue Jan 31 2023)
|
|
2
|
+
|
|
3
|
+
#### 🐛 Bug Fix
|
|
4
|
+
|
|
5
|
+
- Bump independent versions \[skip ci\] ([@seanspeaks](https://github.com/seanspeaks))
|
|
6
|
+
|
|
7
|
+
#### Authors: 1
|
|
8
|
+
|
|
9
|
+
- Sean Matthews ([@seanspeaks](https://github.com/seanspeaks))
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# v1.0.8 (Mon Jan 09 2023)
|
|
14
|
+
|
|
15
|
+
#### 🐛 Bug Fix
|
|
16
|
+
|
|
17
|
+
- Merge remote-tracking branch 'origin/main' into gitbook-updates [#48](https://github.com/friggframework/frigg/pull/48) ([@seanspeaks](https://github.com/seanspeaks))
|
|
18
|
+
- Bump independent versions \[skip ci\] ([@seanspeaks](https://github.com/seanspeaks))
|
|
19
|
+
- Merge remote-tracking branch 'origin/main' into simplify-mongoose-models ([@seanspeaks](https://github.com/seanspeaks))
|
|
20
|
+
- Add READMEs for all packages and api-modules [#20](https://github.com/friggframework/frigg/pull/20) ([@seanspeaks](https://github.com/seanspeaks))
|
|
21
|
+
- Add READMEs for all packages and api-modules ([@seanspeaks](https://github.com/seanspeaks))
|
|
22
|
+
|
|
23
|
+
#### ⚠️ Pushed to `main`
|
|
24
|
+
|
|
25
|
+
- Merge branch 'main' into gitbook-updates ([@seanspeaks](https://github.com/seanspeaks))
|
|
26
|
+
- Refactored for more conventional naming (at least for packages) ([@seanspeaks](https://github.com/seanspeaks))
|
|
27
|
+
|
|
28
|
+
#### Authors: 1
|
|
29
|
+
|
|
30
|
+
- Sean Matthews ([@seanspeaks](https://github.com/seanspeaks))
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
# v1.0.7 (Tue Dec 06 2022)
|
|
35
|
+
|
|
36
|
+
#### 🐛 Bug Fix
|
|
37
|
+
|
|
38
|
+
- fix modules to @friggframework [#74](https://github.com/friggframework/frigg/pull/74) ([@sheehantoufiq](https://github.com/sheehantoufiq))
|
|
39
|
+
- fix modules to @friggframework ([@sheehantoufiq](https://github.com/sheehantoufiq))
|
|
40
|
+
- Bump independent versions \[skip ci\] ([@seanspeaks](https://github.com/seanspeaks))
|
|
41
|
+
|
|
42
|
+
#### Authors: 2
|
|
43
|
+
|
|
44
|
+
- Sean Matthews ([@seanspeaks](https://github.com/seanspeaks))
|
|
45
|
+
- Sheehan Toufiq Khan ([@sheehantoufiq](https://github.com/sheehantoufiq))
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
# v1.0.6 (Mon Sep 19 2022)
|
|
50
|
+
|
|
51
|
+
#### 🐛 Bug Fix
|
|
52
|
+
|
|
53
|
+
- Merge remote-tracking branch 'origin/main' into gitbook-updates [#48](https://github.com/friggframework/frigg/pull/48) ([@seanspeaks](https://github.com/seanspeaks))
|
|
54
|
+
|
|
55
|
+
#### Authors: 1
|
|
56
|
+
|
|
57
|
+
- Sean Matthews ([@seanspeaks](https://github.com/seanspeaks))
|
package/logs/LICENSE.md
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2022 Left Hook Inc.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
6
|
+
|
|
7
|
+
The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/logs/README.md
ADDED
package/logs/bump3.txt
ADDED
|
File without changes
|
package/logs/index.js
ADDED
package/logs/logger.js
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
const util = require('util');
|
|
2
|
+
const aws = require('aws-sdk');
|
|
3
|
+
|
|
4
|
+
// Except in some outlier circumstances, for example steam or event error handlers, this should be the only place that calls `console.*`. That way, this file can be modified to log everything properly on a variety of platforms because all the logging code is here in one place.
|
|
5
|
+
/* eslint-disable no-console */
|
|
6
|
+
|
|
7
|
+
const logs = [];
|
|
8
|
+
let flushCalled = false;
|
|
9
|
+
|
|
10
|
+
// Log AWS SDK calls
|
|
11
|
+
aws.config.logger = { log: debug };
|
|
12
|
+
|
|
13
|
+
function debug(...messages) {
|
|
14
|
+
if (messages.length) {
|
|
15
|
+
const date = new Date();
|
|
16
|
+
const text = util.format.apply(null, messages);
|
|
17
|
+
|
|
18
|
+
if (process.env.DEBUG_VERBOSE === '1') {
|
|
19
|
+
console.debug(date, text);
|
|
20
|
+
} else {
|
|
21
|
+
logs.push({ date, text });
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function initDebugLog(...initMessages) {
|
|
27
|
+
flushCalled = false;
|
|
28
|
+
|
|
29
|
+
// Hacky but fast way to empty an array.
|
|
30
|
+
logs.length = 0;
|
|
31
|
+
|
|
32
|
+
// Log initial event
|
|
33
|
+
debug(...initMessages);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function flushDebugLog(error) {
|
|
37
|
+
if (flushCalled) {
|
|
38
|
+
console.debug(
|
|
39
|
+
'Another error was encountered while handling the same request or event! All debug messages are included again in this output as well.'
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
flushCalled = true;
|
|
44
|
+
|
|
45
|
+
// Output unless in verbose mode. In verbose mode, these will already have been output so we don't want to output the messages twice.
|
|
46
|
+
if (process.env.DEBUG_VERBOSE !== '1') {
|
|
47
|
+
if (logs?.length > 0) {
|
|
48
|
+
for (const { date, text } of logs) {
|
|
49
|
+
console.debug(date, text);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (!error) {
|
|
55
|
+
error = new Error('flushDebugLog called with empty error');
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
console.error(error);
|
|
59
|
+
|
|
60
|
+
let { cause: parentError } = error;
|
|
61
|
+
|
|
62
|
+
while (parentError) {
|
|
63
|
+
console.error('(Caused By)-------------------------');
|
|
64
|
+
console.error(parentError);
|
|
65
|
+
parentError = parentError.cause;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
module.exports = { debug, initDebugLog, flushDebugLog };
|