@joystick.js/node-canary 0.0.0-canary.32 → 0.0.0-canary.321

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 (92) hide show
  1. package/_package.json +2 -3
  2. package/dist/action/class.js +21 -0
  3. package/dist/api/get.js +15 -13
  4. package/dist/api/set.js +15 -13
  5. package/dist/app/accounts/createMetadataTableColumns.js +12 -0
  6. package/dist/app/accounts/deleteUser.js +7 -0
  7. package/dist/app/accounts/generateSession.js +2 -4
  8. package/dist/app/accounts/getBrowserSafeUser.js +5 -2
  9. package/dist/app/accounts/hasLoginTokenExpired.js +1 -2
  10. package/dist/app/accounts/index.js +2 -0
  11. package/dist/app/accounts/login.js +6 -0
  12. package/dist/app/accounts/recoverPassword.js +3 -0
  13. package/dist/app/accounts/resetPassword.js +6 -0
  14. package/dist/app/accounts/signup.js +50 -7
  15. package/dist/app/accounts/verifyEmail.js +3 -0
  16. package/dist/app/cluster.js +26 -0
  17. package/dist/app/databases/generate_sql_from_object.js +60 -0
  18. package/dist/app/databases/mongodb/buildConnectionString.js +1 -1
  19. package/dist/app/databases/mongodb/createAccountsIndexes.js +18 -0
  20. package/dist/app/databases/mongodb/createSessionsIndexes.js +10 -0
  21. package/dist/app/databases/mongodb/index.js +1 -1
  22. package/dist/app/databases/mongodb/queries/accounts.js +8 -1
  23. package/dist/app/databases/mongodb/queries/queues.js +40 -26
  24. package/dist/app/databases/mongodb/queries/sessions.js +26 -0
  25. package/dist/app/databases/postgresql/createSessionsIndexes.js +10 -0
  26. package/dist/app/databases/postgresql/createSessionsTables.js +14 -0
  27. package/dist/app/databases/postgresql/handleCleanupQueues.js +35 -0
  28. package/dist/app/databases/postgresql/index.js +88 -2
  29. package/dist/app/databases/postgresql/queries/accounts.js +5 -2
  30. package/dist/app/databases/postgresql/queries/queues.js +71 -37
  31. package/dist/app/databases/postgresql/queries/sessions.js +43 -0
  32. package/dist/app/databases/queryMap.js +6 -2
  33. package/dist/app/databases/stringToSnakeCase.js +6 -0
  34. package/dist/app/getBrowserSafeRequest.js +3 -2
  35. package/dist/app/index.js +226 -75
  36. package/dist/app/initExpress.js +1 -1
  37. package/dist/app/middleware/csp.js +2 -2
  38. package/dist/app/middleware/getTranslations.js +64 -0
  39. package/dist/app/middleware/get_insecure_landing_page_html.js +71 -0
  40. package/dist/app/middleware/hmr/client.js +13 -9
  41. package/dist/app/middleware/index.js +6 -5
  42. package/dist/app/middleware/insecure.js +3 -4
  43. package/dist/app/middleware/render.js +11 -68
  44. package/dist/app/middleware/session.js +12 -11
  45. package/dist/app/queues/index.js +64 -28
  46. package/dist/app/registerGetters.js +5 -6
  47. package/dist/app/registerSetters.js +5 -6
  48. package/dist/app/runGetter.js +17 -5
  49. package/dist/app/runSessionQuery.js +15 -0
  50. package/dist/app/runSetter.js +17 -5
  51. package/dist/app/sanitizeAPIResponse.js +1 -1
  52. package/dist/app/validateInstanceToken.js +16 -0
  53. package/dist/app/validateSession.js +8 -3
  54. package/dist/app/validateUploaderOptions.js +3 -3
  55. package/dist/app/validateUploads.js +12 -1
  56. package/dist/email/send.js +7 -1
  57. package/dist/email/templates/reset-password.js +0 -1
  58. package/dist/fixture/index.js +40 -0
  59. package/dist/index.js +19 -0
  60. package/dist/lib/escapeKeyValuePair.js +13 -0
  61. package/dist/lib/formatAPIError.js +0 -1
  62. package/dist/lib/getBuildPath.js +1 -1
  63. package/dist/lib/getSSLCertificates.js +3 -3
  64. package/dist/lib/importFile.js +7 -0
  65. package/dist/lib/isValidJSONString.js +1 -1
  66. package/dist/lib/log.js +0 -3
  67. package/dist/lib/objectToSQLKeysString.js +1 -1
  68. package/dist/lib/objectToSQLValuesString.js +1 -1
  69. package/dist/lib/serializeQueryParameters.js +1 -1
  70. package/dist/lib/timestamps.js +47 -0
  71. package/dist/lib/wait.js +8 -0
  72. package/dist/push/logs/index.js +32 -17
  73. package/dist/settings/load.js +3 -5
  74. package/dist/ssr/compileCSS.js +4 -4
  75. package/dist/ssr/findComponentInTree.js +1 -1
  76. package/dist/ssr/getAPIForDataFunctions.js +35 -0
  77. package/dist/ssr/getDataFromComponent.js +15 -0
  78. package/dist/ssr/index.js +19 -45
  79. package/dist/ssr/replaceWhenTags.js +2 -3
  80. package/dist/ssr/setHeadTagsInHTML.js +3 -3
  81. package/dist/test/index.js +9 -0
  82. package/dist/test/trackFunctionCall.js +17 -0
  83. package/dist/validation/inputWithSchema/index.js +3 -3
  84. package/dist/validation/schema/index.js +5 -5
  85. package/dist/websockets/index.js +4 -0
  86. package/getSanitizedContext.js +43 -0
  87. package/package.json +2 -2
  88. package/dist/app/accounts/roles/index.test.js +0 -123
  89. package/dist/app/index.test.js +0 -575
  90. package/dist/app/middleware/sanitizeRequestParameters.js +0 -26
  91. package/dist/email/send.test.js +0 -37
  92. package/dist/validation/index.test.js +0 -463
