@friggframework/core 0.2.31-v1-alpha.6 → 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.
Files changed (193) hide show
  1. package/assertions/CHANGELOG.md +87 -0
  2. package/assertions/LICENSE.md +9 -0
  3. package/assertions/README.md +3 -0
  4. package/assertions/bump.txt +1 -0
  5. package/assertions/get.js +139 -0
  6. package/assertions/index.js +19 -0
  7. package/assertions/is-equal.js +17 -0
  8. package/associations/LICENSE.md +9 -0
  9. package/associations/README.md +3 -0
  10. package/associations/association.js +78 -0
  11. package/associations/bump3.txt +0 -0
  12. package/associations/jest.config.js +5 -0
  13. package/associations/model.js +54 -0
  14. package/bump.txt +0 -0
  15. package/core/.eslintrc.json +3 -0
  16. package/{Delegate.js → core/Delegate.js} +1 -1
  17. package/core/LICENSE.md +9 -0
  18. package/{Worker.js → core/Worker.js} +2 -2
  19. package/core/bump3.txt +0 -0
  20. package/{create-handler.js → core/create-handler.js} +2 -2
  21. package/core/index.js +6 -0
  22. package/core/jest.config.js +5 -0
  23. package/database/.eslintrc.json +3 -0
  24. package/database/CHANGELOG.md +97 -0
  25. package/database/LICENSE.md +9 -0
  26. package/database/README.md +3 -0
  27. package/database/bump3.txt +0 -0
  28. package/database/index.js +17 -0
  29. package/database/jest.config.js +5 -0
  30. package/database/models/IndividualUser.js +76 -0
  31. package/database/models/OrganizationUser.js +29 -0
  32. package/database/models/State.js +9 -0
  33. package/database/models/Token.js +70 -0
  34. package/database/models/UserModel.js +7 -0
  35. package/database/mongo.js +45 -0
  36. package/database/mongoose.js +5 -0
  37. package/encrypt/.eslintrc.json +3 -0
  38. package/encrypt/CHANGELOG.md +65 -0
  39. package/encrypt/Cryptor.js +236 -0
  40. package/encrypt/Cryptor.test.js +32 -0
  41. package/encrypt/LICENSE.md +9 -0
  42. package/encrypt/README.md +3 -0
  43. package/encrypt/aes.js +27 -0
  44. package/encrypt/bump3.txt +0 -0
  45. package/encrypt/encrypt.js +124 -0
  46. package/encrypt/encrypt.test.js +1068 -0
  47. package/encrypt/index.js +3 -0
  48. package/encrypt/jest.config.js +5 -0
  49. package/encrypt/test-encrypt.js +107 -0
  50. package/errors/.eslintrc.json +3 -0
  51. package/errors/CHANGELOG.md +44 -0
  52. package/errors/LICENSE.md +9 -0
  53. package/errors/README.md +3 -0
  54. package/errors/base-error.js +23 -0
  55. package/errors/base-error.test.js +32 -0
  56. package/errors/bump.txt +1 -0
  57. package/errors/bump3.txt +0 -0
  58. package/errors/fetch-error.js +72 -0
  59. package/errors/fetch-error.test.js +79 -0
  60. package/errors/halt-error.js +10 -0
  61. package/errors/halt-error.test.js +11 -0
  62. package/errors/index.js +15 -0
  63. package/errors/jest.config.js +5 -0
  64. package/errors/validation-errors.js +23 -0
  65. package/errors/validation-errors.test.js +120 -0
  66. package/eslint-config/.eslintrc.json +3 -0
  67. package/eslint-config/CHANGELOG.md +17 -0
  68. package/eslint-config/LICENSE.md +9 -0
  69. package/eslint-config/README.md +3 -0
  70. package/eslint-config/bump3.txt +0 -0
  71. package/eslint-config/index.js +38 -0
  72. package/index.js +29 -5
  73. package/integrations/.eslintrc.json +3 -0
  74. package/integrations/CHANGELOG.md +244 -0
  75. package/integrations/LICENSE.md +9 -0
  76. package/integrations/README.md +3 -0
  77. package/integrations/bump3.txt +0 -0
  78. package/integrations/create-frigg-backend.js +31 -0
  79. package/integrations/index.js +19 -0
  80. package/integrations/integration-base.js +162 -0
  81. package/integrations/integration-factory.js +166 -0
  82. package/integrations/integration-mapping.js +43 -0
  83. package/integrations/integration-model.js +42 -0
  84. package/integrations/integration-router.js +367 -0
  85. package/integrations/integration-user.js +144 -0
  86. package/integrations/jest-setup.js +2 -0
  87. package/integrations/jest-teardown.js +2 -0
  88. package/integrations/jest.config.js +12 -0
  89. package/integrations/options.js +54 -0
  90. package/integrations/test/integration-base.test.js +143 -0
  91. package/lambda/README.md +3 -0
  92. package/lambda/TimeoutCatcher.js +43 -0
  93. package/lambda/TimeoutCatcher.test.js +68 -0
  94. package/lambda/bump3.txt +0 -0
  95. package/lambda/index.js +3 -0
  96. package/lambda/jest.config.js +3 -0
  97. package/logs/.eslintrc.json +3 -0
  98. package/logs/CHANGELOG.md +57 -0
  99. package/logs/LICENSE.md +9 -0
  100. package/logs/README.md +3 -0
  101. package/logs/bump3.txt +0 -0
  102. package/logs/index.js +7 -0
  103. package/logs/jest.config.js +5 -0
  104. package/logs/logger.js +69 -0
  105. package/logs/logger.test.js +76 -0
  106. package/migrations/README.md +3 -0
  107. package/migrations/bump3.txt +0 -0
  108. package/migrations/index.js +9 -0
  109. package/migrations/jest.config.js +3 -0
  110. package/migrations/manager.js +33 -0
  111. package/migrations/migrator.js +170 -0
  112. package/migrations/options.js +28 -0
  113. package/module-plugin/.eslintrc.json +3 -0
  114. package/module-plugin/CHANGELOG.md +224 -0
  115. package/module-plugin/LICENSE.md +9 -0
  116. package/module-plugin/ModuleConstants.js +10 -0
  117. package/module-plugin/README.md +3 -0
  118. package/module-plugin/auther.js +342 -0
  119. package/module-plugin/bump3.txt +0 -0
  120. package/module-plugin/credential.js +22 -0
  121. package/module-plugin/entity-manager.js +70 -0
  122. package/module-plugin/entity.js +46 -0
  123. package/module-plugin/index.js +25 -0
  124. package/module-plugin/jest-setup.js +3 -0
  125. package/module-plugin/jest-teardown.js +2 -0
  126. package/module-plugin/jest.config.js +20 -0
  127. package/module-plugin/manager.js +169 -0
  128. package/module-plugin/module-factory.js +60 -0
  129. package/module-plugin/requester/api-key.js +36 -0
  130. package/module-plugin/requester/basic.js +43 -0
  131. package/module-plugin/requester/oauth-2.js +219 -0
  132. package/module-plugin/requester/requester.js +150 -0
  133. package/module-plugin/requester/requester.test.js +28 -0
  134. package/module-plugin/test/auther.test.js +97 -0
  135. package/module-plugin/test/mock-api/api.js +29 -0
  136. package/module-plugin/test/mock-api/definition.js +48 -0
  137. package/module-plugin/test/mock-api/mocks/hubspot.js +43 -0
  138. package/package.json +37 -13
  139. package/prettier-config/.eslintrc.json +3 -0
  140. package/prettier-config/CHANGELOG.md +17 -0
  141. package/prettier-config/LICENSE.md +9 -0
  142. package/prettier-config/README.md +3 -0
  143. package/prettier-config/bump3.txt +0 -0
  144. package/prettier-config/index.js +6 -0
  145. package/syncs/README.md +3 -0
  146. package/syncs/bump3.txt +0 -0
  147. package/syncs/jest.config.js +5 -0
  148. package/syncs/manager.js +466 -0
  149. package/syncs/model.js +62 -0
  150. package/syncs/sync.js +114 -0
  151. package/test-environment/.eslintrc.json +3 -0
  152. package/test-environment/Authenticator.js +73 -0
  153. package/test-environment/CHANGELOG.md +46 -0
  154. package/test-environment/LICENSE.md +9 -0
  155. package/test-environment/README.md +3 -0
  156. package/test-environment/auther-definition-method-tester.js +45 -0
  157. package/test-environment/auther-definition-tester.js +104 -0
  158. package/test-environment/bump3.txt +0 -0
  159. package/test-environment/index.js +24 -0
  160. package/test-environment/integration-validator.js +2 -0
  161. package/test-environment/jest-global-setup.js +6 -0
  162. package/test-environment/jest-global-teardown.js +3 -0
  163. package/test-environment/jest-preset.js +14 -0
  164. package/test-environment/mock-api-readme.md +102 -0
  165. package/test-environment/mock-api.js +284 -0
  166. package/test-environment/mock-integration.js +82 -0
  167. package/test-environment/mongodb.js +22 -0
  168. package/test-environment/override-environment.js +11 -0
  169. package/types/CHANGELOG.md +49 -0
  170. package/types/README.md +24 -0
  171. package/types/assertions/index.d.ts +83 -0
  172. package/types/associations/index.d.ts +74 -0
  173. package/types/bump3.txt +0 -0
  174. package/types/core/index.d.ts +54 -0
  175. package/types/database/index.d.ts +3 -0
  176. package/types/encrypt/index.d.ts +5 -0
  177. package/types/errors/index.d.ts +66 -0
  178. package/types/eslint-config/index.d.ts +41 -0
  179. package/types/index.d.ts +14 -0
  180. package/types/integrations/index.d.ts +191 -0
  181. package/types/lambda/index.d.ts +31 -0
  182. package/types/logs/index.d.ts +5 -0
  183. package/types/module-plugin/index.d.ts +293 -0
  184. package/types/prettier-config/index.d.ts +6 -0
  185. package/types/syncs/index.d.ts +128 -0
  186. package/types/test-environment/index.d.ts +17 -0
  187. package/types/tsconfig.json +103 -0
  188. /package/{.eslintrc.json → assertions/.eslintrc.json} +0 -0
  189. /package/{bump3.txt → assertions/bump3.txt} +0 -0
  190. /package/{jest.config.js → assertions/jest.config.js} +0 -0
  191. /package/{CHANGELOG.md → core/CHANGELOG.md} +0 -0
  192. /package/{README.md → core/README.md} +0 -0
  193. /package/{load-installed-modules.js → core/load-installed-modules.js} +0 -0
