@contentstack/datasync-mongodb-sdk 1.0.10 → 1.0.12

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,31 @@
1
+ name: Create Jira Ticket for Github Issue
2
+
3
+ on:
4
+ issues:
5
+ types: [opened]
6
+
7
+ jobs:
8
+ issue-jira:
9
+ runs-on: ubuntu-latest
10
+ steps:
11
+
12
+ - name: Login to Jira
13
+ uses: atlassian/gajira-login@master
14
+ env:
15
+ JIRA_BASE_URL: ${{ secrets.JIRA_BASE_URL }}
16
+ JIRA_USER_EMAIL: ${{ secrets.JIRA_USER_EMAIL }}
17
+ JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }}
18
+
19
+ - name: Create Jira Issue
20
+ id: create_jira
21
+ uses: atlassian/gajira-create@master
22
+ with:
23
+ project: ${{ secrets.JIRA_PROJECT }}
24
+ issuetype: ${{ secrets.JIRA_ISSUE_TYPE }}
25
+ summary: Github | Issue | ${{ github.event.repository.name }} | ${{ github.event.issue.title }}
26
+ description: |
27
+ *GitHub Issue:* ${{ github.event.issue.html_url }}
28
+
29
+ *Description:*
30
+ ${{ github.event.issue.body }}
31
+ fields: "${{ secrets.ISSUES_JIRA_FIELDS }}"
@@ -0,0 +1,46 @@
1
+ name: Checks the security policy and configurations
2
+ on:
3
+ pull_request:
4
+ types: [opened, synchronize, reopened]
5
+ jobs:
6
+ security-policy:
7
+ if: github.event.repository.visibility == 'public'
8
+ runs-on: ubuntu-latest
9
+ defaults:
10
+ run:
11
+ shell: bash
12
+ steps:
13
+ - uses: actions/checkout@master
14
+ - name: Checks for SECURITY.md policy file
15
+ run: |
16
+ if ! [[ -f "SECURITY.md" || -f ".github/SECURITY.md" ]]; then exit 1; fi
17
+ security-license:
18
+ if: github.event.repository.visibility == 'public'
19
+ runs-on: ubuntu-latest
20
+ defaults:
21
+ run:
22
+ shell: bash
23
+ steps:
24
+ - uses: actions/checkout@master
25
+ - name: Checks for License file
26
+ run: |
27
+ expected_license_files=("LICENSE" "LICENSE.txt" "LICENSE.md" "License.txt")
28
+ license_file_found=false
29
+ current_year=$(date +"%Y")
30
+
31
+ for license_file in "${expected_license_files[@]}"; do
32
+ if [ -f "$license_file" ]; then
33
+ license_file_found=true
34
+ # check the license file for the current year, if not exists, exit with error
35
+ if ! grep -q "$current_year" "$license_file"; then
36
+ echo "License file $license_file does not contain the current year."
37
+ exit 2
38
+ fi
39
+ break
40
+ fi
41
+ done
42
+
43
+ if [ "$license_file_found" = false ]; then
44
+ echo "No license file found. Please add a license file to the repository."
45
+ exit 1
46
+ fi
@@ -28,12 +28,12 @@ jobs:
28
28
  uses: codex-team/action-nodejs-package-info@v1.1
29
29
 
30
30
  # Install npm-pack-all to create a package archive
31
- - name: Install npm-pack-all
32
- run: npm install npm-pack-all
33
-
31
+ - name: Install npm pack
32
+ run: npm install npm-pack
33
+
34
34
  # Pack the package into a .tgz archive
35
35
  - name: Pack the npm package
36
- run: node node_modules/.bin/npm-pack-all
36
+ run: npm pack
37
37
 
38
38
  # Publish the package to npm
39
39
  - name: Publish to npm
@@ -0,0 +1,69 @@
1
+ #!/usr/bin/env sh
2
+ # Pre-commit hook to run Snyk and Talisman scans, completing both before deciding to commit
3
+
4
+ # Function to check if a command exists
5
+ command_exists() {
6
+ command -v "$1" >/dev/null 2>&1
7
+ }
8
+
9
+ # Check if Snyk is installed
10
+ if ! command_exists snyk; then
11
+ echo "Error: Snyk is not installed. Please install it and try again."
12
+ exit 1
13
+ fi
14
+
15
+ # Check if Talisman is installed
16
+ if ! command_exists talisman; then
17
+ echo "Error: Talisman is not installed. Please install it and try again."
18
+ exit 1
19
+ fi
20
+
21
+ # Allow bypassing the hook with an environment variable
22
+ if [ "$SKIP_HOOK" = "1" ]; then
23
+ echo "Skipping Snyk and Talisman scans (SKIP_HOOK=1)."
24
+ exit 0
25
+ fi
26
+
27
+ # Initialize variables to track scan results
28
+ snyk_failed=false
29
+ talisman_failed=false
30
+
31
+ # Run Snyk vulnerability scan
32
+ echo "Running Snyk vulnerability scan..."
33
+ snyk test --all-projects > snyk_output.log 2>&1
34
+ snyk_exit_code=$?
35
+
36
+ if [ $snyk_exit_code -eq 0 ]; then
37
+ echo "Snyk scan passed: No vulnerabilities found."
38
+ elif [ $snyk_exit_code -eq 1 ]; then
39
+ echo "Snyk found vulnerabilities. See snyk_output.log for details."
40
+ snyk_failed=true
41
+ else
42
+ echo "Snyk scan failed with error (exit code $snyk_exit_code). See snyk_output.log for details."
43
+ snyk_failed=true
44
+ fi
45
+
46
+ # Run Talisman secret scan (continues even if Snyk failed)
47
+ echo "Running Talisman secret scan..."
48
+ talisman --githook pre-commit > talisman_output.log 2>&1
49
+ talisman_exit_code=$?
50
+
51
+ if [ $talisman_exit_code -eq 0 ]; then
52
+ echo "Talisman scan passed: No secrets found."
53
+ else
54
+ echo "Talisman scan failed (exit code $talisman_exit_code). See talisman_output.log for details."
55
+ talisman_failed=true
56
+ fi
57
+
58
+ # Evaluate results after both scans
59
+ if [ "$snyk_failed" = true ] || [ "$talisman_failed" = true ]; then
60
+ echo "Commit aborted due to issues found in one or both scans."
61
+ [ "$snyk_failed" = true ] && echo "- Snyk issues: Check snyk_output.log"
62
+ [ "$talisman_failed" = true ] && echo "- Talisman issues: Check talisman_output.log"
63
+ exit 1
64
+ fi
65
+
66
+ # If both scans pass, allow the commit
67
+ echo "All scans passed. Proceeding with commit.cd ."
68
+ rm -f snyk_output.log talisman_output.log
69
+ exit 0
package/.talismanrc CHANGED
@@ -1,4 +1,10 @@
1
1
  fileignoreconfig:
2
+ - filename: .github/workflows/secrets-scan.yml
3
+ ignore_detectors:
4
+ - filecontent
2
5
  - filename: package-lock.json
3
- checksum: a618ae6c113021eef425f224f1dfd7066b15af1a45249ea063a193517ce5a92f
4
- version: ""
6
+ checksum: 37742c5c56859fdfed906c14e97881c4a10e0a52464fda489e4eb8154298fde4
7
+ - filename: .husky/pre-commit
8
+ checksum: 5baabd7d2c391648163f9371f0e5e9484f8fb90fa2284cfc378732ec3192c193
9
+ version: ""
10
+ base64_entropy: false
package/CODEOWNERS CHANGED
@@ -1 +1,11 @@
1
- * @contentstack/security-admin
1
+ * @contentstack/devex-pr-reviewers
2
+
3
+ .github/workflows/sca-scan.yml @contentstack/security-admin
4
+
5
+ .github/workflows/codeql-anaylsis.yml @contentstack/security-admin
6
+
7
+ **/.snyk @contentstack/security-admin
8
+
9
+ .github/workflows/policy-scan.yml @contentstack/security-admin
10
+
11
+ .github/workflows/issues-jira.yml @contentstack/security-admin
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ /**
3
+ * Centralized error messages and warnings for the DataSync MongoDB SDK
4
+ * This file contains all user-facing messages for consistency and maintainability
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.WarningMessages = exports.ErrorMessages = void 0;
8
+ exports.ErrorMessages = {
9
+ // Configuration errors
10
+ INVALID_MONGODB_URI: (uri) => `MongoDB connection URL: ${uri} must be of type string`,
11
+ INVALID_DBNAME: 'Content store dbName should be of type string and not empty',
12
+ // Sorting errors
13
+ INVALID_ASCENDING_PARAMS: 'Invalid parameters for .ascending(). Expected a valid string field name',
14
+ INVALID_DESCENDING_PARAMS: 'Invalid parameters for .descending(). Expected a valid string field name',
15
+ // Language errors
16
+ INVALID_LANGUAGE_PARAMS: 'Invalid parameters for .language(). Expected a valid language code string',
17
+ // Logical operator errors
18
+ INVALID_AND_PARAMS: 'Invalid parameters for .and(). Expected an array of query objects',
19
+ INVALID_OR_PARAMS: 'Invalid parameters for .or(). Expected an array of query objects',
20
+ // Comparison operator errors
21
+ INVALID_LESSTHAN_PARAMS: 'Invalid key or value parameters for .lessThan(). Expected a string key and a value',
22
+ INVALID_LESSTHAN_OR_EQUAL_PARAMS: 'Invalid key or value parameters for .lessThanOrEqualTo(). Expected a string key and a value',
23
+ INVALID_GREATERTHAN_PARAMS: 'Invalid key or value parameters for .greaterThan(). Expected a string key and a value',
24
+ INVALID_GREATERTHAN_OR_EQUAL_PARAMS: 'Invalid key or value parameters for .greaterThanOrEqualTo(). Expected a string key and a value',
25
+ INVALID_NOTEQUAL_PARAMS: 'Invalid key or value parameters for .notEqualTo(). Expected a string key and a value',
26
+ INVALID_CONTAINED_IN_PARAMS: 'Invalid key or value parameters for .containedIn(). Expected a string key and an array value',
27
+ INVALID_NOT_CONTAINED_IN_PARAMS: 'Invalid key or value parameters for .notContainedIn(). Expected a string key and an array value',
28
+ INVALID_EXISTS_PARAMS: 'Invalid key parameter for .exists(). Expected a valid string field name',
29
+ INVALID_NOT_EXISTS_PARAMS: 'Invalid key parameter for .notExists(). Expected a valid string field name',
30
+ // Content type errors
31
+ MISSING_CONTENT_TYPE_UID: 'Content type UID is required. Please provide a valid content type UID',
32
+ MISSING_CONTENT_TYPE_FOR_ENTRY: 'Please call .contentType() before .entry()',
33
+ MISSING_CONTENT_TYPE_FOR_ENTRIES: 'Please call .contentType() before .entries()',
34
+ // Pagination errors
35
+ INVALID_LIMIT_VALUE: 'Invalid value for .limit(). Expected a positive numeric value',
36
+ INVALID_SKIP_VALUE: 'Invalid value for .skip(). Expected a non-negative numeric value',
37
+ // Projection errors
38
+ INVALID_ONLY_PARAMS: 'Invalid field values for .only(). Expected a non-empty array of field names',
39
+ INVALID_EXCEPT_PARAMS: 'Invalid field values for .except(). Expected a non-empty array of field names',
40
+ // Query errors
41
+ INVALID_REGEX_PARAMS: 'Invalid field or pattern parameters for .regex(). Expected string values for both field and pattern',
42
+ INVALID_TAGS_PARAMS: 'Invalid field values for .tags(). Expected an array of tag values',
43
+ INVALID_WHERE_PARAMS: 'Invalid expression for .where(). Expected a valid expression or function',
44
+ INVALID_QUERY_REFERENCES_PARAMS: 'Invalid query object for .queryReferences(). Expected a valid query object',
45
+ INVALID_INCLUDE_PARAMS: 'Invalid reference field path for .include(). Expected a valid string or array of strings',
46
+ // Query validation errors
47
+ INVALID_QUERY: 'Invalid query provided. Please ensure your query is properly formatted',
48
+ INVALID_QUERIES: 'Invalid queries provided. Please ensure all queries are properly formatted',
49
+ };
50
+ exports.WarningMessages = {
51
+ // Performance warnings
52
+ SLOW_INCLUDE_REFERENCES: '.includeReferences(...) is a relatively slow query. Consider limiting the depth or using .include() for specific references',
53
+ };
package/dist/stack.js CHANGED
@@ -23,6 +23,7 @@ const mongodb_1 = require("mongodb");
23
23
  const sift_1 = __importDefault(require("sift"));
24
24
  const config_1 = require("./config");
25
25
  const util_1 = require("./util");
26
+ const messages_1 = require("./messages");
26
27
  /**
27
28
  * @class Stack
28
29
  * @descriptionExpose SDK query methods on Stack
@@ -67,7 +68,7 @@ class Stack {
67
68
  */