package/dist/ssr/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import fs from "fs";
2
- import { __package } from "../index.js";
2
+ import joystick, { __package } from "../index.js";
3
3
  import get from "../api/get";
4
4
  import set from "../api/set";
5
5
  import getBrowserSafeRequest from "../app/getBrowserSafeRequest";
@@ -8,6 +8,9 @@ import getCSSFromTree from "./getCSSFromTree";
8
8
  import replaceWhenTags from "./replaceWhenTags";
9
9
  import setHeadTagsInHTML from "./setHeadTagsInHTML";
10
10
  import { parseHTML } from "linkedom";
11
+ import getDataFromComponent from "./getDataFromComponent.js";
12
+ import getAPIForDataFunctions from "./getAPIForDataFunctions.js";
13
+ import getBrowserSafeUser from "../app/accounts/getBrowserSafeUser.js";
11
14
  const injectCSSIntoHTML = (html, baseCSS = "", css = "") => {
12
15
  try {
13
16
  return html.replace("${css}", css).replace("${globalCSS}", `<style>${baseCSS || ""}</style>`).replace("${componentCSS}", css);
@@ -21,6 +24,7 @@ const handleHTMLReplacementsForApp = ({
21
24
  componentInstance = {},
22
25
  dataFromComponent,
23
26
  dataForClient = {},
27
+ browserSafeUser = {},
24
28
  browserSafeRequest = {},
25
29
  props = {},
26
30
  translations = {},
@@ -44,9 +48,10 @@ const handleHTMLReplacementsForApp = ({
44
48
  window.__joystick_ssr__ = true;
45
49
  ${process.env.NODE_ENV === "development" ? `window.__joystick_hmr_port__ = ${parseInt(process.env.PORT, 10) + 1};` : ""}
46
50
  window.__joystick_data__ = ${JSON.stringify({
47
- [componentInstance.id]: dataFromComponent?.data || {},
51
+ [componentInstance.id]: dataFromComponent?.data,
48
52
  ...dataForClient || {}
49
53
  })};
54
+ window.__joystick_user__ = ${JSON.stringify(browserSafeUser)};
50
55
  window.__joystick_req__ = ${JSON.stringify(browserSafeRequest)};
51
56
  window.__joystick_ssr_props__ = ${JSON.stringify(props)};
52
57
  window.__joystick_i18n__ = ${JSON.stringify(translations)};
@@ -90,6 +95,7 @@ const getHTMLWithTargetReplacements = ({
90
95
  baseHTML = "",
91
96
  dataFromComponent = {},
92
97
  dataForClient = {},
98
+ browserSafeUser = {},
93
99
  browserSafeRequest = {},
94
100
  props = {},
95
101
  translations = {},
@@ -112,6 +118,7 @@ const getHTMLWithTargetReplacements = ({
112
118
  componentHTML,
113
119
  dataFromComponent,
114
120
  dataForClient,
121
+ browserSafeUser,
115
122
  browserSafeRequest,
116
123
  props,
117
124
  translations,
@@ -146,6 +153,7 @@ const processHTML = ({
146
153
  isEmailRender = false,
147
154
  emailSubject = "",
148
155
  emailPreheader = "",
156
+ browserSafeUser = {},
149
157
  browserSafeRequest = {},
150
158
  props = {},
151
159
  translations = {},
@@ -169,6 +177,7 @@ const processHTML = ({
169
177
  dataFromComponent,
170
178
  dataFromTree,
171
179
  dataForClient,
180
+ browserSafeUser,
172
181
  browserSafeRequest,
173
182
  props,
174
183
  translations,
@@ -200,7 +209,7 @@ const getBaseCSS = (baseHTMLName = "") => {
200
209
  };
201
210
  const addAttributesToDOM = (dom = {}, attributes = {}) => {
202
211
  try {
203
- const attributeKeys = Object.keys(attributes);
212
+ const attributeKeys = Object.keys(attributes || {});
204
213
  const attributeKeysWithoutClassList = attributeKeys?.filter((key) => key !== "class");
205
214
  if (Array.isArray(attributes?.class?.list)) {
206
215
  if (attributes?.class?.method === "replace") {
@@ -290,17 +299,6 @@ const buildTreeForComponent = (componentInstance = {}, ssrTree = {}, translation
290
299
  throw new Error(`[ssr.buildTreeForComponent] ${exception.message}`);
291
300
  }
292
301
  };
293
- const getDataFromComponent = async (componentInstance = {}, api = {}, browserSafeRequest = {}) => {
294
- try {
295
- const data = await componentInstance.handleFetchData(api, browserSafeRequest, {}, componentInstance);
296
- return {
297
- componentId: componentInstance?.id,
298
- data
299
- };
300
- } catch (exception) {
301
- throw new Error(`[ssr.getDataFromComponent] ${exception.message}`);
302
- }
303
- };
304
302
  const getTreeForSSR = (componentInstance = {}) => {
305
303
  try {
306
304
  return {
@@ -321,34 +319,6 @@ const getComponentInstance = (Component, options = {}) => {
321
319
  throw new Error(`[ssr.getComponentInstance] ${exception.message}`);
322
320
  }
323
321
  };
324
- const getAPIForDataFunctions = (req = {}, api = {}) => {
325
- try {
326
- return {
327
- get: (getterName = "", getterOptions = {}) => {
328
- return get({
329
- getterName,
330
- getterOptions: api?.getters[getterName] || {},
331
- input: getterOptions?.input,
332
- output: getterOptions?.output,
333
- context: req?.context,
334
- APIOptions: api?.options
335
- });
336
- },
337
- set: (setterName = "", setterOptions = {}) => {
338
- return set({
339
- setterName,
340
- setterOptions: api?.setters[setterName] || {},
341
- input: setterOptions?.input,
342
- output: setterOptions?.output,
343
- context: req?.context,
344
- APIOptions: api?.options
345
- });
346
- }
347
- };
348
- } catch (exception) {
349
- throw new Error(`[ssr.getAPIForDataFunctions] ${exception.message}`);
350
- }
351
- };
352
322
  const validateOptions = (options) => {
353
323
  try {
354
324
  if (!options)
@@ -363,6 +333,7 @@ const ssr = async (options, { resolve, reject }) => {
363
333
  try {
364
334
  validateOptions(options);
365
335
  const apiForDataFunctions = getAPIForDataFunctions(options.req, options?.api);
336
+ const browserSafeUser = getBrowserSafeUser(options?.req?.context?.user);
366
337
  const browserSafeRequest = options?.email ? {} : getBrowserSafeRequest({ ...options?.req || {} });
367
338
  const componentInstance = getComponentInstance(options.componentFunction, {
368
339
  props: options?.props || {},
@@ -371,13 +342,14 @@ const ssr = async (options, { resolve, reject }) => {
371
342
  api: apiForDataFunctions,
372
343
  req: browserSafeRequest
373
344
  });
345
+ componentInstance.user = browserSafeUser;
374
346
  const ssrTree = getTreeForSSR(componentInstance);
375
347
  const ssrTreeForCSS = getTreeForSSR(componentInstance);
376
- const dataFromComponent = await getDataFromComponent(componentInstance, apiForDataFunctions, browserSafeRequest).then((data) => data).catch((error) => {
348
+ const dataFromComponent = await getDataFromComponent(componentInstance, apiForDataFunctions, browserSafeUser, browserSafeRequest).then((data) => data).catch((error) => {
377
349
  return [{ error }];
378
350
  });
379
- buildTreeForComponent(componentInstance, ssrTree);
380
- buildTreeForComponent(componentInstance, ssrTreeForCSS);
351
+ buildTreeForComponent(componentInstance, ssrTree, options?.translations);
352
+ buildTreeForComponent(componentInstance, ssrTreeForCSS, options?.translations);
381
353
  const dataFromTree = await getDataFromTree(ssrTree).then((data) => data).catch((error) => {
382
354
  return [{ error }];
383
355
  });
@@ -397,6 +369,7 @@ const ssr = async (options, { resolve, reject }) => {
397
369
  isEmailRender: options?.email,
398
370
  emailSubject: options?.emailSubject,
399
371
  emailPreheader: options?.emailPreheader,
372
+ browserSafeUser,
400
373
  browserSafeRequest,
401
374
  props: options?.props,
402
375
  translations: options?.translations,
@@ -417,6 +390,7 @@ const ssr = async (options, { resolve, reject }) => {
417
390
  isEmailRender: options?.email,
418
391
  emailSubject: options?.emailSubject,
419
392
  emailPreheader: options?.emailPreheader,
393
+ browserSafeUser,
420
394
  browserSafeRequest,
421
395
  props: options?.props,
422
396
  translations: options?.translations,
@@ -25,9 +25,8 @@ const flattenAndReplaceWhenElements = (dom = {}, options = {}) => {
25
25
  };
26
26
  var replaceWhenTags_default = (html = "") => {
27
27
  try {
28
- const { document: parseHTMLDocument } = parseHTML(html);
29
- const dom = flattenAndReplaceWhenElements(parseHTMLDocument);
30
- return dom.toString();
28
+ const whenRegex = new RegExp("<when>|</when>", "g");
29
+ return html?.replace(whenRegex, "");
31
30
  } catch (exception) {
32
31
  throw new Error(`[ssr.replaceWhenTags] ${exception.message}`);
33
32
  }
@@ -28,7 +28,7 @@ var setHeadTagsInHTML_default = (htmlString = "", head = null, req = {}) => {
28
28
  const metaTag = head.tags.meta[currentMetaTag];
29
29
  const existingTag = headTag.querySelector(`meta[name="${metaTag.name}"]`);
30
30
  const newTag = htmlParser.parse(`<meta />`);
31
- const metaTagEntries = Object.entries(metaTag);
31
+ const metaTagEntries = Object.entries(metaTag || {});
32
32
  let currentMetaTagEntry = metaTagEntries.length;
33
33
  while (currentMetaTagEntry--) {
34
34
  const [attributeName, attributeValue] = metaTagEntries[currentMetaTagEntry];
@@ -47,7 +47,7 @@ var setHeadTagsInHTML_default = (htmlString = "", head = null, req = {}) => {
47
47
  while (currentLinkTag--) {
48
48
  const linkTag = head.tags.link[currentLinkTag];
49
49
  const newTag = htmlParser.parse(`<link />`);
50
- let linkTagEntries = Object.entries(linkTag);
50
+ let linkTagEntries = Object.entries(linkTag || {});
51
51
  let currentLinkTagEntry = linkTagEntries.length;
52
52
  while (currentLinkTagEntry--) {
53
53
  const [attributeName, attributeValue] = linkTagEntries[currentLinkTagEntry];
@@ -61,7 +61,7 @@ var setHeadTagsInHTML_default = (htmlString = "", head = null, req = {}) => {
61
61
  while (currentScriptTag--) {
62
62
  const scriptTag = head.tags.script[currentScriptTag];
63
63
  const newTag = htmlParser.parse(`<script><\/script>`);
64
- let scriptTagEntries = Object.entries(scriptTag);
64
+ let scriptTagEntries = Object.entries(scriptTag || {});
65
65
  let currentScriptTagEntry = scriptTagEntries.length;
66
66
  while (currentScriptTagEntry--) {
67
67
  const [attributeName, attributeValue] = scriptTagEntries[currentScriptTagEntry];
@@ -0,0 +1,9 @@
1
+ import trackFunctionCall from "./trackFunctionCall.js";
2
+ var test_default = {
3
+ utils: {
4
+ trackFunctionCall
5
+ }
6
+ };
7
+ export {
8
+ test_default as default
9
+ };
@@ -0,0 +1,17 @@
1
+ var trackFunctionCall_default = (path = "", args = []) => {
2
+ if (process.env.NODE_ENV === "test") {
3
+ process.test = {
4
+ ...process.test || {},
5
+ functionCalls: {
6
+ ...process?.test?.functionCalls || {},
7
+ [path]: [
8
+ ...process?.test?.functionCalls && process?.test?.functionCalls[path] || [],
9
+ { calledAt: new Date().toISOString(), args }
10
+ ]
11
+ }
12
+ };
13
+ }
14
+ };
15
+ export {
16
+ trackFunctionCall_default as default
17
+ };
@@ -47,7 +47,7 @@ const addToValidationQueue = (queue = [], schema = {}, input = {}, parentPath =
47
47
  }
48
48
  const rulesOnlySchema = !!(schema.type && constants.types.includes(schema.type));
49
49
  if (!rulesOnlySchema) {
50
- Object.entries(schema).forEach(([field, rules]) => {
50
+ Object.entries(schema || {}).forEach(([field, rules]) => {
51
51
  const path = `${parentPath ? `${parentPath}.${field}` : field}`;
52
52
  const validationTask = addValidationTask({
53
53
  queue,
@@ -77,12 +77,12 @@ const validateInputWithSchema = async (input = null, schema = null, parentPath =
77
77
  if (!input) {
78
78
  errors.push("Input is required.");
79
79
  }
80
- if (schema && Object.keys(schema) && !schema.type) {
80
+ if (schema && Object.keys(schema || {}) && !schema.type) {
81
81
  validateSchema(schema);
82
82
  }
83
83
  const queue = addToValidationQueue([], schema, input, parentPath);
84
84
  await Promise.all(queue.flatMap((validationTask) => {
85
- return Object.entries(validationTask.rules).flatMap(async ([ruleName, ruleValue]) => {
85
+ return Object.entries(validationTask.rules || {}).flatMap(async ([ruleName, ruleValue]) => {
86
86
  const validator = constants.rules[ruleName];
87
87
  if (validator && !validationTask.path.includes(".$.")) {
88
88
  const result = await validator(ruleValue, validationTask.inputValue, validationTask.path);
@@ -2,16 +2,16 @@ import throwError from "../lib/throwError";
2
2
  import { isObject } from "../lib/typeValidators";
3
3
  import constants from "../lib/constants";
4
4
  const validateRuleType = (schema) => {
5
- Object.entries(schema).forEach(([field, rules]) => {
5
+ Object.entries(schema || {}).forEach(([field, rules]) => {
6
6
  if (rules && rules.type && !constants.types.includes(rules.type)) {
7
7
  throw new Error(`Invalid value for schema field "${field}" type rule. ${rules.type} is not supported. Use one of the following: ${constants.types.slice(0, constants.types.length - 1).join(", ")}, or ${constants.types[constants.types.length - 1]}.`);
8
8
  }
9
9
  });
10
10
  };
11
11
  const validateRuleNames = (schema) => {
12
- Object.entries(schema).forEach(([field, rules]) => {
13
- Object.keys(rules).forEach((ruleName) => {
14
- const ruleNames = Object.keys(constants.rules);
12
+ Object.entries(schema || {}).forEach(([field, rules]) => {
13
+ Object.keys(rules || {}).forEach((ruleName) => {
14
+ const ruleNames = Object.keys(constants.rules || {});
15
15
  if (!ruleNames.includes(ruleName)) {
16
16
  throw new Error(`Invalid rule name ${ruleName} in rule for ${field} field.`);
17
17
  }
@@ -19,7 +19,7 @@ const validateRuleNames = (schema) => {
19
19
  });
20
20
  };
21
21
  const validateRulesAreObjects = (schema) => {
22
- Object.entries(schema).forEach(([field, rules]) => {
22
+ Object.entries(schema || {}).forEach(([field, rules]) => {
23
23
  if (!isObject(rules)) {
24
24
  throw new Error(`Must pass an object containing rules to validate by for ${field} field.`);
25
25
  }
@@ -1,8 +1,12 @@
1
1
  import emitWebsocketEvent from "./emitWebsocketEvent";
2
+ import trackFunctionCall from "../test/trackFunctionCall.js";
2
3
  var websockets_default = (serverName = "") => {
3
4
  return {
4
5
  send: (payload = {}, uniqueConnectionId = "") => {
5
6
  const emitterName = uniqueConnectionId ? `${serverName}_${uniqueConnectionId}` : serverName;
7
+ trackFunctionCall(`node.websockets.${serverName}.send`, [
8
+ payload
9
+ ]);
6
10
  emitWebsocketEvent(emitterName, "message", payload);
7
11
  }
8
12
  };
@@ -0,0 +1,43 @@
1
+ export default (context = {}) => {
2
+ const sanitizedContext = { ...context };
3
+
4
+ if (sanitizedContext?.req) {
5
+ delete sanitizedContext.req;
6
+ sanitizedContext.req = {
7
+ method: context?.req?.method,
8
+ headers: context?.req?.headers,
9
+ url: context?.req?.url,
10
+ };
11
+ }
12
+
13
+ if (sanitizedContext?.res) {
14
+ delete sanitizedContext.res;
15
+ sanitizedContext.res = {
16
+ method: context?.res?.method,
17
+ headers: context?.res?.headers,
18
+ url: context?.res?.url,
19
+ };
20
+ }
21
+
22
+ if (sanitizedContext.mongodb) {
23
+ delete sanitizedContext.mongodb;
24
+ }
25
+
26
+ if (sanitizedContext.postgresql) {
27
+ delete sanitizedContext.postgresql;
28
+ }
29
+
30
+ if (sanitizedContext.redis) {
31
+ delete sanitizedContext.redis;
32
+ }
33
+
34
+ if (sanitizedContext._users) {
35
+ delete sanitizedContext._users;
36
+ }
37
+
38
+ if (sanitizedContext._queues) {
39
+ delete sanitizedContext._queues;
40
+ }
41
+
42
+ return sanitizedContext;
43
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@joystick.js/node-canary",
3
- "version": "0.0.0-canary.32",
3
+ "version": "0.0.0-canary.321",
4
4
  "type": "module",
5
5
  "description": "A Node.js framework for building web apps.",
6
6
  "main": "./dist/index.js",
@@ -16,7 +16,6 @@
16
16
  "author": "",
17
17
  "license": "SAUCR",
18
18
  "dependencies": {
19
- "@tuskdb/node": "^0.11.0",
20
19
  "aws-sdk": "^2.1046.0",
21
20
  "bcrypt": "^5.0.1",
22
21
  "chalk": "^4.1.2",
@@ -38,6 +37,7 @@
38
37
  "node-html-parser": "^5.1.0",
39
38
  "nodemailer": "^6.7.0",
40
39
  "pg": "^8.7.3",
40
+ "pg-escape": "^0.2.0",
41
41
  "process": "^0.11.10",
42
42
  "query-string": "^7.0.1",
43
43
  "sanitize-html": "^2.7.3",
@@ -1,123 +0,0 @@
1
- import { jest } from "@jest/globals";
2
- import { killPortProcess } from "kill-port-process";
3
- import setAppSettingsForTest from "../../../tests/lib/setAppSettingsForTest";
4
- import startTestDatabase from "../../../tests/lib/databases/start";
5
- import stopTestDatabase from "../../../tests/lib/databases/stop";
6
- import { beforeEach, expect, test } from "@jest/globals";
7
- import roles from "./index";
8
- import accounts from "../index";
9
- jest.mock("../../../../node_modules/dayjs", () => {
10
- const _dayjs = jest.requireActual("../../../../node_modules/dayjs");
11
- const _utc = jest.requireActual("../../../../node_modules/dayjs/plugin/utc");
12
- _dayjs.extend(_utc);
13
- return () => _dayjs("2022-01-01T00:00:00.000Z");
14
- });
15
- setAppSettingsForTest({
16
- "config": {
17
- "databases": [
18
- {
19
- "provider": "mongodb",
20
- "users": true,
21
- "options": {}
22
- }
23
- ],
24
- "i18n": {
25
- "defaultLanguage": "en-US"
26
- },
27
- "middleware": {},
28
- "email": {
29
- "from": "app@test.com",
30
- "smtp": {
31
- "host": "fake.email.com",
32
- "port": 587,
33
- "username": "test",
34
- "password": "password"
35
- }
36
- }
37
- },
38
- "global": {},
39
- "public": {},
40
- "private": {}
41
- });
42
- global.joystick = {
43
- settings: {
44
- config: {
45
- databases: [{
46
- "provider": "mongodb",
47
- "users": true,
48
- "options": {}
49
- }]
50
- }
51
- }
52
- };
53
- const app = (await import("../../index")).default;
54
- let instance;
55
- describe("app/accounts/roles/index.js", () => {
56
- beforeAll(async () => {
57
- process.env.PORT = 3600;
58
- });
59
- beforeEach(async () => {
60
- instance = await app({});
61
- });
62
- afterEach(async () => {
63
- if (instance?.server?.close && typeof instance.server.close === "function") {
64
- instance.server.close();
65
- }
66
- await killPortProcess(process.env.PORT);
67
- });
68
- afterAll(async () => {
69
- });
70
- test("roles.add adds role to roles collection in database", async () => {
71
- await roles.add("admin");
72
- const roleExists = await process.databases.mongodb.collection("roles").findOne({ role: "admin" });
73
- expect(!!roleExists).toBe(true);
74
- });
75
- test("roles.remove removes role from roles collection in database", async () => {
76
- await roles.add("admin");
77
- await roles.remove("admin");
78
- const roleExists = await process.databases.mongodb.collection("roles").findOne({ role: "admin" });
79
- expect(!!roleExists).toBe(false);
80
- });
81
- test("roles.list returns a list of roles in the roles collection in database", async () => {
82
- await roles.add("admin");
83
- await roles.add("manager");
84
- await roles.add("employee");
85
- const rolesInDatabase = await roles.list();
86
- await roles.remove("admin");
87
- await roles.remove("manager");
88
- await roles.remove("employee");
89
- expect(rolesInDatabase).toEqual(["admin", "manager", "employee"]);
90
- });
91
- test("roles.grant adds role to user in users collection in database", async () => {
92
- await process.databases.mongodb.collection("users").deleteOne({ emailAddress: "test@test.com" });
93
- const user = await accounts.signup({ emailAddress: "test@test.com", password: "password" });
94
- await roles.grant(user?.userId, "admin");
95
- await roles.grant(user?.userId, "rolethatdoesntexist");
96
- const userAfterGrant = await process.databases.mongodb.collection("users").findOne({ _id: user?.userId });
97
- const rolesCreated = await process.databases.mongodb.collection("roles").find().toArray();
98
- expect(userAfterGrant?.roles?.includes("admin")).toBe(true);
99
- expect(rolesCreated?.length).toBe(2);
100
- });
101
- test("roles.revoke removes role from user in users collection in database", async () => {
102
- await process.databases.mongodb.collection("users").deleteOne({ emailAddress: "test@test.com" });
103
- const user = await accounts.signup({ emailAddress: "test@test.com", password: "password" });
104
- await roles.grant(user?.userId, "admin");
105
- await roles.revoke(user?.userId, "admin");
106
- const userAfterGrant = await process.databases.mongodb.collection("users").findOne({ _id: user?.userId });
107
- expect(userAfterGrant?.roles?.includes("admin")).toBe(false);
108
- });
109
- test("roles.userHasRole returns true if user has role", async () => {
110
- await process.databases.mongodb.collection("users").deleteOne({ emailAddress: "test@test.com" });
111
- const user = await accounts.signup({ emailAddress: "test@test.com", password: "password" });
112
- await roles.grant(user?.userId, "admin");
113
- const userHasRole = await roles.userHasRole(user?.userId, "admin");
114
- expect(userHasRole).toBe(true);
115
- });
116
- test("roles.userHasRole returns false if user does not have role", async () => {
117
- await process.databases.mongodb.collection("users").deleteOne({ emailAddress: "test@test.com" });
118
- const user = await accounts.signup({ emailAddress: "test@test.com", password: "password" });
119
- await roles.grant(user?.userId, "admin");
120
- const userHasRole = await roles.userHasRole(user?.userId, "manager");
121
- expect(userHasRole).toBe(false);
122
- });
123
- });