@friggframework/core 2.0.0--canary.454.cbce887.0 → 2.0.0--canary.454.dc05b2f.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.
@@ -0,0 +1,78 @@
1
+ const md5 = require("md5");
2
+ const { get } = require('../assertions');
3
+
4
+ /**
5
+ * @file This file is meant to be the thing that enforces proper use of
6
+ * the Association model.
7
+ * For now, we're going to use the model directly and worry about proper use
8
+ * later...
9
+ */
10
+ class Association {
11
+ static Config = {
12
+ name: "Association",
13
+
14
+ reverseModuleMap: {},
15
+ };
16
+ constructor(params) {
17
+ this.data = {};
18
+
19
+ let data = get(params, "data");
20
+ this.moduleName = get(params, "moduleName");
21
+ this.dataIdentifier = get(params, "dataIdentifier");
22
+
23
+ this.dataIdentifierHash = this.constructor.hashJSON(this.dataIdentifier);
24
+
25
+ for (let key of this.constructor.Config.keys) {
26
+ this.data[key] =
27
+ this.constructor.Config.moduleMap[this.moduleName][key](data);
28
+ }
29
+
30
+ // matchHash is used to find matches between two sync objects
31
+ let matchHashData = [];
32
+ for (let key of this.constructor.Config.matchOn) {
33
+ matchHashData.push(this.data[key]);
34
+ }
35
+ this.matchHash = this.constructor.hashJSON(matchHashData);
36
+
37
+ this.syncId = null;
38
+ }
39
+
40
+ equals(syncObj) {
41
+ return this.matchHash === syncObj.matchHash;
42
+ }
43
+ dataKeyIsReplaceable(key) {
44
+ return this.data[key] === null || this.data[key] === "";
45
+ }
46
+
47
+ isModuleInMap(moduleName) {
48
+ return this.constructor.Config.moduleMap[name];
49
+ }
50
+
51
+ getName() {
52
+ return this.name;
53
+ }
54
+
55
+ getHashData() {
56
+ let orderedData = [];
57
+ for (let key of this.constructor.Config.keys) {
58
+ orderedData.push(this.data[key]);
59
+ }
60
+
61
+ return this.constructor.hashJSON(orderedData);
62
+ }
63
+
64
+ setSyncId(syncId) {
65
+ this.syncId = syncId;
66
+ }
67
+
68
+ reverseModuleMap(moduleName) {
69
+ return this.constructor.Config.reverseModuleMap[moduleName](this.data);
70
+ }
71
+
72
+ static hashJSON(data) {
73
+ let dataString = JSON.stringify(data, null, 2);
74
+ return md5(dataString);
75
+ }
76
+ }
77
+
78
+ module.exports = Association;
@@ -0,0 +1,54 @@
1
+ const mongoose = require("mongoose");
2
+
3
+ const schema = new mongoose.Schema({
4
+ integration: {
5
+ type: mongoose.Schema.Types.ObjectId,
6
+ ref: "Integration",
7
+ required: true,
8
+ },
9
+ name: { type: String, required: true },
10
+ type: {
11
+ type: String,
12
+ enum: ["ONE_TO_MANY", "ONE_TO_ONE", "MANY_TO_ONE"],
13
+ required: true,
14
+ },
15
+ primaryObject: { type: String, required: true },
16
+ objects: [
17
+ {
18
+ entity: {
19
+ type: mongoose.Schema.Types.ObjectId,
20
+ ref: "Entity",
21
+ required: true,
22
+ },
23
+ objectType: { type: String, required: true },
24
+ objId: { type: String, required: true },
25
+ metadata: { type: Object, required: false },
26
+ },
27
+ ],
28
+ });
29
+
30
+ schema.statics({
31
+ addAssociation: async function (id, object) {
32
+ return this.update({ _id: id }, { $push: { objects: object } });
33
+ },
34
+ findAssociation: async function (name, dataIdentifierHash) {
35
+ const syncList = await this.list({
36
+ name: name,
37
+ "dataIdentifiers.hash": dataIdentifierHash,
38
+ });
39
+
40
+ if (syncList.length === 1) {
41
+ return syncList[0];
42
+ } else if (syncList.length === 0) {
43
+ return null;
44
+ } else {
45
+ throw new Error(
46
+ `there are multiple sync objects with the name ${name}, for entities [${entities}]`
47
+ );
48
+ }
49
+ },
50
+ });
51
+
52
+ const Association =
53
+ mongoose.models.Association || mongoose.model("Association", schema);
54
+ module.exports = { Association };
@@ -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,3 @@
1
+ const { TimeoutCatcher } = require('./TimeoutCatcher');
2
+
3
+ module.exports = { TimeoutCatcher }
package/package.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "@friggframework/core",
3
3
  "prettier": "@friggframework/prettier-config",
4
- "version": "2.0.0--canary.454.cbce887.0",
4
+ "version": "2.0.0--canary.454.dc05b2f.0",
5
5
  "files": [
6
6
  "*.js",
7
7
  "*.d.ts",
8
8
  "application/",
9
9
  "assertions/",
10
+ "associations/",
10
11
  "core/",
11
12
  "credential/",
12
13
  "database/",
@@ -14,12 +15,15 @@
14
15
  "errors/",
15
16
  "handlers/",
16
17
  "integrations/",
18
+ "lambda/",
17
19
  "logs/",
18
20
  "modules/",
21
+ "queues/",
19
22
  "syncs/",
20
23
  "token/",
21
24
  "types/",
22
25
  "user/",
26
+ "utils/",
23
27
  "websocket/",
24
28
  "prisma-mongodb/",
25
29
  "prisma-postgresql/",
@@ -49,9 +53,9 @@
49
53
  "uuid": "^9.0.1"
50
54
  },