68
69
  ascending(field) {
69
70
  if (typeof this.q.content_type_uid !== 'string' || typeof field !== 'string' || field.length === 0) {
70
- throw new Error('Kindly provide valid parameters for .ascending!');
71
+ throw new Error(messages_1.ErrorMessages.INVALID_ASCENDING_PARAMS);
71
72
  }
72
73
  else if (this.internal.sort && typeof this.internal.sort === 'object') {
73
74
  this.internal.sort[field] = 1;
@@ -105,7 +106,7 @@ class Stack {
105
106
  */
106
107
  descending(field) {
107
108
  if (typeof this.q.content_type_uid !== 'string' || typeof field !== 'string' || field.length === 0) {
108
- throw new Error('Kindly provide valid parameters for .descending()!');
109
+ throw new Error(messages_1.ErrorMessages.INVALID_DESCENDING_PARAMS);
109
110
  }
110
111
  else if (this.internal.sort && typeof this.internal.sort === 'object') {
111
112
  this.internal.sort[field] = -1;
@@ -182,7 +183,7 @@ class Stack {
182
183
  */
183
184
  language(code) {
184
185
  if (typeof code !== 'string' || code.length === 0) {
185
- throw new Error('Kindly pass valid parameters for .language()!');
186
+ throw new Error(messages_1.ErrorMessages.INVALID_LANGUAGE_PARAMS);
186
187
  }
187
188
  this.q.locale = code;
188
189
  return this;
@@ -217,7 +218,7 @@ class Stack {
217
218
  */
218
219
  and(queries) {
219
220
  if (typeof queries !== 'object' || !Array.isArray(queries)) {
220
- throw new Error('Kindly provide valid parameters for .and()!');
221
+ throw new Error(messages_1.ErrorMessages.INVALID_AND_PARAMS);
221
222
  }
222
223
  else if (this.q.query && typeof this.q.query === 'object') {
223
224
  this.q.query = (0, lodash_1.merge)(this.q.query, {
@@ -261,7 +262,7 @@ class Stack {
261
262
  */
262
263
  or(queries) {
263
264
  if (typeof queries !== 'object' || !Array.isArray(queries)) {
264
- throw new Error('Kindly provide valid parameters for .or()!');
265
+ throw new Error(messages_1.ErrorMessages.INVALID_OR_PARAMS);
265
266
  }
266
267
  else if (this.q.query && typeof this.q.query === 'object') {
267
268
  this.q.query = (0, lodash_1.merge)(this.q.query, {
@@ -303,7 +304,7 @@ class Stack {
303
304
  */
304
305
  lessThan(key, value) {
305
306
  if (typeof key !== 'string' || typeof value === 'undefined') {
306
- throw new Error('Kindly pass valid key and value parameters for \'.lessThan()\'');
307
+ throw new Error(messages_1.ErrorMessages.INVALID_LESSTHAN_PARAMS);
307
308
  }
308
309
  else if (this.q.query && typeof this.q.query === 'object') {
309
310
  this.q.query[key] = {
@@ -347,7 +348,7 @@ class Stack {
347
348
  */
348
349
  lessThanOrEqualTo(key, value) {
349
350
  if (typeof key !== 'string' || typeof value === 'undefined') {
350
- throw new Error('Kindly pass valid key and value parameters for \'.lessThanOrEqualTo()\'');
351
+ throw new Error(messages_1.ErrorMessages.INVALID_LESSTHAN_OR_EQUAL_PARAMS);
351
352
  }
352
353
  else if (this.q.query && typeof this.q.query === 'object') {
353
354
  this.q.query[key] = {
@@ -391,7 +392,7 @@ class Stack {
391
392
  */
392
393
  greaterThan(key, value) {
393
394
  if (typeof key !== 'string' || typeof value === 'undefined') {
394
- throw new Error('Kindly pass valid key and value parameters for \'.greaterThan()\'');
395
+ throw new Error(messages_1.ErrorMessages.INVALID_GREATERTHAN_PARAMS);
395
396
  }
396
397
  else if (this.q.query && typeof this.q.query === 'object') {
397
398
  this.q.query[key] = {
@@ -435,7 +436,7 @@ class Stack {
435
436
  */
436
437
  greaterThanOrEqualTo(key, value) {
437
438
  if (typeof key !== 'string' || typeof value === 'undefined') {
438
- throw new Error('Kindly pass valid key and value parameters for \'.greaterThanOrEqualTo()\'');
439
+ throw new Error(messages_1.ErrorMessages.INVALID_GREATERTHAN_OR_EQUAL_PARAMS);
439
440
  }
440
441
  else if (this.q.query && typeof this.q.query === 'object') {
441
442
  this.q.query[key] = {
@@ -484,7 +485,7 @@ class Stack {
484
485
  */
485
486
  notEqualTo(key, value) {
486
487
  if (typeof key !== 'string' || typeof value === 'undefined') {
487
- throw new Error('Kindly pass valid key and value parameters for \'.notEqualTo()\'');
488
+ throw new Error(messages_1.ErrorMessages.INVALID_NOTEQUAL_PARAMS);
488
489
  }
489
490
  else if (this.q.query && typeof this.q.query === 'object') {
490
491
  this.q.query[key] = {
@@ -534,7 +535,7 @@ class Stack {
534
535
  */
535
536
  containedIn(key, value) {
536
537
  if (typeof key !== 'string' || typeof value !== 'object' || !(value instanceof Array)) {
537
- throw new Error('Kindly pass valid key and value parameters for \'.containedIn()\'');
538
+ throw new Error(messages_1.ErrorMessages.INVALID_CONTAINED_IN_PARAMS);
538
539
  }
539
540
  else if (this.q.query && typeof this.q.query === 'object') {
540
541
  this.q.query[key] = {
@@ -584,7 +585,7 @@ class Stack {
584
585
  */
585
586
  notContainedIn(key, value) {
586
587
  if (typeof key !== 'string' || typeof value !== 'object' || !(value instanceof Array)) {
587
- throw new Error('Kindly pass valid key and value parameters for \'.notContainedIn()\'');
588
+ throw new Error(messages_1.ErrorMessages.INVALID_NOT_CONTAINED_IN_PARAMS);
588
589
  }
589
590
  else if (this.q.query && typeof this.q.query === 'object') {
590
591
  this.q.query[key] = {
@@ -634,7 +635,7 @@ class Stack {
634
635
  */
635
636
  exists(key) {
636
637
  if (typeof key !== 'string') {
637
- throw new Error('Kindly pass valid key for \'.exists()\'');
638
+ throw new Error(messages_1.ErrorMessages.INVALID_EXISTS_PARAMS);
638
639
  }
639
640
  else if (this.q.query && typeof this.q.query === 'object') {
640
641
  this.q.query[key] = {
@@ -683,7 +684,7 @@ class Stack {
683
684
  */
684
685
  notExists(key) {
685
686
  if (typeof key !== 'string') {
686
- throw new Error('Kindly pass valid key for \'.notExists()\'');
687
+ throw new Error(messages_1.ErrorMessages.INVALID_NOT_EXISTS_PARAMS);
687
688
  }
688
689
  else if (this.q.query && typeof this.q.query === 'object') {
689
690
  this.q.query[key] = {
@@ -725,7 +726,7 @@ class Stack {
725
726
  stack.q.content_type_uid = uid;
726
727
  return stack;
727
728
  }
728
- throw new Error('Kindly pass the content type\'s uid');
729
+ throw new Error(messages_1.ErrorMessages.MISSING_CONTENT_TYPE_UID);
729
730
  }
730
731
  /**
731
732
  * @public
@@ -751,7 +752,7 @@ class Stack {
751
752
  */
752
753
  entry(uid) {
753
754
  if (!(this.q.content_type_uid)) {
754
- throw new Error('Kindly call \'contentType()\' before \'entry()\'!');
755
+ throw new Error(messages_1.ErrorMessages.MISSING_CONTENT_TYPE_FOR_ENTRY);
755
756
  }
756
757
  if (uid && typeof uid === 'string') {
757
758
  this.q.query = this.q.query || {};
@@ -785,7 +786,7 @@ class Stack {
785
786
  if (this.q.content_type_uid && typeof this.q.content_type_uid === 'string') {
786
787
  return this;
787
788
  }
788
- throw new Error('Kindly call \'contentType()\' before \'entries()\'!');
789
+ throw new Error(messages_1.ErrorMessages.MISSING_CONTENT_TYPE_FOR_ENTRIES);
789
790
  }
790
791
  /**
791
792
  * @public
@@ -956,7 +957,7 @@ class Stack {
956
957
  this.internal.limit = no;
957
958
  return this;
958
959
  }
959
- throw new Error('Kindly provide a valid \'numeric\' value for \'limit()\'');
960
+ throw new Error(messages_1.ErrorMessages.INVALID_LIMIT_VALUE);
960
961
  }
961
962
  /**
962
963
  * @public
@@ -987,7 +988,7 @@ class Stack {
987
988
  this.internal.skip = no;
988
989
  return this;
989
990
  }
990
- throw new Error('Kindly provide a valid \'numeric\' value for \'skip()\'');
991
+ throw new Error(messages_1.ErrorMessages.INVALID_SKIP_VALUE);
991
992
  }
992
993
  /**
993
994
  * @public
@@ -1045,7 +1046,7 @@ class Stack {
1045
1046
  */
1046
1047
  only(fields) {
1047
1048
  if (!fields || typeof fields !== 'object' || !(fields instanceof Array) || fields.length === 0) {
1048
- throw new Error('Kindly provide valid \'field\' values for \'only()\'');
1049
+ throw new Error(messages_1.ErrorMessages.INVALID_ONLY_PARAMS);
1049
1050
  }
1050
1051
  this.internal.only = this.internal.only || {};
1051
1052
  this.internal.only._id = 0;
@@ -1080,7 +1081,7 @@ class Stack {
1080
1081
  */
1081
1082
  except(fields) {
1082
1083
  if (!fields || typeof fields !== 'object' || !(fields instanceof Array) || fields.length === 0) {
1083
- throw new Error('Kindly provide valid \'field\' values for \'except()\'');
1084
+ throw new Error(messages_1.ErrorMessages.INVALID_EXCEPT_PARAMS);
1084
1085
  }
1085
1086
  this.internal.except = this.internal.except || {};
1086
1087
  fields.forEach((field) => {
@@ -1117,7 +1118,7 @@ class Stack {
1117
1118
  */
1118
1119
  regex(field, pattern, options = 'i') {
1119
1120
  if (!(field) || !(pattern) || typeof field !== 'string' || typeof pattern !== 'string') {
1120
- throw new Error('Kindly provide a valid field and pattern value for \'.regex()\'');
1121
+ throw new Error(messages_1.ErrorMessages.INVALID_REGEX_PARAMS);
1121
1122
  }
1122
1123
  else if (this.q.query && typeof this.q.query === 'object') {
1123
1124
  this.q.query = (0, lodash_1.merge)(this.q.query, {
@@ -1160,7 +1161,7 @@ class Stack {
1160
1161
  */
1161
1162
  tags(values) {
1162
1163
  if (!values || typeof values !== 'object' || !(values instanceof Array)) {
1163
- throw new Error('Kindly provide valid \'field\' values for \'tags()\'');
1164
+ throw new Error(messages_1.ErrorMessages.INVALID_TAGS_PARAMS);
1164
1165
  }
1165
1166
  // filter non-string keys
1166
1167
  (0, lodash_1.remove)(values, (value) => {
@@ -1213,7 +1214,7 @@ class Stack {
1213
1214
  */
1214
1215
  where(expr) {
1215
1216
  if (!(expr)) {
1216
- throw new Error('Kindly provide a valid field and expr/fn value for \'.where()\'');
1217
+ throw new Error(messages_1.ErrorMessages.INVALID_WHERE_PARAMS);
1217
1218
  }
1218
1219
  else if (this.q.query && typeof this.q.query === 'object') {
1219
1220
  if (typeof expr === 'function') {
@@ -1360,7 +1361,7 @@ class Stack {
1360
1361
  this.internal.queryReferences = query;
1361
1362
  return this;
1362
1363
  }
1363
- throw new Error('Kindly pass a query object for \'.queryReferences()\'');
1364
+ throw new Error(messages_1.ErrorMessages.INVALID_QUERY_REFERENCES_PARAMS);
1364
1365
  }
1365
1366
  /**
1366
1367
  * @public
@@ -1393,7 +1394,7 @@ class Stack {
1393
1394
  * @returns {Stack} Returns 'this' instance (of Stack)
1394
1395
  */
1395
1396
  includeReferences(depth) {
1396
- console.warn('.includeReferences() is a relatively slow query..!');
1397
+ console.warn(messages_1.WarningMessages.SLOW_INCLUDE_REFERENCES);
1397
1398
  if (typeof depth === 'number') {
1398
1399
  this.q.referenceDepth = depth;
1399
1400
  }
@@ -1415,7 +1416,7 @@ class Stack {
1415
1416
  */
1416
1417
  include(fields) {
1417
1418
  if (fields.length === 0) {
1418
- throw new Error('Kindly pass a valid reference field path to \'.include()\' ');
1419
+ throw new Error(messages_1.ErrorMessages.INVALID_INCLUDE_PARAMS);
1419
1420
  }
1420
1421
  else if (typeof fields === 'string') {
1421
1422
  this.internal.includeSpecificReferences = [fields];
@@ -1815,8 +1816,9 @@ class Stack {
1815
1816
  return this.includeReferenceIteration(queries, schemaList, locale, pendingPath, shelf);
1816
1817
  });
1817
1818
  }
1818
- fetchPathDetails(data, locale, pathArr, queryBucket, shelf, assetsOnly = false, parent, pos, counter = 0) {
1819
+ fetchPathDetails(data = {}, locale, pathArr, queryBucket, shelf = [], assetsOnly = false, parent = {}, pos, counter = 0) {
1819
1820
  if (counter === (pathArr.length)) {
1821
+ queryBucket = this.sanitizeQueryBucket(queryBucket);
1820
1822
  if (data && typeof data === 'object') {
1821
1823
  if (data instanceof Array && data.length) {
1822
1824
  data.forEach((elem, idx) => {
@@ -1883,13 +1885,13 @@ class Stack {
1883
1885
  // tslint:disable-next-line: prefer-for-of
1884
1886
  for (let i = 0; i < data.length; i++) {
1885
1887
  if (data[i][currentField]) {
1886
- this.fetchPathDetails(data[i][currentField], locale, pathArr, queryBucket, shelf, assetsOnly, data[i], currentField, counter);
1888
+ this.fetchPathDetails(data[i][currentField], locale, pathArr, this.sanitizeQueryBucket(queryBucket), shelf, assetsOnly, data[i], currentField, counter);
1887
1889
  }
1888
1890
  }
1889
1891
  }
1890
1892
  else {
1891
1893
  if (data[currentField]) {
1892
- this.fetchPathDetails(data[currentField], locale, pathArr, queryBucket, shelf, assetsOnly, data, currentField, counter);
1894
+ this.fetchPathDetails(data[currentField], locale, pathArr, this.sanitizeQueryBucket(queryBucket), shelf, assetsOnly, data, currentField, counter);
1893
1895
  }
1894
1896
  }
1895
1897
  }
@@ -1899,14 +1901,12 @@ class Stack {
1899
1901
  bindLeftoverAssets(queries, locale, pointerList) {
1900
1902
  return __awaiter(this, void 0, void 0, function* () {
1901
1903
  // const contents = await readFile(getAssetsPath(locale) + '.json')
1902
- if (!this.sanitizeIQuery(queries)) {
1903
- throw new Error('Invalid queries provided');
1904
- }
1904
+ const queriesSanitize = this.sanitizeQueryBucket(queries);
1905
1905
  const filteredAssets = yield this.db.collection((0, util_1.getCollectionName)({
1906
1906
  content_type_uid: this.types.assets,
1907
1907
  locale,
1908
1908
  }, this.collectionNames))
1909
- .find(queries)
1909
+ .find(queriesSanitize)
1910
1910
  .project({
1911
1911
  _content_type_uid: 0,
1912
1912
  _id: 0,
@@ -1966,13 +1966,14 @@ class Stack {
1966
1966
  getReferencePath(query, locale, currentInclude) {
1967
1967
  return __awaiter(this, void 0, void 0, function* () {
1968
1968
  if (!this.sanityQueryAny(query)) {
1969
- throw new Error('Invalid query provided');
1969
+ throw new Error(messages_1.ErrorMessages.INVALID_QUERY);
1970
1970
  }
1971
+ const querySanitize = this.sanitizeQueryBucket(query);
1971
1972
  const schemas = yield this.db.collection((0, util_1.getCollectionName)({
1972
1973
  content_type_uid: this.types.content_types,
1973
1974
  locale,
1974
1975
  }, this.collectionNames))
1975
- .find(query)
1976
+ .find(querySanitize)
1976
1977
  .project({
1977
1978
  _assets: 1,
1978
1979
  _id: 0,
@@ -2053,13 +2054,14 @@ class Stack {
2053
2054
  fetchEntries(query, locale, paths, include, includeAll = false) {
2054
2055
  return __awaiter(this, void 0, void 0, function* () {
2055
2056
  if (!this.sanitizeIQuery(query)) {
2056
- throw new Error('Invalid queries provided');
2057
+ throw new Error(messages_1.ErrorMessages.INVALID_QUERIES);
2057
2058
  }
2059
+ const sanitizeQuery = this.sanitizeQueryBucket(query);
2058
2060
  const result = yield this.db.collection((0, util_1.getCollectionName)({
2059
2061
  content_type_uid: 'entries',
2060
2062
  locale,
2061
2063
  }, this.collectionNames))
2062
- .find(query)
2064
+ .find(sanitizeQuery)
2063
2065
  .project({
2064
2066
  _content_type_uid: 0,
2065
2067
  _id: 0,
@@ -2114,7 +2116,7 @@ class Stack {
2114
2116
  // iterate over each path in the entries and fetch the references
2115
2117
  // while fetching, keep track of their location
2116
2118
  for (let i = 0, j = paths.length; i < j; i++) {
2117
- this.fetchPathDetails(entries, locale, paths[i].split('.'), queries, objectPointerList, true, entries, 0);
2119
+ this.fetchPathDetails(entries, locale, paths[i].split('.'), this.sanitizeQueryBucket(queries), objectPointerList, true, entries, 0);
2118
2120
  }
2119
2121
  // even after traversing, if no references were found, simply return the entries found thusfar
2120
2122
  if (objectPointerList.length === 0) {
@@ -2268,5 +2270,40 @@ class Stack {
2268
2270
  }
2269
2271
  return true;
2270
2272
  }
2273
+ sanitizeQueryBucket(queryBucket) {
2274
+ if (!queryBucket || typeof queryBucket !== 'object') {
2275
+ return { $or: [{ _id: { $exists: true } }] };
2276
+ }
2277
+ const sanitized = { $or: [] };
2278
+ if (!Array.isArray(queryBucket.$or)) {
2279
+ return { $or: [{ _id: { $exists: true } }] };
2280
+ }
2281
+ for (const item of queryBucket.$or) {
2282
+ if (!item || typeof item !== 'object') {
2283
+ continue;
2284
+ }
2285
+ const safeItem = {};
2286
+ if (typeof item._content_type_uid === 'string') {
2287
+ safeItem._content_type_uid = item._content_type_uid;
2288
+ }
2289
+ if (typeof item.uid === 'string') {
2290
+ safeItem.uid = item.uid;
2291
+ }
2292
+ if (typeof item.locale === 'string') {
2293
+ safeItem.locale = item.locale;
2294
+ }
2295
+ if (item._version && typeof item._version === 'object' &&
2296
+ typeof item._version.$exists === 'boolean') {
2297
+ safeItem._version = { $exists: item._version.$exists };
2298
+ }
2299
+ if (safeItem._content_type_uid && safeItem.uid) {
2300
+ sanitized.$or.push(safeItem);
2301
+ }
2302
+ }
2303
+ if (sanitized.$or.length === 0) {
2304
+ return { $or: [{ _id: { $exists: true } }] };
2305
+ }
2306
+ return sanitized;
2307
+ }
2271
2308
  }
2272
2309
  exports.Stack = Stack;
package/dist/util.js CHANGED
@@ -7,6 +7,7 @@
7
7
  Object.defineProperty(exports, "__esModule", { value: true });
8
8
  exports.getCollectionName = exports.validateConfig = exports.checkCyclic = exports.validateURI = void 0;
9
9
  const lodash_1 = require("lodash");
10
+ const messages_1 = require("./messages");
10
11
  /**
11
12
  * @private
12
13
  * @method validateURI
@@ -17,7 +18,7 @@ const lodash_1 = require("lodash");
17
18
  */
18
19
  const validateURI = (uri) => {
19
20
  if (typeof uri !== 'string' || uri.length === 0) {
20
- throw new Error(`Mongodb connection url: ${uri} must be of type string`);
21
+ throw new Error(messages_1.ErrorMessages.INVALID_MONGODB_URI(uri));
21
22
  }
22
23
  return uri;
23
24
  };
@@ -56,7 +57,7 @@ const getParents = (child, mapping) => {
56
57
  };
57
58
  const validateContentStore = (contentStore) => {
58
59
  if (typeof contentStore.dbName !== 'string' || contentStore.dbName.length === 0) {
59
- throw new Error('Contentstore dbName should be of type string and not empty!');
60
+ throw new Error(messages_1.ErrorMessages.INVALID_DBNAME);
60
61
  }
61
62
  if (typeof contentStore.collectionName === 'string') {
62
63
  contentStore.collection = {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "author": "Contentstack Ecosystem <ecosystem@contentstack.com>",
3
3
  "name": "@contentstack/datasync-mongodb-sdk",
4
- "version": "1.0.10",
4
+ "version": "1.0.12",
5
5
  "description": "Mongodb query wrapper around contents synced via @contentstack/content-store-mongodb",
6
6
  "main": "dist/index.js",
7
7
  "scripts": {
@@ -11,6 +11,7 @@
11
11
  "watch-ts": "npm run clean && tsc -w",
12
12
  "compile": "tsc",
13
13
  "prepare": "npm run compile",
14
+ "pre-commit": "husky install && husky && chmod +x .husky/pre-commit",
14
15
  "start": "dist/index.js",
15
16
  "tslint": "npx tslint -c tslint.json 'src/**/*.ts' --fix",
16
17
  "test": "jest"
@@ -19,7 +20,7 @@
19
20
  "dependencies": {
20
21
  "lodash": "^4.17.21",
21
22
  "mongodb": "^6.12.0",
22
- "npm-pack-all": "^1.12.7",
23
+ "npm-pack": "^1.0.0",
23
24
  "sift": "^17.1.3"
24
25
  },
25
26
  "devDependencies": {
@@ -28,6 +29,7 @@
28
29
  "@types/node": "10.17.60",
29
30
  "@types/rimraf": "4.0.5",
30
31
  "debug": "^4.4.0",
32
+ "husky": "^9.1.7",
31
33
  "jest": "^29.7.0",
32
34
  "jsdoc": "^4.0.4",
33
35
  "node-notifier": "^10.0.1",
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Centralized error messages and warnings for the DataSync MongoDB SDK
3
+ * This file contains all user-facing messages for consistency and maintainability
4
+ */
5
+ export declare const ErrorMessages: {
6
+ readonly INVALID_MONGODB_URI: (uri: any) => string;
7
+ readonly INVALID_DBNAME: "Content store dbName should be of type string and not empty";
8
+ readonly INVALID_ASCENDING_PARAMS: "Invalid parameters for .ascending(). Expected a valid string field name";
9
+ readonly INVALID_DESCENDING_PARAMS: "Invalid parameters for .descending(). Expected a valid string field name";
10
+ readonly INVALID_LANGUAGE_PARAMS: "Invalid parameters for .language(). Expected a valid language code string";
11
+ readonly INVALID_AND_PARAMS: "Invalid parameters for .and(). Expected an array of query objects";
12
+ readonly INVALID_OR_PARAMS: "Invalid parameters for .or(). Expected an array of query objects";
13
+ readonly INVALID_LESSTHAN_PARAMS: "Invalid key or value parameters for .lessThan(). Expected a string key and a value";
14
+ readonly INVALID_LESSTHAN_OR_EQUAL_PARAMS: "Invalid key or value parameters for .lessThanOrEqualTo(). Expected a string key and a value";
15
+ readonly INVALID_GREATERTHAN_PARAMS: "Invalid key or value parameters for .greaterThan(). Expected a string key and a value";
16
+ readonly INVALID_GREATERTHAN_OR_EQUAL_PARAMS: "Invalid key or value parameters for .greaterThanOrEqualTo(). Expected a string key and a value";
17
+ readonly INVALID_NOTEQUAL_PARAMS: "Invalid key or value parameters for .notEqualTo(). Expected a string key and a value";
18
+ readonly INVALID_CONTAINED_IN_PARAMS: "Invalid key or value parameters for .containedIn(). Expected a string key and an array value";
19
+ readonly INVALID_NOT_CONTAINED_IN_PARAMS: "Invalid key or value parameters for .notContainedIn(). Expected a string key and an array value";
20
+ readonly INVALID_EXISTS_PARAMS: "Invalid key parameter for .exists(). Expected a valid string field name";
21
+ readonly INVALID_NOT_EXISTS_PARAMS: "Invalid key parameter for .notExists(). Expected a valid string field name";
22
+ readonly MISSING_CONTENT_TYPE_UID: "Content type UID is required. Please provide a valid content type UID";
23
+ readonly MISSING_CONTENT_TYPE_FOR_ENTRY: "Please call .contentType() before .entry()";
24
+ readonly MISSING_CONTENT_TYPE_FOR_ENTRIES: "Please call .contentType() before .entries()";
25
+ readonly INVALID_LIMIT_VALUE: "Invalid value for .limit(). Expected a positive numeric value";
26
+ readonly INVALID_SKIP_VALUE: "Invalid value for .skip(). Expected a non-negative numeric value";
27
+ readonly INVALID_ONLY_PARAMS: "Invalid field values for .only(). Expected a non-empty array of field names";
28
+ readonly INVALID_EXCEPT_PARAMS: "Invalid field values for .except(). Expected a non-empty array of field names";
29
+ readonly INVALID_REGEX_PARAMS: "Invalid field or pattern parameters for .regex(). Expected string values for both field and pattern";
30
+ readonly INVALID_TAGS_PARAMS: "Invalid field values for .tags(). Expected an array of tag values";
31
+ readonly INVALID_WHERE_PARAMS: "Invalid expression for .where(). Expected a valid expression or function";
32
+ readonly INVALID_QUERY_REFERENCES_PARAMS: "Invalid query object for .queryReferences(). Expected a valid query object";
33
+ readonly INVALID_INCLUDE_PARAMS: "Invalid reference field path for .include(). Expected a valid string or array of strings";
34
+ readonly INVALID_QUERY: "Invalid query provided. Please ensure your query is properly formatted";
35
+ readonly INVALID_QUERIES: "Invalid queries provided. Please ensure all queries are properly formatted";
36
+ };
37
+ export declare const WarningMessages: {
38
+ readonly SLOW_INCLUDE_REFERENCES: ".includeReferences(...) is a relatively slow query. Consider limiting the depth or using .include() for specific references";
39
+ };
@@ -1094,4 +1094,5 @@ export declare class Stack {
1094
1094
  private getAllReferencePaths;
1095
1095
  private sanitizeIQuery;
1096
1096
  private sanityQueryAny;
1097
+ private sanitizeQueryBucket;
1097
1098
  }
@@ -1,33 +0,0 @@
1
- name: Create JIRA ISSUE
2
- on:
3
- pull_request:
4
- types: [opened]
5
- jobs:
6
- security-jira:
7
- if: ${{ github.actor == 'dependabot[bot]' || github.actor == 'snyk-bot' || contains(github.event.pull_request.head.ref, 'snyk-fix-') || contains(github.event.pull_request.head.ref, 'snyk-upgrade-')}}
8
- runs-on: ubuntu-latest
9
- steps:
10
- - uses: actions/checkout@v2
11
- - name: Login into JIRA
12
- uses: atlassian/gajira-login@master
13
- env:
14
- JIRA_BASE_URL: ${{ secrets.JIRA_BASE_URL }}
15
- JIRA_USER_EMAIL: ${{ secrets.JIRA_USER_EMAIL }}
16
- JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }}
17
- - name: Create a JIRA Issue
18
- id: create
19
- uses: atlassian/gajira-create@master
20
- with:
21
- project: ${{ secrets.JIRA_PROJECT }}
22
- issuetype: ${{ secrets.JIRA_ISSUE_TYPE }}
23
- summary: |
24
- Snyk | Vulnerability | ${{ github.event.repository.name }} | ${{ github.event.pull_request.title }}
25
- description: |
26
- PR: ${{ github.event.pull_request.html_url }}
27
-
28
- fields: "${{ secrets.JIRA_FIELDS }}"
29
- - name: Transition issue
30
- uses: atlassian/gajira-transition@v3
31
- with:
32
- issue: ${{ steps.create.outputs.issue }}
33
- transition: ${{ secrets.JIRA_TRANSITION }}
@@ -1,11 +0,0 @@
1
- name: SAST Scan
2
- on:
3
- pull_request:
4
- types: [opened, synchronize, reopened]
5
- jobs:
6
- security-sast:
7
- runs-on: ubuntu-latest
8
- steps:
9
- - uses: actions/checkout@v2
10
- - name: Semgrep Scan
11
- run: docker run -v /var/run/docker.sock:/var/run/docker.sock -v "${PWD}:/src" returntocorp/semgrep semgrep scan --config auto
File without changes