@@ -0,0 +1,169 @@
1
+ const { Delegate } = require('../core');
2
+ const { Credential } = require('./credential');
3
+ const { Entity } = require('./entity');
4
+ const { get } = require('../assertions');
5
+
6
+ class ModuleManager extends Delegate {
7
+ static Entity = Entity;
8
+ static Credential = Credential;
9
+
10
+ constructor(params) {
11
+ super(params);
12
+ this.userId = get(params, 'userId', null); // Making this non-required
13
+ }
14
+
15
+ static getName() {
16
+ throw new Error('Module name is not defined');
17
+ }
18
+
19
+ static async getInstance(params) {
20
+ throw new Error(
21
+ 'getInstance is not implemented. It is required for ModuleManager. '
22
+ );
23
+ }
24
+
25
+ static async getEntitiesForUserId(userId) {
26
+ // Only return non-internal fields. Leverages "select" and "options" to non-excepted fields and a pure object.
27
+ const list = await this.Entity.find(
28
+ { user: userId },
29
+ '-dateCreated -dateUpdated -user -credentials -credential -__t -__v',
30
+ { lean: true }
31
+ );
32
+ return list.map((entity) => ({
33
+ id: entity._id,
34
+ type: this.getName(),
35
+ ...entity,
36
+ }));
37
+ }
38
+
39
+ async getEntityId() {
40
+ const list = await Entity.find({ user: this.userId });
41
+ if (list.length > 1) {
42
+ throw new Error(
43
+ 'There should not be more than one entity associated with a user for this specific class type'
44
+ );
45
+ }
46
+ if (list.length == 0) {
47
+ return null;
48
+ }
49
+ return list[0].id;
50
+ }
51
+
52
+ async validateAuthorizationRequirements() {
53
+ const requirements = await this.getAuthorizationRequirements();
54
+ let valid = true;
55
+ if (['oauth1', 'oauth2'].includes(requirements.type) && !requirements.url) {
56
+ valid = false;
57
+ }
58
+ return valid;
59
+ }
60
+
61
+ async getAuthorizationRequirements(params) {
62
+ // this function must return a dictionary with the following format
63
+ // node only url key is required. Data would be used for Base Authentication
64
+ // let returnData = {
65
+ // url: "callback url for the data or teh redirect url for login",
66
+ // type: one of the types defined in modules/Constants.js
67
+ // data: ["required", "fields", "we", "may", "need"]
68
+ // }
69
+ throw new Error(
70
+ 'Authorization requirements method getAuthorizationRequirements() is not defined in the class'
71
+ );
72
+ }
73
+
74
+ async testAuth(params) {
75
+ // this function must invoke a method on the API using authentication
76
+ // if it fails, an exception should be thrown
77
+ throw new Error(
78
+ 'Authentication test method testAuth() is not defined in the class'
79
+ );
80
+ }
81
+
82
+ async processAuthorizationCallback(params) {
83
+ // this function takes in a dictionary of callback information along with
84
+ // a unique user id to associate with the entity in the form of
85
+ // {
86
+ // userId: "some id",
87
+ // data: {}
88
+ // }
89
+
90
+ throw new Error(
91
+ 'Authorization requirements method processAuthorizationCallback() is not defined in the class'
92
+ );
93
+ }
94
+
95
+ //----------------------------------------------------------------------------------------------------
96
+ // optional
97
+
98
+ async getEntityOptions() {
99
+ // May not be needed if the callback already creates the entity, such as in situations
100
+ // like HubSpot where the account is determined in the authorization flow.
101
+ // This should only be used in situations such as FreshBooks where the user needs to make
102
+ // an account decision on the front end.
103
+ throw new Error(
104
+ 'Entity requirement method getEntityOptions() is not defined in the class'
105
+ );
106
+ }
107
+
108
+ async findOrCreateEntity(params) {
109
+ // May not be needed if the callback already creates the entity, such as in situations
110
+ // like HubSpot where the account is determined in the authorization flow.
111
+ // This should only be used in situations such as FreshBooks where the user needs to make
112
+ // an account decision on the front end.
113
+ throw new Error(
114
+ 'Entity requirement method findOrCreateEntity() is not defined in the class'
115
+ );
116
+ }
117
+
118
+ async getAllSyncObjects(SyncClass) {
119
+ // takes in a Sync class and will return all objects associated with the SyncClass in an array
120
+ // in the form of
121
+ // [
122
+ // {...object1},{...object2}...
123
+ // ]
124
+
125
+ throw new Error(
126
+ 'The method "getAllSyncObjects()" is not defined in the class'
127
+ );
128
+ }
129
+
130
+ async batchCreateSyncObjects(syncObjects, syncManager) {
131
+ // takes in an array of Sync objects that has two pieces of data that
132
+ // are important to the updating module:
133
+ // 1. obj.data -> The data mapped to the obj.keys data
134
+ // 2. obj.syncId -> the id of the newly created sync object in our database. You will need to update
135
+ // the sync object in the database with the your id associated with this data. You
136
+ // can do this by calling the SyncManager function updateSyncObject.
137
+ // [
138
+ // syncObject1,syncObject2, ...
139
+ // ]
140
+
141
+ throw new Error(
142
+ 'The method "batchUpdateSyncObjects()" is not defined in the class'
143
+ );
144
+ }
145
+
146
+ async batchUpdateSyncObjects(syncObjects, syncManager) {
147
+ // takes in an array of Sync objects that has two pieces of data that
148
+ // are important to the updating module:
149
+ // 1. obj.data -> The data mapped to the obj.keys data
150
+ // 2. obj.moduleObjectIds[this.constructor.getName()] -> Indexed from the point of view of the module manager
151
+ // it will return a json object holding all of the keys
152
+ // required update this datapoint. an example would be:
153
+ // {companyId:12, email:"test@test.com"}
154
+ // [
155
+ // syncObject1,syncObject2, ...
156
+ // ]
157
+
158
+ throw new Error(
159
+ 'The method "batchUpdateSyncObjects()" is not defined in the class'
160
+ );
161
+ }
162
+
163
+ async markCredentialsInvalid() {
164
+ this.credential.auth_is_valid = false;
165
+ return await this.credential.save();
166
+ }
167
+ }
168
+
169
+ module.exports = { ModuleManager };
@@ -0,0 +1,60 @@
1
+ const { Entity } = require("./entity");
2
+ const { Auther } = require('./auther');
3
+
4
+ class ModuleFactory {
5
+ constructor(...params) {
6
+ this.moduleDefinitions = params;
7
+ this.moduleTypes = this.moduleDefinitions.map(
8
+ (def) => def.moduleName
9
+ );
10
+ }
11
+
12
+ async getEntitiesForUser(userId) {
13
+ let results = [];
14
+ for (const moduleDefinition of this.moduleDefinitions) {
15
+ const moduleInstance = await Auther.getInstance({
16
+ userId,
17
+ definition: moduleDefinition
18
+ });
19
+ const list = await moduleInstance.getEntitiesForUserId(userId);
20
+ results.push(...list);
21
+ }
22
+ return results;
23
+ }
24
+
25
+ checkIsValidType(entityType) {
26
+ return this.moduleTypes.includes(entityType);
27
+ }
28
+
29
+ getModuleDefinitionFromTypeName(typeName) {
30
+ return
31
+ }
32
+
33
+
34
+ async getModuleInstanceFromEntityId(entityId, userId) {
35
+ const entity = await Entity.findById(entityId);
36
+ const moduleDefinition = this.moduleDefinitions.find(
37
+ (def) => entity['__t'] === Auther.getEntityModelFromDefinition(def).modelName
38
+ )
39
+ if (!moduleDefinition) {
40
+ throw new Error('Module definition not found for entity type: ' + entity['__t']);
41
+ }
42
+ return await Auther.getInstance({
43
+ userId,
44
+ entityId,
45
+ definition: moduleDefinition
46
+ });
47
+ }
48
+
49
+ async getInstanceFromTypeName(typeName, userId) {
50
+ const moduleDefinition =this.moduleDefinitions.find(
51
+ (def) => def.getName() === typeName
52
+ );
53
+ return await Auther.getInstance({
54
+ userId,
55
+ definition: moduleDefinition
56
+ });
57
+ }
58
+ }
59
+
60
+ module.exports = { ModuleFactory };
@@ -0,0 +1,36 @@
1
+ const { Requester } = require('./requester');
2
+ const { ModuleConstants } = require('../ModuleConstants');
3
+
4
+
5
+ class ApiKeyRequester extends Requester {
6
+
7
+ static requesterType = ModuleConstants.authType.apiKey;
8
+
9
+ constructor(params) {
10
+ super(params);
11
+ this.requesterType = 'apiKey';
12
+ this.API_KEY_NAME = 'key';
13
+ this.API_KEY_VALUE = null;
14
+ }
15
+
16
+ async addAuthHeaders(headers) {
17
+ if (this.API_KEY_VALUE) {
18
+ headers[this.API_KEY_NAME] = this.API_KEY_VALUE;
19
+ }
20
+ return headers;
21
+ }
22
+
23
+ isAuthenticated() {
24
+ return (
25
+ this.API_KEY_VALUE !== null &&
26
+ this.API_KEY_VALUE !== undefined &&
27
+ this.API_KEY_VALUE.trim().length() > 0
28
+ );
29
+ }
30
+
31
+ setApiKey(api_key) {
32
+ this.API_KEY_VALUE = api_key;
33
+ }
34
+ }
35
+
36
+ module.exports = { ApiKeyRequester };
@@ -0,0 +1,43 @@
1
+ const { Requester } = require('./requester');
2
+ const { get } = require('../../assertions');
3
+ const { ModuleConstants } = require('../ModuleConstants');
4
+
5
+ class BasicAuthRequester extends Requester {
6
+
7
+ static requesterType = ModuleConstants.authType.basic;
8
+
9
+ constructor(params) {
10
+ super(params);
11
+
12
+ this.username = get(params, 'username', null);
13
+ this.password = get(params, 'password', null);
14
+ }
15
+
16
+ async addAuthHeaders(headers) {
17
+ if (this.username && this.password) {
18
+ headers['Authorization'] =
19
+ 'Basic ' +
20
+ Buffer.from(this.username + ':' + this.password).toString(
21
+ 'base64'
22
+ );
23
+ }
24
+ return headers;
25
+ }
26
+
27
+ isAuthenticated() {
28
+ return (
29
+ this.username !== null &&
30
+ this.username !== undefined &&
31
+ this.username.trim().length() > 0
32
+ );
33
+ }
34
+
35
+ setUsername(username) {
36
+ this.username = username;
37
+ }
38
+ setPassword(password) {
39
+ this.password = password;
40
+ }
41
+ }
42
+
43
+ module.exports = { BasicAuthRequester };
@@ -0,0 +1,219 @@
1
+ const { Requester } = require('./requester');
2
+ const { get } = require('../../assertions');
3
+ const { ModuleConstants } = require('../ModuleConstants');
4
+
5
+ class OAuth2Requester extends Requester {
6
+
7
+ static requesterType = ModuleConstants.authType.oauth2;
8
+
9
+ constructor(params) {
10
+ super(params);
11
+ this.DLGT_TOKEN_UPDATE = 'TOKEN_UPDATE';
12
+ this.DLGT_TOKEN_DEAUTHORIZED = 'TOKEN_DEAUTHORIZED';
13
+
14
+ this.delegateTypes.push(this.DLGT_TOKEN_UPDATE);
15
+ this.delegateTypes.push(this.DLGT_TOKEN_DEAUTHORIZED);
16
+
17
+ this.grant_type = get(params, 'grant_type', 'authorization_code');
18
+ this.client_id = get(params, 'client_id', null);
19
+ this.client_secret = get(params, 'client_secret', null);
20
+ this.redirect_uri = get(params, 'redirect_uri', null);
21
+ this.scope = get(params, 'scope', null);
22
+ this.authorizationUri = get(params, 'authorizationUri', null);
23
+ this.baseURL = get(params, 'baseURL', null);
24
+ this.access_token = get(params, 'access_token', null);
25
+ this.refresh_token = get(params, 'refresh_token', null);
26
+ this.accessTokenExpire = get(params, 'accessTokenExpire', null);
27
+ this.refreshTokenExpire = get(params, 'refreshTokenExpire', null);
28
+ this.audience = get(params, 'audience', null);
29
+ this.username = get(params, 'username', null);
30
+ this.password = get(params, 'password', null);
31
+ this.state = get(params, 'state', null);
32
+
33
+ this.isRefreshable = true;
34
+ }
35
+
36
+ async setTokens(params) {
37
+ this.access_token = get(params, 'access_token');
38
+ this.refresh_token = get(params, 'refresh_token', null);
39
+ const accessExpiresIn = get(params, 'expires_in', null);
40
+ const refreshExpiresIn = get(
41
+ params,
42
+ 'x_refresh_token_expires_in',
43
+ null
44
+ );
45
+
46
+ this.accessTokenExpire = new Date(Date.now() + accessExpiresIn * 1000);
47
+ this.refreshTokenExpire = new Date(Date.now() + refreshExpiresIn * 1000);
48
+
49
+ await this.notify(this.DLGT_TOKEN_UPDATE);
50
+ }
51
+
52
+ getAuthorizationUri() {
53
+ return this.authorizationUri;
54
+ }
55
+
56
+ getAuthorizationRequirements() {
57
+ return {
58
+ url: this.getAuthorizationUri(),
59
+ type: 'oauth2',
60
+ };
61
+ }
62
+
63
+ // this.client_id, this.client_secret, this.redirect_uri, and this.tokenUri
64
+ // will need to be defined in the child class before super(params)
65
+ async getTokenFromCode(code) {
66
+ const params = new URLSearchParams();
67
+ params.append('grant_type', 'authorization_code');
68
+ params.append('client_id', this.client_id);
69
+ params.append('client_secret', this.client_secret);
70
+ params.append('redirect_uri', this.redirect_uri);
71
+ params.append('scope', this.scope);
72
+ params.append('code', code);
73
+ const options = {
74
+ body: params,
75
+ headers: {
76
+ 'Content-Type': 'application/x-www-form-urlencoded',
77
+ },
78
+ url: this.tokenUri,
79
+ };
80
+ const response = await this._post(options, false);
81
+ await this.setTokens(response);
82
+ return response;
83
+ }
84
+
85
+ // REPLACE getTokenFromCode IN THE CHILD IF NEEDED
86
+ // this.client_id, this.client_secret, this.redirect_uri, and this.tokenUri
87
+ // will need to be defined in the child class before super(params)
88
+ async getTokenFromCodeBasicAuthHeader(code) {
89
+ const params = new URLSearchParams();
90
+ params.append('grant_type', 'authorization_code');
91
+ params.append('client_id', this.client_id);
92
+ params.append('redirect_uri', this.redirect_uri);
93
+ params.append('code', code);
94
+
95
+ const options = {
96
+ body: params,
97
+ headers: {
98
+ 'Content-Type': 'application/x-www-form-urlencoded',
99
+ Authorization: `Basic ${Buffer.from(
100
+ `${this.client_id}:${this.client_secret}`
101
+ ).toString('base64')}`,
102
+ },
103
+ url: this.tokenUri,
104
+ };
105
+
106
+ const response = await this._post(options, false);
107
+ await this.setTokens(response);
108
+ return response;
109
+ }
110
+
111
+ // this.client_id, this.client_secret, this.redirect_uri, and this.tokenUri
112
+ // will need to be defined in the child class before super(params)
113
+ async refreshAccessToken(refreshTokenObject) {
114
+ this.access_token = undefined;
115
+ const params = new URLSearchParams();
116
+ params.append('grant_type', 'refresh_token');
117
+ params.append('client_id', this.client_id);
118
+ params.append('client_secret', this.client_secret);
119
+ params.append('refresh_token', refreshTokenObject.refresh_token);
120
+ params.append('redirect_uri', this.redirect_uri);
121
+
122
+ const options = {
123
+ body: params,
124
+ url: this.tokenUri,
125
+ headers: {
126
+ 'Content-Type': 'application/x-www-form-urlencoded',
127
+ },
128
+ };
129
+ const response = await this._post(options, false);
130
+ await this.setTokens(response);
131
+ return response;
132
+ }
133
+
134
+ async addAuthHeaders(headers) {
135
+ if (this.access_token) {
136
+ headers.Authorization = `Bearer ${this.access_token}`;
137
+ }
138
+
139
+ return headers;
140
+ }
141
+
142
+ isAuthenticated() {
143
+ return (
144
+ this.accessToken !== null &&
145
+ this.refreshToken !== null &&
146
+ this.accessTokenExpire &&
147
+ this.refreshTokenExpire
148
+ );
149
+ }
150
+
151
+ async refreshAuth() {
152
+ try {
153
+ if (this.grantType !== 'client_credentials') {
154
+ await this.refreshAccessToken({
155
+ refresh_token: this.refresh_token,
156
+ });
157
+ } else {
158
+ await this.getTokenFromClientCredentials();
159
+ }
160
+ } catch {
161
+ await this.notify(this.DLGT_INVALID_AUTH);
162
+ }
163
+ }
164
+
165
+ async getTokenFromUsernamePassword() {
166
+ try {
167
+ const url = this.tokenUri;
168
+
169
+ const body = {
170
+ username: this.username,
171
+ password: this.password,
172
+ grant_type: 'password',
173
+ };
174
+ const headers = {
175
+ 'Content-Type': 'application/json',
176
+ };
177
+
178
+ const tokenRes = await this._post({
179
+ url,
180
+ body,
181
+ headers,
182
+ });
183
+
184
+ await this.setTokens(tokenRes);
185
+ return tokenRes;
186
+ } catch {
187
+ await this.notify(this.DLGT_INVALID_AUTH);
188
+ }
189
+ }
190
+
191
+ async getTokenFromClientCredentials() {
192
+ try {
193
+ const url = this.tokenUri;
194
+
195
+ const body = {
196
+ audience: this.audience,
197
+ client_id: this.client_id,
198
+ client_secret: this.client_secret,
199
+ grant_type: 'client_credentials',
200
+ };
201
+ const headers = {
202
+ 'Content-Type': 'application/json',
203
+ };
204
+
205
+ const tokenRes = await this._post({
206
+ url,
207
+ body,
208
+ headers,
209
+ });
210
+
211
+ await this.setTokens(tokenRes);
212
+ return tokenRes;
213
+ } catch {
214
+ await this.notify(this.DLGT_INVALID_AUTH);
215
+ }
216
+ }
217
+ }
218
+
219
+ module.exports = { OAuth2Requester };
@@ -0,0 +1,150 @@
1
+ const fetch = require('node-fetch');
2
+ const { Delegate } = require('../../core');
3
+ const { FetchError } = require('../../errors');
4
+ const { get } = require('../../assertions');
5
+
6
+ class Requester extends Delegate {
7
+ constructor(params) {
8
+ super(params);
9
+ this.backOff = get(params, 'backOff', [1, 3, 10, 30, 60, 180]);
10
+ this.isRefreshable = false;
11
+ this.refreshCount = 0;
12
+ this.DLGT_INVALID_AUTH = 'INVALID_AUTH';
13
+ this.delegateTypes.push(this.DLGT_INVALID_AUTH);
14
+ this.agent = get(params, 'agent', null);
15
+
16
+ // Allow passing in the fetch function
17
+ // Instance methods can use this.fetch without differentiating
18
+ this.fetch = get(params, 'fetch', fetch);
19
+ }
20
+
21
+ parsedBody = async (resp) => {
22
+ const contentType = resp.headers.get('Content-Type') || '';
23
+
24
+ if (
25
+ contentType.match(/^application\/json/) ||
26
+ contentType.match(/^application\/vnd.api\+json/)
27
+ ) {
28
+ return resp.json();
29
+ }
30
+
31
+ return resp.text();
32
+ };
33
+
34
+ async _request(url, options, i = 0) {
35
+ let encodedUrl = encodeURI(url);
36
+ if (options.query) {
37
+ let queryBuild = '?';
38
+ for (const key in options.query) {
39
+ queryBuild += `${encodeURIComponent(key)}=${encodeURIComponent(
40
+ options.query[key]
41
+ )}&`;
42
+ }
43
+ encodedUrl += queryBuild.slice(0, -1);
44
+ }
45
+
46
+ options.headers = await this.addAuthHeaders(options.headers);
47
+
48
+ if (this.agent) options.agent = this.agent;
49
+
50
+ const response = await this.fetch(encodedUrl, options);
51
+ const { status } = response;
52
+
53
+ // If the status is retriable and there are back off requests left, retry the request
54
+ if ((status === 429 || status >= 500) && i < this.backOff.length) {
55
+ const delay = this.backOff[i] * 1000;
56
+ await new Promise((resolve) => setTimeout(resolve, delay));
57
+ return this._request(url, options, i + 1);
58
+ } else if (status === 401) {
59
+ if (!this.isRefreshable || this.refreshCount > 0) {
60
+ await this.notify(this.DLGT_INVALID_AUTH);
61
+ } else {
62
+ this.refreshCount++;
63
+ await this.refreshAuth();
64
+ return this._request(url, options, i + 1); // Retries
65
+ }
66
+ }
67
+
68
+ // If the error wasn't retried, throw.
69
+ if (status >= 400) {
70
+ throw await FetchError.create({
71
+ resource: encodedUrl,
72
+ init: options,
73
+ response,
74
+ });
75
+ }
76
+
77
+ return options.returnFullRes
78
+ ? response
79
+ : await this.parsedBody(response);
80
+ }
81
+
82
+ async _get(options) {
83
+ const fetchOptions = {
84
+ method: 'GET',
85
+ credentials: 'include',
86
+ headers: options.headers || {},
87
+ query: options.query || {},
88
+ returnFullRes: options.returnFullRes || false,
89
+ };
90
+
91
+ const res = await this._request(options.url, fetchOptions);
92
+ return res;
93
+ }
94
+
95
+ async _post(options, stringify = true) {
96
+ const fetchOptions = {
97
+ method: 'POST',
98
+ credentials: 'include',
99
+ headers: options.headers || {},
100
+ query: options.query || {},
101
+ body: stringify ? JSON.stringify(options.body) : options.body,
102
+ returnFullRes: options.returnFullRes || false,
103
+ };
104
+ const res = await this._request(options.url, fetchOptions);
105
+ return res;
106
+ }
107
+
108
+ async _patch(options, stringify = true) {
109
+ const fetchOptions = {
110
+ method: 'PATCH',
111
+ credentials: 'include',
112
+ headers: options.headers || {},
113
+ query: options.query || {},
114
+ body: stringify ? JSON.stringify(options.body) : options.body,
115
+ returnFullRes: options.returnFullRes || false,
116
+ };
117
+ const res = await this._request(options.url, fetchOptions);
118
+ return res;
119
+ }
120
+
121
+ async _put(options, stringify = true) {
122
+ const fetchOptions = {
123
+ method: 'PUT',
124
+ credentials: 'include',
125
+ headers: options.headers || {},
126
+ query: options.query || {},
127
+ body: stringify ? JSON.stringify(options.body) : options.body,
128
+ returnFullRes: options.returnFullRes || false,
129
+ };
130
+ const res = await this._request(options.url, fetchOptions);
131
+ return res;
132
+ }
133
+
134
+ async _delete(options) {
135
+ const fetchOptions = {
136
+ method: 'DELETE',
137
+ credentials: 'include',
138
+ headers: options.headers || {},
139
+ query: options.query || {},
140
+ returnFullRes: options.returnFullRes || true,
141
+ };
142
+ return this._request(options.url, fetchOptions);
143
+ }
144
+
145
+ async refreshAuth() {
146
+ throw new Error('refreshAuth not yet defined in child of Requester');
147
+ }
148
+ }
149
+
150
+ module.exports = { Requester };