51
55
  "devDependencies": {
52
- "@friggframework/eslint-config": "2.0.0--canary.454.cbce887.0",
53
- "@friggframework/prettier-config": "2.0.0--canary.454.cbce887.0",
54
- "@friggframework/test": "2.0.0--canary.454.cbce887.0",
56
+ "@friggframework/eslint-config": "2.0.0--canary.454.dc05b2f.0",
57
+ "@friggframework/prettier-config": "2.0.0--canary.454.dc05b2f.0",
58
+ "@friggframework/test": "2.0.0--canary.454.dc05b2f.0",
55
59
  "@types/lodash": "4.17.15",
56
60
  "@typescript-eslint/eslint-plugin": "^8.0.0",
57
61
  "chai": "^4.3.6",
@@ -89,5 +93,5 @@
89
93
  "publishConfig": {
90
94
  "access": "public"
91
95
  },
92
- "gitHead": "cbce8872aea93a375c1088a0dcb8d05a5d48d8b7"
96
+ "gitHead": "dc05b2f560edc9f63630fd4e71fe3c57894dff8e"
93
97
  }
@@ -0,0 +1,4 @@
1
+ const { QueuerUtil } = require('./queuer-util');
2
+ module.exports = {
3
+ QueuerUtil,
4
+ };
@@ -0,0 +1,61 @@
1
+ const { v4: uuid } = require('uuid');
2
+ const AWS = require('aws-sdk');
3
+ const awsConfigOptions = () => {
4
+ const config = {};
5
+ if (process.env.IS_OFFLINE) {
6
+ console.log('Running in offline mode');
7
+ }
8
+ if (process.env.AWS_ENDPOINT) {
9
+ config.endpoint = process.env.AWS_ENDPOINT;
10
+ }
11
+ return config;
12
+ };
13
+ AWS.config.update(awsConfigOptions());
14
+ const sqs = new AWS.SQS();
15
+
16
+ const QueuerUtil = {
17
+ batchSend: async (entries = [], queueUrl) => {
18
+ console.log(
19
+ `Enqueuing ${entries.length} entries on SQS to queue ${queueUrl}`
20
+ );
21
+ const buffer = [];
22
+ const batchSize = 10;
23
+
24
+ for (const entry of entries) {
25
+ buffer.push({
26
+ Id: uuid(),
27
+ MessageBody: JSON.stringify(entry),
28
+ });
29
+ // Sends 10, then purges the buffer
30
+ if (buffer.length === batchSize) {
31
+ console.log('Buffer at 10, sending batch');
32
+ await sqs
33
+ .sendMessageBatch({
34
+ Entries: buffer,
35
+ QueueUrl: queueUrl,
36
+ })
37
+ .promise();
38
+ // Purge the buffer
39
+ buffer.splice(0, buffer.length);
40
+ }
41
+ }
42
+ console.log('Buffer at end, sending final batch');
43
+
44
+ // If any remaining entries under 10 are left in the buffer, send and return
45
+ if (buffer.length > 0) {
46
+ console.log(buffer);
47
+ return sqs
48
+ .sendMessageBatch({
49
+ Entries: buffer,
50
+ QueueUrl: queueUrl,
51
+ })
52
+ .promise();
53
+ }
54
+
55
+ // If we're exact... just return an empty object for now
56
+
57
+ return {};
58
+ },
59
+ };
60
+
61
+ module.exports = { QueuerUtil };
@@ -0,0 +1,38 @@
1
+ const fs = require('fs-extra');
2
+ const path = require('node:path');
3
+ const PACKAGE_JSON = 'package.json';
4
+
5
+ function findNearestBackendPackageJson() {
6
+ let currentDir = process.cwd();
7
+
8
+ // First check if we're in production by looking for package.json in the current directory
9
+ const rootPackageJson = path.join(currentDir, PACKAGE_JSON);
10
+ if (fs.existsSync(rootPackageJson)) {
11
+ // In production environment, check for index.js in the same directory
12
+ const indexJs = path.join(currentDir, 'index.js');
13
+ if (fs.existsSync(indexJs)) {
14
+ return rootPackageJson;
15
+ }
16
+ }
17
+
18
+ // If not found at root or not in production, look for it in the backend directory
19
+ while (currentDir !== path.parse(currentDir).root) {
20
+ const packageJsonPath = path.join(currentDir, 'backend', PACKAGE_JSON);
21
+ if (fs.existsSync(packageJsonPath)) {
22
+ return packageJsonPath;
23
+ }
24
+ currentDir = path.dirname(currentDir);
25
+ }
26
+ return null;
27
+ }
28
+
29
+ function validateBackendPath(backendPath) {
30
+ if (!backendPath) {
31
+ throw new Error('Could not find a backend package.json file.');
32
+ }
33
+ }
34
+
35
+ module.exports = {
36
+ findNearestBackendPackageJson,
37
+ validateBackendPath,
38
+ };
package/utils/index.js ADDED
@@ -0,0 +1,6 @@
1
+ const { findNearestBackendPackageJson, validateBackendPath } = require('./backend-path');
2
+
3
+ module.exports = {
4
+ findNearestBackendPackageJson,
5
+ validateBackendPath,
6
+ };